@poodle64/librarian 2026.10.3 → 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.
Files changed (54) hide show
  1. package/README.md +12 -4
  2. package/dist/chat.svelte.d.ts +40 -0
  3. package/dist/chat.svelte.js +57 -3
  4. package/dist/client.d.ts +3 -0
  5. package/dist/client.js +4 -1
  6. package/dist/components/activity-group/activity-group.svelte +72 -77
  7. package/dist/components/activity-group/activity-group.svelte.d.ts +6 -1
  8. package/dist/components/agent-transcript/agent-transcript.svelte +225 -9
  9. package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +19 -0
  10. package/dist/components/artefact-pane/artefact-pane.svelte +387 -43
  11. package/dist/components/artefact-pane/artefact-pane.svelte.d.ts +10 -1
  12. package/dist/components/conversation/conversation.svelte +74 -4
  13. package/dist/components/conversation/conversation.svelte.d.ts +15 -0
  14. package/dist/components/markdown/markdown.d.ts +30 -2
  15. package/dist/components/markdown/markdown.js +142 -5
  16. package/dist/components/markdown/markdown.svelte +130 -16
  17. package/dist/components/markdown/markdown.svelte.d.ts +12 -0
  18. package/dist/components/source-fold/index.d.ts +2 -0
  19. package/dist/components/source-fold/index.js +2 -0
  20. package/dist/components/source-fold/source-fold.svelte +225 -0
  21. package/dist/components/source-fold/source-fold.svelte.d.ts +17 -0
  22. package/dist/components/source-list/source-list.svelte +5 -3
  23. package/dist/components/source-list/source-list.svelte.d.ts +2 -0
  24. package/dist/components/thinking-row/thinking-row.svelte +38 -16
  25. package/dist/components/thinking-row/thinking-row.svelte.d.ts +3 -0
  26. package/dist/components/tool-row/tool-row.svelte +161 -49
  27. package/dist/components/tool-row/tool-row.svelte.d.ts +2 -3
  28. package/dist/components/visual-block/entity-card.svelte +94 -0
  29. package/dist/components/visual-block/entity-card.svelte.d.ts +7 -0
  30. package/dist/components/visual-block/timeline.svelte +99 -0
  31. package/dist/components/visual-block/timeline.svelte.d.ts +7 -0
  32. package/dist/components/visual-block/vega-chart.svelte +110 -0
  33. package/dist/components/visual-block/vega-chart.svelte.d.ts +7 -0
  34. package/dist/components/visual-block/visual-block.svelte +53 -0
  35. package/dist/components/visual-block/visual-block.svelte.d.ts +13 -0
  36. package/dist/components/written-card/index.d.ts +2 -0
  37. package/dist/components/written-card/index.js +2 -0
  38. package/dist/components/written-card/written-card.svelte +178 -0
  39. package/dist/components/written-card/written-card.svelte.d.ts +15 -0
  40. package/dist/copy.d.ts +73 -0
  41. package/dist/copy.js +56 -0
  42. package/dist/diff.d.ts +8 -0
  43. package/dist/diff.js +37 -0
  44. package/dist/steps.d.ts +28 -0
  45. package/dist/steps.js +168 -0
  46. package/dist/transcript.svelte.d.ts +19 -34
  47. package/dist/transcript.svelte.js +64 -56
  48. package/dist/visuals.d.ts +38 -0
  49. package/dist/visuals.js +82 -0
  50. package/dist/web-sources.d.ts +34 -0
  51. package/dist/web-sources.js +90 -0
  52. package/dist/written.d.ts +71 -0
  53. package/dist/written.js +83 -0
  54. package/package.json +14 -1
@@ -0,0 +1,225 @@
1
+ <!--
2
+ What an answer drew on, folded behind one line: "6 sources · 4 library · 2 web".
3
+
4
+ It opens on the library's numbered sources and then the web pages, numbered
5
+ on from them: the persona cites a page by its link, and the page numbers the
6
+ link with the same chip a library source gets.
7
+ -->
8
+ <script lang="ts">
9
+ import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
10
+ import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
11
+ import GlobeIcon from '@lucide/svelte/icons/globe';
12
+ import LibraryIcon from '@lucide/svelte/icons/library';
13
+ import type { Citation } from '../../citations';
14
+ import { fill, type ChatCopy } from '../../copy';
15
+ import type { WebSource } from '../../web-sources';
16
+ import SourceList from '../source-list/source-list.svelte';
17
+
18
+ interface Props {
19
+ library: Citation[];
20
+ /** Pages, each with the number the answer's chip for it carries. */
21
+ web: Array<WebSource & { n: number }>;
22
+ words: ChatCopy;
23
+ oncite?: (citation: Citation) => void;
24
+ /** When the pages were read, in words ("read today"), for a page the turn read. */
25
+ readWhen?: string;
26
+ }
27
+
28
+ let { library, web, words, oncite, readWhen = '' }: Props = $props();
29
+ let open = $state(false);
30
+
31
+ const total = $derived(library.length + web.length);
32
+
33
+ function when(page: WebSource): string {
34
+ if (page.date) {
35
+ const date = new Date(page.date);
36
+ return date.toLocaleDateString(undefined, { day: 'numeric', month: 'short', year: 'numeric' });
37
+ }
38
+ return page.read ? readWhen : '';
39
+ }
40
+ </script>
41
+
42
+ <section class="ds-lib-fold">
43
+ <button
44
+ type="button"
45
+ class="ds-lib-fold-head"
46
+ onclick={() => (open = !open)}
47
+ aria-expanded={open}
48
+ >
49
+ <span class="ds-lib-fold-icons" aria-hidden="true">
50
+ {#if library.length > 0}<span><LibraryIcon size={11} /></span>{/if}
51
+ {#if web.length > 0}<span><GlobeIcon size={11} /></span>{/if}
52
+ </span>
53
+ <span class="ds-lib-fold-count">
54
+ {total === 1 ? words.sourcesCountOne : fill(words.sourcesCount, { n: total })}
55
+ </span>
56
+ {#if library.length > 0 && web.length > 0}
57
+ <span class="ds-lib-fold-split">
58
+ {fill(words.sourcesLibrary, { n: library.length })} · {fill(words.sourcesWeb, { n: web.length })}
59
+ </span>
60
+ {/if}
61
+ <span class="ds-lib-fold-chevron" aria-hidden="true">
62
+ {#if open}<ChevronUpIcon size={15} />{:else}<ChevronDownIcon size={15} />{/if}
63
+ </span>
64
+ </button>
65
+
66
+ {#if open}
67
+ <div class="ds-lib-fold-body">
68
+ {#if library.length > 0}
69
+ <SourceList sources={library} {words} {oncite} heading={words.fromTheLibrary} />
70
+ {/if}
71
+ {#if web.length > 0}
72
+ <section>
73
+ <h2 class="ds-lib-fold-heading">{words.fromTheWeb}</h2>
74
+ <ol class="ds-lib-fold-pages">
75
+ {#each web as page (page.url)}
76
+ <li>
77
+ <a class="ds-lib-fold-page" href={page.url} target="_blank" rel="noopener noreferrer">
78
+ <span class="ds-lib-fold-n">{page.n}</span>
79
+ <span class="ds-lib-fold-page-text">
80
+ <span class="ds-lib-fold-page-title">{page.title}</span>
81
+ <span class="ds-lib-fold-page-meta">
82
+ {[page.host, when(page)].filter(Boolean).join(' · ')}
83
+ </span>
84
+ </span>
85
+ </a>
86
+ </li>
87
+ {/each}
88
+ </ol>
89
+ </section>
90
+ {/if}
91
+ </div>
92
+ {/if}
93
+ </section>
94
+
95
+ <style>
96
+ .ds-lib-fold-head {
97
+ display: flex;
98
+ min-height: 2.5rem;
99
+ align-items: center;
100
+ gap: 0.625rem;
101
+ border: 0;
102
+ background: none;
103
+ padding: 0;
104
+ color: var(--ds-color-muted-foreground);
105
+ font: inherit;
106
+ font-size: 0.875rem;
107
+ cursor: pointer;
108
+ }
109
+
110
+ .ds-lib-fold-head:hover {
111
+ color: var(--ds-color-foreground);
112
+ }
113
+
114
+ .ds-lib-fold-head:focus-visible {
115
+ outline: 2px solid var(--ds-color-ring);
116
+ outline-offset: 2px;
117
+ }
118
+
119
+ .ds-lib-fold-icons {
120
+ display: flex;
121
+ }
122
+
123
+ .ds-lib-fold-icons span {
124
+ display: flex;
125
+ width: 1.375rem;
126
+ height: 1.375rem;
127
+ align-items: center;
128
+ justify-content: center;
129
+ border: 1px solid var(--ds-color-border);
130
+ border-radius: var(--ds-radius-full);
131
+ background: var(--ds-color-surface-2);
132
+ }
133
+
134
+ .ds-lib-fold-icons span + span {
135
+ margin-inline-start: -0.375rem;
136
+ }
137
+
138
+ .ds-lib-fold-count {
139
+ color: var(--ds-color-foreground);
140
+ font-weight: 500;
141
+ }
142
+
143
+ .ds-lib-fold-split {
144
+ font-size: var(--ds-text-2xs);
145
+ }
146
+
147
+ .ds-lib-fold-chevron {
148
+ display: flex;
149
+ }
150
+
151
+ .ds-lib-fold-body {
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: 1rem;
155
+ margin-top: 0.5rem;
156
+ }
157
+
158
+ .ds-lib-fold-heading {
159
+ margin: 0 0 0.375rem;
160
+ color: var(--ds-color-muted-foreground);
161
+ font-family: inherit;
162
+ font-size: var(--ds-text-2xs);
163
+ font-weight: 500;
164
+ letter-spacing: 0.05em;
165
+ text-transform: uppercase;
166
+ }
167
+
168
+ .ds-lib-fold-pages {
169
+ display: flex;
170
+ flex-direction: column;
171
+ gap: 0.125rem;
172
+ margin: 0;
173
+ border-top: 1px solid var(--ds-color-border);
174
+ padding: 0.375rem 0 0;
175
+ list-style: none;
176
+ }
177
+
178
+ .ds-lib-fold-page {
179
+ display: flex;
180
+ align-items: baseline;
181
+ gap: 0.5rem;
182
+ border-radius: var(--ds-radius-md);
183
+ padding: 0.375rem 0.625rem;
184
+ color: inherit;
185
+ font-size: 0.875rem;
186
+ text-decoration: none;
187
+ }
188
+
189
+ .ds-lib-fold-page:hover {
190
+ background: var(--ds-color-surface-2);
191
+ }
192
+
193
+ .ds-lib-fold-page:focus-visible {
194
+ outline: 2px solid var(--ds-color-ring);
195
+ outline-offset: 2px;
196
+ }
197
+
198
+ .ds-lib-fold-n {
199
+ flex: none;
200
+ border-radius: var(--ds-radius-sm);
201
+ background: color-mix(in oklab, var(--ds-color-primary) 15%, transparent);
202
+ padding-inline: 0.375rem;
203
+ color: var(--ds-color-foreground);
204
+ font-family: var(--ds-font-code);
205
+ font-size: var(--ds-text-2xs);
206
+ font-variant-numeric: tabular-nums;
207
+ }
208
+
209
+ .ds-lib-fold-page-text {
210
+ display: flex;
211
+ min-width: 0;
212
+ flex-direction: column;
213
+ }
214
+
215
+ .ds-lib-fold-page-title {
216
+ color: var(--ds-color-foreground);
217
+ font-weight: 500;
218
+ overflow-wrap: anywhere;
219
+ }
220
+
221
+ .ds-lib-fold-page-meta {
222
+ color: var(--ds-color-muted-foreground);
223
+ font-size: var(--ds-text-2xs);
224
+ }
225
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { Citation } from '../../citations';
2
+ import { type ChatCopy } from '../../copy';
3
+ import type { WebSource } from '../../web-sources';
4
+ interface Props {
5
+ library: Citation[];
6
+ /** Pages, each with the number the answer's chip for it carries. */
7
+ web: Array<WebSource & {
8
+ n: number;
9
+ }>;
10
+ words: ChatCopy;
11
+ oncite?: (citation: Citation) => void;
12
+ /** When the pages were read, in words ("read today"), for a page the turn read. */
13
+ readWhen?: string;
14
+ }
15
+ declare const SourceFold: import("svelte").Component<Props, {}, "">;
16
+ type SourceFold = ReturnType<typeof SourceFold>;
17
+ export default SourceFold;
@@ -17,16 +17,18 @@
17
17
  oncite?: (citation: Citation) => void;
18
18
  /** `h2` in a transcript turn, `h3` under the artefact's own title. */
19
19
  level?: 2 | 3;
20
+ /** The heading, where it is not "Sources". */
21
+ heading?: string;
20
22
  }
21
23
 
22
- let { sources, words, oncite, level = 2 }: Props = $props();
24
+ let { sources, words, oncite, level = 2, heading = words.sources }: Props = $props();
23
25
  </script>
24
26
 
25
27
  <section class="ds-lib-sources">
26
28
  {#if level === 2}
27
- <h2 class="ds-lib-sources-heading">{words.sources}</h2>
29
+ <h2 class="ds-lib-sources-heading">{heading}</h2>
28
30
  {:else}
29
- <h3 class="ds-lib-sources-heading">{words.sources}</h3>
31
+ <h3 class="ds-lib-sources-heading">{heading}</h3>
30
32
  {/if}
31
33
  <ol class="ds-lib-sources-list">
32
34
  {#each sources as source (source.n)}
@@ -7,6 +7,8 @@ interface Props {
7
7
  oncite?: (citation: Citation) => void;
8
8
  /** `h2` in a transcript turn, `h3` under the artefact's own title. */
9
9
  level?: 2 | 3;
10
+ /** The heading, where it is not "Sources". */
11
+ heading?: string;
10
12
  }
11
13
  declare const SourceList: import("svelte").Component<Props, {}, "">;
12
14
  type SourceList = ReturnType<typeof SourceList>;
@@ -11,15 +11,30 @@
11
11
  -->
12
12
  <script lang="ts">
13
13
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
14
+ import LightbulbIcon from '@lucide/svelte/icons/lightbulb';
15
+ import { fill, resolveCopy, type ChatCopy } from '../../copy';
16
+ import { thoughtSeconds } from '../../steps';
14
17
  import type { TextBlock, ThinkingBlock } from '../../transcript.svelte';
15
18
 
16
19
  interface Props {
17
20
  block: ThinkingBlock | TextBlock;
21
+ /** Still being written: "Thinking…" rather than "Thought". */
18
22
  active: boolean;
23
+ copy?: Partial<ChatCopy>;
19
24
  }
20
25
 
21
- let { block, active }: Props = $props();
26
+ let { block, active, copy }: Props = $props();
22
27
  let open = $state(false);
28
+
29
+ const words = $derived(resolveCopy(copy));
30
+ const seconds = $derived(thoughtSeconds(block));
31
+ const label = $derived(
32
+ active
33
+ ? words.thinking
34
+ : seconds === undefined
35
+ ? words.thought
36
+ : fill(words.thoughtFor, { n: seconds })
37
+ );
23
38
  </script>
24
39
 
25
40
  <div class="ds-lib-thinking">
@@ -29,11 +44,10 @@
29
44
  onclick={() => (open = !open)}
30
45
  aria-expanded={open}
31
46
  >
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
- {active ? 'Thinking…' : 'Thought'}
47
+ <span class="ds-lib-thinking-icon" aria-hidden="true"><LightbulbIcon size={14} /></span>
48
+ <span class="ds-lib-thinking-label" class:is-active={active}>{label}</span>
49
+ <span class="ds-lib-thinking-chevron" class:is-open={open} aria-hidden="true">
50
+ <ChevronRightIcon size={14} />
37
51
  </span>
38
52
  </button>
39
53
  {#if open}
@@ -49,9 +63,10 @@
49
63
 
50
64
  .ds-lib-thinking-row {
51
65
  display: flex;
66
+ width: 100%;
52
67
  align-items: center;
53
- gap: 0.5rem;
54
- padding-block: 0.125rem;
68
+ gap: 0.625rem;
69
+ padding-block: 0.25rem;
55
70
  border: 0;
56
71
  background: none;
57
72
  color: inherit;
@@ -60,18 +75,14 @@
60
75
  cursor: pointer;
61
76
  }
62
77
 
63
- .ds-lib-thinking-chevron {
78
+ .ds-lib-thinking-icon {
64
79
  display: flex;
65
80
  flex: none;
66
81
  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
82
  }
73
83
 
74
84
  .ds-lib-thinking-label {
85
+ flex: 1;
75
86
  color: var(--ds-color-muted-foreground);
76
87
  }
77
88
 
@@ -80,11 +91,22 @@
80
91
  font-weight: 500;
81
92
  }
82
93
 
94
+ .ds-lib-thinking-chevron {
95
+ display: flex;
96
+ flex: none;
97
+ color: var(--ds-color-muted-foreground);
98
+ transition: transform 150ms ease;
99
+ }
100
+
101
+ .ds-lib-thinking-chevron.is-open {
102
+ transform: rotate(90deg);
103
+ }
104
+
83
105
  .ds-lib-thinking-text {
84
106
  margin: 0.25rem 0 0;
85
- margin-inline-start: 0.3125rem;
107
+ margin-inline-start: 0.4375rem;
86
108
  border-inline-start: 1px solid var(--ds-color-border);
87
- padding-inline-start: 0.75rem;
109
+ padding-inline-start: 1rem;
88
110
  color: var(--ds-color-muted-foreground);
89
111
  white-space: pre-wrap;
90
112
  }
@@ -1,7 +1,10 @@
1
+ import { type ChatCopy } from '../../copy';
1
2
  import type { TextBlock, ThinkingBlock } from '../../transcript.svelte';
2
3
  interface Props {
3
4
  block: ThinkingBlock | TextBlock;
5
+ /** Still being written: "Thinking…" rather than "Thought". */
4
6
  active: boolean;
7
+ copy?: Partial<ChatCopy>;
5
8
  }
6
9
  declare const ThinkingRow: import("svelte").Component<Props, {}, "">;
7
10
  type ThinkingRow = ReturnType<typeof ThinkingRow>;