@atom63/slides 0.1.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/LICENSE +21 -0
- package/README.md +210 -0
- package/dist/chunk-AD3ZOVWR.js +6149 -0
- package/dist/chunk-AD3ZOVWR.js.map +1 -0
- package/dist/editor/index.css +68 -0
- package/dist/editor/index.css.map +1 -0
- package/dist/editor/index.d.ts +96 -0
- package/dist/editor/index.js +252 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/index.css +68 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.ts +1284 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/template-registry-BOJP4rlI.d.ts +81 -0
- package/dist/vite/index.d.ts +30 -0
- package/dist/vite/index.js +121 -0
- package/dist/vite/index.js.map +1 -0
- package/package.json +139 -0
- package/src/editor/styles.css +206 -0
- package/src/editor/styles.d.ts +1 -0
- package/src/styles/slides.css +30 -0
- package/src/styles/theme-defaults.css +223 -0
- package/src/styles/themes/bold.css +48 -0
- package/src/styles/themes/dark.css +45 -0
- package/src/styles/themes/editorial.css +48 -0
- package/src/styles/themes/neon.css +47 -0
- package/src/styles/themes/terminal.css +50 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* src/components/syllabus/syllabus-print.css */
|
|
2
|
+
.syllabus-prose a {
|
|
3
|
+
cursor: pointer;
|
|
4
|
+
}
|
|
5
|
+
.syllabus-prose [data-syllabus-block=meta] {
|
|
6
|
+
padding: 0;
|
|
7
|
+
border: 0;
|
|
8
|
+
}
|
|
9
|
+
.syllabus-prose [data-syllabus-block=meta] > div {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 8rem 1fr;
|
|
12
|
+
gap: 1rem;
|
|
13
|
+
padding: 0.5rem 0;
|
|
14
|
+
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
|
|
15
|
+
}
|
|
16
|
+
.syllabus-prose [data-syllabus-block=meta] > div:last-child {
|
|
17
|
+
border-bottom: 0;
|
|
18
|
+
}
|
|
19
|
+
.syllabus-prose [data-syllabus-block=meta] [data-syllabus-meta-label] {
|
|
20
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
21
|
+
font-size: 0.6875rem;
|
|
22
|
+
color: var(--muted-foreground);
|
|
23
|
+
text-transform: uppercase;
|
|
24
|
+
letter-spacing: 0.1em;
|
|
25
|
+
}
|
|
26
|
+
.syllabus-prose [data-syllabus-block=meta] [data-syllabus-meta-value] {
|
|
27
|
+
font-family: var(--font-serif, ui-serif, Georgia, serif);
|
|
28
|
+
font-size: 0.9375rem;
|
|
29
|
+
line-height: 1.5;
|
|
30
|
+
color: var(--foreground);
|
|
31
|
+
}
|
|
32
|
+
@media print {
|
|
33
|
+
#syllabus-print-clone {
|
|
34
|
+
box-sizing: border-box !important;
|
|
35
|
+
display: block !important;
|
|
36
|
+
width: 100% !important;
|
|
37
|
+
max-width: none !important;
|
|
38
|
+
padding: 0.6in 0.7in !important;
|
|
39
|
+
margin: 0 !important;
|
|
40
|
+
color: black !important;
|
|
41
|
+
background: white !important;
|
|
42
|
+
-webkit-box-decoration-break: clone !important;
|
|
43
|
+
box-decoration-break: clone !important;
|
|
44
|
+
}
|
|
45
|
+
#syllabus-print-clone .syllabus-prose h1,
|
|
46
|
+
#syllabus-print-clone .syllabus-prose h2,
|
|
47
|
+
#syllabus-print-clone .syllabus-prose h3,
|
|
48
|
+
#syllabus-print-clone .syllabus-prose h4,
|
|
49
|
+
#syllabus-print-clone .syllabus-prose h5,
|
|
50
|
+
#syllabus-print-clone .syllabus-prose h6 {
|
|
51
|
+
break-after: avoid;
|
|
52
|
+
page-break-after: avoid;
|
|
53
|
+
}
|
|
54
|
+
#syllabus-print-clone a {
|
|
55
|
+
color: black !important;
|
|
56
|
+
text-decoration-color: #999 !important;
|
|
57
|
+
}
|
|
58
|
+
#syllabus-print-clone > header {
|
|
59
|
+
break-inside: avoid;
|
|
60
|
+
page-break-inside: avoid;
|
|
61
|
+
border-top-color: black !important;
|
|
62
|
+
}
|
|
63
|
+
@page {
|
|
64
|
+
size: letter;
|
|
65
|
+
margin: 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/syllabus/syllabus-print.css"],"sourcesContent":["/*\n * Styles for the syllabus reading layout and print pipeline.\n *\n * The slide-primitive CSS reset that previously lived here is no longer needed.\n * Markdown inside <Syllabus> now renders with native HTML elements styled by\n * mdxStyles tokens via SyllabusContentContext — no !important overrides required.\n *\n * Only the meta block (definition-list layout) and print pipeline remain.\n */\n\n/* ──────── Link interactivity ──────── */\n\n/* Hover color is driven by JS (onClonedAnchorEnter/Leave in syllabus-view.tsx)\n * because CSS :hover is suppressed on DOM-cloned nodes inside the presentation view. */\n.syllabus-prose a {\n cursor: pointer;\n}\n\n/* ──────── Meta block: definition-list style ──────── */\n\n.syllabus-prose [data-syllabus-block=\"meta\"] {\n padding: 0;\n border: 0;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] > div {\n display: grid;\n grid-template-columns: 8rem 1fr;\n gap: 1rem;\n padding: 0.5rem 0;\n border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] > div:last-child {\n border-bottom: 0;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] [data-syllabus-meta-label] {\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-transform: uppercase;\n letter-spacing: 0.1em;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] [data-syllabus-meta-value] {\n font-family: var(--font-serif, ui-serif, Georgia, serif);\n font-size: 0.9375rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n/* ──────── Print styles ──────── */\n\n/* Print pipeline (see syllabus-view.tsx):\n *\n * On `beforeprint` we clone the article into <body id=\"syllabus-print-clone\">\n * and hide every other body child with `display:none !important`. That\n * bypasses the presentation overlay's transform / position-absolute stack which\n * otherwise breaks paginated layout. */\n\n@media print {\n /* The html/body/#root page-level resets are applied as inline styles by the\n syllabus print handler (syllabus-view.tsx beforePrint), only during an\n actual syllabus print. A global rule here would also fire during another\n app's print (e.g. the resume) whenever slides is merely open, so it is\n intentionally omitted to avoid leaking across apps. */\n\n #syllabus-print-clone {\n box-sizing: border-box !important;\n display: block !important;\n width: 100% !important;\n max-width: none !important;\n padding: 0.6in 0.7in !important;\n margin: 0 !important;\n color: black !important;\n background: white !important;\n -webkit-box-decoration-break: clone !important;\n box-decoration-break: clone !important;\n }\n\n #syllabus-print-clone .syllabus-prose h1,\n #syllabus-print-clone .syllabus-prose h2,\n #syllabus-print-clone .syllabus-prose h3,\n #syllabus-print-clone .syllabus-prose h4,\n #syllabus-print-clone .syllabus-prose h5,\n #syllabus-print-clone .syllabus-prose h6 {\n break-after: avoid;\n page-break-after: avoid;\n }\n\n #syllabus-print-clone a {\n color: black !important;\n text-decoration-color: #999 !important;\n }\n\n #syllabus-print-clone > header {\n break-inside: avoid;\n page-break-inside: avoid;\n border-top-color: black !important;\n }\n\n @page {\n size: letter;\n margin: 0;\n }\n}\n"],"mappings":";AAcA,CAAC,eAAe;AACd,UAAQ;AACV;AAIA,CANC,eAMe,CAAC;AACf,WAAS;AACT,UAAQ;AACV;AACA,CAVC,eAUe,CAAC,0BAA4B,EAAE;AAC7C,WAAS;AACT,yBAAuB,KAAK;AAC5B,OAAK;AACL,WAAS,OAAO;AAChB,iBAAe,IAAI,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,UAAU,GAAG,EAAE;AACjE;AACA,CAjBC,eAiBe,CAAC,0BAA4B,EAAE,GAAG;AAChD,iBAAe;AACjB;AACA,CApBC,eAoBe,CAAC,0BAA4B,CAAC;AAC5C,eAAa,IAAI,WAAW,EAAE,YAAY,EAAE;AAC5C,aAAW;AACX,SAAO,IAAI;AACX,kBAAgB;AAChB,kBAAgB;AAClB;AACA,CA3BC,eA2Be,CAAC,0BAA4B,CAAC;AAC5C,eAAa,IAAI,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE;AAClD,aAAW;AACX,eAAa;AACb,SAAO,IAAI;AACb;AAWA,OAAO;AAOL,GAAC;AACC,gBAAY;AACZ,aAAS;AACT,WAAO;AACP,eAAW;AACX,aAAS,MAAM;AACf,YAAQ;AACR,WAAO;AACP,gBAAY;AACZ,kCAA8B;AAC9B,0BAAsB;AACxB;AAEA,GAbC,qBAaqB,CA/DvB,eA+DuC;AAAA,EACtC,CAdC,qBAcqB,CAhEvB,eAgEuC;AAAA,EACtC,CAfC,qBAeqB,CAjEvB,eAiEuC;AAAA,EACtC,CAhBC,qBAgBqB,CAlEvB,eAkEuC;AAAA,EACtC,CAjBC,qBAiBqB,CAnEvB,eAmEuC;AAAA,EACtC,CAlBC,qBAkBqB,CApEvB,eAoEuC;AACpC,iBAAa;AACb,sBAAkB;AACpB;AAEA,GAvBC,qBAuBqB;AACpB,WAAO;AACP,2BAAuB;AACzB;AAEA,GA5BC,qBA4BqB,EAAE;AACtB,kBAAc;AACd,uBAAmB;AACnB,sBAAkB;AACpB;AAEA;AACE,UAAM;AACN,YAAQ;AACV;AACF;","names":[]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { b as SlideDeckMeta, f as TemplateDef } from '../template-registry-BOJP4rlI.js';
|
|
3
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Result of a runtime deck compile.
|
|
7
|
+
*
|
|
8
|
+
* On success: the parsed frontmatter `meta` plus a `Content` component ready to
|
|
9
|
+
* hand to `SlidesPlayer` as `deck.content`. On failure: a human-readable
|
|
10
|
+
* `error` string (compile + frontmatter parsing both throw on bad input).
|
|
11
|
+
*/
|
|
12
|
+
type CompileDeckResult = {
|
|
13
|
+
ok: true;
|
|
14
|
+
meta: SlideDeckMeta;
|
|
15
|
+
Content: ComponentType;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
error: string;
|
|
19
|
+
};
|
|
20
|
+
/** Remove bare `import ... from "..."` lines so runtime evaluate doesn't choke. */
|
|
21
|
+
declare function stripImports(body: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Compile a deck's raw MDX source into a renderable Content component using a
|
|
24
|
+
* runtime MDX pipeline (no bundler step).
|
|
25
|
+
*
|
|
26
|
+
* Pipeline:
|
|
27
|
+
* 1. `parseFrontmatter` splits the YAML frontmatter (the deck `meta`) from the
|
|
28
|
+
* body using a browser-safe parser (no Node `Buffer`).
|
|
29
|
+
* 2. `stripImports` removes bare `import ... from "@atom63/slides"` lines that
|
|
30
|
+
* the runtime evaluator cannot resolve.
|
|
31
|
+
* 3. `@mdx-js/mdx`'s `evaluate` compiles + runs the body. The slide components
|
|
32
|
+
* (templates + primitives + markdown mappings) are provided through MDX
|
|
33
|
+
* context via `useMDXComponents: () => slideMdxComponents`, so bare JSX such
|
|
34
|
+
* as `<CoverSlide/>` resolves with no import. `---` thematic breaks are
|
|
35
|
+
* preserved as the engine's slide separators.
|
|
36
|
+
*
|
|
37
|
+
* Async and can throw on malformed MDX/frontmatter — callers should debounce
|
|
38
|
+
* and keep the last good render on failure (DeckEditor does this).
|
|
39
|
+
*/
|
|
40
|
+
declare function compileDeck(source: string): Promise<CompileDeckResult>;
|
|
41
|
+
|
|
42
|
+
interface DeckEditorProps {
|
|
43
|
+
/** Raw deck MDX source (frontmatter + body with `---` slide separators). */
|
|
44
|
+
source: string;
|
|
45
|
+
/**
|
|
46
|
+
* Called with the next source on every edit / template insert. When omitted,
|
|
47
|
+
* the editor manages source internally (uncontrolled).
|
|
48
|
+
*/
|
|
49
|
+
onChange?: (next: string) => void;
|
|
50
|
+
/** Debounce window (ms) before recompiling the preview. Default 300. */
|
|
51
|
+
debounceMs?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* v0.1 GUI deck editor for @atom63/slides.
|
|
55
|
+
*
|
|
56
|
+
* Two panes:
|
|
57
|
+
* - left: a LIVE preview rendered by `SlidesPlayer`, compiled from the source
|
|
58
|
+
* at runtime via {@link compileDeck} (browser-safe frontmatter split +
|
|
59
|
+
* strip-imports + `@mdx-js/mdx` evaluate, with slide components injected
|
|
60
|
+
* through MDX context).
|
|
61
|
+
* - right: a `<textarea>` editing surface, a registry-driven template palette
|
|
62
|
+
* (click inserts the template's MDX snippet at the end of the source), and a
|
|
63
|
+
* light/dark theme toggle for the preview container.
|
|
64
|
+
*
|
|
65
|
+
* Compilation is debounced and resilient: the LAST good preview is retained on
|
|
66
|
+
* a failed compile and the error is surfaced in a non-blocking banner, so a bad
|
|
67
|
+
* keystroke never blanks or crashes the editor.
|
|
68
|
+
*
|
|
69
|
+
* The editor's own chrome is styled by self-contained plain CSS shipped as a
|
|
70
|
+
* side-effect import — consumers must `import '@atom63/slides/editor/styles'`
|
|
71
|
+
* (the CSS is intentionally NOT bundled into the JS so it isn't orphaned by the
|
|
72
|
+
* build, mirroring how `@atom63/slides` ships `./styles`).
|
|
73
|
+
*
|
|
74
|
+
* A `<textarea>` is intentional
|
|
75
|
+
* for v0.1; CodeMirror (syntax highlight, structured editing) is a future
|
|
76
|
+
* upgrade. Structured per-slide form editing and template-switch-by-form are
|
|
77
|
+
* explicitly v2 — the source textarea is the only editing surface here.
|
|
78
|
+
*/
|
|
79
|
+
declare function DeckEditor({ source: sourceProp, onChange, debounceMs }: DeckEditorProps): react_jsx_runtime.JSX.Element;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Synthesize a minimal, valid MDX usage example from a template's schema.
|
|
83
|
+
* - Simple templates → self-closing tag with its props.
|
|
84
|
+
* - Compound templates → open tag + one instance of each slot at `min` (or 1).
|
|
85
|
+
*/
|
|
86
|
+
declare function synthExample(t: TemplateDef): string;
|
|
87
|
+
/**
|
|
88
|
+
* Wrap a synthesized example as an insertable slide: a leading `---` separator
|
|
89
|
+
* (the engine's slide break) followed by the example, so appending to a deck
|
|
90
|
+
* body always starts a fresh slide.
|
|
91
|
+
*/
|
|
92
|
+
declare function toInsertSnippet(t: TemplateDef): string;
|
|
93
|
+
/** Map of `templateName -> insertable MDX snippet`, built from the live registry. */
|
|
94
|
+
declare const templateSnippets: Record<string, string>;
|
|
95
|
+
|
|
96
|
+
export { type CompileDeckResult, DeckEditor, type DeckEditorProps, compileDeck, stripImports, synthExample, templateSnippets, toInsertSnippet };
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { listTemplates, slideMdxComponents, SlidesPlayer } from '../chunk-AD3ZOVWR.js';
|
|
2
|
+
import { evaluate } from '@mdx-js/mdx';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import * as runtime from 'react/jsx-runtime';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
import remarkGfm from 'remark-gfm';
|
|
7
|
+
import { useState, useRef, useMemo, useCallback, useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
function parseFrontmatter(source) {
|
|
10
|
+
const normalized = source.replace(/^/, "");
|
|
11
|
+
const lines = normalized.split("\n");
|
|
12
|
+
if (lines[0]?.trim() !== "---") {
|
|
13
|
+
return { meta: {}, body: source };
|
|
14
|
+
}
|
|
15
|
+
let end = -1;
|
|
16
|
+
for (let i = 1; i < lines.length; i++) {
|
|
17
|
+
if (lines[i].trim() === "---") {
|
|
18
|
+
end = i;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (end === -1) {
|
|
23
|
+
return { meta: {}, body: source };
|
|
24
|
+
}
|
|
25
|
+
const yamlBlock = lines.slice(1, end).join("\n");
|
|
26
|
+
const body = lines.slice(end + 1).join("\n");
|
|
27
|
+
const loaded = yaml.load(yamlBlock);
|
|
28
|
+
const meta = loaded && typeof loaded === "object" ? loaded : {};
|
|
29
|
+
return { meta, body };
|
|
30
|
+
}
|
|
31
|
+
var IMPORT_RE = /^[ \t]*import\b[\s\S]*?(?:from\s*['"][^'"]*['"]|['"][^'"]*['"])[ \t]*;?[ \t]*$/gm;
|
|
32
|
+
function stripImports(body) {
|
|
33
|
+
return body.replace(IMPORT_RE, "");
|
|
34
|
+
}
|
|
35
|
+
var DEFAULT_META = {
|
|
36
|
+
title: "Untitled deck",
|
|
37
|
+
date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
38
|
+
};
|
|
39
|
+
async function compileDeck(source) {
|
|
40
|
+
try {
|
|
41
|
+
const { meta: data, body: content } = parseFrontmatter(source);
|
|
42
|
+
const meta = { ...DEFAULT_META, ...data };
|
|
43
|
+
const body = stripImports(content);
|
|
44
|
+
const { default: Content } = await evaluate(body, {
|
|
45
|
+
...runtime,
|
|
46
|
+
useMDXComponents: () => slideMdxComponents,
|
|
47
|
+
remarkPlugins: [remarkGfm]
|
|
48
|
+
});
|
|
49
|
+
return { ok: true, meta, Content };
|
|
50
|
+
} catch (err) {
|
|
51
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
52
|
+
return { ok: false, error };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// src/editor/template-snippets.ts
|
|
57
|
+
var PLACEHOLDER_IMG = "/images/placeholder-1920x1080.webp";
|
|
58
|
+
function exampleValue(prop) {
|
|
59
|
+
switch (prop.kind) {
|
|
60
|
+
case "media":
|
|
61
|
+
return PLACEHOLDER_IMG;
|
|
62
|
+
default:
|
|
63
|
+
return `${prop.label}\u2026`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function renderPropAttrs(props, indent = " ") {
|
|
67
|
+
const lines = [];
|
|
68
|
+
for (const prop of props) {
|
|
69
|
+
if (prop.key === "children") continue;
|
|
70
|
+
if (prop.array) {
|
|
71
|
+
if (prop.kind === "media") {
|
|
72
|
+
lines.push(
|
|
73
|
+
`${indent}${prop.key}={["${PLACEHOLDER_IMG}", "${PLACEHOLDER_IMG}", "${PLACEHOLDER_IMG}"]}`
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
lines.push(`${indent}${prop.key}={[{ label: "Label", value: "Value", href: "https://\u2026" }]}`);
|
|
77
|
+
}
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
lines.push(`${indent}${prop.key}="${exampleValue(prop)}"`);
|
|
81
|
+
}
|
|
82
|
+
return lines;
|
|
83
|
+
}
|
|
84
|
+
function renderSlotChild(templateName, slot) {
|
|
85
|
+
const attrs = [];
|
|
86
|
+
let childrenText = null;
|
|
87
|
+
for (const prop of slot.props) {
|
|
88
|
+
if (prop.key === "children") {
|
|
89
|
+
childrenText = `${prop.label}\u2026`;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (prop.array) {
|
|
93
|
+
attrs.push(`${prop.key}={[\u2026]}`);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const value = prop.kind === "media" ? PLACEHOLDER_IMG : `${prop.label}\u2026`;
|
|
97
|
+
attrs.push(`${prop.key}="${value}"`);
|
|
98
|
+
}
|
|
99
|
+
const tag = `${templateName}.${slot.name}`;
|
|
100
|
+
const attrStr = attrs.length ? ` ${attrs.join(" ")}` : "";
|
|
101
|
+
if (childrenText !== null) {
|
|
102
|
+
return ` <${tag}${attrStr}>${childrenText}</${tag}>`;
|
|
103
|
+
}
|
|
104
|
+
return ` <${tag}${attrStr} />`;
|
|
105
|
+
}
|
|
106
|
+
function synthExample(t) {
|
|
107
|
+
const hasSlots = t.slots.length > 0;
|
|
108
|
+
const propLines = renderPropAttrs(t.props);
|
|
109
|
+
if (!hasSlots) {
|
|
110
|
+
if (propLines.length === 0) return `<${t.name} />`;
|
|
111
|
+
return [`<${t.name}`, ...propLines, "/>"].join("\n");
|
|
112
|
+
}
|
|
113
|
+
const open = propLines.length ? [`<${t.name}`, ...propLines, ">"] : [`<${t.name}>`];
|
|
114
|
+
const childLines = [];
|
|
115
|
+
for (const slot of t.slots) {
|
|
116
|
+
const count = Math.min(Math.max(slot.min, 1), 3);
|
|
117
|
+
for (let i = 0; i < count; i++) {
|
|
118
|
+
childLines.push(renderSlotChild(t.name, slot));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return [...open, ...childLines, `</${t.name}>`].join("\n");
|
|
122
|
+
}
|
|
123
|
+
function toInsertSnippet(t) {
|
|
124
|
+
return `
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
${synthExample(t)}
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
var templateSnippets = Object.fromEntries(
|
|
131
|
+
listTemplates().map((t) => [t.name, toInsertSnippet(t)])
|
|
132
|
+
);
|
|
133
|
+
function DeckEditor({ source: sourceProp, onChange, debounceMs = 300 }) {
|
|
134
|
+
const isControlled = onChange !== void 0;
|
|
135
|
+
const [internalSource, setInternalSource] = useState(sourceProp);
|
|
136
|
+
const source = isControlled ? sourceProp : internalSource;
|
|
137
|
+
const [preview, setPreview] = useState(null);
|
|
138
|
+
const [error, setError] = useState(null);
|
|
139
|
+
const [theme, setTheme] = useState("light");
|
|
140
|
+
const textareaRef = useRef(null);
|
|
141
|
+
const templates = useMemo(() => listTemplates(), []);
|
|
142
|
+
const setSource = useCallback(
|
|
143
|
+
(next) => {
|
|
144
|
+
if (isControlled) onChange(next);
|
|
145
|
+
else setInternalSource(next);
|
|
146
|
+
},
|
|
147
|
+
[isControlled, onChange]
|
|
148
|
+
);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
let cancelled = false;
|
|
151
|
+
const handle = setTimeout(async () => {
|
|
152
|
+
const result = await compileDeck(source);
|
|
153
|
+
if (cancelled) return;
|
|
154
|
+
if (result.ok) {
|
|
155
|
+
setPreview({ Content: result.Content, meta: result.meta });
|
|
156
|
+
setError(null);
|
|
157
|
+
} else {
|
|
158
|
+
setError(result.error);
|
|
159
|
+
}
|
|
160
|
+
}, debounceMs);
|
|
161
|
+
return () => {
|
|
162
|
+
cancelled = true;
|
|
163
|
+
clearTimeout(handle);
|
|
164
|
+
};
|
|
165
|
+
}, [source, debounceMs]);
|
|
166
|
+
const handleChange = useCallback(
|
|
167
|
+
(e) => setSource(e.target.value),
|
|
168
|
+
[setSource]
|
|
169
|
+
);
|
|
170
|
+
const handleInsertTemplate = useCallback(
|
|
171
|
+
(name) => {
|
|
172
|
+
const snippet = templateSnippets[name];
|
|
173
|
+
if (!snippet) return;
|
|
174
|
+
const next = `${source.replace(/\s*$/, "")}
|
|
175
|
+
${snippet}`;
|
|
176
|
+
setSource(next);
|
|
177
|
+
requestAnimationFrame(() => {
|
|
178
|
+
const el = textareaRef.current;
|
|
179
|
+
if (!el) return;
|
|
180
|
+
el.focus();
|
|
181
|
+
el.selectionStart = el.selectionEnd = el.value.length;
|
|
182
|
+
el.scrollTop = el.scrollHeight;
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
[source, setSource]
|
|
186
|
+
);
|
|
187
|
+
const deck = useMemo(() => {
|
|
188
|
+
if (!preview) return null;
|
|
189
|
+
return { slug: "draft", meta: preview.meta, content: preview.Content };
|
|
190
|
+
}, [preview]);
|
|
191
|
+
return /* @__PURE__ */ jsxs("div", { className: "a63-editor", children: [
|
|
192
|
+
/* @__PURE__ */ jsxs("section", { className: "a63-editor__preview", "data-theme": theme, children: [
|
|
193
|
+
error ? /* @__PURE__ */ jsxs("div", { className: "a63-editor__error", role: "alert", children: [
|
|
194
|
+
/* @__PURE__ */ jsx("span", { className: "a63-editor__error-tag", children: "MDX error" }),
|
|
195
|
+
/* @__PURE__ */ jsx("span", { className: "a63-editor__error-msg", children: error })
|
|
196
|
+
] }) : null,
|
|
197
|
+
deck ? (
|
|
198
|
+
// Remount on theme change so the player picks up the canvas theme.
|
|
199
|
+
/* @__PURE__ */ jsx("div", { className: "a63-editor__preview-stage", children: /* @__PURE__ */ jsx(SlidesPlayer, { deck, onBack: () => {
|
|
200
|
+
} }) }, `${theme}`)
|
|
201
|
+
) : /* @__PURE__ */ jsx("div", { className: "a63-editor__preview-empty", children: error ? "Fix the MDX error to render a preview." : "Compiling preview\u2026" })
|
|
202
|
+
] }),
|
|
203
|
+
/* @__PURE__ */ jsxs("section", { className: "a63-editor__source", children: [
|
|
204
|
+
/* @__PURE__ */ jsxs("div", { className: "a63-editor__toolbar", children: [
|
|
205
|
+
/* @__PURE__ */ jsx("span", { className: "a63-editor__title", children: "Deck source \xB7 MDX" }),
|
|
206
|
+
/* @__PURE__ */ jsx(
|
|
207
|
+
"button",
|
|
208
|
+
{
|
|
209
|
+
type: "button",
|
|
210
|
+
className: "a63-editor__theme-toggle",
|
|
211
|
+
onClick: () => setTheme((t) => t === "light" ? "dark" : "light"),
|
|
212
|
+
"aria-pressed": theme === "dark",
|
|
213
|
+
children: theme === "light" ? "\u2600\uFE0E Light" : "\u263E Dark"
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
] }),
|
|
217
|
+
/* @__PURE__ */ jsx(
|
|
218
|
+
"textarea",
|
|
219
|
+
{
|
|
220
|
+
ref: textareaRef,
|
|
221
|
+
className: "a63-editor__textarea",
|
|
222
|
+
value: source,
|
|
223
|
+
onChange: handleChange,
|
|
224
|
+
spellCheck: false,
|
|
225
|
+
"aria-label": "Deck MDX source"
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "a63-editor__palette", children: [
|
|
229
|
+
/* @__PURE__ */ jsx("div", { className: "a63-editor__palette-label", children: "Templates \xB7 click to append" }),
|
|
230
|
+
/* @__PURE__ */ jsx("div", { className: "a63-editor__palette-grid", children: templates.map((t) => /* @__PURE__ */ jsxs(
|
|
231
|
+
"button",
|
|
232
|
+
{
|
|
233
|
+
type: "button",
|
|
234
|
+
className: "a63-editor__chip",
|
|
235
|
+
onClick: () => handleInsertTemplate(t.name),
|
|
236
|
+
title: `Insert ${t.name}`,
|
|
237
|
+
children: [
|
|
238
|
+
/* @__PURE__ */ jsx("span", { className: "a63-editor__chip-name", children: t.name }),
|
|
239
|
+
/* @__PURE__ */ jsx("span", { className: "a63-editor__chip-meta", children: t.label })
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
t.name
|
|
243
|
+
)) }),
|
|
244
|
+
/* @__PURE__ */ jsx("p", { className: "a63-editor__footnote", children: "v0.1 \xB7 plain-textarea editing (CodeMirror is a future upgrade). Per-slide form editing & template-switch are deferred to v2." })
|
|
245
|
+
] })
|
|
246
|
+
] })
|
|
247
|
+
] });
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { DeckEditor, compileDeck, stripImports, synthExample, templateSnippets, toInsertSnippet };
|
|
251
|
+
//# sourceMappingURL=index.js.map
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/editor/compile-deck.ts","../../src/editor/template-snippets.ts","../../src/editor/deck-editor.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { evaluate } from '@mdx-js/mdx'\nimport yaml from 'js-yaml'\nimport type { ComponentType } from 'react'\nimport * as runtime from 'react/jsx-runtime'\nimport remarkGfm from 'remark-gfm'\nimport { slideMdxComponents } from '../content/mdx-components'\nimport type { SlideDeckMeta } from '../types'\n\n/**\n * Browser-safe YAML frontmatter split. We deliberately avoid the common\n * \"gray matter\" style parser because it depends on Node's `Buffer`, which\n * doesn't exist in the browser — a browser library must not force consumers to\n * polyfill Node globals.\n *\n * If `source` begins with a `---` fence line, the block up to the next `---`\n * fence is parsed as YAML into `meta` and the remainder is returned as `body`.\n * Otherwise `meta` is empty and the whole source is the `body`.\n */\nexport function parseFrontmatter(source: string): { meta: Record<string, unknown>; body: string } {\n // Frontmatter must start at the very top of the file (allowing a UTF-8 BOM).\n const normalized = source.replace(/^/, '')\n const lines = normalized.split('\\n')\n if (lines[0]?.trim() !== '---') {\n return { meta: {}, body: source }\n }\n\n // Find the closing fence line.\n let end = -1\n for (let i = 1; i < lines.length; i++) {\n if (lines[i].trim() === '---') {\n end = i\n break\n }\n }\n if (end === -1) {\n // No closing fence — treat the whole thing as body, no frontmatter.\n return { meta: {}, body: source }\n }\n\n const yamlBlock = lines.slice(1, end).join('\\n')\n const body = lines.slice(end + 1).join('\\n')\n const loaded = yaml.load(yamlBlock)\n const meta = loaded && typeof loaded === 'object' ? (loaded as Record<string, unknown>) : {}\n return { meta, body }\n}\n\n/**\n * Result of a runtime deck compile.\n *\n * On success: the parsed frontmatter `meta` plus a `Content` component ready to\n * hand to `SlidesPlayer` as `deck.content`. On failure: a human-readable\n * `error` string (compile + frontmatter parsing both throw on bad input).\n */\nexport type CompileDeckResult =\n | { ok: true; meta: SlideDeckMeta; Content: ComponentType }\n | { ok: false; error: string }\n\n/**\n * Matches bare ES `import` statements at the start of a line. A deck's MDX body\n * carries lines like `import { CoverSlide } from \"@atom63/slides\"`. The runtime\n * `evaluate` below has no module resolver, so these must be stripped — the\n * components are injected via `useMDXComponents` instead (see compileDeck).\n *\n * Covers single- and multi-line import specifier blocks:\n * import Foo from \"x\"\n * import { A, B } from \"x\"\n * import {\n * A,\n * B,\n * } from \"x\"\n */\nconst IMPORT_RE = /^[ \\t]*import\\b[\\s\\S]*?(?:from\\s*['\"][^'\"]*['\"]|['\"][^'\"]*['\"])[ \\t]*;?[ \\t]*$/gm\n\n/** Remove bare `import ... from \"...\"` lines so runtime evaluate doesn't choke. */\nexport function stripImports(body: string): string {\n return body.replace(IMPORT_RE, '')\n}\n\nconst DEFAULT_META: SlideDeckMeta = {\n title: 'Untitled deck',\n date: new Date().toISOString().slice(0, 10),\n}\n\n/**\n * Compile a deck's raw MDX source into a renderable Content component using a\n * runtime MDX pipeline (no bundler step).\n *\n * Pipeline:\n * 1. `parseFrontmatter` splits the YAML frontmatter (the deck `meta`) from the\n * body using a browser-safe parser (no Node `Buffer`).\n * 2. `stripImports` removes bare `import ... from \"@atom63/slides\"` lines that\n * the runtime evaluator cannot resolve.\n * 3. `@mdx-js/mdx`'s `evaluate` compiles + runs the body. The slide components\n * (templates + primitives + markdown mappings) are provided through MDX\n * context via `useMDXComponents: () => slideMdxComponents`, so bare JSX such\n * as `<CoverSlide/>` resolves with no import. `---` thematic breaks are\n * preserved as the engine's slide separators.\n *\n * Async and can throw on malformed MDX/frontmatter — callers should debounce\n * and keep the last good render on failure (DeckEditor does this).\n */\nexport async function compileDeck(source: string): Promise<CompileDeckResult> {\n try {\n const { meta: data, body: content } = parseFrontmatter(source)\n const meta: SlideDeckMeta = { ...DEFAULT_META, ...(data as Partial<SlideDeckMeta>) }\n const body = stripImports(content)\n\n const { default: Content } = await evaluate(body, {\n ...runtime,\n useMDXComponents: () => slideMdxComponents,\n remarkPlugins: [remarkGfm],\n })\n\n return { ok: true, meta, Content: Content as ComponentType }\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err)\n return { ok: false, error }\n }\n}\n","import {\n listTemplates,\n type SlotDef,\n type SlotGroupDef,\n type TemplateDef,\n} from '../content/template-registry'\n\n/**\n * Generates a minimal, paste-ready MDX snippet per template from the engine's\n * registry (`listTemplates()`), mirroring the synthesis logic in\n * `skill/scripts/gen-templates.mjs`. The palette in\n * <DeckEditor> appends `templateSnippets[name]` when a template is clicked.\n *\n * Registry is the single source of truth; snippets cannot drift from the\n * published template props/slots.\n */\n\nconst PLACEHOLDER_IMG = '/images/placeholder-1920x1080.webp'\n\nfunction exampleValue(prop: SlotDef): string {\n switch (prop.kind) {\n case 'media':\n return PLACEHOLDER_IMG\n default:\n // text / richtext / list-as-scalar\n return `${prop.label}…`\n }\n}\n\n/** Render the direct-prop attributes for a template's opening tag. */\nfunction renderPropAttrs(props: SlotDef[], indent = ' '): string[] {\n const lines: string[] = []\n for (const prop of props) {\n if (prop.key === 'children') continue\n if (prop.array) {\n if (prop.kind === 'media') {\n lines.push(\n `${indent}${prop.key}={[\"${PLACEHOLDER_IMG}\", \"${PLACEHOLDER_IMG}\", \"${PLACEHOLDER_IMG}\"]}`\n )\n } else {\n lines.push(`${indent}${prop.key}={[{ label: \"Label\", value: \"Value\", href: \"https://…\" }]}`)\n }\n continue\n }\n lines.push(`${indent}${prop.key}=\"${exampleValue(prop)}\"`)\n }\n return lines\n}\n\n/** Render one compound-slot child element, e.g. `<HeroBento.Card title=\"…\" />`. */\nfunction renderSlotChild(templateName: string, slot: SlotGroupDef): string {\n const attrs: string[] = []\n let childrenText: string | null = null\n for (const prop of slot.props) {\n if (prop.key === 'children') {\n childrenText = `${prop.label}…`\n continue\n }\n if (prop.array) {\n attrs.push(`${prop.key}={[…]}`)\n continue\n }\n const value = prop.kind === 'media' ? PLACEHOLDER_IMG : `${prop.label}…`\n attrs.push(`${prop.key}=\"${value}\"`)\n }\n const tag = `${templateName}.${slot.name}`\n const attrStr = attrs.length ? ` ${attrs.join(' ')}` : ''\n if (childrenText !== null) {\n return ` <${tag}${attrStr}>${childrenText}</${tag}>`\n }\n return ` <${tag}${attrStr} />`\n}\n\n/**\n * Synthesize a minimal, valid MDX usage example from a template's schema.\n * - Simple templates → self-closing tag with its props.\n * - Compound templates → open tag + one instance of each slot at `min` (or 1).\n */\nexport function synthExample(t: TemplateDef): string {\n const hasSlots = t.slots.length > 0\n const propLines = renderPropAttrs(t.props)\n\n if (!hasSlots) {\n if (propLines.length === 0) return `<${t.name} />`\n return [`<${t.name}`, ...propLines, '/>'].join('\\n')\n }\n\n const open = propLines.length ? [`<${t.name}`, ...propLines, '>'] : [`<${t.name}>`]\n const childLines: string[] = []\n for (const slot of t.slots) {\n const count = Math.min(Math.max(slot.min, 1), 3)\n for (let i = 0; i < count; i++) {\n childLines.push(renderSlotChild(t.name, slot))\n }\n }\n return [...open, ...childLines, `</${t.name}>`].join('\\n')\n}\n\n/**\n * Wrap a synthesized example as an insertable slide: a leading `---` separator\n * (the engine's slide break) followed by the example, so appending to a deck\n * body always starts a fresh slide.\n */\nexport function toInsertSnippet(t: TemplateDef): string {\n return `\\n---\\n\\n${synthExample(t)}\\n`\n}\n\n/** Map of `templateName -> insertable MDX snippet`, built from the live registry. */\nexport const templateSnippets: Record<string, string> = Object.fromEntries(\n listTemplates().map(t => [t.name, toInsertSnippet(t)])\n)\n","import {\n type ChangeEvent,\n type ComponentType,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport { listTemplates } from '../content/template-registry'\nimport { SlidesPlayer } from '../player/slides-player'\nimport type { SlideDeckItem } from '../types'\nimport { compileDeck } from './compile-deck'\nimport { templateSnippets } from './template-snippets'\n\nexport interface DeckEditorProps {\n /** Raw deck MDX source (frontmatter + body with `---` slide separators). */\n source: string\n /**\n * Called with the next source on every edit / template insert. When omitted,\n * the editor manages source internally (uncontrolled).\n */\n onChange?: (next: string) => void\n /** Debounce window (ms) before recompiling the preview. Default 300. */\n debounceMs?: number\n}\n\ntype PreviewState = {\n Content: ComponentType\n meta: SlideDeckItem['meta']\n} | null\n\ntype EditorTheme = 'light' | 'dark'\n\n/**\n * v0.1 GUI deck editor for @atom63/slides.\n *\n * Two panes:\n * - left: a LIVE preview rendered by `SlidesPlayer`, compiled from the source\n * at runtime via {@link compileDeck} (browser-safe frontmatter split +\n * strip-imports + `@mdx-js/mdx` evaluate, with slide components injected\n * through MDX context).\n * - right: a `<textarea>` editing surface, a registry-driven template palette\n * (click inserts the template's MDX snippet at the end of the source), and a\n * light/dark theme toggle for the preview container.\n *\n * Compilation is debounced and resilient: the LAST good preview is retained on\n * a failed compile and the error is surfaced in a non-blocking banner, so a bad\n * keystroke never blanks or crashes the editor.\n *\n * The editor's own chrome is styled by self-contained plain CSS shipped as a\n * side-effect import — consumers must `import '@atom63/slides/editor/styles'`\n * (the CSS is intentionally NOT bundled into the JS so it isn't orphaned by the\n * build, mirroring how `@atom63/slides` ships `./styles`).\n *\n * A `<textarea>` is intentional\n * for v0.1; CodeMirror (syntax highlight, structured editing) is a future\n * upgrade. Structured per-slide form editing and template-switch-by-form are\n * explicitly v2 — the source textarea is the only editing surface here.\n */\nexport function DeckEditor({ source: sourceProp, onChange, debounceMs = 300 }: DeckEditorProps) {\n const isControlled = onChange !== undefined\n const [internalSource, setInternalSource] = useState(sourceProp)\n const source = isControlled ? sourceProp : internalSource\n\n const [preview, setPreview] = useState<PreviewState>(null)\n const [error, setError] = useState<string | null>(null)\n const [theme, setTheme] = useState<EditorTheme>('light')\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n\n const templates = useMemo(() => listTemplates(), [])\n\n const setSource = useCallback(\n (next: string) => {\n if (isControlled) onChange(next)\n else setInternalSource(next)\n },\n [isControlled, onChange]\n )\n\n // Debounced runtime compile. Keeps the last good preview on error.\n useEffect(() => {\n let cancelled = false\n const handle = setTimeout(async () => {\n const result = await compileDeck(source)\n if (cancelled) return\n if (result.ok) {\n setPreview({ Content: result.Content, meta: result.meta })\n setError(null)\n } else {\n setError(result.error)\n }\n }, debounceMs)\n return () => {\n cancelled = true\n clearTimeout(handle)\n }\n }, [source, debounceMs])\n\n const handleChange = useCallback(\n (e: ChangeEvent<HTMLTextAreaElement>) => setSource(e.target.value),\n [setSource]\n )\n\n const handleInsertTemplate = useCallback(\n (name: string) => {\n const snippet = templateSnippets[name]\n if (!snippet) return\n const next = `${source.replace(/\\s*$/, '')}\\n${snippet}`\n setSource(next)\n // Move caret to the end so the inserted slide is visible.\n requestAnimationFrame(() => {\n const el = textareaRef.current\n if (!el) return\n el.focus()\n el.selectionStart = el.selectionEnd = el.value.length\n el.scrollTop = el.scrollHeight\n })\n },\n [source, setSource]\n )\n\n const deck: SlideDeckItem | null = useMemo(() => {\n if (!preview) return null\n return { slug: 'draft', meta: preview.meta, content: preview.Content }\n }, [preview])\n\n return (\n <div className=\"a63-editor\">\n <section className=\"a63-editor__preview\" data-theme={theme}>\n {error ? (\n <div className=\"a63-editor__error\" role=\"alert\">\n <span className=\"a63-editor__error-tag\">MDX error</span>\n <span className=\"a63-editor__error-msg\">{error}</span>\n </div>\n ) : null}\n {deck ? (\n // Remount on theme change so the player picks up the canvas theme.\n <div className=\"a63-editor__preview-stage\" key={`${theme}`}>\n <SlidesPlayer deck={deck} onBack={() => {}} />\n </div>\n ) : (\n <div className=\"a63-editor__preview-empty\">\n {error ? 'Fix the MDX error to render a preview.' : 'Compiling preview…'}\n </div>\n )}\n </section>\n\n <section className=\"a63-editor__source\">\n <div className=\"a63-editor__toolbar\">\n <span className=\"a63-editor__title\">Deck source · MDX</span>\n <button\n type=\"button\"\n className=\"a63-editor__theme-toggle\"\n onClick={() => setTheme(t => (t === 'light' ? 'dark' : 'light'))}\n aria-pressed={theme === 'dark'}\n >\n {theme === 'light' ? '☀︎ Light' : '☾ Dark'}\n </button>\n </div>\n\n <textarea\n ref={textareaRef}\n className=\"a63-editor__textarea\"\n value={source}\n onChange={handleChange}\n spellCheck={false}\n aria-label=\"Deck MDX source\"\n />\n\n <div className=\"a63-editor__palette\">\n <div className=\"a63-editor__palette-label\">Templates · click to append</div>\n <div className=\"a63-editor__palette-grid\">\n {templates.map(t => (\n <button\n key={t.name}\n type=\"button\"\n className=\"a63-editor__chip\"\n onClick={() => handleInsertTemplate(t.name)}\n title={`Insert ${t.name}`}\n >\n <span className=\"a63-editor__chip-name\">{t.name}</span>\n <span className=\"a63-editor__chip-meta\">{t.label}</span>\n </button>\n ))}\n </div>\n <p className=\"a63-editor__footnote\">\n v0.1 · plain-textarea editing (CodeMirror is a future upgrade). Per-slide form editing\n & template-switch are deferred to v2.\n </p>\n </div>\n </section>\n </div>\n )\n}\n"]}
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* src/components/syllabus/syllabus-print.css */
|
|
2
|
+
.syllabus-prose a {
|
|
3
|
+
cursor: pointer;
|
|
4
|
+
}
|
|
5
|
+
.syllabus-prose [data-syllabus-block=meta] {
|
|
6
|
+
padding: 0;
|
|
7
|
+
border: 0;
|
|
8
|
+
}
|
|
9
|
+
.syllabus-prose [data-syllabus-block=meta] > div {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 8rem 1fr;
|
|
12
|
+
gap: 1rem;
|
|
13
|
+
padding: 0.5rem 0;
|
|
14
|
+
border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);
|
|
15
|
+
}
|
|
16
|
+
.syllabus-prose [data-syllabus-block=meta] > div:last-child {
|
|
17
|
+
border-bottom: 0;
|
|
18
|
+
}
|
|
19
|
+
.syllabus-prose [data-syllabus-block=meta] [data-syllabus-meta-label] {
|
|
20
|
+
font-family: var(--font-mono, ui-monospace, monospace);
|
|
21
|
+
font-size: 0.6875rem;
|
|
22
|
+
color: var(--muted-foreground);
|
|
23
|
+
text-transform: uppercase;
|
|
24
|
+
letter-spacing: 0.1em;
|
|
25
|
+
}
|
|
26
|
+
.syllabus-prose [data-syllabus-block=meta] [data-syllabus-meta-value] {
|
|
27
|
+
font-family: var(--font-serif, ui-serif, Georgia, serif);
|
|
28
|
+
font-size: 0.9375rem;
|
|
29
|
+
line-height: 1.5;
|
|
30
|
+
color: var(--foreground);
|
|
31
|
+
}
|
|
32
|
+
@media print {
|
|
33
|
+
#syllabus-print-clone {
|
|
34
|
+
box-sizing: border-box !important;
|
|
35
|
+
display: block !important;
|
|
36
|
+
width: 100% !important;
|
|
37
|
+
max-width: none !important;
|
|
38
|
+
padding: 0.6in 0.7in !important;
|
|
39
|
+
margin: 0 !important;
|
|
40
|
+
color: black !important;
|
|
41
|
+
background: white !important;
|
|
42
|
+
-webkit-box-decoration-break: clone !important;
|
|
43
|
+
box-decoration-break: clone !important;
|
|
44
|
+
}
|
|
45
|
+
#syllabus-print-clone .syllabus-prose h1,
|
|
46
|
+
#syllabus-print-clone .syllabus-prose h2,
|
|
47
|
+
#syllabus-print-clone .syllabus-prose h3,
|
|
48
|
+
#syllabus-print-clone .syllabus-prose h4,
|
|
49
|
+
#syllabus-print-clone .syllabus-prose h5,
|
|
50
|
+
#syllabus-print-clone .syllabus-prose h6 {
|
|
51
|
+
break-after: avoid;
|
|
52
|
+
page-break-after: avoid;
|
|
53
|
+
}
|
|
54
|
+
#syllabus-print-clone a {
|
|
55
|
+
color: black !important;
|
|
56
|
+
text-decoration-color: #999 !important;
|
|
57
|
+
}
|
|
58
|
+
#syllabus-print-clone > header {
|
|
59
|
+
break-inside: avoid;
|
|
60
|
+
page-break-inside: avoid;
|
|
61
|
+
border-top-color: black !important;
|
|
62
|
+
}
|
|
63
|
+
@page {
|
|
64
|
+
size: letter;
|
|
65
|
+
margin: 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/*# sourceMappingURL=index.css.map */
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/syllabus/syllabus-print.css"],"sourcesContent":["/*\n * Styles for the syllabus reading layout and print pipeline.\n *\n * The slide-primitive CSS reset that previously lived here is no longer needed.\n * Markdown inside <Syllabus> now renders with native HTML elements styled by\n * mdxStyles tokens via SyllabusContentContext — no !important overrides required.\n *\n * Only the meta block (definition-list layout) and print pipeline remain.\n */\n\n/* ──────── Link interactivity ──────── */\n\n/* Hover color is driven by JS (onClonedAnchorEnter/Leave in syllabus-view.tsx)\n * because CSS :hover is suppressed on DOM-cloned nodes inside the presentation view. */\n.syllabus-prose a {\n cursor: pointer;\n}\n\n/* ──────── Meta block: definition-list style ──────── */\n\n.syllabus-prose [data-syllabus-block=\"meta\"] {\n padding: 0;\n border: 0;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] > div {\n display: grid;\n grid-template-columns: 8rem 1fr;\n gap: 1rem;\n padding: 0.5rem 0;\n border-bottom: 1px solid color-mix(in srgb, var(--border) 40%, transparent);\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] > div:last-child {\n border-bottom: 0;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] [data-syllabus-meta-label] {\n font-family: var(--font-mono, ui-monospace, monospace);\n font-size: 0.6875rem;\n color: var(--muted-foreground);\n text-transform: uppercase;\n letter-spacing: 0.1em;\n}\n.syllabus-prose [data-syllabus-block=\"meta\"] [data-syllabus-meta-value] {\n font-family: var(--font-serif, ui-serif, Georgia, serif);\n font-size: 0.9375rem;\n line-height: 1.5;\n color: var(--foreground);\n}\n\n/* ──────── Print styles ──────── */\n\n/* Print pipeline (see syllabus-view.tsx):\n *\n * On `beforeprint` we clone the article into <body id=\"syllabus-print-clone\">\n * and hide every other body child with `display:none !important`. That\n * bypasses the presentation overlay's transform / position-absolute stack which\n * otherwise breaks paginated layout. */\n\n@media print {\n /* The html/body/#root page-level resets are applied as inline styles by the\n syllabus print handler (syllabus-view.tsx beforePrint), only during an\n actual syllabus print. A global rule here would also fire during another\n app's print (e.g. the resume) whenever slides is merely open, so it is\n intentionally omitted to avoid leaking across apps. */\n\n #syllabus-print-clone {\n box-sizing: border-box !important;\n display: block !important;\n width: 100% !important;\n max-width: none !important;\n padding: 0.6in 0.7in !important;\n margin: 0 !important;\n color: black !important;\n background: white !important;\n -webkit-box-decoration-break: clone !important;\n box-decoration-break: clone !important;\n }\n\n #syllabus-print-clone .syllabus-prose h1,\n #syllabus-print-clone .syllabus-prose h2,\n #syllabus-print-clone .syllabus-prose h3,\n #syllabus-print-clone .syllabus-prose h4,\n #syllabus-print-clone .syllabus-prose h5,\n #syllabus-print-clone .syllabus-prose h6 {\n break-after: avoid;\n page-break-after: avoid;\n }\n\n #syllabus-print-clone a {\n color: black !important;\n text-decoration-color: #999 !important;\n }\n\n #syllabus-print-clone > header {\n break-inside: avoid;\n page-break-inside: avoid;\n border-top-color: black !important;\n }\n\n @page {\n size: letter;\n margin: 0;\n }\n}\n"],"mappings":";AAcA,CAAC,eAAe;AACd,UAAQ;AACV;AAIA,CANC,eAMe,CAAC;AACf,WAAS;AACT,UAAQ;AACV;AACA,CAVC,eAUe,CAAC,0BAA4B,EAAE;AAC7C,WAAS;AACT,yBAAuB,KAAK;AAC5B,OAAK;AACL,WAAS,OAAO;AAChB,iBAAe,IAAI,MAAM,UAAU,GAAG,IAAI,EAAE,IAAI,UAAU,GAAG,EAAE;AACjE;AACA,CAjBC,eAiBe,CAAC,0BAA4B,EAAE,GAAG;AAChD,iBAAe;AACjB;AACA,CApBC,eAoBe,CAAC,0BAA4B,CAAC;AAC5C,eAAa,IAAI,WAAW,EAAE,YAAY,EAAE;AAC5C,aAAW;AACX,SAAO,IAAI;AACX,kBAAgB;AAChB,kBAAgB;AAClB;AACA,CA3BC,eA2Be,CAAC,0BAA4B,CAAC;AAC5C,eAAa,IAAI,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE;AAClD,aAAW;AACX,eAAa;AACb,SAAO,IAAI;AACb;AAWA,OAAO;AAOL,GAAC;AACC,gBAAY;AACZ,aAAS;AACT,WAAO;AACP,eAAW;AACX,aAAS,MAAM;AACf,YAAQ;AACR,WAAO;AACP,gBAAY;AACZ,kCAA8B;AAC9B,0BAAsB;AACxB;AAEA,GAbC,qBAaqB,CA/DvB,eA+DuC;AAAA,EACtC,CAdC,qBAcqB,CAhEvB,eAgEuC;AAAA,EACtC,CAfC,qBAeqB,CAjEvB,eAiEuC;AAAA,EACtC,CAhBC,qBAgBqB,CAlEvB,eAkEuC;AAAA,EACtC,CAjBC,qBAiBqB,CAnEvB,eAmEuC;AAAA,EACtC,CAlBC,qBAkBqB,CApEvB,eAoEuC;AACpC,iBAAa;AACb,sBAAkB;AACpB;AAEA,GAvBC,qBAuBqB;AACpB,WAAO;AACP,2BAAuB;AACzB;AAEA,GA5BC,qBA4BqB,EAAE;AACtB,kBAAc;AACd,uBAAmB;AACnB,sBAAkB;AACpB;AAEA;AACE,UAAM;AACN,YAAQ;AACV;AACF;","names":[]}
|