feat(desktop): surface language toggle#20
Conversation
Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com>
There was a problem hiding this comment.
Findings
-
[Major] Wiring
registerLocaleIpc()makes locale persistence live, but that implementation currently writes to a hardcoded~/.config/open-codesign/locale.jsonpath. The repo explicitly says not to hard-code paths and to respect XDG / Electron path helpers, so this will diverge from non-default config roots. Evidenceapps/desktop/src/main/index.ts:258, contextapps/desktop/src/main/locale-ipc.ts:19.
Suggested fix:import { join } from 'node:path'; import { app } from './electron-runtime'; const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');
-
[Major] The new locale IPC contract is unversioned (
locale:get-system,locale:get-current,locale:set).docs/PRINCIPLES.md§5b requires every IPC channel to be versioned so future contract changes can ship side-by-side without breaking older renderers. Evidenceapps/desktop/src/preload/index.ts:56.
Suggested fix:const LOCALE_GET_SYSTEM = 'locale:v1:get-system'; const LOCALE_GET_CURRENT = 'locale:v1:get-current'; const LOCALE_SET = 'locale:v1:set';
-
[Minor]
LanguageToggleintroduces a hardcodedtext-[12px]literal. UI values are supposed to come frompackages/uitokens, so this adds a new §8 violation on a freshly added component. Evidenceapps/desktop/src/renderer/src/components/LanguageToggle.tsx:33.
Suggested fix:className="... text-[var(--text-xs)] ..."
-
[Minor] The newly modified top bar still hardcodes English tooltip/button copy, so switching to
zh-CNleaves part of the shell untranslated even though this PR is specifically surfacing language switching. Evidenceapps/desktop/src/renderer/src/components/TopBar.tsx:39.
Suggested fix:<Tooltip label={t('commands.tooltips.commandPalette')}> <IconButton label={t('commands.items.openCommandPalette')} size="sm" onClick={openCommandPalette}>
Summary
- Review mode: initial. Four issues found: one path-handling regression activated by the new locale IPC wiring, one compatibility violation in the new IPC surface, and two renderer-level consistency issues in the new language toggle/top-bar UI.
Testing
- Not run (automation): this runner has no installed
node_modules, socorepack pnpm --filter @open-codesign/desktop typecheckandcorepack pnpm --filter @open-codesign/i18n testboth failed before execution. No new Vitest/Playwright coverage was added for locale persistence, bootstrap, or translated shell copy.
open-codesign Bot
| initLogger(); | ||
| await loadConfigOnBoot(); | ||
| registerIpcHandlers(); | ||
| registerLocaleIpc(); |
There was a problem hiding this comment.
Activating registerLocaleIpc() makes locale persistence live, but that implementation currently writes to a hardcoded ~/.config/open-codesign/locale.json path (apps/desktop/src/main/locale-ipc.ts:19). The repo explicitly says not to hard-code paths and to respect XDG / Electron path helpers. Can this be moved under app.getPath("userData") or the same config-dir helper used elsewhere?
Suggested fix:
import { join } from 'node:path';
import { app } from './electron-runtime';
const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');| export: (payload: { format: ExportFormat; htmlContent: string; defaultFilename?: string }) => | ||
| ipcRenderer.invoke('codesign:export', payload) as Promise<ExportInvokeResponse>, | ||
| locale: { | ||
| getSystem: () => ipcRenderer.invoke('locale:get-system') as Promise<string>, |
There was a problem hiding this comment.
These new locale IPC channels are unversioned (locale:get-system, locale:get-current, locale:set). docs/PRINCIPLES.md §5b requires every IPC channel to be versioned so protocol changes can ship side-by-side without breaking older renderers.
Suggested fix:
const LOCALE_GET_SYSTEM = 'locale:v1:get-system';
const LOCALE_GET_CURRENT = 'locale:v1:get-current';
const LOCALE_SET = 'locale:v1:set';| <button | ||
| type="button" | ||
| onClick={() => void handleToggle()} | ||
| className="inline-flex items-center gap-2 h-8 px-3 rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] text-[12px] text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors" |
There was a problem hiding this comment.
text-[12px] introduces a new hardcoded UI literal on a brand-new component. The repo rule is that visual values come from packages/ui tokens, so this should use an existing token or add one first.
Suggested fix:
className="... text-[var(--text-xs)] ..."|
|
||
| <div className="flex items-center gap-1" style={noDragStyle}> | ||
| <Tooltip label="Command palette ⌘K"> | ||
| <Tooltip label="Command palette Ctrl/Cmd+K"> |
There was a problem hiding this comment.
This newly modified top-bar copy is still hardcoded in English, so switching to zh-CN leaves part of the shell untranslated even though this PR is about surfacing language switching. Can these strings move behind translation keys as well?
Suggested fix:
<Tooltip label={t('commands.tooltips.commandPalette')}>
<IconButton label={t('commands.items.openCommandPalette')} size="sm" onClick={openCommandPalette}>0fdd707 to
ed6e073
Compare
There was a problem hiding this comment.
Findings
-
[Major] Locale persistence is now live, but the registered handler still writes to a hardcoded
~/.config/open-codesign/locale.jsonpath. That bypasses Electron/XDG path helpers and directly violates the repo rule against hardcoded paths. Evidenceapps/desktop/src/main/index.ts:258, contextapps/desktop/src/main/locale-ipc.ts:19.
Suggested fix:import { join } from 'node:path'; import { app } from './electron-runtime'; const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');
-
[Major] The new locale IPC surface is unversioned (
locale:get-system,locale:get-current,locale:set).docs/PRINCIPLES.md§5b makes IPC versioning non-optional, so shipping these names as-is creates a compatibility contract we cannot evolve safely. Evidenceapps/desktop/src/preload/index.ts:56.
Suggested fix:const LOCALE_GET_SYSTEM = 'locale:v1:get-system'; const LOCALE_GET_CURRENT = 'locale:v1:get-current'; const LOCALE_SET = 'locale:v1:set';
-
[Major] The renderer now silently degrades when the preload bridge is missing: bootstrap falls back to the default locale and the toggle applies an in-memory change without persisting it. That violates §10's “no silent fallbacks” rule and turns a broken IPC bridge into a hard-to-debug “language reverted after restart” bug. Evidence
apps/desktop/src/renderer/src/main.tsx:12,apps/desktop/src/renderer/src/components/LanguageToggle.tsx:24.
Suggested fix:if (!window.codesign) { throw new Error('Locale bridge unavailable'); } const locale = await window.codesign.locale.getCurrent();
-
[Major] Surfacing
LanguageTogglein onboarding exposes a language switch on a flow whose visible copy is still almost entirely hard-coded English (Welcome,PasteKey,ChooseModel). Inzh-CN, users still see “Design with any model”, “Paste your API key”, “Pick default models”, etc., so the new onboarding toggle does not actually localize the screen it was added to. Evidenceapps/desktop/src/renderer/src/onboarding/index.tsx:70, contextapps/desktop/src/renderer/src/onboarding/Welcome.tsx:15,apps/desktop/src/renderer/src/onboarding/PasteKey.tsx:142,apps/desktop/src/renderer/src/onboarding/ChooseModel.tsx:50.
Suggested fix:const t = useT(); <h1>{t('onboarding.welcome.title')}</h1> <p>{t('onboarding.welcome.subtitle')}</p>
Summary
- Review mode: follow-up after new commits. Four issues found in the newly wired locale/onboarding surfaces: one path-handling regression, one IPC compatibility violation, one silent-fallback regression, and one onboarding-localization gap.
Testing
- Not run (automation):
corepack pnpm --filter @open-codesign/desktop typecheckfailed becausenode_modulesare absent and the runner is on Nodev20.20.2while the repo requires>=22;corepack pnpm --filter @open-codesign/i18n testfailed becausevitestis unavailable without dependencies installed. No new Vitest/Playwright coverage was added for locale bootstrap, persistence, or the onboarding toggle.
open-codesign Bot
| initLogger(); | ||
| await loadConfigOnBoot(); | ||
| registerIpcHandlers(); | ||
| registerLocaleIpc(); |
There was a problem hiding this comment.
Activating registerLocaleIpc() makes locale persistence live, but locale-ipc.ts still writes to a hardcoded ~/.config/open-codesign/locale.json path (apps/desktop/src/main/locale-ipc.ts:19). CLAUDE.md explicitly forbids hardcoded paths. Can this move under app.getPath("userData") or the same config-dir helper used elsewhere?
Suggested fix:
import { join } from 'node:path';
import { app } from './electron-runtime';
const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');| export: (payload: { format: ExportFormat; htmlContent: string; defaultFilename?: string }) => | ||
| ipcRenderer.invoke('codesign:export', payload) as Promise<ExportInvokeResponse>, | ||
| locale: { | ||
| getSystem: () => ipcRenderer.invoke('locale:get-system') as Promise<string>, |
There was a problem hiding this comment.
These new locale IPC channels are unversioned. docs/PRINCIPLES.md §5b requires every IPC contract to ship as vN so future renderer/main mismatches can coexist safely.
Suggested fix:
const LOCALE_GET_SYSTEM = 'locale:v1:get-system';
const LOCALE_GET_CURRENT = 'locale:v1:get-current';
const LOCALE_SET = 'locale:v1:set';| </StrictMode>, | ||
| ); | ||
| async function bootstrap(): Promise<void> { | ||
| const locale = window.codesign ? await window.codesign.locale.getCurrent() : undefined; |
There was a problem hiding this comment.
This boot path silently falls back when the preload bridge is missing. That means a broken locale IPC surface boots in the default language instead of failing loudly, and LanguageToggle has the same pattern when persisting. docs/PRINCIPLES.md §10 bans this kind of masked fallback.
Suggested fix:
if (!window.codesign) {
throw new Error('Locale bridge unavailable');
}
const locale = await window.codesign.locale.getCurrent();| <Wordmark badge="pre-alpha" /> | ||
| <Stepper current={idx} total={3} /> | ||
| <div className="flex items-center gap-2"> | ||
| <LanguageToggle /> |
There was a problem hiding this comment.
Adding LanguageToggle here makes onboarding advertise language switching, but the actual onboarding copy is still hard-coded English in Welcome.tsx, PasteKey.tsx, and ChooseModel.tsx. In zh-CN, the user still sees "Design with any model", "Paste your API key", etc., so this new surface does not localize the screen it was added to.
Suggested fix:
const t = useT();
<h1>{t('onboarding.welcome.title')}</h1>
<p>{t('onboarding.welcome.subtitle')}</p>Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com>
There was a problem hiding this comment.
Findings
-
[Major] Locale persistence still writes to a hardcoded
~/.config/open-codesign/locale.jsonpath once this PR activatesregisterLocaleIpc(). That bypasses Electron/XDG path helpers and violates the repo rule against hardcoded paths. Evidenceapps/desktop/src/main/index.ts:259, contextapps/desktop/src/main/locale-ipc.ts:19.
Suggested fix:import { join } from 'node:path'; import { app } from './electron-runtime'; const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');
-
[Major] The new locale bridge is still exposing unversioned IPC channels (
locale:get-system,locale:get-current,locale:set).docs/PRINCIPLES.md§5b makes IPC versioning non-optional, so this ships a compatibility contract we cannot evolve safely. Evidenceapps/desktop/src/preload/index.ts:56.
Suggested fix:const LOCALE_GET_SYSTEM = 'locale:v1:get-system'; const LOCALE_GET_CURRENT = 'locale:v1:get-current'; const LOCALE_SET = 'locale:v1:set';
-
[Major] Locale bootstrap/toggling still silently falls back when the preload bridge is missing.
main.tsxboots withundefined, andLanguageToggleapplies the locale in memory without persistence, which violates PRINCIPLES §10 and makes a broken preload surface look like “language reverted after restart”. Evidenceapps/desktop/src/renderer/src/main.tsx:12,apps/desktop/src/renderer/src/components/LanguageToggle.tsx:24.
Suggested fix:if (!window.codesign) { throw new Error('Locale bridge unavailable'); } const locale = await window.codesign.locale.getCurrent(); const persisted = await window.codesign.locale.set(target);
-
[Major] Onboarding now advertises a language switch, but the onboarding screens still render hard-coded English even though
onboarding.*translations already exist in the locale bundles. Inzh-CN, switching language still leavesWelcome,Paste your API key, andPick default modelsuntranslated, so the new surface does not localize the flow it was added to. Evidenceapps/desktop/src/renderer/src/onboarding/index.tsx:70, contextapps/desktop/src/renderer/src/onboarding/Welcome.tsx:14,apps/desktop/src/renderer/src/onboarding/PasteKey.tsx:141,apps/desktop/src/renderer/src/onboarding/ChooseModel.tsx:49,packages/i18n/src/locales/en.json:82.
Suggested fix:const t = useT(); <h1>{t('onboarding.welcome.title')}</h1> <p>{t('onboarding.welcome.subtitle')}</p>
Summary
- Review mode: follow-up after new commits. Four issues remain in the locale/onboarding path: hardcoded locale storage, unversioned IPC, silent bridge fallbacks, and an onboarding toggle that does not actually localize the onboarding flow.
Testing
- Not run (automation):
node_modules/is absent in this runner. - Existing coverage only exercises
packages/i18n/src/i18n.test.ts; no Vitest/Playwright coverage was added for Electron preload IPC, renderer bootstrap from persisted locale, or the onboarding toggle.
open-codesign Bot
| initLogger(); | ||
| await loadConfigOnBoot(); | ||
| registerIpcHandlers(); | ||
| registerLocaleIpc(); |
There was a problem hiding this comment.
Activating registerLocaleIpc() makes locale persistence live, but locale-ipc.ts still writes to a hardcoded ~/.config/open-codesign/locale.json path (apps/desktop/src/main/locale-ipc.ts:19). CLAUDE.md forbids hardcoded paths. Can this move under app.getPath('userData')?
Suggested fix:
import { join } from 'node:path';
import { app } from './electron-runtime';
const LOCALE_FILE = join(app.getPath('userData'), 'locale.json');| export: (payload: { format: ExportFormat; htmlContent: string; defaultFilename?: string }) => | ||
| ipcRenderer.invoke('codesign:export', payload) as Promise<ExportInvokeResponse>, | ||
| locale: { | ||
| getSystem: () => ipcRenderer.invoke('locale:get-system') as Promise<string>, |
There was a problem hiding this comment.
These locale IPC channels are still unversioned. docs/PRINCIPLES.md §5b requires every IPC contract to ship as vN so future renderer/main mismatches can coexist safely.
Suggested fix:
const LOCALE_GET_SYSTEM = 'locale:v1:get-system';
const LOCALE_GET_CURRENT = 'locale:v1:get-current';
const LOCALE_SET = 'locale:v1:set';| </StrictMode>, | ||
| ); | ||
| async function bootstrap(): Promise<void> { | ||
| const locale = window.codesign ? await window.codesign.locale.getCurrent() : undefined; |
There was a problem hiding this comment.
This boot path still silently falls back when the preload bridge is missing, and LanguageToggle keeps the same pattern when persisting (apps/desktop/src/renderer/src/components/LanguageToggle.tsx:24). That turns a broken locale bridge into a quiet “language reverted after restart” bug.
Suggested fix:
if (!window.codesign) {
throw new Error('Locale bridge unavailable');
}
const locale = await window.codesign.locale.getCurrent();
const persisted = await window.codesign.locale.set(target);| <Wordmark badge="pre-alpha" /> | ||
| <Stepper current={idx} total={3} /> | ||
| <div className="flex items-center gap-2"> | ||
| <LanguageToggle /> |
There was a problem hiding this comment.
Adding LanguageToggle here advertises locale switching, but the actual onboarding copy is still hard-coded English in Welcome.tsx, PasteKey.tsx, and ChooseModel.tsx, even though packages/i18n/src/locales/en.json / zh-CN.json already define onboarding.* keys. In zh-CN, this flow still renders English.
Suggested fix:
const t = useT();
<h1>{t('onboarding.welcome.title')}</h1>
<p>{t('onboarding.welcome.subtitle')}</p>…in features Rebase onto origin/main exposed conflicts where PRs #18, #19, #20 had advanced store, preload, shared and main/index.ts beyond the branch point. Resolve by taking origin/main as the base and layering our cancellation additions on top: - shared/index.ts: add LocalInputFile, SelectedElement, ElementSelectionRect, ApplyCommentPayload, StoredDesignSystem exports; add generationId to GeneratePayload; keep CancelGenerationPayloadV1 - preload/index.ts: add locale API, pickInputFiles, pickDesignSystemDirectory, clearDesignSystem, applyComment; add generationId to generate payload; keep cancelGeneration with schemaVersion: 1 - main/index.ts: add applyComment, scanDesignSystem, preparePromptContext, registerLocaleIpc; wire AbortController into generate handler; keep codesign:v1:cancel-generation handler - store.ts: merge all origin/main state (selectedElement, inputFiles, referenceUrl, i18n tr(), applyInlineComment, etc.) with PR's activeGenerationId / cancelGeneration / finishIfCurrent guards - store.test.ts: update sendPrompt calls to object form; add designSystem: null to OnboardingState fixture; relax toast title assertion for i18n Signed-off-by: hqhq1025 <1506751656@qq.com>
…in features Rebase onto origin/main exposed conflicts where PRs #18, #19, #20 had advanced store, preload, shared and main/index.ts beyond the branch point. Resolve by taking origin/main as the base and layering our cancellation additions on top: - shared/index.ts: add LocalInputFile, SelectedElement, ElementSelectionRect, ApplyCommentPayload, StoredDesignSystem exports; add generationId to GeneratePayload; keep CancelGenerationPayloadV1 - preload/index.ts: add locale API, pickInputFiles, pickDesignSystemDirectory, clearDesignSystem, applyComment; add generationId to generate payload; keep cancelGeneration with schemaVersion: 1 - main/index.ts: add applyComment, scanDesignSystem, preparePromptContext, registerLocaleIpc; wire AbortController into generate handler; keep codesign:v1:cancel-generation handler - store.ts: merge all origin/main state (selectedElement, inputFiles, referenceUrl, i18n tr(), applyInlineComment, etc.) with PR's activeGenerationId / cancelGeneration / finishIfCurrent guards - store.test.ts: update sendPrompt calls to object form; add designSystem: null to OnboardingState fixture; relax toast title assertion for i18n Signed-off-by: hqhq1025 <1506751656@qq.com>
* feat(desktop): redesign sidebar input + add cancellation IPC - Replace single-line input with autosize textarea (1–6 rows, max-h 144 px) - Move send button into bottom-right corner of textarea container, icon-only 28 px - During generation, send button becomes a stop button (Square icon) wired to cancelGeneration - Remove redundant keyboard-hint bar; fold hint into placeholder text - Enter sends, Shift+Enter inserts newline, ⌘↵ global send preserved - main: extract runGenerate helper; maintain Map<id, AbortController> for in-flight requests; new codesign:cancel-generation IPC handler; logger scope 'ipc' - preload: expose cancelGeneration(id) on window.codesign - shared: GeneratePayload accepts optional generationId - store: add activeGenerationId state + cancelGeneration action; suppress abort errors silently without pushing an error toast Signed-off-by: hqhq1025 <1506751656@qq.com> * fix: harden generation cancellation flows Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com> Signed-off-by: hqhq1025 <1506751656@qq.com> * fix: version cancellation ipc and tokenise sidebar Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com> Signed-off-by: hqhq1025 <1506751656@qq.com> * fix: preserve current-generation failures Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com> Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): guard cancel race + version IPC payload + add tests - applyGenerateSuccess/applyGenerateError already guarded via finishIfCurrent and the early-return id check; confirm with tests - Add noop test: unknown generationId leaves all in-flight intact - Add schema rejection test: empty generationId and missing schemaVersion both throw via CancelGenerationPayloadV1.parse - Fix biome formatting in store.test.ts (pre-existing) Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): properly merge rebase onto main — restore all origin/main features Rebase onto origin/main exposed conflicts where PRs #18, #19, #20 had advanced store, preload, shared and main/index.ts beyond the branch point. Resolve by taking origin/main as the base and layering our cancellation additions on top: - shared/index.ts: add LocalInputFile, SelectedElement, ElementSelectionRect, ApplyCommentPayload, StoredDesignSystem exports; add generationId to GeneratePayload; keep CancelGenerationPayloadV1 - preload/index.ts: add locale API, pickInputFiles, pickDesignSystemDirectory, clearDesignSystem, applyComment; add generationId to generate payload; keep cancelGeneration with schemaVersion: 1 - main/index.ts: add applyComment, scanDesignSystem, preparePromptContext, registerLocaleIpc; wire AbortController into generate handler; keep codesign:v1:cancel-generation handler - store.ts: merge all origin/main state (selectedElement, inputFiles, referenceUrl, i18n tr(), applyInlineComment, etc.) with PR's activeGenerationId / cancelGeneration / finishIfCurrent guards - store.test.ts: update sendPrompt calls to object form; add designSystem: null to OnboardingState fixture; relax toast title assertion for i18n Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): wait for cancel IPC ack + version generate payload Major #1: cancelGeneration now waits for the IPC round-trip before clearing isGenerating/activeGenerationId. If main rejects the cancel (bad id, already aborted) the error becomes visible via toast instead of being silently swallowed. Updated store test to reflect async cancel and drain microtasks between cancel and the follow-up sendPrompt. Major #2: add GeneratePayloadV1 (schemaVersion: 1, generationId required) to @open-codesign/shared and rename the IPC channel to codesign:v1:generate, matching the pattern established by codesign:v1:cancel-generation. Preload builds the v1 envelope with satisfies check; main validates with GeneratePayloadV1.parse(). Add 5 schema tests for missing/wrong schemaVersion and empty generationId. Add notifications.cancellationFailed i18n key. Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): restore legacy generate IPC shim + use ui tokens in sidebar - Add codesign:generate legacy handler that accepts old GeneratePayload (no schemaVersion) and promotes it to V1 by injecting schemaVersion:1 and falling back generationId to gen-${Date.now()} when absent. Emits a warn log to track removal in the next minor release. - Add --color-on-accent token to packages/ui tokens.css (light + dark) so accent-button text never hard-codes a color value. - Replace text-white in Sidebar.tsx with text-[var(--color-on-accent)] on both the stop and send IconButton variants. - Add 3 tests for GeneratePayload legacy schema and the V1 promotion path that the shim relies on. Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): surface error when renderer bridge missing during cancel Split the combined `!id || !window.codesign` guard in `cancelGeneration` into two separate checks. A missing `activeGenerationId` is a legitimate no-op, but a missing IPC bridge is a real error that must be surfaced via `errorMessage`/`lastError` and a toast. Adds a Vitest test to cover the new error path (with `beforeAll` i18n init so `tr()` keys resolve). Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): restore attachments/refURL/designSystem entries + i18n sidebar strings Restores the local context panel (attachments, reference URL, design system) that was accidentally dropped during rebase when the cancel button was added. All hardcoded English strings are now routed through useT(); chat.send and chat.stop keys were added to en.json / zh-CN.json. Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): fix import order and trailing blank line after rebase Biome import sort and formatter fixes introduced during rebase conflict resolution. Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): remove hardcoded textarea fallback, throw on missing tokens Removes FALLBACK_FONT_SIZE and FALLBACK_LINE_HEIGHT_MULTIPLIER constants from getTextareaLineHeight. When computed lineHeight is not a finite positive number and either fontSize or --leading-body token is absent or invalid, the function now throws instead of silently using magic numbers. The --leading-body token is already defined in packages/ui/src/tokens.css. Updates Sidebar.test.ts: replaces the "falls back" test with two tests — one verifying valid-token multiplication and one verifying the throw path. Signed-off-by: hqhq1025 <1506751656@qq.com> --------- Signed-off-by: hqhq1025 <1506751656@qq.com> Signed-off-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com> Co-authored-by: Sun-sunshine06 <Sun-sunshine06@users.noreply.github.com>
The PR commits were originally based on an older version of the codebase and accidentally removed features added by PRs #18 and #20: - Restore design-system.ts and prompt-context.ts (deleted in branch) - Restore InlineCommentComposer.tsx and PreviewPane.test.ts (deleted) - Restore StoredDesignSystem / designSystem support in config.ts - Re-export StoredDesignSystem + STORED_DESIGN_SYSTEM_SCHEMA_VERSION from shared - Re-add getOnboardingState() / setDesignSystem() to onboarding-ipc.ts (these are needed by main/index.ts design-system IPC handlers) - Add full import set to main/index.ts (stat, basename, applyComment, dialog, scanDesignSystem, getCachedConfig, getOnboardingState, setDesignSystem, preparePromptContext) - Restore preload/index.ts: add back applyComment, pickInputFiles, pickDesignSystemDirectory, clearDesignSystem methods removed in PR; keep PR-added settings/preferences namespaces and interfaces - Restore all other files regressed to pre-#18/#20 state (Sidebar.tsx, store.ts, App.tsx, core/index.ts, providers, templates, ui tokens, etc.) - Fix Settings.tsx formatting (spurious blank line) - Fix store.test.ts READY_CONFIG missing designSystem field
…#16) * feat(desktop): settings tabs with multi-provider management and preferences persistence Implements all four settings tabs (Models, Appearance, Storage, Advanced) with full persistence and correct error handling. Key changes: - Settings UI: four-tab panel (Models / Appearance / Storage / Advanced) with provider cards, add/delete/activate flows, model selector, path viewer, reset - toProviderRows: soft-fail on safeStorage decryption — returns error:'decryption_failed' row instead of throwing; UI shows red badge + 'Re-enter key' button - preferences-ipc.ts: new IPC module persisting updateChannel and generationTimeoutSec to ~/.config/open-codesign/preferences.json (schemaVersion:1) - locale-ipc.ts and preferences-ipc.ts registered in main/index.ts - preload: settings, preferences, and locale namespaces exposed to renderer - AppearanceTab: language select reads/writes via locale:get-current / locale:set - AdvancedTab: update channel and generation timeout read/write via preferences IPC - ActiveModelSelector: 400ms debounce with proper useEffect cleanup on unmount - delete-provider: fix ghost active provider — when all providers removed, write tombstone config (empty secrets) instead of falling back to hardcoded 'openai'; re-adding a new provider correctly auto-activates it - provider-settings.ts and onboarding-ipc.ts: use electron-runtime import pattern - Tests: 8 total (5 new), covering decryption-failed row, masked-key row, ghost-provider auto-activate, provider preservation, assertProviderHasStoredSecret Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): switch model defaults on provider delete + guard validate against stale form - Extract computeDeleteProviderResult pure helper to provider-settings.ts; when the active provider is deleted the new active provider's PROVIDER_SHORTLIST defaults are used for modelPrimary/modelFast instead of carrying over the old provider's model IDs (Blocker from #16 review). - Tombstone path (last provider removed) now writes empty model strings so a stale model ID is never persisted. - Guard handleValidate in AddProviderModal: snapshot provider/apiKey/baseUrl before the async call and discard the result via applyValidateResult if the form changed while awaiting (Major race fix from #16 review). - Add applyValidateResult pure exported helper for unit testing without a DOM. - 5 new tests (3 computeDeleteProviderResult + 4 applyValidateResult, total 15). Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): version settings/preferences IPC + ui token compliance - Add settings:v1:* and preferences:v1:* versioned IPC channels (8 settings + 2 prefs) - Keep legacy settings:* and preferences:* channels as shims with logger.warn - Preload now calls v1 channels exclusively - Extract per-handler run* helpers shared by v1 and legacy registrations - Replace text-white with text-[var(--color-on-accent)] in Settings badges/buttons - Replace text-[10px] with text-[var(--font-size-badge)] in ProviderCard badges - Add --color-on-accent and --font-size-badge tokens to packages/ui/src/tokens.css - Add onboarding-ipc.test.ts: 3 tests covering v1 channel registration and legacy compat Signed-off-by: hqhq1025 <1506751656@qq.com> * fix: surface silent failures in locale/preferences/generate paths - Settings.tsx locale-load .catch(() => {}) → pushToast error - Settings.tsx preferences bootstrap .catch(() => {}) → pushToast error - preferences-ipc.ts non-ENOENT read errors → throw CodesignError (PREFERENCES_READ_FAILED) - packages/core generate() empty artifacts → throw CodesignError (OUTPUT_MISSING_ARTIFACT) - Add pushIframeError to CodesignState interface (was implemented but missing from type) - Fix store.test.ts: remove stale designSystem field not present in OnboardingState - Add preferences-ipc.test.ts: 2 tests covering ENOENT default and non-ENOENT throw - Add generate.test.ts: 4 tests covering OUTPUT_MISSING_ARTIFACT error path Signed-off-by: hqhq1025 <1506751656@qq.com> * fix(desktop): incorporate i18n dep and shared type additions from main Add @open-codesign/i18n workspace dep, LocalInputFile/ElementSelection types from main, and fix sendPrompt call signature in App.tsx to match the object-input type declared in store.ts. * fix(desktop): restore main-branch features removed during rebase The PR commits were originally based on an older version of the codebase and accidentally removed features added by PRs #18 and #20: - Restore design-system.ts and prompt-context.ts (deleted in branch) - Restore InlineCommentComposer.tsx and PreviewPane.test.ts (deleted) - Restore StoredDesignSystem / designSystem support in config.ts - Re-export StoredDesignSystem + STORED_DESIGN_SYSTEM_SCHEMA_VERSION from shared - Re-add getOnboardingState() / setDesignSystem() to onboarding-ipc.ts (these are needed by main/index.ts design-system IPC handlers) - Add full import set to main/index.ts (stat, basename, applyComment, dialog, scanDesignSystem, getCachedConfig, getOnboardingState, setDesignSystem, preparePromptContext) - Restore preload/index.ts: add back applyComment, pickInputFiles, pickDesignSystemDirectory, clearDesignSystem methods removed in PR; keep PR-added settings/preferences namespaces and interfaces - Restore all other files regressed to pre-#18/#20 state (Sidebar.tsx, store.ts, App.tsx, core/index.ts, providers, templates, ui tokens, etc.) - Fix Settings.tsx formatting (spurious blank line) - Fix store.test.ts READY_CONFIG missing designSystem field --------- Signed-off-by: hqhq1025 <1506751656@qq.com>
Summary
Testing