@signal9/era-ui 2.21.0 → 2.23.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/artifact/artifact-actions.svelte +3 -1
- package/dist/ai/artifact/artifact-content.svelte +3 -1
- package/dist/ai/artifact/artifact-header.svelte +5 -1
- package/dist/ai/artifact/artifact.svelte +5 -1
- package/dist/ai/chain-of-thought/chain-of-thought-step.svelte +5 -1
- package/dist/ai/chain-of-thought/chain-of-thought.svelte +1 -1
- package/dist/ai/confirmation/confirmation-args.svelte +7 -7
- package/dist/ai/conversation/conversation-content.svelte +4 -1
- package/dist/ai/conversations/conversations-group.svelte +1 -1
- package/dist/ai/conversations/conversations.svelte.js +5 -1
- package/dist/ai/error-panel/error-panel.svelte +1 -1
- package/dist/ai/inline-citation/inline-citation.svelte +22 -5
- package/dist/ai/loader.svelte +4 -1
- package/dist/ai/message/message-usage.svelte +4 -2
- package/dist/ai/open-in-chat/open-in-chat.svelte +16 -4
- package/dist/ai/plan/plan-header.svelte +1 -1
- package/dist/ai/project.js +10 -2
- package/dist/ai/prompt-input/prompt-input-attachments.svelte +1 -1
- package/dist/ai/prompt-input/prompt-input.svelte +2 -6
- package/dist/ai/run-status/run-status.svelte +5 -2
- package/dist/ai/runtime-feed/runtime-feed-item.svelte +1 -1
- package/dist/ai/runtime-feed/runtime-feed-root.svelte +1 -3
- package/dist/ai/structured-value/structured-value.svelte +8 -7
- package/dist/ai/suggestion/suggestions.svelte +4 -1
- package/dist/ai/task/task-content.svelte +4 -1
- package/dist/ai/tool/tool-chip.svelte +1 -1
- package/dist/ai/tool/tool-exec.svelte +1 -3
- package/dist/ai/tool/tool.svelte +1 -4
- package/dist/ai/web-preview/web-preview.svelte +1 -1
- package/dist/ai/workflow/node.svelte +3 -1
- package/dist/dev/audit/audits/_helpers.d.ts +19 -0
- package/dist/dev/audit/audits/_helpers.js +68 -0
- package/dist/dev/audit/audits/collapsed-text-trim.d.ts +2 -0
- package/dist/dev/audit/audits/collapsed-text-trim.js +52 -0
- package/dist/dev/audit/audits/flat-chrome.d.ts +2 -0
- package/dist/dev/audit/audits/flat-chrome.js +46 -0
- package/dist/dev/audit/audits/icon-flush.d.ts +2 -0
- package/dist/dev/audit/audits/icon-flush.js +51 -0
- package/dist/dev/audit/audits/index.d.ts +7 -1
- package/dist/dev/audit/audits/index.js +20 -1
- package/dist/dev/audit/audits/off-axis-motion.d.ts +2 -0
- package/dist/dev/audit/audits/off-axis-motion.js +60 -0
- package/dist/dev/audit/audits/off-tier-height.d.ts +2 -0
- package/dist/dev/audit/audits/off-tier-height.js +47 -0
- package/dist/dev/audit/audits/single-glyph-square.d.ts +2 -0
- package/dist/dev/audit/audits/single-glyph-square.js +50 -0
- package/dist/dev/axis-signature.d.ts +17 -0
- package/dist/dev/axis-signature.js +87 -0
- package/dist/dev/index.d.ts +1 -0
- package/dist/dev/index.js +1 -0
- package/dist/era-ui.css +1 -1
- package/dist/os/window.svelte +1 -1
- package/dist/styles/typography.css +9 -1
- package/dist/ui/button/button.svelte +10 -0
- package/dist/ui/card/card.svelte +3 -1
- package/dist/ui/command/command-input.svelte +1 -1
- package/dist/ui/command/command-item.svelte +1 -1
- package/dist/ui/command/command-link-item.svelte +1 -1
- package/dist/ui/command-bar/command-bar.svelte +2 -2
- package/dist/ui/copy-button/copy-button.svelte +3 -1
- package/dist/ui/input/input.svelte +1 -1
- package/dist/ui/kv/kv.svelte +1 -1
- package/dist/ui/pane/pane-close.svelte +9 -1
- package/dist/ui/select/select-trigger.svelte +1 -1
- package/dist/ui/step/step-summary.svelte +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +9 -9
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
let { children, class: className }: { children?: Snippet; class?: string } = $props();
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
|
-
<div class={cn('flex shrink-0 items-center gap-(--era-gap)', className)}>
|
|
7
|
+
<div class={cn('flex shrink-0 items-center gap-(--era-gap)', className)}>
|
|
8
|
+
{@render children?.()}
|
|
9
|
+
</div>
|
|
@@ -4,4 +4,6 @@
|
|
|
4
4
|
let { children, class: className }: { children?: Snippet; class?: string } = $props();
|
|
5
5
|
</script>
|
|
6
6
|
|
|
7
|
-
<div class={cn('min-h-0 flex-1 overflow-auto p-(--era-pad-md)', className)}>
|
|
7
|
+
<div class={cn('min-h-0 flex-1 overflow-auto p-(--era-pad-md)', className)}>
|
|
8
|
+
{@render children?.()}
|
|
9
|
+
</div>
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { cn } from '../../utils/index.js';
|
|
5
|
-
let {
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();
|
|
6
10
|
</script>
|
|
7
11
|
|
|
8
12
|
<div
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { cn } from '../../utils/index.js';
|
|
5
|
-
let {
|
|
5
|
+
let {
|
|
6
|
+
children,
|
|
7
|
+
class: className,
|
|
8
|
+
...restProps
|
|
9
|
+
}: HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();
|
|
6
10
|
</script>
|
|
7
11
|
|
|
8
12
|
<div
|
|
@@ -26,7 +26,11 @@
|
|
|
26
26
|
<Icon
|
|
27
27
|
class={cn(
|
|
28
28
|
'size-(--era-h-xs) shrink-0',
|
|
29
|
-
status === 'active'
|
|
29
|
+
status === 'active'
|
|
30
|
+
? 'text-bright'
|
|
31
|
+
: status === 'pending'
|
|
32
|
+
? 'text-muted opacity-50'
|
|
33
|
+
: 'text-muted'
|
|
30
34
|
)}
|
|
31
35
|
aria-hidden="true"
|
|
32
36
|
/>
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
import { Switch } from '../../ui/switch/index.js';
|
|
5
5
|
import { ApprovalController, getApprovalContext } from './approval.svelte.js';
|
|
6
6
|
|
|
7
|
-
let {
|
|
8
|
-
|
|
9
|
-
class: className
|
|
10
|
-
}: { controller?: ApprovalController; class?: string } = $props();
|
|
7
|
+
let { controller, class: className }: { controller?: ApprovalController; class?: string } =
|
|
8
|
+
$props();
|
|
11
9
|
|
|
12
10
|
const ctrl = $derived(controller ?? getApprovalContext());
|
|
13
11
|
|
|
@@ -27,10 +25,12 @@
|
|
|
27
25
|
<div
|
|
28
26
|
class={cn(
|
|
29
27
|
'flex min-w-0 flex-col gap-(--era-gap)',
|
|
30
|
-
ctrl.isDirty(key) && 'border-s-2
|
|
28
|
+
ctrl.isDirty(key) && 'border-warning border-s-2 ps-(--era-pad-sm)'
|
|
31
29
|
)}
|
|
32
30
|
>
|
|
33
|
-
<span class="text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase"
|
|
31
|
+
<span class="text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase"
|
|
32
|
+
>{key}</span
|
|
33
|
+
>
|
|
34
34
|
|
|
35
35
|
{#if kind === 'boolean'}
|
|
36
36
|
<Switch
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
)}
|
|
52
52
|
/>
|
|
53
53
|
{#if !ctrl.isValid(key)}
|
|
54
|
-
<span class="text-body text-
|
|
54
|
+
<span class="text-body era-text-trim text-destructive">Invalid JSON</span>
|
|
55
55
|
{/if}
|
|
56
56
|
{:else}
|
|
57
57
|
<Input
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
>
|
|
25
25
|
<div
|
|
26
26
|
{@attach stick.content}
|
|
27
|
-
class={cn(
|
|
27
|
+
class={cn(
|
|
28
|
+
'mx-auto flex w-full max-w-2xl flex-col gap-(--era-pad-lg) p-(--era-pad-md)',
|
|
29
|
+
className
|
|
30
|
+
)}
|
|
28
31
|
{...restProps}
|
|
29
32
|
>
|
|
30
33
|
{@render children?.()}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
<div class={cn('flex min-w-0 flex-col gap-(--era-gap)', className)} {...restProps}>
|
|
20
20
|
{#if label}
|
|
21
21
|
<span
|
|
22
|
-
class="px-(--era-inset-md) text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase
|
|
22
|
+
class="px-(--era-inset-md) text-[0.625rem] era-text-trim-caps leading-none tracking-[0.15em] text-muted uppercase"
|
|
23
23
|
>
|
|
24
24
|
{label}
|
|
25
25
|
</span>
|
|
@@ -46,7 +46,11 @@ export function bucketConversations(items, now) {
|
|
|
46
46
|
const newestFirst = (a, b) => b.updatedAt - a.updatedAt;
|
|
47
47
|
const groups = [];
|
|
48
48
|
if (starred.length > 0) {
|
|
49
|
-
groups.push({
|
|
49
|
+
groups.push({
|
|
50
|
+
bucket: 'starred',
|
|
51
|
+
label: BUCKET_LABELS.starred,
|
|
52
|
+
items: starred.sort(newestFirst)
|
|
53
|
+
});
|
|
50
54
|
}
|
|
51
55
|
for (const bucket of AGE_ORDER) {
|
|
52
56
|
if (byAge[bucket].length > 0) {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
{#if showDetail}
|
|
74
74
|
<!-- The whole summary row toggles the raw detail; quiet until hovered. -->
|
|
75
75
|
<Collapsible.Trigger
|
|
76
|
-
class="flex w-full min-w-0 cursor-pointer items-center gap-(--era-gap) text-body text-muted
|
|
76
|
+
class="flex w-full min-w-0 era-interactive cursor-pointer items-center gap-(--era-gap) text-body text-muted"
|
|
77
77
|
>
|
|
78
78
|
{@render head()}
|
|
79
79
|
<ChevronDown
|
|
@@ -36,28 +36,45 @@
|
|
|
36
36
|
target="_blank"
|
|
37
37
|
rel="noreferrer"
|
|
38
38
|
>
|
|
39
|
-
<Badge class="ml-1"
|
|
39
|
+
<Badge class="ml-1"
|
|
40
|
+
>{host(sources[0]?.href ?? '')}{sources.length > 1 ? ` +${sources.length - 1}` : ''}</Badge
|
|
41
|
+
>
|
|
40
42
|
</LinkPreview.Trigger>
|
|
41
43
|
<LinkPreview.Content class="flex w-72 flex-col gap-(--era-gap)">
|
|
42
44
|
{#if sources.length > 1}
|
|
43
45
|
<div class="flex items-center justify-between">
|
|
44
46
|
<span class="text-body text-muted tabular-nums">{index + 1} of {sources.length}</span>
|
|
45
47
|
<div class="flex gap-(--era-gap)">
|
|
46
|
-
<Button
|
|
48
|
+
<Button
|
|
49
|
+
icon
|
|
50
|
+
size="icon"
|
|
51
|
+
aria-label="Previous source"
|
|
52
|
+
onclick={() => (index = (index - 1 + sources.length) % sources.length)}
|
|
53
|
+
>
|
|
47
54
|
<ChevronLeft class="size-(--era-h-xs)" />
|
|
48
55
|
</Button>
|
|
49
|
-
<Button
|
|
56
|
+
<Button
|
|
57
|
+
icon
|
|
58
|
+
size="icon"
|
|
59
|
+
aria-label="Next source"
|
|
60
|
+
onclick={() => (index = (index + 1) % sources.length)}
|
|
61
|
+
>
|
|
50
62
|
<ChevronRight class="size-(--era-h-xs)" />
|
|
51
63
|
</Button>
|
|
52
64
|
</div>
|
|
53
65
|
</div>
|
|
54
66
|
{/if}
|
|
55
67
|
{#if active}
|
|
56
|
-
<a
|
|
68
|
+
<a
|
|
69
|
+
href={active.href}
|
|
70
|
+
target="_blank"
|
|
71
|
+
rel="noreferrer"
|
|
72
|
+
class="truncate text-body text-primary hover:underline"
|
|
73
|
+
>
|
|
57
74
|
{active.title ?? active.href}
|
|
58
75
|
</a>
|
|
59
76
|
{#if active.quote}
|
|
60
|
-
<blockquote class="border-
|
|
77
|
+
<blockquote class="border-divider-faded border-s-2 ps-(--era-gap) text-body text-muted">
|
|
61
78
|
{active.quote}
|
|
62
79
|
</blockquote>
|
|
63
80
|
{/if}
|
package/dist/ai/loader.svelte
CHANGED
|
@@ -5,4 +5,7 @@
|
|
|
5
5
|
let { class: className }: { class?: string } = $props();
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
|
-
<LoaderCircle
|
|
8
|
+
<LoaderCircle
|
|
9
|
+
class={cn('size-(--era-h-xs) animate-spin text-muted', className)}
|
|
10
|
+
aria-label="Loading"
|
|
11
|
+
/>
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
|
|
85
85
|
<div
|
|
86
86
|
class={cn(
|
|
87
|
-
'flex items-center gap-(--era-inset-sm) font-mono text-body
|
|
87
|
+
'flex items-center gap-(--era-inset-sm) font-mono text-body era-text-trim text-muted tabular-nums',
|
|
88
88
|
className
|
|
89
89
|
)}
|
|
90
90
|
{...restProps}
|
|
@@ -109,7 +109,9 @@
|
|
|
109
109
|
class="h-auto flex-col items-start gap-(--era-inset-xxs) py-(--era-inset-xxs) whitespace-normal"
|
|
110
110
|
>
|
|
111
111
|
{#each rows as [label, value] (label)}
|
|
112
|
-
<span
|
|
112
|
+
<span
|
|
113
|
+
class="flex w-full items-center justify-between gap-(--era-inset-md) tabular-nums"
|
|
114
|
+
>
|
|
113
115
|
<span class="text-muted">{label}</span>
|
|
114
116
|
<span>{formatTokens(value)}</span>
|
|
115
117
|
</span>
|
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
/** Deep-link builders for "continue this prompt elsewhere". */
|
|
3
3
|
export const providers = {
|
|
4
|
-
chatgpt: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
chatgpt: {
|
|
5
|
+
title: 'ChatGPT',
|
|
6
|
+
url: (q: string) => `https://chatgpt.com/?${new URLSearchParams({ q })}`
|
|
7
|
+
},
|
|
8
|
+
claude: {
|
|
9
|
+
title: 'Claude',
|
|
10
|
+
url: (q: string) => `https://claude.ai/new?${new URLSearchParams({ q })}`
|
|
11
|
+
},
|
|
12
|
+
t3: {
|
|
13
|
+
title: 'T3 Chat',
|
|
14
|
+
url: (q: string) => `https://t3.chat/new?${new URLSearchParams({ q })}`
|
|
15
|
+
},
|
|
16
|
+
scira: {
|
|
17
|
+
title: 'Scira',
|
|
18
|
+
url: (q: string) => `https://scira.ai/?${new URLSearchParams({ q })}`
|
|
19
|
+
},
|
|
8
20
|
v0: { title: 'v0', url: (q: string) => `https://v0.dev/chat?${new URLSearchParams({ q })}` }
|
|
9
21
|
} as const;
|
|
10
22
|
export type ProviderId = keyof typeof providers;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<Collapsible.Trigger
|
|
15
15
|
class={cn(
|
|
16
|
-
'flex h-(--era-h-md) w-full cursor-pointer
|
|
16
|
+
'flex h-(--era-h-md) w-full era-interactive cursor-pointer items-center gap-(--era-gap) rounded-(--era-rd-md) px-(--era-inset-md) text-body text-bright hover:bg-(--era-highlight)',
|
|
17
17
|
className
|
|
18
18
|
)}
|
|
19
19
|
>
|
package/dist/ai/project.js
CHANGED
|
@@ -29,7 +29,11 @@ export function projectMessages(messages) {
|
|
|
29
29
|
const flushRow = () => {
|
|
30
30
|
if (row.length === 0)
|
|
31
31
|
return;
|
|
32
|
-
blocks.push({
|
|
32
|
+
blocks.push({
|
|
33
|
+
type: 'tool-row',
|
|
34
|
+
key: `${message.id}:tools:${row[0].toolCallId}`,
|
|
35
|
+
tools: row
|
|
36
|
+
});
|
|
33
37
|
row = [];
|
|
34
38
|
};
|
|
35
39
|
for (const [i, part] of message.parts.entries()) {
|
|
@@ -42,7 +46,11 @@ export function projectMessages(messages) {
|
|
|
42
46
|
case 'reasoning':
|
|
43
47
|
flushRow();
|
|
44
48
|
if (part.text)
|
|
45
|
-
blocks.push({
|
|
49
|
+
blocks.push({
|
|
50
|
+
type: 'reasoning',
|
|
51
|
+
key: `${message.id}:reasoning:${i}`,
|
|
52
|
+
text: part.text
|
|
53
|
+
});
|
|
46
54
|
break;
|
|
47
55
|
case 'tool-call': {
|
|
48
56
|
if (seenTools.has(part.toolCallId))
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
<!-- md control geometry, like a Button with an icon: wall inset and
|
|
23
23
|
icon gap both --era-inset-md, ink centered by era-text-trim. -->
|
|
24
24
|
<div
|
|
25
|
-
class="flex h-(--era-h-md) items-center gap-(--era-inset-md) rounded-(--era-rd-md) bg-(--era-surface-bg) px-(--era-inset-md) text-body text-muted shadow-(--era-shadow)
|
|
25
|
+
class="flex h-(--era-h-md) items-center gap-(--era-inset-md) rounded-(--era-rd-md) bg-(--era-surface-bg) px-(--era-inset-md) text-body era-text-trim text-muted shadow-(--era-shadow)"
|
|
26
26
|
>
|
|
27
27
|
<Paperclip class="size-(--era-h-xs) shrink-0" />
|
|
28
28
|
<span class="max-w-32 truncate">{a.filename}</span>
|
|
@@ -2,11 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLFormAttributes } from 'svelte/elements';
|
|
4
4
|
import { cn } from '../../utils/index.js';
|
|
5
|
-
import {
|
|
6
|
-
Attachments,
|
|
7
|
-
setAttachmentsContext,
|
|
8
|
-
type PromptMessage
|
|
9
|
-
} from './attachments.svelte.js';
|
|
5
|
+
import { Attachments, setAttachmentsContext, type PromptMessage } from './attachments.svelte.js';
|
|
10
6
|
|
|
11
7
|
let {
|
|
12
8
|
accept = '',
|
|
@@ -88,7 +84,7 @@
|
|
|
88
84
|
// base fill while a button inside is hovered, so the highlight lands on the
|
|
89
85
|
// button, not the bar. (General rule for any highlighting container with
|
|
90
86
|
// interactive children.)
|
|
91
|
-
'flex w-full flex-col
|
|
87
|
+
'flex w-full era-interactive flex-col rounded-(--era-rd-lg) bg-(--era-surface-bg-elevated) p-(--era-gap) text-body text-fg shadow-(--era-shadow-well) focus-within:bg-(--era-highlight) hover:bg-(--era-highlight) has-[button:hover]:bg-(--era-surface-bg-elevated)',
|
|
92
88
|
className
|
|
93
89
|
)}
|
|
94
90
|
{...restProps}
|
|
@@ -90,8 +90,11 @@
|
|
|
90
90
|
>
|
|
91
91
|
<div class="flex h-(--era-h-md) items-center gap-(--era-gap) px-(--era-inset-md)">
|
|
92
92
|
<!-- leading status dot + label -->
|
|
93
|
-
<span
|
|
94
|
-
|
|
93
|
+
<span
|
|
94
|
+
class="flex items-center gap-(--era-inset-xxs) font-mono text-body {style.text} era-text-trim"
|
|
95
|
+
>
|
|
96
|
+
<span class={cn('size-1.5 shrink-0 rounded-full', style.dot, style.pulse && 'animate-pulse')}
|
|
97
|
+
></span>
|
|
95
98
|
{style.label}
|
|
96
99
|
</span>
|
|
97
100
|
|
|
@@ -19,9 +19,7 @@
|
|
|
19
19
|
} = $props();
|
|
20
20
|
|
|
21
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
|
-
);
|
|
22
|
+
const visible = $derived(items && limit !== undefined ? items.slice(-limit) : (items ?? []));
|
|
25
23
|
</script>
|
|
26
24
|
|
|
27
25
|
<div class={cn('flex min-w-0 flex-col gap-(--era-gap)', className)} {...restProps}>
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
function isPrimitive(input: unknown): input is Primitive {
|
|
12
12
|
return (
|
|
13
|
-
input === null ||
|
|
13
|
+
input === null ||
|
|
14
|
+
input === undefined ||
|
|
15
|
+
['string', 'number', 'boolean'].includes(typeof input)
|
|
14
16
|
);
|
|
15
17
|
}
|
|
16
18
|
|
|
@@ -58,12 +60,11 @@
|
|
|
58
60
|
|
|
59
61
|
{#snippet tag(kind: 'key' | 'index', label: string)}
|
|
60
62
|
{#if kind === 'index'}
|
|
61
|
-
<span
|
|
62
|
-
class="shrink-0 text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase"
|
|
63
|
+
<span class="shrink-0 text-[0.625rem] leading-none tracking-[0.15em] text-muted uppercase"
|
|
63
64
|
>{label}</span
|
|
64
65
|
>
|
|
65
66
|
{:else}
|
|
66
|
-
<span class="shrink-0 text-
|
|
67
|
+
<span class="shrink-0 era-text-trim text-muted">{label}:</span>
|
|
67
68
|
{/if}
|
|
68
69
|
{/snippet}
|
|
69
70
|
|
|
@@ -105,10 +106,10 @@
|
|
|
105
106
|
|
|
106
107
|
<div class={cn('flex min-w-0 flex-col gap-(--era-gap) text-body', className)} {...restProps}>
|
|
107
108
|
{#if depth > MAX_DEPTH}
|
|
108
|
-
<span class="text-
|
|
109
|
+
<span class="era-text-trim text-muted">…</span>
|
|
109
110
|
{:else if isRecord(value)}
|
|
110
111
|
{#if Object.keys(value).length === 0}
|
|
111
|
-
<span class="text-
|
|
112
|
+
<span class="era-text-trim text-muted">empty</span>
|
|
112
113
|
{:else}
|
|
113
114
|
{#each Object.entries(value) as [key, entry] (key)}
|
|
114
115
|
{@render row('key', key, entry)}
|
|
@@ -116,7 +117,7 @@
|
|
|
116
117
|
{/if}
|
|
117
118
|
{:else if Array.isArray(value)}
|
|
118
119
|
{#if value.length === 0}
|
|
119
|
-
<span class="text-
|
|
120
|
+
<span class="era-text-trim text-muted">empty</span>
|
|
120
121
|
{:else if isSimpleArray(value)}
|
|
121
122
|
{@render leaf(value)}
|
|
122
123
|
{:else}
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
</script>
|
|
12
12
|
|
|
13
13
|
<div
|
|
14
|
-
class={cn(
|
|
14
|
+
class={cn(
|
|
15
|
+
'scrollbar-none flex w-full flex-nowrap items-center gap-(--era-gap) overflow-x-auto',
|
|
16
|
+
className
|
|
17
|
+
)}
|
|
15
18
|
{...restProps}
|
|
16
19
|
>
|
|
17
20
|
{@render children?.()}
|
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- The task log rail: items hang off a left border, terminal-trace style. -->
|
|
10
10
|
<Step.Content
|
|
11
|
-
class={cn(
|
|
11
|
+
class={cn(
|
|
12
|
+
'border-divider-faded border-s-2 bg-transparent p-0 ps-(--era-pad-sm) shadow-none',
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
12
15
|
>
|
|
13
16
|
{@render children?.()}
|
|
14
17
|
</Step.Content>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
data-status={status}
|
|
48
48
|
{onclick}
|
|
49
49
|
class={cn(
|
|
50
|
-
'inline-flex h-(--era-h-xxs)
|
|
50
|
+
'inline-flex h-(--era-h-xxs) max-w-full min-w-0 shrink-0 era-interactive cursor-pointer items-center gap-(--era-gap) rounded-(--era-rd-xxs) px-(--era-pill-xxs) text-body era-text-trim text-muted transition-colors duration-(--era-duration) ease-(--era-ease) hover:bg-(--era-highlight) hover:text-fg',
|
|
51
51
|
active && 'bg-hover text-fg',
|
|
52
52
|
className
|
|
53
53
|
)}
|
|
@@ -23,9 +23,7 @@
|
|
|
23
23
|
{#if command || failed}
|
|
24
24
|
<div class="flex min-w-0 items-center gap-(--era-gap)">
|
|
25
25
|
{#if command}
|
|
26
|
-
<code class="min-w-0 flex-1 truncate font-mono text-body text-muted"
|
|
27
|
-
>{command}</code
|
|
28
|
-
>
|
|
26
|
+
<code class="min-w-0 flex-1 truncate font-mono text-body text-muted">{command}</code>
|
|
29
27
|
{/if}
|
|
30
28
|
{#if failed}
|
|
31
29
|
<Badge tone="destructive" class="ml-auto shrink-0">exit {exitCode}</Badge>
|
package/dist/ai/tool/tool.svelte
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
2
|
/** AI-SDK tool part lifecycle. */
|
|
3
3
|
export type ToolState =
|
|
4
|
-
| 'input-
|
|
5
|
-
| 'input-available'
|
|
6
|
-
| 'output-available'
|
|
7
|
-
| 'output-error';
|
|
4
|
+
'input-streaming' | 'input-available' | 'output-available' | 'output-error';
|
|
8
5
|
</script>
|
|
9
6
|
|
|
10
7
|
<script lang="ts">
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
className
|
|
22
22
|
)}
|
|
23
23
|
>
|
|
24
|
-
<Bar size="md" class="shrink-0
|
|
24
|
+
<Bar size="md" class="shrink-0 gap-(--era-gap) rounded-none">
|
|
25
25
|
<Button icon size="sm" aria-label="Reload" onclick={() => (nonce += 1)}>
|
|
26
26
|
<RotateCw class="size-(--era-h-xs)" />
|
|
27
27
|
</Button>
|
|
@@ -18,7 +18,9 @@
|
|
|
18
18
|
>
|
|
19
19
|
<Handle type="target" position={Position.Left} />
|
|
20
20
|
{#if title}
|
|
21
|
-
<div
|
|
21
|
+
<div
|
|
22
|
+
class="flex h-(--era-h-md) items-center border-b border-divider-faded px-(--era-inset-md) font-medium text-bright"
|
|
23
|
+
>
|
|
22
24
|
{title}
|
|
23
25
|
</div>
|
|
24
26
|
{/if}
|
|
@@ -27,3 +27,22 @@ export declare function siblingGap(a: HTMLElement, b: HTMLElement): number;
|
|
|
27
27
|
export declare function wallGap(container: HTMLElement, first: HTMLElement): number;
|
|
28
28
|
export declare function isFlex(style: CSSStyleDeclaration): boolean;
|
|
29
29
|
export declare function isHorizontalFlex(style: CSSStyleDeclaration): boolean;
|
|
30
|
+
export declare function tokenPx(el: HTMLElement, name: string): number;
|
|
31
|
+
/** The five tier heights, in px, in the element's density context. */
|
|
32
|
+
export declare function tiers(el: HTMLElement): {
|
|
33
|
+
name: string;
|
|
34
|
+
px: number;
|
|
35
|
+
}[];
|
|
36
|
+
/**
|
|
37
|
+
* Does this element paint chrome of its own — a fill, a border, or a shadow?
|
|
38
|
+
*
|
|
39
|
+
* The distinction decides whether a geometry rule is OBSERVABLE. A radius on a
|
|
40
|
+
* box that paints nothing is invisible, so holding it to the concentricity or
|
|
41
|
+
* single-glyph rules reports a defect nobody can see (the ":" literal in a time
|
|
42
|
+
* field is exactly this: a bare text span that happens to carry a radius).
|
|
43
|
+
*/
|
|
44
|
+
export declare function paintsChrome(el: HTMLElement): boolean;
|
|
45
|
+
/** The motion axis's own duration, in ms, as the element sees it (0 at data-motion="instant"). */
|
|
46
|
+
export declare function eraDurationMs(el: HTMLElement): number;
|
|
47
|
+
/** Parse a computed time list ("0.15s, 0s") to ms. */
|
|
48
|
+
export declare function timesMs(value: string): number[];
|
|
@@ -46,3 +46,71 @@ export function isFlex(style) {
|
|
|
46
46
|
export function isHorizontalFlex(style) {
|
|
47
47
|
return isFlex(style) && !style.flexDirection.startsWith('column');
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve an --era-* length token to px, in the element's own context.
|
|
51
|
+
*
|
|
52
|
+
* Every tier token is a calc() over --era-sp, and a computed custom property
|
|
53
|
+
* keeps its calc() UNEVALUATED — getComputedStyle hands back the expression, not
|
|
54
|
+
* a number. The only way to learn what a tier is actually worth is to make the
|
|
55
|
+
* browser lay something out at that height and measure it.
|
|
56
|
+
*
|
|
57
|
+
* Context matters: density can be scoped (a pinned-dense Pane handle inside a
|
|
58
|
+
* spacious page), so the probe is appended to the element's own density host,
|
|
59
|
+
* never to <html>. The cache is keyed by the host's --era-sp, so it invalidates
|
|
60
|
+
* itself the moment the density axis flips.
|
|
61
|
+
*/
|
|
62
|
+
const tokenCache = new WeakMap();
|
|
63
|
+
export function tokenPx(el, name) {
|
|
64
|
+
const host = (el.closest('[data-mode]') ?? document.documentElement);
|
|
65
|
+
const sp = getComputedStyle(host).getPropertyValue('--era-sp').trim();
|
|
66
|
+
const key = `${name}@${sp}`;
|
|
67
|
+
let cache = tokenCache.get(host);
|
|
68
|
+
if (!cache)
|
|
69
|
+
tokenCache.set(host, (cache = new Map()));
|
|
70
|
+
const hit = cache.get(key);
|
|
71
|
+
if (hit !== undefined)
|
|
72
|
+
return hit;
|
|
73
|
+
const probe = document.createElement('div');
|
|
74
|
+
probe.style.cssText = `position:absolute;visibility:hidden;height:var(${name})`;
|
|
75
|
+
host.appendChild(probe);
|
|
76
|
+
const px = probe.getBoundingClientRect().height;
|
|
77
|
+
probe.remove();
|
|
78
|
+
cache.set(key, px);
|
|
79
|
+
return px;
|
|
80
|
+
}
|
|
81
|
+
/** The five tier heights, in px, in the element's density context. */
|
|
82
|
+
export function tiers(el) {
|
|
83
|
+
return ['--era-h-xs', '--era-h-xxs', '--era-h-sm', '--era-h-md', '--era-h-lg'].map((name) => ({
|
|
84
|
+
name,
|
|
85
|
+
px: tokenPx(el, name)
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Does this element paint chrome of its own — a fill, a border, or a shadow?
|
|
90
|
+
*
|
|
91
|
+
* The distinction decides whether a geometry rule is OBSERVABLE. A radius on a
|
|
92
|
+
* box that paints nothing is invisible, so holding it to the concentricity or
|
|
93
|
+
* single-glyph rules reports a defect nobody can see (the ":" literal in a time
|
|
94
|
+
* field is exactly this: a bare text span that happens to carry a radius).
|
|
95
|
+
*/
|
|
96
|
+
export function paintsChrome(el) {
|
|
97
|
+
const s = getComputedStyle(el);
|
|
98
|
+
const transparentBg = s.backgroundColor === 'transparent' || /,\s*0\)$/.test(s.backgroundColor);
|
|
99
|
+
return !transparentBg || s.boxShadow !== 'none' || parseFloat(s.borderTopWidth) > 0;
|
|
100
|
+
}
|
|
101
|
+
/** The motion axis's own duration, in ms, as the element sees it (0 at data-motion="instant"). */
|
|
102
|
+
export function eraDurationMs(el) {
|
|
103
|
+
const raw = getComputedStyle(el).getPropertyValue('--era-duration').trim();
|
|
104
|
+
if (!raw)
|
|
105
|
+
return 0;
|
|
106
|
+
return raw.endsWith('ms') ? parseFloat(raw) : parseFloat(raw) * 1000;
|
|
107
|
+
}
|
|
108
|
+
/** Parse a computed time list ("0.15s, 0s") to ms. */
|
|
109
|
+
export function timesMs(value) {
|
|
110
|
+
return value
|
|
111
|
+
.split(',')
|
|
112
|
+
.map((v) => v.trim())
|
|
113
|
+
.filter(Boolean)
|
|
114
|
+
.map((v) => (v.endsWith('ms') ? parseFloat(v) : parseFloat(v) * 1000))
|
|
115
|
+
.filter((n) => !Number.isNaN(n));
|
|
116
|
+
}
|