@poodle64/librarian 2026.10.4 → 2026.10.5
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/README.md +7 -5
- package/dist/components/activity-group/activity-group.svelte +99 -17
- package/dist/components/activity-group/activity-group.svelte.d.ts +4 -1
- package/dist/components/agent-transcript/agent-transcript.svelte +82 -26
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +4 -3
- package/dist/components/artefact-pane/artefact-pane.svelte +278 -78
- package/dist/components/conversation/conversation.svelte +6 -6
- package/dist/components/conversation/conversation.svelte.d.ts +4 -4
- package/dist/components/markdown/markdown.d.ts +27 -10
- package/dist/components/markdown/markdown.js +99 -22
- package/dist/components/markdown/markdown.svelte +122 -28
- package/dist/components/markdown/markdown.svelte.d.ts +12 -4
- package/dist/components/source-fold/index.d.ts +2 -0
- package/dist/components/source-fold/index.js +2 -0
- package/dist/components/source-fold/source-fold.svelte +225 -0
- package/dist/components/source-fold/source-fold.svelte.d.ts +17 -0
- package/dist/components/source-list/source-list.svelte +5 -3
- package/dist/components/source-list/source-list.svelte.d.ts +2 -0
- package/dist/components/thinking-row/thinking-row.svelte +37 -16
- package/dist/components/thinking-row/thinking-row.svelte.d.ts +2 -0
- package/dist/components/tool-row/tool-row.svelte +130 -38
- package/dist/components/visual-block/entity-card.svelte +94 -0
- package/dist/components/visual-block/entity-card.svelte.d.ts +7 -0
- package/dist/components/visual-block/timeline.svelte +99 -0
- package/dist/components/visual-block/timeline.svelte.d.ts +7 -0
- package/dist/components/visual-block/vega-chart.svelte +110 -0
- package/dist/components/visual-block/vega-chart.svelte.d.ts +7 -0
- package/dist/components/visual-block/visual-block.svelte +53 -0
- package/dist/components/visual-block/visual-block.svelte.d.ts +13 -0
- package/dist/components/written-card/written-card.svelte +97 -49
- package/dist/components/written-card/written-card.svelte.d.ts +1 -1
- package/dist/copy.d.ts +54 -0
- package/dist/copy.js +44 -1
- package/dist/diff.d.ts +8 -0
- package/dist/diff.js +37 -0
- package/dist/steps.d.ts +28 -0
- package/dist/steps.js +168 -0
- package/dist/transcript.svelte.d.ts +4 -0
- package/dist/transcript.svelte.js +13 -2
- package/dist/visuals.d.ts +38 -0
- package/dist/visuals.js +82 -0
- package/dist/web-sources.d.ts +19 -3
- package/dist/web-sources.js +39 -7
- package/dist/written.d.ts +26 -1
- package/dist/written.js +42 -0
- package/package.json +10 -1
- package/dist/components/web-list/index.d.ts +0 -2
- package/dist/components/web-list/index.js +0 -2
- package/dist/components/web-list/web-list.svelte +0 -84
- package/dist/components/web-list/web-list.svelte.d.ts +0 -9
|
@@ -12,6 +12,10 @@
|
|
|
12
12
|
only ever one, in the same place, closing whichever was there.
|
|
13
13
|
-->
|
|
14
14
|
<script lang="ts">
|
|
15
|
+
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
16
|
+
import CopyIcon from '@lucide/svelte/icons/copy';
|
|
17
|
+
import CheckIcon from '@lucide/svelte/icons/check';
|
|
18
|
+
import DownloadIcon from '@lucide/svelte/icons/download';
|
|
15
19
|
import XIcon from '@lucide/svelte/icons/x';
|
|
16
20
|
import { untrack } from 'svelte';
|
|
17
21
|
import { segment, type TextBlock, type Turn } from '../../transcript.svelte';
|
|
@@ -21,6 +25,8 @@
|
|
|
21
25
|
downloads,
|
|
22
26
|
framed,
|
|
23
27
|
isHtml,
|
|
28
|
+
kindLabel,
|
|
29
|
+
stripFrontMatter,
|
|
24
30
|
writtenKey,
|
|
25
31
|
type LoadWritten,
|
|
26
32
|
type Written,
|
|
@@ -65,7 +71,11 @@
|
|
|
65
71
|
// The newest, until the reader picks another.
|
|
66
72
|
const current = $derived(versions.find((v) => v.version === version) ?? versions.at(-1));
|
|
67
73
|
const currentKey = $derived(current ? writtenKey(current) : '');
|
|
74
|
+
// The one before it, whose blocks this one is marked against.
|
|
75
|
+
const before = $derived(current ? versions.filter((v) => v.version < current.version).at(-1) : undefined);
|
|
76
|
+
const beforeKey = $derived(before ? writtenKey(before) : '');
|
|
68
77
|
let source = $state<{ text: string; mediaType: string } | null>(null);
|
|
78
|
+
let earlier = $state<string | null>(null);
|
|
69
79
|
let failed = $state(false);
|
|
70
80
|
|
|
71
81
|
// Keyed on the version alone: a new version of the same name arriving in
|
|
@@ -85,6 +95,21 @@
|
|
|
85
95
|
return () => (live = false);
|
|
86
96
|
});
|
|
87
97
|
|
|
98
|
+
$effect(() => {
|
|
99
|
+
earlier = null;
|
|
100
|
+
if (!beforeKey) return;
|
|
101
|
+
const prior = untrack(() => before);
|
|
102
|
+
const load = untrack(() => loadWritten);
|
|
103
|
+
if (!prior || !load || isHtml(prior.media_type)) return;
|
|
104
|
+
let live = true;
|
|
105
|
+
// A version that cannot be read is no reason to mark nothing else.
|
|
106
|
+
load(prior.name, prior.version).then(
|
|
107
|
+
(read) => live && (earlier = stripFrontMatter(read.text)),
|
|
108
|
+
() => {}
|
|
109
|
+
);
|
|
110
|
+
return () => (live = false);
|
|
111
|
+
});
|
|
112
|
+
|
|
88
113
|
const texts = $derived(
|
|
89
114
|
segment(turn?.blocks ?? []).filter((s): s is TextBlock => s.kind === 'text')
|
|
90
115
|
);
|
|
@@ -92,6 +117,37 @@
|
|
|
92
117
|
const split = $derived(splitSources(rawAnswer));
|
|
93
118
|
const sources = $derived(resolveCitations(turn?.citations ?? [], split.citations));
|
|
94
119
|
|
|
120
|
+
const links = $derived(current && writtenUrl ? downloads(current, words) : []);
|
|
121
|
+
// The one download the bar offers directly: Word where there is one, else the source.
|
|
122
|
+
const primary = $derived(links.find((link) => link.format === 'docx') ?? links[0]);
|
|
123
|
+
const sub = $derived.by(() => {
|
|
124
|
+
if (!current) return '';
|
|
125
|
+
const fresh = current.at !== undefined && Date.now() - current.at < 120_000;
|
|
126
|
+
const when = fresh
|
|
127
|
+
? fill(current.version > 1 ? words.paneEdited : words.paneWritten, { when: words.justNow })
|
|
128
|
+
: '';
|
|
129
|
+
return [kindLabel(current, words), when].filter(Boolean).join(' · ');
|
|
130
|
+
});
|
|
131
|
+
const changed = $derived(
|
|
132
|
+
current?.changes && before
|
|
133
|
+
? fill(words.changedSince, { n: before.version, changes: current.changes })
|
|
134
|
+
: ''
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
let menu = $state(false);
|
|
138
|
+
let copied = $state(false);
|
|
139
|
+
|
|
140
|
+
async function copyDocument() {
|
|
141
|
+
if (!source) return;
|
|
142
|
+
try {
|
|
143
|
+
await navigator.clipboard.writeText(stripFrontMatter(source.text));
|
|
144
|
+
copied = true;
|
|
145
|
+
setTimeout(() => (copied = false), 1600);
|
|
146
|
+
} catch {
|
|
147
|
+
// A denied clipboard is not worth an error state on text the reader can select.
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
95
151
|
let closeButton = $state<HTMLButtonElement | null>(null);
|
|
96
152
|
|
|
97
153
|
// Opening a pane a reader reached with the keyboard must move focus into
|
|
@@ -103,74 +159,113 @@
|
|
|
103
159
|
});
|
|
104
160
|
|
|
105
161
|
function keydown(event: KeyboardEvent) {
|
|
106
|
-
if (event.key
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
162
|
+
if (event.key !== 'Escape') return;
|
|
163
|
+
event.stopPropagation();
|
|
164
|
+
if (menu) menu = false;
|
|
165
|
+
else onclose();
|
|
110
166
|
}
|
|
111
167
|
</script>
|
|
112
168
|
|
|
113
169
|
<svelte:window onkeydown={keydown} />
|
|
114
170
|
|
|
115
|
-
<aside aria-label={current?.
|
|
171
|
+
<aside aria-label={current?.title ?? 'Study artefact'} class="ds-lib-pane">
|
|
116
172
|
<!-- The sheet's grabber. On a phone this is an overlay a reader has to be
|
|
117
173
|
able to see the top edge of; on a desktop it is a column, so there is
|
|
118
174
|
nothing here to grab. -->
|
|
119
175
|
<div class="ds-lib-pane-grabber" aria-hidden="true"><span></span></div>
|
|
120
176
|
|
|
121
177
|
<header class="ds-lib-pane-header">
|
|
178
|
+
<button
|
|
179
|
+
bind:this={closeButton}
|
|
180
|
+
type="button"
|
|
181
|
+
class="ds-lib-pane-close"
|
|
182
|
+
onclick={onclose}
|
|
183
|
+
aria-label={words.closeSource}
|
|
184
|
+
>
|
|
185
|
+
<XIcon size={16} />
|
|
186
|
+
</button>
|
|
122
187
|
<div class="ds-lib-pane-heading">
|
|
123
188
|
{#if current}
|
|
124
|
-
<h2 class="ds-lib-pane-title">{current.
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
189
|
+
<h2 class="ds-lib-pane-title">{current.title}</h2>
|
|
190
|
+
{#if sub}<p class="ds-lib-pane-subtitle">{sub}</p>{/if}
|
|
191
|
+
{:else}
|
|
192
|
+
<h2 class="ds-lib-pane-title">{turn?.artefact?.title}</h2>
|
|
193
|
+
{#if turn?.artefact?.summary}
|
|
194
|
+
<p class="ds-lib-pane-subtitle">{turn.artefact.summary}</p>
|
|
195
|
+
{/if}
|
|
196
|
+
{/if}
|
|
197
|
+
</div>
|
|
198
|
+
{#if current}
|
|
199
|
+
<select
|
|
200
|
+
class="ds-lib-pane-versions"
|
|
201
|
+
aria-label={words.writtenVersions}
|
|
202
|
+
value={current.version}
|
|
203
|
+
onchange={(event) => onversion?.(Number(event.currentTarget.value))}
|
|
204
|
+
>
|
|
205
|
+
{#each versions as option (option.version)}
|
|
206
|
+
<option value={option.version}>{fill(words.writtenVersion, { n: option.version })}</option>
|
|
207
|
+
{/each}
|
|
208
|
+
</select>
|
|
209
|
+
{#if source && !failed}
|
|
210
|
+
<button
|
|
211
|
+
type="button"
|
|
212
|
+
class="ds-lib-pane-tool ds-lib-pane-copy"
|
|
213
|
+
onclick={copyDocument}
|
|
214
|
+
aria-label={copied ? words.copiedDocument : words.copyDocument}
|
|
215
|
+
>
|
|
216
|
+
{#if copied}<CheckIcon size={15} />{:else}<CopyIcon size={15} />{/if}
|
|
217
|
+
</button>
|
|
218
|
+
{/if}
|
|
219
|
+
{#if writtenUrl && links.length > 0}
|
|
220
|
+
<div class="ds-lib-pane-menu">
|
|
221
|
+
<button
|
|
222
|
+
type="button"
|
|
223
|
+
class="ds-lib-pane-tool"
|
|
224
|
+
aria-haspopup="menu"
|
|
225
|
+
aria-expanded={menu}
|
|
226
|
+
onclick={() => (menu = !menu)}
|
|
131
227
|
>
|
|
132
|
-
{
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
228
|
+
<DownloadIcon size={15} />{words.download}<ChevronDownIcon size={14} />
|
|
229
|
+
</button>
|
|
230
|
+
{#if menu}
|
|
231
|
+
<ul class="ds-lib-pane-links" role="menu" aria-label={words.download}>
|
|
232
|
+
{#each links as link (link.format)}
|
|
233
|
+
<li role="none">
|
|
234
|
+
<a
|
|
235
|
+
role="menuitem"
|
|
236
|
+
href={writtenUrl(current.name, current.version, link.format)}
|
|
237
|
+
onclick={() => (menu = false)}>{link.label}</a
|
|
238
|
+
>
|
|
141
239
|
</li>
|
|
142
240
|
{/each}
|
|
143
241
|
</ul>
|
|
144
242
|
{/if}
|
|
145
243
|
</div>
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
244
|
+
{#if primary}
|
|
245
|
+
<a
|
|
246
|
+
class="ds-lib-pane-tool ds-lib-pane-quick"
|
|
247
|
+
href={writtenUrl(current.name, current.version, primary.format)}
|
|
248
|
+
>
|
|
249
|
+
<DownloadIcon size={15} />{primary.label}
|
|
250
|
+
</a>
|
|
150
251
|
{/if}
|
|
151
252
|
{/if}
|
|
152
|
-
|
|
153
|
-
<button
|
|
154
|
-
bind:this={closeButton}
|
|
155
|
-
type="button"
|
|
156
|
-
class="ds-lib-pane-close"
|
|
157
|
-
onclick={onclose}
|
|
158
|
-
aria-label={words.closeSource}
|
|
159
|
-
>
|
|
160
|
-
<XIcon size={16} />
|
|
161
|
-
</button>
|
|
253
|
+
{/if}
|
|
162
254
|
</header>
|
|
163
255
|
|
|
164
256
|
{#if current}
|
|
257
|
+
{#if changed}<p class="ds-lib-pane-changed">{changed}</p>{/if}
|
|
165
258
|
{@const html = source !== null && isHtml(source.mediaType || current.media_type)}
|
|
166
259
|
<div class="ds-lib-pane-body" class:is-frame={html}>
|
|
167
260
|
{#if failed}
|
|
168
|
-
<p class="ds-lib-pane-
|
|
261
|
+
<p class="ds-lib-pane-note">{words.documentUnavailable}</p>
|
|
169
262
|
{:else if source && html}
|
|
170
|
-
<iframe class="ds-lib-pane-frame" sandbox="" srcdoc={framed(source.text)} title={current.
|
|
263
|
+
<iframe class="ds-lib-pane-frame" sandbox="" srcdoc={framed(source.text)} title={current.title}
|
|
171
264
|
></iframe>
|
|
172
265
|
{:else if source}
|
|
173
|
-
<
|
|
266
|
+
<div class="ds-lib-pane-paper">
|
|
267
|
+
<Markdown content={stripFrontMatter(source.text)} previous={earlier ?? undefined} />
|
|
268
|
+
</div>
|
|
174
269
|
{/if}
|
|
175
270
|
</div>
|
|
176
271
|
{:else}
|
|
@@ -189,17 +284,12 @@
|
|
|
189
284
|
<style>
|
|
190
285
|
.ds-lib-pane {
|
|
191
286
|
position: fixed;
|
|
192
|
-
inset
|
|
193
|
-
bottom: 0;
|
|
287
|
+
inset: 0;
|
|
194
288
|
z-index: 40;
|
|
195
289
|
display: flex;
|
|
196
|
-
height:
|
|
290
|
+
height: 100dvh;
|
|
197
291
|
flex-direction: column;
|
|
198
|
-
border-top: 1px solid var(--ds-color-border);
|
|
199
|
-
border-start-start-radius: var(--ds-radius-xl);
|
|
200
|
-
border-start-end-radius: var(--ds-radius-xl);
|
|
201
292
|
background: var(--ds-color-surface-1);
|
|
202
|
-
box-shadow: 0 -12px 40px rgb(0 0 0 / 0.25);
|
|
203
293
|
}
|
|
204
294
|
|
|
205
295
|
.ds-lib-pane-grabber {
|
|
@@ -216,17 +306,19 @@
|
|
|
216
306
|
background: var(--ds-color-border);
|
|
217
307
|
}
|
|
218
308
|
|
|
309
|
+
/* A phone's sheet leads with Close; the column ends with it. */
|
|
219
310
|
.ds-lib-pane-header {
|
|
220
311
|
display: flex;
|
|
221
|
-
align-items:
|
|
312
|
+
align-items: center;
|
|
222
313
|
gap: 0.5rem;
|
|
223
314
|
border-bottom: 1px solid var(--ds-color-border);
|
|
224
|
-
padding: 0.
|
|
315
|
+
padding: 0.5rem 1rem 0.75rem;
|
|
225
316
|
}
|
|
226
317
|
|
|
227
318
|
.ds-lib-pane-heading {
|
|
228
319
|
min-width: 0;
|
|
229
320
|
flex: 1;
|
|
321
|
+
order: 1;
|
|
230
322
|
}
|
|
231
323
|
|
|
232
324
|
.ds-lib-pane-title,
|
|
@@ -240,23 +332,24 @@
|
|
|
240
332
|
.ds-lib-pane-title {
|
|
241
333
|
color: var(--ds-color-foreground);
|
|
242
334
|
font-family: inherit;
|
|
243
|
-
font-size: 0.
|
|
335
|
+
font-size: 0.9375rem;
|
|
244
336
|
font-weight: 600;
|
|
245
337
|
}
|
|
246
338
|
|
|
247
339
|
.ds-lib-pane-subtitle {
|
|
340
|
+
display: none;
|
|
248
341
|
color: var(--ds-color-muted-foreground);
|
|
249
342
|
font-size: var(--ds-text-2xs);
|
|
250
343
|
}
|
|
251
344
|
|
|
252
345
|
.ds-lib-pane-close {
|
|
253
346
|
display: flex;
|
|
254
|
-
width:
|
|
255
|
-
height:
|
|
347
|
+
width: 2.5rem;
|
|
348
|
+
height: 2.5rem;
|
|
256
349
|
flex: none;
|
|
257
350
|
align-items: center;
|
|
258
351
|
justify-content: center;
|
|
259
|
-
|
|
352
|
+
order: 0;
|
|
260
353
|
border: 0;
|
|
261
354
|
border-radius: var(--ds-radius-lg);
|
|
262
355
|
background: none;
|
|
@@ -272,50 +365,135 @@
|
|
|
272
365
|
color: var(--ds-color-foreground);
|
|
273
366
|
}
|
|
274
367
|
|
|
275
|
-
.ds-lib-pane-close:focus-visible
|
|
368
|
+
.ds-lib-pane-close:focus-visible,
|
|
369
|
+
.ds-lib-pane-tool:focus-visible,
|
|
370
|
+
.ds-lib-pane-versions:focus-visible {
|
|
276
371
|
outline: 2px solid var(--ds-color-ring);
|
|
277
372
|
outline-offset: 2px;
|
|
278
373
|
}
|
|
279
374
|
|
|
280
|
-
.ds-lib-pane-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
padding: 0.
|
|
375
|
+
.ds-lib-pane-versions {
|
|
376
|
+
order: 2;
|
|
377
|
+
border: 1px solid var(--ds-color-border);
|
|
378
|
+
border-radius: var(--ds-radius-md);
|
|
379
|
+
background: var(--ds-color-surface-1);
|
|
380
|
+
padding: 0.375rem 0.5rem;
|
|
381
|
+
color: var(--ds-color-foreground);
|
|
382
|
+
font: inherit;
|
|
383
|
+
font-size: 0.8125rem;
|
|
286
384
|
}
|
|
287
385
|
|
|
288
|
-
.ds-lib-pane-
|
|
289
|
-
display: flex;
|
|
290
|
-
|
|
386
|
+
.ds-lib-pane-tool {
|
|
387
|
+
display: inline-flex;
|
|
388
|
+
min-height: 2.25rem;
|
|
291
389
|
align-items: center;
|
|
292
|
-
gap: 0.
|
|
293
|
-
margin-top: 0.25rem;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
.ds-lib-pane-versions {
|
|
390
|
+
gap: 0.375rem;
|
|
297
391
|
border: 1px solid var(--ds-color-border);
|
|
298
392
|
border-radius: var(--ds-radius-md);
|
|
299
|
-
background:
|
|
300
|
-
padding: 0.
|
|
393
|
+
background: none;
|
|
394
|
+
padding: 0.25rem 0.625rem;
|
|
301
395
|
color: var(--ds-color-foreground);
|
|
302
396
|
font: inherit;
|
|
303
|
-
font-size:
|
|
397
|
+
font-size: 0.8125rem;
|
|
398
|
+
text-decoration: none;
|
|
399
|
+
cursor: pointer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.ds-lib-pane-tool:hover {
|
|
403
|
+
background: var(--ds-color-surface-2);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.ds-lib-pane-copy {
|
|
407
|
+
display: none;
|
|
408
|
+
order: 3;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.ds-lib-pane-menu {
|
|
412
|
+
position: relative;
|
|
413
|
+
display: none;
|
|
414
|
+
order: 4;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.ds-lib-pane-quick {
|
|
418
|
+
order: 3;
|
|
304
419
|
}
|
|
305
420
|
|
|
306
421
|
.ds-lib-pane-links {
|
|
422
|
+
position: absolute;
|
|
423
|
+
inset-block-start: calc(100% + 0.25rem);
|
|
424
|
+
inset-inline-end: 0;
|
|
425
|
+
z-index: 2;
|
|
307
426
|
display: flex;
|
|
308
|
-
|
|
309
|
-
|
|
427
|
+
min-width: 9rem;
|
|
428
|
+
flex-direction: column;
|
|
310
429
|
margin: 0;
|
|
311
|
-
|
|
312
|
-
|
|
430
|
+
border: 1px solid var(--ds-color-border);
|
|
431
|
+
border-radius: var(--ds-radius-md);
|
|
432
|
+
background: var(--ds-color-surface-1);
|
|
433
|
+
padding: 0.25rem;
|
|
434
|
+
box-shadow: 0 8px 24px rgb(0 0 0 / 0.25);
|
|
313
435
|
list-style: none;
|
|
314
436
|
}
|
|
315
437
|
|
|
316
438
|
.ds-lib-pane-links a {
|
|
439
|
+
display: block;
|
|
440
|
+
border-radius: var(--ds-radius-sm);
|
|
441
|
+
padding: 0.5rem 0.625rem;
|
|
442
|
+
color: var(--ds-color-foreground);
|
|
443
|
+
font-size: 0.8125rem;
|
|
444
|
+
text-decoration: none;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.ds-lib-pane-links a:hover,
|
|
448
|
+
.ds-lib-pane-links a:focus-visible {
|
|
449
|
+
background: var(--ds-color-surface-2);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.ds-lib-pane-changed {
|
|
453
|
+
display: flex;
|
|
454
|
+
align-items: center;
|
|
455
|
+
gap: 0.5rem;
|
|
456
|
+
margin: 0;
|
|
457
|
+
border-bottom: 1px solid var(--ds-color-border);
|
|
458
|
+
padding: 0.5rem 1rem;
|
|
317
459
|
color: var(--ds-color-muted-foreground);
|
|
318
|
-
|
|
460
|
+
font-size: 0.8125rem;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.ds-lib-pane-changed::before {
|
|
464
|
+
content: '';
|
|
465
|
+
width: 2px;
|
|
466
|
+
height: 0.875rem;
|
|
467
|
+
flex: none;
|
|
468
|
+
background: var(--ds-color-foreground);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.ds-lib-pane-body {
|
|
472
|
+
min-height: 0;
|
|
473
|
+
flex: 1;
|
|
474
|
+
overflow-y: auto;
|
|
475
|
+
overscroll-behavior: contain;
|
|
476
|
+
padding: 0.75rem 1rem;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.ds-lib-pane-note {
|
|
480
|
+
margin: 0;
|
|
481
|
+
color: var(--ds-color-muted-foreground);
|
|
482
|
+
font-size: var(--ds-text-2xs);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/* The document is paper, light in either theme: its own ink and rules
|
|
486
|
+
replace the app's, so a dark page does not paint it grey on grey. */
|
|
487
|
+
.ds-lib-pane-paper {
|
|
488
|
+
--ds-color-foreground: var(--ds-paper-ink, #1b1c1e);
|
|
489
|
+
--ds-color-muted-foreground: var(--ds-paper-muted, #5f6368);
|
|
490
|
+
--ds-color-border: var(--ds-paper-rule, #c4c7cc);
|
|
491
|
+
--ds-color-surface-1: var(--ds-paper-shade, #dcdee2);
|
|
492
|
+
--ds-color-primary-ink: var(--ds-paper-ink, #1b1c1e);
|
|
493
|
+
border-radius: var(--ds-radius-sm);
|
|
494
|
+
background: var(--ds-paper, #e9eaec);
|
|
495
|
+
padding: 1.25rem 1.5rem;
|
|
496
|
+
color: var(--ds-color-foreground);
|
|
319
497
|
}
|
|
320
498
|
|
|
321
499
|
.ds-lib-pane-body.is-frame {
|
|
@@ -340,16 +518,38 @@
|
|
|
340
518
|
position: relative;
|
|
341
519
|
inset: auto;
|
|
342
520
|
height: auto;
|
|
343
|
-
width:
|
|
521
|
+
width: 42rem;
|
|
344
522
|
flex: none;
|
|
345
|
-
border-top: 0;
|
|
346
523
|
border-inline-start: 1px solid var(--ds-color-border);
|
|
347
|
-
border-radius: 0;
|
|
348
|
-
box-shadow: none;
|
|
349
524
|
}
|
|
350
525
|
|
|
351
526
|
.ds-lib-pane-grabber {
|
|
352
527
|
display: none;
|
|
353
528
|
}
|
|
529
|
+
|
|
530
|
+
.ds-lib-pane-header {
|
|
531
|
+
padding-top: 0.75rem;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.ds-lib-pane-heading {
|
|
535
|
+
order: 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.ds-lib-pane-subtitle {
|
|
539
|
+
display: block;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.ds-lib-pane-copy,
|
|
543
|
+
.ds-lib-pane-menu {
|
|
544
|
+
display: inline-flex;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.ds-lib-pane-quick {
|
|
548
|
+
display: none;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.ds-lib-pane-close {
|
|
552
|
+
order: 5;
|
|
553
|
+
}
|
|
354
554
|
}
|
|
355
555
|
</style>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
type AgentTranscriptProps
|
|
34
34
|
} from '../agent-transcript/agent-transcript.svelte';
|
|
35
35
|
import ArtefactPane from '../artefact-pane/artefact-pane.svelte';
|
|
36
|
-
import type {
|
|
36
|
+
import type { Visual } from '../../visuals';
|
|
37
37
|
import DocumentPane from '../document-pane/document-pane.svelte';
|
|
38
38
|
import ScopeStatement from '../scope-statement/scope-statement.svelte';
|
|
39
39
|
|
|
@@ -109,9 +109,9 @@
|
|
|
109
109
|
loadWritten?: LoadWritten;
|
|
110
110
|
/** Where a version downloads. Without it no download links render. */
|
|
111
111
|
writtenUrl?: WrittenUrl;
|
|
112
|
-
/**
|
|
113
|
-
* rather than as code. */
|
|
114
|
-
|
|
112
|
+
/** Renderers by a fence's language (`defaultVisuals`, and the app's own):
|
|
113
|
+
* a block of one is drawn by its component rather than as code. */
|
|
114
|
+
visuals?: Record<string, Visual>;
|
|
115
115
|
/** Prices a turn's `outcome.costUsd` in the host's own currency and
|
|
116
116
|
* words ("about A$0.34"), for the footer's cost line beside a turn's
|
|
117
117
|
* duration. The package never converts currency itself; omitted, the
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
onedit,
|
|
163
163
|
loadWritten,
|
|
164
164
|
writtenUrl,
|
|
165
|
-
|
|
165
|
+
visuals,
|
|
166
166
|
collectionNames = new Set(),
|
|
167
167
|
composer,
|
|
168
168
|
turn: presentTurn,
|
|
@@ -386,7 +386,7 @@
|
|
|
386
386
|
showingWritten: shownWritten?.key,
|
|
387
387
|
onopenwritten: loadWritten ? openWritten : undefined,
|
|
388
388
|
writtenUrl,
|
|
389
|
-
|
|
389
|
+
visuals,
|
|
390
390
|
onedit:
|
|
391
391
|
onedit && !running && !turn.kind && turn.question
|
|
392
392
|
? (text: string) => onedit(index, text)
|
|
@@ -5,7 +5,7 @@ import type { Citation, LoadDocument } from '../../citations';
|
|
|
5
5
|
import { type LoadWritten, type WrittenUrl } from '../../written';
|
|
6
6
|
import { type ChatCopy } from '../../copy';
|
|
7
7
|
import { type AgentTranscriptProps } from '../agent-transcript/agent-transcript.svelte';
|
|
8
|
-
import type {
|
|
8
|
+
import type { Visual } from '../../visuals';
|
|
9
9
|
interface Props {
|
|
10
10
|
turns: Turn[];
|
|
11
11
|
running: boolean;
|
|
@@ -68,9 +68,9 @@ interface Props {
|
|
|
68
68
|
loadWritten?: LoadWritten;
|
|
69
69
|
/** Where a version downloads. Without it no download links render. */
|
|
70
70
|
writtenUrl?: WrittenUrl;
|
|
71
|
-
/**
|
|
72
|
-
* rather than as code. */
|
|
73
|
-
|
|
71
|
+
/** Renderers by a fence's language (`defaultVisuals`, and the app's own):
|
|
72
|
+
* a block of one is drawn by its component rather than as code. */
|
|
73
|
+
visuals?: Record<string, Visual>;
|
|
74
74
|
/** Prices a turn's `outcome.costUsd` in the host's own currency and
|
|
75
75
|
* words ("about A$0.34"), for the footer's cost line beside a turn's
|
|
76
76
|
* duration. The package never converts currency itself; omitted, the
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
* Sanitised on the way out, always. The content is model output rendered as
|
|
16
16
|
* HTML, which is exactly the case DOMPurify exists for.
|
|
17
17
|
*/
|
|
18
|
-
import { type Component } from 'svelte';
|
|
19
18
|
/** Close anything the stream has opened but not yet finished. */
|
|
20
19
|
export declare function balance(markdown: string): string;
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
/**
|
|
21
|
+
* `markdown` as sanitised HTML. With `previous`, the version before it, each
|
|
22
|
+
* block it changed is marked on the sanitised DOM, before its one serialisation.
|
|
23
|
+
*/
|
|
24
|
+
export declare function render(markdown: string, { streaming, previous }?: {
|
|
25
|
+
streaming?: boolean;
|
|
26
|
+
previous?: string;
|
|
23
27
|
}): string;
|
|
24
28
|
/**
|
|
25
29
|
* Mark every inline `code` span that names a known collection.
|
|
@@ -43,16 +47,29 @@ export declare function markCollections(root: HTMLElement, collections: Set<stri
|
|
|
43
47
|
* button ROLE and a tab stop, and the component delegates the events.
|
|
44
48
|
*/
|
|
45
49
|
export declare function markCitations(root: HTMLElement, valid: Set<number>): void;
|
|
46
|
-
/**
|
|
47
|
-
export type
|
|
50
|
+
/** A turn's prose and the fenced blocks of a registered language, in order. */
|
|
51
|
+
export type Part = {
|
|
52
|
+
kind: 'md';
|
|
53
|
+
text: string;
|
|
54
|
+
} | {
|
|
55
|
+
kind: 'visual';
|
|
56
|
+
lang: string;
|
|
48
57
|
source: string;
|
|
49
|
-
|
|
58
|
+
closed: boolean;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Cut `markdown` at each fenced block whose language `has` a renderer for.
|
|
62
|
+
* A fence of another language (or none) is ordinary code and stays in the
|
|
63
|
+
* prose, and so does anything inside one: an example of a visual's fence in a
|
|
64
|
+
* code block is text. A block still open at the end is `closed: false`.
|
|
65
|
+
*/
|
|
66
|
+
export declare function splitVisuals(markdown: string, has: (lang: string) => boolean): Part[];
|
|
50
67
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
68
|
+
* Turn each link to a numbered web page into its citation chip: the link's
|
|
69
|
+
* text stays, unlinked, and the chip after it carries the page's number and
|
|
70
|
+
* opens the page. Against the rendered DOM, for the reason `markCitations` is.
|
|
54
71
|
*/
|
|
55
|
-
export declare function
|
|
72
|
+
export declare function markWebLinks(root: HTMLElement, numbers: Map<string, number>): void;
|
|
56
73
|
/**
|
|
57
74
|
* Replace every `<pre><code>` in a rendered fragment with a highlighted one.
|
|
58
75
|
* Runs against the DOM node rather than the HTML string so it can be applied
|