Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/cli/src/registry/registryBlocks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ describe("registry blocks", () => {

expect(contractFiles, templateId).toHaveLength(1);
expect(composition, templateId).toBeDefined();
const editingContract = readFileSync(join(itemDir, "TEMPLATE.md"), "utf8");
expect(editingContract, templateId).toContain("## Safe editing mechanics");
expect(editingContract, templateId).toContain("set_template_variable_defaults");
expect(editingContract, templateId).toContain("HTML-entity-encoded JSON");
const html = readFileSync(join(itemDir, composition?.path ?? ""), "utf8");
const { document } = parseHTML(html);
const declarations = JSON.parse(
Expand Down
18 changes: 18 additions & 0 deletions packages/lint/src/rules/composition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,24 @@ describe("composition rules", () => {
expect(finding).toBeUndefined();
});

it("does not warn for an HTML-entity-encoded declarations array", async () => {
const declarations = JSON.stringify([
{
id: "title",
type: "string",
label: "Title",
description: 'A "quoted" title',
default: "Hello",
},
]).replaceAll('"', """);
const html = `<html data-composition-variables='${declarations}'><body><div data-composition-id="x"></div></body></html>`;
const result = await lintHyperframeHtml(html);
const finding = result.findings.find(
(f) => f.code === "invalid_composition_variables_declaration",
);
expect(finding).toBeUndefined();
});

it("does not warn when data-composition-variables is absent", async () => {
const html = `<html><body><div data-composition-id="x"></div></body></html>`;
const result = await lintHyperframeHtml(html);
Expand Down
24 changes: 5 additions & 19 deletions packages/lint/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,27 +194,13 @@ export function readDecodedAttr(tagSource: string, attr: string): string | null
}

/**
* Read an attribute that may legitimately contain the opposite quote
* character. `readAttr` truncates `data-variable-values='{"title":"Hello"}'`
* at the first internal `"` because its `[^"']+` class excludes both quote
* types. This variant alternates: a double-quoted value never contains an
* unescaped `"`, and a single-quoted value never contains an unescaped `'`,
* so each branch can use a quote-specific class.
*
* Use for attributes whose values are JSON or otherwise carry the opposite
* quote character. Existing single-token attributes (`id`, `class`, etc.)
* stick with `readAttr` for consistency with the rest of the lint code.
* Read a JSON-bearing attribute with browser-equivalent character-reference
* decoding. Imported or formatter-serialized HTML commonly stores JSON quotes
* as `&quot;`; lint must inspect the same decoded value that `getAttribute()`
* exposes at runtime.
*/
export function readJsonAttr(tagSource: string, attr: string): string | null {
if (!tagSource) return null;
const escaped = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
// See readAttr: `(?<![\w-])` prevents a short name from matching the tail of a
// longer hyphenated attribute (e.g. "id" inside `data-hf-id`).
const match = tagSource.match(
new RegExp(`(?<![\\w-])${escaped}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "i"),
);
if (!match) return null;
return match[1] ?? match[2] ?? null;
return readDecodedAttr(tagSource, attr);
}

export function collectCompositionIds(tags: OpenTag[]): Set<string> {
Expand Down
4 changes: 4 additions & 0 deletions registry/blocks/ai-chat-reveal/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Only defaults declared in `data-composition-variables` are editable:

Typed and streamed copy is length-locked to within 20% of the original.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. For an image slot, pass only the token returned by an image tool. Validate after the setter succeeds.

## Protected

Do not change chat chrome, keyboard, layout, palette, fonts, scene order, duration, timing, easing, typing cadence, or reveal logic. Do not replace any image outside the declared closing-card logo slot, and do not restyle the assistant interface to match the supplied website. This contract declares no color variables.
4 changes: 4 additions & 0 deletions registry/blocks/chatgpt-exchange/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Only defaults declared in `data-composition-variables` are editable:

Typed and streamed copy is length-locked to within 20% of the original.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. Validate after the setter succeeds.

## Protected

Preserve the ChatGPT name, labels, suggestions, predictive keyboard text, header, composer, keyboard, icons, fonts, palette, layout, status UI, table geometry, scene structure, duration, timing, easing, typing cadence, and reveal behavior.
Expand Down
4 changes: 4 additions & 0 deletions registry/blocks/claude-exchange/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Only defaults declared in `data-composition-variables` are editable:

Typed and streamed copy is length-locked to within 20% of the original.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. Validate after the setter succeeds.

## Protected

Preserve the Claude name, model name, usage notice, placeholders, disclaimer, source treatment, header, composer, icons, starburst, fonts, palette, layout, status UI, scene structure, duration, timing, easing, typing cadence, and reveal behavior.
Expand Down
4 changes: 4 additions & 0 deletions registry/blocks/message-thread-reveal/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Only defaults declared in `data-composition-variables` are editable:

Keep replacement copy within 20% of the original length.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. For an image slot, pass only the token returned by an image tool. Validate after the setter succeeds.

## Protected

Preserve messaging chrome, bubble styling, receipts, palette, typography, geometry, scene structure, duration, timing, easing, and reveal behavior. Do not replace any image outside the declared link-card and closing-card logo slots, and do not recolor the messaging interface to match the supplied website.
4 changes: 4 additions & 0 deletions registry/blocks/notes-reveal/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Only defaults declared in `data-composition-variables` are editable:

Typed copy is length-locked to within 20% of the original.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. Validate after the setter succeeds.

## Protected

Preserve notes chrome, paper treatment, fonts, colors, checklist geometry, scene structure, duration, timing, easing, handwriting motion, and reveal cadence.
4 changes: 4 additions & 0 deletions registry/blocks/notification-cascade/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Only defaults declared in `data-composition-variables` are editable:

Keep replacement copy within 20% of the original length.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. For an image slot, pass only the token returned by an image tool. Validate after the setter succeeds.

## Protected

Do not change CSS, layout, backdrop, notification chrome, scene structure, duration, timing, easing, stacking, or reveal behavior. Do not replace any image outside the declared closing-card logo slot. Colors are protected because this template declares no color variables.
4 changes: 4 additions & 0 deletions registry/blocks/share-sheet-carousel/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ Only defaults declared in `data-composition-variables` are editable:

Keep replacement copy within 20% of the original length.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. For an image slot, pass only the token returned by an image tool. Validate after the setter succeeds.

## Protected

Preserve the share-sheet palette, typography, buttons, geometry, carousel layout, scene structure, duration, timing, easing, and tap animation. Do not replace any image outside the declared slide and logo slots, and do not recolor the operating-system interface.
4 changes: 4 additions & 0 deletions registry/blocks/slack-notification-ad/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ Only defaults declared in `data-composition-variables` are editable:

Keep replacement copy within 20% of the original length. The payoff logo must remain legible in the existing icon tile.

## Safe editing mechanics

Call `set_template_variable_defaults` once with the existing variable ids and their new defaults. Do not directly edit or rewrite `index.html` or its `data-composition-variables` attribute; the imported declaration is HTML-entity-encoded JSON and the setter preserves that encoding. Never edit `__template_baseline__.html` or a duplicate composition file. For the payoff logo, pass only the token returned by an image tool. Validate after the setter succeeds.

## Protected

Preserve the Slack mark on request notifications, iOS status/date/clock labels, wallpaper, notification chrome, fonts, palette, stacking geometry, scene structure, duration, timing, easing, and arrival cadence.
Expand Down
Loading