mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
refactor: separate components
This commit is contained in:
@@ -19,7 +19,10 @@ web/src/lib/
|
||||
core/ # операции над ImageData; без DOM, кроме io.ts/domText.ts
|
||||
executor/ # единственная точка исполнения инструментов
|
||||
i18n/ # словари ru/en, t(), состояние локали
|
||||
components/ # UI (fields/, layout/, ui/, schema-компоненты)
|
||||
components/ # UI по слоям: ui/ примитивы, layout/ каркас,
|
||||
# catalog/ каталог, schema/ view инструмента,
|
||||
# fields/ поля (+ fields/schema/ контролы схемы),
|
||||
# display/ типографика и карточки, feedback/ состояния
|
||||
zip.ts # сборка мультифайлового результата (1 → many)
|
||||
theme.svelte.ts # light/dark + localStorage
|
||||
web/src/routes/
|
||||
@@ -49,9 +52,10 @@ Coverage). Всё остальное в `core/**` — чистые функци
|
||||
него нет. Модель страницы уже хранит список `steps`, но исполняет первый шаг,
|
||||
а `registry.test.ts` требует, чтобы шаг был ровно один: композиция формы из
|
||||
полей двух инструментов — отдельная работа, описанная как фаза 11
|
||||
`docs/roadmap.md`. Под неё зарезервированы `components/StepCard.svelte`
|
||||
(сейчас импортируется только витриной `/kit`) и ключи `chain.*`,
|
||||
`errors.badPipelineShape`, `errors.pipelineVersion`, `errors.noSteps`.
|
||||
`docs/roadmap.md`. Под неё зарезервированы
|
||||
`components/display/StepCard.svelte` (сейчас импортируется только витриной
|
||||
`/kit`) и ключи `chain.*`, `errors.badPipelineShape`,
|
||||
`errors.pipelineVersion`, `errors.noSteps`.
|
||||
- **Разделения страниц, которые делят один инструмент** — сводит все
|
||||
convert-страницы на один инструмент; задача в `docs/backlog.md`. Пока
|
||||
`registry.test.ts` не требует, чтобы на инструмент ссылались две страницы, а
|
||||
@@ -169,8 +173,7 @@ registry/{tools,pages}/*.ts
|
||||
допустимому виду. Правка поведения полей идёт здесь, а не в компоненте.
|
||||
|
||||
Новый вид поля требует: спека + фабрика `field.*` → запись в `fieldSpecs` →
|
||||
контрол (`components/fields/schema/`, а для `dimension` —
|
||||
`fields/DimensionField.svelte`) → ключ в `FIELDS` → дефолт и санитизация →
|
||||
контрол (`components/fields/schema/`) → ключ в `FIELDS` → дефолт и санитизация →
|
||||
i18n-подписи → тест в `registry-schema.test.ts`.
|
||||
|
||||
## 5. Исполнение
|
||||
|
||||
+2
-2
@@ -92,12 +92,12 @@
|
||||
- [ ] `SchemaTextResult` вердикт-бейдж рисует руками: свои классы
|
||||
`badge`/`badge-tone--{tone}` и свой CSS в `<style>` вместо
|
||||
`ui/Badge.svelte` — тон и разметка бейджа живут в двух местах
|
||||
(`web/src/lib/components/SchemaTextResult.svelte`).
|
||||
(`web/src/lib/components/schema/SchemaTextResult.svelte`).
|
||||
- [ ] `PreviewTile`: высота плитки задана только `min-height: clamp(...)`,
|
||||
поэтому она не следует за пропорциями результата — широкий результат
|
||||
уезжает в узкую полосу, а высокий упирается в `max-height: 100vh`. Нужно,
|
||||
чтобы соотношение сторон выбиралось по результату в диапазоне 16:9…9:16
|
||||
(`web/src/lib/components/layout/PreviewTile.svelte`).
|
||||
(`web/src/lib/components/schema/PreviewTile.svelte`).
|
||||
- [ ] Радиусы бордеров: `--radius-m` ненулевой и применён ко всем бордерам
|
||||
(`app.css`, `SchemaToolView.svelte`). Решить, где скругления реально есть
|
||||
в дизайне, а где должен быть 0
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Upload } from "@lucide/svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import DownloadButton from "./ui/DownloadButton.svelte";
|
||||
|
||||
interface Props {
|
||||
inputMode: "image" | "text" | "none";
|
||||
canDownload: boolean;
|
||||
running: boolean;
|
||||
onupload: (file: File) => void;
|
||||
ondownload: () => void;
|
||||
}
|
||||
let { inputMode, canDownload, running, onupload, ondownload }: Props =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<div class="actions">
|
||||
{#if inputMode === "image"}
|
||||
<label class="btn upload">
|
||||
<Upload size={14} />
|
||||
{t("actions.openImage")}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onchange={(e) => {
|
||||
const f = (e.target as HTMLInputElement).files?.[0];
|
||||
if (f) onupload(f);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{/if}
|
||||
{#if canDownload}
|
||||
<DownloadButton
|
||||
label={t("actions.downloadResult")}
|
||||
onclick={ondownload}
|
||||
disabled={running}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-l);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
padding: var(--space-m) var(--space-l);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-m);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
.upload input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
+5
-23
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Funnel, Search } from "@lucide/svelte";
|
||||
import ChipButton from "$lib/components/ui/ChipButton.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
|
||||
interface Props {
|
||||
@@ -33,14 +34,11 @@
|
||||
<div class="catalog-filters">
|
||||
<Funnel size={15} />
|
||||
{#each FILTERS as f (f.value)}
|
||||
<button
|
||||
type="button"
|
||||
class:active={category === f.value}
|
||||
aria-pressed={category === f.value}
|
||||
<ChipButton
|
||||
label={f.label}
|
||||
active={category === f.value}
|
||||
onclick={() => (category = f.value)}
|
||||
>
|
||||
{f.label}
|
||||
</button>
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,20 +79,4 @@
|
||||
flex-wrap: wrap;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.catalog-filters button {
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
background: var(--color-panel);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
text-transform: uppercase;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
border-radius: var(--radius-m);
|
||||
cursor: pointer;
|
||||
}
|
||||
.catalog-filters button.active {
|
||||
background: var(--color-main);
|
||||
color: var(--color-background);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
</style>
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
import { resolve } from "$app/paths";
|
||||
import { ArrowUpRight } from "@lucide/svelte";
|
||||
import type { Component } from "svelte";
|
||||
import Icon from "./ui/Icon.svelte";
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Copy } from "@lucide/svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import IconButton from "./ui/IconButton.svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
|
||||
interface Props {
|
||||
code: string;
|
||||
+7
-21
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Ellipsis, GripVertical, X } from "@lucide/svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
@@ -26,14 +27,13 @@
|
||||
</div>
|
||||
<div class="step-tools">
|
||||
{#if tools}{@render tools()}{/if}
|
||||
<button
|
||||
type="button"
|
||||
class="step-remove"
|
||||
aria-label={t("chain.removeStepAria")}
|
||||
<IconButton
|
||||
icon={X}
|
||||
label={t("chain.removeStepAria")}
|
||||
variant="clear"
|
||||
size="s"
|
||||
onclick={() => onremove?.()}
|
||||
>
|
||||
<X size={16} />
|
||||
</button>
|
||||
/>
|
||||
<Ellipsis size={17} />
|
||||
</div>
|
||||
</div>
|
||||
@@ -81,24 +81,10 @@
|
||||
align-items: center;
|
||||
gap: var(--space-l);
|
||||
}
|
||||
.step-remove {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
line-height: inherit;
|
||||
& :global(svg) {
|
||||
display: block;
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
.step-card :global(.drag) {
|
||||
margin-top: var(--space-xl);
|
||||
color: var(--color-border);
|
||||
}
|
||||
.step-remove:hover {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
.step-body > :not(.step-heading) {
|
||||
padding: var(--space-l);
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Component, Snippet } from "svelte";
|
||||
import Icon from "./ui/Icon.svelte";
|
||||
import Icon from "$lib/components/ui/Icon.svelte";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import StatusDot from "./StatusDot.svelte";
|
||||
import MonoLabel from "./MonoLabel.svelte";
|
||||
import MonoLabel from "$lib/components/display/MonoLabel.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
import MonoLabel from "../MonoLabel.svelte";
|
||||
import MonoLabel from "$lib/components/display/MonoLabel.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import ChipButton from "$lib/components/ui/ChipButton.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
|
||||
interface Preset {
|
||||
@@ -49,10 +50,12 @@
|
||||
</div>
|
||||
<div class="angle-presets">
|
||||
{#each presets as preset (preset.value)}
|
||||
<button
|
||||
class:active={preset.value === value}
|
||||
onclick={() => onchange(preset.value)}>{preset.label}</button
|
||||
>
|
||||
<ChipButton
|
||||
label={preset.label}
|
||||
variant="outline"
|
||||
active={preset.value === value}
|
||||
onclick={() => onchange(preset.value)}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,22 +91,4 @@
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.angle-presets button {
|
||||
padding: var(--space-m) 0;
|
||||
color: var(--color-text-muted);
|
||||
background: var(--color-background);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
cursor: pointer;
|
||||
}
|
||||
.angle-presets button:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
.angle-presets button.active {
|
||||
color: var(--color-main);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { CheckboxSpec, FieldSpec } from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -15,30 +16,16 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<label class="control checkbox-control">
|
||||
<span>{label}</span>
|
||||
<Control element="label" {label} row>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="checkbox-field"
|
||||
checked={current}
|
||||
onchange={(e) => onchange?.((e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
</label>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.checkbox-control {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.checkbox-field {
|
||||
width: var(--space-xl);
|
||||
height: var(--space-xl);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { ColorSpec, FieldSpec } from "$lib/registry-schema";
|
||||
import ColorSwatchInput from "./ColorSwatchInput.svelte";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -13,47 +15,6 @@
|
||||
const current = $derived(typeof value === "string" ? value : sp.default);
|
||||
</script>
|
||||
|
||||
<label class="control color-control">
|
||||
<span>{label}</span>
|
||||
<span class="color-row">
|
||||
<span class="swatch" style="background:{current}"></span>
|
||||
<input
|
||||
type="color"
|
||||
value={current}
|
||||
oninput={(e) => onchange?.((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.color-control > span:first-child {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
.color-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
input[type="color"] {
|
||||
width: 100%;
|
||||
height: var(--space-xxl);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
background: var(--color-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
<Control element="label" {label}>
|
||||
<ColorSwatchInput value={current} onchange={(v) => onchange?.(v)} />
|
||||
</Control>
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
ColorPairSpec,
|
||||
FieldSpec,
|
||||
} from "$lib/registry-schema";
|
||||
import ColorSwatchInput from "./ColorSwatchInput.svelte";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -28,45 +30,26 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control color-pair-control">
|
||||
<span class="pair-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="pair-field">
|
||||
<label class="pair-axis">
|
||||
<span class="swatch" style="background:{current.from}"></span>
|
||||
<input
|
||||
type="color"
|
||||
<ColorSwatchInput
|
||||
value={current.from}
|
||||
oninput={(e) => setColor("from", (e.target as HTMLInputElement).value)}
|
||||
onchange={(v) => setColor("from", v)}
|
||||
/>
|
||||
<span class="axis-label">{t("ui.from")}</span>
|
||||
</label>
|
||||
<label class="pair-axis">
|
||||
<span class="swatch" style="background:{current.to}"></span>
|
||||
<input
|
||||
type="color"
|
||||
<ColorSwatchInput
|
||||
value={current.to}
|
||||
oninput={(e) => setColor("to", (e.target as HTMLInputElement).value)}
|
||||
onchange={(v) => setColor("to", v)}
|
||||
/>
|
||||
<span class="axis-label">{t("ui.to")}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.pair-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.pair-field {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -74,25 +57,6 @@
|
||||
}
|
||||
.pair-axis {
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-xxl) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.pair-axis input[type="color"] {
|
||||
width: 100%;
|
||||
height: var(--space-xxl);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
background: var(--color-background);
|
||||
cursor: pointer;
|
||||
}
|
||||
.axis-label {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
onchange?: (value: string) => void;
|
||||
}
|
||||
let { value, disabled = false, onchange }: Props = $props();
|
||||
</script>
|
||||
|
||||
<span class="swatch-field">
|
||||
<span class="swatch" style="background:{value}"></span>
|
||||
<input
|
||||
type="color"
|
||||
{value}
|
||||
{disabled}
|
||||
oninput={(e) => onchange?.((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</span>
|
||||
|
||||
<style>
|
||||
.swatch-field {
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-xxl) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
width: 100%;
|
||||
height: var(--space-xxl);
|
||||
padding: 0;
|
||||
background: var(--color-background);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -1,10 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { X } from "@lucide/svelte";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type {
|
||||
ColorList,
|
||||
ColorListSpec,
|
||||
FieldSpec,
|
||||
} from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -41,8 +45,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control colors-control">
|
||||
<span class="ctrl-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="chips">
|
||||
{#each current as hex, i (i)}
|
||||
<div class="chip">
|
||||
@@ -55,38 +58,28 @@
|
||||
/>
|
||||
</label>
|
||||
<span class="hex">{hex}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="remove"
|
||||
<IconButton
|
||||
icon={X}
|
||||
label={t("ui.removeColor")}
|
||||
variant="clear"
|
||||
size="s"
|
||||
onclick={() => removeColor(i)}
|
||||
disabled={current.length <= 1}
|
||||
aria-label={t("ui.removeColor")}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<button type="button" class="add" onclick={addColor}
|
||||
>{t("ui.addColor")}</button
|
||||
>
|
||||
<div class="add-row">
|
||||
<Button
|
||||
label={t("ui.addColor")}
|
||||
variant="outline"
|
||||
size="s"
|
||||
onclick={addColor}
|
||||
/>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.ctrl-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -121,39 +114,8 @@
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
.remove {
|
||||
.add-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: var(--space-xl);
|
||||
height: var(--space-xl);
|
||||
border: none;
|
||||
border-radius: var(--radius-s);
|
||||
background: none;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-s);
|
||||
cursor: pointer;
|
||||
}
|
||||
.remove:hover:not(:disabled) {
|
||||
color: var(--color-text);
|
||||
background: var(--color-background-muted);
|
||||
}
|
||||
.remove:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
.add {
|
||||
justify-self: start;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-m);
|
||||
background: none;
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.add:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-text-muted);
|
||||
justify-content: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
element?: "div" | "label";
|
||||
caps?: boolean;
|
||||
row?: boolean;
|
||||
trailing?: Snippet;
|
||||
children: Snippet;
|
||||
}
|
||||
let {
|
||||
label,
|
||||
element = "div",
|
||||
caps = false,
|
||||
row = false,
|
||||
trailing,
|
||||
children,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<svelte:element this={element} class="control" class:control--row={row}>
|
||||
<span class="label" class:caps>
|
||||
{label}
|
||||
{#if trailing}{@render trailing()}{/if}
|
||||
</span>
|
||||
{@render children()}
|
||||
</svelte:element>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
|
||||
.control--row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.caps {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
</style>
|
||||
+3
-17
@@ -5,6 +5,7 @@
|
||||
DimensionSpec,
|
||||
FieldSpec,
|
||||
} from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -34,8 +35,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control dimension-control">
|
||||
<span class="dimension-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="dimension-field">
|
||||
<label class="dimension-axis">
|
||||
<span>{t("ui.width")}</span>
|
||||
@@ -60,23 +60,9 @@
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.dimension-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.dimension-field {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -6,6 +6,8 @@
|
||||
FontStyle,
|
||||
FontStyleSpec,
|
||||
} from "$lib/registry-schema";
|
||||
import ColorSwatchInput from "./ColorSwatchInput.svelte";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -43,8 +45,7 @@
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="control font-style-control">
|
||||
<span class="fs-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="fs-grid">
|
||||
<label class="fs-cell">
|
||||
<span class="fs-sub">{t("ui.font")}</span>
|
||||
@@ -73,15 +74,10 @@
|
||||
</label>
|
||||
<label class="fs-cell">
|
||||
<span class="fs-sub">{t("ui.color")}</span>
|
||||
<span class="fs-color">
|
||||
<span class="swatch" style="background:{current.color}"></span>
|
||||
<input
|
||||
class="fs-color-input"
|
||||
type="color"
|
||||
<ColorSwatchInput
|
||||
value={current.color}
|
||||
oninput={(e) => set("color", (e.target as HTMLInputElement).value)}
|
||||
onchange={(v) => set("color", v)}
|
||||
/>
|
||||
</span>
|
||||
</label>
|
||||
<label class="fs-cell fs-bold">
|
||||
<span class="fs-sub">{t("ui.bold")}</span>
|
||||
@@ -92,23 +88,9 @@
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.fs-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.fs-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -122,8 +104,7 @@
|
||||
font-size: var(--font-size-s);
|
||||
}
|
||||
.fs-select,
|
||||
.fs-input,
|
||||
.fs-color-input {
|
||||
.fs-input {
|
||||
width: 100%;
|
||||
padding: var(--space-m) var(--space-l);
|
||||
background: var(--color-background);
|
||||
@@ -132,23 +113,6 @@
|
||||
color: var(--color-text);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
}
|
||||
.fs-color {
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-xxl) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.fs-color-input {
|
||||
height: var(--space-xxl);
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.fs-bold {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<script lang="ts">
|
||||
import AngleControl from "./AngleControl.svelte";
|
||||
import ColorSwatchInput from "./ColorSwatchInput.svelte";
|
||||
import Control from "./Control.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { FieldSpec, Gradient, GradientSpec } from "$lib/registry-schema";
|
||||
|
||||
@@ -29,46 +31,21 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control gradient-control">
|
||||
<span class="gradient-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="gradient-colors">
|
||||
<label class="color-field">
|
||||
<span>{t("ui.from")}</span>
|
||||
<span class="swatch" style="background:{current.from}"></span>
|
||||
<input
|
||||
type="color"
|
||||
value={current.from}
|
||||
oninput={(e) => set("from", (e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<ColorSwatchInput value={current.from} onchange={(v) => set("from", v)} />
|
||||
</label>
|
||||
<label class="color-field">
|
||||
<span>{t("ui.to")}</span>
|
||||
<span class="swatch" style="background:{current.to}"></span>
|
||||
<input
|
||||
type="color"
|
||||
value={current.to}
|
||||
oninput={(e) => set("to", (e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
<ColorSwatchInput value={current.to} onchange={(v) => set("to", v)} />
|
||||
</label>
|
||||
</div>
|
||||
<AngleControl value={current.angle} onchange={(a) => set("angle", a)} />
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.gradient-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.gradient-colors {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -76,22 +53,8 @@
|
||||
}
|
||||
.color-field {
|
||||
display: grid;
|
||||
grid-template-columns: auto var(--space-xxl) 1fr;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.color-field input[type="color"] {
|
||||
width: 100%;
|
||||
height: var(--space-xxl);
|
||||
padding: 0;
|
||||
background: var(--color-background);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { FieldSpec, Offset, OffsetSpec } from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -23,8 +24,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control offset-control">
|
||||
<span class="offset-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="offset-field">
|
||||
<label class="offset-axis">
|
||||
<span>X</span>
|
||||
@@ -49,23 +49,9 @@
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.offset-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.offset-field {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { FieldSpec, Plate, PlateSpec } from "$lib/registry-schema";
|
||||
import ColorSwatchInput from "./ColorSwatchInput.svelte";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -28,8 +30,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="control plate-control">
|
||||
<span class="plate-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="plate-row {current.enabled ? '' : 'off'}">
|
||||
<label class="plate-enabled">
|
||||
<span>{t("ui.backingPlate")}</span>
|
||||
@@ -39,15 +40,11 @@
|
||||
onchange={(e) => set("enabled", (e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
</label>
|
||||
<label class="plate-color">
|
||||
<span class="swatch" style="background:{current.color}"></span>
|
||||
<input
|
||||
type="color"
|
||||
<ColorSwatchInput
|
||||
value={current.color}
|
||||
disabled={!current.enabled}
|
||||
oninput={(e) => set("color", (e.target as HTMLInputElement).value)}
|
||||
onchange={(v) => set("color", v)}
|
||||
/>
|
||||
</label>
|
||||
<label class="plate-opacity">
|
||||
<span>
|
||||
{t("ui.opacity")}
|
||||
@@ -65,23 +62,9 @@
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.plate-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.plate-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@@ -101,26 +84,6 @@
|
||||
height: var(--space-xl);
|
||||
accent-color: var(--color-main);
|
||||
}
|
||||
.plate-color {
|
||||
display: grid;
|
||||
grid-template-columns: var(--space-xxl) 1fr;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
}
|
||||
.swatch {
|
||||
width: var(--space-xxl);
|
||||
height: var(--space-xxl);
|
||||
border-radius: var(--radius-s);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.plate-color input[type="color"] {
|
||||
width: 100%;
|
||||
height: var(--space-xxl);
|
||||
padding: 0;
|
||||
background: var(--color-background);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-s);
|
||||
}
|
||||
.plate-opacity {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
type FieldSpec,
|
||||
type Position9Spec,
|
||||
} from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -23,8 +24,7 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="control position-control">
|
||||
<span class="position-label">{label}</span>
|
||||
<Control {label} caps>
|
||||
<div class="grid">
|
||||
{#each POSITION9_VALUES as position (position)}
|
||||
<button
|
||||
@@ -38,23 +38,9 @@
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.position-label {
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-s);
|
||||
letter-spacing: var(--space-text-l);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.grid {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(3, var(--space-xl));
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { FieldSpec, NumberSpec, SliderSpec } from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -19,11 +20,10 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<label class="control">
|
||||
<span>
|
||||
{label}
|
||||
<Control element="label" {label}>
|
||||
{#snippet trailing()}
|
||||
<output>{current}</output>
|
||||
</span>
|
||||
{/snippet}
|
||||
<input
|
||||
aria-label={label}
|
||||
type="range"
|
||||
@@ -33,22 +33,10 @@
|
||||
value={current}
|
||||
oninput={handle}
|
||||
/>
|
||||
</label>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.control > span {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.control output {
|
||||
output {
|
||||
color: var(--color-text);
|
||||
}
|
||||
input[type="range"] {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { optionLabel } from "$lib/i18n/schema-tool-strings";
|
||||
import type { FieldSpec, SelectSpec } from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -20,8 +21,7 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<label class="control">
|
||||
<span>{label}</span>
|
||||
<Control element="label" {label}>
|
||||
<select
|
||||
class="select-field"
|
||||
value={current}
|
||||
@@ -33,17 +33,9 @@
|
||||
</option>
|
||||
{/each}
|
||||
</select>
|
||||
</label>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.select-field {
|
||||
width: 100%;
|
||||
padding: var(--space-m) var(--space-l);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { FieldSpec, TextSpec } from "$lib/registry-schema";
|
||||
import Control from "./Control.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
@@ -13,8 +14,7 @@
|
||||
const current = $derived(typeof value === "string" ? value : sp.default);
|
||||
</script>
|
||||
|
||||
<label class="control">
|
||||
<span>{label}</span>
|
||||
<Control element="label" {label}>
|
||||
<input
|
||||
class="text-field"
|
||||
type="text"
|
||||
@@ -22,17 +22,9 @@
|
||||
placeholder={sp.placeholder}
|
||||
oninput={(e) => onchange?.((e.target as HTMLInputElement).value)}
|
||||
/>
|
||||
</label>
|
||||
</Control>
|
||||
|
||||
<style>
|
||||
.control {
|
||||
display: grid;
|
||||
gap: var(--space-m);
|
||||
margin-bottom: var(--space-xl);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
}
|
||||
.text-field {
|
||||
width: 100%;
|
||||
padding: var(--space-m) var(--space-l);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
import PanelHeading from "../PanelHeading.svelte";
|
||||
import PanelHeading from "./PanelHeading.svelte";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from "svelte";
|
||||
import MonoLabel from "./MonoLabel.svelte";
|
||||
import MonoLabel from "$lib/components/display/MonoLabel.svelte";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from "$app/paths";
|
||||
import LangToggle from "../ui/LangToggle.svelte";
|
||||
import ThemeToggle from "../ui/ThemeToggle.svelte";
|
||||
import TopNav from "../ui/TopNav.svelte";
|
||||
import LangToggle from "$lib/components/ui/LangToggle.svelte";
|
||||
import ThemeToggle from "$lib/components/ui/ThemeToggle.svelte";
|
||||
import TopNav from "./TopNav.svelte";
|
||||
|
||||
interface Props {
|
||||
theme: "light" | "dark";
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import DownloadButton from "$lib/components/ui/DownloadButton.svelte";
|
||||
import UploadButton from "$lib/components/ui/UploadButton.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
|
||||
interface Props {
|
||||
inputMode: "image" | "text" | "none";
|
||||
canDownload: boolean;
|
||||
running: boolean;
|
||||
onupload: (file: File) => void;
|
||||
ondownload: () => void;
|
||||
}
|
||||
let { inputMode, canDownload, running, onupload, ondownload }: Props =
|
||||
$props();
|
||||
</script>
|
||||
|
||||
<div class="actions">
|
||||
{#if inputMode === "image"}
|
||||
<UploadButton label={t("actions.openImage")} onfile={onupload} />
|
||||
{/if}
|
||||
{#if canDownload}
|
||||
<DownloadButton
|
||||
label={t("actions.downloadResult")}
|
||||
onclick={ondownload}
|
||||
disabled={running}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-l);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
</style>
|
||||
+21
-30
@@ -9,19 +9,20 @@
|
||||
import { t } from "$lib/i18n/t";
|
||||
import { RotateCcw } from "@lucide/svelte";
|
||||
import type { Component } from "svelte";
|
||||
import DimensionField from "./fields/DimensionField.svelte";
|
||||
import CheckboxControl from "./fields/schema/CheckboxControl.svelte";
|
||||
import ColorControl from "./fields/schema/ColorControl.svelte";
|
||||
import ColorPairControl from "./fields/schema/ColorPairControl.svelte";
|
||||
import ColorsControl from "./fields/schema/ColorsControl.svelte";
|
||||
import RangeControl from "./fields/schema/RangeControl.svelte";
|
||||
import SelectControl from "./fields/schema/SelectControl.svelte";
|
||||
import TextControl from "./fields/schema/TextControl.svelte";
|
||||
import OffsetControl from "./fields/schema/OffsetControl.svelte";
|
||||
import PositionControl from "./fields/schema/PositionControl.svelte";
|
||||
import FontStyleControl from "./fields/schema/FontStyleControl.svelte";
|
||||
import PlateControl from "./fields/schema/PlateControl.svelte";
|
||||
import GradientControl from "./fields/schema/GradientControl.svelte";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import DimensionField from "$lib/components/fields/schema/DimensionField.svelte";
|
||||
import CheckboxControl from "$lib/components/fields/schema/CheckboxControl.svelte";
|
||||
import ColorControl from "$lib/components/fields/schema/ColorControl.svelte";
|
||||
import ColorPairControl from "$lib/components/fields/schema/ColorPairControl.svelte";
|
||||
import ColorsControl from "$lib/components/fields/schema/ColorsControl.svelte";
|
||||
import RangeControl from "$lib/components/fields/schema/RangeControl.svelte";
|
||||
import SelectControl from "$lib/components/fields/schema/SelectControl.svelte";
|
||||
import TextControl from "$lib/components/fields/schema/TextControl.svelte";
|
||||
import OffsetControl from "$lib/components/fields/schema/OffsetControl.svelte";
|
||||
import PositionControl from "$lib/components/fields/schema/PositionControl.svelte";
|
||||
import FontStyleControl from "$lib/components/fields/schema/FontStyleControl.svelte";
|
||||
import PlateControl from "$lib/components/fields/schema/PlateControl.svelte";
|
||||
import GradientControl from "$lib/components/fields/schema/GradientControl.svelte";
|
||||
|
||||
interface FieldControlProps {
|
||||
label: string;
|
||||
@@ -86,10 +87,13 @@
|
||||
{/each}
|
||||
|
||||
<div class="panel-foot">
|
||||
<button class="reset-btn" onclick={onreset}>
|
||||
<RotateCcw size={14} />
|
||||
{t("ui.reset")}
|
||||
</button>
|
||||
<Button
|
||||
icon={RotateCcw}
|
||||
label={t("ui.reset")}
|
||||
variant="clear"
|
||||
size="s"
|
||||
onclick={onreset}
|
||||
/>
|
||||
<span class="auto-note"><i></i> {t("ui.autoUpdate")}</span>
|
||||
</div>
|
||||
|
||||
@@ -120,19 +124,6 @@
|
||||
padding-top: var(--space-l);
|
||||
border-top: var(--size-border) solid var(--color-border);
|
||||
}
|
||||
.reset-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.reset-btn:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
.auto-note i {
|
||||
width: var(--size-border-thick);
|
||||
height: var(--size-border-thick);
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import MetaList from "$lib/components/MetaList.svelte";
|
||||
import { buildSchemaPreviewModel } from "$lib/components/schema-preview-model";
|
||||
import SchemaActions from "$lib/components/SchemaActions.svelte";
|
||||
import SchemaResultTile from "$lib/components/SchemaResultTile.svelte";
|
||||
import SchemaSourceTile from "$lib/components/SchemaSourceTile.svelte";
|
||||
import MetaList from "$lib/components/display/MetaList.svelte";
|
||||
import { buildSchemaPreviewModel } from "./schema-preview-model";
|
||||
import SchemaActions from "./SchemaActions.svelte";
|
||||
import SchemaResultTile from "./SchemaResultTile.svelte";
|
||||
import SchemaSourceTile from "./SchemaSourceTile.svelte";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { FileResult, InputMode, ResultKind } from "$lib/registry";
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
import type { FileResult, ResultKind } from "$lib/registry";
|
||||
import { Check } from "@lucide/svelte";
|
||||
import SchemaTextResult from "./SchemaTextResult.svelte";
|
||||
import PreviewTile from "./layout/PreviewTile.svelte";
|
||||
import PreviewTile from "./PreviewTile.svelte";
|
||||
|
||||
interface Props {
|
||||
resultKind?: ResultKind;
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import type { InputMode } from "$lib/registry";
|
||||
import PreviewTile from "./layout/PreviewTile.svelte";
|
||||
import PreviewTile from "./PreviewTile.svelte";
|
||||
import SchemaTextSource from "./SchemaTextSource.svelte";
|
||||
|
||||
interface Props {
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
import { verdictText, verdictTone } from "$lib/i18n/schema-tool-strings";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import { Copy, Download, FileText } from "@lucide/svelte";
|
||||
import IconButton from "./ui/IconButton.svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
+15
-40
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { ArrowUp, Upload } from "@lucide/svelte";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
|
||||
interface Props {
|
||||
@@ -36,15 +37,21 @@
|
||||
}}></textarea>
|
||||
<div class="actions">
|
||||
{#if onsample}
|
||||
<button type="button" class="secondary" onclick={onsample}>
|
||||
<Upload size={14} />
|
||||
{t("textSource.trySample")}
|
||||
</button>
|
||||
<Button
|
||||
icon={Upload}
|
||||
label={t("textSource.trySample")}
|
||||
variant="outline"
|
||||
size="s"
|
||||
onclick={onsample}
|
||||
/>
|
||||
{/if}
|
||||
<button type="button" class="render" onclick={onrender} {disabled}>
|
||||
<ArrowUp size={14} />
|
||||
{t("textSource.render")}
|
||||
</button>
|
||||
<Button
|
||||
icon={ArrowUp}
|
||||
label={t("textSource.render")}
|
||||
size="s"
|
||||
onclick={onrender}
|
||||
{disabled}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -76,36 +83,4 @@
|
||||
gap: var(--space-m);
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.render {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
padding: var(--space-m) var(--space-l);
|
||||
border: var(--size-border) solid var(--color-main);
|
||||
border-radius: var(--radius-m);
|
||||
background: var(--color-main);
|
||||
color: var(--color-background);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.render:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: default;
|
||||
}
|
||||
.secondary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
padding: var(--space-m) var(--space-l);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-m);
|
||||
background: none;
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.secondary:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-text-muted);
|
||||
}
|
||||
</style>
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import SchemaFields from "$lib/components/SchemaFields.svelte";
|
||||
import SchemaPreview from "$lib/components/SchemaPreview.svelte";
|
||||
import SchemaFields from "./SchemaFields.svelte";
|
||||
import SchemaPreview from "./SchemaPreview.svelte";
|
||||
import { debounce } from "$lib/core/debounce";
|
||||
import { ToolError, type ErrorVars } from "$lib/core/errors";
|
||||
import { decodeFile, downloadBlob, encode } from "$lib/core/io";
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { makeImage } from "../core/test-helpers";
|
||||
import { makeImage } from "../../core/test-helpers";
|
||||
import {
|
||||
buildSchemaPreviewModel,
|
||||
type PreviewTranslate,
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import type { PixelImage } from "../core/types";
|
||||
import type { FileResult, InputMode, ResultKind } from "../registry";
|
||||
import type { PixelImage } from "$lib/core/types";
|
||||
import type { FileResult, InputMode, ResultKind } from "$lib/registry";
|
||||
|
||||
export type PreviewTranslate = (key: string) => string;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { Component } from "svelte";
|
||||
import { ButtonVariantDefine, type ButtonVariant } from "../define";
|
||||
import {
|
||||
ButtonVariantDefine,
|
||||
type ButtonVariant,
|
||||
type ButtonSize,
|
||||
} from "./define";
|
||||
import Icon from "./Icon.svelte";
|
||||
|
||||
interface Props {
|
||||
@@ -8,21 +12,27 @@
|
||||
ariaLabel?: string;
|
||||
onclick?: () => void;
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
icon?: Component<{ size?: number; class?: string }>;
|
||||
disabled?: boolean;
|
||||
type?: "button" | "submit";
|
||||
}
|
||||
let {
|
||||
label,
|
||||
ariaLabel = label,
|
||||
onclick,
|
||||
variant = ButtonVariantDefine.PRIMARY,
|
||||
size = "m",
|
||||
icon,
|
||||
disabled = false,
|
||||
type = "button",
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
class="btn btn--{variant}"
|
||||
class="btn btn--{variant} btn--{size}"
|
||||
class:btn--icon={!label}
|
||||
{type}
|
||||
{disabled}
|
||||
aria-label={ariaLabel}
|
||||
onclick={() => onclick?.()}
|
||||
@@ -51,12 +61,36 @@
|
||||
color: var(--color-background);
|
||||
}
|
||||
|
||||
.btn--s {
|
||||
gap: var(--space-m);
|
||||
padding: var(--space-m) var(--space-l);
|
||||
border-width: var(--size-border);
|
||||
border-radius: var(--radius-m);
|
||||
font-size: var(--font-size-s);
|
||||
font-family: var(--font-mono);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.btn--s.btn--icon {
|
||||
padding: var(--space-s);
|
||||
}
|
||||
|
||||
.btn--outline {
|
||||
background: transparent;
|
||||
color: var(--color-main-tint);
|
||||
border-color: var(--color-main-tint);
|
||||
}
|
||||
|
||||
.btn--s.btn--outline {
|
||||
color: var(--color-text-muted);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
.btn--s.btn--outline:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.btn--accent {
|
||||
background: var(--color-accent);
|
||||
color: var(--color-background);
|
||||
@@ -69,6 +103,16 @@
|
||||
border-color: var(--color-danger);
|
||||
}
|
||||
|
||||
.btn--clear {
|
||||
background: none;
|
||||
border-color: transparent;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.btn--clear:hover {
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
label: string;
|
||||
active?: boolean;
|
||||
variant?: "filled" | "outline";
|
||||
onclick?: () => void;
|
||||
}
|
||||
let { label, active = false, variant = "filled", onclick }: Props = $props();
|
||||
</script>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="chip chip--{variant}"
|
||||
class:active
|
||||
aria-pressed={active}
|
||||
onclick={() => onclick?.()}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
|
||||
<style>
|
||||
.chip {
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
background: var(--color-panel);
|
||||
color: var(--color-text-muted);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
letter-spacing: var(--space-text-m);
|
||||
text-transform: uppercase;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
border-radius: var(--radius-m);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.chip--filled.active {
|
||||
background: var(--color-main);
|
||||
color: var(--color-background);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
|
||||
.chip--outline {
|
||||
padding: var(--space-m) 0;
|
||||
background: var(--color-background);
|
||||
border-radius: var(--radius-s);
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
.chip--outline:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
|
||||
.chip--outline.active {
|
||||
color: var(--color-main);
|
||||
border-color: var(--color-main);
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,10 @@
|
||||
<script lang="ts">
|
||||
import type { Component } from "svelte";
|
||||
import { ButtonVariantDefine, type ButtonVariant } from "../define";
|
||||
import {
|
||||
ButtonVariantDefine,
|
||||
type ButtonVariant,
|
||||
type ButtonSize,
|
||||
} from "./define";
|
||||
import Button from "./Button.svelte";
|
||||
|
||||
interface Props {
|
||||
@@ -8,6 +12,7 @@
|
||||
label: string;
|
||||
onclick?: () => void;
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@@ -16,8 +21,17 @@
|
||||
label,
|
||||
onclick,
|
||||
variant = ButtonVariantDefine.PRIMARY,
|
||||
size = "m",
|
||||
disabled = false,
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<Button label="" {onclick} {variant} {disabled} {icon} ariaLabel={label} />
|
||||
<Button
|
||||
label=""
|
||||
{onclick}
|
||||
{variant}
|
||||
{size}
|
||||
{disabled}
|
||||
{icon}
|
||||
ariaLabel={label}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Moon, Sun } from "@lucide/svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import IconButton from "../ui/IconButton.svelte";
|
||||
import IconButton from "./IconButton.svelte";
|
||||
|
||||
interface Props {
|
||||
theme: "light" | "dark";
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import { Upload } from "@lucide/svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
|
||||
interface Props {
|
||||
label: string;
|
||||
accept?: string;
|
||||
onfile: (file: File) => void;
|
||||
}
|
||||
let { label, accept = "image/*", onfile }: Props = $props();
|
||||
</script>
|
||||
|
||||
<label class="upload">
|
||||
<Icon icon={Upload} size={14} />
|
||||
{label}
|
||||
<input
|
||||
type="file"
|
||||
{accept}
|
||||
onchange={(e) => {
|
||||
const f = (e.target as HTMLInputElement).files?.[0];
|
||||
if (f) onfile(f);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<style>
|
||||
.upload {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-m);
|
||||
padding: var(--space-m) var(--space-l);
|
||||
border: var(--size-border) solid var(--color-border);
|
||||
border-radius: var(--radius-m);
|
||||
font: var(--font-size-s) var(--font-mono);
|
||||
color: var(--color-text-muted);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.upload:hover {
|
||||
color: var(--color-text);
|
||||
border-color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
.upload input {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -8,3 +8,11 @@ export const ButtonVariantDefine = {
|
||||
|
||||
export type ButtonVariant =
|
||||
(typeof ButtonVariantDefine)[keyof typeof ButtonVariantDefine];
|
||||
|
||||
export const ButtonSizeDefine = {
|
||||
M: "m",
|
||||
S: "s",
|
||||
} as const;
|
||||
|
||||
export type ButtonSize =
|
||||
(typeof ButtonSizeDefine)[keyof typeof ButtonSizeDefine];
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { PREVIEW_GROUPS, PREVIEW_TOTAL } from "$lib/catalog";
|
||||
import CatalogGroup from "$lib/components/CatalogGroup.svelte";
|
||||
import CatalogHeader from "$lib/components/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/ToolCard.svelte";
|
||||
import CatalogGroup from "$lib/components/catalog/CatalogGroup.svelte";
|
||||
import CatalogHeader from "$lib/components/catalog/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/catalog/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/catalog/ToolCard.svelte";
|
||||
import {
|
||||
pageDescription,
|
||||
pageTitle,
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
<script lang="ts">
|
||||
import CheckerCanvas from "$lib/components/CheckerCanvas.svelte";
|
||||
import CodeBlock from "$lib/components/CodeBlock.svelte";
|
||||
import EmptyState from "$lib/components/EmptyState.svelte";
|
||||
import ToolCard from "$lib/components/catalog/ToolCard.svelte";
|
||||
import CheckerCanvas from "$lib/components/display/CheckerCanvas.svelte";
|
||||
import CodeBlock from "$lib/components/display/CodeBlock.svelte";
|
||||
import ImageCard from "$lib/components/display/ImageCard.svelte";
|
||||
import MetaList from "$lib/components/display/MetaList.svelte";
|
||||
import SettingGroup from "$lib/components/display/SettingGroup.svelte";
|
||||
import SettingsFooter from "$lib/components/display/SettingsFooter.svelte";
|
||||
import StepCard from "$lib/components/display/StepCard.svelte";
|
||||
import EmptyState from "$lib/components/feedback/EmptyState.svelte";
|
||||
import StatusLine from "$lib/components/feedback/StatusLine.svelte";
|
||||
import CheckboxField from "$lib/components/fields/CheckboxField.svelte";
|
||||
import ColorField from "$lib/components/fields/ColorField.svelte";
|
||||
import SelectField from "$lib/components/fields/SelectField.svelte";
|
||||
import SliderField from "$lib/components/fields/SliderField.svelte";
|
||||
import TextField from "$lib/components/fields/TextField.svelte";
|
||||
import ImageCard from "$lib/components/ImageCard.svelte";
|
||||
import Panel from "$lib/components/layout/Panel.svelte";
|
||||
import MetaList from "$lib/components/MetaList.svelte";
|
||||
import SettingGroup from "$lib/components/SettingGroup.svelte";
|
||||
import SettingsFooter from "$lib/components/SettingsFooter.svelte";
|
||||
import StatusLine from "$lib/components/StatusLine.svelte";
|
||||
import StepCard from "$lib/components/StepCard.svelte";
|
||||
import ToolCard from "$lib/components/ToolCard.svelte";
|
||||
import Badge from "$lib/components/ui/Badge.svelte";
|
||||
import Button from "$lib/components/ui/Button.svelte";
|
||||
import ChipButton from "$lib/components/ui/ChipButton.svelte";
|
||||
import DownloadButton from "$lib/components/ui/DownloadButton.svelte";
|
||||
import IconButton from "$lib/components/ui/IconButton.svelte";
|
||||
import Segmented from "$lib/components/ui/Segmented.svelte";
|
||||
import Toggle from "$lib/components/ui/Toggle.svelte";
|
||||
import UploadButton from "$lib/components/ui/UploadButton.svelte";
|
||||
import { Download, ImageOff, Plus, Trash2 } from "@lucide/svelte";
|
||||
|
||||
import { PAGES } from "$lib/registry";
|
||||
@@ -52,6 +54,26 @@
|
||||
<Button variant="outline" onclick={() => {}} label="Outline" />
|
||||
<Button variant="accent" onclick={() => {}} label="Accent" />
|
||||
<Button variant="danger" onclick={() => {}} label="Danger" />
|
||||
<Button variant="clear" onclick={() => {}} label="Clear" />
|
||||
</SettingGroup>
|
||||
<SettingGroup label="size s">
|
||||
<Button size="s" onclick={() => {}} label="Primary" />
|
||||
<Button size="s" variant="outline" onclick={() => {}} label="Outline" />
|
||||
<Button size="s" variant="clear" onclick={() => {}} label="Clear" />
|
||||
<IconButton size="s" icon={Plus} label="Add" onclick={() => {}} />
|
||||
<IconButton
|
||||
size="s"
|
||||
variant="clear"
|
||||
icon={Trash2}
|
||||
label="Delete"
|
||||
onclick={() => {}}
|
||||
/>
|
||||
</SettingGroup>
|
||||
<SettingGroup label="chips">
|
||||
<ChipButton label="All" active onclick={() => {}} />
|
||||
<ChipButton label="Convert" onclick={() => {}} />
|
||||
<ChipButton label="0°" variant="outline" active onclick={() => {}} />
|
||||
<ChipButton label="90°" variant="outline" onclick={() => {}} />
|
||||
</SettingGroup>
|
||||
<SettingGroup label="states">
|
||||
<Button disabled onclick={() => {}} label="Disabled" />
|
||||
@@ -71,6 +93,7 @@
|
||||
</SettingGroup>
|
||||
</div>
|
||||
<SettingsFooter>
|
||||
<UploadButton label="Open image" onfile={() => {}} />
|
||||
<DownloadButton label="Download PNG" onclick={() => {}} />
|
||||
</SettingsFooter>
|
||||
</Panel>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { PREVIEW_GROUPS, PREVIEW_TOTAL } from "$lib/catalog";
|
||||
import CatalogGroup from "$lib/components/CatalogGroup.svelte";
|
||||
import CatalogHeader from "$lib/components/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/ToolCard.svelte";
|
||||
import CatalogGroup from "$lib/components/catalog/CatalogGroup.svelte";
|
||||
import CatalogHeader from "$lib/components/catalog/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/catalog/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/catalog/ToolCard.svelte";
|
||||
import {
|
||||
pageDescription,
|
||||
pageTitle,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import EmptyState from "$lib/components/EmptyState.svelte";
|
||||
import SchemaToolView from "$lib/components/SchemaToolView.svelte";
|
||||
import EmptyState from "$lib/components/feedback/EmptyState.svelte";
|
||||
import SchemaToolView from "$lib/components/schema/SchemaToolView.svelte";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import { getPageBySlug, getTool } from "$lib/registry";
|
||||
import { SlidersHorizontal as ToolIcon } from "@lucide/svelte";
|
||||
|
||||
@@ -20,7 +20,7 @@ export default defineConfig({
|
||||
"src/lib/**/test-helpers.ts",
|
||||
"src/lib/**/*.svelte.ts",
|
||||
"src/lib/executor/executor.worker.ts",
|
||||
"src/lib/components/define.ts",
|
||||
"src/lib/components/ui/define.ts",
|
||||
"src/lib/core/domText.ts",
|
||||
"src/lib/core/io.ts",
|
||||
"src/lib/i18n/en.ts",
|
||||
|
||||
Reference in New Issue
Block a user