portable-agent-layer 0.31.0 → 0.33.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/assets/skills/consulting-report/SKILL.md +8 -2
- package/assets/skills/consulting-report/demo/app/globals.css +115 -0
- package/assets/skills/consulting-report/demo/app/page.tsx +75 -28
- package/assets/skills/consulting-report/demo/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/demo/components/section.tsx +3 -2
- package/assets/skills/consulting-report/demo/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/demo/components/table-of-contents.tsx +27 -0
- package/assets/skills/consulting-report/template/app/globals.css +115 -0
- package/assets/skills/consulting-report/template/app/page.tsx +55 -28
- package/assets/skills/consulting-report/template/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/template/components/section.tsx +3 -2
- package/assets/skills/consulting-report/template/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/template/components/table-of-contents.tsx +27 -0
- package/assets/skills/presentation/SKILL.md +124 -5
- package/assets/skills/presentation/WORKSHOP.md +128 -0
- package/assets/skills/presentation/theme-base/base.css +113 -0
- package/assets/skills/presentation/theme-base/layouts.css +11 -2
- package/assets/skills/presentation/tools/build.ts +136 -6
- package/assets/skills/presentation/tools/doctor.ts +106 -317
- package/assets/skills/presentation/tools/lib/lint-helpers.ts +150 -0
- package/assets/skills/presentation/tools/lib/lint-rules.ts +744 -0
- package/assets/skills/presentation/tools/lib/lint-types.ts +40 -0
- package/assets/skills/presentation/tools/new-deck.ts +9 -4
- package/assets/skills/presentation/vendor/reveal/plugin/highlight/github-dark.css +118 -0
- package/assets/skills/projects/SKILL.md +111 -0
- package/assets/skills/telos/SKILL.md +4 -1
- package/assets/templates/AGENTS.md.template +28 -7
- package/assets/templates/PAL/ALGORITHM.md +2 -0
- package/assets/templates/PAL/README.md +0 -1
- package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +1 -1
- package/assets/templates/pal-settings.json +2 -2
- package/package.json +1 -1
- package/src/hooks/UserPromptOrchestrator.ts +3 -1
- package/src/hooks/handlers/auto-graduate.ts +169 -0
- package/src/hooks/handlers/inject-retrieval.ts +50 -0
- package/src/hooks/handlers/project-touch.ts +39 -0
- package/src/hooks/lib/context.ts +9 -8
- package/src/hooks/lib/paths.ts +2 -0
- package/src/hooks/lib/projects.ts +270 -0
- package/src/hooks/lib/retrieval-index.ts +223 -0
- package/src/hooks/lib/retrieval.ts +170 -0
- package/src/hooks/lib/security.ts +2 -0
- package/src/hooks/lib/stop.ts +9 -1
- package/src/hooks/lib/text-similarity.ts +13 -9
- package/src/hooks/lib/wisdom.ts +155 -1
- package/src/tools/agent/project.ts +336 -0
- package/src/tools/self-model.ts +3 -3
- package/assets/templates/PAL/CONTEXT_ROUTING.md +0 -30
|
@@ -244,6 +244,14 @@
|
|
|
244
244
|
.reveal tr:nth-child(even) td { background: var(--brand-surface); }
|
|
245
245
|
.reveal tr:last-child td { border-bottom: 0; }
|
|
246
246
|
|
|
247
|
+
/* ── Text alignment utilities — composable on any block element.
|
|
248
|
+
* `!important` is needed because Reveal's print stylesheet forces
|
|
249
|
+
* `text-align: left !important` on every div/p/ol/ul, which would otherwise
|
|
250
|
+
* override author intent (inline styles or any non-`!important` rule). */
|
|
251
|
+
.reveal .text-center { text-align: center !important; }
|
|
252
|
+
.reveal .text-right { text-align: right !important; }
|
|
253
|
+
.reveal .text-left { text-align: left !important; }
|
|
254
|
+
|
|
247
255
|
/* ── Image utilities (composable on any <img> or wrapper) */
|
|
248
256
|
.reveal .image-rounded img,
|
|
249
257
|
.reveal img.image-rounded { border-radius: var(--radius-md); }
|
|
@@ -323,3 +331,108 @@
|
|
|
323
331
|
color: var(--brand-accent);
|
|
324
332
|
height: 2px;
|
|
325
333
|
}
|
|
334
|
+
|
|
335
|
+
/* ── Print
|
|
336
|
+
* Reveal's vendored print stylesheet (`@media print { html:not(.print-pdf) … }`)
|
|
337
|
+
* forces every heading to `color:#000!important` and `p/li/td` to `#000`. That
|
|
338
|
+
* inverts the white-on-brand text we use on `section` and `closing` dividers
|
|
339
|
+
* (whose gradient background is on the section element itself, so it still
|
|
340
|
+
* renders blue) — producing black-on-blue when the user hits Cmd+P without
|
|
341
|
+
* `?print-pdf`. Restore brand text colors here, and force color-adjust:exact
|
|
342
|
+
* so the gradient prints reliably. */
|
|
343
|
+
@media print {
|
|
344
|
+
.reveal section[data-layout="section"],
|
|
345
|
+
.reveal section[data-layout="closing"] {
|
|
346
|
+
-webkit-print-color-adjust: exact !important;
|
|
347
|
+
print-color-adjust: exact !important;
|
|
348
|
+
}
|
|
349
|
+
.reveal section[data-layout="section"] h1,
|
|
350
|
+
.reveal section[data-layout="section"] h4,
|
|
351
|
+
.reveal section[data-layout="closing"] h1 {
|
|
352
|
+
color: #fff !important;
|
|
353
|
+
}
|
|
354
|
+
.reveal section[data-layout="section"] h2,
|
|
355
|
+
.reveal section[data-layout="section"] h3 {
|
|
356
|
+
color: rgba(255, 255, 255, 0.7) !important;
|
|
357
|
+
}
|
|
358
|
+
.reveal section[data-layout="section"] p,
|
|
359
|
+
.reveal section[data-layout="section"] li,
|
|
360
|
+
.reveal section[data-layout="closing"] h2,
|
|
361
|
+
.reveal section[data-layout="closing"] p,
|
|
362
|
+
.reveal section[data-layout="closing"] li {
|
|
363
|
+
color: rgba(255, 255, 255, 0.85) !important;
|
|
364
|
+
}
|
|
365
|
+
.reveal section[data-layout="title"] h1 {
|
|
366
|
+
color: var(--brand-primary) !important;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* ── Cmd+P scope (browser print on the regular URL, NOT ?print-pdf)
|
|
370
|
+
* Reveal forces every section to `display:block; position:static; padding:60px 20px;
|
|
371
|
+
* transform:none`, which strips the flex centering from title/section/closing/quote/
|
|
372
|
+
* big-stat/pull-quote and leaves their content top-aligned. Restore centering, then
|
|
373
|
+
* frame every printed page with a hairline so it reads like a printed sheet. */
|
|
374
|
+
|
|
375
|
+
/* Hairline frame — inset so it lives inside the printable area and survives
|
|
376
|
+
* the printer's unprintable-margin clipping. */
|
|
377
|
+
html:not(.print-pdf) .reveal .slides section {
|
|
378
|
+
box-shadow: inset 0 0 0 1px var(--neutral-300) !important;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* Make every section fill the printable page. Two effects, no layout
|
|
382
|
+
* coercion:
|
|
383
|
+
* 1. The inset frame above wraps the full page edge instead of
|
|
384
|
+
* shrink-wrapping content.
|
|
385
|
+
* 2. The flex-centered layouts below have a tall container to center
|
|
386
|
+
* within (without a tall container, `justify-content: center` has
|
|
387
|
+
* nothing to center against). */
|
|
388
|
+
html:not(.print-pdf) .reveal .slides section {
|
|
389
|
+
min-height: 100vh !important;
|
|
390
|
+
box-sizing: border-box !important;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/* Apply `--table-scale` to td/th in print. Reveal's print stylesheet forces
|
|
394
|
+
* `font-size: 20pt !important` on all td, which would otherwise override
|
|
395
|
+
* the screen rule. Scale font AND padding together — scaling font alone
|
|
396
|
+
* leaves the row height dominated by static cell padding, dampening the
|
|
397
|
+
* shrink effect. */
|
|
398
|
+
html:not(.print-pdf) .reveal section[data-layout="table"] td,
|
|
399
|
+
html:not(.print-pdf) .reveal section[data-layout="table"] th {
|
|
400
|
+
font-size: calc(20pt * var(--table-scale, 1)) !important;
|
|
401
|
+
padding: calc(0.5rem * var(--table-scale, 1)) calc(1rem * var(--table-scale, 1)) !important;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* Reveal's print stylesheet forces every section to `display: block`,
|
|
405
|
+
* stripping the on-screen flex centering from the layouts that use it.
|
|
406
|
+
* Restore `display: flex` for those layouts ONLY — `flex-direction`,
|
|
407
|
+
* `justify-content`, and `align-items` come from layouts.css unchanged.
|
|
408
|
+
* Layouts not listed here stay as `display: block` and sit at the top of
|
|
409
|
+
* the page; that preserves the natural flow of layouts that depend on it
|
|
410
|
+
* (image-text and two-column rely on inline-block siblings, content/agenda/
|
|
411
|
+
* comparison/metric-grid/code/table all flow as block stacks). */
|
|
412
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="title"],
|
|
413
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="section"],
|
|
414
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="closing"],
|
|
415
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="quote"],
|
|
416
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="big-stat"],
|
|
417
|
+
html:not(.print-pdf) .reveal .slides section[data-layout="pull-quote"] {
|
|
418
|
+
display: flex !important;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/* ── Print-view polish (`?print-pdf` URL — Reveal stacks each slide as a `.pdf-page`)
|
|
423
|
+
* Reveal sizes `.pdf-page` to slide dims and stacks them flush-left with no
|
|
424
|
+
* separation. Center each one and draw a hairline frame so the print preview
|
|
425
|
+
* reads like a sheaf of printed pages, not a left-aligned wall of slides.
|
|
426
|
+
*
|
|
427
|
+
* Why box-shadow inset and not `border`: Reveal forces `@page { margin: 0 }`,
|
|
428
|
+
* so an outside border would land at the edge of paper and be clipped by the
|
|
429
|
+
* printer's unprintable margin. An inset shadow draws the line *inside* the
|
|
430
|
+
* page box and survives the print. */
|
|
431
|
+
html.print-pdf .reveal .slides .pdf-page {
|
|
432
|
+
box-shadow: inset 0 0 0 1px var(--neutral-300);
|
|
433
|
+
}
|
|
434
|
+
@media screen {
|
|
435
|
+
html.print-pdf .reveal .slides .pdf-page {
|
|
436
|
+
margin: 16px auto !important;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
@@ -213,9 +213,13 @@
|
|
|
213
213
|
letter-spacing: var(--tracking-wide);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
/* ── 9. table ── styling already in base; just ensure breathing room
|
|
216
|
+
/* ── 9. table ── styling already in base; just ensure breathing room.
|
|
217
|
+
* `--table-scale` is set per-slide by build.ts (`injectTableScale`) when row
|
|
218
|
+
* count exceeds 6 — multiplies the cell font-size to keep dense tables on a
|
|
219
|
+
* single page. Defaults to 1 (no change) when the var is unset. */
|
|
217
220
|
.reveal section[data-layout="table"] table {
|
|
218
221
|
margin-top: var(--space-3);
|
|
222
|
+
font-size: calc(var(--text-sm) * var(--table-scale, 1));
|
|
219
223
|
}
|
|
220
224
|
|
|
221
225
|
/* ── 10. comparison ── 2-3 option boxes with numbered badge + top stripe */
|
|
@@ -265,7 +269,12 @@
|
|
|
265
269
|
max-height: 65vh;
|
|
266
270
|
margin: var(--space-2) 0;
|
|
267
271
|
}
|
|
268
|
-
|
|
272
|
+
/* Code font size scales down for long blocks. The build script sets
|
|
273
|
+
--code-scale on each code-layout section based on line count: 1.0 for ≤15
|
|
274
|
+
lines, 0.6 for ≥25 lines, linear in between. Defaults to 1.0 if unset. */
|
|
275
|
+
.reveal section[data-layout="code"] pre code {
|
|
276
|
+
font-size: calc(0.9em * var(--code-scale, 1));
|
|
277
|
+
}
|
|
269
278
|
|
|
270
279
|
/* ── 12. big-stat ── One giant number + a muted caption beneath. Use sparingly.
|
|
271
280
|
* Author surface:
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// <out>/<deck-name>/<deck-name>.md concatenated slides (written first)
|
|
9
9
|
// <out>/<deck-name>/<deck-name>.html self-contained presentation
|
|
10
10
|
//
|
|
11
|
-
// --out defaults to
|
|
11
|
+
// --out defaults to the deck-dir itself (output lands at <deck-dir>/<deck-name>/,
|
|
12
|
+
// which the scaffolder gitignores). The deck-name subdir is always created
|
|
12
13
|
// inside --out, even when --out is explicitly provided. Existing files in
|
|
13
14
|
// the subdir are preserved unless --force is passed.
|
|
14
15
|
|
|
@@ -19,6 +20,71 @@ import { dataUri, escapeForTextarea, readText } from "./lib/inline";
|
|
|
19
20
|
import { THEME_BASE, VENDOR_REVEAL } from "./lib/paths";
|
|
20
21
|
import { getTemplate } from "./lib/registry";
|
|
21
22
|
|
|
23
|
+
// Walk markdown line-by-line, skipping fenced code blocks, applying `transform`
|
|
24
|
+
// to each non-fenced line. Used by both the concat-step path rewrite and the
|
|
25
|
+
// HTML-step image inliner so neither touches example image syntax inside ``` blocks.
|
|
26
|
+
async function mapMarkdownOutsideFences(
|
|
27
|
+
md: string,
|
|
28
|
+
transform: (line: string) => string | Promise<string>
|
|
29
|
+
): Promise<string> {
|
|
30
|
+
const out: string[] = [];
|
|
31
|
+
let inFence = false;
|
|
32
|
+
for (const line of md.split("\n")) {
|
|
33
|
+
if (/^```/.test(line)) {
|
|
34
|
+
inFence = !inFence;
|
|
35
|
+
out.push(line);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
out.push(inFence ? line : await transform(line));
|
|
39
|
+
}
|
|
40
|
+
return out.join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Rewrite `../assets/X` (the natural relative path from a `slides/*.md` file)
|
|
44
|
+
// to `assets/X` so the concatenated markdown — which lives at the deck root —
|
|
45
|
+
// resolves images correctly when previewed directly. Bare `X.png` references
|
|
46
|
+
// (no path) are left to the doctor to flag; we don't guess where they live.
|
|
47
|
+
function rewriteImageRefsForConcat(md: string): Promise<string> {
|
|
48
|
+
return mapMarkdownOutsideFences(md, (line) =>
|
|
49
|
+
line.replace(/(!\[[^\]]*\]\()([^)]+)(\))/g, (whole, open, ref, close) => {
|
|
50
|
+
const trimmed = ref.trim();
|
|
51
|
+
if (/^(https?:|data:)/i.test(trimmed)) return whole;
|
|
52
|
+
if (trimmed.startsWith("../assets/")) {
|
|
53
|
+
return `${open}${trimmed.slice(3)}${close}`;
|
|
54
|
+
}
|
|
55
|
+
return whole;
|
|
56
|
+
})
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Inline every local image reference in the concatenated markdown as a data: URI
|
|
61
|
+
// so the resulting HTML is truly self-contained (emailable, USB-stickable).
|
|
62
|
+
// Resolves refs against `deckDir` (the concat-md's location). Missing files are
|
|
63
|
+
// left untouched — the doctor flags them; build doesn't crash on author errors.
|
|
64
|
+
async function inlineImagesInMarkdown(md: string, deckDir: string): Promise<string> {
|
|
65
|
+
return mapMarkdownOutsideFences(md, async (line) => {
|
|
66
|
+
const matches = [...line.matchAll(/(!\[[^\]]*\]\()([^)]+)(\))/g)];
|
|
67
|
+
if (matches.length === 0) return line;
|
|
68
|
+
let result = line;
|
|
69
|
+
for (const m of matches) {
|
|
70
|
+
const [whole, open, ref, close] = m;
|
|
71
|
+
const trimmed = ref.trim();
|
|
72
|
+
if (/^(https?:|data:)/i.test(trimmed)) continue;
|
|
73
|
+
const abs = resolve(deckDir, trimmed);
|
|
74
|
+
try {
|
|
75
|
+
await access(abs, fsConst.F_OK);
|
|
76
|
+
} catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
// dataUri returns `url("data:...")` for CSS use; strip the `url("…")` wrapper for <img>.
|
|
80
|
+
const wrapped = await dataUri(abs);
|
|
81
|
+
const inner = wrapped.replace(/^url\("/, "").replace(/"\)$/, "");
|
|
82
|
+
result = result.replace(whole, `${open}${inner}${close}`);
|
|
83
|
+
}
|
|
84
|
+
return result;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
22
88
|
const ASPECTS: Record<string, [number, number]> = {
|
|
23
89
|
"16:9": [1920, 1080],
|
|
24
90
|
"16:10": [1920, 1200],
|
|
@@ -65,6 +131,62 @@ function deckSlug(deckDir: string): string {
|
|
|
65
131
|
return slug || "deck";
|
|
66
132
|
}
|
|
67
133
|
|
|
134
|
+
// Build-time injection: code-layout slides with > 15 lines of code get a
|
|
135
|
+
// `style="--code-scale: X"` attribute baked into the slide directive. CSS in
|
|
136
|
+
// layouts.css multiplies the base code font by this variable. Linear from 1.0
|
|
137
|
+
// at 15 lines to 0.6 at 25 lines; clamped at 0.6 beyond. Build-time keeps the
|
|
138
|
+
// attribute on the rendered <section>, so navigation/Highlight re-runs cannot
|
|
139
|
+
// strip it.
|
|
140
|
+
function injectCodeScale(slideMarkdown: string): string {
|
|
141
|
+
const layoutRe = /<!--\s*\.slide:\s*data-layout="code"([^>]*)-->/i;
|
|
142
|
+
const layoutMatch = layoutRe.exec(slideMarkdown);
|
|
143
|
+
if (!layoutMatch) return slideMarkdown;
|
|
144
|
+
|
|
145
|
+
const codeRe = /```[^\n]*\n([\s\S]*?)\n```/;
|
|
146
|
+
const codeMatch = codeRe.exec(slideMarkdown);
|
|
147
|
+
if (!codeMatch) return slideMarkdown;
|
|
148
|
+
|
|
149
|
+
const lines = codeMatch[1].split("\n").length;
|
|
150
|
+
if (lines <= 15) return slideMarkdown;
|
|
151
|
+
|
|
152
|
+
const scale = Math.max(0.6, 1 - (lines - 15) * 0.04).toFixed(2);
|
|
153
|
+
// Don't double-inject if a previous build already set it.
|
|
154
|
+
const extras = layoutMatch[1].replace(/\s+style="--code-scale:\s*[^"]+"/i, "").trim();
|
|
155
|
+
const attrs = extras
|
|
156
|
+
? `${extras} style="--code-scale: ${scale}"`
|
|
157
|
+
: `style="--code-scale: ${scale}"`;
|
|
158
|
+
const newDirective = `<!-- .slide: data-layout="code" ${attrs} -->`;
|
|
159
|
+
return slideMarkdown.replace(layoutMatch[0], newDirective);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Build-time injection: table-layout slides with > 4 rows get a
|
|
163
|
+
// `style="--table-scale: X"` attribute baked into the slide directive. CSS
|
|
164
|
+
// multiplies cell font-size AND cell padding by this var so both shrink
|
|
165
|
+
// together (font-only shrinking is dampened by static padding). Linear from
|
|
166
|
+
// 1.0 at 4 rows to 0.6 at ≥10 rows. Mirrors `injectCodeScale`.
|
|
167
|
+
function injectTableScale(slideMarkdown: string): string {
|
|
168
|
+
const layoutRe = /<!--\s*\.slide:\s*data-layout="table"([^>]*)-->/i;
|
|
169
|
+
const layoutMatch = layoutRe.exec(slideMarkdown);
|
|
170
|
+
if (!layoutMatch) return slideMarkdown;
|
|
171
|
+
|
|
172
|
+
// Count markdown table rows (lines starting with `|`) excluding the
|
|
173
|
+
// separator (`| --- | --- |`) which doesn't render as a row.
|
|
174
|
+
const sepRe = /^\s*\|(\s*:?-+:?\s*\|)+\s*$/;
|
|
175
|
+
let rows = 0;
|
|
176
|
+
for (const line of slideMarkdown.split("\n")) {
|
|
177
|
+
if (/^\s*\|/.test(line) && !sepRe.test(line)) rows++;
|
|
178
|
+
}
|
|
179
|
+
if (rows <= 4) return slideMarkdown;
|
|
180
|
+
|
|
181
|
+
const scale = Math.max(0.6, 1 - (rows - 4) * 0.067).toFixed(2);
|
|
182
|
+
const extras = layoutMatch[1].replace(/\s+style="--table-scale:\s*[^"]+"/i, "").trim();
|
|
183
|
+
const attrs = extras
|
|
184
|
+
? `${extras} style="--table-scale: ${scale}"`
|
|
185
|
+
: `style="--table-scale: ${scale}"`;
|
|
186
|
+
const newDirective = `<!-- .slide: data-layout="table" ${attrs} -->`;
|
|
187
|
+
return slideMarkdown.replace(layoutMatch[0], newDirective);
|
|
188
|
+
}
|
|
189
|
+
|
|
68
190
|
async function buildConcat(deckDir: string): Promise<string> {
|
|
69
191
|
const slidesDir = join(deckDir, "slides");
|
|
70
192
|
if (await exists(slidesDir)) {
|
|
@@ -73,7 +195,8 @@ async function buildConcat(deckDir: string): Promise<string> {
|
|
|
73
195
|
throw new Error(`slides/ is empty at ${slidesDir}`);
|
|
74
196
|
}
|
|
75
197
|
const parts = await Promise.all(files.map((f) => readText(join(slidesDir, f))));
|
|
76
|
-
|
|
198
|
+
const joined = `${parts.map((p) => injectTableScale(injectCodeScale(p.trim()))).join("\n\n---\n\n")}\n`;
|
|
199
|
+
return rewriteImageRefsForConcat(joined);
|
|
77
200
|
}
|
|
78
201
|
const legacy = join(deckDir, "content.md");
|
|
79
202
|
if (await exists(legacy)) {
|
|
@@ -90,7 +213,7 @@ async function main() {
|
|
|
90
213
|
}
|
|
91
214
|
const deckDir = resolve(argv[0]);
|
|
92
215
|
|
|
93
|
-
let outRoot =
|
|
216
|
+
let outRoot = deckDir;
|
|
94
217
|
let force = false;
|
|
95
218
|
for (let i = 1; i < argv.length; i++) {
|
|
96
219
|
if (argv[i] === "--out") outRoot = resolve(argv[++i]);
|
|
@@ -110,7 +233,10 @@ async function main() {
|
|
|
110
233
|
}
|
|
111
234
|
|
|
112
235
|
const slug = deckSlug(deckDir);
|
|
113
|
-
|
|
236
|
+
// When --out is the deck-dir itself (the default), write directly into it —
|
|
237
|
+
// no extra <slug>/ subdir. Otherwise create the subdir so multiple decks
|
|
238
|
+
// can coexist under one shared --out.
|
|
239
|
+
const outDir = resolve(outRoot) === deckDir ? deckDir : join(outRoot, slug);
|
|
114
240
|
const concatPath = join(outDir, `${slug}.md`);
|
|
115
241
|
const htmlPath = join(outDir, `${slug}.html`);
|
|
116
242
|
|
|
@@ -145,7 +271,7 @@ async function main() {
|
|
|
145
271
|
const skeleton = await readText(join(THEME_BASE, "skeleton.html"));
|
|
146
272
|
const revealCss = await readText(join(VENDOR_REVEAL, "reveal.css"));
|
|
147
273
|
const highlightCss = await readText(
|
|
148
|
-
join(VENDOR_REVEAL, "plugin", "highlight", "
|
|
274
|
+
join(VENDOR_REVEAL, "plugin", "highlight", "github-dark.css")
|
|
149
275
|
);
|
|
150
276
|
const revealJs = await readText(join(VENDOR_REVEAL, "reveal.js"));
|
|
151
277
|
const markdownJs = await readText(
|
|
@@ -156,7 +282,11 @@ async function main() {
|
|
|
156
282
|
);
|
|
157
283
|
const notesJs = await readText(join(VENDOR_REVEAL, "plugin", "notes", "notes.js"));
|
|
158
284
|
|
|
159
|
-
|
|
285
|
+
// Read the concat back from disk, then inline image refs as data: URIs so
|
|
286
|
+
// the HTML is self-contained. The on-disk concat keeps plain `assets/X` paths
|
|
287
|
+
// for direct markdown preview; only the HTML embeds full image bytes.
|
|
288
|
+
const contentMdRaw = await readFile(concatPath, "utf8");
|
|
289
|
+
const contentMd = await inlineImagesInMarkdown(contentMdRaw, deckDir);
|
|
160
290
|
|
|
161
291
|
let deckOverridesCss = "";
|
|
162
292
|
const overridesPath = join(deckDir, "overrides.css");
|