@juspay/svelte-ui-components 2.130.1 → 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.
- package/dist/TaskList/TaskList.svelte +268 -0
- package/dist/TaskList/TaskList.svelte.d.ts +4 -0
- package/dist/TaskList/properties.d.ts +24 -0
- package/dist/TaskList/properties.js +1 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +621 -35
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte.d.ts +1 -1
- package/dist/ThinkingIndicator/properties.d.ts +63 -3
- package/dist/ToolCallLog/ToolCallLog.svelte +418 -0
- package/dist/ToolCallLog/ToolCallLog.svelte.d.ts +4 -0
- package/dist/ToolCallLog/properties.d.ts +27 -0
- package/dist/ToolCallLog/properties.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/soundKit/properties.d.ts +29 -0
- package/dist/soundKit/properties.js +1 -0
- package/dist/soundKit/soundKit.d.ts +2 -0
- package/dist/soundKit/soundKit.js +284 -0
- package/package.json +1 -1
|
@@ -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
|
-
/**
|
|
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,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 {};
|