@replohq/sandbox-runtime 1.24.1 → 1.24.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/buildMetadata.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sandbox-runtime",
3
- "version": "1.24.1",
3
+ "version": "1.24.3",
4
4
  "branch": "HEAD",
5
- "commit": "043ae54a4a52039285aac996cc7b2b67a922dfdd",
6
- "builtAt": "2026-11-03T00:13:53.913Z"
5
+ "commit": "e1eeef82df82036e9d623fccf943736d34fb994d",
6
+ "builtAt": "2026-11-04T19:25:11.953Z"
7
7
  };
@@ -3,7 +3,7 @@ import type { CommentOverlayAnchorStatus, CommentOverlayPin, CommentOverlayRect
3
3
  import type { DisplayMode } from "../_vendor/schemas/generated/selectMode";
4
4
  import type { ElementSource } from "./capture";
5
5
  export declare const OVERLAY_Z_INDEX = 2147483645;
6
- export declare const HIGHLIGHT_BACKGROUND = "rgba(250, 204, 21, 0.28)";
6
+ export declare const HIGHLIGHT_BACKGROUND = "color-mix(in srgb, var(--replo-comment-accent) 12%, transparent)";
7
7
  export declare const BADGE_SIZE_PX = 32;
8
8
  export declare const RERESOLVE_DEBOUNCE_MS = 250;
9
9
  export type CommentsOverlayCallbacks = {
@@ -20,6 +20,7 @@ export type CommentsOverlayCallbacks = {
20
20
  onComposeFromSelection: (input: {
21
21
  anchor: CommentAnchorElement;
22
22
  }) => void;
23
+ /** Esc, or a click on empty page, closed the pending pick or open thread. */
23
24
  onPickCancelled: () => void;
24
25
  /** C or Esc pressed in the frame; the host owns the mode. */
25
26
  onModeShortcut: (input: {
@@ -2,8 +2,9 @@ import { captureAnchor, getAnchorElement } from "./capture";
2
2
  import { isResolutionVisible, resolveAnchor } from "./resolve";
3
3
  import { OVERLAY_ROOT_ATTRIBUTE } from "./textQuote";
4
4
  const OVERLAY_Z_INDEX = 2147483645;
5
- const HIGHLIGHT_BACKGROUND = "rgba(250, 204, 21, 0.28)";
6
- const QUOTE_UNDERLINE = "2px solid rgba(250, 204, 21, 0.9)";
5
+ const HIGHLIGHT_BACKGROUND = "color-mix(in srgb, var(--replo-comment-accent) 12%, transparent)";
6
+ const SOFT_OUTLINE_COLOR = "color-mix(in srgb, var(--replo-comment-accent) 55%, transparent)";
7
+ const HIGHLIGHT_OUTLINE = "1.5px solid var(--replo-comment-accent)";
7
8
  const BADGE_SIZE_PX = 32;
8
9
  const BADGE_HIT_TARGET_PX = 44;
9
10
  const BADGE_HIT_TARGET_WIDTH_PX = 64;
@@ -11,7 +12,7 @@ const COMPACT_GUTTER_MAX_WIDTH_PX = 600;
11
12
  const RERESOLVE_DEBOUNCE_MS = 250;
12
13
  const SELECTION_PROMPT_DEBOUNCE_MS = 150;
13
14
  const TEXT_PICK_CLICK_SUPPRESS_MS = 750;
14
- const HOVER_OUTLINE_STYLE = "position:absolute;pointer-events:none;box-sizing:border-box;border:1px solid rgba(37,99,235,.45);background:rgba(37,99,235,.025);border-radius:3px";
15
+ const HOVER_OUTLINE_STYLE = `position:absolute;pointer-events:none;box-sizing:border-box;border:${HIGHLIGHT_OUTLINE};background:transparent;border-radius:4px`;
15
16
  const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
16
17
  const EDITABLE_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
17
18
  function getReportedStatus(resolution) {
@@ -31,11 +32,8 @@ function createCommentsOverlay(options) {
31
32
  let pins = [];
32
33
  let resolutions = /* @__PURE__ */ new Map();
33
34
  const boundElements = /* @__PURE__ */ new Map();
34
- let focusedThreadId = null;
35
+ let selection = null;
35
36
  let hoveredBadgeThreadId = null;
36
- let draftAnchor = null;
37
- let draftResolution = null;
38
- let draftElement = null;
39
37
  let statusesKey = "";
40
38
  let hoveredElement = null;
41
39
  let hoverOutline = null;
@@ -52,7 +50,7 @@ function createCommentsOverlay(options) {
52
50
  let isDestroyed = false;
53
51
  const root = document.createElement("div");
54
52
  root.setAttribute(OVERLAY_ROOT_ATTRIBUTE, "true");
55
- root.style.cssText = `position:fixed;inset:0;z-index:${OVERLAY_Z_INDEX};pointer-events:none;overflow:hidden`;
53
+ root.style.cssText = `position:fixed;inset:0;z-index:${OVERLAY_Z_INDEX};pointer-events:none;overflow:hidden;--replo-comment-accent:light-dark(#7861bf,#b09be8);--replo-comment-surface:light-dark(#f0eafa,#372e4d);--replo-comment-text:light-dark(#7058b4,#b09be8);--replo-comment-on-accent:light-dark(#fff,#20182f)`;
56
54
  document.documentElement.append(root);
57
55
  const isInOverlay = (node) => {
58
56
  return node instanceof Node && root.contains(node);
@@ -66,22 +64,22 @@ function createCommentsOverlay(options) {
66
64
  const isPageElement = (node) => {
67
65
  return node instanceof Element && node !== document.body && node !== document.documentElement && !isInOverlay(node);
68
66
  };
69
- const isEditableSelection = (selection) => {
70
- const node = selection.anchorNode;
67
+ const isEditableSelection = (selection2) => {
68
+ const node = selection2.anchorNode;
71
69
  const element = node instanceof Element ? node : node?.parentElement;
72
70
  return Boolean(
73
71
  element?.closest(EDITABLE_SELECTOR) || document.activeElement?.matches(EDITABLE_SELECTOR)
74
72
  );
75
73
  };
76
74
  const getUsableSelectionRange = () => {
77
- const selection = window.getSelection();
78
- if (!selection || selection.isCollapsed || selection.rangeCount === 0) {
75
+ const selection2 = window.getSelection();
76
+ if (!selection2 || selection2.isCollapsed || selection2.rangeCount === 0) {
79
77
  return null;
80
78
  }
81
- if (isEditableSelection(selection)) {
79
+ if (isEditableSelection(selection2)) {
82
80
  return null;
83
81
  }
84
- const range = selection.getRangeAt(0);
82
+ const range = selection2.getRangeAt(0);
85
83
  if (!range.toString().trim() || isInOverlay(range.commonAncestorContainer)) {
86
84
  return null;
87
85
  }
@@ -132,11 +130,12 @@ function createCommentsOverlay(options) {
132
130
  }
133
131
  }
134
132
  resolutions = next;
135
- draftResolution = draftAnchor ? resolveAnchor(draftAnchor, document, {
136
- previous: draftElement,
137
- getSource: cachedGetSource
138
- }) : null;
139
- draftElement = draftResolution?.element ?? null;
133
+ if (selection?.kind === "draft") {
134
+ selection.resolution = resolveAnchor(selection.anchor, document, {
135
+ previous: selection.resolution?.element ?? null,
136
+ getSource: cachedGetSource
137
+ });
138
+ }
140
139
  const statuses = getElementAnchors().map(({ pin }) => {
141
140
  return {
142
141
  threadId: pin.threadId,
@@ -157,7 +156,7 @@ function createCommentsOverlay(options) {
157
156
  resolveTimer = setTimeout(resolveAll, RERESOLVE_DEBOUNCE_MS);
158
157
  };
159
158
  const paintHighlight = (resolution, threadId) => {
160
- const isQuote = Boolean(resolution.range);
159
+ const isOutlined = threadId === null || threadId === hoveredBadgeThreadId || selection?.kind === "thread" && threadId === selection.threadId;
161
160
  for (const rect of getResolutionRects(resolution)) {
162
161
  if (rect.width <= 0 || rect.height <= 0 || rect.bottom < 0 || rect.top > window.innerHeight) {
163
162
  continue;
@@ -168,10 +167,7 @@ function createCommentsOverlay(options) {
168
167
  } else {
169
168
  highlight.dataset.commentDraft = "true";
170
169
  }
171
- highlight.style.cssText = `position:absolute;pointer-events:none;background:${HIGHLIGHT_BACKGROUND};border-radius:2px;box-sizing:border-box`;
172
- if (isQuote) {
173
- highlight.style.borderBottom = QUOTE_UNDERLINE;
174
- }
170
+ highlight.style.cssText = `position:absolute;pointer-events:none;background:${threadId ? HIGHLIGHT_BACKGROUND : "transparent"};border:${isOutlined ? HIGHLIGHT_OUTLINE : "0"};border-radius:4px;box-sizing:border-box`;
175
171
  highlight.style.left = `${rect.left}px`;
176
172
  highlight.style.top = `${rect.top}px`;
177
173
  highlight.style.width = `${rect.width}px`;
@@ -180,7 +176,7 @@ function createCommentsOverlay(options) {
180
176
  }
181
177
  };
182
178
  const isHighlightShown = (threadId) => {
183
- return mode === "comment" || threadId === focusedThreadId || threadId === hoveredBadgeThreadId;
179
+ return mode === "comment" || selection?.kind === "thread" && threadId === selection.threadId || threadId === hoveredBadgeThreadId;
184
180
  };
185
181
  const createIcon = (size, path) => {
186
182
  const icon = document.createElementNS(SVG_NAMESPACE, "svg");
@@ -320,7 +316,7 @@ function createCommentsOverlay(options) {
320
316
  };
321
317
  for (const pin of pins) {
322
318
  const resolution = resolutions.get(pin.threadId);
323
- const isFocused = pin.threadId === focusedThreadId;
319
+ const isFocused = selection?.kind === "thread" && pin.threadId === selection.threadId;
324
320
  if (!resolution || pin.status === "resolved" && !isFocused || !isResolutionVisible(resolution)) {
325
321
  continue;
326
322
  }
@@ -350,12 +346,16 @@ function createCommentsOverlay(options) {
350
346
  badge.style.top = `${position.top}px`;
351
347
  const isActive = isFocused || pin.threadId === hoveredBadgeThreadId;
352
348
  const dot = document.createElement("span");
353
- dot.style.cssText = `display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;min-width:48px;height:${BADGE_SIZE_PX}px;padding:0 6px;border-radius:8px;border:1px solid rgba(0,0,0,.12);background:#ffeb80;color:#171717;box-shadow:0 1px 3px rgba(0,0,0,.12);font:600 14px/1 system-ui,-apple-system,sans-serif;font-variant-numeric:tabular-nums;white-space:nowrap`;
349
+ dot.style.cssText = `display:flex;align-items:center;justify-content:center;gap:6px;box-sizing:border-box;min-width:48px;height:${BADGE_SIZE_PX}px;padding:0 6px;border-radius:999px;border:1px solid ${SOFT_OUTLINE_COLOR};background:var(--replo-comment-surface);color:var(--replo-comment-text);box-shadow:0 1px 3px rgba(0,0,0,.08);font:600 14px/1 system-ui,-apple-system,sans-serif;font-variant-numeric:tabular-nums;white-space:nowrap`;
354
350
  if (isCompact) {
355
351
  dot.style.marginRight = "3px";
356
352
  }
357
353
  if (isActive) {
358
- dot.style.borderColor = "rgba(0,0,0,.4)";
354
+ dot.style.borderColor = "var(--replo-comment-accent)";
355
+ }
356
+ if (isFocused) {
357
+ dot.style.background = "var(--replo-comment-accent)";
358
+ dot.style.color = "var(--replo-comment-on-accent)";
359
359
  }
360
360
  if (resolution.status === "ambiguous") {
361
361
  dot.style.borderStyle = "dashed";
@@ -380,8 +380,8 @@ function createCommentsOverlay(options) {
380
380
  child.remove();
381
381
  }
382
382
  }
383
- if (draftResolution && isResolutionVisible(draftResolution)) {
384
- paintHighlight(draftResolution, null);
383
+ if (selection?.kind === "draft" && selection.resolution && isResolutionVisible(selection.resolution)) {
384
+ paintHighlight(selection.resolution, null);
385
385
  }
386
386
  paintBadges();
387
387
  paintHover();
@@ -409,21 +409,32 @@ function createCommentsOverlay(options) {
409
409
  const anchor = captureAnchor(target, point);
410
410
  const range = "tagName" in target ? void 0 : target;
411
411
  const rect = (range ?? element).getBoundingClientRect();
412
- draftAnchor = anchor;
413
- draftElement = element;
414
- draftResolution = {
415
- status: "attached",
416
- element,
417
- ...range ? { range } : {}
412
+ selection = {
413
+ kind: "draft",
414
+ anchor,
415
+ resolution: {
416
+ status: "attached",
417
+ element,
418
+ ...range ? { range } : {}
419
+ }
418
420
  };
419
421
  hoveredElement = null;
420
422
  void finishAnchor(anchor).then((prepared) => report(prepared, rect));
421
423
  schedulePaint();
422
424
  };
423
425
  const clearDraft = () => {
424
- draftAnchor = null;
425
- draftResolution = null;
426
- draftElement = null;
426
+ if (selection?.kind === "draft") {
427
+ selection = null;
428
+ }
429
+ };
430
+ const dismissSelection = () => {
431
+ if (!selection) {
432
+ return false;
433
+ }
434
+ selection = null;
435
+ options.onPickCancelled();
436
+ schedulePaint();
437
+ return true;
427
438
  };
428
439
  const refreshSelectionPrompt = () => {
429
440
  selectionTimer = null;
@@ -579,7 +590,7 @@ function createCommentsOverlay(options) {
579
590
  return;
580
591
  }
581
592
  if (!isPageElement(target)) {
582
- options.onModeShortcut({ mode: "browse" });
593
+ dismissSelection();
583
594
  return;
584
595
  }
585
596
  pick(target, { x: event.clientX, y: event.clientY }, reportPicked);
@@ -595,16 +606,12 @@ function createCommentsOverlay(options) {
595
606
  if (event.key !== "Escape") {
596
607
  return;
597
608
  }
598
- if (mode === "comment" || draftAnchor) {
609
+ if (mode === "comment" || selection?.kind === "draft") {
599
610
  if (mode === "comment") {
600
611
  event.preventDefault();
601
612
  event.stopImmediatePropagation();
602
613
  }
603
- if (draftAnchor) {
604
- clearDraft();
605
- options.onPickCancelled();
606
- schedulePaint();
607
- } else {
614
+ if (!dismissSelection()) {
608
615
  options.onModeShortcut({ mode: "browse" });
609
616
  }
610
617
  return;
@@ -713,7 +720,9 @@ function createCommentsOverlay(options) {
713
720
  mode = nextMode;
714
721
  selectionPrompt = null;
715
722
  hoveredElement = null;
716
- focusedThreadId = null;
723
+ if (selection?.kind === "thread") {
724
+ selection = null;
725
+ }
717
726
  setCursor(nextMode, previousMode);
718
727
  if (previousMode === "comment") {
719
728
  window.getSelection()?.removeAllRanges();
@@ -731,7 +740,7 @@ function createCommentsOverlay(options) {
731
740
  resolveAll();
732
741
  },
733
742
  focusThread: (threadId) => {
734
- focusedThreadId = threadId;
743
+ selection = { kind: "thread", threadId };
735
744
  const resolution = resolutions.get(threadId);
736
745
  const target = resolution?.range?.startContainer.parentElement ?? resolution?.element ?? null;
737
746
  if (resolution && isResolutionVisible(resolution)) {
@@ -748,7 +757,11 @@ function createCommentsOverlay(options) {
748
757
  schedulePaint();
749
758
  },
750
759
  setDraftAnchor: (anchor) => {
751
- draftAnchor = anchor?.type === "element" ? anchor : null;
760
+ if (anchor?.type === "element") {
761
+ selection = { kind: "draft", anchor, resolution: null };
762
+ } else {
763
+ clearDraft();
764
+ }
752
765
  resolveAll();
753
766
  },
754
767
  refresh: resolveAll,
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sandbox-runtime",
3
- "version": "1.24.1",
3
+ "version": "1.24.3",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sandbox-runtime",
6
- "version": "1.24.1",
6
+ "version": "1.24.3",
7
7
  "branch": "HEAD",
8
- "commit": "043ae54a4a52039285aac996cc7b2b67a922dfdd",
9
- "builtAt": "2026-11-03T00:13:53.913Z"
8
+ "commit": "e1eeef82df82036e9d623fccf943736d34fb994d",
9
+ "builtAt": "2026-11-04T19:25:11.953Z"
10
10
  },
11
11
  "description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",