@lincyaw/kernel 1.0.0-rc.0 → 1.0.0-rc.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/README.md +7 -7
- package/package.json +6 -2
- package/src/access.ts +18 -0
- package/src/anchors.ts +5 -1
- package/src/authoring.ts +56 -0
- package/src/blocks.ts +0 -8
- package/src/component-code.ts +6 -0
- package/src/contracts/agent.ts +5 -82
- package/src/contracts/anchors.ts +46 -0
- package/src/contracts/authoring-actions.ts +60 -0
- package/src/contracts/authoring-clients.ts +213 -0
- package/src/contracts/authoring.ts +180 -0
- package/src/contracts/checks.ts +29 -0
- package/src/contracts/classroom.ts +49 -0
- package/src/contracts/diagnostics.ts +37 -0
- package/src/contracts/document-audio.ts +150 -0
- package/src/contracts/document-comments.ts +31 -0
- package/src/contracts/document-history.ts +63 -0
- package/src/contracts/document-image.ts +32 -0
- package/src/contracts/document-reads.ts +57 -0
- package/src/contracts/document-voice.ts +44 -0
- package/src/contracts/document.ts +240 -0
- package/src/contracts/execution.ts +4 -5
- package/src/contracts/locale.ts +3 -0
- package/src/contracts/offline.ts +2 -0
- package/src/contracts/plugin.ts +183 -0
- package/src/contracts/presenter-panels.ts +17 -0
- package/src/contracts/preview.ts +305 -310
- package/src/contracts/production-assets.ts +61 -0
- package/src/contracts/production.ts +109 -0
- package/src/contracts/projects.ts +59 -0
- package/src/contracts/server.ts +1 -51
- package/src/contracts/speech-settings.ts +16 -0
- package/src/contracts/speech.ts +82 -70
- package/src/contracts/steps.ts +15 -0
- package/src/contracts/transcription-adoption.ts +79 -0
- package/src/contracts/transcription.ts +112 -0
- package/src/contracts/voice.ts +97 -0
- package/src/contracts/voiceover-execution.ts +68 -0
- package/src/contracts/workspace.ts +458 -0
- package/src/contracts.ts +28 -278
- package/src/diagnostics.ts +7 -0
- package/src/document-authoring-actions.ts +47 -0
- package/src/document-caption-timings.ts +64 -0
- package/src/document-colors.ts +25 -0
- package/src/document-comments.ts +118 -0
- package/src/document-components.ts +251 -0
- package/src/document-operations.ts +132 -0
- package/src/document-pages.ts +215 -0
- package/src/document-reads.ts +81 -0
- package/src/document-schema.ts +85 -0
- package/src/document-voices.ts +153 -0
- package/src/document.ts +252 -0
- package/src/durations.ts +7 -0
- package/src/errors.ts +8 -0
- package/src/execution.ts +5 -1
- package/src/fingerprint.ts +1 -1
- package/src/follow.ts +26 -30
- package/src/git.ts +1 -1
- package/src/index.ts +66 -13
- package/src/ir.ts +54 -5
- package/src/jupyter-kernel.ts +9 -0
- package/src/kernel.ts +38 -25
- package/src/keyboard.ts +43 -0
- package/src/locale.ts +13 -0
- package/src/lock.ts +4 -8
- package/src/logging.ts +1 -1
- package/src/{markdown.ts → lower-myst.ts} +2 -1
- package/src/node/assets.ts +23 -0
- package/src/node/authoring-actions.ts +50 -0
- package/src/node/authoring-clients.ts +14 -0
- package/src/node/document-audio.ts +22 -0
- package/src/node/document-history.ts +11 -0
- package/src/node/document-image.ts +8 -0
- package/src/node/document-reads.ts +10 -0
- package/src/node/document-voice.ts +7 -0
- package/src/node/ffmpeg.ts +29 -0
- package/src/node/file-version.ts +6 -0
- package/src/node/fonts.ts +22 -0
- package/src/node/index.ts +29 -133
- package/src/node/installed-packages.ts +11 -0
- package/src/node/lines.ts +24 -0
- package/src/node/locale.ts +7 -0
- package/src/node/network.ts +11 -0
- package/src/node/operator-speech.ts +91 -0
- package/src/node/packages.ts +74 -0
- package/src/node/paths.ts +7 -0
- package/src/node/projects.ts +7 -0
- package/src/node/resources.ts +86 -0
- package/src/node/revision.ts +6 -0
- package/src/node/run-directory.ts +70 -0
- package/src/node/transcription-adoption.ts +9 -0
- package/src/node/transcription.ts +10 -0
- package/src/node/voiceover-routes.ts +73 -0
- package/src/node/voiceover.ts +199 -0
- package/src/page-source.ts +359 -0
- package/src/parameters.ts +81 -0
- package/src/preview-edit.ts +18 -0
- package/src/preview-events.ts +57 -0
- package/src/preview-frame.ts +1220 -0
- package/src/props.ts +17 -0
- package/src/push-stream.ts +67 -0
- package/src/reading.ts +12 -7
- package/src/recording.ts +38 -0
- package/src/renderer-components.ts +30 -0
- package/src/responses.ts +49 -0
- package/src/run-outputs.ts +15 -0
- package/src/runner.py +3 -3
- package/src/services.ts +6 -6
- package/src/slides.ts +36 -0
- package/src/speech-words.ts +76 -0
- package/src/speech.ts +13 -0
- package/src/step-keys.ts +38 -0
- package/src/steps.ts +7 -0
- package/src/transcription-adoption.ts +105 -0
- package/src/uri-decoding.ts +8 -0
- package/src/urls.ts +13 -0
- package/src/views.ts +47 -0
- package/src/voice.ts +86 -0
package/src/views.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { irBlocks } from './blocks.ts'
|
|
2
|
+
import type { IRBlock, IRDocument, IRSection } from './ir.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Closes the gaps that removed blocks leave in a section's steps. With `stepMap`, the section records
|
|
6
|
+
* the new number of each old step, so positions in the whole document can be mapped to it.
|
|
7
|
+
*/
|
|
8
|
+
export function compactSteps(section: IRSection, options: { stepMap?: boolean } = {}): IRSection {
|
|
9
|
+
// Explicit authoring steps can carry narration and production timing even
|
|
10
|
+
// when this renderer has no visual change. Keep their identities and states.
|
|
11
|
+
if (section.stepIds) return options.stepMap ? { ...section, stepMap: section.stepIds.map((_, index) => index) } : section
|
|
12
|
+
const used = new Set([0])
|
|
13
|
+
for (const block of irBlocks(section.blocks)) {
|
|
14
|
+
if (block.role !== 'visual') continue
|
|
15
|
+
for (let step = block.at; step <= block.at + (block.span ?? 0); step++) used.add(step)
|
|
16
|
+
if (block.until !== undefined) used.add(block.until)
|
|
17
|
+
}
|
|
18
|
+
if (used.size > section.steps) return section
|
|
19
|
+
const stepMap: number[] = []
|
|
20
|
+
for (let step = 0, n = -1; step <= section.steps; step++) stepMap.push(used.has(step) ? ++n : n)
|
|
21
|
+
const map = (step: number) => stepMap[Math.min(step, section.steps)]!
|
|
22
|
+
const renumber = (blocks: IRBlock[]): IRBlock[] => blocks.map(block => ({
|
|
23
|
+
...block,
|
|
24
|
+
at: map(block.at),
|
|
25
|
+
...block.until === undefined ? {} : { until: map(block.until) },
|
|
26
|
+
...block.node.type === 'component' && block.node.children ? { node: { ...block.node, children: renumber(block.node.children) } } : {},
|
|
27
|
+
}))
|
|
28
|
+
return { ...section, steps: map(section.steps), ...options.stepMap ? { stepMap } : {}, blocks: renumber(section.blocks) }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The document as one renderer sees it: blocks limited to other renderers are gone, the steps closed up. */
|
|
32
|
+
export function viewFor(doc: IRDocument, renderer: string): IRDocument {
|
|
33
|
+
let changed = false
|
|
34
|
+
const keep = (blocks: IRBlock[]): IRBlock[] => blocks.flatMap((block) => {
|
|
35
|
+
if (block.only && !block.only.includes(renderer)) {
|
|
36
|
+
changed = true
|
|
37
|
+
return []
|
|
38
|
+
}
|
|
39
|
+
return block.node.type === 'component' && block.node.children ? [{ ...block, node: { ...block.node, children: keep(block.node.children) } }] : [block]
|
|
40
|
+
})
|
|
41
|
+
const sections = doc.sections.map((section) => {
|
|
42
|
+
changed = false
|
|
43
|
+
const blocks = keep(section.blocks)
|
|
44
|
+
return changed ? compactSteps({ ...section, blocks }, { stepMap: true }) : section
|
|
45
|
+
})
|
|
46
|
+
return { ...doc, sections }
|
|
47
|
+
}
|
package/src/voice.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { irBlocks } from './blocks.ts'
|
|
2
|
+
import { isRecord } from './errors.ts'
|
|
3
|
+
import { fnv1a64 } from './fingerprint.ts'
|
|
4
|
+
import type { IRDocument, IRSection, MdastNode } from './ir.ts'
|
|
5
|
+
import { stepKeys } from './step-keys.ts'
|
|
6
|
+
import type { SpeechVoice } from './contracts/speech.ts'
|
|
7
|
+
import type { ResolvedVoice, VoiceOverrides, VoiceTarget } from './contracts/voice.ts'
|
|
8
|
+
|
|
9
|
+
export function speechVoice(value: unknown): SpeechVoice | undefined {
|
|
10
|
+
if (!isRecord(value) || typeof value.voice !== 'string' || !value.voice.trim()) return undefined
|
|
11
|
+
if (value.engine !== undefined && (typeof value.engine !== 'string' || !value.engine.trim())) return undefined
|
|
12
|
+
if (value.rate !== undefined && (typeof value.rate !== 'number' || !Number.isFinite(value.rate) || value.rate <= 0)) return undefined
|
|
13
|
+
if (value.style !== undefined && typeof value.style !== 'string') return undefined
|
|
14
|
+
return { voice: value.voice, ...typeof value.engine === 'string' ? { engine: value.engine } : {}, ...typeof value.rate === 'number' ? { rate: value.rate } : {}, ...typeof value.style === 'string' ? { style: value.style } : {} }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function voiceTarget(section: Pick<IRSection, 'id' | 'steps' | 'stepIds' | 'blocks'>, step: number): VoiceTarget {
|
|
18
|
+
if (!Number.isSafeInteger(step) || step < 0 || step > section.steps) throw new Error('voice-target')
|
|
19
|
+
return { section: section.id, step, binding: fnv1a64(JSON.stringify([section.id, stepKeys(section)])) }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function documentVoice(doc: IRDocument): SpeechVoice | undefined {
|
|
23
|
+
if (doc.production) return doc.production.defaults.voiceSource?.type === 'tts' ? doc.production.defaults.voiceSource.voice : undefined
|
|
24
|
+
return speechVoice(doc.config.renderers.lecture?.voice)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function voiceOverrides(doc: IRDocument): VoiceOverrides {
|
|
28
|
+
if (doc.production) {
|
|
29
|
+
const production = doc.production
|
|
30
|
+
const pages = Object.fromEntries(Object.entries(production.sectionOverrides ?? {}).flatMap(([id, value]) => value.voiceSource?.type === 'tts' ? [[id, value.voiceSource.voice]] : []))
|
|
31
|
+
const steps = doc.sections.flatMap(section => (section.stepIds ?? []).flatMap((id, step) => {
|
|
32
|
+
const source = production.stepOverrides[id]?.voiceSource
|
|
33
|
+
return source?.type === 'tts' ? [{ ...voiceTarget(section, step), voice: source.voice }] : []
|
|
34
|
+
}))
|
|
35
|
+
return { pages, steps }
|
|
36
|
+
}
|
|
37
|
+
const raw = doc.config.renderers.lecture?.voiceOverrides
|
|
38
|
+
if (raw === undefined) return { pages: {}, steps: [] }
|
|
39
|
+
if (!isRecord(raw) || raw.pages !== undefined && !isRecord(raw.pages) || raw.steps !== undefined && !Array.isArray(raw.steps)) throw new Error('voice-settings-invalid')
|
|
40
|
+
const pages: Record<string, SpeechVoice> = Object.create(null)
|
|
41
|
+
for (const [section, value] of Object.entries(raw.pages ?? {})) {
|
|
42
|
+
const voice = speechVoice(value)
|
|
43
|
+
if (!voice) throw new Error('voice-settings-invalid')
|
|
44
|
+
pages[section] = voice
|
|
45
|
+
}
|
|
46
|
+
const steps: VoiceOverrides['steps'] = []
|
|
47
|
+
const used = new Set<string>()
|
|
48
|
+
for (const value of raw.steps ?? []) {
|
|
49
|
+
if (!isRecord(value) || typeof value.section !== 'string' || typeof value.step !== 'number' || !Number.isSafeInteger(value.step) || value.step < 0 || typeof value.binding !== 'string') throw new Error('voice-settings-invalid')
|
|
50
|
+
const voice = speechVoice(value.voice), key = JSON.stringify([value.section, value.step])
|
|
51
|
+
if (!voice || used.has(key)) throw new Error('voice-settings-invalid')
|
|
52
|
+
used.add(key)
|
|
53
|
+
steps.push({ section: value.section, step: value.step, binding: value.binding, voice })
|
|
54
|
+
}
|
|
55
|
+
return { pages, steps }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function advances(node: MdastNode): boolean {
|
|
59
|
+
if (node.type === 'presmiumRole' && node.plugin === 'presmium.lecture' && node.name === 'cue' && isRecord(node.options) && typeof node.options.value === 'string' && /^next(?:\s|$)/.test(node.options.value.trim())) return true
|
|
60
|
+
return node.children?.some(advances) ?? false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** A cue can change the visual step inside one audio clip. That clip cannot yet take independent step voices. */
|
|
64
|
+
export function stepVoiceReason(doc: IRDocument, section: IRSection): ResolvedVoice['reason'] | undefined {
|
|
65
|
+
if ([...irBlocks(section.blocks)].some(block => block.role === 'narration' && block.node.type === 'markdown' && advances(block.node.mdast))) return 'cue-step'
|
|
66
|
+
const expected = voiceTarget(section, 0).binding
|
|
67
|
+
if (voiceOverrides(doc).steps.some(entry => entry.section === section.id && (entry.binding !== expected || entry.step > section.steps))) return 'step-binding-stale'
|
|
68
|
+
return undefined
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function resolvedVoice(doc: IRDocument, section: IRSection, step: number): ResolvedVoice {
|
|
72
|
+
if (doc.production) {
|
|
73
|
+
const target = voiceTarget(section, step), production = doc.production
|
|
74
|
+
const stepId = section.stepIds?.[step]
|
|
75
|
+
const own = stepId ? production.stepOverrides[stepId]?.voiceSource : undefined
|
|
76
|
+
const page = production.sectionOverrides?.[section.id]?.voiceSource
|
|
77
|
+
const source = own ?? page ?? production.defaults.voiceSource
|
|
78
|
+
const reason = source?.type === 'recording' ? 'recording-unavailable' as const : stepVoiceReason(doc, section)
|
|
79
|
+
return { target, ...source?.type === 'tts' ? { voice: source.voice, ...own ? { override: source.voice } : {} } : {}, inheritedFrom: own ? 'step' : page ? 'page' : source ? 'document' : 'unset', editable: !reason, ...reason ? { reason } : {} }
|
|
80
|
+
}
|
|
81
|
+
const target = voiceTarget(section, step), overrides = voiceOverrides(doc)
|
|
82
|
+
const reason = stepVoiceReason(doc, section)
|
|
83
|
+
const own = !reason && overrides.steps.find(entry => entry.section === section.id && entry.step === step && entry.binding === target.binding)?.voice
|
|
84
|
+
const page = overrides.pages[section.id], fallback = documentVoice(doc)
|
|
85
|
+
return { target, ...own || page || fallback ? { voice: own || page || fallback } : {}, inheritedFrom: own ? 'step' : page ? 'page' : fallback ? 'document' : 'unset', ...own ? { override: own } : {}, editable: !reason, ...reason ? { reason } : {} }
|
|
86
|
+
}
|