From c747f721ba7a424f0c8f591728da0642b9abe271 Mon Sep 17 00:00:00 2001 From: creatixchu Date: Wed, 29 Jul 2026 13:29:17 +0800 Subject: [PATCH] feat(host): fixed-label show-hidden toggle; even divider clearance; 680x500 card --- .../src/client/DirectoryBrowser.module.css | 40 ++++++++++++------- .../src/client/DirectoryBrowser.tsx | 11 +++-- .../src/client/index.ts | 2 - .../tests/client-flow.spec.tsx | 1 - .../tests/directory-browser.spec.tsx | 11 +++-- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.module.css b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.module.css index bdb57ca848..a2c712c811 100644 --- a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.module.css +++ b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.module.css @@ -1,6 +1,7 @@ /* Directory-browser dialog (figma 813-23126 family). The shared Modal renders * headless here — mask, card, Escape only — and this module owns the figma - * frame: 600×420 card (viewport-clamped), header (title + crumbs, l3 separator), + * frame: 680×500 card (viewport-clamped; upsized from the figma 600×420), + * header (title + crumbs, l3 separator), * the one-or-two-column Miller content, and the bordered footer. */ /* Doubled class beats Modal's own .dialog regardless of stylesheet order. */ @@ -8,19 +9,19 @@ * columns scroll, so shrinking the height keeps Open/Cancel reachable * instead of clipping them below a fixed overlay. */ .dialog.dialog { - width: min(600px, 100%); - height: min(420px, calc(100dvh - 32px)); + width: min(680px, 100%); + height: min(500px, calc(100dvh - 32px)); padding: 0; gap: 0; } -/* Header block: pl24 pr14 pt22 pb12, 8px between title row and crumb row. */ +/* Header block: pl24 pr14 pt16 pb8, 8px between title row and crumb row. */ .header { display: flex; flex-direction: column; gap: 8px; flex: none; - padding: 22px 14px 12px 24px; + padding: 16px 14px 8px 24px; border-bottom: 1px solid var(--dsw-alias-border-l3); } @@ -56,6 +57,7 @@ min-height: 0; gap: 20px; overflow-x: auto; + scrollbar-width: none; } .crumbTrail { @@ -126,28 +128,31 @@ color: var(--dsw-alias-label-primary); } -/* Miller content: pt16 px24; columns are 256 wide (or full width solo) with - * the hairline divider centered between them; each column scrolls alone. */ +/* Miller content: symmetric 16px vertical padding so the divider clears the + * header and footer rules evenly; columns are 256 wide (or full width solo) + * with the hairline divider centered between them; each column scrolls alone. */ .content { display: flex; flex-direction: column; flex: 1 1 0; min-height: 0; - padding: 16px 24px 0; + padding: 16px 24px; } +/* Two-pane columns split the row evenly around the divider; 256px is the + * floor below which the row scrolls (scrollbar hidden, the effect pins the + * child pane into view) instead of squeezing the panes. */ .column { display: flex; flex-direction: column; gap: 2px; - width: 256px; - flex: none; + flex: 1 1 0; + min-width: 256px; overflow-y: auto; } .columnWide { width: 100%; - flex: 1 1 0; } .divider { @@ -228,8 +233,8 @@ color: var(--dsw-alias-state-error-primary); } -/* Footer: l3 separator on top, pt12 px24, New-folder pinned left; the fixed - * card leaves the figma 28px below the 36px buttons. */ +/* Footer: l3 separator on top, symmetric padding so the row sits vertically + * centered in the bar; New-folder and the show-hidden toggle pin left. */ .footerBar { display: flex; align-items: center; @@ -238,12 +243,17 @@ flex-wrap: wrap; gap: 8px; flex: none; - padding: 12px 24px 28px; + padding: 16px 24px; border-top: 1px solid var(--dsw-alias-border-l3); } -/* Show-hidden toggle: a subtle text button in the footer, left of the gap. */ +/* Show-hidden toggle: a subtle fixed-label text button left of the gap; + * the pressed state seats a check glyph before the label (Menu's selected + * vocabulary) instead of flipping the wording. */ .showHiddenToggle { + display: inline-flex; + align-items: center; + gap: 4px; border: none; background: transparent; padding: 0; diff --git a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx index 20fef7a827..149e04d87e 100644 --- a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx +++ b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx @@ -10,13 +10,14 @@ * selects the created folder. Open adopts the selected folder, falling back * to the listed level. Pure consumer of the injected browse calls — the * owning flow decides what "Open" means and owns the workspace-creation - * error surface. Hidden entries are host-flagged and hidden by default; - * a "Show hidden files" toggle in the footer reveals them (client-side only). + * error surface. Hidden entries are host-flagged and hidden by default; the + * footer's fixed-label "Show hidden files" toggle (aria-pressed, check when + * on) reveals them (client-side only). */ import { useCallback, useEffect, useRef, useState } from 'react' import clsx from 'clsx' import { - Button, IconChevronRightOutline14, IconFolderClose16, IconFolderOpen16, IconPlusOutline16, Modal, + Button, IconCheckOutline16, IconChevronRightOutline14, IconFolderClose16, IconFolderOpen16, IconPlusOutline16, Modal, } from '@deepseek-ai/dsh-client-ui-primitives' import type { DirectoryEntry, DirectoryListing } from '@deepseek-ai/dsh-client-runtime/client' import { DirectoryBrowseError } from '@deepseek-ai/dsh-client-runtime/client' @@ -451,10 +452,12 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen, diff --git a/packages/host/directory-picker-browse/src/client/index.ts b/packages/host/directory-picker-browse/src/client/index.ts index e47613cdaf..a458ca94c7 100644 --- a/packages/host/directory-picker-browse/src/client/index.ts +++ b/packages/host/directory-picker-browse/src/client/index.ts @@ -47,7 +47,6 @@ export function apply(ctx: ClientContext): void { 'browser.loading': '加载中…', 'browser.truncated': '文件夹过多,仅显示开头部分。', 'browser.showHidden': '显示隐藏文件', - 'browser.hideHidden': '隐藏隐藏文件', }], ['en', { 'browser.title': 'Select Workspace Directory', @@ -63,7 +62,6 @@ export function apply(ctx: ClientContext): void { 'browser.loading': 'Loading…', 'browser.truncated': 'Too many folders to list; only the beginning is shown.', 'browser.showHidden': 'Show hidden files', - 'browser.hideHidden': 'Hide hidden files', }], ] try { diff --git a/packages/host/directory-picker-browse/tests/client-flow.spec.tsx b/packages/host/directory-picker-browse/tests/client-flow.spec.tsx index c29afc935c..31ec5a4927 100644 --- a/packages/host/directory-picker-browse/tests/client-flow.spec.tsx +++ b/packages/host/directory-picker-browse/tests/client-flow.spec.tsx @@ -163,7 +163,6 @@ describe('directory-picker-browse client half', () => { expect(injected.t('browser.title')).toBe('选择工作区目录') expect(injected.t('browser.newFolder')).toBe('新建文件夹') expect(injected.t('browser.showHidden')).toBe('显示隐藏文件') - expect(injected.t('browser.hideHidden')).toBe('隐藏隐藏文件') }) it('drives the injected browse calls through the hole entry', async () => { diff --git a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx index 624a2c8705..bc2ad81f76 100644 --- a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx +++ b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx @@ -107,11 +107,14 @@ describe('DirectoryBrowser', () => { const b = mount() await waitFor(() => { expect(screen.getByRole('listitem')).toBeTruthy() }) expect(screen.queryByText('.config')).toBeNull() - // Toggle hidden files on. - fireEvent.click(screen.getByRole('button', { name: 'browser.showHidden' })) + // The fixed-label toggle reports its state through aria-pressed. + const toggle = screen.getByRole('button', { name: 'browser.showHidden' }) + expect(toggle.getAttribute('aria-pressed')).toBe('false') + fireEvent.click(toggle) + expect(toggle.getAttribute('aria-pressed')).toBe('true') expect(screen.getByText('.config')).toBeTruthy() - // Toggle hidden files off. - fireEvent.click(screen.getByRole('button', { name: 'browser.hideHidden' })) + fireEvent.click(toggle) + expect(toggle.getAttribute('aria-pressed')).toBe('false') expect(screen.queryByText('.config')).toBeNull() // Close resets the toggle. b.view.rerender()