@popmelt.com/core 0.1.0 → 0.2.0

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 (3) hide show
  1. package/README.md +2 -0
  2. package/dist/index.mjs +854 -428
  3. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -35,7 +35,7 @@ var __objRest = (source, exclude) => {
35
35
  // src/components/PopmeltProvider.tsx
36
36
  import {
37
37
  createContext,
38
- useCallback as useCallback8,
38
+ useCallback as useCallback9,
39
39
  useContext,
40
40
  useEffect as useEffect20,
41
41
  useMemo as useMemo4,
@@ -487,7 +487,7 @@ function handleSetAnnotating(state, payload) {
487
487
  return __spreadProps(__spreadValues({}, state), { isAnnotating: payload });
488
488
  }
489
489
  function handleSetTool(state, payload) {
490
- return __spreadProps(__spreadValues({}, state), { activeTool: payload });
490
+ return __spreadProps(__spreadValues({}, state), { activeTool: payload, inspectedElement: null });
491
491
  }
492
492
  function handleSetColor(state, payload) {
493
493
  return __spreadProps(__spreadValues({}, state), { activeColor: payload });
@@ -3706,6 +3706,16 @@ function PaddingHandles({ element, padding, accentColor, hoveredSide, draggingSi
3706
3706
  // src/components/StylePanel.tsx
3707
3707
  import { useCallback as useCallback4, useEffect as useEffect12, useMemo, useRef as useRef4, useState as useState9 } from "react";
3708
3708
  import { AlignCenter, AlignHorizontalSpaceAround, AlignJustify, AlignLeft, AlignRight, AlignVerticalSpaceAround, Baseline, Check, ChevronDown, Columns3, Grid2x2, MoveHorizontal, Plus, RectangleHorizontal, RotateCcw, Rows3, Shrink, UnfoldHorizontal, UnfoldVertical, WholeWord, X } from "lucide-react";
3709
+
3710
+ // src/styles/border.ts
3711
+ var DIAG_SVG = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMicgaGVpZ2h0PScxMic+PGRlZnM+PHBhdHRlcm4gaWQ9J2QnIHdpZHRoPSc0JyBoZWlnaHQ9JzQnIHBhdHRlcm5Vbml0cz0ndXNlclNwYWNlT25Vc2UnPjxwYXRoIGQ9J00tMSwxIGwyLC0yIE0wLDQgbDQsLTQgTTMsNSBsMiwtMicgc3Ryb2tlPSdibGFjaycgc3Ryb2tlLXdpZHRoPScuNScvPjwvcGF0dGVybj48L2RlZnM+PHJlY3Qgd2lkdGg9JzEyJyBoZWlnaHQ9JzEyJyBmaWxsPSd1cmwoI2QpJy8+PC9zdmc+";
3712
+ var POPMELT_BORDER = {
3713
+ borderWidth: 3,
3714
+ borderStyle: "solid",
3715
+ borderImage: `url("${DIAG_SVG}") 4 / 1.9 / 0 round`
3716
+ };
3717
+
3718
+ // src/components/StylePanel.tsx
3709
3719
  import { Fragment as Fragment4, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
3710
3720
  function colorWithAlpha6(color, alpha) {
3711
3721
  const oklchMatch = color.match(/^oklch\(([^)]+)\)$/i);
@@ -4675,10 +4685,16 @@ function UnitInput({
4675
4685
  style: inputStyle,
4676
4686
  placeholder,
4677
4687
  showUnit = true,
4678
- unitStyle: customUnitStyle
4688
+ unitStyle: customUnitStyle,
4689
+ preferredUnit,
4690
+ onUnitCycle
4679
4691
  }) {
4680
4692
  const parsed = parseValue(value);
4681
- const effectiveUnit = isModified ? parsed.unit || getDefaultUnit(property) : getDefaultUnit(property);
4693
+ const propertyDefault = getDefaultUnit(property);
4694
+ const units = PROPERTY_UNITS[property];
4695
+ const canUsePreference = preferredUnit && units && units.includes(preferredUnit);
4696
+ const defaultUnit = canUsePreference ? preferredUnit : propertyDefault;
4697
+ const effectiveUnit = isModified ? parsed.unit || defaultUnit : defaultUnit;
4682
4698
  const displayNum = !isModified && parsed.unit && parsed.unit !== effectiveUnit ? convertFromPx(parsed.num, effectiveUnit) : parsed.num;
4683
4699
  const [isFocused, setIsFocused] = useState9(false);
4684
4700
  const [editText, setEditText] = useState9("");
@@ -4737,12 +4753,17 @@ function UnitInput({
4737
4753
  const displayValue = isFocused ? editText : isNumericValue ? String(displayNum) : "";
4738
4754
  const hasTypedUnit = isFocused && /\s*(rem|em|px|%)\s*$/i.test(editText);
4739
4755
  const shownUnit = hasTypedUnit ? "" : effectiveUnit;
4756
+ const isUnitClickable = onUnitCycle && (shownUnit === "rem" || shownUnit === "px");
4740
4757
  const defaultUnitSuffix = {
4741
4758
  fontSize: 10,
4742
4759
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
4743
4760
  color: "#999",
4744
4761
  pointerEvents: "none"
4745
4762
  };
4763
+ const clickableUnitStyle = __spreadProps(__spreadValues({}, customUnitStyle != null ? customUnitStyle : defaultUnitSuffix), {
4764
+ pointerEvents: "auto",
4765
+ cursor: "pointer"
4766
+ });
4746
4767
  return /* @__PURE__ */ jsxs6(Fragment4, { children: [
4747
4768
  /* @__PURE__ */ jsx7(
4748
4769
  "input",
@@ -4758,7 +4779,15 @@ function UnitInput({
4758
4779
  style: inputStyle
4759
4780
  }
4760
4781
  ),
4761
- showUnit && shownUnit && /* @__PURE__ */ jsx7("span", { style: customUnitStyle != null ? customUnitStyle : defaultUnitSuffix, children: shownUnit })
4782
+ showUnit && shownUnit && /* @__PURE__ */ jsx7(
4783
+ "span",
4784
+ {
4785
+ style: isUnitClickable ? clickableUnitStyle : customUnitStyle != null ? customUnitStyle : defaultUnitSuffix,
4786
+ onClick: isUnitClickable ? onUnitCycle : void 0,
4787
+ title: isUnitClickable ? "Click to switch units" : void 0,
4788
+ children: shownUnit
4789
+ }
4790
+ )
4762
4791
  ] });
4763
4792
  }
4764
4793
  function ColorInput({
@@ -5179,7 +5208,10 @@ function LayoutSection({
5179
5208
  activeDropdown,
5180
5209
  onDropdownChange,
5181
5210
  panelContentRef,
5182
- accentColor
5211
+ accentColor,
5212
+ onFieldHover,
5213
+ preferredUnit,
5214
+ onUnitCycle
5183
5215
  }) {
5184
5216
  const setActiveDropdown = onDropdownChange;
5185
5217
  const display = getValue("display");
@@ -5207,7 +5239,10 @@ function LayoutSection({
5207
5239
  const overflow = getValue("overflow");
5208
5240
  const gridCols = gridTemplateCols.split(/\s+/).filter((v) => v && v !== "none").length || 1;
5209
5241
  const gridRows = gridTemplateRows.split(/\s+/).filter((v) => v && v !== "none").length || 1;
5242
+ const [gridHovered, setGridHovered] = useState9(false);
5210
5243
  const hasActiveDropdown = activeDropdown !== null;
5244
+ const dimSiblings = hasActiveDropdown || gridHovered;
5245
+ const siblingOpacity = hasActiveDropdown ? 0.3 : gridHovered ? 0.65 : 1;
5211
5246
  const DisplayModeButton = ({ mode, icon, active }) => /* @__PURE__ */ jsx7(
5212
5247
  "button",
5213
5248
  {
@@ -5246,84 +5281,159 @@ function LayoutSection({
5246
5281
  const v = value || "0";
5247
5282
  handleChange("padding", `${v} ${current.right} ${v} ${current.left}`);
5248
5283
  };
5249
- const AlignmentGrid = () => {
5250
- const justify = getValue("justify-content");
5251
- const align = getValue("align-items");
5252
- const isColumn = flexDirection === "column" || flexDirection === "column-reverse";
5253
- const getJustifyIndex = (val) => {
5254
- if (val === "flex-start" || val === "start") return 0;
5255
- if (val === "center") return 1;
5256
- if (val === "flex-end" || val === "end") return 2;
5257
- return 0;
5258
- };
5259
- const getAlignIndex = (val) => {
5260
- if (val === "flex-start" || val === "start") return 0;
5261
- if (val === "center") return 1;
5262
- if (val === "flex-end" || val === "end") return 2;
5263
- return 0;
5264
- };
5265
- const justifyIdx = getJustifyIndex(justify);
5266
- const alignIdx = getAlignIndex(align);
5267
- const activeCol = isColumn ? alignIdx : justifyIdx;
5268
- const activeRow = isColumn ? justifyIdx : alignIdx;
5269
- const handleClick = (col, row) => {
5270
- const justifyValues = ["flex-start", "center", "flex-end"];
5271
- const alignValues = ["flex-start", "center", "flex-end"];
5272
- if (isColumn) {
5273
- handleChange("justify-content", justifyValues[row]);
5274
- handleChange("align-items", alignValues[col]);
5275
- } else {
5276
- handleChange("justify-content", justifyValues[col]);
5277
- handleChange("align-items", alignValues[row]);
5284
+ const [scrubDisplay, setScrubDisplay] = useState9({});
5285
+ const scrubPreview = useCallback4((key, domUpdate) => (v) => {
5286
+ domUpdate(v);
5287
+ setScrubDisplay((prev) => __spreadProps(__spreadValues({}, prev), { [key]: v }));
5288
+ }, []);
5289
+ const clearScrub = useCallback4((key) => {
5290
+ setScrubDisplay((prev) => {
5291
+ const next = __spreadValues({}, prev);
5292
+ delete next[key];
5293
+ return next;
5294
+ });
5295
+ }, []);
5296
+ const previewPaddingHorizontal = useCallback4((v) => {
5297
+ const current = parseSpacing(getValue("padding"));
5298
+ applyInlineStyle(element, "padding", `${current.top} ${v} ${current.bottom} ${v}`);
5299
+ }, [element, getValue]);
5300
+ const previewPaddingVertical = useCallback4((v) => {
5301
+ const current = parseSpacing(getValue("padding"));
5302
+ applyInlineStyle(element, "padding", `${v} ${current.right} ${v} ${current.left}`);
5303
+ }, [element, getValue]);
5304
+ const previewProperty = useCallback4((property) => (v) => {
5305
+ applyInlineStyle(element, property, v);
5306
+ }, [element]);
5307
+ const isColumn = flexDirection === "column" || flexDirection === "column-reverse";
5308
+ const getAxisIndex = (val) => {
5309
+ if (val === "center") return 1;
5310
+ if (val === "flex-end" || val === "end") return 2;
5311
+ return 0;
5312
+ };
5313
+ const justifyIdx = getAxisIndex(getValue("justify-content"));
5314
+ const alignIdx = getAxisIndex(getValue("align-items"));
5315
+ const gridActiveCol = isColumn ? alignIdx : justifyIdx;
5316
+ const gridActiveRow = isColumn ? justifyIdx : alignIdx;
5317
+ const gridRef = useRef4(null);
5318
+ const gridSwipeAccum = useRef4({ x: 0, y: 0 });
5319
+ const gridPosRef = useRef4({ col: gridActiveCol, row: gridActiveRow });
5320
+ gridPosRef.current = { col: gridActiveCol, row: gridActiveRow };
5321
+ const applyGridPosition = useCallback4((col, row) => {
5322
+ const vals = ["flex-start", "center", "flex-end"];
5323
+ if (isColumn) {
5324
+ handleChange("justify-content", vals[row]);
5325
+ handleChange("align-items", vals[col]);
5326
+ } else {
5327
+ handleChange("justify-content", vals[col]);
5328
+ handleChange("align-items", vals[row]);
5329
+ }
5330
+ }, [isColumn, handleChange]);
5331
+ const applyGridRef = useRef4(applyGridPosition);
5332
+ applyGridRef.current = applyGridPosition;
5333
+ useEffect12(() => {
5334
+ const THRESHOLD = 30;
5335
+ const onWheel = (e) => {
5336
+ const grid = gridRef.current;
5337
+ if (!grid || !grid.contains(e.target)) return;
5338
+ e.preventDefault();
5339
+ e.stopPropagation();
5340
+ gridSwipeAccum.current.x += e.deltaX;
5341
+ gridSwipeAccum.current.y += e.deltaY;
5342
+ let { col, row } = gridPosRef.current;
5343
+ let moved = false;
5344
+ if (Math.abs(gridSwipeAccum.current.x) >= THRESHOLD) {
5345
+ col = Math.max(0, Math.min(2, col + (gridSwipeAccum.current.x > 0 ? 1 : -1)));
5346
+ gridSwipeAccum.current.x = 0;
5347
+ gridSwipeAccum.current.y = 0;
5348
+ moved = true;
5349
+ }
5350
+ if (!moved && Math.abs(gridSwipeAccum.current.y) >= THRESHOLD) {
5351
+ row = Math.max(0, Math.min(2, row + (gridSwipeAccum.current.y > 0 ? 1 : -1)));
5352
+ gridSwipeAccum.current.x = 0;
5353
+ gridSwipeAccum.current.y = 0;
5354
+ moved = true;
5355
+ }
5356
+ if (moved && (col !== gridPosRef.current.col || row !== gridPosRef.current.row)) {
5357
+ applyGridRef.current(col, row);
5278
5358
  }
5279
5359
  };
5280
- return /* @__PURE__ */ jsx7("div", { style: {
5281
- width: 56,
5282
- height: 56,
5283
- backgroundColor: FIELD_BG,
5284
- borderRadius: 2,
5285
- display: "grid",
5286
- gridTemplateColumns: "repeat(3, 1fr)",
5287
- gridTemplateRows: "repeat(3, 1fr)",
5288
- padding: 6,
5289
- gap: 2
5290
- }, children: [0, 1, 2].map(
5291
- (row) => [0, 1, 2].map((col) => {
5292
- const isActive = col === activeCol && row === activeRow;
5293
- return /* @__PURE__ */ jsx7(
5294
- "button",
5295
- {
5296
- type: "button",
5297
- onClick: () => handleClick(col, row),
5298
- style: {
5299
- width: "100%",
5300
- height: "100%",
5301
- display: "flex",
5302
- alignItems: "center",
5303
- justifyContent: "center",
5304
- border: "none",
5305
- backgroundColor: "transparent",
5306
- cursor: "pointer",
5307
- padding: 0
5360
+ document.addEventListener("wheel", onWheel, { passive: false, capture: true });
5361
+ return () => document.removeEventListener("wheel", onWheel, { capture: true });
5362
+ }, []);
5363
+ const renderAlignmentGrid = () => /* @__PURE__ */ jsx7(
5364
+ "div",
5365
+ {
5366
+ ref: gridRef,
5367
+ onMouseEnter: () => {
5368
+ setGridHovered(true);
5369
+ if (panelContentRef.current) panelContentRef.current.style.overflowY = "hidden";
5370
+ },
5371
+ onMouseLeave: () => {
5372
+ setGridHovered(false);
5373
+ if (panelContentRef.current) panelContentRef.current.style.overflowY = "auto";
5374
+ },
5375
+ style: {
5376
+ width: 56,
5377
+ height: 56,
5378
+ backgroundColor: FIELD_BG,
5379
+ borderRadius: 2,
5380
+ display: "grid",
5381
+ gridTemplateColumns: "repeat(3, 1fr)",
5382
+ gridTemplateRows: "repeat(3, 1fr)",
5383
+ padding: 6,
5384
+ gap: 2,
5385
+ touchAction: "none"
5386
+ },
5387
+ children: [0, 1, 2].map(
5388
+ (row) => [0, 1, 2].map((col) => {
5389
+ const isActive = col === gridActiveCol && row === gridActiveRow;
5390
+ return /* @__PURE__ */ jsx7(
5391
+ "button",
5392
+ {
5393
+ type: "button",
5394
+ onClick: () => applyGridPosition(col, row),
5395
+ style: {
5396
+ width: "100%",
5397
+ height: "100%",
5398
+ display: "flex",
5399
+ alignItems: "center",
5400
+ justifyContent: "center",
5401
+ border: "none",
5402
+ backgroundColor: "transparent",
5403
+ cursor: "pointer",
5404
+ padding: 0
5405
+ },
5406
+ children: isActive ? /* @__PURE__ */ jsx7("svg", { width: "10", height: "10", viewBox: "0 0 10 10", fill: "none", style: { flexShrink: 0 }, children: col === 0 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
5407
+ /* @__PURE__ */ jsx7("rect", { x: "1", y: "1.5", width: "8", height: "1.2", rx: "0.5", fill: accentColor }),
5408
+ /* @__PURE__ */ jsx7("rect", { x: "1", y: "4.4", width: "5", height: "1.2", rx: "0.5", fill: accentColor }),
5409
+ /* @__PURE__ */ jsx7("rect", { x: "1", y: "7.3", width: "7", height: "1.2", rx: "0.5", fill: accentColor })
5410
+ ] }) : col === 1 ? /* @__PURE__ */ jsxs6(Fragment4, { children: [
5411
+ /* @__PURE__ */ jsx7("rect", { x: "1", y: "1.5", width: "8", height: "1.2", rx: "0.5", fill: accentColor }),
5412
+ /* @__PURE__ */ jsx7("rect", { x: "2.5", y: "4.4", width: "5", height: "1.2", rx: "0.5", fill: accentColor }),
5413
+ /* @__PURE__ */ jsx7("rect", { x: "1.5", y: "7.3", width: "7", height: "1.2", rx: "0.5", fill: accentColor })
5414
+ ] }) : /* @__PURE__ */ jsxs6(Fragment4, { children: [
5415
+ /* @__PURE__ */ jsx7("rect", { x: "1", y: "1.5", width: "8", height: "1.2", rx: "0.5", fill: accentColor }),
5416
+ /* @__PURE__ */ jsx7("rect", { x: "4", y: "4.4", width: "5", height: "1.2", rx: "0.5", fill: accentColor }),
5417
+ /* @__PURE__ */ jsx7("rect", { x: "2", y: "7.3", width: "7", height: "1.2", rx: "0.5", fill: accentColor })
5418
+ ] }) }) : /* @__PURE__ */ jsx7("div", { style: {
5419
+ width: 5,
5420
+ height: 5,
5421
+ borderRadius: "50%",
5422
+ backgroundColor: "#aaa"
5423
+ } })
5308
5424
  },
5309
- children: /* @__PURE__ */ jsx7("div", { style: {
5310
- width: 6,
5311
- height: 6,
5312
- borderRadius: "50%",
5313
- backgroundColor: isActive ? accentColor : "#d1d5db"
5314
- } })
5315
- },
5316
- `${row}-${col}`
5317
- );
5318
- })
5319
- ) });
5320
- };
5425
+ `${row}-${col}`
5426
+ );
5427
+ })
5428
+ )
5429
+ }
5430
+ );
5321
5431
  const gridModified = isModified("grid-template-columns") || isModified("grid-template-rows");
5322
5432
  const sectionTitle = isFlexOrGrid2 ? "Auto layout" : "Layout";
5323
5433
  return /* @__PURE__ */ jsxs6("div", { style: { borderBottom: "1px solid rgba(0,0,0,0.08)" }, children: [
5324
5434
  /* @__PURE__ */ jsx7("div", { style: sectionHeaderStyle, children: /* @__PURE__ */ jsx7("span", { children: sectionTitle }) }),
5325
5435
  /* @__PURE__ */ jsxs6("div", { style: { padding: "8px 12px" }, children: [
5326
- /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 2, marginBottom: 8, backgroundColor: FIELD_BG, borderRadius: 2, padding: 2, opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
5436
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 2, marginBottom: 8, backgroundColor: FIELD_BG, borderRadius: 2, padding: 2, opacity: siblingOpacity, transition: "opacity 150ms ease" }, children: [
5327
5437
  /* @__PURE__ */ jsx7(DisplayModeButton, { mode: "block", icon: /* @__PURE__ */ jsx7(RectangleHorizontal, { size: 16 }), active: displayMode === "block" }),
5328
5438
  /* @__PURE__ */ jsx7(DisplayModeButton, { mode: "flex-col", icon: /* @__PURE__ */ jsx7(Rows3, { size: 16 }), active: displayMode === "flex-col" }),
5329
5439
  /* @__PURE__ */ jsx7(DisplayModeButton, { mode: "flex-row", icon: /* @__PURE__ */ jsx7(Columns3, { size: 16 }), active: displayMode === "flex-row" }),
@@ -5397,9 +5507,9 @@ function LayoutSection({
5397
5507
  }
5398
5508
  )
5399
5509
  ] }),
5400
- isFlex && /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, marginBottom: 8, opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
5401
- /* @__PURE__ */ jsx7(AlignmentGrid, {}),
5402
- /* @__PURE__ */ jsxs6("div", { style: { flex: 1 }, children: [
5510
+ isFlex && /* @__PURE__ */ jsxs6("div", { onMouseEnter: () => onFieldHover == null ? void 0 : onFieldHover("gap"), onMouseLeave: () => onFieldHover == null ? void 0 : onFieldHover("element"), style: { display: "flex", gap: 8, marginBottom: 8 }, children: [
5511
+ /* @__PURE__ */ jsx7("div", { style: { opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: renderAlignmentGrid() }),
5512
+ /* @__PURE__ */ jsxs6("div", { style: { flex: 1, opacity: siblingOpacity, transition: "opacity 150ms ease" }, children: [
5403
5513
  /* @__PURE__ */ jsx7(
5404
5514
  "div",
5405
5515
  {
@@ -5418,17 +5528,19 @@ function LayoutSection({
5418
5528
  ),
5419
5529
  /* @__PURE__ */ jsx7(FieldWrapper, { dimmed: hasActiveDropdown, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center" }, children: [
5420
5530
  /* @__PURE__ */ jsx7(
5421
- "span",
5531
+ ScrubLabel,
5422
5532
  {
5423
- onClick: isModified("gap") ? () => onResetProperty("gap") : void 0,
5424
- title: isModified("gap") ? "Click to reset" : void 0,
5425
- style: {
5426
- color: isModified("gap") ? accentColor : "#999",
5427
- padding: "0 4px",
5428
- display: "flex",
5429
- alignItems: "center",
5430
- cursor: isModified("gap") ? "pointer" : "default"
5533
+ value: gap,
5534
+ onChange: (v) => {
5535
+ clearScrub("gap");
5536
+ handleChange("gap", v);
5431
5537
  },
5538
+ onPreview: scrubPreview("gap", previewProperty("gap")),
5539
+ onScrubEnd: () => clearScrub("gap"),
5540
+ onReset: () => onResetProperty("gap"),
5541
+ isModified: isModified("gap"),
5542
+ accentColor,
5543
+ defaultUnit: preferredUnit,
5432
5544
  children: flexDirection === "column" || flexDirection === "column-reverse" ? /* @__PURE__ */ jsx7(UnfoldVertical, { size: 12, strokeWidth: isModified("gap") ? 2.5 : 1.5 }) : /* @__PURE__ */ jsx7(UnfoldHorizontal, { size: 12, strokeWidth: isModified("gap") ? 2.5 : 1.5 })
5433
5545
  }
5434
5546
  ),
@@ -5436,17 +5548,19 @@ function LayoutSection({
5436
5548
  UnitInput,
5437
5549
  {
5438
5550
  property: "gap",
5439
- value: gap,
5551
+ value: scrubDisplay["gap"] || gap,
5440
5552
  onChange: (v) => handleChange("gap", v),
5441
- isModified: isModified("gap"),
5553
+ isModified: isModified("gap") || "gap" in scrubDisplay,
5442
5554
  style: __spreadProps(__spreadValues({}, compactInputStyle), { flex: 1, minWidth: 0 }),
5443
- unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" }
5555
+ unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" },
5556
+ preferredUnit,
5557
+ onUnitCycle
5444
5558
  }
5445
5559
  )
5446
5560
  ] }) })
5447
5561
  ] })
5448
5562
  ] }),
5449
- isGrid && /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 8, marginBottom: 8, opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
5563
+ isGrid && /* @__PURE__ */ jsxs6("div", { onMouseEnter: () => onFieldHover == null ? void 0 : onFieldHover("gap"), onMouseLeave: () => onFieldHover == null ? void 0 : onFieldHover("element"), style: { display: "flex", gap: 8, marginBottom: 8, opacity: siblingOpacity, transition: "opacity 150ms ease" }, children: [
5450
5564
  /* @__PURE__ */ jsx7(
5451
5565
  GridDimensions,
5452
5566
  {
@@ -5461,17 +5575,19 @@ function LayoutSection({
5461
5575
  /* @__PURE__ */ jsxs6("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: 4 }, children: [
5462
5576
  /* @__PURE__ */ jsx7(FieldWrapper, { dimmed: hasActiveDropdown, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center" }, children: [
5463
5577
  /* @__PURE__ */ jsx7(
5464
- "span",
5578
+ ScrubLabel,
5465
5579
  {
5466
- onClick: isModified("column-gap") ? () => onResetProperty("column-gap") : void 0,
5467
- title: isModified("column-gap") ? "Click to reset" : void 0,
5468
- style: {
5469
- color: isModified("column-gap") ? accentColor : "#999",
5470
- padding: "0 4px",
5471
- display: "flex",
5472
- alignItems: "center",
5473
- cursor: isModified("column-gap") ? "pointer" : "default"
5580
+ value: columnGap || gap,
5581
+ onChange: (v) => {
5582
+ clearScrub("column-gap");
5583
+ handleChange("column-gap", v);
5474
5584
  },
5585
+ onPreview: scrubPreview("column-gap", previewProperty("column-gap")),
5586
+ onScrubEnd: () => clearScrub("column-gap"),
5587
+ onReset: () => onResetProperty("column-gap"),
5588
+ isModified: isModified("column-gap"),
5589
+ accentColor,
5590
+ defaultUnit: preferredUnit,
5475
5591
  children: /* @__PURE__ */ jsx7(UnfoldHorizontal, { size: 12, strokeWidth: isModified("column-gap") ? 2.5 : 1.5 })
5476
5592
  }
5477
5593
  ),
@@ -5479,28 +5595,32 @@ function LayoutSection({
5479
5595
  UnitInput,
5480
5596
  {
5481
5597
  property: "column-gap",
5482
- value: columnGap || gap,
5598
+ value: scrubDisplay["column-gap"] || columnGap || gap,
5483
5599
  onChange: (v) => handleChange("column-gap", v),
5484
- isModified: isModified("column-gap"),
5600
+ isModified: isModified("column-gap") || "column-gap" in scrubDisplay,
5485
5601
  placeholder: "col",
5486
5602
  style: __spreadProps(__spreadValues({}, compactInputStyle), { flex: 1, minWidth: 0 }),
5487
- unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" }
5603
+ unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" },
5604
+ preferredUnit,
5605
+ onUnitCycle
5488
5606
  }
5489
5607
  )
5490
5608
  ] }) }),
5491
5609
  /* @__PURE__ */ jsx7(FieldWrapper, { dimmed: hasActiveDropdown, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center" }, children: [
5492
5610
  /* @__PURE__ */ jsx7(
5493
- "span",
5611
+ ScrubLabel,
5494
5612
  {
5495
- onClick: isModified("row-gap") ? () => onResetProperty("row-gap") : void 0,
5496
- title: isModified("row-gap") ? "Click to reset" : void 0,
5497
- style: {
5498
- color: isModified("row-gap") ? accentColor : "#999",
5499
- padding: "0 4px",
5500
- display: "flex",
5501
- alignItems: "center",
5502
- cursor: isModified("row-gap") ? "pointer" : "default"
5613
+ value: rowGap || gap,
5614
+ onChange: (v) => {
5615
+ clearScrub("row-gap");
5616
+ handleChange("row-gap", v);
5503
5617
  },
5618
+ onPreview: scrubPreview("row-gap", previewProperty("row-gap")),
5619
+ onScrubEnd: () => clearScrub("row-gap"),
5620
+ onReset: () => onResetProperty("row-gap"),
5621
+ isModified: isModified("row-gap"),
5622
+ accentColor,
5623
+ defaultUnit: preferredUnit,
5504
5624
  children: /* @__PURE__ */ jsx7(UnfoldVertical, { size: 12, strokeWidth: isModified("row-gap") ? 2.5 : 1.5 })
5505
5625
  }
5506
5626
  ),
@@ -5508,31 +5628,36 @@ function LayoutSection({
5508
5628
  UnitInput,
5509
5629
  {
5510
5630
  property: "row-gap",
5511
- value: rowGap || gap,
5631
+ value: scrubDisplay["row-gap"] || rowGap || gap,
5512
5632
  onChange: (v) => handleChange("row-gap", v),
5513
- isModified: isModified("row-gap"),
5633
+ isModified: isModified("row-gap") || "row-gap" in scrubDisplay,
5514
5634
  placeholder: "row",
5515
5635
  style: __spreadProps(__spreadValues({}, compactInputStyle), { flex: 1, minWidth: 0 }),
5516
- unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" }
5636
+ unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" },
5637
+ preferredUnit,
5638
+ onUnitCycle
5517
5639
  }
5518
5640
  )
5519
5641
  ] }) })
5520
5642
  ] })
5521
5643
  ] }),
5522
- isFlexOrGrid2 && /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 4, marginBottom: 8, opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
5644
+ isFlexOrGrid2 && /* @__PURE__ */ jsxs6("div", { onMouseEnter: () => onFieldHover == null ? void 0 : onFieldHover("padding"), onMouseLeave: () => onFieldHover == null ? void 0 : onFieldHover("element"), style: { display: "flex", gap: 4, marginBottom: 8, opacity: siblingOpacity, transition: "opacity 150ms ease" }, children: [
5523
5645
  /* @__PURE__ */ jsx7(FieldWrapper, { style: { flex: 1 }, dimmed: hasActiveDropdown, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center" }, children: [
5524
5646
  /* @__PURE__ */ jsx7(
5525
- "span",
5647
+ ScrubLabel,
5526
5648
  {
5527
- onClick: isModified("padding") ? () => onResetProperty("padding") : void 0,
5528
- title: isModified("padding") ? "Click to reset" : void 0,
5529
- style: {
5530
- color: isModified("padding") ? accentColor : "#999",
5531
- padding: "0 4px",
5532
- display: "flex",
5533
- alignItems: "center",
5534
- cursor: isModified("padding") ? "pointer" : "default"
5649
+ value: padding.left,
5650
+ onChange: (v) => {
5651
+ clearScrub("padding-h");
5652
+ handlePaddingHorizontal(v);
5535
5653
  },
5654
+ onPreview: scrubPreview("padding-h", previewPaddingHorizontal),
5655
+ onScrubEnd: () => clearScrub("padding-h"),
5656
+ onReset: () => onResetProperty("padding"),
5657
+ isModified: isModified("padding"),
5658
+ accentColor,
5659
+ defaultUnit: preferredUnit,
5660
+ snapSteps: PADDING_SNAP_STEPS,
5536
5661
  children: /* @__PURE__ */ jsx7(AlignHorizontalSpaceAround, { size: 12, strokeWidth: isModified("padding") ? 2.5 : 1.5 })
5537
5662
  }
5538
5663
  ),
@@ -5540,28 +5665,33 @@ function LayoutSection({
5540
5665
  UnitInput,
5541
5666
  {
5542
5667
  property: "padding",
5543
- value: padding.left,
5668
+ value: scrubDisplay["padding-h"] || padding.left,
5544
5669
  onChange: (v) => handlePaddingHorizontal(v),
5545
- isModified: isModified("padding"),
5670
+ isModified: isModified("padding") || "padding-h" in scrubDisplay,
5546
5671
  placeholder: "H pad",
5547
5672
  style: __spreadProps(__spreadValues({}, compactInputStyle), { flex: 1, minWidth: 0 }),
5548
- unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" }
5673
+ unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" },
5674
+ preferredUnit,
5675
+ onUnitCycle
5549
5676
  }
5550
5677
  )
5551
5678
  ] }) }),
5552
5679
  /* @__PURE__ */ jsx7(FieldWrapper, { style: { flex: 1 }, dimmed: hasActiveDropdown, children: /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center" }, children: [
5553
5680
  /* @__PURE__ */ jsx7(
5554
- "span",
5681
+ ScrubLabel,
5555
5682
  {
5556
- onClick: isModified("padding") ? () => onResetProperty("padding") : void 0,
5557
- title: isModified("padding") ? "Click to reset" : void 0,
5558
- style: {
5559
- color: isModified("padding") ? accentColor : "#999",
5560
- padding: "0 4px",
5561
- display: "flex",
5562
- alignItems: "center",
5563
- cursor: isModified("padding") ? "pointer" : "default"
5683
+ value: padding.top,
5684
+ onChange: (v) => {
5685
+ clearScrub("padding-v");
5686
+ handlePaddingVertical(v);
5564
5687
  },
5688
+ onPreview: scrubPreview("padding-v", previewPaddingVertical),
5689
+ onScrubEnd: () => clearScrub("padding-v"),
5690
+ onReset: () => onResetProperty("padding"),
5691
+ isModified: isModified("padding"),
5692
+ accentColor,
5693
+ defaultUnit: preferredUnit,
5694
+ snapSteps: PADDING_SNAP_STEPS,
5565
5695
  children: /* @__PURE__ */ jsx7(AlignVerticalSpaceAround, { size: 12, strokeWidth: isModified("padding") ? 2.5 : 1.5 })
5566
5696
  }
5567
5697
  ),
@@ -5569,17 +5699,19 @@ function LayoutSection({
5569
5699
  UnitInput,
5570
5700
  {
5571
5701
  property: "padding",
5572
- value: padding.top,
5702
+ value: scrubDisplay["padding-v"] || padding.top,
5573
5703
  onChange: (v) => handlePaddingVertical(v),
5574
- isModified: isModified("padding"),
5704
+ isModified: isModified("padding") || "padding-v" in scrubDisplay,
5575
5705
  placeholder: "V pad",
5576
5706
  style: __spreadProps(__spreadValues({}, compactInputStyle), { flex: 1, minWidth: 0 }),
5577
- unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" }
5707
+ unitStyle: { fontSize: 10, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#999", padding: "0 8px" },
5708
+ preferredUnit,
5709
+ onUnitCycle
5578
5710
  }
5579
5711
  )
5580
5712
  ] }) })
5581
5713
  ] }),
5582
- /* @__PURE__ */ jsxs6("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 11, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#64748b", opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
5714
+ /* @__PURE__ */ jsxs6("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", fontSize: 11, fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace', color: "#64748b", opacity: siblingOpacity, transition: "opacity 150ms ease" }, children: [
5583
5715
  /* @__PURE__ */ jsx7(
5584
5716
  "input",
5585
5717
  {
@@ -5618,7 +5750,9 @@ function TypographySection({
5618
5750
  colorVariables,
5619
5751
  activeColorDropdown,
5620
5752
  onColorDropdownChange,
5621
- panelContentRef
5753
+ panelContentRef,
5754
+ preferredUnit,
5755
+ onUnitCycle
5622
5756
  }) {
5623
5757
  var _a;
5624
5758
  const fontFamily = getValue("font-family");
@@ -5717,9 +5851,10 @@ function TypographySection({
5717
5851
  transform: "translateY(-50%)",
5718
5852
  fontSize: 10,
5719
5853
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
5720
- color: "#999",
5721
- pointerEvents: "none"
5722
- }
5854
+ color: "#999"
5855
+ },
5856
+ preferredUnit,
5857
+ onUnitCycle
5723
5858
  }
5724
5859
  ) }) })
5725
5860
  ] }),
@@ -5820,6 +5955,130 @@ function parseValue(value) {
5820
5955
  }
5821
5956
  return { num: 0, unit: "" };
5822
5957
  }
5958
+ var PADDING_SNAP_STEPS = [0, 1, 2, 4, 8, 12, 16, 20, 24, 28, 32];
5959
+ function ScrubLabel({
5960
+ value,
5961
+ onChange,
5962
+ onPreview,
5963
+ onScrubEnd,
5964
+ onReset,
5965
+ isModified,
5966
+ accentColor,
5967
+ defaultUnit = "rem",
5968
+ snapSteps,
5969
+ color,
5970
+ style,
5971
+ children
5972
+ }) {
5973
+ const ref = useRef4(null);
5974
+ const scrub = useRef4(null);
5975
+ const resetRef = useRef4(onReset);
5976
+ const modifiedRef = useRef4(isModified);
5977
+ const lastShiftRef = useRef4(false);
5978
+ resetRef.current = onReset;
5979
+ modifiedRef.current = isModified;
5980
+ useEffect12(() => {
5981
+ const onMove = (e) => {
5982
+ const s = scrub.current;
5983
+ if (!s) return;
5984
+ s.hasMoved = true;
5985
+ lastShiftRef.current = e.shiftKey;
5986
+ const sens = s.unit === "rem" || s.unit === "em" ? 0.1 : 1;
5987
+ s.accum += e.movementX * sens;
5988
+ let newVal = Math.max(0, Math.round((s.startValue + s.accum) * 10) / 10);
5989
+ if (e.shiftKey && snapSteps) {
5990
+ const rootFs = s.unit === "rem" || s.unit === "em" ? parseFloat(getComputedStyle(document.documentElement).fontSize) || 16 : 1;
5991
+ const pxVal = s.unit === "rem" || s.unit === "em" ? newVal * rootFs : newVal;
5992
+ let snapped = snapSteps[snapSteps.length - 1];
5993
+ for (let i = 0; i < snapSteps.length - 1; i++) {
5994
+ const lo = snapSteps[i];
5995
+ const hi = snapSteps[i + 1];
5996
+ if (pxVal <= (lo + hi) / 2) {
5997
+ snapped = lo;
5998
+ break;
5999
+ }
6000
+ if (pxVal < hi) {
6001
+ snapped = hi;
6002
+ break;
6003
+ }
6004
+ }
6005
+ if (pxVal > snapSteps[snapSteps.length - 1]) {
6006
+ snapped = Math.round(pxVal / 8) * 8;
6007
+ }
6008
+ newVal = s.unit === "rem" || s.unit === "em" ? Math.round(snapped / rootFs * 1e3) / 1e3 : snapped;
6009
+ }
6010
+ onPreview == null ? void 0 : onPreview(`${newVal}${s.unit}`);
6011
+ };
6012
+ const onUp = () => {
6013
+ const s = scrub.current;
6014
+ if (!s) return;
6015
+ let finalVal = Math.max(0, Math.round((s.startValue + s.accum) * 10) / 10);
6016
+ if (lastShiftRef.current && snapSteps) {
6017
+ const rootFs = s.unit === "rem" || s.unit === "em" ? parseFloat(getComputedStyle(document.documentElement).fontSize) || 16 : 1;
6018
+ const pxVal = s.unit === "rem" || s.unit === "em" ? finalVal * rootFs : finalVal;
6019
+ let snapped = snapSteps[snapSteps.length - 1];
6020
+ for (let i = 0; i < snapSteps.length - 1; i++) {
6021
+ const lo = snapSteps[i];
6022
+ const hi = snapSteps[i + 1];
6023
+ if (pxVal <= (lo + hi) / 2) {
6024
+ snapped = lo;
6025
+ break;
6026
+ }
6027
+ if (pxVal < hi) {
6028
+ snapped = hi;
6029
+ break;
6030
+ }
6031
+ }
6032
+ if (pxVal > snapSteps[snapSteps.length - 1]) {
6033
+ snapped = Math.round(pxVal / 8) * 8;
6034
+ }
6035
+ finalVal = s.unit === "rem" || s.unit === "em" ? Math.round(snapped / rootFs * 1e3) / 1e3 : snapped;
6036
+ }
6037
+ const changed = s.hasMoved && finalVal !== s.startValue;
6038
+ scrub.current = null;
6039
+ document.exitPointerLock();
6040
+ if (changed) {
6041
+ onChange(`${finalVal}${s.unit}`);
6042
+ } else if (s.hasMoved) {
6043
+ onPreview == null ? void 0 : onPreview(`${s.startValue}${s.unit}`);
6044
+ } else if (modifiedRef.current && resetRef.current) {
6045
+ resetRef.current();
6046
+ }
6047
+ onScrubEnd == null ? void 0 : onScrubEnd();
6048
+ };
6049
+ document.addEventListener("mousemove", onMove);
6050
+ document.addEventListener("mouseup", onUp);
6051
+ return () => {
6052
+ document.removeEventListener("mousemove", onMove);
6053
+ document.removeEventListener("mouseup", onUp);
6054
+ };
6055
+ }, [onChange, onPreview, onScrubEnd]);
6056
+ const onMouseDown = useCallback4((e) => {
6057
+ var _a;
6058
+ if (e.button !== 0) return;
6059
+ e.preventDefault();
6060
+ const parsed = parseValue(value);
6061
+ const unit = parsed.unit && parsed.unit !== "px" ? parsed.unit : defaultUnit;
6062
+ scrub.current = { startValue: parsed.num, unit, accum: 0, hasMoved: false };
6063
+ (_a = ref.current) == null ? void 0 : _a.requestPointerLock();
6064
+ }, [value, defaultUnit]);
6065
+ return /* @__PURE__ */ jsx7(
6066
+ "span",
6067
+ {
6068
+ ref,
6069
+ onMouseDown,
6070
+ title: isModified ? "Click to reset \xB7 Drag to scrub" : "Drag to scrub",
6071
+ style: __spreadValues({
6072
+ color: isModified ? accentColor || "#3b82f6" : color || "#999",
6073
+ padding: "0 4px",
6074
+ display: "flex",
6075
+ alignItems: "center",
6076
+ cursor: "ew-resize"
6077
+ }, style),
6078
+ children
6079
+ }
6080
+ );
6081
+ }
5823
6082
  function StylePanel({
5824
6083
  element,
5825
6084
  elementInfo,
@@ -5827,19 +6086,31 @@ function StylePanel({
5827
6086
  styleModifications,
5828
6087
  dispatch,
5829
6088
  onClose,
6089
+ onHover,
5830
6090
  accentColor = "#3b82f6"
5831
6091
  }) {
5832
6092
  var _a, _b;
5833
6093
  const panelRef = useRef4(null);
5834
6094
  const panelContentRef = useRef4(null);
5835
- const [visible, setVisible] = useState9(false);
6095
+ const [visible, setVisible] = useState9(() => {
6096
+ try {
6097
+ return !!localStorage.getItem("devtools-panel-position");
6098
+ } catch (e) {
6099
+ return false;
6100
+ }
6101
+ });
5836
6102
  useEffect12(() => {
6103
+ if (visible) return;
5837
6104
  const id = setTimeout(() => setVisible(true), 50);
5838
6105
  return () => clearTimeout(id);
5839
- }, []);
6106
+ }, [visible]);
5840
6107
  const [activeDropdown, setActiveDropdown] = useState9(null);
5841
6108
  const [activeColorDropdown, setActiveColorDropdown] = useState9(null);
5842
6109
  const hasActiveDropdown = activeDropdown !== null || activeColorDropdown !== null;
6110
+ const [preferredUnit, setPreferredUnit] = useState9("rem");
6111
+ const cycleUnit = useCallback4(() => {
6112
+ setPreferredUnit((u) => u === "rem" ? "px" : "rem");
6113
+ }, []);
5843
6114
  const originalValuesRef = useRef4(/* @__PURE__ */ new Map());
5844
6115
  const [rawCss, setRawCss] = useState9("");
5845
6116
  const colorVariables = useMemo(() => getColorVariables(), []);
@@ -5856,36 +6127,121 @@ function StylePanel({
5856
6127
  }, [onClose]);
5857
6128
  const positionRef = useRef4({ top: 0, left: 0, maxHeight: 400 });
5858
6129
  const [, forceUpdate] = useState9(0);
6130
+ const PANEL_POS_KEY = "devtools-panel-position";
6131
+ const dragOffset = useRef4({ x: 0, y: 0 });
6132
+ const dragState = useRef4(null);
6133
+ const hasSavedPosition = useRef4(false);
6134
+ useEffect12(() => {
6135
+ try {
6136
+ const stored = localStorage.getItem(PANEL_POS_KEY);
6137
+ if (stored) {
6138
+ const pos = JSON.parse(stored);
6139
+ if (typeof pos.top === "number" && typeof pos.left === "number") {
6140
+ hasSavedPosition.current = true;
6141
+ positionRef.current = __spreadProps(__spreadValues({}, positionRef.current), { top: pos.top, left: pos.left });
6142
+ }
6143
+ }
6144
+ } catch (e) {
6145
+ }
6146
+ }, []);
5859
6147
  useEffect12(() => {
6148
+ const onMouseMove = (e) => {
6149
+ const ds = dragState.current;
6150
+ if (!ds) return;
6151
+ const rawX = ds.startOffsetX + (e.clientX - ds.startX);
6152
+ const rawY = ds.startOffsetY + (e.clientY - ds.startY);
6153
+ const panelWidth = 280;
6154
+ const edgePad = 16;
6155
+ const clampedLeft = Math.max(edgePad, Math.min(window.innerWidth - panelWidth - edgePad, positionRef.current.left + rawX));
6156
+ const clampedTop = Math.max(edgePad, positionRef.current.top + rawY);
6157
+ dragOffset.current = {
6158
+ x: clampedLeft - positionRef.current.left,
6159
+ y: clampedTop - positionRef.current.top
6160
+ };
6161
+ const panel = panelRef.current;
6162
+ const wrapper = panel == null ? void 0 : panel.parentElement;
6163
+ if (!wrapper) return;
6164
+ wrapper.style.top = `${clampedTop}px`;
6165
+ wrapper.style.left = `${clampedLeft}px`;
6166
+ const toolbar = document.getElementById("devtools-toolbar");
6167
+ const toolbarRect = toolbar == null ? void 0 : toolbar.getBoundingClientRect();
6168
+ let bottomLimit = window.innerHeight - 16;
6169
+ if (toolbarRect && clampedLeft + panelWidth > toolbarRect.left) {
6170
+ bottomLimit = toolbarRect.top - 8;
6171
+ }
6172
+ const visibleTop = Math.max(0, clampedTop);
6173
+ const maxHeight = Math.max(200, bottomLimit - visibleTop);
6174
+ if (panel) panel.style.maxHeight = `${maxHeight}px`;
6175
+ };
6176
+ const onMouseUp = () => {
6177
+ if (!dragState.current) return;
6178
+ const finalTop = positionRef.current.top + dragOffset.current.y;
6179
+ const finalLeft = positionRef.current.left + dragOffset.current.x;
6180
+ positionRef.current = __spreadProps(__spreadValues({}, positionRef.current), { top: finalTop, left: finalLeft });
6181
+ dragOffset.current = { x: 0, y: 0 };
6182
+ hasSavedPosition.current = true;
6183
+ try {
6184
+ localStorage.setItem(PANEL_POS_KEY, JSON.stringify({ top: finalTop, left: finalLeft }));
6185
+ } catch (e) {
6186
+ }
6187
+ dragState.current = null;
6188
+ };
6189
+ window.addEventListener("mousemove", onMouseMove);
6190
+ window.addEventListener("mouseup", onMouseUp);
6191
+ return () => {
6192
+ window.removeEventListener("mousemove", onMouseMove);
6193
+ window.removeEventListener("mouseup", onMouseUp);
6194
+ };
6195
+ }, []);
6196
+ const handleHeaderMouseDown = useCallback4((e) => {
6197
+ if (e.button !== 0 || e.target.closest("button")) return;
6198
+ e.preventDefault();
6199
+ dragState.current = {
6200
+ startX: e.clientX,
6201
+ startY: e.clientY,
6202
+ startOffsetX: dragOffset.current.x,
6203
+ startOffsetY: dragOffset.current.y
6204
+ };
6205
+ }, []);
6206
+ useEffect12(() => {
6207
+ dragOffset.current = { x: 0, y: 0 };
5860
6208
  const updatePosition = (isScrollEvent = false) => {
5861
6209
  const panel = panelRef.current;
5862
- const rect = element.getBoundingClientRect();
5863
6210
  const panelWidth = 280;
5864
- const padding = 16;
5865
- const toolbarWidth = 300;
5866
- const toolbarHeight = 50;
5867
- const toolbarRight = padding;
5868
- const toolbarBottom = padding;
5869
- let left = rect.right + padding;
5870
- let top = rect.top;
5871
- if (left + panelWidth > window.innerWidth - padding) {
5872
- left = rect.left - panelWidth - padding;
5873
- }
5874
- if (left < padding) {
5875
- left = Math.max(padding, (window.innerWidth - panelWidth) / 2);
5876
- }
5877
- const panelRight = left + panelWidth;
5878
- const toolbarLeft = window.innerWidth - toolbarRight - toolbarWidth;
5879
- const overlapsToolbar = panelRight > toolbarLeft;
5880
- const bottomLimit = overlapsToolbar ? window.innerHeight - toolbarBottom - toolbarHeight - padding : window.innerHeight - padding;
6211
+ const gap = 8;
6212
+ let top;
6213
+ let left;
6214
+ if (hasSavedPosition.current) {
6215
+ top = positionRef.current.top;
6216
+ left = positionRef.current.left;
6217
+ } else {
6218
+ const rect = element.getBoundingClientRect();
6219
+ left = rect.right + gap;
6220
+ top = rect.top;
6221
+ if (left + panelWidth > window.innerWidth - gap) {
6222
+ left = rect.left - panelWidth - gap;
6223
+ }
6224
+ if (left < gap) {
6225
+ left = Math.max(gap, (window.innerWidth - panelWidth) / 2);
6226
+ }
6227
+ }
6228
+ const toolbar = document.getElementById("devtools-toolbar");
6229
+ const toolbarRect = toolbar == null ? void 0 : toolbar.getBoundingClientRect();
6230
+ let bottomLimit = window.innerHeight - 16;
6231
+ if (toolbarRect) {
6232
+ const panelRight = left + panelWidth;
6233
+ if (panelRight > toolbarRect.left) {
6234
+ bottomLimit = toolbarRect.top - gap;
6235
+ }
6236
+ }
5881
6237
  const visibleTop = Math.max(0, top);
5882
6238
  const maxHeight = Math.max(200, bottomLimit - visibleTop);
5883
6239
  positionRef.current = { top, left, maxHeight };
5884
6240
  if (isScrollEvent && panel) {
5885
6241
  const wrapper = panel.parentElement;
5886
- if (wrapper) {
5887
- wrapper.style.top = `${top}px`;
5888
- wrapper.style.left = `${left}px`;
6242
+ if (wrapper && !hasSavedPosition.current) {
6243
+ wrapper.style.top = `${top + dragOffset.current.y}px`;
6244
+ wrapper.style.left = `${left + dragOffset.current.x}px`;
5889
6245
  }
5890
6246
  panel.style.maxHeight = `${maxHeight}px`;
5891
6247
  } else {
@@ -6079,9 +6435,10 @@ function StylePanel({
6079
6435
  transform: "translateY(-50%)",
6080
6436
  fontSize: 10,
6081
6437
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
6082
- color: "#999",
6083
- pointerEvents: "none"
6084
- }
6438
+ color: "#999"
6439
+ },
6440
+ preferredUnit,
6441
+ onUnitCycle: cycleUnit
6085
6442
  }
6086
6443
  ) });
6087
6444
  }
@@ -6150,7 +6507,7 @@ function StylePanel({
6150
6507
  };
6151
6508
  const modificationCount = (_a = currentModification == null ? void 0 : currentModification.changes.length) != null ? _a : 0;
6152
6509
  const isCaptured = (_b = currentModification == null ? void 0 : currentModification.captured) != null ? _b : false;
6153
- const panelStyle2 = {
6510
+ const panelStyle2 = __spreadProps(__spreadValues({
6154
6511
  position: "fixed",
6155
6512
  top: positionRef.current.top,
6156
6513
  left: positionRef.current.left,
@@ -6158,9 +6515,8 @@ function StylePanel({
6158
6515
  maxHeight: positionRef.current.maxHeight,
6159
6516
  backgroundColor: "rgba(255, 255, 255, 0.85)",
6160
6517
  backdropFilter: "blur(32px)",
6161
- WebkitBackdropFilter: "blur(32px)",
6162
- border: "1px solid rgba(0,0,0,0.1)",
6163
- borderRadius: 0,
6518
+ WebkitBackdropFilter: "blur(32px)"
6519
+ }, POPMELT_BORDER), {
6164
6520
  zIndex: 1e4,
6165
6521
  fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
6166
6522
  fontSize: 12,
@@ -6169,22 +6525,16 @@ function StylePanel({
6169
6525
  flexDirection: "column",
6170
6526
  opacity: visible ? 1 : 0,
6171
6527
  transition: "opacity 150ms ease"
6172
- };
6173
- const cornerDotStyle = {
6174
- position: "absolute",
6175
- width: 2,
6176
- height: 2,
6177
- backgroundColor: "rgba(0, 0, 0, 0.25)",
6178
- pointerEvents: "none",
6179
- zIndex: 1
6180
- };
6528
+ });
6181
6529
  const headerStyle = {
6182
6530
  display: "flex",
6183
6531
  alignItems: "center",
6184
6532
  justifyContent: "space-between",
6533
+ margin: "3px 3px 0",
6185
6534
  padding: "8px 7px 8px 12px",
6186
6535
  borderBottom: "1px solid rgba(0,0,0,0.1)",
6187
- backgroundColor: "rgba(248, 250, 252, 0.6)"
6536
+ backgroundColor: "#f8fafc",
6537
+ cursor: dragState.current ? "grabbing" : "grab"
6188
6538
  };
6189
6539
  const sectionHeaderStyle = {
6190
6540
  display: "flex",
@@ -6210,198 +6560,193 @@ function StylePanel({
6210
6560
  color: "#64748b",
6211
6561
  flexShrink: 0
6212
6562
  };
6213
- return /* @__PURE__ */ jsxs6("div", { "data-devtools": "panel-wrapper", style: {
6563
+ return /* @__PURE__ */ jsx7("div", { "data-devtools": "panel-wrapper", style: {
6214
6564
  position: "fixed",
6215
- top: positionRef.current.top,
6216
- left: positionRef.current.left,
6565
+ top: positionRef.current.top + dragOffset.current.y,
6566
+ left: positionRef.current.left + dragOffset.current.x,
6217
6567
  zIndex: 1e4,
6218
6568
  pointerEvents: "none"
6219
- }, children: [
6220
- /* @__PURE__ */ jsxs6("div", { ref: panelRef, "data-devtools": "panel", style: __spreadProps(__spreadValues({}, panelStyle2), { position: "relative", top: 0, left: 0, zIndex: 0, pointerEvents: "auto" }), children: [
6221
- /* @__PURE__ */ jsxs6("div", { style: headerStyle, children: [
6222
- /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: 8, overflow: "hidden" }, children: [
6223
- /* @__PURE__ */ jsxs6("span", { style: {
6224
- fontWeight: 600,
6225
- fontSize: 11,
6226
- overflow: "hidden",
6227
- textOverflow: "ellipsis",
6228
- whiteSpace: "nowrap"
6229
- }, children: [
6230
- /* @__PURE__ */ jsx7("span", { style: { color: accentColor, fontSize: 13 }, children: "\u22B9" }),
6231
- " ",
6232
- elementInfo.tagName
6233
- ] }),
6234
- modificationCount > 0 && /* @__PURE__ */ jsx7("span", { style: {
6235
- backgroundColor: isCaptured ? "#999999" : accentColor,
6236
- color: "#fff",
6237
- fontSize: 9,
6238
- padding: "1px 4px",
6239
- borderRadius: 2
6240
- }, children: modificationCount })
6241
- ] }),
6242
- /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 4 }, children: [
6243
- modificationCount > 0 && /* @__PURE__ */ jsx7(
6244
- "button",
6569
+ }, children: /* @__PURE__ */ jsxs6("div", { ref: panelRef, "data-devtools": "panel", style: __spreadProps(__spreadValues({}, panelStyle2), { position: "relative", top: 0, left: 0, zIndex: 0, pointerEvents: "auto" }), onMouseEnter: () => onHover == null ? void 0 : onHover("element"), onMouseLeave: () => onHover == null ? void 0 : onHover(null), children: [
6570
+ /* @__PURE__ */ jsxs6("div", { style: headerStyle, onMouseDown: handleHeaderMouseDown, children: [
6571
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", alignItems: "center", gap: 8, overflow: "hidden" }, children: [
6572
+ /* @__PURE__ */ jsx7("span", { style: {
6573
+ fontWeight: 600,
6574
+ fontSize: 11,
6575
+ overflow: "hidden",
6576
+ textOverflow: "ellipsis",
6577
+ whiteSpace: "nowrap"
6578
+ }, children: elementInfo.tagName }),
6579
+ modificationCount > 0 && /* @__PURE__ */ jsx7("span", { style: {
6580
+ backgroundColor: isCaptured ? "#999999" : accentColor,
6581
+ color: "#fff",
6582
+ fontSize: 9,
6583
+ padding: "1px 4px",
6584
+ borderRadius: 2
6585
+ }, children: modificationCount })
6586
+ ] }),
6587
+ /* @__PURE__ */ jsxs6("div", { style: { display: "flex", gap: 4 }, children: [
6588
+ modificationCount > 0 && /* @__PURE__ */ jsx7(
6589
+ "button",
6590
+ {
6591
+ type: "button",
6592
+ onClick: handleReset,
6593
+ title: "Reset all changes",
6594
+ style: {
6595
+ display: "flex",
6596
+ alignItems: "center",
6597
+ justifyContent: "center",
6598
+ width: 24,
6599
+ height: 24,
6600
+ border: "none",
6601
+ background: "none",
6602
+ cursor: "pointer",
6603
+ color: "#64748b",
6604
+ borderRadius: 2
6605
+ },
6606
+ children: /* @__PURE__ */ jsx7(RotateCcw, { size: 14 })
6607
+ }
6608
+ ),
6609
+ /* @__PURE__ */ jsx7(
6610
+ "button",
6611
+ {
6612
+ type: "button",
6613
+ onClick: onClose,
6614
+ title: "Close",
6615
+ style: {
6616
+ display: "flex",
6617
+ alignItems: "center",
6618
+ justifyContent: "center",
6619
+ width: 24,
6620
+ height: 24,
6621
+ border: "none",
6622
+ background: "none",
6623
+ cursor: "pointer",
6624
+ color: "#64748b",
6625
+ borderRadius: 2
6626
+ },
6627
+ children: /* @__PURE__ */ jsx7(X, { size: 14 })
6628
+ }
6629
+ )
6630
+ ] })
6631
+ ] }),
6632
+ /* @__PURE__ */ jsxs6("div", { ref: panelContentRef, style: { flex: 1, overflowY: "auto", margin: "0 3px 3px" }, children: [
6633
+ /* @__PURE__ */ jsx7("div", { style: { opacity: activeColorDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: /* @__PURE__ */ jsx7(
6634
+ LayoutSection,
6635
+ {
6636
+ element,
6637
+ getValue,
6638
+ getOriginalValue,
6639
+ handleChange,
6640
+ isModified,
6641
+ onResetProperty: handleResetProperty,
6642
+ isCollapsed: false,
6643
+ onToggle: () => {
6644
+ },
6645
+ sectionHeaderStyle,
6646
+ activeDropdown,
6647
+ onDropdownChange: setActiveDropdown,
6648
+ panelContentRef,
6649
+ accentColor,
6650
+ onFieldHover: onHover,
6651
+ preferredUnit,
6652
+ onUnitCycle: cycleUnit
6653
+ }
6654
+ ) }),
6655
+ /* @__PURE__ */ jsx7("div", { style: { opacity: activeDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: /* @__PURE__ */ jsx7(
6656
+ TypographySection,
6657
+ {
6658
+ element,
6659
+ getValue,
6660
+ handleChange,
6661
+ isModified,
6662
+ onResetProperty: handleResetProperty,
6663
+ isCollapsed: false,
6664
+ onToggle: () => {
6665
+ },
6666
+ sectionHeaderStyle,
6667
+ accentColor,
6668
+ colorVariables,
6669
+ activeColorDropdown,
6670
+ onColorDropdownChange: setActiveColorDropdown,
6671
+ panelContentRef,
6672
+ preferredUnit,
6673
+ onUnitCycle: cycleUnit
6674
+ }
6675
+ ) }),
6676
+ SECTIONS.map((section, index) => {
6677
+ const isLast = index === SECTIONS.length - 1;
6678
+ const sectionHasActiveColorDropdown = activeColorDropdown && section.properties.some((p) => p.property === activeColorDropdown);
6679
+ const shouldFadeSection = hasActiveDropdown && !sectionHasActiveColorDropdown;
6680
+ return /* @__PURE__ */ jsxs6("div", { style: { borderBottom: isLast ? "none" : "1px solid rgba(0,0,0,0.08)", opacity: shouldFadeSection ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
6681
+ /* @__PURE__ */ jsx7("div", { style: sectionHeaderStyle, children: /* @__PURE__ */ jsx7("span", { children: section.name }) }),
6682
+ /* @__PURE__ */ jsx7("div", { style: { padding: "4px 0" }, children: section.properties.map((prop) => {
6683
+ const modified = isModified(prop.property);
6684
+ const shouldFadeRow = sectionHasActiveColorDropdown && prop.property !== activeColorDropdown;
6685
+ return /* @__PURE__ */ jsxs6("div", { style: __spreadProps(__spreadValues({}, propertyRowStyle), { opacity: shouldFadeRow ? 0.3 : 1, transition: "opacity 150ms ease" }), children: [
6686
+ /* @__PURE__ */ jsx7(
6687
+ "span",
6688
+ {
6689
+ onClick: modified ? () => handleResetProperty(prop.property) : void 0,
6690
+ title: modified ? "Click to reset" : void 0,
6691
+ style: __spreadProps(__spreadValues({}, labelStyle), {
6692
+ color: modified ? accentColor : "#64748b",
6693
+ fontWeight: modified ? 600 : 400,
6694
+ cursor: modified ? "pointer" : "default"
6695
+ }),
6696
+ children: prop.label
6697
+ }
6698
+ ),
6699
+ /* @__PURE__ */ jsx7("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: renderInput(prop) })
6700
+ ] }, prop.property);
6701
+ }) })
6702
+ ] }, section.name);
6703
+ }),
6704
+ /* @__PURE__ */ jsxs6("div", { style: { opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
6705
+ /* @__PURE__ */ jsx7("div", { style: sectionHeaderStyle, children: /* @__PURE__ */ jsx7("span", { children: "Raw CSS" }) }),
6706
+ /* @__PURE__ */ jsxs6("div", { style: { padding: "8px 12px" }, children: [
6707
+ /* @__PURE__ */ jsx7(
6708
+ "textarea",
6245
6709
  {
6246
- type: "button",
6247
- onClick: handleReset,
6248
- title: "Reset all changes",
6710
+ value: rawCss,
6711
+ onChange: (e) => setRawCss(e.target.value),
6712
+ placeholder: "property: value; ...",
6249
6713
  style: {
6250
- display: "flex",
6251
- alignItems: "center",
6252
- justifyContent: "center",
6253
- width: 24,
6254
- height: 24,
6255
- border: "none",
6256
- background: "none",
6257
- cursor: "pointer",
6258
- color: "#64748b",
6259
- borderRadius: 2
6260
- },
6261
- children: /* @__PURE__ */ jsx7(RotateCcw, { size: 14 })
6714
+ width: "100%",
6715
+ height: 60,
6716
+ padding: 8,
6717
+ fontSize: 11,
6718
+ fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
6719
+ border: "1px solid rgba(0,0,0,0.1)",
6720
+ borderRadius: 2,
6721
+ resize: "vertical",
6722
+ outline: "none"
6723
+ }
6262
6724
  }
6263
6725
  ),
6264
- /* @__PURE__ */ jsx7(
6726
+ rawCss.trim() && /* @__PURE__ */ jsx7(
6265
6727
  "button",
6266
6728
  {
6267
6729
  type: "button",
6268
- onClick: onClose,
6269
- title: "Close",
6730
+ onClick: handleApplyRawCss,
6270
6731
  style: {
6271
- display: "flex",
6272
- alignItems: "center",
6273
- justifyContent: "center",
6274
- width: 24,
6275
- height: 24,
6732
+ marginTop: 4,
6733
+ padding: "4px 8px",
6734
+ width: "100%",
6735
+ fontSize: 11,
6276
6736
  border: "none",
6277
- background: "none",
6737
+ borderRadius: 2,
6738
+ backgroundColor: accentColor,
6739
+ color: "#fff",
6278
6740
  cursor: "pointer",
6279
- color: "#64748b",
6280
- borderRadius: 2
6741
+ opacity: rawCss.trim() ? 1 : 0.5
6281
6742
  },
6282
- children: /* @__PURE__ */ jsx7(X, { size: 14 })
6743
+ children: "Apply"
6283
6744
  }
6284
6745
  )
6285
6746
  ] })
6286
- ] }),
6287
- /* @__PURE__ */ jsxs6("div", { ref: panelContentRef, style: { flex: 1, overflowY: "auto" }, children: [
6288
- /* @__PURE__ */ jsx7("div", { style: { opacity: activeColorDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: /* @__PURE__ */ jsx7(
6289
- LayoutSection,
6290
- {
6291
- element,
6292
- getValue,
6293
- getOriginalValue,
6294
- handleChange,
6295
- isModified,
6296
- onResetProperty: handleResetProperty,
6297
- isCollapsed: false,
6298
- onToggle: () => {
6299
- },
6300
- sectionHeaderStyle,
6301
- activeDropdown,
6302
- onDropdownChange: setActiveDropdown,
6303
- panelContentRef,
6304
- accentColor
6305
- }
6306
- ) }),
6307
- /* @__PURE__ */ jsx7("div", { style: { opacity: activeDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: /* @__PURE__ */ jsx7(
6308
- TypographySection,
6309
- {
6310
- element,
6311
- getValue,
6312
- handleChange,
6313
- isModified,
6314
- onResetProperty: handleResetProperty,
6315
- isCollapsed: false,
6316
- onToggle: () => {
6317
- },
6318
- sectionHeaderStyle,
6319
- accentColor,
6320
- colorVariables,
6321
- activeColorDropdown,
6322
- onColorDropdownChange: setActiveColorDropdown,
6323
- panelContentRef
6324
- }
6325
- ) }),
6326
- SECTIONS.map((section, index) => {
6327
- const isLast = index === SECTIONS.length - 1;
6328
- const sectionHasActiveColorDropdown = activeColorDropdown && section.properties.some((p) => p.property === activeColorDropdown);
6329
- const shouldFadeSection = hasActiveDropdown && !sectionHasActiveColorDropdown;
6330
- return /* @__PURE__ */ jsxs6("div", { style: { borderBottom: isLast ? "none" : "1px solid rgba(0,0,0,0.08)", opacity: shouldFadeSection ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
6331
- /* @__PURE__ */ jsx7("div", { style: sectionHeaderStyle, children: /* @__PURE__ */ jsx7("span", { children: section.name }) }),
6332
- /* @__PURE__ */ jsx7("div", { style: { padding: "4px 0" }, children: section.properties.map((prop) => {
6333
- const modified = isModified(prop.property);
6334
- const shouldFadeRow = sectionHasActiveColorDropdown && prop.property !== activeColorDropdown;
6335
- return /* @__PURE__ */ jsxs6("div", { style: __spreadProps(__spreadValues({}, propertyRowStyle), { opacity: shouldFadeRow ? 0.3 : 1, transition: "opacity 150ms ease" }), children: [
6336
- /* @__PURE__ */ jsx7(
6337
- "span",
6338
- {
6339
- onClick: modified ? () => handleResetProperty(prop.property) : void 0,
6340
- title: modified ? "Click to reset" : void 0,
6341
- style: __spreadProps(__spreadValues({}, labelStyle), {
6342
- color: modified ? accentColor : "#64748b",
6343
- fontWeight: modified ? 600 : 400,
6344
- cursor: modified ? "pointer" : "default"
6345
- }),
6346
- children: prop.label
6347
- }
6348
- ),
6349
- /* @__PURE__ */ jsx7("div", { style: { flex: 1, minWidth: 0, overflow: "hidden" }, children: renderInput(prop) })
6350
- ] }, prop.property);
6351
- }) })
6352
- ] }, section.name);
6353
- }),
6354
- /* @__PURE__ */ jsxs6("div", { style: { opacity: hasActiveDropdown ? 0.3 : 1, transition: "opacity 150ms ease" }, children: [
6355
- /* @__PURE__ */ jsx7("div", { style: sectionHeaderStyle, children: /* @__PURE__ */ jsx7("span", { children: "Raw CSS" }) }),
6356
- /* @__PURE__ */ jsxs6("div", { style: { padding: "8px 12px" }, children: [
6357
- /* @__PURE__ */ jsx7(
6358
- "textarea",
6359
- {
6360
- value: rawCss,
6361
- onChange: (e) => setRawCss(e.target.value),
6362
- placeholder: "property: value; ...",
6363
- style: {
6364
- width: "100%",
6365
- height: 60,
6366
- padding: 8,
6367
- fontSize: 11,
6368
- fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace',
6369
- border: "1px solid rgba(0,0,0,0.1)",
6370
- borderRadius: 2,
6371
- resize: "vertical",
6372
- outline: "none"
6373
- }
6374
- }
6375
- ),
6376
- rawCss.trim() && /* @__PURE__ */ jsx7(
6377
- "button",
6378
- {
6379
- type: "button",
6380
- onClick: handleApplyRawCss,
6381
- style: {
6382
- marginTop: 4,
6383
- padding: "4px 8px",
6384
- width: "100%",
6385
- fontSize: 11,
6386
- border: "none",
6387
- borderRadius: 2,
6388
- backgroundColor: accentColor,
6389
- color: "#fff",
6390
- cursor: "pointer",
6391
- opacity: rawCss.trim() ? 1 : 0.5
6392
- },
6393
- children: "Apply"
6394
- }
6395
- )
6396
- ] })
6397
- ] })
6398
6747
  ] })
6399
- ] }),
6400
- /* @__PURE__ */ jsx7("div", { style: __spreadProps(__spreadValues({}, cornerDotStyle), { top: -1, left: -1 }) }),
6401
- /* @__PURE__ */ jsx7("div", { style: __spreadProps(__spreadValues({}, cornerDotStyle), { top: -1, right: -1 }) }),
6402
- /* @__PURE__ */ jsx7("div", { style: __spreadProps(__spreadValues({}, cornerDotStyle), { bottom: -1, left: -1 }) }),
6403
- /* @__PURE__ */ jsx7("div", { style: __spreadProps(__spreadValues({}, cornerDotStyle), { bottom: -1, right: -1 }) })
6404
- ] });
6748
+ ] })
6749
+ ] }) });
6405
6750
  }
6406
6751
 
6407
6752
  // src/components/SwipeHints.tsx
@@ -6672,7 +7017,7 @@ function TextHandles({ element, fontSize, lineHeight, accentColor, hoveredProper
6672
7017
  // src/components/AnnotationCanvas.tsx
6673
7018
  import { Fragment as Fragment6, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
6674
7019
  var HANDLE_SIZE = 8;
6675
- var PADDING_SNAP_STEPS = [0, 1, 2, 4, 8, 12, 16, 20, 24, 28, 32];
7020
+ var PADDING_SNAP_STEPS2 = [0, 1, 2, 4, 8, 12, 16, 20, 24, 28, 32];
6676
7021
  var BADGE_HEIGHT = 22;
6677
7022
  var ACTIVE_TEXT_STORAGE_KEY = "devtools-active-text";
6678
7023
  function calculateLinkedPosition(rect, anchor, stackOffset = 0) {
@@ -6706,6 +7051,7 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
6706
7051
  const handDragRef = useRef5({ isDragging: false, side: null, startX: 0, startY: 0, original: { top: 0, right: 0, bottom: 0, left: 0 }, element: null, elementInfo: null, selector: null, durableSelector: null });
6707
7052
  const [handHoveredElement, setHandHoveredElement] = useState12(null);
6708
7053
  const [handHoveredSide, setHandHoveredSide] = useState12(null);
7054
+ const [stylePanelHint, setStylePanelHint] = useState12(null);
6709
7055
  const [handDragging, setHandDragging] = useState12(null);
6710
7056
  const handCursorRef = useRef5({ x: 0, y: 0 });
6711
7057
  const [handCursorPos, setHandCursorPos] = useState12({ x: 0, y: 0 });
@@ -7406,9 +7752,9 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
7406
7752
  setActiveText(null);
7407
7753
  }, [activeText, dispatch]);
7408
7754
  const snapPadding = useCallback5((value) => {
7409
- for (let i = 0; i < PADDING_SNAP_STEPS.length - 1; i++) {
7410
- const lo = PADDING_SNAP_STEPS[i];
7411
- const hi = PADDING_SNAP_STEPS[i + 1];
7755
+ for (let i = 0; i < PADDING_SNAP_STEPS2.length - 1; i++) {
7756
+ const lo = PADDING_SNAP_STEPS2[i];
7757
+ const hi = PADDING_SNAP_STEPS2[i + 1];
7412
7758
  if (value <= (lo + hi) / 2) return lo;
7413
7759
  if (value < hi) return hi;
7414
7760
  }
@@ -7438,6 +7784,12 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
7438
7784
  (e) => {
7439
7785
  var _a2;
7440
7786
  if (!state.isAnnotating) return;
7787
+ if (state.inspectedElement && state.activeTool === "hand" && !("button" in e && e.button === 2)) {
7788
+ e.preventDefault();
7789
+ e.stopPropagation();
7790
+ dispatch({ type: "SELECT_ELEMENT", payload: null });
7791
+ return;
7792
+ }
7441
7793
  const point = getPoint(e);
7442
7794
  const isShiftClick = "shiftKey" in e && e.shiftKey;
7443
7795
  if (state.activeTool === "inspector") {
@@ -7663,7 +8015,7 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
7663
8015
  setIsDrawing(true);
7664
8016
  dispatch({ type: "START_PATH", payload: point });
7665
8017
  },
7666
- [state.isAnnotating, state.activeTool, state.annotations, activeText, selectedAnnotationIds, hoveredElement, handHoveredElement, handHoveredSide, radiusHoveredElement, radiusHoveredCorner, gapHoveredElement, gapHoveredAxis, gapIsAuto, textHoveredElement, textHoveredProperty, getPoint, findAnnotationAtPoint, findHandleAtPoint, dispatch, selectAnnotation, clearSelection, commitActiveText]
8018
+ [state.isAnnotating, state.activeTool, state.inspectedElement, state.annotations, activeText, selectedAnnotationIds, hoveredElement, handHoveredElement, handHoveredSide, radiusHoveredElement, radiusHoveredCorner, gapHoveredElement, gapHoveredAxis, gapIsAuto, textHoveredElement, textHoveredProperty, getPoint, findAnnotationAtPoint, findHandleAtPoint, dispatch, selectAnnotation, clearSelection, commitActiveText]
7667
8019
  );
7668
8020
  const handlePointerMove = useCallback5(
7669
8021
  (e) => {
@@ -8268,7 +8620,8 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
8268
8620
  if (gapDragRef.current.isDragging) {
8269
8621
  const drag = gapDragRef.current;
8270
8622
  const el = drag.element;
8271
- if (!drag.hasMoved && el && drag.selector && drag.elementInfo) {
8623
+ const isRightClick = "button" in e && e.button === 2;
8624
+ if (!drag.hasMoved && !isRightClick && el && drag.selector && drag.elementInfo) {
8272
8625
  if (el instanceof HTMLElement) {
8273
8626
  el.style.removeProperty("transition");
8274
8627
  }
@@ -8489,17 +8842,18 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
8489
8842
  }
8490
8843
  }, [commitActiveText]);
8491
8844
  const handleContextMenu = useCallback5((e) => {
8492
- if (state.activeTool !== "inspector" || !state.isAnnotating) return;
8845
+ if (state.activeTool !== "hand" || !state.isAnnotating) return;
8493
8846
  e.preventDefault();
8494
- if (hoveredElement && !isElementInFlight(hoveredElement)) {
8495
- const info = extractElementInfo(hoveredElement);
8496
- const selector = getUniqueSelector(hoveredElement);
8847
+ const target = handHoveredElement || gapHoveredElement || radiusHoveredElement || textHoveredElement;
8848
+ if (target && !isElementInFlight(target)) {
8849
+ const info = extractElementInfo(target);
8850
+ const selector = getUniqueSelector(target);
8497
8851
  dispatch({
8498
8852
  type: "SELECT_ELEMENT",
8499
- payload: { el: hoveredElement, info: __spreadProps(__spreadValues({}, info), { selector }) }
8853
+ payload: { el: target, info: __spreadProps(__spreadValues({}, info), { selector }) }
8500
8854
  });
8501
8855
  }
8502
- }, [state.activeTool, state.isAnnotating, hoveredElement, dispatch, isElementInFlight]);
8856
+ }, [state.activeTool, state.isAnnotating, handHoveredElement, gapHoveredElement, radiusHoveredElement, textHoveredElement, dispatch, isElementInFlight]);
8503
8857
  useEffect15(() => {
8504
8858
  const linkedAnnotations = state.annotations.filter((a) => a.linkedSelector);
8505
8859
  if (linkedAnnotations.length === 0) return;
@@ -8811,28 +9165,28 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
8811
9165
  refreshKey: textDragging ? textDragging.fontSize + textDragging.lineHeight * 1e3 : 0
8812
9166
  }
8813
9167
  ),
8814
- state.activeTool === "inspector" && state.isAnnotating && /* @__PURE__ */ jsxs9(Fragment6, { children: [
8815
- hoveredElement && !state.inspectedElement && (() => {
8816
- const hasLinkedAnnotation = !!pendingLinkedText || !!(activeText == null ? void 0 : activeText.linkedSelector) || state.annotations.some((a) => {
8817
- if (!a.linkedSelector) return false;
8818
- try {
8819
- return hoveredElement.matches(a.linkedSelector);
8820
- } catch (e) {
8821
- return false;
8822
- }
8823
- });
8824
- return /* @__PURE__ */ jsx10(
8825
- ElementHighlight,
8826
- {
8827
- element: hoveredElement,
8828
- isSelected: false,
8829
- elementInfo: hoveredElementInfo,
8830
- color: state.activeColor,
8831
- hideTooltip: hasLinkedAnnotation
8832
- }
8833
- );
8834
- })(),
8835
- state.inspectedElement && (() => {
9168
+ state.activeTool === "inspector" && state.isAnnotating && /* @__PURE__ */ jsx10(Fragment6, { children: hoveredElement && !state.inspectedElement && (() => {
9169
+ const hasLinkedAnnotation = !!pendingLinkedText || !!(activeText == null ? void 0 : activeText.linkedSelector) || state.annotations.some((a) => {
9170
+ if (!a.linkedSelector) return false;
9171
+ try {
9172
+ return hoveredElement.matches(a.linkedSelector);
9173
+ } catch (e) {
9174
+ return false;
9175
+ }
9176
+ });
9177
+ return /* @__PURE__ */ jsx10(
9178
+ ElementHighlight,
9179
+ {
9180
+ element: hoveredElement,
9181
+ isSelected: false,
9182
+ elementInfo: hoveredElementInfo,
9183
+ color: state.activeColor,
9184
+ hideTooltip: hasLinkedAnnotation
9185
+ }
9186
+ );
9187
+ })() }),
9188
+ state.activeTool === "hand" && state.isAnnotating && state.inspectedElement && /* @__PURE__ */ jsxs9(Fragment6, { children: [
9189
+ stylePanelHint && stylePanelHint !== "padding" && stylePanelHint !== "gap" && (() => {
8836
9190
  var _a2;
8837
9191
  const annotationGroupCount = new Set(
8838
9192
  state.annotations.map((a) => a.groupId || a.id)
@@ -8855,7 +9209,27 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
8855
9209
  }
8856
9210
  );
8857
9211
  })(),
8858
- state.inspectedElement && /* @__PURE__ */ jsx10(
9212
+ stylePanelHint === "padding" && /* @__PURE__ */ jsx10(
9213
+ PaddingHandles,
9214
+ {
9215
+ element: state.inspectedElement.el,
9216
+ padding: getComputedPadding(state.inspectedElement.el),
9217
+ accentColor: state.activeColor,
9218
+ hoveredSide: null,
9219
+ draggingSide: null
9220
+ }
9221
+ ),
9222
+ stylePanelHint === "gap" && /* @__PURE__ */ jsx10(
9223
+ GapHandles,
9224
+ {
9225
+ element: state.inspectedElement.el,
9226
+ gap: getComputedGap(state.inspectedElement.el),
9227
+ accentColor: state.activeColor,
9228
+ hoveredAxis: null,
9229
+ draggingAxis: null
9230
+ }
9231
+ ),
9232
+ /* @__PURE__ */ jsx10(
8859
9233
  StylePanel,
8860
9234
  {
8861
9235
  element: state.inspectedElement.el,
@@ -8864,6 +9238,7 @@ function AnnotationCanvas({ state, dispatch, onScreenshot, inFlightAnnotationIds
8864
9238
  styleModifications: state.styleModifications,
8865
9239
  dispatch,
8866
9240
  onClose: () => dispatch({ type: "SELECT_ELEMENT", payload: null }),
9241
+ onHover: setStylePanelHint,
8867
9242
  accentColor: state.activeColor
8868
9243
  }
8869
9244
  )
@@ -9792,13 +10167,6 @@ function useLocalStorageBatch(isExpanded, state, hasRestoredAnnotations, hasActi
9792
10167
  ]);
9793
10168
  }
9794
10169
 
9795
- // src/styles/border.ts
9796
- var POPMELT_BORDER = {
9797
- borderWidth: 4,
9798
- borderStyle: "solid",
9799
- borderImage: "repeating-linear-gradient(-45deg, rgba(0,0,0,1) 0, rgba(0,0,0,1) 1px, rgba(0,0,0,0.1) 1px, rgba(0,0,0,0.1) 4px) 4"
9800
- };
9801
-
9802
10170
  // src/components/ToolButton.tsx
9803
10171
  import { jsx as jsx11 } from "react/jsx-runtime";
9804
10172
  var colors = {
@@ -9919,13 +10287,15 @@ var TOOL_GUIDANCE = {
9919
10287
  "Corners \u2192 rounding",
9920
10288
  "Right of text \u2192 font size",
9921
10289
  "Below text \u2192 line height",
9922
- "Click a spacing handle to cycle distribution"
10290
+ "Click a spacing handle to cycle distribution",
10291
+ "Right-click \u2192 inspect element styles"
9923
10292
  ],
9924
10293
  keys: [
9925
10294
  { key: "H", desc: "Select tool" },
9926
10295
  { key: "Shift", desc: "Snap to scale" },
9927
10296
  { key: "\u2325 + swipe", desc: "Cycle justify / flip direction" },
9928
10297
  { key: "\u21E7 + swipe", desc: "Cycle align-items" },
10298
+ { key: "Right-click", desc: "Inspect styles" },
9929
10299
  { key: "Esc", desc: "Deselect" },
9930
10300
  { key: "\u2318 Enter", desc: "Hand off to your AI", accent: true }
9931
10301
  ]
@@ -10951,7 +11321,7 @@ function AnnotationToolbar({
10951
11321
  }
10952
11322
 
10953
11323
  // src/components/BridgeStatusPanel.tsx
10954
- import { useEffect as useEffect18, useRef as useRef8, useState as useState14 } from "react";
11324
+ import { useCallback as useCallback7, useEffect as useEffect18, useRef as useRef8, useState as useState14 } from "react";
10955
11325
  import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
10956
11326
  var stackContainerStyle = {
10957
11327
  position: "fixed",
@@ -10960,21 +11330,22 @@ var stackContainerStyle = {
10960
11330
  zIndex: 9999,
10961
11331
  display: "flex",
10962
11332
  flexDirection: "column",
10963
- gap: 1
11333
+ gap: 10
10964
11334
  };
10965
- var rowStyle = {
11335
+ var rowStyle = __spreadProps(__spreadValues({
10966
11336
  display: "flex",
10967
11337
  alignItems: "center",
10968
11338
  gap: 6,
10969
11339
  padding: "4px 10px",
10970
- backgroundColor: "rgba(255, 255, 255, 0.85)",
10971
- border: "1px solid rgba(0, 0, 0, 0.1)",
11340
+ backgroundColor: "rgba(255, 255, 255, 0.85)"
11341
+ }, POPMELT_BORDER), {
10972
11342
  backdropFilter: "blur(32px)",
10973
11343
  fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
10974
11344
  fontSize: 11,
10975
11345
  color: "#1f2937",
10976
- whiteSpace: "nowrap"
10977
- };
11346
+ whiteSpace: "nowrap",
11347
+ transition: "transform 200ms ease, opacity 200ms ease"
11348
+ });
10978
11349
  function formatStepText(events) {
10979
11350
  var _a;
10980
11351
  const toolEvents = events.filter((e) => e.type === "tool_use");
@@ -11065,6 +11436,60 @@ function ErrorDot() {
11065
11436
  }
11066
11437
  );
11067
11438
  }
11439
+ function SwipeDismiss({ onDismiss, children }) {
11440
+ const ref = useRef8(null);
11441
+ const drag = useRef8(null);
11442
+ const [offset, setOffset] = useState14(0);
11443
+ const [dismissed, setDismissed] = useState14(false);
11444
+ const THRESHOLD = 60;
11445
+ const onPointerDown = useCallback7((e) => {
11446
+ var _a;
11447
+ drag.current = { startX: e.clientX, startY: e.clientY, locked: false };
11448
+ (_a = ref.current) == null ? void 0 : _a.setPointerCapture(e.pointerId);
11449
+ }, []);
11450
+ const onPointerMove = useCallback7((e) => {
11451
+ const d = drag.current;
11452
+ if (!d) return;
11453
+ const dx = e.clientX - d.startX;
11454
+ const dy = e.clientY - d.startY;
11455
+ if (!d.locked) {
11456
+ if (Math.abs(dx) < 4 && Math.abs(dy) < 4) return;
11457
+ d.locked = true;
11458
+ if (Math.abs(dy) > Math.abs(dx)) {
11459
+ drag.current = null;
11460
+ return;
11461
+ }
11462
+ }
11463
+ setOffset(Math.max(0, dx));
11464
+ }, []);
11465
+ const onPointerUp = useCallback7(() => {
11466
+ if (!drag.current) return;
11467
+ drag.current = null;
11468
+ if (offset >= THRESHOLD) {
11469
+ setDismissed(true);
11470
+ setTimeout(onDismiss, 200);
11471
+ } else {
11472
+ setOffset(0);
11473
+ }
11474
+ }, [offset, onDismiss]);
11475
+ return /* @__PURE__ */ jsx13(
11476
+ "div",
11477
+ {
11478
+ ref,
11479
+ onPointerDown,
11480
+ onPointerMove,
11481
+ onPointerUp,
11482
+ onPointerCancel: onPointerUp,
11483
+ style: {
11484
+ transform: dismissed ? "translateX(120%)" : `translateX(${offset}px)`,
11485
+ opacity: dismissed ? 0 : 1 - offset / (THRESHOLD * 3),
11486
+ transition: drag.current ? "none" : "transform 200ms ease, opacity 200ms ease",
11487
+ touchAction: "pan-y"
11488
+ },
11489
+ children
11490
+ }
11491
+ );
11492
+ }
11068
11493
  function BridgeEventStack({ bridge, inFlightJobs, isVisible: isVisible2, onHover, clearSignal }) {
11069
11494
  const [entries, setEntries] = useState14([]);
11070
11495
  useEffect18(() => {
@@ -11118,20 +11543,20 @@ function BridgeEventStack({ bridge, inFlightJobs, isVisible: isVisible2, onHover
11118
11543
  onMouseLeave: () => onHover(false),
11119
11544
  children: entries.map((entry) => {
11120
11545
  const label = entry.status === "working" ? formatStepText(bridge.events.filter((e) => e.data.jobId === entry.jobId)) : entry.status === "queued" ? "Queued" : entry.status === "done" ? entry.doneLabel || "Done" : "Error";
11121
- return /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
11546
+ return /* @__PURE__ */ jsx13(SwipeDismiss, { onDismiss: () => setEntries((prev) => prev.filter((e) => e.jobId !== entry.jobId)), children: /* @__PURE__ */ jsxs11("div", { style: rowStyle, children: [
11122
11547
  entry.status === "working" && /* @__PURE__ */ jsx13(DotSpinner, { color: entry.color }),
11123
11548
  entry.status === "queued" && /* @__PURE__ */ jsx13(ColorSquare, { color: entry.color }),
11124
11549
  entry.status === "done" && /* @__PURE__ */ jsx13(Checkmark, { color: entry.color }),
11125
11550
  entry.status === "error" && /* @__PURE__ */ jsx13(ErrorDot, {}),
11126
11551
  /* @__PURE__ */ jsx13("span", { style: { color: entry.status === "queued" ? "#9ca3af" : "#1f2937" }, children: label })
11127
- ] }, entry.jobId);
11552
+ ] }) }, entry.jobId);
11128
11553
  })
11129
11554
  }
11130
11555
  );
11131
11556
  }
11132
11557
 
11133
11558
  // src/components/ThreadPanel.tsx
11134
- import { useCallback as useCallback7, useEffect as useEffect19, useRef as useRef9, useState as useState15 } from "react";
11559
+ import { useCallback as useCallback8, useEffect as useEffect19, useRef as useRef9, useState as useState15 } from "react";
11135
11560
 
11136
11561
  // src/utils/threadMarkdown.tsx
11137
11562
  import { Fragment as Fragment9, jsx as jsx14 } from "react/jsx-runtime";
@@ -11465,7 +11890,7 @@ function ThreadPanel({
11465
11890
  const scrollRef = useRef9(null);
11466
11891
  const panelRef = useRef9(null);
11467
11892
  const prevStreamingRef = useRef9(isStreaming);
11468
- const fetchThread = useCallback7(() => {
11893
+ const fetchThread = useCallback8(() => {
11469
11894
  fetch(`${bridgeUrl}/thread/${threadId}`).then((r) => r.json()).then((data) => {
11470
11895
  var _a2;
11471
11896
  setMessages((_a2 = data.messages) != null ? _a2 : []);
@@ -11494,7 +11919,7 @@ function ThreadPanel({
11494
11919
  document.addEventListener("keydown", handleEscape);
11495
11920
  return () => document.removeEventListener("keydown", handleEscape);
11496
11921
  }, [onClose]);
11497
- const handleSubmit = useCallback7(() => {
11922
+ const handleSubmit = useCallback8(() => {
11498
11923
  if (!replyText.trim() || !onReply) return;
11499
11924
  const text = replyText.trim();
11500
11925
  setMessages((prev) => [...prev, {
@@ -11506,7 +11931,7 @@ function ThreadPanel({
11506
11931
  onReply(threadId, text);
11507
11932
  setReplyText("");
11508
11933
  }, [replyText, threadId, onReply]);
11509
- const handleKeyDown = useCallback7((e) => {
11934
+ const handleKeyDown = useCallback8((e) => {
11510
11935
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
11511
11936
  e.preventDefault();
11512
11937
  handleSubmit();
@@ -11782,6 +12207,7 @@ var CLAUDE_MODELS = [
11782
12207
  ];
11783
12208
  var CODEX_MODELS = [
11784
12209
  { id: "gpt-5.3-codex", label: "Codex 5.3" },
12210
+ { id: "gpt-5.3-codex-spark", label: "Spark 5.3" },
11785
12211
  { id: "gpt-5.1-codex-mini", label: "Mini 5.1" }
11786
12212
  ];
11787
12213
  function equivalentModelIndex(fromProvider, toProvider, currentIndex) {
@@ -11826,7 +12252,7 @@ function PopmeltProvider({
11826
12252
  }, [availableProviders, provider]);
11827
12253
  const models = provider === "codex" ? CODEX_MODELS : CLAUDE_MODELS;
11828
12254
  const currentModel = (_a = models[modelIndex]) != null ? _a : models[0];
11829
- const handleProviderChange = useCallback8((newProvider) => {
12255
+ const handleProviderChange = useCallback9((newProvider) => {
11830
12256
  const oldProvider = provider;
11831
12257
  setProvider(newProvider);
11832
12258
  localStorage.setItem(PROVIDER_STORAGE_KEY, newProvider);
@@ -11834,7 +12260,7 @@ function PopmeltProvider({
11834
12260
  setModelIndex(newIndex);
11835
12261
  localStorage.setItem(MODEL_STORAGE_KEY, String(newIndex));
11836
12262
  }, [provider, modelIndex]);
11837
- const handleModelChange = useCallback8((newIndex) => {
12263
+ const handleModelChange = useCallback9((newIndex) => {
11838
12264
  setModelIndex(newIndex);
11839
12265
  localStorage.setItem(MODEL_STORAGE_KEY, String(newIndex));
11840
12266
  }, []);
@@ -12026,7 +12452,7 @@ function PopmeltProvider({
12026
12452
  setInFlightJobs({});
12027
12453
  }
12028
12454
  }, [bridge.status]);
12029
- const handleScreenshot = useCallback8(async () => {
12455
+ const handleScreenshot = useCallback9(async () => {
12030
12456
  const canvas = document.getElementById("devtools-canvas");
12031
12457
  if (!canvas) return false;
12032
12458
  const blobs = await captureScreenshot(document.body, canvas, state.annotations);
@@ -12037,7 +12463,7 @@ function PopmeltProvider({
12037
12463
  }
12038
12464
  return success;
12039
12465
  }, [state.annotations, state.styleModifications, dispatch]);
12040
- const handlePlanGoal = useCallback8(async (goal) => {
12466
+ const handlePlanGoal = useCallback9(async (goal) => {
12041
12467
  var _a2;
12042
12468
  try {
12043
12469
  const activeAnnotations = state.annotations.filter((a) => {
@@ -12085,7 +12511,7 @@ function PopmeltProvider({
12085
12511
  return false;
12086
12512
  }
12087
12513
  }, [bridgeUrl, provider, currentModel.id, state.annotations, state.styleModifications, state.inspectedElement, dispatch]);
12088
- const materializePlan = useCallback8(async (tasks) => {
12514
+ const materializePlan = useCallback9(async (tasks) => {
12089
12515
  const planId = activePlan == null ? void 0 : activePlan.planId;
12090
12516
  if (!planId) return;
12091
12517
  const baseHueMatch = state.activeColor.match(/oklch\([^)]*\s+([\d.]+)\s*\)/);
@@ -12130,7 +12556,7 @@ function PopmeltProvider({
12130
12556
  }
12131
12557
  }
12132
12558
  }, [activePlan == null ? void 0 : activePlan.planId, state.activeColor, dispatch]);
12133
- const handleSendToClaude = useCallback8(async () => {
12559
+ const handleSendToClaude = useCallback9(async () => {
12134
12560
  const canvas = document.getElementById("devtools-canvas");
12135
12561
  if (!canvas) return false;
12136
12562
  const activeAnnotations = state.annotations.filter((a) => {
@@ -12170,7 +12596,7 @@ function PopmeltProvider({
12170
12596
  return false;
12171
12597
  }
12172
12598
  }, [state.annotations, state.styleModifications, state.activeColor, dispatch, bridgeUrl, provider, currentModel.id]);
12173
- const handleReply = useCallback8(async (threadId, reply) => {
12599
+ const handleReply = useCallback9(async (threadId, reply) => {
12174
12600
  try {
12175
12601
  const hexColor = cssColorToHex(state.activeColor);
12176
12602
  const { jobId } = await sendReplyToBridge(threadId, reply, bridgeUrl, hexColor, provider, currentModel.id);
@@ -12205,7 +12631,7 @@ function PopmeltProvider({
12205
12631
  console.error("[Pare] Failed to send reply:", err);
12206
12632
  }
12207
12633
  }, [state.activeColor, state.annotations, bridgeUrl, bridge.dismissQuestion, dispatch, provider, currentModel.id]);
12208
- const handleApprovePlan = useCallback8(async () => {
12634
+ const handleApprovePlan = useCallback9(async () => {
12209
12635
  if (!activePlan || !activePlan.planId) return;
12210
12636
  try {
12211
12637
  await approvePlan(activePlan.planId, bridgeUrl);
@@ -12278,7 +12704,7 @@ function PopmeltProvider({
12278
12704
  console.error("[Pare] Failed to approve plan:", err);
12279
12705
  }
12280
12706
  }, [activePlan, state.annotations, state.activeColor, bridgeUrl, provider, currentModel.id, dispatch]);
12281
- const handleDismissPlan = useCallback8(() => {
12707
+ const handleDismissPlan = useCallback9(() => {
12282
12708
  setActivePlan(null);
12283
12709
  if (activePlan == null ? void 0 : activePlan.planId) {
12284
12710
  const planAnnotations = state.annotations.filter((a) => a.planId === activePlan.planId);
@@ -12376,7 +12802,7 @@ function PopmeltProvider({
12376
12802
  const values = Object.values(inFlightJobs);
12377
12803
  return values.length > 0 ? values[values.length - 1].color : void 0;
12378
12804
  }, [bridge.activeJobId, inFlightJobs]);
12379
- const handleViewThread = useCallback8((threadId) => {
12805
+ const handleViewThread = useCallback9((threadId) => {
12380
12806
  setOpenThreadId(threadId);
12381
12807
  }, []);
12382
12808
  const threadActiveJobId = useMemo4(() => {
@@ -12389,7 +12815,7 @@ function PopmeltProvider({
12389
12815
  const [eventStreamVisible, setEventStreamVisible] = useState16(false);
12390
12816
  const [clearSignal, setClearSignal] = useState16(0);
12391
12817
  const hideTimeoutRef = useRef10(null);
12392
- const handleEventStreamHover = useCallback8((hovering) => {
12818
+ const handleEventStreamHover = useCallback9((hovering) => {
12393
12819
  if (hovering) {
12394
12820
  if (hideTimeoutRef.current) {
12395
12821
  clearTimeout(hideTimeoutRef.current);
@@ -12403,7 +12829,7 @@ function PopmeltProvider({
12403
12829
  }, 150);
12404
12830
  }
12405
12831
  }, []);
12406
- const handleClearEventStream = useCallback8(() => {
12832
+ const handleClearEventStream = useCallback9(() => {
12407
12833
  setClearSignal((s) => s + 1);
12408
12834
  bridge.clearEvents();
12409
12835
  }, [bridge.clearEvents]);