@happyvertical/smrt-chat 0.55.9 → 0.55.10
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/AGENTS.md +25 -14
- package/README.md +12 -0
- package/agents/dev-workbench.md +69 -0
- package/agents/helper-preferences.md +65 -0
- package/agents/overview-tools.md +172 -0
- package/agents/token-streaming.md +3 -2
- package/dist/chunks/{ChatService-C9UxuJaw.js → ChatService-CFaaDHyU.js} +2 -2
- package/dist/chunks/{ChatService-C9UxuJaw.js.map → ChatService-CFaaDHyU.js.map} +1 -1
- package/dist/chunks/chat-stream-DN6Tw-Xu.js +2027 -0
- package/dist/chunks/chat-stream-DN6Tw-Xu.js.map +1 -0
- package/dist/dev-assistant-transport.d.ts +5 -0
- package/dist/dev-assistant-transport.d.ts.map +1 -0
- package/dist/dev-character-persistence-client.d.ts +24 -0
- package/dist/dev-character-persistence-client.d.ts.map +1 -0
- package/dist/dev-helper-client.d.ts +4 -0
- package/dist/dev-helper-client.d.ts.map +1 -0
- package/dist/dev-helper-server.d.ts +18 -0
- package/dist/dev-helper-server.d.ts.map +1 -0
- package/dist/helper-preferences-service.d.ts +22 -0
- package/dist/helper-preferences-service.d.ts.map +1 -0
- package/dist/helper-preferences.d.ts +84 -0
- package/dist/helper-preferences.d.ts.map +1 -0
- package/dist/helper-preferences.js +32 -0
- package/dist/helper-preferences.js.map +1 -0
- package/dist/helper-profile-store.d.ts +27 -0
- package/dist/helper-profile-store.d.ts.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +683 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/agent-runtime.js +1 -1
- package/dist/manifest.json +122 -3
- package/dist/overview-tools.d.ts +221 -0
- package/dist/overview-tools.d.ts.map +1 -0
- package/dist/playground.d.ts +1 -1
- package/dist/playground.d.ts.map +1 -1
- package/dist/playground.js +99 -88
- package/dist/playground.js.map +1 -1
- package/dist/recipes.d.ts +81 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/routes/api/dev-character-conversation/protocol.d.ts +33 -0
- package/dist/routes/api/dev-character-conversation/protocol.d.ts.map +1 -0
- package/dist/routes/api/dev-character-persistence/config.d.ts +24 -0
- package/dist/routes/api/dev-character-persistence/config.d.ts.map +1 -0
- package/dist/scripts/provision-dev-character-persistence.d.ts +3 -0
- package/dist/scripts/provision-dev-character-persistence.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +144 -11
- package/dist/svelte/components/assistant/AssistantDock.svelte +58 -2
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts +9 -0
- package/dist/svelte/components/assistant/AssistantDock.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/FloatingAssistant.svelte +309 -0
- package/dist/svelte/components/assistant/FloatingAssistant.svelte.d.ts +27 -0
- package/dist/svelte/components/assistant/FloatingAssistant.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte +105 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte.d.ts +11 -0
- package/dist/svelte/components/assistant/captions/CaptionOverlay.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte +65 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte.d.ts +19 -0
- package/dist/svelte/components/assistant/captions/HeardCaptions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte +65 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte.d.ts +21 -0
- package/dist/svelte/components/assistant/captions/SpokenCaptions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.d.ts +3 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-overlay-context.js +2 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.d.ts +71 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.d.ts.map +1 -0
- package/dist/svelte/components/assistant/captions/caption-state.svelte.js +225 -0
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.d.ts.map +1 -1
- package/dist/svelte/components/assistant/create-assistant-dock-controller.svelte.js +3 -1
- package/dist/svelte/components/helper/HelperControlPanel.svelte +401 -0
- package/dist/svelte/components/helper/HelperControlPanel.svelte.d.ts +23 -0
- package/dist/svelte/components/helper/HelperControlPanel.svelte.d.ts.map +1 -0
- package/dist/svelte/components/helper/registry.d.ts +53 -0
- package/dist/svelte/components/helper/registry.d.ts.map +1 -0
- package/dist/svelte/components/helper/registry.js +100 -0
- package/dist/svelte/i18n.d.ts +14 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +17 -0
- package/dist/svelte/i18n.messages.d.ts +32 -0
- package/dist/svelte/i18n.messages.d.ts.map +1 -1
- package/dist/svelte/i18n.messages.js +33 -0
- package/dist/svelte/index.d.ts +7 -0
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +15 -5
- package/dist/svelte/playground.d.ts +94 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +16 -5
- package/dist/sveltekit.js +2 -2
- package/package.json +27 -17
- package/dist/chunks/chat-stream-BBk_5_Ph.js +0 -2027
- package/dist/chunks/chat-stream-BBk_5_Ph.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantDock.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/assistant/AssistantDock.svelte.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"AssistantDock.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/assistant/AssistantDock.svelte.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* A permanently mounted AssistantDock with a compact launcher.
|
|
4
|
+
*
|
|
5
|
+
* The wrapper intentionally owns only presentation. AssistantDock remains
|
|
6
|
+
* the one controller, conversation, and confirmation authority. A character
|
|
7
|
+
* is an optional Svelte snippet so a host can use a still image, CSS art, or
|
|
8
|
+
* an animation package without making any of them a chat dependency.
|
|
9
|
+
*/
|
|
10
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
11
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
12
|
+
import { type Snippet, tick } from 'svelte';
|
|
13
|
+
import type { Attachment } from 'svelte/attachments';
|
|
14
|
+
import type { AssistantStatus } from '../../../assistant-turn-events.js';
|
|
15
|
+
import { M } from '../../i18n.js';
|
|
16
|
+
import AssistantDock, {
|
|
17
|
+
type Props as AssistantDockProps,
|
|
18
|
+
} from './AssistantDock.svelte';
|
|
19
|
+
import type {
|
|
20
|
+
AssistantDockController,
|
|
21
|
+
AssistantRun,
|
|
22
|
+
} from './create-assistant-dock-controller.svelte.js';
|
|
23
|
+
|
|
24
|
+
const { t } = useI18n();
|
|
25
|
+
|
|
26
|
+
export interface FloatingAssistantPresentationState {
|
|
27
|
+
expanded: boolean;
|
|
28
|
+
status: AssistantStatus;
|
|
29
|
+
run: AssistantRun | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Props extends AssistantDockProps {
|
|
33
|
+
/** Optional host-owned visual; it receives no media or animation contract. */
|
|
34
|
+
character?: Snippet<[FloatingAssistantPresentationState]>;
|
|
35
|
+
/** Accessible launcher text. */
|
|
36
|
+
launcherLabel?: string;
|
|
37
|
+
/** Accessible panel title. */
|
|
38
|
+
panelLabel?: string;
|
|
39
|
+
/** Starts expanded; the bindable value can also be controlled by the host. */
|
|
40
|
+
expanded?: boolean;
|
|
41
|
+
/** Fixed viewport corner used by the launcher and its conversation panel. */
|
|
42
|
+
placement?: 'bottom-left' | 'bottom-right';
|
|
43
|
+
/** Hides empty controls-only chrome while preserving active runs and decisions. */
|
|
44
|
+
hideIdleControls?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
character,
|
|
49
|
+
launcherLabel = 'Open assistant',
|
|
50
|
+
panelLabel = 'Assistant',
|
|
51
|
+
expanded = $bindable(false),
|
|
52
|
+
placement = 'bottom-right',
|
|
53
|
+
hideIdleControls = false,
|
|
54
|
+
visible = true,
|
|
55
|
+
presentation = 'full',
|
|
56
|
+
onstatus,
|
|
57
|
+
onrun,
|
|
58
|
+
onattentionchange,
|
|
59
|
+
oncontroller,
|
|
60
|
+
...dockProps
|
|
61
|
+
}: Props = $props();
|
|
62
|
+
|
|
63
|
+
let status = $state<AssistantStatus>({ state: 'idle', label: null });
|
|
64
|
+
let run = $state<AssistantRun | null>(null);
|
|
65
|
+
let attentionRequired = $state(false);
|
|
66
|
+
let dockController = $state.raw<AssistantDockController | null>(null);
|
|
67
|
+
let launcher: HTMLButtonElement | undefined = $state();
|
|
68
|
+
let panel: HTMLElement | undefined = $state();
|
|
69
|
+
|
|
70
|
+
// Button forwards Svelte attachments to its native button; retain that DOM
|
|
71
|
+
// reference for collapse focus without introducing a second control.
|
|
72
|
+
const captureLauncher: Attachment<HTMLButtonElement> = (element) => {
|
|
73
|
+
launcher = element;
|
|
74
|
+
return () => {
|
|
75
|
+
launcher = undefined;
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const floatingAssistantId = $props.id();
|
|
80
|
+
const panelId = `floating-assistant-${floatingAssistantId}`;
|
|
81
|
+
const hasActionableRun = $derived(
|
|
82
|
+
run?.state === 'running' ||
|
|
83
|
+
run?.state === 'paused' ||
|
|
84
|
+
run?.state === 'waiting',
|
|
85
|
+
);
|
|
86
|
+
const hasActionableStatus = $derived(
|
|
87
|
+
status.state === 'working' || status.state === 'error',
|
|
88
|
+
);
|
|
89
|
+
const hasControllerError = $derived(!!dockController?.error);
|
|
90
|
+
const panelExpanded = $derived(
|
|
91
|
+
visible &&
|
|
92
|
+
(expanded || attentionRequired) &&
|
|
93
|
+
(!hideIdleControls ||
|
|
94
|
+
presentation === 'full' ||
|
|
95
|
+
attentionRequired ||
|
|
96
|
+
hasActionableRun ||
|
|
97
|
+
hasActionableStatus ||
|
|
98
|
+
hasControllerError),
|
|
99
|
+
);
|
|
100
|
+
const launcherVisible = $derived(
|
|
101
|
+
!hideIdleControls ||
|
|
102
|
+
presentation === 'full' ||
|
|
103
|
+
attentionRequired ||
|
|
104
|
+
hasActionableRun ||
|
|
105
|
+
hasActionableStatus ||
|
|
106
|
+
hasControllerError,
|
|
107
|
+
);
|
|
108
|
+
const presentationState = $derived({ expanded: panelExpanded, status, run });
|
|
109
|
+
|
|
110
|
+
function open() {
|
|
111
|
+
expanded = true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function collapse() {
|
|
115
|
+
if (!expanded || attentionRequired) return;
|
|
116
|
+
const focusWasInPanel =
|
|
117
|
+
typeof document !== 'undefined' &&
|
|
118
|
+
!!panel?.contains(document.activeElement);
|
|
119
|
+
expanded = false;
|
|
120
|
+
if (focusWasInPanel) void tick().then(() => launcher?.focus());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function handleStatus(next: AssistantStatus) {
|
|
124
|
+
status = next;
|
|
125
|
+
onstatus?.(next);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function handleRun(next: AssistantRun | null) {
|
|
129
|
+
run = next;
|
|
130
|
+
// A browser-tool confirmation is authoritative only in the dock. Reveal
|
|
131
|
+
// that existing UI rather than creating another approval path here.
|
|
132
|
+
if (next?.waitingFor?.kind === 'confirm') open();
|
|
133
|
+
onrun?.(next);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleAttentionChange(required: boolean) {
|
|
137
|
+
// Previewed data actions and pending browser-tool calls must remain
|
|
138
|
+
// reachable. This changes visibility only; it never confirms an action.
|
|
139
|
+
attentionRequired = required;
|
|
140
|
+
if (required) open();
|
|
141
|
+
onattentionchange?.(required);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function handleController(next: AssistantDockController) {
|
|
145
|
+
dockController = next;
|
|
146
|
+
oncontroller?.(next);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
150
|
+
if (event.key !== 'Escape' || !visible || !expanded) return;
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
collapse();
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<svelte:window onkeydown={handleKeydown} />
|
|
157
|
+
|
|
158
|
+
<div
|
|
159
|
+
class="floating-assistant"
|
|
160
|
+
class:bottom-left={placement === 'bottom-left'}
|
|
161
|
+
data-expanded={panelExpanded || undefined}
|
|
162
|
+
hidden={!visible}
|
|
163
|
+
inert={!visible}
|
|
164
|
+
aria-hidden={!visible}
|
|
165
|
+
>
|
|
166
|
+
{#if launcherVisible}
|
|
167
|
+
<Button
|
|
168
|
+
{@attach captureLauncher}
|
|
169
|
+
variant="ghost"
|
|
170
|
+
type="button"
|
|
171
|
+
class="floating-assistant-launcher"
|
|
172
|
+
aria-expanded={panelExpanded}
|
|
173
|
+
aria-controls={panelId}
|
|
174
|
+
onclick={open}
|
|
175
|
+
>
|
|
176
|
+
{#if character}
|
|
177
|
+
<span class="floating-assistant-character" aria-hidden="true">
|
|
178
|
+
{@render character(presentationState)}
|
|
179
|
+
</span>
|
|
180
|
+
{/if}
|
|
181
|
+
<span>{launcherLabel}</span>
|
|
182
|
+
</Button>
|
|
183
|
+
{/if}
|
|
184
|
+
|
|
185
|
+
<section
|
|
186
|
+
bind:this={panel}
|
|
187
|
+
id={panelId}
|
|
188
|
+
class="floating-assistant-panel"
|
|
189
|
+
aria-label={panelLabel}
|
|
190
|
+
aria-hidden={!panelExpanded}
|
|
191
|
+
inert={!panelExpanded}
|
|
192
|
+
>
|
|
193
|
+
<Button
|
|
194
|
+
variant="ghost"
|
|
195
|
+
type="button"
|
|
196
|
+
class="floating-assistant-collapse"
|
|
197
|
+
aria-label={t(M['chat.floating_assistant.collapse'])}
|
|
198
|
+
onclick={collapse}
|
|
199
|
+
>
|
|
200
|
+
{t(M['chat.floating_assistant.close'])}
|
|
201
|
+
</Button>
|
|
202
|
+
<AssistantDock
|
|
203
|
+
{...dockProps}
|
|
204
|
+
{presentation}
|
|
205
|
+
visible={panelExpanded}
|
|
206
|
+
oncontroller={handleController}
|
|
207
|
+
onstatus={handleStatus}
|
|
208
|
+
onrun={handleRun}
|
|
209
|
+
onattentionchange={handleAttentionChange}
|
|
210
|
+
/>
|
|
211
|
+
</section>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
<style>
|
|
215
|
+
.floating-assistant {
|
|
216
|
+
position: fixed;
|
|
217
|
+
z-index: var(--smrt-z-index-overlay, 1200);
|
|
218
|
+
right: max(1rem, env(safe-area-inset-right));
|
|
219
|
+
bottom: max(1rem, env(safe-area-inset-bottom));
|
|
220
|
+
display: grid;
|
|
221
|
+
justify-items: end;
|
|
222
|
+
gap: 0.75rem;
|
|
223
|
+
}
|
|
224
|
+
.floating-assistant.bottom-left { right: auto; left: max(1rem, env(safe-area-inset-left)); justify-items: start; }
|
|
225
|
+
|
|
226
|
+
.floating-assistant[hidden] {
|
|
227
|
+
display: none;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.floating-assistant :global(.floating-assistant-launcher),
|
|
231
|
+
.floating-assistant :global(.floating-assistant-collapse) {
|
|
232
|
+
border: 1px solid var(--smrt-color-outline, currentColor);
|
|
233
|
+
border-radius: var(--smrt-radius-full, 9999px);
|
|
234
|
+
background: var(--smrt-color-surface, Canvas);
|
|
235
|
+
color: var(--smrt-color-on-surface, CanvasText);
|
|
236
|
+
font: inherit;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.floating-assistant :global(.floating-assistant-launcher) {
|
|
240
|
+
display: inline-flex;
|
|
241
|
+
align-items: center;
|
|
242
|
+
gap: 0.5rem;
|
|
243
|
+
min-block-size: 2.75rem;
|
|
244
|
+
padding: 0.5rem 0.875rem;
|
|
245
|
+
box-shadow: var(
|
|
246
|
+
--smrt-elevation-4,
|
|
247
|
+
0 0.5rem 1.5rem color-mix(in srgb, CanvasText 18%, transparent)
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.floating-assistant-character {
|
|
252
|
+
display: grid;
|
|
253
|
+
place-items: center;
|
|
254
|
+
inline-size: 1.75rem;
|
|
255
|
+
block-size: 1.75rem;
|
|
256
|
+
overflow: hidden;
|
|
257
|
+
border-radius: var(--smrt-radius-full, 9999px);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.floating-assistant-panel {
|
|
261
|
+
box-sizing: border-box;
|
|
262
|
+
display: block;
|
|
263
|
+
inline-size: min(28rem, calc(100vw - 2rem));
|
|
264
|
+
max-block-size: min(42rem, calc(100dvh - 6rem));
|
|
265
|
+
overflow: auto;
|
|
266
|
+
padding: 0.75rem;
|
|
267
|
+
border: 1px solid var(--smrt-color-outline, currentColor);
|
|
268
|
+
border-radius: 1rem;
|
|
269
|
+
background: var(--smrt-color-surface, Canvas);
|
|
270
|
+
color: var(--smrt-color-on-surface, CanvasText);
|
|
271
|
+
box-shadow: var(
|
|
272
|
+
--smrt-elevation-5,
|
|
273
|
+
0 1rem 2rem color-mix(in srgb, CanvasText 24%, transparent)
|
|
274
|
+
);
|
|
275
|
+
transition: opacity 160ms ease, transform 160ms ease, visibility 160ms ease;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.floating-assistant:not([data-expanded]) .floating-assistant-panel {
|
|
279
|
+
visibility: hidden;
|
|
280
|
+
pointer-events: none;
|
|
281
|
+
opacity: 0;
|
|
282
|
+
transform: translateY(0.5rem);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.floating-assistant :global(.floating-assistant-collapse) {
|
|
286
|
+
float: inline-end;
|
|
287
|
+
min-block-size: 2rem;
|
|
288
|
+
padding-inline: 0.625rem;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
@media (max-width: 30rem) {
|
|
292
|
+
.floating-assistant {
|
|
293
|
+
right: max(0.5rem, env(safe-area-inset-right));
|
|
294
|
+
bottom: max(0.5rem, env(safe-area-inset-bottom));
|
|
295
|
+
}
|
|
296
|
+
.floating-assistant.bottom-left { right: auto; left: max(0.5rem, env(safe-area-inset-left)); }
|
|
297
|
+
|
|
298
|
+
.floating-assistant-panel {
|
|
299
|
+
inline-size: calc(100vw - 1rem);
|
|
300
|
+
max-block-size: calc(100dvh - 4.5rem);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
@media (prefers-reduced-motion: reduce) {
|
|
305
|
+
.floating-assistant-panel {
|
|
306
|
+
transition: none;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
</style>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { AssistantStatus } from '../../../assistant-turn-events.js';
|
|
3
|
+
import { type Props as AssistantDockProps } from './AssistantDock.svelte';
|
|
4
|
+
import type { AssistantRun } from './create-assistant-dock-controller.svelte.js';
|
|
5
|
+
export interface FloatingAssistantPresentationState {
|
|
6
|
+
expanded: boolean;
|
|
7
|
+
status: AssistantStatus;
|
|
8
|
+
run: AssistantRun | null;
|
|
9
|
+
}
|
|
10
|
+
export interface Props extends AssistantDockProps {
|
|
11
|
+
/** Optional host-owned visual; it receives no media or animation contract. */
|
|
12
|
+
character?: Snippet<[FloatingAssistantPresentationState]>;
|
|
13
|
+
/** Accessible launcher text. */
|
|
14
|
+
launcherLabel?: string;
|
|
15
|
+
/** Accessible panel title. */
|
|
16
|
+
panelLabel?: string;
|
|
17
|
+
/** Starts expanded; the bindable value can also be controlled by the host. */
|
|
18
|
+
expanded?: boolean;
|
|
19
|
+
/** Fixed viewport corner used by the launcher and its conversation panel. */
|
|
20
|
+
placement?: 'bottom-left' | 'bottom-right';
|
|
21
|
+
/** Hides empty controls-only chrome while preserving active runs and decisions. */
|
|
22
|
+
hideIdleControls?: boolean;
|
|
23
|
+
}
|
|
24
|
+
declare const FloatingAssistant: import("svelte").Component<Props, {}, "expanded">;
|
|
25
|
+
type FloatingAssistant = ReturnType<typeof FloatingAssistant>;
|
|
26
|
+
export default FloatingAssistant;
|
|
27
|
+
//# sourceMappingURL=FloatingAssistant.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatingAssistant.svelte.d.ts","sourceRoot":"","sources":["../../../../src/svelte/components/assistant/FloatingAssistant.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,KAAK,OAAO,EAAQ,MAAM,QAAQ,CAAC;AAE5C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAsB,EACpB,KAAK,KAAK,IAAI,kBAAkB,EACjC,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAEV,YAAY,EACb,MAAM,8CAA8C,CAAC;AAGtD,MAAM,WAAW,kCAAkC;IACjD,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,eAAe,CAAC;IACxB,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,KAAM,SAAQ,kBAAkB;IAC/C,8EAA8E;IAC9E,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC,kCAAkC,CAAC,CAAC,CAAC;IAC1D,gCAAgC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAAC;IAC3C,mFAAmF;IACnF,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAuJD,QAAA,MAAM,iBAAiB,mDAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
4
|
+
import { type Snippet, setContext } from 'svelte';
|
|
5
|
+
import { M } from '../../../i18n.js';
|
|
6
|
+
import { CAPTION_OVERLAY } from './caption-overlay-context.js';
|
|
7
|
+
|
|
8
|
+
export interface Props {
|
|
9
|
+
/** Caption components sharing this viewport-bottom group, in reading order. */
|
|
10
|
+
children: Snippet;
|
|
11
|
+
/** Localized accessible name for the caption group. */
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { children, label = 'Captions' }: Props = $props();
|
|
16
|
+
const { t } = useI18n();
|
|
17
|
+
setContext(CAPTION_OVERLAY, true);
|
|
18
|
+
let group: HTMLElement;
|
|
19
|
+
let viewport: HTMLDivElement;
|
|
20
|
+
let content: HTMLDivElement;
|
|
21
|
+
let overflowing = $state(false);
|
|
22
|
+
let atStart = $state(true);
|
|
23
|
+
let atEnd = $state(false);
|
|
24
|
+
|
|
25
|
+
function updatePosition() {
|
|
26
|
+
atStart = viewport.scrollTop <= 0;
|
|
27
|
+
atEnd =
|
|
28
|
+
Math.ceil(viewport.scrollTop + viewport.clientHeight) >=
|
|
29
|
+
viewport.scrollHeight;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function scroll(direction: number) {
|
|
33
|
+
viewport.scrollBy({
|
|
34
|
+
top: direction * viewport.clientHeight,
|
|
35
|
+
behavior: 'instant',
|
|
36
|
+
});
|
|
37
|
+
updatePosition();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
$effect(() => {
|
|
41
|
+
// Observe only this owned group. Compare against the group's entire budget,
|
|
42
|
+
// not the viewport reduced by the buttons, so controls disappear when text fits.
|
|
43
|
+
const observer = new ResizeObserver(() => {
|
|
44
|
+
overflowing = content.scrollHeight > group.clientHeight;
|
|
45
|
+
updatePosition();
|
|
46
|
+
});
|
|
47
|
+
observer.observe(group);
|
|
48
|
+
observer.observe(viewport);
|
|
49
|
+
observer.observe(content);
|
|
50
|
+
return () => observer.disconnect();
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<section bind:this={group} class="caption-overlay" aria-label={label}>
|
|
55
|
+
<div bind:this={viewport} class="caption-overlay-viewport" onscroll={updatePosition}>
|
|
56
|
+
<div bind:this={content} class="caption-overlay-content">
|
|
57
|
+
{@render children()}
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
{#if overflowing}
|
|
61
|
+
<div class="caption-overlay-controls">
|
|
62
|
+
<Button size="sm" disabled={atStart} onclick={() => scroll(-1)}>{t(M['chat.caption_overlay.scroll_up'])}</Button>
|
|
63
|
+
<Button size="sm" disabled={atEnd} onclick={() => scroll(1)}>{t(M['chat.caption_overlay.scroll_down'])}</Button>
|
|
64
|
+
</div>
|
|
65
|
+
{/if}
|
|
66
|
+
</section>
|
|
67
|
+
|
|
68
|
+
<style>
|
|
69
|
+
.caption-overlay {
|
|
70
|
+
position: fixed;
|
|
71
|
+
z-index: var(--smrt-z-index-sticky, 1100);
|
|
72
|
+
inset-inline-start: max(var(--smrt-spacing-4, 1rem), env(safe-area-inset-left));
|
|
73
|
+
inset-inline-end: max(var(--smrt-spacing-4, 1rem), env(safe-area-inset-right));
|
|
74
|
+
inset-block-end: max(var(--smrt-spacing-4, 1rem), env(safe-area-inset-bottom));
|
|
75
|
+
box-sizing: border-box;
|
|
76
|
+
display: grid;
|
|
77
|
+
grid-template-rows: minmax(0, 1fr) auto;
|
|
78
|
+
max-inline-size: 42rem;
|
|
79
|
+
max-block-size: min(50dvh, calc(100dvh - env(safe-area-inset-top) - env(safe-area-inset-bottom) - 2 * var(--smrt-spacing-4, 1rem)));
|
|
80
|
+
margin-inline: auto;
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.caption-overlay:not(:has(.caption-overlay-content > *)) { display: none; }
|
|
85
|
+
|
|
86
|
+
.caption-overlay-viewport {
|
|
87
|
+
min-block-size: 0;
|
|
88
|
+
overflow: auto;
|
|
89
|
+
overscroll-behavior: contain;
|
|
90
|
+
scroll-behavior: auto;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.caption-overlay-content {
|
|
94
|
+
display: grid;
|
|
95
|
+
gap: var(--smrt-spacing-3, .75rem);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.caption-overlay-controls {
|
|
99
|
+
display: flex;
|
|
100
|
+
justify-content: space-between;
|
|
101
|
+
gap: var(--smrt-spacing-3, .75rem);
|
|
102
|
+
padding-block-start: var(--smrt-spacing-2, .5rem);
|
|
103
|
+
background: var(--smrt-color-surface, Canvas);
|
|
104
|
+
}
|
|
105
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Caption components sharing this viewport-bottom group, in reading order. */
|
|
4
|
+
children: Snippet;
|
|
5
|
+
/** Localized accessible name for the caption group. */
|
|
6
|
+
label?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const CaptionOverlay: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type CaptionOverlay = ReturnType<typeof CaptionOverlay>;
|
|
10
|
+
export default CaptionOverlay;
|
|
11
|
+
//# sourceMappingURL=CaptionOverlay.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CaptionOverlay.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/svelte/components/assistant/captions/CaptionOverlay.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAc,MAAM,QAAQ,CAAC;AAKlD,MAAM,WAAW,KAAK;IACpB,+EAA+E;IAC/E,QAAQ,EAAE,OAAO,CAAC;IAClB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAkED,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import CaptionOverlay from './CaptionOverlay.svelte';
|
|
4
|
+
import { CAPTION_OVERLAY } from './caption-overlay-context.js';
|
|
5
|
+
import type { CaptionLine } from './caption-state.svelte.js';
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
/** Shows captions when the host enables this speaker surface. */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/** Completed caption lines in chronological order. */
|
|
11
|
+
lines?: CaptionLine[];
|
|
12
|
+
/** Current unfinished transcript displayed without live announcement. */
|
|
13
|
+
interim?: string;
|
|
14
|
+
/** `bottom` overlays an app; `inline` belongs in a panel. */
|
|
15
|
+
placement?: 'bottom' | 'inline';
|
|
16
|
+
/** Maximum number of completed lines retained on screen. */
|
|
17
|
+
maxLines?: number;
|
|
18
|
+
/** Localized visible and accessible label identifying the speaker. */
|
|
19
|
+
speakerLabel?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
enabled = false,
|
|
24
|
+
lines = [],
|
|
25
|
+
interim = '',
|
|
26
|
+
placement = 'bottom',
|
|
27
|
+
maxLines = 3,
|
|
28
|
+
speakerLabel = 'You said',
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const inOverlay = getContext<boolean>(CAPTION_OVERLAY) ?? false;
|
|
32
|
+
const visibleLines = $derived(lines.slice(-Math.max(1, maxLines)));
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
{#snippet surface()}
|
|
36
|
+
<section class="heard-captions" aria-label={speakerLabel}>
|
|
37
|
+
<p class="speaker">{speakerLabel}</p>
|
|
38
|
+
<div class="caption-lines" aria-live="polite" aria-atomic="false">
|
|
39
|
+
{#each visibleLines as line (line.id)}
|
|
40
|
+
<p>{line.text}</p>
|
|
41
|
+
{/each}
|
|
42
|
+
</div>
|
|
43
|
+
{#if interim}
|
|
44
|
+
<!-- A partial transcript is visually useful but should not repeat the
|
|
45
|
+
final phrase in a screen reader's live region. -->
|
|
46
|
+
<p class="interim" aria-hidden="true">{interim}</p>
|
|
47
|
+
{/if}
|
|
48
|
+
</section>
|
|
49
|
+
{/snippet}
|
|
50
|
+
|
|
51
|
+
{#if enabled && (visibleLines.length > 0 || interim)}
|
|
52
|
+
{#if placement === 'bottom' && !inOverlay}
|
|
53
|
+
<CaptionOverlay>{@render surface()}</CaptionOverlay>
|
|
54
|
+
{:else}
|
|
55
|
+
{@render surface()}
|
|
56
|
+
{/if}
|
|
57
|
+
{/if}
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.heard-captions { box-sizing: border-box; min-inline-size: 0; max-inline-size: min(42rem, calc(100vw - 2rem)); overflow-wrap: anywhere; padding: .6rem .85rem; border-inline-start: .25rem solid var(--smrt-color-primary, #3154a5); background: color-mix(in srgb, var(--smrt-color-surface, white) 92%, var(--smrt-color-primary-container, #dce6ff)); color: var(--smrt-color-on-surface, #172033); border-radius: .35rem; box-shadow: var(--smrt-elevation-4, 0 .25rem .9rem color-mix(in srgb, var(--smrt-color-shadow, black) 16%, transparent)); }
|
|
61
|
+
.speaker { margin: 0 0 .25rem; font-weight: var(--smrt-typography-weight-bold, 700); letter-spacing: .03em; text-transform: uppercase; font-size: var(--smrt-typography-label-medium-size, .75rem); }
|
|
62
|
+
.caption-lines p, .interim { margin: .15rem 0 0; }
|
|
63
|
+
.interim { font-style: italic; opacity: .8; }
|
|
64
|
+
@media (prefers-reduced-motion: reduce) { .heard-captions { scroll-behavior: auto; } }
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { CaptionLine } from './caption-state.svelte.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Shows captions when the host enables this speaker surface. */
|
|
4
|
+
enabled?: boolean;
|
|
5
|
+
/** Completed caption lines in chronological order. */
|
|
6
|
+
lines?: CaptionLine[];
|
|
7
|
+
/** Current unfinished transcript displayed without live announcement. */
|
|
8
|
+
interim?: string;
|
|
9
|
+
/** `bottom` overlays an app; `inline` belongs in a panel. */
|
|
10
|
+
placement?: 'bottom' | 'inline';
|
|
11
|
+
/** Maximum number of completed lines retained on screen. */
|
|
12
|
+
maxLines?: number;
|
|
13
|
+
/** Localized visible and accessible label identifying the speaker. */
|
|
14
|
+
speakerLabel?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const HeardCaptions: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type HeardCaptions = ReturnType<typeof HeardCaptions>;
|
|
18
|
+
export default HeardCaptions;
|
|
19
|
+
//# sourceMappingURL=HeardCaptions.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HeardCaptions.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/svelte/components/assistant/captions/HeardCaptions.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,MAAM,WAAW,KAAK;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAgDD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext } from 'svelte';
|
|
3
|
+
import CaptionOverlay from './CaptionOverlay.svelte';
|
|
4
|
+
import { CAPTION_OVERLAY } from './caption-overlay-context.js';
|
|
5
|
+
import type { CaptionLine } from './caption-state.svelte.js';
|
|
6
|
+
|
|
7
|
+
export interface Props {
|
|
8
|
+
/** Shows captions when the host enables this speaker surface. */
|
|
9
|
+
enabled?: boolean;
|
|
10
|
+
/** Completed caption lines in chronological order. */
|
|
11
|
+
lines?: CaptionLine[];
|
|
12
|
+
/** Current unfinished transcript displayed without live announcement. */
|
|
13
|
+
interim?: string;
|
|
14
|
+
/** `bottom` overlays an app; `inline` belongs in a panel. */
|
|
15
|
+
placement?: 'bottom' | 'inline';
|
|
16
|
+
/** Maximum number of completed lines retained on screen. */
|
|
17
|
+
maxLines?: number;
|
|
18
|
+
/** Localized visible and accessible label identifying the speaker. */
|
|
19
|
+
speakerLabel?: string;
|
|
20
|
+
/** Spoken audio already conveys this text. Opt in only when needed. */
|
|
21
|
+
announce?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let {
|
|
25
|
+
enabled = false,
|
|
26
|
+
lines = [],
|
|
27
|
+
interim = '',
|
|
28
|
+
placement = 'bottom',
|
|
29
|
+
maxLines = 3,
|
|
30
|
+
speakerLabel = 'Assistant is speaking',
|
|
31
|
+
announce = false,
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
const inOverlay = getContext<boolean>(CAPTION_OVERLAY) ?? false;
|
|
34
|
+
const visibleLines = $derived(lines.slice(-Math.max(1, maxLines)));
|
|
35
|
+
</script>
|
|
36
|
+
|
|
37
|
+
{#snippet surface()}
|
|
38
|
+
<section class="spoken-captions" aria-label={speakerLabel}>
|
|
39
|
+
<p class="speaker"><span aria-hidden="true">◈</span> {speakerLabel}</p>
|
|
40
|
+
<div class="caption-lines" aria-live={announce ? 'polite' : 'off'} aria-atomic="false">
|
|
41
|
+
{#each visibleLines as line (line.id)}
|
|
42
|
+
<p>{line.text}</p>
|
|
43
|
+
{/each}
|
|
44
|
+
</div>
|
|
45
|
+
{#if interim}
|
|
46
|
+
<p class="interim" aria-hidden="true">{interim}</p>
|
|
47
|
+
{/if}
|
|
48
|
+
</section>
|
|
49
|
+
{/snippet}
|
|
50
|
+
|
|
51
|
+
{#if enabled && (visibleLines.length > 0 || interim)}
|
|
52
|
+
{#if placement === 'bottom' && !inOverlay}
|
|
53
|
+
<CaptionOverlay>{@render surface()}</CaptionOverlay>
|
|
54
|
+
{:else}
|
|
55
|
+
{@render surface()}
|
|
56
|
+
{/if}
|
|
57
|
+
{/if}
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.spoken-captions { box-sizing: border-box; min-inline-size: 0; max-inline-size: min(42rem, calc(100vw - 2rem)); overflow-wrap: anywhere; padding: .7rem .9rem; border: 2px solid var(--smrt-color-outline, #533b72); background: var(--smrt-color-inverse-surface, #221c2c); color: var(--smrt-color-inverse-on-surface, #fff7ff); border-radius: 1rem; box-shadow: var(--smrt-elevation-4, 0 .35rem 1.2rem color-mix(in srgb, var(--smrt-color-shadow, black) 28%, transparent)); }
|
|
61
|
+
.speaker { margin: 0 0 .3rem; font-weight: var(--smrt-typography-weight-bold, 700); font-size: var(--smrt-typography-label-medium-size, .8rem); }
|
|
62
|
+
.caption-lines p, .interim { margin: .18rem 0 0; }
|
|
63
|
+
.interim { opacity: .8; }
|
|
64
|
+
@media (prefers-reduced-motion: reduce) { .spoken-captions { scroll-behavior: auto; } }
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CaptionLine } from './caption-state.svelte.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
/** Shows captions when the host enables this speaker surface. */
|
|
4
|
+
enabled?: boolean;
|
|
5
|
+
/** Completed caption lines in chronological order. */
|
|
6
|
+
lines?: CaptionLine[];
|
|
7
|
+
/** Current unfinished transcript displayed without live announcement. */
|
|
8
|
+
interim?: string;
|
|
9
|
+
/** `bottom` overlays an app; `inline` belongs in a panel. */
|
|
10
|
+
placement?: 'bottom' | 'inline';
|
|
11
|
+
/** Maximum number of completed lines retained on screen. */
|
|
12
|
+
maxLines?: number;
|
|
13
|
+
/** Localized visible and accessible label identifying the speaker. */
|
|
14
|
+
speakerLabel?: string;
|
|
15
|
+
/** Spoken audio already conveys this text. Opt in only when needed. */
|
|
16
|
+
announce?: boolean;
|
|
17
|
+
}
|
|
18
|
+
declare const SpokenCaptions: import("svelte").Component<Props, {}, "">;
|
|
19
|
+
type SpokenCaptions = ReturnType<typeof SpokenCaptions>;
|
|
20
|
+
export default SpokenCaptions;
|
|
21
|
+
//# sourceMappingURL=SpokenCaptions.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpokenCaptions.svelte.d.ts","sourceRoot":"","sources":["../../../../../src/svelte/components/assistant/captions/SpokenCaptions.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAG7D,MAAM,WAAW,KAAK;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,yEAAyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,SAAS,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAChC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA+CD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caption-overlay-context.d.ts","sourceRoot":"","sources":["../../../../../src/svelte/components/assistant/captions/caption-overlay-context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,eAAe,eAA4B,CAAC"}
|