nudge-ui 0.2.0 → 0.2.2
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/bin/nudge-ui.mjs +3 -1
- package/dist/cli/agentCommand.d.ts +8 -0
- package/dist/cli/agentCommand.js +59 -0
- package/dist/client.mjs +878 -11
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
- package/dist/hosts/astro/clientTransport.js +63 -11
- package/dist/hosts/astro/integration.d.ts +2 -2
- package/dist/hosts/astro/integration.js +4 -3
- package/dist/hosts/environment.d.ts +15 -0
- package/dist/hosts/environment.js +16 -0
- package/dist/hosts/next/sidecar.d.ts +4 -0
- package/dist/hosts/next/sidecar.js +56 -7
- package/dist/hosts/next/wrapper.d.ts +2 -0
- package/dist/hosts/next/wrapper.js +32 -3
- package/dist/hosts/projectBridge.d.ts +21 -0
- package/dist/hosts/projectBridge.js +98 -0
- package/dist/hosts/static/cli.d.ts +1 -1
- package/dist/hosts/static/cli.js +6 -1
- package/dist/hosts/static/manifest.d.ts +1 -1
- package/dist/hosts/static/manifest.js +3 -1
- package/dist/hosts/static/server.js +45 -3
- package/dist/hosts/vite/vite.d.ts +6 -1
- package/dist/hosts/vite/vite.js +82 -15
- package/dist/inspector/agent/McpConnectionDialog.d.ts +5 -4
- package/dist/inspector/agent/McpConnectionDialog.js +20 -37
- package/dist/inspector/agent/client.d.ts +16 -3
- package/dist/inspector/agent/client.js +114 -11
- package/dist/inspector/agent/connectionStatus.d.ts +3 -3
- package/dist/inspector/agent/connectionStatus.js +11 -4
- package/dist/inspector/agent/httpTransport.d.ts +8 -0
- package/dist/inspector/agent/httpTransport.js +78 -18
- package/dist/inspector/agent/protocol.d.ts +11 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +1 -1
- package/dist/inspector/agent/verification.js +69 -12
- package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
- package/dist/inspector/canvas/CanvasCard.js +181 -91
- package/dist/inspector/canvas/CanvasElementOverlay.js +288 -52
- package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
- package/dist/inspector/canvas/CanvasToolbar.js +24 -0
- package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +410 -72
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
- package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
- package/dist/inspector/canvas/canvasResize.d.ts +30 -0
- package/dist/inspector/canvas/canvasResize.js +52 -0
- package/dist/inspector/canvas/canvasStore.d.ts +30 -6
- package/dist/inspector/canvas/canvasStore.js +128 -61
- package/dist/inspector/canvas/frameProtocol.d.ts +82 -5
- package/dist/inspector/canvas/frameProtocol.js +63 -2
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +4 -0
- package/dist/inspector/canvas/keyboardShortcuts.js +16 -0
- package/dist/inspector/canvas/linkEligibility.d.ts +0 -5
- package/dist/inspector/canvas/linkEligibility.js +0 -29
- package/dist/inspector/canvas/projection.d.ts +7 -0
- package/dist/inspector/canvas/projection.js +118 -25
- package/dist/inspector/canvas/rendererBootstrap.js +42 -29
- package/dist/inspector/canvas/rendererElementSelector.d.ts +1 -1
- package/dist/inspector/canvas/rendererElementSelector.js +385 -58
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
- package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
- package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
- package/dist/inspector/canvas/sessionStore.d.ts +1 -0
- package/dist/inspector/canvas/sessionStore.js +12 -4
- package/dist/inspector/canvas/staleChangeDetector.js +16 -14
- package/dist/inspector/changes/changesLog.js +31 -1
- package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
- package/dist/inspector/changes/managedStyleProjection.js +3 -3
- package/dist/inspector/changes/structuralTypes.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.d.ts +4 -0
- package/dist/inspector/changes/workspaceChanges.js +29 -3
- package/dist/inspector/clientManifest.d.ts +7 -0
- package/dist/inspector/clientManifest.js +12 -0
- package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
- package/dist/inspector/index.d.ts +2 -0
- package/dist/inspector/index.js +80 -32
- package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
- package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
- package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
- package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
- package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
- package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
- package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
- package/dist/inspector/inline-text/textEditChange.js +165 -0
- package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
- package/dist/inspector/inspection/selectionProperty.js +1 -0
- package/dist/inspector/overlay/DomNavigator.d.ts +10 -0
- package/dist/inspector/overlay/DomNavigator.js +40 -0
- package/dist/inspector/overlay/InspectorOverlay.js +6 -2
- package/dist/inspector/overlay/clickPolicy.d.ts +18 -2
- package/dist/inspector/overlay/clickPolicy.js +34 -3
- package/dist/inspector/overlay/elementSelector.d.ts +1 -1
- package/dist/inspector/overlay/elementSelector.js +18 -7
- package/dist/inspector/overlay/selectedGeometry.js +21 -2
- package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
- package/dist/inspector/overlay/spacingGestures.js +351 -0
- package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
- package/dist/inspector/overlay/structuralGestures.js +12 -10
- package/dist/inspector/projection/managedStylesheet.js +4 -1
- package/dist/inspector/projection/renderedInstance.d.ts +2 -0
- package/dist/inspector/projection/renderedInstance.js +5 -1
- package/dist/inspector/projection/structuralProjection.d.ts +2 -0
- package/dist/inspector/projection/structuralProjection.js +85 -1
- package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
- package/dist/inspector/projection/textProjection.d.ts +2 -0
- package/dist/inspector/projection/textProjection.js +10 -4
- package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
- package/dist/inspector/prompt/clipboardHandoff.js +3 -3
- package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
- package/dist/inspector/prompt/generatePrompt.js +19 -2
- package/dist/inspector/runtime/editorShell.d.ts +2 -0
- package/dist/inspector/runtime/editorShell.js +4 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +5 -1
- package/dist/inspector/runtime/runtimeConfig.js +4 -0
- package/dist/inspector/selection/hierarchy.d.ts +6 -0
- package/dist/inspector/selection/hierarchy.js +8 -0
- package/dist/inspector/selection/selectionStore.js +2 -0
- package/dist/inspector/settings/SettingsDialog.d.ts +1 -1
- package/dist/inspector/settings/SettingsDialog.js +12 -2
- package/dist/inspector/shell/AppShell.js +5 -1
- package/dist/inspector/shell/ChangesLog.js +26 -22
- package/dist/inspector/shell/CopyPromptButton.js +102 -19
- package/dist/inspector/shell/EmptyState.js +1 -1
- package/dist/inspector/shell/InspectorShell.d.ts +0 -1
- package/dist/inspector/shell/InspectorShell.js +38 -42
- package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalButton.js +57 -0
- package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
- package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
- package/dist/inspector/shell/originalPreview.d.ts +9 -0
- package/dist/inspector/shell/originalPreview.js +61 -0
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +15 -0
- package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
- package/dist/inspector/sketch/SketchChanges.js +72 -0
- package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
- package/dist/inspector/sketch/SketchEditor.js +402 -0
- package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
- package/dist/inspector/sketch/SketchEntryButton.js +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
- package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
- package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
- package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
- package/dist/inspector/sketch/SketchOverlay.js +420 -0
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
- package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
- package/dist/inspector/sketch/SketchWorkspace.js +152 -0
- package/dist/inspector/sketch/capture.d.ts +55 -0
- package/dist/inspector/sketch/capture.js +350 -0
- package/dist/inspector/sketch/freehand.d.ts +13 -0
- package/dist/inspector/sketch/freehand.js +64 -0
- package/dist/inspector/sketch/geometry.d.ts +17 -0
- package/dist/inspector/sketch/geometry.js +98 -0
- package/dist/inspector/sketch/handoff.d.ts +21 -0
- package/dist/inspector/sketch/handoff.js +72 -0
- package/dist/inspector/sketch/interaction.d.ts +21 -0
- package/dist/inspector/sketch/interaction.js +98 -0
- package/dist/inspector/sketch/model.d.ts +104 -0
- package/dist/inspector/sketch/model.js +282 -0
- package/dist/inspector/sketch/persistence.d.ts +21 -0
- package/dist/inspector/sketch/persistence.js +205 -0
- package/dist/inspector/sketch/raster.d.ts +22 -0
- package/dist/inspector/sketch/raster.js +203 -0
- package/dist/inspector/sketch/sketchNote.d.ts +10 -0
- package/dist/inspector/sketch/sketchNote.js +30 -0
- package/dist/inspector/sketch/store.d.ts +47 -0
- package/dist/inspector/sketch/store.js +434 -0
- package/dist/inspector/styleEditors/BorderEditor.js +15 -19
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
- package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
- package/dist/inspector/styleEditors/ColorPicker.js +11 -36
- package/dist/inspector/styleEditors/GapField.d.ts +3 -2
- package/dist/inspector/styleEditors/GapField.js +2 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +11 -8
- package/dist/inspector/styleEditors/SpacingBox.js +31 -33
- package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
- package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
- package/dist/inspector/styleEditors/layoutValue.js +3 -0
- package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
- package/dist/inspector/styleEditors/nudgeValue.js +93 -5
- package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
- package/dist/inspector/styleEditors/stylePresence.js +22 -0
- package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
- package/dist/inspector/styleEditors/textRelevance.js +35 -0
- package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
- package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
- package/dist/inspector/tokens/TokenField.js +130 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/dragPointerLock.d.ts +8 -0
- package/dist/inspector/tokens/dragPointerLock.js +70 -0
- package/dist/inspector/tokens/editActions.d.ts +2 -0
- package/dist/inspector/tokens/editActions.js +2 -2
- package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
- package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
- package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
- package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
- package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
- package/dist/inspector/tokens/resolution.d.ts +3 -26
- package/dist/inspector/tokens/resolution.js +44 -735
- package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
- package/dist/inspector/tokens/tokenAvailability.js +204 -0
- package/dist/inspector/ui/Button.d.ts +1 -1
- package/dist/inspector/ui/Disclosure.d.ts +22 -0
- package/dist/inspector/ui/Disclosure.js +13 -0
- package/dist/inspector/ui/SideValuesField.js +2 -2
- package/dist/inspector/ui/StatusCallout.d.ts +4 -1
- package/dist/inspector/ui/StatusCallout.js +2 -2
- package/dist/inspector/ui/dragCursor.d.ts +8 -0
- package/dist/inspector/ui/dragCursor.js +21 -0
- package/dist/inspector/ui/styles.js +21 -17
- package/dist/internal/agent-protocol.d.ts +65 -1
- package/dist/internal/agent-protocol.js +183 -3
- package/dist/nudge-ui.mjs +276 -58
- package/dist/transport/editor.d.ts +47 -0
- package/dist/transport/editor.js +163 -0
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/transport/routes.d.ts +10 -0
- package/dist/transport/routes.js +10 -0
- package/package.json +3 -1
- package/dist/inspector/shell/DomNavigation.d.ts +0 -8
- package/dist/inspector/shell/DomNavigation.js +0 -30
- package/dist/inspector/shell/panelLayout.d.ts +0 -2
- package/dist/inspector/shell/panelLayout.js +0 -29
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { readTailwindV4AlphaUtility, tailwindV4AlphaExpression } from "../../css/dialects/index.js";
|
|
2
|
-
import { INTERACTION_STATES } from "../shell/styleState.js";
|
|
3
2
|
import { getElementComputedStyle } from "../runtime/domRealm.js";
|
|
4
|
-
import { collectRules as collectCssomRules, documentRevisions as getDocumentRevisions,
|
|
5
|
-
import {
|
|
3
|
+
import { collectRules as collectCssomRules, documentRevisions as getDocumentRevisions, registerResolutionLineage, } from "./resolution/cssomCollector.js";
|
|
4
|
+
import { createRuleMatcher, matchRuleForElement } from "./resolution/ruleMatching.js";
|
|
6
5
|
import { compareAuthorCascade } from "./resolution/cascade.js";
|
|
7
|
-
export { invalidateStyleResolutionCache } from "./resolution/cssomCollector.js";
|
|
8
6
|
import { interpretValue, normalizeOpacityPercent, } from "../../css/value-semantics/index.js";
|
|
9
7
|
import { createInspectorValueContext, inspectorTokenOrigin } from "./valueSemanticsAdapter.js";
|
|
10
|
-
|
|
8
|
+
export { getAvailableInteractionStates, resetSourceSiteMatchCache, sourceSiteMatchCacheSize } from "./resolution/ruleMatching.js";
|
|
9
|
+
export { buildTokenTable, getTokenTable, getAvailableTokenEntriesForElement, getAvailableTokenCatalog } from "./tokenAvailability.js";
|
|
10
|
+
export { invalidateStyleResolutionCache } from "./resolution/cssomCollector.js";
|
|
11
11
|
/**
|
|
12
12
|
* Builds (or reuses) the document's CSSOM rule snapshot ahead of a resolution
|
|
13
13
|
* sweep. Idempotent: the walk runs once per stylesheet revision and is a cache
|
|
@@ -19,20 +19,6 @@ export function prewarmCssomRuleSnapshot(doc) {
|
|
|
19
19
|
}
|
|
20
20
|
const MAX_PROPERTIES = 100;
|
|
21
21
|
const EMPTY_LOCAL_ALIASES = new Map();
|
|
22
|
-
const tokenTableMemo = new WeakMap();
|
|
23
|
-
export function buildTokenTable(entries) {
|
|
24
|
-
const cached = tokenTableMemo.get(entries);
|
|
25
|
-
if (cached)
|
|
26
|
-
return cached;
|
|
27
|
-
const table = {};
|
|
28
|
-
for (const entry of entries) {
|
|
29
|
-
table[entry.name] = entry;
|
|
30
|
-
if (entry.cssName)
|
|
31
|
-
table[entry.cssName] = entry;
|
|
32
|
-
}
|
|
33
|
-
tokenTableMemo.set(entries, table);
|
|
34
|
-
return table;
|
|
35
|
-
}
|
|
36
22
|
/**
|
|
37
23
|
* Returns true when a `calc()` expression is safe to treat as a simple
|
|
38
24
|
* numeric value. We exclude percentages, viewport units, and font-relative
|
|
@@ -50,200 +36,6 @@ function canBecomeNumeric(value) {
|
|
|
50
36
|
const withoutVars = inner.replace(/var\([^)]+\)/g, "");
|
|
51
37
|
return !/\b\d+(?:\.\d+)?(?:%|vw|vh|vmin|vmax|dvw|dvh|sv[lw]h|lv[lw]h|[ce]m|ex|ch)\b/i.test(withoutVars);
|
|
52
38
|
}
|
|
53
|
-
let tableCache = null;
|
|
54
|
-
let tableSource = null;
|
|
55
|
-
export function getTokenTable() {
|
|
56
|
-
const { tokens } = getNudgeUiRuntimeConfig();
|
|
57
|
-
if (tableCache !== null && tableSource === tokens)
|
|
58
|
-
return tableCache;
|
|
59
|
-
tableCache = buildTokenTable(tokens);
|
|
60
|
-
tableSource = tokens;
|
|
61
|
-
return tableCache;
|
|
62
|
-
}
|
|
63
|
-
function isCustomPropertyToken(definition) {
|
|
64
|
-
return definition.cssName.startsWith("--");
|
|
65
|
-
}
|
|
66
|
-
function entryFromDefinition(definition, value) {
|
|
67
|
-
return {
|
|
68
|
-
name: definition.name,
|
|
69
|
-
cssName: definition.cssName,
|
|
70
|
-
value,
|
|
71
|
-
source: definition.declarations[0]?.source ?? "",
|
|
72
|
-
cssValue: definition.cssValue,
|
|
73
|
-
adapter: definition.adapter,
|
|
74
|
-
origin: definition.origin,
|
|
75
|
-
editable: definition.editable,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
const tokenEntriesCache = new WeakMap();
|
|
79
|
-
function registerWithAncestors(el) {
|
|
80
|
-
let current = el;
|
|
81
|
-
while (current) {
|
|
82
|
-
registerResolutionElement(current);
|
|
83
|
-
current = current.parentElement;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* The build-time catalog is intentionally an inventory of every project token.
|
|
88
|
-
* Element edits must instead use only custom properties resolved in that
|
|
89
|
-
* element's cascade; a token defined by a lazy stylesheet is not usable until
|
|
90
|
-
* that stylesheet is attached to the document. Entries are cached per element
|
|
91
|
-
* until the element or stylesheet revision changes; the derived table reuses
|
|
92
|
-
* `buildTokenTable`'s input-identity memo.
|
|
93
|
-
*/
|
|
94
|
-
export function getAvailableTokenEntriesForElement(el, definitions = getNudgeUiRuntimeConfig().tokenCatalog) {
|
|
95
|
-
const revisions = getDocumentRevisions(el.ownerDocument ?? document);
|
|
96
|
-
const cached = tokenEntriesCache.get(el);
|
|
97
|
-
if (cached && cached.elementRevision === revisions.element
|
|
98
|
-
&& cached.stylesheetRevision === revisions.stylesheet
|
|
99
|
-
&& cached.definitions === definitions) {
|
|
100
|
-
return cached.entries;
|
|
101
|
-
}
|
|
102
|
-
registerWithAncestors(el);
|
|
103
|
-
const computed = getElementComputedStyle(el);
|
|
104
|
-
const available = definitions.flatMap((definition) => {
|
|
105
|
-
if (!isCustomPropertyToken(definition)) {
|
|
106
|
-
// Literal adapters (for example Tailwind v3) do not have a browser
|
|
107
|
-
// custom property to probe. Their adapter owns their availability.
|
|
108
|
-
return [entryFromDefinition(definition, definition.declarations[0]?.value ?? "")];
|
|
109
|
-
}
|
|
110
|
-
const value = computed.getPropertyValue(definition.cssName).trim();
|
|
111
|
-
return value ? [entryFromDefinition(definition, value)] : [];
|
|
112
|
-
});
|
|
113
|
-
const intermediateTable = buildTokenTable(available);
|
|
114
|
-
const entries = available.map((entry) => ({
|
|
115
|
-
...entry,
|
|
116
|
-
value: resolveTokenValue(entry.value, intermediateTable).resolvedValue,
|
|
117
|
-
}));
|
|
118
|
-
tokenEntriesCache.set(el, {
|
|
119
|
-
elementRevision: revisions.element,
|
|
120
|
-
stylesheetRevision: revisions.stylesheet,
|
|
121
|
-
definitions,
|
|
122
|
-
entries,
|
|
123
|
-
});
|
|
124
|
-
return entries;
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Replaces build-time token declaration hints with declarations serialized by
|
|
128
|
-
* the browser from the stylesheets currently attached to this document.
|
|
129
|
-
*/
|
|
130
|
-
function hydrateTokenCatalogFromCssom(definitions, doc = document) {
|
|
131
|
-
const needsHydration = (definition) => definition.declarations.length === 0
|
|
132
|
-
|| (definition.adapter === "vanilla-extract" && definition.declarations.every((declaration) => declaration.value.trim() === `var(${definition.cssName})`
|
|
133
|
-
&& !declaration.context.selector
|
|
134
|
-
&& !declaration.context.wrappers?.length));
|
|
135
|
-
const known = new Set(definitions.filter(needsHydration).map((definition) => definition.cssName));
|
|
136
|
-
const declarations = new Map();
|
|
137
|
-
for (const rule of collectCssomRules(doc).rules) {
|
|
138
|
-
if (rule.source === "#nudge-ui-styles")
|
|
139
|
-
continue;
|
|
140
|
-
for (const declaration of rule.declarations) {
|
|
141
|
-
if (!known.has(declaration.property))
|
|
142
|
-
continue;
|
|
143
|
-
const wrappers = [
|
|
144
|
-
...(rule.layer ? [{ kind: "layer", params: rule.layer }] : []),
|
|
145
|
-
...(rule.atRules ?? []).flatMap((atRule) => atRule.kind === "media" || atRule.kind === "supports"
|
|
146
|
-
? [{ kind: atRule.kind, params: atRule.params }]
|
|
147
|
-
: []),
|
|
148
|
-
];
|
|
149
|
-
const list = declarations.get(declaration.property) ?? [];
|
|
150
|
-
list.push({
|
|
151
|
-
id: `${declaration.property}\u0000${rule.source ?? "cssom"}\u0000${rule.sourceOrder ?? list.length}`,
|
|
152
|
-
order: rule.sourceOrder,
|
|
153
|
-
value: declaration.value,
|
|
154
|
-
source: rule.source ?? "cssom",
|
|
155
|
-
important: Boolean(declaration.important),
|
|
156
|
-
context: {
|
|
157
|
-
selector: rule.selectorText,
|
|
158
|
-
...(wrappers.length > 0 ? { wrappers } : {}),
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
declarations.set(declaration.property, list);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return definitions.map((definition) => {
|
|
165
|
-
if (!needsHydration(definition))
|
|
166
|
-
return definition;
|
|
167
|
-
const accepted = declarations.get(definition.cssName);
|
|
168
|
-
return accepted?.length ? { ...definition, declarations: accepted } : definition;
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
function sourceFile(source) {
|
|
172
|
-
return source.replace(/:\d+$/, "").split(/[?#]/, 1)[0] ?? source;
|
|
173
|
-
}
|
|
174
|
-
function normalizedPath(value) {
|
|
175
|
-
return decodeURIComponent(value).replace(/\\/g, "/").replace(/^file:\/\//, "").replace(/\/+$/, "");
|
|
176
|
-
}
|
|
177
|
-
function loadedStylesheetSources(doc) {
|
|
178
|
-
const sourceIdentityByPath = new Map();
|
|
179
|
-
for (const node of doc.querySelectorAll('style[data-vite-dev-id], link[rel~="stylesheet"][href]')) {
|
|
180
|
-
const providesAuthoredIdentity = node.tagName === "STYLE";
|
|
181
|
-
const source = providesAuthoredIdentity
|
|
182
|
-
? node.dataset.viteDevId
|
|
183
|
-
: node.getAttribute("href");
|
|
184
|
-
if (!source)
|
|
185
|
-
continue;
|
|
186
|
-
const path = normalizedPath(source.split(/[?#]/, 1)[0] ?? source);
|
|
187
|
-
sourceIdentityByPath.set(path, providesAuthoredIdentity || sourceIdentityByPath.get(path) === true);
|
|
188
|
-
}
|
|
189
|
-
return Array.from(sourceIdentityByPath, ([path, providesAuthoredIdentity]) => ({
|
|
190
|
-
path,
|
|
191
|
-
providesAuthoredIdentity,
|
|
192
|
-
}));
|
|
193
|
-
}
|
|
194
|
-
function isLoadedCssSource(source, loadedSources) {
|
|
195
|
-
const file = sourceFile(source);
|
|
196
|
-
if (!/\.css$/i.test(file) || loadedSources.length === 0)
|
|
197
|
-
return true;
|
|
198
|
-
const normalizedFile = normalizedPath(file);
|
|
199
|
-
return loadedSources.some((loaded) => loaded === normalizedFile
|
|
200
|
-
|| loaded.endsWith(`/${normalizedFile}`)
|
|
201
|
-
|| normalizedFile.endsWith(`/${loaded}`));
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Returns stylesheet identities only when the browser gave us a complete,
|
|
205
|
-
* authored-source mapping. A compiled host can expose a mixture of source-like
|
|
206
|
-
* and opaque CSS URLs; treating the mapped subset as complete would make
|
|
207
|
-
* declarations behind the opaque URLs look lazy even while their variables are
|
|
208
|
-
* live in the document.
|
|
209
|
-
*/
|
|
210
|
-
function confidentlyLoadedStylesheetSources(definitions, loadedSources) {
|
|
211
|
-
const cssSources = definitions.flatMap((definition) => definition.declarations
|
|
212
|
-
.map((declaration) => declaration.source)
|
|
213
|
-
.filter((source) => /\.css$/i.test(sourceFile(source))));
|
|
214
|
-
const known = new Set();
|
|
215
|
-
for (const loaded of loadedSources) {
|
|
216
|
-
if (loaded.providesAuthoredIdentity
|
|
217
|
-
|| cssSources.some((source) => isLoadedCssSource(source, [loaded.path]))) {
|
|
218
|
-
known.add(loaded.path);
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
// One opaque compiled URL can contain any authored declaration, so the
|
|
222
|
-
// document does not provide enough evidence to remove unloaded sources.
|
|
223
|
-
return [];
|
|
224
|
-
}
|
|
225
|
-
return [...known];
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Produces the page catalog used by the global Tokens settings section. It retains the
|
|
229
|
-
* build-time inventory separately, while removing declarations from CSS files
|
|
230
|
-
* that have not loaded when the document exposes mappable source identities
|
|
231
|
-
* (such as Vite's lazy route stylesheets). Opaque compiled URLs do not narrow
|
|
232
|
-
* the adapter-supplied catalog.
|
|
233
|
-
*/
|
|
234
|
-
export function getAvailableTokenCatalog(root = document.documentElement, definitions = getNudgeUiRuntimeConfig().tokenCatalog) {
|
|
235
|
-
const computed = getElementComputedStyle(root);
|
|
236
|
-
const hydrated = hydrateTokenCatalogFromCssom(definitions, root.ownerDocument ?? document);
|
|
237
|
-
const loadedSources = confidentlyLoadedStylesheetSources(hydrated, loadedStylesheetSources(root.ownerDocument ?? document));
|
|
238
|
-
return hydrated.flatMap((definition) => {
|
|
239
|
-
if (!isCustomPropertyToken(definition))
|
|
240
|
-
return [definition];
|
|
241
|
-
if (!computed.getPropertyValue(definition.cssName).trim())
|
|
242
|
-
return [];
|
|
243
|
-
const declarations = definition.declarations.filter((declaration) => isLoadedCssSource(declaration.source, loadedSources));
|
|
244
|
-
return declarations.length > 0 ? [{ ...definition, declarations }] : [];
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
39
|
function normalizeInElementContext(el, property, value) {
|
|
248
40
|
const doc = el.ownerDocument;
|
|
249
41
|
const probe = doc.createElement(el.tagName.toLowerCase());
|
|
@@ -265,8 +57,8 @@ function candidateMatchesPainted(el, row, painted) {
|
|
|
265
57
|
}
|
|
266
58
|
/**
|
|
267
59
|
* Resolves one authored value through the value-semantics Module using the
|
|
268
|
-
* inspector's integration context.
|
|
269
|
-
*
|
|
60
|
+
* inspector's integration context. Value semantics remains the sole
|
|
61
|
+
* interpretation authority.
|
|
270
62
|
*/
|
|
271
63
|
function resolveTokenValue(value, tokenTable, localAliases = EMPTY_LOCAL_ALIASES) {
|
|
272
64
|
return interpretValue("--nudge-ui-token", value, createInspectorValueContext(tokenTable, localAliases))[0];
|
|
@@ -373,12 +165,6 @@ function atRulesApplyToElement(el, atRules) {
|
|
|
373
165
|
return true;
|
|
374
166
|
});
|
|
375
167
|
}
|
|
376
|
-
function specificityForBranch(rule, branch) {
|
|
377
|
-
// Single-branch selectors reuse the specificity collected from CSSOM; the
|
|
378
|
-
// matched branch is the whole selector. Multi-branch selectors need the
|
|
379
|
-
// matched branch's own weight, so recompute per branch.
|
|
380
|
-
return rule.selectorText.includes(",") ? computeSpecificity(branch) : rule.specificity;
|
|
381
|
-
}
|
|
382
168
|
function collectLocalAliases(el, rules, ruleApplies) {
|
|
383
169
|
// Custom properties inherit independently of the property being resolved.
|
|
384
170
|
// Resolve the winning declaration separately for each element in the
|
|
@@ -399,11 +185,11 @@ function collectLocalAliases(el, rules, ruleApplies) {
|
|
|
399
185
|
return;
|
|
400
186
|
if (!ruleApplies(rule))
|
|
401
187
|
return;
|
|
402
|
-
const
|
|
403
|
-
if (!
|
|
188
|
+
const match = matchRuleForElement(element, { rule, selectorText: rule.selectorText });
|
|
189
|
+
if (!match)
|
|
404
190
|
return;
|
|
405
191
|
const sourceOrder = rule.sourceOrder ?? index;
|
|
406
|
-
const specificity =
|
|
192
|
+
const { specificity } = match;
|
|
407
193
|
for (const declaration of rule.declarations) {
|
|
408
194
|
if (!declaration.property.startsWith("--"))
|
|
409
195
|
continue;
|
|
@@ -459,324 +245,13 @@ function makeRuleApplies(el) {
|
|
|
459
245
|
return result;
|
|
460
246
|
};
|
|
461
247
|
}
|
|
462
|
-
const TRANSIENT_SELECTOR = /:(?:hover|active|focus|focus-visible|focus-within|visited|target)(?:\b|\()/;
|
|
463
|
-
const transformedRulesMemo = new WeakMap();
|
|
464
|
-
function rulesForTransform(rules, transform) {
|
|
465
|
-
let byTransform = transformedRulesMemo.get(rules);
|
|
466
|
-
if (!byTransform) {
|
|
467
|
-
byTransform = new Map();
|
|
468
|
-
transformedRulesMemo.set(rules, byTransform);
|
|
469
|
-
}
|
|
470
|
-
const cached = byTransform.get(transform);
|
|
471
|
-
if (cached)
|
|
472
|
-
return cached;
|
|
473
|
-
let transformed;
|
|
474
|
-
if (transform === "live") {
|
|
475
|
-
transformed = rules.map((rule) => ({ rule, selectorText: rule.selectorText }));
|
|
476
|
-
}
|
|
477
|
-
else if (transform === "stable") {
|
|
478
|
-
transformed = rules
|
|
479
|
-
.filter((rule) => !TRANSIENT_SELECTOR.test(rule.selectorText))
|
|
480
|
-
.map((rule) => ({ rule, selectorText: rule.selectorText }));
|
|
481
|
-
}
|
|
482
|
-
else {
|
|
483
|
-
transformed = rules.flatMap((rule) => {
|
|
484
|
-
const selectorText = selectorForState(rule.selectorText, transform);
|
|
485
|
-
return selectorText ? [{ rule, selectorText }] : [];
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
byTransform.set(transform, transformed);
|
|
489
|
-
return transformed;
|
|
490
|
-
}
|
|
491
|
-
function isElementSensitiveSelector(selector) {
|
|
492
|
-
let attributeDepth = 0;
|
|
493
|
-
let quote = null;
|
|
494
|
-
let escaped = false;
|
|
495
|
-
for (let index = 0; index < selector.length; index++) {
|
|
496
|
-
const character = selector[index];
|
|
497
|
-
if (quote) {
|
|
498
|
-
if (escaped)
|
|
499
|
-
escaped = false;
|
|
500
|
-
else if (character === "\\")
|
|
501
|
-
escaped = true;
|
|
502
|
-
else if (character === quote)
|
|
503
|
-
quote = null;
|
|
504
|
-
continue;
|
|
505
|
-
}
|
|
506
|
-
if (attributeDepth > 0) {
|
|
507
|
-
if (character === '"' || character === "'")
|
|
508
|
-
quote = character;
|
|
509
|
-
else if (character === "[")
|
|
510
|
-
attributeDepth++;
|
|
511
|
-
else if (character === "]")
|
|
512
|
-
attributeDepth--;
|
|
513
|
-
continue;
|
|
514
|
-
}
|
|
515
|
-
if (character === "[") {
|
|
516
|
-
attributeDepth = 1;
|
|
517
|
-
continue;
|
|
518
|
-
}
|
|
519
|
-
if (character === "+" || character === "~")
|
|
520
|
-
return true;
|
|
521
|
-
if (character !== ":")
|
|
522
|
-
continue;
|
|
523
|
-
if (selector[index + 1] === ":")
|
|
524
|
-
return true;
|
|
525
|
-
const pseudo = /^[A-Za-z-]+/.exec(selector.slice(index + 1))?.[0];
|
|
526
|
-
// :root is represented by the complete ancestor lineage. Other
|
|
527
|
-
// pseudo-classes can vary per concrete element without an attribute or
|
|
528
|
-
// sibling mutation (for example :checked, :visited, and :target).
|
|
529
|
-
if (pseudo && pseudo.toLowerCase() !== "root")
|
|
530
|
-
return true;
|
|
531
|
-
}
|
|
532
|
-
return false;
|
|
533
|
-
}
|
|
534
|
-
const sourceSiteMatchBucketsMemo = new WeakMap();
|
|
535
|
-
function sourceSiteMatchBuckets(rules, transform) {
|
|
536
|
-
let byTransform = sourceSiteMatchBucketsMemo.get(rules);
|
|
537
|
-
if (!byTransform) {
|
|
538
|
-
byTransform = new Map();
|
|
539
|
-
sourceSiteMatchBucketsMemo.set(rules, byTransform);
|
|
540
|
-
}
|
|
541
|
-
const cached = byTransform.get(transform);
|
|
542
|
-
if (cached)
|
|
543
|
-
return cached;
|
|
544
|
-
const cacheable = [];
|
|
545
|
-
const elementSensitive = [];
|
|
546
|
-
for (const entry of rulesForTransform(rules, transform)) {
|
|
547
|
-
(isElementSensitiveSelector(entry.selectorText) ? elementSensitive : cacheable).push(entry);
|
|
548
|
-
}
|
|
549
|
-
const buckets = { cacheable, elementSensitive };
|
|
550
|
-
byTransform.set(transform, buckets);
|
|
551
|
-
return buckets;
|
|
552
|
-
}
|
|
553
|
-
const SOURCE_SITE_CACHE_MAX = 4096;
|
|
554
|
-
let sourceSiteMatchCaches = new WeakMap();
|
|
555
|
-
let sourceSiteMatchCacheEntries = 0;
|
|
556
|
-
const concreteElementMatchCaches = new WeakMap();
|
|
557
|
-
// The "all matched" set (inactive rules included) is matched fresh against
|
|
558
|
-
// every lineage element on every resolution otherwise, duplicating the
|
|
559
|
-
// cached sweep's cost. State-independent selectors use the same revision-keyed
|
|
560
|
-
// memo shape as the active sweep. Element-sensitive selectors stay fresh: a
|
|
561
|
-
// checked state, sibling relationship, or similar browser state can change
|
|
562
|
-
// without a DOM mutation that this registry observes.
|
|
563
|
-
const allMatchesMemo = new WeakMap();
|
|
564
|
-
/** Test hook: clears the per-source-site matched-rule cache. */
|
|
565
|
-
export function resetSourceSiteMatchCache() {
|
|
566
|
-
sourceSiteMatchCaches = new WeakMap();
|
|
567
|
-
sourceSiteMatchCacheEntries = 0;
|
|
568
|
-
}
|
|
569
|
-
/** Test hook: number of distinct cached source-site match sets. */
|
|
570
|
-
export function sourceSiteMatchCacheSize() {
|
|
571
|
-
return sourceSiteMatchCacheEntries;
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Captures selector-relevant state without serializing the whole document.
|
|
575
|
-
* Element and ancestor attributes cover the common source-site selectors while
|
|
576
|
-
* child/sibling-sensitive selectors are handled conservatively below.
|
|
577
|
-
*/
|
|
578
|
-
function sourceSiteContextKey(el) {
|
|
579
|
-
const lineage = [];
|
|
580
|
-
let current = el;
|
|
581
|
-
while (current) {
|
|
582
|
-
const attributes = Array.from(current.attributes)
|
|
583
|
-
.filter((attribute) => attribute.name !== "data-renderer-id")
|
|
584
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
585
|
-
.map((attribute) => `${attribute.name}=${attribute.value}`)
|
|
586
|
-
.join("\u0002");
|
|
587
|
-
lineage.push(`${current.tagName}\u0003${attributes}`);
|
|
588
|
-
current = current.parentElement;
|
|
589
|
-
}
|
|
590
|
-
return lineage.join("\u0001");
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Source-site identity key for the matched-rule cache. The element's class is
|
|
594
|
-
* part of the key because sibling instances of a source site can carry
|
|
595
|
-
* different classes (for example rows in a large list), which changes which
|
|
596
|
-
* rules match even though their `data-cid`/`data-src` are identical.
|
|
597
|
-
*/
|
|
598
|
-
function sourceSiteKey(el, transform) {
|
|
599
|
-
const cid = el.getAttribute("data-cid") ?? "";
|
|
600
|
-
const src = el.getAttribute("data-src") ?? "";
|
|
601
|
-
const instance = el.getAttribute("data-projection-instance") ?? "";
|
|
602
|
-
const className = typeof el.className === "string" ? el.className : "";
|
|
603
|
-
return `${cid}\u0000${src}\u0000${instance}\u0000${className}\u0000${transform}`;
|
|
604
|
-
}
|
|
605
|
-
function matchRuleForElement(el, entry) {
|
|
606
|
-
const branch = matchingSelectorBranch(el, entry.selectorText);
|
|
607
|
-
if (!branch)
|
|
608
|
-
return null;
|
|
609
|
-
return {
|
|
610
|
-
rule: entry.rule,
|
|
611
|
-
selectorText: entry.selectorText,
|
|
612
|
-
branch,
|
|
613
|
-
specificity: specificityForBranch({ ...entry.rule, selectorText: entry.selectorText }, branch),
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Captures the current match outcome for selectors whose truth can change
|
|
618
|
-
* without a document revision. This keeps the expensive resolved-row cache
|
|
619
|
-
* usable while invalidating it when states such as `checked` actually change.
|
|
620
|
-
*/
|
|
621
|
-
function elementSensitiveMatchKey(el, rules, transform) {
|
|
622
|
-
const entries = sourceSiteMatchBuckets(rules, transform).elementSensitive;
|
|
623
|
-
if (entries.length === 0)
|
|
624
|
-
return "";
|
|
625
|
-
const matches = [];
|
|
626
|
-
let current = el;
|
|
627
|
-
let depth = 0;
|
|
628
|
-
while (current) {
|
|
629
|
-
for (const entry of entries) {
|
|
630
|
-
const branch = matchingSelectorBranch(current, entry.selectorText);
|
|
631
|
-
if (branch)
|
|
632
|
-
matches.push(`${depth}:${entry.rule.sourceOrder ?? 0}:${branch}`);
|
|
633
|
-
}
|
|
634
|
-
current = current.parentElement;
|
|
635
|
-
depth++;
|
|
636
|
-
}
|
|
637
|
-
return matches.join("\u0001");
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* Matches every transformed rule against one element. For elements carrying a
|
|
641
|
-
* source-site identity (`data-cid`) the selector-match result is cached per
|
|
642
|
-
* `(document, data-cid, data-src, data-projection-instance, transform, element and
|
|
643
|
-
* stylesheet revision, context)` so repeated selections and equivalent sibling instances of the
|
|
644
|
-
* same source site reuse the matched rule set. Elements without a stable
|
|
645
|
-
* identity, or selectors that depend on unsupported relationships, are matched
|
|
646
|
-
* fresh on every call.
|
|
647
|
-
*/
|
|
648
|
-
function getCachedElementMatches(el, rules, transform) {
|
|
649
|
-
const { cacheable, elementSensitive } = sourceSiteMatchBuckets(rules, transform);
|
|
650
|
-
const collect = (entries) => {
|
|
651
|
-
const matched = [];
|
|
652
|
-
for (const entry of entries) {
|
|
653
|
-
if (entry.rule.active === false)
|
|
654
|
-
continue;
|
|
655
|
-
const match = matchRuleForElement(el, entry);
|
|
656
|
-
if (match)
|
|
657
|
-
matched.push(match);
|
|
658
|
-
}
|
|
659
|
-
return matched;
|
|
660
|
-
};
|
|
661
|
-
const cid = el.getAttribute("data-cid");
|
|
662
|
-
const doc = el.ownerDocument ?? document;
|
|
663
|
-
const revisions = getDocumentRevisions(doc);
|
|
664
|
-
if (!cid || cacheable.length === 0) {
|
|
665
|
-
let cachedMatches = [];
|
|
666
|
-
if (cacheable.length > 0) {
|
|
667
|
-
let cache = concreteElementMatchCaches.get(el);
|
|
668
|
-
if (!cache) {
|
|
669
|
-
cache = new Map();
|
|
670
|
-
concreteElementMatchCaches.set(el, cache);
|
|
671
|
-
}
|
|
672
|
-
const cached = cache.get(transform);
|
|
673
|
-
if (cached
|
|
674
|
-
&& cached.elementRevision === revisions.element
|
|
675
|
-
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
676
|
-
cachedMatches = cached.matched;
|
|
677
|
-
}
|
|
678
|
-
else {
|
|
679
|
-
cachedMatches = collect(cacheable);
|
|
680
|
-
cache.set(transform, {
|
|
681
|
-
elementRevision: revisions.element,
|
|
682
|
-
stylesheetRevision: revisions.stylesheet,
|
|
683
|
-
matched: cachedMatches,
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
const sensitiveMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
688
|
-
return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
|
|
689
|
-
}
|
|
690
|
-
const key = `${sourceSiteKey(el, transform)}\u0000${sourceSiteContextKey(el)}`;
|
|
691
|
-
let cache = sourceSiteMatchCaches.get(doc);
|
|
692
|
-
if (!cache) {
|
|
693
|
-
cache = new Map();
|
|
694
|
-
sourceSiteMatchCaches.set(doc, cache);
|
|
695
|
-
}
|
|
696
|
-
const cached = cache.get(key);
|
|
697
|
-
let cachedMatches;
|
|
698
|
-
if (cached
|
|
699
|
-
&& cached.elementRevision === revisions.element
|
|
700
|
-
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
701
|
-
cachedMatches = cached.matched;
|
|
702
|
-
}
|
|
703
|
-
else {
|
|
704
|
-
cachedMatches = collect(cacheable);
|
|
705
|
-
if (sourceSiteMatchCacheEntries >= SOURCE_SITE_CACHE_MAX) {
|
|
706
|
-
sourceSiteMatchCaches = new WeakMap();
|
|
707
|
-
sourceSiteMatchCacheEntries = 0;
|
|
708
|
-
cache = new Map();
|
|
709
|
-
sourceSiteMatchCaches.set(doc, cache);
|
|
710
|
-
}
|
|
711
|
-
if (!cache.has(key))
|
|
712
|
-
sourceSiteMatchCacheEntries++;
|
|
713
|
-
cache.set(key, {
|
|
714
|
-
elementRevision: revisions.element,
|
|
715
|
-
stylesheetRevision: revisions.stylesheet,
|
|
716
|
-
matched: cachedMatches,
|
|
717
|
-
});
|
|
718
|
-
}
|
|
719
|
-
// Match element-sensitive selectors on every resolution. Browser state such
|
|
720
|
-
// as `checked`, `target`, or a sibling relationship can change without a DOM
|
|
721
|
-
// or stylesheet revision, so a revision-keyed entry can return stale rows.
|
|
722
|
-
const elementMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
723
|
-
return elementMatches.length === 0 ? cachedMatches : [...cachedMatches, ...elementMatches];
|
|
724
|
-
}
|
|
725
|
-
/**
|
|
726
|
-
* Matches selectors without applying conditional wrappers. Resolution still
|
|
727
|
-
* uses the active-only set for cascade decisions; this set preserves inactive
|
|
728
|
-
* media-query alternatives for property attribution in the inspector.
|
|
729
|
-
* Memoized per (element, transform, revisions) like the cached sweep for
|
|
730
|
-
* state-independent selectors. Element-sensitive selectors stay fresh because
|
|
731
|
-
* their state can change without a revision bump (for example, `:checked`),
|
|
732
|
-
* and the "live" transform always keeps its raw selector set fresh.
|
|
733
|
-
*/
|
|
734
|
-
function getAllElementMatches(el, rules, transform) {
|
|
735
|
-
if (transform === "live") {
|
|
736
|
-
return rulesForTransform(rules, transform).flatMap((entry) => {
|
|
737
|
-
const match = matchRuleForElement(el, entry);
|
|
738
|
-
return match ? [match] : [];
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
const { cacheable, elementSensitive } = sourceSiteMatchBuckets(rules, transform);
|
|
742
|
-
const collect = (entries) => entries.flatMap((entry) => {
|
|
743
|
-
const match = matchRuleForElement(el, entry);
|
|
744
|
-
return match ? [match] : [];
|
|
745
|
-
});
|
|
746
|
-
const doc = el.ownerDocument ?? document;
|
|
747
|
-
const revisions = getDocumentRevisions(doc);
|
|
748
|
-
let cachedMatches = [];
|
|
749
|
-
if (cacheable.length > 0) {
|
|
750
|
-
let byTransform = allMatchesMemo.get(el);
|
|
751
|
-
if (!byTransform) {
|
|
752
|
-
byTransform = new Map();
|
|
753
|
-
allMatchesMemo.set(el, byTransform);
|
|
754
|
-
}
|
|
755
|
-
const cached = byTransform.get(transform);
|
|
756
|
-
if (cached
|
|
757
|
-
&& cached.elementRevision === revisions.element
|
|
758
|
-
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
759
|
-
cachedMatches = cached.matched;
|
|
760
|
-
}
|
|
761
|
-
else {
|
|
762
|
-
cachedMatches = collect(cacheable);
|
|
763
|
-
byTransform.set(transform, {
|
|
764
|
-
elementRevision: revisions.element,
|
|
765
|
-
stylesheetRevision: revisions.stylesheet,
|
|
766
|
-
matched: cachedMatches,
|
|
767
|
-
});
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
const sensitiveMatches = elementSensitive.length > 0 ? collect(elementSensitive) : [];
|
|
771
|
-
return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
|
|
772
|
-
}
|
|
773
248
|
/**
|
|
774
249
|
* One matching pass over the rules for the element and its ancestors, with the
|
|
775
250
|
* selector-match results shared between the element's own resolution and the
|
|
776
251
|
* inherited phase. Local aliases are collected per element so no ancestor
|
|
777
252
|
* re-walks the lineage × rules.
|
|
778
253
|
*/
|
|
779
|
-
function resolveLineage(el,
|
|
254
|
+
function resolveLineage(el, matcher) {
|
|
780
255
|
const lineage = [];
|
|
781
256
|
let current = el;
|
|
782
257
|
while (current) {
|
|
@@ -787,8 +262,9 @@ function resolveLineage(el, rules, transform) {
|
|
|
787
262
|
const selectorMatches = new Map();
|
|
788
263
|
const allSelectorMatches = new Map();
|
|
789
264
|
for (const element of lineage) {
|
|
790
|
-
|
|
791
|
-
|
|
265
|
+
const { matched, allMatched } = matcher.matchesForElement(element);
|
|
266
|
+
selectorMatches.set(element, matched);
|
|
267
|
+
allSelectorMatches.set(element, allMatched);
|
|
792
268
|
}
|
|
793
269
|
const byElement = new Map();
|
|
794
270
|
for (let index = 0; index < lineage.length; index++) {
|
|
@@ -980,72 +456,6 @@ export function resolveRuleFixture(el, rules, tokenTable) {
|
|
|
980
456
|
const localAliases = collectLocalAliases(el, rules, ruleApplies);
|
|
981
457
|
return rowsFromMatches(el, matched, localAliases, tokenTable, allMatched);
|
|
982
458
|
}
|
|
983
|
-
// Selector branch splits are pure functions of the selector text and repeat
|
|
984
|
-
// for every element and resolution sweep. Memoize them so repeated matching
|
|
985
|
-
// passes skip re-scanning the same selector strings. Bounded like the other
|
|
986
|
-
// match caches; the snapshot's selector set is finite per stylesheet revision.
|
|
987
|
-
const SELECTOR_SPLIT_CACHE_MAX = 8192;
|
|
988
|
-
const selectorSplitMemo = new Map();
|
|
989
|
-
// The matched branch for one (element, selector) pair is a pure function of
|
|
990
|
-
// the element's selector-relevant state and the CSSOM snapshot — except for
|
|
991
|
-
// selectors whose match depends on transient state the revisions cannot see
|
|
992
|
-
// (`:hover`, `:checked`, sibling combinators, …). Those keep the freshness
|
|
993
|
-
// contract of the element-sensitive bucket and are matched uncached.
|
|
994
|
-
// Resolution sweeps re-match the same state-independent selector strings
|
|
995
|
-
// against the same lineage elements across cascades (authored base, stable,
|
|
996
|
-
// interaction states) and across the active/all rule sets, so memoizing the
|
|
997
|
-
// branch turns repeat sweeps into cache hits. Invalidation follows the
|
|
998
|
-
// document revisions, the same strategy as the per-element match caches.
|
|
999
|
-
const ELEMENT_BRANCH_CACHE_MAX = 16384;
|
|
1000
|
-
const elementBranchMemo = new WeakMap();
|
|
1001
|
-
function isBranchMemoizable(selectorText) {
|
|
1002
|
-
return !TRANSIENT_SELECTOR.test(selectorText) && !isElementSensitiveSelector(selectorText);
|
|
1003
|
-
}
|
|
1004
|
-
function matchingSelectorBranch(el, selectorText) {
|
|
1005
|
-
const doc = el.ownerDocument ?? document;
|
|
1006
|
-
const revisions = getDocumentRevisions(doc);
|
|
1007
|
-
let bySelector = elementBranchMemo.get(el);
|
|
1008
|
-
const memoizable = isBranchMemoizable(selectorText);
|
|
1009
|
-
if (memoizable) {
|
|
1010
|
-
if (!bySelector) {
|
|
1011
|
-
bySelector = new Map();
|
|
1012
|
-
elementBranchMemo.set(el, bySelector);
|
|
1013
|
-
}
|
|
1014
|
-
const cached = bySelector.get(selectorText);
|
|
1015
|
-
if (cached
|
|
1016
|
-
&& cached.elementRevision === revisions.element
|
|
1017
|
-
&& cached.stylesheetRevision === revisions.stylesheet) {
|
|
1018
|
-
return cached.branch;
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
let branches = selectorSplitMemo.get(selectorText);
|
|
1022
|
-
if (!branches) {
|
|
1023
|
-
branches = splitTopLevel(selectorText, ",");
|
|
1024
|
-
if (selectorSplitMemo.size >= SELECTOR_SPLIT_CACHE_MAX)
|
|
1025
|
-
selectorSplitMemo.clear();
|
|
1026
|
-
selectorSplitMemo.set(selectorText, branches);
|
|
1027
|
-
}
|
|
1028
|
-
let branch = null;
|
|
1029
|
-
for (const candidate of branches) {
|
|
1030
|
-
try {
|
|
1031
|
-
if (el.matches(candidate.trim())) {
|
|
1032
|
-
branch = candidate.trim();
|
|
1033
|
-
break;
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
catch { /* invalid/unsupported selector */ }
|
|
1037
|
-
}
|
|
1038
|
-
if (memoizable) {
|
|
1039
|
-
if (bySelector.size >= ELEMENT_BRANCH_CACHE_MAX)
|
|
1040
|
-
bySelector.clear();
|
|
1041
|
-
bySelector.set(selectorText, {
|
|
1042
|
-
elementRevision: revisions.element,
|
|
1043
|
-
stylesheetRevision: revisions.stylesheet,
|
|
1044
|
-
branch,
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
return branch;
|
|
1048
|
-
}
|
|
1049
459
|
function compareCandidate(a, b) {
|
|
1050
460
|
return compareAuthorCascade({
|
|
1051
461
|
important: a.evidence.important,
|
|
@@ -1091,34 +501,6 @@ function inferTailwindV4ColorOpacity(el, tokenTable, rows) {
|
|
|
1091
501
|
return;
|
|
1092
502
|
}
|
|
1093
503
|
}
|
|
1094
|
-
// Splits CSS values at top-level combinators or an optional delimiter.
|
|
1095
|
-
function splitTopLevel(s, sep) {
|
|
1096
|
-
const parts = [];
|
|
1097
|
-
let depth = 0;
|
|
1098
|
-
let start = 0;
|
|
1099
|
-
for (let i = 0; i < s.length; i++) {
|
|
1100
|
-
if (s[i] === "(")
|
|
1101
|
-
depth++;
|
|
1102
|
-
else if (s[i] === ")")
|
|
1103
|
-
depth--;
|
|
1104
|
-
else if (depth === 0 && sep && s[i] === sep) {
|
|
1105
|
-
parts.push(s.slice(start, i));
|
|
1106
|
-
start = i + 1;
|
|
1107
|
-
}
|
|
1108
|
-
else if (depth === 0 && !sep && (s[i] === " " || s[i] === ">" || s[i] === "+" || s[i] === "~")) {
|
|
1109
|
-
// Only split on combinators that have non-space around them
|
|
1110
|
-
// If previous char was also a combinator, skip
|
|
1111
|
-
if (i > start) {
|
|
1112
|
-
parts.push(s.slice(start, i).trim());
|
|
1113
|
-
}
|
|
1114
|
-
start = i + 1;
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
const last = s.slice(start).trim();
|
|
1118
|
-
if (last)
|
|
1119
|
-
parts.push(last);
|
|
1120
|
-
return parts;
|
|
1121
|
-
}
|
|
1122
504
|
const stateResolutionSnapshots = new WeakMap();
|
|
1123
505
|
const INHERITED_PROPERTIES = new Set([
|
|
1124
506
|
"color", "font", "font-family", "font-size", "font-style", "font-variant", "font-weight",
|
|
@@ -1156,19 +538,28 @@ function resolveInheritedProperties(el, tokenTable, lineage, result, inaccessibl
|
|
|
1156
538
|
}
|
|
1157
539
|
return result;
|
|
1158
540
|
}
|
|
1159
|
-
|
|
1160
|
-
const doc = el.ownerDocument ?? document;
|
|
1161
|
-
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1162
|
-
const lineage = resolveLineage(el, rules, "live");
|
|
541
|
+
function resolveMatchedProperties(el, tokenTable, lineage, inaccessible, mode) {
|
|
1163
542
|
const entry = lineage.byElement.get(el);
|
|
1164
543
|
const result = rowsFromMatches(el, entry.matched, entry.aliases, tokenTable, entry.allMatched);
|
|
1165
544
|
const computed = getElementComputedStyle(el);
|
|
545
|
+
if (mode !== "stable")
|
|
546
|
+
annotatePaintedValues(el, result, computed, inaccessible, mode);
|
|
547
|
+
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
548
|
+
hydratePropertyOpacityRows(result, computed);
|
|
549
|
+
promoteNumericCalcRows(result, tokenTable);
|
|
550
|
+
return resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
551
|
+
}
|
|
552
|
+
function annotatePaintedValues(el, result, computed, inaccessible, mode) {
|
|
1166
553
|
for (const prop of result) {
|
|
1167
554
|
const cv = computed.getPropertyValue(prop.property);
|
|
1168
555
|
if (cv && !/\b(?:var|calc)\s*\(/.test(cv)) {
|
|
1169
|
-
|
|
556
|
+
// A hypothetical state retains its authored value while `computed`
|
|
557
|
+
// records what the browser is painting. Live and base use painted values.
|
|
558
|
+
if (mode === "live" || mode === "base" || !prop.resolvedValue)
|
|
559
|
+
prop.resolvedValue = cv;
|
|
1170
560
|
prop.computed = cv;
|
|
1171
|
-
|
|
561
|
+
if (mode === "live")
|
|
562
|
+
hydratePropertyOpacity(prop, cv);
|
|
1172
563
|
const validated = candidateMatchesPainted(el, prop, cv);
|
|
1173
564
|
prop.confidence = validated && !inaccessible && !prop.evidence.layer ? "exact" : prop.tokenName ? "probable" : "unknown";
|
|
1174
565
|
prop.evidence.inaccessibleStylesheet = inaccessible || undefined;
|
|
@@ -1177,10 +568,12 @@ export function getResolvedProperties(el, tokenTable) {
|
|
|
1177
568
|
: "painted value has no attributable catalog token";
|
|
1178
569
|
}
|
|
1179
570
|
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
571
|
+
}
|
|
572
|
+
export function getResolvedProperties(el, tokenTable) {
|
|
573
|
+
const doc = el.ownerDocument ?? document;
|
|
574
|
+
const { rules, inaccessible } = collectCssomRules(doc);
|
|
575
|
+
const lineage = resolveLineage(el, createRuleMatcher(rules, "live"));
|
|
576
|
+
return resolveMatchedProperties(el, tokenTable, lineage, inaccessible, "live");
|
|
1184
577
|
}
|
|
1185
578
|
function applyInlineDeclarations(el, result, tokenTable, computed, inaccessible) {
|
|
1186
579
|
// Inline declarations participate in the cascade and are explicit evidence,
|
|
@@ -1247,22 +640,6 @@ function promoteNumericCalcRows(result, tokenTable) {
|
|
|
1247
640
|
prop.capability = capabilityFor(prop.property, numeric, tokenTable);
|
|
1248
641
|
}
|
|
1249
642
|
}
|
|
1250
|
-
const INTERACTION_SELECTOR = /:(hover|active|focus-visible|focus|disabled)(?:\b|\()/g;
|
|
1251
|
-
function selectorForState(selector, state) {
|
|
1252
|
-
const states = new Set();
|
|
1253
|
-
INTERACTION_SELECTOR.lastIndex = 0;
|
|
1254
|
-
let match;
|
|
1255
|
-
while ((match = INTERACTION_SELECTOR.exec(selector)) !== null)
|
|
1256
|
-
states.add(match[1]);
|
|
1257
|
-
if (state === "base")
|
|
1258
|
-
return states.size === 0 ? selector : null;
|
|
1259
|
-
if (!states.has(state))
|
|
1260
|
-
return states.size === 0 ? selector : null;
|
|
1261
|
-
// CSSOM cannot ask the browser whether a hypothetical pseudo-class matches.
|
|
1262
|
-
// Removing interaction pseudo-classes gives the authored rule a stable
|
|
1263
|
-
// element match for inspection. The real rule remains untouched.
|
|
1264
|
-
return selector.replace(INTERACTION_SELECTOR, "");
|
|
1265
|
-
}
|
|
1266
643
|
/**
|
|
1267
644
|
* Resolve the authored cascade for a chosen interaction state without relying
|
|
1268
645
|
* on the pointer's current location. This is intentionally attribution-first:
|
|
@@ -1273,7 +650,8 @@ export function getResolvedPropertiesForState(el, tokenTable, state) {
|
|
|
1273
650
|
const doc = el.ownerDocument ?? document;
|
|
1274
651
|
const revisions = getDocumentRevisions(doc);
|
|
1275
652
|
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1276
|
-
const
|
|
653
|
+
const matcher = createRuleMatcher(rules, state);
|
|
654
|
+
const dynamicMatchKey = matcher.dynamicMatchKey(el);
|
|
1277
655
|
let snapshots = stateResolutionSnapshots.get(el);
|
|
1278
656
|
if (!snapshots) {
|
|
1279
657
|
snapshots = new Map();
|
|
@@ -1287,33 +665,9 @@ export function getResolvedPropertiesForState(el, tokenTable, state) {
|
|
|
1287
665
|
&& cached.dynamicMatchKey === dynamicMatchKey) {
|
|
1288
666
|
return cached.rows;
|
|
1289
667
|
}
|
|
1290
|
-
const lineage = resolveLineage(el,
|
|
1291
|
-
const
|
|
1292
|
-
|
|
1293
|
-
const computed = getElementComputedStyle(el);
|
|
1294
|
-
for (const prop of result) {
|
|
1295
|
-
const cv = computed.getPropertyValue(prop.property);
|
|
1296
|
-
if (cv && !/\b(?:var|calc)\s*\(/.test(cv)) {
|
|
1297
|
-
// A selected interaction state is hypothetical: the element may still
|
|
1298
|
-
// be painting its Base styles. `rowsFromMatches` has already resolved
|
|
1299
|
-
// the authored declaration for the requested state, so preserve that
|
|
1300
|
-
// value for editor controls and keep the live CSSOM value in `computed`.
|
|
1301
|
-
if (state === "base" || !prop.resolvedValue)
|
|
1302
|
-
prop.resolvedValue = cv;
|
|
1303
|
-
prop.computed = cv;
|
|
1304
|
-
const validated = candidateMatchesPainted(el, prop, cv);
|
|
1305
|
-
prop.confidence = validated && !inaccessible && !prop.evidence.layer ? "exact" : prop.tokenName ? "probable" : "unknown";
|
|
1306
|
-
prop.evidence.inaccessibleStylesheet = inaccessible || undefined;
|
|
1307
|
-
prop.evidence.reason = prop.tokenName
|
|
1308
|
-
? (validated ? "authored token declaration validated against computed style" : "authored token candidate could not be proven uniquely")
|
|
1309
|
-
: "painted value has no attributable catalog token";
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
1313
|
-
hydratePropertyOpacityRows(result, computed);
|
|
1314
|
-
promoteNumericCalcRows(result, tokenTable);
|
|
1315
|
-
const rows = resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
1316
|
-
registerWithAncestors(el);
|
|
668
|
+
const lineage = resolveLineage(el, matcher);
|
|
669
|
+
const rows = resolveMatchedProperties(el, tokenTable, lineage, inaccessible, state);
|
|
670
|
+
registerResolutionLineage(el);
|
|
1317
671
|
snapshots.set(state, {
|
|
1318
672
|
elementRevision: revisions.element,
|
|
1319
673
|
stylesheetRevision: revisions.stylesheet,
|
|
@@ -1323,46 +677,6 @@ export function getResolvedPropertiesForState(el, tokenTable, state) {
|
|
|
1323
677
|
});
|
|
1324
678
|
return rows;
|
|
1325
679
|
}
|
|
1326
|
-
/**
|
|
1327
|
-
* Interaction states whose pseudo-class substring appears in at least one
|
|
1328
|
-
* snapshot selector, memoized per CSSOM snapshot. The availability check for a
|
|
1329
|
-
* state is `matched.some((m) => m.rule.selectorText.includes(":state"))`, so a
|
|
1330
|
-
* state whose substring appears nowhere can never become available for any
|
|
1331
|
-
* element; skipping its per-state match sweep is exactly equivalent and
|
|
1332
|
-
* removes the dominant cost of re-scanning interaction states after every
|
|
1333
|
-
* stylesheet revision.
|
|
1334
|
-
*/
|
|
1335
|
-
const statesWithPseudoInSnapshot = new WeakMap();
|
|
1336
|
-
function statesWithPseudoInSelectors(rules) {
|
|
1337
|
-
let present = statesWithPseudoInSnapshot.get(rules);
|
|
1338
|
-
if (!present) {
|
|
1339
|
-
present = new Set();
|
|
1340
|
-
for (const rule of rules) {
|
|
1341
|
-
for (const state of INTERACTION_STATES) {
|
|
1342
|
-
if (!present.has(state) && rule.selectorText.includes(`:${state}`)) {
|
|
1343
|
-
present.add(state);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
}
|
|
1347
|
-
statesWithPseudoInSnapshot.set(rules, present);
|
|
1348
|
-
}
|
|
1349
|
-
return present;
|
|
1350
|
-
}
|
|
1351
|
-
export function getAvailableInteractionStates(el) {
|
|
1352
|
-
const doc = el.ownerDocument ?? document;
|
|
1353
|
-
const { rules } = collectCssomRules(doc);
|
|
1354
|
-
const present = statesWithPseudoInSelectors(rules);
|
|
1355
|
-
const available = ["base"];
|
|
1356
|
-
for (const state of INTERACTION_STATES) {
|
|
1357
|
-
if (!present.has(state))
|
|
1358
|
-
continue;
|
|
1359
|
-
const matched = getCachedElementMatches(el, rules, state);
|
|
1360
|
-
const relevant = matched.some((m) => m.rule.selectorText.includes(`:${state}`));
|
|
1361
|
-
if (relevant)
|
|
1362
|
-
available.push(state);
|
|
1363
|
-
}
|
|
1364
|
-
return available;
|
|
1365
|
-
}
|
|
1366
680
|
const stableTokenCache = new WeakMap();
|
|
1367
681
|
/**
|
|
1368
682
|
* Resolves the cascade with transient interaction selectors removed. Used when
|
|
@@ -1373,7 +687,8 @@ export function getResolvedPropertiesStable(el, tokenTable) {
|
|
|
1373
687
|
const doc = el.ownerDocument ?? document;
|
|
1374
688
|
const revisions = getDocumentRevisions(doc);
|
|
1375
689
|
const { rules, inaccessible } = collectCssomRules(doc);
|
|
1376
|
-
const
|
|
690
|
+
const matcher = createRuleMatcher(rules, "stable");
|
|
691
|
+
const dynamicMatchKey = matcher.dynamicMatchKey(el);
|
|
1377
692
|
const cached = stableTokenCache.get(el);
|
|
1378
693
|
if (cached
|
|
1379
694
|
&& cached.elementRevision === revisions.element
|
|
@@ -1382,14 +697,8 @@ export function getResolvedPropertiesStable(el, tokenTable) {
|
|
|
1382
697
|
&& cached.dynamicMatchKey === dynamicMatchKey) {
|
|
1383
698
|
return cached.rows;
|
|
1384
699
|
}
|
|
1385
|
-
const lineage = resolveLineage(el,
|
|
1386
|
-
const
|
|
1387
|
-
const result = rowsFromMatches(el, entry.matched, entry.aliases, tokenTable, entry.allMatched);
|
|
1388
|
-
const computed = getElementComputedStyle(el);
|
|
1389
|
-
applyInlineDeclarations(el, result, tokenTable, computed, inaccessible);
|
|
1390
|
-
hydratePropertyOpacityRows(result, computed);
|
|
1391
|
-
promoteNumericCalcRows(result, tokenTable);
|
|
1392
|
-
const rows = resolveInheritedProperties(el, tokenTable, lineage, result, inaccessible);
|
|
700
|
+
const lineage = resolveLineage(el, matcher);
|
|
701
|
+
const rows = resolveMatchedProperties(el, tokenTable, lineage, inaccessible, "stable");
|
|
1393
702
|
stableTokenCache.set(el, {
|
|
1394
703
|
elementRevision: revisions.element,
|
|
1395
704
|
stylesheetRevision: revisions.stylesheet,
|