@lijinmei-810/dev-inspector 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,2551 @@
1
+ // src/DevInspector.tsx
2
+ import { useState, useEffect, useRef, useCallback } from "react";
3
+ import { CircleHelp } from "lucide-react";
4
+
5
+ // src/DevInspectorProvider.tsx
6
+ import { createContext, useContext } from "react";
7
+
8
+ // src/config.ts
9
+ var FALLBACK_TOKENS = {
10
+ colorPalette: [],
11
+ tokenLabels: {},
12
+ radiusPresets: [
13
+ { label: "\u65E0", sub: "", value: "0px", token: "" },
14
+ { label: "S", sub: "4px", value: "4px", token: "" },
15
+ { label: "M", sub: "8px", value: "8px", token: "" },
16
+ { label: "L", sub: "12px", value: "12px", token: "" },
17
+ { label: "XL", sub: "16px", value: "16px", token: "" },
18
+ { label: "XXL", sub: "24px", value: "24px", token: "" }
19
+ ],
20
+ spaceSteps: [
21
+ { label: "\u65E0", size: "", val: "0px" },
22
+ { label: "S", size: "4px", val: "4px" },
23
+ { label: "M", size: "8px", val: "8px" },
24
+ { label: "L", size: "12px", val: "12px" },
25
+ { label: "XL", size: "16px", val: "16px" },
26
+ { label: "XXL", size: "24px", val: "24px" }
27
+ ],
28
+ borderWidthSteps: ["0px", "1px", "2px", "4px"],
29
+ fontSizeOptions: [],
30
+ fontWeightOptions: [
31
+ { label: "\u5E38", value: "400" },
32
+ { label: "\u4E2D", value: "500" },
33
+ { label: "\u7C97", value: "600" },
34
+ { label: "\u9ED1", value: "700" }
35
+ ],
36
+ shadowTokens: [],
37
+ typographyTokens: []
38
+ };
39
+ var defaultDevInspectorConfig = {
40
+ rootId: "dev-inspector-root",
41
+ endpoints: {
42
+ applyCss: "/__dev/apply-css",
43
+ submitStyleIntent: "/__dev/submit-style-intent",
44
+ styleIntents: "/__dev/style-intents",
45
+ deleteStyleIntent: "/__dev/style-intents/delete",
46
+ handoff: "/__dev/handoff",
47
+ reveal: "/__dev/reveal"
48
+ },
49
+ tokens: FALLBACK_TOKENS
50
+ };
51
+ function mergeDevInspectorConfig(overrides) {
52
+ return {
53
+ ...defaultDevInspectorConfig,
54
+ ...overrides,
55
+ endpoints: {
56
+ ...defaultDevInspectorConfig.endpoints,
57
+ ...overrides?.endpoints
58
+ },
59
+ tokens: {
60
+ ...defaultDevInspectorConfig.tokens,
61
+ ...overrides?.tokens
62
+ }
63
+ };
64
+ }
65
+
66
+ // src/DevInspectorProvider.tsx
67
+ import { jsx } from "react/jsx-runtime";
68
+ var DevInspectorConfigContext = createContext(defaultDevInspectorConfig);
69
+ function DevInspectorProvider({
70
+ children,
71
+ config
72
+ }) {
73
+ return /* @__PURE__ */ jsx(DevInspectorConfigContext.Provider, { value: mergeDevInspectorConfig(config), children });
74
+ }
75
+ function useDevInspectorConfig() {
76
+ return useContext(DevInspectorConfigContext);
77
+ }
78
+
79
+ // src/DevInspector.tsx
80
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
81
+ function calcDropPos(rect, dropHeight = 280) {
82
+ const spaceBelow = window.innerHeight - rect.bottom;
83
+ const top = spaceBelow >= dropHeight + 8 ? rect.bottom + 4 : rect.top - dropHeight - 4;
84
+ return { top: Math.max(8, top), left: rect.left };
85
+ }
86
+ var COLOR_PROPS = [
87
+ { label: "\u80CC\u666F\u8272", prop: "background-color" }
88
+ ];
89
+ function getColorLabel(val, colorPalette) {
90
+ for (const g of colorPalette) {
91
+ const found = g.colors.find((c) => c.val === val);
92
+ if (found) return `${g.group}\xB7${found.label}`;
93
+ }
94
+ return null;
95
+ }
96
+ function getTypographyToken(fontSize, fontWeight, color, typographyTokens) {
97
+ const normalizedColor = normalizeColor(color);
98
+ return typographyTokens.find(
99
+ (token) => token.fontSize === fontSize.trim() && token.fontWeight === fontWeight.trim() && normalizeColor(token.color) === normalizedColor
100
+ ) ?? null;
101
+ }
102
+ var BORDER_STYLE_OPTIONS = [
103
+ { label: "\u2500", value: "solid", title: "\u5B9E\u7EBF" },
104
+ { label: "\u2504", value: "dashed", title: "\u865A\u7EBF" },
105
+ { label: "\u22EF", value: "dotted", title: "\u70B9\u7EBF" },
106
+ { label: "\u2715", value: "none", title: "\u65E0" }
107
+ ];
108
+ var SIZE_OPTIONS = [
109
+ { label: "Fill", mode: "fill", value: "100%" },
110
+ { label: "Hug", mode: "hug", value: "fit-content" },
111
+ { label: "Fixed", mode: "fixed" }
112
+ ];
113
+ function rgbToHex(rgb) {
114
+ const m = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
115
+ if (!m) return rgb;
116
+ return "#" + [m[1], m[2], m[3]].map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
117
+ }
118
+ function normalizeColor(val) {
119
+ const m = val.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
120
+ if (m) return "#" + [m[1], m[2], m[3]].map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
121
+ return val.trim();
122
+ }
123
+ function formatColorDisplay(val) {
124
+ const m = val.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/);
125
+ if (m) {
126
+ const hex = "#" + [m[1], m[2], m[3]].map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
127
+ const alpha = Math.round(parseFloat(m[4]) * 100);
128
+ return alpha < 100 ? `${hex} / ${alpha}%` : hex;
129
+ }
130
+ return val;
131
+ }
132
+ var shadowValueCache = /* @__PURE__ */ new Map();
133
+ var _copiedStyles = [];
134
+ function canonicalizeShadowValue(val) {
135
+ const raw = (val || "none").trim() || "none";
136
+ if (shadowValueCache.has(raw)) return shadowValueCache.get(raw);
137
+ if (typeof document === "undefined" || !document.body) return raw;
138
+ const probe = document.createElement("div");
139
+ probe.style.boxShadow = raw;
140
+ probe.style.position = "fixed";
141
+ probe.style.opacity = "0";
142
+ probe.style.pointerEvents = "none";
143
+ document.body.appendChild(probe);
144
+ const normalized = getComputedStyle(probe).boxShadow.trim() || raw;
145
+ probe.remove();
146
+ shadowValueCache.set(raw, normalized);
147
+ return normalized;
148
+ }
149
+ function formatShadowDisplay(val) {
150
+ if (!val || val === "none") return "none";
151
+ return val.replace(/\s+/g, " ");
152
+ }
153
+ function getShadowDisplay(val, authoredVal, shadowTokens) {
154
+ const authoredVar = authoredVal.match(/var\((--[^),\s]+)/)?.[1];
155
+ const normalized = canonicalizeShadowValue(val);
156
+ const matched = shadowTokens.find((token) => token.cssVar === authoredVar) ?? shadowTokens.find((token) => canonicalizeShadowValue(token.value) === normalized);
157
+ if (matched) {
158
+ return {
159
+ label: matched.label,
160
+ sub: matched.cssVar,
161
+ isHardcoded: false
162
+ };
163
+ }
164
+ return {
165
+ label: val === "none" ? "\u65E0" : "\u81EA\u5B9A\u4E49",
166
+ sub: formatShadowDisplay(val),
167
+ isHardcoded: val !== "none"
168
+ };
169
+ }
170
+ function getClasses(el) {
171
+ return Array.from(el.classList).filter((c) => !c.startsWith("di-"));
172
+ }
173
+ var STATE_CLASS_NAMES = /* @__PURE__ */ new Set([
174
+ "active",
175
+ "selected",
176
+ "current",
177
+ "open",
178
+ "expanded",
179
+ "collapsed",
180
+ "disabled",
181
+ "enabled",
182
+ "checked",
183
+ "focus",
184
+ "focused",
185
+ "hover",
186
+ "pressed",
187
+ "loading",
188
+ "error",
189
+ "success",
190
+ "warning"
191
+ ]);
192
+ function isStateClass(className) {
193
+ return STATE_CLASS_NAMES.has(className) || className.startsWith("is-") || className.startsWith("has-") || className.startsWith("state-") || className.endsWith("--active") || className.endsWith("--selected") || className.endsWith("--current") || className.endsWith("--open") || className.endsWith("--disabled");
194
+ }
195
+ function getComponentClasses(el) {
196
+ const componentClasses = getClasses(el).filter(
197
+ (c) => !isStateClass(c) && !/^lucide(-|$)/.test(c)
198
+ );
199
+ return componentClasses.length ? componentClasses : getClasses(el);
200
+ }
201
+ function getStateClasses(el) {
202
+ return getClasses(el).filter((c) => !/^lucide(-|$)/.test(c));
203
+ }
204
+ var UNSAFE_GLOBAL_TAGS = /* @__PURE__ */ new Set([
205
+ "a",
206
+ "aside",
207
+ "button",
208
+ "div",
209
+ "em",
210
+ "h1",
211
+ "h2",
212
+ "h3",
213
+ "h4",
214
+ "h5",
215
+ "h6",
216
+ "img",
217
+ "input",
218
+ "label",
219
+ "li",
220
+ "main",
221
+ "ol",
222
+ "p",
223
+ "path",
224
+ "section",
225
+ "span",
226
+ "strong",
227
+ "svg",
228
+ "textarea",
229
+ "ul"
230
+ ]);
231
+ function classSelector(classes) {
232
+ const escapeClass = (value) => {
233
+ try {
234
+ return CSS.escape(value);
235
+ } catch {
236
+ return value.replace(/[^a-zA-Z0-9_-]/g, "\\$&");
237
+ }
238
+ };
239
+ return classes.length ? "." + classes.map(escapeClass).join(".") : "";
240
+ }
241
+ function queryByClasses(el, classes) {
242
+ if (!classes.length) return [el];
243
+ const includePanels = !!el.closest(".di-panel");
244
+ try {
245
+ return Array.from(document.querySelectorAll(classSelector(classes))).filter((e) => includePanels || !e.closest(".di-panel"));
246
+ } catch {
247
+ return [el];
248
+ }
249
+ }
250
+ function getSameComponentEls(el) {
251
+ return queryByClasses(el, getComponentClasses(el));
252
+ }
253
+ function getScopeTargets(el, scope) {
254
+ if (scope === "component") return getSameComponentEls(el);
255
+ return [el];
256
+ }
257
+ function getClassSelectorForScope(el, scope) {
258
+ const classes = scope === "component" ? getComponentClasses(el) : getStateClasses(el);
259
+ return classSelector(classes) || el.tagName.toLowerCase();
260
+ }
261
+ function getStructuralStep(el) {
262
+ const tag = el.tagName.toLowerCase();
263
+ const parent = el.parentElement;
264
+ if (!parent) return tag;
265
+ const sameTagSiblings = Array.from(parent.children).filter((child) => child.tagName === el.tagName);
266
+ if (sameTagSiblings.length <= 1) return tag;
267
+ return `${tag}:nth-of-type(${sameTagSiblings.indexOf(el) + 1})`;
268
+ }
269
+ function getContextualSelectorForScope(el, scope) {
270
+ let anchor = el.parentElement;
271
+ while (anchor && !getClasses(anchor).length) anchor = anchor.parentElement;
272
+ if (!anchor) return "";
273
+ const anchorSelector = getClassSelectorForScope(anchor, scope);
274
+ const path = [];
275
+ let cursor = el;
276
+ while (cursor && cursor !== anchor) {
277
+ path.unshift(getStructuralStep(cursor));
278
+ cursor = cursor.parentElement;
279
+ }
280
+ return path.length ? `${anchorSelector} > ${path.join(" > ")}` : anchorSelector;
281
+ }
282
+ function getSelectorForScope(el, scope) {
283
+ const classes = scope === "component" ? getComponentClasses(el) : getStateClasses(el);
284
+ if (classes.length) return classSelector(classes);
285
+ const tag = el.tagName.toLowerCase();
286
+ if (!UNSAFE_GLOBAL_TAGS.has(tag)) return tag;
287
+ const contextualSelector = getContextualSelectorForScope(el, scope);
288
+ return contextualSelector || tag;
289
+ }
290
+ function canPersistSelector(el, scope) {
291
+ const classes = scope === "component" ? getComponentClasses(el) : getStateClasses(el);
292
+ if (classes.length) return true;
293
+ if (el.classList.contains("lucide") || Array.from(el.classList).some((c) => /^lucide-/.test(c))) {
294
+ return getContextualSelectorForScope(el, scope).length > 0;
295
+ }
296
+ const tag = el.tagName.toLowerCase();
297
+ if (!UNSAFE_GLOBAL_TAGS.has(tag)) return true;
298
+ return getContextualSelectorForScope(el, scope).length > 0;
299
+ }
300
+ function isInsidePanel(el) {
301
+ return el.closest(".di-panel") !== null || el.closest(".di-trigger") !== null || el.closest(".di-label") !== null;
302
+ }
303
+ function scanTokenMap() {
304
+ const map = {};
305
+ try {
306
+ for (const sheet of Array.from(document.styleSheets)) {
307
+ try {
308
+ for (const rule of Array.from(sheet.cssRules)) {
309
+ if (rule instanceof CSSStyleRule && rule.selectorText === ":root") {
310
+ for (let i = 0; i < rule.style.length; i++) {
311
+ const prop = rule.style[i];
312
+ if (!prop.startsWith("--")) continue;
313
+ const raw = rule.style.getPropertyValue(prop).trim();
314
+ const hex = raw.startsWith("rgb") ? rgbToHex(raw) : raw;
315
+ map[raw] = prop;
316
+ if (hex !== raw) map[hex] = prop;
317
+ }
318
+ }
319
+ }
320
+ } catch {
321
+ }
322
+ }
323
+ } catch {
324
+ }
325
+ return map;
326
+ }
327
+ function getComputedColor(el, prop) {
328
+ return normalizeColor(getComputedStyle(el).getPropertyValue(prop).trim());
329
+ }
330
+ function getComputedRadius(el) {
331
+ return getComputedStyle(el).getPropertyValue("border-radius").trim();
332
+ }
333
+ function parseTranslate(val) {
334
+ const raw = (val || "none").trim();
335
+ if (!raw || raw === "none") return { x: 0, y: 0 };
336
+ const parts = raw.split(/\s+/);
337
+ const toNum = (v) => v ? Number.parseFloat(v) || 0 : 0;
338
+ return { x: toNum(parts[0]), y: toNum(parts[1]) };
339
+ }
340
+ function formatTranslate(pos) {
341
+ return `${Math.round(pos.x)}px ${Math.round(pos.y)}px`;
342
+ }
343
+ function normalizeCssSize(v) {
344
+ const raw = v.trim();
345
+ if (!raw) return "auto";
346
+ if (/^-?\d+(\.\d+)?$/.test(raw)) return `${raw}px`;
347
+ const appendedPxNumber = raw.match(/px(-?\d+(?:\.\d+)?)$/i);
348
+ if (appendedPxNumber) return `${appendedPxNumber[1]}px`;
349
+ return raw;
350
+ }
351
+ function getAuthoredStyleValue(el, prop) {
352
+ const inline = el.style.getPropertyValue(prop).trim();
353
+ if (inline) return inline;
354
+ let found = "";
355
+ try {
356
+ for (const sheet of Array.from(document.styleSheets)) {
357
+ try {
358
+ for (const rule of Array.from(sheet.cssRules)) {
359
+ if (!(rule instanceof CSSStyleRule)) continue;
360
+ if (!rule.style.getPropertyValue(prop)) continue;
361
+ if (el.matches(rule.selectorText)) found = rule.style.getPropertyValue(prop).trim();
362
+ }
363
+ } catch {
364
+ }
365
+ }
366
+ } catch {
367
+ }
368
+ return found;
369
+ }
370
+ function inferSizeMode(el, axis, computedSize) {
371
+ const cs = getComputedStyle(el);
372
+ const authored = getAuthoredStyleValue(el, axis).toLowerCase();
373
+ const display = cs.display.trim();
374
+ const flexGrow = Number.parseFloat(cs.flexGrow) || 0;
375
+ if (authored) {
376
+ if (authored === "auto") return axis === "height" ? "hug" : "fill";
377
+ if (authored.includes("fit-content") || authored.includes("max-content") || authored.includes("min-content")) return "hug";
378
+ if (authored.includes("%") || authored.includes("vw") || authored.includes("vh") || flexGrow > 0) return "fill";
379
+ return "fixed";
380
+ }
381
+ if (axis === "width") {
382
+ if (flexGrow > 0) return "fill";
383
+ if (display === "block" && el.parentElement) {
384
+ const parentWidth = el.parentElement.getBoundingClientRect().width;
385
+ const selfWidth = el.getBoundingClientRect().width;
386
+ if (parentWidth > 0 && selfWidth / parentWidth > 0.88) return "fill";
387
+ }
388
+ if (display.startsWith("inline")) return "hug";
389
+ }
390
+ if (axis === "height" && !getAuthoredStyleValue(el, "min-height") && computedSize !== "0px") {
391
+ return "hug";
392
+ }
393
+ return "fixed";
394
+ }
395
+ function sizeValueForMode(mode, current) {
396
+ if (mode === "fill") return "100%";
397
+ if (mode === "hug") return "fit-content";
398
+ return current || "0px";
399
+ }
400
+ function displaySizeValue(cssValue, current) {
401
+ if (!cssValue || cssValue === "100%" || cssValue === "fit-content") return current || "0px";
402
+ return cssValue;
403
+ }
404
+ function getOneLineHugHeight(el) {
405
+ if (!(el instanceof HTMLElement)) return 0;
406
+ const hasText = (el.textContent ?? "").trim().length > 0;
407
+ const isTextControl = el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLButtonElement;
408
+ if (!hasText && !isTextControl) return 0;
409
+ const cs = getComputedStyle(el);
410
+ const fontSize = Number.parseFloat(cs.fontSize) || 0;
411
+ const lineHeight = cs.lineHeight === "normal" ? fontSize * 1.2 : Number.parseFloat(cs.lineHeight) || fontSize * 1.2;
412
+ const paddingY = (Number.parseFloat(cs.paddingTop) || 0) + (Number.parseFloat(cs.paddingBottom) || 0);
413
+ const borderY = (Number.parseFloat(cs.borderTopWidth) || 0) + (Number.parseFloat(cs.borderBottomWidth) || 0);
414
+ return Math.ceil(lineHeight + paddingY + borderY);
415
+ }
416
+ function clampHeightToOneLine(cssValue, el) {
417
+ const px = cssValue.match(/^(-?\d+(?:\.\d+)?)px$/i);
418
+ if (!px) return cssValue;
419
+ const minH = getOneLineHugHeight(el);
420
+ if (!minH) return cssValue;
421
+ return `${Math.max(Number.parseFloat(px[1]), minH)}px`;
422
+ }
423
+ function calcPanelPos(rect) {
424
+ const W = 380;
425
+ const maxH = window.innerHeight * 0.9;
426
+ let left = rect.right + 16;
427
+ let top = rect.top;
428
+ if (left + W > window.innerWidth - 8) left = Math.max(8, rect.left - W - 16);
429
+ top = Math.min(top, window.innerHeight - maxH - 8);
430
+ top = Math.max(8, top);
431
+ return { top, left };
432
+ }
433
+ function matchPreset(presets, val) {
434
+ const px = val.replace(/\s/g, "");
435
+ return presets.find((p) => p.value === px || p.value === val) ?? null;
436
+ }
437
+ function resizeTextareaToContent(el) {
438
+ el.style.height = "auto";
439
+ el.style.height = `${el.scrollHeight}px`;
440
+ }
441
+ function SideInput({
442
+ value,
443
+ onChange,
444
+ spaceSteps,
445
+ wide,
446
+ compact
447
+ }) {
448
+ const [focused, setFocused] = useState(false);
449
+ const [draft, setDraft] = useState("");
450
+ const [popupPos, setPopupPos] = useState({ top: 0, left: 0 });
451
+ const triggerRef = useRef(null);
452
+ const step = spaceSteps.find((s) => s.val === value);
453
+ const displayLabel = step && step.label !== "\u65E0" ? step.label : value === "0px" ? "0" : value;
454
+ const displaySub = step && step.label !== "\u65E0" ? value : null;
455
+ return /* @__PURE__ */ jsxs("div", { className: `di-side-combo${wide ? " di-side-combo--wide" : ""}${compact ? " di-side-combo--compact" : ""}`, children: [
456
+ /* @__PURE__ */ jsxs("div", { className: "di-side-display", ref: triggerRef, onClick: () => {
457
+ if (triggerRef.current) {
458
+ const r = triggerRef.current.getBoundingClientRect();
459
+ setPopupPos({ top: r.bottom + 4, left: wide ? r.left : r.left + r.width / 2 });
460
+ }
461
+ setFocused(true);
462
+ setDraft(value);
463
+ }, children: [
464
+ /* @__PURE__ */ jsx2("span", { className: "di-side-display-label", children: displayLabel }),
465
+ displaySub && /* @__PURE__ */ jsx2("span", { className: "di-side-display-sub", children: displaySub })
466
+ ] }),
467
+ focused && /* @__PURE__ */ jsxs(Fragment, { children: [
468
+ /* @__PURE__ */ jsx2("div", { className: "di-side-overlay", onClick: () => setFocused(false) }),
469
+ /* @__PURE__ */ jsxs("div", { className: "di-side-popup", style: { top: popupPos.top, left: popupPos.left, transform: wide ? "none" : "translateX(-50%)" }, children: [
470
+ /* @__PURE__ */ jsx2(
471
+ "input",
472
+ {
473
+ className: "di-side-input-edit",
474
+ value: draft,
475
+ autoFocus: true,
476
+ onChange: (e) => setDraft(e.target.value),
477
+ onKeyDown: (e) => {
478
+ if (e.key === "Enter") {
479
+ const s = spaceSteps.find((s2) => s2.label.toLowerCase() === draft.toLowerCase());
480
+ onChange(s ? s.val : draft || "0px");
481
+ setFocused(false);
482
+ }
483
+ if (e.key === "Escape") setFocused(false);
484
+ },
485
+ placeholder: "\u8F93\u5165\u6570\u503C\u2026"
486
+ }
487
+ ),
488
+ /* @__PURE__ */ jsx2("div", { className: "di-side-steps", children: spaceSteps.map((s) => /* @__PURE__ */ jsxs(
489
+ "button",
490
+ {
491
+ className: `di-side-step-item${value === s.val ? " di-side-step-item--on" : ""}`,
492
+ onMouseDown: (e) => {
493
+ e.preventDefault();
494
+ onChange(s.val);
495
+ setFocused(false);
496
+ },
497
+ children: [
498
+ /* @__PURE__ */ jsx2("span", { children: s.label }),
499
+ s.val !== "0px" && /* @__PURE__ */ jsx2("span", { className: "di-side-step-sub", children: s.val })
500
+ ]
501
+ },
502
+ s.val
503
+ )) })
504
+ ] })
505
+ ] })
506
+ ] });
507
+ }
508
+ function parseFourSides(val) {
509
+ const p = (val || "0px").trim().split(/\s+/);
510
+ if (p.length === 1) return { top: p[0], right: p[0], bottom: p[0], left: p[0] };
511
+ if (p.length === 2) return { top: p[0], right: p[1], bottom: p[0], left: p[1] };
512
+ if (p.length === 3) return { top: p[0], right: p[1], bottom: p[2], left: p[1] };
513
+ return { top: p[0], right: p[1], bottom: p[2], left: p[3] };
514
+ }
515
+ function joinFourSides(s) {
516
+ if (s.top === s.right && s.right === s.bottom && s.bottom === s.left) return s.top;
517
+ if (s.top === s.bottom && s.left === s.right) return `${s.top} ${s.right}`;
518
+ return `${s.top} ${s.right} ${s.bottom} ${s.left}`;
519
+ }
520
+ function SpaceCard({ title, variant, value, onChange, spaceSteps }) {
521
+ if (variant === "gap") {
522
+ return /* @__PURE__ */ jsxs("div", { className: "di-space-card", children: [
523
+ /* @__PURE__ */ jsx2("div", { className: "di-space-head", children: /* @__PURE__ */ jsx2("span", { className: "di-space-title", children: title }) }),
524
+ /* @__PURE__ */ jsxs("div", { className: "di-gap-input-row", children: [
525
+ /* @__PURE__ */ jsx2("div", { className: "di-gap-block" }),
526
+ /* @__PURE__ */ jsx2(SideInput, { value, onChange, spaceSteps, wide: true }),
527
+ /* @__PURE__ */ jsx2("div", { className: "di-gap-block" })
528
+ ] })
529
+ ] });
530
+ }
531
+ const sides = parseFourSides(value);
532
+ function updateSide(side, v) {
533
+ const next = { ...sides, [side]: v };
534
+ onChange(joinFourSides(next));
535
+ }
536
+ return /* @__PURE__ */ jsxs("div", { className: "di-space-card", children: [
537
+ /* @__PURE__ */ jsx2("div", { className: "di-space-head", children: /* @__PURE__ */ jsx2("span", { className: "di-space-title", children: title }) }),
538
+ /* @__PURE__ */ jsxs("div", { className: `di-boxing di-boxing--${variant}`, children: [
539
+ /* @__PURE__ */ jsxs("div", { className: "di-box-top", children: [
540
+ " ",
541
+ /* @__PURE__ */ jsx2(SideInput, { value: sides.top, onChange: (v) => updateSide("top", v), spaceSteps, compact: true })
542
+ ] }),
543
+ /* @__PURE__ */ jsxs("div", { className: "di-box-left", children: [
544
+ " ",
545
+ /* @__PURE__ */ jsx2(SideInput, { value: sides.left, onChange: (v) => updateSide("left", v), spaceSteps, compact: true })
546
+ ] }),
547
+ /* @__PURE__ */ jsx2("div", { className: "di-box-center", children: /* @__PURE__ */ jsx2("div", { className: "di-boxing-block" }) }),
548
+ /* @__PURE__ */ jsxs("div", { className: "di-box-right", children: [
549
+ " ",
550
+ /* @__PURE__ */ jsx2(SideInput, { value: sides.right, onChange: (v) => updateSide("right", v), spaceSteps, compact: true })
551
+ ] }),
552
+ /* @__PURE__ */ jsx2("div", { className: "di-box-bottom", children: /* @__PURE__ */ jsx2(SideInput, { value: sides.bottom, onChange: (v) => updateSide("bottom", v), spaceSteps, compact: true }) })
553
+ ] })
554
+ ] });
555
+ }
556
+ function getTextContent(el) {
557
+ const text = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent ?? "").join("").trim();
558
+ return text.length > 0 ? el.textContent?.trim() ?? null : null;
559
+ }
560
+ var TOKEN_TYPES = [
561
+ { key: "bg", label: "\u80CC\u666F\u8272", code: "bg" },
562
+ { key: "text", label: "\u6587\u5B57\u8272", code: "text" },
563
+ { key: "accent", label: "\u5F3A\u8C03\u8272", code: "accent" },
564
+ { key: "border", label: "\u8FB9\u6846\u8272", code: "border" }
565
+ ];
566
+ var TOKEN_COMPONENTS = [
567
+ { key: "global", label: "\u5168\u5C40", code: "global" },
568
+ { key: "sidebar", label: "\u4FA7\u8FB9\u680F", code: "sidebar" },
569
+ { key: "button", label: "\u6309\u94AE", code: "button" },
570
+ { key: "card", label: "\u5361\u7247", code: "card" },
571
+ { key: "tag", label: "\u6807\u7B7E", code: "tag" },
572
+ { key: "input", label: "\u8F93\u5165\u6846", code: "input" }
573
+ ];
574
+ var TOKEN_STATES = [
575
+ { key: "default", label: "\u9ED8\u8BA4\u6001", code: "default" },
576
+ { key: "hover", label: "\u60AC\u6D6E\u6001", code: "hover" },
577
+ { key: "active", label: "\u6FC0\u6D3B\u6001", code: "active" },
578
+ { key: "disabled", label: "\u7981\u7528\u6001", code: "disabled" },
579
+ { key: "success", label: "\u6210\u529F\u6001", code: "success" },
580
+ { key: "warning", label: "\u8B66\u544A\u6001", code: "warning" }
581
+ ];
582
+ function ColorDropdown({ value, onChange, onClose, onAddToken, pos, colorPalette }) {
583
+ const initHex = value.startsWith("#") ? value : "#6b7280";
584
+ const [hexInput, setHexInput] = useState(initHex);
585
+ const [alpha, setAlpha] = useState(100);
586
+ function buildColor(hex, a) {
587
+ const m = hex.match(/^#([0-9a-f]{6})$/i);
588
+ if (!m || a >= 100) return hex;
589
+ const r = parseInt(m[1].slice(0, 2), 16), g = parseInt(m[1].slice(2, 4), 16), b = parseInt(m[1].slice(4, 6), 16);
590
+ return `rgba(${r},${g},${b},${(a / 100).toFixed(2)})`;
591
+ }
592
+ function applyColor(hex, a) {
593
+ onChange(buildColor(hex, a), "");
594
+ }
595
+ const style = pos ? { position: "fixed", top: pos.top, left: pos.left, zIndex: 99998 } : {};
596
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
597
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 99997 }, onClick: onClose }),
598
+ /* @__PURE__ */ jsxs("div", { className: "di-palette-dropdown", style, children: [
599
+ /* @__PURE__ */ jsxs("div", { className: "di-palette-group", children: [
600
+ /* @__PURE__ */ jsx2("div", { className: "di-palette-group-label", children: "\u65E0" }),
601
+ /* @__PURE__ */ jsx2("div", { className: "di-palette-swatches", children: /* @__PURE__ */ jsx2(
602
+ "button",
603
+ {
604
+ className: "di-palette-none",
605
+ title: "\u65E0\u80CC\u666F\u8272 / transparent",
606
+ onClick: () => {
607
+ onChange("transparent", "");
608
+ onClose();
609
+ }
610
+ }
611
+ ) })
612
+ ] }),
613
+ colorPalette.map((g) => /* @__PURE__ */ jsxs("div", { className: "di-palette-group", children: [
614
+ /* @__PURE__ */ jsx2("div", { className: "di-palette-group-label", children: g.group }),
615
+ /* @__PURE__ */ jsxs("div", { className: "di-palette-swatches", children: [
616
+ g.colors.map((c) => /* @__PURE__ */ jsx2(
617
+ "button",
618
+ {
619
+ className: `di-palette-swatch${value === c.val ? " di-palette-swatch--on" : ""}`,
620
+ style: { background: c.val },
621
+ title: `${g.group}\xB7${c.label} ${c.val}`,
622
+ onClick: () => {
623
+ onChange(c.val, c.token);
624
+ onClose();
625
+ }
626
+ },
627
+ c.token
628
+ )),
629
+ onAddToken && /* @__PURE__ */ jsx2(
630
+ "button",
631
+ {
632
+ className: "di-palette-add-btn",
633
+ title: `\u6DFB\u52A0\u4E3A${g.group}\u8272 token`,
634
+ onClick: () => {
635
+ onAddToken(buildColor(hexInput, alpha));
636
+ onClose();
637
+ },
638
+ children: "+"
639
+ }
640
+ )
641
+ ] })
642
+ ] }, g.group)),
643
+ /* @__PURE__ */ jsxs("div", { className: "di-custom-bottom", children: [
644
+ /* @__PURE__ */ jsx2(
645
+ "button",
646
+ {
647
+ className: "di-custom-color-trigger",
648
+ style: { background: buildColor(hexInput, alpha), flexShrink: 0 },
649
+ onClick: () => {
650
+ }
651
+ }
652
+ ),
653
+ /* @__PURE__ */ jsxs("div", { className: "di-field-wrap", children: [
654
+ /* @__PURE__ */ jsx2("span", { className: "di-field-prefix", children: "#" }),
655
+ /* @__PURE__ */ jsx2(
656
+ "input",
657
+ {
658
+ className: "di-field-input",
659
+ value: hexInput.replace(/^#/, ""),
660
+ placeholder: "ffffff",
661
+ onChange: (e) => {
662
+ const h = "#" + e.target.value;
663
+ setHexInput(h);
664
+ applyColor(h, alpha);
665
+ },
666
+ onBlur: (e) => {
667
+ if (!/^[0-9a-f]{6}$/i.test(e.target.value)) setHexInput(initHex);
668
+ }
669
+ }
670
+ )
671
+ ] }),
672
+ /* @__PURE__ */ jsxs("div", { className: "di-field-wrap di-field-wrap--fixed", children: [
673
+ /* @__PURE__ */ jsx2(
674
+ "input",
675
+ {
676
+ className: "di-field-input di-field-input--num",
677
+ type: "number",
678
+ min: "0",
679
+ max: "100",
680
+ value: alpha,
681
+ onChange: (e) => {
682
+ const a = Math.min(100, Math.max(0, +e.target.value || 0));
683
+ setAlpha(a);
684
+ applyColor(hexInput, a);
685
+ }
686
+ }
687
+ ),
688
+ /* @__PURE__ */ jsx2("span", { className: "di-field-suffix", children: "%" })
689
+ ] })
690
+ ] })
691
+ ] })
692
+ ] });
693
+ }
694
+ function inferType(cssProp) {
695
+ if (!cssProp) return "bg";
696
+ if (cssProp === "color") return "text";
697
+ if (cssProp.includes("border")) return "border";
698
+ if (cssProp.includes("background")) return "bg";
699
+ return "accent";
700
+ }
701
+ function inferComponent(classes) {
702
+ const joined = classes.join(" ").toLowerCase();
703
+ if (/sidebar|nav-item|nav/.test(joined)) return "sidebar";
704
+ if (/button|btn|start/.test(joined)) return "button";
705
+ if (/card/.test(joined)) return "card";
706
+ if (/tag|badge|chip/.test(joined)) return "tag";
707
+ if (/input|field|form/.test(joined)) return "input";
708
+ return "global";
709
+ }
710
+ function inferState(classes) {
711
+ const joined = classes.join(" ").toLowerCase();
712
+ if (/is-active|active|is-current|current/.test(joined)) return "active";
713
+ if (/disabled/.test(joined)) return "disabled";
714
+ if (/hover/.test(joined)) return "hover";
715
+ if (/success/.test(joined)) return "success";
716
+ if (/warning/.test(joined)) return "warning";
717
+ return "default";
718
+ }
719
+ function AddTokenModal({ value, cssProp, elementClasses, onClose, onConfirm, colorPalette }) {
720
+ const [mode, setMode] = useState("new");
721
+ const [type, setType] = useState(() => inferType(cssProp));
722
+ const [component, setComponent] = useState(() => inferComponent(elementClasses ?? []));
723
+ const [state, setState] = useState(() => inferState(elementClasses ?? []));
724
+ const [replaceTarget, setReplaceTarget] = useState("");
725
+ const allTokens = colorPalette.flatMap((g) => g.colors);
726
+ const typeObj = TOKEN_TYPES.find((t) => t.key === type);
727
+ const componentObj = TOKEN_COMPONENTS.find((c) => c.key === component);
728
+ const stateObj = TOKEN_STATES.find((s) => s.key === state);
729
+ const generatedVar = mode === "new" ? `--color-${typeObj.code}-${componentObj.code}-${stateObj.code}` : replaceTarget;
730
+ const BtnGroup = ({ items, active, onSelect }) => /* @__PURE__ */ jsx2("div", { style: { display: "flex", flexWrap: "wrap", gap: 6 }, children: items.map((i) => /* @__PURE__ */ jsx2(
731
+ "button",
732
+ {
733
+ className: `di-modal-opt-btn${active === i.key ? " --on" : ""}`,
734
+ onClick: () => onSelect(i.key),
735
+ children: i.label
736
+ },
737
+ i.key
738
+ )) });
739
+ return /* @__PURE__ */ jsx2("div", { className: "di-modal-overlay", onClick: onClose, children: /* @__PURE__ */ jsxs("div", { className: "di-modal", onClick: (e) => e.stopPropagation(), children: [
740
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-header", children: [
741
+ /* @__PURE__ */ jsx2("span", { className: "di-modal-swatch", style: { background: value } }),
742
+ /* @__PURE__ */ jsx2("span", { className: "di-modal-hex", children: value }),
743
+ /* @__PURE__ */ jsx2("div", { style: { flex: 1 } }),
744
+ /* @__PURE__ */ jsx2("span", { style: { fontSize: 12, color: "#6b7280" }, children: "\u6DFB\u52A0\u4E3A token" })
745
+ ] }),
746
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-body", children: [
747
+ /* @__PURE__ */ jsx2("div", { className: "di-modal-tabs", children: [{ k: "new", l: "\u65B0\u5EFA token" }, { k: "replace", l: "\u66FF\u6362\u5DF2\u6709 token" }].map((m) => /* @__PURE__ */ jsx2(
748
+ "button",
749
+ {
750
+ className: `di-modal-tab${mode === m.k ? " --on" : ""}`,
751
+ onClick: () => setMode(m.k),
752
+ children: m.l
753
+ },
754
+ m.k
755
+ )) }),
756
+ mode === "new" ? /* @__PURE__ */ jsxs(Fragment, { children: [
757
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field", children: [
758
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field-label", children: [
759
+ "\u989C\u8272\u7C7B\u578B ",
760
+ /* @__PURE__ */ jsx2("span", { className: "di-modal-auto-tag", children: "\u81EA\u52A8\u8BC6\u522B" })
761
+ ] }),
762
+ /* @__PURE__ */ jsx2(BtnGroup, { items: TOKEN_TYPES, active: type, onSelect: setType })
763
+ ] }),
764
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field", children: [
765
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field-label", children: [
766
+ "\u7EC4\u4EF6 ",
767
+ /* @__PURE__ */ jsx2("span", { className: "di-modal-auto-tag", children: "\u81EA\u52A8\u8BC6\u522B" })
768
+ ] }),
769
+ /* @__PURE__ */ jsx2(BtnGroup, { items: TOKEN_COMPONENTS, active: component, onSelect: setComponent })
770
+ ] }),
771
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field", children: [
772
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-field-label", children: [
773
+ "\u7528\u9014 / \u72B6\u6001 ",
774
+ /* @__PURE__ */ jsx2("span", { className: "di-modal-auto-tag", children: "\u81EA\u52A8\u63A8\u65AD\uFF0C\u53EF\u6539" })
775
+ ] }),
776
+ /* @__PURE__ */ jsx2(BtnGroup, { items: TOKEN_STATES, active: state, onSelect: setState })
777
+ ] })
778
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "di-modal-field", children: [
779
+ /* @__PURE__ */ jsx2("div", { className: "di-modal-field-label", children: "\u9009\u62E9\u8981\u66FF\u6362\u7684 token" }),
780
+ /* @__PURE__ */ jsxs(
781
+ "select",
782
+ {
783
+ className: "di-modal-select",
784
+ value: replaceTarget,
785
+ onChange: (e) => setReplaceTarget(e.target.value),
786
+ children: [
787
+ /* @__PURE__ */ jsx2("option", { value: "", children: "\u8BF7\u9009\u62E9\u2026" }),
788
+ allTokens.map((c) => /* @__PURE__ */ jsxs("option", { value: c.token, children: [
789
+ c.token,
790
+ "\uFF08",
791
+ c.val,
792
+ "\uFF09"
793
+ ] }, c.token))
794
+ ]
795
+ }
796
+ )
797
+ ] }),
798
+ generatedVar && /* @__PURE__ */ jsxs("div", { className: "di-modal-result", children: [
799
+ /* @__PURE__ */ jsx2("div", { className: "di-modal-result-label", children: "\u7CFB\u7EDF\u751F\u6210 token \u540D\u79F0" }),
800
+ /* @__PURE__ */ jsx2("div", { className: "di-modal-result-var", children: generatedVar }),
801
+ mode === "new" && /* @__PURE__ */ jsxs("div", { className: "di-modal-result-trail", children: [
802
+ typeObj.label,
803
+ " \u2192 ",
804
+ /* @__PURE__ */ jsx2("code", { children: typeObj.code }),
805
+ "\u2002",
806
+ componentObj.label,
807
+ " \u2192 ",
808
+ /* @__PURE__ */ jsx2("code", { children: componentObj.code }),
809
+ "\u2002",
810
+ stateObj.label,
811
+ " \u2192 ",
812
+ /* @__PURE__ */ jsx2("code", { children: stateObj.code })
813
+ ] })
814
+ ] })
815
+ ] }),
816
+ /* @__PURE__ */ jsxs("div", { className: "di-modal-foot", children: [
817
+ /* @__PURE__ */ jsx2("button", { className: "di-modal-cancel", onClick: onClose, children: "\u53D6\u6D88" }),
818
+ /* @__PURE__ */ jsx2(
819
+ "button",
820
+ {
821
+ className: "di-modal-ok",
822
+ disabled: !generatedVar,
823
+ onClick: () => {
824
+ if (generatedVar) {
825
+ onConfirm(generatedVar, value, type);
826
+ onClose();
827
+ }
828
+ },
829
+ children: "\u786E\u8BA4\u521B\u5EFA"
830
+ }
831
+ )
832
+ ] })
833
+ ] }) });
834
+ }
835
+ function getDisplayLabel(val, tokenMap, colorPalette, tokenLabels) {
836
+ const displayVal = formatColorDisplay(val);
837
+ const paletteLabel = getColorLabel(val, colorPalette);
838
+ if (paletteLabel) return { label: paletteLabel, sub: displayVal, isHardcoded: false };
839
+ const token = tokenMap[val];
840
+ if (token) return { label: tokenLabels[token] ?? token.replace("--", ""), sub: displayVal, isHardcoded: false };
841
+ return { label: displayVal, sub: "", isHardcoded: true };
842
+ }
843
+ function InspectorPanel({
844
+ targetEl,
845
+ tokenMap,
846
+ onTokenMapUpdate,
847
+ onClose
848
+ }) {
849
+ const isSecondary = false;
850
+ const { endpoints, tokens } = useDevInspectorConfig();
851
+ const {
852
+ colorPalette,
853
+ tokenLabels,
854
+ radiusPresets,
855
+ spaceSteps,
856
+ borderWidthSteps,
857
+ fontSizeOptions,
858
+ fontWeightOptions,
859
+ shadowTokens,
860
+ typographyTokens
861
+ } = tokens;
862
+ const shadowOptions = [
863
+ { cssVar: "", value: "none", label: "\u65E0", usage: "\u4E0D\u4F7F\u7528\u9634\u5F71" },
864
+ ...shadowTokens
865
+ ];
866
+ const [isEditing, setIsEditing] = useState(false);
867
+ const panelElRef = useRef(null);
868
+ const [selected, setSelected] = useState(targetEl);
869
+ const [panelPos, setPanelPos] = useState(() => calcPanelPos(targetEl.getBoundingClientRect()));
870
+ const dragRef = useRef(null);
871
+ const modalOpenRef = useRef(false);
872
+ const [colors, setColors] = useState({});
873
+ const [radiusVal, setRadiusVal] = useState("");
874
+ const [paddingVal, setPaddingVal] = useState("0px");
875
+ const [marginVal, setMarginVal] = useState("0px");
876
+ const [gapVal, setGapVal] = useState("0px");
877
+ const [translateVal, setTranslateVal] = useState({ x: 0, y: 0 });
878
+ const [widthVal, setWidthVal] = useState("");
879
+ const [heightVal, setHeightVal] = useState("");
880
+ const [widthMode, setWidthMode] = useState("fixed");
881
+ const [heightMode, setHeightMode] = useState("hug");
882
+ const [customRadius, setCustomRadius] = useState("");
883
+ const [scope, setScope] = useState("current");
884
+ const [note, setNote] = useState("");
885
+ const [textContent, setTextContent] = useState(null);
886
+ const [pendingColors, setPendingColors] = useState({});
887
+ const [pendingRadius, setPendingRadius] = useState("");
888
+ const [shadowVal, setShadowVal] = useState("none");
889
+ const [shadowAuthoredVal, setShadowAuthoredVal] = useState("");
890
+ const [pendingShadow, setPendingShadow] = useState("");
891
+ const [borderColorVal, setBorderColorVal] = useState("");
892
+ const [borderWidthVal, setBorderWidthVal] = useState("0px");
893
+ const [borderStyleVal, setBorderStyleVal] = useState("none");
894
+ const [pendingBorderColor, setPendingBorderColor] = useState("");
895
+ const [pendingBorderWidth, setPendingBorderWidth] = useState("");
896
+ const [pendingBorderStyle, setPendingBorderStyle] = useState("");
897
+ const [expandedBorderColor, setExpandedBorderColor] = useState(false);
898
+ const [showShadowDrop, setShowShadowDrop] = useState(false);
899
+ const [showStyleDrop, setShowStyleDrop] = useState(false);
900
+ const [showWeightDrop, setShowWeightDrop] = useState(false);
901
+ const [showFontSizeDrop, setShowFontSizeDrop] = useState(false);
902
+ const [showScopeHelp, setShowScopeHelp] = useState(false);
903
+ const [dropPos, setDropPos] = useState({ top: 0, left: 0 });
904
+ const [fontSizeVal, setFontSizeVal] = useState("");
905
+ const [fontWeightVal, setFontWeightVal] = useState("");
906
+ const [textColorVal, setTextColorVal] = useState("");
907
+ const [fontSizeCustomDraft, setFontSizeCustomDraft] = useState("");
908
+ const [expandedTextColor, setExpandedTextColor] = useState(false);
909
+ const [pendingFontSize, setPendingFontSize] = useState("");
910
+ const [pendingFontWeight, setPendingFontWeight] = useState("");
911
+ const [pendingTextColor, setPendingTextColor] = useState("");
912
+ const [pendingPadding, setPendingPadding] = useState("");
913
+ const [pendingMargin, setPendingMargin] = useState("");
914
+ const [pendingGap, setPendingGap] = useState("");
915
+ const [pendingTranslate, setPendingTranslate] = useState(null);
916
+ const [pendingWidth, setPendingWidth] = useState("");
917
+ const [pendingHeight, setPendingHeight] = useState("");
918
+ const [pendingWidthMode, setPendingWidthMode] = useState(null);
919
+ const [pendingHeightMode, setPendingHeightMode] = useState(null);
920
+ const [sizeDraft, setSizeDraft] = useState({});
921
+ const [newTokenProp, setNewTokenProp] = useState(null);
922
+ const [newTokenName, setNewTokenName] = useState("");
923
+ const [saveMsg, setSaveMsg] = useState("");
924
+ const [copyMsg, setCopyMsg] = useState("");
925
+ const [submitMsg, setSubmitMsg] = useState("");
926
+ const [styleIntentSummary, setStyleIntentSummary] = useState({ pendingCount: 0, latestPending: null, pendingEntries: [] });
927
+ const [hasCopied, setHasCopied] = useState(() => _copiedStyles.length > 0);
928
+ const [expandedColor, setExpandedColor] = useState(null);
929
+ const [pendingNewToken, setPendingNewToken] = useState(null);
930
+ const [addTokenModal, setAddTokenModal] = useState(null);
931
+ const [customColorVals, setCustomColorVals] = useState({});
932
+ const selectedRef = useRef(targetEl);
933
+ const gapTargetRef = useRef(null);
934
+ useEffect(() => {
935
+ modalOpenRef.current = addTokenModal !== null;
936
+ }, [addTokenModal]);
937
+ const formatStyleIntentSummary = useCallback((r) => ({
938
+ pendingCount: r.pendingCount ?? 0,
939
+ latestPending: r.latestPending ? {
940
+ id: r.latestPending.id,
941
+ targetLabel: r.latestPending.targetLabel,
942
+ selector: r.latestPending.selector,
943
+ createdAt: r.latestPending.createdAt,
944
+ note: r.latestPending.note,
945
+ entries: r.latestPending.entries ?? []
946
+ } : null,
947
+ pendingEntries: Array.isArray(r.pendingEntries) ? r.pendingEntries.map((entry) => ({
948
+ id: entry.id,
949
+ targetLabel: entry.targetLabel,
950
+ selector: entry.selector,
951
+ createdAt: entry.createdAt,
952
+ note: entry.note,
953
+ entries: entry.entries ?? []
954
+ })) : []
955
+ }), []);
956
+ const refreshStyleIntentSummary = useCallback(() => {
957
+ fetch(endpoints.styleIntents).then((r) => r.json()).then((r) => {
958
+ if (r.ok) {
959
+ setStyleIntentSummary(formatStyleIntentSummary(r));
960
+ }
961
+ }).catch(() => {
962
+ });
963
+ }, [endpoints.styleIntents, formatStyleIntentSummary]);
964
+ useEffect(() => {
965
+ refreshStyleIntentSummary();
966
+ }, [refreshStyleIntentSummary]);
967
+ const selectEl = useCallback((el) => {
968
+ selectedRef.current = el;
969
+ const rect = el.getBoundingClientRect();
970
+ setPanelPos(calcPanelPos(rect));
971
+ const elCs = getComputedStyle(el);
972
+ const c = {};
973
+ for (const { prop } of COLOR_PROPS) {
974
+ const val = getComputedColor(el, prop);
975
+ if (!val || val === "rgba(0, 0, 0, 0)" || val === "transparent") continue;
976
+ if (prop === "border-color" && elCs.borderWidth.trim() === "0px") continue;
977
+ c[prop] = val;
978
+ }
979
+ setColors(c);
980
+ setPendingColors({});
981
+ setFontSizeVal(elCs.fontSize.trim());
982
+ setFontWeightVal(elCs.fontWeight.trim());
983
+ setTextColorVal(normalizeColor(elCs.color.trim()));
984
+ setFontSizeCustomDraft(elCs.fontSize.trim());
985
+ setPendingFontSize("");
986
+ setPendingFontWeight("");
987
+ setPendingTextColor("");
988
+ setExpandedTextColor(false);
989
+ const bw = elCs.borderTopWidth.trim();
990
+ const bs = elCs.borderTopStyle.trim();
991
+ const bc = normalizeColor(elCs.borderTopColor.trim());
992
+ setBorderWidthVal(bw);
993
+ setBorderStyleVal(bs);
994
+ setBorderColorVal(bw !== "0px" ? bc : "");
995
+ setPendingBorderColor("");
996
+ setPendingBorderWidth("");
997
+ setPendingBorderStyle("");
998
+ setExpandedBorderColor(false);
999
+ const r = getComputedRadius(el);
1000
+ setRadiusVal(r);
1001
+ setPendingRadius("");
1002
+ const shadowComputed = elCs.boxShadow.trim() || "none";
1003
+ setShadowVal(shadowComputed === "rgba(0, 0, 0, 0) 0px 0px 0px 0px" ? "none" : shadowComputed);
1004
+ setShadowAuthoredVal(getAuthoredStyleValue(el, "box-shadow"));
1005
+ setPendingShadow("");
1006
+ setShowShadowDrop(false);
1007
+ const cs = getComputedStyle(el);
1008
+ const normPad = (v) => {
1009
+ const parts = v.trim().split(" ");
1010
+ return parts.every((p) => p === parts[0]) ? parts[0] : v.trim();
1011
+ };
1012
+ setPaddingVal(normPad(`${cs.paddingTop} ${cs.paddingRight} ${cs.paddingBottom} ${cs.paddingLeft}`));
1013
+ setMarginVal(normPad(`${cs.marginTop} ${cs.marginRight} ${cs.marginBottom} ${cs.marginLeft}`));
1014
+ const isFlexGrid = (d) => d.includes("flex") || d.includes("grid");
1015
+ if (isFlexGrid(cs.display)) {
1016
+ const g = cs.gap.trim();
1017
+ setGapVal(g === "normal" ? "0px" : g);
1018
+ gapTargetRef.current = el;
1019
+ } else if (el.parentElement) {
1020
+ const pcs = getComputedStyle(el.parentElement);
1021
+ if (isFlexGrid(pcs.display)) {
1022
+ const g = pcs.gap.trim();
1023
+ setGapVal(g === "normal" ? "0px" : g);
1024
+ gapTargetRef.current = el.parentElement;
1025
+ } else {
1026
+ setGapVal("0px");
1027
+ gapTargetRef.current = el;
1028
+ }
1029
+ }
1030
+ setPendingPadding("");
1031
+ setPendingMargin("");
1032
+ setPendingGap("");
1033
+ setTranslateVal(parseTranslate(cs.translate));
1034
+ setWidthVal(cs.width.trim());
1035
+ setHeightVal(cs.height.trim());
1036
+ setWidthMode(inferSizeMode(el, "width", cs.width.trim()));
1037
+ setHeightMode(inferSizeMode(el, "height", cs.height.trim()));
1038
+ setPendingTranslate(null);
1039
+ setPendingWidth("");
1040
+ setPendingHeight("");
1041
+ setPendingWidthMode(null);
1042
+ setPendingHeightMode(null);
1043
+ setSizeDraft({});
1044
+ setNote("");
1045
+ setTextContent(getTextContent(el));
1046
+ setNewTokenProp(null);
1047
+ setExpandedColor(null);
1048
+ setShowScopeHelp(false);
1049
+ setIsEditing(false);
1050
+ setSelected(el);
1051
+ }, []);
1052
+ useEffect(() => {
1053
+ selectEl(targetEl);
1054
+ }, [targetEl]);
1055
+ useEffect(() => {
1056
+ document.querySelectorAll(".di-selected").forEach((e) => e.classList.remove("di-selected"));
1057
+ if (scope !== "current") {
1058
+ getScopeTargets(selected, scope).forEach((e) => e.classList.add("di-selected"));
1059
+ } else {
1060
+ selected.classList.add("di-selected");
1061
+ }
1062
+ return () => {
1063
+ document.querySelectorAll(".di-selected").forEach((e) => e.classList.remove("di-selected"));
1064
+ };
1065
+ }, [selected, scope]);
1066
+ function applyToDOM(changes) {
1067
+ const el = selectedRef.current;
1068
+ if (!el) return;
1069
+ const targets = getScopeTargets(el, scope);
1070
+ changes.forEach(({ prop, val }) => {
1071
+ targets.forEach((t) => t.style.setProperty(prop, val));
1072
+ });
1073
+ }
1074
+ function onDragStart(e) {
1075
+ if (e.target.closest("button, input, textarea")) return;
1076
+ e.preventDefault();
1077
+ dragRef.current = { sx: e.clientX, sy: e.clientY, sl: panelPos.left, st: panelPos.top };
1078
+ const onMove = (ev) => {
1079
+ if (!dragRef.current) return;
1080
+ const left = Math.max(0, Math.min(window.innerWidth - 380, dragRef.current.sl + ev.clientX - dragRef.current.sx));
1081
+ const top = Math.max(0, Math.min(window.innerHeight - 60, dragRef.current.st + ev.clientY - dragRef.current.sy));
1082
+ setPanelPos({ left, top });
1083
+ };
1084
+ const onUp = () => {
1085
+ dragRef.current = null;
1086
+ document.removeEventListener("mousemove", onMove);
1087
+ document.removeEventListener("mouseup", onUp);
1088
+ };
1089
+ document.addEventListener("mousemove", onMove);
1090
+ document.addEventListener("mouseup", onUp);
1091
+ }
1092
+ function handleAddToken(val, cssProp) {
1093
+ setAddTokenModal({ value: val, cssProp });
1094
+ }
1095
+ function handleTokenConfirm(cssVar, val, usage) {
1096
+ setPendingNewToken({ cssVar, value: val, usage });
1097
+ onTokenMapUpdate({ [val]: cssVar });
1098
+ tokenLabels[cssVar] = cssVar.replace("--color-", "").replace(/-/g, "\xB7");
1099
+ }
1100
+ function liveApply(prop, val) {
1101
+ if (prop === "gap" && gapTargetRef.current) {
1102
+ gapTargetRef.current.style.setProperty("gap", val);
1103
+ } else {
1104
+ applyToDOM([{ prop, val }]);
1105
+ }
1106
+ }
1107
+ function liveApplyMany(changes) {
1108
+ applyToDOM(changes);
1109
+ }
1110
+ function getPendingEntries() {
1111
+ const el = selectedRef.current;
1112
+ if (!el) return null;
1113
+ const fixedMinHeight = pendingHeight && pendingHeightMode === "fixed" ? `${getOneLineHugHeight(el) || Number.parseFloat(pendingHeight) || 0}px` : "";
1114
+ const pending = [
1115
+ ...Object.entries(pendingColors),
1116
+ ...pendingTextColor ? [["color", pendingTextColor]] : [],
1117
+ ...pendingRadius ? [["border-radius", pendingRadius]] : [],
1118
+ ...pendingShadow ? [["box-shadow", pendingShadow]] : [],
1119
+ ...pendingBorderColor ? [["border-color", pendingBorderColor]] : [],
1120
+ ...pendingBorderWidth ? [["border-width", pendingBorderWidth]] : [],
1121
+ ...pendingBorderStyle ? [["border-style", pendingBorderStyle]] : [],
1122
+ ...pendingFontSize ? [["font-size", pendingFontSize]] : [],
1123
+ ...pendingFontWeight ? [["font-weight", pendingFontWeight]] : [],
1124
+ ...pendingPadding ? [["padding", pendingPadding]] : [],
1125
+ ...pendingMargin ? [["margin", pendingMargin]] : [],
1126
+ ...pendingGap ? [["gap", pendingGap]] : [],
1127
+ ...pendingTranslate ? [["translate", formatTranslate(pendingTranslate)]] : [],
1128
+ ...pendingWidth ? [["width", pendingWidth]] : [],
1129
+ ...pendingHeight ? [["height", pendingHeight]] : [],
1130
+ ...fixedMinHeight ? [["min-height", fixedMinHeight]] : [],
1131
+ ...pendingHeight && pendingHeightMode === "fixed" ? [["max-height", pendingHeight]] : []
1132
+ ];
1133
+ const gapEntry = pending.find(([p]) => p === "gap");
1134
+ const nonGapPending = pending.filter(([p]) => p !== "gap");
1135
+ const selector = getSelectorForScope(el, scope);
1136
+ const entries = [];
1137
+ if (gapEntry && gapTargetRef.current && gapTargetRef.current !== el) {
1138
+ entries.push({
1139
+ selector: getSelectorForScope(gapTargetRef.current, "current"),
1140
+ changes: [{ prop: "gap", from: gapVal, val: gapEntry[1] }]
1141
+ });
1142
+ }
1143
+ entries.push({
1144
+ selector,
1145
+ changes: (gapEntry && gapTargetRef.current !== el ? nonGapPending : pending).map(([prop, val]) => ({ prop, from: getOriginalValueForProp(prop), val })),
1146
+ note: note || void 0
1147
+ });
1148
+ return {
1149
+ el,
1150
+ selector,
1151
+ pending,
1152
+ entries
1153
+ };
1154
+ }
1155
+ function nudgeSelected(dx, dy) {
1156
+ const base = pendingTranslate ?? translateVal;
1157
+ const next = { x: base.x + dx, y: base.y + dy };
1158
+ setPendingTranslate(next);
1159
+ liveApply("translate", formatTranslate(next));
1160
+ }
1161
+ function setSize(prop, val, mode = "fixed") {
1162
+ const current = prop === "width" ? widthVal : heightVal;
1163
+ const next = normalizeCssSize(val);
1164
+ const selectedEl = selectedRef.current;
1165
+ const cssValue = prop === "height" && mode === "fixed" ? clampHeightToOneLine(next, selectedEl) : mode === "fixed" ? next : sizeValueForMode(mode, current);
1166
+ if (prop === "width") {
1167
+ setPendingWidth(cssValue);
1168
+ setPendingWidthMode(mode);
1169
+ } else {
1170
+ setPendingHeight(cssValue);
1171
+ setPendingHeightMode(mode);
1172
+ }
1173
+ if (prop === "height" && mode === "fixed") {
1174
+ const minHeight = `${getOneLineHugHeight(selectedEl) || Number.parseFloat(cssValue) || 0}px`;
1175
+ liveApplyMany([
1176
+ { prop: "height", val: cssValue },
1177
+ { prop: "min-height", val: minHeight },
1178
+ { prop: "max-height", val: cssValue }
1179
+ ]);
1180
+ return;
1181
+ }
1182
+ if (prop === "height") {
1183
+ const el = selectedRef.current;
1184
+ if (el) {
1185
+ const targets = getScopeTargets(el, scope);
1186
+ targets.forEach((t) => t.style.removeProperty("min-height"));
1187
+ targets.forEach((t) => t.style.removeProperty("max-height"));
1188
+ }
1189
+ }
1190
+ liveApply(prop, cssValue);
1191
+ }
1192
+ function handleSave() {
1193
+ const payload = getPendingEntries();
1194
+ if (!payload) return;
1195
+ const { el, pending, entries } = payload;
1196
+ if (pending.length === 0 && !note && !newTokenName) {
1197
+ setSaveMsg("\u65E0\u6539\u52A8");
1198
+ setTimeout(() => setSaveMsg(""), 1500);
1199
+ return;
1200
+ }
1201
+ applyToDOM(pending.map(([prop, val]) => ({ prop, val })));
1202
+ if (!canPersistSelector(el, scope)) {
1203
+ setSaveMsg("\u9700\u9009\u4E2D\u5177\u4F53\u7C7B\u540D");
1204
+ setTimeout(() => setSaveMsg(""), 2e3);
1205
+ return;
1206
+ }
1207
+ fetch(endpoints.applyCss, {
1208
+ method: "POST",
1209
+ headers: { "Content-Type": "application/json" },
1210
+ body: JSON.stringify({ entries })
1211
+ }).then((r) => r.json()).then((r) => {
1212
+ setSaveMsg(r.ok ? "\u5DF2\u4FDD\u5B58 \u2713" : "\u4FDD\u5B58\u5931\u8D25");
1213
+ setTimeout(() => setSaveMsg(""), 2e3);
1214
+ }).catch(() => {
1215
+ setSaveMsg("\u4FDD\u5B58\u5931\u8D25");
1216
+ setTimeout(() => setSaveMsg(""), 2e3);
1217
+ });
1218
+ }
1219
+ async function copyTextToClipboard(text) {
1220
+ if (navigator.clipboard?.writeText) {
1221
+ try {
1222
+ await navigator.clipboard.writeText(text);
1223
+ return true;
1224
+ } catch {
1225
+ }
1226
+ }
1227
+ const ta = document.createElement("textarea");
1228
+ ta.value = text;
1229
+ ta.style.cssText = "position:fixed;opacity:0;top:0;left:0";
1230
+ document.body.appendChild(ta);
1231
+ ta.select();
1232
+ const copied = document.execCommand("copy");
1233
+ document.body.removeChild(ta);
1234
+ return copied;
1235
+ }
1236
+ function buildAiTaskPrompt(params) {
1237
+ const inboxJson = "/Users/heqiao/Desktop/Claude\u7EC3\u4E60/\u9879\u76EE3-\u5FEB\u6D88AI\u4E2D\u53F0/docs/style-inbox.json";
1238
+ const inboxMd = "/Users/heqiao/Desktop/Claude\u7EC3\u4E60/\u9879\u76EE3-\u5FEB\u6D88AI\u4E2D\u53F0/docs/STYLE_INBOX.md";
1239
+ const changes = params.changes.map((change, index) => `${index + 1}. ${getChangeLabel(change.prop)}\uFF1A${change.from} \u2192 ${change.val}`).join("\n");
1240
+ const latestHint = params.entryId ? `\u4F18\u5148\u5904\u7406 id = ${params.entryId} \u8FD9\u6761\u8BB0\u5F55\u3002` : "\u8FD9\u6761\u4EFB\u52A1\u6587\u672C\u6765\u81EA\u5F53\u524D\u9009\u4E2D\u5143\u7D20\u7684\u672C\u8F6E\u4FEE\u6539\u3002";
1241
+ return [
1242
+ "\u8BF7\u5904\u7406\u8FD9\u6761 DevInspector \u6837\u5F0F\u4EFB\u52A1\uFF1A",
1243
+ "",
1244
+ latestHint,
1245
+ `\u5982\u9700\u67E5\u770B\u7D2F\u8BA1\u8BB0\u5F55\uFF0C\u8BFB\u53D6\uFF1A${inboxJson}`,
1246
+ `\u5FC5\u8981\u65F6\u5BF9\u7167\uFF1A${inboxMd}`,
1247
+ "",
1248
+ `\u5BF9\u8C61\uFF1A${params.targetLabel}`,
1249
+ `\u4F5C\u7528\u8303\u56F4\uFF1A${params.scopeLabel}`,
1250
+ `\u9009\u62E9\u5668\uFF1A${params.selector}`,
1251
+ "\u6539\u52A8\uFF1A",
1252
+ changes,
1253
+ "",
1254
+ "\u8BF7\u5148\u5224\u65AD\u662F\u5426\u9002\u5408\u56FA\u5316\u8FDB\u6B63\u5F0F\u7EC4\u4EF6\u6837\u5F0F\uFF0C\u518D\u4FEE\u6539\u4EE3\u7801\uFF0C\u5E76\u5728\u5904\u7406\u540E\u628A\u8FD9\u6761\u4EFB\u52A1\u6807\u8BB0\u4E3A\u5DF2\u5904\u7406\u3002"
1255
+ ].join("\n");
1256
+ }
1257
+ function handleSubmitToAi() {
1258
+ const payload = getPendingEntries();
1259
+ if (!payload) return;
1260
+ const { el, pending, selector } = payload;
1261
+ const changes = getPendingChangeRecords();
1262
+ if (pending.length === 0 && !note) {
1263
+ setSubmitMsg("\u5148\u6539\u70B9\u6837\u5F0F\u6216\u5199\u5907\u6CE8");
1264
+ setTimeout(() => setSubmitMsg(""), 1800);
1265
+ return;
1266
+ }
1267
+ const scopeLabel = scope === "current" ? "\u5F53\u524D\u5143\u7D20" : "\u540C\u7EC4\u4EF6";
1268
+ const targetClasses = getClasses(el);
1269
+ const targetLabel = targetClasses.length ? `${el.tagName.toLowerCase()}.${targetClasses.join(".")}` : el.tagName.toLowerCase();
1270
+ const prompt2 = buildAiTaskPrompt({
1271
+ targetLabel,
1272
+ selector,
1273
+ scopeLabel,
1274
+ changes
1275
+ });
1276
+ copyTextToClipboard(prompt2).then((copied) => {
1277
+ setSubmitMsg(copied ? "\u4EFB\u52A1\u6587\u672C\u5DF2\u590D\u5236 \u2713" : "\u590D\u5236\u5931\u8D25");
1278
+ setTimeout(() => setSubmitMsg(""), copied ? 2200 : 1800);
1279
+ }).catch(() => {
1280
+ setSubmitMsg("\u590D\u5236\u5931\u8D25");
1281
+ setTimeout(() => setSubmitMsg(""), 1800);
1282
+ });
1283
+ }
1284
+ function handleDeleteStyleIntent(intentId, selector, prop) {
1285
+ fetch(endpoints.deleteStyleIntent, {
1286
+ method: "POST",
1287
+ headers: { "Content-Type": "application/json" },
1288
+ body: JSON.stringify({ id: intentId, selector, prop })
1289
+ }).then(async (response) => {
1290
+ let body = null;
1291
+ try {
1292
+ body = await response.json();
1293
+ } catch {
1294
+ body = null;
1295
+ }
1296
+ return {
1297
+ httpOk: response.ok,
1298
+ ...body ?? {}
1299
+ };
1300
+ }).then((r) => {
1301
+ if (r.ok) {
1302
+ setStyleIntentSummary(formatStyleIntentSummary(r));
1303
+ setSubmitMsg(prop ? "\u5DF2\u5220\u9664\u5C5E\u6027" : "\u5DF2\u5220\u9664\u5BF9\u8C61");
1304
+ } else {
1305
+ const errorMsg = !r.httpOk ? "\u5220\u9664\u5931\u8D25\uFF1A\u672C\u5730\u670D\u52A1\u5F02\u5E38" : `\u5220\u9664\u5931\u8D25${r.error ? `\uFF1A${String(r.error)}` : ""}`;
1306
+ setSubmitMsg(errorMsg);
1307
+ }
1308
+ setTimeout(() => setSubmitMsg(""), 1800);
1309
+ }).catch(() => {
1310
+ setSubmitMsg("\u5220\u9664\u5931\u8D25\uFF1A\u8BF7\u5237\u65B0\u9875\u9762\u6216\u91CD\u542F\u672C\u5730 dev");
1311
+ setTimeout(() => setSubmitMsg(""), 1800);
1312
+ });
1313
+ }
1314
+ function getOriginalValueForProp(prop) {
1315
+ if (prop === "font-size") return fontSizeVal;
1316
+ if (prop === "font-weight") return fontWeightVal;
1317
+ if (prop === "color") return getDisplayLabel(textColorVal, tokenMap, colorPalette, tokenLabels).label;
1318
+ if (prop === "background-color") return getDisplayLabel(colors[prop] ?? "", tokenMap, colorPalette, tokenLabels).label;
1319
+ if (prop === "border-color") return getDisplayLabel(borderColorVal, tokenMap, colorPalette, tokenLabels).label;
1320
+ if (prop === "border-width") return borderWidthVal;
1321
+ if (prop === "border-style") return borderStyleVal;
1322
+ if (prop === "border-radius") return radiusVal;
1323
+ if (prop === "box-shadow") return getShadowDisplay(shadowVal, shadowAuthoredVal, shadowTokens).label;
1324
+ if (prop === "padding") return paddingVal;
1325
+ if (prop === "margin") return marginVal;
1326
+ if (prop === "gap") return gapVal;
1327
+ if (prop === "translate") return formatTranslate(translateVal);
1328
+ if (prop === "width") return widthVal;
1329
+ if (prop === "height") return heightVal;
1330
+ if (prop === "min-height") return `${getOneLineHugHeight(selectedRef.current) || Number.parseFloat(heightVal) || 0}px`;
1331
+ if (prop === "max-height") return heightVal;
1332
+ return "";
1333
+ }
1334
+ function getPendingChangeRecords() {
1335
+ const records = [];
1336
+ const add = (prop, from, val) => records.push({ prop, from, val });
1337
+ if (pendingFontSize) add("font-size", fontSizeVal, pendingFontSize);
1338
+ if (pendingFontWeight) add("font-weight", fontWeightVal, pendingFontWeight);
1339
+ if (pendingTextColor) add("color", getDisplayLabel(textColorVal, tokenMap, colorPalette, tokenLabels).label, getDisplayLabel(pendingTextColor, tokenMap, colorPalette, tokenLabels).label);
1340
+ for (const [prop, val] of Object.entries(pendingColors)) {
1341
+ add(prop, getDisplayLabel(colors[prop] ?? "", tokenMap, colorPalette, tokenLabels).label, getDisplayLabel(val, tokenMap, colorPalette, tokenLabels).label);
1342
+ }
1343
+ if (pendingBorderColor) add("border-color", getDisplayLabel(borderColorVal, tokenMap, colorPalette, tokenLabels).label, getDisplayLabel(pendingBorderColor, tokenMap, colorPalette, tokenLabels).label);
1344
+ if (pendingBorderWidth) add("border-width", borderWidthVal, pendingBorderWidth);
1345
+ if (pendingBorderStyle) add("border-style", borderStyleVal, pendingBorderStyle);
1346
+ if (pendingRadius) add("border-radius", radiusVal, pendingRadius);
1347
+ if (pendingShadow) add("box-shadow", getShadowDisplay(shadowVal, shadowAuthoredVal, shadowTokens).label, getShadowDisplay(pendingShadow, pendingShadow, shadowTokens).label);
1348
+ if (pendingPadding) add("padding", paddingVal, pendingPadding);
1349
+ if (pendingMargin) add("margin", marginVal, pendingMargin);
1350
+ if (pendingGap) add("gap", gapVal, pendingGap);
1351
+ if (pendingTranslate) add("translate", formatTranslate(translateVal), formatTranslate(pendingTranslate));
1352
+ if (pendingWidth) add("width", widthVal, pendingWidth);
1353
+ if (pendingHeight) add("height", heightVal, pendingHeight);
1354
+ if (pendingNewToken) add("token", "\u65B0\u589E", `${pendingNewToken.cssVar}: ${pendingNewToken.value}`);
1355
+ return records;
1356
+ }
1357
+ function getChangeLabel(prop) {
1358
+ const map = {
1359
+ "font-size": "\u5B57\u53F7",
1360
+ "font-weight": "\u5B57\u91CD",
1361
+ "color": "\u6587\u5B57\u8272",
1362
+ "background-color": "\u80CC\u666F\u8272",
1363
+ "border-color": "\u8FB9\u6846\u8272",
1364
+ "border-width": "\u8FB9\u6846\u7C97\u7EC6",
1365
+ "border-style": "\u8FB9\u6846\u6837\u5F0F",
1366
+ "border-radius": "\u5706\u89D2",
1367
+ "box-shadow": "\u9634\u5F71",
1368
+ "padding": "\u5185\u8FB9\u8DDD",
1369
+ "margin": "\u5916\u8FB9\u8DDD",
1370
+ "gap": "\u5143\u7D20\u95F4\u8DDD",
1371
+ "translate": "\u4F4D\u7F6E",
1372
+ "width": "\u5BBD\u5EA6",
1373
+ "height": "\u9AD8\u5EA6",
1374
+ "min-height": "\u6700\u5C0F\u9AD8\u5EA6",
1375
+ "max-height": "\u6700\u5927\u9AD8\u5EA6"
1376
+ };
1377
+ return map[prop] || prop;
1378
+ }
1379
+ function handleReset() {
1380
+ const el = selectedRef.current;
1381
+ if (!el) return;
1382
+ const targets = getScopeTargets(el, scope);
1383
+ targets.forEach((t) => {
1384
+ const hEl = t;
1385
+ [...Object.keys(pendingColors)].forEach((p) => hEl.style.removeProperty(p));
1386
+ if (pendingRadius) hEl.style.removeProperty("border-radius");
1387
+ if (pendingShadow) hEl.style.removeProperty("box-shadow");
1388
+ if (pendingBorderColor) hEl.style.removeProperty("border-color");
1389
+ if (pendingBorderWidth) hEl.style.removeProperty("border-width");
1390
+ if (pendingBorderStyle) hEl.style.removeProperty("border-style");
1391
+ if (pendingPadding) hEl.style.removeProperty("padding");
1392
+ if (pendingMargin) hEl.style.removeProperty("margin");
1393
+ if (pendingGap) hEl.style.removeProperty("gap");
1394
+ if (pendingTranslate) hEl.style.removeProperty("translate");
1395
+ if (pendingWidth) hEl.style.removeProperty("width");
1396
+ if (pendingHeight) hEl.style.removeProperty("height");
1397
+ if (pendingHeight) hEl.style.removeProperty("min-height");
1398
+ if (pendingHeight) hEl.style.removeProperty("max-height");
1399
+ });
1400
+ setPendingColors({});
1401
+ setPendingRadius("");
1402
+ setPendingShadow("");
1403
+ setPendingFontSize("");
1404
+ setPendingFontWeight("");
1405
+ setPendingTextColor("");
1406
+ setPendingBorderColor("");
1407
+ setPendingBorderWidth("");
1408
+ setPendingBorderStyle("");
1409
+ setPendingPadding("");
1410
+ setPendingMargin("");
1411
+ setPendingGap("");
1412
+ setPendingTranslate(null);
1413
+ setPendingWidth("");
1414
+ setPendingHeight("");
1415
+ setPendingWidthMode(null);
1416
+ setPendingHeightMode(null);
1417
+ setSizeDraft({});
1418
+ setCustomColorVals({});
1419
+ setColors(Object.fromEntries(
1420
+ COLOR_PROPS.map(({ prop }) => [prop, getComputedColor(el, prop)]).filter(([, v]) => v && v !== "rgba(0, 0, 0, 0)" && v !== "transparent")
1421
+ ));
1422
+ const cs = getComputedStyle(el);
1423
+ setRadiusVal(getComputedRadius(el));
1424
+ setShadowVal(cs.boxShadow.trim() === "rgba(0, 0, 0, 0) 0px 0px 0px 0px" ? "none" : cs.boxShadow.trim());
1425
+ setShadowAuthoredVal(getAuthoredStyleValue(el, "box-shadow"));
1426
+ const normSpace = (v) => {
1427
+ const parts = v.trim().split(" ");
1428
+ return parts.every((p) => p === parts[0]) ? parts[0] : v.trim();
1429
+ };
1430
+ setPaddingVal(normSpace(`${cs.paddingTop} ${cs.paddingRight} ${cs.paddingBottom} ${cs.paddingLeft}`));
1431
+ setMarginVal(normSpace(`${cs.marginTop} ${cs.marginRight} ${cs.marginBottom} ${cs.marginLeft}`));
1432
+ const g = cs.gap.trim();
1433
+ setGapVal(g === "normal" ? "0px" : g);
1434
+ setTranslateVal(parseTranslate(cs.translate));
1435
+ setWidthVal(cs.width.trim());
1436
+ setHeightVal(cs.height.trim());
1437
+ setWidthMode(inferSizeMode(el, "width", cs.width.trim()));
1438
+ setHeightMode(inferSizeMode(el, "height", cs.height.trim()));
1439
+ }
1440
+ function handleClose() {
1441
+ onClose();
1442
+ }
1443
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1444
+ addTokenModal && /* @__PURE__ */ jsx2(
1445
+ AddTokenModal,
1446
+ {
1447
+ value: addTokenModal.value,
1448
+ cssProp: addTokenModal.cssProp,
1449
+ elementClasses: getClasses(selected),
1450
+ colorPalette,
1451
+ onClose: () => setAddTokenModal(null),
1452
+ onConfirm: handleTokenConfirm
1453
+ }
1454
+ ),
1455
+ /* @__PURE__ */ jsxs(
1456
+ "div",
1457
+ {
1458
+ ref: panelElRef,
1459
+ className: "di-panel",
1460
+ "data-di-panel-role": "primary",
1461
+ style: { top: panelPos.top, left: panelPos.left },
1462
+ children: [
1463
+ /* @__PURE__ */ jsxs("div", { className: "di-head", onMouseDown: onDragStart, children: [
1464
+ /* @__PURE__ */ jsxs("div", { className: "di-head-left", children: [
1465
+ /* @__PURE__ */ jsx2("span", { className: "di-title", children: "\u9875\u9762\u6837\u5F0F" }),
1466
+ /* @__PURE__ */ jsx2("span", { className: "di-badge-dev", children: "Dev Only" }),
1467
+ /* @__PURE__ */ jsx2(
1468
+ "button",
1469
+ {
1470
+ className: "di-layer-btn",
1471
+ title: "\u9009\u4E2D\u7236\u5C42",
1472
+ disabled: !selected.parentElement || selected.parentElement === document.body,
1473
+ onClick: () => {
1474
+ if (selected.parentElement && selected.parentElement !== document.body) selectEl(selected.parentElement);
1475
+ },
1476
+ children: "\u2191"
1477
+ }
1478
+ ),
1479
+ /* @__PURE__ */ jsx2(
1480
+ "button",
1481
+ {
1482
+ className: "di-layer-btn",
1483
+ title: "\u9009\u4E2D\u7B2C\u4E00\u4E2A\u5B50\u5C42",
1484
+ disabled: !selected.firstElementChild,
1485
+ onClick: () => {
1486
+ if (selected.firstElementChild) selectEl(selected.firstElementChild);
1487
+ },
1488
+ children: "\u2193"
1489
+ }
1490
+ )
1491
+ ] }),
1492
+ /* @__PURE__ */ jsx2("div", { className: "di-head-right", children: /* @__PURE__ */ jsx2("button", { className: "di-close", onClick: handleClose, children: "\xD7" }) })
1493
+ ] }),
1494
+ /* @__PURE__ */ jsxs("div", { className: "di-body", children: [
1495
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1496
+ /* @__PURE__ */ jsxs("div", { className: "di-section-title-row", children: [
1497
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u4F5C\u7528\u8303\u56F4" }),
1498
+ /* @__PURE__ */ jsx2(
1499
+ "button",
1500
+ {
1501
+ type: "button",
1502
+ className: `di-help-icon${showScopeHelp ? " di-help-icon--on" : ""}`,
1503
+ "aria-label": "\u67E5\u770B\u4F5C\u7528\u8303\u56F4\u8BF4\u660E",
1504
+ "aria-expanded": showScopeHelp,
1505
+ onClick: () => setShowScopeHelp((v) => !v),
1506
+ children: /* @__PURE__ */ jsx2(CircleHelp, { size: 14, strokeWidth: 1.8 })
1507
+ }
1508
+ )
1509
+ ] }),
1510
+ showScopeHelp && /* @__PURE__ */ jsx2("div", { className: "di-help-popover", children: "\u540C\u7EC4\u4EF6 = \u5FFD\u7565\u6FC0\u6D3B / \u5F53\u524D / \u5C55\u5F00\u7B49\u72B6\u6001\u7C7B\u540E\uFF0C\u5171\u4EAB\u540C\u4E00\u4E1A\u52A1\u7C7B\u540D\u7684\u5143\u7D20\u3002" }),
1511
+ /* @__PURE__ */ jsxs("div", { className: "di-scope-row", children: [
1512
+ /* @__PURE__ */ jsx2("button", { className: `di-scope-btn${scope === "current" ? " di-scope-btn--on" : ""}`, onClick: () => setScope("current"), children: "\u5F53\u524D\u5143\u7D20" }),
1513
+ /* @__PURE__ */ jsxs("button", { className: `di-scope-btn${scope === "component" ? " di-scope-btn--on" : ""}`, onClick: () => setScope("component"), children: [
1514
+ "\u540C\u7EC4\u4EF6 ",
1515
+ selected && /* @__PURE__ */ jsx2("span", { className: "di-scope-count", children: getSameComponentEls(selected).length })
1516
+ ] })
1517
+ ] })
1518
+ ] }),
1519
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1520
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u6587\u5B57" }),
1521
+ textContent !== null && /* @__PURE__ */ jsx2(
1522
+ "textarea",
1523
+ {
1524
+ className: "di-note di-note--sm",
1525
+ defaultValue: textContent,
1526
+ ref: (el) => {
1527
+ if (el) resizeTextareaToContent(el);
1528
+ },
1529
+ placeholder: "\u4FEE\u6539\u6587\u5B57\u5185\u5BB9...",
1530
+ onChange: (e) => {
1531
+ resizeTextareaToContent(e.currentTarget);
1532
+ const el = selectedRef.current;
1533
+ if (!el) return;
1534
+ const ts = getScopeTargets(el, scope);
1535
+ ts.forEach((t) => {
1536
+ t.textContent = e.currentTarget.value;
1537
+ });
1538
+ }
1539
+ }
1540
+ ),
1541
+ /* @__PURE__ */ jsxs("div", { className: "di-border-row", children: [
1542
+ /* @__PURE__ */ jsxs("div", { className: "di-border-card", children: [
1543
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u5B57\u53F7" }),
1544
+ (() => {
1545
+ const curSize = pendingFontSize || fontSizeVal || "\u2014";
1546
+ const curWeight = pendingFontWeight || fontWeightVal;
1547
+ const curColor = pendingTextColor || textColorVal;
1548
+ const matchedToken = getTypographyToken(curSize, curWeight, curColor, typographyTokens);
1549
+ const curOpt = matchedToken ? fontSizeOptions.find((opt) => opt.key === matchedToken.key) : null;
1550
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1551
+ /* @__PURE__ */ jsxs(
1552
+ "div",
1553
+ {
1554
+ className: "di-border-style-single di-font-token-trigger",
1555
+ role: "button",
1556
+ tabIndex: 0,
1557
+ onClick: (e) => {
1558
+ const r = e.currentTarget.getBoundingClientRect();
1559
+ setDropPos(calcDropPos(r));
1560
+ setShowWeightDrop(false);
1561
+ setShowStyleDrop(false);
1562
+ setExpandedTextColor(false);
1563
+ setExpandedBorderColor(false);
1564
+ setShowShadowDrop(false);
1565
+ setCustomRadius("");
1566
+ setSizeDraft({});
1567
+ setFontSizeCustomDraft(curSize);
1568
+ setShowFontSizeDrop((v) => !v);
1569
+ },
1570
+ onKeyDown: (e) => {
1571
+ if (e.key === "Enter" || e.key === " ") {
1572
+ e.preventDefault();
1573
+ const r = e.currentTarget.getBoundingClientRect();
1574
+ setDropPos(calcDropPos(r));
1575
+ setFontSizeCustomDraft(curSize);
1576
+ setShowFontSizeDrop((v) => !v);
1577
+ }
1578
+ },
1579
+ children: [
1580
+ curOpt && /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", children: curOpt.label }),
1581
+ /* @__PURE__ */ jsx2("span", { className: `di-border-style-name${curOpt ? "" : " di-border-style-name--custom"}`, children: curSize }),
1582
+ /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", fill: "none", style: { marginLeft: "auto", flexShrink: 0 }, children: /* @__PURE__ */ jsx2("path", { d: "M1 1l3 3 3-3", stroke: "#9ca3af", strokeWidth: "1.5", strokeLinecap: "round" }) })
1583
+ ]
1584
+ }
1585
+ ),
1586
+ showFontSizeDrop && /* @__PURE__ */ jsxs(Fragment, { children: [
1587
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 99997 }, onClick: () => setShowFontSizeDrop(false) }),
1588
+ /* @__PURE__ */ jsxs("div", { className: "di-border-style-drop", style: { position: "fixed", top: dropPos.top, left: dropPos.left, zIndex: 99998 }, children: [
1589
+ fontSizeOptions.map((opt) => /* @__PURE__ */ jsxs(
1590
+ "button",
1591
+ {
1592
+ className: `di-border-style-drop-item${curOpt?.key === opt.key ? " di-border-style-drop-item--on" : ""}`,
1593
+ onClick: () => {
1594
+ setPendingFontSize(opt.value);
1595
+ setPendingFontWeight(opt.fontWeight);
1596
+ setPendingTextColor(opt.color);
1597
+ liveApplyMany([
1598
+ { prop: "font-size", val: opt.value },
1599
+ { prop: "font-weight", val: opt.fontWeight },
1600
+ { prop: "color", val: opt.color }
1601
+ ]);
1602
+ setShowFontSizeDrop(false);
1603
+ },
1604
+ children: [
1605
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", children: opt.label }),
1606
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: `${opt.value} / ${opt.fontWeight}` })
1607
+ ]
1608
+ },
1609
+ opt.key
1610
+ )),
1611
+ /* @__PURE__ */ jsxs("div", { className: "di-dropdown-custom", children: [
1612
+ /* @__PURE__ */ jsx2("span", { className: "di-dropdown-custom-label", children: "\u81EA\u5B9A\u4E49" }),
1613
+ /* @__PURE__ */ jsx2(
1614
+ "input",
1615
+ {
1616
+ className: "di-dropdown-custom-input",
1617
+ value: fontSizeCustomDraft,
1618
+ placeholder: "13px",
1619
+ onChange: (e) => setFontSizeCustomDraft(e.target.value),
1620
+ onClick: (e) => e.stopPropagation()
1621
+ }
1622
+ ),
1623
+ /* @__PURE__ */ jsx2(
1624
+ "button",
1625
+ {
1626
+ type: "button",
1627
+ className: "di-dropdown-custom-apply",
1628
+ onClick: (e) => {
1629
+ e.stopPropagation();
1630
+ if (!fontSizeCustomDraft.trim()) return;
1631
+ setPendingFontSize(fontSizeCustomDraft.trim());
1632
+ liveApply("font-size", fontSizeCustomDraft.trim());
1633
+ setShowFontSizeDrop(false);
1634
+ },
1635
+ children: "\u5E94\u7528"
1636
+ }
1637
+ )
1638
+ ] })
1639
+ ] })
1640
+ ] })
1641
+ ] });
1642
+ })()
1643
+ ] }),
1644
+ /* @__PURE__ */ jsxs("div", { className: "di-border-card", style: { position: "relative" }, children: [
1645
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u5B57\u91CD" }),
1646
+ (() => {
1647
+ const cur = pendingFontWeight || fontWeightVal;
1648
+ const curOpt = fontWeightOptions.find((o) => o.value === cur) ?? fontWeightOptions[1];
1649
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1650
+ /* @__PURE__ */ jsxs(
1651
+ "button",
1652
+ {
1653
+ className: "di-border-style-single",
1654
+ style: { marginTop: 4 },
1655
+ onClick: (e) => {
1656
+ const r = e.currentTarget.getBoundingClientRect();
1657
+ setDropPos(calcDropPos(r));
1658
+ setShowWeightDrop((v) => !v);
1659
+ },
1660
+ children: [
1661
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: curOpt.value === "none" ? { color: "#9ca3af" } : void 0, children: curOpt.label }),
1662
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: cur }),
1663
+ /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", fill: "none", style: { marginLeft: "auto", flexShrink: 0 }, children: /* @__PURE__ */ jsx2("path", { d: "M1 1l3 3 3-3", stroke: "#9ca3af", strokeWidth: "1.5", strokeLinecap: "round" }) })
1664
+ ]
1665
+ }
1666
+ ),
1667
+ showWeightDrop && /* @__PURE__ */ jsxs(Fragment, { children: [
1668
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 99997 }, onClick: () => setShowWeightDrop(false) }),
1669
+ /* @__PURE__ */ jsx2("div", { className: "di-border-style-drop", style: { position: "fixed", top: dropPos.top, left: dropPos.left, zIndex: 99998 }, children: fontWeightOptions.map((opt) => /* @__PURE__ */ jsxs(
1670
+ "button",
1671
+ {
1672
+ className: `di-border-style-drop-item${cur === opt.value ? " di-border-style-drop-item--on" : ""}`,
1673
+ onClick: () => {
1674
+ setPendingFontWeight(opt.value);
1675
+ liveApply("font-weight", opt.value);
1676
+ setShowWeightDrop(false);
1677
+ },
1678
+ children: [
1679
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: opt.value === "none" ? { color: "#9ca3af" } : void 0, children: opt.label }),
1680
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: opt.value })
1681
+ ]
1682
+ },
1683
+ opt.value
1684
+ )) })
1685
+ ] })
1686
+ ] });
1687
+ })()
1688
+ ] }),
1689
+ (() => {
1690
+ const tcVal = pendingTextColor || textColorVal;
1691
+ const { label: tcLabel, sub: tcSub, isHardcoded: tcHard } = getDisplayLabel(tcVal, tokenMap, colorPalette, tokenLabels);
1692
+ const tcDark = (() => {
1693
+ const m = tcVal.match(/^#([0-9a-f]{6})$/i);
1694
+ if (!m) return true;
1695
+ const r = parseInt(m[1].slice(0, 2), 16), g = parseInt(m[1].slice(2, 4), 16), b = parseInt(m[1].slice(4, 6), 16);
1696
+ return (r * 299 + g * 587 + b * 114) / 1e3 < 128;
1697
+ })();
1698
+ return /* @__PURE__ */ jsxs("div", { className: "di-border-card", style: { position: "relative" }, children: [
1699
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u989C\u8272" }),
1700
+ /* @__PURE__ */ jsxs("div", { className: "di-border-color-body", style: { marginTop: 4 }, children: [
1701
+ /* @__PURE__ */ jsx2(
1702
+ "button",
1703
+ {
1704
+ className: `di-swatch-btn${tcDark ? " di-swatch-btn--dark" : ""}`,
1705
+ style: { background: tcVal || "#1d293d" },
1706
+ onClick: (e) => {
1707
+ const r = e.currentTarget.getBoundingClientRect();
1708
+ setDropPos(calcDropPos(r));
1709
+ setExpandedTextColor((v) => !v);
1710
+ },
1711
+ children: /* @__PURE__ */ jsx2("svg", { width: "10", height: "6", viewBox: "0 0 10 6", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M1 1l4 4 4-4", stroke: tcDark ? "#fff" : "#374151", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1712
+ }
1713
+ ),
1714
+ /* @__PURE__ */ jsxs("div", { className: "di-border-color-info", children: [
1715
+ /* @__PURE__ */ jsx2("span", { className: tcHard ? "di-token-name--plain" : "di-token-name", children: tcLabel }),
1716
+ tcSub && /* @__PURE__ */ jsx2("span", { className: "di-hex", children: tcSub })
1717
+ ] })
1718
+ ] }),
1719
+ expandedTextColor && /* @__PURE__ */ jsx2(
1720
+ ColorDropdown,
1721
+ {
1722
+ value: tcVal,
1723
+ pos: dropPos,
1724
+ colorPalette,
1725
+ onChange: (c) => {
1726
+ setPendingTextColor(c);
1727
+ liveApply("color", c);
1728
+ },
1729
+ onClose: () => setExpandedTextColor(false),
1730
+ onAddToken: (v) => handleAddToken(v, "color")
1731
+ }
1732
+ )
1733
+ ] });
1734
+ })()
1735
+ ] })
1736
+ ] }),
1737
+ Object.keys(colors).length > 0 && /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1738
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u989C\u8272" }),
1739
+ COLOR_PROPS.filter(({ prop }) => colors[prop]).map(({ label, prop }) => {
1740
+ const val = pendingColors[prop] ?? colors[prop];
1741
+ const token = tokenMap[val];
1742
+ const isDark = (() => {
1743
+ const m = val.match(/^#([0-9a-f]{6})$/i);
1744
+ if (!m) return false;
1745
+ const r = parseInt(m[1].slice(0, 2), 16), g = parseInt(m[1].slice(2, 4), 16), b = parseInt(m[1].slice(4, 6), 16);
1746
+ return (r * 299 + g * 587 + b * 114) / 1e3 < 128;
1747
+ })();
1748
+ const isExpanded = expandedColor === prop;
1749
+ const tokenList = Object.entries(tokenMap).filter(([v]) => v.startsWith("#") && v !== val).map(([v, t]) => ({ val: v, token: t }));
1750
+ const { label: dispLabel, sub: dispSub, isHardcoded: dispHard } = getDisplayLabel(val, tokenMap, colorPalette, tokenLabels);
1751
+ return /* @__PURE__ */ jsxs("div", { className: "di-color-row", children: [
1752
+ /* @__PURE__ */ jsx2("span", { className: "di-attr-label", children: label }),
1753
+ /* @__PURE__ */ jsx2(
1754
+ "button",
1755
+ {
1756
+ className: `di-swatch-btn${isDark ? " di-swatch-btn--dark" : ""}`,
1757
+ style: { background: val },
1758
+ onClick: (e) => {
1759
+ const r = e.currentTarget.getBoundingClientRect();
1760
+ setDropPos(calcDropPos(r));
1761
+ setExpandedColor(isExpanded ? null : prop);
1762
+ },
1763
+ children: /* @__PURE__ */ jsx2("svg", { width: "10", height: "6", viewBox: "0 0 10 6", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M1 1l4 4 4-4", stroke: isDark ? "#fff" : "#374151", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1764
+ }
1765
+ ),
1766
+ /* @__PURE__ */ jsxs("div", { className: "di-color-info", children: [
1767
+ /* @__PURE__ */ jsx2("span", { className: dispHard ? "di-token-name--plain" : "di-token-name", children: dispLabel }),
1768
+ dispSub && /* @__PURE__ */ jsx2("span", { className: "di-hex", children: dispSub })
1769
+ ] }),
1770
+ isExpanded && /* @__PURE__ */ jsx2(
1771
+ ColorDropdown,
1772
+ {
1773
+ value: val,
1774
+ pos: dropPos,
1775
+ colorPalette,
1776
+ onChange: (c, _tk) => {
1777
+ setPendingColors((prev) => ({ ...prev, [prop]: c }));
1778
+ liveApply(prop, c);
1779
+ },
1780
+ onClose: () => setExpandedColor(null),
1781
+ onAddToken: (v) => handleAddToken(v, prop)
1782
+ }
1783
+ )
1784
+ ] }, prop);
1785
+ })
1786
+ ] }),
1787
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1788
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u5E03\u5C40" }),
1789
+ /* @__PURE__ */ jsxs("div", { className: "di-layout-grid", children: [
1790
+ /* @__PURE__ */ jsxs("div", { className: "di-layout-card", children: [
1791
+ /* @__PURE__ */ jsx2("div", { className: "di-layout-card-title", children: "\u4F4D\u7F6E" }),
1792
+ /* @__PURE__ */ jsxs("div", { className: "di-nudge-pad", "aria-label": "\u79FB\u52A8\u5143\u7D20", children: [
1793
+ /* @__PURE__ */ jsx2("button", { className: "di-nudge-btn di-nudge-btn--up", onClick: () => nudgeSelected(0, -1), title: "\u4E0A\u79FB 1px", children: "\u2191" }),
1794
+ /* @__PURE__ */ jsx2("button", { className: "di-nudge-btn di-nudge-btn--left", onClick: () => nudgeSelected(-1, 0), title: "\u5DE6\u79FB 1px", children: "\u2190" }),
1795
+ /* @__PURE__ */ jsxs("div", { className: "di-nudge-readout", children: [
1796
+ /* @__PURE__ */ jsxs("span", { children: [
1797
+ "X ",
1798
+ Math.round((pendingTranslate ?? translateVal).x)
1799
+ ] }),
1800
+ /* @__PURE__ */ jsxs("span", { children: [
1801
+ "Y ",
1802
+ Math.round((pendingTranslate ?? translateVal).y)
1803
+ ] })
1804
+ ] }),
1805
+ /* @__PURE__ */ jsx2("button", { className: "di-nudge-btn di-nudge-btn--right", onClick: () => nudgeSelected(1, 0), title: "\u53F3\u79FB 1px", children: "\u2192" }),
1806
+ /* @__PURE__ */ jsx2("button", { className: "di-nudge-btn di-nudge-btn--down", onClick: () => nudgeSelected(0, 1), title: "\u4E0B\u79FB 1px", children: "\u2193" })
1807
+ ] })
1808
+ ] }),
1809
+ /* @__PURE__ */ jsxs("div", { className: "di-layout-card di-layout-card--size", children: [
1810
+ /* @__PURE__ */ jsx2("div", { className: "di-layout-card-title", children: "\u5C3A\u5BF8" }),
1811
+ [
1812
+ { key: "width", label: "W", current: widthVal, pending: pendingWidth, mode: widthMode, pendingMode: pendingWidthMode },
1813
+ { key: "height", label: "H", current: heightVal, pending: pendingHeight, mode: heightMode, pendingMode: pendingHeightMode }
1814
+ ].map((item) => {
1815
+ const cur = displaySizeValue(item.pending, item.current);
1816
+ const inputVal = sizeDraft[item.key] ?? cur;
1817
+ const activeMode = item.pendingMode ?? item.mode;
1818
+ return /* @__PURE__ */ jsxs("div", { className: "di-size-row", children: [
1819
+ /* @__PURE__ */ jsx2("span", { className: "di-size-axis", children: item.label }),
1820
+ /* @__PURE__ */ jsx2(
1821
+ "input",
1822
+ {
1823
+ className: "di-size-value",
1824
+ value: inputVal,
1825
+ "aria-label": `${item.label} \u81EA\u5B9A\u4E49\u5C3A\u5BF8`,
1826
+ onChange: (e) => {
1827
+ setSizeDraft((prev) => ({ ...prev, [item.key]: e.target.value }));
1828
+ setSize(item.key, e.target.value, "fixed");
1829
+ },
1830
+ onFocus: (e) => {
1831
+ setSizeDraft((prev) => ({ ...prev, [item.key]: cur }));
1832
+ e.currentTarget.select();
1833
+ },
1834
+ onClick: (e) => e.currentTarget.select(),
1835
+ onMouseUp: (e) => e.preventDefault(),
1836
+ onBlur: () => setSizeDraft((prev) => {
1837
+ const next = { ...prev };
1838
+ delete next[item.key];
1839
+ return next;
1840
+ }),
1841
+ onKeyDown: (e) => {
1842
+ if (e.key === "Enter") e.currentTarget.blur();
1843
+ }
1844
+ }
1845
+ ),
1846
+ /* @__PURE__ */ jsx2(
1847
+ "select",
1848
+ {
1849
+ className: "di-size-mode-select",
1850
+ value: activeMode,
1851
+ onChange: (e) => {
1852
+ const mode = e.target.value;
1853
+ setSize(item.key, item.current, mode);
1854
+ },
1855
+ children: SIZE_OPTIONS.map((opt) => /* @__PURE__ */ jsx2("option", { value: opt.mode, children: opt.label }, opt.mode))
1856
+ }
1857
+ )
1858
+ ] }, item.key);
1859
+ })
1860
+ ] })
1861
+ ] })
1862
+ ] }),
1863
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1864
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u9634\u5F71" }),
1865
+ /* @__PURE__ */ jsx2("div", { className: "di-shadow-grid", children: (() => {
1866
+ const activeShadow = pendingShadow || shadowVal;
1867
+ const shadowDisplay = getShadowDisplay(activeShadow, shadowAuthoredVal, shadowTokens);
1868
+ const matchedOption = shadowOptions.find((option) => canonicalizeShadowValue(option.value) === canonicalizeShadowValue(activeShadow)) ?? null;
1869
+ const activeOption = matchedOption ?? shadowOptions[0];
1870
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1871
+ /* @__PURE__ */ jsxs("div", { className: "di-shadow-card", style: { position: "relative" }, children: [
1872
+ /* @__PURE__ */ jsx2("div", { className: "di-shadow-card-title", children: "Token" }),
1873
+ /* @__PURE__ */ jsxs(
1874
+ "button",
1875
+ {
1876
+ className: "di-border-style-single",
1877
+ onClick: (e) => {
1878
+ const r = e.currentTarget.getBoundingClientRect();
1879
+ setDropPos(calcDropPos(r));
1880
+ setShowShadowDrop((v) => !v);
1881
+ },
1882
+ children: [
1883
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: activeOption.cssVar ? void 0 : { color: "#9ca3af" }, children: activeOption.label }),
1884
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: activeOption.cssVar || "none" }),
1885
+ /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", fill: "none", style: { marginLeft: "auto", flexShrink: 0 }, children: /* @__PURE__ */ jsx2("path", { d: "M1 1l3 3 3-3", stroke: "#9ca3af", strokeWidth: "1.5", strokeLinecap: "round" }) })
1886
+ ]
1887
+ }
1888
+ ),
1889
+ showShadowDrop && /* @__PURE__ */ jsxs(Fragment, { children: [
1890
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 99997 }, onClick: () => setShowShadowDrop(false) }),
1891
+ /* @__PURE__ */ jsxs("div", { className: "di-border-style-drop di-shadow-drop", style: { position: "fixed", top: dropPos.top, left: dropPos.left, zIndex: 99998 }, children: [
1892
+ shadowOptions.map((option) => {
1893
+ const isOn = canonicalizeShadowValue(option.value) === canonicalizeShadowValue(activeShadow);
1894
+ return /* @__PURE__ */ jsxs(
1895
+ "button",
1896
+ {
1897
+ className: `di-border-style-drop-item${isOn ? " di-border-style-drop-item--on" : ""}`,
1898
+ onClick: () => {
1899
+ setPendingShadow(option.value);
1900
+ liveApply("box-shadow", option.value);
1901
+ setShowShadowDrop(false);
1902
+ },
1903
+ children: [
1904
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: option.cssVar ? void 0 : { color: "#9ca3af" }, children: option.label }),
1905
+ /* @__PURE__ */ jsxs("span", { className: "di-shadow-drop-meta", children: [
1906
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: option.cssVar || option.usage }),
1907
+ option.cssVar && /* @__PURE__ */ jsx2("span", { className: "di-shadow-drop-value", children: option.value })
1908
+ ] })
1909
+ ]
1910
+ },
1911
+ option.cssVar || option.label
1912
+ );
1913
+ }),
1914
+ /* @__PURE__ */ jsxs(
1915
+ "button",
1916
+ {
1917
+ className: `di-border-style-drop-item${matchedOption ? "" : " di-border-style-drop-item--on"}`,
1918
+ onClick: () => {
1919
+ const next = prompt("\u81EA\u5B9A\u4E49\u9634\u5F71\uFF08\u5982 0 12px 30px rgba(15,23,42,0.08)\uFF09", activeShadow === "none" ? "" : activeShadow);
1920
+ if (next === null) return;
1921
+ const value = next.trim() || "none";
1922
+ setPendingShadow(value);
1923
+ liveApply("box-shadow", value);
1924
+ setShowShadowDrop(false);
1925
+ },
1926
+ children: [
1927
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", children: "\u81EA\u5B9A\u4E49" }),
1928
+ /* @__PURE__ */ jsxs("span", { className: "di-shadow-drop-meta", children: [
1929
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: shadowDisplay.label }),
1930
+ /* @__PURE__ */ jsx2("span", { className: "di-shadow-drop-value", children: formatShadowDisplay(activeShadow) })
1931
+ ] })
1932
+ ]
1933
+ }
1934
+ )
1935
+ ] })
1936
+ ] })
1937
+ ] }),
1938
+ /* @__PURE__ */ jsxs("div", { className: "di-shadow-card", children: [
1939
+ /* @__PURE__ */ jsx2("div", { className: "di-shadow-card-title", children: "\u9884\u89C8" }),
1940
+ /* @__PURE__ */ jsxs("div", { className: "di-shadow-preview-row", children: [
1941
+ /* @__PURE__ */ jsx2("div", { className: "di-shadow-preview-chip", style: { boxShadow: activeShadow === "none" ? "none" : activeShadow } }),
1942
+ /* @__PURE__ */ jsxs("div", { className: "di-shadow-preview-copy", children: [
1943
+ /* @__PURE__ */ jsx2("span", { className: shadowDisplay.isHardcoded ? "di-token-name--plain" : "di-token-name", children: shadowDisplay.label }),
1944
+ /* @__PURE__ */ jsx2("span", { className: "di-hex di-hex--wrap", children: shadowDisplay.sub })
1945
+ ] })
1946
+ ] })
1947
+ ] })
1948
+ ] });
1949
+ })() })
1950
+ ] }),
1951
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
1952
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u8FB9\u6846" }),
1953
+ /* @__PURE__ */ jsxs("div", { className: "di-border-row", children: [
1954
+ (() => {
1955
+ const bcVal = pendingBorderColor || borderColorVal;
1956
+ const { label: bcLabel, sub: bcSub, isHardcoded: bcHard } = getDisplayLabel(bcVal || "", tokenMap, colorPalette, tokenLabels);
1957
+ const bcDark = (() => {
1958
+ const m = bcVal?.match(/^#([0-9a-f]{6})$/i);
1959
+ if (!m) return false;
1960
+ const r = parseInt(m[1].slice(0, 2), 16), g = parseInt(m[1].slice(2, 4), 16), b = parseInt(m[1].slice(4, 6), 16);
1961
+ return (r * 299 + g * 587 + b * 114) / 1e3 < 128;
1962
+ })();
1963
+ return /* @__PURE__ */ jsxs("div", { className: "di-border-card", children: [
1964
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u989C\u8272" }),
1965
+ /* @__PURE__ */ jsxs("div", { className: "di-border-color-body", children: [
1966
+ /* @__PURE__ */ jsx2(
1967
+ "button",
1968
+ {
1969
+ className: `di-swatch-btn${bcDark ? " di-swatch-btn--dark" : ""}${!bcVal ? " di-swatch-btn--empty" : ""}`,
1970
+ style: { background: bcVal || void 0 },
1971
+ onClick: (e) => {
1972
+ const r = e.currentTarget.getBoundingClientRect();
1973
+ setDropPos(calcDropPos(r));
1974
+ setExpandedBorderColor((v) => !v);
1975
+ },
1976
+ children: /* @__PURE__ */ jsx2("svg", { width: "10", height: "6", viewBox: "0 0 10 6", fill: "none", children: /* @__PURE__ */ jsx2("path", { d: "M1 1l4 4 4-4", stroke: !bcVal ? "#9ca3af" : bcDark ? "#fff" : "#374151", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1977
+ }
1978
+ ),
1979
+ /* @__PURE__ */ jsxs("div", { className: "di-border-color-info", children: [
1980
+ /* @__PURE__ */ jsx2("span", { className: bcHard ? "di-token-name--plain" : "di-token-name", children: bcVal ? bcLabel : "\u672A\u8BBE\u7F6E" }),
1981
+ bcSub && /* @__PURE__ */ jsx2("span", { className: "di-hex", children: bcSub })
1982
+ ] })
1983
+ ] }),
1984
+ expandedBorderColor && /* @__PURE__ */ jsx2(
1985
+ ColorDropdown,
1986
+ {
1987
+ value: bcVal || "",
1988
+ pos: dropPos,
1989
+ colorPalette,
1990
+ onChange: (c) => {
1991
+ setPendingBorderColor(c);
1992
+ liveApply("border-color", c);
1993
+ if ((pendingBorderWidth || borderWidthVal) === "0px") {
1994
+ liveApply("border-width", "1px");
1995
+ liveApply("border-style", "solid");
1996
+ setPendingBorderWidth("1px");
1997
+ setPendingBorderStyle("solid");
1998
+ }
1999
+ },
2000
+ onClose: () => setExpandedBorderColor(false),
2001
+ onAddToken: (v) => handleAddToken(v, "border-color")
2002
+ }
2003
+ )
2004
+ ] });
2005
+ })(),
2006
+ /* @__PURE__ */ jsxs("div", { className: "di-border-card", style: { display: "flex", flexDirection: "column", gap: 6 }, children: [
2007
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u7C97\u7EC6" }),
2008
+ (() => {
2009
+ const cur = pendingBorderWidth || borderWidthVal || "0px";
2010
+ const idx = borderWidthSteps.indexOf(cur);
2011
+ return /* @__PURE__ */ jsxs("div", { className: "di-spinner", children: [
2012
+ /* @__PURE__ */ jsx2("span", { className: "di-spinner-val", children: cur }),
2013
+ /* @__PURE__ */ jsxs("div", { className: "di-spinner-btns", children: [
2014
+ /* @__PURE__ */ jsx2(
2015
+ "button",
2016
+ {
2017
+ className: "di-spinner-up",
2018
+ disabled: idx <= 0,
2019
+ onClick: () => {
2020
+ const nv = borderWidthSteps[Math.max(0, idx - 1)];
2021
+ setPendingBorderWidth(nv);
2022
+ liveApply("border-width", nv);
2023
+ },
2024
+ children: /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", children: /* @__PURE__ */ jsx2("path", { d: "M1 4l3-3 3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }) })
2025
+ }
2026
+ ),
2027
+ /* @__PURE__ */ jsx2(
2028
+ "button",
2029
+ {
2030
+ className: "di-spinner-dn",
2031
+ disabled: idx >= borderWidthSteps.length - 1,
2032
+ onClick: () => {
2033
+ const nv = borderWidthSteps[Math.min(borderWidthSteps.length - 1, idx < 0 ? 1 : idx + 1)];
2034
+ setPendingBorderWidth(nv);
2035
+ liveApply("border-width", nv);
2036
+ },
2037
+ children: /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", children: /* @__PURE__ */ jsx2("path", { d: "M1 1l3 3 3-3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", fill: "none" }) })
2038
+ }
2039
+ )
2040
+ ] })
2041
+ ] });
2042
+ })()
2043
+ ] }),
2044
+ /* @__PURE__ */ jsxs("div", { className: "di-border-card", style: { position: "relative" }, children: [
2045
+ /* @__PURE__ */ jsx2("div", { className: "di-border-card-title", children: "\u6837\u5F0F" }),
2046
+ (() => {
2047
+ const cur = pendingBorderStyle || borderStyleVal;
2048
+ const curOpt = BORDER_STYLE_OPTIONS.find((o) => o.value === cur) ?? BORDER_STYLE_OPTIONS[0];
2049
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2050
+ /* @__PURE__ */ jsxs(
2051
+ "button",
2052
+ {
2053
+ className: "di-border-style-single",
2054
+ onClick: (e) => {
2055
+ const r = e.currentTarget.getBoundingClientRect();
2056
+ setDropPos(calcDropPos(r));
2057
+ setShowStyleDrop((v) => !v);
2058
+ },
2059
+ children: [
2060
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: curOpt.value === "none" ? { color: "#9ca3af" } : void 0, children: curOpt.label }),
2061
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: curOpt.title }),
2062
+ /* @__PURE__ */ jsx2("svg", { width: "8", height: "5", viewBox: "0 0 8 5", fill: "none", style: { marginLeft: "auto", flexShrink: 0 }, children: /* @__PURE__ */ jsx2("path", { d: "M1 1l3 3 3-3", stroke: "#9ca3af", strokeWidth: "1.5", strokeLinecap: "round" }) })
2063
+ ]
2064
+ }
2065
+ ),
2066
+ showStyleDrop && /* @__PURE__ */ jsxs(Fragment, { children: [
2067
+ /* @__PURE__ */ jsx2("div", { style: { position: "fixed", inset: 0, zIndex: 99997 }, onClick: () => setShowStyleDrop(false) }),
2068
+ /* @__PURE__ */ jsx2("div", { className: "di-border-style-drop", style: { position: "fixed", top: dropPos.top, left: dropPos.left, zIndex: 99998 }, children: BORDER_STYLE_OPTIONS.map((opt) => /* @__PURE__ */ jsxs(
2069
+ "button",
2070
+ {
2071
+ className: `di-border-style-drop-item${cur === opt.value ? " di-border-style-drop-item--on" : ""}`,
2072
+ onClick: () => {
2073
+ setPendingBorderStyle(opt.value);
2074
+ liveApply("border-style", opt.value);
2075
+ setShowStyleDrop(false);
2076
+ },
2077
+ children: [
2078
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-label", style: opt.value === "none" ? { color: "#9ca3af" } : void 0, children: opt.label }),
2079
+ /* @__PURE__ */ jsx2("span", { className: "di-border-style-name", children: opt.title })
2080
+ ]
2081
+ },
2082
+ opt.value
2083
+ )) })
2084
+ ] })
2085
+ ] });
2086
+ })()
2087
+ ] })
2088
+ ] })
2089
+ ] }),
2090
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
2091
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u5706\u89D2" }),
2092
+ /* @__PURE__ */ jsxs("div", { className: "di-preset-row", children: [
2093
+ radiusPresets.map((opt) => {
2094
+ const current = pendingRadius || radiusVal;
2095
+ const isOn = matchPreset(radiusPresets, current)?.value === opt.value;
2096
+ return /* @__PURE__ */ jsxs(
2097
+ "button",
2098
+ {
2099
+ className: `di-preset${isOn ? " di-preset--on" : ""}`,
2100
+ onClick: () => {
2101
+ setPendingRadius(opt.value);
2102
+ setCustomRadius("");
2103
+ liveApply("border-radius", opt.value);
2104
+ },
2105
+ children: [
2106
+ /* @__PURE__ */ jsx2("span", { className: "di-preset-label", children: opt.label }),
2107
+ opt.sub && /* @__PURE__ */ jsx2("span", { className: "di-preset-sub", children: opt.sub })
2108
+ ]
2109
+ },
2110
+ opt.label
2111
+ );
2112
+ }),
2113
+ (() => {
2114
+ const cur = pendingRadius || radiusVal;
2115
+ const isCustom = !!cur && cur !== "0px" && !matchPreset(radiusPresets, cur);
2116
+ return /* @__PURE__ */ jsxs(
2117
+ "button",
2118
+ {
2119
+ className: `di-preset${isCustom ? " di-preset--on" : ""}`,
2120
+ onClick: () => {
2121
+ const v = prompt("\u81EA\u5B9A\u4E49\u5706\u89D2\uFF08\u5982 12px\uFF09", cur || "");
2122
+ if (v) {
2123
+ setPendingRadius(v);
2124
+ setCustomRadius(v);
2125
+ liveApply("border-radius", v);
2126
+ }
2127
+ },
2128
+ children: [
2129
+ /* @__PURE__ */ jsx2("span", { className: "di-preset-label", children: "\u81EA\u5B9A\u4E49" }),
2130
+ isCustom && /* @__PURE__ */ jsx2("span", { className: "di-preset-sub", children: cur })
2131
+ ]
2132
+ }
2133
+ );
2134
+ })()
2135
+ ] })
2136
+ ] }),
2137
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
2138
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u95F4\u8DDD" }),
2139
+ /* @__PURE__ */ jsxs("div", { className: "di-space-row", children: [
2140
+ /* @__PURE__ */ jsx2(
2141
+ SpaceCard,
2142
+ {
2143
+ title: "\u5185\u8FB9\u8DDD",
2144
+ variant: "padding",
2145
+ value: pendingPadding || paddingVal,
2146
+ spaceSteps,
2147
+ onChange: (v) => {
2148
+ setPendingPadding(v);
2149
+ liveApply("padding", v);
2150
+ }
2151
+ }
2152
+ ),
2153
+ /* @__PURE__ */ jsx2(
2154
+ SpaceCard,
2155
+ {
2156
+ title: "\u5916\u8FB9\u8DDD",
2157
+ variant: "margin",
2158
+ value: pendingMargin || marginVal,
2159
+ spaceSteps,
2160
+ onChange: (v) => {
2161
+ setPendingMargin(v);
2162
+ liveApply("margin", v);
2163
+ }
2164
+ }
2165
+ ),
2166
+ /* @__PURE__ */ jsx2(
2167
+ SpaceCard,
2168
+ {
2169
+ title: "\u5143\u7D20\u95F4\u8DDD",
2170
+ variant: "gap",
2171
+ value: pendingGap || gapVal,
2172
+ spaceSteps,
2173
+ onChange: (v) => {
2174
+ setPendingGap(v);
2175
+ liveApply("gap", v);
2176
+ }
2177
+ }
2178
+ )
2179
+ ] })
2180
+ ] }),
2181
+ /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
2182
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u672C\u6B21\u4FEE\u6539\u5185\u5BB9" }),
2183
+ /* @__PURE__ */ jsxs("div", { className: "di-inbox-summary", children: [
2184
+ /* @__PURE__ */ jsxs("div", { className: "di-inbox-pill", children: [
2185
+ "\u5DF2\u8BB0\u5F55 ",
2186
+ styleIntentSummary.pendingCount
2187
+ ] }),
2188
+ (() => {
2189
+ const currentChanges = getPendingChangeRecords();
2190
+ if (currentChanges.length === 0) return null;
2191
+ const el = selectedRef.current;
2192
+ const targetClasses = el ? getClasses(el) : [];
2193
+ const targetLabel = el ? targetClasses.length ? `${el.tagName.toLowerCase()}.${targetClasses.join(".")}` : el.tagName.toLowerCase() : "\u5F53\u524D\u5BF9\u8C61";
2194
+ return /* @__PURE__ */ jsx2("div", { className: "di-inbox-list", children: /* @__PURE__ */ jsxs("div", { className: "di-inbox-card di-inbox-card--draft", children: [
2195
+ /* @__PURE__ */ jsx2("div", { className: "di-inbox-card-head", children: /* @__PURE__ */ jsx2("div", { className: "di-inbox-target", children: targetLabel }) }),
2196
+ /* @__PURE__ */ jsx2("ol", { className: "di-inbox-change-list", children: currentChanges.map((change, idx) => /* @__PURE__ */ jsx2("li", { className: "di-inbox-change-item", children: /* @__PURE__ */ jsxs("span", { className: "di-inbox-change-text", children: [
2197
+ idx + 1,
2198
+ ". ",
2199
+ getChangeLabel(change.prop),
2200
+ "\uFF1A",
2201
+ change.from,
2202
+ " \u2192 ",
2203
+ change.val
2204
+ ] }) }, `current-${change.prop}-${idx}`)) })
2205
+ ] }) });
2206
+ })(),
2207
+ styleIntentSummary.pendingEntries.length > 0 ? /* @__PURE__ */ jsx2("div", { className: "di-inbox-list", children: styleIntentSummary.pendingEntries.map((entry) => /* @__PURE__ */ jsxs("div", { className: "di-inbox-card", children: [
2208
+ /* @__PURE__ */ jsxs("div", { className: "di-inbox-card-head", children: [
2209
+ /* @__PURE__ */ jsx2("div", { className: "di-inbox-target", children: entry.targetLabel || entry.selector || "\u672A\u547D\u540D\u5BF9\u8C61" }),
2210
+ /* @__PURE__ */ jsx2("button", { className: "di-inbox-delete", onClick: () => handleDeleteStyleIntent(entry.id), children: "\u5220\u9664\u5BF9\u8C61" })
2211
+ ] }),
2212
+ entry.note ? /* @__PURE__ */ jsx2("div", { className: "di-inbox-note", children: entry.note }) : null,
2213
+ /* @__PURE__ */ jsx2("ol", { className: "di-inbox-change-list", children: entry.entries.flatMap((group) => group.changes.map((change) => ({ selector: group.selector, ...change }))).map((change, idx) => /* @__PURE__ */ jsxs("li", { className: "di-inbox-change-item", children: [
2214
+ /* @__PURE__ */ jsxs("span", { className: "di-inbox-change-text", children: [
2215
+ idx + 1,
2216
+ ". ",
2217
+ getChangeLabel(change.prop),
2218
+ "\uFF1A",
2219
+ change.from ? `${change.from} \u2192 ` : "",
2220
+ change.val
2221
+ ] }),
2222
+ /* @__PURE__ */ jsx2(
2223
+ "button",
2224
+ {
2225
+ className: "di-inbox-delete di-inbox-delete--mini",
2226
+ onClick: () => handleDeleteStyleIntent(entry.id, change.selector, change.prop),
2227
+ children: "\u5220\u9664"
2228
+ }
2229
+ )
2230
+ ] }, `${entry.id}-${change.selector}-${change.prop}-${idx}`)) })
2231
+ ] }, entry.id)) }) : /* @__PURE__ */ jsx2("div", { className: "di-empty", children: "\u8FD8\u6CA1\u6709\u8BB0\u5F55\u7684\u4FEE\u6539" })
2232
+ ] })
2233
+ ] })
2234
+ ] }),
2235
+ (() => {
2236
+ const hasPending = Object.keys(pendingColors).length > 0 || !!pendingTextColor || !!pendingRadius || !!pendingShadow || !!pendingBorderColor || !!pendingBorderWidth || !!pendingBorderStyle || !!pendingFontSize || !!pendingFontWeight || !!pendingPadding || !!pendingMargin || !!pendingGap || !!pendingTranslate || !!pendingWidth || !!pendingHeight || !!note;
2237
+ return /* @__PURE__ */ jsxs("div", { className: "di-foot", children: [
2238
+ /* @__PURE__ */ jsx2("button", { className: "di-btn-cancel", onClick: handleReset, children: "\u91CD\u7F6E" }),
2239
+ /* @__PURE__ */ jsx2("button", { className: "di-btn-cancel", onClick: () => {
2240
+ const el = selectedRef.current;
2241
+ if (!el) return;
2242
+ const effectiveColors = { ...colors, ...pendingColors };
2243
+ const pending = [
2244
+ ...Object.entries(effectiveColors).filter(([, v]) => v && v !== "transparent"),
2245
+ [pendingTextColor || textColorVal ? "color" : "", pendingTextColor || textColorVal],
2246
+ [pendingRadius || radiusVal ? "border-radius" : "", pendingRadius || radiusVal],
2247
+ [pendingShadow || (shadowVal !== "none" ? shadowVal : "") ? "box-shadow" : "", pendingShadow || (shadowVal !== "none" ? shadowVal : "")],
2248
+ [pendingBorderColor || borderColorVal ? "border-color" : "", pendingBorderColor || borderColorVal],
2249
+ [pendingBorderWidth || (borderWidthVal !== "0px" ? borderWidthVal : "") ? "border-width" : "", pendingBorderWidth || borderWidthVal],
2250
+ [pendingBorderStyle || (borderStyleVal !== "none" ? borderStyleVal : "") ? "border-style" : "", pendingBorderStyle || borderStyleVal],
2251
+ [pendingFontSize || fontSizeVal ? "font-size" : "", pendingFontSize || fontSizeVal],
2252
+ [pendingFontWeight || fontWeightVal ? "font-weight" : "", pendingFontWeight || fontWeightVal],
2253
+ [pendingPadding || (paddingVal !== "0px" ? paddingVal : "") ? "padding" : "", pendingPadding || paddingVal],
2254
+ [pendingMargin || (marginVal !== "0px" ? marginVal : "") ? "margin" : "", pendingMargin || marginVal],
2255
+ [pendingGap || (gapVal !== "0px" ? gapVal : "") ? "gap" : "", pendingGap || gapVal],
2256
+ [pendingWidth || widthVal ? "width" : "", pendingWidth || widthVal],
2257
+ [pendingHeight || heightVal ? "height" : "", pendingHeight || heightVal]
2258
+ ].filter(([k, v]) => k && v);
2259
+ const selector = getSelectorForScope(el, scope);
2260
+ const css = pending.length > 0 ? `${selector} {
2261
+ ${pending.map(([p, v]) => ` ${p}: ${v};`).join("\n")}
2262
+ }` : `/* ${selector} \u2014 \u6682\u65E0\u6539\u52A8 */`;
2263
+ const ta = document.createElement("textarea");
2264
+ ta.value = css;
2265
+ ta.style.cssText = "position:fixed;opacity:0;top:0;left:0";
2266
+ document.body.appendChild(ta);
2267
+ ta.select();
2268
+ document.execCommand("copy");
2269
+ document.body.removeChild(ta);
2270
+ _copiedStyles = pending.map(([p, v]) => ({ prop: p, val: v }));
2271
+ setHasCopied(true);
2272
+ setCopyMsg("\u5DF2\u590D\u5236 \u2713");
2273
+ setTimeout(() => setCopyMsg(""), 1500);
2274
+ }, children: copyMsg || "\u590D\u5236\u6837\u5F0F" }),
2275
+ /* @__PURE__ */ jsx2(
2276
+ "button",
2277
+ {
2278
+ className: "di-btn-save",
2279
+ onClick: handleSubmitToAi,
2280
+ disabled: !hasPending,
2281
+ style: !hasPending ? { opacity: 0.4, cursor: "not-allowed" } : {},
2282
+ title: "\u590D\u5236\u4E00\u6BB5\u53EF\u76F4\u63A5\u53D1\u7ED9 AI \u7684\u4EFB\u52A1\u6587\u672C",
2283
+ children: submitMsg || "\u53D1\u9001\u7ED9AI"
2284
+ }
2285
+ )
2286
+ ] });
2287
+ })(),
2288
+ hasCopied && /* @__PURE__ */ jsx2("div", { className: "di-paste-bar", children: /* @__PURE__ */ jsxs("button", { className: "di-btn-paste", onClick: () => {
2289
+ _copiedStyles.forEach(({ prop, val }) => {
2290
+ if (prop === "background-color" || prop === "background")
2291
+ setPendingColors((prev) => ({ ...prev, [prop]: val }));
2292
+ else if (prop === "color") setPendingTextColor(val);
2293
+ else if (prop === "border-radius") setPendingRadius(val);
2294
+ else if (prop === "box-shadow") setPendingShadow(val);
2295
+ else if (prop === "border-color") setPendingBorderColor(val);
2296
+ else if (prop === "border-width") setPendingBorderWidth(val);
2297
+ else if (prop === "border-style") setPendingBorderStyle(val);
2298
+ else if (prop === "font-size") setPendingFontSize(val);
2299
+ else if (prop === "font-weight") setPendingFontWeight(val);
2300
+ else if (prop === "padding") setPendingPadding(val);
2301
+ else if (prop === "margin") setPendingMargin(val);
2302
+ else if (prop === "gap") setPendingGap(val);
2303
+ else if (prop === "width") setPendingWidth(val);
2304
+ else if (prop === "height") setPendingHeight(val);
2305
+ });
2306
+ applyToDOM(_copiedStyles);
2307
+ }, children: [
2308
+ "\u7C98\u8D34\u6837\u5F0F\uFF08",
2309
+ _copiedStyles.length,
2310
+ " \u4E2A\u5C5E\u6027\uFF09"
2311
+ ] }) })
2312
+ ]
2313
+ }
2314
+ )
2315
+ ] });
2316
+ }
2317
+ var CODE_FILES = [
2318
+ { name: "src/", desc: "React \u7EC4\u4EF6\u3001\u9875\u9762\u3001\u6837\u5F0F\u6E90\u7801\uFF08\u4E0D\u542B\u8C03\u8BD5\u5DE5\u5177\uFF09" },
2319
+ { name: "index.html", desc: "\u5E94\u7528\u5165\u53E3 HTML" },
2320
+ { name: "package.json", desc: "\u4F9D\u8D56\u5305\u4E0E\u811A\u672C\u914D\u7F6E" },
2321
+ { name: "tsconfig.json", desc: "TypeScript \u7F16\u8BD1\u914D\u7F6E" },
2322
+ { name: "vite.config.ts", desc: "\u6784\u5EFA\u5DE5\u5177\u914D\u7F6E" }
2323
+ ];
2324
+ var PRODUCT_FILES = [
2325
+ { name: "docs/PRODUCT_PLAN.md", desc: "\u4EA7\u54C1\u89C4\u5212\u4E0E\u8DEF\u7EBF\u56FE" },
2326
+ { name: "docs/PROJECT.md", desc: "\u9879\u76EE\u80CC\u666F\u4E0E\u76EE\u6807\u6982\u8FF0" },
2327
+ { name: "docs/DECISIONS.md", desc: "\u5173\u952E\u4EA7\u54C1\u51B3\u7B56\u8BB0\u5F55" },
2328
+ { name: "docs/CHANGELOG.md", desc: "\u529F\u80FD\u8FED\u4EE3\u53D8\u66F4\u65E5\u5FD7" },
2329
+ { name: "docs/CODE_STRUCTURE.md", desc: "\u524D\u7AEF\u76EE\u5F55\u7ED3\u6784\u8BF4\u660E" },
2330
+ { name: "docs/DESIGN_STANDARDS.md", desc: "\u8BBE\u8BA1\u89C4\u8303\u603B\u89C8" }
2331
+ ];
2332
+ var DESIGN_FILES = [
2333
+ { name: "docs/design/OVERVIEW.md", desc: "\u8BBE\u8BA1\u7CFB\u7EDF\u603B\u89C8" },
2334
+ { name: "docs/design/tokens.md", desc: "Design Token \u4F7F\u7528\u8BF4\u660E" },
2335
+ { name: "docs/design/layout.md", desc: "\u9875\u9762\u5E03\u5C40\u89C4\u8303" },
2336
+ { name: "docs/design/component-index.md", desc: "\u7EC4\u4EF6\u6E05\u5355\u7D22\u5F15" },
2337
+ { name: "docs/design/business-components.md", desc: "\u4E1A\u52A1\u7EC4\u4EF6\u8BF4\u660E" }
2338
+ ];
2339
+ function FileList({ files }) {
2340
+ return /* @__PURE__ */ jsx2("ul", { className: "di-dl-file-list", children: files.map((f) => /* @__PURE__ */ jsxs("li", { children: [
2341
+ /* @__PURE__ */ jsx2("span", { className: "di-dl-file-name", children: f.name }),
2342
+ /* @__PURE__ */ jsx2("span", { className: "di-dl-file-desc", children: f.desc })
2343
+ ] }, f.name)) });
2344
+ }
2345
+ function DownloadButton() {
2346
+ const { endpoints } = useDevInspectorConfig();
2347
+ const [open, setOpen] = useState(false);
2348
+ const [status, setStatus] = useState("idle");
2349
+ const [filePath, setFilePath] = useState("");
2350
+ const [details, setDetails] = useState({ code: false, product: false, design: false });
2351
+ const [opts, setOpts] = useState({ code: true, product: false, design: false });
2352
+ function toggle(k) {
2353
+ setOpts((prev) => ({ ...prev, [k]: !prev[k] }));
2354
+ }
2355
+ function toggleDetail(k) {
2356
+ setDetails((prev) => ({ ...prev, [k]: !prev[k] }));
2357
+ }
2358
+ function handleOpen() {
2359
+ setOpen((v) => !v);
2360
+ if (open) setStatus("idle");
2361
+ }
2362
+ async function download() {
2363
+ if (!opts.code && !opts.product && !opts.design) return;
2364
+ setStatus("packing");
2365
+ try {
2366
+ const params = new URLSearchParams({
2367
+ code: opts.code ? "1" : "0",
2368
+ product: opts.product ? "1" : "0",
2369
+ design: opts.design ? "1" : "0"
2370
+ });
2371
+ const res = await fetch(`${endpoints.handoff}?${params}`);
2372
+ const json = await res.json();
2373
+ if (!json.ok) throw new Error(json.error);
2374
+ setFilePath(json.path);
2375
+ setStatus("done");
2376
+ } catch {
2377
+ setStatus("error");
2378
+ }
2379
+ }
2380
+ async function revealInFinder() {
2381
+ await fetch(`${endpoints.reveal}?path=${encodeURIComponent(filePath)}`);
2382
+ }
2383
+ const sections = [
2384
+ { key: "code", label: "\u524D\u7AEF\u4EE3\u7801", files: CODE_FILES },
2385
+ { key: "product", label: "\u4EA7\u54C1\u6587\u6863", files: PRODUCT_FILES },
2386
+ { key: "design", label: "\u8BBE\u8BA1\u6587\u6863", files: DESIGN_FILES }
2387
+ ];
2388
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2389
+ /* @__PURE__ */ jsx2("button", { className: "di-dl-btn", onClick: handleOpen, children: "\u4E0B\u8F7D" }),
2390
+ open && /* @__PURE__ */ jsxs("div", { className: "di-dl-modal", children: [
2391
+ /* @__PURE__ */ jsx2("div", { className: "di-dl-title", children: "\u9009\u62E9\u4E0B\u8F7D\u5185\u5BB9" }),
2392
+ status === "packing" && /* @__PURE__ */ jsxs("div", { className: "di-dl-status", children: [
2393
+ /* @__PURE__ */ jsx2("span", { className: "di-dl-spinner" }),
2394
+ "\u6B63\u5728\u6253\u5305\uFF0C\u8BF7\u7A0D\u5019\u2026"
2395
+ ] }),
2396
+ status === "done" && /* @__PURE__ */ jsxs("div", { className: "di-dl-done", children: [
2397
+ /* @__PURE__ */ jsx2("div", { className: "di-dl-done-check", children: "\u2713 \u5DF2\u4FDD\u5B58\u81F3\u684C\u9762" }),
2398
+ /* @__PURE__ */ jsx2("div", { className: "di-dl-done-path", children: filePath.replace(/.*\//, "") }),
2399
+ /* @__PURE__ */ jsx2("button", { className: "di-dl-reveal-btn", onClick: revealInFinder, children: "\u5728 Finder \u4E2D\u663E\u793A" })
2400
+ ] }),
2401
+ status === "error" && /* @__PURE__ */ jsx2("div", { className: "di-dl-status di-dl-status--error", children: "\u6253\u5305\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u63A7\u5236\u53F0" }),
2402
+ (status === "idle" || status === "error") && /* @__PURE__ */ jsxs(Fragment, { children: [
2403
+ sections.map(({ key, label, files }) => /* @__PURE__ */ jsxs("div", { children: [
2404
+ /* @__PURE__ */ jsxs("div", { className: "di-dl-row", children: [
2405
+ /* @__PURE__ */ jsxs("label", { className: "di-dl-check-label", children: [
2406
+ /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: opts[key], onChange: () => toggle(key) }),
2407
+ label
2408
+ ] }),
2409
+ /* @__PURE__ */ jsx2("button", { className: "di-dl-detail-btn", onClick: () => toggleDetail(key), children: details[key] ? "\u6536\u8D77" : "\u8BE6\u60C5" })
2410
+ ] }),
2411
+ details[key] && /* @__PURE__ */ jsx2(FileList, { files })
2412
+ ] }, key)),
2413
+ /* @__PURE__ */ jsx2(
2414
+ "button",
2415
+ {
2416
+ className: "di-dl-confirm",
2417
+ onClick: download,
2418
+ disabled: !opts.code && !opts.product && !opts.design,
2419
+ children: "\u4E0B\u8F7D"
2420
+ }
2421
+ )
2422
+ ] })
2423
+ ] })
2424
+ ] });
2425
+ }
2426
+ function DevInspector() {
2427
+ const [active, setActive] = useState(false);
2428
+ const [tokenMap, setTokenMap] = useState({});
2429
+ const [panels, setPanels] = useState([]);
2430
+ const modalOpenRef = useRef(false);
2431
+ useEffect(() => {
2432
+ setTokenMap(scanTokenMap());
2433
+ }, []);
2434
+ useEffect(() => {
2435
+ const fn = (e) => {
2436
+ if (e.altKey && e.key === "i") setActive((v) => !v);
2437
+ if (e.key === "Escape") {
2438
+ setActive(false);
2439
+ setPanels([]);
2440
+ }
2441
+ };
2442
+ window.addEventListener("keydown", fn);
2443
+ return () => window.removeEventListener("keydown", fn);
2444
+ }, []);
2445
+ useEffect(() => {
2446
+ const onOver = (e) => {
2447
+ if (!active || modalOpenRef.current) return;
2448
+ const el = e.target;
2449
+ if (!el?.classList) return;
2450
+ if (isInsidePanel(el)) {
2451
+ el.classList.remove("di-hover");
2452
+ return;
2453
+ }
2454
+ el.classList.add("di-hover");
2455
+ };
2456
+ const onOut = (e) => {
2457
+ e.target?.classList?.remove("di-hover");
2458
+ };
2459
+ const onClick = (e) => {
2460
+ if (!active || modalOpenRef.current) return;
2461
+ const el = e.target;
2462
+ if (isInsidePanel(el)) return;
2463
+ e.preventDefault();
2464
+ e.stopPropagation();
2465
+ el.classList.remove("di-hover");
2466
+ setPanels(
2467
+ (prev) => prev.length === 0 ? [{ id: "main", el }] : prev.map((p, i) => i === 0 ? { ...p, el } : p)
2468
+ );
2469
+ };
2470
+ document.addEventListener("mouseover", onOver, true);
2471
+ document.addEventListener("mouseout", onOut, true);
2472
+ document.addEventListener("click", onClick, true);
2473
+ return () => {
2474
+ document.removeEventListener("mouseover", onOver, true);
2475
+ document.removeEventListener("mouseout", onOut, true);
2476
+ document.removeEventListener("click", onClick, true);
2477
+ };
2478
+ }, [active]);
2479
+ useEffect(() => {
2480
+ if (!active) {
2481
+ document.querySelectorAll(".di-selected,.di-hover").forEach((e) => e.classList.remove("di-selected", "di-hover"));
2482
+ }
2483
+ }, [active]);
2484
+ useEffect(() => {
2485
+ document.body.style.cursor = active ? "crosshair" : "";
2486
+ return () => {
2487
+ document.body.style.cursor = "";
2488
+ };
2489
+ }, [active]);
2490
+ const primaryEl = panels[0]?.el;
2491
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2492
+ /* @__PURE__ */ jsx2(DownloadButton, {}),
2493
+ /* @__PURE__ */ jsx2(
2494
+ "button",
2495
+ {
2496
+ className: `di-trigger${active ? " di-trigger--on" : ""}`,
2497
+ onClick: () => {
2498
+ setActive((v) => !v);
2499
+ if (active) setPanels([]);
2500
+ },
2501
+ title: "Dev Inspector (Alt+I)",
2502
+ children: active ? "\u9000\u51FA" : "\u7F16\u8F91"
2503
+ }
2504
+ ),
2505
+ active && primaryEl && (() => {
2506
+ const rect = primaryEl.getBoundingClientRect();
2507
+ const classes = getClasses(primaryEl);
2508
+ return /* @__PURE__ */ jsxs("div", { className: "di-label", style: { top: rect.top - 34, left: rect.left + rect.width / 2 }, children: [
2509
+ "\u5DF2\u9009\u4E2D ",
2510
+ classes[0] ?? primaryEl.tagName.toLowerCase()
2511
+ ] });
2512
+ })(),
2513
+ panels.map((p) => /* @__PURE__ */ jsx2(
2514
+ InspectorPanel,
2515
+ {
2516
+ targetEl: p.el,
2517
+ tokenMap,
2518
+ onTokenMapUpdate: (updates) => setTokenMap((prev) => ({ ...prev, ...updates })),
2519
+ onClose: () => setPanels((prev) => prev.filter((x) => x.id !== p.id))
2520
+ },
2521
+ p.id
2522
+ ))
2523
+ ] });
2524
+ }
2525
+
2526
+ // src/mount.tsx
2527
+ import { StrictMode } from "react";
2528
+ import { createRoot } from "react-dom/client";
2529
+ import { jsx as jsx3 } from "react/jsx-runtime";
2530
+ function mountDevInspector(config) {
2531
+ const resolved = mergeDevInspectorConfig(config);
2532
+ const existingRoot = document.getElementById(resolved.rootId);
2533
+ if (existingRoot) return existingRoot;
2534
+ const devRoot = document.createElement("div");
2535
+ devRoot.id = resolved.rootId;
2536
+ document.body.appendChild(devRoot);
2537
+ createRoot(devRoot).render(
2538
+ /* @__PURE__ */ jsx3(StrictMode, { children: /* @__PURE__ */ jsx3(DevInspectorProvider, { config: resolved, children: /* @__PURE__ */ jsx3(DevInspector, {}) }) })
2539
+ );
2540
+ return devRoot;
2541
+ }
2542
+ export {
2543
+ DevInspector,
2544
+ DevInspectorProvider,
2545
+ InspectorPanel,
2546
+ defaultDevInspectorConfig,
2547
+ mergeDevInspectorConfig,
2548
+ mountDevInspector,
2549
+ useDevInspectorConfig
2550
+ };
2551
+ //# sourceMappingURL=index.js.map