@khorsheed/dsh-reader 0.2.1 → 0.3.1
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/CHANGELOG.md +27 -0
- package/README.en.md +26 -121
- package/README.i18n.yaml +3 -3
- package/README.md +26 -121
- package/cordis.patch.yml +7 -0
- package/lib/client.js +30580 -6892
- package/lib/index.js +4189 -621
- package/lib/typert.host.js +2265 -319
- package/lib/typert.remote-client.d.ts +99 -3
- package/lib/typert.remote-client.js +1618 -240
- package/lib/types/captions.d.ts +99 -0
- package/lib/types/captions.js +236 -0
- package/lib/types/client/MediaPlayer.d.ts +55 -0
- package/lib/types/client/MediaPlayer.js +106 -0
- package/lib/types/client/NotesView.d.ts +57 -0
- package/lib/types/client/NotesView.js +124 -0
- package/lib/types/client/ReaderPane.d.ts +20 -3
- package/lib/types/client/ReaderPane.js +905 -164
- package/lib/types/client/SpeakerLegend.d.ts +18 -0
- package/lib/types/client/SpeakerLegend.js +26 -0
- package/lib/types/client/canvas-sync.d.ts +84 -0
- package/lib/types/client/canvas-sync.js +59 -0
- package/lib/types/client/contract.d.ts +138 -3
- package/lib/types/client/extract-article.d.ts +38 -1
- package/lib/types/client/extract-article.js +501 -28
- package/lib/types/client/icons.d.ts +1 -1
- package/lib/types/client/icons.js +1 -1
- package/lib/types/client/index.js +82 -2
- package/lib/types/client/locales.d.ts +279 -11
- package/lib/types/client/locales.js +278 -10
- package/lib/types/client/notes.d.ts +142 -0
- package/lib/types/client/notes.js +334 -0
- package/lib/types/client/parse-rss.js +40 -0
- package/lib/types/client/preset-visibility.d.ts +93 -0
- package/lib/types/client/preset-visibility.js +115 -0
- package/lib/types/client/selectors.d.ts +5 -0
- package/lib/types/client/selectors.js +5 -0
- package/lib/types/client/session.d.ts +21 -1
- package/lib/types/client/session.js +12 -0
- package/lib/types/client/store.js +4 -1
- package/lib/types/client/translate.d.ts +26 -1
- package/lib/types/client/translate.js +29 -12
- package/lib/types/client/useLocalTranscript.d.ts +43 -0
- package/lib/types/client/useLocalTranscript.js +129 -0
- package/lib/types/client/useMedia.d.ts +68 -0
- package/lib/types/client/useMedia.js +299 -0
- package/lib/types/client/useMediaStates.d.ts +9 -0
- package/lib/types/client/useMediaStates.js +43 -0
- package/lib/types/client/useNotes.d.ts +110 -0
- package/lib/types/client/useNotes.js +423 -0
- package/lib/types/client/usePolish.d.ts +47 -0
- package/lib/types/client/usePolish.js +162 -0
- package/lib/types/image-fetch.d.ts +81 -0
- package/lib/types/image-fetch.js +385 -0
- package/lib/types/index.d.ts +6 -0
- package/lib/types/index.js +3 -0
- package/lib/types/local-transcribe.d.ts +171 -0
- package/lib/types/local-transcribe.js +473 -0
- package/lib/types/media-host.d.ts +91 -0
- package/lib/types/media-host.js +230 -0
- package/lib/types/media.d.ts +100 -0
- package/lib/types/media.js +207 -0
- package/lib/types/notes-types.d.ts +158 -0
- package/lib/types/notes-types.js +8 -0
- package/lib/types/notes.d.ts +213 -0
- package/lib/types/notes.js +718 -0
- package/lib/types/polish.d.ts +41 -0
- package/lib/types/polish.js +136 -0
- package/lib/types/remote.d.ts +127 -1
- package/lib/types/remote.js +162 -1
- package/lib/types/service.d.ts +100 -1
- package/lib/types/service.js +320 -59
- package/lib/types/skill.d.ts +20 -0
- package/lib/types/skill.js +78 -0
- package/lib/types/store.js +30 -8
- package/lib/types/tool.d.ts +23 -0
- package/lib/types/tool.js +40 -0
- package/lib/types/tools.d.ts +69 -0
- package/lib/types/tools.js +400 -0
- package/lib/types/types.d.ts +106 -0
- package/lib/types/types.js +66 -0
- package/lib/types/xiaoyuzhou.d.ts +43 -0
- package/lib/types/xiaoyuzhou.js +134 -0
- package/lib/types/ytdlp.d.ts +111 -0
- package/lib/types/ytdlp.js +269 -0
- package/package.json +29 -20
- package/lib/invariant.js +0 -38
- package/lib/types/invariant.d.ts +0 -29
- package/lib/types/invariant.js +0 -38
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transcript's speakers, one chip each, above the tabs: the colour that
|
|
3
|
+
* marks their paragraphs and their name — click to rename (Enter keeps it,
|
|
4
|
+
* Esc drops it, an empty name goes back to 说话人 A). A rename applies to the
|
|
5
|
+
* whole transcript and is kept on the host.
|
|
6
|
+
*
|
|
7
|
+
* @module @khorsheed/dsh-reader/client/SpeakerLegend
|
|
8
|
+
*/
|
|
9
|
+
import { type ReactNode } from 'react';
|
|
10
|
+
export interface SpeakerLegendProps {
|
|
11
|
+
readonly count: number;
|
|
12
|
+
readonly label: (speaker: number) => string;
|
|
13
|
+
readonly onRename: (speaker: number, name: string) => void;
|
|
14
|
+
readonly hint: string;
|
|
15
|
+
readonly renameTitle: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function SpeakerLegend(props: SpeakerLegendProps): ReactNode;
|
|
18
|
+
//# sourceMappingURL=SpeakerLegend.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The transcript's speakers, one chip each, above the tabs: the colour that
|
|
4
|
+
* marks their paragraphs and their name — click to rename (Enter keeps it,
|
|
5
|
+
* Esc drops it, an empty name goes back to 说话人 A). A rename applies to the
|
|
6
|
+
* whole transcript and is kept on the host.
|
|
7
|
+
*
|
|
8
|
+
* @module @khorsheed/dsh-reader/client/SpeakerLegend
|
|
9
|
+
*/
|
|
10
|
+
import { useState } from 'react';
|
|
11
|
+
import css from './ReaderPane.module.css';
|
|
12
|
+
export function SpeakerLegend(props) {
|
|
13
|
+
const { count, label, onRename, hint, renameTitle } = props;
|
|
14
|
+
const [editing, setEditing] = useState(null);
|
|
15
|
+
const [draft, setDraft] = useState('');
|
|
16
|
+
return (_jsxs("div", { className: css.speakerLegend, children: [Array.from({ length: count }, (_, speaker) => (editing === speaker
|
|
17
|
+
? (_jsx("input", { className: css.speakerInput, "data-spk": speaker, autoFocus: true, value: draft, "aria-label": renameTitle, onChange: event => { setDraft(event.target.value); }, onBlur: () => { setEditing(null); }, onKeyDown: event => {
|
|
18
|
+
if (event.key === 'Enter') {
|
|
19
|
+
onRename(speaker, draft);
|
|
20
|
+
setEditing(null);
|
|
21
|
+
}
|
|
22
|
+
if (event.key === 'Escape')
|
|
23
|
+
setEditing(null);
|
|
24
|
+
} }, speaker))
|
|
25
|
+
: (_jsx("button", { type: "button", className: css.speakerChip, "data-spk": speaker, title: renameTitle, onClick: () => { setDraft(label(speaker)); setEditing(speaker); }, children: label(speaker) }, speaker)))), _jsx("span", { className: css.speakerHint, children: hint })] }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「同步到画布」: a note (or a discussion note) becomes a kept card on a canvas
|
|
3
|
+
* the reader picks.
|
|
4
|
+
*
|
|
5
|
+
* The canvas is another package. Its client provides `ctx.canvasInbox`
|
|
6
|
+
* (`@khorsheed/dsh-canvas/client/inbox.ts`); this module mirrors that shape
|
|
7
|
+
* structurally instead of importing it, and the pane hides the gesture when
|
|
8
|
+
* the probe answers nothing. One direction only: the reader writes to the
|
|
9
|
+
* canvas, the canvas never reaches back.
|
|
10
|
+
*
|
|
11
|
+
* The card carries its provenance as text (the article, the ¶, a link to the
|
|
12
|
+
* original) because the canvas does not render a card's `source` field; the
|
|
13
|
+
* structured `source` rides along for the model.
|
|
14
|
+
*
|
|
15
|
+
* @module @khorsheed/dsh-reader/client/canvas-sync
|
|
16
|
+
*/
|
|
17
|
+
import type { ReaderDiscussion, ReaderNote } from '../notes-types.ts';
|
|
18
|
+
/** One canvas a card can go to (the inbox's list row). */
|
|
19
|
+
export interface SyncCanvas {
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly title: string;
|
|
22
|
+
readonly cardCount: number;
|
|
23
|
+
}
|
|
24
|
+
/** One category a card can be filed under. */
|
|
25
|
+
export interface SyncKind {
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly label: string;
|
|
28
|
+
}
|
|
29
|
+
/** What putting a card answers. */
|
|
30
|
+
export type SyncOutcome = {
|
|
31
|
+
readonly ok: true;
|
|
32
|
+
readonly cardId: string;
|
|
33
|
+
} | {
|
|
34
|
+
readonly ok: false;
|
|
35
|
+
readonly error: 'needs-kind';
|
|
36
|
+
readonly kinds: readonly SyncKind[];
|
|
37
|
+
} | {
|
|
38
|
+
readonly ok: false;
|
|
39
|
+
readonly error: string;
|
|
40
|
+
};
|
|
41
|
+
/** The card source the canvas stores (its BoardCardSource). */
|
|
42
|
+
export interface SyncSource {
|
|
43
|
+
readonly type: 'url' | 'paste';
|
|
44
|
+
readonly ref: string;
|
|
45
|
+
readonly title?: string;
|
|
46
|
+
}
|
|
47
|
+
/** The canvas inbox, as this package sees it. */
|
|
48
|
+
export interface CanvasInboxLike {
|
|
49
|
+
current(): string | null;
|
|
50
|
+
listCanvases(): Promise<SyncCanvas[]>;
|
|
51
|
+
addCard(sessionId: string, request: {
|
|
52
|
+
canvasId: string;
|
|
53
|
+
text: string;
|
|
54
|
+
kind?: string;
|
|
55
|
+
source?: SyncSource;
|
|
56
|
+
}): Promise<SyncOutcome>;
|
|
57
|
+
openCard(canvasId: string, cardId: string): void;
|
|
58
|
+
}
|
|
59
|
+
/** The article a card comes from. */
|
|
60
|
+
export interface SyncArticle {
|
|
61
|
+
readonly title: string;
|
|
62
|
+
readonly url?: string;
|
|
63
|
+
}
|
|
64
|
+
/** The localized words a card needs. */
|
|
65
|
+
export interface SyncWords {
|
|
66
|
+
readonly conclusion: string;
|
|
67
|
+
readonly original: string;
|
|
68
|
+
readonly kindCheck: string;
|
|
69
|
+
readonly kindDiscussion: string;
|
|
70
|
+
}
|
|
71
|
+
/** The canvas the reader synced to last, when the browser remembers one. */
|
|
72
|
+
export declare function lastCanvas(): string | null;
|
|
73
|
+
/** Remember the canvas the reader just synced to. */
|
|
74
|
+
export declare function rememberCanvas(canvasId: string): void;
|
|
75
|
+
/** The structured source the canvas keeps (the model reads it). */
|
|
76
|
+
export declare function sourceOf(article: SyncArticle): SyncSource;
|
|
77
|
+
/**
|
|
78
|
+
* One note as card text: the excerpt (and its translation), the comment, the
|
|
79
|
+
* conclusions under it when asked for, and where it came from.
|
|
80
|
+
*/
|
|
81
|
+
export declare function noteCardText(note: ReaderNote, article: SyncArticle, words: SyncWords, withReplies: boolean): string;
|
|
82
|
+
/** One discussion note as card text: its points (with ¶), the aside, where it came from. */
|
|
83
|
+
export declare function discussionCardText(discussion: ReaderDiscussion, article: SyncArticle, words: SyncWords): string;
|
|
84
|
+
//# sourceMappingURL=canvas-sync.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Where the last sync went, remembered per browser (a convenience only). */
|
|
2
|
+
const LAST_CANVAS_KEY = 'dsh-reader.lastCanvas';
|
|
3
|
+
/** The canvas the reader synced to last, when the browser remembers one. */
|
|
4
|
+
export function lastCanvas() {
|
|
5
|
+
try {
|
|
6
|
+
return globalThis.localStorage?.getItem(LAST_CANVAS_KEY) ?? null;
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Remember the canvas the reader just synced to. */
|
|
13
|
+
export function rememberCanvas(canvasId) {
|
|
14
|
+
try {
|
|
15
|
+
globalThis.localStorage?.setItem(LAST_CANVAS_KEY, canvasId);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// A private window or blocked storage: the picker just preselects the first canvas.
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** The provenance line every synced card ends with. */
|
|
22
|
+
function provenance(article, words, para) {
|
|
23
|
+
const where = `— 《${article.title}》${para === undefined ? '' : ` ¶${String(para)}`}`;
|
|
24
|
+
return article.url === undefined ? where : `${where} · [${words.original}](${article.url})`;
|
|
25
|
+
}
|
|
26
|
+
/** The structured source the canvas keeps (the model reads it). */
|
|
27
|
+
export function sourceOf(article) {
|
|
28
|
+
return article.url === undefined
|
|
29
|
+
? { type: 'paste', ref: article.title }
|
|
30
|
+
: { type: 'url', ref: article.url, title: article.title };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* One note as card text: the excerpt (and its translation), the comment, the
|
|
34
|
+
* conclusions under it when asked for, and where it came from.
|
|
35
|
+
*/
|
|
36
|
+
export function noteCardText(note, article, words, withReplies) {
|
|
37
|
+
const lines = note.anchor.quote.split('\n').map(line => `> ${line}`);
|
|
38
|
+
if (note.anchor.translated !== undefined)
|
|
39
|
+
lines.push(`> (${note.anchor.translated})`);
|
|
40
|
+
if (note.comment !== undefined)
|
|
41
|
+
lines.push('', note.comment);
|
|
42
|
+
if (withReplies) {
|
|
43
|
+
for (const reply of note.replies ?? [])
|
|
44
|
+
lines.push('', `${reply.by === 'agent' ? words.conclusion : ''}${reply.text}`);
|
|
45
|
+
}
|
|
46
|
+
lines.push('', provenance(article, words, note.anchor.block + 1));
|
|
47
|
+
return lines.join('\n');
|
|
48
|
+
}
|
|
49
|
+
/** One discussion note as card text: its points (with ¶), the aside, where it came from. */
|
|
50
|
+
export function discussionCardText(discussion, article, words) {
|
|
51
|
+
const head = `${discussion.kind === 'check' ? words.kindCheck : words.kindDiscussion}${discussion.title === '' ? '' : ` · ${discussion.title}`}`;
|
|
52
|
+
const lines = [head, ''];
|
|
53
|
+
for (const point of discussion.points)
|
|
54
|
+
lines.push(`- ${point.text}${point.para === undefined ? '' : `(¶${String(point.para)})`}`);
|
|
55
|
+
if (discussion.aside !== undefined)
|
|
56
|
+
lines.push('', discussion.aside);
|
|
57
|
+
lines.push('', provenance(article, words));
|
|
58
|
+
return lines.join('\n');
|
|
59
|
+
}
|
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
11
11
|
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
12
12
|
import type { GlobalStandardProps, InjectFace, PropsLocale, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
|
|
13
|
-
import type { ReaderAddFailure, ReaderAddOutcome, ReaderAddRefusal, ReaderAnnotationOutcome, ReaderBackfillCandidate, ReaderEntryBodyView, ReaderEntryFetchState, ReaderBody, ReaderCapabilities, ReaderEntryTranslationView, ReaderMutationOutcome, ReaderRefreshResult, ReaderRecentEntry, ReaderSentenceLearn, ReaderSourceSummary, ReaderStorageStats, ReaderTag } from '../types.ts';
|
|
13
|
+
import type { ReaderAddFailure, ReaderAddOutcome, ReaderAddRefusal, ReaderAnnotationOutcome, ReaderBackfillCandidate, ReaderEntryBodyView, ReaderEntryFetchState, ReaderBody, ReaderCapabilities, ReaderEntryTranslationView, ReaderImageResult, ReaderMutationOutcome, ReaderRefreshResult, ReaderRecentEntry, ReaderSentenceLearn, ReaderSourceSummary, ReaderStorageStats, ReaderTag, LocalTranscriptStatus, MediaCardStates, ReaderMedia, ReaderPolishView } from '../types.ts';
|
|
14
|
+
import type { ReaderEntryIdentity, ReaderEntryNotes, ReaderNote, ReaderNoteInput, ReaderNoteOutcome } from '../notes-types.ts';
|
|
14
15
|
import type { createReaderStore } from './store.ts';
|
|
16
|
+
import type { CanvasInboxLike } from './canvas-sync.ts';
|
|
15
17
|
export type { RemoteResult };
|
|
16
18
|
/** The result of extracting one article, as the detail view consumes it. */
|
|
17
19
|
export interface ReaderArticle {
|
|
@@ -48,6 +50,15 @@ export interface ReaderExtractedMeta {
|
|
|
48
50
|
readonly title?: string;
|
|
49
51
|
readonly excerpt?: string;
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Which leg of one entry fetch is running, reported between the awaits.
|
|
55
|
+
*
|
|
56
|
+
* The fetch is a relay — the host downloads, THIS process extracts, the host
|
|
57
|
+
* stores — and a long article makes each leg visible to the reader. The pane
|
|
58
|
+
* renders the phase (plus elapsed time) so a slow fetch reads as work in
|
|
59
|
+
* progress, not as a dead click.
|
|
60
|
+
*/
|
|
61
|
+
export type ReaderFetchPhase = 'downloading' | 'extracting' | 'storing';
|
|
51
62
|
/**
|
|
52
63
|
* The business face every reader surface receives from its registration.
|
|
53
64
|
*
|
|
@@ -140,9 +151,18 @@ export interface ReaderPaneInjected {
|
|
|
140
151
|
/**
|
|
141
152
|
* Fetch one entry's article, extract it in THIS process (the host has no
|
|
142
153
|
* parser) and cache what came out. One call from the view's perspective; the
|
|
143
|
-
* extracted title/excerpt ride along for the card upgrade.
|
|
154
|
+
* extracted title/excerpt ride along for the card upgrade. `onPhase` fires
|
|
155
|
+
* between the legs, so a slow fetch has something to show.
|
|
156
|
+
*/
|
|
157
|
+
fetchEntryBody: (entryId: string, url: string, onPhase?: (phase: ReaderFetchPhase) => void) => Promise<ReaderEntryBodyView & ReaderExtractedMeta>;
|
|
158
|
+
/**
|
|
159
|
+
* Fetch one image through the host, for an `<img>` the browser already
|
|
160
|
+
* failed on (CORP/hotlink protection — the bytes answer 200 to a direct
|
|
161
|
+
* fetch but are withheld from a cross-origin page). The pane re-points the
|
|
162
|
+
* image at a `data:` URI built from the answer; a host without the verb (or
|
|
163
|
+
* an origin that refuses the host too) leaves the broken image alone.
|
|
144
164
|
*/
|
|
145
|
-
|
|
165
|
+
fetchImage: (url: string) => Promise<RemoteResult<ReaderImageResult>>;
|
|
146
166
|
/** The tags on one entry. */
|
|
147
167
|
entryTags: (entryId: string) => Promise<RemoteResult<{
|
|
148
168
|
tags: ReaderTag[];
|
|
@@ -246,6 +266,121 @@ export interface ReaderPaneInjected {
|
|
|
246
266
|
* never cached, never thrown.
|
|
247
267
|
*/
|
|
248
268
|
captureRemote: () => ReaderCaptureRemote | undefined;
|
|
269
|
+
/** One article's notes and the conversations that discussed it. */
|
|
270
|
+
entryNotes: (entryId: string) => Promise<RemoteResult<{
|
|
271
|
+
entry?: ReaderEntryNotes;
|
|
272
|
+
}>>;
|
|
273
|
+
/** How many notes each article carries. */
|
|
274
|
+
noteCounts: () => Promise<RemoteResult<{
|
|
275
|
+
counts: Record<string, number>;
|
|
276
|
+
}>>;
|
|
277
|
+
/** Keep one excerpt (and its comment). */
|
|
278
|
+
addNote: (request: ReaderNoteInput) => Promise<RemoteResult<ReaderNote | 'empty' | 'full'>>;
|
|
279
|
+
/** Change or clear one note's comment. */
|
|
280
|
+
updateNote: (request: {
|
|
281
|
+
entryId: string;
|
|
282
|
+
noteId: string;
|
|
283
|
+
comment: string;
|
|
284
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
285
|
+
/** Delete one note. */
|
|
286
|
+
deleteNote: (request: {
|
|
287
|
+
entryId: string;
|
|
288
|
+
noteId: string;
|
|
289
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
290
|
+
/** Add the reader's own line under a note. */
|
|
291
|
+
addReply: (request: {
|
|
292
|
+
entryId: string;
|
|
293
|
+
noteId: string;
|
|
294
|
+
text: string;
|
|
295
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
296
|
+
/** Rewrite (or, with empty text, remove) one line under a note. */
|
|
297
|
+
editReply: (request: {
|
|
298
|
+
entryId: string;
|
|
299
|
+
noteId: string;
|
|
300
|
+
index: number;
|
|
301
|
+
text: string;
|
|
302
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
303
|
+
/** Remember that a note or a discussion note became a canvas card. */
|
|
304
|
+
markSynced: (request: {
|
|
305
|
+
entryId: string;
|
|
306
|
+
noteId?: string;
|
|
307
|
+
discussionId?: string;
|
|
308
|
+
canvasId: string;
|
|
309
|
+
cardId: string;
|
|
310
|
+
canvasTitle: string;
|
|
311
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
312
|
+
/** The canvas plugin's inbox, probed live; absent hides 「同步到画布」. */
|
|
313
|
+
canvasInbox: () => CanvasInboxLike | undefined;
|
|
314
|
+
/** Delete one discussion note. */
|
|
315
|
+
deleteDiscussion: (request: {
|
|
316
|
+
entryId: string;
|
|
317
|
+
discussionId: string;
|
|
318
|
+
}) => Promise<RemoteResult<ReaderNoteOutcome>>;
|
|
319
|
+
/** Remember that this conversation discussed the article. */
|
|
320
|
+
recordDiscussion: (request: ReaderEntryIdentity & {
|
|
321
|
+
sessionId: string;
|
|
322
|
+
sessionTitle?: string;
|
|
323
|
+
}) => Promise<RemoteResult<'ok' | 'empty'>>;
|
|
324
|
+
/** Hand the host the article's original text for the model. */
|
|
325
|
+
snapshotEntry: (request: ReaderEntryIdentity & {
|
|
326
|
+
paragraphs: string[];
|
|
327
|
+
}) => Promise<RemoteResult<'ok' | 'empty'>>;
|
|
328
|
+
/** A video entry's transcript from its platform captions (stored; `none` when it has none). */
|
|
329
|
+
mediaTranscript: (request: {
|
|
330
|
+
entryId: string;
|
|
331
|
+
url: string;
|
|
332
|
+
}) => Promise<RemoteResult<{
|
|
333
|
+
entryId: string;
|
|
334
|
+
html?: string;
|
|
335
|
+
captions: 'platform' | 'none' | 'failed';
|
|
336
|
+
language?: string;
|
|
337
|
+
error?: string;
|
|
338
|
+
}>>;
|
|
339
|
+
/** Start (or rejoin) local transcription of an entry without captions; `unavailable` without the engine, `needs-models` until the first download is confirmed. */
|
|
340
|
+
startLocalTranscript: (request: {
|
|
341
|
+
entryId: string;
|
|
342
|
+
url: string;
|
|
343
|
+
media: ReaderMedia;
|
|
344
|
+
title?: string;
|
|
345
|
+
confirmModels?: boolean;
|
|
346
|
+
}) => Promise<RemoteResult<LocalTranscriptStatus>>;
|
|
347
|
+
/** Where an entry's local transcription stands, with the transcript so far. */
|
|
348
|
+
localTranscript: (request: {
|
|
349
|
+
entryId: string;
|
|
350
|
+
}) => Promise<RemoteResult<LocalTranscriptStatus>>;
|
|
351
|
+
/** The article's 润色 layer, when there is one for its current text. */
|
|
352
|
+
polish: (request: {
|
|
353
|
+
entryId: string;
|
|
354
|
+
}) => Promise<RemoteResult<ReaderPolishView | null>>;
|
|
355
|
+
/** Show one paragraph's machine translation instead of the 润色 (or back). */
|
|
356
|
+
setPolishOff: (request: {
|
|
357
|
+
entryId: string;
|
|
358
|
+
paragraph: number;
|
|
359
|
+
off: boolean;
|
|
360
|
+
}) => Promise<RemoteResult<ReaderPolishView | null>>;
|
|
361
|
+
/** The reader's names for a transcript's speakers, by index. */
|
|
362
|
+
speakerNames: (request: {
|
|
363
|
+
entryId: string;
|
|
364
|
+
}) => Promise<RemoteResult<Record<string, string>>>;
|
|
365
|
+
/** Name one speaker (empty: back to the default); answers all the names. */
|
|
366
|
+
nameSpeaker: (request: {
|
|
367
|
+
entryId: string;
|
|
368
|
+
speaker: number;
|
|
369
|
+
name: string;
|
|
370
|
+
}) => Promise<RemoteResult<Record<string, string>>>;
|
|
371
|
+
/** The wall's video / audio cards: what each one's transcript is. */
|
|
372
|
+
mediaStates: (request: {
|
|
373
|
+
entryIds: string[];
|
|
374
|
+
}) => Promise<RemoteResult<MediaCardStates>>;
|
|
375
|
+
/** Leave (withdraws a queued job) or, with `force`, stop an entry's local transcription. */
|
|
376
|
+
cancelLocalTranscript: (request: {
|
|
377
|
+
entryId: string;
|
|
378
|
+
force: boolean;
|
|
379
|
+
}) => Promise<RemoteResult<LocalTranscriptStatus>>;
|
|
380
|
+
/** Whether a conversation can be opened from the pane (probed live). */
|
|
381
|
+
canOpenSession: () => boolean;
|
|
382
|
+
/** Open a conversation in the main area; a no-op when the seam is absent. */
|
|
383
|
+
openSession: (sessionId: string) => void;
|
|
249
384
|
}
|
|
250
385
|
/** Full props of the reader pane: the seat's session + store + locale + face. */
|
|
251
386
|
export type ReaderPaneProps = {
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
* - {@link normalizeRichText} takes markup that is ALREADY known to be a body
|
|
15
15
|
* (a feed's `content:encoded`, or the block extraction selected) and puts it
|
|
16
16
|
* through a tag whitelist, so the renderer never sees third-party scripts,
|
|
17
|
-
* styles, forms or event handlers.
|
|
17
|
+
* styles, forms or event handlers. The one sanctioned exception: a content
|
|
18
|
+
* embed (`embedElement`) survives as a sandboxed iframe — `allow-scripts`
|
|
19
|
+
* without `allow-same-origin`, an opaque origin that cannot see the pane —
|
|
20
|
+
* or as a native controls-only media element.
|
|
18
21
|
*
|
|
19
22
|
* Everything runs in the browser because the host runtime has no DOM parser at
|
|
20
23
|
* all; nothing here touches the network.
|
|
@@ -73,6 +76,40 @@ export declare function extractArticle(pageHtml: string, baseUrl: string): Extra
|
|
|
73
76
|
* @returns whitelisted HTML, or an empty string when nothing survives.
|
|
74
77
|
*/
|
|
75
78
|
export declare function normalizeRichText(html: string, baseUrl?: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Rewrite every `var(--…)` in a kept SVG value to something that paints here.
|
|
81
|
+
*
|
|
82
|
+
* A statically served chart colors itself through the PAGE's custom
|
|
83
|
+
* properties (`style="stroke:var(--line)"`), defined in a stylesheet the
|
|
84
|
+
* extraction never carries. Left as-is, each reference resolves against the
|
|
85
|
+
* reader's document instead: an undefined one drops the stroke (an axis-less
|
|
86
|
+
* scatter of black dots — claude.dev's Figure 1), and a name that collides
|
|
87
|
+
* with a host token paints in a color nobody chose. So no reference survives:
|
|
88
|
+
* a declared fallback wins, a ground-like name becomes `none` (callout discs
|
|
89
|
+
* stop hiding what lies under them), and every other one becomes
|
|
90
|
+
* `currentColor` — the article's ink, legible in both themes. A
|
|
91
|
+
* capture-rendered page never reaches this: its styles arrive computed.
|
|
92
|
+
*
|
|
93
|
+
* @param value - one attribute value.
|
|
94
|
+
* @returns the value with no custom-property reference left.
|
|
95
|
+
*/
|
|
96
|
+
export declare function resolveSvgVars(value: string): string;
|
|
97
|
+
/**
|
|
98
|
+
* Bring a body stored by an older extractor up to the current rules.
|
|
99
|
+
*
|
|
100
|
+
* Bodies are extracted once and cached, so the fixes above that can be
|
|
101
|
+
* replayed on whitelisted markup (`resolveSvgVars`, `isResponsiveAlternate`,
|
|
102
|
+
* `settleMathJaxTwins`, `settleListLabels`) would otherwise reach only
|
|
103
|
+
* articles fetched after they shipped — a cached claude.dev post kept drawing
|
|
104
|
+
* its figure as bare dots until its TTL ran out. This replays them at display
|
|
105
|
+
* time. A body with no `<svg>` or `<li>`, or one the current extractor wrote,
|
|
106
|
+
* comes back as the same string. (What the old extractor DROPPED — a
|
|
107
|
+
* foreignObject's labels — is not here to restore; that takes a refetch.)
|
|
108
|
+
*
|
|
109
|
+
* @param html - a stored (already whitelisted) article body.
|
|
110
|
+
* @returns the body under the current rules.
|
|
111
|
+
*/
|
|
112
|
+
export declare function repairStoredBody(html: string): string;
|
|
76
113
|
/**
|
|
77
114
|
* The payload size that separates a real inlined figure from a 1px tracking
|
|
78
115
|
* placeholder. A transparent-gif placeholder is ~70 characters of base64; the
|