@kitnai/chat 0.7.0 → 0.8.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.
- package/README.md +9 -9
- package/dist/custom-elements.json +1626 -883
- package/dist/kitn-chat.es.js +36 -36
- package/dist/llms/llms-full.txt +303 -142
- package/dist/llms/llms.txt +18 -18
- package/dist/schemas/card-envelope.schema.json +14 -0
- package/dist/schemas/card-event.schema.json +12 -0
- package/dist/schemas/confirm.schema.json +65 -0
- package/dist/schemas/embed.schema.json +65 -0
- package/dist/schemas/form.result.schema.json +7 -0
- package/dist/schemas/form.schema.json +33 -0
- package/dist/schemas/link.schema.json +56 -0
- package/dist/schemas/task-list.result.schema.json +16 -0
- package/dist/schemas/task-list.schema.json +78 -0
- package/dist/theme.tokens.css +65 -65
- package/dist/tsx-B8rCNbgL.js +1 -0
- package/dist/typescript-RycA9KXf.js +1 -0
- package/frameworks/react/index.tsx +356 -189
- package/frameworks/react/runtime.tsx +2 -2
- package/llms-full.txt +303 -142
- package/llms.txt +18 -18
- package/package.json +5 -2
- package/src/components/artifact.stories.tsx +138 -0
- package/src/components/artifact.tsx +581 -0
- package/src/components/attachments.stories.tsx +7 -8
- package/src/components/attachments.tsx +2 -2
- package/src/components/card.tsx +110 -0
- package/src/components/chain-of-thought.stories.tsx +7 -8
- package/src/components/chat-container.stories.tsx +7 -8
- package/src/components/chat-container.tsx +4 -0
- package/src/components/checkpoint.stories.tsx +7 -8
- package/src/components/checkpoint.tsx +3 -0
- package/src/components/code-block.stories.tsx +8 -9
- package/src/components/code-block.tsx +5 -2
- package/src/components/component-meta.json +3419 -0
- package/src/components/confirm-card.stories.tsx +74 -0
- package/src/components/confirm-card.tsx +299 -0
- package/src/components/context.stories.tsx +7 -8
- package/src/components/conversation-item.stories.tsx +7 -8
- package/src/components/conversation-item.tsx +2 -2
- package/src/components/conversation-list.stories.tsx +7 -8
- package/src/components/conversation-list.tsx +1 -1
- package/src/components/embed.tsx +196 -0
- package/src/components/empty.stories.tsx +8 -9
- package/src/components/feedback-bar.stories.tsx +7 -8
- package/src/components/file-tree.stories.tsx +73 -0
- package/src/components/file-tree.tsx +383 -0
- package/src/components/file-upload.stories.tsx +7 -8
- package/src/components/form-widgets.tsx +461 -0
- package/src/components/form.tsx +796 -0
- package/src/components/image.stories.tsx +7 -8
- package/src/components/link-card.tsx +194 -0
- package/src/components/loader.stories.tsx +7 -8
- package/src/components/markdown.stories.tsx +7 -8
- package/src/components/message-narrow.stories.tsx +12 -13
- package/src/components/message-skills.stories.tsx +16 -17
- package/src/components/message.stories.tsx +17 -18
- package/src/components/model-switcher.stories.tsx +7 -8
- package/src/components/prompt-input.stories.tsx +8 -9
- package/src/components/prompt-suggestion.stories.tsx +7 -8
- package/src/components/prompt-suggestion.tsx +3 -3
- package/src/components/reasoning.stories.tsx +7 -8
- package/src/components/scroll-button.stories.tsx +7 -8
- package/src/components/slash-command.stories.tsx +8 -9
- package/src/components/slash-command.tsx +2 -2
- package/src/components/source.stories.tsx +7 -8
- package/src/components/source.tsx +1 -1
- package/src/components/task-list-card.stories.tsx +78 -0
- package/src/components/task-list-card.tsx +388 -0
- package/src/components/text-shimmer.stories.tsx +7 -8
- package/src/components/thinking-bar.stories.tsx +7 -8
- package/src/components/tool.stories.tsx +7 -8
- package/src/components/tool.tsx +2 -2
- package/src/components/voice-input.stories.tsx +7 -8
- package/src/elements/artifact.stories.tsx +291 -0
- package/src/elements/artifact.tsx +72 -0
- package/src/elements/{kitn-attachments.stories.tsx → attachments.stories.tsx} +11 -20
- package/src/elements/attachments.tsx +4 -4
- package/src/elements/card.stories.tsx +118 -0
- package/src/elements/card.tsx +40 -0
- package/src/elements/catalog.stories.tsx +491 -0
- package/src/elements/{kitn-chain-of-thought.stories.tsx → chain-of-thought.stories.tsx} +13 -22
- package/src/elements/chain-of-thought.tsx +3 -3
- package/src/elements/{kitn-chat-scope-picker.stories.tsx → chat-scope-picker.stories.tsx} +10 -19
- package/src/elements/chat-scope-picker.tsx +4 -4
- package/src/elements/{kitn-chat-workspace.stories.tsx → chat-workspace.stories.tsx} +15 -23
- package/src/elements/chat-workspace.tsx +2 -2
- package/src/elements/{kitn-chat.stories.tsx → chat.stories.tsx} +12 -20
- package/src/elements/chat.tsx +2 -2
- package/src/elements/{kitn-checkpoint.stories.tsx → checkpoint.stories.tsx} +11 -20
- package/src/elements/checkpoint.tsx +8 -4
- package/src/elements/{kitn-code-block.stories.tsx → code-block.stories.tsx} +10 -19
- package/src/elements/code-block.tsx +3 -3
- package/src/elements/compiled.css +1 -1
- package/src/elements/composed-shell.stories.tsx +316 -0
- package/src/elements/confirm-card.stories.tsx +186 -0
- package/src/elements/confirm-card.tsx +45 -0
- package/src/elements/{kitn-context-meter.stories.tsx → context-meter.stories.tsx} +10 -19
- package/src/elements/context-meter.tsx +3 -3
- package/src/elements/{kitn-conversation-list.stories.tsx → conversation-list.stories.tsx} +12 -20
- package/src/elements/conversation-list.tsx +2 -2
- package/src/elements/css.ts +1 -1
- package/src/elements/define.tsx +10 -10
- package/src/elements/element-meta.json +1379 -733
- package/src/elements/element-types.d.ts +251 -125
- package/src/elements/embed.stories.tsx +197 -0
- package/src/elements/embed.tsx +35 -0
- package/src/elements/{kitn-empty.stories.tsx → empty.stories.tsx} +12 -21
- package/src/elements/empty.tsx +3 -3
- package/src/elements/{kitn-feedback-bar.stories.tsx → feedback-bar.stories.tsx} +11 -20
- package/src/elements/feedback-bar.tsx +4 -4
- package/src/elements/file-tree.stories.tsx +133 -0
- package/src/elements/file-tree.tsx +52 -0
- package/src/elements/{kitn-file-upload.stories.tsx → file-upload.stories.tsx} +12 -21
- package/src/elements/file-upload.tsx +4 -4
- package/src/elements/form.stories.tsx +204 -0
- package/src/elements/form.tsx +37 -0
- package/src/elements/{kitn-image.stories.tsx → image.stories.tsx} +10 -19
- package/src/elements/image.tsx +3 -3
- package/src/elements/link-card.stories.tsx +193 -0
- package/src/elements/link-card.tsx +34 -0
- package/src/elements/{kitn-loader.stories.tsx → loader.stories.tsx} +11 -20
- package/src/elements/loader.tsx +3 -3
- package/src/elements/{kitn-markdown.stories.tsx → markdown.stories.tsx} +10 -19
- package/src/elements/markdown.tsx +3 -3
- package/src/elements/{kitn-message-skills.stories.tsx → message-skills.stories.tsx} +10 -19
- package/src/elements/message-skills.tsx +3 -3
- package/src/elements/{kitn-message.stories.tsx → message.stories.tsx} +12 -21
- package/src/elements/message.tsx +5 -5
- package/src/elements/{kitn-model-switcher.stories.tsx → model-switcher.stories.tsx} +10 -19
- package/src/elements/model-switcher.tsx +5 -5
- package/src/elements/{kitn-prompt-input.stories.tsx → prompt-input.stories.tsx} +14 -22
- package/src/elements/prompt-input.tsx +3 -3
- package/src/elements/{kitn-prompt-suggestions.stories.tsx → prompt-suggestions.stories.tsx} +13 -22
- package/src/elements/prompt-suggestions.tsx +4 -4
- package/src/elements/{kitn-reasoning.stories.tsx → reasoning.stories.tsx} +10 -19
- package/src/elements/reasoning.tsx +4 -4
- package/src/elements/register.ts +11 -1
- package/src/elements/resizable.stories.tsx +200 -0
- package/src/elements/resizable.tsx +264 -0
- package/src/elements/{kitn-response-stream.stories.tsx → response-stream.stories.tsx} +10 -19
- package/src/elements/response-stream.tsx +4 -4
- package/src/elements/{kitn-source-list.stories.tsx → source-list.stories.tsx} +11 -20
- package/src/elements/{kitn-source.stories.tsx → source.stories.tsx} +12 -21
- package/src/elements/source.tsx +5 -5
- package/src/elements/styles.css +140 -1
- package/src/elements/task-list-card.stories.tsx +194 -0
- package/src/elements/task-list-card.tsx +40 -0
- package/src/elements/{kitn-text-shimmer.stories.tsx → text-shimmer.stories.tsx} +10 -19
- package/src/elements/text-shimmer.tsx +3 -3
- package/src/elements/{kitn-thinking-bar.stories.tsx → thinking-bar.stories.tsx} +11 -20
- package/src/elements/thinking-bar.tsx +5 -5
- package/src/elements/{kitn-tool.stories.tsx → tool.stories.tsx} +10 -19
- package/src/elements/tool.tsx +3 -3
- package/src/elements/{kitn-voice-input.stories.tsx → voice-input.stories.tsx} +10 -19
- package/src/elements/voice-input.tsx +4 -4
- package/src/index.ts +94 -2
- package/src/primitives/card-contract.ts +60 -0
- package/src/primitives/card-host.tsx +35 -0
- package/src/primitives/card-routing.ts +79 -0
- package/src/primitives/card-schemas/card-envelope.schema.json +14 -0
- package/src/primitives/card-schemas/card-event.schema.json +12 -0
- package/src/primitives/card-schemas/confirm.schema.json +65 -0
- package/src/primitives/card-schemas/embed.schema.json +65 -0
- package/src/primitives/card-schemas/form.result.schema.json +7 -0
- package/src/primitives/card-schemas/form.schema.json +33 -0
- package/src/primitives/card-schemas/link.schema.json +56 -0
- package/src/primitives/card-schemas/task-list.result.schema.json +16 -0
- package/src/primitives/card-schemas/task-list.schema.json +78 -0
- package/src/primitives/card-validate.ts +95 -0
- package/src/primitives/embed-providers.ts +254 -0
- package/src/primitives/highlighter.ts +4 -0
- package/src/primitives/link-preview.ts +87 -0
- package/src/primitives/pdf-preview.ts +121 -0
- package/src/stories/chat-panel-layout.stories.tsx +2 -1
- package/src/stories/chat-scene.tsx +22 -21
- package/src/stories/checkpoint-restore.stories.tsx +10 -10
- package/src/stories/conversation-with-reasoning.stories.tsx +4 -4
- package/src/stories/conversation-with-sources.stories.tsx +7 -7
- package/src/stories/docs/Accessibility.mdx +2 -2
- package/src/stories/docs/ForAIAgents.mdx +3 -3
- package/src/stories/docs/GettingStarted.mdx +2 -2
- package/src/stories/docs/Installation.mdx +2 -2
- package/src/stories/docs/Integrations.mdx +29 -29
- package/src/stories/docs/Introduction.mdx +3 -3
- package/src/stories/docs/Theming.mdx +2 -2
- package/src/stories/docs/element-controls.ts +32 -0
- package/src/stories/docs/theme-editor/theme-editor.tsx +1 -0
- package/src/stories/examples/ChoosingComponents.mdx +94 -0
- package/src/stories/examples/sample-data.ts +79 -0
- package/src/stories/message-actions.stories.tsx +13 -13
- package/src/stories/pattern-centered-conversation.stories.tsx +3 -3
- package/src/stories/pattern-docked-widget.stories.tsx +1 -1
- package/src/stories/pattern-empty-state.stories.tsx +3 -3
- package/src/stories/prompt-input-variants.stories.tsx +13 -13
- package/src/stories/streaming-response.stories.tsx +3 -3
- package/src/stories/typography.stories.tsx +4 -4
- package/src/ui/avatar.stories.tsx +7 -8
- package/src/ui/badge.stories.tsx +7 -8
- package/src/ui/button.stories.tsx +8 -9
- package/src/ui/button.tsx +1 -0
- package/src/ui/collapsible.stories.tsx +6 -7
- package/src/ui/dropdown.stories.tsx +6 -7
- package/src/ui/hover-card.stories.tsx +6 -7
- package/src/ui/resizable.stories.tsx +74 -9
- package/src/ui/resizable.tsx +351 -71
- package/src/ui/scroll-area.stories.tsx +6 -7
- package/src/ui/scroll-area.tsx +3 -1
- package/src/ui/separator.stories.tsx +7 -8
- package/src/ui/skeleton.stories.tsx +7 -8
- package/src/ui/textarea.stories.tsx +6 -7
- package/src/ui/tooltip.stories.tsx +8 -9
- package/theme.css +65 -65
- package/src/stories/docs/element-spec.tsx +0 -86
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
2
|
import { onMount } from 'solid-js';
|
|
3
3
|
import './register'; // side effect: registers the custom elements
|
|
4
|
-
import {
|
|
5
|
-
import { argTypesFor } from '../stories/docs/element-controls';
|
|
4
|
+
import { argTypesFor, specDescription } from '../stories/docs/element-controls';
|
|
6
5
|
|
|
7
6
|
interface Step {
|
|
8
7
|
label: string;
|
|
@@ -14,7 +13,7 @@ declare module 'solid-js' {
|
|
|
14
13
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
15
14
|
namespace JSX {
|
|
16
15
|
interface IntrinsicElements {
|
|
17
|
-
'
|
|
16
|
+
'kc-chain-of-thought': JSX.HTMLAttributes<HTMLElement>;
|
|
18
17
|
}
|
|
19
18
|
}
|
|
20
19
|
}
|
|
@@ -25,19 +24,19 @@ const steps: Step[] = [
|
|
|
25
24
|
{ label: 'Build & verify' },
|
|
26
25
|
];
|
|
27
26
|
|
|
28
|
-
/** Render `<
|
|
27
|
+
/** Render `<kc-chain-of-thought>` with the `steps` set as a JS property. */
|
|
29
28
|
function CotElement(props: { steps: Step[] }) {
|
|
30
29
|
let el: (HTMLElement & { steps?: Step[] }) | undefined;
|
|
31
30
|
onMount(() => {
|
|
32
31
|
if (el) el.steps = props.steps;
|
|
33
32
|
});
|
|
34
33
|
return (
|
|
35
|
-
<
|
|
34
|
+
<kc-chain-of-thought ref={(e) => (el = e as HTMLElement)} style={{ display: 'block', padding: '24px', 'max-width': '560px' }} />
|
|
36
35
|
);
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
40
|
-
<
|
|
39
|
+
<kc-chain-of-thought id="cot"></kc-chain-of-thought>
|
|
41
40
|
|
|
42
41
|
<script type="module">
|
|
43
42
|
import '@kitnai/chat/elements'; // registers the custom elements
|
|
@@ -50,20 +49,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
|
50
49
|
</script>`;
|
|
51
50
|
|
|
52
51
|
const meta = {
|
|
53
|
-
title: 'Web Components/
|
|
52
|
+
title: 'Web Components/kc-chain-of-thought',
|
|
54
53
|
tags: ['autodocs'],
|
|
55
|
-
argTypes: argTypesFor('
|
|
54
|
+
argTypes: argTypesFor('kc-chain-of-thought'),
|
|
56
55
|
parameters: {
|
|
57
56
|
layout: 'fullscreen',
|
|
58
57
|
docs: {
|
|
59
|
-
description:
|
|
60
|
-
component
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
].join('\n\n'),
|
|
66
|
-
},
|
|
58
|
+
description: specDescription('kc-chain-of-thought', [
|
|
59
|
+
'`<kc-chain-of-thought>` is the framework-agnostic **web component** for step-by-step reasoning — a connected list of steps, each with optional collapsible detail — isolated in **Shadow DOM**. The compound primitive collapses to a single `steps` data model (Route 1).',
|
|
60
|
+
'**When to use:** surfacing an agent\'s plan or reasoning trace in a non-Solid app. In SolidJS, compose the `ChainOfThought` primitives for finer control.',
|
|
61
|
+
"**How to use:** register once with `import '@kitnai/chat/elements'`, then set the `steps` **property** — an array of `{ label, content? }`. Steps with `content` become expandable.",
|
|
62
|
+
'See the **Code** tab for HTML usage.',
|
|
63
|
+
]),
|
|
67
64
|
},
|
|
68
65
|
},
|
|
69
66
|
} satisfies Meta;
|
|
@@ -71,12 +68,6 @@ const meta = {
|
|
|
71
68
|
export default meta;
|
|
72
69
|
type Story = StoryObj;
|
|
73
70
|
|
|
74
|
-
/** Full generated API reference — properties, events, tokens, and composed-from. */
|
|
75
|
-
export const API: Story = {
|
|
76
|
-
render: () => <ElementSpec tag="kitn-chain-of-thought" />,
|
|
77
|
-
parameters: { layout: 'padded' },
|
|
78
|
-
};
|
|
79
|
-
|
|
80
71
|
/** A three-step reasoning trace; the last step has no detail. */
|
|
81
72
|
export const Default: Story = {
|
|
82
73
|
render: () => <CotElement steps={steps} />,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { For, Show } from 'solid-js';
|
|
2
|
-
import {
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
3
|
import {
|
|
4
4
|
ChainOfThought,
|
|
5
5
|
ChainOfThoughtStep,
|
|
@@ -22,10 +22,10 @@ interface Props extends Record<string, unknown> {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* `<
|
|
25
|
+
* `<kc-chain-of-thought>` — step-by-step reasoning with connectors and
|
|
26
26
|
* per-step collapsible detail. Data via the `steps` property.
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
defineWebComponent<Props>('kc-chain-of-thought', {
|
|
29
29
|
steps: [],
|
|
30
30
|
}, (props) => (
|
|
31
31
|
<ChainOfThought>
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
2
|
import { onMount } from 'solid-js';
|
|
3
3
|
import './register'; // side effect: registers the custom elements
|
|
4
|
-
import {
|
|
5
|
-
import { argTypesFor } from '../stories/docs/element-controls';
|
|
4
|
+
import { argTypesFor, specDescription } from '../stories/docs/element-controls';
|
|
6
5
|
|
|
7
6
|
// The web components are custom DOM elements, so declare the tags for JSX.
|
|
8
7
|
declare module 'solid-js' {
|
|
9
8
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
10
9
|
namespace JSX {
|
|
11
10
|
interface IntrinsicElements {
|
|
12
|
-
'
|
|
11
|
+
'kc-scope-picker': JSX.HTMLAttributes<HTMLElement> & {
|
|
13
12
|
'current-label'?: string;
|
|
14
13
|
};
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
/** Render `<
|
|
18
|
+
/** Render `<kc-scope-picker>` with author/tag options set as properties. */
|
|
20
19
|
function ScopePickerElement(props: { authors: string[]; tags: string[] }) {
|
|
21
20
|
let el: (HTMLElement & { availableAuthors?: string[]; availableTags?: string[] }) | undefined;
|
|
22
21
|
onMount(() => {
|
|
@@ -29,12 +28,12 @@ function ScopePickerElement(props: { authors: string[]; tags: string[] }) {
|
|
|
29
28
|
});
|
|
30
29
|
});
|
|
31
30
|
return (
|
|
32
|
-
<
|
|
31
|
+
<kc-scope-picker ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '40px' }} />
|
|
33
32
|
);
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
37
|
-
<
|
|
36
|
+
<kc-scope-picker id="scope"></kc-scope-picker>
|
|
38
37
|
|
|
39
38
|
<script type="module">
|
|
40
39
|
import '@kitnai/chat/elements'; // registers the custom elements
|
|
@@ -47,20 +46,18 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
|
47
46
|
</script>`;
|
|
48
47
|
|
|
49
48
|
const meta = {
|
|
50
|
-
title: 'Web Components/
|
|
49
|
+
title: 'Web Components/kc-scope-picker',
|
|
51
50
|
tags: ['autodocs'],
|
|
52
|
-
argTypes: argTypesFor('
|
|
51
|
+
argTypes: argTypesFor('kc-scope-picker'),
|
|
53
52
|
parameters: {
|
|
54
53
|
layout: 'fullscreen',
|
|
55
54
|
docs: {
|
|
56
|
-
description:
|
|
57
|
-
|
|
58
|
-
'`<kitn-chat-scope-picker>` is the framework-agnostic **web component** for scoping a chat by author or tag — a dropdown that emits the chosen filters — isolated in **Shadow DOM**.',
|
|
55
|
+
description: specDescription('kc-scope-picker', [
|
|
56
|
+
'`<kc-scope-picker>` is the framework-agnostic **web component** for scoping a chat by author or tag — a dropdown that emits the chosen filters — isolated in **Shadow DOM**.',
|
|
59
57
|
'**When to use:** letting users narrow a conversation/search to a subset of content. In SolidJS, use the `ChatScopePicker` primitive.',
|
|
60
58
|
"**How to use:** register once with `import '@kitnai/chat/elements'`, set the `availableAuthors` / `availableTags` **properties** (and optionally `current-label`), and listen for the `scopechange` **CustomEvent** (`undefined` filters = \"All Content\").",
|
|
61
59
|
'See the **Code** tab for HTML usage.',
|
|
62
|
-
]
|
|
63
|
-
},
|
|
60
|
+
]),
|
|
64
61
|
},
|
|
65
62
|
},
|
|
66
63
|
} satisfies Meta;
|
|
@@ -68,12 +65,6 @@ const meta = {
|
|
|
68
65
|
export default meta;
|
|
69
66
|
type Story = StoryObj;
|
|
70
67
|
|
|
71
|
-
/** Full generated API reference — properties, events, tokens, and composed-from. */
|
|
72
|
-
export const API: Story = {
|
|
73
|
-
render: () => <ElementSpec tag="kitn-chat-scope-picker" />,
|
|
74
|
-
parameters: { layout: 'padded' },
|
|
75
|
-
};
|
|
76
|
-
|
|
77
68
|
/** Authors and tags available as scope filters. */
|
|
78
69
|
export const Default: Story = {
|
|
79
70
|
render: () => <ScopePickerElement authors={['Rob', 'Alex']} tags={['design', 'api']} />,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineWebComponent } from './define';
|
|
2
2
|
import { ChatScopePicker } from '../components/chat-scope-picker';
|
|
3
3
|
import type { SearchFilters } from '../types';
|
|
4
4
|
|
|
@@ -11,18 +11,18 @@ interface Props extends Record<string, unknown> {
|
|
|
11
11
|
currentLabel?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/** Events fired by `<
|
|
14
|
+
/** Events fired by `<kc-scope-picker>`. */
|
|
15
15
|
interface Events {
|
|
16
16
|
/** A scope was chosen (`undefined` filters = "All Content"). */
|
|
17
17
|
scopechange: { filters: SearchFilters | undefined };
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* `<
|
|
21
|
+
* `<kc-scope-picker>` — a dropdown to scope a chat by author or tag.
|
|
22
22
|
* Options via `available-authors`/`available-tags` properties; emits
|
|
23
23
|
* `scopechange`.
|
|
24
24
|
*/
|
|
25
|
-
|
|
25
|
+
defineWebComponent<Props, Events>('kc-scope-picker', {
|
|
26
26
|
availableAuthors: [],
|
|
27
27
|
availableTags: [],
|
|
28
28
|
currentLabel: 'All Content',
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
2
|
import { onMount } from 'solid-js';
|
|
3
|
-
import './register'; // side effect: registers all kitn custom elements including <
|
|
3
|
+
import './register'; // side effect: registers all kitn custom elements including <kc-workspace>
|
|
4
4
|
import type { ConversationGroup, ConversationSummary, ModelOption } from '../types';
|
|
5
5
|
import type { ChatMessage } from './chat-types';
|
|
6
|
-
import {
|
|
7
|
-
import { argTypesFor } from '../stories/docs/element-controls';
|
|
6
|
+
import { argTypesFor, specDescription } from '../stories/docs/element-controls';
|
|
8
7
|
|
|
9
8
|
// The web components are custom DOM elements, so declare the tags for JSX.
|
|
10
9
|
declare module 'solid-js' {
|
|
11
10
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
12
11
|
namespace JSX {
|
|
13
12
|
interface IntrinsicElements {
|
|
14
|
-
'
|
|
13
|
+
'kc-workspace': JSX.HTMLAttributes<HTMLElement>;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
16
|
}
|
|
@@ -52,7 +51,7 @@ const sampleMessages: ChatMessage[] = [
|
|
|
52
51
|
id: 'm2',
|
|
53
52
|
role: 'assistant',
|
|
54
53
|
content:
|
|
55
|
-
'Use `<
|
|
54
|
+
'Use `<kc-workspace>` — set `conversations`, `messages`, and `models` as properties and listen for `conversationselect` + `submit`.',
|
|
56
55
|
actions: ['copy', 'like'],
|
|
57
56
|
},
|
|
58
57
|
];
|
|
@@ -82,7 +81,7 @@ type WorkspaceEl = HTMLElement & {
|
|
|
82
81
|
value?: string;
|
|
83
82
|
};
|
|
84
83
|
|
|
85
|
-
/** Live demo of the actual `<
|
|
84
|
+
/** Live demo of the actual `<kc-workspace>` custom element (Shadow DOM and all). */
|
|
86
85
|
function WorkspaceElement(props: { args?: Record<string, unknown> }) {
|
|
87
86
|
let el: WorkspaceEl | undefined;
|
|
88
87
|
onMount(() => {
|
|
@@ -114,7 +113,7 @@ function WorkspaceElement(props: { args?: Record<string, unknown> }) {
|
|
|
114
113
|
});
|
|
115
114
|
return (
|
|
116
115
|
<div style={{ height: '720px', width: '100%' }}>
|
|
117
|
-
<
|
|
116
|
+
<kc-workspace
|
|
118
117
|
ref={(e) => (el = e as WorkspaceEl)}
|
|
119
118
|
style={{ display: 'block', height: '100%' }}
|
|
120
119
|
/>
|
|
@@ -123,7 +122,7 @@ function WorkspaceElement(props: { args?: Record<string, unknown> }) {
|
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
126
|
-
<
|
|
125
|
+
<kc-workspace id="workspace" style="display:block; height:100vh;"></kc-workspace>
|
|
127
126
|
|
|
128
127
|
<script type="module">
|
|
129
128
|
import '@kitnai/chat/elements'; // registers the custom elements
|
|
@@ -138,7 +137,7 @@ const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
|
138
137
|
];
|
|
139
138
|
workspace.messages = [
|
|
140
139
|
{ id: 'm1', role: 'user', content: 'How do I drop the whole chat app in with one tag?' },
|
|
141
|
-
{ id: 'm2', role: 'assistant', content: 'Use <
|
|
140
|
+
{ id: 'm2', role: 'assistant', content: 'Use <kc-workspace> — set conversations, messages, and models as properties.' },
|
|
142
141
|
];
|
|
143
142
|
workspace.models = [
|
|
144
143
|
{ id: 'claude-4', name: 'Claude 4 Opus', provider: 'Anthropic' },
|
|
@@ -171,7 +170,7 @@ function Workspace() {
|
|
|
171
170
|
];
|
|
172
171
|
const messages: ChatMessage[] = [
|
|
173
172
|
{ id: 'm1', role: 'user', content: 'How do I drop the whole chat app in with one tag?' },
|
|
174
|
-
{ id: 'm2', role: 'assistant', content: 'Use <
|
|
173
|
+
{ id: 'm2', role: 'assistant', content: 'Use <kc-workspace> — set conversations, messages, and models as properties.' },
|
|
175
174
|
];
|
|
176
175
|
onMount(() => {
|
|
177
176
|
el.conversations = conversations;
|
|
@@ -182,7 +181,7 @@ function Workspace() {
|
|
|
182
181
|
el.addEventListener('sidebartoggle', (e) => console.log('sidebar collapsed:', e.detail.collapsed));
|
|
183
182
|
});
|
|
184
183
|
return (
|
|
185
|
-
<
|
|
184
|
+
<kc-workspace
|
|
186
185
|
ref={el}
|
|
187
186
|
style={{ display: 'block', height: '100vh' }}
|
|
188
187
|
/>
|
|
@@ -190,21 +189,19 @@ function Workspace() {
|
|
|
190
189
|
}`;
|
|
191
190
|
|
|
192
191
|
const meta = {
|
|
193
|
-
title: 'Web Components/
|
|
192
|
+
title: 'Web Components/kc-workspace',
|
|
194
193
|
tags: ['autodocs'],
|
|
195
|
-
argTypes: argTypesFor('
|
|
194
|
+
argTypes: argTypesFor('kc-workspace'),
|
|
196
195
|
parameters: {
|
|
197
196
|
layout: 'fullscreen',
|
|
198
197
|
docs: {
|
|
199
|
-
description:
|
|
200
|
-
|
|
201
|
-
'`<kitn-chat-workspace>` is the full chat shell as a single **web component** — a resizable split layout with a collapsible conversation list on the left and a full message thread on the right, all isolated in **Shadow DOM**. SolidJS is bundled in, so the host needs nothing.',
|
|
198
|
+
description: specDescription('kc-workspace', [
|
|
199
|
+
'`<kc-workspace>` is the full chat shell as a single **web component** — a resizable split layout with a collapsible conversation list on the left and a full message thread on the right, all isolated in **Shadow DOM**. SolidJS is bundled in, so the host needs nothing.',
|
|
202
200
|
'**When to use:** dropping an entire chat application shell into a non-Solid app (React, Vue, Svelte, plain HTML), or anywhere you want zero style conflicts and a ready-made list+chat layout. If you *are* in SolidJS and want fine-grained control, compose the `ConversationList` and `ChatThread` primitives directly.',
|
|
203
201
|
'**How to use:** register once with `import \'@kitnai/chat/elements\'`, set rich data as JS **properties** (`el.conversations = [...]`, `el.messages = [...]`, `el.models = [...]`), and listen for **CustomEvents** (`conversationselect`, `submit`, `sidebartoggle`, `newchat`) directly on the element.',
|
|
204
202
|
'**Placement:** as a full-page surface or large panel. Give it an explicit height (e.g. `height: 100vh`). The sidebar is drag-resizable and can be collapsed via the toggle button in its header.',
|
|
205
203
|
'See the **Code** tab below for the HTML usage; the *SolidJS* story shows the same element inside a Solid component.',
|
|
206
|
-
]
|
|
207
|
-
},
|
|
204
|
+
]),
|
|
208
205
|
},
|
|
209
206
|
},
|
|
210
207
|
} satisfies Meta;
|
|
@@ -238,8 +235,3 @@ export const InSolidJS: Story = {
|
|
|
238
235
|
parameters: { docs: { source: { code: SOLID_SNIPPET, language: 'tsx' } } },
|
|
239
236
|
};
|
|
240
237
|
|
|
241
|
-
/** Full generated API reference — properties, events, tokens, and the SolidJS components this element is composed from. */
|
|
242
|
-
export const API: Story = {
|
|
243
|
-
render: () => <ElementSpec tag="kitn-chat-workspace" />,
|
|
244
|
-
parameters: { layout: 'padded' },
|
|
245
|
-
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createSignal, Show } from 'solid-js';
|
|
2
|
-
import {
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
3
|
import { ChatThread, type ChatThreadContextUsage } from '../components/chat-thread';
|
|
4
4
|
import { ConversationList } from '../components/conversation-list';
|
|
5
5
|
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '../ui/resizable';
|
|
@@ -73,7 +73,7 @@ interface Events {
|
|
|
73
73
|
suggestionclick: { value: string };
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
defineWebComponent<Props, Events>('kc-workspace', {
|
|
77
77
|
groups: [], conversations: [], activeId: undefined, messages: [],
|
|
78
78
|
value: undefined, placeholder: 'Send a message...', loading: false,
|
|
79
79
|
suggestions: undefined, suggestionMode: 'submit', proseSize: 'sm',
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
2
|
import { onMount } from 'solid-js';
|
|
3
|
-
import './register'; // side effect: registers <
|
|
3
|
+
import './register'; // side effect: registers <kc-chat>, <kc-conversations>, <kc-prompt-input>
|
|
4
4
|
import type { ChatMessage } from './chat-types';
|
|
5
|
-
import {
|
|
6
|
-
import { argTypesFor } from '../stories/docs/element-controls';
|
|
5
|
+
import { argTypesFor, specDescription } from '../stories/docs/element-controls';
|
|
7
6
|
|
|
8
7
|
// The web components are custom DOM elements, so declare the tags for JSX.
|
|
9
8
|
declare module 'solid-js' {
|
|
10
9
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
11
10
|
namespace JSX {
|
|
12
11
|
interface IntrinsicElements {
|
|
13
|
-
'
|
|
12
|
+
'kc-chat': JSX.HTMLAttributes<HTMLElement>;
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
15
|
}
|
|
@@ -43,7 +42,7 @@ type ChatEl = HTMLElement & {
|
|
|
43
42
|
slashCompact?: boolean;
|
|
44
43
|
};
|
|
45
44
|
|
|
46
|
-
/** Live demo of the actual `<
|
|
45
|
+
/** Live demo of the actual `<kc-chat>` custom element (Shadow DOM and all). */
|
|
47
46
|
function ChatElement(props: { args?: Record<string, unknown> }) {
|
|
48
47
|
let el: ChatEl | undefined;
|
|
49
48
|
onMount(() => {
|
|
@@ -64,11 +63,11 @@ function ChatElement(props: { args?: Record<string, unknown> }) {
|
|
|
64
63
|
}
|
|
65
64
|
}
|
|
66
65
|
});
|
|
67
|
-
return <
|
|
66
|
+
return <kc-chat ref={(e) => (el = e as ChatEl)} style={{ display: 'block', height: '560px' }} />;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
71
|
-
<
|
|
70
|
+
<kc-chat id="chat" style="display:block; height:100vh;"></kc-chat>
|
|
72
71
|
|
|
73
72
|
<script type="module">
|
|
74
73
|
import '@kitnai/chat/elements'; // registers the custom elements
|
|
@@ -95,7 +94,7 @@ function Chat() {
|
|
|
95
94
|
];
|
|
96
95
|
onMount(() => { el.messages = messages; });
|
|
97
96
|
return (
|
|
98
|
-
<
|
|
97
|
+
<kc-chat
|
|
99
98
|
ref={el}
|
|
100
99
|
style={{ display: 'block', height: '100vh' }}
|
|
101
100
|
on:submit={(e) => console.log('user sent:', e.detail.value)}
|
|
@@ -104,21 +103,19 @@ function Chat() {
|
|
|
104
103
|
}`;
|
|
105
104
|
|
|
106
105
|
const meta = {
|
|
107
|
-
title: 'Web Components/
|
|
106
|
+
title: 'Web Components/kc-chat',
|
|
108
107
|
tags: ['autodocs'],
|
|
109
|
-
argTypes: argTypesFor('
|
|
108
|
+
argTypes: argTypesFor('kc-chat'),
|
|
110
109
|
parameters: {
|
|
111
110
|
layout: 'fullscreen',
|
|
112
111
|
docs: {
|
|
113
|
-
description:
|
|
114
|
-
|
|
115
|
-
'`<kitn-chat>` is the framework-agnostic **web component** version of the chat UI — a complete message thread plus prompt input, isolated in **Shadow DOM** so the host page\'s CSS can\'t leak in and the kit\'s styles can\'t leak out. SolidJS is bundled in, so the host needs nothing.',
|
|
112
|
+
description: specDescription('kc-chat', [
|
|
113
|
+
'`<kc-chat>` is the framework-agnostic **web component** version of the chat UI — a complete message thread plus prompt input, isolated in **Shadow DOM** so the host page\'s CSS can\'t leak in and the kit\'s styles can\'t leak out. SolidJS is bundled in, so the host needs nothing.',
|
|
116
114
|
'**When to use:** dropping a full chat into a non-Solid app (React, Vue, Svelte, plain HTML), or anywhere you want zero style conflicts. If you *are* in SolidJS and want fine-grained control, compose the primitives (`ChatContainer`, `Message`, `PromptInput`) instead.',
|
|
117
115
|
'**How to use:** register once with `import \'@kitnai/chat/elements\'`, set rich data as JS **properties** (`el.messages = [...]`), and listen for **CustomEvents** (`submit`, `messageaction`, `valuechange`) directly on the element.',
|
|
118
116
|
'**Placement:** as a top-level panel or full-page surface. Give it an explicit height (e.g. `height: 100vh`).',
|
|
119
117
|
'See the **Code** tab below for the HTML usage; the *SolidJS* story shows the same element inside a Solid component.',
|
|
120
|
-
]
|
|
121
|
-
},
|
|
118
|
+
]),
|
|
122
119
|
},
|
|
123
120
|
},
|
|
124
121
|
} satisfies Meta;
|
|
@@ -151,8 +148,3 @@ export const InSolidJS: Story = {
|
|
|
151
148
|
parameters: { docs: { source: { code: SOLID_SNIPPET, language: 'tsx' } } },
|
|
152
149
|
};
|
|
153
150
|
|
|
154
|
-
/** Full generated API reference — properties, events, tokens, and the SolidJS components this element is composed from. */
|
|
155
|
-
export const API: Story = {
|
|
156
|
-
render: () => <ElementSpec tag="kitn-chat" />,
|
|
157
|
-
parameters: { layout: 'padded' },
|
|
158
|
-
};
|
package/src/elements/chat.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineWebComponent } from './define';
|
|
2
2
|
import { ChatThread, type ChatThreadProps, type ChatThreadContextUsage } from '../components/chat-thread';
|
|
3
3
|
import type { AttachmentData } from '../components/attachments';
|
|
4
4
|
import type { SlashCommandItem } from '../components/slash-command';
|
|
@@ -29,7 +29,7 @@ interface Events {
|
|
|
29
29
|
voice: Record<string, never>;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
defineWebComponent<Props, Events>('kc-chat', {
|
|
33
33
|
messages: [], value: undefined, placeholder: 'Send a message...', loading: false,
|
|
34
34
|
suggestions: undefined, suggestionMode: 'submit', proseSize: 'sm',
|
|
35
35
|
codeTheme: 'github-dark-dimmed', codeHighlight: true, chatTitle: undefined,
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from 'storybook-solidjs-vite';
|
|
2
2
|
import { onMount } from 'solid-js';
|
|
3
3
|
import './register'; // side effect: registers the custom elements
|
|
4
|
-
import {
|
|
5
|
-
import { argTypesFor } from '../stories/docs/element-controls';
|
|
4
|
+
import { argTypesFor, specDescription } from '../stories/docs/element-controls';
|
|
6
5
|
|
|
7
6
|
// The web components are custom DOM elements, so declare the tags for JSX.
|
|
8
7
|
declare module 'solid-js' {
|
|
9
8
|
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
10
9
|
namespace JSX {
|
|
11
10
|
interface IntrinsicElements {
|
|
12
|
-
'
|
|
11
|
+
'kc-checkpoint': JSX.HTMLAttributes<HTMLElement>;
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
/** Render the actual `<
|
|
16
|
+
/** Render the actual `<kc-checkpoint>` custom element configured by attributes. */
|
|
18
17
|
function CheckpointElement(props: { label?: string; tooltip?: string; variant?: string; size?: string }) {
|
|
19
18
|
let el: HTMLElement | undefined;
|
|
20
19
|
onMount(() => {
|
|
@@ -25,35 +24,33 @@ function CheckpointElement(props: { label?: string; tooltip?: string; variant?:
|
|
|
25
24
|
if (props.size) el.setAttribute('size', props.size);
|
|
26
25
|
el.addEventListener('select', () => console.log('checkpoint selected'));
|
|
27
26
|
});
|
|
28
|
-
return <
|
|
27
|
+
return <kc-checkpoint ref={(e) => (el = e as HTMLElement)} style={{ display: 'inline-block', padding: '16px' }} />;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
const HTML_SNIPPET = `<!-- Works in any framework or plain HTML -->
|
|
32
|
-
<
|
|
31
|
+
<kc-checkpoint label="Restore" tooltip="Restore this checkpoint" variant="outline" size="sm"></kc-checkpoint>
|
|
33
32
|
|
|
34
33
|
<script type="module">
|
|
35
34
|
import '@kitnai/chat/elements'; // registers the custom elements
|
|
36
35
|
|
|
37
|
-
const cp = document.querySelector('
|
|
36
|
+
const cp = document.querySelector('kc-checkpoint');
|
|
38
37
|
// events are CustomEvents on the element (they do not bubble)
|
|
39
38
|
cp.addEventListener('select', () => console.log('restore checkpoint'));
|
|
40
39
|
</script>`;
|
|
41
40
|
|
|
42
41
|
const meta = {
|
|
43
|
-
title: 'Web Components/
|
|
42
|
+
title: 'Web Components/kc-checkpoint',
|
|
44
43
|
tags: ['autodocs'],
|
|
45
|
-
argTypes: argTypesFor('
|
|
44
|
+
argTypes: argTypesFor('kc-checkpoint'),
|
|
46
45
|
parameters: {
|
|
47
46
|
layout: 'fullscreen',
|
|
48
47
|
docs: {
|
|
49
|
-
description:
|
|
50
|
-
|
|
51
|
-
'`<kitn-checkpoint>` is the framework-agnostic **web component** for a bookmark/checkpoint button (with an optional tooltip and label), isolated in **Shadow DOM**.',
|
|
48
|
+
description: specDescription('kc-checkpoint', [
|
|
49
|
+
'`<kc-checkpoint>` is the framework-agnostic **web component** for a bookmark/checkpoint button (with an optional tooltip and label), isolated in **Shadow DOM**.',
|
|
52
50
|
'**When to use:** marking a restore point in a conversation in a non-Solid app. In SolidJS, compose the `Checkpoint` primitives.',
|
|
53
51
|
"**How to use:** register once with `import '@kitnai/chat/elements'`, set `label`, `tooltip`, `variant` (`ghost` | `default` | `outline`), and `size` via attributes, and listen for the `select` **CustomEvent** on click.",
|
|
54
52
|
'See the **Code** tab for HTML usage.',
|
|
55
|
-
]
|
|
56
|
-
},
|
|
53
|
+
]),
|
|
57
54
|
},
|
|
58
55
|
},
|
|
59
56
|
} satisfies Meta;
|
|
@@ -61,12 +58,6 @@ const meta = {
|
|
|
61
58
|
export default meta;
|
|
62
59
|
type Story = StoryObj;
|
|
63
60
|
|
|
64
|
-
/** Full generated API reference — properties, events, tokens, and composed-from. */
|
|
65
|
-
export const API: Story = {
|
|
66
|
-
render: () => <ElementSpec tag="kitn-checkpoint" />,
|
|
67
|
-
parameters: { layout: 'padded' },
|
|
68
|
-
};
|
|
69
|
-
|
|
70
61
|
/** A labeled checkpoint button. (Add a `tooltip` attribute for a hover hint — see the Code tab.) */
|
|
71
62
|
export const Labeled: Story = {
|
|
72
63
|
render: () => <CheckpointElement label="Restore" variant="outline" />,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Show } from 'solid-js';
|
|
2
|
-
import {
|
|
2
|
+
import { defineWebComponent } from './define';
|
|
3
3
|
import { Checkpoint, CheckpointIcon, CheckpointTrigger } from '../components/checkpoint';
|
|
4
4
|
|
|
5
5
|
interface Props extends Record<string, unknown> {
|
|
@@ -13,17 +13,17 @@ interface Props extends Record<string, unknown> {
|
|
|
13
13
|
size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm';
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/** Events fired by `<
|
|
16
|
+
/** Events fired by `<kc-checkpoint>`. */
|
|
17
17
|
interface Events {
|
|
18
18
|
/** The checkpoint was clicked. */
|
|
19
19
|
select: void;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* `<
|
|
23
|
+
* `<kc-checkpoint>` — a bookmark/checkpoint button (optional tooltip + label).
|
|
24
24
|
* Emits `select`.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
defineWebComponent<Props, Events>('kc-checkpoint', {
|
|
27
27
|
label: undefined,
|
|
28
28
|
tooltip: undefined,
|
|
29
29
|
variant: 'ghost',
|
|
@@ -32,6 +32,10 @@ defineKitnElement<Props, Events>('kitn-checkpoint', {
|
|
|
32
32
|
<Checkpoint>
|
|
33
33
|
<CheckpointTrigger
|
|
34
34
|
tooltip={props.tooltip}
|
|
35
|
+
// Icon-only (no visible label) needs an accessible name: prefer the
|
|
36
|
+
// tooltip text, else a sensible default. With a visible label, the text
|
|
37
|
+
// is the name — leave aria-label unset so it isn't duplicated.
|
|
38
|
+
aria-label={props.label ? undefined : (props.tooltip ?? 'Checkpoint')}
|
|
35
39
|
variant={props.variant}
|
|
36
40
|
size={props.size}
|
|
37
41
|
onClick={() => dispatch('select')}
|