@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.
Files changed (50) hide show
  1. package/README.md +7 -5
  2. package/dist/components/activity-group/activity-group.svelte +99 -17
  3. package/dist/components/activity-group/activity-group.svelte.d.ts +4 -1
  4. package/dist/components/agent-transcript/agent-transcript.svelte +82 -26
  5. package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +4 -3
  6. package/dist/components/artefact-pane/artefact-pane.svelte +278 -78
  7. package/dist/components/conversation/conversation.svelte +6 -6
  8. package/dist/components/conversation/conversation.svelte.d.ts +4 -4
  9. package/dist/components/markdown/markdown.d.ts +27 -10
  10. package/dist/components/markdown/markdown.js +99 -22
  11. package/dist/components/markdown/markdown.svelte +122 -28
  12. package/dist/components/markdown/markdown.svelte.d.ts +12 -4
  13. package/dist/components/source-fold/index.d.ts +2 -0
  14. package/dist/components/source-fold/index.js +2 -0
  15. package/dist/components/source-fold/source-fold.svelte +225 -0
  16. package/dist/components/source-fold/source-fold.svelte.d.ts +17 -0
  17. package/dist/components/source-list/source-list.svelte +5 -3
  18. package/dist/components/source-list/source-list.svelte.d.ts +2 -0
  19. package/dist/components/thinking-row/thinking-row.svelte +37 -16
  20. package/dist/components/thinking-row/thinking-row.svelte.d.ts +2 -0
  21. package/dist/components/tool-row/tool-row.svelte +130 -38
  22. package/dist/components/visual-block/entity-card.svelte +94 -0
  23. package/dist/components/visual-block/entity-card.svelte.d.ts +7 -0
  24. package/dist/components/visual-block/timeline.svelte +99 -0
  25. package/dist/components/visual-block/timeline.svelte.d.ts +7 -0
  26. package/dist/components/visual-block/vega-chart.svelte +110 -0
  27. package/dist/components/visual-block/vega-chart.svelte.d.ts +7 -0
  28. package/dist/components/visual-block/visual-block.svelte +53 -0
  29. package/dist/components/visual-block/visual-block.svelte.d.ts +13 -0
  30. package/dist/components/written-card/written-card.svelte +97 -49
  31. package/dist/components/written-card/written-card.svelte.d.ts +1 -1
  32. package/dist/copy.d.ts +54 -0
  33. package/dist/copy.js +44 -1
  34. package/dist/diff.d.ts +8 -0
  35. package/dist/diff.js +37 -0
  36. package/dist/steps.d.ts +28 -0
  37. package/dist/steps.js +168 -0
  38. package/dist/transcript.svelte.d.ts +4 -0
  39. package/dist/transcript.svelte.js +13 -2
  40. package/dist/visuals.d.ts +38 -0
  41. package/dist/visuals.js +82 -0
  42. package/dist/web-sources.d.ts +19 -3
  43. package/dist/web-sources.js +39 -7
  44. package/dist/written.d.ts +26 -1
  45. package/dist/written.js +42 -0
  46. package/package.json +10 -1
  47. package/dist/components/web-list/index.d.ts +0 -2
  48. package/dist/components/web-list/index.js +0 -2
  49. package/dist/components/web-list/web-list.svelte +0 -84
  50. package/dist/components/web-list/web-list.svelte.d.ts +0 -9
@@ -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,16 +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;
18
21
  /** Still being written: "Thinking…" rather than "Thought". */
19
22
  active: boolean;
23
+ copy?: Partial<ChatCopy>;
20
24
  }
21
25
 
22
- let { block, active }: Props = $props();
26
+ let { block, active, copy }: Props = $props();
23
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
+ );
24
38
  </script>
25
39
 
26
40
  <div class="ds-lib-thinking">
@@ -30,11 +44,10 @@
30
44
  onclick={() => (open = !open)}
31
45
  aria-expanded={open}
32
46
  >
33
- <span class="ds-lib-thinking-chevron" class:is-open={open}>
34
- <ChevronRightIcon size={12} />
35
- </span>
36
- <span class="ds-lib-thinking-label" class:is-active={active}>
37
- {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} />
38
51
  </span>
39
52
  </button>
40
53
  {#if open}
@@ -50,9 +63,10 @@
50
63
 
51
64
  .ds-lib-thinking-row {
52
65
  display: flex;
66
+ width: 100%;
53
67
  align-items: center;
54
- gap: 0.5rem;
55
- padding-block: 0.125rem;
68
+ gap: 0.625rem;
69
+ padding-block: 0.25rem;
56
70
  border: 0;
57
71
  background: none;
58
72
  color: inherit;
@@ -61,18 +75,14 @@
61
75
  cursor: pointer;
62
76
  }
63
77
 
64
- .ds-lib-thinking-chevron {
78
+ .ds-lib-thinking-icon {
65
79
  display: flex;
66
80
  flex: none;
67
81
  color: var(--ds-color-muted-foreground);
68
- transition: transform 150ms ease;
69
- }
70
-
71
- .ds-lib-thinking-chevron.is-open {
72
- transform: rotate(90deg);
73
82
  }
74
83
 
75
84
  .ds-lib-thinking-label {
85
+ flex: 1;
76
86
  color: var(--ds-color-muted-foreground);
77
87
  }
78
88
 
@@ -81,11 +91,22 @@
81
91
  font-weight: 500;
82
92
  }
83
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
+
84
105
  .ds-lib-thinking-text {
85
106
  margin: 0.25rem 0 0;
86
- margin-inline-start: 0.3125rem;
107
+ margin-inline-start: 0.4375rem;
87
108
  border-inline-start: 1px solid var(--ds-color-border);
88
- padding-inline-start: 0.75rem;
109
+ padding-inline-start: 1rem;
89
110
  color: var(--ds-color-muted-foreground);
90
111
  white-space: pre-wrap;
91
112
  }
@@ -1,8 +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;
4
5
  /** Still being written: "Thinking…" rather than "Thought". */
5
6
  active: boolean;
7
+ copy?: Partial<ChatCopy>;
6
8
  }
7
9
  declare const ThinkingRow: import("svelte").Component<Props, {}, "">;
8
10
  type ThinkingRow = ReturnType<typeof ThinkingRow>;
@@ -1,19 +1,27 @@
1
1
  <!--
2
- One tool call, as a row in the transcript.
2
+ One tool call, as a row in the turn's fold.
3
3
 
4
- Not a card: a status dot, an action in bold, its target beside it, one
5
- dimmed sub-line. Borders and badge pills make a run of five calls read as
6
- five separate events rather than one train of thought.
7
-
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: the host's `DescribeTool`, or the
4
+ Not a card: the kind of step as an icon, an action in bold, its target
5
+ beside it, and at the right hand what the result counted ("9 found") and a
6
+ chevron. The row says what the persona DID, in a reader's own words — never
7
+ the tool's name or the raw command it ran: the host's `DescribeTool`, or the
10
8
  package's own `describe()`. Expanding it shows what went in and what came
11
- back, the way Claude Code's own panel does.
9
+ back, the way Claude Code's own panel does; a web search shows the pages it
10
+ found instead, as small cards.
12
11
  -->
13
12
  <script lang="ts">
13
+ import BookOpenTextIcon from '@lucide/svelte/icons/book-open-text';
14
14
  import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
15
+ import CircleIcon from '@lucide/svelte/icons/circle';
16
+ import FilePenIcon from '@lucide/svelte/icons/file-pen';
17
+ import FileTextIcon from '@lucide/svelte/icons/file-text';
18
+ import GlobeIcon from '@lucide/svelte/icons/globe';
19
+ import LibraryIcon from '@lucide/svelte/icons/library';
20
+ import PencilIcon from '@lucide/svelte/icons/pencil';
15
21
  import { describe, type ToolBlock, type ToolWords } from '../../transcript.svelte';
16
22
  import { resolveCopy, type ChatCopy } from '../../copy';
23
+ import { stepKind, stepMeta, type StepKind } from '../../steps';
24
+ import { searchPages } from '../../web-sources';
17
25
 
18
26
  interface Props {
19
27
  block: ToolBlock;
@@ -26,9 +34,23 @@
26
34
  let { block, running, words, copy }: Props = $props();
27
35
  let open = $state(false);
28
36
 
37
+ const ICONS = {
38
+ attachment: FileTextIcon,
39
+ library: LibraryIcon,
40
+ web: GlobeIcon,
41
+ open: BookOpenTextIcon,
42
+ write: FilePenIcon,
43
+ edit: PencilIcon,
44
+ thought: CircleIcon,
45
+ other: CircleIcon
46
+ } satisfies Record<StepKind, unknown>;
47
+
29
48
  const label = $derived(resolveCopy(copy));
30
49
  const settled = $derived(block.result !== undefined);
31
50
  const said = $derived(words ?? describe(block));
51
+ const Icon = $derived(ICONS[stepKind(block)]);
52
+ const meta = $derived(stepMeta(block, label));
53
+ const pages = $derived(searchPages(block));
32
54
  const tone = $derived(block.isError ? 'error' : settled ? 'success' : 'info');
33
55
  // Pretty while it parses; the raw text while it is still arriving.
34
56
  const input = $derived.by(() => {
@@ -50,26 +72,43 @@
50
72
  onclick={() => (open = !open)}
51
73
  aria-expanded={open}
52
74
  >
53
- <span class="ds-lib-tool-chevron" class:is-open={open}>
54
- <ChevronRightIcon size={12} />
75
+ <span class="ds-lib-tool-icon" data-tone={tone} class:is-live={!settled && running} aria-hidden="true">
76
+ <Icon size={14} />
55
77
  </span>
56
- <span class="ds-lib-tool-dot" data-tone={tone} class:is-live={!settled && running}></span>
57
78
  <span class="ds-lib-tool-said">
58
79
  <span class="ds-lib-tool-verb">{said.verb}</span>
59
80
  {#if said.object}<span class="ds-lib-tool-object">{said.object}</span>{/if}
60
81
  </span>
61
82
  {#if block.isError}<span class="ds-lib-tool-failed">{label.stepFailed}</span>{/if}
83
+ {#if meta}<span class="ds-lib-tool-meta">{meta}</span>{/if}
84
+ <span class="ds-lib-tool-chevron" class:is-open={open} aria-hidden="true">
85
+ <ChevronRightIcon size={14} />
86
+ </span>
62
87
  </button>
63
88
 
64
89
  {#if open}
65
90
  <div class="ds-lib-tool-body">
66
- {#if input}
67
- <p class="ds-lib-tool-label">{label.stepInput}</p>
68
- <pre class="ds-lib-tool-result">{input}</pre>
69
- {/if}
70
- {#if block.result}
71
- <p class="ds-lib-tool-label">{label.stepResult}</p>
72
- <pre class="ds-lib-tool-result">{block.result}</pre>
91
+ {#if pages.length > 0}
92
+ <ul class="ds-lib-tool-pages">
93
+ {#each pages as page (page.url)}
94
+ <li>
95
+ <a class="ds-lib-tool-page" href={page.url} target="_blank" rel="noopener noreferrer">
96
+ <span class="ds-lib-tool-page-host"><GlobeIcon size={11} />{page.host}</span>
97
+ <span class="ds-lib-tool-page-title">{page.title}</span>
98
+ {#if page.snippet}<span class="ds-lib-tool-page-snippet">{page.snippet}</span>{/if}
99
+ </a>
100
+ </li>
101
+ {/each}
102
+ </ul>
103
+ {:else}
104
+ {#if input}
105
+ <p class="ds-lib-tool-label">{label.stepInput}</p>
106
+ <pre class="ds-lib-tool-result">{input}</pre>
107
+ {/if}
108
+ {#if block.result}
109
+ <p class="ds-lib-tool-label">{label.stepResult}</p>
110
+ <pre class="ds-lib-tool-result">{block.result}</pre>
111
+ {/if}
73
112
  {/if}
74
113
  </div>
75
114
  {:else if said.detail}
@@ -87,8 +126,8 @@
87
126
  display: flex;
88
127
  width: 100%;
89
128
  align-items: center;
90
- gap: 0.5rem;
91
- padding-block: 0.125rem;
129
+ gap: 0.625rem;
130
+ padding-block: 0.25rem;
92
131
  border: 0;
93
132
  background: none;
94
133
  color: inherit;
@@ -97,12 +136,23 @@
97
136
  cursor: pointer;
98
137
  }
99
138
 
139
+ .ds-lib-tool-icon {
140
+ display: flex;
141
+ flex: none;
142
+ color: var(--ds-color-muted-foreground);
143
+ }
144
+
145
+ .ds-lib-tool-icon[data-tone='error'] {
146
+ color: var(--ds-color-status-error);
147
+ }
148
+
149
+ .ds-lib-tool-icon.is-live {
150
+ animation: ds-lib-tool-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
151
+ }
152
+
100
153
  .ds-lib-tool-chevron {
101
154
  display: flex;
102
- width: 0.75rem;
103
155
  flex: none;
104
- align-items: center;
105
- justify-content: center;
106
156
  color: var(--ds-color-muted-foreground);
107
157
  transition: transform 150ms ease;
108
158
  }
@@ -111,28 +161,70 @@
111
161
  transform: rotate(90deg);
112
162
  }
113
163
 
114
- .ds-lib-tool-dot {
115
- width: 0.375rem;
116
- height: 0.375rem;
164
+ .ds-lib-tool-meta {
117
165
  flex: none;
118
- border-radius: var(--ds-radius-full);
119
- background: var(--ds-color-status-info);
166
+ color: var(--ds-color-muted-foreground);
167
+ font-size: var(--ds-text-2xs);
168
+ font-variant-numeric: tabular-nums;
120
169
  }
121
170
 
122
- .ds-lib-tool-dot[data-tone='error'] {
123
- background: var(--ds-color-status-error);
171
+ .ds-lib-tool-pages {
172
+ display: grid;
173
+ grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
174
+ gap: 0.5rem;
175
+ margin: 0;
176
+ padding: 0;
177
+ list-style: none;
124
178
  }
125
179
 
126
- .ds-lib-tool-dot[data-tone='success'] {
127
- background: var(--ds-color-status-success);
180
+ .ds-lib-tool-page {
181
+ display: flex;
182
+ height: 100%;
183
+ flex-direction: column;
184
+ gap: 0.125rem;
185
+ border: 1px solid var(--ds-color-border);
186
+ border-radius: var(--ds-radius-md);
187
+ padding: 0.5rem 0.625rem;
188
+ color: inherit;
189
+ font-size: var(--ds-text-2xs);
190
+ line-height: 1.35;
191
+ text-decoration: none;
128
192
  }
129
193
 
130
- .ds-lib-tool-dot.is-live {
131
- animation: ds-lib-tool-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
194
+ .ds-lib-tool-page:hover {
195
+ background: var(--ds-color-surface-2);
196
+ }
197
+
198
+ .ds-lib-tool-page:focus-visible {
199
+ outline: 2px solid var(--ds-color-ring);
200
+ outline-offset: 2px;
201
+ }
202
+
203
+ .ds-lib-tool-page-host {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 0.25rem;
207
+ color: var(--ds-color-muted-foreground);
208
+ }
209
+
210
+ .ds-lib-tool-page-title {
211
+ color: var(--ds-color-foreground);
212
+ font-size: 0.8125rem;
213
+ font-weight: 600;
214
+ }
215
+
216
+ .ds-lib-tool-page-snippet {
217
+ display: -webkit-box;
218
+ -webkit-box-orient: vertical;
219
+ -webkit-line-clamp: 3;
220
+ line-clamp: 3;
221
+ overflow: hidden;
222
+ color: var(--ds-color-muted-foreground);
132
223
  }
133
224
 
134
225
  .ds-lib-tool-said {
135
226
  min-width: 0;
227
+ flex: 1;
136
228
  overflow: hidden;
137
229
  text-overflow: ellipsis;
138
230
  white-space: nowrap;
@@ -150,9 +242,9 @@
150
242
 
151
243
  .ds-lib-tool-body {
152
244
  margin-top: 0.25rem;
153
- margin-inline-start: 1.375rem;
245
+ margin-inline-start: 0.4375rem;
154
246
  border-inline-start: 1px solid var(--ds-color-border);
155
- padding-inline-start: 0.75rem;
247
+ padding-inline-start: 1rem;
156
248
  }
157
249
 
158
250
  .ds-lib-tool-result {
@@ -181,7 +273,7 @@
181
273
 
182
274
  .ds-lib-tool-lines {
183
275
  margin: 0;
184
- padding-inline-start: 1.375rem;
276
+ padding-inline-start: 1.5rem;
185
277
  color: var(--ds-color-muted-foreground);
186
278
  font-size: var(--ds-text-2xs);
187
279
  }
@@ -193,7 +285,7 @@
193
285
  }
194
286
 
195
287
  @media (prefers-reduced-motion: reduce) {
196
- .ds-lib-tool-dot.is-live {
288
+ .ds-lib-tool-icon.is-live {
197
289
  animation: none;
198
290
  }
199
291
 
@@ -0,0 +1,94 @@
1
+ <!--
2
+ One thing, its kind, a line on it, and its facts.
3
+ -->
4
+ <script lang="ts">
5
+ import type { CardValue } from '../../visuals';
6
+
7
+ interface Props {
8
+ value: unknown;
9
+ source: string;
10
+ }
11
+
12
+ let { value }: Props = $props();
13
+ const card = $derived(value as CardValue);
14
+ </script>
15
+
16
+ <figure class="ds-lib-entity">
17
+ <header class="ds-lib-entity-head">
18
+ <span class="ds-lib-entity-name">{card.name}</span>
19
+ {#if card.kind}<span class="ds-lib-entity-kind">{card.kind}</span>{/if}
20
+ </header>
21
+ {#if card.summary}<p class="ds-lib-entity-summary">{card.summary}</p>{/if}
22
+ {#if card.facts.length > 0}
23
+ <dl class="ds-lib-entity-facts">
24
+ {#each card.facts as fact, i (i)}
25
+ <div class="ds-lib-entity-fact">
26
+ <dt>{fact.label}</dt>
27
+ <dd>{fact.value}</dd>
28
+ </div>
29
+ {/each}
30
+ </dl>
31
+ {/if}
32
+ </figure>
33
+
34
+ <style>
35
+ .ds-lib-entity {
36
+ margin: 0.75em 0;
37
+ border: 1px solid var(--ds-color-border);
38
+ border-radius: var(--ds-radius-lg);
39
+ background: var(--ds-color-surface-1);
40
+ padding: 0.75rem 1rem;
41
+ font-size: 0.875rem;
42
+ line-height: 1.5;
43
+ }
44
+
45
+ .ds-lib-entity-head {
46
+ display: flex;
47
+ flex-wrap: wrap;
48
+ align-items: baseline;
49
+ gap: 0.25rem 0.625rem;
50
+ }
51
+
52
+ .ds-lib-entity-name {
53
+ color: var(--ds-color-foreground);
54
+ font-size: 1rem;
55
+ font-weight: 600;
56
+ }
57
+
58
+ .ds-lib-entity-kind {
59
+ color: var(--ds-color-muted-foreground);
60
+ font-size: var(--ds-text-2xs);
61
+ letter-spacing: 0.05em;
62
+ text-transform: uppercase;
63
+ }
64
+
65
+ .ds-lib-entity-summary {
66
+ margin: 0.25rem 0 0;
67
+ color: var(--ds-color-muted-foreground);
68
+ }
69
+
70
+ .ds-lib-entity-facts {
71
+ display: grid;
72
+ grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
73
+ gap: 0.5rem 1rem;
74
+ margin: 0.625rem 0 0;
75
+ border-top: 1px solid var(--ds-color-border);
76
+ padding-top: 0.625rem;
77
+ }
78
+
79
+ .ds-lib-entity-fact {
80
+ margin: 0;
81
+ }
82
+
83
+ .ds-lib-entity-fact dt {
84
+ color: var(--ds-color-muted-foreground);
85
+ font-size: var(--ds-text-2xs);
86
+ }
87
+
88
+ .ds-lib-entity-fact dd {
89
+ margin: 0;
90
+ color: var(--ds-color-foreground);
91
+ font-weight: 500;
92
+ overflow-wrap: anywhere;
93
+ }
94
+ </style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ value: unknown;
3
+ source: string;
4
+ }
5
+ declare const EntityCard: import("svelte").Component<Props, {}, "">;
6
+ type EntityCard = ReturnType<typeof EntityCard>;
7
+ export default EntityCard;
@@ -0,0 +1,99 @@
1
+ <!--
2
+ A run of dated events, as a vertical line with a mark at each.
3
+ -->
4
+ <script lang="ts">
5
+ import type { TimelineValue } from '../../visuals';
6
+
7
+ interface Props {
8
+ value: unknown;
9
+ source: string;
10
+ }
11
+
12
+ let { value }: Props = $props();
13
+ const timeline = $derived(value as TimelineValue);
14
+ </script>
15
+
16
+ <figure class="ds-lib-timeline">
17
+ {#if timeline.title}<figcaption class="ds-lib-timeline-title">{timeline.title}</figcaption>{/if}
18
+ <ol class="ds-lib-timeline-events">
19
+ {#each timeline.events as event, i (i)}
20
+ <li class="ds-lib-timeline-event">
21
+ {#if event.date}<time class="ds-lib-timeline-date">{event.date}</time>{/if}
22
+ <span class="ds-lib-timeline-name">{event.title}</span>
23
+ {#if event.detail}<span class="ds-lib-timeline-detail">{event.detail}</span>{/if}
24
+ </li>
25
+ {/each}
26
+ </ol>
27
+ </figure>
28
+
29
+ <style>
30
+ .ds-lib-timeline {
31
+ margin: 0.75em 0;
32
+ border: 1px solid var(--ds-color-border);
33
+ border-radius: var(--ds-radius-lg);
34
+ background: var(--ds-color-surface-1);
35
+ padding: 0.75rem 1rem;
36
+ font-size: 0.875rem;
37
+ line-height: 1.5;
38
+ }
39
+
40
+ .ds-lib-timeline-title {
41
+ margin-bottom: 0.5rem;
42
+ color: var(--ds-color-foreground);
43
+ font-weight: 600;
44
+ }
45
+
46
+ .ds-lib-timeline-events {
47
+ position: relative;
48
+ margin: 0;
49
+ padding: 0;
50
+ list-style: none;
51
+ }
52
+
53
+ .ds-lib-timeline-events::before {
54
+ content: '';
55
+ position: absolute;
56
+ inset-block: 0.5rem;
57
+ inset-inline-start: 0.28125rem;
58
+ width: 1px;
59
+ background: var(--ds-color-border-strong, var(--ds-color-border));
60
+ }
61
+
62
+ .ds-lib-timeline-event {
63
+ position: relative;
64
+ display: flex;
65
+ flex-direction: column;
66
+ margin: 0;
67
+ padding: 0 0 0.75rem 1.5rem;
68
+ }
69
+
70
+ .ds-lib-timeline-event:last-child {
71
+ padding-bottom: 0;
72
+ }
73
+
74
+ .ds-lib-timeline-event::before {
75
+ content: '';
76
+ position: absolute;
77
+ inset-block-start: 0.4rem;
78
+ inset-inline-start: 0;
79
+ width: 0.5625rem;
80
+ height: 0.5625rem;
81
+ border-radius: var(--ds-radius-full);
82
+ background: var(--chart-1, var(--ds-color-primary));
83
+ }
84
+
85
+ .ds-lib-timeline-date {
86
+ color: var(--ds-color-muted-foreground);
87
+ font-family: var(--ds-font-code);
88
+ font-size: var(--ds-text-2xs);
89
+ }
90
+
91
+ .ds-lib-timeline-name {
92
+ color: var(--ds-color-foreground);
93
+ font-weight: 600;
94
+ }
95
+
96
+ .ds-lib-timeline-detail {
97
+ color: var(--ds-color-muted-foreground);
98
+ }
99
+ </style>
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ value: unknown;
3
+ source: string;
4
+ }
5
+ declare const Timeline: import("svelte").Component<Props, {}, "">;
6
+ type Timeline = ReturnType<typeof Timeline>;
7
+ export default Timeline;