@kitn.ai/ui 0.28.0 → 0.29.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.
- package/README.md +1 -1
- package/dist/agent-tooling/construct/public.d.ts +15 -0
- package/dist/agent-tooling/construct/schema.d.ts +111 -0
- package/dist/construct-cli.es.js +3 -3
- package/dist/construct.d.ts +5 -0
- package/dist/construct.js +327 -0
- package/dist/core-C8fzo39E.js +1 -12
- package/dist/engine-javascript-C1x7zo1_.js +1 -141
- package/dist/mcp.es.js +1 -1
- package/package.json +16 -16
- package/bin/route.test.js +0 -58
- package/src/agent-tooling/README.md +0 -192
- package/src/agent-tooling/archetypes.ts +0 -205
- package/src/agent-tooling/catalog/README.md +0 -676
- package/src/agent-tooling/catalog/catalog-types.ts +0 -231
- package/src/agent-tooling/catalog/fabrications.ts +0 -96
- package/src/agent-tooling/catalog/invariants.ts +0 -284
- package/src/agent-tooling/catalog/labs-titles.ts +0 -114
- package/src/agent-tooling/catalog/scenarios.ts +0 -87
- package/src/agent-tooling/catalog/surfaces.ts +0 -347
- package/src/agent-tooling/construct/cli-entry.ts +0 -8
- package/src/agent-tooling/construct/cli.ts +0 -147
- package/src/agent-tooling/construct/codegen.ts +0 -1635
- package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
- package/src/agent-tooling/construct/dev.ts +0 -148
- package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
- package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
- package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
- package/src/agent-tooling/construct/schema.ts +0 -452
- package/src/agent-tooling/integrations/anthropic.ts +0 -442
- package/src/agent-tooling/integrations/cloudflare.ts +0 -218
- package/src/agent-tooling/integrations/langgraph.ts +0 -133
- package/src/agent-tooling/integrations/mastra.ts +0 -182
- package/src/agent-tooling/integrations/mock.ts +0 -148
- package/src/agent-tooling/integrations/ollama.ts +0 -97
- package/src/agent-tooling/integrations/openai.ts +0 -95
- package/src/agent-tooling/integrations/openrouter.ts +0 -82
- package/src/agent-tooling/integrations/pi.ts +0 -176
- package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
- package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
- package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
- package/src/agent-tooling/mcp/manifest.ts +0 -422
- package/src/agent-tooling/mcp/server.ts +0 -116
- package/src/agent-tooling/mcp/stdio.ts +0 -18
- package/src/agent-tooling/mcp/tools/construct.ts +0 -130
- package/src/agent-tooling/mcp/tools/debug.ts +0 -621
- package/src/agent-tooling/mcp/tools/reference.ts +0 -980
- package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
- package/src/agent-tooling/mcp/tools/theme.ts +0 -456
- package/src/agent-tooling/mcp/tools/types.ts +0 -14
- package/src/agent-tooling/mcp/validate-args.ts +0 -141
- package/src/agent-tooling/recipes/composed-thread.ts +0 -714
- package/src/agent-tooling/recipes/index.ts +0 -22
- package/src/agent-tooling/recipes/types.ts +0 -32
- package/src/agent-tooling/registry.ts +0 -132
- package/src/agent-tooling/route-emit.ts +0 -305
- package/src/agent-tooling/types.ts +0 -390
- package/src/components/artifact-card.tsx +0 -120
- package/src/components/artifact.tsx +0 -914
- package/src/components/attachment-types.ts +0 -66
- package/src/components/attachments.tsx +0 -536
- package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
- package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
- package/src/components/audio-visualizer/fit-scale.ts +0 -112
- package/src/components/audio-visualizer/index.tsx +0 -528
- package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
- package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
- package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
- package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
- package/src/components/audio-visualizer/sizes.ts +0 -54
- package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
- package/src/components/audio-visualizer/variant-bar.tsx +0 -175
- package/src/components/audio-visualizer/variant-custom.tsx +0 -344
- package/src/components/audio-visualizer/variant-grid.tsx +0 -175
- package/src/components/audio-visualizer/variant-radial.tsx +0 -163
- package/src/components/audio-visualizer/variant-wave.tsx +0 -130
- package/src/components/audio-visualizer/wave.glsl.ts +0 -130
- package/src/components/card-fallback.tsx +0 -60
- package/src/components/card-renderer.tsx +0 -220
- package/src/components/card.tsx +0 -212
- package/src/components/chain-of-thought.tsx +0 -348
- package/src/components/chat-container.tsx +0 -89
- package/src/components/chat-scope-picker.tsx +0 -57
- package/src/components/chat-thread.tsx +0 -1014
- package/src/components/checkpoint.tsx +0 -88
- package/src/components/choice-card.tsx +0 -743
- package/src/components/coachmark.tsx +0 -249
- package/src/components/code-block.tsx +0 -181
- package/src/components/composer-dom.ts +0 -159
- package/src/components/composer-highlight.ts +0 -242
- package/src/components/composer-history.ts +0 -82
- package/src/components/composer.tsx +0 -1075
- package/src/components/confirm-card.tsx +0 -402
- package/src/components/context.tsx +0 -377
- package/src/components/conversation-item.tsx +0 -241
- package/src/components/conversation-list.tsx +0 -429
- package/src/components/conversation-panel.tsx +0 -118
- package/src/components/dismissed-stub.tsx +0 -80
- package/src/components/embed.tsx +0 -200
- package/src/components/empty.tsx +0 -177
- package/src/components/feedback-bar.tsx +0 -166
- package/src/components/file-tree.tsx +0 -530
- package/src/components/file-upload.tsx +0 -167
- package/src/components/form-widgets.tsx +0 -525
- package/src/components/form.tsx +0 -1296
- package/src/components/home-panel.tsx +0 -159
- package/src/components/image.tsx +0 -65
- package/src/components/link-preview.tsx +0 -195
- package/src/components/loader.tsx +0 -367
- package/src/components/markdown.tsx +0 -162
- package/src/components/message-skills.tsx +0 -40
- package/src/components/message.tsx +0 -765
- package/src/components/model-switcher.tsx +0 -132
- package/src/components/prompt-input.tsx +0 -188
- package/src/components/prompt-suggestion.tsx +0 -150
- package/src/components/reasoning.tsx +0 -293
- package/src/components/response-compare-types.ts +0 -152
- package/src/components/response-compare.tsx +0 -455
- package/src/components/response-stream.tsx +0 -109
- package/src/components/screen.tsx +0 -181
- package/src/components/scroll-button.tsx +0 -93
- package/src/components/source.tsx +0 -192
- package/src/components/tasks-card.tsx +0 -801
- package/src/components/text-shimmer.tsx +0 -37
- package/src/components/thinking-bar.tsx +0 -50
- package/src/components/thread.tsx +0 -228
- package/src/components/toast.tsx +0 -503
- package/src/components/tool-classify.ts +0 -26
- package/src/components/tool-types.ts +0 -49
- package/src/components/tool.tsx +0 -190
- package/src/components/use-card-resolution.ts +0 -44
- package/src/components/voice-input.tsx +0 -222
- package/src/components/voice-output.tsx +0 -242
- package/src/components/widget-tab-bar.tsx +0 -73
- package/src/components/workspace-shell.tsx +0 -285
- package/src/diagnostics/hook.ts +0 -352
- package/src/diagnostics/index.ts +0 -82
- package/src/diagnostics/report-request.ts +0 -296
- package/src/elements/agent-card.tsx +0 -67
- package/src/elements/artifact.tsx +0 -194
- package/src/elements/attachments.tsx +0 -140
- package/src/elements/audio-visualizer.tsx +0 -227
- package/src/elements/autoloader.ts +0 -89
- package/src/elements/avatar.tsx +0 -33
- package/src/elements/badge.tsx +0 -32
- package/src/elements/button.tsx +0 -149
- package/src/elements/card.tsx +0 -133
- package/src/elements/cards.tsx +0 -266
- package/src/elements/chain-of-thought.tsx +0 -141
- package/src/elements/chat-scope-picker.tsx +0 -65
- package/src/elements/chat-types.ts +0 -102
- package/src/elements/chat-workspace.tsx +0 -188
- package/src/elements/chat.tsx +0 -304
- package/src/elements/checkbox-group.tsx +0 -164
- package/src/elements/checkbox.tsx +0 -134
- package/src/elements/checkpoint.tsx +0 -47
- package/src/elements/choice.tsx +0 -100
- package/src/elements/coachmark.tsx +0 -90
- package/src/elements/code-block.tsx +0 -60
- package/src/elements/command.tsx +0 -197
- package/src/elements/compare.tsx +0 -111
- package/src/elements/compiled.css +0 -2
- package/src/elements/composer.tsx +0 -162
- package/src/elements/confirm-card.tsx +0 -76
- package/src/elements/context-meter.tsx +0 -107
- package/src/elements/conversation-item.tsx +0 -120
- package/src/elements/conversation-list.tsx +0 -203
- package/src/elements/css.ts +0 -5
- package/src/elements/default-input.tsx +0 -392
- package/src/elements/define-entry.ts +0 -12
- package/src/elements/define.tsx +0 -563
- package/src/elements/diagnostic-events.ts +0 -114
- package/src/elements/dialog.tsx +0 -145
- package/src/elements/disclosure.ts +0 -95
- package/src/elements/dock.tsx +0 -215
- package/src/elements/dropdown.tsx +0 -123
- package/src/elements/editable-label.tsx +0 -140
- package/src/elements/element-data-types.ts +0 -152
- package/src/elements/element-diagnostics.ts +0 -392
- package/src/elements/element-manifest.json +0 -354
- package/src/elements/element-nonscalar.json +0 -187
- package/src/elements/element-types.d.ts +0 -4353
- package/src/elements/embed.tsx +0 -35
- package/src/elements/empty.tsx +0 -29
- package/src/elements/feedback-bar.tsx +0 -66
- package/src/elements/file-tree.tsx +0 -62
- package/src/elements/file-upload.tsx +0 -44
- package/src/elements/form.tsx +0 -94
- package/src/elements/hover-card.tsx +0 -80
- package/src/elements/icon.tsx +0 -41
- package/src/elements/image.tsx +0 -32
- package/src/elements/input.tsx +0 -492
- package/src/elements/kbd.tsx +0 -45
- package/src/elements/link-preview.tsx +0 -34
- package/src/elements/loader.tsx +0 -25
- package/src/elements/markdown.tsx +0 -38
- package/src/elements/menu.tsx +0 -241
- package/src/elements/message-skills.tsx +0 -83
- package/src/elements/message.tsx +0 -361
- package/src/elements/model-switcher.tsx +0 -121
- package/src/elements/nav.tsx +0 -87
- package/src/elements/notice.tsx +0 -53
- package/src/elements/pane-grid.tsx +0 -111
- package/src/elements/pane-group.tsx +0 -119
- package/src/elements/pane.tsx +0 -129
- package/src/elements/popover.tsx +0 -81
- package/src/elements/progress-bar.tsx +0 -39
- package/src/elements/prompt-dock.tsx +0 -82
- package/src/elements/prompt-input.tsx +0 -240
- package/src/elements/prompt-suggestions.tsx +0 -113
- package/src/elements/radio-group.tsx +0 -129
- package/src/elements/reasoning.tsx +0 -71
- package/src/elements/register-impl.ts +0 -148
- package/src/elements/register.ts +0 -76
- package/src/elements/remote.tsx +0 -225
- package/src/elements/resizable.globals.d.ts +0 -36
- package/src/elements/resizable.tsx +0 -704
- package/src/elements/response-stream.tsx +0 -40
- package/src/elements/screen.tsx +0 -90
- package/src/elements/scroll-area.tsx +0 -33
- package/src/elements/scroll-button.tsx +0 -179
- package/src/elements/search.tsx +0 -185
- package/src/elements/segmented.tsx +0 -129
- package/src/elements/select.tsx +0 -165
- package/src/elements/separator.tsx +0 -36
- package/src/elements/setting-item.tsx +0 -50
- package/src/elements/settings-group.tsx +0 -37
- package/src/elements/skeleton.tsx +0 -45
- package/src/elements/slider.tsx +0 -171
- package/src/elements/slot-text.ts +0 -73
- package/src/elements/slots.ts +0 -787
- package/src/elements/source.tsx +0 -126
- package/src/elements/status.tsx +0 -42
- package/src/elements/styles.css +0 -328
- package/src/elements/switch.tsx +0 -118
- package/src/elements/tabs.tsx +0 -95
- package/src/elements/tasks.tsx +0 -103
- package/src/elements/text-shimmer.tsx +0 -28
- package/src/elements/thinking-bar.tsx +0 -43
- package/src/elements/thread.tsx +0 -139
- package/src/elements/toast.tsx +0 -106
- package/src/elements/tool.tsx +0 -78
- package/src/elements/tooltip.tsx +0 -85
- package/src/elements/validate-messages.ts +0 -76
- package/src/elements/voice-input.tsx +0 -100
- package/src/elements/voice-output.tsx +0 -82
- package/src/index.ts +0 -430
- package/src/primitives/audio-bands.ts +0 -174
- package/src/primitives/card-component-types.ts +0 -63
- package/src/primitives/card-contract.ts +0 -80
- package/src/primitives/card-data-types.ts +0 -361
- package/src/primitives/card-host.tsx +0 -37
- package/src/primitives/card-recovery.ts +0 -160
- package/src/primitives/card-registry.tsx +0 -78
- package/src/primitives/card-resolution.ts +0 -36
- package/src/primitives/card-routing.ts +0 -85
- package/src/primitives/card-schemas/artifact.schema.json +0 -92
- package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
- package/src/primitives/card-schemas/card-event.schema.json +0 -12
- package/src/primitives/card-schemas/choice.schema.json +0 -75
- package/src/primitives/card-schemas/confirm.schema.json +0 -65
- package/src/primitives/card-schemas/embed.schema.json +0 -63
- package/src/primitives/card-schemas/form.result.schema.json +0 -7
- package/src/primitives/card-schemas/form.schema.json +0 -53
- package/src/primitives/card-schemas/link.schema.json +0 -54
- package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
- package/src/primitives/card-schemas/tasks.schema.json +0 -83
- package/src/primitives/card-tags.ts +0 -61
- package/src/primitives/card-validate-cards.ts +0 -255
- package/src/primitives/card-validate-schemas.ts +0 -58
- package/src/primitives/card-validate.ts +0 -131
- package/src/primitives/chat-config.tsx +0 -103
- package/src/primitives/composer-model.ts +0 -49
- package/src/primitives/composer-triggers.ts +0 -35
- package/src/primitives/controllable.ts +0 -21
- package/src/primitives/conversation-store.ts +0 -248
- package/src/primitives/create-kai-chat.ts +0 -66
- package/src/primitives/create-tween.ts +0 -266
- package/src/primitives/embed-providers.ts +0 -254
- package/src/primitives/field-mask.ts +0 -256
- package/src/primitives/field-semantics.ts +0 -115
- package/src/primitives/highlighter.ts +0 -157
- package/src/primitives/input-mask.ts +0 -853
- package/src/primitives/link-preview.ts +0 -87
- package/src/primitives/message-feedback.ts +0 -124
- package/src/primitives/pdf-preview.ts +0 -121
- package/src/primitives/toast-store.ts +0 -338
- package/src/primitives/url-scheme-policy.ts +0 -70
- package/src/primitives/use-audio-analysis.ts +0 -340
- package/src/primitives/use-auto-resize.ts +0 -31
- package/src/primitives/use-resize-observer.ts +0 -45
- package/src/primitives/use-sequencer.ts +0 -72
- package/src/primitives/use-speech-recognition.ts +0 -146
- package/src/primitives/use-stick-to-bottom.ts +0 -75
- package/src/primitives/use-text-stream.ts +0 -143
- package/src/primitives/use-voice-recorder.ts +0 -62
- package/src/primitives/visualizer-sequences.ts +0 -350
- package/src/remote/host-embed.ts +0 -345
- package/src/remote/index.ts +0 -2
- package/src/remote/origin.ts +0 -30
- package/src/remote/provider-runtime.ts +0 -262
- package/src/remote/provider.ts +0 -2
- package/src/remote/validate.ts +0 -22
- package/src/remote/version.ts +0 -12
- package/src/remote/wire.ts +0 -48
- package/src/schemas/from-tool-call.ts +0 -151
- package/src/schemas/index.ts +0 -341
- package/src/schemas/provider-subsets.ts +0 -538
- package/src/schemas/registry.ts +0 -355
- package/src/schemas/tool-defs.ts +0 -793
- package/src/solid.ts +0 -173
- package/src/state/index.ts +0 -44
- package/src/state/messages.ts +0 -53
- package/src/state/mock.ts +0 -297
- package/src/state/parts.ts +0 -295
- package/src/state/persistence.ts +0 -180
- package/src/state/stream.ts +0 -246
- package/src/state/suggestions.ts +0 -9
- package/src/state/threads.ts +0 -142
- package/src/test-utils/fake-clock.ts +0 -88
- package/src/types.ts +0 -95
- package/src/ui/action-icons.ts +0 -53
- package/src/ui/agent-card.tsx +0 -218
- package/src/ui/avatar.tsx +0 -23
- package/src/ui/badge.tsx +0 -21
- package/src/ui/button.tsx +0 -49
- package/src/ui/card.tsx +0 -245
- package/src/ui/checkbox-group.tsx +0 -153
- package/src/ui/checkbox.tsx +0 -63
- package/src/ui/collapsible.tsx +0 -163
- package/src/ui/command.tsx +0 -103
- package/src/ui/dialog.tsx +0 -223
- package/src/ui/dock.tsx +0 -630
- package/src/ui/dropdown.tsx +0 -636
- package/src/ui/editable-label.tsx +0 -112
- package/src/ui/hover-card.tsx +0 -345
- package/src/ui/icon.tsx +0 -167
- package/src/ui/input.tsx +0 -391
- package/src/ui/kbd.tsx +0 -118
- package/src/ui/nav.tsx +0 -282
- package/src/ui/notice.tsx +0 -84
- package/src/ui/overlay.tsx +0 -277
- package/src/ui/pane-grid.tsx +0 -117
- package/src/ui/pane-group.tsx +0 -291
- package/src/ui/pane.tsx +0 -193
- package/src/ui/popover.tsx +0 -106
- package/src/ui/progress-bar.tsx +0 -83
- package/src/ui/prompt-dock.tsx +0 -145
- package/src/ui/radio.tsx +0 -150
- package/src/ui/resizable.tsx +0 -693
- package/src/ui/scroll-area.tsx +0 -26
- package/src/ui/segmented.tsx +0 -109
- package/src/ui/select.tsx +0 -168
- package/src/ui/separator.tsx +0 -10
- package/src/ui/settings-group.tsx +0 -67
- package/src/ui/skeleton.tsx +0 -73
- package/src/ui/slider.tsx +0 -178
- package/src/ui/status.tsx +0 -48
- package/src/ui/switch.tsx +0 -119
- package/src/ui/tabs.tsx +0 -168
- package/src/ui/textarea.tsx +0 -21
- package/src/ui/tooltip.tsx +0 -118
- package/src/utils/cn.ts +0 -30
- package/src/wire/chunk.ts +0 -248
- package/src/wire/consume.ts +0 -599
- package/src/wire/diagnostics.ts +0 -727
- package/src/wire/encode-probe.ts +0 -214
- package/src/wire/encode.ts +0 -947
- package/src/wire/files.ts +0 -342
- package/src/wire/formats/anthropic.ts +0 -274
- package/src/wire/formats/openai.ts +0 -228
- package/src/wire/index.ts +0 -95
- package/src/wire/media-types.ts +0 -344
- package/src/wire/read.ts +0 -396
- package/src/wire/sink-helpers.ts +0 -50
- package/src/wire/sse.ts +0 -153
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import { type JSX, type Accessor, splitProps, createSignal, createContext, useContext, createEffect, createUniqueId, onCleanup, Show } from 'solid-js';
|
|
2
|
-
import { cn } from '../utils/cn';
|
|
3
|
-
import { ChevronDown } from 'lucide-solid';
|
|
4
|
-
import { Markdown } from './markdown';
|
|
5
|
-
import { observeContentHeight } from '../primitives/use-resize-observer';
|
|
6
|
-
import { TextShimmer } from './text-shimmer';
|
|
7
|
-
|
|
8
|
-
interface ReasoningContextValue {
|
|
9
|
-
isOpen: () => boolean;
|
|
10
|
-
onOpenChange: (open: boolean) => void;
|
|
11
|
-
disabled: () => boolean;
|
|
12
|
-
/** Id minted on the root and shared by the trigger's `aria-controls` and the
|
|
13
|
-
* content's `id` — the same wiring `Collapsible` uses, so a screen reader can
|
|
14
|
-
* name the panel this disclosure toggles. */
|
|
15
|
-
contentId: string;
|
|
16
|
-
/** ReasoningTrigger registers its button here on mount; the content reads it
|
|
17
|
-
* when a panel holding focus collapses, so focus has somewhere to land. */
|
|
18
|
-
registerTrigger: (el: HTMLElement | undefined) => void;
|
|
19
|
-
/** The registered trigger. Deliberately a plain getter over a mutable
|
|
20
|
-
* variable, NOT a signal — registration must not re-run the content effect. */
|
|
21
|
-
trigger: () => HTMLElement | undefined;
|
|
22
|
-
/** Whether the reasoning is currently streaming — read by `ReasoningTrigger`
|
|
23
|
-
* to decide how its own label renders (shimmer while streaming, plain
|
|
24
|
-
* neutral text once settled). Kit-decides-HOW: every consumer gets the same
|
|
25
|
-
* "Thinking…" shimmer behavior for free, with no prop of their own. */
|
|
26
|
-
isStreaming: () => boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Imperative open controller, handed to a parent (the kai-reasoning facade) via
|
|
30
|
-
* `controllerRef` so it can drive/observe open state — mirrors
|
|
31
|
-
* CollapsibleController/HoverCardController. */
|
|
32
|
-
export interface ReasoningController { open: Accessor<boolean>; setOpen: (v: boolean) => void; }
|
|
33
|
-
|
|
34
|
-
const ReasoningContext = createContext<ReasoningContextValue>();
|
|
35
|
-
|
|
36
|
-
function useReasoningContext() {
|
|
37
|
-
const context = useContext(ReasoningContext);
|
|
38
|
-
if (!context) {
|
|
39
|
-
throw new Error('useReasoningContext must be used within a Reasoning provider');
|
|
40
|
-
}
|
|
41
|
-
return context;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// --- Reasoning (Root) ---
|
|
45
|
-
|
|
46
|
-
export interface ReasoningProps {
|
|
47
|
-
children: JSX.Element;
|
|
48
|
-
class?: string;
|
|
49
|
-
open?: boolean;
|
|
50
|
-
/** Initial open state (uncontrolled seed). */
|
|
51
|
-
defaultOpen?: boolean;
|
|
52
|
-
onOpenChange?: (open: boolean) => void;
|
|
53
|
-
isStreaming?: boolean;
|
|
54
|
-
/** Gate the trigger — programmatic control via the controller still works. */
|
|
55
|
-
disabled?: boolean;
|
|
56
|
-
/** Receive the open controller (open accessor + setOpen) once mounted. */
|
|
57
|
-
controllerRef?: (api: ReasoningController) => void;
|
|
58
|
-
/** Keep auto-opening while streaming and auto-closing when it settles (the
|
|
59
|
-
* pre-Task-19f `full` behavior). Default false: the panel starts per
|
|
60
|
-
* `defaultOpen` and stays exactly where the user leaves it — streaming only
|
|
61
|
-
* changes the trigger's label (shimmer), never the open state, matching the
|
|
62
|
-
* "closed chip, expand on click" default (owner ruling, 2026-08-26). */
|
|
63
|
-
openOnStream?: boolean;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function Reasoning(props: ReasoningProps) {
|
|
67
|
-
const [local] = splitProps(props, ['children', 'class', 'open', 'defaultOpen', 'onOpenChange', 'isStreaming', 'disabled', 'controllerRef', 'openOnStream']);
|
|
68
|
-
const [internalOpen, setInternalOpen] = createSignal(local.defaultOpen ?? false);
|
|
69
|
-
const [wasAutoOpened, setWasAutoOpened] = createSignal(false);
|
|
70
|
-
|
|
71
|
-
const isControlled = () => local.open !== undefined;
|
|
72
|
-
const isOpen = () => (isControlled() ? local.open! : internalOpen());
|
|
73
|
-
const disabled = () => !!local.disabled;
|
|
74
|
-
|
|
75
|
-
// The single open-mutate path, shared by the trigger and the imperative
|
|
76
|
-
// controller. In controlled mode we don't touch internal state; the consumer
|
|
77
|
-
// reflects via the `open` prop and observes via onOpenChange.
|
|
78
|
-
const handleOpenChange = (newOpen: boolean) => {
|
|
79
|
-
if (isOpen() === newOpen) return;
|
|
80
|
-
if (!isControlled()) {
|
|
81
|
-
setInternalOpen(newOpen);
|
|
82
|
-
}
|
|
83
|
-
local.onOpenChange?.(newOpen);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
createEffect(() => {
|
|
87
|
-
if (!local.openOnStream) return;
|
|
88
|
-
const streaming = local.isStreaming;
|
|
89
|
-
if (streaming && !wasAutoOpened()) {
|
|
90
|
-
if (!isControlled()) setInternalOpen(true);
|
|
91
|
-
setWasAutoOpened(true);
|
|
92
|
-
}
|
|
93
|
-
if (!streaming && wasAutoOpened()) {
|
|
94
|
-
if (!isControlled()) setInternalOpen(false);
|
|
95
|
-
setWasAutoOpened(false);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Hand the controller up once. setOpen routes through handleOpenChange so it
|
|
100
|
-
// notifies onOpenChange and respects controlled/uncontrolled mode. Not gated
|
|
101
|
-
// by disabled — the facade's wireDisclosure applies disabled-gating itself.
|
|
102
|
-
local.controllerRef?.({ open: isOpen, setOpen: handleOpenChange });
|
|
103
|
-
|
|
104
|
-
const contentId = createUniqueId();
|
|
105
|
-
|
|
106
|
-
// The trigger element, for the focus hand-off in ReasoningContent. Read once,
|
|
107
|
-
// at the moment a panel collapses — a signal would only add a spurious
|
|
108
|
-
// dependency to the content effect when the trigger mounts.
|
|
109
|
-
let triggerEl: HTMLElement | undefined;
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<ReasoningContext.Provider
|
|
113
|
-
value={{
|
|
114
|
-
isOpen,
|
|
115
|
-
onOpenChange: handleOpenChange,
|
|
116
|
-
disabled,
|
|
117
|
-
contentId,
|
|
118
|
-
registerTrigger: (el) => { triggerEl = el; },
|
|
119
|
-
trigger: () => triggerEl,
|
|
120
|
-
isStreaming: () => !!local.isStreaming,
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
<div class={local.class}>{local.children}</div>
|
|
124
|
-
</ReasoningContext.Provider>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// --- ReasoningTrigger ---
|
|
129
|
-
|
|
130
|
-
export interface ReasoningTriggerProps extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
131
|
-
children: JSX.Element;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function ReasoningTrigger(props: ReasoningTriggerProps) {
|
|
135
|
-
// `ref` is split out rather than left in `rest`: the trigger takes its own ref
|
|
136
|
-
// to register as the panel's focus-return target, and a spread `ref` would
|
|
137
|
-
// silently replace it. The consumer's ref is forwarded below instead.
|
|
138
|
-
const [local, rest] = splitProps(props, ['children', 'class', 'ref']);
|
|
139
|
-
const { isOpen, onOpenChange, disabled, contentId, registerTrigger, isStreaming } = useReasoningContext();
|
|
140
|
-
|
|
141
|
-
return (
|
|
142
|
-
// The same disclosure contract as CollapsibleTrigger: an explicit
|
|
143
|
-
// `type="button"` (so a trigger inside a form never submits it),
|
|
144
|
-
// `aria-expanded` + `aria-controls` so assistive tech can read the state and
|
|
145
|
-
// find the panel, and the `data-state` / `data-expanded` / `data-closed`
|
|
146
|
-
// handles styling and tests hang off. `{...rest}` stays LAST so a consumer
|
|
147
|
-
// can still override any of them.
|
|
148
|
-
<button
|
|
149
|
-
ref={(el) => {
|
|
150
|
-
registerTrigger(el);
|
|
151
|
-
// Solid hands a component's `ref` down as a callback; tolerate a raw
|
|
152
|
-
// element for the hand-written case.
|
|
153
|
-
if (typeof local.ref === 'function') (local.ref as (el: HTMLButtonElement) => void)(el);
|
|
154
|
-
}}
|
|
155
|
-
type="button"
|
|
156
|
-
class={cn('flex cursor-pointer items-center gap-2 text-meta', local.class)}
|
|
157
|
-
disabled={disabled() || undefined}
|
|
158
|
-
aria-expanded={isOpen()}
|
|
159
|
-
aria-controls={contentId}
|
|
160
|
-
data-expanded={isOpen() ? '' : undefined}
|
|
161
|
-
data-closed={isOpen() ? undefined : ''}
|
|
162
|
-
data-state={isOpen() ? 'open' : 'closed'}
|
|
163
|
-
onClick={() => { if (!disabled()) onOpenChange(!isOpen()); }}
|
|
164
|
-
{...rest}
|
|
165
|
-
>
|
|
166
|
-
{/* Content/chrome, not a control: was `text-primary`, the BRAND token, so
|
|
167
|
-
a consumer's branded --kai-color-primary bled onto the "Thinking"
|
|
168
|
-
label. `text-foreground` matches the sibling `<Tool>` disclosure
|
|
169
|
-
trigger's label (rendered via `Button variant="ghost"`, itself
|
|
170
|
-
`text-foreground`) — the established neutral for a disclosure
|
|
171
|
-
trigger's own label text in this codebase.
|
|
172
|
-
Kit-decides-HOW: while streaming, the label renders with the kit's
|
|
173
|
-
own `TextShimmer` (the common "Thinking…" shimmer, already used by
|
|
174
|
-
`Loader`'s `text-shimmer` variant) instead of static text; once
|
|
175
|
-
settled it's the plain neutral span above. Every consumer gets this
|
|
176
|
-
for free — no prop of their own. */}
|
|
177
|
-
<Show when={isStreaming()} fallback={<span class="text-foreground">{local.children}</span>}>
|
|
178
|
-
<TextShimmer class="font-medium">{local.children}</TextShimmer>
|
|
179
|
-
</Show>
|
|
180
|
-
<div
|
|
181
|
-
class={cn(
|
|
182
|
-
'transform transition-transform',
|
|
183
|
-
isOpen() ? 'rotate-180' : ''
|
|
184
|
-
)}
|
|
185
|
-
>
|
|
186
|
-
<ChevronDown class="size-4" />
|
|
187
|
-
</div>
|
|
188
|
-
</button>
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// --- ReasoningContent ---
|
|
193
|
-
|
|
194
|
-
export interface ReasoningContentProps extends JSX.HTMLAttributes<HTMLDivElement> {
|
|
195
|
-
children: JSX.Element;
|
|
196
|
-
markdown?: boolean;
|
|
197
|
-
contentClass?: string;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function ReasoningContent(props: ReasoningContentProps) {
|
|
201
|
-
const [local, rest] = splitProps(props, ['children', 'class', 'contentClass', 'markdown']);
|
|
202
|
-
const { isOpen, contentId, trigger } = useReasoningContext();
|
|
203
|
-
|
|
204
|
-
let contentRef: HTMLDivElement | undefined;
|
|
205
|
-
let innerRef: HTMLDivElement | undefined;
|
|
206
|
-
|
|
207
|
-
/** Hand focus back to the trigger when the panel about to go inert is holding
|
|
208
|
-
* it. Chromium keeps `activeElement` on the now-inert node for the rest of
|
|
209
|
-
* the task and then resets the document's focus to `<body>` — so without this
|
|
210
|
-
* a user who was inside the panel loses the focus ring entirely and their
|
|
211
|
-
* next Tab restarts from the top. `Collapsible` has exactly this gap: it sets
|
|
212
|
-
* `bool:inert` and never looks at where focus was. Runs BEFORE the attribute
|
|
213
|
-
* lands, so the move is ours rather than the browser's fallback. */
|
|
214
|
-
const releaseFocus = () => {
|
|
215
|
-
if (!contentRef) return;
|
|
216
|
-
// getRootNode(), not `document`: inside the <kai-reasoning> shadow root
|
|
217
|
-
// document.activeElement only ever resolves to the HOST element.
|
|
218
|
-
const root = contentRef.getRootNode() as Document | ShadowRoot;
|
|
219
|
-
const active = root.activeElement;
|
|
220
|
-
// A disabled trigger cannot take focus; the browser then falls back to
|
|
221
|
-
// <body>, which is no worse than not trying.
|
|
222
|
-
if (active && contentRef.contains(active)) trigger()?.focus();
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
createEffect(() => {
|
|
226
|
-
if (!contentRef || !innerRef) return;
|
|
227
|
-
|
|
228
|
-
const dispose = observeContentHeight(innerRef, () => {
|
|
229
|
-
if (contentRef && innerRef && isOpen()) {
|
|
230
|
-
contentRef.style.maxHeight = `${innerRef.scrollHeight}px`;
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
if (isOpen()) {
|
|
235
|
-
contentRef.style.maxHeight = `${innerRef.scrollHeight}px`;
|
|
236
|
-
// Dropped in the same tick as the open, NOT on transitionend: the panel is
|
|
237
|
-
// interactive from the first frame, so an inert outliving the animation can
|
|
238
|
-
// never swallow the first click or Tab into it.
|
|
239
|
-
contentRef.removeAttribute('inert');
|
|
240
|
-
} else {
|
|
241
|
-
contentRef.style.maxHeight = '0px';
|
|
242
|
-
releaseFocus();
|
|
243
|
-
// Applied in the same tick as the collapse, again NOT on transitionend:
|
|
244
|
-
// `inert` has no visual effect of its own, so the 150ms max-height
|
|
245
|
-
// transition still runs to completion underneath it (verified in Chromium:
|
|
246
|
-
// transitionend fires and the final computed max-height is reached), while
|
|
247
|
-
// the a11y tree matches the collapsed intent immediately instead of 150ms
|
|
248
|
-
// late. setAttribute rather than the `inert` IDL property because Solid
|
|
249
|
-
// routes `inert` through Properties (el.inert = …), which jsdom does not
|
|
250
|
-
// reflect back to an attribute — the same reason Collapsible uses `bool:`.
|
|
251
|
-
contentRef.setAttribute('inert', '');
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
onCleanup(dispose);
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
// `id` matches the trigger's `aria-controls`; `data-state` gives styling and
|
|
259
|
-
// tests an attribute handle on the panel instead of forcing them to read the
|
|
260
|
-
// animated `max-height`. `inert` while collapsed (as CollapsibleContent does)
|
|
261
|
-
// is owned by the effect above, not bound here — it has to be sequenced
|
|
262
|
-
// against the focus hand-off, and a second reactive owner would race it.
|
|
263
|
-
// `{...rest}` stays LAST so a consumer can still override.
|
|
264
|
-
<div
|
|
265
|
-
ref={contentRef}
|
|
266
|
-
id={contentId}
|
|
267
|
-
class={cn(
|
|
268
|
-
'overflow-hidden transition-[max-height] duration-150 ease-out',
|
|
269
|
-
local.class
|
|
270
|
-
)}
|
|
271
|
-
style={{ 'max-height': '0px' }}
|
|
272
|
-
data-state={isOpen() ? 'open' : 'closed'}
|
|
273
|
-
{...rest}
|
|
274
|
-
>
|
|
275
|
-
<div
|
|
276
|
-
ref={innerRef}
|
|
277
|
-
class={cn(
|
|
278
|
-
// Markdown content is styled by the token-based `.chat-markdown` (see
|
|
279
|
-
// Markdown component), which themes via design tokens — so no Tailwind
|
|
280
|
-
// `prose`/`dark:prose-invert` is needed (those wouldn't follow a scoped theme).
|
|
281
|
-
'text-muted-foreground text-body',
|
|
282
|
-
local.contentClass
|
|
283
|
-
)}
|
|
284
|
-
>
|
|
285
|
-
<Show when={local.markdown} fallback={local.children}>
|
|
286
|
-
<Markdown content={local.children as string} />
|
|
287
|
-
</Show>
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export { Reasoning, ReasoningTrigger, ReasoningContent };
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
// Types + pure helpers for the dual-response comparison card (`ResponseCompare` /
|
|
2
|
-
// `<kai-compare>`). A compare presents exactly TWO assistant candidates for the
|
|
3
|
-
// same prompt side-by-side; the user picks the better one (a COMMIT, not a
|
|
4
|
-
// Submit), which the consumer turns into a `(prompt, chosen, rejected)`
|
|
5
|
-
// preference pair. Kept framework-agnostic and DOM-free so it unit-tests in
|
|
6
|
-
// isolation.
|
|
7
|
-
import type { ToolPart } from './tool';
|
|
8
|
-
import type { AttachmentData } from './attachments';
|
|
9
|
-
import type { MessagePart } from '../elements/chat-types';
|
|
10
|
-
|
|
11
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
-
// Types
|
|
13
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
/** One assistant response in a comparison. Rendered identically to an assistant
|
|
16
|
-
* message (reasoning + tools + attachments + markdown content) via `MessageBody`. */
|
|
17
|
-
export interface CompareCandidate {
|
|
18
|
-
/** Stable id, unique within the pair; echoed back in the selection. */
|
|
19
|
-
id: string;
|
|
20
|
-
/** The response text/markdown. */
|
|
21
|
-
content: string;
|
|
22
|
-
/** Optional collapsible reasoning block. */
|
|
23
|
-
reasoning?: { text: string; label?: string };
|
|
24
|
-
/** Tool-call parts rendered above the content. */
|
|
25
|
-
tools?: ToolPart[];
|
|
26
|
-
/** Inline attachment previews rendered above the content. */
|
|
27
|
-
attachments?: AttachmentData[];
|
|
28
|
-
/** Optional short column label (e.g. "A" / "B" / "Concise"). */
|
|
29
|
-
label?: string;
|
|
30
|
-
/** Optional model name shown as a sub-label. */
|
|
31
|
-
model?: string;
|
|
32
|
-
/** When true this candidate is still streaming — its pick control is disabled
|
|
33
|
-
* and a per-column shimmer shows until it (and its sibling) settle. */
|
|
34
|
-
streaming?: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** The two-candidate tuple. Enforced at the type level; validated at runtime by
|
|
38
|
-
* `normalizeCandidates`. */
|
|
39
|
-
export type ComparePair = [CompareCandidate, CompareCandidate];
|
|
40
|
-
|
|
41
|
-
/** How the card collapses once the user picks. `'winner'` (default) shows just the
|
|
42
|
-
* chosen candidate's body; `'none'` keeps both columns but marks the choice. */
|
|
43
|
-
export type CompareCollapse = 'winner' | 'none';
|
|
44
|
-
|
|
45
|
-
/** The compare definition (set as the `data` JS property on `<kai-compare>`). */
|
|
46
|
-
export interface ResponseCompareData {
|
|
47
|
-
/** Optional prompt/question shown above the two columns. */
|
|
48
|
-
prompt?: string;
|
|
49
|
-
/** Exactly two candidates: [A, B]. */
|
|
50
|
-
candidates: ComparePair;
|
|
51
|
-
/** Collapse behaviour after a pick. Default `'winner'`. */
|
|
52
|
-
collapse?: CompareCollapse;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** Emitted (and re-hydratable) when the user picks. The consumer pairs
|
|
56
|
-
* `(prompt, chosenId, rejectedIds)` for preference capture / RLHF. */
|
|
57
|
-
export interface CompareSelection {
|
|
58
|
-
/** The id of the chosen candidate. */
|
|
59
|
-
chosenId: string;
|
|
60
|
-
/** The id(s) of the rejected candidate(s) — for a pair, the other one. */
|
|
61
|
-
rejectedIds: string[];
|
|
62
|
-
/** Epoch ms when the pick was made. */
|
|
63
|
-
at?: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
67
|
-
// Pure helpers (unit-tested in isolation).
|
|
68
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Validate + normalize the candidate list. A compare requires EXACTLY two
|
|
72
|
-
* candidates with unique, non-empty string ids. Returns the usable pair, or an
|
|
73
|
-
* error message when the definition is unusable (rendered as an inline error /
|
|
74
|
-
* emitted as a `kai-error`). Modeled on `normalizeOptions`.
|
|
75
|
-
*/
|
|
76
|
-
export function normalizeCandidates(candidates: unknown): {
|
|
77
|
-
candidates: ComparePair | null;
|
|
78
|
-
error?: string;
|
|
79
|
-
} {
|
|
80
|
-
if (!Array.isArray(candidates)) {
|
|
81
|
-
return { candidates: null, error: "This comparison couldn't be displayed." };
|
|
82
|
-
}
|
|
83
|
-
if (candidates.length !== 2) {
|
|
84
|
-
return {
|
|
85
|
-
candidates: null,
|
|
86
|
-
error: `A comparison needs exactly two candidates (got ${candidates.length}).`,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
const out: CompareCandidate[] = [];
|
|
90
|
-
const seen = new Set<string>();
|
|
91
|
-
for (const c of candidates) {
|
|
92
|
-
if (!c || typeof c !== 'object') {
|
|
93
|
-
return { candidates: null, error: "This comparison couldn't be displayed." };
|
|
94
|
-
}
|
|
95
|
-
const cand = c as Partial<CompareCandidate>;
|
|
96
|
-
if (typeof cand.id !== 'string' || cand.id.length === 0) {
|
|
97
|
-
return { candidates: null, error: 'Each candidate needs a non-empty id.' };
|
|
98
|
-
}
|
|
99
|
-
if (seen.has(cand.id)) {
|
|
100
|
-
return { candidates: null, error: `Duplicate candidate id "${cand.id}".` };
|
|
101
|
-
}
|
|
102
|
-
seen.add(cand.id);
|
|
103
|
-
out.push({
|
|
104
|
-
id: cand.id,
|
|
105
|
-
content: typeof cand.content === 'string' ? cand.content : '',
|
|
106
|
-
reasoning: cand.reasoning,
|
|
107
|
-
tools: Array.isArray(cand.tools) ? cand.tools : undefined,
|
|
108
|
-
attachments: Array.isArray(cand.attachments) ? cand.attachments : undefined,
|
|
109
|
-
label: typeof cand.label === 'string' ? cand.label : undefined,
|
|
110
|
-
model: typeof cand.model === 'string' ? cand.model : undefined,
|
|
111
|
-
streaming: cand.streaming === true,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
return { candidates: [out[0], out[1]] };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Build the selection payload for choosing `chosenId` out of the pair (the other
|
|
118
|
-
* candidate is the rejected one). Stamps `at` with the current time. */
|
|
119
|
-
export function buildSelection(pair: ComparePair, chosenId: string): CompareSelection {
|
|
120
|
-
const rejectedIds = pair.filter((c) => c.id !== chosenId).map((c) => c.id);
|
|
121
|
-
return { chosenId, rejectedIds, at: Date.now() };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/** Whether any candidate in the pair is still streaming (pick stays disabled). */
|
|
125
|
-
export function isAnyStreaming(pair: ComparePair | null): boolean {
|
|
126
|
-
if (!pair) return false;
|
|
127
|
-
return pair.some((c) => c.streaming === true);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** Map a `CompareCandidate`'s flat legacy fields (content/reasoning/tools/
|
|
131
|
-
* attachments) into the ordered `MessagePart[]` `MessageBody` now renders.
|
|
132
|
-
* `CompareCandidate` stays its own independent shape (it is `<kai-compare>`'s
|
|
133
|
-
* public `data` JSON, not a `ChatMessage`). This is purely a render-time
|
|
134
|
-
* adapter, applied at each `MessageBody` call site. Order: reasoning (the
|
|
135
|
-
* thinking trace) → tool calls (the actions taken) → attachments (anything
|
|
136
|
-
* produced) → the final text answer, mirroring how an assistant turn reads. */
|
|
137
|
-
export function candidateToParts(candidate: CompareCandidate): MessagePart[] {
|
|
138
|
-
const parts: MessagePart[] = [];
|
|
139
|
-
if (candidate.reasoning) {
|
|
140
|
-
parts.push({ type: 'reasoning', text: candidate.reasoning.text, label: candidate.reasoning.label });
|
|
141
|
-
}
|
|
142
|
-
for (const tool of candidate.tools ?? []) {
|
|
143
|
-
parts.push({ type: 'tool', tool });
|
|
144
|
-
}
|
|
145
|
-
for (const attachment of candidate.attachments ?? []) {
|
|
146
|
-
parts.push({ type: 'file', attachment });
|
|
147
|
-
}
|
|
148
|
-
if (candidate.content) {
|
|
149
|
-
parts.push({ type: 'text', text: candidate.content });
|
|
150
|
-
}
|
|
151
|
-
return parts;
|
|
152
|
-
}
|