From 8cf6e1d91e46a354e7e2e084ca65309458a319e8 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Tue, 28 Jul 2026 22:52:56 +0200 Subject: [PATCH 1/4] fix(feature-flags): recognize dedicated per-feature UI toggles in the parity audit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit audit-feature-parity.ts's "has a UI toggle" check only looked at the generic FeatureFlagsSection.tsx catalog rendering, so enableIdbAtRestEncryption — deliberately hidden from that generic list because toggling it without the passphrase setup flow would lock users out — was reported as "no UI toggle" even though it has its own dedicated one in PrivacySection.tsx. Added extractDedicatedUiFlags(), which reads each catalog entry's existing gateLocations field (already documents "components/settings/PrivacySection.tsx:20") and treats a gateLocation under components/settings/ (other than FeatureFlagsSection.tsx itself) as a real UI toggle. The audit now reports this as informational (shown distinctly as ◆ in the table) instead of a warning; 0 warnings instead of 1. --- scripts/audit-feature-parity.ts | 54 +++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/scripts/audit-feature-parity.ts b/scripts/audit-feature-parity.ts index 768fd2f2..66f749b0 100644 --- a/scripts/audit-feature-parity.ts +++ b/scripts/audit-feature-parity.ts @@ -134,6 +134,32 @@ function extractSectionFlags(sectionSrc: string, catalogSrc: string): Set !hidden.has(flag))); } +// QNBS-v3: a flag hidden from FeatureFlagsSection.tsx (e.g. enableIdbAtRestEncryption, whose toggle +// would lock users out without the passphrase setup flow) can still have a real UI toggle — just a +// dedicated one elsewhere (PrivacySection.tsx), not the generic catalog-driven list. Each catalog +// entry's own `gateLocations` already documents this; a flag counts as having a dedicated toggle if +// any gateLocation file lives under components/settings/ and isn't FeatureFlagsSection.tsx itself +// (that generic list is what extractSectionFlags/inSection already checks). +function extractDedicatedUiFlags(catalogSrc: string): Set { + const dedicated = new Set(); + const flagKeyMatches = [...catalogSrc.matchAll(/flagKey:\s*['"`](enable\w+)['"`]/g)]; + for (let i = 0; i < flagKeyMatches.length; i++) { + const match = flagKeyMatches[i]; + const flag = match?.[1]; + if (!flag || match.index === undefined) continue; + const nextIndex = flagKeyMatches[i + 1]?.index ?? catalogSrc.length; + const entrySrc = catalogSrc.slice(match.index, nextIndex); + const gateLocationsInner = entrySrc.match(/gateLocations:\s*\[([\s\S]*?)\n\s*\],/)?.[1]; + if ( + gateLocationsInner && + /components\/settings\/(?!FeatureFlagsSection\.tsx)[\w-]+\.tsx/.test(gateLocationsInner) + ) { + dedicated.add(flag); + } + } + return dedicated; +} + // --------------------------------------------------------------------------- // Step 5: Extract flags from useSettingsView.ts switch // --------------------------------------------------------------------------- @@ -165,6 +191,7 @@ const sliceFlags = extractFlagsFromSlice(sliceSrc); const defaultFlags = extractDefaultsFromSlice(sliceSrc); const localeFlags = extractLocaleFlags(localeSrc); const sectionFlags = extractSectionFlags(sectionSrc, catalogSrc); +const dedicatedUiFlags = extractDedicatedUiFlags(catalogSrc); const handlerFlags = extractHandlerFlags(hookSrc); let errors = 0; @@ -178,6 +205,7 @@ const rows: Array<{ inDefaults: boolean; inLocale: boolean; inSection: boolean; + hasDedicatedUi: boolean; inHandler: boolean; hasRuntime: boolean; }> = []; @@ -186,9 +214,10 @@ for (const flag of sliceFlags) { const inDefaults = defaultFlags.has(flag); const inLocale = localeFlags.has(flag); const inSection = sectionFlags.has(flag); + const hasDedicatedUi = dedicatedUiFlags.has(flag); const inHandler = handlerFlags.has(flag); const hasRuntime = hasRuntimeConsumption(flag); - rows.push({ flag, inDefaults, inLocale, inSection, inHandler, hasRuntime }); + rows.push({ flag, inDefaults, inLocale, inSection, hasDedicatedUi, inHandler, hasRuntime }); } // Print table header @@ -207,13 +236,16 @@ console.log('─'.repeat(90)); for (const row of rows) { const check = (v: boolean) => (v ? green('✅') : red('❌')); const runtimeCheck = row.hasRuntime ? green('✅') : yellow('⚠️ '); + // QNBS-v3: a dedicated toggle elsewhere (e.g. PrivacySection.tsx) is a real UI toggle, just not + // the generic catalog-driven one — show it distinctly rather than as a flat ❌ "no toggle at all". + const sectionCheck = row.inSection ? green('✅') : row.hasDedicatedUi ? green('◆') : red('❌'); const flagLabel = row.flag.padEnd(34); const line = [ flagLabel, check(row.inDefaults).padEnd(18), check(row.inLocale).padEnd(16), - check(row.inSection).padEnd(18), + sectionCheck.padEnd(18), check(row.inHandler).padEnd(18), runtimeCheck, ].join(''); @@ -223,7 +255,7 @@ for (const row of rows) { // Count issues if (!row.inDefaults) errors++; if (!row.inLocale) errors++; - if (!row.inSection) warnings++; // warning — dev-only flags may intentionally skip UI + if (!row.inSection && !row.hasDedicatedUi) warnings++; // warning — dev-only flags may intentionally skip UI entirely if (!row.inSection && row.inHandler) errors++; // handler without UI toggle is dead code if (row.inSection && !row.inHandler) errors++; // UI toggle with no handler = critical bug if (!row.hasRuntime) warnings++; // ghost flag @@ -280,10 +312,20 @@ if (ghostFlags.length > 0) { console.log(); } -// Warnings: flags in section but not in handler -const noUiToggle = rows.filter((r) => !r.inSection); +// Info: flags with a dedicated toggle outside the generic catalog-driven section (real UI, not missing) +const dedicatedUiOnly = rows.filter((r) => !r.inSection && r.hasDedicatedUi); +if (dedicatedUiOnly.length > 0) { + console.log(green('INFO — Flags with a dedicated UI toggle outside FeatureFlagsSection.tsx:')); + for (const r of dedicatedUiOnly) { + console.log(green(` • ${r.flag} — see its gateLocations entry in featureCatalog.ts`)); + } + console.log(); +} + +// Warnings: flags with no UI toggle anywhere (neither the generic section nor a dedicated one) +const noUiToggle = rows.filter((r) => !r.inSection && !r.hasDedicatedUi); if (noUiToggle.length > 0) { - console.log(yellow('INFO — Flags with no UI toggle (Settings cannot change them):')); + console.log(yellow('WARNING — Flags with no UI toggle anywhere (Settings cannot change them):')); for (const r of noUiToggle) { console.log(yellow(` • ${r.flag}`)); } From 199d3ac10c350231e816b333dd80b07fda6d5b6d Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Tue, 28 Jul 2026 22:53:56 +0200 Subject: [PATCH 2/4] ci: add Dependabot auto-merge for patch-level bumps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A 31-day commit gap (2026-06-25 to 2026-07-26) let 20 Dependabot PRs stack up (see AUDIT.md / TODO.md), all triaged in one burst afterward. Auto-merging patch-level bumps once CI is green cuts that latency without touching the judgment calls minor/major bumps deserve — those still land as normal PRs. Uses dependabot/fetch-metadata (pinned to its v3.1.0 commit) to read the update-type, then `gh pr merge --auto --squash` only for version-update:semver-patch. The existing dev-tooling/tauri-deps groups and the 7-day release cooldown (.npmrc minimum-release-age) are unchanged. --- .github/workflows/dependabot-automerge.yml | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/dependabot-automerge.yml diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml new file mode 100644 index 00000000..4ec79a60 --- /dev/null +++ b/.github/workflows/dependabot-automerge.yml @@ -0,0 +1,37 @@ +# QNBS-v3: proposed during the deployment-surface/OpenRouter audit (2026-07-28) — a 31-day commit +# gap let 20 Dependabot PRs stack up (see AUDIT.md / TODO.md). Auto-merging PATCH-only bumps once +# CI is green cuts that latency without touching the judgment calls minor/major bumps deserve. The +# existing dev-tooling/tauri-deps groups and the 7-day release cooldown (.npmrc minimum-release-age) +# are unchanged — this only adds a merge step gated on CI + semver-patch. +name: Dependabot Auto-Merge + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + auto-merge: + name: Auto-merge patch-level Dependabot PRs + if: github.actor == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Fetch Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + # QNBS-v3: patch-only, on purpose — minor/major bumps (or any bump outside npm/cargo/ + # github-actions, e.g. a group update mixing semver levels) still need a human look. + - name: Enable auto-merge for semver-patch updates + if: steps.metadata.outputs.update-type == 'version-update:semver-patch' + run: gh pr merge --auto --squash "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5f0179357383d2de033b911dd1064a398240132a Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Tue, 28 Jul 2026 22:54:53 +0200 Subject: [PATCH 3/4] fix(i18n): deduplicate 10 keys shared between settings.json and common/dashboard.json, fix ES mistranslation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found while investigating why scripts/sync-readme-metrics.mjs's i18n key count kept disagreeing with the canonical check-i18n-keys.mjs count by exactly 10: sceneboard.minimap.ariaLabel and both error.boundary.* keys were defined in BOTH common.json and settings.json; all 7 dashboard.backup.* keys were defined in BOTH dashboard.json and settings.json — in every locale. Since the runtime bundle is a flat merge across all 21 modules, a duplicate key isn't just redundant storage — whichever module the merge processes last silently wins, so the two copies could (and did) diverge per locale without anything catching it. Removed the settings.json copy in all 19 locales, keeping each key in its semantic "home" module (common.json for the cross-cutting error/sceneboard keys, dashboard.json for the backup ones). While auditing which copy was actually correct per locale: **es's dashboard.json copies of all 7 dashboard.backup.* keys were in German** ("Backup & Wiederherstellung", "Projekt exportieren...") — not Spanish — while the settings.json duplicates were untranslated English placeholders. Neither was Spanish. Replaced with real translations matching this locale's established terminology elsewhere in the app (help.json: "copia de seguridad", "instantánea"). Every other locale's two copies were either identical or both legitimate (alternate phrasing); a few had a mixed-language artifact in the settings.json copy specifically (e.g. ja/pt/zh: "エクスポート your project, import a backup"), which is now gone since that copy no longer exists. i18n:check confirms the deduplicated key count is unchanged (2849 — the duplicates were never counted as extra keys by the canonical dedup-via-Set logic, only by sync-readme-metrics.mjs's naive per-file sum). That script now settles on the correct 2849 on every run instead of flip-flopping to 2859. --- locales/ar/settings.json | 10 ---------- locales/de/settings.json | 10 ---------- locales/el/settings.json | 10 ---------- locales/en/settings.json | 24 +++++++----------------- locales/es/dashboard.json | 14 +++++++------- locales/es/settings.json | 10 ---------- locales/eu/settings.json | 10 ---------- locales/fa/settings.json | 10 ---------- locales/fi/settings.json | 10 ---------- locales/fr/settings.json | 10 ---------- locales/he/settings.json | 10 ---------- locales/hu/settings.json | 10 ---------- locales/is/settings.json | 10 ---------- locales/it/settings.json | 10 ---------- locales/ja/settings.json | 10 ---------- locales/ko/settings.json | 10 ---------- locales/pt/settings.json | 10 ---------- locales/ru/settings.json | 10 ---------- locales/sv/settings.json | 10 ---------- locales/zh/settings.json | 10 ---------- public/locales/el/bundle.json | 2 +- public/locales/en/bundle.json | 14 +++++++------- public/locales/es/bundle.json | 14 +++++++------- public/locales/eu/bundle.json | 4 ++-- public/locales/fa/bundle.json | 12 ++++++------ public/locales/fi/bundle.json | 6 +++--- public/locales/hu/bundle.json | 4 ++-- public/locales/is/bundle.json | 6 +++--- public/locales/ja/bundle.json | 4 ++-- public/locales/pt/bundle.json | 2 +- public/locales/sv/bundle.json | 8 ++++---- public/locales/zh/bundle.json | 2 +- 32 files changed, 53 insertions(+), 243 deletions(-) diff --git a/locales/ar/settings.json b/locales/ar/settings.json index 6de202c7..b1d78832 100644 --- a/locales/ar/settings.json +++ b/locales/ar/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "تصدير JSON", - "dashboard.backup.hint": "صدّر مشروعك، أو استورد نسخة احتياطية، أو افتح إعدادات النسخ الاحتياطي الكاملة.", - "dashboard.backup.importJson": "استيراد JSON", - "dashboard.backup.latestSnapshot": "أحدث لقطة: {{name}} ‏({{date}})", - "dashboard.backup.openSettings": "إعدادات النسخ الاحتياطي", - "dashboard.backup.title": "النسخ الاحتياطي والاستعادة", - "dashboard.backup.unnamedSnapshot": "لقطة بلا اسم", - "error.boundary.retryAnnouncement": "أُعيد تحميل {{view}} بعد خطأ.", - "error.boundary.retryAnnouncementGeneric": "أُعيد تحميل العرض بعد خطأ.", - "sceneboard.minimap.ariaLabel": "خريطة مصغّرة للوحة الحبكة", "settings.about.description": "شريكك الإبداعي المدعوم بالذكاء الاصطناعي للكتابة.", "settings.about.githubDescription": "اعرض الكود المصدري، وأبلغ عن المشكلات، وساهم.", "settings.about.githubLabel": "مفتوح المصدر على GitHub", diff --git a/locales/de/settings.json b/locales/de/settings.json index 8f9799ed..54ba181a 100644 --- a/locales/de/settings.json +++ b/locales/de/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "JSON exportieren", - "dashboard.backup.hint": "Projekt exportieren, Backup importieren oder Einstellungen öffnen.", - "dashboard.backup.importJson": "JSON importieren", - "dashboard.backup.latestSnapshot": "Letzter Snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup-Einstellungen", - "dashboard.backup.title": "Backup & Wiederherstellung", - "dashboard.backup.unnamedSnapshot": "Unbenannter Snapshot", - "error.boundary.retryAnnouncement": "{{view}} nach einem Fehler neu geladen.", - "error.boundary.retryAnnouncementGeneric": "Ansicht nach einem Fehler neu geladen.", - "sceneboard.minimap.ariaLabel": "Plot-Board-Minikarte", "settings.about.description": "Ihr KI-gestützter kreativer Partner für das Schreiben.", "settings.about.githubDescription": "Quellcode ansehen, Fehler melden und mitmachen.", "settings.about.githubLabel": "Open-Source auf GitHub", diff --git a/locales/el/settings.json b/locales/el/settings.json index c7db449c..508922ec 100644 --- a/locales/el/settings.json +++ b/locales/el/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Εξαγωγή JSON", - "dashboard.backup.hint": "Εξαγωγή your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Εισαγωγή JSON", - "dashboard.backup.latestSnapshot": "Τελευταίο στιγμιότυπο: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Ρυθμίσεις δημιουργίας αντιγράφων ασφαλείας", - "dashboard.backup.title": "Δημιουργία αντιγράφων ασφαλείας και επαναφορά", - "dashboard.backup.unnamedSnapshot": "Στιγμιότυπο χωρίς όνομα", - "error.boundary.retryAnnouncement": "Το {{view}} φορτώθηκε ξανά μετά από σφάλμα.", - "error.boundary.retryAnnouncementGeneric": "Η προβολή φορτώθηκε ξανά μετά από σφάλμα.", - "sceneboard.minimap.ariaLabel": "Μίνι χάρτης σανίδας οικοπέδου", "settings.about.description": "Ο δημιουργικός συνεργάτης σας με τεχνητή νοημοσύνη για τη γραφή.", "settings.about.githubDescription": "Δείτε τον πηγαίο κώδικα, αναφέρετε προβλήματα και συνεισφέρετε.", "settings.about.githubLabel": "Ανοιχτού κώδικα στο GitHub", diff --git a/locales/en/settings.json b/locales/en/settings.json index d1f8f829..2df90e31 100644 --- a/locales/en/settings.json +++ b/locales/en/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Export JSON", - "dashboard.backup.hint": "Export your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Import JSON", - "dashboard.backup.latestSnapshot": "Latest snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup settings", - "dashboard.backup.title": "Backup & restore", - "dashboard.backup.unnamedSnapshot": "Unnamed snapshot", - "error.boundary.retryAnnouncement": "{{view}} reloaded after an error.", - "error.boundary.retryAnnouncementGeneric": "View reloaded after an error.", - "sceneboard.minimap.ariaLabel": "Plot board mini-map", "settings.about.description": "Your AI-powered creative partner for writing.", "settings.about.githubDescription": "View source code, report issues, and contribute.", "settings.about.githubLabel": "Open-source on GitHub", @@ -248,16 +238,16 @@ "settings.ai.temperature.creative": "2 – Creative", "settings.ai.temperature.precise": "0 – Precise", "settings.ai.testConnection": "Test connection", - "settings.ai.testError.noApiKey": "No {{provider}} API key set", - "settings.ai.testError.httpError": "HTTP {{status}}", - "settings.ai.testError.timeout": "Connection timed out ({{url}})", - "settings.ai.testError.unreachable": "Not reachable ({{url}})", - "settings.ai.testError.pluginUnavailable": "Local server networking is unavailable: the desktop HTTP plugin failed to load.", - "settings.ai.testError.desktopRequired": "Ollama and local OpenAI-compatible servers are only available in the desktop app. Browsers block direct connections from web pages to localhost (CORS and Private Network Access).", "settings.ai.testError.backendProxyRequired": "Claude requires a backend proxy (CORS restriction)", + "settings.ai.testError.desktopRequired": "Ollama and local OpenAI-compatible servers are only available in the desktop app. Browsers block direct connections from web pages to localhost (CORS and Private Network Access).", + "settings.ai.testError.httpError": "HTTP {{status}}", + "settings.ai.testError.noApiKey": "No {{provider}} API key set", "settings.ai.testError.noWebgpu": "WebGPU unavailable in this browser — WebLLM needs WebGPU (try Chrome/Edge or enable flags).", - "settings.ai.testError.unknownProvider": "Unknown provider", + "settings.ai.testError.pluginUnavailable": "Local server networking is unavailable: the desktop HTTP plugin failed to load.", + "settings.ai.testError.timeout": "Connection timed out ({{url}})", "settings.ai.testError.unexpected": "An unexpected error occurred. Please try again.", + "settings.ai.testError.unknownProvider": "Unknown provider", + "settings.ai.testError.unreachable": "Not reachable ({{url}})", "settings.ai.title": "AI Configuration", "settings.ai.transformers.hint": "Runs any Xenova-compatible HuggingFace model locally via WebAssembly or WebGPU.", "settings.ai.transformers.modelId": "Model ID (HuggingFace)", diff --git a/locales/es/dashboard.json b/locales/es/dashboard.json index 619cd6fe..f82fbaf5 100644 --- a/locales/es/dashboard.json +++ b/locales/es/dashboard.json @@ -10,13 +10,13 @@ "dashboard.authorInsights.timelineClear": "Sin avisos desde los metadatos de escena.", "dashboard.authorInsights.timelineWarnBadge": "{{count}} avisos — revisa fechas/duraciones en el tablero.", "dashboard.authorInsights.title": "Información de pipeline del autor", - "dashboard.backup.exportJson": "JSON exportieren", - "dashboard.backup.hint": "Projekt exportieren, Backup importieren oder Einstellungen öffnen.", - "dashboard.backup.importJson": "JSON importieren", - "dashboard.backup.latestSnapshot": "Letzter Snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup-Einstellungen", - "dashboard.backup.title": "Backup & Wiederherstellung", - "dashboard.backup.unnamedSnapshot": "Unbenannter Snapshot", + "dashboard.backup.exportJson": "Exportar JSON", + "dashboard.backup.hint": "Exporta tu proyecto, importa una copia de seguridad o abre la configuración completa de copias de seguridad.", + "dashboard.backup.importJson": "Importar JSON", + "dashboard.backup.latestSnapshot": "Última instantánea: {{name}} ({{date}})", + "dashboard.backup.openSettings": "Configuración de copias de seguridad", + "dashboard.backup.title": "Copia de seguridad y restauración", + "dashboard.backup.unnamedSnapshot": "Instantánea sin nombre", "dashboard.composition.avgPerScene": "Media / escena", "dashboard.composition.empty": "Añade escenas en el Manuscrito para ver su composición.", "dashboard.composition.readingTime": "Tiempo de lectura", diff --git a/locales/es/settings.json b/locales/es/settings.json index ebcb17b8..4db6a854 100644 --- a/locales/es/settings.json +++ b/locales/es/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Export JSON", - "dashboard.backup.hint": "Export your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Import JSON", - "dashboard.backup.latestSnapshot": "Latest snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup settings", - "dashboard.backup.title": "Backup & restore", - "dashboard.backup.unnamedSnapshot": "Unnamed snapshot", - "error.boundary.retryAnnouncement": "{{view}} reloaded after an error.", - "error.boundary.retryAnnouncementGeneric": "View reloaded after an error.", - "sceneboard.minimap.ariaLabel": "Plot board mini-map", "settings.about.description": "Tu compañero creativo de escritura con IA.", "settings.about.githubDescription": "Ver el código fuente, reportar errores y contribuir.", "settings.about.githubLabel": "Código abierto en GitHub", diff --git a/locales/eu/settings.json b/locales/eu/settings.json index 875d914d..17ae6dd9 100644 --- a/locales/eu/settings.json +++ b/locales/eu/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Esportatu JSON", - "dashboard.backup.hint": "Esportatu zure proiektua, inportatu babeskopia bat edo ireki babeskopien ezarpen osoak.", - "dashboard.backup.importJson": "Inportatu JSON", - "dashboard.backup.latestSnapshot": "Azken argazkia: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Babeskopia-ezarpenak", - "dashboard.backup.title": "Babeskopia egin eta leheneratu", - "dashboard.backup.unnamedSnapshot": "Izenik gabeko argazkia", - "error.boundary.retryAnnouncement": "{{view}} berriro kargatu da errore baten ondoren.", - "error.boundary.retryAnnouncementGeneric": "Ikusi birkargatu da errore baten ondoren.", - "sceneboard.minimap.ariaLabel": "Orube-taula txiki-mapa", "settings.about.description": "Idazketarako AI-k bultzatutako sormen-kidea.", "settings.about.githubDescription": "Ikusi iturburu-kodea, jakinarazi arazoen berri eta eman ekarpenak.", "settings.about.githubLabel": "Kode irekia GitHub-en", diff --git a/locales/fa/settings.json b/locales/fa/settings.json index 1129ac17..5a9cab56 100644 --- a/locales/fa/settings.json +++ b/locales/fa/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "صادرات JSON", - "dashboard.backup.hint": "پروژه خود را صادر کنید، یک نسخه پشتیبان وارد کنید یا تنظیمات پشتیبان کامل را باز کنید.", - "dashboard.backup.importJson": "JSON را وارد کنید", - "dashboard.backup.latestSnapshot": "آخرین عکس فوری: {{name}} ({{date}})", - "dashboard.backup.openSettings": "تنظیمات پشتیبان گیری", - "dashboard.backup.title": "پشتیبان گیری و بازیابی", - "dashboard.backup.unnamedSnapshot": "عکس فوری بدون نام", - "error.boundary.retryAnnouncement": "{{view}} پس از یک خطا دوباره بارگیری شد.", - "error.boundary.retryAnnouncementGeneric": "مشاهده پس از خطا بارگیری مجدد شد.", - "sceneboard.minimap.ariaLabel": "مینی نقشه تخته پلات", "settings.about.description": "شریک خلاق شما با هوش مصنوعی برای نوشتن.", "settings.about.githubDescription": "مشاهده کد منبع، گزارش مشکلات، و مشارکت.", "settings.about.githubLabel": "منبع باز در GitHub", diff --git a/locales/fi/settings.json b/locales/fi/settings.json index 1f4e2c01..8a5f4e5b 100644 --- a/locales/fi/settings.json +++ b/locales/fi/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Vie JSON", - "dashboard.backup.hint": "Vie projektisi, tuo varmuuskopio tai avaa täydelliset varmuuskopiointiasetukset.", - "dashboard.backup.importJson": "Tuo JSON", - "dashboard.backup.latestSnapshot": "Viimeisin tilannekuva: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Varmuuskopiointiasetukset", - "dashboard.backup.title": "Varmuuskopiointi ja palautus", - "dashboard.backup.unnamedSnapshot": "Nimetön tilannekuva", - "error.boundary.retryAnnouncement": "{{view}} ladattu uudelleen virheen jälkeen.", - "error.boundary.retryAnnouncementGeneric": "Näkymä ladattu uudelleen virheen jälkeen.", - "sceneboard.minimap.ariaLabel": "Tonttitaulun minikartta", "settings.about.description": "Tekoälyllä toimiva luova kumppanisi kirjoittamiseen.", "settings.about.githubDescription": "Tarkastele lähdekoodia, ilmoita ongelmista ja osallistu.", "settings.about.githubLabel": "Avoin lähdekoodi GitHubissa", diff --git a/locales/fr/settings.json b/locales/fr/settings.json index 84f78cb6..4892b12f 100644 --- a/locales/fr/settings.json +++ b/locales/fr/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Export JSON", - "dashboard.backup.hint": "Export your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Import JSON", - "dashboard.backup.latestSnapshot": "Latest snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup settings", - "dashboard.backup.title": "Backup & restore", - "dashboard.backup.unnamedSnapshot": "Unnamed snapshot", - "error.boundary.retryAnnouncement": "{{view}} reloaded after an error.", - "error.boundary.retryAnnouncementGeneric": "View reloaded after an error.", - "sceneboard.minimap.ariaLabel": "Plot board mini-map", "settings.about.description": "Votre partenaire créatif pour l'écriture, propulsé par l'IA.", "settings.about.githubDescription": "Voir le code source, signaler des bugs et contribuer.", "settings.about.githubLabel": "Open-source sur GitHub", diff --git a/locales/he/settings.json b/locales/he/settings.json index 049a0938..03836944 100644 --- a/locales/he/settings.json +++ b/locales/he/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "ייצוא JSON", - "dashboard.backup.hint": "ייצאו את הפרויקט, ייבאו גיבוי, או פתחו את הגדרות הגיבוי המלאות.", - "dashboard.backup.importJson": "ייבוא JSON", - "dashboard.backup.latestSnapshot": "תמונת המצב האחרונה: {{name}} ‏({{date}})", - "dashboard.backup.openSettings": "הגדרות גיבוי", - "dashboard.backup.title": "גיבוי ושחזור", - "dashboard.backup.unnamedSnapshot": "תמונת מצב ללא שם", - "error.boundary.retryAnnouncement": "{{view}} נטענה מחדש לאחר שגיאה.", - "error.boundary.retryAnnouncementGeneric": "התצוגה נטענה מחדש לאחר שגיאה.", - "sceneboard.minimap.ariaLabel": "מפה ממוזערת של לוח העלילה", "settings.about.description": "השותף היצירתי המופעל ב‑AI שלכם לכתיבה.", "settings.about.githubDescription": "צפו בקוד המקור, דווחו על בעיות ותרמו.", "settings.about.githubLabel": "קוד פתוח ב‑GitHub", diff --git a/locales/hu/settings.json b/locales/hu/settings.json index 980f6f37..94afe2f4 100644 --- a/locales/hu/settings.json +++ b/locales/hu/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "JSON exportálása", - "dashboard.backup.hint": "Exportálja projektjét, importáljon biztonsági másolatot, vagy nyissa meg a teljes biztonsági mentési beállításokat.", - "dashboard.backup.importJson": "JSON importálása", - "dashboard.backup.latestSnapshot": "Legutóbbi pillanatkép: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Biztonsági mentés beállításai", - "dashboard.backup.title": "Biztonsági mentés és visszaállítás", - "dashboard.backup.unnamedSnapshot": "Névtelen pillanatkép", - "error.boundary.retryAnnouncement": "{{view}} hiba után újratöltve.", - "error.boundary.retryAnnouncementGeneric": "A nézet újratöltve hiba után.", - "sceneboard.minimap.ariaLabel": "Telektábla mini-térkép", "settings.about.description": "Az Ön mesterséges intelligenciájú kreatív partnere az íráshoz.", "settings.about.githubDescription": "Tekintse meg a forráskódot, jelentse a problémákat és járuljon hozzá.", "settings.about.githubLabel": "Nyílt forráskód a GitHubon", diff --git a/locales/is/settings.json b/locales/is/settings.json index f612e7a8..b53f975f 100644 --- a/locales/is/settings.json +++ b/locales/is/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Flytja út JSON", - "dashboard.backup.hint": "Flyttu út verkefnið þitt, fluttu inn afrit eða opnaðu allar öryggisafritunarstillingar.", - "dashboard.backup.importJson": "Flytja inn JSON", - "dashboard.backup.latestSnapshot": "Nýjasta skyndimynd: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Stillingar fyrir öryggisafrit", - "dashboard.backup.title": "Afrita og endurheimta", - "dashboard.backup.unnamedSnapshot": "Ónefnd skyndimynd", - "error.boundary.retryAnnouncement": "{{view}} endurhlaðinn eftir villu.", - "error.boundary.retryAnnouncementGeneric": "Skoða endurhlaðinn eftir villu.", - "sceneboard.minimap.ariaLabel": "Lítið kort af teikniborði", "settings.about.description": "Skapandi félagi þinn með gervigreind til að skrifa.", "settings.about.githubDescription": "Skoðaðu frumkóðann, tilkynntu vandamál og leggðu þitt af mörkum.", "settings.about.githubLabel": "Opinn uppspretta á GitHub", diff --git a/locales/it/settings.json b/locales/it/settings.json index 2d8dd439..073c1423 100644 --- a/locales/it/settings.json +++ b/locales/it/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Export JSON", - "dashboard.backup.hint": "Export your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Import JSON", - "dashboard.backup.latestSnapshot": "Latest snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup settings", - "dashboard.backup.title": "Backup & restore", - "dashboard.backup.unnamedSnapshot": "Unnamed snapshot", - "error.boundary.retryAnnouncement": "{{view}} reloaded after an error.", - "error.boundary.retryAnnouncementGeneric": "View reloaded after an error.", - "sceneboard.minimap.ariaLabel": "Plot board mini-map", "settings.about.description": "Il tuo partner creativo per la scrittura, potenziato dall'IA.", "settings.about.githubDescription": "Visualizza il codice sorgente, segnala problemi e contribuisci.", "settings.about.githubLabel": "Open-source su GitHub", diff --git a/locales/ja/settings.json b/locales/ja/settings.json index f3a93c03..ff8aafbd 100644 --- a/locales/ja/settings.json +++ b/locales/ja/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "エクスポート JSON", - "dashboard.backup.hint": "エクスポート your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "JSONをインポートする", - "dashboard.backup.latestSnapshot": "最新のスナップショット: {{name}} ({{date}})", - "dashboard.backup.openSettings": "バックアップ設定", - "dashboard.backup.title": "バックアップと復元", - "dashboard.backup.unnamedSnapshot": "名前のないスナップショット", - "error.boundary.retryAnnouncement": "エラー後に {{view}} がリロードされました。", - "error.boundary.retryAnnouncementGeneric": "エラー後にビューがリロードされました。", - "sceneboard.minimap.ariaLabel": "プロットボードのミニマップ", "settings.about.description": "AI を活用したライティングのためのクリエイティブ パートナー。", "settings.about.githubDescription": "ソース コードを表示し、問題を報告し、貢献します。", "settings.about.githubLabel": "GitHub 上のオープンソース", diff --git a/locales/ko/settings.json b/locales/ko/settings.json index 6eefe41d..a7b019f6 100644 --- a/locales/ko/settings.json +++ b/locales/ko/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "JSON 내보내기", - "dashboard.backup.hint": "프로젝트를 내보내거나, 백업을 가져오거나, 전체 백업 설정을 엽니다.", - "dashboard.backup.importJson": "JSON 가져오기", - "dashboard.backup.latestSnapshot": "최신 스냅샷: {{name}} ({{date}})", - "dashboard.backup.openSettings": "백업 설정", - "dashboard.backup.title": "백업 및 복원", - "dashboard.backup.unnamedSnapshot": "이름이 지정되지 않은 스냅샷", - "error.boundary.retryAnnouncement": "{{view}} 오류가 발생한 후 다시 로드되었습니다.", - "error.boundary.retryAnnouncementGeneric": "오류가 발생한 후 다시 로드된 보기입니다.", - "sceneboard.minimap.ariaLabel": "플롯 보드 미니맵", "settings.about.description": "글쓰기를 위한 AI 기반 크리에이티브 파트너.", "settings.about.githubDescription": "소스 코드를 보고, 문제를 보고하고, 기여하세요.", "settings.about.githubLabel": "GitHub의 오픈 소스", diff --git a/locales/pt/settings.json b/locales/pt/settings.json index 9b1d7642..ae1d3c90 100644 --- a/locales/pt/settings.json +++ b/locales/pt/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Exportar JSON", - "dashboard.backup.hint": "Exportar your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Importar JSON", - "dashboard.backup.latestSnapshot": "Instantâneo mais recente: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Configurações de backup", - "dashboard.backup.title": "Backup e restauração", - "dashboard.backup.unnamedSnapshot": "Instantâneo sem nome", - "error.boundary.retryAnnouncement": "{{view}} recarregado após um erro.", - "error.boundary.retryAnnouncementGeneric": "Visualização recarregada após um erro.", - "sceneboard.minimap.ariaLabel": "Minimapa do quadro de plotagem", "settings.about.description": "Your IA-powered creative partner for writing.", "settings.about.githubDescription": "Visualize o código-fonte, relate problemas e contribua.", "settings.about.githubLabel": "Código aberto no GitHub", diff --git a/locales/ru/settings.json b/locales/ru/settings.json index 9e7c47d7..9d8fde22 100644 --- a/locales/ru/settings.json +++ b/locales/ru/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Экспорт JSON", - "dashboard.backup.hint": "Экспортируйте свой проект, импортируйте резервную копию или откройте полные настройки резервного копирования.", - "dashboard.backup.importJson": "Импортировать JSON", - "dashboard.backup.latestSnapshot": "Последний снимок: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Настройки резервного копирования", - "dashboard.backup.title": "Резервное копирование и восстановление", - "dashboard.backup.unnamedSnapshot": "Безымянный снимок", - "error.boundary.retryAnnouncement": "{{view}} перезагружается после ошибки.", - "error.boundary.retryAnnouncementGeneric": "Просмотр перезагружен после ошибки.", - "sceneboard.minimap.ariaLabel": "Мини-карта планшета", "settings.about.description": "Ваш творческий партнер на базе искусственного интеллекта для написания статей.", "settings.about.githubDescription": "Просматривайте исходный код, сообщайте о проблемах и вносите свой вклад.", "settings.about.githubLabel": "Открытый исходный код на GitHub", diff --git a/locales/sv/settings.json b/locales/sv/settings.json index 519951cb..3dd3c2de 100644 --- a/locales/sv/settings.json +++ b/locales/sv/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "Exportera JSON", - "dashboard.backup.hint": "Exportera ditt projekt, importera en säkerhetskopia eller öppna inställningar för fullständig säkerhetskopiering.", - "dashboard.backup.importJson": "Importera JSON", - "dashboard.backup.latestSnapshot": "Senaste ögonblicksbilden: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Säkerhetskopieringsinställningar", - "dashboard.backup.title": "Säkerhetskopiera och återställa", - "dashboard.backup.unnamedSnapshot": "Namnlös ögonblicksbild", - "error.boundary.retryAnnouncement": "{{view}} laddades om efter ett fel.", - "error.boundary.retryAnnouncementGeneric": "Vyn har laddats om efter ett fel.", - "sceneboard.minimap.ariaLabel": "Rittavla minikarta", "settings.about.description": "Din AI-drivna kreativa partner för skrivande.", "settings.about.githubDescription": "Visa källkod, rapportera problem och bidra.", "settings.about.githubLabel": "Öppen källkod på GitHub", diff --git a/locales/zh/settings.json b/locales/zh/settings.json index 1b62b778..13e90189 100644 --- a/locales/zh/settings.json +++ b/locales/zh/settings.json @@ -1,14 +1,4 @@ { - "dashboard.backup.exportJson": "导出 JSON", - "dashboard.backup.hint": "导出 your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "导入 JSON", - "dashboard.backup.latestSnapshot": "最新快照:{{name}}({{date}})", - "dashboard.backup.openSettings": "备份设置", - "dashboard.backup.title": "备份与恢复", - "dashboard.backup.unnamedSnapshot": "未命名快照", - "error.boundary.retryAnnouncement": "{{view}} 发生错误后重新加载。", - "error.boundary.retryAnnouncementGeneric": "出现错误后重新加载视图。", - "sceneboard.minimap.ariaLabel": "绘图板小地图", "settings.about.description": "您的人工智能写作创意合作伙伴。", "settings.about.githubDescription": "查看源代码、报告问题并做出贡献。", "settings.about.githubLabel": "在 GitHub 上开源", diff --git a/public/locales/el/bundle.json b/public/locales/el/bundle.json index ea30e576..6dd91d27 100644 --- a/public/locales/el/bundle.json +++ b/public/locales/el/bundle.json @@ -885,7 +885,7 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} προειδοποιήσεις — βελτιώστε τις ημερομηνίες/διάρκειες των σκηνών στον πίνακα σκηνής.", "dashboard.authorInsights.title": "Insights για τη διοχέτευση του συγγραφέα", "dashboard.backup.exportJson": "Εξαγωγή JSON", - "dashboard.backup.hint": "Εξαγωγή your project, import a backup, or open full backup settings.", + "dashboard.backup.hint": "Εξάγετε το έργο σας, εισαγάγετε ένα αντίγραφο ασφαλείας ή ανοίξτε τις ρυθμίσεις πλήρους αντιγράφου ασφαλείας.", "dashboard.backup.importJson": "Εισαγωγή JSON", "dashboard.backup.latestSnapshot": "Τελευταίο στιγμιότυπο: {{name}} ({{date}})", "dashboard.backup.openSettings": "Ρυθμίσεις δημιουργίας αντιγράφων ασφαλείας", diff --git a/public/locales/en/bundle.json b/public/locales/en/bundle.json index fc4e3d4e..18818fa2 100644 --- a/public/locales/en/bundle.json +++ b/public/locales/en/bundle.json @@ -1856,16 +1856,16 @@ "settings.ai.temperature.creative": "2 – Creative", "settings.ai.temperature.precise": "0 – Precise", "settings.ai.testConnection": "Test connection", - "settings.ai.testError.noApiKey": "No {{provider}} API key set", - "settings.ai.testError.httpError": "HTTP {{status}}", - "settings.ai.testError.timeout": "Connection timed out ({{url}})", - "settings.ai.testError.unreachable": "Not reachable ({{url}})", - "settings.ai.testError.pluginUnavailable": "Local server networking is unavailable: the desktop HTTP plugin failed to load.", - "settings.ai.testError.desktopRequired": "Ollama and local OpenAI-compatible servers are only available in the desktop app. Browsers block direct connections from web pages to localhost (CORS and Private Network Access).", "settings.ai.testError.backendProxyRequired": "Claude requires a backend proxy (CORS restriction)", + "settings.ai.testError.desktopRequired": "Ollama and local OpenAI-compatible servers are only available in the desktop app. Browsers block direct connections from web pages to localhost (CORS and Private Network Access).", + "settings.ai.testError.httpError": "HTTP {{status}}", + "settings.ai.testError.noApiKey": "No {{provider}} API key set", "settings.ai.testError.noWebgpu": "WebGPU unavailable in this browser — WebLLM needs WebGPU (try Chrome/Edge or enable flags).", - "settings.ai.testError.unknownProvider": "Unknown provider", + "settings.ai.testError.pluginUnavailable": "Local server networking is unavailable: the desktop HTTP plugin failed to load.", + "settings.ai.testError.timeout": "Connection timed out ({{url}})", "settings.ai.testError.unexpected": "An unexpected error occurred. Please try again.", + "settings.ai.testError.unknownProvider": "Unknown provider", + "settings.ai.testError.unreachable": "Not reachable ({{url}})", "settings.ai.title": "AI Configuration", "settings.ai.transformers.hint": "Runs any Xenova-compatible HuggingFace model locally via WebAssembly or WebGPU.", "settings.ai.transformers.modelId": "Model ID (HuggingFace)", diff --git a/public/locales/es/bundle.json b/public/locales/es/bundle.json index 9bc1fd03..485327f2 100644 --- a/public/locales/es/bundle.json +++ b/public/locales/es/bundle.json @@ -886,13 +886,13 @@ "dashboard.authorInsights.timelineClear": "Sin avisos desde los metadatos de escena.", "dashboard.authorInsights.timelineWarnBadge": "{{count}} avisos — revisa fechas/duraciones en el tablero.", "dashboard.authorInsights.title": "Información de pipeline del autor", - "dashboard.backup.exportJson": "Export JSON", - "dashboard.backup.hint": "Export your project, import a backup, or open full backup settings.", - "dashboard.backup.importJson": "Import JSON", - "dashboard.backup.latestSnapshot": "Latest snapshot: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Backup settings", - "dashboard.backup.title": "Backup & restore", - "dashboard.backup.unnamedSnapshot": "Unnamed snapshot", + "dashboard.backup.exportJson": "Exportar JSON", + "dashboard.backup.hint": "Exporta tu proyecto, importa una copia de seguridad o abre la configuración completa de copias de seguridad.", + "dashboard.backup.importJson": "Importar JSON", + "dashboard.backup.latestSnapshot": "Última instantánea: {{name}} ({{date}})", + "dashboard.backup.openSettings": "Configuración de copias de seguridad", + "dashboard.backup.title": "Copia de seguridad y restauración", + "dashboard.backup.unnamedSnapshot": "Instantánea sin nombre", "dashboard.composition.avgPerScene": "Media / escena", "dashboard.composition.empty": "Añade escenas en el Manuscrito para ver su composición.", "dashboard.composition.readingTime": "Tiempo de lectura", diff --git a/public/locales/eu/bundle.json b/public/locales/eu/bundle.json index 4429ecdc..9bfb5856 100644 --- a/public/locales/eu/bundle.json +++ b/public/locales/eu/bundle.json @@ -885,11 +885,11 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} abisu — zehaztu eszenen datak/iraupenak Eszena-taulan.", "dashboard.authorInsights.title": "Egilearen prozesuaren ikuspegiak", "dashboard.backup.exportJson": "Esportatu JSON", - "dashboard.backup.hint": "Esportatu zure proiektua, inportatu babeskopia bat edo ireki babeskopien ezarpen osoak.", + "dashboard.backup.hint": "Esportatu zure proiektua, inportatu babeskopia bat, edo ireki babeskopia-ezarpen osoak.", "dashboard.backup.importJson": "Inportatu JSON", "dashboard.backup.latestSnapshot": "Azken argazkia: {{name}} ({{date}})", "dashboard.backup.openSettings": "Babeskopia-ezarpenak", - "dashboard.backup.title": "Babeskopia egin eta leheneratu", + "dashboard.backup.title": "Babeskopia eta leheneratzea", "dashboard.backup.unnamedSnapshot": "Izenik gabeko argazkia", "dashboard.composition.avgPerScene": "Batez best. / eszena", "dashboard.composition.empty": "Gehitu eszenak Eskuizkribuan haren osaera ikusteko.", diff --git a/public/locales/fa/bundle.json b/public/locales/fa/bundle.json index d12b9128..ce2e417d 100644 --- a/public/locales/fa/bundle.json +++ b/public/locales/fa/bundle.json @@ -885,12 +885,12 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} هشدار — تاریخ‌ها/مدت‌های صحنه را در تخته‌صحنه دقیق‌تر کنید.", "dashboard.authorInsights.title": "بینش‌های خط‌لوله نویسنده", "dashboard.backup.exportJson": "صادرات JSON", - "dashboard.backup.hint": "پروژه خود را صادر کنید، یک نسخه پشتیبان وارد کنید یا تنظیمات پشتیبان کامل را باز کنید.", - "dashboard.backup.importJson": "JSON را وارد کنید", - "dashboard.backup.latestSnapshot": "آخرین عکس فوری: {{name}} ({{date}})", - "dashboard.backup.openSettings": "تنظیمات پشتیبان گیری", - "dashboard.backup.title": "پشتیبان گیری و بازیابی", - "dashboard.backup.unnamedSnapshot": "عکس فوری بدون نام", + "dashboard.backup.hint": "پروژه خود را خروجی بگیرید، یک پشتیبان وارد کنید، یا تنظیمات کامل پشتیبان‌گیری را باز کنید.", + "dashboard.backup.importJson": "ورودی JSON", + "dashboard.backup.latestSnapshot": "آخرین تصویر لحظه‌ای: {{name}} ({{date}})", + "dashboard.backup.openSettings": "تنظیمات پشتیبان‌گیری", + "dashboard.backup.title": "پشتیبان‌گیری و بازیابی", + "dashboard.backup.unnamedSnapshot": "تصویر لحظه‌ای بی‌نام", "dashboard.composition.avgPerScene": "میانگین / صحنه", "dashboard.composition.empty": "برای دیدن ترکیب دست‌نوشته، در آن صحنه اضافه کنید.", "dashboard.composition.readingTime": "زمان مطالعه", diff --git a/public/locales/fi/bundle.json b/public/locales/fi/bundle.json index 306b5ac7..1468c5c3 100644 --- a/public/locales/fi/bundle.json +++ b/public/locales/fi/bundle.json @@ -885,12 +885,12 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} varoitusta — tarkenna kohtausten päiviä/kestoja Kohtaustaulussa.", "dashboard.authorInsights.title": "Kirjailijan putken oivallukset", "dashboard.backup.exportJson": "Vie JSON", - "dashboard.backup.hint": "Vie projektisi, tuo varmuuskopio tai avaa täydelliset varmuuskopiointiasetukset.", + "dashboard.backup.hint": "Vie projektisi, tuo varmuuskopio tai avaa täydet varmuuskopiointiasetukset.", "dashboard.backup.importJson": "Tuo JSON", - "dashboard.backup.latestSnapshot": "Viimeisin tilannekuva: {{name}} ({{date}})", + "dashboard.backup.latestSnapshot": "Viimeisin tilannevedos: {{name}} ({{date}})", "dashboard.backup.openSettings": "Varmuuskopiointiasetukset", "dashboard.backup.title": "Varmuuskopiointi ja palautus", - "dashboard.backup.unnamedSnapshot": "Nimetön tilannekuva", + "dashboard.backup.unnamedSnapshot": "Nimetön tilannevedos", "dashboard.composition.avgPerScene": "Keskim. / kohtaus", "dashboard.composition.empty": "Lisää kohtauksia Käsikirjoitukseen nähdäksesi sen koostumuksen.", "dashboard.composition.readingTime": "Lukuaika", diff --git a/public/locales/hu/bundle.json b/public/locales/hu/bundle.json index dada5bb6..109146ac 100644 --- a/public/locales/hu/bundle.json +++ b/public/locales/hu/bundle.json @@ -885,10 +885,10 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} figyelmeztetés — pontosítsd a jelenetek dátumait/időtartamait a Jelenettáblán.", "dashboard.authorInsights.title": "Szerzői folyamat – betekintések", "dashboard.backup.exportJson": "JSON exportálása", - "dashboard.backup.hint": "Exportálja projektjét, importáljon biztonsági másolatot, vagy nyissa meg a teljes biztonsági mentési beállításokat.", + "dashboard.backup.hint": "Exportáld a projektedet, importálj egy biztonsági mentést, vagy nyisd meg a teljes mentési beállításokat.", "dashboard.backup.importJson": "JSON importálása", "dashboard.backup.latestSnapshot": "Legutóbbi pillanatkép: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Biztonsági mentés beállításai", + "dashboard.backup.openSettings": "Mentési beállítások", "dashboard.backup.title": "Biztonsági mentés és visszaállítás", "dashboard.backup.unnamedSnapshot": "Névtelen pillanatkép", "dashboard.composition.avgPerScene": "Átl. / jelenet", diff --git a/public/locales/is/bundle.json b/public/locales/is/bundle.json index d94ffd56..0c6888a8 100644 --- a/public/locales/is/bundle.json +++ b/public/locales/is/bundle.json @@ -885,11 +885,11 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} viðvaranir — fínstilltu dagsetningar/lengdir senu á Senuborðinu.", "dashboard.authorInsights.title": "Innsýn í höfundarferlið", "dashboard.backup.exportJson": "Flytja út JSON", - "dashboard.backup.hint": "Flyttu út verkefnið þitt, fluttu inn afrit eða opnaðu allar öryggisafritunarstillingar.", + "dashboard.backup.hint": "Flyttu út verkefnið þitt, flyttu inn afrit, eða opnaðu fullar afritunarstillingar.", "dashboard.backup.importJson": "Flytja inn JSON", "dashboard.backup.latestSnapshot": "Nýjasta skyndimynd: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Stillingar fyrir öryggisafrit", - "dashboard.backup.title": "Afrita og endurheimta", + "dashboard.backup.openSettings": "Afritunarstillingar", + "dashboard.backup.title": "Afritun og endurheimt", "dashboard.backup.unnamedSnapshot": "Ónefnd skyndimynd", "dashboard.composition.avgPerScene": "Meðaltal / senu", "dashboard.composition.empty": "Bættu senum við í Handritið til að sjá samsetningu þess.", diff --git a/public/locales/ja/bundle.json b/public/locales/ja/bundle.json index 2d8ba81f..1e13e4ee 100644 --- a/public/locales/ja/bundle.json +++ b/public/locales/ja/bundle.json @@ -884,8 +884,8 @@ "dashboard.authorInsights.timelineClear": "シーンのメタデータからのタイムライン警告はありません。", "dashboard.authorInsights.timelineWarnBadge": "{{count}} 件の警告 — シーン ボードでシーンの日付/期間を調整します。", "dashboard.authorInsights.title": "著者パイプラインの洞察", - "dashboard.backup.exportJson": "エクスポート JSON", - "dashboard.backup.hint": "エクスポート your project, import a backup, or open full backup settings.", + "dashboard.backup.exportJson": "JSONのエクスポート", + "dashboard.backup.hint": "プロジェクトをエクスポートするか、バックアップをインポートするか、完全バックアップ設定を開きます。", "dashboard.backup.importJson": "JSONをインポートする", "dashboard.backup.latestSnapshot": "最新のスナップショット: {{name}} ({{date}})", "dashboard.backup.openSettings": "バックアップ設定", diff --git a/public/locales/pt/bundle.json b/public/locales/pt/bundle.json index ffff2afd..215efa72 100644 --- a/public/locales/pt/bundle.json +++ b/public/locales/pt/bundle.json @@ -885,7 +885,7 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} avisos — refine as datas/durações das cenas no Scene Board.", "dashboard.authorInsights.title": "Insights do pipeline do autor", "dashboard.backup.exportJson": "Exportar JSON", - "dashboard.backup.hint": "Exportar your project, import a backup, or open full backup settings.", + "dashboard.backup.hint": "Exporte seu projeto, importe um backup ou abra as configurações de backup completo.", "dashboard.backup.importJson": "Importar JSON", "dashboard.backup.latestSnapshot": "Instantâneo mais recente: {{name}} ({{date}})", "dashboard.backup.openSettings": "Configurações de backup", diff --git a/public/locales/sv/bundle.json b/public/locales/sv/bundle.json index bf4e2b06..6c542cb6 100644 --- a/public/locales/sv/bundle.json +++ b/public/locales/sv/bundle.json @@ -885,11 +885,11 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} varningar — finjustera scenernas datum/varaktigheter på Scentavlan.", "dashboard.authorInsights.title": "Insikter om författarpipelinen", "dashboard.backup.exportJson": "Exportera JSON", - "dashboard.backup.hint": "Exportera ditt projekt, importera en säkerhetskopia eller öppna inställningar för fullständig säkerhetskopiering.", + "dashboard.backup.hint": "Exportera ditt projekt, importera en säkerhetskopia eller öppna fullständiga inställningar för säkerhetskopiering.", "dashboard.backup.importJson": "Importera JSON", - "dashboard.backup.latestSnapshot": "Senaste ögonblicksbilden: {{name}} ({{date}})", - "dashboard.backup.openSettings": "Säkerhetskopieringsinställningar", - "dashboard.backup.title": "Säkerhetskopiera och återställa", + "dashboard.backup.latestSnapshot": "Senaste ögonblicksbild: {{name}} ({{date}})", + "dashboard.backup.openSettings": "Inställningar för säkerhetskopiering", + "dashboard.backup.title": "Säkerhetskopiering och återställning", "dashboard.backup.unnamedSnapshot": "Namnlös ögonblicksbild", "dashboard.composition.avgPerScene": "Snitt / scen", "dashboard.composition.empty": "Lägg till scener i manuskriptet för att se dess sammansättning.", diff --git a/public/locales/zh/bundle.json b/public/locales/zh/bundle.json index e0e54f7a..aa09be0f 100644 --- a/public/locales/zh/bundle.json +++ b/public/locales/zh/bundle.json @@ -885,7 +885,7 @@ "dashboard.authorInsights.timelineWarnBadge": "{{count}} 条警告 — 在场景板上优化场景日期/持续时间。", "dashboard.authorInsights.title": "作者管道见解", "dashboard.backup.exportJson": "导出 JSON", - "dashboard.backup.hint": "导出 your project, import a backup, or open full backup settings.", + "dashboard.backup.hint": "导出项目、导入备份或打开完整备份设置。", "dashboard.backup.importJson": "导入 JSON", "dashboard.backup.latestSnapshot": "最新快照:{{name}}({{date}})", "dashboard.backup.openSettings": "备份设置", From 48c59e1537a50abae86d9a7247e34e9fb3d67c0e Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Wed, 29 Jul 2026 03:18:01 +0200 Subject: [PATCH 4/4] fix: address CodeRabbit review wave on PR #281 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dependabot-automerge.yml gated a contents:write/pull-requests:write job on github.actor == 'dependabot[bot]'. github.actor reflects the last actor to trigger the workflow event, not the immutable PR author, and is spoofable (confused-deputy risk — zizmor's bot-conditions rule flagged this too). Switched to github.event.pull_request.user.login, set at PR creation and unaffected by later synchronize/re-run events. - audit-feature-parity.ts: collapsed a 6-line rationale comment to one line and added the single-line QNBS-v3 markers CodeRabbit found missing on the dedicatedUiFlags row-model/warning-count/reporting changes. --- .github/workflows/dependabot-automerge.yml | 5 ++++- scripts/audit-feature-parity.ts | 17 +++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 4ec79a60..30f3999e 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -15,7 +15,10 @@ permissions: jobs: auto-merge: name: Auto-merge patch-level Dependabot PRs - if: github.actor == 'dependabot[bot]' + # QNBS-v3: github.actor is the last actor to trigger the event, not the immutable PR author — + # spoofable (confused-deputy risk) for gating a contents:write/pull-requests:write job. + # github.event.pull_request.user.login is set at PR creation and can't be manipulated this way. + if: github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest permissions: contents: write diff --git a/scripts/audit-feature-parity.ts b/scripts/audit-feature-parity.ts index 66f749b0..8af3d079 100644 --- a/scripts/audit-feature-parity.ts +++ b/scripts/audit-feature-parity.ts @@ -134,12 +134,7 @@ function extractSectionFlags(sectionSrc: string, catalogSrc: string): Set !hidden.has(flag))); } -// QNBS-v3: a flag hidden from FeatureFlagsSection.tsx (e.g. enableIdbAtRestEncryption, whose toggle -// would lock users out without the passphrase setup flow) can still have a real UI toggle — just a -// dedicated one elsewhere (PrivacySection.tsx), not the generic catalog-driven list. Each catalog -// entry's own `gateLocations` already documents this; a flag counts as having a dedicated toggle if -// any gateLocation file lives under components/settings/ and isn't FeatureFlagsSection.tsx itself -// (that generic list is what extractSectionFlags/inSection already checks). +// QNBS-v3: a flag hidden from FeatureFlagsSection.tsx can still have a real toggle elsewhere (e.g. enableIdbAtRestEncryption → PrivacySection.tsx) — derived from each catalog entry's own gateLocations. function extractDedicatedUiFlags(catalogSrc: string): Set { const dedicated = new Set(); const flagKeyMatches = [...catalogSrc.matchAll(/flagKey:\s*['"`](enable\w+)['"`]/g)]; @@ -191,6 +186,7 @@ const sliceFlags = extractFlagsFromSlice(sliceSrc); const defaultFlags = extractDefaultsFromSlice(sliceSrc); const localeFlags = extractLocaleFlags(localeSrc); const sectionFlags = extractSectionFlags(sectionSrc, catalogSrc); +// QNBS-v3: tracked separately from sectionFlags so a dedicated-elsewhere toggle doesn't get conflated with the generic catalog-driven list. const dedicatedUiFlags = extractDedicatedUiFlags(catalogSrc); const handlerFlags = extractHandlerFlags(hookSrc); @@ -205,6 +201,7 @@ const rows: Array<{ inDefaults: boolean; inLocale: boolean; inSection: boolean; + // QNBS-v3: kept alongside inSection so the report can distinguish "no UI" from "UI elsewhere". hasDedicatedUi: boolean; inHandler: boolean; hasRuntime: boolean; @@ -214,6 +211,7 @@ for (const flag of sliceFlags) { const inDefaults = defaultFlags.has(flag); const inLocale = localeFlags.has(flag); const inSection = sectionFlags.has(flag); + // QNBS-v3: computed independently of inSection — a flag can be both "not in the generic section" and "has a dedicated toggle" at once. const hasDedicatedUi = dedicatedUiFlags.has(flag); const inHandler = handlerFlags.has(flag); const hasRuntime = hasRuntimeConsumption(flag); @@ -236,8 +234,7 @@ console.log('─'.repeat(90)); for (const row of rows) { const check = (v: boolean) => (v ? green('✅') : red('❌')); const runtimeCheck = row.hasRuntime ? green('✅') : yellow('⚠️ '); - // QNBS-v3: a dedicated toggle elsewhere (e.g. PrivacySection.tsx) is a real UI toggle, just not - // the generic catalog-driven one — show it distinctly rather than as a flat ❌ "no toggle at all". + // QNBS-v3: a dedicated toggle elsewhere (e.g. PrivacySection.tsx) is real UI, not a flat "no toggle" ❌ — shown distinctly as ◆. const sectionCheck = row.inSection ? green('✅') : row.hasDedicatedUi ? green('◆') : red('❌'); const flagLabel = row.flag.padEnd(34); @@ -255,7 +252,7 @@ for (const row of rows) { // Count issues if (!row.inDefaults) errors++; if (!row.inLocale) errors++; - if (!row.inSection && !row.hasDedicatedUi) warnings++; // warning — dev-only flags may intentionally skip UI entirely + if (!row.inSection && !row.hasDedicatedUi) warnings++; // QNBS-v3: only warns when there's truly no toggle anywhere — dev-only flags may intentionally skip UI if (!row.inSection && row.inHandler) errors++; // handler without UI toggle is dead code if (row.inSection && !row.inHandler) errors++; // UI toggle with no handler = critical bug if (!row.hasRuntime) warnings++; // ghost flag @@ -312,7 +309,7 @@ if (ghostFlags.length > 0) { console.log(); } -// Info: flags with a dedicated toggle outside the generic catalog-driven section (real UI, not missing) +// QNBS-v3: reported separately from noUiToggle below — these flags DO have real UI, just not in the generic section, so they shouldn't read as a gap. const dedicatedUiOnly = rows.filter((r) => !r.inSection && r.hasDedicatedUi); if (dedicatedUiOnly.length > 0) { console.log(green('INFO — Flags with a dedicated UI toggle outside FeatureFlagsSection.tsx:'));