@spotpatch/runtime 1.12.0 → 1.14.0

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.
Files changed (38) hide show
  1. package/dist/{chunk-BXRMVHNT.js → chunk-ASRUAKA5.js} +1 -1
  2. package/dist/chunk-ASRUAKA5.js.map +1 -0
  3. package/dist/chunk-KIDHJQRG.js +14 -0
  4. package/dist/chunk-KIDHJQRG.js.map +1 -0
  5. package/dist/{chunk-7ES63LA7.js → chunk-P6X75LO5.js} +11 -1
  6. package/dist/chunk-P6X75LO5.js.map +1 -0
  7. package/dist/chunk-P7W3F4UA.js +115 -0
  8. package/dist/chunk-P7W3F4UA.js.map +1 -0
  9. package/dist/contextual-ask-panel.cjs +1637 -0
  10. package/dist/contextual-ask-panel.cjs.map +1 -0
  11. package/dist/contextual-ask-panel.d.cts +163 -0
  12. package/dist/contextual-ask-panel.d.ts +163 -0
  13. package/dist/contextual-ask-panel.js +1593 -0
  14. package/dist/contextual-ask-panel.js.map +1 -0
  15. package/dist/data-flow-panel.cjs +9 -0
  16. package/dist/data-flow-panel.cjs.map +1 -1
  17. package/dist/data-flow-panel.js +1 -1
  18. package/dist/external-handoff-panel.cjs +29 -9
  19. package/dist/external-handoff-panel.cjs.map +1 -1
  20. package/dist/external-handoff-panel.d.cts +3 -3
  21. package/dist/external-handoff-panel.d.ts +3 -3
  22. package/dist/external-handoff-panel.js +22 -11
  23. package/dist/external-handoff-panel.js.map +1 -1
  24. package/dist/index.cjs +1285 -279
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.js +1250 -356
  27. package/dist/index.js.map +1 -1
  28. package/dist/motion-extension-contract-B3m26CML.d.cts +72 -0
  29. package/dist/motion-extension-contract-B3m26CML.d.ts +72 -0
  30. package/dist/motion.cjs +1251 -0
  31. package/dist/motion.cjs.map +1 -0
  32. package/dist/motion.d.cts +11 -0
  33. package/dist/motion.d.ts +11 -0
  34. package/dist/motion.js +1092 -0
  35. package/dist/motion.js.map +1 -0
  36. package/package.json +31 -3
  37. package/dist/chunk-7ES63LA7.js.map +0 -1
  38. package/dist/chunk-BXRMVHNT.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -424,7 +424,7 @@ function omitBrowserCodeContext(request) {
424
424
  };
425
425
  }
426
426
  function runtimeApiErrorCode(error) {
427
- return error instanceof RuntimeApiError ? error.code : void 0;
427
+ return error instanceof RuntimeApiError && isAgentErrorCode(error.code) ? error.code : void 0;
428
428
  }
429
429
  function createRuntimeApi(options) {
430
430
  const pendingRequests = /* @__PURE__ */ new Set();
@@ -1138,6 +1138,15 @@ var UI_Z_INDEX = Object.freeze({
1138
1138
  highlight: 2147483646,
1139
1139
  controls: 2147483647
1140
1140
  });
1141
+ var FLOATING_SURFACE_LAYOUT = Object.freeze({
1142
+ compactInset: 8,
1143
+ compactViewportWidth: 520,
1144
+ desktopInset: 24,
1145
+ dragActivationDistance: 6,
1146
+ edgeSnapDistance: 20,
1147
+ workbenchMaxHeight: 620,
1148
+ workbenchMaxWidth: 460
1149
+ });
1141
1150
 
1142
1151
  // src/picker/hit-test.ts
1143
1152
  function isInsideSpotPatchUI(element2) {
@@ -1986,6 +1995,7 @@ function createAgentPanel(document, ai, localizer) {
1986
1995
  root.hidden = !ai.enabled;
1987
1996
  const header = createMarkedElement(document, "div");
1988
1997
  header.className = "spotpatch-agent-head";
1998
+ header.setAttribute("data-spotpatch-agent-identity", "");
1989
1999
  const title = createMarkedElement(document, "span");
1990
2000
  title.className = "spotpatch-agent-title";
1991
2001
  const badge = createMarkedElement(document, "span");
@@ -2492,7 +2502,27 @@ function svgElement(document, tagName, attributes) {
2492
2502
  }
2493
2503
  return element2;
2494
2504
  }
2495
- function createBrandMark(document, content) {
2505
+ function namespaceSvgIds(svg, prefix) {
2506
+ const safePrefix = prefix.replace(/[^a-zA-Z0-9_-]/gu, "-");
2507
+ const ids = /* @__PURE__ */ new Map();
2508
+ for (const element2 of svg.querySelectorAll("[id]")) {
2509
+ const id = element2.id;
2510
+ const namespacedId = `${safePrefix}-${id}`;
2511
+ ids.set(id, namespacedId);
2512
+ element2.id = namespacedId;
2513
+ }
2514
+ for (const element2 of [svg, ...svg.querySelectorAll("*")]) {
2515
+ for (const attribute of [...element2.attributes]) {
2516
+ let value = attribute.value;
2517
+ for (const [id, namespacedId] of ids) {
2518
+ value = value.replaceAll(`url(#${id})`, `url(#${namespacedId})`);
2519
+ if (value === `#${id}`) value = `#${namespacedId}`;
2520
+ }
2521
+ if (value !== attribute.value) element2.setAttribute(attribute.name, value);
2522
+ }
2523
+ }
2524
+ }
2525
+ function createBrandMark(document, content, idPrefix) {
2496
2526
  const resolved = resolveBrandMarkContent(content);
2497
2527
  const svg = svgElement(document, "svg", {
2498
2528
  xmlns: SVG_NAMESPACE,
@@ -2502,6 +2532,7 @@ function createBrandMark(document, content) {
2502
2532
  focusable: "false"
2503
2533
  });
2504
2534
  svg.innerHTML = resolved.content;
2535
+ if (idPrefix !== void 0) namespaceSvgIds(svg, idPrefix);
2505
2536
  return svg;
2506
2537
  }
2507
2538
 
@@ -2511,79 +2542,24 @@ function getDataFlowExtension(target2 = globalThis) {
2511
2542
  return target2[DATA_FLOW_EXTENSION_KEY];
2512
2543
  }
2513
2544
 
2514
- // src/ui/dialog-placement.ts
2515
- var VIEWPORT_MARGIN = 16;
2516
- var TARGET_GAP = 14;
2517
- var TARGET_INSET = 24;
2518
- var ANCHOR_INSET = 22;
2519
- function clamp(value, minimum, maximum) {
2520
- return Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
2521
- }
2522
- function calculateDialogPlacement({
2523
- dialogHeight,
2524
- dialogWidth,
2525
- target: target2,
2526
- viewportHeight,
2527
- viewportWidth
2528
- }) {
2529
- const maxLeft = viewportWidth - dialogWidth - VIEWPORT_MARGIN;
2530
- const maxTop = viewportHeight - dialogHeight - VIEWPORT_MARGIN;
2531
- const viewportLeft = clamp(
2532
- (viewportWidth - dialogWidth) / 2,
2533
- VIEWPORT_MARGIN,
2534
- maxLeft
2535
- );
2536
- const viewportTop = clamp(
2537
- (viewportHeight - dialogHeight) / 2,
2538
- VIEWPORT_MARGIN,
2539
- maxTop
2540
- );
2541
- if (target2 === void 0) {
2542
- return Object.freeze({
2543
- anchorX: clamp(dialogWidth / 2, ANCHOR_INSET, dialogWidth - ANCHOR_INSET),
2544
- anchorY: clamp(dialogHeight / 2, ANCHOR_INSET, dialogHeight - ANCHOR_INSET),
2545
- left: viewportLeft,
2546
- mode: "viewport",
2547
- top: viewportTop
2548
- });
2549
- }
2550
- const targetCenterX = target2.x + target2.width / 2;
2551
- const targetCenterY = target2.y + target2.height / 2;
2552
- const centeredLeft = clamp(targetCenterX - dialogWidth / 2, VIEWPORT_MARGIN, maxLeft);
2553
- const centeredTop = clamp(targetCenterY - dialogHeight / 2, VIEWPORT_MARGIN, maxTop);
2554
- let left = centeredLeft;
2555
- let top = centeredTop;
2556
- let mode = "viewport";
2557
- if (target2.width >= dialogWidth + TARGET_INSET * 2 && target2.height >= dialogHeight + TARGET_INSET * 2) {
2558
- mode = "center";
2559
- } else if (target2.y - dialogHeight - TARGET_GAP >= VIEWPORT_MARGIN) {
2560
- top = target2.y - dialogHeight - TARGET_GAP;
2561
- mode = "above";
2562
- } else if (target2.y + target2.height + TARGET_GAP + dialogHeight <= viewportHeight - VIEWPORT_MARGIN) {
2563
- top = target2.y + target2.height + TARGET_GAP;
2564
- mode = "below";
2565
- } else if (target2.x + target2.width + TARGET_GAP + dialogWidth <= viewportWidth - VIEWPORT_MARGIN) {
2566
- left = target2.x + target2.width + TARGET_GAP;
2567
- mode = "right";
2568
- } else if (target2.x - dialogWidth - TARGET_GAP >= VIEWPORT_MARGIN) {
2569
- left = target2.x - dialogWidth - TARGET_GAP;
2570
- mode = "left";
2571
- }
2572
- return Object.freeze({
2573
- anchorX: clamp(targetCenterX - left, ANCHOR_INSET, dialogWidth - ANCHOR_INSET),
2574
- anchorY: clamp(targetCenterY - top, ANCHOR_INSET, dialogHeight - ANCHOR_INSET),
2575
- left,
2576
- mode,
2577
- top
2578
- });
2579
- }
2580
-
2581
2545
  // src/ui/external-handoff-contract.ts
2582
2546
  var EXTERNAL_HANDOFF_EXTENSION_KEY = /* @__PURE__ */ Symbol.for("spotpatch.external-handoff.v1");
2583
2547
  function getExternalHandoffExtension() {
2584
2548
  return globalThis[EXTERNAL_HANDOFF_EXTENSION_KEY];
2585
2549
  }
2586
2550
 
2551
+ // src/ui/contextual-ask-contract.ts
2552
+ var CONTEXTUAL_ASK_EXTENSION_KEY = /* @__PURE__ */ Symbol.for("spotpatch.contextual-ask.v1");
2553
+ function getContextualAskExtension() {
2554
+ return globalThis[CONTEXTUAL_ASK_EXTENSION_KEY];
2555
+ }
2556
+
2557
+ // src/ui/motion-extension-contract.ts
2558
+ var MOTION_EXTENSION_KEY = /* @__PURE__ */ Symbol.for("spotpatch.motion.v1");
2559
+ function getFloatingSurfaceMotionExtension() {
2560
+ return globalThis[MOTION_EXTENSION_KEY];
2561
+ }
2562
+
2587
2563
  // src/ui/localization.ts
2588
2564
  var import_shared6 = require("@spotpatch/shared");
2589
2565
  var STATUS_EN = Object.freeze({
@@ -2616,6 +2592,51 @@ var STATUS_ZH = Object.freeze({
2616
2592
  reverted: "\u5DF2\u64A4\u9500",
2617
2593
  failed: "\u5931\u8D25"
2618
2594
  });
2595
+ var EXECUTION_ACTIVITY_EN = Object.freeze({
2596
+ prepare: "Preparing context",
2597
+ dispatch: "Dispatching context",
2598
+ discover: "Discovering project files",
2599
+ search: "Searching source",
2600
+ read: "Reading",
2601
+ patch: "Generating a minimal patch",
2602
+ check: "Running",
2603
+ audit: "Auditing proposed changes",
2604
+ apply: "Applying verified changes",
2605
+ sync: "Returning results to SpotPatch",
2606
+ unknown: "Processing the current task"
2607
+ });
2608
+ var EXECUTION_ACTIVITY_ZH = Object.freeze({
2609
+ prepare: "\u6B63\u5728\u51C6\u5907\u4E0A\u4E0B\u6587",
2610
+ dispatch: "\u6B63\u5728\u6D3E\u53D1\u4E0A\u4E0B\u6587",
2611
+ discover: "\u6B63\u5728\u67E5\u627E\u9879\u76EE\u6587\u4EF6",
2612
+ search: "\u6B63\u5728\u641C\u7D22\u6E90\u7801",
2613
+ read: "\u6B63\u5728\u8BFB\u53D6",
2614
+ patch: "\u6B63\u5728\u751F\u6210\u6700\u5C0F\u4FEE\u6539\u8865\u4E01",
2615
+ check: "\u6B63\u5728\u8FD0\u884C",
2616
+ audit: "\u6B63\u5728\u5BA1\u8BA1\u5019\u9009\u4FEE\u6539",
2617
+ apply: "\u6B63\u5728\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6539",
2618
+ sync: "\u6B63\u5728\u5C06\u7ED3\u679C\u56DE\u6D41 SpotPatch",
2619
+ unknown: "\u6B63\u5728\u5904\u7406\u5F53\u524D\u4EFB\u52A1"
2620
+ });
2621
+ var EXECUTION_LANE_LABELS = Object.freeze({
2622
+ prepare: "prepare",
2623
+ dispatch: "dispatch",
2624
+ discover: "list",
2625
+ search: "search",
2626
+ read: "read",
2627
+ patch: "patch",
2628
+ check: "check",
2629
+ audit: "audit",
2630
+ apply: "apply",
2631
+ sync: "sync",
2632
+ unknown: "agent"
2633
+ });
2634
+ function executionActivityAction(labels, kind, detail) {
2635
+ return detail === void 0 || detail.length === 0 ? labels[kind] : `${labels[kind]} ${detail}`;
2636
+ }
2637
+ function executionActivityLane(kind, detail) {
2638
+ return detail === void 0 || detail.length === 0 ? EXECUTION_LANE_LABELS[kind] : `${EXECUTION_LANE_LABELS[kind]} \xB7 ${detail}`;
2639
+ }
2619
2640
  var EXTERNAL_HANDOFF_ERROR_EN = "The external Agent handoff request failed.";
2620
2641
  var EXTERNAL_HANDOFF_ERROR_ZH = "\u5916\u90E8 Agent \u4EA4\u63A5\u8BF7\u6C42\u5931\u8D25\u3002";
2621
2642
  var ERROR_MESSAGES_EN = Object.freeze({
@@ -2833,6 +2854,11 @@ var UI_MESSAGES = Object.freeze({
2833
2854
  previewTitle: "Review the request",
2834
2855
  previewSubtitle: "Verify the complete context before it leaves the browser."
2835
2856
  }),
2857
+ floatingSurface: Object.freeze({
2858
+ dragHandle: "Drag to move the SpotPatch workbench",
2859
+ resetPosition: "Reset workbench position",
2860
+ positionReset: "SpotPatch position reset to the lower-right corner."
2861
+ }),
2836
2862
  context: Object.freeze({
2837
2863
  collecting: "Collecting context",
2838
2864
  ready: "Context ready",
@@ -2919,6 +2945,27 @@ var UI_MESSAGES = Object.freeze({
2919
2945
  noOutput: "No output.",
2920
2946
  status: (status) => STATUS_EN[status]
2921
2947
  }),
2948
+ execution: Object.freeze({
2949
+ claude: "Claude",
2950
+ codex: "Codex",
2951
+ receivingTitle: "Receiving context",
2952
+ dispatchingTitle: (identity) => `Handing off to ${identity}`,
2953
+ runningTitle: (target2) => `Modifying ${target2}`,
2954
+ expandedRunningTitle: (identity) => `${identity} is making changes`,
2955
+ completedTitle: "Change complete",
2956
+ expandedCompletedTitle: "Changes complete",
2957
+ failedTitle: "Execution failed",
2958
+ receivingStatus: "Receiving",
2959
+ dispatchingStatus: "Dispatching",
2960
+ runningStatus: "Running",
2961
+ checkingStatus: "Checking",
2962
+ completedStatus: "Done",
2963
+ failedStatus: "Failed",
2964
+ resultReturned: "The result is back in SpotPatch",
2965
+ resultSummary: (files) => `${String(files)} ${files === 1 ? "file" : "files"}`,
2966
+ activityAction: (kind, detail) => executionActivityAction(EXECUTION_ACTIVITY_EN, kind, detail),
2967
+ activityLane: executionActivityLane
2968
+ }),
2922
2969
  announcements: Object.freeze({
2923
2970
  adapterDisabled: "React inspection was disabled after an adapter failure.",
2924
2971
  selectionEnabled: "Element selection enabled.",
@@ -2971,6 +3018,11 @@ var UI_MESSAGES = Object.freeze({
2971
3018
  previewTitle: "\u5BA1\u9605\u4FEE\u6539\u8BF7\u6C42",
2972
3019
  previewSubtitle: "\u53D1\u9001\u7ED9 AI \u524D\uFF0C\u8BF7\u6838\u5BF9\u5B8C\u6574\u4E0A\u4E0B\u6587\u4E0E\u6BCF\u9879\u76EE\u6807\u8BF4\u660E\u3002"
2973
3020
  }),
3021
+ floatingSurface: Object.freeze({
3022
+ dragHandle: "\u62D6\u62FD\u4EE5\u79FB\u52A8 SpotPatch \u5DE5\u4F5C\u53F0",
3023
+ resetPosition: "\u91CD\u7F6E\u5DE5\u4F5C\u53F0\u4F4D\u7F6E",
3024
+ positionReset: "SpotPatch \u5DF2\u6062\u590D\u5230\u53F3\u4E0B\u89D2\u3002"
3025
+ }),
2974
3026
  context: Object.freeze({
2975
3027
  collecting: "\u6B63\u5728\u91C7\u96C6\u4E0A\u4E0B\u6587",
2976
3028
  ready: "\u4E0A\u4E0B\u6587\u5DF2\u5C31\u7EEA",
@@ -3057,6 +3109,27 @@ var UI_MESSAGES = Object.freeze({
3057
3109
  noOutput: "\u6CA1\u6709\u8F93\u51FA\u3002",
3058
3110
  status: (status) => STATUS_ZH[status]
3059
3111
  }),
3112
+ execution: Object.freeze({
3113
+ claude: "Claude",
3114
+ codex: "Codex",
3115
+ receivingTitle: "\u6B63\u5728\u63A5\u6536\u4E0A\u4E0B\u6587",
3116
+ dispatchingTitle: (identity) => `\u6B63\u5728\u4EA4\u63A5\u7ED9 ${identity}`,
3117
+ runningTitle: (target2) => `\u6B63\u5728\u4FEE\u6539 ${target2}`,
3118
+ expandedRunningTitle: (identity) => `${identity} \u6B63\u5728\u6267\u884C\u4FEE\u6539`,
3119
+ completedTitle: "\u4FEE\u6539\u5B8C\u6210",
3120
+ expandedCompletedTitle: "\u4FEE\u6539\u5DF2\u5B8C\u6210",
3121
+ failedTitle: "\u6267\u884C\u5931\u8D25",
3122
+ receivingStatus: "\u63A5\u6536\u4E2D",
3123
+ dispatchingStatus: "\u6D3E\u53D1\u4E2D",
3124
+ runningStatus: "\u6267\u884C\u4E2D",
3125
+ checkingStatus: "\u68C0\u67E5\u4E2D",
3126
+ completedStatus: "\u5B8C\u6210",
3127
+ failedStatus: "\u5931\u8D25",
3128
+ resultReturned: "\u7ED3\u679C\u5DF2\u56DE\u6D41 SpotPatch",
3129
+ resultSummary: (files) => `${String(files)} \u4E2A\u6587\u4EF6`,
3130
+ activityAction: (kind, detail) => executionActivityAction(EXECUTION_ACTIVITY_ZH, kind, detail),
3131
+ activityLane: executionActivityLane
3132
+ }),
3060
3133
  announcements: Object.freeze({
3061
3134
  adapterDisabled: "React \u9002\u914D\u5668\u5F02\u5E38\uFF0C\u672C\u6B21\u4F1A\u8BDD\u5DF2\u505C\u7528 React \u68C0\u67E5\u3002",
3062
3135
  selectionEnabled: "\u5143\u7D20\u9009\u62E9\u5DF2\u542F\u7528\u3002",
@@ -3130,13 +3203,500 @@ function createUiLocalizer(document, preference = "auto") {
3130
3203
  });
3131
3204
  }
3132
3205
 
3133
- // src/ui/runtime-view.ts
3134
- var DIALOG_MAX_WIDTH = 460;
3135
- var DIALOG_MAX_HEIGHT = 620;
3136
- var DIALOG_FALLBACK_HEIGHT = Object.freeze({
3137
- previewing: 560,
3138
- selected: DIALOG_MAX_HEIGHT
3206
+ // src/state/floating-surface-session.ts
3207
+ var STORAGE_PREFIX2 = "spotpatch:floating-surface:";
3208
+ var SNAPSHOT_VERSION2 = 1;
3209
+ var MAX_SNAPSHOT_CHARACTERS2 = 1024;
3210
+ function record2(value) {
3211
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
3212
+ }
3213
+ function finiteRatio(value) {
3214
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
3215
+ }
3216
+ function alignment(value) {
3217
+ return value === "start" || value === "end";
3218
+ }
3219
+ function parseSnapshot2(value) {
3220
+ const snapshot = record2(value);
3221
+ if (snapshot?.version !== SNAPSHOT_VERSION2 || !alignment(snapshot.horizontal) || !alignment(snapshot.vertical) || !finiteRatio(snapshot.xRatio) || !finiteRatio(snapshot.yRatio)) {
3222
+ return void 0;
3223
+ }
3224
+ return Object.freeze({
3225
+ horizontal: snapshot.horizontal,
3226
+ vertical: snapshot.vertical,
3227
+ xRatio: snapshot.xRatio,
3228
+ yRatio: snapshot.yRatio
3229
+ });
3230
+ }
3231
+ function createFloatingSurfaceSession(window, sessionId) {
3232
+ const key = `${STORAGE_PREFIX2}${sessionId}`;
3233
+ let storage;
3234
+ try {
3235
+ storage = window.sessionStorage;
3236
+ } catch {
3237
+ storage = void 0;
3238
+ }
3239
+ return Object.freeze({
3240
+ clear() {
3241
+ try {
3242
+ storage?.removeItem(key);
3243
+ } catch {
3244
+ }
3245
+ },
3246
+ load() {
3247
+ let serialized;
3248
+ try {
3249
+ serialized = storage?.getItem(key) ?? null;
3250
+ } catch {
3251
+ return void 0;
3252
+ }
3253
+ if (serialized === null || serialized.length > MAX_SNAPSHOT_CHARACTERS2) {
3254
+ return void 0;
3255
+ }
3256
+ try {
3257
+ return parseSnapshot2(JSON.parse(serialized));
3258
+ } catch {
3259
+ return void 0;
3260
+ }
3261
+ },
3262
+ save(position) {
3263
+ try {
3264
+ storage?.setItem(
3265
+ key,
3266
+ JSON.stringify({ version: SNAPSHOT_VERSION2, ...position })
3267
+ );
3268
+ } catch {
3269
+ }
3270
+ }
3271
+ });
3272
+ }
3273
+
3274
+ // src/ui/floating-surface-position.ts
3275
+ var DEFAULT_FLOATING_SURFACE_POSITION = Object.freeze({
3276
+ horizontal: "end",
3277
+ vertical: "end",
3278
+ xRatio: 1,
3279
+ yRatio: 1
3139
3280
  });
3281
+ function clamp(value, minimum, maximum) {
3282
+ return Math.min(Math.max(value, minimum), maximum);
3283
+ }
3284
+ function finiteNonNegative(value) {
3285
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
3286
+ }
3287
+ function boundedRatio(value) {
3288
+ return Number.isFinite(value) ? clamp(value, 0, 1) : 0.5;
3289
+ }
3290
+ function normalizedViewport(viewport) {
3291
+ return Object.freeze({
3292
+ height: finiteNonNegative(viewport.height),
3293
+ left: Number.isFinite(viewport.left) ? viewport.left : 0,
3294
+ top: Number.isFinite(viewport.top) ? viewport.top : 0,
3295
+ width: finiteNonNegative(viewport.width)
3296
+ });
3297
+ }
3298
+ function horizontalAnchor(rect, alignment2) {
3299
+ return alignment2 === "end" ? rect.left + rect.width : rect.left;
3300
+ }
3301
+ function verticalAnchor(rect, alignment2) {
3302
+ return alignment2 === "end" ? rect.top + rect.height : rect.top;
3303
+ }
3304
+ function normalizedCoordinate(coordinate, origin, length) {
3305
+ if (length <= 0) {
3306
+ return 0.5;
3307
+ }
3308
+ return boundedRatio((coordinate - origin) / length);
3309
+ }
3310
+ function positionFromAnchor(anchor, viewport, horizontal, vertical) {
3311
+ return Object.freeze({
3312
+ horizontal,
3313
+ vertical,
3314
+ xRatio: normalizedCoordinate(anchor.x, viewport.left, viewport.width),
3315
+ yRatio: normalizedCoordinate(anchor.y, viewport.top, viewport.height)
3316
+ });
3317
+ }
3318
+ function resolveFloatingSurfaceRect(position, size, viewport) {
3319
+ const width = finiteNonNegative(size.width);
3320
+ const height = finiteNonNegative(size.height);
3321
+ const normalized = normalizedViewport(viewport);
3322
+ const viewportWidth = normalized.width;
3323
+ const viewportHeight = normalized.height;
3324
+ const leftBoundary = normalized.left;
3325
+ const topBoundary = normalized.top;
3326
+ const rightBoundary = leftBoundary + viewportWidth;
3327
+ const bottomBoundary = topBoundary + viewportHeight;
3328
+ const anchorX = leftBoundary + boundedRatio(position.xRatio) * viewportWidth;
3329
+ const anchorY = topBoundary + boundedRatio(position.yRatio) * viewportHeight;
3330
+ const requestedLeft = position.horizontal === "end" ? anchorX - width : anchorX;
3331
+ const requestedTop = position.vertical === "end" ? anchorY - height : anchorY;
3332
+ const maximumLeft = Math.max(leftBoundary, rightBoundary - width);
3333
+ const maximumTop = Math.max(topBoundary, bottomBoundary - height);
3334
+ return Object.freeze({
3335
+ height,
3336
+ left: clamp(requestedLeft, leftBoundary, maximumLeft),
3337
+ top: clamp(requestedTop, topBoundary, maximumTop),
3338
+ width
3339
+ });
3340
+ }
3341
+ function snapFloatingSurfaceRect(rect, viewport, snapDistance) {
3342
+ const distance = finiteNonNegative(snapDistance);
3343
+ const width = finiteNonNegative(rect.width);
3344
+ const height = finiteNonNegative(rect.height);
3345
+ const normalized = normalizedViewport(viewport);
3346
+ const viewportRight = normalized.left + normalized.width;
3347
+ const viewportBottom = normalized.top + normalized.height;
3348
+ const maximumLeft = Math.max(normalized.left, viewportRight - width);
3349
+ const maximumTop = Math.max(normalized.top, viewportBottom - height);
3350
+ const requestedLeft = clamp(rect.left, normalized.left, maximumLeft);
3351
+ const requestedTop = clamp(rect.top, normalized.top, maximumTop);
3352
+ const leftDistance = Math.abs(requestedLeft - normalized.left);
3353
+ const rightDistance = Math.abs(viewportRight - (requestedLeft + width));
3354
+ const topDistance = Math.abs(requestedTop - normalized.top);
3355
+ const bottomDistance = Math.abs(viewportBottom - (requestedTop + height));
3356
+ const left = Math.min(leftDistance, rightDistance) <= distance ? leftDistance <= rightDistance ? normalized.left : viewportRight - width : requestedLeft;
3357
+ const top = Math.min(topDistance, bottomDistance) <= distance ? topDistance <= bottomDistance ? normalized.top : viewportBottom - height : requestedTop;
3358
+ return Object.freeze({ height, left, top, width });
3359
+ }
3360
+ function positionFromFloatingSurfaceRect(rect, viewport, previous) {
3361
+ const bounded = snapFloatingSurfaceRect(rect, viewport, 0);
3362
+ const centerX = bounded.left + bounded.width / 2;
3363
+ const centerY = bounded.top + bounded.height / 2;
3364
+ const viewportCenterX = viewport.left + finiteNonNegative(viewport.width) / 2;
3365
+ const viewportCenterY = viewport.top + finiteNonNegative(viewport.height) / 2;
3366
+ const horizontal = centerX === viewportCenterX ? previous.horizontal : centerX < viewportCenterX ? "start" : "end";
3367
+ const vertical = centerY === viewportCenterY ? previous.vertical : centerY < viewportCenterY ? "start" : "end";
3368
+ return positionFromAnchor(
3369
+ {
3370
+ x: horizontalAnchor(bounded, horizontal),
3371
+ y: verticalAnchor(bounded, vertical)
3372
+ },
3373
+ viewport,
3374
+ horizontal,
3375
+ vertical
3376
+ );
3377
+ }
3378
+
3379
+ // src/ui/floating-surface-controller.ts
3380
+ function finite(value, fallback = 0) {
3381
+ return Number.isFinite(value) ? value : fallback;
3382
+ }
3383
+ function distanceBetween(first, second) {
3384
+ return Math.hypot(first.x - second.x, first.y - second.y);
3385
+ }
3386
+ function isPrimaryPointer(event) {
3387
+ return event.button === 0 && event.isPrimary;
3388
+ }
3389
+ function surfaceSize(surface) {
3390
+ const rect = surface.getBoundingClientRect();
3391
+ return Object.freeze({
3392
+ height: Math.max(0, finite(rect.height)),
3393
+ width: Math.max(0, finite(rect.width))
3394
+ });
3395
+ }
3396
+ function requestFrame(window, callback) {
3397
+ if (typeof window.requestAnimationFrame === "function") {
3398
+ return window.requestAnimationFrame(callback);
3399
+ }
3400
+ return window.setTimeout(() => {
3401
+ callback(Date.now());
3402
+ }, 0);
3403
+ }
3404
+ function cancelFrame(window, frame) {
3405
+ if (typeof window.cancelAnimationFrame === "function") {
3406
+ window.cancelAnimationFrame(frame);
3407
+ return;
3408
+ }
3409
+ window.clearTimeout(frame);
3410
+ }
3411
+ function createFloatingSurfaceController(window, session, layout) {
3412
+ let position = session.load() ?? DEFAULT_FLOATING_SURFACE_POSITION;
3413
+ let drag;
3414
+ let frame;
3415
+ let suppressClickFor;
3416
+ let suppressClickTimer;
3417
+ let disposed = false;
3418
+ const surfaces = /* @__PURE__ */ new Set();
3419
+ const cleanup = /* @__PURE__ */ new Set();
3420
+ function viewport() {
3421
+ const visualViewport = window.visualViewport;
3422
+ const width = Math.max(0, finite(visualViewport?.width ?? window.innerWidth));
3423
+ const height = Math.max(0, finite(visualViewport?.height ?? window.innerHeight));
3424
+ const inset = width <= layout.compactViewportWidth ? layout.compactInset : layout.desktopInset;
3425
+ return Object.freeze({
3426
+ height: Math.max(0, height - inset * 2),
3427
+ left: finite(visualViewport?.offsetLeft ?? 0) + inset,
3428
+ top: finite(visualViewport?.offsetTop ?? 0) + inset,
3429
+ width: Math.max(0, width - inset * 2)
3430
+ });
3431
+ }
3432
+ function isCompact() {
3433
+ return viewport().width + layout.compactInset * 2 <= layout.compactViewportWidth;
3434
+ }
3435
+ function apply(surface) {
3436
+ if (surface.hidden) {
3437
+ return void 0;
3438
+ }
3439
+ if (surface.dataset.motionMorphing === "true" && surface.dataset.floatingPositioned === "true") {
3440
+ return void 0;
3441
+ }
3442
+ const size = surfaceSize(surface);
3443
+ if (size.width === 0 || size.height === 0) {
3444
+ return void 0;
3445
+ }
3446
+ const rect = resolveFloatingSurfaceRect(position, size, viewport());
3447
+ surface.style.left = `${String(rect.left)}px`;
3448
+ surface.style.top = `${String(rect.top)}px`;
3449
+ surface.style.right = "auto";
3450
+ surface.style.bottom = "auto";
3451
+ surface.dataset.floatingPositioned = "true";
3452
+ return rect;
3453
+ }
3454
+ function reconcile() {
3455
+ if (disposed) {
3456
+ return;
3457
+ }
3458
+ if (frame !== void 0) {
3459
+ cancelFrame(window, frame);
3460
+ frame = void 0;
3461
+ }
3462
+ for (const surface of surfaces) {
3463
+ apply(surface);
3464
+ }
3465
+ }
3466
+ function requestReconcile() {
3467
+ if (disposed || frame !== void 0) {
3468
+ return;
3469
+ }
3470
+ frame = requestFrame(window, () => {
3471
+ frame = void 0;
3472
+ reconcile();
3473
+ });
3474
+ }
3475
+ function commit(nextPosition) {
3476
+ position = nextPosition;
3477
+ session.save(position);
3478
+ }
3479
+ function finishDrag(commitPosition) {
3480
+ const current = drag;
3481
+ if (current === void 0) {
3482
+ return;
3483
+ }
3484
+ drag = void 0;
3485
+ current.handle.dataset.dragging = "false";
3486
+ current.surface.dataset.dragging = "false";
3487
+ try {
3488
+ if (current.handle.hasPointerCapture(current.pointerId)) {
3489
+ current.handle.releasePointerCapture(current.pointerId);
3490
+ }
3491
+ } catch {
3492
+ }
3493
+ if (commitPosition) {
3494
+ const rect = apply(current.surface);
3495
+ if (rect !== void 0) {
3496
+ const snapped = snapFloatingSurfaceRect(
3497
+ rect,
3498
+ viewport(),
3499
+ layout.edgeSnapDistance
3500
+ );
3501
+ commit(positionFromFloatingSurfaceRect(snapped, viewport(), position));
3502
+ apply(current.surface);
3503
+ }
3504
+ } else {
3505
+ position = session.load() ?? DEFAULT_FLOATING_SURFACE_POSITION;
3506
+ reconcile();
3507
+ }
3508
+ }
3509
+ function updateDrag(event) {
3510
+ const current = drag;
3511
+ if (current === void 0) {
3512
+ return;
3513
+ }
3514
+ if (event.pointerId !== current.pointerId) {
3515
+ return;
3516
+ }
3517
+ const pointer = Object.freeze({ x: event.clientX, y: event.clientY });
3518
+ const start = Object.freeze({ x: current.startX, y: current.startY });
3519
+ if (current.handle.dataset.dragging !== "true" && distanceBetween(pointer, start) < layout.dragActivationDistance) {
3520
+ return;
3521
+ }
3522
+ if (current.handle.dataset.dragging !== "true") {
3523
+ current.onDragStart?.();
3524
+ }
3525
+ current.handle.dataset.dragging = "true";
3526
+ current.surface.dataset.dragging = "true";
3527
+ position = positionFromAnchor(
3528
+ {
3529
+ x: pointer.x + current.anchorOffsetX,
3530
+ y: pointer.y + current.anchorOffsetY
3531
+ },
3532
+ viewport(),
3533
+ position.horizontal,
3534
+ position.vertical
3535
+ );
3536
+ requestReconcile();
3537
+ event.preventDefault();
3538
+ }
3539
+ function addListener(target2, type, listener, options) {
3540
+ target2.addEventListener(type, listener, options);
3541
+ cleanup.add(() => {
3542
+ target2.removeEventListener(type, listener, options);
3543
+ });
3544
+ }
3545
+ function attachDraggable(handle, surface, options = {}) {
3546
+ const onPointerDown = (event) => {
3547
+ if (disposed || drag !== void 0 || !isPrimaryPointer(event) || options.canStartDrag?.(event) === false) {
3548
+ return;
3549
+ }
3550
+ const rect = apply(surface) ?? surface.getBoundingClientRect();
3551
+ const anchorX = position.horizontal === "end" ? rect.left + rect.width : rect.left;
3552
+ const anchorY = position.vertical === "end" ? rect.top + rect.height : rect.top;
3553
+ drag = Object.freeze({
3554
+ anchorOffsetX: anchorX - event.clientX,
3555
+ anchorOffsetY: anchorY - event.clientY,
3556
+ handle,
3557
+ ...options.onDragStart === void 0 ? {} : { onDragStart: options.onDragStart },
3558
+ pointerId: event.pointerId,
3559
+ startX: event.clientX,
3560
+ startY: event.clientY,
3561
+ suppressClickOnDrag: options.suppressClickOnDrag ?? false,
3562
+ surface
3563
+ });
3564
+ try {
3565
+ handle.setPointerCapture(event.pointerId);
3566
+ } catch {
3567
+ }
3568
+ };
3569
+ const onLostPointerCapture = (event) => {
3570
+ if (drag?.pointerId === event.pointerId) {
3571
+ finishDrag(handle.dataset.dragging === "true");
3572
+ }
3573
+ };
3574
+ const onClickCapture = (event) => {
3575
+ if (suppressClickFor !== handle) {
3576
+ return;
3577
+ }
3578
+ suppressClickFor = void 0;
3579
+ if (suppressClickTimer !== void 0) {
3580
+ window.clearTimeout(suppressClickTimer);
3581
+ suppressClickTimer = void 0;
3582
+ }
3583
+ event.preventDefault();
3584
+ event.stopImmediatePropagation();
3585
+ };
3586
+ addListener(handle, "pointerdown", (event) => {
3587
+ onPointerDown(event);
3588
+ });
3589
+ addListener(handle, "lostpointercapture", (event) => {
3590
+ onLostPointerCapture(event);
3591
+ });
3592
+ addListener(
3593
+ handle,
3594
+ "click",
3595
+ (event) => {
3596
+ onClickCapture(event);
3597
+ },
3598
+ true
3599
+ );
3600
+ }
3601
+ function reset() {
3602
+ finishDrag(false);
3603
+ position = DEFAULT_FLOATING_SURFACE_POSITION;
3604
+ session.clear();
3605
+ reconcile();
3606
+ }
3607
+ function cancelOnWindowBlur() {
3608
+ finishDrag(false);
3609
+ }
3610
+ function cancelOnEscape(event) {
3611
+ if (event.key !== "Escape" || drag === void 0) {
3612
+ return;
3613
+ }
3614
+ finishDrag(false);
3615
+ event.preventDefault();
3616
+ event.stopImmediatePropagation();
3617
+ }
3618
+ function endPointer(event) {
3619
+ if (drag?.pointerId !== event.pointerId) {
3620
+ return;
3621
+ }
3622
+ const current = drag;
3623
+ const moved = current.handle.dataset.dragging === "true";
3624
+ finishDrag(moved);
3625
+ if (moved && current.suppressClickOnDrag) {
3626
+ suppressClickFor = current.handle;
3627
+ suppressClickTimer = window.setTimeout(() => {
3628
+ suppressClickFor = void 0;
3629
+ suppressClickTimer = void 0;
3630
+ }, 0);
3631
+ }
3632
+ }
3633
+ function cancelPointer(event) {
3634
+ if (drag?.pointerId === event.pointerId) {
3635
+ finishDrag(false);
3636
+ }
3637
+ }
3638
+ addListener(window, "pointermove", (event) => {
3639
+ updateDrag(event);
3640
+ });
3641
+ addListener(window, "pointerup", (event) => {
3642
+ endPointer(event);
3643
+ });
3644
+ addListener(window, "pointercancel", (event) => {
3645
+ cancelPointer(event);
3646
+ });
3647
+ addListener(window, "resize", requestReconcile);
3648
+ addListener(window, "blur", cancelOnWindowBlur);
3649
+ addListener(
3650
+ window,
3651
+ "keydown",
3652
+ (event) => {
3653
+ cancelOnEscape(event);
3654
+ },
3655
+ true
3656
+ );
3657
+ window.visualViewport?.addEventListener("resize", requestReconcile);
3658
+ window.visualViewport?.addEventListener("scroll", requestReconcile);
3659
+ cleanup.add(
3660
+ () => window.visualViewport?.removeEventListener("resize", requestReconcile)
3661
+ );
3662
+ cleanup.add(
3663
+ () => window.visualViewport?.removeEventListener("scroll", requestReconcile)
3664
+ );
3665
+ return Object.freeze({
3666
+ attachDraggable,
3667
+ dispose() {
3668
+ if (disposed) {
3669
+ return;
3670
+ }
3671
+ disposed = true;
3672
+ finishDrag(false);
3673
+ if (frame !== void 0) {
3674
+ cancelFrame(window, frame);
3675
+ frame = void 0;
3676
+ }
3677
+ if (suppressClickTimer !== void 0) {
3678
+ window.clearTimeout(suppressClickTimer);
3679
+ suppressClickTimer = void 0;
3680
+ }
3681
+ suppressClickFor = void 0;
3682
+ for (const disposeListener of cleanup) {
3683
+ disposeListener();
3684
+ }
3685
+ cleanup.clear();
3686
+ surfaces.clear();
3687
+ },
3688
+ isCompact,
3689
+ reconcile,
3690
+ registerSurface(surface) {
3691
+ surfaces.add(surface);
3692
+ },
3693
+ requestReconcile,
3694
+ reset
3695
+ });
3696
+ }
3697
+
3698
+ // src/ui/runtime-view.ts
3699
+ var SUCCESS_SETTLE_MS = 1500;
3140
3700
  function resolveStyleNonce(document) {
3141
3701
  const nonces = new Set(
3142
3702
  [...document.querySelectorAll("script[nonce]")].map((script) => script.nonce?.trim() ?? "").filter(Boolean)
@@ -3167,53 +3727,20 @@ function createStyles(document) {
3167
3727
  --spotpatch-success: #34d399;
3168
3728
  --spotpatch-warning: #f59e0b;
3169
3729
  --spotpatch-text-on-accent: #0b0b12;
3170
- --spotpatch-radius-panel: 16px;
3171
3730
  --spotpatch-radius-card: 10px;
3172
- --spotpatch-shadow-panel: 0 30px 60px -20px rgb(0 0 0 / 70%);
3173
- color-scheme: dark;
3174
3731
  color: var(--spotpatch-text);
3175
3732
  font-family: Inter, "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
3176
3733
  font-size: 14px;
3177
3734
  line-height: 1.5;
3178
- text-rendering: optimizeLegibility;
3179
- -webkit-font-smoothing: antialiased;
3180
3735
  }
3181
3736
  [hidden] { display: none !important; }
3182
3737
  button, textarea, select { font: inherit; }
3183
- button { -webkit-tap-highlight-color: transparent; }
3184
- .spotpatch-trigger {
3738
+ .spotpatch-floating-surface {
3185
3739
  position: fixed;
3186
- right: 24px;
3187
- bottom: 24px;
3740
+ right: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3741
+ bottom: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3188
3742
  z-index: ${String(UI_Z_INDEX.controls)};
3189
- display: inline-flex;
3190
- align-items: center;
3191
- gap: 10px;
3192
- min-height: 44px;
3193
- border: 1px solid rgb(255 255 255 / 12%);
3194
- border-radius: 999px;
3195
- padding: 10px 17px;
3196
- color: #f8fafc;
3197
- background: #171b23;
3198
- box-shadow: 0 12px 34px rgb(0 0 0 / 25%), inset 0 1px rgb(255 255 255 / 5%);
3199
- cursor: pointer;
3200
- font-size: 14px;
3201
- font-weight: 650;
3202
- transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease;
3203
- }
3204
- .spotpatch-trigger::before {
3205
- width: 9px;
3206
- height: 9px;
3207
- border-radius: 999px;
3208
- background: var(--spotpatch-accent);
3209
- box-shadow: 0 0 0 4px rgb(99 102 241 / 10%);
3210
- content: "";
3211
- }
3212
- .spotpatch-trigger:hover { transform: translateY(-1px); border-color: rgb(129 140 248 / 52%); }
3213
- .spotpatch-trigger[aria-pressed="true"] {
3214
- border-color: rgb(124 58 237 / 55%);
3215
- background: #24203b;
3216
- box-shadow: 0 14px 42px rgb(76 29 149 / 24%), inset 0 1px rgb(255 255 255 / 10%);
3743
+ isolation: isolate;
3217
3744
  }
3218
3745
  .spotpatch-highlight {
3219
3746
  position: fixed;
@@ -3282,82 +3809,28 @@ function createStyles(document) {
3282
3809
  background: #5546dc;
3283
3810
  }
3284
3811
  .spotpatch-dialog {
3285
- --spotpatch-anchor-x: 50%;
3286
- --spotpatch-anchor-y: 50%;
3287
- position: fixed;
3288
- top: 16px;
3289
- left: 16px;
3290
- z-index: ${String(UI_Z_INDEX.controls)};
3812
+ position: relative;
3291
3813
  box-sizing: border-box;
3292
- width: min(${String(DIALOG_MAX_WIDTH)}px, calc(100vw - 32px));
3293
- color: #edf0f7;
3814
+ width: min(${String(FLOATING_SURFACE_LAYOUT.workbenchMaxWidth)}px, calc(100vw - ${String(FLOATING_SURFACE_LAYOUT.desktopInset * 2)}px));
3294
3815
  outline: none;
3295
- filter: drop-shadow(var(--spotpatch-shadow-panel));
3296
3816
  }
3297
- .spotpatch-anchor {
3298
- position: absolute;
3299
- z-index: 0;
3300
- width: 14px;
3301
- height: 14px;
3302
- border: 1px solid rgb(255 255 255 / 12%);
3303
- background: var(--spotpatch-bg);
3304
- transform: rotate(45deg);
3305
- }
3306
- .spotpatch-dialog[data-placement="above"] .spotpatch-anchor {
3307
- bottom: -6px;
3308
- left: calc(var(--spotpatch-anchor-x) - 7px);
3309
- border-top: 0;
3310
- border-left: 0;
3311
- }
3312
- .spotpatch-dialog[data-placement="below"] .spotpatch-anchor {
3313
- top: -6px;
3314
- left: calc(var(--spotpatch-anchor-x) - 7px);
3315
- border-right: 0;
3316
- border-bottom: 0;
3317
- }
3318
- .spotpatch-dialog[data-placement="left"] .spotpatch-anchor {
3319
- top: calc(var(--spotpatch-anchor-y) - 7px);
3320
- right: -6px;
3321
- border-bottom: 0;
3322
- border-left: 0;
3323
- }
3324
- .spotpatch-dialog[data-placement="right"] .spotpatch-anchor {
3325
- top: calc(var(--spotpatch-anchor-y) - 7px);
3326
- left: -6px;
3327
- border-top: 0;
3328
- border-right: 0;
3329
- }
3330
- .spotpatch-dialog[data-placement="center"] .spotpatch-anchor,
3331
- .spotpatch-dialog[data-placement="viewport"] .spotpatch-anchor { display: none; }
3332
3817
  .spotpatch-shell {
3333
3818
  position: relative;
3334
3819
  z-index: 1;
3335
3820
  display: flex;
3336
3821
  box-sizing: border-box;
3337
- max-height: min(${String(DIALOG_MAX_HEIGHT)}px, calc(100vh - 32px));
3822
+ max-height: min(${String(FLOATING_SURFACE_LAYOUT.workbenchMaxHeight)}px, calc(100vh - ${String(FLOATING_SURFACE_LAYOUT.desktopInset * 2)}px));
3338
3823
  overflow: hidden;
3339
3824
  flex-direction: column;
3340
- border: 1px solid var(--spotpatch-border);
3341
- border-radius: var(--spotpatch-radius-panel);
3342
- background: var(--spotpatch-bg);
3343
- box-shadow: inset 0 1px rgb(255 255 255 / 5%), 0 0 60px -32px rgb(139 123 255 / 20%);
3344
- }
3345
- .spotpatch-shell::before {
3346
- position: absolute;
3347
- z-index: 2;
3348
- top: 0;
3349
- right: 20%;
3350
- left: 20%;
3351
- height: 1px;
3352
- background: linear-gradient(90deg, transparent, var(--spotpatch-accent), var(--spotpatch-accent-cyan), transparent);
3353
- content: "";
3354
- opacity: .65;
3355
- pointer-events: none;
3356
3825
  }
3357
3826
  .spotpatch-header {
3358
3827
  position: relative;
3359
3828
  padding: 0 18px 14px;
3360
3829
  }
3830
+ .spotpatch-header[data-spotpatch-drag-handle] { cursor: grab; touch-action: none; user-select: none; }
3831
+ .spotpatch-header[data-dragging="true"] { cursor: grabbing; }
3832
+ .spotpatch-header button,
3833
+ .spotpatch-header a { cursor: pointer; user-select: auto; }
3361
3834
  .spotpatch-brand-row {
3362
3835
  display: flex;
3363
3836
  align-items: center;
@@ -3381,18 +3854,6 @@ function createStyles(document) {
3381
3854
  filter: drop-shadow(0 6px 12px rgb(76 29 149 / 22%));
3382
3855
  }
3383
3856
  .spotpatch-brand-copy { display: grid; min-width: 0; gap: 1px; }
3384
- .spotpatch-brand-name {
3385
- color: #fff;
3386
- font-size: 13.5px;
3387
- font-weight: 680;
3388
- letter-spacing: -.01em;
3389
- }
3390
- .spotpatch-brand-context {
3391
- color: #8e98aa;
3392
- font-size: 10.5px;
3393
- font-weight: 560;
3394
- letter-spacing: .02em;
3395
- }
3396
3857
  .spotpatch-header-controls {
3397
3858
  display: inline-flex;
3398
3859
  align-items: center;
@@ -3401,6 +3862,7 @@ function createStyles(document) {
3401
3862
  }
3402
3863
  .spotpatch-repository,
3403
3864
  .spotpatch-locale,
3865
+ .spotpatch-reset-position,
3404
3866
  .spotpatch-close {
3405
3867
  height: 27px;
3406
3868
  border: 1px solid rgb(255 255 255 / 11%);
@@ -3424,6 +3886,16 @@ function createStyles(document) {
3424
3886
  font-size: 12px;
3425
3887
  font-weight: 650;
3426
3888
  }
3889
+ .spotpatch-reset-position {
3890
+ display: inline-grid;
3891
+ width: 27px;
3892
+ padding: 0;
3893
+ place-items: center;
3894
+ color: #c5cad5;
3895
+ cursor: pointer;
3896
+ font-size: 15px;
3897
+ line-height: 1;
3898
+ }
3427
3899
  .spotpatch-close {
3428
3900
  display: inline-grid;
3429
3901
  width: 27px;
@@ -3437,21 +3909,8 @@ function createStyles(document) {
3437
3909
  }
3438
3910
  .spotpatch-close:hover,
3439
3911
  .spotpatch-locale:hover,
3912
+ .spotpatch-reset-position:hover,
3440
3913
  .spotpatch-repository:hover { border-color: rgb(129 112 247 / 45%); color: #fff; background: rgb(109 93 246 / 10%); }
3441
- .spotpatch-title {
3442
- margin: 0;
3443
- color: #fff;
3444
- font-size: 18px;
3445
- font-weight: 680;
3446
- letter-spacing: -.015em;
3447
- }
3448
- .spotpatch-subtitle {
3449
- max-width: 390px;
3450
- margin: 4px 0 0;
3451
- color: var(--spotpatch-text-secondary);
3452
- font-size: 12.5px;
3453
- line-height: 1.5;
3454
- }
3455
3914
  .spotpatch-target-row {
3456
3915
  display: flex;
3457
3916
  align-items: center;
@@ -3815,6 +4274,7 @@ function createStyles(document) {
3815
4274
  .spotpatch-target-open:focus-visible,
3816
4275
  .spotpatch-target-remove:focus-visible,
3817
4276
  .spotpatch-close:focus-visible,
4277
+ .spotpatch-reset-position:focus-visible,
3818
4278
  .spotpatch-trigger:focus-visible,
3819
4279
  .spotpatch-target-select:focus-visible,
3820
4280
  .spotpatch-target-editor textarea:focus-visible,
@@ -3839,8 +4299,8 @@ function createStyles(document) {
3839
4299
  .spotpatch-diagnostics > summary::before { transition: none; }
3840
4300
  }
3841
4301
  @media (max-width: 520px) {
3842
- .spotpatch-dialog { top: 8px; left: 8px; width: calc(100vw - 16px); }
3843
- .spotpatch-shell { max-height: calc(100vh - 16px); border-radius: 14px; }
4302
+ .spotpatch-dialog { width: calc(100vw - 16px); }
4303
+ .spotpatch-shell { max-height: calc(100dvh - 16px); }
3844
4304
  .spotpatch-header, .spotpatch-body { padding-left: 14px; padding-right: 14px; }
3845
4305
  .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 14px; padding-left: 14px; }
3846
4306
  .spotpatch-actions { padding-right: 14px; padding-left: 14px; }
@@ -3856,6 +4316,38 @@ function summaryLine(summary, prefix) {
3856
4316
  const line = summary.split("\n").find((candidate) => candidate.startsWith(`${prefix}: `));
3857
4317
  return line?.slice(prefix.length + 2).trim();
3858
4318
  }
4319
+ function startsOnInteractiveControl(event) {
4320
+ const target2 = event.target;
4321
+ return target2 instanceof Element && target2.closest(
4322
+ "a, button, input, select, textarea, summary, [contenteditable='true']"
4323
+ ) !== null;
4324
+ }
4325
+ function createFallbackExecutionIsland(document) {
4326
+ const root = createButton(document, "", "spotpatch-execution-island");
4327
+ root.hidden = true;
4328
+ root.setAttribute("aria-hidden", "true");
4329
+ const logo = createBrandMark(
4330
+ document,
4331
+ void 0,
4332
+ "spotpatch-fallback-execution-island"
4333
+ );
4334
+ logo.classList.add("spotpatch-execution-logo");
4335
+ const copy = createMarkedElement(document, "span");
4336
+ root.append(logo, copy);
4337
+ return Object.freeze({
4338
+ canExpand: () => false,
4339
+ isExpanded: () => false,
4340
+ root,
4341
+ setExpanded: () => void 0,
4342
+ render(projection) {
4343
+ const text2 = [projection.headline, projection.action, projection.meta].filter((value) => value.length > 0).join(" \xB7 ");
4344
+ copy.textContent = text2;
4345
+ root.setAttribute("aria-label", text2);
4346
+ },
4347
+ dispose() {
4348
+ }
4349
+ });
4350
+ }
3859
4351
  function createUnavailableDataFlowPanel(document, changesRoot, diagnosticsRoot) {
3860
4352
  changesRoot.append(diagnosticsRoot);
3861
4353
  return Object.freeze({
@@ -3867,12 +4359,38 @@ function createUnavailableDataFlowPanel(document, changesRoot, diagnosticsRoot)
3867
4359
  resetView: () => void 0
3868
4360
  });
3869
4361
  }
3870
- function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: false }), localePreference = "auto", dataFlowEnabled = false, externalAgentEnabled = false, framework = "vite", sessionId = "") {
4362
+ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: false }), localePreference = "auto", dataFlowEnabled = false, externalAgentEnabled = false, framework = "vite", sessionId = "", contextualAskEnabled = false) {
3871
4363
  const localizer = createUiLocalizer(document, localePreference);
4364
+ const associatedWindow = document.defaultView;
4365
+ if (associatedWindow === null) {
4366
+ throw new Error("SpotPatch requires a document with an associated window.");
4367
+ }
4368
+ const runtimeWindow = associatedWindow;
4369
+ const floatingSurface = createFloatingSurfaceController(
4370
+ runtimeWindow,
4371
+ createFloatingSurfaceSession(runtimeWindow, sessionId),
4372
+ FLOATING_SURFACE_LAYOUT
4373
+ );
3872
4374
  let messages = localizer.messages();
4375
+ let currentStatus = "idle";
4376
+ let contextualAskMode = "change";
4377
+ let contextualAskTitle = "";
4378
+ let contextualAskSubtitle = "";
4379
+ let plannerVisible = false;
4380
+ let executionSuppressed = false;
4381
+ let executionProjection;
4382
+ let pendingAgentMotion;
4383
+ let successSettleKey;
4384
+ let settledSuccessKey;
4385
+ let successSettleTimer;
4386
+ let observedExecutionStartedAt;
3873
4387
  const host = document.createElement("spotpatch-root");
3874
4388
  host.setAttribute(UI_MARKER_ATTRIBUTE, "");
3875
4389
  const shadowRoot = host.attachShadow({ mode: "open" });
4390
+ const floatingSurfaceRoot = createMarkedElement(document, "div");
4391
+ floatingSurfaceRoot.className = "spotpatch-floating-surface";
4392
+ floatingSurfaceRoot.dataset.scene = "pill";
4393
+ floatingSurfaceRoot.dataset.tone = "neutral";
3876
4394
  const triggerButton = createButton(
3877
4395
  document,
3878
4396
  messages.trigger.select,
@@ -3880,6 +4398,10 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3880
4398
  );
3881
4399
  triggerButton.title = messages.trigger.title(shortcut);
3882
4400
  triggerButton.setAttribute("aria-pressed", "false");
4401
+ const motionExtension = getFloatingSurfaceMotionExtension();
4402
+ const motionExecutionIsland = motionExtension?.createExecutionIsland(document);
4403
+ const executionIslandView = motionExecutionIsland ?? createFallbackExecutionIsland(document);
4404
+ const executionIsland = executionIslandView.root;
3883
4405
  const highlight = createMarkedElement(document, "div");
3884
4406
  highlight.className = "spotpatch-highlight";
3885
4407
  highlight.hidden = true;
@@ -3893,16 +4415,13 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3893
4415
  dialog.className = "spotpatch-dialog";
3894
4416
  dialog.hidden = true;
3895
4417
  dialog.tabIndex = -1;
3896
- dialog.dataset.placement = "viewport";
3897
4418
  dialog.setAttribute("role", "dialog");
3898
4419
  dialog.setAttribute("aria-labelledby", "spotpatch-selection-title");
3899
- const anchor = createMarkedElement(document, "span");
3900
- anchor.className = "spotpatch-anchor";
3901
- anchor.setAttribute("aria-hidden", "true");
3902
4420
  const shell = createMarkedElement(document, "div");
3903
4421
  shell.className = "spotpatch-shell";
3904
4422
  const header = createMarkedElement(document, "header");
3905
4423
  header.className = "spotpatch-header";
4424
+ header.setAttribute("data-spotpatch-drag-handle", "");
3906
4425
  const brandRow = createMarkedElement(document, "div");
3907
4426
  brandRow.className = "spotpatch-brand-row";
3908
4427
  const brand = createMarkedElement(document, "div");
@@ -3923,8 +4442,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3923
4442
  repositoryLink.target = "_blank";
3924
4443
  repositoryLink.rel = "noopener noreferrer";
3925
4444
  const localeButton = createButton(document, "", "spotpatch-locale");
4445
+ const resetPositionButton = createButton(document, "\u2316", "spotpatch-reset-position");
3926
4446
  const closeButton = createButton(document, "\xD7", "spotpatch-close");
3927
- headerControls.append(repositoryLink, localeButton, closeButton);
4447
+ headerControls.append(repositoryLink, localeButton, resetPositionButton, closeButton);
3928
4448
  brandRow.append(brand, headerControls);
3929
4449
  const title = createMarkedElement(document, "h2");
3930
4450
  title.id = "spotpatch-selection-title";
@@ -3984,13 +4504,20 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3984
4504
  diagnostics.append(diagnosticsLabel, summary);
3985
4505
  const agentPanel = createAgentPanel(document, ai, localizer);
3986
4506
  const changesPanel = createMarkedElement(document, "div");
4507
+ function requestFloatingSurfaceLayout() {
4508
+ if (plannerVisible) {
4509
+ floatingSurface.requestReconcile();
4510
+ }
4511
+ }
3987
4512
  const externalHandoffPanel = externalAgentEnabled ? getExternalHandoffExtension()?.createPanel(
3988
4513
  document,
3989
4514
  framework,
3990
4515
  localizer.locale,
3991
4516
  sessionId,
3992
4517
  localizer.subscribe,
3993
- placeDialog
4518
+ requestFloatingSurfaceLayout,
4519
+ renderExternalDispatch,
4520
+ renderExternalControl
3994
4521
  ) : void 0;
3995
4522
  changesPanel.append(
3996
4523
  targetsPanel,
@@ -4003,7 +4530,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4003
4530
  localizer.locale,
4004
4531
  changesPanel,
4005
4532
  diagnostics,
4006
- placeDialog
4533
+ requestFloatingSurfaceLayout
4007
4534
  ) ?? createUnavailableDataFlowPanel(document, changesPanel, diagnostics);
4008
4535
  selectionPanel.append(dataFlowPanel.root);
4009
4536
  const previewPanel = createMarkedElement(document, "div");
@@ -4058,15 +4585,40 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4058
4585
  backButton
4059
4586
  );
4060
4587
  actions.append(editorFeedback, secondaryActions, primaryActions);
4588
+ const contextualAskPanel = contextualAskEnabled ? getContextualAskExtension()?.createPanel({
4589
+ document,
4590
+ locale: localizer.locale,
4591
+ subscribeLocale: localizer.subscribe,
4592
+ changeRoot: dataFlowPanel.root,
4593
+ changeActions: actions,
4594
+ announce,
4595
+ onModeChange(mode, askTitle, askSubtitle) {
4596
+ contextualAskMode = mode;
4597
+ contextualAskTitle = askTitle;
4598
+ contextualAskSubtitle = askSubtitle;
4599
+ if (host.isConnected) renderPanelStatus(currentStatus);
4600
+ },
4601
+ onExecutionChange(projection) {
4602
+ executionProjection = projection;
4603
+ executionSuppressed = false;
4604
+ renderFloatingSurfaceMotion();
4605
+ },
4606
+ onViewChange: requestFloatingSurfaceLayout
4607
+ }) : void 0;
4608
+ if (contextualAskPanel !== void 0) {
4609
+ selectionPanel.prepend(contextualAskPanel.root);
4610
+ }
4061
4611
  shell.append(header, body, actions);
4062
- dialog.append(anchor, shell);
4612
+ dialog.append(shell);
4063
4613
  const liveRegion = createMarkedElement(document, "div");
4064
4614
  liveRegion.className = "spotpatch-live";
4065
4615
  liveRegion.setAttribute("aria-live", "polite");
4066
4616
  liveRegion.setAttribute("aria-atomic", "true");
4067
4617
  const styles2 = [
4068
4618
  createStyles(document),
4619
+ ...motionExtension === void 0 ? [] : [motionExtension.createStyles(document)],
4069
4620
  dataFlowPanel.styles,
4621
+ ...contextualAskPanel === void 0 ? [] : [contextualAskPanel.styles],
4070
4622
  ...externalHandoffPanel === void 0 ? [] : [externalHandoffPanel.styles]
4071
4623
  ];
4072
4624
  const styleNonce = resolveStyleNonce(document);
@@ -4079,14 +4631,38 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4079
4631
  ...styles2,
4080
4632
  selectionHighlights,
4081
4633
  highlight,
4082
- dialog,
4083
- triggerButton,
4634
+ floatingSurfaceRoot,
4084
4635
  liveRegion
4085
4636
  );
4637
+ floatingSurfaceRoot.append(triggerButton, dialog, executionIsland);
4086
4638
  document.documentElement.append(host);
4087
- let currentRect;
4088
- let activeSelectionRect;
4089
- let currentStatus = "idle";
4639
+ floatingSurface.registerSurface(floatingSurfaceRoot);
4640
+ const motionController = motionExtension === void 0 || motionExecutionIsland === void 0 ? void 0 : motionExtension.createController(
4641
+ document,
4642
+ Object.freeze({
4643
+ execution: motionExecutionIsland.elements,
4644
+ pill: triggerButton,
4645
+ planner: dialog,
4646
+ surface: floatingSurfaceRoot
4647
+ }),
4648
+ floatingSurface.reconcile
4649
+ );
4650
+ const cancelMotionOnDrag = () => {
4651
+ motionController?.cancel();
4652
+ };
4653
+ floatingSurface.attachDraggable(triggerButton, floatingSurfaceRoot, {
4654
+ onDragStart: cancelMotionOnDrag,
4655
+ suppressClickOnDrag: true
4656
+ });
4657
+ floatingSurface.attachDraggable(header, floatingSurfaceRoot, {
4658
+ canStartDrag: (event) => !floatingSurface.isCompact() && !startsOnInteractiveControl(event),
4659
+ onDragStart: cancelMotionOnDrag
4660
+ });
4661
+ floatingSurface.attachDraggable(executionIsland, floatingSurfaceRoot, {
4662
+ onDragStart: cancelMotionOnDrag,
4663
+ suppressClickOnDrag: true
4664
+ });
4665
+ floatingSurface.reconcile();
4090
4666
  let currentCanOpenEditor = false;
4091
4667
  let currentCanPreview = false;
4092
4668
  let currentSummaryText = "";
@@ -4101,12 +4677,274 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4101
4677
  page: Object.freeze({ status: dataFlowEnabled ? "idle" : "disabled" }),
4102
4678
  observationCount: 0
4103
4679
  });
4680
+ function currentTargetSummary() {
4681
+ const target2 = currentTargets.find((candidate) => candidate.active);
4682
+ return target2 === void 0 ? Object.freeze({
4683
+ label: messages.context.selectedElement,
4684
+ source: ""
4685
+ }) : Object.freeze({ label: target2.label, source: target2.source });
4686
+ }
4687
+ function currentTargetContext() {
4688
+ const target2 = currentTargetSummary();
4689
+ return target2.source.length === 0 ? target2.label : `${target2.label} \xB7 ${target2.source}`;
4690
+ }
4691
+ function beginObservedExecution() {
4692
+ observedExecutionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
4693
+ return observedExecutionStartedAt;
4694
+ }
4695
+ function observedExecutionStart() {
4696
+ return observedExecutionStartedAt ?? beginObservedExecution();
4697
+ }
4698
+ function clearSuccessSettle(resetKeys) {
4699
+ if (successSettleTimer !== void 0) {
4700
+ runtimeWindow.clearTimeout(successSettleTimer);
4701
+ successSettleTimer = void 0;
4702
+ }
4703
+ if (resetKeys) {
4704
+ successSettleKey = void 0;
4705
+ settledSuccessKey = void 0;
4706
+ }
4707
+ }
4708
+ function showSuccessUntilSettled(key) {
4709
+ if (settledSuccessKey === key) return false;
4710
+ if (successSettleKey === key) return true;
4711
+ clearSuccessSettle(false);
4712
+ successSettleKey = key;
4713
+ successSettleTimer = runtimeWindow.setTimeout(() => {
4714
+ successSettleTimer = void 0;
4715
+ if (!host.isConnected) return;
4716
+ settledSuccessKey = key;
4717
+ executionProjection = void 0;
4718
+ observedExecutionStartedAt = void 0;
4719
+ executionSuppressed = false;
4720
+ plannerVisible = false;
4721
+ renderFloatingSurfaceMotion();
4722
+ }, SUCCESS_SETTLE_MS);
4723
+ return true;
4724
+ }
4725
+ function externalAgentIdentity(kind) {
4726
+ return kind.startsWith("claude") ? messages.execution.claude : kind.startsWith("codex") ? messages.execution.codex : kind;
4727
+ }
4728
+ function projectAgentActivity(item) {
4729
+ return Object.freeze({
4730
+ ...item.detail === void 0 ? {} : { detail: item.detail },
4731
+ key: item.key,
4732
+ kind: item.kind,
4733
+ label: messages.execution.activityLane(item.kind, item.detail),
4734
+ state: item.state
4735
+ });
4736
+ }
4737
+ function motionActivity(key, kind, detail, state = "active") {
4738
+ return Object.freeze({
4739
+ ...detail === void 0 ? {} : { detail },
4740
+ key,
4741
+ kind,
4742
+ label: messages.execution.activityLane(kind, detail),
4743
+ state
4744
+ });
4745
+ }
4746
+ function renderFloatingSurfaceMotion() {
4747
+ const capturing = currentStatus === "inspecting";
4748
+ const pillActive = !plannerVisible || capturing;
4749
+ const projection = pillActive ? {
4750
+ scene: capturing ? "capturing" : "pill",
4751
+ tone: capturing ? "capturing" : "neutral",
4752
+ headline: triggerButton.textContent,
4753
+ action: "",
4754
+ meta: "",
4755
+ recentActivities: Object.freeze([])
4756
+ } : !executionSuppressed && executionProjection !== void 0 ? executionProjection : {
4757
+ scene: "planner",
4758
+ tone: "ready",
4759
+ headline: messages.dialog.editTitle,
4760
+ action: messages.dialog.editSubtitle,
4761
+ meta: messages.context.ready,
4762
+ recentActivities: Object.freeze([])
4763
+ };
4764
+ if (motionController !== void 0) {
4765
+ motionController.render(projection, () => {
4766
+ executionIslandView.render(projection);
4767
+ });
4768
+ return;
4769
+ }
4770
+ executionIslandView.render(projection);
4771
+ floatingSurfaceRoot.dataset.scene = projection.scene;
4772
+ floatingSurfaceRoot.dataset.tone = projection.tone;
4773
+ const plannerActive = projection.scene === "planner";
4774
+ triggerButton.hidden = !pillActive;
4775
+ triggerButton.inert = !pillActive;
4776
+ dialog.hidden = !plannerActive;
4777
+ dialog.inert = !plannerActive;
4778
+ dialog.setAttribute("aria-hidden", String(!plannerActive));
4779
+ executionIsland.hidden = pillActive || plannerActive;
4780
+ executionIsland.inert = pillActive || plannerActive;
4781
+ executionIsland.setAttribute("aria-hidden", String(pillActive || plannerActive));
4782
+ floatingSurface.reconcile();
4783
+ }
4784
+ function renderExternalDispatch(dispatch) {
4785
+ if (dispatch === null) {
4786
+ pendingAgentMotion = void 0;
4787
+ observedExecutionStartedAt = void 0;
4788
+ } else if (pendingAgentMotion !== void 0 && dispatch.phase !== "failed" && dispatch.phase !== "delivery-unknown") {
4789
+ startStagedAgentRequest();
4790
+ } else if (dispatch.phase === "failed" || dispatch.phase === "delivery-unknown") {
4791
+ pendingAgentMotion = void 0;
4792
+ }
4793
+ if (dispatch !== null && !executionSuppressed) {
4794
+ const failed = dispatch.phase === "failed" || dispatch.phase === "delivery-unknown";
4795
+ const completed = dispatch.phase === "completed";
4796
+ const startedAt = failed || completed ? void 0 : observedExecutionStart();
4797
+ if (failed || completed) observedExecutionStartedAt = void 0;
4798
+ const successKey = `dispatch:${String(dispatch.revision)}`;
4799
+ if (completed && !showSuccessUntilSettled(successKey)) {
4800
+ executionProjection = void 0;
4801
+ renderFloatingSurfaceMotion();
4802
+ return;
4803
+ }
4804
+ const identity = externalAgentIdentity(dispatch.adapterKind);
4805
+ const target2 = currentTargetSummary();
4806
+ const activityKind = dispatch.phase === "queued" || dispatch.phase === "dispatching" || dispatch.phase === "dispatched" ? "dispatch" : completed ? "sync" : "unknown";
4807
+ const activity = motionActivity(
4808
+ `dispatch:${String(dispatch.revision)}:${dispatch.phase}`,
4809
+ activityKind,
4810
+ dispatch.phase,
4811
+ failed ? "failure" : completed ? "success" : "active"
4812
+ );
4813
+ const scene = failed ? "failed" : completed ? "success" : dispatch.phase === "working" ? "running" : "handoff";
4814
+ executionProjection = {
4815
+ scene,
4816
+ tone: failed ? "danger" : completed ? "success" : "running",
4817
+ headline: completed ? messages.execution.completedTitle : failed ? messages.execution.failedTitle : scene === "running" ? messages.execution.runningTitle(target2.label) : messages.execution.dispatchingTitle(identity),
4818
+ action: completed ? messages.execution.resultReturned : target2.source,
4819
+ expandedHeadline: completed ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "running" ? messages.execution.expandedRunningTitle(identity) : messages.execution.dispatchingTitle(identity),
4820
+ expandedAction: completed ? messages.execution.resultReturned : currentTargetContext(),
4821
+ meta: failed ? messages.execution.failedStatus : completed ? messages.execution.completedStatus : scene === "running" ? messages.execution.runningStatus : messages.execution.dispatchingStatus,
4822
+ activity,
4823
+ recentActivities: Object.freeze([activity]),
4824
+ ...startedAt === void 0 ? {} : { startedAt }
4825
+ };
4826
+ } else if (dispatch === null) {
4827
+ executionProjection = void 0;
4828
+ }
4829
+ renderFloatingSurfaceMotion();
4830
+ }
4831
+ function renderExternalControl(status) {
4832
+ const task = status?.task;
4833
+ if (status === void 0 || task === void 0 || executionSuppressed) return;
4834
+ const phase = task.managedPhase;
4835
+ const failed = phase === "failed" || phase === "cleanup-warning";
4836
+ const completed = phase === "completed" || phase === "review-required";
4837
+ const cancelled = phase === "cancelled";
4838
+ if (failed || cancelled) {
4839
+ pendingAgentMotion = void 0;
4840
+ } else if (pendingAgentMotion !== void 0) {
4841
+ startStagedAgentRequest();
4842
+ }
4843
+ if (cancelled) {
4844
+ executionProjection = void 0;
4845
+ observedExecutionStartedAt = void 0;
4846
+ renderFloatingSurfaceMotion();
4847
+ return;
4848
+ }
4849
+ const startedAt = failed || completed ? void 0 : observedExecutionStart();
4850
+ if (failed || completed) observedExecutionStartedAt = void 0;
4851
+ if (phase === "completed" && !showSuccessUntilSettled(`managed:${String(task.revision)}`)) {
4852
+ executionProjection = void 0;
4853
+ renderFloatingSurfaceMotion();
4854
+ return;
4855
+ }
4856
+ const scene = failed ? "failed" : completed ? "success" : phase === "preparing" ? "handoff" : "running";
4857
+ const tone = failed ? "danger" : completed ? "success" : "running";
4858
+ const identity = messages.execution.codex;
4859
+ const target2 = currentTargetSummary();
4860
+ const activityKind = phase === "preparing" ? "prepare" : phase === "auditing" ? "audit" : phase === "validating" ? "check" : phase === "applying" ? "apply" : completed ? "sync" : "unknown";
4861
+ const latestCheck = task.checks.at(-1);
4862
+ const latestFile = task.files.at(-1);
4863
+ const activityDetail = phase === "validating" ? latestCheck?.id : phase === "auditing" || phase === "applying" ? latestFile?.path : void 0;
4864
+ const activity = motionActivity(
4865
+ `managed:${String(task.revision)}:${phase}:${activityDetail ?? ""}`,
4866
+ activityKind,
4867
+ activityDetail,
4868
+ failed ? "failure" : completed ? "success" : "active"
4869
+ );
4870
+ const recentActivities = Object.freeze([
4871
+ ...task.files.slice(-2).map(
4872
+ (file) => motionActivity(
4873
+ `managed-file:${String(task.revision)}:${file.path}`,
4874
+ "patch",
4875
+ file.path,
4876
+ "success"
4877
+ )
4878
+ ),
4879
+ ...task.checks.slice(-2).map(
4880
+ (check) => motionActivity(
4881
+ `managed-check:${String(task.revision)}:${check.id}`,
4882
+ "check",
4883
+ check.id,
4884
+ check.outcome === "passed" ? "success" : "failure"
4885
+ )
4886
+ ),
4887
+ activity
4888
+ ]);
4889
+ executionProjection = {
4890
+ scene,
4891
+ tone,
4892
+ headline: completed ? messages.execution.completedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : phase === "validating" ? messages.execution.activityAction("check", activityDetail) : messages.execution.runningTitle(target2.label),
4893
+ action: completed ? task.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(task.files.length) : target2.source,
4894
+ expandedHeadline: completed ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : messages.execution.expandedRunningTitle(identity),
4895
+ expandedAction: completed ? messages.execution.resultReturned : currentTargetContext(),
4896
+ meta: failed ? messages.execution.failedStatus : completed ? messages.execution.completedStatus : phase === "validating" ? messages.execution.checkingStatus : scene === "handoff" ? messages.execution.dispatchingStatus : messages.execution.runningStatus,
4897
+ activity,
4898
+ recentActivities,
4899
+ ...startedAt === void 0 ? {} : { startedAt }
4900
+ };
4901
+ renderFloatingSurfaceMotion();
4902
+ }
4903
+ function beginAgentRequest(target2, agentCard) {
4904
+ if (!plannerVisible) {
4905
+ return;
4906
+ }
4907
+ clearSuccessSettle(true);
4908
+ const startedAt = beginObservedExecution();
4909
+ executionSuppressed = false;
4910
+ const activity = motionActivity(
4911
+ `dispatch-start:${currentTargetContext()}`,
4912
+ "dispatch",
4913
+ currentTargetContext()
4914
+ );
4915
+ executionProjection = {
4916
+ scene: "agent-charging",
4917
+ tone: "running",
4918
+ headline: messages.execution.receivingTitle,
4919
+ action: currentTargetContext(),
4920
+ expandedHeadline: messages.execution.receivingTitle,
4921
+ expandedAction: currentTargetContext(),
4922
+ meta: messages.execution.receivingStatus,
4923
+ activity,
4924
+ recentActivities: Object.freeze([activity]),
4925
+ startedAt
4926
+ };
4927
+ renderFloatingSurfaceMotion();
4928
+ motionController?.dispatch(target2, agentCard);
4929
+ }
4930
+ function stageAgentRequest(source2, target2) {
4931
+ if (!plannerVisible || source2.disabled) return;
4932
+ pendingAgentMotion = Object.freeze({ source: source2, target: target2 });
4933
+ }
4934
+ function startStagedAgentRequest() {
4935
+ const pending = pendingAgentMotion;
4936
+ pendingAgentMotion = void 0;
4937
+ if (pending === void 0 || !pending.source.isConnected || !pending.target.isConnected) {
4938
+ return;
4939
+ }
4940
+ beginAgentRequest(pending.source, pending.target);
4941
+ }
4104
4942
  function renderEditorStatus(state) {
4105
4943
  currentEditorFeedbackState = state;
4106
4944
  editorFeedback.dataset.state = state;
4107
4945
  editorFeedback.hidden = state === "idle";
4108
4946
  editorFeedback.textContent = state === "opening" ? messages.announcements.editorOpening : state === "success" ? messages.announcements.editorOpened : state === "error" ? messages.announcements.editorFailed : "";
4109
- placeDialog();
4947
+ requestFloatingSurfaceLayout();
4110
4948
  }
4111
4949
  function statusText(target2) {
4112
4950
  if (target2.status === "ready") {
@@ -4241,6 +5079,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4241
5079
  replacement?.setSelectionRange(selectionStart, selectionEnd);
4242
5080
  }
4243
5081
  }
5082
+ requestFloatingSurfaceLayout();
4244
5083
  }
4245
5084
  function updateTargetInstruction(targetId, instruction) {
4246
5085
  currentTargets = currentTargets.map(
@@ -4289,8 +5128,6 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4289
5128
  }
4290
5129
  function showSelectionHighlights(targets) {
4291
5130
  selectionHighlights.replaceChildren();
4292
- activeSelectionRect = targets.find((target2) => target2.active)?.rect;
4293
- currentRect = activeSelectionRect;
4294
5131
  for (const [index, target2] of targets.entries()) {
4295
5132
  const box = createMarkedElement(document, "div");
4296
5133
  box.className = "spotpatch-selection-highlight";
@@ -4304,31 +5141,6 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4304
5141
  box.append(label);
4305
5142
  selectionHighlights.append(box);
4306
5143
  }
4307
- placeDialog();
4308
- }
4309
- function placeDialog() {
4310
- if (dialog.hidden) {
4311
- return;
4312
- }
4313
- const view = document.defaultView;
4314
- const viewportWidth = view?.innerWidth ?? document.documentElement.clientWidth;
4315
- const viewportHeight = view?.innerHeight ?? document.documentElement.clientHeight;
4316
- const measured = dialog.getBoundingClientRect();
4317
- const status = currentStatus === "previewing" ? "previewing" : "selected";
4318
- const dialogWidth = measured.width > 0 ? measured.width : Math.min(DIALOG_MAX_WIDTH, viewportWidth - 32);
4319
- const dialogHeight = measured.height > 0 ? measured.height : Math.min(DIALOG_FALLBACK_HEIGHT[status], viewportHeight - 32);
4320
- const placement = calculateDialogPlacement({
4321
- dialogWidth,
4322
- dialogHeight,
4323
- viewportWidth,
4324
- viewportHeight,
4325
- ...currentRect === void 0 ? {} : { target: currentRect }
4326
- });
4327
- dialog.style.left = `${String(placement.left)}px`;
4328
- dialog.style.top = `${String(placement.top)}px`;
4329
- dialog.style.setProperty("--spotpatch-anchor-x", `${String(placement.anchorX)}px`);
4330
- dialog.style.setProperty("--spotpatch-anchor-y", `${String(placement.anchorY)}px`);
4331
- dialog.dataset.placement = placement.mode;
4332
5144
  }
4333
5145
  function updateContextOverview(summaryText) {
4334
5146
  const summaryMessages = messages.summary;
@@ -4359,14 +5171,18 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4359
5171
  previewButton.disabled = !canPreview;
4360
5172
  agentPanel.setContextReady(canPreview);
4361
5173
  externalHandoffPanel?.setContextReady(canPreview);
5174
+ contextualAskPanel?.setSelectionPreview({
5175
+ contextReady: currentTargets.every((target2) => target2.status !== "loading"),
5176
+ targetCount: currentTargets.length,
5177
+ sourceCount: currentTargets.filter((target2) => target2.canOpenEditor).length
5178
+ });
4362
5179
  updateContextOverview(summaryText);
4363
- placeDialog();
5180
+ requestFloatingSurfaceLayout();
4364
5181
  }
4365
5182
  function renderPanelStatus(status) {
4366
5183
  currentStatus = status;
4367
5184
  const selected = status === "selected";
4368
5185
  const previewing = status === "previewing";
4369
- triggerButton.hidden = selected || previewing;
4370
5186
  selectionPanel.hidden = !selected;
4371
5187
  previewPanel.hidden = !previewing;
4372
5188
  reselectButton.hidden = !selected;
@@ -4376,11 +5192,12 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4376
5192
  secondaryActions.hidden = !selected;
4377
5193
  agentPanel.setSelectionVisible(selected);
4378
5194
  externalHandoffPanel?.setSelectionVisible(selected);
5195
+ contextualAskPanel?.setSelectionVisible(selected);
4379
5196
  copyButton.hidden = !previewing;
4380
5197
  backButton.hidden = !previewing;
4381
- title.textContent = previewing ? messages.dialog.previewTitle : messages.dialog.editTitle;
4382
- subtitle.textContent = previewing ? messages.dialog.previewSubtitle : messages.dialog.editSubtitle;
4383
- placeDialog();
5198
+ title.textContent = previewing ? messages.dialog.previewTitle : contextualAskMode === "ask" && contextualAskTitle.length > 0 ? contextualAskTitle : messages.dialog.editTitle;
5199
+ subtitle.textContent = previewing ? messages.dialog.previewSubtitle : contextualAskMode === "ask" && contextualAskSubtitle.length > 0 ? contextualAskSubtitle : messages.dialog.editSubtitle;
5200
+ renderFloatingSurfaceMotion();
4384
5201
  }
4385
5202
  function applyMessages() {
4386
5203
  messages = localizer.messages();
@@ -4392,8 +5209,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4392
5209
  localeButton.textContent = messages.alternateLocaleName;
4393
5210
  localeButton.title = messages.switchLocale;
4394
5211
  localeButton.setAttribute("aria-label", messages.switchLocale);
5212
+ resetPositionButton.title = messages.floatingSurface.resetPosition;
5213
+ resetPositionButton.setAttribute(
5214
+ "aria-label",
5215
+ messages.floatingSurface.resetPosition
5216
+ );
4395
5217
  closeButton.setAttribute("aria-label", messages.dialog.close);
4396
5218
  closeButton.title = messages.dialog.close;
5219
+ header.title = messages.floatingSurface.dragHandle;
4397
5220
  targetsPanel.setAttribute("aria-label", messages.targets.ariaLabel);
4398
5221
  targetsTitle.textContent = messages.targets.title;
4399
5222
  diagnosticsTitle.textContent = messages.diagnostics.title;
@@ -4429,8 +5252,58 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4429
5252
  contextState.textContent = messages.context.collecting;
4430
5253
  }
4431
5254
  }
4432
- diagnostics.addEventListener("toggle", placeDialog);
5255
+ function announce(message) {
5256
+ liveRegion.textContent = "";
5257
+ liveRegion.textContent = message;
5258
+ }
5259
+ function resetFloatingSurfacePosition() {
5260
+ floatingSurface.reset();
5261
+ announce(messages.floatingSurface.positionReset);
5262
+ }
5263
+ diagnostics.addEventListener("toggle", requestFloatingSurfaceLayout);
4433
5264
  localeButton.addEventListener("click", localizer.toggle);
5265
+ resetPositionButton.addEventListener("click", resetFloatingSurfacePosition);
5266
+ const onAgentRun = () => {
5267
+ stageAgentRequest(agentPanel.runButton, agentPanel.root);
5268
+ };
5269
+ const onExternalSend = () => {
5270
+ if (externalHandoffPanel !== void 0) {
5271
+ stageAgentRequest(externalHandoffPanel.sendButton, externalHandoffPanel.root);
5272
+ }
5273
+ };
5274
+ const onExecutionOpen = () => {
5275
+ if (executionIslandView.canExpand() && !executionIslandView.isExpanded()) {
5276
+ if (motionController === void 0) {
5277
+ executionIslandView.setExpanded(true);
5278
+ floatingSurface.reconcile();
5279
+ } else {
5280
+ motionController.updateLayout(() => {
5281
+ executionIslandView.setExpanded(true);
5282
+ });
5283
+ }
5284
+ return;
5285
+ }
5286
+ clearSuccessSettle(false);
5287
+ executionSuppressed = true;
5288
+ renderFloatingSurfaceMotion();
5289
+ };
5290
+ const onExecutionKeyDown = (event) => {
5291
+ if (event.key !== "Escape" || !executionIslandView.isExpanded()) return;
5292
+ event.preventDefault();
5293
+ event.stopPropagation();
5294
+ if (motionController === void 0) {
5295
+ executionIslandView.setExpanded(false);
5296
+ floatingSurface.reconcile();
5297
+ } else {
5298
+ motionController.updateLayout(() => {
5299
+ executionIslandView.setExpanded(false);
5300
+ });
5301
+ }
5302
+ };
5303
+ agentPanel.runButton.addEventListener("click", onAgentRun);
5304
+ externalHandoffPanel?.sendButton.addEventListener("click", onExternalSend);
5305
+ executionIsland.addEventListener("click", onExecutionOpen);
5306
+ executionIsland.addEventListener("keydown", onExecutionKeyDown);
4434
5307
  const unsubscribeLocale = localizer.subscribe(applyMessages);
4435
5308
  applyMessages();
4436
5309
  return Object.freeze({
@@ -4458,8 +5331,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4458
5331
  agentRevertButton: agentPanel.revertButton,
4459
5332
  agentResetButton: agentPanel.resetButton,
4460
5333
  ...externalHandoffPanel === void 0 ? {} : { externalHandoffPanel },
5334
+ ...contextualAskPanel === void 0 ? {} : { contextualAskPanel },
4461
5335
  renderStatus(status) {
4462
5336
  const inspecting = status === "inspecting";
5337
+ if (status === "idle" || inspecting) {
5338
+ plannerVisible = false;
5339
+ } else {
5340
+ plannerVisible = true;
5341
+ }
4463
5342
  triggerButton.setAttribute("aria-pressed", String(inspecting));
4464
5343
  triggerButton.textContent = inspecting ? messages.trigger.stop : messages.trigger.select;
4465
5344
  renderPanelStatus(status);
@@ -4468,36 +5347,28 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4468
5347
  renderDataFlow(state) {
4469
5348
  currentDataFlowState = state;
4470
5349
  dataFlowPanel.render(state);
4471
- placeDialog();
5350
+ requestFloatingSurfaceLayout();
4472
5351
  },
4473
5352
  showHighlight(rect, label) {
4474
- currentRect = rect;
4475
5353
  highlight.hidden = false;
4476
5354
  highlight.style.transform = `translate(${String(rect.x)}px, ${String(rect.y)}px)`;
4477
5355
  highlight.style.width = `${String(rect.width)}px`;
4478
5356
  highlight.style.height = `${String(rect.height)}px`;
4479
5357
  highlightLabel.textContent = label;
4480
5358
  targetLabel.textContent = label;
4481
- placeDialog();
4482
5359
  },
4483
5360
  hideHighlight() {
4484
- currentRect = activeSelectionRect;
4485
5361
  highlight.hidden = true;
4486
5362
  highlightLabel.textContent = "";
4487
- placeDialog();
4488
5363
  },
4489
5364
  showSelectionHighlights,
4490
5365
  hideSelectionHighlights() {
4491
5366
  selectionHighlights.replaceChildren();
4492
- activeSelectionRect = void 0;
4493
- currentRect = void 0;
4494
- placeDialog();
4495
5367
  },
4496
5368
  showSelection(summaryText, canOpenEditor, canPreview) {
4497
5369
  updateSelection(summaryText, canOpenEditor, canPreview);
4498
- triggerButton.hidden = true;
4499
- dialog.hidden = false;
4500
- placeDialog();
5370
+ plannerVisible = true;
5371
+ renderPanelStatus(currentStatus === "previewing" ? "previewing" : "selected");
4501
5372
  },
4502
5373
  updateSelection,
4503
5374
  renderTargets,
@@ -4509,8 +5380,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4509
5380
  externalHandoffPanel?.setContextReady(enabled);
4510
5381
  },
4511
5382
  hideSelection() {
4512
- dialog.hidden = true;
4513
- triggerButton.hidden = false;
5383
+ plannerVisible = false;
5384
+ executionSuppressed = false;
5385
+ executionProjection = void 0;
4514
5386
  targetList.replaceChildren();
4515
5387
  currentTargets = [];
4516
5388
  currentMaximum = 0;
@@ -4533,17 +5405,21 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4533
5405
  agentPanel.setSelectionVisible(false);
4534
5406
  externalHandoffPanel?.setContextReady(false);
4535
5407
  externalHandoffPanel?.setSelectionVisible(false);
5408
+ contextualAskPanel?.setSelectionVisible(false);
4536
5409
  agentPanel.setEditingEnabled(true);
4537
5410
  agentPanel.resetJob();
4538
5411
  dataFlowPanel.resetView();
5412
+ renderFloatingSurfaceMotion();
4539
5413
  },
4540
5414
  hideSelectionTemporarily() {
4541
- dialog.hidden = true;
4542
- triggerButton.hidden = false;
5415
+ plannerVisible = false;
5416
+ executionSuppressed = false;
5417
+ executionProjection = void 0;
5418
+ renderFloatingSurfaceMotion();
4543
5419
  },
4544
5420
  showPreview(prompt) {
4545
5421
  promptOutput.textContent = prompt;
4546
- placeDialog();
5422
+ requestFloatingSurfaceLayout();
4547
5423
  },
4548
5424
  readAgentSelection() {
4549
5425
  return agentPanel.readSelection();
@@ -4567,25 +5443,86 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4567
5443
  previewButton.disabled = !enabled || !currentCanPreview;
4568
5444
  externalHandoffPanel?.setContextReady(enabled && currentCanPreview);
4569
5445
  agentPanel.setEditingEnabled(enabled);
4570
- placeDialog();
5446
+ requestFloatingSurfaceLayout();
4571
5447
  },
4572
5448
  renderAgentCapability(state, message, capabilitySnapshot, errorCode) {
4573
5449
  agentPanel.renderCapability(state, message, capabilitySnapshot, errorCode);
5450
+ if (state === "error") {
5451
+ pendingAgentMotion = void 0;
5452
+ renderFloatingSurfaceMotion();
5453
+ }
4574
5454
  const agentReady = state === "ready" && capabilitySnapshot?.state === "agent-ready";
4575
5455
  previewButton.classList.toggle("spotpatch-primary", !agentReady);
4576
- placeDialog();
5456
+ requestFloatingSurfaceLayout();
4577
5457
  },
4578
5458
  renderAgentWorkspaceHealth(state, snapshot, errorCode) {
4579
5459
  agentPanel.renderWorkspaceHealth(state, snapshot, errorCode);
4580
- placeDialog();
5460
+ if (state === "blocked") {
5461
+ pendingAgentMotion = void 0;
5462
+ renderFloatingSurfaceMotion();
5463
+ }
5464
+ requestFloatingSurfaceLayout();
4581
5465
  },
4582
5466
  renderAgentJob(snapshot, result, activities, errorCode) {
4583
5467
  agentPanel.renderJob(snapshot, result, activities, errorCode);
4584
- placeDialog();
5468
+ if (pendingAgentMotion !== void 0 && snapshot.status !== "failed" && snapshot.status !== "cancelled") {
5469
+ startStagedAgentRequest();
5470
+ }
5471
+ if (snapshot.status === "failed" || snapshot.status === "cancelled") {
5472
+ pendingAgentMotion = void 0;
5473
+ }
5474
+ const successful = snapshot.status === "awaiting-review" || snapshot.status === "applied" || snapshot.status === "completed" || snapshot.status === "reverted";
5475
+ const failed = snapshot.status === "failed";
5476
+ const cancelled = snapshot.status === "cancelled";
5477
+ const terminalSuccess = snapshot.status === "applied" || snapshot.status === "completed" || snapshot.status === "reverted";
5478
+ const scene = successful ? "success" : failed ? "failed" : snapshot.status === "queued" || snapshot.status === "preparing" ? "handoff" : "running";
5479
+ if (!executionSuppressed) {
5480
+ if (terminalSuccess && !showSuccessUntilSettled(`job:${snapshot.jobId}:${snapshot.status}`)) {
5481
+ executionProjection = void 0;
5482
+ renderFloatingSurfaceMotion();
5483
+ requestFloatingSurfaceLayout();
5484
+ return;
5485
+ }
5486
+ const identity = snapshot.providerLabel;
5487
+ const target2 = currentTargetSummary();
5488
+ const projectedActivities = Object.freeze(activities.map(projectAgentActivity));
5489
+ const latestActivity = activities.at(-1);
5490
+ const phaseKind = snapshot.status === "queued" || snapshot.status === "preparing" ? "prepare" : snapshot.status === "validating" ? "check" : snapshot.status === "applying" || snapshot.status === "reverting" ? "apply" : snapshot.status === "awaiting-review" || successful ? "sync" : "unknown";
5491
+ const phaseActivity = motionActivity(
5492
+ `job-phase:${snapshot.jobId}:${snapshot.status}:${snapshot.updatedAt}`,
5493
+ phaseKind,
5494
+ void 0,
5495
+ successful ? "success" : failed ? "failure" : "active"
5496
+ );
5497
+ const activity = snapshot.status === "running" && latestActivity !== void 0 ? projectAgentActivity(latestActivity) : phaseActivity;
5498
+ executionProjection = cancelled ? void 0 : {
5499
+ scene,
5500
+ tone: successful ? "success" : failed ? "danger" : "running",
5501
+ headline: successful ? messages.execution.completedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : snapshot.status === "validating" ? messages.execution.activityAction(
5502
+ "check",
5503
+ latestActivity?.detail
5504
+ ) : snapshot.status === "applying" || snapshot.status === "reverting" ? messages.execution.activityAction(phaseKind) : messages.execution.runningTitle(target2.label),
5505
+ action: successful ? result === void 0 || result.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(result.files.length) : latestActivity?.detail ?? target2.source,
5506
+ expandedHeadline: successful ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : messages.execution.expandedRunningTitle(identity),
5507
+ expandedAction: successful ? result === void 0 || result.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(result.files.length) : currentTargetContext(),
5508
+ meta: successful ? messages.execution.completedStatus : failed ? messages.execution.failedStatus : snapshot.status === "validating" ? messages.execution.checkingStatus : scene === "handoff" ? messages.execution.dispatchingStatus : messages.execution.runningStatus,
5509
+ activity,
5510
+ recentActivities: projectedActivities.length === 0 ? Object.freeze([phaseActivity]) : projectedActivities,
5511
+ startedAt: snapshot.createdAt
5512
+ };
5513
+ }
5514
+ renderFloatingSurfaceMotion();
5515
+ requestFloatingSurfaceLayout();
4585
5516
  },
4586
5517
  resetAgentJob() {
4587
5518
  agentPanel.resetJob();
4588
- placeDialog();
5519
+ observedExecutionStartedAt = void 0;
5520
+ if (executionProjection !== void 0) {
5521
+ executionProjection = void 0;
5522
+ executionSuppressed = false;
5523
+ renderFloatingSurfaceMotion();
5524
+ }
5525
+ requestFloatingSurfaceLayout();
4589
5526
  },
4590
5527
  focusTargetInstruction(targetId) {
4591
5528
  instructionInput(targetId)?.focus({ preventScroll: true });
@@ -4593,21 +5530,28 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4593
5530
  focusPrompt() {
4594
5531
  promptOutput.focus({ preventScroll: true });
4595
5532
  },
4596
- announce(message) {
4597
- liveRegion.textContent = "";
4598
- liveRegion.textContent = message;
4599
- },
5533
+ announce,
4600
5534
  locale: localizer.locale,
4601
5535
  messages: localizer.messages,
4602
5536
  agentWorkspaceConsentGranted: agentPanel.workspaceConsentGranted,
4603
5537
  subscribeLocale: localizer.subscribe,
4604
5538
  dispose() {
4605
- diagnostics.removeEventListener("toggle", placeDialog);
5539
+ diagnostics.removeEventListener("toggle", requestFloatingSurfaceLayout);
4606
5540
  localeButton.removeEventListener("click", localizer.toggle);
5541
+ resetPositionButton.removeEventListener("click", resetFloatingSurfacePosition);
5542
+ agentPanel.runButton.removeEventListener("click", onAgentRun);
5543
+ externalHandoffPanel?.sendButton.removeEventListener("click", onExternalSend);
5544
+ executionIsland.removeEventListener("click", onExecutionOpen);
5545
+ executionIsland.removeEventListener("keydown", onExecutionKeyDown);
4607
5546
  unsubscribeLocale();
4608
5547
  dataFlowPanel.dispose();
4609
5548
  externalHandoffPanel?.dispose();
5549
+ contextualAskPanel?.dispose();
4610
5550
  agentPanel.dispose();
5551
+ motionController?.dispose();
5552
+ executionIslandView.dispose();
5553
+ clearSuccessSettle(true);
5554
+ floatingSurface.dispose();
4611
5555
  host.remove();
4612
5556
  }
4613
5557
  });
@@ -4674,17 +5618,33 @@ var import_shared8 = require("@spotpatch/shared");
4674
5618
  function capabilityKey(providerProfileId, modelProfileId) {
4675
5619
  return `${providerProfileId}:${modelProfileId}`;
4676
5620
  }
5621
+ var TOOL_ACTIVITY_KIND = Object.freeze({
5622
+ list_files: "discover",
5623
+ search_text: "search",
5624
+ read_file: "read",
5625
+ replace_text: "patch",
5626
+ apply_patch: "patch",
5627
+ run_check: "check"
5628
+ });
5629
+ function toolActivityKind(toolName) {
5630
+ return TOOL_ACTIVITY_KIND[toolName] ?? "unknown";
5631
+ }
4677
5632
  function activityFromEvent(event) {
4678
5633
  if (event.type === "tool") {
5634
+ const detail = event.data.relativePath ?? event.data.checkLabel;
4679
5635
  return Object.freeze({
4680
5636
  key: `tool:${String(event.data.turn)}:${event.data.toolCallId}`,
4681
- label: `${event.data.toolName} \xB7 ${event.data.state}`,
5637
+ kind: toolActivityKind(event.data.toolName),
5638
+ ...detail === void 0 ? {} : { detail },
5639
+ label: `${event.data.toolName}${detail === void 0 ? "" : ` \xB7 ${detail}`} \xB7 ${event.data.state}`,
4682
5640
  state: event.data.state === "started" ? "active" : event.data.state === "succeeded" ? "success" : "failure"
4683
5641
  });
4684
5642
  }
4685
5643
  if (event.type === "check") {
4686
5644
  return Object.freeze({
4687
5645
  key: `check:${event.data.result.checkId}:${String(event.sequence)}`,
5646
+ kind: "check",
5647
+ detail: event.data.result.label,
4688
5648
  label: `${event.data.result.label} \xB7 ${event.data.result.status}`,
4689
5649
  state: event.data.result.status === "passed" ? "success" : event.data.result.status === "failed" || event.data.result.status === "timed-out" ? "failure" : "info"
4690
5650
  });
@@ -5105,7 +6065,8 @@ function createController(config, dependencies = {}) {
5105
6065
  config.dataFlow.enabled,
5106
6066
  config.externalAgent.enabled,
5107
6067
  config.framework,
5108
- config.sessionId
6068
+ config.sessionId,
6069
+ config.contextualAsk.enabled
5109
6070
  );
5110
6071
  const api = dependencies.api ?? createRuntimeApi({
5111
6072
  apiBase: config.apiBase,
@@ -5308,6 +6269,19 @@ function createController(config, dependencies = {}) {
5308
6269
  (target2) => target2.instruction.trim().length > 0 && target2.elementContext !== void 0 && target2.styles !== void 0 && target2.apiStatus !== "loading"
5309
6270
  );
5310
6271
  }
6272
+ function selectedAskSelection() {
6273
+ const snapshots = targets.flatMap((target2) => {
6274
+ const snapshot = snapshotTarget(target2);
6275
+ return snapshot === void 0 ? [] : [snapshot];
6276
+ });
6277
+ if (snapshots.length === 0 || snapshots.length !== targets.length) {
6278
+ return void 0;
6279
+ }
6280
+ return Object.freeze({
6281
+ locale: view.locale(),
6282
+ targets: snapshots
6283
+ });
6284
+ }
5311
6285
  function selectionSummary() {
5312
6286
  const messages = view.messages().summary;
5313
6287
  return targets.map((target2, index) => {
@@ -5429,6 +6403,7 @@ ${summary}`;
5429
6403
  api.cancelPending();
5430
6404
  agentWorkflow.disposeSelection();
5431
6405
  externalHandoffWorkflow?.cancelPending();
6406
+ contextualAskWorkflow?.cancelPending();
5432
6407
  clearCollectionTimers();
5433
6408
  resizeObserver?.disconnect();
5434
6409
  targets = [];
@@ -5554,6 +6529,27 @@ ${summary}`;
5554
6529
  config.sessionToken,
5555
6530
  browser.window
5556
6531
  ) : void 0;
6532
+ const contextualAskWorkflow = config.contextualAsk.enabled && view.contextualAskPanel !== void 0 ? getContextualAskExtension()?.createWorkflow({
6533
+ ...clipboard === void 0 ? {} : { clipboard },
6534
+ createId,
6535
+ fetch: browser.window.fetch.bind(browser.window),
6536
+ getSelection: selectedAskSelection,
6537
+ onBusyChange(busy) {
6538
+ view.setAgentEditingEnabled(!busy);
6539
+ },
6540
+ onConvert() {
6541
+ view.focusTargetInstruction(activeTargetId);
6542
+ },
6543
+ async onOpenSource(source2) {
6544
+ await api.openEditor({
6545
+ fileId: source2.fileId,
6546
+ line: source2.startLine,
6547
+ column: 1
6548
+ });
6549
+ },
6550
+ panel: view.contextualAskPanel,
6551
+ sessionToken: config.sessionToken
6552
+ }) : void 0;
5557
6553
  async function loadSourceContext(target2, revision) {
5558
6554
  const marker = target2.marker;
5559
6555
  if (marker === void 0) {
@@ -5667,6 +6663,7 @@ ${summary}`;
5667
6663
  previewPrompt = "";
5668
6664
  agentWorkflow.beginSelection();
5669
6665
  workflowSelectionActive = true;
6666
+ contextualAskWorkflow?.beginSelection();
5670
6667
  }
5671
6668
  targetSequence += 1;
5672
6669
  const target2 = {
@@ -5685,6 +6682,9 @@ ${summary}`;
5685
6682
  dataFlowStatus: "idle"
5686
6683
  };
5687
6684
  targets.push(target2);
6685
+ if (!initialSelection) {
6686
+ contextualAskWorkflow?.selectionChanged();
6687
+ }
5688
6688
  activeTargetId = target2.id;
5689
6689
  addingTarget = false;
5690
6690
  const revision = sessionRevision;
@@ -5801,10 +6801,12 @@ ${summary}`;
5801
6801
  pageDataFlowStatus = "idle";
5802
6802
  renderDataFlowView();
5803
6803
  selectionSession.clear();
6804
+ contextualAskWorkflow?.selectionChanged();
5804
6805
  view.announce(view.messages().announcements.allTargetsRemoved);
5805
6806
  return;
5806
6807
  }
5807
6808
  refreshSelectionView();
6809
+ contextualAskWorkflow?.selectionChanged();
5808
6810
  void loadDataFlowReports();
5809
6811
  persistSelection();
5810
6812
  view.announce(view.messages().announcements.targetRemoved);
@@ -5983,6 +6985,7 @@ ${summary}`;
5983
6985
  }
5984
6986
  mounted = true;
5985
6987
  externalHandoffWorkflow?.mount();
6988
+ contextualAskWorkflow?.mount();
5986
6989
  view.renderStatus(state.status);
5987
6990
  browser.document.addEventListener("pointermove", handlePointerMove, true);
5988
6991
  browser.document.addEventListener("click", handleClick, true);
@@ -6034,6 +7037,7 @@ ${summary}`;
6034
7037
  if (selectionOpen && targets.length > 0) {
6035
7038
  transition({ type: "RESTORE" });
6036
7039
  agentWorkflow.beginSelection();
7040
+ contextualAskWorkflow?.beginSelection();
6037
7041
  workflowSelectionActive = true;
6038
7042
  refreshSelectionView(true);
6039
7043
  }
@@ -6050,6 +7054,7 @@ ${summary}`;
6050
7054
  api.dispose();
6051
7055
  agentWorkflow.disposeSelection();
6052
7056
  externalHandoffWorkflow?.dispose();
7057
+ contextualAskWorkflow?.dispose();
6053
7058
  sourceResolver.dispose();
6054
7059
  return;
6055
7060
  }
@@ -6114,6 +7119,7 @@ ${summary}`;
6114
7119
  api.dispose();
6115
7120
  agentWorkflow.disposeSelection();
6116
7121
  externalHandoffWorkflow?.dispose();
7122
+ contextualAskWorkflow?.dispose();
6117
7123
  sourceResolver.dispose();
6118
7124
  view.dispose();
6119
7125
  state = INITIAL_RUNTIME_STATE;
@@ -6258,9 +7264,9 @@ function installWritableDataProperty(target2, key, replacement, original = ownPr
6258
7264
  }
6259
7265
  };
6260
7266
  }
6261
- function recordWithoutAffectingHost(record2) {
7267
+ function recordWithoutAffectingHost(record3) {
6262
7268
  try {
6263
- record2();
7269
+ record3();
6264
7270
  } catch {
6265
7271
  }
6266
7272
  }