@agentaily/design-system 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/DESIGN.md +56 -7
  2. package/README.md +5 -5
  3. package/dist/components/ai/Confirmation.js +58 -22
  4. package/dist/components/ai/Confirmation.js.map +1 -1
  5. package/dist/components/ai/Queue.d.ts +23 -0
  6. package/dist/components/ai/Queue.js +52 -1
  7. package/dist/components/ai/Queue.js.map +1 -1
  8. package/dist/components/ai/ToolCall.js +69 -30
  9. package/dist/components/ai/ToolCall.js.map +1 -1
  10. package/dist/components/auth/AccountControl.d.ts +23 -0
  11. package/dist/components/auth/AccountControl.js +47 -0
  12. package/dist/components/auth/AccountControl.js.map +1 -0
  13. package/dist/components/auth/AuthDialog.d.ts +39 -0
  14. package/dist/components/auth/AuthDialog.js +327 -0
  15. package/dist/components/auth/AuthDialog.js.map +1 -0
  16. package/dist/components/auth/SignInPage.d.ts +69 -0
  17. package/dist/components/auth/SignInPage.js +239 -0
  18. package/dist/components/auth/SignInPage.js.map +1 -0
  19. package/dist/components/chat/CodeBlock.js +3 -2
  20. package/dist/components/chat/CodeBlock.js.map +1 -1
  21. package/dist/components/chat/ConversationThread.d.ts +67 -0
  22. package/dist/components/chat/ConversationThread.js +129 -0
  23. package/dist/components/chat/ConversationThread.js.map +1 -0
  24. package/dist/components/code/Artifact.js +44 -9
  25. package/dist/components/code/Artifact.js.map +1 -1
  26. package/dist/components/code/JSXPreview.js +19 -12
  27. package/dist/components/code/JSXPreview.js.map +1 -1
  28. package/dist/components/code/Snippet.js +2 -2
  29. package/dist/components/code/Snippet.js.map +1 -1
  30. package/dist/components/code/Terminal.js +24 -10
  31. package/dist/components/code/Terminal.js.map +1 -1
  32. package/dist/components/code/WebPreview.js +44 -10
  33. package/dist/components/code/WebPreview.js.map +1 -1
  34. package/dist/components/display/Skeleton.js +17 -4
  35. package/dist/components/display/Skeleton.js.map +1 -1
  36. package/dist/components/display/StatusPill.d.ts +12 -0
  37. package/dist/components/display/StatusPill.js +17 -0
  38. package/dist/components/display/StatusPill.js.map +1 -0
  39. package/dist/components/inputs/SecretField.d.ts +21 -0
  40. package/dist/components/inputs/SecretField.js +70 -0
  41. package/dist/components/inputs/SecretField.js.map +1 -0
  42. package/dist/components/layout/AppShell.d.ts +30 -0
  43. package/dist/components/layout/AppShell.js +117 -0
  44. package/dist/components/layout/AppShell.js.map +1 -0
  45. package/dist/components/layout/DesignerShell.d.ts +39 -0
  46. package/dist/components/layout/DesignerShell.js +146 -0
  47. package/dist/components/layout/DesignerShell.js.map +1 -0
  48. package/dist/components/layout/DocsLayout.d.ts +24 -0
  49. package/dist/components/layout/DocsLayout.js +113 -0
  50. package/dist/components/layout/DocsLayout.js.map +1 -0
  51. package/dist/components/layout/SettingsPage.d.ts +31 -0
  52. package/dist/components/layout/SettingsPage.js +92 -0
  53. package/dist/components/layout/SettingsPage.js.map +1 -0
  54. package/dist/components/review/MarkupLayer.d.ts +20 -0
  55. package/dist/components/review/MarkupLayer.js +237 -0
  56. package/dist/components/review/MarkupLayer.js.map +1 -0
  57. package/dist/components/settings/HelpSteps.d.ts +20 -0
  58. package/dist/components/settings/HelpSteps.js +40 -0
  59. package/dist/components/settings/HelpSteps.js.map +1 -0
  60. package/dist/components/settings/IntegrationSettings.d.ts +15 -0
  61. package/dist/components/settings/IntegrationSettings.js +630 -0
  62. package/dist/components/settings/IntegrationSettings.js.map +1 -0
  63. package/dist/components/settings/TestRow.d.ts +21 -0
  64. package/dist/components/settings/TestRow.js +66 -0
  65. package/dist/components/settings/TestRow.js.map +1 -0
  66. package/dist/components/utilities/BrandMark.d.ts +17 -0
  67. package/dist/components/utilities/BrandMark.js +43 -0
  68. package/dist/components/utilities/BrandMark.js.map +1 -0
  69. package/dist/components/utilities/Icon.d.ts +28 -0
  70. package/dist/components/utilities/Icon.js +196 -0
  71. package/dist/components/utilities/Icon.js.map +1 -0
  72. package/dist/components/utilities/RotatingTagline.d.ts +30 -0
  73. package/dist/components/utilities/RotatingTagline.js +90 -0
  74. package/dist/components/utilities/RotatingTagline.js.map +1 -0
  75. package/dist/index.d.ts +23 -0
  76. package/dist/index.js +34 -0
  77. package/dist/index.js.map +1 -1
  78. package/dist/styles.css +67 -64
  79. package/package.json +2 -2
package/DESIGN.md CHANGED
@@ -36,7 +36,7 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
36
36
 
37
37
  ## VISUAL FOUNDATIONS
38
38
 
39
- **Color.** Monochrome-first. Two scales: **ink** (dark, the default on `:root`) and **paper** (light, scoped under `[data-theme="light"]`). The accent is **inversion, not hue** — the primary button is white-on-black in dark, black-on-white in light. Semantic green/amber/red exist (`--ok/--warn/--danger`) but appear only as status; never decorative. There are no gradients anywhere except the dot-grid mask fade.
39
+ **Color.** Monochrome-first. Two scales: **paper** (light, the default on `:root`) and **ink** (dark, scoped under `[data-theme="dark"]`). The accent is **inversion, not hue** — the primary button is white-on-black in dark, black-on-white in light. Semantic green/amber/red exist (`--ok/--warn/--danger`) but appear only as status; never decorative. There are no gradients anywhere except the dot-grid mask fade.
40
40
 
41
41
  **Type.** Space Grotesk (display + UI) and JetBrains Mono (code, timestamps, labels). CJK falls back to system sans (PingFang SC / Microsoft YaHei). Display sizes use tight tracking (−0.02em) and medium weight — never bold-black. The mono ALL-CAPS label (12px, +0.08em) is the system's signature. Body is 15px/1.6, max ~76ch.
42
42
 
@@ -88,26 +88,75 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
88
88
  | `guidelines/` | 17 specimen cards (Colors ×4, Type ×4, Spacing ×5, Brand ×4) |
89
89
  | `states/` | 5 interaction-state matrices (buttons, form controls, selection/nav, loading/streaming, status/feedback) — force-rendered hover/focus/active/disabled |
90
90
  | `components/buttons/` | Button, IconButton, **ButtonGroup** |
91
- | `components/inputs/` | Input, Textarea, Select, Switch, Checkbox, Label, RadioGroup, Slider, Toggle, ToggleGroup, **Field, FieldGroup, InputGroup, InputOTP, Combobox, Calendar, DatePicker, Form (+FormActions, Form.useForm)** |
92
- | `components/display/` | Card, Badge, Avatar, Tabs, Kbd, Separator, Skeleton, Progress, Accordion, Breadcrumb, Table, Pagination, Empty, **Collapsible, Item, Carousel, Chart (BarChart/LineChart), DataTable, Typography (Prose/Text)** |
91
+ | `components/inputs/` | Input, Textarea, Select, Switch, Checkbox, Label, RadioGroup, Slider, Toggle, ToggleGroup, **Field, FieldGroup, InputGroup, InputOTP, Combobox, Calendar, DatePicker, Form (+FormActions, Form.useForm), SecretField** |
92
+ | `components/display/` | Card, Badge, Avatar, Tabs, Kbd, Separator, Skeleton, Progress, Accordion, Breadcrumb, Table, Pagination, Empty, **Collapsible, Item, Carousel, Chart (BarChart/LineChart), DataTable, Typography (Prose/Text), StatusPill** |
93
93
  | `components/feedback/` | Spinner, Toast, Tooltip, Dialog, Alert |
94
94
  | `components/overlay/` | Popover, DropdownMenu, Command, Sheet, **HoverCard, ContextMenu, Menubar, NavigationMenu, AlertDialog** |
95
- | `components/layout/` | **AspectRatio, ScrollArea, Resizable, Sidebar** |
96
- | `components/chat/` | Message, Composer, CodeBlock |
95
+ | `components/layout/` | **AspectRatio, ScrollArea, Resizable, Sidebar, AppShell, DesignerShell, DocsLayout, SettingsPage** — incl. full-page shells/frames |
96
+ | `components/chat/` | Message, Composer, CodeBlock, **ConversationThread** |
97
97
  | `components/ai/` | Reasoning, ToolCall, Sources + Citation, Suggestion/Suggestions, ModelSelector, Attachments, Shimmer, **Conversation, Task, Plan, Context, Confirmation, Checkpoint, Queue** |
98
98
  | `components/code/` | **Terminal, FileTree, Snippet, StackTrace, TestResults, Artifact, WebPreview, Agent, Commit, EnvironmentVariables, PackageInfo, Sandbox, SchemaDisplay, JSXPreview** |
99
99
  | `components/voice/` | **AudioPlayer, MicSelector, VoiceSelector, SpeechInput, Transcription, Persona** |
100
100
  | `components/workflow/` | **Flow (Canvas), Canvas, Node, Edge, Connection, Controls, Panel, Toolbar** |
101
- | `components/utilities/` | **Image, OpenInChat** |
101
+ | `components/utilities/` | **Image, OpenInChat, Icon (unified Lucide set), BrandMark, RotatingTagline** |
102
+ | `components/settings/` | **TestRow, HelpSteps, IntegrationSettings** — connection-card primitives + the DeepSeek/Feishu integration modal |
103
+ | `components/auth/` | **AuthDialog (+ AuthDialog.useAuth), AccountControl, SignInPage** — sign-in/register modal + persisted session + top-bar account menu + full-page sign-in |
104
+ | `components/review/` | **MarkupLayer** — point-at-an-element review overlay (`data-mk-label`) |
105
+ | **Hooks** (headless) | `Queue.useQueue`, `AuthDialog.useAuth`, `Form.useForm` / `Form.useFieldArray` — logic without UI, exposed as statics on the paired component |
102
106
  | `ui_kits/chat/` | Chat app (interactive) |
103
107
  | `ui_kits/website/` | Marketing landing page |
104
108
  | `ui_kits/docs/` | Documentation site (interactive) |
105
109
  | `SKILL.md` | Agent-skill entry point |
106
110
 
107
- Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **118 component exports across 11 categories** (buttons, inputs, display, feedback, overlay, layout, chat, ai, code, voice, workflow, utilities). This is a deliberately exhaustive set covering shadcn/ui primitives + AI-native surfaces (reasoning, tools, agents, voice, workflow graphs). Consume via the compiled bundle: `window.AxiomDesignSystem_7fc962`. Read each component's `.prompt.md` for copy-paste usage.
111
+ Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **146 component exports** across the primitive categories (buttons, inputs, display, feedback, overlay, layout, chat, ai, code, voice, workflow, utilities) plus product-domain layers (**settings, auth, review**). Full-page frames — `AppShell` / `DesignerShell` / `DocsLayout` / `SettingsPage` (layout), `ConversationThread` (chat), `SignInPage` (auth) — are **live components, not copy-templates**: change one, every consuming project benefits on re-sync. Consume via the compiled bundle: `window.AxiomDesignSystem_7fc962`. Read each component's `.prompt.md` for copy-paste usage.
108
112
 
109
113
  **Forms are layered, not monolithic.** Presentational controls (Input/Select/Field…) own layout and never depend on a form engine. `Form` + `FormActions` add pure structure. `Form.useForm` is an **optional**, zero-dependency orchestration hook (values/errors/touched/validate/submit) exposed off the capitalized `Form` export — drop it for react-hook-form or TanStack and the controls still work. Errors surface only after blur or submit; spread `form.field(name)` onto any value control, or `form.field(name, {type:"checkbox"})` for boolean ones.
110
114
 
115
+ ## ARCHITECTURE — 分层与复用 (layering & reuse)
116
+
117
+ The system sits on **two orthogonal axes**. Don't conflate them: a *domain* is not a *layer*.
118
+
119
+ ### ① 纵向 — abstraction layers (dependencies point **down only**)
120
+
121
+ | Layer | What | Depends on | Examples |
122
+ |---|---|---|---|
123
+ | **L0 Tokens** | colour / type / space / radius / shadow / motion variables | — | `tokens/*.css` |
124
+ | **L1 Primitives** (atoms) | no business meaning, usable anywhere | tokens | `Button`, `Input`, `Badge`, `Icon`, `BrandMark`, `RotatingTagline` |
125
+ | **L2 Composites** (molecules) | a few primitives combined; still cross-product | L1 | `Composer`, `SecretField`, `StatusPill` |
126
+ | **L3 Patterns** (organisms) | self-contained interaction + state, configurable | L1–L2 | `AuthDialog`, `IntegrationSettings`, `MarkupLayer`, `ConversationThread` |
127
+ | **L4 Page shells / frames** | full-page layout with slots | L1–L3 | `AppShell`, `DesignerShell`, `DocsLayout`, `SettingsPage`, `SignInPage` |
128
+ | **L5 Product code** | real content + business logic — **never lives in the DS** | L4 | each app's flow / preview renderer |
129
+
130
+ ### ② 横向 — domains (categorization *inside* a layer, not a level)
131
+
132
+ `buttons · inputs · display · feedback · overlay · layout · chat · ai · code · voice · workflow · utilities · settings · auth · review`. A product-domain component (e.g. `IntegrationSettings`) lives at its **abstraction layer** (L3) and is merely *filed* under its **domain folder** (`settings/`). Domains are folders, not tiers.
133
+
134
+ **Where does a component go?** Ask: (1) what does it depend on? — composites + own state → higher. (2) reused by ≥2 surfaces? — if not, keep it in the app, not the DS. (3) how much business meaning? — more → higher layer, more generic → lower.
135
+
136
+ ### 复用类型与传播 (reuse types & propagation)
137
+
138
+ | Type | Mechanism | Does an upstream change propagate? |
139
+ |---|---|---|
140
+ | Tokens ①, global/motif classes ②, components ③ | **reference** — linked `styles.css` + `_ds_bundle.js` loaded at runtime | ✅ **yes** — re-sync the binding and every consumer updates (single source of truth) |
141
+ | Templates (`templates/<slug>/`) | **copy / fork** — the folder is copied into the consuming project | ❌ **no** — frozen at copy time; *however* the copy still live-links tokens + components, so its *colours/buttons* update while its *layout* does not |
142
+
143
+ ### 原则:有共享就「上移」成组件,而不是复制 (share by moving up)
144
+
145
+ For internal unification, push anything shared **up into a component** (live, single-source) — not into a template. Templates are one-off scaffolds meant to be forked and to diverge. This is exactly why the designer shell and the former `starters/` pages now live as **components** (`AppShell`, `DesignerShell`, `DocsLayout`, `SettingsPage`, `ConversationThread`, `SignInPage`): fix the shell once and every project benefits on re-sync. The single rule that keeps layering from rotting: **dependencies are strictly one-way down — a lower layer never reaches up.**
146
+
147
+ ### Headless hooks (逻辑层 — logic without UI)
148
+
149
+ Some logic deserves reuse without dragging a specific UI along. It ships as a **headless hook**, exposed as a static on the capitalized component it pairs with (mirroring `Form.useForm`). **The hook owns the state; the component only renders.** The caller holds the hook and injects it (e.g. `<ConversationThread controller={q} />`), so one piece of state can drive UI in several places at once — a composer, a publish button, and a markup layer can all share a single `Queue.useQueue`.
150
+
151
+ | Hook | Returns | Pairs with |
152
+ |---|---|---|
153
+ | `Queue.useQueue({ onFirst, onBatch })` | `{ queue, busy, enqueue, remove, reset }` — keep-sending-while-busy buffer | `<Queue>`, `ConversationThread controller` |
154
+ | `AuthDialog.useAuth(storageKey?)` | `{ user, signIn, signOut }` — localStorage-persisted session | `<AuthDialog>`, `<AccountControl>` |
155
+ | `Form.useForm(config)` | controlled-form orchestration (values/errors/touched/validate/submit) | the input controls |
156
+ | `Form.useFieldArray({ form, name })` | dynamic list fields (append/remove/move…) | `Form` |
157
+
158
+ This is the headless pattern (React Aria / Downshift / TanStack): split logic from rendering, compose downstream. Keep the *reusable* glue upstream too — if every consumer wires a hook + component the same way, add a convenience wrapper; if the wiring genuinely differs (e.g. each app's `renderTurn` turn-mapping), leave it downstream. Headless hooks are their own layer (browsable under **Hooks** in the Design System tab), not buried inside components.
159
+
111
160
  ## CAVEATS
112
161
  - **Fonts are CDN substitutions:** Space Grotesk + JetBrains Mono via Google Fonts `@import` (no binaries shipped). Replace `tokens/fonts.css` with real `@font-face` + files for offline/production use.
113
162
  - **Icons are Lucide** (CDN / copied paths), not a bespoke set.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # agentaily design system
2
2
 
3
- Agentaily(AI chatbot)设计系统:116 个 React 组件 + Storybook,单色暗色优先。品牌一句话:**极客风格,简约,大气,科技感**。
3
+ Agentaily(AI chatbot)设计系统:113 个 React 组件 + Storybook,单色亮色优先(暗色可切)。品牌一句话:**极客风格,简约,大气,科技感**。
4
4
 
5
5
  📖 **在线 Storybook:** https://agentaily.github.io/design-system/
6
6
 
@@ -28,7 +28,7 @@ npm run build:lib # 产出 dist/:每组件一个 .js + index.d.ts + styles.css
28
28
 
29
29
  | 路径 | 内容 |
30
30
  | ----------------------------------------------- | ----------------------------------------- |
31
- | `dist/index.js` | ESM 入口,re-export 全部 110 个组件符号 |
31
+ | `dist/index.js` | ESM 入口,re-export 全部 113 个组件符号 |
32
32
  | `dist/components/**/*.js` | 每个组件独立模块(含运行时 CSS 注入) |
33
33
  | `dist/index.d.ts` + `dist/components/**/*.d.ts` | TypeScript 类型契约 |
34
34
  | `dist/styles.css` | 内联好的 tokens + 字体,消费方 import 一次 |
@@ -64,12 +64,12 @@ ln -s "$PWD/skill" ~/.claude/skills/agentaily-design
64
64
  ## 结构
65
65
 
66
66
  ```
67
- .storybook/ Storybook 配置(深色默认 + 工具栏 ink/paper 双主题切换)
67
+ .storybook/ Storybook 配置(浅色默认 + 工具栏 paper/ink 双主题切换)
68
68
  src/styles.css 全局入口,@import 所有 token
69
69
  src/tokens/ 颜色 / 字体 / 间距 / 效果 / 基础母题 CSS 变量
70
70
  src/assets/logo/ 品牌 mark SVG(白/黑)
71
71
  src/foundations/ Foundations stories(Intro / Colors / Typography / Spacing / Effects / Motifs)
72
- src/components/ 11 个类别的组件源码 + stories
72
+ src/components/ 15 个类别的组件源码 + stories(含 auth / settings / review 产品域)
73
73
  <cat>/<Name>.jsx 组件(自包含,注入自身 CSS,只消费 token 变量)
74
74
  <cat>/<Name>.d.ts props 契约
75
75
  <cat>/<Name>.prompt.md 用法说明
@@ -78,7 +78,7 @@ src/components/ 11 个类别的组件源码 + stories
78
78
 
79
79
  ## 约定
80
80
 
81
- - 暗色 ink 是默认主题(`:root`);亮色 paper 走 `[data-theme="light"]`,用 Storybook 工具栏切换。
81
+ - 亮色 paper 是默认主题(`:root`);暗色 ink 走 `[data-theme="dark"]`,用 Storybook 工具栏切换。
82
82
  - 单色反相 accent,无渐变;语义色(--ok/--warn/--danger)只作状态。
83
83
  - 圆角 2/4/8px;层级靠 1px hairline 边框;阴影只给浮层。
84
84
  - 文案:平实精确,无 emoji,具体数字即文案("0.4s"、"128k")。
@@ -7,8 +7,8 @@ const AX_CONFIRM_CSS = `
7
7
  .ax-confirm__title { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-body); flex: 1; }
8
8
  .ax-confirm__body { padding: 0 14px 12px 40px; }
9
9
  .ax-confirm__desc { font-size: var(--text-sm); color: var(--text-muted); margin: 0; line-height: var(--leading-body); }
10
- .ax-confirm__detail { margin-top: 10px; padding: 8px 10px; background: var(--bg-0); border: 1px solid var(--border-default); border-radius: var(--radius-2); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); white-space: pre-wrap; word-break: break-word; }
11
- [data-theme="light"] .ax-confirm__detail { background: var(--bg-1); }
10
+ .ax-confirm__detail { margin-top: 10px; padding: 8px 10px; background: var(--bg-1); border: 1px solid var(--border-default); border-radius: var(--radius-2); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); white-space: pre-wrap; word-break: break-word; }
11
+ [data-theme="dark"] .ax-confirm__detail { background: var(--bg-0); }
12
12
  .ax-confirm__actions { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--border-default); background: var(--surface-panel); }
13
13
  .ax-confirm__spacer { flex: 1; }
14
14
  .ax-confirm__btn { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 0 14px; cursor: pointer; border-radius: var(--radius-2); font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-medium); border: 1px solid transparent; transition: background var(--dur-1) var(--ease-out); }
@@ -27,26 +27,62 @@ if (typeof document !== "undefined" && !document.getElementById("ax-confirm-css"
27
27
  s.textContent = AX_CONFIRM_CSS;
28
28
  document.head.appendChild(s);
29
29
  }
30
- const Warn = /* @__PURE__ */ jsxs("svg", { className: "ax-confirm__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
31
- /* @__PURE__ */ jsx("path", { d: "M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" }),
32
- /* @__PURE__ */ jsx("path", { d: "M12 9v4M12 17h.01" })
33
- ] });
34
- function Confirmation({ title = "Confirm action", description, detail, allowLabel = "Allow", denyLabel = "Deny", resolved, onAllow, onDeny, className = "", ...rest }) {
35
- return /* @__PURE__ */ jsxs("div", { className: ["ax-confirm", resolved ? "ax-confirm--resolved" : "", className].filter(Boolean).join(" "), ...rest, children: [
36
- /* @__PURE__ */ jsxs("div", { className: "ax-confirm__head", children: [
37
- Warn,
38
- /* @__PURE__ */ jsx("span", { className: "ax-confirm__title", children: title })
39
- ] }),
40
- description || detail ? /* @__PURE__ */ jsxs("div", { className: "ax-confirm__body", children: [
41
- description ? /* @__PURE__ */ jsx("p", { className: "ax-confirm__desc", children: description }) : null,
42
- detail ? /* @__PURE__ */ jsx("div", { className: "ax-confirm__detail", children: detail }) : null
43
- ] }) : null,
44
- resolved ? /* @__PURE__ */ jsx("div", { className: "ax-confirm__verdict ax-confirm__verdict--" + (resolved === "allowed" ? "allowed" : "denied"), children: resolved === "allowed" ? "✓ Allowed" : "✕ Denied" }) : /* @__PURE__ */ jsxs("div", { className: "ax-confirm__actions", children: [
45
- /* @__PURE__ */ jsx("span", { className: "ax-confirm__spacer" }),
46
- /* @__PURE__ */ jsx("button", { className: "ax-confirm__btn ax-confirm__btn--deny", onClick: onDeny, children: denyLabel }),
47
- /* @__PURE__ */ jsx("button", { className: "ax-confirm__btn ax-confirm__btn--allow", onClick: onAllow, children: allowLabel })
48
- ] })
49
- ] });
30
+ const Warn = /* @__PURE__ */ jsxs(
31
+ "svg",
32
+ {
33
+ className: "ax-confirm__icon",
34
+ viewBox: "0 0 24 24",
35
+ fill: "none",
36
+ stroke: "currentColor",
37
+ strokeWidth: "2",
38
+ strokeLinecap: "round",
39
+ strokeLinejoin: "round",
40
+ children: [
41
+ /* @__PURE__ */ jsx("path", { d: "M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z" }),
42
+ /* @__PURE__ */ jsx("path", { d: "M12 9v4M12 17h.01" })
43
+ ]
44
+ }
45
+ );
46
+ function Confirmation({
47
+ title = "Confirm action",
48
+ description,
49
+ detail,
50
+ allowLabel = "Allow",
51
+ denyLabel = "Deny",
52
+ resolved,
53
+ onAllow,
54
+ onDeny,
55
+ className = "",
56
+ ...rest
57
+ }) {
58
+ return /* @__PURE__ */ jsxs(
59
+ "div",
60
+ {
61
+ className: ["ax-confirm", resolved ? "ax-confirm--resolved" : "", className].filter(Boolean).join(" "),
62
+ ...rest,
63
+ children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "ax-confirm__head", children: [
65
+ Warn,
66
+ /* @__PURE__ */ jsx("span", { className: "ax-confirm__title", children: title })
67
+ ] }),
68
+ description || detail ? /* @__PURE__ */ jsxs("div", { className: "ax-confirm__body", children: [
69
+ description ? /* @__PURE__ */ jsx("p", { className: "ax-confirm__desc", children: description }) : null,
70
+ detail ? /* @__PURE__ */ jsx("div", { className: "ax-confirm__detail", children: detail }) : null
71
+ ] }) : null,
72
+ resolved ? /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ className: "ax-confirm__verdict ax-confirm__verdict--" + (resolved === "allowed" ? "allowed" : "denied"),
76
+ children: resolved === "allowed" ? "✓ Allowed" : "✕ Denied"
77
+ }
78
+ ) : /* @__PURE__ */ jsxs("div", { className: "ax-confirm__actions", children: [
79
+ /* @__PURE__ */ jsx("span", { className: "ax-confirm__spacer" }),
80
+ /* @__PURE__ */ jsx("button", { className: "ax-confirm__btn ax-confirm__btn--deny", onClick: onDeny, children: denyLabel }),
81
+ /* @__PURE__ */ jsx("button", { className: "ax-confirm__btn ax-confirm__btn--allow", onClick: onAllow, children: allowLabel })
82
+ ] })
83
+ ]
84
+ }
85
+ );
50
86
  }
51
87
  export {
52
88
  Confirmation
@@ -1 +1 @@
1
- {"version":3,"file":"Confirmation.js","sources":["../../../src/components/ai/Confirmation.jsx"],"sourcesContent":["import React from \"react\";\n\nconst AX_CONFIRM_CSS = `\n.ax-confirm { border: 1px solid var(--border-strong); border-radius: var(--radius-3); background: var(--surface-card); overflow: hidden; }\n.ax-confirm__head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }\n.ax-confirm__icon { width: 16px; height: 16px; flex: none; color: var(--warn); }\n.ax-confirm__title { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-body); flex: 1; }\n.ax-confirm__body { padding: 0 14px 12px 40px; }\n.ax-confirm__desc { font-size: var(--text-sm); color: var(--text-muted); margin: 0; line-height: var(--leading-body); }\n.ax-confirm__detail { margin-top: 10px; padding: 8px 10px; background: var(--bg-0); border: 1px solid var(--border-default); border-radius: var(--radius-2); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); white-space: pre-wrap; word-break: break-word; }\n[data-theme=\"light\"] .ax-confirm__detail { background: var(--bg-1); }\n.ax-confirm__actions { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--border-default); background: var(--surface-panel); }\n.ax-confirm__spacer { flex: 1; }\n.ax-confirm__btn { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 0 14px; cursor: pointer; border-radius: var(--radius-2); font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-medium); border: 1px solid transparent; transition: background var(--dur-1) var(--ease-out); }\n.ax-confirm__btn--deny { background: transparent; color: var(--text-muted); border-color: var(--border-strong); }\n.ax-confirm__btn--deny:hover { background: var(--surface-raised); color: var(--text-body); }\n.ax-confirm__btn--allow { background: var(--accent); color: var(--accent-fg); }\n.ax-confirm__btn--allow:hover { background: var(--accent-hover); }\n.ax-confirm--resolved { opacity: 0.6; }\n.ax-confirm__verdict { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: var(--tracking-label); text-transform: uppercase; padding: 12px 14px; border-top: 1px solid var(--border-default); }\n.ax-confirm__verdict--allowed { color: var(--ok); }\n.ax-confirm__verdict--denied { color: var(--danger); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-confirm-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-confirm-css\";\n s.textContent = AX_CONFIRM_CSS;\n document.head.appendChild(s);\n}\n\nconst Warn = <svg className=\"ax-confirm__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z\"></path><path d=\"M12 9v4M12 17h.01\"></path></svg>;\n\nexport function Confirmation({ title = \"Confirm action\", description, detail, allowLabel = \"Allow\", denyLabel = \"Deny\", resolved, onAllow, onDeny, className = \"\", ...rest }) {\n return (\n <div className={[\"ax-confirm\", resolved ? \"ax-confirm--resolved\" : \"\", className].filter(Boolean).join(\" \")} {...rest}>\n <div className=\"ax-confirm__head\">\n {Warn}\n <span className=\"ax-confirm__title\">{title}</span>\n </div>\n {(description || detail) ? (\n <div className=\"ax-confirm__body\">\n {description ? <p className=\"ax-confirm__desc\">{description}</p> : null}\n {detail ? <div className=\"ax-confirm__detail\">{detail}</div> : null}\n </div>\n ) : null}\n {resolved ? (\n <div className={\"ax-confirm__verdict ax-confirm__verdict--\" + (resolved === \"allowed\" ? \"allowed\" : \"denied\")}>\n {resolved === \"allowed\" ? \"✓ Allowed\" : \"✕ Denied\"}\n </div>\n ) : (\n <div className=\"ax-confirm__actions\">\n <span className=\"ax-confirm__spacer\"></span>\n <button className=\"ax-confirm__btn ax-confirm__btn--deny\" onClick={onDeny}>{denyLabel}</button>\n <button className=\"ax-confirm__btn ax-confirm__btn--allow\" onClick={onAllow}>{allowLabel}</button>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;AAEA,MAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBvB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,gBAAgB,GAAG;AACjF,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEA,MAAM,OAAO,qBAAC,OAAA,EAAI,WAAU,oBAAmB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,GAAE,gFAAA,CAAgF;AAAA,EAAO,oBAAC,QAAA,EAAK,GAAE,oBAAA,CAAoB;AAAA,GAAO;AAE7R,SAAS,aAAa,EAAE,QAAQ,kBAAkB,aAAa,QAAQ,aAAa,SAAS,YAAY,QAAQ,UAAU,SAAS,QAAQ,YAAY,IAAI,GAAG,QAAQ;AAC5K,8BACG,OAAA,EAAI,WAAW,CAAC,cAAc,WAAW,yBAAyB,IAAI,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,MAC/G,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAI,WAAU,oBACZ,UAAA;AAAA,MAAA;AAAA,MACD,oBAAC,QAAA,EAAK,WAAU,qBAAqB,UAAA,MAAA,CAAM;AAAA,IAAA,GAC7C;AAAA,IACE,eAAe,SACf,qBAAC,OAAA,EAAI,WAAU,oBACZ,UAAA;AAAA,MAAA,cAAc,oBAAC,KAAA,EAAE,WAAU,oBAAoB,uBAAY,IAAO;AAAA,MAClE,SAAS,oBAAC,OAAA,EAAI,WAAU,sBAAsB,kBAAO,IAAS;AAAA,IAAA,EAAA,CACjE,IACE;AAAA,IACH,WACC,oBAAC,OAAA,EAAI,WAAW,+CAA+C,aAAa,YAAY,YAAY,WACjG,UAAA,aAAa,YAAY,cAAc,YAC1C,IAEA,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,qBAAA,CAAqB;AAAA,0BACpC,UAAA,EAAO,WAAU,yCAAwC,SAAS,QAAS,UAAA,WAAU;AAAA,0BACrF,UAAA,EAAO,WAAU,0CAAyC,SAAS,SAAU,UAAA,WAAA,CAAW;AAAA,IAAA,EAAA,CAC3F;AAAA,EAAA,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"Confirmation.js","sources":["../../../src/components/ai/Confirmation.jsx"],"sourcesContent":["import React from \"react\";\n\nconst AX_CONFIRM_CSS = `\n.ax-confirm { border: 1px solid var(--border-strong); border-radius: var(--radius-3); background: var(--surface-card); overflow: hidden; }\n.ax-confirm__head { display: flex; align-items: center; gap: 10px; padding: 12px 14px; }\n.ax-confirm__icon { width: 16px; height: 16px; flex: none; color: var(--warn); }\n.ax-confirm__title { font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-body); flex: 1; }\n.ax-confirm__body { padding: 0 14px 12px 40px; }\n.ax-confirm__desc { font-size: var(--text-sm); color: var(--text-muted); margin: 0; line-height: var(--leading-body); }\n.ax-confirm__detail { margin-top: 10px; padding: 8px 10px; background: var(--bg-1); border: 1px solid var(--border-default); border-radius: var(--radius-2); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-body); white-space: pre-wrap; word-break: break-word; }\n[data-theme=\"dark\"] .ax-confirm__detail { background: var(--bg-0); }\n.ax-confirm__actions { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid var(--border-default); background: var(--surface-panel); }\n.ax-confirm__spacer { flex: 1; }\n.ax-confirm__btn { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 0 14px; cursor: pointer; border-radius: var(--radius-2); font-family: var(--font-body); font-size: var(--text-sm); font-weight: var(--weight-medium); border: 1px solid transparent; transition: background var(--dur-1) var(--ease-out); }\n.ax-confirm__btn--deny { background: transparent; color: var(--text-muted); border-color: var(--border-strong); }\n.ax-confirm__btn--deny:hover { background: var(--surface-raised); color: var(--text-body); }\n.ax-confirm__btn--allow { background: var(--accent); color: var(--accent-fg); }\n.ax-confirm__btn--allow:hover { background: var(--accent-hover); }\n.ax-confirm--resolved { opacity: 0.6; }\n.ax-confirm__verdict { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: var(--tracking-label); text-transform: uppercase; padding: 12px 14px; border-top: 1px solid var(--border-default); }\n.ax-confirm__verdict--allowed { color: var(--ok); }\n.ax-confirm__verdict--denied { color: var(--danger); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-confirm-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-confirm-css\";\n s.textContent = AX_CONFIRM_CSS;\n document.head.appendChild(s);\n}\n\nconst Warn = (\n <svg\n className=\"ax-confirm__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z\"></path>\n <path d=\"M12 9v4M12 17h.01\"></path>\n </svg>\n);\n\nexport function Confirmation({\n title = \"Confirm action\",\n description,\n detail,\n allowLabel = \"Allow\",\n denyLabel = \"Deny\",\n resolved,\n onAllow,\n onDeny,\n className = \"\",\n ...rest\n}) {\n return (\n <div\n className={[\"ax-confirm\", resolved ? \"ax-confirm--resolved\" : \"\", className]\n .filter(Boolean)\n .join(\" \")}\n {...rest}\n >\n <div className=\"ax-confirm__head\">\n {Warn}\n <span className=\"ax-confirm__title\">{title}</span>\n </div>\n {description || detail ? (\n <div className=\"ax-confirm__body\">\n {description ? <p className=\"ax-confirm__desc\">{description}</p> : null}\n {detail ? <div className=\"ax-confirm__detail\">{detail}</div> : null}\n </div>\n ) : null}\n {resolved ? (\n <div\n className={\n \"ax-confirm__verdict ax-confirm__verdict--\" +\n (resolved === \"allowed\" ? \"allowed\" : \"denied\")\n }\n >\n {resolved === \"allowed\" ? \"✓ Allowed\" : \"✕ Denied\"}\n </div>\n ) : (\n <div className=\"ax-confirm__actions\">\n <span className=\"ax-confirm__spacer\"></span>\n <button className=\"ax-confirm__btn ax-confirm__btn--deny\" onClick={onDeny}>\n {denyLabel}\n </button>\n <button className=\"ax-confirm__btn ax-confirm__btn--allow\" onClick={onAllow}>\n {allowLabel}\n </button>\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;AAEA,MAAM,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsBvB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,gBAAgB,GAAG;AACjF,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEA,MAAM,OACJ;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAU;AAAA,IACV,SAAQ;AAAA,IACR,MAAK;AAAA,IACL,QAAO;AAAA,IACP,aAAY;AAAA,IACZ,eAAc;AAAA,IACd,gBAAe;AAAA,IAEf,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,GAAE,gFAAA,CAAgF;AAAA,MACxF,oBAAC,QAAA,EAAK,GAAE,oBAAA,CAAoB;AAAA,IAAA;AAAA,EAAA;AAC9B;AAGK,SAAS,aAAa;AAAA,EAC3B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW,CAAC,cAAc,WAAW,yBAAyB,IAAI,SAAS,EACxE,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MACV,GAAG;AAAA,MAEJ,UAAA;AAAA,QAAA,qBAAC,OAAA,EAAI,WAAU,oBACZ,UAAA;AAAA,UAAA;AAAA,UACD,oBAAC,QAAA,EAAK,WAAU,qBAAqB,UAAA,MAAA,CAAM;AAAA,QAAA,GAC7C;AAAA,QACC,eAAe,SACd,qBAAC,OAAA,EAAI,WAAU,oBACZ,UAAA;AAAA,UAAA,cAAc,oBAAC,KAAA,EAAE,WAAU,oBAAoB,uBAAY,IAAO;AAAA,UAClE,SAAS,oBAAC,OAAA,EAAI,WAAU,sBAAsB,kBAAO,IAAS;AAAA,QAAA,EAAA,CACjE,IACE;AAAA,QACH,WACC;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WACE,+CACC,aAAa,YAAY,YAAY;AAAA,YAGvC,UAAA,aAAa,YAAY,cAAc;AAAA,UAAA;AAAA,QAAA,IAG1C,qBAAC,OAAA,EAAI,WAAU,uBACb,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,qBAAA,CAAqB;AAAA,8BACpC,UAAA,EAAO,WAAU,yCAAwC,SAAS,QAChE,UAAA,WACH;AAAA,8BACC,UAAA,EAAO,WAAU,0CAAyC,SAAS,SACjE,UAAA,WAAA,CACH;AAAA,QAAA,EAAA,CACF;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAIR;"}
@@ -2,3 +2,26 @@
2
2
  export interface QueueItem { text: React.ReactNode; status?: "running" | "pending"; }
3
3
  export interface QueueProps { title?: string; items: Array<string | QueueItem>; onRemove?: (index: number) => void; }
4
4
  export declare function Queue(props: QueueProps): JSX.Element;
5
+
6
+ export interface UseQueueOptions {
7
+ /** Async handler for the very first prompt (bootstrap), run once. Falls back to onBatch([text]). */
8
+ onFirst?: (text: string) => void | Promise<void>;
9
+ /** Async handler for each drained buffer — every prompt that accumulated while busy, flushed together. */
10
+ onBatch?: (texts: string[]) => void | Promise<void>;
11
+ }
12
+ export interface UseQueueReturn {
13
+ /** Pending (not-yet-running) items: { id, text }. Feed to <Queue items={queue}/>. */
14
+ queue: Array<{ id: number; text: string }>;
15
+ /** True while a drain loop is running. */
16
+ busy: boolean;
17
+ /** Send a prompt — runs now if idle, otherwise buffers for the next drain. */
18
+ enqueue: (text: string) => void;
19
+ /** Remove a pending item by index. */
20
+ remove: (index: number) => void;
21
+ /** Clear the buffer and let the next prompt bootstrap again (e.g. new chat). */
22
+ reset: () => void;
23
+ }
24
+ export declare namespace Queue {
25
+ /** Headless "keep sending, buffer while busy" driver. Pairs with <Queue>. */
26
+ function useQueue(options?: UseQueueOptions): UseQueueReturn;
27
+ }
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
- import "react";
2
+ import { useState, useRef, useCallback } from "react";
3
3
  const AX_QUEUE_CSS = `
4
4
  .ax-queue { display: flex; flex-direction: column; gap: 6px; }
5
5
  .ax-queue__head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
@@ -39,6 +39,57 @@ function Queue({ title = "Queued", items = [], onRemove, className = "", ...rest
39
39
  })
40
40
  ] });
41
41
  }
42
+ let _qid = 0;
43
+ function useQueue({ onFirst, onBatch } = {}) {
44
+ const [queue, setQueue] = useState([]);
45
+ const [busy, setBusy] = useState(false);
46
+ const itemsRef = useRef([]);
47
+ const pumpingRef = useRef(false);
48
+ const initedRef = useRef(false);
49
+ const cbRef = useRef(null);
50
+ cbRef.current = { onFirst, onBatch };
51
+ const pumpRef = useRef(null);
52
+ if (!pumpRef.current) {
53
+ pumpRef.current = async () => {
54
+ if (pumpingRef.current) return;
55
+ pumpingRef.current = true;
56
+ setBusy(true);
57
+ const cb = cbRef.current || {};
58
+ if (!initedRef.current && itemsRef.current.length) {
59
+ const job = itemsRef.current.shift();
60
+ setQueue(itemsRef.current.slice());
61
+ initedRef.current = true;
62
+ if (cb.onFirst) await cb.onFirst(job.text);
63
+ else if (cb.onBatch) await cb.onBatch([job.text]);
64
+ }
65
+ while (itemsRef.current.length) {
66
+ const batch = itemsRef.current.splice(0, itemsRef.current.length);
67
+ setQueue([]);
68
+ if (cb.onBatch) await cb.onBatch(batch.map((j) => j.text));
69
+ }
70
+ setBusy(false);
71
+ pumpingRef.current = false;
72
+ };
73
+ }
74
+ const enqueue = useCallback((text) => {
75
+ const t = (text || "").trim();
76
+ if (!t) return;
77
+ itemsRef.current = [...itemsRef.current, { id: ++_qid, text: t }];
78
+ setQueue(itemsRef.current.slice());
79
+ pumpRef.current();
80
+ }, []);
81
+ const remove = useCallback((i) => {
82
+ itemsRef.current = itemsRef.current.filter((_, idx) => idx !== i);
83
+ setQueue(itemsRef.current.slice());
84
+ }, []);
85
+ const reset = useCallback(() => {
86
+ itemsRef.current = [];
87
+ initedRef.current = false;
88
+ setQueue([]);
89
+ }, []);
90
+ return { queue, busy, enqueue, remove, reset };
91
+ }
92
+ Queue.useQueue = useQueue;
42
93
  export {
43
94
  Queue
44
95
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Queue.js","sources":["../../../src/components/ai/Queue.jsx"],"sourcesContent":["import React from \"react\";\n\nconst AX_QUEUE_CSS = `\n.ax-queue { display: flex; flex-direction: column; gap: 6px; }\n.ax-queue__head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }\n.ax-queue__title { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-faint); flex: 1; }\n.ax-queue__count { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); }\n.ax-queue__item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--surface-card); }\n.ax-queue__num { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); width: 16px; flex: none; }\n.ax-queue__text { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--text-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ax-queue__item--running { border-color: var(--border-strong); }\n.ax-queue__item--running .ax-queue__num { color: var(--warn); }\n.ax-queue__status { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase; flex: none; color: var(--text-faint); }\n.ax-queue__item--running .ax-queue__status { color: var(--warn); }\n.ax-queue__remove { appearance: none; background: none; border: none; cursor: pointer; color: var(--text-faint); font-family: var(--font-mono); font-size: 11px; padding: 2px 4px; border-radius: var(--radius-1); flex: none; }\n.ax-queue__remove:hover { color: var(--danger); background: var(--danger-dim); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-queue-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-queue-css\";\n s.textContent = AX_QUEUE_CSS;\n document.head.appendChild(s);\n}\n\nexport function Queue({ title = \"Queued\", items = [], onRemove, className = \"\", ...rest }) {\n return (\n <div className={[\"ax-queue\", className].filter(Boolean).join(\" \")} {...rest}>\n <div className=\"ax-queue__head\">\n <span className=\"ax-queue__title\">{title}</span>\n <span className=\"ax-queue__count\">{items.length}</span>\n </div>\n {items.map((it, i) => {\n const item = typeof it === \"string\" ? { text: it } : it;\n const running = item.status === \"running\";\n return (\n <div key={i} className={\"ax-queue__item\" + (running ? \" ax-queue__item--running\" : \"\")}>\n <span className=\"ax-queue__num\">{running ? \"▶\" : i + 1}</span>\n <span className=\"ax-queue__text\">{item.text}</span>\n {item.status ? <span className=\"ax-queue__status\">{item.status}</span> : null}\n {onRemove && !running ? <button className=\"ax-queue__remove\" aria-label=\"Remove\" onClick={() => onRemove(i)}>✕</button> : null}\n </div>\n );\n })}\n </div>\n );\n}\n"],"names":[],"mappings":";;AAEA,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBrB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,cAAc,GAAG;AAC/E,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,MAAM,EAAE,QAAQ,UAAU,QAAQ,CAAA,GAAI,UAAU,YAAY,IAAI,GAAG,QAAQ;AACzF,SACE,qBAAC,OAAA,EAAI,WAAW,CAAC,YAAY,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,MACrE,UAAA;AAAA,IAAA,qBAAC,OAAA,EAAI,WAAU,kBACb,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,mBAAmB,UAAA,OAAM;AAAA,MACzC,oBAAC,QAAA,EAAK,WAAU,mBAAmB,gBAAM,OAAA,CAAO;AAAA,IAAA,GAClD;AAAA,IACC,MAAM,IAAI,CAAC,IAAI,MAAM;AACpB,YAAM,OAAO,OAAO,OAAO,WAAW,EAAE,MAAM,OAAO;AACrD,YAAM,UAAU,KAAK,WAAW;AAChC,kCACG,OAAA,EAAY,WAAW,oBAAoB,UAAU,6BAA6B,KACjF,UAAA;AAAA,QAAA,oBAAC,UAAK,WAAU,iBAAiB,UAAA,UAAU,MAAM,IAAI,GAAE;AAAA,QACvD,oBAAC,QAAA,EAAK,WAAU,kBAAkB,eAAK,MAAK;AAAA,QAC3C,KAAK,SAAS,oBAAC,QAAA,EAAK,WAAU,oBAAoB,UAAA,KAAK,QAAO,IAAU;AAAA,QACxE,YAAY,CAAC,UAAU,oBAAC,YAAO,WAAU,oBAAmB,cAAW,UAAS,SAAS,MAAM,SAAS,CAAC,GAAG,eAAC,IAAY;AAAA,MAAA,EAAA,GAJlH,CAKV;AAAA,IAEJ,CAAC;AAAA,EAAA,GACH;AAEJ;"}
1
+ {"version":3,"file":"Queue.js","sources":["../../../src/components/ai/Queue.jsx"],"sourcesContent":["import React, { useState, useRef, useCallback } from \"react\";\n\nconst AX_QUEUE_CSS = `\n.ax-queue { display: flex; flex-direction: column; gap: 6px; }\n.ax-queue__head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }\n.ax-queue__title { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-faint); flex: 1; }\n.ax-queue__count { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); }\n.ax-queue__item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--surface-card); }\n.ax-queue__num { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); width: 16px; flex: none; }\n.ax-queue__text { flex: 1; min-width: 0; font-size: var(--text-sm); color: var(--text-body); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ax-queue__item--running { border-color: var(--border-strong); }\n.ax-queue__item--running .ax-queue__num { color: var(--warn); }\n.ax-queue__status { font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase; flex: none; color: var(--text-faint); }\n.ax-queue__item--running .ax-queue__status { color: var(--warn); }\n.ax-queue__remove { appearance: none; background: none; border: none; cursor: pointer; color: var(--text-faint); font-family: var(--font-mono); font-size: 11px; padding: 2px 4px; border-radius: var(--radius-1); flex: none; }\n.ax-queue__remove:hover { color: var(--danger); background: var(--danger-dim); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-queue-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-queue-css\";\n s.textContent = AX_QUEUE_CSS;\n document.head.appendChild(s);\n}\n\nexport function Queue({ title = \"Queued\", items = [], onRemove, className = \"\", ...rest }) {\n return (\n <div className={[\"ax-queue\", className].filter(Boolean).join(\" \")} {...rest}>\n <div className=\"ax-queue__head\">\n <span className=\"ax-queue__title\">{title}</span>\n <span className=\"ax-queue__count\">{items.length}</span>\n </div>\n {items.map((it, i) => {\n const item = typeof it === \"string\" ? { text: it } : it;\n const running = item.status === \"running\";\n return (\n <div key={i} className={\"ax-queue__item\" + (running ? \" ax-queue__item--running\" : \"\")}>\n <span className=\"ax-queue__num\">{running ? \"▶\" : i + 1}</span>\n <span className=\"ax-queue__text\">{item.text}</span>\n {item.status ? <span className=\"ax-queue__status\">{item.status}</span> : null}\n {onRemove && !running ? (\n <button className=\"ax-queue__remove\" aria-label=\"Remove\" onClick={() => onRemove(i)}>\n ✕\n </button>\n ) : null}\n </div>\n );\n })}\n </div>\n );\n}\n\n/* ============================================================\n useQueue — headless \"keep sending, buffer while busy\" driver.\n Exposed as Queue.useQueue. Pairs with the <Queue> display above.\n The first prompt bootstraps via onFirst (once); everything sent\n while busy accumulates and drains together as a BUFFER via onBatch.\n Both callbacks are async and own the app-specific work (push\n messages, run tools) — the hook only owns the queue + pump.\n ============================================================ */\nlet _qid = 0;\nfunction useQueue({ onFirst, onBatch } = {}) {\n const [queue, setQueue] = useState([]); // pending items: { id, text }\n const [busy, setBusy] = useState(false); // a drain loop is running\n const itemsRef = useRef([]); // source of truth for the pump\n const pumpingRef = useRef(false);\n const initedRef = useRef(false); // first prompt has bootstrapped\n const cbRef = useRef(null);\n cbRef.current = { onFirst, onBatch }; // always call the latest callbacks\n const pumpRef = useRef(null);\n\n if (!pumpRef.current) {\n pumpRef.current = async () => {\n if (pumpingRef.current) return;\n pumpingRef.current = true;\n setBusy(true);\n const cb = cbRef.current || {};\n if (!initedRef.current && itemsRef.current.length) {\n const job = itemsRef.current.shift();\n setQueue(itemsRef.current.slice());\n initedRef.current = true;\n if (cb.onFirst) await cb.onFirst(job.text);\n else if (cb.onBatch) await cb.onBatch([job.text]);\n }\n while (itemsRef.current.length) {\n const batch = itemsRef.current.splice(0, itemsRef.current.length);\n setQueue([]);\n if (cb.onBatch) await cb.onBatch(batch.map((j) => j.text));\n }\n setBusy(false);\n pumpingRef.current = false;\n };\n }\n\n const enqueue = useCallback((text) => {\n const t = (text || \"\").trim();\n if (!t) return;\n itemsRef.current = [...itemsRef.current, { id: ++_qid, text: t }];\n setQueue(itemsRef.current.slice());\n pumpRef.current();\n }, []);\n\n const remove = useCallback((i) => {\n itemsRef.current = itemsRef.current.filter((_, idx) => idx !== i);\n setQueue(itemsRef.current.slice());\n }, []);\n\n // clear the buffer and let the next prompt bootstrap again (e.g. \"new chat\")\n const reset = useCallback(() => {\n itemsRef.current = [];\n initedRef.current = false;\n setQueue([]);\n }, []);\n\n return { queue, busy, enqueue, remove, reset };\n}\nQueue.useQueue = useQueue;\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { useState } from "react";
3
3
  const AX_TOOLCALL_CSS = `
4
- .ax-tool { border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--bg-0); overflow: hidden; font-family: var(--font-mono); }
5
- [data-theme="light"] .ax-tool { background: var(--bg-1); }
4
+ .ax-tool { border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--bg-1); overflow: hidden; font-family: var(--font-mono); }
5
+ [data-theme="dark"] .ax-tool { background: var(--bg-0); }
6
6
  .ax-tool__head { display: flex; align-items: center; gap: 10px; width: 100%; appearance: none; background: none; border: none; cursor: pointer; text-align: left; padding: 9px 12px; }
7
7
  .ax-tool__head:focus-visible { outline: none; box-shadow: var(--ring); }
8
8
  .ax-tool__icon { width: 14px; height: 14px; flex: none; color: var(--text-faint); }
@@ -32,37 +32,76 @@ if (typeof document !== "undefined" && !document.getElementById("ax-toolcall-css
32
32
  s.textContent = AX_TOOLCALL_CSS;
33
33
  document.head.appendChild(s);
34
34
  }
35
- const Wrench = /* @__PURE__ */ jsx("svg", { className: "ax-tool__icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.4L3 18v3h3l6.3-6.3a4 4 0 0 0 5.4-5.4l-2.1 2.1-2.4-.6-.6-2.4 2.1-2.1z" }) });
36
- const Chev = /* @__PURE__ */ jsx("svg", { className: "ax-tool__chev", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
35
+ const Wrench = /* @__PURE__ */ jsx(
36
+ "svg",
37
+ {
38
+ className: "ax-tool__icon",
39
+ viewBox: "0 0 24 24",
40
+ fill: "none",
41
+ stroke: "currentColor",
42
+ strokeWidth: "2",
43
+ strokeLinecap: "round",
44
+ strokeLinejoin: "round",
45
+ children: /* @__PURE__ */ jsx("path", { d: "M14.7 6.3a4 4 0 0 0-5.4 5.4L3 18v3h3l6.3-6.3a4 4 0 0 0 5.4-5.4l-2.1 2.1-2.4-.6-.6-2.4 2.1-2.1z" })
46
+ }
47
+ );
48
+ const Chev = /* @__PURE__ */ jsx(
49
+ "svg",
50
+ {
51
+ className: "ax-tool__chev",
52
+ viewBox: "0 0 24 24",
53
+ fill: "none",
54
+ stroke: "currentColor",
55
+ strokeWidth: "2.2",
56
+ strokeLinecap: "round",
57
+ strokeLinejoin: "round",
58
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
59
+ }
60
+ );
37
61
  const STATUS_LABEL = { running: "Running", done: "Done", error: "Error" };
38
- function ToolCall({ name, args, result, status = "done", defaultOpen = false, className = "", ...rest }) {
62
+ function ToolCall({
63
+ name,
64
+ args,
65
+ result,
66
+ status = "done",
67
+ defaultOpen = false,
68
+ className = "",
69
+ ...rest
70
+ }) {
39
71
  const [open, setOpen] = useState(defaultOpen);
40
72
  const fmt = (v) => typeof v === "string" ? v : JSON.stringify(v, null, 2);
41
- return /* @__PURE__ */ jsxs("div", { className: ["ax-tool", "ax-tool--" + status, open ? "ax-tool--open" : "", className].filter(Boolean).join(" "), ...rest, children: [
42
- /* @__PURE__ */ jsxs("button", { className: "ax-tool__head", "aria-expanded": open, onClick: () => setOpen((o) => !o), children: [
43
- Wrench,
44
- /* @__PURE__ */ jsxs("span", { className: "ax-tool__name", children: [
45
- /* @__PURE__ */ jsx("b", { children: name }),
46
- /* @__PURE__ */ jsx("span", { className: "ax-tool__paren", children: "()" })
47
- ] }),
48
- /* @__PURE__ */ jsx("span", { className: "ax-tool__spacer" }),
49
- /* @__PURE__ */ jsxs("span", { className: "ax-tool__status", children: [
50
- /* @__PURE__ */ jsx("span", { className: "ax-tool__dot" }),
51
- STATUS_LABEL[status]
52
- ] }),
53
- Chev
54
- ] }),
55
- /* @__PURE__ */ jsxs("div", { className: "ax-tool__body", style: { display: open ? "flex" : "none" }, children: [
56
- args != null ? /* @__PURE__ */ jsxs("div", { children: [
57
- /* @__PURE__ */ jsx("div", { className: "ax-tool__block-label", children: "Arguments" }),
58
- /* @__PURE__ */ jsx("pre", { className: "ax-tool__pre", children: fmt(args) })
59
- ] }) : null,
60
- result != null ? /* @__PURE__ */ jsxs("div", { children: [
61
- /* @__PURE__ */ jsx("div", { className: "ax-tool__block-label", children: "Result" }),
62
- /* @__PURE__ */ jsx("pre", { className: "ax-tool__pre", children: fmt(result) })
63
- ] }) : null
64
- ] })
65
- ] });
73
+ return /* @__PURE__ */ jsxs(
74
+ "div",
75
+ {
76
+ className: ["ax-tool", "ax-tool--" + status, open ? "ax-tool--open" : "", className].filter(Boolean).join(" "),
77
+ ...rest,
78
+ children: [
79
+ /* @__PURE__ */ jsxs("button", { className: "ax-tool__head", "aria-expanded": open, onClick: () => setOpen((o) => !o), children: [
80
+ Wrench,
81
+ /* @__PURE__ */ jsxs("span", { className: "ax-tool__name", children: [
82
+ /* @__PURE__ */ jsx("b", { children: name }),
83
+ /* @__PURE__ */ jsx("span", { className: "ax-tool__paren", children: "()" })
84
+ ] }),
85
+ /* @__PURE__ */ jsx("span", { className: "ax-tool__spacer" }),
86
+ /* @__PURE__ */ jsxs("span", { className: "ax-tool__status", children: [
87
+ /* @__PURE__ */ jsx("span", { className: "ax-tool__dot" }),
88
+ STATUS_LABEL[status]
89
+ ] }),
90
+ Chev
91
+ ] }),
92
+ /* @__PURE__ */ jsxs("div", { className: "ax-tool__body", style: { display: open ? "flex" : "none" }, children: [
93
+ args != null ? /* @__PURE__ */ jsxs("div", { children: [
94
+ /* @__PURE__ */ jsx("div", { className: "ax-tool__block-label", children: "Arguments" }),
95
+ /* @__PURE__ */ jsx("pre", { className: "ax-tool__pre", children: fmt(args) })
96
+ ] }) : null,
97
+ result != null ? /* @__PURE__ */ jsxs("div", { children: [
98
+ /* @__PURE__ */ jsx("div", { className: "ax-tool__block-label", children: "Result" }),
99
+ /* @__PURE__ */ jsx("pre", { className: "ax-tool__pre", children: fmt(result) })
100
+ ] }) : null
101
+ ] })
102
+ ]
103
+ }
104
+ );
66
105
  }
67
106
  export {
68
107
  ToolCall
@@ -1 +1 @@
1
- {"version":3,"file":"ToolCall.js","sources":["../../../src/components/ai/ToolCall.jsx"],"sourcesContent":["import React, { useState } from \"react\";\n\nconst AX_TOOLCALL_CSS = `\n.ax-tool { border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--bg-0); overflow: hidden; font-family: var(--font-mono); }\n[data-theme=\"light\"] .ax-tool { background: var(--bg-1); }\n.ax-tool__head { display: flex; align-items: center; gap: 10px; width: 100%; appearance: none; background: none; border: none; cursor: pointer; text-align: left; padding: 9px 12px; }\n.ax-tool__head:focus-visible { outline: none; box-shadow: var(--ring); }\n.ax-tool__icon { width: 14px; height: 14px; flex: none; color: var(--text-faint); }\n.ax-tool__name { font-size: var(--text-sm); color: var(--text-body); }\n.ax-tool__name b { font-weight: var(--weight-medium); }\n.ax-tool__paren { color: var(--text-faint); }\n.ax-tool__spacer { flex: 1; }\n.ax-tool__status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; letter-spacing: var(--tracking-label); text-transform: uppercase; }\n.ax-tool__dot { width: 6px; height: 6px; border-radius: 1px; }\n.ax-tool--running .ax-tool__status { color: var(--warn); }\n.ax-tool--running .ax-tool__dot { background: var(--warn); animation: ax-tool-pulse 1s var(--ease-out) infinite; }\n.ax-tool--done .ax-tool__status { color: var(--ok); }\n.ax-tool--done .ax-tool__dot { background: var(--ok); }\n.ax-tool--error .ax-tool__status { color: var(--danger); }\n.ax-tool--error .ax-tool__dot { background: var(--danger); }\n.ax-tool__chev { width: 11px; height: 11px; color: var(--text-faint); transition: transform var(--dur-2) var(--ease-out); }\n.ax-tool--open .ax-tool__chev { transform: rotate(180deg); }\n.ax-tool__body { border-top: 1px solid var(--border-default); padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }\n.ax-tool__block-label { font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-faint); margin-bottom: 4px; }\n.ax-tool__pre { margin: 0; font-size: var(--text-xs); line-height: 1.6; color: var(--text-body); white-space: pre-wrap; word-break: break-word; }\n@keyframes ax-tool-pulse { 50% { opacity: 0.35; } }\n@media (prefers-reduced-motion: reduce) { .ax-tool--running .ax-tool__dot { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-toolcall-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-toolcall-css\";\n s.textContent = AX_TOOLCALL_CSS;\n document.head.appendChild(s);\n}\n\nconst Wrench = <svg className=\"ax-tool__icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"M14.7 6.3a4 4 0 0 0-5.4 5.4L3 18v3h3l6.3-6.3a4 4 0 0 0 5.4-5.4l-2.1 2.1-2.4-.6-.6-2.4 2.1-2.1z\"></path></svg>;\nconst Chev = <svg className=\"ax-tool__chev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><path d=\"m6 9 6 6 6-6\"></path></svg>;\n\nconst STATUS_LABEL = { running: \"Running\", done: \"Done\", error: \"Error\" };\n\nexport function ToolCall({ name, args, result, status = \"done\", defaultOpen = false, className = \"\", ...rest }) {\n const [open, setOpen] = useState(defaultOpen);\n const fmt = (v) => typeof v === \"string\" ? v : JSON.stringify(v, null, 2);\n return (\n <div className={[\"ax-tool\", \"ax-tool--\" + status, open ? \"ax-tool--open\" : \"\", className].filter(Boolean).join(\" \")} {...rest}>\n <button className=\"ax-tool__head\" aria-expanded={open} onClick={() => setOpen((o) => !o)}>\n {Wrench}\n <span className=\"ax-tool__name\"><b>{name}</b><span className=\"ax-tool__paren\">()</span></span>\n <span className=\"ax-tool__spacer\"></span>\n <span className=\"ax-tool__status\"><span className=\"ax-tool__dot\"></span>{STATUS_LABEL[status]}</span>\n {Chev}\n </button>\n <div className=\"ax-tool__body\" style={{ display: open ? \"flex\" : \"none\" }}>\n {args != null ? (\n <div>\n <div className=\"ax-tool__block-label\">Arguments</div>\n <pre className=\"ax-tool__pre\">{fmt(args)}</pre>\n </div>\n ) : null}\n {result != null ? (\n <div>\n <div className=\"ax-tool__block-label\">Result</div>\n <pre className=\"ax-tool__pre\">{fmt(result)}</pre>\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AAEA,MAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BxB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,iBAAiB,GAAG;AAClF,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEA,MAAM,6BAAU,OAAA,EAAI,WAAU,iBAAgB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,UAAA,oBAAC,QAAA,EAAK,GAAE,iGAAA,CAAiG,EAAA,CAAO;AACjR,MAAM,2BAAQ,OAAA,EAAI,WAAU,iBAAgB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,UAAA,oBAAC,QAAA,EAAK,GAAE,eAAA,CAAe,EAAA,CAAO;AAE/L,MAAM,eAAe,EAAE,SAAS,WAAW,MAAM,QAAQ,OAAO,QAAA;AAEzD,SAAS,SAAS,EAAE,MAAM,MAAM,QAAQ,SAAS,QAAQ,cAAc,OAAO,YAAY,IAAI,GAAG,QAAQ;AAC9G,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,WAAW;AAC5C,QAAM,MAAM,CAAC,MAAM,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,GAAG,MAAM,CAAC;AACxE,8BACG,OAAA,EAAI,WAAW,CAAC,WAAW,cAAc,QAAQ,OAAO,kBAAkB,IAAI,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAI,GAAG,MACvH,UAAA;AAAA,IAAA,qBAAC,UAAA,EAAO,WAAU,iBAAgB,iBAAe,MAAM,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,GACpF,UAAA;AAAA,MAAA;AAAA,MACD,qBAAC,QAAA,EAAK,WAAU,iBAAgB,UAAA;AAAA,QAAA,oBAAC,OAAG,UAAA,KAAA,CAAK;AAAA,QAAI,oBAAC,QAAA,EAAK,WAAU,kBAAiB,UAAA,KAAA,CAAE;AAAA,MAAA,GAAO;AAAA,MACvF,oBAAC,QAAA,EAAK,WAAU,kBAAA,CAAkB;AAAA,MAClC,qBAAC,QAAA,EAAK,WAAU,mBAAkB,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,eAAA,CAAe;AAAA,QAAQ,aAAa,MAAM;AAAA,MAAA,GAAE;AAAA,MAC7F;AAAA,IAAA,GACH;AAAA,IACA,qBAAC,OAAA,EAAI,WAAU,iBAAgB,OAAO,EAAE,SAAS,OAAO,SAAS,OAAA,GAC9D,UAAA;AAAA,MAAA,QAAQ,4BACN,OAAA,EACC,UAAA;AAAA,QAAA,oBAAC,OAAA,EAAI,WAAU,wBAAuB,UAAA,aAAS;AAAA,4BAC9C,OAAA,EAAI,WAAU,gBAAgB,UAAA,IAAI,IAAI,EAAA,CAAE;AAAA,MAAA,EAAA,CAC3C,IACE;AAAA,MACH,UAAU,OACT,qBAAC,OAAA,EACC,UAAA;AAAA,QAAA,oBAAC,OAAA,EAAI,WAAU,wBAAuB,UAAA,UAAM;AAAA,4BAC3C,OAAA,EAAI,WAAU,gBAAgB,UAAA,IAAI,MAAM,EAAA,CAAE;AAAA,MAAA,EAAA,CAC7C,IACE;AAAA,IAAA,EAAA,CACN;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"ToolCall.js","sources":["../../../src/components/ai/ToolCall.jsx"],"sourcesContent":["import React, { useState } from \"react\";\n\nconst AX_TOOLCALL_CSS = `\n.ax-tool { border: 1px solid var(--border-default); border-radius: var(--radius-2); background: var(--bg-1); overflow: hidden; font-family: var(--font-mono); }\n[data-theme=\"dark\"] .ax-tool { background: var(--bg-0); }\n.ax-tool__head { display: flex; align-items: center; gap: 10px; width: 100%; appearance: none; background: none; border: none; cursor: pointer; text-align: left; padding: 9px 12px; }\n.ax-tool__head:focus-visible { outline: none; box-shadow: var(--ring); }\n.ax-tool__icon { width: 14px; height: 14px; flex: none; color: var(--text-faint); }\n.ax-tool__name { font-size: var(--text-sm); color: var(--text-body); }\n.ax-tool__name b { font-weight: var(--weight-medium); }\n.ax-tool__paren { color: var(--text-faint); }\n.ax-tool__spacer { flex: 1; }\n.ax-tool__status { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; letter-spacing: var(--tracking-label); text-transform: uppercase; }\n.ax-tool__dot { width: 6px; height: 6px; border-radius: 1px; }\n.ax-tool--running .ax-tool__status { color: var(--warn); }\n.ax-tool--running .ax-tool__dot { background: var(--warn); animation: ax-tool-pulse 1s var(--ease-out) infinite; }\n.ax-tool--done .ax-tool__status { color: var(--ok); }\n.ax-tool--done .ax-tool__dot { background: var(--ok); }\n.ax-tool--error .ax-tool__status { color: var(--danger); }\n.ax-tool--error .ax-tool__dot { background: var(--danger); }\n.ax-tool__chev { width: 11px; height: 11px; color: var(--text-faint); transition: transform var(--dur-2) var(--ease-out); }\n.ax-tool--open .ax-tool__chev { transform: rotate(180deg); }\n.ax-tool__body { border-top: 1px solid var(--border-default); padding: 10px 12px; display: flex; flex-direction: column; gap: 10px; }\n.ax-tool__block-label { font-size: 10px; letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-faint); margin-bottom: 4px; }\n.ax-tool__pre { margin: 0; font-size: var(--text-xs); line-height: 1.6; color: var(--text-body); white-space: pre-wrap; word-break: break-word; }\n@keyframes ax-tool-pulse { 50% { opacity: 0.35; } }\n@media (prefers-reduced-motion: reduce) { .ax-tool--running .ax-tool__dot { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-toolcall-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-toolcall-css\";\n s.textContent = AX_TOOLCALL_CSS;\n document.head.appendChild(s);\n}\n\nconst Wrench = (\n <svg\n className=\"ax-tool__icon\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M14.7 6.3a4 4 0 0 0-5.4 5.4L3 18v3h3l6.3-6.3a4 4 0 0 0 5.4-5.4l-2.1 2.1-2.4-.6-.6-2.4 2.1-2.1z\"></path>\n </svg>\n);\nconst Chev = (\n <svg\n className=\"ax-tool__chev\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m6 9 6 6 6-6\"></path>\n </svg>\n);\n\nconst STATUS_LABEL = { running: \"Running\", done: \"Done\", error: \"Error\" };\n\nexport function ToolCall({\n name,\n args,\n result,\n status = \"done\",\n defaultOpen = false,\n className = \"\",\n ...rest\n}) {\n const [open, setOpen] = useState(defaultOpen);\n const fmt = (v) => (typeof v === \"string\" ? v : JSON.stringify(v, null, 2));\n return (\n <div\n className={[\"ax-tool\", \"ax-tool--\" + status, open ? \"ax-tool--open\" : \"\", className]\n .filter(Boolean)\n .join(\" \")}\n {...rest}\n >\n <button className=\"ax-tool__head\" aria-expanded={open} onClick={() => setOpen((o) => !o)}>\n {Wrench}\n <span className=\"ax-tool__name\">\n <b>{name}</b>\n <span className=\"ax-tool__paren\">()</span>\n </span>\n <span className=\"ax-tool__spacer\"></span>\n <span className=\"ax-tool__status\">\n <span className=\"ax-tool__dot\"></span>\n {STATUS_LABEL[status]}\n </span>\n {Chev}\n </button>\n <div className=\"ax-tool__body\" style={{ display: open ? \"flex\" : \"none\" }}>\n {args != null ? (\n <div>\n <div className=\"ax-tool__block-label\">Arguments</div>\n <pre className=\"ax-tool__pre\">{fmt(args)}</pre>\n </div>\n ) : null}\n {result != null ? (\n <div>\n <div className=\"ax-tool__block-label\">Result</div>\n <pre className=\"ax-tool__pre\">{fmt(result)}</pre>\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Top-bar account control. Signed out → a sign-in button (onLogin). Signed in →
3
+ * an avatar that opens a DropdownMenu with the email, your custom items, and a
4
+ * sign-out action. Custom items use the DropdownMenu item shape.
5
+ */
6
+ export interface AccountMenuItem {
7
+ label?: string;
8
+ icon?: React.ReactNode;
9
+ onSelect?: () => void;
10
+ type?: "label" | "separator";
11
+ danger?: boolean;
12
+ }
13
+ export interface AccountControlProps {
14
+ /** Session user, or null/undefined when signed out. */
15
+ user?: { email: string; name?: string } | null;
16
+ onLogin?: () => void;
17
+ onLogout?: () => void;
18
+ /** Extra menu items inserted between the email and sign-out (e.g. 我的脚本). */
19
+ items?: AccountMenuItem[];
20
+ /** @default "登录" */
21
+ signInLabel?: string;
22
+ }
23
+ export declare function AccountControl(props: AccountControlProps): JSX.Element;