nudge-ui 0.2.0 → 0.2.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.
Files changed (201) hide show
  1. package/bin/nudge-ui.mjs +3 -1
  2. package/dist/cli/agentCommand.d.ts +8 -0
  3. package/dist/cli/agentCommand.js +59 -0
  4. package/dist/client.mjs +872 -12
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -1
  6. package/dist/hosts/astro/clientTransport.js +60 -11
  7. package/dist/hosts/next/sidecar.d.ts +4 -0
  8. package/dist/hosts/next/sidecar.js +49 -6
  9. package/dist/hosts/next/wrapper.js +30 -2
  10. package/dist/hosts/projectBridge.d.ts +20 -0
  11. package/dist/hosts/projectBridge.js +97 -0
  12. package/dist/hosts/static/cli.d.ts +1 -1
  13. package/dist/hosts/static/cli.js +6 -1
  14. package/dist/hosts/static/manifest.d.ts +1 -1
  15. package/dist/hosts/static/manifest.js +1 -1
  16. package/dist/hosts/static/server.js +44 -3
  17. package/dist/hosts/vite/vite.d.ts +5 -1
  18. package/dist/hosts/vite/vite.js +78 -14
  19. package/dist/inspector/agent/McpConnectionDialog.d.ts +2 -2
  20. package/dist/inspector/agent/McpConnectionDialog.js +11 -35
  21. package/dist/inspector/agent/client.d.ts +10 -3
  22. package/dist/inspector/agent/client.js +80 -9
  23. package/dist/inspector/agent/connectionStatus.js +3 -3
  24. package/dist/inspector/agent/httpTransport.d.ts +2 -0
  25. package/dist/inspector/agent/httpTransport.js +37 -17
  26. package/dist/inspector/agent/protocol.d.ts +8 -3
  27. package/dist/inspector/agent/protocol.js +1 -1
  28. package/dist/inspector/agent/verification.d.ts +1 -1
  29. package/dist/inspector/agent/verification.js +69 -12
  30. package/dist/inspector/canvas/CanvasCard.d.ts +5 -3
  31. package/dist/inspector/canvas/CanvasCard.js +181 -91
  32. package/dist/inspector/canvas/CanvasElementOverlay.js +246 -52
  33. package/dist/inspector/canvas/CanvasToolbar.d.ts +12 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +24 -0
  35. package/dist/inspector/canvas/CanvasWorkspace.d.ts +4 -1
  36. package/dist/inspector/canvas/CanvasWorkspace.js +392 -73
  37. package/dist/inspector/canvas/activeCanvasDocument.d.ts +5 -0
  38. package/dist/inspector/canvas/activeCanvasDocument.js +23 -0
  39. package/dist/inspector/canvas/canvasResize.d.ts +30 -0
  40. package/dist/inspector/canvas/canvasResize.js +52 -0
  41. package/dist/inspector/canvas/canvasStore.d.ts +30 -6
  42. package/dist/inspector/canvas/canvasStore.js +128 -61
  43. package/dist/inspector/canvas/frameProtocol.d.ts +69 -5
  44. package/dist/inspector/canvas/frameProtocol.js +56 -2
  45. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -0
  46. package/dist/inspector/canvas/keyboardShortcuts.js +14 -0
  47. package/dist/inspector/canvas/projection.d.ts +7 -0
  48. package/dist/inspector/canvas/projection.js +118 -25
  49. package/dist/inspector/canvas/rendererBootstrap.js +29 -29
  50. package/dist/inspector/canvas/rendererElementSelector.js +341 -30
  51. package/dist/inspector/canvas/rendererMessageRouter.d.ts +11 -0
  52. package/dist/inspector/canvas/rendererMessageRouter.js +43 -0
  53. package/dist/inspector/canvas/rendererSelectionProxy.js +6 -2
  54. package/dist/inspector/canvas/sessionStore.d.ts +1 -0
  55. package/dist/inspector/canvas/sessionStore.js +12 -4
  56. package/dist/inspector/canvas/staleChangeDetector.js +16 -14
  57. package/dist/inspector/changes/changesLog.js +31 -1
  58. package/dist/inspector/changes/managedStyleProjection.d.ts +1 -1
  59. package/dist/inspector/changes/managedStyleProjection.js +3 -3
  60. package/dist/inspector/changes/structuralTypes.d.ts +4 -0
  61. package/dist/inspector/clientManifest.d.ts +5 -0
  62. package/dist/inspector/clientManifest.js +11 -0
  63. package/dist/inspector/componentSemantics/adapterRegistry.js +4 -3
  64. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +2 -2
  65. package/dist/inspector/componentSemantics/runtimeBridge.js +16 -3
  66. package/dist/inspector/index.d.ts +2 -0
  67. package/dist/inspector/index.js +74 -24
  68. package/dist/inspector/inline-text/inlineTextDiagnostics.d.ts +10 -0
  69. package/dist/inspector/inline-text/inlineTextDiagnostics.js +65 -0
  70. package/dist/inspector/inline-text/inlineTextEditor.d.ts +3 -5
  71. package/dist/inspector/inline-text/inlineTextEditor.js +74 -206
  72. package/dist/inspector/inline-text/inlineTextFeedback.d.ts +5 -0
  73. package/dist/inspector/inline-text/inlineTextFeedback.js +27 -0
  74. package/dist/inspector/inline-text/inlineTextLifecycle.js +2 -0
  75. package/dist/inspector/inline-text/textEditChange.d.ts +14 -0
  76. package/dist/inspector/inline-text/textEditChange.js +165 -0
  77. package/dist/inspector/inspection/selectionProperty.d.ts +1 -0
  78. package/dist/inspector/inspection/selectionProperty.js +1 -0
  79. package/dist/inspector/overlay/selectedGeometry.js +21 -2
  80. package/dist/inspector/overlay/spacingGestures.d.ts +45 -0
  81. package/dist/inspector/overlay/spacingGestures.js +353 -0
  82. package/dist/inspector/overlay/structuralGestures.d.ts +2 -2
  83. package/dist/inspector/overlay/structuralGestures.js +12 -10
  84. package/dist/inspector/projection/managedStylesheet.js +4 -1
  85. package/dist/inspector/projection/renderedInstance.d.ts +2 -0
  86. package/dist/inspector/projection/renderedInstance.js +5 -1
  87. package/dist/inspector/projection/structuralProjection.d.ts +2 -0
  88. package/dist/inspector/projection/structuralProjection.js +85 -1
  89. package/dist/inspector/projection/structuralProjectionBoundary.js +17 -1
  90. package/dist/inspector/projection/textProjection.d.ts +2 -0
  91. package/dist/inspector/projection/textProjection.js +10 -4
  92. package/dist/inspector/prompt/clipboardHandoff.d.ts +1 -1
  93. package/dist/inspector/prompt/clipboardHandoff.js +3 -3
  94. package/dist/inspector/prompt/generatePrompt.d.ts +2 -1
  95. package/dist/inspector/prompt/generatePrompt.js +19 -2
  96. package/dist/inspector/runtime/editorShell.d.ts +2 -0
  97. package/dist/inspector/runtime/editorShell.js +4 -0
  98. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  99. package/dist/inspector/runtime/runtimeConfig.js +4 -0
  100. package/dist/inspector/selection/selectionStore.js +2 -0
  101. package/dist/inspector/settings/SettingsDialog.js +10 -0
  102. package/dist/inspector/shell/AppShell.js +5 -1
  103. package/dist/inspector/shell/ChangesLog.js +8 -4
  104. package/dist/inspector/shell/CopyPromptButton.js +95 -19
  105. package/dist/inspector/shell/EmptyState.js +1 -1
  106. package/dist/inspector/shell/InspectorShell.d.ts +0 -1
  107. package/dist/inspector/shell/InspectorShell.js +37 -38
  108. package/dist/inspector/shell/PeekOriginalButton.d.ts +7 -0
  109. package/dist/inspector/shell/PeekOriginalButton.js +57 -0
  110. package/dist/inspector/shell/PeekOriginalIcon.d.ts +7 -0
  111. package/dist/inspector/shell/PeekOriginalIcon.js +9 -0
  112. package/dist/inspector/shell/originalPreview.d.ts +9 -0
  113. package/dist/inspector/shell/originalPreview.js +61 -0
  114. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  115. package/dist/inspector/shell/shortcuts.js +15 -0
  116. package/dist/inspector/sketch/SketchChanges.d.ts +2 -0
  117. package/dist/inspector/sketch/SketchChanges.js +72 -0
  118. package/dist/inspector/sketch/SketchEditor.d.ts +21 -0
  119. package/dist/inspector/sketch/SketchEditor.js +402 -0
  120. package/dist/inspector/sketch/SketchEntryButton.d.ts +4 -0
  121. package/dist/inspector/sketch/SketchEntryButton.js +11 -0
  122. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +11 -0
  123. package/dist/inspector/sketch/SketchFrameOverlay.js +119 -0
  124. package/dist/inspector/sketch/SketchLayersPanel.d.ts +2 -0
  125. package/dist/inspector/sketch/SketchLayersPanel.js +63 -0
  126. package/dist/inspector/sketch/SketchOverlay.d.ts +19 -0
  127. package/dist/inspector/sketch/SketchOverlay.js +420 -0
  128. package/dist/inspector/sketch/SketchPromptPanel.d.ts +15 -0
  129. package/dist/inspector/sketch/SketchPromptPanel.js +18 -0
  130. package/dist/inspector/sketch/SketchWorkspace.d.ts +13 -0
  131. package/dist/inspector/sketch/SketchWorkspace.js +152 -0
  132. package/dist/inspector/sketch/capture.d.ts +55 -0
  133. package/dist/inspector/sketch/capture.js +350 -0
  134. package/dist/inspector/sketch/freehand.d.ts +13 -0
  135. package/dist/inspector/sketch/freehand.js +64 -0
  136. package/dist/inspector/sketch/geometry.d.ts +17 -0
  137. package/dist/inspector/sketch/geometry.js +98 -0
  138. package/dist/inspector/sketch/handoff.d.ts +21 -0
  139. package/dist/inspector/sketch/handoff.js +72 -0
  140. package/dist/inspector/sketch/interaction.d.ts +21 -0
  141. package/dist/inspector/sketch/interaction.js +98 -0
  142. package/dist/inspector/sketch/model.d.ts +104 -0
  143. package/dist/inspector/sketch/model.js +282 -0
  144. package/dist/inspector/sketch/persistence.d.ts +21 -0
  145. package/dist/inspector/sketch/persistence.js +205 -0
  146. package/dist/inspector/sketch/raster.d.ts +22 -0
  147. package/dist/inspector/sketch/raster.js +203 -0
  148. package/dist/inspector/sketch/sketchNote.d.ts +10 -0
  149. package/dist/inspector/sketch/sketchNote.js +30 -0
  150. package/dist/inspector/sketch/store.d.ts +47 -0
  151. package/dist/inspector/sketch/store.js +409 -0
  152. package/dist/inspector/styleEditors/BorderEditor.js +15 -19
  153. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -6
  154. package/dist/inspector/styleEditors/BoxShadowEditor.js +12 -1
  155. package/dist/inspector/styleEditors/ColorPicker.js +11 -36
  156. package/dist/inspector/styleEditors/GapField.d.ts +3 -2
  157. package/dist/inspector/styleEditors/GapField.js +2 -2
  158. package/dist/inspector/styleEditors/GridSection.js +1 -1
  159. package/dist/inspector/styleEditors/LayoutSection.js +11 -8
  160. package/dist/inspector/styleEditors/SpacingBox.js +23 -30
  161. package/dist/inspector/styleEditors/layoutPreviewState.d.ts +5 -0
  162. package/dist/inspector/styleEditors/layoutPreviewState.js +19 -0
  163. package/dist/inspector/styleEditors/layoutValue.js +3 -0
  164. package/dist/inspector/styleEditors/nudgeValue.d.ts +11 -0
  165. package/dist/inspector/styleEditors/nudgeValue.js +91 -5
  166. package/dist/inspector/styleEditors/stylePresence.d.ts +7 -0
  167. package/dist/inspector/styleEditors/stylePresence.js +22 -0
  168. package/dist/inspector/styleEditors/textRelevance.d.ts +3 -0
  169. package/dist/inspector/styleEditors/textRelevance.js +35 -0
  170. package/dist/inspector/styleEditors/useFieldVisibility.d.ts +6 -0
  171. package/dist/inspector/styleEditors/useFieldVisibility.js +26 -0
  172. package/dist/inspector/tokens/TokenField.js +111 -7
  173. package/dist/inspector/tokens/editActions.d.ts +2 -0
  174. package/dist/inspector/tokens/editActions.js +2 -2
  175. package/dist/inspector/tokens/resolution/cssomCollector.d.ts +1 -1
  176. package/dist/inspector/tokens/resolution/cssomCollector.js +9 -5
  177. package/dist/inspector/tokens/resolution/ruleMatching.d.ts +40 -0
  178. package/dist/inspector/tokens/resolution/ruleMatching.js +423 -0
  179. package/dist/inspector/tokens/resolution/selectorSemantics.d.ts +19 -3
  180. package/dist/inspector/tokens/resolution/selectorSemantics.js +378 -4
  181. package/dist/inspector/tokens/resolution.d.ts +3 -26
  182. package/dist/inspector/tokens/resolution.js +44 -735
  183. package/dist/inspector/tokens/tokenAvailability.d.ts +18 -0
  184. package/dist/inspector/tokens/tokenAvailability.js +204 -0
  185. package/dist/inspector/ui/Button.d.ts +1 -1
  186. package/dist/inspector/ui/SideValuesField.js +2 -2
  187. package/dist/inspector/ui/StatusCallout.d.ts +4 -1
  188. package/dist/inspector/ui/StatusCallout.js +2 -2
  189. package/dist/inspector/ui/styles.js +13 -11
  190. package/dist/internal/agent-protocol.d.ts +65 -1
  191. package/dist/internal/agent-protocol.js +183 -3
  192. package/dist/nudge-ui.mjs +261 -57
  193. package/dist/transport/editor.d.ts +28 -0
  194. package/dist/transport/editor.js +132 -0
  195. package/dist/transport/index.d.ts +1 -0
  196. package/dist/transport/index.js +1 -0
  197. package/dist/transport/routes.d.ts +7 -0
  198. package/dist/transport/routes.js +7 -0
  199. package/package.json +3 -1
  200. package/dist/inspector/shell/panelLayout.d.ts +0 -2
  201. package/dist/inspector/shell/panelLayout.js +0 -29
@@ -0,0 +1,423 @@
1
+ import { INTERACTION_STATES } from "../../shell/styleState.js";
2
+ import { collectRules as collectCssomRules, documentRevisions as getDocumentRevisions } from "./cssomCollector.js";
3
+ import { computeSpecificity, hasSelectorPseudoClass, isElementSensitiveSelector, canMatchSelectorSubject, selectorPseudoClasses, splitSelectorAtTopLevel, } from "./selectorSemantics.js";
4
+ const TRANSIENT_PSEUDO_CLASSES = new Set([
5
+ "hover",
6
+ "active",
7
+ "focus",
8
+ "focus-visible",
9
+ "focus-within",
10
+ "visited",
11
+ "target",
12
+ ]);
13
+ const INTERACTION_PSEUDO_CLASSES = new Set(INTERACTION_STATES);
14
+ function selectorForState(selector, state) {
15
+ const pseudoClasses = selectorPseudoClasses(selector)
16
+ .filter(({ name }) => INTERACTION_PSEUDO_CLASSES.has(name));
17
+ const states = new Set(pseudoClasses.map(({ name }) => name));
18
+ if (state === "base")
19
+ return states.size === 0 ? selector : null;
20
+ if (!states.has(state))
21
+ return states.size === 0 ? selector : null;
22
+ // CSSOM cannot ask the browser whether a hypothetical pseudo-class matches.
23
+ // Removing interaction pseudo-classes gives the authored rule a stable
24
+ // element match for inspection. The real rule remains untouched.
25
+ let transformed = selector;
26
+ for (let index = pseudoClasses.length - 1; index >= 0; index--) {
27
+ const pseudoClass = pseudoClasses[index];
28
+ transformed = transformed.slice(0, pseudoClass.start) + transformed.slice(pseudoClass.end);
29
+ }
30
+ return transformed;
31
+ }
32
+ function specificityForBranch(rule, branch) {
33
+ // Single-branch selectors reuse the specificity collected from CSSOM; the
34
+ // matched branch is the whole selector. Multi-branch selectors need the
35
+ // matched branch's own weight, so recompute per branch.
36
+ return rule.selectorText.includes(",") ? computeSpecificity(branch) : rule.specificity;
37
+ }
38
+ const transformedRulesMemo = new WeakMap();
39
+ function rulesForTransform(rules, transform) {
40
+ let byTransform = transformedRulesMemo.get(rules);
41
+ if (!byTransform) {
42
+ byTransform = new Map();
43
+ transformedRulesMemo.set(rules, byTransform);
44
+ }
45
+ const cached = byTransform.get(transform);
46
+ if (cached)
47
+ return cached;
48
+ let transformed;
49
+ if (transform === "live") {
50
+ transformed = rules.map((rule) => ({ rule, selectorText: rule.selectorText }));
51
+ }
52
+ else if (transform === "stable") {
53
+ transformed = rules
54
+ .filter((rule) => !hasSelectorPseudoClass(rule.selectorText, TRANSIENT_PSEUDO_CLASSES))
55
+ .map((rule) => ({ rule, selectorText: rule.selectorText }));
56
+ }
57
+ else {
58
+ transformed = rules.flatMap((rule) => {
59
+ const selectorText = selectorForState(rule.selectorText, transform);
60
+ return selectorText ? [{ rule, selectorText }] : [];
61
+ });
62
+ }
63
+ byTransform.set(transform, transformed);
64
+ return transformed;
65
+ }
66
+ const sourceSiteMatchBucketsMemo = new WeakMap();
67
+ function sourceSiteMatchBuckets(rules, transform) {
68
+ let byTransform = sourceSiteMatchBucketsMemo.get(rules);
69
+ if (!byTransform) {
70
+ byTransform = new Map();
71
+ sourceSiteMatchBucketsMemo.set(rules, byTransform);
72
+ }
73
+ const cached = byTransform.get(transform);
74
+ if (cached)
75
+ return cached;
76
+ const cacheable = [];
77
+ const elementSensitive = [];
78
+ for (const entry of rulesForTransform(rules, transform)) {
79
+ (isElementSensitiveSelector(entry.selectorText) ? elementSensitive : cacheable).push(entry);
80
+ }
81
+ const buckets = { cacheable, elementSensitive };
82
+ byTransform.set(transform, buckets);
83
+ return buckets;
84
+ }
85
+ const SOURCE_SITE_CACHE_MAX = 4096;
86
+ let sourceSiteMatchCaches = new WeakMap();
87
+ let sourceSiteMatchCacheEntries = 0;
88
+ const concreteElementMatchCaches = new WeakMap();
89
+ // The all-rules sweep shares the active sweep's revision cache for stable
90
+ // selectors. Element-sensitive selectors stay fresh because browser state can
91
+ // change without a mutation observed by the registry.
92
+ const allMatchesMemo = new WeakMap();
93
+ /** Test-only cache reset. */
94
+ export function resetSourceSiteMatchCache() {
95
+ sourceSiteMatchCaches = new WeakMap();
96
+ sourceSiteMatchCacheEntries = 0;
97
+ }
98
+ /** Test-only cache size. */
99
+ export function sourceSiteMatchCacheSize() {
100
+ return sourceSiteMatchCacheEntries;
101
+ }
102
+ /**
103
+ * Captures selector-relevant state without serializing the whole document.
104
+ * Element and ancestor attributes cover the common source-site selectors while
105
+ * child/sibling-sensitive selectors are handled conservatively below.
106
+ */
107
+ function sourceSiteContextKey(el) {
108
+ const lineage = [];
109
+ let current = el;
110
+ while (current) {
111
+ const attributes = Array.from(current.attributes)
112
+ .filter((attribute) => attribute.name !== "data-renderer-id")
113
+ .sort((a, b) => a.name.localeCompare(b.name))
114
+ .map((attribute) => `${attribute.name}=${attribute.value}`)
115
+ .join("\u0002");
116
+ lineage.push(`${current.tagName}\u0003${attributes}`);
117
+ current = current.parentElement;
118
+ }
119
+ return lineage.join("\u0001");
120
+ }
121
+ /**
122
+ * Source-site identity key for the matched-rule cache. The element's class is
123
+ * part of the key because sibling instances of a source site can carry
124
+ * different classes (for example rows in a large list), which changes which
125
+ * rules match even though their `data-cid`/`data-src` are identical.
126
+ */
127
+ function sourceSiteKey(el, transform) {
128
+ const cid = el.getAttribute("data-cid") ?? "";
129
+ const src = el.getAttribute("data-src") ?? "";
130
+ const instance = el.getAttribute("data-projection-instance") ?? "";
131
+ const className = typeof el.className === "string" ? el.className : "";
132
+ return `${cid}\u0000${src}\u0000${instance}\u0000${className}\u0000${transform}`;
133
+ }
134
+ export function matchRuleForElement(el, entry) {
135
+ const branch = matchingSelectorBranch(el, entry.selectorText);
136
+ if (!branch)
137
+ return null;
138
+ return {
139
+ rule: entry.rule,
140
+ selectorText: entry.selectorText,
141
+ branch,
142
+ specificity: specificityForBranch({ ...entry.rule, selectorText: entry.selectorText }, branch),
143
+ };
144
+ }
145
+ /**
146
+ * Captures the current match outcome for selectors whose truth can change
147
+ * without a document revision. This keeps the expensive resolved-row cache
148
+ * usable while invalidating it when states such as `checked` actually change.
149
+ */
150
+ function elementSensitiveMatchKey(el, rules, transform, sensitiveMatchesByElement) {
151
+ const entries = sourceSiteMatchBuckets(rules, transform).elementSensitive;
152
+ if (entries.length === 0)
153
+ return "";
154
+ const matches = [];
155
+ let current = el;
156
+ let depth = 0;
157
+ while (current) {
158
+ const elementMatches = [];
159
+ for (const entry of entries) {
160
+ const match = matchRuleForElement(current, entry);
161
+ if (!match)
162
+ continue;
163
+ elementMatches.push(match);
164
+ matches.push(`${depth}:${entry.rule.sourceOrder ?? 0}:${match.branch}`);
165
+ }
166
+ sensitiveMatchesByElement?.set(current, elementMatches);
167
+ current = current.parentElement;
168
+ depth++;
169
+ }
170
+ return matches.join("\u0001");
171
+ }
172
+ function sensitiveMatchesForElement(el, buckets, matchesByElement) {
173
+ const cached = matchesByElement.get(el);
174
+ if (cached)
175
+ return cached;
176
+ const matched = buckets.elementSensitive.flatMap((entry) => {
177
+ const match = matchRuleForElement(el, entry);
178
+ return match ? [match] : [];
179
+ });
180
+ matchesByElement.set(el, matched);
181
+ return matched;
182
+ }
183
+ /**
184
+ * Matches transformed rules against one element. Stable source-site identities
185
+ * reuse revision-keyed matches; unstable identities and relationship-sensitive
186
+ * selectors are matched on each call.
187
+ */
188
+ function getCachedElementMatches(el, rules, transform, sensitiveMatchesByElement = new Map()) {
189
+ const buckets = sourceSiteMatchBuckets(rules, transform);
190
+ const { cacheable, elementSensitive } = buckets;
191
+ const collect = (entries) => {
192
+ const matched = [];
193
+ for (const entry of entries) {
194
+ if (entry.rule.active === false)
195
+ continue;
196
+ const match = matchRuleForElement(el, entry);
197
+ if (match)
198
+ matched.push(match);
199
+ }
200
+ return matched;
201
+ };
202
+ const cid = el.getAttribute("data-cid");
203
+ const doc = el.ownerDocument ?? document;
204
+ const revisions = getDocumentRevisions(doc);
205
+ if (!cid || cacheable.length === 0) {
206
+ let cachedMatches = [];
207
+ if (cacheable.length > 0) {
208
+ let cache = concreteElementMatchCaches.get(el);
209
+ if (!cache) {
210
+ cache = new Map();
211
+ concreteElementMatchCaches.set(el, cache);
212
+ }
213
+ const cached = cache.get(transform);
214
+ if (cached
215
+ && cached.elementRevision === revisions.element
216
+ && cached.stylesheetRevision === revisions.stylesheet) {
217
+ cachedMatches = cached.matched;
218
+ }
219
+ else {
220
+ cachedMatches = collect(cacheable);
221
+ cache.set(transform, {
222
+ elementRevision: revisions.element,
223
+ stylesheetRevision: revisions.stylesheet,
224
+ matched: cachedMatches,
225
+ });
226
+ }
227
+ }
228
+ const sensitiveMatches = elementSensitive.length > 0
229
+ ? sensitiveMatchesForElement(el, buckets, sensitiveMatchesByElement).filter((match) => match.rule.active !== false)
230
+ : [];
231
+ return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
232
+ }
233
+ const key = `${sourceSiteKey(el, transform)}\u0000${sourceSiteContextKey(el)}`;
234
+ let cache = sourceSiteMatchCaches.get(doc);
235
+ if (!cache) {
236
+ cache = new Map();
237
+ sourceSiteMatchCaches.set(doc, cache);
238
+ }
239
+ const cached = cache.get(key);
240
+ let cachedMatches;
241
+ if (cached
242
+ && cached.elementRevision === revisions.element
243
+ && cached.stylesheetRevision === revisions.stylesheet) {
244
+ cachedMatches = cached.matched;
245
+ }
246
+ else {
247
+ cachedMatches = collect(cacheable);
248
+ if (sourceSiteMatchCacheEntries >= SOURCE_SITE_CACHE_MAX) {
249
+ sourceSiteMatchCaches = new WeakMap();
250
+ sourceSiteMatchCacheEntries = 0;
251
+ cache = new Map();
252
+ sourceSiteMatchCaches.set(doc, cache);
253
+ }
254
+ if (!cache.has(key))
255
+ sourceSiteMatchCacheEntries++;
256
+ cache.set(key, {
257
+ elementRevision: revisions.element,
258
+ stylesheetRevision: revisions.stylesheet,
259
+ matched: cachedMatches,
260
+ });
261
+ }
262
+ // Browser state such as `checked` or `target` can change without a DOM or
263
+ // stylesheet revision, so element-sensitive matches must stay fresh.
264
+ const elementMatches = elementSensitive.length > 0
265
+ ? sensitiveMatchesForElement(el, buckets, sensitiveMatchesByElement).filter((match) => match.rule.active !== false)
266
+ : [];
267
+ return elementMatches.length === 0 ? cachedMatches : [...cachedMatches, ...elementMatches];
268
+ }
269
+ /**
270
+ * Matches selectors without applying conditional wrappers. The result retains
271
+ * inactive media alternatives for attribution, while transient selectors and
272
+ * the `live` transform retain their freshness requirements.
273
+ */
274
+ function getAllElementMatches(el, rules, transform, sensitiveMatchesByElement = new Map()) {
275
+ const buckets = sourceSiteMatchBuckets(rules, transform);
276
+ if (transform === "live") {
277
+ const cacheableMatches = buckets.cacheable.flatMap((entry) => {
278
+ const match = matchRuleForElement(el, entry);
279
+ return match ? [match] : [];
280
+ });
281
+ return [...cacheableMatches, ...sensitiveMatchesForElement(el, buckets, sensitiveMatchesByElement)];
282
+ }
283
+ const { cacheable, elementSensitive } = buckets;
284
+ const collect = (entries) => entries.flatMap((entry) => {
285
+ const match = matchRuleForElement(el, entry);
286
+ return match ? [match] : [];
287
+ });
288
+ const doc = el.ownerDocument ?? document;
289
+ const revisions = getDocumentRevisions(doc);
290
+ let cachedMatches = [];
291
+ if (cacheable.length > 0) {
292
+ let byTransform = allMatchesMemo.get(el);
293
+ if (!byTransform) {
294
+ byTransform = new Map();
295
+ allMatchesMemo.set(el, byTransform);
296
+ }
297
+ const cached = byTransform.get(transform);
298
+ if (cached
299
+ && cached.elementRevision === revisions.element
300
+ && cached.stylesheetRevision === revisions.stylesheet) {
301
+ cachedMatches = cached.matched;
302
+ }
303
+ else {
304
+ cachedMatches = collect(cacheable);
305
+ byTransform.set(transform, {
306
+ elementRevision: revisions.element,
307
+ stylesheetRevision: revisions.stylesheet,
308
+ matched: cachedMatches,
309
+ });
310
+ }
311
+ }
312
+ const sensitiveMatches = elementSensitive.length > 0
313
+ ? sensitiveMatchesForElement(el, buckets, sensitiveMatchesByElement)
314
+ : [];
315
+ return sensitiveMatches.length === 0 ? cachedMatches : [...cachedMatches, ...sensitiveMatches];
316
+ }
317
+ // Branch splits repeat across sweeps, so memoize them for the finite selector
318
+ // set in a stylesheet snapshot.
319
+ const SELECTOR_SPLIT_CACHE_MAX = 8192;
320
+ const selectorSplitMemo = new Map();
321
+ // Branch matches are reusable for state-independent selectors. Transient
322
+ // selectors stay uncached because their state is not represented by document
323
+ // revisions.
324
+ const ELEMENT_BRANCH_CACHE_MAX = 16384;
325
+ const elementBranchMemo = new WeakMap();
326
+ function isBranchMemoizable(selectorText) {
327
+ return !hasSelectorPseudoClass(selectorText, TRANSIENT_PSEUDO_CLASSES)
328
+ && !isElementSensitiveSelector(selectorText);
329
+ }
330
+ function matchingSelectorBranch(el, selectorText) {
331
+ const doc = el.ownerDocument ?? document;
332
+ const revisions = getDocumentRevisions(doc);
333
+ let bySelector = elementBranchMemo.get(el);
334
+ const memoizable = isBranchMemoizable(selectorText);
335
+ if (memoizable) {
336
+ if (!bySelector) {
337
+ bySelector = new Map();
338
+ elementBranchMemo.set(el, bySelector);
339
+ }
340
+ const cached = bySelector.get(selectorText);
341
+ if (cached
342
+ && cached.elementRevision === revisions.element
343
+ && cached.stylesheetRevision === revisions.stylesheet) {
344
+ return cached.branch;
345
+ }
346
+ }
347
+ let branches = selectorSplitMemo.get(selectorText);
348
+ if (!branches) {
349
+ branches = splitSelectorAtTopLevel(selectorText, ",");
350
+ if (selectorSplitMemo.size >= SELECTOR_SPLIT_CACHE_MAX)
351
+ selectorSplitMemo.clear();
352
+ selectorSplitMemo.set(selectorText, branches);
353
+ }
354
+ let branch = null;
355
+ for (const candidate of branches) {
356
+ const trimmedCandidate = candidate.trim();
357
+ if (!canMatchSelectorSubject(el, trimmedCandidate))
358
+ continue;
359
+ try {
360
+ if (el.matches(trimmedCandidate)) {
361
+ branch = trimmedCandidate;
362
+ break;
363
+ }
364
+ }
365
+ catch { /* invalid/unsupported selector */ }
366
+ }
367
+ if (memoizable) {
368
+ if (bySelector.size >= ELEMENT_BRANCH_CACHE_MAX)
369
+ bySelector.clear();
370
+ bySelector.set(selectorText, {
371
+ elementRevision: revisions.element,
372
+ stylesheetRevision: revisions.stylesheet,
373
+ branch,
374
+ });
375
+ }
376
+ return branch;
377
+ }
378
+ /** Creates a matcher for one CSSOM snapshot. */
379
+ export function createRuleMatcher(rules, transform) {
380
+ const sensitiveMatches = new Map();
381
+ return {
382
+ dynamicMatchKey: (element) => elementSensitiveMatchKey(element, rules, transform, sensitiveMatches),
383
+ matchesForElement: (element) => ({
384
+ matched: getCachedElementMatches(element, rules, transform, sensitiveMatches),
385
+ allMatched: getAllElementMatches(element, rules, transform, sensitiveMatches),
386
+ }),
387
+ };
388
+ }
389
+ /**
390
+ * Interaction states whose pseudo-class appears in the snapshot. Skipping
391
+ * absent states avoids a full match sweep after each stylesheet revision.
392
+ */
393
+ const statesWithPseudoInSnapshot = new WeakMap();
394
+ function statesWithPseudoInSelectors(rules) {
395
+ let present = statesWithPseudoInSnapshot.get(rules);
396
+ if (!present) {
397
+ present = new Set();
398
+ for (const rule of rules) {
399
+ for (const state of INTERACTION_STATES) {
400
+ if (!present.has(state) && rule.selectorText.includes(`:${state}`)) {
401
+ present.add(state);
402
+ }
403
+ }
404
+ }
405
+ statesWithPseudoInSnapshot.set(rules, present);
406
+ }
407
+ return present;
408
+ }
409
+ export function getAvailableInteractionStates(el) {
410
+ const doc = el.ownerDocument ?? document;
411
+ const { rules } = collectCssomRules(doc);
412
+ const present = statesWithPseudoInSelectors(rules);
413
+ const available = ["base"];
414
+ for (const state of INTERACTION_STATES) {
415
+ if (!present.has(state))
416
+ continue;
417
+ const matched = getCachedElementMatches(el, rules, state);
418
+ const relevant = matched.some((m) => m.rule.selectorText.includes(`:${state}`));
419
+ if (relevant)
420
+ available.push(state);
421
+ }
422
+ return available;
423
+ }
@@ -1,6 +1,7 @@
1
- /** Number of unique selector strings actually computed (for tests). */
1
+ export declare function splitSelectorAtTopLevel(selector: string, separator?: string): string[];
2
+ /** Test-only specificity cache size. */
2
3
  export declare function specificityComputationCount(): number;
3
- /** Clears the specificity memo (for tests). */
4
+ /** Test-only specificity cache reset. */
4
5
  export declare function resetSpecificityMemo(): void;
5
6
  /**
6
7
  * Computes the cascade weight for a selector without asking the DOM to match
@@ -8,5 +9,20 @@ export declare function resetSpecificityMemo(): void;
8
9
  * still recompute branch specificity (multi-branch selectors).
9
10
  */
10
11
  export declare function computeSpecificity(selectorText: string): number;
11
- /** The unmemoized implementation, exposed for equivalence tests. */
12
+ /** Unmemoized implementation used by equivalence tests. */
12
13
  export declare function computeSpecificityCore(selectorText: string): number;
14
+ export interface SelectorPseudoClass {
15
+ name: string;
16
+ start: number;
17
+ end: number;
18
+ }
19
+ export declare function selectorPseudoClasses(selector: string): readonly SelectorPseudoClass[];
20
+ export declare function hasSelectorPseudoClass(selector: string, names: ReadonlySet<string>): boolean;
21
+ export declare function isElementSensitiveSelector(selector: string): boolean;
22
+ /**
23
+ * Rejects selectors whose rightmost compound requires a class the element
24
+ * does not have. This is a conservative prefilter: it only returns false for
25
+ * an impossible positive class requirement and leaves all other selectors to
26
+ * the browser's selector engine.
27
+ */
28
+ export declare function canMatchSelectorSubject(el: Element, selector: string): boolean;