@poodle64/librarian 2026.9.13 → 2026.9.15
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 +77 -19
- package/dist/client.d.ts +16 -1
- package/dist/client.js +55 -8
- package/dist/components/activity-group/activity-group.svelte +76 -8
- package/dist/components/agent-transcript/agent-transcript.svelte +346 -150
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +7 -0
- package/dist/components/artefact-card/artefact-card.svelte +30 -2
- package/dist/components/artefact-pane/artefact-pane.svelte +141 -50
- package/dist/components/composer/composer.svelte +255 -38
- package/dist/components/composer/composer.svelte.d.ts +6 -0
- package/dist/components/conversation/conversation.svelte +254 -81
- package/dist/components/conversation/conversation.svelte.d.ts +7 -0
- package/dist/components/document-pane/document-pane.svelte +216 -33
- package/dist/components/markdown/markdown.svelte +73 -24
- package/dist/components/scope-statement/scope-statement.svelte +79 -12
- package/dist/components/source-list/index.d.ts +2 -0
- package/dist/components/source-list/index.js +2 -0
- package/dist/components/source-list/source-list.svelte +137 -0
- package/dist/components/source-list/source-list.svelte.d.ts +13 -0
- package/dist/components/thinking-row/thinking-row.svelte +69 -18
- package/dist/components/tool-row/tool-row.svelte +132 -26
- package/dist/components/working/working.svelte +51 -7
- package/dist/components/working/working.svelte.d.ts +3 -0
- package/dist/copy.d.ts +45 -10
- package/dist/copy.js +77 -32
- package/dist/transcript.svelte.d.ts +10 -0
- package/dist/transcript.svelte.js +4 -1
- package/package.json +4 -2
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
<!--
|
|
2
2
|
A study artefact, open in the reading column.
|
|
3
3
|
|
|
4
|
-
The same shell shape `DocumentPane` uses (a column at
|
|
5
|
-
below it) because decision 2 of the
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
The same shell shape `DocumentPane` uses (a column at 64rem, a bottom sheet
|
|
5
|
+
below it) because decision 2 of the agent surface is that a colleague never
|
|
6
|
+
has to learn which of the two things is in the column before they open it —
|
|
7
|
+
only ever one, in the same place, closing whichever was there.
|
|
8
8
|
-->
|
|
9
9
|
<script lang="ts">
|
|
10
10
|
import XIcon from '@lucide/svelte/icons/x';
|
|
11
11
|
import { segment, type TextBlock, type Turn } from '../../transcript.svelte';
|
|
12
|
-
import { resolveCitations, splitSources,
|
|
12
|
+
import { resolveCitations, splitSources, type Citation } from '../../citations';
|
|
13
13
|
import { resolveCopy, type LibrarianCopy } from '../../copy';
|
|
14
14
|
import Markdown from '../markdown/markdown.svelte';
|
|
15
|
+
import SourceList from '../source-list/source-list.svelte';
|
|
15
16
|
|
|
16
17
|
interface Props {
|
|
17
18
|
turn: Turn;
|
|
@@ -52,69 +53,159 @@
|
|
|
52
53
|
|
|
53
54
|
<svelte:window onkeydown={keydown} />
|
|
54
55
|
|
|
55
|
-
<aside
|
|
56
|
-
|
|
57
|
-
class="bg-surface-1 border-border fixed inset-x-0 bottom-0 z-40 flex h-[80svh] flex-col rounded-t-2xl border-t shadow-2xl lg:relative lg:h-auto lg:w-[560px] lg:shrink-0 lg:rounded-none lg:border-t-0 lg:border-l lg:shadow-none"
|
|
58
|
-
>
|
|
59
|
-
<!-- The sheet's grabber. Below `lg` this is an overlay a reader has to be
|
|
56
|
+
<aside aria-label="Study artefact" class="ds-lib-pane">
|
|
57
|
+
<!-- The sheet's grabber. On a phone this is an overlay a reader has to be
|
|
60
58
|
able to see the top edge of; on a desktop it is a column, so there is
|
|
61
59
|
nothing here to grab. -->
|
|
62
|
-
<div class="
|
|
63
|
-
<span class="bg-border h-1 w-9 rounded-full"></span>
|
|
64
|
-
</div>
|
|
60
|
+
<div class="ds-lib-pane-grabber" aria-hidden="true"><span></span></div>
|
|
65
61
|
|
|
66
|
-
<header class="
|
|
67
|
-
<div class="
|
|
68
|
-
<h2 class="
|
|
69
|
-
<p class="
|
|
62
|
+
<header class="ds-lib-pane-header">
|
|
63
|
+
<div class="ds-lib-pane-heading">
|
|
64
|
+
<h2 class="ds-lib-pane-title">{turn.title ?? 'Briefing'}</h2>
|
|
65
|
+
<p class="ds-lib-pane-subtitle">
|
|
70
66
|
Briefing doc · {sources.length} source{sources.length === 1 ? '' : 's'}
|
|
71
67
|
</p>
|
|
72
68
|
</div>
|
|
73
69
|
<button
|
|
74
70
|
bind:this={closeButton}
|
|
75
71
|
type="button"
|
|
72
|
+
class="ds-lib-pane-close"
|
|
76
73
|
onclick={onclose}
|
|
77
74
|
aria-label={words.closeSource}
|
|
78
|
-
class="text-muted-foreground hover:text-foreground hover:bg-surface-2 focus-visible:ring-ring -mt-1 flex size-8 shrink-0 items-center justify-center rounded-lg transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
|
79
75
|
>
|
|
80
|
-
<XIcon
|
|
76
|
+
<XIcon size={16} />
|
|
81
77
|
</button>
|
|
82
78
|
</header>
|
|
83
79
|
|
|
84
|
-
<div class="
|
|
80
|
+
<div class="ds-lib-pane-body">
|
|
85
81
|
<Markdown content={split.citations.length > 0 ? split.body : rawAnswer} />
|
|
86
82
|
|
|
87
83
|
{#if sources.length > 0}
|
|
88
|
-
<
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
</h3>
|
|
92
|
-
<ol class="flex flex-col gap-1">
|
|
93
|
-
{#each sources as source (source.n)}
|
|
94
|
-
{@const mark = trustMark(source, words)}
|
|
95
|
-
<li>
|
|
96
|
-
<button
|
|
97
|
-
type="button"
|
|
98
|
-
onclick={() => oncite?.(source)}
|
|
99
|
-
disabled={!source.document_id || !oncite}
|
|
100
|
-
class="border-border hover:border-border-strong hover:bg-surface-2 focus-visible:ring-ring flex w-full items-baseline gap-2 rounded-lg border px-2.5 py-1.5 text-left text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:cursor-default disabled:hover:bg-transparent"
|
|
101
|
-
>
|
|
102
|
-
<span
|
|
103
|
-
class="bg-primary/15 text-foreground shrink-0 rounded px-1.5 font-mono text-xs tabular-nums"
|
|
104
|
-
>{source.n}</span
|
|
105
|
-
>
|
|
106
|
-
<span class="min-w-0">
|
|
107
|
-
<span class="text-foreground">{source.title}</span>
|
|
108
|
-
{#if source.section}<span class="text-muted-foreground">
|
|
109
|
-
· {source.section}</span
|
|
110
|
-
>{/if}
|
|
111
|
-
{#if mark}<span class="text-muted-foreground/80"> · {mark}</span>{/if}
|
|
112
|
-
</span>
|
|
113
|
-
</button>
|
|
114
|
-
</li>
|
|
115
|
-
{/each}
|
|
116
|
-
</ol>
|
|
117
|
-
</section>
|
|
84
|
+
<div class="ds-lib-pane-sources">
|
|
85
|
+
<SourceList {sources} {words} {oncite} level={3} />
|
|
86
|
+
</div>
|
|
118
87
|
{/if}
|
|
119
88
|
</div>
|
|
120
89
|
</aside>
|
|
90
|
+
|
|
91
|
+
<style>
|
|
92
|
+
.ds-lib-pane {
|
|
93
|
+
position: fixed;
|
|
94
|
+
inset-inline: 0;
|
|
95
|
+
bottom: 0;
|
|
96
|
+
z-index: 40;
|
|
97
|
+
display: flex;
|
|
98
|
+
height: 80svh;
|
|
99
|
+
flex-direction: column;
|
|
100
|
+
border-top: 1px solid var(--ds-color-border);
|
|
101
|
+
border-start-start-radius: var(--ds-radius-xl);
|
|
102
|
+
border-start-end-radius: var(--ds-radius-xl);
|
|
103
|
+
background: var(--ds-color-surface-1);
|
|
104
|
+
box-shadow: 0 -12px 40px rgb(0 0 0 / 0.25);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.ds-lib-pane-grabber {
|
|
108
|
+
display: flex;
|
|
109
|
+
justify-content: center;
|
|
110
|
+
padding-top: 0.5rem;
|
|
111
|
+
padding-bottom: 0.25rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.ds-lib-pane-grabber span {
|
|
115
|
+
width: 2.25rem;
|
|
116
|
+
height: 0.25rem;
|
|
117
|
+
border-radius: var(--ds-radius-full);
|
|
118
|
+
background: var(--ds-color-border);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ds-lib-pane-header {
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: flex-start;
|
|
124
|
+
gap: 0.5rem;
|
|
125
|
+
border-bottom: 1px solid var(--ds-color-border);
|
|
126
|
+
padding: 0.75rem 1rem;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.ds-lib-pane-heading {
|
|
130
|
+
min-width: 0;
|
|
131
|
+
flex: 1;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.ds-lib-pane-title,
|
|
135
|
+
.ds-lib-pane-subtitle {
|
|
136
|
+
margin: 0;
|
|
137
|
+
overflow: hidden;
|
|
138
|
+
text-overflow: ellipsis;
|
|
139
|
+
white-space: nowrap;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.ds-lib-pane-title {
|
|
143
|
+
color: var(--ds-color-foreground);
|
|
144
|
+
font-family: inherit;
|
|
145
|
+
font-size: 0.875rem;
|
|
146
|
+
font-weight: 600;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ds-lib-pane-subtitle {
|
|
150
|
+
color: var(--ds-color-muted-foreground);
|
|
151
|
+
font-size: var(--ds-text-2xs);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ds-lib-pane-close {
|
|
155
|
+
display: flex;
|
|
156
|
+
width: 2rem;
|
|
157
|
+
height: 2rem;
|
|
158
|
+
flex: none;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: center;
|
|
161
|
+
margin-top: -0.25rem;
|
|
162
|
+
border: 0;
|
|
163
|
+
border-radius: var(--ds-radius-lg);
|
|
164
|
+
background: none;
|
|
165
|
+
color: var(--ds-color-muted-foreground);
|
|
166
|
+
cursor: pointer;
|
|
167
|
+
transition:
|
|
168
|
+
color 150ms ease,
|
|
169
|
+
background-color 150ms ease;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.ds-lib-pane-close:hover {
|
|
173
|
+
background: var(--ds-color-surface-2);
|
|
174
|
+
color: var(--ds-color-foreground);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ds-lib-pane-close:focus-visible {
|
|
178
|
+
outline: 2px solid var(--ds-color-ring);
|
|
179
|
+
outline-offset: 2px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.ds-lib-pane-body {
|
|
183
|
+
min-height: 0;
|
|
184
|
+
flex: 1;
|
|
185
|
+
overflow-y: auto;
|
|
186
|
+
overscroll-behavior: contain;
|
|
187
|
+
padding: 0.75rem 1rem;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.ds-lib-pane-sources {
|
|
191
|
+
margin-top: 1.5rem;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
@media (min-width: 64rem) {
|
|
195
|
+
.ds-lib-pane {
|
|
196
|
+
position: relative;
|
|
197
|
+
inset: auto;
|
|
198
|
+
height: auto;
|
|
199
|
+
width: 35rem;
|
|
200
|
+
flex: none;
|
|
201
|
+
border-top: 0;
|
|
202
|
+
border-inline-start: 1px solid var(--ds-color-border);
|
|
203
|
+
border-radius: 0;
|
|
204
|
+
box-shadow: none;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.ds-lib-pane-grabber {
|
|
208
|
+
display: none;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
</style>
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
MAX_FILES,
|
|
24
24
|
rejectionMessage
|
|
25
25
|
} from '../../attachments';
|
|
26
|
+
import { DEFAULT_PERSONA, resolveCopy, type LibrarianCopy } from '../../copy';
|
|
26
27
|
|
|
27
28
|
export type Scope = 'document' | 'collection' | 'library';
|
|
28
29
|
|
|
@@ -44,6 +45,11 @@
|
|
|
44
45
|
* control renders — studying is a mode of asking, never a tab, so a
|
|
45
46
|
* host with nothing to build offers none rather than a disabled one. */
|
|
46
47
|
onbriefing?: () => void;
|
|
48
|
+
/** Who is being asked. A slug is fine — `penny` renders as "Penny" — and
|
|
49
|
+
* both placeholders are composed from it. */
|
|
50
|
+
name?: string;
|
|
51
|
+
/** Overrides for the package's own words. */
|
|
52
|
+
copy?: Partial<LibrarianCopy>;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
let {
|
|
@@ -57,9 +63,13 @@
|
|
|
57
63
|
onscope,
|
|
58
64
|
onsubmit,
|
|
59
65
|
onstop,
|
|
60
|
-
onbriefing
|
|
66
|
+
onbriefing,
|
|
67
|
+
name = DEFAULT_PERSONA,
|
|
68
|
+
copy
|
|
61
69
|
}: Props = $props();
|
|
62
70
|
|
|
71
|
+
const words = $derived(resolveCopy(copy, name));
|
|
72
|
+
|
|
63
73
|
// What Milton is being asked about — this document, this collection, or
|
|
64
74
|
// the whole library. Callers that have no narrower scope pass neither
|
|
65
75
|
// name, leaving "The whole library" the sole entry — and a sole entry is
|
|
@@ -177,30 +187,25 @@
|
|
|
177
187
|
</script>
|
|
178
188
|
|
|
179
189
|
<div
|
|
190
|
+
class="ds-lib-composer-lift"
|
|
180
191
|
style="transform: translateY(-{lift}px); padding-bottom: {lift
|
|
181
192
|
? '0px'
|
|
182
193
|
: 'env(safe-area-inset-bottom, 0px)'}"
|
|
183
|
-
class="transition-transform duration-150"
|
|
184
194
|
>
|
|
185
|
-
<div
|
|
186
|
-
class="border-border bg-surface-1 focus-within:border-primary/60 rounded-xl border transition-colors"
|
|
187
|
-
>
|
|
195
|
+
<div class="ds-lib-box">
|
|
188
196
|
{#if files.length > 0}
|
|
189
|
-
<ul class="
|
|
197
|
+
<ul class="ds-lib-files">
|
|
190
198
|
{#each files as file, index (file.name + file.size)}
|
|
191
|
-
<li
|
|
192
|
-
class="
|
|
193
|
-
|
|
194
|
-
<span class="text-foreground truncate">{file.name}</span>
|
|
195
|
-
<span class="text-muted-foreground shrink-0 tabular-nums">{formatSize(file.size)}</span
|
|
196
|
-
>
|
|
199
|
+
<li class="ds-lib-file">
|
|
200
|
+
<span class="ds-lib-file-name">{file.name}</span>
|
|
201
|
+
<span class="ds-lib-file-size">{formatSize(file.size)}</span>
|
|
197
202
|
<button
|
|
198
203
|
type="button"
|
|
204
|
+
class="ds-lib-file-remove"
|
|
199
205
|
onclick={() => remove(index)}
|
|
200
206
|
aria-label="Remove {file.name}"
|
|
201
|
-
class="text-muted-foreground hover:text-foreground hover:bg-surface-3 focus-visible:ring-ring flex size-5 shrink-0 items-center justify-center rounded transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
|
202
207
|
>
|
|
203
|
-
<XIcon
|
|
208
|
+
<XIcon size={12} />
|
|
204
209
|
</button>
|
|
205
210
|
</li>
|
|
206
211
|
{/each}
|
|
@@ -214,11 +219,11 @@
|
|
|
214
219
|
onkeydown={keydown}
|
|
215
220
|
rows="1"
|
|
216
221
|
disabled={running}
|
|
217
|
-
placeholder={running ?
|
|
218
|
-
class="
|
|
222
|
+
placeholder={running ? words.answeringPlaceholder : words.askPlaceholder}
|
|
223
|
+
class="ds-lib-input"
|
|
219
224
|
></textarea>
|
|
220
225
|
|
|
221
|
-
<div class="
|
|
226
|
+
<div class="ds-lib-controls">
|
|
222
227
|
{#if attachments}
|
|
223
228
|
<input
|
|
224
229
|
bind:this={picker}
|
|
@@ -226,75 +231,287 @@
|
|
|
226
231
|
multiple
|
|
227
232
|
accept={ACCEPT_ATTRIBUTE}
|
|
228
233
|
onchange={pick}
|
|
229
|
-
class="
|
|
234
|
+
class="ds-lib-picker"
|
|
230
235
|
tabindex="-1"
|
|
231
236
|
aria-hidden="true"
|
|
232
237
|
/>
|
|
233
238
|
<button
|
|
234
239
|
type="button"
|
|
240
|
+
class="ds-lib-icon-button"
|
|
235
241
|
onclick={() => picker?.click()}
|
|
236
242
|
disabled={running || files.length >= MAX_FILES}
|
|
237
243
|
aria-label="Attach a file"
|
|
238
|
-
class="text-muted-foreground hover:text-foreground hover:bg-surface-2 focus-visible:ring-ring flex size-8 shrink-0 items-center justify-center rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30"
|
|
239
244
|
>
|
|
240
|
-
<PaperclipIcon
|
|
245
|
+
<PaperclipIcon size={16} />
|
|
241
246
|
</button>
|
|
242
247
|
{/if}
|
|
243
248
|
|
|
244
249
|
{#if onbriefing}
|
|
245
250
|
<button
|
|
246
251
|
type="button"
|
|
252
|
+
class="ds-lib-icon-button"
|
|
247
253
|
onclick={onbriefing}
|
|
248
254
|
disabled={running}
|
|
249
255
|
aria-label="Ask for a briefing"
|
|
250
256
|
title="Ask for a briefing"
|
|
251
|
-
class="text-muted-foreground hover:text-foreground hover:bg-surface-2 focus-visible:ring-ring flex size-8 shrink-0 items-center justify-center rounded-full transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:opacity-30"
|
|
252
257
|
>
|
|
253
|
-
<FileTextIcon
|
|
258
|
+
<FileTextIcon size={16} />
|
|
254
259
|
</button>
|
|
255
260
|
{/if}
|
|
256
261
|
|
|
257
262
|
{#if hasChoice}
|
|
258
|
-
<div class="
|
|
263
|
+
<div class="ds-lib-scopes">
|
|
259
264
|
{#each choices as choice (choice.id)}
|
|
260
265
|
<button
|
|
261
266
|
type="button"
|
|
267
|
+
class="ds-lib-scope-chip"
|
|
268
|
+
class:is-chosen={scope === choice.id}
|
|
262
269
|
onclick={() => onscope(choice.id)}
|
|
263
|
-
class="max-w-full truncate rounded-full px-2 py-0.5 text-xs transition-colors {scope ===
|
|
264
|
-
choice.id
|
|
265
|
-
? 'bg-primary/15 text-foreground border-primary/40 border'
|
|
266
|
-
: 'text-muted-foreground hover:text-foreground border border-transparent'}"
|
|
267
270
|
>
|
|
268
271
|
{choice.label}
|
|
269
272
|
</button>
|
|
270
273
|
{/each}
|
|
271
274
|
</div>
|
|
272
275
|
{/if}
|
|
273
|
-
<span class="
|
|
276
|
+
<span class="ds-lib-spacer"></span>
|
|
274
277
|
{#if running}
|
|
275
|
-
<button
|
|
276
|
-
|
|
277
|
-
onclick={onstop}
|
|
278
|
-
aria-label="Stop"
|
|
279
|
-
class="bg-foreground text-background hover:bg-foreground/90 flex size-8 items-center justify-center rounded-full transition-colors"
|
|
280
|
-
>
|
|
281
|
-
<SquareIcon class="size-3.5 fill-current" />
|
|
278
|
+
<button type="button" class="ds-lib-stop" onclick={onstop} aria-label="Stop">
|
|
279
|
+
<SquareIcon size={14} fill="currentColor" />
|
|
282
280
|
</button>
|
|
283
281
|
{:else}
|
|
284
282
|
<button
|
|
285
283
|
type="button"
|
|
284
|
+
class="ds-lib-send"
|
|
286
285
|
onclick={submit}
|
|
287
286
|
disabled={!value.trim()}
|
|
288
287
|
aria-label="Send"
|
|
289
|
-
class="bg-primary text-primary-foreground hover:bg-primary/90 disabled:hover:bg-primary flex size-8 items-center justify-center rounded-full transition-all disabled:opacity-30"
|
|
290
288
|
>
|
|
291
|
-
<ArrowUpIcon
|
|
289
|
+
<ArrowUpIcon size={16} />
|
|
292
290
|
</button>
|
|
293
291
|
{/if}
|
|
294
292
|
</div>
|
|
295
293
|
</div>
|
|
296
294
|
|
|
297
295
|
{#if rejected}
|
|
298
|
-
<p class="
|
|
296
|
+
<p class="ds-lib-rejected" role="status" aria-live="polite">{rejected}</p>
|
|
299
297
|
{/if}
|
|
300
298
|
</div>
|
|
299
|
+
|
|
300
|
+
<style>
|
|
301
|
+
.ds-lib-composer-lift {
|
|
302
|
+
transition: transform 150ms ease;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.ds-lib-box {
|
|
306
|
+
border: 1px solid var(--ds-color-border);
|
|
307
|
+
border-radius: var(--ds-radius-xl);
|
|
308
|
+
background: var(--ds-color-surface-1);
|
|
309
|
+
transition: border-color 150ms ease;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.ds-lib-box:focus-within {
|
|
313
|
+
border-color: color-mix(in oklab, var(--ds-color-primary) 60%, transparent);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.ds-lib-files {
|
|
317
|
+
display: flex;
|
|
318
|
+
flex-wrap: wrap;
|
|
319
|
+
gap: 0.375rem;
|
|
320
|
+
margin: 0;
|
|
321
|
+
padding: 0.75rem 0.75rem 0;
|
|
322
|
+
list-style: none;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.ds-lib-file {
|
|
326
|
+
display: flex;
|
|
327
|
+
max-width: 100%;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: 0.375rem;
|
|
330
|
+
border: 1px solid var(--ds-color-border);
|
|
331
|
+
border-radius: var(--ds-radius-lg);
|
|
332
|
+
background: var(--ds-color-surface-2);
|
|
333
|
+
padding: 0.25rem 0.25rem 0.25rem 0.5rem;
|
|
334
|
+
font-size: var(--ds-text-2xs);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.ds-lib-file-name {
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
text-overflow: ellipsis;
|
|
340
|
+
white-space: nowrap;
|
|
341
|
+
color: var(--ds-color-foreground);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.ds-lib-file-size {
|
|
345
|
+
flex: none;
|
|
346
|
+
color: var(--ds-color-muted-foreground);
|
|
347
|
+
font-variant-numeric: tabular-nums;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ds-lib-file-remove {
|
|
351
|
+
display: flex;
|
|
352
|
+
width: 1.25rem;
|
|
353
|
+
height: 1.25rem;
|
|
354
|
+
flex: none;
|
|
355
|
+
align-items: center;
|
|
356
|
+
justify-content: center;
|
|
357
|
+
border: 0;
|
|
358
|
+
border-radius: var(--ds-radius-sm);
|
|
359
|
+
background: none;
|
|
360
|
+
color: var(--ds-color-muted-foreground);
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
transition:
|
|
363
|
+
color 150ms ease,
|
|
364
|
+
background-color 150ms ease;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.ds-lib-file-remove:hover {
|
|
368
|
+
background: var(--ds-color-surface-3);
|
|
369
|
+
color: var(--ds-color-foreground);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.ds-lib-input {
|
|
373
|
+
display: block;
|
|
374
|
+
width: 100%;
|
|
375
|
+
resize: none;
|
|
376
|
+
border: 0;
|
|
377
|
+
background: transparent;
|
|
378
|
+
padding: 0.75rem 1rem 0.5rem;
|
|
379
|
+
color: var(--ds-color-foreground);
|
|
380
|
+
font: inherit;
|
|
381
|
+
font-size: 1rem;
|
|
382
|
+
outline: none;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.ds-lib-input::placeholder {
|
|
386
|
+
color: var(--ds-color-muted-foreground);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.ds-lib-input:disabled {
|
|
390
|
+
opacity: 0.6;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.ds-lib-controls {
|
|
394
|
+
display: flex;
|
|
395
|
+
align-items: center;
|
|
396
|
+
gap: 0.5rem;
|
|
397
|
+
padding: 0 0.75rem 0.625rem;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.ds-lib-picker {
|
|
401
|
+
display: none;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.ds-lib-icon-button,
|
|
405
|
+
.ds-lib-stop,
|
|
406
|
+
.ds-lib-send {
|
|
407
|
+
display: flex;
|
|
408
|
+
width: 2rem;
|
|
409
|
+
height: 2rem;
|
|
410
|
+
flex: none;
|
|
411
|
+
align-items: center;
|
|
412
|
+
justify-content: center;
|
|
413
|
+
border: 0;
|
|
414
|
+
border-radius: var(--ds-radius-full);
|
|
415
|
+
cursor: pointer;
|
|
416
|
+
transition:
|
|
417
|
+
color 150ms ease,
|
|
418
|
+
background-color 150ms ease,
|
|
419
|
+
opacity 150ms ease;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.ds-lib-icon-button {
|
|
423
|
+
background: none;
|
|
424
|
+
color: var(--ds-color-muted-foreground);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.ds-lib-icon-button:hover:not(:disabled) {
|
|
428
|
+
background: var(--ds-color-surface-2);
|
|
429
|
+
color: var(--ds-color-foreground);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.ds-lib-icon-button:disabled,
|
|
433
|
+
.ds-lib-send:disabled {
|
|
434
|
+
cursor: default;
|
|
435
|
+
opacity: 0.3;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.ds-lib-icon-button:focus-visible,
|
|
439
|
+
.ds-lib-stop:focus-visible,
|
|
440
|
+
.ds-lib-send:focus-visible,
|
|
441
|
+
.ds-lib-scope-chip:focus-visible,
|
|
442
|
+
.ds-lib-file-remove:focus-visible {
|
|
443
|
+
outline: 2px solid var(--ds-color-ring);
|
|
444
|
+
outline-offset: 2px;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
.ds-lib-stop {
|
|
448
|
+
background: var(--ds-color-foreground);
|
|
449
|
+
color: var(--ds-color-background);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.ds-lib-send {
|
|
453
|
+
background: var(--ds-color-primary);
|
|
454
|
+
color: var(--ds-color-primary-foreground);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.ds-lib-send:hover:not(:disabled),
|
|
458
|
+
.ds-lib-stop:hover {
|
|
459
|
+
opacity: 0.9;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/* Three chips in a narrow column clipped all three to fragments
|
|
463
|
+
("defence-s…", "All colle…"). Wrapping beats truncating: a chip a reader
|
|
464
|
+
cannot finish reading is not a control, it is decoration. */
|
|
465
|
+
.ds-lib-scopes {
|
|
466
|
+
display: flex;
|
|
467
|
+
min-width: 0;
|
|
468
|
+
flex-wrap: wrap;
|
|
469
|
+
align-items: center;
|
|
470
|
+
gap: 0.25rem;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.ds-lib-scope-chip {
|
|
474
|
+
max-width: 100%;
|
|
475
|
+
overflow: hidden;
|
|
476
|
+
border: 1px solid transparent;
|
|
477
|
+
border-radius: var(--ds-radius-full);
|
|
478
|
+
background: none;
|
|
479
|
+
padding: 0.125rem 0.5rem;
|
|
480
|
+
color: var(--ds-color-muted-foreground);
|
|
481
|
+
font: inherit;
|
|
482
|
+
font-size: var(--ds-text-2xs);
|
|
483
|
+
text-overflow: ellipsis;
|
|
484
|
+
white-space: nowrap;
|
|
485
|
+
cursor: pointer;
|
|
486
|
+
transition:
|
|
487
|
+
color 150ms ease,
|
|
488
|
+
background-color 150ms ease;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.ds-lib-scope-chip:hover {
|
|
492
|
+
color: var(--ds-color-foreground);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.ds-lib-scope-chip.is-chosen {
|
|
496
|
+
border-color: color-mix(in oklab, var(--ds-color-primary) 40%, transparent);
|
|
497
|
+
background: color-mix(in oklab, var(--ds-color-primary) 15%, transparent);
|
|
498
|
+
color: var(--ds-color-foreground);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.ds-lib-spacer {
|
|
502
|
+
flex: 1;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.ds-lib-rejected {
|
|
506
|
+
margin: 0;
|
|
507
|
+
padding: 0.375rem 0.25rem 0;
|
|
508
|
+
color: var(--ds-color-status-error);
|
|
509
|
+
font-size: var(--ds-text-2xs);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
@media (prefers-reduced-motion: reduce) {
|
|
513
|
+
.ds-lib-composer-lift {
|
|
514
|
+
transition: none;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
</style>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LibrarianCopy } from '../../copy';
|
|
1
2
|
export type Scope = 'document' | 'collection' | 'library';
|
|
2
3
|
interface Props {
|
|
3
4
|
value: string;
|
|
@@ -17,6 +18,11 @@ interface Props {
|
|
|
17
18
|
* control renders — studying is a mode of asking, never a tab, so a
|
|
18
19
|
* host with nothing to build offers none rather than a disabled one. */
|
|
19
20
|
onbriefing?: () => void;
|
|
21
|
+
/** Who is being asked. A slug is fine — `penny` renders as "Penny" — and
|
|
22
|
+
* both placeholders are composed from it. */
|
|
23
|
+
name?: string;
|
|
24
|
+
/** Overrides for the package's own words. */
|
|
25
|
+
copy?: Partial<LibrarianCopy>;
|
|
20
26
|
}
|
|
21
27
|
declare const Composer: import("svelte").Component<Props, {}, "value" | "files">;
|
|
22
28
|
type Composer = ReturnType<typeof Composer>;
|