@miragon/create-slidev-deck 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/bin/index.mjs +256 -0
- package/package.json +44 -0
- package/templates/README.md +76 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @miragon/create-slidev-deck
|
|
2
|
+
|
|
3
|
+
Scaffold a lean [Miragon Slidev deck](https://github.com/Miragon/slidev-deck-template): a fresh
|
|
4
|
+
repo with **only** the files a deck needs, and `@miragon/slidev-toolkit` pulled from npm instead of
|
|
5
|
+
vendored.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm create @miragon/slidev-deck@latest my-talk
|
|
11
|
+
# equivalently:
|
|
12
|
+
npx @miragon/create-slidev-deck@latest my-talk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
cd my-talk
|
|
19
|
+
npm install
|
|
20
|
+
npm run dev
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Options
|
|
24
|
+
|
|
25
|
+
| Flag | Default | Effect |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `--ref <tag\|sha\|branch>` | this version's release tag | which template snapshot to fetch the skeleton from |
|
|
28
|
+
| `--toolkit-version <x>` | pinned default | pin `@miragon/slidev-toolkit` in the generated `package.json` |
|
|
29
|
+
| `-v`, `--version` | — | print the `create-slidev-deck` version |
|
|
30
|
+
| `-h`, `--help` | — | show usage |
|
|
31
|
+
|
|
32
|
+
Launched via `pnpm create` / `yarn create` / `bun create`, the printed next steps use that package manager.
|
|
33
|
+
|
|
34
|
+
## What it emits
|
|
35
|
+
|
|
36
|
+
Running `npm create @miragon/slidev-deck@latest my-talk` produces:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
my-talk/
|
|
40
|
+
├── deck/ # your content — this is what you edit
|
|
41
|
+
│ ├── slides.md # entry: cover + one src: import per chapter + closing
|
|
42
|
+
│ ├── vite.config.ts # chapter-resources plugin + shaders pre-bundle
|
|
43
|
+
│ ├── public/og-image.png
|
|
44
|
+
│ ├── chapter/
|
|
45
|
+
│ │ ├── 01-intro/01-intro.md
|
|
46
|
+
│ │ ├── 02-slidev/… # each chapter: NN-name.md + its own resources/
|
|
47
|
+
│ │ ├── 03-theme/…
|
|
48
|
+
│ │ ├── 04-diagrams/… # .bpmn, .dmn, .excalidraw.svg demos
|
|
49
|
+
│ │ └── 05-authoring/…
|
|
50
|
+
│ └── README.md
|
|
51
|
+
├── verify/ # brand guardrails — `npm run verify`
|
|
52
|
+
│ ├── rules/ # sanctioned-layout, no-raw-html, excalidraw checks…
|
|
53
|
+
│ ├── slides.spec.ts
|
|
54
|
+
│ └── …
|
|
55
|
+
├── .claude/skills/ # authoring guidance for Claude Code
|
|
56
|
+
│ ├── slides/
|
|
57
|
+
│ └── excalidraw/
|
|
58
|
+
├── .github/workflows/ # only Build Deck + Pin Check
|
|
59
|
+
│ ├── build-and-deploy.yml
|
|
60
|
+
│ └── pin-check.yml
|
|
61
|
+
├── CLAUDE.md # design-system rules, auto-loaded by Claude Code
|
|
62
|
+
├── .npmrc # save-exact=true
|
|
63
|
+
├── .gitignore
|
|
64
|
+
├── README.md # ← generated overlay (deck-focused)
|
|
65
|
+
└── package.json # ← generated overlay (standalone, toolkit pinned, no workspace)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Everything except the two `← generated overlay` files is the shared **skeleton**, fetched from the
|
|
69
|
+
template repo (a single source of design truth). It never emits the template-only infrastructure:
|
|
70
|
+
`packages/`, the release-please / pr-title workflows and config, `LICENSE`, or `netlify.toml`.
|
|
71
|
+
|
|
72
|
+
## How it stays a single source of truth
|
|
73
|
+
|
|
74
|
+
The skeleton is **fetched**, not duplicated here — pinned to the git tag that matches this package's
|
|
75
|
+
version, so a given `create-slidev-deck` version always produces an identical deck. Template content
|
|
76
|
+
changes reach new decks when a new `create-slidev-deck` version is released (a deliberate snapshot),
|
|
77
|
+
keeping the two release lines independent.
|
|
78
|
+
|
|
79
|
+
**Nothing pins a dependency version by hand in this package.** The generated `package.json` is
|
|
80
|
+
*derived* from the fetched skeleton's own manifests at scaffold time:
|
|
81
|
+
|
|
82
|
+
- Slidev runtime deps (`@slidev/cli`, the addons, `vue`) come from the reference `deck/package.json`.
|
|
83
|
+
- The verify tooling (`@playwright/test`, `playwright-chromium`) and the verify scripts come from the
|
|
84
|
+
root `package.json`.
|
|
85
|
+
- Only `@miragon/slidev-toolkit` comes from this package's own pinned devDependency (the reference
|
|
86
|
+
deck resolves the toolkit via a workspace symlink, so it has no version to read). `--toolkit-version`
|
|
87
|
+
overrides it.
|
|
88
|
+
|
|
89
|
+
All of those manifests are kept current by the monorepo's Dependabot, so a freshly-scaffolded deck
|
|
90
|
+
always gets the versions the reference deck currently uses — with no version list to maintain in this
|
|
91
|
+
package.
|
|
92
|
+
|
|
93
|
+
For local development, set `CREATE_DECK_SKELETON=/path/to/template-checkout` to copy the skeleton
|
|
94
|
+
from a local checkout instead of fetching a tag.
|
package/bin/index.mjs
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Scaffold a lean Miragon Slidev deck.
|
|
3
|
+
//
|
|
4
|
+
// Emits ONLY what a deck needs: the shared skeleton (deck/, .claude/, CLAUDE.md,
|
|
5
|
+
// verify/, .npmrc, .gitignore, the two CI workflows) plus a generated overlay (a
|
|
6
|
+
// standalone package.json + a deck-focused README). The toolkit is NOT vendored:
|
|
7
|
+
// it is added as an exact-pinned npm dependency and consumed via the theme.
|
|
8
|
+
//
|
|
9
|
+
// The generated package.json is DERIVED from the fetched skeleton's own manifests
|
|
10
|
+
// (deck/package.json for the Slidev runtime deps, the root package.json for the
|
|
11
|
+
// verify tooling), so the deck's versions are whatever the reference deck currently
|
|
12
|
+
// pins — kept current by the monorepo's Dependabot, nothing to hand-maintain here.
|
|
13
|
+
// Only the toolkit version comes from this package (the reference deck resolves it
|
|
14
|
+
// via a workspace symlink, so it has no version to read).
|
|
15
|
+
//
|
|
16
|
+
// Reproducibility: the skeleton is fetched from this template repo pinned to the
|
|
17
|
+
// tag that matches THIS package's version, so a given create-slidev-deck version
|
|
18
|
+
// always emits an identical deck. `--ref` and `--toolkit-version` override.
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync, statSync } from 'node:fs'
|
|
21
|
+
import { cp, mkdir, mkdtemp, readdir, rm, writeFile } from 'node:fs/promises'
|
|
22
|
+
import { tmpdir } from 'node:os'
|
|
23
|
+
import { basename, dirname, join, resolve } from 'node:path'
|
|
24
|
+
import { fileURLToPath } from 'node:url'
|
|
25
|
+
import { parseArgs } from 'node:util'
|
|
26
|
+
import { downloadTemplate } from 'giget'
|
|
27
|
+
|
|
28
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
29
|
+
const REPO = 'Miragon/slidev-deck-template'
|
|
30
|
+
const SELF = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
31
|
+
|
|
32
|
+
// The toolkit version written into the generated package.json: this package's own
|
|
33
|
+
// pinned @miragon/slidev-toolkit devDependency, so Dependabot keeps the default
|
|
34
|
+
// current and a given create-slidev-deck version emits a byte-identical deck.
|
|
35
|
+
const TOOLKIT_VERSION = SELF.devDependencies['@miragon/slidev-toolkit']
|
|
36
|
+
|
|
37
|
+
// Paths copied verbatim from the fetched skeleton into the new deck. Anything not
|
|
38
|
+
// listed (packages/, release-please*, pr-title.yml, LICENSE, netlify.toml, docs/)
|
|
39
|
+
// is intentionally left out — that is the whole point of the scaffold.
|
|
40
|
+
const SKELETON = [
|
|
41
|
+
'deck',
|
|
42
|
+
'.claude',
|
|
43
|
+
'verify',
|
|
44
|
+
'CLAUDE.md',
|
|
45
|
+
'.npmrc',
|
|
46
|
+
'.gitignore',
|
|
47
|
+
'.github/workflows/build-and-deploy.yml',
|
|
48
|
+
'.github/workflows/pin-check.yml',
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
// Skeleton files READ (not copied) to derive the generated package.json.
|
|
52
|
+
const MANIFESTS = ['package.json', 'deck/package.json']
|
|
53
|
+
|
|
54
|
+
// Skeleton files that must not survive into a standalone deck. The deck's own
|
|
55
|
+
// workspace sub-manifest is replaced by the generated root package.json below.
|
|
56
|
+
const PRUNE = ['deck/package.json']
|
|
57
|
+
|
|
58
|
+
// The verify tooling the generated deck needs, pulled (by name) from the root
|
|
59
|
+
// package.json's devDependencies so the versions track the reference repo.
|
|
60
|
+
const VERIFY_DEV_DEPS = ['@playwright/test', 'playwright-chromium']
|
|
61
|
+
|
|
62
|
+
/** Parse argv with node:util — it validates unknown options and missing values for us. */
|
|
63
|
+
function parseCliArgs(argv) {
|
|
64
|
+
const { values, positionals } = parseArgs({
|
|
65
|
+
args: argv,
|
|
66
|
+
allowPositionals: true,
|
|
67
|
+
options: {
|
|
68
|
+
ref: { type: 'string' },
|
|
69
|
+
'toolkit-version': { type: 'string' },
|
|
70
|
+
version: { type: 'boolean', short: 'v' },
|
|
71
|
+
help: { type: 'boolean', short: 'h' },
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
if (positionals.length > 1) throw new Error(`Unexpected argument: ${positionals[1]}`)
|
|
75
|
+
return {
|
|
76
|
+
target: positionals[0],
|
|
77
|
+
ref: values.ref,
|
|
78
|
+
toolkitVersion: values['toolkit-version'],
|
|
79
|
+
version: values.version,
|
|
80
|
+
help: values.help,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const USAGE = `Usage: npm create @miragon/slidev-deck@latest <dir> [options]
|
|
85
|
+
|
|
86
|
+
<dir> target directory for the new deck (must be empty)
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
--ref <tag|sha|branch> skeleton source ref (default: this version's release tag)
|
|
90
|
+
--toolkit-version <x> pin @miragon/slidev-toolkit to <x> (default: ${TOOLKIT_VERSION})
|
|
91
|
+
-v, --version print the create-slidev-deck version
|
|
92
|
+
-h, --help show this help
|
|
93
|
+
|
|
94
|
+
Env:
|
|
95
|
+
CREATE_DECK_SKELETON=<dir> copy the skeleton from a local checkout instead of
|
|
96
|
+
fetching (development / offline).`
|
|
97
|
+
|
|
98
|
+
/** Detect the package manager the initializer was launched with (npm create / pnpm create / …). */
|
|
99
|
+
function packageManager() {
|
|
100
|
+
const name = (process.env.npm_config_user_agent ?? '').split('/')[0]
|
|
101
|
+
return ['pnpm', 'yarn', 'bun'].includes(name) ? name : 'npm'
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Fetch (or locally copy) the template repo into a scratch dir; return its path. */
|
|
105
|
+
async function fetchSkeleton(ref) {
|
|
106
|
+
const local = process.env.CREATE_DECK_SKELETON
|
|
107
|
+
const scratch = await mkdtemp(join(tmpdir(), 'create-slidev-deck-'))
|
|
108
|
+
if (!local) {
|
|
109
|
+
await downloadTemplate(`github:${REPO}#${ref}`, { dir: scratch, force: true })
|
|
110
|
+
return scratch
|
|
111
|
+
}
|
|
112
|
+
// Local mode brings the copy whitelist plus the manifests we read to build the
|
|
113
|
+
// package.json (giget's full download already contains them).
|
|
114
|
+
const src = resolve(local)
|
|
115
|
+
for (const rel of [...SKELETON, ...MANIFESTS]) {
|
|
116
|
+
const from = join(src, rel)
|
|
117
|
+
if (existsSync(from)) {
|
|
118
|
+
await mkdir(dirname(join(scratch, rel)), { recursive: true })
|
|
119
|
+
await cp(from, join(scratch, rel), { recursive: true })
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return scratch
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Turn a target directory into a valid, lowercase npm package name. */
|
|
126
|
+
function deckNameFrom(dir) {
|
|
127
|
+
return basename(dir).toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '') || 'my-deck'
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The verify tooling versions, read (by name) from the root package.json's devDependencies. */
|
|
131
|
+
function verifyDevDeps(rootPkg) {
|
|
132
|
+
const deps = {}
|
|
133
|
+
for (const name of VERIFY_DEV_DEPS) {
|
|
134
|
+
const version = rootPkg.devDependencies?.[name]
|
|
135
|
+
if (!version) throw new Error(`Root package.json is missing devDependency ${name}`)
|
|
136
|
+
deps[name] = version
|
|
137
|
+
}
|
|
138
|
+
return deps
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Build the standalone deck package.json from the fetched skeleton's manifests. */
|
|
142
|
+
function buildPackageJson(scratch, deckName, toolkitVersion) {
|
|
143
|
+
const readManifest = (rel) => JSON.parse(readFileSync(join(scratch, rel), 'utf8'))
|
|
144
|
+
const deckPkg = readManifest('deck/package.json')
|
|
145
|
+
const rootPkg = readManifest('package.json')
|
|
146
|
+
const pkg = {
|
|
147
|
+
name: deckName,
|
|
148
|
+
type: 'module',
|
|
149
|
+
private: true,
|
|
150
|
+
scripts: {
|
|
151
|
+
dev: 'slidev deck/slides.md --open',
|
|
152
|
+
build: 'slidev build deck/slides.md --out ../dist',
|
|
153
|
+
export: 'slidev export deck/slides.md',
|
|
154
|
+
verify: rootPkg.scripts.verify,
|
|
155
|
+
'verify:source': rootPkg.scripts['verify:source'],
|
|
156
|
+
},
|
|
157
|
+
dependencies: { '@miragon/slidev-toolkit': toolkitVersion, ...deckPkg.dependencies },
|
|
158
|
+
devDependencies: verifyDevDeps(rootPkg),
|
|
159
|
+
}
|
|
160
|
+
return JSON.stringify(pkg, null, 2) + '\n'
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Exit unless `target` is absent or an empty directory. Returns whether it already exists. */
|
|
164
|
+
async function ensureEmptyTarget(target) {
|
|
165
|
+
if (!existsSync(target)) return false
|
|
166
|
+
const refuse = (why) => {
|
|
167
|
+
console.error(`Refusing to scaffold: ${target} ${why}.`)
|
|
168
|
+
process.exit(1)
|
|
169
|
+
}
|
|
170
|
+
if (!statSync(target).isDirectory()) refuse('exists and is not a directory')
|
|
171
|
+
if ((await readdir(target)).length > 0) refuse('exists and is not empty')
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Copy each whitelisted skeleton path into the target. */
|
|
176
|
+
async function copySkeleton(scratch, target, ref) {
|
|
177
|
+
for (const rel of SKELETON) {
|
|
178
|
+
const from = join(scratch, rel)
|
|
179
|
+
if (!existsSync(from)) throw new Error(`Skeleton is missing ${rel} (ref ${ref}).`)
|
|
180
|
+
const to = join(target, rel)
|
|
181
|
+
await mkdir(dirname(to), { recursive: true })
|
|
182
|
+
await cp(from, to, { recursive: true })
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Undo a half-written target so a retry is not blocked by the empty-dir guard. */
|
|
187
|
+
async function rollback(target, preexisting) {
|
|
188
|
+
if (!preexisting) return rm(target, { recursive: true, force: true })
|
|
189
|
+
for (const entry of await readdir(target).catch(() => [])) {
|
|
190
|
+
await rm(join(target, entry), { recursive: true, force: true })
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Assemble the deck in `target`: skeleton, prune, then the generated overlay. */
|
|
195
|
+
async function layDownDeck({ scratch, target, deckName, toolkitVersion, ref, preexisting }) {
|
|
196
|
+
try {
|
|
197
|
+
await mkdir(target, { recursive: true })
|
|
198
|
+
await copySkeleton(scratch, target, ref)
|
|
199
|
+
for (const rel of PRUNE) await rm(join(target, rel), { force: true })
|
|
200
|
+
|
|
201
|
+
const packageJson = buildPackageJson(scratch, deckName, toolkitVersion)
|
|
202
|
+
await writeFile(join(target, 'package.json'), packageJson)
|
|
203
|
+
await cp(join(HERE, '..', 'templates', 'README.md'), join(target, 'README.md'))
|
|
204
|
+
} catch (err) {
|
|
205
|
+
await rollback(target, preexisting)
|
|
206
|
+
throw err
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function printNextSteps(dir) {
|
|
211
|
+
const pm = packageManager()
|
|
212
|
+
console.log(`
|
|
213
|
+
Done. Your deck is ready in ${dir}
|
|
214
|
+
|
|
215
|
+
Next steps:
|
|
216
|
+
cd ${dir}
|
|
217
|
+
${pm} install
|
|
218
|
+
${pm} run dev
|
|
219
|
+
|
|
220
|
+
Build with '${pm} run build', check brand guardrails with '${pm} run verify'.`)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function main() {
|
|
224
|
+
const opts = parseCliArgs(process.argv.slice(2))
|
|
225
|
+
if (opts.version) {
|
|
226
|
+
console.log(SELF.version)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
if (opts.help || !opts.target) {
|
|
230
|
+
console.log(USAGE)
|
|
231
|
+
process.exit(opts.help ? 0 : 1)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const target = resolve(opts.target)
|
|
235
|
+
const preexisting = await ensureEmptyTarget(target)
|
|
236
|
+
const ref = opts.ref ?? `create-slidev-deck-v${SELF.version}`
|
|
237
|
+
const toolkitVersion = opts.toolkitVersion ?? TOOLKIT_VERSION
|
|
238
|
+
const deckName = deckNameFrom(target)
|
|
239
|
+
|
|
240
|
+
const source = process.env.CREATE_DECK_SKELETON ? 'local checkout' : `${REPO}#${ref}`
|
|
241
|
+
console.log(`Scaffolding ${deckName} from ${source} ...`)
|
|
242
|
+
|
|
243
|
+
const scratch = await fetchSkeleton(ref)
|
|
244
|
+
try {
|
|
245
|
+
await layDownDeck({ scratch, target, deckName, toolkitVersion, ref, preexisting })
|
|
246
|
+
} finally {
|
|
247
|
+
await rm(scratch, { recursive: true, force: true })
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
printNextSteps(opts.target)
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
main().catch((err) => {
|
|
254
|
+
console.error(err instanceof Error ? err.message : err)
|
|
255
|
+
process.exit(1)
|
|
256
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miragon/create-slidev-deck",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scaffold a lean Miragon Slidev deck: only the files a deck needs, with @miragon/slidev-toolkit pulled from npm. Run via `npm create @miragon/slidev-deck`.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"slidev",
|
|
8
|
+
"miragon",
|
|
9
|
+
"create",
|
|
10
|
+
"scaffold",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/Miragon/slidev-deck-template.git",
|
|
17
|
+
"directory": "packages/create-deck"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/Miragon/slidev-deck-template/tree/main/packages/create-deck#readme",
|
|
20
|
+
"bugs": "https://github.com/Miragon/slidev-deck-template/issues",
|
|
21
|
+
"bin": {
|
|
22
|
+
"create-slidev-deck": "bin/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "node --test"
|
|
26
|
+
},
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=20"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"giget": "3.3.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@miragon/slidev-toolkit": "1.2.1"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"bin",
|
|
38
|
+
"templates"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public",
|
|
42
|
+
"provenance": true
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# __DECK_NAME__
|
|
2
|
+
|
|
3
|
+
A [Slidev](https://sli.dev) presentation in the Miragon corporate design, scaffolded from
|
|
4
|
+
[`@miragon/slidev-deck-template`](https://github.com/Miragon/slidev-deck-template). The design
|
|
5
|
+
system ships as the [`@miragon/slidev-toolkit`](https://www.npmjs.com/package/@miragon/slidev-toolkit)
|
|
6
|
+
npm package — you fill in the content, the theme is fixed.
|
|
7
|
+
|
|
8
|
+
## Quick start
|
|
9
|
+
|
|
10
|
+
Needs **Node 20+** and a modern browser (WebGL2 for the animated background, with a CSS-gradient fallback).
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install
|
|
14
|
+
npm run dev # opens the deck on http://localhost:3030 with live reload
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Edit files under `deck/` and save — the preview updates instantly. Every demo slide carries a
|
|
18
|
+
`REQUIRED / OPTIONAL / LIMIT / HOW TO USE` comment block: your in-place authoring guide.
|
|
19
|
+
|
|
20
|
+
## Layout
|
|
21
|
+
|
|
22
|
+
| Path | What it is |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `deck/` | **Your content** — `slides.md` is the entry (cover + one `src:` import per chapter + closing); each chapter is a folder `deck/chapter/NN-name/` with its own `resources/`. |
|
|
25
|
+
| `verify/` | Brand guardrails (`npm run verify`) — every slide declares a sanctioned layout, cards stay white, no em-dashes, headings black, diagrams light/transparent. |
|
|
26
|
+
| `CLAUDE.md` + `.claude/skills/` | Authoring guidance for Claude Code, so a session knows the design system on the first prompt. |
|
|
27
|
+
| `.github/workflows/` | **Build Deck** and **Pin Check** run on every push and PR. |
|
|
28
|
+
|
|
29
|
+
The deck consumes the toolkit by name (`theme: '@miragon/slidev-toolkit'`); you never touch the theme.
|
|
30
|
+
|
|
31
|
+
## Commands
|
|
32
|
+
|
|
33
|
+
| Command | Result |
|
|
34
|
+
|---|---|
|
|
35
|
+
| `npm run dev` | Live preview on `:3030`; `p` for presenter mode, `o` for overview |
|
|
36
|
+
| `npm run build` | Static `dist/` you can host anywhere |
|
|
37
|
+
| `npm run export` | `slidev-exported.pdf` locally (needs Chromium) |
|
|
38
|
+
| `npm run verify` | Screenshot + checklist per slide against the design rules |
|
|
39
|
+
|
|
40
|
+
## Next steps
|
|
41
|
+
|
|
42
|
+
1. Replace the demo content under `deck/`; keep the comment-block guardrails.
|
|
43
|
+
2. Point the `seoMeta` block in `deck/slides.md` at your own domain, or delete it.
|
|
44
|
+
3. Commit the generated `package-lock.json` after the first `npm install` so CI (`npm ci`) is reproducible.
|
|
45
|
+
4. Or open the repo with Claude Code and let it draft the first pass from your outline.
|
|
46
|
+
|
|
47
|
+
Authoring conventions live in the `slides` skill (`.claude/skills/slides/`). Starter prompts:
|
|
48
|
+
|
|
49
|
+
> Outline a 30-minute talk on [topic] using `cover`, three `section` chapters with two `content` slides each, then `closing`.
|
|
50
|
+
|
|
51
|
+
> Build a `goodbad` slide asking "Which error message helps the user more?" Make Model A the avoid side.
|
|
52
|
+
|
|
53
|
+
## Staying up to date
|
|
54
|
+
|
|
55
|
+
Your dependencies (`@miragon/slidev-toolkit`, `@slidev/cli`, the addons) are exact-pinned so installs stay
|
|
56
|
+
reproducible. For a deck you keep around, enable [Dependabot](https://docs.github.com/en/code-security/dependabot/dependabot-version-updates)
|
|
57
|
+
so it opens PRs when new versions ship — you get the latest toolkit and Slidev without hunting for updates,
|
|
58
|
+
and **Build Deck** + **Pin Check** gate each PR. Add `.github/dependabot.yml`:
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
version: 2
|
|
62
|
+
updates:
|
|
63
|
+
- package-ecosystem: npm
|
|
64
|
+
directory: /
|
|
65
|
+
schedule:
|
|
66
|
+
interval: weekly
|
|
67
|
+
groups:
|
|
68
|
+
npm:
|
|
69
|
+
patterns: ["*"]
|
|
70
|
+
- package-ecosystem: github-actions
|
|
71
|
+
directory: /
|
|
72
|
+
schedule:
|
|
73
|
+
interval: monthly
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For a one-off talk you can skip this — the pinned versions keep working as-is.
|