@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.cjs CHANGED
@@ -3,9 +3,9 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
6
+ var __export = (target2, all) => {
7
7
  for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
8
+ __defProp(target2, name, { get: all[name], enumerable: true });
9
9
  };
10
10
  var __copyProps = (to, from, except, desc) => {
11
11
  if (from && typeof from === "object" || typeof from === "function") {
@@ -31,45 +31,46 @@ var import_shared9 = require("@spotpatch/shared");
31
31
 
32
32
  // src/annotation/create-annotation.ts
33
33
  var import_shared = require("@spotpatch/shared");
34
- function freezeSource(source, code) {
34
+ function freezeSource(source2, code2) {
35
35
  return Object.freeze({
36
- ...source,
37
- ...source.relativePath === void 0 && code !== void 0 ? { relativePath: code.relativePath } : {}
36
+ ...source2,
37
+ ...source2.relativePath === void 0 && code2 !== void 0 ? { relativePath: code2.relativePath } : {}
38
38
  });
39
39
  }
40
- function freezeReact(react) {
40
+ function freezeReact(react2) {
41
41
  return Object.freeze({
42
- ...react,
43
- componentStack: Object.freeze([...react.componentStack]),
44
- ...react.source === void 0 ? {} : { source: freezeSource(react.source) }
42
+ ...react2,
43
+ componentStack: Object.freeze([...react2.componentStack]),
44
+ ...react2.source === void 0 ? {} : { source: freezeSource(react2.source) }
45
45
  });
46
46
  }
47
- function freezeElement(element) {
47
+ function freezeElement(element2) {
48
48
  return Object.freeze({
49
- ...element,
50
- rect: Object.freeze({ ...element.rect })
49
+ ...element2,
50
+ rect: Object.freeze({ ...element2.rect })
51
51
  });
52
52
  }
53
- function freezeStyles(styles) {
53
+ function freezeStyles(styles2) {
54
54
  return Object.freeze({
55
- ...styles,
56
- classNames: Object.freeze([...styles.classNames]),
55
+ ...styles2,
56
+ classNames: Object.freeze([...styles2.classNames]),
57
57
  matchedRules: Object.freeze(
58
- styles.matchedRules.map((rule) => Object.freeze({ ...rule }))
58
+ styles2.matchedRules.map((rule) => Object.freeze({ ...rule }))
59
59
  ),
60
- computed: Object.freeze({ ...styles.computed }),
61
- warnings: Object.freeze([...styles.warnings])
60
+ computed: Object.freeze({ ...styles2.computed }),
61
+ warnings: Object.freeze([...styles2.warnings])
62
62
  });
63
63
  }
64
- function freezeTarget(target) {
64
+ function freezeTarget(target2) {
65
65
  return Object.freeze({
66
- instruction: target.instruction.trim(),
67
- source: freezeSource(target.source, target.code),
68
- react: freezeReact(target.react),
69
- element: freezeElement(target.element),
70
- styles: freezeStyles(target.styles),
71
- ...target.code === void 0 ? {} : { code: Object.freeze({ ...target.code }) },
72
- warnings: Object.freeze([...target.warnings])
66
+ instruction: target2.instruction.trim(),
67
+ ...target2.page === void 0 ? {} : { page: Object.freeze({ ...target2.page }) },
68
+ source: freezeSource(target2.source, target2.code),
69
+ react: freezeReact(target2.react),
70
+ element: freezeElement(target2.element),
71
+ styles: freezeStyles(target2.styles),
72
+ ...target2.code === void 0 ? {} : { code: Object.freeze({ ...target2.code }) },
73
+ warnings: Object.freeze([...target2.warnings])
73
74
  });
74
75
  }
75
76
  function createAnnotation(input) {
@@ -78,7 +79,7 @@ function createAnnotation(input) {
78
79
  `SpotPatch annotations require between 1 and ${String(import_shared.MAX_ANNOTATION_TARGETS)} targets.`
79
80
  );
80
81
  }
81
- const instructions = input.targets.map((target) => target.instruction.trim());
82
+ const instructions = input.targets.map((target2) => target2.instruction.trim());
82
83
  if (instructions.some(
83
84
  (instruction) => instruction.length === 0 || instruction.length > import_shared.MAX_TARGET_INSTRUCTION_CHARACTERS
84
85
  ) || instructions.reduce((total, instruction) => total + instruction.length, 0) > import_shared.MAX_ANNOTATION_INSTRUCTION_CHARACTERS) {
@@ -265,10 +266,10 @@ var MAX_EVENT_LINE_BYTES = 1e5;
265
266
  var MAX_EVENT_STREAM_BYTES = 4e6;
266
267
  var RuntimeApiError = class extends Error {
267
268
  code;
268
- constructor(code) {
269
+ constructor(code2) {
269
270
  super("SpotPatch local API request failed.");
270
271
  this.name = "RuntimeApiError";
271
- this.code = code;
272
+ this.code = code2;
272
273
  }
273
274
  };
274
275
  function isRecord2(value) {
@@ -340,9 +341,9 @@ async function readBoundedText(response, maximumBytes) {
340
341
  reader.releaseLock();
341
342
  }
342
343
  }
343
- function parseJson(text) {
344
+ function parseJson(text2) {
344
345
  try {
345
- return JSON.parse(text);
346
+ return JSON.parse(text2);
346
347
  } catch {
347
348
  throw new RuntimeApiError();
348
349
  }
@@ -385,13 +386,13 @@ function omitBrowserCodeContext(request) {
385
386
  ...request,
386
387
  annotation: {
387
388
  ...request.annotation,
388
- targets: request.annotation.targets.map((target) => ({
389
- instruction: target.instruction,
390
- source: target.source,
391
- react: target.react,
392
- element: target.element,
393
- styles: target.styles,
394
- warnings: target.warnings
389
+ targets: request.annotation.targets.map((target2) => ({
390
+ instruction: target2.instruction,
391
+ source: target2.source,
392
+ react: target2.react,
393
+ element: target2.element,
394
+ styles: target2.styles,
395
+ warnings: target2.warnings
395
396
  }))
396
397
  }
397
398
  };
@@ -682,12 +683,12 @@ function contextCharacterCount(classNames, inlineStyle, rules, computed, warning
682
683
  function stylesheetSource(sheet, baseUrl) {
683
684
  return sheet.href === null ? void 0 : (0, import_shared4.sanitizeUrl)(sheet.href, baseUrl);
684
685
  }
685
- function collectMatchedRules(element, rules, source, inheritedConditions, output, warnings) {
686
+ function collectMatchedRules(element2, rules, source2, inheritedConditions, output, warnings) {
686
687
  for (const rule of rules) {
687
688
  if (isStyleRule(rule)) {
688
689
  let matches = false;
689
690
  try {
690
- matches = element.matches(rule.selectorText);
691
+ matches = element2.matches(rule.selectorText);
691
692
  } catch {
692
693
  warnings.add(CSS_COLLECTION_WARNINGS.selector);
693
694
  continue;
@@ -697,7 +698,7 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
697
698
  Object.freeze({
698
699
  selector: sanitizeCssText(rule.selectorText),
699
700
  declarations: sanitizeCssText(rule.style.cssText),
700
- ...source === void 0 ? {} : { source },
701
+ ...source2 === void 0 ? {} : { source: source2 },
701
702
  ...inheritedConditions.length === 0 ? {} : { media: inheritedConditions.join(" and ") }
702
703
  })
703
704
  );
@@ -707,9 +708,9 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
707
708
  if (isGroupingRule(rule)) {
708
709
  const condition = groupingCondition(rule);
709
710
  collectMatchedRules(
710
- element,
711
+ element2,
711
712
  rule.cssRules,
712
- source,
713
+ source2,
713
714
  condition === void 0 ? inheritedConditions : [...inheritedConditions, condition],
714
715
  output,
715
716
  warnings
@@ -717,11 +718,11 @@ function collectMatchedRules(element, rules, source, inheritedConditions, output
717
718
  }
718
719
  }
719
720
  }
720
- function collectComputedStyle(element, getComputedStyle) {
721
+ function collectComputedStyle(element2, getComputedStyle) {
721
722
  if (getComputedStyle === void 0) {
722
723
  return {};
723
724
  }
724
- const declaration = getComputedStyle(element);
725
+ const declaration = getComputedStyle(element2);
725
726
  const computed = {};
726
727
  for (const property of COMPUTED_STYLE_PROPERTIES) {
727
728
  const value = sanitizeCssText(declaration.getPropertyValue(property).trim());
@@ -859,11 +860,11 @@ function escapeHtml(value) {
859
860
  function isPreservedAttribute(name) {
860
861
  return SAFE_ATTRIBUTE_NAMES.has(name) || name.startsWith("aria-") || name === "data-testid";
861
862
  }
862
- function openingTag(element) {
863
- const tagName = element.tagName.toLowerCase();
863
+ function openingTag(element2) {
864
+ const tagName = element2.tagName.toLowerCase();
864
865
  const attributes = [];
865
- const baseUrl = element.ownerDocument.baseURI;
866
- for (const attribute of element.attributes) {
866
+ const baseUrl = element2.ownerDocument.baseURI;
867
+ for (const attribute of element2.attributes) {
867
868
  const name = attribute.name.toLowerCase();
868
869
  if (!isPreservedAttribute(name)) {
869
870
  continue;
@@ -878,28 +879,28 @@ function openingTag(element) {
878
879
  function normalizedText(value) {
879
880
  return (0, import_shared4.redactSensitiveText)(value).replaceAll(/\s+/gu, " ").trim();
880
881
  }
881
- function serializeElement(element, depth, state, lines) {
882
+ function serializeElement(element2, depth, state, lines) {
882
883
  if (state.nodeCount >= DOM_COLLECTION_LIMITS.maxNodes) {
883
884
  state.truncated = true;
884
885
  return;
885
886
  }
886
887
  state.nodeCount += 1;
887
888
  const indent = " ".repeat(depth);
888
- const tagName = element.tagName.toLowerCase();
889
- lines.push(`${indent}${openingTag(element)}`);
889
+ const tagName = element2.tagName.toLowerCase();
890
+ lines.push(`${indent}${openingTag(element2)}`);
890
891
  if (VOID_ELEMENT_NAMES.has(tagName)) {
891
892
  return;
892
893
  }
893
- for (const child of element.childNodes) {
894
+ for (const child of element2.childNodes) {
894
895
  if (state.nodeCount >= DOM_COLLECTION_LIMITS.maxNodes) {
895
896
  state.truncated = true;
896
897
  break;
897
898
  }
898
899
  if (child.nodeType === 3) {
899
- const text = normalizedText(child.textContent ?? "");
900
- if (text.length > 0) {
900
+ const text2 = normalizedText(child.textContent ?? "");
901
+ if (text2.length > 0) {
901
902
  state.nodeCount += 1;
902
- const truncated = text.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text : `${text.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`;
903
+ const truncated = text2.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text2 : `${text2.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`;
903
904
  lines.push(`${" ".repeat(depth + 1)}${escapeHtml(truncated)}`);
904
905
  }
905
906
  continue;
@@ -918,9 +919,9 @@ function serializeElement(element, depth, state, lines) {
918
919
  }
919
920
  lines.push(`${indent}</${tagName}>`);
920
921
  }
921
- function parentContext(element) {
922
+ function parentContext(element2) {
922
923
  const parents = [];
923
- let parent = element.parentElement;
924
+ let parent = element2.parentElement;
924
925
  while (parent !== null && parents.length < DOM_COLLECTION_LIMITS.maxParentDepth) {
925
926
  parents.push(openingTag(parent));
926
927
  parent = parent.parentElement;
@@ -941,26 +942,26 @@ function escapeCssIdentifier(value) {
941
942
  function escapeCssAttribute(value) {
942
943
  return value.replaceAll("\\", "\\\\").replaceAll('"', '\\"');
943
944
  }
944
- function selectorSegment(element) {
945
- const tagName = element.tagName.toLowerCase();
946
- if (element.id.length > 0) {
947
- return `${tagName}#${escapeCssIdentifier(element.id)}`;
945
+ function selectorSegment(element2) {
946
+ const tagName = element2.tagName.toLowerCase();
947
+ if (element2.id.length > 0) {
948
+ return `${tagName}#${escapeCssIdentifier(element2.id)}`;
948
949
  }
949
- const testId = element.getAttribute("data-testid");
950
+ const testId = element2.getAttribute("data-testid");
950
951
  if (testId !== null && testId.length > 0) {
951
952
  return `${tagName}[data-testid="${escapeCssAttribute(testId)}"]`;
952
953
  }
953
- const classes = Array.from(element.classList).slice(0, 2).map((name) => `.${escapeCssIdentifier(name)}`).join("");
954
- const sameTagSiblings = element.parentElement === null ? [] : Array.from(element.parentElement.children).filter(
955
- (sibling) => sibling.tagName === element.tagName
954
+ const classes = Array.from(element2.classList).slice(0, 2).map((name) => `.${escapeCssIdentifier(name)}`).join("");
955
+ const sameTagSiblings = element2.parentElement === null ? [] : Array.from(element2.parentElement.children).filter(
956
+ (sibling) => sibling.tagName === element2.tagName
956
957
  );
957
- const position = sameTagSiblings.indexOf(element);
958
+ const position = sameTagSiblings.indexOf(element2);
958
959
  const nth = sameTagSiblings.length > 1 && position >= 0 ? `:nth-of-type(${String(position + 1)})` : "";
959
960
  return `${tagName}${classes}${nth}`;
960
961
  }
961
- function createSelector(element) {
962
+ function createSelector(element2) {
962
963
  const segments = [];
963
- let current = element;
964
+ let current = element2;
964
965
  while (current !== null && segments.length < 5) {
965
966
  const segment = selectorSegment(current);
966
967
  segments.unshift(segment);
@@ -979,15 +980,15 @@ function collectElementContext(options) {
979
980
  if (parents.length > 0) {
980
981
  lines.push("<!-- Parent context: nearest first -->", ...parents);
981
982
  }
982
- const text = normalizedText(options.element.textContent);
983
+ const text2 = normalizedText(options.element.textContent);
983
984
  const rect = options.element.getBoundingClientRect();
984
985
  const role = options.element.getAttribute("role");
985
986
  return Object.freeze({
986
987
  tagName: options.element.tagName.toLowerCase(),
987
988
  selector: createSelector(options.element),
988
989
  sanitizedHtml: truncateContext(lines.join("\n"), options.maxCharacters),
989
- ...text.length === 0 ? {} : {
990
- textPreview: text.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text : `${text.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`
990
+ ...text2.length === 0 ? {} : {
991
+ textPreview: text2.length <= DOM_COLLECTION_LIMITS.maxTextCharacters ? text2 : `${text2.slice(0, DOM_COLLECTION_LIMITS.maxTextCharacters)}\u2026`
991
992
  },
992
993
  ...role === null ? {} : { role },
993
994
  rect: Object.freeze({
@@ -1006,11 +1007,11 @@ function isApplePlatform(platform) {
1006
1007
  function normalizedKey(key) {
1007
1008
  return key.toLowerCase();
1008
1009
  }
1009
- function isTextEntryTarget(target) {
1010
- if (!(target instanceof Element)) {
1010
+ function isTextEntryTarget(target2) {
1011
+ if (!(target2 instanceof Element)) {
1011
1012
  return false;
1012
1013
  }
1013
- return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement || target.closest("[contenteditable]:not([contenteditable='false'])") !== null;
1014
+ return target2 instanceof HTMLInputElement || target2 instanceof HTMLTextAreaElement || target2 instanceof HTMLSelectElement || target2.closest("[contenteditable]:not([contenteditable='false'])") !== null;
1014
1015
  }
1015
1016
  function matchesShortcut(event, shortcut, platform) {
1016
1017
  const tokens = shortcut.split("+").map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0);
@@ -1060,15 +1061,25 @@ function unionRects(rects) {
1060
1061
  height: bottom - top
1061
1062
  });
1062
1063
  }
1063
- function getElementRect(element, view) {
1064
- const display = view.getComputedStyle(element).display;
1064
+ function measureElementRect(element2, display) {
1065
1065
  if (display.startsWith("inline")) {
1066
- const lineBoxRect = unionRects(Array.from(element.getClientRects()));
1066
+ const lineBoxRect = unionRects(Array.from(element2.getClientRects()));
1067
1067
  if (lineBoxRect !== void 0) {
1068
1068
  return lineBoxRect;
1069
1069
  }
1070
1070
  }
1071
- return toElementRect(element.getBoundingClientRect());
1071
+ return toElementRect(element2.getBoundingClientRect());
1072
+ }
1073
+ function getElementRect(element2, view) {
1074
+ return measureElementRect(element2, view.getComputedStyle(element2).display);
1075
+ }
1076
+ function getVisibleElementRect(element2, view) {
1077
+ const style = view.getComputedStyle(element2);
1078
+ if (style.display === "none" || style.visibility === "hidden") {
1079
+ return void 0;
1080
+ }
1081
+ const rect = measureElementRect(element2, style.display);
1082
+ return rect.width > 0 && rect.height > 0 ? rect : void 0;
1072
1083
  }
1073
1084
 
1074
1085
  // src/ui/ui-constants.ts
@@ -1079,32 +1090,27 @@ var UI_Z_INDEX = Object.freeze({
1079
1090
  });
1080
1091
 
1081
1092
  // src/picker/hit-test.ts
1082
- function isInsideSpotPatchUI(element) {
1083
- if (element.closest(`[${UI_MARKER_ATTRIBUTE}]`) !== null) {
1093
+ function isInsideSpotPatchUI(element2) {
1094
+ if (element2.closest(`[${UI_MARKER_ATTRIBUTE}]`) !== null) {
1084
1095
  return true;
1085
1096
  }
1086
- const root = element.getRootNode();
1097
+ const root = element2.getRootNode();
1087
1098
  return root instanceof ShadowRoot && root.host.hasAttribute(UI_MARKER_ATTRIBUTE);
1088
1099
  }
1089
- function hasVisibleArea(element, view) {
1090
- const style = view.getComputedStyle(element);
1091
- if (style.display === "none" || style.visibility === "hidden") {
1092
- return false;
1093
- }
1094
- const rect = element.getBoundingClientRect();
1095
- return rect.width > 0 && rect.height > 0;
1100
+ function hasVisibleArea(element2, view) {
1101
+ return getVisibleElementRect(element2, view) !== void 0;
1096
1102
  }
1097
- function isDocumentRoot(element) {
1098
- return element.tagName === "HTML" || element.tagName === "BODY";
1103
+ function isDocumentRoot(element2) {
1104
+ return element2.tagName === "HTML" || element2.tagName === "BODY";
1099
1105
  }
1100
1106
  function pickElementAt(document, view, clientX, clientY) {
1101
1107
  const visibleCandidates = document.elementsFromPoint(clientX, clientY).filter(
1102
- (element) => !isInsideSpotPatchUI(element) && hasVisibleArea(element, view)
1108
+ (element2) => !isInsideSpotPatchUI(element2) && hasVisibleArea(element2, view)
1103
1109
  );
1104
- return visibleCandidates.find((element) => !isDocumentRoot(element)) ?? visibleCandidates[0];
1110
+ return visibleCandidates.find((element2) => !isDocumentRoot(element2)) ?? visibleCandidates[0];
1105
1111
  }
1106
- function isSpotPatchUIEventTarget(target) {
1107
- return target instanceof Element && isInsideSpotPatchUI(target);
1112
+ function isSpotPatchUIEventTarget(target2) {
1113
+ return target2 instanceof Element && isInsideSpotPatchUI(target2);
1108
1114
  }
1109
1115
 
1110
1116
  // src/prompt/prompt-composer.ts
@@ -1183,10 +1189,10 @@ function fenced(language, value) {
1183
1189
  ${value}
1184
1190
  ${fence}`;
1185
1191
  }
1186
- function sourceLocation(target, messages) {
1187
- const path = target.code?.relativePath ?? target.source.relativePath;
1188
- const line = target.source.line;
1189
- const column = target.source.column;
1192
+ function sourceLocation(target2, messages) {
1193
+ const path = target2.code?.relativePath ?? target2.source.relativePath;
1194
+ const line = target2.source.line;
1195
+ const column = target2.source.column;
1190
1196
  if (path === void 0) {
1191
1197
  return messages.unavailable;
1192
1198
  }
@@ -1205,10 +1211,10 @@ function renderRule(rule) {
1205
1211
  }`;
1206
1212
  return [...metadata, body].join("\n");
1207
1213
  }
1208
- function renderStyles(target, rules, messages) {
1214
+ function renderStyles(target2, rules, messages) {
1209
1215
  const metadata = [
1210
- target.styles.classNames.length === 0 ? void 0 : `/* classes: ${target.styles.classNames.map(sanitizeCssText).join(" ")} */`,
1211
- target.styles.inlineStyle === void 0 ? void 0 : `/* inline: ${sanitizeCssText(target.styles.inlineStyle)} */`
1216
+ target2.styles.classNames.length === 0 ? void 0 : `/* classes: ${target2.styles.classNames.map(sanitizeCssText).join(" ")} */`,
1217
+ target2.styles.inlineStyle === void 0 ? void 0 : `/* inline: ${sanitizeCssText(target2.styles.inlineStyle)} */`
1212
1218
  ].filter((line) => line !== void 0);
1213
1219
  const blocks = [...metadata, ...rules.map(renderRule)];
1214
1220
  return blocks.length === 0 ? messages.unavailable : fenced("css", blocks.join("\n\n"));
@@ -1221,41 +1227,47 @@ function renderComputed(entries, messages) {
1221
1227
  ).join("\n")
1222
1228
  );
1223
1229
  }
1224
- function renderTarget(target, draft, index, messages) {
1230
+ function renderTarget(target2, draft, index, messages) {
1225
1231
  const reactLines = [
1226
- `- ${messages.adapter}: ${target.react.supported ? messages.supported : messages.unsupported}`,
1227
- ...target.react.version === void 0 ? [] : [`- ${messages.reactVersion}: ${(0, import_shared4.redactSensitiveText)(target.react.version)}`],
1228
- ...target.react.componentName === void 0 ? [] : [
1229
- `- ${messages.component}: ${(0, import_shared4.redactSensitiveText)(target.react.componentName)}`
1232
+ `- ${messages.adapter}: ${target2.react.supported ? messages.supported : messages.unsupported}`,
1233
+ ...target2.react.version === void 0 ? [] : [`- ${messages.reactVersion}: ${(0, import_shared4.redactSensitiveText)(target2.react.version)}`],
1234
+ ...target2.react.componentName === void 0 ? [] : [
1235
+ `- ${messages.component}: ${(0, import_shared4.redactSensitiveText)(target2.react.componentName)}`
1230
1236
  ],
1231
1237
  ...draft.reactStack.length === 0 ? [] : [
1232
1238
  `- ${messages.stack}: ${draft.reactStack.map(import_shared4.redactSensitiveText).join(" > ")}`
1233
1239
  ]
1234
1240
  ];
1235
- const code = target.code === void 0 || draft.codeLines.length === 0 ? messages.unavailable : `- ${messages.boundary}: ${target.code.boundary}
1241
+ const code2 = target2.code === void 0 || draft.codeLines.length === 0 ? messages.unavailable : `- ${messages.boundary}: ${target2.code.boundary}
1236
1242
 
1237
1243
  ${fenced(
1238
- target.code.language,
1244
+ target2.code.language,
1239
1245
  (0, import_shared4.redactSensitiveText)(draft.codeLines.join("\n"))
1240
1246
  )}`;
1241
1247
  return [
1242
1248
  `### ${messages.target(index + 1)}`,
1243
1249
  `#### ${messages.requestedChange}`,
1244
1250
  draft.instruction,
1251
+ ...target2.page === void 0 ? [] : [
1252
+ `#### ${messages.pageEnvironment}`,
1253
+ `- ${messages.url}: <${(0, import_shared4.sanitizeUrl)(target2.page.url, target2.page.url)}>
1254
+ - ${messages.pathname}: ${(0, import_shared4.redactSensitiveText)(target2.page.pathname)}
1255
+ - ${messages.title}: ${(0, import_shared4.redactSensitiveText)(target2.page.title) || messages.unavailable}`
1256
+ ],
1245
1257
  `#### ${messages.reactContext}`,
1246
1258
  reactLines.join("\n"),
1247
1259
  `#### ${messages.source}`,
1248
- `- ${messages.file}: ${sourceLocation(target, messages)}
1249
- - ${messages.origin}: ${target.source.origin}
1250
- - ${messages.confidence}: ${target.source.confidence}`,
1260
+ `- ${messages.file}: ${sourceLocation(target2, messages)}
1261
+ - ${messages.origin}: ${target2.source.origin}
1262
+ - ${messages.confidence}: ${target2.source.confidence}`,
1251
1263
  `#### ${messages.element}`,
1252
1264
  fenced("html", (0, import_shared4.redactSensitiveText)(draft.dom)),
1253
1265
  `#### ${messages.styles}`,
1254
- renderStyles(target, draft.matchedRules, messages),
1266
+ renderStyles(target2, draft.matchedRules, messages),
1255
1267
  `#### ${messages.computedStyles}`,
1256
1268
  renderComputed(draft.computedEntries, messages),
1257
1269
  `#### ${messages.code}`,
1258
- code,
1270
+ code2,
1259
1271
  `#### ${messages.warnings}`,
1260
1272
  draft.warnings.length === 0 ? `- ${messages.none}` : draft.warnings.map((warning) => `- ${(0, import_shared4.redactSensitiveText)(warning)}`).join("\n")
1261
1273
  ].join("\n\n");
@@ -1271,19 +1283,19 @@ function renderPrompt(annotation, draft) {
1271
1283
  - ${messages.viewport}: ${String(annotation.page.viewportWidth)} \xD7 ${String(annotation.page.viewportHeight)}
1272
1284
  - ${messages.devicePixelRatio}: ${String(annotation.page.devicePixelRatio)}`,
1273
1285
  `## ${messages.selectedTargets(annotation.targets.length)}`,
1274
- annotation.targets.map((target, index) => {
1286
+ annotation.targets.map((target2, index) => {
1275
1287
  const targetDraft = draft.targets[index];
1276
1288
  if (targetDraft === void 0) {
1277
1289
  throw new RangeError("Prompt target draft is missing.");
1278
1290
  }
1279
- return renderTarget(target, targetDraft, index, messages);
1291
+ return renderTarget(target2, targetDraft, index, messages);
1280
1292
  }).join("\n\n"),
1281
1293
  `## ${messages.modificationRequirements}`,
1282
1294
  messages.modificationConstraint
1283
1295
  ].join("\n\n");
1284
1296
  }
1285
- function shrinkCode(target, draft) {
1286
- if (draft.codeLines.length === 0 || target.code === void 0) {
1297
+ function shrinkCode(target2, draft) {
1298
+ if (draft.codeLines.length === 0 || target2.code === void 0) {
1287
1299
  return false;
1288
1300
  }
1289
1301
  if (draft.codeLines.length === 1) {
@@ -1295,9 +1307,9 @@ function shrinkCode(target, draft) {
1295
1307
  }
1296
1308
  return true;
1297
1309
  }
1298
- const selectedLine = target.source.line ?? target.code.startLine;
1299
- const firstDistance = Math.abs(target.code.startLine - selectedLine);
1300
- const lastLine = target.code.startLine + draft.codeLines.length - 1;
1310
+ const selectedLine = target2.source.line ?? target2.code.startLine;
1311
+ const firstDistance = Math.abs(target2.code.startLine - selectedLine);
1312
+ const lastLine = target2.code.startLine + draft.codeLines.length - 1;
1301
1313
  const lastDistance = Math.abs(lastLine - selectedLine);
1302
1314
  if (lastDistance >= firstDistance) {
1303
1315
  draft.codeLines.pop();
@@ -1306,15 +1318,15 @@ function shrinkCode(target, draft) {
1306
1318
  }
1307
1319
  return true;
1308
1320
  }
1309
- function createTargetDraft(target) {
1321
+ function createTargetDraft(target2) {
1310
1322
  return {
1311
- instruction: (0, import_shared4.redactSensitiveText)(target.instruction.trim()),
1312
- reactStack: [...target.react.componentStack],
1313
- dom: target.element.sanitizedHtml,
1314
- matchedRules: [...target.styles.matchedRules],
1315
- computedEntries: Object.entries(target.styles.computed),
1316
- codeLines: target.code?.excerpt.split(/\r?\n/u) ?? [],
1317
- warnings: Array.from(/* @__PURE__ */ new Set([...target.styles.warnings, ...target.warnings]))
1323
+ instruction: (0, import_shared4.redactSensitiveText)(target2.instruction.trim()),
1324
+ reactStack: [...target2.react.componentStack],
1325
+ dom: target2.element.sanitizedHtml,
1326
+ matchedRules: [...target2.styles.matchedRules],
1327
+ computedEntries: Object.entries(target2.styles.computed),
1328
+ codeLines: target2.code?.excerpt.split(/\r?\n/u) ?? [],
1329
+ warnings: Array.from(/* @__PURE__ */ new Set([...target2.styles.warnings, ...target2.warnings]))
1318
1330
  };
1319
1331
  }
1320
1332
  function createDraft(annotation) {
@@ -1343,9 +1355,9 @@ function shrinkTargetsFairly(annotation, draft, cursor) {
1343
1355
  for (const operation of operations) {
1344
1356
  for (let offset = 0; offset < annotation.targets.length; offset += 1) {
1345
1357
  const index = (cursor + offset) % annotation.targets.length;
1346
- const target = annotation.targets[index];
1358
+ const target2 = annotation.targets[index];
1347
1359
  const targetDraft = draft.targets[index];
1348
- if (target !== void 0 && targetDraft !== void 0 && operation(target, targetDraft)) {
1360
+ if (target2 !== void 0 && targetDraft !== void 0 && operation(target2, targetDraft)) {
1349
1361
  return (index + 1) % annotation.targets.length;
1350
1362
  }
1351
1363
  }
@@ -1354,15 +1366,15 @@ function shrinkTargetsFairly(annotation, draft, cursor) {
1354
1366
  }
1355
1367
  function renderCompactPrompt(annotation, draft) {
1356
1368
  const messages = PROMPT_MESSAGES[annotation.locale];
1357
- const targets = annotation.targets.map((target, index) => {
1369
+ const targets = annotation.targets.map((target2, index) => {
1358
1370
  const targetDraft = draft.targets[index];
1359
- const component = target.react.componentName;
1371
+ const component = target2.react.componentName;
1360
1372
  return [
1361
1373
  `### ${messages.target(index + 1)}`,
1362
- `- ${messages.requestedChange}: ${targetDraft?.instruction ?? (0, import_shared4.redactSensitiveText)(target.instruction)}`,
1363
- `- ${messages.file}: ${sourceLocation(target, messages)}`,
1374
+ `- ${messages.requestedChange}: ${targetDraft?.instruction ?? (0, import_shared4.redactSensitiveText)(target2.instruction)}`,
1375
+ `- ${messages.file}: ${sourceLocation(target2, messages)}`,
1364
1376
  ...component === void 0 ? [] : [`- ${messages.component}: ${(0, import_shared4.redactSensitiveText)(component)}`],
1365
- `- ${messages.element}: ${(0, import_shared4.redactSensitiveText)(openingTag2(targetDraft?.dom ?? target.element.sanitizedHtml, messages.unavailable))}`
1377
+ `- ${messages.element}: ${(0, import_shared4.redactSensitiveText)(openingTag2(targetDraft?.dom ?? target2.element.sanitizedHtml, messages.unavailable))}`
1366
1378
  ].join("\n");
1367
1379
  });
1368
1380
  return [
@@ -1420,7 +1432,10 @@ var STATES = Object.freeze({
1420
1432
  });
1421
1433
  var INITIAL_RUNTIME_STATE = STATES.idle;
1422
1434
  function reduceIdle(event) {
1423
- return event.type === "ACTIVATE" ? STATES.inspecting : STATES.idle;
1435
+ if (event.type === "ACTIVATE") {
1436
+ return STATES.inspecting;
1437
+ }
1438
+ return event.type === "RESTORE" ? STATES.selected : STATES.idle;
1424
1439
  }
1425
1440
  function reduceInspecting(event) {
1426
1441
  switch (event.type) {
@@ -1472,17 +1487,132 @@ function reduceRuntimeState(state, event) {
1472
1487
  }
1473
1488
  }
1474
1489
 
1490
+ // src/state/selection-session.ts
1491
+ var STORAGE_PREFIX = "spotpatch:selection:";
1492
+ var SNAPSHOT_VERSION = 1;
1493
+ var MAX_SNAPSHOT_CHARACTERS = 1e6;
1494
+ function record(value) {
1495
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1496
+ }
1497
+ function text(value) {
1498
+ return typeof value === "string";
1499
+ }
1500
+ function optionalText(value) {
1501
+ return value === void 0 || text(value);
1502
+ }
1503
+ function strings(value) {
1504
+ return Array.isArray(value) && value.length <= 256 && value.every(text);
1505
+ }
1506
+ function finiteNumber(value) {
1507
+ return typeof value === "number" && Number.isFinite(value);
1508
+ }
1509
+ function optionalNumber(value) {
1510
+ return value === void 0 || finiteNumber(value);
1511
+ }
1512
+ function source(value) {
1513
+ const item = record(value);
1514
+ return item !== void 0 && text(item.origin) && text(item.confidence) && optionalText(item.fileId) && optionalText(item.relativePath) && optionalNumber(item.line) && optionalNumber(item.column);
1515
+ }
1516
+ function react(value) {
1517
+ const item = record(value);
1518
+ return item !== void 0 && typeof item.supported === "boolean" && optionalText(item.version) && optionalText(item.componentName) && strings(item.componentStack) && (item.source === void 0 || source(item.source));
1519
+ }
1520
+ function page(value) {
1521
+ const item = record(value);
1522
+ return item !== void 0 && text(item.url) && text(item.pathname) && text(item.title) && finiteNumber(item.viewportWidth) && finiteNumber(item.viewportHeight) && finiteNumber(item.devicePixelRatio);
1523
+ }
1524
+ function element(value) {
1525
+ const item = record(value);
1526
+ const rect = record(item?.rect);
1527
+ 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);
1528
+ }
1529
+ function styles(value) {
1530
+ const item = record(value);
1531
+ const computed = record(item?.computed);
1532
+ return item !== void 0 && strings(item.classNames) && optionalText(item.inlineStyle) && Array.isArray(item.matchedRules) && item.matchedRules.length <= 256 && item.matchedRules.every((value2) => {
1533
+ const rule = record(value2);
1534
+ return rule !== void 0 && text(rule.selector) && text(rule.declarations) && optionalText(rule.source) && optionalText(rule.media);
1535
+ }) && computed !== void 0 && Object.values(computed).every(text) && strings(item.warnings);
1536
+ }
1537
+ function code(value) {
1538
+ const item = record(value);
1539
+ return item !== void 0 && text(item.relativePath) && text(item.language) && finiteNumber(item.startLine) && finiteNumber(item.endLine) && text(item.excerpt) && text(item.boundary);
1540
+ }
1541
+ function target(value) {
1542
+ const item = record(value);
1543
+ 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));
1544
+ }
1545
+ function parseSnapshot(value, maximumTargets) {
1546
+ const item = record(value);
1547
+ 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)) {
1548
+ return void 0;
1549
+ }
1550
+ const targets = item.targets;
1551
+ const ids = targets.map(({ id }) => id);
1552
+ if (new Set(ids).size !== ids.length || item.activeTargetId !== void 0 && !ids.includes(item.activeTargetId)) {
1553
+ return void 0;
1554
+ }
1555
+ return {
1556
+ ...item.activeTargetId === void 0 ? {} : { activeTargetId: item.activeTargetId },
1557
+ open: item.open,
1558
+ sequence: Number(item.sequence),
1559
+ targets
1560
+ };
1561
+ }
1562
+ function createSelectionSession(window, sessionId, maximumTargets) {
1563
+ const key = `${STORAGE_PREFIX}${sessionId}`;
1564
+ let storage;
1565
+ try {
1566
+ storage = window.sessionStorage;
1567
+ } catch {
1568
+ storage = void 0;
1569
+ }
1570
+ return Object.freeze({
1571
+ clear() {
1572
+ try {
1573
+ storage?.removeItem(key);
1574
+ } catch {
1575
+ }
1576
+ },
1577
+ load() {
1578
+ let serialized;
1579
+ try {
1580
+ serialized = storage?.getItem(key) ?? null;
1581
+ } catch {
1582
+ return void 0;
1583
+ }
1584
+ if (serialized === null || serialized.length > MAX_SNAPSHOT_CHARACTERS) {
1585
+ return void 0;
1586
+ }
1587
+ try {
1588
+ return parseSnapshot(JSON.parse(serialized), maximumTargets);
1589
+ } catch {
1590
+ return void 0;
1591
+ }
1592
+ },
1593
+ save(snapshot) {
1594
+ try {
1595
+ storage?.setItem(
1596
+ key,
1597
+ JSON.stringify({ version: SNAPSHOT_VERSION, ...snapshot })
1598
+ );
1599
+ } catch {
1600
+ }
1601
+ }
1602
+ });
1603
+ }
1604
+
1475
1605
  // src/source/source-resolver.ts
1476
1606
  var import_shared5 = require("@spotpatch/shared");
1477
1607
  var EMPTY_REACT_CONTEXT = Object.freeze({
1478
1608
  supported: false,
1479
1609
  componentStack: Object.freeze([])
1480
1610
  });
1481
- function directMarker(element) {
1482
- return (0, import_shared5.parseSourceMarker)(element.getAttribute(import_shared5.SOURCE_MARKER_ATTRIBUTE));
1611
+ function directMarker(element2) {
1612
+ return (0, import_shared5.parseSourceMarker)(element2.getAttribute(import_shared5.SOURCE_MARKER_ATTRIBUTE));
1483
1613
  }
1484
- function ancestorMarker(element) {
1485
- let ancestor = element.parentElement;
1614
+ function ancestorMarker(element2) {
1615
+ let ancestor = element2.parentElement;
1486
1616
  while (ancestor !== null) {
1487
1617
  const marker = directMarker(ancestor);
1488
1618
  if (marker !== void 0) {
@@ -1504,21 +1634,21 @@ function markerSource(marker, origin, confidence) {
1504
1634
  function unknownSource() {
1505
1635
  return Object.freeze({ origin: "none", confidence: "unknown" });
1506
1636
  }
1507
- function sourceRefToMarker(source) {
1508
- return source.fileId === void 0 || source.line === void 0 || source.column === void 0 ? void 0 : Object.freeze({
1509
- fileId: source.fileId,
1510
- line: source.line,
1511
- column: source.column
1637
+ function sourceRefToMarker(source2) {
1638
+ return source2.fileId === void 0 || source2.line === void 0 || source2.column === void 0 ? void 0 : Object.freeze({
1639
+ fileId: source2.fileId,
1640
+ line: source2.line,
1641
+ column: source2.column
1512
1642
  });
1513
1643
  }
1514
1644
  function createSourceResolver(options) {
1515
1645
  let adapterEnabled = true;
1516
- function inspectReact(element) {
1646
+ function inspectReact(element2) {
1517
1647
  if (!adapterEnabled) {
1518
1648
  return EMPTY_REACT_CONTEXT;
1519
1649
  }
1520
1650
  try {
1521
- return options.adapter.inspect(element);
1651
+ return options.adapter.inspect(element2);
1522
1652
  } catch {
1523
1653
  adapterEnabled = false;
1524
1654
  options.onAdapterError?.();
@@ -1526,26 +1656,26 @@ function createSourceResolver(options) {
1526
1656
  }
1527
1657
  }
1528
1658
  return Object.freeze({
1529
- resolve(element) {
1530
- const marker = directMarker(element);
1531
- const react = inspectReact(element);
1659
+ resolve(element2) {
1660
+ const marker = directMarker(element2);
1661
+ const react2 = inspectReact(element2);
1532
1662
  if (marker !== void 0) {
1533
1663
  return Object.freeze({
1534
- react,
1664
+ react: react2,
1535
1665
  source: markerSource(marker, "jsx-host", "exact")
1536
1666
  });
1537
1667
  }
1538
- if (react.source !== void 0) {
1539
- return Object.freeze({ react, source: react.source });
1668
+ if (react2.source !== void 0) {
1669
+ return Object.freeze({ react: react2, source: react2.source });
1540
1670
  }
1541
- const nearestAncestor = ancestorMarker(element);
1671
+ const nearestAncestor = ancestorMarker(element2);
1542
1672
  if (nearestAncestor !== void 0) {
1543
1673
  return Object.freeze({
1544
- react,
1674
+ react: react2,
1545
1675
  source: markerSource(nearestAncestor, "dom-ancestor", "approximate")
1546
1676
  });
1547
1677
  }
1548
- return Object.freeze({ react, source: unknownSource() });
1678
+ return Object.freeze({ react: react2, source: unknownSource() });
1549
1679
  },
1550
1680
  dispose() {
1551
1681
  adapterEnabled = false;
@@ -1559,9 +1689,9 @@ var import_shared7 = require("@spotpatch/shared");
1559
1689
 
1560
1690
  // src/ui/dom.ts
1561
1691
  function createMarkedElement(document, tagName) {
1562
- const element = document.createElement(tagName);
1563
- element.setAttribute(UI_MARKER_ATTRIBUTE, "");
1564
- return element;
1692
+ const element2 = document.createElement(tagName);
1693
+ element2.setAttribute(UI_MARKER_ATTRIBUTE, "");
1694
+ return element2;
1565
1695
  }
1566
1696
  function createButton(document, label, className = "") {
1567
1697
  const button = createMarkedElement(document, "button");
@@ -1574,7 +1704,7 @@ function createButton(document, label, className = "") {
1574
1704
  // src/ui/agent-panel.ts
1575
1705
  var AGENT_PANEL_STYLES = `
1576
1706
  .spotpatch-agent {
1577
- margin-top: 14px;
1707
+ margin-top: 10px;
1578
1708
  overflow: hidden;
1579
1709
  border: 1px solid var(--spotpatch-border-subtle);
1580
1710
  border-radius: var(--spotpatch-radius-card);
@@ -1584,36 +1714,36 @@ var AGENT_PANEL_STYLES = `
1584
1714
  display: flex;
1585
1715
  align-items: center;
1586
1716
  justify-content: space-between;
1587
- gap: 12px;
1588
- padding: 14px 16px;
1717
+ gap: 10px;
1718
+ padding: 10px 12px;
1589
1719
  border-bottom: 1px solid rgb(255 255 255 / 7%);
1590
1720
  }
1591
- .spotpatch-agent-title { color: #eef0f5; font-size: 16px; font-weight: 680; }
1721
+ .spotpatch-agent-title { color: #eef0f5; font-size: 13px; font-weight: 650; }
1592
1722
  .spotpatch-agent-badge {
1593
1723
  border: 1px solid rgb(129 112 247 / 24%);
1594
1724
  border-radius: 999px;
1595
1725
  padding: 3px 7px;
1596
1726
  color: #c5cafa;
1597
1727
  background: rgb(109 93 246 / 9%);
1598
- font-size: 11px;
1728
+ font-size: 10px;
1599
1729
  font-weight: 650;
1600
1730
  }
1601
- .spotpatch-agent-setup { padding: 18px; }
1602
- .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 15px; }
1603
- .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 14px; font-weight: 580; }
1731
+ .spotpatch-agent-setup { padding: 12px; }
1732
+ .spotpatch-agent-selectors { display: grid; grid-template-columns: 1fr; gap: 10px; }
1733
+ .spotpatch-agent-selectors label { min-width: 0; color: var(--spotpatch-text-secondary); font-size: 11.5px; font-weight: 580; }
1604
1734
  .spotpatch-agent select {
1605
1735
  box-sizing: border-box;
1606
1736
  width: 100%;
1607
1737
  min-width: 0;
1608
- min-height: 46px;
1609
- margin-top: 7px;
1738
+ min-height: 34px;
1739
+ margin-top: 4px;
1610
1740
  border: 1px solid var(--spotpatch-border);
1611
- border-radius: 12px;
1612
- padding: 10px 38px 10px 13px;
1741
+ border-radius: 8px;
1742
+ padding: 7px 32px 7px 9px;
1613
1743
  color: #e8ebf2;
1614
1744
  color-scheme: dark;
1615
1745
  background: linear-gradient(180deg, rgb(255 255 255 / 4%), rgb(255 255 255 / 2%));
1616
- font-size: 15px;
1746
+ font-size: 12px;
1617
1747
  font-weight: 560;
1618
1748
  line-height: 1.35;
1619
1749
  outline: none;
@@ -1625,7 +1755,7 @@ var AGENT_PANEL_STYLES = `
1625
1755
  @supports (appearance: base-select) {
1626
1756
  .spotpatch-agent select,
1627
1757
  ::picker(select) { appearance: base-select; }
1628
- .spotpatch-agent select { padding: 10px 13px; }
1758
+ .spotpatch-agent select { padding: 7px 9px; }
1629
1759
  .spotpatch-agent select::picker-icon {
1630
1760
  color: #9da6b5;
1631
1761
  transition: rotate 180ms cubic-bezier(.2, .8, .2, 1);
@@ -1672,24 +1802,24 @@ var AGENT_PANEL_STYLES = `
1672
1802
  .spotpatch-consent {
1673
1803
  display: flex;
1674
1804
  align-items: flex-start;
1675
- gap: 10px;
1676
- margin-top: 16px;
1805
+ gap: 8px;
1806
+ margin-top: 10px;
1677
1807
  color: var(--spotpatch-text-secondary);
1678
1808
  cursor: pointer;
1679
- font-size: 14px;
1680
- line-height: 1.6;
1809
+ font-size: 11.5px;
1810
+ line-height: 1.5;
1681
1811
  }
1682
- .spotpatch-consent input { width: 16px; height: 16px; flex: none; margin: 3px 0 0; accent-color: var(--spotpatch-accent); }
1812
+ .spotpatch-consent input { width: 14px; height: 14px; flex: none; margin: 2px 0 0; accent-color: var(--spotpatch-accent); }
1683
1813
  .spotpatch-workspace-consent {
1684
- margin-top: 12px;
1814
+ margin-top: 9px;
1685
1815
  border: 1px solid rgb(251 191 36 / 18%);
1686
1816
  border-radius: 10px;
1687
- padding: 11px 12px;
1817
+ padding: 9px 10px;
1688
1818
  background: rgb(120 53 15 / 9%);
1689
1819
  }
1690
- .spotpatch-workspace-consent strong { display: block; color: #fde68a; font-size: 13px; font-weight: 620; }
1691
- .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: #aeb6c4; font-size: 12px; line-height: 1.5; }
1692
- .spotpatch-agent-health-list { display: grid; gap: 8px; margin-top: 14px; }
1820
+ .spotpatch-workspace-consent strong { display: block; color: #fde68a; font-size: 11.5px; font-weight: 620; }
1821
+ .spotpatch-workspace-consent small { display: block; margin-top: 2px; color: #aeb6c4; font-size: 10.5px; line-height: 1.45; }
1822
+ .spotpatch-agent-health-list { display: grid; gap: 6px; margin-top: 10px; }
1693
1823
  .spotpatch-agent-workspace,
1694
1824
  .spotpatch-agent-capability {
1695
1825
  display: flex;
@@ -1697,7 +1827,7 @@ var AGENT_PANEL_STYLES = `
1697
1827
  gap: 8px;
1698
1828
  margin: 0;
1699
1829
  color: #929bab;
1700
- font-size: 13px;
1830
+ font-size: 11px;
1701
1831
  }
1702
1832
  .spotpatch-agent-capability::before {
1703
1833
  width: 6px;
@@ -1727,7 +1857,7 @@ var AGENT_PANEL_STYLES = `
1727
1857
  .spotpatch-agent-workspace[data-state="consent-required"]::before { background: #fbbf24; }
1728
1858
  .spotpatch-agent-workspace[data-state="blocked"] { color: #fecaca; }
1729
1859
  .spotpatch-agent-workspace[data-state="blocked"]::before { background: var(--spotpatch-danger); }
1730
- .spotpatch-agent-job { padding: 16px; }
1860
+ .spotpatch-agent-job { padding: 12px; }
1731
1861
  .spotpatch-agent-job-meta { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
1732
1862
  .spotpatch-agent-model { min-width: 0; overflow: hidden; color: #d6dafe; font-size: 13px; text-overflow: ellipsis; white-space: nowrap; }
1733
1863
  .spotpatch-agent-status {
@@ -2178,68 +2308,81 @@ function createAgentPanel(document, ai, localizer) {
2178
2308
  });
2179
2309
  }
2180
2310
 
2311
+ // src/ui/brand-mark-content.ts
2312
+ var BRAND_MARK_CONTENT = `
2313
+ <defs>
2314
+ <linearGradient id="locator-gradient" x1="76" y1="92" x2="436" y2="374" gradientUnits="userSpaceOnUse">
2315
+ <stop offset="0" stop-color="#B61CFF" />
2316
+ <stop offset="0.38" stop-color="#6D35FF" />
2317
+ <stop offset="0.72" stop-color="#168EFF" />
2318
+ <stop offset="1" stop-color="#00D9E9" />
2319
+ </linearGradient>
2320
+ <linearGradient id="left-code-gradient" x1="165" y1="166" x2="236" y2="258" gradientUnits="userSpaceOnUse">
2321
+ <stop stop-color="#A51EFF" />
2322
+ <stop offset="1" stop-color="#653BFF" />
2323
+ </linearGradient>
2324
+ <linearGradient id="right-code-gradient" x1="276" y1="166" x2="347" y2="258" gradientUnits="userSpaceOnUse">
2325
+ <stop stop-color="#158DFF" />
2326
+ <stop offset="1" stop-color="#00D8E9" />
2327
+ </linearGradient>
2328
+ <linearGradient id="bolt-gradient" x1="270" y1="111" x2="252" y2="365" gradientUnits="userSpaceOnUse">
2329
+ <stop stop-color="#6840FF" />
2330
+ <stop offset="0.48" stop-color="#257BFF" />
2331
+ <stop offset="1" stop-color="#00CBEF" />
2332
+ </linearGradient>
2333
+ </defs>
2334
+ <path
2335
+ fill="url(#locator-gradient)"
2336
+ fill-rule="evenodd"
2337
+ clip-rule="evenodd"
2338
+ 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"
2339
+ />
2340
+ <rect x="238" y="20" width="36" height="84" rx="4" fill="url(#locator-gradient)" />
2341
+ <rect x="62" y="196" width="84" height="36" rx="4" fill="url(#locator-gradient)" />
2342
+ <rect x="366" y="196" width="84" height="36" rx="4" fill="url(#locator-gradient)" />
2343
+ <path
2344
+ d="M213.5 160L158 211.5L213.5 263L238 236.5L211 211.5L238 186.5L213.5 160Z"
2345
+ fill="url(#left-code-gradient)"
2346
+ />
2347
+ <path
2348
+ d="M298.5 160L354 211.5L298.5 263L274 236.5L301 211.5L274 186.5L298.5 160Z"
2349
+ fill="url(#right-code-gradient)"
2350
+ />
2351
+ <path
2352
+ d="M283 108L232 212L266 253L238 369L302 237L267 198L283 108Z"
2353
+ fill="url(#bolt-gradient)"
2354
+ />
2355
+ `;
2356
+
2181
2357
  // src/ui/brand-mark.ts
2182
2358
  var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
2359
+ function resolveBrandMarkContent(override) {
2360
+ if (override !== void 0) {
2361
+ return { content: override, viewBox: "0 0 512 512" };
2362
+ }
2363
+ const injected = typeof __SPOTPATCH_BRAND_MARK_CONTENT__ === "string" ? __SPOTPATCH_BRAND_MARK_CONTENT__ : void 0;
2364
+ if (typeof injected === "string") {
2365
+ return { content: injected, viewBox: "0 0 512 512" };
2366
+ }
2367
+ return true ? { content: BRAND_MARK_CONTENT, viewBox: "0 0 512 512" } : { content: FALLBACK_BRAND_MARK_CONTENT, viewBox: "0 0 1 1" };
2368
+ }
2183
2369
  function svgElement(document, tagName, attributes) {
2184
- const element = document.createElementNS(SVG_NAMESPACE, tagName);
2370
+ const element2 = document.createElementNS(SVG_NAMESPACE, tagName);
2185
2371
  for (const [name, value] of Object.entries(attributes)) {
2186
- element.setAttribute(name, value);
2372
+ element2.setAttribute(name, value);
2187
2373
  }
2188
- return element;
2374
+ return element2;
2189
2375
  }
2190
- function createBrandMark(document) {
2376
+ function createBrandMark(document, content) {
2377
+ const resolved = resolveBrandMarkContent(content);
2191
2378
  const svg = svgElement(document, "svg", {
2379
+ xmlns: SVG_NAMESPACE,
2192
2380
  class: "spotpatch-brand-mark",
2193
- viewBox: "0 0 210 208",
2381
+ viewBox: resolved.viewBox,
2194
2382
  "aria-hidden": "true",
2195
2383
  focusable: "false"
2196
2384
  });
2197
- const definitions = svgElement(document, "defs", {});
2198
- const gradient = svgElement(document, "linearGradient", {
2199
- id: "spotpatch-brand-gradient",
2200
- x1: "35",
2201
- y1: "42",
2202
- x2: "184",
2203
- y2: "176",
2204
- gradientUnits: "userSpaceOnUse"
2205
- });
2206
- gradient.append(
2207
- svgElement(document, "stop", { offset: "0", "stop-color": "#7c22ff" }),
2208
- svgElement(document, "stop", { offset: ".52", "stop-color": "#4a69ff" }),
2209
- svgElement(document, "stop", { offset: "1", "stop-color": "#06d9ef" })
2210
- );
2211
- definitions.append(gradient);
2212
- const commonStroke = {
2213
- fill: "none",
2214
- stroke: "url(#spotpatch-brand-gradient)",
2215
- "stroke-linecap": "square",
2216
- "stroke-linejoin": "miter"
2217
- };
2218
- const outer = svgElement(document, "path", {
2219
- 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",
2220
- ...commonStroke,
2221
- "stroke-width": "14"
2222
- });
2223
- const topStem = svgElement(document, "path", {
2224
- d: "M106 25v42",
2225
- ...commonStroke,
2226
- "stroke-width": "14"
2227
- });
2228
- const sideStems = svgElement(document, "path", {
2229
- d: "M35 109h20m102 0h38",
2230
- ...commonStroke,
2231
- "stroke-width": "13"
2232
- });
2233
- const brackets = svgElement(document, "path", {
2234
- d: "m94 89-18 19 18 17m39-36 18 19-18 17",
2235
- ...commonStroke,
2236
- "stroke-width": "9"
2237
- });
2238
- const bolt = svgElement(document, "path", {
2239
- d: "m119 61-20 45 15 11-13 47 30-56-16-12 4-35Z",
2240
- fill: "url(#spotpatch-brand-gradient)"
2241
- });
2242
- svg.append(definitions, outer, topStem, sideStems, brackets, bolt);
2385
+ svg.innerHTML = resolved.content;
2243
2386
  return svg;
2244
2387
  }
2245
2388
 
@@ -2254,32 +2397,51 @@ function clamp(value, minimum, maximum) {
2254
2397
  function calculateDialogPlacement({
2255
2398
  dialogHeight,
2256
2399
  dialogWidth,
2257
- target,
2400
+ target: target2,
2258
2401
  viewportHeight,
2259
2402
  viewportWidth
2260
2403
  }) {
2261
2404
  const maxLeft = viewportWidth - dialogWidth - VIEWPORT_MARGIN;
2262
2405
  const maxTop = viewportHeight - dialogHeight - VIEWPORT_MARGIN;
2263
- const targetCenterX = target.x + target.width / 2;
2264
- const targetCenterY = target.y + target.height / 2;
2406
+ const viewportLeft = clamp(
2407
+ (viewportWidth - dialogWidth) / 2,
2408
+ VIEWPORT_MARGIN,
2409
+ maxLeft
2410
+ );
2411
+ const viewportTop = clamp(
2412
+ (viewportHeight - dialogHeight) / 2,
2413
+ VIEWPORT_MARGIN,
2414
+ maxTop
2415
+ );
2416
+ if (target2 === void 0) {
2417
+ return Object.freeze({
2418
+ anchorX: clamp(dialogWidth / 2, ANCHOR_INSET, dialogWidth - ANCHOR_INSET),
2419
+ anchorY: clamp(dialogHeight / 2, ANCHOR_INSET, dialogHeight - ANCHOR_INSET),
2420
+ left: viewportLeft,
2421
+ mode: "viewport",
2422
+ top: viewportTop
2423
+ });
2424
+ }
2425
+ const targetCenterX = target2.x + target2.width / 2;
2426
+ const targetCenterY = target2.y + target2.height / 2;
2265
2427
  const centeredLeft = clamp(targetCenterX - dialogWidth / 2, VIEWPORT_MARGIN, maxLeft);
2266
2428
  const centeredTop = clamp(targetCenterY - dialogHeight / 2, VIEWPORT_MARGIN, maxTop);
2267
2429
  let left = centeredLeft;
2268
2430
  let top = centeredTop;
2269
2431
  let mode = "viewport";
2270
- if (target.width >= dialogWidth + TARGET_INSET * 2 && target.height >= dialogHeight + TARGET_INSET * 2) {
2432
+ if (target2.width >= dialogWidth + TARGET_INSET * 2 && target2.height >= dialogHeight + TARGET_INSET * 2) {
2271
2433
  mode = "center";
2272
- } else if (target.y - dialogHeight - TARGET_GAP >= VIEWPORT_MARGIN) {
2273
- top = target.y - dialogHeight - TARGET_GAP;
2434
+ } else if (target2.y - dialogHeight - TARGET_GAP >= VIEWPORT_MARGIN) {
2435
+ top = target2.y - dialogHeight - TARGET_GAP;
2274
2436
  mode = "above";
2275
- } else if (target.y + target.height + TARGET_GAP + dialogHeight <= viewportHeight - VIEWPORT_MARGIN) {
2276
- top = target.y + target.height + TARGET_GAP;
2437
+ } else if (target2.y + target2.height + TARGET_GAP + dialogHeight <= viewportHeight - VIEWPORT_MARGIN) {
2438
+ top = target2.y + target2.height + TARGET_GAP;
2277
2439
  mode = "below";
2278
- } else if (target.x + target.width + TARGET_GAP + dialogWidth <= viewportWidth - VIEWPORT_MARGIN) {
2279
- left = target.x + target.width + TARGET_GAP;
2440
+ } else if (target2.x + target2.width + TARGET_GAP + dialogWidth <= viewportWidth - VIEWPORT_MARGIN) {
2441
+ left = target2.x + target2.width + TARGET_GAP;
2280
2442
  mode = "right";
2281
- } else if (target.x - dialogWidth - TARGET_GAP >= VIEWPORT_MARGIN) {
2282
- left = target.x - dialogWidth - TARGET_GAP;
2443
+ } else if (target2.x - dialogWidth - TARGET_GAP >= VIEWPORT_MARGIN) {
2444
+ left = target2.x - dialogWidth - TARGET_GAP;
2283
2445
  mode = "left";
2284
2446
  }
2285
2447
  return Object.freeze({
@@ -2596,7 +2758,7 @@ var UI_MESSAGES = Object.freeze({
2596
2758
  noSelectable: "No selectable element was found.",
2597
2759
  allTargetsRemoved: "All targets were removed. Choose an element to continue.",
2598
2760
  targetRemoved: "Selected target removed.",
2599
- detachedTargetRemoved: "A removed page element was dropped from the selection.",
2761
+ detachedTargetPreserved: "The page element was unloaded; its collected context remains selected.",
2600
2762
  contextWarning: "Browser context collection completed with a warning.",
2601
2763
  contextCollected: "Browser context collected.",
2602
2764
  editorOpening: "Opening source\u2026",
@@ -2732,7 +2894,7 @@ var UI_MESSAGES = Object.freeze({
2732
2894
  noSelectable: "\u5F53\u524D\u4F4D\u7F6E\u6CA1\u6709\u53EF\u9009\u62E9\u7684\u5143\u7D20\u3002",
2733
2895
  allTargetsRemoved: "\u5DF2\u79FB\u9664\u5168\u90E8\u76EE\u6807\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u5143\u7D20\u3002",
2734
2896
  targetRemoved: "\u5DF2\u79FB\u9664\u9009\u4E2D\u76EE\u6807\u3002",
2735
- detachedTargetRemoved: "\u9875\u9762\u4E2D\u7684\u76EE\u6807\u5DF2\u5378\u8F7D\uFF0C\u5DF2\u4ECE\u5F53\u524D\u9009\u62E9\u4E2D\u79FB\u9664\u3002",
2897
+ detachedTargetPreserved: "\u9875\u9762\u5143\u7D20\u5DF2\u5378\u8F7D\uFF0C\u5DF2\u91C7\u96C6\u7684\u4E0A\u4E0B\u6587\u4ECD\u4FDD\u7559\u5728\u9009\u62E9\u4E2D\u3002",
2736
2898
  contextWarning: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\uFF0C\u4F46\u5B58\u5728\u8B66\u544A\u3002",
2737
2899
  contextCollected: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\u3002",
2738
2900
  editorOpening: "\u6B63\u5728\u6253\u5F00\u6E90\u7801\u2026\u2026",
@@ -2790,33 +2952,36 @@ function createUiLocalizer(document, preference = "auto") {
2790
2952
  }
2791
2953
 
2792
2954
  // src/ui/runtime-view.ts
2793
- var DIALOG_FALLBACK_WIDTH = 560;
2955
+ var DIALOG_MAX_WIDTH = 460;
2956
+ var DIALOG_MAX_HEIGHT = 620;
2794
2957
  var DIALOG_FALLBACK_HEIGHT = Object.freeze({
2795
- previewing: 640,
2796
- selected: 720
2958
+ previewing: 560,
2959
+ selected: DIALOG_MAX_HEIGHT
2797
2960
  });
2798
2961
  function createStyles(document) {
2799
2962
  const style = document.createElement("style");
2800
2963
  style.textContent = `
2801
2964
  :host {
2802
2965
  all: initial;
2803
- --spotpatch-bg: #11141a;
2804
- --spotpatch-bg-raised: #191d25;
2805
- --spotpatch-bg-input: #0c0f14;
2806
- --spotpatch-border: #303641;
2807
- --spotpatch-border-subtle: #262c35;
2808
- --spotpatch-text: #f4f6fb;
2809
- --spotpatch-text-secondary: #a4adbd;
2810
- --spotpatch-text-muted: #7f899a;
2811
- --spotpatch-accent: #7060ee;
2812
- --spotpatch-accent-strong: #604ee8;
2813
- --spotpatch-accent-cyan: #27b8cf;
2966
+ --spotpatch-bg: #0e0e12;
2967
+ --spotpatch-bg-raised: #17171c;
2968
+ --spotpatch-bg-active: #1c1c22;
2969
+ --spotpatch-bg-input: #0b0b0f;
2970
+ --spotpatch-border: #2a2a32;
2971
+ --spotpatch-border-subtle: #232329;
2972
+ --spotpatch-text: #f2f2f5;
2973
+ --spotpatch-text-secondary: #96969f;
2974
+ --spotpatch-text-muted: #686872;
2975
+ --spotpatch-accent: #8b7bff;
2976
+ --spotpatch-accent-strong: #6a5ce8;
2977
+ --spotpatch-accent-cyan: #52a8ff;
2814
2978
  --spotpatch-danger: #fb7185;
2815
2979
  --spotpatch-success: #34d399;
2816
2980
  --spotpatch-warning: #f59e0b;
2817
- --spotpatch-radius-panel: 24px;
2818
- --spotpatch-radius-card: 15px;
2819
- --spotpatch-shadow-panel: 0 28px 72px rgb(0 0 0 / 38%);
2981
+ --spotpatch-text-on-accent: #0b0b12;
2982
+ --spotpatch-radius-panel: 16px;
2983
+ --spotpatch-radius-card: 10px;
2984
+ --spotpatch-shadow-panel: 0 30px 60px -20px rgb(0 0 0 / 70%);
2820
2985
  color-scheme: dark;
2821
2986
  color: var(--spotpatch-text);
2822
2987
  font-family: Inter, "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
@@ -2936,7 +3101,7 @@ function createStyles(document) {
2936
3101
  left: 16px;
2937
3102
  z-index: ${String(UI_Z_INDEX.controls)};
2938
3103
  box-sizing: border-box;
2939
- width: min(560px, calc(100vw - 32px));
3104
+ width: min(${String(DIALOG_MAX_WIDTH)}px, calc(100vw - 32px));
2940
3105
  color: #edf0f7;
2941
3106
  outline: none;
2942
3107
  filter: drop-shadow(var(--spotpatch-shadow-panel));
@@ -2980,78 +3145,92 @@ function createStyles(document) {
2980
3145
  position: relative;
2981
3146
  z-index: 1;
2982
3147
  display: flex;
2983
- max-height: min(720px, calc(100vh - 32px));
3148
+ box-sizing: border-box;
3149
+ max-height: min(${String(DIALOG_MAX_HEIGHT)}px, calc(100vh - 32px));
2984
3150
  overflow: hidden;
2985
3151
  flex-direction: column;
2986
3152
  border: 1px solid var(--spotpatch-border);
2987
3153
  border-radius: var(--spotpatch-radius-panel);
2988
3154
  background: var(--spotpatch-bg);
2989
- box-shadow: inset 0 1px rgb(255 255 255 / 7%), inset 0 0 0 1px rgb(2 6 23 / 40%);
3155
+ box-shadow: inset 0 1px rgb(255 255 255 / 5%), 0 0 60px -32px rgb(139 123 255 / 20%);
3156
+ }
3157
+ .spotpatch-shell::before {
3158
+ position: absolute;
3159
+ z-index: 2;
3160
+ top: 0;
3161
+ right: 20%;
3162
+ left: 20%;
3163
+ height: 1px;
3164
+ background: linear-gradient(90deg, transparent, var(--spotpatch-accent), var(--spotpatch-accent-cyan), transparent);
3165
+ content: "";
3166
+ opacity: .65;
3167
+ pointer-events: none;
2990
3168
  }
2991
3169
  .spotpatch-header {
2992
3170
  position: relative;
2993
- padding: 20px 22px 18px;
2994
- border-bottom: 1px solid rgb(255 255 255 / 8%);
3171
+ padding: 0 18px 14px;
2995
3172
  }
2996
3173
  .spotpatch-brand-row {
2997
3174
  display: flex;
2998
3175
  align-items: center;
2999
3176
  justify-content: space-between;
3000
- gap: 16px;
3001
- margin-bottom: 18px;
3177
+ gap: 12px;
3178
+ margin: 0 -18px 18px;
3179
+ padding: 14px 18px 13px;
3180
+ border-bottom: 1px solid var(--spotpatch-border-subtle);
3002
3181
  }
3003
3182
  .spotpatch-brand {
3004
3183
  display: inline-flex;
3005
3184
  align-items: center;
3006
- gap: 11px;
3185
+ gap: 10px;
3007
3186
  min-width: 0;
3008
3187
  color: #f8fafc;
3009
3188
  }
3010
3189
  .spotpatch-brand-mark {
3011
- width: 34px;
3012
- height: 34px;
3190
+ width: 30px;
3191
+ height: 30px;
3013
3192
  flex: none;
3014
3193
  filter: drop-shadow(0 6px 12px rgb(76 29 149 / 22%));
3015
3194
  }
3016
3195
  .spotpatch-brand-copy { display: grid; min-width: 0; gap: 1px; }
3017
3196
  .spotpatch-brand-name {
3018
3197
  color: #fff;
3019
- font-size: 15px;
3020
- font-weight: 720;
3198
+ font-size: 13.5px;
3199
+ font-weight: 680;
3021
3200
  letter-spacing: -.01em;
3022
3201
  }
3023
3202
  .spotpatch-brand-context {
3024
3203
  color: #8e98aa;
3025
- font-size: 11px;
3204
+ font-size: 10.5px;
3026
3205
  font-weight: 560;
3027
3206
  letter-spacing: .02em;
3028
3207
  }
3029
3208
  .spotpatch-header-controls {
3030
3209
  display: inline-flex;
3031
3210
  align-items: center;
3032
- gap: 8px;
3211
+ gap: 6px;
3033
3212
  flex: none;
3034
3213
  }
3035
3214
  .spotpatch-repository,
3036
3215
  .spotpatch-locale,
3037
3216
  .spotpatch-close {
3038
- height: 30px;
3217
+ height: 27px;
3039
3218
  border: 1px solid rgb(255 255 255 / 11%);
3040
- border-radius: 9px;
3041
- background: rgb(255 255 255 / 4%);
3219
+ border-radius: 7px;
3220
+ background: transparent;
3042
3221
  }
3043
3222
  .spotpatch-repository {
3044
3223
  display: inline-flex;
3045
3224
  align-items: center;
3046
- padding: 0 10px;
3047
- color: #bcecf2;
3225
+ padding: 0 9px;
3226
+ color: var(--spotpatch-text-secondary);
3048
3227
  font-size: 11px;
3049
3228
  font-weight: 680;
3050
3229
  text-decoration: none;
3051
3230
  }
3052
3231
  .spotpatch-locale {
3053
- min-width: 38px;
3054
- padding: 0 9px;
3232
+ min-width: 34px;
3233
+ padding: 0 8px;
3055
3234
  color: #c5cad5;
3056
3235
  cursor: pointer;
3057
3236
  font-size: 12px;
@@ -3059,12 +3238,12 @@ function createStyles(document) {
3059
3238
  }
3060
3239
  .spotpatch-close {
3061
3240
  display: inline-grid;
3062
- width: 30px;
3241
+ width: 27px;
3063
3242
  padding: 0;
3064
3243
  place-items: center;
3065
3244
  color: #9da5b4;
3066
3245
  cursor: pointer;
3067
- font-size: 17px;
3246
+ font-size: 15px;
3068
3247
  line-height: 1;
3069
3248
  transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
3070
3249
  }
@@ -3074,30 +3253,30 @@ function createStyles(document) {
3074
3253
  .spotpatch-title {
3075
3254
  margin: 0;
3076
3255
  color: #fff;
3077
- font-size: 24px;
3078
- font-weight: 730;
3079
- letter-spacing: -.025em;
3256
+ font-size: 18px;
3257
+ font-weight: 680;
3258
+ letter-spacing: -.015em;
3080
3259
  }
3081
3260
  .spotpatch-subtitle {
3082
- max-width: 470px;
3083
- margin: 6px 0 0;
3084
- color: #9ca5b5;
3085
- font-size: 15px;
3086
- line-height: 1.55;
3261
+ max-width: 390px;
3262
+ margin: 4px 0 0;
3263
+ color: var(--spotpatch-text-secondary);
3264
+ font-size: 12.5px;
3265
+ line-height: 1.5;
3087
3266
  }
3088
3267
  .spotpatch-target-row {
3089
3268
  display: flex;
3090
3269
  align-items: center;
3091
3270
  gap: 8px;
3092
3271
  min-width: 0;
3093
- margin-top: 16px;
3272
+ margin-top: 14px;
3094
3273
  }
3095
3274
  .spotpatch-target-label {
3096
3275
  min-width: 0;
3097
3276
  overflow: hidden;
3098
3277
  border: 1px solid rgb(129 112 247 / 24%);
3099
3278
  border-radius: 999px;
3100
- padding: 6px 10px;
3279
+ padding: 4px 10px;
3101
3280
  color: #cdd2ff;
3102
3281
  background: rgb(109 93 246 / 10%);
3103
3282
  font: 600 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
@@ -3110,7 +3289,7 @@ function createStyles(document) {
3110
3289
  gap: 6px;
3111
3290
  flex: none;
3112
3291
  color: #a5adba;
3113
- font-size: 12px;
3292
+ font-size: 11px;
3114
3293
  white-space: nowrap;
3115
3294
  }
3116
3295
  .spotpatch-context-state::before {
@@ -3131,7 +3310,7 @@ function createStyles(document) {
3131
3310
  .spotpatch-body {
3132
3311
  min-height: 0;
3133
3312
  overflow: auto;
3134
- padding: 20px 22px 22px;
3313
+ padding: 0 18px 14px;
3135
3314
  scrollbar-color: rgb(100 116 139 / 50%) transparent;
3136
3315
  scrollbar-width: thin;
3137
3316
  }
@@ -3142,36 +3321,54 @@ function createStyles(document) {
3142
3321
  display: flex;
3143
3322
  align-items: center;
3144
3323
  justify-content: space-between;
3145
- gap: 12px;
3146
- margin-bottom: 7px;
3147
- color: #eef0f5;
3148
- font-size: 14px;
3149
- font-weight: 680;
3324
+ gap: 10px;
3325
+ margin-bottom: 8px;
3326
+ color: var(--spotpatch-text-secondary);
3327
+ font-size: 11px;
3328
+ font-weight: 650;
3329
+ letter-spacing: .04em;
3330
+ text-transform: uppercase;
3150
3331
  }
3151
3332
  .spotpatch-targets-meta {
3152
3333
  display: flex;
3153
3334
  align-items: center;
3154
- justify-content: space-between;
3155
- gap: 12px;
3156
- margin-bottom: 12px;
3335
+ gap: 8px;
3336
+ min-width: 0;
3337
+ letter-spacing: 0;
3338
+ text-transform: none;
3157
3339
  }
3158
- .spotpatch-target-complete { color: var(--spotpatch-text-secondary); font-size: 12px; font-weight: 560; }
3340
+ .spotpatch-target-complete { color: var(--spotpatch-text-muted); font-size: 11px; font-weight: 560; }
3159
3341
  .spotpatch-target-budget {
3160
3342
  color: var(--spotpatch-text-muted);
3161
- font: 550 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3343
+ font: 550 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3162
3344
  }
3345
+ .spotpatch-target-budget[data-state="ready"] { display: none; }
3163
3346
  .spotpatch-target-budget[data-state="over"] { color: #fda4af; }
3164
3347
  .spotpatch-target-count {
3348
+ margin-left: auto;
3165
3349
  border-radius: 999px;
3166
3350
  padding: 3px 8px;
3167
- color: #b8bffd;
3168
- background: rgb(109 93 246 / 11%);
3169
- font: 650 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3351
+ color: var(--spotpatch-text-muted);
3352
+ font: 600 10.5px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace;
3353
+ }
3354
+ .spotpatch-target-progress {
3355
+ height: 3px;
3356
+ margin-bottom: 13px;
3357
+ overflow: hidden;
3358
+ border-radius: 999px;
3359
+ background: var(--spotpatch-border-subtle);
3360
+ }
3361
+ .spotpatch-target-progress-fill {
3362
+ width: 0;
3363
+ height: 100%;
3364
+ border-radius: inherit;
3365
+ background: linear-gradient(90deg, var(--spotpatch-accent), var(--spotpatch-accent-cyan));
3366
+ transition: width 180ms ease;
3170
3367
  }
3171
3368
  .spotpatch-target-list {
3172
3369
  display: grid;
3173
- gap: 12px;
3174
- max-height: min(390px, 48vh);
3370
+ gap: 8px;
3371
+ max-height: min(340px, 48vh);
3175
3372
  overflow: auto;
3176
3373
  padding-right: 2px;
3177
3374
  }
@@ -3179,30 +3376,30 @@ function createStyles(document) {
3179
3376
  overflow: hidden;
3180
3377
  border: 1px solid var(--spotpatch-border-subtle);
3181
3378
  border-radius: var(--spotpatch-radius-card);
3182
- background: rgb(255 255 255 / 2.7%);
3379
+ background: var(--spotpatch-bg-raised);
3183
3380
  transition: border-color 150ms ease, background 150ms ease;
3184
3381
  }
3185
3382
  .spotpatch-target-item[data-active="true"] {
3186
- border-color: rgb(125 106 248 / 42%);
3187
- background: rgb(109 93 246 / 6%);
3188
- box-shadow: inset 3px 0 var(--spotpatch-accent-strong);
3383
+ border-color: rgb(139 123 255 / 34%);
3384
+ background: var(--spotpatch-bg-active);
3385
+ box-shadow: 0 0 0 3px rgb(139 123 255 / 10%);
3189
3386
  }
3190
3387
  .spotpatch-target-summary {
3191
3388
  display: grid;
3192
- grid-template-columns: minmax(0, 1fr) 34px 34px;
3389
+ grid-template-columns: minmax(0, 1fr) 28px 28px;
3193
3390
  align-items: stretch;
3194
- gap: 4px;
3195
- padding: 5px;
3391
+ gap: 2px;
3392
+ padding: 4px;
3196
3393
  }
3197
3394
  .spotpatch-target-select {
3198
3395
  display: grid;
3199
- grid-template-columns: 34px minmax(0, 1fr) auto;
3396
+ grid-template-columns: 24px minmax(0, 1fr) auto;
3200
3397
  align-items: center;
3201
- gap: 11px;
3398
+ gap: 9px;
3202
3399
  min-width: 0;
3203
3400
  border: 0;
3204
- border-radius: 11px;
3205
- padding: 10px 11px;
3401
+ border-radius: 8px;
3402
+ padding: 8px;
3206
3403
  color: inherit;
3207
3404
  background: transparent;
3208
3405
  cursor: pointer;
@@ -3211,14 +3408,14 @@ function createStyles(document) {
3211
3408
  .spotpatch-target-select:hover { background: rgb(255 255 255 / 3.5%); }
3212
3409
  .spotpatch-target-index {
3213
3410
  display: inline-grid;
3214
- width: 30px;
3215
- height: 30px;
3411
+ width: 22px;
3412
+ height: 22px;
3216
3413
  place-items: center;
3217
3414
  border: 1px solid rgb(68 202 224 / 25%);
3218
- border-radius: 9px;
3219
- color: #9ce7f0;
3220
- background: rgb(8 145 178 / 10%);
3221
- font: 700 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
3415
+ border-radius: 6px;
3416
+ color: var(--spotpatch-text-secondary);
3417
+ background: var(--spotpatch-bg);
3418
+ font: 650 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
3222
3419
  }
3223
3420
  .spotpatch-target-copy { min-width: 0; }
3224
3421
  .spotpatch-target-name,
@@ -3228,14 +3425,14 @@ function createStyles(document) {
3228
3425
  text-overflow: ellipsis;
3229
3426
  white-space: nowrap;
3230
3427
  }
3231
- .spotpatch-target-name { color: #f2f4f8; font-size: 16px; font-weight: 680; }
3232
- .spotpatch-target-source { margin-top: 5px; color: var(--spotpatch-text-muted); font: 500 13px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace; }
3428
+ .spotpatch-target-name { color: #f2f2f5; font-size: 13px; font-weight: 650; }
3429
+ .spotpatch-target-source { margin-top: 1px; color: var(--spotpatch-text-muted); font: 500 10.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; }
3233
3430
  .spotpatch-target-state {
3234
3431
  border-radius: 999px;
3235
- padding: 4px 8px;
3432
+ padding: 3px 8px;
3236
3433
  color: #a7f3d0;
3237
3434
  background: rgb(16 185 129 / 9%);
3238
- font-size: 11px;
3435
+ font-size: 10.5px;
3239
3436
  font-weight: 650;
3240
3437
  white-space: nowrap;
3241
3438
  }
@@ -3243,13 +3440,13 @@ function createStyles(document) {
3243
3440
  .spotpatch-target-open,
3244
3441
  .spotpatch-target-remove {
3245
3442
  display: inline-grid;
3246
- width: 34px;
3443
+ width: 28px;
3247
3444
  height: 100%;
3248
- min-height: 40px;
3445
+ min-height: 34px;
3249
3446
  padding: 0;
3250
3447
  place-items: center;
3251
3448
  border: 1px solid transparent;
3252
- border-radius: 10px;
3449
+ border-radius: 7px;
3253
3450
  color: #7f899a;
3254
3451
  background: transparent;
3255
3452
  cursor: pointer;
@@ -3258,31 +3455,30 @@ function createStyles(document) {
3258
3455
  .spotpatch-target-open:hover:not(:disabled) { border-color: rgb(68 202 224 / 30%); color: #c8f7ff; background: rgb(8 145 178 / 11%); }
3259
3456
  .spotpatch-target-remove:hover:not(:disabled) { border-color: rgb(251 113 133 / 24%); color: #fda4af; background: rgb(127 29 29 / 12%); }
3260
3457
  .spotpatch-target-editor {
3261
- border-top: 1px solid rgb(255 255 255 / 7%);
3262
- padding: 18px;
3458
+ padding: 0 12px 12px;
3263
3459
  }
3264
3460
  .spotpatch-target-editor-head {
3265
3461
  display: flex;
3266
3462
  align-items: baseline;
3267
3463
  justify-content: space-between;
3268
3464
  gap: 12px;
3269
- margin-bottom: 9px;
3465
+ margin-bottom: 6px;
3270
3466
  }
3271
- .spotpatch-target-editor-label { color: #dfe3ea; font-size: 15px; font-weight: 650; }
3272
- .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }
3467
+ .spotpatch-target-editor-label { color: var(--spotpatch-text-secondary); font-size: 11px; font-weight: 650; }
3468
+ .spotpatch-target-editor-count { color: var(--spotpatch-text-muted); font: 500 10.5px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace; }
3273
3469
  .spotpatch-target-editor textarea {
3274
3470
  box-sizing: border-box;
3275
3471
  width: 100%;
3276
- min-height: 126px;
3472
+ min-height: 74px;
3277
3473
  resize: vertical;
3278
3474
  border: 1px solid var(--spotpatch-border);
3279
- border-radius: 12px;
3280
- padding: 14px 15px;
3475
+ border-radius: 7px;
3476
+ padding: 9px 10px;
3281
3477
  color: #f8fafc;
3282
3478
  caret-color: #8b7cf7;
3283
3479
  background: var(--spotpatch-bg-input);
3284
- font-size: 15px;
3285
- line-height: 1.65;
3480
+ font-size: 12.5px;
3481
+ line-height: 1.55;
3286
3482
  outline: none;
3287
3483
  transition: border-color 150ms ease, box-shadow 150ms ease, background 150ms ease;
3288
3484
  }
@@ -3291,23 +3487,23 @@ function createStyles(document) {
3291
3487
  .spotpatch-target-editor textarea:focus {
3292
3488
  border-color: rgb(139 124 247 / 68%);
3293
3489
  background: rgb(3 7 18 / 65%);
3294
- box-shadow: 0 0 0 3px rgb(109 93 246 / 10%);
3490
+ box-shadow: 0 0 0 2px rgb(109 93 246 / 10%);
3295
3491
  }
3296
3492
  .spotpatch-diagnostics {
3297
- margin-top: 14px;
3493
+ margin-top: 10px;
3298
3494
  overflow: hidden;
3299
3495
  border: 1px solid rgb(255 255 255 / 8%);
3300
- border-radius: 13px;
3496
+ border-radius: 9px;
3301
3497
  background: rgb(255 255 255 / 2.5%);
3302
3498
  }
3303
3499
  .spotpatch-diagnostics > summary {
3304
3500
  display: flex;
3305
3501
  align-items: center;
3306
3502
  gap: 8px;
3307
- padding: 11px 13px;
3503
+ padding: 9px 11px;
3308
3504
  color: #bfc5d0;
3309
3505
  cursor: pointer;
3310
- font-size: 12px;
3506
+ font-size: 11px;
3311
3507
  font-weight: 650;
3312
3508
  list-style: none;
3313
3509
  user-select: none;
@@ -3336,9 +3532,9 @@ function createStyles(document) {
3336
3532
  margin: 0;
3337
3533
  overflow: auto;
3338
3534
  border-top: 1px solid rgb(255 255 255 / 7%);
3339
- padding: 12px 13px 13px;
3535
+ padding: 10px 11px 11px;
3340
3536
  color: #9ca5b5;
3341
- font: 11.5px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
3537
+ font: 10.5px/1.55 ui-monospace, SFMono-Regular, Menlo, monospace;
3342
3538
  overflow-wrap: anywhere;
3343
3539
  white-space: pre-wrap;
3344
3540
  user-select: text;
@@ -3348,39 +3544,44 @@ function createStyles(document) {
3348
3544
  margin: 0;
3349
3545
  overflow: auto;
3350
3546
  border: 1px solid rgb(255 255 255 / 9%);
3351
- border-radius: 14px;
3352
- padding: 13px;
3547
+ border-radius: 9px;
3548
+ padding: 11px;
3353
3549
  color: #d8ddeb;
3354
3550
  background: rgb(3 7 18 / 55%);
3355
- font: 12px/1.65 ui-monospace, SFMono-Regular, Menlo, monospace;
3551
+ font: 11px/1.6 ui-monospace, SFMono-Regular, Menlo, monospace;
3356
3552
  overflow-wrap: anywhere;
3357
3553
  white-space: pre-wrap;
3358
3554
  user-select: text;
3359
3555
  }
3360
3556
  .spotpatch-actions {
3361
- display: flex;
3362
- flex-wrap: wrap;
3363
- gap: 9px;
3364
- padding: 14px 22px 18px;
3557
+ display: grid;
3558
+ gap: 8px;
3559
+ padding: 10px 18px 14px;
3365
3560
  border-top: 1px solid rgb(255 255 255 / 8%);
3366
- background: rgb(8 12 22 / 82%);
3561
+ background: rgb(8 8 12 / 86%);
3367
3562
  }
3368
3563
  .spotpatch-editor-feedback {
3369
- flex-basis: 100%;
3370
3564
  white-space: normal;
3371
3565
  }
3566
+ .spotpatch-secondary-actions,
3567
+ .spotpatch-primary-actions {
3568
+ display: flex;
3569
+ align-items: center;
3570
+ gap: 7px;
3571
+ }
3572
+ .spotpatch-secondary-actions { min-height: 30px; }
3372
3573
  .spotpatch-actions button {
3373
3574
  display: inline-flex;
3374
- min-height: 40px;
3575
+ min-height: 34px;
3375
3576
  align-items: center;
3376
3577
  justify-content: center;
3377
3578
  border: 1px solid rgb(255 255 255 / 10%);
3378
- border-radius: 11px;
3379
- padding: 9px 13px;
3380
- color: #d8ddeb;
3579
+ border-radius: 9px;
3580
+ padding: 7px 11px;
3581
+ color: var(--spotpatch-text-secondary);
3381
3582
  background: rgb(255 255 255 / 4%);
3382
3583
  cursor: pointer;
3383
- font-size: 13px;
3584
+ font-size: 12px;
3384
3585
  font-weight: 650;
3385
3586
  transition: border-color 150ms ease, box-shadow 150ms ease, color 150ms ease, transform 150ms ease;
3386
3587
  }
@@ -3390,14 +3591,36 @@ function createStyles(document) {
3390
3591
  transform: translateY(-1px);
3391
3592
  }
3392
3593
  .spotpatch-actions .spotpatch-primary {
3393
- min-width: 154px;
3594
+ min-width: 138px;
3394
3595
  flex: 1;
3395
- border-color: rgb(139 124 247 / 58%);
3396
- color: #fff;
3397
- background: var(--spotpatch-accent-strong);
3398
- box-shadow: 0 9px 24px rgb(55 48 163 / 20%), inset 0 1px rgb(255 255 255 / 14%);
3596
+ border-color: transparent;
3597
+ color: var(--spotpatch-text-on-accent);
3598
+ background: linear-gradient(135deg, var(--spotpatch-accent), var(--spotpatch-accent-strong));
3599
+ box-shadow: 0 8px 20px -8px rgb(139 123 255 / 55%);
3399
3600
  }
3601
+ .spotpatch-actions .spotpatch-primary:hover:not(:disabled) { color: var(--spotpatch-text-on-accent); filter: brightness(1.08); }
3400
3602
  .spotpatch-actions .spotpatch-primary::after { margin-left: 8px; content: "\u2197"; font-size: 12px; }
3603
+ .spotpatch-actions .spotpatch-secondary-action {
3604
+ min-height: 30px;
3605
+ border-style: dashed;
3606
+ padding: 5px 10px;
3607
+ color: var(--spotpatch-text-muted);
3608
+ background: transparent;
3609
+ font-size: 11px;
3610
+ }
3611
+ .spotpatch-actions .spotpatch-icon-action {
3612
+ width: 34px;
3613
+ min-width: 34px;
3614
+ padding: 0;
3615
+ font-size: 0;
3616
+ }
3617
+ .spotpatch-actions .spotpatch-icon-action::before {
3618
+ color: var(--spotpatch-text-secondary);
3619
+ content: attr(data-compact-icon);
3620
+ font-size: 18px;
3621
+ font-weight: 400;
3622
+ line-height: 1;
3623
+ }
3401
3624
  .spotpatch-actions button:disabled { cursor: not-allowed; opacity: .4; transform: none; }
3402
3625
  .spotpatch-actions button:focus-visible,
3403
3626
  .spotpatch-repository:focus-visible,
@@ -3429,11 +3652,13 @@ function createStyles(document) {
3429
3652
  }
3430
3653
  @media (max-width: 520px) {
3431
3654
  .spotpatch-dialog { top: 8px; left: 8px; width: calc(100vw - 16px); }
3432
- .spotpatch-shell { max-height: calc(100vh - 16px); border-radius: 18px; }
3433
- .spotpatch-header, .spotpatch-body { padding-left: 16px; padding-right: 16px; }
3434
- .spotpatch-actions { padding-left: 16px; padding-right: 16px; }
3435
- .spotpatch-target-select { grid-template-columns: 32px minmax(0, 1fr); }
3655
+ .spotpatch-shell { max-height: calc(100vh - 16px); border-radius: 14px; }
3656
+ .spotpatch-header, .spotpatch-body { padding-left: 14px; padding-right: 14px; }
3657
+ .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 14px; padding-left: 14px; }
3658
+ .spotpatch-actions { padding-right: 14px; padding-left: 14px; }
3659
+ .spotpatch-target-select { grid-template-columns: 24px minmax(0, 1fr); }
3436
3660
  .spotpatch-target-state { display: none; }
3661
+ .spotpatch-repository { display: none; }
3437
3662
  }
3438
3663
  ${AGENT_PANEL_STYLES}
3439
3664
  `;
@@ -3536,10 +3761,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3536
3761
  const targetCount = createMarkedElement(document, "span");
3537
3762
  targetCount.className = "spotpatch-target-count";
3538
3763
  targetsMeta.append(targetComplete, targetBudget);
3539
- targetsHeading.append(targetsTitle, targetCount);
3764
+ targetRow.append(targetCount);
3765
+ targetsHeading.append(targetsTitle, targetsMeta);
3766
+ const targetProgress = createMarkedElement(document, "div");
3767
+ targetProgress.className = "spotpatch-target-progress";
3768
+ targetProgress.setAttribute("aria-hidden", "true");
3769
+ const targetProgressFill = createMarkedElement(document, "div");
3770
+ targetProgressFill.className = "spotpatch-target-progress-fill";
3771
+ targetProgress.append(targetProgressFill);
3540
3772
  const targetList = createMarkedElement(document, "div");
3541
3773
  targetList.className = "spotpatch-target-list";
3542
- targetsPanel.append(targetsHeading, targetsMeta, targetList);
3774
+ targetsPanel.append(targetsHeading, targetProgress, targetList);
3543
3775
  const diagnostics = createMarkedElement(document, "details");
3544
3776
  diagnostics.className = "spotpatch-diagnostics";
3545
3777
  const diagnosticsLabel = createMarkedElement(document, "summary");
@@ -3580,21 +3812,30 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3580
3812
  );
3581
3813
  const copyButton = createButton(document, messages.actions.copy, "spotpatch-primary");
3582
3814
  const backButton = createButton(document, messages.actions.back);
3583
- actions.append(
3584
- editorFeedback,
3815
+ const secondaryActions = createMarkedElement(document, "div");
3816
+ secondaryActions.className = "spotpatch-secondary-actions";
3817
+ const primaryActions = createMarkedElement(document, "div");
3818
+ primaryActions.className = "spotpatch-primary-actions";
3819
+ addTargetButton.classList.add("spotpatch-icon-action");
3820
+ addTargetButton.dataset.compactIcon = "+";
3821
+ agentPanel.testButton.classList.add("spotpatch-icon-action");
3822
+ agentPanel.testButton.dataset.compactIcon = "\u2713";
3823
+ openEditorButton.classList.add("spotpatch-secondary-action");
3824
+ reselectButton.classList.add("spotpatch-secondary-action");
3825
+ secondaryActions.append(openEditorButton, reselectButton);
3826
+ primaryActions.append(
3827
+ agentPanel.testButton,
3828
+ addTargetButton,
3585
3829
  agentPanel.runButton,
3586
3830
  previewButton,
3587
- agentPanel.testButton,
3588
3831
  agentPanel.cancelButton,
3589
3832
  agentPanel.applyButton,
3590
3833
  agentPanel.revertButton,
3591
3834
  agentPanel.resetButton,
3592
- addTargetButton,
3593
- openEditorButton,
3594
- reselectButton,
3595
3835
  copyButton,
3596
3836
  backButton
3597
3837
  );
3838
+ actions.append(editorFeedback, secondaryActions, primaryActions);
3598
3839
  shell.append(header, body, actions);
3599
3840
  dialog.append(anchor, shell);
3600
3841
  const liveRegion = createMarkedElement(document, "div");
@@ -3627,11 +3868,15 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3627
3868
  editorFeedback.textContent = state === "opening" ? messages.announcements.editorOpening : state === "success" ? messages.announcements.editorOpened : state === "error" ? messages.announcements.editorFailed : "";
3628
3869
  placeDialog();
3629
3870
  }
3630
- function statusText(target) {
3631
- if (target.status === "ready") {
3871
+ function statusText(target2) {
3872
+ if (target2.status === "ready") {
3632
3873
  return messages.targets.statusReady;
3633
3874
  }
3634
- return target.status === "warning" ? messages.targets.statusPartial : messages.targets.statusCollecting;
3875
+ return target2.status === "warning" ? messages.targets.statusPartial : messages.targets.statusCollecting;
3876
+ }
3877
+ function renderTargetProgress(complete, total) {
3878
+ const ratio = total === 0 ? 0 : complete / total;
3879
+ targetProgressFill.style.width = `${String(ratio * 100)}%`;
3635
3880
  }
3636
3881
  function instructionInput(targetId) {
3637
3882
  const inputs = targetList.querySelectorAll(
@@ -3648,16 +3893,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3648
3893
  const focusedTargetId = focusedInstruction?.dataset.targetInstructionId;
3649
3894
  const selectionStart = focusedInstruction?.selectionStart;
3650
3895
  const selectionEnd = focusedInstruction?.selectionEnd;
3651
- currentTargets = targets.map((target) => Object.freeze({ ...target }));
3896
+ currentTargets = targets.map((target2) => Object.freeze({ ...target2 }));
3652
3897
  currentMaximum = maximum;
3653
3898
  targetList.replaceChildren();
3654
3899
  const completeCount = targets.filter(
3655
- (target) => target.instruction.trim().length > 0
3900
+ (target2) => target2.instruction.trim().length > 0
3656
3901
  ).length;
3657
3902
  const instructionCharacters = targets.reduce(
3658
- (total, target) => total + target.instruction.trim().length,
3903
+ (total, target2) => total + target2.instruction.trim().length,
3659
3904
  0
3660
3905
  );
3906
+ renderTargetProgress(completeCount, targets.length);
3661
3907
  targetCount.textContent = messages.targets.count(targets.length, maximum);
3662
3908
  targetComplete.textContent = messages.targets.complete(
3663
3909
  completeCount,
@@ -3675,18 +3921,18 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3675
3921
  addTargetButton.disabled = !editingEnabled || targets.length >= maximum;
3676
3922
  addTargetButton.title = targets.length >= maximum ? messages.targets.limitTitle(maximum) : messages.targets.addTitle;
3677
3923
  targetLabel.textContent = targets.length === 1 ? targets[0]?.label ?? messages.context.selectedElement : messages.context.selectedCount(targets.length);
3678
- for (const [index, target] of targets.entries()) {
3924
+ for (const [index, target2] of targets.entries()) {
3679
3925
  const item = createMarkedElement(document, "div");
3680
3926
  item.className = "spotpatch-target-item";
3681
- item.dataset.active = String(target.active);
3682
- item.dataset.status = target.status;
3683
- item.dataset.targetId = target.id;
3927
+ item.dataset.active = String(target2.active);
3928
+ item.dataset.status = target2.status;
3929
+ item.dataset.targetId = target2.id;
3684
3930
  const targetSummary = createMarkedElement(document, "div");
3685
3931
  targetSummary.className = "spotpatch-target-summary";
3686
3932
  const select = createButton(document, "", "spotpatch-target-select");
3687
- select.dataset.activateTargetId = target.id;
3933
+ select.dataset.activateTargetId = target2.id;
3688
3934
  select.setAttribute("aria-label", messages.targets.activate(index + 1));
3689
- select.setAttribute("aria-expanded", String(target.active));
3935
+ select.setAttribute("aria-expanded", String(target2.active));
3690
3936
  const number = createMarkedElement(document, "span");
3691
3937
  number.className = "spotpatch-target-index";
3692
3938
  number.textContent = String(index + 1);
@@ -3694,54 +3940,54 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3694
3940
  copy.className = "spotpatch-target-copy";
3695
3941
  const name = createMarkedElement(document, "span");
3696
3942
  name.className = "spotpatch-target-name";
3697
- name.textContent = target.label;
3698
- const source = createMarkedElement(document, "span");
3699
- source.className = "spotpatch-target-source";
3700
- source.textContent = `${statusText(target)} \xB7 ${target.source}`;
3701
- copy.append(name, source);
3702
- const instructionComplete = target.instruction.trim().length > 0;
3943
+ name.textContent = target2.label;
3944
+ const source2 = createMarkedElement(document, "span");
3945
+ source2.className = "spotpatch-target-source";
3946
+ source2.textContent = `${statusText(target2)} \xB7 ${target2.source}`;
3947
+ copy.append(name, source2);
3948
+ const instructionComplete = target2.instruction.trim().length > 0;
3703
3949
  const state = createMarkedElement(document, "span");
3704
3950
  state.className = "spotpatch-target-state";
3705
3951
  state.dataset.complete = String(instructionComplete);
3706
3952
  state.textContent = instructionComplete ? messages.targets.instructionReady : messages.targets.instructionMissing;
3707
3953
  select.append(number, copy, state);
3708
3954
  const open = createButton(document, "\u2197", "spotpatch-target-open");
3709
- open.dataset.openTargetId = target.id;
3710
- open.disabled = !target.canOpenEditor;
3955
+ open.dataset.openTargetId = target2.id;
3956
+ open.disabled = !target2.canOpenEditor;
3711
3957
  open.setAttribute("aria-label", messages.actions.openTarget(index + 1));
3712
3958
  open.title = messages.actions.openTarget(index + 1);
3713
3959
  const remove = createButton(document, "\xD7", "spotpatch-target-remove");
3714
- remove.dataset.removeTargetId = target.id;
3960
+ remove.dataset.removeTargetId = target2.id;
3715
3961
  remove.disabled = !editingEnabled;
3716
3962
  remove.setAttribute("aria-label", messages.targets.remove(index + 1));
3717
3963
  remove.title = messages.targets.removeTitle;
3718
3964
  targetSummary.append(select, open, remove);
3719
3965
  item.append(targetSummary);
3720
- if (target.active) {
3966
+ if (target2.active) {
3721
3967
  const editor = createMarkedElement(document, "label");
3722
3968
  editor.className = "spotpatch-target-editor";
3723
3969
  const editorHead = createMarkedElement(document, "span");
3724
3970
  editorHead.className = "spotpatch-target-editor-head";
3725
3971
  const editorLabel = createMarkedElement(document, "span");
3726
3972
  editorLabel.className = "spotpatch-target-editor-label";
3727
- editorLabel.textContent = messages.targets.instructionLabel(target.label);
3973
+ editorLabel.textContent = messages.targets.instructionLabel(target2.label);
3728
3974
  const characterCount = createMarkedElement(document, "span");
3729
3975
  characterCount.className = "spotpatch-target-editor-count";
3730
3976
  characterCount.textContent = messages.targets.instructionCount(
3731
- target.instruction.length,
3977
+ target2.instruction.length,
3732
3978
  import_shared7.MAX_TARGET_INSTRUCTION_CHARACTERS
3733
3979
  );
3734
3980
  editorHead.append(editorLabel, characterCount);
3735
3981
  const input = createMarkedElement(document, "textarea");
3736
3982
  input.rows = 4;
3737
3983
  input.maxLength = import_shared7.MAX_TARGET_INSTRUCTION_CHARACTERS;
3738
- input.value = target.instruction;
3984
+ input.value = target2.instruction;
3739
3985
  input.placeholder = messages.targets.instructionPlaceholder;
3740
3986
  input.disabled = !editingEnabled;
3741
- input.dataset.targetInstructionId = target.id;
3987
+ input.dataset.targetInstructionId = target2.id;
3742
3988
  input.setAttribute(
3743
3989
  "aria-label",
3744
- messages.targets.instructionLabel(target.label)
3990
+ messages.targets.instructionLabel(target2.label)
3745
3991
  );
3746
3992
  editor.append(editorHead, input);
3747
3993
  item.append(editor);
@@ -3758,7 +4004,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3758
4004
  }
3759
4005
  function updateTargetInstruction(targetId, instruction) {
3760
4006
  currentTargets = currentTargets.map(
3761
- (target) => target.id === targetId ? Object.freeze({ ...target, instruction }) : target
4007
+ (target2) => target2.id === targetId ? Object.freeze({ ...target2, instruction }) : target2
3762
4008
  );
3763
4009
  const item = Array.from(
3764
4010
  targetList.querySelectorAll(".spotpatch-target-item")
@@ -3779,12 +4025,16 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3779
4025
  import_shared7.MAX_TARGET_INSTRUCTION_CHARACTERS
3780
4026
  );
3781
4027
  }
4028
+ const completeCount = currentTargets.filter(
4029
+ (target2) => target2.instruction.trim().length > 0
4030
+ ).length;
3782
4031
  targetComplete.textContent = messages.targets.complete(
3783
- currentTargets.filter((target) => target.instruction.trim().length > 0).length,
4032
+ completeCount,
3784
4033
  currentTargets.length
3785
4034
  );
4035
+ renderTargetProgress(completeCount, currentTargets.length);
3786
4036
  const instructionCharacters = currentTargets.reduce(
3787
- (total, target) => total + target.instruction.trim().length,
4037
+ (total, target2) => total + target2.instruction.trim().length,
3788
4038
  0
3789
4039
  );
3790
4040
  const instructionLimitExceeded = instructionCharacters > import_shared7.MAX_ANNOTATION_INSTRUCTION_CHARACTERS;
@@ -3799,25 +4049,25 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3799
4049
  }
3800
4050
  function showSelectionHighlights(targets) {
3801
4051
  selectionHighlights.replaceChildren();
3802
- activeSelectionRect = targets.find((target) => target.active)?.rect;
4052
+ activeSelectionRect = targets.find((target2) => target2.active)?.rect;
3803
4053
  currentRect = activeSelectionRect;
3804
- for (const [index, target] of targets.entries()) {
4054
+ for (const [index, target2] of targets.entries()) {
3805
4055
  const box = createMarkedElement(document, "div");
3806
4056
  box.className = "spotpatch-selection-highlight";
3807
- box.dataset.targetId = target.id;
3808
- box.dataset.active = String(target.active);
3809
- box.style.transform = `translate(${String(target.rect.x)}px, ${String(target.rect.y)}px)`;
3810
- box.style.width = `${String(target.rect.width)}px`;
3811
- box.style.height = `${String(target.rect.height)}px`;
4057
+ box.dataset.targetId = target2.id;
4058
+ box.dataset.active = String(target2.active);
4059
+ box.style.transform = `translate(${String(target2.rect.x)}px, ${String(target2.rect.y)}px)`;
4060
+ box.style.width = `${String(target2.rect.width)}px`;
4061
+ box.style.height = `${String(target2.rect.height)}px`;
3812
4062
  const label = createMarkedElement(document, "span");
3813
- label.textContent = `${String(index + 1)} \xB7 ${target.label}`;
4063
+ label.textContent = `${String(index + 1)} \xB7 ${target2.label}`;
3814
4064
  box.append(label);
3815
4065
  selectionHighlights.append(box);
3816
4066
  }
3817
4067
  placeDialog();
3818
4068
  }
3819
4069
  function placeDialog() {
3820
- if (dialog.hidden || currentRect === void 0) {
4070
+ if (dialog.hidden) {
3821
4071
  return;
3822
4072
  }
3823
4073
  const view = document.defaultView;
@@ -3825,14 +4075,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3825
4075
  const viewportHeight = view?.innerHeight ?? document.documentElement.clientHeight;
3826
4076
  const measured = dialog.getBoundingClientRect();
3827
4077
  const status = currentStatus === "previewing" ? "previewing" : "selected";
3828
- const dialogWidth = measured.width > 0 ? measured.width : Math.min(DIALOG_FALLBACK_WIDTH, viewportWidth - 32);
4078
+ const dialogWidth = measured.width > 0 ? measured.width : Math.min(DIALOG_MAX_WIDTH, viewportWidth - 32);
3829
4079
  const dialogHeight = measured.height > 0 ? measured.height : Math.min(DIALOG_FALLBACK_HEIGHT[status], viewportHeight - 32);
3830
4080
  const placement = calculateDialogPlacement({
3831
- target: currentRect,
3832
4081
  dialogWidth,
3833
4082
  dialogHeight,
3834
4083
  viewportWidth,
3835
- viewportHeight
4084
+ viewportHeight,
4085
+ ...currentRect === void 0 ? {} : { target: currentRect }
3836
4086
  });
3837
4087
  dialog.style.left = `${String(placement.left)}px`;
3838
4088
  dialog.style.top = `${String(placement.top)}px`;
@@ -3842,8 +4092,8 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3842
4092
  }
3843
4093
  function updateContextOverview(summaryText) {
3844
4094
  const summaryMessages = messages.summary;
3845
- const source = summaryLine(summaryText, summaryMessages.source);
3846
- sourcePeek.textContent = source ?? messages.diagnostics.noExactSource;
4095
+ const source2 = summaryLine(summaryText, summaryMessages.source);
4096
+ sourcePeek.textContent = source2 ?? messages.diagnostics.noExactSource;
3847
4097
  const browserReady = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.ready}`;
3848
4098
  const browserLoading = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.loading}`;
3849
4099
  const browserFailed = `${summaryMessages.browserContext}: ${summaryMessages.collectionStatuses.failed}`;
@@ -3882,6 +4132,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3882
4132
  addTargetButton.hidden = !selected;
3883
4133
  openEditorButton.hidden = !selected;
3884
4134
  previewButton.hidden = !selected;
4135
+ secondaryActions.hidden = !selected;
3885
4136
  agentPanel.setSelectionVisible(selected);
3886
4137
  copyButton.hidden = !previewing;
3887
4138
  backButton.hidden = !previewing;
@@ -3920,6 +4171,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3920
4171
  } else {
3921
4172
  targetCount.textContent = messages.targets.count(0, currentMaximum);
3922
4173
  targetComplete.textContent = messages.targets.complete(0, 0);
4174
+ renderTargetProgress(0, 0);
3923
4175
  targetBudget.dataset.state = "ready";
3924
4176
  targetBudget.textContent = messages.targets.instructionBudget(
3925
4177
  0,
@@ -3988,6 +4240,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3988
4240
  selectionHighlights.replaceChildren();
3989
4241
  activeSelectionRect = void 0;
3990
4242
  currentRect = void 0;
4243
+ placeDialog();
3991
4244
  },
3992
4245
  showSelection(summaryText, canOpenEditor, canPreview) {
3993
4246
  updateSelection(summaryText, canOpenEditor, canPreview);
@@ -4013,6 +4266,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4013
4266
  renderEditorStatus("idle");
4014
4267
  targetCount.textContent = messages.targets.count(0, 0);
4015
4268
  targetComplete.textContent = messages.targets.complete(0, 0);
4269
+ renderTargetProgress(0, 0);
4016
4270
  targetLabel.textContent = messages.context.selectedElement;
4017
4271
  summary.textContent = "";
4018
4272
  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 ?? (0, import_react_adapter.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 <= import_shared9.MAX_ANNOTATION_INSTRUCTION_CHARACTERS && 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
  // Annotate the CommonJS export names for ESM import in node: