@lessonkit/cli 0.9.3 → 1.0.1

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/dist/index.js CHANGED
@@ -156,6 +156,12 @@ async function runInit(opts, logger) {
156
156
  exitCode: EXIT_INVALID_PROJECT
157
157
  });
158
158
  }
159
+ if (opts.force && !opts.here) {
160
+ throw new CliError("--force requires --here (initialize in the current directory).", {
161
+ code: "INVALID_PROJECT",
162
+ exitCode: EXIT_INVALID_PROJECT
163
+ });
164
+ }
159
165
  const slug = slugifyId(rawName ?? "my-course");
160
166
  const projectName = rawName ?? slug;
161
167
  const projectDir = opts.here ? cwd : resolve(cwd, slug);
@@ -207,18 +213,13 @@ async function runInit(opts, logger) {
207
213
  import { readFileSync, existsSync as existsSync2 } from "fs";
208
214
  import { readFile as readFile2 } from "fs/promises";
209
215
  import { dirname as dirname2, join as join2, parse, resolve as resolve2 } from "path";
210
- import { validateDescriptor, validateProjectPaths } from "@lessonkit/lxpack";
216
+ import { parseLessonkitManifest } from "@lessonkit/lxpack";
211
217
  var LESSONKIT_JSON = "lessonkit.json";
212
218
  var PACKAGE_JSON = "package.json";
213
- var DEFAULT_PATHS = {
214
- spaDistDir: "dist",
215
- lxpackOutDir: ".lxpack/course",
216
- outputBaseDir: ".lxpack/out"
217
- };
218
219
  function isProjectManifest(configPath) {
219
220
  try {
220
221
  const raw = JSON.parse(readFileSync(configPath, "utf8"));
221
- return raw.schemaVersion === 1 && typeof raw.name === "string" && raw.course !== null && typeof raw.course === "object";
222
+ return raw.schemaVersion === 1 && typeof raw.name === "string" && raw.course !== null && typeof raw.course === "object" && !Array.isArray(raw.course);
222
223
  } catch {
223
224
  return false;
224
225
  }
@@ -251,124 +252,63 @@ async function loadLessonkitJson(projectRoot) {
251
252
  exitCode: EXIT_INVALID_PROJECT
252
253
  });
253
254
  }
254
- if (!raw || typeof raw !== "object") {
255
- throw new CliError(`${configPath} must be a JSON object.`, {
256
- code: "INVALID_PROJECT",
257
- exitCode: EXIT_INVALID_PROJECT
258
- });
255
+ const parsed = parseLessonkitManifest(raw, configPath, projectRoot);
256
+ if (!parsed.ok) {
257
+ throwManifestCliError(configPath, parsed.issues);
259
258
  }
260
- const config = raw;
261
- const schemaVersion = config.schemaVersion;
262
- if (schemaVersion !== 1) {
263
- throw new CliError(`${configPath}: schemaVersion must be 1 (got ${String(schemaVersion)}).`, {
264
- code: "INVALID_PROJECT",
265
- exitCode: EXIT_INVALID_PROJECT
266
- });
267
- }
268
- const name = config.name;
269
- if (typeof name !== "string" || !name.trim()) {
270
- throw new CliError(`${configPath}: "name" must be a non-empty string.`, {
271
- code: "INVALID_PROJECT",
272
- exitCode: EXIT_INVALID_PROJECT
273
- });
259
+ return {
260
+ ...parsed.manifest,
261
+ root: projectRoot
262
+ };
263
+ }
264
+ function throwManifestCliError(configPath, issues) {
265
+ const layoutIssue = issues.find((i) => i.path === "course.layout");
266
+ if (layoutIssue?.message.includes("per-lesson-spa")) {
267
+ throw new CliError(
268
+ `${configPath}: per-lesson-spa layout is not supported by lessonkit package yet. Use single-spa or package via @lessonkit/lxpack directly.`,
269
+ { code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
270
+ );
274
271
  }
275
- const courseRaw = config.course;
276
- if (!courseRaw || typeof courseRaw !== "object") {
277
- throw new CliError(`${configPath}: "course" must be an object.`, {
272
+ const lessonsIssue = issues.find((i) => i.path === "course.lessons");
273
+ if (lessonsIssue) {
274
+ throw new CliError(`${configPath}: "course.lessons" must be an array.`, {
278
275
  code: "INVALID_PROJECT",
279
276
  exitCode: EXIT_INVALID_PROJECT
280
277
  });
281
278
  }
282
- const courseObj = courseRaw;
283
- if (courseObj.lessons !== void 0 && !Array.isArray(courseObj.lessons)) {
284
- throw new CliError(`${configPath}: "course.lessons" must be an array.`, {
279
+ const spaDistTypeIssue = issues.find((i) => i.path === "paths.spaDistDir");
280
+ if (spaDistTypeIssue && spaDistTypeIssue.message.includes("non-empty string")) {
281
+ throw new CliError(`${configPath}: "paths.spaDistDir" must be a non-empty string.`, {
285
282
  code: "INVALID_PROJECT",
286
283
  exitCode: EXIT_INVALID_PROJECT
287
284
  });
288
285
  }
289
- if (courseObj.assessments !== void 0 && !Array.isArray(courseObj.assessments)) {
290
- throw new CliError(`${configPath}: "course.assessments" must be an array.`, {
286
+ const courseSpaIssue = issues.find((i) => i.path === "course.spaDistDir");
287
+ if (courseSpaIssue) {
288
+ throw new CliError(`${configPath}: ${courseSpaIssue.message}`, {
291
289
  code: "INVALID_PROJECT",
292
290
  exitCode: EXIT_INVALID_PROJECT
293
291
  });
294
292
  }
295
- const validation = validateDescriptor(courseRaw);
296
- if (!validation.ok) {
297
- throw new CliError(`${configPath}: invalid course descriptor.`, {
293
+ if (issues.some((i) => i.path.startsWith("paths."))) {
294
+ throw new CliError(`${configPath}: invalid paths.`, {
298
295
  code: "INVALID_PROJECT",
299
296
  exitCode: EXIT_INVALID_PROJECT,
300
- issues: validation.issues.map((i) => ({
301
- path: i.path,
302
- message: i.message
303
- }))
297
+ issues: issues.map((i) => ({ path: i.path, message: i.message }))
304
298
  });
305
299
  }
306
- if (validation.descriptor.layout === "per-lesson-spa") {
307
- throw new CliError(
308
- `${configPath}: per-lesson-spa layout is not supported by lessonkit package yet. Use single-spa or package via @lessonkit/lxpack directly.`,
309
- { code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
310
- );
311
- }
312
- const pathsRaw = config.paths;
313
- const paths = { ...DEFAULT_PATHS };
314
- if (pathsRaw !== void 0 && (typeof pathsRaw !== "object" || pathsRaw === null)) {
315
- throw new CliError(`${configPath}: "paths" must be an object.`, {
300
+ const schemaIssue = issues.find((i) => i.path === "schemaVersion");
301
+ if (schemaIssue) {
302
+ throw new CliError(`${configPath}: schemaVersion must be 1 (got ${schemaIssue.message.replace(/^must be 1 \(got /, "").replace(/\)$/, "")}).`, {
316
303
  code: "INVALID_PROJECT",
317
304
  exitCode: EXIT_INVALID_PROJECT
318
305
  });
319
306
  }
320
- if (pathsRaw && typeof pathsRaw === "object") {
321
- const p = pathsRaw;
322
- if (p.spaDistDir !== void 0) {
323
- if (typeof p.spaDistDir !== "string" || !p.spaDistDir.trim()) {
324
- throw new CliError(`${configPath}: "paths.spaDistDir" must be a non-empty string.`, {
325
- code: "INVALID_PROJECT",
326
- exitCode: EXIT_INVALID_PROJECT
327
- });
328
- }
329
- paths.spaDistDir = p.spaDistDir;
330
- }
331
- if (p.lxpackOutDir !== void 0) {
332
- if (typeof p.lxpackOutDir !== "string" || !p.lxpackOutDir.trim()) {
333
- throw new CliError(`${configPath}: "paths.lxpackOutDir" must be a non-empty string.`, {
334
- code: "INVALID_PROJECT",
335
- exitCode: EXIT_INVALID_PROJECT
336
- });
337
- }
338
- paths.lxpackOutDir = p.lxpackOutDir;
339
- }
340
- if (p.outputBaseDir !== void 0) {
341
- if (typeof p.outputBaseDir !== "string" || !p.outputBaseDir.trim()) {
342
- throw new CliError(`${configPath}: "paths.outputBaseDir" must be a non-empty string.`, {
343
- code: "INVALID_PROJECT",
344
- exitCode: EXIT_INVALID_PROJECT
345
- });
346
- }
347
- paths.outputBaseDir = p.outputBaseDir;
348
- }
349
- }
350
- const courseSpaDistDir = validation.descriptor.spaDistDir?.trim();
351
- if (courseSpaDistDir && courseSpaDistDir !== paths.spaDistDir) {
352
- throw new CliError(
353
- `${configPath}: "course.spaDistDir" (${courseSpaDistDir}) differs from "paths.spaDistDir" (${paths.spaDistDir}). Use paths.spaDistDir for CLI build and package.`,
354
- { code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
355
- );
356
- }
357
- const pathIssues = validateProjectPaths(projectRoot, paths);
358
- if (pathIssues.length) {
359
- throw new CliError(`${configPath}: invalid paths.`, {
360
- code: "INVALID_PROJECT",
361
- exitCode: EXIT_INVALID_PROJECT,
362
- issues: pathIssues
363
- });
364
- }
365
- return {
366
- root: projectRoot,
367
- schemaVersion: 1,
368
- name,
369
- course: validation.descriptor,
370
- paths
371
- };
307
+ throw new CliError(`${configPath}: invalid lessonkit manifest.`, {
308
+ code: "INVALID_PROJECT",
309
+ exitCode: EXIT_INVALID_PROJECT,
310
+ issues: issues.map((i) => ({ path: i.path, message: i.message }))
311
+ });
372
312
  }
373
313
  async function loadProject(cwd = process.cwd()) {
374
314
  const root = findProjectRoot(cwd);
@@ -386,7 +326,7 @@ async function readPackageJson(projectRoot) {
386
326
  }
387
327
  }
388
328
  function assertViteProject(pkg, projectRoot) {
389
- const vite = pkg.devDependencies?.vite ?? pkg.dependencies?.vite ?? (existsSync2(join2(projectRoot, "node_modules", ".bin", "vite")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite.cmd")) ? "present" : void 0);
329
+ const vite = pkg.devDependencies?.vite ?? pkg.dependencies?.vite ?? (existsSync2(join2(projectRoot, "node_modules", "vite", "bin", "vite.js")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite")) || existsSync2(join2(projectRoot, "node_modules", ".bin", "vite.cmd")) ? "present" : void 0);
390
330
  if (!vite) {
391
331
  throw new CliError(
392
332
  `No Vite dependency found in ${join2(projectRoot, PACKAGE_JSON)}. LessonKit projects require Vite.`,
@@ -394,28 +334,25 @@ function assertViteProject(pkg, projectRoot) {
394
334
  );
395
335
  }
396
336
  }
397
- function resolveViteBin(projectRoot) {
337
+ function resolveViteJs(projectRoot) {
398
338
  let dir = resolve2(projectRoot);
399
339
  const fsRoot = parse(dir).root;
400
340
  while (true) {
401
- const binDir = join2(dir, "node_modules", ".bin");
402
- const bin = join2(binDir, "vite");
403
- if (existsSync2(bin)) return bin;
404
- const binCmd = join2(binDir, "vite.cmd");
405
- if (existsSync2(binCmd)) return binCmd;
341
+ const viteJs = join2(dir, "node_modules", "vite", "bin", "vite.js");
342
+ if (existsSync2(viteJs)) return viteJs;
406
343
  if (dir === fsRoot) break;
407
344
  dir = dirname2(dir);
408
345
  }
409
346
  throw new CliError(
410
- `Vite binary not found near ${projectRoot}. Run npm install in the project first.`,
347
+ `Vite not found near ${projectRoot}. Run npm install in the project first.`,
411
348
  { code: "INVALID_PROJECT", exitCode: EXIT_INVALID_PROJECT }
412
349
  );
413
350
  }
414
- function assertNode20ForLxpack() {
351
+ function assertNode18ForLxpack() {
415
352
  const major = Number(process.versions.node.split(".")[0]);
416
- if (major < 20) {
353
+ if (major < 18) {
417
354
  throw new CliError(
418
- `LMS packaging requires Node.js 20+ (current: ${process.versions.node}). See docs/PACKAGING.md.`,
355
+ `LMS packaging requires Node.js 18+ (current: ${process.versions.node}). See docs/PACKAGING.md.`,
419
356
  { code: "NODE_VERSION", exitCode: EXIT_INVALID_PROJECT }
420
357
  );
421
358
  }
@@ -424,6 +361,18 @@ function assertNode20ForLxpack() {
424
361
  // src/lib/paths.ts
425
362
  import { resolve as resolve3 } from "path";
426
363
  import { resolveSafePackageOutputOverride } from "@lessonkit/lxpack";
364
+
365
+ // src/lib/targetTypes.ts
366
+ var PACKAGE_TARGETS = [
367
+ "react-vite",
368
+ "scorm12",
369
+ "scorm2004",
370
+ "xapi",
371
+ "cmi5",
372
+ "standalone"
373
+ ];
374
+
375
+ // src/lib/paths.ts
427
376
  function resolveDistDir(project) {
428
377
  return resolve3(project.root, project.paths.spaDistDir);
429
378
  }
@@ -454,14 +403,6 @@ function resolveViteBuildArgs(project) {
454
403
  }
455
404
  return args;
456
405
  }
457
- var PACKAGE_TARGETS = [
458
- "react-vite",
459
- "scorm12",
460
- "scorm2004",
461
- "xapi",
462
- "cmi5",
463
- "standalone"
464
- ];
465
406
  function parsePackageTarget(value) {
466
407
  if (!value) {
467
408
  throw new Error("TARGET_REQUIRED");
@@ -477,17 +418,19 @@ async function runDev(opts) {
477
418
  const project = await loadProject(opts.cwd ?? process.cwd());
478
419
  const pkg = await readPackageJson(project.root);
479
420
  assertViteProject(pkg, project.root);
480
- const viteBin = resolveViteBin(project.root);
481
- await runCommand(viteBin, opts.viteArgs ?? [], { cwd: project.root });
421
+ const viteJs = resolveViteJs(project.root);
422
+ await runCommand(process.execPath, [viteJs, ...opts.viteArgs ?? []], { cwd: project.root });
482
423
  return { ok: true, command: "dev", projectRoot: project.root };
483
424
  }
484
425
  async function runBuild(opts) {
485
426
  const project = await loadProject(opts.cwd ?? process.cwd());
486
427
  const pkg = await readPackageJson(project.root);
487
428
  assertViteProject(pkg, project.root);
488
- const viteBin = resolveViteBin(project.root);
429
+ const viteJs = resolveViteJs(project.root);
489
430
  const buildArgs = resolveViteBuildArgs(project);
490
- await runCommand(viteBin, [...buildArgs, ...opts.viteArgs ?? []], { cwd: project.root });
431
+ await runCommand(process.execPath, [viteJs, ...buildArgs, ...opts.viteArgs ?? []], {
432
+ cwd: project.root
433
+ });
491
434
  return { ok: true, command: "build", projectRoot: project.root };
492
435
  }
493
436
 
@@ -510,8 +453,17 @@ async function runPackage(opts) {
510
453
  }
511
454
  const project = await loadProject(opts.cwd ?? process.cwd());
512
455
  const distDir = resolveDistDir(project);
456
+ if (opts.noBuild && !existsSync3(distDir)) {
457
+ throw new CliError(
458
+ `dist directory not found at ${distDir}. Run lessonkit build before packaging with --no-build.`,
459
+ {
460
+ code: "INVALID_PROJECT",
461
+ exitCode: EXIT_INVALID_PROJECT
462
+ }
463
+ );
464
+ }
513
465
  if (target === "react-vite") {
514
- if (!opts.noBuild || !existsSync3(distDir)) {
466
+ if (!opts.noBuild) {
515
467
  await runBuild({ cwd: project.root, json: opts.json });
516
468
  }
517
469
  if (!existsSync3(distDir)) {
@@ -520,10 +472,10 @@ async function runPackage(opts) {
520
472
  exitCode: EXIT_INVALID_PROJECT
521
473
  });
522
474
  }
523
- return { ok: true, target, projectRoot: project.root, distDir };
475
+ return { ok: true, command: "package", target, projectRoot: project.root, distDir };
524
476
  }
525
- assertNode20ForLxpack();
526
- if (!opts.noBuild || !existsSync3(distDir)) {
477
+ assertNode18ForLxpack();
478
+ if (!opts.noBuild) {
527
479
  await runBuild({ cwd: project.root, json: opts.json });
528
480
  }
529
481
  if (!existsSync3(distDir)) {
@@ -553,6 +505,7 @@ async function runPackage(opts) {
553
505
  }
554
506
  return {
555
507
  ok: true,
508
+ command: "package",
556
509
  target,
557
510
  projectRoot: project.root,
558
511
  outputPath: result.outputPath,
@@ -596,7 +549,10 @@ async function handleCommand(fn, logger, json) {
596
549
  function createProgram(baseLogger = console) {
597
550
  const program = new Command();
598
551
  program.name("lessonkit").description("LessonKit CLI").version(version);
599
- program.command("init").description("Initialize a LessonKit project from the Vite + React template").argument("[name]", "Project directory name").option("--here", "Initialize in the current directory").option("--skip-install", "Skip npm install").option("--force", "Initialize into a non-empty directory").option("--json", "Emit structured JSON result").action(async (name, opts) => {
552
+ program.command("init").description("Initialize a LessonKit project from the Vite + React template").argument("[name]", "Project directory name").option("--here", "Initialize in the current directory").option("--skip-install", "Skip npm install").option(
553
+ "--force",
554
+ "Requires --here: allow init when the directory is empty or contains only dotfiles"
555
+ ).option("--json", "Emit structured JSON result").action(async (name, opts) => {
600
556
  const logger = createLogger({ json: opts.json });
601
557
  await handleCommand(
602
558
  () => runInit({ name, here: opts.here, skipInstall: opts.skipInstall, force: opts.force, json: opts.json }, logger),
@@ -616,7 +572,9 @@ function createProgram(baseLogger = console) {
616
572
  Boolean(opts.json)
617
573
  );
618
574
  });
619
- addCwdAndJson(program.command("build").description("Production Vite build")).action(
575
+ addCwdAndJson(
576
+ program.command("build").description("Production Vite build").allowUnknownOption().allowExcessArguments()
577
+ ).action(
620
578
  async (opts, command) => {
621
579
  const logger = createLogger({ json: opts.json });
622
580
  const viteArgs = command.args;
@@ -630,13 +588,26 @@ function createProgram(baseLogger = console) {
630
588
  program.command("package").description("Build or package for web / LMS delivery").requiredOption("--target <target>", `Export target (${PACKAGE_TARGETS.join(", ")})`).option("--cwd <dir>", "Project root directory").option("--no-build", "Skip implicit Vite build for LMS targets").option("--out <path>", "Override output artifact path").option("--json", "Emit structured JSON result").action(async (opts) => {
631
589
  const logger = createLogger({ json: opts.json });
632
590
  await handleCommand(
633
- () => runPackage({
634
- target: opts.target,
635
- cwd: opts.cwd,
636
- noBuild: opts.build === false,
637
- out: opts.out,
638
- json: opts.json
639
- }),
591
+ async () => {
592
+ const result = await runPackage({
593
+ target: opts.target,
594
+ cwd: opts.cwd,
595
+ noBuild: opts.build === false,
596
+ out: opts.out,
597
+ json: opts.json
598
+ });
599
+ if (!opts.json && result.ok) {
600
+ if (result.command === "package" && result.target === "react-vite") {
601
+ logger.log(`Built react-vite \u2192 ${result.distDir}`);
602
+ } else if (result.command === "package") {
603
+ const dest = result.outputPath ?? result.outputDir;
604
+ logger.log(
605
+ `Packaged ${result.target}${dest ? ` \u2192 ${dest}` : ""} (${result.fileCount} files)`
606
+ );
607
+ }
608
+ }
609
+ return result;
610
+ },
640
611
  logger,
641
612
  Boolean(opts.json)
642
613
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/cli",
3
- "version": "0.9.3",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "description": "LessonKit CLI — init, dev, build, and package learning experiences.",
6
6
  "license": "Apache-2.0",
@@ -42,8 +42,8 @@
42
42
  "lint": "echo \"(no lint configured yet)\""
43
43
  },
44
44
  "dependencies": {
45
- "@lessonkit/core": "0.9.3",
46
- "@lessonkit/lxpack": "0.9.3",
45
+ "@lessonkit/core": "1.0.1",
46
+ "@lessonkit/lxpack": "1.0.1",
47
47
  "commander": "^14.0.1"
48
48
  },
49
49
  "engines": {
@@ -1,19 +1,21 @@
1
- # LessonKit Vite + React template
1
+ # LessonKit starter template
2
2
 
3
- [![Documentation](https://readthedocs.org/projects/lessonkit/badge/?version=latest)](https://lessonkit.readthedocs.io/en/latest/)
4
- [![License](https://img.shields.io/github/license/eddiethedean/lessonkit)](https://github.com/eddiethedean/lessonkit/blob/main/LICENSE)
3
+ Vite + React scaffold for new LessonKit courses. Created by `lessonkit init`.
5
4
 
6
- Starter template copied by `lessonkit init`. See the [CLI reference](https://lessonkit.readthedocs.io/en/latest/reference/cli.html) and [React quickstart](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/quickstart.html).
7
-
8
- ## Run
5
+ ## Commands
9
6
 
10
7
  ```bash
11
8
  npm install
12
- npm run dev
9
+ npm run dev # lessonkit dev
10
+ npm run build # lessonkit build
11
+ npm run package:scorm12
13
12
  ```
14
13
 
15
- ## Notes
14
+ ## Files
15
+
16
+ - `src/App.tsx` — course UI (IDs match `lessonkit.json`)
17
+ - `lessonkit.json` — manifest for CLI and LXPack packaging
18
+
19
+ ## Docs
16
20
 
17
- - Depends on [`@lessonkit/react`](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/components-and-hooks.html).
18
- - Copied by [`@lessonkit/cli`](https://lessonkit.readthedocs.io/en/latest/reference/cli.html) when you run `lessonkit init`.
19
- - Package for an LMS with the [packaging guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/packaging-and-cli.html).
21
+ [CLI reference](https://lessonkit.readthedocs.io/en/latest/reference/cli.html) · [React quickstart](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/quickstart.html) · [Packaging guide](https://lessonkit.readthedocs.io/en/latest/guides/react-developers/packaging-and-cli.html)
@@ -15,7 +15,12 @@
15
15
  "passingScore": 1
16
16
  }
17
17
  ],
18
- "theme": { "preset": "default" }
18
+ "theme": { "preset": "default" },
19
+ "tracking": {
20
+ "xapi": {
21
+ "activityIri": "https://example.com/courses/my-course"
22
+ }
23
+ }
19
24
  },
20
25
  "paths": {
21
26
  "spaDistDir": "dist",
@@ -13,16 +13,16 @@
13
13
  "test:coverage": "vitest run --coverage --passWithNoTests=false"
14
14
  },
15
15
  "dependencies": {
16
- "@lessonkit/core": "^0.9.3",
17
- "@lessonkit/react": "^0.9.3",
18
- "@lessonkit/themes": "^0.9.3",
19
- "@lessonkit/xapi": "^0.9.3",
16
+ "@lessonkit/core": "^1.0.1",
17
+ "@lessonkit/react": "^1.0.1",
18
+ "@lessonkit/themes": "^1.0.1",
19
+ "@lessonkit/xapi": "^1.0.1",
20
20
  "react": "^18.3.1",
21
21
  "react-dom": "^18.3.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@lessonkit/cli": "^0.9.3",
25
- "@lessonkit/lxpack": "^0.9.3",
24
+ "@lessonkit/cli": "^1.0.1",
25
+ "@lessonkit/lxpack": "^1.0.1",
26
26
  "@testing-library/react": "^16.3.0",
27
27
  "@types/react": "^18.3.23",
28
28
  "@types/react-dom": "^18.3.7",
@@ -1 +0,0 @@
1
- html,body{height:100%}body{margin:0;background:var(--lk-color-background);color:var(--lk-color-foreground);font-family:var(--lk-font-family);font-size:var(--lk-font-size-base);line-height:var(--lk-line-height-base)}.app-shell{margin:0 auto;padding:var(--lk-space-xl) var(--lk-space-lg);max-width:720px}section,article{border:1px solid var(--lk-color-border);border-radius:var(--lk-radius-lg);padding:var(--lk-space-lg);margin:var(--lk-space-md) 0;background:var(--lk-color-panel);box-shadow:var(--lk-shadow-md)}h1,h2{margin:0 0 var(--lk-space-sm);font-weight:var(--lk-font-weight-strong)}button{border:1px solid var(--lk-color-border);background:var(--lk-color-panel);color:var(--lk-color-foreground);border-radius:var(--lk-radius-md);padding:var(--lk-space-sm) var(--lk-space-md);font-weight:var(--lk-font-weight-strong);cursor:pointer}input[type=radio]{accent-color:var(--lk-color-primary)}label{display:block;margin:var(--lk-space-xs) 0}