@lincyaw/kernel 0.0.0-stage → 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/LICENSE +202 -0
- package/README.md +32 -2
- package/package.json +41 -3
- package/src/access.ts +18 -0
- package/src/anchors.ts +58 -0
- package/src/authoring.ts +56 -0
- package/src/blocks.ts +19 -0
- package/src/checks.ts +35 -0
- package/src/component-code.ts +6 -0
- package/src/contracts/agent.ts +76 -0
- 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 +118 -0
- package/src/contracts/locale.ts +3 -0
- package/src/contracts/offline.ts +2 -0
- package/src/contracts/operations.ts +8 -0
- package/src/contracts/plugin.ts +183 -0
- package/src/contracts/presenter-panels.ts +17 -0
- package/src/contracts/preview.ts +386 -0
- 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 +108 -0
- package/src/contracts/service.ts +8 -0
- package/src/contracts/speech-settings.ts +16 -0
- package/src/contracts/speech.ts +149 -0
- 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 +35 -0
- 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 +15 -0
- package/src/events.ts +32 -0
- package/src/execution.ts +25 -0
- package/src/fingerprint.ts +31 -0
- package/src/follow.ts +33 -0
- package/src/git.ts +12 -0
- package/src/html.ts +41 -0
- package/src/index.ts +82 -0
- package/src/ir.ts +367 -0
- package/src/jupyter-kernel.ts +9 -0
- package/src/kernel.ts +190 -0
- package/src/keyboard.ts +43 -0
- package/src/locale.ts +13 -0
- package/src/lock.ts +81 -0
- package/src/logging.ts +11 -0
- package/src/lower-myst.ts +312 -0
- 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 -0
- 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/strip-types.mjs +15 -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/published.ts +11 -0
- package/src/push-stream.ts +67 -0
- package/src/reading.ts +96 -0
- package/src/recording.ts +38 -0
- package/src/renderer-components.ts +30 -0
- package/src/requests.ts +28 -0
- package/src/responses.ts +49 -0
- package/src/run-outputs.ts +15 -0
- package/src/runner.py +454 -0
- package/src/services.ts +146 -0
- 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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { Finding } from './checks.ts'
|
|
3
|
+
import type { DocumentIssue, MediaClip, TimeRange } from './document.ts'
|
|
4
|
+
import { DOCUMENT_AUDIO_MAX_BYTES } from './production-assets.ts'
|
|
5
|
+
import type { DocumentAudioImportAnswer, DocumentAudioInspectionAnswer, ProductionAssetInspection, ProductionAudioPlanQuery } from './production-assets.ts'
|
|
6
|
+
import type { ProductionBinding, ProductionPlan } from './production.ts'
|
|
7
|
+
import type { Member } from './server.ts'
|
|
8
|
+
import type { DocumentChangeAnswer } from './workspace.ts'
|
|
9
|
+
|
|
10
|
+
export const DOCUMENT_AUDIO_REQUEST_MAX_BYTES = 16 << 10
|
|
11
|
+
|
|
12
|
+
export interface DocumentAudioCapabilities {
|
|
13
|
+
source: CanonicalSource
|
|
14
|
+
available: boolean
|
|
15
|
+
status: 'available' | 'unavailable' | 'unknown'
|
|
16
|
+
reason?: 'audio-unavailable' | 'services-unavailable'
|
|
17
|
+
verification: 'configuration-only'
|
|
18
|
+
findings?: Finding[]
|
|
19
|
+
actions: { inspect: boolean, inspectWorkspace: boolean, importWorkspace: boolean, readProductionPlan: boolean, validateClipChange: boolean, changeClip: boolean, adoptRecording: boolean }
|
|
20
|
+
importUnavailableReason?: 'review-required' | 'operation-disabled'
|
|
21
|
+
clipChangeUnavailableReason?: 'review-required' | 'operation-disabled'
|
|
22
|
+
clips: { upsert: boolean, remove: true, upsertRequires: 'current-production-binding' }
|
|
23
|
+
limits: { audioBytes: number, requestBytes: number, receipts: number, concurrent: number }
|
|
24
|
+
workspaceFileScope: 'same-document'
|
|
25
|
+
idempotency: 'process-current-source'
|
|
26
|
+
review: 'required-unsupported' | 'locks-and-history' | 'locks-only'
|
|
27
|
+
}
|
|
28
|
+
export interface DocumentAudioInspect { source: CanonicalSource, assetId: string }
|
|
29
|
+
/** Missing range adopts the whole file. A range edits only an already adopted same-asset step recording. */
|
|
30
|
+
export interface DocumentRecordingAdoption { id: string, source: CanonicalSource, stepId: string, assetId: string, sha256: string, sourceRange?: TimeRange }
|
|
31
|
+
export interface VerifiedDocumentRecordingAdoption extends DocumentChangeAnswer {
|
|
32
|
+
verification: { saved: true, source: CanonicalSource, stepId: string, assetId: string, sha256: string, durationMs: number, sourceRange?: TimeRange, usedDurationMs?: number, compiled: 'not-requested', runtime: 'not-requested' }
|
|
33
|
+
}
|
|
34
|
+
/** Inspect an existing real snapshot; an expected revision is optional during discovery. */
|
|
35
|
+
export type DocProductionPlanQuery = ProductionAudioPlanQuery
|
|
36
|
+
export interface DocumentWorkspaceAudioInspect { source: CanonicalSource, path: string }
|
|
37
|
+
export interface WorkspaceAudioInspectionAnswer {
|
|
38
|
+
document: string
|
|
39
|
+
source: CanonicalSource
|
|
40
|
+
file: { path: string, sha256: string, byteLength: number }
|
|
41
|
+
inspection: Omit<ProductionAssetInspection, 'assetId' | 'mediaId'>
|
|
42
|
+
}
|
|
43
|
+
export interface DocumentWorkspaceAudioImport {
|
|
44
|
+
id: string
|
|
45
|
+
source: CanonicalSource
|
|
46
|
+
/** Workspace-relative regular file permitted by the selected document's existing source scope. */
|
|
47
|
+
file: { path: string, sha256: string, byteLength: number }
|
|
48
|
+
}
|
|
49
|
+
export interface VerifiedDocumentAudioImport extends DocumentAudioImportAnswer {
|
|
50
|
+
verification: {
|
|
51
|
+
saved: true
|
|
52
|
+
source: CanonicalSource
|
|
53
|
+
asset: { assetId: string, path: string, sha256: string, byteLength: number }
|
|
54
|
+
compiled: 'not-requested'
|
|
55
|
+
runtime: 'not-requested'
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** A repeatable check against saved content; no plan or write intent is retained. */
|
|
59
|
+
export type DocumentMediaClipValidation =
|
|
60
|
+
| { source: CanonicalSource, binding: ProductionBinding, clip: MediaClip }
|
|
61
|
+
| { source: CanonicalSource, removeClipId: string }
|
|
62
|
+
export type DocumentMediaClipEdit = DocumentMediaClipValidation & { id: string }
|
|
63
|
+
export interface DocumentMediaClipValidationAnswer {
|
|
64
|
+
document: string
|
|
65
|
+
source: CanonicalSource
|
|
66
|
+
operation: 'add' | 'update' | 'remove'
|
|
67
|
+
clipId: string
|
|
68
|
+
before?: MediaClip
|
|
69
|
+
after?: MediaClip
|
|
70
|
+
impact: { stepIds: string[], assetIds: string[] }
|
|
71
|
+
issues: DocumentIssue[]
|
|
72
|
+
checked: { canonical: true, production: 'validated' | 'not-required', binding?: ProductionBinding }
|
|
73
|
+
}
|
|
74
|
+
export interface VerifiedDocumentMediaClipChange extends DocumentChangeAnswer {
|
|
75
|
+
verification: {
|
|
76
|
+
saved: true
|
|
77
|
+
source: CanonicalSource
|
|
78
|
+
production: 'validated-before-save' | 'not-required'
|
|
79
|
+
binding?: ProductionBinding
|
|
80
|
+
compiled: 'not-requested'
|
|
81
|
+
runtime: 'not-requested'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Workspace authoring boundary, distinct from the plugin's low-level ProductionAudioAuthoring. */
|
|
85
|
+
export interface DocumentAudioAuthoring {
|
|
86
|
+
capabilities(document: string, member: Member): Promise<DocumentAudioCapabilities>
|
|
87
|
+
inspect(document: string, request: DocumentAudioInspect, member: Member, signal?: AbortSignal): Promise<DocumentAudioInspectionAnswer>
|
|
88
|
+
inspectWorkspace(document: string, request: DocumentWorkspaceAudioInspect, member: Member, signal?: AbortSignal): Promise<WorkspaceAudioInspectionAnswer>
|
|
89
|
+
readProductionPlan(document: string, request: DocProductionPlanQuery, member: Member, signal?: AbortSignal): Promise<ProductionPlan>
|
|
90
|
+
/** Agent entry. Origin and required review come from the host, never from this request. */
|
|
91
|
+
/** The import's checks without its transaction (scope, file bytes and SHA-256/length, audio format, current source or a replayable receipt); `importWorkspace` checks everything again. */
|
|
92
|
+
checkWorkspaceImport(document: string, request: DocumentWorkspaceAudioImport, member: Member, signal?: AbortSignal): Promise<void>
|
|
93
|
+
importWorkspace(document: string, request: DocumentWorkspaceAudioImport, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentAudioImport>
|
|
94
|
+
validateClipChange(document: string, request: DocumentMediaClipValidation, member: Member, signal?: AbortSignal): Promise<DocumentMediaClipValidationAnswer>
|
|
95
|
+
/** Agent entry; the transaction rechecks source, real media and current production timing. */
|
|
96
|
+
changeClip(document: string, request: DocumentMediaClipEdit, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentMediaClipChange>
|
|
97
|
+
/** The change's checks without writing (as validateClipChange, plus review and a replayable receipt). */
|
|
98
|
+
checkClipChange(document: string, request: DocumentMediaClipEdit, member: Member, signal?: AbortSignal): Promise<void>
|
|
99
|
+
/** The adoption's checks without writing: step, asset bytes and SHA-256, range, current source or a replayable receipt. */
|
|
100
|
+
checkRecordingAdoption(document: string, request: DocumentRecordingAdoption, member: Member, signal?: AbortSignal): Promise<void>
|
|
101
|
+
adoptRecording(document: string, request: DocumentRecordingAdoption, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentRecordingAdoption>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const source = { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] } as const
|
|
105
|
+
export const DOCUMENT_AUDIO_INSPECTION_SCHEMA = {
|
|
106
|
+
type: 'object', additionalProperties: false,
|
|
107
|
+
properties: { source, assetId: { type: 'string', minLength: 1, maxLength: 200 } },
|
|
108
|
+
required: ['source', 'assetId'],
|
|
109
|
+
} as const
|
|
110
|
+
export const DOCUMENT_RECORDING_ADOPTION_SCHEMA = {
|
|
111
|
+
type: 'object', additionalProperties: false,
|
|
112
|
+
properties: { id: { type: 'string', minLength: 1, maxLength: 200 }, source, stepId: { type: 'string', minLength: 1, maxLength: 200 }, assetId: { type: 'string', minLength: 1, maxLength: 200 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, sourceRange: { type: 'object', additionalProperties: false, properties: { startMs: { type: 'number', minimum: 0 }, endMs: { type: 'number', minimum: 0 } }, required: ['startMs', 'endMs'] } },
|
|
113
|
+
required: ['id', 'source', 'stepId', 'assetId', 'sha256'],
|
|
114
|
+
} as const
|
|
115
|
+
export const DOCUMENT_AUDIO_WORKSPACE_IMPORT_SCHEMA = {
|
|
116
|
+
type: 'object', additionalProperties: false,
|
|
117
|
+
properties: {
|
|
118
|
+
id: { type: 'string', minLength: 1, maxLength: 200 }, source,
|
|
119
|
+
file: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', minLength: 1, maxLength: 1024 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, byteLength: { type: 'integer', minimum: 1, maximum: DOCUMENT_AUDIO_MAX_BYTES } }, required: ['path', 'sha256', 'byteLength'] },
|
|
120
|
+
},
|
|
121
|
+
required: ['id', 'source', 'file'],
|
|
122
|
+
} as const
|
|
123
|
+
|
|
124
|
+
export const DOCUMENT_WORKSPACE_AUDIO_INSPECTION_SCHEMA = {
|
|
125
|
+
type: 'object', additionalProperties: false,
|
|
126
|
+
properties: { source, path: { type: 'string', minLength: 1, maxLength: 1024 } },
|
|
127
|
+
required: ['source', 'path'],
|
|
128
|
+
} as const
|
|
129
|
+
|
|
130
|
+
const clipId = { type: 'string', minLength: 1, maxLength: 200, pattern: '^[^\\u0000-\\u001f]+$', not: { enum: ['__proto__', 'constructor', 'prototype'] } } as const
|
|
131
|
+
const clip = {
|
|
132
|
+
type: 'object', additionalProperties: false,
|
|
133
|
+
properties: {
|
|
134
|
+
id: clipId, assetId: clipId,
|
|
135
|
+
sourceRange: { type: 'object', additionalProperties: false, properties: { startMs: { type: 'number', minimum: 0 }, endMs: { type: 'number', minimum: 0 } }, required: ['startMs', 'endMs'] },
|
|
136
|
+
attachment: { type: 'object', additionalProperties: false, properties: { stepId: clipId, offsetMs: { type: 'number', minimum: 0 } }, required: ['stepId', 'offsetMs'] },
|
|
137
|
+
volume: { type: 'number', minimum: 0, maximum: 1 }, muted: { type: 'boolean' },
|
|
138
|
+
},
|
|
139
|
+
required: ['id', 'assetId', 'sourceRange', 'attachment', 'volume'],
|
|
140
|
+
} as const
|
|
141
|
+
const productionBinding = { type: 'object', additionalProperties: false, properties: { source, irRevision: { type: 'string', pattern: '^[a-f\\d]{16}$' }, planId: { type: 'string', pattern: '^[a-f\\d]{32}$' } }, required: ['source', 'irRevision', 'planId'] } as const
|
|
142
|
+
export const DOCUMENT_PRODUCTION_PLAN_SCHEMA = { type: 'object', additionalProperties: false, properties: { source, irRevision: productionBinding.properties.irRevision }, required: ['source'] } as const
|
|
143
|
+
const clipRequestSchema = (edit: boolean) => ({
|
|
144
|
+
oneOf: [
|
|
145
|
+
{ type: 'object', additionalProperties: false, properties: { ...edit ? { id: clipId } : {}, source, binding: productionBinding, clip }, required: [...edit ? ['id'] : [], 'source', 'binding', 'clip'] },
|
|
146
|
+
{ type: 'object', additionalProperties: false, properties: { ...edit ? { id: clipId } : {}, source, removeClipId: clipId }, required: [...edit ? ['id'] : [], 'source', 'removeClipId'] },
|
|
147
|
+
],
|
|
148
|
+
})
|
|
149
|
+
export const DOCUMENT_MEDIA_CLIP_VALIDATION_SCHEMA = /* @__PURE__ */ clipRequestSchema(false)
|
|
150
|
+
export const DOCUMENT_MEDIA_CLIP_CHANGE_SCHEMA = /* @__PURE__ */ clipRequestSchema(true)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DocumentThread, JsonObject, RichTextNode } from './document.ts'
|
|
2
|
+
import type { Member } from './server.ts'
|
|
3
|
+
|
|
4
|
+
/** Canonical comment intent; author identity, dates and new entity IDs are supplied by the adapter. */
|
|
5
|
+
export type DocumentCommentAction =
|
|
6
|
+
| { type: 'comment.create', content: RichTextNode[], anchor?: DocumentThread['anchor'] | null, metadata?: JsonObject, commentMetadata?: JsonObject }
|
|
7
|
+
| { type: 'comment.reply', threadId: string, content: RichTextNode[], metadata?: JsonObject }
|
|
8
|
+
| { type: 'comment.update', threadId: string, commentId: string, content: RichTextNode[], metadata?: JsonObject }
|
|
9
|
+
| { type: 'comment.react', threadId: string, commentId: string, emoji: string, active: boolean }
|
|
10
|
+
| { type: 'comment.resolve' | 'comment.reopen', threadId: string }
|
|
11
|
+
| { type: 'comment.delete', threadId: string, commentId?: string }
|
|
12
|
+
|
|
13
|
+
export interface DocumentCommentContext {
|
|
14
|
+
member?: Member
|
|
15
|
+
now: string
|
|
16
|
+
allocate(): string
|
|
17
|
+
/** MCP content IDs are only input placeholders; native projections retain their own stable rich IDs. */
|
|
18
|
+
allocateContentIds?: boolean
|
|
19
|
+
}
|
|
20
|
+
export interface DocumentCommentEffect {
|
|
21
|
+
threadId: string
|
|
22
|
+
commentId?: string
|
|
23
|
+
changed: boolean
|
|
24
|
+
anchored: boolean
|
|
25
|
+
/** The target thread’s prior anchor was invalid; the discussion remains explicitly unanchored. */
|
|
26
|
+
anchorDetached?: boolean
|
|
27
|
+
threadRemoved: boolean
|
|
28
|
+
removedComments: number
|
|
29
|
+
comments: number
|
|
30
|
+
reactions: number
|
|
31
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Member } from './server.ts'
|
|
2
|
+
import type { HistoryRevision, RevisionsAnswer } from './workspace.ts'
|
|
3
|
+
|
|
4
|
+
export const DOCUMENT_HISTORY_MAX_REQUEST_CHARS = 2048
|
|
5
|
+
export const DOCUMENT_HISTORY_MAX_RESPONSE_CHARS = 7168
|
|
6
|
+
export const DOCUMENT_HISTORY_MAX_FILE_BYTES = 2 << 20
|
|
7
|
+
export const DOCUMENT_HISTORY_PAGE_SIZE = 20
|
|
8
|
+
|
|
9
|
+
/** Actual saved bytes; a malformed current JSON can still be inspected and undone. */
|
|
10
|
+
export interface DocumentHistorySource { version: string, fingerprint: string }
|
|
11
|
+
export interface DocumentHistoryBinding { source: DocumentHistorySource, snapshot: string }
|
|
12
|
+
export interface DocumentHistoryQuery { offset?: number, snapshot?: string }
|
|
13
|
+
export interface DocumentHistoryAnswer extends RevisionsAnswer {
|
|
14
|
+
document: string
|
|
15
|
+
available: boolean
|
|
16
|
+
unborn?: true
|
|
17
|
+
historyBinding?: DocumentHistoryBinding
|
|
18
|
+
nextOffset?: number
|
|
19
|
+
/** Compact summaries and file lists may be abbreviated, never commit IDs. */
|
|
20
|
+
truncated?: true
|
|
21
|
+
}
|
|
22
|
+
export interface DocumentHistoryRead { revision: string, path?: string, offset?: number }
|
|
23
|
+
export interface DocumentHistoryFile {
|
|
24
|
+
document: string
|
|
25
|
+
revision: string
|
|
26
|
+
path: string
|
|
27
|
+
encoding: 'utf8-text-fragment'
|
|
28
|
+
text: string
|
|
29
|
+
offset: number
|
|
30
|
+
nextOffset?: number
|
|
31
|
+
totalChars: number
|
|
32
|
+
complete: boolean
|
|
33
|
+
sha256: string
|
|
34
|
+
verification: 'historical-file-only'
|
|
35
|
+
}
|
|
36
|
+
export interface DocumentHistoryChange extends DocumentHistoryBinding { id: string, expect: string }
|
|
37
|
+
export interface DocumentHistoryChanged extends DocumentHistoryBinding {
|
|
38
|
+
document: string
|
|
39
|
+
ok: true
|
|
40
|
+
revision: Pick<HistoryRevision, 'id' | 'origin'>
|
|
41
|
+
verification: { saved: true, compiled: 'not-requested', runtime: 'not-requested' }
|
|
42
|
+
}
|
|
43
|
+
/** One workspace owner; HTTP and MCP do not implement reversion or write files. */
|
|
44
|
+
export interface DocumentHistory {
|
|
45
|
+
list(document: string, query: DocumentHistoryQuery, member: Member): Promise<DocumentHistoryAnswer>
|
|
46
|
+
read(document: string, query: DocumentHistoryRead, member: Member): Promise<DocumentHistoryFile>
|
|
47
|
+
/** The change's checks without writing: binding, target, document scope, next undo/redo, conflicts and uncommitted files, or a replayable receipt. */
|
|
48
|
+
checkChange(document: string, which: 'undo' | 'redo' | 'revert', request: DocumentHistoryChange, member: Member, origin?: 'editor' | 'agent'): Promise<void>
|
|
49
|
+
change(document: string, which: 'undo' | 'redo' | 'revert', request: DocumentHistoryChange, member: Member, origin?: 'editor' | 'agent'): Promise<DocumentHistoryChanged>
|
|
50
|
+
}
|
|
51
|
+
const hash = { type: 'string', pattern: '^(?:[a-f0-9]{40}|[a-f0-9]{64})$' } as const
|
|
52
|
+
export const DOCUMENT_HISTORY_QUERY_SCHEMA = { type: 'object', additionalProperties: false, properties: {
|
|
53
|
+
offset: { type: 'integer', minimum: 0 }, snapshot: hash,
|
|
54
|
+
} } as const
|
|
55
|
+
export const DOCUMENT_HISTORY_READ_SCHEMA = { type: 'object', additionalProperties: false, required: ['revision'], properties: {
|
|
56
|
+
revision: hash, path: { type: 'string', minLength: 1, maxLength: 500 }, offset: { type: 'integer', minimum: 0 },
|
|
57
|
+
} } as const
|
|
58
|
+
export const DOCUMENT_HISTORY_CHANGE_SCHEMA = { type: 'object', additionalProperties: false, required: ['id', 'source', 'snapshot', 'expect'], properties: {
|
|
59
|
+
id: { type: 'string', minLength: 1, maxLength: 200 }, snapshot: hash, expect: hash,
|
|
60
|
+
source: { type: 'object', additionalProperties: false, required: ['version', 'fingerprint'], properties: {
|
|
61
|
+
version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
|
|
62
|
+
} },
|
|
63
|
+
} } as const
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { Member } from './server.ts'
|
|
3
|
+
import type { DocumentImageImportAnswer } from './workspace.ts'
|
|
4
|
+
import { DOCUMENT_IMAGE_MAX_BYTES } from './workspace.ts'
|
|
5
|
+
|
|
6
|
+
export interface DocumentWorkspaceImageInspect { source: CanonicalSource, path: string }
|
|
7
|
+
export interface DocumentWorkspaceImageImport { id: string, source: CanonicalSource, file: { path: string, sha256: string, byteLength: number } }
|
|
8
|
+
export interface WorkspaceImageInspection {
|
|
9
|
+
document: string, source: CanonicalSource
|
|
10
|
+
file: { path: string, sha256: string, byteLength: number }
|
|
11
|
+
image: { mediaType: string, dimensions: { width: number, height: number } }
|
|
12
|
+
}
|
|
13
|
+
export interface VerifiedDocumentImageImport extends DocumentImageImportAnswer {
|
|
14
|
+
verification: { saved: true, source: CanonicalSource, asset: { assetId: string, path: string, sha256: string, byteLength: number }, compiled: 'not-requested', runtime: 'not-requested' }
|
|
15
|
+
}
|
|
16
|
+
export interface DocumentImageCapabilities {
|
|
17
|
+
source: CanonicalSource, available: true, verification: 'configuration-only'
|
|
18
|
+
actions: { inspectWorkspace: true, importWorkspace: boolean }
|
|
19
|
+
importUnavailableReason?: 'review-required' | 'operation-disabled'
|
|
20
|
+
limits: { imageBytes: number, receipts: number, concurrent: number }, workspaceFileScope: 'same-document', idempotency: 'process-current-source'
|
|
21
|
+
review: 'required-unsupported' | 'locks-and-history' | 'locks-only'
|
|
22
|
+
}
|
|
23
|
+
export interface DocumentImageAuthoring {
|
|
24
|
+
capabilities(document: string, member: Member): Promise<DocumentImageCapabilities>
|
|
25
|
+
inspectWorkspace(document: string, request: DocumentWorkspaceImageInspect, member: Member, signal?: AbortSignal): Promise<WorkspaceImageInspection>
|
|
26
|
+
/** The import's checks without its transaction (scope, file bytes and SHA-256/length, current source or a replayable receipt), so a doomed import is refused before the author is asked; `importWorkspace` checks everything again. */
|
|
27
|
+
checkWorkspaceImport(document: string, request: DocumentWorkspaceImageImport, member: Member, signal?: AbortSignal): Promise<void>
|
|
28
|
+
importWorkspace(document: string, request: DocumentWorkspaceImageImport, member: Member, signal?: AbortSignal): Promise<VerifiedDocumentImageImport>
|
|
29
|
+
}
|
|
30
|
+
const source = { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] } as const
|
|
31
|
+
export const DOCUMENT_WORKSPACE_IMAGE_INSPECTION_SCHEMA = { type: 'object', additionalProperties: false, properties: { source, path: { type: 'string', minLength: 1, maxLength: 1024 } }, required: ['source', 'path'] } as const
|
|
32
|
+
export const DOCUMENT_WORKSPACE_IMAGE_IMPORT_SCHEMA = { type: 'object', additionalProperties: false, properties: { id: { type: 'string', minLength: 1, maxLength: 200 }, source, file: { type: 'object', additionalProperties: false, properties: { path: { type: 'string', minLength: 1, maxLength: 1024 }, sha256: { type: 'string', pattern: '^[a-f\\d]{64}$' }, byteLength: { type: 'integer', minimum: 1, maximum: DOCUMENT_IMAGE_MAX_BYTES } }, required: ['path', 'sha256', 'byteLength'] } }, required: ['id', 'source', 'file'] } as const
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { JsonValue } from './document.ts'
|
|
3
|
+
|
|
4
|
+
export const DOCUMENT_READ_MAX_REQUEST_CHARS = 2048
|
|
5
|
+
/** Includes JSON escaping and indentation used by the MCP text result. */
|
|
6
|
+
export const DOCUMENT_READ_MAX_RESPONSE_CHARS = 7168
|
|
7
|
+
export const DOCUMENT_ENTITY_COLLECTIONS = ['sections', 'blocks', 'steps', 'narrations', 'assets', 'definitions', 'comments'] as const
|
|
8
|
+
export const DOCUMENT_ROOT_FIELDS = ['schemaVersion', 'id', 'metadata', 'sectionOrder', 'production'] as const
|
|
9
|
+
export type DocumentEntityCollection = typeof DOCUMENT_ENTITY_COLLECTIONS[number]
|
|
10
|
+
export type DocumentPartTarget = { collection: DocumentEntityCollection, id: string, path?: string[] } | { collection: typeof DOCUMENT_ROOT_FIELDS[number], path?: string[] }
|
|
11
|
+
export interface DocumentOutlineQuery { source?: CanonicalSource, collection?: DocumentEntityCollection, offset?: number }
|
|
12
|
+
export interface DocumentPartQuery { source: CanonicalSource, target: DocumentPartTarget, offset?: number }
|
|
13
|
+
export interface DocumentOutlineEntry {
|
|
14
|
+
id: string
|
|
15
|
+
type?: string
|
|
16
|
+
sectionId?: string
|
|
17
|
+
title?: { preview: string, complete: boolean, totalChars: number }
|
|
18
|
+
counts?: { children?: number, steps?: number, narrations?: number }
|
|
19
|
+
}
|
|
20
|
+
export interface DocumentOutlineAnswer {
|
|
21
|
+
source: CanonicalSource
|
|
22
|
+
documentSchemaVersion: number
|
|
23
|
+
counts: Record<DocumentEntityCollection, number>
|
|
24
|
+
collection: DocumentEntityCollection
|
|
25
|
+
present: boolean
|
|
26
|
+
offset: number
|
|
27
|
+
nextOffset?: number
|
|
28
|
+
total: number
|
|
29
|
+
complete: boolean
|
|
30
|
+
entries: DocumentOutlineEntry[]
|
|
31
|
+
}
|
|
32
|
+
interface DocumentPartAnswerBase {
|
|
33
|
+
source: CanonicalSource
|
|
34
|
+
target: DocumentPartTarget
|
|
35
|
+
/** FNV-1a 64 of JSON.stringify(selected canonical value), with no indentation. */
|
|
36
|
+
valueDigest: { algorithm: 'fnv1a64', serialization: 'JSON.stringify', value: string }
|
|
37
|
+
}
|
|
38
|
+
export type DocumentPartAnswer = DocumentPartAnswerBase & (
|
|
39
|
+
{ encoding: 'value', complete: true, value: JsonValue } |
|
|
40
|
+
{ encoding: 'json-text-fragment', offsetUnit: 'utf16', offset: number, nextOffset?: number, totalChars: number, complete: boolean, text: string }
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const source = { type: 'object', additionalProperties: false, required: ['documentId', 'version', 'fingerprint'], properties: {
|
|
44
|
+
documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
|
|
45
|
+
} } as const
|
|
46
|
+
const offset = { type: 'integer', minimum: 0, maximum: Number.MAX_SAFE_INTEGER } as const
|
|
47
|
+
const path = { type: 'array', maxItems: 64, items: { type: 'string', maxLength: 1024 } } as const
|
|
48
|
+
export const DOCUMENT_OUTLINE_QUERY_SCHEMA = {
|
|
49
|
+
type: 'object', additionalProperties: false, properties: { source, collection: { enum: DOCUMENT_ENTITY_COLLECTIONS }, offset },
|
|
50
|
+
if: { required: ['offset'], properties: { offset: { minimum: 1 } } }, then: { required: ['source'] },
|
|
51
|
+
} as const
|
|
52
|
+
export const DOCUMENT_PART_QUERY_SCHEMA = {
|
|
53
|
+
type: 'object', additionalProperties: false, required: ['source', 'target'], properties: { source, offset, target: { oneOf: [
|
|
54
|
+
{ type: 'object', additionalProperties: false, required: ['collection', 'id'], properties: { collection: { enum: DOCUMENT_ENTITY_COLLECTIONS }, id: { type: 'string', minLength: 1, maxLength: 200 }, path } },
|
|
55
|
+
{ type: 'object', additionalProperties: false, required: ['collection'], properties: { collection: { enum: DOCUMENT_ROOT_FIELDS }, path } },
|
|
56
|
+
] } },
|
|
57
|
+
} as const
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { Finding } from './checks.ts'
|
|
3
|
+
import type { Member } from './server.ts'
|
|
4
|
+
import type { SpeechEngineInfo } from './speech.ts'
|
|
5
|
+
import type { VoiceCatalogAnswer, VoiceoverCancellation } from './voice.ts'
|
|
6
|
+
import type { DocumentVoiceTasks, VoiceoverJobQuery, VoiceoverStart, VoicePreviewStart } from './voiceover-execution.ts'
|
|
7
|
+
|
|
8
|
+
export interface DocumentVoiceCapabilities {
|
|
9
|
+
document: string
|
|
10
|
+
source: CanonicalSource
|
|
11
|
+
/** Configuration discovery does not prove provider authentication or synthesis success. */
|
|
12
|
+
verification: 'configuration-only'
|
|
13
|
+
status: 'configured' | 'unavailable' | 'unknown'
|
|
14
|
+
reason?: 'not-configured' | 'services-unavailable'
|
|
15
|
+
voiceover: boolean
|
|
16
|
+
preview: boolean
|
|
17
|
+
engines: SpeechEngineInfo[]
|
|
18
|
+
defaultEngine?: string
|
|
19
|
+
actions: { status: boolean, list: true, get: true, start: boolean, cancel: true, quotePreview: boolean, preview: boolean, readPreview: true }
|
|
20
|
+
tasks: { idempotency: 'process', limits: { intents: number, recentJobs: number, concurrent: number, quotes: number, quoteLifetimeMs: number, previewBytes: number, previewResources: number, retainedPreviewBytes: number } }
|
|
21
|
+
findings?: Finding[]
|
|
22
|
+
}
|
|
23
|
+
export interface DocumentVoiceCatalogQuery { source?: CanonicalSource, engine?: string, lang?: string }
|
|
24
|
+
export interface DocumentVoiceCatalogAnswer extends VoiceCatalogAnswer { document: string, source: CanonicalSource, findings?: Finding[] }
|
|
25
|
+
|
|
26
|
+
/** Shared voice discovery and explicitly authorized tasks; discovery never compiles or synthesizes. */
|
|
27
|
+
export interface DocumentVoiceAuthoring extends DocumentVoiceTasks {
|
|
28
|
+
capabilities(document: string, member: Member, signal?: AbortSignal): Promise<DocumentVoiceCapabilities>
|
|
29
|
+
catalog(document: string, query: DocumentVoiceCatalogQuery, member: Member, signal?: AbortSignal): Promise<DocumentVoiceCatalogAnswer>
|
|
30
|
+
/** Checks a start or preview with the same path as the action, without synthesizing, so an agent asks only for one that would run. */
|
|
31
|
+
checkStart(document: string, request: VoiceoverStart, member: Member, signal?: AbortSignal): Promise<void>
|
|
32
|
+
checkPreview(document: string, request: VoicePreviewStart, member: Member, signal?: AbortSignal): Promise<void>
|
|
33
|
+
/** Undefined when there is something to cancel; otherwise the unchanged state that cancel would report. */
|
|
34
|
+
checkCancel(document: string, query: VoiceoverJobQuery, member: Member): Promise<VoiceoverCancellation | undefined>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const DOCUMENT_VOICE_CATALOG_SCHEMA = {
|
|
38
|
+
type: 'object', additionalProperties: false,
|
|
39
|
+
properties: {
|
|
40
|
+
source: { type: 'object', additionalProperties: false, properties: { documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', pattern: '^[a-f\\d]{20}$' }, fingerprint: { type: 'string', pattern: '^[a-f\\d]{16}$' } }, required: ['documentId', 'version', 'fingerprint'] },
|
|
41
|
+
engine: { type: 'string', minLength: 1, maxLength: 200 },
|
|
42
|
+
lang: { type: 'string', minLength: 1, maxLength: 100 },
|
|
43
|
+
},
|
|
44
|
+
} as const
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { ExecuteConfig } from './execution.ts'
|
|
2
|
+
import type { SpeechVoice } from './speech.ts'
|
|
3
|
+
|
|
4
|
+
export const DOCUMENT_SCHEMA_VERSION = 1 as const
|
|
5
|
+
export const DOCUMENT_ENTRY = 'lecture.json'
|
|
6
|
+
export const ASSET_TITLE_MAX_LENGTH = 200
|
|
7
|
+
|
|
8
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | JsonObject
|
|
9
|
+
export interface JsonObject { [key: string]: JsonValue }
|
|
10
|
+
export type JsonSchema = JsonObject | boolean
|
|
11
|
+
export type DocumentId = string
|
|
12
|
+
export type SectionId = string
|
|
13
|
+
export type BlockId = string
|
|
14
|
+
export type StepId = string
|
|
15
|
+
export type NarrationId = string
|
|
16
|
+
export type AssetId = string
|
|
17
|
+
export type DefinitionId = string
|
|
18
|
+
export type ClipId = string
|
|
19
|
+
|
|
20
|
+
export interface DocumentMetadata {
|
|
21
|
+
title: string
|
|
22
|
+
lang: string
|
|
23
|
+
description?: string
|
|
24
|
+
tags?: string[]
|
|
25
|
+
plugins?: string[]
|
|
26
|
+
presmium?: Record<string, JsonObject>
|
|
27
|
+
renderers?: Record<string, JsonObject>
|
|
28
|
+
execute?: ExecuteConfig
|
|
29
|
+
checks?: JsonObject
|
|
30
|
+
translations?: Record<string, string>
|
|
31
|
+
translationOf?: { entry: string, digest: string }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Canonical authoring data. Runtime positions, resolved assets and playback plans are derived. */
|
|
35
|
+
export interface TeachingDocument {
|
|
36
|
+
schemaVersion: typeof DOCUMENT_SCHEMA_VERSION
|
|
37
|
+
id: DocumentId
|
|
38
|
+
metadata: DocumentMetadata
|
|
39
|
+
sectionOrder: SectionId[]
|
|
40
|
+
sections: Record<SectionId, Section>
|
|
41
|
+
blocks: Record<BlockId, DocumentBlock>
|
|
42
|
+
steps: Record<StepId, Step>
|
|
43
|
+
narrations: Record<NarrationId, Narration>
|
|
44
|
+
assets: Record<AssetId, Asset>
|
|
45
|
+
definitions: Record<DefinitionId, Definition>
|
|
46
|
+
production: Production
|
|
47
|
+
comments?: Record<string, DocumentThread>
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface Layout {
|
|
51
|
+
type: 'stack' | 'columns' | 'canvas' | 'scrolly'
|
|
52
|
+
properties?: JsonObject
|
|
53
|
+
renderers?: Record<string, JsonObject>
|
|
54
|
+
}
|
|
55
|
+
export interface Section {
|
|
56
|
+
id: SectionId
|
|
57
|
+
title: string
|
|
58
|
+
level?: 1 | 2 | 3 | 4 | 5 | 6
|
|
59
|
+
children: BlockId[]
|
|
60
|
+
stepOrder: StepId[]
|
|
61
|
+
layout: Layout
|
|
62
|
+
audience?: string[]
|
|
63
|
+
config?: Record<string, JsonObject>
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type TextAlignment = 'left' | 'center' | 'right' | 'justify'
|
|
67
|
+
export interface TextStyle { alignment?: TextAlignment, color?: string, background?: string }
|
|
68
|
+
export type TextMark =
|
|
69
|
+
| { type: 'bold' | 'italic' | 'underline' | 'strike' | 'code' }
|
|
70
|
+
| { type: 'color' | 'background', value: string }
|
|
71
|
+
export type InlineNode =
|
|
72
|
+
| { type: 'text', text: string, marks?: TextMark[] }
|
|
73
|
+
| { type: 'link', href: string, children: InlineNode[] }
|
|
74
|
+
| { type: 'math', value: string }
|
|
75
|
+
| { type: 'break' }
|
|
76
|
+
| InlineExtensionNode
|
|
77
|
+
|
|
78
|
+
/** A registered extension retains its own versioned semantics and an explicit readable fallback. */
|
|
79
|
+
export interface InlineExtensionNode { type: 'extension', kind: string, schemaVersion: number, data: JsonObject, fallback: string }
|
|
80
|
+
export interface RichTextExtensionNode { id: string, type: 'extension', kind: string, schemaVersion: number, data: JsonObject, fallback: RichTextNode[] }
|
|
81
|
+
|
|
82
|
+
export interface ParagraphNode { id: string, type: 'paragraph', children: InlineNode[], style?: TextStyle }
|
|
83
|
+
export interface HeadingNode { id: string, type: 'heading', level: 1 | 2 | 3 | 4 | 5 | 6, children: InlineNode[], style?: TextStyle }
|
|
84
|
+
export interface ListItemNode { id: string, type: 'listItem', children: RichTextNode[], checked?: boolean }
|
|
85
|
+
export interface ListNode { id: string, type: 'list', ordered: boolean, start?: number, children: ListItemNode[] }
|
|
86
|
+
export interface QuoteNode { id: string, type: 'blockquote', children: RichTextNode[] }
|
|
87
|
+
export interface CodeTextNode { id: string, type: 'code', language: string, value: string }
|
|
88
|
+
export interface MathTextNode { id: string, type: 'math', value: string }
|
|
89
|
+
export interface TableCellNode { id: string, type: 'tableCell', children: InlineNode[], style?: TextStyle, colspan?: number, rowspan?: number }
|
|
90
|
+
export interface TableRowNode { id: string, type: 'tableRow', children: TableCellNode[] }
|
|
91
|
+
export interface TableNode { id: string, type: 'table', children: TableRowNode[], headerRows?: number, headerColumns?: number, columnWidths?: number[] }
|
|
92
|
+
export type RichTextNode = ParagraphNode | HeadingNode | ListNode | QuoteNode | CodeTextNode | MathTextNode | TableNode | RichTextExtensionNode
|
|
93
|
+
|
|
94
|
+
export interface BlockScope { only?: string[], audience?: string[] }
|
|
95
|
+
export interface TextBlock extends BlockScope { id: BlockId, type: 'text', content: RichTextNode[], reveal?: { type: 'items', order: string[] } }
|
|
96
|
+
export interface ContainerProperties { title?: string, columnWidths?: number[], tabTitles?: string[] }
|
|
97
|
+
export interface ContainerBlock extends BlockScope { id: BlockId, type: 'container', layout: 'stack' | 'columns' | 'tabs', children: BlockId[], properties: ContainerProperties }
|
|
98
|
+
/** An image without an asset is an authored placeholder, not an asset with an empty location. */
|
|
99
|
+
export interface ImageBlock extends BlockScope { id: BlockId, type: 'image', assetId?: AssetId, alt: string, caption?: InlineNode[], width?: number, alignment?: TextAlignment }
|
|
100
|
+
export interface CodeReference { anchor: string, pin?: string, repo?: string, highlightRegions?: Record<StepId, string> }
|
|
101
|
+
export interface CodeExecution { args?: Record<string, string>, stdin?: string, timeoutMs?: number, executor?: string, when: 'compile' | 'live' | 'both', session?: string }
|
|
102
|
+
export interface CodeVariantSource { id: string, title: string, value?: string, origin?: Omit<CodeReference, 'highlightRegions'> }
|
|
103
|
+
export interface CodeBlock extends BlockScope {
|
|
104
|
+
id: BlockId
|
|
105
|
+
type: 'code'
|
|
106
|
+
language: string
|
|
107
|
+
value?: string
|
|
108
|
+
origin?: CodeReference
|
|
109
|
+
title?: string
|
|
110
|
+
execution?: CodeExecution
|
|
111
|
+
highlight?: Record<StepId, number[]>
|
|
112
|
+
variants?: CodeVariantSource[]
|
|
113
|
+
echo?: boolean
|
|
114
|
+
}
|
|
115
|
+
export interface ComponentBlock extends BlockScope { id: BlockId, type: 'component', definitionId: DefinitionId, parameters: JsonObject, children?: BlockId[] }
|
|
116
|
+
export type DocumentBlock = TextBlock | ContainerBlock | ImageBlock | CodeBlock | ComponentBlock
|
|
117
|
+
|
|
118
|
+
/** State is independent from ordering and production timing. Omitted blocks are invisible. */
|
|
119
|
+
export interface Step {
|
|
120
|
+
id: StepId
|
|
121
|
+
sectionId: SectionId
|
|
122
|
+
title?: string
|
|
123
|
+
visuals: VisualState[]
|
|
124
|
+
narrationIds: NarrationId[]
|
|
125
|
+
inherits?: StepId
|
|
126
|
+
}
|
|
127
|
+
export interface VisualState { blockId: BlockId, visible: boolean, state?: string, parameters?: JsonObject }
|
|
128
|
+
export type NarrationNode =
|
|
129
|
+
| { type: 'text', text: string }
|
|
130
|
+
| { type: 'pronunciation', display: string, spoken: string }
|
|
131
|
+
| { id: string, type: 'cue', action: string, target?: { blockId: BlockId, element?: string }, parameters?: JsonObject }
|
|
132
|
+
export interface Narration {
|
|
133
|
+
id: NarrationId
|
|
134
|
+
content: NarrationNode[]
|
|
135
|
+
/** Attribution of an explicit adoption; never a second caption or spoken-text source. */
|
|
136
|
+
provenance?: { type: 'transcription', transcriptionId: string, revision: string, assetId: AssetId, audioSha256: string, segmentIds: string[] }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface Asset { id: AssetId, kind: 'image' | 'audio' | 'video' | 'file', location: string, title?: string, fingerprint?: string, mediaType?: string }
|
|
140
|
+
export type ImplementationRef =
|
|
141
|
+
| { type: 'file', path: string, exportName?: string, fingerprint?: string }
|
|
142
|
+
| { type: 'module', specifier: string, exportName?: string }
|
|
143
|
+
| { type: 'plugin', plugin: string, name: string }
|
|
144
|
+
export interface StateDescriptor {
|
|
145
|
+
id: string
|
|
146
|
+
title?: string
|
|
147
|
+
parameterSchema?: JsonSchema
|
|
148
|
+
/** A migration must retain numerical implementation bindings explicitly, never invent semantic names. */
|
|
149
|
+
legacyIndex?: number
|
|
150
|
+
}
|
|
151
|
+
export interface Definition {
|
|
152
|
+
id: DefinitionId
|
|
153
|
+
kind: string
|
|
154
|
+
schemaVersion: number
|
|
155
|
+
implementation: ImplementationRef
|
|
156
|
+
parameterSchema: JsonSchema
|
|
157
|
+
states: StateDescriptor[]
|
|
158
|
+
data?: JsonObject
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type VoiceSource = { type: 'none' } | { type: 'tts', voice: SpeechVoice } | { type: 'recording', assetId: AssetId, sourceRange?: TimeRange }
|
|
162
|
+
export interface StepProduction {
|
|
163
|
+
narrationStart?: 'with-animation' | 'after-animation'
|
|
164
|
+
minimumHoldMs?: number
|
|
165
|
+
trailingPauseMs?: number
|
|
166
|
+
voiceSource?: VoiceSource
|
|
167
|
+
}
|
|
168
|
+
export interface ProductionDefaults extends StepProduction {}
|
|
169
|
+
export interface TimeRange { startMs: number, endMs: number }
|
|
170
|
+
/** Explicit author times; offsets partition the original ordered step narration display in UTF16. */
|
|
171
|
+
export interface CaptionTiming {
|
|
172
|
+
narrationIds: NarrationId[]
|
|
173
|
+
sourceFingerprint: string
|
|
174
|
+
playbackFingerprint: string
|
|
175
|
+
durationMs: number
|
|
176
|
+
segments: { id: string, from: number, to: number, startMs: number, endMs: number }[]
|
|
177
|
+
}
|
|
178
|
+
export interface MediaClip {
|
|
179
|
+
id: ClipId
|
|
180
|
+
assetId: AssetId
|
|
181
|
+
sourceRange: TimeRange
|
|
182
|
+
attachment: { stepId: StepId, offsetMs: number }
|
|
183
|
+
/** Output gain, from zero to one; muting does not replace this value. */
|
|
184
|
+
volume: number
|
|
185
|
+
muted?: boolean
|
|
186
|
+
}
|
|
187
|
+
export interface Production {
|
|
188
|
+
defaults: ProductionDefaults
|
|
189
|
+
sectionOverrides?: Record<SectionId, StepProduction>
|
|
190
|
+
stepOverrides: Record<StepId, StepProduction>
|
|
191
|
+
/** Migrated narration defaults: after page settings, before an explicit step override; shared narrations retain this source. */
|
|
192
|
+
narrationOverrides?: Record<NarrationId, { narrationStart: 'with-animation' | 'after-animation' }>
|
|
193
|
+
/** Cue placement is authored in narration; its delay is owned only here. */
|
|
194
|
+
cueTimings?: Record<string, { pauseMs: number }>
|
|
195
|
+
/** Stale mappings remain inspectable but never retime audio or replace changed narration. */
|
|
196
|
+
captionTimings?: Record<StepId, CaptionTiming>
|
|
197
|
+
mediaClips: MediaClip[]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** A stable rich-text/code ID, or `page:${sectionId}` reserved for an existing section title. */
|
|
201
|
+
export interface DocumentTextPoint { blockId: string, offset: number }
|
|
202
|
+
export interface DocumentComment {
|
|
203
|
+
id: string
|
|
204
|
+
type: 'comment'
|
|
205
|
+
userId: string
|
|
206
|
+
createdAt: string
|
|
207
|
+
updatedAt: string
|
|
208
|
+
deletedAt?: string
|
|
209
|
+
content?: RichTextNode[]
|
|
210
|
+
metadata?: JsonObject
|
|
211
|
+
reactions: { emoji: string, createdAt: string, userIds: string[] }[]
|
|
212
|
+
}
|
|
213
|
+
export interface DocumentThread {
|
|
214
|
+
id: string
|
|
215
|
+
type: 'thread'
|
|
216
|
+
createdAt: string
|
|
217
|
+
updatedAt: string
|
|
218
|
+
resolved: boolean
|
|
219
|
+
resolvedUpdatedAt?: string
|
|
220
|
+
resolvedBy?: string
|
|
221
|
+
metadata?: JsonObject
|
|
222
|
+
deletedAt?: string
|
|
223
|
+
anchor?: { from: DocumentTextPoint, to: DocumentTextPoint }
|
|
224
|
+
comments: DocumentComment[]
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export interface DocumentIssue { path: string, code: string, message: string, severity: 'error' | 'warning' }
|
|
228
|
+
export interface DocumentValidation { document?: TeachingDocument, issues: DocumentIssue[] }
|
|
229
|
+
|
|
230
|
+
/** A source-document transaction. Its CAS version is unrelated to a compiled snapshot ID. */
|
|
231
|
+
export type DocumentEditOperation =
|
|
232
|
+
| { type: 'insertBlock', sectionId: SectionId, parentId?: BlockId, index: number, block: DocumentBlock }
|
|
233
|
+
| { type: 'moveBlock', blockId: BlockId, sectionId: SectionId, parentId?: BlockId, index: number }
|
|
234
|
+
| { type: 'deleteBlock', blockId: BlockId }
|
|
235
|
+
| { type: 'updateText', blockId: BlockId, content: RichTextNode[] }
|
|
236
|
+
| { type: 'updateSection', sectionId: SectionId, title?: string, layout?: Layout }
|
|
237
|
+
| { type: 'setStepState', stepId: StepId, visuals: VisualState[], inherits?: StepId }
|
|
238
|
+
| { type: 'setNarration', narration: Narration, stepId?: StepId, index?: number }
|
|
239
|
+
| { type: 'setTiming', stepId?: StepId, sectionId?: SectionId, value: StepProduction }
|
|
240
|
+
| { type: 'replaceAsset', asset: Asset }
|