nudge-ui 0.2.3 → 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.
- package/dist/cli/agentCommand.js +4 -28
- package/dist/client.mjs +870 -212
- package/dist/hosts/next/wrapper.js +1 -7
- package/dist/hosts/projectBridge.js +3 -17
- package/dist/inspector/agent/McpConnectionDialog.js +7 -3
- package/dist/inspector/agent/activity.d.ts +1 -1
- package/dist/inspector/agent/activity.js +3 -2
- package/dist/inspector/agent/client.d.ts +2 -2
- package/dist/inspector/agent/client.js +28 -29
- package/dist/inspector/agent/connectionStatus.d.ts +3 -1
- package/dist/inspector/agent/connectionStatus.js +25 -0
- package/dist/inspector/agent/httpTransport.js +31 -147
- package/dist/inspector/agent/protocol.d.ts +1 -1
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +5 -7
- package/dist/inspector/agent/verification.js +67 -20
- package/dist/inspector/boot.d.ts +3 -0
- package/dist/inspector/boot.js +297 -0
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
- package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
- package/dist/inspector/canvas/CanvasCard.js +23 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
- package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
- package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
- package/dist/inspector/canvas/PageCard.d.ts +10 -0
- package/dist/inspector/canvas/PageCard.js +51 -0
- package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
- package/dist/inspector/canvas/SketchToolOptions.js +86 -0
- package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
- package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
- package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
- package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
- package/dist/inspector/canvas/assets/eraser.svg +28 -0
- package/dist/inspector/canvas/assets/pen.svg +25 -0
- package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
- package/dist/inspector/canvas/canvasFrame.js +10 -0
- package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
- package/dist/inspector/canvas/canvasGestures.js +0 -6
- package/dist/inspector/canvas/canvasStore.d.ts +6 -0
- package/dist/inspector/canvas/canvasStore.js +36 -1
- package/dist/inspector/canvas/frameConnection.d.ts +48 -0
- package/dist/inspector/canvas/frameConnection.js +73 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
- package/dist/inspector/canvas/frameProtocol.js +36 -0
- package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
- package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
- package/dist/inspector/canvas/pageSurface.d.ts +11 -0
- package/dist/inspector/canvas/pageSurface.js +34 -0
- package/dist/inspector/canvas/projection.d.ts +19 -9
- package/dist/inspector/canvas/projection.js +58 -31
- package/dist/inspector/canvas/rendererBootstrap.js +4 -1
- package/dist/inspector/canvas/rendererElementSelector.js +9 -1
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
- package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
- package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
- package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
- package/dist/inspector/canvas/rendererStylesheet.js +15 -0
- package/dist/inspector/canvas/sessionStore.d.ts +2 -2
- package/dist/inspector/canvas/sessionStore.js +12 -9
- package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
- package/dist/inspector/canvas/staleChangeDetector.js +10 -22
- package/dist/inspector/changes/codecs.d.ts +2 -0
- package/dist/inspector/changes/codecs.js +27 -3
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +18 -0
- package/dist/inspector/changes/editModel.js +16 -3
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +18 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- package/dist/inspector/comments/element.js +10 -3
- package/dist/inspector/comments/store.js +3 -2
- package/dist/inspector/comments/verification.js +1 -1
- package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
- package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
- package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
- package/dist/inspector/componentSemantics/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
- package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- package/dist/inspector/componentSemantics/types.d.ts +11 -0
- package/dist/inspector/drafts/draftStore.d.ts +109 -0
- package/dist/inspector/drafts/draftStore.js +451 -0
- package/dist/inspector/drafts/model.d.ts +9 -1
- package/dist/inspector/drafts/model.js +5 -1
- package/dist/inspector/drafts/store.d.ts +53 -15
- package/dist/inspector/drafts/store.js +143 -169
- package/dist/inspector/index.d.ts +2 -4
- package/dist/inspector/index.js +2 -264
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
- package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
- package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
- package/dist/inspector/inspection/bridge.d.ts +1 -1
- package/dist/inspector/inspection/bridge.js +2 -1
- package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
- package/dist/inspector/inspection/browserCssInspection.js +28 -6
- package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
- package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
- package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
- package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
- package/dist/inspector/iterations/capture.d.ts +2 -1
- package/dist/inspector/iterations/capture.js +2 -1
- package/dist/inspector/iterations/client.d.ts +2 -1
- package/dist/inspector/iterations/coordinator.d.ts +4 -3
- package/dist/inspector/iterations/coordinator.js +5 -3
- package/dist/inspector/iterations/prompt.js +9 -8
- package/dist/inspector/overlay/interactionStyles.js +17 -0
- package/dist/inspector/pageInspector.d.ts +42 -0
- package/dist/inspector/pageInspector.js +342 -0
- package/dist/inspector/projection/draftProjection.d.ts +10 -2
- package/dist/inspector/projection/draftProjection.js +9 -1
- package/dist/inspector/projection/managedStylesheet.js +24 -4
- package/dist/inspector/projection/renderedInstance.d.ts +15 -2
- package/dist/inspector/projection/renderedInstance.js +60 -7
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +16 -46
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +77 -24
- package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
- package/dist/inspector/prompt/clipboardHandoff.js +47 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +248 -148
- package/dist/inspector/prompt/promptDocument.d.ts +3 -0
- package/dist/inspector/prompt/promptDocument.js +8 -0
- package/dist/inspector/prompt/promptSettings.js +7 -5
- package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
- package/dist/inspector/runtime/inspectorStorage.js +42 -0
- package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
- package/dist/inspector/runtime/ownedStylesheets.js +72 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
- package/dist/inspector/runtime/runtimeConfig.js +10 -1
- package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
- package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
- package/dist/inspector/selection/editScope.d.ts +1 -0
- package/dist/inspector/selection/editScope.js +13 -2
- package/dist/inspector/selection/sourceSite.d.ts +12 -0
- package/dist/inspector/selection/sourceSite.js +28 -0
- package/dist/inspector/shell/AgentStatusButton.js +2 -2
- package/dist/inspector/shell/AppShell.js +6 -2
- package/dist/inspector/shell/ChangesLog.js +27 -9
- package/dist/inspector/shell/CopyPromptButton.js +74 -26
- package/dist/inspector/shell/EmptyState.js +3 -3
- package/dist/inspector/shell/InspectorShell.d.ts +0 -3
- package/dist/inspector/shell/InspectorShell.js +21 -13
- package/dist/inspector/shell/PeekOriginalButton.js +3 -3
- package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
- package/dist/inspector/shell/interactionStatePreview.js +112 -0
- package/dist/inspector/shell/originalPreview.d.ts +3 -2
- package/dist/inspector/shell/originalPreview.js +10 -37
- package/dist/inspector/shell/stateValue.js +2 -3
- package/dist/inspector/sketch/SketchEditor.js +5 -4
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
- package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
- package/dist/inspector/sketch/SketchOverlay.js +139 -263
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
- package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
- package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
- package/dist/inspector/sketch/SketchThumbnail.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
- package/dist/inspector/sketch/SketchWorkspace.js +69 -34
- package/dist/inspector/sketch/capture.js +19 -3
- package/dist/inspector/sketch/copySketch.d.ts +10 -0
- package/dist/inspector/sketch/copySketch.js +52 -0
- package/dist/inspector/sketch/drawingTools.d.ts +17 -0
- package/dist/inspector/sketch/drawingTools.js +47 -0
- package/dist/inspector/sketch/frameHeading.d.ts +9 -0
- package/dist/inspector/sketch/frameHeading.js +30 -0
- package/dist/inspector/sketch/freehand.js +3 -3
- package/dist/inspector/sketch/geometry.d.ts +6 -0
- package/dist/inspector/sketch/geometry.js +19 -0
- package/dist/inspector/sketch/history.d.ts +30 -0
- package/dist/inspector/sketch/history.js +58 -0
- package/dist/inspector/sketch/interaction.d.ts +1 -3
- package/dist/inspector/sketch/interaction.js +6 -9
- package/dist/inspector/sketch/model.d.ts +35 -1
- package/dist/inspector/sketch/model.js +116 -61
- package/dist/inspector/sketch/persistence.d.ts +14 -0
- package/dist/inspector/sketch/persistence.js +63 -7
- package/dist/inspector/sketch/raster.d.ts +6 -2
- package/dist/inspector/sketch/raster.js +43 -17
- package/dist/inspector/sketch/store.d.ts +1 -1
- package/dist/inspector/sketch/store.js +1 -1
- package/dist/inspector/sketch/videoFrames.d.ts +6 -0
- package/dist/inspector/sketch/videoFrames.js +81 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +3 -2
- package/dist/inspector/styleEditors/BorderEditor.js +30 -130
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +2 -2
- package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
- package/dist/inspector/styleEditors/borderModel.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.js +2 -3
- package/dist/inspector/tokens/TokenField.js +40 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +15 -3
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
- package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
- package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
- package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
- package/dist/inspector/tokens/resolution.d.ts +6 -3
- package/dist/inspector/tokens/resolution.js +91 -112
- package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
- package/dist/inspector/tokens/tokenEdits.js +1 -1
- package/dist/inspector/ui/InlineStyleWarning.js +2 -2
- package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
- package/dist/inspector/ui/InspectorStyle.js +26 -0
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +3 -3
- package/dist/inspector/ui/Toast.d.ts +14 -0
- package/dist/inspector/ui/Toast.js +30 -0
- package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
- package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
- package/dist/inspector/ui/customCursors.d.ts +5 -0
- package/dist/inspector/ui/customCursors.js +32 -6
- package/dist/inspector/ui/styles.js +12 -10
- package/dist/inspector/workspace/commands.js +1 -2
- package/dist/inspector/workspace/controller.js +1 -2
- package/dist/inspector/workspace/handoff.js +3 -2
- package/dist/inspector/workspace/sentItems.d.ts +16 -0
- package/dist/inspector/workspace/sentItems.js +33 -0
- package/dist/inspector/workspace/timeline.d.ts +24 -10
- package/dist/inspector/workspace/timeline.js +118 -96
- package/dist/internal/agent-protocol.d.ts +50 -13
- package/dist/internal/agent-protocol.js +145 -19
- package/dist/nudge-ui.mjs +81 -65
- package/dist/project/mcpPackage.d.ts +8 -0
- package/dist/project/mcpPackage.js +60 -0
- package/package.json +13 -3
- package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
- package/dist/inspector/canvas/measurementProjection.js +0 -16
- package/dist/inspector/changes/changesLog.d.ts +0 -47
- package/dist/inspector/changes/changesLog.js +0 -247
- package/dist/inspector/changes/draftChanges.d.ts +0 -56
- package/dist/inspector/changes/draftChanges.js +0 -300
- package/dist/inspector/conformance/projection.d.ts +0 -2
- package/dist/inspector/conformance/projection.js +0 -3
- package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
- package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
- package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
- package/dist/inspector/overlay/InspectorOverlay.js +0 -332
- package/dist/inspector/overlay/elementSelector.d.ts +0 -1
- package/dist/inspector/overlay/elementSelector.js +0 -111
- package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
- package/dist/inspector/sketch/SketchChanges.js +0 -72
- package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
- package/dist/inspector/sketch/SketchEntryButton.js +0 -11
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
- 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 {
|
|
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 =
|
|
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 "../
|
|
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({
|
|
@@ -104,11 +105,13 @@ function primaryFontFamily(value) {
|
|
|
104
105
|
return value.trim();
|
|
105
106
|
}
|
|
106
107
|
function rgbToHex(value) {
|
|
107
|
-
const
|
|
108
|
-
|
|
108
|
+
const rgb = value.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)/i);
|
|
109
|
+
const srgb = rgb ? null : value.match(/^color\(\s*srgb\s+([\d.e+-]+)\s+([\d.e+-]+)\s+([\d.e+-]+)/i);
|
|
110
|
+
const channels = rgb ? [rgb[1], rgb[2], rgb[3]].map(Number) : srgb ? [srgb[1], srgb[2], srgb[3]].map((part) => Number(part) * 255) : null;
|
|
111
|
+
if (!channels || channels.some((channel) => !Number.isFinite(channel)))
|
|
109
112
|
return null;
|
|
110
|
-
return `#${
|
|
111
|
-
.map((
|
|
113
|
+
return `#${channels
|
|
114
|
+
.map((channel) => Math.max(0, Math.min(255, Math.round(channel))).toString(16).padStart(2, "0"))
|
|
112
115
|
.join("")}`;
|
|
113
116
|
}
|
|
114
117
|
export function colorValueToHex(value) {
|
|
@@ -233,6 +236,10 @@ export function TokenValueField(props) {
|
|
|
233
236
|
return Array.from(new Set(suggestions));
|
|
234
237
|
}, [suggestions]);
|
|
235
238
|
function handleSuggestionSelect(value) {
|
|
239
|
+
if (value === DETACH_VALUE) {
|
|
240
|
+
handleDelink();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
236
243
|
const rawSuggestion = decodeRawSuggestion(value);
|
|
237
244
|
if (rawSuggestion !== null) {
|
|
238
245
|
selectedFromPopover.current = true;
|
|
@@ -465,9 +472,20 @@ export function TokenValueField(props) {
|
|
|
465
472
|
if (activeToken) {
|
|
466
473
|
const chipValue = chipVariant === "small" ? stripCssUnit(activeToken.value) : activeToken.name;
|
|
467
474
|
const embedColorSwatch = isColor && chipVariant !== "small";
|
|
468
|
-
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",
|
|
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) => {
|
|
469
487
|
handleSuggestionSelect(value);
|
|
470
|
-
} }), _jsx(TokenChip.Action, { children: _jsx(Tooltip, { stableTrigger: true, children: _jsx(IconButton, { variant: "quiet", size: "compact", style: { borderRadius: "var(--radius-4)" }, label: "
|
|
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 })] }));
|
|
471
489
|
}
|
|
472
490
|
const showPopover = isFocused && (filteredTokens.length > 0 || availableSuggestions.length > 0);
|
|
473
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: () => {
|
|
@@ -604,6 +622,21 @@ function tokenSuggestion(entry, groups) {
|
|
|
604
622
|
};
|
|
605
623
|
}
|
|
606
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
|
+
}
|
|
607
640
|
function rawSuggestion(value) {
|
|
608
641
|
return {
|
|
609
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
|
|
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/
|
|
2
|
+
import type { ElementChangeRecord } from "../changes/types.js";
|
|
3
3
|
import { type EditTarget } from "../selection/editTarget.js";
|
|
4
|
-
export type { ChangeRecord } from "../changes/
|
|
5
|
-
export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../
|
|
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
|
|
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 {
|
|
@@ -42,9 +44,17 @@ function sourceFields(el) {
|
|
|
42
44
|
file: parsed.file,
|
|
43
45
|
line: parsed.line,
|
|
44
46
|
column: parsed.column,
|
|
47
|
+
sourceEvidence: {
|
|
48
|
+
tagName: normalizeRuntimeTag(el.tagName),
|
|
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),
|
|
54
|
+
},
|
|
45
55
|
};
|
|
46
56
|
}
|
|
47
|
-
export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../
|
|
57
|
+
export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../drafts/store.js";
|
|
48
58
|
/** Returns whether a style write can address every element in the target safely. */
|
|
49
59
|
export function canEditStyles(target) {
|
|
50
60
|
return canPlanBatchEditScopes(targetElements(target));
|
|
@@ -87,6 +97,7 @@ export function swapTokens(target, changes) {
|
|
|
87
97
|
newToken: change.newToken,
|
|
88
98
|
source: { file: source.file, line: source.line, component: cid },
|
|
89
99
|
runtimeEvidence: source.runtimeEvidence,
|
|
100
|
+
sourceEvidence: source.sourceEvidence,
|
|
90
101
|
state,
|
|
91
102
|
...scopePlan.get(el),
|
|
92
103
|
};
|
|
@@ -155,6 +166,7 @@ function buildStyleRecord(el, scopePlan, { property, value, oldRawValue, metadat
|
|
|
155
166
|
oldRawValue: (oldRawValue ?? getStateStyleValue(el, property)) || undefined,
|
|
156
167
|
source: { file: source.file, line: source.line, component: cid },
|
|
157
168
|
runtimeEvidence: source.runtimeEvidence,
|
|
169
|
+
sourceEvidence: source.sourceEvidence,
|
|
158
170
|
state,
|
|
159
171
|
...scopePlan.get(el),
|
|
160
172
|
};
|
|
@@ -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
|
|
382
|
-
//
|
|
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
|
|
386
|
-
|
|
387
|
-
|
|
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
|
|
445
|
-
specificity: computeSpecificityCore(
|
|
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,
|
|
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
|
-
|
|
55
|
-
|
|
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(
|
|
161
|
+
specificity: specificityForBranch(entry.rule, branch),
|
|
143
162
|
};
|
|
144
163
|
}
|
|
145
164
|
/**
|