mdeck 2.3.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/CHANGELOG.md +259 -0
- package/LICENSE +21 -0
- package/README.md +156 -0
- package/assets/base.css +196 -0
- package/assets/extensions/layouts/chapter/extension.toml +26 -0
- package/assets/extensions/layouts/chapter/layout.jsx +21 -0
- package/assets/extensions/layouts/chapter/starter.md +8 -0
- package/assets/extensions/layouts/focus/extension.toml +8 -0
- package/assets/extensions/layouts/focus/layout.jsx +10 -0
- package/assets/extensions/layouts/focus/starter.md +4 -0
- package/assets/extensions/layouts/full-bleed-image/extension.toml +31 -0
- package/assets/extensions/layouts/full-bleed-image/layout.jsx +19 -0
- package/assets/extensions/layouts/full-bleed-image/starter.md +6 -0
- package/assets/extensions/layouts/generic/extension.toml +8 -0
- package/assets/extensions/layouts/generic/layout.jsx +8 -0
- package/assets/extensions/layouts/generic/starter.md +4 -0
- package/assets/extensions/layouts/image-text/extension.toml +25 -0
- package/assets/extensions/layouts/image-text/layout.jsx +17 -0
- package/assets/extensions/layouts/image-text/starter.md +6 -0
- package/assets/extensions/layouts/split/extension.toml +25 -0
- package/assets/extensions/layouts/split/layout.jsx +19 -0
- package/assets/extensions/layouts/split/starter.md +14 -0
- package/assets/extensions/layouts/title/extension.toml +26 -0
- package/assets/extensions/layouts/title/layout.jsx +14 -0
- package/assets/extensions/layouts/title/starter.md +5 -0
- package/assets/extensions/palettes/brand/extension.toml +29 -0
- package/assets/extensions/palettes/cobalt/extension.toml +27 -0
- package/assets/extensions/palettes/ember/extension.toml +27 -0
- package/assets/extensions/palettes/forest/extension.toml +27 -0
- package/assets/extensions/palettes/graphite/extension.toml +27 -0
- package/assets/extensions/palettes/lagoon/extension.toml +27 -0
- package/assets/extensions/palettes/neon/extension.toml +27 -0
- package/assets/extensions/palettes/nordic/extension.toml +27 -0
- package/assets/extensions/palettes/pastel/extension.toml +30 -0
- package/assets/extensions/palettes/phosphor/extension.toml +27 -0
- package/assets/extensions/palettes/swiss/extension.toml +27 -0
- package/assets/extensions/palettes/terra/extension.toml +27 -0
- package/assets/extensions/themes/academic/extension.toml +53 -0
- package/assets/extensions/themes/academic/styles.css +631 -0
- package/assets/extensions/themes/aurora/extension.toml +37 -0
- package/assets/extensions/themes/aurora/styles.css +644 -0
- package/assets/extensions/themes/duet/extension.toml +46 -0
- package/assets/extensions/themes/duet/styles.css +573 -0
- package/assets/extensions/themes/editorial/extension.toml +46 -0
- package/assets/extensions/themes/editorial/styles.css +574 -0
- package/assets/extensions/themes/fhnw/extension.toml +61 -0
- package/assets/extensions/themes/fhnw/styles.css +604 -0
- package/assets/extensions/themes/neue/extension.toml +46 -0
- package/assets/extensions/themes/neue/styles.css +489 -0
- package/assets/extensions/themes/sketch/extension.toml +48 -0
- package/assets/extensions/themes/sketch/styles.css +633 -0
- package/assets/extensions/themes/terminal/extension.toml +38 -0
- package/assets/extensions/themes/terminal/styles.css +550 -0
- package/assets/logo/avatar.png +0 -0
- package/assets/logo/logo-dark.svg +10 -0
- package/assets/logo/logo-light.svg +10 -0
- package/assets/logo/mark.svg +15 -0
- package/bin/mdeck.js +2 -0
- package/docs/README.md +8 -0
- package/docs/reference/authoring.md +795 -0
- package/docs/reference/claude-skill.md +96 -0
- package/docs/reference/extensions.md +250 -0
- package/docs/reference/layouts.md +153 -0
- package/docs/reference/palettes.md +106 -0
- package/docs/reference/structured-slides.md +120 -0
- package/docs/reference/themes.md +133 -0
- package/docs/site/assets/favicon.svg +15 -0
- package/docs/site/assets/github-logo.svg +1 -0
- package/docs/site/assets/site.css +162 -0
- package/docs/site/assets/site.js +123 -0
- package/docs/site/build.js +148 -0
- package/docs/site/content/appearance.md +203 -0
- package/docs/site/content/audience.md +178 -0
- package/docs/site/content/commands.md +153 -0
- package/docs/site/content/components.md +134 -0
- package/docs/site/content/drawing.md +72 -0
- package/docs/site/content/editing.md +59 -0
- package/docs/site/content/getting-started.md +103 -0
- package/docs/site/content/layouts.md +126 -0
- package/docs/site/content/media.md +107 -0
- package/docs/site/content/more-content.md +130 -0
- package/docs/site/content/presenting.md +93 -0
- package/docs/site/content/reusable-layouts.md +74 -0
- package/docs/site/content/sharing.md +88 -0
- package/docs/site/content/theme-authoring.md +144 -0
- package/docs/site/content/theme-repository.md +76 -0
- package/docs/site/content/troubleshooting.md +92 -0
- package/docs/site/content/views.md +56 -0
- package/docs/site/content/welcome.md +71 -0
- package/docs/site/content/writing.md +121 -0
- package/docs/site/examples/first-talk.md +88 -0
- package/docs/site/examples/notebook.svg +58 -0
- package/docs/site/images/themes/academic-dark.webp +0 -0
- package/docs/site/images/themes/academic-light.webp +0 -0
- package/docs/site/images/themes/academic.webp +0 -0
- package/docs/site/images/themes/aurora-dark.webp +0 -0
- package/docs/site/images/themes/aurora-light.webp +0 -0
- package/docs/site/images/themes/aurora.webp +0 -0
- package/docs/site/images/themes/duet-dark.webp +0 -0
- package/docs/site/images/themes/duet-light.webp +0 -0
- package/docs/site/images/themes/duet.webp +0 -0
- package/docs/site/images/themes/editorial-dark.webp +0 -0
- package/docs/site/images/themes/editorial-light.webp +0 -0
- package/docs/site/images/themes/editorial.webp +0 -0
- package/docs/site/images/themes/fhnw.webp +0 -0
- package/docs/site/images/themes/neue-dark.webp +0 -0
- package/docs/site/images/themes/neue-light.webp +0 -0
- package/docs/site/images/themes/neue.webp +0 -0
- package/docs/site/images/themes/sketch-dark.webp +0 -0
- package/docs/site/images/themes/sketch-light.webp +0 -0
- package/docs/site/images/themes/sketch.webp +0 -0
- package/docs/site/images/themes/terminal-dark.webp +0 -0
- package/docs/site/images/themes/terminal-light.webp +0 -0
- package/docs/site/images/themes/terminal.webp +0 -0
- package/docs/site/package.json +9 -0
- package/docs/site/pages.js +27 -0
- package/docs/site/server.js +58 -0
- package/examples/custom-layouts/extensions/comparison/extension.toml +35 -0
- package/examples/custom-layouts/extensions/comparison/layout.jsx +15 -0
- package/examples/custom-layouts/extensions/comparison/starter.md +17 -0
- package/examples/custom-layouts/extensions/comparison/styles.css +4 -0
- package/examples/custom-layouts/slides.md +60 -0
- package/examples/poll/slides.md +127 -0
- package/examples/python/.mdeck-backups/slides.drawings-2026-10-06T08-36-47Z.json +19 -0
- package/examples/python/.mdeck-backups/slides.drawings-2026-10-06T11-27-30Z.json +6 -0
- package/examples/python/slides.drawings.json +6 -0
- package/examples/python/slides.md +493 -0
- package/examples/showcase/.mdeck-backups/slides.drawings-2026-10-06T08-08-30Z.json +10 -0
- package/examples/showcase/.mdeck-backups/slides.drawings-2026-10-06T11-41-35Z.json +13 -0
- package/examples/showcase/img/image.jpg +0 -0
- package/examples/showcase/img/mdeck.svg +66 -0
- package/examples/showcase/slides.drawings.json +11 -0
- package/examples/showcase/slides.md +427 -0
- package/package.json +75 -0
- package/skills/write-slides/SKILL.md +144 -0
- package/src/build/check.js +49 -0
- package/src/build/chrome.js +130 -0
- package/src/build/components.js +44 -0
- package/src/build/config.js +61 -0
- package/src/build/drawings.js +14 -0
- package/src/build/editorPlugin.js +272 -0
- package/src/build/editorServer.js +36 -0
- package/src/build/fonts.js +84 -0
- package/src/build/homePlugin.js +208 -0
- package/src/build/inkPlugin.js +192 -0
- package/src/build/ownWrites.js +18 -0
- package/src/build/pairing.js +81 -0
- package/src/build/pdf.js +34 -0
- package/src/build/relayAccess.js +85 -0
- package/src/build/slidesPlugin.js +371 -0
- package/src/build/themeCheck.js +151 -0
- package/src/build/tunnelClient.js +184 -0
- package/src/cli/args.js +76 -0
- package/src/cli/index.js +758 -0
- package/src/cli/migrate.js +123 -0
- package/src/cli/skill.js +40 -0
- package/src/cli/themes.js +140 -0
- package/src/components/CodeBlock.jsx +168 -0
- package/src/components/ConnectedViews.jsx +25 -0
- package/src/components/Icon.jsx +8 -0
- package/src/components/Poll.jsx +67 -0
- package/src/components/QrCode.jsx +29 -0
- package/src/components/QrCodeTag.jsx +27 -0
- package/src/components/Question.jsx +28 -0
- package/src/components/Scale.jsx +47 -0
- package/src/components/VideoPlayer.jsx +108 -0
- package/src/components/WordCloud.jsx +106 -0
- package/src/components/activity.jsx +35 -0
- package/src/components/callout.css +78 -0
- package/src/components/code-block.css +123 -0
- package/src/components/columns.css +11 -0
- package/src/components/poll.css +81 -0
- package/src/components/video-player.css +14 -0
- package/src/core/devPath.js +4 -0
- package/src/core/editDeck.js +230 -0
- package/src/core/icons.js +69 -0
- package/src/core/ink.js +221 -0
- package/src/core/labels.js +144 -0
- package/src/core/outline.js +33 -0
- package/src/core/parseSlides.js +152 -0
- package/src/core/source.js +137 -0
- package/src/core/urls.js +34 -0
- package/src/core/validateDeck.js +139 -0
- package/src/editor/api.js +67 -0
- package/src/editor/components/App.jsx +75 -0
- package/src/editor/components/ConflictBanner.jsx +10 -0
- package/src/editor/components/DeckSettings.jsx +108 -0
- package/src/editor/components/DesignApp.jsx +252 -0
- package/src/editor/components/Diagnostics.jsx +10 -0
- package/src/editor/components/ExtensionEditor.jsx +66 -0
- package/src/editor/components/ExtensionForms.jsx +134 -0
- package/src/editor/components/ExtensionsPanel.jsx +114 -0
- package/src/editor/components/Field.jsx +24 -0
- package/src/editor/components/Inspector.jsx +36 -0
- package/src/editor/components/LayoutPicker.jsx +23 -0
- package/src/editor/components/LayoutSelect.jsx +25 -0
- package/src/editor/components/LookThumbnail.jsx +8 -0
- package/src/editor/components/MetaFields.jsx +24 -0
- package/src/editor/components/Outline.jsx +28 -0
- package/src/editor/components/PalettePicker.jsx +32 -0
- package/src/editor/components/Preview.jsx +63 -0
- package/src/editor/components/PropsForm.jsx +46 -0
- package/src/editor/components/RawSlideEditor.jsx +9 -0
- package/src/editor/components/RegionEditor.jsx +15 -0
- package/src/editor/components/Resizer.jsx +18 -0
- package/src/editor/designLink.js +19 -0
- package/src/editor/editor.css +139 -0
- package/src/editor/extensions.js +86 -0
- package/src/editor/keyboard.js +21 -0
- package/src/editor/model.js +38 -0
- package/src/editor/sampleDeck.js +165 -0
- package/src/editor/saveQueue.js +47 -0
- package/src/editor/schema.js +36 -0
- package/src/editor/state.js +92 -0
- package/src/editor/useDeckDocument.js +67 -0
- package/src/extensions/appearance.js +79 -0
- package/src/extensions/contrast.js +57 -0
- package/src/extensions/discover.js +126 -0
- package/src/extensions/manifest.js +209 -0
- package/src/extensions/packs.js +268 -0
- package/src/extensions/tokens.js +25 -0
- package/src/home/Home.jsx +362 -0
- package/src/home/home.css +148 -0
- package/src/layouts/layoutApi.jsx +205 -0
- package/src/layouts/layoutProps.js +30 -0
- package/src/layouts/renderSlide.jsx +17 -0
- package/src/live/answer/answer.css +34 -0
- package/src/live/answer/answer.html +14 -0
- package/src/live/answer/answer.js +130 -0
- package/src/live/client.js +290 -0
- package/src/live/code.js +19 -0
- package/src/live/follow.js +17 -0
- package/src/live/pairing.js +34 -0
- package/src/live/presence.js +33 -0
- package/src/live/roomTag.js +38 -0
- package/src/live/rooms.js +249 -0
- package/src/live/server.js +82 -0
- package/src/live/tunnel.js +180 -0
- package/src/paths.js +9 -0
- package/src/runtime/ReaderView.jsx +193 -0
- package/src/runtime/SlideErrorBoundary.jsx +28 -0
- package/src/runtime/chrome.jsx +55 -0
- package/src/runtime/deck-stage.js +1533 -0
- package/src/runtime/design.html +12 -0
- package/src/runtime/design.jsx +5 -0
- package/src/runtime/editor.html +12 -0
- package/src/runtime/editor.jsx +5 -0
- package/src/runtime/editorBridge.js +58 -0
- package/src/runtime/fit.js +149 -0
- package/src/runtime/home.html +13 -0
- package/src/runtime/home.jsx +5 -0
- package/src/runtime/index.html +15 -0
- package/src/runtime/ink/InkLayer.jsx +17 -0
- package/src/runtime/ink/Toolbar.jsx +145 -0
- package/src/runtime/ink/attach.js +69 -0
- package/src/runtime/ink/bus.js +126 -0
- package/src/runtime/ink/controller.js +132 -0
- package/src/runtime/ink/room.js +101 -0
- package/src/runtime/ink/select.js +124 -0
- package/src/runtime/ink/server.js +75 -0
- package/src/runtime/ink/store.js +55 -0
- package/src/runtime/ink/toolbar.css +79 -0
- package/src/runtime/main.jsx +863 -0
- package/src/runtime/markedSetup.js +147 -0
- package/src/runtime/reader.css +59 -0
- package/src/runtime/registry.jsx +26 -0
- package/src/runtime/syncOrder.js +32 -0
- package/src/runtime/themeLoader.js +68 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { marked } from 'marked'
|
|
2
|
+
import markedKatex from 'marked-katex-extension'
|
|
3
|
+
import 'katex/dist/katex.min.css'
|
|
4
|
+
import '../components/callout.css'
|
|
5
|
+
import '../components/columns.css'
|
|
6
|
+
import { scanDirectives, sourceLines, fenceState } from '../core/source.js'
|
|
7
|
+
import { CALLOUT_LABELS, labelSetFor } from '../core/labels.js'
|
|
8
|
+
|
|
9
|
+
function readDirective(src, name) {
|
|
10
|
+
if (!/^:::\s*[\w-]+/.test(src)) return null
|
|
11
|
+
const node = scanDirectives(src).nodes[0]
|
|
12
|
+
if (!node || node.end == null || (name && node.name !== name)) return null
|
|
13
|
+
return { ...node, raw: src.slice(0, node.end), body: src.slice(node.bodyStart, node.bodyEnd) }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function splitColumns(src) {
|
|
17
|
+
const columns = []
|
|
18
|
+
let start = 0, depth = 0, fence = null
|
|
19
|
+
for (const line of sourceLines(src)) {
|
|
20
|
+
const before = fence
|
|
21
|
+
fence = fenceState(line.text, fence)
|
|
22
|
+
if (before || fence) continue
|
|
23
|
+
if (/^:::\s*[\w-]+/.test(line.text)) depth++
|
|
24
|
+
else if (/^:::\s*$/.test(line.text)) depth--
|
|
25
|
+
if (!depth && /^\+\+\+[ \t]*$/.test(line.text)) {
|
|
26
|
+
columns.push(src.slice(start, line.start))
|
|
27
|
+
start = line.end
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
columns.push(src.slice(start))
|
|
31
|
+
return columns
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function escapeHtml(text) {
|
|
35
|
+
return String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
marked.use(markedKatex({ throwOnError: false, output: 'html' }))
|
|
39
|
+
|
|
40
|
+
// marked.use() prepends via unshift, so last-registered = highest priority.
|
|
41
|
+
// Register callout first (lowest priority) so columns and steps match before it.
|
|
42
|
+
|
|
43
|
+
let calloutLabels = CALLOUT_LABELS.en
|
|
44
|
+
|
|
45
|
+
// Ein Deck waehlt mit `lang` einen Satz Standardbeschriftungen und kann mit
|
|
46
|
+
// `callouts` einzelne davon ueberschreiben. Ohne Angabe bleibt es bei
|
|
47
|
+
// Englisch, damit bestehende Decks unveraendert aussehen. Ein Titel hinter
|
|
48
|
+
// dem Typ (`::: tip Eigener Titel`) sticht beides.
|
|
49
|
+
export function setCalloutLabels({ lang, callouts } = {}) {
|
|
50
|
+
// A regional code such as de-CH uses its language's titles.
|
|
51
|
+
const base = CALLOUT_LABELS[labelSetFor(lang)]
|
|
52
|
+
calloutLabels = { ...base, ...(callouts ?? {}) }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
marked.use({
|
|
56
|
+
extensions: [{
|
|
57
|
+
name: 'callout',
|
|
58
|
+
level: 'block',
|
|
59
|
+
start(src) { return src.match(/^:::/m)?.index },
|
|
60
|
+
tokenizer(src) {
|
|
61
|
+
const m = readDirective(src)
|
|
62
|
+
if (!m) return
|
|
63
|
+
const token = { type: 'callout', raw: m.raw, calloutType: m.name.toLowerCase(), title: m.argument, tokens: [] }
|
|
64
|
+
this.lexer.blockTokens(m.body, token.tokens)
|
|
65
|
+
return token
|
|
66
|
+
},
|
|
67
|
+
renderer(token) {
|
|
68
|
+
const title = token.title || calloutLabels[token.calloutType] || token.calloutType
|
|
69
|
+
return `<div class="callout callout-${token.calloutType}"><div class="callout-title">${escapeHtml(title)}</div>${this.parser.parse(token.tokens)}</div>\n`
|
|
70
|
+
},
|
|
71
|
+
}],
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
marked.use({
|
|
75
|
+
extensions: [{
|
|
76
|
+
name: 'columns',
|
|
77
|
+
level: 'block',
|
|
78
|
+
start(src) { return src.match(/^:::columns\b/m)?.index },
|
|
79
|
+
tokenizer(src) {
|
|
80
|
+
const m = readDirective(src, 'columns')
|
|
81
|
+
if (!m) return
|
|
82
|
+
const colSrcs = splitColumns(m.body)
|
|
83
|
+
const token = { type: 'columns', raw: m.raw, columns: [] }
|
|
84
|
+
for (const colSrc of colSrcs) {
|
|
85
|
+
const colTokens = []
|
|
86
|
+
this.lexer.blockTokens(colSrc.trim(), colTokens)
|
|
87
|
+
token.columns.push(colTokens)
|
|
88
|
+
}
|
|
89
|
+
return token
|
|
90
|
+
},
|
|
91
|
+
renderer(token) {
|
|
92
|
+
const cols = token.columns
|
|
93
|
+
.map(colTokens => `<div class="column">${this.parser.parse(colTokens)}</div>`)
|
|
94
|
+
.join('\n')
|
|
95
|
+
return `<div class="columns">\n${cols}\n</div>\n`
|
|
96
|
+
},
|
|
97
|
+
}],
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
marked.use({
|
|
101
|
+
extensions: [{
|
|
102
|
+
name: 'steps',
|
|
103
|
+
level: 'block',
|
|
104
|
+
start(src) { return src.match(/^:::steps\b/m)?.index },
|
|
105
|
+
tokenizer(src) {
|
|
106
|
+
const m = readDirective(src, 'steps')
|
|
107
|
+
if (!m) return
|
|
108
|
+
const token = { type: 'steps', raw: m.raw, tokens: [] }
|
|
109
|
+
this.lexer.blockTokens(m.body, token.tokens)
|
|
110
|
+
return token
|
|
111
|
+
},
|
|
112
|
+
renderer(token) {
|
|
113
|
+
let counter = 0
|
|
114
|
+
const links = token.tokens.links || {}
|
|
115
|
+
const parts = []
|
|
116
|
+
for (const t of token.tokens) {
|
|
117
|
+
if (t.type === 'space') continue
|
|
118
|
+
if (t.type === 'list') {
|
|
119
|
+
const tag = t.ordered ? 'ol' : 'ul'
|
|
120
|
+
const items = t.items.map(item => {
|
|
121
|
+
const body = this.parser.parse(Object.assign([...item.tokens], { links }))
|
|
122
|
+
return `<li data-step="${counter++}">${body}</li>`
|
|
123
|
+
})
|
|
124
|
+
parts.push(`<${tag}>${items.join('')}</${tag}>`)
|
|
125
|
+
} else {
|
|
126
|
+
const html = this.parser.parse(Object.assign([t], { links }))
|
|
127
|
+
parts.push(`<div data-step="${counter++}">${html}</div>`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return `<div class="steps-container">\n${parts.join('\n')}\n</div>\n`
|
|
131
|
+
},
|
|
132
|
+
}],
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
marked.use({
|
|
136
|
+
renderer: {
|
|
137
|
+
code({ text: code, lang: infoString }) {
|
|
138
|
+
const [lang = 'text', ...flags] = (infoString || '').split(/\s+/).filter(Boolean)
|
|
139
|
+
const attrs = flags.map(f => `${f}=""`).join(' ')
|
|
140
|
+
const encoded = (code || '')
|
|
141
|
+
.replace(/&/g, '&')
|
|
142
|
+
.replace(/</g, '<')
|
|
143
|
+
.replace(/>/g, '>')
|
|
144
|
+
return `<codeblock lang="${lang}"${attrs ? ' ' + attrs : ''}>${encoded}</codeblock>`
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
.reader-view { position: fixed; inset: 0; display: grid; grid-template-rows: 48px 1fr; background: #101010; color: #e6e6e6; font: 14px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif; }
|
|
2
|
+
.reader-view * { box-sizing: border-box; }
|
|
3
|
+
/* Theme stylesheets style the document; the chrome must not inherit them. */
|
|
4
|
+
.reader-top, .reader-nav, .reader-bottom, .reader-toast, .reader-page-caption, .reader-page-notes,
|
|
5
|
+
.reader-top *, .reader-nav *, .reader-bottom *, .reader-toast *, .reader-page-caption * { font-family: system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 14px; font-weight: 400; line-height: 1.5; letter-spacing: normal; text-transform: none; color: #e6e6e6; margin: 0; padding: 0; background: none; border: 0; }
|
|
6
|
+
.reader-top { display: flex; align-items: center; gap: 10px; padding: 0 14px !important; border-bottom: 1px solid #262626; background: #171717; min-width: 0; }
|
|
7
|
+
.reader-top h1 { font-size: 15px; font-weight: 600; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: #fff; }
|
|
8
|
+
.reader-top .reader-meta { color: #a8a8a8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
9
|
+
.reader-top .reader-spacer { flex: 1; }
|
|
10
|
+
.reader-top .reader-count { color: #b8b8b8; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
11
|
+
.reader-btn { appearance: none; display: inline-flex; align-items: center; gap: 6px; padding: 6px 11px !important; border-radius: 6px; border: 1px solid #3a3a3a !important; background: #222 !important; color: #e6e6e6; cursor: pointer; font: inherit; text-decoration: none; white-space: nowrap; }
|
|
12
|
+
.reader-btn:hover { border-color: #5a5a5a; color: #fff; }
|
|
13
|
+
.reader-btn:disabled { opacity: 0.45; cursor: default; }
|
|
14
|
+
.reader-btn:disabled:hover { border-color: #3a3a3a; color: #e6e6e6; }
|
|
15
|
+
.reader-icon { flex: 0 0 auto; opacity: 0.85; }
|
|
16
|
+
.reader-btn.is-active .reader-icon { opacity: 1; }
|
|
17
|
+
.reader-btn.is-active { background: #2a2a2a !important; border-color: #6a6a6a !important; }
|
|
18
|
+
.reader-main { display: grid; grid-template-columns: 260px minmax(0, 1fr); min-height: 0; }
|
|
19
|
+
.reader-nav { overflow: auto; border-right: 1px solid #262626 !important; padding: 8px !important; }
|
|
20
|
+
.reader-nav ol { list-style: none; margin: 0; padding: 0; }
|
|
21
|
+
.reader-nav li { display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 8px; padding: 7px 8px !important; border-radius: 6px; cursor: pointer; color: #d6d6d6; align-items: baseline; }
|
|
22
|
+
.reader-nav li:hover { background: #1e1e1e; }
|
|
23
|
+
.reader-nav li.is-current { background: #23384f; color: #fff; }
|
|
24
|
+
.reader-nav li.is-chapter { margin-top: 8px; color: #fff; font-weight: 600; }
|
|
25
|
+
.reader-nav li .reader-num { color: #8a8a8a; font-variant-numeric: tabular-nums; font-size: 12px; }
|
|
26
|
+
.reader-nav li .reader-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
27
|
+
.reader-nav li .reader-part { display: block; font-size: 11px; font-weight: 400; color: #9a9a9a; text-transform: uppercase; letter-spacing: 0.06em; }
|
|
28
|
+
.reader-content { display: flex; flex-direction: column; min-height: 0; min-width: 0; background: #0b0b0b; }
|
|
29
|
+
.reader-stage-wrap { flex: 1; position: relative; min-height: 0; }
|
|
30
|
+
.reader-stage-wrap deck-stage { position: absolute !important; inset: 0 !important; width: 100%; height: 100%; }
|
|
31
|
+
.reader-bottom { display: flex; align-items: center; gap: 8px; padding: 8px 14px !important; border-top: 1px solid #262626; background: #171717; }
|
|
32
|
+
.reader-bottom .reader-spacer { flex: 1; }
|
|
33
|
+
.reader-read { flex: 1; overflow: auto; padding: 24px; display: flex; flex-direction: column; gap: 28px; align-items: center; }
|
|
34
|
+
.reader-page { width: min(100%, 1400px); }
|
|
35
|
+
.reader-page-frame { position: relative; width: 100%; overflow: hidden; box-shadow: 0 6px 30px rgba(0,0,0,0.5); border-radius: 4px; background: #000; }
|
|
36
|
+
.reader-page-scale { position: absolute; top: 0; left: 0; transform-origin: top left; }
|
|
37
|
+
.reader-page-scale > section.slide { width: 100%; height: 100%; }
|
|
38
|
+
.reader-read [data-step] { opacity: 1 !important; }
|
|
39
|
+
.reader-page-notes { margin: 10px 4px 0 !important; padding: 12px 16px !important; border-left: 3px solid #3d6094 !important; background: #161a20 !important; color: #d0d8e4; border-radius: 0 6px 6px 0; }
|
|
40
|
+
.reader-page-notes p { margin: 0 0 6px; }
|
|
41
|
+
.reader-page-caption { margin: 6px 4px 0; color: #9a9a9a; font-size: 12px; display: flex; gap: 8px; }
|
|
42
|
+
.reader-page-caption b { color: #d0d0d0; font-weight: 500; }
|
|
43
|
+
.reader-toast { position: fixed; bottom: 60px; left: 50%; transform: translateX(-50%); background: #27436a !important; color: #fff; padding: 8px 14px !important; border-radius: 6px; }
|
|
44
|
+
.reader-nav-toggle { display: none; }
|
|
45
|
+
@media (max-width: 820px) {
|
|
46
|
+
.reader-main { grid-template-columns: 1fr; }
|
|
47
|
+
.reader-nav { display: none; position: fixed; top: 48px; bottom: 0; left: 0; width: min(85vw, 320px); background: #141414; z-index: 4; box-shadow: 0 0 40px rgba(0,0,0,0.7); }
|
|
48
|
+
.reader-nav.is-open { display: block; }
|
|
49
|
+
.reader-nav-toggle { display: inline-flex; }
|
|
50
|
+
.reader-top .reader-meta, .reader-top .label-text { display: none; }
|
|
51
|
+
.reader-read { padding: 10px; }
|
|
52
|
+
}
|
|
53
|
+
@media print {
|
|
54
|
+
.reader-top, .reader-nav, .reader-bottom, .reader-toast { display: none !important; }
|
|
55
|
+
.reader-view, .reader-main, .reader-content, .reader-stage-wrap { position: static !important; display: block !important; background: none !important; }
|
|
56
|
+
}
|
|
57
|
+
.reader-entry { position: fixed; left: 14px; bottom: 14px; z-index: 50; padding: 6px 10px; border-radius: 6px; background: rgba(20,20,20,0.7); color: #fff; font: 13px system-ui, sans-serif; text-decoration: none; opacity: 0.25; transition: opacity 200ms; }
|
|
58
|
+
.reader-entry:hover { opacity: 1; }
|
|
59
|
+
@media print { .reader-entry { display: none !important; } }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import CodeBlock from '../components/CodeBlock.jsx'
|
|
2
|
+
import QrCodeTag from '../components/QrCodeTag.jsx'
|
|
3
|
+
import VideoPlayer from '../components/VideoPlayer.jsx'
|
|
4
|
+
import Poll from '../components/Poll.jsx'
|
|
5
|
+
import Question from '../components/Question.jsx'
|
|
6
|
+
import WordCloud from '../components/WordCloud.jsx'
|
|
7
|
+
import Scale from '../components/Scale.jsx'
|
|
8
|
+
import deckComponents from 'virtual:deck-components'
|
|
9
|
+
|
|
10
|
+
// Maps lowercase HTML tag names to Preact components.
|
|
11
|
+
// Add framework-wide components here and use them in slides.md as <tagname ...>.
|
|
12
|
+
//
|
|
13
|
+
// A deck can also ship its own components in a `components/` folder next to its
|
|
14
|
+
// .md file — those are picked up automatically and merged in below, so a widget
|
|
15
|
+
// that only one deck needs never becomes part of every other deck's bundle.
|
|
16
|
+
// Deck-local components win on a name collision.
|
|
17
|
+
export const registry = {
|
|
18
|
+
codeblock: CodeBlock,
|
|
19
|
+
qrcode: QrCodeTag,
|
|
20
|
+
videoplayer: VideoPlayer,
|
|
21
|
+
poll: Poll,
|
|
22
|
+
question: Question,
|
|
23
|
+
wordcloud: WordCloud,
|
|
24
|
+
scale: Scale,
|
|
25
|
+
...deckComponents,
|
|
26
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Orders the positions windows of one talk send each other (presenter view
|
|
2
|
+
// ↔ audience windows). Messages can arrive late, after the receiver has
|
|
3
|
+
// already moved on; a position that is older than the receiver's own last
|
|
4
|
+
// change must not take it back. Every change carries a counter (a Lamport
|
|
5
|
+
// clock): a window ignores what is older than what it has. Two changes made
|
|
6
|
+
// at the same moment on both sides carry the same counter; then the window
|
|
7
|
+
// with the larger id wins on both sides, so they end up on the same slide.
|
|
8
|
+
const randomId = () => Array.from(crypto.getRandomValues(new Uint8Array(8)), b => b.toString(16).padStart(2, '0')).join('')
|
|
9
|
+
|
|
10
|
+
export function createOrder(id = randomId()) {
|
|
11
|
+
let latest = { seq: 0, from: id }
|
|
12
|
+
return {
|
|
13
|
+
id,
|
|
14
|
+
/** The stamp for a change made in this window. */
|
|
15
|
+
stamp() { latest = { seq: latest.seq + 1, from: id }; return { ...latest } },
|
|
16
|
+
/** This window's position as it is, for a window that just opened: taken unless it moved on. */
|
|
17
|
+
current() { return { ...latest, initial: true } },
|
|
18
|
+
/** Whether a position from another window is newer than this one's; it is then this window's too. */
|
|
19
|
+
accept(stamp) {
|
|
20
|
+
// A sender without stamps (an older page) is followed as before.
|
|
21
|
+
if (!Number.isInteger(stamp?.seq)) return true
|
|
22
|
+
if (stamp.seq < latest.seq) return false
|
|
23
|
+
// Before anyone has navigated, a new window takes the presenter's
|
|
24
|
+
// initial position regardless of its own id. Once navigation starts,
|
|
25
|
+
// replies and forwarded positions obey the same ordering as changes.
|
|
26
|
+
const opening = latest.seq === 0 && stamp.initial
|
|
27
|
+
if (stamp.seq === latest.seq && !opening && !(String(stamp.from) > latest.from)) return false
|
|
28
|
+
latest = { seq: stamp.seq, from: String(stamp.from) }
|
|
29
|
+
return true
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import baseCSS from '../../assets/base.css?inline'
|
|
2
|
+
import { themes, palettes } from 'virtual:mdeck-extensions'
|
|
3
|
+
import { buildAppearance } from '../extensions/appearance.js'
|
|
4
|
+
|
|
5
|
+
const SELF_CONTAINED = typeof __MDECK_SELF_CONTAINED__ !== 'undefined'
|
|
6
|
+
&& __MDECK_SELF_CONTAINED__
|
|
7
|
+
|
|
8
|
+
export const THEME_NAMES = Object.keys(themes)
|
|
9
|
+
export const THEME_METAS = Object.fromEntries(Object.entries(themes).map(([id, theme]) => [id, theme.manifest]))
|
|
10
|
+
export const PALETTES = Object.fromEntries(Object.entries(palettes).map(([id, palette]) => [id, palette.manifest]))
|
|
11
|
+
|
|
12
|
+
// The editor previews unsaved themes and palettes by handing their manifests
|
|
13
|
+
// (and, for themes, stylesheet text) to the runtime instead of the built
|
|
14
|
+
// module. Overrides win over registered extensions of the same id.
|
|
15
|
+
let overrides = { themes: {}, palettes: {} }
|
|
16
|
+
export function setExtensionOverrides(next = {}) {
|
|
17
|
+
overrides = { themes: next.themes ?? {}, palettes: next.palettes ?? {} }
|
|
18
|
+
}
|
|
19
|
+
function themeEntry(id) {
|
|
20
|
+
const override = overrides.themes[id]
|
|
21
|
+
if (override) return { manifest: override.manifest ?? override, load: () => typeof override.styles === 'string' ? Promise.resolve(override.styles) : themes[id]?.load() ?? Promise.resolve('') }
|
|
22
|
+
return themes[id]
|
|
23
|
+
}
|
|
24
|
+
// Every palette, with the editor's unsaved ones in place of the registered.
|
|
25
|
+
const allPalettes = () => ({ ...PALETTES, ...Object.fromEntries(Object.entries(overrides.palettes).map(([id, palette]) => [id, palette.manifest ?? palette])) })
|
|
26
|
+
|
|
27
|
+
function upsertStyle(id, textContent) {
|
|
28
|
+
let el = document.getElementById(id)
|
|
29
|
+
if (!el) {
|
|
30
|
+
el = document.createElement('style')
|
|
31
|
+
el.id = id
|
|
32
|
+
document.head.appendChild(el)
|
|
33
|
+
}
|
|
34
|
+
el.textContent = textContent
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function syncThemeFonts(urls) {
|
|
38
|
+
for (const existing of [...document.querySelectorAll('link[data-deck-theme-font]')]) {
|
|
39
|
+
existing.remove()
|
|
40
|
+
}
|
|
41
|
+
for (const fontUrl of urls ?? []) {
|
|
42
|
+
const link = document.createElement('link')
|
|
43
|
+
link.rel = 'stylesheet'
|
|
44
|
+
link.href = fontUrl
|
|
45
|
+
link.setAttribute('data-deck-theme-font', '1')
|
|
46
|
+
document.head.appendChild(link)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function loadTheme({ theme = 'neue', palette, appearance, params = {}, meta = {} } = {}) {
|
|
51
|
+
const entry = themeEntry(theme)
|
|
52
|
+
if (!entry) throw new Error(`Unknown theme: "${theme}". Available: ${THEME_NAMES.join(', ')}`)
|
|
53
|
+
|
|
54
|
+
const styles = await entry.load()
|
|
55
|
+
const look = buildAppearance({
|
|
56
|
+
theme: entry.manifest, palettes: allPalettes(), palette, appearance,
|
|
57
|
+
params, meta, offline: SELF_CONTAINED,
|
|
58
|
+
})
|
|
59
|
+
for (const warning of look.warnings) console.warn(warning)
|
|
60
|
+
|
|
61
|
+
// Token defaults come from the manifest; the stylesheet only holds rules.
|
|
62
|
+
upsertStyle('deck-theme', [baseCSS, look.themeCss, styles].join('\n'))
|
|
63
|
+
// Offline-ready builds deliberately use the declared system-font fallbacks.
|
|
64
|
+
syncThemeFonts(look.fonts)
|
|
65
|
+
// The palette's colours, between the theme's tokens and the deck's params.
|
|
66
|
+
upsertStyle('deck-palette', look.paletteCss)
|
|
67
|
+
upsertStyle('deck-overrides', look.overridesCss)
|
|
68
|
+
}
|