diff --git a/web/src/lib/components/kit/SchemaFields.svelte b/web/src/lib/components/kit/SchemaFields.svelte
new file mode 100644
index 0000000..bc3b232
--- /dev/null
+++ b/web/src/lib/components/kit/SchemaFields.svelte
@@ -0,0 +1,134 @@
+
+
+{#each Object.entries(schema.fields) as [id, field] (id)}
+ {#if field.spec.kind === "number" || field.spec.kind === "slider"}
+
+ {:else if field.spec.kind === "color"}
+
+ {/if}
+{/each}
+
+
+
+
diff --git a/web/src/lib/components/kit/SchemaPreview.svelte b/web/src/lib/components/kit/SchemaPreview.svelte
new file mode 100644
index 0000000..77fa39c
--- /dev/null
+++ b/web/src/lib/components/kit/SchemaPreview.svelte
@@ -0,0 +1,174 @@
+
+
+
+
SOURCE / RESULT
+
+
+
+
+
+
+{#if error}
+ {error}
+{/if}
+
+
+
+ SOURCE
+
+ {#if sourceUrl}
+

+ {:else}
+
choose an image
+ {/if}
+
+
+
+ RESULT {running ? "…" : ""}
+
+ {#if resultUrl}
+

+ {:else if running}
+
+ {:else}
+
no result yet
+ {/if}
+
+
+
+
+
+
+
+
+
diff --git a/web/src/lib/components/kit/SchemaToolView.svelte b/web/src/lib/components/kit/SchemaToolView.svelte
new file mode 100644
index 0000000..50c2437
--- /dev/null
+++ b/web/src/lib/components/kit/SchemaToolView.svelte
@@ -0,0 +1,216 @@
+
+
+{#if schema}
+
+{:else}
+ This tool has no schema yet.
+{/if}
+
+
diff --git a/web/src/lib/core/debounce.ts b/web/src/lib/core/debounce.ts
new file mode 100644
index 0000000..0efc07e
--- /dev/null
+++ b/web/src/lib/core/debounce.ts
@@ -0,0 +1,17 @@
+export function debounce(
+ fn: () => void,
+ ms: number,
+): { (): void; cancel: () => void } {
+ let timer: ReturnType | undefined;
+
+ const debounced = () => {
+ if (timer !== undefined) clearTimeout(timer);
+ timer = setTimeout(() => fn(), ms);
+ };
+
+ debounced.cancel = () => {
+ if (timer !== undefined) clearTimeout(timer);
+ };
+
+ return debounced;
+}
diff --git a/web/src/preview.css b/web/src/preview.css
index cc0c572..0af34b1 100644
--- a/web/src/preview.css
+++ b/web/src/preview.css
@@ -144,9 +144,15 @@
--font-size-s: 12px;
--font-size-m: 14px;
--font-size-l: 16px;
+ --font-size-xl: 24px;
+ --font-size-2xl: 32px;
--radius-s: 0px;
--radius-m: 4px;
+
+ --bp-mobile: 640px;
+ --bp-tablet: 800px;
+ --bp-desktop: 1100px;
}
[data-theme="dark"] {
diff --git a/web/src/routes/preview/tools/[id]/+page.svelte b/web/src/routes/preview/tools/[id]/+page.svelte
index 42ce9ea..8d3af18 100644
--- a/web/src/routes/preview/tools/[id]/+page.svelte
+++ b/web/src/routes/preview/tools/[id]/+page.svelte
@@ -1,6 +1,7 @@