mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-15 23:56:29 +03:00
Add wrap CLI helper and optimize agent generation loop
Three optimizations to cut the generate loop from ~40s to ~15-20s: 1. wrap CLI helper (src/live/wrap.mjs): finds an element in source by ID, class names, or tag+class combo, wraps it in the variant container with original snapshot, and returns the file path + insert line. Replaces 3-4 agent tool calls (grep + read + edit) with one. Supports --element-id, --classes (comma-separated), --tag, --query (fallback). Searches in priority order: ID > class combo > single class > raw text. Auto-detects comment syntax (HTML vs JSX). 2. Batch variant writes: skill reference updated to instruct the agent to write ALL variants in a single file edit instead of one per variant. Saves N-1 tool call round-trips (~3-5s each). 3. Page URL in generate event: browser now includes location.pathname so the agent can map URL to source file directly (/ = index.html, /about = about.tsx, etc.) without grepping. Net effect: agent flow is now 4 tool calls (wrap + edit + read-variant + poll-reply) instead of 8+ (grep + read + create-wrapper + N edits + poll-reply). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
26e0b8a786
commit
4535525f8e
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -28,6 +28,7 @@ Commands:
|
||||
live stop Stop a running live server
|
||||
poll Wait for a browser event from the live server
|
||||
poll --reply <id> <status> Reply to a pending event (done, error)
|
||||
wrap --id ID --count N --query Q Find element in source and create variant wrapper
|
||||
skills help List all available skills and commands
|
||||
skills install Install impeccable skills into your project
|
||||
skills update Update skills to the latest version
|
||||
@@ -59,6 +60,10 @@ if (command === 'detect') {
|
||||
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
|
||||
const { pollCli } = await import('../src/live/poll.mjs');
|
||||
await pollCli();
|
||||
} else if (command === 'wrap') {
|
||||
process.argv = [process.argv[0], process.argv[1], ...args.slice(1)];
|
||||
const { wrapCli } = await import('../src/live/wrap.mjs');
|
||||
await wrapCli();
|
||||
} else if (command === 'skills') {
|
||||
const { run } = await import('./commands/skills.mjs');
|
||||
await run(args.slice(1));
|
||||
|
||||
@@ -76,80 +76,68 @@ END LOOP
|
||||
|
||||
## Handle Generate
|
||||
|
||||
The event contains: `{id, action, freeformPrompt, count, element}`.
|
||||
The event contains: `{id, action, freeformPrompt, count, pageUrl, element}`.
|
||||
|
||||
### Step 1: Find the source file
|
||||
**Speed matters.** The user is watching a spinner. Minimize tool calls by using the `wrap` helper and writing all variants in a single edit.
|
||||
|
||||
Use `element.tagName`, `element.id`, `element.classes`, `element.textContent`, and `element.outerHTML` to locate the element in the project source. Search for matching markup across the codebase.
|
||||
### Step 1: Wrap the element (one CLI call)
|
||||
|
||||
### Step 2: Create the variant wrapper
|
||||
Use the `wrap` helper to find the element and create the variant container:
|
||||
|
||||
Wrap the original element in a variant container. Use the comment syntax appropriate for the framework:
|
||||
|
||||
**HTML / Vue / Svelte:**
|
||||
```html
|
||||
<!-- impeccable-variants-start SESSION_ID -->
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count="COUNT" style="display: contents">
|
||||
<div data-impeccable-variant="original" style="display: none">
|
||||
<!-- move the original element here -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- impeccable-variants-end SESSION_ID -->
|
||||
```bash
|
||||
npx impeccable wrap --id EVENT_ID --count EVENT_COUNT --element-id "ELEMENT_ID" --classes "class1,class2" --tag "div"
|
||||
```
|
||||
|
||||
**JSX / TSX:**
|
||||
```jsx
|
||||
{/* impeccable-variants-start SESSION_ID */}
|
||||
<div data-impeccable-variants="SESSION_ID" data-impeccable-variant-count={COUNT} style={{display: 'contents'}}>
|
||||
<div data-impeccable-variant="original" style={{display: 'none'}}>
|
||||
{/* move the original element here */}
|
||||
</div>
|
||||
</div>
|
||||
{/* impeccable-variants-end SESSION_ID */}
|
||||
Pass the element's id (`event.element.id`), classes (`event.element.classes` joined with commas), and tag name. The command searches in priority order: ID match first, then class names, then tag+class combo. If `event.pageUrl` hints at the file (e.g., `/` is usually `index.html`), pass `--file PATH` to skip the search.
|
||||
|
||||
The command outputs JSON with the file path and the insert line:
|
||||
```json
|
||||
{"file": "public/index.html", "insertLine": 93, "commentSyntax": {"open": "<!--", "close": "-->"}}
|
||||
```
|
||||
|
||||
Replace SESSION_ID with `event.id` and COUNT with `event.count`.
|
||||
If `wrap` fails, fall back to manual grep + edit.
|
||||
|
||||
`display: contents` makes the wrapper layout-transparent, preserving the original element's relationship with its parent (flex/grid child, etc.).
|
||||
|
||||
### Step 3: Generate variants one by one
|
||||
|
||||
For each variant (1 through COUNT):
|
||||
### Step 2: Generate all variants and write them in a SINGLE edit
|
||||
|
||||
1. **Load the design command's reference file.** If `event.action` is "bolder", load `reference/bolder.md`. If "impeccable" (the default), use the main design principles from this skill without loading a sub-command reference.
|
||||
|
||||
2. **Generate a complete replacement** for the original element. Each variant is a full HTML+CSS rewrite, not a patch. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
2. **Generate ALL variants at once.** For each variant, create a complete HTML replacement of the original element. Consider the element's context (computed styles, parent structure, CSS custom properties from `event.element`).
|
||||
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate 4 variations on the same idea.
|
||||
3. **Diversify across variants.** Each variant should take a distinctly different approach. For "bolder", one might focus on type weight, another on color saturation, another on spatial scale, another on structural change. Do NOT generate N variations on the same idea.
|
||||
|
||||
4. **If a freeform prompt was provided** (`event.freeformPrompt`), use it as additional guidance for all variants.
|
||||
|
||||
5. **Write the variant** into the wrapper in the source file:
|
||||
```html
|
||||
<div data-impeccable-variant="N" style="display: none">
|
||||
<!-- variant N content -->
|
||||
</div>
|
||||
```
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default).
|
||||
5. **Write all variants in a single file edit** at the insert line reported by `wrap`. Use the comment syntax from the `wrap` output:
|
||||
|
||||
6. **Write scoped CSS** if the variant needs styles beyond inline:
|
||||
```html
|
||||
<!-- Variants: insert below this line -->
|
||||
<div data-impeccable-variant="1">
|
||||
<!-- variant 1: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="2" style="display: none">
|
||||
<!-- variant 2: full element replacement -->
|
||||
</div>
|
||||
<div data-impeccable-variant="3" style="display: none">
|
||||
<!-- variant 3: full element replacement -->
|
||||
</div>
|
||||
```
|
||||
|
||||
The first variant should NOT have `style="display: none"` (it should be visible by default). All others should.
|
||||
|
||||
6. **Write scoped CSS** if the variants need styles beyond inline:
|
||||
```css
|
||||
/* impeccable-variants-css-start SESSION_ID */
|
||||
@scope ([data-impeccable-variant="N"]) {
|
||||
:scope { /* styles for the variant root */ }
|
||||
.child-class { /* styles for children */ }
|
||||
}
|
||||
@scope ([data-impeccable-variant="1"]) { ... }
|
||||
@scope ([data-impeccable-variant="2"]) { ... }
|
||||
/* impeccable-variants-css-end SESSION_ID */
|
||||
```
|
||||
Place the CSS in a `<style>` block in the same file, or in the component's CSS file.
|
||||
|
||||
7. **Save the file** after each variant. The dev server's HMR will update the browser, and the live script's MutationObserver will detect the new variant and activate it in the cycler UI.
|
||||
**IMPORTANT**: Write all variants in ONE edit call, not one per variant. This saves multiple round-trips and the browser's MutationObserver will pick up all variants at once.
|
||||
|
||||
### Step 4: Signal completion
|
||||
### Step 3: Signal completion
|
||||
|
||||
After all variants are written:
|
||||
```bash
|
||||
npx impeccable poll --reply SESSION_ID done
|
||||
npx impeccable poll --reply EVENT_ID done
|
||||
```
|
||||
|
||||
## Handle Accept
|
||||
|
||||
@@ -864,6 +864,7 @@
|
||||
action: selectedAction,
|
||||
freeformPrompt: prompt || undefined,
|
||||
count: selectedCount,
|
||||
pageUrl: location.pathname,
|
||||
element: extractContext(selectedElement),
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* CLI helper: find an element in source and wrap it in a variant container.
|
||||
*
|
||||
* Usage:
|
||||
* npx impeccable wrap --id SESSION_ID --count N --query "hero-combined-left" [--file path]
|
||||
*
|
||||
* Searches project files for the element matching the query (class name, ID, or
|
||||
* text snippet), wraps it with the variant scaffolding, and prints the file path
|
||||
* + line range where the agent should insert variant HTML.
|
||||
*
|
||||
* This replaces 3-4 agent tool calls (grep + read + edit) with a single CLI call.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const EXTENSIONS = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro'];
|
||||
|
||||
export async function wrapCli() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.includes('--help') || args.includes('-h')) {
|
||||
console.log(`Usage: impeccable wrap [options]
|
||||
|
||||
Find an element in source and wrap it in a variant container.
|
||||
|
||||
Required:
|
||||
--id ID Session ID for the variant wrapper
|
||||
--count N Number of expected variants (1-8)
|
||||
|
||||
Element identification (at least one required):
|
||||
--element-id ID HTML id attribute of the element
|
||||
--classes A,B,C Comma-separated CSS class names
|
||||
--tag TAG Tag name (div, section, etc.)
|
||||
--query TEXT Fallback: raw text to search for
|
||||
|
||||
Optional:
|
||||
--file PATH Source file to search in (skips auto-detection)
|
||||
--help Show this help message
|
||||
|
||||
Output (JSON):
|
||||
{ file, startLine, endLine, insertLine, commentSyntax }
|
||||
|
||||
The agent should insert variant HTML at insertLine.`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const id = argVal(args, '--id');
|
||||
const count = parseInt(argVal(args, '--count') || '3');
|
||||
const elementId = argVal(args, '--element-id');
|
||||
const classes = argVal(args, '--classes');
|
||||
const tag = argVal(args, '--tag');
|
||||
const query = argVal(args, '--query');
|
||||
const filePath = argVal(args, '--file');
|
||||
|
||||
if (!id) { console.error('Missing --id'); process.exit(1); }
|
||||
if (!elementId && !classes && !query) {
|
||||
console.error('Need at least one of: --element-id, --classes, --query');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Build search queries in priority order (most specific first)
|
||||
const queries = buildSearchQueries(elementId, classes, tag, query);
|
||||
|
||||
// Find the source file
|
||||
let targetFile = filePath;
|
||||
let matchedQuery = null;
|
||||
if (!targetFile) {
|
||||
for (const q of queries) {
|
||||
targetFile = findFileWithQuery(q, process.cwd());
|
||||
if (targetFile) { matchedQuery = q; break; }
|
||||
}
|
||||
if (!targetFile) {
|
||||
console.error(JSON.stringify({ error: 'Could not find element in project files. Searched for: ' + queries.join(', ') }));
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
matchedQuery = queries[0];
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(targetFile, 'utf-8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Find the element, trying each query in priority order
|
||||
let match = null;
|
||||
for (const q of queries) {
|
||||
match = findElement(lines, q);
|
||||
if (match) break;
|
||||
}
|
||||
if (!match) {
|
||||
console.error(JSON.stringify({ error: 'Found file but could not locate element in ' + targetFile + '. Searched for: ' + queries.join(', ') }));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const { startLine, endLine } = match;
|
||||
const commentSyntax = detectCommentSyntax(targetFile);
|
||||
const indent = lines[startLine].match(/^(\s*)/)[1];
|
||||
|
||||
// Extract the original element
|
||||
const originalLines = lines.slice(startLine, endLine + 1);
|
||||
const originalIndented = originalLines.map(l => indent + ' ' + l.trimStart()).join('\n');
|
||||
|
||||
// Build the wrapper
|
||||
const wrapperLines = [
|
||||
indent + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
|
||||
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" style="display: contents">',
|
||||
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
|
||||
indent + ' <div data-impeccable-variant="original" style="display: none">',
|
||||
originalIndented,
|
||||
indent + ' </div>',
|
||||
indent + ' ' + commentSyntax.open + ' Variants: insert below this line ' + commentSyntax.close,
|
||||
indent + '</div>',
|
||||
indent + commentSyntax.open + ' impeccable-variants-end ' + id + ' ' + commentSyntax.close,
|
||||
];
|
||||
|
||||
// Replace the original element with the wrapper
|
||||
const newLines = [
|
||||
...lines.slice(0, startLine),
|
||||
...wrapperLines,
|
||||
...lines.slice(endLine + 1),
|
||||
];
|
||||
fs.writeFileSync(targetFile, newLines.join('\n'), 'utf-8');
|
||||
|
||||
// Calculate insert line (the "insert below this line" comment)
|
||||
const insertLine = startLine + 6; // 0-indexed in the new file
|
||||
|
||||
console.log(JSON.stringify({
|
||||
file: path.relative(process.cwd(), targetFile),
|
||||
startLine: startLine + 1, // 1-indexed for the agent
|
||||
endLine: startLine + wrapperLines.length, // 1-indexed
|
||||
insertLine: insertLine + 1, // 1-indexed: where variants go
|
||||
commentSyntax: commentSyntax,
|
||||
originalLineCount: originalLines.length,
|
||||
}));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function argVal(args, flag) {
|
||||
const idx = args.indexOf(flag);
|
||||
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build search query strings in priority order (most specific first).
|
||||
* ID is most reliable, then specific class combos, then single classes, then raw query.
|
||||
*/
|
||||
function buildSearchQueries(elementId, classes, tag, query) {
|
||||
const queries = [];
|
||||
|
||||
// 1. ID is the most specific
|
||||
if (elementId) {
|
||||
queries.push('id="' + elementId + '"');
|
||||
}
|
||||
|
||||
// 2. Full class attribute match (for elements with distinctive multi-class combos)
|
||||
if (classes) {
|
||||
const classList = classes.split(',').map(c => c.trim()).filter(Boolean);
|
||||
if (classList.length > 1) {
|
||||
// Try the most distinctive class first (longest, most specific)
|
||||
const sorted = [...classList].sort((a, b) => b.length - a.length);
|
||||
queries.push('class="' + classList.join(' ') + '"'); // exact full match
|
||||
queries.push(sorted[0]); // most distinctive single class
|
||||
} else if (classList.length === 1) {
|
||||
queries.push(classList[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Tag + class combo (e.g., <section class="hero">)
|
||||
if (tag && classes) {
|
||||
const firstClass = classes.split(',')[0].trim();
|
||||
queries.push('<' + tag + ' class="' + firstClass);
|
||||
}
|
||||
|
||||
// 4. Raw fallback query
|
||||
if (query) {
|
||||
queries.push(query);
|
||||
}
|
||||
|
||||
return queries;
|
||||
}
|
||||
|
||||
function detectCommentSyntax(filePath) {
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (ext === '.jsx' || ext === '.tsx') {
|
||||
return { open: '{/*', close: '*/}' };
|
||||
}
|
||||
// HTML, Vue, Svelte, Astro all use HTML comments
|
||||
return { open: '<!--', close: '-->' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Search project files for the query string (class name, ID, etc.)
|
||||
* Returns the first matching file path, or null.
|
||||
*/
|
||||
function findFileWithQuery(query, cwd) {
|
||||
const searchDirs = ['src', 'app', 'pages', 'components', 'public', 'views', 'templates', '.'];
|
||||
const seen = new Set();
|
||||
|
||||
for (const dir of searchDirs) {
|
||||
const absDir = path.join(cwd, dir);
|
||||
if (!fs.existsSync(absDir)) continue;
|
||||
const result = searchDir(absDir, query, seen, 0);
|
||||
if (result) return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function searchDir(dir, query, seen, depth) {
|
||||
if (depth > 5) return null; // don't go too deep
|
||||
const realDir = fs.realpathSync(dir);
|
||||
if (seen.has(realDir)) return null;
|
||||
seen.add(realDir);
|
||||
|
||||
let entries;
|
||||
try { entries = fs.readdirSync(dir, { withFileTypes: true }); }
|
||||
catch { return null; }
|
||||
|
||||
// Check files first
|
||||
for (const entry of entries) {
|
||||
if (!entry.isFile()) continue;
|
||||
const ext = path.extname(entry.name).toLowerCase();
|
||||
if (!EXTENSIONS.includes(ext)) continue;
|
||||
|
||||
const filePath = path.join(dir, entry.name);
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
if (content.includes(query)) return filePath;
|
||||
} catch { /* skip unreadable files */ }
|
||||
}
|
||||
|
||||
// Then recurse into directories
|
||||
for (const entry of entries) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === 'dist' || entry.name === 'build') continue;
|
||||
const result = searchDir(path.join(dir, entry.name), query, seen, depth + 1);
|
||||
if (result) return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the element's start and end line in the file.
|
||||
* The query is a class name, ID, or text snippet.
|
||||
* We find the line containing the query, then find the matching closing tag.
|
||||
*/
|
||||
function findElement(lines, query) {
|
||||
// Find the line containing the query
|
||||
let startLine = -1;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (lines[i].includes(query)) {
|
||||
// Make sure this looks like a tag opening, not a comment or string
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith('<!--') || line.startsWith('{/*') || line.startsWith('//')) continue;
|
||||
// Skip lines inside data-impeccable-variant containers (already wrapped)
|
||||
if (lines[i].includes('data-impeccable-variant')) continue;
|
||||
startLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (startLine === -1) return null;
|
||||
|
||||
// Find the end of this element by counting open/close tags
|
||||
const endLine = findClosingLine(lines, startLine);
|
||||
return { startLine, endLine };
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting from a line with an opening tag, find the line with the matching
|
||||
* closing tag by counting tag nesting depth.
|
||||
*/
|
||||
function findClosingLine(lines, start) {
|
||||
// Extract the tag name from the opening line
|
||||
const openMatch = lines[start].match(/<(\w+)[\s>]/);
|
||||
if (!openMatch) return start; // self-closing or text-only line
|
||||
|
||||
const tagName = openMatch[1];
|
||||
let depth = 0;
|
||||
|
||||
for (let i = start; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
// Count opening tags (not self-closing)
|
||||
const opens = (line.match(new RegExp('<' + tagName + '[\\s>]', 'g')) || []).length;
|
||||
const selfCloses = (line.match(new RegExp('<' + tagName + '[^>]*/>', 'g')) || []).length;
|
||||
const closes = (line.match(new RegExp('</' + tagName + '\\s*>', 'g')) || []).length;
|
||||
|
||||
depth += opens - selfCloses - closes;
|
||||
|
||||
if (depth <= 0) return i;
|
||||
}
|
||||
|
||||
// If we can't find the close, return a reasonable guess
|
||||
return Math.min(start + 50, lines.length - 1);
|
||||
}
|
||||
Reference in New Issue
Block a user