@juspay/svelte-ui-components 2.130.0 → 2.131.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.
@@ -1,4 +1,4 @@
1
1
  import type { ThinkingIndicatorProperties } from './properties';
2
- declare const ThinkingIndicator: import("svelte").Component<ThinkingIndicatorProperties, {}, "expanded">;
2
+ declare const ThinkingIndicator: import("svelte").Component<ThinkingIndicatorProperties, {}, "selected" | "expanded">;
3
3
  type ThinkingIndicator = ReturnType<typeof ThinkingIndicator>;
4
4
  export default ThinkingIndicator;
@@ -1,21 +1,46 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  export type ThinkingIndicatorVariant = 'default' | 'bare';
3
+ export type ThinkingIndicatorKind = 'steps' | 'reasoning' | 'search' | 'coding';
4
+ export type ThinkingIndicatorTraceRow = {
5
+ /** The step, sentence, source title, or file action. */
6
+ primary: string;
7
+ /** A count, domain, filename or command shown after the primary text. */
8
+ secondary?: string;
9
+ /** Render `secondary` in the mono face (filenames, commands). */
10
+ mono?: boolean;
11
+ /** Coding rows: added line count, rendered as a +N stat. */
12
+ added?: number;
13
+ /** Coding rows: removed line count, rendered as a −N stat. */
14
+ removed?: number;
15
+ /** Search rows: renders the row as a link that opens in a new tab. */
16
+ href?: string;
17
+ };
3
18
  export type ThinkingIndicatorProperties = OptionalThinkingIndicatorProperties & MandatoryThinkingIndicatorProperties;
4
19
  export type MandatoryThinkingIndicatorProperties = {
5
20
  label: string;
6
21
  };
7
22
  export type OptionalThinkingIndicatorProperties = {
8
- /** Reasoning/steps text. Providing one makes the indicator an expandable disclosure. */
23
+ /**
24
+ * Reasoning text. Providing one (or `rows`) makes the indicator an expandable
25
+ * disclosure. Ignored in favour of `rows` when both are present.
26
+ */
9
27
  detail?: string;
10
- /** Bindable disclosure state — meaningful only when `detail` is set. */
28
+ /** Bindable disclosure state — meaningful only when `detail` or `rows` is set. */
11
29
  expanded?: boolean;
12
30
  /**
13
31
  * `bare` renders only the shimmering label — for chat bubbles where the surrounding
14
32
  * UI already supplies the avatar and layout. It never becomes expandable.
15
33
  */
16
34
  variant?: ThinkingIndicatorVariant;
35
+ /**
36
+ * Renders an elapsed `Ns` counter while the label is live. Starts at 0 when a busy
37
+ * phase begins, ticks every second, and freezes at its final value once the label
38
+ * settles (driven by `busy` when set, otherwise by the legacy status-line/detail
39
+ * shape). No effect on the `bare` variant.
40
+ */
41
+ showElapsed?: boolean;
17
42
  onToggle?: () => void;
18
- /** Leading indicator. Falls back to the built-in spinner. */
43
+ /** Leading indicator. Falls back to the built-in `Loader` spinner. */
19
44
  avatar?: Snippet;
20
45
  /** Disclosure chevron. Falls back to a built-in chevron that rotates on expand. */
21
46
  toggleIcon?: Snippet;
@@ -27,4 +52,39 @@ export type OptionalThinkingIndicatorProperties = {
27
52
  /** Test id for the status label itself (none by default). */
28
53
  labelTestId?: string;
29
54
  classes?: string;
55
+ /**
56
+ * The rows revealed so far for a kind-aware reasoning trace — append as the model
57
+ * streams; newly appended rows stagger in. When present (even as `[]`) the
58
+ * Accordion body renders the `kind` row renderer INSTEAD of the `detail`
59
+ * paragraph, and the indicator becomes expandable even without a `detail` string.
60
+ */
61
+ rows?: ThinkingIndicatorTraceRow[];
62
+ /** Which trace row renderer `rows` gets: checklist, prose, sources, or file edits. */
63
+ kind?: ThinkingIndicatorKind;
64
+ /**
65
+ * Host-owned turn state. While `true`: the label shimmers, the `steps` kind's
66
+ * newest row shows a live spinner, the elapsed counter ticks, and the disclosure
67
+ * auto-opens. Flipping it to `false` freezes the elapsed counter, fires
68
+ * `onsettled` exactly once, and — unless a user has toggled the disclosure by
69
+ * hand — schedules the automatic collapse after `collapseDelayMs`. Omit it
70
+ * entirely to get exactly today's released behaviour (manual toggling only).
71
+ */
72
+ busy?: boolean;
73
+ /** Search kind: the query rendered as a chip above the source rows. */
74
+ query?: string;
75
+ /** Search kind: settled trailing caption, e.g. "+7 more". */
76
+ moreLabel?: string;
77
+ /** Coding kind: rows become toggle buttons and report selection. */
78
+ selectable?: boolean;
79
+ /** Bindable index of the selected coding row (`null` = none). */
80
+ selected?: number | null;
81
+ onrowselect?: (index: number | null) => void;
82
+ /** Fires exactly once, the moment `busy` flips false. No effect while `busy` is never set. */
83
+ onsettled?: () => void;
84
+ /**
85
+ * Delay (ms) before the automatic post-settle collapse fires once `busy` flips
86
+ * false. `null` disables the automatic collapse. Has no effect while `busy` is
87
+ * never passed, or once the disclosure has been toggled by hand.
88
+ */
89
+ collapseDelayMs?: number | null;
30
90
  };
@@ -0,0 +1,418 @@
1
+ <script lang="ts">
2
+ import type { Action } from 'svelte/action';
3
+ import { onMount } from 'svelte';
4
+ import Loader from '../Loader/Loader.svelte';
5
+ import Pill from '../Pill/Pill.svelte';
6
+ import { computeMenuDropdownPosition } from '../Menu/dropdownPosition';
7
+ import type { ToolCallChip, ToolCallLogProperties } from './properties';
8
+
9
+ let { chips, onchipclick, testId, classes }: ToolCallLogProperties = $props();
10
+
11
+ // Exactly one popover open at a time, component-local.
12
+ let openIndex = $state<number | null>(null);
13
+
14
+ // Chips appended in one update stagger relative to the batch, not the list start.
15
+ let staggerBase = $state(0);
16
+
17
+ // Anchor elements for the portaled popover's position math, index-aligned with `chips`.
18
+ let chipEls: (HTMLButtonElement | null)[] = $state([]);
19
+
20
+ // The open popover's own element and measured size, fed back into the position math.
21
+ let popoverEl: HTMLDivElement | null = $state(null);
22
+ let popoverWidth = $state(0);
23
+ let popoverHeight = $state(0);
24
+ // Bumped on scroll/resize while a popover is mounted, so `popoverStyle` re-derives.
25
+ let popoverTick = $state(0);
26
+
27
+ // Gap between the chip and the popover below it. A plain constant rather than a
28
+ // themeable CSS var, matching Menu's own PORTAL_MENU_GAP — the portal's inline
29
+ // `position: fixed` coordinates already own layout, so a CSS var here would be
30
+ // unreadable from JS without a getComputedStyle round trip for no real gain.
31
+ const POPOVER_GAP = 6;
32
+
33
+ const growthWatcher: Action<HTMLElement, number> = (_node, initialCount) => {
34
+ let previousCount = initialCount;
35
+ return {
36
+ update(count: number): void {
37
+ if (count > previousCount) {
38
+ staggerBase = previousCount;
39
+ }
40
+ previousCount = count;
41
+ }
42
+ };
43
+ };
44
+
45
+ const chipDelay = (index: number): string => {
46
+ return `${Math.max(0, index - staggerBase) * 120}ms`;
47
+ };
48
+
49
+ const handleChipClick = (index: number, chip: ToolCallChip): void => {
50
+ if (typeof chip.detail === 'string' && chip.detail.length > 0) {
51
+ openIndex = openIndex === index ? null : index;
52
+ return;
53
+ }
54
+ onchipclick?.(index, chip);
55
+ };
56
+
57
+ // Bound to the window (rather than the popover) so Escape closes it regardless
58
+ // of focus, without forcing an interactive role onto the popover itself.
59
+ const handleWindowKeydown = (event: KeyboardEvent): void => {
60
+ if (event.key === 'Escape' && openIndex !== null) {
61
+ openIndex = null;
62
+ }
63
+ };
64
+
65
+ /**
66
+ * Fixed-position coordinates for the open popover. Re-derives whenever the
67
+ * open index, the anchor's live rect (via `popoverTick`, bumped on scroll/
68
+ * resize), or the popover's own measured size changes. Reuses Menu's portal
69
+ * placement math — the pure bottom-left corner case, viewport-clamped, is
70
+ * exactly what a chip popover anchored below-left of its chip needs, and the
71
+ * clamp is what keeps it from overflowing the right/bottom edge inside a
72
+ * narrow card.
73
+ */
74
+ const popoverStyle = $derived.by(() => {
75
+ if (openIndex === null || typeof window === 'undefined') {
76
+ return '';
77
+ }
78
+ const anchor = chipEls[openIndex];
79
+ if (anchor === null || typeof anchor === 'undefined') {
80
+ return '';
81
+ }
82
+ void popoverTick;
83
+ const anchorRect = anchor.getBoundingClientRect();
84
+ const { left, top } = computeMenuDropdownPosition({
85
+ container: {
86
+ left: anchorRect.left,
87
+ right: anchorRect.right,
88
+ top: anchorRect.top,
89
+ bottom: anchorRect.bottom
90
+ },
91
+ dropdown: { width: popoverWidth, height: popoverHeight },
92
+ placement: 'bottom-left',
93
+ gap: POPOVER_GAP,
94
+ viewport: { width: window.innerWidth, height: window.innerHeight }
95
+ });
96
+ return `position:fixed;left:${left}px;top:${top}px;right:auto;bottom:auto;margin:0;z-index:var(--tool-call-log-popover-z-index,1000);`;
97
+ });
98
+
99
+ /**
100
+ * Svelte action for the popover element: relocates it to `document.body`
101
+ * (mirroring Menu's `usePortal`) so an `overflow: hidden` or scrolling
102
+ * ancestor — a chat bubble, a card — can never clip it, and keeps
103
+ * `popoverTick` bumping (rAF-coalesced) while it is mounted so `popoverStyle`
104
+ * re-derives on scroll/resize. The popover only exists in the DOM while open
105
+ * (see the `{#if}` below), so the listeners live exactly as long as it does;
106
+ * `use:` actions never run during SSR.
107
+ */
108
+ const popoverPortal: Action<HTMLElement> = (node) => {
109
+ document.body.appendChild(node);
110
+ let frame: number | null = null;
111
+ const bump = (): void => {
112
+ if (frame !== null) {
113
+ return;
114
+ }
115
+ frame = requestAnimationFrame(() => {
116
+ frame = null;
117
+ popoverTick += 1;
118
+ });
119
+ };
120
+ window.addEventListener('scroll', bump, { capture: true, passive: true });
121
+ window.addEventListener('resize', bump);
122
+ return {
123
+ destroy(): void {
124
+ window.removeEventListener('scroll', bump, { capture: true });
125
+ window.removeEventListener('resize', bump);
126
+ if (frame !== null) {
127
+ cancelAnimationFrame(frame);
128
+ }
129
+ node.remove();
130
+ }
131
+ };
132
+ };
133
+
134
+ /**
135
+ * Closes the open popover on a click outside both its trigger chip and the
136
+ * popover itself. A click on ANY chip is deliberately treated as "inside" —
137
+ * that click's own handler (`handleChipClick`) already decides the next
138
+ * `openIndex`, and closing here too would race it: this listener runs after
139
+ * the chip's own `onclick` on the same bubbling click event, so without the
140
+ * guard it would immediately re-close a popover a click just opened.
141
+ */
142
+ const handleDocumentClick = (event: MouseEvent): void => {
143
+ const target = event.target;
144
+ if (openIndex === null || !(target instanceof Node)) {
145
+ return;
146
+ }
147
+ const clickedInsideChip = chipEls.some((chipEl) => chipEl !== null && chipEl.contains(target));
148
+ const clickedInsidePopover = popoverEl !== null && popoverEl.contains(target);
149
+ if (!clickedInsideChip && !clickedInsidePopover) {
150
+ openIndex = null;
151
+ }
152
+ };
153
+
154
+ onMount(() => {
155
+ document.addEventListener('click', handleDocumentClick);
156
+ return () => {
157
+ document.removeEventListener('click', handleDocumentClick);
158
+ };
159
+ });
160
+ </script>
161
+
162
+ <svelte:window onkeydown={handleWindowKeydown} />
163
+
164
+ {#snippet diffstatBadges(chip: ToolCallChip)}
165
+ {#if typeof chip.added === 'number'}
166
+ <span class="diffstat-pill diffstat-added">
167
+ <Pill text={`+${chip.added}`} />
168
+ </span>
169
+ {/if}
170
+ {#if typeof chip.removed === 'number'}
171
+ <span class="diffstat-pill diffstat-removed">
172
+ <Pill text={`−${chip.removed}`} />
173
+ </span>
174
+ {/if}
175
+ {/snippet}
176
+
177
+ <div
178
+ class="tool-call-log {classes ?? ''}"
179
+ use:growthWatcher={chips.length}
180
+ data-pw={typeof testId === 'string' ? testId : null}
181
+ testID={typeof testId === 'string' ? testId : null}
182
+ >
183
+ {#each chips as chip, index (index)}
184
+ {@const detail = chip.detail}
185
+ {@const hasDetail = typeof detail === 'string' && detail.length > 0}
186
+ {@const hasDiffstat = typeof chip.added === 'number' || typeof chip.removed === 'number'}
187
+ <div class="chip-wrap">
188
+ <button
189
+ class="chip"
190
+ class:error={chip.state === 'error'}
191
+ type="button"
192
+ style:animation-delay={chipDelay(index)}
193
+ aria-expanded={hasDetail ? openIndex === index : null}
194
+ onclick={() => handleChipClick(index, chip)}
195
+ bind:this={chipEls[index]}
196
+ data-pw={typeof testId === 'string' ? `${testId}-chip-${index}` : null}
197
+ testID={typeof testId === 'string' ? `${testId}-chip-${index}` : null}
198
+ >
199
+ {#if chip.state === 'running'}
200
+ <span class="chip-spinner" aria-hidden="true">
201
+ <Loader />
202
+ </span>
203
+ {/if}
204
+ <b class="chip-label">{chip.label}</b>
205
+ {#if typeof chip.meta === 'string' && chip.meta.length > 0}
206
+ <span class="chip-meta" class:mono={chip.mono}>{chip.meta}</span>
207
+ {/if}
208
+ {#if hasDiffstat}
209
+ <span class="diffstat chip-diffstat">
210
+ {@render diffstatBadges(chip)}
211
+ </span>
212
+ {/if}
213
+ </button>
214
+ {#if hasDetail && openIndex === index}
215
+ <div
216
+ class="chip-popover"
217
+ role="dialog"
218
+ aria-label={`${chip.label} details`}
219
+ tabindex="-1"
220
+ style={popoverStyle}
221
+ bind:this={popoverEl}
222
+ bind:clientWidth={popoverWidth}
223
+ bind:clientHeight={popoverHeight}
224
+ use:popoverPortal
225
+ data-pw={typeof testId === 'string' ? `${testId}-popover-${index}` : null}
226
+ testID={typeof testId === 'string' ? `${testId}-popover-${index}` : null}
227
+ >
228
+ <p class="popover-detail" class:mono={chip.mono}>{detail}</p>
229
+ {#if hasDiffstat}
230
+ <span class="diffstat popover-diffstat">
231
+ {@render diffstatBadges(chip)}
232
+ </span>
233
+ {/if}
234
+ </div>
235
+ {/if}
236
+ </div>
237
+ {/each}
238
+ </div>
239
+
240
+ <style>
241
+ @keyframes tool-call-log-fade-up {
242
+ from {
243
+ opacity: 0;
244
+ transform: translateY(9px);
245
+ }
246
+ to {
247
+ opacity: 1;
248
+ transform: none;
249
+ }
250
+ }
251
+
252
+ .tool-call-log {
253
+ display: flex;
254
+ flex-wrap: wrap;
255
+ align-items: flex-start;
256
+ gap: var(--tool-call-log-gap, 8px);
257
+ }
258
+
259
+ /* Bespoke <button> markup (Pill's root is a non-interactive <div> and can't carry
260
+ aria-expanded/button semantics — see docs), but its sizing recipe is deliberately
261
+ pulled from Pill's own tokens (line-height, cursor) so it reads as the same family
262
+ of control as the diffstat Pill badges nested inside it. */
263
+ .chip {
264
+ display: inline-flex;
265
+ align-items: center;
266
+ gap: var(--tool-call-log-chip-gap, 6px);
267
+ padding: var(--tool-call-log-chip-padding, 6px 10px);
268
+ background: var(--tool-call-log-chip-background, #fafafa);
269
+ border: var(--tool-call-log-chip-border, 1px solid #e4e4e7);
270
+ border-radius: var(--tool-call-log-chip-radius, 6px);
271
+ font: inherit;
272
+ font-size: var(--tool-call-log-font-size, 0.8125rem);
273
+ line-height: var(--tool-call-log-chip-line-height, 1);
274
+ color: var(--tool-call-log-label-color, #2b2b2b);
275
+ cursor: var(--tool-call-log-chip-cursor, pointer);
276
+ max-width: 100%;
277
+ animation: tool-call-log-fade-up 320ms var(--tool-call-log-ease, cubic-bezier(0.23, 1, 0.32, 1))
278
+ both;
279
+ transition:
280
+ background 150ms ease,
281
+ border-color 150ms ease;
282
+ }
283
+ .chip:hover {
284
+ background: var(--tool-call-log-chip-hover-background, #f1f1f1);
285
+ }
286
+ .chip[aria-expanded='true'] {
287
+ border-color: var(--tool-call-log-chip-open-border-color, #c7c7cc);
288
+ }
289
+
290
+ .chip.error {
291
+ color: var(--tool-call-log-error-color, #c93f38);
292
+ border-color: var(--tool-call-log-error-border-color, #f2b8b5);
293
+ background: var(--tool-call-log-error-background, #fdf1f0);
294
+ }
295
+ .chip.error:hover {
296
+ background: var(--tool-call-log-error-hover-background, #fbe6e5);
297
+ }
298
+
299
+ .chip-label {
300
+ font-weight: var(--tool-call-log-label-weight, 500);
301
+ white-space: nowrap;
302
+ overflow: hidden;
303
+ text-overflow: ellipsis;
304
+ }
305
+
306
+ .chip-meta {
307
+ color: var(--tool-call-log-meta-color, #9a9a9a);
308
+ font-size: var(--tool-call-log-meta-font-size, 0.75rem);
309
+ white-space: nowrap;
310
+ overflow: hidden;
311
+ text-overflow: ellipsis;
312
+ }
313
+ .chip-meta.mono {
314
+ font-family: var(--tool-call-log-mono-font, ui-monospace, Menlo, monospace);
315
+ }
316
+
317
+ /* Sizes and colors the nested library Loader via CSS custom-property
318
+ inheritance (no :global() needed — Loader is a plain DOM descendant of
319
+ this span, same pattern as ThinkingIndicator's `.avatar`). Loader itself
320
+ ships no literal fallback for --loader-foreground/-foreground-end/
321
+ -background, so all three are set explicitly here rather than left to
322
+ chance. */
323
+ .chip-spinner {
324
+ display: inline-flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ flex-shrink: 0;
328
+ --loader-width: var(--tool-call-log-spinner-size, 11px);
329
+ --loader-height: var(--tool-call-log-spinner-size, 11px);
330
+ --loader-before-width: 5px;
331
+ --loader-before-height: 5px;
332
+ --loader-after-width: 8px;
333
+ --loader-after-height: 8px;
334
+ --loader-foreground: var(--tool-call-log-spinner-color, #6b6b6b);
335
+ --loader-foreground-end: var(--tool-call-log-spinner-track-color, #dcdcdc);
336
+ --loader-background: var(--tool-call-log-chip-background, #fafafa);
337
+ }
338
+
339
+ .diffstat {
340
+ display: inline-flex;
341
+ align-items: center;
342
+ gap: 4px;
343
+ font-variant-numeric: tabular-nums;
344
+ flex-shrink: 0;
345
+ }
346
+ .chip-diffstat {
347
+ margin-left: var(--tool-call-log-diffstat-margin, 2px);
348
+ }
349
+
350
+ /* Pill customized into the small diffstat badge via its own --pill-* tokens
351
+ (inherited the same way — Pill is a plain descendant of this span). Pill
352
+ defaults `cursor` to `pointer` even when non-interactive, so it is pinned
353
+ back to `default` here: these badges carry no onclick. */
354
+ .diffstat-pill {
355
+ display: inline-flex;
356
+ --pill-padding: var(--tool-call-log-diffstat-padding, 0 4px);
357
+ --pill-border-radius: var(--tool-call-log-diffstat-radius, 4px);
358
+ --pill-font-size: var(--tool-call-log-diffstat-font-size, 0.6875rem);
359
+ --pill-font-family: var(--tool-call-log-mono-font, ui-monospace, Menlo, monospace);
360
+ --pill-line-height: 1;
361
+ --pill-cursor: default;
362
+ }
363
+ .diffstat-added {
364
+ --pill-background: var(--tool-call-log-added-background, #e4f5ee);
365
+ --pill-color: var(--tool-call-log-added-color, #1f7a5f);
366
+ }
367
+ .diffstat-removed {
368
+ --pill-background: var(--tool-call-log-removed-background, #fbeceb);
369
+ --pill-color: var(--tool-call-log-removed-color, #c93f38);
370
+ }
371
+
372
+ /* Always portaled to document.body (see `popoverPortal`), so `position:
373
+ fixed` is the baseline rather than a portal-only override — left/top/
374
+ z-index come from the inline `popoverStyle`, computed against the live
375
+ anchor rect. */
376
+ .chip-popover {
377
+ position: fixed;
378
+ z-index: var(--tool-call-log-popover-z-index, 1000);
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: var(--tool-call-log-popover-gap, 6px);
382
+ min-width: var(--tool-call-log-popover-min-width, 220px);
383
+ max-width: var(--tool-call-log-popover-max-width, 340px);
384
+ padding: var(--tool-call-log-popover-padding, 10px 12px);
385
+ background: var(--tool-call-log-popover-background, #ffffff);
386
+ border: var(--tool-call-log-popover-border, 1px solid #e4e4e7);
387
+ border-radius: var(--tool-call-log-popover-radius, 10px);
388
+ box-shadow: var(--tool-call-log-popover-shadow, 0 10px 30px rgba(0, 0, 0, 0.12));
389
+ animation: tool-call-log-fade-up 200ms var(--tool-call-log-ease, cubic-bezier(0.23, 1, 0.32, 1))
390
+ both;
391
+ }
392
+
393
+ .popover-detail {
394
+ margin: 0;
395
+ color: var(--tool-call-log-popover-color, #2b2b2b);
396
+ font-size: var(--tool-call-log-popover-font-size, 0.8125rem);
397
+ white-space: pre-wrap;
398
+ overflow-wrap: anywhere;
399
+ }
400
+ .popover-detail.mono {
401
+ font-family: var(--tool-call-log-mono-font, ui-monospace, Menlo, monospace);
402
+ }
403
+
404
+ @media (prefers-reduced-motion: reduce) {
405
+ .chip,
406
+ .chip-popover {
407
+ animation-duration: 0.001s;
408
+ }
409
+ /* Loader's own spin keyframe lives on its internally-scoped `.loader`
410
+ class, which this file cannot reach with a plain scoped selector —
411
+ :global() is required to disable it, same pattern as `.pill-dismiss
412
+ :global(svg)` elsewhere in the library. Loader ships no
413
+ reduced-motion guard of its own. */
414
+ .chip-spinner :global(.loader) {
415
+ animation: none;
416
+ }
417
+ }
418
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { ToolCallLogProperties } from './properties';
2
+ declare const ToolCallLog: import("svelte").Component<ToolCallLogProperties, {}, "">;
3
+ type ToolCallLog = ReturnType<typeof ToolCallLog>;
4
+ export default ToolCallLog;
@@ -0,0 +1,27 @@
1
+ export type ToolCallChipState = 'running' | 'done' | 'error';
2
+ export type ToolCallChip = {
3
+ /** The tool action, e.g. "Read", "Edit", "Run". */
4
+ label: string;
5
+ /** A filename, command or count shown after the label. */
6
+ meta?: string;
7
+ /** Render `meta` in the mono face (filenames, commands). */
8
+ mono?: boolean;
9
+ /** Chip state: `running` shows a spinner, `error` renders red-toned. */
10
+ state?: ToolCallChipState;
11
+ /** Diff stat: added line count, rendered as a +N pill. */
12
+ added?: number;
13
+ /** Diff stat: removed line count, rendered as a −N pill. */
14
+ removed?: number;
15
+ /** When present, the chip becomes expandable and shows this text in a popover. */
16
+ detail?: string;
17
+ };
18
+ export type ToolCallLogProperties = MandatoryToolCallLogProperties & OptionalToolCallLogProperties;
19
+ export type MandatoryToolCallLogProperties = {
20
+ /** The tool calls made so far this turn — append as they run; new chips stagger in. */
21
+ chips: ToolCallChip[];
22
+ };
23
+ export type OptionalToolCallLogProperties = {
24
+ onchipclick?: (index: number, chip: ToolCallChip) => void;
25
+ testId?: string;
26
+ classes?: string;
27
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -54,6 +54,8 @@ export { default as Calendar } from './Calendar/Calendar.svelte';
54
54
  export { default as RelativeTime } from './RelativeTime/RelativeTime.svelte';
55
55
  export { default as ThemeSwitcher } from './ThemeSwitcher/ThemeSwitcher.svelte';
56
56
  export { default as ThinkingIndicator } from './ThinkingIndicator/ThinkingIndicator.svelte';
57
+ export { default as ToolCallLog } from './ToolCallLog/ToolCallLog.svelte';
58
+ export { default as TaskList } from './TaskList/TaskList.svelte';
57
59
  export { default as Book } from './Book/Book.svelte';
58
60
  export { default as Browser } from './Browser/Browser.svelte';
59
61
  export { default as Phone } from './Phone/Phone.svelte';
@@ -143,6 +145,9 @@ export type * from './Calendar/properties';
143
145
  export type * from './RelativeTime/properties';
144
146
  export type * from './ThemeSwitcher/properties';
145
147
  export type * from './ThinkingIndicator/properties';
148
+ export type * from './ToolCallLog/properties';
149
+ export type * from './TaskList/properties';
150
+ export type * from './soundKit/properties';
146
151
  export type * from './Book/properties';
147
152
  export type * from './Browser/properties';
148
153
  export type * from './Phone/properties';
@@ -183,5 +188,6 @@ export type * from './ChatToolStatus/properties';
183
188
  export type * from './ChatBubble/properties';
184
189
  export type * from './HITL/properties';
185
190
  export type * from './Resizable/properties';
191
+ export { createSoundKit } from './soundKit/soundKit';
186
192
  export { validateInput } from './utils';
187
193
  export { formatNumberIndian } from './_chart/format';
package/dist/index.js CHANGED
@@ -54,6 +54,8 @@ export { default as Calendar } from './Calendar/Calendar.svelte';
54
54
  export { default as RelativeTime } from './RelativeTime/RelativeTime.svelte';
55
55
  export { default as ThemeSwitcher } from './ThemeSwitcher/ThemeSwitcher.svelte';
56
56
  export { default as ThinkingIndicator } from './ThinkingIndicator/ThinkingIndicator.svelte';
57
+ export { default as ToolCallLog } from './ToolCallLog/ToolCallLog.svelte';
58
+ export { default as TaskList } from './TaskList/TaskList.svelte';
57
59
  export { default as Book } from './Book/Book.svelte';
58
60
  export { default as Browser } from './Browser/Browser.svelte';
59
61
  export { default as Phone } from './Phone/Phone.svelte';
@@ -94,5 +96,6 @@ export { default as Resizable } from './Resizable/Resizable.svelte';
94
96
  export { ChatController } from './Chat/controller.svelte';
95
97
  export { partyOf } from './Chat/roles';
96
98
  export { SpeechToTextController } from './SpeechToText/controller.svelte';
99
+ export { createSoundKit } from './soundKit/soundKit';
97
100
  export { validateInput } from './utils';
98
101
  export { formatNumberIndian } from './_chart/format';
@@ -0,0 +1,29 @@
1
+ /** One of the five synthesized recipes SoundKit can play. */
2
+ export type SoundName = 'press' | 'tick' | 'release' | 'page' | 'pulse';
3
+ export type SoundKitOptions = {
4
+ /** localStorage key the enabled flag is persisted under. Defaults to `'sui-sound-enabled'`. */
5
+ storageKey?: string;
6
+ /** Gain applied to the shared master bus before the destination. Defaults to `0.32`. */
7
+ masterGain?: number;
8
+ };
9
+ export type SoundKit = {
10
+ /** Play one recipe by name. Silently does nothing while disabled or off the main thread. */
11
+ play: (name: SoundName) => void;
12
+ /**
13
+ * Install one capture-phase click listener on `root` (defaults to `document`) that maps
14
+ * click targets to sounds: an ancestor carrying `data-sound` always wins, otherwise plain
15
+ * semantics apply (checkbox/radio/switch/tab-ish -> tick, links -> page, buttons -> press).
16
+ * Calling it again re-scopes the listener to the new root.
17
+ */
18
+ attachClicks: (root?: Document | HTMLElement) => void;
19
+ /** Remove the listener installed by `attachClicks`, if any. */
20
+ detachClicks: () => void;
21
+ /** Set the enabled flag and persist it. */
22
+ setEnabled: (enabled: boolean) => void;
23
+ /** Read the current enabled flag, resolving it from storage on first call. */
24
+ isEnabled: () => boolean;
25
+ /** Flip the enabled flag and persist it, returning the new value. */
26
+ toggle: () => boolean;
27
+ /** `detachClicks` plus close the AudioContext, if one was ever created. */
28
+ dispose: () => void;
29
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { SoundKit, SoundKitOptions } from './properties';
2
+ export declare const createSoundKit: (options?: SoundKitOptions) => SoundKit;