@open-aippt/core 1.13.2

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 (142) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/bin.js +2 -0
  4. package/dist/build-DxTqmvsO.js +17 -0
  5. package/dist/cli/bin.d.ts +1 -0
  6. package/dist/cli/bin.js +86 -0
  7. package/dist/config-CjzqjrEA.js +4280 -0
  8. package/dist/config-DIC-yVPp.d.ts +23 -0
  9. package/dist/design-cpzS8aud.js +35 -0
  10. package/dist/dev-BYuTeJbA.js +20 -0
  11. package/dist/format-BCeKbTOM.js +1605 -0
  12. package/dist/index.d.ts +134 -0
  13. package/dist/index.js +467 -0
  14. package/dist/locale/index.d.ts +24 -0
  15. package/dist/locale/index.js +3 -0
  16. package/dist/preview-DlQvnJPq.js +18 -0
  17. package/dist/sync-BPZ0m27m.js +139 -0
  18. package/dist/sync-EsYusbbL.js +3 -0
  19. package/dist/types-CHmFPIG_.d.ts +430 -0
  20. package/dist/vite/index.d.ts +14 -0
  21. package/dist/vite/index.js +4 -0
  22. package/env.d.ts +59 -0
  23. package/package.json +103 -0
  24. package/skills/apply-comments/SKILL.md +83 -0
  25. package/skills/create-slide/SKILL.md +91 -0
  26. package/skills/create-theme/SKILL.md +250 -0
  27. package/skills/current-slide/SKILL.md +110 -0
  28. package/skills/slide-authoring/SKILL.md +625 -0
  29. package/src/app/app.tsx +47 -0
  30. package/src/app/components/asset-view.tsx +966 -0
  31. package/src/app/components/history-provider.tsx +120 -0
  32. package/src/app/components/image-placeholder.tsx +243 -0
  33. package/src/app/components/inspector/asset-picker-dialog.tsx +196 -0
  34. package/src/app/components/inspector/comment-widget.tsx +93 -0
  35. package/src/app/components/inspector/image-crop-dialog.tsx +212 -0
  36. package/src/app/components/inspector/inspect-overlay.tsx +387 -0
  37. package/src/app/components/inspector/inspector-panel.tsx +1115 -0
  38. package/src/app/components/inspector/inspector-provider.tsx +1218 -0
  39. package/src/app/components/inspector/save-bar.tsx +48 -0
  40. package/src/app/components/language-toggle.tsx +39 -0
  41. package/src/app/components/notes-drawer.tsx +120 -0
  42. package/src/app/components/overview-grid.tsx +363 -0
  43. package/src/app/components/panel/panel-fields.tsx +60 -0
  44. package/src/app/components/panel/panel-shell.tsx +80 -0
  45. package/src/app/components/panel/save-card.tsx +142 -0
  46. package/src/app/components/pdf-progress-toast.tsx +32 -0
  47. package/src/app/components/player.tsx +466 -0
  48. package/src/app/components/pptx-progress-toast.tsx +32 -0
  49. package/src/app/components/present/blackout-overlay.tsx +18 -0
  50. package/src/app/components/present/control-bar.tsx +315 -0
  51. package/src/app/components/present/help-overlay.tsx +57 -0
  52. package/src/app/components/present/jump-input.tsx +74 -0
  53. package/src/app/components/present/laser-pointer.tsx +39 -0
  54. package/src/app/components/present/progress-bar.tsx +26 -0
  55. package/src/app/components/present/use-idle.ts +46 -0
  56. package/src/app/components/present/use-pointer-near-bottom.ts +34 -0
  57. package/src/app/components/present/use-presenter-channel.ts +66 -0
  58. package/src/app/components/present/use-touch-swipe.ts +66 -0
  59. package/src/app/components/shared-element.tsx +48 -0
  60. package/src/app/components/sidebar/folder-item.tsx +258 -0
  61. package/src/app/components/sidebar/icon-picker.tsx +61 -0
  62. package/src/app/components/sidebar/mobile-pill.tsx +34 -0
  63. package/src/app/components/sidebar/sidebar-footer.tsx +105 -0
  64. package/src/app/components/sidebar/sidebar.tsx +284 -0
  65. package/src/app/components/slide-canvas.tsx +102 -0
  66. package/src/app/components/slide-transition-layer.tsx +844 -0
  67. package/src/app/components/style-panel/design-provider.tsx +148 -0
  68. package/src/app/components/style-panel/style-panel.tsx +349 -0
  69. package/src/app/components/style-panel/use-design.ts +112 -0
  70. package/src/app/components/theme-toggle.tsx +59 -0
  71. package/src/app/components/themes/theme-detail.tsx +305 -0
  72. package/src/app/components/themes/themes-gallery.tsx +149 -0
  73. package/src/app/components/thumbnail-rail.tsx +805 -0
  74. package/src/app/components/ui/badge.tsx +45 -0
  75. package/src/app/components/ui/button.tsx +99 -0
  76. package/src/app/components/ui/card.tsx +92 -0
  77. package/src/app/components/ui/context-menu.tsx +237 -0
  78. package/src/app/components/ui/dialog.tsx +157 -0
  79. package/src/app/components/ui/dropdown-menu.tsx +245 -0
  80. package/src/app/components/ui/input.tsx +25 -0
  81. package/src/app/components/ui/label.tsx +24 -0
  82. package/src/app/components/ui/popover.tsx +75 -0
  83. package/src/app/components/ui/progress.tsx +31 -0
  84. package/src/app/components/ui/scroll-area.tsx +53 -0
  85. package/src/app/components/ui/select.tsx +196 -0
  86. package/src/app/components/ui/separator.tsx +28 -0
  87. package/src/app/components/ui/slider.tsx +61 -0
  88. package/src/app/components/ui/sonner.tsx +48 -0
  89. package/src/app/components/ui/tabs.tsx +79 -0
  90. package/src/app/components/ui/textarea.tsx +22 -0
  91. package/src/app/components/ui/toggle-group.tsx +83 -0
  92. package/src/app/components/ui/toggle.tsx +45 -0
  93. package/src/app/components/ui/tooltip.tsx +58 -0
  94. package/src/app/favicon.ico +0 -0
  95. package/src/app/index.html +13 -0
  96. package/src/app/lib/assets.ts +242 -0
  97. package/src/app/lib/design-presets.ts +94 -0
  98. package/src/app/lib/design.ts +58 -0
  99. package/src/app/lib/export-html.ts +326 -0
  100. package/src/app/lib/export-pdf.ts +298 -0
  101. package/src/app/lib/export-pptx.ts +284 -0
  102. package/src/app/lib/folders.ts +239 -0
  103. package/src/app/lib/inspector/fiber.test.ts +154 -0
  104. package/src/app/lib/inspector/fiber.ts +85 -0
  105. package/src/app/lib/inspector/use-comments.ts +74 -0
  106. package/src/app/lib/inspector/use-editor.ts +73 -0
  107. package/src/app/lib/inspector/use-notes.ts +134 -0
  108. package/src/app/lib/locale-store.ts +67 -0
  109. package/src/app/lib/page-context.tsx +38 -0
  110. package/src/app/lib/print-ready.test.ts +32 -0
  111. package/src/app/lib/print-ready.ts +51 -0
  112. package/src/app/lib/sdk.test.ts +13 -0
  113. package/src/app/lib/sdk.ts +37 -0
  114. package/src/app/lib/slides.ts +26 -0
  115. package/src/app/lib/step-context.tsx +261 -0
  116. package/src/app/lib/themes.ts +22 -0
  117. package/src/app/lib/transition.ts +30 -0
  118. package/src/app/lib/use-agent-socket.ts +18 -0
  119. package/src/app/lib/use-click-page-navigation.ts +60 -0
  120. package/src/app/lib/use-is-mobile.ts +21 -0
  121. package/src/app/lib/use-locale.ts +8 -0
  122. package/src/app/lib/use-prefers-reduced-motion.ts +19 -0
  123. package/src/app/lib/use-slide-module.ts +48 -0
  124. package/src/app/lib/use-wheel-page-navigation.ts +99 -0
  125. package/src/app/lib/utils.test.ts +25 -0
  126. package/src/app/lib/utils.ts +6 -0
  127. package/src/app/main.tsx +14 -0
  128. package/src/app/routes/assets.tsx +9 -0
  129. package/src/app/routes/home-shell.tsx +213 -0
  130. package/src/app/routes/home.tsx +807 -0
  131. package/src/app/routes/presenter.tsx +418 -0
  132. package/src/app/routes/slide.tsx +1108 -0
  133. package/src/app/routes/themes.tsx +34 -0
  134. package/src/app/styles.css +429 -0
  135. package/src/app/virtual.d.ts +51 -0
  136. package/src/locale/en.ts +416 -0
  137. package/src/locale/format.ts +12 -0
  138. package/src/locale/index.ts +6 -0
  139. package/src/locale/ja.ts +422 -0
  140. package/src/locale/types.ts +443 -0
  141. package/src/locale/zh-cn.ts +414 -0
  142. package/src/locale/zh-tw.ts +414 -0
package/package.json ADDED
@@ -0,0 +1,103 @@
1
+ {
2
+ "name": "@open-aippt/core",
3
+ "version": "1.13.2",
4
+ "description": "Runtime and CLI for open-aippt — write slides in slides/, we handle the rest.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "import": "./dist/index.js"
10
+ },
11
+ "./vite": {
12
+ "types": "./dist/vite/index.d.ts",
13
+ "import": "./dist/vite/index.js"
14
+ },
15
+ "./locale": {
16
+ "types": "./dist/locale/index.d.ts",
17
+ "import": "./dist/locale/index.js"
18
+ },
19
+ "./env": {
20
+ "types": "./env.d.ts"
21
+ }
22
+ },
23
+ "bin": {
24
+ "open-aippt": "./bin.js"
25
+ },
26
+ "files": [
27
+ "bin.js",
28
+ "dist",
29
+ "env.d.ts",
30
+ "src/app",
31
+ "src/locale",
32
+ "skills",
33
+ "README.md"
34
+ ],
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "keywords": [
39
+ "slides",
40
+ "presentation",
41
+ "react",
42
+ "vite"
43
+ ],
44
+ "license": "MIT",
45
+ "author": {
46
+ "name": "aibabelx",
47
+ "url": "https://github.com/aibabelx"
48
+ },
49
+ "homepage": "https://open-aippt.dev",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/aibabelx/open-aippt.git",
53
+ "directory": "packages/core"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/aibabelx/open-aippt/issues"
57
+ },
58
+ "publishConfig": {
59
+ "access": "public"
60
+ },
61
+ "dependencies": {
62
+ "@babel/parser": "^7.29.2",
63
+ "@babel/types": "^7.29.0",
64
+ "@dnd-kit/core": "^6.3.1",
65
+ "@dnd-kit/sortable": "^10.0.0",
66
+ "@dnd-kit/utilities": "^3.2.2",
67
+ "@fontsource-variable/geist": "^5.2.8",
68
+ "@tailwindcss/vite": "^4.2.2",
69
+ "@vitejs/plugin-react": "^4.3.3",
70
+ "chalk": "^5.3.0",
71
+ "class-variance-authority": "^0.7.1",
72
+ "clsx": "^2.1.1",
73
+ "commander": "^15.0.0",
74
+ "emoji-picker-react": "^4.19.1",
75
+ "fast-glob": "^3.3.2",
76
+ "fflate": "^0.8.2",
77
+ "html-to-image": "^1.11.13",
78
+ "lucide-react": "^1.8.0",
79
+ "next-themes": "^0.4.6",
80
+ "radix-ui": "^1.4.3",
81
+ "react": "^18.3.1",
82
+ "react-dom": "^18.3.1",
83
+ "react-image-crop": "^11.0.10",
84
+ "react-router-dom": "^7.18.0",
85
+ "shadcn": "^4.11.0",
86
+ "sonner": "^2.0.7",
87
+ "tailwind-merge": "^3.5.0",
88
+ "tailwindcss": "^4.2.2",
89
+ "tw-animate-css": "^1.4.0",
90
+ "vite": "^5.4.10"
91
+ },
92
+ "devDependencies": {
93
+ "@types/node": "^22.19.17",
94
+ "@types/react": "^18.3.12",
95
+ "@types/react-dom": "^18.3.1",
96
+ "tsdown": "^0.9.9",
97
+ "typescript": "^5.9.3"
98
+ },
99
+ "scripts": {
100
+ "build": "tsdown",
101
+ "typecheck": "tsc --noEmit"
102
+ }
103
+ }
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: apply-comments
3
+ description: Apply pending @slide-comment markers written by the open-aippt inspector tool. Use when the user asks to "apply comments", "process slide comments", "apply the inspector comments", or references markers left inside `slides/<id>/index.tsx`.
4
+ ---
5
+
6
+ # Apply slide comments
7
+
8
+ The open-aippt editor has an inspector tool that lets the user click on a rendered page element and attach a textual comment (e.g. *"make this red"*, *"change to 'open-aippt Rocks'"*). Each comment is persisted as an in-source JSX marker inside `slides/<slideId>/index.tsx`.
9
+
10
+ Your job: read those markers, perform the described edits, and delete the markers.
11
+
12
+ > **Before making any page edit**, consult the **`slide-authoring`** skill — it is the technical reference for how `slides/<id>/index.tsx` is structured (canvas, type scale, palette, assets, file contract). A comment like *"make this bigger"* or *"change the accent colour"* should be applied in a way that stays consistent with those rules.
13
+
14
+ ## Marker format
15
+
16
+ ```
17
+ {/* @slide-comment id="c-<8hex>" ts="<ISO>" text="<base64url(JSON)>" */}
18
+ ```
19
+
20
+ - Always sits on its own line as the **first child inside** the JSX element it refers to (i.e. between that element's opening `>` and its other children). The marker is dropped *into* its target, not floated above it.
21
+ - `text` is base64url-encoded JSON: `{"note": "...", "hint"?: "..."}`.
22
+ - Detection regex (authoritative — use exactly this):
23
+
24
+ ```
25
+ /\{\/\*\s*@slide-comment\s+id="(c-[a-f0-9]+)"\s+ts="([^"]+)"\s+text="([A-Za-z0-9_\-]+={0,2})"\s*\*\/\}/g
26
+ ```
27
+
28
+ ## Procedure
29
+
30
+ 1. **Identify the target slide(s).**
31
+ - If the user names one (`example-slide`, `youbike-3-survey`, etc.), work on that single `slides/<slideId>/index.tsx`.
32
+ - If they say "all" or don't specify, scan every `slides/*/index.tsx`. Process each slide one at a time.
33
+
34
+ 2. **Read the file and find all markers.**
35
+ - Run the regex above against the whole file.
36
+ - For each match, base64url-decode `text` and `JSON.parse` it to get `{ note, hint? }`.
37
+ - Record each hit as `{ id, lineIndex (0-based), indent, note, hint }`.
38
+ - If there are no markers, tell the user and stop.
39
+
40
+ 3. **Understand each comment in context.**
41
+ - The targeted JSX element is the **enclosing** element of the marker — i.e. read upward from the marker line until you reach the unclosed JSX opening tag whose body the marker lives in. That element is the target. (For self-closing elements like `<img />`, the inspector hoists the marker to the nearest non-self-closing ancestor; in that case the comment usually refers to a child of the enclosing element rather than the enclosing element itself — use the `note` text to disambiguate.)
42
+ - Read enough surrounding code (parent element, sibling elements, inline styles) to apply the change faithfully. A comment inside a `<div>` with an inline `background` style usually refers to that element's styling, for example.
43
+ - If the `note` is ambiguous, do the smallest reasonable interpretation and mention the assumption in your summary.
44
+
45
+ 4. **Apply edits in reverse line order.**
46
+ - Sort markers by descending `lineIndex` and process one at a time, using the `Edit` tool.
47
+ - Processing top-down would invalidate line numbers for later markers as the file shrinks/grows.
48
+
49
+ 5. **Remove each marker after applying its edit.**
50
+ - Delete the entire marker line including its trailing `\n`.
51
+ - Never leave a marker behind. An un-removed marker signals a failure.
52
+
53
+ 6. **Verify.**
54
+ - After all edits, re-read the file and confirm zero remaining markers.
55
+ - Run `pnpm tsc --noEmit` and `pnpm biome check` (or `pnpm lint`). Fix any introduced errors.
56
+
57
+ 7. **Report.**
58
+ - Summarise: `N applied, 0 remaining` plus a one-line description of each change (including the slide id).
59
+
60
+ ## base64url decoding helper
61
+
62
+ ```js
63
+ function decode(s) {
64
+ const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4));
65
+ return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/') + pad, 'base64').toString('utf8');
66
+ }
67
+ ```
68
+
69
+ You can run this inline via `node -e '...'` if you need to inspect a payload; otherwise just reason about the decoded string.
70
+
71
+ ## Edge cases
72
+
73
+ - **Marker with no enclosing JSX element** (shouldn't happen — the inspector won't write one — but if you find one): delete it and note as orphan.
74
+ - **Multiple markers stacked on consecutive lines inside the same element**: they all refer to that enclosing element. Apply them in source order but still delete each line individually.
75
+ - **`_debugSource` used SWC instead of Babel**: not your problem — the marker line is authoritative.
76
+ - **Comment asks for something outside the target element's scope** (e.g. "add a new page"): do the closest-reasonable edit and mention the scope expansion in your summary.
77
+ - **Can't resolve the comment** (e.g. truly ambiguous, or the file changed shape such that the target element doesn't exist): leave the marker in place and report it as skipped. Don't guess.
78
+
79
+ ## Do not
80
+
81
+ - Do not touch `package.json`, `open-aippt.config.ts`, or files outside `slides/`.
82
+ - Do not add dependencies.
83
+ - Do not re-introduce markers or leave `TODO` breadcrumbs — the user already has a record in git.
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: create-slide
3
+ description: Use this skill when the user wants to create, draft, author, or generate new slides / a presentation in this open-aippt repo. Triggers on phrases like "make slides about X", "create a presentation", "draft slides for", "new slide", or when the user asks to add content under `slides/`. Do NOT use for editing the framework itself — only for authoring content inside `slides/<id>/`.
4
+ ---
5
+
6
+ # Create a slide in open-aippt
7
+
8
+ This skill owns the **workflow** for drafting a new deck. The technical reference — file contract, 1920×1080 canvas, type scale, palette, layout, assets — lives in the **`slide-authoring`** skill. Read that skill whenever you need details on *how* a page is structured. This skill assumes you'll consult it before writing code.
9
+
10
+ You only write files under `slides/<id>/`. Never modify `package.json`, `open-aippt.config.ts`, or existing slides.
11
+
12
+ ## Step 1 — Pick a theme
13
+
14
+ List files under `themes/`. If any theme markdown files exist (anything other than `README.md`), call `AskUserQuestion` with each theme id as an option plus a final **"no theme — design from scratch"** option.
15
+
16
+ - If the user picks a theme: read `themes/<id>.md` end-to-end. The theme's palette, typography, layout, and Title/Footer components are now authoritative — copy them directly into the slide. **Also set `theme: '<theme-id>'` on the `meta` export in `index.tsx`** (e.g. `export const meta: SlideMeta = { title: '…', theme: '<theme-id>' };`) so the slide back-links to the theme (chip on the slide card + listing on `/themes/<id>`). In Step 2, skip the **aesthetic direction** question (the theme already commits to one direction); you still need the topic itself, so confirm it before moving on. Page count, text density, and motion are independent of theme — ask those normally.
17
+ - If the user picks "no theme", or `themes/` is empty (or contains only `README.md`): proceed to Step 2 unchanged.
18
+
19
+ If you skip the aesthetic question because a theme was picked, restate the theme name in Step 2 so the user can correct course before you start writing.
20
+
21
+ ## Step 2 — Clarify requirements (MUST ask before writing code)
22
+
23
+ **Before writing any code, lock in the four key style decisions below via `AskUserQuestion`.** They shape every downstream choice (layout, type scale, asset needs, motion code), so locking them in up front avoids rework. Only skip a question when the user's original message already gave an unambiguous answer for it — and if you skip, restate your assumption so they can correct it.
24
+
25
+ **Topic comes first.** A meaningful aesthetic recommendation requires knowing what the deck is about. If the user's initial request is thin ("make me a deck", "draft some slides"), make a *separate* `AskUserQuestion` call first to gather topic, audience, and any draft outline. Skip this only if the topic is already clear from the user's message — in which case restate your reading of the topic in the next call so they can correct course.
26
+
27
+ Then ask these four in a single `AskUserQuestion` call (multi-question form):
28
+
29
+ 1. **Aesthetic direction** — propose 3 visual directions tailored to *this* topic. Do **not** pull from a fixed preset list. Each option must combine a vibe word + a concrete visual cue (palette, typography, motif) so the user can picture it; bare labels like "minimal" or "corporate" alone are too vague. The three options should feel meaningfully different from each other — not three flavors of the same idea.
30
+
31
+ How options should shift with topic:
32
+ - *"Intro to Rust for backend engineers"* → **rust-orange technical editorial** (warm rust/charcoal, mono headings, code-grid layout) · **blueprint dev-doc** (cyan grid on near-black, monospace, schematic feel) · **brutalist terminal** (lime-on-black, ASCII rules, no-nonsense)
33
+ - *"Q2 product roadmap for stakeholders"* → **calm corporate clean** (off-white, single accent, generous whitespace) · **confident editorial** (large display serif, tight grid, one bold accent) · **data-forward dashboard** (charts as hero, muted neutrals + status colors)
34
+ - *"Kindergarten parent night"* → **playful crayon** (paper texture, hand-drawn accents, primary colors) · **soft pastel storybook** (peach/mint, rounded type, illustrated icons) · **warm photo-led** (full-bleed kid photos, simple captions)
35
+
36
+ Mark the option that best fits the topic and audience as "(Recommended)" so the user has a sensible default. (`AskUserQuestion` already auto-adds "Other" — don't add a generic catch-all yourself.)
37
+
38
+ 2. **Page count** — rough length. Offer brackets: 3–5 (short), 6–10 (standard), 11–20 (deep dive), custom.
39
+ 3. **Text density per page** — how much copy lives on each page? Offer: minimal (one line / big number), light (heading + 2–3 bullets), standard (heading + 4–5 bullets or short paragraph), dense (multi-column / detailed). This directly drives type scale and layout.
40
+ 4. **Motion** — does the user want CSS/React animations and transitions, or a fully static deck? Offer: static (no motion), subtle (fades / entrance only), rich (keyframes, staggered reveals, looping visuals). If animated, plan to use CSS `@keyframes` / inline `style` + `useEffect`; no extra libraries.
41
+
42
+ After those four, ask follow-ups **only if still unclear**: brand colors, required assets. Don't pad the conversation with questions already answered.
43
+
44
+ ## Step 3 — Pick a slide id
45
+
46
+ Use **kebab-case**, short, descriptive. Examples: `rust-intro`, `q2-roadmap`, `team-offsite-2026`. Check `slides/` to avoid collisions.
47
+
48
+ ## Step 4 — Plan the structure
49
+
50
+ Sketch the slide as a list of page roles before writing code. Common page types:
51
+
52
+ | Role | Purpose |
53
+ | ---------------- | --------------------------------------------- |
54
+ | Cover | Title + subtitle, strong visual |
55
+ | Agenda | What's coming (3–5 items) |
56
+ | Section divider | Big label between chapters |
57
+ | Content | Heading + 2–5 bullets OR heading + one visual |
58
+ | Big number | One statistic the size of the canvas |
59
+ | Quote | Pull-quote with attribution |
60
+ | Comparison | Two-column before/after or A vs B |
61
+ | Closing | CTA, thanks, contact |
62
+
63
+ **Rule of thumb**: one idea per page. If you're tempted to put two, split them.
64
+
65
+ If the deck topic naturally calls for specific real images the user must supply (product screenshots, team photos, customer dashboards), plan where those go and use `<ImagePlaceholder>` from `@open-aippt/core` — see the **Image placeholders** section in `slide-authoring`. Default is **no placeholders**: only insert one when a real image is genuinely required.
66
+
67
+ ## Step 5 — Commit to a visual direction
68
+
69
+ Pick one coherent palette / type scale / aesthetic and hold it across every page. The full set of constraints (palette structure, type scale, padding, aesthetic options) lives in `slide-authoring` — apply it.
70
+
71
+ **Default: declare a top-level `export const design: DesignSystem = { … }`** at the top of `index.tsx` (after imports) using the chosen palette / type scale, and reference the values via `var(--osd-X)` from inline styles. This keeps the slide tweakable from the Design panel after generation, which is what the user almost always wants. Only skip the `design` const for a one-off slide whose palette is intentionally locked and not meant to be re-themed — in that case, fall back to the local `palette` constants pattern. The "Design system" section of `slide-authoring` covers the format and available tokens.
72
+
73
+ Consult the `frontend-design` skill for deeper aesthetic guidance if the user wants something bold.
74
+
75
+ ## Step 6 — Write `slides/<id>/index.tsx`
76
+
77
+ Read the **`slide-authoring`** skill before writing — it covers the file contract, canvas rules, type scale, spacing, and asset imports, and it includes a starter template you can copy. Don't duplicate that knowledge here; use it.
78
+
79
+ ## Step 7 — Self-review
80
+
81
+ Run the checklist in `slide-authoring` ("Self-review before finishing"). It covers structural correctness, layout discipline, and asset existence.
82
+
83
+ ## Step 8 — Hand off to the user
84
+
85
+ Tell the user:
86
+
87
+ - The slide id and file path you created.
88
+ - That the dev server will hot-reload — they can open `http://localhost:5173/s/<id>` (or refresh the home page).
89
+ - If dev isn't running: `pnpm dev` from the repo root.
90
+
91
+ Don't run the dev server yourself unless asked.
@@ -0,0 +1,250 @@
1
+ ---
2
+ name: create-theme
3
+ description: Use this skill when the user wants to create, draft, author, or extract a slide theme in this open-aippt repo. Triggers on phrases like "create a theme", "make a theme called X", "extract a theme from <slide>", "build a theme from these images". Produces two paired files under `themes/` — `<id>.md` (palette, typography, layout, fixed Title/Footer components, motion) and `<id>.demo.tsx` (a runnable demo slide that the dev-UI Themes panel previews). Do NOT use for editing real slides — only for authoring the theme bundle.
4
+ ---
5
+
6
+ # Create a slide theme
7
+
8
+ This skill produces a **theme bundle** under `themes/`: two paired files that together describe a reusable visual identity.
9
+
10
+ 1. `themes/<id>.md` — agent-facing documentation: palette, typography, layout, fixed Title/Footer/Eyebrow components, motion. This is what `create-slide` reads when an author picks the theme.
11
+ 2. `themes/<id>.demo.tsx` — a runnable mini-slide (a normal slide module: `export default Page[]`) that demonstrates the theme on 2–3 pages. The dev UI's **Themes panel** loads this file and renders it as the theme's live preview.
12
+
13
+ Both files share the same stem so the runtime can pair them automatically.
14
+
15
+ A theme is **distinct from a slide's `design` const**. The theme markdown is authoring-time aesthetic direction (copied into a real slide's source by `create-slide`). The demo `.tsx` is a self-contained preview, not a real slide — it does not appear in the slides list. A per-slide `const design: DesignSystem = { … }` (declared at the top of `slides/<id>/index.tsx`) is the runtime tokens object the user can tweak from the Design panel. The markdown commits the *direction*; the per-slide `design` const makes the slide *tweakable*; the demo `.tsx` makes the theme *previewable*.
16
+
17
+ You only write files under `themes/<id>.md` and `themes/<id>.demo.tsx`. Never modify real slides or other configuration. The canvas / type-scale defaults that themes can override live in the **`slide-authoring`** skill — read it before writing the theme so your overrides are stated explicitly.
18
+
19
+ ## Step 1 — Identify the input source
20
+
21
+ A theme can be derived from any combination of three input shapes:
22
+
23
+ - **Image references** — paths or URLs to slide screenshots, mood-board images, brand assets.
24
+ - **Free-text description** — prose describing the desired palette, fonts, feel.
25
+ - **An existing slide** — `slides/<id>/index.tsx` whose visual identity should be lifted out into a reusable theme.
26
+
27
+ If the user's original message already specifies the inputs unambiguously, skip the question and proceed. Otherwise call `AskUserQuestion` (multi-select) so they can pick one or more sources, and ask follow-ups (paths, slide id, prose) only as needed.
28
+
29
+ ## Step 2 — Gather raw inputs
30
+
31
+ - **Images**: read each path with the `Read` tool (it accepts images). Note dominant colors as hex, type weight/style, layout rhythm, decorative motifs, and any recurring chrome (header bar, footer line, page numbers).
32
+ - **Text**: extract explicit tokens (hex codes, font names, motion verbs) and implicit tone words ("editorial", "playful", "brutalist"). Resolve vague language into concrete decisions before writing.
33
+ - **Existing slide**: read `slides/<id>/index.tsx` and pull:
34
+ - The `palette` object → Palette section.
35
+ - Font constants and any `font-size` patterns → Typography section.
36
+ - Padding / alignment patterns → Layout section.
37
+ - Recurring components (TrafficLights, Eyebrow, Footer-style helpers, WindowShell, …) → Fixed components section.
38
+ - `@keyframes` blocks and the shared `styles` string → Motion section.
39
+ - The aesthetic feel implied by the design → Aesthetic paragraph.
40
+
41
+ When inputs disagree (e.g. images use blue but the description says green), ask the user which to honor.
42
+
43
+ ## Step 3 — Pick a theme id
44
+
45
+ Use **kebab-case**, short, descriptive. Examples: `editorial-noir`, `brutalist-mono`, `pastel-soft`, `dev-terminal`. Check `themes/` to avoid collisions.
46
+
47
+ ## Step 4 — Write `themes/<id>.md`
48
+
49
+ Produce a file with this exact section order. Section bodies adapt to the theme; the headings stay consistent across all themes.
50
+
51
+ ````markdown
52
+ ---
53
+ name: <Human title, e.g. "Editorial Noir">
54
+ description: <one-line elevator pitch>
55
+ ---
56
+
57
+ # <Theme name>
58
+
59
+ ## Palette
60
+
61
+ | Role | Value | Notes |
62
+ | ------ | --------- | ------------------------------ |
63
+ | bg | `#0f172a` | page background |
64
+ | text | `#f8fafc` | primary copy |
65
+ | accent | `#fbbf24` | callouts, eyebrow, key numbers |
66
+ | muted | `#94a3b8` | secondary copy, dividers |
67
+ | ... | ... | extend as the theme requires |
68
+
69
+ ## Typography
70
+
71
+ - Display font: `<stack>` — weight 800–900 for headlines.
72
+ - Body font: `<stack>` — weight 400–500.
73
+ - Type-scale overrides (only list what differs from `slide-authoring` defaults):
74
+ - Hero title: 180 px (default 140–200 ✓)
75
+ - Body text: 36 px
76
+
77
+ ## Layout
78
+
79
+ - Content padding: 120 px from canvas edges (1920 × 1080).
80
+ - Alignment: left-aligned, single column.
81
+ - Grid notes: optional 12-column overlay at 80 px gutter for content pages.
82
+
83
+ ## Fixed components
84
+
85
+ These are paste-ready. Copy them verbatim into a slide that uses this theme.
86
+
87
+ ### Title
88
+
89
+ ```tsx
90
+ const Title = ({ children }: { children: React.ReactNode }) => (
91
+ <h1
92
+ style={{
93
+ fontSize: 140,
94
+ fontWeight: 900,
95
+ lineHeight: 1.05,
96
+ letterSpacing: '-0.02em',
97
+ margin: 0,
98
+ color: '#f8fafc',
99
+ }}
100
+ >
101
+ {children}
102
+ </h1>
103
+ );
104
+ ```
105
+
106
+ ### Footer
107
+
108
+ Pull the page number from `useSlidePageNumber()` — never hardcode `pageNum` / `total` props.
109
+
110
+ ```tsx
111
+ import { useSlidePageNumber } from '@open-aippt/core';
112
+
113
+ const Footer = () => {
114
+ const { current, total } = useSlidePageNumber();
115
+ return (
116
+ <div
117
+ style={{
118
+ position: 'absolute',
119
+ left: 120,
120
+ right: 120,
121
+ bottom: 60,
122
+ display: 'flex',
123
+ justifyContent: 'space-between',
124
+ fontSize: 24,
125
+ color: '#94a3b8',
126
+ }}
127
+ >
128
+ <span>EDITORIAL NOIR · 2026</span>
129
+ <span>{current} / {total}</span>
130
+ </div>
131
+ );
132
+ };
133
+ ```
134
+
135
+ ### Eyebrow / accents (optional)
136
+
137
+ ```tsx
138
+ const Eyebrow = ({ children }: { children: React.ReactNode }) => (
139
+ <div style={{ fontSize: 26, letterSpacing: '0.2em', color: '#fbbf24' }}>
140
+ {children}
141
+ </div>
142
+ );
143
+ ```
144
+
145
+ ## Motion
146
+
147
+ - Philosophy: static / subtle / rich — pick one and explain in one sentence.
148
+ - Reusable keyframes (paste-ready, only if the philosophy is subtle or rich):
149
+
150
+ ```css
151
+ @keyframes fadeUp {
152
+ from { opacity: 0; transform: translateY(24px); }
153
+ to { opacity: 1; transform: translateY(0); }
154
+ }
155
+ ```
156
+
157
+ ## Aesthetic
158
+
159
+ One paragraph. What it feels like, the references it draws on, what to avoid (e.g. "no rounded corners; no gradients; no decorative emoji"). Commit to a single direction — minimal, maximalist, editorial, retro, brutalist, soft/pastel, neon, paper/print.
160
+
161
+ ## Example usage
162
+
163
+ ```tsx
164
+ const Cover: Page = () => (
165
+ <div style={{ width: '100%', height: '100%', background: '#0f172a', color: '#f8fafc', padding: 120, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
166
+ <Eyebrow>CHAPTER 01</Eyebrow>
167
+ <Title>The Big Idea</Title>
168
+ <p style={{ fontSize: 36, color: '#94a3b8', maxWidth: 1200, marginTop: 32 }}>
169
+ A short subtitle that explains what this slide is about.
170
+ </p>
171
+ <Footer />
172
+ </div>
173
+ );
174
+ ```
175
+ ````
176
+
177
+ ## Step 4b — Write `themes/<id>.demo.tsx`
178
+
179
+ The demo is a normal slide module — same shape as `slides/<id>/index.tsx`, just sitting under `themes/` so the runtime knows it's preview-only. The dev-UI Themes panel imports it and renders it inside `SlideCanvas` (1920×1080).
180
+
181
+ Contract:
182
+
183
+ - `import { type Page, useSlidePageNumber } from '@open-aippt/core';`
184
+ - Inline the **same** `Title`, `Footer`, `Eyebrow` components defined in the theme markdown — verbatim, no abstractions, no imports from elsewhere. The demo and the markdown must stay in lockstep so what the user sees in the panel matches what `create-slide` will paste into a real slide.
185
+ - Export 2–3 `Page` components and a default array. Aim for: a Cover (Eyebrow + Title + subtitle), one Content page exercising body type + accent, and a Closer or "End" card. The "Example usage" block at the bottom of the markdown is a good starting point — extend it.
186
+ - If the theme has runtime-tweakable tokens worth surfacing in the Design panel later, also `export const design: DesignSystem = {...}`.
187
+ - No external assets, no `import` from `@/`, no slides-only helpers (e.g. `WindowShell` from a real slide). Demo files must be self-contained.
188
+
189
+ Skeleton:
190
+
191
+ ```tsx
192
+ import { type Page, useSlidePageNumber } from '@open-aippt/core';
193
+
194
+ const Title = ({ children }: { children: React.ReactNode }) => (
195
+ // …same JSX as in themes/<id>.md
196
+ );
197
+ const Footer = () => {
198
+ const { current, total } = useSlidePageNumber();
199
+ // …
200
+ };
201
+ const Eyebrow = ({ children }: { children: React.ReactNode }) => (
202
+ // …
203
+ );
204
+
205
+ const Cover: Page = () => (
206
+ // …
207
+ );
208
+ const Content: Page = () => (
209
+ // …
210
+ );
211
+ const Closer: Page = () => (
212
+ // …
213
+ );
214
+
215
+ export default [Cover, Content, Closer];
216
+ ```
217
+
218
+ ## Step 5 — Self-review
219
+
220
+ Run this checklist before finishing:
221
+
222
+ - [ ] Palette covers `bg` / `text` / `accent` / `muted` at minimum, all as hex.
223
+ - [ ] Type scale specifies hero, heading, body, caption sizes (or explicitly defers to `slide-authoring` defaults).
224
+ - [ ] At least Title and Footer are defined as paste-ready React with concrete inline styles.
225
+ - [ ] Motion section commits to one of static / subtle / rich.
226
+ - [ ] Aesthetic paragraph names a single coherent direction.
227
+ - [ ] Both files written: `themes/<id>.md` and `themes/<id>.demo.tsx`. No slide changes, no config changes.
228
+ - [ ] Demo `.tsx` exports 2–3 pages and inlines the same Title/Footer/Eyebrow components defined in the markdown.
229
+ - [ ] Demo opens cleanly in the **Themes** panel of the dev UI — re-checked by you only by reading the file (do not start a server).
230
+
231
+ ## Step 6 — Hand off
232
+
233
+ Tell the user:
234
+
235
+ - The theme id and the two file paths (`themes/<id>.md` + `themes/<id>.demo.tsx`).
236
+ - That the demo will appear in the dev UI's **Themes** panel as a live card and detail view (HMR — no restart needed).
237
+ - That `/create-slide` will list the theme as a picker option on its next run.
238
+ - A one-line summary of the look (palette + aesthetic).
239
+
240
+ Do not run the dev server. Do not modify real slides — even to demonstrate the theme; the demo `.tsx` is the demonstration.
241
+
242
+ ## Anti-patterns
243
+
244
+ - ❌ Writing executable code in `themes/<id>.md` outside the labeled component snippets — the markdown is documentation.
245
+ - ❌ Producing only the markdown without the demo, or only the demo without the markdown. A theme is the **bundle** — both files, every time.
246
+ - ❌ Treating `themes/<id>.demo.tsx` as a real slide. It is preview-only and lives outside the slides list; never put it under `slides/`.
247
+ - ❌ Importing from `@/` or any slide-specific helper inside the demo. The demo is self-contained.
248
+ - ❌ Inventing palette / fonts when the user supplied images or an existing slide. Extract, don't fabricate.
249
+ - ❌ Editing `slides/`, `packages/`, `package.json`, or `open-aippt.config.ts`.
250
+ - ❌ Skipping the Fixed components section. Title and Footer are the most common reuse target — they must be paste-ready.
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: current-slide
3
+ description: Resolve which slide, page, and (optionally) selected element the user is currently viewing in the open-aippt dev server. Consult this whenever the user references "this page", "this slide", "this element", "the slide I'm on", "the current page", or any deictic reference to slide content without naming it. Re-read `node_modules/.open-aippt/current.json` at the start of every such turn — the user navigates between turns, so a value you read earlier in the conversation is almost certainly stale.
4
+ ---
5
+
6
+ # Where is the user right now?
7
+
8
+ When the user says "fix this page", "tweak this heading", or "the slide I'm looking at", they almost never name the slide id, page number, or element — they mean wherever they are in the dev viewer. Before asking "which slide?" or "which element?", check the file the dev server writes on every navigation and inspector pick.
9
+
10
+ ## Re-read on every deictic turn — never reuse a prior read
11
+
12
+ `current.json` is a live cursor, not a fact about the conversation. The user moves between slides, pages, and elements freely between your turns — including while you were doing other work. **Read the file fresh at the start of every new turn that uses a deictic reference**, even if:
13
+
14
+ - you already read it earlier in this same conversation,
15
+ - you just finished editing the slide it pointed to,
16
+ - the user's new message sounds like a continuation ("now make it bigger", "also fix this one", "keep going").
17
+
18
+ A "continue editing" follow-up is exactly the case where the user has likely just navigated to a different slide or picked a different element. Trusting your last read here will silently edit the wrong file. Re-read, compare `slideId` / `pageIndex` / `selection` against what you used last time, and act on the new values.
19
+
20
+ ## How to read it
21
+
22
+ ```
23
+ node_modules/.open-aippt/current.json
24
+ ```
25
+
26
+ Path is relative to the project root (the user's `cwd`, the directory that contains `slides/` and `package.json`). Use the `Read` tool. The file is JSON.
27
+
28
+ ## What you get
29
+
30
+ ```json
31
+ {
32
+ "slideId": "q2-roadmap",
33
+ "pageIndex": 2,
34
+ "pageNumber": 3,
35
+ "totalPages": 8,
36
+ "slideTitle": "Q2 Roadmap",
37
+ "view": "slides",
38
+ "pagePath": "slides/q2-roadmap/index.tsx",
39
+ "selection": {
40
+ "line": 42,
41
+ "column": 6,
42
+ "tagName": "h1",
43
+ "text": "Q2 Roadmap"
44
+ },
45
+ "updatedAt": "2026-05-09T14:32:11.123Z"
46
+ }
47
+ ```
48
+
49
+ - `slideId` — folder name under `slides/`. Use as-is for any `/__slides/<id>/...` API or as the URL segment.
50
+ - `pageIndex` — 0-based, for use with the page array in `index.tsx` (`export default [Cover, Body, ...]`).
51
+ - `pageNumber` — 1-based, for use in messages to the user ("page 3 of 8") and for the URL `?p=N`.
52
+ - `pagePath` — relative path to the slide source. Hand straight to `Read` / `Edit`.
53
+ - `view` — `"slides"` (canvas view) or `"assets"` (asset manager). If `"assets"`, the user is browsing files for that slide rather than viewing a page.
54
+ - `selection` — `null` if nothing is selected. Otherwise, the JSX element the user picked in the inspector overlay:
55
+ - `line` (1-indexed) and `column` (0-indexed) point to the JSX opening tag inside `pagePath`. This is the canonical handle — match against the source line, not the rendered DOM.
56
+ - `tagName` is the rendered DOM tag, lowercased (`"h1"`, `"div"`, `"img"`).
57
+ - `text` is a trimmed text snippet (≤120 chars) from the element's `textContent`, useful as a sanity check that you're looking at the right node.
58
+ - Selection auto-clears whenever the user navigates to a different slide or page.
59
+ - `updatedAt` — ISO timestamp of the last navigation or selection change. Use it to detect staleness.
60
+
61
+ ## When to use this
62
+
63
+ - The user references the current slide/page deictically: "this", "here", "the page I'm on", "the slide I'm looking at", "what I'm working on".
64
+ - The user references a specific element: "this heading", "this image", "the button I just clicked", "tighten this", "change the color of this". If `selection` is non-null, that's the element they mean.
65
+ - Before asking "which slide?" or "which element?" as a clarifying question — check this file first.
66
+ - Before guessing from `git log`, recently-edited files, or the most recent slide folder.
67
+
68
+ ## When NOT to use this
69
+
70
+ - The user names a slide explicitly ("edit `q2-roadmap`") — use that name directly.
71
+ - The `apply-comments` workflow already finds the right file via `@slide-comment` markers; it doesn't need this skill.
72
+ - For listing or discovering slides — read `slides/` directly.
73
+
74
+ ## Staleness — verify before acting
75
+
76
+ `updatedAt` is the last time the user navigated. Treat it like a cache:
77
+
78
+ - **Fresh (under ~5 minutes old)**: trust it. Open `pagePath`, do the work.
79
+ - **Older than ~5 minutes, or older than your last interaction with the user**: confirm with the user before editing. The dev server may not be running; the user may have switched contexts.
80
+ - **Hours/days old**: ignore it. Ask the user which slide they mean.
81
+
82
+ A *newer* `updatedAt` than the one you saw last turn is the normal signal that the user has moved — switch to the new `slideId` / `pageIndex` / `selection` without asking.
83
+
84
+ ## When the file is missing
85
+
86
+ - The dev server hasn't been opened on a slide yet, or has never run.
87
+ - Don't create the file or guess. Ask the user which slide they mean, or suggest they open the slide in the dev server first.
88
+
89
+ ## Example — page-level reference
90
+
91
+ User: "tighten the spacing on this page"
92
+
93
+ 1. Read `node_modules/.open-aippt/current.json`.
94
+ 2. Check `updatedAt` is recent.
95
+ 3. Read `pagePath` (e.g. `slides/q2-roadmap/index.tsx`).
96
+ 4. Identify the page at `pageIndex` in the default-exported array.
97
+ 5. Consult the `slide-authoring` skill for spacing rules, then edit that page in place.
98
+
99
+ If `current.json` is missing or stale, ask: "Which slide and page should I tighten? The dev server hasn't published a current page recently."
100
+
101
+ ## Example — element-level reference
102
+
103
+ User: "make this bigger"
104
+
105
+ 1. Read `node_modules/.open-aippt/current.json`.
106
+ 2. If `selection` is non-null, the user means that element. Read `pagePath`, jump to `selection.line`, and find the JSX opening tag near that line/column. Confirm with the snippet in `selection.text` and the `tagName`.
107
+ 3. Consult `slide-authoring` for type-scale and layout rules before editing.
108
+ 4. Edit the JSX node in place.
109
+
110
+ If `selection` is null, fall back to the page-level flow above — and consider asking "which element?" since the user used a deictic but hasn't picked one in the inspector.