@spotpatch/runtime 1.11.0 → 1.13.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 (45) hide show
  1. package/dist/{chunk-QY5T4DPA.js → chunk-2WA6XVA4.js} +13 -6
  2. package/dist/chunk-2WA6XVA4.js.map +1 -0
  3. package/dist/{chunk-BXRMVHNT.js → chunk-ASRUAKA5.js} +1 -1
  4. package/dist/chunk-ASRUAKA5.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/chunk-RZDOL5BT.js +1 -0
  10. package/dist/chunk-RZDOL5BT.js.map +1 -0
  11. package/dist/data-flow-panel.cjs +171 -0
  12. package/dist/data-flow-panel.cjs.map +1 -1
  13. package/dist/data-flow-panel.d.cts +4 -2
  14. package/dist/data-flow-panel.d.ts +4 -2
  15. package/dist/data-flow-panel.js +27 -1
  16. package/dist/data-flow-panel.js.map +1 -1
  17. package/dist/{data-flow-runtime-B3itbieT.d.cts → data-flow-runtime-DMmV0d3E.d.cts} +7 -3
  18. package/dist/{data-flow-runtime-B3itbieT.d.ts → data-flow-runtime-DMmV0d3E.d.ts} +7 -3
  19. package/dist/data-flow.cjs +12 -5
  20. package/dist/data-flow.cjs.map +1 -1
  21. package/dist/data-flow.d.cts +1 -1
  22. package/dist/data-flow.d.ts +1 -1
  23. package/dist/data-flow.js +2 -1
  24. package/dist/external-handoff-panel.cjs +29 -9
  25. package/dist/external-handoff-panel.cjs.map +1 -1
  26. package/dist/external-handoff-panel.d.cts +3 -3
  27. package/dist/external-handoff-panel.d.ts +3 -3
  28. package/dist/external-handoff-panel.js +22 -11
  29. package/dist/external-handoff-panel.js.map +1 -1
  30. package/dist/index.cjs +1204 -279
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +1 -1
  33. package/dist/index.d.ts +1 -1
  34. package/dist/index.js +1164 -355
  35. package/dist/index.js.map +1 -1
  36. package/dist/motion.cjs +1251 -0
  37. package/dist/motion.cjs.map +1 -0
  38. package/dist/motion.d.cts +79 -0
  39. package/dist/motion.d.ts +79 -0
  40. package/dist/motion.js +1092 -0
  41. package/dist/motion.js.map +1 -0
  42. package/package.json +19 -4
  43. package/dist/chunk-7ES63LA7.js.map +0 -1
  44. package/dist/chunk-BXRMVHNT.js.map +0 -1
  45. package/dist/chunk-QY5T4DPA.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -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,18 @@ 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/motion-extension-contract.ts
2552
+ var MOTION_EXTENSION_KEY = /* @__PURE__ */ Symbol.for("spotpatch.motion.v1");
2553
+ function getFloatingSurfaceMotionExtension() {
2554
+ return globalThis[MOTION_EXTENSION_KEY];
2555
+ }
2556
+
2587
2557
  // src/ui/localization.ts
2588
2558
  var import_shared6 = require("@spotpatch/shared");
2589
2559
  var STATUS_EN = Object.freeze({
@@ -2616,6 +2586,51 @@ var STATUS_ZH = Object.freeze({
2616
2586
  reverted: "\u5DF2\u64A4\u9500",
2617
2587
  failed: "\u5931\u8D25"
2618
2588
  });
2589
+ var EXECUTION_ACTIVITY_EN = Object.freeze({
2590
+ prepare: "Preparing context",
2591
+ dispatch: "Dispatching context",
2592
+ discover: "Discovering project files",
2593
+ search: "Searching source",
2594
+ read: "Reading",
2595
+ patch: "Generating a minimal patch",
2596
+ check: "Running",
2597
+ audit: "Auditing proposed changes",
2598
+ apply: "Applying verified changes",
2599
+ sync: "Returning results to SpotPatch",
2600
+ unknown: "Processing the current task"
2601
+ });
2602
+ var EXECUTION_ACTIVITY_ZH = Object.freeze({
2603
+ prepare: "\u6B63\u5728\u51C6\u5907\u4E0A\u4E0B\u6587",
2604
+ dispatch: "\u6B63\u5728\u6D3E\u53D1\u4E0A\u4E0B\u6587",
2605
+ discover: "\u6B63\u5728\u67E5\u627E\u9879\u76EE\u6587\u4EF6",
2606
+ search: "\u6B63\u5728\u641C\u7D22\u6E90\u7801",
2607
+ read: "\u6B63\u5728\u8BFB\u53D6",
2608
+ patch: "\u6B63\u5728\u751F\u6210\u6700\u5C0F\u4FEE\u6539\u8865\u4E01",
2609
+ check: "\u6B63\u5728\u8FD0\u884C",
2610
+ audit: "\u6B63\u5728\u5BA1\u8BA1\u5019\u9009\u4FEE\u6539",
2611
+ apply: "\u6B63\u5728\u5E94\u7528\u5DF2\u9A8C\u8BC1\u4FEE\u6539",
2612
+ sync: "\u6B63\u5728\u5C06\u7ED3\u679C\u56DE\u6D41 SpotPatch",
2613
+ unknown: "\u6B63\u5728\u5904\u7406\u5F53\u524D\u4EFB\u52A1"
2614
+ });
2615
+ var EXECUTION_LANE_LABELS = Object.freeze({
2616
+ prepare: "prepare",
2617
+ dispatch: "dispatch",
2618
+ discover: "list",
2619
+ search: "search",
2620
+ read: "read",
2621
+ patch: "patch",
2622
+ check: "check",
2623
+ audit: "audit",
2624
+ apply: "apply",
2625
+ sync: "sync",
2626
+ unknown: "agent"
2627
+ });
2628
+ function executionActivityAction(labels, kind, detail) {
2629
+ return detail === void 0 || detail.length === 0 ? labels[kind] : `${labels[kind]} ${detail}`;
2630
+ }
2631
+ function executionActivityLane(kind, detail) {
2632
+ return detail === void 0 || detail.length === 0 ? EXECUTION_LANE_LABELS[kind] : `${EXECUTION_LANE_LABELS[kind]} \xB7 ${detail}`;
2633
+ }
2619
2634
  var EXTERNAL_HANDOFF_ERROR_EN = "The external Agent handoff request failed.";
2620
2635
  var EXTERNAL_HANDOFF_ERROR_ZH = "\u5916\u90E8 Agent \u4EA4\u63A5\u8BF7\u6C42\u5931\u8D25\u3002";
2621
2636
  var ERROR_MESSAGES_EN = Object.freeze({
@@ -2833,6 +2848,11 @@ var UI_MESSAGES = Object.freeze({
2833
2848
  previewTitle: "Review the request",
2834
2849
  previewSubtitle: "Verify the complete context before it leaves the browser."
2835
2850
  }),
2851
+ floatingSurface: Object.freeze({
2852
+ dragHandle: "Drag to move the SpotPatch workbench",
2853
+ resetPosition: "Reset workbench position",
2854
+ positionReset: "SpotPatch position reset to the lower-right corner."
2855
+ }),
2836
2856
  context: Object.freeze({
2837
2857
  collecting: "Collecting context",
2838
2858
  ready: "Context ready",
@@ -2919,6 +2939,27 @@ var UI_MESSAGES = Object.freeze({
2919
2939
  noOutput: "No output.",
2920
2940
  status: (status) => STATUS_EN[status]
2921
2941
  }),
2942
+ execution: Object.freeze({
2943
+ claude: "Claude",
2944
+ codex: "Codex",
2945
+ receivingTitle: "Receiving context",
2946
+ dispatchingTitle: (identity) => `Handing off to ${identity}`,
2947
+ runningTitle: (target2) => `Modifying ${target2}`,
2948
+ expandedRunningTitle: (identity) => `${identity} is making changes`,
2949
+ completedTitle: "Change complete",
2950
+ expandedCompletedTitle: "Changes complete",
2951
+ failedTitle: "Execution failed",
2952
+ receivingStatus: "Receiving",
2953
+ dispatchingStatus: "Dispatching",
2954
+ runningStatus: "Running",
2955
+ checkingStatus: "Checking",
2956
+ completedStatus: "Done",
2957
+ failedStatus: "Failed",
2958
+ resultReturned: "The result is back in SpotPatch",
2959
+ resultSummary: (files) => `${String(files)} ${files === 1 ? "file" : "files"}`,
2960
+ activityAction: (kind, detail) => executionActivityAction(EXECUTION_ACTIVITY_EN, kind, detail),
2961
+ activityLane: executionActivityLane
2962
+ }),
2922
2963
  announcements: Object.freeze({
2923
2964
  adapterDisabled: "React inspection was disabled after an adapter failure.",
2924
2965
  selectionEnabled: "Element selection enabled.",
@@ -2971,6 +3012,11 @@ var UI_MESSAGES = Object.freeze({
2971
3012
  previewTitle: "\u5BA1\u9605\u4FEE\u6539\u8BF7\u6C42",
2972
3013
  previewSubtitle: "\u53D1\u9001\u7ED9 AI \u524D\uFF0C\u8BF7\u6838\u5BF9\u5B8C\u6574\u4E0A\u4E0B\u6587\u4E0E\u6BCF\u9879\u76EE\u6807\u8BF4\u660E\u3002"
2973
3014
  }),
3015
+ floatingSurface: Object.freeze({
3016
+ dragHandle: "\u62D6\u62FD\u4EE5\u79FB\u52A8 SpotPatch \u5DE5\u4F5C\u53F0",
3017
+ resetPosition: "\u91CD\u7F6E\u5DE5\u4F5C\u53F0\u4F4D\u7F6E",
3018
+ positionReset: "SpotPatch \u5DF2\u6062\u590D\u5230\u53F3\u4E0B\u89D2\u3002"
3019
+ }),
2974
3020
  context: Object.freeze({
2975
3021
  collecting: "\u6B63\u5728\u91C7\u96C6\u4E0A\u4E0B\u6587",
2976
3022
  ready: "\u4E0A\u4E0B\u6587\u5DF2\u5C31\u7EEA",
@@ -3057,6 +3103,27 @@ var UI_MESSAGES = Object.freeze({
3057
3103
  noOutput: "\u6CA1\u6709\u8F93\u51FA\u3002",
3058
3104
  status: (status) => STATUS_ZH[status]
3059
3105
  }),
3106
+ execution: Object.freeze({
3107
+ claude: "Claude",
3108
+ codex: "Codex",
3109
+ receivingTitle: "\u6B63\u5728\u63A5\u6536\u4E0A\u4E0B\u6587",
3110
+ dispatchingTitle: (identity) => `\u6B63\u5728\u4EA4\u63A5\u7ED9 ${identity}`,
3111
+ runningTitle: (target2) => `\u6B63\u5728\u4FEE\u6539 ${target2}`,
3112
+ expandedRunningTitle: (identity) => `${identity} \u6B63\u5728\u6267\u884C\u4FEE\u6539`,
3113
+ completedTitle: "\u4FEE\u6539\u5B8C\u6210",
3114
+ expandedCompletedTitle: "\u4FEE\u6539\u5DF2\u5B8C\u6210",
3115
+ failedTitle: "\u6267\u884C\u5931\u8D25",
3116
+ receivingStatus: "\u63A5\u6536\u4E2D",
3117
+ dispatchingStatus: "\u6D3E\u53D1\u4E2D",
3118
+ runningStatus: "\u6267\u884C\u4E2D",
3119
+ checkingStatus: "\u68C0\u67E5\u4E2D",
3120
+ completedStatus: "\u5B8C\u6210",
3121
+ failedStatus: "\u5931\u8D25",
3122
+ resultReturned: "\u7ED3\u679C\u5DF2\u56DE\u6D41 SpotPatch",
3123
+ resultSummary: (files) => `${String(files)} \u4E2A\u6587\u4EF6`,
3124
+ activityAction: (kind, detail) => executionActivityAction(EXECUTION_ACTIVITY_ZH, kind, detail),
3125
+ activityLane: executionActivityLane
3126
+ }),
3060
3127
  announcements: Object.freeze({
3061
3128
  adapterDisabled: "React \u9002\u914D\u5668\u5F02\u5E38\uFF0C\u672C\u6B21\u4F1A\u8BDD\u5DF2\u505C\u7528 React \u68C0\u67E5\u3002",
3062
3129
  selectionEnabled: "\u5143\u7D20\u9009\u62E9\u5DF2\u542F\u7528\u3002",
@@ -3130,13 +3197,500 @@ function createUiLocalizer(document, preference = "auto") {
3130
3197
  });
3131
3198
  }
3132
3199
 
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
3200
+ // src/state/floating-surface-session.ts
3201
+ var STORAGE_PREFIX2 = "spotpatch:floating-surface:";
3202
+ var SNAPSHOT_VERSION2 = 1;
3203
+ var MAX_SNAPSHOT_CHARACTERS2 = 1024;
3204
+ function record2(value) {
3205
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
3206
+ }
3207
+ function finiteRatio(value) {
3208
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
3209
+ }
3210
+ function alignment(value) {
3211
+ return value === "start" || value === "end";
3212
+ }
3213
+ function parseSnapshot2(value) {
3214
+ const snapshot = record2(value);
3215
+ if (snapshot?.version !== SNAPSHOT_VERSION2 || !alignment(snapshot.horizontal) || !alignment(snapshot.vertical) || !finiteRatio(snapshot.xRatio) || !finiteRatio(snapshot.yRatio)) {
3216
+ return void 0;
3217
+ }
3218
+ return Object.freeze({
3219
+ horizontal: snapshot.horizontal,
3220
+ vertical: snapshot.vertical,
3221
+ xRatio: snapshot.xRatio,
3222
+ yRatio: snapshot.yRatio
3223
+ });
3224
+ }
3225
+ function createFloatingSurfaceSession(window, sessionId) {
3226
+ const key = `${STORAGE_PREFIX2}${sessionId}`;
3227
+ let storage;
3228
+ try {
3229
+ storage = window.sessionStorage;
3230
+ } catch {
3231
+ storage = void 0;
3232
+ }
3233
+ return Object.freeze({
3234
+ clear() {
3235
+ try {
3236
+ storage?.removeItem(key);
3237
+ } catch {
3238
+ }
3239
+ },
3240
+ load() {
3241
+ let serialized;
3242
+ try {
3243
+ serialized = storage?.getItem(key) ?? null;
3244
+ } catch {
3245
+ return void 0;
3246
+ }
3247
+ if (serialized === null || serialized.length > MAX_SNAPSHOT_CHARACTERS2) {
3248
+ return void 0;
3249
+ }
3250
+ try {
3251
+ return parseSnapshot2(JSON.parse(serialized));
3252
+ } catch {
3253
+ return void 0;
3254
+ }
3255
+ },
3256
+ save(position) {
3257
+ try {
3258
+ storage?.setItem(
3259
+ key,
3260
+ JSON.stringify({ version: SNAPSHOT_VERSION2, ...position })
3261
+ );
3262
+ } catch {
3263
+ }
3264
+ }
3265
+ });
3266
+ }
3267
+
3268
+ // src/ui/floating-surface-position.ts
3269
+ var DEFAULT_FLOATING_SURFACE_POSITION = Object.freeze({
3270
+ horizontal: "end",
3271
+ vertical: "end",
3272
+ xRatio: 1,
3273
+ yRatio: 1
3139
3274
  });
3275
+ function clamp(value, minimum, maximum) {
3276
+ return Math.min(Math.max(value, minimum), maximum);
3277
+ }
3278
+ function finiteNonNegative(value) {
3279
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
3280
+ }
3281
+ function boundedRatio(value) {
3282
+ return Number.isFinite(value) ? clamp(value, 0, 1) : 0.5;
3283
+ }
3284
+ function normalizedViewport(viewport) {
3285
+ return Object.freeze({
3286
+ height: finiteNonNegative(viewport.height),
3287
+ left: Number.isFinite(viewport.left) ? viewport.left : 0,
3288
+ top: Number.isFinite(viewport.top) ? viewport.top : 0,
3289
+ width: finiteNonNegative(viewport.width)
3290
+ });
3291
+ }
3292
+ function horizontalAnchor(rect, alignment2) {
3293
+ return alignment2 === "end" ? rect.left + rect.width : rect.left;
3294
+ }
3295
+ function verticalAnchor(rect, alignment2) {
3296
+ return alignment2 === "end" ? rect.top + rect.height : rect.top;
3297
+ }
3298
+ function normalizedCoordinate(coordinate, origin, length) {
3299
+ if (length <= 0) {
3300
+ return 0.5;
3301
+ }
3302
+ return boundedRatio((coordinate - origin) / length);
3303
+ }
3304
+ function positionFromAnchor(anchor, viewport, horizontal, vertical) {
3305
+ return Object.freeze({
3306
+ horizontal,
3307
+ vertical,
3308
+ xRatio: normalizedCoordinate(anchor.x, viewport.left, viewport.width),
3309
+ yRatio: normalizedCoordinate(anchor.y, viewport.top, viewport.height)
3310
+ });
3311
+ }
3312
+ function resolveFloatingSurfaceRect(position, size, viewport) {
3313
+ const width = finiteNonNegative(size.width);
3314
+ const height = finiteNonNegative(size.height);
3315
+ const normalized = normalizedViewport(viewport);
3316
+ const viewportWidth = normalized.width;
3317
+ const viewportHeight = normalized.height;
3318
+ const leftBoundary = normalized.left;
3319
+ const topBoundary = normalized.top;
3320
+ const rightBoundary = leftBoundary + viewportWidth;
3321
+ const bottomBoundary = topBoundary + viewportHeight;
3322
+ const anchorX = leftBoundary + boundedRatio(position.xRatio) * viewportWidth;
3323
+ const anchorY = topBoundary + boundedRatio(position.yRatio) * viewportHeight;
3324
+ const requestedLeft = position.horizontal === "end" ? anchorX - width : anchorX;
3325
+ const requestedTop = position.vertical === "end" ? anchorY - height : anchorY;
3326
+ const maximumLeft = Math.max(leftBoundary, rightBoundary - width);
3327
+ const maximumTop = Math.max(topBoundary, bottomBoundary - height);
3328
+ return Object.freeze({
3329
+ height,
3330
+ left: clamp(requestedLeft, leftBoundary, maximumLeft),
3331
+ top: clamp(requestedTop, topBoundary, maximumTop),
3332
+ width
3333
+ });
3334
+ }
3335
+ function snapFloatingSurfaceRect(rect, viewport, snapDistance) {
3336
+ const distance = finiteNonNegative(snapDistance);
3337
+ const width = finiteNonNegative(rect.width);
3338
+ const height = finiteNonNegative(rect.height);
3339
+ const normalized = normalizedViewport(viewport);
3340
+ const viewportRight = normalized.left + normalized.width;
3341
+ const viewportBottom = normalized.top + normalized.height;
3342
+ const maximumLeft = Math.max(normalized.left, viewportRight - width);
3343
+ const maximumTop = Math.max(normalized.top, viewportBottom - height);
3344
+ const requestedLeft = clamp(rect.left, normalized.left, maximumLeft);
3345
+ const requestedTop = clamp(rect.top, normalized.top, maximumTop);
3346
+ const leftDistance = Math.abs(requestedLeft - normalized.left);
3347
+ const rightDistance = Math.abs(viewportRight - (requestedLeft + width));
3348
+ const topDistance = Math.abs(requestedTop - normalized.top);
3349
+ const bottomDistance = Math.abs(viewportBottom - (requestedTop + height));
3350
+ const left = Math.min(leftDistance, rightDistance) <= distance ? leftDistance <= rightDistance ? normalized.left : viewportRight - width : requestedLeft;
3351
+ const top = Math.min(topDistance, bottomDistance) <= distance ? topDistance <= bottomDistance ? normalized.top : viewportBottom - height : requestedTop;
3352
+ return Object.freeze({ height, left, top, width });
3353
+ }
3354
+ function positionFromFloatingSurfaceRect(rect, viewport, previous) {
3355
+ const bounded = snapFloatingSurfaceRect(rect, viewport, 0);
3356
+ const centerX = bounded.left + bounded.width / 2;
3357
+ const centerY = bounded.top + bounded.height / 2;
3358
+ const viewportCenterX = viewport.left + finiteNonNegative(viewport.width) / 2;
3359
+ const viewportCenterY = viewport.top + finiteNonNegative(viewport.height) / 2;
3360
+ const horizontal = centerX === viewportCenterX ? previous.horizontal : centerX < viewportCenterX ? "start" : "end";
3361
+ const vertical = centerY === viewportCenterY ? previous.vertical : centerY < viewportCenterY ? "start" : "end";
3362
+ return positionFromAnchor(
3363
+ {
3364
+ x: horizontalAnchor(bounded, horizontal),
3365
+ y: verticalAnchor(bounded, vertical)
3366
+ },
3367
+ viewport,
3368
+ horizontal,
3369
+ vertical
3370
+ );
3371
+ }
3372
+
3373
+ // src/ui/floating-surface-controller.ts
3374
+ function finite(value, fallback = 0) {
3375
+ return Number.isFinite(value) ? value : fallback;
3376
+ }
3377
+ function distanceBetween(first, second) {
3378
+ return Math.hypot(first.x - second.x, first.y - second.y);
3379
+ }
3380
+ function isPrimaryPointer(event) {
3381
+ return event.button === 0 && event.isPrimary;
3382
+ }
3383
+ function surfaceSize(surface) {
3384
+ const rect = surface.getBoundingClientRect();
3385
+ return Object.freeze({
3386
+ height: Math.max(0, finite(rect.height)),
3387
+ width: Math.max(0, finite(rect.width))
3388
+ });
3389
+ }
3390
+ function requestFrame(window, callback) {
3391
+ if (typeof window.requestAnimationFrame === "function") {
3392
+ return window.requestAnimationFrame(callback);
3393
+ }
3394
+ return window.setTimeout(() => {
3395
+ callback(Date.now());
3396
+ }, 0);
3397
+ }
3398
+ function cancelFrame(window, frame) {
3399
+ if (typeof window.cancelAnimationFrame === "function") {
3400
+ window.cancelAnimationFrame(frame);
3401
+ return;
3402
+ }
3403
+ window.clearTimeout(frame);
3404
+ }
3405
+ function createFloatingSurfaceController(window, session, layout) {
3406
+ let position = session.load() ?? DEFAULT_FLOATING_SURFACE_POSITION;
3407
+ let drag;
3408
+ let frame;
3409
+ let suppressClickFor;
3410
+ let suppressClickTimer;
3411
+ let disposed = false;
3412
+ const surfaces = /* @__PURE__ */ new Set();
3413
+ const cleanup = /* @__PURE__ */ new Set();
3414
+ function viewport() {
3415
+ const visualViewport = window.visualViewport;
3416
+ const width = Math.max(0, finite(visualViewport?.width ?? window.innerWidth));
3417
+ const height = Math.max(0, finite(visualViewport?.height ?? window.innerHeight));
3418
+ const inset = width <= layout.compactViewportWidth ? layout.compactInset : layout.desktopInset;
3419
+ return Object.freeze({
3420
+ height: Math.max(0, height - inset * 2),
3421
+ left: finite(visualViewport?.offsetLeft ?? 0) + inset,
3422
+ top: finite(visualViewport?.offsetTop ?? 0) + inset,
3423
+ width: Math.max(0, width - inset * 2)
3424
+ });
3425
+ }
3426
+ function isCompact() {
3427
+ return viewport().width + layout.compactInset * 2 <= layout.compactViewportWidth;
3428
+ }
3429
+ function apply(surface) {
3430
+ if (surface.hidden) {
3431
+ return void 0;
3432
+ }
3433
+ if (surface.dataset.motionMorphing === "true" && surface.dataset.floatingPositioned === "true") {
3434
+ return void 0;
3435
+ }
3436
+ const size = surfaceSize(surface);
3437
+ if (size.width === 0 || size.height === 0) {
3438
+ return void 0;
3439
+ }
3440
+ const rect = resolveFloatingSurfaceRect(position, size, viewport());
3441
+ surface.style.left = `${String(rect.left)}px`;
3442
+ surface.style.top = `${String(rect.top)}px`;
3443
+ surface.style.right = "auto";
3444
+ surface.style.bottom = "auto";
3445
+ surface.dataset.floatingPositioned = "true";
3446
+ return rect;
3447
+ }
3448
+ function reconcile() {
3449
+ if (disposed) {
3450
+ return;
3451
+ }
3452
+ if (frame !== void 0) {
3453
+ cancelFrame(window, frame);
3454
+ frame = void 0;
3455
+ }
3456
+ for (const surface of surfaces) {
3457
+ apply(surface);
3458
+ }
3459
+ }
3460
+ function requestReconcile() {
3461
+ if (disposed || frame !== void 0) {
3462
+ return;
3463
+ }
3464
+ frame = requestFrame(window, () => {
3465
+ frame = void 0;
3466
+ reconcile();
3467
+ });
3468
+ }
3469
+ function commit(nextPosition) {
3470
+ position = nextPosition;
3471
+ session.save(position);
3472
+ }
3473
+ function finishDrag(commitPosition) {
3474
+ const current = drag;
3475
+ if (current === void 0) {
3476
+ return;
3477
+ }
3478
+ drag = void 0;
3479
+ current.handle.dataset.dragging = "false";
3480
+ current.surface.dataset.dragging = "false";
3481
+ try {
3482
+ if (current.handle.hasPointerCapture(current.pointerId)) {
3483
+ current.handle.releasePointerCapture(current.pointerId);
3484
+ }
3485
+ } catch {
3486
+ }
3487
+ if (commitPosition) {
3488
+ const rect = apply(current.surface);
3489
+ if (rect !== void 0) {
3490
+ const snapped = snapFloatingSurfaceRect(
3491
+ rect,
3492
+ viewport(),
3493
+ layout.edgeSnapDistance
3494
+ );
3495
+ commit(positionFromFloatingSurfaceRect(snapped, viewport(), position));
3496
+ apply(current.surface);
3497
+ }
3498
+ } else {
3499
+ position = session.load() ?? DEFAULT_FLOATING_SURFACE_POSITION;
3500
+ reconcile();
3501
+ }
3502
+ }
3503
+ function updateDrag(event) {
3504
+ const current = drag;
3505
+ if (current === void 0) {
3506
+ return;
3507
+ }
3508
+ if (event.pointerId !== current.pointerId) {
3509
+ return;
3510
+ }
3511
+ const pointer = Object.freeze({ x: event.clientX, y: event.clientY });
3512
+ const start = Object.freeze({ x: current.startX, y: current.startY });
3513
+ if (current.handle.dataset.dragging !== "true" && distanceBetween(pointer, start) < layout.dragActivationDistance) {
3514
+ return;
3515
+ }
3516
+ if (current.handle.dataset.dragging !== "true") {
3517
+ current.onDragStart?.();
3518
+ }
3519
+ current.handle.dataset.dragging = "true";
3520
+ current.surface.dataset.dragging = "true";
3521
+ position = positionFromAnchor(
3522
+ {
3523
+ x: pointer.x + current.anchorOffsetX,
3524
+ y: pointer.y + current.anchorOffsetY
3525
+ },
3526
+ viewport(),
3527
+ position.horizontal,
3528
+ position.vertical
3529
+ );
3530
+ requestReconcile();
3531
+ event.preventDefault();
3532
+ }
3533
+ function addListener(target2, type, listener, options) {
3534
+ target2.addEventListener(type, listener, options);
3535
+ cleanup.add(() => {
3536
+ target2.removeEventListener(type, listener, options);
3537
+ });
3538
+ }
3539
+ function attachDraggable(handle, surface, options = {}) {
3540
+ const onPointerDown = (event) => {
3541
+ if (disposed || drag !== void 0 || !isPrimaryPointer(event) || options.canStartDrag?.(event) === false) {
3542
+ return;
3543
+ }
3544
+ const rect = apply(surface) ?? surface.getBoundingClientRect();
3545
+ const anchorX = position.horizontal === "end" ? rect.left + rect.width : rect.left;
3546
+ const anchorY = position.vertical === "end" ? rect.top + rect.height : rect.top;
3547
+ drag = Object.freeze({
3548
+ anchorOffsetX: anchorX - event.clientX,
3549
+ anchorOffsetY: anchorY - event.clientY,
3550
+ handle,
3551
+ ...options.onDragStart === void 0 ? {} : { onDragStart: options.onDragStart },
3552
+ pointerId: event.pointerId,
3553
+ startX: event.clientX,
3554
+ startY: event.clientY,
3555
+ suppressClickOnDrag: options.suppressClickOnDrag ?? false,
3556
+ surface
3557
+ });
3558
+ try {
3559
+ handle.setPointerCapture(event.pointerId);
3560
+ } catch {
3561
+ }
3562
+ };
3563
+ const onLostPointerCapture = (event) => {
3564
+ if (drag?.pointerId === event.pointerId) {
3565
+ finishDrag(handle.dataset.dragging === "true");
3566
+ }
3567
+ };
3568
+ const onClickCapture = (event) => {
3569
+ if (suppressClickFor !== handle) {
3570
+ return;
3571
+ }
3572
+ suppressClickFor = void 0;
3573
+ if (suppressClickTimer !== void 0) {
3574
+ window.clearTimeout(suppressClickTimer);
3575
+ suppressClickTimer = void 0;
3576
+ }
3577
+ event.preventDefault();
3578
+ event.stopImmediatePropagation();
3579
+ };
3580
+ addListener(handle, "pointerdown", (event) => {
3581
+ onPointerDown(event);
3582
+ });
3583
+ addListener(handle, "lostpointercapture", (event) => {
3584
+ onLostPointerCapture(event);
3585
+ });
3586
+ addListener(
3587
+ handle,
3588
+ "click",
3589
+ (event) => {
3590
+ onClickCapture(event);
3591
+ },
3592
+ true
3593
+ );
3594
+ }
3595
+ function reset() {
3596
+ finishDrag(false);
3597
+ position = DEFAULT_FLOATING_SURFACE_POSITION;
3598
+ session.clear();
3599
+ reconcile();
3600
+ }
3601
+ function cancelOnWindowBlur() {
3602
+ finishDrag(false);
3603
+ }
3604
+ function cancelOnEscape(event) {
3605
+ if (event.key !== "Escape" || drag === void 0) {
3606
+ return;
3607
+ }
3608
+ finishDrag(false);
3609
+ event.preventDefault();
3610
+ event.stopImmediatePropagation();
3611
+ }
3612
+ function endPointer(event) {
3613
+ if (drag?.pointerId !== event.pointerId) {
3614
+ return;
3615
+ }
3616
+ const current = drag;
3617
+ const moved = current.handle.dataset.dragging === "true";
3618
+ finishDrag(moved);
3619
+ if (moved && current.suppressClickOnDrag) {
3620
+ suppressClickFor = current.handle;
3621
+ suppressClickTimer = window.setTimeout(() => {
3622
+ suppressClickFor = void 0;
3623
+ suppressClickTimer = void 0;
3624
+ }, 0);
3625
+ }
3626
+ }
3627
+ function cancelPointer(event) {
3628
+ if (drag?.pointerId === event.pointerId) {
3629
+ finishDrag(false);
3630
+ }
3631
+ }
3632
+ addListener(window, "pointermove", (event) => {
3633
+ updateDrag(event);
3634
+ });
3635
+ addListener(window, "pointerup", (event) => {
3636
+ endPointer(event);
3637
+ });
3638
+ addListener(window, "pointercancel", (event) => {
3639
+ cancelPointer(event);
3640
+ });
3641
+ addListener(window, "resize", requestReconcile);
3642
+ addListener(window, "blur", cancelOnWindowBlur);
3643
+ addListener(
3644
+ window,
3645
+ "keydown",
3646
+ (event) => {
3647
+ cancelOnEscape(event);
3648
+ },
3649
+ true
3650
+ );
3651
+ window.visualViewport?.addEventListener("resize", requestReconcile);
3652
+ window.visualViewport?.addEventListener("scroll", requestReconcile);
3653
+ cleanup.add(
3654
+ () => window.visualViewport?.removeEventListener("resize", requestReconcile)
3655
+ );
3656
+ cleanup.add(
3657
+ () => window.visualViewport?.removeEventListener("scroll", requestReconcile)
3658
+ );
3659
+ return Object.freeze({
3660
+ attachDraggable,
3661
+ dispose() {
3662
+ if (disposed) {
3663
+ return;
3664
+ }
3665
+ disposed = true;
3666
+ finishDrag(false);
3667
+ if (frame !== void 0) {
3668
+ cancelFrame(window, frame);
3669
+ frame = void 0;
3670
+ }
3671
+ if (suppressClickTimer !== void 0) {
3672
+ window.clearTimeout(suppressClickTimer);
3673
+ suppressClickTimer = void 0;
3674
+ }
3675
+ suppressClickFor = void 0;
3676
+ for (const disposeListener of cleanup) {
3677
+ disposeListener();
3678
+ }
3679
+ cleanup.clear();
3680
+ surfaces.clear();
3681
+ },
3682
+ isCompact,
3683
+ reconcile,
3684
+ registerSurface(surface) {
3685
+ surfaces.add(surface);
3686
+ },
3687
+ requestReconcile,
3688
+ reset
3689
+ });
3690
+ }
3691
+
3692
+ // src/ui/runtime-view.ts
3693
+ var SUCCESS_SETTLE_MS = 1500;
3140
3694
  function resolveStyleNonce(document) {
3141
3695
  const nonces = new Set(
3142
3696
  [...document.querySelectorAll("script[nonce]")].map((script) => script.nonce?.trim() ?? "").filter(Boolean)
@@ -3167,53 +3721,20 @@ function createStyles(document) {
3167
3721
  --spotpatch-success: #34d399;
3168
3722
  --spotpatch-warning: #f59e0b;
3169
3723
  --spotpatch-text-on-accent: #0b0b12;
3170
- --spotpatch-radius-panel: 16px;
3171
3724
  --spotpatch-radius-card: 10px;
3172
- --spotpatch-shadow-panel: 0 30px 60px -20px rgb(0 0 0 / 70%);
3173
- color-scheme: dark;
3174
3725
  color: var(--spotpatch-text);
3175
3726
  font-family: Inter, "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
3176
3727
  font-size: 14px;
3177
3728
  line-height: 1.5;
3178
- text-rendering: optimizeLegibility;
3179
- -webkit-font-smoothing: antialiased;
3180
3729
  }
3181
3730
  [hidden] { display: none !important; }
3182
3731
  button, textarea, select { font: inherit; }
3183
- button { -webkit-tap-highlight-color: transparent; }
3184
- .spotpatch-trigger {
3732
+ .spotpatch-floating-surface {
3185
3733
  position: fixed;
3186
- right: 24px;
3187
- bottom: 24px;
3734
+ right: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3735
+ bottom: ${String(FLOATING_SURFACE_LAYOUT.desktopInset)}px;
3188
3736
  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%);
3737
+ isolation: isolate;
3217
3738
  }
3218
3739
  .spotpatch-highlight {
3219
3740
  position: fixed;
@@ -3282,82 +3803,28 @@ function createStyles(document) {
3282
3803
  background: #5546dc;
3283
3804
  }
3284
3805
  .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)};
3806
+ position: relative;
3291
3807
  box-sizing: border-box;
3292
- width: min(${String(DIALOG_MAX_WIDTH)}px, calc(100vw - 32px));
3293
- color: #edf0f7;
3808
+ width: min(${String(FLOATING_SURFACE_LAYOUT.workbenchMaxWidth)}px, calc(100vw - ${String(FLOATING_SURFACE_LAYOUT.desktopInset * 2)}px));
3294
3809
  outline: none;
3295
- filter: drop-shadow(var(--spotpatch-shadow-panel));
3296
3810
  }
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
3811
  .spotpatch-shell {
3333
3812
  position: relative;
3334
3813
  z-index: 1;
3335
3814
  display: flex;
3336
3815
  box-sizing: border-box;
3337
- max-height: min(${String(DIALOG_MAX_HEIGHT)}px, calc(100vh - 32px));
3816
+ max-height: min(${String(FLOATING_SURFACE_LAYOUT.workbenchMaxHeight)}px, calc(100vh - ${String(FLOATING_SURFACE_LAYOUT.desktopInset * 2)}px));
3338
3817
  overflow: hidden;
3339
3818
  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
3819
  }
3357
3820
  .spotpatch-header {
3358
3821
  position: relative;
3359
3822
  padding: 0 18px 14px;
3360
3823
  }
3824
+ .spotpatch-header[data-spotpatch-drag-handle] { cursor: grab; touch-action: none; user-select: none; }
3825
+ .spotpatch-header[data-dragging="true"] { cursor: grabbing; }
3826
+ .spotpatch-header button,
3827
+ .spotpatch-header a { cursor: pointer; user-select: auto; }
3361
3828
  .spotpatch-brand-row {
3362
3829
  display: flex;
3363
3830
  align-items: center;
@@ -3381,18 +3848,6 @@ function createStyles(document) {
3381
3848
  filter: drop-shadow(0 6px 12px rgb(76 29 149 / 22%));
3382
3849
  }
3383
3850
  .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
3851
  .spotpatch-header-controls {
3397
3852
  display: inline-flex;
3398
3853
  align-items: center;
@@ -3401,6 +3856,7 @@ function createStyles(document) {
3401
3856
  }
3402
3857
  .spotpatch-repository,
3403
3858
  .spotpatch-locale,
3859
+ .spotpatch-reset-position,
3404
3860
  .spotpatch-close {
3405
3861
  height: 27px;
3406
3862
  border: 1px solid rgb(255 255 255 / 11%);
@@ -3424,6 +3880,16 @@ function createStyles(document) {
3424
3880
  font-size: 12px;
3425
3881
  font-weight: 650;
3426
3882
  }
3883
+ .spotpatch-reset-position {
3884
+ display: inline-grid;
3885
+ width: 27px;
3886
+ padding: 0;
3887
+ place-items: center;
3888
+ color: #c5cad5;
3889
+ cursor: pointer;
3890
+ font-size: 15px;
3891
+ line-height: 1;
3892
+ }
3427
3893
  .spotpatch-close {
3428
3894
  display: inline-grid;
3429
3895
  width: 27px;
@@ -3437,21 +3903,8 @@ function createStyles(document) {
3437
3903
  }
3438
3904
  .spotpatch-close:hover,
3439
3905
  .spotpatch-locale:hover,
3906
+ .spotpatch-reset-position:hover,
3440
3907
  .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
3908
  .spotpatch-target-row {
3456
3909
  display: flex;
3457
3910
  align-items: center;
@@ -3815,6 +4268,7 @@ function createStyles(document) {
3815
4268
  .spotpatch-target-open:focus-visible,
3816
4269
  .spotpatch-target-remove:focus-visible,
3817
4270
  .spotpatch-close:focus-visible,
4271
+ .spotpatch-reset-position:focus-visible,
3818
4272
  .spotpatch-trigger:focus-visible,
3819
4273
  .spotpatch-target-select:focus-visible,
3820
4274
  .spotpatch-target-editor textarea:focus-visible,
@@ -3839,8 +4293,8 @@ function createStyles(document) {
3839
4293
  .spotpatch-diagnostics > summary::before { transition: none; }
3840
4294
  }
3841
4295
  @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; }
4296
+ .spotpatch-dialog { width: calc(100vw - 16px); }
4297
+ .spotpatch-shell { max-height: calc(100dvh - 16px); }
3844
4298
  .spotpatch-header, .spotpatch-body { padding-left: 14px; padding-right: 14px; }
3845
4299
  .spotpatch-brand-row { margin-right: -14px; margin-left: -14px; padding-right: 14px; padding-left: 14px; }
3846
4300
  .spotpatch-actions { padding-right: 14px; padding-left: 14px; }
@@ -3856,6 +4310,38 @@ function summaryLine(summary, prefix) {
3856
4310
  const line = summary.split("\n").find((candidate) => candidate.startsWith(`${prefix}: `));
3857
4311
  return line?.slice(prefix.length + 2).trim();
3858
4312
  }
4313
+ function startsOnInteractiveControl(event) {
4314
+ const target2 = event.target;
4315
+ return target2 instanceof Element && target2.closest(
4316
+ "a, button, input, select, textarea, summary, [contenteditable='true']"
4317
+ ) !== null;
4318
+ }
4319
+ function createFallbackExecutionIsland(document) {
4320
+ const root = createButton(document, "", "spotpatch-execution-island");
4321
+ root.hidden = true;
4322
+ root.setAttribute("aria-hidden", "true");
4323
+ const logo = createBrandMark(
4324
+ document,
4325
+ void 0,
4326
+ "spotpatch-fallback-execution-island"
4327
+ );
4328
+ logo.classList.add("spotpatch-execution-logo");
4329
+ const copy = createMarkedElement(document, "span");
4330
+ root.append(logo, copy);
4331
+ return Object.freeze({
4332
+ canExpand: () => false,
4333
+ isExpanded: () => false,
4334
+ root,
4335
+ setExpanded: () => void 0,
4336
+ render(projection) {
4337
+ const text2 = [projection.headline, projection.action, projection.meta].filter((value) => value.length > 0).join(" \xB7 ");
4338
+ copy.textContent = text2;
4339
+ root.setAttribute("aria-label", text2);
4340
+ },
4341
+ dispose() {
4342
+ }
4343
+ });
4344
+ }
3859
4345
  function createUnavailableDataFlowPanel(document, changesRoot, diagnosticsRoot) {
3860
4346
  changesRoot.append(diagnosticsRoot);
3861
4347
  return Object.freeze({
@@ -3869,10 +4355,33 @@ function createUnavailableDataFlowPanel(document, changesRoot, diagnosticsRoot)
3869
4355
  }
3870
4356
  function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: false }), localePreference = "auto", dataFlowEnabled = false, externalAgentEnabled = false, framework = "vite", sessionId = "") {
3871
4357
  const localizer = createUiLocalizer(document, localePreference);
4358
+ const associatedWindow = document.defaultView;
4359
+ if (associatedWindow === null) {
4360
+ throw new Error("SpotPatch requires a document with an associated window.");
4361
+ }
4362
+ const runtimeWindow = associatedWindow;
4363
+ const floatingSurface = createFloatingSurfaceController(
4364
+ runtimeWindow,
4365
+ createFloatingSurfaceSession(runtimeWindow, sessionId),
4366
+ FLOATING_SURFACE_LAYOUT
4367
+ );
3872
4368
  let messages = localizer.messages();
4369
+ let currentStatus = "idle";
4370
+ let plannerVisible = false;
4371
+ let executionSuppressed = false;
4372
+ let executionProjection;
4373
+ let pendingAgentMotion;
4374
+ let successSettleKey;
4375
+ let settledSuccessKey;
4376
+ let successSettleTimer;
4377
+ let observedExecutionStartedAt;
3873
4378
  const host = document.createElement("spotpatch-root");
3874
4379
  host.setAttribute(UI_MARKER_ATTRIBUTE, "");
3875
4380
  const shadowRoot = host.attachShadow({ mode: "open" });
4381
+ const floatingSurfaceRoot = createMarkedElement(document, "div");
4382
+ floatingSurfaceRoot.className = "spotpatch-floating-surface";
4383
+ floatingSurfaceRoot.dataset.scene = "pill";
4384
+ floatingSurfaceRoot.dataset.tone = "neutral";
3876
4385
  const triggerButton = createButton(
3877
4386
  document,
3878
4387
  messages.trigger.select,
@@ -3880,6 +4389,10 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3880
4389
  );
3881
4390
  triggerButton.title = messages.trigger.title(shortcut);
3882
4391
  triggerButton.setAttribute("aria-pressed", "false");
4392
+ const motionExtension = getFloatingSurfaceMotionExtension();
4393
+ const motionExecutionIsland = motionExtension?.createExecutionIsland(document);
4394
+ const executionIslandView = motionExecutionIsland ?? createFallbackExecutionIsland(document);
4395
+ const executionIsland = executionIslandView.root;
3883
4396
  const highlight = createMarkedElement(document, "div");
3884
4397
  highlight.className = "spotpatch-highlight";
3885
4398
  highlight.hidden = true;
@@ -3893,16 +4406,13 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3893
4406
  dialog.className = "spotpatch-dialog";
3894
4407
  dialog.hidden = true;
3895
4408
  dialog.tabIndex = -1;
3896
- dialog.dataset.placement = "viewport";
3897
4409
  dialog.setAttribute("role", "dialog");
3898
4410
  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
4411
  const shell = createMarkedElement(document, "div");
3903
4412
  shell.className = "spotpatch-shell";
3904
4413
  const header = createMarkedElement(document, "header");
3905
4414
  header.className = "spotpatch-header";
4415
+ header.setAttribute("data-spotpatch-drag-handle", "");
3906
4416
  const brandRow = createMarkedElement(document, "div");
3907
4417
  brandRow.className = "spotpatch-brand-row";
3908
4418
  const brand = createMarkedElement(document, "div");
@@ -3923,8 +4433,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3923
4433
  repositoryLink.target = "_blank";
3924
4434
  repositoryLink.rel = "noopener noreferrer";
3925
4435
  const localeButton = createButton(document, "", "spotpatch-locale");
4436
+ const resetPositionButton = createButton(document, "\u2316", "spotpatch-reset-position");
3926
4437
  const closeButton = createButton(document, "\xD7", "spotpatch-close");
3927
- headerControls.append(repositoryLink, localeButton, closeButton);
4438
+ headerControls.append(repositoryLink, localeButton, resetPositionButton, closeButton);
3928
4439
  brandRow.append(brand, headerControls);
3929
4440
  const title = createMarkedElement(document, "h2");
3930
4441
  title.id = "spotpatch-selection-title";
@@ -3984,13 +4495,20 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
3984
4495
  diagnostics.append(diagnosticsLabel, summary);
3985
4496
  const agentPanel = createAgentPanel(document, ai, localizer);
3986
4497
  const changesPanel = createMarkedElement(document, "div");
4498
+ function requestFloatingSurfaceLayout() {
4499
+ if (plannerVisible) {
4500
+ floatingSurface.requestReconcile();
4501
+ }
4502
+ }
3987
4503
  const externalHandoffPanel = externalAgentEnabled ? getExternalHandoffExtension()?.createPanel(
3988
4504
  document,
3989
4505
  framework,
3990
4506
  localizer.locale,
3991
4507
  sessionId,
3992
4508
  localizer.subscribe,
3993
- placeDialog
4509
+ requestFloatingSurfaceLayout,
4510
+ renderExternalDispatch,
4511
+ renderExternalControl
3994
4512
  ) : void 0;
3995
4513
  changesPanel.append(
3996
4514
  targetsPanel,
@@ -4003,7 +4521,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4003
4521
  localizer.locale,
4004
4522
  changesPanel,
4005
4523
  diagnostics,
4006
- placeDialog
4524
+ requestFloatingSurfaceLayout
4007
4525
  ) ?? createUnavailableDataFlowPanel(document, changesPanel, diagnostics);
4008
4526
  selectionPanel.append(dataFlowPanel.root);
4009
4527
  const previewPanel = createMarkedElement(document, "div");
@@ -4059,13 +4577,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4059
4577
  );
4060
4578
  actions.append(editorFeedback, secondaryActions, primaryActions);
4061
4579
  shell.append(header, body, actions);
4062
- dialog.append(anchor, shell);
4580
+ dialog.append(shell);
4063
4581
  const liveRegion = createMarkedElement(document, "div");
4064
4582
  liveRegion.className = "spotpatch-live";
4065
4583
  liveRegion.setAttribute("aria-live", "polite");
4066
4584
  liveRegion.setAttribute("aria-atomic", "true");
4067
4585
  const styles2 = [
4068
4586
  createStyles(document),
4587
+ ...motionExtension === void 0 ? [] : [motionExtension.createStyles(document)],
4069
4588
  dataFlowPanel.styles,
4070
4589
  ...externalHandoffPanel === void 0 ? [] : [externalHandoffPanel.styles]
4071
4590
  ];
@@ -4079,14 +4598,38 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4079
4598
  ...styles2,
4080
4599
  selectionHighlights,
4081
4600
  highlight,
4082
- dialog,
4083
- triggerButton,
4601
+ floatingSurfaceRoot,
4084
4602
  liveRegion
4085
4603
  );
4604
+ floatingSurfaceRoot.append(triggerButton, dialog, executionIsland);
4086
4605
  document.documentElement.append(host);
4087
- let currentRect;
4088
- let activeSelectionRect;
4089
- let currentStatus = "idle";
4606
+ floatingSurface.registerSurface(floatingSurfaceRoot);
4607
+ const motionController = motionExtension === void 0 || motionExecutionIsland === void 0 ? void 0 : motionExtension.createController(
4608
+ document,
4609
+ Object.freeze({
4610
+ execution: motionExecutionIsland.elements,
4611
+ pill: triggerButton,
4612
+ planner: dialog,
4613
+ surface: floatingSurfaceRoot
4614
+ }),
4615
+ floatingSurface.reconcile
4616
+ );
4617
+ const cancelMotionOnDrag = () => {
4618
+ motionController?.cancel();
4619
+ };
4620
+ floatingSurface.attachDraggable(triggerButton, floatingSurfaceRoot, {
4621
+ onDragStart: cancelMotionOnDrag,
4622
+ suppressClickOnDrag: true
4623
+ });
4624
+ floatingSurface.attachDraggable(header, floatingSurfaceRoot, {
4625
+ canStartDrag: (event) => !floatingSurface.isCompact() && !startsOnInteractiveControl(event),
4626
+ onDragStart: cancelMotionOnDrag
4627
+ });
4628
+ floatingSurface.attachDraggable(executionIsland, floatingSurfaceRoot, {
4629
+ onDragStart: cancelMotionOnDrag,
4630
+ suppressClickOnDrag: true
4631
+ });
4632
+ floatingSurface.reconcile();
4090
4633
  let currentCanOpenEditor = false;
4091
4634
  let currentCanPreview = false;
4092
4635
  let currentSummaryText = "";
@@ -4101,12 +4644,274 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4101
4644
  page: Object.freeze({ status: dataFlowEnabled ? "idle" : "disabled" }),
4102
4645
  observationCount: 0
4103
4646
  });
4647
+ function currentTargetSummary() {
4648
+ const target2 = currentTargets.find((candidate) => candidate.active);
4649
+ return target2 === void 0 ? Object.freeze({
4650
+ label: messages.context.selectedElement,
4651
+ source: ""
4652
+ }) : Object.freeze({ label: target2.label, source: target2.source });
4653
+ }
4654
+ function currentTargetContext() {
4655
+ const target2 = currentTargetSummary();
4656
+ return target2.source.length === 0 ? target2.label : `${target2.label} \xB7 ${target2.source}`;
4657
+ }
4658
+ function beginObservedExecution() {
4659
+ observedExecutionStartedAt = (/* @__PURE__ */ new Date()).toISOString();
4660
+ return observedExecutionStartedAt;
4661
+ }
4662
+ function observedExecutionStart() {
4663
+ return observedExecutionStartedAt ?? beginObservedExecution();
4664
+ }
4665
+ function clearSuccessSettle(resetKeys) {
4666
+ if (successSettleTimer !== void 0) {
4667
+ runtimeWindow.clearTimeout(successSettleTimer);
4668
+ successSettleTimer = void 0;
4669
+ }
4670
+ if (resetKeys) {
4671
+ successSettleKey = void 0;
4672
+ settledSuccessKey = void 0;
4673
+ }
4674
+ }
4675
+ function showSuccessUntilSettled(key) {
4676
+ if (settledSuccessKey === key) return false;
4677
+ if (successSettleKey === key) return true;
4678
+ clearSuccessSettle(false);
4679
+ successSettleKey = key;
4680
+ successSettleTimer = runtimeWindow.setTimeout(() => {
4681
+ successSettleTimer = void 0;
4682
+ if (!host.isConnected) return;
4683
+ settledSuccessKey = key;
4684
+ executionProjection = void 0;
4685
+ observedExecutionStartedAt = void 0;
4686
+ executionSuppressed = false;
4687
+ plannerVisible = false;
4688
+ renderFloatingSurfaceMotion();
4689
+ }, SUCCESS_SETTLE_MS);
4690
+ return true;
4691
+ }
4692
+ function externalAgentIdentity(kind) {
4693
+ return kind.startsWith("claude") ? messages.execution.claude : kind.startsWith("codex") ? messages.execution.codex : kind;
4694
+ }
4695
+ function projectAgentActivity(item) {
4696
+ return Object.freeze({
4697
+ ...item.detail === void 0 ? {} : { detail: item.detail },
4698
+ key: item.key,
4699
+ kind: item.kind,
4700
+ label: messages.execution.activityLane(item.kind, item.detail),
4701
+ state: item.state
4702
+ });
4703
+ }
4704
+ function motionActivity(key, kind, detail, state = "active") {
4705
+ return Object.freeze({
4706
+ ...detail === void 0 ? {} : { detail },
4707
+ key,
4708
+ kind,
4709
+ label: messages.execution.activityLane(kind, detail),
4710
+ state
4711
+ });
4712
+ }
4713
+ function renderFloatingSurfaceMotion() {
4714
+ const capturing = currentStatus === "inspecting";
4715
+ const pillActive = !plannerVisible || capturing;
4716
+ const projection = pillActive ? {
4717
+ scene: capturing ? "capturing" : "pill",
4718
+ tone: capturing ? "capturing" : "neutral",
4719
+ headline: triggerButton.textContent,
4720
+ action: "",
4721
+ meta: "",
4722
+ recentActivities: Object.freeze([])
4723
+ } : !executionSuppressed && executionProjection !== void 0 ? executionProjection : {
4724
+ scene: "planner",
4725
+ tone: "ready",
4726
+ headline: messages.dialog.editTitle,
4727
+ action: messages.dialog.editSubtitle,
4728
+ meta: messages.context.ready,
4729
+ recentActivities: Object.freeze([])
4730
+ };
4731
+ if (motionController !== void 0) {
4732
+ motionController.render(projection, () => {
4733
+ executionIslandView.render(projection);
4734
+ });
4735
+ return;
4736
+ }
4737
+ executionIslandView.render(projection);
4738
+ floatingSurfaceRoot.dataset.scene = projection.scene;
4739
+ floatingSurfaceRoot.dataset.tone = projection.tone;
4740
+ const plannerActive = projection.scene === "planner";
4741
+ triggerButton.hidden = !pillActive;
4742
+ triggerButton.inert = !pillActive;
4743
+ dialog.hidden = !plannerActive;
4744
+ dialog.inert = !plannerActive;
4745
+ dialog.setAttribute("aria-hidden", String(!plannerActive));
4746
+ executionIsland.hidden = pillActive || plannerActive;
4747
+ executionIsland.inert = pillActive || plannerActive;
4748
+ executionIsland.setAttribute("aria-hidden", String(pillActive || plannerActive));
4749
+ floatingSurface.reconcile();
4750
+ }
4751
+ function renderExternalDispatch(dispatch) {
4752
+ if (dispatch === null) {
4753
+ pendingAgentMotion = void 0;
4754
+ observedExecutionStartedAt = void 0;
4755
+ } else if (pendingAgentMotion !== void 0 && dispatch.phase !== "failed" && dispatch.phase !== "delivery-unknown") {
4756
+ startStagedAgentRequest();
4757
+ } else if (dispatch.phase === "failed" || dispatch.phase === "delivery-unknown") {
4758
+ pendingAgentMotion = void 0;
4759
+ }
4760
+ if (dispatch !== null && !executionSuppressed) {
4761
+ const failed = dispatch.phase === "failed" || dispatch.phase === "delivery-unknown";
4762
+ const completed = dispatch.phase === "completed";
4763
+ const startedAt = failed || completed ? void 0 : observedExecutionStart();
4764
+ if (failed || completed) observedExecutionStartedAt = void 0;
4765
+ const successKey = `dispatch:${String(dispatch.revision)}`;
4766
+ if (completed && !showSuccessUntilSettled(successKey)) {
4767
+ executionProjection = void 0;
4768
+ renderFloatingSurfaceMotion();
4769
+ return;
4770
+ }
4771
+ const identity = externalAgentIdentity(dispatch.adapterKind);
4772
+ const target2 = currentTargetSummary();
4773
+ const activityKind = dispatch.phase === "queued" || dispatch.phase === "dispatching" || dispatch.phase === "dispatched" ? "dispatch" : completed ? "sync" : "unknown";
4774
+ const activity = motionActivity(
4775
+ `dispatch:${String(dispatch.revision)}:${dispatch.phase}`,
4776
+ activityKind,
4777
+ dispatch.phase,
4778
+ failed ? "failure" : completed ? "success" : "active"
4779
+ );
4780
+ const scene = failed ? "failed" : completed ? "success" : dispatch.phase === "working" ? "running" : "handoff";
4781
+ executionProjection = {
4782
+ scene,
4783
+ tone: failed ? "danger" : completed ? "success" : "running",
4784
+ headline: completed ? messages.execution.completedTitle : failed ? messages.execution.failedTitle : scene === "running" ? messages.execution.runningTitle(target2.label) : messages.execution.dispatchingTitle(identity),
4785
+ action: completed ? messages.execution.resultReturned : target2.source,
4786
+ expandedHeadline: completed ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "running" ? messages.execution.expandedRunningTitle(identity) : messages.execution.dispatchingTitle(identity),
4787
+ expandedAction: completed ? messages.execution.resultReturned : currentTargetContext(),
4788
+ meta: failed ? messages.execution.failedStatus : completed ? messages.execution.completedStatus : scene === "running" ? messages.execution.runningStatus : messages.execution.dispatchingStatus,
4789
+ activity,
4790
+ recentActivities: Object.freeze([activity]),
4791
+ ...startedAt === void 0 ? {} : { startedAt }
4792
+ };
4793
+ } else if (dispatch === null) {
4794
+ executionProjection = void 0;
4795
+ }
4796
+ renderFloatingSurfaceMotion();
4797
+ }
4798
+ function renderExternalControl(status) {
4799
+ const task = status?.task;
4800
+ if (status === void 0 || task === void 0 || executionSuppressed) return;
4801
+ const phase = task.managedPhase;
4802
+ const failed = phase === "failed" || phase === "cleanup-warning";
4803
+ const completed = phase === "completed" || phase === "review-required";
4804
+ const cancelled = phase === "cancelled";
4805
+ if (failed || cancelled) {
4806
+ pendingAgentMotion = void 0;
4807
+ } else if (pendingAgentMotion !== void 0) {
4808
+ startStagedAgentRequest();
4809
+ }
4810
+ if (cancelled) {
4811
+ executionProjection = void 0;
4812
+ observedExecutionStartedAt = void 0;
4813
+ renderFloatingSurfaceMotion();
4814
+ return;
4815
+ }
4816
+ const startedAt = failed || completed ? void 0 : observedExecutionStart();
4817
+ if (failed || completed) observedExecutionStartedAt = void 0;
4818
+ if (phase === "completed" && !showSuccessUntilSettled(`managed:${String(task.revision)}`)) {
4819
+ executionProjection = void 0;
4820
+ renderFloatingSurfaceMotion();
4821
+ return;
4822
+ }
4823
+ const scene = failed ? "failed" : completed ? "success" : phase === "preparing" ? "handoff" : "running";
4824
+ const tone = failed ? "danger" : completed ? "success" : "running";
4825
+ const identity = messages.execution.codex;
4826
+ const target2 = currentTargetSummary();
4827
+ const activityKind = phase === "preparing" ? "prepare" : phase === "auditing" ? "audit" : phase === "validating" ? "check" : phase === "applying" ? "apply" : completed ? "sync" : "unknown";
4828
+ const latestCheck = task.checks.at(-1);
4829
+ const latestFile = task.files.at(-1);
4830
+ const activityDetail = phase === "validating" ? latestCheck?.id : phase === "auditing" || phase === "applying" ? latestFile?.path : void 0;
4831
+ const activity = motionActivity(
4832
+ `managed:${String(task.revision)}:${phase}:${activityDetail ?? ""}`,
4833
+ activityKind,
4834
+ activityDetail,
4835
+ failed ? "failure" : completed ? "success" : "active"
4836
+ );
4837
+ const recentActivities = Object.freeze([
4838
+ ...task.files.slice(-2).map(
4839
+ (file) => motionActivity(
4840
+ `managed-file:${String(task.revision)}:${file.path}`,
4841
+ "patch",
4842
+ file.path,
4843
+ "success"
4844
+ )
4845
+ ),
4846
+ ...task.checks.slice(-2).map(
4847
+ (check) => motionActivity(
4848
+ `managed-check:${String(task.revision)}:${check.id}`,
4849
+ "check",
4850
+ check.id,
4851
+ check.outcome === "passed" ? "success" : "failure"
4852
+ )
4853
+ ),
4854
+ activity
4855
+ ]);
4856
+ executionProjection = {
4857
+ scene,
4858
+ tone,
4859
+ 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),
4860
+ action: completed ? task.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(task.files.length) : target2.source,
4861
+ expandedHeadline: completed ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : messages.execution.expandedRunningTitle(identity),
4862
+ expandedAction: completed ? messages.execution.resultReturned : currentTargetContext(),
4863
+ meta: failed ? messages.execution.failedStatus : completed ? messages.execution.completedStatus : phase === "validating" ? messages.execution.checkingStatus : scene === "handoff" ? messages.execution.dispatchingStatus : messages.execution.runningStatus,
4864
+ activity,
4865
+ recentActivities,
4866
+ ...startedAt === void 0 ? {} : { startedAt }
4867
+ };
4868
+ renderFloatingSurfaceMotion();
4869
+ }
4870
+ function beginAgentRequest(target2, agentCard) {
4871
+ if (!plannerVisible) {
4872
+ return;
4873
+ }
4874
+ clearSuccessSettle(true);
4875
+ const startedAt = beginObservedExecution();
4876
+ executionSuppressed = false;
4877
+ const activity = motionActivity(
4878
+ `dispatch-start:${currentTargetContext()}`,
4879
+ "dispatch",
4880
+ currentTargetContext()
4881
+ );
4882
+ executionProjection = {
4883
+ scene: "agent-charging",
4884
+ tone: "running",
4885
+ headline: messages.execution.receivingTitle,
4886
+ action: currentTargetContext(),
4887
+ expandedHeadline: messages.execution.receivingTitle,
4888
+ expandedAction: currentTargetContext(),
4889
+ meta: messages.execution.receivingStatus,
4890
+ activity,
4891
+ recentActivities: Object.freeze([activity]),
4892
+ startedAt
4893
+ };
4894
+ renderFloatingSurfaceMotion();
4895
+ motionController?.dispatch(target2, agentCard);
4896
+ }
4897
+ function stageAgentRequest(source2, target2) {
4898
+ if (!plannerVisible || source2.disabled) return;
4899
+ pendingAgentMotion = Object.freeze({ source: source2, target: target2 });
4900
+ }
4901
+ function startStagedAgentRequest() {
4902
+ const pending = pendingAgentMotion;
4903
+ pendingAgentMotion = void 0;
4904
+ if (pending === void 0 || !pending.source.isConnected || !pending.target.isConnected) {
4905
+ return;
4906
+ }
4907
+ beginAgentRequest(pending.source, pending.target);
4908
+ }
4104
4909
  function renderEditorStatus(state) {
4105
4910
  currentEditorFeedbackState = state;
4106
4911
  editorFeedback.dataset.state = state;
4107
4912
  editorFeedback.hidden = state === "idle";
4108
4913
  editorFeedback.textContent = state === "opening" ? messages.announcements.editorOpening : state === "success" ? messages.announcements.editorOpened : state === "error" ? messages.announcements.editorFailed : "";
4109
- placeDialog();
4914
+ requestFloatingSurfaceLayout();
4110
4915
  }
4111
4916
  function statusText(target2) {
4112
4917
  if (target2.status === "ready") {
@@ -4241,6 +5046,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4241
5046
  replacement?.setSelectionRange(selectionStart, selectionEnd);
4242
5047
  }
4243
5048
  }
5049
+ requestFloatingSurfaceLayout();
4244
5050
  }
4245
5051
  function updateTargetInstruction(targetId, instruction) {
4246
5052
  currentTargets = currentTargets.map(
@@ -4289,8 +5095,6 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4289
5095
  }
4290
5096
  function showSelectionHighlights(targets) {
4291
5097
  selectionHighlights.replaceChildren();
4292
- activeSelectionRect = targets.find((target2) => target2.active)?.rect;
4293
- currentRect = activeSelectionRect;
4294
5098
  for (const [index, target2] of targets.entries()) {
4295
5099
  const box = createMarkedElement(document, "div");
4296
5100
  box.className = "spotpatch-selection-highlight";
@@ -4304,31 +5108,6 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4304
5108
  box.append(label);
4305
5109
  selectionHighlights.append(box);
4306
5110
  }
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
5111
  }
4333
5112
  function updateContextOverview(summaryText) {
4334
5113
  const summaryMessages = messages.summary;
@@ -4360,13 +5139,12 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4360
5139
  agentPanel.setContextReady(canPreview);
4361
5140
  externalHandoffPanel?.setContextReady(canPreview);
4362
5141
  updateContextOverview(summaryText);
4363
- placeDialog();
5142
+ requestFloatingSurfaceLayout();
4364
5143
  }
4365
5144
  function renderPanelStatus(status) {
4366
5145
  currentStatus = status;
4367
5146
  const selected = status === "selected";
4368
5147
  const previewing = status === "previewing";
4369
- triggerButton.hidden = selected || previewing;
4370
5148
  selectionPanel.hidden = !selected;
4371
5149
  previewPanel.hidden = !previewing;
4372
5150
  reselectButton.hidden = !selected;
@@ -4380,7 +5158,7 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4380
5158
  backButton.hidden = !previewing;
4381
5159
  title.textContent = previewing ? messages.dialog.previewTitle : messages.dialog.editTitle;
4382
5160
  subtitle.textContent = previewing ? messages.dialog.previewSubtitle : messages.dialog.editSubtitle;
4383
- placeDialog();
5161
+ renderFloatingSurfaceMotion();
4384
5162
  }
4385
5163
  function applyMessages() {
4386
5164
  messages = localizer.messages();
@@ -4392,8 +5170,14 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4392
5170
  localeButton.textContent = messages.alternateLocaleName;
4393
5171
  localeButton.title = messages.switchLocale;
4394
5172
  localeButton.setAttribute("aria-label", messages.switchLocale);
5173
+ resetPositionButton.title = messages.floatingSurface.resetPosition;
5174
+ resetPositionButton.setAttribute(
5175
+ "aria-label",
5176
+ messages.floatingSurface.resetPosition
5177
+ );
4395
5178
  closeButton.setAttribute("aria-label", messages.dialog.close);
4396
5179
  closeButton.title = messages.dialog.close;
5180
+ header.title = messages.floatingSurface.dragHandle;
4397
5181
  targetsPanel.setAttribute("aria-label", messages.targets.ariaLabel);
4398
5182
  targetsTitle.textContent = messages.targets.title;
4399
5183
  diagnosticsTitle.textContent = messages.diagnostics.title;
@@ -4429,8 +5213,58 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4429
5213
  contextState.textContent = messages.context.collecting;
4430
5214
  }
4431
5215
  }
4432
- diagnostics.addEventListener("toggle", placeDialog);
5216
+ function announce(message) {
5217
+ liveRegion.textContent = "";
5218
+ liveRegion.textContent = message;
5219
+ }
5220
+ function resetFloatingSurfacePosition() {
5221
+ floatingSurface.reset();
5222
+ announce(messages.floatingSurface.positionReset);
5223
+ }
5224
+ diagnostics.addEventListener("toggle", requestFloatingSurfaceLayout);
4433
5225
  localeButton.addEventListener("click", localizer.toggle);
5226
+ resetPositionButton.addEventListener("click", resetFloatingSurfacePosition);
5227
+ const onAgentRun = () => {
5228
+ stageAgentRequest(agentPanel.runButton, agentPanel.root);
5229
+ };
5230
+ const onExternalSend = () => {
5231
+ if (externalHandoffPanel !== void 0) {
5232
+ stageAgentRequest(externalHandoffPanel.sendButton, externalHandoffPanel.root);
5233
+ }
5234
+ };
5235
+ const onExecutionOpen = () => {
5236
+ if (executionIslandView.canExpand() && !executionIslandView.isExpanded()) {
5237
+ if (motionController === void 0) {
5238
+ executionIslandView.setExpanded(true);
5239
+ floatingSurface.reconcile();
5240
+ } else {
5241
+ motionController.updateLayout(() => {
5242
+ executionIslandView.setExpanded(true);
5243
+ });
5244
+ }
5245
+ return;
5246
+ }
5247
+ clearSuccessSettle(false);
5248
+ executionSuppressed = true;
5249
+ renderFloatingSurfaceMotion();
5250
+ };
5251
+ const onExecutionKeyDown = (event) => {
5252
+ if (event.key !== "Escape" || !executionIslandView.isExpanded()) return;
5253
+ event.preventDefault();
5254
+ event.stopPropagation();
5255
+ if (motionController === void 0) {
5256
+ executionIslandView.setExpanded(false);
5257
+ floatingSurface.reconcile();
5258
+ } else {
5259
+ motionController.updateLayout(() => {
5260
+ executionIslandView.setExpanded(false);
5261
+ });
5262
+ }
5263
+ };
5264
+ agentPanel.runButton.addEventListener("click", onAgentRun);
5265
+ externalHandoffPanel?.sendButton.addEventListener("click", onExternalSend);
5266
+ executionIsland.addEventListener("click", onExecutionOpen);
5267
+ executionIsland.addEventListener("keydown", onExecutionKeyDown);
4434
5268
  const unsubscribeLocale = localizer.subscribe(applyMessages);
4435
5269
  applyMessages();
4436
5270
  return Object.freeze({
@@ -4460,6 +5294,11 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4460
5294
  ...externalHandoffPanel === void 0 ? {} : { externalHandoffPanel },
4461
5295
  renderStatus(status) {
4462
5296
  const inspecting = status === "inspecting";
5297
+ if (status === "idle" || inspecting) {
5298
+ plannerVisible = false;
5299
+ } else {
5300
+ plannerVisible = true;
5301
+ }
4463
5302
  triggerButton.setAttribute("aria-pressed", String(inspecting));
4464
5303
  triggerButton.textContent = inspecting ? messages.trigger.stop : messages.trigger.select;
4465
5304
  renderPanelStatus(status);
@@ -4468,36 +5307,28 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4468
5307
  renderDataFlow(state) {
4469
5308
  currentDataFlowState = state;
4470
5309
  dataFlowPanel.render(state);
4471
- placeDialog();
5310
+ requestFloatingSurfaceLayout();
4472
5311
  },
4473
5312
  showHighlight(rect, label) {
4474
- currentRect = rect;
4475
5313
  highlight.hidden = false;
4476
5314
  highlight.style.transform = `translate(${String(rect.x)}px, ${String(rect.y)}px)`;
4477
5315
  highlight.style.width = `${String(rect.width)}px`;
4478
5316
  highlight.style.height = `${String(rect.height)}px`;
4479
5317
  highlightLabel.textContent = label;
4480
5318
  targetLabel.textContent = label;
4481
- placeDialog();
4482
5319
  },
4483
5320
  hideHighlight() {
4484
- currentRect = activeSelectionRect;
4485
5321
  highlight.hidden = true;
4486
5322
  highlightLabel.textContent = "";
4487
- placeDialog();
4488
5323
  },
4489
5324
  showSelectionHighlights,
4490
5325
  hideSelectionHighlights() {
4491
5326
  selectionHighlights.replaceChildren();
4492
- activeSelectionRect = void 0;
4493
- currentRect = void 0;
4494
- placeDialog();
4495
5327
  },
4496
5328
  showSelection(summaryText, canOpenEditor, canPreview) {
4497
5329
  updateSelection(summaryText, canOpenEditor, canPreview);
4498
- triggerButton.hidden = true;
4499
- dialog.hidden = false;
4500
- placeDialog();
5330
+ plannerVisible = true;
5331
+ renderPanelStatus(currentStatus === "previewing" ? "previewing" : "selected");
4501
5332
  },
4502
5333
  updateSelection,
4503
5334
  renderTargets,
@@ -4509,8 +5340,9 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4509
5340
  externalHandoffPanel?.setContextReady(enabled);
4510
5341
  },
4511
5342
  hideSelection() {
4512
- dialog.hidden = true;
4513
- triggerButton.hidden = false;
5343
+ plannerVisible = false;
5344
+ executionSuppressed = false;
5345
+ executionProjection = void 0;
4514
5346
  targetList.replaceChildren();
4515
5347
  currentTargets = [];
4516
5348
  currentMaximum = 0;
@@ -4536,14 +5368,17 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4536
5368
  agentPanel.setEditingEnabled(true);
4537
5369
  agentPanel.resetJob();
4538
5370
  dataFlowPanel.resetView();
5371
+ renderFloatingSurfaceMotion();
4539
5372
  },
4540
5373
  hideSelectionTemporarily() {
4541
- dialog.hidden = true;
4542
- triggerButton.hidden = false;
5374
+ plannerVisible = false;
5375
+ executionSuppressed = false;
5376
+ executionProjection = void 0;
5377
+ renderFloatingSurfaceMotion();
4543
5378
  },
4544
5379
  showPreview(prompt) {
4545
5380
  promptOutput.textContent = prompt;
4546
- placeDialog();
5381
+ requestFloatingSurfaceLayout();
4547
5382
  },
4548
5383
  readAgentSelection() {
4549
5384
  return agentPanel.readSelection();
@@ -4567,25 +5402,86 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4567
5402
  previewButton.disabled = !enabled || !currentCanPreview;
4568
5403
  externalHandoffPanel?.setContextReady(enabled && currentCanPreview);
4569
5404
  agentPanel.setEditingEnabled(enabled);
4570
- placeDialog();
5405
+ requestFloatingSurfaceLayout();
4571
5406
  },
4572
5407
  renderAgentCapability(state, message, capabilitySnapshot, errorCode) {
4573
5408
  agentPanel.renderCapability(state, message, capabilitySnapshot, errorCode);
5409
+ if (state === "error") {
5410
+ pendingAgentMotion = void 0;
5411
+ renderFloatingSurfaceMotion();
5412
+ }
4574
5413
  const agentReady = state === "ready" && capabilitySnapshot?.state === "agent-ready";
4575
5414
  previewButton.classList.toggle("spotpatch-primary", !agentReady);
4576
- placeDialog();
5415
+ requestFloatingSurfaceLayout();
4577
5416
  },
4578
5417
  renderAgentWorkspaceHealth(state, snapshot, errorCode) {
4579
5418
  agentPanel.renderWorkspaceHealth(state, snapshot, errorCode);
4580
- placeDialog();
5419
+ if (state === "blocked") {
5420
+ pendingAgentMotion = void 0;
5421
+ renderFloatingSurfaceMotion();
5422
+ }
5423
+ requestFloatingSurfaceLayout();
4581
5424
  },
4582
5425
  renderAgentJob(snapshot, result, activities, errorCode) {
4583
5426
  agentPanel.renderJob(snapshot, result, activities, errorCode);
4584
- placeDialog();
5427
+ if (pendingAgentMotion !== void 0 && snapshot.status !== "failed" && snapshot.status !== "cancelled") {
5428
+ startStagedAgentRequest();
5429
+ }
5430
+ if (snapshot.status === "failed" || snapshot.status === "cancelled") {
5431
+ pendingAgentMotion = void 0;
5432
+ }
5433
+ const successful = snapshot.status === "awaiting-review" || snapshot.status === "applied" || snapshot.status === "completed" || snapshot.status === "reverted";
5434
+ const failed = snapshot.status === "failed";
5435
+ const cancelled = snapshot.status === "cancelled";
5436
+ const terminalSuccess = snapshot.status === "applied" || snapshot.status === "completed" || snapshot.status === "reverted";
5437
+ const scene = successful ? "success" : failed ? "failed" : snapshot.status === "queued" || snapshot.status === "preparing" ? "handoff" : "running";
5438
+ if (!executionSuppressed) {
5439
+ if (terminalSuccess && !showSuccessUntilSettled(`job:${snapshot.jobId}:${snapshot.status}`)) {
5440
+ executionProjection = void 0;
5441
+ renderFloatingSurfaceMotion();
5442
+ requestFloatingSurfaceLayout();
5443
+ return;
5444
+ }
5445
+ const identity = snapshot.providerLabel;
5446
+ const target2 = currentTargetSummary();
5447
+ const projectedActivities = Object.freeze(activities.map(projectAgentActivity));
5448
+ const latestActivity = activities.at(-1);
5449
+ 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";
5450
+ const phaseActivity = motionActivity(
5451
+ `job-phase:${snapshot.jobId}:${snapshot.status}:${snapshot.updatedAt}`,
5452
+ phaseKind,
5453
+ void 0,
5454
+ successful ? "success" : failed ? "failure" : "active"
5455
+ );
5456
+ const activity = snapshot.status === "running" && latestActivity !== void 0 ? projectAgentActivity(latestActivity) : phaseActivity;
5457
+ executionProjection = cancelled ? void 0 : {
5458
+ scene,
5459
+ tone: successful ? "success" : failed ? "danger" : "running",
5460
+ headline: successful ? messages.execution.completedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : snapshot.status === "validating" ? messages.execution.activityAction(
5461
+ "check",
5462
+ latestActivity?.detail
5463
+ ) : snapshot.status === "applying" || snapshot.status === "reverting" ? messages.execution.activityAction(phaseKind) : messages.execution.runningTitle(target2.label),
5464
+ action: successful ? result === void 0 || result.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(result.files.length) : latestActivity?.detail ?? target2.source,
5465
+ expandedHeadline: successful ? messages.execution.expandedCompletedTitle : failed ? messages.execution.failedTitle : scene === "handoff" ? messages.execution.dispatchingTitle(identity) : messages.execution.expandedRunningTitle(identity),
5466
+ expandedAction: successful ? result === void 0 || result.files.length === 0 ? messages.execution.resultReturned : messages.execution.resultSummary(result.files.length) : currentTargetContext(),
5467
+ meta: successful ? messages.execution.completedStatus : failed ? messages.execution.failedStatus : snapshot.status === "validating" ? messages.execution.checkingStatus : scene === "handoff" ? messages.execution.dispatchingStatus : messages.execution.runningStatus,
5468
+ activity,
5469
+ recentActivities: projectedActivities.length === 0 ? Object.freeze([phaseActivity]) : projectedActivities,
5470
+ startedAt: snapshot.createdAt
5471
+ };
5472
+ }
5473
+ renderFloatingSurfaceMotion();
5474
+ requestFloatingSurfaceLayout();
4585
5475
  },
4586
5476
  resetAgentJob() {
4587
5477
  agentPanel.resetJob();
4588
- placeDialog();
5478
+ observedExecutionStartedAt = void 0;
5479
+ if (executionProjection !== void 0) {
5480
+ executionProjection = void 0;
5481
+ executionSuppressed = false;
5482
+ renderFloatingSurfaceMotion();
5483
+ }
5484
+ requestFloatingSurfaceLayout();
4589
5485
  },
4590
5486
  focusTargetInstruction(targetId) {
4591
5487
  instructionInput(targetId)?.focus({ preventScroll: true });
@@ -4593,21 +5489,27 @@ function createRuntimeView(document, shortcut, ai = Object.freeze({ enabled: fal
4593
5489
  focusPrompt() {
4594
5490
  promptOutput.focus({ preventScroll: true });
4595
5491
  },
4596
- announce(message) {
4597
- liveRegion.textContent = "";
4598
- liveRegion.textContent = message;
4599
- },
5492
+ announce,
4600
5493
  locale: localizer.locale,
4601
5494
  messages: localizer.messages,
4602
5495
  agentWorkspaceConsentGranted: agentPanel.workspaceConsentGranted,
4603
5496
  subscribeLocale: localizer.subscribe,
4604
5497
  dispose() {
4605
- diagnostics.removeEventListener("toggle", placeDialog);
5498
+ diagnostics.removeEventListener("toggle", requestFloatingSurfaceLayout);
4606
5499
  localeButton.removeEventListener("click", localizer.toggle);
5500
+ resetPositionButton.removeEventListener("click", resetFloatingSurfacePosition);
5501
+ agentPanel.runButton.removeEventListener("click", onAgentRun);
5502
+ externalHandoffPanel?.sendButton.removeEventListener("click", onExternalSend);
5503
+ executionIsland.removeEventListener("click", onExecutionOpen);
5504
+ executionIsland.removeEventListener("keydown", onExecutionKeyDown);
4607
5505
  unsubscribeLocale();
4608
5506
  dataFlowPanel.dispose();
4609
5507
  externalHandoffPanel?.dispose();
4610
5508
  agentPanel.dispose();
5509
+ motionController?.dispose();
5510
+ executionIslandView.dispose();
5511
+ clearSuccessSettle(true);
5512
+ floatingSurface.dispose();
4611
5513
  host.remove();
4612
5514
  }
4613
5515
  });
@@ -4674,17 +5576,33 @@ var import_shared8 = require("@spotpatch/shared");
4674
5576
  function capabilityKey(providerProfileId, modelProfileId) {
4675
5577
  return `${providerProfileId}:${modelProfileId}`;
4676
5578
  }
5579
+ var TOOL_ACTIVITY_KIND = Object.freeze({
5580
+ list_files: "discover",
5581
+ search_text: "search",
5582
+ read_file: "read",
5583
+ replace_text: "patch",
5584
+ apply_patch: "patch",
5585
+ run_check: "check"
5586
+ });
5587
+ function toolActivityKind(toolName) {
5588
+ return TOOL_ACTIVITY_KIND[toolName] ?? "unknown";
5589
+ }
4677
5590
  function activityFromEvent(event) {
4678
5591
  if (event.type === "tool") {
5592
+ const detail = event.data.relativePath ?? event.data.checkLabel;
4679
5593
  return Object.freeze({
4680
5594
  key: `tool:${String(event.data.turn)}:${event.data.toolCallId}`,
4681
- label: `${event.data.toolName} \xB7 ${event.data.state}`,
5595
+ kind: toolActivityKind(event.data.toolName),
5596
+ ...detail === void 0 ? {} : { detail },
5597
+ label: `${event.data.toolName}${detail === void 0 ? "" : ` \xB7 ${detail}`} \xB7 ${event.data.state}`,
4682
5598
  state: event.data.state === "started" ? "active" : event.data.state === "succeeded" ? "success" : "failure"
4683
5599
  });
4684
5600
  }
4685
5601
  if (event.type === "check") {
4686
5602
  return Object.freeze({
4687
5603
  key: `check:${event.data.result.checkId}:${String(event.sequence)}`,
5604
+ kind: "check",
5605
+ detail: event.data.result.label,
4688
5606
  label: `${event.data.result.label} \xB7 ${event.data.result.status}`,
4689
5607
  state: event.data.result.status === "passed" ? "success" : event.data.result.status === "failed" || event.data.result.status === "timed-out" ? "failure" : "info"
4690
5608
  });
@@ -6258,13 +7176,13 @@ function installWritableDataProperty(target2, key, replacement, original = ownPr
6258
7176
  }
6259
7177
  };
6260
7178
  }
6261
- function recordWithoutAffectingHost(record2) {
7179
+ function recordWithoutAffectingHost(record3) {
6262
7180
  try {
6263
- record2();
7181
+ record3();
6264
7182
  } catch {
6265
7183
  }
6266
7184
  }
6267
- function createDataFlowRuntime(config, target2 = globalThis) {
7185
+ function createDataFlowRuntime(config, target2 = globalThis, policy = {}) {
6268
7186
  const componentRegistry = /* @__PURE__ */ new WeakMap();
6269
7187
  const xhrMetadata = /* @__PURE__ */ new WeakMap();
6270
7188
  const store = createRingStore(config.limits, target2);
@@ -6296,7 +7214,11 @@ function createDataFlowRuntime(config, target2 = globalThis) {
6296
7214
  if (isSpotPatchInternalUrl(
6297
7215
  rawUrl,
6298
7216
  target2.location?.href ?? "http://spotpatch.invalid/"
6299
- )) {
7217
+ ) || policy.shouldObserveFetch?.(
7218
+ input,
7219
+ init,
7220
+ target2.location?.href ?? "http://spotpatch.invalid/"
7221
+ ) === false) {
6300
7222
  return;
6301
7223
  }
6302
7224
  store.add(
@@ -6352,7 +7274,10 @@ function createDataFlowRuntime(config, target2 = globalThis) {
6352
7274
  if (metadata === void 0 || isSpotPatchInternalUrl(
6353
7275
  metadata.url,
6354
7276
  target2.location?.href ?? "http://spotpatch.invalid/"
6355
- )) {
7277
+ ) || policy.shouldObserveXhr?.(
7278
+ metadata.url,
7279
+ target2.location?.href ?? "http://spotpatch.invalid/"
7280
+ ) === false) {
6356
7281
  return;
6357
7282
  }
6358
7283
  const frame = currentRequestFrame;
@@ -6526,9 +7451,9 @@ function createDataFlowRuntime(config, target2 = globalThis) {
6526
7451
  });
6527
7452
  return runtime;
6528
7453
  }
6529
- function installDataFlowPrelude(config, target2 = globalThis) {
7454
+ function installDataFlowPrelude(config, target2 = globalThis, policy = {}) {
6530
7455
  if (!config.enabled) return void 0;
6531
- const runtime = target2[RUNTIME_KEY] ?? createDataFlowRuntime(config, target2);
7456
+ const runtime = target2[RUNTIME_KEY] ?? createDataFlowRuntime(config, target2, policy);
6532
7457
  target2[RUNTIME_KEY] = runtime;
6533
7458
  return runtime;
6534
7459
  }