@poodle64/librarian 2026.9.12 → 2026.9.14
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/components/agent-transcript/agent-transcript.svelte +8 -2
- package/dist/components/composer/composer.svelte +5 -2
- package/dist/components/composer/composer.svelte.d.ts +2 -0
- package/dist/components/conversation/conversation.svelte +40 -6
- package/dist/components/conversation/conversation.svelte.d.ts +3 -0
- package/dist/copy.d.ts +3 -0
- package/dist/copy.js +1 -0
- package/package.json +1 -1
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
trustMark,
|
|
26
26
|
type Citation
|
|
27
27
|
} from '../../citations';
|
|
28
|
-
import { resolveCopy, type LibrarianCopy } from '../../copy';
|
|
28
|
+
import { DEFAULT_COPY, resolveCopy, type LibrarianCopy } from '../../copy';
|
|
29
29
|
import Working from '../working/working.svelte';
|
|
30
30
|
import ActivityGroup from '../activity-group/activity-group.svelte';
|
|
31
31
|
import ArtefactCard from '../artefact-card/artefact-card.svelte';
|
|
@@ -133,9 +133,15 @@
|
|
|
133
133
|
* a duration badge, and — worse — the "he holds nothing on this" line,
|
|
134
134
|
* which is a claim about the shelf made off a run that never finished
|
|
135
135
|
* looking.
|
|
136
|
+
*
|
|
137
|
+
* `error` still carries `DEFAULT_COPY.unreachable` verbatim from `client.ts`
|
|
138
|
+
* — the stream layer has no `name` to compose with — so that exact default
|
|
139
|
+
* is swapped for `words.unreachable`, which `Conversation`'s `name` prop
|
|
140
|
+
* (or a host's own `copy` override) already resolved.
|
|
136
141
|
*/
|
|
137
142
|
const failure = $derived(
|
|
138
|
-
outcome?.error
|
|
143
|
+
(outcome?.error === DEFAULT_COPY.unreachable ? words.unreachable : outcome?.error) ??
|
|
144
|
+
(outcome?.isError ? words.answerFailed : null)
|
|
139
145
|
);
|
|
140
146
|
|
|
141
147
|
// A failed turn settles too, and "Ask again" is the one thing a reader wants
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
* control renders — studying is a mode of asking, never a tab, so a
|
|
45
45
|
* host with nothing to build offers none rather than a disabled one. */
|
|
46
46
|
onbriefing?: () => void;
|
|
47
|
+
/** The persona's display name; the placeholder in both states takes it. */
|
|
48
|
+
name?: string;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
let {
|
|
@@ -57,7 +59,8 @@
|
|
|
57
59
|
onscope,
|
|
58
60
|
onsubmit,
|
|
59
61
|
onstop,
|
|
60
|
-
onbriefing
|
|
62
|
+
onbriefing,
|
|
63
|
+
name = 'Milton'
|
|
61
64
|
}: Props = $props();
|
|
62
65
|
|
|
63
66
|
// What Milton is being asked about — this document, this collection, or
|
|
@@ -214,7 +217,7 @@
|
|
|
214
217
|
onkeydown={keydown}
|
|
215
218
|
rows="1"
|
|
216
219
|
disabled={running}
|
|
217
|
-
placeholder={running ?
|
|
220
|
+
placeholder={running ? `${name} is answering…` : `Ask ${name}…`}
|
|
218
221
|
class="text-foreground placeholder:text-muted-foreground w-full resize-none bg-transparent px-4 pt-3 pb-2 text-base outline-none disabled:opacity-60"
|
|
219
222
|
></textarea>
|
|
220
223
|
|
|
@@ -17,6 +17,8 @@ interface Props {
|
|
|
17
17
|
* control renders — studying is a mode of asking, never a tab, so a
|
|
18
18
|
* host with nothing to build offers none rather than a disabled one. */
|
|
19
19
|
onbriefing?: () => void;
|
|
20
|
+
/** The persona's display name; the placeholder in both states takes it. */
|
|
21
|
+
name?: string;
|
|
20
22
|
}
|
|
21
23
|
declare const Composer: import("svelte").Component<Props, {}, "value" | "files">;
|
|
22
24
|
type Composer = ReturnType<typeof Composer>;
|
|
@@ -31,6 +31,9 @@
|
|
|
31
31
|
/** `Transcript.version` — bumped per event. Text grows in place, so a
|
|
32
32
|
* count of turns is not enough to keep the scroll following. */
|
|
33
33
|
version?: number;
|
|
34
|
+
/** The persona's display name. The aria-label, the `welcome` default and
|
|
35
|
+
* the unreachable alert all take it. */
|
|
36
|
+
name?: string;
|
|
34
37
|
welcome?: string;
|
|
35
38
|
/** Up to three, shown in the empty state as tappable pills. */
|
|
36
39
|
examples?: string[];
|
|
@@ -59,7 +62,8 @@
|
|
|
59
62
|
turns,
|
|
60
63
|
running,
|
|
61
64
|
version = 0,
|
|
62
|
-
|
|
65
|
+
name = 'Milton',
|
|
66
|
+
welcome = `Ask ${name} a question about the library.`,
|
|
63
67
|
examples = [],
|
|
64
68
|
onexample,
|
|
65
69
|
scope,
|
|
@@ -73,6 +77,12 @@
|
|
|
73
77
|
|
|
74
78
|
const words = $derived(resolveCopy(copy));
|
|
75
79
|
|
|
80
|
+
// A host's own `copy.unreachable` still wins: it comes last in the spread.
|
|
81
|
+
const effectiveCopy = $derived<Partial<LibrarianCopy>>({
|
|
82
|
+
unreachable: `${name} can't be reached right now.`,
|
|
83
|
+
...copy
|
|
84
|
+
});
|
|
85
|
+
|
|
76
86
|
let viewport = $state<HTMLElement | null>(null);
|
|
77
87
|
let column = $state<Column>(null);
|
|
78
88
|
const follow = new FollowScroll();
|
|
@@ -107,6 +117,25 @@
|
|
|
107
117
|
viewport.scrollTop = viewport.scrollHeight;
|
|
108
118
|
});
|
|
109
119
|
|
|
120
|
+
// Asking for a briefing puts its card in the transcript AND opens the
|
|
121
|
+
// artefact in the column, closing whatever document was there — a
|
|
122
|
+
// colleague who just asked for one is asking to read it, not merely to
|
|
123
|
+
// see that it exists. Fires on the RUNNING → settled transition only, so
|
|
124
|
+
// reopening a past conversation whose last turn happens to be an
|
|
125
|
+
// artefact does not reopen a pane the reader may have since closed.
|
|
126
|
+
//
|
|
127
|
+
// Plain, not `$state`: read and written in the same effect, which would
|
|
128
|
+
// otherwise re-trigger itself forever (the pattern this file's `seen`
|
|
129
|
+
// already uses for the same reason).
|
|
130
|
+
let wasRunning = false;
|
|
131
|
+
$effect(() => {
|
|
132
|
+
const last = turns.at(-1);
|
|
133
|
+
if (wasRunning && !running && last?.kind === 'artefact' && !last.outcome?.isError) {
|
|
134
|
+
column = { kind: 'artefact', turn: last };
|
|
135
|
+
}
|
|
136
|
+
wasRunning = running;
|
|
137
|
+
});
|
|
138
|
+
|
|
110
139
|
function scrolled() {
|
|
111
140
|
if (viewport) follow.measure(metrics(viewport));
|
|
112
141
|
}
|
|
@@ -141,7 +170,7 @@
|
|
|
141
170
|
role="log"
|
|
142
171
|
aria-live="polite"
|
|
143
172
|
aria-busy={running}
|
|
144
|
-
aria-label="Conversation with
|
|
173
|
+
aria-label="Conversation with {name}"
|
|
145
174
|
class="min-h-0 flex-1 overflow-y-auto overscroll-contain"
|
|
146
175
|
>
|
|
147
176
|
<div class="mx-auto flex w-full max-w-[46rem] flex-col gap-8 px-4 py-6">
|
|
@@ -171,7 +200,7 @@
|
|
|
171
200
|
sit at the top of the same column so a reader meets them in
|
|
172
201
|
the same order whether or not they have asked anything yet. -->
|
|
173
202
|
{#if scope}
|
|
174
|
-
<ScopeStatement statement={scope} expanded={turns.length === 0} {
|
|
203
|
+
<ScopeStatement statement={scope} expanded={turns.length === 0} copy={effectiveCopy} />
|
|
175
204
|
{/if}
|
|
176
205
|
|
|
177
206
|
{#each turns as turn, index (turn.id)}
|
|
@@ -183,7 +212,7 @@
|
|
|
183
212
|
citations={turn.citations ?? []}
|
|
184
213
|
suggestions={turn.suggestions ?? []}
|
|
185
214
|
{collectionNames}
|
|
186
|
-
{
|
|
215
|
+
copy={effectiveCopy}
|
|
187
216
|
kind={turn.kind}
|
|
188
217
|
title={turn.title}
|
|
189
218
|
onopenartefact={() => openArtefact(turn)}
|
|
@@ -216,11 +245,16 @@
|
|
|
216
245
|
</div>
|
|
217
246
|
|
|
218
247
|
{#if column?.kind === 'document' && loadDocument}
|
|
219
|
-
<DocumentPane
|
|
248
|
+
<DocumentPane
|
|
249
|
+
citation={column.citation}
|
|
250
|
+
{loadDocument}
|
|
251
|
+
copy={effectiveCopy}
|
|
252
|
+
onclose={() => (column = null)}
|
|
253
|
+
/>
|
|
220
254
|
{:else if column?.kind === 'artefact'}
|
|
221
255
|
<ArtefactPane
|
|
222
256
|
turn={column.turn}
|
|
223
|
-
{
|
|
257
|
+
copy={effectiveCopy}
|
|
224
258
|
onclose={() => (column = null)}
|
|
225
259
|
oncite={(citation) => cite(citation)}
|
|
226
260
|
/>
|
|
@@ -8,6 +8,9 @@ interface Props {
|
|
|
8
8
|
/** `Transcript.version` — bumped per event. Text grows in place, so a
|
|
9
9
|
* count of turns is not enough to keep the scroll following. */
|
|
10
10
|
version?: number;
|
|
11
|
+
/** The persona's display name. The aria-label, the `welcome` default and
|
|
12
|
+
* the unreachable alert all take it. */
|
|
13
|
+
name?: string;
|
|
11
14
|
welcome?: string;
|
|
12
15
|
/** Up to three, shown in the empty state as tappable pills. */
|
|
13
16
|
examples?: string[];
|
package/dist/copy.d.ts
CHANGED
|
@@ -21,6 +21,9 @@ export interface LibrarianCopy {
|
|
|
21
21
|
notHeld: string;
|
|
22
22
|
/** Shown when the run itself ended in error and said nothing about why. */
|
|
23
23
|
answerFailed: string;
|
|
24
|
+
/** Shown when the stream never opened at all. `Conversation`'s `name`
|
|
25
|
+
* prop composes this default; a host overriding `copy` directly wins. */
|
|
26
|
+
unreachable: string;
|
|
24
27
|
/** Heading over the list of what an answer cited. */
|
|
25
28
|
sources: string;
|
|
26
29
|
/** Heading over the follow-up questions the librarian offered. */
|
package/dist/copy.js
CHANGED
|
@@ -23,6 +23,7 @@ export const DEFAULT_COPY = {
|
|
|
23
23
|
// A run can end `is_error` carrying no message at all, and a turn that
|
|
24
24
|
// simply stops is the one thing a reader must not have to guess about.
|
|
25
25
|
answerFailed: 'Milton stopped before he finished this one.',
|
|
26
|
+
unreachable: "Milton can't be reached right now.",
|
|
26
27
|
sources: 'Sources',
|
|
27
28
|
suggestions: 'Ask next',
|
|
28
29
|
scope: 'What Milton answers from',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poodle64/librarian",
|
|
3
|
-
"version": "2026.9.
|
|
3
|
+
"version": "2026.9.14",
|
|
4
4
|
"description": "Milton's conversation surface as a consumable Svelte 5 package: the stream client, transcript state and chat components (transcript, composer, markdown) every household app renders instead of rebuilding.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|