mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 02:26:31 +03:00
skill: merge orphan reference files into command sub-skills + inline S-tier invariants
Two related restructurings: 1. SKILL.md now carries the cross-domain invariants that catch defects in any project (contrast/placeholder/gray-on-color, similar-font pairing, text-wrap, tabular-nums, centered-stack default, Flex/Grid choice, auto-fit grids, semantic z-index, reduced motion, stagger vs section-fade, premium motion materials, focus-visible, placeholders-aren't-labels, dropdown overflow trap, button/link copy). Greenfield-only rules (theme picking, color strategy, tinted neutrals) live under "New projects only". 2. Reference files merged into their command counterparts: - spatial-design.md -> layout.md - motion-design.md -> animate.md - color-and-contrast.md -> colorize.md - responsive-design.md -> adapt.md - ux-writing.md -> clarify.md - typography.md -> typeset.md (bolder.md redirected) - cognitive-load.md + heuristics-scoring.md + personas.md -> critique.md craft.md and shape.md "load references" lists updated to new file homes. interaction-design.md stays standalone (no 1:1 command verb). Net: 36 -> 27 reference files. Same content, fewer files, no orphaned reference loaded only from craft.md. Also extends the routing rules: if the user's first word doesn't match a command but the intent clearly maps to one, load that command's reference and proceed as if invoked. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
33467e5f0f
commit
4edde64768
@@ -1841,45 +1841,54 @@
|
||||
fetch(url)
|
||||
.then(r => { if (!r.ok) throw new Error(r.status); return r.text(); })
|
||||
.then(html => {
|
||||
// Parse the raw source HTML
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
|
||||
let srcWrapper = null;
|
||||
|
||||
// Full-file parse works for HTML/JSX; Astro/Vue sources need marker extraction.
|
||||
const startMark = '<!-- impeccable-variants-start ' + sessionId + ' -->';
|
||||
const endMark = '<!-- impeccable-variants-end ' + sessionId + ' -->';
|
||||
const startIdx = html.indexOf(startMark);
|
||||
const endIdx = html.indexOf(endMark);
|
||||
const block = startIdx !== -1 && endIdx !== -1 && endIdx > startIdx
|
||||
? html.slice(startIdx + startMark.length, endIdx).trim()
|
||||
: html;
|
||||
const doc = parser.parseFromString(block, 'text/html');
|
||||
srcWrapper = doc.querySelector('[data-impeccable-variants="' + sessionId + '"]');
|
||||
if (!srcWrapper) {
|
||||
console.error('[impeccable] Variant wrapper not found in source file.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the original element in the live DOM.
|
||||
// The original is inside the wrapper in the source. We find the
|
||||
// corresponding element in the live DOM by matching the first child's
|
||||
// tag + classes from the original snapshot.
|
||||
const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
|
||||
if (!origContent) return;
|
||||
|
||||
const tag = origContent.tagName.toLowerCase();
|
||||
const cls = origContent.className;
|
||||
let liveEl = null;
|
||||
if (origContent.id) {
|
||||
liveEl = document.getElementById(origContent.id);
|
||||
} else if (cls) {
|
||||
// Find by tag + exact class match
|
||||
const candidates = document.querySelectorAll(tag + '.' + cls.split(' ')[0]);
|
||||
for (const c of candidates) {
|
||||
if (c.className === cls && !own(c)) { liveEl = c; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!liveEl) {
|
||||
console.error('[impeccable] Could not find original element in live DOM.');
|
||||
return;
|
||||
}
|
||||
|
||||
const previousVisibleVariant = currentSessionId === sessionId ? visibleVariant : 0;
|
||||
|
||||
// Replace the live element with the full wrapper from source
|
||||
const wrapper = srcWrapper.cloneNode(true);
|
||||
liveEl.parentElement.replaceChild(wrapper, liveEl);
|
||||
|
||||
// Wrapper already in DOM (wrap HMR landed, variant insert did not).
|
||||
const existingWrapper = document.querySelector('[data-impeccable-variants="' + sessionId + '"]');
|
||||
if (existingWrapper) {
|
||||
existingWrapper.parentElement.replaceChild(wrapper, existingWrapper);
|
||||
} else {
|
||||
const origContent = srcWrapper.querySelector('[data-impeccable-variant="original"] > :first-child');
|
||||
if (!origContent) return;
|
||||
|
||||
const tag = origContent.tagName.toLowerCase();
|
||||
const cls = origContent.className;
|
||||
let liveEl = null;
|
||||
if (origContent.id) {
|
||||
liveEl = document.getElementById(origContent.id);
|
||||
} else if (cls) {
|
||||
const candidates = document.querySelectorAll(tag + '.' + cls.split(' ')[0]);
|
||||
for (const c of candidates) {
|
||||
if (c.className === cls && !own(c)) { liveEl = c; break; }
|
||||
}
|
||||
}
|
||||
|
||||
if (!liveEl) {
|
||||
console.error('[impeccable] Could not find original element in live DOM.');
|
||||
return;
|
||||
}
|
||||
|
||||
liveEl.parentElement.replaceChild(wrapper, liveEl);
|
||||
}
|
||||
|
||||
// Update state: count variants, preserving the user's current variant
|
||||
// when a late HMR/source reinjection lands after they have cycled.
|
||||
@@ -2221,6 +2230,11 @@
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Source fallback when HMR did not land variants in this tab.
|
||||
if (msg.file && msg.id && state === 'GENERATING' && msg.id === currentSessionId) {
|
||||
injectVariantsFromSource(msg.file, msg.id);
|
||||
break;
|
||||
}
|
||||
// Variants are in source but not in the DOM yet. Common when the
|
||||
// picked element lived inside conditional render (closed modal,
|
||||
// hidden tab, a route the user navigated away from). The variant
|
||||
|
||||
@@ -370,11 +370,14 @@ function buildCssAuthoring(styleMode, count) {
|
||||
selectorExamples: variantNumbers.map((n) => `[data-impeccable-variant="${n}"] > .variant-class`),
|
||||
requirements: [
|
||||
'Use the styleTag exactly; the is:inline attribute is required for this file.',
|
||||
'Put raw CSS directly between the styleTag opening and a plain </style> close.',
|
||||
'Prefix every preview selector with the matching [data-impeccable-variant="N"] selector.',
|
||||
'Keep selectors anchored to the generated variant wrapper; do not rely on component CSS scoping for preview rules.',
|
||||
],
|
||||
forbidden: [
|
||||
'Do not use @scope for this styleMode.',
|
||||
'Do not wrap style content in a JSX/TSX template literal ({` ... `}); that syntax is for .tsx/.jsx only.',
|
||||
'Do not put { immediately after the style opening tag; Astro parses { as expression syntax.',
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user