@spotpatch/runtime 1.0.0 → 1.1.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.d.cts CHANGED
@@ -1,10 +1,11 @@
1
- import { SPOTPATCH_API_BASE, RuntimeAiConfig, ContextBudget, SpotPatchLocalePreference } from '@spotpatch/shared';
1
+ import { SPOTPATCH_API_BASE, RuntimeAiConfig, ContextBudget, SpotPatchEditorPreference, SpotPatchLocalePreference } from '@spotpatch/shared';
2
2
 
3
3
  interface RuntimeConfig {
4
4
  readonly apiBase: typeof SPOTPATCH_API_BASE;
5
5
  readonly ai: RuntimeAiConfig;
6
6
  readonly budget: Readonly<ContextBudget>;
7
7
  readonly debug: boolean;
8
+ readonly editor: SpotPatchEditorPreference;
8
9
  readonly locale: SpotPatchLocalePreference;
9
10
  readonly maxTargets: number;
10
11
  readonly redact: boolean;
package/dist/index.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { SPOTPATCH_API_BASE, RuntimeAiConfig, ContextBudget, SpotPatchLocalePreference } from '@spotpatch/shared';
1
+ import { SPOTPATCH_API_BASE, RuntimeAiConfig, ContextBudget, SpotPatchEditorPreference, SpotPatchLocalePreference } from '@spotpatch/shared';
2
2
 
3
3
  interface RuntimeConfig {
4
4
  readonly apiBase: typeof SPOTPATCH_API_BASE;
5
5
  readonly ai: RuntimeAiConfig;
6
6
  readonly budget: Readonly<ContextBudget>;
7
7
  readonly debug: boolean;
8
+ readonly editor: SpotPatchEditorPreference;
8
9
  readonly locale: SpotPatchLocalePreference;
9
10
  readonly maxTargets: number;
10
11
  readonly redact: boolean;
package/dist/index.js CHANGED
@@ -474,7 +474,11 @@ function createRuntimeApi(options) {
474
474
  return Object.freeze({ ...data });
475
475
  },
476
476
  async openEditor(request) {
477
- await requestJson(SPOTPATCH_ENDPOINTS.openEditor, "POST", request);
477
+ const data = await requestJson(SPOTPATCH_ENDPOINTS.openEditor, "POST", request);
478
+ if (!isRecord2(data) || data.editor !== "auto" && data.editor !== "vscode" && data.editor !== "cursor") {
479
+ throw new RuntimeApiError();
480
+ }
481
+ return Object.freeze({ editor: data.editor });
478
482
  },
479
483
  async agentCapability(request) {
480
484
  return parseCapability(
@@ -1504,7 +1508,8 @@ function createSourceResolver(options) {
1504
1508
  // src/ui/runtime-view.ts
1505
1509
  import {
1506
1510
  MAX_ANNOTATION_INSTRUCTION_CHARACTERS as MAX_ANNOTATION_INSTRUCTION_CHARACTERS2,
1507
- MAX_TARGET_INSTRUCTION_CHARACTERS as MAX_TARGET_INSTRUCTION_CHARACTERS2
1511
+ MAX_TARGET_INSTRUCTION_CHARACTERS as MAX_TARGET_INSTRUCTION_CHARACTERS2,
1512
+ SPOTPATCH_REPOSITORY_URL
1508
1513
  } from "@spotpatch/shared";
1509
1514
 
1510
1515
  // src/ui/dom.ts
@@ -2267,7 +2272,12 @@ var UI_MESSAGES = Object.freeze({
2267
2272
  localeName: "EN",
2268
2273
  alternateLocaleName: "\u4E2D",
2269
2274
  switchLocale: "Switch interface language to Chinese",
2270
- brand: Object.freeze({ name: "SpotPatch", context: "Live context" }),
2275
+ brand: Object.freeze({
2276
+ name: "SpotPatch",
2277
+ context: "Live context",
2278
+ repository: "GitHub \u2197",
2279
+ repositoryTitle: "Star SpotPatch on GitHub"
2280
+ }),
2271
2281
  trigger: Object.freeze({
2272
2282
  select: "Select element",
2273
2283
  stop: "Stop selecting",
@@ -2319,7 +2329,8 @@ var UI_MESSAGES = Object.freeze({
2319
2329
  actions: Object.freeze({
2320
2330
  addElement: "Add element",
2321
2331
  reselect: "Start over",
2322
- openEditor: "Open in VS Code",
2332
+ openEditor: "Open source",
2333
+ openTarget: (index) => `Open source for target ${String(index)}`,
2323
2334
  preview: "Preview prompt",
2324
2335
  copy: "Copy prompt",
2325
2336
  back: "Back to edit"
@@ -2375,8 +2386,9 @@ var UI_MESSAGES = Object.freeze({
2375
2386
  detachedTargetRemoved: "A removed page element was dropped from the selection.",
2376
2387
  contextWarning: "Browser context collection completed with a warning.",
2377
2388
  contextCollected: "Browser context collected.",
2378
- editorOpened: "VS Code open request sent.",
2379
- editorFailed: "VS Code could not be opened.",
2389
+ editorOpening: "Opening source\u2026",
2390
+ editorOpened: "Source opened in the editor.",
2391
+ editorFailed: "Could not open the editor. Start it or configure editor.",
2380
2392
  completeInstructions: "Add an instruction for every target and wait for context collection to finish.",
2381
2393
  promptCopied: "Prompt copied to the clipboard.",
2382
2394
  clipboardUnavailable: "Clipboard access is unavailable. Select the prompt manually.",
@@ -2389,7 +2401,12 @@ var UI_MESSAGES = Object.freeze({
2389
2401
  localeName: "\u4E2D",
2390
2402
  alternateLocaleName: "EN",
2391
2403
  switchLocale: "\u5C06\u754C\u9762\u8BED\u8A00\u5207\u6362\u4E3A\u82F1\u6587",
2392
- brand: Object.freeze({ name: "SpotPatch", context: "\u5B9E\u65F6\u4E0A\u4E0B\u6587" }),
2404
+ brand: Object.freeze({
2405
+ name: "SpotPatch",
2406
+ context: "\u5B9E\u65F6\u4E0A\u4E0B\u6587",
2407
+ repository: "GitHub \u2197",
2408
+ repositoryTitle: "\u5728 GitHub \u4E0A Star SpotPatch"
2409
+ }),
2393
2410
  trigger: Object.freeze({
2394
2411
  select: "\u9009\u62E9\u5143\u7D20",
2395
2412
  stop: "\u505C\u6B62\u9009\u62E9",
@@ -2441,7 +2458,8 @@ var UI_MESSAGES = Object.freeze({
2441
2458
  actions: Object.freeze({
2442
2459
  addElement: "\u6DFB\u52A0\u5143\u7D20",
2443
2460
  reselect: "\u91CD\u65B0\u5F00\u59CB",
2444
- openEditor: "\u5728 VS Code \u4E2D\u6253\u5F00",
2461
+ openEditor: "\u6253\u5F00\u6E90\u7801",
2462
+ openTarget: (index) => `\u6253\u5F00\u76EE\u6807 ${String(index)} \u7684\u6E90\u7801`,
2445
2463
  preview: "\u9884\u89C8 Prompt",
2446
2464
  copy: "\u590D\u5236 Prompt",
2447
2465
  back: "\u8FD4\u56DE\u7F16\u8F91"
@@ -2497,8 +2515,9 @@ var UI_MESSAGES = Object.freeze({
2497
2515
  detachedTargetRemoved: "\u9875\u9762\u4E2D\u7684\u76EE\u6807\u5DF2\u5378\u8F7D\uFF0C\u5DF2\u4ECE\u5F53\u524D\u9009\u62E9\u4E2D\u79FB\u9664\u3002",
2498
2516
  contextWarning: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\uFF0C\u4F46\u5B58\u5728\u8B66\u544A\u3002",
2499
2517
  contextCollected: "\u6D4F\u89C8\u5668\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\u3002",
2500
- editorOpened: "\u5DF2\u53D1\u9001 VS Code \u6253\u5F00\u8BF7\u6C42\u3002",
2501
- editorFailed: "\u65E0\u6CD5\u6253\u5F00 VS Code\u3002",
2518
+ editorOpening: "\u6B63\u5728\u6253\u5F00\u6E90\u7801\u2026\u2026",
2519
+ editorOpened: "\u6E90\u7801\u5DF2\u5728\u7F16\u8F91\u5668\u4E2D\u6253\u5F00\u3002",
2520
+ editorFailed: "\u65E0\u6CD5\u6253\u5F00\u7F16\u8F91\u5668\u3002\u8BF7\u5148\u542F\u52A8\u7F16\u8F91\u5668\u6216\u68C0\u67E5 editor \u914D\u7F6E\u3002",
2502
2521
  completeInstructions: "\u8BF7\u4E3A\u6BCF\u4E2A\u76EE\u6807\u586B\u5199\u4FEE\u6539\u8BF4\u660E\uFF0C\u5E76\u7B49\u5F85\u4E0A\u4E0B\u6587\u91C7\u96C6\u5B8C\u6210\u3002",
2503
2522
  promptCopied: "Prompt \u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F\u3002",
2504
2523
  clipboardUnavailable: "\u65E0\u6CD5\u8BBF\u95EE\u526A\u8D34\u677F\uFF0C\u8BF7\u624B\u52A8\u9009\u62E9\u5E76\u590D\u5236 Prompt\u3002",
@@ -2793,14 +2812,27 @@ function createStyles(document) {
2793
2812
  gap: 8px;
2794
2813
  flex: none;
2795
2814
  }
2796
- .spotpatch-locale {
2797
- min-width: 38px;
2815
+ .spotpatch-repository,
2816
+ .spotpatch-locale,
2817
+ .spotpatch-close {
2798
2818
  height: 30px;
2799
2819
  border: 1px solid rgb(255 255 255 / 11%);
2800
2820
  border-radius: 9px;
2821
+ background: rgb(255 255 255 / 4%);
2822
+ }
2823
+ .spotpatch-repository {
2824
+ display: inline-flex;
2825
+ align-items: center;
2826
+ padding: 0 10px;
2827
+ color: #bcecf2;
2828
+ font-size: 11px;
2829
+ font-weight: 680;
2830
+ text-decoration: none;
2831
+ }
2832
+ .spotpatch-locale {
2833
+ min-width: 38px;
2801
2834
  padding: 0 9px;
2802
2835
  color: #c5cad5;
2803
- background: rgb(255 255 255 / 4%);
2804
2836
  cursor: pointer;
2805
2837
  font-size: 12px;
2806
2838
  font-weight: 650;
@@ -2808,20 +2840,17 @@ function createStyles(document) {
2808
2840
  .spotpatch-close {
2809
2841
  display: inline-grid;
2810
2842
  width: 30px;
2811
- height: 30px;
2812
2843
  padding: 0;
2813
2844
  place-items: center;
2814
- border: 1px solid rgb(255 255 255 / 11%);
2815
- border-radius: 9px;
2816
2845
  color: #9da5b4;
2817
- background: rgb(255 255 255 / 4%);
2818
2846
  cursor: pointer;
2819
2847
  font-size: 17px;
2820
2848
  line-height: 1;
2821
2849
  transition: border-color 150ms ease, color 150ms ease, background 150ms ease;
2822
2850
  }
2823
2851
  .spotpatch-close:hover,
2824
- .spotpatch-locale:hover { border-color: rgb(129 112 247 / 45%); color: #fff; background: rgb(109 93 246 / 10%); }
2852
+ .spotpatch-locale:hover,
2853
+ .spotpatch-repository:hover { border-color: rgb(129 112 247 / 45%); color: #fff; background: rgb(109 93 246 / 10%); }
2825
2854
  .spotpatch-title {
2826
2855
  margin: 0;
2827
2856
  color: #fff;
@@ -2871,10 +2900,14 @@ function createStyles(document) {
2871
2900
  background: #f59e0b;
2872
2901
  content: "";
2873
2902
  }
2874
- .spotpatch-context-state[data-state="ready"] { color: #9fe3c4; }
2875
- .spotpatch-context-state[data-state="ready"]::before { background: #34d399; }
2876
- .spotpatch-context-state[data-state="warning"] { color: #fecaca; }
2877
- .spotpatch-context-state[data-state="warning"]::before { background: #fb7185; }
2903
+ .spotpatch-context-state[data-state="ready"],
2904
+ .spotpatch-editor-feedback[data-state="success"] { color: #9fe3c4; }
2905
+ .spotpatch-context-state[data-state="ready"]::before,
2906
+ .spotpatch-editor-feedback[data-state="success"]::before { background: #34d399; }
2907
+ .spotpatch-context-state[data-state="warning"],
2908
+ .spotpatch-editor-feedback[data-state="error"] { color: #fecaca; }
2909
+ .spotpatch-context-state[data-state="warning"]::before,
2910
+ .spotpatch-editor-feedback[data-state="error"]::before { background: #fb7185; }
2878
2911
  .spotpatch-body {
2879
2912
  min-height: 0;
2880
2913
  overflow: auto;
@@ -2936,7 +2969,7 @@ function createStyles(document) {
2936
2969
  }
2937
2970
  .spotpatch-target-summary {
2938
2971
  display: grid;
2939
- grid-template-columns: minmax(0, 1fr) 34px;
2972
+ grid-template-columns: minmax(0, 1fr) 34px 34px;
2940
2973
  align-items: stretch;
2941
2974
  gap: 4px;
2942
2975
  padding: 5px;
@@ -2987,6 +3020,7 @@ function createStyles(document) {
2987
3020
  white-space: nowrap;
2988
3021
  }
2989
3022
  .spotpatch-target-state[data-complete="false"] { color: #f5c97a; background: rgb(245 158 11 / 9%); }
3023
+ .spotpatch-target-open,
2990
3024
  .spotpatch-target-remove {
2991
3025
  display: inline-grid;
2992
3026
  width: 34px;
@@ -3000,6 +3034,8 @@ function createStyles(document) {
3000
3034
  background: transparent;
3001
3035
  cursor: pointer;
3002
3036
  }
3037
+ .spotpatch-target-open { color: #79d9e7; }
3038
+ .spotpatch-target-open:hover:not(:disabled) { border-color: rgb(68 202 224 / 30%); color: #c8f7ff; background: rgb(8 145 178 / 11%); }
3003
3039
  .spotpatch-target-remove:hover:not(:disabled) { border-color: rgb(251 113 133 / 24%); color: #fda4af; background: rgb(127 29 29 / 12%); }
3004
3040
  .spotpatch-target-editor {
3005
3041
  border-top: 1px solid rgb(255 255 255 / 7%);
@@ -3109,6 +3145,10 @@ function createStyles(document) {
3109
3145
  border-top: 1px solid rgb(255 255 255 / 8%);
3110
3146
  background: rgb(8 12 22 / 82%);
3111
3147
  }
3148
+ .spotpatch-editor-feedback {
3149
+ flex-basis: 100%;
3150
+ white-space: normal;
3151
+ }
3112
3152
  .spotpatch-actions button {
3113
3153
  display: inline-flex;
3114
3154
  min-height: 40px;
@@ -3140,6 +3180,8 @@ function createStyles(document) {
3140
3180
  .spotpatch-actions .spotpatch-primary::after { margin-left: 8px; content: "\u2197"; font-size: 12px; }
3141
3181
  .spotpatch-actions button:disabled { cursor: not-allowed; opacity: .4; transform: none; }
3142
3182
  .spotpatch-actions button:focus-visible,
3183
+ .spotpatch-repository:focus-visible,
3184
+ .spotpatch-target-open:focus-visible,
3143
3185
  .spotpatch-target-remove:focus-visible,
3144
3186
  .spotpatch-close:focus-visible,
3145
3187
  .spotpatch-trigger:focus-visible,
@@ -3231,9 +3273,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3231
3273
  brand.append(createBrandMark(document), brandCopy);
3232
3274
  const headerControls = createMarkedElement(document, "span");
3233
3275
  headerControls.className = "spotpatch-header-controls";
3276
+ const repositoryLink = createMarkedElement(document, "a");
3277
+ repositoryLink.className = "spotpatch-repository";
3278
+ repositoryLink.href = SPOTPATCH_REPOSITORY_URL;
3279
+ repositoryLink.target = "_blank";
3280
+ repositoryLink.rel = "noopener noreferrer";
3234
3281
  const localeButton = createButton(document, "", "spotpatch-locale");
3235
3282
  const closeButton = createButton(document, "\xD7", "spotpatch-close");
3236
- headerControls.append(localeButton, closeButton);
3283
+ headerControls.append(repositoryLink, localeButton, closeButton);
3237
3284
  brandRow.append(brand, headerControls);
3238
3285
  const title = createMarkedElement(document, "h2");
3239
3286
  title.id = "spotpatch-selection-title";
@@ -3297,6 +3344,12 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3297
3344
  body.append(selectionPanel, previewPanel);
3298
3345
  const actions = createMarkedElement(document, "footer");
3299
3346
  actions.className = "spotpatch-actions";
3347
+ const editorFeedback = createMarkedElement(document, "div");
3348
+ editorFeedback.className = "spotpatch-context-state spotpatch-editor-feedback";
3349
+ editorFeedback.dataset.state = "idle";
3350
+ editorFeedback.setAttribute("role", "status");
3351
+ editorFeedback.setAttribute("aria-live", "polite");
3352
+ editorFeedback.hidden = true;
3300
3353
  const addTargetButton = createButton(document, messages.actions.addElement);
3301
3354
  const reselectButton = createButton(document, messages.actions.reselect);
3302
3355
  const openEditorButton = createButton(document, messages.actions.openEditor);
@@ -3308,6 +3361,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3308
3361
  const copyButton = createButton(document, messages.actions.copy, "spotpatch-primary");
3309
3362
  const backButton = createButton(document, messages.actions.back);
3310
3363
  actions.append(
3364
+ editorFeedback,
3311
3365
  agentPanel.runButton,
3312
3366
  previewButton,
3313
3367
  agentPanel.testButton,
@@ -3345,6 +3399,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3345
3399
  let editingEnabled = true;
3346
3400
  let currentTargets = [];
3347
3401
  let currentMaximum = 0;
3402
+ let currentEditorFeedbackState = "idle";
3403
+ function renderEditorStatus(state) {
3404
+ currentEditorFeedbackState = state;
3405
+ editorFeedback.dataset.state = state;
3406
+ editorFeedback.hidden = state === "idle";
3407
+ editorFeedback.textContent = state === "opening" ? messages.announcements.editorOpening : state === "success" ? messages.announcements.editorOpened : state === "error" ? messages.announcements.editorFailed : "";
3408
+ placeDialog();
3409
+ }
3348
3410
  function statusText(target) {
3349
3411
  if (target.status === "ready") {
3350
3412
  return messages.targets.statusReady;
@@ -3423,12 +3485,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3423
3485
  state.dataset.complete = String(instructionComplete);
3424
3486
  state.textContent = instructionComplete ? messages.targets.instructionReady : messages.targets.instructionMissing;
3425
3487
  select.append(number, copy, state);
3488
+ const open = createButton(document, "\u2197", "spotpatch-target-open");
3489
+ open.dataset.openTargetId = target.id;
3490
+ open.disabled = !target.canOpenEditor;
3491
+ open.setAttribute("aria-label", messages.actions.openTarget(index + 1));
3492
+ open.title = messages.actions.openTarget(index + 1);
3426
3493
  const remove = createButton(document, "\xD7", "spotpatch-target-remove");
3427
3494
  remove.dataset.removeTargetId = target.id;
3428
3495
  remove.disabled = !editingEnabled;
3429
3496
  remove.setAttribute("aria-label", messages.targets.remove(index + 1));
3430
3497
  remove.title = messages.targets.removeTitle;
3431
- targetSummary.append(select, remove);
3498
+ targetSummary.append(select, open, remove);
3432
3499
  item.append(targetSummary);
3433
3500
  if (target.active) {
3434
3501
  const editor = createMarkedElement(document, "label");
@@ -3606,6 +3673,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3606
3673
  messages = localizer.messages();
3607
3674
  brandName.textContent = messages.brand.name;
3608
3675
  brandContext.textContent = messages.brand.context;
3676
+ repositoryLink.textContent = messages.brand.repository;
3677
+ repositoryLink.title = messages.brand.repositoryTitle;
3678
+ repositoryLink.setAttribute("aria-label", messages.brand.repositoryTitle);
3609
3679
  localeButton.textContent = messages.alternateLocaleName;
3610
3680
  localeButton.title = messages.switchLocale;
3611
3681
  localeButton.setAttribute("aria-label", messages.switchLocale);
@@ -3624,6 +3694,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3624
3694
  triggerButton.title = messages.trigger.title(shortcut);
3625
3695
  triggerButton.textContent = currentStatus === "inspecting" ? messages.trigger.stop : messages.trigger.select;
3626
3696
  renderPanelStatus(currentStatus);
3697
+ renderEditorStatus(currentEditorFeedbackState);
3627
3698
  if (currentTargets.length > 0) {
3628
3699
  renderTargets(currentTargets, currentMaximum);
3629
3700
  } else {
@@ -3654,6 +3725,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3654
3725
  targetList,
3655
3726
  reselectButton,
3656
3727
  openEditorButton,
3728
+ repositoryLink,
3657
3729
  previewButton,
3658
3730
  copyButton,
3659
3731
  backButton,
@@ -3673,6 +3745,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3673
3745
  triggerButton.textContent = inspecting ? messages.trigger.stop : messages.trigger.select;
3674
3746
  renderPanelStatus(status);
3675
3747
  },
3748
+ renderEditorStatus,
3676
3749
  showHighlight(rect, label) {
3677
3750
  currentRect = rect;
3678
3751
  highlight.hidden = false;
@@ -3716,6 +3789,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3716
3789
  currentTargets = [];
3717
3790
  currentMaximum = 0;
3718
3791
  currentSummaryText = "";
3792
+ renderEditorStatus("idle");
3719
3793
  targetCount.textContent = messages.targets.count(0, 0);
3720
3794
  targetComplete.textContent = messages.targets.complete(0, 0);
3721
3795
  targetLabel.textContent = messages.context.selectedElement;
@@ -3759,7 +3833,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3759
3833
  ).forEach((control) => {
3760
3834
  control.disabled = !enabled;
3761
3835
  });
3762
- openEditorButton.disabled = !enabled || !currentCanOpenEditor;
3836
+ openEditorButton.disabled = !currentCanOpenEditor;
3763
3837
  previewButton.disabled = !enabled || !currentCanPreview;
3764
3838
  agentPanel.setEditingEnabled(enabled);
3765
3839
  placeDialog();
@@ -4280,6 +4354,7 @@ function createController(config, dependencies = {}) {
4280
4354
  let activeTargetId;
4281
4355
  let targetSequence = 0;
4282
4356
  let sessionRevision = 0;
4357
+ let editorRequestRevision = 0;
4283
4358
  let addingTarget = false;
4284
4359
  let previewPrompt = "";
4285
4360
  let previousFocus;
@@ -4354,6 +4429,7 @@ ${summary}`;
4354
4429
  id: target.id,
4355
4430
  label: target.resolution.react.componentName ?? (target.element === void 0 ? target.elementContext?.tagName ?? "Element" : elementLabel(target.element)),
4356
4431
  source: sourceLabel(target, view.messages().context.sourceUnavailable),
4432
+ canOpenEditor: target.marker !== void 0,
4357
4433
  status: targetStatus(target),
4358
4434
  active: target.id === activeTargetId,
4359
4435
  instruction: target.instruction
@@ -4497,7 +4573,6 @@ ${summary}`;
4497
4573
  resizeObserver?.disconnect();
4498
4574
  for (const target of targets) {
4499
4575
  target.element = void 0;
4500
- target.marker = void 0;
4501
4576
  }
4502
4577
  view.hideHighlight();
4503
4578
  view.hideSelectionHighlights();
@@ -4774,24 +4849,31 @@ ${summary}`;
4774
4849
  }
4775
4850
  refreshHighlights();
4776
4851
  }
4777
- function handleOpenEditor() {
4778
- const current = activeTarget();
4852
+ function handleOpenEditor(targetId) {
4853
+ const current = targetId === void 0 ? activeTarget() : targets.find((target) => target.id === targetId);
4779
4854
  if (state.status !== "selected" || current?.marker === void 0) {
4780
4855
  return;
4781
4856
  }
4857
+ if (activeTargetId !== current.id) {
4858
+ activeTargetId = current.id;
4859
+ refreshSelectionView();
4860
+ }
4782
4861
  const marker = current.marker;
4862
+ const selectionRevision = sessionRevision;
4863
+ const requestRevision = ++editorRequestRevision;
4783
4864
  transition({ type: "OPEN_EDITOR" });
4865
+ view.renderEditorStatus("opening");
4784
4866
  void api.openEditor({
4785
4867
  fileId: marker.fileId,
4786
4868
  line: marker.line,
4787
4869
  column: marker.column
4788
4870
  }).then(() => {
4789
- if (mounted && state.status === "selected") {
4790
- view.announce(view.messages().announcements.editorOpened);
4871
+ if (mounted && state.status === "selected" && selectionRevision === sessionRevision && requestRevision === editorRequestRevision && activeTargetId === current.id && targets.includes(current)) {
4872
+ view.renderEditorStatus("success");
4791
4873
  }
4792
4874
  }).catch(() => {
4793
- if (mounted && state.status === "selected") {
4794
- view.announce(view.messages().announcements.editorFailed);
4875
+ if (mounted && state.status === "selected" && selectionRevision === sessionRevision && requestRevision === editorRequestRevision && activeTargetId === current.id && targets.includes(current)) {
4876
+ view.renderEditorStatus("error");
4795
4877
  }
4796
4878
  });
4797
4879
  }
@@ -4808,6 +4890,9 @@ ${summary}`;
4808
4890
  view.updateTargetInstruction(target.id, target.instruction);
4809
4891
  view.setPreviewEnabled(canPreview());
4810
4892
  }
4893
+ function handleOpenEditorButtonClick() {
4894
+ handleOpenEditor();
4895
+ }
4811
4896
  function handleTargetListKeydown(event) {
4812
4897
  if (config.ai.enabled && state.status === "selected" && event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
4813
4898
  event.preventDefault();
@@ -4885,6 +4970,13 @@ ${summary}`;
4885
4970
  removeTarget(removeButton.dataset.removeTargetId);
4886
4971
  return;
4887
4972
  }
4973
+ const openButton = eventTarget.closest(
4974
+ "button[data-open-target-id]"
4975
+ );
4976
+ if (openButton?.dataset.openTargetId !== void 0) {
4977
+ handleOpenEditor(openButton.dataset.openTargetId);
4978
+ return;
4979
+ }
4888
4980
  const activateButton = eventTarget.closest(
4889
4981
  "button[data-activate-target-id]"
4890
4982
  );
@@ -4916,7 +5008,7 @@ ${summary}`;
4916
5008
  view.targetList.addEventListener("click", handleTargetListClick);
4917
5009
  view.targetList.addEventListener("input", handleTargetListInput);
4918
5010
  view.targetList.addEventListener("keydown", handleTargetListKeydown);
4919
- view.openEditorButton.addEventListener("click", handleOpenEditor);
5011
+ view.openEditorButton.addEventListener("click", handleOpenEditorButtonClick);
4920
5012
  view.previewButton.addEventListener("click", handlePreview);
4921
5013
  view.copyButton.addEventListener("click", handleCopy);
4922
5014
  view.backButton.addEventListener("click", handleBack);
@@ -4976,7 +5068,7 @@ ${summary}`;
4976
5068
  view.targetList.removeEventListener("click", handleTargetListClick);
4977
5069
  view.targetList.removeEventListener("input", handleTargetListInput);
4978
5070
  view.targetList.removeEventListener("keydown", handleTargetListKeydown);
4979
- view.openEditorButton.removeEventListener("click", handleOpenEditor);
5071
+ view.openEditorButton.removeEventListener("click", handleOpenEditorButtonClick);
4980
5072
  view.previewButton.removeEventListener("click", handlePreview);
4981
5073
  view.copyButton.removeEventListener("click", handleCopy);
4982
5074
  view.backButton.removeEventListener("click", handleBack);