+
{VARIANTS.map((variant) => (
-
-
+
+
+
+
))}
@@ -41,65 +44,38 @@ export const VariantMatrix: Story = {
export const SizeMatrix: Story = {
render: () => (
-
- {SIZES.map((size) => (
-
-
-
- ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
),
};
export const WithIcon: Story = {
render: () => (
-
-
-
-
-
+
),
};
export const Loading: Story = {
- render: () => (
-
-
-
-
-
- ),
-};
-
-export const NavSize: Story = {
render: () => (
-
-
+
+
),
-};
\ No newline at end of file
+};
diff --git a/packages/ui/stories/design-tokens.stories.tsx b/packages/ui/stories/design-tokens.stories.tsx
index 6dc94a328b..5639d8b1aa 100644
--- a/packages/ui/stories/design-tokens.stories.tsx
+++ b/packages/ui/stories/design-tokens.stories.tsx
@@ -241,7 +241,6 @@ export const PrimaryActions: Story = {
-
@@ -257,9 +256,7 @@ export const PrimaryActions: Story = {
-
+
@@ -277,13 +274,10 @@ export const PrimaryActions: Story = {
-
-
@@ -302,7 +296,7 @@ export const PrimaryActions: Story = {
Syncing
-
+
@@ -343,4 +337,4 @@ export const SemanticColors: Story = {
),
-};
\ No newline at end of file
+};
diff --git a/packages/ui/stories/empty.stories.tsx b/packages/ui/stories/empty.stories.tsx
index bf071b71dc..a3cfc0bb23 100644
--- a/packages/ui/stories/empty.stories.tsx
+++ b/packages/ui/stories/empty.stories.tsx
@@ -89,7 +89,7 @@ export const WithAction: Story = {
开始第一次对话吧。
- 新建会话
+ 新建会话
@@ -110,4 +110,4 @@ export const Loading: Story = {
),
-};
\ No newline at end of file
+};
diff --git a/packages/ui/stories/interaction-states.stories.tsx b/packages/ui/stories/interaction-states.stories.tsx
index 1a889c0135..90909547b4 100644
--- a/packages/ui/stories/interaction-states.stories.tsx
+++ b/packages/ui/stories/interaction-states.stories.tsx
@@ -1,4 +1,7 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
+import type { SessionSummary } from '@maka/core';
+import { userEvent } from 'storybook/test';
+import { SessionListPanel } from '../src/session-list-panel.js';
import { Button } from '../src/ui.js';
const meta = {
@@ -7,76 +10,163 @@ const meta = {
} satisfies Meta;
export default meta;
-
type Story = StoryObj
;
-// Visual contract for the two interaction-state backgrounds:
-// hover -> --state-hover-bg (4% foreground alpha, transient)
-// selected -> --state-selected-bg (6.5% foreground alpha, persistent)
-// pressed -> :active background = --state-selected-bg (no scale transform)
-// The rows below pin each state statically so reviewers can compare wash
-// intensity and confirm hover (4%) < selected (6.5%). Press feedback is a
-// background change, not a scale — pinned by the state-token contracts from #499.
+const VARIANTS = ['default', 'secondary', 'ghost', 'quiet', 'destructive'] as const;
+const NEUTRAL_VARIANTS = ['secondary', 'ghost', 'quiet'] as const;
+const noop = () => undefined;
+
+const COMPOSITE_ROW_SESSIONS: SessionSummary[] = [
+ {
+ id: 'interaction-active',
+ name: '整理中文 compact controls',
+ isFlagged: false,
+ isArchived: false,
+ labels: [],
+ hasUnread: false,
+ status: 'active',
+ backend: 'fake',
+ llmConnectionSlug: 'fixture',
+ model: 'fixture-model',
+ permissionMode: 'ask',
+ },
+ {
+ id: 'interaction-default',
+ name: 'Review English interaction states',
+ isFlagged: false,
+ isArchived: false,
+ labels: [],
+ hasUnread: true,
+ status: 'active',
+ backend: 'fake',
+ llmConnectionSlug: 'fixture',
+ model: 'fixture-model',
+ permissionMode: 'ask',
+ },
+];
-const rowStyle: React.CSSProperties = {
- display: 'flex',
+function StoryFrame(props: { children: React.ReactNode; description: string; title: string }) {
+ return (
+
+
+
{props.title}
+
+ {props.description}
+
+
+ {props.children}
+
+ );
+}
+
+const rowStyle = {
alignItems: 'center',
- gap: 12,
- padding: '8px 12px',
- borderRadius: 6,
- fontSize: 13,
- fontFamily: 'var(--font-sans)',
- border: '1px solid var(--border)',
+ display: 'grid',
+ gap: 8,
+ gridTemplateColumns: '88px repeat(6, max-content)',
+} as const;
+
+export const ButtonStates: Story = {
+ render: () => (
+
+ {VARIANTS.map((variant) => (
+
+ {variant}
+ 中文状态
+ English state
+ 已禁用 / Disabled
+
+ ))}
+
+ ),
};
export const ListRowStates: Story = {
render: () => (
-
-
- default
- transparent
-
-
-
hover
-
--state-hover-bg · 4%
+
+
+
-
- selected / pressed
- --state-selected-bg · 6.5% · no scale
-
-
+
),
+ play: async ({ canvasElement }) => {
+ const hoverTarget = canvasElement.querySelector
('.maka-nav-row:not([data-active="true"])');
+ hoverTarget?.setAttribute('data-state-target', 'hover');
+ const focusTarget = canvasElement.querySelector('.maka-list-row[data-active="true"] .maka-list-row-main');
+ focusTarget?.setAttribute('data-state-target', 'focus');
+ const tabStops = Array.from(canvasElement.querySelectorAll('button:not([disabled])'));
+ const focusIndex = focusTarget ? tabStops.indexOf(focusTarget) : -1;
+ if (focusIndex > 0) {
+ tabStops[focusIndex - 1]?.focus();
+ await userEvent.tab();
+ }
+ },
};
export const NeutralButtonStates: Story = {
render: () => (
-
-
-
ghost default
-
- ghost hover (demo)
-
-
- ghost pressed (demo)
-
+
+
+ secondary
+ 静止
+ 悬停
+ 按下
+ 焦点
+ 禁用
+ ARIA 禁用
-
- Neutral variants (ghost/outline/secondary/quiet) press with state tokens: hover = bg-muted (var(--muted)), pressed = --state-selected-bg. Matches ui.tsx buttonVariants.
-
-
+ {NEUTRAL_VARIANTS.slice(1).map((variant) => (
+
+ {variant}
+ 静止
+ 禁用
+ ARIA 禁用
+
+ ))}
+
),
+ play: async ({ canvasElement }) => {
+ canvasElement.querySelector
('[data-state-target="focus"]')?.focus();
+ },
};
export const SolidButtonStates: Story = {
render: () => (
-
-
- default
- destructive
-
-
- Solid variants (default/destructive) fill with primary/destructive and press to their hover state (hover:bg-primary/90 active:bg-primary/90). They are NOT state-token surfaces — see ui.tsx buttonVariants for the real values.
-
-
+
+ {(['default', 'destructive'] as const).map((variant) => (
+
+ {variant}
+ 静止
+ 悬停
+ 按下
+ 焦点
+ 禁用
+ ARIA 禁用
+
+ ))}
+
),
+ play: async ({ canvasElement }) => {
+ canvasElement.querySelector('[data-state-target="focus"]')?.focus();
+ },
};
diff --git a/packages/ui/stories/menu.stories.tsx b/packages/ui/stories/menu.stories.tsx
index 6bc69f5385..7f0190182e 100644
--- a/packages/ui/stories/menu.stories.tsx
+++ b/packages/ui/stories/menu.stories.tsx
@@ -32,7 +32,7 @@ function OpenMenuCell({ label, children }: { label: string; children: React.Reac
return (
@@ -42,7 +42,7 @@ function OpenMenuCell({ label, children }: { label: string; children: React.Reac
export const Basic: Story = {
render: () => (
),
-};
\ No newline at end of file
+};
diff --git a/packages/ui/stories/toast.stories.tsx b/packages/ui/stories/toast.stories.tsx
index 01aab28a40..153691b071 100644
--- a/packages/ui/stories/toast.stories.tsx
+++ b/packages/ui/stories/toast.stories.tsx
@@ -31,14 +31,14 @@ export const Interactive: Story = {
{VARIANTS.map((variant) => (
toast.toast({ title: `${variant} 标题`, description: `${variant} 说明文字`, variant })}
>
{variant}
))}
toast.toast({
title: '已删除会话',
@@ -101,7 +101,7 @@ export const ConfirmPlain: Story = {
结果:{result}
{
const ok = await toast.confirm({ title: '保存修改?', confirmLabel: '保存' });
setResult(ok ? '已确认' : '已取消');
@@ -112,4 +112,4 @@ export const ConfirmPlain: Story = {
);
},
-};
\ No newline at end of file
+};