mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-09-14 21:46:35 +00:00
fix: worker use only i18n keys instead of full i18n
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
fileResult?: FileResult | null;
|
||||
textSource?: string;
|
||||
textResult?: string | null;
|
||||
textVars?: Record<string, string | number>;
|
||||
running?: boolean;
|
||||
error?: string;
|
||||
onupload: (file: File) => void;
|
||||
@@ -34,6 +35,7 @@
|
||||
fileResult = null,
|
||||
textSource = "",
|
||||
textResult = null,
|
||||
textVars = undefined,
|
||||
running = false,
|
||||
error = "",
|
||||
onupload,
|
||||
@@ -106,6 +108,7 @@
|
||||
{result}
|
||||
{fileResult}
|
||||
{textResult}
|
||||
{textVars}
|
||||
{toolId}
|
||||
{running}
|
||||
oncopy={oncopytext}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
result: PixelImage | null;
|
||||
fileResult?: FileResult | null;
|
||||
textResult?: string | null;
|
||||
textVars?: Record<string, string | number>;
|
||||
toolId?: string;
|
||||
running?: boolean;
|
||||
oncopy?: () => void;
|
||||
@@ -22,6 +23,7 @@
|
||||
result,
|
||||
fileResult = null,
|
||||
textResult = null,
|
||||
textVars = undefined,
|
||||
toolId = "",
|
||||
running = false,
|
||||
oncopy,
|
||||
@@ -64,6 +66,7 @@
|
||||
<SchemaTextResult
|
||||
value={textResult}
|
||||
kind="verdict"
|
||||
vars={textVars}
|
||||
{toolId}
|
||||
oncopy={oncopy ?? (() => {})}
|
||||
ondownload={ondownloadtxt ?? (() => {})}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { Copy, Download, FileText } from "@lucide/svelte";
|
||||
import { verdictTone, verdictText } from "$lib/i18n/schema-tool-strings";
|
||||
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";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
kind: "text" | "verdict";
|
||||
toolId?: string;
|
||||
vars?: Record<string, string | number>;
|
||||
fileName?: string;
|
||||
oncopy: () => void;
|
||||
ondownload: () => void;
|
||||
@@ -16,6 +17,7 @@
|
||||
value,
|
||||
kind,
|
||||
toolId = "",
|
||||
vars = undefined,
|
||||
fileName = "result.txt",
|
||||
oncopy,
|
||||
ondownload,
|
||||
@@ -23,7 +25,7 @@
|
||||
|
||||
const tone = $derived(verdictTone(value));
|
||||
const displayValue = $derived(
|
||||
kind === "verdict" && toolId ? verdictText(toolId, value) : value,
|
||||
kind === "verdict" && toolId ? verdictText(toolId, value, vars) : value,
|
||||
);
|
||||
</script>
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@
|
||||
let fileResult = $state<FileResult | null>(null);
|
||||
let textSource = $state("");
|
||||
let textResult = $state<string | null>(null);
|
||||
let verdictVars = $state<Record<string, string | number> | undefined>(
|
||||
undefined,
|
||||
);
|
||||
let running = $state(false);
|
||||
let error = $state("");
|
||||
let started = $state(false);
|
||||
@@ -59,6 +62,7 @@
|
||||
result = null;
|
||||
fileResult = null;
|
||||
textResult = null;
|
||||
verdictVars = undefined;
|
||||
error = "";
|
||||
}
|
||||
|
||||
@@ -90,7 +94,16 @@
|
||||
result = null;
|
||||
textResult = null;
|
||||
} else {
|
||||
textResult = out as string;
|
||||
if (typeof out === "string") {
|
||||
textResult = out;
|
||||
verdictVars = undefined;
|
||||
} else if (out && "key" in out) {
|
||||
textResult = out.key;
|
||||
verdictVars = out.vars;
|
||||
} else {
|
||||
textResult = null;
|
||||
verdictVars = undefined;
|
||||
}
|
||||
result = null;
|
||||
fileResult = null;
|
||||
}
|
||||
@@ -125,7 +138,9 @@
|
||||
async function copyText() {
|
||||
if (!textResult) return;
|
||||
const copyValue =
|
||||
resultKind === "verdict" ? verdictText(tool.id, textResult) : textResult;
|
||||
resultKind === "verdict"
|
||||
? verdictText(tool.id, textResult, verdictVars)
|
||||
: textResult;
|
||||
try {
|
||||
await navigator.clipboard.writeText(copyValue);
|
||||
} catch (e) {
|
||||
@@ -196,6 +211,7 @@
|
||||
{fileResult}
|
||||
{textSource}
|
||||
{textResult}
|
||||
textVars={verdictVars}
|
||||
{inputMode}
|
||||
{resultKind}
|
||||
{running}
|
||||
|
||||
@@ -95,6 +95,7 @@ function ensureWorker(): Worker | null {
|
||||
data: Uint8ClampedArray;
|
||||
}[];
|
||||
text?: string;
|
||||
textVars?: Record<string, string | number>;
|
||||
error?: string;
|
||||
errorKey?: string;
|
||||
errorVars?: Record<string, string | number>;
|
||||
@@ -120,7 +121,11 @@ function ensureWorker(): Worker | null {
|
||||
})),
|
||||
});
|
||||
} else if (payload.ok && typeof payload.text === "string") {
|
||||
entry.resolve(payload.text);
|
||||
entry.resolve(
|
||||
payload.textVars
|
||||
? { key: payload.text, vars: payload.textVars }
|
||||
: payload.text,
|
||||
);
|
||||
} else if (payload.errorKey) {
|
||||
entry.reject(new ToolError(payload.errorKey, payload.errorVars));
|
||||
} else {
|
||||
|
||||
@@ -20,6 +20,7 @@ type WorkerResponse =
|
||||
height?: number;
|
||||
data?: Uint8ClampedArray;
|
||||
text?: string;
|
||||
textVars?: Record<string, string | number>;
|
||||
files?: WorkerImageFile[];
|
||||
}
|
||||
| {
|
||||
@@ -67,6 +68,15 @@ async function handle(request: WorkerRequest): Promise<void> {
|
||||
} satisfies WorkerResponse);
|
||||
return;
|
||||
}
|
||||
if ("key" in output) {
|
||||
(self as unknown as Worker).postMessage({
|
||||
id: request.id,
|
||||
ok: true,
|
||||
text: output.key,
|
||||
textVars: output.vars,
|
||||
} satisfies WorkerResponse);
|
||||
return;
|
||||
}
|
||||
if ("files" in output) {
|
||||
const files = output.files.map((file) => ({
|
||||
name: file.name,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { ToolEntry } from "$lib/registry";
|
||||
import type { Field } from "$lib/registry-schema";
|
||||
import { ru } from "./ru";
|
||||
import { getMergedDict } from "./locale.svelte";
|
||||
import type { SearchDoc } from "./matching";
|
||||
import { t } from "./t";
|
||||
import { normalizeForSearch, scoreDoc, type SearchDoc } from "./matching";
|
||||
import { ru } from "./ru";
|
||||
import { interpolate, t } from "./t";
|
||||
|
||||
function labelOf(id: string): string {
|
||||
return id
|
||||
@@ -43,8 +43,15 @@ export function optionLabel(
|
||||
return getMergedDict().tools[toolId]?.options?.[fieldId]?.[value] ?? fallback;
|
||||
}
|
||||
|
||||
export function verdictText(toolId: string, key: string): string {
|
||||
return getMergedDict().tools[toolId]?.results?.[key] ?? key;
|
||||
export function verdictText(
|
||||
toolId: string,
|
||||
key: string,
|
||||
vars?: Record<string, string | number>,
|
||||
): string {
|
||||
return interpolate(
|
||||
getMergedDict().tools[toolId]?.results?.[key] ?? key,
|
||||
vars,
|
||||
);
|
||||
}
|
||||
|
||||
export function verdictTone(key: string): "success" | "danger" | "info" {
|
||||
@@ -73,3 +80,17 @@ export function toolSearchDoc(tool: ToolEntry): SearchDoc {
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Кросс-языковой поиск по каталогу: скор через `scoreDoc` на
|
||||
* `toolSearchDoc` (заголовки/описания всех локалей). Порядок каталога
|
||||
* сохраняется, элементы без совпадения отбрасываются.
|
||||
*/
|
||||
export function searchTools(
|
||||
tools: readonly ToolEntry[],
|
||||
query: string,
|
||||
): ToolEntry[] {
|
||||
const q = normalizeForSearch(query.trim());
|
||||
if (q.length === 0) return [...tools];
|
||||
return tools.filter((tool) => scoreDoc(toolSearchDoc(tool), q) !== null);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
renderPredicateMask,
|
||||
} from "../core/masks";
|
||||
import { base64ToBytes, looksLikePng, stripDataUri } from "../core/textio";
|
||||
import { t } from "../i18n/t";
|
||||
import { field, toolSchema } from "../registry-schema";
|
||||
import { imgTool, textGen, type ToolEntry } from "./types";
|
||||
|
||||
@@ -277,7 +276,7 @@ const pngFileSize: ToolEntry<NoParams> = {
|
||||
const blob = await encode(img, "image/png");
|
||||
const kb = blob.size / 1024;
|
||||
const kbText = kb >= 100 ? Math.round(kb).toString() : kb.toFixed(1);
|
||||
return t("tools.png-file-size.results.line", { kb: kbText });
|
||||
return { key: "line", vars: { kb: kbText } };
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import type { ToolEntry } from "./types";
|
||||
import { geometryEntries } from "./geometry";
|
||||
import { alphaEntries } from "./alpha";
|
||||
import { convertEntries } from "./convert";
|
||||
import { analyzeEntries } from "./analyze";
|
||||
import { filtersEntries } from "./filters";
|
||||
import { colorEntries } from "./color";
|
||||
import { convertEntries } from "./convert";
|
||||
import { filtersEntries } from "./filters";
|
||||
import { generateEntries } from "./generate";
|
||||
import { geometryEntries } from "./geometry";
|
||||
import { textEntries } from "./text";
|
||||
import type { ToolEntry } from "./types";
|
||||
|
||||
export {
|
||||
genTool,
|
||||
imgTool,
|
||||
textGen,
|
||||
INPUT_MODES,
|
||||
requireSource,
|
||||
requireText,
|
||||
INPUT_MODES,
|
||||
RESULT_KINDS,
|
||||
textGen,
|
||||
} from "./types";
|
||||
export type {
|
||||
FileResult,
|
||||
InputMode,
|
||||
ResultKind,
|
||||
ToolContext,
|
||||
ToolEntry,
|
||||
ToolResult,
|
||||
ToolImageFile,
|
||||
FileResult,
|
||||
ToolResult,
|
||||
VerdictResult,
|
||||
} from "./types";
|
||||
|
||||
export const TOOLS: ToolEntry[] = [
|
||||
|
||||
@@ -49,7 +49,18 @@ export interface FileResult {
|
||||
files: ToolImageFile[];
|
||||
}
|
||||
|
||||
export type ToolResult = PixelImage | string | FileResult;
|
||||
/**
|
||||
* Вердикт с подстановками: `key` — ключ словаря (`tools[id].results[key]`),
|
||||
* `vars` — значения для интерполяции `{name}`. Локализация выполняется на
|
||||
* стороне UI; из run() (и тем более из worker) локализованные строки не
|
||||
* возвращаются.
|
||||
*/
|
||||
export interface VerdictResult {
|
||||
key: string;
|
||||
vars?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
export type ToolResult = PixelImage | string | FileResult | VerdictResult;
|
||||
|
||||
/**
|
||||
* Инструмент нового registry: полностью типизирован на `Params`, схема —
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
import CatalogHeader from "$lib/components/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/ToolCard.svelte";
|
||||
import {
|
||||
searchTools,
|
||||
toolDescription,
|
||||
toolTitle,
|
||||
} from "$lib/i18n/schema-tool-strings";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import { TOOL_ICONS } from "$lib/tool-icons";
|
||||
|
||||
@@ -14,12 +19,8 @@
|
||||
PREVIEW_GROUPS.map((g) => ({
|
||||
id: g.id,
|
||||
label: t(`categories.${g.id}`),
|
||||
tools: g.tools.filter(
|
||||
(t) =>
|
||||
(category === "all" || category === g.id) &&
|
||||
(query.trim() === "" ||
|
||||
t.title.toLowerCase().includes(query.trim().toLowerCase()) ||
|
||||
t.description.toLowerCase().includes(query.trim().toLowerCase())),
|
||||
tools: searchTools(g.tools, query).filter(
|
||||
() => category === "all" || category === g.id,
|
||||
),
|
||||
})).filter((g) => g.tools.length > 0),
|
||||
);
|
||||
@@ -35,9 +36,9 @@
|
||||
<CatalogGroup label={group.label} count={group.tools.length}>
|
||||
{#each group.tools as tool, i (tool.id)}
|
||||
<ToolCard
|
||||
title={tool.title}
|
||||
title={toolTitle(tool)}
|
||||
id={tool.id}
|
||||
description={tool.description}
|
||||
description={toolDescription(tool)}
|
||||
index={i + 1}
|
||||
icon={TOOL_ICONS[tool.id]}
|
||||
/>
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
import CatalogHeader from "$lib/components/CatalogHeader.svelte";
|
||||
import CatalogToolbar from "$lib/components/CatalogToolbar.svelte";
|
||||
import ToolCard from "$lib/components/ToolCard.svelte";
|
||||
import {
|
||||
searchTools,
|
||||
toolDescription,
|
||||
toolTitle,
|
||||
} from "$lib/i18n/schema-tool-strings";
|
||||
import { t } from "$lib/i18n/t";
|
||||
import { TOOL_ICONS } from "$lib/tool-icons";
|
||||
|
||||
@@ -14,12 +19,8 @@
|
||||
PREVIEW_GROUPS.map((g) => ({
|
||||
id: g.id,
|
||||
label: t(`categories.${g.id}`),
|
||||
tools: g.tools.filter(
|
||||
(t) =>
|
||||
(category === "all" || category === g.id) &&
|
||||
(query.trim() === "" ||
|
||||
t.title.toLowerCase().includes(query.trim().toLowerCase()) ||
|
||||
t.description.toLowerCase().includes(query.trim().toLowerCase())),
|
||||
tools: searchTools(g.tools, query).filter(
|
||||
() => category === "all" || category === g.id,
|
||||
),
|
||||
})).filter((g) => g.tools.length > 0),
|
||||
);
|
||||
@@ -33,9 +34,9 @@
|
||||
<CatalogGroup label={group.label} count={group.tools.length}>
|
||||
{#each group.tools as tool, i (tool.id)}
|
||||
<ToolCard
|
||||
title={tool.title}
|
||||
title={toolTitle(tool)}
|
||||
id={tool.id}
|
||||
description={tool.description}
|
||||
description={toolDescription(tool)}
|
||||
index={i + 1}
|
||||
icon={TOOL_ICONS[tool.id]}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user