From f3dca163b3826337d71973164b43aca55785f326 Mon Sep 17 00:00:00 2001 From: GCWing Date: Tue, 8 Sep 2026 18:15:57 +0800 Subject: [PATCH 1/3] feat(web-ui): add workspace session tabs and rolling text --- .../design-lab/src/i18n/componentMetadata.ts | 1 + .../apps/design-lab/src/i18n/messages.ts | 12 + .../src/pages/ComponentDetailPage.tsx | 9 + .../design-lab/src/pages/ComponentsPage.tsx | 3 + .../src/preview/RollingTextPreview.tsx | 34 ++ design-system/apps/design-lab/src/styles.css | 5 + .../vite/component-detail-contract.test.mjs | 4 + .../vite/component-preview-render.test.mjs | 8 + .../packages/design-tokens/README.md | 5 + .../packages/design-tokens/scripts/build.mjs | 2 + .../design-tokens/src/system.tokens.json | 4 + .../design-tokens/tests/contract.test.mjs | 1 + design-system/packages/ui/README.md | 41 ++ .../RollingText/RollingText.meta.ts | 20 + .../RollingText/RollingText.module.css | 30 ++ .../components/RollingText/RollingText.tsx | 102 +++++ .../ui/src/components/RollingText/index.ts | 1 + .../RollingText/useRollingTextMotion.ts | 99 +++++ .../src/components/TabGroup/TabGroup.meta.ts | 1 + .../ui/src/components/TabGroup/TabGroup.tsx | 20 +- design-system/packages/ui/src/index.ts | 1 + .../ui/src/internal/useReducedMotion.ts | 20 + design-system/packages/ui/src/registry.ts | 2 + .../packages/ui/tests/registry.test.mjs | 1 + .../packages/ui/tests/rolling-text.test.mjs | 36 ++ docs/architecture/session-projection.md | 45 ++- .../ui/generated/design-system.css | 3 + .../sections/sessions/SessionsSection.tsx | 40 +- .../sections/sessions/sessionNavSelection.ts | 6 +- .../NavPanel/workspaceSessionView.test.ts | 21 +- .../NavPanel/workspaceSessionView.ts | 12 +- .../app/components/SceneBar/SceneBar.test.tsx | 41 +- .../src/app/components/SceneBar/SceneBar.tsx | 18 +- .../src/app/components/SceneBar/types.ts | 24 +- src/web-ui/src/app/creation/creationUiApi.ts | 10 +- .../globalSearchArchitecture.test.ts | 4 +- .../global-search/productActionActivator.ts | 3 +- src/web-ui/src/app/hooks/index.ts | 2 +- .../src/app/hooks/useCurrentSessionTitle.ts | 25 -- src/web-ui/src/app/hooks/useSceneManager.ts | 11 +- .../app/hooks/useSessionTabLabels.test.tsx | 48 +++ .../src/app/hooks/useSessionTabLabels.ts | 36 ++ src/web-ui/src/app/layout/WorkspaceBody.tsx | 4 +- .../src/app/scenes/SceneViewport.test.tsx | 15 + src/web-ui/src/app/scenes/SceneViewport.tsx | 15 +- .../app/scenes/file-viewer/FileViewerNav.scss | 78 +++- src/web-ui/src/app/scenes/registry.ts | 3 +- .../app/scenes/session/AuxPane.appearance.ts | 5 +- .../src/app/scenes/session/AuxPane.scss | 17 + src/web-ui/src/app/scenes/session/AuxPane.tsx | 13 +- .../src/app/scenes/welcome/WelcomeScene.scss | 117 ++++-- .../src/app/scenes/welcome/WelcomeScene.tsx | 91 +++-- .../src/app/scenes/welcome/appearance.ts | 1 + .../WorkspaceResourcePanel.tsx | 19 +- .../workspaceResources.test.ts | 10 +- .../services/sessionSceneLifecycle.test.ts | 116 +++++- .../src/app/services/sessionSceneLifecycle.ts | 97 ++++- .../app/services/sessionSceneTarget.test.ts | 49 +++ .../src/app/services/sessionSceneTarget.ts | 39 ++ src/web-ui/src/app/stores/sceneStore.test.ts | 101 ++++- src/web-ui/src/app/stores/sceneStore.ts | 359 +++++++++++------- .../components/voice/voiceClientContext.ts | 7 +- .../src/flow_chat/services/FlowChatManager.ts | 7 +- .../flow-chat-manager/SessionModule.test.ts | 13 + .../flow-chat-manager/SessionModule.ts | 8 +- .../services/sessionActivation.test.ts | 54 +++ .../flow_chat/services/sessionActivation.ts | 60 ++- .../services/sessionNavStatusService.test.ts | 84 ++++ .../services/sessionNavStatusService.ts | 63 ++- .../utils/sessionNavOrdering.test.ts | 112 ++++++ .../src/flow_chat/utils/sessionNavOrdering.ts | 62 +++ .../flow_chat/utils/workspaceScope.test.ts | 5 + .../src/flow_chat/utils/workspaceScope.ts | 16 +- .../RollingTextIntegration.test.tsx | 231 +++++++++++ src/web-ui/src/locales/en-US/common.json | 10 +- src/web-ui/src/locales/zh-CN/common.json | 10 +- src/web-ui/src/locales/zh-TW/common.json | 10 +- .../shared/services/openShellSessionTarget.ts | 3 +- .../services/sceneOpenTargetResolver.test.ts | 12 +- .../services/sceneOpenTargetResolver.ts | 5 +- 80 files changed, 2302 insertions(+), 430 deletions(-) create mode 100644 design-system/apps/design-lab/src/preview/RollingTextPreview.tsx create mode 100644 design-system/packages/ui/src/components/RollingText/RollingText.meta.ts create mode 100644 design-system/packages/ui/src/components/RollingText/RollingText.module.css create mode 100644 design-system/packages/ui/src/components/RollingText/RollingText.tsx create mode 100644 design-system/packages/ui/src/components/RollingText/index.ts create mode 100644 design-system/packages/ui/src/components/RollingText/useRollingTextMotion.ts create mode 100644 design-system/packages/ui/src/internal/useReducedMotion.ts create mode 100644 design-system/packages/ui/tests/rolling-text.test.mjs delete mode 100644 src/web-ui/src/app/hooks/useCurrentSessionTitle.ts create mode 100644 src/web-ui/src/app/hooks/useSessionTabLabels.test.tsx create mode 100644 src/web-ui/src/app/hooks/useSessionTabLabels.ts create mode 100644 src/web-ui/src/app/services/sessionSceneTarget.test.ts create mode 100644 src/web-ui/src/app/services/sessionSceneTarget.ts create mode 100644 src/web-ui/src/flow_chat/utils/sessionNavOrdering.test.ts create mode 100644 src/web-ui/src/flow_chat/utils/sessionNavOrdering.ts create mode 100644 src/web-ui/src/infrastructure/design-system/RollingTextIntegration.test.tsx diff --git a/design-system/apps/design-lab/src/i18n/componentMetadata.ts b/design-system/apps/design-lab/src/i18n/componentMetadata.ts index f5145ec5fd..7e771f2c1b 100644 --- a/design-system/apps/design-lab/src/i18n/componentMetadata.ts +++ b/design-system/apps/design-lab/src/i18n/componentMetadata.ts @@ -75,6 +75,7 @@ const descriptionKeys: Readonly> = { ProminentToolCard: "component.ProminentToolCard.description", ReadFileToolCard: "component.ReadFileToolCard.description", ReviewSummaryToolCard: "component.ReviewSummaryToolCard.description", + RollingText: "component.RollingText.description", RunCodeToolCard: "component.RunCodeToolCard.description", SearchField: "component.SearchField.description", SegmentedControl: "component.SegmentedControl.description", diff --git a/design-system/apps/design-lab/src/i18n/messages.ts b/design-system/apps/design-lab/src/i18n/messages.ts index 53aae20b83..e8a7318e65 100644 --- a/design-system/apps/design-lab/src/i18n/messages.ts +++ b/design-system/apps/design-lab/src/i18n/messages.ts @@ -129,6 +129,10 @@ export const enUSMessages = { "component.Spinner.description": "A compact activity indicator with matrix and bar presentations.", "component.Switch.description": "Toggles a single setting between on and off with native checkbox semantics.", "component.TabGroup.description": "A compact single-selection tab list with optional leading icons, end actions, and automatic keyboard activation.", + "component.RollingText.description": "Vertical text replacement with coordinated width, interruption handling, and reduced-motion support. Try repeated clicks in the replacement specimen.", + "components.preview.replaceText": "Replace title", + "components.preview.rollingTextLong": "Review workspace navigation and session switching", + "detail.option.replacing": "Replacement", "component.TerminalControlToolCard.description": "An ambient trace for interrupting or terminating a terminal session.", "component.TodoToolCard.description": "An ambient task-progress card with compact and expandable list presentations.", "component.Toolbar.description": "A responsive bar with independent leading, centered, trailing, and horizontally scrollable composition regions.", @@ -865,6 +869,10 @@ export const zhCNMessages = { "component.Spinner.description": "提供点阵与条形样式的紧凑活动指示器。", "component.Switch.description": "通过原生复选框语义在开启与关闭之间切换单项设置。", "component.TabGroup.description": "紧凑的单选标签组,支持前置图标、尾部操作与方向键自动切换。", + "component.RollingText.description": "垂直滚动替换文本,宽度同步过渡,支持连续切换与减少动态效果。可在替换示例中连续点击体验。", + "components.preview.replaceText": "替换标题", + "components.preview.rollingTextLong": "检查工作区导航与会话切换时的交互行为", + "detail.option.replacing": "替换", "component.TerminalControlToolCard.description": "低关注终端控制轨迹,用于呈现中断或终止终端会话的结果。", "component.TodoToolCard.description": "低关注任务进度卡片,支持紧凑摘要与可展开列表两种呈现。", "component.Toolbar.description": "具备独立前置、居中、尾部与横向滚动区域的响应式工具栏。", @@ -1635,6 +1643,10 @@ export const zhTWMessages = { "component.Spinner.description": "提供點陣與條形樣式的緊湊活動指示器。", "component.Switch.description": "以原生核取方塊語意在開啟與關閉之間切換單一設定。", "component.TabGroup.description": "緊湊的單選標籤群組,支援前置圖示、尾端操作與方向鍵自動切換。", + "component.RollingText.description": "垂直捲動替換文字,寬度同步過渡,支援連續切換與減少動態效果。可在替換範例中連續點擊體驗。", + "components.preview.replaceText": "替換標題", + "components.preview.rollingTextLong": "檢查工作區導覽與工作階段切換時的互動行為", + "detail.option.replacing": "替換", "component.TerminalControlToolCard.description": "低關注終端控制軌跡,用於呈現中斷或終止終端工作階段的結果。", "component.TodoToolCard.description": "低關注任務進度卡片,支援緊湊摘要與可展開列表兩種呈現。", "component.Toolbar.description": "具備獨立前置、置中、尾端與橫向捲動區域的響應式工具列。", diff --git a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx index a691451490..d26957c5d5 100644 --- a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx +++ b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx @@ -121,6 +121,7 @@ import { import type { ComponentMeta } from "@openbitfun/ui/registry"; import previewImage from "../assets/design-system-hero.webp"; import { IconCompositionPreview } from "../preview/IconCompositionPreview"; +import { RollingTextPreview } from "../preview/RollingTextPreview"; import { NestedMenuPattern } from "./ReferencePatterns"; import { useI18n, type MessageKey } from "../i18n"; import { @@ -174,6 +175,7 @@ const optionLabelKeys: Readonly> = { chevron: "detail.option.chevron", center: "detail.option.center", default: "detail.option.default", + replacing: "detail.option.replacing", disabled: "detail.option.disabled", display: "detail.option.display", error: "detail.option.error", @@ -437,6 +439,9 @@ export function ComponentDetailPage({ : states; const codeSample = useMemo(() => { + if (component.name === "RollingText") { + return 'import { RollingText, TabGroup } from "@openbitfun/ui";\n\n// Keep the identity stable for title edits; change it when replacing the resource.\n{record.title}\n\n// TabGroup owns the text slot and composes RollingText without nested clipping.\n\n'; + } if (component.name === "MobileActionSheet") return `import { MobileActionSheet } from "@openbitfun/ui/mobile";\n\n setOpen(false)}\n open={open}\n title="${t("components.preview.session")}"\n/>`; if (component.name === "MobileComposer") return `import { MobileComposer } from "@openbitfun/ui/mobile";\n\n}\n startActions={}\n endActions={}\n>\n