@joewinke/jatui 0.38.6 → 0.40.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/CLAUDE.md +54 -5
- package/THIRD_PARTY_NOTICES.md +67 -0
- package/dist/actions/scrollEdge.d.ts +55 -0
- package/dist/actions/scrollEdge.js +144 -0
- package/dist/artifact-blocks/blocks/diagram/DiagramBlock.svelte.d.ts +1 -1
- package/dist/artifact-blocks/blocks/diagram/schema.d.ts +1 -1
- package/dist/components/BottomSheet.svelte +7 -6
- package/dist/components/BottomSheet.svelte.d.ts +2 -0
- package/dist/components/BulkActionBar.svelte +32 -2
- package/dist/components/BulkActionBar.svelte.d.ts +5 -1
- package/dist/components/CloseButton.svelte +75 -0
- package/dist/components/CloseButton.svelte.d.ts +29 -0
- package/dist/components/Drawer.svelte +10 -11
- package/dist/components/Drawer.svelte.d.ts +2 -8
- package/dist/components/FloatingActionBar.svelte +102 -34
- package/dist/components/FloatingActionBar.svelte.d.ts +9 -0
- package/dist/components/IconButton.svelte +200 -0
- package/dist/components/IconButton.svelte.d.ts +46 -0
- package/dist/components/LinkButton.svelte +117 -0
- package/dist/components/LinkButton.svelte.d.ts +32 -0
- package/dist/components/MediaExplorer.svelte +249 -0
- package/dist/components/MediaExplorer.svelte.d.ts +37 -0
- package/dist/components/MediaLightbox.svelte +2 -7
- package/dist/components/Modal.svelte +2 -10
- package/dist/components/NotificationCenter.svelte +481 -0
- package/dist/components/NotificationCenter.svelte.d.ts +29 -0
- package/dist/components/PinInput.svelte +94 -31
- package/dist/components/PinInput.svelte.d.ts +4 -0
- package/dist/components/SearchDropdown.svelte +54 -8
- package/dist/components/SearchDropdown.svelte.d.ts +12 -0
- package/dist/components/TextArea.svelte +67 -5
- package/dist/components/TextArea.svelte.d.ts +4 -6
- package/dist/components/ThemeSelector.svelte +29 -9
- package/dist/components/ThemeSelector.svelte.d.ts +6 -0
- package/dist/components/ThemeToggle.svelte +28 -3
- package/dist/components/ThemeToggle.svelte.d.ts +9 -0
- package/dist/components/agent/AgentLog.svelte +217 -0
- package/dist/components/agent/AgentLog.svelte.d.ts +72 -0
- package/dist/components/agent/AgentLogRow.svelte +281 -0
- package/dist/components/agent/AgentLogRow.svelte.d.ts +21 -0
- package/dist/components/agent/AgentTaskList.svelte +607 -0
- package/dist/components/agent/AgentTaskList.svelte.d.ts +40 -0
- package/dist/components/agent/AgentThinking.svelte +280 -0
- package/dist/components/agent/AgentThinking.svelte.d.ts +22 -0
- package/dist/components/agent/ComposerGlow.svelte +265 -0
- package/dist/components/agent/ComposerGlow.svelte.d.ts +18 -0
- package/dist/components/agent/ContextMeter.svelte +277 -0
- package/dist/components/agent/ContextMeter.svelte.d.ts +21 -0
- package/dist/components/agent/Questionnaire.svelte +767 -0
- package/dist/components/agent/Questionnaire.svelte.d.ts +29 -0
- package/dist/components/agent/ShimmerText.svelte +100 -0
- package/dist/components/agent/ShimmerText.svelte.d.ts +19 -0
- package/dist/components/agent/StreamedText.svelte +135 -0
- package/dist/components/agent/StreamedText.svelte.d.ts +13 -0
- package/dist/components/agent/agentLog.d.ts +22 -0
- package/dist/components/agent/agentLog.js +26 -0
- package/dist/components/agent/agentTaskList.d.ts +90 -0
- package/dist/components/agent/agentTaskList.js +93 -0
- package/dist/components/agent/agentThinking.d.ts +46 -0
- package/dist/components/agent/agentThinking.js +78 -0
- package/dist/components/agent/contextMeter.d.ts +97 -0
- package/dist/components/agent/contextMeter.js +156 -0
- package/dist/components/agent/questionnaire.d.ts +104 -0
- package/dist/components/agent/questionnaire.js +225 -0
- package/dist/components/agent/revealCore.d.ts +74 -0
- package/dist/components/agent/revealCore.js +160 -0
- package/dist/components/agent/revealTicker.d.ts +34 -0
- package/dist/components/agent/revealTicker.js +73 -0
- package/dist/components/agent/streamedText.d.ts +28 -0
- package/dist/components/agent/streamedText.js +59 -0
- package/dist/components/basic/Breadcrumb.svelte +226 -11
- package/dist/components/basic/Breadcrumb.svelte.d.ts +27 -1
- package/dist/components/basic/breadcrumbCollapse.d.ts +19 -0
- package/dist/components/basic/breadcrumbCollapse.js +12 -0
- package/dist/components/graph/GraphCanvas.svelte.d.ts +2 -2
- package/dist/components/messaging/MessageInput.svelte +34 -4
- package/dist/components/messaging/MessageInput.svelte.d.ts +7 -0
- package/dist/components/messaging/MessageItem.svelte +2 -2
- package/dist/components/notificationCenter.d.ts +65 -0
- package/dist/components/notificationCenter.js +110 -0
- package/dist/components/progress/Steps.svelte +269 -5
- package/dist/components/progress/Steps.svelte.d.ts +16 -1
- package/dist/components/progress/stepsSummary.d.ts +14 -0
- package/dist/components/progress/stepsSummary.js +32 -0
- package/dist/index.d.ts +48 -1
- package/dist/index.js +39 -1
- package/dist/selection/clickSelection.d.ts +89 -0
- package/dist/selection/clickSelection.js +152 -0
- package/dist/selection/createClickSelection.d.ts +79 -0
- package/dist/selection/createClickSelection.js +134 -0
- package/dist/styles/tokens.css +80 -1
- package/dist/utils/pinInput.d.ts +33 -0
- package/dist/utils/pinInput.js +63 -0
- package/dist/utils/textareaRows.d.ts +23 -0
- package/dist/utils/textareaRows.js +13 -0
- package/dist/utils/themeWipe.d.ts +36 -17
- package/dist/utils/themeWipe.js +145 -35
- package/package.json +3 -2
|
@@ -9,6 +9,15 @@ type $$ComponentProps = {
|
|
|
9
9
|
onchange?: (theme: string) => void;
|
|
10
10
|
/** Hit-target size — sm 40px, md 44px, lg 48px. Never below the 40px touch floor. */
|
|
11
11
|
size?: 'sm' | 'md' | 'lg';
|
|
12
|
+
/**
|
|
13
|
+
* Reveal the new theme with a circular View Transition wipe from the
|
|
14
|
+
* button (`themeWipe`): `true`/'hard' = crisp edge, 'soft' = feathered.
|
|
15
|
+
* The flip (bound `theme` + `onchange`) runs inside the transition, so
|
|
16
|
+
* the consumer's theme write must be synchronous in `onchange` or in an
|
|
17
|
+
* effect (it is flushed). Keyboard activation grows from the button's
|
|
18
|
+
* centre; reduced motion swaps instantly. Default off.
|
|
19
|
+
*/
|
|
20
|
+
reveal?: boolean | 'hard' | 'soft';
|
|
12
21
|
};
|
|
13
22
|
declare const ThemeToggle: import("svelte").Component<$$ComponentProps, {}, "theme">;
|
|
14
23
|
type ThemeToggle = ReturnType<typeof ThemeToggle>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AgentLog — a streaming agent transcript that draws itself down the page:
|
|
3
|
+
rows arrive one at a time, each growing in (height, a brief blur and a small
|
|
4
|
+
lift, a soft fading clip edge) while a curved tree guide strokes alongside
|
|
5
|
+
them, plus an optional "working" line at the tail.
|
|
6
|
+
|
|
7
|
+
This is the machinery only; what a row says is yours. Two ways to fill it:
|
|
8
|
+
|
|
9
|
+
items + row snippet <AgentLog items={visible} key={(s) => s.id}>
|
|
10
|
+
{#snippet row(step)}{step.label}{/snippet}
|
|
11
|
+
</AgentLog>
|
|
12
|
+
children <AgentLog>
|
|
13
|
+
<AgentLogRow first>…</AgentLogRow>
|
|
14
|
+
<AgentLogRow last>…</AgentLogRow>
|
|
15
|
+
</AgentLog>
|
|
16
|
+
|
|
17
|
+
Pacing is separate: slice your items with createRevealTicker (timer or
|
|
18
|
+
real-event driven). Rows already present on the log's first render rest;
|
|
19
|
+
only rows added afterwards animate (pass `appear` to animate those too).
|
|
20
|
+
|
|
21
|
+
Adapted from BoardUI's agent-log (WorkingRow, GuideBridge, the overall
|
|
22
|
+
structure): MIT License — Copyright (c) 2026 Mertcan Dundar Esmergul (BoardUI)
|
|
23
|
+
The working row sits deliberately OFF the guide: the tree records what the
|
|
24
|
+
agent did, and that one line has not happened yet.
|
|
25
|
+
-->
|
|
26
|
+
<script lang="ts" generics="T">
|
|
27
|
+
import { onMount, type Snippet } from 'svelte';
|
|
28
|
+
import AgentThinking from './AgentThinking.svelte';
|
|
29
|
+
import AgentLogRow from './AgentLogRow.svelte';
|
|
30
|
+
import { getAgentLogContext, setAgentLogContext } from './agentLog';
|
|
31
|
+
import { prefersReducedMotion } from '../../motion/level';
|
|
32
|
+
|
|
33
|
+
interface Props {
|
|
34
|
+
/** Rows to render (already sliced to what should be visible). */
|
|
35
|
+
items?: T[];
|
|
36
|
+
/** Row content for `items`. */
|
|
37
|
+
row?: Snippet<[item: T, index: number]>;
|
|
38
|
+
/** Stable key per item. Default: index. */
|
|
39
|
+
key?: (item: T, index: number) => string | number;
|
|
40
|
+
/** Extra classes per row (items mode). */
|
|
41
|
+
rowClass?: (item: T, index: number) => string;
|
|
42
|
+
/** Manual rows (AgentLogRow) instead of `items`. */
|
|
43
|
+
children?: Snippet;
|
|
44
|
+
/**
|
|
45
|
+
* Working line at the tail (AgentThinking "stars"): a label, or
|
|
46
|
+
* false/null/'' for none.
|
|
47
|
+
*/
|
|
48
|
+
working?: string | false | null;
|
|
49
|
+
/** Elapsed timer on the working line. */
|
|
50
|
+
workingTimer?: boolean;
|
|
51
|
+
/** When the work started (epoch ms) — keeps the working timer honest across re-mounts. */
|
|
52
|
+
workingStartedAt?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Draw the tree guide. Default true.
|
|
55
|
+
*/
|
|
56
|
+
guide?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Nested list: draw a stretch of trunk this many px ABOVE the first row
|
|
59
|
+
* (from the parent row's glyph down to this list) and make the first branch
|
|
60
|
+
* wait for it, so the two levels read as one line. 0 = none.
|
|
61
|
+
*/
|
|
62
|
+
bridge?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Animate rows present on the first render. Default: inherit — a log
|
|
65
|
+
* mounted inside a row that is itself growing in animates; a top-level log
|
|
66
|
+
* does not.
|
|
67
|
+
*/
|
|
68
|
+
appear?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Announce additions to screen readers (role="log", polite). Turn off for a
|
|
71
|
+
* log nested inside another live region.
|
|
72
|
+
*/
|
|
73
|
+
live?: boolean;
|
|
74
|
+
/** Accessible name for the log. */
|
|
75
|
+
ariaLabel?: string;
|
|
76
|
+
/** aria-busy while work is in progress. Default: a working line is shown. */
|
|
77
|
+
busy?: boolean;
|
|
78
|
+
class?: string;
|
|
79
|
+
listClass?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let {
|
|
83
|
+
items,
|
|
84
|
+
row,
|
|
85
|
+
key,
|
|
86
|
+
rowClass,
|
|
87
|
+
children,
|
|
88
|
+
working = null,
|
|
89
|
+
workingTimer = false,
|
|
90
|
+
workingStartedAt,
|
|
91
|
+
guide = true,
|
|
92
|
+
bridge = 0,
|
|
93
|
+
appear,
|
|
94
|
+
live = true,
|
|
95
|
+
ariaLabel,
|
|
96
|
+
busy,
|
|
97
|
+
class: className = '',
|
|
98
|
+
listClass = ''
|
|
99
|
+
}: Props = $props();
|
|
100
|
+
|
|
101
|
+
const parent = getAgentLogContext();
|
|
102
|
+
// Plain (non-reactive) flag: each row reads it once, when it mounts.
|
|
103
|
+
// svelte-ignore state_referenced_locally
|
|
104
|
+
const flag = { live: appear ?? parent?.isLive() ?? false };
|
|
105
|
+
const reduced = prefersReducedMotion();
|
|
106
|
+
|
|
107
|
+
setAgentLogContext({
|
|
108
|
+
isLive: () => flag.live,
|
|
109
|
+
get bridged() {
|
|
110
|
+
return bridge > 0;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
onMount(() => {
|
|
115
|
+
flag.live = true;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const keyOf = (item: T, i: number) => (key ? key(item, i) : i);
|
|
119
|
+
const showWorking = $derived(!!working);
|
|
120
|
+
</script>
|
|
121
|
+
|
|
122
|
+
<div
|
|
123
|
+
class="agent-log {className}"
|
|
124
|
+
role={live ? 'log' : undefined}
|
|
125
|
+
aria-live={live ? 'polite' : undefined}
|
|
126
|
+
aria-relevant={live ? 'additions' : undefined}
|
|
127
|
+
aria-label={ariaLabel}
|
|
128
|
+
aria-busy={busy ?? showWorking}
|
|
129
|
+
>
|
|
130
|
+
{#if bridge > 0 && guide && ((items && items.length > 0) || children)}
|
|
131
|
+
<span
|
|
132
|
+
class="al-bridge"
|
|
133
|
+
class:is-drawing={!reduced && flag.live}
|
|
134
|
+
style:top="-{bridge}px"
|
|
135
|
+
style:height="{bridge}px"
|
|
136
|
+
aria-hidden="true"
|
|
137
|
+
></span>
|
|
138
|
+
{/if}
|
|
139
|
+
<ol class="al-list {listClass}" class:has-bridge={bridge > 0} role="list">
|
|
140
|
+
{#if items && row}
|
|
141
|
+
{#each items as item, i (keyOf(item, i))}
|
|
142
|
+
<AgentLogRow
|
|
143
|
+
first={i === 0}
|
|
144
|
+
last={i === items.length - 1}
|
|
145
|
+
{guide}
|
|
146
|
+
class={rowClass?.(item, i) ?? ''}
|
|
147
|
+
>
|
|
148
|
+
{@render row(item, i)}
|
|
149
|
+
</AgentLogRow>
|
|
150
|
+
{/each}
|
|
151
|
+
{:else if children}
|
|
152
|
+
{@render children()}
|
|
153
|
+
{/if}
|
|
154
|
+
</ol>
|
|
155
|
+
|
|
156
|
+
{#if showWorking}
|
|
157
|
+
<AgentLogRow as="div" guide={false} class="al-working">
|
|
158
|
+
<div class="al-working-inner">
|
|
159
|
+
<AgentThinking
|
|
160
|
+
variant="stars"
|
|
161
|
+
size="sm"
|
|
162
|
+
label={working || 'Working'}
|
|
163
|
+
showTimer={workingTimer}
|
|
164
|
+
startedAt={workingStartedAt}
|
|
165
|
+
/>
|
|
166
|
+
</div>
|
|
167
|
+
</AgentLogRow>
|
|
168
|
+
{/if}
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<style>
|
|
172
|
+
.agent-log {
|
|
173
|
+
position: relative;
|
|
174
|
+
min-width: 0;
|
|
175
|
+
}
|
|
176
|
+
.al-list {
|
|
177
|
+
position: relative;
|
|
178
|
+
margin: 0;
|
|
179
|
+
padding: 0;
|
|
180
|
+
list-style: none;
|
|
181
|
+
/* rows must touch: each row's trunk meets the next row's branch */
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* From the parent row's glyph down into this list's first branch. */
|
|
188
|
+
.al-bridge {
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
position: absolute;
|
|
191
|
+
inset-inline-start: 0;
|
|
192
|
+
width: 1px;
|
|
193
|
+
background: color-mix(in oklab, var(--color-base-content) 24%, transparent);
|
|
194
|
+
transform-origin: top;
|
|
195
|
+
}
|
|
196
|
+
.al-bridge.is-drawing {
|
|
197
|
+
animation: al-bridge calc(var(--motion-fast, 150ms) * 0.8) linear both;
|
|
198
|
+
}
|
|
199
|
+
@keyframes al-bridge {
|
|
200
|
+
from {
|
|
201
|
+
transform: scaleY(0);
|
|
202
|
+
}
|
|
203
|
+
to {
|
|
204
|
+
transform: scaleY(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.al-working-inner {
|
|
209
|
+
padding-block: 0.25rem;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@media (prefers-reduced-motion: reduce) {
|
|
213
|
+
.al-bridge.is-drawing {
|
|
214
|
+
animation: none;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
</style>
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
declare function $$render<T>(): {
|
|
3
|
+
props: {
|
|
4
|
+
/** Rows to render (already sliced to what should be visible). */
|
|
5
|
+
items?: T[];
|
|
6
|
+
/** Row content for `items`. */
|
|
7
|
+
row?: Snippet<[item: T, index: number]>;
|
|
8
|
+
/** Stable key per item. Default: index. */
|
|
9
|
+
key?: (item: T, index: number) => string | number;
|
|
10
|
+
/** Extra classes per row (items mode). */
|
|
11
|
+
rowClass?: (item: T, index: number) => string;
|
|
12
|
+
/** Manual rows (AgentLogRow) instead of `items`. */
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
/**
|
|
15
|
+
* Working line at the tail (AgentThinking "stars"): a label, or
|
|
16
|
+
* false/null/'' for none.
|
|
17
|
+
*/
|
|
18
|
+
working?: string | false | null;
|
|
19
|
+
/** Elapsed timer on the working line. */
|
|
20
|
+
workingTimer?: boolean;
|
|
21
|
+
/** When the work started (epoch ms) — keeps the working timer honest across re-mounts. */
|
|
22
|
+
workingStartedAt?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Draw the tree guide. Default true.
|
|
25
|
+
*/
|
|
26
|
+
guide?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Nested list: draw a stretch of trunk this many px ABOVE the first row
|
|
29
|
+
* (from the parent row's glyph down to this list) and make the first branch
|
|
30
|
+
* wait for it, so the two levels read as one line. 0 = none.
|
|
31
|
+
*/
|
|
32
|
+
bridge?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Animate rows present on the first render. Default: inherit — a log
|
|
35
|
+
* mounted inside a row that is itself growing in animates; a top-level log
|
|
36
|
+
* does not.
|
|
37
|
+
*/
|
|
38
|
+
appear?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Announce additions to screen readers (role="log", polite). Turn off for a
|
|
41
|
+
* log nested inside another live region.
|
|
42
|
+
*/
|
|
43
|
+
live?: boolean;
|
|
44
|
+
/** Accessible name for the log. */
|
|
45
|
+
ariaLabel?: string;
|
|
46
|
+
/** aria-busy while work is in progress. Default: a working line is shown. */
|
|
47
|
+
busy?: boolean;
|
|
48
|
+
class?: string;
|
|
49
|
+
listClass?: string;
|
|
50
|
+
};
|
|
51
|
+
exports: {};
|
|
52
|
+
bindings: "";
|
|
53
|
+
slots: {};
|
|
54
|
+
events: {};
|
|
55
|
+
};
|
|
56
|
+
declare class __sveltets_Render<T> {
|
|
57
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
58
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
59
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
60
|
+
bindings(): "";
|
|
61
|
+
exports(): {};
|
|
62
|
+
}
|
|
63
|
+
interface $$IsomorphicComponent {
|
|
64
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
65
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
66
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
67
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
68
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
69
|
+
}
|
|
70
|
+
declare const AgentLog: $$IsomorphicComponent;
|
|
71
|
+
type AgentLog<T> = InstanceType<typeof AgentLog<T>>;
|
|
72
|
+
export default AgentLog;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AgentLogRow — one row of an AgentLog: the grow-in reveal, the soft clipping
|
|
3
|
+
edge that keeps it from being cut as it grows, and the stretch of tree guide
|
|
4
|
+
that belongs to it.
|
|
5
|
+
|
|
6
|
+
Adapted from BoardUI's agent-log (LogRow / RowConnector / useRevealMask):
|
|
7
|
+
MIT License — Copyright (c) 2026 Mertcan Dundar Esmergul (BoardUI)
|
|
8
|
+
What changed in the port: the `motion` library is replaced by CSS — height
|
|
9
|
+
0 → auto is a grid-template-rows 0fr → 1fr keyframe, the soft edge is a
|
|
10
|
+
registered `--jatui-reveal-fade` length animated inside a mask gradient,
|
|
11
|
+
the branch draws with stroke-dashoffset on pathLength=1 and the trunk with
|
|
12
|
+
a scaleY. Durations come from --motion-* tokens (so data-motion levels
|
|
13
|
+
rescale them) with glide easing; the guide pen stays linear on purpose so
|
|
14
|
+
junctions don't stall. Rows present on the log's first render rest instead
|
|
15
|
+
of animating.
|
|
16
|
+
|
|
17
|
+
At rest a row carries no mask, no blur, no overflow clip and no
|
|
18
|
+
compositing layer. Under prefers-reduced-motion it always renders at rest.
|
|
19
|
+
-->
|
|
20
|
+
<script lang="ts">
|
|
21
|
+
import type { Snippet } from 'svelte';
|
|
22
|
+
import { prefersReducedMotion } from '../../motion/level';
|
|
23
|
+
import { BRANCH_PATH, BRANCH_WIDTH, BRANCH_Y, getAgentLogContext } from './agentLog';
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
/** First row of its list: its branch starts with the row (no tail above to wait for). */
|
|
27
|
+
first?: boolean;
|
|
28
|
+
/** Last row of its list: no trunk below the elbow, so the guide ends on the corner. */
|
|
29
|
+
last?: boolean;
|
|
30
|
+
/** Draw the tree guide. false for rows that are not findings (e.g. the working row). */
|
|
31
|
+
guide?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Force the grow-in on or off. Default: animate when the row is added after
|
|
34
|
+
* its AgentLog's first render (always, when used outside an AgentLog).
|
|
35
|
+
*/
|
|
36
|
+
animate?: boolean;
|
|
37
|
+
/** Element to render: `li` inside a list, `div` elsewhere. */
|
|
38
|
+
as?: 'li' | 'div';
|
|
39
|
+
class?: string;
|
|
40
|
+
children: Snippet;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let {
|
|
44
|
+
first = false,
|
|
45
|
+
last = false,
|
|
46
|
+
guide = true,
|
|
47
|
+
animate,
|
|
48
|
+
as = 'li',
|
|
49
|
+
class: className = '',
|
|
50
|
+
children
|
|
51
|
+
}: Props = $props();
|
|
52
|
+
|
|
53
|
+
const ctx = getAgentLogContext();
|
|
54
|
+
const reduced = prefersReducedMotion();
|
|
55
|
+
// Decided once, at mount: a row never starts animating later.
|
|
56
|
+
// svelte-ignore state_referenced_locally
|
|
57
|
+
const live = !reduced && (animate ?? ctx?.isLive() ?? true);
|
|
58
|
+
// svelte-ignore state_referenced_locally
|
|
59
|
+
const waitForTail = !first || (first && !!ctx?.bridged);
|
|
60
|
+
|
|
61
|
+
let entering = $state(live);
|
|
62
|
+
|
|
63
|
+
$effect(() => {
|
|
64
|
+
if (!entering) return;
|
|
65
|
+
// Backstop in case animationend never arrives (a hidden ancestor, a host
|
|
66
|
+
// stylesheet overriding the animation).
|
|
67
|
+
const id = setTimeout(() => (entering = false), 2000);
|
|
68
|
+
return () => clearTimeout(id);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
function onAnimationEnd(e: AnimationEvent) {
|
|
72
|
+
if (e.animationName.endsWith('al-fade')) entering = false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Trunk/branch draw only for rows (or trunks) that arrive live. */
|
|
76
|
+
function trunkLive(): boolean {
|
|
77
|
+
return !reduced && (animate ?? ctx?.isLive() ?? true);
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<svelte:element
|
|
82
|
+
this={as}
|
|
83
|
+
class="al-row {className}"
|
|
84
|
+
class:is-entering={entering}
|
|
85
|
+
onanimationend={onAnimationEnd}
|
|
86
|
+
>
|
|
87
|
+
<div class="al-clip">
|
|
88
|
+
<div class="al-body" class:has-guide={guide}>
|
|
89
|
+
{#if guide}
|
|
90
|
+
<span class="al-guide" aria-hidden="true">
|
|
91
|
+
<svg
|
|
92
|
+
class="al-svg"
|
|
93
|
+
width={BRANCH_WIDTH}
|
|
94
|
+
height={BRANCH_Y + 1}
|
|
95
|
+
viewBox="0 0 {BRANCH_WIDTH} {BRANCH_Y + 1}"
|
|
96
|
+
fill="none"
|
|
97
|
+
>
|
|
98
|
+
<path
|
|
99
|
+
class="al-branch"
|
|
100
|
+
class:is-drawing={live}
|
|
101
|
+
class:waits={waitForTail}
|
|
102
|
+
d={BRANCH_PATH}
|
|
103
|
+
pathLength="1"
|
|
104
|
+
stroke="currentColor"
|
|
105
|
+
stroke-width="1"
|
|
106
|
+
/>
|
|
107
|
+
</svg>
|
|
108
|
+
{#if !last}
|
|
109
|
+
<span class="al-trunk" class:is-drawing={trunkLive()}></span>
|
|
110
|
+
{/if}
|
|
111
|
+
</span>
|
|
112
|
+
{/if}
|
|
113
|
+
{@render children()}
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</svelte:element>
|
|
117
|
+
|
|
118
|
+
<style>
|
|
119
|
+
@property --jatui-reveal-fade {
|
|
120
|
+
syntax: '<length>';
|
|
121
|
+
inherits: false;
|
|
122
|
+
initial-value: 0px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.al-row {
|
|
126
|
+
/* Timings: lengths of the BoardUI reveal, expressed on motion tokens. */
|
|
127
|
+
--al-grow-d: calc(var(--motion-move, 300ms) * 1.27);
|
|
128
|
+
--al-in-d: calc(var(--motion-move, 300ms) * 1.4);
|
|
129
|
+
--al-fade-d: calc(var(--motion-move, 300ms) * 1.47);
|
|
130
|
+
/* One pen speed (~160px/s): the tail above, then this row's branch. */
|
|
131
|
+
--al-tail-d: calc(var(--motion-fast, 150ms) * 0.8);
|
|
132
|
+
--al-branch-d: calc(var(--motion-fast, 150ms) * 0.93);
|
|
133
|
+
|
|
134
|
+
position: relative;
|
|
135
|
+
display: grid;
|
|
136
|
+
grid-template-rows: 1fr;
|
|
137
|
+
list-style: none;
|
|
138
|
+
margin: 0;
|
|
139
|
+
padding: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.al-clip {
|
|
143
|
+
position: relative;
|
|
144
|
+
min-height: 0;
|
|
145
|
+
min-width: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.al-body {
|
|
149
|
+
position: relative;
|
|
150
|
+
min-width: 0;
|
|
151
|
+
}
|
|
152
|
+
.al-body.has-guide {
|
|
153
|
+
padding-inline-start: 1rem;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ── grow-in ──────────────────────────────────────────────────────── */
|
|
157
|
+
.al-row.is-entering {
|
|
158
|
+
animation: al-grow var(--al-grow-d) var(--motion-glide, ease-out) both;
|
|
159
|
+
}
|
|
160
|
+
.al-row.is-entering > .al-clip {
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
/* Mask stops are alpha only (not a theme colour). The transparent stop
|
|
163
|
+
sits 1px past the box: two stops at exactly 100% make a degenerate
|
|
164
|
+
gradient that softens the last pixel row and breaks the guide where
|
|
165
|
+
rows meet. */
|
|
166
|
+
-webkit-mask-image: linear-gradient(
|
|
167
|
+
to bottom,
|
|
168
|
+
black calc(100% - var(--jatui-reveal-fade)),
|
|
169
|
+
transparent calc(100% + 1px)
|
|
170
|
+
);
|
|
171
|
+
mask-image: linear-gradient(
|
|
172
|
+
to bottom,
|
|
173
|
+
black calc(100% - var(--jatui-reveal-fade)),
|
|
174
|
+
transparent calc(100% + 1px)
|
|
175
|
+
);
|
|
176
|
+
animation:
|
|
177
|
+
al-in var(--al-in-d) var(--motion-glide, ease-out) both,
|
|
178
|
+
al-fade var(--al-fade-d) var(--motion-glide, ease-out) both;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@keyframes al-grow {
|
|
182
|
+
from {
|
|
183
|
+
grid-template-rows: 0fr;
|
|
184
|
+
}
|
|
185
|
+
to {
|
|
186
|
+
grid-template-rows: 1fr;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
@keyframes al-in {
|
|
190
|
+
from {
|
|
191
|
+
opacity: 0;
|
|
192
|
+
translate: 0 calc(var(--motion-travel, 16px) * 0.25);
|
|
193
|
+
filter: blur(6px);
|
|
194
|
+
}
|
|
195
|
+
to {
|
|
196
|
+
opacity: 1;
|
|
197
|
+
translate: 0 0;
|
|
198
|
+
filter: blur(0);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/* Longer than a text line, so a row is faintest exactly where it is clipped. */
|
|
202
|
+
@keyframes al-fade {
|
|
203
|
+
from {
|
|
204
|
+
--jatui-reveal-fade: 22px;
|
|
205
|
+
}
|
|
206
|
+
to {
|
|
207
|
+
--jatui-reveal-fade: 0px;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* ── tree guide ───────────────────────────────────────────────────── */
|
|
212
|
+
.al-guide {
|
|
213
|
+
pointer-events: none;
|
|
214
|
+
position: absolute;
|
|
215
|
+
inset-block: 0;
|
|
216
|
+
inset-inline-start: 0;
|
|
217
|
+
width: 0.75rem;
|
|
218
|
+
color: color-mix(in oklab, var(--color-base-content) 24%, transparent);
|
|
219
|
+
}
|
|
220
|
+
.al-svg {
|
|
221
|
+
position: absolute;
|
|
222
|
+
top: 0;
|
|
223
|
+
inset-inline-start: 0;
|
|
224
|
+
overflow: visible;
|
|
225
|
+
}
|
|
226
|
+
.al-svg:dir(rtl) {
|
|
227
|
+
scale: -1 1;
|
|
228
|
+
}
|
|
229
|
+
.al-branch.is-drawing {
|
|
230
|
+
stroke-dasharray: 1;
|
|
231
|
+
stroke-dashoffset: 1;
|
|
232
|
+
animation: al-draw var(--al-branch-d) linear forwards;
|
|
233
|
+
}
|
|
234
|
+
.al-branch.is-drawing.waits {
|
|
235
|
+
animation-delay: var(--al-tail-d);
|
|
236
|
+
}
|
|
237
|
+
@keyframes al-draw {
|
|
238
|
+
to {
|
|
239
|
+
stroke-dashoffset: 0;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.al-trunk {
|
|
244
|
+
position: absolute;
|
|
245
|
+
inset-inline-start: 0;
|
|
246
|
+
top: 8px; /* BRANCH_Y - BRANCH_RADIUS */
|
|
247
|
+
bottom: 0;
|
|
248
|
+
width: 1px;
|
|
249
|
+
background: currentColor;
|
|
250
|
+
transform-origin: top;
|
|
251
|
+
}
|
|
252
|
+
.al-trunk.is-drawing {
|
|
253
|
+
animation: al-scale var(--al-tail-d) linear both;
|
|
254
|
+
}
|
|
255
|
+
@keyframes al-scale {
|
|
256
|
+
from {
|
|
257
|
+
transform: scaleY(0);
|
|
258
|
+
}
|
|
259
|
+
to {
|
|
260
|
+
transform: scaleY(1);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/* Reduced motion: final state — no growth, no mask, no blur, no draw. */
|
|
265
|
+
@media (prefers-reduced-motion: reduce) {
|
|
266
|
+
.al-row.is-entering,
|
|
267
|
+
.al-row.is-entering > .al-clip,
|
|
268
|
+
.al-branch.is-drawing,
|
|
269
|
+
.al-trunk.is-drawing {
|
|
270
|
+
animation: none;
|
|
271
|
+
}
|
|
272
|
+
.al-row.is-entering > .al-clip {
|
|
273
|
+
overflow: visible;
|
|
274
|
+
-webkit-mask-image: none;
|
|
275
|
+
mask-image: none;
|
|
276
|
+
}
|
|
277
|
+
.al-branch.is-drawing {
|
|
278
|
+
stroke-dashoffset: 0;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** First row of its list: its branch starts with the row (no tail above to wait for). */
|
|
4
|
+
first?: boolean;
|
|
5
|
+
/** Last row of its list: no trunk below the elbow, so the guide ends on the corner. */
|
|
6
|
+
last?: boolean;
|
|
7
|
+
/** Draw the tree guide. false for rows that are not findings (e.g. the working row). */
|
|
8
|
+
guide?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Force the grow-in on or off. Default: animate when the row is added after
|
|
11
|
+
* its AgentLog's first render (always, when used outside an AgentLog).
|
|
12
|
+
*/
|
|
13
|
+
animate?: boolean;
|
|
14
|
+
/** Element to render: `li` inside a list, `div` elsewhere. */
|
|
15
|
+
as?: 'li' | 'div';
|
|
16
|
+
class?: string;
|
|
17
|
+
children: Snippet;
|
|
18
|
+
}
|
|
19
|
+
declare const AgentLogRow: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type AgentLogRow = ReturnType<typeof AgentLogRow>;
|
|
21
|
+
export default AgentLogRow;
|