@openpresentation/opf-editor 0.24.2 → 0.25.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 +124 -2
- package/dist/galleries.js +2 -2
- package/dist/layout-canvas.js +748 -0
- package/dist/layout-diagram.js +25 -0
- package/dist/layout-editor-ui.js +232 -0
- package/dist/layout-editor.d.ts +212 -0
- package/dist/layout-editor.js +197 -0
- package/dist/layout-inspector.js +339 -0
- package/dist/layout-layers.js +331 -0
- package/dist/layout-model.js +299 -0
- package/dist/layout-palette.js +77 -0
- package/dist/layout-samples.js +84 -0
- package/dist/layout-templates.d.ts +32 -8
- package/dist/layout-templates.js +89 -37
- package/dist/layout-tree.d.ts +87 -0
- package/dist/layout-tree.js +704 -0
- package/dist/presenter.d.ts +105 -0
- package/dist/presenter.js +431 -0
- package/dist/read-deck-file.d.ts +59 -0
- package/dist/read-deck-file.js +142 -0
- package/dist/slide-manager.js +3 -7
- package/dist/styles.css +1003 -25
- package/package.json +25 -10
- package/dist/custom-layout-editor.d.ts +0 -7
- package/dist/custom-layout-editor.js +0 -105
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf-editor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Embeddable local OPF editor primitives built on the deterministic renderer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -190,9 +190,13 @@
|
|
|
190
190
|
"types": "./dist/layout-templates.d.ts",
|
|
191
191
|
"default": "./dist/layout-templates.js"
|
|
192
192
|
},
|
|
193
|
-
"./
|
|
194
|
-
"types": "./dist/
|
|
195
|
-
"default": "./dist/
|
|
193
|
+
"./layout-editor": {
|
|
194
|
+
"types": "./dist/layout-editor.d.ts",
|
|
195
|
+
"default": "./dist/layout-editor.js"
|
|
196
|
+
},
|
|
197
|
+
"./layout-tree": {
|
|
198
|
+
"types": "./dist/layout-tree.d.ts",
|
|
199
|
+
"default": "./dist/layout-tree.js"
|
|
196
200
|
},
|
|
197
201
|
"./outline-view": {
|
|
198
202
|
"types": "./dist/outline-view.d.ts",
|
|
@@ -230,6 +234,14 @@
|
|
|
230
234
|
"types": "./dist/source-pane.d.ts",
|
|
231
235
|
"default": "./dist/source-pane.js"
|
|
232
236
|
},
|
|
237
|
+
"./presenter": {
|
|
238
|
+
"types": "./dist/presenter.d.ts",
|
|
239
|
+
"default": "./dist/presenter.js"
|
|
240
|
+
},
|
|
241
|
+
"./read-deck-file": {
|
|
242
|
+
"types": "./dist/read-deck-file.d.ts",
|
|
243
|
+
"default": "./dist/read-deck-file.js"
|
|
244
|
+
},
|
|
233
245
|
"./export-menu": {
|
|
234
246
|
"types": "./dist/export-menu.d.ts",
|
|
235
247
|
"default": "./dist/export-menu.js"
|
|
@@ -270,7 +282,7 @@
|
|
|
270
282
|
"check:font-hotlinks": "node --test scripts/check-font-hotlinks.test.mjs && node scripts/check-font-hotlinks.mjs",
|
|
271
283
|
"check:font-hotlinks:built": "node scripts/check-font-hotlinks.mjs --built dist artifacts/playground",
|
|
272
284
|
"build:playground": "node scripts/build-playground.mjs",
|
|
273
|
-
"test:playground": "npm run build:playground && npm run check:font-hotlinks:built && node test/playground-pptx.mjs && node test/playground-script-fonts.mjs artifacts/playground-script-fonts && node test/playground-lazy-fonts.mjs artifacts/playground-lazy-fonts && node test/playground-base-fonts.mjs artifacts/playground-base-fonts && node test/playground-source.mjs && node test/design-controls-browser.mjs && node test/design-gaps-browser.mjs && node test/chart-options-browser.mjs && node test/persistence-browser.mjs && node test/slides-browser.mjs && node test/slide-navigator-browser.mjs && node test/layout-templates-browser.mjs && node test/template-panel-browser.mjs && node test/numbering-panel-browser.mjs && node test/playground-ensure-fonts.mjs artifacts/playground-ensure-fonts && node test/canvas-fonts-browser.mjs artifacts/canvas-fonts && node test/find-replace-browser.mjs && node test/image-crop-browser.mjs && node test/mobile-browser.mjs && node test/review-panel-browser.mjs && node test/review-hook-browser.mjs && node test/source-pane-browser.mjs && node test/playground-download.mjs artifacts/playground-download && node test/export-menu-browser.mjs && node test/agent-handoff-browser.mjs",
|
|
285
|
+
"test:playground": "npm run build:playground && npm run check:font-hotlinks:built && node test/playground-pptx.mjs && node test/playground-script-fonts.mjs artifacts/playground-script-fonts && node test/playground-lazy-fonts.mjs artifacts/playground-lazy-fonts && node test/playground-base-fonts.mjs artifacts/playground-base-fonts && node test/playground-source.mjs && node test/design-controls-browser.mjs && node test/design-gaps-browser.mjs && node test/chart-options-browser.mjs && node test/persistence-browser.mjs && node test/slides-browser.mjs && node test/slide-navigator-browser.mjs && node test/layout-templates-browser.mjs && node test/layout-editor-browser.mjs && node test/template-panel-browser.mjs && node test/numbering-panel-browser.mjs && node test/playground-ensure-fonts.mjs artifacts/playground-ensure-fonts && node test/canvas-fonts-browser.mjs artifacts/canvas-fonts && node test/find-replace-browser.mjs && node test/image-crop-browser.mjs && node test/mobile-browser.mjs && node test/review-panel-browser.mjs && node test/review-hook-browser.mjs && node test/source-pane-browser.mjs && node test/playground-download.mjs artifacts/playground-download && node test/export-menu-browser.mjs && node test/agent-handoff-browser.mjs && node test/presenter-browser.mjs && node test/present-button-browser.mjs",
|
|
274
286
|
"test:code-browser": "node test/code-browser.mjs",
|
|
275
287
|
"test:selection-browser": "node test/selection-browser.mjs",
|
|
276
288
|
"test:click-entry-browser": "node test/click-entry-browser.mjs",
|
|
@@ -294,9 +306,12 @@
|
|
|
294
306
|
"test:review-panel-browser": "node test/review-panel-browser.mjs",
|
|
295
307
|
"test:review-hook-browser": "node test/review-hook-browser.mjs",
|
|
296
308
|
"test:source-pane-browser": "node test/source-pane-browser.mjs",
|
|
309
|
+
"test:layout-editor-browser": "node test/layout-editor-browser.mjs",
|
|
297
310
|
"test:export-license-browser": "node test/export-license-browser.mjs",
|
|
298
311
|
"test:chart-options-browser": "node test/chart-options-browser.mjs",
|
|
299
312
|
"test:numbering-panel-browser": "node test/numbering-panel-browser.mjs",
|
|
313
|
+
"test:presenter-browser": "node test/presenter-browser.mjs",
|
|
314
|
+
"test:present-button-browser": "node test/present-button-browser.mjs",
|
|
300
315
|
"test:export-menu-browser": "node test/export-menu-browser.mjs",
|
|
301
316
|
"test:agent-handoff-browser": "node test/agent-handoff-browser.mjs"
|
|
302
317
|
},
|
|
@@ -311,11 +326,11 @@
|
|
|
311
326
|
"@codemirror/state": "6.7.6",
|
|
312
327
|
"@codemirror/view": "6.43.14",
|
|
313
328
|
"@lezer/highlight": "1.2.5",
|
|
314
|
-
"@openpresentation/opf": "^0.
|
|
329
|
+
"@openpresentation/opf": "^0.25.0",
|
|
315
330
|
"jsonc-parser": "3.3.1"
|
|
316
331
|
},
|
|
317
332
|
"peerDependencies": {
|
|
318
|
-
"@openpresentation/opf-render": "^0.
|
|
333
|
+
"@openpresentation/opf-render": "^0.25.0"
|
|
319
334
|
},
|
|
320
335
|
"peerDependenciesMeta": {
|
|
321
336
|
"@openpresentation/opf-render": {
|
|
@@ -323,7 +338,7 @@
|
|
|
323
338
|
}
|
|
324
339
|
},
|
|
325
340
|
"devDependencies": {
|
|
326
|
-
"@openpresentation/gallery": "^
|
|
341
|
+
"@openpresentation/gallery": "^5.0.0",
|
|
327
342
|
"@expo-google-fonts/arimo": "0.4.3",
|
|
328
343
|
"@expo-google-fonts/caladea": "0.4.2",
|
|
329
344
|
"@expo-google-fonts/cousine": "0.4.3",
|
|
@@ -368,8 +383,8 @@
|
|
|
368
383
|
"@expo-google-fonts/roboto-mono": "0.4.2",
|
|
369
384
|
"@expo-google-fonts/stix-two-math": "0.4.0",
|
|
370
385
|
"@expo-google-fonts/tinos": "0.4.2",
|
|
371
|
-
"@openpresentation/opf-pptx": "^0.
|
|
372
|
-
"@openpresentation/opf-render": "^0.
|
|
386
|
+
"@openpresentation/opf-pptx": "^0.25.0",
|
|
387
|
+
"@openpresentation/opf-render": "^0.25.0",
|
|
373
388
|
"@resvg/resvg-js": "2.6.2",
|
|
374
389
|
"axe-core": "4.14.0",
|
|
375
390
|
"esbuild": "0.28.2",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { EditorSession } from "./index.js";
|
|
2
|
-
|
|
3
|
-
export declare function openCustomLayoutEditor(editor: EditorSession, options?: {
|
|
4
|
-
document?: Document;
|
|
5
|
-
onSave?: (layout: { id: string; record: Record<string, unknown> }) => void;
|
|
6
|
-
onClose?: () => void;
|
|
7
|
-
}): { dialog: HTMLDialogElement; close(): void };
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
// A small, local grid authoring dialog for OPF 0.19 layout templates.
|
|
2
|
-
import { LAYOUT_REGION_FLOWS, LAYOUT_REGION_KINDS, LAYOUT_REGION_ROLES, layoutFromGrid, saveCustomLayout } from "./layout-templates.js";
|
|
3
|
-
|
|
4
|
-
const element = (doc, tag, text) => { const node = doc.createElement(tag); if (text !== undefined) node.textContent = text; return node; };
|
|
5
|
-
const field = (doc, title, input) => { const label = element(doc, "label", title); label.className = "opf-grid-field"; label.append(input); return label; };
|
|
6
|
-
const select = (doc, values, current) => {
|
|
7
|
-
const input = element(doc, "select");
|
|
8
|
-
for (const value of values) { const option = element(doc, "option", value); option.value = value; input.append(option); }
|
|
9
|
-
input.value = current;
|
|
10
|
-
return input;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/** Draw rectangular areas, set each region's accepted kinds, role and flow, then save one undoable custom record. */
|
|
14
|
-
export function openCustomLayoutEditor(editor, options = {}) {
|
|
15
|
-
const doc = options.document ?? globalThis.document;
|
|
16
|
-
const dialog = element(doc, "dialog");
|
|
17
|
-
dialog.className = "opf-sm-dialog opf-grid-dialog";
|
|
18
|
-
const heading = element(doc, "h2", "Create a custom layout");
|
|
19
|
-
heading.id = "opf-grid-title";
|
|
20
|
-
dialog.setAttribute("aria-labelledby", heading.id);
|
|
21
|
-
const error = element(doc, "p"); error.setAttribute("role", "alert"); error.className = "opf-sm-error";
|
|
22
|
-
const id = element(doc, "input"); id.placeholder = "roadmap-split"; id.pattern = "[a-z][a-z0-9-]*"; id.required = true;
|
|
23
|
-
const name = element(doc, "input"); name.placeholder = "Roadmap split"; name.required = true;
|
|
24
|
-
const summary = element(doc, "input"); summary.placeholder = "When to use this layout";
|
|
25
|
-
const width = element(doc, "input"); width.type = "number"; width.min = "1"; width.max = "12"; width.value = "3";
|
|
26
|
-
const height = element(doc, "input"); height.type = "number"; height.min = "1"; height.max = "12"; height.value = "3";
|
|
27
|
-
const paint = element(doc, "input"); paint.value = "content"; paint.placeholder = "content or . to erase";
|
|
28
|
-
const grid = element(doc, "div"); grid.className = "opf-custom-layout-grid"; grid.setAttribute("aria-label", "Layout cells");
|
|
29
|
-
const regionsPanel = element(doc, "div"); regionsPanel.className = "opf-grid-regions";
|
|
30
|
-
const hint = element(doc, "p", "Choose an area name, then drag across cells to draw a rectangle. Use title for the heading and . to clear cells."); hint.className = "field-help";
|
|
31
|
-
const body = element(doc, "div"); body.className = "opf-grid-body";
|
|
32
|
-
body.append(field(doc, "Layout ID", id), field(doc, "Name", name), field(doc, "Summary", summary));
|
|
33
|
-
const dimensions = element(doc, "div"); dimensions.className = "opf-grid-dimensions";
|
|
34
|
-
dimensions.append(field(doc, "Columns", width), field(doc, "Rows", height), field(doc, "Paint area", paint));
|
|
35
|
-
body.append(dimensions, hint, grid, regionsPanel, error);
|
|
36
|
-
const cancel = element(doc, "button", "Cancel"); cancel.type = "button"; cancel.className = "secondary";
|
|
37
|
-
const save = element(doc, "button", "Save layout"); save.type = "submit"; save.className = "primary";
|
|
38
|
-
const actions = element(doc, "div"); actions.className = "opf-sm-actions"; actions.append(cancel, save);
|
|
39
|
-
const form = element(doc, "form"); form.append(heading, body, actions); dialog.append(form);
|
|
40
|
-
let cells = [["title", "title", "title"], ["content", "content", "content"], ["content", "content", "content"]];
|
|
41
|
-
const settings = new Map();
|
|
42
|
-
let down = null;
|
|
43
|
-
const opener = doc.activeElement;
|
|
44
|
-
const regionNames = () => [...new Set(cells.flat())].filter((value) => value !== "." && value !== "title" && value !== "subtitle");
|
|
45
|
-
function resize() {
|
|
46
|
-
const cols = Number(width.value), rows = Number(height.value);
|
|
47
|
-
if (!Number.isInteger(cols) || !Number.isInteger(rows) || cols < 1 || cols > 12 || rows < 1 || rows > 12) return;
|
|
48
|
-
cells = Array.from({ length: rows }, (_, y) => Array.from({ length: cols }, (_, x) => cells[y]?.[x] ?? "."));
|
|
49
|
-
render();
|
|
50
|
-
}
|
|
51
|
-
function draw(from, to) {
|
|
52
|
-
const value = paint.value.trim() || ".";
|
|
53
|
-
if (value !== "." && !/^[a-z][a-z0-9-]*$/.test(value)) { error.textContent = "Use lowercase area names with letters, digits and hyphens."; return; }
|
|
54
|
-
for (let y = Math.min(from.y, to.y); y <= Math.max(from.y, to.y); y++)
|
|
55
|
-
for (let x = Math.min(from.x, to.x); x <= Math.max(from.x, to.x); x++) cells[y][x] = value;
|
|
56
|
-
error.textContent = ""; render();
|
|
57
|
-
grid.querySelector(`[data-x="${to.x}"][data-y="${to.y}"]`)?.focus();
|
|
58
|
-
}
|
|
59
|
-
function render() {
|
|
60
|
-
grid.replaceChildren(); grid.style.gridTemplateColumns = `repeat(${cells[0].length}, minmax(38px, 1fr))`;
|
|
61
|
-
cells.forEach((row, y) => row.forEach((area, x) => {
|
|
62
|
-
const cell = element(doc, "button", area); cell.type = "button"; cell.className = "opf-grid-cell"; cell.dataset.area = area;
|
|
63
|
-
cell.setAttribute("aria-label", `Row ${y + 1}, column ${x + 1}: ${area === "." ? "empty" : area}`);
|
|
64
|
-
cell.onpointerdown = (event) => { if (event.button !== 0) return; down = { x, y }; cell.setPointerCapture?.(event.pointerId); };
|
|
65
|
-
cell.onpointerup = (event) => { if (!down) return; const target = doc.elementFromPoint?.(event.clientX, event.clientY)?.closest?.(".opf-grid-cell"); const last = target ? { x: Number(target.dataset.x), y: Number(target.dataset.y) } : { x, y }; draw(down, last); down = null; };
|
|
66
|
-
cell.dataset.x = String(x); cell.dataset.y = String(y);
|
|
67
|
-
cell.onkeydown = (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); draw({ x, y }, { x, y }); } };
|
|
68
|
-
grid.append(cell);
|
|
69
|
-
}));
|
|
70
|
-
regionsPanel.replaceChildren();
|
|
71
|
-
for (const area of regionNames()) {
|
|
72
|
-
const setting = settings.get(area) ?? { accepts: new Set(["text"]), role: "primary", flow: "auto" };
|
|
73
|
-
settings.set(area, setting);
|
|
74
|
-
const section = element(doc, "fieldset"); section.className = "opf-grid-region";
|
|
75
|
-
section.append(element(doc, "legend", area));
|
|
76
|
-
const kinds = element(doc, "div"); kinds.className = "opf-grid-kinds";
|
|
77
|
-
for (const kind of LAYOUT_REGION_KINDS) {
|
|
78
|
-
const check = element(doc, "input"); check.type = "checkbox"; check.value = kind; check.checked = setting.accepts.has(kind);
|
|
79
|
-
check.onchange = () => { if (check.checked) setting.accepts.add(kind); else setting.accepts.delete(kind); };
|
|
80
|
-
const label = element(doc, "label", kind); label.prepend(check); kinds.append(label);
|
|
81
|
-
}
|
|
82
|
-
const role = select(doc, LAYOUT_REGION_ROLES, setting.role); role.onchange = () => { setting.role = role.value; };
|
|
83
|
-
const flow = select(doc, LAYOUT_REGION_FLOWS, setting.flow); flow.onchange = () => { setting.flow = flow.value; };
|
|
84
|
-
section.append(element(doc, "span", "Accepts"), kinds, field(doc, "Role", role), field(doc, "Flow", flow)); regionsPanel.append(section);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
width.onchange = resize; height.onchange = resize;
|
|
88
|
-
cancel.onclick = () => dialog.close();
|
|
89
|
-
form.onsubmit = (event) => {
|
|
90
|
-
event.preventDefault();
|
|
91
|
-
try {
|
|
92
|
-
const regions = Object.fromEntries(regionNames().map((area) => {
|
|
93
|
-
const setting = settings.get(area);
|
|
94
|
-
return [area, { accepts: [...setting.accepts], role: setting.role, flow: setting.flow }];
|
|
95
|
-
}));
|
|
96
|
-
const layout = layoutFromGrid({ id: id.value.trim(), name: name.value, summary: summary.value, cells, regions });
|
|
97
|
-
saveCustomLayout(editor, layout);
|
|
98
|
-
options.onSave?.(layout);
|
|
99
|
-
dialog.close();
|
|
100
|
-
} catch (cause) { error.textContent = cause?.issues?.[0]?.message ?? cause.message; }
|
|
101
|
-
};
|
|
102
|
-
dialog.onclose = () => { dialog.remove(); opener?.focus?.(); options.onClose?.(); };
|
|
103
|
-
doc.body.append(dialog); render(); dialog.showModal(); id.focus();
|
|
104
|
-
return { dialog, close: () => dialog.close() };
|
|
105
|
-
}
|