diff --git a/web/src/lib/components/schema/PreviewTile.svelte b/web/src/lib/components/schema/PreviewTile.svelte
index 304eb5f..b848528 100644
--- a/web/src/lib/components/schema/PreviewTile.svelte
+++ b/web/src/lib/components/schema/PreviewTile.svelte
@@ -3,6 +3,7 @@
import { RefreshCw } from "@lucide/svelte";
import { t } from "$lib/i18n/t";
import Badge from "$lib/components/ui/Badge.svelte";
+ import DetailBadge from "$lib/components/ui/DetailBadge.svelte";
import type { Snippet } from "svelte";
interface Props {
@@ -15,11 +16,11 @@
dims?: string;
note?: string;
noteTone?: "warning" | "info";
- // Runtime warnings (chain/download events): shown as badges next to the
- // derived result note, but from a different source.
- warnings?: string[];
- // Runtime errors: danger badges; detail goes to the native tooltip until
- // a popover with full text exists.
+ // Runtime warnings (chain/source/download events): shown as badges next
+ // to the derived result note, full message in the "?" popover.
+ warnings?: { label: string; detail?: string }[];
+ // Runtime errors: danger badges; the full message lives in the
+ // DetailBadge popover behind the "?" button.
errors?: { label: string; detail?: string }[];
actions?: Snippet;
}
@@ -61,15 +62,20 @@
{/if}
{#each errors as error (error.label + (error.detail ?? ""))}
-
{/each}
- {#each warnings as warning (warning)}
-
+ {#each warnings as warning (warning.label + (warning.detail ?? ""))}
+
{/each}
diff --git a/web/src/lib/components/schema/SchemaActions.svelte b/web/src/lib/components/schema/SchemaActions.svelte
index f4efaa7..84b2eb4 100644
--- a/web/src/lib/components/schema/SchemaActions.svelte
+++ b/web/src/lib/components/schema/SchemaActions.svelte
@@ -1,7 +1,7 @@
+
+
+
+ {#if detail}
+
+ (open = e.newState === "open")}
+ >
+ {detail}
+
+ {/if}
+
+
+
diff --git a/web/src/lib/i18n/en.ts b/web/src/lib/i18n/en.ts
index e58bad6..2946365 100644
--- a/web/src/lib/i18n/en.ts
+++ b/web/src/lib/i18n/en.ts
@@ -70,6 +70,10 @@ export const en: Dict = {
warnSizeLimitMiss:
"Could not fit into {limit} KB — downloaded file is {actual} KB",
stepFailed: "Step {n} failed",
+ warnPartialShort: "Some files failed",
+ warnFirstOnlyShort: "First file only",
+ warnSourceSkipShort: "Files skipped",
+ warnSizeLimitMissShort: "Over size limit",
},
savedChains: {
pageTitle: "Saved pipelines — easy-png-tools",
@@ -172,6 +176,7 @@ export const en: Dict = {
nothingFound: "Nothing found — try another word.",
},
ui: {
+ detailsAria: "Show details",
showMask: "Show mask",
decrease: "Decrease",
increase: "Increase",
diff --git a/web/src/lib/i18n/ru.ts b/web/src/lib/i18n/ru.ts
index 9b6a9ca..516ed77 100644
--- a/web/src/lib/i18n/ru.ts
+++ b/web/src/lib/i18n/ru.ts
@@ -72,6 +72,10 @@ export const ru: Dict = {
warnSizeLimitMiss:
"Не удалось ужаться до {limit} КБ — скачан файл на {actual} КБ",
stepFailed: "Шаг {n}: ошибка",
+ warnPartialShort: "Часть файлов с ошибкой",
+ warnFirstOnlyShort: "Только первый файл",
+ warnSourceSkipShort: "Файлы пропущены",
+ warnSizeLimitMissShort: "Больше лимита",
},
savedChains: {
pageTitle: "Сохранённые пайплайны — easy-png-tools",
@@ -173,6 +177,7 @@ export const ru: Dict = {
nothingFound: "Ничего не найдено — попробуйте другое слово.",
},
ui: {
+ detailsAria: "Показать подробности",
showMask: "Показать маску",
decrease: "Уменьшить",
increase: "Увеличить",
diff --git a/web/src/lib/i18n/schema-tool-strings.ts b/web/src/lib/i18n/schema-tool-strings.ts
index 9bfd35c..29c6c4b 100644
--- a/web/src/lib/i18n/schema-tool-strings.ts
+++ b/web/src/lib/i18n/schema-tool-strings.ts
@@ -68,23 +68,41 @@ export function verdictTone(key: string): "success" | "danger" | "info" {
return "info";
}
-/** Human-readable text for a chain run/source warning. */
-export function chainWarningText(w: ChainWarning): string {
+/** Short label for a warning badge; detail holds the full message (popover). */
+export interface WarningNotice {
+ label: string;
+ detail: string;
+}
+
+/** Human-readable badge + popover text for a chain run/source warning. */
+export function chainWarningNotice(w: ChainWarning): WarningNotice {
switch (w.kind) {
case "partial":
- return t("chain.warnPartial", { n: w.step, ok: w.ok, total: w.total });
+ return {
+ label: t("chain.warnPartialShort"),
+ detail: t("chain.warnPartial", { n: w.step, ok: w.ok, total: w.total }),
+ };
case "firstOnly":
- return t("chain.warnFirstOnly", { n: w.step, total: w.total });
+ return {
+ label: t("chain.warnFirstOnlyShort"),
+ detail: t("chain.warnFirstOnly", { n: w.step, total: w.total }),
+ };
case "sourceSkip":
- return t("chain.warnSourceSkip", {
- skipped: w.skipped,
- total: w.total,
- });
+ return {
+ label: t("chain.warnSourceSkipShort"),
+ detail: t("chain.warnSourceSkip", {
+ skipped: w.skipped,
+ total: w.total,
+ }),
+ };
case "sizeLimitMiss":
- return t("chain.warnSizeLimitMiss", {
- limit: w.limitKb,
- actual: w.actualKb,
- });
+ return {
+ label: t("chain.warnSizeLimitMissShort"),
+ detail: t("chain.warnSizeLimitMiss", {
+ limit: w.limitKb,
+ actual: w.actualKb,
+ }),
+ };
}
}