@autono/open-pages 0.1.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/LICENSE +23 -0
- package/README.md +108 -0
- package/bin.js +2 -0
- package/dist/build-TP72kiw7.js +35 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +100 -0
- package/dist/config-C9or4m4J.d.ts +23 -0
- package/dist/config-L0IbktJ0.js +3135 -0
- package/dist/design-C13iz9_4.js +35 -0
- package/dist/dev-I2IyEh5W.js +97 -0
- package/dist/export-B-kBRWB1.js +4 -0
- package/dist/export-ic28osQP.js +131 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +3 -0
- package/dist/locale/index.d.ts +24 -0
- package/dist/locale/index.js +881 -0
- package/dist/open-pages-plugin-D_DN_zjh.js +632 -0
- package/dist/preview-BrDuvgAN.js +21 -0
- package/dist/sync-Be_hxSOF.js +139 -0
- package/dist/sync-lKEXyPOh.js +3 -0
- package/dist/types-C5NCdDT-.d.ts +219 -0
- package/dist/update-CV62NhBT.js +37 -0
- package/dist/update-package-Cxt57pz7.js +152 -0
- package/dist/version-B6AiiGvT.js +26 -0
- package/dist/version-CeffgdfZ.js +3 -0
- package/dist/vite/index.d.ts +14 -0
- package/dist/vite/index.js +7 -0
- package/env.d.ts +59 -0
- package/package.json +106 -0
- package/skills/apply-comments/SKILL.md +86 -0
- package/skills/create-page/SKILL.md +92 -0
- package/skills/create-theme/SKILL.md +223 -0
- package/skills/current-page/SKILL.md +106 -0
- package/skills/page-authoring/SKILL.md +148 -0
- package/skills/page-authoring/references/assets-and-fonts.md +85 -0
- package/skills/page-authoring/references/html-pages.md +65 -0
- package/skills/page-authoring/references/interactivity.md +77 -0
- package/skills/page-authoring/references/layout-and-responsive.md +71 -0
- package/skills/page-authoring/references/typography-and-color.md +60 -0
- package/src/app/app.tsx +45 -0
- package/src/app/components/asset-view.tsx +1665 -0
- package/src/app/components/command/command-menu.tsx +227 -0
- package/src/app/components/command/command.tsx +142 -0
- package/src/app/components/command/home-command-menu.tsx +96 -0
- package/src/app/components/icon-tooltip.tsx +34 -0
- package/src/app/components/language-toggle.tsx +46 -0
- package/src/app/components/sidebar/folder-item.tsx +269 -0
- package/src/app/components/sidebar/icon-picker.tsx +61 -0
- package/src/app/components/sidebar/sidebar-footer.tsx +139 -0
- package/src/app/components/sidebar/sidebar.tsx +302 -0
- package/src/app/components/theme-toggle.tsx +66 -0
- package/src/app/components/themes/theme-detail.tsx +226 -0
- package/src/app/components/themes/themes-gallery.tsx +104 -0
- package/src/app/components/ui/badge.tsx +48 -0
- package/src/app/components/ui/button.tsx +93 -0
- package/src/app/components/ui/card.tsx +92 -0
- package/src/app/components/ui/context-menu.tsx +247 -0
- package/src/app/components/ui/dialog.tsx +155 -0
- package/src/app/components/ui/dropdown-menu.tsx +267 -0
- package/src/app/components/ui/input.tsx +25 -0
- package/src/app/components/ui/label.tsx +21 -0
- package/src/app/components/ui/popover.tsx +82 -0
- package/src/app/components/ui/progress.tsx +33 -0
- package/src/app/components/ui/scroll-area.tsx +53 -0
- package/src/app/components/ui/select.tsx +195 -0
- package/src/app/components/ui/separator.tsx +26 -0
- package/src/app/components/ui/slider.tsx +68 -0
- package/src/app/components/ui/sonner.tsx +48 -0
- package/src/app/components/ui/tabs.tsx +79 -0
- package/src/app/components/ui/textarea.tsx +22 -0
- package/src/app/components/ui/toggle-group.tsx +84 -0
- package/src/app/components/ui/toggle.tsx +45 -0
- package/src/app/components/ui/tooltip.tsx +75 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/frame/inspect.ts +140 -0
- package/src/app/frame/main.tsx +91 -0
- package/src/app/frame.html +12 -0
- package/src/app/index.html +13 -0
- package/src/app/lib/asset-filter.test.ts +176 -0
- package/src/app/lib/asset-filter.ts +80 -0
- package/src/app/lib/assets.ts +257 -0
- package/src/app/lib/design.ts +58 -0
- package/src/app/lib/folders.ts +239 -0
- package/src/app/lib/frame.ts +38 -0
- package/src/app/lib/locale-store.ts +67 -0
- package/src/app/lib/page-thumb.tsx +46 -0
- package/src/app/lib/pages.ts +28 -0
- package/src/app/lib/sdk.ts +43 -0
- package/src/app/lib/themes.ts +22 -0
- package/src/app/lib/use-locale.ts +8 -0
- package/src/app/lib/use-page-module.ts +48 -0
- package/src/app/lib/use-page-titles.ts +29 -0
- package/src/app/lib/use-restart-server.ts +78 -0
- package/src/app/lib/utils.test.ts +25 -0
- package/src/app/lib/utils.ts +6 -0
- package/src/app/main.tsx +14 -0
- package/src/app/routes/assets.tsx +9 -0
- package/src/app/routes/home-shell.tsx +252 -0
- package/src/app/routes/home.tsx +846 -0
- package/src/app/routes/page.tsx +329 -0
- package/src/app/routes/themes.tsx +34 -0
- package/src/app/styles.css +405 -0
- package/src/app/virtual.d.ts +53 -0
- package/src/locale/en.ts +218 -0
- package/src/locale/format.ts +12 -0
- package/src/locale/index.ts +6 -0
- package/src/locale/ja.ts +221 -0
- package/src/locale/types.ts +215 -0
- package/src/locale/zh-cn.ts +218 -0
- package/src/locale/zh-tw.ts +218 -0
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@autono/open-pages",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Runtime and CLI for open-pages — live preview of React and HTML pages, click-to-comment inspector, and static export.",
|
|
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-pages": "./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
|
+
"scripts": {
|
|
36
|
+
"build": "tsdown",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"test:e2e": "pnpm run build && playwright test",
|
|
39
|
+
"prepack": "pnpm build"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18"
|
|
43
|
+
},
|
|
44
|
+
"keywords": [
|
|
45
|
+
"pages",
|
|
46
|
+
"website",
|
|
47
|
+
"landing-page",
|
|
48
|
+
"react",
|
|
49
|
+
"vite",
|
|
50
|
+
"agents",
|
|
51
|
+
"tailwind",
|
|
52
|
+
"html"
|
|
53
|
+
],
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"author": {
|
|
56
|
+
"name": "autonoco",
|
|
57
|
+
"url": "https://github.com/autonoco"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://openpages.sh",
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/autonoco/open-pages.git",
|
|
63
|
+
"directory": "packages/core"
|
|
64
|
+
},
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/autonoco/open-pages/issues"
|
|
67
|
+
},
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"@babel/parser": "^8.0.4",
|
|
73
|
+
"@babel/types": "^8.0.4",
|
|
74
|
+
"@base-ui/react": "1.6.0",
|
|
75
|
+
"@fontsource-variable/geist": "^5.2.8",
|
|
76
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
77
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
78
|
+
"chalk": "^6.0.0",
|
|
79
|
+
"class-variance-authority": "^0.7.1",
|
|
80
|
+
"clsx": "^2.1.1",
|
|
81
|
+
"cmdk": "^1.1.1",
|
|
82
|
+
"commander": "^15.0.0",
|
|
83
|
+
"emoji-picker-react": "^4.19.1",
|
|
84
|
+
"fast-glob": "^3.3.2",
|
|
85
|
+
"lucide-react": "^1.25.0",
|
|
86
|
+
"next-themes": "^0.4.6",
|
|
87
|
+
"react": "^18.3.1",
|
|
88
|
+
"react-dom": "^18.3.1",
|
|
89
|
+
"react-router-dom": "^7.18.1",
|
|
90
|
+
"shadcn": "^4.19.0",
|
|
91
|
+
"sonner": "^2.0.7",
|
|
92
|
+
"tailwind-merge": "^3.5.0",
|
|
93
|
+
"tailwindcss": "^4.2.2",
|
|
94
|
+
"tw-animate-css": "^1.4.0",
|
|
95
|
+
"use-sync-external-store": "^1.6.0",
|
|
96
|
+
"vite": "^5.4.10"
|
|
97
|
+
},
|
|
98
|
+
"devDependencies": {
|
|
99
|
+
"@playwright/test": "~1.62.1",
|
|
100
|
+
"@types/node": "^22.19.17",
|
|
101
|
+
"@types/react": "^18.3.12",
|
|
102
|
+
"@types/react-dom": "^18.3.1",
|
|
103
|
+
"tsdown": "^0.9.9",
|
|
104
|
+
"typescript": "^5.9.3"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: apply-comments
|
|
3
|
+
description: Apply pending @page-comment markers written by the open-pages inspector tool. Use when the user asks to "apply comments", "process page comments", "apply the inspector comments", or references markers left inside `pages/<id>/index.tsx` (or its `components/*.tsx`).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Apply page comments
|
|
7
|
+
|
|
8
|
+
The open-pages viewer has an inspector that lets the user click any element on the live page and attach a textual comment (e.g. *"make this red"*, *"change to 'Open Pages Rocks'"*). Each comment is persisted as an in-source JSX marker inside the page's source — usually `pages/<pageId>/index.tsx`, occasionally a file under `pages/<pageId>/components/`.
|
|
9
|
+
|
|
10
|
+
Your job: read those markers, perform the described edits, and delete the markers.
|
|
11
|
+
|
|
12
|
+
> **Before making any page edit**, consult the **`page-authoring`** skill — it is the technical reference for how a page is structured (file contract, `className` styling, layout and responsive rules, type scale, interactivity). A comment like *"make this bigger"* or *"change the accent colour"* should be applied in a way that stays consistent with those rules and still works on mobile.
|
|
13
|
+
|
|
14
|
+
## Marker format
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
{/* @page-comment id="c-<8hex>" ts="<ISO>" text="<base64url(JSON)>" */}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- Inserted as the **first child inside** the JSX element it refers to: a newline + indent + the marker, spliced immediately after the element's opening `>`. The marker is dropped *into* its target, not floated above it. **The marker does not necessarily end its line** — for an element that was written on one line (`<h1>Title</h1>`), the element's children and closing tag follow the marker on the same line.
|
|
21
|
+
- `text` is base64url-encoded JSON: `{"note": "...", "hint"?: "..."}`.
|
|
22
|
+
- Detection regex (authoritative — use exactly this):
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
/\{\/\*\s*@page-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 page(s).**
|
|
31
|
+
- If the user names one (`launch`, `pricing`, etc.), work on that single page's source files.
|
|
32
|
+
- If they say "all" or don't specify, scan every `pages/*/index.tsx` and `pages/*/components/*.tsx`. Process each page 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), 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, `className` strings, any state the element depends on) to apply the change faithfully. A comment inside a `<button>` with an `onClick` may be about behaviour, not looks.
|
|
43
|
+
- If the marker sits inside a `.map` body, the comment applies to the row template — every rendered row changes. If the user clearly meant one item ("make the *middle* one green"), change the data or add a per-item field rather than special-casing the JSX.
|
|
44
|
+
- If the `note` is ambiguous, do the smallest reasonable interpretation and mention the assumption in your summary.
|
|
45
|
+
|
|
46
|
+
4. **Apply edits in reverse line order.**
|
|
47
|
+
- Sort markers by descending `lineIndex` and process one at a time, using the `Edit` tool.
|
|
48
|
+
- Processing top-down would invalidate line numbers for later markers as the file shrinks/grows.
|
|
49
|
+
|
|
50
|
+
5. **Remove each marker after applying its edit.**
|
|
51
|
+
- Delete **only the marker text itself** — the `{/* @page-comment … */}` span matched by the detection regex — plus the newline and indentation immediately *before* it (the whitespace the inspector inserted). Never delete the whole line: children and the closing tag often share the marker's line, and removing the line destroys them.
|
|
52
|
+
- After removal, if the element is left split across two lines that were originally one (`<h1 …>\n Title</h1>`), it is fine to rejoin them, but not required.
|
|
53
|
+
- Never leave a marker behind for an edit you applied — that signals a failure. Markers deliberately skipped per the edge cases below stay in place.
|
|
54
|
+
|
|
55
|
+
6. **Verify.**
|
|
56
|
+
- After all edits, re-read the file and confirm the only remaining markers are ones you reported as skipped.
|
|
57
|
+
- Confirm the edited JSX is well-formed (balanced tags, no dangling attributes) and that changed `className` strings are literal Tailwind utilities. If the project's `package.json` has typecheck/lint scripts, run them with the project's package manager; scaffolded projects ship neither TypeScript nor a linter — there, rely on the running dev server (or the `build` script) to surface compile errors. Fix any errors you introduced.
|
|
58
|
+
- For layout changes, mentally check the Mobile viewport (390px): did the edit introduce a fixed width or a grid with no stacking fallback?
|
|
59
|
+
|
|
60
|
+
7. **Report.**
|
|
61
|
+
- Summarise: `N applied, M skipped` plus a one-line description of each change (including the page id).
|
|
62
|
+
|
|
63
|
+
## base64url decoding helper
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
function decode(s) {
|
|
67
|
+
const pad = s.length % 4 === 0 ? '' : '='.repeat(4 - (s.length % 4));
|
|
68
|
+
return Buffer.from(s.replace(/-/g, '+').replace(/_/g, '/') + pad, 'base64').toString('utf8');
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
You can run this inline via `node -e '...'` if you need to inspect a payload; otherwise just reason about the decoded string.
|
|
73
|
+
|
|
74
|
+
## Edge cases
|
|
75
|
+
|
|
76
|
+
- **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.
|
|
77
|
+
- **Multiple markers stacked on consecutive lines inside the same element**: they all refer to that enclosing element. Read their notes in source order to understand the combined intent, then apply and delete them bottom-up per step 4.
|
|
78
|
+
- **Comment asks for something outside the target element's scope** (e.g. "add a testimonials section"): do the closest-reasonable edit and mention the scope expansion in your summary.
|
|
79
|
+
- **Comment on a plain HTML page** (`pages/<id>/index.html`): the inspector does not run there, so no markers exist. Apply the user's request directly to the HTML.
|
|
80
|
+
- **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.
|
|
81
|
+
|
|
82
|
+
## Do not
|
|
83
|
+
|
|
84
|
+
- Do not touch `package.json`, `open-pages.config.ts`, or files outside `pages/`.
|
|
85
|
+
- Do not add dependencies.
|
|
86
|
+
- Do not re-introduce markers or leave `TODO` breadcrumbs — the user already has a record in git.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-page
|
|
3
|
+
description: Use this skill when the user wants to create, build, draft, or generate a new web page, site, or landing page in this open-pages repo. Triggers on phrases like "make a landing page for X", "build a pricing page", "create a portfolio site", "new page", "a dashboard UI", "an HTML page", or when the user asks to add content under `pages/`. Do NOT use for editing the framework itself — only for authoring content inside `pages/<id>/`.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create a page in open-pages
|
|
7
|
+
|
|
8
|
+
This skill owns the **workflow** for building a new page. The technical reference — file contract, Tailwind via `className`, layout and responsive rules, type scale, interactivity, assets — lives in the **`page-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 `pages/<id>/`. Never modify `package.json`, `open-pages.config.ts`, or existing pages.
|
|
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. (`AskUserQuestion` holds at most 4 options — with 4+ themes, offer the 3 most topic-relevant plus "no theme"; the auto-added "Other" lets the user name any omitted theme.)
|
|
15
|
+
|
|
16
|
+
- If the user picks a theme: read `themes/<id>.md` end-to-end. The theme's palette, typography, and fixed components are now authoritative — copy them directly into the page. **Also set `theme: '<theme-id>'` on the `meta` export** so the page back-links to the theme. In Step 2, skip the **visual direction** question (the theme already commits to one); confirm the page's purpose itself before moving on. Sections and interactivity are independent of theme — ask those normally.
|
|
17
|
+
- If the user picks "no theme", or `themes/` contains no theme markdown files: proceed to Step 2 unchanged.
|
|
18
|
+
|
|
19
|
+
If you skip the visual-direction 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 key decisions below via `AskUserQuestion`.** They shape every downstream choice, so locking them in up front avoids rework. Only skip a question when it's already unambiguously answered — by the user's original message, or by a theme picked in Step 1 — and if you skip, restate your assumption so they can correct it.
|
|
24
|
+
|
|
25
|
+
**Purpose comes first.** If the user's initial request is thin ("make me a page", "build a site"), make a *separate* `AskUserQuestion` call first to gather what the page is for, who lands on it, and what content they already have (copy, product names, prices, screenshots, links). Skip this only if already clear — then restate your reading so they can correct course.
|
|
26
|
+
|
|
27
|
+
Then ask these four in a single `AskUserQuestion` call (multi-question form):
|
|
28
|
+
|
|
29
|
+
1. **Page type** — offer the closest fits: landing / marketing page, product or pricing page, dashboard or app UI, docs or long-form content page, portfolio or personal site, form or signup flow, internal tool. Mark the best fit "(Recommended)". This drives structure and how much interactivity to expect.
|
|
30
|
+
|
|
31
|
+
2. **Visual direction** — propose 3 directions tailored to *this* page and its audience. Do **not** pull from a fixed preset list. Each option must combine a vibe word + a concrete visual cue (palette, weight, surfaces) so the user can picture it; bare labels like "modern" are too vague. The three options should feel meaningfully different.
|
|
32
|
+
|
|
33
|
+
How options should shift with page type:
|
|
34
|
+
- *SaaS landing page* → **dark launch** (near-black, one neon accent, large display type) · **clean product** (white, slate text, one indigo accent, soft cards) · **editorial** (warm off-white, serif display, generous whitespace)
|
|
35
|
+
- *Dashboard* → **ops console** (dense, slate surfaces, status colors) · **calm workspace** (airy, rounded cards, one accent) · **data-forward** (tables and charts as the spine, mono numerals)
|
|
36
|
+
- *Portfolio* → **gallery** (big imagery, minimal chrome) · **typographic** (type does the work, one accent) · **playful** (color blocks, rounded shapes, motion)
|
|
37
|
+
|
|
38
|
+
Mark the best fit "(Recommended)". (`AskUserQuestion` auto-adds "Other" — don't add a catch-all yourself.)
|
|
39
|
+
|
|
40
|
+
3. **Sections / content** — offer bundles that fit the type, e.g. for a landing page: hero + features + pricing + FAQ + footer (Recommended); hero + social proof + CTA (short); hero + long-form explainer + CTA. The auto-added "Other" covers custom lists.
|
|
41
|
+
|
|
42
|
+
4. **Interactivity** — offer: static (links only), light (tabs, toggles, accordions, a pricing switch), form (a contact/signup form that mirrors state locally or posts to a URL they supply), app-like (filters, local state, multiple views). Note that no backend exists — anything that needs one requires a URL from the user.
|
|
43
|
+
|
|
44
|
+
After those, ask follow-ups **only if still unclear**: brand colors, logo/screenshots, real copy (headlines, prices, names). Real pages live on real content — placeholder copy like "Acme, $10/mo" is a last resort; prefer asking for the real values. Responsive is not a question: every page must work at Desktop, Tablet (820px), and Mobile (390px).
|
|
45
|
+
|
|
46
|
+
## Step 3 — Pick a page id
|
|
47
|
+
|
|
48
|
+
Use **kebab-case**, short, descriptive. Examples: `launch`, `pricing`, `status-board`, `founder-portfolio`, `waitlist`. Check `pages/` to avoid collisions.
|
|
49
|
+
|
|
50
|
+
## Step 4 — Plan the structure
|
|
51
|
+
|
|
52
|
+
Sketch the page as an ordered list of sections before writing code. Common shapes:
|
|
53
|
+
|
|
54
|
+
| Section | Purpose |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| Header / nav | Wordmark, 3–5 links, one CTA; collapses on mobile |
|
|
57
|
+
| Hero | One headline, one lede, primary + secondary CTA, optional visual |
|
|
58
|
+
| Social proof | Logos, a metric row, one quote |
|
|
59
|
+
| Features | 3–6 cards or alternating rows, each one benefit |
|
|
60
|
+
| Pricing | 2–3 tiers, a billing toggle, clear CTA per tier |
|
|
61
|
+
| FAQ | Accordion or plain list |
|
|
62
|
+
| CTA band | Restate the ask before the footer |
|
|
63
|
+
| Footer | Links, legal line |
|
|
64
|
+
| App shell | Sidebar/topbar + content area for dashboards and tools |
|
|
65
|
+
|
|
66
|
+
**Rule of thumb:** one idea per section, one CTA per screen. If a section lists things, it wants a grid or a table, not a paragraph.
|
|
67
|
+
|
|
68
|
+
Decide the data shape now: which sections render from a typed const array (`.map`), which are explicit component instances — `page-authoring` explains why this matters for the inspector.
|
|
69
|
+
|
|
70
|
+
## Step 5 — Commit to a visual direction
|
|
71
|
+
|
|
72
|
+
One palette, one type scale, held for the whole page. The constraints (web type scale, palette structure, contrast) live in `page-authoring` and its `references/typography-and-color.md` — apply them. Define the palette as repeated Tailwind utilities (or a small const map of class strings). Fonts: default to the system stack; load a Google Font or self-hosted file only when the user names one or a theme requires it (`references/assets-and-fonts.md`).
|
|
73
|
+
|
|
74
|
+
## Step 6 — Write `pages/<id>/index.tsx`
|
|
75
|
+
|
|
76
|
+
Read the **`page-authoring`** skill before writing — file contract, `className` styling, layout and responsive rules, interactivity constraints, assets. Its file-contract example is the starter template. Split large pages into `pages/<id>/components/*.tsx` when a section is more than ~80 lines.
|
|
77
|
+
|
|
78
|
+
## Step 7 — Self-review
|
|
79
|
+
|
|
80
|
+
Run the checklist in `page-authoring` ("Self-review before finishing"). Check all three viewports.
|
|
81
|
+
|
|
82
|
+
## Step 8 — Hand off to the user
|
|
83
|
+
|
|
84
|
+
Tell the user:
|
|
85
|
+
|
|
86
|
+
- The page id and file path you created.
|
|
87
|
+
- The preview URL — `http://localhost:5173/p/<id>` — hot-reloads on every edit, with Desktop / Tablet / Mobile toggles and **Open** to view the page by itself.
|
|
88
|
+
- That they can hit **Inspect** (or `i`) in the preview, click any element, and leave comments — then ask you to run `apply-comments`.
|
|
89
|
+
- That `open-pages export <id>` writes `export/<id>/` — a static folder (index.html + assets) they can deploy to Netlify, Vercel, Cloudflare Pages, GitHub Pages, or any static host.
|
|
90
|
+
- If dev isn't running: run the project's `dev` script from the project root with its package manager (`npm run dev`, `pnpm dev`, … — match the lockfile).
|
|
91
|
+
|
|
92
|
+
Don't run the dev server yourself unless asked.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-theme
|
|
3
|
+
description: Use this skill when the user wants to create, draft, author, or extract a page theme in this open-pages repo. Triggers on phrases like "create a theme", "make a theme called X", "extract a theme from <page>", "build a design system from these screenshots", "match our brand". Produces two paired files under `themes/` — `<id>.md` (palette, typography, layout, fixed components) and `<id>.demo.tsx` (a runnable demo page the workspace's Themes panel previews live). Do NOT use for editing real pages — only for authoring the theme bundle.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create a page theme
|
|
7
|
+
|
|
8
|
+
This skill produces a **theme bundle** under `themes/`: two paired files that together describe a reusable visual identity for web pages.
|
|
9
|
+
|
|
10
|
+
1. `themes/<id>.md` — agent-facing documentation: palette, typography, layout, fixed components (nav, hero, section wrapper, buttons, card, footer). This is what `create-page` reads when an author picks the theme.
|
|
11
|
+
2. `themes/<id>.demo.tsx` — a runnable demo page (same module shape as `pages/<id>/index.tsx`: **one default-exported component**) that shows the theme on a single scrolling page. The workspace's Themes panel renders it live, exactly like a real page.
|
|
12
|
+
|
|
13
|
+
Both files share the same stem so the runtime can pair them automatically.
|
|
14
|
+
|
|
15
|
+
The theme markdown is authoring-time direction — `create-page` copies its palette, utilities, and components into a real page's source. The demo `.tsx` is a self-contained preview, not a real page — it does not appear in the pages list.
|
|
16
|
+
|
|
17
|
+
You only write `themes/<id>.md` and `themes/<id>.demo.tsx`. Never modify real pages or configuration. The styling rules and web defaults that themes override live in the **`page-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 screenshots, mood-board images, brand assets.
|
|
24
|
+
- **Free-text description** — prose describing the desired palette, weight, feel.
|
|
25
|
+
- **An existing page** — `pages/<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, page 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 and family feel, corner radius, surface treatment (flat vs. cards vs. borders), density, and recurring chrome (nav style, footer).
|
|
32
|
+
- **Text**: extract explicit tokens (hex codes, font names, tone words) and resolve vague language into concrete decisions before writing.
|
|
33
|
+
- **Existing page**: read `pages/<id>/index.tsx` (and `components/`) and pull:
|
|
34
|
+
- The Tailwind color utilities used consistently (`bg-…`, `text-…`, accent classes) → Palette section.
|
|
35
|
+
- Type sizes and any font loading (`<link>` to Google Fonts, `font-[…]`) → Typography section.
|
|
36
|
+
- Container widths, section padding, breakpoints used → Layout section.
|
|
37
|
+
- Recurring helper components (nav, hero, cards, buttons, footer) → Fixed components section.
|
|
38
|
+
- The aesthetic feel implied → Aesthetic paragraph.
|
|
39
|
+
|
|
40
|
+
When inputs disagree (e.g. images use blue but the description says green), ask the user which to honor.
|
|
41
|
+
|
|
42
|
+
## Step 3 — Pick a theme id
|
|
43
|
+
|
|
44
|
+
Use **kebab-case**, short, descriptive. Examples: `dark-launch`, `clean-saas`, `editorial-warm`, `ops-console`. Check `themes/` to avoid collisions.
|
|
45
|
+
|
|
46
|
+
## Step 4 — Write `themes/<id>.md`
|
|
47
|
+
|
|
48
|
+
Produce a file with this exact section order. Section bodies adapt to the theme; the headings stay consistent across all themes.
|
|
49
|
+
|
|
50
|
+
````markdown
|
|
51
|
+
---
|
|
52
|
+
name: <Human title, e.g. "Dark Launch">
|
|
53
|
+
description: <one-line elevator pitch>
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
# <Theme name>
|
|
57
|
+
|
|
58
|
+
## Palette
|
|
59
|
+
|
|
60
|
+
| Role | Tailwind | Hex | Notes |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| background | `bg-[#0b0b10]` | #0b0b10 | page root |
|
|
63
|
+
| surface | `bg-white/[0.04] border-white/10` | — | cards, panels |
|
|
64
|
+
| text | `text-white` | #ffffff | headings, body |
|
|
65
|
+
| muted | `text-white/60` | — | secondary copy, labels |
|
|
66
|
+
| accent | `bg-emerald-400 text-black` / `text-emerald-400` | #34d399 | primary CTA, eyebrows |
|
|
67
|
+
| border | `border-white/10` | — | dividers, card edges |
|
|
68
|
+
|
|
69
|
+
## Typography
|
|
70
|
+
|
|
71
|
+
- Font: system stack, or a named family with how to load it (Google Fonts `<link>` rendered in the page, or a self-hosted file the page must place under `assets/`).
|
|
72
|
+
- Type-scale overrides (only list what differs from `page-authoring` defaults):
|
|
73
|
+
- Hero heading: `text-5xl sm:text-7xl font-bold leading-[1.02] tracking-tight`
|
|
74
|
+
- Section heading: `text-3xl font-bold tracking-tight`
|
|
75
|
+
- Body: `text-lg text-white/60`
|
|
76
|
+
- Eyebrow: `text-xs font-semibold uppercase tracking-[0.25em] text-emerald-400`
|
|
77
|
+
|
|
78
|
+
## Layout
|
|
79
|
+
|
|
80
|
+
- Container: `mx-auto max-w-6xl px-6`.
|
|
81
|
+
- Section rhythm: `py-20`, sections separated by `border-t border-white/10`.
|
|
82
|
+
- Breakpoints: single column by default; `sm:grid-cols-3` for feature and pricing grids; nav links `hidden sm:flex`.
|
|
83
|
+
- Radius: `rounded-full` for buttons, `rounded-2xl` for cards.
|
|
84
|
+
|
|
85
|
+
## Fixed components
|
|
86
|
+
|
|
87
|
+
These are paste-ready React JSX with `className`. Copy them verbatim into a page that uses this theme.
|
|
88
|
+
|
|
89
|
+
### Nav
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
const Nav = ({ brand, links, cta }: { brand: string; links: { label: string; href: string }[]; cta: { label: string; href: string } }) => (
|
|
93
|
+
<header className="mx-auto flex max-w-6xl items-center justify-between px-6 py-6">
|
|
94
|
+
<span className="font-semibold tracking-tight">{brand}</span>
|
|
95
|
+
<nav className="hidden gap-8 text-sm text-white/70 sm:flex">
|
|
96
|
+
{links.map((l) => (
|
|
97
|
+
<a key={l.href} href={l.href} className="hover:text-white">{l.label}</a>
|
|
98
|
+
))}
|
|
99
|
+
</nav>
|
|
100
|
+
<a href={cta.href} className="rounded-full bg-white px-4 py-2 text-sm font-medium text-black hover:bg-white/90">{cta.label}</a>
|
|
101
|
+
</header>
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Hero
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
const Hero = ({ eyebrow, title, lede, children }: { eyebrow: string; title: string; lede: string; children?: React.ReactNode }) => (
|
|
109
|
+
<section className="mx-auto max-w-6xl px-6 pt-20 pb-24 text-center">
|
|
110
|
+
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-emerald-400">{eyebrow}</p>
|
|
111
|
+
<h1 className="mx-auto mt-6 max-w-3xl text-5xl font-bold leading-[1.02] tracking-tight sm:text-7xl">{title}</h1>
|
|
112
|
+
<p className="mx-auto mt-6 max-w-xl text-lg text-white/60">{lede}</p>
|
|
113
|
+
<div className="mt-10 flex flex-col justify-center gap-3 sm:flex-row">{children}</div>
|
|
114
|
+
</section>
|
|
115
|
+
);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Section
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
const Section = ({ id, children }: { id?: string; children: React.ReactNode }) => (
|
|
122
|
+
<section id={id} className="border-t border-white/10">
|
|
123
|
+
<div className="mx-auto max-w-6xl px-6 py-20">{children}</div>
|
|
124
|
+
</section>
|
|
125
|
+
);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Buttons
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
const PrimaryButton = ({ href, children }: { href: string; children: React.ReactNode }) => (
|
|
132
|
+
<a href={href} className="rounded-full bg-emerald-400 px-6 py-3 font-medium text-black hover:bg-emerald-300">{children}</a>
|
|
133
|
+
);
|
|
134
|
+
const SecondaryButton = ({ href, children }: { href: string; children: React.ReactNode }) => (
|
|
135
|
+
<a href={href} className="rounded-full border border-white/20 px-6 py-3 font-medium text-white/80 hover:border-white/40">{children}</a>
|
|
136
|
+
);
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Card
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const Card = ({ title, children }: { title: string; children: React.ReactNode }) => (
|
|
143
|
+
<div className="rounded-2xl border border-white/10 bg-white/[0.03] p-6">
|
|
144
|
+
<h3 className="font-semibold">{title}</h3>
|
|
145
|
+
<div className="mt-2 text-white/60">{children}</div>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Footer
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const Footer = ({ left, right }: { left: string; right: string }) => (
|
|
154
|
+
<footer className="border-t border-white/10">
|
|
155
|
+
<div className="mx-auto flex max-w-6xl items-center justify-between px-6 py-8 text-sm text-white/40">
|
|
156
|
+
<span>{left}</span>
|
|
157
|
+
<span>{right}</span>
|
|
158
|
+
</div>
|
|
159
|
+
</footer>
|
|
160
|
+
);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Aesthetic
|
|
164
|
+
|
|
165
|
+
One paragraph. What it feels like, the references it draws on, what to avoid (e.g. "no gradients; one accent only; borders over shadows; motion limited to hover color changes"). Commit to a single direction.
|
|
166
|
+
|
|
167
|
+
## Example usage
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
<main className="min-h-screen bg-[#0b0b10] text-white antialiased">
|
|
171
|
+
<Nav brand="Meridian" links={links} cta={{ label: 'Get started', href: '#pricing' }} />
|
|
172
|
+
<Hero eyebrow="Now in public beta" title="Your analytics, turned into decisions" lede="…">
|
|
173
|
+
<PrimaryButton href="#pricing">Start free</PrimaryButton>
|
|
174
|
+
<SecondaryButton href="#features">See how it works</SecondaryButton>
|
|
175
|
+
</Hero>
|
|
176
|
+
{/* … */}
|
|
177
|
+
<Footer left="© 2026 Meridian" right="Built with open-pages" />
|
|
178
|
+
</main>
|
|
179
|
+
```
|
|
180
|
+
````
|
|
181
|
+
|
|
182
|
+
## Step 4b — Write `themes/<id>.demo.tsx`
|
|
183
|
+
|
|
184
|
+
The demo is a normal page module — same shape as `pages/<id>/index.tsx`, just sitting under `themes/` so the runtime knows it's preview-only.
|
|
185
|
+
|
|
186
|
+
Contract:
|
|
187
|
+
|
|
188
|
+
- `import type { PageMeta } from '@autono/open-pages';` and React hooks as needed.
|
|
189
|
+
- **One default-exported component** — a single scrolling page that exercises the theme: nav, hero with both buttons, a section with a card grid, a footer.
|
|
190
|
+
- Inline the **same** fixed components defined in the theme markdown — verbatim, no abstractions. Demo and markdown must stay in lockstep so what `create-page` pastes matches what the demo shows.
|
|
191
|
+
- Root element sets `min-h-screen`, the theme background, and text color. Must look right at Mobile (390px) as well as Desktop.
|
|
192
|
+
- Content should be plausible and realistic, not lorem ipsum. Self-contained: no `@/` imports, no page-local assets; if the theme names a Google Font, render the `<link>` tag in the demo too.
|
|
193
|
+
|
|
194
|
+
## Step 5 — Self-review
|
|
195
|
+
|
|
196
|
+
- [ ] Palette table covers background / surface / text / muted / accent / border as Tailwind utilities, with hex where fixed.
|
|
197
|
+
- [ ] Frontmatter has `name` and `description` only (the runtime reads nothing else).
|
|
198
|
+
- [ ] Typography names only fonts the theme explains how to load (or the system stack).
|
|
199
|
+
- [ ] Layout specifies container width, section rhythm, and breakpoints.
|
|
200
|
+
- [ ] Fixed components are paste-ready React JSX (`className`, typed props, no `@/` imports) and cover nav, hero, section, buttons, card, footer.
|
|
201
|
+
- [ ] Aesthetic paragraph names a single coherent direction.
|
|
202
|
+
- [ ] Both files written: `themes/<id>.md` and `themes/<id>.demo.tsx`. No page changes, no config changes.
|
|
203
|
+
- [ ] Demo `.tsx` default-exports one component and inlines the same fixed components as the markdown; contrast holds; nothing overflows on mobile.
|
|
204
|
+
|
|
205
|
+
## Step 6 — Hand off
|
|
206
|
+
|
|
207
|
+
Tell the user:
|
|
208
|
+
|
|
209
|
+
- The theme id and the two file paths.
|
|
210
|
+
- That the Themes panel in the workspace (`http://localhost:5173/themes`) previews the demo live, and `/create-page` will list the theme as a picker option on its next run.
|
|
211
|
+
- A one-line summary of the look (palette + aesthetic).
|
|
212
|
+
|
|
213
|
+
Do not run the dev server. Do not modify real pages — the demo `.tsx` is the demonstration.
|
|
214
|
+
|
|
215
|
+
## Anti-patterns
|
|
216
|
+
|
|
217
|
+
- ❌ Writing executable code in `themes/<id>.md` outside the labeled component snippets — the markdown is documentation.
|
|
218
|
+
- ❌ Producing only the markdown without the demo, or only the demo without the markdown. A theme is the **bundle** — both files, every time.
|
|
219
|
+
- ❌ Desktop-only components: a nav with no mobile behaviour, grids with no stacking fallback.
|
|
220
|
+
- ❌ Naming font families the theme never explains how to load.
|
|
221
|
+
- ❌ Inventing palette / styling when the user supplied images or an existing page. Extract, don't fabricate.
|
|
222
|
+
- ❌ Editing `pages/`, `packages/`, `package.json`, or `open-pages.config.ts`.
|
|
223
|
+
- ❌ Skipping Fixed components. Nav, hero, buttons, and footer are the most common reuse targets — they must be paste-ready.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: current-page
|
|
3
|
+
description: Resolve which page and (optionally) selected element the user is currently viewing in the open-pages dev server. Consult this whenever the user references "this page", "this site", "this element", "the page I'm on", "the button I clicked", or any deictic reference to page content without naming it. Re-read `node_modules/.open-pages/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 page I'm looking at", they almost never name the page id or element — they mean wherever they are in the dev viewer. Before asking "which page?" 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 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 page 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 page or picked a different element. Trusting your last read here will silently edit the wrong file. Re-read, compare `pageId` / `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-pages/current.json
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Path is relative to the project root (the user's `cwd`, the directory that contains `pages/` and `package.json`). Use the `Read` tool. The file is JSON.
|
|
27
|
+
|
|
28
|
+
## What you get
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"pageId": "launch",
|
|
33
|
+
"pageTitle": "Meridian — Launch",
|
|
34
|
+
"view": "pages",
|
|
35
|
+
"pagePath": "pages/launch/index.tsx",
|
|
36
|
+
"selection": {
|
|
37
|
+
"line": 52,
|
|
38
|
+
"column": 8,
|
|
39
|
+
"tagName": "h1",
|
|
40
|
+
"text": "Your analytics, turned into decisions"
|
|
41
|
+
},
|
|
42
|
+
"updatedAt": "2026-08-28T14:32:11.123Z"
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- `pageId` — folder name under `pages/`. Use as-is for any `/__pages/<id>/...` API or as the URL segment (`/p/<id>`).
|
|
47
|
+
- `pageTitle` — the page's `meta.title` (or `<title>` for an HTML page), falling back to the id.
|
|
48
|
+
- `pagePath` — page entry path **relative to the project root**: `pages/<id>/index.tsx`, or `pages/<id>/index.html` for a plain HTML page. Prefix it with the project root before handing it to `Read` / `Edit`. Note the selection may point into a file under `pages/<id>/components/` if the page is split — match the line against the file whose JSX contains that tag and text.
|
|
49
|
+
- `view` — `"pages"` when the user is viewing the page, `"assets"` when they are browsing that page's files in the asset manager rather than the page itself.
|
|
50
|
+
- `selection` — `null` if nothing is selected. Otherwise, the JSX element the user picked in the inspector:
|
|
51
|
+
- `line` (1-indexed) and `column` (0-indexed) point to the JSX opening tag in the page source. This is the canonical handle — match against the source line.
|
|
52
|
+
- `tagName` is the rendered HTML tag, lowercased (`"h1"`, `"div"`, `"button"`).
|
|
53
|
+
- `text` is a trimmed text snippet (≤120 chars) of the element's content — a sanity check that you're looking at the right node.
|
|
54
|
+
- Selection auto-clears whenever the user navigates to a different page or clears it in the viewer. HTML pages never produce a selection.
|
|
55
|
+
- `updatedAt` — ISO timestamp of the last navigation or selection change. Use it to detect staleness.
|
|
56
|
+
|
|
57
|
+
## When to use this
|
|
58
|
+
|
|
59
|
+
- The user references the current page deictically: "this", "here", "the page I'm on", "the site I'm looking at", "what I'm working on".
|
|
60
|
+
- 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.
|
|
61
|
+
- Before asking "which page?" or "which element?" as a clarifying question — check this file first.
|
|
62
|
+
- Before guessing from `git log`, recently-edited files, or the most recent page folder.
|
|
63
|
+
|
|
64
|
+
## When NOT to use this
|
|
65
|
+
|
|
66
|
+
- The user names a page explicitly ("edit `launch`") — use that name directly.
|
|
67
|
+
- The `apply-comments` workflow already finds the right file via `@page-comment` markers; it doesn't need this skill.
|
|
68
|
+
- For listing or discovering pages — read `pages/` directly.
|
|
69
|
+
|
|
70
|
+
## Staleness — verify before acting
|
|
71
|
+
|
|
72
|
+
`updatedAt` is the last time the user navigated. Treat it like a cache:
|
|
73
|
+
|
|
74
|
+
- **Fresh (under ~5 minutes old)**: trust it. Open `pagePath`, do the work.
|
|
75
|
+
- **Older than ~5 minutes**: confirm with the user before editing. The dev server may not be running; the user may have switched contexts.
|
|
76
|
+
- **Hours/days old**: ignore it. Ask the user which page they mean.
|
|
77
|
+
|
|
78
|
+
A *newer* `updatedAt` than the one you saw last turn is the normal signal that the user has moved — switch to the new `pageId` / `selection` without asking.
|
|
79
|
+
|
|
80
|
+
## When the file is missing
|
|
81
|
+
|
|
82
|
+
- The dev server hasn't been opened on a page yet, or has never run.
|
|
83
|
+
- Don't create the file or guess. Ask the user which page they mean, or suggest they open the page in the dev server first.
|
|
84
|
+
|
|
85
|
+
## Example — page-level reference
|
|
86
|
+
|
|
87
|
+
User: "tighten the spacing on this page"
|
|
88
|
+
|
|
89
|
+
1. Read `node_modules/.open-pages/current.json`.
|
|
90
|
+
2. Check `updatedAt` is recent.
|
|
91
|
+
3. Read `pagePath` (e.g. `pages/launch/index.tsx`).
|
|
92
|
+
4. If `selection` is set, jump to that line; otherwise identify the relevant section from the user's words.
|
|
93
|
+
5. Consult the `page-authoring` skill for spacing and layout rules, then edit in place.
|
|
94
|
+
|
|
95
|
+
If `current.json` is missing or stale, ask: "Which page should I tighten? The dev server hasn't published a current page recently."
|
|
96
|
+
|
|
97
|
+
## Example — element-level reference
|
|
98
|
+
|
|
99
|
+
User: "make this bigger"
|
|
100
|
+
|
|
101
|
+
1. Read `node_modules/.open-pages/current.json`.
|
|
102
|
+
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`.
|
|
103
|
+
3. Consult `page-authoring` for type-scale and responsive rules before editing (bigger on desktop usually means a `sm:`/`lg:` step, not a fixed size).
|
|
104
|
+
4. Edit the JSX node in place.
|
|
105
|
+
|
|
106
|
+
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.
|