@openpresentation/opf-editor 0.27.0 → 0.28.0
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 +4 -4
- package/dist/design-controls.js +3 -2
- package/dist/design-options.js +1 -1
- package/dist/json-editor.d.ts +33 -0
- package/dist/json-editor.js +83 -2
- package/dist/layout-adapter.js +96 -0
- package/dist/layout-canvas.js +122 -232
- package/dist/layout-editor-ui.js +46 -185
- package/dist/layout-inspector.js +36 -99
- package/dist/layout-layers.js +75 -92
- package/dist/layout-model.js +62 -103
- package/dist/react.d.ts +4 -0
- package/dist/react.js +9 -2
- package/dist/source-color-scheme.js +60 -0
- package/dist/source-pane.d.ts +85 -0
- package/dist/source-pane.js +151 -9
- package/dist/studio-kit/commands.d.ts +38 -0
- package/dist/studio-kit/commands.js +148 -0
- package/dist/studio-kit/dom.d.ts +36 -0
- package/dist/studio-kit/dom.js +162 -0
- package/dist/studio-kit/drag.d.ts +26 -0
- package/dist/studio-kit/drag.js +127 -0
- package/dist/studio-kit/gesture.d.ts +29 -0
- package/dist/studio-kit/gesture.js +87 -0
- package/dist/studio-kit/index.d.ts +29 -0
- package/dist/studio-kit/index.js +12 -0
- package/dist/studio-kit/inspector.d.ts +22 -0
- package/dist/studio-kit/inspector.js +119 -0
- package/dist/studio-kit/overlay.d.ts +29 -0
- package/dist/studio-kit/overlay.js +182 -0
- package/dist/studio-kit/selection.d.ts +30 -0
- package/dist/studio-kit/selection.js +106 -0
- package/dist/studio-kit/tree.d.ts +9 -0
- package/dist/studio-kit/tree.js +82 -0
- package/dist/studio-kit/viewport.d.ts +29 -0
- package/dist/studio-kit/viewport.js +153 -0
- package/dist/styles.css +28 -7
- package/dist/workspace.d.ts +26 -1
- package/dist/workspace.js +13 -0
- package/package.json +10 -10
package/dist/styles.css
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
--opf-ui-border-strong: hsl(214 22% 72%);
|
|
20
20
|
--opf-ui-ring: hsl(214 57% 28%);
|
|
21
21
|
--opf-ui-danger: hsl(0 72% 45%);
|
|
22
|
-
--opf-ui-warn: hsl(28 75%
|
|
22
|
+
--opf-ui-warn: hsl(28 75% 30%);
|
|
23
23
|
--opf-ui-success: hsl(152 76% 26%);
|
|
24
24
|
--opf-ui-code-bg: hsl(220 30% 96%);
|
|
25
25
|
--opf-ui-code-border: hsl(214 18% 84%);
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
--opf-ui-border-strong: hsl(214 22% 72%);
|
|
91
91
|
--opf-ui-ring: hsl(214 57% 28%);
|
|
92
92
|
--opf-ui-danger: hsl(0 72% 45%);
|
|
93
|
-
--opf-ui-warn: hsl(28 75%
|
|
93
|
+
--opf-ui-warn: hsl(28 75% 30%);
|
|
94
94
|
--opf-ui-success: hsl(152 76% 26%);
|
|
95
95
|
--opf-ui-code-bg: hsl(220 30% 96%);
|
|
96
96
|
--opf-ui-code-border: hsl(214 18% 84%);
|
|
@@ -3673,9 +3673,21 @@ input.opf-lx-input[data-field="max"] { font-family: var(--opf-ui-mono); font-siz
|
|
|
3673
3673
|
background: color-mix(in srgb, var(--opf-ui-accent) 8%, transparent);
|
|
3674
3674
|
}
|
|
3675
3675
|
|
|
3676
|
-
:is(
|
|
3677
|
-
:
|
|
3678
|
-
|
|
3676
|
+
/* The selection is a layer behind the text, so the text keeps its own colors: the tint is light enough that every token color (test/selection-contrast-browser.mjs
|
|
3677
|
+
measures each) keeps 4.5:1 against it, focused or not, in the light and the dark scheme. The focused rule of CodeMirror's base theme
|
|
3678
|
+
(`&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground`, `#d7d4f0` for light and `#233` for dark) has five classes, so
|
|
3679
|
+
the second selector has six and wins whichever stylesheet loads last; src/source-color-scheme.js flags the view dark so the rest of the base theme follows. */
|
|
3680
|
+
:is(.opf-ui, .opf-json, .opf-code) .cm-editor .cm-selectionLayer .cm-selectionBackground,
|
|
3681
|
+
:is(.opf-ui, .opf-json, .opf-code) .cm-editor.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground {
|
|
3682
|
+
background: color-mix(in srgb, var(--opf-ui-accent) 16%, transparent);
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
/* A selection that is not drawn by CodeMirror (the handoff prompt, the data grid's text areas, the numbering preview) uses the browser's own, and a browser's
|
|
3686
|
+
default highlight is not made for our surfaces: the same tint, with the text color the surface already has, is 4.5:1 or better in both schemes. */
|
|
3687
|
+
:is(.opf-json, .opf-code, .opf-source-code, .opf-handoff-box, .opf-numbering-preview) ::selection,
|
|
3688
|
+
:is(.opf-handoff-prompt, .opf-numbering-preview, .opf-grid details textarea)::selection {
|
|
3689
|
+
background: color-mix(in srgb, var(--opf-ui-accent) 16%, transparent);
|
|
3690
|
+
color: var(--opf-ui-fg);
|
|
3679
3691
|
}
|
|
3680
3692
|
|
|
3681
3693
|
:is(.opf-ui, .opf-json, .opf-code) .cm-selectionMatch {
|
|
@@ -4518,8 +4530,7 @@ input.opf-lx-input[data-field="max"] { font-family: var(--opf-ui-mono); font-siz
|
|
|
4518
4530
|
.opf-source-code .cm-gutters { border: 0; background: transparent; color: color-mix(in srgb, var(--opf-ui-muted-fg) 60%, transparent); }
|
|
4519
4531
|
.opf-source-code .cm-lineNumbers .cm-gutterElement { min-width: 30px; padding: 0 12px 0 0; text-align: right; font-size: 11px; }
|
|
4520
4532
|
.opf-source-code .cm-activeLine { background: transparent; }
|
|
4521
|
-
.
|
|
4522
|
-
.opf-source-code .cm-focused .cm-selectionBackground { background: color-mix(in srgb, var(--opf-ui-accent) 16%, transparent); }
|
|
4533
|
+
/* The selection color is the shared one in panels.css (JSON editor section). */
|
|
4523
4534
|
.opf-source-code .cm-cursor { border-left-color: var(--opf-ui-fg); }
|
|
4524
4535
|
.opf-source-code .tok-key { color: var(--opf-ui-accent-strong); }
|
|
4525
4536
|
.opf-source-code .tok-str { color: var(--opf-ui-fg); }
|
|
@@ -4532,6 +4543,16 @@ input.opf-lx-input[data-field="max"] { font-family: var(--opf-ui-mono); font-siz
|
|
|
4532
4543
|
.opf-source-code .tok-comment { font-style: italic; }
|
|
4533
4544
|
.opf-source-code .code-key.is-active { background: color-mix(in srgb, var(--opf-ui-accent) 10%, transparent); border-radius: var(--opf-ui-radius-sm); }
|
|
4534
4545
|
.opf-source-code .cm-line.is-hover { background: color-mix(in srgb, var(--opf-ui-accent) 6%, transparent); }
|
|
4546
|
+
/* CE-17: decorations a host draws (setDecorations) and the flash of revealPath; the host styles any other kind as .opf-source-decoration-<kind>. */
|
|
4547
|
+
.opf-source-code .opf-source-decoration { background: color-mix(in srgb, var(--opf-ui-accent) 16%, transparent); border-radius: var(--opf-ui-radius-sm); }
|
|
4548
|
+
.opf-source-code .opf-source-decoration-error { background: color-mix(in srgb, var(--opf-ui-danger) 18%, transparent); }
|
|
4549
|
+
.opf-source-code .opf-source-decoration-warning { background: color-mix(in srgb, var(--opf-ui-warn) 20%, transparent); }
|
|
4550
|
+
.opf-source-code .opf-source-decoration-info { background: color-mix(in srgb, var(--opf-ui-accent) 10%, transparent); }
|
|
4551
|
+
.opf-source-code .opf-source-decoration-added { background: color-mix(in srgb, var(--opf-ui-success) 20%, transparent); }
|
|
4552
|
+
.opf-source-code .opf-source-decoration-changed { background: color-mix(in srgb, var(--opf-ui-warn) 14%, transparent); }
|
|
4553
|
+
.opf-source-code .cm-line.opf-source-flash { animation: opf-source-flash 1.2s ease-out; }
|
|
4554
|
+
@keyframes opf-source-flash { from { background: color-mix(in srgb, var(--opf-ui-accent) 32%, transparent); } to { background: transparent; } }
|
|
4555
|
+
@media (prefers-reduced-motion: reduce) { .opf-source-code .cm-line.opf-source-flash { animation: none; background: color-mix(in srgb, var(--opf-ui-accent) 20%, transparent); } }
|
|
4535
4556
|
.opf-source-code .cm-panels { background: var(--opf-ui-surface); color: var(--opf-ui-fg); border-color: var(--opf-ui-border); }
|
|
4536
4557
|
.opf-source-code .cm-tooltip { background: var(--opf-ui-surface); border: 1px solid var(--opf-ui-border); border-radius: var(--opf-ui-radius); color: var(--opf-ui-fg); }
|
|
4537
4558
|
.opf-source-code .cm-lintRange-error { background-image: none; text-decoration: underline wavy var(--opf-ui-danger); }
|
package/dist/workspace.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ import type { Presenter } from "./presenter.js";
|
|
|
11
11
|
import type { ReviewFinding, ReviewHook } from "./review.js";
|
|
12
12
|
import type { ReviewPanel, ReviewPanelOptions } from "./review-panel.js";
|
|
13
13
|
import type { SlideManager } from "./slide-manager.js";
|
|
14
|
-
import type { SourceFormat, SourceFile, SourcePane, SourcePaneOptions } from "./source-pane.js";
|
|
14
|
+
import type { SourceCursor, SourceFormat, SourceFile, SourcePane, SourcePaneOptions } from "./source-pane.js";
|
|
15
15
|
|
|
16
16
|
export type OpfEditorLayout = "full" | "canvas" | "source";
|
|
17
17
|
export type OpfEditorView = "slide" | "sorter" | "outline" | "layouts";
|
|
@@ -77,6 +77,19 @@ export interface ThemeController {
|
|
|
77
77
|
/** Apply a theme to the editor mounted in `root`. `createOpfEditor` does this for `options.theme` and `handle.setTheme`. */
|
|
78
78
|
export declare function createThemeController(root: HTMLElement, theme?: OpfEditorTheme): ThemeController;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* The options for composing slide `slideIndex` of `deck` with the fonts handle `fonts`: its text measurement, made script-aware for this document
|
|
82
|
+
* (Japanese under Aptos measures with Noto Sans JP, where the plain strict measurement would throw). Pass the result to the session's
|
|
83
|
+
* `composeSlide` and `paginateSlide`, and the same `catalogs` the editor was given. Without a handle it is `{}`, and a handle without a
|
|
84
|
+
* measurement is passed through.
|
|
85
|
+
*/
|
|
86
|
+
export declare function layoutOptionsFor(
|
|
87
|
+
fonts: EditorFonts | undefined,
|
|
88
|
+
deck: unknown,
|
|
89
|
+
slideIndex: number,
|
|
90
|
+
catalogs?: readonly Catalog[],
|
|
91
|
+
): { fonts?: EditorFonts };
|
|
92
|
+
|
|
80
93
|
/** What a host's own acquire function returns: the handle and the way to give it back. */
|
|
81
94
|
export interface OpfEditorFontsLease {
|
|
82
95
|
fonts: EditorFonts;
|
|
@@ -168,6 +181,8 @@ export interface OpfEditorSourceOptions {
|
|
|
168
181
|
checks?: { only?: readonly string[]; [key: string]: unknown };
|
|
169
182
|
outline?: SourcePaneOptions["outline"];
|
|
170
183
|
applyDelay?: number;
|
|
184
|
+
/** Milliseconds of quiet before a `cursor` event (default 50). */
|
|
185
|
+
cursorDelay?: number;
|
|
171
186
|
completions?: boolean;
|
|
172
187
|
label?: string;
|
|
173
188
|
catalogs?: SourcePaneOptions["catalogs"];
|
|
@@ -240,6 +255,8 @@ export interface OpfEditorOptions {
|
|
|
240
255
|
|
|
241
256
|
onChange?(detail: OpfEditorEvents["change"]): void;
|
|
242
257
|
onSelection?(detail: OpfEditorEvents["selection"]): void;
|
|
258
|
+
/** The caret of the source pane moved to another place in the text. The same as `on: { cursor }`. */
|
|
259
|
+
onCursor?(detail: OpfEditorEvents["cursor"]): void;
|
|
243
260
|
onError?(error: Error, detail: Omit<OpfEditorEvents["error"], "error">): void;
|
|
244
261
|
onReady?(detail: OpfEditorEvents["ready"]): void;
|
|
245
262
|
/** Listeners by event name; the same as calling `on` before anything happens. */
|
|
@@ -261,6 +278,12 @@ export interface OpfEditorEvents {
|
|
|
261
278
|
/** The document changed (an edit in the canvas or the source, undo, redo, a loaded document). Not fired for a catalogs change. */
|
|
262
279
|
change: { document: unknown; canUndo: boolean; canRedo: boolean; event: EditorEvent };
|
|
263
280
|
selection: { path: string; value: unknown; slideIndex: number; element: Element | undefined };
|
|
281
|
+
/**
|
|
282
|
+
* The caret or selection of the source pane moved, in JSON, YAML or Markdown, also while the pane has no focus: the path under it as a JSON Pointer
|
|
283
|
+
* and by id, and the selection in source offsets. Quiet for `source.cursorDelay` first and not repeated for a place already reported; the detail is
|
|
284
|
+
* frozen. For the rest of the pane (`revealPath`, `setDecorations`, `getCursor`) use `editor.sourcePane`.
|
|
285
|
+
*/
|
|
286
|
+
cursor: SourceCursor;
|
|
264
287
|
/** The slide changed (0-based). */
|
|
265
288
|
slide: { index: number; count: number };
|
|
266
289
|
view: { view: OpfEditorView; previous: OpfEditorView };
|
|
@@ -333,6 +356,8 @@ export interface OpfEditor {
|
|
|
333
356
|
readonly readOnly: boolean;
|
|
334
357
|
/** False while the text in the source pane is not a valid deck: the slide shows the last valid one. */
|
|
335
358
|
readonly inSync: boolean;
|
|
359
|
+
/** The latest `cursor` event, or `null` before the first (and without a source pane). The same object until the next event. */
|
|
360
|
+
readonly sourceCursor: SourceCursor | null;
|
|
336
361
|
/** The latest verdict on the text of the source pane (what the last `source` event carried), or `null` before the first. */
|
|
337
362
|
readonly sourceState: OpfEditorSourceState | null;
|
|
338
363
|
readonly lastError: Error | undefined;
|
package/dist/workspace.js
CHANGED
|
@@ -36,6 +36,8 @@ import { retainedSlideIndex } from "./slide-retention.js";
|
|
|
36
36
|
import { createSourceMemory, updateJsonSource } from "./exact-source.js";
|
|
37
37
|
|
|
38
38
|
export { THEME_TOKENS, createThemeController, normalizeTheme, normalizeThemeTokens } from "./workspace-theme.js";
|
|
39
|
+
// The script-aware measurement options for composing one slide: a host that calls `composeSlide` or `paginateSlide` on the session passes these (the playground does).
|
|
40
|
+
export { layoutOptionsFor } from "./workspace-measure.js";
|
|
39
41
|
|
|
40
42
|
const VIEWS = Object.freeze(["slide", "sorter", "outline", "layouts"]);
|
|
41
43
|
const MUTATORS = new Set(["set", "setGroupComposition", "setCatalog", "setCatalogs", "applyPatch", "replaceDocument", "restoreState", "undo", "redo", "setComposition", "paginateSlide"]);
|
|
@@ -150,6 +152,7 @@ export function createOpfEditor(element, options = {}) {
|
|
|
150
152
|
for (const [name, listener] of Object.entries(options.on ?? {})) on(name, listener);
|
|
151
153
|
if (options.onChange) on("change", options.onChange);
|
|
152
154
|
if (options.onSelection) on("selection", options.onSelection);
|
|
155
|
+
if (options.onCursor) on("cursor", options.onCursor);
|
|
153
156
|
if (options.onError) on("error", ({ error, ...detail }) => options.onError(error, detail));
|
|
154
157
|
if (options.onReady) on("ready", options.onReady);
|
|
155
158
|
|
|
@@ -220,6 +223,7 @@ export function createOpfEditor(element, options = {}) {
|
|
|
220
223
|
let drafting = false;
|
|
221
224
|
let openedText;
|
|
222
225
|
let sourceState = null;
|
|
226
|
+
let sourceCursor = null;
|
|
223
227
|
let fontsFailure;
|
|
224
228
|
let refreshToken = 0;
|
|
225
229
|
let lastRefresh = Promise.resolve();
|
|
@@ -538,6 +542,7 @@ export function createOpfEditor(element, options = {}) {
|
|
|
538
542
|
return {
|
|
539
543
|
...(configured.outline !== undefined ? { outline: configured.outline } : {}),
|
|
540
544
|
...(configured.applyDelay !== undefined ? { applyDelay: configured.applyDelay } : {}),
|
|
545
|
+
...(configured.cursorDelay !== undefined ? { cursorDelay: configured.cursorDelay } : {}),
|
|
541
546
|
...(configured.completions !== undefined ? { completions: configured.completions } : {}),
|
|
542
547
|
...(configured.catalogs !== undefined ? { catalogs: configured.catalogs } : {}),
|
|
543
548
|
...(configured.name !== undefined ? { name: configured.name } : {}),
|
|
@@ -553,6 +558,11 @@ export function createOpfEditor(element, options = {}) {
|
|
|
553
558
|
configured.onRename?.(name);
|
|
554
559
|
emit("rename", { name });
|
|
555
560
|
},
|
|
561
|
+
// CE-17: the path under the caret of the source pane, as `cursor` events.
|
|
562
|
+
onCursor: (detail) => {
|
|
563
|
+
sourceCursor = Object.freeze(detail);
|
|
564
|
+
emit("cursor", sourceCursor);
|
|
565
|
+
},
|
|
556
566
|
getFindings: ({ format, text: shown }) => {
|
|
557
567
|
reportedText = shown;
|
|
558
568
|
const made = reportSource(format, shown, { catalogs: catalogs(), checks: configured.checks ?? SOURCE_CHECKS });
|
|
@@ -1090,6 +1100,9 @@ export function createOpfEditor(element, options = {}) {
|
|
|
1090
1100
|
return inSync;
|
|
1091
1101
|
},
|
|
1092
1102
|
/** The latest verdict on the text of the source pane (what the last `source` event carried), or `null` before the first. */
|
|
1103
|
+
get sourceCursor() {
|
|
1104
|
+
return sourceCursor;
|
|
1105
|
+
},
|
|
1093
1106
|
get sourceState() {
|
|
1094
1107
|
return sourceState;
|
|
1095
1108
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "Embeddable local OPF editor primitives built on the deterministic renderer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -283,7 +283,7 @@
|
|
|
283
283
|
"types": "./dist/index.d.ts",
|
|
284
284
|
"sideEffects": false,
|
|
285
285
|
"dependencies": {
|
|
286
|
-
"@codemirror/autocomplete": "6.20.
|
|
286
|
+
"@codemirror/autocomplete": "6.20.4",
|
|
287
287
|
"@codemirror/commands": "6.11.1",
|
|
288
288
|
"@codemirror/lang-json": "6.0.2",
|
|
289
289
|
"@codemirror/lang-yaml": "6.1.3",
|
|
@@ -291,15 +291,15 @@
|
|
|
291
291
|
"@codemirror/lint": "6.9.7",
|
|
292
292
|
"@codemirror/search": "6.7.2",
|
|
293
293
|
"@codemirror/state": "6.7.6",
|
|
294
|
-
"@codemirror/view": "6.43.
|
|
294
|
+
"@codemirror/view": "6.43.15",
|
|
295
295
|
"@lezer/highlight": "1.2.5",
|
|
296
296
|
"jsonc-parser": "3.3.1",
|
|
297
297
|
"yaml": "^2.9.1",
|
|
298
|
-
"@openpresentation/opf": "^0.
|
|
298
|
+
"@openpresentation/opf": "^0.28.0"
|
|
299
299
|
},
|
|
300
300
|
"peerDependencies": {
|
|
301
301
|
"react": ">=18",
|
|
302
|
-
"@openpresentation/opf-render": "^0.
|
|
302
|
+
"@openpresentation/opf-render": "^0.28.0"
|
|
303
303
|
},
|
|
304
304
|
"peerDependenciesMeta": {
|
|
305
305
|
"@openpresentation/opf-render": {
|
|
@@ -360,14 +360,14 @@
|
|
|
360
360
|
"jszip": "3.10.2",
|
|
361
361
|
"pdf-lib": "1.17.1",
|
|
362
362
|
"pdfjs-dist": "6.4.299",
|
|
363
|
-
"playwright": "1.64.
|
|
363
|
+
"playwright": "1.64.1",
|
|
364
364
|
"react": "19.3.1",
|
|
365
365
|
"react-dom": "19.3.1",
|
|
366
366
|
"react-18": "npm:react@18.3.1",
|
|
367
367
|
"react-dom-18": "npm:react-dom@18.3.1",
|
|
368
|
-
"@openpresentation/
|
|
369
|
-
"@openpresentation/
|
|
370
|
-
"@openpresentation/opf-render": "0.
|
|
368
|
+
"@openpresentation/gallery": "0.28.0",
|
|
369
|
+
"@openpresentation/opf-pptx": "0.28.0",
|
|
370
|
+
"@openpresentation/opf-render": "0.28.0"
|
|
371
371
|
},
|
|
372
372
|
"scripts": {
|
|
373
373
|
"build": "node scripts/build.mjs",
|
|
@@ -420,5 +420,5 @@
|
|
|
420
420
|
"test:workspace-browser": "node test/workspace-browser.mjs",
|
|
421
421
|
"test:workspace-react-browser": "node test/workspace-react-browser.mjs"
|
|
422
422
|
},
|
|
423
|
-
"gitHead": "
|
|
423
|
+
"gitHead": "abc56634a584db38a305a27edc3bc76b4116637b"
|
|
424
424
|
}
|