reflet-sdk 0.6.7 → 0.6.9

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.
@@ -1,5 +1,5 @@
1
- import { a as EDITORS, c as handoffFileSchema, d as sessionEvidenceSchema, f as sessionHandoffSchema, g as sessionSummarySchema, h as sessionSegmentSchema, l as sessionApiErrorSchema, m as sessionProposalSchema, n as DEVTOOLS_REQUEST_HEADER, p as sessionIdentitySchema, r as DEVTOOLS_ROUTE_BASE, s as analysisStatusSchema, t as DEVTOOLS_ENDPOINTS, u as sessionDetailSchema } from "./protocol-vJhQHTcB.js";
2
- import { B as clip, D as MoreIcon, G as describeElement, K as getElementRect, M as TargetIcon, R as DEFAULT_WIDGET_LABELS, T as GripIcon, W as buildSelector, Y as redactUrl, Z as inspectReactElement, _ as matchesHotkey, a as uploadBlob, c as useFloatingPosition, g as isSubmitEnter, it as releaseCapture, j as Svg, l as onViewportChange, m as startsInWidget, o as ElementPicker, p as listenToKeydown, q as REDACT_ATTRIBUTE, tt as captureViewport, u as visibleViewport, w as CloseIcon } from "./submit-DZX0f2_0.js";
1
+ import { a as EDITORS, c as handoffFileSchema, d as sessionEvidenceSchema, f as sessionHandoffSchema, g as sessionSummarySchema, h as sessionSegmentSchema, l as sessionApiErrorSchema, m as sessionProposalSchema, n as DEVTOOLS_REQUEST_HEADER, p as sessionIdentitySchema, r as DEVTOOLS_ROUTE_BASE, s as analysisStatusSchema, t as DEVTOOLS_ENDPOINTS, u as sessionDetailSchema } from "./protocol-DyAiTtzM.js";
2
+ import { B as clip, D as MoreIcon, G as describeElement, K as getElementRect, M as TargetIcon, R as DEFAULT_WIDGET_LABELS, T as GripIcon, W as buildSelector, Y as redactUrl, Z as inspectReactElement, _ as matchesHotkey, a as uploadBlob, c as useFloatingPosition, g as isSubmitEnter, it as releaseCapture, j as Svg, l as onViewportChange, m as startsInWidget, o as ElementPicker, p as listenToKeydown, q as REDACT_ATTRIBUTE, tt as captureViewport, u as visibleViewport, w as CloseIcon } from "./submit-CSIJr7-R.js";
3
3
  import { t as REFLET_Z_INDEX } from "./z-index-CwokqVq_.js";
4
4
  import { z } from "zod";
5
5
  import { createContext, useContext, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
@@ -136,6 +136,7 @@ function createReviewEndpoints({ get, post }) {
136
136
  detail: (sessionId) => get("/api/v1/sessions/detail", sessionDetailSchema, { sessionId }),
137
137
  identity: () => get("/api/v1/sessions/identity", sessionIdentitySchema),
138
138
  list: async () => (await get("/api/v1/sessions/list", z.object({ sessions: z.array(sessionSummarySchema) }))).sessions,
139
+ removeEvidence: async (evidenceId) => (await post("/api/v1/sessions/evidence/remove", { evidenceId }, z.object({ evidence: sessionEvidenceSchema }))).evidence,
139
140
  retrySegment: async (segmentId) => (await post("/api/v1/sessions/segments/retry", { segmentId }, segmentResponse)).segment,
140
141
  update: async (body) => (await post("/api/v1/sessions/update", body, summaryResponse)).session
141
142
  };
@@ -1492,7 +1493,7 @@ function captureFrame() {
1492
1493
  }
1493
1494
  //#endregion
1494
1495
  //#region src/sessions/capture/page/interaction-recorder.ts
1495
- const DWELL_MS = 700;
1496
+ const MIN_DWELL_MS = 700;
1496
1497
  const POINTER_SAMPLE_INTERVAL_MS = 100;
1497
1498
  const SETTLE_DEBOUNCE_MS = 400;
1498
1499
  function eventElement(event) {
@@ -1503,14 +1504,14 @@ function eventElement(event) {
1503
1504
  return element === document.body || element === document.documentElement ? null : element;
1504
1505
  }
1505
1506
  function pointerOf(event) {
1506
- return event instanceof MouseEvent ? {
1507
+ return {
1507
1508
  x: Math.round(event.clientX),
1508
1509
  y: Math.round(event.clientY)
1509
- } : {
1510
- x: 0,
1511
- y: 0
1512
1510
  };
1513
1511
  }
1512
+ function isPhysicalPointerClick(event) {
1513
+ return event instanceof MouseEvent && event.detail > 0;
1514
+ }
1514
1515
  function debounced(signal, run) {
1515
1516
  let settleTimer;
1516
1517
  signal.addEventListener("abort", () => window.clearTimeout(settleTimer));
@@ -1527,34 +1528,50 @@ function recordClick(options) {
1527
1528
  element: describeElementRef(element, options),
1528
1529
  frame: captureFrame(),
1529
1530
  kind: "click",
1530
- pointer: pointerOf(event)
1531
+ pointer: isPhysicalPointerClick(event) ? pointerOf(event) : null
1531
1532
  });
1532
1533
  };
1533
1534
  }
1534
1535
  function watchDwell(options, signal) {
1535
1536
  let hovered = null;
1536
1537
  let lastSampleAt = Number.NEGATIVE_INFINITY;
1537
- let dwellTimer;
1538
- signal.addEventListener("abort", () => window.clearTimeout(dwellTimer));
1539
- return (event) => {
1538
+ let entered = null;
1539
+ const settle = () => {
1540
+ if (!entered) return;
1541
+ const durationMs = options.atMs() - entered.atMs;
1542
+ if (durationMs >= MIN_DWELL_MS) options.onEvent({
1543
+ ...entered,
1544
+ durationMs
1545
+ });
1546
+ entered = null;
1547
+ };
1548
+ signal.addEventListener("abort", settle);
1549
+ const onPointerMove = (event) => {
1550
+ if (!(event instanceof MouseEvent)) return;
1540
1551
  const now = Date.now();
1541
1552
  if (now - lastSampleAt < POINTER_SAMPLE_INTERVAL_MS) return;
1542
1553
  lastSampleAt = now;
1543
1554
  const element = eventElement(event);
1544
1555
  if (element === hovered) return;
1556
+ settle();
1545
1557
  hovered = element;
1546
- window.clearTimeout(dwellTimer);
1547
- if (!element) return;
1548
- const atMs = options.atMs();
1549
- const pointer = pointerOf(event);
1550
- dwellTimer = window.setTimeout(() => options.onEvent({
1551
- atMs,
1552
- durationMs: DWELL_MS,
1558
+ if (element) entered = {
1559
+ atMs: options.atMs(),
1553
1560
  element: describeElementRef(element, options),
1554
1561
  frame: captureFrame(),
1555
1562
  kind: "dwell",
1556
- pointer
1557
- }), DWELL_MS);
1563
+ pointer: pointerOf(event)
1564
+ };
1565
+ };
1566
+ const onPointerLeaveWindow = (event) => {
1567
+ if (event instanceof MouseEvent && event.relatedTarget === null) {
1568
+ settle();
1569
+ hovered = null;
1570
+ }
1571
+ };
1572
+ return {
1573
+ onPointerLeaveWindow,
1574
+ onPointerMove
1558
1575
  };
1559
1576
  }
1560
1577
  function startInteractionRecorder(options) {
@@ -1578,8 +1595,10 @@ function startInteractionRecorder(options) {
1578
1595
  viewportHeight: window.innerHeight,
1579
1596
  viewportWidth: window.innerWidth
1580
1597
  }));
1598
+ const dwell = watchDwell(options, signal);
1581
1599
  document.addEventListener("click", recordClick(options), listen);
1582
- document.addEventListener("pointermove", watchDwell(options, signal), listen);
1600
+ document.addEventListener("pointermove", dwell.onPointerMove, listen);
1601
+ document.addEventListener("pointerout", dwell.onPointerLeaveWindow, listen);
1583
1602
  window.addEventListener("scroll", recordScroll, listen);
1584
1603
  window.addEventListener("resize", recordViewport, listen);
1585
1604
  return { stop: () => controller.abort() };
@@ -1712,6 +1731,12 @@ function blankFieldValues(root) {
1712
1731
  const NAVIGATION_SETTLE_MS = 1500;
1713
1732
  const CAPTURE_FAILED = "The page could not be captured.";
1714
1733
  const CAPTURE_TOO_LARGE = "The screenshot was larger than the upload limit.";
1734
+ const STOPPED_BEFORE_CAPTURE = "Recording stopped before the screenshot was taken.";
1735
+ const stoppedBeforeCapture = (base) => ({
1736
+ ...base,
1737
+ image: null,
1738
+ missingReason: STOPPED_BEFORE_CAPTURE
1739
+ });
1715
1740
  async function shoot(base, maxImageBytes) {
1716
1741
  const image = await captureViewport({
1717
1742
  excludeSelectors: SESSION_MASK_SELECTORS,
@@ -1745,6 +1770,7 @@ function createEvidenceScheduler(options) {
1745
1770
  let lastCaptureAtMs = Number.NEGATIVE_INFINITY;
1746
1771
  let navigationTimer;
1747
1772
  let previousCapture = Promise.resolve();
1773
+ let collectionRun = 0;
1748
1774
  const take = (trigger, request) => {
1749
1775
  if (captured >= limits.maxEvidence) return null;
1750
1776
  captured += 1;
@@ -1759,11 +1785,20 @@ function createEvidenceScheduler(options) {
1759
1785
  note: request.note ?? null,
1760
1786
  trigger
1761
1787
  };
1762
- previousCapture = previousCapture.then(() => shoot(base, limits.maxImageBytes)).then(options.onEvidence);
1788
+ const takenInRun = collectionRun;
1789
+ const captureUnlessStopped = async () => {
1790
+ if (takenInRun !== collectionRun) return stoppedBeforeCapture(base);
1791
+ const evidence = await shoot(base, limits.maxImageBytes);
1792
+ return takenInRun === collectionRun ? evidence : stoppedBeforeCapture(base);
1793
+ };
1794
+ previousCapture = previousCapture.then(captureUnlessStopped).then(options.onEvidence);
1763
1795
  return clientKey;
1764
1796
  };
1765
1797
  return {
1766
- cancelPending: () => window.clearTimeout(navigationTimer),
1798
+ cancelPending: () => {
1799
+ window.clearTimeout(navigationTimer);
1800
+ collectionRun += 1;
1801
+ },
1767
1802
  capture: (request) => take(request.trigger, request),
1768
1803
  navigated: () => {
1769
1804
  window.clearTimeout(navigationTimer);
@@ -2070,7 +2105,6 @@ var LiveSession = class {
2070
2105
  }
2071
2106
  stop() {
2072
2107
  this.stopCollecting();
2073
- this.evidence.cancelPending();
2074
2108
  this.events.stop();
2075
2109
  }
2076
2110
  stopCollecting() {
@@ -2078,6 +2112,7 @@ var LiveSession = class {
2078
2112
  this.run = null;
2079
2113
  this.stopListening?.abort();
2080
2114
  this.stopListening = null;
2115
+ this.evidence.cancelPending();
2081
2116
  window.clearInterval(this.tick);
2082
2117
  this.events.flushAll();
2083
2118
  }
@@ -4138,6 +4173,12 @@ function withSegment(detail, segment) {
4138
4173
  segments: [...others, segment].sort((a, b) => a.startMs - b.startMs)
4139
4174
  };
4140
4175
  }
4176
+ function withEvidence(detail, evidence) {
4177
+ return {
4178
+ ...detail,
4179
+ evidence: detail.evidence.map((item) => item.id === evidence.id ? evidence : item)
4180
+ };
4181
+ }
4141
4182
  function isHidden() {
4142
4183
  return typeof document !== "undefined" && document.visibilityState === "hidden";
4143
4184
  }
@@ -5077,7 +5118,89 @@ function AttachLink({ detail, onDone, review }) {
5077
5118
  });
5078
5119
  }
5079
5120
  //#endregion
5121
+ //#region src/sessions/ui/review/transcript/remove-evidence.tsx
5122
+ const REMOVE_COPY = "Remove this screenshot? The image is deleted for good. Briefs you already copied or exported can't be recalled.";
5123
+ function useFocusWhen(shouldFocus) {
5124
+ const ref = useRef(null);
5125
+ useEffect(() => {
5126
+ if (shouldFocus) ref.current?.focus();
5127
+ }, [shouldFocus]);
5128
+ return ref;
5129
+ }
5130
+ function RemoveConfirmation({ evidence, onCancel, removal }) {
5131
+ const { api } = useSessionWorkspace();
5132
+ const run = useSessionAction();
5133
+ const cancelRef = useFocusWhen(true);
5134
+ const remove = async () => {
5135
+ const removed = await run("The screenshot was not removed", () => api.removeEvidence(evidence.id));
5136
+ if (removed !== null) {
5137
+ removal.review.patch((detail) => withEvidence(detail, removed));
5138
+ removal.onRemoved(removed.id);
5139
+ announce("Screenshot removed");
5140
+ }
5141
+ };
5142
+ return /* @__PURE__ */ jsxs("div", {
5143
+ className: "dt-ses-banner",
5144
+ children: [/* @__PURE__ */ jsx("p", { children: REMOVE_COPY }), /* @__PURE__ */ jsxs("div", {
5145
+ className: "dt-actions",
5146
+ children: [/* @__PURE__ */ jsx("button", {
5147
+ className: "dt-btn",
5148
+ "data-variant": "danger",
5149
+ onClick: remove,
5150
+ type: "button",
5151
+ children: "Remove"
5152
+ }), /* @__PURE__ */ jsx("button", {
5153
+ className: "dt-btn",
5154
+ onClick: onCancel,
5155
+ ref: cancelRef,
5156
+ type: "button",
5157
+ children: "Cancel"
5158
+ })]
5159
+ })]
5160
+ });
5161
+ }
5162
+ function RemoveEvidence({ evidence, removal }) {
5163
+ const [isConfirming, setIsConfirming] = useState(false);
5164
+ const [hasCancelled, setHasCancelled] = useState(false);
5165
+ const triggerRef = useFocusWhen(hasCancelled);
5166
+ if (isConfirming) return /* @__PURE__ */ jsx(RemoveConfirmation, {
5167
+ evidence,
5168
+ onCancel: () => {
5169
+ setIsConfirming(false);
5170
+ setHasCancelled(true);
5171
+ },
5172
+ removal
5173
+ });
5174
+ return /* @__PURE__ */ jsx("button", {
5175
+ "aria-label": `Remove screenshot at ${formatClock(evidence.atMs)}`,
5176
+ className: "dt-ses-link",
5177
+ onClick: () => {
5178
+ setIsConfirming(true);
5179
+ setHasCancelled(false);
5180
+ },
5181
+ ref: triggerRef,
5182
+ type: "button",
5183
+ children: "Remove"
5184
+ });
5185
+ }
5186
+ //#endregion
5080
5187
  //#region src/sessions/ui/review/transcript/evidence-tab.tsx
5188
+ const isViewable = (evidence) => evidence.status === "available" && Boolean(evidence.image?.url);
5189
+ function MissingEvidenceItem({ caption, evidence, isJustRemoved }) {
5190
+ const cardRef = useFocusWhen(isJustRemoved);
5191
+ return /* @__PURE__ */ jsxs("li", {
5192
+ className: "dt-card",
5193
+ ref: cardRef,
5194
+ tabIndex: isJustRemoved ? -1 : void 0,
5195
+ children: [/* @__PURE__ */ jsx("p", {
5196
+ className: "dt-meta",
5197
+ children: caption
5198
+ }), /* @__PURE__ */ jsxs("p", {
5199
+ className: "dt-meta",
5200
+ children: ["Screenshot missing: ", evidence.missingReason ?? "not captured."]
5201
+ })]
5202
+ });
5203
+ }
5081
5204
  function LinkEvidenceItem({ link }) {
5082
5205
  return /* @__PURE__ */ jsxs("li", {
5083
5206
  className: "dt-card",
@@ -5100,18 +5223,13 @@ function LinkEvidenceItem({ link }) {
5100
5223
  ]
5101
5224
  });
5102
5225
  }
5103
- function EvidenceItem({ evidence, onEnlarge }) {
5226
+ function EvidenceItem({ evidence, onEnlarge, removal }) {
5104
5227
  const caption = `${formatClock(evidence.atMs)} · ${evidenceCaption(evidence)}`;
5105
5228
  if (evidence.link) return /* @__PURE__ */ jsx(LinkEvidenceItem, { link: evidence.link });
5106
- if (evidence.status === "missing" || !evidence.image?.url) return /* @__PURE__ */ jsxs("li", {
5107
- className: "dt-card",
5108
- children: [/* @__PURE__ */ jsx("p", {
5109
- className: "dt-meta",
5110
- children: caption
5111
- }), /* @__PURE__ */ jsxs("p", {
5112
- className: "dt-meta",
5113
- children: ["Screenshot missing: ", evidence.missingReason ?? "not captured."]
5114
- })]
5229
+ if (!isViewable(evidence)) return /* @__PURE__ */ jsx(MissingEvidenceItem, {
5230
+ caption,
5231
+ evidence,
5232
+ isJustRemoved: evidence.id === removal.removedId
5115
5233
  });
5116
5234
  return /* @__PURE__ */ jsxs("li", {
5117
5235
  className: "dt-card",
@@ -5130,11 +5248,15 @@ function EvidenceItem({ evidence, onEnlarge }) {
5130
5248
  evidence.note && /* @__PURE__ */ jsx("p", {
5131
5249
  className: "dt-note-text",
5132
5250
  children: evidence.note
5251
+ }),
5252
+ removal.canRemove && /* @__PURE__ */ jsx(RemoveEvidence, {
5253
+ evidence,
5254
+ removal
5133
5255
  })
5134
5256
  ]
5135
5257
  });
5136
5258
  }
5137
- function EnlargedEvidence({ evidence, onBack }) {
5259
+ function EnlargedEvidence({ evidence, onBack, removal }) {
5138
5260
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5139
5261
  /* @__PURE__ */ jsx("button", {
5140
5262
  className: "dt-ses-link",
@@ -5153,10 +5275,14 @@ function EnlargedEvidence({ evidence, onBack }) {
5153
5275
  " · ",
5154
5276
  evidenceCaption(evidence)
5155
5277
  ]
5278
+ }),
5279
+ removal.canRemove && /* @__PURE__ */ jsx(RemoveEvidence, {
5280
+ evidence,
5281
+ removal
5156
5282
  })
5157
5283
  ] });
5158
5284
  }
5159
- function EvidenceList({ evidence, onEnlarge }) {
5285
+ function EvidenceList({ evidence, onEnlarge, removal }) {
5160
5286
  return /* @__PURE__ */ jsxs(Fragment, { children: [evidence.length === 0 && /* @__PURE__ */ jsx("p", {
5161
5287
  className: "dt-empty",
5162
5288
  children: "No screenshots or links in this session."
@@ -5164,26 +5290,19 @@ function EvidenceList({ evidence, onEnlarge }) {
5164
5290
  className: "dt-ses-rows",
5165
5291
  children: evidence.map((item) => /* @__PURE__ */ jsx(EvidenceItem, {
5166
5292
  evidence: item,
5167
- onEnlarge: () => onEnlarge(item.id)
5293
+ onEnlarge: () => onEnlarge(item.id),
5294
+ removal
5168
5295
  }, item.id))
5169
5296
  })] });
5170
5297
  }
5171
- function EvidenceTab({ detail, review }) {
5172
- const [enlargedId, setEnlargedId] = useState(null);
5298
+ function AttachLinkAction({ detail, review }) {
5173
5299
  const [isAttaching, setIsAttaching] = useState(false);
5174
- const enlarged = detail.evidence.find((item) => item.id === enlargedId);
5175
- if (enlarged) return /* @__PURE__ */ jsx(EnlargedEvidence, {
5176
- evidence: enlarged,
5177
- onBack: () => setEnlargedId(null)
5178
- });
5179
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(EvidenceList, {
5180
- evidence: detail.evidence,
5181
- onEnlarge: setEnlargedId
5182
- }), detail.session.canEdit && (isAttaching ? /* @__PURE__ */ jsx(AttachLink, {
5300
+ if (isAttaching) return /* @__PURE__ */ jsx(AttachLink, {
5183
5301
  detail,
5184
5302
  onDone: () => setIsAttaching(false),
5185
5303
  review
5186
- }) : /* @__PURE__ */ jsx("div", {
5304
+ });
5305
+ return /* @__PURE__ */ jsx("div", {
5187
5306
  className: "dt-actions",
5188
5307
  children: /* @__PURE__ */ jsx("button", {
5189
5308
  className: "dt-btn",
@@ -5191,7 +5310,34 @@ function EvidenceTab({ detail, review }) {
5191
5310
  type: "button",
5192
5311
  children: "Attach a link"
5193
5312
  })
5194
- }))] });
5313
+ });
5314
+ }
5315
+ function EvidenceTab({ detail, review }) {
5316
+ const [enlargedId, setEnlargedId] = useState(null);
5317
+ const [removedId, setRemovedId] = useState(null);
5318
+ const enlarged = detail.evidence.find((item) => item.id === enlargedId && isViewable(item));
5319
+ const removal = {
5320
+ canRemove: detail.session.canEdit,
5321
+ onRemoved: (evidenceId) => {
5322
+ setEnlargedId(null);
5323
+ setRemovedId(evidenceId);
5324
+ },
5325
+ removedId,
5326
+ review
5327
+ };
5328
+ if (enlarged) return /* @__PURE__ */ jsx(EnlargedEvidence, {
5329
+ evidence: enlarged,
5330
+ onBack: () => setEnlargedId(null),
5331
+ removal
5332
+ });
5333
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(EvidenceList, {
5334
+ evidence: detail.evidence,
5335
+ onEnlarge: setEnlargedId,
5336
+ removal
5337
+ }), detail.session.canEdit && /* @__PURE__ */ jsx(AttachLinkAction, {
5338
+ detail,
5339
+ review
5340
+ })] });
5195
5341
  }
5196
5342
  //#endregion
5197
5343
  //#region src/sessions/ui/review/transcript/segment-row.tsx
@@ -6007,4 +6153,4 @@ function SessionsPanel({ navigation, onStarted, onWriteNote }) {
6007
6153
  //#endregion
6008
6154
  export { announce as A, searchCode as C, openDatabase as D, describeStorageFailure as E, settle as O, probeDevRoute as S, committed as T, teamTransport as _, DevtoolsBar as a, fetchBoardFeedback as b, copyToClipboard as c, PersonIcon as d, SessionWorkspaceProvider as f, devtoolsTransport as g, purgeAccount as h, BarButton as i, reportFailure as j, Notices as k, SESSIONS_STYLES as l, useWorkspaceLoader as m, DEVTOOLS_STYLES as n, DevtoolsBarButtons as o, failureMessage as p, DevtoolsSheet as r, SessionsBarButton as s, SessionsPanel as t, RecordingLayer as u, callReflet as v, startBoardConnect as w, fetchSourceFile as x, disconnectBoard as y };
6009
6155
 
6010
- //# sourceMappingURL=sessions-panel-CkxIekZX.js.map
6156
+ //# sourceMappingURL=sessions-panel-CaOJ6vWR.js.map