From 6001f0167cf38802f22ac5ed1f0d9683e6c4cdc0 Mon Sep 17 00:00:00 2001 From: HuanLinOTO Date: Mon, 24 Aug 2026 17:54:33 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=90=88=E5=B9=B6=20main=20=E5=90=8E?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E5=9B=9E=E5=BD=92=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - index.ts: 移除 #130 遗留的 ctx.on('session/disposed')(fake ctx 无 on, 且 #130 已按取 HEAD 决策,closeSession 调用方一并移除) - browser.probe: 405→GET 重试后跳过 embed-signals GET,避免多余第 3 次请求(#69) - EditorHost: 移植 #228 的 dirty 确认守卫到 HEAD 的 reloadSeq 结构(refreshFile) - tests: fake ctx 补 get()(#357 ctx.get 回归,free-window/editor-refresh/markdown) - tests: prefs 期望对象补 browserAllowedLoopback(smoke/plugin-shape/state) - tests: builtins 浏览器设置行补 browserAllowedLoopback(#69) - tests: install-powershell BOM 门限仅对实际文档化远程调用的文件生效 - locales: 19 语言补齐 showInFolder / refreshUnsavedConfirm(#94 #215 #228) --- src/client/EditorHost.tsx | 14 +++++++++++++- src/client/locales-ar.ts | 2 ++ src/client/locales-de.ts | 2 ++ src/client/locales-fr.ts | 2 ++ src/client/locales-hi.ts | 2 ++ src/client/locales-id.ts | 2 ++ src/client/locales-it.ts | 2 ++ src/client/locales-ja.ts | 2 ++ src/client/locales-ko.ts | 2 ++ src/client/locales-nl.ts | 2 ++ src/client/locales-pl.ts | 2 ++ src/client/locales-pt.ts | 2 ++ src/client/locales-ru.ts | 2 ++ src/client/locales-sv.ts | 2 ++ src/client/locales-th.ts | 2 ++ src/client/locales-tr.ts | 2 ++ src/client/locales-vi.ts | 2 ++ src/client/locales-zh-HK.ts | 2 ++ src/client/locales-zh-MO.ts | 2 ++ src/client/locales-zh-TW.ts | 2 ++ src/index.ts | 13 +++++-------- src/pty-manager.ts | 6 ------ tests/builtins.spec.ts | 1 + tests/editor-refresh.spec.tsx | 1 + tests/free-window.spec.tsx | 1 + tests/install-powershell.spec.ts | 9 ++++++++- tests/markdown-manual-refresh.spec.tsx | 5 ++++- tests/plugin-shape.spec.ts | 2 +- tests/smoke.spec.ts | 1 + tests/state.spec.ts | 5 ++++- 30 files changed, 77 insertions(+), 19 deletions(-) diff --git a/src/client/EditorHost.tsx b/src/client/EditorHost.tsx index 31a019e..2ae2124 100644 --- a/src/client/EditorHost.tsx +++ b/src/client/EditorHost.tsx @@ -113,6 +113,18 @@ export function EditorHost(props: { // with the same path/scope — the only reload entry besides open/close. const [reloadSeq, setReloadSeq] = useState(0) + // Manual refresh (issue #167 + PR #228): a dirty draft is dropped by the + // reload (the editor instance remounts), so confirm before discarding it. + const refreshFile = (): void => { + if (toolbar?.dirty === true) { + const confirmed = typeof window.confirm === 'function' + ? window.confirm(t('refreshUnsavedConfirm')) + : false + if (!confirmed) return + } + setReloadSeq(sequence => sequence + 1) + } + // Reactive prefs read: flipping editorExplorer re-renders this tab with no // reload. The snapshot is the bare boolean so unrelated store churn never // re-renders the editor. @@ -426,7 +438,7 @@ export function EditorHost(props: { className={css.iconButton} aria-label={t('refresh')} title={t('refresh')} - onClick={() => { setReloadSeq(sequence => sequence + 1) }} + onClick={refreshFile} > diff --git a/src/client/locales-ar.ts b/src/client/locales-ar.ts index f1bdc62..fe0b11f 100644 --- a/src/client/locales-ar.ts +++ b/src/client/locales-ar.ts @@ -88,6 +88,8 @@ export const ar: Record = { mermaidZoomReset: 'إعادة تعيين', mermaidZoomHint: 'العجلة للتقريب · السحب للتحريك · Esc للإغلاق', refresh: 'تحديث', + showInFolder: 'إظهار في المجلد', + refreshUnsavedConfirm: 'تغيّر الملف على القرص. سيؤدي التحديث إلى فقدان التعديلات غير المحفوظة. متابعة؟', save: 'حفظ', saved: 'تم الحفظ', unsaved: 'غير محفوظ', diff --git a/src/client/locales-de.ts b/src/client/locales-de.ts index cda2c46..748dc1d 100644 --- a/src/client/locales-de.ts +++ b/src/client/locales-de.ts @@ -73,6 +73,8 @@ export const de: Record = { mermaidZoomReset: 'Zurücksetzen', mermaidZoomHint: 'Scrollen zum Zoomen · Ziehen zum Verschieben · Esc zum Schließen', refresh: 'Aktualisieren', + showInFolder: 'Im Ordner anzeigen', + refreshUnsavedConfirm: 'Die Datei wurde geändert. Beim Aktualisieren gehen ungespeicherte Änderungen verloren. Fortfahren?', save: 'Speichern', saved: 'Gespeichert', unsaved: 'Nicht gespeichert', diff --git a/src/client/locales-fr.ts b/src/client/locales-fr.ts index ebbd02e..1633cb2 100644 --- a/src/client/locales-fr.ts +++ b/src/client/locales-fr.ts @@ -80,6 +80,8 @@ export const fr: Record = { mermaidZoomReset: 'Réinitialiser', mermaidZoomHint: 'Molette pour zoomer · glisser pour déplacer · Échap pour fermer', refresh: 'Actualiser', + showInFolder: 'Afficher dans le dossier', + refreshUnsavedConfirm: 'Le fichier a changé sur le disque. Actualiser supprimera les modifications non enregistrées. Continuer ?', save: 'Enregistrer', saved: 'Enregistré', unsaved: 'Non enregistré', diff --git a/src/client/locales-hi.ts b/src/client/locales-hi.ts index c2fa81c..b9b0739 100644 --- a/src/client/locales-hi.ts +++ b/src/client/locales-hi.ts @@ -87,6 +87,8 @@ export const hi: Record = { mermaidZoomReset: 'रीसेट', mermaidZoomHint: 'स्क्रॉल से ज़ूम · खींचें पैन करने · Esc से बंद', refresh: 'ताज़ा करें', + showInFolder: 'फ़ोल्डर में दिखाएँ', + refreshUnsavedConfirm: 'डिस्क पर फ़ाइल बदल गई है। रीफ़्रेश करने पर असहेज किए गए संपादन खो जाएँगे। जारी रखें?', save: 'सहेजें', saved: 'सहेजा गया', unsaved: 'असहेजित', diff --git a/src/client/locales-id.ts b/src/client/locales-id.ts index 91ed6ad..cd34049 100644 --- a/src/client/locales-id.ts +++ b/src/client/locales-id.ts @@ -85,6 +85,8 @@ export const id: Record = { mermaidZoomReset: 'Reset', mermaidZoomHint: 'Gulir untuk zoom · seret untuk geser · Esc untuk tutup', refresh: 'Segarkan', + showInFolder: 'Tampilkan di folder', + refreshUnsavedConfirm: 'File berubah di disk. Menyegarkan akan membuang edit yang belum disimpan. Lanjutkan?', save: 'Simpan', saved: 'Tersimpan', unsaved: 'Belum disimpan', diff --git a/src/client/locales-it.ts b/src/client/locales-it.ts index 9476777..841321f 100644 --- a/src/client/locales-it.ts +++ b/src/client/locales-it.ts @@ -78,6 +78,8 @@ export const it: Record = { mermaidZoomReset: 'Ripristina', mermaidZoomHint: 'Scorra per zoom · trascini per panoramica · Esc per chiudere', refresh: 'Aggiorna', + showInFolder: 'Mostra nella cartella', + refreshUnsavedConfirm: 'Il file è cambiato su disco. Aggiornare scarterà le modifiche non salvate. Continuare?', save: 'Salva', saved: 'Salvato', unsaved: 'Non salvato', diff --git a/src/client/locales-ja.ts b/src/client/locales-ja.ts index c9d275b..3ba93c1 100644 --- a/src/client/locales-ja.ts +++ b/src/client/locales-ja.ts @@ -87,6 +87,8 @@ export const ja: Record = { mermaidZoomReset: 'リセット', mermaidZoomHint: 'スクロールで拡大 ・ ドラッグで移動 ・ Esc で閉じる', refresh: '更新', + showInFolder: 'フォルダーで表示', + refreshUnsavedConfirm: 'ディスク上のファイルが変更されました。更新すると未保存の編集が失われます。続行しますか?', save: '保存', saved: '保存済み', unsaved: '未保存', diff --git a/src/client/locales-ko.ts b/src/client/locales-ko.ts index b22f2e7..3fbc2df 100644 --- a/src/client/locales-ko.ts +++ b/src/client/locales-ko.ts @@ -79,6 +79,8 @@ export const ko: Record = { mermaidZoomReset: '초기화', mermaidZoomHint: '휠로 확대/축소 · 드래그로 이동 · Esc로 닫기', refresh: '새로고침', + showInFolder: '폴더에서 표시', + refreshUnsavedConfirm: '디스크의 파일이 변경되었습니다. 새로고침하면 저장되지 않은 편집 내용이 손실됩니다. 계속하시겠습니까?', save: '저장', saved: '저장됨', unsaved: '저장 안 됨', diff --git a/src/client/locales-nl.ts b/src/client/locales-nl.ts index 6b1d4f5..259bf31 100644 --- a/src/client/locales-nl.ts +++ b/src/client/locales-nl.ts @@ -85,6 +85,8 @@ export const nl: Record = { mermaidZoomReset: 'Resetten', mermaidZoomHint: 'Scrollen om te zoomen · slepen om te pannen · Esc om te sluiten', refresh: 'Vernieuwen', + showInFolder: 'In map tonen', + refreshUnsavedConfirm: 'Het bestand is op schijf gewijzigd. Vernieuwen gooit niet-opgeslagen wijzigingen weg. Doorgaan?', save: 'Opslaan', saved: 'Opgeslagen', unsaved: 'Niet opgeslagen', diff --git a/src/client/locales-pl.ts b/src/client/locales-pl.ts index 47bce32..a6ef610 100644 --- a/src/client/locales-pl.ts +++ b/src/client/locales-pl.ts @@ -89,6 +89,8 @@ export const pl: Record = { mermaidZoomReset: 'Resetuj', mermaidZoomHint: 'Kółko: powiększenie · przeciąganie: panowanie · Esc: zamknięcie', refresh: 'Odśwież', + showInFolder: 'Pokaż w folderze', + refreshUnsavedConfirm: 'Plik zmienił się na dysku. Odświeżenie odrzuci niezapisane zmiany. Kontynuować?', save: 'Zapisz', saved: 'Zapisano', unsaved: 'Niezapisane', diff --git a/src/client/locales-pt.ts b/src/client/locales-pt.ts index 3dd75ee..8c6c876 100644 --- a/src/client/locales-pt.ts +++ b/src/client/locales-pt.ts @@ -70,6 +70,8 @@ export const pt: Record = { mermaidZoomReset: 'Redefinir', mermaidZoomHint: 'Rolar para ampliar · arrastar para mover · Esc para fechar', refresh: 'Atualizar', + showInFolder: 'Mostrar na pasta', + refreshUnsavedConfirm: 'O arquivo mudou no disco. Atualizar descartará edições não salvas. Continuar?', save: 'Salvar', saved: 'Salvo', unsaved: 'Não salvo', diff --git a/src/client/locales-ru.ts b/src/client/locales-ru.ts index 63db703..dab6236 100644 --- a/src/client/locales-ru.ts +++ b/src/client/locales-ru.ts @@ -83,6 +83,8 @@ export const ru: Record = { mermaidZoomReset: 'Сбросить', mermaidZoomHint: 'Колесо — масштаб · перетаскивание — панорама · Esc — закрыть', refresh: 'Обновить', + showInFolder: 'Показать в папке', + refreshUnsavedConfirm: 'Файл изменился на диске. Обновление отбросит несохранённые изменения. Продолжить?', save: 'Сохранить', saved: 'Сохранено', unsaved: 'Не сохранено', diff --git a/src/client/locales-sv.ts b/src/client/locales-sv.ts index 3efe359..a6aed2f 100644 --- a/src/client/locales-sv.ts +++ b/src/client/locales-sv.ts @@ -70,6 +70,8 @@ export const sv: Record = { mermaidZoomReset: 'Återställ', mermaidZoomHint: 'Rulla för att zooma · dra för att panorera · Esc för att stänga', refresh: 'Uppdatera', + showInFolder: 'Visa i mapp', + refreshUnsavedConfirm: 'Filen ändrades på disken. Uppdatering kastar osparade ändringar. Fortsätta?', save: 'Spara', saved: 'Sparad', unsaved: 'Osparad', diff --git a/src/client/locales-th.ts b/src/client/locales-th.ts index 63c1501..4431c93 100644 --- a/src/client/locales-th.ts +++ b/src/client/locales-th.ts @@ -87,6 +87,8 @@ export const th: Record = { mermaidZoomReset: 'รีเซ็ต', mermaidZoomHint: 'เลื่อนเพื่อซูม · ลากเพื่อแพน · Esc เพื่อปิด', refresh: 'รีเฟรช', + showInFolder: 'แสดงในโฟลเดอร์', + refreshUnsavedConfirm: 'ไฟล์เปลี่ยนบนดิสก์ การรีเฟรชจะทิ้งการแก้ไขที่ยังไม่ได้บันทึก ดำเนินการต่อหรือไม่', save: 'บันทึก', saved: 'บันทึกแล้ว', unsaved: 'ยังไม่ได้บันทึก', diff --git a/src/client/locales-tr.ts b/src/client/locales-tr.ts index cb33f11..7e385d8 100644 --- a/src/client/locales-tr.ts +++ b/src/client/locales-tr.ts @@ -87,6 +87,8 @@ export const tr: Record = { mermaidZoomReset: 'Sıfırla', mermaidZoomHint: 'Yakınlaştırmak için kaydır · taşımak için sürükle · kapatmak için Esc', refresh: 'Yenile', + showInFolder: 'Klasörde göster', + refreshUnsavedConfirm: 'Dosya diskte değişti. Yenileme kaydedilmemiş düzenlemeleri atar. Devam edilsin mi?', save: 'Kaydet', saved: 'Kaydedildi', unsaved: 'Kaydedilmedi', diff --git a/src/client/locales-vi.ts b/src/client/locales-vi.ts index 6cbac91..1578e52 100644 --- a/src/client/locales-vi.ts +++ b/src/client/locales-vi.ts @@ -87,6 +87,8 @@ export const vi: Record = { mermaidZoomReset: 'Đặt lại', mermaidZoomHint: 'Cuộn để zoom · kéo để di chuyển · Esc để đóng', refresh: 'Làm mới', + showInFolder: 'Hiển thị trong thư mục', + refreshUnsavedConfirm: 'Tệp đã thay đổi trên đĩa. Làm mới sẽ loại bỏ các chỉnh sửa chưa lưu. Tiếp tục?', save: 'Lưu', saved: 'Đã lưu', unsaved: 'Chưa lưu', diff --git a/src/client/locales-zh-HK.ts b/src/client/locales-zh-HK.ts index 6be4a8b..9df284e 100644 --- a/src/client/locales-zh-HK.ts +++ b/src/client/locales-zh-HK.ts @@ -102,6 +102,8 @@ export const zhHK: Record = { mermaidZoomReset: '重設', mermaidZoomHint: '滾輪縮放 · 拖曳平移 · Esc 關閉', refresh: '重新整理', + showInFolder: '在文件夾中顯示', + refreshUnsavedConfirm: '檔案已在磁碟上變更。重新整理會丟失尚未儲存的編輯。繼續?', save: '儲存', saved: '已儲存', unsaved: '未儲存', diff --git a/src/client/locales-zh-MO.ts b/src/client/locales-zh-MO.ts index 4d72472..ff6b5e7 100644 --- a/src/client/locales-zh-MO.ts +++ b/src/client/locales-zh-MO.ts @@ -102,6 +102,8 @@ export const zhMO: Record = { mermaidZoomReset: '重設', mermaidZoomHint: '滾輪縮放 · 拖曳平移 · Esc 關閉', refresh: '重新整理', + showInFolder: '在文件夾中顯示', + refreshUnsavedConfirm: '檔案已在磁碟上變更。重新整理會丟失尚未儲存的編輯。繼續?', save: '儲存', saved: '已儲存', unsaved: '未儲存', diff --git a/src/client/locales-zh-TW.ts b/src/client/locales-zh-TW.ts index 2c153f2..05084e8 100644 --- a/src/client/locales-zh-TW.ts +++ b/src/client/locales-zh-TW.ts @@ -102,6 +102,8 @@ export const zhTW: Record = { mermaidZoomReset: '重設', mermaidZoomHint: '滾輪縮放 · 拖曳平移 · Esc 關閉', refresh: '重新整理', + showInFolder: '在資料夾中顯示', + refreshUnsavedConfirm: '檔案已在磁碟上變更。重新整理會丟失尚未儲存的編輯。繼續?', save: '儲存', saved: '已儲存', unsaved: '未儲存', diff --git a/src/index.ts b/src/index.ts index d9e47ad..c7498da 100644 --- a/src/index.ts +++ b/src/index.ts @@ -533,8 +533,10 @@ function buildApi( let response = await fetch(parsed, { method: 'HEAD', redirect: 'follow', signal: controller.signal }) // Some servers answer HEAD with 405/501; retry once as GET (the // body is discarded — only the headers matter). + let retriedFromHeadRejection = false if (response.status === 405 || response.status === 501) { response = await fetch(parsed, { method: 'GET', redirect: 'follow', signal: controller.signal }) + retriedFromHeadRejection = true } // Some servers (e.g. aliyun consoles) answer HEAD without the // X-Frame-Options / CSP headers that only their GET response @@ -542,9 +544,11 @@ function buildApi( // would wrongly report the site as embeddable and the plain iframe // would surface the browser's misleading "refused to connect". // Retry once as GET when both signals are absent (body discarded). + // A 405/501 retry already fetched the GET response, so the signals + // are either there or genuinely absent — another GET adds nothing. const hasEmbedSignals = response.headers.get('content-security-policy') !== null || response.headers.get('x-frame-options') !== null - if (!hasEmbedSignals && response.status !== 405 && response.status !== 501) { + if (!hasEmbedSignals && !retriedFromHeadRejection && response.status !== 405 && response.status !== 501) { response = await fetch(parsed, { method: 'GET', redirect: 'follow', signal: controller.signal }) } const csp = response.headers.get('content-security-policy') @@ -1006,13 +1010,6 @@ export function apply(ctx: Context, config?: SidebarConfig): void { agentListWss.close() agentOpenWss.close() }, 'dsh-better-sidebar: teardown') - - // Close all UI terminals of a session when the conversation is deleted — - // terminals follow the session, not the reconnect grace. Agent terminals - // are owned by the agent lifecycle and are not touched here. - ctx.on('session/disposed', (session) => { - try { ptyManager?.closeSession(session?.id) } catch {} - }, { global: true }) } /** Push queued `sidebar_open` requests for one session to a connected view. */ diff --git a/src/pty-manager.ts b/src/pty-manager.ts index 93a56f5..0b0a068 100644 --- a/src/pty-manager.ts +++ b/src/pty-manager.ts @@ -243,12 +243,6 @@ export class PtyManager { } } - /** Close every terminal of one session (the conversation was deleted — - * terminals must follow the session, not linger until the grace expires). */ - closeSession(sessionId: string): void { - for (const key of this.keysOf(sessionId)) this.close(key) - } - /** Close every terminal (plugin teardown). */ disposeAll(): void { for (const timer of this.pendingCloses.values()) clearTimeout(timer) diff --git a/tests/builtins.spec.ts b/tests/builtins.spec.ts index e1baa80..8eaac40 100644 --- a/tests/builtins.spec.ts +++ b/tests/builtins.spec.ts @@ -131,6 +131,7 @@ describe('built-in tab registrations', () => { 'browserInterceptLinks', 'browserInterceptHttp', 'browserInterceptHttps', + 'browserAllowedLoopback', ]) for (const toggle of toggles) { expect(toggle.title).toBeDefined() diff --git a/tests/editor-refresh.spec.tsx b/tests/editor-refresh.spec.tsx index 387a060..a6e9734 100644 --- a/tests/editor-refresh.spec.tsx +++ b/tests/editor-refresh.spec.tsx @@ -80,6 +80,7 @@ function setup(initialMode: 'preview' | 'edit' = 'preview', dirty = false): { const ctx = { betterSidebar: service, sessions: { list: { subscribe: () => () => {}, getSnapshot: () => sessionsSnapshot } }, + get: (name: string) => name === 'betterSidebar' ? service : undefined, } as unknown as Context ctx.betterSidebar.openTab({ type: 'editor', title: 'a.ts', path: '/tmp/a.ts', id: 'editor:/tmp/a.ts' }) const fileTab = (): SidebarTab => diff --git a/tests/free-window.spec.tsx b/tests/free-window.spec.tsx index 568bfe2..963f720 100644 --- a/tests/free-window.spec.tsx +++ b/tests/free-window.spec.tsx @@ -99,6 +99,7 @@ function mountSidebar(sessionId: string = `s1-${++sessionSeq}`): Mounted { locale: { subscribe: () => () => {}, getSnapshot: () => localeSnapshot }, sessions: { list: { subscribe: () => () => {}, getSnapshot: () => sessionsSnapshot } }, betterSidebar: service, + get: (name: string) => name === 'betterSidebar' ? service : undefined, } const root: Root = createRoot(container) act(() => { root.render(createElement(Sidebar, { ctx: ctx as never, store })) }) diff --git a/tests/install-powershell.spec.ts b/tests/install-powershell.spec.ts index bdcd029..6a7c3f5 100644 --- a/tests/install-powershell.spec.ts +++ b/tests/install-powershell.spec.ts @@ -79,7 +79,14 @@ describe('PowerShell installer entry points', () => { const source = readFileSync(resolve(ROOT, file), 'utf8') expect(source, file).not.toMatch(new RegExp(`irm [^\\r\\n]*${INSTALLER_URL.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[^\\r\\n]*\\|\\s*iex`, 'i')) expect(source, file).not.toMatch(/\[scriptblock\]::Create\(\(irm\b/i) - expect(source.match(/\.TrimStart\(\[char\]0xFEFF\)/g)?.length, file).toBeGreaterThanOrEqual(2) + // Where remote invocation IS documented, it must be BOM-safe: the + // READMEs switched to the prompt-based install flow and no longer + // document `irm` at all, so the TrimStart gate applies to the script + // (whose usage comment demonstrates the safe pattern). + const documentsInvocation = /\birm\b|\$script\s*=|\[scriptblock\]/.test(source) + if (documentsInvocation) { + expect(source.match(/\.TrimStart\(\[char\]0xFEFF\)/g)?.length, file).toBeGreaterThanOrEqual(2) + } } }) diff --git a/tests/markdown-manual-refresh.spec.tsx b/tests/markdown-manual-refresh.spec.tsx index 6da925b..dd95b79 100644 --- a/tests/markdown-manual-refresh.spec.tsx +++ b/tests/markdown-manual-refresh.spec.tsx @@ -60,7 +60,10 @@ function setup(): { .flatMap(leaf => leaf.tabs) .find(candidate => candidate.type === 'editor')! const tab: SidebarTab = { ...home, path: '/tmp/notes.md', title: 'notes.md' } - const ctx = { betterSidebar: service } as unknown as Context + const ctx = { + betterSidebar: service, + get: (name: string) => name === 'betterSidebar' ? service : undefined, + } as unknown as Context return { store, ctx, tab } } diff --git a/tests/plugin-shape.spec.ts b/tests/plugin-shape.spec.ts index 992c573..81c1fb0 100644 --- a/tests/plugin-shape.spec.ts +++ b/tests/plugin-shape.spec.ts @@ -94,6 +94,6 @@ describe('dsh-better-sidebar plugin export shape', () => { const overridden = (PrefsSchema as unknown as { (input: Record | undefined): Record })({ openByDefault: false, defaultWidthPercent: 45 }) - expect(overridden).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, agentOpenTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, editorExplorer: false, terminalShell: '', terminalShellArgs: '', titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} }) + expect(overridden).toEqual({ openByDefault: false, defaultWidthPercent: 45, autoOpenSubagent: true, autoOpenJobs: true, agentTerminalTools: false, agentOpenTools: false, bottomPanelAutoTerminal: true, terminalFontFamily: '', terminalFontSize: 13, interceptOpenPath: true, editorExplorer: false, terminalShell: '', terminalShellArgs: '', titleBarCompat: false, titleBarStripPx: 40, htmlViewerNoSandbox: false, htmlViewerDefaultUnsafe: false, browserNoSandbox: false, browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, browserAllowedLoopback: '', tabsEnabled: {}, viewersEnabled: {}, pluginSettings: {} }) }) }) diff --git a/tests/smoke.spec.ts b/tests/smoke.spec.ts index ad21a51..dd98d33 100644 --- a/tests/smoke.spec.ts +++ b/tests/smoke.spec.ts @@ -907,6 +907,7 @@ describe('side card settings routes', () => { browserInterceptLinks: true, browserInterceptHttp: true, browserInterceptHttps: false, + browserAllowedLoopback: '', // The enable-switch maps default to {} (everything on). tabsEnabled: {}, viewersEnabled: {}, diff --git a/tests/state.spec.ts b/tests/state.spec.ts index d542b12..4ca4ec3 100644 --- a/tests/state.spec.ts +++ b/tests/state.spec.ts @@ -871,7 +871,10 @@ describe('v0.12.0 store additions', () => { // timers BOTH writes are pending and both land when they fire. expect(timers.size).toBe(2) for (const [, fn] of [...timers]) fn() - expect(writes).toEqual(['dsh-sidebar:v1:a', 'dsh-sidebar:v1:b']) + // Each persist also syncs the shared cross-session width key (PR #36). + expect(writes.filter(key => key !== 'dsh-sidebar:v1:width')) + .toEqual(['dsh-sidebar:v1:a', 'dsh-sidebar:v1:b']) + expect(writes).toContain('dsh-sidebar:v1:width') } finally { delete g.window delete g.localStorage