mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-14 07:06:45 +03:00
Refresh the Impeccable product experience
Rework the landing page proof, steering demo, feature grid, slop catalog, detector coverage, theming, Live workflow, and responsive behavior.\n\nAI-assisted implementation by OpenAI Codex.
This commit is contained in:
+17
-93
@@ -20,15 +20,6 @@ import {
|
||||
scaffoldSvelteComponentSession,
|
||||
shouldUseSvelteComponentInjection,
|
||||
} from './live/svelte-component.mjs';
|
||||
import {
|
||||
buildVueComponentCssAuthoring,
|
||||
scaffoldVueComponentSession,
|
||||
shouldUseVueComponentInjection,
|
||||
} from './live/vue-component.mjs';
|
||||
import {
|
||||
SOURCE_ARTIFACT_PREVIEW_MODE,
|
||||
scaffoldSourceArtifactSession,
|
||||
} from './live/source-artifact.mjs';
|
||||
|
||||
const EXTENSIONS = ['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro'];
|
||||
|
||||
@@ -59,8 +50,6 @@ Optional:
|
||||
--page-url URL Current page URL. Required when pending manual edits may
|
||||
affect the picked source block. Pending edits are filtered
|
||||
to this page so an edit on /a doesn't bleed into /b.
|
||||
--isolated Keep ordinary HTML/JSX/Astro source untouched during
|
||||
preview; write the wrapper to an isolated Live artifact.
|
||||
--help Show this help message
|
||||
|
||||
Output (JSON):
|
||||
@@ -79,7 +68,6 @@ The agent should insert variant HTML at insertLine.`);
|
||||
const filePath = argVal(args, '--file');
|
||||
const text = argVal(args, '--text');
|
||||
const pageUrl = argVal(args, '--page-url');
|
||||
const isolated = args.includes('--isolated');
|
||||
|
||||
if (!id) { console.error('Missing --id'); process.exit(1); }
|
||||
if (!elementId && !classes && !query) {
|
||||
@@ -172,29 +160,11 @@ The agent should insert variant HTML at insertLine.`);
|
||||
if (filtered.length === 1) {
|
||||
match = filtered[0];
|
||||
} else if (filtered.length === 0) {
|
||||
const normalizedText = String(text).replace(/\s+/g, ' ').trim();
|
||||
if (normalizedText.length < 8) {
|
||||
// Very short labels cannot disambiguate siblings reliably. Preserve
|
||||
// the legacy behavior for these low-information picker events.
|
||||
match = candidates[0];
|
||||
} else {
|
||||
// Rendered text that is absent from every candidate usually means
|
||||
// the source uses expressions or component props. Picking the first
|
||||
// same-class sibling silently edits the wrong instance (observed on
|
||||
// Astro result cards), so stop and surface every candidate instead.
|
||||
console.error(JSON.stringify({
|
||||
error: 'element_ambiguous',
|
||||
fallback: 'agent-driven',
|
||||
reason: 'rendered_text_not_in_source',
|
||||
file: path.relative(process.cwd(), targetFile),
|
||||
candidates: candidates.map((c) => ({
|
||||
startLine: c.startLine + 1,
|
||||
endLine: c.endLine + 1,
|
||||
})),
|
||||
hint: 'Rendered text does not occur in any matching source branch. The element may use dynamic props or expressions; inspect the candidates and wrap the intended instance manually.',
|
||||
}));
|
||||
process.exit(1);
|
||||
}
|
||||
// Source uses dynamic content (`<h1>{title}</h1>` etc.) so the
|
||||
// browser-side textContent doesn't appear literally in source. Fall
|
||||
// back to first-match rather than refusing — this is the same
|
||||
// behavior unmodified callers see, just preserved.
|
||||
match = candidates[0];
|
||||
} else {
|
||||
// Multiple candidates ALSO match the text. Truly ambiguous — refuse
|
||||
// rather than pick wrong, and hand the agent the candidate locations
|
||||
@@ -237,7 +207,6 @@ The agent should insert variant HTML at insertLine.`);
|
||||
// Strip only the COMMON minimum leading whitespace across the picked lines;
|
||||
// `deindentContent` on the accept side already mirrors this convention.
|
||||
let originalLines = lines.slice(startLine, endLine + 1);
|
||||
const sourceOriginalLines = [...originalLines];
|
||||
|
||||
// Buffer-aware "original" content: if the user has pending manual edits for
|
||||
// this page whose originalText appears in the picked source range, apply
|
||||
@@ -300,9 +269,6 @@ The agent should insert variant HTML at insertLine.`);
|
||||
const originalIndented = reindentOriginal(' ');
|
||||
const relTargetFile = path.relative(process.cwd(), targetFile).split(path.sep).join('/');
|
||||
const useSvelteComponent = shouldUseSvelteComponentInjection(targetFile);
|
||||
const useVueComponent = !useSvelteComponent && shouldUseVueComponentInjection(targetFile);
|
||||
const useFrameworkComponent = useSvelteComponent || useVueComponent;
|
||||
const useSourceArtifact = isolated && !useFrameworkComponent;
|
||||
|
||||
// Wrapper attributes differ by syntax. HTML allows plain string attrs;
|
||||
// JSX requires object-literal style and parses string attrs as HTML (which
|
||||
@@ -321,11 +287,8 @@ The agent should insert variant HTML at insertLine.`);
|
||||
// tuck both marker comments INSIDE it. accept/discard then expands its
|
||||
// replacement range to include the wrapper's `<div>` open / close lines
|
||||
// so the entire scaffold gets removed cleanly.
|
||||
const sourceArtifactAttr = useSourceArtifact
|
||||
? ' data-impeccable-preview="' + SOURCE_ARTIFACT_PREVIEW_MODE + '"'
|
||||
: '';
|
||||
const wrapperLines = isJsx ? [
|
||||
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '"' + sourceArtifactAttr + ' ' + styleContents + '>',
|
||||
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" ' + styleContents + '>',
|
||||
indent + ' ' + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
|
||||
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
|
||||
indent + ' <div data-impeccable-variant="original">',
|
||||
@@ -336,7 +299,7 @@ The agent should insert variant HTML at insertLine.`);
|
||||
indent + '</div>',
|
||||
] : [
|
||||
indent + commentSyntax.open + ' impeccable-variants-start ' + id + ' ' + commentSyntax.close,
|
||||
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '"' + sourceArtifactAttr + ' ' + styleContents + '>',
|
||||
indent + '<div data-impeccable-variants="' + id + '" data-impeccable-variant-count="' + count + '" ' + styleContents + '>',
|
||||
indent + ' ' + commentSyntax.open + ' Original ' + commentSyntax.close,
|
||||
indent + ' <div data-impeccable-variant="original">',
|
||||
originalIndented,
|
||||
@@ -352,8 +315,6 @@ The agent should insert variant HTML at insertLine.`);
|
||||
let outputEndLine = startLine + wrapperLines.length + (originalLines.length - 1);
|
||||
let insertLine;
|
||||
let svelteSession = null;
|
||||
let vueSession = null;
|
||||
let sourceArtifactSession = null;
|
||||
|
||||
if (useSvelteComponent) {
|
||||
// Svelte/SvelteKit resets component-local state on markup HMR updates.
|
||||
@@ -373,38 +334,6 @@ The agent should insert variant HTML at insertLine.`);
|
||||
outputStartLine = 1;
|
||||
outputEndLine = 1;
|
||||
insertLine = 1;
|
||||
} else if (useVueComponent) {
|
||||
// Nuxt route-module HMR can invalidate the active page while a generated
|
||||
// wrapper is only partially written. Stage real Vue SFCs in an app-local
|
||||
// dev module tree and leave the route untouched until Accept.
|
||||
vueSession = scaffoldVueComponentSession({
|
||||
id,
|
||||
count,
|
||||
sourceFile: relTargetFile,
|
||||
sourceStartLine: startLine + 1,
|
||||
sourceEndLine: endLine + 1,
|
||||
originalLines,
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
outputFile = path.resolve(process.cwd(), vueSession.manifestFile);
|
||||
outputStartLine = 1;
|
||||
outputEndLine = 1;
|
||||
insertLine = 1;
|
||||
} else if (useSourceArtifact) {
|
||||
sourceArtifactSession = scaffoldSourceArtifactSession({
|
||||
id,
|
||||
count,
|
||||
sourceFile: relTargetFile,
|
||||
sourceStartLine: startLine + 1,
|
||||
sourceEndLine: endLine + 1,
|
||||
originalSource: sourceOriginalLines.join('\n'),
|
||||
previewContent: wrapperLines.join('\n'),
|
||||
cwd: process.cwd(),
|
||||
});
|
||||
outputFile = path.resolve(process.cwd(), sourceArtifactSession.previewFile);
|
||||
outputStartLine = 1;
|
||||
outputEndLine = wrapperLines.length + (originalLines.length - 1);
|
||||
insertLine = 6 + (originalLines.length - 1) + 1;
|
||||
} else {
|
||||
// Replace the original element with the wrapper
|
||||
const newLines = [
|
||||
@@ -427,20 +356,15 @@ The agent should insert variant HTML at insertLine.`);
|
||||
const outputRelFile = path.relative(process.cwd(), outputFile).split(path.sep).join('/');
|
||||
|
||||
const svelteComponentAuthoring = useSvelteComponent ? buildSvelteComponentCssAuthoring(count) : null;
|
||||
const vueComponentAuthoring = useVueComponent ? buildVueComponentCssAuthoring(count) : null;
|
||||
const componentSession = svelteSession || vueSession;
|
||||
const componentPreviewMode = useSvelteComponent ? 'svelte-component' : useVueComponent ? 'vue-component' : undefined;
|
||||
const previewMode = componentPreviewMode || (useSourceArtifact ? SOURCE_ARTIFACT_PREVIEW_MODE : undefined);
|
||||
|
||||
console.log(JSON.stringify({
|
||||
file: outputRelFile,
|
||||
sourceFile: useFrameworkComponent || useSourceArtifact ? relTargetFile : undefined,
|
||||
previewMode,
|
||||
previewManifest: sourceArtifactSession?.manifestFile,
|
||||
componentDir: componentSession?.componentDir,
|
||||
propContract: componentSession?.propContract,
|
||||
sourceStartLine: useFrameworkComponent ? startLine + 1 : undefined,
|
||||
sourceEndLine: useFrameworkComponent ? endLine + 1 : undefined,
|
||||
sourceFile: useSvelteComponent ? relTargetFile : undefined,
|
||||
previewMode: useSvelteComponent ? 'svelte-component' : undefined,
|
||||
componentDir: svelteSession?.componentDir,
|
||||
propContract: svelteSession?.propContract,
|
||||
sourceStartLine: useSvelteComponent ? startLine + 1 : undefined,
|
||||
sourceEndLine: useSvelteComponent ? endLine + 1 : undefined,
|
||||
startLine: outputStartLine, // 1-indexed for the agent
|
||||
// wrapperLines is an array but one element (the original-content slot)
|
||||
// is a `\n`-joined multi-line string, so the actual file-row count is
|
||||
@@ -450,10 +374,10 @@ The agent should insert variant HTML at insertLine.`);
|
||||
endLine: outputEndLine, // 1-indexed
|
||||
insertLine, // 1-indexed: where variants go
|
||||
commentSyntax: commentSyntax,
|
||||
styleMode: componentPreviewMode || styleMode.mode,
|
||||
styleTag: useFrameworkComponent ? null : styleMode.styleTag,
|
||||
cssSelectorPrefixExamples: useFrameworkComponent ? [] : buildCssSelectorPrefixExamples(styleMode.mode, count),
|
||||
cssAuthoring: svelteComponentAuthoring || vueComponentAuthoring || buildCssAuthoring(styleMode, count),
|
||||
styleMode: useSvelteComponent ? 'svelte-component' : styleMode.mode,
|
||||
styleTag: useSvelteComponent ? null : styleMode.styleTag,
|
||||
cssSelectorPrefixExamples: useSvelteComponent ? [] : buildCssSelectorPrefixExamples(styleMode.mode, count),
|
||||
cssAuthoring: useSvelteComponent ? svelteComponentAuthoring : buildCssAuthoring(styleMode, count),
|
||||
originalLineCount: originalLines.length,
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user