@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.
Files changed (28) hide show
  1. package/README.md +77 -19
  2. package/dist/client.d.ts +16 -1
  3. package/dist/client.js +55 -8
  4. package/dist/components/activity-group/activity-group.svelte +76 -8
  5. package/dist/components/agent-transcript/agent-transcript.svelte +346 -150
  6. package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +7 -0
  7. package/dist/components/artefact-card/artefact-card.svelte +30 -2
  8. package/dist/components/artefact-pane/artefact-pane.svelte +141 -50
  9. package/dist/components/composer/composer.svelte +255 -38
  10. package/dist/components/composer/composer.svelte.d.ts +6 -0
  11. package/dist/components/conversation/conversation.svelte +254 -81
  12. package/dist/components/conversation/conversation.svelte.d.ts +7 -0
  13. package/dist/components/document-pane/document-pane.svelte +216 -33
  14. package/dist/components/markdown/markdown.svelte +73 -24
  15. package/dist/components/scope-statement/scope-statement.svelte +79 -12
  16. package/dist/components/source-list/index.d.ts +2 -0
  17. package/dist/components/source-list/index.js +2 -0
  18. package/dist/components/source-list/source-list.svelte +137 -0
  19. package/dist/components/source-list/source-list.svelte.d.ts +13 -0
  20. package/dist/components/thinking-row/thinking-row.svelte +69 -18
  21. package/dist/components/tool-row/tool-row.svelte +132 -26
  22. package/dist/components/working/working.svelte +51 -7
  23. package/dist/components/working/working.svelte.d.ts +3 -0
  24. package/dist/copy.d.ts +45 -10
  25. package/dist/copy.js +77 -32
  26. package/dist/transcript.svelte.d.ts +10 -0
  27. package/dist/transcript.svelte.js +4 -1
  28. package/package.json +4 -2
@@ -0,0 +1,137 @@
1
+ <!--
2
+ What an answer cited, as a list a reader can open.
3
+
4
+ One component rather than the same twenty lines in the transcript and again
5
+ in the artefact pane: the trust mark, the number chip and the disabled state
6
+ of a citation with no document behind it are one decision, and two copies of
7
+ it is two places for them to drift apart.
8
+ -->
9
+ <script lang="ts">
10
+ import { trustMark, type Citation } from '../../citations';
11
+ import type { LibrarianCopy } from '../../copy';
12
+
13
+ interface Props {
14
+ sources: Citation[];
15
+ words: LibrarianCopy;
16
+ /** Opens the document. Omit and the rows list but do not open. */
17
+ oncite?: (citation: Citation) => void;
18
+ /** `h2` in a transcript turn, `h3` under the artefact's own title. */
19
+ level?: 2 | 3;
20
+ }
21
+
22
+ let { sources, words, oncite, level = 2 }: Props = $props();
23
+ </script>
24
+
25
+ <section class="ds-lib-sources">
26
+ {#if level === 2}
27
+ <h2 class="ds-lib-sources-heading">{words.sources}</h2>
28
+ {:else}
29
+ <h3 class="ds-lib-sources-heading">{words.sources}</h3>
30
+ {/if}
31
+ <ol class="ds-lib-sources-list">
32
+ {#each sources as source (source.n)}
33
+ {@const mark = trustMark(source, words)}
34
+ <li>
35
+ <button
36
+ type="button"
37
+ class="ds-lib-source"
38
+ onclick={() => oncite?.(source)}
39
+ disabled={!source.document_id || !oncite}
40
+ >
41
+ <span class="ds-lib-source-n">{source.n}</span>
42
+ <span class="ds-lib-source-text">
43
+ <span class="ds-lib-source-title">{source.title}</span>
44
+ {#if source.section}<span class="ds-lib-source-section"> · {source.section}</span>{/if}
45
+ <!-- Same muted register as the section, deliberately. An
46
+ unverified source is not an error and must not be dressed
47
+ as one; the words carry the difference, and a red one
48
+ would have a colleague discount a document that is
49
+ simply new. -->
50
+ {#if mark}<span class="ds-lib-source-mark"> · {mark}</span>{/if}
51
+ </span>
52
+ </button>
53
+ </li>
54
+ {/each}
55
+ </ol>
56
+ </section>
57
+
58
+ <style>
59
+ .ds-lib-sources-heading {
60
+ margin: 0 0 0.375rem;
61
+ color: var(--ds-color-muted-foreground);
62
+ font-family: inherit;
63
+ font-size: var(--ds-text-2xs);
64
+ font-weight: 500;
65
+ letter-spacing: 0.05em;
66
+ text-transform: uppercase;
67
+ }
68
+
69
+ .ds-lib-sources-list {
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 0.25rem;
73
+ margin: 0;
74
+ padding: 0;
75
+ list-style: none;
76
+ }
77
+
78
+ .ds-lib-source {
79
+ display: flex;
80
+ width: 100%;
81
+ align-items: baseline;
82
+ gap: 0.5rem;
83
+ border: 1px solid var(--ds-color-border);
84
+ border-radius: var(--ds-radius-lg);
85
+ background: none;
86
+ padding: 0.375rem 0.625rem;
87
+ color: inherit;
88
+ font: inherit;
89
+ font-size: 0.875rem;
90
+ text-align: start;
91
+ cursor: pointer;
92
+ transition:
93
+ background-color 150ms ease,
94
+ border-color 150ms ease;
95
+ }
96
+
97
+ .ds-lib-source:hover:not(:disabled) {
98
+ border-color: var(--ds-color-border-strong);
99
+ background: var(--ds-color-surface-2);
100
+ }
101
+
102
+ .ds-lib-source:focus-visible {
103
+ outline: 2px solid var(--ds-color-ring);
104
+ outline-offset: 2px;
105
+ }
106
+
107
+ .ds-lib-source:disabled {
108
+ cursor: default;
109
+ }
110
+
111
+ .ds-lib-source-n {
112
+ flex: none;
113
+ border-radius: var(--ds-radius-sm);
114
+ background: color-mix(in oklab, var(--ds-color-primary) 15%, transparent);
115
+ padding-inline: 0.375rem;
116
+ color: var(--ds-color-foreground);
117
+ font-family: var(--ds-font-code);
118
+ font-size: var(--ds-text-2xs);
119
+ font-variant-numeric: tabular-nums;
120
+ }
121
+
122
+ .ds-lib-source-text {
123
+ min-width: 0;
124
+ }
125
+
126
+ .ds-lib-source-title {
127
+ color: var(--ds-color-foreground);
128
+ }
129
+
130
+ .ds-lib-source-section {
131
+ color: var(--ds-color-muted-foreground);
132
+ }
133
+
134
+ .ds-lib-source-mark {
135
+ color: color-mix(in oklab, var(--ds-color-muted-foreground) 80%, transparent);
136
+ }
137
+ </style>
@@ -0,0 +1,13 @@
1
+ import { type Citation } from '../../citations';
2
+ import type { LibrarianCopy } from '../../copy';
3
+ interface Props {
4
+ sources: Citation[];
5
+ words: LibrarianCopy;
6
+ /** Opens the document. Omit and the rows list but do not open. */
7
+ oncite?: (citation: Citation) => void;
8
+ /** `h2` in a transcript turn, `h3` under the artefact's own title. */
9
+ level?: 2 | 3;
10
+ }
11
+ declare const SourceList: import("svelte").Component<Props, {}, "">;
12
+ type SourceList = ReturnType<typeof SourceList>;
13
+ export default SourceList;
@@ -1,13 +1,13 @@
1
1
  <!--
2
- One line of Milton's working-out, behind a chevron.
2
+ One line of the persona's working-out, behind a chevron.
3
3
 
4
4
  Two different blocks render as this row and deliberately read the same. A
5
- real `thinking` block is one; the other is a `text` block Milton wrote
6
- BETWEEN two tool calls — "Let me also check whether…" — which the caller
7
- stream gives no way to tell from the answer except by position, and which
8
- read as answer prose on production until `segment()` started re-homing it
9
- here. To a reader both are the same thing: what he was working through, not
10
- what he concluded. So both fold, and both fold by default.
5
+ real `thinking` block is one; the other is a `text` block written BETWEEN two
6
+ tool calls — "Let me also check whether…" — which the caller stream gives no
7
+ way to tell from the answer except by position, and which read as answer
8
+ prose on production until `segment()` started re-homing it here. To a reader
9
+ both are the same thing: what was being worked through, not what was
10
+ concluded. So both fold, and both fold by default.
11
11
  -->
12
12
  <script lang="ts">
13
13
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
@@ -22,25 +22,76 @@
22
22
  let open = $state(false);
23
23
  </script>
24
24
 
25
- <div class="text-sm">
25
+ <div class="ds-lib-thinking">
26
26
  <button
27
27
  type="button"
28
- class="flex items-center gap-2 py-0.5 text-left"
28
+ class="ds-lib-thinking-row"
29
29
  onclick={() => (open = !open)}
30
30
  aria-expanded={open}
31
31
  >
32
- <ChevronRightIcon
33
- class="text-muted-foreground size-3 transition-transform {open ? 'rotate-90' : ''}"
34
- />
35
- <span class={active ? 'text-foreground font-medium' : 'text-muted-foreground'}>
32
+ <span class="ds-lib-thinking-chevron" class:is-open={open}>
33
+ <ChevronRightIcon size={12} />
34
+ </span>
35
+ <span class="ds-lib-thinking-label" class:is-active={active}>
36
36
  {active ? 'Thinking…' : 'Thought'}
37
37
  </span>
38
38
  </button>
39
39
  {#if open}
40
- <p
41
- class="text-muted-foreground border-border mt-1 ml-1.25 border-l pl-3 text-sm whitespace-pre-wrap"
42
- >
43
- {block.text}
44
- </p>
40
+ <p class="ds-lib-thinking-text">{block.text}</p>
45
41
  {/if}
46
42
  </div>
43
+
44
+ <style>
45
+ .ds-lib-thinking {
46
+ font-size: 0.875rem;
47
+ line-height: 1.25rem;
48
+ }
49
+
50
+ .ds-lib-thinking-row {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 0.5rem;
54
+ padding-block: 0.125rem;
55
+ border: 0;
56
+ background: none;
57
+ color: inherit;
58
+ font: inherit;
59
+ text-align: start;
60
+ cursor: pointer;
61
+ }
62
+
63
+ .ds-lib-thinking-chevron {
64
+ display: flex;
65
+ flex: none;
66
+ color: var(--ds-color-muted-foreground);
67
+ transition: transform 150ms ease;
68
+ }
69
+
70
+ .ds-lib-thinking-chevron.is-open {
71
+ transform: rotate(90deg);
72
+ }
73
+
74
+ .ds-lib-thinking-label {
75
+ color: var(--ds-color-muted-foreground);
76
+ }
77
+
78
+ .ds-lib-thinking-label.is-active {
79
+ color: var(--ds-color-foreground);
80
+ font-weight: 500;
81
+ }
82
+
83
+ .ds-lib-thinking-text {
84
+ margin: 0.25rem 0 0;
85
+ margin-inline-start: 0.3125rem;
86
+ border-inline-start: 1px solid var(--ds-color-border);
87
+ padding-inline-start: 0.75rem;
88
+ color: var(--ds-color-muted-foreground);
89
+ white-space: pre-wrap;
90
+ }
91
+
92
+ @media (prefers-reduced-motion: reduce) {
93
+ .ds-lib-thinking-chevron {
94
+ transition: none;
95
+ }
96
+ }
97
+ </style>
@@ -5,9 +5,9 @@
5
5
  dimmed sub-line. Borders and badge pills make a run of five calls read as
6
6
  five separate events rather than one train of thought.
7
7
 
8
- The row says what Milton DID, in a reader's own words — never the tool's
9
- name or the raw command it ran. Expanding a settled row shows what came
10
- back, never what was typed.
8
+ The row says what the persona DID, in a reader's own words — never the
9
+ tool's name or the raw command it ran. Expanding a settled row shows what
10
+ came back, never what was typed.
11
11
  -->
12
12
  <script lang="ts">
13
13
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
@@ -26,46 +26,152 @@
26
26
 
27
27
  const settled = $derived(block.result !== undefined);
28
28
  const said = $derived(describe(block));
29
- const tone = $derived(
30
- block.isError ? 'bg-status-error' : settled ? 'bg-status-success' : 'bg-status-info'
31
- );
29
+ const tone = $derived(block.isError ? 'error' : settled ? 'success' : 'info');
32
30
  const lines = $derived(block.result ? block.result.split('\n').length : 0);
33
31
  </script>
34
32
 
35
- <div class="text-sm">
33
+ <div class="ds-lib-tool">
36
34
  <button
37
35
  type="button"
38
- class="flex w-full items-center gap-2 rounded py-0.5 text-left"
36
+ class="ds-lib-tool-row"
39
37
  onclick={() => (open = !open)}
40
38
  disabled={!settled}
41
39
  aria-expanded={open}
42
40
  >
43
- <span class="flex w-3 shrink-0 items-center justify-center">
44
- {#if settled}
45
- <ChevronRightIcon
46
- class="text-muted-foreground size-3 transition-transform {open ? 'rotate-90' : ''}"
47
- />
48
- {/if}
41
+ <span class="ds-lib-tool-chevron" class:is-open={open}>
42
+ {#if settled}<ChevronRightIcon size={12} />{/if}
49
43
  </span>
50
- <span class="size-1.5 shrink-0 rounded-full {tone} {!settled && running ? 'animate-pulse' : ''}"
51
- ></span>
52
- <span class="text-muted-foreground truncate">
53
- <span class="text-foreground font-medium">{said.verb}</span>
54
- {#if said.object}<span class="text-foreground/80">{said.object}</span>{/if}
55
- {#if repeats > 1}<span class="text-muted-foreground">· {repeats} pages</span>{/if}
44
+ <span class="ds-lib-tool-dot" data-tone={tone} class:is-live={!settled && running}></span>
45
+ <span class="ds-lib-tool-said">
46
+ <span class="ds-lib-tool-verb">{said.verb}</span>
47
+ {#if said.object}<span class="ds-lib-tool-object">{said.object}</span>{/if}
48
+ {#if repeats > 1}<span>· {repeats} pages</span>{/if}
56
49
  </span>
57
50
  </button>
58
51
 
59
52
  {#if open}
60
- <div class="border-border mt-1 ml-5.5 border-l pl-3">
53
+ <div class="ds-lib-tool-body">
61
54
  {#if block.result}
62
- <pre
63
- class="text-muted-foreground mt-1 max-h-72 overflow-auto font-mono text-xs whitespace-pre-wrap">{block.result}</pre>
55
+ <pre class="ds-lib-tool-result">{block.result}</pre>
64
56
  {/if}
65
57
  </div>
66
58
  {:else if settled && lines > 0}
67
- <p class="text-muted-foreground pl-5.5 text-xs">
68
- {lines === 1 ? '1 line' : `${lines} lines`}
69
- </p>
59
+ <p class="ds-lib-tool-lines">{lines === 1 ? '1 line' : `${lines} lines`}</p>
70
60
  {/if}
71
61
  </div>
62
+
63
+ <style>
64
+ .ds-lib-tool {
65
+ font-size: 0.875rem;
66
+ line-height: 1.25rem;
67
+ }
68
+
69
+ .ds-lib-tool-row {
70
+ display: flex;
71
+ width: 100%;
72
+ align-items: center;
73
+ gap: 0.5rem;
74
+ padding-block: 0.125rem;
75
+ border: 0;
76
+ background: none;
77
+ color: inherit;
78
+ font: inherit;
79
+ text-align: start;
80
+ cursor: pointer;
81
+ }
82
+
83
+ .ds-lib-tool-row:disabled {
84
+ cursor: default;
85
+ }
86
+
87
+ .ds-lib-tool-chevron {
88
+ display: flex;
89
+ width: 0.75rem;
90
+ flex: none;
91
+ align-items: center;
92
+ justify-content: center;
93
+ color: var(--ds-color-muted-foreground);
94
+ transition: transform 150ms ease;
95
+ }
96
+
97
+ .ds-lib-tool-chevron.is-open {
98
+ transform: rotate(90deg);
99
+ }
100
+
101
+ .ds-lib-tool-dot {
102
+ width: 0.375rem;
103
+ height: 0.375rem;
104
+ flex: none;
105
+ border-radius: var(--ds-radius-full);
106
+ background: var(--ds-color-status-info);
107
+ }
108
+
109
+ .ds-lib-tool-dot[data-tone='error'] {
110
+ background: var(--ds-color-status-error);
111
+ }
112
+
113
+ .ds-lib-tool-dot[data-tone='success'] {
114
+ background: var(--ds-color-status-success);
115
+ }
116
+
117
+ .ds-lib-tool-dot.is-live {
118
+ animation: ds-lib-tool-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
119
+ }
120
+
121
+ .ds-lib-tool-said {
122
+ min-width: 0;
123
+ overflow: hidden;
124
+ text-overflow: ellipsis;
125
+ white-space: nowrap;
126
+ color: var(--ds-color-muted-foreground);
127
+ }
128
+
129
+ .ds-lib-tool-verb {
130
+ color: var(--ds-color-foreground);
131
+ font-weight: 500;
132
+ }
133
+
134
+ .ds-lib-tool-object {
135
+ color: color-mix(in oklab, var(--ds-color-foreground) 80%, transparent);
136
+ }
137
+
138
+ .ds-lib-tool-body {
139
+ margin-top: 0.25rem;
140
+ margin-inline-start: 1.375rem;
141
+ border-inline-start: 1px solid var(--ds-color-border);
142
+ padding-inline-start: 0.75rem;
143
+ }
144
+
145
+ .ds-lib-tool-result {
146
+ margin-top: 0.25rem;
147
+ max-height: 18rem;
148
+ overflow: auto;
149
+ color: var(--ds-color-muted-foreground);
150
+ font-family: var(--ds-font-code);
151
+ font-size: var(--ds-text-2xs);
152
+ white-space: pre-wrap;
153
+ }
154
+
155
+ .ds-lib-tool-lines {
156
+ margin: 0;
157
+ padding-inline-start: 1.375rem;
158
+ color: var(--ds-color-muted-foreground);
159
+ font-size: var(--ds-text-2xs);
160
+ }
161
+
162
+ @keyframes ds-lib-tool-pulse {
163
+ 50% {
164
+ opacity: 0.4;
165
+ }
166
+ }
167
+
168
+ @media (prefers-reduced-motion: reduce) {
169
+ .ds-lib-tool-dot.is-live {
170
+ animation: none;
171
+ }
172
+
173
+ .ds-lib-tool-chevron {
174
+ transition: none;
175
+ }
176
+ }
177
+ </style>
@@ -8,17 +8,22 @@
8
8
 
9
9
  Two moving parts, deliberately: a word that changes so the page is evidently
10
10
  alive, and a clock that only goes up so a long wait still reads as progress
11
- rather than as a hang.
11
+ rather than as a hang. The words name whoever is answering — they came from
12
+ `copy`, which the host's `name` composed, so this component knows no persona.
12
13
  -->
13
14
  <script lang="ts">
15
+ import { resolveCopy, type LibrarianCopy } from '../../copy';
16
+
14
17
  interface Props {
15
18
  /** Shown instead of the cycling word once real work is identifiable. */
16
19
  label?: string;
20
+ /** The persona's words. Resolved from `name` upstream. */
21
+ copy?: Partial<LibrarianCopy>;
17
22
  }
18
23
 
19
- let { label }: Props = $props();
24
+ let { label, copy }: Props = $props();
20
25
 
21
- const WORDS = ['Milton is looking', 'Milton is reading'];
26
+ const words = $derived(resolveCopy(copy));
22
27
 
23
28
  let tick = $state(0);
24
29
  let elapsed = $state(0);
@@ -32,11 +37,50 @@
32
37
  };
33
38
  });
34
39
 
35
- const word = $derived(label ?? WORDS[tick % WORDS.length]);
40
+ const word = $derived(label ?? words.working[tick % words.working.length]);
36
41
  </script>
37
42
 
38
- <div class="text-muted-foreground flex items-center gap-2 text-sm">
39
- <span class="bg-status-info size-1.5 animate-pulse rounded-full"></span>
43
+ <div class="ds-lib-working">
44
+ <span class="ds-lib-working-dot"></span>
40
45
  <span>{word}…</span>
41
- <span class="text-muted-foreground/70 font-mono text-xs tabular-nums">{elapsed}s</span>
46
+ <span class="ds-lib-working-clock">{elapsed}s</span>
42
47
  </div>
48
+
49
+ <style>
50
+ .ds-lib-working {
51
+ display: flex;
52
+ align-items: center;
53
+ gap: 0.5rem;
54
+ color: var(--ds-color-muted-foreground);
55
+ font-size: 0.875rem;
56
+ line-height: 1.25rem;
57
+ }
58
+
59
+ .ds-lib-working-dot {
60
+ width: 0.375rem;
61
+ height: 0.375rem;
62
+ flex: none;
63
+ border-radius: var(--ds-radius-full);
64
+ background: var(--ds-color-status-info);
65
+ animation: ds-lib-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
66
+ }
67
+
68
+ .ds-lib-working-clock {
69
+ font-family: var(--ds-font-code);
70
+ font-size: var(--ds-text-2xs);
71
+ font-variant-numeric: tabular-nums;
72
+ opacity: 0.7;
73
+ }
74
+
75
+ @keyframes ds-lib-pulse {
76
+ 50% {
77
+ opacity: 0.4;
78
+ }
79
+ }
80
+
81
+ @media (prefers-reduced-motion: reduce) {
82
+ .ds-lib-working-dot {
83
+ animation: none;
84
+ }
85
+ }
86
+ </style>
@@ -1,6 +1,9 @@
1
+ import { type LibrarianCopy } from '../../copy';
1
2
  interface Props {
2
3
  /** Shown instead of the cycling word once real work is identifiable. */
3
4
  label?: string;
5
+ /** The persona's words. Resolved from `name` upstream. */
6
+ copy?: Partial<LibrarianCopy>;
4
7
  }
5
8
  declare const Working: import("svelte").Component<Props, {}, "">;
6
9
  type Working = ReturnType<typeof Working>;
package/dist/copy.d.ts CHANGED
@@ -2,16 +2,24 @@
2
2
  * Every word this package puts in front of a reader, in one place.
3
3
  *
4
4
  * Here rather than inline in each component because these sentences are the
5
- * package's opinion about how Milton talks to a colleague, and an app that
6
- * needs a different register — a different name for the librarian, a room
7
- * whose boundary is worded by its own subject-matter owner — must be able to
8
- * change one string without forking a component.
5
+ * package's opinion about how an agent talks to a colleague, and an app that
6
+ * needs a different register — a room whose boundary is worded by its own
7
+ * subject-matter owner — must be able to change one string without forking a
8
+ * component.
9
+ *
10
+ * NO STRING HERE NAMES A PERSONA. The name is an argument: a consuming app
11
+ * says who is speaking once, and every sentence the package renders is built
12
+ * from that. Milton is the library's own persona and therefore the default,
13
+ * which is a different thing from being baked in — a package that hardcodes
14
+ * one app's persona is a package that lies in every other app, and did:
15
+ * Pebblestone's Penny introduced herself as Milton for a fortnight.
9
16
  *
10
17
  * Every component takes `copy` as a PARTIAL and resolves it itself, so a host
11
- * overriding one line does not have to restate the other eleven, and a
12
- * component used directly behaves the same as one reached through
13
- * `Conversation`.
18
+ * overriding one line does not have to restate the others, and a component
19
+ * used directly behaves the same as one reached through `Conversation`.
14
20
  */
21
+ /** The library's own persona: the default wherever a consumer names none. */
22
+ export declare const DEFAULT_PERSONA = "Milton";
15
23
  export interface LibrarianCopy {
16
24
  /** Prefixes the date on a verified source: "verified 23 Jun 2026". */
17
25
  verified: string;
@@ -21,12 +29,23 @@ export interface LibrarianCopy {
21
29
  notHeld: string;
22
30
  /** Shown when the run itself ended in error and said nothing about why. */
23
31
  answerFailed: string;
32
+ /** Shown when the stream never opened, or died mid-answer. */
33
+ unreachable: string;
24
34
  /** Heading over the list of what an answer cited. */
25
35
  sources: string;
26
- /** Heading over the follow-up questions the librarian offered. */
36
+ /** Heading over the follow-up questions the persona offered. */
27
37
  suggestions: string;
28
38
  /** The collapsed scope statement's own label. */
29
39
  scope: string;
40
+ /** The empty transcript's opening line. */
41
+ welcome: string;
42
+ /** The composer's placeholder, idle and while an answer streams. */
43
+ askPlaceholder: string;
44
+ answeringPlaceholder: string;
45
+ /** The pre-first-token words, cycled one every 2.6s. */
46
+ working: string[];
47
+ /** The scroll region's accessible name. */
48
+ conversationLabel: string;
30
49
  copyAnswer: string;
31
50
  copiedAnswer: string;
32
51
  askAgain: string;
@@ -34,13 +53,29 @@ export interface LibrarianCopy {
34
53
  closeSource: string;
35
54
  documentUnavailable: string;
36
55
  }
56
+ /**
57
+ * The display name for a persona a host identified by its slug.
58
+ *
59
+ * A route parameter is `penny`, and a colleague reading the screen is talking
60
+ * to Penny. A host that already wrote the name the way it should read — any
61
+ * capital at all — is left alone, so "McTavish" and "eight" both survive.
62
+ */
63
+ export declare function personaName(name: string): string;
64
+ /**
65
+ * The package's words, spoken by one named persona.
66
+ *
67
+ * Deliberately free of pronouns: the persona is whoever the consuming app
68
+ * says it is, and a sentence that guesses is wrong for half of them.
69
+ */
70
+ export declare function copyFor(name?: string): LibrarianCopy;
71
+ /** The library's own words, for a host that names no persona. */
37
72
  export declare const DEFAULT_COPY: LibrarianCopy;
38
73
  /**
39
- * The package's words with a host's overrides on top.
74
+ * One persona's words with a host's overrides on top.
40
75
  *
41
76
  * An explicitly `undefined` key is dropped rather than spread, because
42
77
  * `{...defaults, ...{ sources: undefined }}` leaves a component rendering
43
78
  * nothing at all — and that is exactly the shape a host produces by passing a
44
79
  * value it computed from state that has not loaded yet.
45
80
  */
46
- export declare function resolveCopy(overrides?: Partial<LibrarianCopy>): LibrarianCopy;
81
+ export declare function resolveCopy(overrides?: Partial<LibrarianCopy>, name?: string): LibrarianCopy;