mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
fix: update i18n - schema components
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
# План: инструмент «Разрезать PNG на части» (split-into-parts-png)
|
# План: инструмент «Разрезать PNG на части» (split-into-parts-png)
|
||||||
|
|
||||||
> Статус: **выполнено и в архиве** (2026-09). Инструмент и механизм «результат =
|
> Статус: **выполнено и в архиве** (2026-09). Инструмент и механизм «результат =
|
||||||
> набор файлов» (1 → many) реализованы; первый потребитель — `split-into-parts-png`.
|
> набор файлов» (1 → many) реализованы; первый потребитель —
|
||||||
> Общий процесс таких инструментов — задача 18 в `docs/backlog.md`.
|
> `split-into-parts-png`. Общий процесс таких инструментов — задача 18 в
|
||||||
|
> `docs/backlog.md`.
|
||||||
|
|
||||||
## Цель
|
## Цель
|
||||||
|
|
||||||
|
|||||||
+18
-31
@@ -156,12 +156,11 @@
|
|||||||
- `verdictTone(key)` — `success`|`danger`|`info` по имени ключа
|
- `verdictTone(key)` — `success`|`danger`|`info` по имени ключа
|
||||||
(`*Yes`/`*No`/`*Portrait`/`*Landscape`) — не по строке.
|
(`*Yes`/`*No`/`*Portrait`/`*Landscape`) — не по строке.
|
||||||
- **0.9** Расширить тип `Dict` (только новая ветка `lib/i18n`):
|
- **0.9** Расширить тип `Dict` (только новая ветка `lib/i18n`):
|
||||||
- секция `preview` **не заводится**: оставшиеся после чистки mono-лейблов (см.
|
- секция `preview` **не заводится**: строки нового UI живут в собственных
|
||||||
решение B) строки нового UI живут в собственных категориях — расширяем
|
категориях — расширяем существующие (`catalog`, `header`, `dropZone`,
|
||||||
существующие (`catalog`, `header`, `dropZone`, `sourceCard`, `resultCard`,
|
`sourceCard`, `resultCard`, `paramsCard`, `textResult`, `ui`) и заводим две
|
||||||
`paramsCard`, `textResult`, `ui`) и заводим две маленькие новые: `actions`
|
маленькие новые: `actions` (Generate / Open image / Download result…) и
|
||||||
(Generate / Open image / Download result…) и `textSource` (placeholder, Try
|
`textSource` (placeholder, Try sample…);
|
||||||
sample…);
|
|
||||||
- новая глобальная секция `fields: Record<string, string>` — переводы подписей
|
- новая глобальная секция `fields: Record<string, string>` — переводы подписей
|
||||||
полей по ключу из схемы (решение C);
|
полей по ключу из схемы (решение C);
|
||||||
- новая глобальная секция `groups: Record<string, string>` — переводы
|
- новая глобальная секция `groups: Record<string, string>` — переводы
|
||||||
@@ -197,36 +196,29 @@
|
|||||||
`t("header.footerNote")`.
|
`t("header.footerNote")`.
|
||||||
- **3.3** `tools/[id]/+page.svelte`: «Tool not found», fallback title → `t()`.
|
- **3.3** `tools/[id]/+page.svelte`: «Tool not found», fallback title → `t()`.
|
||||||
|
|
||||||
### Фаза 4: Компоненты — основной блок (по одному коммиту на компонент)
|
### Фаза 4: Компоненты — основной блок (коммиты кучками по смыслу)
|
||||||
|
|
||||||
Каждый коммит: удаление декоративных mono-лейблов (решение B) + замена
|
Все текстовые подписи в компонентах — информативные, сохраняем и переводим на
|
||||||
оставшихся строк на `t`/хелперы + lint/check. Список mono-лейблов, которые
|
`t`/хелперы. Каждый коммит: замена строк + lint/check.
|
||||||
**удаляем**: eyebrow'ы (`PNG PROCESSING / SINGLE TOOL`, `KIT SHOWCASE`,
|
|
||||||
`EASY-PNG-TOOLS / CATALOG`), статусы (`LIVE PREVIEW`, `rendering ok`), панельные
|
|
||||||
подписи (`TOOL SETTINGS`), дублирующие капшены (`SOURCE / RESULT`,
|
|
||||||
`GENERATOR / RESULT`). Оставляем и локализуем только информативные: категории
|
|
||||||
каталога, счётчики инструментов, подписи данных (`FORMAT`, `parts`, `px`, «ZIP
|
|
||||||
(PNG)»), alt-тексты, а также содержательные заголовки («Configure output»).
|
|
||||||
|
|
||||||
- `SchemaToolView.svelte`: убрать eyebrow/`LIVE PREVIEW`/`TOOL SETTINGS`,
|
- `SchemaToolView.svelte`: title/description →
|
||||||
перевести «Configure output» и «This tool has no schema yet.».
|
`toolTitle()`/`toolDescription()`, перевести `TOOL SETTINGS`, «Configure
|
||||||
- `SchemaSourceTile.svelte`: убрать figcaption `SOURCE`, перевести «choose an
|
output» и «This tool has no schema yet.».
|
||||||
|
- `SchemaSourceTile.svelte`: перевести figcaption `SOURCE`, «choose an
|
||||||
image»/«no source…» и alt.
|
image»/«no source…» и alt.
|
||||||
- `SchemaResultTile.svelte`: убрать figcaption `RESULT`, перевести «no result
|
- `SchemaResultTile.svelte`: перевести figcaption `RESULT`, «no result yet» и
|
||||||
yet»/«parts» и alt.
|
alt; суффикс «parts» — в `PreviewTile`.
|
||||||
- `SchemaPreview.svelte`: убрать панельные
|
- `SchemaPreview.svelte`: перевести панельный лейбл `PREVIEW PANEL` и
|
||||||
`SOURCE / RESULT`/`GENERATOR / RESULT`, перевести meta-капшены (`FORMAT` и
|
meta-капшены `SOURCE / RESULT / FORMAT`; значения «parts»/«text» → `t()`.
|
||||||
т.п.).
|
|
||||||
- `SchemaActions.svelte`: Generating…, Generate, Open image, Download result.
|
- `SchemaActions.svelte`: Generating…, Generate, Open image, Download result.
|
||||||
- `SchemaFields.svelte`: Reset, «updates automatically», `labelOf(field.id)` →
|
- `SchemaFields.svelte`: Reset, «updates automatically», `labelOf(field.id)` →
|
||||||
`fieldLabel(field)`.
|
`fieldLabel(field)`.
|
||||||
- `SchemaTextSource.svelte`: placeholder, Try sample, Render text.
|
- `SchemaTextSource.svelte`: placeholder, Try sample, Render text.
|
||||||
- `SchemaTextResult.svelte`: убрать лейбл `RESULT` у вердикта, перевести Copy /
|
- `SchemaTextResult.svelte`: перевести лейбл `RESULT` у вердикта, Copy /
|
||||||
Download .txt; tone detection → `verdictTone()`.
|
Download .txt; tone detection → `verdictTone()`.
|
||||||
- `CatalogToolbar.svelte`: фильтры → `t("categories.all")` +
|
- `CatalogToolbar.svelte`: фильтры → `t("categories.all")` +
|
||||||
`t("categories.X")`, поисковый placeholder.
|
`t("categories.X")`, поисковый placeholder.
|
||||||
- `CatalogHeader.svelte`: убрать eyebrow, перевести heading/lead и счётчик
|
- `CatalogHeader.svelte`: перевести heading/lead и счётчик «TOOLS AVAILABLE».
|
||||||
«TOOLS AVAILABLE».
|
|
||||||
- `CatalogGroup.svelte`: перевести суффикс счётчика «TOOLS».
|
- `CatalogGroup.svelte`: перевести суффикс счётчика «TOOLS».
|
||||||
- `Dropzone.svelte`: текст зоны → `t("dropZone.pickDefault")`.
|
- `Dropzone.svelte`: текст зоны → `t("dropZone.pickDefault")`.
|
||||||
- Schema field controls (по одному или группой): Width/Height, From/To, Backing
|
- Schema field controls (по одному или группой): Width/Height, From/To, Backing
|
||||||
@@ -320,11 +312,6 @@
|
|||||||
(`title: "groups.canvas"`), перевод — в глобальной секции `groups` словаря
|
(`title: "groups.canvas"`), перевод — в глобальной секции `groups` словаря
|
||||||
(`Dict.groups: Record<string, string>`). В UI — `groupLabel(title)` =
|
(`Dict.groups: Record<string, string>`). В UI — `groupLabel(title)` =
|
||||||
`t(title)` с фолбэком на сырой ключ.
|
`t(title)` с фолбэком на сырой ключ.
|
||||||
- **B. Mono-лейблы**: декоративные лейблы без полезной информации **удаляем**
|
|
||||||
(eyebrow'ы, статусы, панельные капшены, дублирующие подписи секций) — они
|
|
||||||
занимают место. Оставляем только там, где реально нужно, и локализуем:
|
|
||||||
счётчики инструментов, категории каталога, подписи данных (FORMAT, parts, px,
|
|
||||||
«ZIP (PNG)»), alt-тексты, содержательные headings.
|
|
||||||
- **C. Лейблы полей**: в схеме задаём i18n-ключ
|
- **C. Лейблы полей**: в схеме задаём i18n-ключ
|
||||||
(`field.number({ label: "fields.thickness", … })`), в UI выводится перевод —
|
(`field.number({ label: "fields.thickness", … })`), в UI выводится перевод —
|
||||||
`fieldLabel(field)` = `t(label)`. Ключи — глобальная секция `fields` словаря,
|
`fieldLabel(field)` = `t(label)`. Ключи — глобальная секция `fields` словаря,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Copy } from "@lucide/svelte";
|
import { Copy } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import IconButton from "./ui/IconButton.svelte";
|
import IconButton from "./ui/IconButton.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -16,7 +17,11 @@
|
|||||||
<pre class="code-pre"><code>{code}</code></pre>
|
<pre class="code-pre"><code>{code}</code></pre>
|
||||||
{#if copyable}
|
{#if copyable}
|
||||||
<div class="code-copy">
|
<div class="code-copy">
|
||||||
<IconButton icon={Copy} label="Copy" onclick={() => oncopy?.()} />
|
<IconButton
|
||||||
|
icon={Copy}
|
||||||
|
label={t("textResult.copy")}
|
||||||
|
onclick={() => oncopy?.()}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Upload } from "@lucide/svelte";
|
import { Upload } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import DownloadButton from "./ui/DownloadButton.svelte";
|
import DownloadButton from "./ui/DownloadButton.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -16,7 +17,8 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#if inputMode === "image"}
|
{#if inputMode === "image"}
|
||||||
<label class="btn upload">
|
<label class="btn upload">
|
||||||
<Upload size={14} /> Open image
|
<Upload size={14} />
|
||||||
|
{t("actions.openImage")}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
@@ -29,7 +31,7 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if canDownload}
|
{#if canDownload}
|
||||||
<DownloadButton
|
<DownloadButton
|
||||||
label="Download result"
|
label={t("actions.downloadResult")}
|
||||||
onclick={ondownload}
|
onclick={ondownload}
|
||||||
disabled={running}
|
disabled={running}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import SchemaResultTile from "$lib/components/SchemaResultTile.svelte";
|
import SchemaResultTile from "$lib/components/SchemaResultTile.svelte";
|
||||||
import SchemaSourceTile from "$lib/components/SchemaSourceTile.svelte";
|
import SchemaSourceTile from "$lib/components/SchemaSourceTile.svelte";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import type { FileResult, InputMode, ResultKind } from "$lib/registry";
|
import type { FileResult, InputMode, ResultKind } from "$lib/registry";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -46,7 +47,7 @@
|
|||||||
case "none":
|
case "none":
|
||||||
return "—";
|
return "—";
|
||||||
case "text":
|
case "text":
|
||||||
return "text";
|
return t("textInput.heading");
|
||||||
case "image":
|
case "image":
|
||||||
return source ? `${source.width} × ${source.height} px` : "—";
|
return source ? `${source.width} × ${source.height} px` : "—";
|
||||||
}
|
}
|
||||||
@@ -58,10 +59,10 @@
|
|||||||
: "—"
|
: "—"
|
||||||
: resultKind === "files"
|
: resultKind === "files"
|
||||||
? fileResult
|
? fileResult
|
||||||
? `${fileResult.files.length} parts`
|
? `${fileResult.files.length} ${t("resultCard.parts")}`
|
||||||
: "—"
|
: "—"
|
||||||
: textResult
|
: textResult
|
||||||
? "text"
|
? t("textInput.heading")
|
||||||
: "—",
|
: "—",
|
||||||
);
|
);
|
||||||
const formatValue = $derived(resultKind === "files" ? "ZIP (PNG)" : "PNG");
|
const formatValue = $derived(resultKind === "files" ? "ZIP (PNG)" : "PNG");
|
||||||
@@ -75,7 +76,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="panel-head">
|
<div class="panel-head">
|
||||||
<span class="label">PREVIEW PANEL</span>
|
<span class="label">{t("resultCard.previewPanel")}</span>
|
||||||
<SchemaActions
|
<SchemaActions
|
||||||
{inputMode}
|
{inputMode}
|
||||||
canDownload={hasResult}
|
canDownload={hasResult}
|
||||||
@@ -112,9 +113,9 @@
|
|||||||
<div class="meta">
|
<div class="meta">
|
||||||
<MetaList
|
<MetaList
|
||||||
items={[
|
items={[
|
||||||
{ caption: "SOURCE", value: sourceValue },
|
{ caption: t("sourceCard.source"), value: sourceValue },
|
||||||
{ caption: "RESULT", value: resultValue },
|
{ caption: t("resultCard.result"), value: resultValue },
|
||||||
{ caption: "FORMAT", value: formatValue },
|
{ caption: t("resultCard.format"), value: formatValue },
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -132,6 +133,7 @@
|
|||||||
.label {
|
.label {
|
||||||
font: var(--font-size-s) var(--font-mono);
|
font: var(--font-size-s) var(--font-mono);
|
||||||
letter-spacing: var(--space-text-l);
|
letter-spacing: var(--space-text-l);
|
||||||
|
text-transform: uppercase;
|
||||||
color: var(--color-main);
|
color: var(--color-main);
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toDataUrl } from "$lib/core/io";
|
import { toDataUrl } from "$lib/core/io";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import type { FileResult, ResultKind } from "$lib/registry";
|
import type { FileResult, ResultKind } from "$lib/registry";
|
||||||
import { Check } from "@lucide/svelte";
|
import { Check } from "@lucide/svelte";
|
||||||
import SchemaTextResult from "./SchemaTextResult.svelte";
|
import SchemaTextResult from "./SchemaTextResult.svelte";
|
||||||
@@ -40,7 +41,12 @@
|
|||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PreviewTile label="RESULT" viewMode={resultKind} loading={running} {parts}>
|
<PreviewTile
|
||||||
|
label={t("resultCard.result")}
|
||||||
|
viewMode={resultKind}
|
||||||
|
loading={running}
|
||||||
|
{parts}
|
||||||
|
>
|
||||||
<div class="canvas" class:checker={resultKind === "image"}>
|
<div class="canvas" class:checker={resultKind === "image"}>
|
||||||
{#if resultKind === "text" && textResult}
|
{#if resultKind === "text" && textResult}
|
||||||
<div class="text-result-wrap">
|
<div class="text-result-wrap">
|
||||||
@@ -70,11 +76,11 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{:else if resultKind === "image" && resultUrl}
|
{:else if resultKind === "image" && resultUrl}
|
||||||
<img src={resultUrl} alt="result" />
|
<img src={resultUrl} alt={t("resultCard.alt")} />
|
||||||
{:else if running}
|
{:else if running}
|
||||||
<Check size={22} />
|
<Check size={22} />
|
||||||
{:else}
|
{:else}
|
||||||
<span class="empty">no result yet</span>
|
<span class="empty">{t("resultCard.noResult")}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</PreviewTile>
|
</PreviewTile>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { toDataUrl } from "$lib/core/io";
|
import { toDataUrl } from "$lib/core/io";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import type { InputMode } from "$lib/registry";
|
import type { InputMode } from "$lib/registry";
|
||||||
import PreviewTile from "./layout/PreviewTile.svelte";
|
import PreviewTile from "./layout/PreviewTile.svelte";
|
||||||
import SchemaTextSource from "./SchemaTextSource.svelte";
|
import SchemaTextSource from "./SchemaTextSource.svelte";
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
const sourceUrl = $derived(source ? toDataUrl(source) : null);
|
const sourceUrl = $derived(source ? toDataUrl(source) : null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PreviewTile label="SOURCE" viewMode={mode}>
|
<PreviewTile label={t("sourceCard.source")} viewMode={mode}>
|
||||||
{#if mode === "text"}
|
{#if mode === "text"}
|
||||||
<div class="text-source-wrap">
|
<div class="text-source-wrap">
|
||||||
<SchemaTextSource
|
<SchemaTextSource
|
||||||
@@ -36,11 +37,11 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else if mode === "image" && sourceUrl}
|
{:else if mode === "image" && sourceUrl}
|
||||||
<img src={sourceUrl} alt="source" />
|
<img src={sourceUrl} alt={t("sourceCard.alt")} />
|
||||||
{:else if mode === "image"}
|
{:else if mode === "image"}
|
||||||
<span class="empty">choose an image</span>
|
<span class="empty">{t("sourceCard.chooseImage")}</span>
|
||||||
{:else}
|
{:else}
|
||||||
<span class="empty">no source — configure the parameters</span>
|
<span class="empty">{t("sourceCard.noSource")}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</PreviewTile>
|
</PreviewTile>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Copy, Download, FileText } from "@lucide/svelte";
|
import { Copy, Download, FileText } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import IconButton from "./ui/IconButton.svelte";
|
import IconButton from "./ui/IconButton.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -28,10 +29,10 @@
|
|||||||
|
|
||||||
{#if kind === "verdict"}
|
{#if kind === "verdict"}
|
||||||
<div class="verdict">
|
<div class="verdict">
|
||||||
<span class="verdict-label">RESULT</span>
|
<span class="verdict-label">{t("resultCard.result")}</span>
|
||||||
<div class="badge badge-tone--{tone}" role="status">
|
<div class="badge badge-tone--{tone}" role="status">
|
||||||
<span class="verdict-text">{value}</span>
|
<span class="verdict-text">{value}</span>
|
||||||
<IconButton icon={Copy} label="Copy" onclick={oncopy} />
|
<IconButton icon={Copy} label={t("textResult.copy")} onclick={oncopy} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
@@ -39,10 +40,10 @@
|
|||||||
<div class="result-head">
|
<div class="result-head">
|
||||||
<span class="result-label"><FileText size={14} /> {fileName}</span>
|
<span class="result-label"><FileText size={14} /> {fileName}</span>
|
||||||
<div class="result-actions">
|
<div class="result-actions">
|
||||||
<IconButton icon={Copy} label="Copy" onclick={oncopy} />
|
<IconButton icon={Copy} label={t("textResult.copy")} onclick={oncopy} />
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={Download}
|
icon={Download}
|
||||||
label="Download .txt"
|
label={t("textResult.downloadTxt")}
|
||||||
onclick={ondownload}
|
onclick={ondownload}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
font: var(--font-size-s) var(--font-mono);
|
font: var(--font-size-s) var(--font-mono);
|
||||||
letter-spacing: var(--space-text-l);
|
letter-spacing: var(--space-text-l);
|
||||||
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
.badge {
|
.badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ArrowUp, Upload } from "@lucide/svelte";
|
import { ArrowUp, Upload } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
}
|
}
|
||||||
let {
|
let {
|
||||||
value,
|
value,
|
||||||
placeholder = "Paste base64 / hex / bytes…",
|
placeholder = t("textSource.placeholder"),
|
||||||
disabled = false,
|
disabled = false,
|
||||||
oninput,
|
oninput,
|
||||||
onrender,
|
onrender,
|
||||||
@@ -35,11 +36,13 @@
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
{#if onsample}
|
{#if onsample}
|
||||||
<button type="button" class="secondary" onclick={onsample}>
|
<button type="button" class="secondary" onclick={onsample}>
|
||||||
<Upload size={14} /> Try sample
|
<Upload size={14} />
|
||||||
|
{t("textSource.trySample")}
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<button type="button" class="render" onclick={onrender} {disabled}>
|
<button type="button" class="render" onclick={onrender} {disabled}>
|
||||||
<ArrowUp size={14} /> Render text
|
<ArrowUp size={14} />
|
||||||
|
{t("textSource.render")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import { decodeFile, encode } from "$lib/core/io";
|
import { decodeFile, encode } from "$lib/core/io";
|
||||||
import type { PixelImage } from "$lib/core/types";
|
import type { PixelImage } from "$lib/core/types";
|
||||||
import { execute } from "$lib/executor";
|
import { execute } from "$lib/executor";
|
||||||
|
import { toolDescription, toolTitle } from "$lib/i18n/schema-tool-strings";
|
||||||
import { t } from "$lib/i18n/t";
|
import { t } from "$lib/i18n/t";
|
||||||
import type { FileResult, ToolEntry } from "$lib/registry";
|
import type { FileResult, ToolEntry } from "$lib/registry";
|
||||||
import {
|
import {
|
||||||
@@ -161,16 +162,16 @@
|
|||||||
<div class="schema-tool">
|
<div class="schema-tool">
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<div class="title-block">
|
<div class="title-block">
|
||||||
<h1>{tool.title}</h1>
|
<h1>{toolTitle(tool)}</h1>
|
||||||
<p class="lede">{tool.description}</p>
|
<p class="lede">{toolDescription(tool)}</p>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="workspace">
|
<div class="workspace">
|
||||||
<section class="panel settings">
|
<section class="panel settings">
|
||||||
<div class="panel-head">
|
<div class="panel-head">
|
||||||
<span class="label">TOOL SETTINGS</span>
|
<span class="label">{t("paramsCard.toolSettings")}</span>
|
||||||
<strong>Configure output</strong>
|
<strong>{t("paramsCard.configureOutput")}</strong>
|
||||||
</div>
|
</div>
|
||||||
<SchemaFields {schema} {values} onchange={setValue} onreset={reset} />
|
<SchemaFields {schema} {values} onchange={setValue} onreset={reset} />
|
||||||
</section>
|
</section>
|
||||||
@@ -199,7 +200,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="no-schema">This tool has no schema yet.</p>
|
<p class="no-schema">{t("paramsCard.noSchema")}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -223,6 +224,7 @@
|
|||||||
.label {
|
.label {
|
||||||
font: var(--font-size-s) var(--font-mono);
|
font: var(--font-size-s) var(--font-mono);
|
||||||
letter-spacing: var(--space-text-l);
|
letter-spacing: var(--space-text-l);
|
||||||
|
text-transform: uppercase;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
.label {
|
.label {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Ellipsis, GripVertical, X } from "@lucide/svelte";
|
import { Ellipsis, GripVertical, X } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="step-remove"
|
class="step-remove"
|
||||||
aria-label="Remove step"
|
aria-label={t("chain.removeStepAria")}
|
||||||
onclick={() => onremove?.()}
|
onclick={() => onremove?.()}
|
||||||
>
|
>
|
||||||
<X size={16} />
|
<X size={16} />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { InputMode, ResultKind } from "$lib/registry";
|
import type { InputMode, ResultKind } from "$lib/registry";
|
||||||
import { RefreshCw } from "@lucide/svelte";
|
import { RefreshCw } from "@lucide/svelte";
|
||||||
|
import { t } from "$lib/i18n/t";
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
<span>
|
<span>
|
||||||
{label}
|
{label}
|
||||||
{#if parts !== undefined}
|
{#if parts !== undefined}
|
||||||
<span class="count">{parts} parts</span>
|
<span class="count">{parts} {t("resultCard.parts")}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<RefreshCw class="rotating" size="12" />
|
<RefreshCw class="rotating" size="12" />
|
||||||
@@ -45,6 +46,7 @@
|
|||||||
gap: var(--space-m);
|
gap: var(--space-m);
|
||||||
margin-bottom: var(--space-m);
|
margin-bottom: var(--space-m);
|
||||||
font: var(--font-size-s) var(--font-mono);
|
font: var(--font-size-s) var(--font-mono);
|
||||||
|
text-transform: uppercase;
|
||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
& :global(.rotating) {
|
& :global(.rotating) {
|
||||||
animation: rotate var(--duration-l) linear infinite;
|
animation: rotate var(--duration-l) linear infinite;
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ export const en: Dict = {
|
|||||||
removeStepAria: "Remove step",
|
removeStepAria: "Remove step",
|
||||||
},
|
},
|
||||||
sourceCard: {
|
sourceCard: {
|
||||||
|
source: "Source",
|
||||||
replaceImage: "Replace image",
|
replaceImage: "Replace image",
|
||||||
chooseImage: "choose an image",
|
chooseImage: "choose an image",
|
||||||
noSource: "no source — configure the parameters",
|
noSource: "no source — configure the parameters",
|
||||||
@@ -71,11 +72,14 @@ export const en: Dict = {
|
|||||||
nextTool: "⛓ Next tool",
|
nextTool: "⛓ Next tool",
|
||||||
breakChain: "✂ Break the chain",
|
breakChain: "✂ Break the chain",
|
||||||
noResult: "no result yet",
|
noResult: "no result yet",
|
||||||
|
result: "Result",
|
||||||
|
previewPanel: "Preview",
|
||||||
parts: "parts",
|
parts: "parts",
|
||||||
format: "Format",
|
format: "Format",
|
||||||
alt: "Result image",
|
alt: "Result image",
|
||||||
},
|
},
|
||||||
paramsCard: {
|
paramsCard: {
|
||||||
|
toolSettings: "Tool settings",
|
||||||
noParams: "This tool has no parameters — the result is ready as is.",
|
noParams: "This tool has no parameters — the result is ready as is.",
|
||||||
configureOutput: "Configure output",
|
configureOutput: "Configure output",
|
||||||
noSchema: "This tool has no schema yet.",
|
noSchema: "This tool has no schema yet.",
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export const ru: Dict = {
|
|||||||
removeStepAria: "Убрать шаг",
|
removeStepAria: "Убрать шаг",
|
||||||
},
|
},
|
||||||
sourceCard: {
|
sourceCard: {
|
||||||
|
source: "Источник",
|
||||||
replaceImage: "Заменить изображение",
|
replaceImage: "Заменить изображение",
|
||||||
chooseImage: "выберите изображение",
|
chooseImage: "выберите изображение",
|
||||||
noSource: "нет источника — настройте параметры",
|
noSource: "нет источника — настройте параметры",
|
||||||
@@ -72,11 +73,14 @@ export const ru: Dict = {
|
|||||||
nextTool: "⛓ Следующий инструмент",
|
nextTool: "⛓ Следующий инструмент",
|
||||||
breakChain: "✂ Оборвать цепочку",
|
breakChain: "✂ Оборвать цепочку",
|
||||||
noResult: "результата пока нет",
|
noResult: "результата пока нет",
|
||||||
|
result: "Результат",
|
||||||
|
previewPanel: "Предпросмотр",
|
||||||
parts: "частей",
|
parts: "частей",
|
||||||
format: "Формат",
|
format: "Формат",
|
||||||
alt: "Изображение-результат",
|
alt: "Изображение-результат",
|
||||||
},
|
},
|
||||||
paramsCard: {
|
paramsCard: {
|
||||||
|
toolSettings: "Настройки инструмента",
|
||||||
noParams: "У этого инструмента нет параметров — результат уже готов.",
|
noParams: "У этого инструмента нет параметров — результат уже готов.",
|
||||||
configureOutput: "Настроить вывод",
|
configureOutput: "Настроить вывод",
|
||||||
noSchema: "У этого инструмента пока нет схемы.",
|
noSchema: "У этого инструмента пока нет схемы.",
|
||||||
|
|||||||
Reference in New Issue
Block a user