@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.
- package/README.md +12 -4
- package/dist/chat.svelte.d.ts +40 -0
- package/dist/chat.svelte.js +57 -3
- package/dist/client.d.ts +3 -0
- package/dist/client.js +4 -1
- package/dist/components/activity-group/activity-group.svelte +72 -77
- package/dist/components/activity-group/activity-group.svelte.d.ts +6 -1
- package/dist/components/agent-transcript/agent-transcript.svelte +225 -9
- package/dist/components/agent-transcript/agent-transcript.svelte.d.ts +19 -0
- package/dist/components/artefact-pane/artefact-pane.svelte +387 -43
- package/dist/components/artefact-pane/artefact-pane.svelte.d.ts +10 -1
- package/dist/components/conversation/conversation.svelte +74 -4
- package/dist/components/conversation/conversation.svelte.d.ts +15 -0
- package/dist/components/markdown/markdown.d.ts +30 -2
- package/dist/components/markdown/markdown.js +142 -5
- package/dist/components/markdown/markdown.svelte +130 -16
- package/dist/components/markdown/markdown.svelte.d.ts +12 -0
- 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 +38 -16
- package/dist/components/thinking-row/thinking-row.svelte.d.ts +3 -0
- package/dist/components/tool-row/tool-row.svelte +161 -49
- package/dist/components/tool-row/tool-row.svelte.d.ts +2 -3
- 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/vega-chart.svelte +110 -0
- package/dist/components/visual-block/vega-chart.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/index.d.ts +2 -0
- package/dist/components/written-card/index.js +2 -0
- package/dist/components/written-card/written-card.svelte +178 -0
- package/dist/components/written-card/written-card.svelte.d.ts +15 -0
- package/dist/copy.d.ts +73 -0
- package/dist/copy.js +56 -0
- 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 +19 -34
- package/dist/transcript.svelte.js +64 -56
- package/dist/visuals.d.ts +38 -0
- package/dist/visuals.js +82 -0
- package/dist/web-sources.d.ts +34 -0
- package/dist/web-sources.js +90 -0
- package/dist/written.d.ts +71 -0
- package/dist/written.js +83 -0
- package/package.json +14 -1
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
One fenced block of a language the host has a renderer for.
|
|
3
|
+
|
|
4
|
+
While its closing fence has not arrived the block is a placeholder: half a
|
|
5
|
+
JSON document is not a chart. A closed block is parsed by its renderer, and
|
|
6
|
+
one that will not parse is shown as the code it was, never a blank.
|
|
7
|
+
-->
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import type { ChatCopy } from '../../copy';
|
|
10
|
+
import type { Visual } from '../../visuals';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
lang: string;
|
|
14
|
+
visual: Visual;
|
|
15
|
+
source: string;
|
|
16
|
+
closed: boolean;
|
|
17
|
+
streaming: boolean;
|
|
18
|
+
words: ChatCopy;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { lang, visual, source, closed, streaming, words }: Props = $props();
|
|
22
|
+
|
|
23
|
+
const drawn = $derived.by(() => {
|
|
24
|
+
if (!closed) return null;
|
|
25
|
+
try {
|
|
26
|
+
return { value: visual.parse(source) };
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const Drawing = $derived(visual.component);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if !closed && streaming}
|
|
35
|
+
<p class="ds-lib-drawing" role="status">{words.drawing}</p>
|
|
36
|
+
{:else if drawn}
|
|
37
|
+
<Drawing value={drawn.value} {source} />
|
|
38
|
+
{:else}
|
|
39
|
+
<!-- Marked, so the highlighter that follows the prose leaves what this owns. -->
|
|
40
|
+
<pre data-highlighted="true"><code class="language-{lang}">{source}</code></pre>
|
|
41
|
+
{/if}
|
|
42
|
+
|
|
43
|
+
<style>
|
|
44
|
+
.ds-lib-drawing {
|
|
45
|
+
margin: 0.75em 0;
|
|
46
|
+
border: 1px dashed var(--ds-color-border);
|
|
47
|
+
border-radius: var(--ds-radius-lg);
|
|
48
|
+
padding: 1.25rem 1rem;
|
|
49
|
+
color: var(--ds-color-muted-foreground);
|
|
50
|
+
font-size: 0.875rem;
|
|
51
|
+
text-align: center;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ChatCopy } from '../../copy';
|
|
2
|
+
import type { Visual } from '../../visuals';
|
|
3
|
+
interface Props {
|
|
4
|
+
lang: string;
|
|
5
|
+
visual: Visual;
|
|
6
|
+
source: string;
|
|
7
|
+
closed: boolean;
|
|
8
|
+
streaming: boolean;
|
|
9
|
+
words: ChatCopy;
|
|
10
|
+
}
|
|
11
|
+
declare const VisualBlock: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type VisualBlock = ReturnType<typeof VisualBlock>;
|
|
13
|
+
export default VisualBlock;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
A document the persona wrote, as a card in the turn: a page icon, its title,
|
|
3
|
+
"Minute · 1 page · v1" and Open (the reading column shows that version),
|
|
4
|
+
with a chip under it that downloads it: Word where the app renders one, else
|
|
5
|
+
the source as written.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import DownloadIcon from '@lucide/svelte/icons/download';
|
|
9
|
+
import FileTextIcon from '@lucide/svelte/icons/file-text';
|
|
10
|
+
import { fill, resolveCopy, type ChatCopy } from '../../copy';
|
|
11
|
+
import { chipFor, writtenMeta, type Written, type WrittenUrl } from '../../written';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
written: Written;
|
|
15
|
+
/** The column is showing this version now. */
|
|
16
|
+
open?: boolean;
|
|
17
|
+
/** Omit and the card lists and links but does not open. */
|
|
18
|
+
onopen?: () => void;
|
|
19
|
+
/** Omit and no download chip renders. */
|
|
20
|
+
url?: WrittenUrl;
|
|
21
|
+
copy?: Partial<ChatCopy>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let { written, open = false, onopen, url, copy }: Props = $props();
|
|
25
|
+
|
|
26
|
+
const words = $derived(resolveCopy(copy));
|
|
27
|
+
const chip = $derived(chipFor(written, words));
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<div class="ds-lib-written">
|
|
31
|
+
<div class="ds-lib-written-card" class:is-open={open}>
|
|
32
|
+
<span class="ds-lib-written-icon" aria-hidden="true"><FileTextIcon size={20} /></span>
|
|
33
|
+
<span class="ds-lib-written-text">
|
|
34
|
+
<span class="ds-lib-written-name">{written.title}</span>
|
|
35
|
+
<span class="ds-lib-written-meta">{writtenMeta(written, words)}</span>
|
|
36
|
+
</span>
|
|
37
|
+
{#if onopen}
|
|
38
|
+
<button type="button" class="ds-lib-written-open" onclick={onopen}>
|
|
39
|
+
{open ? words.showingArtefact : words.openArtefact}
|
|
40
|
+
</button>
|
|
41
|
+
{/if}
|
|
42
|
+
</div>
|
|
43
|
+
{#if url}
|
|
44
|
+
<a
|
|
45
|
+
class="ds-lib-written-chip"
|
|
46
|
+
href={url(written.name, written.version, chip.format)}
|
|
47
|
+
download={chip.file}
|
|
48
|
+
>
|
|
49
|
+
<FileTextIcon size={14} aria-hidden="true" />
|
|
50
|
+
<span class="ds-lib-written-file">{chip.file}</span>
|
|
51
|
+
<span class="ds-lib-written-format">
|
|
52
|
+
{chip.label} · {fill(words.writtenVersion, { n: written.version })}
|
|
53
|
+
</span>
|
|
54
|
+
<span class="ds-lib-written-download">
|
|
55
|
+
<DownloadIcon size={14} aria-hidden="true" />{words.download}
|
|
56
|
+
</span>
|
|
57
|
+
</a>
|
|
58
|
+
{/if}
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<style>
|
|
62
|
+
.ds-lib-written {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
align-items: flex-start;
|
|
66
|
+
gap: 0.5rem;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.ds-lib-written-card {
|
|
70
|
+
display: flex;
|
|
71
|
+
width: 100%;
|
|
72
|
+
max-width: 36rem;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 0.75rem;
|
|
75
|
+
border: 1px solid var(--ds-color-border);
|
|
76
|
+
border-radius: var(--ds-radius-lg);
|
|
77
|
+
background: var(--ds-color-surface-1);
|
|
78
|
+
padding: 0.625rem 0.75rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.ds-lib-written-card.is-open {
|
|
82
|
+
border-color: var(--ds-color-primary);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* A sheet of paper: light in either theme, as the document is. */
|
|
86
|
+
.ds-lib-written-icon {
|
|
87
|
+
display: flex;
|
|
88
|
+
width: 2.5rem;
|
|
89
|
+
height: 2.75rem;
|
|
90
|
+
flex: none;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
border-radius: var(--ds-radius-sm);
|
|
94
|
+
background: var(--ds-paper, #f1f1ef);
|
|
95
|
+
color: var(--ds-paper-ink, #1c1c1c);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.ds-lib-written-text {
|
|
99
|
+
display: flex;
|
|
100
|
+
min-width: 0;
|
|
101
|
+
flex: 1;
|
|
102
|
+
flex-direction: column;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ds-lib-written-name {
|
|
106
|
+
font-size: 0.9375rem;
|
|
107
|
+
font-weight: 600;
|
|
108
|
+
line-height: 1.25rem;
|
|
109
|
+
overflow-wrap: anywhere;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.ds-lib-written-meta {
|
|
113
|
+
color: var(--ds-color-muted-foreground);
|
|
114
|
+
font-size: var(--ds-text-2xs);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.ds-lib-written-open {
|
|
118
|
+
flex: none;
|
|
119
|
+
border: 0;
|
|
120
|
+
border-radius: var(--ds-radius-md);
|
|
121
|
+
background: none;
|
|
122
|
+
padding: 0.5rem 0.625rem;
|
|
123
|
+
color: var(--ds-color-muted-foreground);
|
|
124
|
+
font: inherit;
|
|
125
|
+
font-size: 0.8125rem;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.ds-lib-written-open:hover {
|
|
130
|
+
color: var(--ds-color-foreground);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.ds-lib-written-open:focus-visible,
|
|
134
|
+
.ds-lib-written-chip:focus-visible {
|
|
135
|
+
outline: 2px solid var(--ds-color-ring);
|
|
136
|
+
outline-offset: 2px;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.ds-lib-written-chip {
|
|
140
|
+
display: inline-flex;
|
|
141
|
+
max-width: 100%;
|
|
142
|
+
min-height: 2.25rem;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 0.5rem;
|
|
145
|
+
border: 1px solid var(--ds-color-border);
|
|
146
|
+
border-radius: var(--ds-radius-full);
|
|
147
|
+
padding: 0.25rem 0.75rem;
|
|
148
|
+
color: var(--ds-color-foreground);
|
|
149
|
+
font-size: 0.8125rem;
|
|
150
|
+
text-decoration: none;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.ds-lib-written-chip:hover {
|
|
154
|
+
background: var(--ds-color-surface-2);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.ds-lib-written-file {
|
|
158
|
+
min-width: 0;
|
|
159
|
+
font-weight: 600;
|
|
160
|
+
overflow-wrap: anywhere;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.ds-lib-written-format {
|
|
164
|
+
color: var(--ds-color-muted-foreground);
|
|
165
|
+
font-size: var(--ds-text-2xs);
|
|
166
|
+
white-space: nowrap;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.ds-lib-written-download {
|
|
170
|
+
display: inline-flex;
|
|
171
|
+
align-items: center;
|
|
172
|
+
gap: 0.25rem;
|
|
173
|
+
border-inline-start: 1px solid var(--ds-color-border);
|
|
174
|
+
padding-inline-start: 0.5rem;
|
|
175
|
+
font-size: var(--ds-text-2xs);
|
|
176
|
+
white-space: nowrap;
|
|
177
|
+
}
|
|
178
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ChatCopy } from '../../copy';
|
|
2
|
+
import { type Written, type WrittenUrl } from '../../written';
|
|
3
|
+
interface Props {
|
|
4
|
+
written: Written;
|
|
5
|
+
/** The column is showing this version now. */
|
|
6
|
+
open?: boolean;
|
|
7
|
+
/** Omit and the card lists and links but does not open. */
|
|
8
|
+
onopen?: () => void;
|
|
9
|
+
/** Omit and no download chip renders. */
|
|
10
|
+
url?: WrittenUrl;
|
|
11
|
+
copy?: Partial<ChatCopy>;
|
|
12
|
+
}
|
|
13
|
+
declare const WrittenCard: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type WrittenCard = ReturnType<typeof WrittenCard>;
|
|
15
|
+
export default WrittenCard;
|
package/dist/copy.d.ts
CHANGED
|
@@ -69,6 +69,79 @@ export interface ChatCopy {
|
|
|
69
69
|
showingArtefact: string;
|
|
70
70
|
/** Beside the clock on an answer this page did not start. */
|
|
71
71
|
stillAnswering: string;
|
|
72
|
+
/** An expanded tool step: what went in, what came back, and a call that failed. */
|
|
73
|
+
stepInput: string;
|
|
74
|
+
stepResult: string;
|
|
75
|
+
stepFailed: string;
|
|
76
|
+
/** Editing a question already asked: the control, the send, and what the
|
|
77
|
+
* edit does not carry. */
|
|
78
|
+
editQuestion: string;
|
|
79
|
+
editSend: string;
|
|
80
|
+
editFilesNote: string;
|
|
81
|
+
/** The fold over a turn's steps: its sentence is composed from the steps,
|
|
82
|
+
* each phrase lower-case and joined by `stepsAnd` ("Read your attachment,
|
|
83
|
+
* searched the library and the web, and wrote a minute"). `{kind}` is the
|
|
84
|
+
* document's own kind, or `documentKind`. */
|
|
85
|
+
stepsRead: string;
|
|
86
|
+
stepsSearchedLibrary: string;
|
|
87
|
+
stepsSearchedWeb: string;
|
|
88
|
+
stepsSearchedBoth: string;
|
|
89
|
+
stepsOpened: string;
|
|
90
|
+
stepsWrote: string;
|
|
91
|
+
stepsEdited: string;
|
|
92
|
+
stepsThought: string;
|
|
93
|
+
stepsOther: string;
|
|
94
|
+
stepsAnd: string;
|
|
95
|
+
/** What is happening now, while the turn runs. */
|
|
96
|
+
nowRead: string;
|
|
97
|
+
nowLibrary: string;
|
|
98
|
+
nowWeb: string;
|
|
99
|
+
nowOpen: string;
|
|
100
|
+
nowWrite: string;
|
|
101
|
+
nowEdit: string;
|
|
102
|
+
nowThought: string;
|
|
103
|
+
nowOther: string;
|
|
104
|
+
stepCount: string;
|
|
105
|
+
stepCountOne: string;
|
|
106
|
+
/** The right-hand count of a step's row. */
|
|
107
|
+
stepFound: string;
|
|
108
|
+
stepPagesRead: string;
|
|
109
|
+
stepPageRead: string;
|
|
110
|
+
/** A thinking row: while it streams, once done, and with its duration. */
|
|
111
|
+
thinking: string;
|
|
112
|
+
thought: string;
|
|
113
|
+
thoughtFor: string;
|
|
114
|
+
/** The kind of a document that names none. */
|
|
115
|
+
documentKind: string;
|
|
116
|
+
pagesCount: string;
|
|
117
|
+
pageCount: string;
|
|
118
|
+
/** The pane: when a version was written, what changed, and copying it. */
|
|
119
|
+
paneWritten: string;
|
|
120
|
+
paneEdited: string;
|
|
121
|
+
justNow: string;
|
|
122
|
+
changedSince: string;
|
|
123
|
+
copyDocument: string;
|
|
124
|
+
copiedDocument: string;
|
|
125
|
+
/** The sources fold: its summary and its two groups. */
|
|
126
|
+
sourcesCount: string;
|
|
127
|
+
sourcesCountOne: string;
|
|
128
|
+
sourcesLibrary: string;
|
|
129
|
+
sourcesWeb: string;
|
|
130
|
+
fromTheLibrary: string;
|
|
131
|
+
readToday: string;
|
|
132
|
+
readOn: string;
|
|
133
|
+
/** A fenced block still being written. */
|
|
134
|
+
drawing: string;
|
|
135
|
+
/** Web pages an answer drew on, listed apart from its numbered sources. */
|
|
136
|
+
fromTheWeb: string;
|
|
137
|
+
/** A document the persona wrote: its version (`{n}`), the version switcher's
|
|
138
|
+
* name, and what each download is called. */
|
|
139
|
+
writtenVersion: string;
|
|
140
|
+
writtenVersions: string;
|
|
141
|
+
formatMarkdown: string;
|
|
142
|
+
formatHtml: string;
|
|
143
|
+
formatWord: string;
|
|
144
|
+
formatPdf: string;
|
|
72
145
|
/** The answer mark: the two verdicts, the optional note, and after. */
|
|
73
146
|
markHelpful: string;
|
|
74
147
|
markNotHelpful: string;
|
package/dist/copy.js
CHANGED
|
@@ -80,6 +80,62 @@ export function copyFor(name = DEFAULT_PERSONA) {
|
|
|
80
80
|
openArtefact: 'Open',
|
|
81
81
|
showingArtefact: 'Showing',
|
|
82
82
|
stillAnswering: `${who} is still answering`,
|
|
83
|
+
stepInput: 'Input',
|
|
84
|
+
stepResult: 'Result',
|
|
85
|
+
stepFailed: 'Failed',
|
|
86
|
+
editQuestion: 'Edit',
|
|
87
|
+
editSend: 'Send',
|
|
88
|
+
editFilesNote: 'Files attached to the original question are not sent again.',
|
|
89
|
+
stepsRead: 'read your attachment',
|
|
90
|
+
stepsSearchedLibrary: 'searched the library',
|
|
91
|
+
stepsSearchedWeb: 'searched the web',
|
|
92
|
+
stepsSearchedBoth: 'searched the library and the web',
|
|
93
|
+
stepsOpened: 'opened a document',
|
|
94
|
+
stepsWrote: 'wrote a {kind}',
|
|
95
|
+
stepsEdited: 'edited the {kind}',
|
|
96
|
+
stepsThought: 'thought',
|
|
97
|
+
stepsOther: 'looked into it',
|
|
98
|
+
stepsAnd: 'and',
|
|
99
|
+
nowRead: 'Reading your attachment…',
|
|
100
|
+
nowLibrary: 'Searching the library…',
|
|
101
|
+
nowWeb: 'Searching the web…',
|
|
102
|
+
nowOpen: 'Opening a document…',
|
|
103
|
+
nowWrite: 'Writing a {kind}…',
|
|
104
|
+
nowEdit: 'Editing the {kind}…',
|
|
105
|
+
nowThought: 'Thinking…',
|
|
106
|
+
nowOther: 'Working on it…',
|
|
107
|
+
stepCount: '{n} steps',
|
|
108
|
+
stepCountOne: '1 step',
|
|
109
|
+
stepFound: '{n} found',
|
|
110
|
+
stepPagesRead: '{n} pages read',
|
|
111
|
+
stepPageRead: '1 page read',
|
|
112
|
+
thinking: 'Thinking…',
|
|
113
|
+
thought: 'Thought',
|
|
114
|
+
thoughtFor: 'Thought for {n}s',
|
|
115
|
+
documentKind: 'document',
|
|
116
|
+
pagesCount: '{n} pages',
|
|
117
|
+
pageCount: '1 page',
|
|
118
|
+
paneWritten: 'written {when}',
|
|
119
|
+
paneEdited: 'edited {when}',
|
|
120
|
+
justNow: 'just now',
|
|
121
|
+
changedSince: 'Changed since v{n}: {changes}',
|
|
122
|
+
copyDocument: 'Copy',
|
|
123
|
+
copiedDocument: 'Copied',
|
|
124
|
+
sourcesCount: '{n} sources',
|
|
125
|
+
sourcesCountOne: '1 source',
|
|
126
|
+
sourcesLibrary: '{n} library',
|
|
127
|
+
sourcesWeb: '{n} web',
|
|
128
|
+
fromTheLibrary: 'From the library',
|
|
129
|
+
readToday: 'read today',
|
|
130
|
+
readOn: 'read {date}',
|
|
131
|
+
drawing: 'Drawing…',
|
|
132
|
+
fromTheWeb: 'From the web',
|
|
133
|
+
writtenVersion: 'v{n}',
|
|
134
|
+
writtenVersions: 'Version',
|
|
135
|
+
formatMarkdown: 'Markdown',
|
|
136
|
+
formatHtml: 'HTML',
|
|
137
|
+
formatWord: 'Word',
|
|
138
|
+
formatPdf: 'PDF',
|
|
83
139
|
markHelpful: 'Helpful',
|
|
84
140
|
markNotHelpful: 'Not helpful',
|
|
85
141
|
markNote: 'Anything to add? (optional)',
|
package/dist/diff.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a version changed, read off the rendered document: each block of the
|
|
3
|
+
* new one that the old one has no block of the same text for is marked
|
|
4
|
+
* `data-changed`, for a bar beside it. A block is a top-level paragraph,
|
|
5
|
+
* heading, quote or code block, a list item, or a table row.
|
|
6
|
+
*/
|
|
7
|
+
/** Mark each block of `now` that `before` has no block of the same text for, on the DOM `render` sanitised. */
|
|
8
|
+
export declare function markChanges(now: Element, before: Element): void;
|
package/dist/diff.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a version changed, read off the rendered document: each block of the
|
|
3
|
+
* new one that the old one has no block of the same text for is marked
|
|
4
|
+
* `data-changed`, for a bar beside it. A block is a top-level paragraph,
|
|
5
|
+
* heading, quote or code block, a list item, or a table row.
|
|
6
|
+
*/
|
|
7
|
+
function blocksOf(body) {
|
|
8
|
+
const blocks = [];
|
|
9
|
+
for (const el of body.children) {
|
|
10
|
+
if (el.tagName === 'UL' || el.tagName === 'OL')
|
|
11
|
+
blocks.push(...el.querySelectorAll(':scope > li'));
|
|
12
|
+
else if (el.tagName === 'TABLE')
|
|
13
|
+
blocks.push(...el.querySelectorAll('tr'));
|
|
14
|
+
else
|
|
15
|
+
blocks.push(el);
|
|
16
|
+
}
|
|
17
|
+
return blocks;
|
|
18
|
+
}
|
|
19
|
+
const textOf = (el) => (el.textContent ?? '').replace(/\s+/g, ' ').trim();
|
|
20
|
+
/** Mark each block of `now` that `before` has no block of the same text for, on the DOM `render` sanitised. */
|
|
21
|
+
export function markChanges(now, before) {
|
|
22
|
+
const held = new Map();
|
|
23
|
+
for (const block of blocksOf(before)) {
|
|
24
|
+
const text = textOf(block);
|
|
25
|
+
held.set(text, (held.get(text) ?? 0) + 1);
|
|
26
|
+
}
|
|
27
|
+
for (const block of blocksOf(now)) {
|
|
28
|
+
const text = textOf(block);
|
|
29
|
+
if (!text)
|
|
30
|
+
continue;
|
|
31
|
+
const left = held.get(text) ?? 0;
|
|
32
|
+
if (left > 0)
|
|
33
|
+
held.set(text, left - 1);
|
|
34
|
+
else
|
|
35
|
+
block.setAttribute('data-changed', '');
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/steps.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A turn's steps, said in one sentence.
|
|
3
|
+
*
|
|
4
|
+
* The turn folds its steps behind one line composed from them ("Read your
|
|
5
|
+
* attachment, searched the library and the web, and wrote a minute"), the way
|
|
6
|
+
* Claude.ai folds its own. Composed here from what each step IS (its tool and,
|
|
7
|
+
* for a document, the document's own kind), never from the persona's words.
|
|
8
|
+
*/
|
|
9
|
+
import { type ChatCopy } from './copy';
|
|
10
|
+
import type { ActivityStep, Block } from './transcript.svelte';
|
|
11
|
+
import type { Written } from './written';
|
|
12
|
+
export type StepKind = 'attachment' | 'library' | 'web' | 'open' | 'write' | 'edit' | 'thought' | 'other';
|
|
13
|
+
/** What sort of step a block is. */
|
|
14
|
+
export declare function stepKind(block: Block): StepKind;
|
|
15
|
+
/**
|
|
16
|
+
* The fold's line: the sentence, and what trails it ("6 steps", or the
|
|
17
|
+
* version a lone edit made).
|
|
18
|
+
*/
|
|
19
|
+
export declare function stepsSummary(steps: ActivityStep[], written: Written[], words: ChatCopy): {
|
|
20
|
+
sentence: string;
|
|
21
|
+
trail: string;
|
|
22
|
+
};
|
|
23
|
+
/** What is happening now: the step still running, or the latest one. */
|
|
24
|
+
export declare function stepsNow(steps: ActivityStep[], written: Written[], words: ChatCopy): string;
|
|
25
|
+
/** The count at a step's right hand, where its result shows one. */
|
|
26
|
+
export declare function stepMeta(block: Block, words: ChatCopy): string;
|
|
27
|
+
/** How long a thinking block ran, in whole seconds, where it was timed. */
|
|
28
|
+
export declare function thoughtSeconds(block: Block): number | undefined;
|
package/dist/steps.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A turn's steps, said in one sentence.
|
|
3
|
+
*
|
|
4
|
+
* The turn folds its steps behind one line composed from them ("Read your
|
|
5
|
+
* attachment, searched the library and the web, and wrote a minute"), the way
|
|
6
|
+
* Claude.ai folds its own. Composed here from what each step IS (its tool and,
|
|
7
|
+
* for a document, the document's own kind), never from the persona's words.
|
|
8
|
+
*/
|
|
9
|
+
import { fill } from './copy';
|
|
10
|
+
const WEB = new Set(['mcp__supacrawl__supacrawl_search', 'mcp__supacrawl__supacrawl_scrape']);
|
|
11
|
+
/** What sort of step a block is. */
|
|
12
|
+
export function stepKind(block) {
|
|
13
|
+
if (block.kind !== 'tool')
|
|
14
|
+
return 'thought';
|
|
15
|
+
const { name } = block;
|
|
16
|
+
if (name === 'mcp__library__search')
|
|
17
|
+
return 'library';
|
|
18
|
+
if (name === 'mcp__library__document_content' || name === 'mcp__library__document_detail')
|
|
19
|
+
return 'open';
|
|
20
|
+
if (WEB.has(name))
|
|
21
|
+
return 'web';
|
|
22
|
+
if (name === 'Write')
|
|
23
|
+
return 'write';
|
|
24
|
+
if (name === 'Edit')
|
|
25
|
+
return 'edit';
|
|
26
|
+
if (name.startsWith('mcp__') && name.endsWith('__ocr'))
|
|
27
|
+
return 'attachment';
|
|
28
|
+
if (name === 'Read') {
|
|
29
|
+
// A staged library page reads as a document opened; anything else Read
|
|
30
|
+
// takes is what the person attached.
|
|
31
|
+
const path = pathOf(block);
|
|
32
|
+
return /page-\d+\.md$/.test(path) ? 'open' : 'attachment';
|
|
33
|
+
}
|
|
34
|
+
return 'other';
|
|
35
|
+
}
|
|
36
|
+
function inputOf(block) {
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(block.rawInput);
|
|
39
|
+
return typeof parsed === 'object' && parsed !== null ? parsed : {};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const pathOf = (block) => {
|
|
46
|
+
const input = inputOf(block);
|
|
47
|
+
const path = input.file_path ?? input.path;
|
|
48
|
+
return typeof path === 'string' ? path : '';
|
|
49
|
+
};
|
|
50
|
+
/** The document a Write or Edit step made a version of, by the name it wrote to. */
|
|
51
|
+
function documentOf(block, written) {
|
|
52
|
+
const path = pathOf(block);
|
|
53
|
+
if (!path)
|
|
54
|
+
return undefined;
|
|
55
|
+
return written.filter((w) => path === w.name || path.endsWith(`/${w.name}`)).at(-1);
|
|
56
|
+
}
|
|
57
|
+
const kindWord = (doc, words) => ((doc?.kind ?? '').trim() || words.documentKind).toLowerCase();
|
|
58
|
+
const upper = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
59
|
+
function join(phrases, and) {
|
|
60
|
+
if (phrases.length <= 2)
|
|
61
|
+
return phrases.join(` ${and} `);
|
|
62
|
+
return `${phrases.slice(0, -1).join(', ')}, ${and} ${phrases.at(-1)}`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The fold's line: the sentence, and what trails it ("6 steps", or the
|
|
66
|
+
* version a lone edit made).
|
|
67
|
+
*/
|
|
68
|
+
export function stepsSummary(steps, written, words) {
|
|
69
|
+
let library = false;
|
|
70
|
+
let web = false;
|
|
71
|
+
let searchAt = -1;
|
|
72
|
+
const phrases = [];
|
|
73
|
+
const add = (phrase) => {
|
|
74
|
+
if (!phrases.includes(phrase))
|
|
75
|
+
phrases.push(phrase);
|
|
76
|
+
};
|
|
77
|
+
for (const { block } of steps) {
|
|
78
|
+
const kind = stepKind(block);
|
|
79
|
+
const doc = block.kind === 'tool' ? documentOf(block, written) : undefined;
|
|
80
|
+
if (kind === 'library' || kind === 'web') {
|
|
81
|
+
if (kind === 'library')
|
|
82
|
+
library = true;
|
|
83
|
+
else
|
|
84
|
+
web = true;
|
|
85
|
+
if (searchAt < 0) {
|
|
86
|
+
searchAt = phrases.length;
|
|
87
|
+
phrases.push('');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (kind === 'attachment')
|
|
91
|
+
add(words.stepsRead);
|
|
92
|
+
else if (kind === 'open')
|
|
93
|
+
add(words.stepsOpened);
|
|
94
|
+
else if (kind === 'write')
|
|
95
|
+
add(fill(words.stepsWrote, { kind: kindWord(doc, words) }));
|
|
96
|
+
else if (kind === 'edit')
|
|
97
|
+
add(fill(words.stepsEdited, { kind: kindWord(doc, words) }));
|
|
98
|
+
else if (kind === 'other')
|
|
99
|
+
add(words.stepsOther);
|
|
100
|
+
}
|
|
101
|
+
if (searchAt >= 0)
|
|
102
|
+
phrases[searchAt] =
|
|
103
|
+
library && web ? words.stepsSearchedBoth : library ? words.stepsSearchedLibrary : words.stepsSearchedWeb;
|
|
104
|
+
// Thinking is told only where it is all there was.
|
|
105
|
+
if (phrases.length === 0)
|
|
106
|
+
phrases.push(words.stepsThought);
|
|
107
|
+
const only = steps.length === 1 ? steps[0].block : undefined;
|
|
108
|
+
const lone = only?.kind === 'tool' && stepKind(only) === 'edit' ? documentOf(only, written) : undefined;
|
|
109
|
+
return {
|
|
110
|
+
sentence: upper(join(phrases, words.stepsAnd)),
|
|
111
|
+
trail: lone
|
|
112
|
+
? fill(words.writtenVersion, { n: lone.version })
|
|
113
|
+
: steps.length === 1
|
|
114
|
+
? words.stepCountOne
|
|
115
|
+
: fill(words.stepCount, { n: steps.length })
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** What is happening now: the step still running, or the latest one. */
|
|
119
|
+
export function stepsNow(steps, written, words) {
|
|
120
|
+
const step = steps.findLast(({ block }) => block.kind === 'tool' && block.result === undefined)
|
|
121
|
+
?? steps.at(-1);
|
|
122
|
+
if (!step)
|
|
123
|
+
return words.nowOther;
|
|
124
|
+
const kind = stepKind(step.block);
|
|
125
|
+
const doc = step.block.kind === 'tool' ? documentOf(step.block, written) : undefined;
|
|
126
|
+
const now = {
|
|
127
|
+
attachment: words.nowRead,
|
|
128
|
+
library: words.nowLibrary,
|
|
129
|
+
web: words.nowWeb,
|
|
130
|
+
open: words.nowOpen,
|
|
131
|
+
write: fill(words.nowWrite, { kind: kindWord(doc, words) }),
|
|
132
|
+
edit: fill(words.nowEdit, { kind: kindWord(doc, words) }),
|
|
133
|
+
thought: words.nowThought,
|
|
134
|
+
other: words.nowOther
|
|
135
|
+
};
|
|
136
|
+
return now[kind];
|
|
137
|
+
}
|
|
138
|
+
function parse(text) {
|
|
139
|
+
try {
|
|
140
|
+
return text ? JSON.parse(text) : undefined;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/** The count at a step's right hand, where its result shows one. */
|
|
147
|
+
export function stepMeta(block, words) {
|
|
148
|
+
if (block.kind !== 'tool' || block.isError || block.result === undefined)
|
|
149
|
+
return '';
|
|
150
|
+
if (block.name === 'mcp__library__search') {
|
|
151
|
+
const found = parse(block.result);
|
|
152
|
+
const list = Array.isArray(found) ? found : (found?.hits ?? found?.results);
|
|
153
|
+
return Array.isArray(list) ? fill(words.stepFound, { n: list.length }) : '';
|
|
154
|
+
}
|
|
155
|
+
if (block.name === 'mcp__supacrawl__supacrawl_search') {
|
|
156
|
+
const found = parse(block.result)?.results;
|
|
157
|
+
if (!Array.isArray(found))
|
|
158
|
+
return '';
|
|
159
|
+
return found.length === 1 ? words.stepPageRead : fill(words.stepPagesRead, { n: found.length });
|
|
160
|
+
}
|
|
161
|
+
return '';
|
|
162
|
+
}
|
|
163
|
+
/** How long a thinking block ran, in whole seconds, where it was timed. */
|
|
164
|
+
export function thoughtSeconds(block) {
|
|
165
|
+
if (block.kind !== 'thinking' || block.startedAt === undefined || block.endedAt === undefined)
|
|
166
|
+
return undefined;
|
|
167
|
+
return Math.max(1, Math.round((block.endedAt - block.startedAt) / 1000));
|
|
168
|
+
}
|