From b1359c0f00096b37e089c704706dcb12395ce064 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Thu, 30 Apr 2026 11:19:25 +0200 Subject: [PATCH 1/7] Add examples generator SKILL draft --- .../skills/cps-examples-generator/SKILL.md | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 .github/skills/cps-examples-generator/SKILL.md diff --git a/.github/skills/cps-examples-generator/SKILL.md b/.github/skills/cps-examples-generator/SKILL.md new file mode 100644 index 00000000..ed7b663e --- /dev/null +++ b/.github/skills/cps-examples-generator/SKILL.md @@ -0,0 +1,204 @@ +--- +name: cps-examples-generator +description: > + Generates `.examples.ts` files for cps-shared-ui composition pages. Use this skill + whenever someone asks to add code examples, create an examples file, generate + examples.ts, wire up CodeExampleComponent for a new component page, or populate + HTML/TS code strings for composition previews. Triggers on phrases like "generate + examples for X component", "add code examples to Y page", "create examples.ts for Z", + or "wire up the code preview for W". Always use this skill — not general knowledge — + when working on cps-shared-ui example generation tasks. +--- + +# CPS Examples Generator + +Generates `-page.examples.ts` files for the `cps-shared-ui` composition app. +These files hold the HTML and TypeScript code strings shown in the `CodeExampleComponent` +(Preview / HTML / TS tab switcher) introduced in PR #554. + +## Context + +The composition app lives at `projects/composition/src/app/pages/-page/`. +Each component page has three files: + +- `-page.component.ts` — Angular component, imports `CodeExampleComponent`, exposes `examples` +- `-page.component.html` — Template, wraps each preview in `` +- `-page.examples.ts` — **Your output**: typed object with `html` and `ts` strings per example + +The `CodeExampleComponent` accepts: + +```html + + + +``` + +## Your Job + +Given: + +- The page's **HTML template** (to discover the live preview blocks) +- The page's **TypeScript component** (to discover reactive state, signals, options arrays, etc.) + +Produce a complete `-page.examples.ts` that: + +1. Exports a **single const object** (e.g. `export const buttonExamples = { ... }`) +2. Has one key per `` block (camelCase, matching the `label`) +3. Each key maps to `{ html: string, ts: string }` +4. The `html` string is a **faithful, minimal reproduction** of that preview block — stripped of the outer `` wrapper but keeping inner content verbatim (attributes, bindings, classes, event bindings — everything) +5. The `ts` string shows only what is **relevant to that example**: signal/property declarations, option arrays, event handlers, imports — nothing unrelated to the example + +## Step-by-Step Process + +### 1. Read the inputs + +Ask the user to paste (or point to) the following files for the component being wired up: + +- `-page.component.html` +- `-page.component.ts` + +If both are available in context already, proceed without asking. + +### 2. Identify examples + +Scan the HTML template for every `` block. For each block: + +- Note the `label` input value → this becomes the camelCase key +- Extract the inner content (the live preview markup) → this becomes the `html` string + +### 3. Extract relevant TS per example + +For each example, identify which TypeScript pieces are used by that example's HTML: + +- Signal/property declarations (`myProp = signal(...)`, `options = [...]`) +- Methods/event handlers called in the template (`onSelect(...)`, `onChange(...)`) +- Any reactive computation relevant to the example + +Do **not** include: + +- Imports (readers can figure those out from the component; they clutter the snippet) +- Lifecycle hooks unless they directly set up something used in this specific example +- Component-level boilerplate (`@Component`, `host`, `constructor`) — only include constructor logic if it directly initialises something used in this example +- Properties/signals used only in _other_ examples + +### 4. Format the output + +```typescript +// -page.examples.ts + +export const Examples = { + exampleOneLabel: { + html: ` + +`, + ts: ` +someValue = 'hello'; +handler(event: SomeType) { + // ... +}`, + }, + + exampleTwoLabel: { + html: `...`, + ts: `...`, + }, +}; + +export type Examples = typeof Examples; +``` + +## Strict Rules (never break these) + +| Rule | Why | +| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **Copy HTML verbatim** — do not paraphrase, simplify, or reformat attributes | Users copy these snippets; any change breaks their code | +| **Preserve Angular syntax exactly** — `[input]`, `(event)`, `*ngIf`, `@if`, signal calls `()`, etc. | Angular is strict about template syntax | +| **No hallucinated properties** — only include TS properties you actually see used in that specific example's HTML | Hallucinated props compile-error or mislead users | +| **No invented imports** — never add import statements to the `ts` string | They vary by project setup and add noise | +| **One object export per file** — don't split into multiple exports or use `default export` | The component imports it as `import { xExamples } from './x-page.examples'` | +| **Preserve indentation style** — if the project uses 2-space indent, keep it | Consistency with the codebase | +| **`ts` string may be empty** — if an example needs no TypeScript at all, use `ts: ''` | `CodeExampleComponent` handles empty TS gracefully | + +## Example Output (Button) + +```typescript +// button-page.examples.ts + +export const buttonExamples = { + primaryButton: { + html: ` +`, + ts: ` +onButtonClick() { + console.log('Button clicked'); +}` + }, + + disabledButton: { + html: ` +`, + ts: `` + }, + + buttonWithIcon: { + html: ` +`, + ts: `` + } +}; + +export type ButtonExamples = typeof buttonExamples; +``` + +## After generating examples.ts + +Remind the user of the two integration steps still needed: + +**1. In `-page.component.ts`**, import `CodeExampleComponent` and the examples: + +```typescript +import { CodeExampleComponent } from '../../components/code-example/code-example.component'; +import { componentExamples } from './-page.examples'; + +// inside @Component: +imports: [..., CodeExampleComponent], + +// inside class: +readonly examples = componentExamples; +``` + +**2. In `-page.component.html`**, wrap each preview: + +```html + + + +``` + +Point them to an already-wired component (Autocomplete, Button, Button Toggle from PR #554) +as a reference if they want to see a complete end-to-end example. + +## Handling Ambiguity + +- **Signal vs property unclear?** Default to property syntax in the `ts` string; it's safe for both +- **Example has complex reactive setup shared across examples?** Extract the shared state once into both `ts` strings — duplication is fine; accuracy matters more +- **Template uses `@if` / `@for` control flow?** Include in `html` verbatim; do NOT convert to `*ngIf`/`*ngFor` +- **Example uses a service injected in constructor?** Show the injection in the `ts` string as a comment: `// inject(MyService) used here` + +## File placement in the repo + +``` +projects/composition/src/app/pages/-page/ +├── -page.component.ts +├── -page.component.html +├── -page.component.scss +└── -page.examples.ts ← generated file goes here +``` From 86bcb3a145aa873235b356a27cd90a1f50d5ea5c Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 16:58:13 +0200 Subject: [PATCH 2/7] Adjust the skill --- .../skills/cps-examples-generator/SKILL.md | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/.github/skills/cps-examples-generator/SKILL.md b/.github/skills/cps-examples-generator/SKILL.md index ed7b663e..853cb9b0 100644 --- a/.github/skills/cps-examples-generator/SKILL.md +++ b/.github/skills/cps-examples-generator/SKILL.md @@ -30,8 +30,8 @@ The `CodeExampleComponent` accepts: ```html + [htmlCode]="examples.someExample.html" + [tsCode]="examples.someExample.ts"> ``` @@ -89,7 +89,7 @@ Do **not** include: ```typescript // -page.examples.ts -export const Examples = { +export const Examples: Record = { exampleOneLabel: { html: ` Examples = typeof Examples; ``` ## Strict Rules (never break these) @@ -122,14 +119,14 @@ export type Examples = typeof Examples; | **No invented imports** — never add import statements to the `ts` string | They vary by project setup and add noise | | **One object export per file** — don't split into multiple exports or use `default export` | The component imports it as `import { xExamples } from './x-page.examples'` | | **Preserve indentation style** — if the project uses 2-space indent, keep it | Consistency with the codebase | -| **`ts` string may be empty** — if an example needs no TypeScript at all, use `ts: ''` | `CodeExampleComponent` handles empty TS gracefully | +| **Omit `ts` when unused** — if an example needs no TypeScript, leave out the `ts` key entirely (it is `ts?: string`) | Omitting is cleaner than `ts: ''`; `CodeExampleComponent` handles absence gracefully | ## Example Output (Button) ```typescript // button-page.examples.ts -export const buttonExamples = { +export const buttonExamples: Record = { primaryButton: { html: ` `, @@ -141,18 +138,14 @@ onButtonClick() { disabledButton: { html: ` -`, - ts: `` +` }, buttonWithIcon: { html: ` -`, - ts: `` +` } }; - -export type ButtonExamples = typeof buttonExamples; ``` ## After generating examples.ts @@ -177,8 +170,8 @@ readonly examples = componentExamples; ```html + [htmlCode]="examples.primaryButton.html" + [tsCode]="examples.primaryButton.ts"> ``` @@ -189,7 +182,7 @@ as a reference if they want to see a complete end-to-end example. ## Handling Ambiguity - **Signal vs property unclear?** Default to property syntax in the `ts` string; it's safe for both -- **Example has complex reactive setup shared across examples?** Extract the shared state once into both `ts` strings — duplication is fine; accuracy matters more +- **Multiple examples share the same TS setup?** Extract it into a local `const` at the top of the file and reference it in each entry — e.g. `const citiesOptionsTs = \`options = [...]\`;` then `ts: citiesOptionsTs`. Duplication is fine when setups differ; factoring is preferred when they are identical. - **Template uses `@if` / `@for` control flow?** Include in `html` verbatim; do NOT convert to `*ngIf`/`*ngFor` - **Example uses a service injected in constructor?** Show the injection in the `ts` string as a comment: `// inject(MyService) used here` From 61457b223ad29a8521d9323563768457fb3d5829 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 17:00:46 +0200 Subject: [PATCH 3/7] Further skill adjustments --- .github/skills/cps-examples-generator/SKILL.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/skills/cps-examples-generator/SKILL.md b/.github/skills/cps-examples-generator/SKILL.md index 853cb9b0..29508bd6 100644 --- a/.github/skills/cps-examples-generator/SKILL.md +++ b/.github/skills/cps-examples-generator/SKILL.md @@ -150,9 +150,12 @@ onButtonClick() { ## After generating examples.ts -Remind the user of the two integration steps still needed: +Immediately apply the two integration steps — do not just describe them. -**1. In `-page.component.ts`**, import `CodeExampleComponent` and the examples: +### Step A — Update `-page.component.ts` + +1. Add `CodeExampleComponent` to the `imports` array (already imported from `'../../components/code-example/code-example.component'`) +2. Add the examples import and expose it as a readonly class property ```typescript import { CodeExampleComponent } from '../../components/code-example/code-example.component'; @@ -165,19 +168,20 @@ imports: [..., CodeExampleComponent], readonly examples = componentExamples; ``` -**2. In `-page.component.html`**, wrap each preview: +### Step B — Update `-page.component.html` + +Wrap each live preview block in ``. The `label` must match the key used in the examples object (human-readable form). If the example has a `ts` entry, bind `[tsCode]` too; omit it otherwise. ```html - + ``` -Point them to an already-wired component (Autocomplete, Button, Button Toggle from PR #554) -as a reference if they want to see a complete end-to-end example. +After applying both steps, confirm to the user that all three files have been written. ## Handling Ambiguity From 6838ba7a25f3ae82164d91b360c5e0efaaf5cfe2 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 17:05:46 +0200 Subject: [PATCH 4/7] Add example for checkbox --- .../checkbox-page.component.html | 58 ++++++++++++------- .../checkbox-page/checkbox-page.component.ts | 6 +- .../checkbox-page/checkbox-page.examples.ts | 40 +++++++++++++ 3 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 projects/composition/src/app/pages/checkbox-page/checkbox-page.examples.ts diff --git a/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.html b/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.html index 1674a190..345b11c7 100644 --- a/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.html +++ b/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.html @@ -1,30 +1,44 @@ - -
- - - - - - - + +
+ + + + + +
+
+ + +
+ + +
+
+ +
Is checked: {{ syncVal }}
-
+
diff --git a/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.ts b/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.ts index bc39403a..e304b2fb 100644 --- a/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.ts +++ b/projects/composition/src/app/pages/checkbox-page/checkbox-page.component.ts @@ -3,14 +3,17 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CpsCheckboxComponent } from 'cps-ui-kit'; import ComponentData from '../../api-data/cps-checkbox.json'; +import { CodeExampleComponent } from '../../components/code-example/code-example.component'; import { ComponentDocsViewerComponent } from '../../components/component-docs-viewer/component-docs-viewer.component'; +import { checkboxExamples } from './checkbox-page.examples'; @Component({ imports: [ CpsCheckboxComponent, ReactiveFormsModule, FormsModule, - ComponentDocsViewerComponent + ComponentDocsViewerComponent, + CodeExampleComponent ], selector: 'app-checkbox-page', templateUrl: './checkbox-page.component.html', @@ -20,4 +23,5 @@ import { ComponentDocsViewerComponent } from '../../components/component-docs-vi export class CheckboxPageComponent { syncVal = true; componentData = ComponentData; + readonly examples = checkboxExamples; } diff --git a/projects/composition/src/app/pages/checkbox-page/checkbox-page.examples.ts b/projects/composition/src/app/pages/checkbox-page/checkbox-page.examples.ts new file mode 100644 index 00000000..7a7a9711 --- /dev/null +++ b/projects/composition/src/app/pages/checkbox-page/checkbox-page.examples.ts @@ -0,0 +1,40 @@ +export const checkboxExamples: Record = { + default: { + html: ` + + + + +` + }, + + disabled: { + html: ` + +` + }, + + twoWayBinding: { + html: ` +
+ +
Is checked: {{ syncVal }}
+
`, + ts: ` +syncVal = true;` + } +}; From 4d619c528ccdd7828656998bab674af5413940e9 Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 17:09:07 +0200 Subject: [PATCH 5/7] Rename skill --- .../SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/skills/{cps-examples-generator => code-examples-generator}/SKILL.md (99%) diff --git a/.github/skills/cps-examples-generator/SKILL.md b/.github/skills/code-examples-generator/SKILL.md similarity index 99% rename from .github/skills/cps-examples-generator/SKILL.md rename to .github/skills/code-examples-generator/SKILL.md index 29508bd6..75890b52 100644 --- a/.github/skills/cps-examples-generator/SKILL.md +++ b/.github/skills/code-examples-generator/SKILL.md @@ -1,5 +1,5 @@ --- -name: cps-examples-generator +name: code-examples-generator description: > Generates `.examples.ts` files for cps-shared-ui composition pages. Use this skill whenever someone asks to add code examples, create an examples file, generate @@ -10,7 +10,7 @@ description: > when working on cps-shared-ui example generation tasks. --- -# CPS Examples Generator +# Code Examples Generator Generates `-page.examples.ts` files for the `cps-shared-ui` composition app. These files hold the HTML and TypeScript code strings shown in the `CodeExampleComponent` From 3420622bf7aeca467d6921be15c14848c6fcac3e Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 17:14:47 +0200 Subject: [PATCH 6/7] Update skill to follow repo's lint/formatting rules --- .../skills/code-examples-generator/SKILL.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/skills/code-examples-generator/SKILL.md b/.github/skills/code-examples-generator/SKILL.md index 75890b52..d21e7c32 100644 --- a/.github/skills/code-examples-generator/SKILL.md +++ b/.github/skills/code-examples-generator/SKILL.md @@ -86,6 +86,8 @@ Do **not** include: ### 4. Format the output +Follow the project's Prettier config: **single quotes, semicolons, no trailing commas, 80-char print width, 2-space indent.** + ```typescript // -page.examples.ts @@ -100,12 +102,12 @@ export const Examples: Record someValue = 'hello'; handler(event: SomeType) { // ... -}`, +}` }, exampleTwoLabel: { - html: `...`, - }, + html: `...` + } }; ``` @@ -181,7 +183,15 @@ Wrap each live preview block in ``. The `label` must match the ``` -After applying both steps, confirm to the user that all three files have been written. +### Step C — Run lint + +After all three files are written, run: + +```bash +npm run lint -- --fix +``` + +If any errors remain that `--fix` cannot resolve, fix them manually before confirming the task is done. ## Handling Ambiguity From 3e696e42d88875f18f088248f2e0fe4aeb27ce0a Mon Sep 17 00:00:00 2001 From: Lukas Matta Date: Tue, 12 May 2026 17:30:32 +0200 Subject: [PATCH 7/7] Adjust skill --- .../skills/code-examples-generator/SKILL.md | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/.github/skills/code-examples-generator/SKILL.md b/.github/skills/code-examples-generator/SKILL.md index d21e7c32..72017d34 100644 --- a/.github/skills/code-examples-generator/SKILL.md +++ b/.github/skills/code-examples-generator/SKILL.md @@ -47,9 +47,9 @@ Produce a complete `-page.examples.ts` that: 1. Exports a **single const object** (e.g. `export const buttonExamples = { ... }`) 2. Has one key per `` block (camelCase, matching the `label`) -3. Each key maps to `{ html: string, ts: string }` -4. The `html` string is a **faithful, minimal reproduction** of that preview block — stripped of the outer `` wrapper but keeping inner content verbatim (attributes, bindings, classes, event bindings — everything) -5. The `ts` string shows only what is **relevant to that example**: signal/property declarations, option arrays, event handlers, imports — nothing unrelated to the example +3. Each key maps to `{ html?: string, ts?: string }` — at least one of the two must be present; omit whichever is not needed +4. The `html` string is a **faithful, minimal reproduction** of that preview block — stripped of the outer `` wrapper, with all `cps-*` component attributes and Angular syntax preserved exactly, but composition-only layout wrapper elements removed (see Strict Rules) +5. The `ts` string shows only what is **relevant to that example**: signal/property declarations, option arrays, event handlers — nothing unrelated to the example, and never imports ## Step-by-Step Process @@ -62,13 +62,38 @@ Ask the user to paste (or point to) the following files for the component being If both are available in context already, proceed without asking. +Then scan a few existing `*.examples.ts` files from other component pages under +`projects/composition/src/app/pages/` to calibrate: + +- The level of detail expected in `html` and `ts` strings for similar patterns + (e.g. option arrays, event handlers, disabled states, multi-variant sections) +- How section comments (``, ``) are used in practice +- Any recurring shared-const patterns (factored `ts` strings reused across entries) + +Use these as a style reference, not a template to copy. The goal is consistency +with the rest of the codebase, not uniformity. + ### 2. Identify examples -Scan the HTML template for every `` block. For each block: +**If the template already contains `` blocks** (already migrated): - Note the `label` input value → this becomes the camelCase key - Extract the inner content (the live preview markup) → this becomes the `html` string +**If the template has no `` blocks** (pre-migration — initial wiring): + +Identify example boundaries by looking for natural groupings in the template. Treat each of the following as a separate example: + +- A discrete preview section separated by a heading, comment, or `
` +- A self-contained component usage block with its own set of inputs/bindings that differ meaningfully from its neighbours +- Any block the page's `.ts` file describes separately (e.g. distinct signal declarations, separate option arrays) + +For each identified block: + +- Infer a short, human-readable label from its heading or the variant it demonstrates (e.g. `"Primary button"`, `"Disabled state"`) +- Treat the raw markup of that block as the `html` string +- You will wrap it in `` in Step B after generating the examples file + ### 3. Extract relevant TS per example For each example, identify which TypeScript pieces are used by that example's HTML: @@ -91,7 +116,7 @@ Follow the project's Prettier config: **single quotes, semicolons, no trailing c ```typescript // -page.examples.ts -export const Examples: Record = { +export const Examples: Record = { exampleOneLabel: { html: ` ` elements whose sole purpose is page layout and whose class is defined only in the page's `.scss` file (e.g. `.checkboxes-group`); keep structural wrappers that are semantically part of the example (e.g. a div that displays a bound value) | Those classes don't exist outside the composition app and mislead consumers | +| **Add section comments for multi-variant examples** — when one example contains multiple size or style variants, label each group with an HTML comment (e.g. ``, ``) as in the existing button examples | Helps consumers scan the snippet without running it | | **Preserve Angular syntax exactly** — `[input]`, `(event)`, `*ngIf`, `@if`, signal calls `()`, etc. | Angular is strict about template syntax | | **No hallucinated properties** — only include TS properties you actually see used in that specific example's HTML | Hallucinated props compile-error or mislead users | | **No invented imports** — never add import statements to the `ts` string | They vary by project setup and add noise | | **One object export per file** — don't split into multiple exports or use `default export` | The component imports it as `import { xExamples } from './x-page.examples'` | | **Preserve indentation style** — if the project uses 2-space indent, keep it | Consistency with the codebase | -| **Omit `ts` when unused** — if an example needs no TypeScript, leave out the `ts` key entirely (it is `ts?: string`) | Omitting is cleaner than `ts: ''`; `CodeExampleComponent` handles absence gracefully | +| **Omit unused keys** — omit `html` when the example has no template, omit `ts` when it needs no TypeScript; at least one must be present | Omitting is cleaner than empty strings; `CodeExampleComponent` handles absence gracefully | ## Example Output (Button) ```typescript // button-page.examples.ts -export const buttonExamples: Record = { +export const buttonExamples: Record = { primaryButton: { html: ` `, @@ -172,7 +199,7 @@ readonly examples = componentExamples; ### Step B — Update `-page.component.html` -Wrap each live preview block in ``. The `label` must match the key used in the examples object (human-readable form). If the example has a `ts` entry, bind `[tsCode]` too; omit it otherwise. +Wrap each live preview block in ``. The `label` must match the key used in the examples object (human-readable form). Bind `[htmlCode]` and `[tsCode]` only for entries that have the corresponding key; omit bindings for absent keys. ```html