@a4ui/core 0.24.3 → 0.26.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 +11 -11
- package/dist/elements.css +145 -0
- package/dist/full.css +145 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +3225 -2582
- package/dist/lib/createOptimistic.d.ts +17 -0
- package/dist/lib/easing.d.ts +42 -0
- package/dist/lib/viewTransition.d.ts +8 -0
- package/dist/ui/ArtifactPanel.d.ts +29 -0
- package/dist/ui/CategoryStrip.d.ts +24 -0
- package/dist/ui/ChatThread.d.ts +24 -0
- package/dist/ui/Citation.d.ts +44 -0
- package/dist/ui/CodeTabs.d.ts +27 -0
- package/dist/ui/FloatingToolbar.d.ts +26 -0
- package/dist/ui/InlineSelect.d.ts +33 -0
- package/dist/ui/MasterDetail.d.ts +27 -0
- package/dist/ui/Message.d.ts +28 -0
- package/dist/ui/PageTransition.d.ts +25 -0
- package/dist/ui/PillSearch.d.ts +38 -0
- package/dist/ui/PromptComposer.d.ts +34 -0
- package/dist/ui/StreamingText.d.ts +26 -0
- package/package.json +1 -1
- package/preset.js +20 -0
- package/src/index.ts +21 -1
- package/src/lib/createOptimistic.ts +43 -0
- package/src/lib/easing.ts +43 -0
- package/src/lib/viewTransition.ts +32 -0
- package/src/ui/ArtifactPanel.tsx +88 -0
- package/src/ui/CategoryStrip.tsx +80 -0
- package/src/ui/ChatThread.tsx +59 -0
- package/src/ui/Citation.tsx +97 -0
- package/src/ui/CodeTabs.tsx +100 -0
- package/src/ui/FloatingToolbar.tsx +75 -0
- package/src/ui/InlineSelect.tsx +101 -0
- package/src/ui/MasterDetail.tsx +117 -0
- package/src/ui/Message.tsx +69 -0
- package/src/ui/PageTransition.tsx +90 -0
- package/src/ui/PillSearch.tsx +86 -0
- package/src/ui/PromptComposer.tsx +138 -0
- package/src/ui/StreamingText.tsx +98 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Holds an optimistic value that's updated immediately on `run`, then
|
|
3
|
+
* reconciled with the resolved (or rolled back on rejected) async result.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* const list = createOptimistic<Todo[]>([])
|
|
8
|
+
* async function addTodo(todo: Todo) {
|
|
9
|
+
* await list.run([...list.value(), todo], () => api.addTodo(todo))
|
|
10
|
+
* }
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare function createOptimistic<T>(initial: T): {
|
|
14
|
+
value: () => T;
|
|
15
|
+
pending: () => boolean;
|
|
16
|
+
run: (optimistic: T, commit: () => Promise<T>) => Promise<T>;
|
|
17
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Named cubic-bezier easing curves, as CSS `transition-timing-function` strings. */
|
|
2
|
+
export declare const EASE: {
|
|
3
|
+
/** Gentle ease-out — the sensible default for entrances and most UI motion. */
|
|
4
|
+
readonly out: "cubic-bezier(0.22, 1, 0.36, 1)";
|
|
5
|
+
/** Balanced ease-in-out for reversible state toggles. */
|
|
6
|
+
readonly inOut: "cubic-bezier(0.65, 0, 0.35, 1)";
|
|
7
|
+
/** Emphasized, decisive curve (fast start, long settle) for hero moments. */
|
|
8
|
+
readonly emphasized: "cubic-bezier(0.2, 0, 0, 1)";
|
|
9
|
+
/** No easing. */
|
|
10
|
+
readonly linear: "linear";
|
|
11
|
+
};
|
|
12
|
+
/** A key of {@link EASE}. */
|
|
13
|
+
export type EaseName = keyof typeof EASE;
|
|
14
|
+
/**
|
|
15
|
+
* Spring option presets for the `spring` helper (motion.dev), re-exported from
|
|
16
|
+
* `@a4ui/core`. Pass one straight through:
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { animate, spring, SPRING } from '@a4ui/core'
|
|
21
|
+
* animate(el, { y: 0 }, { type: spring, ...SPRING.snappy })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare const SPRING: {
|
|
25
|
+
/** Soft, natural settle — good for panels and layout shifts. */
|
|
26
|
+
readonly gentle: {
|
|
27
|
+
readonly stiffness: 170;
|
|
28
|
+
readonly damping: 26;
|
|
29
|
+
};
|
|
30
|
+
/** Quick and tight, minimal overshoot — good for toggles and small controls. */
|
|
31
|
+
readonly snappy: {
|
|
32
|
+
readonly stiffness: 320;
|
|
33
|
+
readonly damping: 30;
|
|
34
|
+
};
|
|
35
|
+
/** Playful overshoot — good for playful accents. Use sparingly. */
|
|
36
|
+
readonly bouncy: {
|
|
37
|
+
readonly stiffness: 420;
|
|
38
|
+
readonly damping: 18;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/** A key of {@link SPRING}. */
|
|
42
|
+
export type SpringName = keyof typeof SPRING;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run `update` inside a View Transition when the browser supports it, so DOM
|
|
3
|
+
* changes cross-fade automatically; otherwise just run `update` synchronously.
|
|
4
|
+
* Respects reduced-motion (skips the transition).
|
|
5
|
+
* @example
|
|
6
|
+
* startViewTransition(() => setRoute('details'))
|
|
7
|
+
*/
|
|
8
|
+
export declare function startViewTransition(update: () => void): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ArtifactPanelProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose?: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Panel width in px when open. @default 420 */
|
|
7
|
+
width?: number;
|
|
8
|
+
children: JSX.Element;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Right-side split panel that holds generated output (code, preview, doc) next
|
|
13
|
+
* to a chat — for AI UIs that keep the artifact and the conversation visible at
|
|
14
|
+
* once. Inline, not an overlay: place it as the last child of a flex row next
|
|
15
|
+
* to the main content. Slides open/closed by animating width (and a matching
|
|
16
|
+
* fade/slide on its content), reduced-motion aware via `motionReduced()`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* const [open, setOpen] = createSignal(false)
|
|
21
|
+
* <div class="flex h-full">
|
|
22
|
+
* <main class="min-w-0 flex-1">…chat…</main>
|
|
23
|
+
* <ArtifactPanel open={open()} onClose={() => setOpen(false)} title="script.py">
|
|
24
|
+
* <pre class="p-4 text-sm">…generated code…</pre>
|
|
25
|
+
* </ArtifactPanel>
|
|
26
|
+
* </div>
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function ArtifactPanel(props: ArtifactPanelProps): JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** A single selectable category: value, label, and optional leading icon. */
|
|
3
|
+
export interface CategoryItem {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: JSX.Element;
|
|
7
|
+
}
|
|
8
|
+
export interface CategoryStripProps {
|
|
9
|
+
items: CategoryItem[];
|
|
10
|
+
value: string;
|
|
11
|
+
onChange: (value: string) => void;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Horizontally-scrollable row of icon-over-label category tabs with a
|
|
16
|
+
* bottom-underline indicator on the active item. Left/Right arrow keys move
|
|
17
|
+
* the selection between items.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <CategoryStrip items={categories} value={category()} onChange={setCategory} />
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function CategoryStrip(props: CategoryStripProps): JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface ChatThreadProps {
|
|
3
|
+
children: JSX.Element;
|
|
4
|
+
/** Max width of the reading column, in px. Defaults to `768`. */
|
|
5
|
+
maxWidth?: number;
|
|
6
|
+
/** Auto-scroll to the newest message on mount and whenever children change. Defaults to `true`. */
|
|
7
|
+
stickToBottom?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Scrollable, centered reading column that stacks chat messages. Layout-only:
|
|
12
|
+
* it renders whatever is passed as `children`, so pair it with your own
|
|
13
|
+
* message bubbles/components.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <ChatThread maxWidth={640}>
|
|
18
|
+
* <div>Hi! Ask me anything about your itinerary.</div>
|
|
19
|
+
* <div>What's the weather like in Rivertown this weekend?</div>
|
|
20
|
+
* <div>Sunny and mild, 18–22°C, light breeze on Saturday.</div>
|
|
21
|
+
* </ChatThread>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function ChatThread(props: ChatThreadProps): JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface CitationProps {
|
|
3
|
+
/** The source number shown in the chip, e.g. `1`. */
|
|
4
|
+
index: number;
|
|
5
|
+
/** Links out to the source when present (opens in a new tab). */
|
|
6
|
+
href?: string;
|
|
7
|
+
/** Shown as a native tooltip and used as the accessible name. */
|
|
8
|
+
title?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Small inline chip citing a numbered source, meant to sit inside a sentence
|
|
13
|
+
* right after the claim it backs up.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <p>
|
|
18
|
+
* Glass surfaces use a translucent backdrop blur
|
|
19
|
+
* <Citation index={1} href="https://example.com/spec" title="Design spec" />.
|
|
20
|
+
* </p>
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function Citation(props: CitationProps): JSX.Element;
|
|
24
|
+
export interface SourceListProps {
|
|
25
|
+
sources: {
|
|
26
|
+
title: string;
|
|
27
|
+
href?: string;
|
|
28
|
+
}[];
|
|
29
|
+
class?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compact numbered "Sources" block matching the indices used by {@link Citation}.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* <SourceList
|
|
37
|
+
* sources={[
|
|
38
|
+
* { title: 'Design spec', href: 'https://example.com/spec' },
|
|
39
|
+
* { title: 'Internal style guide' },
|
|
40
|
+
* ]}
|
|
41
|
+
* />
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function SourceList(props: SourceListProps): JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** A single tab's content: label, source code, and optional language tag. */
|
|
3
|
+
export interface CodeTab {
|
|
4
|
+
label: string;
|
|
5
|
+
code: string;
|
|
6
|
+
/** Language tag, e.g. `'tsx'`. Not used for highlighting; informational only. */
|
|
7
|
+
lang?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CodeTabsProps {
|
|
10
|
+
tabs: CodeTab[];
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Card of tabbed code blocks with a copy button for the active tab, for
|
|
15
|
+
* showing several code samples or language variants side by side.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <CodeTabs
|
|
20
|
+
* tabs={[
|
|
21
|
+
* { label: 'npm', code: 'npm install @a4ui/core' },
|
|
22
|
+
* { label: 'pnpm', code: 'pnpm add @a4ui/core' },
|
|
23
|
+
* ]}
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function CodeTabs(props: CodeTabsProps): JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface FloatingToolbarProps {
|
|
3
|
+
children: JSX.Element;
|
|
4
|
+
/** Which edge of the viewport the bar is anchored to. Defaults to `'bottom'`. */
|
|
5
|
+
position?: 'top' | 'bottom';
|
|
6
|
+
/** Shrink padding/scale and intensify the glass blur once scrolled past ~24px. Defaults to `true`. */
|
|
7
|
+
condenseOnScroll?: boolean;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Fixed, horizontally-centered glass toolbar that floats above the page
|
|
12
|
+
* content. When `condenseOnScroll` is on (the default), it tightens its
|
|
13
|
+
* padding and scales down slightly once the user scrolls past a small
|
|
14
|
+
* threshold, mimicking a "Liquid Glass" detached bar. Reduced-motion aware:
|
|
15
|
+
* the condense change applies instantly, without a transition, when
|
|
16
|
+
* {@link motionReduced} is true.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <FloatingToolbar position="bottom">
|
|
21
|
+
* <IconButton label="Undo"><UndoIcon /></IconButton>
|
|
22
|
+
* <IconButton label="Redo"><RedoIcon /></IconButton>
|
|
23
|
+
* </FloatingToolbar>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function FloatingToolbar(props: FloatingToolbarProps): JSX.Element;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface InlineSelectOption {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface InlineSelectProps {
|
|
7
|
+
value: string;
|
|
8
|
+
options: InlineSelectOption[];
|
|
9
|
+
onChange: (value: string) => void;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Edit-in-place select: renders the current value as a clickable chip until
|
|
15
|
+
* clicked, then swaps to a `Select` for picking a new value. Committing a
|
|
16
|
+
* choice returns it to text; blurring away or pressing Escape cancels back
|
|
17
|
+
* to the previous value. Built for status/priority-style editing inline in
|
|
18
|
+
* rows/tables.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <InlineSelect
|
|
23
|
+
* value={status()}
|
|
24
|
+
* options={[
|
|
25
|
+
* { value: 'todo', label: 'To do' },
|
|
26
|
+
* { value: 'doing', label: 'In progress' },
|
|
27
|
+
* { value: 'done', label: 'Done' },
|
|
28
|
+
* ]}
|
|
29
|
+
* onChange={setStatus}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function InlineSelect(props: InlineSelectProps): JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface MasterDetailItem {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
meta?: JSX.Element;
|
|
6
|
+
detail: JSX.Element;
|
|
7
|
+
}
|
|
8
|
+
export interface MasterDetailProps {
|
|
9
|
+
items: MasterDetailItem[];
|
|
10
|
+
/** Controlled selection. Uncontrolled (internal signal, defaults to the first item) when omitted. */
|
|
11
|
+
selectedId?: string;
|
|
12
|
+
onSelect?: (id: string) => void;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* List + detail-pane layout: a scrollable list of items on the left, the
|
|
17
|
+
* selected item's detail content on the right. Arrow keys (and Home/End)
|
|
18
|
+
* move the selection within the list.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <MasterDetail
|
|
23
|
+
* items={messages.map((m) => ({ id: m.id, label: m.subject, meta: <Badge>{m.from}</Badge>, detail: <MessageBody message={m} /> }))}
|
|
24
|
+
* />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function MasterDetail(props: MasterDetailProps): JSX.Element;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** Who sent a {@link Message}; drives its subtle visual treatment. */
|
|
3
|
+
export type ChatRole = 'user' | 'assistant' | 'system';
|
|
4
|
+
export interface MessageProps {
|
|
5
|
+
role: ChatRole;
|
|
6
|
+
children: JSX.Element;
|
|
7
|
+
/** Display name shown in the header line. Omitted for `system` messages. */
|
|
8
|
+
author?: string;
|
|
9
|
+
/** Small avatar node, e.g. an icon or `<Avatar/>`. Omitted for `system` messages. */
|
|
10
|
+
avatar?: JSX.Element;
|
|
11
|
+
/** Preformatted timestamp string, e.g. `'2:45 PM'`. */
|
|
12
|
+
timestamp?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Full-width chat message row with no bubble: an optional avatar/author
|
|
17
|
+
* header line followed by a prose content block. Role is differentiated with
|
|
18
|
+
* subtle, token-based treatment only — `assistant` gets a faint muted panel,
|
|
19
|
+
* `user` stays plain, `system` is a small centered note.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <Message role="assistant" author="Nova" timestamp="2:45 PM">
|
|
24
|
+
* <p>Here's the summary you asked for.</p>
|
|
25
|
+
* </Message>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function Message(props: MessageProps): JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
/** Transition style for {@link PageTransition}. `slide` adds a small vertical shift. */
|
|
3
|
+
export type PageTransitionMode = 'fade' | 'slide';
|
|
4
|
+
export interface PageTransitionProps {
|
|
5
|
+
/** Change this value (e.g. a route or view id) to trigger a transition. */
|
|
6
|
+
key: string | number;
|
|
7
|
+
children: JSX.Element;
|
|
8
|
+
/** `fade` = opacity only; `slide` = opacity + small vertical shift. @default 'fade' */
|
|
9
|
+
mode?: PageTransitionMode;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cross-fades (or slides) its `children` out and in whenever `key` changes —
|
|
14
|
+
* for route/view transitions. Driven purely by the `key` prop; pair it with a
|
|
15
|
+
* router's current path or any view-switch signal. Swaps instantly under
|
|
16
|
+
* reduced motion.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <PageTransition key={activeTab()} mode="slide">
|
|
21
|
+
* <TabContent id={activeTab()} />
|
|
22
|
+
* </PageTransition>
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function PageTransition(props: PageTransitionProps): JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface PillField {
|
|
3
|
+
/** Stable identifier passed back via {@link PillSearchProps.onFieldClick}. */
|
|
4
|
+
key: string;
|
|
5
|
+
/** Small label shown above the value, e.g. `"Where"`. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Current value shown below the label. Falls back to `placeholder` when unset. */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Muted hint shown when `value` is unset. */
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PillSearchProps {
|
|
13
|
+
fields: PillField[];
|
|
14
|
+
/** Called with the clicked field's `key`. */
|
|
15
|
+
onFieldClick?: (key: string) => void;
|
|
16
|
+
/** Called when the round search button is pressed. */
|
|
17
|
+
onSearch?: () => void;
|
|
18
|
+
class?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Rounded-full search bar that packs several fields into segments with a
|
|
22
|
+
* round primary search button — the classic "Where / When / Who" pattern
|
|
23
|
+
* compressed into one compact control.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <PillSearch
|
|
28
|
+
* fields={[
|
|
29
|
+
* { key: 'where', label: 'Where', value: 'Lisbon' },
|
|
30
|
+
* { key: 'when', label: 'When', placeholder: 'Add dates' },
|
|
31
|
+
* { key: 'who', label: 'Who', placeholder: 'Add guests' },
|
|
32
|
+
* ]}
|
|
33
|
+
* onFieldClick={(key) => console.log('open', key)}
|
|
34
|
+
* onSearch={() => console.log('search')}
|
|
35
|
+
* />
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare function PillSearch(props: PillSearchProps): JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface PromptComposerProps {
|
|
3
|
+
value: string;
|
|
4
|
+
onInput: (value: string) => void;
|
|
5
|
+
onSubmit: (value: string) => void;
|
|
6
|
+
placeholder?: string;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
/** Small helper text under the field, e.g. model name or a character count. */
|
|
9
|
+
hint?: JSX.Element;
|
|
10
|
+
/** Attachment labels/filenames shown as removable chips above the input. */
|
|
11
|
+
attachments?: string[];
|
|
12
|
+
onRemoveAttachment?: (index: number) => void;
|
|
13
|
+
/** When set, shows a paperclip button that calls this on click. */
|
|
14
|
+
onAttach?: () => void;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The input area for a chat/AI UI: an auto-growing textarea inside a glass
|
|
19
|
+
* card, removable attachment chips, and a bottom bar with an optional attach
|
|
20
|
+
* button and a Send button. Fully controlled — the parent owns `value`.
|
|
21
|
+
* Submits on **Cmd/Ctrl+Enter** or the Send button click.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* const [value, setValue] = createSignal('')
|
|
26
|
+
* <PromptComposer
|
|
27
|
+
* value={value()}
|
|
28
|
+
* onInput={setValue}
|
|
29
|
+
* onSubmit={(v) => { sendMessage(v); setValue('') }}
|
|
30
|
+
* placeholder="Ask anything…"
|
|
31
|
+
* />
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function PromptComposer(props: PromptComposerProps): JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export interface StreamingTextProps {
|
|
3
|
+
/** The (possibly growing) full text so far. */
|
|
4
|
+
text: string;
|
|
5
|
+
/** Whether more text is still coming; shows the blinking caret while `true`. @default true */
|
|
6
|
+
streaming?: boolean;
|
|
7
|
+
/** Reveal speed, in characters per second, for newly appended text. @default 120 */
|
|
8
|
+
speed?: number;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Reveals `text` as it "streams" in: when `text` grows, the newly appended
|
|
13
|
+
* characters ease in at `speed` chars/sec instead of snapping into place,
|
|
14
|
+
* with a blinking caret while `streaming`. Already-revealed characters never
|
|
15
|
+
* re-animate — only the new tail is eased in, so appending to `text` doesn't
|
|
16
|
+
* replay the whole reveal. Skips the reveal (shows the full text at once)
|
|
17
|
+
* under reduced motion — the caret still blinks while `streaming` in that
|
|
18
|
+
* case. `streaming={false}` shows the full text immediately with no caret,
|
|
19
|
+
* for the final/settled answer.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <StreamingText text={answer()} streaming={!done()} />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function StreamingText(props: StreamingTextProps): JSX.Element;
|
package/package.json
CHANGED
package/preset.js
CHANGED
|
@@ -28,6 +28,26 @@ const glass = plugin(({ addComponents, addVariant }) => {
|
|
|
28
28
|
// and a darker one on light to keep WCAG AA in both.
|
|
29
29
|
addVariant('light', "[data-theme='light'] &")
|
|
30
30
|
addComponents({
|
|
31
|
+
// ---- Elevation scale ----
|
|
32
|
+
// A layered depth system: `.elevation-1..4` for raised surfaces (rising
|
|
33
|
+
// shadow), `.pressed` for an inset/recessed look. Uses the same `--shadow`
|
|
34
|
+
// token as the glass surfaces, so it recolors per theme.
|
|
35
|
+
'.elevation-1': {
|
|
36
|
+
boxShadow: '0 1px 2px hsl(var(--shadow) / 0.06), 0 1px 3px hsl(var(--shadow) / 0.1)',
|
|
37
|
+
},
|
|
38
|
+
'.elevation-2': {
|
|
39
|
+
boxShadow: '0 2px 4px hsl(var(--shadow) / 0.06), 0 4px 12px hsl(var(--shadow) / 0.12)',
|
|
40
|
+
},
|
|
41
|
+
'.elevation-3': {
|
|
42
|
+
boxShadow: '0 2px 6px hsl(var(--shadow) / 0.08), 0 8px 24px hsl(var(--shadow) / 0.14)',
|
|
43
|
+
},
|
|
44
|
+
'.elevation-4': {
|
|
45
|
+
boxShadow: '0 4px 12px hsl(var(--shadow) / 0.1), 0 16px 40px hsl(var(--shadow) / 0.18)',
|
|
46
|
+
},
|
|
47
|
+
'.pressed': {
|
|
48
|
+
boxShadow: 'inset 0 1px 2px hsl(var(--shadow) / 0.18)',
|
|
49
|
+
},
|
|
50
|
+
|
|
31
51
|
// ---- Primary glass surface ----
|
|
32
52
|
'.card': {
|
|
33
53
|
position: 'relative',
|
package/src/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// import '@a4ui/core/styles.css'
|
|
9
9
|
// import { Button, Card, Modal } from '@a4ui/core'
|
|
10
10
|
|
|
11
|
-
export const A4UI_VERSION = '0.
|
|
11
|
+
export const A4UI_VERSION = '0.26.0'
|
|
12
12
|
|
|
13
13
|
// Helpers (src/lib) — generic, framework-level utilities.
|
|
14
14
|
export { cn } from './lib/cn'
|
|
@@ -33,6 +33,9 @@ export {
|
|
|
33
33
|
} from './lib/motion'
|
|
34
34
|
export { useMediaQuery } from './lib/media'
|
|
35
35
|
export { remeasureAfterLayout } from './lib/virtual'
|
|
36
|
+
export { createOptimistic } from './lib/createOptimistic'
|
|
37
|
+
export { startViewTransition } from './lib/viewTransition'
|
|
38
|
+
export { EASE, SPRING, type EaseName, type SpringName } from './lib/easing'
|
|
36
39
|
|
|
37
40
|
// UI components (src/ui) — all 18 extracted. See CLAUDE.md.
|
|
38
41
|
export { Accordion, type AccordionItem } from './ui/Accordion'
|
|
@@ -145,6 +148,23 @@ export { Portal, type PortalProps } from './ui/Portal'
|
|
|
145
148
|
export { Sortable, type SortableProps } from './ui/Sortable'
|
|
146
149
|
export { Clock, type ClockProps } from './ui/Clock'
|
|
147
150
|
|
|
151
|
+
// Conversation / AI, transitions, and depth surfaces (roadmap phase 1).
|
|
152
|
+
export { ChatThread, type ChatThreadProps } from './ui/ChatThread'
|
|
153
|
+
export { Message, type MessageProps, type ChatRole } from './ui/Message'
|
|
154
|
+
export { StreamingText, type StreamingTextProps } from './ui/StreamingText'
|
|
155
|
+
export { Citation, type CitationProps, SourceList, type SourceListProps } from './ui/Citation'
|
|
156
|
+
export { PromptComposer, type PromptComposerProps } from './ui/PromptComposer'
|
|
157
|
+
export { ArtifactPanel, type ArtifactPanelProps } from './ui/ArtifactPanel'
|
|
158
|
+
export { FloatingToolbar, type FloatingToolbarProps } from './ui/FloatingToolbar'
|
|
159
|
+
export { PageTransition, type PageTransitionProps } from './ui/PageTransition'
|
|
160
|
+
export { InlineSelect, type InlineSelectProps, type InlineSelectOption } from './ui/InlineSelect'
|
|
161
|
+
|
|
162
|
+
// Phase 2 — code, search, navigation, master-detail.
|
|
163
|
+
export { CodeTabs, type CodeTabsProps, type CodeTab } from './ui/CodeTabs'
|
|
164
|
+
export { PillSearch, type PillSearchProps, type PillField } from './ui/PillSearch'
|
|
165
|
+
export { CategoryStrip, type CategoryStripProps, type CategoryItem } from './ui/CategoryStrip'
|
|
166
|
+
export { MasterDetail, type MasterDetailProps, type MasterDetailItem } from './ui/MasterDetail'
|
|
167
|
+
|
|
148
168
|
// Motion components (src/ui) — animation primitives built on the `motion` engine
|
|
149
169
|
// (adapted from motion.dev examples). All tree-shakeable and reduced-motion aware;
|
|
150
170
|
// `motion` is external, so importing one of these is the only thing that pulls it.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Optimistic-update helper: show a value immediately, then reconcile with the
|
|
2
|
+
// real async result. On failure, rolls back to the previous settled value and
|
|
3
|
+
// rethrows so callers still see the error (e.g. to show a toast).
|
|
4
|
+
import { createSignal } from 'solid-js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Holds an optimistic value that's updated immediately on `run`, then
|
|
8
|
+
* reconciled with the resolved (or rolled back on rejected) async result.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const list = createOptimistic<Todo[]>([])
|
|
13
|
+
* async function addTodo(todo: Todo) {
|
|
14
|
+
* await list.run([...list.value(), todo], () => api.addTodo(todo))
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function createOptimistic<T>(initial: T): {
|
|
19
|
+
value: () => T
|
|
20
|
+
pending: () => boolean
|
|
21
|
+
run: (optimistic: T, commit: () => Promise<T>) => Promise<T>
|
|
22
|
+
} {
|
|
23
|
+
const [value, setValue] = createSignal(initial)
|
|
24
|
+
const [pending, setPending] = createSignal(false)
|
|
25
|
+
|
|
26
|
+
async function run(optimistic: T, commit: () => Promise<T>): Promise<T> {
|
|
27
|
+
const previous = value()
|
|
28
|
+
setValue(() => optimistic)
|
|
29
|
+
setPending(true)
|
|
30
|
+
try {
|
|
31
|
+
const resolved = await commit()
|
|
32
|
+
setValue(() => resolved)
|
|
33
|
+
return resolved
|
|
34
|
+
} catch (err) {
|
|
35
|
+
setValue(() => previous)
|
|
36
|
+
throw err
|
|
37
|
+
} finally {
|
|
38
|
+
setPending(false)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { value, pending, run }
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Shared motion vocabulary — named easing curves and spring presets, so every
|
|
2
|
+
// component transition can pull from one consistent set instead of hand-rolling
|
|
3
|
+
// cubic-beziers. `EASE` values are plain CSS `transition-timing-function`
|
|
4
|
+
// strings (drop into an inline style, a `transition-timing-function`, or a
|
|
5
|
+
// Tailwind `ease-[…]` arbitrary value). `SPRING` values are option objects for
|
|
6
|
+
// the `spring` helper re-exported from `@a4ui/core` (motion.dev).
|
|
7
|
+
|
|
8
|
+
/** Named cubic-bezier easing curves, as CSS `transition-timing-function` strings. */
|
|
9
|
+
export const EASE = {
|
|
10
|
+
/** Gentle ease-out — the sensible default for entrances and most UI motion. */
|
|
11
|
+
out: 'cubic-bezier(0.22, 1, 0.36, 1)',
|
|
12
|
+
/** Balanced ease-in-out for reversible state toggles. */
|
|
13
|
+
inOut: 'cubic-bezier(0.65, 0, 0.35, 1)',
|
|
14
|
+
/** Emphasized, decisive curve (fast start, long settle) for hero moments. */
|
|
15
|
+
emphasized: 'cubic-bezier(0.2, 0, 0, 1)',
|
|
16
|
+
/** No easing. */
|
|
17
|
+
linear: 'linear',
|
|
18
|
+
} as const
|
|
19
|
+
|
|
20
|
+
/** A key of {@link EASE}. */
|
|
21
|
+
export type EaseName = keyof typeof EASE
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Spring option presets for the `spring` helper (motion.dev), re-exported from
|
|
25
|
+
* `@a4ui/core`. Pass one straight through:
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { animate, spring, SPRING } from '@a4ui/core'
|
|
30
|
+
* animate(el, { y: 0 }, { type: spring, ...SPRING.snappy })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export const SPRING = {
|
|
34
|
+
/** Soft, natural settle — good for panels and layout shifts. */
|
|
35
|
+
gentle: { stiffness: 170, damping: 26 },
|
|
36
|
+
/** Quick and tight, minimal overshoot — good for toggles and small controls. */
|
|
37
|
+
snappy: { stiffness: 320, damping: 30 },
|
|
38
|
+
/** Playful overshoot — good for playful accents. Use sparingly. */
|
|
39
|
+
bouncy: { stiffness: 420, damping: 18 },
|
|
40
|
+
} as const
|
|
41
|
+
|
|
42
|
+
/** A key of {@link SPRING}. */
|
|
43
|
+
export type SpringName = keyof typeof SPRING
|