@markdstage/markdstage 3.3.0 → 3.4.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 +23 -12
- package/package.json +1 -1
- package/shared/README.md +7 -6
- package/shared/renderer/renderer.js +15 -2
- package/shared/runtime/deck-session.mjs +37 -6
- package/shared/runtime/presentation-server.mjs +368 -95
- package/src/cli.mjs +92 -19
- package/src/commands/present.mjs +133 -143
- package/src/deck.mjs +4 -0
- package/src/skills.mjs +12 -8
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@ exported PDF, or hybrid editable PowerPoint deck.
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
```console
|
|
19
|
-
npx @markdstage/markdstage
|
|
20
|
-
npx @markdstage/markdstage
|
|
19
|
+
npx @markdstage/markdstage
|
|
20
|
+
npx @markdstage/markdstage slides.md
|
|
21
21
|
npm install --global @markdstage/markdstage
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -31,6 +31,8 @@ npm install --global .\markdstage-markdstage-<version>.tgz
|
|
|
31
31
|
## Commands
|
|
32
32
|
|
|
33
33
|
```console
|
|
34
|
+
markdstage
|
|
35
|
+
markdstage slides.md
|
|
34
36
|
markdstage present slides.md
|
|
35
37
|
markdstage preview slides.md --watch
|
|
36
38
|
markdstage validate slides.md --json
|
|
@@ -43,10 +45,14 @@ markdstage skill install --target codex
|
|
|
43
45
|
markdstage skill install --target claude
|
|
44
46
|
```
|
|
45
47
|
|
|
48
|
+
`markdstage` opens an empty Canvas-equivalent UI for the current workspace.
|
|
49
|
+
Choose **Open Markdown** to load a deck. `markdstage slides.md` opens the same UI
|
|
50
|
+
in slide view with automatic refresh enabled.
|
|
51
|
+
|
|
46
52
|
| Command | Description |
|
|
47
53
|
| --- | --- |
|
|
48
|
-
| `present` | Opens the presenter view
|
|
49
|
-
| `preview` |
|
|
54
|
+
| `present` | Opens the full MarkdStage UI in presenter view. Open Markdown, automatic refresh, editing, export, and audience controls remain available. `--watch` starts in live mode, and `--no-open` serves the UI without launching a browser. |
|
|
55
|
+
| `preview` | Opens the same full UI in slide view. It is a compatibility/convenience entry point; `--watch` starts in live mode, and `--no-open` serves the UI without launching a browser. |
|
|
50
56
|
| `validate` | Checks deck structure, Architecture DSL blocks, themes, and theme paths. |
|
|
51
57
|
| `inspect` | Reports the same compact 1280x720 clipping diagnostics as the canvas `inspect_layout` action. `--slide <n>` limits it to one page, `--all` includes slides that fit, `--fail-on-issues` exits with code 5. |
|
|
52
58
|
| `capture` | Writes 1280x720 PNG files. Without `--pages` only the slides reported as clipped are captured. |
|
|
@@ -55,19 +61,24 @@ markdstage skill install --target claude
|
|
|
55
61
|
| `skill` | Installs or checks the portable Agent Skills for Codex (`.agents/skills/markdstage/`), Claude Code (`.claude/skills/markdstage/`), and GitHub Copilot (`.github/skills/markdstage/`). Locally modified files are never overwritten without `--force`. |
|
|
56
62
|
| `help` | Shows the overview, or the help for one command. `markdstage help <command>` prints the same text as `markdstage <command> --help`. |
|
|
57
63
|
|
|
58
|
-
|
|
59
|
-
`--
|
|
64
|
+
Application options: `--workspace <dir>`, `--theme <name>`,
|
|
65
|
+
`--theme-file <path>`, `--no-open`, and `--json`. Use `--help` and `--version`
|
|
66
|
+
for global information.
|
|
60
67
|
|
|
61
68
|
## Architecture editing
|
|
62
69
|
|
|
63
|
-
Run `markdstage
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
Run `markdstage slides.md` for the live authoring workflow. The browser starts
|
|
71
|
+
in the fixed 16:9 output preview and remains in viewing mode.
|
|
72
|
+
Select **Output preview** to switch to the retained responsive layout. Select
|
|
73
|
+
the pencil control to move Architecture elements; editing automatically switches
|
|
74
|
+
to the responsive layout, and those placement changes are saved atomically to
|
|
75
|
+
the matching `architecture` fence. Select **Advanced edit** to add, update,
|
|
76
|
+
duplicate, reparent, or delete elements in the detailed designer, then select
|
|
77
|
+
**Save**.
|
|
68
78
|
|
|
69
79
|
The server rejects a save if the Markdown changed outside the editor. Successful
|
|
70
|
-
saves reload the watched deck without changing the current slide.
|
|
80
|
+
saves reload the watched deck without changing the current slide. Automatic
|
|
81
|
+
refresh can be toggled without disabling Architecture editing. Presenter,
|
|
71
82
|
capture, inspect, and export views contain no editing UI.
|
|
72
83
|
|
|
73
84
|
```console
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markdstage/markdstage",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Present, validate, inspect, capture, and export MarkdStage Markdown decks from the command line — no Copilot canvas required.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "runceel",
|
package/shared/README.md
CHANGED
|
@@ -118,12 +118,13 @@ The themed slide is displayed and updates automatically
|
|
|
118
118
|
dismissed or another export starts. Success and error notifications also have
|
|
119
119
|
a dismiss button and are announced to screen readers. Notifications never
|
|
120
120
|
appear in exported files or the external presentation window.
|
|
121
|
-
-
|
|
122
|
-
the
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
state. A visible and accessible warning identifies
|
|
126
|
-
clipped.
|
|
121
|
+
- The canvas starts in **More controls > Output preview**, letterboxing the
|
|
122
|
+
current slide with the same fixed 1280×720 typography, spacing, diagram
|
|
123
|
+
limits, and clipping used by PDF output. Select **Output preview** to return
|
|
124
|
+
to the retained responsive canvas layout. This preview is local to the canvas
|
|
125
|
+
and does not change deck state. A visible and accessible warning identifies
|
|
126
|
+
content that would be clipped. Entering lightweight Architecture editing also
|
|
127
|
+
returns to the responsive layout so its placement controls remain available.
|
|
127
128
|
- AI should call **`inspect_layout` before exporting a non-scrolling deck**. It
|
|
128
129
|
renders the currently registered in-memory output snapshot in headless
|
|
129
130
|
Chromium; it does not read or validate the source file named by `sourceName`.
|
|
@@ -426,13 +426,14 @@ function collectDeckLayout(rendered) {
|
|
|
426
426
|
function updateFixedPreviewWarning() {
|
|
427
427
|
const warning = document.getElementById("layoutWarning");
|
|
428
428
|
const button = document.getElementById("navFixedPreview");
|
|
429
|
-
|
|
429
|
+
const empty = document.body.classList.contains("markdstage-empty");
|
|
430
|
+
if (!fixedPreviewMode || !layoutTarget || empty) {
|
|
430
431
|
document.body.classList.remove("fixed-preview-overflow");
|
|
431
432
|
if (warning) {
|
|
432
433
|
warning.hidden = true;
|
|
433
434
|
warning.textContent = "";
|
|
434
435
|
}
|
|
435
|
-
if (button) button.dataset.state = fixedPreviewMode ? "active" : "";
|
|
436
|
+
if (button) button.dataset.state = fixedPreviewMode && !empty ? "active" : "";
|
|
436
437
|
syncMoreControls();
|
|
437
438
|
return;
|
|
438
439
|
}
|
|
@@ -3020,6 +3021,7 @@ async function fetchDeck() {
|
|
|
3020
3021
|
function setArchitectureEditMode(enabled) {
|
|
3021
3022
|
const next = Boolean(enabled) && architectureEditAvailable && !presenterMode;
|
|
3022
3023
|
if (next === architectureEditMode) return false;
|
|
3024
|
+
if (next && fixedPreviewMode) setFixedPreviewMode(false);
|
|
3023
3025
|
architectureEditMode = next;
|
|
3024
3026
|
document.body.classList.toggle("architecture-edit-mode", next);
|
|
3025
3027
|
updateArchitectureEditButton(next);
|
|
@@ -4352,6 +4354,17 @@ function init() {
|
|
|
4352
4354
|
requestArchitectureEditMode(true);
|
|
4353
4355
|
}
|
|
4354
4356
|
|
|
4357
|
+
// Canvas and CLI preview start on the fixed 16:9 output surface. Presenter
|
|
4358
|
+
// views keep their purpose-built layouts, and the control still lets users
|
|
4359
|
+
// switch back to the responsive canvas layout.
|
|
4360
|
+
if (
|
|
4361
|
+
!presenterMode &&
|
|
4362
|
+
!presenterViewRequested &&
|
|
4363
|
+
params.get("responsive") !== "1"
|
|
4364
|
+
) {
|
|
4365
|
+
setFixedPreviewMode(true);
|
|
4366
|
+
}
|
|
4367
|
+
|
|
4355
4368
|
updateArchitectureEditButton();
|
|
4356
4369
|
if (!previewMode) wireControls();
|
|
4357
4370
|
else if (navigationEnabled) {
|
|
@@ -112,14 +112,25 @@ export async function createDeckSession({
|
|
|
112
112
|
// An explicit --workspace wins; otherwise confine the deck to its Git
|
|
113
113
|
// repository root (or the folder holding the Markdown file).
|
|
114
114
|
const deckDirectory = file ? resolve(file, "..") : process.cwd();
|
|
115
|
-
const
|
|
115
|
+
const requestedRoot = workspaceRoot
|
|
116
116
|
? resolve(workspaceRoot)
|
|
117
117
|
: resolveWorkspaceRoot(deckDirectory, deckDirectory);
|
|
118
|
-
|
|
118
|
+
let root;
|
|
119
|
+
try {
|
|
120
|
+
root = await realpath(requestedRoot);
|
|
121
|
+
} catch (_) {
|
|
122
|
+
throw new MarkdStageError(
|
|
123
|
+
"workspace_not_found",
|
|
124
|
+
`Could not read workspace directory: ${requestedRoot}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const resolved = file ? await resolveDeckFile(file, root) : null;
|
|
119
128
|
const session = {
|
|
120
|
-
file: resolved
|
|
121
|
-
workspaceRoot: resolved
|
|
122
|
-
sourceName:
|
|
129
|
+
file: resolved?.path ?? "",
|
|
130
|
+
workspaceRoot: resolved?.workspaceRoot ?? root,
|
|
131
|
+
sourceName: resolved
|
|
132
|
+
? workspaceRelative(resolved.workspaceRoot, resolved.path)
|
|
133
|
+
: "",
|
|
123
134
|
url: "",
|
|
124
135
|
version: 0,
|
|
125
136
|
deckVersion: 0,
|
|
@@ -145,6 +156,9 @@ export async function createDeckSession({
|
|
|
145
156
|
};
|
|
146
157
|
|
|
147
158
|
session.load = async ({ preserveIndex = false } = {}) => {
|
|
159
|
+
if (!session.file) {
|
|
160
|
+
throw new MarkdStageError("no_deck", "Open a Markdown file first.");
|
|
161
|
+
}
|
|
148
162
|
const { markdown, slides } = await readDeckSlides(session.file);
|
|
149
163
|
const selection = resolveDeckTheme({
|
|
150
164
|
slides,
|
|
@@ -176,6 +190,23 @@ export async function createDeckSession({
|
|
|
176
190
|
return session.slides.length;
|
|
177
191
|
};
|
|
178
192
|
|
|
193
|
+
session.openFile = async (nextFile, { preserveIndex = false } = {}) => {
|
|
194
|
+
const next = await resolveDeckFile(nextFile, session.workspaceRoot);
|
|
195
|
+
const previous = {
|
|
196
|
+
file: session.file,
|
|
197
|
+
sourceName: session.sourceName,
|
|
198
|
+
};
|
|
199
|
+
session.file = next.path;
|
|
200
|
+
session.sourceName = workspaceRelative(next.workspaceRoot, next.path);
|
|
201
|
+
try {
|
|
202
|
+
return await session.load({ preserveIndex });
|
|
203
|
+
} catch (error) {
|
|
204
|
+
session.file = previous.file;
|
|
205
|
+
session.sourceName = previous.sourceName;
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
179
210
|
session.navigate = (target) => {
|
|
180
211
|
const next = clampIndex(target, session.slides.length);
|
|
181
212
|
if (next === session.index) return false;
|
|
@@ -185,6 +216,6 @@ export async function createDeckSession({
|
|
|
185
216
|
return true;
|
|
186
217
|
};
|
|
187
218
|
|
|
188
|
-
await session.load();
|
|
219
|
+
if (session.file) await session.load();
|
|
189
220
|
return session;
|
|
190
221
|
}
|