From e950bc08632c4f1c0f86ab88c204c5e1ad003b67 Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:16:54 +0800 Subject: [PATCH 1/2] fix(app): layer the global reset so Tailwind spacing applies An unlayered *, *::before, *::after { margin: 0; padding: 0 } reset wins over every @layer utilities rule, so m-*, mx-auto and space-* silently did nothing. Move the reset into @layer base and note the invariant in the design-system SSOT. Co-authored-by: Codex --- app/shared/src/styles/tokens-base.css | 16 ++++++++++------ docs/architecture/07-design-system-ssot.md | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/shared/src/styles/tokens-base.css b/app/shared/src/styles/tokens-base.css index a98ccb72b..37defe8d7 100644 --- a/app/shared/src/styles/tokens-base.css +++ b/app/shared/src/styles/tokens-base.css @@ -419,12 +419,16 @@ } /* ── Global Reset ──────────────────────────────────────────────────── */ -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; - padding: 0; +/* Layered so Tailwind utilities (h-, p-, m-, gap-…) can override the reset. + An unlayered reset silently wins over every `@layer utilities` rule. */ +@layer base { + *, + *::before, + *::after { + box-sizing: border-box; + margin: 0; + padding: 0; + } } html, diff --git a/docs/architecture/07-design-system-ssot.md b/docs/architecture/07-design-system-ssot.md index 0f2ae00ed..fce02a88b 100644 --- a/docs/architecture/07-design-system-ssot.md +++ b/docs/architecture/07-design-system-ssot.md @@ -39,7 +39,7 @@ Entry CSS load: desktop/web `main.tsx` imports surface `styles/{tokens,themes,pr 8. **Design token gate**:修改 `app/shared/src/styles/` 或 `app/shared/src/designTokens.ts` 后,运行 `pnpm --dir app --filter @agenthub/shared test`;`designTokens.test.ts`、`styles/tokens-base.test.ts`、`styles/surfaceStyles.test.ts` 必须全绿。 9. **Shared component acceptance**:新组件验收见 [component-acceptance.md](../component-acceptance.md)。 10. **New chat surfaces** use the vendored shadcn primitives (`Message`, `Bubble`, `InputGroup`, `Attachment`) plus Tailwind utilities whose colors/radii resolve from `shadcn.css`. Do not add a second palette or a component-local radius scale. -11. **Preflight stays off during migration.** `shadcn.css` imports Tailwind theme + utilities only; enabling Preflight would reset legacy CSS-Module surfaces. Remove this exception only in the change that retires the last migrated module. +11. **Preflight stays off during migration.** `shadcn.css` imports Tailwind theme + utilities only; enabling Preflight would reset legacy CSS-Module surfaces. Remove this exception only in the change that retires the last migrated module. The hand-rolled reset in `tokens-base.css` therefore lives inside `@layer base`: an unlayered `*, *::before, *::after { margin: 0; padding: 0 }` wins over every `@layer utilities` rule and silently kills `m-*`, `mx-auto` and `space-*`. 12. **Flat chrome, glass overlays.** Workbench chrome (rail, sidebar, header, inspector, composer) uses flat `--background` / `--surface-dim` surfaces and 9–18% foreground hairlines. Frosted glass remains for transient overlays and previews, not for the persistent shell. ## 3. Legitimate surface glue (not forks) From 570951eb5af1d3cc4eab4b0f50e7eab00fbd2439 Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Tue, 20 Oct 2026 01:17:02 +0800 Subject: [PATCH 2/2] refactor(workbench): collapse the composer into one toolbar row The composer stacked a mainchain pill strip, a bordered toolbar and a bulleted status list under the input: three rows restating the same agent/target/readiness facts, plus an Enter keycap that read as a button. - Drop the mainchain strip; the agent and target pickers already show the same labels and the disabled send button carries the blocker. - Delete the strip component, its four label helpers, the chrome flag and the derived-state fields that only fed it. - Pickers become flat ghost controls sized to their content; the native select overlays them invisibly so the chevron sits next to the label instead of at the end of a fixed-width box. - Status telemetry collapses to one truncating line at 60% muted ink. Co-authored-by: Codex --- .../src/ComposerContextParts.test.tsx | 19 +----- app/workbench/src/ComposerContextParts.tsx | 63 ++----------------- app/workbench/src/ComposerControlParts.tsx | 53 ++++++++-------- .../src/ComposerPartsHelpers.test.ts | 13 +--- app/workbench/src/ComposerPartsHelpers.ts | 32 ---------- app/workbench/src/UnifiedComposer.tsx | 25 ++------ app/workbench/src/UnifiedComposerParts.tsx | 1 - app/workbench/src/__tests__/composer.test.tsx | 12 ++-- .../src/unifiedComposerHelpers.test.ts | 8 +-- app/workbench/src/unifiedComposerHelpers.ts | 25 -------- .../src/unifiedComposerHostHelpers.test.ts | 4 -- .../src/unifiedComposerHostHelpers.ts | 1 - .../src/unifiedComposerHostViewModel.test.ts | 3 - .../src/unifiedComposerHostViewModel.ts | 19 ------ 14 files changed, 45 insertions(+), 233 deletions(-) diff --git a/app/workbench/src/ComposerContextParts.test.tsx b/app/workbench/src/ComposerContextParts.test.tsx index c61dc984d..e8784cab7 100644 --- a/app/workbench/src/ComposerContextParts.test.tsx +++ b/app/workbench/src/ComposerContextParts.test.tsx @@ -2,7 +2,6 @@ import '@testing-library/jest-dom/vitest'; import { fireEvent, render, screen } from '@testing-library/react'; import { beforeAll, describe, expect, it, vi } from 'vitest'; import { - ComposerMainchainStrip, ComposerMentionChips, ComposerQuoteBar, ComposerReplyBar, @@ -59,20 +58,8 @@ describe('ComposerContextParts', () => { expect(onRemove).toHaveBeenCalledWith('profile-builder'); }); - it('renders mainchain and status strips', () => { - render( - , - ); - expect(screen.getByText('Agent @Builder')).toBeInTheDocument(); - expect(screen.getByText('目标未选')).toBeInTheDocument(); - expect(screen.getByText('需填写内容')).toBeInTheDocument(); - - render(); - expect(screen.getByText('数据:真实数据')).toBeInTheDocument(); + it('renders the status strip as one quiet line', () => { + render(); + expect(screen.getByRole('status')).toHaveTextContent('数据:真实数据 · 目标:就绪'); }); }); diff --git a/app/workbench/src/ComposerContextParts.tsx b/app/workbench/src/ComposerContextParts.tsx index fe7e6e4fa..f9ca11cc2 100644 --- a/app/workbench/src/ComposerContextParts.tsx +++ b/app/workbench/src/ComposerContextParts.tsx @@ -9,12 +9,8 @@ import { Button } from '@shared/shadcn/ui/button'; import { cn } from '@shared/lib/utils'; import { DesignNavIcon } from './designIcons'; import { - formatMainchainAgentLabel, - formatMainchainTargetLabel, - formatMainchainTaskLabel, formatMentionChipLabel, formatReplyToLabel, - mainchainDataState, } from './ComposerPartsHelpers'; import { formatQuotePreview } from './unifiedComposerHelpers'; @@ -153,67 +149,18 @@ export function ComposerMentionChips({ ); } -export function ComposerMainchainStrip({ - selectedAgentLabel, - selectedTargetLabel, - targetSelected, - mainchainTask, -}: { - selectedAgentLabel: string; - selectedTargetLabel: string | undefined; - targetSelected: boolean; - mainchainTask: 'ready' | 'draft required'; -}): React.ReactElement { - const { t } = useTranslation(CHATVIEW_I18N_NAMESPACE); - return ( -
- - {formatMainchainAgentLabel(selectedAgentLabel, t)} - - - {formatMainchainTargetLabel(selectedTargetLabel, t)} - - - {formatMainchainTaskLabel(mainchainTask, t)} - -
- ); -} - export function ComposerStatusStrip({ statusItems, }: { statusItems: string[]; }): React.ReactElement { return ( -
- {statusItems.map((item) => ( - - - ))} -
+ {statusItems.join(' · ')} +

); } diff --git a/app/workbench/src/ComposerControlParts.tsx b/app/workbench/src/ComposerControlParts.tsx index e883acb35..9f99578a5 100644 --- a/app/workbench/src/ComposerControlParts.tsx +++ b/app/workbench/src/ComposerControlParts.tsx @@ -36,31 +36,28 @@ export function ComposerAgentPicker({ onSelect: (agentId: string) => void; }): React.ReactElement { const { t } = useTranslation(CHATVIEW_I18N_NAMESPACE); + const placeholder = agentPickerPlaceholder(availableMentionOptions.length, t); return ( -