@steerable/agent-ui 0.2.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 (104) hide show
  1. package/LICENSE +91 -0
  2. package/README.md +118 -0
  3. package/dist/components/ChatPanel.d.ts +42 -0
  4. package/dist/components/ChatPanel.d.ts.map +1 -0
  5. package/dist/components/ChatPanel.js +46 -0
  6. package/dist/components/ChatPanel.js.map +1 -0
  7. package/dist/components/ChatPanel.stories.d.ts +17 -0
  8. package/dist/components/ChatPanel.stories.d.ts.map +1 -0
  9. package/dist/components/ChatPanel.stories.js +143 -0
  10. package/dist/components/ChatPanel.stories.js.map +1 -0
  11. package/dist/components/MessageList.d.ts +44 -0
  12. package/dist/components/MessageList.d.ts.map +1 -0
  13. package/dist/components/MessageList.js +43 -0
  14. package/dist/components/MessageList.js.map +1 -0
  15. package/dist/components/MessageList.stories.d.ts +12 -0
  16. package/dist/components/MessageList.stories.d.ts.map +1 -0
  17. package/dist/components/MessageList.stories.js +88 -0
  18. package/dist/components/MessageList.stories.js.map +1 -0
  19. package/dist/components/OrchestrationPlanCard.d.ts +32 -0
  20. package/dist/components/OrchestrationPlanCard.d.ts.map +1 -0
  21. package/dist/components/OrchestrationPlanCard.js +48 -0
  22. package/dist/components/OrchestrationPlanCard.js.map +1 -0
  23. package/dist/components/OrchestrationPlanCard.stories.d.ts +12 -0
  24. package/dist/components/OrchestrationPlanCard.stories.d.ts.map +1 -0
  25. package/dist/components/OrchestrationPlanCard.stories.js +84 -0
  26. package/dist/components/OrchestrationPlanCard.stories.js.map +1 -0
  27. package/dist/components/SSEStreamView.d.ts +29 -0
  28. package/dist/components/SSEStreamView.d.ts.map +1 -0
  29. package/dist/components/SSEStreamView.js +78 -0
  30. package/dist/components/SSEStreamView.js.map +1 -0
  31. package/dist/components/SSEStreamView.stories.d.ts +12 -0
  32. package/dist/components/SSEStreamView.stories.d.ts.map +1 -0
  33. package/dist/components/SSEStreamView.stories.js +86 -0
  34. package/dist/components/SSEStreamView.stories.js.map +1 -0
  35. package/dist/components/ToolCallRenderer.d.ts +42 -0
  36. package/dist/components/ToolCallRenderer.d.ts.map +1 -0
  37. package/dist/components/ToolCallRenderer.js +70 -0
  38. package/dist/components/ToolCallRenderer.js.map +1 -0
  39. package/dist/components/ToolCallRenderer.stories.d.ts +13 -0
  40. package/dist/components/ToolCallRenderer.stories.d.ts.map +1 -0
  41. package/dist/components/ToolCallRenderer.stories.js +112 -0
  42. package/dist/components/ToolCallRenderer.stories.js.map +1 -0
  43. package/dist/components/cn.d.ts +7 -0
  44. package/dist/components/cn.d.ts.map +1 -0
  45. package/dist/components/cn.js +9 -0
  46. package/dist/components/cn.js.map +1 -0
  47. package/dist/components/index.d.ts +11 -0
  48. package/dist/components/index.d.ts.map +1 -0
  49. package/dist/components/index.js +6 -0
  50. package/dist/components/index.js.map +1 -0
  51. package/dist/hooks/index.d.ts +7 -0
  52. package/dist/hooks/index.d.ts.map +1 -0
  53. package/dist/hooks/index.js +4 -0
  54. package/dist/hooks/index.js.map +1 -0
  55. package/dist/hooks/useAgentSession.d.ts +56 -0
  56. package/dist/hooks/useAgentSession.d.ts.map +1 -0
  57. package/dist/hooks/useAgentSession.js +128 -0
  58. package/dist/hooks/useAgentSession.js.map +1 -0
  59. package/dist/hooks/useChatStream.d.ts +59 -0
  60. package/dist/hooks/useChatStream.d.ts.map +1 -0
  61. package/dist/hooks/useChatStream.js +251 -0
  62. package/dist/hooks/useChatStream.js.map +1 -0
  63. package/dist/hooks/useToolCallStatus.d.ts +47 -0
  64. package/dist/hooks/useToolCallStatus.d.ts.map +1 -0
  65. package/dist/hooks/useToolCallStatus.js +57 -0
  66. package/dist/hooks/useToolCallStatus.js.map +1 -0
  67. package/dist/index.d.ts +12 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +12 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/tailwind-preset.d.ts +76 -0
  72. package/dist/tailwind-preset.d.ts.map +1 -0
  73. package/dist/tailwind-preset.js +104 -0
  74. package/dist/tailwind-preset.js.map +1 -0
  75. package/package.json +97 -0
  76. package/src/Introduction.mdx +71 -0
  77. package/src/components/ChatPanel.stories.tsx +173 -0
  78. package/src/components/ChatPanel.tsx +188 -0
  79. package/src/components/MessageList.stories.tsx +112 -0
  80. package/src/components/MessageList.test.tsx +57 -0
  81. package/src/components/MessageList.tsx +144 -0
  82. package/src/components/OrchestrationPlanCard.stories.tsx +98 -0
  83. package/src/components/OrchestrationPlanCard.test.tsx +34 -0
  84. package/src/components/OrchestrationPlanCard.tsx +159 -0
  85. package/src/components/SSEStreamView.stories.tsx +99 -0
  86. package/src/components/SSEStreamView.test.tsx +42 -0
  87. package/src/components/SSEStreamView.tsx +155 -0
  88. package/src/components/ToolCallRenderer.stories.tsx +152 -0
  89. package/src/components/ToolCallRenderer.test.tsx +93 -0
  90. package/src/components/ToolCallRenderer.tsx +213 -0
  91. package/src/components/cn.ts +10 -0
  92. package/src/components/index.ts +18 -0
  93. package/src/hooks/index.ts +21 -0
  94. package/src/hooks/useAgentSession.mdx +115 -0
  95. package/src/hooks/useAgentSession.test.ts +127 -0
  96. package/src/hooks/useAgentSession.ts +181 -0
  97. package/src/hooks/useChatStream.mdx +129 -0
  98. package/src/hooks/useChatStream.test.ts +233 -0
  99. package/src/hooks/useChatStream.ts +341 -0
  100. package/src/hooks/useToolCallStatus.mdx +102 -0
  101. package/src/hooks/useToolCallStatus.test.ts +99 -0
  102. package/src/hooks/useToolCallStatus.ts +99 -0
  103. package/src/index.ts +12 -0
  104. package/src/tailwind-preset.ts +109 -0
package/LICENSE ADDED
@@ -0,0 +1,91 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity.
18
+
19
+ "You" (or "Your") shall mean an individual or Legal Entity
20
+ exercising permissions granted by this License.
21
+
22
+ "Source" form shall mean the preferred form for making modifications.
23
+
24
+ "Object" form shall mean any form resulting from mechanical
25
+ transformation or translation of a Source form.
26
+
27
+ "Work" shall mean the work of authorship made available under the
28
+ License.
29
+
30
+ "Derivative Works" shall mean any work based on the Work.
31
+
32
+ "Contribution" shall mean any work of authorship intentionally
33
+ submitted to Licensor for inclusion in the Work.
34
+
35
+ "Contributor" shall mean Licensor and any individual or Legal Entity
36
+ on behalf of whom a Contribution has been received by Licensor.
37
+
38
+ 2. Grant of Copyright License.
39
+ Subject to the terms and conditions of this License, each Contributor
40
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
41
+ royalty-free, irrevocable copyright license to reproduce, prepare
42
+ Derivative Works of, publicly display, publicly perform, sublicense,
43
+ and distribute the Work and such Derivative Works in Source or Object
44
+ form.
45
+
46
+ 3. Grant of Patent License.
47
+ Subject to the terms and conditions of this License, each Contributor
48
+ hereby grants to You a perpetual, worldwide, non-exclusive, no-charge,
49
+ royalty-free, irrevocable patent license to make, have made, use,
50
+ offer to sell, sell, import, and otherwise transfer the Work.
51
+
52
+ 4. Redistribution.
53
+ You may reproduce and distribute copies of the Work or Derivative
54
+ Works thereof in any medium, with or without modifications, and in
55
+ Source or Object form, provided that You meet the following conditions:
56
+
57
+ (a) You must give any other recipients of the Work or Derivative
58
+ Works a copy of this License; and
59
+ (b) You must cause any modified files to carry prominent notices
60
+ stating that You changed the files; and
61
+ (c) You must retain, in the Source form of any Derivative Works that
62
+ You distribute, all copyright, patent, trademark, and attribution
63
+ notices from the Source form of the Work; and
64
+ (d) If the Work includes a "NOTICE" text file as part of its
65
+ distribution, then any Derivative Works that You distribute must
66
+ include a readable copy of the attribution notices contained within
67
+ such NOTICE file.
68
+
69
+ 5. Submission of Contributions.
70
+ Unless You explicitly state otherwise, any Contribution intentionally
71
+ submitted for inclusion in the Work shall be under the terms and
72
+ conditions of this License.
73
+
74
+ 6. Trademarks.
75
+ This License does not grant permission to use the trade names,
76
+ trademarks, service marks, or product names of the Licensor.
77
+
78
+ 7. Disclaimer of Warranty.
79
+ Unless required by applicable law or agreed to in writing, Licensor
80
+ provides the Work on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
81
+ OF ANY KIND.
82
+
83
+ 8. Limitation of Liability.
84
+ In no event and under no legal theory shall any Contributor be liable
85
+ to You for damages arising as a result of this License or out of the
86
+ use or inability to use the Work.
87
+
88
+ 9. Accepting Warranty or Additional Liability.
89
+ While redistributing the Work or Derivative Works thereof, You may
90
+ choose to offer, and charge a fee for, acceptance of support, warranty,
91
+ indemnity, or other liability obligations.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # @steerable/agent-ui
2
+
3
+ > Headless React components + hooks + Tailwind preset for building agent UIs on
4
+ > top of `@steerable/agent-protocol`. Drop-in compatible with `deeppath/apps/web`'s
5
+ > ChatPanel architecture; designed to be reused by `deeppath-agent` (Electron)
6
+ > after sidecar wiring lands.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @steerable/agent-ui @steerable/agent-protocol react react-dom
12
+ ```
13
+
14
+ `@steerable/agent-ui` ships only **types + hooks + headless components**. UI
15
+ styling is delegated to your Tailwind config via the `@steerable/agent-ui`
16
+ preset.
17
+
18
+ ## Tailwind preset
19
+
20
+ ```ts
21
+ // tailwind.config.ts
22
+ import preset from '@steerable/agent-ui/tailwind-preset';
23
+
24
+ export default {
25
+ presets: [preset],
26
+ content: [
27
+ './src/**/*.{ts,tsx}',
28
+ './node_modules/@steerable/agent-ui/dist/**/*.js',
29
+ ],
30
+ };
31
+ ```
32
+
33
+ The preset exposes design tokens (`bg-agent-canvas`, `text-agent-foreground`,
34
+ `border-agent-border`, …) that map to CSS variables. Override them in your own
35
+ `globals.css` to retheme without forking components.
36
+
37
+ ## Architecture
38
+
39
+ ```
40
+ @steerable/agent-protocol (types: SSEEvent, ChatMessage, ToolCall, ...)
41
+
42
+ │ types-only
43
+
44
+ @steerable/agent-ui
45
+ ├─ hooks/ ← framework-agnostic state + side-effects
46
+ │ ├─ useAgentSession create / resume / list sessions
47
+ │ ├─ useChatStream consume an SSE stream of events into ChatMessage[]
48
+ │ └─ useToolCallStatus tool call lifecycle (proposed → running → done)
49
+
50
+ ├─ components/ ← headless React (Tailwind class names only)
51
+ │ ├─ MessageList virtualized + auto-scroll list of ChatMessages
52
+ │ ├─ ChatPanel orchestrates header / list / input
53
+ │ ├─ OrchestrationPlanCard multi-step plan with progress
54
+ │ ├─ ToolCallRenderer safe (read) / proposed (write) / running / done
55
+ │ └─ SSEStreamView live JSON event log (debug + power-user)
56
+
57
+ └─ tailwind-preset.ts ← shared design tokens
58
+ ```
59
+
60
+ Each hook returns plain data; each component takes plain props. Nothing imports
61
+ React Router, Next.js, or any specific HTTP client — the consumer wires the
62
+ actual transport (fetch / SSE / IPC / sidecar).
63
+
64
+ ## Status (0.1.0)
65
+
66
+ This is the **foundation release** of the framework UI. It ships:
67
+
68
+ * ✅ Tailwind preset with the design tokens used by `deeppath/apps/web`.
69
+ * ✅ Three foundational hooks listed in the architecture diagram.
70
+ * ✅ Five core components listed in the architecture diagram (intentionally
71
+ the minimum for a usable chat UI). Each is fully type-safe against
72
+ `@steerable/agent-protocol`.
73
+ * ✅ Vitest test suite covering hooks + render snapshots.
74
+ * ⏸ Storybook is **deferred** to 0.2.0 / P8 docs (per project decision).
75
+ * ⏸ Multi-agent group chat (ChatTabs / AgentManagementModal), slash command
76
+ palette, automation alerts, file upload, share-image modal, etc. are tracked
77
+ for 0.2.0 / 0.3.0 — they exist in `deeppath/apps/web` today and will be
78
+ ported once the foundational set is consumed in production by P7.
79
+
80
+ ## Usage example
81
+
82
+ ```tsx
83
+ import {
84
+ ChatPanel,
85
+ MessageList,
86
+ ToolCallRenderer,
87
+ useChatStream,
88
+ } from '@steerable/agent-ui';
89
+ import type { SSEEvent } from '@steerable/agent-protocol';
90
+
91
+ function MyChat({ chatId }: { chatId: string }) {
92
+ const { messages, isStreaming, sendUserMessage } = useChatStream({
93
+ transport: {
94
+ stream: async (input, onEvent) => {
95
+ const res = await fetch('/api/v2/chats/' + chatId + '/stream', {
96
+ method: 'POST',
97
+ body: JSON.stringify(input),
98
+ });
99
+ const reader = res.body!.getReader();
100
+ // … decode + dispatch onEvent(SSEEvent) …
101
+ },
102
+ },
103
+ });
104
+
105
+ return (
106
+ <ChatPanel
107
+ messages={messages}
108
+ isStreaming={isStreaming}
109
+ onSubmit={sendUserMessage}
110
+ renderToolCall={(call) => <ToolCallRenderer call={call} />}
111
+ />
112
+ );
113
+ }
114
+ ```
115
+
116
+ See `apps/web/src/app/goals/desktop/components/ChatPanel/ChatPanel.tsx` in the
117
+ `deeppath` repo for the production-grade caller (which P7 will rewrite to
118
+ import these primitives).
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `ChatPanel` — the foundational orchestration shell.
3
+ *
4
+ * Composes `MessageList` + a minimal input row + a stop button. The component
5
+ * is intentionally minimal so consumers can layer on the production-grade
6
+ * extras (model selector, tool toggles, mention picker, slash commands, file
7
+ * upload, automation alerts, share-image button, multi-tab agent switcher,
8
+ * orchestration sidebar) without having to fork the chat itself.
9
+ *
10
+ * Production callers in `deeppath/apps/web` will build their fancier
11
+ * `<ChatHeader />` and `<ChatInput />` separately and feed `ChatPanel` only
12
+ * the `messages` + `onSubmit` props — the rest is rendered around it.
13
+ */
14
+ import type { ChatMessage, ToolCall, ToolResult } from '@steerable/agent-protocol';
15
+ export interface ChatPanelProps {
16
+ messages: ChatMessage[];
17
+ isStreaming?: boolean;
18
+ /**
19
+ * Called when the user submits the input. Receives the trimmed string.
20
+ * The parent is responsible for calling `useChatStream().sendUserMessage`.
21
+ */
22
+ onSubmit: (input: {
23
+ content: string;
24
+ }) => void | Promise<void>;
25
+ onCancel?: () => void;
26
+ className?: string;
27
+ inputPlaceholder?: string;
28
+ /** Slot rendered above the message list (header, agent picker, …). */
29
+ header?: React.ReactNode;
30
+ /**
31
+ * Tool-call renderer. Default uses the framework's `ToolCallRenderer`;
32
+ * pass `() => null` to suppress, or your own component to inject a
33
+ * domain-specific renderer (CIFLog cards, SQL query previews, …).
34
+ */
35
+ renderToolCall?: (call: ToolCall, result?: ToolResult) => React.ReactNode;
36
+ /** Empty-state slot for the message list. */
37
+ emptyState?: React.ReactNode;
38
+ /** Disable the input (e.g. during initial chat load). */
39
+ disabled?: boolean;
40
+ }
41
+ export declare function ChatPanel(props: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
42
+ //# sourceMappingURL=ChatPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAKnF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;OAGG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1E,6CAA6C;IAC7C,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAMD,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CA0I9C"}
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `ChatPanel` — the foundational orchestration shell.
4
+ *
5
+ * Composes `MessageList` + a minimal input row + a stop button. The component
6
+ * is intentionally minimal so consumers can layer on the production-grade
7
+ * extras (model selector, tool toggles, mention picker, slash commands, file
8
+ * upload, automation alerts, share-image button, multi-tab agent switcher,
9
+ * orchestration sidebar) without having to fork the chat itself.
10
+ *
11
+ * Production callers in `deeppath/apps/web` will build their fancier
12
+ * `<ChatHeader />` and `<ChatInput />` separately and feed `ChatPanel` only
13
+ * the `messages` + `onSubmit` props — the rest is rendered around it.
14
+ */
15
+ import { useCallback, useState } from 'react';
16
+ import { MessageList } from './MessageList.js';
17
+ import { ToolCallRenderer } from './ToolCallRenderer.js';
18
+ import { cn } from './cn.js';
19
+ function defaultRenderToolCall(call, result) {
20
+ return _jsx(ToolCallRenderer, { call: call, result: result }, call.id);
21
+ }
22
+ export function ChatPanel(props) {
23
+ const { messages, isStreaming = false, onSubmit, onCancel, className, inputPlaceholder = 'Send a message…', header, renderToolCall = defaultRenderToolCall, emptyState, disabled = false, } = props;
24
+ const [draft, setDraft] = useState('');
25
+ const handleSubmit = useCallback(async (e) => {
26
+ e?.preventDefault();
27
+ const trimmed = draft.trim();
28
+ if (!trimmed || disabled)
29
+ return;
30
+ setDraft('');
31
+ await onSubmit({ content: trimmed });
32
+ }, [draft, disabled, onSubmit]);
33
+ const renderMessage = useCallback((rendererProps) => {
34
+ const { message, isLastAssistant, isStreaming: streaming } = rendererProps;
35
+ const isAssistant = message.role === 'assistant';
36
+ return (_jsx("div", { "data-role": message.role, className: cn('flex w-full', isAssistant ? 'justify-start' : 'justify-end'), children: _jsxs("div", { className: cn('flex max-w-[80%] flex-col gap-2', isAssistant ? 'items-start' : 'items-end'), children: [_jsxs("div", { className: cn('rounded-agent-md border px-3 py-2 text-sm leading-relaxed', isAssistant
37
+ ? 'border-agent-border bg-agent-muted text-agent-foreground'
38
+ : 'border-transparent bg-agent-accent text-agent-accent-foreground'), children: [_jsx("div", { className: "whitespace-pre-wrap", children: message.content || (isLastAssistant && streaming ? '…' : '') }), isLastAssistant && streaming ? (_jsx("span", { "aria-hidden": true, className: "ml-0.5 inline-block h-3 w-1.5 align-baseline bg-agent-foreground animate-agent-cursor-blink" })) : null] }), Array.isArray(message.toolCalls) && message.toolCalls.length > 0 ? (_jsx("div", { className: "flex w-full flex-col gap-1.5", children: message.toolCalls.map((call) => renderToolCall(call,
39
+ // The hook attaches a single `toolResult` to the message;
40
+ // production callers that want per-call results should
41
+ // override `renderToolCall` and look them up themselves.
42
+ message.toolResult)) })) : null] }) }, message.id ?? rendererProps.index));
43
+ }, [renderToolCall]);
44
+ return (_jsxs("div", { className: cn('flex h-full w-full flex-col bg-agent-canvas text-agent-foreground', className), children: [header, _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(MessageList, { messages: messages, isStreaming: isStreaming, renderMessage: renderMessage, emptyState: emptyState }) }), _jsxs("form", { className: "flex items-center gap-2 border-t border-agent-border bg-agent-canvas px-3 py-2", onSubmit: handleSubmit, children: [_jsx("input", { type: "text", value: draft, onChange: (e) => setDraft(e.target.value), placeholder: inputPlaceholder, disabled: disabled, className: cn('flex-1 rounded-agent-md border border-agent-border bg-agent-canvas px-3 py-2 text-sm text-agent-foreground placeholder:text-agent-muted-foreground', 'focus:border-agent-accent focus:outline-none focus:ring-1 focus:ring-agent-accent', disabled && 'opacity-50') }), isStreaming && onCancel ? (_jsx("button", { type: "button", onClick: onCancel, className: "rounded-agent-md border border-agent-border px-3 py-2 text-sm text-agent-foreground hover:bg-agent-muted", children: "Stop" })) : (_jsx("button", { type: "submit", disabled: disabled || !draft.trim(), className: cn('rounded-agent-md bg-agent-accent px-3 py-2 text-sm font-medium text-agent-accent-foreground', (disabled || !draft.trim()) && 'opacity-50'), children: "Send" }))] })] }));
45
+ }
46
+ //# sourceMappingURL=ChatPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatPanel.js","sourceRoot":"","sources":["../../src/components/ChatPanel.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { ChatPanel } from './ChatPanel.js';
3
+ declare const meta: Meta<typeof ChatPanel>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof ChatPanel>;
6
+ export declare const Empty: Story;
7
+ export declare const WithMessages: Story;
8
+ export declare const Streaming: Story;
9
+ export declare const Disabled: Story;
10
+ export declare const WithToolCalls: Story;
11
+ /**
12
+ * Demonstrates the full controlled pattern: parent owns the message list,
13
+ * appends a new turn on submit, and toggles `isStreaming` while the assistant
14
+ * is "responding". Useful for sandbox-testing custom renderers.
15
+ */
16
+ export declare const Interactive: Story;
17
+ //# sourceMappingURL=ChatPanel.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatPanel.stories.d.ts","sourceRoot":"","sources":["../../src/components/ChatPanel.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,SAAS,CAwBhC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,SAAS,CAAC,CAAC;AA0BxC,eAAO,MAAM,KAAK,EAAE,KAUnB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAI1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAavB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAMtB,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KA6B3B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,KAqCzB,CAAC"}
@@ -0,0 +1,143 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { ChatPanel } from './ChatPanel.js';
4
+ const meta = {
5
+ title: 'Components/ChatPanel',
6
+ component: ChatPanel,
7
+ parameters: {
8
+ layout: 'fullscreen',
9
+ actions: { argTypesRegex: '^on[A-Z].*' },
10
+ docs: {
11
+ description: {
12
+ component: 'Foundational orchestration shell. Renders a `MessageList` plus a minimal input row and a stop button. Production callers typically wrap it with their own header/composer and only feed in `messages` + `onSubmit`.',
13
+ },
14
+ },
15
+ },
16
+ args: {
17
+ onSubmit: () => { },
18
+ inputPlaceholder: 'Send a message…',
19
+ },
20
+ decorators: [
21
+ (Story) => (_jsx("div", { className: "h-[640px] w-full max-w-3xl border border-agent-border bg-agent-canvas", children: _jsx(Story, {}) })),
22
+ ],
23
+ };
24
+ export default meta;
25
+ const NOW = '2026-05-15T00:00:00Z';
26
+ const sampleMessages = [
27
+ {
28
+ id: 'm-1',
29
+ role: 'user',
30
+ content: 'Plan a 3-day trip to Tokyo for a first-time visitor.',
31
+ createdAt: NOW,
32
+ },
33
+ {
34
+ id: 'm-2',
35
+ role: 'assistant',
36
+ content: "Sure — here's a balanced 3-day plan covering culture, food and a half-day trip outside the city. I'll list each day with morning/afternoon/evening blocks.",
37
+ createdAt: NOW,
38
+ },
39
+ {
40
+ id: 'm-3',
41
+ role: 'user',
42
+ content: 'Great. Also include a vegetarian dinner each night.',
43
+ createdAt: NOW,
44
+ },
45
+ ];
46
+ export const Empty = {
47
+ args: {
48
+ messages: [],
49
+ emptyState: (_jsxs("div", { className: "m-auto max-w-sm text-center text-sm text-agent-muted-foreground", children: [_jsx("p", { className: "font-medium text-agent-foreground", children: "Start a new conversation" }), _jsx("p", { className: "mt-1", children: "Ask anything \u2014 I'll plan, search and call tools as needed." })] })),
50
+ },
51
+ };
52
+ export const WithMessages = {
53
+ args: {
54
+ messages: sampleMessages,
55
+ },
56
+ };
57
+ export const Streaming = {
58
+ args: {
59
+ messages: [
60
+ ...sampleMessages,
61
+ {
62
+ id: 'm-4',
63
+ role: 'assistant',
64
+ content: "Day 1 — Asakusa & Akihabara. Morning: visit Sensō-ji",
65
+ createdAt: NOW,
66
+ },
67
+ ],
68
+ isStreaming: true,
69
+ },
70
+ };
71
+ export const Disabled = {
72
+ args: {
73
+ messages: sampleMessages,
74
+ disabled: true,
75
+ inputPlaceholder: 'Loading conversation…',
76
+ },
77
+ };
78
+ export const WithToolCalls = {
79
+ args: {
80
+ messages: [
81
+ sampleMessages[0],
82
+ {
83
+ id: 'm-tc',
84
+ role: 'assistant',
85
+ content: 'Looking up current restaurant options in Tokyo for you.',
86
+ createdAt: NOW,
87
+ toolCalls: [
88
+ {
89
+ id: 'call-1',
90
+ name: 'web_search',
91
+ arguments: { query: 'best vegetarian restaurants Shibuya 2026' },
92
+ },
93
+ ],
94
+ toolResult: {
95
+ success: true,
96
+ message: 'Found 2 results.',
97
+ data: {
98
+ hits: [
99
+ { title: 'T\u2019s Tantan (Shibuya)', url: 'https://example.com' },
100
+ { title: 'AIN SOPH. Soar', url: 'https://example.com' },
101
+ ],
102
+ },
103
+ },
104
+ },
105
+ ],
106
+ },
107
+ };
108
+ /**
109
+ * Demonstrates the full controlled pattern: parent owns the message list,
110
+ * appends a new turn on submit, and toggles `isStreaming` while the assistant
111
+ * is "responding". Useful for sandbox-testing custom renderers.
112
+ */
113
+ export const Interactive = {
114
+ args: {
115
+ messages: sampleMessages,
116
+ },
117
+ render: (args) => {
118
+ const InteractiveChat = () => {
119
+ const [messages, setMessages] = useState(args.messages);
120
+ const [streaming, setStreaming] = useState(false);
121
+ return (_jsx(ChatPanel, { ...args, messages: messages, isStreaming: streaming, onSubmit: async ({ content }) => {
122
+ setMessages((prev) => [
123
+ ...prev,
124
+ { id: `u-${prev.length}`, role: 'user', content, createdAt: new Date().toISOString() },
125
+ ]);
126
+ setStreaming(true);
127
+ await new Promise((r) => setTimeout(r, 600));
128
+ setMessages((prev) => [
129
+ ...prev,
130
+ {
131
+ id: `a-${prev.length}`,
132
+ role: 'assistant',
133
+ content: `Echo: ${content}`,
134
+ createdAt: new Date().toISOString(),
135
+ },
136
+ ]);
137
+ setStreaming(false);
138
+ } }));
139
+ };
140
+ return _jsx(InteractiveChat, {});
141
+ },
142
+ };
143
+ //# sourceMappingURL=ChatPanel.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatPanel.stories.js","sourceRoot":"","sources":["../../src/components/ChatPanel.stories.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `MessageList` — render a list of `ChatMessage`s with auto-scroll-to-bottom
3
+ * behaviour. The component is intentionally **headless**: it knows the list
4
+ * shape but lets the caller render every individual bubble.
5
+ *
6
+ * Why a render prop instead of a fixed bubble component? Because every
7
+ * consumer wants to layer additional UI (markdown, search-source pills,
8
+ * mention chips, action renderers) onto the assistant message and we don't
9
+ * want to ship every pluggable variant from `deeppath/apps/web` here.
10
+ */
11
+ import type { ChatMessage } from '@steerable/agent-protocol';
12
+ export interface MessageRendererProps {
13
+ message: ChatMessage;
14
+ index: number;
15
+ isLastAssistant: boolean;
16
+ isStreaming: boolean;
17
+ }
18
+ export interface MessageListProps {
19
+ messages: ChatMessage[];
20
+ isStreaming?: boolean;
21
+ className?: string;
22
+ /**
23
+ * Bubble renderer. If omitted, `MessageList` falls back to the built-in
24
+ * `DefaultMessageBubble` which just renders role + content.
25
+ */
26
+ renderMessage?: (props: MessageRendererProps) => React.ReactNode;
27
+ /**
28
+ * If true (default), scrolls the container to the bottom every time the
29
+ * message list grows or the last assistant message gets new content.
30
+ */
31
+ autoScroll?: boolean;
32
+ /**
33
+ * Slot rendered above the first message — typically a "start of conversation"
34
+ * marker or a system prompt summary.
35
+ */
36
+ header?: React.ReactNode;
37
+ /**
38
+ * Slot rendered when `messages` is empty. Production callers usually drop
39
+ * an `<EmptyChat />` here.
40
+ */
41
+ emptyState?: React.ReactNode;
42
+ }
43
+ export declare function MessageList(props: MessageListProps): import("react/jsx-runtime").JSX.Element;
44
+ //# sourceMappingURL=MessageList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageList.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,KAAK,KAAK,CAAC,SAAS,CAAC;IACjE;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAgCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAiElD"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * `MessageList` — render a list of `ChatMessage`s with auto-scroll-to-bottom
4
+ * behaviour. The component is intentionally **headless**: it knows the list
5
+ * shape but lets the caller render every individual bubble.
6
+ *
7
+ * Why a render prop instead of a fixed bubble component? Because every
8
+ * consumer wants to layer additional UI (markdown, search-source pills,
9
+ * mention chips, action renderers) onto the assistant message and we don't
10
+ * want to ship every pluggable variant from `deeppath/apps/web` here.
11
+ */
12
+ import { useEffect, useMemo, useRef } from 'react';
13
+ import { cn } from './cn.js';
14
+ function DefaultMessageBubble({ message, isLastAssistant, isStreaming }) {
15
+ const isAssistant = message.role === 'assistant';
16
+ return (_jsx("div", { "data-role": message.role, className: cn('flex w-full', isAssistant ? 'justify-start' : 'justify-end'), children: _jsxs("div", { className: cn('max-w-[80%] rounded-agent-md border px-3 py-2 text-sm leading-relaxed', isAssistant
17
+ ? 'border-agent-border bg-agent-muted text-agent-foreground'
18
+ : 'border-transparent bg-agent-accent text-agent-accent-foreground'), children: [_jsx("div", { className: "whitespace-pre-wrap", children: message.content || (isLastAssistant && isStreaming ? '…' : '') }), isLastAssistant && isStreaming ? (_jsx("span", { "aria-hidden": true, className: "ml-0.5 inline-block h-3 w-1.5 align-baseline bg-agent-foreground animate-agent-cursor-blink" })) : null] }) }));
19
+ }
20
+ export function MessageList(props) {
21
+ const { messages, isStreaming = false, className, renderMessage, autoScroll = true, header, emptyState, } = props;
22
+ const containerRef = useRef(null);
23
+ const lastAssistantIndex = useMemo(() => {
24
+ for (let i = messages.length - 1; i >= 0; i--) {
25
+ if (messages[i].role === 'assistant')
26
+ return i;
27
+ }
28
+ return -1;
29
+ }, [messages]);
30
+ // Scroll to bottom on growth or when the streaming assistant gets longer.
31
+ const lastAssistantContent = lastAssistantIndex >= 0 ? messages[lastAssistantIndex].content : '';
32
+ useEffect(() => {
33
+ if (!autoScroll)
34
+ return;
35
+ const el = containerRef.current;
36
+ if (!el)
37
+ return;
38
+ el.scrollTop = el.scrollHeight;
39
+ }, [autoScroll, messages.length, lastAssistantContent]);
40
+ const Renderer = renderMessage ?? DefaultMessageBubble;
41
+ return (_jsxs("div", { ref: containerRef, className: cn('flex h-full w-full flex-col gap-3 overflow-y-auto bg-agent-canvas px-4 py-3', 'focus:outline-none focus-visible:ring-2 focus-visible:ring-agent-accent', className), role: "log", "aria-live": "polite", "aria-label": "Conversation", tabIndex: 0, children: [header, messages.length === 0 ? (emptyState ?? (_jsx("div", { className: "m-auto text-sm text-agent-muted-foreground", children: "No messages yet." }))) : (messages.map((message, index) => (_jsx(Renderer, { message: message, index: index, isLastAssistant: index === lastAssistantIndex, isStreaming: isStreaming }, message.id ?? index))))] }));
42
+ }
43
+ //# sourceMappingURL=MessageList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageList.js","sourceRoot":"","sources":["../../src/components/MessageList.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { MessageList } from './MessageList.js';
3
+ declare const meta: Meta<typeof MessageList>;
4
+ export default meta;
5
+ type Story = StoryObj<typeof MessageList>;
6
+ export declare const Default: Story;
7
+ export declare const Empty: Story;
8
+ export declare const EmptyWithCustomState: Story;
9
+ export declare const Streaming: Story;
10
+ export declare const StreamingEmptyAssistant: Story;
11
+ export declare const LongConversation: Story;
12
+ //# sourceMappingURL=MessageList.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MessageList.stories.d.ts","sourceRoot":"","sources":["../../src/components/MessageList.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,WAAW,CAmBlC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,WAAW,CAAC,CAAC;AAuB1C,eAAO,MAAM,OAAO,EAAE,KAIrB,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAInB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,KAUlC,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAavB,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KAQrC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAa9B,CAAC"}