import type { Meta, StoryObj } from 'storybook-solidjs-vite'; import { onMount } from 'solid-js'; import { ChatContainer, ChatContainerContent, ChatContainerScrollAnchor, Message, MessageAvatar, MessageContent, MessageActions, PromptInput, PromptInputTextarea, PromptInputActions, Source, SourceTrigger, SourceContent, SourceList, Button, Separator, } from '../index'; import { Copy, ThumbsUp, ThumbsDown, ArrowUp } from 'lucide-solid'; import '../elements/register'; // registers kc-sources / kc-source // kc-sources / kc-source IntrinsicElements are already declared in // src/elements/source-list.stories.tsx (same TS project, merged interface). // Re-declare here so this file is self-contained; types must match exactly. declare module 'solid-js' { // eslint-disable-next-line @typescript-eslint/no-namespace namespace JSX { interface IntrinsicElements { // Plain HTMLAttributes — `numbered` is set via setAttribute in onMount. 'kc-sources': JSX.HTMLAttributes; 'kc-source': JSX.HTMLAttributes & { href?: string; label?: string; headline?: string; description?: string; 'show-favicon'?: boolean | ''; }; } } } const meta: Meta = { title: 'Examples/Conversation with Sources', parameters: { layout: 'padded', docs: { description: { component: [ 'Show inline citations below an assistant reply. Two routes:', '', '**Prop route (`sources` array):** set `.sources = [{ href, title, description, label?, showFavicon? }]` as a JS property. Prop sources render first.', '', '**Declarative route (`` children):** place `` elements as children of ``. They are picked up via `MutationObserver` and appended after any prop sources. Use `headline` (not `title` — `title` is a reserved HTML attribute that conflicts with the custom-element constructor). Confirmed in `src/elements/source.tsx`.', '', '**Automatic numbered labels (`numbered` prop):** set `` (HTML) or `el.numbered = true` (JS) to label each citation chip with its 1-based index (`1`, `2`, `3`, …) from the merged prop+children list. Per-item `label` values are ignored when `numbered` is set. For manual control, omit `numbered` and set `label={1}` / `label={2}` etc. on each `SourceTrigger` (Solid) or the `label` attribute on each `` child.', '', '**When to omit favicons:** the favicon is fetched from the link\'s domain. Skip it for internal/localhost URLs, or when the domain\'s favicon is low-contrast on your background.', ].join('\n'), }, }, }, }; export default meta; type Story = StoryObj; export const Default: Story = { name: 'Multi-turn with Citations', parameters: { docs: { description: { story: [ 'Two exchanges, two citation styles. First exchange uses `label={1|2|3}` for numbered badges, no favicons. Second exchange adds `showFavicon` per-trigger.', '', '**Note:** `` uses `headline` not `title` for the hover-card heading — `title` is a reserved HTML attribute that conflicts with the custom-element constructor (`src/elements/source.tsx`).', '', 'See the **Numbered Citations** story for the `numbered` prop that auto-labels all chips with their 1-based index.', ].join('\n'), }, }, }, render: () => (

Research: WebAssembly in Production

{/* First exchange */} What are the real-world performance benefits of using WebAssembly in production web apps? I want concrete examples, not just benchmarks.
{`Several major companies have deployed WebAssembly in production with measurable results: **Figma** replaced their C++ to asm.js pipeline with Wasm and saw a **3x performance improvement** in load times. Their design tool renders complex vector graphics at 60fps using a Wasm-compiled engine. **Google Earth** ported their native rendering engine to Wasm, enabling the full 3D globe experience in browsers without plugins. Frame rendering dropped from **40ms to 12ms** per frame. **Shopify** uses Wasm for their theme editor's Liquid template parsing, achieving **10x faster** template compilation compared to their previous JavaScript parser. The pattern is consistent: CPU-intensive tasks like **image processing, cryptography, physics simulation, and codec operations** see the largest gains. IO-bound operations don't benefit as much.`}
{/* Second exchange with different citation pattern */} What about the downsides? Are there situations where Wasm actually hurts performance?
{`Yes, there are real tradeoffs to consider: **Bundle size** -- Wasm modules can be significantly larger than equivalent JavaScript. The Squoosh image editor found their Wasm codecs added **2-4MB** of download size, which they mitigated with lazy loading. **DOM access overhead** -- Every call from Wasm to the DOM crosses a boundary with serialization cost. Applications that frequently manipulate the DOM (like React-style UI frameworks) can actually be **slower** in Wasm than optimized JavaScript. **Startup latency** -- Wasm modules must be compiled before execution. While streaming compilation helps, large modules can take **200-500ms** to initialize on mobile devices. **Garbage collection** -- Until the Wasm GC proposal ships broadly, languages like Go and Java bring their own GC runtime, adding **5-20MB** to module size. The consensus from the Chrome team's analysis is: **use Wasm for compute-heavy inner loops, keep UI and orchestration in JavaScript**.`}
), }; // --------------------------------------------------------------------------- // Numbered Citations story // --------------------------------------------------------------------------- export const Numbered: Story = { name: 'Numbered Citations', parameters: { docs: { description: { story: [ 'Demonstrates the `numbered` boolean prop on `` / `SourceList`.', '', 'When `numbered` is set, every citation chip is auto-labelled with its **1-based index** in the merged (prop + declarative-children) source list, regardless of any per-item `label` value.', '', '- Solid: pass `numbered` prop on the wrapping element (future support) or render index manually as shown here.', '- Web component: `` or `el.numbered = true`.', '', '**Inline prose citations** (e.g. `[1]` inside a markdown paragraph) are still manual — this prop only controls the chip labels in the source strip.', '', '**Note:** `` uses `headline` (not `title`) as the hover-card attribute.', ].join('\n'), }, }, }, render: () => (

Sources strip with numbered — chips show 1, 2, 3 automatically:

Same sources without numbering — chips fall back to domain labels:

Numbered with favicons (showFavicon per-trigger):

), }; // --------------------------------------------------------------------------- // Numbered Citations (composition) story // --------------------------------------------------------------------------- export const NumberedComposition: Story = { name: 'Numbered Citations (composition)', parameters: { docs: { description: { story: [ 'The **same** numbered-citation strip as the "Numbered Citations" story — but built entirely from `` + `` children in markup.', '', 'No `sources` JS property is set. The `` element reads its `` light-DOM children via `MutationObserver` and appends them to the merged source list automatically. The `numbered` boolean attribute then labels each chip with its 1-based index.', '', 'Attribute reference for ``:', '- `href` — the URL to link to (domain seeds the default label/favicon)', '- `headline` — hover-card heading (**not** `title` — `title` is a reserved HTML attribute)', '- `description` — hover-card body text', '- `label` — custom chip label (ignored when the parent `` has `numbered`)', '- `show-favicon` — bare boolean attribute to show the domain favicon', '', '`` attribute reference:', '- `numbered` — auto-labels all chips with their 1-based index from the merged list', '- `show-favicon` — enables favicons for all children (per-item attribute overrides)', '', '**When to choose composition:** great for static/authored citations in plain HTML or any template language — no JS wiring needed beyond the element registration.', ].join('\n'), }, }, }, render: () => { let el: HTMLElement | undefined; onMount(() => { // The `numbered` attribute is a boolean — set it imperatively so it is // registered after the element upgrades (avoids timing issues in SSR/Storybook). el?.setAttribute('numbered', ''); }); return (

Composition: <kc-sources numbered> with <kc-source> children — chips auto-labelled 1, 2, 3, 4:

(el = e)} style={{ display: 'block' }}>

Composition without numbering — chips fall back to domain labels:

Composition with favicons and custom labels (no numbered):

); }, };