quiver-cli 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +237 -10
- package/package.json +1 -1
- package/template/.agents/skills/agent-browser/SKILL.md +1 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +2 -0
- package/template/.agents/skills/data/prisma-cli/SKILL.md +25 -7
- package/template/.agents/skills/data/prisma-cli/references/agent-safety.md +27 -0
- package/template/.agents/skills/data/prisma-cli/references/complete.md +22 -0
- package/template/.agents/skills/data/prisma-cli/references/db-push.md +2 -0
- package/template/.agents/skills/data/prisma-cli/references/init.md +3 -0
- package/template/.agents/skills/data/prisma-cli/references/mcp.md +2 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +1 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +2 -0
- package/template/.agents/skills/data/prisma-client-api/SKILL.md +1 -1
- package/template/.agents/skills/data/prisma-client-api/references/constructor.md +13 -0
- package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +4 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +5 -5
- package/template/.agents/skills/design/impeccable/reference/android.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +3 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +3 -1
- package/template/.agents/skills/design/impeccable/reference/craft-floor.md +2 -0
- package/template/.agents/skills/design/impeccable/reference/critique.md +23 -5
- package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +15 -68
- package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +13 -12
- package/template/.agents/skills/design/impeccable/reference/distill.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/doctor.md +1 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/extract.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/hooks.md +17 -11
- package/template/.agents/skills/design/impeccable/reference/init.md +9 -3
- package/template/.agents/skills/design/impeccable/reference/ios.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/new-work.md +69 -29
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/polish.md +13 -5
- package/template/.agents/skills/design/impeccable/reference/quieter.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/routing.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/visualize.md +21 -22
- package/template/.agents/skills/design/impeccable/scripts/build-phase.mjs +1022 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-diff.mjs +391 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-spec.mjs +513 -0
- package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +297 -41
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +10 -19
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +124 -9
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +279 -19
- package/template/.agents/skills/design/impeccable/scripts/data/font-index-failures.json +121 -0
- package/template/.agents/skills/design/impeccable/scripts/data/font-index.json +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detect.mjs +9 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +192 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +10 -16
- package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +339 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +1482 -722
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +64 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +580 -29
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +62 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +31 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +23 -22
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +18 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +471 -370
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +474 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +17 -2
- package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +14 -21
- package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +81 -48
- package/template/.agents/skills/design/impeccable/scripts/font-match.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +219 -12
- package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +93 -15
- package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +41 -19
- package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +507 -117
- package/template/.agents/skills/design/impeccable/scripts/hook.mjs +10 -9
- package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +40 -1
- package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +120 -82
- package/template/.agents/skills/design/impeccable/scripts/lib/font-fingerprint.mjs +564 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/font-index.mjs +130 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/hero-checks.mjs +246 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/image-metrics.mjs +306 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +41 -59
- package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +5 -2
- package/template/.agents/skills/design/impeccable/scripts/lib/live-path-globs.mjs +37 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/open-system-browser.mjs +26 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/png.mjs +281 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/raster.mjs +194 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +26 -19
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +31 -3
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +93 -17
- package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +9 -11
- package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +31 -2
- package/template/.agents/skills/design/impeccable/scripts/live/project-ignores.mjs +139 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +10 -2
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +26 -2
- package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +15 -27
- package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +4 -25
- package/template/.agents/skills/design/impeccable/scripts/live/ui-surfaces.mjs +75 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +21 -37
- package/template/.agents/skills/design/impeccable/scripts/live-browser-ignores.js +242 -0
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +62 -23
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +68 -112
- package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +132 -15
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +2 -42
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +5 -4
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +56 -19
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +12 -37
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +8 -5
- package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +1006 -155
- package/template/.agents/skills/design/shadcn/rules/chat.md +26 -0
- package/template/.agents/skills/hono/SKILL.md +17 -3
- package/template/.agents/skills/integrations/langfuse/SKILL.md +8 -4
- package/template/.agents/skills/integrations/langfuse/references/cli.md +1 -1
- package/template/.agents/skills/integrations/langfuse/references/create-dataset.md +35 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +9 -10
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +8 -6
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +34 -0
- package/template/.agents/skills/integrations/langfuse/references/setting-up-evals.md +65 -0
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +20 -40
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -30
- package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +3 -1
- package/template/.agents/skills/supabase/CHANGELOG.md +7 -0
- package/template/.agents/skills/supabase/SKILL.md +5 -1
- package/template/.agents/skills/writing/humanizer/LICENSE +21 -0
- package/template/.agents/skills/writing/humanizer/README.md +209 -0
- package/template/.agents/skills/writing/humanizer/SKILL.md +173 -338
- package/template/.agents/upstreams.json +21 -22
- package/template/.agents/skills/design/impeccable/scripts/live/ui-core.mjs +0 -180
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical inventory of the Live overlay's UI surfaces: one entry per piece of
|
|
3
|
+
* chrome Live mounts on the user's page, with the element ids that make it up.
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth, consumed by:
|
|
6
|
+
* - skill/scripts/live/browser-script-parts.mjs — serializes this into
|
|
7
|
+
* window.__IMPECCABLE_LIVE_UI_SURFACES__ in the /live.js prelude.
|
|
8
|
+
* - skill/scripts/live-browser.js — publishes it on
|
|
9
|
+
* window.__IMPECCABLE_LIVE_CHROME_CORE__ for adapters and E2E probes. That
|
|
10
|
+
* file is served raw and injected as a classic <script>, so it cannot
|
|
11
|
+
* import this module at runtime; it reads the injected global instead, the
|
|
12
|
+
* same path live/vocabulary.mjs already takes for the command palette.
|
|
13
|
+
* - the private impeccable-site repo — site/components/LiveUiGallery.astro
|
|
14
|
+
* and tests/live-ui-lab.test.mjs import LIVE_UI_SURFACES at build time and
|
|
15
|
+
* fail the site build when the Live UI lab has no snapshot for a surface
|
|
16
|
+
* defined here. That guard only guards if it reads this list rather than a
|
|
17
|
+
* copy the site keeps, so this module must stay importable from Node.
|
|
18
|
+
* Renaming a key or the module is a breaking change for that build; the
|
|
19
|
+
* list was briefly inlined into live-browser.js and the site had to parse
|
|
20
|
+
* it back out with a regex.
|
|
21
|
+
*
|
|
22
|
+
* Add a surface here and both the browser bundle and the site lab follow.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Id prefix every Live chrome element carries. Mirrored by PREFIX in live-browser.js. */
|
|
26
|
+
export const LIVE_UI_PREFIX = 'impeccable-live';
|
|
27
|
+
|
|
28
|
+
const id = (suffix) => `${LIVE_UI_PREFIX}-${suffix}`;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The mount contract every Live chrome adapter (DOM, Svelte, ...) satisfies.
|
|
32
|
+
* Published alongside the surfaces on __IMPECCABLE_LIVE_CHROME_CORE__.
|
|
33
|
+
*/
|
|
34
|
+
export const LIVE_CHROME_MOUNT_CONTRACT = Object.freeze(['root', 'transport', 'state', 'actions']);
|
|
35
|
+
|
|
36
|
+
export const LIVE_UI_SURFACES = Object.freeze([
|
|
37
|
+
{
|
|
38
|
+
key: 'global-bottom-bar',
|
|
39
|
+
ids: [
|
|
40
|
+
id('global-bar'), id('global-bar-brand'), id('pick-toggle'), id('insert-toggle'),
|
|
41
|
+
id('detect-toggle'), id('detect-badge'), id('design-toggle'), id('page-chat'),
|
|
42
|
+
id('page-chat-input'), id('page-chat-voice'), id('page-chat-send'),
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
{ key: 'pending-copy-edit-dock', ids: [id('pending-dock')] },
|
|
46
|
+
{
|
|
47
|
+
key: 'element-selection-chrome',
|
|
48
|
+
ids: [
|
|
49
|
+
id('highlight'), id('tooltip'), id('bar'), id('selection-pill'), id('input'),
|
|
50
|
+
id('configure-voice'), id('configure-bar-tooltip'),
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{ key: 'action-picker', ids: [id('picker')] },
|
|
54
|
+
{ key: 'edit-chrome', ids: [id('edit-badge')] },
|
|
55
|
+
{ key: 'generating-row', ids: [id('bar'), id('shader')] },
|
|
56
|
+
{ key: 'variant-cycling-row', ids: [id('bar'), id('params-panel')] },
|
|
57
|
+
{ key: 'variant-params-panel', ids: [id('params-panel')] },
|
|
58
|
+
{ key: 'saving-confirmed-rows', ids: [id('bar')] },
|
|
59
|
+
{
|
|
60
|
+
key: 'insert-mode-chrome',
|
|
61
|
+
ids: [
|
|
62
|
+
id('insert-line'), id('insert-placeholder'), id('placeholder-resize'), id('insert-input'),
|
|
63
|
+
id('insert-voice'), id('insert-create'), id('insert-create-tooltip'),
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{ key: 'annotation-chrome', ids: [id('annot'), id('annot-svg'), id('annot-pins'), id('annot-clear')] },
|
|
67
|
+
{ key: 'design-system-panel', ids: [id('design-host')] },
|
|
68
|
+
{ key: 'toasts-and-errors', ids: [id('toast'), id('mount-error')] },
|
|
69
|
+
{ key: 'css-isolation-boundary', ids: [id('root')] },
|
|
70
|
+
].map((surface) => Object.freeze({ ...surface, ids: Object.freeze(surface.ids) })));
|
|
71
|
+
|
|
72
|
+
/** Every id any surface owns, de-duplicated, in surface order. */
|
|
73
|
+
export const LIVE_UI_COMPONENT_IDS = Object.freeze([
|
|
74
|
+
...new Set(LIVE_UI_SURFACES.flatMap((surface) => surface.ids)),
|
|
75
|
+
]);
|
|
@@ -170,51 +170,35 @@ Output (JSON):
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
if (svelteComponentManifest) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
{
|
|
173
|
+
const { sourceFile, componentDir } = svelteComponentManifest;
|
|
174
|
+
const resultContext = {
|
|
175
|
+
file: sourceFile,
|
|
176
|
+
...(isDiscard ? { carbonize: false } : { sourceFile }),
|
|
177
|
+
previewMode: 'svelte-component',
|
|
178
|
+
componentDir,
|
|
179
|
+
};
|
|
180
|
+
const runOperation = isDiscard
|
|
181
|
+
? () => {
|
|
182
|
+
removeSvelteComponentSession(id, process.cwd());
|
|
183
|
+
return { handled: true, ...resultContext };
|
|
184
|
+
}
|
|
185
|
+
: () => inlineSvelteComponentAccept(
|
|
186
|
+
svelteComponentManifest,
|
|
187
|
+
variantNum,
|
|
188
|
+
paramValues,
|
|
189
|
+
process.cwd(),
|
|
184
190
|
);
|
|
185
|
-
} catch (err) {
|
|
186
|
-
result = operationFailure(err);
|
|
187
|
-
}
|
|
188
|
-
emitResult({
|
|
189
|
-
...result,
|
|
190
|
-
file: svelteComponentManifest.sourceFile,
|
|
191
|
-
carbonize: false,
|
|
192
|
-
previewMode: 'svelte-component',
|
|
193
|
-
componentDir: svelteComponentManifest.componentDir,
|
|
194
|
-
});
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
191
|
|
|
198
192
|
let result;
|
|
199
193
|
try {
|
|
200
194
|
result = withSourceLockSync(
|
|
201
|
-
path.resolve(process.cwd(),
|
|
202
|
-
'
|
|
203
|
-
|
|
204
|
-
svelteComponentManifest,
|
|
205
|
-
variantNum,
|
|
206
|
-
paramValues,
|
|
207
|
-
process.cwd(),
|
|
208
|
-
),
|
|
195
|
+
path.resolve(process.cwd(), sourceFile),
|
|
196
|
+
requestedOperation + ':' + id,
|
|
197
|
+
runOperation,
|
|
209
198
|
{ waitMs: ACCEPT_LOCK_WAIT_MS },
|
|
210
199
|
);
|
|
211
200
|
} catch (err) {
|
|
212
|
-
result = operationFailure(err,
|
|
213
|
-
file: svelteComponentManifest.sourceFile,
|
|
214
|
-
sourceFile: svelteComponentManifest.sourceFile,
|
|
215
|
-
previewMode: 'svelte-component',
|
|
216
|
-
componentDir: svelteComponentManifest.componentDir,
|
|
217
|
-
});
|
|
201
|
+
result = operationFailure(err, resultContext);
|
|
218
202
|
}
|
|
219
203
|
if (result.carbonize) {
|
|
220
204
|
result.todo = 'REQUIRED before next poll: carbonize cleanup in ' + result.file + '. See reference/live.md "Required after accept".';
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side resolution of project detector waivers for Impeccable live mode.
|
|
3
|
+
*
|
|
4
|
+
* The live server serializes `.impeccable/config.json` + `config.local.json`
|
|
5
|
+
* detector ignores (plus the served-root prefixes from the inject config's
|
|
6
|
+
* `files` globs) into `window.__IMPECCABLE_PROJECT_IGNORES__`. This part
|
|
7
|
+
* resolves that config against the current page's URL path when a detect scan
|
|
8
|
+
* starts, so the overlay suppresses the same findings the CLI and the edit
|
|
9
|
+
* hook do (issue #639).
|
|
10
|
+
*
|
|
11
|
+
* Mirrors filterDetectionFindings in cli/lib/impeccable-config.mjs:
|
|
12
|
+
* 1. `ignoreRules` suppress a rule project-wide.
|
|
13
|
+
* 2. `ignoreValues` entries with `value: "*"` suppress their rule in the
|
|
14
|
+
* files their globs name. The CLI never applies an unscoped wildcard
|
|
15
|
+
* (isIgnoredFindingValue returns false for it), so neither does this.
|
|
16
|
+
* 3. Remaining `ignoreValues` entries match on the finding's own value;
|
|
17
|
+
* those are forwarded as `disabledValues` for the detector bundle to
|
|
18
|
+
* apply where the findings are assembled.
|
|
19
|
+
* 4. `ignoreFiles` globs that name the page waive it wholesale: the
|
|
20
|
+
* resolver reports `skipScan: true` and the detector answers the scan
|
|
21
|
+
* with zero findings, mirroring shouldIgnoreDetectionFile in the CLI
|
|
22
|
+
* and the edit hook's own ignoreFiles gate.
|
|
23
|
+
*
|
|
24
|
+
* `pageFiles`, when the server could resolve it, lists the real project
|
|
25
|
+
* files the inject config serves. A URL that suffix-matches exactly one of
|
|
26
|
+
* them takes that file as its only project identity; an ambiguous or absent
|
|
27
|
+
* match falls back to the served-root common ancestor below.
|
|
28
|
+
*
|
|
29
|
+
* Known gap, unchanged from PR #645: framework apps inject into source files
|
|
30
|
+
* (src/routes/about/+page.svelte) while scans see route URLs (/about), so
|
|
31
|
+
* entries scoped to source or asset paths never match a page candidate and
|
|
32
|
+
* are dropped. That shows the finding, which is the conservative direction.
|
|
33
|
+
*
|
|
34
|
+
* Kept separate from live-browser.js so the glob and page-scope logic can be
|
|
35
|
+
* unit tested in Node (tests/live-browser-ignores.test.mjs) without the full
|
|
36
|
+
* overlay UI bundle.
|
|
37
|
+
*/
|
|
38
|
+
(function (root) {
|
|
39
|
+
'use strict';
|
|
40
|
+
if (!root) return;
|
|
41
|
+
|
|
42
|
+
// Keep in step with normalizeIgnoreRule / normalizeIgnoreValue in
|
|
43
|
+
// cli/lib/impeccable-config.mjs.
|
|
44
|
+
function normalizeIgnoreRule(rule) {
|
|
45
|
+
return String(rule || '').trim().toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeIgnoreValue(value) {
|
|
49
|
+
return String(value || '')
|
|
50
|
+
.trim()
|
|
51
|
+
.replace(/^["']|["']$/g, '')
|
|
52
|
+
.replace(/\+/g, ' ')
|
|
53
|
+
.replace(/\s+/g, ' ')
|
|
54
|
+
.toLowerCase();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Glob -> RegExp. Supports `**`, `*`, `?`, and `{a,b}` alternation.
|
|
58
|
+
// Keep in step with globToRegex in cli/lib/impeccable-config.mjs.
|
|
59
|
+
function globToRegex(glob) {
|
|
60
|
+
let re = '^';
|
|
61
|
+
let i = 0;
|
|
62
|
+
while (i < glob.length) {
|
|
63
|
+
const c = glob[i];
|
|
64
|
+
if (c === '*') {
|
|
65
|
+
if (glob[i + 1] === '*') {
|
|
66
|
+
re += '.*';
|
|
67
|
+
i += 2;
|
|
68
|
+
if (glob[i] === '/') i += 1;
|
|
69
|
+
} else {
|
|
70
|
+
re += '[^/]*';
|
|
71
|
+
i += 1;
|
|
72
|
+
}
|
|
73
|
+
} else if (c === '?') {
|
|
74
|
+
re += '[^/]';
|
|
75
|
+
i += 1;
|
|
76
|
+
} else if (c === '{') {
|
|
77
|
+
const end = glob.indexOf('}', i);
|
|
78
|
+
if (end === -1) { re += '\\{'; i += 1; continue; }
|
|
79
|
+
const parts = glob.slice(i + 1, end).split(',').map((p) => p.replace(/[.+^$()|[\]\\]/g, '\\$&'));
|
|
80
|
+
re += `(?:${parts.join('|')})`;
|
|
81
|
+
i = end + 1;
|
|
82
|
+
} else if (/[.+^$()|[\]\\]/.test(c)) {
|
|
83
|
+
re += `\\${c}`;
|
|
84
|
+
i += 1;
|
|
85
|
+
} else {
|
|
86
|
+
re += c;
|
|
87
|
+
i += 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
re += '$';
|
|
91
|
+
return new RegExp(re);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The project-relative paths this page could be known as. Ignore globs are
|
|
95
|
+
// project-relative (prototype/foo.html) and the URL is site-relative
|
|
96
|
+
// (/foo.html), because a static server's root usually sits inside the
|
|
97
|
+
// project; `roots` carries that prefix. The server reads it from the inject
|
|
98
|
+
// config's own `files` globs, which already state where the served pages
|
|
99
|
+
// are. Do not derive it from the ignore globs: a single entry scoped to
|
|
100
|
+
// prototype/library/** would then lend prototype/library/ as a candidate
|
|
101
|
+
// prefix to every page, and that rule would suppress site-wide.
|
|
102
|
+
//
|
|
103
|
+
// Each prefixed path also contributes its slash suffixes, mirroring
|
|
104
|
+
// findingMatchesScopedIgnoreFile in cli/lib/impeccable-config.mjs (which
|
|
105
|
+
// matches globs against every path suffix of the finding's file).
|
|
106
|
+
//
|
|
107
|
+
// One live session is served by one server, so a single document root must
|
|
108
|
+
// sit at or above every configured page. The only prefix that can safely
|
|
109
|
+
// be asserted is therefore the deepest common ancestor of the glob roots.
|
|
110
|
+
// Treating each glob's own prefix as an identity goes wrong in both
|
|
111
|
+
// directions: disjoint roots (src/ and public/) invent simultaneous
|
|
112
|
+
// identities for one URL, so a waiver scoped to src/foo.html hides a
|
|
113
|
+
// finding on a page served from public/foo.html; nested roots (prototype/
|
|
114
|
+
// and prototype/library/, from globs at two depths in one tree) are not
|
|
115
|
+
// alternatives at all, and demanding a waiver match under both stops
|
|
116
|
+
// prototype/index.html from applying anywhere. When the globs share no
|
|
117
|
+
// common root, no prefix is asserted and only the URL path itself matches.
|
|
118
|
+
function pageCandidates(pathname, roots, pageFiles) {
|
|
119
|
+
let pagePath = String(pathname || '');
|
|
120
|
+
try {
|
|
121
|
+
pagePath = decodeURIComponent(pagePath);
|
|
122
|
+
} catch {
|
|
123
|
+
// Malformed percent-escape: match on the raw path rather than throwing.
|
|
124
|
+
}
|
|
125
|
+
pagePath = pagePath.replace(/^\/+/, '');
|
|
126
|
+
// A directory URL serves that directory's index, and the ignore globs
|
|
127
|
+
// name files. Without this, /news/ never matches prototype/news/index.html.
|
|
128
|
+
if (pagePath === '' || pagePath.endsWith('/')) pagePath += 'index.html';
|
|
129
|
+
|
|
130
|
+
const candidates = new Set();
|
|
131
|
+
const addSuffixes = (fullPath) => {
|
|
132
|
+
const parts = fullPath.split('/').filter(Boolean);
|
|
133
|
+
for (let i = 0; i < parts.length; i++) {
|
|
134
|
+
candidates.add(parts.slice(i).join('/'));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
addSuffixes(pagePath);
|
|
138
|
+
|
|
139
|
+
// The served page list names the real files the inject config serves.
|
|
140
|
+
// A URL that suffix-matches exactly one of them has an unambiguous
|
|
141
|
+
// project identity; assert that identity and stop guessing from roots
|
|
142
|
+
// (PR #645 review: with src/ and public/ both served, /foo.html must not
|
|
143
|
+
// borrow src/foo.html's waivers while actually serving public/foo.html).
|
|
144
|
+
// Zero matches or several fall through to the common-ancestor fallback:
|
|
145
|
+
// ambiguity resolves toward showing the finding.
|
|
146
|
+
const knownPages = [];
|
|
147
|
+
for (const entry of Array.isArray(pageFiles) ? pageFiles : []) {
|
|
148
|
+
if (typeof entry !== 'string' || !entry) continue;
|
|
149
|
+
if (entry === pagePath || entry.endsWith('/' + pagePath)) knownPages.push(entry);
|
|
150
|
+
}
|
|
151
|
+
if (knownPages.length === 1) {
|
|
152
|
+
addSuffixes(knownPages[0]);
|
|
153
|
+
return [...candidates];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const prefixes = [];
|
|
157
|
+
for (const entry of Array.isArray(roots) ? roots : []) {
|
|
158
|
+
if (typeof entry !== 'string') continue;
|
|
159
|
+
prefixes.push(entry.split('/').filter(Boolean));
|
|
160
|
+
}
|
|
161
|
+
let common = prefixes.length > 0 ? prefixes[0] : [];
|
|
162
|
+
for (const segments of prefixes.slice(1)) {
|
|
163
|
+
let i = 0;
|
|
164
|
+
while (i < common.length && i < segments.length && common[i] === segments[i]) i += 1;
|
|
165
|
+
common = common.slice(0, i);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (common.length > 0) addSuffixes(common.join('/') + '/' + pagePath);
|
|
169
|
+
return [...candidates];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function matchesScope(globs, candidates) {
|
|
173
|
+
return globs.some((glob) => {
|
|
174
|
+
let re;
|
|
175
|
+
try {
|
|
176
|
+
re = globToRegex(String(glob));
|
|
177
|
+
} catch {
|
|
178
|
+
// Malformed glob: skip it, as matchesAnyGlob does in the CLI.
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
return candidates.some((candidate) => re.test(candidate));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Resolve the serialized project ignores for one page.
|
|
187
|
+
*
|
|
188
|
+
* @param {object} options
|
|
189
|
+
* @param {object} options.ignores window.__IMPECCABLE_PROJECT_IGNORES__,
|
|
190
|
+
* in whatever state it arrived: absent, null, or hand-edited into the
|
|
191
|
+
* wrong shape. Every read tolerates that and degrades to no filtering.
|
|
192
|
+
* @param {string} options.pathname location.pathname of the scanned page.
|
|
193
|
+
* @returns {{ disabledRules: string[], disabledValues: Array<{rule: string, value: string}>, skipScan: boolean }}
|
|
194
|
+
*/
|
|
195
|
+
function resolveDetectIgnores({ ignores, pathname } = {}) {
|
|
196
|
+
const config = ignores && typeof ignores === 'object' ? ignores : {};
|
|
197
|
+
const asArray = (value) => (Array.isArray(value) ? value : []);
|
|
198
|
+
const candidates = pageCandidates(pathname, config.roots, config.pageFiles);
|
|
199
|
+
|
|
200
|
+
// detector.ignoreFiles waives whole files. When any glob names this
|
|
201
|
+
// page, the scan itself is skipped; rule and value lists are returned
|
|
202
|
+
// empty because nothing will run.
|
|
203
|
+
const ignoreFileGlobs = asArray(config.ignoreFiles)
|
|
204
|
+
.filter((glob) => typeof glob === 'string' && glob.trim());
|
|
205
|
+
if (ignoreFileGlobs.length > 0 && matchesScope(ignoreFileGlobs, candidates)) {
|
|
206
|
+
return { disabledRules: [], disabledValues: [], skipScan: true };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const disabledRules = new Set(
|
|
210
|
+
asArray(config.ignoreRules)
|
|
211
|
+
.filter((rule) => typeof rule === 'string')
|
|
212
|
+
.map(normalizeIgnoreRule)
|
|
213
|
+
.filter(Boolean),
|
|
214
|
+
);
|
|
215
|
+
const disabledValues = [];
|
|
216
|
+
|
|
217
|
+
for (const entry of asArray(config.ignoreValues)) {
|
|
218
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
219
|
+
const rule = normalizeIgnoreRule(entry.rule);
|
|
220
|
+
const value = normalizeIgnoreValue(entry.value);
|
|
221
|
+
if (!rule || !value) continue;
|
|
222
|
+
const files = [
|
|
223
|
+
...(typeof entry.file === 'string' && entry.file.trim() ? [entry.file.trim()] : []),
|
|
224
|
+
...asArray(entry.files).filter((glob) => typeof glob === 'string' && glob.trim()),
|
|
225
|
+
];
|
|
226
|
+
if (value === '*') {
|
|
227
|
+
// Wildcards suppress their rule only inside the files they name.
|
|
228
|
+
if (files.length > 0 && matchesScope(files, candidates)) disabledRules.add(rule);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (files.length > 0 && !matchesScope(files, candidates)) continue;
|
|
232
|
+
disabledValues.push({ rule, value });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { disabledRules: [...disabledRules], disabledValues, skipScan: false };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
root.__IMPECCABLE_LIVE_IGNORES__ = {
|
|
239
|
+
version: 1,
|
|
240
|
+
resolveDetectIgnores,
|
|
241
|
+
};
|
|
242
|
+
})(typeof window !== 'undefined' ? window : globalThis);
|
|
@@ -97,23 +97,20 @@
|
|
|
97
97
|
return { value: c.value, label: c.label };
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{ key: 'toasts-and-errors', ids: [PREFIX + '-toast', PREFIX + '-mount-error'] },
|
|
115
|
-
{ key: 'css-isolation-boundary', ids: [PREFIX + '-root'] },
|
|
116
|
-
];
|
|
100
|
+
// The Live chrome inventory (which surfaces exist, and the element ids each
|
|
101
|
+
// one owns) comes from the canonical source, skill/scripts/live/ui-surfaces.mjs,
|
|
102
|
+
// which the /live.js assembler serializes into these globals alongside the
|
|
103
|
+
// token/port/vocabulary. This file is served raw and injected as a classic
|
|
104
|
+
// script, so it cannot import that module; the private impeccable-site repo
|
|
105
|
+
// imports it directly to check its Live UI lab holds a snapshot for every
|
|
106
|
+
// surface, which only works while the list has exactly one definition.
|
|
107
|
+
// Add a surface in ui-surfaces.mjs, not here.
|
|
108
|
+
const LIVE_CHROME_MOUNT_CONTRACT = Array.isArray(window.__IMPECCABLE_LIVE_MOUNT_CONTRACT__)
|
|
109
|
+
? window.__IMPECCABLE_LIVE_MOUNT_CONTRACT__
|
|
110
|
+
: ['root', 'transport', 'state', 'actions'];
|
|
111
|
+
const LIVE_UI_SURFACES = Array.isArray(window.__IMPECCABLE_LIVE_UI_SURFACES__)
|
|
112
|
+
? window.__IMPECCABLE_LIVE_UI_SURFACES__
|
|
113
|
+
: [];
|
|
117
114
|
const LIVE_UI_COMPONENT_IDS = [...new Set(LIVE_UI_SURFACES.flatMap((surface) => surface.ids))];
|
|
118
115
|
|
|
119
116
|
//
|
|
@@ -3766,7 +3763,10 @@
|
|
|
3766
3763
|
const container = copyEditContainerContext(contextElement);
|
|
3767
3764
|
if (container) for (const op of ops) op.container = container;
|
|
3768
3765
|
try {
|
|
3769
|
-
|
|
3766
|
+
// Token in the query string as well as the body: the URL token is what
|
|
3767
|
+
// authorizes the CORS preflight when the page runs on a non-loopback
|
|
3768
|
+
// dev host (ddev, Valet), since the preflight carries no request body.
|
|
3769
|
+
const res = await fetch('http://localhost:' + PORT + '/manual-edit-stash?token=' + encodeURIComponent(TOKEN), {
|
|
3770
3770
|
method: 'POST',
|
|
3771
3771
|
headers: { 'Content-Type': 'application/json' },
|
|
3772
3772
|
body: JSON.stringify({
|
|
@@ -4902,6 +4902,13 @@
|
|
|
4902
4902
|
saveSession();
|
|
4903
4903
|
}
|
|
4904
4904
|
|
|
4905
|
+
function completeParameterGenerationIfReady() {
|
|
4906
|
+
if (expectedVariants <= 0 || arrivedVariants < expectedVariants) return;
|
|
4907
|
+
if (parameterGenerationState === 'pending' || parameterGenerationState === 'loading') {
|
|
4908
|
+
completeParameterPublication();
|
|
4909
|
+
}
|
|
4910
|
+
}
|
|
4911
|
+
|
|
4905
4912
|
function toggleTunePopover() {
|
|
4906
4913
|
if (pendingApplyInFlight) { showManualApplyBusyToast(); return; }
|
|
4907
4914
|
if (tuneOpen) { closeTunePopover(); return; }
|
|
@@ -5796,7 +5803,7 @@
|
|
|
5796
5803
|
setLiveState('CYCLING');
|
|
5797
5804
|
showOrUpdateCyclingBar();
|
|
5798
5805
|
saveSession();
|
|
5799
|
-
|
|
5806
|
+
completeParameterGenerationIfReady();
|
|
5800
5807
|
return;
|
|
5801
5808
|
}
|
|
5802
5809
|
|
|
@@ -5884,7 +5891,7 @@
|
|
|
5884
5891
|
refreshParamsPanel();
|
|
5885
5892
|
positionBar();
|
|
5886
5893
|
saveSession();
|
|
5887
|
-
|
|
5894
|
+
completeParameterGenerationIfReady();
|
|
5888
5895
|
console.log('[impeccable] Mounted ' + arrivedVariants + ' ' + manifest.framework + ' component variants.');
|
|
5889
5896
|
} catch (err) {
|
|
5890
5897
|
console.error('[impeccable] Failed to mount component-preview variants:', err);
|
|
@@ -6329,7 +6336,7 @@
|
|
|
6329
6336
|
refreshParamsPanel();
|
|
6330
6337
|
positionBar();
|
|
6331
6338
|
saveSession();
|
|
6332
|
-
|
|
6339
|
+
completeParameterGenerationIfReady();
|
|
6333
6340
|
console.log('[impeccable] Injected ' + arrivedVariants + ' variants from source file.');
|
|
6334
6341
|
})
|
|
6335
6342
|
.catch(err => {
|
|
@@ -6836,6 +6843,7 @@
|
|
|
6836
6843
|
|
|
6837
6844
|
const expected = parseInt(wrapper.dataset.impeccableVariantCount || '0');
|
|
6838
6845
|
if (expected > 0) expectedVariants = expected;
|
|
6846
|
+
completeParameterGenerationIfReady();
|
|
6839
6847
|
|
|
6840
6848
|
if (arrivedVariants > 0) {
|
|
6841
6849
|
setLiveState('CYCLING');
|
|
@@ -7150,7 +7158,10 @@
|
|
|
7150
7158
|
console.debug('[impeccable] Dropped optional live event:', err);
|
|
7151
7159
|
return null;
|
|
7152
7160
|
}
|
|
7153
|
-
|
|
7161
|
+
// Token in the query string as well as the body: the URL token is what
|
|
7162
|
+
// authorizes the CORS preflight when the page runs on a non-loopback
|
|
7163
|
+
// dev host (ddev, Valet), since the preflight carries no request body.
|
|
7164
|
+
const doSend = () => fetch('http://localhost:' + PORT + '/events?token=' + encodeURIComponent(TOKEN), {
|
|
7154
7165
|
method: 'POST',
|
|
7155
7166
|
headers: { 'Content-Type': 'application/json' },
|
|
7156
7167
|
body: JSON.stringify(msg),
|
|
@@ -11132,10 +11143,36 @@ void main() {
|
|
|
11132
11143
|
const scanId = String(++detectScanSeq);
|
|
11133
11144
|
activeDetectScanId = scanId;
|
|
11134
11145
|
pendingDetectScanId = scanId;
|
|
11146
|
+
// Send the project's detector waivers with the scan so the overlay
|
|
11147
|
+
// filters the same findings the CLI and the edit hook do (issue #639).
|
|
11148
|
+
// live-browser-ignores.js resolves .impeccable config for this page:
|
|
11149
|
+
// ignoreRules suppress outright, wildcard ignoreValues suppress their
|
|
11150
|
+
// rule in the files they name, ignoreFiles that name the page skip the
|
|
11151
|
+
// scan wholesale, and the rest match on the finding's own value inside
|
|
11152
|
+
// the detector. Guarded twice: a stale cached live.js without the
|
|
11153
|
+
// resolver part still scans, and a resolver that throws must not brick
|
|
11154
|
+
// the detect toggle; both degrade to an unfiltered scan.
|
|
11155
|
+
const ignoresApi = window.__IMPECCABLE_LIVE_IGNORES__;
|
|
11156
|
+
let ignores = { disabledRules: [], disabledValues: [], skipScan: false };
|
|
11157
|
+
if (typeof ignoresApi?.resolveDetectIgnores === 'function') {
|
|
11158
|
+
try {
|
|
11159
|
+
ignores = ignoresApi.resolveDetectIgnores({
|
|
11160
|
+
ignores: window.__IMPECCABLE_PROJECT_IGNORES__,
|
|
11161
|
+
pathname: location.pathname,
|
|
11162
|
+
}) || ignores;
|
|
11163
|
+
} catch (e) {
|
|
11164
|
+
ignores = { disabledRules: [], disabledValues: [], skipScan: false };
|
|
11165
|
+
}
|
|
11166
|
+
}
|
|
11135
11167
|
window.postMessage({
|
|
11136
11168
|
source: 'impeccable-command',
|
|
11137
11169
|
action: 'scan',
|
|
11138
|
-
config: {
|
|
11170
|
+
config: {
|
|
11171
|
+
scanId,
|
|
11172
|
+
disabledRules: ignores.disabledRules || [],
|
|
11173
|
+
disabledValues: ignores.disabledValues || [],
|
|
11174
|
+
skipScan: ignores.skipScan === true,
|
|
11175
|
+
},
|
|
11139
11176
|
}, '*');
|
|
11140
11177
|
}
|
|
11141
11178
|
|
|
@@ -11969,7 +12006,9 @@ void main() {
|
|
|
11969
12006
|
rules: [
|
|
11970
12007
|
...(md.colors?.rules || []).map((r) => ({ ...r, section: 'colors' })),
|
|
11971
12008
|
...(md.typography?.rules || []).map((r) => ({ ...r, section: 'typography' })),
|
|
12009
|
+
...(md.layout?.rules || []).map((r) => ({ ...r, section: 'layout' })),
|
|
11972
12010
|
...(md.elevation?.rules || []).map((r) => ({ ...r, section: 'elevation' })),
|
|
12011
|
+
...(md.shapes?.rules || []).map((r) => ({ ...r, section: 'shapes' })),
|
|
11973
12012
|
],
|
|
11974
12013
|
dos: md.dosDonts?.dos || [],
|
|
11975
12014
|
donts: md.dosDonts?.donts || [],
|