import type { Meta, StoryObj } from 'storybook-solidjs-vite'; import { onMount } from 'solid-js'; import './register'; // side effect: registers the custom elements import { argTypesFor, specDescription } from '../stories/docs/element-controls'; // The web components are custom DOM elements, so declare the tags for JSX. declare module 'solid-js' { // eslint-disable-next-line @typescript-eslint/no-namespace namespace JSX { interface IntrinsicElements { 'kc-sources': JSX.HTMLAttributes; 'kc-source': JSX.HTMLAttributes & { href?: string; label?: string; headline?: string; description?: string; 'show-favicon'?: boolean | '' }; } } } interface SourceItem { href: string; title?: string; description?: string; label?: string; showFavicon?: boolean; } const sampleSources: SourceItem[] = [ { href: 'https://kitn.dev', title: 'kitn — the kit', description: 'Composable SolidJS + web-component chat UI.', showFavicon: true }, { href: 'https://solidjs.com', title: 'SolidJS', description: 'A reactive UI library.', showFavicon: true }, ]; /** Render the actual `` custom element with a `sources` property. */ function SourceListElement() { let el: (HTMLElement & { sources?: SourceItem[] }) | undefined; onMount(() => { if (el) el.sources = sampleSources; }); return ( (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} /> ); } const HTML_SNIPPET = ` `; const meta = { title: 'Components/Sources', tags: ['autodocs'], argTypes: argTypesFor('kc-sources'), parameters: { layout: 'fullscreen', docs: { description: specDescription('kc-sources', [ '`` is the framework-agnostic **web component** for a wrapped row of citation links (each with its own hover-card preview), isolated in **Shadow DOM**.', '**When to use:** showing the sources behind an assistant answer in a non-Solid app. For a single citation, use ``; in SolidJS, compose `SourceList` + `Source`.', '**Placement:** below an assistant message, above the prompt input — rendered as a wrapping row of citation chips that spans the full message column width.', "**How to use:** register once with `import '@kitn.ai/chat/elements'`, set the data via the `sources` **property** (each item: `href`, `title`, `description`, `label`, `showFavicon`), and set `show-favicon` to enable favicons for all items (a per-item `showFavicon` overrides it).", '**Anatomy:** a wrapping flex row of **citation chips** — each chip is a `` (or a `sources`-property item): a **trigger button** (domain label or `numbered` index, optional favicon) + a **hover-card** (headline + description, shown on hover/focus). Declarative `` children are invisible data carriers merged after the `sources` property list.', 'See the **Code** tab for HTML usage.', ]), }, }, } satisfies Meta; export default meta; type Story = StoryObj; /** Two citations rendered as a wrapped list with favicons. */ export const Default: Story = { render: () => , parameters: { docs: { source: { code: HTML_SNIPPET, language: 'html' } } }, }; const DECLARATIVE_HTML_SNIPPET = ` `; /** Declarative sources — `` light-DOM children instead of a `sources` * property. Each child carries `href`, `label`, `headline`, `description`, and the * `show-favicon` flag as HTML attributes. Great for plain HTML with no JS wiring. */ export const DeclarativeSources: Story = { name: 'Declarative Sources (kc-source)', render: () => { let el: HTMLElement | undefined; onMount(() => { if (!el) return; el.setAttribute('show-favicon', ''); }); return ( (el = e as HTMLElement)} style={{ display: 'block', padding: '16px', 'max-width': '720px' }} > ); }, parameters: { docs: { source: { code: DECLARATIVE_HTML_SNIPPET, language: 'html', }, }, }, };