@lincyaw/kernel 1.0.0-rc.13 → 1.0.0-rc.14
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/package.json +2 -1
- package/src/contracts/workspace.ts +2 -0
- package/src/embed.ts +26 -0
- package/src/escape-html.ts +4 -0
- package/src/html.ts +1 -4
- package/src/index.ts +9 -4
- package/src/ir.ts +12 -0
- package/src/lower-myst.ts +3 -12
- package/src/myst-marks.ts +15 -0
- package/src/preview-frame-connect.ts +1123 -0
- package/src/preview-frame.ts +17 -1131
- package/src/stale-module.ts +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lincyaw/kernel",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.14",
|
|
4
4
|
"description": "Presmium's shared kernel: the IR and plugin contracts, the service registry, anchors and checks",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"ir"
|
|
16
16
|
],
|
|
17
17
|
"type": "module",
|
|
18
|
+
"sideEffects": false,
|
|
18
19
|
"engines": {
|
|
19
20
|
"node": ">=22.19"
|
|
20
21
|
},
|
|
@@ -94,6 +94,8 @@ export interface DocumentSummary {
|
|
|
94
94
|
description?: string
|
|
95
95
|
lang?: string
|
|
96
96
|
tags?: string[]
|
|
97
|
+
/** Whether its metadata asks for a presentation (`renderers.slides`), which a site serves below its pages. */
|
|
98
|
+
slides?: boolean
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
/** @experimental The workspace service's answer to `documents`. */
|
package/src/embed.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a page tells the page that embeds it in a frame, as `postMessage` to its parent: whether its current slide is
|
|
3
|
+
* drawn (no component is still loading) and the state of its lecture. An embedding page (a site's home page playing a
|
|
4
|
+
* document's presentation) keeps its own cover until `drawn`, and offers Play when the lecture is `waiting`.
|
|
5
|
+
*/
|
|
6
|
+
export const EMBED_MESSAGE = 'presmium:state'
|
|
7
|
+
|
|
8
|
+
export type EmbeddedLecture = 'loading' | 'waiting' | 'playing' | 'paused' | 'failed'
|
|
9
|
+
|
|
10
|
+
export interface EmbedState {
|
|
11
|
+
type: typeof EMBED_MESSAGE
|
|
12
|
+
drawn: boolean
|
|
13
|
+
section?: string
|
|
14
|
+
step?: number
|
|
15
|
+
/** Absent when the page plays no lecture. `waiting`: the browser refused autoplay; the reader's Play starts it. */
|
|
16
|
+
lecture?: EmbeddedLecture
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let last: EmbedState = { type: EMBED_MESSAGE, drawn: false }
|
|
20
|
+
|
|
21
|
+
/** Sends the page's state, merged with what was sent before, to the embedding page; nothing when not in a frame. */
|
|
22
|
+
export function notifyEmbedder(change: Partial<Omit<EmbedState, 'type'>>): void {
|
|
23
|
+
if (typeof window === 'undefined' || window.parent === window) return
|
|
24
|
+
last = { ...last, ...change }
|
|
25
|
+
window.parent.postMessage(last, '*')
|
|
26
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Text as it reads in HTML, in an element or an attribute's value; apart from `html.ts`, so that escaping loads no HTML parser. */
|
|
2
|
+
export function escapeHtml(text: string): string {
|
|
3
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
4
|
+
}
|
package/src/html.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { parseFragment } from 'parse5'
|
|
2
2
|
import type { DefaultTreeAdapterMap } from 'parse5'
|
|
3
|
-
|
|
4
|
-
export function escapeHtml(text: string): string {
|
|
5
|
-
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
6
|
-
}
|
|
3
|
+
import { escapeHtml } from './escape-html.ts'
|
|
7
4
|
|
|
8
5
|
export interface HtmlAttribute {
|
|
9
6
|
name: string
|
package/src/index.ts
CHANGED
|
@@ -25,7 +25,8 @@ export { errorMessage, isRecord, PreviewPreparing } from './errors.ts'
|
|
|
25
25
|
export { createEventBus } from './events.ts'
|
|
26
26
|
export { CREDENTIAL_VARIABLE, environmentExecutor, failedRun, languageId } from './execution.ts'
|
|
27
27
|
export { gitConfigArgs, gitEnvironment } from './git.ts'
|
|
28
|
-
export { escapeHtml
|
|
28
|
+
export { escapeHtml } from './escape-html.ts'
|
|
29
|
+
export { htmlAttributes, mapHtmlUrls, URL_ATTRIBUTES } from './html.ts'
|
|
29
30
|
export type { HtmlAttribute } from './html.ts'
|
|
30
31
|
export { hostName, localRequestRefusal } from './requests.ts'
|
|
31
32
|
export type { RequestHeaders } from './requests.ts'
|
|
@@ -39,9 +40,13 @@ export type { KernelOptions } from './kernel.ts'
|
|
|
39
40
|
export { deckLocale, localeOf } from './locale.ts'
|
|
40
41
|
export { ANCHOR_LOCK_PATH, AnchorLock } from './lock.ts'
|
|
41
42
|
export { prefixedLogger } from './logging.ts'
|
|
42
|
-
export { lowerMyst
|
|
43
|
+
export { lowerMyst } from './lower-myst.ts'
|
|
43
44
|
export type { LinkedType, LoweredMarkdown, LowerOptions } from './lower-myst.ts'
|
|
44
|
-
export {
|
|
45
|
+
export { isPreviewActive, isPreviewEmbedded, previewAppearance, reportPreview, siteFrameAncestors } from './preview-frame.ts'
|
|
46
|
+
export { connectPreviewFrame } from './preview-frame-connect.ts'
|
|
47
|
+
export { reportStaleModule, STALE_MODULE_EVENT, STALE_MODULE_PATTERN } from './stale-module.ts'
|
|
48
|
+
export { EMBED_MESSAGE, notifyEmbedder } from './embed.ts'
|
|
49
|
+
export type { EmbeddedLecture, EmbedState } from './embed.ts'
|
|
45
50
|
export { keyBelongsToTarget, watchFocusSource } from './keyboard.ts'
|
|
46
51
|
export type { PreviewFrame, PreviewPage, PreviewProblem, PreviewTextEdit, PreviewTextHandle } from './preview-frame.ts'
|
|
47
52
|
export { previewEditBinding } from './preview-edit.ts'
|
|
@@ -65,7 +70,7 @@ export type { StepKeys } from './step-keys.ts'
|
|
|
65
70
|
export { parseDuration } from './durations.ts'
|
|
66
71
|
export { stepStateAt } from './steps.ts'
|
|
67
72
|
export { ABSOLUTE_URL, NON_FILE_URL, urlSlug } from './urls.ts'
|
|
68
|
-
export { isButton } from './
|
|
73
|
+
export { isButton, roleMark } from './myst-marks.ts'
|
|
69
74
|
export { componentProps, findComponentRenderer, hasRendererExtension, WEB_RENDERER_PACKAGE, withPublishedAssets } from './renderer-components.ts'
|
|
70
75
|
export { htmlAsText, isProseRun } from './run-outputs.ts'
|
|
71
76
|
export { safeDecodeURIComponent } from './uri-decoding.ts'
|
package/src/ir.ts
CHANGED
|
@@ -326,11 +326,23 @@ export interface SiteDocument {
|
|
|
326
326
|
description?: string
|
|
327
327
|
lang?: string
|
|
328
328
|
tags?: string[]
|
|
329
|
+
/** Whether it has a presentation, before its IR is compiled (`DocumentSummary.slides`). */
|
|
330
|
+
slides?: boolean
|
|
329
331
|
doc?: IRDocument
|
|
330
332
|
translations?: { lang: string, doc: IRDocument }[]
|
|
331
333
|
nodeVitePlugins?: NodeVitePlugin[]
|
|
332
334
|
}
|
|
333
335
|
|
|
336
|
+
/**
|
|
337
|
+
* A built site keeps its viewer apps below its path, each at `<APP_DIR>/<key>/` by its content. A presentation built as
|
|
338
|
+
* part of a site (renderer option `appBase`, the site's path) names its app there, so the site's pages and all its
|
|
339
|
+
* presentations load one app.
|
|
340
|
+
*/
|
|
341
|
+
export const APP_DIR = '_app'
|
|
342
|
+
|
|
343
|
+
/** A site serves a document's presentation (`renderers.slides`) below the document's pages, at `<document>/<SLIDES_DIR>/`. */
|
|
344
|
+
export const SLIDES_DIR = 'slides'
|
|
345
|
+
|
|
334
346
|
export interface SiteSpec {
|
|
335
347
|
title: string
|
|
336
348
|
documents: SiteDocument[]
|
package/src/lower-myst.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { escapeHtml as escape
|
|
1
|
+
import { escapeHtml as escape } from './escape-html.ts'
|
|
2
|
+
import { mapHtmlUrls } from './html.ts'
|
|
3
|
+
import { isButton, roleMark } from './myst-marks.ts'
|
|
2
4
|
import type { MdastNode } from './ir.ts'
|
|
3
5
|
|
|
4
6
|
export type LinkedType = 'image' | 'link' | 'definition'
|
|
@@ -198,11 +200,6 @@ function cardsAs(options: LowerOptions): 'element' | 'headings' {
|
|
|
198
200
|
return options.cards ?? (options.iframes === 'element' ? 'element' : 'headings')
|
|
199
201
|
}
|
|
200
202
|
|
|
201
|
-
/** Whether a MyST node has the class `button`, which renders a link or span as a button. */
|
|
202
|
-
export function isButton(node: MdastNode): boolean {
|
|
203
|
-
return typeof node.class === 'string' && node.class.split(/\s+/).includes('button')
|
|
204
|
-
}
|
|
205
|
-
|
|
206
203
|
function layout(node: MdastNode, ctx: Lowering): MdastNode[] {
|
|
207
204
|
const all = (nodes: MdastNode[] = []) => nodes.flatMap(child => lower(child, ctx))
|
|
208
205
|
const element = cardsAs(ctx.options) === 'element'
|
|
@@ -248,12 +245,6 @@ function layout(node: MdastNode, ctx: Lowering): MdastNode[] {
|
|
|
248
245
|
return all(items)
|
|
249
246
|
}
|
|
250
247
|
|
|
251
|
-
export function roleMark(node: MdastNode): string {
|
|
252
|
-
const options = typeof node.options === 'object' && node.options !== null ? node.options as Record<string, unknown> : {}
|
|
253
|
-
const value = typeof options.value === 'string' ? options.value.trim() : ''
|
|
254
|
-
return `〔${String(node.name ?? '')}${value ? ` ${value}` : ''}〕`
|
|
255
|
-
}
|
|
256
|
-
|
|
257
248
|
function footnoteNumber(node: MdastNode): string {
|
|
258
249
|
return String(node.enumerator ?? node.label ?? node.identifier)
|
|
259
250
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MdastNode } from './ir.ts'
|
|
2
|
+
|
|
3
|
+
// What a viewer reads from MyST nodes as they are, apart from their lowering (`lower-myst.ts`), which rewrites HTML.
|
|
4
|
+
|
|
5
|
+
/** Whether a MyST node has the class `button`, which renders a link or span as a button. */
|
|
6
|
+
export function isButton(node: MdastNode): boolean {
|
|
7
|
+
return typeof node.class === 'string' && node.class.split(/\s+/).includes('button')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** The mark a role without content shows: its name and value. */
|
|
11
|
+
export function roleMark(node: MdastNode): string {
|
|
12
|
+
const options = typeof node.options === 'object' && node.options !== null ? node.options as Record<string, unknown> : {}
|
|
13
|
+
const value = typeof options.value === 'string' ? options.value.trim() : ''
|
|
14
|
+
return `〔${String(node.name ?? '')}${value ? ` ${value}` : ''}〕`
|
|
15
|
+
}
|