@kitnai/chat 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/README.md +11 -0
  2. package/dist/custom-elements.json +2494 -0
  3. package/dist/kitn-chat.es.js +52 -39
  4. package/dist/llms/llms-full.txt +667 -0
  5. package/dist/llms/llms.txt +104 -0
  6. package/dist/theme.tokens.css +133 -0
  7. package/frameworks/react/index.tsx +530 -0
  8. package/frameworks/react/runtime.tsx +94 -0
  9. package/llms-full.txt +667 -0
  10. package/llms.txt +104 -0
  11. package/package.json +34 -5
  12. package/src/components/attachments.tsx +4 -2
  13. package/src/components/chain-of-thought.tsx +1 -1
  14. package/src/components/chat-scope-picker.tsx +2 -2
  15. package/src/components/checkpoint.tsx +7 -3
  16. package/src/components/context.tsx +14 -18
  17. package/src/components/conversation-item.tsx +1 -1
  18. package/src/components/conversation-list.tsx +5 -4
  19. package/src/components/message-skills.tsx +1 -1
  20. package/src/components/message.tsx +1 -0
  21. package/src/components/model-switcher.tsx +3 -3
  22. package/src/components/prompt-input.tsx +15 -2
  23. package/src/components/reasoning.tsx +2 -2
  24. package/src/components/scroll-button.tsx +1 -0
  25. package/src/components/slash-command.tsx +17 -8
  26. package/src/components/source.tsx +2 -2
  27. package/src/components/thinking-bar.tsx +2 -2
  28. package/src/components/tool.tsx +17 -6
  29. package/src/components/voice-input.tsx +5 -1
  30. package/src/elements/attachments.tsx +132 -0
  31. package/src/elements/chain-of-thought.tsx +45 -0
  32. package/src/elements/chat-scope-picker.tsx +36 -0
  33. package/src/elements/chat.tsx +51 -7
  34. package/src/elements/checkpoint.tsx +43 -0
  35. package/src/elements/code-block.tsx +42 -0
  36. package/src/elements/compiled.css +1 -1
  37. package/src/elements/context-meter.tsx +71 -0
  38. package/src/elements/conversation-list.tsx +6 -0
  39. package/src/elements/default-input.tsx +22 -1
  40. package/src/elements/define.tsx +102 -13
  41. package/src/elements/element-types.d.ts +404 -0
  42. package/src/elements/empty.tsx +29 -0
  43. package/src/elements/feedback-bar.tsx +33 -0
  44. package/src/elements/file-upload.tsx +44 -0
  45. package/src/elements/image.tsx +32 -0
  46. package/src/elements/kitn-attachments.stories.tsx +181 -0
  47. package/src/elements/kitn-chain-of-thought.stories.tsx +75 -0
  48. package/src/elements/kitn-chat-scope-picker.stories.tsx +72 -0
  49. package/src/elements/kitn-checkpoint.stories.tsx +71 -0
  50. package/src/elements/kitn-code-block.stories.tsx +82 -0
  51. package/src/elements/kitn-context-meter.stories.tsx +85 -0
  52. package/src/elements/kitn-empty.stories.tsx +110 -0
  53. package/src/elements/kitn-feedback-bar.stories.tsx +73 -0
  54. package/src/elements/kitn-file-upload.stories.tsx +81 -0
  55. package/src/elements/kitn-image.stories.tsx +70 -0
  56. package/src/elements/kitn-loader.stories.tsx +87 -0
  57. package/src/elements/kitn-markdown.stories.tsx +75 -0
  58. package/src/elements/kitn-message-skills.stories.tsx +74 -0
  59. package/src/elements/kitn-message.stories.tsx +105 -0
  60. package/src/elements/kitn-model-switcher.stories.tsx +80 -0
  61. package/src/elements/kitn-prompt-input.stories.tsx +74 -16
  62. package/src/elements/kitn-prompt-suggestions.stories.tsx +157 -0
  63. package/src/elements/kitn-reasoning.stories.tsx +76 -0
  64. package/src/elements/kitn-response-stream.stories.tsx +79 -0
  65. package/src/elements/kitn-source-list.stories.tsx +77 -0
  66. package/src/elements/kitn-source.stories.tsx +87 -0
  67. package/src/elements/kitn-text-shimmer.stories.tsx +63 -0
  68. package/src/elements/kitn-thinking-bar.stories.tsx +72 -0
  69. package/src/elements/kitn-tool.stories.tsx +88 -0
  70. package/src/elements/kitn-voice-input.stories.tsx +87 -0
  71. package/src/elements/loader.tsx +25 -0
  72. package/src/elements/markdown.tsx +38 -0
  73. package/src/elements/message-skills.tsx +22 -0
  74. package/src/elements/message.tsx +125 -0
  75. package/src/elements/model-switcher.tsx +35 -0
  76. package/src/elements/prompt-input.tsx +83 -7
  77. package/src/elements/prompt-suggestions.tsx +58 -0
  78. package/src/elements/reasoning.tsx +50 -0
  79. package/src/elements/register.ts +31 -0
  80. package/src/elements/response-stream.tsx +40 -0
  81. package/src/elements/source.tsx +67 -0
  82. package/src/elements/text-shimmer.tsx +28 -0
  83. package/src/elements/thinking-bar.tsx +34 -0
  84. package/src/elements/tool.tsx +23 -0
  85. package/src/elements/voice-input.tsx +41 -0
  86. package/src/index.ts +0 -1
  87. package/src/primitives/chat-config.tsx +2 -2
  88. package/src/stories/docs/Accessibility.mdx +119 -0
  89. package/src/stories/docs/ForAIAgents.mdx +93 -0
  90. package/src/stories/docs/GettingStarted.mdx +2 -2
  91. package/src/stories/docs/Installation.mdx +2 -2
  92. package/src/stories/docs/Integrations.mdx +415 -15
  93. package/src/stories/docs/Introduction.mdx +5 -5
  94. package/src/stories/docs/Theming.mdx +1 -1
  95. package/src/stories/typography.stories.tsx +78 -0
  96. package/src/ui/button.tsx +1 -1
  97. package/src/ui/collapsible.tsx +119 -8
  98. package/src/ui/dropdown.tsx +177 -12
  99. package/src/ui/hover-card.tsx +147 -26
  100. package/src/ui/overlay.tsx +151 -0
  101. package/src/ui/textarea.tsx +1 -1
  102. package/src/ui/tooltip.stories.tsx +1 -1
  103. package/src/ui/tooltip.tsx +59 -13
  104. package/src/utils/cn.ts +19 -1
  105. package/theme.css +72 -43
  106. package/src/ui/dialog.tsx +0 -21
@@ -0,0 +1,132 @@
1
+ import { For, Show } from 'solid-js';
2
+ import { defineKitnElement } from './define';
3
+ import {
4
+ Attachments,
5
+ Attachment,
6
+ AttachmentPreview,
7
+ AttachmentInfo,
8
+ AttachmentRemove,
9
+ AttachmentHoverCard,
10
+ AttachmentHoverCardTrigger,
11
+ AttachmentHoverCardContent,
12
+ AttachmentEmpty,
13
+ getAttachmentLabel,
14
+ getMediaCategory,
15
+ type AttachmentData,
16
+ type AttachmentVariant,
17
+ } from '../components/attachments';
18
+
19
+ interface Props extends Record<string, unknown> {
20
+ /** The attachments to render. Set as a JS property (array). */
21
+ items: AttachmentData[];
22
+ /** Layout: `grid` = visual tiles, `inline` = icon + label chips, `list` = rows. */
23
+ variant?: AttachmentVariant;
24
+ /** Wrap each item in a hover card that previews its details. */
25
+ hoverCard?: boolean;
26
+ /** Show a remove button per item; clicking it fires a `remove` event. */
27
+ removable?: boolean;
28
+ /** Also show the media type beneath the filename (non-grid variants). */
29
+ showMediaType?: boolean;
30
+ /** Text shown when `items` is empty. */
31
+ emptyText?: string;
32
+ }
33
+
34
+ /** Events fired by `<kitn-attachments>`. */
35
+ interface Events {
36
+ /** A remove button was clicked. */
37
+ remove: { id: string };
38
+ }
39
+
40
+ /**
41
+ * `<kitn-attachments>` — the exemplar for the "collapse a compound primitive to
42
+ * ONE configurable element" pattern (Route 1). The presentation knobs that the
43
+ * SolidJS layer expresses by composing sub-parts (`<AttachmentPreview>`,
44
+ * `<AttachmentInfo>`, `<AttachmentHoverCard>`, `<AttachmentRemove>`) become
45
+ * attributes/flags here:
46
+ *
47
+ * - icon + label .......... `variant="inline"`
48
+ * - visual + hover card .... `variant="grid" hover-card`
49
+ * - removable chips ........ add `removable` (emits `remove` → { id })
50
+ *
51
+ * Data in via the `items` property; the only interaction (`remove`) comes back
52
+ * as an event. For fully-custom hover content, the SolidJS primitives remain the
53
+ * escape hatch (a templated slot — "Route 2" — is a deliberate future add).
54
+ */
55
+ defineKitnElement<Props, Events>('kitn-attachments', {
56
+ items: [],
57
+ variant: 'grid',
58
+ hoverCard: false,
59
+ removable: false,
60
+ showMediaType: false,
61
+ emptyText: undefined,
62
+ }, (props, { dispatch, flag }) => {
63
+ const variant = () => props.variant ?? 'grid';
64
+ const hoverCard = () => flag('hoverCard');
65
+ const removable = () => flag('removable');
66
+ const showMediaType = () => flag('showMediaType');
67
+
68
+ return (
69
+ <Show
70
+ when={props.items.length}
71
+ fallback={<Show when={props.emptyText}><AttachmentEmpty>{props.emptyText}</AttachmentEmpty></Show>}
72
+ >
73
+ <Attachments variant={variant()}>
74
+ <For each={props.items}>
75
+ {(item) => (
76
+ <Attachment
77
+ data={item}
78
+ onRemove={removable() ? () => dispatch('remove', { id: item.id }) : undefined}
79
+ >
80
+ <Show
81
+ when={hoverCard() && variant() !== 'grid'}
82
+ fallback={
83
+ <>
84
+ <AttachmentPreview />
85
+ {/* Info only for non-grid; grid is a self-contained visual tile. */}
86
+ <Show when={variant() !== 'grid'}>
87
+ <AttachmentInfo showMediaType={showMediaType()} />
88
+ </Show>
89
+ </>
90
+ }
91
+ >
92
+ {/* Hover preview is for compact inline/list chips; grid tiles are
93
+ already the visual, so they skip it (wrapping them would also
94
+ collapse non-image tiles to the icon's height). */}
95
+ <AttachmentHoverCard>
96
+ <AttachmentHoverCardTrigger>
97
+ <div class="flex items-center gap-1.5">
98
+ <AttachmentPreview />
99
+ <AttachmentInfo showMediaType={showMediaType()} />
100
+ </div>
101
+ </AttachmentHoverCardTrigger>
102
+ <AttachmentHoverCardContent>
103
+ {/* For image attachments, preview the actual thumbnail;
104
+ otherwise fall back to the label + media-type details. */}
105
+ <Show
106
+ when={getMediaCategory(item) === 'image' && item.type === 'file' && item.url}
107
+ fallback={
108
+ <>
109
+ <div class="text-body font-medium">{getAttachmentLabel(item)}</div>
110
+ <Show when={item.mediaType}>
111
+ <div class="text-muted-foreground text-caption">{item.mediaType}</div>
112
+ </Show>
113
+ </>
114
+ }
115
+ >
116
+ <img
117
+ src={item.url}
118
+ alt={getAttachmentLabel(item)}
119
+ class="block max-h-64 max-w-xs rounded object-contain"
120
+ />
121
+ </Show>
122
+ </AttachmentHoverCardContent>
123
+ </AttachmentHoverCard>
124
+ </Show>
125
+ <AttachmentRemove />
126
+ </Attachment>
127
+ )}
128
+ </For>
129
+ </Attachments>
130
+ </Show>
131
+ );
132
+ });
@@ -0,0 +1,45 @@
1
+ import { For, Show } from 'solid-js';
2
+ import { defineKitnElement } from './define';
3
+ import {
4
+ ChainOfThought,
5
+ ChainOfThoughtStep,
6
+ ChainOfThoughtTrigger,
7
+ ChainOfThoughtContent,
8
+ ChainOfThoughtItem,
9
+ } from '../components/chain-of-thought';
10
+
11
+ interface Step {
12
+ /** The step's heading (the always-visible trigger). */
13
+ label: string;
14
+ /** Optional expandable detail. */
15
+ content?: string;
16
+ }
17
+
18
+ interface Props extends Record<string, unknown> {
19
+ /** The reasoning steps. Set as a JS property. Compound sub-parts collapse to
20
+ * this one data model (Route 1). */
21
+ steps: Step[];
22
+ }
23
+
24
+ /**
25
+ * `<kitn-chain-of-thought>` — step-by-step reasoning with connectors and
26
+ * per-step collapsible detail. Data via the `steps` property.
27
+ */
28
+ defineKitnElement<Props>('kitn-chain-of-thought', {
29
+ steps: [],
30
+ }, (props) => (
31
+ <ChainOfThought>
32
+ <For each={props.steps}>
33
+ {(step, i) => (
34
+ <ChainOfThoughtStep isLast={i() === props.steps.length - 1}>
35
+ <ChainOfThoughtTrigger>{step.label}</ChainOfThoughtTrigger>
36
+ <Show when={step.content}>
37
+ <ChainOfThoughtContent>
38
+ <ChainOfThoughtItem>{step.content}</ChainOfThoughtItem>
39
+ </ChainOfThoughtContent>
40
+ </Show>
41
+ </ChainOfThoughtStep>
42
+ )}
43
+ </For>
44
+ </ChainOfThought>
45
+ ));
@@ -0,0 +1,36 @@
1
+ import { defineKitnElement } from './define';
2
+ import { ChatScopePicker } from '../components/chat-scope-picker';
3
+ import type { SearchFilters } from '../types';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** Authors to offer as scope filters. Set as a JS property. */
7
+ availableAuthors: string[];
8
+ /** Tags to offer as scope filters. Set as a JS property. */
9
+ availableTags: string[];
10
+ /** The label shown on the trigger for the active scope. */
11
+ currentLabel?: string;
12
+ }
13
+
14
+ /** Events fired by `<kitn-chat-scope-picker>`. */
15
+ interface Events {
16
+ /** A scope was chosen (`undefined` filters = "All Content"). */
17
+ scopechange: { filters: SearchFilters | undefined };
18
+ }
19
+
20
+ /**
21
+ * `<kitn-chat-scope-picker>` — a dropdown to scope a chat by author or tag.
22
+ * Options via `available-authors`/`available-tags` properties; emits
23
+ * `scopechange`.
24
+ */
25
+ defineKitnElement<Props, Events>('kitn-chat-scope-picker', {
26
+ availableAuthors: [],
27
+ availableTags: [],
28
+ currentLabel: 'All Content',
29
+ }, (props, { dispatch }) => (
30
+ <ChatScopePicker
31
+ currentLabel={props.currentLabel ?? 'All Content'}
32
+ availableAuthors={props.availableAuthors}
33
+ availableTags={props.availableTags}
34
+ onScopeChange={(filters) => dispatch('scopechange', { filters })}
35
+ />
36
+ ));
@@ -22,6 +22,7 @@ import { Button } from '../ui/button';
22
22
  import { Copy, ThumbsUp, ThumbsDown, RefreshCw, Pencil } from 'lucide-solid';
23
23
  import type { Component } from 'solid-js';
24
24
  import { DefaultPromptInput } from './default-input';
25
+ import type { SlashCommandItem } from '../components/slash-command';
25
26
  import type { ChatMessage, ChatMessageAction } from './chat-types';
26
27
  import type { ProseSize } from '../primitives/chat-config';
27
28
  import type { ModelOption } from '../types';
@@ -35,13 +36,32 @@ interface ContextUsage {
35
36
  }
36
37
 
37
38
  interface Props extends Record<string, unknown> {
39
+ /** The full message thread to render, newest last. Each entry carries its role,
40
+ * content, and optional reasoning/tools/attachments/actions. Set as a JS
41
+ * property (`el.messages = [...]`). */
38
42
  messages: ChatMessage[];
43
+ /** Controlled value of the input. When set, the host owns the input text and
44
+ * must update it on `valuechange`; leave unset for uncontrolled behavior. */
39
45
  value?: string;
46
+ /** Placeholder text shown in the empty input. */
40
47
  placeholder?: string;
48
+ /** When true, shows the loading/streaming state and disables submit (use while
49
+ * awaiting the assistant's reply). */
41
50
  loading?: boolean;
51
+ /** Starter prompts shown above the input when the thread is empty. Clicking one
52
+ * follows `suggestionMode`. Set as a JS property. */
42
53
  suggestions?: string[];
54
+ /** What clicking a suggestion does: `'submit'` (default) sends it immediately
55
+ * as if typed and submitted; `'fill'` just places it in the input. */
56
+ suggestionMode?: 'submit' | 'fill';
57
+ /** Body/prose font scale for rendered markdown (`'xs' | 'sm' | 'base' | 'lg'`).
58
+ * Defaults to `'sm'`. */
43
59
  proseSize?: ProseSize;
60
+ /** Shiki theme name for syntax-highlighted code blocks (e.g.
61
+ * `'github-dark-dimmed'`). */
44
62
  codeTheme?: string;
63
+ /** Enable Shiki syntax highlighting in code blocks. Turn off to render plain
64
+ * `<pre>` blocks (lighter, no highlighter load). Default true. */
45
65
  codeHighlight?: boolean;
46
66
  /** Optional header title shown on the left of the header. */
47
67
  chatTitle?: string;
@@ -59,6 +79,13 @@ interface Props extends Record<string, unknown> {
59
79
  search?: boolean;
60
80
  /** Show a Voice (Mic) button in the input toolbar; fires a `voice` event. */
61
81
  voice?: boolean;
82
+ /** Slash commands — when set, typing `/` in the input opens the command
83
+ * palette and fires `slashselect`. Set as a JS property. */
84
+ slashCommands?: SlashCommandItem[];
85
+ /** Command ids to highlight as active in the palette. */
86
+ slashActiveIds?: string[];
87
+ /** Single-line palette rows. */
88
+ slashCompact?: boolean;
62
89
  }
63
90
 
64
91
  const ACTION_LABEL: Record<ChatMessageAction, string> = {
@@ -75,7 +102,8 @@ defineKitnElement<Props>('kitn-chat', {
75
102
  placeholder: 'Send a message...',
76
103
  loading: false,
77
104
  suggestions: undefined,
78
- proseSize: 'base',
105
+ suggestionMode: 'submit',
106
+ proseSize: 'sm',
79
107
  codeTheme: 'github-dark-dimmed',
80
108
  codeHighlight: true,
81
109
  chatTitle: undefined,
@@ -85,7 +113,10 @@ defineKitnElement<Props>('kitn-chat', {
85
113
  scrollButton: true,
86
114
  search: false,
87
115
  voice: false,
88
- }, (props, { dispatch }) => {
116
+ slashCommands: undefined,
117
+ slashActiveIds: undefined,
118
+ slashCompact: false,
119
+ }, (props, { dispatch, flag }) => {
89
120
  // Preserve the shadow-root portal mount from the wrapper's outer ChatConfig
90
121
  // when we nest a second ChatConfig to set proseSize/codeTheme.
91
122
  const outer = useChatConfig();
@@ -98,13 +129,22 @@ defineKitnElement<Props>('kitn-chat', {
98
129
  dispatch('submit', { value: current(), attachments: attachments() });
99
130
  setAttachments([]);
100
131
  };
101
- const handleSuggestionClick = (v: string) => { handleChange(v); dispatch('suggestionclick', { value: v }); };
132
+ const handleSuggestionClick = (v: string) => {
133
+ if ((props.suggestionMode ?? 'submit') === 'fill') {
134
+ handleChange(v);
135
+ dispatch('suggestionclick', { value: v });
136
+ } else {
137
+ // Default: behave as if the user typed the suggestion and pressed submit.
138
+ dispatch('submit', { value: v, attachments: attachments() });
139
+ setAttachments([]);
140
+ }
141
+ };
102
142
 
103
143
  const showHeader = () => !!(props.chatTitle || props.models || props.context);
104
144
  const showScrollButton = () => props.scrollButton !== false;
105
145
 
106
146
  return (
107
- <ChatConfig proseSize={props.proseSize} codeTheme={props.codeTheme} codeHighlight={props.codeHighlight} portalMount={outer.portalMount()}>
147
+ <ChatConfig proseSize={props.proseSize} codeTheme={props.codeTheme} codeHighlight={flag('codeHighlight')} portalMount={outer.portalMount()}>
108
148
  <div class="flex h-full flex-col bg-background">
109
149
  <Show when={showHeader()}>
110
150
  <header class="flex h-14 shrink-0 items-center justify-between border-b border-border px-5">
@@ -214,17 +254,21 @@ defineKitnElement<Props>('kitn-chat', {
214
254
  <DefaultPromptInput
215
255
  value={current()}
216
256
  placeholder={props.placeholder}
217
- loading={props.loading}
257
+ loading={flag('loading')}
218
258
  suggestions={props.suggestions}
219
259
  attachments={attachments()}
220
- search={props.search}
221
- voice={props.voice}
260
+ search={flag('search')}
261
+ voice={flag('voice')}
262
+ slashCommands={props.slashCommands}
263
+ slashActiveIds={props.slashActiveIds}
264
+ slashCompact={flag('slashCompact')}
222
265
  onValueChange={handleChange}
223
266
  onSubmit={handleSubmit}
224
267
  onSuggestionClick={handleSuggestionClick}
225
268
  onAttachmentsChange={setAttachments}
226
269
  onSearch={() => dispatch('search', {})}
227
270
  onVoice={() => dispatch('voice', {})}
271
+ onSlashSelect={(command) => dispatch('slashselect', { command })}
228
272
  />
229
273
  </div>
230
274
  </div>
@@ -0,0 +1,43 @@
1
+ import { Show } from 'solid-js';
2
+ import { defineKitnElement } from './define';
3
+ import { Checkpoint, CheckpointIcon, CheckpointTrigger } from '../components/checkpoint';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** Optional text beside the icon. */
7
+ label?: string;
8
+ /** Tooltip on hover. */
9
+ tooltip?: string;
10
+ /** Visual button style. */
11
+ variant?: 'ghost' | 'default' | 'outline';
12
+ /** Button size (use an `icon*` size for an icon-only checkpoint). */
13
+ size?: 'sm' | 'md' | 'lg' | 'icon' | 'icon-sm';
14
+ }
15
+
16
+ /** Events fired by `<kitn-checkpoint>`. */
17
+ interface Events {
18
+ /** The checkpoint was clicked. */
19
+ select: void;
20
+ }
21
+
22
+ /**
23
+ * `<kitn-checkpoint>` — a bookmark/checkpoint button (optional tooltip + label).
24
+ * Emits `select`.
25
+ */
26
+ defineKitnElement<Props, Events>('kitn-checkpoint', {
27
+ label: undefined,
28
+ tooltip: undefined,
29
+ variant: 'ghost',
30
+ size: 'sm',
31
+ }, (props, { dispatch }) => (
32
+ <Checkpoint>
33
+ <CheckpointTrigger
34
+ tooltip={props.tooltip}
35
+ variant={props.variant}
36
+ size={props.size}
37
+ onClick={() => dispatch('select')}
38
+ >
39
+ <CheckpointIcon />
40
+ <Show when={props.label}><span class="ml-1.5">{props.label}</span></Show>
41
+ </CheckpointTrigger>
42
+ </Checkpoint>
43
+ ));
@@ -0,0 +1,42 @@
1
+ import { defineKitnElement } from './define';
2
+ import { CodeBlock, CodeBlockCode } from '../components/code-block';
3
+ import { ChatConfig, useChatConfig, type ProseSize } from '../primitives/chat-config';
4
+
5
+ interface Props extends Record<string, unknown> {
6
+ /** The source code to render. */
7
+ code: string;
8
+ /** Language grammar (e.g. `js`, `python`). Defaults to `tsx`. */
9
+ language?: string;
10
+ /** Shiki theme name. */
11
+ codeTheme?: string;
12
+ /** Disable syntax highlighting (renders plain text, no Shiki). */
13
+ codeHighlight?: boolean;
14
+ /** Code text sizing. */
15
+ proseSize?: ProseSize;
16
+ }
17
+
18
+ /**
19
+ * `<kitn-code-block>` — one syntax-highlighted code block (with a copy button).
20
+ * Code via the `code` property; `language`/`code-theme` via attributes.
21
+ */
22
+ defineKitnElement<Props>('kitn-code-block', {
23
+ code: '',
24
+ language: undefined,
25
+ codeTheme: 'github-dark-dimmed',
26
+ codeHighlight: true,
27
+ proseSize: 'sm',
28
+ }, (props, { flag }) => {
29
+ const outer = useChatConfig();
30
+ return (
31
+ <ChatConfig
32
+ proseSize={props.proseSize}
33
+ codeTheme={props.codeTheme}
34
+ codeHighlight={flag('codeHighlight')}
35
+ portalMount={outer.portalMount()}
36
+ >
37
+ <CodeBlock>
38
+ <CodeBlockCode code={props.code} language={props.language} />
39
+ </CodeBlock>
40
+ </ChatConfig>
41
+ );
42
+ });