@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
|
@@ -25,6 +25,12 @@ export const en = {
|
|
|
25
25
|
'action.filter': 'Filter',
|
|
26
26
|
'action.fetchBody': 'Fetch the text',
|
|
27
27
|
'action.more': 'More actions',
|
|
28
|
+
'translate.wallOff': 'Turn translation off',
|
|
29
|
+
'chip.unread': 'Unread',
|
|
30
|
+
'chip.more': 'Filter…',
|
|
31
|
+
'state.incompleteCount': '{count} sources incomplete',
|
|
32
|
+
'action.showList': 'Show',
|
|
33
|
+
'action.foldList': 'Hide',
|
|
28
34
|
'action.addTag': 'Tags…',
|
|
29
35
|
'action.translate': 'Translate',
|
|
30
36
|
'translate.view': 'Translation view',
|
|
@@ -36,6 +42,7 @@ export const en = {
|
|
|
36
42
|
'translate.preparing': 'Preparing the Chinese language pack',
|
|
37
43
|
'translate.working': 'Translating',
|
|
38
44
|
'translate.local': 'On-device · Chinese (Simplified)',
|
|
45
|
+
'translate.stored': 'Saved translation · Chinese (Simplified)',
|
|
39
46
|
'translate.failed': 'Translation stopped: {reason}',
|
|
40
47
|
'translate.failedAll': 'No batch came back — try again from the globe menu',
|
|
41
48
|
'translate.nothing': 'Nothing in this body to translate',
|
|
@@ -66,7 +73,6 @@ export const en = {
|
|
|
66
73
|
'action.back': 'Back',
|
|
67
74
|
'action.copyLink': 'Copy link',
|
|
68
75
|
'action.openExternal': 'Open in browser',
|
|
69
|
-
'action.quote': 'Quote',
|
|
70
76
|
'action.manage': 'Manage subscriptions',
|
|
71
77
|
'action.recent': 'Recently read',
|
|
72
78
|
'recent.title': 'Recently read',
|
|
@@ -92,6 +98,7 @@ export const en = {
|
|
|
92
98
|
'verdict.unsupportedContent': 'That address did not return a feed or an article.',
|
|
93
99
|
'verdict.fetchFailed': 'Could not fetch that address.',
|
|
94
100
|
'preview.blocked': 'this site refuses automatic fetches (it answers with a bot challenge)',
|
|
101
|
+
'verdict.willRender': 'Opening it retries through the rendered fetch (a headless browser).',
|
|
95
102
|
'preview.login': 'this address is behind a login (an institutional proxy or single sign-on)',
|
|
96
103
|
'preview.unsupportedType': 'this address is not a web page (a PDF or another file)',
|
|
97
104
|
'preview.redirected': 'this address moves to another site, and the redirect cannot be followed here',
|
|
@@ -101,6 +108,11 @@ export const en = {
|
|
|
101
108
|
'preview.unreadable': 'this page is a script-rendered app behind an anti-bot wall, so there is nothing to read server-side — if it has an arXiv version, paste that link instead',
|
|
102
109
|
'detail.linkOnlyBadge': 'Link only',
|
|
103
110
|
'detail.fetchingBody': 'Fetching the full text…',
|
|
111
|
+
'detail.phase.downloading': 'Downloading the page…',
|
|
112
|
+
'detail.phase.extracting': 'Extracting the article…',
|
|
113
|
+
'detail.phase.storing': 'Saving the body…',
|
|
114
|
+
'detail.fetchElapsed': '{seconds}s',
|
|
115
|
+
'add.busy': 'Identifying and fetching the link…',
|
|
104
116
|
'detail.refetch': 'Fetch again',
|
|
105
117
|
'detail.refetching': 'Fetching…',
|
|
106
118
|
'detail.refetchTitle': 'Fetch this article again — what is on screen is the copy fetched earlier',
|
|
@@ -161,8 +173,6 @@ export const en = {
|
|
|
161
173
|
'detail.summaryOnly': 'This feed publishes only a summary for this entry — the full text lives on the original page.',
|
|
162
174
|
'detail.resolvedFrom': 'Resolved from the link you pasted ({url}) — click to open it',
|
|
163
175
|
'detail.alsoFrom': 'Also from this source',
|
|
164
|
-
'detail.composerLabel': 'Current conversation draft',
|
|
165
|
-
'detail.composerEmpty': '(empty)',
|
|
166
176
|
'foot.refreshedAt': 'Refreshed {when}',
|
|
167
177
|
'foot.scheduled': 'daily {time}',
|
|
168
178
|
'foot.never': 'Not refreshed yet',
|
|
@@ -184,17 +194,141 @@ export const en = {
|
|
|
184
194
|
'fetch.failedOpenTitle': 'Fetch failed: {reason} — click for details',
|
|
185
195
|
'fetch.failedFinalTitle': 'Fetch failed: {reason}',
|
|
186
196
|
'detail.renderFetch': 'Fetch rendered',
|
|
197
|
+
'detail.renderingWall': 'This site turned away the plain fetch — rendering it in a headless browser instead (about 15–30 s)…',
|
|
198
|
+
'detail.renderFailed': 'The rendered fetch did not get the article either ({reason}).',
|
|
187
199
|
'detail.rendering': 'Rendering this page in the browser — script-drawn figures appear when it lands (the first render is slower)…',
|
|
188
200
|
'quote.copied': 'Copied',
|
|
189
201
|
'quote.copyFailed': 'Could not reach the clipboard',
|
|
190
202
|
'quote.quoted': 'Quoted {count} characters',
|
|
191
|
-
'
|
|
192
|
-
'
|
|
203
|
+
'add.fetching': 'Fetching…',
|
|
204
|
+
'notes.checkShort': 'Quiz me',
|
|
205
|
+
'polish.action': '✦ Let the agent polish the translation',
|
|
206
|
+
'polish.draft': 'Polish the Chinese translation of “{title}” (article id {id}, {count} paragraphs). Load the reader skill, read the original with reader_read batch by batch, translate whole paragraphs from the original and write each batch back with reader_translate, until every paragraph is done. Faithful, natural Chinese; names, terms and speakers consistent.\nMy wishes: ',
|
|
207
|
+
'polish.continueDraft': 'Continue polishing the Chinese translation of “{title}” (article id {id}, {count} paragraphs) from ¶{from}: read with reader_read from={from}, write back with reader_translate, until every paragraph is done.',
|
|
208
|
+
'polish.tag': 'polished',
|
|
209
|
+
'polish.tagTitle': 'Show the machine translation for this paragraph',
|
|
210
|
+
'polish.running': 'The agent is polishing · {done} / {total} paragraphs',
|
|
211
|
+
'polish.stalled': 'Polished {done} / {total}',
|
|
212
|
+
'polish.continue': 'Polish the remaining {count}',
|
|
213
|
+
'polish.done': '✦ Polished by the agent · {done} paragraphs',
|
|
214
|
+
'polish.showMachine': 'Show the machine translation',
|
|
215
|
+
'polish.showPolish': 'Show the polished one',
|
|
216
|
+
'polish.awaiting': 'The polish request is in the input box — send it and progress shows here',
|
|
193
217
|
'when.justNow': 'just now',
|
|
194
218
|
'when.minutes': '{count} min ago',
|
|
195
219
|
'when.hours': '{count} h ago',
|
|
196
220
|
'when.yesterday': 'yesterday',
|
|
197
221
|
'when.days': '{count} d ago',
|
|
222
|
+
'notes.tabArticle': 'Article',
|
|
223
|
+
'notes.tabNotes': 'Notes',
|
|
224
|
+
'notes.tabsLabel': 'Article and notes',
|
|
225
|
+
'notes.take': 'Take a note',
|
|
226
|
+
'notes.sheetTitle': 'Take a note',
|
|
227
|
+
'notes.excerpt': 'Excerpt',
|
|
228
|
+
'notes.commentPlaceholder': 'What do you make of it? (optional)',
|
|
229
|
+
'notes.commentOptional': 'The comment is optional — the excerpt alone is kept too',
|
|
230
|
+
'notes.save': 'Save',
|
|
231
|
+
'notes.saveFailed': 'Could not save the note',
|
|
232
|
+
'notes.empty': 'No notes yet. Select a passage and choose “Take a note”.',
|
|
233
|
+
'notes.excerpts': 'Excerpts · in article order',
|
|
234
|
+
'notes.discussions': 'Discussed in',
|
|
235
|
+
'notes.noDiscussions': 'Not discussed yet. Bring your notes to the conversation.',
|
|
236
|
+
'notes.select': 'Select',
|
|
237
|
+
'notes.selectDone': 'Done',
|
|
238
|
+
'notes.selected': '{count} selected',
|
|
239
|
+
'notes.discuss': 'Discuss',
|
|
240
|
+
'notes.discussAll': 'Discuss with these notes',
|
|
241
|
+
'notes.edit': 'Edit comment',
|
|
242
|
+
'notes.delete': 'Delete',
|
|
243
|
+
'notes.deleteConfirm': 'Delete this note?',
|
|
244
|
+
'notes.notFound': 'The article changed — this passage is no longer in it',
|
|
245
|
+
'notes.inTranslation': 'Translation',
|
|
246
|
+
'notes.discussedIn': 'Back to the conversation',
|
|
247
|
+
'notes.draftHeading': 'My notes on this article ({count}):',
|
|
248
|
+
'notes.draftComment': 'My comment: ',
|
|
249
|
+
'notes.draftHint': '(article id {id} — reader_read reads the full text)',
|
|
250
|
+
'notes.untitledSession': 'Conversation',
|
|
251
|
+
'notes.badge': '{count} notes',
|
|
252
|
+
'notes.filter': 'Has notes',
|
|
253
|
+
'notes.replyAdd': '+ Add a line',
|
|
254
|
+
'notes.replyAgent': 'Conclusion',
|
|
255
|
+
'notes.replyUser': 'Me',
|
|
256
|
+
'notes.replyEdited': 'Me (rewritten from a conclusion)',
|
|
257
|
+
'notes.replyPlaceholder': 'Add to it, or say it your way',
|
|
258
|
+
'notes.replyEdit': 'Edit',
|
|
259
|
+
'notes.replyRemove': 'Remove',
|
|
260
|
+
'notes.discussionNotes': 'Discussion notes · newest first',
|
|
261
|
+
'notes.kindDiscussion': 'Discussion',
|
|
262
|
+
'notes.kindCheck': 'Quiz me',
|
|
263
|
+
'notes.noDiscussionNotes': 'None yet. Ask the agent to “keep the conclusions”, or let it quiz you.',
|
|
264
|
+
'notes.pointsCheck': 'Not quite there yet',
|
|
265
|
+
'notes.pointsDiscussion': 'Points',
|
|
266
|
+
'notes.check': 'Let the agent quiz me',
|
|
267
|
+
'notes.checkLead': 'Finished? Explaining it in your own words shows best what you have not got yet.',
|
|
268
|
+
'notes.checkDraft': 'Quiz me on “{title}” (article id {id}). I have finished it. Load the reader skill and run a quiz-me round: read the article and my notes with reader_read, play a student who has not read it, ask 3–5 explain-it-to-me questions in one message, and once I answer them all, explain against the article only what I have not got right (with ¶), then keep it with reader_note as a quiz-me note. No score.',
|
|
269
|
+
'notes.checkDraftNotes': 'My notes so far: {count}.',
|
|
270
|
+
'notes.backToSession': 'Back to the conversation',
|
|
271
|
+
'media.collapse': 'Collapse video',
|
|
272
|
+
'media.noFollow': 'Bilibili\'s player reports no playback time: a time plays from there by reloading the player, and the transcript does not follow playback',
|
|
273
|
+
'media.expand': 'Show video',
|
|
274
|
+
'media.captions': 'Captions',
|
|
275
|
+
'media.sourcePlatform': 'Source: platform captions',
|
|
276
|
+
'media.loading': 'Fetching captions…',
|
|
277
|
+
'media.noCaptions': 'This video has no captions — install the Local Transcription plugin to transcribe it on this machine',
|
|
278
|
+
'media.noCaptionsAudio': 'This audio has no captions — install the Local Transcription plugin to transcribe it on this machine',
|
|
279
|
+
'media.sourceLocal': 'Source: transcribed on this machine',
|
|
280
|
+
'media.play': 'Play',
|
|
281
|
+
'media.pause': 'Pause',
|
|
282
|
+
'media.startedAt': 'Played from {time}',
|
|
283
|
+
'media.tabTranscript': 'Transcript',
|
|
284
|
+
'media.autoTranslate': 'Translate when transcribed',
|
|
285
|
+
'media.translateAfter': 'Translation opens once the transcript is complete',
|
|
286
|
+
'media.chipTitle': 'Play from {time}',
|
|
287
|
+
'media.chipTitleReload': 'Play from {time} (the player reloads)',
|
|
288
|
+
'media.timeHint': 'Click the time before a paragraph to play the video from there',
|
|
289
|
+
'media.timeHintAudio': 'Click the time before a paragraph to play from there',
|
|
290
|
+
'media.timeHintOk': 'Got it',
|
|
291
|
+
'media.backToNow': 'Back to what is playing',
|
|
292
|
+
'media.speakerDefault': 'Speaker {letter}',
|
|
293
|
+
'media.speakerHint': 'Click a name to rename it',
|
|
294
|
+
'media.speakerRename': 'Rename this speaker (the whole transcript)',
|
|
295
|
+
'media.speakersRunning': 'telling the speakers apart…',
|
|
296
|
+
'media.cardPlatform': 'Captions',
|
|
297
|
+
'media.cardLocal': 'Transcribed',
|
|
298
|
+
'media.cardQueued': 'Queued',
|
|
299
|
+
'media.cardQueuedAhead': 'Queued #{count}',
|
|
300
|
+
'media.cardRunning': 'Transcribing {percent}%',
|
|
301
|
+
'media.cardFailed': 'Transcription failed',
|
|
302
|
+
'media.cardNone': 'No captions',
|
|
303
|
+
'media.cardNoneAuto': 'No captions · transcribes on open',
|
|
304
|
+
'media.localFetching': 'Getting the audio…',
|
|
305
|
+
'media.localQueued': 'Waiting to transcribe…',
|
|
306
|
+
'media.localQueuedAhead': 'Queued — {count} ahead',
|
|
307
|
+
'media.localRunning': 'Transcribing on this machine · {percent}% — what is done can be read now',
|
|
308
|
+
'media.localEta': 'about {minutes} min left',
|
|
309
|
+
'media.localStop': 'Cancel',
|
|
310
|
+
'media.localStopped': 'Transcription stopped',
|
|
311
|
+
'media.localFailed': 'Transcription failed: {error}',
|
|
312
|
+
'media.localRetry': 'Transcribe again',
|
|
313
|
+
'media.localModels': 'The first local transcription downloads the speech models (about {size} MB, once). After that it runs by itself.',
|
|
314
|
+
'media.localModelsGo': 'Download and transcribe',
|
|
315
|
+
'media.failed': 'Captions could not be fetched',
|
|
316
|
+
'media.kindVideo': 'Video',
|
|
317
|
+
'media.kindAudio': 'Audio',
|
|
318
|
+
'notes.sync': 'Sync to canvas',
|
|
319
|
+
'notes.syncTitle': 'Sync to canvas · {count}',
|
|
320
|
+
'notes.syncLast': 'used last',
|
|
321
|
+
'notes.syncCards': '{count} cards',
|
|
322
|
+
'notes.syncReplies': 'Bring the conclusions under them along',
|
|
323
|
+
'notes.syncKind': 'This canvas has no Fragment category — pick one:',
|
|
324
|
+
'notes.syncDone': 'Synced to “{canvas}”',
|
|
325
|
+
'notes.syncOpen': 'Open',
|
|
326
|
+
'notes.syncFailed': 'The sync did not go through: {reason}',
|
|
327
|
+
'notes.syncedIn': 'On “{canvas}”',
|
|
328
|
+
'notes.syncNoCanvas': 'No canvas yet — create one on the canvas tab first.',
|
|
329
|
+
'notes.syncConclusion': 'Conclusion: ',
|
|
330
|
+
'notes.syncOriginal': 'original',
|
|
331
|
+
'notes.syncRun': 'Sync',
|
|
198
332
|
};
|
|
199
333
|
export const zh = {
|
|
200
334
|
'tab.label': '灵感空间',
|
|
@@ -209,6 +343,12 @@ export const zh = {
|
|
|
209
343
|
'action.filter': '筛选',
|
|
210
344
|
'action.fetchBody': '抓取正文',
|
|
211
345
|
'action.more': '更多操作',
|
|
346
|
+
'translate.wallOff': '关闭翻译',
|
|
347
|
+
'chip.unread': '未读',
|
|
348
|
+
'chip.more': '筛选…',
|
|
349
|
+
'state.incompleteCount': '{count} 个来源未完整',
|
|
350
|
+
'action.showList': '查看',
|
|
351
|
+
'action.foldList': '收起',
|
|
212
352
|
'action.addTag': '打标签…',
|
|
213
353
|
'action.translate': '翻译',
|
|
214
354
|
'translate.view': '翻译视图',
|
|
@@ -220,6 +360,7 @@ export const zh = {
|
|
|
220
360
|
'translate.preparing': '正在准备中文语言包',
|
|
221
361
|
'translate.working': '翻译中',
|
|
222
362
|
'translate.local': '浏览器本地 · 中文(简体)',
|
|
363
|
+
'translate.stored': '已保存的译文 · 中文(简体)',
|
|
223
364
|
'translate.failed': '翻译中断:{reason}',
|
|
224
365
|
'translate.failedAll': '各批次都没翻出来 —— 可从地球菜单里重试',
|
|
225
366
|
'translate.nothing': '这段正文里没有可翻译的散文',
|
|
@@ -250,7 +391,6 @@ export const zh = {
|
|
|
250
391
|
'action.back': '返回',
|
|
251
392
|
'action.copyLink': '复制链接',
|
|
252
393
|
'action.openExternal': '在浏览器打开原文',
|
|
253
|
-
'action.quote': '引用',
|
|
254
394
|
'action.manage': '订阅管理',
|
|
255
395
|
'action.recent': '最近阅读',
|
|
256
396
|
'recent.title': '最近阅读',
|
|
@@ -276,6 +416,7 @@ export const zh = {
|
|
|
276
416
|
'verdict.unsupportedContent': '这个地址返回的既不是订阅源也不是网页。',
|
|
277
417
|
'verdict.fetchFailed': '抓取失败。',
|
|
278
418
|
'preview.blocked': '这个站点拒绝自动抓取(返回的是验证页)',
|
|
419
|
+
'verdict.willRender': '打开这条时会改用渲染抓取(无头浏览器)再试。',
|
|
279
420
|
'preview.login': '这个地址需要登录(机构代理或单点登录),本插件取不到正文',
|
|
280
421
|
'preview.unsupportedType': '这个地址不是网页(是 PDF 或其它文件)',
|
|
281
422
|
'preview.redirected': '这个地址会跳到另一个站点,跨站跳转在本地无法跟随',
|
|
@@ -285,6 +426,11 @@ export const zh = {
|
|
|
285
426
|
'preview.unreadable': '这个页面是 JS 应用且有反爬,服务器端读不到内容 —— 如果它有 arxiv 版,直接粘贴 arxiv 链接',
|
|
286
427
|
'detail.linkOnlyBadge': '仅链接',
|
|
287
428
|
'detail.fetchingBody': '正在抓取正文…',
|
|
429
|
+
'detail.phase.downloading': '正在下载页面…',
|
|
430
|
+
'detail.phase.extracting': '正在解析正文…',
|
|
431
|
+
'detail.phase.storing': '正在保存正文…',
|
|
432
|
+
'detail.fetchElapsed': '{seconds} 秒',
|
|
433
|
+
'add.busy': '正在识别并抓取链接…',
|
|
288
434
|
'detail.refetch': '重新抓取',
|
|
289
435
|
'detail.refetching': '抓取中',
|
|
290
436
|
'detail.refetchTitle': '重新抓取这篇正文 —— 现在看到的是之前抓下来的版本',
|
|
@@ -345,8 +491,6 @@ export const zh = {
|
|
|
345
491
|
'detail.summaryOnly': '这条订阅源只发布了摘要 —— 全文在原文页面上。',
|
|
346
492
|
'detail.resolvedFrom': '这条由粘贴的链接({url})解析而来 —— 点击打开原链接',
|
|
347
493
|
'detail.alsoFrom': '同一来源',
|
|
348
|
-
'detail.composerLabel': '当前会话草稿',
|
|
349
|
-
'detail.composerEmpty': '(空)',
|
|
350
494
|
'foot.refreshedAt': '刷出于 {when}',
|
|
351
495
|
'foot.scheduled': '每日 {time}',
|
|
352
496
|
'foot.never': '尚未刷新',
|
|
@@ -368,15 +512,139 @@ export const zh = {
|
|
|
368
512
|
'fetch.failedOpenTitle': '抓取失败:{reason} —— 点一下看详情',
|
|
369
513
|
'fetch.failedFinalTitle': '抓取失败:{reason}',
|
|
370
514
|
'detail.renderFetch': '渲染抓取',
|
|
515
|
+
'detail.renderingWall': '这个站点拦截了普通抓取,正在改用无头浏览器渲染(约 15–30 秒)…',
|
|
516
|
+
'detail.renderFailed': '渲染抓取也没拿到正文({reason})。',
|
|
371
517
|
'detail.rendering': '正在用浏览器渲染这一页——脚本绘制的插图会在渲染后出现(首次较慢)…',
|
|
372
518
|
'quote.copied': '已复制',
|
|
373
519
|
'quote.copyFailed': '剪贴板不可用',
|
|
374
520
|
'quote.quoted': '已引用 {count} 字',
|
|
375
|
-
'
|
|
376
|
-
'
|
|
521
|
+
'add.fetching': '抓取中…',
|
|
522
|
+
'notes.checkShort': '考考我',
|
|
523
|
+
'polish.action': '✦ 让 Agent 润色译文',
|
|
524
|
+
'polish.draft': '请润色《{title}》(文章 id {id},共 {count} 段)的中文译文:加载 reader 技能,用 reader_read 分批读原文,从原文整段翻译,每批用 reader_translate 写回,直到全部写完。忠实原意、读起来像中文;专名、术语和说话人前后一致。\n我的要求:',
|
|
525
|
+
'polish.continueDraft': '请从 ¶{from} 接着润色《{title}》(文章 id {id},共 {count} 段)的中文译文:用 reader_read from={from} 读,用 reader_translate 写回,直到全部写完。',
|
|
526
|
+
'polish.tag': '润色',
|
|
527
|
+
'polish.tagTitle': '这一段改回机翻',
|
|
528
|
+
'polish.running': 'Agent 润色中 · 已润色 {done} / {total} 段',
|
|
529
|
+
'polish.stalled': '已润色 {done} / {total} 段',
|
|
530
|
+
'polish.continue': '继续润色剩下的 {count} 段',
|
|
531
|
+
'polish.done': '✦ 已由 Agent 润色 {done} 段',
|
|
532
|
+
'polish.showMachine': '看机翻',
|
|
533
|
+
'polish.showPolish': '看润色版',
|
|
534
|
+
'polish.awaiting': '润色请求已放进输入框,发送后这里会显示进度',
|
|
377
535
|
'when.justNow': '刚刚',
|
|
378
536
|
'when.minutes': '{count} 分钟前',
|
|
379
537
|
'when.hours': '{count} 小时前',
|
|
380
538
|
'when.yesterday': '昨天',
|
|
381
539
|
'when.days': '{count} 天前',
|
|
540
|
+
'notes.tabArticle': '正文',
|
|
541
|
+
'notes.tabNotes': '笔记',
|
|
542
|
+
'notes.tabsLabel': '正文与笔记',
|
|
543
|
+
'notes.take': '记笔记',
|
|
544
|
+
'notes.sheetTitle': '记笔记',
|
|
545
|
+
'notes.excerpt': '摘抄',
|
|
546
|
+
'notes.commentPlaceholder': '写点你的想法(可空)',
|
|
547
|
+
'notes.commentOptional': '评论可空,只存摘抄也行',
|
|
548
|
+
'notes.save': '保存',
|
|
549
|
+
'notes.saveFailed': '笔记没有存上',
|
|
550
|
+
'notes.empty': '还没有笔记。在正文里选中一段,点「记笔记」。',
|
|
551
|
+
'notes.excerpts': '摘抄笔记 · 按原文顺序',
|
|
552
|
+
'notes.discussions': '讨论过这篇的会话',
|
|
553
|
+
'notes.noDiscussions': '还没有讨论过。带着笔记去会话里聊聊。',
|
|
554
|
+
'notes.select': '选择',
|
|
555
|
+
'notes.selectDone': '完成',
|
|
556
|
+
'notes.selected': '已选 {count} 条',
|
|
557
|
+
'notes.discuss': '带着讨论',
|
|
558
|
+
'notes.discussAll': '带着笔记讨论',
|
|
559
|
+
'notes.edit': '编辑评论',
|
|
560
|
+
'notes.delete': '删除',
|
|
561
|
+
'notes.deleteConfirm': '删除这条笔记?',
|
|
562
|
+
'notes.notFound': '原文已变,找不到这一句',
|
|
563
|
+
'notes.inTranslation': '译文',
|
|
564
|
+
'notes.discussedIn': '回到会话',
|
|
565
|
+
'notes.draftHeading': '我在这篇里的笔记({count} 条):',
|
|
566
|
+
'notes.draftComment': '我的评论:',
|
|
567
|
+
'notes.draftHint': '(文章 id:{id},可用 reader_read 读全文)',
|
|
568
|
+
'notes.untitledSession': '会话',
|
|
569
|
+
'notes.badge': '笔记 {count}',
|
|
570
|
+
'notes.filter': '有笔记',
|
|
571
|
+
'notes.replyAdd': '+ 追加一句',
|
|
572
|
+
'notes.replyAgent': '讨论结论',
|
|
573
|
+
'notes.replyUser': '我',
|
|
574
|
+
'notes.replyEdited': '我(改自讨论结论)',
|
|
575
|
+
'notes.replyPlaceholder': '补充一句,或换成你自己的说法',
|
|
576
|
+
'notes.replyEdit': '改',
|
|
577
|
+
'notes.replyRemove': '删',
|
|
578
|
+
'notes.discussionNotes': '讨论笔记 · 最近的在前',
|
|
579
|
+
'notes.kindDiscussion': '讨论',
|
|
580
|
+
'notes.kindCheck': '考考我',
|
|
581
|
+
'notes.noDiscussionNotes': '还没有。讨论时说一句「把结论记到笔记」,或者让 Agent 考考你。',
|
|
582
|
+
'notes.pointsCheck': '没理解到位的',
|
|
583
|
+
'notes.pointsDiscussion': '要点',
|
|
584
|
+
'notes.check': '让Agent考考我',
|
|
585
|
+
'notes.checkLead': '读完了?用自己的话讲一遍,最能看出哪里没懂。',
|
|
586
|
+
'notes.checkDraft': '考考我:《{title}》(文章 id:{id})。我读完了,请加载 reader 技能,按「考考我」来:用 reader_read 读这篇和我的笔记,当一个没读过的学生,一次问我 3–5 个「讲给我听」的问题;我一条消息答完后,对照原文只讲我没讲到位的地方(带 ¶),再用 reader_note 记一条考考我笔记。不打分。',
|
|
587
|
+
'notes.checkDraftNotes': '我目前记了 {count} 条笔记。',
|
|
588
|
+
'notes.backToSession': '回到会话',
|
|
589
|
+
'media.collapse': '收起视频',
|
|
590
|
+
'media.noFollow': 'B 站播放器不报告播放时间:点时间会重新加载播放器、从那里播放,播放时转写稿不会自动跟随',
|
|
591
|
+
'media.expand': '展开视频',
|
|
592
|
+
'media.captions': '字幕',
|
|
593
|
+
'media.sourcePlatform': '来源:平台字幕',
|
|
594
|
+
'media.loading': '正在获取字幕…',
|
|
595
|
+
'media.noCaptions': '该视频无字幕;装上「本机转写」插件后可在本机转写',
|
|
596
|
+
'media.noCaptionsAudio': '该音频无字幕;装上「本机转写」插件后可在本机转写',
|
|
597
|
+
'media.sourceLocal': '来源:本机转写',
|
|
598
|
+
'media.play': '播放',
|
|
599
|
+
'media.pause': '暂停',
|
|
600
|
+
'media.startedAt': '从 {time} 播放',
|
|
601
|
+
'media.tabTranscript': '转写稿',
|
|
602
|
+
'media.autoTranslate': '转完自动翻译',
|
|
603
|
+
'media.translateAfter': '转写完成后可以翻译',
|
|
604
|
+
'media.chipTitle': '从 {time} 开始播放',
|
|
605
|
+
'media.chipTitleReload': '从 {time} 开始播放(会重新加载播放器)',
|
|
606
|
+
'media.timeHint': '点段落前的时间,视频就从那里播放',
|
|
607
|
+
'media.timeHintAudio': '点段落前的时间,就从那里播放',
|
|
608
|
+
'media.timeHintOk': '知道了',
|
|
609
|
+
'media.backToNow': '回到正在播放',
|
|
610
|
+
'media.speakerDefault': '说话人 {letter}',
|
|
611
|
+
'media.speakerHint': '点名字可以改名',
|
|
612
|
+
'media.speakerRename': '给这位说话人改名(整期替换)',
|
|
613
|
+
'media.speakersRunning': '正在区分说话人…',
|
|
614
|
+
'media.cardPlatform': '有字幕',
|
|
615
|
+
'media.cardLocal': '本机转写',
|
|
616
|
+
'media.cardQueued': '排队中',
|
|
617
|
+
'media.cardQueuedAhead': '排队第 {count}',
|
|
618
|
+
'media.cardRunning': '转写中 {percent}%',
|
|
619
|
+
'media.cardFailed': '转写失败',
|
|
620
|
+
'media.cardNone': '无字幕',
|
|
621
|
+
'media.cardNoneAuto': '无字幕 · 打开时转写',
|
|
622
|
+
'media.localFetching': '正在获取音频…',
|
|
623
|
+
'media.localQueued': '等待转写…',
|
|
624
|
+
'media.localQueuedAhead': '排队中 · 前面还有 {count} 个',
|
|
625
|
+
'media.localRunning': '本机转写中 · {percent}% · 已转写的部分可以先读',
|
|
626
|
+
'media.localEta': '约还需 {minutes} 分钟',
|
|
627
|
+
'media.localStop': '取消',
|
|
628
|
+
'media.localStopped': '已停止转写',
|
|
629
|
+
'media.localFailed': '转写失败:{error}',
|
|
630
|
+
'media.localRetry': '重新转写',
|
|
631
|
+
'media.localModels': '第一次本机转写需要下载语音模型(约 {size} MB,只需一次),之后都自动进行。',
|
|
632
|
+
'media.localModelsGo': '下载并转写',
|
|
633
|
+
'media.failed': '字幕获取失败',
|
|
634
|
+
'media.kindVideo': '视频',
|
|
635
|
+
'media.kindAudio': '音频',
|
|
636
|
+
'notes.sync': '同步到画布',
|
|
637
|
+
'notes.syncTitle': '同步到画布 · {count} 条',
|
|
638
|
+
'notes.syncLast': '上次用的',
|
|
639
|
+
'notes.syncCards': '{count} 张卡',
|
|
640
|
+
'notes.syncReplies': '把下面挂的讨论结论一起带上',
|
|
641
|
+
'notes.syncKind': '这块画布没有「灵感」分类,选一个:',
|
|
642
|
+
'notes.syncDone': '已同步到《{canvas}》',
|
|
643
|
+
'notes.syncOpen': '打开',
|
|
644
|
+
'notes.syncFailed': '同步没有成功:{reason}',
|
|
645
|
+
'notes.syncedIn': '已在《{canvas}》',
|
|
646
|
+
'notes.syncNoCanvas': '还没有画布——先在画布页建一块。',
|
|
647
|
+
'notes.syncConclusion': '讨论结论:',
|
|
648
|
+
'notes.syncOriginal': '原文',
|
|
649
|
+
'notes.syncRun': '同步',
|
|
382
650
|
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notes in the browser: turning a selection into an excerpt, finding excerpts
|
|
3
|
+
* again for the highlight, the article snapshot the model reads, and the draft
|
|
4
|
+
* a discussion starts from.
|
|
5
|
+
*
|
|
6
|
+
* Everything here reads the DOM and never writes it. The article body is
|
|
7
|
+
* `translate.ts`'s to decorate (it splits text nodes into sentence spans), so
|
|
8
|
+
* a note highlight that wrapped text in `<mark>` would fight it on every view
|
|
9
|
+
* switch. Highlights go through the CSS Custom Highlight API instead: ranges
|
|
10
|
+
* over whatever text nodes are on screen, recomputed after each rebuild. A
|
|
11
|
+
* browser without the API shows no highlight — the notes themselves still work.
|
|
12
|
+
*
|
|
13
|
+
* Blocks are the article's top-level children minus `translate.ts`'s reveals —
|
|
14
|
+
* the reading position's unit (`articleBlocks` in the pane), so ¶n here, in the
|
|
15
|
+
* snapshot, and in a note's anchor all mean the same paragraph.
|
|
16
|
+
*
|
|
17
|
+
* @module @khorsheed/dsh-reader/client/notes
|
|
18
|
+
*/
|
|
19
|
+
import type { ReaderNote } from '../notes-types.ts';
|
|
20
|
+
import type { BuiltArticle } from './translate.ts';
|
|
21
|
+
/** The highlight registry names (the pane's stylesheet paints them). */
|
|
22
|
+
export declare const NOTE_HIGHLIGHT = "reader-note";
|
|
23
|
+
export declare const NOTE_FLASH_HIGHLIGHT = "reader-note-flash";
|
|
24
|
+
/** Collapse whitespace the way stored excerpts are collapsed. */
|
|
25
|
+
export declare function normalizeText(value: string): string;
|
|
26
|
+
/** The article's blocks: top-level children, reveals excluded. */
|
|
27
|
+
export declare function blockElements(article: Element): HTMLElement[];
|
|
28
|
+
/**
|
|
29
|
+
* The block a node sits in, as an index into {@link blockElements}.
|
|
30
|
+
*
|
|
31
|
+
* A node inside a sibling-placed reveal belongs to the block the reveal
|
|
32
|
+
* follows; one inside an inside-placed reveal is already inside its block.
|
|
33
|
+
*/
|
|
34
|
+
export declare function blockIndexOf(article: Element, node: Node): number;
|
|
35
|
+
/** What a selection becomes before the reader adds a comment. */
|
|
36
|
+
export interface NoteSelection {
|
|
37
|
+
readonly block: number;
|
|
38
|
+
readonly start?: number;
|
|
39
|
+
readonly quote: string;
|
|
40
|
+
readonly translated?: string;
|
|
41
|
+
/** A transcript paragraph's start (seconds), when the article is a transcript. */
|
|
42
|
+
readonly time?: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read the current selection as an excerpt of this article.
|
|
46
|
+
*
|
|
47
|
+
* Selected in the translated view, the excerpt is the ORIGINAL of every
|
|
48
|
+
* sentence the selection touched, and the selected translation rides along —
|
|
49
|
+
* the stored quote is always original text (decision D8). Selected in the
|
|
50
|
+
* original (or in a reveal's original lines), the selection is the quote.
|
|
51
|
+
*
|
|
52
|
+
* @param article - the rendered article root.
|
|
53
|
+
* @param built - the translation segmentation, when there is one.
|
|
54
|
+
* @param selection - the selection to read (defaults to the window's).
|
|
55
|
+
* @returns the excerpt, or `null` when nothing in this article is selected.
|
|
56
|
+
*/
|
|
57
|
+
export declare function captureSelection(article: Element, built: BuiltArticle | null, selection?: Selection | null): NoteSelection | null;
|
|
58
|
+
/**
|
|
59
|
+
* The article's original prose, one string per block, for the host snapshot.
|
|
60
|
+
*
|
|
61
|
+
* Parsed from the stored markup rather than read off the screen: the screen may
|
|
62
|
+
* be showing a translation, and the model must read what the author wrote.
|
|
63
|
+
*
|
|
64
|
+
* @param html - the article markup the pane renders.
|
|
65
|
+
* @returns the blocks' text (a bare-text body is one block).
|
|
66
|
+
*/
|
|
67
|
+
export declare function paragraphsOf(html: string, speakerLabel?: (speaker: number) => string): string[];
|
|
68
|
+
/**
|
|
69
|
+
* Find a normalized string inside an element's text, as a DOM Range.
|
|
70
|
+
*
|
|
71
|
+
* The text is flattened with whitespace collapsed (the stored quote's form),
|
|
72
|
+
* keeping a map from each flattened character back to its node and offset.
|
|
73
|
+
*
|
|
74
|
+
* @param root - where to search.
|
|
75
|
+
* @param needle - the normalized text to find.
|
|
76
|
+
* @param skip - elements whose text does not count.
|
|
77
|
+
* @returns the range, or `null`.
|
|
78
|
+
*/
|
|
79
|
+
export declare function findText(root: Element, needle: string, skip?: (element: Element) => boolean): Range | null;
|
|
80
|
+
/** True when two texts share content: one holds the other, or one runs into the other. */
|
|
81
|
+
export declare function overlaps(left: string, right: string, edge?: number): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Where each note is on screen right now.
|
|
84
|
+
*
|
|
85
|
+
* The anchored block is tried first; when the excerpt is not there (the
|
|
86
|
+
* article was refetched and moved), every block is searched. A note found
|
|
87
|
+
* nowhere maps to an empty list — the notes view says so.
|
|
88
|
+
*
|
|
89
|
+
* @param article - the rendered article root.
|
|
90
|
+
* @param built - the translation segmentation, when there is one.
|
|
91
|
+
* @param notes - the article's notes.
|
|
92
|
+
*/
|
|
93
|
+
export declare function noteRanges(article: Element, built: BuiltArticle | null, notes: readonly ReaderNote[]): Map<string, Range[]>;
|
|
94
|
+
/**
|
|
95
|
+
* Paint (or clear) one named highlight.
|
|
96
|
+
*
|
|
97
|
+
* @param name - the registry name.
|
|
98
|
+
* @param ranges - the ranges to paint; empty clears it.
|
|
99
|
+
*/
|
|
100
|
+
export declare function paintHighlight(name: string, ranges: readonly Range[]): void;
|
|
101
|
+
/** The article identity a discussion draft names. */
|
|
102
|
+
export interface NotesDraftSource {
|
|
103
|
+
readonly entryId: string;
|
|
104
|
+
readonly title: string;
|
|
105
|
+
readonly source: string;
|
|
106
|
+
readonly link?: string;
|
|
107
|
+
}
|
|
108
|
+
/** The localized words a draft needs. */
|
|
109
|
+
export interface NotesDraftWords {
|
|
110
|
+
readonly heading: (count: number) => string;
|
|
111
|
+
readonly comment: string;
|
|
112
|
+
readonly hint: (entryId: string) => string;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The discussion draft: the article, then each note as a quote with its comment.
|
|
116
|
+
*
|
|
117
|
+
* Notes carry their ¶ and id so the model can match them to `reader_read`'s
|
|
118
|
+
* output; the closing hint names the article id for the same reason.
|
|
119
|
+
*
|
|
120
|
+
* @param source - the article.
|
|
121
|
+
* @param notes - the notes to bring, in article order.
|
|
122
|
+
* @param words - the localized fragments.
|
|
123
|
+
*/
|
|
124
|
+
export declare function formatNotesDraft(source: NotesDraftSource, notes: readonly ReaderNote[], words: NotesDraftWords): string;
|
|
125
|
+
/**
|
|
126
|
+
* Where a quote-menu「记笔记」lands.
|
|
127
|
+
*
|
|
128
|
+
* The quote menu lives in another package and hands its actions only the
|
|
129
|
+
* selected text; the pane that owns the selected article is found here, by
|
|
130
|
+
* asking each mounted pane whether the selection is inside its article.
|
|
131
|
+
*/
|
|
132
|
+
export interface NoteTarget {
|
|
133
|
+
/** True when this pane's article holds the node. */
|
|
134
|
+
readonly owns: (node: Node) => boolean;
|
|
135
|
+
/** Open the note sheet for the current selection. */
|
|
136
|
+
readonly start: () => void;
|
|
137
|
+
}
|
|
138
|
+
/** Register a pane as a note target; returns the disposer. */
|
|
139
|
+
export declare function registerNoteTarget(target: NoteTarget): () => void;
|
|
140
|
+
/** The pane whose article holds the current selection, when one does. */
|
|
141
|
+
export declare function noteTargetForSelection(selection?: Selection | null): NoteTarget | undefined;
|
|
142
|
+
//# sourceMappingURL=notes.d.ts.map
|