@replohq/sandbox-runtime 1.24.3 → 1.24.4

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.
@@ -168,6 +168,10 @@ export declare const hostToCommentsOverlayMethodConfig: {
168
168
  }, z.core.$strict>;
169
169
  outputSchema: z.ZodVoid;
170
170
  };
171
+ clearThreadFocus: {
172
+ inputSchema: z.ZodUndefined;
173
+ outputSchema: z.ZodVoid;
174
+ };
171
175
  clearDraft: {
172
176
  inputSchema: z.ZodUndefined;
173
177
  outputSchema: z.ZodVoid;
@@ -40,6 +40,10 @@ var hostToCommentsOverlayMethodConfig = {
40
40
  inputSchema: z.strictObject({ threadId: z.string() }),
41
41
  outputSchema: z.void()
42
42
  },
43
+ clearThreadFocus: {
44
+ inputSchema: z.undefined(),
45
+ outputSchema: z.void()
46
+ },
43
47
  clearDraft: {
44
48
  inputSchema: z.undefined(),
45
49
  outputSchema: z.void()
package/buildMetadata.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sandbox-runtime",
3
- "version": "1.24.3",
3
+ "version": "1.24.4",
4
4
  "branch": "HEAD",
5
- "commit": "e1eeef82df82036e9d623fccf943736d34fb994d",
6
- "builtAt": "2026-11-04T19:25:11.953Z"
5
+ "commit": "49a643c73e7608adcefebcd5c5e27c3e6d0c05a9",
6
+ "builtAt": "2026-11-04T22:05:21.699Z"
7
7
  };
@@ -64,6 +64,9 @@ function startCommentsOverlay({
64
64
  focusThread: ({ threadId }) => {
65
65
  getOverlay().focusThread(threadId);
66
66
  },
67
+ clearThreadFocus: () => {
68
+ overlay?.clearThreadFocus();
69
+ },
67
70
  clearDraft: () => {
68
71
  overlay?.clearDraft();
69
72
  },
@@ -32,7 +32,10 @@ export declare function getElementSource(element: Element): ElementSource | null
32
32
  * none. Inside a shadow root it includes nested shadow roots' text.
33
33
  */
34
34
  export declare function getTextPreview(element: Element): string;
35
- /** The element an anchor for this click target or selection describes. */
35
+ /**
36
+ * The element an anchor for this click target or selection describes; null
37
+ * when a click lands in layout spacing or a selection isn't in an element.
38
+ */
36
39
  export declare function getAnchorElement(target: Element | Range): Element | null;
37
40
  /**
38
41
  * Builds an element anchor (D-13) for a clicked element or a text selection.
@@ -168,8 +168,14 @@ function createRootAnchorSelectors(element) {
168
168
  selector: ownSemanticSelector ?? findUniqueSelector(element) ?? createNthChildSelector(element)
169
169
  };
170
170
  }
171
+ const LAYOUT_SELECTOR = "[data-comment-layout]";
171
172
  function getAnchorElement(target) {
172
- const picked = isElement(target) ? target : rangeContainer(target);
173
+ if (isElement(target)) {
174
+ const element = snapToAnchorElement(target);
175
+ const isLayout = element.matches(LAYOUT_SELECTOR) || element.querySelector(LAYOUT_SELECTOR) !== null;
176
+ return isLayout ? null : element;
177
+ }
178
+ const picked = rangeContainer(target);
173
179
  return picked ? snapToAnchorElement(picked) : null;
174
180
  }
175
181
  function captureAnchor(target, point) {
@@ -37,6 +37,7 @@ export type CommentsOverlay = {
37
37
  setMode: (mode: DisplayMode) => void;
38
38
  setPins: (pins: CommentOverlayPin[]) => void;
39
39
  focusThread: (threadId: string) => void;
40
+ clearThreadFocus: () => void;
40
41
  clearDraft: () => void;
41
42
  setDraftAnchor: (anchor: CommentAnchor | null) => void;
42
43
  /** Re-resolves every anchor now, e.g. after symbolication results arrive. */
@@ -12,6 +12,7 @@ const COMPACT_GUTTER_MAX_WIDTH_PX = 600;
12
12
  const RERESOLVE_DEBOUNCE_MS = 250;
13
13
  const SELECTION_PROMPT_DEBOUNCE_MS = 150;
14
14
  const TEXT_PICK_CLICK_SUPPRESS_MS = 750;
15
+ const PENDING_SCROLL_MS = 1e4;
15
16
  const HOVER_OUTLINE_STYLE = `position:absolute;pointer-events:none;box-sizing:border-box;border:${HIGHLIGHT_OUTLINE};background:transparent;border-radius:4px`;
16
17
  const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
17
18
  const EDITABLE_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
@@ -34,6 +35,7 @@ function createCommentsOverlay(options) {
34
35
  const boundElements = /* @__PURE__ */ new Map();
35
36
  let selection = null;
36
37
  let hoveredBadgeThreadId = null;
38
+ let pendingScroll = null;
37
39
  let statusesKey = "";
38
40
  let hoveredElement = null;
39
41
  let hoverOutline = null;
@@ -105,6 +107,19 @@ function createCommentsOverlay(options) {
105
107
  return pin.anchor.type === "element" ? [{ pin, anchor: pin.anchor }] : [];
106
108
  });
107
109
  };
110
+ const scrollToThread = (threadId) => {
111
+ const resolution = resolutions.get(threadId);
112
+ if (!resolution || !isResolutionVisible(resolution)) {
113
+ return false;
114
+ }
115
+ const target = resolution.range?.startContainer.parentElement ?? resolution.element;
116
+ const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false;
117
+ target?.scrollIntoView?.({
118
+ block: "center",
119
+ behavior: reduceMotion ? "auto" : "smooth"
120
+ });
121
+ return true;
122
+ };
108
123
  const resolveAll = () => {
109
124
  resolveTimer = null;
110
125
  observeShadowRoots(document);
@@ -130,6 +145,12 @@ function createCommentsOverlay(options) {
130
145
  }
131
146
  }
132
147
  resolutions = next;
148
+ if (pendingScroll) {
149
+ const isStale = selection?.kind !== "thread" || selection.threadId !== pendingScroll.threadId || Date.now() > pendingScroll.until;
150
+ if (isStale || scrollToThread(pendingScroll.threadId)) {
151
+ pendingScroll = null;
152
+ }
153
+ }
133
154
  if (selection?.kind === "draft") {
134
155
  selection.resolution = resolveAnchor(selection.anchor, document, {
135
156
  previous: selection.resolution?.element ?? null,
@@ -589,7 +610,7 @@ function createCommentsOverlay(options) {
589
610
  if (window.getSelection()?.toString().trim()) {
590
611
  return;
591
612
  }
592
- if (!isPageElement(target)) {
613
+ if (!isPageElement(target) || !getAnchorElement(target)) {
593
614
  dismissSelection();
594
615
  return;
595
616
  }
@@ -741,17 +762,15 @@ function createCommentsOverlay(options) {
741
762
  },
742
763
  focusThread: (threadId) => {
743
764
  selection = { kind: "thread", threadId };
744
- const resolution = resolutions.get(threadId);
745
- const target = resolution?.range?.startContainer.parentElement ?? resolution?.element ?? null;
746
- if (resolution && isResolutionVisible(resolution)) {
747
- const reduceMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches ?? false;
748
- target?.scrollIntoView?.({
749
- block: "center",
750
- behavior: reduceMotion ? "auto" : "smooth"
751
- });
752
- }
765
+ pendingScroll = scrollToThread(threadId) ? null : { threadId, until: Date.now() + PENDING_SCROLL_MS };
753
766
  schedulePaint();
754
767
  },
768
+ clearThreadFocus: () => {
769
+ if (selection?.kind === "thread") {
770
+ selection = null;
771
+ schedulePaint();
772
+ }
773
+ },
755
774
  clearDraft: () => {
756
775
  clearDraft();
757
776
  schedulePaint();
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sandbox-runtime",
3
- "version": "1.24.3",
3
+ "version": "1.24.4",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sandbox-runtime",
6
- "version": "1.24.3",
6
+ "version": "1.24.4",
7
7
  "branch": "HEAD",
8
- "commit": "e1eeef82df82036e9d623fccf943736d34fb994d",
9
- "builtAt": "2026-11-04T19:25:11.953Z"
8
+ "commit": "49a643c73e7608adcefebcd5c5e27c3e6d0c05a9",
9
+ "builtAt": "2026-11-04T22:05:21.699Z"
10
10
  },
11
11
  "description": "Replo sandbox runtime — editor integration for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",