@signal9/era-ui 2.8.3 → 2.9.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/dist/ai/confirmation/approval.svelte.d.ts +42 -0
- package/dist/ai/confirmation/approval.svelte.js +122 -0
- package/dist/ai/confirmation/confirmation-args.svelte +67 -0
- package/dist/ai/confirmation/confirmation-args.svelte.d.ts +8 -0
- package/dist/ai/confirmation/index.d.ts +2 -0
- package/dist/ai/confirmation/index.js +2 -0
- package/dist/ai/conversations/conversations-group.svelte +28 -0
- package/dist/ai/conversations/conversations-group.svelte.d.ts +10 -0
- package/dist/ai/conversations/conversations-item.svelte +208 -0
- package/dist/ai/conversations/conversations-item.svelte.d.ts +28 -0
- package/dist/ai/conversations/conversations-root.svelte +27 -0
- package/dist/ai/conversations/conversations-root.svelte.d.ts +10 -0
- package/dist/ai/conversations/conversations.svelte.d.ts +38 -0
- package/dist/ai/conversations/conversations.svelte.js +73 -0
- package/dist/ai/conversations/index.d.ts +5 -0
- package/dist/ai/conversations/index.js +4 -0
- package/dist/ai/error-panel/categorize-error.d.ts +32 -0
- package/dist/ai/error-panel/categorize-error.js +106 -0
- package/dist/ai/error-panel/error-panel.svelte +130 -0
- package/dist/ai/error-panel/error-panel.svelte.d.ts +17 -0
- package/dist/ai/error-panel/index.d.ts +2 -0
- package/dist/ai/error-panel/index.js +2 -0
- package/dist/ai/index.d.ts +11 -0
- package/dist/ai/index.js +7 -0
- package/dist/ai/message/index.d.ts +2 -0
- package/dist/ai/message/index.js +1 -0
- package/dist/ai/message/message-usage.svelte +126 -0
- package/dist/ai/message/message-usage.svelte.d.ts +22 -0
- package/dist/ai/project.d.ts +64 -0
- package/dist/ai/project.js +70 -0
- package/dist/ai/run-status/index.d.ts +2 -0
- package/dist/ai/run-status/index.js +1 -0
- package/dist/ai/run-status/run-status.svelte +132 -0
- package/dist/ai/run-status/run-status.svelte.d.ts +26 -0
- package/dist/ai/runtime-feed/index.d.ts +2 -0
- package/dist/ai/runtime-feed/index.js +2 -0
- package/dist/ai/runtime-feed/runtime-feed-item.svelte +64 -0
- package/dist/ai/runtime-feed/runtime-feed-item.svelte.d.ts +27 -0
- package/dist/ai/runtime-feed/runtime-feed-root.svelte +35 -0
- package/dist/ai/runtime-feed/runtime-feed-root.svelte.d.ts +13 -0
- package/dist/ai/structured-value/index.d.ts +1 -0
- package/dist/ai/structured-value/index.js +1 -0
- package/dist/ai/structured-value/structured-value.svelte +130 -0
- package/dist/ai/structured-value/structured-value.svelte.d.ts +10 -0
- package/dist/ai/tool/index.d.ts +3 -0
- package/dist/ai/tool/index.js +3 -0
- package/dist/ai/tool/tool-chip.svelte +66 -0
- package/dist/ai/tool/tool-chip.svelte.d.ts +17 -0
- package/dist/ai/tool/tool-chips.svelte +56 -0
- package/dist/ai/tool/tool-chips.svelte.d.ts +13 -0
- package/dist/ai/tool/tool-exec.svelte +49 -0
- package/dist/ai/tool/tool-exec.svelte.d.ts +11 -0
- package/dist/era-ui.css +1 -1
- package/dist/ui/badge/badge.svelte +3 -2
- package/dist/ui/badge/badge.svelte.d.ts +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** Aggregate run lifecycle. `running` pulses; the three active states
|
|
3
|
+
* (queued/running/awaiting) surface the Cancel affordance. */
|
|
4
|
+
export type RunState = 'idle' | 'queued' | 'running' | 'awaiting' | 'done' | 'error';
|
|
5
|
+
|
|
6
|
+
/** Tone for the latest-log line — reuses the era semantic palette. */
|
|
7
|
+
export type LogTone = 'default' | 'muted' | 'success' | 'warning' | 'destructive';
|
|
8
|
+
|
|
9
|
+
export interface RunTools {
|
|
10
|
+
complete: number;
|
|
11
|
+
total: number;
|
|
12
|
+
running?: number;
|
|
13
|
+
failed?: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface StatusStyle {
|
|
17
|
+
label: string;
|
|
18
|
+
dot: string;
|
|
19
|
+
text: string;
|
|
20
|
+
active: boolean;
|
|
21
|
+
pulse: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const STATUS: Record<RunState, StatusStyle> = {
|
|
25
|
+
idle: { label: 'idle', dot: 'bg-muted', text: 'text-muted', active: false, pulse: false },
|
|
26
|
+
queued: { label: 'queued', dot: 'bg-muted', text: 'text-muted', active: true, pulse: false },
|
|
27
|
+
running: { label: 'running', dot: 'bg-primary', text: 'text-fg', active: true, pulse: true },
|
|
28
|
+
awaiting: {
|
|
29
|
+
label: 'awaiting',
|
|
30
|
+
dot: 'bg-warning',
|
|
31
|
+
text: 'text-warning',
|
|
32
|
+
active: true,
|
|
33
|
+
pulse: false
|
|
34
|
+
},
|
|
35
|
+
done: { label: 'done', dot: 'bg-success', text: 'text-success', active: false, pulse: false },
|
|
36
|
+
error: {
|
|
37
|
+
label: 'error',
|
|
38
|
+
dot: 'bg-destructive',
|
|
39
|
+
text: 'text-destructive',
|
|
40
|
+
active: false,
|
|
41
|
+
pulse: false
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const LOG_TONE: Record<LogTone, string> = {
|
|
46
|
+
default: 'text-fg',
|
|
47
|
+
muted: 'text-muted',
|
|
48
|
+
success: 'text-success',
|
|
49
|
+
warning: 'text-warning',
|
|
50
|
+
destructive: 'text-destructive'
|
|
51
|
+
};
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<script lang="ts">
|
|
55
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
56
|
+
import X from '@lucide/svelte/icons/x';
|
|
57
|
+
import { Badge } from '../../ui/badge/index.js';
|
|
58
|
+
import { Button } from '../../ui/button/index.js';
|
|
59
|
+
import { cn } from '../../utils/index.js';
|
|
60
|
+
|
|
61
|
+
let {
|
|
62
|
+
status,
|
|
63
|
+
tools,
|
|
64
|
+
approvals,
|
|
65
|
+
agents,
|
|
66
|
+
latest,
|
|
67
|
+
oncancel,
|
|
68
|
+
class: className,
|
|
69
|
+
...restProps
|
|
70
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
71
|
+
status: RunState;
|
|
72
|
+
tools?: RunTools;
|
|
73
|
+
approvals?: number;
|
|
74
|
+
agents?: number;
|
|
75
|
+
latest?: { tone: LogTone; text: string };
|
|
76
|
+
oncancel?: () => void;
|
|
77
|
+
} = $props();
|
|
78
|
+
|
|
79
|
+
const style = $derived(STATUS[status]);
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<div
|
|
83
|
+
role="status"
|
|
84
|
+
aria-live="polite"
|
|
85
|
+
class={cn(
|
|
86
|
+
'flex flex-col rounded-(--era-rd-lg) bg-(--era-surface-bg) shadow-(--era-shadow)',
|
|
87
|
+
className
|
|
88
|
+
)}
|
|
89
|
+
{...restProps}
|
|
90
|
+
>
|
|
91
|
+
<div class="flex h-(--era-h-md) items-center gap-(--era-gap) px-(--era-inset-md)">
|
|
92
|
+
<!-- leading status dot + label -->
|
|
93
|
+
<span class="flex items-center gap-(--era-inset-xxs) font-mono text-body {style.text} era-text-trim">
|
|
94
|
+
<span class={cn('size-1.5 shrink-0 rounded-full', style.dot, style.pulse && 'animate-pulse')}></span>
|
|
95
|
+
{style.label}
|
|
96
|
+
</span>
|
|
97
|
+
|
|
98
|
+
{#if tools}
|
|
99
|
+
<Badge>{tools.complete}/{tools.total} tools</Badge>
|
|
100
|
+
{#if tools.running}<Badge tone="accent">+{tools.running}</Badge>{/if}
|
|
101
|
+
{#if tools.failed}<Badge tone="destructive">{tools.failed} failed</Badge>{/if}
|
|
102
|
+
{/if}
|
|
103
|
+
|
|
104
|
+
{#if approvals}
|
|
105
|
+
<Badge tone="warning">{approvals} approval</Badge>
|
|
106
|
+
{/if}
|
|
107
|
+
|
|
108
|
+
{#if agents}
|
|
109
|
+
<Badge tone="accent">{agents} agents</Badge>
|
|
110
|
+
{/if}
|
|
111
|
+
|
|
112
|
+
<div class="flex-1"></div>
|
|
113
|
+
|
|
114
|
+
{#if style.active && oncancel}
|
|
115
|
+
<Button size="xs" tone="destructive" onclick={() => oncancel?.()}>
|
|
116
|
+
<X class="size-(--era-h-xs)" aria-hidden="true" />
|
|
117
|
+
Cancel
|
|
118
|
+
</Button>
|
|
119
|
+
{/if}
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
{#if latest}
|
|
123
|
+
<div
|
|
124
|
+
class={cn(
|
|
125
|
+
'truncate px-(--era-inset-md) pb-(--era-inset-sm) font-mono text-body era-text-trim',
|
|
126
|
+
LOG_TONE[latest.tone]
|
|
127
|
+
)}
|
|
128
|
+
>
|
|
129
|
+
{latest.text}
|
|
130
|
+
</div>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Aggregate run lifecycle. `running` pulses; the three active states
|
|
2
|
+
* (queued/running/awaiting) surface the Cancel affordance. */
|
|
3
|
+
export type RunState = 'idle' | 'queued' | 'running' | 'awaiting' | 'done' | 'error';
|
|
4
|
+
/** Tone for the latest-log line — reuses the era semantic palette. */
|
|
5
|
+
export type LogTone = 'default' | 'muted' | 'success' | 'warning' | 'destructive';
|
|
6
|
+
export interface RunTools {
|
|
7
|
+
complete: number;
|
|
8
|
+
total: number;
|
|
9
|
+
running?: number;
|
|
10
|
+
failed?: number;
|
|
11
|
+
}
|
|
12
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
13
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
14
|
+
status: RunState;
|
|
15
|
+
tools?: RunTools;
|
|
16
|
+
approvals?: number;
|
|
17
|
+
agents?: number;
|
|
18
|
+
latest?: {
|
|
19
|
+
tone: LogTone;
|
|
20
|
+
text: string;
|
|
21
|
+
};
|
|
22
|
+
oncancel?: () => void;
|
|
23
|
+
};
|
|
24
|
+
declare const RunStatus: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
25
|
+
type RunStatus = ReturnType<typeof RunStatus>;
|
|
26
|
+
export default RunStatus;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** Tone of a runtime-feed line — maps to a text/dot color. */
|
|
3
|
+
export type RuntimeTone = 'log' | 'progress' | 'complete' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
/** A single line in a RuntimeFeed when driven by the `items` prop. */
|
|
6
|
+
export interface RuntimeFeedItemData {
|
|
7
|
+
/** Stable key for the `{#each}`. */
|
|
8
|
+
id?: string | number;
|
|
9
|
+
/** Tone color of the dot + line. */
|
|
10
|
+
tone?: RuntimeTone;
|
|
11
|
+
/** Optional trailing relative-time / label. */
|
|
12
|
+
label?: string;
|
|
13
|
+
/** The line text. */
|
|
14
|
+
text: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const TONE: Record<RuntimeTone, string> = {
|
|
18
|
+
log: 'text-muted',
|
|
19
|
+
progress: 'text-fg',
|
|
20
|
+
complete: 'text-success',
|
|
21
|
+
warn: 'text-warning',
|
|
22
|
+
error: 'text-destructive'
|
|
23
|
+
};
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import type { Snippet } from 'svelte';
|
|
28
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
29
|
+
import { cn } from '../../utils/index.js';
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
tone = 'log',
|
|
33
|
+
label,
|
|
34
|
+
text,
|
|
35
|
+
children,
|
|
36
|
+
class: className,
|
|
37
|
+
...restProps
|
|
38
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
39
|
+
/** Tone color of the leading dot and the line. */
|
|
40
|
+
tone?: RuntimeTone;
|
|
41
|
+
/** Optional trailing relative-time / label. */
|
|
42
|
+
label?: string;
|
|
43
|
+
/** Line text (alternative to `children`). */
|
|
44
|
+
text?: string;
|
|
45
|
+
children?: Snippet;
|
|
46
|
+
} = $props();
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<!-- One live line: a tone dot that inherits the row color via bg-current, the
|
|
50
|
+
truncated message, and an optional trailing timestamp/label. Single-line by
|
|
51
|
+
design (the tail scrolls, it doesn't wrap) so items-center + era-text-trim
|
|
52
|
+
keep the dot and text on one optical baseline. -->
|
|
53
|
+
<div
|
|
54
|
+
class={cn('flex items-center gap-(--era-gap) text-body', TONE[tone], className)}
|
|
55
|
+
{...restProps}
|
|
56
|
+
>
|
|
57
|
+
<span class="size-(--era-h-xxs) shrink-0 rounded-full bg-current" aria-hidden="true"></span>
|
|
58
|
+
<span class="min-w-0 flex-1 truncate era-text-trim">
|
|
59
|
+
{#if children}{@render children()}{:else}{text}{/if}
|
|
60
|
+
</span>
|
|
61
|
+
{#if label}
|
|
62
|
+
<span class="shrink-0 text-muted tabular-nums era-text-trim">{label}</span>
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Tone of a runtime-feed line — maps to a text/dot color. */
|
|
2
|
+
export type RuntimeTone = 'log' | 'progress' | 'complete' | 'warn' | 'error';
|
|
3
|
+
/** A single line in a RuntimeFeed when driven by the `items` prop. */
|
|
4
|
+
export interface RuntimeFeedItemData {
|
|
5
|
+
/** Stable key for the `{#each}`. */
|
|
6
|
+
id?: string | number;
|
|
7
|
+
/** Tone color of the dot + line. */
|
|
8
|
+
tone?: RuntimeTone;
|
|
9
|
+
/** Optional trailing relative-time / label. */
|
|
10
|
+
label?: string;
|
|
11
|
+
/** The line text. */
|
|
12
|
+
text: string;
|
|
13
|
+
}
|
|
14
|
+
import type { Snippet } from 'svelte';
|
|
15
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
16
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
17
|
+
/** Tone color of the leading dot and the line. */
|
|
18
|
+
tone?: RuntimeTone;
|
|
19
|
+
/** Optional trailing relative-time / label. */
|
|
20
|
+
label?: string;
|
|
21
|
+
/** Line text (alternative to `children`). */
|
|
22
|
+
text?: string;
|
|
23
|
+
children?: Snippet;
|
|
24
|
+
};
|
|
25
|
+
declare const RuntimeFeedItem: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
26
|
+
type RuntimeFeedItem = ReturnType<typeof RuntimeFeedItem>;
|
|
27
|
+
export default RuntimeFeedItem;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn } from '../../utils/index.js';
|
|
5
|
+
import Item, { type RuntimeFeedItemData } from './runtime-feed-item.svelte';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
items,
|
|
9
|
+
limit,
|
|
10
|
+
children,
|
|
11
|
+
class: className,
|
|
12
|
+
...restProps
|
|
13
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
14
|
+
/** Drive the feed declaratively; alternatively compose `Item` children. */
|
|
15
|
+
items?: RuntimeFeedItemData[];
|
|
16
|
+
/** Cap the tail to the most recent N lines. */
|
|
17
|
+
limit?: number;
|
|
18
|
+
children?: Snippet;
|
|
19
|
+
} = $props();
|
|
20
|
+
|
|
21
|
+
// The running-tail feel: keep only the last `limit` lines when capped.
|
|
22
|
+
const visible = $derived(
|
|
23
|
+
items && limit !== undefined ? items.slice(-limit) : (items ?? [])
|
|
24
|
+
);
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div class={cn('flex min-w-0 flex-col gap-(--era-gap)', className)} {...restProps}>
|
|
28
|
+
{#if items}
|
|
29
|
+
{#each visible as item, i (item.id ?? i)}
|
|
30
|
+
<Item tone={item.tone} label={item.label} text={item.text} />
|
|
31
|
+
{/each}
|
|
32
|
+
{:else}
|
|
33
|
+
{@render children?.()}
|
|
34
|
+
{/if}
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type RuntimeFeedItemData } from './runtime-feed-item.svelte';
|
|
4
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
5
|
+
/** Drive the feed declaratively; alternatively compose `Item` children. */
|
|
6
|
+
items?: RuntimeFeedItemData[];
|
|
7
|
+
/** Cap the tail to the most recent N lines. */
|
|
8
|
+
limit?: number;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
};
|
|
11
|
+
declare const RuntimeFeedRoot: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type RuntimeFeedRoot = ReturnType<typeof RuntimeFeedRoot>;
|
|
13
|
+
export default RuntimeFeedRoot;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './structured-value.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Root } from './structured-value.svelte';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** Beyond this depth the tree stops recursing and shows an ellipsis. */
|
|
3
|
+
const MAX_DEPTH = 16;
|
|
4
|
+
|
|
5
|
+
type Primitive = string | number | boolean | null | undefined;
|
|
6
|
+
|
|
7
|
+
function isRecord(input: unknown): input is Record<string, unknown> {
|
|
8
|
+
return typeof input === 'object' && input !== null && !Array.isArray(input);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isPrimitive(input: unknown): input is Primitive {
|
|
12
|
+
return (
|
|
13
|
+
input === null || input === undefined || ['string', 'number', 'boolean'].includes(typeof input)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** An array is "simple" when every entry is a primitive — rendered inline. */
|
|
18
|
+
function isSimpleArray(input: unknown[]): boolean {
|
|
19
|
+
return input.every((entry) => isPrimitive(entry));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Values that sit on the same line as their key/index rather than nesting. */
|
|
23
|
+
function isInline(input: unknown): boolean {
|
|
24
|
+
if (isPrimitive(input)) return true;
|
|
25
|
+
if (Array.isArray(input)) return input.length === 0 || isSimpleArray(input);
|
|
26
|
+
if (isRecord(input)) return Object.keys(input).length === 0;
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function formatPrimitive(input: unknown): string {
|
|
31
|
+
if (input === null || input === undefined) return 'null';
|
|
32
|
+
if (typeof input === 'boolean') return input ? 'true' : 'false';
|
|
33
|
+
return String(input);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function summarize(input: unknown[]): string {
|
|
37
|
+
return input.map(formatPrimitive).join(', ');
|
|
38
|
+
}
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<script lang="ts">
|
|
42
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
43
|
+
import { cn } from '../../utils/index.js';
|
|
44
|
+
import Self from './structured-value.svelte';
|
|
45
|
+
|
|
46
|
+
let {
|
|
47
|
+
value,
|
|
48
|
+
depth = 0,
|
|
49
|
+
class: className,
|
|
50
|
+
...restProps
|
|
51
|
+
}: HTMLAttributes<HTMLDivElement> & {
|
|
52
|
+
/** Arbitrary JSON-ish value to render as a key/value tree. */
|
|
53
|
+
value: unknown;
|
|
54
|
+
/** Internal recursion counter — leave unset at the call site. */
|
|
55
|
+
depth?: number;
|
|
56
|
+
} = $props();
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
{#snippet tag(kind: 'key' | 'index', label: string)}
|
|
60
|
+
{#if kind === 'index'}
|
|
61
|
+
<span
|
|
62
|
+
class="shrink-0 text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase"
|
|
63
|
+
>{label}</span
|
|
64
|
+
>
|
|
65
|
+
{:else}
|
|
66
|
+
<span class="shrink-0 text-muted era-text-trim">{label}:</span>
|
|
67
|
+
{/if}
|
|
68
|
+
{/snippet}
|
|
69
|
+
|
|
70
|
+
{#snippet leaf(v: unknown)}
|
|
71
|
+
{#if v === null || v === undefined}
|
|
72
|
+
<span class="min-w-0 truncate text-muted era-text-trim">null</span>
|
|
73
|
+
{:else if typeof v === 'boolean'}
|
|
74
|
+
<span class="min-w-0 truncate text-warning era-text-trim">{v ? 'true' : 'false'}</span>
|
|
75
|
+
{:else if typeof v === 'number'}
|
|
76
|
+
<span class="min-w-0 truncate text-fg tabular-nums era-text-trim">{v}</span>
|
|
77
|
+
{:else if typeof v === 'string'}
|
|
78
|
+
<span class="min-w-0 truncate text-fg era-text-trim" title={v}>{v}</span>
|
|
79
|
+
{:else if Array.isArray(v)}
|
|
80
|
+
{#if v.length === 0}
|
|
81
|
+
<span class="min-w-0 truncate text-muted era-text-trim">empty</span>
|
|
82
|
+
{:else}
|
|
83
|
+
<span class="min-w-0 truncate text-fg era-text-trim" title={summarize(v)}>{summarize(v)}</span>
|
|
84
|
+
{/if}
|
|
85
|
+
{:else}
|
|
86
|
+
<span class="min-w-0 truncate text-muted era-text-trim">empty</span>
|
|
87
|
+
{/if}
|
|
88
|
+
{/snippet}
|
|
89
|
+
|
|
90
|
+
{#snippet row(kind: 'key' | 'index', label: string, v: unknown)}
|
|
91
|
+
{#if isInline(v)}
|
|
92
|
+
<div class="flex min-w-0 items-baseline gap-(--era-gap)">
|
|
93
|
+
{@render tag(kind, label)}
|
|
94
|
+
{@render leaf(v)}
|
|
95
|
+
</div>
|
|
96
|
+
{:else}
|
|
97
|
+
<div class="flex min-w-0 flex-col gap-(--era-gap)">
|
|
98
|
+
{@render tag(kind, label)}
|
|
99
|
+
<div class="border-l border-divider pl-(--era-inset-md)">
|
|
100
|
+
<Self value={v} depth={depth + 1} />
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
{/snippet}
|
|
105
|
+
|
|
106
|
+
<div class={cn('flex min-w-0 flex-col gap-(--era-gap) text-body', className)} {...restProps}>
|
|
107
|
+
{#if depth > MAX_DEPTH}
|
|
108
|
+
<span class="text-muted era-text-trim">…</span>
|
|
109
|
+
{:else if isRecord(value)}
|
|
110
|
+
{#if Object.keys(value).length === 0}
|
|
111
|
+
<span class="text-muted era-text-trim">empty</span>
|
|
112
|
+
{:else}
|
|
113
|
+
{#each Object.entries(value) as [key, entry] (key)}
|
|
114
|
+
{@render row('key', key, entry)}
|
|
115
|
+
{/each}
|
|
116
|
+
{/if}
|
|
117
|
+
{:else if Array.isArray(value)}
|
|
118
|
+
{#if value.length === 0}
|
|
119
|
+
<span class="text-muted era-text-trim">empty</span>
|
|
120
|
+
{:else if isSimpleArray(value)}
|
|
121
|
+
{@render leaf(value)}
|
|
122
|
+
{:else}
|
|
123
|
+
{#each value as item, index (index)}
|
|
124
|
+
{@render row('index', String(index), item)}
|
|
125
|
+
{/each}
|
|
126
|
+
{/if}
|
|
127
|
+
{:else}
|
|
128
|
+
{@render leaf(value)}
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
type $$ComponentProps = HTMLAttributes<HTMLDivElement> & {
|
|
3
|
+
/** Arbitrary JSON-ish value to render as a key/value tree. */
|
|
4
|
+
value: unknown;
|
|
5
|
+
/** Internal recursion counter — leave unset at the call site. */
|
|
6
|
+
depth?: number;
|
|
7
|
+
};
|
|
8
|
+
declare const StructuredValue: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
9
|
+
type StructuredValue = ReturnType<typeof StructuredValue>;
|
|
10
|
+
export default StructuredValue;
|
package/dist/ai/tool/index.d.ts
CHANGED
|
@@ -2,4 +2,7 @@ export { default as Root, type ToolState } from './tool.svelte';
|
|
|
2
2
|
export { default as Header } from './tool-header.svelte';
|
|
3
3
|
export { default as Input } from './tool-input.svelte';
|
|
4
4
|
export { default as Output } from './tool-output.svelte';
|
|
5
|
+
export { default as Chip } from './tool-chip.svelte';
|
|
6
|
+
export { default as Chips } from './tool-chips.svelte';
|
|
7
|
+
export { default as Exec } from './tool-exec.svelte';
|
|
5
8
|
export { Content } from '../../ui/step/index.js';
|
package/dist/ai/tool/index.js
CHANGED
|
@@ -2,4 +2,7 @@ export { default as Root } from './tool.svelte';
|
|
|
2
2
|
export { default as Header } from './tool-header.svelte';
|
|
3
3
|
export { default as Input } from './tool-input.svelte';
|
|
4
4
|
export { default as Output } from './tool-output.svelte';
|
|
5
|
+
export { default as Chip } from './tool-chip.svelte';
|
|
6
|
+
export { default as Chips } from './tool-chips.svelte';
|
|
7
|
+
export { default as Exec } from './tool-exec.svelte';
|
|
5
8
|
export { Content } from '../../ui/step/index.js';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Component, Snippet } from 'svelte';
|
|
3
|
+
import Wrench from '@lucide/svelte/icons/wrench';
|
|
4
|
+
import { cn } from '../../utils/index.js';
|
|
5
|
+
import type { ToolState } from './tool.svelte';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
state: toolState = 'input-streaming',
|
|
9
|
+
active = false,
|
|
10
|
+
icon: Icon = Wrench,
|
|
11
|
+
onclick,
|
|
12
|
+
children,
|
|
13
|
+
class: className,
|
|
14
|
+
...restProps
|
|
15
|
+
}: {
|
|
16
|
+
/** AI-SDK tool lifecycle — drives the pulse and the failure dot. */
|
|
17
|
+
state?: ToolState;
|
|
18
|
+
/** Selected: this chip owns the shared expansion area. */
|
|
19
|
+
active?: boolean;
|
|
20
|
+
/** Leading glyph; defaults to a wrench. */
|
|
21
|
+
icon?: Component;
|
|
22
|
+
onclick?: () => void;
|
|
23
|
+
/** The chip label — the tool name. */
|
|
24
|
+
children?: Snippet;
|
|
25
|
+
class?: string;
|
|
26
|
+
} = $props();
|
|
27
|
+
|
|
28
|
+
// Same mapping tool.svelte uses to drive Step status.
|
|
29
|
+
const status = $derived(
|
|
30
|
+
(
|
|
31
|
+
{
|
|
32
|
+
'input-streaming': 'pending',
|
|
33
|
+
'input-available': 'running',
|
|
34
|
+
'output-available': 'done',
|
|
35
|
+
'output-error': 'error'
|
|
36
|
+
} as const
|
|
37
|
+
)[toolState]
|
|
38
|
+
);
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<!-- Compact horizontal tool call: a quiet pill that brightens on hover and
|
|
42
|
+
seats an active state (bg-hover) when it owns the expansion. While running
|
|
43
|
+
the icon pulses and the label shimmers — both collapse to static at instant
|
|
44
|
+
motion for free. A trailing destructive dot marks a failed call. -->
|
|
45
|
+
<button
|
|
46
|
+
type="button"
|
|
47
|
+
data-status={status}
|
|
48
|
+
{onclick}
|
|
49
|
+
class={cn(
|
|
50
|
+
'inline-flex h-(--era-h-xxs) min-w-0 max-w-full shrink-0 cursor-pointer era-interactive items-center gap-(--era-gap) rounded-(--era-rd-xxs) px-(--era-pill-xxs) text-body text-muted transition-colors duration-(--era-duration) ease-(--era-ease) hover:bg-(--era-highlight) hover:text-fg era-text-trim',
|
|
51
|
+
active && 'bg-hover text-fg',
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...restProps}
|
|
55
|
+
>
|
|
56
|
+
<Icon
|
|
57
|
+
class={cn('size-(--era-h-xs) shrink-0', status === 'running' && 'animate-pulse')}
|
|
58
|
+
aria-hidden="true"
|
|
59
|
+
/>
|
|
60
|
+
<span class={cn('min-w-0 truncate', status === 'running' && 'era-shimmer')}>
|
|
61
|
+
{@render children?.()}
|
|
62
|
+
</span>
|
|
63
|
+
{#if status === 'error'}
|
|
64
|
+
<span class="size-(--era-gap) shrink-0 rounded-full bg-destructive" aria-hidden="true"></span>
|
|
65
|
+
{/if}
|
|
66
|
+
</button>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Component, Snippet } from 'svelte';
|
|
2
|
+
import type { ToolState } from './tool.svelte';
|
|
3
|
+
type $$ComponentProps = {
|
|
4
|
+
/** AI-SDK tool lifecycle — drives the pulse and the failure dot. */
|
|
5
|
+
state?: ToolState;
|
|
6
|
+
/** Selected: this chip owns the shared expansion area. */
|
|
7
|
+
active?: boolean;
|
|
8
|
+
/** Leading glyph; defaults to a wrench. */
|
|
9
|
+
icon?: Component;
|
|
10
|
+
onclick?: () => void;
|
|
11
|
+
/** The chip label — the tool name. */
|
|
12
|
+
children?: Snippet;
|
|
13
|
+
class?: string;
|
|
14
|
+
};
|
|
15
|
+
declare const ToolChip: Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type ToolChip = ReturnType<typeof ToolChip>;
|
|
17
|
+
export default ToolChip;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { cn } from '../../utils/index.js';
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
streaming = false,
|
|
7
|
+
children,
|
|
8
|
+
expanded,
|
|
9
|
+
class: className
|
|
10
|
+
}: {
|
|
11
|
+
/** Appends a pulsing accent cursor after the last chip while streaming. */
|
|
12
|
+
streaming?: boolean;
|
|
13
|
+
/** The chips — a run of `Tool.Chip`s. */
|
|
14
|
+
children?: Snippet;
|
|
15
|
+
/** Shared detail area, rendered below the row when a chip is active. */
|
|
16
|
+
expanded?: Snippet;
|
|
17
|
+
class?: string;
|
|
18
|
+
} = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<!-- Alternate compact layout to the vertical Step expander: chips wrap in a row,
|
|
22
|
+
hairline-separated (a faded left rule on each chip after the first — the
|
|
23
|
+
cursor is excluded), with one shared expansion area below. -->
|
|
24
|
+
<div class={cn('flex min-w-0 flex-col', className)}>
|
|
25
|
+
<div
|
|
26
|
+
class="flex flex-wrap items-center gap-(--era-gap) [&>*:not(:first-child):not([data-chips-cursor])]:border-l [&>*:not(:first-child):not([data-chips-cursor])]:border-divider-faded [&>*:not(:first-child):not([data-chips-cursor])]:pl-(--era-gap)"
|
|
27
|
+
>
|
|
28
|
+
{@render children?.()}
|
|
29
|
+
{#if streaming}
|
|
30
|
+
<span
|
|
31
|
+
data-chips-cursor
|
|
32
|
+
class="size-(--era-gap) shrink-0 rounded-full bg-primary"
|
|
33
|
+
aria-hidden="true"
|
|
34
|
+
></span>
|
|
35
|
+
{/if}
|
|
36
|
+
</div>
|
|
37
|
+
{#if expanded}
|
|
38
|
+
<div class="mt-(--era-gap) min-w-0">
|
|
39
|
+
{@render expanded()}
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
/* Stream-head cursor pulse — timing derives from the motion axis (6×
|
|
46
|
+
--era-duration). At instant the duration is 0s and the dot freezes fully
|
|
47
|
+
opaque, so reduced-motion and data-motion="instant" both still it. */
|
|
48
|
+
[data-chips-cursor] {
|
|
49
|
+
animation: era-chips-cursor calc(var(--era-duration) * 6) steps(2, start) infinite;
|
|
50
|
+
}
|
|
51
|
+
@keyframes era-chips-cursor {
|
|
52
|
+
50% {
|
|
53
|
+
opacity: 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
/** Appends a pulsing accent cursor after the last chip while streaming. */
|
|
4
|
+
streaming?: boolean;
|
|
5
|
+
/** The chips — a run of `Tool.Chip`s. */
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
/** Shared detail area, rendered below the row when a chip is active. */
|
|
8
|
+
expanded?: Snippet;
|
|
9
|
+
class?: string;
|
|
10
|
+
};
|
|
11
|
+
declare const ToolChips: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type ToolChips = ReturnType<typeof ToolChips>;
|
|
13
|
+
export default ToolChips;
|