@poodle64/librarian 2026.10.4 → 2026.10.6
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 +8 -6
- package/dist/components/activity-group/activity-group.svelte +99 -17
- package/dist/components/activity-group/activity-group.svelte.d.ts +4 -1
- package/dist/components/agent-transcript/agent-transcript.svelte +82 -26
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +4 -3
- package/dist/components/artefact-pane/artefact-pane.svelte +278 -78
- package/dist/components/conversation/conversation.svelte +6 -6
- package/dist/components/conversation/conversation.svelte.d.ts +4 -4
- package/dist/components/markdown/markdown.d.ts +27 -10
- package/dist/components/markdown/markdown.js +99 -22
- package/dist/components/markdown/markdown.svelte +122 -28
- package/dist/components/markdown/markdown.svelte.d.ts +12 -4
- package/dist/components/source-fold/index.d.ts +2 -0
- package/dist/components/source-fold/index.js +2 -0
- package/dist/components/source-fold/source-fold.svelte +225 -0
- package/dist/components/source-fold/source-fold.svelte.d.ts +17 -0
- package/dist/components/source-list/source-list.svelte +5 -3
- package/dist/components/source-list/source-list.svelte.d.ts +2 -0
- package/dist/components/thinking-row/thinking-row.svelte +37 -16
- package/dist/components/thinking-row/thinking-row.svelte.d.ts +2 -0
- package/dist/components/tool-row/tool-row.svelte +130 -38
- package/dist/components/visual-block/chart-draw.svelte +26 -0
- package/dist/components/visual-block/chart-draw.svelte.d.ts +7 -0
- package/dist/components/visual-block/chart.svelte +73 -0
- package/dist/components/visual-block/chart.svelte.d.ts +8 -0
- package/dist/components/visual-block/entity-card.svelte +94 -0
- package/dist/components/visual-block/entity-card.svelte.d.ts +7 -0
- package/dist/components/visual-block/timeline.svelte +99 -0
- package/dist/components/visual-block/timeline.svelte.d.ts +7 -0
- package/dist/components/visual-block/visual-block.svelte +53 -0
- package/dist/components/visual-block/visual-block.svelte.d.ts +13 -0
- package/dist/components/written-card/written-card.svelte +97 -49
- package/dist/components/written-card/written-card.svelte.d.ts +1 -1
- package/dist/copy.d.ts +54 -0
- package/dist/copy.js +44 -1
- package/dist/diff.d.ts +8 -0
- package/dist/diff.js +37 -0
- package/dist/steps.d.ts +28 -0
- package/dist/steps.js +168 -0
- package/dist/transcript.svelte.d.ts +4 -0
- package/dist/transcript.svelte.js +13 -2
- package/dist/visuals.d.ts +46 -0
- package/dist/visuals.js +84 -0
- package/dist/web-sources.d.ts +19 -3
- package/dist/web-sources.js +39 -7
- package/dist/written.d.ts +26 -1
- package/dist/written.js +42 -0
- package/package.json +8 -1
- package/dist/components/web-list/index.d.ts +0 -2
- package/dist/components/web-list/index.js +0 -2
- package/dist/components/web-list/web-list.svelte +0 -84
- 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">{
|
|
29
|
+
<h2 class="ds-lib-sources-heading">{heading}</h2>
|
|
28
30
|
{:else}
|
|
29
|
-
<h3 class="ds-lib-sources-heading">{
|
|
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-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
55
|
-
padding-block: 0.
|
|
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-
|
|
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.
|
|
107
|
+
margin-inline-start: 0.4375rem;
|
|
87
108
|
border-inline-start: 1px solid var(--ds-color-border);
|
|
88
|
-
padding-inline-start:
|
|
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
|
|
2
|
+
One tool call, as a row in the turn's fold.
|
|
3
3
|
|
|
4
|
-
Not a card:
|
|
5
|
-
|
|
6
|
-
|
|
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-
|
|
54
|
-
<
|
|
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
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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.
|
|
91
|
-
padding-block: 0.
|
|
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-
|
|
115
|
-
width: 0.375rem;
|
|
116
|
-
height: 0.375rem;
|
|
164
|
+
.ds-lib-tool-meta {
|
|
117
165
|
flex: none;
|
|
118
|
-
|
|
119
|
-
|
|
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-
|
|
123
|
-
|
|
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-
|
|
127
|
-
|
|
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-
|
|
131
|
-
|
|
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:
|
|
245
|
+
margin-inline-start: 0.4375rem;
|
|
154
246
|
border-inline-start: 1px solid var(--ds-color-border);
|
|
155
|
-
padding-inline-start:
|
|
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.
|
|
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-
|
|
288
|
+
.ds-lib-tool-icon.is-live {
|
|
197
289
|
animation: none;
|
|
198
290
|
}
|
|
199
291
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { AreaChart, BarChart, LineChart } from 'layerchart';
|
|
3
|
+
import type { ChartValue } from '../../visuals';
|
|
4
|
+
|
|
5
|
+
let { value }: { value: ChartValue } = $props();
|
|
6
|
+
|
|
7
|
+
// `--chart-1` to `--chart-5` are the app's own series colours.
|
|
8
|
+
const series = $derived(
|
|
9
|
+
value.y.map((key, index) => ({ key, label: key, color: `var(--chart-${index + 1})` }))
|
|
10
|
+
);
|
|
11
|
+
const legend = $derived(series.length > 1);
|
|
12
|
+
// A line or area over ISO dates is a time axis; anything else is the label it says.
|
|
13
|
+
const data = $derived(
|
|
14
|
+
value.type !== 'bar' && value.data.every((row) => /^\d{4}-\d{2}(-\d{2})?$/.test(String(row[value.x])))
|
|
15
|
+
? value.data.map((row) => ({ ...row, [value.x]: new Date(String(row[value.x])) }))
|
|
16
|
+
: value.data
|
|
17
|
+
);
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
{#if value.type === 'bar'}
|
|
21
|
+
<BarChart {data} x={value.x} {series} {legend} seriesLayout="group" props={{ bars: { strokeWidth: 0, radius: 2 } }} />
|
|
22
|
+
{:else if value.type === 'line'}
|
|
23
|
+
<LineChart {data} x={value.x} {series} {legend} />
|
|
24
|
+
{:else}
|
|
25
|
+
<AreaChart {data} x={value.x} {series} {legend} />
|
|
26
|
+
{/if}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
One chart block, drawn with LayerChart in the app's chart tokens. The persona
|
|
3
|
+
writes the data and the kind; colour, type, axes and size come from here.
|
|
4
|
+
LayerChart is loaded when an answer carries a chart, never with the page.
|
|
5
|
+
-->
|
|
6
|
+
<script lang="ts">
|
|
7
|
+
import type { Component } from 'svelte';
|
|
8
|
+
import type { ChartValue } from '../../visuals';
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
value: unknown;
|
|
12
|
+
source: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { value, source }: Props = $props();
|
|
16
|
+
const chart = $derived(value as ChartValue);
|
|
17
|
+
let Draw = $state<Component<{ value: ChartValue }> | null>(null);
|
|
18
|
+
let failed = $state(false);
|
|
19
|
+
|
|
20
|
+
$effect(() => {
|
|
21
|
+
if (Draw || failed) return;
|
|
22
|
+
import('./chart-draw.svelte').then(
|
|
23
|
+
(module) => (Draw = module.default),
|
|
24
|
+
() => (failed = true)
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
{#if failed}
|
|
30
|
+
<pre data-highlighted="true"><code>{source}</code></pre>
|
|
31
|
+
{:else}
|
|
32
|
+
<figure class="ds-lib-chart">
|
|
33
|
+
{#if chart.title}<figcaption class="ds-lib-chart-title">{chart.title}</figcaption>{/if}
|
|
34
|
+
<div class="ds-lib-chart-host">
|
|
35
|
+
{#if Draw}<Draw value={chart} />{/if}
|
|
36
|
+
</div>
|
|
37
|
+
</figure>
|
|
38
|
+
{/if}
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.ds-lib-chart {
|
|
42
|
+
margin: 0.75em 0;
|
|
43
|
+
border: 1px solid var(--ds-color-border);
|
|
44
|
+
border-radius: var(--ds-radius-lg);
|
|
45
|
+
background: var(--ds-color-surface-1);
|
|
46
|
+
padding: 0.75rem 0.875rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.ds-lib-chart-title {
|
|
50
|
+
margin-bottom: 0.5rem;
|
|
51
|
+
color: var(--ds-color-foreground);
|
|
52
|
+
font-size: 0.875rem;
|
|
53
|
+
font-weight: 600;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.ds-lib-chart-host {
|
|
57
|
+
width: 100%;
|
|
58
|
+
height: 16rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.ds-lib-chart-host :global(text) {
|
|
62
|
+
fill: var(--ds-color-muted-foreground);
|
|
63
|
+
stroke: none;
|
|
64
|
+
font-size: 12px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ds-lib-chart-host :global(.lc-rule),
|
|
68
|
+
.ds-lib-chart-host :global(.lc-axis-tick),
|
|
69
|
+
.ds-lib-chart-host :global(.lc-grid-x-line),
|
|
70
|
+
.ds-lib-chart-host :global(.lc-grid-y-line) {
|
|
71
|
+
stroke: var(--ds-color-border-strong);
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -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>
|