@open-slide/core 1.2.0 → 1.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.
Files changed (47) hide show
  1. package/dist/{build-6BeQ3cxb.js → build-_276DMmJ.js} +2 -2
  2. package/dist/cli/bin.js +5 -5
  3. package/dist/{config-AxZ5OE1u.js → config-BAwKWNtW.js} +215 -18
  4. package/dist/{config-CtT8K4VF.d.ts → config-D9cZ1A0X.d.ts} +2 -1
  5. package/dist/{dev-C9eLmUEq.js → dev-BoqeVXVq.js} +2 -2
  6. package/dist/en-CDKzoZvf.js +351 -0
  7. package/dist/index.d.ts +4 -3
  8. package/dist/index.js +229 -39
  9. package/dist/locale/index.d.ts +1 -1
  10. package/dist/locale/index.js +97 -333
  11. package/dist/{preview-Cunm-f4i.js → preview-BLPxspc9.js} +2 -2
  12. package/dist/sync-j9_QPovT.js +3 -0
  13. package/dist/{types-CRHIeoNq.d.ts → types-JYG1cmwC.d.ts} +31 -1
  14. package/dist/vite/index.d.ts +2 -2
  15. package/dist/vite/index.js +2 -2
  16. package/package.json +9 -1
  17. package/skills/create-slide/SKILL.md +1 -1
  18. package/skills/create-theme/SKILL.md +60 -12
  19. package/skills/slide-authoring/SKILL.md +11 -0
  20. package/src/app/app.tsx +11 -1
  21. package/src/app/components/asset-view.tsx +1 -13
  22. package/src/app/components/image-placeholder.tsx +123 -1
  23. package/src/app/components/inspector/inspector-panel.tsx +123 -10
  24. package/src/app/components/sidebar/folder-item.tsx +16 -5
  25. package/src/app/components/sidebar/mobile-pill.tsx +34 -0
  26. package/src/app/components/sidebar/sidebar.tsx +10 -0
  27. package/src/app/components/themes/theme-detail.tsx +300 -0
  28. package/src/app/components/themes/themes-gallery.tsx +146 -0
  29. package/src/app/components/thumbnail-rail.tsx +17 -5
  30. package/src/app/lib/assets.ts +55 -2
  31. package/src/app/lib/sdk.ts +1 -0
  32. package/src/app/lib/slides.ts +10 -1
  33. package/src/app/lib/themes.ts +22 -0
  34. package/src/app/lib/use-agent-socket.ts +18 -0
  35. package/src/app/routes/home-shell.tsx +173 -0
  36. package/src/app/routes/home.tsx +89 -207
  37. package/src/app/routes/slide.tsx +144 -14
  38. package/src/app/routes/themes.tsx +34 -0
  39. package/src/app/virtual.d.ts +20 -0
  40. package/src/locale/en.ts +35 -3
  41. package/src/locale/ja.ts +36 -3
  42. package/src/locale/types.ts +33 -1
  43. package/src/locale/zh-cn.ts +35 -3
  44. package/src/locale/zh-tw.ts +35 -3
  45. package/dist/sync-B4eLo2H6.js +0 -3
  46. /package/dist/{design-C13iz9_4.js → design-cpzS8aud.js} +0 -0
  47. /package/dist/{sync-3oqN1WyK.js → sync-BCJDRIqo.js} +0 -0
@@ -1,5 +1,5 @@
1
- import "./design-C13iz9_4.js";
2
- import { createViteConfig } from "./config-AxZ5OE1u.js";
1
+ import "./design-cpzS8aud.js";
2
+ import { createViteConfig } from "./config-BAwKWNtW.js";
3
3
  import path from "node:path";
4
4
  import { build as build$1, mergeConfig } from "vite";
5
5
 
package/dist/cli/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { detectSkillsDrift, syncSkills } from "../sync-3oqN1WyK.js";
2
+ import { detectSkillsDrift, syncSkills } from "../sync-BCJDRIqo.js";
3
3
  import chalk from "chalk";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import path from "node:path";
@@ -57,19 +57,19 @@ async function run(argv) {
57
57
  program.name("open-slide").description("Author slides — we handle the Vite/React stack.").version(version, "-v, --version", "print version").helpOption("-h, --help", "show help").showHelpAfterError(chalk.dim("(run `open-slide --help` for usage)"));
58
58
  program.command("dev").description("Start the dev server").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").option("--no-skills-check", "skip the built-in skills drift check").action(async (flags) => {
59
59
  if (flags.skillsCheck !== false) await runSkillsDriftCheck(resolveBuiltinSkillsDir());
60
- const { dev } = await import("../dev-C9eLmUEq.js");
60
+ const { dev } = await import("../dev-BoqeVXVq.js");
61
61
  await dev(flags);
62
62
  });
63
63
  program.command("build").description("Build a static site").option("--out-dir <dir>", "output directory (defaults to `dist`)").action(async (flags) => {
64
- const { build } = await import("../build-6BeQ3cxb.js");
64
+ const { build } = await import("../build-_276DMmJ.js");
65
65
  await build(flags);
66
66
  });
67
67
  program.command("preview").description("Preview the production build").addOption(new Option("-p, --port <port>", "port to listen on").argParser(parsePort)).addOption(new Option("--host [host]", "expose on the network (optional host)")).option("--open", "open the browser on start").action(async (flags) => {
68
- const { preview } = await import("../preview-Cunm-f4i.js");
68
+ const { preview } = await import("../preview-BLPxspc9.js");
69
69
  await preview(flags);
70
70
  });
71
71
  program.command("sync:skills").description("Sync built-in skills from @open-slide/core into this workspace").option("--dry-run", "show what would change without writing").action(async (flags) => {
72
- const { syncSkills: syncSkills$1 } = await import("../sync-B4eLo2H6.js");
72
+ const { syncSkills: syncSkills$1 } = await import("../sync-j9_QPovT.js");
73
73
  await syncSkills$1(resolveBuiltinSkillsDir(), flags);
74
74
  });
75
75
  await program.parseAsync(argv, { from: "user" });
@@ -1,4 +1,4 @@
1
- import { defaultDesign } from "./design-C13iz9_4.js";
1
+ import { defaultDesign } from "./design-cpzS8aud.js";
2
2
  import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -12,7 +12,7 @@ import * as t$1 from "@babel/types";
12
12
  import * as t from "@babel/types";
13
13
  import { isJSXElement, isJSXFragment } from "@babel/types";
14
14
  import fg from "fast-glob";
15
- import { loadConfigFromFile } from "vite";
15
+ import { loadConfigFromFile, normalizePath } from "vite";
16
16
 
17
17
  //#region src/vite/babel-walk.ts
18
18
  const SKIP_KEYS = new Set([
@@ -2505,7 +2505,7 @@ async function readFoldersManifest(file) {
2505
2505
  throw err;
2506
2506
  }
2507
2507
  }
2508
- function resolved(id) {
2508
+ function resolved$1(id) {
2509
2509
  return `\0${id}`;
2510
2510
  }
2511
2511
  async function findSlides(userCwd, slidesDir) {
@@ -2522,19 +2522,59 @@ function toId(absFile, slidesRoot) {
2522
2522
  const rel = path.relative(slidesRoot, absFile);
2523
2523
  return rel.split(path.sep)[0];
2524
2524
  }
2525
- function generateSlidesModule(files, slidesRoot, isDev) {
2526
- const entries = files.map((abs) => {
2525
+ const META_THEME_RE = /(?:^|[\s,{])theme\s*:\s*['"]([^'"]+)['"]/;
2526
+ function extractMetaTheme(src) {
2527
+ const metaStart = src.search(/export\s+const\s+meta\b/);
2528
+ if (metaStart === -1) return null;
2529
+ const eqIdx = src.indexOf("=", metaStart);
2530
+ if (eqIdx === -1) return null;
2531
+ const openBrace = src.indexOf("{", eqIdx);
2532
+ if (openBrace === -1) return null;
2533
+ let depth = 0;
2534
+ let closeBrace = -1;
2535
+ for (let i = openBrace; i < src.length; i++) {
2536
+ const ch = src[i];
2537
+ if (ch === "{") depth++;
2538
+ else if (ch === "}") {
2539
+ depth--;
2540
+ if (depth === 0) {
2541
+ closeBrace = i;
2542
+ break;
2543
+ }
2544
+ }
2545
+ }
2546
+ if (closeBrace === -1) return null;
2547
+ const body = src.slice(openBrace + 1, closeBrace);
2548
+ const m = body.match(META_THEME_RE);
2549
+ return m ? m[1] : null;
2550
+ }
2551
+ async function readSlideTheme(abs) {
2552
+ try {
2553
+ const src = await fs.readFile(abs, "utf8");
2554
+ return extractMetaTheme(src);
2555
+ } catch {
2556
+ return null;
2557
+ }
2558
+ }
2559
+ async function generateSlidesModule(files, slidesRoot, isDev) {
2560
+ const entries = await Promise.all(files.map(async (abs) => {
2527
2561
  const id = toId(abs, slidesRoot);
2528
2562
  const importPath = isDev ? `/@fs/${abs.replace(/^\/+/, "")}` : abs;
2563
+ const theme = await readSlideTheme(abs);
2529
2564
  return {
2530
2565
  id,
2531
- importPath
2566
+ importPath,
2567
+ theme
2532
2568
  };
2533
- });
2569
+ }));
2534
2570
  const ids = JSON.stringify(entries.map((e) => e.id).sort());
2571
+ const themesMap = {};
2572
+ for (const e of entries) if (e.theme) themesMap[e.id] = e.theme;
2573
+ const themesJson = JSON.stringify(themesMap);
2535
2574
  const cases = entries.map((e) => ` case ${JSON.stringify(e.id)}: return import(${JSON.stringify(e.importPath)});`).join("\n");
2536
2575
  return `// virtual:open-slide/slides — generated
2537
2576
  export const slideIds = ${ids};
2577
+ export const slideThemes = ${themesJson};
2538
2578
 
2539
2579
  export async function loadSlide(id) {
2540
2580
  switch (id) {
@@ -2557,17 +2597,17 @@ function openSlidePlugin(opts) {
2557
2597
  return { server: { fs: { allow: [userCwd] } } };
2558
2598
  },
2559
2599
  resolveId(id) {
2560
- if (id === SLIDES_VMOD) return resolved(SLIDES_VMOD);
2561
- if (id === CONFIG_VMOD) return resolved(CONFIG_VMOD);
2562
- if (id === FOLDERS_VMOD) return resolved(FOLDERS_VMOD);
2600
+ if (id === SLIDES_VMOD) return resolved$1(SLIDES_VMOD);
2601
+ if (id === CONFIG_VMOD) return resolved$1(CONFIG_VMOD);
2602
+ if (id === FOLDERS_VMOD) return resolved$1(FOLDERS_VMOD);
2563
2603
  return null;
2564
2604
  },
2565
2605
  async load(id) {
2566
- if (id === resolved(SLIDES_VMOD)) {
2606
+ if (id === resolved$1(SLIDES_VMOD)) {
2567
2607
  const files = await findSlides(userCwd, slidesDir);
2568
- return generateSlidesModule(files, slidesRoot, isDev);
2608
+ return await generateSlidesModule(files, slidesRoot, isDev);
2569
2609
  }
2570
- if (id === resolved(CONFIG_VMOD)) {
2610
+ if (id === resolved$1(CONFIG_VMOD)) {
2571
2611
  const userBuild = config.build ?? {};
2572
2612
  const buildResolved = isDev ? {
2573
2613
  showSlideBrowser: true,
@@ -2584,7 +2624,7 @@ function openSlidePlugin(opts) {
2584
2624
  };
2585
2625
  return `export default ${JSON.stringify(resolvedConfig)};\n`;
2586
2626
  }
2587
- if (id === resolved(FOLDERS_VMOD)) {
2627
+ if (id === resolved$1(FOLDERS_VMOD)) {
2588
2628
  const manifest = await readFoldersManifest(foldersManifestPath);
2589
2629
  return `export default ${JSON.stringify(manifest)};\n`;
2590
2630
  }
@@ -2603,24 +2643,36 @@ function openSlidePlugin(opts) {
2603
2643
  if (reloadTimer) clearTimeout(reloadTimer);
2604
2644
  reloadTimer = setTimeout(() => {
2605
2645
  reloadTimer = null;
2606
- const mod = server.moduleGraph.getModuleById(resolved(SLIDES_VMOD));
2646
+ const mod = server.moduleGraph.getModuleById(resolved$1(SLIDES_VMOD));
2607
2647
  if (mod) server.moduleGraph.invalidateModule(mod);
2608
2648
  server.ws.send({ type: "full-reload" });
2609
2649
  }, 150);
2610
2650
  };
2611
- server.watcher.add(path.join(slidesRoot, "*/index.{tsx,jsx,ts,js}"));
2651
+ if (existsSync(slidesRoot)) server.watcher.add(slidesRoot);
2612
2652
  server.watcher.on("add", (p) => {
2613
2653
  if (isSlideEntry(p)) reload();
2614
2654
  });
2615
2655
  server.watcher.on("unlink", (p) => {
2616
2656
  if (isSlideEntry(p)) reload();
2617
2657
  });
2658
+ let slideThemeTimer = null;
2659
+ const invalidateSlidesVmod = () => {
2660
+ if (slideThemeTimer) clearTimeout(slideThemeTimer);
2661
+ slideThemeTimer = setTimeout(() => {
2662
+ slideThemeTimer = null;
2663
+ const mod = server.moduleGraph.getModuleById(resolved$1(SLIDES_VMOD));
2664
+ if (mod) server.moduleGraph.invalidateModule(mod);
2665
+ }, 100);
2666
+ };
2667
+ server.watcher.on("change", (p) => {
2668
+ if (isSlideEntry(p)) invalidateSlidesVmod();
2669
+ });
2618
2670
  let foldersTimer = null;
2619
2671
  const invalidateFolders = () => {
2620
2672
  if (foldersTimer) clearTimeout(foldersTimer);
2621
2673
  foldersTimer = setTimeout(() => {
2622
2674
  foldersTimer = null;
2623
- const mod = server.moduleGraph.getModuleById(resolved(FOLDERS_VMOD));
2675
+ const mod = server.moduleGraph.getModuleById(resolved$1(FOLDERS_VMOD));
2624
2676
  if (mod) server.moduleGraph.invalidateModule(mod);
2625
2677
  }, 100);
2626
2678
  };
@@ -2647,6 +2699,144 @@ async function loadUserConfig(userCwd) {
2647
2699
  return loaded?.config ?? {};
2648
2700
  }
2649
2701
 
2702
+ //#endregion
2703
+ //#region src/vite/themes-plugin.ts
2704
+ const THEMES_VMOD = "virtual:open-slide/themes";
2705
+ function resolved(id) {
2706
+ return `\0${id}`;
2707
+ }
2708
+ const FM_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
2709
+ function parseFrontmatter(raw, themeId) {
2710
+ const match = raw.match(FM_RE);
2711
+ const fmText = match ? match[1] : "";
2712
+ const body = match ? match[2] : raw;
2713
+ const data = {};
2714
+ for (const line of fmText.split(/\r?\n/)) {
2715
+ const m = line.match(/^([A-Za-z0-9_-]+)\s*:\s*(.*)$/);
2716
+ if (!m) continue;
2717
+ let value = m[2].trim();
2718
+ if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
2719
+ data[m[1]] = value;
2720
+ }
2721
+ return {
2722
+ fm: {
2723
+ name: data.name || themeId,
2724
+ description: data.description || ""
2725
+ },
2726
+ body: body.trim()
2727
+ };
2728
+ }
2729
+ async function findThemes(userCwd, themesDir) {
2730
+ const abs = path.resolve(userCwd, themesDir);
2731
+ if (!existsSync(abs)) return [];
2732
+ const hits = await fg("*.md", {
2733
+ cwd: abs,
2734
+ absolute: true,
2735
+ onlyFiles: true
2736
+ });
2737
+ return hits.sort();
2738
+ }
2739
+ async function readTheme(mdAbs, themesRoot) {
2740
+ const id = path.basename(mdAbs, ".md");
2741
+ const raw = await fs.readFile(mdAbs, "utf8");
2742
+ const { fm, body } = parseFrontmatter(raw, id);
2743
+ const demoCandidates = [
2744
+ `${id}.demo.tsx`,
2745
+ `${id}.demo.jsx`,
2746
+ `${id}.demo.ts`,
2747
+ `${id}.demo.js`
2748
+ ];
2749
+ let demoAbs = null;
2750
+ for (const cand of demoCandidates) {
2751
+ const p = path.join(themesRoot, cand);
2752
+ if (existsSync(p)) {
2753
+ demoAbs = p;
2754
+ break;
2755
+ }
2756
+ }
2757
+ return {
2758
+ id,
2759
+ frontmatter: fm,
2760
+ body,
2761
+ demoAbs
2762
+ };
2763
+ }
2764
+ function generateThemesModule(themes, isDev) {
2765
+ const meta = themes.map((t$3) => ({
2766
+ id: t$3.id,
2767
+ name: t$3.frontmatter.name,
2768
+ description: t$3.frontmatter.description,
2769
+ body: t$3.body,
2770
+ hasDemo: t$3.demoAbs !== null
2771
+ }));
2772
+ const cases = themes.flatMap((t$3) => {
2773
+ const abs = t$3.demoAbs;
2774
+ if (!abs) return [];
2775
+ const importPath = isDev ? `/@fs/${normalizePath(abs).replace(/^\/+/, "")}` : abs;
2776
+ return [` case ${JSON.stringify(t$3.id)}: return import(${JSON.stringify(importPath)});`];
2777
+ }).join("\n");
2778
+ return `// virtual:open-slide/themes — generated
2779
+ export const themes = ${JSON.stringify(meta)};
2780
+
2781
+ export async function loadThemeDemo(id) {
2782
+ switch (id) {
2783
+ ${cases}
2784
+ default: throw new Error('Theme demo not found: ' + id);
2785
+ }
2786
+ }
2787
+ `;
2788
+ }
2789
+ function themesPlugin(opts) {
2790
+ const { userCwd, config } = opts;
2791
+ const themesDir = config.themesDir ?? "themes";
2792
+ const themesRoot = path.resolve(userCwd, themesDir);
2793
+ let isDev = false;
2794
+ return {
2795
+ name: "open-slide:themes",
2796
+ config(_c, env) {
2797
+ isDev = env.command === "serve";
2798
+ },
2799
+ resolveId(id) {
2800
+ if (id === THEMES_VMOD) return resolved(THEMES_VMOD);
2801
+ return null;
2802
+ },
2803
+ async load(id) {
2804
+ if (id !== resolved(THEMES_VMOD)) return null;
2805
+ const files = await findThemes(userCwd, themesDir);
2806
+ const themes = await Promise.all(files.map((f) => readTheme(f, themesRoot)));
2807
+ return generateThemesModule(themes, isDev);
2808
+ },
2809
+ configureServer(server) {
2810
+ const isThemeFile = (p) => {
2811
+ const rel = path.relative(themesRoot, p);
2812
+ if (rel.startsWith("..") || path.isAbsolute(rel)) return false;
2813
+ if (rel.includes(path.sep)) return false;
2814
+ return /\.(md|demo\.(tsx|jsx|ts|js))$/.test(rel);
2815
+ };
2816
+ let reloadTimer = null;
2817
+ const reload = () => {
2818
+ if (reloadTimer) clearTimeout(reloadTimer);
2819
+ reloadTimer = setTimeout(() => {
2820
+ reloadTimer = null;
2821
+ const mod = server.moduleGraph.getModuleById(resolved(THEMES_VMOD));
2822
+ if (mod) server.moduleGraph.invalidateModule(mod);
2823
+ server.ws.send({ type: "full-reload" });
2824
+ }, 150);
2825
+ };
2826
+ if (existsSync(themesRoot)) server.watcher.add(themesRoot);
2827
+ server.watcher.on("add", (p) => {
2828
+ if (isThemeFile(p)) reload();
2829
+ });
2830
+ server.watcher.on("unlink", (p) => {
2831
+ if (isThemeFile(p)) reload();
2832
+ });
2833
+ server.watcher.on("change", (p) => {
2834
+ if (isThemeFile(p)) reload();
2835
+ });
2836
+ }
2837
+ };
2838
+ }
2839
+
2650
2840
  //#endregion
2651
2841
  //#region src/vite/config.ts
2652
2842
  function findPackageRoot(fromFile) {
@@ -2663,7 +2853,9 @@ async function createViteConfig(opts) {
2663
2853
  const userCwd = path.resolve(opts.userCwd);
2664
2854
  const config = opts.config ?? await loadUserConfig(userCwd);
2665
2855
  const slidesDir = config.slidesDir ?? "slides";
2856
+ const themesDir = config.themesDir ?? "themes";
2666
2857
  const slidesAbs = path.resolve(userCwd, slidesDir);
2858
+ const themesAbs = path.resolve(userCwd, themesDir);
2667
2859
  return {
2668
2860
  root: APP_ROOT,
2669
2861
  configFile: false,
@@ -2679,6 +2871,10 @@ async function createViteConfig(opts) {
2679
2871
  userCwd,
2680
2872
  config
2681
2873
  }),
2874
+ themesPlugin({
2875
+ userCwd,
2876
+ config
2877
+ }),
2682
2878
  designPlugin({ userCwd }),
2683
2879
  commentsPlugin({
2684
2880
  userCwd,
@@ -2728,7 +2924,8 @@ async function createViteConfig(opts) {
2728
2924
  fs: { allow: [
2729
2925
  APP_ROOT,
2730
2926
  userCwd,
2731
- slidesAbs
2927
+ slidesAbs,
2928
+ themesAbs
2732
2929
  ] }
2733
2930
  },
2734
2931
  build: {
@@ -1,4 +1,4 @@
1
- import { Locale } from "./types-CRHIeoNq.js";
1
+ import { Locale } from "./types-JYG1cmwC.js";
2
2
 
3
3
  //#region src/config.d.ts
4
4
  type OpenSlideBuildConfig = {
@@ -8,6 +8,7 @@ type OpenSlideBuildConfig = {
8
8
  };
9
9
  type OpenSlideConfig = {
10
10
  slidesDir?: string;
11
+ themesDir?: string;
11
12
  port?: number;
12
13
  locale?: Locale;
13
14
  build?: OpenSlideBuildConfig;
@@ -1,5 +1,5 @@
1
- import "./design-C13iz9_4.js";
2
- import { createViteConfig } from "./config-AxZ5OE1u.js";
1
+ import "./design-cpzS8aud.js";
2
+ import { createViteConfig } from "./config-BAwKWNtW.js";
3
3
  import { createServer, mergeConfig } from "vite";
4
4
 
5
5
  //#region src/cli/dev.ts