@lijinmei-810/dev-inspector 0.2.0 → 0.2.1

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 CHANGED
@@ -1,10 +1,44 @@
1
1
  // src/DevInspector.tsx
2
2
  import { useState, useEffect, useRef, useCallback } from "react";
3
- import { ArrowDown, ArrowLeft, ArrowUp, ChevronDown, CircleHelp, Component as ComponentIcon, Library as LibraryIcon, Minus, Plus, RotateCcw, Trash2, X } from "lucide-react";
3
+ import { ArrowDown, ArrowLeft, ArrowUp, Check, ChevronDown, CircleHelp, Component as ComponentIcon, Library as LibraryIcon, Minus, Plus, RotateCcw, Trash2, Upload, WandSparkles, X } from "lucide-react";
4
4
 
5
5
  // src/DevInspectorProvider.tsx
6
6
  import { createContext, useContext } from "react";
7
7
 
8
+ // src/tokens/typography.json
9
+ var typography_default = {
10
+ title: {
11
+ fontSize: "15px",
12
+ fontWeight: "700",
13
+ lineHeight: "1.4",
14
+ color: "#111827"
15
+ },
16
+ sectionLabel: {
17
+ fontSize: "12px",
18
+ fontWeight: "700",
19
+ lineHeight: "1.4",
20
+ color: "#374151"
21
+ },
22
+ body: {
23
+ fontSize: "12px",
24
+ fontWeight: "400",
25
+ lineHeight: "1.5",
26
+ color: "#6b7280"
27
+ },
28
+ meta: {
29
+ fontSize: "11px",
30
+ fontWeight: "600",
31
+ lineHeight: "1.35",
32
+ color: "#94a3b8"
33
+ },
34
+ tinyBadge: {
35
+ fontSize: "10px",
36
+ fontWeight: "700",
37
+ lineHeight: "1.2",
38
+ color: "#64748b"
39
+ }
40
+ };
41
+
8
42
  // src/config.ts
9
43
  var FALLBACK_TYPOGRAPHY_STYLES = [
10
44
  {
@@ -104,6 +138,7 @@ var FALLBACK_SHADOW_TOKENS = [
104
138
  var FALLBACK_TOKENS = {
105
139
  colorPalette: [],
106
140
  tokenLabels: {},
141
+ inspectorTypography: typography_default,
107
142
  radiusPresets: [
108
143
  { label: "\u65E0", sub: "", value: "0px", token: "" },
109
144
  { label: "S", sub: "4px", value: "4px", token: "" },
@@ -274,12 +309,22 @@ function handleArrowKeyStep(event, onStep, onEnter) {
274
309
  }
275
310
  if (event.key === "Enter") onEnter?.();
276
311
  }
312
+ function renderSubmitLabel(submitMsg) {
313
+ if (submitMsg === "\u5DF2\u590D\u5236") {
314
+ return /* @__PURE__ */ jsxs("span", { className: "di-btn-save-content", children: [
315
+ /* @__PURE__ */ jsx2(Check, { size: 14, strokeWidth: 2.4, "aria-hidden": "true" }),
316
+ /* @__PURE__ */ jsx2("span", { children: "\u5DF2\u590D\u5236" })
317
+ ] });
318
+ }
319
+ return submitMsg || "\u53D1\u9001\u7ED9AI";
320
+ }
277
321
  var COLOR_PROPS = [
278
322
  { label: "\u80CC\u666F\u8272", prop: "background-color" }
279
323
  ];
280
324
  function getColorLabel(val, colorPalette) {
325
+ const normalizedVal = resolveColorValue(val);
281
326
  for (const g of colorPalette) {
282
- const found = g.colors.find((c) => c.val === val);
327
+ const found = g.colors.find((c) => resolveColorValue(c.val) === normalizedVal);
283
328
  if (found) return `${g.group}\xB7${found.label}`;
284
329
  }
285
330
  return null;
@@ -489,10 +534,26 @@ function isTextEntryTarget(target) {
489
534
  function normalizeColor(val) {
490
535
  const raw = val.trim();
491
536
  if (raw === "transparent" || /^rgba\(\s*0,\s*0,\s*0,\s*0\s*\)$/i.test(raw)) return "transparent";
492
- const m = val.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
537
+ const m = raw.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)$/);
493
538
  if (m) return "#" + [m[1], m[2], m[3]].map((n) => parseInt(n).toString(16).padStart(2, "0")).join("");
539
+ if (/^#[0-9a-f]{3,8}$/i.test(raw)) return raw.toLowerCase();
494
540
  return raw;
495
541
  }
542
+ function resolveColorValue(val) {
543
+ const normalized = normalizeColor(val || "");
544
+ if (!normalized || normalized === "transparent") return normalized || "transparent";
545
+ if (/^#[0-9a-f]{6}$/i.test(normalized)) return normalized;
546
+ if (typeof document === "undefined" || !document.body) return normalized;
547
+ const probe = document.createElement("div");
548
+ probe.style.color = normalized;
549
+ probe.style.position = "fixed";
550
+ probe.style.opacity = "0";
551
+ probe.style.pointerEvents = "none";
552
+ document.body.appendChild(probe);
553
+ const computed = normalizeColor(getComputedStyle(probe).color.trim());
554
+ probe.remove();
555
+ return computed || normalized;
556
+ }
496
557
  function isTransparentColor(val) {
497
558
  return normalizeColor(val || "transparent") === "transparent";
498
559
  }
@@ -635,7 +696,7 @@ function getComponentClasses(el) {
635
696
  if (primitiveClasses.length) return primitiveClasses;
636
697
  const classes = getClasses(el);
637
698
  const cardBaseClass = classes.find(
638
- (className) => /(^|[-_])card([-_]|$)/i.test(className) && !className.includes("--")
699
+ (className) => isCardRootClass(className) && !className.includes("--")
639
700
  );
640
701
  if (cardBaseClass) return [cardBaseClass];
641
702
  const badgeBaseClass = classes.find(
@@ -694,6 +755,7 @@ var TEXT_ONLY_TAGS = /* @__PURE__ */ new Set([
694
755
  var TEXT_SEMANTIC_CLASS_RE = /(^|[-_])(caption|copy|desc|description|eyebrow|heading|label|subtitle|text|title)([-_]|$)/i;
695
756
  var STRUCTURAL_CONTAINER_CLASS_RE = /(^|[-_])(area|block|card|container|content|group|grid|item|layout|list|panel|row|section|shell|stack|zone)([-_]|$)/i;
696
757
  var PAGE_SHELL_CLASS_RE = /(^|[-_])(app|page|root|screen|shell|workspace)([-_]|$)/i;
758
+ var CARD_ROOT_CLASS_NAMES = /* @__PURE__ */ new Set(["task-card"]);
697
759
  var TEXT_GROUP_EXCLUDED_SELECTOR = [
698
760
  "button",
699
761
  "input",
@@ -724,6 +786,12 @@ function hasTextSemanticClass(el) {
724
786
  (className) => !isStateClass(className) && !/^lucide(-|$)/.test(className) && TEXT_SEMANTIC_CLASS_RE.test(className)
725
787
  );
726
788
  }
789
+ function getRegisteredComponentType(el) {
790
+ return (el.getAttribute("data-component") || "").trim();
791
+ }
792
+ function isCardRootClass(className) {
793
+ return CARD_ROOT_CLASS_NAMES.has(className) || /(^|[-_])card($|--)/i.test(className);
794
+ }
727
795
  function hasStructuralContainerClass(el) {
728
796
  return getClasses(el).some(
729
797
  (className) => !isStateClass(className) && STRUCTURAL_CONTAINER_CLASS_RE.test(className)
@@ -859,6 +927,17 @@ function getInspectorComponentMeta(el) {
859
927
  const tag = el.tagName.toLowerCase();
860
928
  const classes = getClasses(el);
861
929
  const componentClasses = getComponentClasses(el);
930
+ const registeredComponentType = getRegisteredComponentType(el);
931
+ if (registeredComponentType) {
932
+ const type = registeredComponentType;
933
+ return {
934
+ name: componentClasses[0] ?? type,
935
+ type,
936
+ layer: "Component",
937
+ variant: inferVariantFromClasses(classes),
938
+ state: inferStateFromElement(el, classes)
939
+ };
940
+ }
862
941
  if (tag === "svg" && classes.includes("lucide")) {
863
942
  const iconClass = classes.find((className) => /^lucide-/.test(className));
864
943
  return {
@@ -879,9 +958,9 @@ function getInspectorComponentMeta(el) {
879
958
  state: inferStateFromElement(el, classes)
880
959
  };
881
960
  }
882
- if (classIncludes(classes, /(^|[-_])card([-_]|$)/i)) {
961
+ if (classes.some(isCardRootClass)) {
883
962
  return {
884
- name: componentClasses.find((className) => /card/i.test(className)) ?? componentClasses[0] ?? "card",
963
+ name: componentClasses.find((className) => isCardRootClass(className)) ?? componentClasses[0] ?? "card",
885
964
  type: "Card",
886
965
  layer: "Component",
887
966
  variant: inferCardVariant(classes),
@@ -1509,6 +1588,14 @@ function canPersistSelector(el, scope) {
1509
1588
  function isInsidePanel(el) {
1510
1589
  return el.closest(".di-panel") !== null || el.closest(".di-trigger") !== null || el.closest(".di-label") !== null;
1511
1590
  }
1591
+ function resolveSelectionTarget(raw) {
1592
+ let cursor = raw;
1593
+ while (cursor && cursor !== document.body && cursor !== document.documentElement) {
1594
+ if (getInspectorComponentMeta(cursor)) return cursor;
1595
+ cursor = cursor.parentElement;
1596
+ }
1597
+ return raw;
1598
+ }
1512
1599
  function scanTokenMap() {
1513
1600
  const map = {};
1514
1601
  try {
@@ -1539,6 +1626,12 @@ function getComputedColor(el, prop) {
1539
1626
  function getComputedRadius(el) {
1540
1627
  return getComputedStyle(el).getPropertyValue("border-radius").trim();
1541
1628
  }
1629
+ function getComputedPadding(el) {
1630
+ const cs = getComputedStyle(el);
1631
+ const t = cs.paddingTop, r = cs.paddingRight, b = cs.paddingBottom, l = cs.paddingLeft;
1632
+ if (t === r && r === b && b === l) return t;
1633
+ return `${t} ${r} ${b} ${l}`;
1634
+ }
1542
1635
  function parseTranslate(val) {
1543
1636
  const raw = (val || "none").trim();
1544
1637
  if (!raw || raw === "none") return { x: 0, y: 0 };
@@ -1982,6 +2075,93 @@ function getTextContent(el) {
1982
2075
  const text = Array.from(el.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent ?? "").join("").trim();
1983
2076
  return text.length > 0 ? el.textContent?.trim() ?? null : null;
1984
2077
  }
2078
+ function MatchBadge({
2079
+ label,
2080
+ tooltip,
2081
+ tone
2082
+ }) {
2083
+ return /* @__PURE__ */ jsx2(
2084
+ "span",
2085
+ {
2086
+ className: `di-match-badge di-match-badge--${tone}`,
2087
+ title: tooltip,
2088
+ "data-tooltip": tooltip,
2089
+ tabIndex: 0,
2090
+ children: label
2091
+ }
2092
+ );
2093
+ }
2094
+ function getComputedMargin(el) {
2095
+ const cs = getComputedStyle(el);
2096
+ const t = cs.marginTop, r = cs.marginRight, b = cs.marginBottom, l = cs.marginLeft;
2097
+ if (t === r && r === b && b === l) return t;
2098
+ return `${t} ${r} ${b} ${l}`;
2099
+ }
2100
+ function formatReadableColor(value) {
2101
+ if (!value || isTransparentColor(value)) return "\u900F\u660E";
2102
+ return formatColorDisplay(resolveColorValue(value));
2103
+ }
2104
+ function getComponentElementStyleSummary(el) {
2105
+ const cs = getComputedStyle(el);
2106
+ const parts = [];
2107
+ const text = getTextContent(el);
2108
+ const tag = el.tagName.toLowerCase();
2109
+ const borderWidth = getNumericCssValue(cs.borderTopWidth);
2110
+ const radius = cs.borderRadius.trim();
2111
+ const bg = cs.backgroundColor.trim();
2112
+ const color = cs.color.trim();
2113
+ if (text || /^(p|span|a|button|label|h1|h2|h3|h4|h5|h6|li|strong|em)$/i.test(tag)) {
2114
+ parts.push(`\u5B57 ${formatLengthControlValue(cs.fontSize)}`);
2115
+ parts.push(`\u91CD ${cs.fontWeight}`);
2116
+ parts.push(`\u8272 ${formatReadableColor(color)}`);
2117
+ }
2118
+ if (!isTransparentColor(bg)) {
2119
+ parts.push(`\u80CC\u666F ${formatReadableColor(bg)}`);
2120
+ }
2121
+ if (borderWidth > 0 && !isTransparentColor(cs.borderTopColor)) {
2122
+ parts.push(`\u63CF\u8FB9 ${formatLengthControlValue(cs.borderTopWidth)} ${formatReadableColor(cs.borderTopColor)}`);
2123
+ }
2124
+ if (!isZeroLengthValue(radius)) {
2125
+ parts.push(`\u5706\u89D2 ${formatLengthControlValue(radius)}`);
2126
+ }
2127
+ return parts.join(" / ") || "\u65E0\u660E\u663E\u6837\u5F0F";
2128
+ }
2129
+ function getComponentElementSpacingSummary(el) {
2130
+ const cs = getComputedStyle(el);
2131
+ const parts = [];
2132
+ const paddingSummary = getSpaceSummary("padding", getComputedPadding(el));
2133
+ const marginSummary = getSpaceSummary("margin", getComputedMargin(el));
2134
+ const gapSummary = canControlElementGap(el) ? getSpaceSummary("gap", cs.gap.trim()) : "";
2135
+ if (paddingSummary) parts.push(`\u5185\u8FB9\u8DDD ${paddingSummary}`);
2136
+ if (marginSummary) parts.push(`\u5916\u8FB9\u8DDD ${marginSummary}`);
2137
+ if (gapSummary) parts.push(`\u5143\u7D20\u95F4\u8DDD ${gapSummary.replace(/^gap\s*/i, "")}`);
2138
+ return parts.join(" / ") || "\u65E0\u989D\u5916\u95F4\u8DDD";
2139
+ }
2140
+ function getComponentElementStyleStatus(el, tokenMap, colorPalette, tokenLabels, typographyTokens) {
2141
+ const cs = getComputedStyle(el);
2142
+ const colorInfo = getDisplayLabel(normalizeColor(cs.color), tokenMap, colorPalette, tokenLabels);
2143
+ const backgroundInfo = getDisplayLabel(normalizeColor(cs.backgroundColor), tokenMap, colorPalette, tokenLabels);
2144
+ const borderInfo = getDisplayLabel(normalizeColor(cs.borderTopColor), tokenMap, colorPalette, tokenLabels);
2145
+ const typographyMatched = Boolean(getTypographyToken(cs.fontSize.trim(), cs.fontWeight.trim(), normalizeColor(cs.color), typographyTokens));
2146
+ if (typographyMatched) return "token";
2147
+ if (!colorInfo.isHardcoded || !backgroundInfo.isHardcoded || !borderInfo.isHardcoded) return "token";
2148
+ return "raw";
2149
+ }
2150
+ function getComponentElementSpacingStatus(el, spaceSteps) {
2151
+ const cs = getComputedStyle(el);
2152
+ const paddingValue = getComputedPadding(el);
2153
+ const marginValue = getComputedMargin(el);
2154
+ const gapValue = cs.gap.trim();
2155
+ const paddingSummary = getSpaceSummary("padding", paddingValue);
2156
+ const marginSummary = getSpaceSummary("margin", marginValue);
2157
+ const gapSummary = canControlElementGap(el) ? getSpaceSummary("gap", gapValue) : "";
2158
+ const hasSpacing = Boolean(paddingSummary || marginSummary || gapSummary);
2159
+ if (!hasSpacing) return null;
2160
+ const paddingMatched = !paddingSummary || Boolean(getSpaceStepMatch("padding", paddingValue, spaceSteps));
2161
+ const marginMatched = !marginSummary || Boolean(getSpaceStepMatch("margin", marginValue, spaceSteps));
2162
+ const gapMatched = !gapSummary || Boolean(getSpaceStepMatch("gap", gapValue, spaceSteps));
2163
+ return paddingMatched && marginMatched && gapMatched ? "token" : "raw";
2164
+ }
1985
2165
  var TOKEN_TYPES = [
1986
2166
  { key: "bg", label: "\u80CC\u666F\u8272", code: "bg" },
1987
2167
  { key: "text", label: "\u6587\u5B57\u8272", code: "text" },
@@ -2005,9 +2185,15 @@ var TOKEN_STATES = [
2005
2185
  { key: "warning", label: "\u8B66\u544A\u6001", code: "warning" }
2006
2186
  ];
2007
2187
  function ColorDropdown({ value, onChange, onClose, onAddToken, pos, colorPalette }) {
2008
- const initHex = value.startsWith("#") ? value : "#6b7280";
2188
+ const resolvedValue = resolveColorValue(value);
2189
+ const initHex = /^#[0-9a-f]{6}$/i.test(resolvedValue) ? resolvedValue : "#6b7280";
2190
+ const initAlpha = (() => {
2191
+ const rgbaMatch = value.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)$/i);
2192
+ if (!rgbaMatch) return 100;
2193
+ return Math.max(0, Math.min(100, Math.round(parseFloat(rgbaMatch[4]) * 100)));
2194
+ })();
2009
2195
  const [hexInput, setHexInput] = useState(initHex);
2010
- const [alpha, setAlpha] = useState(100);
2196
+ const [alpha, setAlpha] = useState(initAlpha);
2011
2197
  const customColorInputRef = useRef(null);
2012
2198
  function buildColor(hex, a) {
2013
2199
  const m = hex.match(/^#([0-9a-f]{6})$/i);
@@ -2277,10 +2463,11 @@ function AddTokenModal({ value, cssProp, elementClasses, onClose, onConfirm, col
2277
2463
  }
2278
2464
  function getDisplayLabel(val, tokenMap, colorPalette, tokenLabels) {
2279
2465
  if (isTransparentColor(val)) return { label: "\u65E0", sub: "", isHardcoded: true };
2280
- const displayVal = formatColorDisplay(val);
2281
- const paletteLabel = getColorLabel(val, colorPalette);
2466
+ const resolvedVal = resolveColorValue(val);
2467
+ const displayVal = formatColorDisplay(resolvedVal);
2468
+ const paletteLabel = getColorLabel(resolvedVal, colorPalette);
2282
2469
  if (paletteLabel) return { label: paletteLabel, sub: displayVal, isHardcoded: false };
2283
- const token = tokenMap[val];
2470
+ const token = tokenMap[val] ?? tokenMap[resolvedVal];
2284
2471
  if (token) return { label: tokenLabels[token] ?? token.replace("--", ""), sub: displayVal, isHardcoded: false };
2285
2472
  return { label: displayVal, sub: "", isHardcoded: true };
2286
2473
  }
@@ -2563,6 +2750,7 @@ function InspectorPanel({
2563
2750
  const {
2564
2751
  colorPalette,
2565
2752
  tokenLabels,
2753
+ inspectorTypography,
2566
2754
  radiusPresets,
2567
2755
  spaceSteps,
2568
2756
  borderWidthSteps,
@@ -2577,6 +2765,28 @@ function InspectorPanel({
2577
2765
  { cssVar: "", value: "none", label: "\u65E0", usage: "\u4E0D\u4F7F\u7528\u9634\u5F71" },
2578
2766
  ...shadowTokens
2579
2767
  ];
2768
+ const inspectorTypographyVars = {
2769
+ "--di-text-title-size": inspectorTypography.title.fontSize,
2770
+ "--di-text-title-weight": inspectorTypography.title.fontWeight,
2771
+ "--di-text-title-line-height": inspectorTypography.title.lineHeight,
2772
+ "--di-text-title-color": inspectorTypography.title.color,
2773
+ "--di-text-label-size": inspectorTypography.sectionLabel.fontSize,
2774
+ "--di-text-label-weight": inspectorTypography.sectionLabel.fontWeight,
2775
+ "--di-text-label-line-height": inspectorTypography.sectionLabel.lineHeight,
2776
+ "--di-text-label-color": inspectorTypography.sectionLabel.color,
2777
+ "--di-text-body-size": inspectorTypography.body.fontSize,
2778
+ "--di-text-body-weight": inspectorTypography.body.fontWeight,
2779
+ "--di-text-body-line-height": inspectorTypography.body.lineHeight,
2780
+ "--di-text-body-color": inspectorTypography.body.color,
2781
+ "--di-text-meta-size": inspectorTypography.meta.fontSize,
2782
+ "--di-text-meta-weight": inspectorTypography.meta.fontWeight,
2783
+ "--di-text-meta-line-height": inspectorTypography.meta.lineHeight,
2784
+ "--di-text-meta-color": inspectorTypography.meta.color,
2785
+ "--di-text-tiny-badge-size": inspectorTypography.tinyBadge.fontSize,
2786
+ "--di-text-tiny-badge-weight": inspectorTypography.tinyBadge.fontWeight,
2787
+ "--di-text-tiny-badge-line-height": inspectorTypography.tinyBadge.lineHeight,
2788
+ "--di-text-tiny-badge-color": inspectorTypography.tinyBadge.color
2789
+ };
2580
2790
  const [isEditing, setIsEditing] = useState(false);
2581
2791
  const panelElRef = useRef(null);
2582
2792
  const [selected, setSelected] = useState(targetEl);
@@ -2676,6 +2886,9 @@ function InspectorPanel({
2676
2886
  const [cardVariantVal, setCardVariantVal] = useState("default");
2677
2887
  const [cardVariantClassVal, setCardVariantClassVal] = useState("");
2678
2888
  const [pendingCardVariant, setPendingCardVariant] = useState("");
2889
+ const [componentInfoOpen, setComponentInfoOpen] = useState(false);
2890
+ const [annotationOpen, setAnnotationOpen] = useState(false);
2891
+ const [annotationText, setAnnotationText] = useState("");
2679
2892
  const [componentMakerSourceMode, setComponentMakerSourceMode] = useState("new");
2680
2893
  const [componentMakerAction, setComponentMakerAction] = useState("create-current");
2681
2894
  const [componentSpecDraft, setComponentSpecDraft] = useState(EMPTY_COMPONENT_SPEC_DRAFT);
@@ -2710,6 +2923,8 @@ function InspectorPanel({
2710
2923
  const [selectedLibraryComponentVariantId, setSelectedLibraryComponentVariantId] = useState("");
2711
2924
  const [libraryTokenForm, setLibraryTokenForm] = useState(null);
2712
2925
  const [libraryComponentForm, setLibraryComponentForm] = useState(null);
2926
+ const [libraryComponentImportOpen, setLibraryComponentImportOpen] = useState(false);
2927
+ const [libraryComponentImportMsg, setLibraryComponentImportMsg] = useState("");
2713
2928
  const [libraryCustomTokens, setLibraryCustomTokens] = useState([]);
2714
2929
  const [libraryTokenOverrides, setLibraryTokenOverrides] = useState({});
2715
2930
  const [libraryDeletedTokenIds, setLibraryDeletedTokenIds] = useState({});
@@ -2726,6 +2941,9 @@ function InspectorPanel({
2726
2941
  useEffect(() => {
2727
2942
  localDraftsRef.current = localDrafts;
2728
2943
  }, [localDrafts]);
2944
+ useEffect(() => {
2945
+ setComponentInfoOpen(false);
2946
+ }, [selected]);
2729
2947
  const formatStyleIntentSummary = useCallback((r) => ({
2730
2948
  pendingCount: r.pendingCount ?? 0,
2731
2949
  latestPending: r.latestPending ? {
@@ -2774,6 +2992,50 @@ function InspectorPanel({
2774
2992
  return next;
2775
2993
  });
2776
2994
  }
2995
+ function getAnnotationTargetInfo(el) {
2996
+ const componentMeta2 = getInspectorComponentMeta(el);
2997
+ const selector = getSelectorForScope(el, "current");
2998
+ return {
2999
+ key: `current:${selector}`,
3000
+ selector,
3001
+ targetLabel: getTargetDisplayLabel(el, componentMeta2),
3002
+ scopeLabel: "\u5F53\u524D\u5143\u7D20"
3003
+ };
3004
+ }
3005
+ function getAnnotationChange(changes = []) {
3006
+ return changes.find((change) => change.prop === "annotation:intent");
3007
+ }
3008
+ function updateAnnotationDraft(nextText) {
3009
+ const el = selectedRef.current;
3010
+ if (!el) return;
3011
+ const text = nextText.trim();
3012
+ const info = getAnnotationTargetInfo(el);
3013
+ setLocalDrafts((prev) => {
3014
+ const existing = prev[info.key];
3015
+ const remainingChanges = existing?.changes.filter((change2) => change2.prop !== "annotation:intent") ?? [];
3016
+ if (!text) {
3017
+ if (!existing) return prev;
3018
+ const next = { ...prev };
3019
+ if (remainingChanges.length === 0) {
3020
+ delete next[info.key];
3021
+ } else {
3022
+ next[info.key] = { ...existing, changes: remainingChanges, updatedAt: Date.now() };
3023
+ }
3024
+ return next;
3025
+ }
3026
+ const change = {
3027
+ prop: "annotation:intent",
3028
+ from: "AI\u8BC6\u522B",
3029
+ val: text
3030
+ };
3031
+ const nextEntry = {
3032
+ ...info,
3033
+ changes: [...remainingChanges, change],
3034
+ updatedAt: Date.now()
3035
+ };
3036
+ return { ...prev, [info.key]: nextEntry };
3037
+ });
3038
+ }
2777
3039
  const selectEl = useCallback((el) => {
2778
3040
  selectedRef.current = el;
2779
3041
  const rect = el.getBoundingClientRect();
@@ -2782,6 +3044,8 @@ function InspectorPanel({
2782
3044
  const elCs = getComputedStyle(el);
2783
3045
  const selectedComponentMeta = getInspectorComponentMeta(el);
2784
3046
  const selectedCapability = getComponentCapability(selectedComponentMeta);
3047
+ const annotationDraft = localDraftsRef.current[getAnnotationTargetInfo(el).key];
3048
+ setAnnotationText(getAnnotationChange(annotationDraft?.changes)?.val ?? "");
2785
3049
  const isButtonComponent = selectedCapability?.type === "Button";
2786
3050
  const isIconComponent = selectedCapability?.type === "Icon";
2787
3051
  const isBadgeComponent = selectedCapability?.type === "Badge";
@@ -3244,6 +3508,15 @@ function InspectorPanel({
3244
3508
  setPendingBadgeStatus(status);
3245
3509
  setBadgeStatusOnTargets(targets, status);
3246
3510
  }
3511
+ function updateBadgeStatusOnElement(element, status) {
3512
+ const currentStatus = inferBadgeStatus(getClasses(element));
3513
+ const currentClass = getBadgeStatusClass(element);
3514
+ if (status === currentStatus) {
3515
+ setBadgeStatusOnTargets([element], currentStatus, currentClass);
3516
+ return;
3517
+ }
3518
+ setBadgeStatusOnTargets([element], status);
3519
+ }
3247
3520
  function updateCardVariant(variant) {
3248
3521
  const targets = getComponentTargets();
3249
3522
  if (variant === cardVariantVal) {
@@ -4097,6 +4370,7 @@ function InspectorPanel({
4097
4370
  }
4098
4371
  function copyPluginPrompt(prompt, successMsg) {
4099
4372
  if (!prompt) return;
4373
+ debugAiPrompt(prompt, "plugin");
4100
4374
  copyTextToClipboard(prompt).then((copied) => {
4101
4375
  setPluginMsg(copied ? successMsg : "\u590D\u5236\u5931\u8D25");
4102
4376
  setTimeout(() => setPluginMsg(""), copied ? 2200 : 1800);
@@ -4434,6 +4708,7 @@ function InspectorPanel({
4434
4708
  return `${item.label} / ${item.type} / ${item.categoryLabel} / ${item.summary || "\u672A\u767B\u8BB0\u80FD\u529B"} / ${item.selector || "\u672A\u767B\u8BB0 selector"} / ${item.status}`;
4435
4709
  }
4436
4710
  function openLibraryComponentCreate() {
4711
+ setLibraryComponentImportOpen(false);
4437
4712
  setLibraryComponentForm({
4438
4713
  mode: "create",
4439
4714
  category: libraryComponentCategory === "all" ? "custom" : libraryComponentCategory,
@@ -4445,6 +4720,7 @@ function InspectorPanel({
4445
4720
  });
4446
4721
  }
4447
4722
  function openLibraryComponentEdit(item) {
4723
+ setLibraryComponentImportOpen(false);
4448
4724
  setSelectedLibraryComponentId(item.id);
4449
4725
  setLibraryComponentForm({
4450
4726
  mode: "update",
@@ -4457,6 +4733,103 @@ function InspectorPanel({
4457
4733
  status: item.status.includes("\u8349\u7A3F") ? item.status : "\u7F16\u8F91\u8349\u7A3F"
4458
4734
  });
4459
4735
  }
4736
+ function normalizeComponentImportDraft(raw) {
4737
+ const type = String(raw.type || "").trim() || "Custom";
4738
+ const category = raw.category || getLibraryComponentCategory(type);
4739
+ const variantText = Array.isArray(raw.variants) && raw.variants.length ? `\u53D8\u4F53\uFF1A${raw.variants.join(" / ")}` : "";
4740
+ const sizeText = Array.isArray(raw.sizes) && raw.sizes.length ? `\u5C3A\u5BF8\uFF1A${raw.sizes.join(" / ")}` : "";
4741
+ const tokenText = Array.isArray(raw.tokenRefs) && raw.tokenRefs.length ? `Token\uFF1A${raw.tokenRefs.join(" / ")}` : "";
4742
+ return {
4743
+ mode: "create",
4744
+ category,
4745
+ type,
4746
+ label: String(raw.label || LIBRARY_COMPONENT_LABELS[type] || type).trim(),
4747
+ summary: String(raw.summary || [variantText, sizeText, tokenText].filter(Boolean).join("\uFF1B") || "\u5BFC\u5165\u7EC4\u4EF6\u8349\u7A3F").trim(),
4748
+ selector: String(raw.selector || `.${type.toLowerCase()}`).trim(),
4749
+ status: String(raw.status || "\u5BFC\u5165\u8349\u7A3F").trim()
4750
+ };
4751
+ }
4752
+ function submitImportedLibraryComponents(drafts) {
4753
+ const normalized = drafts.map(normalizeComponentImportDraft);
4754
+ const nextItems = normalized.map((form, index) => ({
4755
+ id: `draft-component-import:${Date.now()}:${index}:${form.type}`,
4756
+ category: form.category,
4757
+ categoryLabel: getLibraryComponentCategoryLabel(form.category),
4758
+ type: form.type,
4759
+ label: form.label,
4760
+ summary: form.summary,
4761
+ selector: form.selector,
4762
+ status: form.status,
4763
+ source: "draft"
4764
+ }));
4765
+ if (!nextItems.length) {
4766
+ setLibraryComponentImportMsg("\u6CA1\u6709\u8BC6\u522B\u5230\u7EC4\u4EF6");
4767
+ setTimeout(() => setLibraryComponentImportMsg(""), 1800);
4768
+ return;
4769
+ }
4770
+ setLibraryCustomComponents((prev) => [...nextItems, ...prev]);
4771
+ setSelectedLibraryComponentId(nextItems[0].id);
4772
+ nextItems.forEach((item) => {
4773
+ recordLibraryCrudChange({
4774
+ resource: "component",
4775
+ action: "create",
4776
+ name: item.type,
4777
+ from: "\u672A\u767B\u8BB0",
4778
+ val: formatLibraryComponentSummary(item)
4779
+ });
4780
+ });
4781
+ setLibraryComponentImportMsg(`\u5DF2\u5BFC\u5165 ${nextItems.length} \u4E2A\u7EC4\u4EF6\u8349\u7A3F`);
4782
+ setTimeout(() => setLibraryComponentImportMsg(""), 2200);
4783
+ }
4784
+ async function handleComponentImportFile(file) {
4785
+ if (!file) return;
4786
+ try {
4787
+ const content = await file.text();
4788
+ const parsed = JSON.parse(content);
4789
+ const drafts = Array.isArray(parsed) ? parsed : parsed.components ?? [];
4790
+ submitImportedLibraryComponents(drafts);
4791
+ } catch {
4792
+ setLibraryComponentImportMsg("JSON \u89E3\u6790\u5931\u8D25");
4793
+ setTimeout(() => setLibraryComponentImportMsg(""), 1800);
4794
+ }
4795
+ }
4796
+ function copyComponentLibraryImportPrompt() {
4797
+ const prompt = [
4798
+ "\u8BF7\u4E3A\u5F53\u524D\u9879\u76EE\u751F\u6210 DevInspector \u7EC4\u4EF6\u5E93\u63A5\u5165\u8349\u7A3F\u3002",
4799
+ "",
4800
+ "\u76EE\u6807\uFF1A",
4801
+ "1. \u626B\u63CF\u73B0\u6709\u7EC4\u4EF6\u6E90\u7801\uFF0C\u8BC6\u522B\u7EC4\u4EF6 type\u3001\u4E2D\u6587 label\u3001selector\u3001\u53D8\u4F53\u3001\u5C3A\u5BF8\u548C tokenRefs\u3002",
4802
+ "2. \u4F18\u5148\u4F7F\u7528 data-component / registry / \u7A33\u5B9A class\uFF1B\u4E0D\u786E\u5B9A\u9879\u6807\u4E3A\u201C\u5F85\u786E\u8BA4\u201D\u3002",
4803
+ "3. \u8F93\u51FA componentPreviews registry \u6216\u4E0B\u9762\u8FD9\u79CD JSON \u8349\u7A3F\u3002",
4804
+ "",
4805
+ "JSON \u683C\u5F0F\uFF1A",
4806
+ "{",
4807
+ ' "components": [',
4808
+ " {",
4809
+ ' "type": "Button",',
4810
+ ' "label": "\u6309\u94AE",',
4811
+ ' "category": "action",',
4812
+ ' "summary": "\u4E3B\u6309\u94AE\u3001\u6B21\u6309\u94AE\u3001\u5F31\u6309\u94AE",',
4813
+ ' "selector": ".button, [data-component=\\"Button\\"]",',
4814
+ ' "variants": ["primary", "secondary", "ghost"],',
4815
+ ' "sizes": ["s", "m", "l"],',
4816
+ ' "tokenRefs": ["--color-brand-primary"],',
4817
+ ' "status": "\u5F85\u786E\u8BA4"',
4818
+ " }",
4819
+ " ]",
4820
+ "}",
4821
+ "",
4822
+ "\u7EA6\u675F\uFF1A\u8BF7\u4FEE\u6539\u6E90\u7801\u5C42\u63A5\u5165\uFF0C\u4E0D\u8981\u76F4\u63A5\u6539 dist \u4EA7\u7269\u3002"
4823
+ ].join("\n");
4824
+ debugAiPrompt(prompt, "plugin");
4825
+ copyTextToClipboard(prompt).then((copied) => {
4826
+ setLibraryComponentImportMsg(copied ? "\u63A5\u5165\u63D0\u793A\u5DF2\u590D\u5236" : "\u590D\u5236\u5931\u8D25");
4827
+ setTimeout(() => setLibraryComponentImportMsg(""), copied ? 2200 : 1800);
4828
+ }).catch(() => {
4829
+ setLibraryComponentImportMsg("\u590D\u5236\u5931\u8D25");
4830
+ setTimeout(() => setLibraryComponentImportMsg(""), 1800);
4831
+ });
4832
+ }
4460
4833
  function submitLibraryComponentForm() {
4461
4834
  if (!libraryComponentForm) return;
4462
4835
  const category = libraryComponentForm.category || getLibraryComponentCategory(libraryComponentForm.type);
@@ -4512,47 +4885,79 @@ function InspectorPanel({
4512
4885
  });
4513
4886
  }
4514
4887
  function buildAiTaskPrompt(params) {
4515
- const changes = params.changes.map((change, index) => `${index + 1}. ${getChangeLabel(change.prop)}\uFF1A${formatStoredChangeRecordValue(change.prop, change.from)} \u2192 ${formatStoredChangeRecordValue(change.prop, change.val)}`).join("\n");
4516
- const latestHint = params.entryId ? `\u4F18\u5148\u5904\u7406 id = ${params.entryId} \u8FD9\u6761\u8BB0\u5F55\u3002` : "\u8FD9\u6761\u4EFB\u52A1\u6765\u81EA DevInspector \u5F53\u524D\u9009\u4E2D\u5143\u7D20\u3002";
4888
+ const changes = formatPromptChanges(params.changes);
4517
4889
  return [
4518
- "DevInspector \u6837\u5F0F\u4EFB\u52A1",
4519
4890
  `\u9875\u9762\uFF1A${params.pageLabel}`,
4520
- `\u5143\u7D20\uFF1A${params.targetLabel}`,
4891
+ "\u5F85\u5904\u7406\u5BF9\u8C61\uFF1A1",
4892
+ `1. \u5143\u7D20\uFF1A${params.targetLabel}`,
4521
4893
  `\u8303\u56F4\uFF1A${params.scopeLabel}`,
4522
4894
  `\u9009\u62E9\u5668\uFF1A${params.selector}`,
4895
+ `\u7C7B\u578B\uFF1A${getPromptChangeTypes(params.changes).join(" / ") || "none"}`,
4523
4896
  "\u6539\u52A8\uFF1A",
4524
- changes || "\u65E0\u6837\u5F0F\u6539\u52A8",
4897
+ changes || "1. \u65E0\u6539\u52A8",
4525
4898
  ...params.note ? ["\u8865\u5145\uFF1A", params.note] : [],
4526
- "\u5B9A\u4F4D\u63D0\u793A\uFF1A",
4527
- latestHint,
4528
- "\u8BF7\u4F18\u5148\u5B9A\u4F4D\u8BE5\u9009\u62E9\u5668\u5BF9\u5E94\u7684\u7EC4\u4EF6\u6216\u6837\u5F0F\u6E90\u7801\uFF0C\u5224\u65AD\u662F\u5426\u5E94\u56FA\u5316\u4E3A\u6B63\u5F0F\u7EC4\u4EF6\u6837\u5F0F\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002"
4899
+ "\u7ED9AI\uFF1A\u6309\u9009\u62E9\u5668\u5148\u5B9A\u4F4D\u6E90\u7801\uFF0C\u518D\u5224\u65AD\u843D\u4E3A\u7EC4\u4EF6\u6837\u5F0F\u3001token\u3001\u5C40\u90E8\u8986\u76D6\u6216\u5B9E\u73B0\u7EA6\u675F\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002"
4529
4900
  ].join("\n");
4530
4901
  }
4531
4902
  function buildAiDraftBasketPrompt(drafts) {
4532
4903
  const hasComponentSpec = drafts.some((draft) => draft.changes.some((change) => change.prop === "component-spec" || change.prop.startsWith("component-new-variant:") || change.prop.startsWith("component-variant-group:")));
4533
4904
  const hasLibraryChange = drafts.some((draft) => draft.changes.some((change) => change.prop.startsWith("library-")));
4905
+ const mode = hasLibraryChange ? "library-crud" : hasComponentSpec ? "component-spec" : "style-update";
4534
4906
  const sections = drafts.sort((a, b) => a.updatedAt - b.updatedAt).map((draft, index) => {
4535
- const changes = draft.changes.map((change, changeIndex) => `${changeIndex + 1}. ${getChangeLabel(change.prop)}\uFF1A${formatStoredChangeRecordValue(change.prop, change.from)} \u2192 ${formatStoredChangeRecordValue(change.prop, change.val)}`).join("\n");
4907
+ const changes = formatPromptChanges(draft.changes);
4536
4908
  return [
4537
4909
  `${index + 1}. \u5143\u7D20\uFF1A${draft.targetLabel}`,
4538
4910
  `\u8303\u56F4\uFF1A${draft.scopeLabel}`,
4539
4911
  `\u9009\u62E9\u5668\uFF1A${draft.selector}`,
4912
+ `\u7C7B\u578B\uFF1A${getPromptChangeTypes(draft.changes).join(" / ") || "none"}`,
4540
4913
  "\u6539\u52A8\uFF1A",
4541
- changes || "\u65E0\u6837\u5F0F\u6539\u52A8"
4914
+ changes || "1. \u65E0\u6539\u52A8"
4542
4915
  ].join("\n");
4543
4916
  }).join("\n\n");
4544
- const title = hasLibraryChange ? "DevInspector \u8BBE\u8BA1\u5E93 / \u7EC4\u4EF6 / \u6837\u5F0F\u4EFB\u52A1" : hasComponentSpec ? "DevInspector \u7EC4\u4EF6 / \u6837\u5F0F\u4EFB\u52A1" : "DevInspector \u6837\u5F0F\u4EFB\u52A1";
4545
- const locationHint = hasLibraryChange ? "\u8FD9\u4E9B\u5185\u5BB9\u6765\u81EA DevInspector \u8BBE\u8BA1\u5E93\u5DE5\u4F5C\u53F0\uFF1BToken / Component \u7684\u589E\u5220\u6539\u90FD\u662F\u7528\u6237\u786E\u8BA4\u7684\u53D7\u63A7\u8349\u7A3F\uFF0C\u8BF7\u5148\u5B9A\u4F4D\u8BBE\u8BA1 token \u914D\u7F6E\u3001\u7EC4\u4EF6\u80FD\u529B\u8868\u6216\u6E90\u7801\u4E2D\u7684\u6B63\u5F0F\u5B9A\u4E49\uFF0C\u518D\u5224\u65AD\u5982\u4F55\u843D\u5730\uFF1B\u5220\u9664\u64CD\u4F5C\u9700\u5148\u5904\u7406\u4F7F\u7528\u5173\u7CFB\uFF0C\u4E0D\u8981\u624B\u6539 dist\u3002" : hasComponentSpec ? "\u8FD9\u4E9B\u5185\u5BB9\u6765\u81EA DevInspector \u672C\u6B21\u4FEE\u6539\u5185\u5BB9\uFF1B\u7EC4\u4EF6\u89C4\u683C\u548C\u65B0\u53D8\u4F53\u662F\u7528\u6237\u786E\u8BA4\u7684\u8349\u7A3F\uFF0C\u8BF7\u6309\u7EC4\u4EF6\u540D\u3001\u53EF\u7F16\u8F91\u5185\u5BB9\u3001\u53D8\u4F53\u7EF4\u5EA6\u3001\u72B6\u6001\u3001\u5C3A\u5BF8\u548C\u6837\u5F0F\u89C4\u5219\u5B9A\u4F4D\u6E90\u7801\u843D\u5730\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002" : "\u8FD9\u4E9B\u5185\u5BB9\u6765\u81EA DevInspector \u672C\u6B21\u4FEE\u6539\u5185\u5BB9\uFF0C\u8BF7\u6309\u5BF9\u8C61\u9010\u4E00\u5B9A\u4F4D\u6E90\u7801\uFF0C\u5224\u65AD\u662F\u5426\u5E94\u56FA\u5316\u4E3A\u7EC4\u4EF6\u6837\u5F0F\u3001token \u6216\u5C40\u90E8\u8986\u76D6\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002";
4917
+ const aiHint = hasLibraryChange ? "\u7ED9AI\uFF1A\u5148\u5B9A\u4F4D\u6B63\u5F0F token \u914D\u7F6E\u3001\u7EC4\u4EF6\u80FD\u529B\u8868\u6216\u6E90\u7801\u5B9A\u4E49\uFF0C\u518D\u5904\u7406\u589E\u5220\u6539\uFF1B\u5220\u9664\u524D\u5148\u68C0\u67E5\u4F7F\u7528\u5173\u7CFB\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002" : hasComponentSpec ? "\u7ED9AI\uFF1A\u7EC4\u4EF6\u89C4\u683C\u548C\u65B0\u53D8\u4F53\u6309\u5DF2\u786E\u8BA4\u8349\u7A3F\u5904\u7406\uFF1B\u5148\u5B9A\u4F4D\u7EC4\u4EF6\u5B9A\u4E49\u3001\u53EF\u7F16\u8F91\u5185\u5BB9\u3001\u53D8\u4F53\u7EF4\u5EA6\u548C token \u6620\u5C04\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002" : "\u7ED9AI\uFF1A\u6309\u5BF9\u8C61\u9010\u4E00\u5B9A\u4F4D\u6E90\u7801\uFF0C\u518D\u5224\u65AD\u843D\u4E3A\u7EC4\u4EF6\u6837\u5F0F\u3001token\u3001\u5C40\u90E8\u8986\u76D6\u6216\u5B9E\u73B0\u7EA6\u675F\uFF1B\u4E0D\u8981\u624B\u6539 dist\u3002";
4546
4918
  return [
4547
- title,
4548
4919
  `\u9875\u9762\uFF1A${document.title || "\u5F53\u524D\u9875\u9762"}\uFF08${window.location.href}\uFF09`,
4549
4920
  `\u5F85\u5904\u7406\u5BF9\u8C61\uFF1A${drafts.length}`,
4550
4921
  sections,
4551
4922
  ...note ? ["\u8865\u5145\uFF1A", note] : [],
4552
- "\u5B9A\u4F4D\u63D0\u793A\uFF1A",
4553
- locationHint
4923
+ aiHint
4554
4924
  ].join("\n");
4555
4925
  }
4926
+ function getPromptChangeType(prop) {
4927
+ if (prop.startsWith("annotation:")) return "annotation";
4928
+ if (prop === "component-spec") return "component-spec";
4929
+ if (prop.startsWith("component-new-variant:")) return "component-new-variant";
4930
+ if (prop.startsWith("component-variant-group:")) return "component-variant-group";
4931
+ if (prop.startsWith("library-token:create")) return "library-token-create";
4932
+ if (prop.startsWith("library-token:update")) return "library-token-update";
4933
+ if (prop.startsWith("library-token:delete")) return "library-token-delete";
4934
+ if (prop.startsWith("library-component:create")) return "library-component-create";
4935
+ if (prop.startsWith("library-component:update")) return "library-component-update";
4936
+ if (prop.startsWith("library-component:delete")) return "library-component-delete";
4937
+ return "style";
4938
+ }
4939
+ function getPromptChangeTypes(changes) {
4940
+ return Array.from(new Set(changes.map((change) => getPromptChangeType(change.prop))));
4941
+ }
4942
+ function formatPromptChanges(changes) {
4943
+ return changes.map((change, index) => {
4944
+ const type = getPromptChangeType(change.prop);
4945
+ if (type === "annotation") {
4946
+ return [
4947
+ `${index + 1}. \u6807\u6CE8\uFF1A${change.val || "\u7A7A"}`
4948
+ ].join("\n");
4949
+ }
4950
+ return [
4951
+ `${index + 1}. ${getChangeLabel(change.prop)}\uFF1A${formatStoredChangeRecordValue(change.prop, change.from) || "\u7A7A"} \u2192 ${formatStoredChangeRecordValue(change.prop, change.val) || "\u7A7A"}`
4952
+ ].join("\n");
4953
+ }).join("\n");
4954
+ }
4955
+ function debugAiPrompt(prompt, source) {
4956
+ window.__DEV_INSPECTOR_LAST_PROMPT__ = prompt;
4957
+ console.groupCollapsed(`[DevInspector] AI prompt (${source})`);
4958
+ console.log(prompt);
4959
+ console.groupEnd();
4960
+ }
4556
4961
  function handleSubmitToAi() {
4557
4962
  const draftEntries = Object.values(localDraftsRef.current);
4558
4963
  if (draftEntries.length > 0) {
@@ -4580,8 +4985,9 @@ function InspectorPanel({
4580
4985
  changes,
4581
4986
  note
4582
4987
  });
4988
+ debugAiPrompt(prompt, "single");
4583
4989
  copyTextToClipboard(prompt).then((copied) => {
4584
- setSubmitMsg(copied ? "\u4EFB\u52A1\u6587\u672C\u5DF2\u590D\u5236 \u2713" : "\u590D\u5236\u5931\u8D25");
4990
+ setSubmitMsg(copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5931\u8D25");
4585
4991
  setTimeout(() => setSubmitMsg(""), copied ? 2200 : 1800);
4586
4992
  }).catch(() => {
4587
4993
  setSubmitMsg("\u590D\u5236\u5931\u8D25");
@@ -4590,8 +4996,9 @@ function InspectorPanel({
4590
4996
  }
4591
4997
  function copyDraftEntriesToAi(draftEntries) {
4592
4998
  const prompt = buildAiDraftBasketPrompt(draftEntries);
4999
+ debugAiPrompt(prompt, "draft-basket");
4593
5000
  copyTextToClipboard(prompt).then((copied) => {
4594
- setSubmitMsg(copied ? "\u4EFB\u52A1\u6587\u672C\u5DF2\u590D\u5236 \u2713" : "\u590D\u5236\u5931\u8D25");
5001
+ setSubmitMsg(copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u5931\u8D25");
4595
5002
  setTimeout(() => setSubmitMsg(""), copied ? 2200 : 1800);
4596
5003
  }).catch(() => {
4597
5004
  setSubmitMsg("\u590D\u5236\u5931\u8D25");
@@ -4773,6 +5180,9 @@ function InspectorPanel({
4773
5180
  });
4774
5181
  });
4775
5182
  function getChangeLabel(prop) {
5183
+ if (prop.startsWith("annotation:")) {
5184
+ return "\u6807\u6CE8 \xB7 AI\u8BC6\u522B";
5185
+ }
4776
5186
  if (prop.startsWith("library-token:") || prop.startsWith("library-component:")) {
4777
5187
  const [resource = "", action = "", name = "\u672A\u547D\u540D"] = prop.split(":");
4778
5188
  const resourceLabel = resource === "library-token" ? "Token" : "\u7EC4\u4EF6\u89C4\u683C";
@@ -4902,6 +5312,7 @@ function InspectorPanel({
4902
5312
  function resetDraftChangeOnPage(draft, change) {
4903
5313
  const isCurrentDraft = selectedRef.current ? getDraftTargetInfo(selectedRef.current).key === draft.key : false;
4904
5314
  if (isCurrentDraft) clearPendingForProp(change.prop);
5315
+ if (change.prop.startsWith("annotation:")) return;
4905
5316
  if (change.prop.startsWith("library-")) return;
4906
5317
  if (change.prop === "component-spec" || change.prop.startsWith("component-new-variant:")) return;
4907
5318
  const targets = getDraftSelectorTargets(draft.selector);
@@ -5218,6 +5629,29 @@ function InspectorPanel({
5218
5629
  const activeBadgeStatus = pendingBadgeStatus || badgeStatusVal;
5219
5630
  const activeCardVariant = pendingCardVariant || cardVariantVal;
5220
5631
  const componentSizeOptions = getComponentSizeControlOptions(componentCapability);
5632
+ const componentPrimaryText = pendingComponentText ?? componentTextDraft ?? componentTextVal ?? componentEditableSlots.map((slot) => componentTextSlotDrafts[slot.key] || componentTextSlotVals[slot.key] || "").find(Boolean) ?? textContent ?? "";
5633
+ const componentElementInfos = componentMeta ? (() => {
5634
+ const items = [];
5635
+ const seen = /* @__PURE__ */ new Set();
5636
+ const pushItem = (label, element) => {
5637
+ if (!element || seen.has(element)) return;
5638
+ seen.add(element);
5639
+ items.push({
5640
+ key: `${label}:${getSelectorForScope(element, "current")}`,
5641
+ label,
5642
+ selector: getSelectorForScope(element, "current"),
5643
+ text: (element.textContent ?? "").trim() || "\u7A7A",
5644
+ style: getComponentElementStyleSummary(element),
5645
+ spacing: getComponentElementSpacingSummary(element),
5646
+ styleStatus: getComponentElementStyleStatus(element, tokenMap, colorPalette, tokenLabels, typographyTokens),
5647
+ spacingStatus: getComponentElementSpacingStatus(element, spaceSteps)
5648
+ });
5649
+ };
5650
+ pushItem("\u7EC4\u4EF6\u5916\u5C42", selected);
5651
+ componentEditableSlots.forEach((slot) => pushItem(slot.label, getComponentSlotElement(selected, slot)));
5652
+ componentChildSlots.forEach((slot) => pushItem(slot.label, getComponentSlotElement(selected, slot)));
5653
+ return items;
5654
+ })() : [];
5221
5655
  const showElementStyleSections = !componentMeta;
5222
5656
  const localDraftEntries = Object.values(localDrafts).sort((a, b) => b.updatedAt - a.updatedAt);
5223
5657
  const localDraftChangeCount = localDraftEntries.reduce((sum, entry) => sum + entry.changes.length, 0);
@@ -5604,7 +6038,7 @@ function InspectorPanel({
5604
6038
  ref: panelElRef,
5605
6039
  className: `di-panel${componentCreateOpen || libraryOpen ? " di-panel--drawer-open" : ""}${libraryWorkbenchOpen ? " di-panel--workbench-open" : ""}`,
5606
6040
  "data-di-panel-role": "primary",
5607
- style: { top: panelPos.top, left: panelPos.left },
6041
+ style: { top: panelPos.top, left: panelPos.left, ...inspectorTypographyVars },
5608
6042
  children: [
5609
6043
  /* @__PURE__ */ jsxs("div", { className: "di-head", onMouseDown: onDragStart, children: [
5610
6044
  /* @__PURE__ */ jsxs("div", { className: "di-head-left", children: [
@@ -6025,7 +6459,24 @@ function InspectorPanel({
6025
6459
  " \u4E2A\u89C4\u683C"
6026
6460
  ] })
6027
6461
  ] }),
6028
- /* @__PURE__ */ jsx2("button", { type: "button", className: "di-btn-save", onClick: openLibraryComponentCreate, children: "+ \u7EC4\u4EF6\u89C4\u683C" })
6462
+ /* @__PURE__ */ jsxs("div", { className: "di-library-toolbar-actions", children: [
6463
+ /* @__PURE__ */ jsxs(
6464
+ "button",
6465
+ {
6466
+ type: "button",
6467
+ className: "di-library-workbench-soft-btn",
6468
+ onClick: () => {
6469
+ setLibraryComponentImportOpen(true);
6470
+ setLibraryComponentForm(null);
6471
+ },
6472
+ children: [
6473
+ /* @__PURE__ */ jsx2(Upload, { size: 13, strokeWidth: 2.2, "aria-hidden": "true" }),
6474
+ "\u5BFC\u5165\u7EC4\u4EF6\u5E93"
6475
+ ]
6476
+ }
6477
+ ),
6478
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "di-btn-save", onClick: openLibraryComponentCreate, children: "+ \u7EC4\u4EF6\u89C4\u683C" })
6479
+ ] })
6029
6480
  ] }),
6030
6481
  /* @__PURE__ */ jsxs("div", { className: "di-library-component-preview-grid", role: "list", "aria-label": "\u7EC4\u4EF6\u771F\u5B9E\u9884\u89C8", children: [
6031
6482
  filteredLibraryComponentItems.map((item) => {
@@ -6097,7 +6548,7 @@ function InspectorPanel({
6097
6548
  className: "di-btn-save",
6098
6549
  disabled: localDraftChangeCount === 0,
6099
6550
  onClick: handleSubmitToAi,
6100
- children: submitMsg || "\u53D1\u9001\u7ED9AI"
6551
+ children: renderSubmitLabel(submitMsg)
6101
6552
  }
6102
6553
  )
6103
6554
  ] }),
@@ -6290,7 +6741,67 @@ function InspectorPanel({
6290
6741
  /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => focusLibraryTokenUsage(selectedLibraryToken), children: "\u5B9A\u4F4D\u4F7F\u7528" }),
6291
6742
  /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => openLibraryTokenEdit(selectedLibraryToken), children: "\u7F16\u8F91" })
6292
6743
  ] })
6293
- ] }) : /* @__PURE__ */ jsx2("div", { className: "di-library-workbench-empty", children: "\u9009\u62E9\u4E00\u4E2A token \u67E5\u770B\u8BE6\u60C5" }) : libraryTab === "components" ? libraryComponentForm ? /* @__PURE__ */ jsxs("div", { className: "di-library-edit-panel", children: [
6744
+ ] }) : /* @__PURE__ */ jsx2("div", { className: "di-library-workbench-empty", children: "\u9009\u62E9\u4E00\u4E2A token \u67E5\u770B\u8BE6\u60C5" }) : libraryTab === "components" ? libraryComponentImportOpen ? /* @__PURE__ */ jsxs("div", { className: "di-library-import-panel", children: [
6745
+ /* @__PURE__ */ jsxs("div", { className: "di-library-edit-head", children: [
6746
+ /* @__PURE__ */ jsx2("strong", { children: "\u5BFC\u5165\u7EC4\u4EF6\u5E93" }),
6747
+ /* @__PURE__ */ jsx2("button", { type: "button", className: "di-head-icon-btn", onClick: () => setLibraryComponentImportOpen(false), "aria-label": "\u5173\u95ED\u5BFC\u5165\u7EC4\u4EF6\u5E93", children: /* @__PURE__ */ jsx2(X, { size: 15, strokeWidth: 2.2, "aria-hidden": "true" }) })
6748
+ ] }),
6749
+ /* @__PURE__ */ jsxs("div", { className: "di-library-import-steps", children: [
6750
+ /* @__PURE__ */ jsx2("span", { children: "\u9009\u4E00\u79CD\u65B9\u5F0F" }),
6751
+ /* @__PURE__ */ jsx2("span", { children: "\u751F\u6210\u63A5\u5165\u8349\u7A3F" }),
6752
+ /* @__PURE__ */ jsx2("span", { children: "\u53D1\u9001\u7ED9 AI \u843D\u5730" })
6753
+ ] }),
6754
+ /* @__PURE__ */ jsxs("div", { className: "di-library-import-option di-library-import-option--primary", children: [
6755
+ /* @__PURE__ */ jsxs("div", { children: [
6756
+ /* @__PURE__ */ jsx2(WandSparkles, { size: 15, strokeWidth: 2.2, "aria-hidden": "true" }),
6757
+ /* @__PURE__ */ jsx2("strong", { children: "\u8BA9 AI \u5E2E\u4F60\u6574\u7406" }),
6758
+ /* @__PURE__ */ jsx2("span", { children: "\u9002\u5408\u7B2C\u4E00\u6B21\u63A5\u5165\u3002\u4F1A\u590D\u5236\u4E00\u6BB5\u63D0\u793A\uFF0C\u8BA9 AI \u626B\u63CF\u9879\u76EE\u5E76\u751F\u6210\u7EC4\u4EF6\u6E05\u5355\u3002" })
6759
+ ] }),
6760
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: copyComponentLibraryImportPrompt, children: "\u590D\u5236\u63D0\u793A" })
6761
+ ] }),
6762
+ /* @__PURE__ */ jsxs("label", { className: "di-library-import-option", children: [
6763
+ /* @__PURE__ */ jsxs("div", { children: [
6764
+ /* @__PURE__ */ jsx2(Upload, { size: 15, strokeWidth: 2.2, "aria-hidden": "true" }),
6765
+ /* @__PURE__ */ jsx2("strong", { children: "\u5DF2\u6709\u7EC4\u4EF6\u6E05\u5355" }),
6766
+ /* @__PURE__ */ jsx2("span", { children: "\u4E0A\u4F20 JSON \u540E\u5148\u53D8\u6210\u8349\u7A3F\uFF0C\u4E0D\u4F1A\u76F4\u63A5\u6539\u6E90\u7801\u3002" })
6767
+ ] }),
6768
+ /* @__PURE__ */ jsx2(
6769
+ "input",
6770
+ {
6771
+ type: "file",
6772
+ accept: "application/json,.json",
6773
+ onChange: (event) => {
6774
+ void handleComponentImportFile(event.currentTarget.files?.[0] ?? null);
6775
+ event.currentTarget.value = "";
6776
+ }
6777
+ }
6778
+ )
6779
+ ] }),
6780
+ /* @__PURE__ */ jsxs("div", { className: "di-library-import-option", children: [
6781
+ /* @__PURE__ */ jsxs("div", { children: [
6782
+ /* @__PURE__ */ jsx2(ComponentIcon, { size: 15, strokeWidth: 2.2, "aria-hidden": "true" }),
6783
+ /* @__PURE__ */ jsx2("strong", { children: "\u5148\u52A0\u4E00\u4E2A\u8BD5\u8BD5" }),
6784
+ /* @__PURE__ */ jsx2("span", { children: "\u53EA\u767B\u8BB0\u4E00\u4E2A\u7EC4\u4EF6\uFF0C\u6BD4\u5982 Button \u6216 Card\u3002" })
6785
+ ] }),
6786
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: openLibraryComponentCreate, children: "\u6DFB\u52A0\u7EC4\u4EF6" })
6787
+ ] }),
6788
+ /* @__PURE__ */ jsxs("details", { className: "di-library-import-format", children: [
6789
+ /* @__PURE__ */ jsx2("summary", { children: "\u67E5\u770B JSON \u683C\u5F0F" }),
6790
+ /* @__PURE__ */ jsx2("pre", { children: `{
6791
+ "components": [
6792
+ {
6793
+ "type": "Button",
6794
+ "label": "\u6309\u94AE",
6795
+ "category": "action",
6796
+ "selector": ".button",
6797
+ "variants": ["primary", "secondary"],
6798
+ "tokenRefs": ["--color-brand-primary"]
6799
+ }
6800
+ ]
6801
+ }` })
6802
+ ] }),
6803
+ libraryComponentImportMsg ? /* @__PURE__ */ jsx2("div", { className: "di-library-import-msg", children: libraryComponentImportMsg }) : null
6804
+ ] }) : libraryComponentForm ? /* @__PURE__ */ jsxs("div", { className: "di-library-edit-panel", children: [
6294
6805
  /* @__PURE__ */ jsxs("div", { className: "di-library-edit-head", children: [
6295
6806
  /* @__PURE__ */ jsx2("strong", { children: libraryComponentForm.mode === "create" ? "\u65B0\u589E\u7EC4\u4EF6\u89C4\u683C" : "\u7F16\u8F91\u7EC4\u4EF6\u89C4\u683C" }),
6296
6807
  /* @__PURE__ */ jsx2("button", { type: "button", className: "di-head-icon-btn", onClick: () => setLibraryComponentForm(null), "aria-label": "\u5173\u95ED\u7EC4\u4EF6\u89C4\u683C\u8868\u5355", children: /* @__PURE__ */ jsx2(X, { size: 15, strokeWidth: 2.2, "aria-hidden": "true" }) })
@@ -6416,7 +6927,7 @@ function InspectorPanel({
6416
6927
  /* @__PURE__ */ jsx2("dd", { children: "\u53EA\u751F\u6210 AI \u4EFB\u52A1\uFF0C\u4E0D\u76F4\u63A5\u5199\u6E90\u7801" })
6417
6928
  ] })
6418
6929
  ] }),
6419
- /* @__PURE__ */ jsx2("div", { className: "di-library-detail-actions", children: /* @__PURE__ */ jsx2("button", { type: "button", onClick: handleSubmitToAi, disabled: localDraftChangeCount === 0, children: submitMsg || "\u53D1\u9001\u7ED9AI" }) })
6930
+ /* @__PURE__ */ jsx2("div", { className: "di-library-detail-actions", children: /* @__PURE__ */ jsx2("button", { type: "button", onClick: handleSubmitToAi, disabled: localDraftChangeCount === 0, children: renderSubmitLabel(submitMsg) }) })
6420
6931
  ] }) : /* @__PURE__ */ jsxs("div", { className: "di-library-detail-card", children: [
6421
6932
  /* @__PURE__ */ jsx2("div", { className: "di-library-detail-kicker", children: "Usage" }),
6422
6933
  /* @__PURE__ */ jsx2("h3", { children: "\u4F7F\u7528\u6CBB\u7406" }),
@@ -6448,7 +6959,7 @@ function InspectorPanel({
6448
6959
  className: "di-btn-save",
6449
6960
  disabled: localDraftChangeCount === 0,
6450
6961
  onClick: handleSubmitToAi,
6451
- children: submitMsg || "\u53D1\u9001\u7ED9AI"
6962
+ children: renderSubmitLabel(submitMsg)
6452
6963
  }
6453
6964
  )
6454
6965
  ] })
@@ -7019,18 +7530,74 @@ function InspectorPanel({
7019
7530
  componentMeta && /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
7020
7531
  /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u7EC4\u4EF6" }),
7021
7532
  /* @__PURE__ */ jsxs("div", { className: "di-component-card", children: [
7022
- /* @__PURE__ */ jsx2("div", { className: "di-component-head", children: /* @__PURE__ */ jsx2("div", { className: "di-component-main", children: /* @__PURE__ */ jsx2("span", { className: "di-component-name", children: componentDisplayName }) }) }),
7023
- /* @__PURE__ */ jsxs("div", { className: "di-component-meta-row", children: [
7024
- showComponentVariantMeta && /* @__PURE__ */ jsxs("span", { children: [
7025
- componentCapability?.variantKind === "card" ? "\u5C55\u793A" : "\u53D8\u4F53",
7026
- "\uFF1A",
7027
- componentCapability?.variantKind === "card" ? getCardDisplayLabel(componentMeta.variant) : componentMeta.variant
7533
+ /* @__PURE__ */ jsxs("div", { className: "di-component-head", children: [
7534
+ /* @__PURE__ */ jsx2("div", { className: "di-component-main", children: /* @__PURE__ */ jsxs("div", { className: "di-component-title-row", children: [
7535
+ /* @__PURE__ */ jsx2("span", { className: "di-component-name", children: componentDisplayName }),
7536
+ /* @__PURE__ */ jsx2(
7537
+ MatchBadge,
7538
+ {
7539
+ label: componentCapability ? "\u5DF2\u5339\u914D" : "\u5F85\u8865\u5145",
7540
+ tone: componentCapability ? "matched" : "pending",
7541
+ tooltip: componentCapability ? "\u5DF2\u5339\u914D\u5230\u53EF\u590D\u7528\u89C4\u5219\uFF0C\u53EF\u6309\u7EC4\u4EF6\u65B9\u5F0F\u67E5\u770B\u548C\u5904\u7406\u3002" : "\u5F53\u524D\u8FD8\u6CA1\u6709\u53EF\u590D\u7528\u89C4\u5219\uFF0C\u5148\u6309\u5F53\u524D\u6837\u5F0F\u5904\u7406\uFF0C\u540E\u7EED\u53EF\u8865\u5145\u5230\u8BBE\u8BA1\u5E93\u3002"
7542
+ }
7543
+ )
7544
+ ] }) }),
7545
+ /* @__PURE__ */ jsxs(
7546
+ "button",
7547
+ {
7548
+ type: "button",
7549
+ className: `di-component-meta-toggle${componentInfoOpen ? " di-component-meta-toggle--on" : ""}`,
7550
+ onClick: () => setComponentInfoOpen((open) => !open),
7551
+ "aria-expanded": componentInfoOpen,
7552
+ "aria-label": componentInfoOpen ? "\u6536\u8D77\u5143\u7D20\u4FE1\u606F" : "\u67E5\u770B\u5143\u7D20\u4FE1\u606F",
7553
+ title: componentInfoOpen ? "\u6536\u8D77\u5143\u7D20\u4FE1\u606F" : "\u67E5\u770B\u5143\u7D20\u4FE1\u606F",
7554
+ children: [
7555
+ /* @__PURE__ */ jsx2("span", { children: "\u5143\u7D20\u4FE1\u606F" }),
7556
+ /* @__PURE__ */ jsx2(ChevronDown, { size: 14, strokeWidth: 2.2, "aria-hidden": "true" })
7557
+ ]
7558
+ }
7559
+ )
7560
+ ] }),
7561
+ /* @__PURE__ */ jsx2("div", { className: "di-component-meta-block", children: componentInfoOpen && /* @__PURE__ */ jsx2("div", { className: "di-component-info-panel", children: componentElementInfos.length > 0 && /* @__PURE__ */ jsx2("div", { className: "di-component-info-elements", children: /* @__PURE__ */ jsx2("div", { className: "di-component-info-element-list", children: componentElementInfos.map((item) => /* @__PURE__ */ jsxs("section", { className: "di-component-info-element-card", children: [
7562
+ /* @__PURE__ */ jsxs("div", { className: "di-component-info-element-head", children: [
7563
+ /* @__PURE__ */ jsx2("span", { className: "di-component-info-element-label", children: item.label }),
7564
+ /* @__PURE__ */ jsx2("span", { className: "di-component-info-element-selector", title: item.selector, children: item.selector })
7028
7565
  ] }),
7029
- /* @__PURE__ */ jsxs("span", { children: [
7030
- "\u72B6\u6001\uFF1A",
7031
- getComponentStateLabel(componentMeta.state)
7566
+ /* @__PURE__ */ jsxs("dl", { className: "di-component-info-element-meta", children: [
7567
+ /* @__PURE__ */ jsxs("div", { className: "di-component-info-row", children: [
7568
+ /* @__PURE__ */ jsx2("dt", { children: "\u5185\u5BB9" }),
7569
+ /* @__PURE__ */ jsx2("dd", { title: item.text, children: item.text })
7570
+ ] }),
7571
+ /* @__PURE__ */ jsxs("div", { className: "di-component-info-row", children: [
7572
+ /* @__PURE__ */ jsx2("dt", { children: "\u6837\u5F0F" }),
7573
+ /* @__PURE__ */ jsx2("dd", { title: item.style, children: /* @__PURE__ */ jsxs("span", { className: "di-component-info-inline", children: [
7574
+ /* @__PURE__ */ jsx2("span", { children: item.style }),
7575
+ /* @__PURE__ */ jsx2(
7576
+ MatchBadge,
7577
+ {
7578
+ label: item.styleStatus === "token" ? "Token" : "\u771F\u5B9E\u503C",
7579
+ tone: item.styleStatus,
7580
+ tooltip: item.styleStatus === "token" ? "\u5F53\u524D\u503C\u5DF2\u6620\u5C04\u5230\u8BBE\u8BA1 token\u3002" : "\u5F53\u524D\u5C55\u793A\u7684\u662F\u9875\u9762\u5B9E\u9645\u6837\u5F0F\u503C\uFF0C\u5C1A\u672A\u6620\u5C04\u5230\u8BBE\u8BA1 token \u6216\u7EC4\u4EF6\u89C4\u5219\u3002"
7581
+ }
7582
+ )
7583
+ ] }) })
7584
+ ] }),
7585
+ /* @__PURE__ */ jsxs("div", { className: "di-component-info-row", children: [
7586
+ /* @__PURE__ */ jsx2("dt", { children: "\u95F4\u8DDD" }),
7587
+ /* @__PURE__ */ jsx2("dd", { title: item.spacing, children: /* @__PURE__ */ jsxs("span", { className: "di-component-info-inline", children: [
7588
+ /* @__PURE__ */ jsx2("span", { children: item.spacing }),
7589
+ item.spacingStatus && /* @__PURE__ */ jsx2(
7590
+ MatchBadge,
7591
+ {
7592
+ label: item.spacingStatus === "token" ? "Token" : "\u771F\u5B9E\u503C",
7593
+ tone: item.spacingStatus,
7594
+ tooltip: item.spacingStatus === "token" ? "\u5F53\u524D\u503C\u5DF2\u6620\u5C04\u5230\u8BBE\u8BA1 token\u3002" : "\u5F53\u524D\u5C55\u793A\u7684\u662F\u9875\u9762\u5B9E\u9645\u6837\u5F0F\u503C\uFF0C\u5C1A\u672A\u6620\u5C04\u5230\u8BBE\u8BA1 token \u6216\u7EC4\u4EF6\u89C4\u5219\u3002"
7595
+ }
7596
+ )
7597
+ ] }) })
7598
+ ] })
7032
7599
  ] })
7033
- ] }),
7600
+ ] }, item.key)) }) }) }) }),
7034
7601
  componentCapability && /* @__PURE__ */ jsxs("div", { className: "di-component-fields", children: [
7035
7602
  componentEditableSlots.map((slot) => /* @__PURE__ */ jsxs("label", { className: "di-component-field di-component-field--text", children: [
7036
7603
  /* @__PURE__ */ jsx2("span", { children: slot.label }),
@@ -7047,24 +7614,38 @@ function InspectorPanel({
7047
7614
  /* @__PURE__ */ jsx2("span", { children: visibleChildSlots.length === 1 ? visibleChildSlots[0].slot.label : "\u53EF\u9009\u9879" }),
7048
7615
  /* @__PURE__ */ jsx2("div", { className: "di-component-child-list", children: visibleChildSlots.map(({ slot, value, element }) => {
7049
7616
  const childMeta = element ? getInspectorComponentMeta(element) : null;
7617
+ const childCapability = getComponentCapability(childMeta);
7050
7618
  const childName = childMeta ? getComponentDisplayName(childMeta) : slot.label;
7051
7619
  const childValue = value || childMeta?.variant || childName;
7052
7620
  const showChildLabel = visibleChildSlots.length > 1;
7621
+ const childBadgeStatus = element && childCapability?.statusKind === "badge" ? inferBadgeStatus(getClasses(element)) : null;
7053
7622
  return /* @__PURE__ */ jsxs("div", { className: `di-component-child-item${showChildLabel ? " di-component-child-item--with-label" : ""}`, children: [
7054
7623
  showChildLabel && /* @__PURE__ */ jsx2("span", { className: "di-component-child-label", children: slot.label }),
7055
- /* @__PURE__ */ jsx2("span", { className: "di-component-child-value", children: childValue }),
7056
- /* @__PURE__ */ jsx2(
7624
+ element && childCapability?.statusKind === "badge" ? /* @__PURE__ */ jsx2("div", { className: "di-component-child-variant-grid", role: "group", "aria-label": `${childName} \u72B6\u6001`, children: BADGE_STATUS_OPTIONS.map((option) => /* @__PURE__ */ jsx2(
7057
7625
  "button",
7058
7626
  {
7059
7627
  type: "button",
7060
- className: "di-component-child-select",
7061
- onClick: () => {
7062
- if (element) selectEl(element);
7063
- },
7064
- title: `\u9009\u62E9 ${childName}`,
7065
- children: "\u9009\u62E9"
7066
- }
7067
- )
7628
+ className: `di-component-child-variant-tile${childBadgeStatus === option.key ? " di-component-child-variant-tile--on" : ""}`,
7629
+ onClick: () => updateBadgeStatusOnElement(element, option.key),
7630
+ title: `${childName} \xB7 ${option.label}`,
7631
+ children: option.label
7632
+ },
7633
+ option.key
7634
+ )) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7635
+ /* @__PURE__ */ jsx2("span", { className: "di-component-child-value", children: childValue }),
7636
+ /* @__PURE__ */ jsx2(
7637
+ "button",
7638
+ {
7639
+ type: "button",
7640
+ className: "di-component-child-select",
7641
+ onClick: () => {
7642
+ if (element) selectEl(element);
7643
+ },
7644
+ title: `\u9009\u62E9 ${childName}`,
7645
+ children: "\u9009\u62E9"
7646
+ }
7647
+ )
7648
+ ] })
7068
7649
  ] }, slot.key);
7069
7650
  }) })
7070
7651
  ] }),
@@ -7120,11 +7701,11 @@ function InspectorPanel({
7120
7701
  ] }),
7121
7702
  componentCapability.statusKind === "badge" && /* @__PURE__ */ jsxs("div", { className: "di-component-field", children: [
7122
7703
  /* @__PURE__ */ jsx2("span", { children: "\u72B6\u6001" }),
7123
- /* @__PURE__ */ jsx2("div", { className: "di-component-segment di-component-segment--tone", role: "group", "aria-label": "\u6807\u7B7E\u72B6\u6001", children: BADGE_STATUS_OPTIONS.map((option) => /* @__PURE__ */ jsx2(
7704
+ /* @__PURE__ */ jsx2("div", { className: "di-component-variant-grid", role: "group", "aria-label": "\u6807\u7B7E\u72B6\u6001", children: BADGE_STATUS_OPTIONS.map((option) => /* @__PURE__ */ jsx2(
7124
7705
  "button",
7125
7706
  {
7126
7707
  type: "button",
7127
- className: `di-component-segment-btn${activeBadgeStatus === option.key ? " di-component-segment-btn--on" : ""}`,
7708
+ className: `di-component-variant-tile${activeBadgeStatus === option.key ? " di-component-variant-tile--on" : ""}`,
7128
7709
  onClick: () => updateBadgeStatus(option.key),
7129
7710
  children: option.label
7130
7711
  },
@@ -8273,6 +8854,43 @@ function InspectorPanel({
8273
8854
  ] })
8274
8855
  ] })
8275
8856
  ] }),
8857
+ /* @__PURE__ */ jsxs("div", { className: "di-section di-annotation-section", children: [
8858
+ /* @__PURE__ */ jsxs("div", { className: `di-section-title-row di-section-title-row--action${annotationOpen ? "" : " di-section-title-row--empty"}`, children: [
8859
+ /* @__PURE__ */ jsxs("div", { className: "di-section-title-group", children: [
8860
+ /* @__PURE__ */ jsx2("div", { className: "di-section-title", children: "\u6807\u6CE8" }),
8861
+ localDraftEntries.some((entry) => entry.changes.some((change) => change.prop.startsWith("annotation:"))) && /* @__PURE__ */ jsx2("span", { className: "di-annotation-count", children: "\u5DF2\u8BB0\u5F55" })
8862
+ ] }),
8863
+ /* @__PURE__ */ jsx2(
8864
+ "button",
8865
+ {
8866
+ type: "button",
8867
+ className: "di-section-icon-action",
8868
+ onClick: () => setAnnotationOpen((open) => !open),
8869
+ title: annotationOpen ? "\u6536\u8D77\u6807\u6CE8" : "\u6DFB\u52A0\u6807\u6CE8",
8870
+ "aria-label": annotationOpen ? "\u6536\u8D77\u6807\u6CE8" : "\u6DFB\u52A0\u6807\u6CE8",
8871
+ children: annotationOpen ? /* @__PURE__ */ jsx2(Minus, { size: 14, strokeWidth: 2.4, "aria-hidden": "true" }) : /* @__PURE__ */ jsx2(Plus, { size: 14, strokeWidth: 2.4, "aria-hidden": "true" })
8872
+ }
8873
+ )
8874
+ ] }),
8875
+ annotationOpen && /* @__PURE__ */ jsx2("div", { className: "di-annotation-card", children: /* @__PURE__ */ jsx2("label", { className: "di-annotation-row di-annotation-row--text", children: /* @__PURE__ */ jsx2(
8876
+ "textarea",
8877
+ {
8878
+ className: "di-annotation-textarea",
8879
+ value: annotationText,
8880
+ placeholder: "\u76F4\u63A5\u5199\u4F60\u7684\u60F3\u6CD5\uFF0C\u4F8B\u5982\uFF1A\u8FD9\u91CC\u8981\u4FDD\u6301\u9AD8\u4EAE\u4F46\u4E0D\u8981\u7528\u5B9E\u5E95\u4E3B\u6309\u94AE\u6837\u5F0F\u3002AI \u4F1A\u5224\u65AD\u8FD9\u662F\u8BBE\u8BA1\u8BF4\u660E\u3001\u4EA7\u54C1\u89C4\u5219\u8FD8\u662F\u5F00\u53D1\u5907\u6CE8\u3002",
8881
+ rows: 2,
8882
+ onChange: (event) => {
8883
+ const nextText = event.currentTarget.value;
8884
+ setAnnotationText(nextText);
8885
+ updateAnnotationDraft(nextText);
8886
+ resizeTextareaToContent(event.currentTarget);
8887
+ },
8888
+ ref: (node) => {
8889
+ if (node) resizeTextareaToContent(node);
8890
+ }
8891
+ }
8892
+ ) }) })
8893
+ ] }),
8276
8894
  /* @__PURE__ */ jsxs("div", { className: "di-section", children: [
8277
8895
  /* @__PURE__ */ jsxs("div", { className: "di-section-title", children: [
8278
8896
  "\u672C\u6B21\u4FEE\u6539\u5185\u5BB9 ",
@@ -8422,7 +9040,7 @@ ${pending.map(([p, v]) => ` ${p}: ${v};`).join("\n")}
8422
9040
  disabled: !hasPending,
8423
9041
  style: !hasPending ? { opacity: 0.4, cursor: "not-allowed" } : {},
8424
9042
  title: "\u590D\u5236\u4E00\u6BB5\u53EF\u76F4\u63A5\u53D1\u7ED9 AI \u7684\u4EFB\u52A1\u6587\u672C",
8425
- children: submitMsg || "\u53D1\u9001\u7ED9AI"
9043
+ children: renderSubmitLabel(submitMsg)
8426
9044
  }
8427
9045
  )
8428
9046
  ] });
@@ -8458,115 +9076,6 @@ ${pending.map(([p, v]) => ` ${p}: ${v};`).join("\n")}
8458
9076
  )
8459
9077
  ] });
8460
9078
  }
8461
- var CODE_FILES = [
8462
- { name: "src/", desc: "React \u7EC4\u4EF6\u3001\u9875\u9762\u3001\u6837\u5F0F\u6E90\u7801\uFF08\u4E0D\u542B\u8C03\u8BD5\u5DE5\u5177\uFF09" },
8463
- { name: "index.html", desc: "\u5E94\u7528\u5165\u53E3 HTML" },
8464
- { name: "package.json", desc: "\u4F9D\u8D56\u5305\u4E0E\u811A\u672C\u914D\u7F6E" },
8465
- { name: "tsconfig.json", desc: "TypeScript \u7F16\u8BD1\u914D\u7F6E" },
8466
- { name: "vite.config.ts", desc: "\u6784\u5EFA\u5DE5\u5177\u914D\u7F6E" }
8467
- ];
8468
- var PRODUCT_FILES = [
8469
- { name: "docs/PRODUCT_PLAN.md", desc: "\u4EA7\u54C1\u89C4\u5212\u4E0E\u8DEF\u7EBF\u56FE" },
8470
- { name: "docs/PROJECT.md", desc: "\u9879\u76EE\u80CC\u666F\u4E0E\u76EE\u6807\u6982\u8FF0" },
8471
- { name: "docs/DECISIONS.md", desc: "\u5173\u952E\u4EA7\u54C1\u51B3\u7B56\u8BB0\u5F55" },
8472
- { name: "docs/CHANGELOG.md", desc: "\u529F\u80FD\u8FED\u4EE3\u53D8\u66F4\u65E5\u5FD7" },
8473
- { name: "docs/CODE_STRUCTURE.md", desc: "\u524D\u7AEF\u76EE\u5F55\u7ED3\u6784\u8BF4\u660E" },
8474
- { name: "docs/DESIGN_STANDARDS.md", desc: "\u8BBE\u8BA1\u89C4\u8303\u603B\u89C8" }
8475
- ];
8476
- var DESIGN_FILES = [
8477
- { name: "docs/design/OVERVIEW.md", desc: "\u8BBE\u8BA1\u7CFB\u7EDF\u603B\u89C8" },
8478
- { name: "docs/design/tokens.md", desc: "Design Token \u4F7F\u7528\u8BF4\u660E" },
8479
- { name: "docs/design/layout.md", desc: "\u9875\u9762\u5E03\u5C40\u89C4\u8303" },
8480
- { name: "docs/design/component-index.md", desc: "\u7EC4\u4EF6\u6E05\u5355\u7D22\u5F15" },
8481
- { name: "docs/design/business-components.md", desc: "\u4E1A\u52A1\u7EC4\u4EF6\u8BF4\u660E" }
8482
- ];
8483
- function FileList({ files }) {
8484
- return /* @__PURE__ */ jsx2("ul", { className: "di-dl-file-list", children: files.map((f) => /* @__PURE__ */ jsxs("li", { children: [
8485
- /* @__PURE__ */ jsx2("span", { className: "di-dl-file-name", children: f.name }),
8486
- /* @__PURE__ */ jsx2("span", { className: "di-dl-file-desc", children: f.desc })
8487
- ] }, f.name)) });
8488
- }
8489
- function DownloadButton() {
8490
- const { endpoints } = useDevInspectorConfig();
8491
- const [open, setOpen] = useState(false);
8492
- const [status, setStatus] = useState("idle");
8493
- const [filePath, setFilePath] = useState("");
8494
- const [details, setDetails] = useState({ code: false, product: false, design: false });
8495
- const [opts, setOpts] = useState({ code: true, product: false, design: false });
8496
- function toggle(k) {
8497
- setOpts((prev) => ({ ...prev, [k]: !prev[k] }));
8498
- }
8499
- function toggleDetail(k) {
8500
- setDetails((prev) => ({ ...prev, [k]: !prev[k] }));
8501
- }
8502
- function handleOpen() {
8503
- setOpen((v) => !v);
8504
- if (open) setStatus("idle");
8505
- }
8506
- async function download() {
8507
- if (!opts.code && !opts.product && !opts.design) return;
8508
- setStatus("packing");
8509
- try {
8510
- const params = new URLSearchParams({
8511
- code: opts.code ? "1" : "0",
8512
- product: opts.product ? "1" : "0",
8513
- design: opts.design ? "1" : "0"
8514
- });
8515
- const res = await fetch(`${endpoints.handoff}?${params}`);
8516
- const json = await res.json();
8517
- if (!json.ok) throw new Error(json.error);
8518
- setFilePath(json.path);
8519
- setStatus("done");
8520
- } catch {
8521
- setStatus("error");
8522
- }
8523
- }
8524
- async function revealInFinder() {
8525
- await fetch(`${endpoints.reveal}?path=${encodeURIComponent(filePath)}`);
8526
- }
8527
- const sections = [
8528
- { key: "code", label: "\u524D\u7AEF\u4EE3\u7801", files: CODE_FILES },
8529
- { key: "product", label: "\u4EA7\u54C1\u6587\u6863", files: PRODUCT_FILES },
8530
- { key: "design", label: "\u8BBE\u8BA1\u6587\u6863", files: DESIGN_FILES }
8531
- ];
8532
- return /* @__PURE__ */ jsxs(Fragment, { children: [
8533
- /* @__PURE__ */ jsx2("button", { className: "di-dl-btn", onClick: handleOpen, children: "\u4E0B\u8F7D" }),
8534
- open && /* @__PURE__ */ jsxs("div", { className: "di-dl-modal", children: [
8535
- /* @__PURE__ */ jsx2("div", { className: "di-dl-title", children: "\u9009\u62E9\u4E0B\u8F7D\u5185\u5BB9" }),
8536
- status === "packing" && /* @__PURE__ */ jsxs("div", { className: "di-dl-status", children: [
8537
- /* @__PURE__ */ jsx2("span", { className: "di-dl-spinner" }),
8538
- "\u6B63\u5728\u6253\u5305\uFF0C\u8BF7\u7A0D\u5019\u2026"
8539
- ] }),
8540
- status === "done" && /* @__PURE__ */ jsxs("div", { className: "di-dl-done", children: [
8541
- /* @__PURE__ */ jsx2("div", { className: "di-dl-done-check", children: "\u2713 \u5DF2\u4FDD\u5B58\u81F3\u684C\u9762" }),
8542
- /* @__PURE__ */ jsx2("div", { className: "di-dl-done-path", children: filePath.replace(/.*\//, "") }),
8543
- /* @__PURE__ */ jsx2("button", { className: "di-dl-reveal-btn", onClick: revealInFinder, children: "\u5728 Finder \u4E2D\u663E\u793A" })
8544
- ] }),
8545
- 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" }),
8546
- (status === "idle" || status === "error") && /* @__PURE__ */ jsxs(Fragment, { children: [
8547
- sections.map(({ key, label, files }) => /* @__PURE__ */ jsxs("div", { children: [
8548
- /* @__PURE__ */ jsxs("div", { className: "di-dl-row", children: [
8549
- /* @__PURE__ */ jsxs("label", { className: "di-dl-check-label", children: [
8550
- /* @__PURE__ */ jsx2("input", { type: "checkbox", checked: opts[key], onChange: () => toggle(key) }),
8551
- label
8552
- ] }),
8553
- /* @__PURE__ */ jsx2("button", { className: "di-dl-detail-btn", onClick: () => toggleDetail(key), children: details[key] ? "\u6536\u8D77" : "\u8BE6\u60C5" })
8554
- ] }),
8555
- details[key] && /* @__PURE__ */ jsx2(FileList, { files })
8556
- ] }, key)),
8557
- /* @__PURE__ */ jsx2(
8558
- "button",
8559
- {
8560
- className: "di-dl-confirm",
8561
- onClick: download,
8562
- disabled: !opts.code && !opts.product && !opts.design,
8563
- children: "\u4E0B\u8F7D"
8564
- }
8565
- )
8566
- ] })
8567
- ] })
8568
- ] });
8569
- }
8570
9079
  function DevInspector() {
8571
9080
  const [active, setActive] = useState(false);
8572
9081
  const [tokenMap, setTokenMap] = useState({});
@@ -8596,26 +9105,29 @@ function DevInspector() {
8596
9105
  useEffect(() => {
8597
9106
  const onOver = (e) => {
8598
9107
  if (!active || modalOpenRef.current) return;
8599
- const el = e.target;
8600
- if (!el?.classList) return;
8601
- if (isInsidePanel(el)) {
8602
- el.classList.remove("di-hover");
9108
+ const raw = e.target;
9109
+ if (!raw?.classList) return;
9110
+ if (isInsidePanel(raw)) {
9111
+ raw.classList.remove("di-hover");
8603
9112
  return;
8604
9113
  }
8605
- el.classList.add("di-hover");
9114
+ document.querySelectorAll(".di-hover").forEach((item) => item.classList.remove("di-hover"));
9115
+ resolveSelectionTarget(raw).classList.add("di-hover");
8606
9116
  };
8607
- const onOut = (e) => {
8608
- e.target?.classList?.remove("di-hover");
9117
+ const onOut = () => {
9118
+ document.querySelectorAll(".di-hover").forEach((item) => item.classList.remove("di-hover"));
8609
9119
  };
8610
9120
  const onClick = (e) => {
8611
9121
  if (!active || modalOpenRef.current) return;
8612
- const el = e.target;
8613
- if (isInsidePanel(el)) return;
9122
+ const raw = e.target;
9123
+ if (!raw?.classList) return;
9124
+ if (isInsidePanel(raw)) return;
8614
9125
  e.preventDefault();
8615
9126
  e.stopPropagation();
8616
- el.classList.remove("di-hover");
9127
+ const target = resolveSelectionTarget(raw);
9128
+ document.querySelectorAll(".di-hover").forEach((item) => item.classList.remove("di-hover"));
8617
9129
  setPanels(
8618
- (prev) => prev.length === 0 ? [{ id: "main", el }] : prev.map((p, i) => i === 0 ? { ...p, el } : p)
9130
+ (prev) => prev.length === 0 ? [{ id: "main", el: target }] : prev.map((p, i) => i === 0 ? { ...p, el: target } : p)
8619
9131
  );
8620
9132
  };
8621
9133
  document.addEventListener("mouseover", onOver, true);
@@ -8656,7 +9168,6 @@ function DevInspector() {
8656
9168
  const primaryEl = panels[0]?.el;
8657
9169
  const triggerTooltip = active ? `\u9000\u51FA\u7F16\u8F91\uFF08${EXIT_SHORTCUT_LABEL}\uFF09` : `\u8FDB\u5165\u7F16\u8F91\uFF08${EDIT_SHORTCUT_LABEL}\uFF09`;
8658
9170
  return /* @__PURE__ */ jsxs(Fragment, { children: [
8659
- /* @__PURE__ */ jsx2(DownloadButton, {}),
8660
9171
  /* @__PURE__ */ jsx2(
8661
9172
  "button",
8662
9173
  {