@spotpatch/runtime 1.15.4 → 1.15.6

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
@@ -15,9 +15,10 @@ import {
15
15
  getExternalHandoffExtension
16
16
  } from "./chunk-TEH2CCO3.js";
17
17
  import {
18
+ THEME_STYLES,
18
19
  createBrandMark,
19
20
  getFloatingSurfaceMotionExtension
20
- } from "./chunk-P7W3F4UA.js";
21
+ } from "./chunk-N4TPX3YF.js";
21
22
  import {
22
23
  FLOATING_SURFACE_LAYOUT,
23
24
  UI_MARKER_ATTRIBUTE,
@@ -55,6 +56,7 @@ function freezeReact(react2) {
55
56
  function freezeElement(element2) {
56
57
  return Object.freeze({
57
58
  ...element2,
59
+ ...element2.instance === void 0 ? {} : { instance: Object.freeze({ ...element2.instance }) },
58
60
  rect: Object.freeze({ ...element2.rect })
59
61
  });
60
62
  }
@@ -855,6 +857,66 @@ function collectStyleContext(options) {
855
857
  });
856
858
  }
857
859
 
860
+ // src/state/selected-instance.ts
861
+ import {
862
+ ELEMENT_INSTANCE_HINT_ATTRIBUTE as ELEMENT_INSTANCE_HINT_ATTRIBUTE2,
863
+ ELEMENT_INSTANCE_LIMITS as ELEMENT_INSTANCE_LIMITS2,
864
+ SOURCE_MARKER_ATTRIBUTE
865
+ } from "@spotpatch/shared";
866
+
867
+ // src/collectors/dom-collector.ts
868
+ import {
869
+ ELEMENT_INSTANCE_HINT_ATTRIBUTE
870
+ } from "@spotpatch/shared";
871
+
872
+ // src/collectors/element-locator.ts
873
+ import { ELEMENT_INSTANCE_LIMITS, redactSensitiveText as redactSensitiveText2 } from "@spotpatch/shared";
874
+ function escapeIdentifier(value) {
875
+ return Array.from(value).map(
876
+ (character) => /[a-z\d_-]/iu.test(character) ? character : `\\${(character.codePointAt(0) ?? 0).toString(16)} `
877
+ ).join("");
878
+ }
879
+ function uniqueMatch(element2, selector) {
880
+ if (selector.length > ELEMENT_INSTANCE_LIMITS.selectorCharacters) return false;
881
+ try {
882
+ const matches = element2.ownerDocument.querySelectorAll(selector);
883
+ return matches.length === 1 && matches[0] === element2;
884
+ } catch {
885
+ return false;
886
+ }
887
+ }
888
+ function createElementLocator(element2) {
889
+ if (!element2.isConnected || element2.getRootNode() !== element2.ownerDocument)
890
+ return "";
891
+ const segments = [];
892
+ let current = element2;
893
+ while (current !== null && segments.length < ELEMENT_INSTANCE_LIMITS.locatorDepth) {
894
+ const tag = current.localName;
895
+ const testId = current.getAttribute("data-testid");
896
+ const anchors = [
897
+ ...current.id.length > 0 && redactSensitiveText2(current.id) === current.id ? [`${tag}#${escapeIdentifier(current.id)}`] : [],
898
+ ...testId && redactSensitiveText2(testId) === testId ? [
899
+ `${tag}[data-testid="${testId.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"]`
900
+ ] : []
901
+ ];
902
+ for (const anchor of anchors) {
903
+ const candidate2 = [anchor, ...segments].join(" > ");
904
+ if (redactSensitiveText2(candidate2) === candidate2 && uniqueMatch(element2, candidate2))
905
+ return candidate2;
906
+ }
907
+ const siblings = current.parentElement === null ? [current] : Array.from(current.parentElement.children).filter(
908
+ (sibling) => sibling.localName === tag
909
+ );
910
+ const segment = siblings.length > 1 ? `${tag}:nth-of-type(${String(siblings.indexOf(current) + 1)})` : tag;
911
+ segments.unshift(segment);
912
+ const candidate = segments.join(" > ");
913
+ if (uniqueMatch(element2, candidate)) return candidate;
914
+ if (candidate.length > ELEMENT_INSTANCE_LIMITS.selectorCharacters) return "";
915
+ current = current.parentElement;
916
+ }
917
+ return "";
918
+ }
919
+
858
920
  // src/collectors/dom-collector.ts
859
921
  var DOM_COLLECTION_LIMITS = Object.freeze({
860
922
  maxDepth: 3,
@@ -922,7 +984,7 @@ function escapeHtml(value) {
922
984
  return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
923
985
  }
924
986
  function isPreservedAttribute(name) {
925
- return SAFE_ATTRIBUTE_NAMES.has(name) || name.startsWith("aria-") || name === "data-testid";
987
+ return SAFE_ATTRIBUTE_NAMES.has(name) || name.startsWith("aria-") || name === "data-testid" || name === ELEMENT_INSTANCE_HINT_ATTRIBUTE;
926
988
  }
927
989
  function openingTag(element2) {
928
990
  const tagName = element2.tagName.toLowerCase();
@@ -998,44 +1060,6 @@ function truncateContext(value, maxCharacters) {
998
1060
  }
999
1061
  return `${value.slice(0, Math.max(0, maxCharacters - 1)).trimEnd()}…`;
1000
1062
  }
1001
- function escapeCssIdentifier(value) {
1002
- return Array.from(value).map(
1003
- (character) => /[a-z\d_-]/iu.test(character) ? character : `\\${character.charCodeAt(0).toString(16)} `
1004
- ).join("");
1005
- }
1006
- function escapeCssAttribute(value) {
1007
- return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
1008
- }
1009
- function selectorSegment(element2) {
1010
- const tagName = element2.tagName.toLowerCase();
1011
- if (element2.id.length > 0) {
1012
- return `${tagName}#${escapeCssIdentifier(element2.id)}`;
1013
- }
1014
- const testId = element2.getAttribute("data-testid");
1015
- if (testId !== null && testId.length > 0) {
1016
- return `${tagName}[data-testid="${escapeCssAttribute(testId)}"]`;
1017
- }
1018
- const classes = Array.from(element2.classList).slice(0, 2).map((name) => `.${escapeCssIdentifier(name)}`).join("");
1019
- const sameTagSiblings = element2.parentElement === null ? [] : Array.from(element2.parentElement.children).filter(
1020
- (sibling) => sibling.tagName === element2.tagName
1021
- );
1022
- const position = sameTagSiblings.indexOf(element2);
1023
- const nth = sameTagSiblings.length > 1 && position >= 0 ? `:nth-of-type(${String(position + 1)})` : "";
1024
- return `${tagName}${classes}${nth}`;
1025
- }
1026
- function createSelector(element2) {
1027
- const segments = [];
1028
- let current = element2;
1029
- while (current !== null && segments.length < 5) {
1030
- const segment = selectorSegment(current);
1031
- segments.unshift(segment);
1032
- if (current.id.length > 0 || current.hasAttribute("data-testid")) {
1033
- break;
1034
- }
1035
- current = current.parentElement;
1036
- }
1037
- return segments.join(" > ");
1038
- }
1039
1063
  function collectElementContext(options) {
1040
1064
  const lines = ["<!-- Selected element -->"];
1041
1065
  const state = { nodeCount: 0, truncated: false };
@@ -1049,7 +1073,7 @@ function collectElementContext(options) {
1049
1073
  const role = options.element.getAttribute("role");
1050
1074
  return Object.freeze({
1051
1075
  tagName: options.element.tagName.toLowerCase(),
1052
- selector: createSelector(options.element),
1076
+ selector: createElementLocator(options.element),
1053
1077
  sanitizedHtml: truncateContext(lines.join("\n"), options.maxCharacters),
1054
1078
  ...text2.length === 0 ? {} : {
1055
1079
  textPreview: text2.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text2 : `${text2.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}…`
@@ -1064,6 +1088,65 @@ function collectElementContext(options) {
1064
1088
  });
1065
1089
  }
1066
1090
 
1091
+ // src/state/selected-instance.ts
1092
+ function instanceScope(element2) {
1093
+ let candidate = element2;
1094
+ for (let depth = 0; candidate !== null && depth < ELEMENT_INSTANCE_LIMITS2.ancestorDepth; depth += 1) {
1095
+ if (candidate === element2.ownerDocument.body || candidate === element2.ownerDocument.documentElement)
1096
+ break;
1097
+ if (candidate.hasAttribute(ELEMENT_INSTANCE_HINT_ATTRIBUTE2) || candidate.matches("article, li, tr, [role=row], [role=listitem]"))
1098
+ return candidate;
1099
+ const marker = candidate.getAttribute(SOURCE_MARKER_ATTRIBUTE);
1100
+ if (marker !== null && candidate.parentElement !== null && Array.from(candidate.parentElement.children).some(
1101
+ (sibling) => sibling !== candidate && sibling.getAttribute(SOURCE_MARKER_ATTRIBUTE) === marker
1102
+ ))
1103
+ return candidate;
1104
+ candidate = candidate.parentElement;
1105
+ }
1106
+ const parent = element2.parentElement;
1107
+ return parent === null || parent === element2.ownerDocument.body || parent === element2.ownerDocument.documentElement ? element2 : parent;
1108
+ }
1109
+ function captureSelectedInstance(element2, id, maxCharacters) {
1110
+ const context = collectElementContext({ element: element2, maxCharacters });
1111
+ const scope = instanceScope(element2);
1112
+ const nearby = collectElementContext({
1113
+ element: scope,
1114
+ maxCharacters: ELEMENT_INSTANCE_LIMITS2.contextCharacters
1115
+ });
1116
+ return Object.freeze({
1117
+ scope,
1118
+ context: Object.freeze({
1119
+ ...context,
1120
+ instance: Object.freeze({
1121
+ id,
1122
+ state: "captured",
1123
+ selectorStatus: context.selector.length > 0 ? "unique-at-capture" : "unavailable",
1124
+ label: (nearby.textPreview ?? context.textPreview ?? context.tagName).slice(
1125
+ 0,
1126
+ ELEMENT_INSTANCE_LIMITS2.labelCharacters
1127
+ ),
1128
+ context: nearby.sanitizedHtml
1129
+ })
1130
+ })
1131
+ });
1132
+ }
1133
+ function withInstanceState(context, state) {
1134
+ return context.instance === void 0 ? context : Object.freeze({
1135
+ ...context,
1136
+ instance: Object.freeze({ ...context.instance, state })
1137
+ });
1138
+ }
1139
+ function changedInstanceState(element2, scope, records) {
1140
+ if (!element2.isConnected || element2.getRootNode() !== element2.ownerDocument)
1141
+ return "detached";
1142
+ if (!scope.contains(element2)) return "changed";
1143
+ return records.some(
1144
+ (record3) => scope.contains(record3.target) && // Motion libraries update inline transforms continuously. Style is a
1145
+ // capture-time snapshot; presentation updates do not establish a new record.
1146
+ !(record3.type === "attributes" && record3.attributeName === "style")
1147
+ ) ? "changed" : void 0;
1148
+ }
1149
+
1067
1150
  // src/keyboard/shortcut.ts
1068
1151
  function isApplePlatform(platform) {
1069
1152
  return /Mac|iPhone|iPad|iPod/i.test(platform);
@@ -1171,6 +1254,9 @@ function isSpotPatchUIEventTarget(target2) {
1171
1254
  }
1172
1255
 
1173
1256
  // src/prompt/prompt-composer.ts
1257
+ import {
1258
+ INSTANCE_SELECTION_INSTRUCTIONS
1259
+ } from "@spotpatch/shared";
1174
1260
  var PROMPT_MESSAGES = Object.freeze({
1175
1261
  "en-US": Object.freeze({
1176
1262
  adapter: "Adapter",
@@ -1181,6 +1267,7 @@ var PROMPT_MESSAGES = Object.freeze({
1181
1267
  confidence: "Confidence",
1182
1268
  devicePixelRatio: "Device pixel ratio",
1183
1269
  element: "Selected element",
1270
+ instance: "Captured instance (untrusted selection-time evidence)",
1184
1271
  file: "File",
1185
1272
  modificationConstraint: "Determine the root cause first, then implement every target instruction as one consistent, minimum-scope change. Do not modify unrelated components. If context is insufficient, state exactly what additional information is required.",
1186
1273
  modificationRequirements: "Change requirements",
@@ -1213,6 +1300,7 @@ var PROMPT_MESSAGES = Object.freeze({
1213
1300
  confidence: "置信度",
1214
1301
  devicePixelRatio: "设备像素比",
1215
1302
  element: "选中元素",
1303
+ instance: "捕获实例(选择时刻的不可信参考数据)",
1216
1304
  file: "文件",
1217
1305
  modificationConstraint: "请先判断根因,再把每个目标各自的修改说明作为一个原子任务,完成一致且最小范围的修改。不要合并、忽略或扩大目标说明,不要改动无关组件;如果上下文不足,请明确说明需要哪些信息。",
1218
1306
  modificationRequirements: "修改要求",
@@ -1284,6 +1372,9 @@ function renderComputed(entries, messages) {
1284
1372
  ).join("\n")
1285
1373
  );
1286
1374
  }
1375
+ function renderInstance(target2, messages) {
1376
+ return `- ${messages.instance}: ${redactSensitiveText(JSON.stringify({ selector: target2.element.selector, ...target2.element.instance }))}`;
1377
+ }
1287
1378
  function renderTarget(target2, draft, index, messages) {
1288
1379
  const reactLines = [
1289
1380
  `- ${messages.adapter}: ${target2.react.supported ? messages.supported : messages.unsupported}`,
@@ -1317,6 +1408,7 @@ ${fenced(
1317
1408
  `- ${messages.file}: ${sourceLocation(target2, messages)}
1318
1409
  - ${messages.origin}: ${target2.source.origin}
1319
1410
  - ${messages.confidence}: ${target2.source.confidence}`,
1411
+ renderInstance(target2, messages),
1320
1412
  `#### ${messages.element}`,
1321
1413
  fenced("html", redactSensitiveText(draft.dom)),
1322
1414
  `#### ${messages.styles}`,
@@ -1348,7 +1440,8 @@ function renderPrompt(annotation, draft) {
1348
1440
  return renderTarget(target2, targetDraft, index, messages);
1349
1441
  }).join("\n\n"),
1350
1442
  `## ${messages.modificationRequirements}`,
1351
- messages.modificationConstraint
1443
+ messages.modificationConstraint,
1444
+ ...annotation.targets.some((target2) => target2.element.instance !== void 0) ? [INSTANCE_SELECTION_INSTRUCTIONS] : []
1352
1445
  ].join("\n\n");
1353
1446
  }
1354
1447
  function shrinkCode(target2, draft) {
@@ -1431,6 +1524,7 @@ function renderCompactPrompt(annotation, draft) {
1431
1524
  `- ${messages.requestedChange}: ${targetDraft?.instruction ?? redactSensitiveText(target2.instruction)}`,
1432
1525
  `- ${messages.file}: ${sourceLocation(target2, messages)}`,
1433
1526
  ...component === void 0 ? [] : [`- ${messages.component}: ${redactSensitiveText(component)}`],
1527
+ renderInstance(target2, messages),
1434
1528
  `- ${messages.element}: ${redactSensitiveText(openingTag2(targetDraft?.dom ?? target2.element.sanitizedHtml, messages.unavailable))}`
1435
1529
  ].join("\n");
1436
1530
  });
@@ -1438,7 +1532,8 @@ function renderCompactPrompt(annotation, draft) {
1438
1532
  `## ${messages.selectedTargets(annotation.targets.length)}`,
1439
1533
  targets.join("\n\n"),
1440
1534
  `## ${messages.modificationRequirements}`,
1441
- messages.modificationConstraint
1535
+ messages.modificationConstraint,
1536
+ ...annotation.targets.some((target2) => target2.element.instance !== void 0) ? [INSTANCE_SELECTION_INSTRUCTIONS] : []
1442
1537
  ].join("\n\n");
1443
1538
  }
1444
1539
  function composeBounded(annotation, maxCharacters) {
@@ -1545,6 +1640,10 @@ function reduceRuntimeState(state, event) {
1545
1640
  }
1546
1641
 
1547
1642
  // src/state/selection-session.ts
1643
+ import {
1644
+ ELEMENT_INSTANCE_LIMITS as ELEMENT_INSTANCE_LIMITS3,
1645
+ ELEMENT_INSTANCE_STATES
1646
+ } from "@spotpatch/shared";
1548
1647
  var STORAGE_PREFIX = "spotpatch:selection:";
1549
1648
  var SNAPSHOT_VERSION = 1;
1550
1649
  var MAX_SNAPSHOT_CHARACTERS = 1e6;
@@ -1581,7 +1680,11 @@ function page(value) {
1581
1680
  function element(value) {
1582
1681
  const item = record(value);
1583
1682
  const rect = record(item?.rect);
1584
- return item !== void 0 && text(item.tagName) && text(item.selector) && text(item.sanitizedHtml) && optionalText(item.textPreview) && optionalText(item.role) && rect !== void 0 && finiteNumber(rect.x) && finiteNumber(rect.y) && finiteNumber(rect.width) && finiteNumber(rect.height);
1683
+ return item !== void 0 && (item.instance === void 0 || instance(item.instance)) && text(item.tagName) && text(item.selector) && text(item.sanitizedHtml) && optionalText(item.textPreview) && optionalText(item.role) && rect !== void 0 && finiteNumber(rect.x) && finiteNumber(rect.y) && finiteNumber(rect.width) && finiteNumber(rect.height);
1684
+ }
1685
+ function instance(value) {
1686
+ const item = record(value);
1687
+ return item !== void 0 && Object.keys(item).length === 5 && text(item.id) && item.id.length <= ELEMENT_INSTANCE_LIMITS3.idCharacters && /^[A-Za-z0-9_-]+$/u.test(item.id) && ELEMENT_INSTANCE_STATES.some((state) => state === item.state) && (item.selectorStatus === "unique-at-capture" || item.selectorStatus === "unavailable") && text(item.label) && item.label.length <= ELEMENT_INSTANCE_LIMITS3.labelCharacters && text(item.context) && item.context.length <= ELEMENT_INSTANCE_LIMITS3.contextCharacters;
1585
1688
  }
1586
1689
  function styles(value) {
1587
1690
  const item = record(value);
@@ -1662,14 +1765,14 @@ function createSelectionSession(window, sessionId, maximumTargets) {
1662
1765
  // src/source/source-resolver.ts
1663
1766
  import {
1664
1767
  parseSourceMarker,
1665
- SOURCE_MARKER_ATTRIBUTE
1768
+ SOURCE_MARKER_ATTRIBUTE as SOURCE_MARKER_ATTRIBUTE2
1666
1769
  } from "@spotpatch/shared";
1667
1770
  var EMPTY_REACT_CONTEXT = Object.freeze({
1668
1771
  supported: false,
1669
1772
  componentStack: Object.freeze([])
1670
1773
  });
1671
1774
  function directMarker(element2) {
1672
- return parseSourceMarker(element2.getAttribute(SOURCE_MARKER_ATTRIBUTE));
1775
+ return parseSourceMarker(element2.getAttribute(SOURCE_MARKER_ATTRIBUTE2));
1673
1776
  }
1674
1777
  function ancestorMarker(element2) {
1675
1778
  let ancestor = element2.parentElement;
@@ -1762,7 +1865,7 @@ var AGENT_PANEL_STYLES = `
1762
1865
  overflow: hidden;
1763
1866
  border: 1px solid var(--spotpatch-border-subtle);
1764
1867
  border-radius: var(--spotpatch-radius-card);
1765
- background: rgb(255 255 255 / 2.5%);
1868
+ background: var(--spotpatch-bg-raised);
1766
1869
  }
1767
1870
  .spotpatch-agent-head {
1768
1871
  display: flex;
@@ -1770,17 +1873,17 @@ var AGENT_PANEL_STYLES = `
1770
1873
  justify-content: space-between;
1771
1874
  gap: 10px;
1772
1875
  padding: 10px 12px;
1773
- border-bottom: 1px solid rgb(255 255 255 / 7%);
1876
+ border-bottom: 1px solid var(--spotpatch-border-subtle);
1774
1877
  }
1775
- .spotpatch-agent-title { color: #eef0f5; font-size: 13px; font-weight: 650; }
1878
+ .spotpatch-agent-title { font-size: 13px; font-weight: 620; }
1776
1879
  .spotpatch-agent-badge {
1777
- border: 1px solid rgb(129 112 247 / 24%);
1778
- border-radius: 999px;
1779
- padding: 3px 7px;
1780
- color: #c5cafa;
1781
- background: rgb(109 93 246 / 9%);
1782
- font-size: 10px;
1783
- font-weight: 650;
1880
+ border: 1px solid var(--spotpatch-accent-line);
1881
+ border-radius: var(--spotpatch-radius-pill);
1882
+ padding: 2px 8px;
1883
+ color: var(--spotpatch-accent-soft);
1884
+ background: var(--spotpatch-accent-tint);
1885
+ font-size: 11px;
1886
+ font-weight: 600;
1784
1887
  }
1785
1888
  .spotpatch-agent-setup { padding: 12px; }
1786
1889
  .spotpatch-agent-selectors { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
@@ -1790,71 +1893,71 @@ var AGENT_PANEL_STYLES = `
1790
1893
  box-sizing: border-box;
1791
1894
  width: 100%;
1792
1895
  min-width: 0;
1793
- min-height: 34px;
1896
+ min-height: 36px;
1794
1897
  margin-top: 4px;
1795
1898
  border: 1px solid var(--spotpatch-border);
1796
- border-radius: 8px;
1797
- padding: 7px 32px 7px 9px;
1798
- color: #e8ebf2;
1799
- color-scheme: dark;
1800
- background: linear-gradient(180deg, rgb(255 255 255 / 4%), rgb(255 255 255 / 2%));
1801
- font-size: 12px;
1899
+ border-radius: var(--spotpatch-radius-sm);
1900
+ padding: 7px 32px 7px 10px;
1901
+ color: var(--spotpatch-text);
1902
+ background: var(--spotpatch-bg-input);
1903
+ font-size: 12.5px;
1802
1904
  font-weight: 560;
1803
1905
  line-height: 1.35;
1804
1906
  outline: none;
1805
- transition: 160ms ease;
1907
+ transition:
1908
+ border-color var(--spotpatch-duration-fast) ease,
1909
+ box-shadow var(--spotpatch-duration-fast) ease;
1806
1910
  }
1807
- .spotpatch-agent select:hover { border-color: rgb(148 163 184 / 38%); background-color: rgb(255 255 255 / 5%); }
1808
- .spotpatch-agent select:focus-visible { border-color: rgb(139 124 247 / 76%); box-shadow: 0 0 0 3px rgb(109 93 246 / 18%); }
1809
- .spotpatch-agent select option { min-height: 42px; color: #e8ebf2; background: #10151e; }
1911
+ .spotpatch-agent select:hover { border-color: var(--spotpatch-border-strong); }
1912
+ .spotpatch-agent select:focus-visible { border-color: var(--spotpatch-accent-line); box-shadow: 0 0 0 3px var(--spotpatch-accent-tint); }
1913
+ .spotpatch-agent select option { min-height: 42px; color: var(--spotpatch-text); background: var(--spotpatch-bg-raised); }
1810
1914
  @supports (appearance: base-select) {
1811
1915
  .spotpatch-agent select,
1812
1916
  ::picker(select) { appearance: base-select; }
1813
- .spotpatch-agent select { padding: 7px 9px; }
1917
+ .spotpatch-agent select { padding: 7px 10px; }
1814
1918
  .spotpatch-agent select::picker-icon {
1815
- color: #9da6b5;
1816
- transition: rotate 180ms cubic-bezier(.2, .8, .2, 1);
1919
+ color: var(--spotpatch-text-muted);
1920
+ transition: rotate var(--spotpatch-duration-base) var(--spotpatch-ease-out);
1817
1921
  }
1818
- .spotpatch-agent select:open::picker-icon { rotate: 180deg; color: #c4b5fd; }
1922
+ .spotpatch-agent select:open::picker-icon { rotate: 180deg; color: var(--spotpatch-accent-soft); }
1819
1923
  .spotpatch-agent select::picker(select) {
1820
- top: calc(anchor(bottom) + 7px);
1924
+ top: calc(anchor(bottom) + 6px);
1821
1925
  left: anchor(left);
1822
1926
  width: anchor-size(width);
1823
1927
  max-height: min(280px, 40vh);
1824
1928
  margin: 0;
1825
1929
  overflow: auto;
1826
- border: 1px solid rgb(139 124 247 / 28%);
1827
- border-radius: 12px;
1828
- padding: 6px;
1829
- background: #10151e;
1830
- box-shadow: 0 18px 50px rgb(0 0 0 / 38%);
1930
+ border: 1px solid var(--spotpatch-border);
1931
+ border-radius: var(--spotpatch-radius-card);
1932
+ padding: 5px;
1933
+ background: var(--spotpatch-bg-raised);
1934
+ box-shadow: var(--spotpatch-shadow-panel);
1831
1935
  opacity: 1;
1832
1936
  transform: translateY(0) scale(1);
1833
- transition: opacity 150ms ease, transform 180ms cubic-bezier(.2, .8, .2, 1), overlay 180ms allow-discrete, display 180ms allow-discrete;
1937
+ transition:
1938
+ opacity var(--spotpatch-duration-fast) ease,
1939
+ transform var(--spotpatch-duration-base) var(--spotpatch-ease-out),
1940
+ overlay var(--spotpatch-duration-base) allow-discrete,
1941
+ display var(--spotpatch-duration-base) allow-discrete;
1834
1942
  }
1835
1943
  @starting-style {
1836
1944
  .spotpatch-agent select::picker(select):popover-open {
1837
1945
  opacity: 0;
1838
- transform: translateY(-5px) scale(.985);
1946
+ transform: translateY(-5px) scale(.98);
1839
1947
  }
1840
1948
  }
1841
1949
  .spotpatch-agent select option {
1842
- min-height: 42px;
1843
- border-radius: 8px;
1844
- padding: 10px 12px;
1845
- color: #dfe4ee;
1950
+ min-height: 40px;
1951
+ border-radius: var(--spotpatch-radius-sm);
1952
+ padding: 9px 11px;
1953
+ color: var(--spotpatch-text-secondary);
1846
1954
  background: transparent;
1847
1955
  cursor: pointer;
1848
1956
  }
1849
1957
  .spotpatch-agent select option:hover,
1850
- .spotpatch-agent select option:focus { color: #fff; background: rgb(109 93 246 / 16%); }
1851
- .spotpatch-agent select option:checked { color: #fff; background: rgb(109 93 246 / 24%); }
1852
- .spotpatch-agent select option::checkmark { color: #8b7cf7; }
1853
- }
1854
- .spotpatch-agent select:focus-visible,
1855
- .spotpatch-consent input:focus-visible {
1856
- outline: 2px solid #8b7cf7;
1857
- outline-offset: 2px;
1958
+ .spotpatch-agent select option:focus { color: var(--spotpatch-text); background: var(--spotpatch-hover); }
1959
+ .spotpatch-agent select option:checked { color: var(--spotpatch-text); background: var(--spotpatch-accent-tint); }
1960
+ .spotpatch-agent select option::checkmark { color: var(--spotpatch-accent-soft); }
1858
1961
  }
1859
1962
  .spotpatch-consent {
1860
1963
  display: flex;
@@ -1863,26 +1966,21 @@ var AGENT_PANEL_STYLES = `
1863
1966
  margin-top: 10px;
1864
1967
  color: var(--spotpatch-text-secondary);
1865
1968
  cursor: pointer;
1866
- font-size: 11.5px;
1969
+ font-size: 12px;
1867
1970
  line-height: 1.5;
1868
1971
  }
1869
1972
  .spotpatch-consent input { width: 14px; height: 14px; flex: none; margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1870
- .spotpatch-trusted-consent {
1871
- border: 1px solid rgb(251 191 36 / 20%);
1872
- border-radius: 10px;
1873
- padding: 9px 10px;
1874
- color: #e5e7eb;
1875
- background: rgb(120 53 15 / 10%);
1876
- }
1973
+ .spotpatch-trusted-consent,
1877
1974
  .spotpatch-workspace-consent {
1878
- margin-top: 9px;
1879
- border: 1px solid rgb(251 191 36 / 18%);
1880
- border-radius: 10px;
1975
+ border: 1px solid var(--spotpatch-warning-line);
1976
+ border-radius: var(--spotpatch-radius-md);
1881
1977
  padding: 9px 10px;
1882
- background: rgb(120 53 15 / 9%);
1978
+ background: var(--spotpatch-warning-tint);
1883
1979
  }
1884
- .spotpatch-workspace-consent strong { display: block; color: #fde68a; font-size: 11.5px; font-weight: 620; }
1885
- .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: #aeb6c4; font-size: 10.5px; line-height: 1.45; }
1980
+ .spotpatch-trusted-consent { color: var(--spotpatch-text); }
1981
+ .spotpatch-workspace-consent { margin-top: 9px; }
1982
+ .spotpatch-workspace-consent strong { display: block; color: var(--spotpatch-warning-text); font-size: 12px; font-weight: 600; }
1983
+ .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: var(--spotpatch-text-secondary); font-size: 11px; line-height: 1.45; }
1886
1984
  .spotpatch-agent-health-list { display: grid; gap: 6px; margin-top: 10px; }
1887
1985
  .spotpatch-agent-workspace,
1888
1986
  .spotpatch-agent-capability {
@@ -1890,83 +1988,85 @@ var AGENT_PANEL_STYLES = `
1890
1988
  align-items: center;
1891
1989
  gap: 8px;
1892
1990
  margin: 0;
1893
- color: #929bab;
1894
- font-size: 11px;
1991
+ color: var(--spotpatch-text-secondary);
1992
+ font-size: 11.5px;
1895
1993
  }
1994
+ .spotpatch-agent-workspace::before,
1896
1995
  .spotpatch-agent-capability::before {
1897
1996
  width: 6px;
1898
1997
  height: 6px;
1899
1998
  flex: none;
1900
- border-radius: 999px;
1901
- background: #64748b;
1902
- content: "";
1903
- }
1904
- .spotpatch-agent-capability[data-state="probing"]::before { background: var(--spotpatch-warning); }
1905
- .spotpatch-agent-capability[data-state="ready"] { color: #a7f3d0; }
1906
- .spotpatch-agent-capability[data-state="ready"]::before { background: var(--spotpatch-success); }
1907
- .spotpatch-agent-capability[data-state="error"] { color: #fecaca; }
1908
- .spotpatch-agent-capability[data-state="error"]::before { background: var(--spotpatch-danger); }
1909
- .spotpatch-agent-workspace::before {
1910
- width: 6px;
1911
- height: 6px;
1912
- flex: none;
1913
- border-radius: 999px;
1914
- background: #64748b;
1999
+ border-radius: 50%;
2000
+ background: var(--spotpatch-text-muted);
1915
2001
  content: "";
1916
2002
  }
2003
+ .spotpatch-agent-capability[data-state="probing"]::before,
1917
2004
  .spotpatch-agent-workspace[data-state="checking"]::before { background: var(--spotpatch-warning); }
1918
- .spotpatch-agent-workspace[data-state="ready"] { color: #a7f3d0; }
2005
+ .spotpatch-agent-capability[data-state="ready"],
2006
+ .spotpatch-agent-workspace[data-state="ready"] { color: var(--spotpatch-success-text); }
2007
+ .spotpatch-agent-capability[data-state="ready"]::before,
1919
2008
  .spotpatch-agent-workspace[data-state="ready"]::before { background: var(--spotpatch-success); }
1920
- .spotpatch-agent-workspace[data-state="consent-required"] { color: #fde68a; }
1921
- .spotpatch-agent-workspace[data-state="consent-required"]::before { background: #fbbf24; }
1922
- .spotpatch-agent-workspace[data-state="blocked"] { color: #fecaca; }
2009
+ .spotpatch-agent-workspace[data-state="consent-required"] { color: var(--spotpatch-warning-text); }
2010
+ .spotpatch-agent-workspace[data-state="consent-required"]::before { background: var(--spotpatch-warning); }
2011
+ .spotpatch-agent-capability[data-state="error"],
2012
+ .spotpatch-agent-workspace[data-state="blocked"] { color: var(--spotpatch-danger-text); }
2013
+ .spotpatch-agent-capability[data-state="error"]::before,
1923
2014
  .spotpatch-agent-workspace[data-state="blocked"]::before { background: var(--spotpatch-danger); }
1924
2015
  .spotpatch-agent-job { padding: 12px; }
1925
2016
  .spotpatch-agent-job-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
1926
- .spotpatch-agent-model { min-width: 0; overflow: hidden; color: #d6dafe; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
2017
+ .spotpatch-agent-model { min-width: 0; overflow: hidden; font-size: 13px; font-weight: 580; text-overflow: ellipsis; white-space: nowrap; }
1927
2018
  .spotpatch-agent-status {
1928
2019
  flex: none;
1929
- border-radius: 999px;
1930
- padding: 3px 7px;
1931
- color: #bae6fd;
1932
- background: rgb(14 116 144 / 20%);
2020
+ border-radius: var(--spotpatch-radius-pill);
2021
+ padding: 3px 8px;
2022
+ color: var(--spotpatch-cyan-text);
2023
+ background: var(--spotpatch-cyan-tint);
1933
2024
  font-size: 11px;
1934
- font-weight: 650;
2025
+ font-weight: 600;
1935
2026
  }
1936
- .spotpatch-agent-phase { margin: 12px 0 0; color: #e2e5ec; font-size: 13px; line-height: 1.55; }
2027
+ .spotpatch-agent-phase { margin: 12px 0 0; font-size: 13px; line-height: 1.55; }
1937
2028
  .spotpatch-agent-error {
1938
2029
  margin: 9px 0 0;
1939
- border: 1px solid rgb(251 113 133 / 22%);
1940
- border-radius: 9px;
2030
+ border: 1px solid var(--spotpatch-danger-line);
2031
+ border-radius: var(--spotpatch-radius-md);
1941
2032
  padding: 9px 10px;
1942
- color: #fecaca;
1943
- background: rgb(127 29 29 / 14%);
2033
+ color: var(--spotpatch-danger-text);
2034
+ background: var(--spotpatch-danger-tint);
1944
2035
  font-size: 12px;
1945
2036
  }
1946
2037
  .spotpatch-agent-activity { display: grid; gap: 5px; margin: 9px 0 0; padding: 0; list-style: none; }
1947
- .spotpatch-agent-activity li { display: flex; gap: 8px; color: #929bab; font: 11.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; }
1948
- .spotpatch-agent-activity li::before { color: #64748b; content: "•"; }
1949
- .spotpatch-agent-activity li[data-state="active"]::before { color: #fbbf24; }
1950
- .spotpatch-agent-activity li[data-state="success"]::before { color: #34d399; }
1951
- .spotpatch-agent-activity li[data-state="failure"]::before { color: #fb7185; }
1952
- .spotpatch-agent-result { margin-top: 11px; border-top: 1px solid rgb(148 163 184 / 10%); padding-top: 10px; }
1953
- .spotpatch-agent-summary { margin: 0; color: #dce1eb; font-size: 13px; line-height: 1.6; white-space: pre-wrap; }
2038
+ .spotpatch-agent-activity li { display: flex; gap: 8px; color: var(--spotpatch-text-secondary); font: 11.5px/1.55 var(--spotpatch-font-mono); }
2039
+ .spotpatch-agent-activity li::before { color: var(--spotpatch-text-muted); content: "•"; }
2040
+ .spotpatch-agent-activity li[data-state="active"]::before { color: var(--spotpatch-warning); }
2041
+ .spotpatch-agent-activity li[data-state="success"]::before { color: var(--spotpatch-success); }
2042
+ .spotpatch-agent-activity li[data-state="failure"]::before { color: var(--spotpatch-danger); }
2043
+ .spotpatch-agent-result { margin-top: 12px; border-top: 1px solid var(--spotpatch-border-subtle); padding-top: 10px; }
2044
+ .spotpatch-agent-summary { margin: 0; font-size: 13px; line-height: 1.6; white-space: pre-wrap; }
1954
2045
  .spotpatch-agent-files { display: grid; gap: 4px; margin: 9px 0 0; padding: 0; list-style: none; }
1955
- .spotpatch-agent-files li { color: #bbc2f7; font: 11.5px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; overflow-wrap: anywhere; }
2046
+ .spotpatch-agent-files li { color: var(--spotpatch-accent-soft); font: 11.5px/1.5 var(--spotpatch-font-mono); overflow-wrap: anywhere; }
1956
2047
  .spotpatch-agent-checks { display: grid; gap: 5px; margin-top: 9px; }
1957
- .spotpatch-agent-checks details { border: 1px solid rgb(148 163 184 / 12%); border-radius: 8px; background: rgb(2 6 23 / 30%); }
1958
- .spotpatch-agent-checks summary { padding: 9px 10px; color: #cbd0db; cursor: pointer; font-size: 11.5px; }
1959
- .spotpatch-agent-checks pre { max-height: 140px; margin: 0; overflow: auto; border-top: 1px solid rgb(255 255 255 / 7%); padding: 10px; color: #929bab; font: 11px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre-wrap; }
2048
+ .spotpatch-agent-checks details { border: 1px solid var(--spotpatch-border-subtle); border-radius: var(--spotpatch-radius-sm); background: var(--spotpatch-bg-input); }
2049
+ .spotpatch-agent-checks summary { padding: 9px 10px; color: var(--spotpatch-text-secondary); cursor: pointer; font-size: 12px; }
2050
+ .spotpatch-agent-checks pre {
2051
+ max-height: 140px;
2052
+ margin: 0;
2053
+ overflow: auto;
2054
+ border-top: 1px solid var(--spotpatch-border-subtle);
2055
+ padding: 10px;
2056
+ color: var(--spotpatch-text-secondary);
2057
+ font: 11px/1.55 var(--spotpatch-font-mono);
2058
+ white-space: pre-wrap;
2059
+ }
1960
2060
  .spotpatch-agent-diff {
1961
2061
  max-height: 300px;
1962
2062
  margin: 9px 0 0;
1963
2063
  overflow: auto;
1964
- border: 1px solid rgb(129 140 248 / 18%);
1965
- border-radius: 9px;
1966
- padding: 8px;
1967
- color: #cbd5e1;
1968
- background: rgb(2 6 23 / 66%);
1969
- font: 11.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
2064
+ border: 1px solid var(--spotpatch-border-subtle);
2065
+ border-radius: var(--spotpatch-radius-md);
2066
+ padding: 8px 10px;
2067
+ color: var(--spotpatch-text);
2068
+ background: var(--spotpatch-bg-input);
2069
+ font: 11.5px/1.6 var(--spotpatch-font-mono);
1970
2070
  overflow-wrap: normal;
1971
2071
  white-space: pre;
1972
2072
  user-select: text;
@@ -2626,6 +2726,13 @@ var ERROR_MESSAGES_ZH = Object.freeze({
2626
2726
  [ERROR_CODES2.INTERNAL_ERROR]: "Agent 失败,未暴露私有细节。"
2627
2727
  });
2628
2728
  var SUMMARY_MESSAGES_EN = Object.freeze({
2729
+ instance: "Selected instance",
2730
+ instanceStates: {
2731
+ captured: "Captured element; locator describes selection time",
2732
+ changed: "Page content changed; snapshot preserved. Select again to confirm the current instance",
2733
+ detached: "Element unloaded; historical snapshot preserved",
2734
+ restored: "Restored snapshot; not bound to the current page"
2735
+ },
2629
2736
  adapter: "React adapter",
2630
2737
  api: "API",
2631
2738
  apiStatuses: Object.freeze({
@@ -2666,6 +2773,13 @@ var SUMMARY_MESSAGES_EN = Object.freeze({
2666
2773
  warning: "Warning"
2667
2774
  });
2668
2775
  var SUMMARY_MESSAGES_ZH = Object.freeze({
2776
+ instance: "选中实例",
2777
+ instanceStates: {
2778
+ captured: "已捕获元素;定位信息对应选择时刻",
2779
+ changed: "页面内容已变化,已保留快照;请重新选择以确认当前实例",
2780
+ detached: "元素已卸载,已保留历史快照",
2781
+ restored: "已恢复历史快照,未绑定当前页面"
2782
+ },
2669
2783
  adapter: "React 适配器",
2670
2784
  api: "API",
2671
2785
  apiStatuses: Object.freeze({
@@ -2850,13 +2964,13 @@ var UI_MESSAGES = Object.freeze({
2850
2964
  addCancelled: "Additional selection cancelled.",
2851
2965
  selectionLimit: (maximum) => `The selection limit of ${String(maximum)} elements has been reached.`,
2852
2966
  chooseAdditional: (selected, maximum) => `Choose another element. ${String(selected)} of ${String(maximum)} selected.`,
2853
- duplicate: "That source target is already selected.",
2967
+ duplicate: "That page element is already selected.",
2968
+ instanceBudgetExceeded: "The prompt cannot preserve every selected instance within its budget. Remove some targets or increase the context budget.",
2854
2969
  sourceProbable: "A probable React component was found without an authorized file token.",
2855
2970
  sourceMissing: "No authorized source marker was found for the selected element.",
2856
2971
  noSelectable: "No selectable element was found.",
2857
2972
  allTargetsRemoved: "All targets were removed. Choose an element to continue.",
2858
2973
  targetRemoved: "Selected target removed.",
2859
- detachedTargetPreserved: "The page element was unloaded; its collected context remains selected.",
2860
2974
  contextWarning: "Browser context collection completed with a warning.",
2861
2975
  contextCollected: "Browser context collected.",
2862
2976
  editorOpening: "Opening source…",
@@ -3014,13 +3128,13 @@ var UI_MESSAGES = Object.freeze({
3014
3128
  addCancelled: "已取消追加选择。",
3015
3129
  selectionLimit: (maximum) => `已达到 ${String(maximum)} 个元素的选择上限。`,
3016
3130
  chooseAdditional: (selected, maximum) => `请选择另一个元素,当前已选 ${String(selected)} / ${String(maximum)}。`,
3017
- duplicate: "该源码目标已经在当前选择中。",
3131
+ duplicate: "该页面元素已经在当前选择中。",
3132
+ instanceBudgetExceeded: "上下文预算无法完整保留所有选中实例,请减少目标数量或提高上下文预算。",
3018
3133
  sourceProbable: "找到可能的 React 组件,但没有获授权的源码标记。",
3019
3134
  sourceMissing: "选中元素没有获授权的源码标记。",
3020
3135
  noSelectable: "当前位置没有可选择的元素。",
3021
3136
  allTargetsRemoved: "已移除全部目标,请重新选择元素。",
3022
3137
  targetRemoved: "已移除选中目标。",
3023
- detachedTargetPreserved: "页面元素已卸载,已采集的上下文仍保留在选择中。",
3024
3138
  contextWarning: "浏览器上下文采集完成,但存在警告。",
3025
3139
  contextCollected: "浏览器上下文采集完成。",
3026
3140
  editorOpening: "正在打开源码……",
@@ -3583,67 +3697,102 @@ function resolveStyleNonce(document) {
3583
3697
  function createStyles(document) {
3584
3698
  const style = document.createElement("style");
3585
3699
  style.textContent = `
3586
- :host {
3587
- all: initial;
3588
- --spotpatch-bg: #0e0e12;
3589
- --spotpatch-bg-raised: #17171c;
3590
- --spotpatch-bg-active: #1c1c22;
3591
- --spotpatch-bg-input: #0b0b0f;
3592
- --spotpatch-border: #2a2a32;
3593
- --spotpatch-border-subtle: #232329;
3594
- --spotpatch-text: #f2f2f5;
3595
- --spotpatch-text-secondary: #96969f;
3596
- --spotpatch-text-muted: #686872;
3597
- --spotpatch-accent: #8b7bff;
3598
- --spotpatch-accent-strong: #6a5ce8;
3599
- --spotpatch-accent-cyan: #52a8ff;
3600
- --spotpatch-danger: #fb7185;
3601
- --spotpatch-success: #34d399;
3602
- --spotpatch-warning: #f59e0b;
3603
- --spotpatch-text-on-accent: #0b0b12;
3604
- --spotpatch-radius-card: 10px;
3605
- color: var(--spotpatch-text);
3606
- font-family: Inter, "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
3607
- font-size: 14px;
3608
- line-height: 1.5;
3609
- }
3610
- [hidden] { display: none !important; }
3611
- button, textarea, select { font: inherit; }
3700
+ ${THEME_STYLES}
3612
3701
  .spotpatch-floating-surface {
3613
3702
  position: fixed;
3614
3703
  right: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3615
3704
  bottom: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3616
3705
  z-index: ${String(UI_Z_INDEX.controls)};
3706
+ box-sizing: border-box;
3707
+ overflow: hidden;
3617
3708
  isolation: isolate;
3709
+ border: 1px solid var(--spotpatch-border);
3710
+ background: var(--spotpatch-surface-fill);
3711
+ box-shadow: var(--spotpatch-shadow-float), var(--spotpatch-shadow-inset);
3712
+ transform-origin: 100% 100%;
3618
3713
  }
3619
- .spotpatch-highlight {
3714
+ .spotpatch-floating-surface[data-scene="pill"],
3715
+ .spotpatch-floating-surface[data-scene="capturing"] {
3716
+ width: max-content;
3717
+ border-radius: var(--spotpatch-radius-pill);
3718
+ }
3719
+ .spotpatch-floating-surface[data-scene="planner"] {
3720
+ border-radius: var(--spotpatch-radius-panel);
3721
+ box-shadow: var(--spotpatch-shadow-panel), var(--spotpatch-shadow-inset);
3722
+ }
3723
+
3724
+ .spotpatch-trigger {
3725
+ display: inline-flex;
3726
+ min-height: 44px;
3727
+ align-items: center;
3728
+ gap: 10px;
3729
+ border: 0;
3730
+ border-radius: var(--spotpatch-radius-pill);
3731
+ padding: 0 18px 0 16px;
3732
+ color: var(--spotpatch-text);
3733
+ background: transparent;
3734
+ cursor: pointer;
3735
+ font-size: 13.5px;
3736
+ font-weight: 620;
3737
+ letter-spacing: -.005em;
3738
+ touch-action: none;
3739
+ user-select: none;
3740
+ transition: background var(--spotpatch-duration-fast) ease;
3741
+ }
3742
+ .spotpatch-trigger::before {
3743
+ width: 8px;
3744
+ height: 8px;
3745
+ border-radius: 50%;
3746
+ background: var(--spotpatch-accent);
3747
+ box-shadow: 0 0 0 4px var(--spotpatch-accent-tint);
3748
+ content: "";
3749
+ transition: background var(--spotpatch-duration-base) ease;
3750
+ }
3751
+ .spotpatch-trigger:hover { background: var(--spotpatch-hover); }
3752
+ .spotpatch-trigger[aria-pressed="true"]::before {
3753
+ background: var(--spotpatch-accent-cyan);
3754
+ animation: spotpatch-trigger-pulse 1.6s var(--spotpatch-ease-standard) infinite;
3755
+ }
3756
+ .spotpatch-trigger[data-dragging="true"] { cursor: grabbing; }
3757
+ @keyframes spotpatch-trigger-pulse {
3758
+ 0% { box-shadow: 0 0 0 0 var(--spotpatch-cyan-line); }
3759
+ 100% { box-shadow: 0 0 0 9px transparent; }
3760
+ }
3761
+
3762
+ .spotpatch-highlight,
3763
+ .spotpatch-selection-highlight {
3620
3764
  position: fixed;
3621
3765
  top: 0;
3622
3766
  left: 0;
3623
- z-index: ${String(UI_Z_INDEX.highlight)};
3624
3767
  box-sizing: border-box;
3625
- border: 2px solid var(--spotpatch-accent);
3626
- border-radius: 6px;
3627
- background: rgb(109 93 246 / 8%);
3628
- box-shadow: inset 0 0 0 1px rgb(255 255 255 / 22%), 0 0 0 1px rgb(109 93 246 / 10%);
3768
+ border-radius: var(--spotpatch-radius-xs);
3629
3769
  pointer-events: none;
3630
3770
  }
3631
- .spotpatch-highlight-label {
3771
+ .spotpatch-highlight {
3772
+ z-index: ${String(UI_Z_INDEX.highlight)};
3773
+ border: 1.5px solid var(--spotpatch-accent);
3774
+ background: var(--spotpatch-accent-tint);
3775
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--spotpatch-accent) 12%, transparent);
3776
+ transition:
3777
+ transform var(--spotpatch-duration-fast) var(--spotpatch-ease-out),
3778
+ width var(--spotpatch-duration-fast) var(--spotpatch-ease-out),
3779
+ height var(--spotpatch-duration-fast) var(--spotpatch-ease-out);
3780
+ }
3781
+ .spotpatch-highlight-label,
3782
+ .spotpatch-selection-highlight > span {
3632
3783
  position: absolute;
3633
- top: -30px;
3634
- left: -2px;
3635
- max-width: min(380px, 80vw);
3784
+ bottom: calc(100% + 6px);
3785
+ left: -1.5px;
3636
3786
  overflow: hidden;
3637
- border: 1px solid rgb(255 255 255 / 14%);
3638
- border-radius: 8px 8px 8px 3px;
3639
- padding: 6px 10px;
3640
- color: #fff;
3641
- background: #5546dc;
3642
- box-shadow: 0 8px 20px rgb(49 46 129 / 22%);
3643
- font: 650 12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3787
+ border-radius: var(--spotpatch-radius-xs);
3788
+ padding: 4px 8px;
3789
+ color: var(--spotpatch-text-on-accent);
3790
+ background: var(--spotpatch-accent-strong);
3791
+ font: 600 11px/1.3 var(--spotpatch-font-mono);
3644
3792
  text-overflow: ellipsis;
3645
3793
  white-space: nowrap;
3646
3794
  }
3795
+ .spotpatch-highlight-label { max-width: min(380px, 80vw); }
3647
3796
  .spotpatch-selection-highlights {
3648
3797
  position: fixed;
3649
3798
  inset: 0;
@@ -3651,37 +3800,27 @@ function createStyles(document) {
3651
3800
  pointer-events: none;
3652
3801
  }
3653
3802
  .spotpatch-selection-highlight {
3654
- position: fixed;
3655
- top: 0;
3656
- left: 0;
3657
- box-sizing: border-box;
3658
- border: 2px solid var(--spotpatch-accent-cyan);
3659
- border-radius: 6px;
3660
- background: rgb(8 182 214 / 5%);
3661
- box-shadow: inset 0 0 0 1px rgb(255 255 255 / 16%);
3803
+ border: 1.5px solid var(--spotpatch-accent-cyan);
3804
+ background: var(--spotpatch-cyan-tint);
3662
3805
  }
3663
3806
  .spotpatch-selection-highlight[data-active="true"] {
3664
- border-color: #7768f7;
3665
- background: rgb(109 93 246 / 8%);
3666
- box-shadow: inset 0 0 0 1px rgb(255 255 255 / 20%);
3807
+ border-color: var(--spotpatch-accent);
3808
+ background: var(--spotpatch-accent-tint);
3809
+ box-shadow: 0 0 0 4px color-mix(in srgb, var(--spotpatch-accent) 12%, transparent);
3810
+ }
3811
+ .spotpatch-selection-highlight[data-entering="true"] {
3812
+ animation: spotpatch-pop var(--spotpatch-duration-base) var(--spotpatch-ease-spring) both;
3667
3813
  }
3668
3814
  .spotpatch-selection-highlight > span {
3669
- position: absolute;
3670
- top: -24px;
3671
- left: -2px;
3672
3815
  max-width: min(300px, 70vw);
3673
- overflow: hidden;
3674
- border-radius: 7px 7px 7px 2px;
3675
- padding: 4px 8px;
3676
- color: #ecfeff;
3677
- background: rgb(14 67 82 / 96%);
3678
- font: 650 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3679
- text-overflow: ellipsis;
3680
- white-space: nowrap;
3816
+ color: var(--spotpatch-bg);
3817
+ background: var(--spotpatch-accent-cyan);
3681
3818
  }
3682
3819
  .spotpatch-selection-highlight[data-active="true"] > span {
3683
- background: #5546dc;
3820
+ color: var(--spotpatch-text-on-accent);
3821
+ background: var(--spotpatch-accent-strong);
3684
3822
  }
3823
+
3685
3824
  .spotpatch-dialog {
3686
3825
  position: relative;
3687
3826
  box-sizing: border-box;
@@ -3689,18 +3828,14 @@ function createStyles(document) {
3689
3828
  outline: none;
3690
3829
  }
3691
3830
  .spotpatch-shell {
3692
- position: relative;
3693
- z-index: 1;
3694
3831
  display: flex;
3695
3832
  box-sizing: border-box;
3696
3833
  max-height: min(${String(FLOATING_SURFACE_LAYOUT.workbenchMaxHeight)}px, calc(100vh - ${String(FLOATING_SURFACE_LAYOUT.desktopInset * 2)}px));
3697
3834
  overflow: hidden;
3698
3835
  flex-direction: column;
3699
3836
  }
3700
- .spotpatch-header {
3701
- position: relative;
3702
- padding: 0 18px 14px;
3703
- }
3837
+
3838
+ .spotpatch-header { padding: 0 18px 16px; }
3704
3839
  .spotpatch-header[data-spotpatch-drag-handle] { cursor: grab; touch-action: none; user-select: none; }
3705
3840
  .spotpatch-header[data-dragging="true"] { cursor: grabbing; }
3706
3841
  .spotpatch-header button,
@@ -3710,133 +3845,115 @@ function createStyles(document) {
3710
3845
  align-items: center;
3711
3846
  justify-content: space-between;
3712
3847
  gap: 12px;
3713
- margin: 0 -18px 18px;
3714
- padding: 14px 18px 13px;
3848
+ margin: 0 -18px 16px;
3849
+ padding: 12px 14px 12px 18px;
3715
3850
  border-bottom: 1px solid var(--spotpatch-border-subtle);
3716
3851
  }
3717
- .spotpatch-brand {
3718
- display: inline-flex;
3719
- align-items: center;
3720
- gap: 10px;
3721
- min-width: 0;
3722
- color: #f8fafc;
3723
- }
3724
- .spotpatch-brand-mark {
3725
- width: 30px;
3726
- height: 30px;
3727
- flex: none;
3728
- filter: drop-shadow(0 6px 12px rgb(76 29 149 / 22%));
3729
- }
3852
+ .spotpatch-brand { display: inline-flex; min-width: 0; align-items: center; gap: 10px; }
3853
+ .spotpatch-brand-mark { width: 28px; height: 28px; flex: none; }
3730
3854
  .spotpatch-brand-copy { display: grid; min-width: 0; gap: 1px; }
3731
- .spotpatch-header-controls {
3732
- display: inline-flex;
3733
- align-items: center;
3734
- gap: 6px;
3735
- flex: none;
3736
- }
3855
+ .spotpatch-brand-name { font-size: 13.5px; font-weight: 650; letter-spacing: -.01em; }
3856
+ .spotpatch-brand-context { color: var(--spotpatch-text-muted); font-size: 11px; font-weight: 520; }
3857
+ .spotpatch-header-controls { display: inline-flex; flex: none; align-items: center; gap: 2px; }
3737
3858
  .spotpatch-repository,
3738
3859
  .spotpatch-locale,
3739
3860
  .spotpatch-reset-position,
3740
3861
  .spotpatch-close {
3741
- height: 27px;
3742
- border: 1px solid rgb(255 255 255 / 11%);
3743
- border-radius: 7px;
3744
- background: transparent;
3745
- }
3746
- .spotpatch-repository {
3747
- display: inline-flex;
3748
- align-items: center;
3749
- padding: 0 9px;
3750
- color: var(--spotpatch-text-secondary);
3751
- font-size: 11px;
3752
- font-weight: 680;
3753
- text-decoration: none;
3754
- }
3755
- .spotpatch-locale {
3756
- min-width: 34px;
3757
- padding: 0 8px;
3758
- color: #c5cad5;
3759
- cursor: pointer;
3760
- font-size: 12px;
3761
- font-weight: 650;
3762
- }
3763
- .spotpatch-reset-position {
3764
3862
  display: inline-grid;
3765
- width: 27px;
3766
- padding: 0;
3767
- place-items: center;
3768
- color: #c5cad5;
3769
- cursor: pointer;
3770
- font-size: 15px;
3771
- line-height: 1;
3772
- }
3773
- .spotpatch-close {
3774
- display: inline-grid;
3775
- width: 27px;
3776
- padding: 0;
3863
+ box-sizing: border-box;
3864
+ min-width: 28px;
3865
+ height: 28px;
3777
3866
  place-items: center;
3778
- color: #9da5b4;
3779
- cursor: pointer;
3780
- font-size: 15px;
3867
+ border: 0;
3868
+ border-radius: var(--spotpatch-radius-sm);
3869
+ padding: 0 7px;
3870
+ color: var(--spotpatch-text-muted);
3871
+ background: transparent;
3872
+ font-size: 12px;
3873
+ font-weight: 600;
3781
3874
  line-height: 1;
3782
- transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
3875
+ text-decoration: none;
3876
+ transition:
3877
+ color var(--spotpatch-duration-fast) ease,
3878
+ background var(--spotpatch-duration-fast) ease;
3783
3879
  }
3784
- .spotpatch-close:hover,
3880
+ .spotpatch-reset-position,
3881
+ .spotpatch-close { padding: 0; font-size: 15px; }
3882
+ .spotpatch-reset-position svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-linecap: round; stroke-width: 1.5; }
3883
+ .spotpatch-repository:hover,
3785
3884
  .spotpatch-locale:hover,
3786
3885
  .spotpatch-reset-position:hover,
3787
- .spotpatch-repository:hover { border-color: rgb(129 112 247 / 45%); color: #fff; background: rgb(109 93 246 / 10%); }
3886
+ .spotpatch-close:hover { color: var(--spotpatch-text); background: var(--spotpatch-hover); }
3887
+
3888
+ .spotpatch-title {
3889
+ margin: 0;
3890
+ font-size: 17px;
3891
+ font-weight: 650;
3892
+ letter-spacing: -.02em;
3893
+ line-height: 1.3;
3894
+ }
3895
+ .spotpatch-subtitle {
3896
+ max-width: 400px;
3897
+ margin: 4px 0 0;
3898
+ color: var(--spotpatch-text-secondary);
3899
+ font-size: 12.5px;
3900
+ line-height: 1.55;
3901
+ }
3788
3902
  .spotpatch-target-row {
3789
3903
  display: flex;
3790
- align-items: center;
3791
- gap: 8px;
3792
3904
  min-width: 0;
3905
+ align-items: center;
3906
+ gap: 10px;
3793
3907
  margin-top: 14px;
3794
3908
  }
3795
3909
  .spotpatch-target-label {
3796
3910
  min-width: 0;
3797
3911
  overflow: hidden;
3798
- border: 1px solid rgb(129 112 247 / 24%);
3799
- border-radius: 999px;
3800
- padding: 4px 10px;
3801
- color: #cdd2ff;
3802
- background: rgb(109 93 246 / 10%);
3803
- font: 600 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3912
+ border: 1px solid var(--spotpatch-accent-line);
3913
+ border-radius: var(--spotpatch-radius-pill);
3914
+ padding: 3px 10px;
3915
+ color: var(--spotpatch-accent-soft);
3916
+ background: var(--spotpatch-accent-tint);
3917
+ font: 600 11px/1.4 var(--spotpatch-font-mono);
3804
3918
  text-overflow: ellipsis;
3805
3919
  white-space: nowrap;
3806
3920
  }
3807
3921
  .spotpatch-context-state {
3808
3922
  display: inline-flex;
3923
+ flex: none;
3809
3924
  align-items: center;
3810
3925
  gap: 6px;
3811
- flex: none;
3812
- color: #a5adba;
3813
- font-size: 11px;
3926
+ color: var(--spotpatch-warning-text);
3927
+ font-size: 11.5px;
3814
3928
  white-space: nowrap;
3815
3929
  }
3816
3930
  .spotpatch-context-state::before {
3817
3931
  width: 6px;
3818
3932
  height: 6px;
3819
- border-radius: 999px;
3820
- background: #f59e0b;
3933
+ border-radius: 50%;
3934
+ background: var(--spotpatch-warning);
3821
3935
  content: "";
3822
3936
  }
3937
+ .spotpatch-context-state[data-state="loading"]::before {
3938
+ animation: spotpatch-dot-breathe 1.4s ease-in-out infinite;
3939
+ }
3823
3940
  .spotpatch-context-state[data-state="ready"],
3824
- .spotpatch-editor-feedback[data-state="success"] { color: #9fe3c4; }
3941
+ .spotpatch-editor-feedback[data-state="success"] { color: var(--spotpatch-success-text); }
3825
3942
  .spotpatch-context-state[data-state="ready"]::before,
3826
- .spotpatch-editor-feedback[data-state="success"]::before { background: #34d399; }
3943
+ .spotpatch-editor-feedback[data-state="success"]::before { background: var(--spotpatch-success); }
3827
3944
  .spotpatch-context-state[data-state="warning"],
3828
- .spotpatch-editor-feedback[data-state="error"] { color: #fecaca; }
3945
+ .spotpatch-editor-feedback[data-state="error"] { color: var(--spotpatch-danger-text); }
3829
3946
  .spotpatch-context-state[data-state="warning"]::before,
3830
- .spotpatch-editor-feedback[data-state="error"]::before { background: #fb7185; }
3947
+ .spotpatch-editor-feedback[data-state="error"]::before { background: var(--spotpatch-danger); }
3948
+ @keyframes spotpatch-dot-breathe {
3949
+ 50% { opacity: .35; }
3950
+ }
3951
+
3831
3952
  .spotpatch-body {
3832
3953
  min-height: 0;
3833
3954
  overflow: auto;
3834
- padding: 0 18px 14px;
3835
- scrollbar-color: rgb(100 116 139 / 50%) transparent;
3836
- scrollbar-width: thin;
3837
- }
3838
- .spotpatch-targets {
3839
- margin: 0;
3955
+ overscroll-behavior: contain;
3956
+ padding: 0 18px 16px;
3840
3957
  }
3841
3958
  .spotpatch-targets-heading {
3842
3959
  display: flex;
@@ -3844,71 +3961,73 @@ function createStyles(document) {
3844
3961
  justify-content: space-between;
3845
3962
  gap: 10px;
3846
3963
  margin-bottom: 8px;
3847
- color: var(--spotpatch-text-secondary);
3964
+ color: var(--spotpatch-text-muted);
3848
3965
  font-size: 11px;
3849
- font-weight: 650;
3850
- letter-spacing: .04em;
3966
+ font-weight: 600;
3967
+ letter-spacing: .06em;
3851
3968
  text-transform: uppercase;
3852
3969
  }
3853
3970
  .spotpatch-targets-meta {
3854
3971
  display: flex;
3972
+ min-width: 0;
3855
3973
  align-items: center;
3856
3974
  gap: 8px;
3857
- min-width: 0;
3858
3975
  letter-spacing: 0;
3859
3976
  text-transform: none;
3860
3977
  }
3861
- .spotpatch-target-complete { color: var(--spotpatch-text-muted); font-size: 11px; font-weight: 560; }
3862
- .spotpatch-target-budget {
3863
- color: var(--spotpatch-text-muted);
3864
- font: 550 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3865
- }
3978
+ .spotpatch-target-budget { font: 500 11px/1.4 var(--spotpatch-font-mono); }
3866
3979
  .spotpatch-target-budget[data-state="ready"] { display: none; }
3867
- .spotpatch-target-budget[data-state="over"] { color: #fda4af; }
3980
+ .spotpatch-target-budget[data-state="over"] { color: var(--spotpatch-danger-text); }
3868
3981
  .spotpatch-target-count {
3869
3982
  margin-left: auto;
3870
- border-radius: 999px;
3871
- padding: 3px 8px;
3872
3983
  color: var(--spotpatch-text-muted);
3873
- font: 600 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3984
+ font: 500 11px/1.4 var(--spotpatch-font-mono);
3874
3985
  }
3875
3986
  .spotpatch-target-progress {
3876
- height: 3px;
3877
- margin-bottom: 13px;
3987
+ height: 2px;
3988
+ margin-bottom: 12px;
3878
3989
  overflow: hidden;
3879
- border-radius: 999px;
3990
+ border-radius: var(--spotpatch-radius-pill);
3880
3991
  background: var(--spotpatch-border-subtle);
3881
3992
  }
3882
3993
  .spotpatch-target-progress-fill {
3883
3994
  width: 0;
3884
3995
  height: 100%;
3885
3996
  border-radius: inherit;
3886
- background: linear-gradient(90deg, var(--spotpatch-accent), var(--spotpatch-accent-cyan));
3887
- transition: width 180ms ease;
3997
+ background: var(--spotpatch-brand-line);
3998
+ transition: width var(--spotpatch-duration-slow) var(--spotpatch-ease-out);
3888
3999
  }
3889
4000
  .spotpatch-target-list {
3890
4001
  display: grid;
3891
4002
  gap: 8px;
3892
4003
  max-height: min(340px, 48vh);
3893
4004
  overflow: auto;
3894
- padding-right: 2px;
4005
+ padding: 2px;
4006
+ margin: -2px;
3895
4007
  }
4008
+
3896
4009
  .spotpatch-target-item {
3897
4010
  overflow: hidden;
3898
4011
  border: 1px solid var(--spotpatch-border-subtle);
3899
4012
  border-radius: var(--spotpatch-radius-card);
3900
4013
  background: var(--spotpatch-bg-raised);
3901
- transition: border-color 150ms ease, background 150ms ease;
4014
+ transition:
4015
+ border-color var(--spotpatch-duration-base) ease,
4016
+ background var(--spotpatch-duration-base) ease,
4017
+ box-shadow var(--spotpatch-duration-base) ease;
3902
4018
  }
3903
4019
  .spotpatch-target-item[data-active="true"] {
3904
- border-color: rgb(139 123 255 / 34%);
4020
+ border-color: var(--spotpatch-accent-line);
3905
4021
  background: var(--spotpatch-bg-active);
3906
- box-shadow: 0 0 0 3px rgb(139 123 255 / 10%);
4022
+ box-shadow: 0 0 0 3px var(--spotpatch-accent-tint);
4023
+ }
4024
+ .spotpatch-target-item[data-entering="true"] {
4025
+ animation: spotpatch-enter var(--spotpatch-duration-slow) var(--spotpatch-ease-out) both;
3907
4026
  }
3908
4027
  .spotpatch-target-summary {
3909
4028
  display: grid;
3910
- grid-template-columns: minmax(0, 1fr) 28px 28px;
3911
- align-items: stretch;
4029
+ grid-template-columns: minmax(0, 1fr) 30px 30px;
4030
+ align-items: center;
3912
4031
  gap: 2px;
3913
4032
  padding: 4px;
3914
4033
  }
@@ -3916,27 +4035,31 @@ function createStyles(document) {
3916
4035
  display: grid;
3917
4036
  grid-template-columns: 24px minmax(0, 1fr) auto;
3918
4037
  align-items: center;
3919
- gap: 9px;
4038
+ gap: 10px;
3920
4039
  min-width: 0;
3921
4040
  border: 0;
3922
- border-radius: 8px;
3923
- padding: 8px;
4041
+ border-radius: var(--spotpatch-radius-sm);
4042
+ padding: 7px 8px;
3924
4043
  color: inherit;
3925
4044
  background: transparent;
3926
4045
  cursor: pointer;
3927
4046
  text-align: left;
4047
+ transition: background var(--spotpatch-duration-fast) ease;
3928
4048
  }
3929
- .spotpatch-target-select:hover { background: rgb(255 255 255 / 3.5%); }
4049
+ .spotpatch-target-select:hover { background: var(--spotpatch-hover); }
3930
4050
  .spotpatch-target-index {
3931
4051
  display: inline-grid;
3932
- width: 22px;
3933
- height: 22px;
4052
+ width: 24px;
4053
+ height: 24px;
3934
4054
  place-items: center;
3935
- border: 1px solid rgb(68 202 224 / 25%);
3936
- border-radius: 6px;
4055
+ border-radius: var(--spotpatch-radius-xs);
3937
4056
  color: var(--spotpatch-text-secondary);
3938
- background: var(--spotpatch-bg);
3939
- font: 650 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
4057
+ background: var(--spotpatch-hover);
4058
+ font: 600 11px/1 var(--spotpatch-font-mono);
4059
+ }
4060
+ [data-active="true"] .spotpatch-target-index {
4061
+ color: var(--spotpatch-text-on-accent);
4062
+ background: var(--spotpatch-accent-strong);
3940
4063
  }
3941
4064
  .spotpatch-target-copy { min-width: 0; }
3942
4065
  .spotpatch-target-name,
@@ -3946,37 +4069,49 @@ function createStyles(document) {
3946
4069
  text-overflow: ellipsis;
3947
4070
  white-space: nowrap;
3948
4071
  }
3949
- .spotpatch-target-name { color: #f2f2f5; font-size: 13px; font-weight: 650; }
3950
- .spotpatch-target-source { margin-top: 1px; color: var(--spotpatch-text-muted); font: 500 10.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
4072
+ .spotpatch-target-name { font-size: 13px; font-weight: 620; }
4073
+ .spotpatch-target-source {
4074
+ margin-top: 1px;
4075
+ color: var(--spotpatch-text-muted);
4076
+ font: 500 11px/1.4 var(--spotpatch-font-mono);
4077
+ }
3951
4078
  .spotpatch-target-state {
3952
- border-radius: 999px;
4079
+ border-radius: var(--spotpatch-radius-pill);
3953
4080
  padding: 3px 8px;
3954
- color: #a7f3d0;
3955
- background: rgb(16 185 129 / 9%);
3956
- font-size: 10.5px;
3957
- font-weight: 650;
4081
+ color: var(--spotpatch-success-text);
4082
+ background: var(--spotpatch-success-tint);
4083
+ font-size: 11px;
4084
+ font-weight: 600;
3958
4085
  white-space: nowrap;
3959
4086
  }
3960
- .spotpatch-target-state[data-complete="false"] { color: #f5c97a; background: rgb(245 158 11 / 9%); }
4087
+ .spotpatch-target-state[data-complete="false"] {
4088
+ color: var(--spotpatch-warning-text);
4089
+ background: var(--spotpatch-warning-tint);
4090
+ }
3961
4091
  .spotpatch-target-open,
3962
4092
  .spotpatch-target-remove {
3963
4093
  display: inline-grid;
3964
- width: 28px;
3965
- height: 100%;
3966
- min-height: 34px;
3967
- padding: 0;
4094
+ width: 30px;
4095
+ height: 30px;
3968
4096
  place-items: center;
3969
- border: 1px solid transparent;
3970
- border-radius: 7px;
3971
- color: #7f899a;
4097
+ border: 0;
4098
+ border-radius: var(--spotpatch-radius-sm);
4099
+ padding: 0;
4100
+ color: var(--spotpatch-text-muted);
3972
4101
  background: transparent;
3973
4102
  cursor: pointer;
3974
- }
3975
- .spotpatch-target-open { color: #79d9e7; }
3976
- .spotpatch-target-open:hover:not(:disabled) { border-color: rgb(68 202 224 / 30%); color: #c8f7ff; background: rgb(8 145 178 / 11%); }
3977
- .spotpatch-target-remove:hover:not(:disabled) { border-color: rgb(251 113 133 / 24%); color: #fda4af; background: rgb(127 29 29 / 12%); }
4103
+ transition:
4104
+ color var(--spotpatch-duration-fast) ease,
4105
+ background var(--spotpatch-duration-fast) ease;
4106
+ }
4107
+ .spotpatch-target-open:hover:not(:disabled) { color: var(--spotpatch-cyan-text); background: var(--spotpatch-cyan-tint); }
4108
+ .spotpatch-target-remove:hover:not(:disabled) { color: var(--spotpatch-danger-text); background: var(--spotpatch-danger-tint); }
4109
+ .spotpatch-target-open:disabled,
4110
+ .spotpatch-target-remove:disabled { cursor: not-allowed; opacity: .35; }
4111
+
3978
4112
  .spotpatch-target-editor {
3979
- padding: 0 12px 12px;
4113
+ display: block;
4114
+ padding: 2px 12px 12px;
3980
4115
  }
3981
4116
  .spotpatch-target-editor-head {
3982
4117
  display: flex;
@@ -3985,179 +4120,178 @@ function createStyles(document) {
3985
4120
  gap: 12px;
3986
4121
  margin-bottom: 6px;
3987
4122
  }
3988
- .spotpatch-target-editor-label { color: var(--spotpatch-text-secondary); font-size: 11px; font-weight: 650; }
3989
- .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 10.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }
4123
+ .spotpatch-target-editor-label { color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 600; }
4124
+ .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 11px/1.3 var(--spotpatch-font-mono); }
3990
4125
  .spotpatch-target-editor textarea {
4126
+ display: block;
3991
4127
  box-sizing: border-box;
3992
4128
  width: 100%;
3993
- min-height: 74px;
4129
+ min-height: 76px;
3994
4130
  resize: vertical;
3995
4131
  border: 1px solid var(--spotpatch-border);
3996
- border-radius: 7px;
3997
- padding: 9px 10px;
3998
- color: #f8fafc;
3999
- caret-color: #8b7cf7;
4132
+ border-radius: var(--spotpatch-radius-md);
4133
+ padding: 10px 12px;
4134
+ color: var(--spotpatch-text);
4135
+ caret-color: var(--spotpatch-accent-soft);
4000
4136
  background: var(--spotpatch-bg-input);
4001
- font-size: 12.5px;
4137
+ font-size: 13px;
4002
4138
  line-height: 1.55;
4003
4139
  outline: none;
4004
- transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease;
4140
+ transition:
4141
+ border-color var(--spotpatch-duration-fast) ease,
4142
+ box-shadow var(--spotpatch-duration-fast) ease;
4005
4143
  }
4006
- .spotpatch-target-editor textarea::placeholder { color: #687284; }
4007
- .spotpatch-target-editor textarea:hover { border-color: rgb(139 124 247 / 34%); }
4144
+ .spotpatch-target-editor textarea::placeholder { color: var(--spotpatch-text-muted); }
4145
+ .spotpatch-target-editor textarea:hover { border-color: var(--spotpatch-border-strong); }
4008
4146
  .spotpatch-target-editor textarea:focus {
4009
- border-color: rgb(139 124 247 / 68%);
4010
- background: rgb(3 7 18 / 65%);
4011
- box-shadow: 0 0 0 2px rgb(109 93 246 / 10%);
4147
+ border-color: var(--spotpatch-accent-line);
4148
+ box-shadow: 0 0 0 3px var(--spotpatch-accent-tint);
4012
4149
  }
4150
+
4013
4151
  .spotpatch-diagnostics {
4014
4152
  margin-top: 10px;
4015
4153
  overflow: hidden;
4016
- border: 1px solid rgb(255 255 255 / 8%);
4017
- border-radius: 9px;
4018
- background: rgb(255 255 255 / 2.5%);
4154
+ border: 1px solid var(--spotpatch-border-subtle);
4155
+ border-radius: var(--spotpatch-radius-md);
4019
4156
  }
4020
4157
  .spotpatch-diagnostics > summary {
4021
4158
  display: flex;
4022
4159
  align-items: center;
4023
4160
  gap: 8px;
4024
- padding: 9px 11px;
4025
- color: #bfc5d0;
4161
+ padding: 9px 12px;
4162
+ color: var(--spotpatch-text-secondary);
4026
4163
  cursor: pointer;
4027
- font-size: 11px;
4028
- font-weight: 650;
4164
+ font-size: 11.5px;
4165
+ font-weight: 600;
4029
4166
  list-style: none;
4030
4167
  user-select: none;
4031
4168
  }
4032
4169
  .spotpatch-diagnostics > summary::-webkit-details-marker { display: none; }
4033
4170
  .spotpatch-diagnostics > summary::before {
4034
- color: #778193;
4171
+ color: var(--spotpatch-text-muted);
4035
4172
  content: "›";
4036
4173
  font-size: 16px;
4037
4174
  line-height: 1;
4038
- transform: rotate(0deg);
4039
- transition: transform 150ms ease;
4175
+ transition: transform var(--spotpatch-duration-base) var(--spotpatch-ease-out);
4040
4176
  }
4041
4177
  .spotpatch-diagnostics[open] > summary::before { transform: rotate(90deg); }
4042
4178
  .spotpatch-source-peek {
4043
4179
  min-width: 0;
4044
- overflow: hidden;
4045
4180
  margin-left: auto;
4046
- color: #64748b;
4047
- font: 500 10.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace;
4181
+ overflow: hidden;
4182
+ color: var(--spotpatch-text-muted);
4183
+ font: 500 11px/1.3 var(--spotpatch-font-mono);
4048
4184
  text-overflow: ellipsis;
4049
4185
  white-space: nowrap;
4050
4186
  }
4051
- .spotpatch-summary {
4052
- max-height: 170px;
4187
+ .spotpatch-summary,
4188
+ .spotpatch-prompt {
4053
4189
  margin: 0;
4054
4190
  overflow: auto;
4055
- border-top: 1px solid rgb(255 255 255 / 7%);
4056
- padding: 10px 11px 11px;
4057
- color: #9ca5b5;
4058
- font: 10.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
4059
4191
  overflow-wrap: anywhere;
4060
4192
  white-space: pre-wrap;
4061
4193
  user-select: text;
4194
+ font: 11px/1.6 var(--spotpatch-font-mono);
4195
+ }
4196
+ .spotpatch-summary {
4197
+ max-height: 170px;
4198
+ border-top: 1px solid var(--spotpatch-border-subtle);
4199
+ padding: 10px 12px 12px;
4200
+ color: var(--spotpatch-text-secondary);
4062
4201
  }
4063
4202
  .spotpatch-prompt {
4064
4203
  max-height: min(500px, 60vh);
4065
- margin: 0;
4066
- overflow: auto;
4067
- border: 1px solid rgb(255 255 255 / 9%);
4068
- border-radius: 9px;
4069
- padding: 11px;
4070
- color: #d8ddeb;
4071
- background: rgb(3 7 18 / 55%);
4072
- font: 11px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
4073
- overflow-wrap: anywhere;
4074
- white-space: pre-wrap;
4075
- user-select: text;
4204
+ border: 1px solid var(--spotpatch-border-subtle);
4205
+ border-radius: var(--spotpatch-radius-md);
4206
+ padding: 12px;
4207
+ color: var(--spotpatch-text);
4208
+ background: var(--spotpatch-bg-input);
4076
4209
  }
4210
+
4077
4211
  .spotpatch-actions {
4078
4212
  display: grid;
4079
4213
  gap: 8px;
4080
- padding: 10px 18px 14px;
4081
- border-top: 1px solid rgb(255 255 255 / 8%);
4082
- background: rgb(8 8 12 / 86%);
4083
- }
4084
- .spotpatch-editor-feedback {
4085
- white-space: normal;
4214
+ border-top: 1px solid var(--spotpatch-border-subtle);
4215
+ padding: 12px 18px 16px;
4086
4216
  }
4217
+ .spotpatch-editor-feedback { white-space: normal; }
4087
4218
  .spotpatch-secondary-actions,
4088
- .spotpatch-primary-actions {
4089
- display: flex;
4090
- align-items: center;
4091
- gap: 7px;
4092
- }
4093
- .spotpatch-secondary-actions { min-height: 30px; }
4219
+ .spotpatch-primary-actions { display: flex; align-items: center; gap: 8px; }
4220
+ .spotpatch-secondary-actions { gap: 2px; margin: 0 -8px; }
4094
4221
  .spotpatch-actions button {
4095
4222
  display: inline-flex;
4096
- min-height: 34px;
4223
+ min-height: 36px;
4097
4224
  align-items: center;
4098
4225
  justify-content: center;
4099
- border: 1px solid rgb(255 255 255 / 10%);
4100
- border-radius: 9px;
4101
- padding: 7px 11px;
4102
- color: var(--spotpatch-text-secondary);
4103
- background: rgb(255 255 255 / 4%);
4226
+ gap: 6px;
4227
+ border: 1px solid var(--spotpatch-border);
4228
+ border-radius: var(--spotpatch-radius-md);
4229
+ padding: 0 12px;
4230
+ color: var(--spotpatch-text);
4231
+ background: var(--spotpatch-bg-raised);
4104
4232
  cursor: pointer;
4105
- font-size: 12px;
4106
- font-weight: 650;
4107
- transition: border-color 150ms ease, box-shadow 150ms ease, color 150ms ease, transform 150ms ease;
4233
+ font-size: 12.5px;
4234
+ font-weight: 600;
4235
+ white-space: nowrap;
4236
+ transition:
4237
+ border-color var(--spotpatch-duration-fast) ease,
4238
+ background var(--spotpatch-duration-fast) ease,
4239
+ box-shadow var(--spotpatch-duration-base) ease,
4240
+ transform var(--spotpatch-duration-fast) ease;
4108
4241
  }
4109
4242
  .spotpatch-actions button:hover:not(:disabled) {
4110
- border-color: rgb(139 124 247 / 45%);
4111
- color: #fff;
4112
- transform: translateY(-1px);
4243
+ border-color: var(--spotpatch-border-strong);
4244
+ background: var(--spotpatch-bg-active);
4113
4245
  }
4246
+ .spotpatch-actions button:active:not(:disabled) { transform: scale(.98); }
4114
4247
  .spotpatch-actions .spotpatch-primary {
4115
- min-width: 138px;
4248
+ min-width: 140px;
4116
4249
  flex: 1;
4117
4250
  border-color: transparent;
4118
4251
  color: var(--spotpatch-text-on-accent);
4119
- background: linear-gradient(135deg, var(--spotpatch-accent), var(--spotpatch-accent-strong));
4120
- box-shadow: 0 8px 20px -8px rgb(139 123 255 / 55%);
4252
+ background: var(--spotpatch-primary-fill);
4253
+ box-shadow: var(--spotpatch-shadow-accent), var(--spotpatch-shadow-inset);
4121
4254
  }
4122
- .spotpatch-actions .spotpatch-primary:hover:not(:disabled) { color: var(--spotpatch-text-on-accent); filter: brightness(1.08); }
4123
- .spotpatch-actions .spotpatch-primary::after { margin-left: 8px; content: "↗"; font-size: 12px; }
4255
+ .spotpatch-actions .spotpatch-primary:hover:not(:disabled) {
4256
+ border-color: transparent;
4257
+ background: var(--spotpatch-primary-fill);
4258
+ filter: brightness(1.1);
4259
+ }
4260
+ .spotpatch-actions .spotpatch-primary::after { content: "↗"; font-size: 12px; opacity: .8; }
4124
4261
  .spotpatch-actions .spotpatch-secondary-action {
4125
4262
  min-height: 30px;
4126
- border-style: dashed;
4127
- padding: 5px 10px;
4128
- color: var(--spotpatch-text-muted);
4263
+ border-color: transparent;
4264
+ padding: 0 8px;
4265
+ color: var(--spotpatch-text-secondary);
4129
4266
  background: transparent;
4130
- font-size: 11px;
4267
+ font-size: 12px;
4268
+ font-weight: 550;
4269
+ }
4270
+ .spotpatch-actions .spotpatch-secondary-action:hover:not(:disabled) {
4271
+ border-color: transparent;
4272
+ color: var(--spotpatch-text);
4273
+ background: var(--spotpatch-hover);
4131
4274
  }
4132
4275
  .spotpatch-actions .spotpatch-icon-action {
4133
- width: 34px;
4134
- min-width: 34px;
4276
+ width: 36px;
4277
+ min-width: 36px;
4135
4278
  padding: 0;
4136
4279
  font-size: 0;
4137
4280
  }
4138
4281
  .spotpatch-actions .spotpatch-icon-action::before {
4139
4282
  color: var(--spotpatch-text-secondary);
4140
4283
  content: attr(data-compact-icon);
4141
- font-size: 18px;
4284
+ font-size: 17px;
4142
4285
  font-weight: 400;
4143
4286
  line-height: 1;
4144
4287
  }
4145
- .spotpatch-actions button:disabled { cursor: not-allowed; opacity: .4; transform: none; }
4146
- .spotpatch-actions button:focus-visible,
4147
- .spotpatch-repository:focus-visible,
4148
- .spotpatch-target-open:focus-visible,
4149
- .spotpatch-target-remove:focus-visible,
4150
- .spotpatch-close:focus-visible,
4151
- .spotpatch-reset-position:focus-visible,
4152
- .spotpatch-trigger:focus-visible,
4153
- .spotpatch-target-select:focus-visible,
4154
- .spotpatch-target-editor textarea:focus-visible,
4155
- .spotpatch-locale:focus-visible,
4156
- .spotpatch-prompt:focus-visible,
4157
- .spotpatch-diagnostics > summary:focus-visible {
4158
- outline: 2px solid #8b7cf7;
4159
- outline-offset: 2px;
4288
+ .spotpatch-actions button:disabled {
4289
+ box-shadow: none;
4290
+ cursor: not-allowed;
4291
+ filter: none;
4292
+ opacity: .4;
4160
4293
  }
4294
+
4161
4295
  .spotpatch-live {
4162
4296
  position: absolute;
4163
4297
  width: 1px;
@@ -4167,16 +4301,11 @@ function createStyles(document) {
4167
4301
  clip-path: inset(50%);
4168
4302
  white-space: nowrap;
4169
4303
  }
4170
- @media (prefers-reduced-motion: reduce) {
4171
- .spotpatch-trigger,
4172
- .spotpatch-actions button,
4173
- .spotpatch-diagnostics > summary::before { transition: none; }
4174
- }
4175
4304
  @media (max-width: 520px) {
4176
4305
  .spotpatch-dialog { width: calc(100vw - 16px); }
4177
4306
  .spotpatch-shell { max-height: calc(100dvh - 16px); }
4178
- .spotpatch-header, .spotpatch-body { padding-left: 14px; padding-right: 14px; }
4179
- .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 14px; padding-left: 14px; }
4307
+ .spotpatch-header, .spotpatch-body { padding-right: 14px; padding-left: 14px; }
4308
+ .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 10px; padding-left: 14px; }
4180
4309
  .spotpatch-actions { padding-right: 14px; padding-left: 14px; }
4181
4310
  .spotpatch-target-select { grid-template-columns: 24px minmax(0, 1fr); }
4182
4311
  .spotpatch-target-state { display: none; }
@@ -4186,6 +4315,19 @@ function createStyles(document) {
4186
4315
  `;
4187
4316
  return style;
4188
4317
  }
4318
+ function createResetPositionIcon(document) {
4319
+ const namespace = "http://www.w3.org/2000/svg";
4320
+ const icon = document.createElementNS(namespace, "svg");
4321
+ icon.setAttribute("viewBox", "0 0 16 16");
4322
+ icon.setAttribute("aria-hidden", "true");
4323
+ const path = document.createElementNS(namespace, "path");
4324
+ path.setAttribute(
4325
+ "d",
4326
+ "M8 1.5v3M8 11.5v3M1.5 8h3M11.5 8h3M11 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
4327
+ );
4328
+ icon.append(path);
4329
+ return icon;
4330
+ }
4189
4331
  function summaryLine(summary, prefix) {
4190
4332
  const line = summary.split("\n").find((candidate) => candidate.startsWith(`${prefix}: `));
4191
4333
  return line?.slice(prefix.length + 2).trim();
@@ -4316,7 +4458,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4316
4458
  repositoryLink.target = "_blank";
4317
4459
  repositoryLink.rel = "noopener noreferrer";
4318
4460
  const localeButton = createButton(document, "", "spotpatch-locale");
4319
- const resetPositionButton = createButton(document, "⌖", "spotpatch-reset-position");
4461
+ const resetPositionButton = createButton(document, "", "spotpatch-reset-position");
4462
+ resetPositionButton.append(createResetPositionIcon(document));
4320
4463
  const closeButton = createButton(document, "×", "spotpatch-close");
4321
4464
  headerControls.append(repositoryLink, localeButton, resetPositionButton, closeButton);
4322
4465
  brandRow.append(brand, headerControls);
@@ -4543,6 +4686,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4543
4686
  let editingEnabled = true;
4544
4687
  let currentTargets = [];
4545
4688
  let currentMaximum = 0;
4689
+ let renderedTargetIds = /* @__PURE__ */ new Set();
4690
+ let renderedHighlightIds = /* @__PURE__ */ new Set();
4546
4691
  let currentEditorFeedbackState = "idle";
4547
4692
  let currentDataFlowState = Object.freeze({
4548
4693
  component: Object.freeze({
@@ -4821,6 +4966,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4821
4966
  requestFloatingSurfaceLayout();
4822
4967
  }
4823
4968
  function statusText(target2) {
4969
+ if (target2.instanceState !== void 0 && target2.instanceState !== "captured") {
4970
+ return messages.summary.instanceStates[target2.instanceState];
4971
+ }
4824
4972
  if (target2.status === "ready") {
4825
4973
  return messages.targets.statusReady;
4826
4974
  }
@@ -4847,6 +4995,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4847
4995
  const selectionEnd = focusedInstruction?.selectionEnd;
4848
4996
  currentTargets = targets.map((target2) => Object.freeze({ ...target2 }));
4849
4997
  currentMaximum = maximum;
4998
+ const previousTargetIds = renderedTargetIds;
4999
+ renderedTargetIds = new Set(targets.map((target2) => target2.id));
4850
5000
  targetList.replaceChildren();
4851
5001
  const completeCount = targets.filter(
4852
5002
  (target2) => target2.instruction.trim().length > 0
@@ -4879,6 +5029,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4879
5029
  item.dataset.active = String(target2.active);
4880
5030
  item.dataset.status = target2.status;
4881
5031
  item.dataset.targetId = target2.id;
5032
+ item.dataset.entering = String(!previousTargetIds.has(target2.id));
4882
5033
  const targetSummary = createMarkedElement(document, "div");
4883
5034
  targetSummary.className = "spotpatch-target-summary";
4884
5035
  const select = createButton(document, "", "spotpatch-target-select");
@@ -5001,11 +5152,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
5001
5152
  );
5002
5153
  }
5003
5154
  function showSelectionHighlights(targets) {
5155
+ const previousHighlightIds = renderedHighlightIds;
5156
+ renderedHighlightIds = new Set(targets.map((target2) => target2.id));
5004
5157
  selectionHighlights.replaceChildren();
5005
5158
  for (const [index, target2] of targets.entries()) {
5006
5159
  const box = createMarkedElement(document, "div");
5007
5160
  box.className = "spotpatch-selection-highlight";
5008
5161
  box.dataset.targetId = target2.id;
5162
+ box.dataset.entering = String(!previousHighlightIds.has(target2.id));
5009
5163
  box.dataset.active = String(target2.active);
5010
5164
  box.style.transform = `translate(${String(target2.rect.x)}px, ${String(target2.rect.y)}px)`;
5011
5165
  box.style.width = `${String(target2.rect.width)}px`;
@@ -5455,6 +5609,11 @@ function createSelectionSummary(input, messages) {
5455
5609
  `${messages.confidence}: ${input.resolution.source.confidence} (${messages.confidenceLabels[input.resolution.source.confidence]})`,
5456
5610
  `${messages.origin}: ${input.resolution.source.origin}`
5457
5611
  ];
5612
+ if (input.instance !== void 0) {
5613
+ lines.push(
5614
+ `${messages.instance}: ${messages.instanceStates[input.instance.state]}`
5615
+ );
5616
+ }
5458
5617
  if (input.resolution.react.componentName !== void 0) {
5459
5618
  lines.push(`${messages.component}: ${input.resolution.react.componentName}`);
5460
5619
  }
@@ -5913,7 +6072,7 @@ function sourceLabel(target2, unavailable) {
5913
6072
  return `${path}${line === void 0 ? "" : `:${String(line)}`}${column === void 0 ? "" : `:${String(column)}`}`;
5914
6073
  }
5915
6074
  function targetStatus(target2) {
5916
- if (target2.apiStatus === "failed" || target2.collectionStatus === "failed") {
6075
+ if (target2.apiStatus === "failed" || target2.collectionStatus === "failed" || target2.elementContext?.instance !== void 0 && target2.elementContext.instance.state !== "captured") {
5917
6076
  return "warning";
5918
6077
  }
5919
6078
  if (target2.apiStatus === "loading" || target2.collectionStatus === "loading") {
@@ -5956,6 +6115,7 @@ function createController(config, dependencies = {}) {
5956
6115
  const now = dependencies.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
5957
6116
  const selectionSession = dependencies.selectionSession ?? createSelectionSession(browser.window, config.sessionId, config.maxTargets);
5958
6117
  const restoredSelection = selectionSession.load();
6118
+ const instanceNamespace = createBrowserAnnotationId(browser.window);
5959
6119
  const sourceResolver = createSourceResolver({
5960
6120
  adapter: dependencies.reactAdapter ?? createReact18Adapter({
5961
6121
  ...config.dataFlow.enabled ? {
@@ -5987,7 +6147,8 @@ function createController(config, dependencies = {}) {
5987
6147
  dataFlowReport: void 0,
5988
6148
  dataFlowStatus: "idle",
5989
6149
  element: void 0,
5990
- elementContext: target2.element,
6150
+ instanceScope: void 0,
6151
+ elementContext: withInstanceState(target2.element, "restored"),
5991
6152
  instruction: target2.instruction,
5992
6153
  marker,
5993
6154
  page: target2.page,
@@ -6146,6 +6307,7 @@ function createController(config, dependencies = {}) {
6146
6307
  );
6147
6308
  }
6148
6309
  function selectedAskSelection() {
6310
+ refreshInstanceStates(mutationObserver?.takeRecords() ?? []);
6149
6311
  const snapshots = targets.flatMap((target2) => {
6150
6312
  const snapshot = snapshotTarget(target2);
6151
6313
  return snapshot === void 0 ? [] : [snapshot];
@@ -6164,6 +6326,7 @@ function createController(config, dependencies = {}) {
6164
6326
  const summary = createSelectionSummary(
6165
6327
  {
6166
6328
  resolution: target2.resolution,
6329
+ ...target2.elementContext?.instance === void 0 ? {} : { instance: target2.elementContext.instance },
6167
6330
  ...target2.code === void 0 ? {} : { code: target2.code },
6168
6331
  ...target2.styles === void 0 ? {} : { styles: target2.styles },
6169
6332
  apiStatus: target2.apiStatus,
@@ -6209,7 +6372,11 @@ ${summary}`;
6209
6372
  view.renderTargets(
6210
6373
  targets.map((target2) => ({
6211
6374
  id: target2.id,
6212
- label: target2.resolution.react.componentName ?? (target2.element === void 0 ? target2.elementContext?.tagName ?? "Element" : elementLabel(target2.element)),
6375
+ ...target2.elementContext?.instance === void 0 ? {} : { instanceState: target2.elementContext.instance.state },
6376
+ label: [
6377
+ target2.resolution.react.componentName ?? target2.elementContext?.tagName ?? "Element",
6378
+ (target2.elementContext?.instance?.label ?? target2.elementContext?.textPreview)?.slice(0, 80)
6379
+ ].filter(Boolean).join(" · "),
6213
6380
  source: sourceLabel(target2, view.messages().context.sourceUnavailable),
6214
6381
  canOpenEditor: target2.marker !== void 0,
6215
6382
  status: targetStatus(target2),
@@ -6229,6 +6396,7 @@ ${summary}`;
6229
6396
  }
6230
6397
  }
6231
6398
  function selectedAnnotation() {
6399
+ refreshInstanceStates(mutationObserver?.takeRecords() ?? []);
6232
6400
  if (!canPreview()) {
6233
6401
  return void 0;
6234
6402
  }
@@ -6381,6 +6549,9 @@ ${summary}`;
6381
6549
  resizeObserver?.disconnect();
6382
6550
  for (const target2 of targets) {
6383
6551
  target2.element = void 0;
6552
+ target2.instanceScope = void 0;
6553
+ if (target2.elementContext !== void 0)
6554
+ target2.elementContext = withInstanceState(target2.elementContext, "detached");
6384
6555
  }
6385
6556
  view.hideHighlight();
6386
6557
  view.hideSelectionHighlights();
@@ -6468,17 +6639,11 @@ ${summary}`;
6468
6639
  if (!targetIsCurrent(target2, revision)) {
6469
6640
  return;
6470
6641
  }
6471
- let failed = false;
6472
- try {
6473
- target2.elementContext = collectElementContext({
6474
- element: element2,
6475
- maxCharacters: config.budget.domCharacters
6476
- });
6477
- } catch {
6478
- target2.elementContext = void 0;
6479
- failed = true;
6480
- }
6642
+ refreshInstanceStates(mutationObserver?.takeRecords() ?? []);
6643
+ let failed = target2.elementContext === void 0;
6481
6644
  try {
6645
+ if (target2.element !== element2)
6646
+ throw new Error("Selected instance changed before style collection.");
6482
6647
  target2.styles = collectStyleContext({
6483
6648
  document: browser.document,
6484
6649
  element: element2,
@@ -6502,18 +6667,11 @@ ${summary}`;
6502
6667
  }, 0);
6503
6668
  collectionTimers.set(target2.id, timer);
6504
6669
  }
6505
- function duplicateTarget(element2, marker) {
6506
- if (marker !== void 0) {
6507
- return targets.find(
6508
- (target2) => target2.marker?.fileId === marker.fileId && target2.marker.line === marker.line && target2.marker.column === marker.column
6509
- );
6510
- }
6511
- return targets.find((target2) => target2.element === element2);
6512
- }
6513
6670
  function selectElement(element2) {
6671
+ refreshInstanceStates(mutationObserver?.takeRecords() ?? []);
6514
6672
  const resolution = sourceResolver.resolve(element2);
6515
6673
  const marker = sourceRefToMarker(resolution.source);
6516
- const duplicate = duplicateTarget(element2, marker);
6674
+ const duplicate = targets.find((target3) => target3.element === element2);
6517
6675
  if (duplicate !== void 0) {
6518
6676
  activeTargetId = duplicate.id;
6519
6677
  addingTarget = false;
@@ -6545,6 +6703,7 @@ ${summary}`;
6545
6703
  const target2 = {
6546
6704
  id: `target-${String(targetSequence)}`,
6547
6705
  element: element2,
6706
+ instanceScope: void 0,
6548
6707
  resolution,
6549
6708
  marker,
6550
6709
  page: collectPageContext(browser.document, browser.window),
@@ -6557,6 +6716,17 @@ ${summary}`;
6557
6716
  dataFlowReport: void 0,
6558
6717
  dataFlowStatus: "idle"
6559
6718
  };
6719
+ try {
6720
+ const capture = captureSelectedInstance(
6721
+ element2,
6722
+ `${instanceNamespace}_${target2.id}`,
6723
+ config.budget.domCharacters
6724
+ );
6725
+ target2.elementContext = capture.context;
6726
+ target2.instanceScope = capture.scope;
6727
+ } catch {
6728
+ target2.collectionStatus = "failed";
6729
+ }
6560
6730
  targets.push(target2);
6561
6731
  if (!initialSelection) {
6562
6732
  contextualAskWorkflow?.selectionChanged();
@@ -6687,25 +6857,32 @@ ${summary}`;
6687
6857
  persistSelection();
6688
6858
  view.announce(view.messages().announcements.targetRemoved);
6689
6859
  }
6690
- function refreshSelectedGeometry() {
6691
- if (state.status === "idle" || targets.length === 0) {
6692
- return;
6860
+ function refreshInstanceStates(records) {
6861
+ let changed = false;
6862
+ for (const target2 of targets) {
6863
+ if (target2.element === void 0) continue;
6864
+ const next = target2.page.url !== browser.window.location.href ? "changed" : changedInstanceState(
6865
+ target2.element,
6866
+ target2.instanceScope ?? target2.element,
6867
+ records
6868
+ );
6869
+ if (next === void 0) continue;
6870
+ resizeObserver?.unobserve(target2.element);
6871
+ target2.element = void 0;
6872
+ target2.instanceScope = void 0;
6873
+ if (target2.elementContext !== void 0)
6874
+ target2.elementContext = withInstanceState(target2.elementContext, next);
6875
+ changed = true;
6693
6876
  }
6694
- const disconnected = targets.filter(
6695
- (target2) => target2.element !== void 0 && !target2.element.isConnected
6696
- );
6697
- if (disconnected.length > 0) {
6698
- for (const target2 of disconnected) {
6699
- if (target2.element !== void 0) {
6700
- resizeObserver?.unobserve(target2.element);
6701
- target2.element = void 0;
6702
- }
6703
- }
6877
+ if (changed) {
6704
6878
  refreshSelectionView();
6705
6879
  persistSelection();
6706
- view.announce(view.messages().announcements.detachedTargetPreserved);
6707
- return;
6880
+ contextualAskWorkflow?.selectionChanged();
6708
6881
  }
6882
+ }
6883
+ function refreshSelectedGeometry() {
6884
+ if (state.status === "idle" || targets.length === 0) return;
6885
+ refreshInstanceStates(mutationObserver?.takeRecords() ?? []);
6709
6886
  refreshHighlights();
6710
6887
  }
6711
6888
  function handleOpenEditor(targetId) {
@@ -6759,6 +6936,15 @@ ${summary}`;
6759
6936
  agentWorkflow.run();
6760
6937
  }
6761
6938
  }
6939
+ function composeSelectionPrompt(annotation) {
6940
+ try {
6941
+ return promptComposer.compose(annotation);
6942
+ } catch (error) {
6943
+ if (!(error instanceof RangeError)) throw error;
6944
+ view.announce(view.messages().announcements.instanceBudgetExceeded);
6945
+ return void 0;
6946
+ }
6947
+ }
6762
6948
  function handlePreview() {
6763
6949
  if (state.status !== "selected") {
6764
6950
  return;
@@ -6768,7 +6954,9 @@ ${summary}`;
6768
6954
  view.announce(view.messages().announcements.completeInstructions);
6769
6955
  return;
6770
6956
  }
6771
- previewPrompt = promptComposer.compose(annotation);
6957
+ const composed = composeSelectionPrompt(annotation);
6958
+ if (composed === void 0) return;
6959
+ previewPrompt = composed;
6772
6960
  transition({ type: "PREVIEW" });
6773
6961
  view.showPreview(previewPrompt);
6774
6962
  view.focusPrompt();
@@ -6777,6 +6965,12 @@ ${summary}`;
6777
6965
  if (state.status !== "previewing" || previewPrompt.length === 0) {
6778
6966
  return;
6779
6967
  }
6968
+ const annotation = selectedAnnotation();
6969
+ if (annotation === void 0) return;
6970
+ const composed = composeSelectionPrompt(annotation);
6971
+ if (composed === void 0) return;
6972
+ previewPrompt = composed;
6973
+ view.showPreview(previewPrompt);
6780
6974
  if (clipboard === void 0) {
6781
6975
  transition({ type: "COPY_FAILURE" });
6782
6976
  view.focusPrompt();
@@ -6813,8 +7007,11 @@ ${summary}`;
6813
7007
  if (state.status === "previewing") {
6814
7008
  const annotation = selectedAnnotation();
6815
7009
  if (annotation !== void 0) {
6816
- previewPrompt = promptComposer.compose(annotation);
6817
- view.showPreview(previewPrompt);
7010
+ const composed = composeSelectionPrompt(annotation);
7011
+ if (composed !== void 0) {
7012
+ previewPrompt = composed;
7013
+ view.showPreview(previewPrompt);
7014
+ }
6818
7015
  }
6819
7016
  }
6820
7017
  }
@@ -6868,6 +7065,8 @@ ${summary}`;
6868
7065
  browser.document.addEventListener("keydown", handleKeydown, true);
6869
7066
  browser.window.addEventListener("scroll", refreshSelectedGeometry, true);
6870
7067
  browser.window.addEventListener("resize", refreshSelectedGeometry);
7068
+ browser.window.addEventListener("popstate", refreshSelectedGeometry);
7069
+ browser.window.addEventListener("hashchange", refreshSelectedGeometry);
6871
7070
  view.triggerButton.addEventListener("click", activate);
6872
7071
  view.addTargetButton.addEventListener("click", beginAddTarget);
6873
7072
  view.reselectButton.addEventListener("click", handleReselect);
@@ -6904,10 +7103,15 @@ ${summary}`;
6904
7103
  resizeObserver = new browser.resizeObserver(refreshSelectedGeometry);
6905
7104
  }
6906
7105
  if (browser.mutationObserver !== void 0) {
6907
- mutationObserver = new browser.mutationObserver(refreshSelectedGeometry);
7106
+ mutationObserver = new browser.mutationObserver((records) => {
7107
+ refreshInstanceStates(records);
7108
+ refreshHighlights();
7109
+ });
6908
7110
  mutationObserver.observe(browser.document.documentElement, {
6909
7111
  childList: true,
6910
- subtree: true
7112
+ subtree: true,
7113
+ attributes: true,
7114
+ characterData: true
6911
7115
  });
6912
7116
  }
6913
7117
  if (selectionOpen && targets.length > 0) {
@@ -6942,6 +7146,8 @@ ${summary}`;
6942
7146
  browser.document.removeEventListener("keydown", handleKeydown, true);
6943
7147
  browser.window.removeEventListener("scroll", refreshSelectedGeometry, true);
6944
7148
  browser.window.removeEventListener("resize", refreshSelectedGeometry);
7149
+ browser.window.removeEventListener("popstate", refreshSelectedGeometry);
7150
+ browser.window.removeEventListener("hashchange", refreshSelectedGeometry);
6945
7151
  view.triggerButton.removeEventListener("click", activate);
6946
7152
  view.addTargetButton.removeEventListener("click", beginAddTarget);
6947
7153
  view.reselectButton.removeEventListener("click", handleReselect);