reflet-sdk 0.7.7 → 0.7.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.
- package/CHANGELOG.md +30 -0
- package/README.md +2 -1
- package/dist/desktop.js +1 -1
- package/dist/{devtools-layer-CYIoe3Vo.js → devtools-layer-Cj3yyqyM.js} +3 -3
- package/dist/{devtools-layer-CYIoe3Vo.js.map → devtools-layer-Cj3yyqyM.js.map} +1 -1
- package/dist/devtools-next.js +1 -1
- package/dist/devtools-vite.js +3 -3
- package/dist/feedback.js +4 -4
- package/dist/{handler-1XhfZvZ0.js → handler-D8c3woEz.js} +3 -3
- package/dist/{handler-1XhfZvZ0.js.map → handler-D8c3woEz.js.map} +1 -1
- package/dist/{protocol-BnurX0CF.js → protocol-ClxYWmhu.js} +1 -2
- package/dist/protocol-ClxYWmhu.js.map +1 -0
- package/dist/{reflet-proxy-DVE6ov2f.js → reflet-proxy-Dg6mea-4.js} +2 -2
- package/dist/{reflet-proxy-DVE6ov2f.js.map → reflet-proxy-Dg6mea-4.js.map} +1 -1
- package/dist/{submit-r3_M1ITo.js → submit-CQ5nAuwm.js} +2 -2
- package/dist/{submit-r3_M1ITo.js.map → submit-CQ5nAuwm.js.map} +1 -1
- package/dist/{team-layer-5-uPYTjV.js → team-layer-LbaaWsQP.js} +3 -3
- package/dist/{team-layer-5-uPYTjV.js.map → team-layer-LbaaWsQP.js.map} +1 -1
- package/dist/{use-sheet-tab-CacYFVjI.js → use-sheet-tab-B-q4G0Hn.js} +1264 -1083
- package/dist/use-sheet-tab-B-q4G0Hn.js.map +1 -0
- package/package.json +1 -1
- package/dist/protocol-BnurX0CF.js.map +0 -1
- package/dist/use-sheet-tab-CacYFVjI.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as EDITORS, n as DEVTOOLS_REQUEST_HEADER, r as DEVTOOLS_ROUTE_BASE, t as DEVTOOLS_ENDPOINTS } from "./protocol-
|
|
2
|
-
import { $ as inspectReactElement, A as PencilIcon, B as DEFAULT_WIDGET_LABELS, C as CheckIcon, D as GripIcon, E as CodeIcon, G as buildElementSelection, H as clip, J as getElementRect, K as buildSelector, N as Svg, P as TargetIcon, Q as getFiberFromNode, T as CloseIcon, U as collectPageContext, V as SDK_VERSION, Y as REDACT_ATTRIBUTE, Z as redactUrl, _ as isSubmitEnter, a as uploadBlob, at as capturedViewportSize, c as useFloatingPosition, d as SelectionOutline, et as resolveComponentStack, h as startsInWidget, it as capturedFromCanvas, k as MoreIcon, l as onViewportChange, m as listenToKeydown, o as ElementPicker, ot as releaseCapture, p as listenOnPageAndWidget, q as describeElement, r as deriveTitle, rt as captureViewport, s as isWidgetOwned, tt as shortenSourcePath, u as visibleViewport, v as matchesHotkey } from "./submit-
|
|
1
|
+
import { a as EDITORS, n as DEVTOOLS_REQUEST_HEADER, r as DEVTOOLS_ROUTE_BASE, t as DEVTOOLS_ENDPOINTS } from "./protocol-ClxYWmhu.js";
|
|
2
|
+
import { $ as inspectReactElement, A as PencilIcon, B as DEFAULT_WIDGET_LABELS, C as CheckIcon, D as GripIcon, E as CodeIcon, G as buildElementSelection, H as clip, I as TrashIcon, J as getElementRect, K as buildSelector, N as Svg, P as TargetIcon, Q as getFiberFromNode, T as CloseIcon, U as collectPageContext, V as SDK_VERSION, Y as REDACT_ATTRIBUTE, Z as redactUrl, _ as isSubmitEnter, a as uploadBlob, at as capturedViewportSize, c as useFloatingPosition, d as SelectionOutline, et as resolveComponentStack, h as startsInWidget, it as capturedFromCanvas, k as MoreIcon, l as onViewportChange, m as listenToKeydown, o as ElementPicker, ot as releaseCapture, p as listenOnPageAndWidget, q as describeElement, r as deriveTitle, rt as captureViewport, s as isWidgetOwned, tt as shortenSourcePath, u as visibleViewport, v as matchesHotkey } from "./submit-CQ5nAuwm.js";
|
|
3
3
|
import { t as REFLET_Z_INDEX } from "./z-index-CwokqVq_.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import * as G$1 from "react";
|
|
@@ -630,7 +630,6 @@ function createProposalEndpoints(requests) {
|
|
|
630
630
|
files: z.array(handoffFileSchema),
|
|
631
631
|
handoff: sessionHandoffSchema
|
|
632
632
|
})),
|
|
633
|
-
createProposal: (body) => postProposal("/api/v1/sessions/proposals/create", body),
|
|
634
633
|
decideProposal: (body) => postProposal("/api/v1/sessions/proposals/decide", body),
|
|
635
634
|
editProposal: (body) => postProposal("/api/v1/sessions/proposals/edit", body),
|
|
636
635
|
markHandoffDelivered: async (body) => (await post("/api/v1/sessions/handoffs/delivered", body, z.object({ handoff: sessionHandoffSchema }))).handoff,
|
|
@@ -920,6 +919,21 @@ function operationMethods(database) {
|
|
|
920
919
|
id: request.result
|
|
921
920
|
};
|
|
922
921
|
},
|
|
922
|
+
cancelDraft: async (deletion) => {
|
|
923
|
+
const row = await packOperation(deletion);
|
|
924
|
+
await database.write([DRAFTS, OPERATIONS], (transaction) => {
|
|
925
|
+
const drafts = transaction.objectStore(DRAFTS);
|
|
926
|
+
const request = drafts.get(deletion.sessionKey);
|
|
927
|
+
request.onsuccess = () => {
|
|
928
|
+
const current = request.result;
|
|
929
|
+
if (current) drafts.put({
|
|
930
|
+
...current,
|
|
931
|
+
state: "cancelled"
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
transaction.objectStore(OPERATIONS).add(row);
|
|
935
|
+
});
|
|
936
|
+
},
|
|
923
937
|
deleteOperations: (ids) => database.write([OPERATIONS], (transaction) => {
|
|
924
938
|
for (const id of ids) transaction.objectStore(OPERATIONS).delete(id);
|
|
925
939
|
}),
|
|
@@ -1231,6 +1245,10 @@ async function sendOperation(context) {
|
|
|
1231
1245
|
kind: "started",
|
|
1232
1246
|
sessionId: (await context.api.start(payload.body)).id
|
|
1233
1247
|
};
|
|
1248
|
+
if (payload.kind === "delete") {
|
|
1249
|
+
if (context.sessionId !== null) await context.api.deleteSession(context.sessionId);
|
|
1250
|
+
return { kind: "done" };
|
|
1251
|
+
}
|
|
1234
1252
|
if (context.sessionId === null) return {
|
|
1235
1253
|
kind: "drop",
|
|
1236
1254
|
message: "This session was never created on Reflet."
|
|
@@ -1297,6 +1315,17 @@ var SessionOutbox = class {
|
|
|
1297
1315
|
}
|
|
1298
1316
|
});
|
|
1299
1317
|
};
|
|
1318
|
+
async cancelSession(sessionKey) {
|
|
1319
|
+
const payload = { kind: "delete" };
|
|
1320
|
+
await this.options.store.cancelDraft({
|
|
1321
|
+
accountKey: this.options.accountKey,
|
|
1322
|
+
bytes: operationBytes(payload),
|
|
1323
|
+
payload,
|
|
1324
|
+
sessionKey
|
|
1325
|
+
});
|
|
1326
|
+
this.options.onDraftsChanged();
|
|
1327
|
+
this.drain();
|
|
1328
|
+
}
|
|
1300
1329
|
async enqueue(sessionKey, payload) {
|
|
1301
1330
|
const bytes = operationBytes(payload);
|
|
1302
1331
|
if (this.snapshot.pendingBytes + bytes > this.options.maxLocalBufferBytes) this.failStorage(BUFFER_FULL);
|
|
@@ -1357,11 +1386,12 @@ var SessionOutbox = class {
|
|
|
1357
1386
|
}
|
|
1358
1387
|
async drainPass() {
|
|
1359
1388
|
const { accountKey, store } = this.options;
|
|
1389
|
+
const drafts = await store.readDrafts(accountKey);
|
|
1360
1390
|
const pass = {
|
|
1361
1391
|
outcomes: /* @__PURE__ */ new Map(),
|
|
1362
|
-
sessionIds: sessionIdsOf(
|
|
1392
|
+
sessionIds: sessionIdsOf(drafts)
|
|
1363
1393
|
};
|
|
1364
|
-
for (const operation of await
|
|
1394
|
+
for (const operation of await this.dropAbandonedUploads(drafts)) {
|
|
1365
1395
|
if (this.isHalted()) return;
|
|
1366
1396
|
if (pass.outcomes.get(operation.sessionKey)?.kind !== "retry") await this.runOperation(operation, pass);
|
|
1367
1397
|
}
|
|
@@ -1373,6 +1403,16 @@ var SessionOutbox = class {
|
|
|
1373
1403
|
await this.forgetFinishedDrafts(remaining);
|
|
1374
1404
|
this.afterPass(pass);
|
|
1375
1405
|
}
|
|
1406
|
+
async dropAbandonedUploads(drafts) {
|
|
1407
|
+
const { accountKey, store } = this.options;
|
|
1408
|
+
const cancelledSessionKeys = new Set(drafts.filter((draft) => draft.state === "cancelled").map((draft) => draft.sessionKey));
|
|
1409
|
+
const abandonedIds = [];
|
|
1410
|
+
const toSend = [];
|
|
1411
|
+
for (const operation of await store.readOperations(accountKey)) if (cancelledSessionKeys.has(operation.sessionKey) && operation.payload.kind !== "delete") abandonedIds.push(operation.id);
|
|
1412
|
+
else toSend.push(operation);
|
|
1413
|
+
await store.deleteOperations(abandonedIds);
|
|
1414
|
+
return toSend;
|
|
1415
|
+
}
|
|
1376
1416
|
afterPass(pass) {
|
|
1377
1417
|
if (![...pass.outcomes.values()].some((outcome) => outcome.kind === "retry")) {
|
|
1378
1418
|
this.failedPasses = 0;
|
|
@@ -1442,7 +1482,7 @@ var SessionOutbox = class {
|
|
|
1442
1482
|
async forgetFinishedDrafts(operations) {
|
|
1443
1483
|
const { accountKey, store } = this.options;
|
|
1444
1484
|
const waiting = new Set(operations.map((operation) => operation.sessionKey));
|
|
1445
|
-
const finished = (await store.readDrafts(accountKey)).filter((draft) => draft.state === "ended" && !waiting.has(draft.sessionKey));
|
|
1485
|
+
const finished = (await store.readDrafts(accountKey)).filter((draft) => (draft.state === "ended" || draft.state === "cancelled") && !waiting.has(draft.sessionKey));
|
|
1446
1486
|
await Promise.all(finished.map((draft) => store.deleteDraft(draft.sessionKey)));
|
|
1447
1487
|
if (finished.length > 0) this.options.onDraftsChanged();
|
|
1448
1488
|
}
|
|
@@ -1463,15 +1503,16 @@ const IDLE_SYNC = {
|
|
|
1463
1503
|
pendingOperations: 0
|
|
1464
1504
|
};
|
|
1465
1505
|
function visibleDraft(stored, isLive) {
|
|
1506
|
+
if (stored.state === "cancelled") return [];
|
|
1466
1507
|
const leftOpen = stored.state === "recording" || stored.state === "paused";
|
|
1467
|
-
return {
|
|
1508
|
+
return [{
|
|
1468
1509
|
lastActiveAt: stored.lastActiveAt,
|
|
1469
1510
|
sessionId: stored.sessionId,
|
|
1470
1511
|
sessionKey: stored.sessionKey,
|
|
1471
1512
|
startedAt: stored.startedAt,
|
|
1472
1513
|
state: leftOpen && !isLive ? "interrupted" : stored.state,
|
|
1473
1514
|
title: stored.title
|
|
1474
|
-
};
|
|
1515
|
+
}];
|
|
1475
1516
|
}
|
|
1476
1517
|
var LocalSessionsRegistry = class {
|
|
1477
1518
|
accounts = /* @__PURE__ */ new Map();
|
|
@@ -1508,7 +1549,8 @@ var LocalSessionsRegistry = class {
|
|
|
1508
1549
|
async discardDraft(options) {
|
|
1509
1550
|
const { accountKey, sessionKey } = options;
|
|
1510
1551
|
const stored = (await this.store.readDrafts(accountKey)).find((candidate) => candidate.sessionKey === sessionKey);
|
|
1511
|
-
|
|
1552
|
+
const isClosed = stored?.state === "ended" || stored?.state === "cancelled";
|
|
1553
|
+
if (!stored || this.live.has(sessionKey) || isClosed) return;
|
|
1512
1554
|
const { outbox } = this.ensureOutbox({
|
|
1513
1555
|
...options,
|
|
1514
1556
|
maxLocalBufferBytes: null
|
|
@@ -1544,7 +1586,7 @@ var LocalSessionsRegistry = class {
|
|
|
1544
1586
|
}
|
|
1545
1587
|
refreshDrafts(accountKey) {
|
|
1546
1588
|
this.store.readDrafts(accountKey).then((stored) => {
|
|
1547
|
-
const visible = stored.
|
|
1589
|
+
const visible = stored.flatMap((item) => visibleDraft(item, this.live.has(item.sessionKey))).sort((a, b) => b.startedAt - a.startedAt);
|
|
1548
1590
|
this.drafts.set(accountKey, visible);
|
|
1549
1591
|
this.notify();
|
|
1550
1592
|
}).catch(() => {
|
|
@@ -2866,6 +2908,13 @@ var RecordingController = class {
|
|
|
2866
2908
|
annotateMark(key, note) {
|
|
2867
2909
|
this.live?.annotate(key, cleanText(note, MAX_NOTE_CHARS));
|
|
2868
2910
|
}
|
|
2911
|
+
async cancel() {
|
|
2912
|
+
const live = this.live;
|
|
2913
|
+
if (!live) return;
|
|
2914
|
+
const cancelling = this.account.outbox.cancelSession(live.sessionKey);
|
|
2915
|
+
this.close(live, IDLE);
|
|
2916
|
+
await cancelling;
|
|
2917
|
+
}
|
|
2869
2918
|
dispose() {
|
|
2870
2919
|
this.interrupt();
|
|
2871
2920
|
this.stopIssues();
|
|
@@ -2921,7 +2970,7 @@ var RecordingController = class {
|
|
|
2921
2970
|
});
|
|
2922
2971
|
}
|
|
2923
2972
|
async resumeDraft(sessionKey) {
|
|
2924
|
-
const draft = (await this.account.store.readDrafts(this.options.account.key)).find((candidate) => candidate.sessionKey === sessionKey && candidate.state !== "ended");
|
|
2973
|
+
const draft = (await this.account.store.readDrafts(this.options.account.key)).find((candidate) => candidate.sessionKey === sessionKey && candidate.state !== "ended" && candidate.state !== "cancelled");
|
|
2925
2974
|
if (!draft || this.isBusy()) return;
|
|
2926
2975
|
const voice = await this.requestVoice();
|
|
2927
2976
|
if (!voice) return;
|
|
@@ -3349,6 +3398,191 @@ function isDetailProcessing(detail) {
|
|
|
3349
3398
|
return transcribing || refining || comparing || isAnalysisBusy(detail.session) || detail.session.collection !== "ended";
|
|
3350
3399
|
}
|
|
3351
3400
|
//#endregion
|
|
3401
|
+
//#region src/sessions/ui/review/use-session-detail.ts
|
|
3402
|
+
const POLL_INTERVAL_MS = 2e3;
|
|
3403
|
+
function nextRequest(current) {
|
|
3404
|
+
return {
|
|
3405
|
+
...current,
|
|
3406
|
+
count: current.count + 1
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
function withProposal(detail, proposal) {
|
|
3410
|
+
const isKnown = detail.proposals.some((item) => item.id === proposal.id);
|
|
3411
|
+
return {
|
|
3412
|
+
...detail,
|
|
3413
|
+
proposals: isKnown ? detail.proposals.map((item) => item.id === proposal.id ? proposal : item) : [...detail.proposals, proposal]
|
|
3414
|
+
};
|
|
3415
|
+
}
|
|
3416
|
+
function withSegment(detail, segment) {
|
|
3417
|
+
const others = detail.segments.filter((item) => item.id !== segment.id);
|
|
3418
|
+
return {
|
|
3419
|
+
...detail,
|
|
3420
|
+
segments: [...others, segment].sort((a, b) => a.startMs - b.startMs)
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
function withEvidence(detail, evidence) {
|
|
3424
|
+
return {
|
|
3425
|
+
...detail,
|
|
3426
|
+
evidence: detail.evidence.map((item) => item.id === evidence.id ? evidence : item)
|
|
3427
|
+
};
|
|
3428
|
+
}
|
|
3429
|
+
function isHidden() {
|
|
3430
|
+
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
3431
|
+
}
|
|
3432
|
+
function usePollTick(lastAnswer) {
|
|
3433
|
+
const [tick, setTick] = useState(0);
|
|
3434
|
+
useEffect(() => {
|
|
3435
|
+
if (lastAnswer.kind !== "ready" || !isDetailProcessing(lastAnswer.detail)) return;
|
|
3436
|
+
let timer = 0;
|
|
3437
|
+
const schedule = () => {
|
|
3438
|
+
window.clearTimeout(timer);
|
|
3439
|
+
if (!isHidden()) timer = window.setTimeout(() => setTick((current) => current + 1), POLL_INTERVAL_MS);
|
|
3440
|
+
};
|
|
3441
|
+
schedule();
|
|
3442
|
+
document.addEventListener("visibilitychange", schedule);
|
|
3443
|
+
return () => {
|
|
3444
|
+
window.clearTimeout(timer);
|
|
3445
|
+
document.removeEventListener("visibilitychange", schedule);
|
|
3446
|
+
};
|
|
3447
|
+
}, [lastAnswer]);
|
|
3448
|
+
return tick;
|
|
3449
|
+
}
|
|
3450
|
+
function failedState(error, current) {
|
|
3451
|
+
if (error instanceof SessionApiError && error.status === 404) return { kind: "missing" };
|
|
3452
|
+
if (current.kind === "ready") return current;
|
|
3453
|
+
return {
|
|
3454
|
+
kind: "failed",
|
|
3455
|
+
message: failureMessage(error)
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3458
|
+
function useLandedUploadTick() {
|
|
3459
|
+
const { account } = useSessionWorkspace();
|
|
3460
|
+
const { pendingOperations } = useSyncStatus(account.key);
|
|
3461
|
+
const [seen, setSeen] = useState({
|
|
3462
|
+
pending: pendingOperations,
|
|
3463
|
+
tick: 0
|
|
3464
|
+
});
|
|
3465
|
+
if (seen.pending !== pendingOperations) setSeen({
|
|
3466
|
+
pending: pendingOperations,
|
|
3467
|
+
tick: pendingOperations < seen.pending ? seen.tick + 1 : seen.tick
|
|
3468
|
+
});
|
|
3469
|
+
return seen.tick;
|
|
3470
|
+
}
|
|
3471
|
+
function useSessionDetail(sessionId) {
|
|
3472
|
+
const { api, onUnauthorized } = useSessionWorkspace();
|
|
3473
|
+
const [state, setState] = useState({ kind: "loading" });
|
|
3474
|
+
const [request, setRequest] = useState({
|
|
3475
|
+
count: 0,
|
|
3476
|
+
sessionId
|
|
3477
|
+
});
|
|
3478
|
+
const pollTick = usePollTick(state);
|
|
3479
|
+
const uploadTick = useLandedUploadTick();
|
|
3480
|
+
useEffect(() => {
|
|
3481
|
+
let isCurrent = true;
|
|
3482
|
+
api.detail(request.sessionId).then((detail) => isCurrent && setState({
|
|
3483
|
+
detail,
|
|
3484
|
+
kind: "ready"
|
|
3485
|
+
}), (error) => {
|
|
3486
|
+
if (isUnauthorized(error)) {
|
|
3487
|
+
onUnauthorized();
|
|
3488
|
+
return;
|
|
3489
|
+
}
|
|
3490
|
+
if (isCurrent) setState((current) => failedState(error, current));
|
|
3491
|
+
const isRefresh = request.count + pollTick + uploadTick > 0;
|
|
3492
|
+
if (isCurrent && isRefresh) reportFailure("The session did not refresh", error);
|
|
3493
|
+
});
|
|
3494
|
+
return () => {
|
|
3495
|
+
isCurrent = false;
|
|
3496
|
+
};
|
|
3497
|
+
}, [
|
|
3498
|
+
api,
|
|
3499
|
+
onUnauthorized,
|
|
3500
|
+
request,
|
|
3501
|
+
pollTick,
|
|
3502
|
+
uploadTick
|
|
3503
|
+
]);
|
|
3504
|
+
return {
|
|
3505
|
+
patch: (update) => setState((current) => current.kind === "ready" ? {
|
|
3506
|
+
detail: update(current.detail),
|
|
3507
|
+
kind: "ready"
|
|
3508
|
+
} : current),
|
|
3509
|
+
reload: () => setRequest(nextRequest),
|
|
3510
|
+
state
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
//#endregion
|
|
3514
|
+
//#region src/sessions/ui/review/transcript/remove-evidence.tsx
|
|
3515
|
+
const REMOVE_COPY = "Remove this screenshot? The image is deleted for good. Briefs you already downloaded keep their copy.";
|
|
3516
|
+
function useFocusWhen(shouldFocus) {
|
|
3517
|
+
const ref = useRef(null);
|
|
3518
|
+
useEffect(() => {
|
|
3519
|
+
if (shouldFocus) ref.current?.focus();
|
|
3520
|
+
}, [shouldFocus]);
|
|
3521
|
+
return ref;
|
|
3522
|
+
}
|
|
3523
|
+
function RemoveConfirmation({ evidence, onCancel, removal }) {
|
|
3524
|
+
const { api } = useSessionWorkspace();
|
|
3525
|
+
const run = useSessionAction();
|
|
3526
|
+
const cancelRef = useFocusWhen(true);
|
|
3527
|
+
const remove = async () => {
|
|
3528
|
+
const removed = await run("The screenshot was not removed", () => api.removeEvidence(evidence.id));
|
|
3529
|
+
if (removed !== null) {
|
|
3530
|
+
removal.review.patch((detail) => withEvidence(detail, removed));
|
|
3531
|
+
removal.review.reload();
|
|
3532
|
+
removal.onRemoved(removed.id);
|
|
3533
|
+
announce("Screenshot removed");
|
|
3534
|
+
}
|
|
3535
|
+
};
|
|
3536
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3537
|
+
className: "dt-ses-banner",
|
|
3538
|
+
children: [/* @__PURE__ */ jsx("p", { children: REMOVE_COPY }), /* @__PURE__ */ jsxs("div", {
|
|
3539
|
+
className: "dt-actions",
|
|
3540
|
+
children: [/* @__PURE__ */ jsx("button", {
|
|
3541
|
+
className: "dt-btn",
|
|
3542
|
+
"data-variant": "danger",
|
|
3543
|
+
onClick: remove,
|
|
3544
|
+
type: "button",
|
|
3545
|
+
children: "Remove"
|
|
3546
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
3547
|
+
className: "dt-btn",
|
|
3548
|
+
onClick: onCancel,
|
|
3549
|
+
ref: cancelRef,
|
|
3550
|
+
type: "button",
|
|
3551
|
+
children: "Cancel"
|
|
3552
|
+
})]
|
|
3553
|
+
})]
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
function RemoveEvidence({ evidence, removal }) {
|
|
3557
|
+
const [isConfirming, setIsConfirming] = useState(false);
|
|
3558
|
+
const [hasCancelled, setHasCancelled] = useState(false);
|
|
3559
|
+
const triggerRef = useFocusWhen(hasCancelled);
|
|
3560
|
+
if (isConfirming) return /* @__PURE__ */ jsx(RemoveConfirmation, {
|
|
3561
|
+
evidence,
|
|
3562
|
+
onCancel: () => {
|
|
3563
|
+
setIsConfirming(false);
|
|
3564
|
+
setHasCancelled(true);
|
|
3565
|
+
},
|
|
3566
|
+
removal
|
|
3567
|
+
});
|
|
3568
|
+
const label = `Remove screenshot at ${formatClock(evidence.atMs)}`;
|
|
3569
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3570
|
+
className: "dt-hover-actions",
|
|
3571
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
3572
|
+
"aria-label": label,
|
|
3573
|
+
className: "icon-btn glass",
|
|
3574
|
+
onClick: () => {
|
|
3575
|
+
setIsConfirming(true);
|
|
3576
|
+
setHasCancelled(false);
|
|
3577
|
+
},
|
|
3578
|
+
ref: triggerRef,
|
|
3579
|
+
title: label,
|
|
3580
|
+
type: "button",
|
|
3581
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {})
|
|
3582
|
+
})
|
|
3583
|
+
});
|
|
3584
|
+
}
|
|
3585
|
+
//#endregion
|
|
3352
3586
|
//#region src/sessions/ui/session-icons.tsx
|
|
3353
3587
|
function MicIcon() {
|
|
3354
3588
|
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("rect", {
|
|
@@ -3375,6 +3609,22 @@ function ClockIcon() {
|
|
|
3375
3609
|
r: "8.5"
|
|
3376
3610
|
}), /* @__PURE__ */ jsx("path", { d: "M12 7.5V12l3 2" })] });
|
|
3377
3611
|
}
|
|
3612
|
+
function LockIcon() {
|
|
3613
|
+
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("rect", {
|
|
3614
|
+
height: "10",
|
|
3615
|
+
rx: "2.5",
|
|
3616
|
+
width: "15",
|
|
3617
|
+
x: "4.5",
|
|
3618
|
+
y: "10.5"
|
|
3619
|
+
}), /* @__PURE__ */ jsx("path", { d: "M8 10.5V8a4 4 0 0 1 8 0v2.5" })] });
|
|
3620
|
+
}
|
|
3621
|
+
function TeamIcon() {
|
|
3622
|
+
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("circle", {
|
|
3623
|
+
cx: "9",
|
|
3624
|
+
cy: "8.5",
|
|
3625
|
+
r: "3.5"
|
|
3626
|
+
}), /* @__PURE__ */ jsx("path", { d: "M2.5 20a6.5 6.5 0 0 1 13 0M16 5.2a3.5 3.5 0 0 1 0 6.6M18.5 14.4a6.5 6.5 0 0 1 3 5.6" })] });
|
|
3627
|
+
}
|
|
3378
3628
|
function IntentionIcon() {
|
|
3379
3629
|
return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M9 18h6M10 21h4M12 3a6 6 0 0 0-3.6 10.8c.6.5 1.1 1.3 1.1 2.2h5c0-.9.5-1.7 1.1-2.2A6 6 0 0 0 12 3z" }) });
|
|
3380
3630
|
}
|
|
@@ -3399,6 +3649,50 @@ function ImageIcon() {
|
|
|
3399
3649
|
] });
|
|
3400
3650
|
}
|
|
3401
3651
|
//#endregion
|
|
3652
|
+
//#region src/sessions/ui/recording/cancel-confirmation.tsx
|
|
3653
|
+
function CancelConfirmation({ onCancelRecording, onKeepRecording }) {
|
|
3654
|
+
const confirmationRef = useRef(null);
|
|
3655
|
+
const keepRef = useFocusWhen(true);
|
|
3656
|
+
useEffect(() => {
|
|
3657
|
+
const confirmation = confirmationRef.current;
|
|
3658
|
+
if (!confirmation) return;
|
|
3659
|
+
const keepOnEscape = (event) => {
|
|
3660
|
+
if (event.key === "Escape" && !event.isComposing) {
|
|
3661
|
+
event.stopPropagation();
|
|
3662
|
+
onKeepRecording();
|
|
3663
|
+
}
|
|
3664
|
+
};
|
|
3665
|
+
confirmation.addEventListener("keydown", keepOnEscape);
|
|
3666
|
+
return () => confirmation.removeEventListener("keydown", keepOnEscape);
|
|
3667
|
+
}, [onKeepRecording]);
|
|
3668
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3669
|
+
"aria-labelledby": "rf-rec-cancel-copy",
|
|
3670
|
+
className: "dt-rec-note",
|
|
3671
|
+
ref: confirmationRef,
|
|
3672
|
+
role: "alertdialog",
|
|
3673
|
+
children: [
|
|
3674
|
+
/* @__PURE__ */ jsx("span", {
|
|
3675
|
+
id: "rf-rec-cancel-copy",
|
|
3676
|
+
children: "Cancel this recording? Nothing from it is kept."
|
|
3677
|
+
}),
|
|
3678
|
+
/* @__PURE__ */ jsx("button", {
|
|
3679
|
+
className: "dt-btn",
|
|
3680
|
+
"data-variant": "danger",
|
|
3681
|
+
onClick: onCancelRecording,
|
|
3682
|
+
type: "button",
|
|
3683
|
+
children: "Discard"
|
|
3684
|
+
}),
|
|
3685
|
+
/* @__PURE__ */ jsx("button", {
|
|
3686
|
+
className: "dt-btn",
|
|
3687
|
+
onClick: onKeepRecording,
|
|
3688
|
+
ref: keepRef,
|
|
3689
|
+
type: "button",
|
|
3690
|
+
children: "Keep recording"
|
|
3691
|
+
})
|
|
3692
|
+
]
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
//#endregion
|
|
3402
3696
|
//#region src/sessions/ui/recording/level-meter.tsx
|
|
3403
3697
|
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
3404
3698
|
/** The level changes many times a second: it goes straight to a CSS variable, never through React state. */
|
|
@@ -3461,25 +3755,19 @@ function MarkNote({ focusOnOpen, onClose }) {
|
|
|
3461
3755
|
//#endregion
|
|
3462
3756
|
//#region src/sessions/ui/recording/recording-bar.tsx
|
|
3463
3757
|
const MARK_HOTKEY = "alt+shift+m";
|
|
3464
|
-
function
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
if (sync.blockedBy === "storage") label = "Not saved on this device";
|
|
3469
|
-
else if (!sync.isOnline) label = "Offline — kept on this device";
|
|
3470
|
-
else if (sync.pendingOperations > 0) label = `${sync.pendingOperations} waiting to upload`;
|
|
3471
|
-
return /* @__PURE__ */ jsx("span", {
|
|
3472
|
-
className: "dt-chip",
|
|
3473
|
-
children: label
|
|
3474
|
-
});
|
|
3758
|
+
function syncProblem(sync) {
|
|
3759
|
+
if (sync.blockedBy === "storage") return "Not saved on this device";
|
|
3760
|
+
if (!sync.isOnline) return "Offline — kept on this device";
|
|
3761
|
+
return null;
|
|
3475
3762
|
}
|
|
3476
|
-
function statusText(snapshot, maxDurationMs) {
|
|
3763
|
+
function statusText(snapshot, sync, maxDurationMs) {
|
|
3477
3764
|
if (snapshot.phase === "failed") return snapshot.failure?.message ?? "Recording stopped.";
|
|
3478
3765
|
if (snapshot.phase === "finishing") return "Finishing…";
|
|
3479
3766
|
if (snapshot.pauseReason === "limit") return "Time limit reached · Finish to review the session";
|
|
3480
3767
|
if (snapshot.pauseReason === "hidden") return "Paused because the tab was hidden";
|
|
3481
|
-
if (snapshot.phase === "paused") return "Paused · nothing is recorded";
|
|
3482
3768
|
if (snapshot.limitsReached.length > 0) return `Limit reached for ${snapshot.limitsReached.join(", ")}`;
|
|
3769
|
+
const problem = syncProblem(sync);
|
|
3770
|
+
if (problem) return problem;
|
|
3483
3771
|
return snapshot.nearLimit ? `${minutesLeft(maxDurationMs - snapshot.activeMs)} min left` : "";
|
|
3484
3772
|
}
|
|
3485
3773
|
function markMoment(recorder, focus, setOpenMark) {
|
|
@@ -3513,42 +3801,67 @@ function useMarkMoment(snapshot, barRef) {
|
|
|
3513
3801
|
};
|
|
3514
3802
|
}
|
|
3515
3803
|
function CaptureButtons({ canCapture, onMark, onPointAt }) {
|
|
3516
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */
|
|
3804
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
3517
3805
|
"aria-keyshortcuts": "Alt+Shift+M",
|
|
3806
|
+
"aria-label": "Mark moment",
|
|
3518
3807
|
className: "dt-btn",
|
|
3808
|
+
"data-icon-only": true,
|
|
3519
3809
|
disabled: !canCapture,
|
|
3520
3810
|
onClick: onMark,
|
|
3521
3811
|
title: "Mark moment (Alt+Shift+M)",
|
|
3522
3812
|
type: "button",
|
|
3523
|
-
children:
|
|
3813
|
+
children: /* @__PURE__ */ jsx(MarkIcon, {})
|
|
3524
3814
|
}), /* @__PURE__ */ jsx("button", {
|
|
3815
|
+
"aria-label": "Point at element",
|
|
3525
3816
|
className: "dt-btn",
|
|
3817
|
+
"data-icon-only": true,
|
|
3526
3818
|
disabled: !canCapture,
|
|
3527
3819
|
onClick: onPointAt,
|
|
3820
|
+
title: "Point at element",
|
|
3528
3821
|
type: "button",
|
|
3529
|
-
children:
|
|
3822
|
+
children: /* @__PURE__ */ jsx(TargetIcon, {})
|
|
3530
3823
|
})] });
|
|
3531
3824
|
}
|
|
3532
3825
|
function PauseToggle({ snapshot }) {
|
|
3533
3826
|
const { recorder } = useSessionWorkspace();
|
|
3534
|
-
if (snapshot.phase === "recording") return /* @__PURE__ */
|
|
3827
|
+
if (snapshot.phase === "recording") return /* @__PURE__ */ jsx("button", {
|
|
3828
|
+
"aria-label": "Pause",
|
|
3535
3829
|
className: "dt-btn",
|
|
3830
|
+
"data-icon-only": true,
|
|
3536
3831
|
onClick: () => recorder.pause("paused"),
|
|
3832
|
+
title: "Pause",
|
|
3537
3833
|
type: "button",
|
|
3538
|
-
children:
|
|
3834
|
+
children: /* @__PURE__ */ jsx(PauseIcon, {})
|
|
3539
3835
|
});
|
|
3540
|
-
if (snapshot.phase === "paused" && snapshot.pauseReason !== "limit") return /* @__PURE__ */
|
|
3836
|
+
if (snapshot.phase === "paused" && snapshot.pauseReason !== "limit") return /* @__PURE__ */ jsx("button", {
|
|
3837
|
+
"aria-label": "Resume",
|
|
3541
3838
|
className: "dt-btn",
|
|
3839
|
+
"data-icon-only": true,
|
|
3542
3840
|
onClick: () => recorder.resume(),
|
|
3841
|
+
title: "Resume",
|
|
3543
3842
|
type: "button",
|
|
3544
|
-
children:
|
|
3843
|
+
children: /* @__PURE__ */ jsx(PlayIcon, {})
|
|
3545
3844
|
});
|
|
3546
3845
|
return null;
|
|
3547
3846
|
}
|
|
3548
|
-
function
|
|
3847
|
+
function CancelButton({ hasKeptRecording, onAskToCancel }) {
|
|
3848
|
+
const buttonRef = useFocusWhen(hasKeptRecording);
|
|
3849
|
+
return /* @__PURE__ */ jsx("button", {
|
|
3850
|
+
"aria-label": "Cancel recording",
|
|
3851
|
+
className: "dt-btn",
|
|
3852
|
+
"data-icon-only": true,
|
|
3853
|
+
onClick: onAskToCancel,
|
|
3854
|
+
ref: buttonRef,
|
|
3855
|
+
title: "Cancel recording",
|
|
3856
|
+
type: "button",
|
|
3857
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
function RecordingControls({ hasKeptRecording, onAskToCancel, onFinish, onMark, onPointAt, snapshot }) {
|
|
3549
3861
|
const { phase } = snapshot;
|
|
3862
|
+
const isActive = phase === "recording" || phase === "paused";
|
|
3550
3863
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3551
|
-
|
|
3864
|
+
isActive && /* @__PURE__ */ jsx(CaptureButtons, {
|
|
3552
3865
|
canCapture: phase === "recording",
|
|
3553
3866
|
onMark,
|
|
3554
3867
|
onPointAt
|
|
@@ -3561,6 +3874,10 @@ function RecordingControls({ onFinish, onMark, onPointAt, snapshot }) {
|
|
|
3561
3874
|
onClick: onFinish,
|
|
3562
3875
|
type: "button",
|
|
3563
3876
|
children: "Finish"
|
|
3877
|
+
}),
|
|
3878
|
+
isActive && /* @__PURE__ */ jsx(CancelButton, {
|
|
3879
|
+
hasKeptRecording,
|
|
3880
|
+
onAskToCancel
|
|
3564
3881
|
})
|
|
3565
3882
|
] });
|
|
3566
3883
|
}
|
|
@@ -3573,10 +3890,14 @@ function RecordingClock({ activeMs }) {
|
|
|
3573
3890
|
children: formatClock(activeMs)
|
|
3574
3891
|
});
|
|
3575
3892
|
}
|
|
3576
|
-
function RecordingBar({ onFinish, onPointAt, snapshot }) {
|
|
3577
|
-
const { identity, recorder } = useSessionWorkspace();
|
|
3893
|
+
function RecordingBar({ onCancel, onFinish, onPointAt, snapshot }) {
|
|
3894
|
+
const { account, identity, recorder } = useSessionWorkspace();
|
|
3895
|
+
const sync = useSyncStatus(account.key);
|
|
3578
3896
|
const barRef = useRef(null);
|
|
3579
3897
|
const { markWithNote, openMark } = useMarkMoment(snapshot, barRef);
|
|
3898
|
+
const [cancelStep, setCancelStep] = useState("idle");
|
|
3899
|
+
const keepRecording = useCallback(() => setCancelStep("kept"), []);
|
|
3900
|
+
const showsMeter = snapshot.phase === "recording" || snapshot.phase === "paused";
|
|
3580
3901
|
return /* @__PURE__ */ jsxs("div", {
|
|
3581
3902
|
"aria-label": "Session recording",
|
|
3582
3903
|
className: "dt-rec glass",
|
|
@@ -3588,24 +3909,29 @@ function RecordingBar({ onFinish, onPointAt, snapshot }) {
|
|
|
3588
3909
|
"aria-hidden": "true",
|
|
3589
3910
|
className: "dt-rec-dot"
|
|
3590
3911
|
}),
|
|
3591
|
-
|
|
3912
|
+
showsMeter && /* @__PURE__ */ jsx(LevelMeter, { recorder }),
|
|
3592
3913
|
/* @__PURE__ */ jsx(RecordingClock, { activeMs: snapshot.activeMs }),
|
|
3593
3914
|
/* @__PURE__ */ jsx("span", {
|
|
3594
3915
|
"aria-live": "polite",
|
|
3595
3916
|
className: "dt-rec-status",
|
|
3596
|
-
children: statusText(snapshot, identity.limits.maxDurationMs)
|
|
3917
|
+
children: statusText(snapshot, sync, identity.limits.maxDurationMs)
|
|
3597
3918
|
}),
|
|
3598
3919
|
/* @__PURE__ */ jsx(RecordingControls, {
|
|
3920
|
+
hasKeptRecording: cancelStep === "kept",
|
|
3921
|
+
onAskToCancel: () => setCancelStep("confirming"),
|
|
3599
3922
|
onFinish,
|
|
3600
3923
|
onMark: markWithNote,
|
|
3601
3924
|
onPointAt,
|
|
3602
3925
|
snapshot
|
|
3603
3926
|
}),
|
|
3604
|
-
/* @__PURE__ */ jsx(SyncChip, {}),
|
|
3605
3927
|
openMark && /* @__PURE__ */ jsx(MarkNote, {
|
|
3606
3928
|
focusOnOpen: openMark.focus,
|
|
3607
3929
|
onClose: openMark.closeNote
|
|
3608
|
-
}, openMark.key)
|
|
3930
|
+
}, openMark.key),
|
|
3931
|
+
cancelStep === "confirming" && /* @__PURE__ */ jsx(CancelConfirmation, {
|
|
3932
|
+
onCancelRecording: onCancel,
|
|
3933
|
+
onKeepRecording: keepRecording
|
|
3934
|
+
})
|
|
3609
3935
|
]
|
|
3610
3936
|
});
|
|
3611
3937
|
}
|
|
@@ -3641,7 +3967,16 @@ function RecordingLayer({ onFinished }) {
|
|
|
3641
3967
|
reportFailure("The session did not finish", error, finish);
|
|
3642
3968
|
}
|
|
3643
3969
|
};
|
|
3970
|
+
const cancel = async () => {
|
|
3971
|
+
setIsPointing(false);
|
|
3972
|
+
try {
|
|
3973
|
+
await recorder.cancel();
|
|
3974
|
+
} catch (error) {
|
|
3975
|
+
reportFailure("The recording was not deleted", error);
|
|
3976
|
+
}
|
|
3977
|
+
};
|
|
3644
3978
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(RecordingBar, {
|
|
3979
|
+
onCancel: cancel,
|
|
3645
3980
|
onFinish: finish,
|
|
3646
3981
|
onPointAt: () => setIsPointing(true),
|
|
3647
3982
|
snapshot
|
|
@@ -3824,6 +4159,15 @@ function evidenceCaption(evidence) {
|
|
|
3824
4159
|
const element = evidence.element ? ` · ${evidence.element.label}` : "";
|
|
3825
4160
|
return `${TRIGGER_LABELS[evidence.trigger]}${element}`;
|
|
3826
4161
|
}
|
|
4162
|
+
function ShotCaption({ atMs, children }) {
|
|
4163
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
4164
|
+
className: "dt-ses-caption",
|
|
4165
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4166
|
+
className: "dt-ses-caption-time",
|
|
4167
|
+
children: [/* @__PURE__ */ jsx(ClockIcon, {}), formatClock(atMs)]
|
|
4168
|
+
}), children]
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
3827
4171
|
const hasViewport = (frame) => frame.viewportWidth > 0 && frame.viewportHeight > 0;
|
|
3828
4172
|
const percent = (value, total) => `${value / total * 100}%`;
|
|
3829
4173
|
function percentBox(rect, frame) {
|
|
@@ -3931,12 +4275,18 @@ function HistoryEntry({ context, entry }) {
|
|
|
3931
4275
|
const evidenceIds = new Set(entry.evidenceIds);
|
|
3932
4276
|
const evidence = context.detail.contributedEvidence.filter((item) => evidenceIds.has(item.id));
|
|
3933
4277
|
return /* @__PURE__ */ jsxs("li", {
|
|
3934
|
-
className: "dt-ses-history",
|
|
4278
|
+
className: "dt-ses-history dt-openable",
|
|
3935
4279
|
"data-removed": entry.detachedAt !== null,
|
|
3936
4280
|
children: [
|
|
3937
4281
|
/* @__PURE__ */ jsx("p", {
|
|
3938
4282
|
className: "dt-ses-subhead",
|
|
3939
|
-
children:
|
|
4283
|
+
children: entry.session.canOpen ? /* @__PURE__ */ jsx("button", {
|
|
4284
|
+
className: "dt-open",
|
|
4285
|
+
onClick: () => context.navigation.onOpenSession(entry.session.id),
|
|
4286
|
+
title: "Open session",
|
|
4287
|
+
type: "button",
|
|
4288
|
+
children: entryTitle(entry)
|
|
4289
|
+
}) : entryTitle(entry)
|
|
3940
4290
|
}),
|
|
3941
4291
|
entry.detachedAt !== null && /* @__PURE__ */ jsxs("p", {
|
|
3942
4292
|
className: "dt-meta",
|
|
@@ -3953,12 +4303,6 @@ function HistoryEntry({ context, entry }) {
|
|
|
3953
4303
|
}) : /* @__PURE__ */ jsx(HistoryExtracts, {
|
|
3954
4304
|
evidence,
|
|
3955
4305
|
quotes: entry.quotes
|
|
3956
|
-
}),
|
|
3957
|
-
entry.session.canOpen && /* @__PURE__ */ jsx("button", {
|
|
3958
|
-
className: "dt-ses-link",
|
|
3959
|
-
onClick: () => context.navigation.onOpenSession(entry.session.id),
|
|
3960
|
-
type: "button",
|
|
3961
|
-
children: "Open session"
|
|
3962
4306
|
})
|
|
3963
4307
|
]
|
|
3964
4308
|
});
|
|
@@ -4363,119 +4707,6 @@ function SaveProblemMessage({ onReload, problem }) {
|
|
|
4363
4707
|
});
|
|
4364
4708
|
}
|
|
4365
4709
|
//#endregion
|
|
4366
|
-
//#region src/sessions/ui/review/use-session-detail.ts
|
|
4367
|
-
const POLL_INTERVAL_MS = 2e3;
|
|
4368
|
-
function nextRequest(current) {
|
|
4369
|
-
return {
|
|
4370
|
-
...current,
|
|
4371
|
-
count: current.count + 1
|
|
4372
|
-
};
|
|
4373
|
-
}
|
|
4374
|
-
function withProposal(detail, proposal) {
|
|
4375
|
-
const isKnown = detail.proposals.some((item) => item.id === proposal.id);
|
|
4376
|
-
return {
|
|
4377
|
-
...detail,
|
|
4378
|
-
proposals: isKnown ? detail.proposals.map((item) => item.id === proposal.id ? proposal : item) : [...detail.proposals, proposal]
|
|
4379
|
-
};
|
|
4380
|
-
}
|
|
4381
|
-
function withSegment(detail, segment) {
|
|
4382
|
-
const others = detail.segments.filter((item) => item.id !== segment.id);
|
|
4383
|
-
return {
|
|
4384
|
-
...detail,
|
|
4385
|
-
segments: [...others, segment].sort((a, b) => a.startMs - b.startMs)
|
|
4386
|
-
};
|
|
4387
|
-
}
|
|
4388
|
-
function withEvidence(detail, evidence) {
|
|
4389
|
-
return {
|
|
4390
|
-
...detail,
|
|
4391
|
-
evidence: detail.evidence.map((item) => item.id === evidence.id ? evidence : item)
|
|
4392
|
-
};
|
|
4393
|
-
}
|
|
4394
|
-
function isHidden() {
|
|
4395
|
-
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
4396
|
-
}
|
|
4397
|
-
function usePollTick(lastAnswer) {
|
|
4398
|
-
const [tick, setTick] = useState(0);
|
|
4399
|
-
useEffect(() => {
|
|
4400
|
-
if (lastAnswer.kind !== "ready" || !isDetailProcessing(lastAnswer.detail)) return;
|
|
4401
|
-
let timer = 0;
|
|
4402
|
-
const schedule = () => {
|
|
4403
|
-
window.clearTimeout(timer);
|
|
4404
|
-
if (!isHidden()) timer = window.setTimeout(() => setTick((current) => current + 1), POLL_INTERVAL_MS);
|
|
4405
|
-
};
|
|
4406
|
-
schedule();
|
|
4407
|
-
document.addEventListener("visibilitychange", schedule);
|
|
4408
|
-
return () => {
|
|
4409
|
-
window.clearTimeout(timer);
|
|
4410
|
-
document.removeEventListener("visibilitychange", schedule);
|
|
4411
|
-
};
|
|
4412
|
-
}, [lastAnswer]);
|
|
4413
|
-
return tick;
|
|
4414
|
-
}
|
|
4415
|
-
function failedState(error, current) {
|
|
4416
|
-
if (error instanceof SessionApiError && error.status === 404) return { kind: "missing" };
|
|
4417
|
-
if (current.kind === "ready") return current;
|
|
4418
|
-
return {
|
|
4419
|
-
kind: "failed",
|
|
4420
|
-
message: failureMessage(error)
|
|
4421
|
-
};
|
|
4422
|
-
}
|
|
4423
|
-
function useLandedUploadTick() {
|
|
4424
|
-
const { account } = useSessionWorkspace();
|
|
4425
|
-
const { pendingOperations } = useSyncStatus(account.key);
|
|
4426
|
-
const [seen, setSeen] = useState({
|
|
4427
|
-
pending: pendingOperations,
|
|
4428
|
-
tick: 0
|
|
4429
|
-
});
|
|
4430
|
-
if (seen.pending !== pendingOperations) setSeen({
|
|
4431
|
-
pending: pendingOperations,
|
|
4432
|
-
tick: pendingOperations < seen.pending ? seen.tick + 1 : seen.tick
|
|
4433
|
-
});
|
|
4434
|
-
return seen.tick;
|
|
4435
|
-
}
|
|
4436
|
-
function useSessionDetail(sessionId) {
|
|
4437
|
-
const { api, onUnauthorized } = useSessionWorkspace();
|
|
4438
|
-
const [state, setState] = useState({ kind: "loading" });
|
|
4439
|
-
const [request, setRequest] = useState({
|
|
4440
|
-
count: 0,
|
|
4441
|
-
sessionId
|
|
4442
|
-
});
|
|
4443
|
-
const pollTick = usePollTick(state);
|
|
4444
|
-
const uploadTick = useLandedUploadTick();
|
|
4445
|
-
useEffect(() => {
|
|
4446
|
-
let isCurrent = true;
|
|
4447
|
-
api.detail(request.sessionId).then((detail) => isCurrent && setState({
|
|
4448
|
-
detail,
|
|
4449
|
-
kind: "ready"
|
|
4450
|
-
}), (error) => {
|
|
4451
|
-
if (isUnauthorized(error)) {
|
|
4452
|
-
onUnauthorized();
|
|
4453
|
-
return;
|
|
4454
|
-
}
|
|
4455
|
-
if (isCurrent) setState((current) => failedState(error, current));
|
|
4456
|
-
const isRefresh = request.count + pollTick + uploadTick > 0;
|
|
4457
|
-
if (isCurrent && isRefresh) reportFailure("The session did not refresh", error);
|
|
4458
|
-
});
|
|
4459
|
-
return () => {
|
|
4460
|
-
isCurrent = false;
|
|
4461
|
-
};
|
|
4462
|
-
}, [
|
|
4463
|
-
api,
|
|
4464
|
-
onUnauthorized,
|
|
4465
|
-
request,
|
|
4466
|
-
pollTick,
|
|
4467
|
-
uploadTick
|
|
4468
|
-
]);
|
|
4469
|
-
return {
|
|
4470
|
-
patch: (update) => setState((current) => current.kind === "ready" ? {
|
|
4471
|
-
detail: update(current.detail),
|
|
4472
|
-
kind: "ready"
|
|
4473
|
-
} : current),
|
|
4474
|
-
reload: () => setRequest(nextRequest),
|
|
4475
|
-
state
|
|
4476
|
-
};
|
|
4477
|
-
}
|
|
4478
|
-
//#endregion
|
|
4479
4710
|
//#region src/sessions/ui/review/proposals/proposal-editor.tsx
|
|
4480
4711
|
const MAX_LIST_ITEMS = 8;
|
|
4481
4712
|
function lines(text) {
|
|
@@ -5252,73 +5483,6 @@ function UpdateCard({ context, continuation }) {
|
|
|
5252
5483
|
});
|
|
5253
5484
|
}
|
|
5254
5485
|
//#endregion
|
|
5255
|
-
//#region src/sessions/ui/review/transcript/remove-evidence.tsx
|
|
5256
|
-
const REMOVE_COPY = "Remove this screenshot? The image is deleted for good. Briefs you already downloaded keep their copy.";
|
|
5257
|
-
function useFocusWhen(shouldFocus) {
|
|
5258
|
-
const ref = useRef(null);
|
|
5259
|
-
useEffect(() => {
|
|
5260
|
-
if (shouldFocus) ref.current?.focus();
|
|
5261
|
-
}, [shouldFocus]);
|
|
5262
|
-
return ref;
|
|
5263
|
-
}
|
|
5264
|
-
function RemoveConfirmation({ evidence, onCancel, removal }) {
|
|
5265
|
-
const { api } = useSessionWorkspace();
|
|
5266
|
-
const run = useSessionAction();
|
|
5267
|
-
const cancelRef = useFocusWhen(true);
|
|
5268
|
-
const remove = async () => {
|
|
5269
|
-
const removed = await run("The screenshot was not removed", () => api.removeEvidence(evidence.id));
|
|
5270
|
-
if (removed !== null) {
|
|
5271
|
-
removal.review.patch((detail) => withEvidence(detail, removed));
|
|
5272
|
-
removal.review.reload();
|
|
5273
|
-
removal.onRemoved(removed.id);
|
|
5274
|
-
announce("Screenshot removed");
|
|
5275
|
-
}
|
|
5276
|
-
};
|
|
5277
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
5278
|
-
className: "dt-ses-banner",
|
|
5279
|
-
children: [/* @__PURE__ */ jsx("p", { children: REMOVE_COPY }), /* @__PURE__ */ jsxs("div", {
|
|
5280
|
-
className: "dt-actions",
|
|
5281
|
-
children: [/* @__PURE__ */ jsx("button", {
|
|
5282
|
-
className: "dt-btn",
|
|
5283
|
-
"data-variant": "danger",
|
|
5284
|
-
onClick: remove,
|
|
5285
|
-
type: "button",
|
|
5286
|
-
children: "Remove"
|
|
5287
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
5288
|
-
className: "dt-btn",
|
|
5289
|
-
onClick: onCancel,
|
|
5290
|
-
ref: cancelRef,
|
|
5291
|
-
type: "button",
|
|
5292
|
-
children: "Cancel"
|
|
5293
|
-
})]
|
|
5294
|
-
})]
|
|
5295
|
-
});
|
|
5296
|
-
}
|
|
5297
|
-
function RemoveEvidence({ evidence, removal }) {
|
|
5298
|
-
const [isConfirming, setIsConfirming] = useState(false);
|
|
5299
|
-
const [hasCancelled, setHasCancelled] = useState(false);
|
|
5300
|
-
const triggerRef = useFocusWhen(hasCancelled);
|
|
5301
|
-
if (isConfirming) return /* @__PURE__ */ jsx(RemoveConfirmation, {
|
|
5302
|
-
evidence,
|
|
5303
|
-
onCancel: () => {
|
|
5304
|
-
setIsConfirming(false);
|
|
5305
|
-
setHasCancelled(true);
|
|
5306
|
-
},
|
|
5307
|
-
removal
|
|
5308
|
-
});
|
|
5309
|
-
return /* @__PURE__ */ jsx("button", {
|
|
5310
|
-
"aria-label": `Remove screenshot at ${formatClock(evidence.atMs)}`,
|
|
5311
|
-
className: "dt-ses-link",
|
|
5312
|
-
onClick: () => {
|
|
5313
|
-
setIsConfirming(true);
|
|
5314
|
-
setHasCancelled(false);
|
|
5315
|
-
},
|
|
5316
|
-
ref: triggerRef,
|
|
5317
|
-
type: "button",
|
|
5318
|
-
children: "Remove"
|
|
5319
|
-
});
|
|
5320
|
-
}
|
|
5321
|
-
//#endregion
|
|
5322
5486
|
//#region src/sessions/ui/review/moment/moment-labels.ts
|
|
5323
5487
|
const URL_PATH = /^[a-z][a-z\d+.-]*:\/\/[^/?#]*([^?#]*)/i;
|
|
5324
5488
|
const KIND_LABELS = {
|
|
@@ -5338,7 +5502,7 @@ function contextKindLabel(item) {
|
|
|
5338
5502
|
return KIND_LABELS[item.kind];
|
|
5339
5503
|
}
|
|
5340
5504
|
function pagePath(url) {
|
|
5341
|
-
if (!url) return null;
|
|
5505
|
+
if (!url || url.startsWith("file:")) return null;
|
|
5342
5506
|
return URL_PATH.exec(url)?.[1] || "/";
|
|
5343
5507
|
}
|
|
5344
5508
|
function contextLine$1(item) {
|
|
@@ -5852,69 +6016,262 @@ function MomentView({ handlers, segmentId, session }) {
|
|
|
5852
6016
|
});
|
|
5853
6017
|
}
|
|
5854
6018
|
//#endregion
|
|
5855
|
-
//#region src/sessions/ui/review/
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
x: left,
|
|
5868
|
-
y: top
|
|
6019
|
+
//#region src/sessions/ui/review/processing-steps.ts
|
|
6020
|
+
function uploadStep(pendingUploads) {
|
|
6021
|
+
return pendingUploads > 0 ? {
|
|
6022
|
+
analyzeLabel: null,
|
|
6023
|
+
key: "upload",
|
|
6024
|
+
label: "Uploading",
|
|
6025
|
+
state: "active"
|
|
6026
|
+
} : {
|
|
6027
|
+
analyzeLabel: null,
|
|
6028
|
+
key: "upload",
|
|
6029
|
+
label: "Uploaded",
|
|
6030
|
+
state: "done"
|
|
5869
6031
|
};
|
|
5870
6032
|
}
|
|
5871
|
-
function
|
|
5872
|
-
const
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
const unplaced = [];
|
|
5890
|
-
const reported = detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null);
|
|
5891
|
-
for (const proposal of reported) {
|
|
5892
|
-
const label = proposal.callout ?? proposal.need;
|
|
5893
|
-
const target = proposal.calloutTarget;
|
|
5894
|
-
const shot = target ? shots.get(target.evidenceId) : void 0;
|
|
5895
|
-
if (!(target && shot)) {
|
|
5896
|
-
unplaced.push({
|
|
5897
|
-
label,
|
|
5898
|
-
outline: null,
|
|
5899
|
-
proposal
|
|
5900
|
-
});
|
|
5901
|
-
continue;
|
|
5902
|
-
}
|
|
5903
|
-
const { frame } = shot.evidence;
|
|
5904
|
-
const outline = target.element && frame ? visibleFraction(target.element.rect, frame) : null;
|
|
5905
|
-
const screen = screensById.get(shot.evidence.id) ?? {
|
|
5906
|
-
...shot,
|
|
5907
|
-
callouts: []
|
|
6033
|
+
function transcriptionStep(detail, isAnalysisQueued) {
|
|
6034
|
+
const voice = detail.segments.filter((segment) => segment.source === "voice");
|
|
6035
|
+
const transcribing = voice.filter(({ transcription }) => transcription.state === "pending" || transcription.state === "running").length;
|
|
6036
|
+
const step = {
|
|
6037
|
+
analyzeLabel: null,
|
|
6038
|
+
key: "transcription"
|
|
6039
|
+
};
|
|
6040
|
+
if (transcribing > 0) {
|
|
6041
|
+
const transcribed = voice.length - transcribing;
|
|
6042
|
+
return {
|
|
6043
|
+
...step,
|
|
6044
|
+
label: "Transcribing your voice",
|
|
6045
|
+
progress: {
|
|
6046
|
+
done: transcribed,
|
|
6047
|
+
total: voice.length,
|
|
6048
|
+
valueText: `${transcribed} of ${countLabel(voice.length, "passage")}`
|
|
6049
|
+
},
|
|
6050
|
+
state: "active"
|
|
5908
6051
|
};
|
|
5909
|
-
screen.callouts.push({
|
|
5910
|
-
label,
|
|
5911
|
-
outline,
|
|
5912
|
-
proposal
|
|
5913
|
-
});
|
|
5914
|
-
screensById.set(shot.evidence.id, screen);
|
|
5915
6052
|
}
|
|
5916
|
-
|
|
5917
|
-
|
|
6053
|
+
if (voice.length > 0) return {
|
|
6054
|
+
...step,
|
|
6055
|
+
label: "Voice transcribed",
|
|
6056
|
+
state: "done"
|
|
6057
|
+
};
|
|
6058
|
+
return isAnalysisQueued ? {
|
|
6059
|
+
...step,
|
|
6060
|
+
label: "Transcribe your voice",
|
|
6061
|
+
state: "waiting"
|
|
6062
|
+
} : null;
|
|
6063
|
+
}
|
|
6064
|
+
function comparedRevision(detail) {
|
|
6065
|
+
const { continuation } = detail;
|
|
6066
|
+
return continuation?.intention && continuation.contribution.detachedAt === null ? continuation.contribution.baseRevision : null;
|
|
6067
|
+
}
|
|
6068
|
+
function intentionStep(detail, isAnalysisQueued) {
|
|
6069
|
+
const { session } = detail;
|
|
6070
|
+
const { analysis } = session;
|
|
6071
|
+
const revision = comparedRevision(detail);
|
|
6072
|
+
const isComparing = revision !== null;
|
|
6073
|
+
const step = {
|
|
6074
|
+
analyzeLabel: null,
|
|
6075
|
+
key: "intentions"
|
|
6076
|
+
};
|
|
6077
|
+
if (analysis.state === "running") {
|
|
6078
|
+
const label = isComparing ? `Comparing with revision ${revision}…` : "Finding intentions with AI…";
|
|
6079
|
+
return {
|
|
6080
|
+
...step,
|
|
6081
|
+
label,
|
|
6082
|
+
state: "active"
|
|
6083
|
+
};
|
|
6084
|
+
}
|
|
6085
|
+
if (analysis.state === "waiting" || isAnalysisQueued) {
|
|
6086
|
+
const label = isComparing ? `Compare with revision ${revision}` : "Find intentions with AI";
|
|
6087
|
+
return {
|
|
6088
|
+
...step,
|
|
6089
|
+
label,
|
|
6090
|
+
state: "waiting"
|
|
6091
|
+
};
|
|
6092
|
+
}
|
|
6093
|
+
if (analysis.state === "failed") {
|
|
6094
|
+
const failure = isComparing ? "The comparison failed" : "Analysis failed";
|
|
6095
|
+
const reason = analysis.error ?? "Unknown error.";
|
|
6096
|
+
return {
|
|
6097
|
+
...step,
|
|
6098
|
+
analyzeLabel: "Try again",
|
|
6099
|
+
label: `${failure}: ${reason}`,
|
|
6100
|
+
state: "failed"
|
|
6101
|
+
};
|
|
6102
|
+
}
|
|
6103
|
+
if (isAnalysisOutdated(session)) return {
|
|
6104
|
+
...step,
|
|
6105
|
+
analyzeLabel: "Analyze again",
|
|
6106
|
+
label: "The transcript changed since the analysis.",
|
|
6107
|
+
state: "waiting"
|
|
6108
|
+
};
|
|
6109
|
+
if (analysis.state === "idle") return detail.segments.length > 0 ? {
|
|
6110
|
+
...step,
|
|
6111
|
+
analyzeLabel: "Analyze",
|
|
6112
|
+
label: "Not analyzed yet.",
|
|
6113
|
+
state: "waiting"
|
|
6114
|
+
} : null;
|
|
6115
|
+
const label = isComparing ? `Compared with revision ${revision}` : `Found ${countLabel(analysis.proposalCount ?? 0, "intention")}`;
|
|
6116
|
+
return {
|
|
6117
|
+
...step,
|
|
6118
|
+
label,
|
|
6119
|
+
state: "done"
|
|
6120
|
+
};
|
|
6121
|
+
}
|
|
6122
|
+
function processingSteps(detail, pendingUploads) {
|
|
6123
|
+
if (detail.session.collection !== "ended") return [];
|
|
6124
|
+
const isAnalysisQueued = pendingUploads > 0 && detail.session.analysis.state === "idle";
|
|
6125
|
+
return [
|
|
6126
|
+
detail.session.isMine ? uploadStep(pendingUploads) : null,
|
|
6127
|
+
transcriptionStep(detail, isAnalysisQueued),
|
|
6128
|
+
intentionStep(detail, isAnalysisQueued)
|
|
6129
|
+
].filter((step) => step !== null);
|
|
6130
|
+
}
|
|
6131
|
+
//#endregion
|
|
6132
|
+
//#region src/sessions/ui/review/processing-progress.tsx
|
|
6133
|
+
function useProcessingSteps(detail) {
|
|
6134
|
+
const { account } = useSessionWorkspace();
|
|
6135
|
+
const sync = useSyncStatus(account.key);
|
|
6136
|
+
return processingSteps(detail, detail.session.isMine ? sync.pendingOperations : 0);
|
|
6137
|
+
}
|
|
6138
|
+
function AnalyzeButton({ detail, label, review }) {
|
|
6139
|
+
const { api } = useSessionWorkspace();
|
|
6140
|
+
const run = useSessionAction();
|
|
6141
|
+
const analyze = async () => {
|
|
6142
|
+
const status = await run("The analysis did not start", () => api.analyze(detail.session.id));
|
|
6143
|
+
if (status) review.patch((current) => ({
|
|
6144
|
+
...current,
|
|
6145
|
+
session: {
|
|
6146
|
+
...current.session,
|
|
6147
|
+
analysis: status
|
|
6148
|
+
}
|
|
6149
|
+
}));
|
|
6150
|
+
};
|
|
6151
|
+
return /* @__PURE__ */ jsx("button", {
|
|
6152
|
+
className: "dt-btn",
|
|
6153
|
+
onClick: analyze,
|
|
6154
|
+
type: "button",
|
|
6155
|
+
children: label
|
|
6156
|
+
});
|
|
6157
|
+
}
|
|
6158
|
+
function ProgressBar({ label, progress }) {
|
|
6159
|
+
if (!progress) return /* @__PURE__ */ jsx("span", {
|
|
6160
|
+
"aria-label": label,
|
|
6161
|
+
className: "dt-ses-bar",
|
|
6162
|
+
role: "progressbar",
|
|
6163
|
+
children: /* @__PURE__ */ jsx("span", { "data-indeterminate": "true" })
|
|
6164
|
+
});
|
|
6165
|
+
const { done, total, valueText } = progress;
|
|
6166
|
+
return /* @__PURE__ */ jsx("span", {
|
|
6167
|
+
"aria-label": label,
|
|
6168
|
+
"aria-valuemax": total,
|
|
6169
|
+
"aria-valuemin": 0,
|
|
6170
|
+
"aria-valuenow": done,
|
|
6171
|
+
"aria-valuetext": valueText,
|
|
6172
|
+
className: "dt-ses-bar",
|
|
6173
|
+
role: "progressbar",
|
|
6174
|
+
title: valueText,
|
|
6175
|
+
children: /* @__PURE__ */ jsx("span", { style: { width: `${done / total * 100}%` } })
|
|
6176
|
+
});
|
|
6177
|
+
}
|
|
6178
|
+
function StepRow({ detail, review, step }) {
|
|
6179
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
6180
|
+
"data-state": step.state,
|
|
6181
|
+
children: [
|
|
6182
|
+
/* @__PURE__ */ jsx("span", { children: step.label }),
|
|
6183
|
+
step.analyzeLabel && detail.session.canEdit && /* @__PURE__ */ jsx(AnalyzeButton, {
|
|
6184
|
+
detail,
|
|
6185
|
+
label: step.analyzeLabel,
|
|
6186
|
+
review
|
|
6187
|
+
}),
|
|
6188
|
+
step.state === "active" && /* @__PURE__ */ jsx(ProgressBar, {
|
|
6189
|
+
label: step.label,
|
|
6190
|
+
progress: step.progress
|
|
6191
|
+
})
|
|
6192
|
+
]
|
|
6193
|
+
});
|
|
6194
|
+
}
|
|
6195
|
+
function ProcessingProgress({ detail, fillsPanel, isProcessing, review, steps }) {
|
|
6196
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6197
|
+
"aria-live": "polite",
|
|
6198
|
+
className: "dt-ses-progress",
|
|
6199
|
+
"data-fills-panel": fillsPanel,
|
|
6200
|
+
children: isProcessing && /* @__PURE__ */ jsx("ol", {
|
|
6201
|
+
"aria-label": "Processing",
|
|
6202
|
+
className: "dt-ses-steps",
|
|
6203
|
+
children: steps.map((step) => /* @__PURE__ */ jsx(StepRow, {
|
|
6204
|
+
detail,
|
|
6205
|
+
review,
|
|
6206
|
+
step
|
|
6207
|
+
}, step.key))
|
|
6208
|
+
})
|
|
6209
|
+
});
|
|
6210
|
+
}
|
|
6211
|
+
//#endregion
|
|
6212
|
+
//#region src/sessions/ui/review/screens/callout-report.ts
|
|
6213
|
+
const clampFraction = (value) => Math.min(1, Math.max(0, value));
|
|
6214
|
+
function visibleFraction(rect, frame) {
|
|
6215
|
+
if (frame.viewportWidth <= 0 || frame.viewportHeight <= 0) return null;
|
|
6216
|
+
const left = clampFraction(rect.x / frame.viewportWidth);
|
|
6217
|
+
const top = clampFraction(rect.y / frame.viewportHeight);
|
|
6218
|
+
const right = clampFraction((rect.x + rect.width) / frame.viewportWidth);
|
|
6219
|
+
const bottom = clampFraction((rect.y + rect.height) / frame.viewportHeight);
|
|
6220
|
+
if (right <= left || bottom <= top) return null;
|
|
6221
|
+
return {
|
|
6222
|
+
height: bottom - top,
|
|
6223
|
+
width: right - left,
|
|
6224
|
+
x: left,
|
|
6225
|
+
y: top
|
|
6226
|
+
};
|
|
6227
|
+
}
|
|
6228
|
+
function shownScreenshots(detail) {
|
|
6229
|
+
const shots = /* @__PURE__ */ new Map();
|
|
6230
|
+
for (const evidence of detail.evidence) {
|
|
6231
|
+
const url = evidence.status === "available" ? evidence.image?.url : null;
|
|
6232
|
+
if (evidence.image && url) shots.set(evidence.id, {
|
|
6233
|
+
evidence,
|
|
6234
|
+
image: {
|
|
6235
|
+
...evidence.image,
|
|
6236
|
+
url
|
|
6237
|
+
}
|
|
6238
|
+
});
|
|
6239
|
+
}
|
|
6240
|
+
return shots;
|
|
6241
|
+
}
|
|
6242
|
+
const readingOrderY = (callout) => callout.outline?.y ?? 0;
|
|
6243
|
+
function calloutReport(detail) {
|
|
6244
|
+
const shots = shownScreenshots(detail);
|
|
6245
|
+
const screensById = /* @__PURE__ */ new Map();
|
|
6246
|
+
const unplaced = [];
|
|
6247
|
+
const reported = detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null);
|
|
6248
|
+
for (const proposal of reported) {
|
|
6249
|
+
const label = proposal.callout ?? proposal.need;
|
|
6250
|
+
const target = proposal.calloutTarget;
|
|
6251
|
+
const shot = target ? shots.get(target.evidenceId) : void 0;
|
|
6252
|
+
if (!(target && shot)) {
|
|
6253
|
+
unplaced.push({
|
|
6254
|
+
label,
|
|
6255
|
+
outline: null,
|
|
6256
|
+
proposal
|
|
6257
|
+
});
|
|
6258
|
+
continue;
|
|
6259
|
+
}
|
|
6260
|
+
const { frame } = shot.evidence;
|
|
6261
|
+
const outline = target.element && frame ? visibleFraction(target.element.rect, frame) : null;
|
|
6262
|
+
const screen = screensById.get(shot.evidence.id) ?? {
|
|
6263
|
+
...shot,
|
|
6264
|
+
callouts: []
|
|
6265
|
+
};
|
|
6266
|
+
screen.callouts.push({
|
|
6267
|
+
label,
|
|
6268
|
+
outline,
|
|
6269
|
+
proposal
|
|
6270
|
+
});
|
|
6271
|
+
screensById.set(shot.evidence.id, screen);
|
|
6272
|
+
}
|
|
6273
|
+
let number = 0;
|
|
6274
|
+
const numbered = (callout) => {
|
|
5918
6275
|
number += 1;
|
|
5919
6276
|
return {
|
|
5920
6277
|
...callout,
|
|
@@ -6036,6 +6393,150 @@ function layoutCallouts(options) {
|
|
|
6036
6393
|
};
|
|
6037
6394
|
}
|
|
6038
6395
|
//#endregion
|
|
6396
|
+
//#region src/sessions/ui/review/optimistic-change.ts
|
|
6397
|
+
function useOptimisticChange(review) {
|
|
6398
|
+
const run = useSessionAction();
|
|
6399
|
+
const applyChange = async (change) => {
|
|
6400
|
+
review.patch(change.show);
|
|
6401
|
+
const saved = await run(change.failureLabel, change.request, () => {
|
|
6402
|
+
applyChange(change);
|
|
6403
|
+
});
|
|
6404
|
+
if (saved === null) {
|
|
6405
|
+
review.patch(change.rollback);
|
|
6406
|
+
return null;
|
|
6407
|
+
}
|
|
6408
|
+
review.patch((detail) => change.settle(detail, saved));
|
|
6409
|
+
return saved;
|
|
6410
|
+
};
|
|
6411
|
+
return applyChange;
|
|
6412
|
+
}
|
|
6413
|
+
//#endregion
|
|
6414
|
+
//#region src/sessions/ui/review/proposals/use-proposal-actions.ts
|
|
6415
|
+
const NOTICE_NEED_LENGTH = 60;
|
|
6416
|
+
const DECISION_NOTICE = {
|
|
6417
|
+
dismissed: "Dismissed",
|
|
6418
|
+
kept: "Confirmed",
|
|
6419
|
+
later: "Saved for later",
|
|
6420
|
+
pending: "Back to undecided"
|
|
6421
|
+
};
|
|
6422
|
+
function quotedNeed(need) {
|
|
6423
|
+
return `“${need.length > NOTICE_NEED_LENGTH ? `${need.slice(0, 59)}…` : need}”`;
|
|
6424
|
+
}
|
|
6425
|
+
function decisionToggle(actions, proposal, decision) {
|
|
6426
|
+
return (event) => {
|
|
6427
|
+
if (event.detail > 1) return;
|
|
6428
|
+
actions.decide([proposal], proposal.decision === decision ? "pending" : decision);
|
|
6429
|
+
};
|
|
6430
|
+
}
|
|
6431
|
+
function useProposalSave(review) {
|
|
6432
|
+
const { api } = useSessionWorkspace();
|
|
6433
|
+
const run = useSessionAction();
|
|
6434
|
+
return async (failureLabel, request) => {
|
|
6435
|
+
const proposal = await run(failureLabel, () => request(api));
|
|
6436
|
+
if (proposal) review.patch((detail) => withProposal(detail, proposal));
|
|
6437
|
+
return proposal;
|
|
6438
|
+
};
|
|
6439
|
+
}
|
|
6440
|
+
function withDecisions(decisions) {
|
|
6441
|
+
return (detail) => ({
|
|
6442
|
+
...detail,
|
|
6443
|
+
proposals: detail.proposals.map((item) => {
|
|
6444
|
+
const decision = decisions.get(item.id);
|
|
6445
|
+
return decision ? {
|
|
6446
|
+
...item,
|
|
6447
|
+
decision
|
|
6448
|
+
} : item;
|
|
6449
|
+
})
|
|
6450
|
+
});
|
|
6451
|
+
}
|
|
6452
|
+
function decisionNotice(proposals, decision) {
|
|
6453
|
+
const [only] = proposals;
|
|
6454
|
+
const subject = proposals.length === 1 && only ? quotedNeed(only.need) : countLabel(proposals.length, "intention");
|
|
6455
|
+
return `${DECISION_NOTICE[decision]} ${subject}`;
|
|
6456
|
+
}
|
|
6457
|
+
function useDecide(review) {
|
|
6458
|
+
const { api } = useSessionWorkspace();
|
|
6459
|
+
const applyChange = useOptimisticChange(review);
|
|
6460
|
+
const changeDecisions = async (failureLabel, from, to) => {
|
|
6461
|
+
const saved = await applyChange({
|
|
6462
|
+
failureLabel,
|
|
6463
|
+
request: () => Promise.all([...to].map(([proposalId, decision]) => api.decideProposal({
|
|
6464
|
+
decision,
|
|
6465
|
+
proposalId
|
|
6466
|
+
}))),
|
|
6467
|
+
rollback: withDecisions(from),
|
|
6468
|
+
settle: (detail, proposals) => proposals.reduce(withProposal, detail),
|
|
6469
|
+
show: withDecisions(to)
|
|
6470
|
+
});
|
|
6471
|
+
if (!saved && to.size > 1) review.reload();
|
|
6472
|
+
return saved;
|
|
6473
|
+
};
|
|
6474
|
+
return async (proposals, decision) => {
|
|
6475
|
+
const previous = new Map(proposals.map((proposal) => [proposal.id, proposal.decision]));
|
|
6476
|
+
const next = new Map(proposals.map((proposal) => [proposal.id, decision]));
|
|
6477
|
+
if (!await changeDecisions("The decision was not saved", previous, next)) return;
|
|
6478
|
+
announce(decisionNotice(proposals, decision), {
|
|
6479
|
+
label: "Undo",
|
|
6480
|
+
run: async () => {
|
|
6481
|
+
if (await changeDecisions("The decision was not undone", next, previous)) announce("Decision undone");
|
|
6482
|
+
}
|
|
6483
|
+
});
|
|
6484
|
+
};
|
|
6485
|
+
}
|
|
6486
|
+
function withFeedbackIds(feedbackIds) {
|
|
6487
|
+
return (detail) => ({
|
|
6488
|
+
...detail,
|
|
6489
|
+
proposals: detail.proposals.map((item) => {
|
|
6490
|
+
const feedbackId = feedbackIds.get(item.id);
|
|
6491
|
+
return feedbackId ? {
|
|
6492
|
+
...item,
|
|
6493
|
+
feedbackId
|
|
6494
|
+
} : item;
|
|
6495
|
+
})
|
|
6496
|
+
});
|
|
6497
|
+
}
|
|
6498
|
+
function useAddToBoard(review) {
|
|
6499
|
+
const { api } = useSessionWorkspace();
|
|
6500
|
+
const run = useSessionAction();
|
|
6501
|
+
return async (proposals) => {
|
|
6502
|
+
const created = await Promise.all(proposals.map(async (proposal) => {
|
|
6503
|
+
const result = await run(`${quotedNeed(proposal.need)} was not added to the board`, () => api.createFeedback(proposal.id));
|
|
6504
|
+
return result ? [proposal.id, result.feedbackId] : null;
|
|
6505
|
+
}));
|
|
6506
|
+
const added = new Map(created.flatMap((entry) => entry ? [entry] : []));
|
|
6507
|
+
if (added.size === 0) return;
|
|
6508
|
+
review.patch(withFeedbackIds(added));
|
|
6509
|
+
const onlyAdded = added.size === 1 ? proposals.find((proposal) => added.has(proposal.id)) : void 0;
|
|
6510
|
+
announce(onlyAdded ? `Added ${quotedNeed(onlyAdded.need)} to the board` : `Added ${countLabel(added.size, "intention")} to the board`);
|
|
6511
|
+
};
|
|
6512
|
+
}
|
|
6513
|
+
function useProposalActions(review) {
|
|
6514
|
+
const save = useProposalSave(review);
|
|
6515
|
+
const decide = useDecide(review);
|
|
6516
|
+
return {
|
|
6517
|
+
addToBoard: useAddToBoard(review),
|
|
6518
|
+
clarify: (proposal, text) => save("The precision was not sent", (client) => client.clarifyProposal({
|
|
6519
|
+
expectedRevision: proposal.revision,
|
|
6520
|
+
proposalId: proposal.id,
|
|
6521
|
+
text
|
|
6522
|
+
})),
|
|
6523
|
+
decide,
|
|
6524
|
+
merge: async (targetId, sourceIds) => {
|
|
6525
|
+
if (await save("The intentions were not merged", (client) => client.mergeProposals({
|
|
6526
|
+
sourceIds,
|
|
6527
|
+
targetId
|
|
6528
|
+
}))) review.reload();
|
|
6529
|
+
},
|
|
6530
|
+
save,
|
|
6531
|
+
separate: (proposalId) => save("The suggestion was not cleared", (client) => client.separateProposal(proposalId)),
|
|
6532
|
+
verify: (proposalId, { note, status }) => save("The check was not saved", (client) => client.verifyProposal({
|
|
6533
|
+
note: note || void 0,
|
|
6534
|
+
proposalId,
|
|
6535
|
+
status
|
|
6536
|
+
}))
|
|
6537
|
+
};
|
|
6538
|
+
}
|
|
6539
|
+
//#endregion
|
|
6039
6540
|
//#region src/sessions/ui/review/screens/callout-pill.tsx
|
|
6040
6541
|
const TOGGLES = [{
|
|
6041
6542
|
decision: "kept",
|
|
@@ -6065,22 +6566,16 @@ function CalloutPill({ callout, context, pillRef, style }) {
|
|
|
6065
6566
|
type: "button",
|
|
6066
6567
|
children: label
|
|
6067
6568
|
}),
|
|
6068
|
-
context.canEdit && TOGGLES.map(({ decision, icon, verb }) => {
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
},
|
|
6079
|
-
title: verb,
|
|
6080
|
-
type: "button",
|
|
6081
|
-
children: icon
|
|
6082
|
-
}, decision);
|
|
6083
|
-
})
|
|
6569
|
+
context.canEdit && TOGGLES.map(({ decision, icon, verb }) => /* @__PURE__ */ jsx("button", {
|
|
6570
|
+
"aria-label": `${verb} “${label}”`,
|
|
6571
|
+
"aria-pressed": proposal.decision === decision,
|
|
6572
|
+
className: "dt-ses-callout-btn",
|
|
6573
|
+
"data-decision": decision,
|
|
6574
|
+
onClick: decisionToggle(context.actions, proposal, decision),
|
|
6575
|
+
title: verb,
|
|
6576
|
+
type: "button",
|
|
6577
|
+
children: icon
|
|
6578
|
+
}, decision))
|
|
6084
6579
|
]
|
|
6085
6580
|
});
|
|
6086
6581
|
}
|
|
@@ -6121,12 +6616,6 @@ function useMeasuredPills(measureKey) {
|
|
|
6121
6616
|
pillRef
|
|
6122
6617
|
};
|
|
6123
6618
|
}
|
|
6124
|
-
function screenCaption(screen) {
|
|
6125
|
-
const { evidence } = screen;
|
|
6126
|
-
const time = formatClock(evidence.capturedAtMs ?? evidence.atMs);
|
|
6127
|
-
const page = pagePath(evidence.frame?.url ?? null);
|
|
6128
|
-
return page ? `${time} · ${page}` : time;
|
|
6129
|
-
}
|
|
6130
6619
|
function CalloutMark({ callout, imageSize, imageTop }) {
|
|
6131
6620
|
const { outline, proposal } = callout;
|
|
6132
6621
|
if (!outline) return null;
|
|
@@ -6142,7 +6631,8 @@ function CalloutMark({ callout, imageSize, imageTop }) {
|
|
|
6142
6631
|
}
|
|
6143
6632
|
});
|
|
6144
6633
|
}
|
|
6145
|
-
function ScreenFigure({ context, screen }) {
|
|
6634
|
+
function ScreenFigure({ context, newPage, screen }) {
|
|
6635
|
+
const atMs = screen.evidence.capturedAtMs ?? screen.evidence.atMs;
|
|
6146
6636
|
const { callouts, image } = screen;
|
|
6147
6637
|
const { canvasRef, measured, pillRef } = useMeasuredPills(callouts.map(({ label, proposal }) => `${proposal.id}:${label}`).join("|"));
|
|
6148
6638
|
const width = measured?.width ?? 0;
|
|
@@ -6162,7 +6652,10 @@ function ScreenFigure({ context, screen }) {
|
|
|
6162
6652
|
const belowImage = layout ? layout.height - imageTop - imageSize.height : 0;
|
|
6163
6653
|
return /* @__PURE__ */ jsxs("figure", {
|
|
6164
6654
|
className: "dt-ses-report",
|
|
6165
|
-
children: [/* @__PURE__ */
|
|
6655
|
+
children: [/* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(ShotCaption, {
|
|
6656
|
+
atMs,
|
|
6657
|
+
children: newPage
|
|
6658
|
+
}) }), /* @__PURE__ */ jsxs("div", {
|
|
6166
6659
|
className: "dt-ses-report-canvas",
|
|
6167
6660
|
"data-measured": layout !== null,
|
|
6168
6661
|
ref: canvasRef,
|
|
@@ -6172,7 +6665,7 @@ function ScreenFigure({ context, screen }) {
|
|
|
6172
6665
|
},
|
|
6173
6666
|
children: [
|
|
6174
6667
|
/* @__PURE__ */ jsx("img", {
|
|
6175
|
-
alt: `Screenshot at ${
|
|
6668
|
+
alt: `Screenshot at ${formatClock(atMs)}`,
|
|
6176
6669
|
height: image.height,
|
|
6177
6670
|
src: image.url,
|
|
6178
6671
|
width: image.width
|
|
@@ -6220,15 +6713,17 @@ function ScreenFigure({ context, screen }) {
|
|
|
6220
6713
|
})
|
|
6221
6714
|
})
|
|
6222
6715
|
]
|
|
6223
|
-
}), /* @__PURE__ */ jsx("figcaption", {
|
|
6224
|
-
className: "dt-meta",
|
|
6225
|
-
children: screenCaption(screen)
|
|
6226
6716
|
})]
|
|
6227
6717
|
});
|
|
6228
6718
|
}
|
|
6229
6719
|
function ScreenReport({ context, report }) {
|
|
6230
|
-
|
|
6720
|
+
const figures = report.screens.map((screen) => ({
|
|
6721
|
+
page: pagePath(screen.evidence.frame?.url ?? null),
|
|
6722
|
+
screen
|
|
6723
|
+
}));
|
|
6724
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [figures.map(({ page, screen }, index) => /* @__PURE__ */ jsx(ScreenFigure, {
|
|
6231
6725
|
context,
|
|
6726
|
+
newPage: figures[index - 1]?.page === page ? null : page,
|
|
6232
6727
|
screen
|
|
6233
6728
|
}, screen.evidence.id)), report.unplaced.length > 0 && /* @__PURE__ */ jsxs("section", {
|
|
6234
6729
|
className: "dt-ses-block",
|
|
@@ -6439,141 +6934,6 @@ function StartSession({ onStarted, onWriteNote }) {
|
|
|
6439
6934
|
] });
|
|
6440
6935
|
}
|
|
6441
6936
|
//#endregion
|
|
6442
|
-
//#region src/sessions/ui/review/optimistic-change.ts
|
|
6443
|
-
function useOptimisticChange(review) {
|
|
6444
|
-
const run = useSessionAction();
|
|
6445
|
-
const applyChange = async (change) => {
|
|
6446
|
-
review.patch(change.show);
|
|
6447
|
-
const saved = await run(change.failureLabel, change.request, () => {
|
|
6448
|
-
applyChange(change);
|
|
6449
|
-
});
|
|
6450
|
-
if (saved === null) {
|
|
6451
|
-
review.patch(change.rollback);
|
|
6452
|
-
return null;
|
|
6453
|
-
}
|
|
6454
|
-
review.patch((detail) => change.settle(detail, saved));
|
|
6455
|
-
return saved;
|
|
6456
|
-
};
|
|
6457
|
-
return applyChange;
|
|
6458
|
-
}
|
|
6459
|
-
//#endregion
|
|
6460
|
-
//#region src/sessions/ui/review/proposals/use-proposal-actions.ts
|
|
6461
|
-
const NOTICE_NEED_LENGTH = 60;
|
|
6462
|
-
const DECISION_NOTICE = {
|
|
6463
|
-
dismissed: "Dismissed",
|
|
6464
|
-
kept: "Confirmed",
|
|
6465
|
-
later: "Saved for later",
|
|
6466
|
-
pending: "Back to undecided"
|
|
6467
|
-
};
|
|
6468
|
-
function quotedNeed(need) {
|
|
6469
|
-
return `“${need.length > NOTICE_NEED_LENGTH ? `${need.slice(0, 59)}…` : need}”`;
|
|
6470
|
-
}
|
|
6471
|
-
function useProposalSave(review) {
|
|
6472
|
-
const { api } = useSessionWorkspace();
|
|
6473
|
-
const run = useSessionAction();
|
|
6474
|
-
return async (failureLabel, request) => {
|
|
6475
|
-
const proposal = await run(failureLabel, () => request(api));
|
|
6476
|
-
if (proposal) review.patch((detail) => withProposal(detail, proposal));
|
|
6477
|
-
return proposal;
|
|
6478
|
-
};
|
|
6479
|
-
}
|
|
6480
|
-
function withDecision(proposalId, decision) {
|
|
6481
|
-
return (detail) => ({
|
|
6482
|
-
...detail,
|
|
6483
|
-
proposals: detail.proposals.map((item) => item.id === proposalId ? {
|
|
6484
|
-
...item,
|
|
6485
|
-
decision
|
|
6486
|
-
} : item)
|
|
6487
|
-
});
|
|
6488
|
-
}
|
|
6489
|
-
function useDecide(review) {
|
|
6490
|
-
const { api } = useSessionWorkspace();
|
|
6491
|
-
const applyChange = useOptimisticChange(review);
|
|
6492
|
-
const changeDecision = (options) => applyChange({
|
|
6493
|
-
failureLabel: options.failureLabel,
|
|
6494
|
-
request: () => api.decideProposal({
|
|
6495
|
-
decision: options.to,
|
|
6496
|
-
proposalId: options.proposalId
|
|
6497
|
-
}),
|
|
6498
|
-
rollback: withDecision(options.proposalId, options.from),
|
|
6499
|
-
settle: withProposal,
|
|
6500
|
-
show: withDecision(options.proposalId, options.to)
|
|
6501
|
-
});
|
|
6502
|
-
return async (proposal, decision) => {
|
|
6503
|
-
const previous = proposal.decision;
|
|
6504
|
-
if (!await changeDecision({
|
|
6505
|
-
failureLabel: "The decision was not saved",
|
|
6506
|
-
from: previous,
|
|
6507
|
-
proposalId: proposal.id,
|
|
6508
|
-
to: decision
|
|
6509
|
-
})) return;
|
|
6510
|
-
announce(`${DECISION_NOTICE[decision]} ${quotedNeed(proposal.need)}`, {
|
|
6511
|
-
label: "Undo",
|
|
6512
|
-
run: async () => {
|
|
6513
|
-
if (await changeDecision({
|
|
6514
|
-
failureLabel: "The decision was not undone",
|
|
6515
|
-
from: decision,
|
|
6516
|
-
proposalId: proposal.id,
|
|
6517
|
-
to: previous
|
|
6518
|
-
})) announce("Decision undone");
|
|
6519
|
-
}
|
|
6520
|
-
});
|
|
6521
|
-
};
|
|
6522
|
-
}
|
|
6523
|
-
function withFeedbackIds(feedbackIds) {
|
|
6524
|
-
return (detail) => ({
|
|
6525
|
-
...detail,
|
|
6526
|
-
proposals: detail.proposals.map((item) => {
|
|
6527
|
-
const feedbackId = feedbackIds.get(item.id);
|
|
6528
|
-
return feedbackId ? {
|
|
6529
|
-
...item,
|
|
6530
|
-
feedbackId
|
|
6531
|
-
} : item;
|
|
6532
|
-
})
|
|
6533
|
-
});
|
|
6534
|
-
}
|
|
6535
|
-
function useAddToBoard(review) {
|
|
6536
|
-
const { api } = useSessionWorkspace();
|
|
6537
|
-
const run = useSessionAction();
|
|
6538
|
-
return async (proposals) => {
|
|
6539
|
-
const created = await Promise.all(proposals.map(async (proposal) => {
|
|
6540
|
-
const result = await run(`${quotedNeed(proposal.need)} was not added to the board`, () => api.createFeedback(proposal.id));
|
|
6541
|
-
return result ? [proposal.id, result.feedbackId] : null;
|
|
6542
|
-
}));
|
|
6543
|
-
const added = new Map(created.flatMap((entry) => entry ? [entry] : []));
|
|
6544
|
-
if (added.size === 0) return;
|
|
6545
|
-
review.patch(withFeedbackIds(added));
|
|
6546
|
-
const onlyAdded = added.size === 1 ? proposals.find((proposal) => added.has(proposal.id)) : void 0;
|
|
6547
|
-
announce(onlyAdded ? `Added ${quotedNeed(onlyAdded.need)} to the board` : `Added ${countLabel(added.size, "intention")} to the board`);
|
|
6548
|
-
};
|
|
6549
|
-
}
|
|
6550
|
-
function useProposalActions(review) {
|
|
6551
|
-
const save = useProposalSave(review);
|
|
6552
|
-
const decide = useDecide(review);
|
|
6553
|
-
return {
|
|
6554
|
-
addToBoard: useAddToBoard(review),
|
|
6555
|
-
clarify: (proposal, text) => save("The precision was not sent", (client) => client.clarifyProposal({
|
|
6556
|
-
expectedRevision: proposal.revision,
|
|
6557
|
-
proposalId: proposal.id,
|
|
6558
|
-
text
|
|
6559
|
-
})),
|
|
6560
|
-
decide,
|
|
6561
|
-
merge: async (targetId, sourceIds) => {
|
|
6562
|
-
if (await save("The intentions were not merged", (client) => client.mergeProposals({
|
|
6563
|
-
sourceIds,
|
|
6564
|
-
targetId
|
|
6565
|
-
}))) review.reload();
|
|
6566
|
-
},
|
|
6567
|
-
save,
|
|
6568
|
-
separate: (proposalId) => save("The suggestion was not cleared", (client) => client.separateProposal(proposalId)),
|
|
6569
|
-
verify: (proposalId, { note, status }) => save("The check was not saved", (client) => client.verifyProposal({
|
|
6570
|
-
note: note || void 0,
|
|
6571
|
-
proposalId,
|
|
6572
|
-
status
|
|
6573
|
-
}))
|
|
6574
|
-
};
|
|
6575
|
-
}
|
|
6576
|
-
//#endregion
|
|
6577
6937
|
//#region src/sessions/ui/review/proposals/delete-intention.tsx
|
|
6578
6938
|
const DELETE_INTENTION_COPY = "Delete this intention? What later sessions added to it goes too, and so do the briefs Reflet saved that include it. Briefs you already copied or downloaded keep their text.";
|
|
6579
6939
|
function DeleteConfirmation$1({ context, onCancel, proposal }) {
|
|
@@ -6884,32 +7244,26 @@ function IntentionSource({ context, proposal }) {
|
|
|
6884
7244
|
if (!segment) return null;
|
|
6885
7245
|
const [firstContext] = segment.context;
|
|
6886
7246
|
const range = `${formatClock(segment.startMs)}–${formatClock(segment.endMs)}`;
|
|
6887
|
-
return /* @__PURE__ */ jsxs("
|
|
6888
|
-
className: "dt-ses-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
className: "dt-ses-link",
|
|
6908
|
-
onClick: (event) => context.navigation.onShowMoment(segment.id, event.currentTarget),
|
|
6909
|
-
type: "button",
|
|
6910
|
-
children: "Show context"
|
|
6911
|
-
})
|
|
6912
|
-
]
|
|
7247
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
7248
|
+
className: "dt-ses-source-btn",
|
|
7249
|
+
onClick: (event) => context.navigation.onShowMoment(segment.id, event.currentTarget),
|
|
7250
|
+
title: "Show the moment",
|
|
7251
|
+
type: "button",
|
|
7252
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
7253
|
+
className: "dt-ses-source",
|
|
7254
|
+
children: [
|
|
7255
|
+
/* @__PURE__ */ jsx("span", {
|
|
7256
|
+
className: "dt-ses-time",
|
|
7257
|
+
children: range
|
|
7258
|
+
}),
|
|
7259
|
+
" “",
|
|
7260
|
+
segment.text,
|
|
7261
|
+
"”"
|
|
7262
|
+
]
|
|
7263
|
+
}), firstContext && /* @__PURE__ */ jsx("span", {
|
|
7264
|
+
className: "dt-meta",
|
|
7265
|
+
children: contextLine$1(firstContext)
|
|
7266
|
+
})]
|
|
6913
7267
|
});
|
|
6914
7268
|
}
|
|
6915
7269
|
//#endregion
|
|
@@ -7156,29 +7510,25 @@ function MergeSuggestion({ context, proposal }) {
|
|
|
7156
7510
|
});
|
|
7157
7511
|
}
|
|
7158
7512
|
function DecisionButtons({ context, onEdit, proposal }) {
|
|
7159
|
-
const
|
|
7160
|
-
if (proposal.decision !== decision) context.actions.decide(proposal, decision);
|
|
7161
|
-
};
|
|
7513
|
+
const toggle = (decision) => decisionToggle(context.actions, proposal, decision);
|
|
7162
7514
|
const isKept = proposal.decision === "kept";
|
|
7163
7515
|
return /* @__PURE__ */ jsxs("div", {
|
|
7164
7516
|
className: "dt-actions",
|
|
7165
7517
|
children: [
|
|
7166
7518
|
/* @__PURE__ */ jsxs("button", {
|
|
7167
|
-
"aria-disabled": isKept,
|
|
7168
7519
|
"aria-pressed": isKept,
|
|
7169
7520
|
className: "dt-btn",
|
|
7170
7521
|
"data-variant": "primary",
|
|
7171
|
-
onClick:
|
|
7522
|
+
onClick: toggle("kept"),
|
|
7172
7523
|
type: "button",
|
|
7173
7524
|
children: [/* @__PURE__ */ jsx(CheckIcon, { size: 14 }), "Confirm"]
|
|
7174
7525
|
}),
|
|
7175
7526
|
ICON_DECISIONS.map(({ decision, icon, label }) => /* @__PURE__ */ jsx("button", {
|
|
7176
|
-
"aria-disabled": proposal.decision === decision,
|
|
7177
7527
|
"aria-label": label,
|
|
7178
7528
|
"aria-pressed": proposal.decision === decision,
|
|
7179
7529
|
className: "dt-btn",
|
|
7180
7530
|
"data-icon-only": true,
|
|
7181
|
-
onClick:
|
|
7531
|
+
onClick: toggle(decision),
|
|
7182
7532
|
title: label,
|
|
7183
7533
|
type: "button",
|
|
7184
7534
|
children: icon
|
|
@@ -7356,87 +7706,57 @@ function ProposalCard({ context, proposal }) {
|
|
|
7356
7706
|
//#endregion
|
|
7357
7707
|
//#region src/sessions/ui/review/proposals/proposals-tab.tsx
|
|
7358
7708
|
function emptyCopy(detail) {
|
|
7359
|
-
|
|
7360
|
-
if (session.collection !== "ended") return "Intentions appear here once the recording has uploaded and Reflet has read it.";
|
|
7709
|
+
if (detail.session.collection !== "ended") return "Intentions appear here once the recording has uploaded and Reflet has read it.";
|
|
7361
7710
|
if (detail.segments.length === 0 && detail.marks.length === 0) return "Nothing was said or marked in this session.";
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
return
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
sessionId
|
|
7381
|
-
}))) onDone();
|
|
7382
|
-
} finally {
|
|
7383
|
-
setIsSaving(false);
|
|
7384
|
-
}
|
|
7385
|
-
};
|
|
7386
|
-
return {
|
|
7387
|
-
add,
|
|
7388
|
-
isSaving,
|
|
7389
|
-
need,
|
|
7390
|
-
observed,
|
|
7391
|
-
setNeed,
|
|
7392
|
-
setObserved
|
|
7393
|
-
};
|
|
7394
|
-
}
|
|
7395
|
-
function AddProposalForm({ context, onDone }) {
|
|
7396
|
-
const { add, isSaving, need, observed, setNeed, setObserved } = useAddProposal(context, onDone);
|
|
7397
|
-
return /* @__PURE__ */ jsxs("form", {
|
|
7398
|
-
className: "dt-card dt-ses-form",
|
|
7399
|
-
onSubmit: (event) => {
|
|
7400
|
-
event.preventDefault();
|
|
7401
|
-
add();
|
|
7402
|
-
},
|
|
7403
|
-
children: [
|
|
7404
|
-
/* @__PURE__ */ jsx(TextField, {
|
|
7405
|
-
label: "Need",
|
|
7406
|
-
maxLength: 300,
|
|
7407
|
-
onChange: setNeed,
|
|
7408
|
-
rows: 2,
|
|
7409
|
-
value: need
|
|
7410
|
-
}),
|
|
7411
|
-
/* @__PURE__ */ jsx(TextField, {
|
|
7412
|
-
label: "What you observed (optional)",
|
|
7413
|
-
maxLength: 2e3,
|
|
7414
|
-
onChange: setObserved,
|
|
7415
|
-
rows: 3,
|
|
7416
|
-
value: observed
|
|
7417
|
-
}),
|
|
7418
|
-
/* @__PURE__ */ jsx(FormActions, {
|
|
7419
|
-
isSubmitDisabled: isSaving || need.trim() === "",
|
|
7420
|
-
onCancel: onDone,
|
|
7421
|
-
submitLabel: "Add intention"
|
|
7422
|
-
})
|
|
7423
|
-
]
|
|
7711
|
+
return "No request stood out in this session.";
|
|
7712
|
+
}
|
|
7713
|
+
function ConfirmAll({ context, undecided }) {
|
|
7714
|
+
return /* @__PURE__ */ jsx("div", {
|
|
7715
|
+
className: "dt-actions",
|
|
7716
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
7717
|
+
className: "dt-btn",
|
|
7718
|
+
onClick: () => context.actions.decide(undecided, "kept"),
|
|
7719
|
+
type: "button",
|
|
7720
|
+
children: [
|
|
7721
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 14 }),
|
|
7722
|
+
"Confirm all",
|
|
7723
|
+
/* @__PURE__ */ jsx("span", {
|
|
7724
|
+
className: "dt-btn-count",
|
|
7725
|
+
children: undecided.length
|
|
7726
|
+
})
|
|
7727
|
+
]
|
|
7728
|
+
})
|
|
7424
7729
|
});
|
|
7425
7730
|
}
|
|
7426
7731
|
function ProposalsTab({ context, view }) {
|
|
7427
|
-
const
|
|
7428
|
-
const
|
|
7429
|
-
const
|
|
7430
|
-
const
|
|
7732
|
+
const { detail, review } = context;
|
|
7733
|
+
const steps = useProcessingSteps(detail);
|
|
7734
|
+
const isProcessing = steps.some((step) => step.state !== "done");
|
|
7735
|
+
const proposals = detail.proposals.filter((proposal) => proposal.mergedIntoId === null);
|
|
7736
|
+
const undecided = proposals.filter((proposal) => proposal.decision === "pending" && proposal.attachment === null);
|
|
7737
|
+
const { continuation } = detail;
|
|
7738
|
+
const hasNothingToReview = proposals.length === 0 && !continuation;
|
|
7739
|
+
const report = calloutReport(detail);
|
|
7431
7740
|
const showsScreens = report.screens.length > 0 && view === "screens";
|
|
7432
7741
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7742
|
+
/* @__PURE__ */ jsx(ProcessingProgress, {
|
|
7743
|
+
detail,
|
|
7744
|
+
fillsPanel: hasNothingToReview,
|
|
7745
|
+
isProcessing,
|
|
7746
|
+
review,
|
|
7747
|
+
steps
|
|
7748
|
+
}),
|
|
7433
7749
|
continuation && /* @__PURE__ */ jsx(UpdateCard, {
|
|
7434
7750
|
context,
|
|
7435
7751
|
continuation
|
|
7436
7752
|
}),
|
|
7437
|
-
|
|
7753
|
+
hasNothingToReview && !isProcessing && /* @__PURE__ */ jsx("p", {
|
|
7438
7754
|
className: "dt-empty",
|
|
7439
|
-
children: emptyCopy(
|
|
7755
|
+
children: emptyCopy(detail)
|
|
7756
|
+
}),
|
|
7757
|
+
context.canEdit && undecided.length > 1 && /* @__PURE__ */ jsx(ConfirmAll, {
|
|
7758
|
+
context,
|
|
7759
|
+
undecided
|
|
7440
7760
|
}),
|
|
7441
7761
|
showsScreens && /* @__PURE__ */ jsx(ScreenReport, {
|
|
7442
7762
|
context,
|
|
@@ -7445,57 +7765,41 @@ function ProposalsTab({ context, view }) {
|
|
|
7445
7765
|
proposals.filter((proposal) => !showsScreens || proposal.attachment !== null).map((proposal) => /* @__PURE__ */ jsx(ProposalCard, {
|
|
7446
7766
|
context,
|
|
7447
7767
|
proposal
|
|
7448
|
-
}, proposal.id))
|
|
7449
|
-
context.canEdit && (isAdding ? /* @__PURE__ */ jsx(AddProposalForm, {
|
|
7450
|
-
context,
|
|
7451
|
-
onDone: () => setIsAdding(false)
|
|
7452
|
-
}) : /* @__PURE__ */ jsx("div", {
|
|
7453
|
-
className: "dt-actions",
|
|
7454
|
-
children: /* @__PURE__ */ jsx("button", {
|
|
7455
|
-
className: "dt-btn",
|
|
7456
|
-
onClick: () => setIsAdding(true),
|
|
7457
|
-
type: "button",
|
|
7458
|
-
children: "Add an intention"
|
|
7459
|
-
})
|
|
7460
|
-
}))
|
|
7768
|
+
}, proposal.id))
|
|
7461
7769
|
] });
|
|
7462
7770
|
}
|
|
7463
7771
|
//#endregion
|
|
7464
7772
|
//#region src/sessions/ui/review/review-footer.tsx
|
|
7773
|
+
const NOTHING_CONFIRMED = "Confirm an intention first";
|
|
7465
7774
|
function ReviewFooter({ context, onOpenBrief }) {
|
|
7466
7775
|
const confirmed = keptProposals(context.detail);
|
|
7467
7776
|
const offBoard = confirmed.filter((proposal) => proposal.feedbackId === null);
|
|
7777
|
+
const hasConfirmed = confirmed.length > 0;
|
|
7778
|
+
const boardBlocker = hasConfirmed ? "Every confirmed intention is on the board" : NOTHING_CONFIRMED;
|
|
7468
7779
|
return /* @__PURE__ */ jsxs("div", {
|
|
7469
|
-
className: "dt-sheet-foot",
|
|
7470
|
-
children: [
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
offBoard.length
|
|
7484
|
-
|
|
7485
|
-
|
|
7486
|
-
|
|
7487
|
-
|
|
7488
|
-
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
}),
|
|
7493
|
-
confirmed.length === 0 && /* @__PURE__ */ jsx("span", {
|
|
7494
|
-
className: "dt-status",
|
|
7495
|
-
id: "rf-brief-hint",
|
|
7496
|
-
children: "Confirm an intention to brief your agent or add it to the board"
|
|
7497
|
-
})
|
|
7498
|
-
]
|
|
7780
|
+
className: "dt-sheet-foot dt-ses-foot",
|
|
7781
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
7782
|
+
className: "dt-btn",
|
|
7783
|
+
"data-variant": "primary",
|
|
7784
|
+
disabled: !hasConfirmed,
|
|
7785
|
+
onClick: onOpenBrief,
|
|
7786
|
+
title: hasConfirmed ? void 0 : NOTHING_CONFIRMED,
|
|
7787
|
+
type: "button",
|
|
7788
|
+
children: ["Prepare brief", hasConfirmed && /* @__PURE__ */ jsx("span", {
|
|
7789
|
+
className: "dt-btn-count",
|
|
7790
|
+
children: confirmed.length
|
|
7791
|
+
})]
|
|
7792
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
7793
|
+
className: "dt-btn",
|
|
7794
|
+
disabled: offBoard.length === 0,
|
|
7795
|
+
onClick: () => context.actions.addToBoard(offBoard),
|
|
7796
|
+
title: offBoard.length === 0 ? boardBlocker : void 0,
|
|
7797
|
+
type: "button",
|
|
7798
|
+
children: ["Add to board", offBoard.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
7799
|
+
className: "dt-btn-count",
|
|
7800
|
+
children: offBoard.length
|
|
7801
|
+
})]
|
|
7802
|
+
})]
|
|
7499
7803
|
});
|
|
7500
7804
|
}
|
|
7501
7805
|
//#endregion
|
|
@@ -7750,6 +8054,24 @@ function DeleteConfirmation({ onCancel, onDeleted, sessionId }) {
|
|
|
7750
8054
|
})]
|
|
7751
8055
|
});
|
|
7752
8056
|
}
|
|
8057
|
+
function SessionFacts({ session }) {
|
|
8058
|
+
const isShared = session.visibility === "team";
|
|
8059
|
+
const sharing = isShared ? "Shared with team" : "Private";
|
|
8060
|
+
return /* @__PURE__ */ jsxs("p", {
|
|
8061
|
+
className: "dt-ses-facts",
|
|
8062
|
+
children: [
|
|
8063
|
+
/* @__PURE__ */ jsx("span", { children: formatDate(session.startedAt) }),
|
|
8064
|
+
/* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx(ClockIcon, {}), shortDuration(session.durationMs)] }),
|
|
8065
|
+
/* @__PURE__ */ jsx("span", {
|
|
8066
|
+
"aria-label": sharing,
|
|
8067
|
+
role: "img",
|
|
8068
|
+
title: sharing,
|
|
8069
|
+
children: isShared ? /* @__PURE__ */ jsx(TeamIcon, {}) : /* @__PURE__ */ jsx(LockIcon, {})
|
|
8070
|
+
}),
|
|
8071
|
+
!session.isMine && /* @__PURE__ */ jsx("span", { children: session.authorName ?? "A teammate" })
|
|
8072
|
+
]
|
|
8073
|
+
});
|
|
8074
|
+
}
|
|
7753
8075
|
function ReviewHeader({ onDeleted, review, session }) {
|
|
7754
8076
|
const [isRenaming, setIsRenaming] = useState(false);
|
|
7755
8077
|
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);
|
|
@@ -7772,44 +8094,34 @@ function ReviewHeader({ onDeleted, review, session }) {
|
|
|
7772
8094
|
];
|
|
7773
8095
|
return /* @__PURE__ */ jsxs("header", {
|
|
7774
8096
|
className: "dt-ses-head",
|
|
7775
|
-
children: [
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
8097
|
+
children: [isRenaming ? /* @__PURE__ */ jsx(TitleEditor, {
|
|
8098
|
+
onDone: () => setIsRenaming(false),
|
|
8099
|
+
review,
|
|
8100
|
+
session
|
|
8101
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
8102
|
+
className: "dt-card-head",
|
|
8103
|
+
children: [
|
|
8104
|
+
/* @__PURE__ */ jsx("h2", {
|
|
7783
8105
|
className: "dt-ses-title",
|
|
7784
8106
|
tabIndex: -1,
|
|
8107
|
+
title: session.title,
|
|
7785
8108
|
children: session.title
|
|
7786
|
-
}),
|
|
8109
|
+
}),
|
|
8110
|
+
/* @__PURE__ */ jsx(SessionFacts, { session }),
|
|
8111
|
+
session.canEdit && /* @__PURE__ */ jsx(Menu, {
|
|
7787
8112
|
items: menuItems,
|
|
7788
8113
|
trigger: {
|
|
7789
8114
|
className: "icon-btn",
|
|
7790
8115
|
content: /* @__PURE__ */ jsx(MoreIcon, {}),
|
|
7791
8116
|
label: "Session options"
|
|
7792
8117
|
}
|
|
7793
|
-
})
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
shortDuration(session.durationMs),
|
|
7801
|
-
" ·",
|
|
7802
|
-
" ",
|
|
7803
|
-
isShared ? "Shared with team" : "Private",
|
|
7804
|
-
session.isMine ? "" : ` · ${session.authorName ?? "A teammate"}`
|
|
7805
|
-
]
|
|
7806
|
-
}),
|
|
7807
|
-
isConfirmingDelete && session.canEdit && /* @__PURE__ */ jsx(DeleteConfirmation, {
|
|
7808
|
-
onCancel: () => setIsConfirmingDelete(false),
|
|
7809
|
-
onDeleted,
|
|
7810
|
-
sessionId: session.id
|
|
7811
|
-
})
|
|
7812
|
-
]
|
|
8118
|
+
})
|
|
8119
|
+
]
|
|
8120
|
+
}), isConfirmingDelete && session.canEdit && /* @__PURE__ */ jsx(DeleteConfirmation, {
|
|
8121
|
+
onCancel: () => setIsConfirmingDelete(false),
|
|
8122
|
+
onDeleted,
|
|
8123
|
+
sessionId: session.id
|
|
8124
|
+
})]
|
|
7813
8125
|
});
|
|
7814
8126
|
}
|
|
7815
8127
|
//#endregion
|
|
@@ -7835,210 +8147,51 @@ function IntentionViewMenu({ place }) {
|
|
|
7835
8147
|
}
|
|
7836
8148
|
});
|
|
7837
8149
|
}
|
|
7838
|
-
function tabOptions(detail) {
|
|
7839
|
-
return [
|
|
7840
|
-
{
|
|
7841
|
-
count: detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null).length,
|
|
7842
|
-
icon: /* @__PURE__ */ jsx(IntentionIcon, {}),
|
|
7843
|
-
label: "Intentions",
|
|
7844
|
-
value: "proposals"
|
|
7845
|
-
},
|
|
7846
|
-
{
|
|
7847
|
-
count: null,
|
|
7848
|
-
icon: /* @__PURE__ */ jsx(TranscriptIcon, {}),
|
|
7849
|
-
label: "Transcript",
|
|
7850
|
-
value: "transcript"
|
|
7851
|
-
},
|
|
7852
|
-
{
|
|
7853
|
-
count: detail.evidence.length,
|
|
7854
|
-
icon: /* @__PURE__ */ jsx(ImageIcon, {}),
|
|
7855
|
-
label: "
|
|
7856
|
-
value: "evidence"
|
|
7857
|
-
}
|
|
7858
|
-
];
|
|
7859
|
-
}
|
|
7860
|
-
function ReviewTabs({ detail, place }) {
|
|
7861
|
-
const canPickView = place.tab === "proposals" && calloutReport(detail).screens.length > 0;
|
|
7862
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
7863
|
-
className: "dt-toolbar",
|
|
7864
|
-
children: [/* @__PURE__ */ jsx("div", {
|
|
7865
|
-
className: "dt-tabs",
|
|
7866
|
-
role: "tablist",
|
|
7867
|
-
children: tabOptions(detail).map((option) => /* @__PURE__ */ jsxs("button", {
|
|
7868
|
-
"aria-selected": option.value === place.tab,
|
|
7869
|
-
className: "dt-tab",
|
|
7870
|
-
onClick: () => place.selectTab(option.value),
|
|
7871
|
-
role: "tab",
|
|
7872
|
-
type: "button",
|
|
7873
|
-
children: [
|
|
7874
|
-
option.icon,
|
|
7875
|
-
option.label,
|
|
7876
|
-
option.count !== null && /* @__PURE__ */ jsx("span", {
|
|
7877
|
-
className: "dt-tab-count",
|
|
7878
|
-
children: option.count
|
|
7879
|
-
})
|
|
7880
|
-
]
|
|
7881
|
-
}, option.value))
|
|
7882
|
-
}), canPickView && /* @__PURE__ */ jsx(IntentionViewMenu, { place })]
|
|
7883
|
-
});
|
|
7884
|
-
}
|
|
7885
|
-
//#endregion
|
|
7886
|
-
//#region src/sessions/ui/review/processing-steps.ts
|
|
7887
|
-
function uploadStep(pendingUploads) {
|
|
7888
|
-
return pendingUploads > 0 ? {
|
|
7889
|
-
analyzeLabel: null,
|
|
7890
|
-
key: "upload",
|
|
7891
|
-
label: `Uploading · ${pendingUploads} waiting`,
|
|
7892
|
-
state: "active"
|
|
7893
|
-
} : {
|
|
7894
|
-
analyzeLabel: null,
|
|
7895
|
-
key: "upload",
|
|
7896
|
-
label: "Uploaded",
|
|
7897
|
-
state: "done"
|
|
7898
|
-
};
|
|
7899
|
-
}
|
|
7900
|
-
function transcriptionStep(detail, isAnalysisQueued) {
|
|
7901
|
-
const voice = detail.segments.filter((segment) => segment.source === "voice");
|
|
7902
|
-
const transcribing = voice.filter(({ transcription }) => transcription.state === "pending" || transcription.state === "running").length;
|
|
7903
|
-
const step = {
|
|
7904
|
-
analyzeLabel: null,
|
|
7905
|
-
key: "transcription"
|
|
7906
|
-
};
|
|
7907
|
-
if (transcribing > 0) {
|
|
7908
|
-
const transcribed = voice.length - transcribing;
|
|
7909
|
-
return {
|
|
7910
|
-
...step,
|
|
7911
|
-
label: `Transcribing your voice · ${transcribed} of ${voice.length}`,
|
|
7912
|
-
state: "active"
|
|
7913
|
-
};
|
|
7914
|
-
}
|
|
7915
|
-
if (voice.length > 0) return {
|
|
7916
|
-
...step,
|
|
7917
|
-
label: "Voice transcribed",
|
|
7918
|
-
state: "done"
|
|
7919
|
-
};
|
|
7920
|
-
return isAnalysisQueued ? {
|
|
7921
|
-
...step,
|
|
7922
|
-
label: "Transcribe your voice",
|
|
7923
|
-
state: "waiting"
|
|
7924
|
-
} : null;
|
|
7925
|
-
}
|
|
7926
|
-
function comparedRevision(detail) {
|
|
7927
|
-
const { continuation } = detail;
|
|
7928
|
-
return continuation?.intention && continuation.contribution.detachedAt === null ? continuation.contribution.baseRevision : null;
|
|
7929
|
-
}
|
|
7930
|
-
function intentionStep(detail, isAnalysisQueued) {
|
|
7931
|
-
const { session } = detail;
|
|
7932
|
-
const { analysis } = session;
|
|
7933
|
-
const revision = comparedRevision(detail);
|
|
7934
|
-
const isComparing = revision !== null;
|
|
7935
|
-
const step = {
|
|
7936
|
-
analyzeLabel: null,
|
|
7937
|
-
key: "intentions"
|
|
7938
|
-
};
|
|
7939
|
-
if (analysis.state === "running") {
|
|
7940
|
-
const label = isComparing ? `Comparing with revision ${revision}…` : "Finding intentions with AI…";
|
|
7941
|
-
return {
|
|
7942
|
-
...step,
|
|
7943
|
-
label,
|
|
7944
|
-
state: "active"
|
|
7945
|
-
};
|
|
7946
|
-
}
|
|
7947
|
-
if (analysis.state === "waiting" || isAnalysisQueued) {
|
|
7948
|
-
const label = isComparing ? `Compare with revision ${revision}` : "Find intentions with AI";
|
|
7949
|
-
return {
|
|
7950
|
-
...step,
|
|
7951
|
-
label,
|
|
7952
|
-
state: "waiting"
|
|
7953
|
-
};
|
|
7954
|
-
}
|
|
7955
|
-
if (analysis.state === "failed") {
|
|
7956
|
-
const failure = isComparing ? "The comparison failed" : "Analysis failed";
|
|
7957
|
-
const reason = analysis.error ?? "Unknown error.";
|
|
7958
|
-
return {
|
|
7959
|
-
...step,
|
|
7960
|
-
analyzeLabel: "Try again",
|
|
7961
|
-
label: `${failure}: ${reason}`,
|
|
7962
|
-
state: "failed"
|
|
7963
|
-
};
|
|
7964
|
-
}
|
|
7965
|
-
if (isAnalysisOutdated(session)) return {
|
|
7966
|
-
...step,
|
|
7967
|
-
analyzeLabel: "Analyze again",
|
|
7968
|
-
label: "The transcript changed since the analysis.",
|
|
7969
|
-
state: "waiting"
|
|
7970
|
-
};
|
|
7971
|
-
if (analysis.state === "idle") return detail.segments.length > 0 ? {
|
|
7972
|
-
...step,
|
|
7973
|
-
analyzeLabel: "Analyze",
|
|
7974
|
-
label: "Not analyzed yet.",
|
|
7975
|
-
state: "waiting"
|
|
7976
|
-
} : null;
|
|
7977
|
-
const label = isComparing ? `Compared with revision ${revision}` : `Found ${countLabel(analysis.proposalCount ?? 0, "intention")}`;
|
|
7978
|
-
return {
|
|
7979
|
-
...step,
|
|
7980
|
-
label,
|
|
7981
|
-
state: "done"
|
|
7982
|
-
};
|
|
7983
|
-
}
|
|
7984
|
-
function processingSteps(detail, pendingUploads) {
|
|
7985
|
-
if (detail.session.collection !== "ended") return [];
|
|
7986
|
-
const isAnalysisQueued = pendingUploads > 0 && detail.session.analysis.state === "idle";
|
|
7987
|
-
return [
|
|
7988
|
-
detail.session.isMine ? uploadStep(pendingUploads) : null,
|
|
7989
|
-
transcriptionStep(detail, isAnalysisQueued),
|
|
7990
|
-
intentionStep(detail, isAnalysisQueued)
|
|
7991
|
-
].filter((step) => step !== null);
|
|
7992
|
-
}
|
|
7993
|
-
//#endregion
|
|
7994
|
-
//#region src/sessions/ui/review/status-strip.tsx
|
|
7995
|
-
function AnalyzeButton({ detail, label, review }) {
|
|
7996
|
-
const { api } = useSessionWorkspace();
|
|
7997
|
-
const run = useSessionAction();
|
|
7998
|
-
const analyze = async () => {
|
|
7999
|
-
const status = await run("The analysis did not start", () => api.analyze(detail.session.id));
|
|
8000
|
-
if (status) review.patch((current) => ({
|
|
8001
|
-
...current,
|
|
8002
|
-
session: {
|
|
8003
|
-
...current.session,
|
|
8004
|
-
analysis: status
|
|
8005
|
-
}
|
|
8006
|
-
}));
|
|
8007
|
-
};
|
|
8008
|
-
return /* @__PURE__ */ jsx("button", {
|
|
8009
|
-
className: "dt-btn",
|
|
8010
|
-
onClick: analyze,
|
|
8011
|
-
type: "button",
|
|
8012
|
-
children: label
|
|
8013
|
-
});
|
|
8014
|
-
}
|
|
8015
|
-
function StepRow({ detail, review, step }) {
|
|
8016
|
-
return /* @__PURE__ */ jsxs("li", {
|
|
8017
|
-
"data-state": step.state,
|
|
8018
|
-
children: [/* @__PURE__ */ jsx("span", { children: step.label }), step.analyzeLabel && detail.session.canEdit && /* @__PURE__ */ jsx(AnalyzeButton, {
|
|
8019
|
-
detail,
|
|
8020
|
-
label: step.analyzeLabel,
|
|
8021
|
-
review
|
|
8022
|
-
})]
|
|
8023
|
-
});
|
|
8150
|
+
function tabOptions(detail) {
|
|
8151
|
+
return [
|
|
8152
|
+
{
|
|
8153
|
+
count: detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null).length,
|
|
8154
|
+
icon: /* @__PURE__ */ jsx(IntentionIcon, {}),
|
|
8155
|
+
label: "Intentions",
|
|
8156
|
+
value: "proposals"
|
|
8157
|
+
},
|
|
8158
|
+
{
|
|
8159
|
+
count: null,
|
|
8160
|
+
icon: /* @__PURE__ */ jsx(TranscriptIcon, {}),
|
|
8161
|
+
label: "Transcript",
|
|
8162
|
+
value: "transcript"
|
|
8163
|
+
},
|
|
8164
|
+
{
|
|
8165
|
+
count: detail.evidence.length,
|
|
8166
|
+
icon: /* @__PURE__ */ jsx(ImageIcon, {}),
|
|
8167
|
+
label: "Screenshots",
|
|
8168
|
+
value: "evidence"
|
|
8169
|
+
}
|
|
8170
|
+
];
|
|
8024
8171
|
}
|
|
8025
|
-
function
|
|
8026
|
-
const
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8172
|
+
function ReviewTabs({ detail, place }) {
|
|
8173
|
+
const canPickView = place.tab === "proposals" && calloutReport(detail).screens.length > 0;
|
|
8174
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
8175
|
+
className: "dt-toolbar",
|
|
8176
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
8177
|
+
className: "dt-tabs",
|
|
8178
|
+
role: "tablist",
|
|
8179
|
+
children: tabOptions(detail).map((option) => /* @__PURE__ */ jsxs("button", {
|
|
8180
|
+
"aria-selected": option.value === place.tab,
|
|
8181
|
+
className: "dt-tab",
|
|
8182
|
+
onClick: () => place.selectTab(option.value),
|
|
8183
|
+
role: "tab",
|
|
8184
|
+
type: "button",
|
|
8185
|
+
children: [
|
|
8186
|
+
option.icon,
|
|
8187
|
+
option.label,
|
|
8188
|
+
option.count !== null && /* @__PURE__ */ jsx("span", {
|
|
8189
|
+
className: "dt-tab-count",
|
|
8190
|
+
children: option.count
|
|
8191
|
+
})
|
|
8192
|
+
]
|
|
8193
|
+
}, option.value))
|
|
8194
|
+
}), canPickView && /* @__PURE__ */ jsx(IntentionViewMenu, { place })]
|
|
8042
8195
|
});
|
|
8043
8196
|
}
|
|
8044
8197
|
//#endregion
|
|
@@ -8190,17 +8343,16 @@ function LinkEvidenceItem({ link }) {
|
|
|
8190
8343
|
]
|
|
8191
8344
|
});
|
|
8192
8345
|
}
|
|
8193
|
-
function
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
});
|
|
8346
|
+
function EvidenceFace({ evidence }) {
|
|
8347
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ShotCaption, {
|
|
8348
|
+
atMs: evidence.atMs,
|
|
8349
|
+
children: evidenceCaption(evidence)
|
|
8350
|
+
}), /* @__PURE__ */ jsx(EvidenceThumb, {
|
|
8351
|
+
evidence,
|
|
8352
|
+
isLarge: true
|
|
8353
|
+
})] });
|
|
8202
8354
|
}
|
|
8203
|
-
function EvidenceItem({ evidence,
|
|
8355
|
+
function EvidenceItem({ evidence, onShowMoment, removal }) {
|
|
8204
8356
|
const caption = `${formatClock(evidence.atMs)} · ${evidenceCaption(evidence)}`;
|
|
8205
8357
|
if (evidence.link) return /* @__PURE__ */ jsx(LinkEvidenceItem, { link: evidence.link });
|
|
8206
8358
|
if (!isViewable(evidence)) return /* @__PURE__ */ jsx(MissingEvidenceItem, {
|
|
@@ -8209,27 +8361,22 @@ function EvidenceItem({ evidence, openers, removal }) {
|
|
|
8209
8361
|
isJustRemoved: evidence.id === removal.removedId
|
|
8210
8362
|
});
|
|
8211
8363
|
return /* @__PURE__ */ jsxs("li", {
|
|
8212
|
-
className: "dt-card",
|
|
8364
|
+
className: "dt-card dt-openable",
|
|
8213
8365
|
children: [
|
|
8214
|
-
/* @__PURE__ */ jsx("button", {
|
|
8215
|
-
"aria-label": `
|
|
8216
|
-
className: "dt-ses-shot-
|
|
8217
|
-
onClick:
|
|
8366
|
+
onShowMoment ? /* @__PURE__ */ jsx("button", {
|
|
8367
|
+
"aria-label": `Show the moment at ${caption}`,
|
|
8368
|
+
className: "dt-open dt-ses-shot-face",
|
|
8369
|
+
onClick: (event) => onShowMoment(event.currentTarget),
|
|
8218
8370
|
type: "button",
|
|
8219
|
-
children: /* @__PURE__ */ jsx(
|
|
8220
|
-
}),
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
children: caption
|
|
8371
|
+
children: /* @__PURE__ */ jsx(EvidenceFace, { evidence })
|
|
8372
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
8373
|
+
className: "dt-ses-shot-face",
|
|
8374
|
+
children: /* @__PURE__ */ jsx(EvidenceFace, { evidence })
|
|
8224
8375
|
}),
|
|
8225
8376
|
evidence.note && /* @__PURE__ */ jsx("p", {
|
|
8226
8377
|
className: "dt-note-text",
|
|
8227
8378
|
children: evidence.note
|
|
8228
8379
|
}),
|
|
8229
|
-
/* @__PURE__ */ jsx(MomentLink, {
|
|
8230
|
-
atMs: evidence.atMs,
|
|
8231
|
-
onShowMoment: openers.onShowMoment
|
|
8232
|
-
}),
|
|
8233
8380
|
removal.canRemove && /* @__PURE__ */ jsx(RemoveEvidence, {
|
|
8234
8381
|
evidence,
|
|
8235
8382
|
removal
|
|
@@ -8237,54 +8384,6 @@ function EvidenceItem({ evidence, openers, removal }) {
|
|
|
8237
8384
|
]
|
|
8238
8385
|
});
|
|
8239
8386
|
}
|
|
8240
|
-
function EnlargedEvidence({ evidence, onBack, removal }) {
|
|
8241
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8242
|
-
/* @__PURE__ */ jsx("button", {
|
|
8243
|
-
className: "dt-ses-link",
|
|
8244
|
-
onClick: onBack,
|
|
8245
|
-
type: "button",
|
|
8246
|
-
children: "← All evidence"
|
|
8247
|
-
}),
|
|
8248
|
-
/* @__PURE__ */ jsx(EvidenceThumb, {
|
|
8249
|
-
evidence,
|
|
8250
|
-
isLarge: true
|
|
8251
|
-
}),
|
|
8252
|
-
/* @__PURE__ */ jsxs("p", {
|
|
8253
|
-
className: "dt-meta",
|
|
8254
|
-
children: [
|
|
8255
|
-
formatClock(evidence.atMs),
|
|
8256
|
-
" · ",
|
|
8257
|
-
evidenceCaption(evidence)
|
|
8258
|
-
]
|
|
8259
|
-
}),
|
|
8260
|
-
removal.canRemove && /* @__PURE__ */ jsx(RemoveEvidence, {
|
|
8261
|
-
evidence,
|
|
8262
|
-
removal
|
|
8263
|
-
})
|
|
8264
|
-
] });
|
|
8265
|
-
}
|
|
8266
|
-
function EvidenceList({ detail, openers, removal }) {
|
|
8267
|
-
const { onEnlarge, onShowMoment } = openers;
|
|
8268
|
-
const { evidence } = detail;
|
|
8269
|
-
const momentOpener = (atMs) => {
|
|
8270
|
-
const segmentId = nearestSegmentId(detail.segments, atMs);
|
|
8271
|
-
return segmentId ? (opener) => onShowMoment(segmentId, opener) : null;
|
|
8272
|
-
};
|
|
8273
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [evidence.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
8274
|
-
className: "dt-empty",
|
|
8275
|
-
children: "No screenshots or links in this session."
|
|
8276
|
-
}), /* @__PURE__ */ jsx("ul", {
|
|
8277
|
-
className: "dt-ses-rows",
|
|
8278
|
-
children: evidence.map((item) => /* @__PURE__ */ jsx(EvidenceItem, {
|
|
8279
|
-
evidence: item,
|
|
8280
|
-
openers: {
|
|
8281
|
-
onEnlarge: () => onEnlarge(item.id),
|
|
8282
|
-
onShowMoment: momentOpener(item.atMs)
|
|
8283
|
-
},
|
|
8284
|
-
removal
|
|
8285
|
-
}, item.id))
|
|
8286
|
-
})] });
|
|
8287
|
-
}
|
|
8288
8387
|
function AttachLinkAction({ detail, review }) {
|
|
8289
8388
|
const [isAttaching, setIsAttaching] = useState(false);
|
|
8290
8389
|
if (isAttaching) return /* @__PURE__ */ jsx(AttachLink, {
|
|
@@ -8303,34 +8402,34 @@ function AttachLinkAction({ detail, review }) {
|
|
|
8303
8402
|
});
|
|
8304
8403
|
}
|
|
8305
8404
|
function EvidenceTab({ detail, onShowMoment, review }) {
|
|
8306
|
-
const [enlargedId, setEnlargedId] = useState(null);
|
|
8307
8405
|
const [removedId, setRemovedId] = useState(null);
|
|
8308
|
-
const
|
|
8406
|
+
const { canEdit } = detail.session;
|
|
8309
8407
|
const removal = {
|
|
8310
|
-
canRemove:
|
|
8311
|
-
onRemoved:
|
|
8312
|
-
setEnlargedId(null);
|
|
8313
|
-
setRemovedId(evidenceId);
|
|
8314
|
-
},
|
|
8408
|
+
canRemove: canEdit,
|
|
8409
|
+
onRemoved: setRemovedId,
|
|
8315
8410
|
removedId,
|
|
8316
8411
|
review
|
|
8317
8412
|
};
|
|
8318
|
-
|
|
8319
|
-
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(EvidenceList, {
|
|
8324
|
-
detail,
|
|
8325
|
-
openers: {
|
|
8326
|
-
onEnlarge: setEnlargedId,
|
|
8327
|
-
onShowMoment
|
|
8328
|
-
},
|
|
8329
|
-
removal
|
|
8330
|
-
}), detail.session.canEdit && /* @__PURE__ */ jsx(AttachLinkAction, {
|
|
8413
|
+
const momentOpener = (atMs) => {
|
|
8414
|
+
const segmentId = nearestSegmentId(detail.segments, atMs);
|
|
8415
|
+
return segmentId ? (opener) => onShowMoment(segmentId, opener) : null;
|
|
8416
|
+
};
|
|
8417
|
+
const attachLink = canEdit && /* @__PURE__ */ jsx(AttachLinkAction, {
|
|
8331
8418
|
detail,
|
|
8332
8419
|
review
|
|
8333
|
-
})
|
|
8420
|
+
});
|
|
8421
|
+
if (detail.evidence.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
8422
|
+
className: "dt-empty",
|
|
8423
|
+
children: [/* @__PURE__ */ jsx("p", { children: "No screenshots or links in this session." }), attachLink]
|
|
8424
|
+
});
|
|
8425
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("ul", {
|
|
8426
|
+
className: "dt-ses-rows",
|
|
8427
|
+
children: detail.evidence.map((item) => /* @__PURE__ */ jsx(EvidenceItem, {
|
|
8428
|
+
evidence: item,
|
|
8429
|
+
onShowMoment: momentOpener(item.atMs),
|
|
8430
|
+
removal
|
|
8431
|
+
}, item.id))
|
|
8432
|
+
}), attachLink] });
|
|
8334
8433
|
}
|
|
8335
8434
|
//#endregion
|
|
8336
8435
|
//#region src/sessions/ui/review/transcript/segment-row.tsx
|
|
@@ -8444,38 +8543,57 @@ function ContextChips({ segment }) {
|
|
|
8444
8543
|
}, `${item.kind}:${item.atMs}:${item.label}`))]
|
|
8445
8544
|
});
|
|
8446
8545
|
}
|
|
8447
|
-
function
|
|
8448
|
-
return /* @__PURE__ */
|
|
8449
|
-
className: "dt-
|
|
8450
|
-
children:
|
|
8451
|
-
|
|
8452
|
-
|
|
8453
|
-
|
|
8454
|
-
|
|
8455
|
-
|
|
8456
|
-
|
|
8457
|
-
|
|
8458
|
-
className: "dt-ses-link",
|
|
8459
|
-
onClick: onEdit,
|
|
8460
|
-
type: "button",
|
|
8461
|
-
children: "Edit"
|
|
8462
|
-
})]
|
|
8546
|
+
function MomentTime({ onShowMoment, time }) {
|
|
8547
|
+
if (!onShowMoment) return /* @__PURE__ */ jsx("span", {
|
|
8548
|
+
className: "dt-ses-time",
|
|
8549
|
+
children: time
|
|
8550
|
+
});
|
|
8551
|
+
return /* @__PURE__ */ jsx("button", {
|
|
8552
|
+
"aria-label": `Show the moment at ${time}`,
|
|
8553
|
+
className: "dt-ses-time dt-open",
|
|
8554
|
+
onClick: (event) => onShowMoment(event.currentTarget),
|
|
8555
|
+
type: "button",
|
|
8556
|
+
children: time
|
|
8463
8557
|
});
|
|
8464
8558
|
}
|
|
8559
|
+
const TEXT_DRAG_THRESHOLD_PX = 4;
|
|
8560
|
+
function useOpenMomentOnRowClick() {
|
|
8561
|
+
const pressedAt = useRef(null);
|
|
8562
|
+
return {
|
|
8563
|
+
onClick(event) {
|
|
8564
|
+
const start = pressedAt.current;
|
|
8565
|
+
const isTextDrag = start !== null && Math.hypot(event.clientX - start.x, event.clientY - start.y) > TEXT_DRAG_THRESHOLD_PX;
|
|
8566
|
+
const isOnControl = event.target instanceof Element && event.target.closest("a, button, input, select, textarea") !== null;
|
|
8567
|
+
if (isTextDrag || isOnControl) return;
|
|
8568
|
+
event.currentTarget.querySelector(":scope > .dt-open")?.click();
|
|
8569
|
+
},
|
|
8570
|
+
onMouseDown(event) {
|
|
8571
|
+
pressedAt.current = {
|
|
8572
|
+
x: event.clientX,
|
|
8573
|
+
y: event.clientY
|
|
8574
|
+
};
|
|
8575
|
+
}
|
|
8576
|
+
};
|
|
8577
|
+
}
|
|
8465
8578
|
function SegmentRow({ editing, onShowMoment, segment }) {
|
|
8466
8579
|
const { canEdit, review } = editing;
|
|
8467
8580
|
const [isEditing, setIsEditing] = useState(false);
|
|
8468
8581
|
const time = formatClock(segment.startMs);
|
|
8469
8582
|
const isSettled = ["done", "empty"].includes(segment.transcription.state);
|
|
8583
|
+
const canStartEditing = canEdit && isSettled && !isEditing;
|
|
8584
|
+
const rowClick = useOpenMomentOnRowClick();
|
|
8470
8585
|
return /* @__PURE__ */ jsxs("li", {
|
|
8471
|
-
className: "dt-ses-seg",
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8586
|
+
className: "dt-ses-seg dt-openable",
|
|
8587
|
+
onClick: rowClick.onClick,
|
|
8588
|
+
onMouseDown: rowClick.onMouseDown,
|
|
8589
|
+
children: [
|
|
8590
|
+
/* @__PURE__ */ jsx(MomentTime, {
|
|
8591
|
+
onShowMoment: isEditing ? null : onShowMoment,
|
|
8592
|
+
time
|
|
8593
|
+
}),
|
|
8594
|
+
/* @__PURE__ */ jsxs("div", {
|
|
8595
|
+
className: "dt-ses-seg-body",
|
|
8596
|
+
children: [isEditing ? /* @__PURE__ */ jsx(SegmentEditor, {
|
|
8479
8597
|
onDone: () => setIsEditing(false),
|
|
8480
8598
|
review,
|
|
8481
8599
|
segment
|
|
@@ -8483,15 +8601,20 @@ function SegmentRow({ editing, onShowMoment, segment }) {
|
|
|
8483
8601
|
onType: canEdit ? () => setIsEditing(true) : void 0,
|
|
8484
8602
|
review,
|
|
8485
8603
|
segment
|
|
8486
|
-
}),
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
time
|
|
8604
|
+
}), /* @__PURE__ */ jsx(ContextChips, { segment })]
|
|
8605
|
+
}),
|
|
8606
|
+
canStartEditing && /* @__PURE__ */ jsx("div", {
|
|
8607
|
+
className: "dt-hover-actions",
|
|
8608
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
8609
|
+
"aria-label": `Edit passage at ${time}`,
|
|
8610
|
+
className: "icon-btn glass",
|
|
8611
|
+
onClick: () => setIsEditing(true),
|
|
8612
|
+
title: "Edit",
|
|
8613
|
+
type: "button",
|
|
8614
|
+
children: /* @__PURE__ */ jsx(PencilIcon, {})
|
|
8492
8615
|
})
|
|
8493
|
-
|
|
8494
|
-
|
|
8616
|
+
})
|
|
8617
|
+
]
|
|
8495
8618
|
});
|
|
8496
8619
|
}
|
|
8497
8620
|
//#endregion
|
|
@@ -8573,25 +8696,21 @@ function transcriptForAgent(detail) {
|
|
|
8573
8696
|
//#endregion
|
|
8574
8697
|
//#region src/sessions/ui/review/transcript/transcript-tab.tsx
|
|
8575
8698
|
function TimelineNote({ atMs, children, onShowMoment }) {
|
|
8576
|
-
const
|
|
8699
|
+
const rowClick = useOpenMomentOnRowClick();
|
|
8577
8700
|
return /* @__PURE__ */ jsxs("li", {
|
|
8578
|
-
className: "dt-ses-seg",
|
|
8701
|
+
className: "dt-ses-seg dt-openable",
|
|
8579
8702
|
"data-kind": "note",
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8703
|
+
onClick: rowClick.onClick,
|
|
8704
|
+
onMouseDown: rowClick.onMouseDown,
|
|
8705
|
+
children: [/* @__PURE__ */ jsx(MomentTime, {
|
|
8706
|
+
onShowMoment,
|
|
8707
|
+
time: formatClock(atMs)
|
|
8708
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
8584
8709
|
className: "dt-ses-seg-body",
|
|
8585
|
-
children:
|
|
8710
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
8586
8711
|
className: "dt-meta",
|
|
8587
8712
|
children
|
|
8588
|
-
})
|
|
8589
|
-
"aria-label": `Show the moment at ${time}`,
|
|
8590
|
-
className: "dt-ses-link",
|
|
8591
|
-
onClick: (event) => onShowMoment(event.currentTarget),
|
|
8592
|
-
type: "button",
|
|
8593
|
-
children: "Show moment"
|
|
8594
|
-
})]
|
|
8713
|
+
})
|
|
8595
8714
|
})]
|
|
8596
8715
|
});
|
|
8597
8716
|
}
|
|
@@ -8786,21 +8905,14 @@ function ReviewPanels({ context, place }) {
|
|
|
8786
8905
|
}
|
|
8787
8906
|
function ReviewTop({ context, links }) {
|
|
8788
8907
|
const { detail, review } = context;
|
|
8789
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
8790
|
-
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
review
|
|
8798
|
-
}),
|
|
8799
|
-
detail.continuation && /* @__PURE__ */ jsx(ContinuationHeader, {
|
|
8800
|
-
continuation: detail.continuation,
|
|
8801
|
-
onOpenSession: links.onOpenSession
|
|
8802
|
-
})
|
|
8803
|
-
] });
|
|
8908
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ReviewHeader, {
|
|
8909
|
+
onDeleted: links.onDeleted,
|
|
8910
|
+
review,
|
|
8911
|
+
session: detail.session
|
|
8912
|
+
}), detail.continuation && /* @__PURE__ */ jsx(ContinuationHeader, {
|
|
8913
|
+
continuation: detail.continuation,
|
|
8914
|
+
onOpenSession: links.onOpenSession
|
|
8915
|
+
})] });
|
|
8804
8916
|
}
|
|
8805
8917
|
function ReviewScreen({ detail, isBriefOpen, links, review }) {
|
|
8806
8918
|
const place = useReviewPlace();
|
|
@@ -9159,6 +9271,7 @@ const INTENTION_STYLES = `
|
|
|
9159
9271
|
.dt-ses-attached { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; }
|
|
9160
9272
|
.dt-ses-attached p { margin: 0; }
|
|
9161
9273
|
.dt-ses-history[data-removed="true"] > .dt-ses-subhead { color: var(--rf-text-muted); text-decoration: line-through; }
|
|
9274
|
+
.dt-ses-history:has(.dt-open) { margin-inline: -8px; padding: 6px 8px; border-radius: var(--dt-radius-field); }
|
|
9162
9275
|
.dt-ses-change { padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); list-style: none; }
|
|
9163
9276
|
.dt-ses-change[data-decision="rejected"] .dt-ses-diff { color: var(--rf-text-muted); text-decoration: line-through; }
|
|
9164
9277
|
.dt-ses-diff { display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; margin: 0; overflow-wrap: anywhere; }
|
|
@@ -9169,7 +9282,8 @@ p.dt-ses-diff { display: block; }
|
|
|
9169
9282
|
//#endregion
|
|
9170
9283
|
//#region src/sessions/ui/review/screens/report-styles.ts
|
|
9171
9284
|
const REPORT_STYLES = `
|
|
9172
|
-
.dt-ses-report { display: flex; flex-direction: column; gap:
|
|
9285
|
+
.dt-ses-report { display: flex; flex-direction: column; gap: 6px; width: 100%; min-width: 0; margin: 0; }
|
|
9286
|
+
.dt-ses-report + .dt-ses-report { margin-top: 12px; }
|
|
9173
9287
|
.dt-ses-report-canvas { position: relative; width: 100%; }
|
|
9174
9288
|
.dt-ses-report-canvas img { display: block; width: 100%; max-width: 100%; height: auto; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
|
|
9175
9289
|
.dt-ses-report-canvas[data-measured="false"] .dt-ses-callout { visibility: hidden; }
|
|
@@ -9183,21 +9297,25 @@ const REPORT_STYLES = `
|
|
|
9183
9297
|
.dt-ses-callouts { margin: 0; padding: 0; list-style: none; }
|
|
9184
9298
|
.dt-ses-callouts[data-flow="list"] { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
9185
9299
|
.dt-ses-callout {
|
|
9186
|
-
|
|
9187
|
-
|
|
9300
|
+
--dt-callout-control: 22px; --dt-callout-inset: 3px;
|
|
9301
|
+
display: flex; align-items: flex-start; gap: 2px; max-width: 100%; padding: var(--dt-callout-inset);
|
|
9302
|
+
border-radius: calc(var(--dt-callout-control) / 2 + var(--dt-callout-inset));
|
|
9303
|
+
background: var(--rf-bg); box-shadow: var(--rf-shadow-inset), var(--rf-shadow-low); font-size: 12px; line-height: 16px;
|
|
9188
9304
|
}
|
|
9189
9305
|
.dt-ses-report-canvas .dt-ses-callout { position: absolute; top: 0; left: 0; width: max-content; max-width: min(280px, 100%); }
|
|
9190
9306
|
.dt-ses-callout-number {
|
|
9191
|
-
display: grid; place-items: center; flex: none; min-width:
|
|
9192
|
-
background: var(--rf-accent); color: var(--rf-accent-text, #fff);
|
|
9307
|
+
display: grid; place-items: center; flex: none; min-width: var(--dt-callout-control); height: var(--dt-callout-control); padding: 0 6px;
|
|
9308
|
+
border-radius: calc(var(--dt-callout-control) / 2); background: var(--rf-accent); color: var(--rf-accent-text, #fff);
|
|
9309
|
+
font-size: 11px; font-weight: 650; font-variant-numeric: tabular-nums;
|
|
9193
9310
|
}
|
|
9194
9311
|
.dt-ses-callout[data-decision="kept"] .dt-ses-callout-number { background: var(--rf-success); }
|
|
9195
9312
|
.dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-number { background: var(--rf-text-muted); }
|
|
9196
|
-
.dt-ses-callout-label { min-width: 0; padding:
|
|
9313
|
+
.dt-ses-callout-label { flex: 1 1 auto; min-width: 0; padding: 3px 4px; font-weight: 550; text-align: left; overflow-wrap: anywhere; }
|
|
9197
9314
|
.dt-ses-callout-label:hover { color: var(--rf-accent); }
|
|
9198
9315
|
.dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-label { color: var(--rf-text-muted); text-decoration: line-through; }
|
|
9199
9316
|
.dt-ses-callout-btn {
|
|
9200
|
-
display: grid; place-items: center; flex: none; width:
|
|
9317
|
+
display: grid; place-items: center; flex: none; width: var(--dt-callout-control); height: var(--dt-callout-control);
|
|
9318
|
+
border-radius: calc(var(--dt-callout-control) / 2); color: var(--rf-text-muted);
|
|
9201
9319
|
transition: background-color 120ms var(--rf-ease), color 120ms var(--rf-ease);
|
|
9202
9320
|
}
|
|
9203
9321
|
.dt-ses-callout-btn svg { width: 13px; height: 13px; }
|
|
@@ -9239,21 +9357,33 @@ const SESSIONS_STYLES = `${`
|
|
|
9239
9357
|
.dt-ses-list li + li { margin-top: 3px; }
|
|
9240
9358
|
.dt-ses-link { align-self: flex-start; padding: 0; color: var(--rf-accent); font-size: 12px; text-align: left; }
|
|
9241
9359
|
.dt-ses-link:hover { text-decoration: underline; }
|
|
9242
|
-
.dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-
|
|
9360
|
+
.dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-handoff { display: flex; flex-direction: column; gap: 6px; }
|
|
9243
9361
|
.dt-ses-banner { padding: 8px 10px; border-radius: var(--dt-radius-card); background: color-mix(in srgb, var(--rf-accent) 10%, transparent); }
|
|
9244
9362
|
.dt-ses-banner p { margin: 0; overflow-wrap: anywhere; }
|
|
9245
|
-
.dt-ses-
|
|
9246
|
-
.dt-ses-
|
|
9247
|
-
.dt-ses-
|
|
9248
|
-
.dt-ses-steps
|
|
9363
|
+
.dt-ses-progress:empty { display: none; }
|
|
9364
|
+
.dt-ses-progress[data-fills-panel="true"] { align-self: center; width: min(100%, 280px); margin: auto; padding: 24px 0; }
|
|
9365
|
+
.dt-sheet-body:has(.dt-ses-progress[data-fills-panel="true"]) > .dt-sheet-foot { margin-top: 0; }
|
|
9366
|
+
.dt-ses-steps { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; list-style: none; color: var(--rf-text-muted); font-size: 12.5px; }
|
|
9367
|
+
.dt-ses-steps li { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
|
|
9368
|
+
.dt-ses-steps li > span:first-child { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; }
|
|
9249
9369
|
.dt-ses-steps li::before { content: ""; flex: none; box-sizing: border-box; width: 10px; height: 10px; border: 1.5px solid currentColor; border-radius: 50%; }
|
|
9250
9370
|
.dt-ses-steps li[data-state="done"]::before { border-color: var(--rf-accent); background: var(--rf-accent); }
|
|
9251
9371
|
.dt-ses-steps li[data-state="active"] { color: var(--rf-text); }
|
|
9252
9372
|
.dt-ses-steps li[data-state="active"]::before { border-color: var(--rf-accent); border-right-color: transparent; animation: dt-step-spin 800ms linear infinite; }
|
|
9253
9373
|
.dt-ses-steps li[data-state="failed"] { color: var(--rf-danger); }
|
|
9254
9374
|
.dt-ses-steps li[data-state="failed"]::before { background: currentColor; }
|
|
9375
|
+
.dt-ses-bar { position: relative; flex: 1 1 100%; height: 4px; margin-left: 18px; overflow: hidden; border-radius: var(--rf-pill); background: var(--rf-bg-hover); }
|
|
9376
|
+
.dt-ses-bar > span { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: var(--rf-accent); transition: width 300ms var(--rf-ease); }
|
|
9377
|
+
.dt-ses-bar > span[data-indeterminate="true"] { width: 35%; animation: dt-bar-slide 1.3s ease-in-out infinite; }
|
|
9255
9378
|
@keyframes dt-step-spin { to { rotate: 360deg; } }
|
|
9256
|
-
@
|
|
9379
|
+
@keyframes dt-bar-slide { from { translate: -100% 0; } to { translate: 300% 0; } }
|
|
9380
|
+
@media (prefers-reduced-motion: reduce) {
|
|
9381
|
+
.dt-ses-steps li::before { animation: none; }
|
|
9382
|
+
.dt-ses-bar > span[data-indeterminate="true"] { width: 100%; opacity: .4; animation: none; }
|
|
9383
|
+
}
|
|
9384
|
+
.dt-ses-seg:has(> .dt-open) { cursor: pointer; }
|
|
9385
|
+
.dt-ses-seg:has(> .dt-open) > .dt-ses-seg-body { position: relative; z-index: 1; }
|
|
9386
|
+
@media (hover: none) { .dt-ses-seg:has(> .dt-hover-actions) { padding-right: 40px; } }
|
|
9257
9387
|
.dt-ses-rows { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
|
|
9258
9388
|
.dt-ses-row {
|
|
9259
9389
|
display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%;
|
|
@@ -9263,8 +9393,17 @@ button.dt-ses-row:hover { background: var(--rf-bg-hover); }
|
|
|
9263
9393
|
.dt-ses-row .dt-card-title { max-width: 100%; }
|
|
9264
9394
|
${`
|
|
9265
9395
|
.dt-ses-title { flex: 1; min-width: 0; margin: 0; font-size: 15px; font-weight: 650; overflow-wrap: anywhere; }
|
|
9396
|
+
.dt-ses-head > .dt-card-head { gap: 10px; }
|
|
9397
|
+
.dt-ses-head .dt-ses-title { flex: 0 1 auto; overflow: hidden; font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
|
|
9266
9398
|
.dt-ses-head .dt-menu > .icon-btn { width: 30px; height: 30px; }
|
|
9399
|
+
.dt-ses-facts { display: flex; flex: 1 0 auto; align-items: center; gap: 10px; margin: 0; color: var(--rf-text-muted); font-size: 12px; white-space: nowrap; }
|
|
9400
|
+
.dt-ses-facts > span { display: inline-flex; align-items: center; gap: 4px; }
|
|
9401
|
+
.dt-ses-facts svg { width: 13px; height: 13px; }
|
|
9402
|
+
.dt-ses-foot { flex-wrap: nowrap; }
|
|
9403
|
+
.dt-ses-foot > .dt-btn { flex: 1 1 0; justify-content: center; height: 32px; }
|
|
9267
9404
|
.dt-btn[data-icon-only] { justify-content: center; width: 28px; padding: 0; }
|
|
9405
|
+
.dt-btn[aria-pressed="true"] { background: color-mix(in srgb, var(--rf-text) 16%, transparent); }
|
|
9406
|
+
.dt-btn[data-variant="primary"][aria-pressed="true"] { background: var(--rf-success); color: #fff; }
|
|
9268
9407
|
.dt-ses-field { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--rf-text-muted); }
|
|
9269
9408
|
.dt-ses-need {
|
|
9270
9409
|
margin: 0; font-weight: 600; overflow-wrap: anywhere;
|
|
@@ -9279,7 +9418,7 @@ ${`
|
|
|
9279
9418
|
.dt-ses-details section { display: flex; flex-direction: column; gap: 2px; }
|
|
9280
9419
|
.dt-ses-quote { padding: 2px 0; font-size: 12px; text-align: left; overflow-wrap: anywhere; }
|
|
9281
9420
|
.dt-ses-quote:hover { color: var(--rf-accent); }
|
|
9282
|
-
.dt-ses-time { flex: none; color: var(--rf-text-muted); font: 11.5px ${MONO_FONT$1}; font-variant-numeric: tabular-nums; }
|
|
9421
|
+
.dt-ses-time { flex: none; align-self: flex-start; color: var(--rf-text-muted); font: 11.5px ${MONO_FONT$1}; font-variant-numeric: tabular-nums; }
|
|
9283
9422
|
.dt-ses-verify { display: flex; flex-direction: column; gap: 6px; margin: 0; padding: 8px 0 0; border: 0; border-top: 1px solid var(--rf-border); }
|
|
9284
9423
|
.dt-ses-timeline { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
9285
9424
|
.dt-ses-seg { display: flex; gap: 8px; padding: 6px 8px; border-radius: var(--dt-radius-field); }
|
|
@@ -9289,10 +9428,18 @@ ${`
|
|
|
9289
9428
|
`}${`
|
|
9290
9429
|
.dt-ses-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
9291
9430
|
.dt-ses-shot { position: relative; display: inline-block; max-width: 100%; line-height: 0; }
|
|
9292
|
-
.dt-ses-shot img { display: block; max-width: 100%; height: auto; max-height: 132px; border-radius: var(--dt-radius-field); box-shadow: var(--rf-shadow-inset); }
|
|
9431
|
+
.dt-ses-shot img { display: block; width: auto; max-width: 100%; height: auto; max-height: 132px; border-radius: var(--dt-radius-field); box-shadow: var(--rf-shadow-inset); }
|
|
9293
9432
|
.dt-ses-shot[data-large="true"] img { max-height: none; }
|
|
9294
9433
|
.dt-ses-shot-mark { position: absolute; border: 2px solid var(--rf-accent); border-radius: var(--dt-radius-tiny); pointer-events: none; }
|
|
9295
|
-
.dt-ses-shot-
|
|
9434
|
+
.dt-ses-shot-face { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; }
|
|
9435
|
+
.dt-ses-caption { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; min-width: 0; color: var(--rf-text-muted); font-size: 11.5px; line-height: 1.4; overflow-wrap: anywhere; }
|
|
9436
|
+
.dt-ses-caption-time { display: inline-flex; align-items: center; gap: 4px; color: var(--rf-text); font-weight: 550; font-variant-numeric: tabular-nums; }
|
|
9437
|
+
.dt-ses-caption-time svg { width: 12px; height: 12px; color: var(--rf-text-muted); }
|
|
9438
|
+
.dt-ses-source-btn {
|
|
9439
|
+
display: flex; flex-direction: column; gap: 2px; margin: 0 -8px; padding: 6px 8px; border-radius: var(--dt-radius-field);
|
|
9440
|
+
text-align: left; transition: background-color 120ms var(--rf-ease);
|
|
9441
|
+
}
|
|
9442
|
+
.dt-ses-source-btn:hover { background: var(--rf-bg-hover); }
|
|
9296
9443
|
.dt-ses-start { display: flex; flex-direction: column; gap: 8px; }
|
|
9297
9444
|
.dt-ses-brief-entry > summary { display: flex; flex-direction: column; gap: 2px; padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); cursor: pointer; list-style: none; }
|
|
9298
9445
|
.dt-ses-brief-entry > summary::-webkit-details-marker { display: none; }
|
|
@@ -9584,55 +9731,59 @@ function SelectionRow({ onOpenCode, onShowSelector, selection }) {
|
|
|
9584
9731
|
request,
|
|
9585
9732
|
title: selection.label
|
|
9586
9733
|
}) : null;
|
|
9587
|
-
return /* @__PURE__ */ jsxs(
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
|
|
9596
|
-
|
|
9597
|
-
|
|
9598
|
-
|
|
9599
|
-
|
|
9600
|
-
|
|
9601
|
-
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
className: "dt-actions",
|
|
9608
|
-
children: [/* @__PURE__ */ jsx("button", {
|
|
9609
|
-
className: "dt-btn",
|
|
9610
|
-
onClick: () => setIsOffPage(!onShowSelector(selection.selector)),
|
|
9611
|
-
type: "button",
|
|
9612
|
-
children: "Show on page"
|
|
9613
|
-
}), onOpenCode && !(request || isSearching) && /* @__PURE__ */ jsx("button", {
|
|
9614
|
-
className: "dt-btn",
|
|
9615
|
-
onClick: () => setIsSearching(true),
|
|
9734
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
9735
|
+
className: "dt-selection dt-openable",
|
|
9736
|
+
children: [
|
|
9737
|
+
/* @__PURE__ */ jsx("p", {
|
|
9738
|
+
className: "dt-meta",
|
|
9739
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
9740
|
+
className: "dt-open",
|
|
9741
|
+
onClick: () => setIsOffPage(!onShowSelector(selection.selector)),
|
|
9742
|
+
title: "Show on page",
|
|
9743
|
+
type: "button",
|
|
9744
|
+
children: [/* @__PURE__ */ jsx("strong", { children: selection.label }), selection.region && ` · ${selection.region}`]
|
|
9745
|
+
})
|
|
9746
|
+
}),
|
|
9747
|
+
selection.comment && /* @__PURE__ */ jsx("p", {
|
|
9748
|
+
className: "dt-note-text",
|
|
9749
|
+
children: selection.comment
|
|
9750
|
+
}),
|
|
9751
|
+
selection.sourceLocation && openSource && /* @__PURE__ */ jsx("button", {
|
|
9752
|
+
className: "dt-source",
|
|
9753
|
+
onClick: openSource,
|
|
9616
9754
|
type: "button",
|
|
9617
|
-
children:
|
|
9618
|
-
})
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9755
|
+
children: selection.sourceLocation
|
|
9756
|
+
}),
|
|
9757
|
+
selection.sourceLocation && !onOpenCode && /* @__PURE__ */ jsx("p", {
|
|
9758
|
+
className: "dt-meta",
|
|
9759
|
+
children: selection.sourceLocation
|
|
9760
|
+
}),
|
|
9761
|
+
onOpenCode && !(request || isSearching) && /* @__PURE__ */ jsx("div", {
|
|
9762
|
+
className: "dt-actions",
|
|
9763
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
9764
|
+
className: "dt-btn",
|
|
9765
|
+
onClick: () => setIsSearching(true),
|
|
9766
|
+
type: "button",
|
|
9767
|
+
children: "Find in code"
|
|
9768
|
+
})
|
|
9769
|
+
}),
|
|
9770
|
+
isOffPage && /* @__PURE__ */ jsx("p", {
|
|
9771
|
+
className: "dt-meta",
|
|
9772
|
+
children: "This element is not on the page right now. Open the state the reporter was in, then try again."
|
|
9773
|
+
}),
|
|
9774
|
+
isSearching && onOpenCode && /* @__PURE__ */ jsx(CodeSearch, {
|
|
9775
|
+
initialQuery: suggestedSearch(selection),
|
|
9776
|
+
onOpenMatch: (match) => onOpenCode({
|
|
9777
|
+
request: {
|
|
9778
|
+
column: null,
|
|
9779
|
+
fileName: match.absolutePath,
|
|
9780
|
+
line: match.line
|
|
9781
|
+
},
|
|
9782
|
+
title: selection.label
|
|
9783
|
+
})
|
|
9633
9784
|
})
|
|
9634
|
-
|
|
9635
|
-
|
|
9785
|
+
]
|
|
9786
|
+
});
|
|
9636
9787
|
}
|
|
9637
9788
|
function FeedbackCard({ item, onOpenCode, onShowSelector }) {
|
|
9638
9789
|
const selections = item.context?.selections ?? [];
|
|
@@ -12271,6 +12422,7 @@ const DEVTOOLS_STYLES = `
|
|
|
12271
12422
|
position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
|
12272
12423
|
margin: auto -10px -10px; padding: 10px; border-top: 1px solid var(--rf-border); background: var(--rf-bg);
|
|
12273
12424
|
}
|
|
12425
|
+
.dt-sheet-body:has(.dt-empty) > .dt-sheet-foot { margin-top: 0; }
|
|
12274
12426
|
.dt-archived { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 10px; border: 1px dashed var(--rf-border); border-radius: var(--dt-radius-card); }
|
|
12275
12427
|
.dt-archived .dt-meta { flex: 1 1 100%; }
|
|
12276
12428
|
.dt-group { margin: 6px 2px 0; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--rf-text-muted); }
|
|
@@ -12286,6 +12438,25 @@ const DEVTOOLS_STYLES = `
|
|
|
12286
12438
|
.dt-source:hover { text-decoration: underline; }
|
|
12287
12439
|
.dt-thumb { display: block; align-self: flex-start; max-width: 100%; height: auto; max-height: 132px; object-fit: contain; border-radius: var(--dt-radius-field); box-shadow: var(--rf-shadow-inset); }
|
|
12288
12440
|
.dt-actions { display: flex; flex-wrap: wrap; gap: 4px; }
|
|
12441
|
+
.dt-openable { position: relative; transition: background-color 120ms var(--rf-ease); }
|
|
12442
|
+
.dt-openable:has(.dt-open):hover { background: var(--rf-bg-hover); }
|
|
12443
|
+
.dt-open { padding: 0; text-align: left; }
|
|
12444
|
+
a.dt-open { color: inherit; text-decoration: none; }
|
|
12445
|
+
.dt-open::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
|
|
12446
|
+
.dt-open:focus-visible { outline: none; }
|
|
12447
|
+
.dt-open:focus-visible::after { outline: 2px solid var(--rf-accent); outline-offset: -2px; }
|
|
12448
|
+
.dt-card-title > .dt-open { display: block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
12449
|
+
.dt-selection { display: flex; flex-direction: column; gap: 4px; margin-inline: -6px; padding: 6px; border-radius: var(--dt-radius-field); }
|
|
12450
|
+
.dt-openable :is(a, button, input, select, textarea):not(.dt-open) { position: relative; z-index: 1; }
|
|
12451
|
+
.dt-hover-actions {
|
|
12452
|
+
position: absolute; top: 6px; right: 6px; z-index: 2; display: flex; gap: 4px; opacity: 0;
|
|
12453
|
+
transition: opacity 120ms var(--rf-ease);
|
|
12454
|
+
}
|
|
12455
|
+
.dt-hover-actions > .icon-btn { width: 28px; height: 28px; }
|
|
12456
|
+
.dt-hover-actions svg { width: 14px; height: 14px; }
|
|
12457
|
+
.dt-openable:hover > .dt-hover-actions, .dt-hover-actions:focus-within { opacity: 1; }
|
|
12458
|
+
.dt-card:has(> .dt-hover-actions):not(:has(> .dt-thumb)) > .dt-card-head { padding-right: 66px; }
|
|
12459
|
+
@media (hover: none) { .dt-hover-actions { opacity: 1; } }
|
|
12289
12460
|
.dt-btn {
|
|
12290
12461
|
display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
|
|
12291
12462
|
border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text);
|
|
@@ -12843,9 +13014,26 @@ function NoteEditor({ initialText, noteId, onDone }) {
|
|
|
12843
13014
|
})]
|
|
12844
13015
|
})] });
|
|
12845
13016
|
}
|
|
12846
|
-
function
|
|
13017
|
+
function NoteTitle({ note, onShowOnPage, pageLink }) {
|
|
12847
13018
|
const { selection } = note;
|
|
12848
13019
|
const owner = selection.componentStack[0];
|
|
13020
|
+
const title = owner ? `<${owner}> ${selection.label}` : selection.label;
|
|
13021
|
+
if (onShowOnPage) return /* @__PURE__ */ jsx("button", {
|
|
13022
|
+
className: "dt-open",
|
|
13023
|
+
onClick: onShowOnPage,
|
|
13024
|
+
title: "Show on page",
|
|
13025
|
+
type: "button",
|
|
13026
|
+
children: title
|
|
13027
|
+
});
|
|
13028
|
+
if (pageLink) return /* @__PURE__ */ jsx("a", {
|
|
13029
|
+
className: "dt-open",
|
|
13030
|
+
href: note.capturedContext.url,
|
|
13031
|
+
title: "Open page",
|
|
13032
|
+
children: title
|
|
13033
|
+
});
|
|
13034
|
+
return title;
|
|
13035
|
+
}
|
|
13036
|
+
function NoteCardHead({ isSending, note, onShowOnPage, pageLink, pinNumber }) {
|
|
12849
13037
|
return /* @__PURE__ */ jsxs("div", {
|
|
12850
13038
|
className: "dt-card-head",
|
|
12851
13039
|
children: [
|
|
@@ -12856,7 +13044,11 @@ function NoteCardHead({ isSending, note, pinNumber }) {
|
|
|
12856
13044
|
}),
|
|
12857
13045
|
/* @__PURE__ */ jsx("h3", {
|
|
12858
13046
|
className: "dt-card-title",
|
|
12859
|
-
children:
|
|
13047
|
+
children: /* @__PURE__ */ jsx(NoteTitle, {
|
|
13048
|
+
note,
|
|
13049
|
+
onShowOnPage,
|
|
13050
|
+
pageLink
|
|
13051
|
+
})
|
|
12860
13052
|
}),
|
|
12861
13053
|
isSending && /* @__PURE__ */ jsx("span", {
|
|
12862
13054
|
className: "dt-chip",
|
|
@@ -12893,36 +13085,25 @@ function NoteSourceLine({ isLocating, note, onOpenCode }) {
|
|
|
12893
13085
|
children: selection.region ?? selection.selector
|
|
12894
13086
|
});
|
|
12895
13087
|
}
|
|
12896
|
-
function
|
|
13088
|
+
function NoteHoverActions({ isSending, note, onEdit }) {
|
|
12897
13089
|
return /* @__PURE__ */ jsxs("div", {
|
|
12898
|
-
className: "dt-actions",
|
|
12899
|
-
children: [
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
})
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
children: "Edit"
|
|
12916
|
-
}),
|
|
12917
|
-
/* @__PURE__ */ jsx("button", {
|
|
12918
|
-
className: "dt-btn",
|
|
12919
|
-
"data-variant": "danger",
|
|
12920
|
-
disabled: isSending,
|
|
12921
|
-
onClick: () => deleteNote(note),
|
|
12922
|
-
type: "button",
|
|
12923
|
-
children: "Delete"
|
|
12924
|
-
})
|
|
12925
|
-
]
|
|
13090
|
+
className: "dt-hover-actions",
|
|
13091
|
+
children: [!(note.sent || isSending) && /* @__PURE__ */ jsx("button", {
|
|
13092
|
+
"aria-label": "Edit note",
|
|
13093
|
+
className: "icon-btn glass",
|
|
13094
|
+
onClick: onEdit,
|
|
13095
|
+
title: "Edit note",
|
|
13096
|
+
type: "button",
|
|
13097
|
+
children: /* @__PURE__ */ jsx(PencilIcon, {})
|
|
13098
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
13099
|
+
"aria-label": "Delete note",
|
|
13100
|
+
className: "icon-btn glass",
|
|
13101
|
+
disabled: isSending,
|
|
13102
|
+
onClick: () => deleteNote(note),
|
|
13103
|
+
title: "Delete note",
|
|
13104
|
+
type: "button",
|
|
13105
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {})
|
|
13106
|
+
})]
|
|
12926
13107
|
});
|
|
12927
13108
|
}
|
|
12928
13109
|
function NoteCard({ isLocating, isSending = false, note, onOpenCode, onShowSelector, pageLink, pinNumber }) {
|
|
@@ -12931,7 +13112,7 @@ function NoteCard({ isLocating, isSending = false, note, onOpenCode, onShowSelec
|
|
|
12931
13112
|
const closeUp = useCloseUpPreview(note.id, isLocating);
|
|
12932
13113
|
const showOnPage = onShowSelector ? () => setIsOffPage(!onShowSelector(note.selection.selector)) : void 0;
|
|
12933
13114
|
return /* @__PURE__ */ jsxs("article", {
|
|
12934
|
-
className: "dt-card",
|
|
13115
|
+
className: "dt-card dt-openable",
|
|
12935
13116
|
"data-sent": Boolean(note.sent),
|
|
12936
13117
|
children: [
|
|
12937
13118
|
closeUp && /* @__PURE__ */ jsx("img", {
|
|
@@ -12944,6 +13125,8 @@ function NoteCard({ isLocating, isSending = false, note, onOpenCode, onShowSelec
|
|
|
12944
13125
|
/* @__PURE__ */ jsx(NoteCardHead, {
|
|
12945
13126
|
isSending,
|
|
12946
13127
|
note,
|
|
13128
|
+
onShowOnPage: showOnPage,
|
|
13129
|
+
pageLink,
|
|
12947
13130
|
pinNumber
|
|
12948
13131
|
}),
|
|
12949
13132
|
/* @__PURE__ */ jsx(NoteSourceLine, {
|
|
@@ -12963,12 +13146,10 @@ function NoteCard({ isLocating, isSending = false, note, onOpenCode, onShowSelec
|
|
|
12963
13146
|
className: "dt-meta",
|
|
12964
13147
|
children: "The element is not on the page right now."
|
|
12965
13148
|
}),
|
|
12966
|
-
!isEditing && /* @__PURE__ */ jsx(
|
|
13149
|
+
!isEditing && /* @__PURE__ */ jsx(NoteHoverActions, {
|
|
12967
13150
|
isSending,
|
|
12968
13151
|
note,
|
|
12969
|
-
onEdit: () => setIsEditing(true)
|
|
12970
|
-
onShowSelector: showOnPage,
|
|
12971
|
-
pageLink
|
|
13152
|
+
onEdit: () => setIsEditing(true)
|
|
12972
13153
|
})
|
|
12973
13154
|
]
|
|
12974
13155
|
});
|
|
@@ -13414,4 +13595,4 @@ function useSheetTab() {
|
|
|
13414
13595
|
//#endregion
|
|
13415
13596
|
export { sessionSheetPlace as A, teamTransport as B, AccountFooter as C, MissingRouteHint as D, MISSING_SECRET_KEY_HINT as E, useWorkspaceLoader as F, SessionApiError as G, disconnectBoard as H, purgeAccount as I, reportFailure as J, sessionApiErrorSchema as K, bridgeBoardCall as L, RecordingLayer as M, SessionWorkspaceProvider as N, SESSIONS_STYLES as O, failureMessage as P, bridgeTransport as R, InboxPanel as S, CONNECT_REQUIREMENT as T, probeDevRoute as U, callReflet as V, startBoardConnect as W, DevtoolsBarButtons as _, NotePicker as a, CodeTab as b, isActiveNote as c, useNotes as d, findOnPage as f, DevtoolsBar as g, DevtoolsSheet as h, NotesPanel as i, useSessionNavigation as j, SessionsPanel as k, isNoteOnPage as l, DEVTOOLS_STYLES as m, useSheetTab as n, enrichNote as o, usePageHighlight as p, Notices as q, NotePins as r, hasTriedToLocate as s, FIRST_SHEET_TAB as t, unsentNoteCount as u, SHEET_TABS as v, AccountNote as w, useCodeLookup as x, SessionsBarButton as y, devtoolsTransport as z };
|
|
13416
13597
|
|
|
13417
|
-
//# sourceMappingURL=use-sheet-tab-
|
|
13598
|
+
//# sourceMappingURL=use-sheet-tab-B-q4G0Hn.js.map
|