@kitn.ai/ui 0.15.0 → 0.15.1

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 (177) hide show
  1. package/dist/kitn-chat.es.js +1 -1
  2. package/package.json +6 -1
  3. package/src/elements/compiled.css +1 -1
  4. package/src/components/artifact.stories.tsx +0 -138
  5. package/src/components/attachments.stories.tsx +0 -303
  6. package/src/components/attachments.test.tsx +0 -64
  7. package/src/components/chain-of-thought.stories.tsx +0 -211
  8. package/src/components/chat-container.stories.tsx +0 -187
  9. package/src/components/chat-thread.test.tsx +0 -58
  10. package/src/components/checkpoint.stories.tsx +0 -102
  11. package/src/components/code-block.stories.tsx +0 -150
  12. package/src/components/confirm-card.stories.tsx +0 -74
  13. package/src/components/context.stories.tsx +0 -229
  14. package/src/components/context.test.tsx +0 -133
  15. package/src/components/conversation-item.stories.tsx +0 -125
  16. package/src/components/conversation-list.stories.tsx +0 -133
  17. package/src/components/default-input.test.tsx +0 -111
  18. package/src/components/empty.stories.tsx +0 -434
  19. package/src/components/feedback-bar.stories.tsx +0 -138
  20. package/src/components/feedback-bar.test.tsx +0 -103
  21. package/src/components/file-tree.stories.tsx +0 -73
  22. package/src/components/file-upload.stories.tsx +0 -156
  23. package/src/components/image.stories.tsx +0 -89
  24. package/src/components/loader.stories.tsx +0 -181
  25. package/src/components/markdown.stories.tsx +0 -180
  26. package/src/components/message-action-bar.test.tsx +0 -128
  27. package/src/components/message-narrow.stories.tsx +0 -329
  28. package/src/components/message-skills.stories.tsx +0 -211
  29. package/src/components/message.stories.tsx +0 -281
  30. package/src/components/model-switcher.stories.tsx +0 -123
  31. package/src/components/model-switcher.test.tsx +0 -65
  32. package/src/components/prompt-input.stories.tsx +0 -222
  33. package/src/components/prompt-suggestion.stories.tsx +0 -142
  34. package/src/components/reasoning.stories.tsx +0 -140
  35. package/src/components/resizable.test.tsx +0 -226
  36. package/src/components/scroll-button.stories.tsx +0 -100
  37. package/src/components/slash-command.stories.tsx +0 -163
  38. package/src/components/source-list.test.tsx +0 -152
  39. package/src/components/source.stories.tsx +0 -124
  40. package/src/components/tasks-card.stories.tsx +0 -78
  41. package/src/components/text-shimmer.stories.tsx +0 -87
  42. package/src/components/thinking-bar.stories.tsx +0 -87
  43. package/src/components/tool.stories.tsx +0 -153
  44. package/src/components/voice-input.stories.tsx +0 -83
  45. package/src/elements/artifact.stories.tsx +0 -506
  46. package/src/elements/attachments.stories.tsx +0 -190
  47. package/src/elements/card.stories.tsx +0 -113
  48. package/src/elements/cards.stories.tsx +0 -59
  49. package/src/elements/catalog.stories.tsx +0 -491
  50. package/src/elements/chain-of-thought.declarative.test.tsx +0 -153
  51. package/src/elements/chain-of-thought.stories.tsx +0 -113
  52. package/src/elements/chat-scope-picker.stories.tsx +0 -73
  53. package/src/elements/chat-workspace.stories.tsx +0 -288
  54. package/src/elements/chat.stories.tsx +0 -151
  55. package/src/elements/checkpoint.stories.tsx +0 -72
  56. package/src/elements/choice.stories.tsx +0 -240
  57. package/src/elements/code-block.stories.tsx +0 -83
  58. package/src/elements/composed-shell.stories.tsx +0 -316
  59. package/src/elements/confirm-card.stories.tsx +0 -227
  60. package/src/elements/context-meter.stories.tsx +0 -151
  61. package/src/elements/conversation-list.declarative.test.tsx +0 -137
  62. package/src/elements/conversation-list.stories.tsx +0 -243
  63. package/src/elements/embed.stories.tsx +0 -213
  64. package/src/elements/empty.stories.tsx +0 -111
  65. package/src/elements/feedback-bar.stories.tsx +0 -114
  66. package/src/elements/file-tree.stories.tsx +0 -134
  67. package/src/elements/file-upload.stories.tsx +0 -82
  68. package/src/elements/form.stories.tsx +0 -248
  69. package/src/elements/image.stories.tsx +0 -71
  70. package/src/elements/link-preview.stories.tsx +0 -209
  71. package/src/elements/loader.stories.tsx +0 -88
  72. package/src/elements/markdown.stories.tsx +0 -76
  73. package/src/elements/message-skills.declarative.test.tsx +0 -104
  74. package/src/elements/message-skills.stories.tsx +0 -114
  75. package/src/elements/message.stories.tsx +0 -218
  76. package/src/elements/model-switcher.declarative.test.tsx +0 -130
  77. package/src/elements/model-switcher.stories.tsx +0 -198
  78. package/src/elements/popover.stories.tsx +0 -80
  79. package/src/elements/prompt-input-slash-command.test.tsx +0 -128
  80. package/src/elements/prompt-input-stoppable.test.tsx +0 -88
  81. package/src/elements/prompt-input-toolbar-actions.test.tsx +0 -120
  82. package/src/elements/prompt-input.stories.tsx +0 -331
  83. package/src/elements/prompt-suggestions.declarative.test.tsx +0 -164
  84. package/src/elements/prompt-suggestions.stories.tsx +0 -213
  85. package/src/elements/reasoning.stories.tsx +0 -77
  86. package/src/elements/remote.stories.tsx +0 -314
  87. package/src/elements/resizable.stories.tsx +0 -426
  88. package/src/elements/response-stream.stories.tsx +0 -79
  89. package/src/elements/scroll-button.stories.tsx +0 -202
  90. package/src/elements/scroll-button.test.tsx +0 -148
  91. package/src/elements/source-list.stories.tsx +0 -143
  92. package/src/elements/source.stories.tsx +0 -88
  93. package/src/elements/switch.stories.tsx +0 -56
  94. package/src/elements/tasks.stories.tsx +0 -237
  95. package/src/elements/text-shimmer.stories.tsx +0 -64
  96. package/src/elements/thinking-bar.stories.tsx +0 -72
  97. package/src/elements/tool.stories.tsx +0 -89
  98. package/src/elements/voice-input.stories.tsx +0 -88
  99. package/src/primitives/controllable.test.ts +0 -47
  100. package/src/stories/chat-panel-layout.stories.tsx +0 -145
  101. package/src/stories/chat-scene.tsx +0 -571
  102. package/src/stories/checkpoint-restore.stories.tsx +0 -256
  103. package/src/stories/context-usage.stories.tsx +0 -212
  104. package/src/stories/conversation-with-reasoning.stories.tsx +0 -182
  105. package/src/stories/conversation-with-sources.stories.tsx +0 -471
  106. package/src/stories/docs/Accessibility.mdx +0 -119
  107. package/src/stories/docs/ChildElements.mdx +0 -269
  108. package/src/stories/docs/ForAIAgents.mdx +0 -162
  109. package/src/stories/docs/GettingStarted.mdx +0 -65
  110. package/src/stories/docs/Installation.mdx +0 -83
  111. package/src/stories/docs/Introduction.mdx +0 -50
  112. package/src/stories/docs/SolidJsAdvanced.mdx +0 -17
  113. package/src/stories/docs/Theming.mdx +0 -85
  114. package/src/stories/docs/element-controls.ts +0 -121
  115. package/src/stories/docs/frameworks/Angular.mdx +0 -245
  116. package/src/stories/docs/frameworks/Html.mdx +0 -227
  117. package/src/stories/docs/frameworks/Overview.mdx +0 -47
  118. package/src/stories/docs/frameworks/React.mdx +0 -227
  119. package/src/stories/docs/frameworks/Solid.mdx +0 -173
  120. package/src/stories/docs/frameworks/Svelte.mdx +0 -194
  121. package/src/stories/docs/frameworks/Vue.mdx +0 -238
  122. package/src/stories/docs/generative-ui-overview.mdx +0 -186
  123. package/src/stories/docs/recipes/SpeechToText.mdx +0 -7
  124. package/src/stories/docs/recipes/Streaming.mdx +0 -68
  125. package/src/stories/docs/recipes/TextToSpeech.mdx +0 -35
  126. package/src/stories/docs/theme-editor/canvas.tsx +0 -32
  127. package/src/stories/docs/theme-editor/inspector.tsx +0 -66
  128. package/src/stories/docs/theme-editor/presets.test.ts +0 -32
  129. package/src/stories/docs/theme-editor/presets.ts +0 -64
  130. package/src/stories/docs/theme-editor/theme-css.test.ts +0 -19
  131. package/src/stories/docs/theme-editor/theme-css.ts +0 -15
  132. package/src/stories/docs/theme-editor/theme-editor.tsx +0 -146
  133. package/src/stories/docs/theme-tokens.tsx +0 -174
  134. package/src/stories/examples/ChoosingComponents.mdx +0 -106
  135. package/src/stories/examples/sample-data.ts +0 -79
  136. package/src/stories/examples/usage/checkpoint-restore.ts +0 -156
  137. package/src/stories/examples/usage/context-usage.ts +0 -591
  138. package/src/stories/examples/usage/conversation-with-reasoning.ts +0 -224
  139. package/src/stories/examples/usage/conversation-with-sources.ts +0 -589
  140. package/src/stories/examples/usage/empty-state.ts +0 -144
  141. package/src/stories/examples/usage/full-chat-app.ts +0 -277
  142. package/src/stories/examples/usage/index.ts +0 -63
  143. package/src/stories/examples/usage/message-actions.ts +0 -704
  144. package/src/stories/examples/usage/pattern-centered-conversation.ts +0 -446
  145. package/src/stories/examples/usage/pattern-chat-panel-layout.ts +0 -442
  146. package/src/stories/examples/usage/pattern-docked-widget.ts +0 -479
  147. package/src/stories/examples/usage/prompt-input-variants.ts +0 -1356
  148. package/src/stories/examples/usage/streaming-response.ts +0 -687
  149. package/src/stories/examples/usage/types.ts +0 -33
  150. package/src/stories/full-chat.stories.tsx +0 -18
  151. package/src/stories/message-actions.stories.tsx +0 -230
  152. package/src/stories/pattern-centered-conversation.stories.tsx +0 -93
  153. package/src/stories/pattern-docked-widget.stories.tsx +0 -93
  154. package/src/stories/pattern-empty-state.stories.tsx +0 -76
  155. package/src/stories/prompt-input-variants.stories.tsx +0 -456
  156. package/src/stories/streaming-response.stories.tsx +0 -438
  157. package/src/stories/theme-editor.stories.tsx +0 -16
  158. package/src/stories/token-reference.stories.tsx +0 -18
  159. package/src/stories/typography.stories.tsx +0 -78
  160. package/src/ui/avatar.stories.tsx +0 -103
  161. package/src/ui/badge.stories.tsx +0 -86
  162. package/src/ui/button.stories.tsx +0 -145
  163. package/src/ui/collapsible.stories.tsx +0 -69
  164. package/src/ui/dropdown.stories.tsx +0 -59
  165. package/src/ui/hover-card.stories.tsx +0 -77
  166. package/src/ui/hover-card.test.tsx +0 -30
  167. package/src/ui/overlay.stories.tsx +0 -115
  168. package/src/ui/popover.stories.tsx +0 -81
  169. package/src/ui/popover.test.tsx +0 -99
  170. package/src/ui/resizable.stories.tsx +0 -236
  171. package/src/ui/scroll-area.stories.tsx +0 -50
  172. package/src/ui/separator.stories.tsx +0 -81
  173. package/src/ui/skeleton.stories.tsx +0 -337
  174. package/src/ui/switch.stories.tsx +0 -51
  175. package/src/ui/switch.test.tsx +0 -59
  176. package/src/ui/textarea.stories.tsx +0 -76
  177. package/src/ui/tooltip.stories.tsx +0 -74
@@ -1,237 +0,0 @@
1
- import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
- import { createSignal, onMount, type JSX } from 'solid-js';
3
- import './tasks';
4
- import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
- import type { TasksCardData } from '../components/tasks-card';
6
- import type { CardEvent } from '../primitives/card-contract';
7
-
8
- declare module 'solid-js' {
9
- // eslint-disable-next-line @typescript-eslint/no-namespace
10
- namespace JSX {
11
- interface IntrinsicElements {
12
- 'kai-tasks': JSX.HTMLAttributes<HTMLElement> & {
13
- heading?: string;
14
- 'card-id'?: string;
15
- ref?: (el: HTMLElement) => void;
16
- };
17
- }
18
- }
19
- }
20
-
21
- type TasksEl = HTMLElement & { data?: TasksCardData; resolution?: Record<string, unknown> };
22
-
23
- function Frame(props: { children: JSX.Element }) {
24
- return <div style={{ 'max-width': '460px' }}>{props.children}</div>;
25
- }
26
-
27
- /** Mounts a <kai-tasks>, sets `.data`, logs the emitted CardEvent under the render. */
28
- function TasksDemo(props: { def: TasksCardData; cardId: string; heading?: string }) {
29
- const [log, setLog] = createSignal<CardEvent[]>([]);
30
- let el: TasksEl | undefined;
31
- onMount(() => {
32
- if (!el) return;
33
- el.data = props.def;
34
- el.addEventListener('kai-card', (e) => {
35
- const detail = (e as CustomEvent<CardEvent>).detail;
36
- setLog((prev) => [...prev, detail]);
37
- });
38
- });
39
- return (
40
- <Frame>
41
- <div style={{ display: 'flex', 'flex-direction': 'column', gap: '12px' }}>
42
- <kai-tasks ref={(e) => (el = e as TasksEl)} card-id={props.cardId} heading={props.heading} />
43
- <pre
44
- style={{
45
- margin: 0,
46
- 'max-height': '180px',
47
- overflow: 'auto',
48
- background: 'var(--color-muted, #f4f4f5)',
49
- 'border-radius': '8px',
50
- padding: '8px',
51
- 'font-size': '12px',
52
- }}
53
- >
54
- {log().length === 0 ? '// emitted CardEvents appear here' : JSON.stringify(log(), null, 2)}
55
- </pre>
56
- </div>
57
- </Frame>
58
- );
59
- }
60
-
61
- const PLAN: TasksCardData = {
62
- mode: 'select',
63
- selectAll: true,
64
- confirmLabel: 'Run selected',
65
- tasks: [
66
- { id: 'lint', label: 'Run linter', checked: true },
67
- { id: 'test', label: 'Run unit tests', checked: true },
68
- { id: 'build', label: 'Build production bundle' },
69
- { id: 'deploy', label: 'Deploy to staging', description: 'Reversible; staging only' },
70
- ],
71
- };
72
-
73
- const REQUIRE_ONE: TasksCardData = {
74
- confirmLabel: 'Apply',
75
- allowEmpty: false,
76
- tasks: [
77
- { id: 'cache', label: 'Clear the CDN cache' },
78
- { id: 'reindex', label: 'Rebuild the search index' },
79
- { id: 'restart', label: 'Restart the workers' },
80
- ],
81
- };
82
-
83
- const BOUNDED: TasksCardData = {
84
- heading: 'Pick up to 2 reviewers',
85
- confirmLabel: 'Request review',
86
- min: 1,
87
- max: 2,
88
- tasks: [
89
- { id: 'ana', label: 'Ana' },
90
- { id: 'ben', label: 'Ben' },
91
- { id: 'cat', label: 'Cat' },
92
- { id: 'dan', label: 'Dan' },
93
- ],
94
- };
95
-
96
- const WITH_DESCRIPTIONS: TasksCardData = {
97
- selectAll: true,
98
- confirmLabel: 'Run cleanup',
99
- tasks: [
100
- { id: 'tmp', label: 'Delete temp files', description: 'Frees ~2.1 GB; safe to remove' },
101
- { id: 'logs', label: 'Rotate logs', description: 'Archives logs older than 30 days' },
102
- { id: 'orphans', label: 'Prune orphaned blobs', description: 'Unreferenced uploads only' },
103
- ],
104
- };
105
-
106
- const HEADING_MAP: Record<string, string | undefined> = {
107
- 'card-plan': 'Approve the plan steps',
108
- 'card-require': 'Choose maintenance steps',
109
- 'card-bounded': undefined,
110
- 'card-desc': 'Storage cleanup',
111
- };
112
-
113
- const HTML_SNIPPET = (def: TasksCardData, cardId: string) => {
114
- const heading = HEADING_MAP[cardId];
115
- return `<kai-tasks${heading ? ` heading="${heading}"` : ''}></kai-tasks>
116
- <script type="module">
117
- import '@kitn.ai/ui/elements'; // registers the custom elements
118
-
119
- const el = document.querySelector('kai-tasks');
120
- // \`data\` is the CardEnvelope.data (set as a property).
121
- el.data = ${JSON.stringify(def, null, 2)};
122
-
123
- // Toggling rows is local; only CONFIRM emits — a single bubbling \`kai-card\` event.
124
- el.addEventListener('kai-card', (e) => {
125
- const ev = e.detail; // { kind:'submit', cardId, data:{ selected } } | ...
126
- if (ev.kind === 'submit') console.log('selected', ev.data.selected);
127
- });
128
- </script>`;
129
- };
130
-
131
- const meta = {
132
- title: 'Generative UI/Cards/kai-tasks',
133
- tags: ['autodocs'],
134
- argTypes: argTypesFor('kai-tasks'),
135
- parameters: {
136
- layout: 'padded',
137
- docs: {
138
- description: specDescription('kai-tasks', [
139
- "`<kai-tasks>` is a **selectable** task/plan list (set via the `data` **property**): checkbox rows + an optional select-all + a confirm button. The user picks a subset, confirms, and the card emits the Card contract's **`submit`** verb up a bubbling **`kai-card`** CustomEvent of `{ kind: 'submit', cardId, data: { selected } }` — the checked ids in **input order**. Toggling rows is local UI state; **only the final confirm emits** (the wire stays quiet, the result atomic).",
140
- '**Anatomy:** `<kai-card>` chrome (optional heading) → **select-all row** (shown when `selectAll:true` and ≥2 toggleable tasks; indeterminate state when partially checked) → **task rows** (one per `tasks[]` item: checkbox + label + optional description; disabled when `max` reached or `task.disabled`) → **card footer** (selected-count label + confirm `<Button>`, disabled until gating passes; replaced by a read-only resolved summary after submission).',
141
- '**Gating:** confirm is enabled when `selectedCount >= (min ?? (allowEmpty ? 0 : 1))` and `<= (max ?? ∞)`. Select-all checks every toggleable (non-`disabled`) row and shows an **indeterminate** (`aria-checked="mixed"`) state when only some are checked. When `max` is reached, unchecked rows become non-toggleable. v1 is **select/approve only** (a future `mode:\'progress\'` is reserved in the schema).',
142
- '**Events** (all frozen Card-contract verbs): `ready` on mount, `submit` on confirm, `error` for a malformed definition (renders the inline `kai-card` error). It **never invents events**. The same shapes flow over the remote iframe transport unchanged.',
143
- ]),
144
- },
145
- },
146
- } satisfies Meta;
147
-
148
- export default meta;
149
- type Story = StoryObj;
150
-
151
- /** Select a plan (select-all on, a few pre-checked). */
152
- export const SelectAPlan: Story = {
153
- render: () => <TasksDemo def={PLAN} cardId="card-plan" heading="Approve the plan steps" />,
154
- parameters: { docs: { source: { code: HTML_SNIPPET(PLAN, 'card-plan'), language: 'html' } } },
155
- };
156
-
157
- /** Require at least one (`allowEmpty:false`, the default) — confirm stays disabled until a row is checked. */
158
- export const RequireAtLeastOne: Story = {
159
- render: () => <TasksDemo def={REQUIRE_ONE} cardId="card-require" heading="Choose maintenance steps" />,
160
- parameters: { docs: { source: { code: HTML_SNIPPET(REQUIRE_ONE, 'card-require'), language: 'html' } } },
161
- };
162
-
163
- /** Bounded (`min`/`max`) — confirm gating + max-reached row disabling. */
164
- export const Bounded: Story = {
165
- render: () => <TasksDemo def={BOUNDED} cardId="card-bounded" />,
166
- parameters: { docs: { source: { code: HTML_SNIPPET(BOUNDED, 'card-bounded'), language: 'html' } } },
167
- };
168
-
169
- /** Rows with secondary descriptions (linked via aria-describedby). */
170
- export const WithDescriptions: Story = {
171
- render: () => <TasksDemo def={WITH_DESCRIPTIONS} cardId="card-desc" heading="Storage cleanup" />,
172
- parameters: { docs: { source: { code: HTML_SNIPPET(WITH_DESCRIPTIONS, 'card-desc'), language: 'html' } } },
173
- };
174
-
175
- const RESOLVED_TASKS: TasksCardData = {
176
- confirmLabel: 'Export',
177
- tasks: [
178
- { id: 'sum', label: 'Executive summary' },
179
- { id: 'data', label: 'Raw data' },
180
- { id: 'charts', label: 'Charts' },
181
- ],
182
- };
183
-
184
- /** The card after the user confirmed their selection — shows "Selected 2 of 3", no checkboxes or button. */
185
- export const Resolved: Story = {
186
- name: 'Resolved (read-only)',
187
- render: () => {
188
- let el: TasksEl | undefined;
189
- onMount(() => {
190
- if (!el) return;
191
- el.data = RESOLVED_TASKS;
192
- el.resolution = { kind: 'submit', data: { selected: ['sum', 'charts'] } };
193
- });
194
- return (
195
- <Frame>
196
- <kai-tasks ref={(e) => (el = e as TasksEl)} card-id="card-resolved-tasks" heading="Export report" />
197
- </Frame>
198
- );
199
- },
200
- parameters: {
201
- docs: {
202
- source: {
203
- code: `<kai-tasks heading="Export report"></kai-tasks>
204
- <script type="module">
205
- import '@kitn.ai/ui/elements';
206
- const el = document.querySelector('kai-tasks');
207
- el.data = ${JSON.stringify(RESOLVED_TASKS, null, 2)};
208
- // Setting .resolution renders the chromed read-only view — no checkboxes or confirm button.
209
- el.resolution = { kind: 'submit', data: { selected: ['sum', 'charts'] } };
210
- </script>`,
211
- language: 'html',
212
- },
213
- },
214
- },
215
- };
216
-
217
- /** A malformed `data` (empty `tasks`) → the inline error state + an `error` event. */
218
- export const ErrorState: Story = {
219
- render: () => <TasksDemo def={{ tasks: [] } as unknown as TasksCardData} cardId="card-bad" />,
220
- parameters: {
221
- docs: {
222
- source: {
223
- code: `<kai-tasks></kai-tasks>
224
- <script type="module">
225
- import '@kitn.ai/ui/elements';
226
- const el = document.querySelector('kai-tasks');
227
- // No tasks → inline error state + an \`error\` event.
228
- el.data = { tasks: [] };
229
- el.addEventListener('kai-card', (e) => {
230
- if (e.detail.kind === 'error') console.warn('tasks error:', e.detail.message);
231
- });
232
- </script>`,
233
- language: 'html',
234
- },
235
- },
236
- },
237
- };
@@ -1,64 +0,0 @@
1
- import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
- import './register'; // side effect: registers the custom elements
3
- import { argTypesFor, specDescription } from '../stories/docs/element-controls';
4
-
5
- // The web components are custom DOM elements, so declare the tags for JSX.
6
- declare module 'solid-js' {
7
- // eslint-disable-next-line @typescript-eslint/no-namespace
8
- namespace JSX {
9
- interface IntrinsicElements {
10
- 'kai-text-shimmer': JSX.HTMLAttributes<HTMLElement> & {
11
- text?: string;
12
- duration?: number;
13
- spread?: number;
14
- };
15
- }
16
- }
17
- }
18
-
19
- const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
20
- <kai-text-shimmer text="Thinking…" duration="3" spread="20"></kai-text-shimmer>
21
-
22
- <script type="module">
23
- import '@kitn.ai/ui/elements'; // registers the custom elements
24
- </script>`;
25
-
26
- const meta = {
27
- title: 'Components/TextShimmer',
28
- tags: ['autodocs'],
29
- argTypes: argTypesFor('kai-text-shimmer'),
30
- parameters: {
31
- layout: 'fullscreen',
32
- docs: {
33
- description: specDescription('kai-text-shimmer', [
34
- '`<kai-text-shimmer>` is the framework-agnostic **web component** for animated shimmering text — a gradient sweep across a label, isolated in **Shadow DOM**.',
35
- '**When to use:** signalling a quiet, in-progress state ("Thinking…", "Generating…") inline. In SolidJS, use the `TextShimmer` primitive.',
36
- '**Placement:** inline within a status row, toolbar label, or above a streaming content area — it renders as an inline `span` so it flows naturally alongside other text or icons.',
37
- "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the `text` attribute, and tune `duration` (seconds) and `spread` (gradient width, 5–45).",
38
- 'See the **Code** tab for HTML usage.',
39
- ]),
40
- },
41
- },
42
- } satisfies Meta;
43
-
44
- export default meta;
45
- type Story = StoryObj;
46
-
47
- /** Default shimmer. */
48
- export const Default: Story = {
49
- render: () => (
50
- <div style={{ padding: '24px', 'font-size': '18px' }}>
51
- <kai-text-shimmer text="Thinking…" />
52
- </div>
53
- ),
54
- parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
55
- };
56
-
57
- /** Faster sweep with a wider gradient spread. */
58
- export const Tuned: Story = {
59
- render: () => (
60
- <div style={{ padding: '24px', 'font-size': '18px' }}>
61
- <kai-text-shimmer text="Generating response…" duration={2} spread={35} />
62
- </div>
63
- ),
64
- };
@@ -1,72 +0,0 @@
1
- import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
- import './register'; // side effect: registers the custom elements
3
- import { argTypesFor, specDescription } from '../stories/docs/element-controls';
4
-
5
- // The web components are custom DOM elements, so declare the tags for JSX.
6
- declare module 'solid-js' {
7
- // eslint-disable-next-line @typescript-eslint/no-namespace
8
- namespace JSX {
9
- interface IntrinsicElements {
10
- 'kai-thinking-bar': JSX.HTMLAttributes<HTMLElement> & {
11
- text?: string;
12
- stoppable?: boolean | string;
13
- 'stop-label'?: string;
14
- 'on:kai-stop'?: (e: CustomEvent) => void;
15
- };
16
- }
17
- }
18
- }
19
-
20
- const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
21
- <kai-thinking-bar text="Thinking" stoppable stop-label="Answer now"></kai-thinking-bar>
22
-
23
- <script type="module">
24
- import '@kitn.ai/ui/elements'; // registers the custom elements
25
-
26
- document.querySelector('kai-thinking-bar')
27
- .addEventListener('kai-stop', () => console.log('user asked to stop'));
28
- </script>`;
29
-
30
- const meta = {
31
- title: 'Components/ThinkingBar',
32
- tags: ['autodocs'],
33
- argTypes: argTypesFor('kai-thinking-bar'),
34
- parameters: {
35
- layout: 'fullscreen',
36
- docs: {
37
- description: specDescription('kai-thinking-bar', [
38
- '`<kai-thinking-bar>` is the framework-agnostic **web component** for an animated "thinking" indicator with an optional stop affordance — a pure leaf element isolated in **Shadow DOM**. (`<kai-chat>` does not surface this; compose it yourself.)',
39
- '**When to use:** showing that the assistant is reasoning, optionally letting the user interrupt with "Answer now". In SolidJS, use the `ThinkingBar` primitive.',
40
- "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the `text`/`stop-label` attributes, add the `stoppable` flag to show the stop button, and listen for the `kai-stop` **CustomEvent**.",
41
- 'See the **Code** tab for HTML usage.',
42
- ]),
43
- },
44
- },
45
- } satisfies Meta;
46
-
47
- export default meta;
48
- type Story = StoryObj;
49
-
50
- /** A plain thinking indicator. */
51
- export const Default: Story = {
52
- render: () => (
53
- <div style={{ padding: '24px' }}>
54
- <kai-thinking-bar text="Thinking" />
55
- </div>
56
- ),
57
- parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
58
- };
59
-
60
- /** Stoppable — shows an "Answer now" affordance that fires a `stop` event. */
61
- export const Stoppable: Story = {
62
- render: () => (
63
- <div style={{ padding: '24px' }}>
64
- <kai-thinking-bar
65
- text="Reasoning"
66
- stoppable
67
- stop-label="Answer now"
68
- on:kai-stop={() => console.log('stop')}
69
- />
70
- </div>
71
- ),
72
- };
@@ -1,89 +0,0 @@
1
- import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
- import { onMount } from 'solid-js';
3
- import './register'; // side effect: registers the custom elements
4
- import type { ToolPart } from '../components/tool';
5
- import { argTypesFor, specDescription } from '../stories/docs/element-controls';
6
-
7
- // The web components are custom DOM elements, so declare the tags for JSX.
8
- declare module 'solid-js' {
9
- // eslint-disable-next-line @typescript-eslint/no-namespace
10
- namespace JSX {
11
- interface IntrinsicElements {
12
- 'kai-tool': JSX.HTMLAttributes<HTMLElement>;
13
- }
14
- }
15
- }
16
-
17
- const completedTool: ToolPart = {
18
- type: 'database_query',
19
- state: 'output-available',
20
- input: { table: 'users', limit: 10 },
21
- output: { rows: 10, ms: 42 },
22
- };
23
-
24
- const runningTool: ToolPart = {
25
- type: 'search',
26
- state: 'input-available',
27
- input: { query: 'kitn docs' },
28
- };
29
-
30
- /** Render the actual `<kai-tool>` custom element with a `tool` property. */
31
- function ToolElement(props: { tool: ToolPart; open?: boolean }) {
32
- let el: (HTMLElement & { tool?: ToolPart; open?: boolean }) | undefined;
33
- onMount(() => {
34
- if (el) {
35
- el.tool = props.tool;
36
- if (props.open) el.open = true;
37
- }
38
- });
39
- return (
40
- <kai-tool ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} />
41
- );
42
- }
43
-
44
- const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
45
- <kai-tool id="tool" open></kai-tool>
46
-
47
- <script type="module">
48
- import '@kitn.ai/ui/elements'; // registers the custom elements
49
-
50
- const tool = document.getElementById('tool');
51
- tool.tool = {
52
- type: 'database_query',
53
- state: 'output-available',
54
- input: { table: 'users', limit: 10 },
55
- output: { rows: 10, ms: 42 },
56
- };
57
- </script>`;
58
-
59
- const meta = {
60
- title: 'Components/Tool',
61
- tags: ['autodocs'],
62
- argTypes: argTypesFor('kai-tool'),
63
- parameters: {
64
- layout: 'fullscreen',
65
- docs: {
66
- description: specDescription('kai-tool', [
67
- '`<kai-tool>` is the framework-agnostic **web component** for a single tool-call panel — a collapsible input/output inspector with a state badge — isolated in **Shadow DOM**.',
68
- '**When to use:** rendering an agent/tool-call trace in a non-Solid app. In SolidJS, use the `Tool` primitive directly.',
69
- '**Placement:** inline inside an assistant message row, one element per tool call, below the text content; renders as a block sized to its content.',
70
- "**How to use:** register once with `import '@kitn.ai/ui/elements'`, set the call via the `tool` **property** (`el.tool = {...}`), and add the `open` flag to start it expanded.",
71
- 'See the **Code** tab for HTML usage.',
72
- ]),
73
- },
74
- },
75
- } satisfies Meta;
76
-
77
- export default meta;
78
- type Story = StoryObj;
79
-
80
- /** A completed call with input and output, started expanded. */
81
- export const Completed: Story = {
82
- render: () => <ToolElement tool={completedTool} open />,
83
- parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
84
- };
85
-
86
- /** A call still awaiting output (collapsed). */
87
- export const Running: Story = {
88
- render: () => <ToolElement tool={runningTool} />,
89
- };
@@ -1,88 +0,0 @@
1
- import type { Meta, StoryObj } from 'storybook-solidjs-vite';
2
- import { onMount } from 'solid-js';
3
- import './register'; // side effect: registers the custom elements
4
- import { argTypesFor, specDescription } from '../stories/docs/element-controls';
5
-
6
- // The web components are custom DOM elements, so declare the tags for JSX.
7
- declare module 'solid-js' {
8
- // eslint-disable-next-line @typescript-eslint/no-namespace
9
- namespace JSX {
10
- interface IntrinsicElements {
11
- 'kai-voice-input': JSX.HTMLAttributes<HTMLElement> & {
12
- disabled?: boolean | string;
13
- };
14
- }
15
- }
16
- }
17
-
18
- /** Render `<kai-voice-input>` with a stub `transcribe` function-property. */
19
- function VoiceElement(props: { disabled?: boolean }) {
20
- let el: (HTMLElement & { transcribe?: (audio: Blob) => Promise<string> }) | undefined;
21
- onMount(() => {
22
- if (!el) return;
23
- // transcribe MUST be set as a JS property — a value-returning callback
24
- // can't be modelled as an attribute.
25
- el.transcribe = async () => {
26
- await new Promise((r) => setTimeout(r, 400));
27
- return 'transcribed text';
28
- };
29
- el.addEventListener('kai-transcription', (e) => {
30
- const ev = e as CustomEvent<{ text: string }>;
31
- console.log('kai-transcription', ev.detail.text);
32
- });
33
- });
34
- return (
35
- <kai-voice-input
36
- ref={(e) => (el = e as HTMLElement)}
37
- disabled={props.disabled ? true : undefined}
38
- style={{ display: 'inline-block', padding: '40px' }}
39
- />
40
- );
41
- }
42
-
43
- const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
44
- <kai-voice-input id="voice"></kai-voice-input>
45
-
46
- <script type="module">
47
- import '@kitn.ai/ui/elements'; // registers the custom elements
48
-
49
- const voice = document.getElementById('voice');
50
- // transcribe is a FUNCTION property — your async transcriber
51
- voice.transcribe = async (blob) => {
52
- const text = await myTranscriptionApi(blob);
53
- return text;
54
- };
55
- voice.addEventListener('kai-transcription', (e) => console.log(e.detail.text));
56
- </script>`;
57
-
58
- const meta = {
59
- title: 'Components/VoiceInput',
60
- tags: ['autodocs'],
61
- argTypes: argTypesFor('kai-voice-input'),
62
- parameters: {
63
- layout: 'fullscreen',
64
- docs: {
65
- description: specDescription('kai-voice-input', [
66
- '`<kai-voice-input>` is the framework-agnostic **web component** for a mic button that records and transcribes audio — isolated in **Shadow DOM**. It is the canonical **function-property** element.',
67
- '**When to use:** adding voice dictation to an input in a non-Solid app. In SolidJS, use the `VoiceInput` primitive.',
68
- '**Placement:** inline beside the prompt input field or as an icon button in the composer toolbar; renders as an inline-block sized to the mic button.',
69
- "**How to use:** register once with `import '@kitn.ai/ui/elements'`, then set the `transcribe` **function property** (`el.transcribe = async blob => '...'`) — a value-returning callback can't be modelled as an event. It also emits `kai-audio-captured` (raw blob) and `kai-transcription` (text) **CustomEvents**.",
70
- 'See the **Code** tab for HTML usage.',
71
- ]),
72
- },
73
- },
74
- } satisfies Meta;
75
-
76
- export default meta;
77
- type Story = StoryObj;
78
-
79
- /** A working mic button wired to a stub transcriber. */
80
- export const Default: Story = {
81
- render: () => <VoiceElement />,
82
- parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } },
83
- };
84
-
85
- /** A disabled mic button (non-interactive). */
86
- export const Disabled: Story = {
87
- render: () => <VoiceElement disabled />,
88
- };
@@ -1,47 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createRoot, createSignal } from 'solid-js';
3
- import { createControllableSignal } from './controllable';
4
-
5
- describe('createControllableSignal', () => {
6
- it('uses the initial value when uncontrolled (controlled accessor is undefined)', () => {
7
- createRoot((dispose) => {
8
- const [value] = createControllableSignal(() => undefined, true);
9
- expect(value()).toBe(true);
10
- dispose();
11
- });
12
- });
13
-
14
- it('updates via the setter when uncontrolled', () => {
15
- createRoot((dispose) => {
16
- const [value, setValue] = createControllableSignal(() => undefined, false);
17
- setValue(true);
18
- expect(value()).toBe(true);
19
- dispose();
20
- });
21
- });
22
-
23
- it('reflects the controlled value and ignores the internal setter while controlled', () => {
24
- createRoot((dispose) => {
25
- const [controlled, setControlled] = createSignal<boolean | undefined>(false);
26
- const [value, setValue] = createControllableSignal(controlled, false);
27
- expect(value()).toBe(false);
28
- setValue(true); // masked while controlled
29
- expect(value()).toBe(false);
30
- setControlled(true); // the controlling value wins
31
- expect(value()).toBe(true);
32
- dispose();
33
- });
34
- });
35
-
36
- it('falls back to the internal value when control is released (becomes undefined)', () => {
37
- createRoot((dispose) => {
38
- const [controlled, setControlled] = createSignal<boolean | undefined>(true);
39
- const [value, setValue] = createControllableSignal(controlled, false);
40
- expect(value()).toBe(true);
41
- setValue(true); // sets internal (masked now)
42
- setControlled(undefined); // uncontrolled → internal shows
43
- expect(value()).toBe(true);
44
- dispose();
45
- });
46
- });
47
- });