@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.
Files changed (119) hide show
  1. package/README.md +7 -7
  2. package/package.json +6 -2
  3. package/src/access.ts +18 -0
  4. package/src/anchors.ts +5 -1
  5. package/src/authoring.ts +56 -0
  6. package/src/blocks.ts +0 -8
  7. package/src/component-code.ts +6 -0
  8. package/src/contracts/agent.ts +5 -82
  9. package/src/contracts/anchors.ts +46 -0
  10. package/src/contracts/authoring-actions.ts +60 -0
  11. package/src/contracts/authoring-clients.ts +213 -0
  12. package/src/contracts/authoring.ts +180 -0
  13. package/src/contracts/checks.ts +29 -0
  14. package/src/contracts/classroom.ts +49 -0
  15. package/src/contracts/diagnostics.ts +37 -0
  16. package/src/contracts/document-audio.ts +150 -0
  17. package/src/contracts/document-comments.ts +31 -0
  18. package/src/contracts/document-history.ts +63 -0
  19. package/src/contracts/document-image.ts +32 -0
  20. package/src/contracts/document-reads.ts +57 -0
  21. package/src/contracts/document-voice.ts +44 -0
  22. package/src/contracts/document.ts +240 -0
  23. package/src/contracts/execution.ts +4 -5
  24. package/src/contracts/locale.ts +3 -0
  25. package/src/contracts/offline.ts +2 -0
  26. package/src/contracts/plugin.ts +183 -0
  27. package/src/contracts/presenter-panels.ts +17 -0
  28. package/src/contracts/preview.ts +305 -310
  29. package/src/contracts/production-assets.ts +61 -0
  30. package/src/contracts/production.ts +109 -0
  31. package/src/contracts/projects.ts +59 -0
  32. package/src/contracts/server.ts +1 -51
  33. package/src/contracts/speech-settings.ts +16 -0
  34. package/src/contracts/speech.ts +82 -70
  35. package/src/contracts/steps.ts +15 -0
  36. package/src/contracts/transcription-adoption.ts +79 -0
  37. package/src/contracts/transcription.ts +112 -0
  38. package/src/contracts/voice.ts +97 -0
  39. package/src/contracts/voiceover-execution.ts +68 -0
  40. package/src/contracts/workspace.ts +458 -0
  41. package/src/contracts.ts +28 -278
  42. package/src/diagnostics.ts +7 -0
  43. package/src/document-authoring-actions.ts +47 -0
  44. package/src/document-caption-timings.ts +64 -0
  45. package/src/document-colors.ts +25 -0
  46. package/src/document-comments.ts +118 -0
  47. package/src/document-components.ts +251 -0
  48. package/src/document-operations.ts +132 -0
  49. package/src/document-pages.ts +215 -0
  50. package/src/document-reads.ts +81 -0
  51. package/src/document-schema.ts +85 -0
  52. package/src/document-voices.ts +153 -0
  53. package/src/document.ts +252 -0
  54. package/src/durations.ts +7 -0
  55. package/src/errors.ts +8 -0
  56. package/src/execution.ts +5 -1
  57. package/src/fingerprint.ts +1 -1
  58. package/src/follow.ts +26 -30
  59. package/src/git.ts +1 -1
  60. package/src/index.ts +66 -13
  61. package/src/ir.ts +54 -5
  62. package/src/jupyter-kernel.ts +9 -0
  63. package/src/kernel.ts +38 -25
  64. package/src/keyboard.ts +43 -0
  65. package/src/locale.ts +13 -0
  66. package/src/lock.ts +4 -8
  67. package/src/logging.ts +1 -1
  68. package/src/{markdown.ts → lower-myst.ts} +2 -1
  69. package/src/node/assets.ts +23 -0
  70. package/src/node/authoring-actions.ts +50 -0
  71. package/src/node/authoring-clients.ts +14 -0
  72. package/src/node/document-audio.ts +22 -0
  73. package/src/node/document-history.ts +11 -0
  74. package/src/node/document-image.ts +8 -0
  75. package/src/node/document-reads.ts +10 -0
  76. package/src/node/document-voice.ts +7 -0
  77. package/src/node/ffmpeg.ts +29 -0
  78. package/src/node/file-version.ts +6 -0
  79. package/src/node/fonts.ts +22 -0
  80. package/src/node/index.ts +29 -133
  81. package/src/node/installed-packages.ts +11 -0
  82. package/src/node/lines.ts +24 -0
  83. package/src/node/locale.ts +7 -0
  84. package/src/node/network.ts +11 -0
  85. package/src/node/operator-speech.ts +91 -0
  86. package/src/node/packages.ts +74 -0
  87. package/src/node/paths.ts +7 -0
  88. package/src/node/projects.ts +7 -0
  89. package/src/node/resources.ts +86 -0
  90. package/src/node/revision.ts +6 -0
  91. package/src/node/run-directory.ts +70 -0
  92. package/src/node/transcription-adoption.ts +9 -0
  93. package/src/node/transcription.ts +10 -0
  94. package/src/node/voiceover-routes.ts +73 -0
  95. package/src/node/voiceover.ts +199 -0
  96. package/src/page-source.ts +359 -0
  97. package/src/parameters.ts +81 -0
  98. package/src/preview-edit.ts +18 -0
  99. package/src/preview-events.ts +57 -0
  100. package/src/preview-frame.ts +1220 -0
  101. package/src/props.ts +17 -0
  102. package/src/push-stream.ts +67 -0
  103. package/src/reading.ts +12 -7
  104. package/src/recording.ts +38 -0
  105. package/src/renderer-components.ts +30 -0
  106. package/src/responses.ts +49 -0
  107. package/src/run-outputs.ts +15 -0
  108. package/src/runner.py +3 -3
  109. package/src/services.ts +6 -6
  110. package/src/slides.ts +36 -0
  111. package/src/speech-words.ts +76 -0
  112. package/src/speech.ts +13 -0
  113. package/src/step-keys.ts +38 -0
  114. package/src/steps.ts +7 -0
  115. package/src/transcription-adoption.ts +105 -0
  116. package/src/uri-decoding.ts +8 -0
  117. package/src/urls.ts +13 -0
  118. package/src/views.ts +47 -0
  119. package/src/voice.ts +86 -0
@@ -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 }
@@ -1,4 +1,4 @@
1
- import type { Logger, Unsubscribe } from '../contracts.ts'
1
+ import type { Logger, Unsubscribe } from './plugin.ts'
2
2
  import { defineService } from './service.ts'
3
3
 
4
4
  export interface RunRequest {
@@ -10,6 +10,7 @@ export interface RunRequest {
10
10
  files?: Record<string, string>
11
11
  executor?: string
12
12
  session?: string
13
+ place?: { section: string, block: string }
13
14
  }
14
15
 
15
16
  export type RunOutput =
@@ -91,6 +92,8 @@ export interface EnvironmentSpec {
91
92
  kernel?: string
92
93
  languages?: string[]
93
94
  auth?: EnvironmentAuth
95
+ /** Operator-defined JupyterHub environment profile; images and resource limits stay on the Hub. */
96
+ profile?: string
94
97
  image?: string
95
98
  path?: string
96
99
  [option: string]: unknown
@@ -102,10 +105,6 @@ export interface ExecuteConfig {
102
105
  rerun?: 'environment'
103
106
  }
104
107
 
105
- export function environmentExecutor(name: string): string {
106
- return `presmium.env.${name}`
107
- }
108
-
109
108
  export interface EnvironmentContext {
110
109
  deckDir: string
111
110
  workspaceRoot: string
@@ -0,0 +1,3 @@
1
+ export type Locale = 'zh-CN' | 'en'
2
+
3
+ export const DEFAULT_LANG = 'zh-CN'
@@ -0,0 +1,2 @@
1
+ /** The requests a page sends the web form's service worker, with a MessageChannel port for the answer. */
2
+ export const OFFLINE_REQUESTS = { runtimes: 'presmium:offline-runtimes', save: 'presmium:save-offline' } as const
@@ -0,0 +1,183 @@
1
+ import type { CanonicalSource, DirectiveSpec, IRDocument, IRPass, Renderer, RendererContribution, RoleSpec } from '../ir.ts'
2
+ import type { AgentTool } from './agent.ts'
3
+ import type { AnchorResolver, AnchorString } from './anchors.ts'
4
+ import type { Check, CheckContext } from './checks.ts'
5
+ import type { EnvironmentProvider, EnvironmentSpec, Executor } from './execution.ts'
6
+ import type { NodeServiceContext, ServerContext, ServerContribution } from './server.ts'
7
+ import type { Locale } from './locale.ts'
8
+ import type { ServiceToken } from './service.ts'
9
+
10
+ export const KERNEL_VERSION = '1.0.0'
11
+
12
+ export type Capability = 'fs' | 'network' | 'exec' | 'git'
13
+
14
+ export interface ServiceRequirement {
15
+ token: ServiceToken<unknown>
16
+ optional?: boolean
17
+ }
18
+
19
+ export interface PluginManifest {
20
+ id: string
21
+ /** The name readers and authors see (the startup report), in both interface languages; the id is never shown. */
22
+ name?: Record<Locale, string>
23
+ version: string
24
+ kernel: string
25
+ provides?: ServiceToken<unknown>[]
26
+ requires?: ServiceRequirement[]
27
+ capabilities?: Capability[]
28
+ }
29
+
30
+ export type PresentationMode = 'slides' | 'document'
31
+
32
+ export interface Logger {
33
+ debug(...args: unknown[]): void
34
+ info(...args: unknown[]): void
35
+ warn(...args: unknown[]): void
36
+ error(...args: unknown[]): void
37
+ }
38
+
39
+ export type Unsubscribe = () => void
40
+
41
+ export interface DomainEvent<Type extends string = string, Payload = unknown> {
42
+ type: Type
43
+ payload: Payload
44
+ }
45
+
46
+ export interface EventBus {
47
+ emit(event: DomainEvent): void
48
+ on<E extends DomainEvent>(type: E['type'], handler: (event: E) => void): Unsubscribe
49
+ }
50
+
51
+ export interface PluginContext {
52
+ config: Record<string, unknown>
53
+ mode: PresentationMode
54
+ provide<T>(token: ServiceToken<T>, implementation: T): void
55
+ get<T>(token: ServiceToken<T>): T
56
+ tryGet<T>(token: ServiceToken<T>): T | undefined
57
+ events: EventBus
58
+ log: Logger
59
+ environments?: Readonly<Record<string, EnvironmentSpec>>
60
+ }
61
+
62
+ export interface PluginDefinition {
63
+ manifest: PluginManifest
64
+ /** Opt in only when dispose stops every resource and this definition can run setup again. */
65
+ restartable?: boolean
66
+ setup(ctx: PluginContext): void | Promise<void>
67
+ /** Also called after setup fails; releases resources acquired during partial initialization. */
68
+ dispose?(): void | Promise<void>
69
+ }
70
+
71
+ export type PluginStatus = 'started' | 'degraded' | 'disabled' | 'failed'
72
+
73
+ export interface PluginReport {
74
+ id: string
75
+ name?: Record<Locale, string>
76
+ status: PluginStatus
77
+ reason?: string
78
+ missing?: string[]
79
+ }
80
+
81
+ export interface StartReport {
82
+ order: string[]
83
+ plugins: PluginReport[]
84
+ cycles: string[][]
85
+ }
86
+
87
+ export type KernelEvent =
88
+ | DomainEvent<'kernel.plugin.started', { id: string }>
89
+ | DomainEvent<'kernel.plugin.degraded', { id: string; missing: string[] }>
90
+ | DomainEvent<'kernel.plugin.disabled', { id: string; reason: string }>
91
+ | DomainEvent<'kernel.plugin.failed', { id: string; error: string }>
92
+
93
+ /**
94
+ * A heavy output of a document that the plugin makes on request, as a job of the workspace service (the slides as a PDF,
95
+ * the lecture as a video): the job shows its progress and log, and keeps the files for the author and the AI.
96
+ */
97
+ export interface Exporter {
98
+ /** Unique among the outputs of a document, renderers' ids included (e.g. `pdf`, `video`). */
99
+ id: string
100
+ /** What the author sees, by language (`zh-CN`, `en`). */
101
+ label: Record<string, string>
102
+ /** Whether the document can be exported so (e.g. it has the slides form); every document when left out. */
103
+ available?(doc: IRDocument, config: Record<string, unknown>): boolean
104
+ run(ctx: ExportContext): Promise<ExportOutput>
105
+ }
106
+
107
+ export interface ExportContext {
108
+ deckDir: string
109
+ workspaceRoot: string
110
+ /** The exact canonical source from which the requested compilation was derived. */
111
+ source?: CanonicalSource
112
+ /** The plugin's configuration in the document (`presmium.<plugin id>`). */
113
+ config: Record<string, unknown>
114
+ /** An empty directory, the job's own, for the files. */
115
+ outDir: string
116
+ /** Aborted when the author cancels the job: the exporter stops its processes and rejects. */
117
+ signal: AbortSignal
118
+ /** Where the job is: a phase, and how far through it. */
119
+ progress(phase: string, done?: number, total?: number): void
120
+ /** A line of the job's log (a tool's output, a step). */
121
+ log(line: string): void
122
+ /** What the one who asked for the export chose, as the exporter documents it (`presmium-export`'s `--with-clicks`). */
123
+ options: Record<string, unknown>
124
+ /**
125
+ * Builds the document with renderer `renderer` (at the root path, with `config` over the document's configuration of
126
+ * the renderer) and gives the directory of its output, the job's own: an exporter of the slides prints them from it.
127
+ */
128
+ build(renderer: string, config?: Record<string, unknown>): Promise<string>
129
+ }
130
+
131
+ export interface ExportOutput {
132
+ /** The files made, below `outDir`, the one to show first. */
133
+ files: string[]
134
+ /** The WebVTT captions of the first file (a video), by language, among `files`. */
135
+ captions?: Record<string, string>
136
+ warnings?: string[]
137
+ }
138
+
139
+ /**
140
+ * Files of an installed package that a plugin's browser code loads while it runs (Pyodide, the WASI runtimes): served
141
+ * below the document's site path at `<base><prefix><file>` and copied there into a built site.
142
+ */
143
+ export interface RuntimeAssets {
144
+ name: string
145
+ /** The directory below the document's site path, e.g. `pyodide/`. */
146
+ prefix: string
147
+ /** The files that are served, with their content types. */
148
+ types: Readonly<Record<string, string>>
149
+ /** The files a build copies; default: all that are served. */
150
+ emitted?: readonly string[]
151
+ /** The path of a file on disk. */
152
+ source(file: string): string
153
+ }
154
+
155
+ export interface PresmiumNodePlugin {
156
+ id: string
157
+ version?: string
158
+ kernel?: string
159
+ resolvers?: AnchorResolver[]
160
+ checks?: Check[]
161
+ anchors?(ctx: CheckContext): AnchorString[] | Promise<AnchorString[]>
162
+ directives?: DirectiveSpec[]
163
+ passes?: IRPass[]
164
+ executors?: Executor[]
165
+ renderer?: Renderer
166
+ rendererExtensions?: Record<string, RendererContribution>
167
+ /** The files the plugin's browser code loads while it runs (a language runtime), for the document's configuration of the plugin. */
168
+ assets?(ctx: { deckDir: string, workspaceRoot: string, config: Record<string, unknown> }): RuntimeAssets[]
169
+ /**
170
+ * The virtual modules of the plugin's browser code whose content depends on the document (e.g. the list of its scene
171
+ * files), by id, with their source. The viewer app, prebuilt once for all documents, gets them from the document's own
172
+ * bundle, built with this source. Only their default export is read there.
173
+ */
174
+ documentModules?(ctx: { deckDir: string, workspaceRoot: string, config: Record<string, unknown> }): Record<string, string>
175
+ /** The heavy outputs the plugin makes on request, as jobs of the workspace service. */
176
+ exporters?: Exporter[]
177
+ services?(ctx: NodeServiceContext): [ServiceToken<unknown>, unknown][]
178
+ environments?: Record<string, EnvironmentProvider>
179
+ server?(ctx: ServerContext): ServerContribution | Promise<ServerContribution>
180
+ roles?: RoleSpec[]
181
+ tools?: AgentTool[]
182
+ operator?: boolean
183
+ }
@@ -0,0 +1,17 @@
1
+ import type { Unsubscribe } from './plugin.ts'
2
+ import { defineService } from './service.ts'
3
+
4
+ export interface PresenterPanel {
5
+ id: string
6
+ title: string
7
+ component: unknown
8
+ order?: number
9
+ }
10
+
11
+ export interface PresenterPanels {
12
+ register(panel: PresenterPanel): Unsubscribe
13
+ list(): PresenterPanel[]
14
+ subscribe(listener: () => void): Unsubscribe
15
+ }
16
+
17
+ export const PRESENTER_PANELS = defineService<PresenterPanels>('presmium.presenterPanels')