nudge-ui 0.2.4 → 0.2.5

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 (250) hide show
  1. package/dist/cli/agentCommand.js +4 -28
  2. package/dist/client.mjs +863 -213
  3. package/dist/hosts/next/wrapper.js +1 -7
  4. package/dist/hosts/projectBridge.js +3 -17
  5. package/dist/inspector/agent/McpConnectionDialog.js +7 -3
  6. package/dist/inspector/agent/activity.d.ts +1 -1
  7. package/dist/inspector/agent/activity.js +3 -2
  8. package/dist/inspector/agent/client.d.ts +2 -2
  9. package/dist/inspector/agent/client.js +28 -29
  10. package/dist/inspector/agent/connectionStatus.d.ts +3 -1
  11. package/dist/inspector/agent/connectionStatus.js +25 -0
  12. package/dist/inspector/agent/httpTransport.js +31 -147
  13. package/dist/inspector/agent/protocol.d.ts +1 -1
  14. package/dist/inspector/agent/protocol.js +1 -1
  15. package/dist/inspector/agent/verification.d.ts +1 -1
  16. package/dist/inspector/agent/verification.js +7 -4
  17. package/dist/inspector/boot.d.ts +3 -0
  18. package/dist/inspector/boot.js +297 -0
  19. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
  20. package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
  21. package/dist/inspector/canvas/CanvasCard.js +21 -12
  22. package/dist/inspector/canvas/CanvasElementOverlay.js +82 -127
  23. package/dist/inspector/canvas/CanvasToolbar.js +2 -1
  24. package/dist/inspector/canvas/CanvasWorkspace.js +74 -97
  25. package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
  26. package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
  27. package/dist/inspector/canvas/PageCard.d.ts +10 -0
  28. package/dist/inspector/canvas/PageCard.js +51 -0
  29. package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
  30. package/dist/inspector/canvas/SketchToolOptions.js +86 -0
  31. package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
  32. package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
  33. package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
  34. package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
  35. package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
  36. package/dist/inspector/canvas/assets/eraser.svg +28 -0
  37. package/dist/inspector/canvas/assets/pen.svg +25 -0
  38. package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
  39. package/dist/inspector/canvas/canvasFrame.js +10 -0
  40. package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
  41. package/dist/inspector/canvas/canvasGestures.js +0 -6
  42. package/dist/inspector/canvas/canvasStore.d.ts +6 -0
  43. package/dist/inspector/canvas/canvasStore.js +36 -1
  44. package/dist/inspector/canvas/frameConnection.d.ts +48 -0
  45. package/dist/inspector/canvas/frameConnection.js +73 -0
  46. package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
  47. package/dist/inspector/canvas/frameProtocol.js +36 -0
  48. package/dist/inspector/canvas/pageSurface.d.ts +11 -0
  49. package/dist/inspector/canvas/pageSurface.js +34 -0
  50. package/dist/inspector/canvas/projection.d.ts +19 -9
  51. package/dist/inspector/canvas/projection.js +58 -31
  52. package/dist/inspector/canvas/rendererBootstrap.js +4 -1
  53. package/dist/inspector/canvas/rendererElementSelector.js +9 -1
  54. package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
  55. package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
  56. package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
  57. package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
  58. package/dist/inspector/canvas/rendererStylesheet.js +15 -0
  59. package/dist/inspector/canvas/sessionStore.d.ts +2 -2
  60. package/dist/inspector/canvas/sessionStore.js +12 -9
  61. package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
  62. package/dist/inspector/canvas/staleChangeDetector.js +10 -22
  63. package/dist/inspector/changes/codecs.d.ts +1 -0
  64. package/dist/inspector/changes/codecs.js +20 -5
  65. package/dist/inspector/changes/draftPreview.d.ts +12 -0
  66. package/dist/inspector/changes/draftPreview.js +133 -0
  67. package/dist/inspector/changes/editModel.d.ts +8 -0
  68. package/dist/inspector/changes/editModel.js +8 -2
  69. package/dist/inspector/changes/managedStyleProjection.js +2 -1
  70. package/dist/inspector/changes/previewDiagnostics.js +1 -1
  71. package/dist/inspector/changes/types.d.ts +11 -1
  72. package/dist/inspector/comments/CommentsOverlay.js +18 -16
  73. package/dist/inspector/comments/store.js +3 -2
  74. package/dist/inspector/comments/verification.js +1 -1
  75. package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
  76. package/dist/inspector/componentSemantics/changeAction.js +1 -1
  77. package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
  78. package/dist/inspector/componentSemantics/textBinding.js +4 -45
  79. package/dist/inspector/drafts/draftStore.d.ts +109 -0
  80. package/dist/inspector/drafts/draftStore.js +451 -0
  81. package/dist/inspector/drafts/model.d.ts +9 -1
  82. package/dist/inspector/drafts/model.js +5 -1
  83. package/dist/inspector/drafts/store.d.ts +53 -15
  84. package/dist/inspector/drafts/store.js +143 -169
  85. package/dist/inspector/index.d.ts +2 -4
  86. package/dist/inspector/index.js +2 -264
  87. package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
  88. package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
  89. package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
  90. package/dist/inspector/inspection/bridge.d.ts +1 -1
  91. package/dist/inspector/inspection/bridge.js +2 -1
  92. package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
  93. package/dist/inspector/inspection/browserCssInspection.js +28 -6
  94. package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
  95. package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
  96. package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
  97. package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
  98. package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
  99. package/dist/inspector/iterations/capture.d.ts +2 -1
  100. package/dist/inspector/iterations/client.d.ts +2 -1
  101. package/dist/inspector/iterations/coordinator.d.ts +4 -3
  102. package/dist/inspector/iterations/coordinator.js +5 -3
  103. package/dist/inspector/iterations/prompt.js +9 -8
  104. package/dist/inspector/overlay/interactionStyles.js +17 -0
  105. package/dist/inspector/pageInspector.d.ts +42 -0
  106. package/dist/inspector/pageInspector.js +342 -0
  107. package/dist/inspector/projection/draftProjection.d.ts +10 -2
  108. package/dist/inspector/projection/draftProjection.js +9 -1
  109. package/dist/inspector/projection/managedStylesheet.js +24 -4
  110. package/dist/inspector/projection/renderedInstance.js +7 -1
  111. package/dist/inspector/projection/structuralProjection.d.ts +0 -9
  112. package/dist/inspector/projection/structuralProjection.js +14 -44
  113. package/dist/inspector/projection/textProjection.d.ts +8 -2
  114. package/dist/inspector/projection/textProjection.js +21 -13
  115. package/dist/inspector/prompt/clipboardHandoff.d.ts +9 -0
  116. package/dist/inspector/prompt/clipboardHandoff.js +42 -16
  117. package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
  118. package/dist/inspector/prompt/generatePrompt.js +243 -147
  119. package/dist/inspector/prompt/promptDocument.d.ts +3 -0
  120. package/dist/inspector/prompt/promptDocument.js +8 -0
  121. package/dist/inspector/prompt/promptSettings.js +7 -5
  122. package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
  123. package/dist/inspector/runtime/inspectorStorage.js +42 -0
  124. package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
  125. package/dist/inspector/runtime/ownedStylesheets.js +72 -0
  126. package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
  127. package/dist/inspector/runtime/runtimeConfig.js +10 -1
  128. package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
  129. package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
  130. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
  131. package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
  132. package/dist/inspector/selection/editScope.d.ts +1 -0
  133. package/dist/inspector/selection/editScope.js +13 -2
  134. package/dist/inspector/shell/AgentStatusButton.js +2 -2
  135. package/dist/inspector/shell/AppShell.js +6 -2
  136. package/dist/inspector/shell/ChangesLog.js +27 -9
  137. package/dist/inspector/shell/CopyPromptButton.js +60 -54
  138. package/dist/inspector/shell/EmptyState.js +3 -3
  139. package/dist/inspector/shell/InspectorShell.d.ts +0 -3
  140. package/dist/inspector/shell/InspectorShell.js +21 -13
  141. package/dist/inspector/shell/PeekOriginalButton.js +3 -3
  142. package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
  143. package/dist/inspector/shell/interactionStatePreview.js +112 -0
  144. package/dist/inspector/shell/originalPreview.d.ts +3 -2
  145. package/dist/inspector/shell/originalPreview.js +10 -37
  146. package/dist/inspector/shell/stateValue.js +2 -3
  147. package/dist/inspector/sketch/SketchEditor.js +5 -4
  148. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
  149. package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
  150. package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
  151. package/dist/inspector/sketch/SketchOverlay.js +139 -263
  152. package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
  153. package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
  154. package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
  155. package/dist/inspector/sketch/SketchThumbnail.js +18 -0
  156. package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
  157. package/dist/inspector/sketch/SketchWorkspace.js +69 -34
  158. package/dist/inspector/sketch/capture.js +19 -3
  159. package/dist/inspector/sketch/copySketch.d.ts +10 -0
  160. package/dist/inspector/sketch/copySketch.js +52 -0
  161. package/dist/inspector/sketch/drawingTools.d.ts +17 -0
  162. package/dist/inspector/sketch/drawingTools.js +47 -0
  163. package/dist/inspector/sketch/frameHeading.d.ts +9 -0
  164. package/dist/inspector/sketch/frameHeading.js +30 -0
  165. package/dist/inspector/sketch/freehand.js +3 -3
  166. package/dist/inspector/sketch/geometry.d.ts +6 -0
  167. package/dist/inspector/sketch/geometry.js +19 -0
  168. package/dist/inspector/sketch/history.d.ts +30 -0
  169. package/dist/inspector/sketch/history.js +58 -0
  170. package/dist/inspector/sketch/interaction.d.ts +1 -3
  171. package/dist/inspector/sketch/interaction.js +6 -9
  172. package/dist/inspector/sketch/model.d.ts +35 -1
  173. package/dist/inspector/sketch/model.js +116 -61
  174. package/dist/inspector/sketch/persistence.d.ts +14 -0
  175. package/dist/inspector/sketch/persistence.js +63 -7
  176. package/dist/inspector/sketch/raster.d.ts +6 -2
  177. package/dist/inspector/sketch/raster.js +43 -17
  178. package/dist/inspector/sketch/store.d.ts +1 -1
  179. package/dist/inspector/sketch/store.js +1 -1
  180. package/dist/inspector/sketch/videoFrames.d.ts +6 -0
  181. package/dist/inspector/sketch/videoFrames.js +81 -0
  182. package/dist/inspector/styleEditors/BackgroundEditor.js +2 -1
  183. package/dist/inspector/styleEditors/BorderEditor.js +30 -130
  184. package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
  185. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
  186. package/dist/inspector/styleEditors/LayoutSection.js +2 -2
  187. package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
  188. package/dist/inspector/styleEditors/borderModel.js +114 -0
  189. package/dist/inspector/styleEditors/layoutValue.js +2 -3
  190. package/dist/inspector/tokens/TokenField.js +34 -3
  191. package/dist/inspector/tokens/TokensPanel.js +2 -1
  192. package/dist/inspector/tokens/editActions.d.ts +3 -3
  193. package/dist/inspector/tokens/editActions.js +9 -3
  194. package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
  195. package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
  196. package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
  197. package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
  198. package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
  199. package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
  200. package/dist/inspector/tokens/resolution.d.ts +6 -3
  201. package/dist/inspector/tokens/resolution.js +91 -112
  202. package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
  203. package/dist/inspector/tokens/tokenEdits.js +1 -1
  204. package/dist/inspector/ui/InlineStyleWarning.js +2 -2
  205. package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
  206. package/dist/inspector/ui/InspectorStyle.js +26 -0
  207. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  208. package/dist/inspector/ui/PopoverListbox.js +3 -3
  209. package/dist/inspector/ui/Toast.d.ts +14 -0
  210. package/dist/inspector/ui/Toast.js +30 -0
  211. package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
  212. package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
  213. package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
  214. package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
  215. package/dist/inspector/ui/customCursors.d.ts +5 -0
  216. package/dist/inspector/ui/customCursors.js +32 -6
  217. package/dist/inspector/ui/styles.js +12 -10
  218. package/dist/inspector/workspace/commands.js +1 -2
  219. package/dist/inspector/workspace/controller.js +1 -2
  220. package/dist/inspector/workspace/handoff.js +3 -2
  221. package/dist/inspector/workspace/sentItems.d.ts +3 -6
  222. package/dist/inspector/workspace/sentItems.js +6 -24
  223. package/dist/inspector/workspace/timeline.d.ts +24 -10
  224. package/dist/inspector/workspace/timeline.js +118 -96
  225. package/dist/internal/agent-protocol.d.ts +50 -13
  226. package/dist/internal/agent-protocol.js +145 -19
  227. package/dist/nudge-ui.mjs +81 -65
  228. package/dist/project/mcpPackage.d.ts +8 -0
  229. package/dist/project/mcpPackage.js +60 -0
  230. package/package.json +12 -2
  231. package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
  232. package/dist/inspector/canvas/measurementProjection.js +0 -16
  233. package/dist/inspector/changes/changesLog.d.ts +0 -49
  234. package/dist/inspector/changes/changesLog.js +0 -255
  235. package/dist/inspector/changes/draftChanges.d.ts +0 -58
  236. package/dist/inspector/changes/draftChanges.js +0 -304
  237. package/dist/inspector/conformance/projection.d.ts +0 -2
  238. package/dist/inspector/conformance/projection.js +0 -3
  239. package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
  240. package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
  241. package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
  242. package/dist/inspector/overlay/InspectorOverlay.js +0 -332
  243. package/dist/inspector/overlay/elementSelector.d.ts +0 -1
  244. package/dist/inspector/overlay/elementSelector.js +0 -111
  245. package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
  246. package/dist/inspector/sketch/SketchChanges.js +0 -72
  247. package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
  248. package/dist/inspector/sketch/SketchEntryButton.js +0 -11
  249. package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
  250. package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
@@ -0,0 +1,114 @@
1
+ import { findTokenRow } from "./rowLookup.js";
2
+ import { isZeroCssValue } from "./stylePresence.js";
3
+ export const BORDER_STYLES = ["none", "hidden", "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset"];
4
+ export const BORDER_SIDES = ["top", "right", "bottom", "left"];
5
+ const BORDER_STYLE_SET = new Set(BORDER_STYLES);
6
+ const INVISIBLE_BORDER_STYLES = new Set(["none", "hidden"]);
7
+ const COMPONENT_FALLBACK = {
8
+ width: "0px",
9
+ style: "none",
10
+ color: "currentcolor",
11
+ };
12
+ export function borderProperty(component, side) {
13
+ return side ? `border-${side}-${component}` : `border-${component}`;
14
+ }
15
+ function isBorderStyle(value) {
16
+ return BORDER_STYLE_SET.has(value);
17
+ }
18
+ function sideProperties(component) {
19
+ return BORDER_SIDES.map((side) => borderProperty(component, side));
20
+ }
21
+ /**
22
+ * Effective value of one border width, style, or color property. Structured
23
+ * shorthand parts win, then authored text, then the computed style.
24
+ */
25
+ function borderComponentValue(source, component, property) {
26
+ const row = findTokenRow(source.rows, property);
27
+ const fallback = COMPONENT_FALLBACK[component];
28
+ const structured = row?.structure?.[component]?.trim();
29
+ if (component === "style") {
30
+ if (structured)
31
+ return structured.toLowerCase();
32
+ const authored = (row?.authored ?? row?.declaredValue ?? "").trim().toLowerCase();
33
+ if (isBorderStyle(authored))
34
+ return authored;
35
+ return (source.computed(property, fallback) || fallback).toLowerCase();
36
+ }
37
+ if (structured)
38
+ return structured;
39
+ return (row?.authored || row?.declaredValue || row?.resolvedValue || source.computed(property, fallback) || fallback).trim();
40
+ }
41
+ /** Width and color compare token bindings too, so a token on one side keeps sides apart. */
42
+ function sideSignature(source, component, property) {
43
+ const value = borderComponentValue(source, component, property).toLowerCase();
44
+ if (component === "style")
45
+ return value;
46
+ return `${value}|${findTokenRow(source.rows, property)?.tokenName ?? ""}`;
47
+ }
48
+ /**
49
+ * A component is linked when every side shares its value. A lone shorthand row
50
+ * with no per-side rows is linked by construction; four different `border-*`
51
+ * shorthands compare by their structured parts, not their full text.
52
+ */
53
+ function componentLinked(source, component) {
54
+ const properties = sideProperties(component);
55
+ const hasShorthandRow = findTokenRow(source.rows, borderProperty(component)) !== null;
56
+ const hasSideRows = properties.some((property) => findTokenRow(source.rows, property) !== null);
57
+ if (hasShorthandRow && !hasSideRows)
58
+ return true;
59
+ return new Set(properties.map((property) => sideSignature(source, component, property))).size === 1;
60
+ }
61
+ function linkedRow(source, component) {
62
+ const shorthand = borderProperty(component);
63
+ const direct = findTokenRow(source.rows, shorthand);
64
+ if (direct)
65
+ return direct;
66
+ const top = findTokenRow(source.rows, borderProperty(component, "top"));
67
+ return top ? { ...top, property: shorthand } : null;
68
+ }
69
+ export function readBorderEditor(source, { group }) {
70
+ const borderRow = findTokenRow(source.rows, "border");
71
+ const hasStructuredRows = source.rows.some((row) => Boolean(row.structure));
72
+ return {
73
+ sidesLinked: !group
74
+ && componentLinked(source, "width")
75
+ && componentLinked(source, "style")
76
+ && componentLinked(source, "color"),
77
+ rawShorthandRow: !group && borderRow && !borderRow.structure && !hasStructuredRows ? borderRow : null,
78
+ linkedStyleDraws: !INVISIBLE_BORDER_STYLES.has(borderComponentValue(source, "style", "border-style")),
79
+ linkedRows: {
80
+ width: linkedRow(source, "width"),
81
+ style: linkedRow(source, "style"),
82
+ color: linkedRow(source, "color"),
83
+ },
84
+ };
85
+ }
86
+ /** Whether any side draws a visible line, including after stylesheet resets. */
87
+ export function hasVisibleBorder(computed) {
88
+ return BORDER_SIDES.some((side) => {
89
+ const style = computed(borderProperty("style", side), "none").toLowerCase();
90
+ if (INVISIBLE_BORDER_STYLES.has(style))
91
+ return false;
92
+ return !isZeroCssValue(computed(borderProperty("width", side), "0px"));
93
+ });
94
+ }
95
+ /** Linking sides writes each component shorthand from the top side's values. */
96
+ function linkSidesDeclarations(source) {
97
+ const components = ["style", "width", "color"];
98
+ return components.map((component) => ({
99
+ property: borderProperty(component),
100
+ value: borderComponentValue(source, component, borderProperty(component, "top")),
101
+ }));
102
+ }
103
+ export function borderDeclarations(source, intent) {
104
+ switch (intent.kind) {
105
+ case "add":
106
+ return [{ property: "border", value: "1px solid" }];
107
+ case "remove":
108
+ return [{ property: "border", value: "0 solid" }];
109
+ case "link-sides":
110
+ return linkSidesDeclarations(source);
111
+ case "set-style":
112
+ return isBorderStyle(intent.style) ? [{ property: intent.property, value: intent.style }] : [];
113
+ }
114
+ }
@@ -1,7 +1,7 @@
1
1
  import { getActiveStyleState } from "../shell/styleState.js";
2
2
  import { getStateStyleValue } from "../shell/stateValue.js";
3
3
  import { getElementComputedStyle } from "../runtime/domRealm.js";
4
- import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
4
+ import { readInspectionSnapshot } from "../inspection/inspectionSnapshot.js";
5
5
  const DEFAULT_LAYOUT_VALUES = {
6
6
  width: "auto",
7
7
  height: "auto",
@@ -33,8 +33,7 @@ export function meaningfulLayoutValue(el, property) {
33
33
  export function readAuthoredStyleValue(el, property) {
34
34
  try {
35
35
  const state = getActiveStyleState();
36
- const row = getBrowserCssInspection(el.ownerDocument ?? document)
37
- .inspect(el, { state }).properties
36
+ const row = readInspectionSnapshot(el, state)?.properties
38
37
  .find((candidate) => candidate.property === property);
39
38
  const authored = row?.authored ?? row?.declaredValue;
40
39
  if (authored?.trim())
@@ -16,10 +16,11 @@ import { Tooltip } from "../ui/Tooltip.js";
16
16
  import { cx } from "../ui/classNames.js";
17
17
  import { TokenChip } from "./TokenChip.js";
18
18
  import { startDragPointerLock } from "./dragPointerLock.js";
19
- import { createChangeHistoryGroup } from "../changes/draftChanges.js";
19
+ import { createChangeHistoryGroup } from "../workspace/timeline.js";
20
20
  import { isMultiTarget } from "../selection/editTarget.js";
21
21
  import { inlineBlockedBy } from "../styleEditors/inlineAuthored.js";
22
22
  import { InlineStyleWarning } from "../ui/InlineStyleWarning.js";
23
+ import { isMacPlatform } from "../shell/shortcuts.js";
23
24
  const NON_COLOR_FACTS = { hasEmbeddedAlpha: false, isExpression: false, opacityEditable: false };
24
25
  function colorFacts(value) {
25
26
  return selectTokens({
@@ -235,6 +236,10 @@ export function TokenValueField(props) {
235
236
  return Array.from(new Set(suggestions));
236
237
  }, [suggestions]);
237
238
  function handleSuggestionSelect(value) {
239
+ if (value === DETACH_VALUE) {
240
+ handleDelink();
241
+ return;
242
+ }
238
243
  const rawSuggestion = decodeRawSuggestion(value);
239
244
  if (rawSuggestion !== null) {
240
245
  selectedFromPopover.current = true;
@@ -467,9 +472,20 @@ export function TokenValueField(props) {
467
472
  if (activeToken) {
468
473
  const chipValue = chipVariant === "small" ? stripCssUnit(activeToken.value) : activeToken.name;
469
474
  const embedColorSwatch = isColor && chipVariant !== "small";
470
- return (_jsxs("span", { className: cx("token-field", isColor && "token-field--color", embedColorSwatch && "token-field--color-chip", blockedBy !== null && "token-field--blocked", className), "data-test": "token-field", "data-property": property, "aria-disabled": fieldDisabled || undefined, "aria-label": label, children: [renderLeading(), embedColorSwatch ? null : colorControlEl, _jsxs(TokenChip, { size: chipVariant, "data-group": groupByTokenName.get(activeToken.name) ?? tokenGroup(activeToken), children: [_jsx(PopoverListbox, { query: "", value: activeToken.name, open: isTokenPickerOpen, trigger: (_jsxs(TokenChip.Picker, { children: [embedColorSwatch ? colorControlEl : null, _jsx(TokenChip.Label, { title: chipVariant === "small" ? activeToken.name : undefined, children: chipValue })] })), triggerClassName: "token-chip__trigger", triggerDataTest: "token-chip", triggerAriaLabel: `Change ${property} token`, searchable: true, searchPlaceholder: "Search tokens\u2026", searchAriaLabel: "Search tokens", items: [...rawSuggestionItems, ...relevantTokens.map((entry) => tokenSuggestion(entry, groupByTokenName))], onQueryChange: () => undefined, onOpenChange: setTokenPickerOpen, disabled: fieldDisabled, onSelect: (value) => {
475
+ return (_jsxs("span", { className: cx("token-field", isColor && "token-field--color", embedColorSwatch && "token-field--color-chip", blockedBy !== null && "token-field--blocked", className), "data-test": "token-field", "data-property": property, "aria-disabled": fieldDisabled || undefined, "aria-label": label, children: [renderLeading(), embedColorSwatch ? null : colorControlEl, _jsxs(TokenChip, { size: chipVariant, "data-group": groupByTokenName.get(activeToken.name) ?? tokenGroup(activeToken), children: [_jsx(PopoverListbox, { query: "", value: activeToken.name, open: isTokenPickerOpen, trigger: (_jsxs(TokenChip.Picker, { children: [embedColorSwatch ? colorControlEl : null, _jsx(TokenChip.Label, { title: chipVariant === "small" ? activeToken.name : undefined, children: chipValue })] })), triggerClassName: "token-chip__trigger", triggerDataTest: "token-chip", triggerAriaLabel: `Change ${property} token`, searchable: true, searchPlaceholder: "Search tokens\u2026", searchAriaLabel: "Search tokens", searchOnKeyDown: (event) => {
476
+ if (fieldDisabled || !isDetachKey(event))
477
+ return;
478
+ event.preventDefault();
479
+ event.stopPropagation();
480
+ setTokenPickerOpen(false);
481
+ handleDelink();
482
+ }, items: [
483
+ detachItem(),
484
+ ...rawSuggestionItems,
485
+ ...relevantTokens.map((entry) => tokenSuggestion(entry, groupByTokenName)),
486
+ ], onQueryChange: () => undefined, onOpenChange: setTokenPickerOpen, disabled: fieldDisabled, onSelect: (value) => {
471
487
  handleSuggestionSelect(value);
472
- } }), _jsx(TokenChip.Action, { children: _jsx(Tooltip, { stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", style: { borderRadius: "var(--radius-4)" }, label: "Replace with raw value", "data-test": "delink-btn", disabled: fieldDisabled, onClick: handleDelink, children: _jsx(IconLinkOff, { size: chipVariant === "small" ? 12 : 14, stroke: 1.75, "aria-hidden": "true" }) }) }) })] }), opacityControl, _jsx(AtRuleIndicator, { atRules: fieldAtRules }), trailing ? _jsx("span", { className: "token-field__trailing", children: trailing }) : null, _jsx(InlineStyleWarning, { blockedBy: blockedBy, dataTest: inlineWarningDataTest, tooltipDataTest: inlineWarningTooltipDataTest })] }));
488
+ } }), chipVariant === "small" ? null : (_jsx(TokenChip.Action, { children: _jsx(Tooltip, { stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", style: { borderRadius: "var(--radius-4)" }, label: "Detach token", "data-test": "delink-btn", disabled: fieldDisabled, onClick: handleDelink, children: _jsx(IconLinkOff, { size: 14, stroke: 1.75, "aria-hidden": "true" }) }) }) }))] }), opacityControl, _jsx(AtRuleIndicator, { atRules: fieldAtRules }), trailing ? _jsx("span", { className: "token-field__trailing", children: trailing }) : null, _jsx(InlineStyleWarning, { blockedBy: blockedBy, dataTest: inlineWarningDataTest, tooltipDataTest: inlineWarningTooltipDataTest })] }));
473
489
  }
474
490
  const showPopover = isFocused && (filteredTokens.length > 0 || availableSuggestions.length > 0);
475
491
  return (_jsxs("span", { className: cx("token-field", "token-field--raw", isColor && "token-field--color", blockedBy !== null && "token-field--blocked", className), "data-test": "token-field", "data-property": property, "aria-disabled": fieldDisabled || undefined, "aria-label": label, children: [renderLeading(), colorControlEl, _jsx(PopoverListbox, { query: rawValue, value: null, open: showPopover, placeholder: property, inputRef: inputRef, inputAppearance: "embedded", inputDataTest: inputDataTest, inputOnFocus: () => {
@@ -606,6 +622,21 @@ function tokenSuggestion(entry, groups) {
606
622
  };
607
623
  }
608
624
  const RAW_SUGGESTION_PREFIX = "\u0000raw:";
625
+ const DETACH_VALUE = "\u0000detach";
626
+ function detachItem() {
627
+ return {
628
+ value: DETACH_VALUE,
629
+ label: "Detach token",
630
+ "data-test": "detach-token-item",
631
+ leading: _jsx(IconLinkOff, { size: 14, stroke: 1.75, "aria-hidden": "true" }),
632
+ trailing: _jsx("kbd", { className: "token-shortcut", children: isMacPlatform() ? "⌫" : "Del" }),
633
+ };
634
+ }
635
+ function isDetachKey(event) {
636
+ return (event.key === "Backspace" || event.key === "Delete")
637
+ && event.currentTarget.value === ""
638
+ && !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
639
+ }
609
640
  function rawSuggestion(value) {
610
641
  return {
611
642
  value: `${RAW_SUGGESTION_PREFIX}${value}`,
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useMemo, useState } from "react";
3
- import { isTokenChange, useChanges } from "../changes/changesLog.js";
3
+ import { isTokenChange } from "../changes/types.js";
4
+ import { useChanges } from "../drafts/store.js";
4
5
  import { TextInput } from "../ui/TextInput.js";
5
6
  import { ControlSurface } from "../ui/ControlSurface.js";
6
7
  import { Disclosure } from "../ui/Disclosure.js";
@@ -1,8 +1,8 @@
1
1
  import type { TokenEntry } from "../../css/model/index.js";
2
- import type { ElementChangeRecord } from "../changes/changesLog.js";
2
+ import type { ElementChangeRecord } from "../changes/types.js";
3
3
  import { type EditTarget } from "../selection/editTarget.js";
4
- export type { ChangeRecord } from "../changes/changesLog.js";
5
- export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../changes/changesLog.js";
4
+ export type { ChangeRecord } from "../changes/types.js";
5
+ export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../drafts/store.js";
6
6
  export interface StyleEditMetadata {
7
7
  sourceProperty?: string;
8
8
  sourceAuthoredValue?: string;
@@ -1,9 +1,9 @@
1
1
  import { parseDataSrc } from "../selection/resolveSelection.js";
2
- import { appendChanges, } from "../changes/changesLog.js";
2
+ import { appendChanges } from "../drafts/store.js";
3
3
  import { canPlanBatchEditScopes, planBatchEditScopes, selectorForElement, sourceSiteSelector } from "../selection/editScope.js";
4
4
  import { getActiveStyleState, selectorForInteractionState } from "../shell/styleState.js";
5
5
  import { getStateStyleValue } from "../shell/stateValue.js";
6
- import { boundRuntimeEvidence, isRuntimeCreatedElement, isRuntimeGeneratedSource, normalizeRuntimeTag, normalizeRuntimeText, } from "../runtime/staticHtmlRuntimeIdentity.js";
6
+ import { boundRuntimeEvidence, isRuntimeCreatedElement, isRuntimeGeneratedSource, normalizeRuntimeTag, normalizeRuntimeText, runtimeDomPath, } from "../runtime/staticHtmlRuntimeIdentity.js";
7
7
  import { targetElements } from "../selection/editTarget.js";
8
8
  import { changeKey } from "../changes/model.js";
9
9
  function boundedRenderedText(el) {
@@ -18,6 +18,8 @@ function sourceFields(el) {
18
18
  text: boundedRenderedText(el),
19
19
  props: boundRuntimeEvidence(el.getAttribute("data-cprops")),
20
20
  ariaLabel: boundRuntimeEvidence(el.getAttribute("aria-label")),
21
+ domPath: runtimeDomPath(el),
22
+ elementId: boundRuntimeEvidence(el.id || null),
21
23
  });
22
24
  if (isRuntimeCreatedElement(el) || isRuntimeGeneratedSource(src)) {
23
25
  return {
@@ -45,10 +47,14 @@ function sourceFields(el) {
45
47
  sourceEvidence: {
46
48
  tagName: normalizeRuntimeTag(el.tagName),
47
49
  props: boundRuntimeEvidence(el.getAttribute("data-cprops")),
50
+ text: boundedRenderedText(el),
51
+ ariaLabel: boundRuntimeEvidence(el.getAttribute("aria-label")),
52
+ domPath: runtimeDomPath(el),
53
+ elementId: boundRuntimeEvidence(el.id || null),
48
54
  },
49
55
  };
50
56
  }
51
- export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../changes/changesLog.js";
57
+ export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../drafts/store.js";
52
58
  /** Returns whether a style write can address every element in the target safely. */
53
59
  export function canEditStyles(target) {
54
60
  return canPlanBatchEditScopes(targetElements(target));
@@ -0,0 +1,15 @@
1
+ export interface ContainerQueryPair {
2
+ element: HTMLElement;
3
+ /** Outer-to-inner conditions, including container names. */
4
+ conditions: readonly string[];
5
+ }
6
+ export interface ContainerQueryEvaluator {
7
+ prefetch(pairs: readonly ContainerQueryPair[]): void;
8
+ matches(element: HTMLElement, conditions: readonly string[]): boolean;
9
+ }
10
+ /**
11
+ * Evaluates container conditions in batches for one inspection refresh.
12
+ * Each pair gets its own property, so inherited probe values cannot answer
13
+ * another element's query. No stylesheet or marker survives a batch.
14
+ */
15
+ export declare function createContainerQueryEvaluator(doc: Document): ContainerQueryEvaluator;
@@ -0,0 +1,86 @@
1
+ import { countInspectionWork } from "../../inspection/performanceDiagnostics.js";
2
+ import { adoptStylesheet, allowsStyleElements, releaseAdoptedStylesheet } from "../../runtime/ownedStylesheets.js";
3
+ const PROBE_SHEET_KEY = "nudge-ui-container-probe";
4
+ let nextProbeId = 0;
5
+ /**
6
+ * Evaluates container conditions in batches for one inspection refresh.
7
+ * Each pair gets its own property, so inherited probe values cannot answer
8
+ * another element's query. No stylesheet or marker survives a batch.
9
+ */
10
+ export function createContainerQueryEvaluator(doc) {
11
+ const results = new WeakMap();
12
+ const key = (conditions) => JSON.stringify(conditions);
13
+ function prefetch(pairs) {
14
+ const pending = [];
15
+ for (const pair of pairs) {
16
+ if (pair.element.ownerDocument !== doc)
17
+ throw new TypeError("Container query target belongs to another document.");
18
+ if (pair.conditions.length === 0)
19
+ continue;
20
+ let values = results.get(pair.element);
21
+ if (!values) {
22
+ values = new Map();
23
+ results.set(pair.element, values);
24
+ }
25
+ const conditionKey = key(pair.conditions);
26
+ if (values.has(conditionKey))
27
+ continue;
28
+ // Reserve the pair before collecting the rest of this batch.
29
+ values.set(conditionKey, false);
30
+ const id = ++nextProbeId;
31
+ pending.push({ ...pair, key: conditionKey, marker: `data-container-probe-${id}`, property: `--nudge-container-probe-${id}` });
32
+ }
33
+ if (!doc.head || pending.length === 0)
34
+ return;
35
+ const cssText = pending.map(({ conditions, marker, property }) => {
36
+ let rule = `[${marker}] { ${property}: 1 !important; }`;
37
+ for (let index = conditions.length - 1; index >= 0; index--) {
38
+ rule = `@container ${conditions[index]} { ${rule} }`;
39
+ }
40
+ return rule;
41
+ }).join("\n");
42
+ const sheet = allowsStyleElements(doc) ? doc.createElement("style") : null;
43
+ countInspectionWork(doc, "probePairs", pending.length);
44
+ countInspectionWork(doc, "probeBatches");
45
+ try {
46
+ for (const pair of pending)
47
+ pair.element.setAttribute(pair.marker, "");
48
+ if (sheet) {
49
+ sheet.setAttribute("data-nudge-ui", "container-probe");
50
+ sheet.textContent = cssText;
51
+ doc.head.append(sheet);
52
+ }
53
+ else {
54
+ adoptStylesheet(doc, PROBE_SHEET_KEY, cssText);
55
+ }
56
+ const styles = new Map();
57
+ for (const pair of pending) {
58
+ let style = styles.get(pair.element);
59
+ if (!style) {
60
+ style = doc.defaultView?.getComputedStyle(pair.element);
61
+ if (style)
62
+ styles.set(pair.element, style);
63
+ }
64
+ results.get(pair.element).set(pair.key, style?.getPropertyValue(pair.property).trim() === "1");
65
+ }
66
+ }
67
+ finally {
68
+ if (sheet)
69
+ sheet.remove();
70
+ else
71
+ releaseAdoptedStylesheet(doc, PROBE_SHEET_KEY);
72
+ for (const pair of pending)
73
+ pair.element.removeAttribute(pair.marker);
74
+ }
75
+ }
76
+ return {
77
+ prefetch,
78
+ matches(element, conditions) {
79
+ countInspectionWork(doc, "containerChecks");
80
+ if (conditions.length === 0)
81
+ return true;
82
+ prefetch([{ element, conditions }]);
83
+ return results.get(element)?.get(key(conditions)) ?? false;
84
+ },
85
+ };
86
+ }
@@ -1,4 +1,7 @@
1
1
  import { computeSpecificityCore } from "./selectorSemantics.js";
2
+ import { resolveNestedSelector } from "./nestedSelectors.js";
3
+ import { adoptedStylesheetKey, STATE_PREVIEW_SHEET_ID } from "../../runtime/ownedStylesheets.js";
4
+ import { INTERACTION_CURSOR_PROPERTY } from "../../ui/customCursors.js";
2
5
  const revisionRecords = new WeakMap();
3
6
  const ruleSnapshots = new WeakMap();
4
7
  const documentResolutionSessions = new WeakMap();
@@ -139,6 +142,20 @@ function isManagedStylesheetNode(node) {
139
142
  * marker attribute set on registered elements. Managed stylesheet text is
140
143
  * intentionally observed because it is a real cascade input.
141
144
  */
145
+ // The renderer rewrites these on hover and spacing drags; letting them bump
146
+ // the element revision re-resolves the whole selected cascade per pointer move.
147
+ const RENDERER_INLINE_CURSOR_PROPERTIES = ["cursor", INTERACTION_CURSOR_PROPERTY];
148
+ function styleTextWithoutCursor(doc, text) {
149
+ const scratch = doc.createElement("div");
150
+ scratch.style.cssText = text ?? "";
151
+ for (const property of RENDERER_INLINE_CURSOR_PROPERTIES)
152
+ scratch.style.removeProperty(property);
153
+ return scratch.style.cssText;
154
+ }
155
+ function isCursorOnlyStyleChange(target, oldValue) {
156
+ const doc = target.ownerDocument;
157
+ return styleTextWithoutCursor(doc, oldValue) === styleTextWithoutCursor(doc, target.getAttribute("style"));
158
+ }
142
159
  function isProbeMutation(record) {
143
160
  if (record.type === "attributes") {
144
161
  const target = record.target;
@@ -160,7 +177,8 @@ function isProbeMutation(record) {
160
177
  if (!registeredElements.has(target))
161
178
  return true;
162
179
  return name.startsWith("data-nudge-ui")
163
- || CONTAINER_PROBE_MARKER.test(name);
180
+ || CONTAINER_PROBE_MARKER.test(name)
181
+ || (name === "style" && isCursorOnlyStyleChange(target, record.oldValue));
164
182
  }
165
183
  if (record.type === "characterData") {
166
184
  return isProbeNode(record.target) && !isManagedStylesheetNode(record.target.parentElement);
@@ -215,6 +233,7 @@ export function documentRevisions(doc) {
215
233
  session.observer = observer;
216
234
  observer.observe(root, {
217
235
  attributes: true,
236
+ attributeOldValue: true,
218
237
  childList: true,
219
238
  characterData: true,
220
239
  subtree: true,
@@ -378,13 +397,16 @@ function contextForRule(cssText, record) {
378
397
  return params ? { kind: "media", params } : null;
379
398
  }
380
399
  if (/^\s*@container\b/i.test(cssText)) {
381
- // CSSContainerRule exposes `conditionText` and `containerName` in modern
382
- // engines. Fall back to the serialised prelude so named, style, and newer
400
+ // CSSContainerRule's `conditionText` is the whole prelude, name included;
401
+ // only prepend `containerName` for engines whose `conditionText` omits it.
402
+ // Fall back to the serialised prelude so named, style, and newer
383
403
  // container queries remain presentable without hand-parsing their grammar.
384
404
  const containerName = record.containerName?.trim();
385
- const fromInterfaces = [containerName && containerName !== "none" ? containerName : undefined, record.conditionText?.trim()]
386
- .filter((part) => Boolean(part))
387
- .join(" ");
405
+ const condition = record.conditionText?.trim() ?? "";
406
+ const hasName = Boolean(containerName) && containerName !== "none";
407
+ const conditionNamed = hasName && condition.startsWith(containerName)
408
+ && /^(?:$|[\s(])/.test(condition.slice(containerName.length));
409
+ const fromInterfaces = hasName && !conditionNamed ? `${containerName} ${condition}`.trim() : condition;
388
410
  const params = fromInterfaces || atRuleParams(cssText, "container");
389
411
  return params ? { kind: "container", params } : null;
390
412
  }
@@ -440,9 +462,12 @@ export function collectRules(doc) {
440
462
  }
441
463
  if (isStyleRuleInDocument(rule, doc)) {
442
464
  try {
465
+ const selectorText = inheritedSelector
466
+ ? resolveNestedSelector(rule.selectorText, inheritedSelector)
467
+ : rule.selectorText;
443
468
  out.push({
444
- selectorText: rule.selectorText,
445
- specificity: computeSpecificityCore(rule.selectorText),
469
+ selectorText,
470
+ specificity: computeSpecificityCore(selectorText),
446
471
  declarations: declarationsFromCssom(rule.style),
447
472
  source,
448
473
  sourceOrder: sourceOrder++,
@@ -453,7 +478,7 @@ export function collectRules(doc) {
453
478
  });
454
479
  const nested = rule.cssRules;
455
480
  if (nested?.length)
456
- walkRules(nested, active, layer, atRules, rule.selectorText, source);
481
+ walkRules(nested, active, layer, atRules, selectorText, source);
457
482
  }
458
483
  catch {
459
484
  continue;
@@ -514,6 +539,8 @@ export function collectRules(doc) {
514
539
  // jsdom does not currently expose CSSStyleSheet.ownerNode; the document
515
540
  // stylesheet list and stylesheet element list retain the same order.
516
541
  const owner = sheet.ownerNode ?? stylesheetNodes[index] ?? null;
542
+ if (owner?.id === STATE_PREVIEW_SHEET_ID)
543
+ continue;
517
544
  const source = owner?.dataset?.viteDevId
518
545
  ?? sheet.href
519
546
  ?? (owner?.id ? `#${owner.id}` : `stylesheet:${index + 1}`);
@@ -523,6 +550,14 @@ export function collectRules(doc) {
523
550
  inaccessible = true;
524
551
  }
525
552
  }
553
+ // Adopted sheets cascade after document stylesheets. Under a strict
554
+ // style-src policy they also hold the inspector's own managed rules.
555
+ for (const [index, sheet] of Array.from(doc.adoptedStyleSheets ?? []).entries()) {
556
+ const key = adoptedStylesheetKey(doc, sheet);
557
+ if (key === STATE_PREVIEW_SHEET_ID)
558
+ continue;
559
+ walkRules(sheet.cssRules, true, undefined, [], undefined, key ? `#${key}` : `adopted:${index + 1}`);
560
+ }
526
561
  const snapshot = { revision, rules: out, layerOrder, inaccessible };
527
562
  ruleSnapshots.set(doc, snapshot);
528
563
  return snapshot;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Resolves a CSSOM nested selector against its already resolved parent.
3
+ * `:is()` preserves nesting's maximum parent-list specificity without
4
+ * expanding every combination of parent and child selectors.
5
+ */
6
+ export declare function resolveNestedSelector(selector: string, parent: string): string;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Resolves a CSSOM nested selector against its already resolved parent.
3
+ * `:is()` preserves nesting's maximum parent-list specificity without
4
+ * expanding every combination of parent and child selectors.
5
+ */
6
+ export function resolveNestedSelector(selector, parent) {
7
+ const subject = `:is(${parent})`;
8
+ const branches = [];
9
+ let branch = "";
10
+ let hasNesting = false;
11
+ let parentheses = 0;
12
+ let brackets = 0;
13
+ let quote = null;
14
+ const finishBranch = () => {
15
+ branches.push(hasNesting ? branch.trim() : `${subject} ${branch.trim()}`);
16
+ branch = "";
17
+ hasNesting = false;
18
+ };
19
+ for (let index = 0; index < selector.length; index++) {
20
+ const character = selector[index];
21
+ if (character === "\\") {
22
+ branch += character + (selector[++index] ?? "");
23
+ continue;
24
+ }
25
+ if (quote) {
26
+ branch += character;
27
+ if (character === quote)
28
+ quote = null;
29
+ continue;
30
+ }
31
+ if (character === '"' || character === "'") {
32
+ quote = character;
33
+ }
34
+ else if (character === "/" && selector[index + 1] === "*") {
35
+ const end = selector.indexOf("*/", index + 2);
36
+ if (end < 0)
37
+ return "";
38
+ branch += selector.slice(index, end + 2);
39
+ index = end + 1;
40
+ continue;
41
+ }
42
+ else if (character === "[") {
43
+ brackets++;
44
+ }
45
+ else if (character === "]") {
46
+ brackets--;
47
+ }
48
+ else if (brackets === 0) {
49
+ if (character === "(")
50
+ parentheses++;
51
+ else if (character === ")")
52
+ parentheses--;
53
+ else if (character === "&") {
54
+ branch += subject;
55
+ hasNesting = true;
56
+ continue;
57
+ }
58
+ else if (character === "," && parentheses === 0) {
59
+ finishBranch();
60
+ continue;
61
+ }
62
+ }
63
+ branch += character;
64
+ }
65
+ finishBranch();
66
+ return branches.join(", ");
67
+ }
@@ -11,7 +11,25 @@ const TRANSIENT_PSEUDO_CLASSES = new Set([
11
11
  "target",
12
12
  ]);
13
13
  const INTERACTION_PSEUDO_CLASSES = new Set(INTERACTION_STATES);
14
+ /**
15
+ * Applies a per-branch selector transform to a selector list, so one stateful
16
+ * branch such as `.b:active` cannot drop a plain sibling branch such as `.a`.
17
+ */
18
+ function transformSelectorBranches(selector, transformBranch) {
19
+ if (!selector.includes(","))
20
+ return transformBranch(selector);
21
+ const kept = splitSelectorAtTopLevel(selector, ",")
22
+ .map((branch) => transformBranch(branch.trim()))
23
+ .filter((branch) => branch !== null && branch.length > 0);
24
+ return kept.length > 0 ? kept.join(", ") : null;
25
+ }
14
26
  function selectorForState(selector, state) {
27
+ return transformSelectorBranches(selector, (branch) => branchForState(branch, state));
28
+ }
29
+ function stableSelector(selector) {
30
+ return transformSelectorBranches(selector, (branch) => hasSelectorPseudoClass(branch, TRANSIENT_PSEUDO_CLASSES) ? null : branch);
31
+ }
32
+ function branchForState(selector, state) {
15
33
  const pseudoClasses = selectorPseudoClasses(selector)
16
34
  .filter(({ name }) => INTERACTION_PSEUDO_CLASSES.has(name));
17
35
  const states = new Set(pseudoClasses.map(({ name }) => name));
@@ -50,9 +68,10 @@ function rulesForTransform(rules, transform) {
50
68
  transformed = rules.map((rule) => ({ rule, selectorText: rule.selectorText }));
51
69
  }
52
70
  else if (transform === "stable") {
53
- transformed = rules
54
- .filter((rule) => !hasSelectorPseudoClass(rule.selectorText, TRANSIENT_PSEUDO_CLASSES))
55
- .map((rule) => ({ rule, selectorText: rule.selectorText }));
71
+ transformed = rules.flatMap((rule) => {
72
+ const selectorText = stableSelector(rule.selectorText);
73
+ return selectorText ? [{ rule, selectorText }] : [];
74
+ });
56
75
  }
57
76
  else {
58
77
  transformed = rules.flatMap((rule) => {
@@ -139,7 +158,7 @@ export function matchRuleForElement(el, entry) {
139
158
  rule: entry.rule,
140
159
  selectorText: entry.selectorText,
141
160
  branch,
142
- specificity: specificityForBranch({ ...entry.rule, selectorText: entry.selectorText }, branch),
161
+ specificity: specificityForBranch(entry.rule, branch),
143
162
  };
144
163
  }
145
164
  /**
@@ -1,4 +1,5 @@
1
1
  import type { InteractionState } from "../shell/styleState.js";
2
+ import { type ContainerQueryEvaluator } from "./resolution/containerQueryEvaluator.js";
2
3
  import type { MatchedRule, ResolvedProperty, TokenTable } from "../../css/model/index.js";
3
4
  export { getAvailableInteractionStates, resetSourceSiteMatchCache, sourceSiteMatchCacheSize } from "./resolution/ruleMatching.js";
4
5
  export { buildTokenTable, getTokenTable, getAvailableTokenEntriesForElement, getAvailableTokenCatalog } from "./tokenAvailability.js";
@@ -10,6 +11,8 @@ export { invalidateStyleResolutionCache } from "./resolution/cssomCollector.js";
10
11
  * subsequent resolution sweep's blocking time small.
11
12
  */
12
13
  export declare function prewarmCssomRuleSnapshot(doc: Document): void;
14
+ /** Prepares both cascades together so a selection refresh inserts one probe sheet. */
15
+ export declare function prefetchContainerQueries(elements: readonly HTMLElement[], state: InteractionState, evaluator: ContainerQueryEvaluator): void;
13
16
  /**
14
17
  * Package-private cascade fixture seam. This exercises selector, layer,
15
18
  * importance, and interaction-state coordination while still crossing the
@@ -17,17 +20,17 @@ export declare function prewarmCssomRuleSnapshot(doc: Document): void;
17
20
  * It is intentionally absent from the package exports.
18
21
  */
19
22
  export declare function resolveRuleFixture(el: HTMLElement, rules: MatchedRule[], tokenTable: TokenTable): ResolvedProperty[];
20
- export declare function getResolvedProperties(el: HTMLElement, tokenTable: TokenTable): ResolvedProperty[];
23
+ export declare function getResolvedProperties(el: HTMLElement, tokenTable: TokenTable, evaluator?: ContainerQueryEvaluator): ResolvedProperty[];
21
24
  /**
22
25
  * Resolve the authored cascade for a chosen interaction state without relying
23
26
  * on the pointer's current location. This is intentionally attribution-first:
24
27
  * values with no authored declaration still fall back to the browser's live
25
28
  * computed value in the field layer.
26
29
  */
27
- export declare function getResolvedPropertiesForState(el: HTMLElement, tokenTable: TokenTable, state: InteractionState): ResolvedProperty[];
30
+ export declare function getResolvedPropertiesForState(el: HTMLElement, tokenTable: TokenTable, state: InteractionState, evaluator?: ContainerQueryEvaluator): ResolvedProperty[];
28
31
  /**
29
32
  * Resolves the cascade with transient interaction selectors removed. Used when
30
33
  * an editor must stay linked to the stable token beneath a live :hover/:focus
31
34
  * paint. Memoized per (element, revisions, tokenTable).
32
35
  */
33
- export declare function getResolvedPropertiesStable(el: HTMLElement, tokenTable: TokenTable): ResolvedProperty[];
36
+ export declare function getResolvedPropertiesStable(el: HTMLElement, tokenTable: TokenTable, evaluator?: ContainerQueryEvaluator): ResolvedProperty[];