nudge-ui 0.2.2 → 0.2.4

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 (244) hide show
  1. package/README.md +164 -46
  2. package/dist/client.mjs +260 -61
  3. package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
  4. package/dist/css/value-semantics/colorSemantics.js +17 -0
  5. package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
  6. package/dist/hosts/astro/clientTransport.js +38 -32
  7. package/dist/hosts/astro/integration.js +5 -1
  8. package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
  9. package/dist/hosts/astro/markdownIdentity.js +81 -0
  10. package/dist/hosts/next/manifest.js +1 -1
  11. package/dist/hosts/next/sidecar.js +5 -1
  12. package/dist/hosts/static/manifest.js +1 -1
  13. package/dist/hosts/static/server.js +3 -0
  14. package/dist/hosts/vite/vite.d.ts +3 -0
  15. package/dist/hosts/vite/vite.js +24 -4
  16. package/dist/inspector/agent/McpConnectionDialog.js +13 -19
  17. package/dist/inspector/agent/activity.d.ts +18 -0
  18. package/dist/inspector/agent/activity.js +70 -0
  19. package/dist/inspector/agent/client.d.ts +2 -0
  20. package/dist/inspector/agent/client.js +29 -1
  21. package/dist/inspector/agent/connectionStatus.js +1 -1
  22. package/dist/inspector/agent/httpTransport.js +9 -2
  23. package/dist/inspector/agent/protocol.d.ts +6 -3
  24. package/dist/inspector/agent/protocol.js +1 -1
  25. package/dist/inspector/agent/verification.d.ts +15 -7
  26. package/dist/inspector/agent/verification.js +113 -41
  27. package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
  28. package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
  29. package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
  30. package/dist/inspector/canvas/CanvasCard.js +135 -38
  31. package/dist/inspector/canvas/CanvasElementOverlay.js +166 -16
  32. package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
  33. package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
  34. package/dist/inspector/canvas/CanvasToolbar.js +8 -4
  35. package/dist/inspector/canvas/CanvasWorkspace.js +270 -122
  36. package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
  37. package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
  38. package/dist/inspector/canvas/agentPresentation.js +64 -52
  39. package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
  40. package/dist/inspector/canvas/canvasGestures.js +1 -1
  41. package/dist/inspector/canvas/canvasStore.d.ts +46 -82
  42. package/dist/inspector/canvas/canvasStore.js +251 -284
  43. package/dist/inspector/canvas/frameContent.d.ts +21 -0
  44. package/dist/inspector/canvas/frameContent.js +44 -0
  45. package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
  46. package/dist/inspector/canvas/frameProtocol.js +3 -3
  47. package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
  48. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  49. package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
  50. package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
  51. package/dist/inspector/canvas/layout.d.ts +52 -0
  52. package/dist/inspector/canvas/layout.js +106 -0
  53. package/dist/inspector/canvas/projection.d.ts +15 -5
  54. package/dist/inspector/canvas/projection.js +69 -13
  55. package/dist/inspector/canvas/rendererBootstrap.js +32 -4
  56. package/dist/inspector/canvas/rendererElementSelector.js +47 -6
  57. package/dist/inspector/canvas/rendererStylesheet.js +2 -2
  58. package/dist/inspector/canvas/resizeHandleScale.js +1 -1
  59. package/dist/inspector/canvas/sessionStore.d.ts +11 -26
  60. package/dist/inspector/canvas/sessionStore.js +43 -183
  61. package/dist/inspector/canvas/staleChangeDetector.js +9 -3
  62. package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
  63. package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
  64. package/dist/inspector/canvas/viewStore.d.ts +38 -0
  65. package/dist/inspector/canvas/viewStore.js +104 -0
  66. package/dist/inspector/canvas/wheelPan.d.ts +8 -0
  67. package/dist/inspector/canvas/wheelPan.js +8 -0
  68. package/dist/inspector/changes/changesLog.d.ts +9 -6
  69. package/dist/inspector/changes/changesLog.js +48 -32
  70. package/dist/inspector/changes/codecs.d.ts +1 -0
  71. package/dist/inspector/changes/codecs.js +10 -1
  72. package/dist/inspector/changes/draftChanges.d.ts +58 -0
  73. package/dist/inspector/changes/draftChanges.js +304 -0
  74. package/dist/inspector/changes/editModel.d.ts +10 -0
  75. package/dist/inspector/changes/editModel.js +9 -2
  76. package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
  77. package/dist/inspector/changes/previewDiagnostics.js +11 -25
  78. package/dist/inspector/changes/types.d.ts +7 -0
  79. package/dist/inspector/clientManifest.d.ts +1 -1
  80. package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
  81. package/dist/inspector/comments/CommentsOverlay.js +250 -0
  82. package/dist/inspector/comments/element.d.ts +6 -0
  83. package/dist/inspector/comments/element.js +54 -0
  84. package/dist/inspector/comments/store.d.ts +29 -0
  85. package/dist/inspector/comments/store.js +121 -0
  86. package/dist/inspector/comments/verification.d.ts +5 -0
  87. package/dist/inspector/comments/verification.js +52 -0
  88. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  89. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  90. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  91. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  92. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  93. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  94. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  95. package/dist/inspector/drafts/model.d.ts +35 -0
  96. package/dist/inspector/drafts/model.js +45 -0
  97. package/dist/inspector/drafts/store.d.ts +28 -0
  98. package/dist/inspector/drafts/store.js +231 -0
  99. package/dist/inspector/index.js +20 -27
  100. package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
  101. package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
  102. package/dist/inspector/iterations/capture.d.ts +2 -0
  103. package/dist/inspector/iterations/capture.js +174 -0
  104. package/dist/inspector/iterations/client.d.ts +7 -0
  105. package/dist/inspector/iterations/client.js +52 -0
  106. package/dist/inspector/iterations/coordinator.d.ts +15 -0
  107. package/dist/inspector/iterations/coordinator.js +130 -0
  108. package/dist/inspector/iterations/prompt.d.ts +3 -0
  109. package/dist/inspector/iterations/prompt.js +26 -0
  110. package/dist/inspector/overlay/DomNavigator.js +21 -15
  111. package/dist/inspector/overlay/interactionStyles.js +3 -1
  112. package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
  113. package/dist/inspector/overlay/spacingGestures.js +82 -5
  114. package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
  115. package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
  116. package/dist/inspector/projection/renderedInstance.d.ts +17 -3
  117. package/dist/inspector/projection/renderedInstance.js +62 -11
  118. package/dist/inspector/projection/structuralProjection.d.ts +2 -1
  119. package/dist/inspector/projection/structuralProjection.js +21 -17
  120. package/dist/inspector/projection/textProjection.d.ts +2 -1
  121. package/dist/inspector/projection/textProjection.js +64 -15
  122. package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
  123. package/dist/inspector/prompt/clipboardHandoff.d.ts +15 -18
  124. package/dist/inspector/prompt/clipboardHandoff.js +97 -143
  125. package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
  126. package/dist/inspector/prompt/generatePrompt.js +22 -17
  127. package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
  128. package/dist/inspector/runtime/runtimeConfig.js +24 -0
  129. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  130. package/dist/inspector/selection/sourceSite.js +28 -0
  131. package/dist/inspector/settings/SettingsDialog.js +21 -8
  132. package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
  133. package/dist/inspector/shell/AgentStatusButton.js +36 -0
  134. package/dist/inspector/shell/AppShell.js +4 -2
  135. package/dist/inspector/shell/ChangesLog.js +46 -26
  136. package/dist/inspector/shell/CopyPromptButton.js +184 -93
  137. package/dist/inspector/shell/EmptyState.js +9 -8
  138. package/dist/inspector/shell/InspectorShell.js +13 -21
  139. package/dist/inspector/shell/PeekOriginalButton.js +24 -23
  140. package/dist/inspector/shell/originalPreview.js +5 -5
  141. package/dist/inspector/shell/shortcuts.d.ts +3 -0
  142. package/dist/inspector/shell/shortcuts.js +11 -0
  143. package/dist/inspector/sketch/SketchEditor.js +14 -14
  144. package/dist/inspector/sketch/SketchEntryButton.js +1 -1
  145. package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
  146. package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
  147. package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
  148. package/dist/inspector/sketch/SketchOverlay.js +7 -7
  149. package/dist/inspector/sketch/SketchWorkspace.js +18 -12
  150. package/dist/inspector/sketch/interaction.d.ts +1 -0
  151. package/dist/inspector/sketch/interaction.js +8 -4
  152. package/dist/inspector/sketch/model.d.ts +5 -0
  153. package/dist/inspector/sketch/model.js +6 -2
  154. package/dist/inspector/sketch/raster.d.ts +1 -2
  155. package/dist/inspector/sketch/raster.js +68 -4
  156. package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
  157. package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
  158. package/dist/inspector/styleEditors/BorderEditor.js +49 -78
  159. package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
  160. package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
  161. package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
  162. package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
  163. package/dist/inspector/styleEditors/ColorPicker.js +10 -4
  164. package/dist/inspector/styleEditors/GapField.js +3 -5
  165. package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
  166. package/dist/inspector/styleEditors/GridChildSection.js +18 -9
  167. package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
  168. package/dist/inspector/styleEditors/GridPicker.js +8 -2
  169. package/dist/inspector/styleEditors/GridSection.js +1 -1
  170. package/dist/inspector/styleEditors/GridValueField.js +19 -14
  171. package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
  172. package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
  173. package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
  174. package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
  175. package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
  176. package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
  177. package/dist/inspector/styleEditors/LayoutSection.js +32 -11
  178. package/dist/inspector/styleEditors/SpacingBox.js +7 -5
  179. package/dist/inspector/styleEditors/Typography.js +11 -3
  180. package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
  181. package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
  182. package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
  183. package/dist/inspector/styleEditors/gradientValue.js +114 -0
  184. package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
  185. package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
  186. package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
  187. package/dist/inspector/styleEditors/layoutValue.js +0 -42
  188. package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
  189. package/dist/inspector/styleEditors/nudgeValue.js +36 -8
  190. package/dist/inspector/tokens/TokenField.d.ts +7 -0
  191. package/dist/inspector/tokens/TokenField.js +35 -25
  192. package/dist/inspector/tokens/TokenLabel.js +5 -14
  193. package/dist/inspector/tokens/editActions.d.ts +1 -1
  194. package/dist/inspector/tokens/editActions.js +7 -1
  195. package/dist/inspector/ui/AtRuleContext.js +4 -6
  196. package/dist/inspector/ui/Button.d.ts +6 -2
  197. package/dist/inspector/ui/Button.js +4 -3
  198. package/dist/inspector/ui/ControlSurface.d.ts +2 -2
  199. package/dist/inspector/ui/ControlSurface.js +4 -3
  200. package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
  201. package/dist/inspector/ui/InlineStyleWarning.js +13 -0
  202. package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
  203. package/dist/inspector/ui/InspectorPopover.js +4 -5
  204. package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
  205. package/dist/inspector/ui/PopoverListbox.js +8 -7
  206. package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
  207. package/dist/inspector/ui/SegmentedControl.js +2 -2
  208. package/dist/inspector/ui/Select.d.ts +6 -1
  209. package/dist/inspector/ui/Select.js +7 -11
  210. package/dist/inspector/ui/SideValuesField.js +1 -1
  211. package/dist/inspector/ui/Tooltip.d.ts +26 -0
  212. package/dist/inspector/ui/Tooltip.js +32 -0
  213. package/dist/inspector/ui/classNames.d.ts +2 -0
  214. package/dist/inspector/ui/classNames.js +4 -0
  215. package/dist/inspector/ui/customCursors.d.ts +23 -0
  216. package/dist/inspector/ui/customCursors.js +122 -0
  217. package/dist/inspector/ui/dragCursor.d.ts +0 -2
  218. package/dist/inspector/ui/dragCursor.js +2 -10
  219. package/dist/inspector/ui/portalContainer.d.ts +2 -0
  220. package/dist/inspector/ui/portalContainer.js +6 -0
  221. package/dist/inspector/ui/styles.js +29 -21
  222. package/dist/inspector/workspace/commands.d.ts +9 -0
  223. package/dist/inspector/workspace/commands.js +82 -0
  224. package/dist/inspector/workspace/controller.d.ts +4 -0
  225. package/dist/inspector/workspace/controller.js +66 -0
  226. package/dist/inspector/workspace/handoff.d.ts +42 -0
  227. package/dist/inspector/workspace/handoff.js +115 -0
  228. package/dist/inspector/workspace/sentItems.d.ts +19 -0
  229. package/dist/inspector/workspace/sentItems.js +51 -0
  230. package/dist/inspector/workspace/timeline.d.ts +30 -0
  231. package/dist/inspector/workspace/timeline.js +108 -0
  232. package/dist/internal/agent-protocol.d.ts +19 -2
  233. package/dist/internal/agent-protocol.js +13 -0
  234. package/dist/nudge-ui.mjs +558 -311
  235. package/dist/project/artifacts.d.ts +13 -0
  236. package/dist/project/artifacts.js +253 -0
  237. package/dist/transport/artifacts.d.ts +8 -0
  238. package/dist/transport/artifacts.js +9 -0
  239. package/dist/transport/editor.js +3 -1
  240. package/dist/transport/routes.d.ts +2 -0
  241. package/dist/transport/routes.js +2 -0
  242. package/package.json +7 -3
  243. package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
  244. package/dist/inspector/changes/workspaceChanges.js +0 -265
@@ -0,0 +1,51 @@
1
+ import { splitTopLevel } from "../../css/value-semantics/cssSyntax.js";
2
+ import { isEmptyColorValue } from "./ColorPicker.js";
3
+ export const BACKGROUND_BLEND_MODES = ["normal", "multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn", "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"]
4
+ .map((value) => ({ value, label: value.replace(/-/g, " ").replace(/^./, (letter) => letter.toUpperCase()) }));
5
+ export function backgroundMode(value) {
6
+ if (!value || value === "none")
7
+ return "solid";
8
+ return /(?:repeating-)?(?:linear|radial|conic)-gradient\(/.test(value) ? "gradient" : "image";
9
+ }
10
+ export function newBackgroundLayer() {
11
+ return { mode: "solid", baseColor: true, value: "#ffffff", blend: "normal", size: "auto", repeat: "repeat", position: "0% 0%" };
12
+ }
13
+ /** CSS repeats shorter background property lists to match the image count. */
14
+ export function readBackgroundLayers(read) {
15
+ const image = read("background-image", "none");
16
+ const images = image === "none" ? [] : splitTopLevel(image, ",").map((value) => value.trim());
17
+ const listValue = (property, fallback, index) => {
18
+ const values = splitTopLevel(read(property, fallback), ",");
19
+ return values[index % values.length]?.trim() || fallback;
20
+ };
21
+ const layers = images.map((value, index) => {
22
+ const flat = /^linear-gradient\((.*)\)$/s.exec(value);
23
+ const colors = flat ? splitTopLevel(flat[1], ",").map((color) => color.trim()) : [];
24
+ const solid = colors.length === 2 && colors[0] === colors[1];
25
+ return {
26
+ mode: solid ? "solid" : backgroundMode(value),
27
+ baseColor: false,
28
+ value: solid ? colors[0] : value,
29
+ blend: listValue("background-blend-mode", "normal", index),
30
+ size: listValue("background-size", "auto", index),
31
+ repeat: listValue("background-repeat", "repeat", index),
32
+ position: listValue("background-position", "0% 0%", index),
33
+ };
34
+ });
35
+ const color = read("background-color", "transparent");
36
+ if (!isEmptyColorValue(color))
37
+ layers.push({ ...newBackgroundLayer(), value: color });
38
+ return layers;
39
+ }
40
+ export function backgroundDeclarations(layers) {
41
+ const base = layers.at(-1)?.mode === "solid" && layers.at(-1)?.baseColor ? layers.at(-1) : undefined;
42
+ const images = base ? layers.slice(0, -1) : layers;
43
+ return [
44
+ { property: "background-color", value: base?.value ?? "transparent" },
45
+ { property: "background-image", value: images.map((layer) => layer.mode === "solid" ? `linear-gradient(${layer.value}, ${layer.value})` : layer.value).join(", ") || "none" },
46
+ ...["blend", "size", "repeat", "position"].map((field) => ({
47
+ property: field === "blend" ? "background-blend-mode" : `background-${field}`,
48
+ value: images.map((layer) => layer[field]).join(", ") || newBackgroundLayer()[field],
49
+ })),
50
+ ];
51
+ }
@@ -0,0 +1,17 @@
1
+ export type GradientType = "linear" | "radial" | "conic";
2
+ export interface GradientStop {
3
+ color: string;
4
+ position: number;
5
+ }
6
+ export interface GradientValue {
7
+ type: GradientType;
8
+ repeating: boolean;
9
+ angle: number;
10
+ /** Preserve the computed shape, origin, and interpolation method. */
11
+ prelude: string;
12
+ stops: GradientStop[];
13
+ }
14
+ export declare function defaultGradient(): GradientValue;
15
+ /** Parse editable computed gradients. Leave unsupported syntax in the CSS field. */
16
+ export declare function parseGradient(value: string): GradientValue | null;
17
+ export declare function serializeGradient(gradient: GradientValue): string;
@@ -0,0 +1,114 @@
1
+ import { splitTopLevel, splitTopLevelWhitespace } from "../../css/value-semantics/cssSyntax.js";
2
+ export function defaultGradient() {
3
+ return { type: "linear", repeating: false, angle: 90, prelude: "", stops: [
4
+ { color: "#dddddd", position: 0 }, { color: "#a4a4a4", position: 100 },
5
+ ] };
6
+ }
7
+ function angleDegrees(value) {
8
+ const match = /^([+-]?(?:\d*\.)?\d+)(deg|grad|rad|turn)$/.exec(value);
9
+ if (!match)
10
+ return null;
11
+ const number = Number(match[1]);
12
+ switch (match[2]) {
13
+ case "grad": return number * 0.9;
14
+ case "rad": return number * 180 / Math.PI;
15
+ case "turn": return number * 360;
16
+ default: return number;
17
+ }
18
+ }
19
+ /** Parse editable computed gradients. Leave unsupported syntax in the CSS field. */
20
+ export function parseGradient(value) {
21
+ if (splitTopLevel(value, ",").length !== 1)
22
+ return null;
23
+ const match = /^(repeating-)?(linear|radial|conic)-gradient\((.*)\)$/s.exec(value.trim());
24
+ if (!match)
25
+ return null;
26
+ const type = match[2];
27
+ const parts = splitTopLevel(match[3], ",").map((part) => part.trim());
28
+ let prelude = "";
29
+ let angle = type === "linear" ? 180 : 0;
30
+ const first = parts[0] ?? "";
31
+ if (/^(?:to\s|[+-]?(?:\d*\.)?\d+(?:deg|grad|rad|turn)\b|circle\b|ellipse\b|closest-|farthest-|at\s|from\s|in\s)/.test(first)
32
+ || (type === "radial" && /\b(?:px|%)\b/.test(first))) {
33
+ prelude = parts.shift();
34
+ if (type === "linear") {
35
+ const direction = /^(to\s+(?:(?:left|right|top|bottom)(?:\s+|$)){1,2})/.exec(prelude)?.[0]?.trim();
36
+ if (direction) {
37
+ const directions = { top: 0, "top right": 45, "right top": 45, right: 90, "bottom right": 135, "right bottom": 135, bottom: 180, "bottom left": 225, "left bottom": 225, left: 270, "top left": 315, "left top": 315 };
38
+ // Corner directions depend on the element's aspect ratio. Keep them as CSS.
39
+ if (direction.slice(3).includes(" "))
40
+ return null;
41
+ angle = directions[direction.slice(3)] ?? 180;
42
+ prelude = prelude.slice(direction.length).trim();
43
+ }
44
+ else if (!prelude.startsWith("in ")) {
45
+ const rawAngle = splitTopLevelWhitespace(prelude)[0];
46
+ const parsed = angleDegrees(rawAngle);
47
+ if (parsed === null)
48
+ return null;
49
+ angle = parsed;
50
+ prelude = prelude.slice(rawAngle.length).trim();
51
+ }
52
+ }
53
+ else if (type === "conic" && prelude.startsWith("from ")) {
54
+ const rawAngle = splitTopLevelWhitespace(prelude)[1];
55
+ const parsed = angleDegrees(rawAngle);
56
+ if (parsed === null)
57
+ return null;
58
+ angle = parsed;
59
+ prelude = prelude.slice(5 + rawAngle.length).trim();
60
+ }
61
+ }
62
+ const stops = [];
63
+ for (const part of parts) {
64
+ const words = splitTopLevelWhitespace(part);
65
+ const positions = [];
66
+ while (words.length > 1) {
67
+ const last = words[words.length - 1];
68
+ const percent = /^([+-]?(?:\d*\.)?\d+)%$/.exec(last);
69
+ const degrees = type === "conic" ? angleDegrees(last) : null;
70
+ if (!percent && degrees === null && last !== "0")
71
+ break;
72
+ positions.unshift(percent ? Number(percent[1]) : degrees !== null ? degrees / 3.6 : 0);
73
+ words.pop();
74
+ }
75
+ const color = words.join(" ");
76
+ // Length stops and color hints cannot be represented by a percentage slider.
77
+ if (!color || words.length !== 1 || /^([+-]?\d|calc\(|var\()/i.test(color) || positions.length > 2)
78
+ return null;
79
+ if (positions.length)
80
+ positions.forEach((position) => stops.push({ color, position }));
81
+ else
82
+ stops.push({ color, position: null });
83
+ }
84
+ if (stops.length < 2)
85
+ return null;
86
+ stops[0].position ??= 0;
87
+ stops[stops.length - 1].position ??= 100;
88
+ let previous = stops[0].position;
89
+ for (const stop of stops) {
90
+ if (stop.position !== null) {
91
+ stop.position = Math.max(previous, stop.position);
92
+ previous = stop.position;
93
+ }
94
+ }
95
+ let start = 0;
96
+ while (start < stops.length - 1) {
97
+ let end = start + 1;
98
+ while (stops[end].position === null)
99
+ end++;
100
+ for (let index = start + 1; index < end; index++) {
101
+ stops[index].position = stops[start].position + (stops[end].position - stops[start].position) * (index - start) / (end - start);
102
+ }
103
+ start = end;
104
+ }
105
+ return { type, repeating: Boolean(match[1]), angle, prelude, stops: stops };
106
+ }
107
+ export function serializeGradient(gradient) {
108
+ const prefix = gradient.type === "linear" ? `${gradient.angle}deg`
109
+ : gradient.type === "conic" ? `from ${gradient.angle}deg` : "";
110
+ const prelude = [prefix, gradient.prelude].filter(Boolean).join(" ");
111
+ const stops = [...gradient.stops].sort((a, b) => a.position - b.position)
112
+ .map((stop) => `${stop.color} ${Number(stop.position.toFixed(3))}%`).join(", ");
113
+ return `${gradient.repeating ? "repeating-" : ""}${gradient.type}-gradient(${prelude ? `${prelude}, ` : ""}${stops})`;
114
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the inline-authored source when `el` carries the property — or a
3
+ * shorthand that sets it — in its `style` attribute. Managed previews can
4
+ * never beat inline styles in the cascade, so a non-null result means the
5
+ * field must present as blocked instead of accepting edits that silently
6
+ * revert (see `verifyPreview`'s "inline-style" conflict).
7
+ */
8
+ export declare function inlineAuthoredValue(el: HTMLElement, property: string): string | null;
9
+ /** First inline-authored source that blocks any of `properties` on any of `elements`. */
10
+ export declare function inlineBlockedBy(elements: readonly HTMLElement[], ...properties: readonly string[]): string | null;
@@ -0,0 +1,114 @@
1
+ import { isLayoutPreview } from "./layoutPreviewState.js";
2
+ /**
3
+ * Shorthands that also author the keyed longhand when present in a `style`
4
+ * attribute. Managed-stylesheet previews lose the cascade to any of these,
5
+ * so fields must treat the longhand as inline-blocked either way.
6
+ */
7
+ const SHORTHAND_SOURCES = {
8
+ "font-family": ["font"],
9
+ "font-style": ["font"],
10
+ "font-weight": ["font"],
11
+ "font-size": ["font"],
12
+ "line-height": ["font"],
13
+ "background-color": ["background"],
14
+ "background-image": ["background"],
15
+ "row-gap": ["gap"],
16
+ "column-gap": ["gap"],
17
+ "flex-grow": ["flex"],
18
+ "flex-shrink": ["flex"],
19
+ "flex-basis": ["flex"],
20
+ "flex-direction": ["flex-flow"],
21
+ "flex-wrap": ["flex-flow"],
22
+ "align-content": ["place-content"],
23
+ "justify-content": ["place-content"],
24
+ "align-items": ["place-items"],
25
+ "justify-items": ["place-items"],
26
+ "align-self": ["place-self"],
27
+ "justify-self": ["place-self"],
28
+ "border-width": ["border"],
29
+ "border-style": ["border"],
30
+ "border-color": ["border"],
31
+ "border-top-left-radius": ["border-radius"],
32
+ "border-top-right-radius": ["border-radius"],
33
+ "border-bottom-right-radius": ["border-radius"],
34
+ "border-bottom-left-radius": ["border-radius"],
35
+ "border-top-width": ["border-top", "border-width", "border"],
36
+ "border-right-width": ["border-right", "border-width", "border"],
37
+ "border-bottom-width": ["border-bottom", "border-width", "border"],
38
+ "border-left-width": ["border-left", "border-width", "border"],
39
+ "border-top-style": ["border-top", "border-style", "border"],
40
+ "border-right-style": ["border-right", "border-style", "border"],
41
+ "border-bottom-style": ["border-bottom", "border-style", "border"],
42
+ "border-left-style": ["border-left", "border-style", "border"],
43
+ "border-top-color": ["border-top", "border-color", "border"],
44
+ "border-right-color": ["border-right", "border-color", "border"],
45
+ "border-bottom-color": ["border-bottom", "border-color", "border"],
46
+ "border-left-color": ["border-left", "border-color", "border"],
47
+ "margin-top": ["margin", "margin-block", "margin-block-start", "margin-block-end"],
48
+ "margin-right": ["margin", "margin-inline", "margin-inline-start", "margin-inline-end"],
49
+ "margin-bottom": ["margin", "margin-block", "margin-block-start", "margin-block-end"],
50
+ "margin-left": ["margin", "margin-inline", "margin-inline-start", "margin-inline-end"],
51
+ "padding-top": ["padding", "padding-block", "padding-block-start", "padding-block-end"],
52
+ "padding-right": ["padding", "padding-inline", "padding-inline-start", "padding-inline-end"],
53
+ "padding-bottom": ["padding", "padding-block", "padding-block-start", "padding-block-end"],
54
+ "padding-left": ["padding", "padding-inline", "padding-inline-start", "padding-inline-end"],
55
+ top: ["inset", "inset-block", "inset-block-start", "inset-block-end"],
56
+ right: ["inset", "inset-inline", "inset-inline-start", "inset-inline-end"],
57
+ bottom: ["inset", "inset-block", "inset-block-start", "inset-block-end"],
58
+ left: ["inset", "inset-inline", "inset-inline-start", "inset-inline-end"],
59
+ "grid-template-columns": ["grid-template", "grid"],
60
+ "grid-template-rows": ["grid-template", "grid"],
61
+ "grid-template-areas": ["grid-template", "grid"],
62
+ "grid-auto-columns": ["grid-auto", "grid"],
63
+ "grid-auto-rows": ["grid-auto", "grid"],
64
+ "grid-auto-flow": ["grid-auto", "grid"],
65
+ "grid-row": ["grid-area", "grid"],
66
+ "grid-column": ["grid-area", "grid"],
67
+ "grid-row-start": ["grid-row", "grid-area", "grid"],
68
+ "grid-row-end": ["grid-row", "grid-area", "grid"],
69
+ "grid-column-start": ["grid-column", "grid-area", "grid"],
70
+ "grid-column-end": ["grid-column", "grid-area", "grid"],
71
+ };
72
+ /**
73
+ * Longhands edited together by one linked field. The field is blocked when
74
+ * any of them is inline-authored, even if the shorthand itself is not.
75
+ */
76
+ const LINKED_LONGHANDS = {
77
+ "border-width": ["border-top-width", "border-right-width", "border-bottom-width", "border-left-width"],
78
+ "border-style": ["border-top-style", "border-right-style", "border-bottom-style", "border-left-style"],
79
+ "border-color": ["border-top-color", "border-right-color", "border-bottom-color", "border-left-color"],
80
+ "border-radius": ["border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius"],
81
+ };
82
+ /**
83
+ * Returns the inline-authored source when `el` carries the property — or a
84
+ * shorthand that sets it — in its `style` attribute. Managed previews can
85
+ * never beat inline styles in the cascade, so a non-null result means the
86
+ * field must present as blocked instead of accepting edits that silently
87
+ * revert (see `verifyPreview`'s "inline-style" conflict).
88
+ */
89
+ export function inlineAuthoredValue(el, property) {
90
+ if (isLayoutPreview(el, property))
91
+ return null;
92
+ for (const source of [property, ...(SHORTHAND_SOURCES[property] ?? [])]) {
93
+ const value = el.style.getPropertyValue(source).trim();
94
+ if (value)
95
+ return `${source}: ${value}`;
96
+ }
97
+ for (const longhand of LINKED_LONGHANDS[property] ?? []) {
98
+ const value = inlineAuthoredValue(el, longhand);
99
+ if (value)
100
+ return value;
101
+ }
102
+ return null;
103
+ }
104
+ /** First inline-authored source that blocks any of `properties` on any of `elements`. */
105
+ export function inlineBlockedBy(elements, ...properties) {
106
+ for (const property of properties) {
107
+ for (const el of elements) {
108
+ const value = inlineAuthoredValue(el, property);
109
+ if (value)
110
+ return value;
111
+ }
112
+ }
113
+ return null;
114
+ }
@@ -4,14 +4,6 @@
4
4
  */
5
5
  export declare function meaningfulLayoutValue(el: HTMLElement, property: string): string;
6
6
  export declare function readAuthoredStyleValue(el: HTMLElement, property: string): string | null;
7
- /**
8
- * Returns the inline-authored source when `el` carries the property — or a
9
- * shorthand that sets it — in its `style` attribute. Managed previews can
10
- * never beat inline styles in the cascade, so a non-null result means the
11
- * field must present as blocked instead of accepting edits that silently
12
- * revert (see `verifyPreview`'s "inline-style" conflict).
13
- */
14
- export declare function inlineAuthoredValue(el: HTMLElement, property: string): string | null;
15
7
  export interface LayoutValue {
16
8
  property: string;
17
9
  authored: string | null;
@@ -2,7 +2,6 @@ import { getActiveStyleState } from "../shell/styleState.js";
2
2
  import { getStateStyleValue } from "../shell/stateValue.js";
3
3
  import { getElementComputedStyle } from "../runtime/domRealm.js";
4
4
  import { getBrowserCssInspection } from "../inspection/browserCssInspectionRegistry.js";
5
- import { isLayoutPreview } from "./layoutPreviewState.js";
6
5
  const DEFAULT_LAYOUT_VALUES = {
7
6
  width: "auto",
8
7
  height: "auto",
@@ -48,47 +47,6 @@ export function readAuthoredStyleValue(el, property) {
48
47
  const inline = el.style.getPropertyValue(property).trim();
49
48
  return inline || null;
50
49
  }
51
- /**
52
- * Shorthands that also author the keyed longhand when present in a `style`
53
- * attribute. Managed-stylesheet previews lose the cascade to any of these,
54
- * so fields must treat the longhand as inline-blocked either way.
55
- */
56
- const INLINE_SHORTHAND_SOURCES = {
57
- "row-gap": ["gap"],
58
- "column-gap": ["gap"],
59
- "margin-top": ["margin"],
60
- "margin-right": ["margin"],
61
- "margin-bottom": ["margin"],
62
- "margin-left": ["margin"],
63
- "padding-top": ["padding"],
64
- "padding-right": ["padding"],
65
- "padding-bottom": ["padding"],
66
- "padding-left": ["padding"],
67
- top: ["inset"],
68
- right: ["inset"],
69
- bottom: ["inset"],
70
- left: ["inset"],
71
- };
72
- /**
73
- * Returns the inline-authored source when `el` carries the property — or a
74
- * shorthand that sets it — in its `style` attribute. Managed previews can
75
- * never beat inline styles in the cascade, so a non-null result means the
76
- * field must present as blocked instead of accepting edits that silently
77
- * revert (see `verifyPreview`'s "inline-style" conflict).
78
- */
79
- export function inlineAuthoredValue(el, property) {
80
- if (isLayoutPreview(el, property))
81
- return null;
82
- const direct = el.style.getPropertyValue(property).trim();
83
- if (direct)
84
- return `${property}: ${direct}`;
85
- for (const shorthand of INLINE_SHORTHAND_SOURCES[property] ?? []) {
86
- const value = el.style.getPropertyValue(shorthand).trim();
87
- if (value)
88
- return `${shorthand}: ${value}`;
89
- }
90
- return null;
91
- }
92
50
  /**
93
51
  * Reads both representations needed by a layout editor. Authored CSS is the
94
52
  * editable source of truth; computed CSS is only the browser preview/fallback.
@@ -7,8 +7,8 @@ export declare function nudgeCssValue(property: string, rawValue: string, direct
7
7
  /** Returns whether a style field exposes the horizontal drag nudge affordance. */
8
8
  export declare function supportsDragNudge(property: string): boolean;
9
9
  /**
10
- * Applies one regular nudge per four horizontal pointer pixels and one large
11
- * nudge per two horizontal pointer pixels. When a large drag starts from an
10
+ * Applies one regular nudge per sixteen horizontal pointer pixels and one large
11
+ * nudge per eight horizontal pointer pixels. When a large drag starts from an
12
12
  * off-grid value, the first step snaps to the next large-step boundary in the
13
13
  * drag direction.
14
14
  */
@@ -2,14 +2,16 @@ import { completeCssValue, CSS_NUMBER_SOURCE } from "./completeCssValue.js";
2
2
  import { valuePolicyFor } from "./valuePolicy.js";
3
3
  const CSS_NUMBER = new RegExp(`^(?:${CSS_NUMBER_SOURCE})$`, "i");
4
4
  const CSS_NUMERIC_LITERAL = new RegExp(`^(${CSS_NUMBER_SOURCE})(px|rem|em|%)?$`, "i");
5
- const DRAG_PIXELS_PER_STEP = 4;
6
- const LARGE_DRAG_PIXELS_PER_STEP = 2;
7
- const DRAG_NUDGE_PROPERTY = /^(?:(?:padding|margin)-(?:top|right|bottom|left|horizontal|vertical)|(?:top|right|bottom|left|inset|inset-(?:horizontal|vertical))|(?:row|column)-gap|border-radius|border-(?:top-left|top-right|bottom-right|bottom-left)-radius)$/;
5
+ const DRAG_PIXELS_PER_STEP = 16;
6
+ const LARGE_DRAG_PIXELS_PER_STEP = 8;
7
+ const DRAG_NUDGE_PROPERTY = /^(?:(?:padding|margin)-(?:top|right|bottom|left|horizontal|vertical)|(?:top|right|bottom|left|inset|inset-(?:horizontal|vertical))|(?:row|column)-gap|(?:font-size|letter-spacing|line-height|opacity)|border-radius|border-(?:top-left|top-right|bottom-right|bottom-left)-radius)$/;
8
8
  /**
9
9
  * Returns a nudged CSS numeric literal, or null when the value is not a
10
10
  * standalone number that the inspector can safely edit.
11
11
  */
12
12
  export function nudgeCssValue(property, rawValue, direction, large = false) {
13
+ if (property === "opacity")
14
+ return nudgeOpacityValue(rawValue, direction, large);
13
15
  const normalised = normaliseForNudge(property, rawValue);
14
16
  const parsed = parseNumericLiteral(normalised);
15
17
  if (!parsed)
@@ -25,8 +27,8 @@ export function supportsDragNudge(property) {
25
27
  return DRAG_NUDGE_PROPERTY.test(property);
26
28
  }
27
29
  /**
28
- * Applies one regular nudge per four horizontal pointer pixels and one large
29
- * nudge per two horizontal pointer pixels. When a large drag starts from an
30
+ * Applies one regular nudge per sixteen horizontal pointer pixels and one large
31
+ * nudge per eight horizontal pointer pixels. When a large drag starts from an
30
32
  * off-grid value, the first step snaps to the next large-step boundary in the
31
33
  * drag direction.
32
34
  */
@@ -59,11 +61,11 @@ export function canNudgeCssValueByDrag(property, rawValue) {
59
61
  }
60
62
  /** Returns a clamped opacity percentage nudged by 1%, or 10% with Shift. */
61
63
  export function nudgeOpacityValue(rawValue, direction, large = false) {
62
- const parsed = parseNumericLiteral(rawValue);
63
- if (!parsed || parsed.unit !== "%")
64
+ const percent = parseOpacityPercent(rawValue);
65
+ if (percent === null)
64
66
  return null;
65
67
  const step = large ? 10 : 1;
66
- const next = Math.min(100, Math.max(0, parsed.number + direction * step));
68
+ const next = Math.min(100, Math.max(0, percent + direction * step));
67
69
  return `${formatNumber(next)}%`;
68
70
  }
69
71
  function normaliseForNudge(property, rawValue) {
@@ -118,6 +120,8 @@ function snapCssValueToLargeStep(property, rawValue, direction) {
118
120
  return snappedParts.join(", ");
119
121
  }
120
122
  function snapNumericValueToLargeStep(property, rawValue, direction) {
123
+ if (property === "opacity")
124
+ return snapOpacityValueToLargeStep(rawValue, direction);
121
125
  const normalised = normaliseForNudge(property, rawValue);
122
126
  const parsed = parseNumericLiteral(normalised);
123
127
  if (!parsed)
@@ -134,6 +138,30 @@ function snapNumericValueToLargeStep(property, rawValue, direction) {
134
138
  next = clampNudgeNumber(property, next);
135
139
  return `${formatNumber(next)}${parsed.unit}`;
136
140
  }
141
+ function snapOpacityValueToLargeStep(rawValue, direction) {
142
+ const percent = parseOpacityPercent(rawValue);
143
+ if (percent === null)
144
+ return null;
145
+ const step = 10;
146
+ let next = direction > 0
147
+ ? Math.ceil(percent / step) * step
148
+ : Math.floor(percent / step) * step;
149
+ if ((direction > 0 && next <= percent) || (direction < 0 && next >= percent)) {
150
+ next += direction * step;
151
+ }
152
+ next = Math.min(100, Math.max(0, next));
153
+ return `${formatNumber(next)}%`;
154
+ }
155
+ function parseOpacityPercent(rawValue) {
156
+ const parsed = parseNumericLiteral(rawValue);
157
+ if (!parsed)
158
+ return null;
159
+ if (parsed.unit === "%")
160
+ return parsed.number;
161
+ if (parsed.unit === "" && parsed.number >= 0 && parsed.number <= 1)
162
+ return parsed.number * 100;
163
+ return null;
164
+ }
137
165
  function isLineHeight(property) {
138
166
  return property === "line-height" || property.includes("line-height");
139
167
  }
@@ -7,6 +7,8 @@ import { type EditTarget } from "../selection/editTarget.js";
7
7
  import type { StyleSelection } from "../selection/styleSelection.js";
8
8
  export interface TokenValueFieldProps {
9
9
  property: string;
10
+ /** Numeric editing policy for a scalar slot inside a composite property. */
11
+ nudgeProperty?: string;
10
12
  domElement?: HTMLElement;
11
13
  semanticSlot?: TokenSemanticSlot;
12
14
  committedValue: string;
@@ -34,6 +36,9 @@ export interface TokenValueFieldProps {
34
36
  onCommitOpacity?(value: string): boolean | void;
35
37
  atRules?: readonly AtRuleContext[];
36
38
  chipVariant?: "default" | "small";
39
+ blockedBy?: string | null;
40
+ inlineWarningDataTest?: string;
41
+ inlineWarningTooltipDataTest?: string;
37
42
  }
38
43
  export interface TokenFieldProps {
39
44
  property: string;
@@ -62,6 +67,8 @@ export interface TokenFieldProps {
62
67
  mixed?: boolean;
63
68
  /** Referenced tokens used for raw color-field behavior; not rendered inline. */
64
69
  attributionTokens?: string[];
70
+ inlineWarningDataTest?: string;
71
+ inlineWarningTooltipDataTest?: string;
65
72
  }
66
73
  export declare function colorValueToHex(value: string): string | null;
67
74
  export declare function TokenValueField(props: TokenValueFieldProps): ReactElement;