@spotpatch/runtime 1.4.0 → 1.5.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
@@ -10,45 +10,46 @@ import {
10
10
  MAX_ANNOTATION_TARGETS,
11
11
  MAX_TARGET_INSTRUCTION_CHARACTERS
12
12
  } from "@spotpatch/shared";
13
- function freezeSource(source, code) {
13
+ function freezeSource(source2, code2) {
14
14
  return Object.freeze({
15
- ...source,
16
- ...source.relativePath === void 0 && code !== void 0 ? { relativePath: code.relativePath } : {}
15
+ ...source2,
16
+ ...source2.relativePath === void 0 && code2 !== void 0 ? { relativePath: code2.relativePath } : {}
17
17
  });
18
18
  }
19
- function freezeReact(react) {
19
+ function freezeReact(react2) {
20
20
  return Object.freeze({
21
- ...react,
22
- componentStack: Object.freeze([...react.componentStack]),
23
- ...react.source === void 0 ? {} : { source: freezeSource(react.source) }
21
+ ...react2,
22
+ componentStack: Object.freeze([...react2.componentStack]),
23
+ ...react2.source === void 0 ? {} : { source: freezeSource(react2.source) }
24
24
  });
25
25
  }
26
- function freezeElement(element) {
26
+ function freezeElement(element2) {
27
27
  return Object.freeze({
28
- ...element,
29
- rect: Object.freeze({ ...element.rect })
28
+ ...element2,
29
+ rect: Object.freeze({ ...element2.rect })
30
30
  });
31
31
  }
32
- function freezeStyles(styles) {
32
+ function freezeStyles(styles2) {
33
33
  return Object.freeze({
34
- ...styles,
35
- classNames: Object.freeze([...styles.classNames]),
34
+ ...styles2,
35
+ classNames: Object.freeze([...styles2.classNames]),
36
36
  matchedRules: Object.freeze(
37
- styles.matchedRules.map((rule) => Object.freeze({ ...rule }))
37
+ styles2.matchedRules.map((rule) => Object.freeze({ ...rule }))
38
38
  ),
39
- computed: Object.freeze({ ...styles.computed }),
40
- warnings: Object.freeze([...styles.warnings])
39
+ computed: Object.freeze({ ...styles2.computed }),
40
+ warnings: Object.freeze([...styles2.warnings])
41
41
  });
42
42
  }
43
- function freezeTarget(target) {
43
+ function freezeTarget(target2) {
44
44
  return Object.freeze({
45
- instruction: target.instruction.trim(),
46
- source: freezeSource(target.source, target.code),
47
- react: freezeReact(target.react),
48
- element: freezeElement(target.element),
49
- styles: freezeStyles(target.styles),
50
- ...target.code === void 0 ? {} : { code: Object.freeze({ ...target.code }) },
51
- warnings: Object.freeze([...target.warnings])
45
+ instruction: target2.instruction.trim(),
46
+ ...target2.page === void 0 ? {} : { page: Object.freeze({ ...target2.page }) },
47
+ source: freezeSource(target2.source, target2.code),
48
+ react: freezeReact(target2.react),
49
+ element: freezeElement(target2.element),
50
+ styles: freezeStyles(target2.styles),
51
+ ...target2.code === void 0 ? {} : { code: Object.freeze({ ...target2.code }) },
52
+ warnings: Object.freeze([...target2.warnings])
52
53
  });
53
54
  }
54
55
  function createAnnotation(input) {
@@ -57,7 +58,7 @@ function createAnnotation(input) {
57
58
  `SpotPatch annotations require between 1 and ${String(MAX_ANNOTATION_TARGETS)} targets.`
58
59
  );
59
60
  }
60
- const instructions = input.targets.map((target) => target.instruction.trim());
61
+ const instructions = input.targets.map((target2) => target2.instruction.trim());
61
62
  if (instructions.some(
62
63
  (instruction) => instruction.length === 0 || instruction.length > MAX_TARGET_INSTRUCTION_CHARACTERS
63
64
  ) || instructions.reduce((total, instruction) => total + instruction.length, 0) > MAX_ANNOTATION_INSTRUCTION_CHARACTERS) {
@@ -254,10 +255,10 @@ var MAX_EVENT_LINE_BYTES = 1e5;
254
255
  var MAX_EVENT_STREAM_BYTES = 4e6;
255
256
  var RuntimeApiError = class extends Error {
256
257
  code;
257
- constructor(code) {
258
+ constructor(code2) {
258
259
  super("SpotPatch local API request failed.");
259
260
  this.name = "RuntimeApiError";
260
- this.code = code;
261
+ this.code = code2;
261
262
  }
262
263
  };
263
264
  function isRecord2(value) {
@@ -329,9 +330,9 @@ async function readBoundedText(response, maximumBytes) {
329
330
  reader.releaseLock();
330
331
  }
331
332
  }
332
- function parseJson(text) {
333
+ function parseJson(text2) {
333
334
  try {
334
- return JSON.parse(text);
335
+ return JSON.parse(text2);
335
336
  } catch {
336
337
  throw new RuntimeApiError();
337
338
  }
@@ -374,13 +375,13 @@ function omitBrowserCodeContext(request) {
374
375
  ...request,
375
376
  annotation: {
376
377
  ...request.annotation,
377
- targets: request.annotation.targets.map((target) => ({
378
- instruction: target.instruction,
379
- source: target.source,
380
- react: target.react,
381
- element: target.element,
382
- styles: target.styles,
383
- warnings: target.warnings
378
+ targets: request.annotation.targets.map((target2) => ({
379
+ instruction: target2.instruction,
380
+ source: target2.source,
381
+ react: target2.react,
382
+ element: target2.element,
383
+ styles: target2.styles,
384
+ warnings: target2.warnings
384
385
  }))
385
386
  }
386
387
  };
@@ -671,12 +672,12 @@ function contextCharacterCount(classNames, inlineStyle, rules, computed, warning
671
672
  function stylesheetSource(sheet, baseUrl) {
672
673
  return sheet.href === null ? void 0 : sanitizeUrl(sheet.href, baseUrl);
673
674
  }
674
- function collectMatchedRules(element, rules, source, inheritedConditions, output, warnings) {
675
+ function collectMatchedRules(element2, rules, source2, inheritedConditions, output, warnings) {
675
676
  for (const rule of rules) {
676
677
  if (isStyleRule(rule)) {
677
678
  let matches = false;
678
679
  try {
679
- matches = element.matches(rule.selectorText);
680
+ matches = element2.matches(rule.selectorText);
680
681
  } catch {
681
682
  warnings.add(CSS_COLLECTION_WARNINGS.selector);
682
683
  continue;
@@ -686,7 +687,7 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
686
687
  Object.freeze({
687
688
  selector: sanitizeCssText(rule.selectorText),
688
689
  declarations: sanitizeCssText(rule.style.cssText),
689
- ...source === void 0 ? {} : { source },
690
+ ...source2 === void 0 ? {} : { source: source2 },
690
691
  ...inheritedConditions.length === 0 ? {} : { media: inheritedConditions.join(" and ") }
691
692
  })
692
693
  );
@@ -696,9 +697,9 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
696
697
  if (isGroupingRule(rule)) {
697
698
  const condition = groupingCondition(rule);
698
699
  collectMatchedRules(
699
- element,
700
+ element2,
700
701
  rule.cssRules,
701
- source,
702
+ source2,
702
703
  condition === void 0 ? inheritedConditions : [...inheritedConditions, condition],
703
704
  output,
704
705
  warnings
@@ -706,11 +707,11 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
706
707
  }
707
708
  }
708
709
  }
709
- function collectComputedStyle(element, getComputedStyle) {
710
+ function collectComputedStyle(element2, getComputedStyle) {
710
711
  if (getComputedStyle === void 0) {
711
712
  return {};
712
713
  }
713
- const declaration = getComputedStyle(element);
714
+ const declaration = getComputedStyle(element2);
714
715
  const computed = {};
715
716
  for (const property of COMPUTED_STYLE_PROPERTIES) {
716
717
  const value = sanitizeCssText(declaration.getPropertyValue(property).trim());
@@ -848,11 +849,11 @@ function escapeHtml(value) {
848
849
  function isPreservedAttribute(name) {
849
850
  return SAFE_ATTRIBUTE_NAMES.has(name) || name.startsWith("aria-") || name === "data-testid";
850
851
  }
851
- function openingTag(element) {
852
- const tagName = element.tagName.toLowerCase();
852
+ function openingTag(element2) {
853
+ const tagName = element2.tagName.toLowerCase();
853
854
  const attributes = [];
854
- const baseUrl = element.ownerDocument.baseURI;
855
- for (const attribute of element.attributes) {
855
+ const baseUrl = element2.ownerDocument.baseURI;
856
+ for (const attribute of element2.attributes) {
856
857
  const name = attribute.name.toLowerCase();
857
858
  if (!isPreservedAttribute(name)) {
858
859
  continue;
@@ -867,28 +868,28 @@ function openingTag(element) {
867
868
  function normalizedText(value) {
868
869
  return redactSensitiveText(value).replaceAll(/\s+/gu, " ").trim();
869
870
  }
870
- function serializeElement(element, depth, state, lines) {
871
+ function serializeElement(element2, depth, state, lines) {
871
872
  if (state.nodeCount >= DOM_COLLECTION_LIMITS.maxNodes) {
872
873
  state.truncated = true;
873
874
  return;
874
875
  }
875
876
  state.nodeCount += 1;
876
877
  const indent = " ".repeat(depth);
877
- const tagName = element.tagName.toLowerCase();
878
- lines.push(`${indent}${openingTag(element)}`);
878
+ const tagName = element2.tagName.toLowerCase();
879
+ lines.push(`${indent}${openingTag(element2)}`);
879
880
  if (VOID_ELEMENT_NAMES.has(tagName)) {
880
881
  return;
881
882
  }
882
- for (const child of element.childNodes) {
883
+ for (const child of element2.childNodes) {
883
884
  if (state.nodeCount >= DOM_COLLECTION_LIMITS.maxNodes) {
884
885
  state.truncated = true;
885
886
  break;
886
887
  }
887
888
  if (child.nodeType === 3) {
888
- const text = normalizedText(child.textContent ?? "");
889
- if (text.length > 0) {
889
+ const text2 = normalizedText(child.textContent ?? "");
890
+ if (text2.length > 0) {
890
891
  state.nodeCount += 1;
891
- const truncated = text.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text : `${text.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`;
892
+ const truncated = text2.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text2 : `${text2.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`;
892
893
  lines.push(`${" ".repeat(depth + 1)}${escapeHtml(truncated)}`);
893
894
  }
894
895
  continue;
@@ -907,9 +908,9 @@ function serializeElement(element, depth, state, lines) {
907
908
  }
908
909
  lines.push(`${indent}</${tagName}>`);
909
910
  }
910
- function parentContext(element) {
911
+ function parentContext(element2) {
911
912
  const parents = [];
912
- let parent = element.parentElement;
913
+ let parent = element2.parentElement;
913
914
  while (parent !== null && parents.length < DOM_COLLECTION_LIMITS.maxParentDepth) {
914
915
  parents.push(openingTag(parent));
915
916
  parent = parent.parentElement;
@@ -930,26 +931,26 @@ function escapeCssIdentifier(value) {
930
931
  function escapeCssAttribute(value) {
931
932
  return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
932
933
  }
933
- function selectorSegment(element) {
934
- const tagName = element.tagName.toLowerCase();
935
- if (element.id.length > 0) {
936
- return `${tagName}#${escapeCssIdentifier(element.id)}`;
934
+ function selectorSegment(element2) {
935
+ const tagName = element2.tagName.toLowerCase();
936
+ if (element2.id.length > 0) {
937
+ return `${tagName}#${escapeCssIdentifier(element2.id)}`;
937
938
  }
938
- const testId = element.getAttribute("data-testid");
939
+ const testId = element2.getAttribute("data-testid");
939
940
  if (testId !== null && testId.length > 0) {
940
941
  return `${tagName}[data-testid="${escapeCssAttribute(testId)}"]`;
941
942
  }
942
- const classes = Array.from(element.classList).slice(0, 2).map((name) => `.${escapeCssIdentifier(name)}`).join("");
943
- const sameTagSiblings = element.parentElement === null ? [] : Array.from(element.parentElement.children).filter(
944
- (sibling) => sibling.tagName === element.tagName
943
+ const classes = Array.from(element2.classList).slice(0, 2).map((name) => `.${escapeCssIdentifier(name)}`).join("");
944
+ const sameTagSiblings = element2.parentElement === null ? [] : Array.from(element2.parentElement.children).filter(
945
+ (sibling) => sibling.tagName === element2.tagName
945
946
  );
946
- const position = sameTagSiblings.indexOf(element);
947
+ const position = sameTagSiblings.indexOf(element2);
947
948
  const nth = sameTagSiblings.length > 1 && position >= 0 ? `:nth-of-type(${String(position + 1)})` : "";
948
949
  return `${tagName}${classes}${nth}`;
949
950
  }
950
- function createSelector(element) {
951
+ function createSelector(element2) {
951
952
  const segments = [];
952
- let current = element;
953
+ let current = element2;
953
954
  while (current !== null && segments.length < 5) {
954
955
  const segment = selectorSegment(current);
955
956
  segments.unshift(segment);
@@ -968,15 +969,15 @@ function collectElementContext(options) {
968
969
  if (parents.length > 0) {
969
970
  lines.push("<!-- Parent context: nearest first -->", ...parents);
970
971
  }
971
- const text = normalizedText(options.element.textContent);
972
+ const text2 = normalizedText(options.element.textContent);
972
973
  const rect = options.element.getBoundingClientRect();
973
974
  const role = options.element.getAttribute("role");
974
975
  return Object.freeze({
975
976
  tagName: options.element.tagName.toLowerCase(),
976
977
  selector: createSelector(options.element),
977
978
  sanitizedHtml: truncateContext(lines.join("\n"), options.maxCharacters),
978
- ...text.length === 0 ? {} : {
979
- textPreview: text.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text : `${text.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`
979
+ ...text2.length === 0 ? {} : {
980
+ textPreview: text2.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text2 : `${text2.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`
980
981
  },
981
982
  ...role === null ? {} : { role },
982
983
  rect: Object.freeze({
@@ -995,11 +996,11 @@ function isApplePlatform(platform) {
995
996
  function normalizedKey(key) {
996
997
  return key.toLowerCase();
997
998
  }
998
- function isTextEntryTarget(target) {
999
- if (!(target instanceof Element)) {
999
+ function isTextEntryTarget(target2) {
1000
+ if (!(target2 instanceof Element)) {
1000
1001
  return false;
1001
1002
  }
1002
- return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.closest("[contenteditable]:not([contenteditable='false'])") !== null;
1003
+ return target2 instanceof HTMLInputElement || target2 instanceof HTMLTextAreaElement || target2 instanceof HTMLSelectElement || target2.closest("[contenteditable]:not([contenteditable='false'])") !== null;
1003
1004
  }
1004
1005
  function matchesShortcut(event, shortcut, platform) {
1005
1006
  const tokens = shortcut.split("+").map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0);
@@ -1049,15 +1050,25 @@ function unionRects(rects) {
1049
1050
  height: bottom - top
1050
1051
  });
1051
1052
  }
1052
- function getElementRect(element, view) {
1053
- const display = view.getComputedStyle(element).display;
1053
+ function measureElementRect(element2, display) {
1054
1054
  if (display.startsWith("inline")) {
1055
- const lineBoxRect = unionRects(Array.from(element.getClientRects()));
1055
+ const lineBoxRect = unionRects(Array.from(element2.getClientRects()));
1056
1056
  if (lineBoxRect !== void 0) {
1057
1057
  return lineBoxRect;
1058
1058
  }
1059
1059
  }
1060
- return toElementRect(element.getBoundingClientRect());
1060
+ return toElementRect(element2.getBoundingClientRect());
1061
+ }
1062
+ function getElementRect(element2, view) {
1063
+ return measureElementRect(element2, view.getComputedStyle(element2).display);
1064
+ }
1065
+ function getVisibleElementRect(element2, view) {
1066
+ const style = view.getComputedStyle(element2);
1067
+ if (style.display === "none" || style.visibility === "hidden") {
1068
+ return void 0;
1069
+ }
1070
+ const rect = measureElementRect(element2, style.display);
1071
+ return rect.width > 0 && rect.height > 0 ? rect : void 0;
1061
1072
  }
1062
1073
 
1063
1074
  // src/ui/ui-constants.ts
@@ -1068,32 +1079,27 @@ var UI_Z_INDEX = Object.freeze({
1068
1079
  });
1069
1080
 
1070
1081
  // src/picker/hit-test.ts
1071
- function isInsideSpotPatchUI(element) {
1072
- if (element.closest(`[${UI_MARKER_ATTRIBUTE}]`) !== null) {
1082
+ function isInsideSpotPatchUI(element2) {
1083
+ if (element2.closest(`[${UI_MARKER_ATTRIBUTE}]`) !== null) {
1073
1084
  return true;
1074
1085
  }
1075
- const root = element.getRootNode();
1086
+ const root = element2.getRootNode();
1076
1087
  return root instanceof ShadowRoot && root.host.hasAttribute(UI_MARKER_ATTRIBUTE);
1077
1088
  }
1078
- function hasVisibleArea(element, view) {
1079
- const style = view.getComputedStyle(element);
1080
- if (style.display === "none" || style.visibility === "hidden") {
1081
- return false;
1082
- }
1083
- const rect = element.getBoundingClientRect();
1084
- return rect.width > 0 && rect.height > 0;
1089
+ function hasVisibleArea(element2, view) {
1090
+ return getVisibleElementRect(element2, view) !== void 0;
1085
1091
  }
1086
- function isDocumentRoot(element) {
1087
- return element.tagName === "HTML" || element.tagName === "BODY";
1092
+ function isDocumentRoot(element2) {
1093
+ return element2.tagName === "HTML" || element2.tagName === "BODY";
1088
1094
  }
1089
1095
  function pickElementAt(document, view, clientX, clientY) {
1090
1096
  const visibleCandidates = document.elementsFromPoint(clientX, clientY).filter(
1091
- (element) => !isInsideSpotPatchUI(element) && hasVisibleArea(element, view)
1097
+ (element2) => !isInsideSpotPatchUI(element2) && hasVisibleArea(element2, view)
1092
1098
  );
1093
- return visibleCandidates.find((element) => !isDocumentRoot(element)) ?? visibleCandidates[0];
1099
+ return visibleCandidates.find((element2) => !isDocumentRoot(element2)) ?? visibleCandidates[0];
1094
1100
  }
1095
- function isSpotPatchUIEventTarget(target) {
1096
- return target instanceof Element && isInsideSpotPatchUI(target);
1101
+ function isSpotPatchUIEventTarget(target2) {
1102
+ return target2 instanceof Element && isInsideSpotPatchUI(target2);
1097
1103
  }
1098
1104
 
1099
1105
  // src/prompt/prompt-composer.ts
@@ -1172,10 +1178,10 @@ function fenced(language, value) {
1172
1178
  ${value}
1173
1179
  ${fence}`;
1174
1180
  }
1175
- function sourceLocation(target, messages) {
1176
- const path = target.code?.relativePath ?? target.source.relativePath;
1177
- const line = target.source.line;
1178
- const column = target.source.column;
1181
+ function sourceLocation(target2, messages) {
1182
+ const path = target2.code?.relativePath ?? target2.source.relativePath;
1183
+ const line = target2.source.line;
1184
+ const column = target2.source.column;
1179
1185
  if (path === void 0) {
1180
1186
  return messages.unavailable;
1181
1187
  }
@@ -1194,10 +1200,10 @@ function renderRule(rule) {
1194
1200
  }`;
1195
1201
  return [...metadata, body].join("\n");
1196
1202
  }
1197
- function renderStyles(target, rules, messages) {
1203
+ function renderStyles(target2, rules, messages) {
1198
1204
  const metadata = [
1199
- target.styles.classNames.length === 0 ? void 0 : `/* classes: ${target.styles.classNames.map(sanitizeCssText).join(" ")} */`,
1200
- target.styles.inlineStyle === void 0 ? void 0 : `/* inline: ${sanitizeCssText(target.styles.inlineStyle)} */`
1205
+ target2.styles.classNames.length === 0 ? void 0 : `/* classes: ${target2.styles.classNames.map(sanitizeCssText).join(" ")} */`,
1206
+ target2.styles.inlineStyle === void 0 ? void 0 : `/* inline: ${sanitizeCssText(target2.styles.inlineStyle)} */`
1201
1207
  ].filter((line) => line !== void 0);
1202
1208
  const blocks = [...metadata, ...rules.map(renderRule)];
1203
1209
  return blocks.length === 0 ? messages.unavailable : fenced("css", blocks.join("\n\n"));
@@ -1210,41 +1216,47 @@ function renderComputed(entries, messages) {
1210
1216
  ).join("\n")
1211
1217
  );
1212
1218
  }
1213
- function renderTarget(target, draft, index, messages) {
1219
+ function renderTarget(target2, draft, index, messages) {
1214
1220
  const reactLines = [
1215
- `- ${messages.adapter}: ${target.react.supported ? messages.supported : messages.unsupported}`,
1216
- ...target.react.version === void 0 ? [] : [`- ${messages.reactVersion}: ${redactSensitiveText(target.react.version)}`],
1217
- ...target.react.componentName === void 0 ? [] : [
1218
- `- ${messages.component}: ${redactSensitiveText(target.react.componentName)}`
1221
+ `- ${messages.adapter}: ${target2.react.supported ? messages.supported : messages.unsupported}`,
1222
+ ...target2.react.version === void 0 ? [] : [`- ${messages.reactVersion}: ${redactSensitiveText(target2.react.version)}`],
1223
+ ...target2.react.componentName === void 0 ? [] : [
1224
+ `- ${messages.component}: ${redactSensitiveText(target2.react.componentName)}`
1219
1225
  ],
1220
1226
  ...draft.reactStack.length === 0 ? [] : [
1221
1227
  `- ${messages.stack}: ${draft.reactStack.map(redactSensitiveText).join(" > ")}`
1222
1228
  ]
1223
1229
  ];
1224
- const code = target.code === void 0 || draft.codeLines.length === 0 ? messages.unavailable : `- ${messages.boundary}: ${target.code.boundary}
1230
+ const code2 = target2.code === void 0 || draft.codeLines.length === 0 ? messages.unavailable : `- ${messages.boundary}: ${target2.code.boundary}
1225
1231
 
1226
1232
  ${fenced(
1227
- target.code.language,
1233
+ target2.code.language,
1228
1234
  redactSensitiveText(draft.codeLines.join("\n"))
1229
1235
  )}`;
1230
1236
  return [
1231
1237
  `### ${messages.target(index + 1)}`,
1232
1238
  `#### ${messages.requestedChange}`,
1233
1239
  draft.instruction,
1240
+ ...target2.page === void 0 ? [] : [
1241
+ `#### ${messages.pageEnvironment}`,
1242
+ `- ${messages.url}: <${sanitizeUrl(target2.page.url, target2.page.url)}>
1243
+ - ${messages.pathname}: ${redactSensitiveText(target2.page.pathname)}
1244
+ - ${messages.title}: ${redactSensitiveText(target2.page.title) || messages.unavailable}`
1245
+ ],
1234
1246
  `#### ${messages.reactContext}`,
1235
1247
  reactLines.join("\n"),
1236
1248
  `#### ${messages.source}`,
1237
- `- ${messages.file}: ${sourceLocation(target, messages)}
1238
- - ${messages.origin}: ${target.source.origin}
1239
- - ${messages.confidence}: ${target.source.confidence}`,
1249
+ `- ${messages.file}: ${sourceLocation(target2, messages)}
1250
+ - ${messages.origin}: ${target2.source.origin}
1251
+ - ${messages.confidence}: ${target2.source.confidence}`,
1240
1252
  `#### ${messages.element}`,
1241
1253
  fenced("html", redactSensitiveText(draft.dom)),
1242
1254
  `#### ${messages.styles}`,
1243
- renderStyles(target, draft.matchedRules, messages),
1255
+ renderStyles(target2, draft.matchedRules, messages),
1244
1256
  `#### ${messages.computedStyles}`,
1245
1257
  renderComputed(draft.computedEntries, messages),
1246
1258
  `#### ${messages.code}`,
1247
- code,
1259
+ code2,
1248
1260
  `#### ${messages.warnings}`,
1249
1261
  draft.warnings.length === 0 ? `- ${messages.none}` : draft.warnings.map((warning) => `- ${redactSensitiveText(warning)}`).join("\n")
1250
1262
  ].join("\n\n");
@@ -1260,19 +1272,19 @@ function renderPrompt(annotation, draft) {
1260
1272
  - ${messages.viewport}: ${String(annotation.page.viewportWidth)} \xD7 ${String(annotation.page.viewportHeight)}
1261
1273
  - ${messages.devicePixelRatio}: ${String(annotation.page.devicePixelRatio)}`,
1262
1274
  `## ${messages.selectedTargets(annotation.targets.length)}`,
1263
- annotation.targets.map((target, index) => {
1275
+ annotation.targets.map((target2, index) => {
1264
1276
  const targetDraft = draft.targets[index];
1265
1277
  if (targetDraft === void 0) {
1266
1278
  throw new RangeError("Prompt target draft is missing.");
1267
1279
  }
1268
- return renderTarget(target, targetDraft, index, messages);
1280
+ return renderTarget(target2, targetDraft, index, messages);
1269
1281
  }).join("\n\n"),
1270
1282
  `## ${messages.modificationRequirements}`,
1271
1283
  messages.modificationConstraint
1272
1284
  ].join("\n\n");
1273
1285
  }
1274
- function shrinkCode(target, draft) {
1275
- if (draft.codeLines.length === 0 || target.code === void 0) {
1286
+ function shrinkCode(target2, draft) {
1287
+ if (draft.codeLines.length === 0 || target2.code === void 0) {
1276
1288
  return false;
1277
1289
  }
1278
1290
  if (draft.codeLines.length === 1) {
@@ -1284,9 +1296,9 @@ function shrinkCode(target, draft) {
1284
1296
  }
1285
1297
  return true;
1286
1298
  }
1287
- const selectedLine = target.source.line ?? target.code.startLine;
1288
- const firstDistance = Math.abs(target.code.startLine - selectedLine);
1289
- const lastLine = target.code.startLine + draft.codeLines.length - 1;
1299
+ const selectedLine = target2.source.line ?? target2.code.startLine;
1300
+ const firstDistance = Math.abs(target2.code.startLine - selectedLine);
1301
+ const lastLine = target2.code.startLine + draft.codeLines.length - 1;
1290
1302
  const lastDistance = Math.abs(lastLine - selectedLine);
1291
1303
  if (lastDistance >= firstDistance) {
1292
1304
  draft.codeLines.pop();
@@ -1295,15 +1307,15 @@ function shrinkCode(target, draft) {
1295
1307
  }
1296
1308
  return true;
1297
1309
  }
1298
- function createTargetDraft(target) {
1310
+ function createTargetDraft(target2) {
1299
1311
  return {
1300
- instruction: redactSensitiveText(target.instruction.trim()),
1301
- reactStack: [...target.react.componentStack],
1302
- dom: target.element.sanitizedHtml,
1303
- matchedRules: [...target.styles.matchedRules],
1304
- computedEntries: Object.entries(target.styles.computed),
1305
- codeLines: target.code?.excerpt.split(/\r?\n/u) ?? [],
1306
- warnings: Array.from(/* @__PURE__ */ new Set([...target.styles.warnings, ...target.warnings]))
1312
+ instruction: redactSensitiveText(target2.instruction.trim()),
1313
+ reactStack: [...target2.react.componentStack],
1314
+ dom: target2.element.sanitizedHtml,
1315
+ matchedRules: [...target2.styles.matchedRules],
1316
+ computedEntries: Object.entries(target2.styles.computed),
1317
+ codeLines: target2.code?.excerpt.split(/\r?\n/u) ?? [],
1318
+ warnings: Array.from(/* @__PURE__ */ new Set([...target2.styles.warnings, ...target2.warnings]))
1307
1319
  };
1308
1320
  }
1309
1321
  function createDraft(annotation) {
@@ -1332,9 +1344,9 @@ function shrinkTargetsFairly(annotation, draft, cursor) {
1332
1344
  for (const operation of operations) {
1333
1345
  for (let offset = 0; offset < annotation.targets.length; offset += 1) {
1334
1346
  const index = (cursor + offset) % annotation.targets.length;
1335
- const target = annotation.targets[index];
1347
+ const target2 = annotation.targets[index];
1336
1348
  const targetDraft = draft.targets[index];
1337
- if (target !== void 0 && targetDraft !== void 0 && operation(target, targetDraft)) {
1349
+ if (target2 !== void 0 && targetDraft !== void 0 && operation(target2, targetDraft)) {
1338
1350
  return (index + 1) % annotation.targets.length;
1339
1351
  }
1340
1352
  }
@@ -1343,15 +1355,15 @@ function shrinkTargetsFairly(annotation, draft, cursor) {
1343
1355
  }
1344
1356
  function renderCompactPrompt(annotation, draft) {
1345
1357
  const messages = PROMPT_MESSAGES[annotation.locale];
1346
- const targets = annotation.targets.map((target, index) => {
1358
+ const targets = annotation.targets.map((target2, index) => {
1347
1359
  const targetDraft = draft.targets[index];
1348
- const component = target.react.componentName;
1360
+ const component = target2.react.componentName;
1349
1361
  return [
1350
1362
  `### ${messages.target(index + 1)}`,
1351
- `- ${messages.requestedChange}: ${targetDraft?.instruction ?? redactSensitiveText(target.instruction)}`,
1352
- `- ${messages.file}: ${sourceLocation(target, messages)}`,
1363
+ `- ${messages.requestedChange}: ${targetDraft?.instruction ?? redactSensitiveText(target2.instruction)}`,
1364
+ `- ${messages.file}: ${sourceLocation(target2, messages)}`,
1353
1365
  ...component === void 0 ? [] : [`- ${messages.component}: ${redactSensitiveText(component)}`],
1354
- `- ${messages.element}: ${redactSensitiveText(openingTag2(targetDraft?.dom ?? target.element.sanitizedHtml, messages.unavailable))}`
1366
+ `- ${messages.element}: ${redactSensitiveText(openingTag2(targetDraft?.dom ?? target2.element.sanitizedHtml, messages.unavailable))}`
1355
1367
  ].join("\n");
1356
1368
  });
1357
1369
  return [
@@ -1409,7 +1421,10 @@ var STATES = Object.freeze({
1409
1421
  });
1410
1422
  var INITIAL_RUNTIME_STATE = STATES.idle;
1411
1423
  function reduceIdle(event) {
1412
- return event.type === "ACTIVATE" ? STATES.inspecting : STATES.idle;
1424
+ if (event.type === "ACTIVATE") {
1425
+ return STATES.inspecting;
1426
+ }
1427
+ return event.type === "RESTORE" ? STATES.selected : STATES.idle;
1413
1428
  }
1414
1429
  function reduceInspecting(event) {
1415
1430
  switch (event.type) {
@@ -1461,6 +1476,121 @@ function reduceRuntimeState(state, event) {
1461
1476
  }
1462
1477
  }
1463
1478
 
1479
+ // src/state/selection-session.ts
1480
+ var STORAGE_PREFIX = "spotpatch:selection:";
1481
+ var SNAPSHOT_VERSION = 1;
1482
+ var MAX_SNAPSHOT_CHARACTERS = 1e6;
1483
+ function record(value) {
1484
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1485
+ }
1486
+ function text(value) {
1487
+ return typeof value === "string";
1488
+ }
1489
+ function optionalText(value) {
1490
+ return value === void 0 || text(value);
1491
+ }
1492
+ function strings(value) {
1493
+ return Array.isArray(value) && value.length <= 256 && value.every(text);
1494
+ }
1495
+ function finiteNumber(value) {
1496
+ return typeof value === "number" && Number.isFinite(value);
1497
+ }
1498
+ function optionalNumber(value) {
1499
+ return value === void 0 || finiteNumber(value);
1500
+ }
1501
+ function source(value) {
1502
+ const item = record(value);
1503
+ return item !== void 0 && text(item.origin) && text(item.confidence) && optionalText(item.fileId) && optionalText(item.relativePath) && optionalNumber(item.line) && optionalNumber(item.column);
1504
+ }
1505
+ function react(value) {
1506
+ const item = record(value);
1507
+ return item !== void 0 && typeof item.supported === "boolean" && optionalText(item.version) && optionalText(item.componentName) && strings(item.componentStack) && (item.source === void 0 || source(item.source));
1508
+ }
1509
+ function page(value) {
1510
+ const item = record(value);
1511
+ return item !== void 0 && text(item.url) && text(item.pathname) && text(item.title) && finiteNumber(item.viewportWidth) && finiteNumber(item.viewportHeight) && finiteNumber(item.devicePixelRatio);
1512
+ }
1513
+ function element(value) {
1514
+ const item = record(value);
1515
+ const rect = record(item?.rect);
1516
+ 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);
1517
+ }
1518
+ function styles(value) {
1519
+ const item = record(value);
1520
+ const computed = record(item?.computed);
1521
+ return item !== void 0 && strings(item.classNames) && optionalText(item.inlineStyle) && Array.isArray(item.matchedRules) && item.matchedRules.length <= 256 && item.matchedRules.every((value2) => {
1522
+ const rule = record(value2);
1523
+ return rule !== void 0 && text(rule.selector) && text(rule.declarations) && optionalText(rule.source) && optionalText(rule.media);
1524
+ }) && computed !== void 0 && Object.values(computed).every(text) && strings(item.warnings);
1525
+ }
1526
+ function code(value) {
1527
+ const item = record(value);
1528
+ return item !== void 0 && text(item.relativePath) && text(item.language) && finiteNumber(item.startLine) && finiteNumber(item.endLine) && text(item.excerpt) && text(item.boundary);
1529
+ }
1530
+ function target(value) {
1531
+ const item = record(value);
1532
+ return item !== void 0 && text(item.id) && text(item.instruction) && page(item.page) && source(item.source) && react(item.react) && element(item.element) && styles(item.styles) && (item.code === void 0 || code(item.code));
1533
+ }
1534
+ function parseSnapshot(value, maximumTargets) {
1535
+ const item = record(value);
1536
+ if (item?.version !== SNAPSHOT_VERSION || typeof item.open !== "boolean" || !Number.isSafeInteger(item.sequence) || Number(item.sequence) < 0 || item.activeTargetId !== void 0 && typeof item.activeTargetId !== "string" || !Array.isArray(item.targets) || item.targets.length === 0 || item.targets.length > maximumTargets || !item.targets.every(target)) {
1537
+ return void 0;
1538
+ }
1539
+ const targets = item.targets;
1540
+ const ids = targets.map(({ id }) => id);
1541
+ if (new Set(ids).size !== ids.length || item.activeTargetId !== void 0 && !ids.includes(item.activeTargetId)) {
1542
+ return void 0;
1543
+ }
1544
+ return {
1545
+ ...item.activeTargetId === void 0 ? {} : { activeTargetId: item.activeTargetId },
1546
+ open: item.open,
1547
+ sequence: Number(item.sequence),
1548
+ targets
1549
+ };
1550
+ }
1551
+ function createSelectionSession(window, sessionId, maximumTargets) {
1552
+ const key = `${STORAGE_PREFIX}${sessionId}`;
1553
+ let storage;
1554
+ try {
1555
+ storage = window.sessionStorage;
1556
+ } catch {
1557
+ storage = void 0;
1558
+ }
1559
+ return Object.freeze({
1560
+ clear() {
1561
+ try {
1562
+ storage?.removeItem(key);
1563
+ } catch {
1564
+ }
1565
+ },
1566
+ load() {
1567
+ let serialized;
1568
+ try {
1569
+ serialized = storage?.getItem(key) ?? null;
1570
+ } catch {
1571
+ return void 0;
1572
+ }
1573
+ if (serialized === null || serialized.length > MAX_SNAPSHOT_CHARACTERS) {
1574
+ return void 0;
1575
+ }
1576
+ try {
1577
+ return parseSnapshot(JSON.parse(serialized), maximumTargets);
1578
+ } catch {
1579
+ return void 0;
1580
+ }
1581
+ },
1582
+ save(snapshot) {
1583
+ try {
1584
+ storage?.setItem(
1585
+ key,
1586
+ JSON.stringify({ version: SNAPSHOT_VERSION, ...snapshot })
1587
+ );
1588
+ } catch {
1589
+ }
1590
+ }
1591
+ });
1592
+ }
1593
+
1464
1594
  // src/source/source-resolver.ts
1465
1595
  import {
1466
1596
  parseSourceMarker,
@@ -1470,11 +1600,11 @@ var EMPTY_REACT_CONTEXT = Object.freeze({
1470
1600
  supported: false,
1471
1601
  componentStack: Object.freeze([])
1472
1602
  });
1473
- function directMarker(element) {
1474
- return parseSourceMarker(element.getAttribute(SOURCE_MARKER_ATTRIBUTE));
1603
+ function directMarker(element2) {
1604
+ return parseSourceMarker(element2.getAttribute(SOURCE_MARKER_ATTRIBUTE));
1475
1605
  }
1476
- function ancestorMarker(element) {
1477
- let ancestor = element.parentElement;
1606
+ function ancestorMarker(element2) {
1607
+ let ancestor = element2.parentElement;
1478
1608
  while (ancestor !== null) {
1479
1609
  const marker = directMarker(ancestor);
1480
1610
  if (marker !== void 0) {
@@ -1496,21 +1626,21 @@ function markerSource(marker, origin, confidence) {
1496
1626
  function unknownSource() {
1497
1627
  return Object.freeze({ origin: "none", confidence: "unknown" });
1498
1628
  }
1499
- function sourceRefToMarker(source) {
1500
- return source.fileId === void 0 || source.line === void 0 || source.column === void 0 ? void 0 : Object.freeze({
1501
- fileId: source.fileId,
1502
- line: source.line,
1503
- column: source.column
1629
+ function sourceRefToMarker(source2) {
1630
+ return source2.fileId === void 0 || source2.line === void 0 || source2.column === void 0 ? void 0 : Object.freeze({
1631
+ fileId: source2.fileId,
1632
+ line: source2.line,
1633
+ column: source2.column
1504
1634
  });
1505
1635
  }
1506
1636
  function createSourceResolver(options) {
1507
1637
  let adapterEnabled = true;
1508
- function inspectReact(element) {
1638
+ function inspectReact(element2) {
1509
1639
  if (!adapterEnabled) {
1510
1640
  return EMPTY_REACT_CONTEXT;
1511
1641
  }
1512
1642
  try {
1513
- return options.adapter.inspect(element);
1643
+ return options.adapter.inspect(element2);
1514
1644
  } catch {
1515
1645
  adapterEnabled = false;
1516
1646
  options.onAdapterError?.();
@@ -1518,26 +1648,26 @@ function createSourceResolver(options) {
1518
1648
  }
1519
1649
  }
1520
1650
  return Object.freeze({
1521
- resolve(element) {
1522
- const marker = directMarker(element);
1523
- const react = inspectReact(element);
1651
+ resolve(element2) {
1652
+ const marker = directMarker(element2);
1653
+ const react2 = inspectReact(element2);
1524
1654
  if (marker !== void 0) {
1525
1655
  return Object.freeze({
1526
- react,
1656
+ react: react2,
1527
1657
  source: markerSource(marker, "jsx-host", "exact")
1528
1658
  });
1529
1659
  }
1530
- if (react.source !== void 0) {
1531
- return Object.freeze({ react, source: react.source });
1660
+ if (react2.source !== void 0) {
1661
+ return Object.freeze({ react: react2, source: react2.source });
1532
1662
  }
1533
- const nearestAncestor = ancestorMarker(element);
1663
+ const nearestAncestor = ancestorMarker(element2);
1534
1664
  if (nearestAncestor !== void 0) {
1535
1665
  return Object.freeze({
1536
- react,
1666
+ react: react2,
1537
1667
  source: markerSource(nearestAncestor, "dom-ancestor", "approximate")
1538
1668
  });
1539
1669
  }
1540
- return Object.freeze({ react, source: unknownSource() });
1670
+ return Object.freeze({ react: react2, source: unknownSource() });
1541
1671
  },
1542
1672
  dispose() {
1543
1673
  adapterEnabled = false;
@@ -1555,9 +1685,9 @@ import {
1555
1685
 
1556
1686
  // src/ui/dom.ts
1557
1687
  function createMarkedElement(document, tagName) {
1558
- const element = document.createElement(tagName);
1559
- element.setAttribute(UI_MARKER_ATTRIBUTE, "");
1560
- return element;
1688
+ const element2 = document.createElement(tagName);
1689
+ element2.setAttribute(UI_MARKER_ATTRIBUTE, "");
1690
+ return element2;
1561
1691
  }
1562
1692
  function createButton(document, label, className = "") {
1563
1693
  const button = createMarkedElement(document, "button");
@@ -1570,7 +1700,7 @@ function createButton(document, label, className = "") {
1570
1700
  // src/ui/agent-panel.ts
1571
1701
  var AGENT_PANEL_STYLES = `
1572
1702
  .spotpatch-agent {
1573
- margin-top: 14px;
1703
+ margin-top: 10px;
1574
1704
  overflow: hidden;
1575
1705
  border: 1px solid var(--spotpatch-border-subtle);
1576
1706
  border-radius: var(--spotpatch-radius-card);
@@ -1580,36 +1710,36 @@ var AGENT_PANEL_STYLES = `
1580
1710
  display: flex;
1581
1711
  align-items: center;
1582
1712
  justify-content: space-between;
1583
- gap: 12px;
1584
- padding: 14px 16px;
1713
+ gap: 10px;
1714
+ padding: 10px 12px;
1585
1715
  border-bottom: 1px solid rgb(255 255 255 / 7%);
1586
1716
  }
1587
- .spotpatch-agent-title { color: #eef0f5; font-size: 16px; font-weight: 680; }
1717
+ .spotpatch-agent-title { color: #eef0f5; font-size: 13px; font-weight: 650; }
1588
1718
  .spotpatch-agent-badge {
1589
1719
  border: 1px solid rgb(129 112 247 / 24%);
1590
1720
  border-radius: 999px;
1591
1721
  padding: 3px 7px;
1592
1722
  color: #c5cafa;
1593
1723
  background: rgb(109 93 246 / 9%);
1594
- font-size: 11px;
1724
+ font-size: 10px;
1595
1725
  font-weight: 650;
1596
1726
  }
1597
- .spotpatch-agent-setup { padding: 18px; }
1598
- .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 15px; }
1599
- .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 14px; font-weight: 580; }
1727
+ .spotpatch-agent-setup { padding: 12px; }
1728
+ .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 10px; }
1729
+ .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 580; }
1600
1730
  .spotpatch-agent select {
1601
1731
  box-sizing: border-box;
1602
1732
  width: 100%;
1603
1733
  min-width: 0;
1604
- min-height: 46px;
1605
- margin-top: 7px;
1734
+ min-height: 34px;
1735
+ margin-top: 4px;
1606
1736
  border: 1px solid var(--spotpatch-border);
1607
- border-radius: 12px;
1608
- padding: 10px 38px 10px 13px;
1737
+ border-radius: 8px;
1738
+ padding: 7px 32px 7px 9px;
1609
1739
  color: #e8ebf2;
1610
1740
  color-scheme: dark;
1611
1741
  background: linear-gradient(180deg, rgb(255 255 255 / 4%), rgb(255 255 255 / 2%));
1612
- font-size: 15px;
1742
+ font-size: 12px;
1613
1743
  font-weight: 560;
1614
1744
  line-height: 1.35;
1615
1745
  outline: none;
@@ -1621,7 +1751,7 @@ var AGENT_PANEL_STYLES = `
1621
1751
  @supports (appearance: base-select) {
1622
1752
  .spotpatch-agent select,
1623
1753
  ::picker(select) { appearance: base-select; }
1624
- .spotpatch-agent select { padding: 10px 13px; }
1754
+ .spotpatch-agent select { padding: 7px 9px; }
1625
1755
  .spotpatch-agent select::picker-icon {
1626
1756
  color: #9da6b5;
1627
1757
  transition: rotate 180ms cubic-bezier(.2, .8, .2, 1);
@@ -1668,24 +1798,24 @@ var AGENT_PANEL_STYLES = `
1668
1798
  .spotpatch-consent {
1669
1799
  display: flex;
1670
1800
  align-items: flex-start;
1671
- gap: 10px;
1672
- margin-top: 16px;
1801
+ gap: 8px;
1802
+ margin-top: 10px;
1673
1803
  color: var(--spotpatch-text-secondary);
1674
1804
  cursor: pointer;
1675
- font-size: 14px;
1676
- line-height: 1.6;
1805
+ font-size: 11.5px;
1806
+ line-height: 1.5;
1677
1807
  }
1678
- .spotpatch-consent input { width: 16px; height: 16px; flex: none; margin: 3px 0 0; accent-color: var(--spotpatch-accent); }
1808
+ .spotpatch-consent input { width: 14px; height: 14px; flex: none; margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1679
1809
  .spotpatch-workspace-consent {
1680
- margin-top: 12px;
1810
+ margin-top: 9px;
1681
1811
  border: 1px solid rgb(251 191 36 / 18%);
1682
1812
  border-radius: 10px;
1683
- padding: 11px 12px;
1813
+ padding: 9px 10px;
1684
1814
  background: rgb(120 53 15 / 9%);
1685
1815
  }
1686
- .spotpatch-workspace-consent strong { display: block; color: #fde68a; font-size: 13px; font-weight: 620; }
1687
- .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: #aeb6c4; font-size: 12px; line-height: 1.5; }
1688
- .spotpatch-agent-health-list { display: grid; gap: 8px; margin-top: 14px; }
1816
+ .spotpatch-workspace-consent strong { display: block; color: #fde68a; font-size: 11.5px; font-weight: 620; }
1817
+ .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: #aeb6c4; font-size: 10.5px; line-height: 1.45; }
1818
+ .spotpatch-agent-health-list { display: grid; gap: 6px; margin-top: 10px; }
1689
1819
  .spotpatch-agent-workspace,
1690
1820
  .spotpatch-agent-capability {
1691
1821
  display: flex;
@@ -1693,7 +1823,7 @@ var AGENT_PANEL_STYLES = `
1693
1823
  gap: 8px;
1694
1824
  margin: 0;
1695
1825
  color: #929bab;
1696
- font-size: 13px;
1826
+ font-size: 11px;
1697
1827
  }
1698
1828
  .spotpatch-agent-capability::before {
1699
1829
  width: 6px;
@@ -1723,7 +1853,7 @@ var AGENT_PANEL_STYLES = `
1723
1853
  .spotpatch-agent-workspace[data-state="consent-required"]::before { background: #fbbf24; }
1724
1854
  .spotpatch-agent-workspace[data-state="blocked"] { color: #fecaca; }
1725
1855
  .spotpatch-agent-workspace[data-state="blocked"]::before { background: var(--spotpatch-danger); }
1726
- .spotpatch-agent-job { padding: 16px; }
1856
+ .spotpatch-agent-job { padding: 12px; }
1727
1857
  .spotpatch-agent-job-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
1728
1858
  .spotpatch-agent-model { min-width: 0; overflow: hidden; color: #d6dafe; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
1729
1859
  .spotpatch-agent-status {
@@ -2174,68 +2304,81 @@ function createAgentPanel(document, ai, localizer) {
2174
2304
  });
2175
2305
  }
2176
2306
 
2307
+ // src/ui/brand-mark-content.ts
2308
+ var BRAND_MARK_CONTENT = `
2309
+ <defs>
2310
+ <linearGradient id="locator-gradient" x1="76" y1="92" x2="436" y2="374" gradientUnits="userSpaceOnUse">
2311
+ <stop offset="0" stop-color="#B61CFF" />
2312
+ <stop offset="0.38" stop-color="#6D35FF" />
2313
+ <stop offset="0.72" stop-color="#168EFF" />
2314
+ <stop offset="1" stop-color="#00D9E9" />
2315
+ </linearGradient>
2316
+ <linearGradient id="left-code-gradient" x1="165" y1="166" x2="236" y2="258" gradientUnits="userSpaceOnUse">
2317
+ <stop stop-color="#A51EFF" />
2318
+ <stop offset="1" stop-color="#653BFF" />
2319
+ </linearGradient>
2320
+ <linearGradient id="right-code-gradient" x1="276" y1="166" x2="347" y2="258" gradientUnits="userSpaceOnUse">
2321
+ <stop stop-color="#158DFF" />
2322
+ <stop offset="1" stop-color="#00D8E9" />
2323
+ </linearGradient>
2324
+ <linearGradient id="bolt-gradient" x1="270" y1="111" x2="252" y2="365" gradientUnits="userSpaceOnUse">
2325
+ <stop stop-color="#6840FF" />
2326
+ <stop offset="0.48" stop-color="#257BFF" />
2327
+ <stop offset="1" stop-color="#00CBEF" />
2328
+ </linearGradient>
2329
+ </defs>
2330
+ <path
2331
+ fill="url(#locator-gradient)"
2332
+ fill-rule="evenodd"
2333
+ clip-rule="evenodd"
2334
+ d="M256 52C345.47 52 418 124.53 418 214C418 267.55 391.98 316.24 354.04 348.02L256 468L157.96 348.02C120.02 316.24 94 267.55 94 214C94 124.53 166.53 52 256 52ZM256 88C186.41 88 130 144.41 130 214C130 258.2 152.76 297.08 187.2 319.57L256 403.8L324.8 319.57C359.24 297.08 382 258.2 382 214C382 144.41 325.59 88 256 88Z"
2335
+ />
2336
+ <rect x="238" y="20" width="36" height="84" rx="4" fill="url(#locator-gradient)" />
2337
+ <rect x="62" y="196" width="84" height="36" rx="4" fill="url(#locator-gradient)" />
2338
+ <rect x="366" y="196" width="84" height="36" rx="4" fill="url(#locator-gradient)" />
2339
+ <path
2340
+ d="M213.5 160L158 211.5L213.5 263L238 236.5L211 211.5L238 186.5L213.5 160Z"
2341
+ fill="url(#left-code-gradient)"
2342
+ />
2343
+ <path
2344
+ d="M298.5 160L354 211.5L298.5 263L274 236.5L301 211.5L274 186.5L298.5 160Z"
2345
+ fill="url(#right-code-gradient)"
2346
+ />
2347
+ <path
2348
+ d="M283 108L232 212L266 253L238 369L302 237L267 198L283 108Z"
2349
+ fill="url(#bolt-gradient)"
2350
+ />
2351
+ `;
2352
+
2177
2353
  // src/ui/brand-mark.ts
2178
2354
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2355
+ function resolveBrandMarkContent(override) {
2356
+ if (override !== void 0) {
2357
+ return { content: override, viewBox: "0 0 512 512" };
2358
+ }
2359
+ const injected = typeof __SPOTPATCH_BRAND_MARK_CONTENT__ === "string" ? __SPOTPATCH_BRAND_MARK_CONTENT__ : void 0;
2360
+ if (typeof injected === "string") {
2361
+ return { content: injected, viewBox: "0 0 512 512" };
2362
+ }
2363
+ return true ? { content: BRAND_MARK_CONTENT, viewBox: "0 0 512 512" } : { content: FALLBACK_BRAND_MARK_CONTENT, viewBox: "0 0 1 1" };
2364
+ }
2179
2365
  function svgElement(document, tagName, attributes) {
2180
- const element = document.createElementNS(SVG_NAMESPACE, tagName);
2366
+ const element2 = document.createElementNS(SVG_NAMESPACE, tagName);
2181
2367
  for (const [name, value] of Object.entries(attributes)) {
2182
- element.setAttribute(name, value);
2368
+ element2.setAttribute(name, value);
2183
2369
  }
2184
- return element;
2370
+ return element2;
2185
2371
  }
2186
- function createBrandMark(document) {
2372
+ function createBrandMark(document, content) {
2373
+ const resolved = resolveBrandMarkContent(content);
2187
2374
  const svg = svgElement(document, "svg", {
2375
+ xmlns: SVG_NAMESPACE,
2188
2376
  class: "spotpatch-brand-mark",
2189
- viewBox: "0 0 210 208",
2377
+ viewBox: resolved.viewBox,
2190
2378
  "aria-hidden": "true",
2191
2379
  focusable: "false"
2192
2380
  });
2193
- const definitions = svgElement(document, "defs", {});
2194
- const gradient = svgElement(document, "linearGradient", {
2195
- id: "spotpatch-brand-gradient",
2196
- x1: "35",
2197
- y1: "42",
2198
- x2: "184",
2199
- y2: "176",
2200
- gradientUnits: "userSpaceOnUse"
2201
- });
2202
- gradient.append(
2203
- svgElement(document, "stop", { offset: "0", "stop-color": "#7c22ff" }),
2204
- svgElement(document, "stop", { offset: ".52", "stop-color": "#4a69ff" }),
2205
- svgElement(document, "stop", { offset: "1", "stop-color": "#06d9ef" })
2206
- );
2207
- definitions.append(gradient);
2208
- const commonStroke = {
2209
- fill: "none",
2210
- stroke: "url(#spotpatch-brand-gradient)",
2211
- "stroke-linecap": "square",
2212
- "stroke-linejoin": "miter"
2213
- };
2214
- const outer = svgElement(document, "path", {
2215
- d: "M106 42C67 42 43 68 43 105c0 36 24 61 63 96 39-35 63-60 63-96 0-37-24-63-63-63Z",
2216
- ...commonStroke,
2217
- "stroke-width": "14"
2218
- });
2219
- const topStem = svgElement(document, "path", {
2220
- d: "M106 25v42",
2221
- ...commonStroke,
2222
- "stroke-width": "14"
2223
- });
2224
- const sideStems = svgElement(document, "path", {
2225
- d: "M35 109h20m102 0h38",
2226
- ...commonStroke,
2227
- "stroke-width": "13"
2228
- });
2229
- const brackets = svgElement(document, "path", {
2230
- d: "m94 89-18 19 18 17m39-36 18 19-18 17",
2231
- ...commonStroke,
2232
- "stroke-width": "9"
2233
- });
2234
- const bolt = svgElement(document, "path", {
2235
- d: "m119 61-20 45 15 11-13 47 30-56-16-12 4-35Z",
2236
- fill: "url(#spotpatch-brand-gradient)"
2237
- });
2238
- svg.append(definitions, outer, topStem, sideStems, brackets, bolt);
2381
+ svg.innerHTML = resolved.content;
2239
2382
  return svg;
2240
2383
  }
2241
2384
 
@@ -2250,32 +2393,51 @@ function clamp(value, minimum, maximum) {
2250
2393
  function calculateDialogPlacement({
2251
2394
  dialogHeight,
2252
2395
  dialogWidth,
2253
- target,
2396
+ target: target2,
2254
2397
  viewportHeight,
2255
2398
  viewportWidth
2256
2399
  }) {
2257
2400
  const maxLeft = viewportWidth - dialogWidth - VIEWPORT_MARGIN;
2258
2401
  const maxTop = viewportHeight - dialogHeight - VIEWPORT_MARGIN;
2259
- const targetCenterX = target.x + target.width / 2;
2260
- const targetCenterY = target.y + target.height / 2;
2402
+ const viewportLeft = clamp(
2403
+ (viewportWidth - dialogWidth) / 2,
2404
+ VIEWPORT_MARGIN,
2405
+ maxLeft
2406
+ );
2407
+ const viewportTop = clamp(
2408
+ (viewportHeight - dialogHeight) / 2,
2409
+ VIEWPORT_MARGIN,
2410
+ maxTop
2411
+ );
2412
+ if (target2 === void 0) {
2413
+ return Object.freeze({
2414
+ anchorX: clamp(dialogWidth / 2, ANCHOR_INSET, dialogWidth - ANCHOR_INSET),
2415
+ anchorY: clamp(dialogHeight / 2, ANCHOR_INSET, dialogHeight - ANCHOR_INSET),
2416
+ left: viewportLeft,
2417
+ mode: "viewport",
2418
+ top: viewportTop
2419
+ });
2420
+ }
2421
+ const targetCenterX = target2.x + target2.width / 2;
2422
+ const targetCenterY = target2.y + target2.height / 2;
2261
2423
  const centeredLeft = clamp(targetCenterX - dialogWidth / 2, VIEWPORT_MARGIN, maxLeft);
2262
2424
  const centeredTop = clamp(targetCenterY - dialogHeight / 2, VIEWPORT_MARGIN, maxTop);
2263
2425
  let left = centeredLeft;
2264
2426
  let top = centeredTop;
2265
2427
  let mode = "viewport";
2266
- if (target.width >= dialogWidth + TARGET_INSET * 2 && target.height >= dialogHeight + TARGET_INSET * 2) {
2428
+ if (target2.width >= dialogWidth + TARGET_INSET * 2 && target2.height >= dialogHeight + TARGET_INSET * 2) {
2267
2429
  mode = "center";
2268
- } else if (target.y - dialogHeight - TARGET_GAP >= VIEWPORT_MARGIN) {
2269
- top = target.y - dialogHeight - TARGET_GAP;
2430
+ } else if (target2.y - dialogHeight - TARGET_GAP >= VIEWPORT_MARGIN) {
2431
+ top = target2.y - dialogHeight - TARGET_GAP;
2270
2432
  mode = "above";
2271
- } else if (target.y + target.height + TARGET_GAP + dialogHeight <= viewportHeight - VIEWPORT_MARGIN) {
2272
- top = target.y + target.height + TARGET_GAP;
2433
+ } else if (target2.y + target2.height + TARGET_GAP + dialogHeight <= viewportHeight - VIEWPORT_MARGIN) {
2434
+ top = target2.y + target2.height + TARGET_GAP;
2273
2435
  mode = "below";
2274
- } else if (target.x + target.width + TARGET_GAP + dialogWidth <= viewportWidth - VIEWPORT_MARGIN) {
2275
- left = target.x + target.width + TARGET_GAP;
2436
+ } else if (target2.x + target2.width + TARGET_GAP + dialogWidth <= viewportWidth - VIEWPORT_MARGIN) {
2437
+ left = target2.x + target2.width + TARGET_GAP;
2276
2438
  mode = "right";
2277
- } else if (target.x - dialogWidth - TARGET_GAP >= VIEWPORT_MARGIN) {
2278
- left = target.x - dialogWidth - TARGET_GAP;
2439
+ } else if (target2.x - dialogWidth - TARGET_GAP >= VIEWPORT_MARGIN) {
2440
+ left = target2.x - dialogWidth - TARGET_GAP;
2279
2441
  mode = "left";
2280
2442
  }
2281
2443
  return Object.freeze({
@@ -2594,7 +2756,7 @@ var UI_MESSAGES = Object.freeze({
2594
2756
  noSelectable: "No selectable element was found.",
2595
2757
  allTargetsRemoved: "All targets were removed. Choose an element to continue.",
2596
2758
  targetRemoved: "Selected target removed.",
2597
- detachedTargetRemoved: "A removed page element was dropped from the selection.",
2759
+ detachedTargetPreserved: "The page element was unloaded; its collected context remains selected.",
2598
2760
  contextWarning: "Browser context collection completed with a warning.",
2599
2761
  contextCollected: "Browser context collected.",
2600
2762
  editorOpening: "Opening source\u2026",
@@ -2730,7 +2892,7 @@ var UI_MESSAGES = Object.freeze({
2730
2892
  noSelectable: "\u5F53\u524D\u4F4D\u7F6E\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u5143\u7D20\u3002",
2731
2893
  allTargetsRemoved: "\u5DF2\u79FB\u9664\u5168\u90E8\u76EE\u6807\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u5143\u7D20\u3002",
2732
2894
  targetRemoved: "\u5DF2\u79FB\u9664\u9009\u4E2D\u76EE\u6807\u3002",
2733
- detachedTargetRemoved: "\u9875\u9762\u4E2D\u7684\u76EE\u6807\u5DF2\u5378\u8F7D\uFF0C\u5DF2\u4ECE\u5F53\u524D\u9009\u62E9\u4E2D\u79FB\u9664\u3002",
2895
+ detachedTargetPreserved: "\u9875\u9762\u5143\u7D20\u5DF2\u5378\u8F7D\uFF0C\u5DF2\u91C7\u96C6\u7684\u4E0A\u4E0B\u6587\u4ECD\u4FDD\u7559\u5728\u9009\u62E9\u4E2D\u3002",
2734
2896
  contextWarning: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\uFF0C\u4F46\u5B58\u5728\u8B66\u544A\u3002",
2735
2897
  contextCollected: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\u3002",
2736
2898
  editorOpening: "\u6B63\u5728\u6253\u5F00\u6E90\u7801\u2026\u2026",
@@ -2788,33 +2950,36 @@ function createUiLocalizer(document, preference = "auto") {
2788
2950
  }
2789
2951
 
2790
2952
  // src/ui/runtime-view.ts
2791
- var DIALOG_FALLBACK_WIDTH = 560;
2953
+ var DIALOG_MAX_WIDTH = 460;
2954
+ var DIALOG_MAX_HEIGHT = 620;
2792
2955
  var DIALOG_FALLBACK_HEIGHT = Object.freeze({
2793
- previewing: 640,
2794
- selected: 720
2956
+ previewing: 560,
2957
+ selected: DIALOG_MAX_HEIGHT
2795
2958
  });
2796
2959
  function createStyles(document) {
2797
2960
  const style = document.createElement("style");
2798
2961
  style.textContent = `
2799
2962
  :host {
2800
2963
  all: initial;
2801
- --spotpatch-bg: #11141a;
2802
- --spotpatch-bg-raised: #191d25;
2803
- --spotpatch-bg-input: #0c0f14;
2804
- --spotpatch-border: #303641;
2805
- --spotpatch-border-subtle: #262c35;
2806
- --spotpatch-text: #f4f6fb;
2807
- --spotpatch-text-secondary: #a4adbd;
2808
- --spotpatch-text-muted: #7f899a;
2809
- --spotpatch-accent: #7060ee;
2810
- --spotpatch-accent-strong: #604ee8;
2811
- --spotpatch-accent-cyan: #27b8cf;
2964
+ --spotpatch-bg: #0e0e12;
2965
+ --spotpatch-bg-raised: #17171c;
2966
+ --spotpatch-bg-active: #1c1c22;
2967
+ --spotpatch-bg-input: #0b0b0f;
2968
+ --spotpatch-border: #2a2a32;
2969
+ --spotpatch-border-subtle: #232329;
2970
+ --spotpatch-text: #f2f2f5;
2971
+ --spotpatch-text-secondary: #96969f;
2972
+ --spotpatch-text-muted: #686872;
2973
+ --spotpatch-accent: #8b7bff;
2974
+ --spotpatch-accent-strong: #6a5ce8;
2975
+ --spotpatch-accent-cyan: #52a8ff;
2812
2976
  --spotpatch-danger: #fb7185;
2813
2977
  --spotpatch-success: #34d399;
2814
2978
  --spotpatch-warning: #f59e0b;
2815
- --spotpatch-radius-panel: 24px;
2816
- --spotpatch-radius-card: 15px;
2817
- --spotpatch-shadow-panel: 0 28px 72px rgb(0 0 0 / 38%);
2979
+ --spotpatch-text-on-accent: #0b0b12;
2980
+ --spotpatch-radius-panel: 16px;
2981
+ --spotpatch-radius-card: 10px;
2982
+ --spotpatch-shadow-panel: 0 30px 60px -20px rgb(0 0 0 / 70%);
2818
2983
  color-scheme: dark;
2819
2984
  color: var(--spotpatch-text);
2820
2985
  font-family: Inter, "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
@@ -2934,7 +3099,7 @@ function createStyles(document) {
2934
3099
  left: 16px;
2935
3100
  z-index: ${String(UI_Z_INDEX.controls)};
2936
3101
  box-sizing: border-box;
2937
- width: min(560px, calc(100vw - 32px));
3102
+ width: min(${String(DIALOG_MAX_WIDTH)}px, calc(100vw - 32px));
2938
3103
  color: #edf0f7;
2939
3104
  outline: none;
2940
3105
  filter: drop-shadow(var(--spotpatch-shadow-panel));
@@ -2978,78 +3143,92 @@ function createStyles(document) {
2978
3143
  position: relative;
2979
3144
  z-index: 1;
2980
3145
  display: flex;
2981
- max-height: min(720px, calc(100vh - 32px));
3146
+ box-sizing: border-box;
3147
+ max-height: min(${String(DIALOG_MAX_HEIGHT)}px, calc(100vh - 32px));
2982
3148
  overflow: hidden;
2983
3149
  flex-direction: column;
2984
3150
  border: 1px solid var(--spotpatch-border);
2985
3151
  border-radius: var(--spotpatch-radius-panel);
2986
3152
  background: var(--spotpatch-bg);
2987
- box-shadow: inset 0 1px rgb(255 255 255 / 7%), inset 0 0 0 1px rgb(2 6 23 / 40%);
3153
+ box-shadow: inset 0 1px rgb(255 255 255 / 5%), 0 0 60px -32px rgb(139 123 255 / 20%);
3154
+ }
3155
+ .spotpatch-shell::before {
3156
+ position: absolute;
3157
+ z-index: 2;
3158
+ top: 0;
3159
+ right: 20%;
3160
+ left: 20%;
3161
+ height: 1px;
3162
+ background: linear-gradient(90deg, transparent, var(--spotpatch-accent), var(--spotpatch-accent-cyan), transparent);
3163
+ content: "";
3164
+ opacity: .65;
3165
+ pointer-events: none;
2988
3166
  }
2989
3167
  .spotpatch-header {
2990
3168
  position: relative;
2991
- padding: 20px 22px 18px;
2992
- border-bottom: 1px solid rgb(255 255 255 / 8%);
3169
+ padding: 0 18px 14px;
2993
3170
  }
2994
3171
  .spotpatch-brand-row {
2995
3172
  display: flex;
2996
3173
  align-items: center;
2997
3174
  justify-content: space-between;
2998
- gap: 16px;
2999
- margin-bottom: 18px;
3175
+ gap: 12px;
3176
+ margin: 0 -18px 18px;
3177
+ padding: 14px 18px 13px;
3178
+ border-bottom: 1px solid var(--spotpatch-border-subtle);
3000
3179
  }
3001
3180
  .spotpatch-brand {
3002
3181
  display: inline-flex;
3003
3182
  align-items: center;
3004
- gap: 11px;
3183
+ gap: 10px;
3005
3184
  min-width: 0;
3006
3185
  color: #f8fafc;
3007
3186
  }
3008
3187
  .spotpatch-brand-mark {
3009
- width: 34px;
3010
- height: 34px;
3188
+ width: 30px;
3189
+ height: 30px;
3011
3190
  flex: none;
3012
3191
  filter: drop-shadow(0 6px 12px rgb(76 29 149 / 22%));
3013
3192
  }
3014
3193
  .spotpatch-brand-copy { display: grid; min-width: 0; gap: 1px; }
3015
3194
  .spotpatch-brand-name {
3016
3195
  color: #fff;
3017
- font-size: 15px;
3018
- font-weight: 720;
3196
+ font-size: 13.5px;
3197
+ font-weight: 680;
3019
3198
  letter-spacing: -.01em;
3020
3199
  }
3021
3200
  .spotpatch-brand-context {
3022
3201
  color: #8e98aa;
3023
- font-size: 11px;
3202
+ font-size: 10.5px;
3024
3203
  font-weight: 560;
3025
3204
  letter-spacing: .02em;
3026
3205
  }
3027
3206
  .spotpatch-header-controls {
3028
3207
  display: inline-flex;
3029
3208
  align-items: center;
3030
- gap: 8px;
3209
+ gap: 6px;
3031
3210
  flex: none;
3032
3211
  }
3033
3212
  .spotpatch-repository,
3034
3213
  .spotpatch-locale,
3035
3214
  .spotpatch-close {
3036
- height: 30px;
3215
+ height: 27px;
3037
3216
  border: 1px solid rgb(255 255 255 / 11%);
3038
- border-radius: 9px;
3039
- background: rgb(255 255 255 / 4%);
3217
+ border-radius: 7px;
3218
+ background: transparent;
3040
3219
  }
3041
3220
  .spotpatch-repository {
3042
3221
  display: inline-flex;
3043
3222
  align-items: center;
3044
- padding: 0 10px;
3045
- color: #bcecf2;
3223
+ padding: 0 9px;
3224
+ color: var(--spotpatch-text-secondary);
3046
3225
  font-size: 11px;
3047
3226
  font-weight: 680;
3048
3227
  text-decoration: none;
3049
3228
  }
3050
3229
  .spotpatch-locale {
3051
- min-width: 38px;
3052
- padding: 0 9px;
3230
+ min-width: 34px;
3231
+ padding: 0 8px;
3053
3232
  color: #c5cad5;
3054
3233
  cursor: pointer;
3055
3234
  font-size: 12px;
@@ -3057,12 +3236,12 @@ function createStyles(document) {
3057
3236
  }
3058
3237
  .spotpatch-close {
3059
3238
  display: inline-grid;
3060
- width: 30px;
3239
+ width: 27px;
3061
3240
  padding: 0;
3062
3241
  place-items: center;
3063
3242
  color: #9da5b4;
3064
3243
  cursor: pointer;
3065
- font-size: 17px;
3244
+ font-size: 15px;
3066
3245
  line-height: 1;
3067
3246
  transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
3068
3247
  }
@@ -3072,30 +3251,30 @@ function createStyles(document) {
3072
3251
  .spotpatch-title {
3073
3252
  margin: 0;
3074
3253
  color: #fff;
3075
- font-size: 24px;
3076
- font-weight: 730;
3077
- letter-spacing: -.025em;
3254
+ font-size: 18px;
3255
+ font-weight: 680;
3256
+ letter-spacing: -.015em;
3078
3257
  }
3079
3258
  .spotpatch-subtitle {
3080
- max-width: 470px;
3081
- margin: 6px 0 0;
3082
- color: #9ca5b5;
3083
- font-size: 15px;
3084
- line-height: 1.55;
3259
+ max-width: 390px;
3260
+ margin: 4px 0 0;
3261
+ color: var(--spotpatch-text-secondary);
3262
+ font-size: 12.5px;
3263
+ line-height: 1.5;
3085
3264
  }
3086
3265
  .spotpatch-target-row {
3087
3266
  display: flex;
3088
3267
  align-items: center;
3089
3268
  gap: 8px;
3090
3269
  min-width: 0;
3091
- margin-top: 16px;
3270
+ margin-top: 14px;
3092
3271
  }
3093
3272
  .spotpatch-target-label {
3094
3273
  min-width: 0;
3095
3274
  overflow: hidden;
3096
3275
  border: 1px solid rgb(129 112 247 / 24%);
3097
3276
  border-radius: 999px;
3098
- padding: 6px 10px;
3277
+ padding: 4px 10px;
3099
3278
  color: #cdd2ff;
3100
3279
  background: rgb(109 93 246 / 10%);
3101
3280
  font: 600 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -3108,7 +3287,7 @@ function createStyles(document) {
3108
3287
  gap: 6px;
3109
3288
  flex: none;
3110
3289
  color: #a5adba;
3111
- font-size: 12px;
3290
+ font-size: 11px;
3112
3291
  white-space: nowrap;
3113
3292
  }
3114
3293
  .spotpatch-context-state::before {
@@ -3129,7 +3308,7 @@ function createStyles(document) {
3129
3308
  .spotpatch-body {
3130
3309
  min-height: 0;
3131
3310
  overflow: auto;
3132
- padding: 20px 22px 22px;
3311
+ padding: 0 18px 14px;
3133
3312
  scrollbar-color: rgb(100 116 139 / 50%) transparent;
3134
3313
  scrollbar-width: thin;
3135
3314
  }
@@ -3140,36 +3319,54 @@ function createStyles(document) {
3140
3319
  display: flex;
3141
3320
  align-items: center;
3142
3321
  justify-content: space-between;
3143
- gap: 12px;
3144
- margin-bottom: 7px;
3145
- color: #eef0f5;
3146
- font-size: 14px;
3147
- font-weight: 680;
3322
+ gap: 10px;
3323
+ margin-bottom: 8px;
3324
+ color: var(--spotpatch-text-secondary);
3325
+ font-size: 11px;
3326
+ font-weight: 650;
3327
+ letter-spacing: .04em;
3328
+ text-transform: uppercase;
3148
3329
  }
3149
3330
  .spotpatch-targets-meta {
3150
3331
  display: flex;
3151
3332
  align-items: center;
3152
- justify-content: space-between;
3153
- gap: 12px;
3154
- margin-bottom: 12px;
3333
+ gap: 8px;
3334
+ min-width: 0;
3335
+ letter-spacing: 0;
3336
+ text-transform: none;
3155
3337
  }
3156
- .spotpatch-target-complete { color: var(--spotpatch-text-secondary); font-size: 12px; font-weight: 560; }
3338
+ .spotpatch-target-complete { color: var(--spotpatch-text-muted); font-size: 11px; font-weight: 560; }
3157
3339
  .spotpatch-target-budget {
3158
3340
  color: var(--spotpatch-text-muted);
3159
- font: 550 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3341
+ font: 550 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3160
3342
  }
3343
+ .spotpatch-target-budget[data-state="ready"] { display: none; }
3161
3344
  .spotpatch-target-budget[data-state="over"] { color: #fda4af; }
3162
3345
  .spotpatch-target-count {
3346
+ margin-left: auto;
3163
3347
  border-radius: 999px;
3164
3348
  padding: 3px 8px;
3165
- color: #b8bffd;
3166
- background: rgb(109 93 246 / 11%);
3167
- font: 650 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3349
+ color: var(--spotpatch-text-muted);
3350
+ font: 600 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3351
+ }
3352
+ .spotpatch-target-progress {
3353
+ height: 3px;
3354
+ margin-bottom: 13px;
3355
+ overflow: hidden;
3356
+ border-radius: 999px;
3357
+ background: var(--spotpatch-border-subtle);
3358
+ }
3359
+ .spotpatch-target-progress-fill {
3360
+ width: 0;
3361
+ height: 100%;
3362
+ border-radius: inherit;
3363
+ background: linear-gradient(90deg, var(--spotpatch-accent), var(--spotpatch-accent-cyan));
3364
+ transition: width 180ms ease;
3168
3365
  }
3169
3366
  .spotpatch-target-list {
3170
3367
  display: grid;
3171
- gap: 12px;
3172
- max-height: min(390px, 48vh);
3368
+ gap: 8px;
3369
+ max-height: min(340px, 48vh);
3173
3370
  overflow: auto;
3174
3371
  padding-right: 2px;
3175
3372
  }
@@ -3177,30 +3374,30 @@ function createStyles(document) {
3177
3374
  overflow: hidden;
3178
3375
  border: 1px solid var(--spotpatch-border-subtle);
3179
3376
  border-radius: var(--spotpatch-radius-card);
3180
- background: rgb(255 255 255 / 2.7%);
3377
+ background: var(--spotpatch-bg-raised);
3181
3378
  transition: border-color 150ms ease, background 150ms ease;
3182
3379
  }
3183
3380
  .spotpatch-target-item[data-active="true"] {
3184
- border-color: rgb(125 106 248 / 42%);
3185
- background: rgb(109 93 246 / 6%);
3186
- box-shadow: inset 3px 0 var(--spotpatch-accent-strong);
3381
+ border-color: rgb(139 123 255 / 34%);
3382
+ background: var(--spotpatch-bg-active);
3383
+ box-shadow: 0 0 0 3px rgb(139 123 255 / 10%);
3187
3384
  }
3188
3385
  .spotpatch-target-summary {
3189
3386
  display: grid;
3190
- grid-template-columns: minmax(0, 1fr) 34px 34px;
3387
+ grid-template-columns: minmax(0, 1fr) 28px 28px;
3191
3388
  align-items: stretch;
3192
- gap: 4px;
3193
- padding: 5px;
3389
+ gap: 2px;
3390
+ padding: 4px;
3194
3391
  }
3195
3392
  .spotpatch-target-select {
3196
3393
  display: grid;
3197
- grid-template-columns: 34px minmax(0, 1fr) auto;
3394
+ grid-template-columns: 24px minmax(0, 1fr) auto;
3198
3395
  align-items: center;
3199
- gap: 11px;
3396
+ gap: 9px;
3200
3397
  min-width: 0;
3201
3398
  border: 0;
3202
- border-radius: 11px;
3203
- padding: 10px 11px;
3399
+ border-radius: 8px;
3400
+ padding: 8px;
3204
3401
  color: inherit;
3205
3402
  background: transparent;
3206
3403
  cursor: pointer;
@@ -3209,14 +3406,14 @@ function createStyles(document) {
3209
3406
  .spotpatch-target-select:hover { background: rgb(255 255 255 / 3.5%); }
3210
3407
  .spotpatch-target-index {
3211
3408
  display: inline-grid;
3212
- width: 30px;
3213
- height: 30px;
3409
+ width: 22px;
3410
+ height: 22px;
3214
3411
  place-items: center;
3215
3412
  border: 1px solid rgb(68 202 224 / 25%);
3216
- border-radius: 9px;
3217
- color: #9ce7f0;
3218
- background: rgb(8 145 178 / 10%);
3219
- font: 700 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
3413
+ border-radius: 6px;
3414
+ color: var(--spotpatch-text-secondary);
3415
+ background: var(--spotpatch-bg);
3416
+ font: 650 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
3220
3417
  }
3221
3418
  .spotpatch-target-copy { min-width: 0; }
3222
3419
  .spotpatch-target-name,
@@ -3226,14 +3423,14 @@ function createStyles(document) {
3226
3423
  text-overflow: ellipsis;
3227
3424
  white-space: nowrap;
3228
3425
  }
3229
- .spotpatch-target-name { color: #f2f4f8; font-size: 16px; font-weight: 680; }
3230
- .spotpatch-target-source { margin-top: 5px; color: var(--spotpatch-text-muted); font: 500 13px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
3426
+ .spotpatch-target-name { color: #f2f2f5; font-size: 13px; font-weight: 650; }
3427
+ .spotpatch-target-source { margin-top: 1px; color: var(--spotpatch-text-muted); font: 500 10.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
3231
3428
  .spotpatch-target-state {
3232
3429
  border-radius: 999px;
3233
- padding: 4px 8px;
3430
+ padding: 3px 8px;
3234
3431
  color: #a7f3d0;
3235
3432
  background: rgb(16 185 129 / 9%);
3236
- font-size: 11px;
3433
+ font-size: 10.5px;
3237
3434
  font-weight: 650;
3238
3435
  white-space: nowrap;
3239
3436
  }
@@ -3241,13 +3438,13 @@ function createStyles(document) {
3241
3438
  .spotpatch-target-open,
3242
3439
  .spotpatch-target-remove {
3243
3440
  display: inline-grid;
3244
- width: 34px;
3441
+ width: 28px;
3245
3442
  height: 100%;
3246
- min-height: 40px;
3443
+ min-height: 34px;
3247
3444
  padding: 0;
3248
3445
  place-items: center;
3249
3446
  border: 1px solid transparent;
3250
- border-radius: 10px;
3447
+ border-radius: 7px;
3251
3448
  color: #7f899a;
3252
3449
  background: transparent;
3253
3450
  cursor: pointer;
@@ -3256,31 +3453,30 @@ function createStyles(document) {
3256
3453
  .spotpatch-target-open:hover:not(:disabled) { border-color: rgb(68 202 224 / 30%); color: #c8f7ff; background: rgb(8 145 178 / 11%); }
3257
3454
  .spotpatch-target-remove:hover:not(:disabled) { border-color: rgb(251 113 133 / 24%); color: #fda4af; background: rgb(127 29 29 / 12%); }
3258
3455
  .spotpatch-target-editor {
3259
- border-top: 1px solid rgb(255 255 255 / 7%);
3260
- padding: 18px;
3456
+ padding: 0 12px 12px;
3261
3457
  }
3262
3458
  .spotpatch-target-editor-head {
3263
3459
  display: flex;
3264
3460
  align-items: baseline;
3265
3461
  justify-content: space-between;
3266
3462
  gap: 12px;
3267
- margin-bottom: 9px;
3463
+ margin-bottom: 6px;
3268
3464
  }
3269
- .spotpatch-target-editor-label { color: #dfe3ea; font-size: 15px; font-weight: 650; }
3270
- .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }
3465
+ .spotpatch-target-editor-label { color: var(--spotpatch-text-secondary); font-size: 11px; font-weight: 650; }
3466
+ .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 10.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }
3271
3467
  .spotpatch-target-editor textarea {
3272
3468
  box-sizing: border-box;
3273
3469
  width: 100%;
3274
- min-height: 126px;
3470
+ min-height: 74px;
3275
3471
  resize: vertical;
3276
3472
  border: 1px solid var(--spotpatch-border);
3277
- border-radius: 12px;
3278
- padding: 14px 15px;
3473
+ border-radius: 7px;
3474
+ padding: 9px 10px;
3279
3475
  color: #f8fafc;
3280
3476
  caret-color: #8b7cf7;
3281
3477
  background: var(--spotpatch-bg-input);
3282
- font-size: 15px;
3283
- line-height: 1.65;
3478
+ font-size: 12.5px;
3479
+ line-height: 1.55;
3284
3480
  outline: none;
3285
3481
  transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease;
3286
3482
  }
@@ -3289,23 +3485,23 @@ function createStyles(document) {
3289
3485
  .spotpatch-target-editor textarea:focus {
3290
3486
  border-color: rgb(139 124 247 / 68%);
3291
3487
  background: rgb(3 7 18 / 65%);
3292
- box-shadow: 0 0 0 3px rgb(109 93 246 / 10%);
3488
+ box-shadow: 0 0 0 2px rgb(109 93 246 / 10%);
3293
3489
  }
3294
3490
  .spotpatch-diagnostics {
3295
- margin-top: 14px;
3491
+ margin-top: 10px;
3296
3492
  overflow: hidden;
3297
3493
  border: 1px solid rgb(255 255 255 / 8%);
3298
- border-radius: 13px;
3494
+ border-radius: 9px;
3299
3495
  background: rgb(255 255 255 / 2.5%);
3300
3496
  }
3301
3497
  .spotpatch-diagnostics > summary {
3302
3498
  display: flex;
3303
3499
  align-items: center;
3304
3500
  gap: 8px;
3305
- padding: 11px 13px;
3501
+ padding: 9px 11px;
3306
3502
  color: #bfc5d0;
3307
3503
  cursor: pointer;
3308
- font-size: 12px;
3504
+ font-size: 11px;
3309
3505
  font-weight: 650;
3310
3506
  list-style: none;
3311
3507
  user-select: none;
@@ -3334,9 +3530,9 @@ function createStyles(document) {
3334
3530
  margin: 0;
3335
3531
  overflow: auto;
3336
3532
  border-top: 1px solid rgb(255 255 255 / 7%);
3337
- padding: 12px 13px 13px;
3533
+ padding: 10px 11px 11px;
3338
3534
  color: #9ca5b5;
3339
- font: 11.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
3535
+ font: 10.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
3340
3536
  overflow-wrap: anywhere;
3341
3537
  white-space: pre-wrap;
3342
3538
  user-select: text;
@@ -3346,39 +3542,44 @@ function createStyles(document) {
3346
3542
  margin: 0;
3347
3543
  overflow: auto;
3348
3544
  border: 1px solid rgb(255 255 255 / 9%);
3349
- border-radius: 14px;
3350
- padding: 13px;
3545
+ border-radius: 9px;
3546
+ padding: 11px;
3351
3547
  color: #d8ddeb;
3352
3548
  background: rgb(3 7 18 / 55%);
3353
- font: 12px/1.65 ui-monospace, SFMono-Regular, Menlo, monospace;
3549
+ font: 11px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
3354
3550
  overflow-wrap: anywhere;
3355
3551
  white-space: pre-wrap;
3356
3552
  user-select: text;
3357
3553
  }
3358
3554
  .spotpatch-actions {
3359
- display: flex;
3360
- flex-wrap: wrap;
3361
- gap: 9px;
3362
- padding: 14px 22px 18px;
3555
+ display: grid;
3556
+ gap: 8px;
3557
+ padding: 10px 18px 14px;
3363
3558
  border-top: 1px solid rgb(255 255 255 / 8%);
3364
- background: rgb(8 12 22 / 82%);
3559
+ background: rgb(8 8 12 / 86%);
3365
3560
  }
3366
3561
  .spotpatch-editor-feedback {
3367
- flex-basis: 100%;
3368
3562
  white-space: normal;
3369
3563
  }
3564
+ .spotpatch-secondary-actions,
3565
+ .spotpatch-primary-actions {
3566
+ display: flex;
3567
+ align-items: center;
3568
+ gap: 7px;
3569
+ }
3570
+ .spotpatch-secondary-actions { min-height: 30px; }
3370
3571
  .spotpatch-actions button {
3371
3572
  display: inline-flex;
3372
- min-height: 40px;
3573
+ min-height: 34px;
3373
3574
  align-items: center;
3374
3575
  justify-content: center;
3375
3576
  border: 1px solid rgb(255 255 255 / 10%);
3376
- border-radius: 11px;
3377
- padding: 9px 13px;
3378
- color: #d8ddeb;
3577
+ border-radius: 9px;
3578
+ padding: 7px 11px;
3579
+ color: var(--spotpatch-text-secondary);
3379
3580
  background: rgb(255 255 255 / 4%);
3380
3581
  cursor: pointer;
3381
- font-size: 13px;
3582
+ font-size: 12px;
3382
3583
  font-weight: 650;
3383
3584
  transition: border-color 150ms ease, box-shadow 150ms ease, color 150ms ease, transform 150ms ease;
3384
3585
  }
@@ -3388,14 +3589,36 @@ function createStyles(document) {
3388
3589
  transform: translateY(-1px);
3389
3590
  }
3390
3591
  .spotpatch-actions .spotpatch-primary {
3391
- min-width: 154px;
3592
+ min-width: 138px;
3392
3593
  flex: 1;
3393
- border-color: rgb(139 124 247 / 58%);
3394
- color: #fff;
3395
- background: var(--spotpatch-accent-strong);
3396
- box-shadow: 0 9px 24px rgb(55 48 163 / 20%), inset 0 1px rgb(255 255 255 / 14%);
3594
+ border-color: transparent;
3595
+ color: var(--spotpatch-text-on-accent);
3596
+ background: linear-gradient(135deg, var(--spotpatch-accent), var(--spotpatch-accent-strong));
3597
+ box-shadow: 0 8px 20px -8px rgb(139 123 255 / 55%);
3397
3598
  }
3599
+ .spotpatch-actions .spotpatch-primary:hover:not(:disabled) { color: var(--spotpatch-text-on-accent); filter: brightness(1.08); }
3398
3600
  .spotpatch-actions .spotpatch-primary::after { margin-left: 8px; content: "\u2197"; font-size: 12px; }
3601
+ .spotpatch-actions .spotpatch-secondary-action {
3602
+ min-height: 30px;
3603
+ border-style: dashed;
3604
+ padding: 5px 10px;
3605
+ color: var(--spotpatch-text-muted);
3606
+ background: transparent;
3607
+ font-size: 11px;
3608
+ }
3609
+ .spotpatch-actions .spotpatch-icon-action {
3610
+ width: 34px;
3611
+ min-width: 34px;
3612
+ padding: 0;
3613
+ font-size: 0;
3614
+ }
3615
+ .spotpatch-actions .spotpatch-icon-action::before {
3616
+ color: var(--spotpatch-text-secondary);
3617
+ content: attr(data-compact-icon);
3618
+ font-size: 18px;
3619
+ font-weight: 400;
3620
+ line-height: 1;
3621
+ }
3399
3622
  .spotpatch-actions button:disabled { cursor: not-allowed; opacity: .4; transform: none; }
3400
3623
  .spotpatch-actions button:focus-visible,
3401
3624
  .spotpatch-repository:focus-visible,
@@ -3427,11 +3650,13 @@ function createStyles(document) {
3427
3650
  }
3428
3651
  @media (max-width: 520px) {
3429
3652
  .spotpatch-dialog { top: 8px; left: 8px; width: calc(100vw - 16px); }
3430
- .spotpatch-shell { max-height: calc(100vh - 16px); border-radius: 18px; }
3431
- .spotpatch-header, .spotpatch-body { padding-left: 16px; padding-right: 16px; }
3432
- .spotpatch-actions { padding-left: 16px; padding-right: 16px; }
3433
- .spotpatch-target-select { grid-template-columns: 32px minmax(0, 1fr); }
3653
+ .spotpatch-shell { max-height: calc(100vh - 16px); border-radius: 14px; }
3654
+ .spotpatch-header, .spotpatch-body { padding-left: 14px; padding-right: 14px; }
3655
+ .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 14px; padding-left: 14px; }
3656
+ .spotpatch-actions { padding-right: 14px; padding-left: 14px; }
3657
+ .spotpatch-target-select { grid-template-columns: 24px minmax(0, 1fr); }
3434
3658
  .spotpatch-target-state { display: none; }
3659
+ .spotpatch-repository { display: none; }
3435
3660
  }
3436
3661
  ${AGENT_PANEL_STYLES}
3437
3662
  `;
@@ -3534,10 +3759,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3534
3759
  const targetCount = createMarkedElement(document, "span");
3535
3760
  targetCount.className = "spotpatch-target-count";
3536
3761
  targetsMeta.append(targetComplete, targetBudget);
3537
- targetsHeading.append(targetsTitle, targetCount);
3762
+ targetRow.append(targetCount);
3763
+ targetsHeading.append(targetsTitle, targetsMeta);
3764
+ const targetProgress = createMarkedElement(document, "div");
3765
+ targetProgress.className = "spotpatch-target-progress";
3766
+ targetProgress.setAttribute("aria-hidden", "true");
3767
+ const targetProgressFill = createMarkedElement(document, "div");
3768
+ targetProgressFill.className = "spotpatch-target-progress-fill";
3769
+ targetProgress.append(targetProgressFill);
3538
3770
  const targetList = createMarkedElement(document, "div");
3539
3771
  targetList.className = "spotpatch-target-list";
3540
- targetsPanel.append(targetsHeading, targetsMeta, targetList);
3772
+ targetsPanel.append(targetsHeading, targetProgress, targetList);
3541
3773
  const diagnostics = createMarkedElement(document, "details");
3542
3774
  diagnostics.className = "spotpatch-diagnostics";
3543
3775
  const diagnosticsLabel = createMarkedElement(document, "summary");
@@ -3578,21 +3810,30 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3578
3810
  );
3579
3811
  const copyButton = createButton(document, messages.actions.copy, "spotpatch-primary");
3580
3812
  const backButton = createButton(document, messages.actions.back);
3581
- actions.append(
3582
- editorFeedback,
3813
+ const secondaryActions = createMarkedElement(document, "div");
3814
+ secondaryActions.className = "spotpatch-secondary-actions";
3815
+ const primaryActions = createMarkedElement(document, "div");
3816
+ primaryActions.className = "spotpatch-primary-actions";
3817
+ addTargetButton.classList.add("spotpatch-icon-action");
3818
+ addTargetButton.dataset.compactIcon = "+";
3819
+ agentPanel.testButton.classList.add("spotpatch-icon-action");
3820
+ agentPanel.testButton.dataset.compactIcon = "\u2713";
3821
+ openEditorButton.classList.add("spotpatch-secondary-action");
3822
+ reselectButton.classList.add("spotpatch-secondary-action");
3823
+ secondaryActions.append(openEditorButton, reselectButton);
3824
+ primaryActions.append(
3825
+ agentPanel.testButton,
3826
+ addTargetButton,
3583
3827
  agentPanel.runButton,
3584
3828
  previewButton,
3585
- agentPanel.testButton,
3586
3829
  agentPanel.cancelButton,
3587
3830
  agentPanel.applyButton,
3588
3831
  agentPanel.revertButton,
3589
3832
  agentPanel.resetButton,
3590
- addTargetButton,
3591
- openEditorButton,
3592
- reselectButton,
3593
3833
  copyButton,
3594
3834
  backButton
3595
3835
  );
3836
+ actions.append(editorFeedback, secondaryActions, primaryActions);
3596
3837
  shell.append(header, body, actions);
3597
3838
  dialog.append(anchor, shell);
3598
3839
  const liveRegion = createMarkedElement(document, "div");
@@ -3625,11 +3866,15 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3625
3866
  editorFeedback.textContent = state === "opening" ? messages.announcements.editorOpening : state === "success" ? messages.announcements.editorOpened : state === "error" ? messages.announcements.editorFailed : "";
3626
3867
  placeDialog();
3627
3868
  }
3628
- function statusText(target) {
3629
- if (target.status === "ready") {
3869
+ function statusText(target2) {
3870
+ if (target2.status === "ready") {
3630
3871
  return messages.targets.statusReady;
3631
3872
  }
3632
- return target.status === "warning" ? messages.targets.statusPartial : messages.targets.statusCollecting;
3873
+ return target2.status === "warning" ? messages.targets.statusPartial : messages.targets.statusCollecting;
3874
+ }
3875
+ function renderTargetProgress(complete, total) {
3876
+ const ratio = total === 0 ? 0 : complete / total;
3877
+ targetProgressFill.style.width = `${String(ratio * 100)}%`;
3633
3878
  }
3634
3879
  function instructionInput(targetId) {
3635
3880
  const inputs = targetList.querySelectorAll(
@@ -3646,16 +3891,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3646
3891
  const focusedTargetId = focusedInstruction?.dataset.targetInstructionId;
3647
3892
  const selectionStart = focusedInstruction?.selectionStart;
3648
3893
  const selectionEnd = focusedInstruction?.selectionEnd;
3649
- currentTargets = targets.map((target) => Object.freeze({ ...target }));
3894
+ currentTargets = targets.map((target2) => Object.freeze({ ...target2 }));
3650
3895
  currentMaximum = maximum;
3651
3896
  targetList.replaceChildren();
3652
3897
  const completeCount = targets.filter(
3653
- (target) => target.instruction.trim().length > 0
3898
+ (target2) => target2.instruction.trim().length > 0
3654
3899
  ).length;
3655
3900
  const instructionCharacters = targets.reduce(
3656
- (total, target) => total + target.instruction.trim().length,
3901
+ (total, target2) => total + target2.instruction.trim().length,
3657
3902
  0
3658
3903
  );
3904
+ renderTargetProgress(completeCount, targets.length);
3659
3905
  targetCount.textContent = messages.targets.count(targets.length, maximum);
3660
3906
  targetComplete.textContent = messages.targets.complete(
3661
3907
  completeCount,
@@ -3673,18 +3919,18 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3673
3919
  addTargetButton.disabled = !editingEnabled || targets.length >= maximum;
3674
3920
  addTargetButton.title = targets.length >= maximum ? messages.targets.limitTitle(maximum) : messages.targets.addTitle;
3675
3921
  targetLabel.textContent = targets.length === 1 ? targets[0]?.label ?? messages.context.selectedElement : messages.context.selectedCount(targets.length);
3676
- for (const [index, target] of targets.entries()) {
3922
+ for (const [index, target2] of targets.entries()) {
3677
3923
  const item = createMarkedElement(document, "div");
3678
3924
  item.className = "spotpatch-target-item";
3679
- item.dataset.active = String(target.active);
3680
- item.dataset.status = target.status;
3681
- item.dataset.targetId = target.id;
3925
+ item.dataset.active = String(target2.active);
3926
+ item.dataset.status = target2.status;
3927
+ item.dataset.targetId = target2.id;
3682
3928
  const targetSummary = createMarkedElement(document, "div");
3683
3929
  targetSummary.className = "spotpatch-target-summary";
3684
3930
  const select = createButton(document, "", "spotpatch-target-select");
3685
- select.dataset.activateTargetId = target.id;
3931
+ select.dataset.activateTargetId = target2.id;
3686
3932
  select.setAttribute("aria-label", messages.targets.activate(index + 1));
3687
- select.setAttribute("aria-expanded", String(target.active));
3933
+ select.setAttribute("aria-expanded", String(target2.active));
3688
3934
  const number = createMarkedElement(document, "span");
3689
3935
  number.className = "spotpatch-target-index";
3690
3936
  number.textContent = String(index + 1);
@@ -3692,54 +3938,54 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3692
3938
  copy.className = "spotpatch-target-copy";
3693
3939
  const name = createMarkedElement(document, "span");
3694
3940
  name.className = "spotpatch-target-name";
3695
- name.textContent = target.label;
3696
- const source = createMarkedElement(document, "span");
3697
- source.className = "spotpatch-target-source";
3698
- source.textContent = `${statusText(target)} \xB7 ${target.source}`;
3699
- copy.append(name, source);
3700
- const instructionComplete = target.instruction.trim().length > 0;
3941
+ name.textContent = target2.label;
3942
+ const source2 = createMarkedElement(document, "span");
3943
+ source2.className = "spotpatch-target-source";
3944
+ source2.textContent = `${statusText(target2)} \xB7 ${target2.source}`;
3945
+ copy.append(name, source2);
3946
+ const instructionComplete = target2.instruction.trim().length > 0;
3701
3947
  const state = createMarkedElement(document, "span");
3702
3948
  state.className = "spotpatch-target-state";
3703
3949
  state.dataset.complete = String(instructionComplete);
3704
3950
  state.textContent = instructionComplete ? messages.targets.instructionReady : messages.targets.instructionMissing;
3705
3951
  select.append(number, copy, state);
3706
3952
  const open = createButton(document, "\u2197", "spotpatch-target-open");
3707
- open.dataset.openTargetId = target.id;
3708
- open.disabled = !target.canOpenEditor;
3953
+ open.dataset.openTargetId = target2.id;
3954
+ open.disabled = !target2.canOpenEditor;
3709
3955
  open.setAttribute("aria-label", messages.actions.openTarget(index + 1));
3710
3956
  open.title = messages.actions.openTarget(index + 1);
3711
3957
  const remove = createButton(document, "\xD7", "spotpatch-target-remove");
3712
- remove.dataset.removeTargetId = target.id;
3958
+ remove.dataset.removeTargetId = target2.id;
3713
3959
  remove.disabled = !editingEnabled;
3714
3960
  remove.setAttribute("aria-label", messages.targets.remove(index + 1));
3715
3961
  remove.title = messages.targets.removeTitle;
3716
3962
  targetSummary.append(select, open, remove);
3717
3963
  item.append(targetSummary);
3718
- if (target.active) {
3964
+ if (target2.active) {
3719
3965
  const editor = createMarkedElement(document, "label");
3720
3966
  editor.className = "spotpatch-target-editor";
3721
3967
  const editorHead = createMarkedElement(document, "span");
3722
3968
  editorHead.className = "spotpatch-target-editor-head";
3723
3969
  const editorLabel = createMarkedElement(document, "span");
3724
3970
  editorLabel.className = "spotpatch-target-editor-label";
3725
- editorLabel.textContent = messages.targets.instructionLabel(target.label);
3971
+ editorLabel.textContent = messages.targets.instructionLabel(target2.label);
3726
3972
  const characterCount = createMarkedElement(document, "span");
3727
3973
  characterCount.className = "spotpatch-target-editor-count";
3728
3974
  characterCount.textContent = messages.targets.instructionCount(
3729
- target.instruction.length,
3975
+ target2.instruction.length,
3730
3976
  MAX_TARGET_INSTRUCTION_CHARACTERS2
3731
3977
  );
3732
3978
  editorHead.append(editorLabel, characterCount);
3733
3979
  const input = createMarkedElement(document, "textarea");
3734
3980
  input.rows = 4;
3735
3981
  input.maxLength = MAX_TARGET_INSTRUCTION_CHARACTERS2;
3736
- input.value = target.instruction;
3982
+ input.value = target2.instruction;
3737
3983
  input.placeholder = messages.targets.instructionPlaceholder;
3738
3984
  input.disabled = !editingEnabled;
3739
- input.dataset.targetInstructionId = target.id;
3985
+ input.dataset.targetInstructionId = target2.id;
3740
3986
  input.setAttribute(
3741
3987
  "aria-label",
3742
- messages.targets.instructionLabel(target.label)
3988
+ messages.targets.instructionLabel(target2.label)
3743
3989
  );
3744
3990
  editor.append(editorHead, input);
3745
3991
  item.append(editor);
@@ -3756,7 +4002,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3756
4002
  }
3757
4003
  function updateTargetInstruction(targetId, instruction) {
3758
4004
  currentTargets = currentTargets.map(
3759
- (target) => target.id === targetId ? Object.freeze({ ...target, instruction }) : target
4005
+ (target2) => target2.id === targetId ? Object.freeze({ ...target2, instruction }) : target2
3760
4006
  );
3761
4007
  const item = Array.from(
3762
4008
  targetList.querySelectorAll(".spotpatch-target-item")
@@ -3777,12 +4023,16 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3777
4023
  MAX_TARGET_INSTRUCTION_CHARACTERS2
3778
4024
  );
3779
4025
  }
4026
+ const completeCount = currentTargets.filter(
4027
+ (target2) => target2.instruction.trim().length > 0
4028
+ ).length;
3780
4029
  targetComplete.textContent = messages.targets.complete(
3781
- currentTargets.filter((target) => target.instruction.trim().length > 0).length,
4030
+ completeCount,
3782
4031
  currentTargets.length
3783
4032
  );
4033
+ renderTargetProgress(completeCount, currentTargets.length);
3784
4034
  const instructionCharacters = currentTargets.reduce(
3785
- (total, target) => total + target.instruction.trim().length,
4035
+ (total, target2) => total + target2.instruction.trim().length,
3786
4036
  0
3787
4037
  );
3788
4038
  const instructionLimitExceeded = instructionCharacters > MAX_ANNOTATION_INSTRUCTION_CHARACTERS2;
@@ -3797,25 +4047,25 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3797
4047
  }
3798
4048
  function showSelectionHighlights(targets) {
3799
4049
  selectionHighlights.replaceChildren();
3800
- activeSelectionRect = targets.find((target) => target.active)?.rect;
4050
+ activeSelectionRect = targets.find((target2) => target2.active)?.rect;
3801
4051
  currentRect = activeSelectionRect;
3802
- for (const [index, target] of targets.entries()) {
4052
+ for (const [index, target2] of targets.entries()) {
3803
4053
  const box = createMarkedElement(document, "div");
3804
4054
  box.className = "spotpatch-selection-highlight";
3805
- box.dataset.targetId = target.id;
3806
- box.dataset.active = String(target.active);
3807
- box.style.transform = `translate(${String(target.rect.x)}px, ${String(target.rect.y)}px)`;
3808
- box.style.width = `${String(target.rect.width)}px`;
3809
- box.style.height = `${String(target.rect.height)}px`;
4055
+ box.dataset.targetId = target2.id;
4056
+ box.dataset.active = String(target2.active);
4057
+ box.style.transform = `translate(${String(target2.rect.x)}px, ${String(target2.rect.y)}px)`;
4058
+ box.style.width = `${String(target2.rect.width)}px`;
4059
+ box.style.height = `${String(target2.rect.height)}px`;
3810
4060
  const label = createMarkedElement(document, "span");
3811
- label.textContent = `${String(index + 1)} \xB7 ${target.label}`;
4061
+ label.textContent = `${String(index + 1)} \xB7 ${target2.label}`;
3812
4062
  box.append(label);
3813
4063
  selectionHighlights.append(box);
3814
4064
  }
3815
4065
  placeDialog();
3816
4066
  }
3817
4067
  function placeDialog() {
3818
- if (dialog.hidden || currentRect === void 0) {
4068
+ if (dialog.hidden) {
3819
4069
  return;
3820
4070
  }
3821
4071
  const view = document.defaultView;
@@ -3823,14 +4073,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3823
4073
  const viewportHeight = view?.innerHeight ?? document.documentElement.clientHeight;
3824
4074
  const measured = dialog.getBoundingClientRect();
3825
4075
  const status = currentStatus === "previewing" ? "previewing" : "selected";
3826
- const dialogWidth = measured.width > 0 ? measured.width : Math.min(DIALOG_FALLBACK_WIDTH, viewportWidth - 32);
4076
+ const dialogWidth = measured.width > 0 ? measured.width : Math.min(DIALOG_MAX_WIDTH, viewportWidth - 32);
3827
4077
  const dialogHeight = measured.height > 0 ? measured.height : Math.min(DIALOG_FALLBACK_HEIGHT[status], viewportHeight - 32);
3828
4078
  const placement = calculateDialogPlacement({
3829
- target: currentRect,
3830
4079
  dialogWidth,
3831
4080
  dialogHeight,
3832
4081
  viewportWidth,
3833
- viewportHeight
4082
+ viewportHeight,
4083
+ ...currentRect === void 0 ? {} : { target: currentRect }
3834
4084
  });
3835
4085
  dialog.style.left = `${String(placement.left)}px`;
3836
4086
  dialog.style.top = `${String(placement.top)}px`;
@@ -3840,8 +4090,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3840
4090
  }
3841
4091
  function updateContextOverview(summaryText) {
3842
4092
  const summaryMessages = messages.summary;
3843
- const source = summaryLine(summaryText, summaryMessages.source);
3844
- sourcePeek.textContent = source ?? messages.diagnostics.noExactSource;
4093
+ const source2 = summaryLine(summaryText, summaryMessages.source);
4094
+ sourcePeek.textContent = source2 ?? messages.diagnostics.noExactSource;
3845
4095
  const browserReady = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.ready}`;
3846
4096
  const browserLoading = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.loading}`;
3847
4097
  const browserFailed = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.failed}`;
@@ -3880,6 +4130,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3880
4130
  addTargetButton.hidden = !selected;
3881
4131
  openEditorButton.hidden = !selected;
3882
4132
  previewButton.hidden = !selected;
4133
+ secondaryActions.hidden = !selected;
3883
4134
  agentPanel.setSelectionVisible(selected);
3884
4135
  copyButton.hidden = !previewing;
3885
4136
  backButton.hidden = !previewing;
@@ -3918,6 +4169,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3918
4169
  } else {
3919
4170
  targetCount.textContent = messages.targets.count(0, currentMaximum);
3920
4171
  targetComplete.textContent = messages.targets.complete(0, 0);
4172
+ renderTargetProgress(0, 0);
3921
4173
  targetBudget.dataset.state = "ready";
3922
4174
  targetBudget.textContent = messages.targets.instructionBudget(
3923
4175
  0,
@@ -3986,6 +4238,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3986
4238
  selectionHighlights.replaceChildren();
3987
4239
  activeSelectionRect = void 0;
3988
4240
  currentRect = void 0;
4241
+ placeDialog();
3989
4242
  },
3990
4243
  showSelection(summaryText, canOpenEditor, canPreview) {
3991
4244
  updateSelection(summaryText, canOpenEditor, canPreview);
@@ -4011,6 +4264,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4011
4264
  renderEditorStatus("idle");
4012
4265
  targetCount.textContent = messages.targets.count(0, 0);
4013
4266
  targetComplete.textContent = messages.targets.complete(0, 0);
4267
+ renderTargetProgress(0, 0);
4014
4268
  targetLabel.textContent = messages.context.selectedElement;
4015
4269
  summary.textContent = "";
4016
4270
  promptOutput.textContent = "";
@@ -4552,25 +4806,25 @@ var EMPTY_STYLE_CONTEXT = Object.freeze({
4552
4806
  computed: Object.freeze({}),
4553
4807
  warnings: Object.freeze(["CSS context collection failed."])
4554
4808
  });
4555
- function elementLabel(element) {
4556
- const id = element.id.length > 0 ? `#${element.id}` : "";
4557
- const className = Array.from(element.classList).slice(0, 2).map((name) => `.${name}`).join("");
4558
- return `<${element.tagName.toLowerCase()}${id}${className}>`;
4559
- }
4560
- function sourceLabel(target, unavailable) {
4561
- const path = target.code?.relativePath ?? target.resolution.source.relativePath;
4562
- const line = target.resolution.source.line;
4563
- const column = target.resolution.source.column;
4809
+ function elementLabel(element2) {
4810
+ const id = element2.id.length > 0 ? `#${element2.id}` : "";
4811
+ const className = Array.from(element2.classList).slice(0, 2).map((name) => `.${name}`).join("");
4812
+ return `<${element2.tagName.toLowerCase()}${id}${className}>`;
4813
+ }
4814
+ function sourceLabel(target2, unavailable) {
4815
+ const path = target2.code?.relativePath ?? target2.resolution.source.relativePath;
4816
+ const line = target2.resolution.source.line;
4817
+ const column = target2.resolution.source.column;
4564
4818
  if (path === void 0) {
4565
4819
  return unavailable;
4566
4820
  }
4567
4821
  return `${path}${line === void 0 ? "" : `:${String(line)}`}${column === void 0 ? "" : `:${String(column)}`}`;
4568
4822
  }
4569
- function targetStatus(target) {
4570
- if (target.apiStatus === "failed" || target.collectionStatus === "failed") {
4823
+ function targetStatus(target2) {
4824
+ if (target2.apiStatus === "failed" || target2.collectionStatus === "failed") {
4571
4825
  return "warning";
4572
4826
  }
4573
- if (target.apiStatus === "loading" || target.collectionStatus === "loading") {
4827
+ if (target2.apiStatus === "loading" || target2.collectionStatus === "loading") {
4574
4828
  return "loading";
4575
4829
  }
4576
4830
  return "ready";
@@ -4597,6 +4851,8 @@ function createController(config, dependencies = {}) {
4597
4851
  const clipboard = dependencies.clipboard ?? resolveClipboardWriter(browser.window.navigator);
4598
4852
  const createId = dependencies.createId ?? (() => createBrowserAnnotationId(browser.window));
4599
4853
  const now = dependencies.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
4854
+ const selectionSession = dependencies.selectionSession ?? createSelectionSession(browser.window, config.sessionId, config.maxTargets);
4855
+ const restoredSelection = selectionSession.load();
4600
4856
  const sourceResolver = createSourceResolver({
4601
4857
  adapter: dependencies.reactAdapter ?? createReact18Adapter({
4602
4858
  maxComponentDepth: config.budget.maxComponentDepth
@@ -4614,9 +4870,26 @@ function createController(config, dependencies = {}) {
4614
4870
  let mounted = false;
4615
4871
  let animationFrame;
4616
4872
  let lastPointer;
4617
- let targets = [];
4618
- let activeTargetId;
4619
- let targetSequence = 0;
4873
+ let targets = restoredSelection?.targets.map((target2) => {
4874
+ const marker = sourceRefToMarker(target2.source);
4875
+ return {
4876
+ id: target2.id,
4877
+ resolution: Object.freeze({ source: target2.source, react: target2.react }),
4878
+ apiStatus: marker === void 0 ? "not-required" : target2.code === void 0 ? "failed" : "connected",
4879
+ code: target2.code,
4880
+ collectionStatus: "ready",
4881
+ element: void 0,
4882
+ elementContext: target2.element,
4883
+ instruction: target2.instruction,
4884
+ marker,
4885
+ page: target2.page,
4886
+ styles: target2.styles
4887
+ };
4888
+ }) ?? [];
4889
+ let activeTargetId = restoredSelection?.activeTargetId;
4890
+ let targetSequence = restoredSelection?.sequence ?? 0;
4891
+ let selectionOpen = restoredSelection?.open ?? false;
4892
+ let workflowSelectionActive = false;
4620
4893
  let sessionRevision = 0;
4621
4894
  let editorRequestRevision = 0;
4622
4895
  let addingTarget = false;
@@ -4631,51 +4904,87 @@ function createController(config, dependencies = {}) {
4631
4904
  view.renderStatus(state.status);
4632
4905
  }
4633
4906
  function activeTarget() {
4634
- return targets.find((target) => target.id === activeTargetId) ?? targets.at(-1);
4907
+ return targets.find((target2) => target2.id === activeTargetId) ?? targets.at(-1);
4908
+ }
4909
+ function snapshotTarget(target2) {
4910
+ if (target2.elementContext === void 0 || target2.styles === void 0) {
4911
+ return void 0;
4912
+ }
4913
+ return {
4914
+ id: target2.id,
4915
+ instruction: target2.instruction,
4916
+ page: target2.page,
4917
+ source: target2.resolution.source,
4918
+ react: target2.resolution.react,
4919
+ element: target2.elementContext,
4920
+ styles: target2.styles,
4921
+ ...target2.code === void 0 ? {} : { code: target2.code }
4922
+ };
4635
4923
  }
4636
- function targetIsCurrent(target, revision) {
4637
- return mounted && revision === sessionRevision && state.status !== "idle" && targets.includes(target);
4924
+ function persistSelection() {
4925
+ const persistedTargets = targets.flatMap((target2) => {
4926
+ const snapshot = snapshotTarget(target2);
4927
+ return snapshot === void 0 ? [] : [snapshot];
4928
+ });
4929
+ if (persistedTargets.length === 0) {
4930
+ selectionSession.clear();
4931
+ return;
4932
+ }
4933
+ const persistedIds = new Set(persistedTargets.map(({ id }) => id));
4934
+ selectionSession.save({
4935
+ ...activeTargetId !== void 0 && persistedIds.has(activeTargetId) ? { activeTargetId } : {},
4936
+ open: selectionOpen,
4937
+ sequence: targetSequence,
4938
+ targets: persistedTargets
4939
+ });
4940
+ }
4941
+ function targetIsCurrent(target2, revision) {
4942
+ return mounted && revision === sessionRevision && state.status !== "idle" && targets.includes(target2);
4638
4943
  }
4639
4944
  function canPreview() {
4640
4945
  const instructionCharacters = targets.reduce(
4641
- (total, target) => total + target.instruction.trim().length,
4946
+ (total, target2) => total + target2.instruction.trim().length,
4642
4947
  0
4643
4948
  );
4644
4949
  return targets.length > 0 && instructionCharacters <= MAX_ANNOTATION_INSTRUCTION_CHARACTERS3 && targets.every(
4645
- (target) => target.instruction.trim().length > 0 && target.element !== void 0 && target.elementContext !== void 0 && target.styles !== void 0 && target.apiStatus !== "loading"
4950
+ (target2) => target2.instruction.trim().length > 0 && target2.elementContext !== void 0 && target2.styles !== void 0 && target2.apiStatus !== "loading"
4646
4951
  );
4647
4952
  }
4648
4953
  function selectionSummary() {
4649
4954
  const messages = view.messages().summary;
4650
- return targets.map((target, index) => {
4955
+ return targets.map((target2, index) => {
4651
4956
  const summary = createSelectionSummary(
4652
4957
  {
4653
- resolution: target.resolution,
4654
- ...target.code === void 0 ? {} : { code: target.code },
4655
- ...target.styles === void 0 ? {} : { styles: target.styles },
4656
- apiStatus: target.apiStatus,
4657
- collectionStatus: target.collectionStatus,
4958
+ resolution: target2.resolution,
4959
+ ...target2.code === void 0 ? {} : { code: target2.code },
4960
+ ...target2.styles === void 0 ? {} : { styles: target2.styles },
4961
+ apiStatus: target2.apiStatus,
4962
+ collectionStatus: target2.collectionStatus,
4658
4963
  framework: config.framework,
4659
4964
  frameworkVersion: config.frameworkVersion,
4660
4965
  spotPatchVersion: config.spotPatchVersion
4661
4966
  },
4662
4967
  messages
4663
4968
  );
4664
- return `${messages.target(index + 1, target.id === activeTargetId)}
4969
+ return `${messages.target(index + 1, target2.id === activeTargetId)}
4665
4970
  ${summary}`;
4666
4971
  }).join("\n\n");
4667
4972
  }
4668
4973
  function refreshHighlights() {
4669
- const highlights = targets.flatMap((target) => {
4670
- if (!target.element?.isConnected) {
4974
+ const highlights = targets.flatMap((target2) => {
4975
+ if (!target2.element?.isConnected) {
4976
+ return [];
4977
+ }
4978
+ const rect = getVisibleElementRect(target2.element, browser.window);
4979
+ if (rect === void 0) {
4671
4980
  return [];
4672
4981
  }
4673
4982
  return [
4674
4983
  {
4675
- id: target.id,
4676
- label: elementLabel(target.element),
4677
- rect: getElementRect(target.element, browser.window),
4678
- active: target.id === activeTargetId
4984
+ id: target2.id,
4985
+ label: elementLabel(target2.element),
4986
+ rect,
4987
+ active: target2.id === activeTargetId
4679
4988
  }
4680
4989
  ];
4681
4990
  });
@@ -4690,14 +4999,14 @@ ${summary}`;
4690
4999
  return;
4691
5000
  }
4692
5001
  view.renderTargets(
4693
- targets.map((target) => ({
4694
- id: target.id,
4695
- label: target.resolution.react.componentName ?? (target.element === void 0 ? target.elementContext?.tagName ?? "Element" : elementLabel(target.element)),
4696
- source: sourceLabel(target, view.messages().context.sourceUnavailable),
4697
- canOpenEditor: target.marker !== void 0,
4698
- status: targetStatus(target),
4699
- active: target.id === activeTargetId,
4700
- instruction: target.instruction
5002
+ targets.map((target2) => ({
5003
+ id: target2.id,
5004
+ label: target2.resolution.react.componentName ?? (target2.element === void 0 ? target2.elementContext?.tagName ?? "Element" : elementLabel(target2.element)),
5005
+ source: sourceLabel(target2, view.messages().context.sourceUnavailable),
5006
+ canOpenEditor: target2.marker !== void 0,
5007
+ status: targetStatus(target2),
5008
+ active: target2.id === activeTargetId,
5009
+ instruction: target2.instruction
4701
5010
  })),
4702
5011
  config.maxTargets
4703
5012
  );
@@ -4715,21 +5024,22 @@ ${summary}`;
4715
5024
  if (!canPreview()) {
4716
5025
  return void 0;
4717
5026
  }
4718
- const annotationTargets = targets.map((target) => {
4719
- if (target.elementContext === void 0 || target.styles === void 0) {
5027
+ const annotationTargets = targets.map((target2) => {
5028
+ if (target2.elementContext === void 0 || target2.styles === void 0) {
4720
5029
  throw new Error("SpotPatch attempted to compose an incomplete target.");
4721
5030
  }
4722
5031
  const warnings = [
4723
- ...target.apiStatus === "failed" ? ["Source context could not be loaded."] : [],
4724
- ...target.collectionStatus === "failed" ? ["Part of the browser context could not be collected."] : []
5032
+ ...target2.apiStatus === "failed" ? ["Source context could not be loaded."] : [],
5033
+ ...target2.collectionStatus === "failed" ? ["Part of the browser context could not be collected."] : []
4725
5034
  ];
4726
5035
  return {
4727
- instruction: target.instruction.trim(),
4728
- source: target.resolution.source,
4729
- react: target.resolution.react,
4730
- element: target.elementContext,
4731
- styles: target.styles,
4732
- ...target.code === void 0 ? {} : { code: target.code },
5036
+ instruction: target2.instruction.trim(),
5037
+ page: target2.page,
5038
+ source: target2.resolution.source,
5039
+ react: target2.resolution.react,
5040
+ element: target2.elementContext,
5041
+ styles: target2.styles,
5042
+ ...target2.code === void 0 ? {} : { code: target2.code },
4733
5043
  warnings
4734
5044
  };
4735
5045
  });
@@ -4741,8 +5051,8 @@ ${summary}`;
4741
5051
  targets: annotationTargets
4742
5052
  });
4743
5053
  }
4744
- function showElementHighlight(element) {
4745
- view.showHighlight(getElementRect(element, browser.window), elementLabel(element));
5054
+ function showElementHighlight(element2) {
5055
+ view.showHighlight(getElementRect(element2, browser.window), elementLabel(element2));
4746
5056
  }
4747
5057
  function restoreFocus() {
4748
5058
  if (previousFocus?.isConnected === true) {
@@ -4764,10 +5074,13 @@ ${summary}`;
4764
5074
  resizeObserver?.disconnect();
4765
5075
  targets = [];
4766
5076
  activeTargetId = void 0;
5077
+ selectionOpen = false;
5078
+ workflowSelectionActive = false;
4767
5079
  addingTarget = false;
4768
5080
  previewPrompt = "";
4769
5081
  view.hideSelection();
4770
5082
  view.hideSelectionHighlights();
5083
+ selectionSession.clear();
4771
5084
  restoreFocus();
4772
5085
  }
4773
5086
  function close() {
@@ -4779,7 +5092,11 @@ ${summary}`;
4779
5092
  transition({ type: "BACK" });
4780
5093
  transition({ type: "CLOSE" });
4781
5094
  }
4782
- releaseSelection();
5095
+ selectionOpen = false;
5096
+ persistSelection();
5097
+ view.hideSelection();
5098
+ view.hideSelectionHighlights();
5099
+ restoreFocus();
4783
5100
  view.hideHighlight();
4784
5101
  }
4785
5102
  function cancelAddTarget() {
@@ -4803,6 +5120,18 @@ ${summary}`;
4803
5120
  close();
4804
5121
  return;
4805
5122
  }
5123
+ if (targets.length > 0) {
5124
+ selectionOpen = true;
5125
+ transition({ type: "RESTORE" });
5126
+ if (!workflowSelectionActive) {
5127
+ agentWorkflow.beginSelection();
5128
+ workflowSelectionActive = true;
5129
+ }
5130
+ refreshSelectionView(true);
5131
+ view.focusTargetInstruction(activeTargetId);
5132
+ persistSelection();
5133
+ return;
5134
+ }
4806
5135
  transition({ type: "ACTIVATE" });
4807
5136
  view.hideSelection();
4808
5137
  view.announce(view.messages().announcements.selectionEnabled);
@@ -4836,12 +5165,13 @@ ${summary}`;
4836
5165
  sessionRevision += 1;
4837
5166
  clearCollectionTimers();
4838
5167
  resizeObserver?.disconnect();
4839
- for (const target of targets) {
4840
- target.element = void 0;
5168
+ for (const target2 of targets) {
5169
+ target2.element = void 0;
4841
5170
  }
4842
5171
  view.hideHighlight();
4843
5172
  view.hideSelectionHighlights();
4844
5173
  refreshSelectionView();
5174
+ selectionSession.clear();
4845
5175
  view.announce(view.messages().announcements.appliedTargetsDetached);
4846
5176
  }
4847
5177
  const agentWorkflow = createAgentWorkflow({
@@ -4854,8 +5184,8 @@ ${summary}`;
4854
5184
  },
4855
5185
  view
4856
5186
  });
4857
- async function loadSourceContext(target, revision) {
4858
- const marker = target.marker;
5187
+ async function loadSourceContext(target2, revision) {
5188
+ const marker = target2.marker;
4859
5189
  if (marker === void 0) {
4860
5190
  return;
4861
5191
  }
@@ -4866,16 +5196,18 @@ ${summary}`;
4866
5196
  column: marker.column,
4867
5197
  maxLines: config.budget.maxCodeLines
4868
5198
  });
4869
- if (targetIsCurrent(target, revision)) {
4870
- target.code = context;
4871
- target.apiStatus = "connected";
5199
+ if (targetIsCurrent(target2, revision)) {
5200
+ target2.code = context;
5201
+ target2.apiStatus = "connected";
5202
+ persistSelection();
4872
5203
  refreshSelectionView();
4873
5204
  view.announce(view.messages().announcements.sourceLoaded);
4874
5205
  }
4875
5206
  } catch (error) {
4876
- if (targetIsCurrent(target, revision)) {
4877
- target.code = void 0;
4878
- target.apiStatus = "failed";
5207
+ if (targetIsCurrent(target2, revision)) {
5208
+ target2.code = void 0;
5209
+ target2.apiStatus = "failed";
5210
+ persistSelection();
4879
5211
  refreshSelectionView();
4880
5212
  view.announce(view.messages().announcements.sourceFailed);
4881
5213
  if (config.debug && !(error instanceof DOMException && error.name === "AbortError")) {
@@ -4884,61 +5216,62 @@ ${summary}`;
4884
5216
  }
4885
5217
  }
4886
5218
  }
4887
- function scheduleBrowserContextCollection(target, revision) {
4888
- const element = target.element;
4889
- if (element === void 0) {
5219
+ function scheduleBrowserContextCollection(target2, revision) {
5220
+ const element2 = target2.element;
5221
+ if (element2 === void 0) {
4890
5222
  return;
4891
5223
  }
4892
5224
  const timer = browser.window.setTimeout(() => {
4893
- collectionTimers.delete(target.id);
4894
- if (!targetIsCurrent(target, revision)) {
5225
+ collectionTimers.delete(target2.id);
5226
+ if (!targetIsCurrent(target2, revision)) {
4895
5227
  return;
4896
5228
  }
4897
5229
  let failed = false;
4898
5230
  try {
4899
- target.elementContext = collectElementContext({
4900
- element,
5231
+ target2.elementContext = collectElementContext({
5232
+ element: element2,
4901
5233
  maxCharacters: config.budget.domCharacters
4902
5234
  });
4903
5235
  } catch {
4904
- target.elementContext = void 0;
5236
+ target2.elementContext = void 0;
4905
5237
  failed = true;
4906
5238
  }
4907
5239
  try {
4908
- target.styles = collectStyleContext({
5240
+ target2.styles = collectStyleContext({
4909
5241
  document: browser.document,
4910
- element,
5242
+ element: element2,
4911
5243
  maxCharacters: config.budget.cssCharacters
4912
5244
  });
4913
5245
  } catch {
4914
- target.styles = EMPTY_STYLE_CONTEXT;
5246
+ target2.styles = EMPTY_STYLE_CONTEXT;
4915
5247
  failed = true;
4916
5248
  }
4917
- target.collectionStatus = failed ? "failed" : "ready";
5249
+ target2.collectionStatus = failed ? "failed" : "ready";
5250
+ persistSelection();
4918
5251
  refreshSelectionView();
4919
5252
  view.announce(
4920
5253
  failed ? view.messages().announcements.contextWarning : view.messages().announcements.contextCollected
4921
5254
  );
4922
- if (config.debug && target.styles.warnings.length > 0) {
5255
+ if (config.debug && target2.styles.warnings.length > 0) {
4923
5256
  console.warn(
4924
- `[spotpatch:runtime] CSS collection completed with ${String(target.styles.warnings.length)} warning(s).`
5257
+ `[spotpatch:runtime] CSS collection completed with ${String(target2.styles.warnings.length)} warning(s).`
4925
5258
  );
4926
5259
  }
4927
5260
  }, 0);
4928
- collectionTimers.set(target.id, timer);
5261
+ collectionTimers.set(target2.id, timer);
4929
5262
  }
4930
- function duplicateTarget(element, marker) {
5263
+ function duplicateTarget(element2, marker) {
4931
5264
  if (marker !== void 0) {
4932
5265
  return targets.find(
4933
- (target) => target.marker?.fileId === marker.fileId && target.marker.line === marker.line && target.marker.column === marker.column
5266
+ (target2) => target2.marker?.fileId === marker.fileId && target2.marker.line === marker.line && target2.marker.column === marker.column
4934
5267
  );
4935
5268
  }
4936
- return targets.find((target) => target.element === element);
5269
+ return targets.find((target2) => target2.element === element2);
4937
5270
  }
4938
- function selectElement(element) {
4939
- const resolution = sourceResolver.resolve(element);
5271
+ function selectElement(element2) {
5272
+ const resolution = sourceResolver.resolve(element2);
4940
5273
  const marker = sourceRefToMarker(resolution.source);
4941
- const duplicate = duplicateTarget(element, marker);
5274
+ const duplicate = duplicateTarget(element2, marker);
4942
5275
  if (duplicate !== void 0) {
4943
5276
  activeTargetId = duplicate.id;
4944
5277
  addingTarget = false;
@@ -4962,13 +5295,15 @@ ${summary}`;
4962
5295
  previousFocus = browser.document.activeElement instanceof HTMLElement ? browser.document.activeElement : void 0;
4963
5296
  previewPrompt = "";
4964
5297
  agentWorkflow.beginSelection();
5298
+ workflowSelectionActive = true;
4965
5299
  }
4966
5300
  targetSequence += 1;
4967
- const target = {
5301
+ const target2 = {
4968
5302
  id: `target-${String(targetSequence)}`,
4969
- element,
5303
+ element: element2,
4970
5304
  resolution,
4971
5305
  marker,
5306
+ page: collectPageContext(browser.document, browser.window),
4972
5307
  code: void 0,
4973
5308
  elementContext: void 0,
4974
5309
  styles: void 0,
@@ -4976,18 +5311,19 @@ ${summary}`;
4976
5311
  apiStatus: marker === void 0 ? "not-required" : "loading",
4977
5312
  collectionStatus: "loading"
4978
5313
  };
4979
- targets.push(target);
4980
- activeTargetId = target.id;
5314
+ targets.push(target2);
5315
+ activeTargetId = target2.id;
4981
5316
  addingTarget = false;
4982
5317
  const revision = sessionRevision;
4983
5318
  transition({ type: "SELECT" });
4984
5319
  view.hideHighlight();
4985
- resizeObserver?.observe(element);
5320
+ resizeObserver?.observe(element2);
4986
5321
  refreshSelectionView(true);
4987
- view.focusTargetInstruction(target.id);
4988
- scheduleBrowserContextCollection(target, revision);
5322
+ selectionOpen = true;
5323
+ view.focusTargetInstruction(target2.id);
5324
+ scheduleBrowserContextCollection(target2, revision);
4989
5325
  if (marker !== void 0) {
4990
- void loadSourceContext(target, revision);
5326
+ void loadSourceContext(target2, revision);
4991
5327
  } else {
4992
5328
  view.announce(
4993
5329
  resolution.source.confidence === "probable" ? view.messages().announcements.sourceProbable : view.messages().announcements.sourceMissing
@@ -5061,7 +5397,7 @@ ${summary}`;
5061
5397
  if (state.status !== "selected" && state.status !== "inspecting") {
5062
5398
  return;
5063
5399
  }
5064
- const index = targets.findIndex((target) => target.id === targetId);
5400
+ const index = targets.findIndex((target2) => target2.id === targetId);
5065
5401
  if (index < 0) {
5066
5402
  return;
5067
5403
  }
@@ -5087,10 +5423,12 @@ ${summary}`;
5087
5423
  }
5088
5424
  view.hideSelectionTemporarily();
5089
5425
  view.hideSelectionHighlights();
5426
+ selectionSession.clear();
5090
5427
  view.announce(view.messages().announcements.allTargetsRemoved);
5091
5428
  return;
5092
5429
  }
5093
5430
  refreshSelectionView();
5431
+ persistSelection();
5094
5432
  view.announce(view.messages().announcements.targetRemoved);
5095
5433
  }
5096
5434
  function refreshSelectedGeometry() {
@@ -5098,24 +5436,24 @@ ${summary}`;
5098
5436
  return;
5099
5437
  }
5100
5438
  const disconnected = targets.filter(
5101
- (target) => target.element !== void 0 && !target.element.isConnected
5439
+ (target2) => target2.element !== void 0 && !target2.element.isConnected
5102
5440
  );
5103
5441
  if (disconnected.length > 0) {
5104
- if (state.status === "previewing") {
5105
- transition({ type: "BACK" });
5106
- }
5107
- for (const target of disconnected) {
5108
- removeTarget(target.id);
5109
- }
5110
- if (targets.length > 0) {
5111
- view.announce(view.messages().announcements.detachedTargetRemoved);
5442
+ for (const target2 of disconnected) {
5443
+ if (target2.element !== void 0) {
5444
+ resizeObserver?.unobserve(target2.element);
5445
+ target2.element = void 0;
5446
+ }
5112
5447
  }
5448
+ refreshSelectionView();
5449
+ persistSelection();
5450
+ view.announce(view.messages().announcements.detachedTargetPreserved);
5113
5451
  return;
5114
5452
  }
5115
5453
  refreshHighlights();
5116
5454
  }
5117
5455
  function handleOpenEditor(targetId) {
5118
- const current = targetId === void 0 ? activeTarget() : targets.find((target) => target.id === targetId);
5456
+ const current = targetId === void 0 ? activeTarget() : targets.find((target2) => target2.id === targetId);
5119
5457
  if (state.status !== "selected" || current?.marker === void 0) {
5120
5458
  return;
5121
5459
  }
@@ -5147,13 +5485,14 @@ ${summary}`;
5147
5485
  return;
5148
5486
  }
5149
5487
  const targetId = event.target.dataset.targetInstructionId;
5150
- const target = targets.find((candidate) => candidate.id === targetId);
5151
- if (target === void 0) {
5488
+ const target2 = targets.find((candidate) => candidate.id === targetId);
5489
+ if (target2 === void 0) {
5152
5490
  return;
5153
5491
  }
5154
- target.instruction = event.target.value;
5155
- view.updateTargetInstruction(target.id, target.instruction);
5492
+ target2.instruction = event.target.value;
5493
+ view.updateTargetInstruction(target2.id, target2.instruction);
5156
5494
  view.setPreviewEnabled(canPreview());
5495
+ persistSelection();
5157
5496
  }
5158
5497
  function handleOpenEditorButtonClick() {
5159
5498
  handleOpenEditor();
@@ -5246,7 +5585,7 @@ ${summary}`;
5246
5585
  "button[data-activate-target-id]"
5247
5586
  );
5248
5587
  const targetId = activateButton?.dataset.activateTargetId;
5249
- if (state.status !== "selected" || targetId === void 0 || !targets.some((target) => target.id === targetId)) {
5588
+ if (state.status !== "selected" || targetId === void 0 || !targets.some((target2) => target2.id === targetId)) {
5250
5589
  return;
5251
5590
  }
5252
5591
  activeTargetId = targetId;
@@ -5304,6 +5643,12 @@ ${summary}`;
5304
5643
  subtree: true
5305
5644
  });
5306
5645
  }
5646
+ if (selectionOpen && targets.length > 0) {
5647
+ transition({ type: "RESTORE" });
5648
+ agentWorkflow.beginSelection();
5649
+ workflowSelectionActive = true;
5650
+ refreshSelectionView(true);
5651
+ }
5307
5652
  if (config.debug) {
5308
5653
  console.info(`[spotpatch:runtime] Mounted. Shortcut: ${config.shortcut}.`);
5309
5654
  }
@@ -5361,6 +5706,7 @@ ${summary}`;
5361
5706
  animationFrame = void 0;
5362
5707
  }
5363
5708
  clearCollectionTimers();
5709
+ persistSelection();
5364
5710
  resizeObserver?.disconnect();
5365
5711
  mutationObserver?.disconnect();
5366
5712
  resizeObserver = void 0;
@@ -5383,10 +5729,10 @@ ${summary}`;
5383
5729
  // src/controller/bootstrap.ts
5384
5730
  var RUNTIME_INSTANCE_KEY = "__spotpatchRuntime__";
5385
5731
  function bootstrapSpotPatch(config) {
5386
- const target = globalThis;
5387
- target[RUNTIME_INSTANCE_KEY]?.dispose();
5732
+ const target2 = globalThis;
5733
+ target2[RUNTIME_INSTANCE_KEY]?.dispose();
5388
5734
  const controller = createController(config);
5389
- target[RUNTIME_INSTANCE_KEY] = controller;
5735
+ target2[RUNTIME_INSTANCE_KEY] = controller;
5390
5736
  controller.mount();
5391
5737
  }
5392
5738
  export {