reflet-sdk 0.7.7 → 0.7.8
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 +21 -0
- package/README.md +2 -1
- package/dist/desktop.js +1 -1
- package/dist/{devtools-layer-CYIoe3Vo.js → devtools-layer-B_b_3MH3.js} +3 -3
- package/dist/{devtools-layer-CYIoe3Vo.js.map → devtools-layer-B_b_3MH3.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-D_DrRBeB.js} +2 -2
- package/dist/{submit-r3_M1ITo.js.map → submit-D_DrRBeB.js.map} +1 -1
- package/dist/{team-layer-5-uPYTjV.js → team-layer-CxMBo9WN.js} +3 -3
- package/dist/{team-layer-5-uPYTjV.js.map → team-layer-CxMBo9WN.js.map} +1 -1
- package/dist/{use-sheet-tab-CacYFVjI.js → use-sheet-tab-azUezLAq.js} +1182 -1053
- package/dist/use-sheet-tab-azUezLAq.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-D_DrRBeB.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,188 @@ 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("button", {
|
|
3570
|
+
"aria-label": label,
|
|
3571
|
+
className: "icon-btn glass dt-ses-hover-action",
|
|
3572
|
+
onClick: () => {
|
|
3573
|
+
setIsConfirming(true);
|
|
3574
|
+
setHasCancelled(false);
|
|
3575
|
+
},
|
|
3576
|
+
ref: triggerRef,
|
|
3577
|
+
title: label,
|
|
3578
|
+
type: "button",
|
|
3579
|
+
children: /* @__PURE__ */ jsx(TrashIcon, {})
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
//#endregion
|
|
3352
3583
|
//#region src/sessions/ui/session-icons.tsx
|
|
3353
3584
|
function MicIcon() {
|
|
3354
3585
|
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("rect", {
|
|
@@ -3375,6 +3606,22 @@ function ClockIcon() {
|
|
|
3375
3606
|
r: "8.5"
|
|
3376
3607
|
}), /* @__PURE__ */ jsx("path", { d: "M12 7.5V12l3 2" })] });
|
|
3377
3608
|
}
|
|
3609
|
+
function LockIcon() {
|
|
3610
|
+
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("rect", {
|
|
3611
|
+
height: "10",
|
|
3612
|
+
rx: "2.5",
|
|
3613
|
+
width: "15",
|
|
3614
|
+
x: "4.5",
|
|
3615
|
+
y: "10.5"
|
|
3616
|
+
}), /* @__PURE__ */ jsx("path", { d: "M8 10.5V8a4 4 0 0 1 8 0v2.5" })] });
|
|
3617
|
+
}
|
|
3618
|
+
function TeamIcon() {
|
|
3619
|
+
return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("circle", {
|
|
3620
|
+
cx: "9",
|
|
3621
|
+
cy: "8.5",
|
|
3622
|
+
r: "3.5"
|
|
3623
|
+
}), /* @__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" })] });
|
|
3624
|
+
}
|
|
3378
3625
|
function IntentionIcon() {
|
|
3379
3626
|
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
3627
|
}
|
|
@@ -3399,6 +3646,50 @@ function ImageIcon() {
|
|
|
3399
3646
|
] });
|
|
3400
3647
|
}
|
|
3401
3648
|
//#endregion
|
|
3649
|
+
//#region src/sessions/ui/recording/cancel-confirmation.tsx
|
|
3650
|
+
function CancelConfirmation({ onCancelRecording, onKeepRecording }) {
|
|
3651
|
+
const confirmationRef = useRef(null);
|
|
3652
|
+
const keepRef = useFocusWhen(true);
|
|
3653
|
+
useEffect(() => {
|
|
3654
|
+
const confirmation = confirmationRef.current;
|
|
3655
|
+
if (!confirmation) return;
|
|
3656
|
+
const keepOnEscape = (event) => {
|
|
3657
|
+
if (event.key === "Escape" && !event.isComposing) {
|
|
3658
|
+
event.stopPropagation();
|
|
3659
|
+
onKeepRecording();
|
|
3660
|
+
}
|
|
3661
|
+
};
|
|
3662
|
+
confirmation.addEventListener("keydown", keepOnEscape);
|
|
3663
|
+
return () => confirmation.removeEventListener("keydown", keepOnEscape);
|
|
3664
|
+
}, [onKeepRecording]);
|
|
3665
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3666
|
+
"aria-labelledby": "rf-rec-cancel-copy",
|
|
3667
|
+
className: "dt-rec-note",
|
|
3668
|
+
ref: confirmationRef,
|
|
3669
|
+
role: "alertdialog",
|
|
3670
|
+
children: [
|
|
3671
|
+
/* @__PURE__ */ jsx("span", {
|
|
3672
|
+
id: "rf-rec-cancel-copy",
|
|
3673
|
+
children: "Cancel this recording? Nothing from it is kept."
|
|
3674
|
+
}),
|
|
3675
|
+
/* @__PURE__ */ jsx("button", {
|
|
3676
|
+
className: "dt-btn",
|
|
3677
|
+
"data-variant": "danger",
|
|
3678
|
+
onClick: onCancelRecording,
|
|
3679
|
+
type: "button",
|
|
3680
|
+
children: "Discard"
|
|
3681
|
+
}),
|
|
3682
|
+
/* @__PURE__ */ jsx("button", {
|
|
3683
|
+
className: "dt-btn",
|
|
3684
|
+
onClick: onKeepRecording,
|
|
3685
|
+
ref: keepRef,
|
|
3686
|
+
type: "button",
|
|
3687
|
+
children: "Keep recording"
|
|
3688
|
+
})
|
|
3689
|
+
]
|
|
3690
|
+
});
|
|
3691
|
+
}
|
|
3692
|
+
//#endregion
|
|
3402
3693
|
//#region src/sessions/ui/recording/level-meter.tsx
|
|
3403
3694
|
const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
3404
3695
|
/** The level changes many times a second: it goes straight to a CSS variable, never through React state. */
|
|
@@ -3461,25 +3752,19 @@ function MarkNote({ focusOnOpen, onClose }) {
|
|
|
3461
3752
|
//#endregion
|
|
3462
3753
|
//#region src/sessions/ui/recording/recording-bar.tsx
|
|
3463
3754
|
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
|
-
});
|
|
3755
|
+
function syncProblem(sync) {
|
|
3756
|
+
if (sync.blockedBy === "storage") return "Not saved on this device";
|
|
3757
|
+
if (!sync.isOnline) return "Offline — kept on this device";
|
|
3758
|
+
return null;
|
|
3475
3759
|
}
|
|
3476
|
-
function statusText(snapshot, maxDurationMs) {
|
|
3760
|
+
function statusText(snapshot, sync, maxDurationMs) {
|
|
3477
3761
|
if (snapshot.phase === "failed") return snapshot.failure?.message ?? "Recording stopped.";
|
|
3478
3762
|
if (snapshot.phase === "finishing") return "Finishing…";
|
|
3479
3763
|
if (snapshot.pauseReason === "limit") return "Time limit reached · Finish to review the session";
|
|
3480
3764
|
if (snapshot.pauseReason === "hidden") return "Paused because the tab was hidden";
|
|
3481
|
-
if (snapshot.phase === "paused") return "Paused · nothing is recorded";
|
|
3482
3765
|
if (snapshot.limitsReached.length > 0) return `Limit reached for ${snapshot.limitsReached.join(", ")}`;
|
|
3766
|
+
const problem = syncProblem(sync);
|
|
3767
|
+
if (problem) return problem;
|
|
3483
3768
|
return snapshot.nearLimit ? `${minutesLeft(maxDurationMs - snapshot.activeMs)} min left` : "";
|
|
3484
3769
|
}
|
|
3485
3770
|
function markMoment(recorder, focus, setOpenMark) {
|
|
@@ -3513,42 +3798,67 @@ function useMarkMoment(snapshot, barRef) {
|
|
|
3513
3798
|
};
|
|
3514
3799
|
}
|
|
3515
3800
|
function CaptureButtons({ canCapture, onMark, onPointAt }) {
|
|
3516
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */
|
|
3801
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
3517
3802
|
"aria-keyshortcuts": "Alt+Shift+M",
|
|
3803
|
+
"aria-label": "Mark moment",
|
|
3518
3804
|
className: "dt-btn",
|
|
3805
|
+
"data-icon-only": true,
|
|
3519
3806
|
disabled: !canCapture,
|
|
3520
3807
|
onClick: onMark,
|
|
3521
3808
|
title: "Mark moment (Alt+Shift+M)",
|
|
3522
3809
|
type: "button",
|
|
3523
|
-
children:
|
|
3810
|
+
children: /* @__PURE__ */ jsx(MarkIcon, {})
|
|
3524
3811
|
}), /* @__PURE__ */ jsx("button", {
|
|
3812
|
+
"aria-label": "Point at element",
|
|
3525
3813
|
className: "dt-btn",
|
|
3814
|
+
"data-icon-only": true,
|
|
3526
3815
|
disabled: !canCapture,
|
|
3527
3816
|
onClick: onPointAt,
|
|
3817
|
+
title: "Point at element",
|
|
3528
3818
|
type: "button",
|
|
3529
|
-
children:
|
|
3819
|
+
children: /* @__PURE__ */ jsx(TargetIcon, {})
|
|
3530
3820
|
})] });
|
|
3531
3821
|
}
|
|
3532
3822
|
function PauseToggle({ snapshot }) {
|
|
3533
3823
|
const { recorder } = useSessionWorkspace();
|
|
3534
|
-
if (snapshot.phase === "recording") return /* @__PURE__ */
|
|
3824
|
+
if (snapshot.phase === "recording") return /* @__PURE__ */ jsx("button", {
|
|
3825
|
+
"aria-label": "Pause",
|
|
3535
3826
|
className: "dt-btn",
|
|
3827
|
+
"data-icon-only": true,
|
|
3536
3828
|
onClick: () => recorder.pause("paused"),
|
|
3829
|
+
title: "Pause",
|
|
3537
3830
|
type: "button",
|
|
3538
|
-
children:
|
|
3831
|
+
children: /* @__PURE__ */ jsx(PauseIcon, {})
|
|
3539
3832
|
});
|
|
3540
|
-
if (snapshot.phase === "paused" && snapshot.pauseReason !== "limit") return /* @__PURE__ */
|
|
3833
|
+
if (snapshot.phase === "paused" && snapshot.pauseReason !== "limit") return /* @__PURE__ */ jsx("button", {
|
|
3834
|
+
"aria-label": "Resume",
|
|
3541
3835
|
className: "dt-btn",
|
|
3836
|
+
"data-icon-only": true,
|
|
3542
3837
|
onClick: () => recorder.resume(),
|
|
3838
|
+
title: "Resume",
|
|
3543
3839
|
type: "button",
|
|
3544
|
-
children:
|
|
3840
|
+
children: /* @__PURE__ */ jsx(PlayIcon, {})
|
|
3545
3841
|
});
|
|
3546
3842
|
return null;
|
|
3547
3843
|
}
|
|
3548
|
-
function
|
|
3844
|
+
function CancelButton({ hasKeptRecording, onAskToCancel }) {
|
|
3845
|
+
const buttonRef = useFocusWhen(hasKeptRecording);
|
|
3846
|
+
return /* @__PURE__ */ jsx("button", {
|
|
3847
|
+
"aria-label": "Cancel recording",
|
|
3848
|
+
className: "dt-btn",
|
|
3849
|
+
"data-icon-only": true,
|
|
3850
|
+
onClick: onAskToCancel,
|
|
3851
|
+
ref: buttonRef,
|
|
3852
|
+
title: "Cancel recording",
|
|
3853
|
+
type: "button",
|
|
3854
|
+
children: /* @__PURE__ */ jsx(CloseIcon, {})
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
function RecordingControls({ hasKeptRecording, onAskToCancel, onFinish, onMark, onPointAt, snapshot }) {
|
|
3549
3858
|
const { phase } = snapshot;
|
|
3859
|
+
const isActive = phase === "recording" || phase === "paused";
|
|
3550
3860
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3551
|
-
|
|
3861
|
+
isActive && /* @__PURE__ */ jsx(CaptureButtons, {
|
|
3552
3862
|
canCapture: phase === "recording",
|
|
3553
3863
|
onMark,
|
|
3554
3864
|
onPointAt
|
|
@@ -3561,6 +3871,10 @@ function RecordingControls({ onFinish, onMark, onPointAt, snapshot }) {
|
|
|
3561
3871
|
onClick: onFinish,
|
|
3562
3872
|
type: "button",
|
|
3563
3873
|
children: "Finish"
|
|
3874
|
+
}),
|
|
3875
|
+
isActive && /* @__PURE__ */ jsx(CancelButton, {
|
|
3876
|
+
hasKeptRecording,
|
|
3877
|
+
onAskToCancel
|
|
3564
3878
|
})
|
|
3565
3879
|
] });
|
|
3566
3880
|
}
|
|
@@ -3573,10 +3887,14 @@ function RecordingClock({ activeMs }) {
|
|
|
3573
3887
|
children: formatClock(activeMs)
|
|
3574
3888
|
});
|
|
3575
3889
|
}
|
|
3576
|
-
function RecordingBar({ onFinish, onPointAt, snapshot }) {
|
|
3577
|
-
const { identity, recorder } = useSessionWorkspace();
|
|
3890
|
+
function RecordingBar({ onCancel, onFinish, onPointAt, snapshot }) {
|
|
3891
|
+
const { account, identity, recorder } = useSessionWorkspace();
|
|
3892
|
+
const sync = useSyncStatus(account.key);
|
|
3578
3893
|
const barRef = useRef(null);
|
|
3579
3894
|
const { markWithNote, openMark } = useMarkMoment(snapshot, barRef);
|
|
3895
|
+
const [cancelStep, setCancelStep] = useState("idle");
|
|
3896
|
+
const keepRecording = useCallback(() => setCancelStep("kept"), []);
|
|
3897
|
+
const showsMeter = snapshot.phase === "recording" || snapshot.phase === "paused";
|
|
3580
3898
|
return /* @__PURE__ */ jsxs("div", {
|
|
3581
3899
|
"aria-label": "Session recording",
|
|
3582
3900
|
className: "dt-rec glass",
|
|
@@ -3588,24 +3906,29 @@ function RecordingBar({ onFinish, onPointAt, snapshot }) {
|
|
|
3588
3906
|
"aria-hidden": "true",
|
|
3589
3907
|
className: "dt-rec-dot"
|
|
3590
3908
|
}),
|
|
3591
|
-
|
|
3909
|
+
showsMeter && /* @__PURE__ */ jsx(LevelMeter, { recorder }),
|
|
3592
3910
|
/* @__PURE__ */ jsx(RecordingClock, { activeMs: snapshot.activeMs }),
|
|
3593
3911
|
/* @__PURE__ */ jsx("span", {
|
|
3594
3912
|
"aria-live": "polite",
|
|
3595
3913
|
className: "dt-rec-status",
|
|
3596
|
-
children: statusText(snapshot, identity.limits.maxDurationMs)
|
|
3914
|
+
children: statusText(snapshot, sync, identity.limits.maxDurationMs)
|
|
3597
3915
|
}),
|
|
3598
3916
|
/* @__PURE__ */ jsx(RecordingControls, {
|
|
3917
|
+
hasKeptRecording: cancelStep === "kept",
|
|
3918
|
+
onAskToCancel: () => setCancelStep("confirming"),
|
|
3599
3919
|
onFinish,
|
|
3600
3920
|
onMark: markWithNote,
|
|
3601
3921
|
onPointAt,
|
|
3602
3922
|
snapshot
|
|
3603
3923
|
}),
|
|
3604
|
-
/* @__PURE__ */ jsx(SyncChip, {}),
|
|
3605
3924
|
openMark && /* @__PURE__ */ jsx(MarkNote, {
|
|
3606
3925
|
focusOnOpen: openMark.focus,
|
|
3607
3926
|
onClose: openMark.closeNote
|
|
3608
|
-
}, openMark.key)
|
|
3927
|
+
}, openMark.key),
|
|
3928
|
+
cancelStep === "confirming" && /* @__PURE__ */ jsx(CancelConfirmation, {
|
|
3929
|
+
onCancelRecording: onCancel,
|
|
3930
|
+
onKeepRecording: keepRecording
|
|
3931
|
+
})
|
|
3609
3932
|
]
|
|
3610
3933
|
});
|
|
3611
3934
|
}
|
|
@@ -3641,7 +3964,16 @@ function RecordingLayer({ onFinished }) {
|
|
|
3641
3964
|
reportFailure("The session did not finish", error, finish);
|
|
3642
3965
|
}
|
|
3643
3966
|
};
|
|
3967
|
+
const cancel = async () => {
|
|
3968
|
+
setIsPointing(false);
|
|
3969
|
+
try {
|
|
3970
|
+
await recorder.cancel();
|
|
3971
|
+
} catch (error) {
|
|
3972
|
+
reportFailure("The recording was not deleted", error);
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3644
3975
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(RecordingBar, {
|
|
3976
|
+
onCancel: cancel,
|
|
3645
3977
|
onFinish: finish,
|
|
3646
3978
|
onPointAt: () => setIsPointing(true),
|
|
3647
3979
|
snapshot
|
|
@@ -3824,6 +4156,15 @@ function evidenceCaption(evidence) {
|
|
|
3824
4156
|
const element = evidence.element ? ` · ${evidence.element.label}` : "";
|
|
3825
4157
|
return `${TRIGGER_LABELS[evidence.trigger]}${element}`;
|
|
3826
4158
|
}
|
|
4159
|
+
function ShotCaption({ atMs, children }) {
|
|
4160
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
4161
|
+
className: "dt-ses-caption",
|
|
4162
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
4163
|
+
className: "dt-ses-caption-time",
|
|
4164
|
+
children: [/* @__PURE__ */ jsx(ClockIcon, {}), formatClock(atMs)]
|
|
4165
|
+
}), children]
|
|
4166
|
+
});
|
|
4167
|
+
}
|
|
3827
4168
|
const hasViewport = (frame) => frame.viewportWidth > 0 && frame.viewportHeight > 0;
|
|
3828
4169
|
const percent = (value, total) => `${value / total * 100}%`;
|
|
3829
4170
|
function percentBox(rect, frame) {
|
|
@@ -4363,119 +4704,6 @@ function SaveProblemMessage({ onReload, problem }) {
|
|
|
4363
4704
|
});
|
|
4364
4705
|
}
|
|
4365
4706
|
//#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
4707
|
//#region src/sessions/ui/review/proposals/proposal-editor.tsx
|
|
4480
4708
|
const MAX_LIST_ITEMS = 8;
|
|
4481
4709
|
function lines(text) {
|
|
@@ -5252,73 +5480,6 @@ function UpdateCard({ context, continuation }) {
|
|
|
5252
5480
|
});
|
|
5253
5481
|
}
|
|
5254
5482
|
//#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
5483
|
//#region src/sessions/ui/review/moment/moment-labels.ts
|
|
5323
5484
|
const URL_PATH = /^[a-z][a-z\d+.-]*:\/\/[^/?#]*([^?#]*)/i;
|
|
5324
5485
|
const KIND_LABELS = {
|
|
@@ -5338,7 +5499,7 @@ function contextKindLabel(item) {
|
|
|
5338
5499
|
return KIND_LABELS[item.kind];
|
|
5339
5500
|
}
|
|
5340
5501
|
function pagePath(url) {
|
|
5341
|
-
if (!url) return null;
|
|
5502
|
+
if (!url || url.startsWith("file:")) return null;
|
|
5342
5503
|
return URL_PATH.exec(url)?.[1] || "/";
|
|
5343
5504
|
}
|
|
5344
5505
|
function contextLine$1(item) {
|
|
@@ -5852,70 +6013,263 @@ function MomentView({ handlers, segmentId, session }) {
|
|
|
5852
6013
|
});
|
|
5853
6014
|
}
|
|
5854
6015
|
//#endregion
|
|
5855
|
-
//#region src/sessions/ui/review/
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
x: left,
|
|
5868
|
-
y: top
|
|
6016
|
+
//#region src/sessions/ui/review/processing-steps.ts
|
|
6017
|
+
function uploadStep(pendingUploads) {
|
|
6018
|
+
return pendingUploads > 0 ? {
|
|
6019
|
+
analyzeLabel: null,
|
|
6020
|
+
key: "upload",
|
|
6021
|
+
label: "Uploading",
|
|
6022
|
+
state: "active"
|
|
6023
|
+
} : {
|
|
6024
|
+
analyzeLabel: null,
|
|
6025
|
+
key: "upload",
|
|
6026
|
+
label: "Uploaded",
|
|
6027
|
+
state: "done"
|
|
5869
6028
|
};
|
|
5870
6029
|
}
|
|
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: []
|
|
6030
|
+
function transcriptionStep(detail, isAnalysisQueued) {
|
|
6031
|
+
const voice = detail.segments.filter((segment) => segment.source === "voice");
|
|
6032
|
+
const transcribing = voice.filter(({ transcription }) => transcription.state === "pending" || transcription.state === "running").length;
|
|
6033
|
+
const step = {
|
|
6034
|
+
analyzeLabel: null,
|
|
6035
|
+
key: "transcription"
|
|
6036
|
+
};
|
|
6037
|
+
if (transcribing > 0) {
|
|
6038
|
+
const transcribed = voice.length - transcribing;
|
|
6039
|
+
return {
|
|
6040
|
+
...step,
|
|
6041
|
+
label: "Transcribing your voice",
|
|
6042
|
+
progress: {
|
|
6043
|
+
done: transcribed,
|
|
6044
|
+
total: voice.length,
|
|
6045
|
+
valueText: `${transcribed} of ${countLabel(voice.length, "passage")}`
|
|
6046
|
+
},
|
|
6047
|
+
state: "active"
|
|
5908
6048
|
};
|
|
5909
|
-
screen.callouts.push({
|
|
5910
|
-
label,
|
|
5911
|
-
outline,
|
|
5912
|
-
proposal
|
|
5913
|
-
});
|
|
5914
|
-
screensById.set(shot.evidence.id, screen);
|
|
5915
6049
|
}
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
6050
|
+
if (voice.length > 0) return {
|
|
6051
|
+
...step,
|
|
6052
|
+
label: "Voice transcribed",
|
|
6053
|
+
state: "done"
|
|
6054
|
+
};
|
|
6055
|
+
return isAnalysisQueued ? {
|
|
6056
|
+
...step,
|
|
6057
|
+
label: "Transcribe your voice",
|
|
6058
|
+
state: "waiting"
|
|
6059
|
+
} : null;
|
|
6060
|
+
}
|
|
6061
|
+
function comparedRevision(detail) {
|
|
6062
|
+
const { continuation } = detail;
|
|
6063
|
+
return continuation?.intention && continuation.contribution.detachedAt === null ? continuation.contribution.baseRevision : null;
|
|
6064
|
+
}
|
|
6065
|
+
function intentionStep(detail, isAnalysisQueued) {
|
|
6066
|
+
const { session } = detail;
|
|
6067
|
+
const { analysis } = session;
|
|
6068
|
+
const revision = comparedRevision(detail);
|
|
6069
|
+
const isComparing = revision !== null;
|
|
6070
|
+
const step = {
|
|
6071
|
+
analyzeLabel: null,
|
|
6072
|
+
key: "intentions"
|
|
6073
|
+
};
|
|
6074
|
+
if (analysis.state === "running") {
|
|
6075
|
+
const label = isComparing ? `Comparing with revision ${revision}…` : "Finding intentions with AI…";
|
|
6076
|
+
return {
|
|
6077
|
+
...step,
|
|
6078
|
+
label,
|
|
6079
|
+
state: "active"
|
|
6080
|
+
};
|
|
6081
|
+
}
|
|
6082
|
+
if (analysis.state === "waiting" || isAnalysisQueued) {
|
|
6083
|
+
const label = isComparing ? `Compare with revision ${revision}` : "Find intentions with AI";
|
|
6084
|
+
return {
|
|
6085
|
+
...step,
|
|
6086
|
+
label,
|
|
6087
|
+
state: "waiting"
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
if (analysis.state === "failed") {
|
|
6091
|
+
const failure = isComparing ? "The comparison failed" : "Analysis failed";
|
|
6092
|
+
const reason = analysis.error ?? "Unknown error.";
|
|
6093
|
+
return {
|
|
6094
|
+
...step,
|
|
6095
|
+
analyzeLabel: "Try again",
|
|
6096
|
+
label: `${failure}: ${reason}`,
|
|
6097
|
+
state: "failed"
|
|
6098
|
+
};
|
|
6099
|
+
}
|
|
6100
|
+
if (isAnalysisOutdated(session)) return {
|
|
6101
|
+
...step,
|
|
6102
|
+
analyzeLabel: "Analyze again",
|
|
6103
|
+
label: "The transcript changed since the analysis.",
|
|
6104
|
+
state: "waiting"
|
|
6105
|
+
};
|
|
6106
|
+
if (analysis.state === "idle") return detail.segments.length > 0 ? {
|
|
6107
|
+
...step,
|
|
6108
|
+
analyzeLabel: "Analyze",
|
|
6109
|
+
label: "Not analyzed yet.",
|
|
6110
|
+
state: "waiting"
|
|
6111
|
+
} : null;
|
|
6112
|
+
const label = isComparing ? `Compared with revision ${revision}` : `Found ${countLabel(analysis.proposalCount ?? 0, "intention")}`;
|
|
6113
|
+
return {
|
|
6114
|
+
...step,
|
|
6115
|
+
label,
|
|
6116
|
+
state: "done"
|
|
6117
|
+
};
|
|
6118
|
+
}
|
|
6119
|
+
function processingSteps(detail, pendingUploads) {
|
|
6120
|
+
if (detail.session.collection !== "ended") return [];
|
|
6121
|
+
const isAnalysisQueued = pendingUploads > 0 && detail.session.analysis.state === "idle";
|
|
6122
|
+
return [
|
|
6123
|
+
detail.session.isMine ? uploadStep(pendingUploads) : null,
|
|
6124
|
+
transcriptionStep(detail, isAnalysisQueued),
|
|
6125
|
+
intentionStep(detail, isAnalysisQueued)
|
|
6126
|
+
].filter((step) => step !== null);
|
|
6127
|
+
}
|
|
6128
|
+
//#endregion
|
|
6129
|
+
//#region src/sessions/ui/review/processing-progress.tsx
|
|
6130
|
+
function useProcessingSteps(detail) {
|
|
6131
|
+
const { account } = useSessionWorkspace();
|
|
6132
|
+
const sync = useSyncStatus(account.key);
|
|
6133
|
+
return processingSteps(detail, detail.session.isMine ? sync.pendingOperations : 0);
|
|
6134
|
+
}
|
|
6135
|
+
function AnalyzeButton({ detail, label, review }) {
|
|
6136
|
+
const { api } = useSessionWorkspace();
|
|
6137
|
+
const run = useSessionAction();
|
|
6138
|
+
const analyze = async () => {
|
|
6139
|
+
const status = await run("The analysis did not start", () => api.analyze(detail.session.id));
|
|
6140
|
+
if (status) review.patch((current) => ({
|
|
6141
|
+
...current,
|
|
6142
|
+
session: {
|
|
6143
|
+
...current.session,
|
|
6144
|
+
analysis: status
|
|
6145
|
+
}
|
|
6146
|
+
}));
|
|
6147
|
+
};
|
|
6148
|
+
return /* @__PURE__ */ jsx("button", {
|
|
6149
|
+
className: "dt-btn",
|
|
6150
|
+
onClick: analyze,
|
|
6151
|
+
type: "button",
|
|
6152
|
+
children: label
|
|
6153
|
+
});
|
|
6154
|
+
}
|
|
6155
|
+
function ProgressBar({ label, progress }) {
|
|
6156
|
+
if (!progress) return /* @__PURE__ */ jsx("span", {
|
|
6157
|
+
"aria-label": label,
|
|
6158
|
+
className: "dt-ses-bar",
|
|
6159
|
+
role: "progressbar",
|
|
6160
|
+
children: /* @__PURE__ */ jsx("span", { "data-indeterminate": "true" })
|
|
6161
|
+
});
|
|
6162
|
+
const { done, total, valueText } = progress;
|
|
6163
|
+
return /* @__PURE__ */ jsx("span", {
|
|
6164
|
+
"aria-label": label,
|
|
6165
|
+
"aria-valuemax": total,
|
|
6166
|
+
"aria-valuemin": 0,
|
|
6167
|
+
"aria-valuenow": done,
|
|
6168
|
+
"aria-valuetext": valueText,
|
|
6169
|
+
className: "dt-ses-bar",
|
|
6170
|
+
role: "progressbar",
|
|
6171
|
+
title: valueText,
|
|
6172
|
+
children: /* @__PURE__ */ jsx("span", { style: { width: `${done / total * 100}%` } })
|
|
6173
|
+
});
|
|
6174
|
+
}
|
|
6175
|
+
function StepRow({ detail, review, step }) {
|
|
6176
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
6177
|
+
"data-state": step.state,
|
|
6178
|
+
children: [
|
|
6179
|
+
/* @__PURE__ */ jsx("span", { children: step.label }),
|
|
6180
|
+
step.analyzeLabel && detail.session.canEdit && /* @__PURE__ */ jsx(AnalyzeButton, {
|
|
6181
|
+
detail,
|
|
6182
|
+
label: step.analyzeLabel,
|
|
6183
|
+
review
|
|
6184
|
+
}),
|
|
6185
|
+
step.state === "active" && /* @__PURE__ */ jsx(ProgressBar, {
|
|
6186
|
+
label: step.label,
|
|
6187
|
+
progress: step.progress
|
|
6188
|
+
})
|
|
6189
|
+
]
|
|
6190
|
+
});
|
|
6191
|
+
}
|
|
6192
|
+
function ProcessingProgress({ detail, fillsPanel, isProcessing, review, steps }) {
|
|
6193
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6194
|
+
"aria-live": "polite",
|
|
6195
|
+
className: "dt-ses-progress",
|
|
6196
|
+
"data-fills-panel": fillsPanel,
|
|
6197
|
+
children: isProcessing && /* @__PURE__ */ jsx("ol", {
|
|
6198
|
+
"aria-label": "Processing",
|
|
6199
|
+
className: "dt-ses-steps",
|
|
6200
|
+
children: steps.map((step) => /* @__PURE__ */ jsx(StepRow, {
|
|
6201
|
+
detail,
|
|
6202
|
+
review,
|
|
6203
|
+
step
|
|
6204
|
+
}, step.key))
|
|
6205
|
+
})
|
|
6206
|
+
});
|
|
6207
|
+
}
|
|
6208
|
+
//#endregion
|
|
6209
|
+
//#region src/sessions/ui/review/screens/callout-report.ts
|
|
6210
|
+
const clampFraction = (value) => Math.min(1, Math.max(0, value));
|
|
6211
|
+
function visibleFraction(rect, frame) {
|
|
6212
|
+
if (frame.viewportWidth <= 0 || frame.viewportHeight <= 0) return null;
|
|
6213
|
+
const left = clampFraction(rect.x / frame.viewportWidth);
|
|
6214
|
+
const top = clampFraction(rect.y / frame.viewportHeight);
|
|
6215
|
+
const right = clampFraction((rect.x + rect.width) / frame.viewportWidth);
|
|
6216
|
+
const bottom = clampFraction((rect.y + rect.height) / frame.viewportHeight);
|
|
6217
|
+
if (right <= left || bottom <= top) return null;
|
|
6218
|
+
return {
|
|
6219
|
+
height: bottom - top,
|
|
6220
|
+
width: right - left,
|
|
6221
|
+
x: left,
|
|
6222
|
+
y: top
|
|
6223
|
+
};
|
|
6224
|
+
}
|
|
6225
|
+
function shownScreenshots(detail) {
|
|
6226
|
+
const shots = /* @__PURE__ */ new Map();
|
|
6227
|
+
for (const evidence of detail.evidence) {
|
|
6228
|
+
const url = evidence.status === "available" ? evidence.image?.url : null;
|
|
6229
|
+
if (evidence.image && url) shots.set(evidence.id, {
|
|
6230
|
+
evidence,
|
|
6231
|
+
image: {
|
|
6232
|
+
...evidence.image,
|
|
6233
|
+
url
|
|
6234
|
+
}
|
|
6235
|
+
});
|
|
6236
|
+
}
|
|
6237
|
+
return shots;
|
|
6238
|
+
}
|
|
6239
|
+
const readingOrderY = (callout) => callout.outline?.y ?? 0;
|
|
6240
|
+
function calloutReport(detail) {
|
|
6241
|
+
const shots = shownScreenshots(detail);
|
|
6242
|
+
const screensById = /* @__PURE__ */ new Map();
|
|
6243
|
+
const unplaced = [];
|
|
6244
|
+
const reported = detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null);
|
|
6245
|
+
for (const proposal of reported) {
|
|
6246
|
+
const label = proposal.callout ?? proposal.need;
|
|
6247
|
+
const target = proposal.calloutTarget;
|
|
6248
|
+
const shot = target ? shots.get(target.evidenceId) : void 0;
|
|
6249
|
+
if (!(target && shot)) {
|
|
6250
|
+
unplaced.push({
|
|
6251
|
+
label,
|
|
6252
|
+
outline: null,
|
|
6253
|
+
proposal
|
|
6254
|
+
});
|
|
6255
|
+
continue;
|
|
6256
|
+
}
|
|
6257
|
+
const { frame } = shot.evidence;
|
|
6258
|
+
const outline = target.element && frame ? visibleFraction(target.element.rect, frame) : null;
|
|
6259
|
+
const screen = screensById.get(shot.evidence.id) ?? {
|
|
6260
|
+
...shot,
|
|
6261
|
+
callouts: []
|
|
6262
|
+
};
|
|
6263
|
+
screen.callouts.push({
|
|
6264
|
+
label,
|
|
6265
|
+
outline,
|
|
6266
|
+
proposal
|
|
6267
|
+
});
|
|
6268
|
+
screensById.set(shot.evidence.id, screen);
|
|
6269
|
+
}
|
|
6270
|
+
let number = 0;
|
|
6271
|
+
const numbered = (callout) => {
|
|
6272
|
+
number += 1;
|
|
5919
6273
|
return {
|
|
5920
6274
|
...callout,
|
|
5921
6275
|
number
|
|
@@ -6029,10 +6383,154 @@ function layoutCallouts(options) {
|
|
|
6029
6383
|
};
|
|
6030
6384
|
}
|
|
6031
6385
|
return {
|
|
6032
|
-
height: imageTop + image.height + bandHeight(belowRows),
|
|
6033
|
-
imageTop,
|
|
6034
|
-
lines,
|
|
6035
|
-
pills
|
|
6386
|
+
height: imageTop + image.height + bandHeight(belowRows),
|
|
6387
|
+
imageTop,
|
|
6388
|
+
lines,
|
|
6389
|
+
pills
|
|
6390
|
+
};
|
|
6391
|
+
}
|
|
6392
|
+
//#endregion
|
|
6393
|
+
//#region src/sessions/ui/review/optimistic-change.ts
|
|
6394
|
+
function useOptimisticChange(review) {
|
|
6395
|
+
const run = useSessionAction();
|
|
6396
|
+
const applyChange = async (change) => {
|
|
6397
|
+
review.patch(change.show);
|
|
6398
|
+
const saved = await run(change.failureLabel, change.request, () => {
|
|
6399
|
+
applyChange(change);
|
|
6400
|
+
});
|
|
6401
|
+
if (saved === null) {
|
|
6402
|
+
review.patch(change.rollback);
|
|
6403
|
+
return null;
|
|
6404
|
+
}
|
|
6405
|
+
review.patch((detail) => change.settle(detail, saved));
|
|
6406
|
+
return saved;
|
|
6407
|
+
};
|
|
6408
|
+
return applyChange;
|
|
6409
|
+
}
|
|
6410
|
+
//#endregion
|
|
6411
|
+
//#region src/sessions/ui/review/proposals/use-proposal-actions.ts
|
|
6412
|
+
const NOTICE_NEED_LENGTH = 60;
|
|
6413
|
+
const DECISION_NOTICE = {
|
|
6414
|
+
dismissed: "Dismissed",
|
|
6415
|
+
kept: "Confirmed",
|
|
6416
|
+
later: "Saved for later",
|
|
6417
|
+
pending: "Back to undecided"
|
|
6418
|
+
};
|
|
6419
|
+
function quotedNeed(need) {
|
|
6420
|
+
return `“${need.length > NOTICE_NEED_LENGTH ? `${need.slice(0, 59)}…` : need}”`;
|
|
6421
|
+
}
|
|
6422
|
+
function decisionToggle(actions, proposal, decision) {
|
|
6423
|
+
return (event) => {
|
|
6424
|
+
if (event.detail > 1) return;
|
|
6425
|
+
actions.decide([proposal], proposal.decision === decision ? "pending" : decision);
|
|
6426
|
+
};
|
|
6427
|
+
}
|
|
6428
|
+
function useProposalSave(review) {
|
|
6429
|
+
const { api } = useSessionWorkspace();
|
|
6430
|
+
const run = useSessionAction();
|
|
6431
|
+
return async (failureLabel, request) => {
|
|
6432
|
+
const proposal = await run(failureLabel, () => request(api));
|
|
6433
|
+
if (proposal) review.patch((detail) => withProposal(detail, proposal));
|
|
6434
|
+
return proposal;
|
|
6435
|
+
};
|
|
6436
|
+
}
|
|
6437
|
+
function withDecisions(decisions) {
|
|
6438
|
+
return (detail) => ({
|
|
6439
|
+
...detail,
|
|
6440
|
+
proposals: detail.proposals.map((item) => {
|
|
6441
|
+
const decision = decisions.get(item.id);
|
|
6442
|
+
return decision ? {
|
|
6443
|
+
...item,
|
|
6444
|
+
decision
|
|
6445
|
+
} : item;
|
|
6446
|
+
})
|
|
6447
|
+
});
|
|
6448
|
+
}
|
|
6449
|
+
function decisionNotice(proposals, decision) {
|
|
6450
|
+
const [only] = proposals;
|
|
6451
|
+
const subject = proposals.length === 1 && only ? quotedNeed(only.need) : countLabel(proposals.length, "intention");
|
|
6452
|
+
return `${DECISION_NOTICE[decision]} ${subject}`;
|
|
6453
|
+
}
|
|
6454
|
+
function useDecide(review) {
|
|
6455
|
+
const { api } = useSessionWorkspace();
|
|
6456
|
+
const applyChange = useOptimisticChange(review);
|
|
6457
|
+
const changeDecisions = async (failureLabel, from, to) => {
|
|
6458
|
+
const saved = await applyChange({
|
|
6459
|
+
failureLabel,
|
|
6460
|
+
request: () => Promise.all([...to].map(([proposalId, decision]) => api.decideProposal({
|
|
6461
|
+
decision,
|
|
6462
|
+
proposalId
|
|
6463
|
+
}))),
|
|
6464
|
+
rollback: withDecisions(from),
|
|
6465
|
+
settle: (detail, proposals) => proposals.reduce(withProposal, detail),
|
|
6466
|
+
show: withDecisions(to)
|
|
6467
|
+
});
|
|
6468
|
+
if (!saved && to.size > 1) review.reload();
|
|
6469
|
+
return saved;
|
|
6470
|
+
};
|
|
6471
|
+
return async (proposals, decision) => {
|
|
6472
|
+
const previous = new Map(proposals.map((proposal) => [proposal.id, proposal.decision]));
|
|
6473
|
+
const next = new Map(proposals.map((proposal) => [proposal.id, decision]));
|
|
6474
|
+
if (!await changeDecisions("The decision was not saved", previous, next)) return;
|
|
6475
|
+
announce(decisionNotice(proposals, decision), {
|
|
6476
|
+
label: "Undo",
|
|
6477
|
+
run: async () => {
|
|
6478
|
+
if (await changeDecisions("The decision was not undone", next, previous)) announce("Decision undone");
|
|
6479
|
+
}
|
|
6480
|
+
});
|
|
6481
|
+
};
|
|
6482
|
+
}
|
|
6483
|
+
function withFeedbackIds(feedbackIds) {
|
|
6484
|
+
return (detail) => ({
|
|
6485
|
+
...detail,
|
|
6486
|
+
proposals: detail.proposals.map((item) => {
|
|
6487
|
+
const feedbackId = feedbackIds.get(item.id);
|
|
6488
|
+
return feedbackId ? {
|
|
6489
|
+
...item,
|
|
6490
|
+
feedbackId
|
|
6491
|
+
} : item;
|
|
6492
|
+
})
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6495
|
+
function useAddToBoard(review) {
|
|
6496
|
+
const { api } = useSessionWorkspace();
|
|
6497
|
+
const run = useSessionAction();
|
|
6498
|
+
return async (proposals) => {
|
|
6499
|
+
const created = await Promise.all(proposals.map(async (proposal) => {
|
|
6500
|
+
const result = await run(`${quotedNeed(proposal.need)} was not added to the board`, () => api.createFeedback(proposal.id));
|
|
6501
|
+
return result ? [proposal.id, result.feedbackId] : null;
|
|
6502
|
+
}));
|
|
6503
|
+
const added = new Map(created.flatMap((entry) => entry ? [entry] : []));
|
|
6504
|
+
if (added.size === 0) return;
|
|
6505
|
+
review.patch(withFeedbackIds(added));
|
|
6506
|
+
const onlyAdded = added.size === 1 ? proposals.find((proposal) => added.has(proposal.id)) : void 0;
|
|
6507
|
+
announce(onlyAdded ? `Added ${quotedNeed(onlyAdded.need)} to the board` : `Added ${countLabel(added.size, "intention")} to the board`);
|
|
6508
|
+
};
|
|
6509
|
+
}
|
|
6510
|
+
function useProposalActions(review) {
|
|
6511
|
+
const save = useProposalSave(review);
|
|
6512
|
+
const decide = useDecide(review);
|
|
6513
|
+
return {
|
|
6514
|
+
addToBoard: useAddToBoard(review),
|
|
6515
|
+
clarify: (proposal, text) => save("The precision was not sent", (client) => client.clarifyProposal({
|
|
6516
|
+
expectedRevision: proposal.revision,
|
|
6517
|
+
proposalId: proposal.id,
|
|
6518
|
+
text
|
|
6519
|
+
})),
|
|
6520
|
+
decide,
|
|
6521
|
+
merge: async (targetId, sourceIds) => {
|
|
6522
|
+
if (await save("The intentions were not merged", (client) => client.mergeProposals({
|
|
6523
|
+
sourceIds,
|
|
6524
|
+
targetId
|
|
6525
|
+
}))) review.reload();
|
|
6526
|
+
},
|
|
6527
|
+
save,
|
|
6528
|
+
separate: (proposalId) => save("The suggestion was not cleared", (client) => client.separateProposal(proposalId)),
|
|
6529
|
+
verify: (proposalId, { note, status }) => save("The check was not saved", (client) => client.verifyProposal({
|
|
6530
|
+
note: note || void 0,
|
|
6531
|
+
proposalId,
|
|
6532
|
+
status
|
|
6533
|
+
}))
|
|
6036
6534
|
};
|
|
6037
6535
|
}
|
|
6038
6536
|
//#endregion
|
|
@@ -6065,22 +6563,16 @@ function CalloutPill({ callout, context, pillRef, style }) {
|
|
|
6065
6563
|
type: "button",
|
|
6066
6564
|
children: label
|
|
6067
6565
|
}),
|
|
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
|
-
})
|
|
6566
|
+
context.canEdit && TOGGLES.map(({ decision, icon, verb }) => /* @__PURE__ */ jsx("button", {
|
|
6567
|
+
"aria-label": `${verb} “${label}”`,
|
|
6568
|
+
"aria-pressed": proposal.decision === decision,
|
|
6569
|
+
className: "dt-ses-callout-btn",
|
|
6570
|
+
"data-decision": decision,
|
|
6571
|
+
onClick: decisionToggle(context.actions, proposal, decision),
|
|
6572
|
+
title: verb,
|
|
6573
|
+
type: "button",
|
|
6574
|
+
children: icon
|
|
6575
|
+
}, decision))
|
|
6084
6576
|
]
|
|
6085
6577
|
});
|
|
6086
6578
|
}
|
|
@@ -6121,12 +6613,6 @@ function useMeasuredPills(measureKey) {
|
|
|
6121
6613
|
pillRef
|
|
6122
6614
|
};
|
|
6123
6615
|
}
|
|
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
6616
|
function CalloutMark({ callout, imageSize, imageTop }) {
|
|
6131
6617
|
const { outline, proposal } = callout;
|
|
6132
6618
|
if (!outline) return null;
|
|
@@ -6142,7 +6628,8 @@ function CalloutMark({ callout, imageSize, imageTop }) {
|
|
|
6142
6628
|
}
|
|
6143
6629
|
});
|
|
6144
6630
|
}
|
|
6145
|
-
function ScreenFigure({ context, screen }) {
|
|
6631
|
+
function ScreenFigure({ context, newPage, screen }) {
|
|
6632
|
+
const atMs = screen.evidence.capturedAtMs ?? screen.evidence.atMs;
|
|
6146
6633
|
const { callouts, image } = screen;
|
|
6147
6634
|
const { canvasRef, measured, pillRef } = useMeasuredPills(callouts.map(({ label, proposal }) => `${proposal.id}:${label}`).join("|"));
|
|
6148
6635
|
const width = measured?.width ?? 0;
|
|
@@ -6162,7 +6649,10 @@ function ScreenFigure({ context, screen }) {
|
|
|
6162
6649
|
const belowImage = layout ? layout.height - imageTop - imageSize.height : 0;
|
|
6163
6650
|
return /* @__PURE__ */ jsxs("figure", {
|
|
6164
6651
|
className: "dt-ses-report",
|
|
6165
|
-
children: [/* @__PURE__ */
|
|
6652
|
+
children: [/* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(ShotCaption, {
|
|
6653
|
+
atMs,
|
|
6654
|
+
children: newPage
|
|
6655
|
+
}) }), /* @__PURE__ */ jsxs("div", {
|
|
6166
6656
|
className: "dt-ses-report-canvas",
|
|
6167
6657
|
"data-measured": layout !== null,
|
|
6168
6658
|
ref: canvasRef,
|
|
@@ -6172,7 +6662,7 @@ function ScreenFigure({ context, screen }) {
|
|
|
6172
6662
|
},
|
|
6173
6663
|
children: [
|
|
6174
6664
|
/* @__PURE__ */ jsx("img", {
|
|
6175
|
-
alt: `Screenshot at ${
|
|
6665
|
+
alt: `Screenshot at ${formatClock(atMs)}`,
|
|
6176
6666
|
height: image.height,
|
|
6177
6667
|
src: image.url,
|
|
6178
6668
|
width: image.width
|
|
@@ -6220,15 +6710,17 @@ function ScreenFigure({ context, screen }) {
|
|
|
6220
6710
|
})
|
|
6221
6711
|
})
|
|
6222
6712
|
]
|
|
6223
|
-
}), /* @__PURE__ */ jsx("figcaption", {
|
|
6224
|
-
className: "dt-meta",
|
|
6225
|
-
children: screenCaption(screen)
|
|
6226
6713
|
})]
|
|
6227
6714
|
});
|
|
6228
6715
|
}
|
|
6229
6716
|
function ScreenReport({ context, report }) {
|
|
6230
|
-
|
|
6717
|
+
const figures = report.screens.map((screen) => ({
|
|
6718
|
+
page: pagePath(screen.evidence.frame?.url ?? null),
|
|
6719
|
+
screen
|
|
6720
|
+
}));
|
|
6721
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [figures.map(({ page, screen }, index) => /* @__PURE__ */ jsx(ScreenFigure, {
|
|
6231
6722
|
context,
|
|
6723
|
+
newPage: figures[index - 1]?.page === page ? null : page,
|
|
6232
6724
|
screen
|
|
6233
6725
|
}, screen.evidence.id)), report.unplaced.length > 0 && /* @__PURE__ */ jsxs("section", {
|
|
6234
6726
|
className: "dt-ses-block",
|
|
@@ -6439,141 +6931,6 @@ function StartSession({ onStarted, onWriteNote }) {
|
|
|
6439
6931
|
] });
|
|
6440
6932
|
}
|
|
6441
6933
|
//#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
6934
|
//#region src/sessions/ui/review/proposals/delete-intention.tsx
|
|
6578
6935
|
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
6936
|
function DeleteConfirmation$1({ context, onCancel, proposal }) {
|
|
@@ -6884,32 +7241,26 @@ function IntentionSource({ context, proposal }) {
|
|
|
6884
7241
|
if (!segment) return null;
|
|
6885
7242
|
const [firstContext] = segment.context;
|
|
6886
7243
|
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
|
-
]
|
|
7244
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
7245
|
+
className: "dt-ses-source-btn",
|
|
7246
|
+
onClick: (event) => context.navigation.onShowMoment(segment.id, event.currentTarget),
|
|
7247
|
+
title: "Show the moment",
|
|
7248
|
+
type: "button",
|
|
7249
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
7250
|
+
className: "dt-ses-source",
|
|
7251
|
+
children: [
|
|
7252
|
+
/* @__PURE__ */ jsx("span", {
|
|
7253
|
+
className: "dt-ses-time",
|
|
7254
|
+
children: range
|
|
7255
|
+
}),
|
|
7256
|
+
" “",
|
|
7257
|
+
segment.text,
|
|
7258
|
+
"”"
|
|
7259
|
+
]
|
|
7260
|
+
}), firstContext && /* @__PURE__ */ jsx("span", {
|
|
7261
|
+
className: "dt-meta",
|
|
7262
|
+
children: contextLine$1(firstContext)
|
|
7263
|
+
})]
|
|
6913
7264
|
});
|
|
6914
7265
|
}
|
|
6915
7266
|
//#endregion
|
|
@@ -7156,29 +7507,25 @@ function MergeSuggestion({ context, proposal }) {
|
|
|
7156
7507
|
});
|
|
7157
7508
|
}
|
|
7158
7509
|
function DecisionButtons({ context, onEdit, proposal }) {
|
|
7159
|
-
const
|
|
7160
|
-
if (proposal.decision !== decision) context.actions.decide(proposal, decision);
|
|
7161
|
-
};
|
|
7510
|
+
const toggle = (decision) => decisionToggle(context.actions, proposal, decision);
|
|
7162
7511
|
const isKept = proposal.decision === "kept";
|
|
7163
7512
|
return /* @__PURE__ */ jsxs("div", {
|
|
7164
7513
|
className: "dt-actions",
|
|
7165
7514
|
children: [
|
|
7166
7515
|
/* @__PURE__ */ jsxs("button", {
|
|
7167
|
-
"aria-disabled": isKept,
|
|
7168
7516
|
"aria-pressed": isKept,
|
|
7169
7517
|
className: "dt-btn",
|
|
7170
7518
|
"data-variant": "primary",
|
|
7171
|
-
onClick:
|
|
7519
|
+
onClick: toggle("kept"),
|
|
7172
7520
|
type: "button",
|
|
7173
7521
|
children: [/* @__PURE__ */ jsx(CheckIcon, { size: 14 }), "Confirm"]
|
|
7174
7522
|
}),
|
|
7175
7523
|
ICON_DECISIONS.map(({ decision, icon, label }) => /* @__PURE__ */ jsx("button", {
|
|
7176
|
-
"aria-disabled": proposal.decision === decision,
|
|
7177
7524
|
"aria-label": label,
|
|
7178
7525
|
"aria-pressed": proposal.decision === decision,
|
|
7179
7526
|
className: "dt-btn",
|
|
7180
7527
|
"data-icon-only": true,
|
|
7181
|
-
onClick:
|
|
7528
|
+
onClick: toggle(decision),
|
|
7182
7529
|
title: label,
|
|
7183
7530
|
type: "button",
|
|
7184
7531
|
children: icon
|
|
@@ -7356,146 +7703,100 @@ function ProposalCard({ context, proposal }) {
|
|
|
7356
7703
|
//#endregion
|
|
7357
7704
|
//#region src/sessions/ui/review/proposals/proposals-tab.tsx
|
|
7358
7705
|
function emptyCopy(detail) {
|
|
7359
|
-
|
|
7360
|
-
if (session.collection !== "ended") return "Intentions appear here once the recording has uploaded and Reflet has read it.";
|
|
7706
|
+
if (detail.session.collection !== "ended") return "Intentions appear here once the recording has uploaded and Reflet has read it.";
|
|
7361
7707
|
if (detail.segments.length === 0 && detail.marks.length === 0) return "Nothing was said or marked in this session.";
|
|
7362
|
-
|
|
7363
|
-
if (session.analysis.state === "failed") return "The transcript is kept. Read it, or write the intention yourself.";
|
|
7364
|
-
if (session.analysis.state === "succeeded") return "No request stood out in this session. Read the transcript or write the intention yourself.";
|
|
7365
|
-
return "Intentions appear here once the session is analyzed.";
|
|
7366
|
-
}
|
|
7367
|
-
function useAddProposal(context, onDone) {
|
|
7368
|
-
const [need, setNeed] = useState("");
|
|
7369
|
-
const [observed, setObserved] = useState("");
|
|
7370
|
-
const [clientKey] = useState(() => crypto.randomUUID());
|
|
7371
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
7372
|
-
const sessionId = context.detail.session.id;
|
|
7373
|
-
const add = async () => {
|
|
7374
|
-
setIsSaving(true);
|
|
7375
|
-
try {
|
|
7376
|
-
if (await context.actions.save("The intention was not added", (api) => api.createProposal({
|
|
7377
|
-
clientKey,
|
|
7378
|
-
need: need.trim(),
|
|
7379
|
-
observed: observed.trim() || void 0,
|
|
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
|
-
};
|
|
7708
|
+
return "No request stood out in this session.";
|
|
7394
7709
|
}
|
|
7395
|
-
function
|
|
7396
|
-
|
|
7397
|
-
|
|
7398
|
-
|
|
7399
|
-
|
|
7400
|
-
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
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
|
-
]
|
|
7710
|
+
function ConfirmAll({ context, undecided }) {
|
|
7711
|
+
return /* @__PURE__ */ jsx("div", {
|
|
7712
|
+
className: "dt-actions",
|
|
7713
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
7714
|
+
className: "dt-btn",
|
|
7715
|
+
onClick: () => context.actions.decide(undecided, "kept"),
|
|
7716
|
+
type: "button",
|
|
7717
|
+
children: [
|
|
7718
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 14 }),
|
|
7719
|
+
"Confirm all",
|
|
7720
|
+
/* @__PURE__ */ jsx("span", {
|
|
7721
|
+
className: "dt-btn-count",
|
|
7722
|
+
children: undecided.length
|
|
7723
|
+
})
|
|
7724
|
+
]
|
|
7725
|
+
})
|
|
7424
7726
|
});
|
|
7425
7727
|
}
|
|
7426
7728
|
function ProposalsTab({ context, view }) {
|
|
7427
|
-
const
|
|
7428
|
-
const
|
|
7429
|
-
const
|
|
7430
|
-
const
|
|
7729
|
+
const { detail, review } = context;
|
|
7730
|
+
const steps = useProcessingSteps(detail);
|
|
7731
|
+
const isProcessing = steps.some((step) => step.state !== "done");
|
|
7732
|
+
const proposals = detail.proposals.filter((proposal) => proposal.mergedIntoId === null);
|
|
7733
|
+
const undecided = proposals.filter((proposal) => proposal.decision === "pending" && proposal.attachment === null);
|
|
7734
|
+
const { continuation } = detail;
|
|
7735
|
+
const hasNothingToReview = proposals.length === 0 && !continuation;
|
|
7736
|
+
const report = calloutReport(detail);
|
|
7431
7737
|
const showsScreens = report.screens.length > 0 && view === "screens";
|
|
7432
7738
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7739
|
+
/* @__PURE__ */ jsx(ProcessingProgress, {
|
|
7740
|
+
detail,
|
|
7741
|
+
fillsPanel: hasNothingToReview,
|
|
7742
|
+
isProcessing,
|
|
7743
|
+
review,
|
|
7744
|
+
steps
|
|
7745
|
+
}),
|
|
7433
7746
|
continuation && /* @__PURE__ */ jsx(UpdateCard, {
|
|
7434
7747
|
context,
|
|
7435
7748
|
continuation
|
|
7436
7749
|
}),
|
|
7437
|
-
|
|
7750
|
+
hasNothingToReview && !isProcessing && /* @__PURE__ */ jsx("p", {
|
|
7438
7751
|
className: "dt-empty",
|
|
7439
|
-
children: emptyCopy(
|
|
7752
|
+
children: emptyCopy(detail)
|
|
7440
7753
|
}),
|
|
7441
|
-
|
|
7754
|
+
context.canEdit && undecided.length > 1 && /* @__PURE__ */ jsx(ConfirmAll, {
|
|
7442
7755
|
context,
|
|
7443
|
-
|
|
7756
|
+
undecided
|
|
7444
7757
|
}),
|
|
7445
|
-
|
|
7446
|
-
context,
|
|
7447
|
-
proposal
|
|
7448
|
-
}, proposal.id)),
|
|
7449
|
-
context.canEdit && (isAdding ? /* @__PURE__ */ jsx(AddProposalForm, {
|
|
7758
|
+
showsScreens && /* @__PURE__ */ jsx(ScreenReport, {
|
|
7450
7759
|
context,
|
|
7451
|
-
|
|
7452
|
-
})
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
type: "button",
|
|
7458
|
-
children: "Add an intention"
|
|
7459
|
-
})
|
|
7460
|
-
}))
|
|
7760
|
+
report
|
|
7761
|
+
}),
|
|
7762
|
+
proposals.filter((proposal) => !showsScreens || proposal.attachment !== null).map((proposal) => /* @__PURE__ */ jsx(ProposalCard, {
|
|
7763
|
+
context,
|
|
7764
|
+
proposal
|
|
7765
|
+
}, proposal.id))
|
|
7461
7766
|
] });
|
|
7462
7767
|
}
|
|
7463
7768
|
//#endregion
|
|
7464
7769
|
//#region src/sessions/ui/review/review-footer.tsx
|
|
7770
|
+
const NOTHING_CONFIRMED = "Confirm an intention first";
|
|
7465
7771
|
function ReviewFooter({ context, onOpenBrief }) {
|
|
7466
7772
|
const confirmed = keptProposals(context.detail);
|
|
7467
7773
|
const offBoard = confirmed.filter((proposal) => proposal.feedbackId === null);
|
|
7774
|
+
const hasConfirmed = confirmed.length > 0;
|
|
7775
|
+
const boardBlocker = hasConfirmed ? "Every confirmed intention is on the board" : NOTHING_CONFIRMED;
|
|
7468
7776
|
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
|
-
]
|
|
7777
|
+
className: "dt-sheet-foot dt-ses-foot",
|
|
7778
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
7779
|
+
className: "dt-btn",
|
|
7780
|
+
"data-variant": "primary",
|
|
7781
|
+
disabled: !hasConfirmed,
|
|
7782
|
+
onClick: onOpenBrief,
|
|
7783
|
+
title: hasConfirmed ? void 0 : NOTHING_CONFIRMED,
|
|
7784
|
+
type: "button",
|
|
7785
|
+
children: ["Prepare brief", hasConfirmed && /* @__PURE__ */ jsx("span", {
|
|
7786
|
+
className: "dt-btn-count",
|
|
7787
|
+
children: confirmed.length
|
|
7788
|
+
})]
|
|
7789
|
+
}), /* @__PURE__ */ jsxs("button", {
|
|
7790
|
+
className: "dt-btn",
|
|
7791
|
+
disabled: offBoard.length === 0,
|
|
7792
|
+
onClick: () => context.actions.addToBoard(offBoard),
|
|
7793
|
+
title: offBoard.length === 0 ? boardBlocker : void 0,
|
|
7794
|
+
type: "button",
|
|
7795
|
+
children: ["Add to board", offBoard.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
7796
|
+
className: "dt-btn-count",
|
|
7797
|
+
children: offBoard.length
|
|
7798
|
+
})]
|
|
7799
|
+
})]
|
|
7499
7800
|
});
|
|
7500
7801
|
}
|
|
7501
7802
|
//#endregion
|
|
@@ -7750,295 +8051,144 @@ function DeleteConfirmation({ onCancel, onDeleted, sessionId }) {
|
|
|
7750
8051
|
})]
|
|
7751
8052
|
});
|
|
7752
8053
|
}
|
|
7753
|
-
function
|
|
7754
|
-
const
|
|
7755
|
-
const
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
|
|
7764
|
-
|
|
7765
|
-
|
|
7766
|
-
|
|
7767
|
-
|
|
7768
|
-
|
|
7769
|
-
onSelect: () => setIsConfirmingDelete(true),
|
|
7770
|
-
variant: "danger"
|
|
7771
|
-
}
|
|
7772
|
-
];
|
|
7773
|
-
return /* @__PURE__ */ jsxs("header", {
|
|
7774
|
-
className: "dt-ses-head",
|
|
7775
|
-
children: [
|
|
7776
|
-
isRenaming ? /* @__PURE__ */ jsx(TitleEditor, {
|
|
7777
|
-
onDone: () => setIsRenaming(false),
|
|
7778
|
-
review,
|
|
7779
|
-
session
|
|
7780
|
-
}) : /* @__PURE__ */ jsxs("div", {
|
|
7781
|
-
className: "dt-card-head",
|
|
7782
|
-
children: [/* @__PURE__ */ jsx("h2", {
|
|
7783
|
-
className: "dt-ses-title",
|
|
7784
|
-
tabIndex: -1,
|
|
7785
|
-
children: session.title
|
|
7786
|
-
}), session.canEdit && /* @__PURE__ */ jsx(Menu, {
|
|
7787
|
-
items: menuItems,
|
|
7788
|
-
trigger: {
|
|
7789
|
-
className: "icon-btn",
|
|
7790
|
-
content: /* @__PURE__ */ jsx(MoreIcon, {}),
|
|
7791
|
-
label: "Session options"
|
|
7792
|
-
}
|
|
7793
|
-
})]
|
|
7794
|
-
}),
|
|
7795
|
-
/* @__PURE__ */ jsxs("p", {
|
|
7796
|
-
className: "dt-meta",
|
|
7797
|
-
children: [
|
|
7798
|
-
formatDate(session.startedAt),
|
|
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
|
-
]
|
|
7813
|
-
});
|
|
7814
|
-
}
|
|
7815
|
-
//#endregion
|
|
7816
|
-
//#region src/sessions/ui/review/review-tabs.tsx
|
|
7817
|
-
const INTENTION_VIEWS = [{
|
|
7818
|
-
label: "On screen",
|
|
7819
|
-
value: "screens"
|
|
7820
|
-
}, {
|
|
7821
|
-
label: "Details",
|
|
7822
|
-
value: "cards"
|
|
7823
|
-
}];
|
|
7824
|
-
function IntentionViewMenu({ place }) {
|
|
7825
|
-
return /* @__PURE__ */ jsx(Menu, {
|
|
7826
|
-
items: INTENTION_VIEWS.map((option) => ({
|
|
7827
|
-
checked: option.value === place.intentionView,
|
|
7828
|
-
label: option.label,
|
|
7829
|
-
onSelect: () => place.selectIntentionView(option.value)
|
|
7830
|
-
})),
|
|
7831
|
-
trigger: {
|
|
7832
|
-
className: "icon-btn",
|
|
7833
|
-
content: /* @__PURE__ */ jsx(ViewOptionsIcon, {}),
|
|
7834
|
-
label: "Show intentions"
|
|
7835
|
-
}
|
|
7836
|
-
});
|
|
7837
|
-
}
|
|
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: "Evidence",
|
|
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
|
|
8054
|
+
function SessionFacts({ session }) {
|
|
8055
|
+
const isShared = session.visibility === "team";
|
|
8056
|
+
const sharing = isShared ? "Shared with team" : "Private";
|
|
8057
|
+
return /* @__PURE__ */ jsxs("p", {
|
|
8058
|
+
className: "dt-ses-facts",
|
|
8059
|
+
children: [
|
|
8060
|
+
/* @__PURE__ */ jsx("span", { children: formatDate(session.startedAt) }),
|
|
8061
|
+
/* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx(ClockIcon, {}), shortDuration(session.durationMs)] }),
|
|
8062
|
+
/* @__PURE__ */ jsx("span", {
|
|
8063
|
+
"aria-label": sharing,
|
|
8064
|
+
role: "img",
|
|
8065
|
+
title: sharing,
|
|
8066
|
+
children: isShared ? /* @__PURE__ */ jsx(TeamIcon, {}) : /* @__PURE__ */ jsx(LockIcon, {})
|
|
8067
|
+
}),
|
|
8068
|
+
!session.isMine && /* @__PURE__ */ jsx("span", { children: session.authorName ?? "A teammate" })
|
|
8069
|
+
]
|
|
8013
8070
|
});
|
|
8014
8071
|
}
|
|
8015
|
-
function
|
|
8016
|
-
|
|
8017
|
-
|
|
8018
|
-
|
|
8019
|
-
|
|
8020
|
-
|
|
8021
|
-
|
|
8072
|
+
function ReviewHeader({ onDeleted, review, session }) {
|
|
8073
|
+
const [isRenaming, setIsRenaming] = useState(false);
|
|
8074
|
+
const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);
|
|
8075
|
+
const update = useSessionUpdate(review, session);
|
|
8076
|
+
const isShared = session.visibility === "team";
|
|
8077
|
+
const menuItems = [
|
|
8078
|
+
{
|
|
8079
|
+
label: "Rename",
|
|
8080
|
+
onSelect: () => setIsRenaming(true)
|
|
8081
|
+
},
|
|
8082
|
+
{
|
|
8083
|
+
label: isShared ? "Make private" : "Share with team",
|
|
8084
|
+
onSelect: () => update("The sharing was not changed", { visibility: isShared ? "private" : "team" })
|
|
8085
|
+
},
|
|
8086
|
+
{
|
|
8087
|
+
label: "Delete session",
|
|
8088
|
+
onSelect: () => setIsConfirmingDelete(true),
|
|
8089
|
+
variant: "danger"
|
|
8090
|
+
}
|
|
8091
|
+
];
|
|
8092
|
+
return /* @__PURE__ */ jsxs("header", {
|
|
8093
|
+
className: "dt-ses-head",
|
|
8094
|
+
children: [isRenaming ? /* @__PURE__ */ jsx(TitleEditor, {
|
|
8095
|
+
onDone: () => setIsRenaming(false),
|
|
8096
|
+
review,
|
|
8097
|
+
session
|
|
8098
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
8099
|
+
className: "dt-card-head",
|
|
8100
|
+
children: [
|
|
8101
|
+
/* @__PURE__ */ jsx("h2", {
|
|
8102
|
+
className: "dt-ses-title",
|
|
8103
|
+
tabIndex: -1,
|
|
8104
|
+
title: session.title,
|
|
8105
|
+
children: session.title
|
|
8106
|
+
}),
|
|
8107
|
+
/* @__PURE__ */ jsx(SessionFacts, { session }),
|
|
8108
|
+
session.canEdit && /* @__PURE__ */ jsx(Menu, {
|
|
8109
|
+
items: menuItems,
|
|
8110
|
+
trigger: {
|
|
8111
|
+
className: "icon-btn",
|
|
8112
|
+
content: /* @__PURE__ */ jsx(MoreIcon, {}),
|
|
8113
|
+
label: "Session options"
|
|
8114
|
+
}
|
|
8115
|
+
})
|
|
8116
|
+
]
|
|
8117
|
+
}), isConfirmingDelete && session.canEdit && /* @__PURE__ */ jsx(DeleteConfirmation, {
|
|
8118
|
+
onCancel: () => setIsConfirmingDelete(false),
|
|
8119
|
+
onDeleted,
|
|
8120
|
+
sessionId: session.id
|
|
8022
8121
|
})]
|
|
8023
8122
|
});
|
|
8024
8123
|
}
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8124
|
+
//#endregion
|
|
8125
|
+
//#region src/sessions/ui/review/review-tabs.tsx
|
|
8126
|
+
const INTENTION_VIEWS = [{
|
|
8127
|
+
label: "On screen",
|
|
8128
|
+
value: "screens"
|
|
8129
|
+
}, {
|
|
8130
|
+
label: "Details",
|
|
8131
|
+
value: "cards"
|
|
8132
|
+
}];
|
|
8133
|
+
function IntentionViewMenu({ place }) {
|
|
8134
|
+
return /* @__PURE__ */ jsx(Menu, {
|
|
8135
|
+
items: INTENTION_VIEWS.map((option) => ({
|
|
8136
|
+
checked: option.value === place.intentionView,
|
|
8137
|
+
label: option.label,
|
|
8138
|
+
onSelect: () => place.selectIntentionView(option.value)
|
|
8139
|
+
})),
|
|
8140
|
+
trigger: {
|
|
8141
|
+
className: "icon-btn",
|
|
8142
|
+
content: /* @__PURE__ */ jsx(ViewOptionsIcon, {}),
|
|
8143
|
+
label: "Show intentions"
|
|
8144
|
+
}
|
|
8145
|
+
});
|
|
8146
|
+
}
|
|
8147
|
+
function tabOptions(detail) {
|
|
8148
|
+
return [
|
|
8149
|
+
{
|
|
8150
|
+
count: detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null).length,
|
|
8151
|
+
icon: /* @__PURE__ */ jsx(IntentionIcon, {}),
|
|
8152
|
+
label: "Intentions",
|
|
8153
|
+
value: "proposals"
|
|
8154
|
+
},
|
|
8155
|
+
{
|
|
8156
|
+
count: null,
|
|
8157
|
+
icon: /* @__PURE__ */ jsx(TranscriptIcon, {}),
|
|
8158
|
+
label: "Transcript",
|
|
8159
|
+
value: "transcript"
|
|
8160
|
+
},
|
|
8161
|
+
{
|
|
8162
|
+
count: detail.evidence.length,
|
|
8163
|
+
icon: /* @__PURE__ */ jsx(ImageIcon, {}),
|
|
8164
|
+
label: "Screenshots",
|
|
8165
|
+
value: "evidence"
|
|
8166
|
+
}
|
|
8167
|
+
];
|
|
8168
|
+
}
|
|
8169
|
+
function ReviewTabs({ detail, place }) {
|
|
8170
|
+
const canPickView = place.tab === "proposals" && calloutReport(detail).screens.length > 0;
|
|
8171
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
8172
|
+
className: "dt-toolbar",
|
|
8173
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
8174
|
+
className: "dt-tabs",
|
|
8175
|
+
role: "tablist",
|
|
8176
|
+
children: tabOptions(detail).map((option) => /* @__PURE__ */ jsxs("button", {
|
|
8177
|
+
"aria-selected": option.value === place.tab,
|
|
8178
|
+
className: "dt-tab",
|
|
8179
|
+
onClick: () => place.selectTab(option.value),
|
|
8180
|
+
role: "tab",
|
|
8181
|
+
type: "button",
|
|
8182
|
+
children: [
|
|
8183
|
+
option.icon,
|
|
8184
|
+
option.label,
|
|
8185
|
+
option.count !== null && /* @__PURE__ */ jsx("span", {
|
|
8186
|
+
className: "dt-tab-count",
|
|
8187
|
+
children: option.count
|
|
8188
|
+
})
|
|
8189
|
+
]
|
|
8190
|
+
}, option.value))
|
|
8191
|
+
}), canPickView && /* @__PURE__ */ jsx(IntentionViewMenu, { place })]
|
|
8042
8192
|
});
|
|
8043
8193
|
}
|
|
8044
8194
|
//#endregion
|
|
@@ -8190,17 +8340,16 @@ function LinkEvidenceItem({ link }) {
|
|
|
8190
8340
|
]
|
|
8191
8341
|
});
|
|
8192
8342
|
}
|
|
8193
|
-
function
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8201
|
-
});
|
|
8343
|
+
function EvidenceFace({ evidence }) {
|
|
8344
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ShotCaption, {
|
|
8345
|
+
atMs: evidence.atMs,
|
|
8346
|
+
children: evidenceCaption(evidence)
|
|
8347
|
+
}), /* @__PURE__ */ jsx(EvidenceThumb, {
|
|
8348
|
+
evidence,
|
|
8349
|
+
isLarge: true
|
|
8350
|
+
})] });
|
|
8202
8351
|
}
|
|
8203
|
-
function EvidenceItem({ evidence,
|
|
8352
|
+
function EvidenceItem({ evidence, onShowMoment, removal }) {
|
|
8204
8353
|
const caption = `${formatClock(evidence.atMs)} · ${evidenceCaption(evidence)}`;
|
|
8205
8354
|
if (evidence.link) return /* @__PURE__ */ jsx(LinkEvidenceItem, { link: evidence.link });
|
|
8206
8355
|
if (!isViewable(evidence)) return /* @__PURE__ */ jsx(MissingEvidenceItem, {
|
|
@@ -8209,27 +8358,22 @@ function EvidenceItem({ evidence, openers, removal }) {
|
|
|
8209
8358
|
isJustRemoved: evidence.id === removal.removedId
|
|
8210
8359
|
});
|
|
8211
8360
|
return /* @__PURE__ */ jsxs("li", {
|
|
8212
|
-
className: "dt-card",
|
|
8361
|
+
className: "dt-card dt-ses-openable",
|
|
8213
8362
|
children: [
|
|
8214
|
-
/* @__PURE__ */ jsx("button", {
|
|
8215
|
-
"aria-label": `
|
|
8216
|
-
className: "dt-ses-shot-
|
|
8217
|
-
onClick:
|
|
8363
|
+
onShowMoment ? /* @__PURE__ */ jsx("button", {
|
|
8364
|
+
"aria-label": `Show the moment at ${caption}`,
|
|
8365
|
+
className: "dt-ses-open dt-ses-shot-face",
|
|
8366
|
+
onClick: (event) => onShowMoment(event.currentTarget),
|
|
8218
8367
|
type: "button",
|
|
8219
|
-
children: /* @__PURE__ */ jsx(
|
|
8220
|
-
}),
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
children: caption
|
|
8368
|
+
children: /* @__PURE__ */ jsx(EvidenceFace, { evidence })
|
|
8369
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
8370
|
+
className: "dt-ses-shot-face",
|
|
8371
|
+
children: /* @__PURE__ */ jsx(EvidenceFace, { evidence })
|
|
8224
8372
|
}),
|
|
8225
8373
|
evidence.note && /* @__PURE__ */ jsx("p", {
|
|
8226
8374
|
className: "dt-note-text",
|
|
8227
8375
|
children: evidence.note
|
|
8228
8376
|
}),
|
|
8229
|
-
/* @__PURE__ */ jsx(MomentLink, {
|
|
8230
|
-
atMs: evidence.atMs,
|
|
8231
|
-
onShowMoment: openers.onShowMoment
|
|
8232
|
-
}),
|
|
8233
8377
|
removal.canRemove && /* @__PURE__ */ jsx(RemoveEvidence, {
|
|
8234
8378
|
evidence,
|
|
8235
8379
|
removal
|
|
@@ -8237,54 +8381,6 @@ function EvidenceItem({ evidence, openers, removal }) {
|
|
|
8237
8381
|
]
|
|
8238
8382
|
});
|
|
8239
8383
|
}
|
|
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
8384
|
function AttachLinkAction({ detail, review }) {
|
|
8289
8385
|
const [isAttaching, setIsAttaching] = useState(false);
|
|
8290
8386
|
if (isAttaching) return /* @__PURE__ */ jsx(AttachLink, {
|
|
@@ -8303,34 +8399,34 @@ function AttachLinkAction({ detail, review }) {
|
|
|
8303
8399
|
});
|
|
8304
8400
|
}
|
|
8305
8401
|
function EvidenceTab({ detail, onShowMoment, review }) {
|
|
8306
|
-
const [enlargedId, setEnlargedId] = useState(null);
|
|
8307
8402
|
const [removedId, setRemovedId] = useState(null);
|
|
8308
|
-
const
|
|
8403
|
+
const { canEdit } = detail.session;
|
|
8309
8404
|
const removal = {
|
|
8310
|
-
canRemove:
|
|
8311
|
-
onRemoved:
|
|
8312
|
-
setEnlargedId(null);
|
|
8313
|
-
setRemovedId(evidenceId);
|
|
8314
|
-
},
|
|
8405
|
+
canRemove: canEdit,
|
|
8406
|
+
onRemoved: setRemovedId,
|
|
8315
8407
|
removedId,
|
|
8316
8408
|
review
|
|
8317
8409
|
};
|
|
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, {
|
|
8410
|
+
const momentOpener = (atMs) => {
|
|
8411
|
+
const segmentId = nearestSegmentId(detail.segments, atMs);
|
|
8412
|
+
return segmentId ? (opener) => onShowMoment(segmentId, opener) : null;
|
|
8413
|
+
};
|
|
8414
|
+
const attachLink = canEdit && /* @__PURE__ */ jsx(AttachLinkAction, {
|
|
8331
8415
|
detail,
|
|
8332
8416
|
review
|
|
8333
|
-
})
|
|
8417
|
+
});
|
|
8418
|
+
if (detail.evidence.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
8419
|
+
className: "dt-empty",
|
|
8420
|
+
children: [/* @__PURE__ */ jsx("p", { children: "No screenshots or links in this session." }), attachLink]
|
|
8421
|
+
});
|
|
8422
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("ul", {
|
|
8423
|
+
className: "dt-ses-rows",
|
|
8424
|
+
children: detail.evidence.map((item) => /* @__PURE__ */ jsx(EvidenceItem, {
|
|
8425
|
+
evidence: item,
|
|
8426
|
+
onShowMoment: momentOpener(item.atMs),
|
|
8427
|
+
removal
|
|
8428
|
+
}, item.id))
|
|
8429
|
+
}), attachLink] });
|
|
8334
8430
|
}
|
|
8335
8431
|
//#endregion
|
|
8336
8432
|
//#region src/sessions/ui/review/transcript/segment-row.tsx
|
|
@@ -8444,22 +8540,17 @@ function ContextChips({ segment }) {
|
|
|
8444
8540
|
}, `${item.kind}:${item.atMs}:${item.label}`))]
|
|
8445
8541
|
});
|
|
8446
8542
|
}
|
|
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
|
-
})]
|
|
8543
|
+
function MomentTime({ onShowMoment, time }) {
|
|
8544
|
+
if (!onShowMoment) return /* @__PURE__ */ jsx("span", {
|
|
8545
|
+
className: "dt-ses-time",
|
|
8546
|
+
children: time
|
|
8547
|
+
});
|
|
8548
|
+
return /* @__PURE__ */ jsx("button", {
|
|
8549
|
+
"aria-label": `Show the moment at ${time}`,
|
|
8550
|
+
className: "dt-ses-time dt-ses-open",
|
|
8551
|
+
onClick: (event) => onShowMoment(event.currentTarget),
|
|
8552
|
+
type: "button",
|
|
8553
|
+
children: time
|
|
8463
8554
|
});
|
|
8464
8555
|
}
|
|
8465
8556
|
function SegmentRow({ editing, onShowMoment, segment }) {
|
|
@@ -8468,14 +8559,15 @@ function SegmentRow({ editing, onShowMoment, segment }) {
|
|
|
8468
8559
|
const time = formatClock(segment.startMs);
|
|
8469
8560
|
const isSettled = ["done", "empty"].includes(segment.transcription.state);
|
|
8470
8561
|
return /* @__PURE__ */ jsxs("li", {
|
|
8471
|
-
className: "dt-ses-seg",
|
|
8472
|
-
children: [
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8476
|
-
|
|
8477
|
-
|
|
8478
|
-
|
|
8562
|
+
className: "dt-ses-seg dt-ses-openable",
|
|
8563
|
+
children: [
|
|
8564
|
+
/* @__PURE__ */ jsx(MomentTime, {
|
|
8565
|
+
onShowMoment: isEditing ? null : onShowMoment,
|
|
8566
|
+
time
|
|
8567
|
+
}),
|
|
8568
|
+
/* @__PURE__ */ jsxs("div", {
|
|
8569
|
+
className: "dt-ses-seg-body",
|
|
8570
|
+
children: [isEditing ? /* @__PURE__ */ jsx(SegmentEditor, {
|
|
8479
8571
|
onDone: () => setIsEditing(false),
|
|
8480
8572
|
review,
|
|
8481
8573
|
segment
|
|
@@ -8483,15 +8575,17 @@ function SegmentRow({ editing, onShowMoment, segment }) {
|
|
|
8483
8575
|
onType: canEdit ? () => setIsEditing(true) : void 0,
|
|
8484
8576
|
review,
|
|
8485
8577
|
segment
|
|
8486
|
-
}),
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8578
|
+
}), /* @__PURE__ */ jsx(ContextChips, { segment })]
|
|
8579
|
+
}),
|
|
8580
|
+
canEdit && isSettled && !isEditing && /* @__PURE__ */ jsx("button", {
|
|
8581
|
+
"aria-label": `Edit passage at ${time}`,
|
|
8582
|
+
className: "icon-btn glass dt-ses-hover-action",
|
|
8583
|
+
onClick: () => setIsEditing(true),
|
|
8584
|
+
title: "Edit",
|
|
8585
|
+
type: "button",
|
|
8586
|
+
children: /* @__PURE__ */ jsx(PencilIcon, {})
|
|
8587
|
+
})
|
|
8588
|
+
]
|
|
8495
8589
|
});
|
|
8496
8590
|
}
|
|
8497
8591
|
//#endregion
|
|
@@ -8573,25 +8667,18 @@ function transcriptForAgent(detail) {
|
|
|
8573
8667
|
//#endregion
|
|
8574
8668
|
//#region src/sessions/ui/review/transcript/transcript-tab.tsx
|
|
8575
8669
|
function TimelineNote({ atMs, children, onShowMoment }) {
|
|
8576
|
-
const time = formatClock(atMs);
|
|
8577
8670
|
return /* @__PURE__ */ jsxs("li", {
|
|
8578
|
-
className: "dt-ses-seg",
|
|
8671
|
+
className: "dt-ses-seg dt-ses-openable",
|
|
8579
8672
|
"data-kind": "note",
|
|
8580
|
-
children: [/* @__PURE__ */ jsx(
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
}), /* @__PURE__ */
|
|
8673
|
+
children: [/* @__PURE__ */ jsx(MomentTime, {
|
|
8674
|
+
onShowMoment,
|
|
8675
|
+
time: formatClock(atMs)
|
|
8676
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
8584
8677
|
className: "dt-ses-seg-body",
|
|
8585
|
-
children:
|
|
8678
|
+
children: /* @__PURE__ */ jsx("p", {
|
|
8586
8679
|
className: "dt-meta",
|
|
8587
8680
|
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
|
-
})]
|
|
8681
|
+
})
|
|
8595
8682
|
})]
|
|
8596
8683
|
});
|
|
8597
8684
|
}
|
|
@@ -8786,21 +8873,14 @@ function ReviewPanels({ context, place }) {
|
|
|
8786
8873
|
}
|
|
8787
8874
|
function ReviewTop({ context, links }) {
|
|
8788
8875
|
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
|
-
] });
|
|
8876
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ReviewHeader, {
|
|
8877
|
+
onDeleted: links.onDeleted,
|
|
8878
|
+
review,
|
|
8879
|
+
session: detail.session
|
|
8880
|
+
}), detail.continuation && /* @__PURE__ */ jsx(ContinuationHeader, {
|
|
8881
|
+
continuation: detail.continuation,
|
|
8882
|
+
onOpenSession: links.onOpenSession
|
|
8883
|
+
})] });
|
|
8804
8884
|
}
|
|
8805
8885
|
function ReviewScreen({ detail, isBriefOpen, links, review }) {
|
|
8806
8886
|
const place = useReviewPlace();
|
|
@@ -9169,7 +9249,8 @@ p.dt-ses-diff { display: block; }
|
|
|
9169
9249
|
//#endregion
|
|
9170
9250
|
//#region src/sessions/ui/review/screens/report-styles.ts
|
|
9171
9251
|
const REPORT_STYLES = `
|
|
9172
|
-
.dt-ses-report { display: flex; flex-direction: column; gap:
|
|
9252
|
+
.dt-ses-report { display: flex; flex-direction: column; gap: 6px; width: 100%; min-width: 0; margin: 0; }
|
|
9253
|
+
.dt-ses-report + .dt-ses-report { margin-top: 12px; }
|
|
9173
9254
|
.dt-ses-report-canvas { position: relative; width: 100%; }
|
|
9174
9255
|
.dt-ses-report-canvas img { display: block; width: 100%; max-width: 100%; height: auto; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
|
|
9175
9256
|
.dt-ses-report-canvas[data-measured="false"] .dt-ses-callout { visibility: hidden; }
|
|
@@ -9183,21 +9264,25 @@ const REPORT_STYLES = `
|
|
|
9183
9264
|
.dt-ses-callouts { margin: 0; padding: 0; list-style: none; }
|
|
9184
9265
|
.dt-ses-callouts[data-flow="list"] { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
9185
9266
|
.dt-ses-callout {
|
|
9186
|
-
|
|
9187
|
-
|
|
9267
|
+
--dt-callout-control: 22px; --dt-callout-inset: 3px;
|
|
9268
|
+
display: flex; align-items: flex-start; gap: 2px; max-width: 100%; padding: var(--dt-callout-inset);
|
|
9269
|
+
border-radius: calc(var(--dt-callout-control) / 2 + var(--dt-callout-inset));
|
|
9270
|
+
background: var(--rf-bg); box-shadow: var(--rf-shadow-inset), var(--rf-shadow-low); font-size: 12px; line-height: 16px;
|
|
9188
9271
|
}
|
|
9189
9272
|
.dt-ses-report-canvas .dt-ses-callout { position: absolute; top: 0; left: 0; width: max-content; max-width: min(280px, 100%); }
|
|
9190
9273
|
.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);
|
|
9274
|
+
display: grid; place-items: center; flex: none; min-width: var(--dt-callout-control); height: var(--dt-callout-control); padding: 0 6px;
|
|
9275
|
+
border-radius: calc(var(--dt-callout-control) / 2); background: var(--rf-accent); color: var(--rf-accent-text, #fff);
|
|
9276
|
+
font-size: 11px; font-weight: 650; font-variant-numeric: tabular-nums;
|
|
9193
9277
|
}
|
|
9194
9278
|
.dt-ses-callout[data-decision="kept"] .dt-ses-callout-number { background: var(--rf-success); }
|
|
9195
9279
|
.dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-number { background: var(--rf-text-muted); }
|
|
9196
|
-
.dt-ses-callout-label { min-width: 0; padding:
|
|
9280
|
+
.dt-ses-callout-label { flex: 1 1 auto; min-width: 0; padding: 3px 4px; font-weight: 550; text-align: left; overflow-wrap: anywhere; }
|
|
9197
9281
|
.dt-ses-callout-label:hover { color: var(--rf-accent); }
|
|
9198
9282
|
.dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-label { color: var(--rf-text-muted); text-decoration: line-through; }
|
|
9199
9283
|
.dt-ses-callout-btn {
|
|
9200
|
-
display: grid; place-items: center; flex: none; width:
|
|
9284
|
+
display: grid; place-items: center; flex: none; width: var(--dt-callout-control); height: var(--dt-callout-control);
|
|
9285
|
+
border-radius: calc(var(--dt-callout-control) / 2); color: var(--rf-text-muted);
|
|
9201
9286
|
transition: background-color 120ms var(--rf-ease), color 120ms var(--rf-ease);
|
|
9202
9287
|
}
|
|
9203
9288
|
.dt-ses-callout-btn svg { width: 13px; height: 13px; }
|
|
@@ -9239,21 +9324,47 @@ const SESSIONS_STYLES = `${`
|
|
|
9239
9324
|
.dt-ses-list li + li { margin-top: 3px; }
|
|
9240
9325
|
.dt-ses-link { align-self: flex-start; padding: 0; color: var(--rf-accent); font-size: 12px; text-align: left; }
|
|
9241
9326
|
.dt-ses-link:hover { text-decoration: underline; }
|
|
9242
|
-
.dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-
|
|
9327
|
+
.dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-handoff { display: flex; flex-direction: column; gap: 6px; }
|
|
9243
9328
|
.dt-ses-banner { padding: 8px 10px; border-radius: var(--dt-radius-card); background: color-mix(in srgb, var(--rf-accent) 10%, transparent); }
|
|
9244
9329
|
.dt-ses-banner p { margin: 0; overflow-wrap: anywhere; }
|
|
9245
|
-
.dt-ses-
|
|
9246
|
-
.dt-ses-
|
|
9247
|
-
.dt-ses-
|
|
9248
|
-
.dt-ses-steps
|
|
9330
|
+
.dt-ses-progress:empty { display: none; }
|
|
9331
|
+
.dt-ses-progress[data-fills-panel="true"] { align-self: center; width: min(100%, 280px); margin: auto; padding: 24px 0; }
|
|
9332
|
+
.dt-sheet-body:has(.dt-ses-progress[data-fills-panel="true"]) > .dt-sheet-foot { margin-top: 0; }
|
|
9333
|
+
.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; }
|
|
9334
|
+
.dt-ses-steps li { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
|
|
9335
|
+
.dt-ses-steps li > span:first-child { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; }
|
|
9249
9336
|
.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
9337
|
.dt-ses-steps li[data-state="done"]::before { border-color: var(--rf-accent); background: var(--rf-accent); }
|
|
9251
9338
|
.dt-ses-steps li[data-state="active"] { color: var(--rf-text); }
|
|
9252
9339
|
.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
9340
|
.dt-ses-steps li[data-state="failed"] { color: var(--rf-danger); }
|
|
9254
9341
|
.dt-ses-steps li[data-state="failed"]::before { background: currentColor; }
|
|
9342
|
+
.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); }
|
|
9343
|
+
.dt-ses-bar > span { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: var(--rf-accent); transition: width 300ms var(--rf-ease); }
|
|
9344
|
+
.dt-ses-bar > span[data-indeterminate="true"] { width: 35%; animation: dt-bar-slide 1.3s ease-in-out infinite; }
|
|
9255
9345
|
@keyframes dt-step-spin { to { rotate: 360deg; } }
|
|
9256
|
-
@
|
|
9346
|
+
@keyframes dt-bar-slide { from { translate: -100% 0; } to { translate: 300% 0; } }
|
|
9347
|
+
@media (prefers-reduced-motion: reduce) {
|
|
9348
|
+
.dt-ses-steps li::before { animation: none; }
|
|
9349
|
+
.dt-ses-bar > span[data-indeterminate="true"] { width: 100%; opacity: .4; animation: none; }
|
|
9350
|
+
}
|
|
9351
|
+
.dt-ses-openable { position: relative; transition: background-color 120ms var(--rf-ease); }
|
|
9352
|
+
.dt-ses-openable:has(> .dt-ses-open):hover { background: var(--rf-bg-hover); }
|
|
9353
|
+
.dt-ses-open { padding: 0; text-align: left; }
|
|
9354
|
+
.dt-ses-open::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
|
|
9355
|
+
.dt-ses-open:focus-visible { outline: none; }
|
|
9356
|
+
.dt-ses-open:focus-visible::after { outline: 2px solid var(--rf-accent); outline-offset: -2px; }
|
|
9357
|
+
.dt-ses-openable :is(a, button, input, select, textarea):not(.dt-ses-open, .dt-ses-hover-action) { position: relative; z-index: 1; }
|
|
9358
|
+
.dt-ses-openable > .dt-ses-hover-action {
|
|
9359
|
+
position: absolute; top: 6px; right: 6px; z-index: 2; width: 28px; height: 28px; opacity: 0;
|
|
9360
|
+
transition: opacity 120ms var(--rf-ease), background 160ms var(--rf-ease), color 160ms var(--rf-ease);
|
|
9361
|
+
}
|
|
9362
|
+
.dt-ses-hover-action svg { width: 14px; height: 14px; }
|
|
9363
|
+
.dt-ses-openable:hover > .dt-ses-hover-action, .dt-ses-openable > .dt-ses-hover-action:focus-visible { opacity: 1; }
|
|
9364
|
+
@media (hover: none) {
|
|
9365
|
+
.dt-ses-openable > .dt-ses-hover-action { opacity: 1; }
|
|
9366
|
+
.dt-ses-seg:has(> .dt-ses-hover-action) { padding-right: 40px; }
|
|
9367
|
+
}
|
|
9257
9368
|
.dt-ses-rows { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
|
|
9258
9369
|
.dt-ses-row {
|
|
9259
9370
|
display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%;
|
|
@@ -9263,8 +9374,17 @@ button.dt-ses-row:hover { background: var(--rf-bg-hover); }
|
|
|
9263
9374
|
.dt-ses-row .dt-card-title { max-width: 100%; }
|
|
9264
9375
|
${`
|
|
9265
9376
|
.dt-ses-title { flex: 1; min-width: 0; margin: 0; font-size: 15px; font-weight: 650; overflow-wrap: anywhere; }
|
|
9377
|
+
.dt-ses-head > .dt-card-head { gap: 10px; }
|
|
9378
|
+
.dt-ses-head .dt-ses-title { flex: 0 1 auto; overflow: hidden; font-size: 14px; text-overflow: ellipsis; white-space: nowrap; }
|
|
9266
9379
|
.dt-ses-head .dt-menu > .icon-btn { width: 30px; height: 30px; }
|
|
9380
|
+
.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; }
|
|
9381
|
+
.dt-ses-facts > span { display: inline-flex; align-items: center; gap: 4px; }
|
|
9382
|
+
.dt-ses-facts svg { width: 13px; height: 13px; }
|
|
9383
|
+
.dt-ses-foot { flex-wrap: nowrap; }
|
|
9384
|
+
.dt-ses-foot > .dt-btn { flex: 1 1 0; justify-content: center; height: 32px; }
|
|
9267
9385
|
.dt-btn[data-icon-only] { justify-content: center; width: 28px; padding: 0; }
|
|
9386
|
+
.dt-btn[aria-pressed="true"] { background: color-mix(in srgb, var(--rf-text) 16%, transparent); }
|
|
9387
|
+
.dt-btn[data-variant="primary"][aria-pressed="true"] { background: var(--rf-success); color: #fff; }
|
|
9268
9388
|
.dt-ses-field { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--rf-text-muted); }
|
|
9269
9389
|
.dt-ses-need {
|
|
9270
9390
|
margin: 0; font-weight: 600; overflow-wrap: anywhere;
|
|
@@ -9279,7 +9399,7 @@ ${`
|
|
|
9279
9399
|
.dt-ses-details section { display: flex; flex-direction: column; gap: 2px; }
|
|
9280
9400
|
.dt-ses-quote { padding: 2px 0; font-size: 12px; text-align: left; overflow-wrap: anywhere; }
|
|
9281
9401
|
.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; }
|
|
9402
|
+
.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
9403
|
.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
9404
|
.dt-ses-timeline { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
|
|
9285
9405
|
.dt-ses-seg { display: flex; gap: 8px; padding: 6px 8px; border-radius: var(--dt-radius-field); }
|
|
@@ -9289,10 +9409,18 @@ ${`
|
|
|
9289
9409
|
`}${`
|
|
9290
9410
|
.dt-ses-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
9291
9411
|
.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); }
|
|
9412
|
+
.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
9413
|
.dt-ses-shot[data-large="true"] img { max-height: none; }
|
|
9294
9414
|
.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-
|
|
9415
|
+
.dt-ses-shot-face { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; }
|
|
9416
|
+
.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; }
|
|
9417
|
+
.dt-ses-caption-time { display: inline-flex; align-items: center; gap: 4px; color: var(--rf-text); font-weight: 550; font-variant-numeric: tabular-nums; }
|
|
9418
|
+
.dt-ses-caption-time svg { width: 12px; height: 12px; color: var(--rf-text-muted); }
|
|
9419
|
+
.dt-ses-source-btn {
|
|
9420
|
+
display: flex; flex-direction: column; gap: 2px; margin: 0 -8px; padding: 6px 8px; border-radius: var(--dt-radius-field);
|
|
9421
|
+
text-align: left; transition: background-color 120ms var(--rf-ease);
|
|
9422
|
+
}
|
|
9423
|
+
.dt-ses-source-btn:hover { background: var(--rf-bg-hover); }
|
|
9296
9424
|
.dt-ses-start { display: flex; flex-direction: column; gap: 8px; }
|
|
9297
9425
|
.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
9426
|
.dt-ses-brief-entry > summary::-webkit-details-marker { display: none; }
|
|
@@ -12271,6 +12399,7 @@ const DEVTOOLS_STYLES = `
|
|
|
12271
12399
|
position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
|
12272
12400
|
margin: auto -10px -10px; padding: 10px; border-top: 1px solid var(--rf-border); background: var(--rf-bg);
|
|
12273
12401
|
}
|
|
12402
|
+
.dt-sheet-body:has(.dt-empty) > .dt-sheet-foot { margin-top: 0; }
|
|
12274
12403
|
.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
12404
|
.dt-archived .dt-meta { flex: 1 1 100%; }
|
|
12276
12405
|
.dt-group { margin: 6px 2px 0; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--rf-text-muted); }
|
|
@@ -13414,4 +13543,4 @@ function useSheetTab() {
|
|
|
13414
13543
|
//#endregion
|
|
13415
13544
|
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
13545
|
|
|
13417
|
-
//# sourceMappingURL=use-sheet-tab-
|
|
13546
|
+
//# sourceMappingURL=use-sheet-tab-azUezLAq.js.map
|