chore: clear lint rules warns: comments, tests

This commit is contained in:
2026-10-08 12:55:34 +05:00
parent 0e8c2478a1
commit 7361a82c18
93 changed files with 899 additions and 940 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
interface Props {
title: string;
description: string;
/** Путь страницы от корня роутера ("/", "/list-tools", "/tools/…"). */
/** Page path from the router root ("/", "/list-tools", "/tools/..."). */
path: string;
}
let { title, description, path }: Props = $props();
@@ -13,7 +13,7 @@
label: string;
value: unknown;
spec: FieldSpec;
/** Размеры входа шага: потолок осей для maxFromSource-полей. */
/** Step input dimensions: axis cap for maxFromSource fields. */
sourceDims?: Dimension;
onchange?: (value: Dimension, axis?: "width" | "height" | "both") => void;
}
@@ -51,7 +51,7 @@
}
function applyPreset(n: number) {
// Пресет задаёт обе оси явно — lockAspect его не пересчитывает.
// Preset sets both axes explicitly; lockAspect does not recompute it.
onchange?.({ width: n, height: n }, "both");
}
</script>
@@ -39,8 +39,8 @@
let root = $state<HTMLDivElement | null>(null);
function choose(mime: OutputMime) {
// Меню — ещё и панель настроек формата: не закрываем при выборе,
// чтобы можно было сразу настроить качество.
// Menu doubles as a format-settings panel: keep it open on select
// so quality can be tuned right away.
onformat(mime);
}
@@ -58,7 +58,7 @@
schema: ToolSchema<Record<string, unknown>>;
values: Record<string, unknown>;
toolId: string;
/** Размеры входа шага: для maxFromSource-полей (crop и т.п.). */
/** Step input dimensions: for maxFromSource fields (crop, etc.). */
sourceDims?: Dimension;
onchange: (
id: string,
@@ -50,7 +50,7 @@
let parts = $derived(
resultKind === "files" && fileResult ? fileResult.files.length : undefined,
);
// Размеры одинаковы у всех частей (канвас дополняется) — показываем один раз.
// All parts share the same size (canvas is padded); show it once.
let partsDims = $derived(
resultKind === "files" && fileResult && fileResult.files.length > 0
? `${fileResult.files[0].image.width} × ${fileResult.files[0].image.height}`
@@ -33,7 +33,7 @@
let dragging = $state(false);
let input = $state<HTMLInputElement | null>(null);
// Подсказка замены: показываем на ховер и гасим через 10с до нового захода.
// Replace hint: show on hover, fade after 10s until the next hover.
let hintVisible = $state(false);
let hintTimer: ReturnType<typeof setTimeout> | null = null;
@@ -76,10 +76,10 @@
const inputMode = $derived(tool.input);
let steps = $state<ChainStep[]>([]);
// Поля, изменённые пользователем (по шагам): при загрузке нового файла им не
// даём перезаписаться source-дефолтами. Reset шага снимает пометку.
// Fields edited by the user (per step): on new file load they must not be
// overwritten by source defaults. Step reset clears the mark.
const touchedByStep = new SvelteMap<string, SvelteSet<string>>();
// Последняя правленая ось dimension-поля — ведущая при lockAspect.
// Last edited axis of a dimension field; leads under lockAspect.
const lastAxis: Record<string, "width" | "height"> = {};
let stepDims = $state<(Dimension | undefined)[]>([]);
let stepResults = $state<(PixelImage | null)[]>([]);
@@ -96,7 +96,7 @@
let displayError = $state<DisplayError | null>(null);
let initedFor = $state("");
let format = $state<OutputMime>("image/png");
// Качество lossy-форматов для инструментов без quality-параметра в схеме.
// Lossy-format quality for tools without a quality param in the schema.
let formatQuality = $state<Record<string, number>>({});
let dragFrom = $state<number | null>(null);
@@ -105,7 +105,7 @@
tool,
);
const resultKind = $derived(lastTool.result ?? "image");
// Цепочку можно продолжить, только если последний шаг отдаёт картинку.
// Chain can be extended only if the last step outputs an image.
const canExtend = $derived((lastTool.result ?? "image") === "image");
const alignedMode = $derived(
aligned && resultKind === "image" && steps.length > 1,
@@ -117,8 +117,8 @@
!outputFormatByMime(format).supportsAlpha &&
hasTransparency(result),
);
// Если в схеме последнего шага есть quality-параметр (convert-инструменты),
// он — единый источник качества; dropdown редактирует его же.
// If the last step's schema has a quality param (convert tools), it is the
// single source of quality; the dropdown edits the same value.
const qualityParamId = $derived(lastTool.output?.qualityParamId);
const lastParams = $derived(steps.at(-1)?.params);
const resultNote = $derived(
@@ -196,8 +196,8 @@
const next: Record<string, unknown> = { ...step.params, [id]: value };
const stepSchema = toolSchemaOf(step);
const spec = stepSchema?.fields[id]?.spec;
// Аспект считается от входа шага: для первого — исходник, для остальных —
// результат предыдущего шага (размеры известны после прогона).
// Aspect derives from the step input: source for the first step,
// previous step's result for the rest (dims known after the run).
const dims = index === 0 ? (source ?? undefined) : stepDims[index - 1];
if (dims && stepSchema && spec) {
const aspect = dims.width / dims.height;
@@ -213,7 +213,7 @@
aspect,
);
} else if (spec.kind === "checkbox" && value === true) {
// Включили lockAspect — сразу подгоняем привязанные поля под аспект.
// lockAspect just enabled: snap bound fields to the aspect at once.
for (const [fid, f] of Object.entries(stepSchema.fields)) {
const fs = f.spec;
if (fs.kind === "dimension" && fs.lockAspectWith === id) {
@@ -261,8 +261,8 @@
steps = steps.with(index, { ...step, collapsed: !step.collapsed });
}
// Замена инструмента на месте: ключ и свёрнутость шага сохраняются,
// параметры — дефолты нового инструмента, вход пересчитывается при прогоне.
// In-place tool swap: step key and collapsed state are kept, params become
// the new tool's defaults, input is recomputed on run.
function replaceStepTool(index: number, toolId: string) {
const step = steps[index];
const next = createStep(toolId);
@@ -433,7 +433,7 @@
init();
});
// Цепочка персистится по slug страницы; сохраняем после инициализации.
// Chain persists per page slug; save only after initialization.
$effect(() => {
if (!initedFor) return;
void steps;
@@ -18,12 +18,12 @@ function input(over: Partial<SchemaPreviewModelInput> = {}) {
}
describe("hasPreviewResult", () => {
it("image: результат только при наличии картинки", () => {
it("image: result only when image present", () => {
expect(hasPreviewResult(input({ result: image }))).toBe(true);
expect(hasPreviewResult(input())).toBe(false);
});
it("files: результат только при непустом наборе", () => {
it("files: result only for non-empty set", () => {
const fileResult = { files: [{ name: "part.png", image }] };
expect(hasPreviewResult(input({ resultKind: "files", fileResult }))).toBe(
true,
@@ -36,7 +36,7 @@ describe("hasPreviewResult", () => {
});
it.each(["text", "verdict"] as const)(
"%s: результат по наличию текста",
"%s: result depends on text presence",
(resultKind) => {
expect(hasPreviewResult(input({ resultKind, textResult: "done" }))).toBe(
true,
@@ -9,7 +9,7 @@ export type SchemaPreviewModelInput = {
textResult: string | null;
};
/** Можно ли скачать результат: он есть и он не пустой. */
/** Whether the result is downloadable: it exists and is non-empty. */
export function hasPreviewResult(input: SchemaPreviewModelInput): boolean {
const { resultKind, result, fileResult, textResult } = input;
if (resultKind === "image") return Boolean(result);