reflet-sdk 0.7.3 → 0.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { a as EDITORS, n as DEVTOOLS_REQUEST_HEADER, r as DEVTOOLS_ROUTE_BASE, t as DEVTOOLS_ENDPOINTS } from "./protocol-cJVVwnPe.js";
2
- import { B as clip, D as MoreIcon, G as describeElement, K as getElementRect, M as TargetIcon, O as PencilIcon, R as DEFAULT_WIDGET_LABELS, S as CheckIcon, T as GripIcon, W as buildSelector, Y as redactUrl, Z as inspectReactElement, _ as matchesHotkey, a as uploadBlob, c as useFloatingPosition, g as isSubmitEnter, it as releaseCapture, j as Svg, l as onViewportChange, m as startsInWidget, o as ElementPicker, p as listenToKeydown, q as REDACT_ATTRIBUTE, s as isWidgetOwned, tt as captureViewport, u as visibleViewport, w as CloseIcon } from "./submit-Dl0rm6TV.js";
2
+ import { B as clip, D as MoreIcon, G as describeElement, K as getElementRect, M as TargetIcon, O as PencilIcon, R as DEFAULT_WIDGET_LABELS, S as CheckIcon, T as GripIcon, W as buildSelector, Y as redactUrl, Z as inspectReactElement, _ as matchesHotkey, a as uploadBlob, c as useFloatingPosition, g as isSubmitEnter, it as releaseCapture, j as Svg, l as onViewportChange, m as startsInWidget, o as ElementPicker, p as listenToKeydown, q as REDACT_ATTRIBUTE, s as isWidgetOwned, tt as captureViewport, u as visibleViewport, w as CloseIcon } from "./submit-CG4LXioK.js";
3
3
  import { t as REFLET_Z_INDEX } from "./z-index-CwokqVq_.js";
4
4
  import { z } from "zod";
5
5
  import { createContext, useContext, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
@@ -274,6 +274,8 @@ const sessionIdentitySchema = z.object({
274
274
  user: z.object({
275
275
  email: z.string().nullable(),
276
276
  id: z.string(),
277
+ /** Absent from older Reflet backends. */
278
+ image: z.string().nullable().optional(),
277
279
  name: z.string().nullable()
278
280
  })
279
281
  });
@@ -3285,11 +3287,25 @@ function preciseSeconds(ms) {
3285
3287
  function minutesLeft(remainingMs) {
3286
3288
  return Math.max(1, Math.ceil(remainingMs / MINUTE_MS));
3287
3289
  }
3288
- function formatDate(epochMs) {
3289
- return new Date(epochMs).toLocaleString(void 0, {
3290
- dateStyle: "medium",
3291
- timeStyle: "short"
3292
- });
3290
+ function startOfDay(epochMs, daysBack) {
3291
+ const day = new Date(epochMs);
3292
+ day.setHours(0, 0, 0, 0);
3293
+ day.setDate(day.getDate() - daysBack);
3294
+ return day.getTime();
3295
+ }
3296
+ function dayLabel(epochMs, now) {
3297
+ if (epochMs >= startOfDay(now, 0)) return "Today";
3298
+ if (epochMs >= startOfDay(now, 1)) return "Yesterday";
3299
+ const date = new Date(epochMs);
3300
+ const isThisYear = date.getFullYear() === new Date(now).getFullYear();
3301
+ return date.toLocaleDateString(void 0, isThisYear ? {
3302
+ day: "numeric",
3303
+ month: "short"
3304
+ } : { dateStyle: "medium" });
3305
+ }
3306
+ function formatDate(epochMs, now = Date.now()) {
3307
+ const time = new Date(epochMs).toLocaleTimeString(void 0, { timeStyle: "short" });
3308
+ return `${dayLabel(epochMs, now)}, ${time}`;
3293
3309
  }
3294
3310
  function isAnalysisBusy(summary) {
3295
3311
  return summary.analysis.state === "waiting" || summary.analysis.state === "running";
@@ -3333,13 +3349,6 @@ function PauseIcon() {
3333
3349
  function PlayIcon() {
3334
3350
  return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m8 5 11 7-11 7z" }) });
3335
3351
  }
3336
- function PersonIcon() {
3337
- return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("circle", {
3338
- cx: "12",
3339
- cy: "8",
3340
- r: "4"
3341
- }), /* @__PURE__ */ jsx("path", { d: "M4.5 20a7.5 7.5 0 0 1 15 0" })] });
3342
- }
3343
3352
  function ClockIcon() {
3344
3353
  return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("circle", {
3345
3354
  cx: "12",
@@ -3347,6 +3356,29 @@ function ClockIcon() {
3347
3356
  r: "8.5"
3348
3357
  }), /* @__PURE__ */ jsx("path", { d: "M12 7.5V12l3 2" })] });
3349
3358
  }
3359
+ function IntentionIcon() {
3360
+ 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" }) });
3361
+ }
3362
+ function TranscriptIcon() {
3363
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M4 6h16M4 11h16M4 16h10" }) });
3364
+ }
3365
+ function ImageIcon() {
3366
+ return /* @__PURE__ */ jsxs(Svg, { children: [
3367
+ /* @__PURE__ */ jsx("rect", {
3368
+ height: "16",
3369
+ rx: "2.5",
3370
+ width: "18",
3371
+ x: "3",
3372
+ y: "4"
3373
+ }),
3374
+ /* @__PURE__ */ jsx("circle", {
3375
+ cx: "9",
3376
+ cy: "10",
3377
+ r: "1.6"
3378
+ }),
3379
+ /* @__PURE__ */ jsx("path", { d: "m21 16-5-5-9 9" })
3380
+ ] });
3381
+ }
3350
3382
  //#endregion
3351
3383
  //#region src/sessions/ui/recording/level-meter.tsx
3352
3384
  const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
@@ -3604,179 +3636,6 @@ function RecordingLayer({ onFinished }) {
3604
3636
  })] });
3605
3637
  }
3606
3638
  //#endregion
3607
- //#region src/sessions/ui/intention-styles.ts
3608
- const INTENTION_STYLES = `
3609
- .dt-ses-panel { display: contents; }
3610
- .dt-ses-panel[hidden] { display: none; }
3611
- .dt-ses-moment, .dt-ses-moment-section, .dt-ses-block, .dt-ses-change, .dt-ses-history { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
3612
- .dt-ses-moment-item { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border-radius: 10px; background: var(--rf-bg-subtle); }
3613
- .dt-ses-moment-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; overflow-wrap: anywhere; }
3614
- .dt-ses-moment-quote { display: flex; flex-direction: column; gap: 2px; margin: 0; padding-left: 10px; border-left: 2px solid var(--rf-border); overflow-wrap: anywhere; }
3615
- .dt-ses-moment-notices { color: var(--rf-text-muted); font-size: 12px; }
3616
- .dt-ses-moment-shot { display: flex; flex-direction: column; gap: 4px; margin: 0; max-width: 100%; }
3617
- .dt-ses-shot-mark[data-match="approximate"] { border-style: dashed; }
3618
- .dt-ses-shot-pointer {
3619
- position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
3620
- background: var(--rf-accent); box-shadow: 0 0 0 2px var(--rf-bg); pointer-events: none;
3621
- }
3622
- .dt-ses-source { margin: 0; font-size: 12px; overflow-wrap: anywhere; }
3623
- .dt-ses-context-line { display: block; }
3624
- .dt-ses-attached { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; }
3625
- .dt-ses-attached p { margin: 0; }
3626
- .dt-ses-history[data-removed="true"] > .dt-ses-subhead { color: var(--rf-text-muted); text-decoration: line-through; }
3627
- .dt-ses-change { padding: 8px 10px; border-radius: 10px; background: var(--rf-bg-subtle); list-style: none; }
3628
- .dt-ses-change[data-decision="rejected"] .dt-ses-diff { color: var(--rf-text-muted); text-decoration: line-through; }
3629
- .dt-ses-diff { display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; margin: 0; overflow-wrap: anywhere; }
3630
- p.dt-ses-diff { display: block; }
3631
- .dt-ses-diff dd { margin: 0; }
3632
- .dt-card:focus-visible { outline: 2px solid var(--rf-accent); outline-offset: 2px; }
3633
- `;
3634
- //#endregion
3635
- //#region src/sessions/ui/review/screens/report-styles.ts
3636
- const REPORT_STYLES = `
3637
- .dt-ses-report { display: flex; flex-direction: column; gap: 4px; width: 100%; min-width: 0; margin: 0; }
3638
- .dt-ses-report-canvas { position: relative; width: 100%; }
3639
- .dt-ses-report-canvas img { display: block; width: 100%; max-width: 100%; height: auto; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
3640
- .dt-ses-report-canvas[data-measured="false"] .dt-ses-callout { visibility: hidden; }
3641
- .dt-ses-report-mark { --dt-mark: var(--rf-accent); position: absolute; border: 2px solid var(--dt-mark); border-radius: 4px; pointer-events: none; }
3642
- .dt-ses-report-lines { position: absolute; inset: 0; overflow: visible; pointer-events: none; }
3643
- .dt-ses-report-lines g { color: var(--rf-accent); }
3644
- .dt-ses-report-lines line { stroke: currentColor; stroke-width: 1.5; }
3645
- .dt-ses-report-lines circle { fill: currentColor; }
3646
- .dt-ses-report-mark[data-decision="kept"], .dt-ses-report-lines g[data-decision="kept"] { --dt-mark: var(--rf-success); color: var(--rf-success); }
3647
- .dt-ses-report-mark[data-decision="dismissed"], .dt-ses-report-lines g[data-decision="dismissed"] { opacity: .35; }
3648
- .dt-ses-callouts { margin: 0; padding: 0; list-style: none; }
3649
- .dt-ses-callouts[data-flow="list"] { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
3650
- .dt-ses-callout {
3651
- display: flex; align-items: center; gap: 4px; max-width: 100%; padding: 3px; border-radius: var(--rf-pill);
3652
- background: var(--rf-bg); box-shadow: var(--rf-shadow-inset), var(--rf-shadow-low); font-size: 12px; line-height: 1.3;
3653
- }
3654
- .dt-ses-report-canvas .dt-ses-callout { position: absolute; top: 0; left: 0; width: max-content; max-width: min(280px, 100%); }
3655
- .dt-ses-callout-number {
3656
- display: grid; place-items: center; flex: none; min-width: 20px; height: 20px; padding: 0 5px; border-radius: var(--rf-pill);
3657
- background: var(--rf-accent); color: var(--rf-accent-text, #fff); font-size: 11px; font-weight: 650; font-variant-numeric: tabular-nums;
3658
- }
3659
- .dt-ses-callout[data-decision="kept"] .dt-ses-callout-number { background: var(--rf-success); }
3660
- .dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-number { background: var(--rf-text-muted); }
3661
- .dt-ses-callout-label { min-width: 0; padding: 0 4px; font-weight: 550; text-align: left; overflow-wrap: anywhere; }
3662
- .dt-ses-callout-label:hover { color: var(--rf-accent); }
3663
- .dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-label { color: var(--rf-text-muted); text-decoration: line-through; }
3664
- .dt-ses-callout-btn {
3665
- display: grid; place-items: center; flex: none; width: 22px; height: 22px; border-radius: 50%; color: var(--rf-text-muted);
3666
- transition: background-color 120ms var(--rf-ease), color 120ms var(--rf-ease);
3667
- }
3668
- .dt-ses-callout-btn svg { width: 13px; height: 13px; }
3669
- .dt-ses-callout-btn:hover { background: var(--rf-bg-hover); color: var(--rf-text); }
3670
- .dt-ses-callout-btn[data-decision="kept"][aria-pressed="true"] { background: var(--rf-success); color: #fff; }
3671
- .dt-ses-callout-btn[data-decision="dismissed"][aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
3672
- `;
3673
- //#endregion
3674
- //#region src/sessions/ui/sessions-styles.ts
3675
- const MONO_FONT$1 = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
3676
- const SESSIONS_STYLES = `${`
3677
- .dt-rec {
3678
- position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar}; left: 50%; translate: -50% 0;
3679
- bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + 16px);
3680
- display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px;
3681
- max-width: calc(100vw - 24px); padding: 6px 6px 6px 12px; border-radius: 18px;
3682
- animation: dt-rec-in 240ms var(--rf-ease);
3683
- }
3684
- @keyframes dt-rec-in { from { transform: translateY(8px); opacity: 0; } }
3685
- .dt-rec-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--rf-danger); }
3686
- .dt-rec[data-phase="recording"] .dt-rec-dot { animation: dt-rec-pulse 1.6s ease-in-out infinite; }
3687
- .dt-rec[data-phase="paused"] .dt-rec-dot, .dt-rec[data-phase="finishing"] .dt-rec-dot { background: var(--rf-text-muted); }
3688
- @keyframes dt-rec-pulse { 50% { opacity: .35; } }
3689
- .dt-rec-meter { display: inline-flex; align-items: center; gap: 2px; height: 16px; }
3690
- .dt-rec-meter span {
3691
- width: 3px; height: 100%; border-radius: 2px; background: var(--rf-text-muted);
3692
- scale: 1 calc(.2 + var(--dt-level, 0) * .8); transition: scale 90ms linear;
3693
- }
3694
- .dt-rec-meter span:nth-child(2) { scale: 1 calc(.2 + var(--dt-level, 0)); }
3695
- .dt-rec-time { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 5ch; }
3696
- .dt-rec-status { color: var(--rf-text-muted); font-size: 12px; overflow-wrap: anywhere; }
3697
- .dt-rec-status:empty { display: none; }
3698
- .dt-rec-note { display: flex; align-items: center; gap: 6px; flex: 1 1 100%; font-size: 12px; color: var(--rf-text-muted); }
3699
- .dt-rec-note .dt-field { flex: 1; min-width: 0; padding: 4px 8px; }
3700
- `}
3701
- .dt-ses-subhead, .dt-ses-label { margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--rf-text); }
3702
- .dt-ses-label { font-size: 11px; color: var(--rf-text-muted); }
3703
- .dt-ses-list { margin: 0; padding-left: 18px; overflow-wrap: anywhere; }
3704
- .dt-ses-list li + li { margin-top: 3px; }
3705
- .dt-ses-link { align-self: flex-start; padding: 0; color: var(--rf-accent); font-size: 12px; text-align: left; }
3706
- .dt-ses-link:hover { text-decoration: underline; }
3707
- .dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-strip, .dt-ses-handoff { display: flex; flex-direction: column; gap: 6px; }
3708
- .dt-ses-banner { padding: 8px 10px; border-radius: 10px; background: color-mix(in srgb, var(--rf-accent) 10%, transparent); }
3709
- .dt-ses-banner p { margin: 0; overflow-wrap: anywhere; }
3710
- .dt-ses-strip:empty { display: none; }
3711
- .dt-ses-rows { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
3712
- .dt-ses-row {
3713
- display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%;
3714
- padding: 8px 10px; border-radius: 10px; background: var(--rf-bg-subtle); text-align: left;
3715
- }
3716
- button.dt-ses-row:hover { background: var(--rf-bg-hover); }
3717
- .dt-ses-row .dt-card-title { max-width: 100%; }
3718
- ${`
3719
- .dt-ses-title { flex: 1; min-width: 0; margin: 0; font-size: 15px; font-weight: 650; overflow-wrap: anywhere; }
3720
- .dt-ses-menu { position: relative; flex: none; }
3721
- .dt-ses-menu > .icon-btn { width: 30px; height: 30px; }
3722
- .dt-ses-menu-list {
3723
- position: absolute; top: calc(100% + 4px); right: 0; z-index: 2; display: flex; flex-direction: column; min-width: 168px; padding: 4px;
3724
- border: 1px solid var(--rf-border); border-radius: var(--rf-radius-sm); background: var(--rf-bg); box-shadow: var(--rf-shadow);
3725
- }
3726
- .dt-ses-menu-item { display: flex; align-items: center; height: 30px; padding: 0 10px; border-radius: 6px; color: var(--rf-text); font-size: 12.5px; text-align: left; }
3727
- .dt-ses-menu-item:hover, .dt-ses-menu-item:focus-visible { background: var(--rf-bg-hover); }
3728
- .dt-ses-menu-item[data-variant="danger"] { color: var(--rf-danger); }
3729
- .dt-btn[data-icon-only] { justify-content: center; width: 28px; padding: 0; }
3730
- .dt-ses-tabs { flex-wrap: wrap; margin: 0; }
3731
- .dt-ses-field { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--rf-text-muted); }
3732
- .dt-ses-need {
3733
- margin: 0; font-weight: 600; overflow-wrap: anywhere;
3734
- display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
3735
- }
3736
- .dt-ses-need[data-expanded="true"] { display: block; }
3737
- .dt-card[data-decision="dismissed"] .dt-ses-need { color: var(--rf-text-muted); text-decoration: line-through; }
3738
- .dt-ses-chips { display: flex; flex-wrap: wrap; gap: 4px; }
3739
- .dt-ses-chips:empty { display: none; }
3740
- .dt-ses-details { display: flex; flex-direction: column; gap: 8px; }
3741
- .dt-ses-details[hidden] { display: none; }
3742
- .dt-ses-details section { display: flex; flex-direction: column; gap: 2px; }
3743
- .dt-ses-quote { padding: 2px 0; font-size: 12px; text-align: left; overflow-wrap: anywhere; }
3744
- .dt-ses-quote:hover { color: var(--rf-accent); }
3745
- .dt-ses-time { flex: none; color: var(--rf-text-muted); font: 11.5px ${MONO_FONT$1}; font-variant-numeric: tabular-nums; }
3746
- .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); }
3747
- .dt-ses-timeline { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
3748
- .dt-ses-seg { display: flex; gap: 8px; padding: 6px 8px; border-radius: 8px; }
3749
- .dt-ses-seg:focus { outline: 2px solid var(--rf-accent); outline-offset: -2px; }
3750
- .dt-ses-seg[data-kind="note"] { padding-block: 2px; }
3751
- .dt-ses-seg-body { display: flex; flex: 1; flex-direction: column; gap: 4px; min-width: 0; }
3752
- `}${`
3753
- .dt-ses-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }
3754
- .dt-ses-shot { position: relative; display: inline-block; max-width: 100%; line-height: 0; }
3755
- .dt-ses-shot img { display: block; max-width: 100%; height: auto; max-height: 132px; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
3756
- .dt-ses-shot[data-large="true"] img { max-height: none; }
3757
- .dt-ses-shot-mark { position: absolute; border: 2px solid var(--rf-accent); border-radius: 4px; pointer-events: none; }
3758
- .dt-ses-shot-btn { align-self: flex-start; padding: 0; border-radius: 8px; }
3759
- .dt-ses-check { display: flex; align-items: flex-start; gap: 6px; overflow-wrap: anywhere; }
3760
- .dt-ses-check input { flex: none; margin-top: 3px; accent-color: var(--rf-accent); }
3761
- .dt-ses-choices { margin: 0; padding: 0; border: 0; }
3762
- .dt-ses-choices .dt-ses-list { padding-left: 0; list-style: none; }
3763
- .dt-ses-brief {
3764
- max-height: 50vh; overflow: auto; margin: 0; padding: 10px; border-radius: 10px;
3765
- background: var(--rf-bg-subtle); font: 11.5px/1.55 ${MONO_FONT$1}; white-space: pre-wrap; overflow-wrap: anywhere;
3766
- }
3767
- `}${`
3768
- .dt-team-anchor {
3769
- --dt-edge-gap: calc(max(var(--rf-offset), 12px) + 48px);
3770
- position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar}; width: min(300px, calc(100vw - 24px));
3771
- }
3772
- .dt-team-anchor[data-side="left"] { left: calc(var(--rf-viewport-left, 0px) + max(12px, var(--rf-offset))); }
3773
- .dt-team-anchor[data-side="right"] { right: calc(var(--rf-viewport-right, 0px) + max(12px, var(--rf-offset))); }
3774
- .dt-team-anchor[data-edge="bottom"] { bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + var(--dt-edge-gap)); }
3775
- .dt-team-anchor[data-edge="top"] { top: calc(var(--rf-viewport-top, 0px) + env(safe-area-inset-top) + var(--dt-edge-gap)); }
3776
- .dt-team-card { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; border-radius: 14px; animation: dt-popover-in 200ms var(--rf-ease); }
3777
- .dt-bar > .dt-btn { margin-inline: 2px 4px; }
3778
- `}${INTENTION_STYLES}${REPORT_STYLES}`;
3779
- //#endregion
3780
3639
  //#region src/devtools/client/clipboard.ts
3781
3640
  /**
3782
3641
  * Safari only lets a page write the clipboard inside the key press or click
@@ -3789,1057 +3648,572 @@ async function copyToClipboard(text) {
3789
3648
  await navigator.clipboard.write([new ClipboardItem({ "text/plain": blob })]);
3790
3649
  }
3791
3650
  //#endregion
3792
- //#region src/devtools/client/devtools-icons.tsx
3793
- function NotesIcon() {
3794
- return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01" }) });
3651
+ //#region src/sessions/ui/handoff/zip-archive.ts
3652
+ const LOCAL_HEADER_SIGNATURE = 67324752;
3653
+ const CENTRAL_HEADER_SIGNATURE = 33639248;
3654
+ const END_SIGNATURE = 101010256;
3655
+ const LOCAL_HEADER_SIZE = 30;
3656
+ const CENTRAL_HEADER_SIZE = 46;
3657
+ const END_RECORD_SIZE = 22;
3658
+ const ZIP_VERSION = 20;
3659
+ const UTF8_NAMES_FLAG = 2048;
3660
+ const DEFLATE_METHOD = 8;
3661
+ const GZIP_HEADER_SIZE = 10;
3662
+ const GZIP_FLAGS_OFFSET = 3;
3663
+ const GZIP_TRAILER_SIZE = 8;
3664
+ const DOS_EPOCH_YEAR = 1980;
3665
+ function dosStamp(date) {
3666
+ return {
3667
+ date: (date.getFullYear() - DOS_EPOCH_YEAR) * 512 + (date.getMonth() + 1) * 32 + date.getDate(),
3668
+ time: date.getHours() * 2048 + date.getMinutes() * 32 + Math.floor(date.getSeconds() / 2)
3669
+ };
3795
3670
  }
3796
- function InboxIcon() {
3797
- return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }), /* @__PURE__ */ jsx("path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" })] });
3671
+ /** gzip wraps raw deflate data and ends with the CRC-32 zip needs, so the browser computes both natively. */
3672
+ async function deflateEntry(data) {
3673
+ const stream = new Response(data).body?.pipeThrough(new CompressionStream("gzip"));
3674
+ const gzip = new Uint8Array(await new Response(stream).arrayBuffer());
3675
+ if (gzip[GZIP_FLAGS_OFFSET] !== 0) throw new Error("The browser wrote an unexpected gzip header.");
3676
+ return {
3677
+ checksum: new DataView(gzip.buffer, gzip.length - GZIP_TRAILER_SIZE).getUint32(0, true),
3678
+ compressed: gzip.slice(GZIP_HEADER_SIZE, gzip.length - GZIP_TRAILER_SIZE)
3679
+ };
3798
3680
  }
3799
- //#endregion
3800
- //#region src/devtools/client/devtools-bar.tsx
3801
- const BAR_POSITION_KEY = "reflet-devtools-bar-position";
3802
- const PICK_BUTTON_LABEL = "Pick an element: note it, copy it, or Shift+click to open its code";
3803
- function BarButton({ children, label, onClick, pressed }) {
3804
- return /* @__PURE__ */ jsx("button", {
3805
- "aria-label": label,
3806
- "aria-pressed": pressed,
3807
- className: "icon-btn",
3808
- onClick,
3809
- title: label,
3810
- type: "button",
3811
- children
3812
- });
3681
+ function writeCommonFields(view, start, options) {
3682
+ const { record, stamp } = options;
3683
+ view.setUint16(start, ZIP_VERSION, true);
3684
+ view.setUint16(start + 2, UTF8_NAMES_FLAG, true);
3685
+ view.setUint16(start + 4, DEFLATE_METHOD, true);
3686
+ view.setUint16(start + 6, stamp.time, true);
3687
+ view.setUint16(start + 8, stamp.date, true);
3688
+ view.setUint32(start + 10, record.checksum, true);
3689
+ view.setUint32(start + 14, record.compressed.length, true);
3690
+ view.setUint32(start + 18, record.size, true);
3691
+ view.setUint16(start + 22, record.name.length, true);
3692
+ view.setUint16(start + 24, 0, true);
3813
3693
  }
3814
- function DevtoolsBar({ children, edge, label = "Reflet devtools", side }) {
3815
- const floating = useFloatingPosition(`${edge}-${side}`, false, {
3816
- anchorSelector: ".dt-bar",
3817
- persistKey: BAR_POSITION_KEY
3818
- });
3819
- return /* @__PURE__ */ jsxs("div", {
3820
- "aria-label": label,
3821
- className: "dt-bar glass",
3822
- "data-edge": edge,
3823
- "data-moved": floating.position !== null,
3824
- "data-side": side,
3825
- ref: floating.rootRef,
3826
- role: "toolbar",
3827
- children: [
3828
- /* @__PURE__ */ jsx("style", { children: floating.styles }),
3829
- /* @__PURE__ */ jsx("button", {
3830
- "aria-label": `Move ${label} (double-click to reset)`,
3831
- className: "icon-btn drag-handle dt-grip",
3832
- onDoubleClick: floating.resetPosition,
3833
- title: "Drag to move · double-click to reset",
3834
- type: "button",
3835
- ...floating.handleProps,
3836
- children: /* @__PURE__ */ jsx(GripIcon, {})
3837
- }),
3838
- children
3839
- ]
3694
+ function localHeader(record, stamp) {
3695
+ const bytes = new Uint8Array(LOCAL_HEADER_SIZE + record.name.length);
3696
+ const view = new DataView(bytes.buffer);
3697
+ view.setUint32(0, LOCAL_HEADER_SIGNATURE, true);
3698
+ writeCommonFields(view, 4, {
3699
+ record,
3700
+ stamp
3840
3701
  });
3702
+ bytes.set(record.name, LOCAL_HEADER_SIZE);
3703
+ return bytes;
3841
3704
  }
3842
- function SessionsBarButton({ isOpen, onToggle }) {
3843
- return /* @__PURE__ */ jsx(BarButton, {
3844
- label: "Sessions",
3845
- onClick: onToggle,
3846
- pressed: isOpen,
3847
- children: /* @__PURE__ */ jsx(MicIcon, {})
3705
+ function centralHeader(record, stamp) {
3706
+ const bytes = new Uint8Array(CENTRAL_HEADER_SIZE + record.name.length);
3707
+ const view = new DataView(bytes.buffer);
3708
+ view.setUint32(0, CENTRAL_HEADER_SIGNATURE, true);
3709
+ view.setUint16(4, ZIP_VERSION, true);
3710
+ writeCommonFields(view, 6, {
3711
+ record,
3712
+ stamp
3848
3713
  });
3714
+ view.setUint32(42, record.offset, true);
3715
+ bytes.set(record.name, CENTRAL_HEADER_SIZE);
3716
+ return bytes;
3849
3717
  }
3850
- function DevtoolsBarButtons({ noteCount, onPick, onToggleSheet, openTab }) {
3851
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3852
- /* @__PURE__ */ jsx(BarButton, {
3853
- label: PICK_BUTTON_LABEL,
3854
- onClick: onPick,
3855
- pressed: false,
3856
- children: /* @__PURE__ */ jsx(TargetIcon, {})
3857
- }),
3858
- /* @__PURE__ */ jsxs(BarButton, {
3859
- label: "Dev notes",
3860
- onClick: () => onToggleSheet("notes"),
3861
- pressed: openTab === "notes",
3862
- children: [/* @__PURE__ */ jsx(NotesIcon, {}), noteCount > 0 && /* @__PURE__ */ jsx("span", {
3863
- className: "dt-count",
3864
- children: noteCount
3865
- })]
3866
- }),
3867
- /* @__PURE__ */ jsx(SessionsBarButton, {
3868
- isOpen: openTab === "sessions",
3869
- onToggle: () => onToggleSheet("sessions")
3870
- }),
3871
- /* @__PURE__ */ jsx(BarButton, {
3872
- label: "Board feedback",
3873
- onClick: () => onToggleSheet("inbox"),
3874
- pressed: openTab === "inbox",
3875
- children: /* @__PURE__ */ jsx(InboxIcon, {})
3876
- })
3877
- ] });
3718
+ function endRecord(options) {
3719
+ const bytes = new Uint8Array(END_RECORD_SIZE);
3720
+ const view = new DataView(bytes.buffer);
3721
+ view.setUint32(0, END_SIGNATURE, true);
3722
+ view.setUint16(8, options.count, true);
3723
+ view.setUint16(10, options.count, true);
3724
+ view.setUint32(12, options.directorySize, true);
3725
+ view.setUint32(16, options.directoryOffset, true);
3726
+ return bytes;
3878
3727
  }
3879
- //#endregion
3880
- //#region src/devtools/client/use-sheet-size.ts
3881
- const SIZE_KEY = "reflet-devtools-sheet-size";
3882
- const DEFAULT_WIDTH = 440;
3883
- const MIN_WIDTH = 320;
3884
- const MIN_HEIGHT = 240;
3885
- const KEYBOARD_STEP = 24;
3886
- const ARROW_DELTAS = {
3887
- ArrowDown: {
3888
- dx: 0,
3889
- dy: KEYBOARD_STEP
3890
- },
3891
- ArrowLeft: {
3892
- dx: -24,
3893
- dy: 0
3894
- },
3895
- ArrowRight: {
3896
- dx: KEYBOARD_STEP,
3897
- dy: 0
3898
- },
3899
- ArrowUp: {
3900
- dx: 0,
3901
- dy: -24
3902
- }
3903
- };
3904
- function readStoredSize() {
3905
- const fallback = {
3906
- height: null,
3907
- width: DEFAULT_WIDTH
3908
- };
3909
- try {
3910
- const stored = JSON.parse(localStorage.getItem(SIZE_KEY) ?? "null");
3911
- if (typeof stored === "object" && stored !== null && "width" in stored && typeof stored.width === "number" && "height" in stored && (stored.height === null || typeof stored.height === "number")) return {
3912
- height: stored.height,
3913
- width: stored.width
3728
+ async function createZipArchive(entries, modifiedAt = /* @__PURE__ */ new Date()) {
3729
+ const stamp = dosStamp(modifiedAt);
3730
+ const encoder = new TextEncoder();
3731
+ const deflated = await Promise.all(entries.map(async (entry) => ({
3732
+ ...await deflateEntry(entry.data),
3733
+ name: encoder.encode(entry.name),
3734
+ size: entry.data.length
3735
+ })));
3736
+ const records = [];
3737
+ const parts = [];
3738
+ let offset = 0;
3739
+ for (const entry of deflated) {
3740
+ const record = {
3741
+ ...entry,
3742
+ offset
3914
3743
  };
3915
- } catch {
3916
- return fallback;
3744
+ const header = localHeader(record, stamp);
3745
+ parts.push(header, record.compressed);
3746
+ records.push(record);
3747
+ offset += header.length + record.compressed.length;
3917
3748
  }
3918
- return fallback;
3749
+ const directory = records.map((record) => centralHeader(record, stamp));
3750
+ const directorySize = directory.reduce((size, part) => size + part.length, 0);
3751
+ const end = endRecord({
3752
+ count: records.length,
3753
+ directoryOffset: offset,
3754
+ directorySize
3755
+ });
3756
+ return new Blob([
3757
+ ...parts,
3758
+ ...directory,
3759
+ end
3760
+ ], { type: "application/zip" });
3919
3761
  }
3920
- /**
3921
- * The stored size is the preference; what renders is that preference fitted
3922
- * to today's window, so shrinking the window never loses the chosen size.
3923
- */
3924
- function fitted(size, viewport) {
3925
- const maxWidth = viewport.width - 24;
3926
- const maxHeight = viewport.height - 24;
3762
+ //#endregion
3763
+ //#region src/sessions/ui/handoff/brief-export.ts
3764
+ const BRIEF_FILE_NAME = "brief.md";
3765
+ const UNSAFE_FILE_CHARACTERS = /[^a-z0-9]+/g;
3766
+ const EDGE_DASHES = /^-|-$/g;
3767
+ async function downloadFile(file) {
3768
+ const response = await fetch(file.url);
3769
+ if (!response.ok) throw new Error(`${file.name} could not be downloaded (${response.status}).`);
3927
3770
  return {
3928
- height: size.height === null || size.height >= maxHeight ? null : Math.max(Math.min(MIN_HEIGHT, maxHeight), size.height),
3929
- width: Math.min(maxWidth, Math.max(MIN_WIDTH, size.width))
3771
+ data: new Uint8Array(await response.arrayBuffer()),
3772
+ name: file.name
3930
3773
  };
3931
3774
  }
3932
- /**
3933
- * Width grows away from the docked side, height grows downward from the top;
3934
- * a height reaching the bottom of the window snaps back to full height.
3935
- */
3936
- function useSheetSize(side) {
3937
- const [preference, setPreference] = useState(readStoredSize);
3938
- const [viewport, setViewport] = useState(visibleViewport);
3939
- const drag = useRef(null);
3940
- const widthDirection = side === "right" ? -1 : 1;
3941
- const size = fitted(preference, viewport);
3942
- useEffect(() => onViewportChange(() => setViewport(visibleViewport())), []);
3943
- useEffect(() => {
3944
- try {
3945
- localStorage.setItem(SIZE_KEY, JSON.stringify(preference));
3946
- } catch {}
3947
- }, [preference]);
3948
- const resize = (axis, from, dx, dy) => {
3949
- const fullHeight = viewport.height - 24;
3950
- setPreference(fitted({
3951
- height: axis === "width" ? from.height : (from.height ?? fullHeight) + dy,
3952
- width: axis === "height" ? from.width : from.width + dx * widthDirection
3953
- }, viewport));
3775
+ function zipFileName(sessionTitle) {
3776
+ return `reflet-session-${sessionTitle.toLowerCase().replace(UNSAFE_FILE_CHARACTERS, "-").replace(EDGE_DASHES, "") || "brief"}.zip`;
3777
+ }
3778
+ /** File URLs are short-lived, so they are fetched right when the export is asked for. */
3779
+ async function buildBriefZip(options) {
3780
+ const evidence = await Promise.all(options.files.map(downloadFile));
3781
+ return await createZipArchive([{
3782
+ data: new TextEncoder().encode(options.brief),
3783
+ name: BRIEF_FILE_NAME
3784
+ }, ...evidence]);
3785
+ }
3786
+ function saveBlob(options) {
3787
+ const url = URL.createObjectURL(options.blob);
3788
+ const link = document.createElement("a");
3789
+ link.download = options.fileName;
3790
+ link.href = url;
3791
+ link.click();
3792
+ setTimeout(() => URL.revokeObjectURL(url), 0);
3793
+ }
3794
+ //#endregion
3795
+ //#region src/sessions/ui/review/transcript/evidence-thumb.tsx
3796
+ const TRIGGER_LABELS = {
3797
+ hover: "Long hover",
3798
+ manual: "Added by hand",
3799
+ mark: "Marked moment",
3800
+ navigation: "Page change",
3801
+ select: "Pointed element",
3802
+ start: "Session start"
3803
+ };
3804
+ function evidenceCaption(evidence) {
3805
+ const element = evidence.element ? ` · ${evidence.element.label}` : "";
3806
+ return `${TRIGGER_LABELS[evidence.trigger]}${element}`;
3807
+ }
3808
+ const hasViewport = (frame) => frame.viewportWidth > 0 && frame.viewportHeight > 0;
3809
+ const percent = (value, total) => `${value / total * 100}%`;
3810
+ function percentBox(rect, frame) {
3811
+ if (!hasViewport(frame)) return null;
3812
+ return {
3813
+ height: percent(rect.height, frame.viewportHeight),
3814
+ left: percent(rect.x, frame.viewportWidth),
3815
+ top: percent(rect.y, frame.viewportHeight),
3816
+ width: percent(rect.width, frame.viewportWidth)
3954
3817
  };
3955
- const handleProps = (axis) => ({
3956
- onDoubleClick: () => setPreference((current) => ({
3957
- height: axis === "width" ? current.height : null,
3958
- width: axis === "height" ? current.width : DEFAULT_WIDTH
3959
- })),
3960
- onKeyDown: (event) => {
3961
- const delta = ARROW_DELTAS[event.key];
3962
- if (!delta) return;
3963
- event.preventDefault();
3964
- const sheetHeight = event.currentTarget.parentElement?.getBoundingClientRect().height ?? null;
3965
- resize(axis, {
3966
- height: size.height ?? sheetHeight,
3967
- width: size.width
3968
- }, delta.dx, delta.dy);
3969
- },
3970
- onLostPointerCapture: () => {
3971
- drag.current = null;
3972
- },
3973
- onPointerDown: (event) => {
3974
- if (event.button !== 0) return;
3975
- const sheet = event.currentTarget.parentElement?.getBoundingClientRect();
3976
- event.currentTarget.setPointerCapture(event.pointerId);
3977
- drag.current = {
3978
- axis,
3979
- size: {
3980
- height: sheet?.height ?? size.height,
3981
- width: size.width
3982
- },
3983
- x: event.clientX,
3984
- y: event.clientY
3985
- };
3986
- },
3987
- onPointerMove: (event) => {
3988
- const start = drag.current;
3989
- if (start && event.currentTarget.hasPointerCapture(event.pointerId)) resize(start.axis, start.size, event.clientX - start.x, event.clientY - start.y);
3990
- }
3991
- });
3818
+ }
3819
+ function percentPoint(point, frame) {
3820
+ if (!hasViewport(frame)) return null;
3992
3821
  return {
3993
- handleProps,
3994
- size
3822
+ left: percent(point.x, frame.viewportWidth),
3823
+ top: percent(point.y, frame.viewportHeight)
3995
3824
  };
3996
3825
  }
3826
+ function EvidenceThumb({ evidence, isLarge = false }) {
3827
+ const { element, frame, image } = evidence;
3828
+ if (!image?.url) return null;
3829
+ const highlight = element && frame ? percentBox(element.rect, frame) : null;
3830
+ return /* @__PURE__ */ jsxs("span", {
3831
+ className: "dt-ses-shot",
3832
+ "data-large": isLarge,
3833
+ children: [/* @__PURE__ */ jsx("img", {
3834
+ alt: evidenceCaption(evidence),
3835
+ height: image.height,
3836
+ src: image.url,
3837
+ width: image.width
3838
+ }), highlight && /* @__PURE__ */ jsx("span", {
3839
+ "aria-hidden": "true",
3840
+ className: "dt-ses-shot-mark",
3841
+ style: highlight
3842
+ })]
3843
+ });
3844
+ }
3997
3845
  //#endregion
3998
- //#region src/devtools/client/devtools-sheet.tsx
3999
- const FULL_HEIGHT = "calc(100dvh - 24px)";
4000
- const TAB_LABELS = {
4001
- code: "Code",
4002
- inbox: "Board",
4003
- notes: "Notes",
4004
- sessions: "Sessions"
4005
- };
4006
- const SHEET_POSITION_KEY = "reflet-devtools-sheet-position";
4007
- function useSheetFocus(rootRef) {
4008
- useLayoutEffect(() => {
4009
- const root = rootRef.current;
4010
- const scope = root?.getRootNode();
4011
- const opener = scope instanceof ShadowRoot || scope instanceof Document ? scope.activeElement : null;
4012
- if (opener instanceof HTMLElement && typeof opener.checkVisibility === "function" && !opener.checkVisibility({ visibilityProperty: true })) root?.querySelector("[role=\"tab\"][aria-selected=\"true\"]")?.focus({ preventScroll: true });
4013
- return () => {
4014
- if (opener instanceof HTMLElement && opener.isConnected) opener.focus({ preventScroll: true });
4015
- };
4016
- }, [rootRef]);
3846
+ //#region src/sessions/ui/review/continuation/intention-history.tsx
3847
+ const FIELD_LABELS = {
3848
+ need: "Need",
3849
+ observed: "Observed",
3850
+ response: "Considered response",
3851
+ setAside: "Set aside",
3852
+ unknowns: "To check"
3853
+ };
3854
+ function fieldValueText(value) {
3855
+ return (Array.isArray(value) ? value.join("; ") : value) || "—";
4017
3856
  }
4018
- function DevtoolsSheet({ children, label = "Reflet devtools", onClose, onPick, onSelectTab, side, tab, tabs }) {
4019
- const { handleProps, size } = useSheetSize(side);
4020
- const floating = useFloatingPosition(`top-${side}`, false, {
4021
- anchorSelector: ".dt-sheet",
4022
- persistKey: SHEET_POSITION_KEY
3857
+ function AppliedChanges({ applied }) {
3858
+ if (applied.length === 0) return null;
3859
+ return /* @__PURE__ */ jsx("ul", {
3860
+ className: "dt-ses-rows",
3861
+ children: applied.map((change) => /* @__PURE__ */ jsxs("li", {
3862
+ className: "dt-ses-row",
3863
+ children: [/* @__PURE__ */ jsxs("p", {
3864
+ className: "dt-meta",
3865
+ children: [
3866
+ "Accepted into revision ",
3867
+ change.revision,
3868
+ " by",
3869
+ " ",
3870
+ change.byName ?? "a teammate",
3871
+ " · ",
3872
+ formatDate(change.at)
3873
+ ]
3874
+ }), /* @__PURE__ */ jsx("ul", {
3875
+ className: "dt-ses-list",
3876
+ children: change.fields.map((field) => /* @__PURE__ */ jsxs("li", { children: [
3877
+ FIELD_LABELS[field.field],
3878
+ ": ",
3879
+ fieldValueText(field.before),
3880
+ " →",
3881
+ " ",
3882
+ fieldValueText(field.after)
3883
+ ] }, field.field))
3884
+ })]
3885
+ }, change.revision))
4023
3886
  });
4024
- const isMoved = floating.position !== null;
4025
- const { rootRef } = floating;
4026
- useSheetFocus(rootRef);
4027
- useEffect(() => listenToKeydown(rootRef.current, (event) => {
4028
- if (event.key === "Escape" && !event.isComposing) onClose();
4029
- }), [onClose, rootRef]);
4030
- return /* @__PURE__ */ jsxs("aside", {
4031
- "aria-label": label,
4032
- className: "dt-sheet glass",
4033
- "data-moved": isMoved,
4034
- "data-side": side,
4035
- ref: rootRef,
4036
- style: {
4037
- height: size.height ?? (isMoved ? FULL_HEIGHT : void 0),
4038
- width: size.width
4039
- },
3887
+ }
3888
+ function HistoryExtracts({ evidence, quotes }) {
3889
+ return /* @__PURE__ */ jsxs(Fragment, { children: [quotes.map((quote) => /* @__PURE__ */ jsxs("blockquote", {
3890
+ className: "dt-ses-moment-quote",
4040
3891
  children: [
4041
- /* @__PURE__ */ jsxs("style", { children: [floating.styles, isMoved ? "" : `.dt-bar[data-moved="false"][data-side="${side}"] { ${side}: ${size.width + 24}px; }`] }),
4042
- /* @__PURE__ */ jsx("button", {
4043
- "aria-label": "Resize panel width (double-click to reset)",
4044
- className: "dt-resize",
4045
- "data-axis": "width",
4046
- type: "button",
4047
- ...handleProps("width")
4048
- }),
4049
- /* @__PURE__ */ jsx("button", {
4050
- "aria-label": "Resize panel height (double-click for full height)",
4051
- className: "dt-resize",
4052
- "data-axis": "height",
4053
- type: "button",
4054
- ...handleProps("height")
3892
+ /* @__PURE__ */ jsx("span", {
3893
+ className: "dt-ses-time",
3894
+ children: formatClock(quote.startMs)
4055
3895
  }),
4056
- /* @__PURE__ */ jsx("button", {
4057
- "aria-label": "Resize panel (double-click to reset)",
4058
- className: "dt-resize",
4059
- "data-axis": "both",
4060
- type: "button",
4061
- ...handleProps("both")
3896
+ ` “${quote.text}”`,
3897
+ quote.context.map((line) => /* @__PURE__ */ jsx("span", {
3898
+ className: "dt-meta dt-ses-context-line",
3899
+ children: line
3900
+ }, line))
3901
+ ]
3902
+ }, quote.segmentId)), evidence.length > 0 && /* @__PURE__ */ jsx("div", {
3903
+ className: "dt-ses-thumbs",
3904
+ children: evidence.map((item) => /* @__PURE__ */ jsx(EvidenceThumb, { evidence: item }, item.id))
3905
+ })] });
3906
+ }
3907
+ function entryTitle(entry) {
3908
+ const { session } = entry;
3909
+ return entry.origin === "continuation" ? `Continued in “${session.title}” · ${formatDate(session.startedAt)}` : `Added from “${session.title}” · ${formatDate(session.startedAt)}`;
3910
+ }
3911
+ function HistoryEntry({ context, entry }) {
3912
+ const evidenceIds = new Set(entry.evidenceIds);
3913
+ const evidence = context.detail.contributedEvidence.filter((item) => evidenceIds.has(item.id));
3914
+ return /* @__PURE__ */ jsxs("li", {
3915
+ className: "dt-ses-history",
3916
+ "data-removed": entry.detachedAt !== null,
3917
+ children: [
3918
+ /* @__PURE__ */ jsx("p", {
3919
+ className: "dt-ses-subhead",
3920
+ children: entryTitle(entry)
4062
3921
  }),
4063
- /* @__PURE__ */ jsxs("div", {
4064
- className: "dt-sheet-head",
3922
+ entry.detachedAt !== null && /* @__PURE__ */ jsxs("p", {
3923
+ className: "dt-meta",
4065
3924
  children: [
4066
- /* @__PURE__ */ jsx("button", {
4067
- "aria-label": "Move panel (double-click to dock it back)",
4068
- className: "dt-sheet-drag",
4069
- onDoubleClick: floating.resetPosition,
4070
- title: "Drag to move · double-click to dock",
4071
- type: "button",
4072
- ...floating.handleProps
4073
- }),
4074
- onPick && /* @__PURE__ */ jsx("button", {
4075
- "aria-label": "Pick an element: note it, copy it, or Shift+click to open its code",
4076
- className: "icon-btn",
4077
- onClick: onPick,
4078
- title: "Pick an element",
4079
- type: "button",
4080
- children: /* @__PURE__ */ jsx(TargetIcon, {})
4081
- }),
4082
- /* @__PURE__ */ jsx("div", {
4083
- className: "dt-tabs",
4084
- role: "tablist",
4085
- children: tabs.map((option) => /* @__PURE__ */ jsx("button", {
4086
- "aria-selected": option === tab,
4087
- className: "dt-tab",
4088
- onClick: () => onSelectTab(option),
4089
- role: "tab",
4090
- type: "button",
4091
- children: TAB_LABELS[option]
4092
- }, option))
4093
- }),
4094
- /* @__PURE__ */ jsx("button", {
4095
- "aria-label": `Close ${label} panel`,
4096
- className: "icon-btn",
4097
- onClick: onClose,
4098
- title: "Close",
4099
- type: "button",
4100
- children: /* @__PURE__ */ jsx(CloseIcon, {})
4101
- })
3925
+ "Removed on ",
3926
+ formatDate(entry.detachedAt),
3927
+ ": what it added was taken back."
4102
3928
  ]
4103
3929
  }),
4104
- /* @__PURE__ */ jsx("div", {
4105
- className: "dt-sheet-body",
4106
- role: "tabpanel",
4107
- children
4108
- }, tab)
3930
+ /* @__PURE__ */ jsx(AppliedChanges, { applied: entry.applied }),
3931
+ entry.extractsHidden ? /* @__PURE__ */ jsx("p", {
3932
+ className: "dt-meta",
3933
+ children: "Extracts are private to the author"
3934
+ }) : /* @__PURE__ */ jsx(HistoryExtracts, {
3935
+ evidence,
3936
+ quotes: entry.quotes
3937
+ }),
3938
+ entry.session.canOpen && /* @__PURE__ */ jsx("button", {
3939
+ className: "dt-ses-link",
3940
+ onClick: () => context.navigation.onOpenSession(entry.session.id),
3941
+ type: "button",
3942
+ children: "Open session"
3943
+ })
4109
3944
  ]
4110
3945
  });
4111
3946
  }
4112
- //#endregion
4113
- //#region src/devtools/client/devtools-styles.ts
4114
- const MONO_FONT = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
4115
- const DEVTOOLS_STYLES = `
4116
- .dt { font: 13px/1.45 var(--rf-font, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif); color: var(--rf-text); }
4117
- .dt[data-recessed="true"] .dt-bar, .dt[data-recessed="true"] .dt-sheet { visibility: hidden; pointer-events: none; }
4118
- @media (max-width: 600px) { .dt[data-sheet-open="true"] .dt-bar { visibility: hidden; pointer-events: none; } }
4119
- .dt-pin {
4120
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 0; left: 0;
4121
- display: flex; align-items: center; justify-content: center;
4122
- min-width: 24px; height: 24px; padding: 0 7px;
4123
- border-radius: 12px 12px 12px 3px; background: var(--rf-accent); color: var(--rf-accent-text);
4124
- font-size: 11.5px; font-weight: 650; box-shadow: 0 0 0 2px var(--rf-bg), var(--rf-shadow-low);
4125
- animation: dt-pin-in 260ms var(--rf-ease);
4126
- transition: scale 140ms var(--rf-ease), opacity 160ms var(--rf-ease);
4127
- }
4128
- .dt-pin:hover, .dt-pin[aria-expanded="true"] { scale: 1.12; }
4129
- .dt-pin[data-sent="true"] { background: var(--rf-text-muted); }
4130
- .dt-pin-badge { display: inline-flex; flex: none; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px 10px 10px 3px; background: var(--rf-accent); color: var(--rf-accent-text); font-size: 10.5px; font-weight: 650; }
4131
- .dt-card[data-sent="true"] .dt-pin-badge { background: var(--rf-text-muted); }
4132
- .dt-pin[data-locating="true"] { animation: dt-pin-in 260ms var(--rf-ease), dt-pin-pulse 1.2s ease-in-out infinite; }
4133
- @keyframes dt-pin-in { from { scale: .4; opacity: 0; } }
4134
- @keyframes dt-pin-pulse { 50% { box-shadow: 0 0 0 2px var(--rf-bg), 0 0 0 6px color-mix(in srgb, var(--rf-accent) 30%, transparent); } }
4135
- .dt-popover {
4136
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; width: 320px; max-height: calc(100dvh - 24px); overflow: auto;
4137
- inset-inline-end: auto; margin: 0; padding: 6px; border-radius: 14px; background: transparent; color: inherit;
4138
- animation: dt-popover-in 200ms var(--rf-ease);
3947
+ function IntentionHistory({ context, intentionId }) {
3948
+ const entries = context.detail.history.filter((entry) => entry.intentionId === intentionId);
3949
+ if (entries.length === 0) return null;
3950
+ return /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h4", {
3951
+ className: "dt-ses-label",
3952
+ children: "History"
3953
+ }), /* @__PURE__ */ jsx("ul", {
3954
+ className: "dt-ses-rows",
3955
+ children: entries.map((entry) => /* @__PURE__ */ jsx(HistoryEntry, {
3956
+ context,
3957
+ entry
3958
+ }, entry.id))
3959
+ })] });
4139
3960
  }
4140
- .dt-popover.glass::before { animation: rf-fade 180ms var(--rf-ease); }
4141
- .dt-popover .dt-card { background: transparent; }
4142
- @keyframes dt-popover-in { from { transform: translateY(4px); } }
4143
- @keyframes dt-notice-in { from { transform: translateY(-6px); } }
4144
- .dt-notices {
4145
- position: fixed; z-index: ${REFLET_Z_INDEX.annotatorOverlay}; left: 50%; top: 16px; translate: -50% 0;
4146
- display: flex; flex-direction: column; gap: 6px; width: min(460px, calc(100vw - 24px));
3961
+ //#endregion
3962
+ //#region src/sessions/ui/handoff/handoff-changes.tsx
3963
+ function differences(proposal, last) {
3964
+ return [
3965
+ {
3966
+ after: proposal.need,
3967
+ before: last.need,
3968
+ label: "Need"
3969
+ },
3970
+ {
3971
+ after: fieldValueText(proposal.response),
3972
+ before: fieldValueText(last.response),
3973
+ label: "Considered response"
3974
+ },
3975
+ {
3976
+ after: fieldValueText(proposal.unknowns),
3977
+ before: last.unknowns === null ? null : fieldValueText(last.unknowns),
3978
+ label: "To check"
3979
+ }
3980
+ ].flatMap(({ after, before, label }) => before === null || before === after ? [] : [{
3981
+ after,
3982
+ before,
3983
+ label
3984
+ }]);
4147
3985
  }
4148
- .dt-notice {
4149
- display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 14px; border-radius: 12px;
4150
- color: var(--rf-danger); font-size: 12.5px; animation: dt-notice-in 220ms var(--rf-ease);
3986
+ function ChangeSummary({ last, proposal }) {
3987
+ return /* @__PURE__ */ jsxs("li", {
3988
+ className: "dt-ses-row",
3989
+ children: [
3990
+ /* @__PURE__ */ jsx("p", {
3991
+ className: "dt-ses-subhead",
3992
+ children: proposal.need
3993
+ }),
3994
+ /* @__PURE__ */ jsxs("p", {
3995
+ className: "dt-meta",
3996
+ children: ["Changed since your brief of ", formatDate(last.at)]
3997
+ }),
3998
+ /* @__PURE__ */ jsx("ul", {
3999
+ className: "dt-ses-list",
4000
+ children: differences(proposal, last).map((difference) => /* @__PURE__ */ jsxs("li", { children: [
4001
+ difference.label,
4002
+ ": ",
4003
+ difference.before,
4004
+ " → ",
4005
+ difference.after
4006
+ ] }, difference.label))
4007
+ })
4008
+ ]
4009
+ });
4151
4010
  }
4152
- .dt-notice[data-kind="status"] { color: var(--rf-text); }
4153
- .dt-notice p { flex: 1; margin: 0; overflow-wrap: anywhere; }
4154
- .dt-notice .icon-btn { width: 30px; height: 30px; }
4155
- .dt-bar {
4156
- --dt-edge-gap: calc(max(var(--rf-offset), 12px) + 48px);
4157
- position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar};
4158
- display: flex; align-items: center; gap: 2px; padding: 3px;
4159
- border-radius: var(--rf-pill);
4160
- }
4161
- .dt-bar[data-moved="false"] { transition: left 260ms var(--rf-ease), right 260ms var(--rf-ease); }
4162
- .dt-bar[data-side="left"] { left: calc(var(--rf-viewport-left, 0px) + max(12px, var(--rf-offset))); }
4163
- .dt-bar[data-side="right"] { right: calc(var(--rf-viewport-right, 0px) + max(12px, var(--rf-offset))); }
4164
- .dt-bar[data-edge="bottom"] { bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + var(--dt-edge-gap)); }
4165
- .dt-bar[data-edge="top"] { top: calc(var(--rf-viewport-top, 0px) + env(safe-area-inset-top) + var(--dt-edge-gap)); }
4166
- .dt-bar .icon-btn { position: relative; width: 34px; height: 34px; }
4167
- .dt-bar .icon-btn.dt-grip { width: 22px; color: var(--rf-text-muted); opacity: .6; }
4168
- .dt-bar .icon-btn.dt-grip:hover { opacity: 1; }
4169
- .dt-bar .icon-btn[aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
4170
- .dt-count {
4171
- position: absolute; top: 1px; right: 1px; display: flex; align-items: center; justify-content: center;
4172
- min-width: 15px; height: 15px; padding: 0 4px; border-radius: var(--rf-pill);
4173
- background: var(--rf-accent); color: var(--rf-accent-text); font-size: 9.5px; font-weight: 600;
4011
+ function ChangedSinceBriefs({ detail, proposalIds }) {
4012
+ const selected = new Set(proposalIds);
4013
+ const changed = detail.proposals.flatMap((proposal) => {
4014
+ const last = proposal.lastHandoff;
4015
+ return selected.has(proposal.id) && last && last.revision < proposal.revision ? [{
4016
+ last,
4017
+ proposal
4018
+ }] : [];
4019
+ });
4020
+ if (changed.length === 0) return null;
4021
+ return /* @__PURE__ */ jsx("ul", {
4022
+ className: "dt-ses-rows",
4023
+ children: changed.map(({ last, proposal }) => /* @__PURE__ */ jsx(ChangeSummary, {
4024
+ last,
4025
+ proposal
4026
+ }, proposal.id))
4027
+ });
4174
4028
  }
4175
- .dt-sheet {
4176
- --dt-enter-x: 20px;
4177
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 12px; bottom: 12px;
4178
- display: flex; flex-direction: column; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px);
4179
- border-radius: 16px; overflow: hidden;
4180
- animation: dt-sheet-slide 320ms var(--rf-ease);
4029
+ const DELIVERY_LABELS = {
4030
+ copied: "Copied",
4031
+ exported: "Downloaded"
4032
+ };
4033
+ function deliveryLine(handoff) {
4034
+ const kinds = new Set(handoff.deliveries.map((delivery) => delivery.kind));
4035
+ if (kinds.size === 0) return "Not copied yet";
4036
+ return [...kinds].map((kind) => DELIVERY_LABELS[kind]).join(", ");
4181
4037
  }
4182
- .dt-sheet[data-side="left"] { --dt-enter-x: -20px; left: 12px; }
4183
- .dt-sheet[data-side="right"] { right: 12px; }
4184
- .dt-sheet.glass::before { animation: rf-fade 220ms var(--rf-ease); }
4185
- .dt-sheet-head, .dt-sheet-body { animation: rf-fade 260ms 60ms var(--rf-ease) both; }
4186
- @keyframes dt-sheet-slide { from { transform: translateX(var(--dt-enter-x)); box-shadow: none; } }
4187
- .dt-resize { position: absolute; z-index: 2; padding: 0; touch-action: none; }
4188
- .dt-resize:active:not(:disabled) { scale: 1; }
4189
- .dt-resize:focus-visible { outline: none; }
4190
- .dt-resize[data-axis="width"] { top: 16px; bottom: 16px; width: 10px; cursor: ew-resize; }
4191
- .dt-sheet[data-side="right"] .dt-resize[data-axis="width"] { left: 0; }
4192
- .dt-sheet[data-side="left"] .dt-resize[data-axis="width"] { right: 0; }
4193
- .dt-resize[data-axis="height"] { left: 16px; right: 16px; bottom: 0; height: 10px; cursor: ns-resize; }
4194
- .dt-resize[data-axis="both"] { bottom: 0; width: 16px; height: 16px; }
4195
- .dt-sheet[data-side="right"] .dt-resize[data-axis="both"] { left: 0; cursor: nesw-resize; }
4196
- .dt-sheet[data-side="left"] .dt-resize[data-axis="both"] { right: 0; cursor: nwse-resize; }
4197
- .dt-resize[data-axis="width"]::after, .dt-resize[data-axis="height"]::after {
4198
- content: ""; position: absolute; border-radius: var(--rf-pill); background: var(--rf-text-muted);
4199
- opacity: 0; transition: opacity 160ms var(--rf-ease);
4038
+ function briefTitle(handoff) {
4039
+ const [only, ...others] = handoff.proposals;
4040
+ if (only && others.length === 0) return only.need;
4041
+ return countLabel(handoff.proposals.length, "intention");
4200
4042
  }
4201
- .dt-resize[data-axis="width"]::after { top: 50%; left: 3px; width: 4px; height: 36px; translate: 0 -50%; }
4202
- .dt-resize[data-axis="height"]::after { left: 50%; top: 3px; width: 36px; height: 4px; translate: -50% 0; }
4203
- .dt-resize:hover::after, .dt-resize:focus-visible::after, .dt-resize:active::after { opacity: .5; }
4204
- .dt-sheet-head { position: relative; display: flex; align-items: center; gap: 4px; padding: 8px 8px 8px 6px; border-bottom: 1px solid var(--rf-border); }
4205
- .dt-sheet-head > :not(.dt-sheet-drag) { position: relative; z-index: 1; }
4206
- .dt-sheet-head .icon-btn { width: 32px; height: 32px; }
4207
- .dt-sheet .dt-sheet-drag { position: absolute; inset: 0; z-index: 0; padding: 0; border-radius: 0; cursor: grab; touch-action: none; }
4208
- .dt-sheet .dt-sheet-drag::before { content: ""; position: absolute; top: 4px; left: 50%; width: 36px; height: 4px; translate: -50% 0; border-radius: var(--rf-pill); background: var(--rf-text-muted); opacity: .3; transition: opacity 160ms var(--rf-ease); }
4209
- .dt-sheet .dt-sheet-drag:hover::before, .dt-sheet .dt-sheet-drag:active::before { opacity: .7; }
4210
- .dt-sheet .dt-sheet-drag:active:not(:disabled) { cursor: grabbing; scale: 1; }
4211
- .dt-sheet .dt-sheet-drag:focus-visible { outline-offset: -2px; }
4212
- .dt-tabs { display: flex; gap: 2px; margin-right: auto; }
4213
- .dt-tab { height: 30px; padding: 0 11px; border-radius: var(--rf-pill); color: var(--rf-text-muted); font-weight: 500; }
4214
- .dt-tab:hover { color: var(--rf-text); }
4215
- .dt-tab[aria-selected="true"], .dt-tab[aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
4216
- .dt-sheet-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 8px; padding: 10px; }
4217
- .dt-sheet-foot {
4218
- position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
4219
- margin: auto -10px -10px; padding: 10px; border-top: 1px solid var(--rf-border); background: var(--rf-bg);
4043
+ function EarlierBriefs({ handoffs }) {
4044
+ if (handoffs.length === 0) return null;
4045
+ return /* @__PURE__ */ jsxs("section", {
4046
+ className: "dt-ses-form",
4047
+ children: [/* @__PURE__ */ jsx("h3", {
4048
+ className: "dt-group",
4049
+ children: "Earlier briefs"
4050
+ }), /* @__PURE__ */ jsx("ul", {
4051
+ className: "dt-ses-rows",
4052
+ children: handoffs.map((handoff) => {
4053
+ const date = formatDate(handoff.createdAt);
4054
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("details", {
4055
+ className: "dt-ses-brief-entry",
4056
+ children: [/* @__PURE__ */ jsxs("summary", { children: [/* @__PURE__ */ jsx("span", {
4057
+ className: "dt-card-title",
4058
+ children: briefTitle(handoff)
4059
+ }), /* @__PURE__ */ jsxs("span", {
4060
+ className: "dt-meta",
4061
+ children: [
4062
+ date,
4063
+ " · ",
4064
+ deliveryLine(handoff)
4065
+ ]
4066
+ })] }), /* @__PURE__ */ jsx("textarea", {
4067
+ "aria-label": `Brief of ${date}`,
4068
+ className: "dt-field dt-ses-brief",
4069
+ readOnly: true,
4070
+ rows: 8,
4071
+ value: handoff.brief
4072
+ })]
4073
+ }) }, handoff.id);
4074
+ })
4075
+ })]
4076
+ });
4220
4077
  }
4221
- .dt-archived { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 10px; border: 1px dashed var(--rf-border); border-radius: 12px; }
4222
- .dt-archived .dt-meta { flex: 1 1 100%; }
4223
- .dt-group { margin: 6px 2px 0; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--rf-text-muted); }
4224
- .dt-card { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: 12px; background: var(--rf-bg-subtle); }
4225
- .dt-card[data-sent="true"] { opacity: .7; }
4226
- .dt-card-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
4227
- .dt-card-title { min-width: 0; margin: 0; overflow: hidden; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
4228
- .dt-meta { margin: 0; font-size: 11.5px; color: var(--rf-text-muted); overflow-wrap: anywhere; }
4229
- .dt-note-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
4230
- .dt-chip { flex: none; padding: 1px 7px; border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text-muted); font-size: 10.5px; font-weight: 600; }
4231
- .dt-chip[data-tone="accent"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); color: var(--rf-accent); }
4232
- .dt-source { padding: 0; font: 11.5px/1.4 ${MONO_FONT}; color: var(--rf-accent); text-align: left; overflow-wrap: anywhere; }
4233
- .dt-source:hover { text-decoration: underline; }
4234
- .dt-thumb { display: block; align-self: flex-start; max-width: 100%; height: auto; max-height: 132px; object-fit: contain; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
4235
- .dt-actions { display: flex; flex-wrap: wrap; gap: 4px; }
4236
- .dt-btn {
4237
- display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
4238
- border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text);
4239
- font-size: 12px; font-weight: 500; text-decoration: none;
4078
+ //#endregion
4079
+ //#region src/sessions/ui/handoff/handoff-selection.tsx
4080
+ function keptProposals(detail) {
4081
+ return detail.proposals.filter((proposal) => proposal.decision === "kept" && proposal.mergedIntoId === null);
4240
4082
  }
4241
- .dt-btn:hover { background: color-mix(in srgb, var(--rf-text) 12%, transparent); }
4242
- .dt-btn[data-variant="primary"] { background: var(--rf-primary); color: var(--rf-primary-text); }
4243
- .dt-btn[data-variant="danger"] { color: var(--rf-danger); }
4244
- .dt-empty { margin: auto; padding: 24px 16px; color: var(--rf-text-muted); font-size: 12.5px; text-align: center; }
4245
- .dt-empty p { margin: 0 0 8px; }
4246
- .dt-empty code, .dt-hint code { padding: 1px 4px; border-radius: 4px; background: var(--rf-bg-hover); font: 11.5px ${MONO_FONT}; overflow-wrap: anywhere; }
4247
- .dt-hint { margin: 0; padding: 8px 10px; border-radius: 10px; background: var(--rf-bg-subtle); color: var(--rf-text-muted); font-size: 12px; }
4248
- .dt-error { margin: 0; color: var(--rf-danger); font-size: 12px; }
4249
- .dt-status { margin: 0 auto 0 0; color: var(--rf-text-muted); font-size: 12px; }
4250
- .dt-field {
4251
- width: 100%; padding: 7px 9px; border: 1px solid var(--rf-border); border-radius: 8px;
4252
- background: var(--rf-bg); color: var(--rf-text); font: inherit; resize: vertical;
4083
+ function evidenceOf(detail, proposals) {
4084
+ const intentionIds = new Set(proposals.map((proposal) => proposal.id));
4085
+ const contributed = detail.history.filter((entry) => intentionIds.has(entry.intentionId) && entry.detachedAt === null);
4086
+ const cited = /* @__PURE__ */ new Set([...proposals.flatMap((proposal) => proposal.evidenceIds), ...contributed.flatMap((entry) => entry.evidenceIds)]);
4087
+ return [...detail.evidence, ...detail.contributedEvidence].filter((item) => cited.has(item.id) && item.status === "available");
4253
4088
  }
4254
- .dt-field:focus { outline: 2px solid var(--rf-accent); outline-offset: 0; }
4255
- .dt-search { display: flex; gap: 6px; }
4256
- .dt-matches { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
4257
- .dt-match { display: flex; flex-direction: column; gap: 1px; width: 100%; padding: 6px 8px; border-radius: 8px; text-align: left; }
4258
- .dt-match:hover { background: var(--rf-bg-hover); }
4259
- .dt-match code { font: 11.5px ${MONO_FONT}; color: var(--rf-accent); }
4260
- .dt-match span { font: 11.5px ${MONO_FONT}; color: var(--rf-text-muted); white-space: pre; overflow: hidden; text-overflow: ellipsis; }
4261
- .dt-code-head { display: flex; flex-direction: column; gap: 6px; }
4262
- .dt-code-path { margin: 0; font: 12px/1.4 ${MONO_FONT}; overflow-wrap: anywhere; }
4263
- .dt-code { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: 8px 0; border-radius: 10px; background: var(--rf-bg-subtle); font: 12px/1.6 ${MONO_FONT}; }
4264
- .dt-code-lines { display: table; min-width: 100%; }
4265
- .dt-line { display: table-row; white-space: pre; }
4266
- .dt-line > span { display: table-cell; padding-right: 14px; }
4267
- .dt-line > .dt-line-number { width: 1%; padding: 0 12px 0 10px; color: var(--rf-text-muted); text-align: right; user-select: none; }
4268
- .dt-line[data-marked="true"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); }
4269
- .dt-line[data-marked="true"] > .dt-line-number { color: var(--rf-accent); }
4270
- `;
4271
- //#endregion
4272
- //#region src/sessions/ui/handoff/zip-archive.ts
4273
- const LOCAL_HEADER_SIGNATURE = 67324752;
4274
- const CENTRAL_HEADER_SIGNATURE = 33639248;
4275
- const END_SIGNATURE = 101010256;
4276
- const LOCAL_HEADER_SIZE = 30;
4277
- const CENTRAL_HEADER_SIZE = 46;
4278
- const END_RECORD_SIZE = 22;
4279
- const ZIP_VERSION = 20;
4280
- const UTF8_NAMES_FLAG = 2048;
4281
- const DEFLATE_METHOD = 8;
4282
- const GZIP_HEADER_SIZE = 10;
4283
- const GZIP_FLAGS_OFFSET = 3;
4284
- const GZIP_TRAILER_SIZE = 8;
4285
- const DOS_EPOCH_YEAR = 1980;
4286
- function dosStamp(date) {
4089
+ function initialSelection(detail) {
4090
+ const proposals = keptProposals(detail);
4287
4091
  return {
4288
- date: (date.getFullYear() - DOS_EPOCH_YEAR) * 512 + (date.getMonth() + 1) * 32 + date.getDate(),
4289
- time: date.getHours() * 2048 + date.getMinutes() * 32 + Math.floor(date.getSeconds() / 2)
4092
+ evidenceIds: evidenceOf(detail, proposals).map((item) => item.id),
4093
+ proposalIds: proposals.map((proposal) => proposal.id)
4290
4094
  };
4291
4095
  }
4292
- /** gzip wraps raw deflate data and ends with the CRC-32 zip needs, so the browser computes both natively. */
4293
- async function deflateEntry(data) {
4294
- const stream = new Response(data).body?.pipeThrough(new CompressionStream("gzip"));
4295
- const gzip = new Uint8Array(await new Response(stream).arrayBuffer());
4296
- if (gzip[GZIP_FLAGS_OFFSET] !== 0) throw new Error("The browser wrote an unexpected gzip header.");
4297
- return {
4298
- checksum: new DataView(gzip.buffer, gzip.length - GZIP_TRAILER_SIZE).getUint32(0, true),
4299
- compressed: gzip.slice(GZIP_HEADER_SIZE, gzip.length - GZIP_TRAILER_SIZE)
4300
- };
4096
+ function toggled(ids, id) {
4097
+ return ids.includes(id) ? ids.filter((other) => other !== id) : [...ids, id];
4301
4098
  }
4302
- function writeCommonFields(view, start, options) {
4303
- const { record, stamp } = options;
4304
- view.setUint16(start, ZIP_VERSION, true);
4305
- view.setUint16(start + 2, UTF8_NAMES_FLAG, true);
4306
- view.setUint16(start + 4, DEFLATE_METHOD, true);
4307
- view.setUint16(start + 6, stamp.time, true);
4308
- view.setUint16(start + 8, stamp.date, true);
4309
- view.setUint32(start + 10, record.checksum, true);
4310
- view.setUint32(start + 14, record.compressed.length, true);
4311
- view.setUint32(start + 18, record.size, true);
4312
- view.setUint16(start + 22, record.name.length, true);
4313
- view.setUint16(start + 24, 0, true);
4099
+ function CheckRow({ isChecked, label, onToggle }) {
4100
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("label", {
4101
+ className: "dt-ses-check",
4102
+ children: [/* @__PURE__ */ jsx("input", {
4103
+ checked: isChecked,
4104
+ onChange: onToggle,
4105
+ type: "checkbox"
4106
+ }), /* @__PURE__ */ jsx("span", { children: label })]
4107
+ }) });
4314
4108
  }
4315
- function localHeader(record, stamp) {
4316
- const bytes = new Uint8Array(LOCAL_HEADER_SIZE + record.name.length);
4317
- const view = new DataView(bytes.buffer);
4318
- view.setUint32(0, LOCAL_HEADER_SIGNATURE, true);
4319
- writeCommonFields(view, 4, {
4320
- record,
4321
- stamp
4109
+ function ChoiceGroup({ children, legend }) {
4110
+ return /* @__PURE__ */ jsxs("fieldset", {
4111
+ className: "dt-ses-choices",
4112
+ children: [/* @__PURE__ */ jsx("legend", {
4113
+ className: "dt-ses-label",
4114
+ children: legend
4115
+ }), /* @__PURE__ */ jsx("ul", {
4116
+ className: "dt-ses-list",
4117
+ children
4118
+ })]
4322
4119
  });
4323
- bytes.set(record.name, LOCAL_HEADER_SIZE);
4324
- return bytes;
4325
4120
  }
4326
- function centralHeader(record, stamp) {
4327
- const bytes = new Uint8Array(CENTRAL_HEADER_SIZE + record.name.length);
4328
- const view = new DataView(bytes.buffer);
4329
- view.setUint32(0, CENTRAL_HEADER_SIGNATURE, true);
4330
- view.setUint16(4, ZIP_VERSION, true);
4331
- writeCommonFields(view, 6, {
4332
- record,
4333
- stamp
4121
+ function HandoffChoices({ detail, onChange, selection }) {
4122
+ const proposals = keptProposals(detail);
4123
+ const evidence = evidenceOf(detail, proposals);
4124
+ const toggleProposal = (id) => onChange({
4125
+ ...selection,
4126
+ proposalIds: toggled(selection.proposalIds, id)
4334
4127
  });
4335
- view.setUint32(42, record.offset, true);
4336
- bytes.set(record.name, CENTRAL_HEADER_SIZE);
4337
- return bytes;
4338
- }
4339
- function endRecord(options) {
4340
- const bytes = new Uint8Array(END_RECORD_SIZE);
4341
- const view = new DataView(bytes.buffer);
4342
- view.setUint32(0, END_SIGNATURE, true);
4343
- view.setUint16(8, options.count, true);
4344
- view.setUint16(10, options.count, true);
4345
- view.setUint32(12, options.directorySize, true);
4346
- view.setUint32(16, options.directoryOffset, true);
4347
- return bytes;
4348
- }
4349
- async function createZipArchive(entries, modifiedAt = /* @__PURE__ */ new Date()) {
4350
- const stamp = dosStamp(modifiedAt);
4351
- const encoder = new TextEncoder();
4352
- const deflated = await Promise.all(entries.map(async (entry) => ({
4353
- ...await deflateEntry(entry.data),
4354
- name: encoder.encode(entry.name),
4355
- size: entry.data.length
4356
- })));
4357
- const records = [];
4358
- const parts = [];
4359
- let offset = 0;
4360
- for (const entry of deflated) {
4361
- const record = {
4362
- ...entry,
4363
- offset
4364
- };
4365
- const header = localHeader(record, stamp);
4366
- parts.push(header, record.compressed);
4367
- records.push(record);
4368
- offset += header.length + record.compressed.length;
4369
- }
4370
- const directory = records.map((record) => centralHeader(record, stamp));
4371
- const directorySize = directory.reduce((size, part) => size + part.length, 0);
4372
- const end = endRecord({
4373
- count: records.length,
4374
- directoryOffset: offset,
4375
- directorySize
4376
- });
4377
- return new Blob([
4378
- ...parts,
4379
- ...directory,
4380
- end
4381
- ], { type: "application/zip" });
4382
- }
4383
- //#endregion
4384
- //#region src/sessions/ui/handoff/brief-export.ts
4385
- const BRIEF_FILE_NAME = "brief.md";
4386
- const UNSAFE_FILE_CHARACTERS = /[^a-z0-9]+/g;
4387
- const EDGE_DASHES = /^-|-$/g;
4388
- async function downloadFile(file) {
4389
- const response = await fetch(file.url);
4390
- if (!response.ok) throw new Error(`${file.name} could not be downloaded (${response.status}).`);
4391
- return {
4392
- data: new Uint8Array(await response.arrayBuffer()),
4393
- name: file.name
4394
- };
4395
- }
4396
- function zipFileName(sessionTitle) {
4397
- return `reflet-session-${sessionTitle.toLowerCase().replace(UNSAFE_FILE_CHARACTERS, "-").replace(EDGE_DASHES, "") || "brief"}.zip`;
4398
- }
4399
- /** File URLs are short-lived, so they are fetched right when the export is asked for. */
4400
- async function buildBriefZip(options) {
4401
- const evidence = await Promise.all(options.files.map(downloadFile));
4402
- return await createZipArchive([{
4403
- data: new TextEncoder().encode(options.brief),
4404
- name: BRIEF_FILE_NAME
4405
- }, ...evidence]);
4406
- }
4407
- function saveBlob(options) {
4408
- const url = URL.createObjectURL(options.blob);
4409
- const link = document.createElement("a");
4410
- link.download = options.fileName;
4411
- link.href = url;
4412
- link.click();
4413
- setTimeout(() => URL.revokeObjectURL(url), 0);
4128
+ const toggleEvidence = (id) => onChange({
4129
+ ...selection,
4130
+ evidenceIds: toggled(selection.evidenceIds, id)
4131
+ });
4132
+ const chosenProposalIds = new Set(selection.proposalIds);
4133
+ const chosenEvidenceIds = new Set(selection.evidenceIds);
4134
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ChoiceGroup, {
4135
+ legend: "Intentions",
4136
+ children: proposals.map((proposal) => /* @__PURE__ */ jsx(CheckRow, {
4137
+ isChecked: chosenProposalIds.has(proposal.id),
4138
+ label: proposal.need,
4139
+ onToggle: () => toggleProposal(proposal.id)
4140
+ }, proposal.id))
4141
+ }), evidence.length > 0 && /* @__PURE__ */ jsx(ChoiceGroup, {
4142
+ legend: "Evidence",
4143
+ children: evidence.map((item) => /* @__PURE__ */ jsx(CheckRow, {
4144
+ isChecked: chosenEvidenceIds.has(item.id),
4145
+ label: `${formatClock(item.atMs)} · ${evidenceCaption(item)}`,
4146
+ onToggle: () => toggleEvidence(item.id)
4147
+ }, item.id))
4148
+ })] });
4414
4149
  }
4415
4150
  //#endregion
4416
- //#region src/sessions/ui/review/transcript/evidence-thumb.tsx
4417
- const TRIGGER_LABELS = {
4418
- hover: "Long hover",
4419
- manual: "Added by hand",
4420
- mark: "Marked moment",
4421
- navigation: "Page change",
4422
- select: "Pointed element",
4423
- start: "Session start"
4424
- };
4425
- function evidenceCaption(evidence) {
4426
- const element = evidence.element ? ` · ${evidence.element.label}` : "";
4427
- return `${TRIGGER_LABELS[evidence.trigger]}${element}`;
4428
- }
4429
- const hasViewport = (frame) => frame.viewportWidth > 0 && frame.viewportHeight > 0;
4430
- const percent = (value, total) => `${value / total * 100}%`;
4431
- function percentBox(rect, frame) {
4432
- if (!hasViewport(frame)) return null;
4433
- return {
4434
- height: percent(rect.height, frame.viewportHeight),
4435
- left: percent(rect.x, frame.viewportWidth),
4436
- top: percent(rect.y, frame.viewportHeight),
4437
- width: percent(rect.width, frame.viewportWidth)
4151
+ //#region src/sessions/ui/handoff/handoff-view.tsx
4152
+ function useDelivery(options) {
4153
+ const { api } = useSessionWorkspace();
4154
+ const run = useSessionAction();
4155
+ const { files, handoff } = options.prepared;
4156
+ const deliver = async (kind, send) => {
4157
+ try {
4158
+ await send();
4159
+ } catch (error) {
4160
+ reportFailure(`The brief was not ${kind}`, error);
4161
+ return;
4162
+ }
4163
+ announce(kind === "copied" ? "Brief copied" : "Brief downloaded");
4164
+ await run("The delivery was not recorded", () => api.markHandoffDelivered({
4165
+ handoffId: handoff.id,
4166
+ kind
4167
+ }));
4438
4168
  };
4439
- }
4440
- function percentPoint(point, frame) {
4441
- if (!hasViewport(frame)) return null;
4442
4169
  return {
4443
- left: percent(point.x, frame.viewportWidth),
4444
- top: percent(point.y, frame.viewportHeight)
4170
+ copy: () => deliver("copied", () => copyToClipboard(Promise.resolve(handoff.brief))),
4171
+ exportZip: () => deliver("exported", async () => {
4172
+ saveBlob({
4173
+ blob: await buildBriefZip({
4174
+ brief: handoff.brief,
4175
+ files
4176
+ }),
4177
+ fileName: zipFileName(options.title)
4178
+ });
4179
+ })
4445
4180
  };
4446
4181
  }
4447
- function EvidenceThumb({ evidence, isLarge = false }) {
4448
- const { element, frame, image } = evidence;
4449
- if (!image?.url) return null;
4450
- const highlight = element && frame ? percentBox(element.rect, frame) : null;
4451
- return /* @__PURE__ */ jsxs("span", {
4452
- className: "dt-ses-shot",
4453
- "data-large": isLarge,
4454
- children: [/* @__PURE__ */ jsx("img", {
4455
- alt: evidenceCaption(evidence),
4456
- height: image.height,
4457
- src: image.url,
4458
- width: image.width
4459
- }), highlight && /* @__PURE__ */ jsx("span", {
4460
- "aria-hidden": "true",
4461
- className: "dt-ses-shot-mark",
4462
- style: highlight
4463
- })]
4464
- });
4465
- }
4466
- //#endregion
4467
- //#region src/sessions/ui/review/continuation/intention-history.tsx
4468
- const FIELD_LABELS = {
4469
- need: "Need",
4470
- observed: "Observed",
4471
- response: "Considered response",
4472
- setAside: "Set aside",
4473
- unknowns: "To check"
4474
- };
4475
- function fieldValueText(value) {
4476
- return (Array.isArray(value) ? value.join("; ") : value) || "—";
4477
- }
4478
- function AppliedChanges({ applied }) {
4479
- if (applied.length === 0) return null;
4480
- return /* @__PURE__ */ jsx("ul", {
4481
- className: "dt-ses-rows",
4482
- children: applied.map((change) => /* @__PURE__ */ jsxs("li", {
4483
- className: "dt-ses-row",
4484
- children: [/* @__PURE__ */ jsxs("p", {
4485
- className: "dt-meta",
4486
- children: [
4487
- "Accepted into revision ",
4488
- change.revision,
4489
- " by",
4490
- " ",
4491
- change.byName ?? "a teammate",
4492
- " · ",
4493
- formatDate(change.at)
4494
- ]
4495
- }), /* @__PURE__ */ jsx("ul", {
4496
- className: "dt-ses-list",
4497
- children: change.fields.map((field) => /* @__PURE__ */ jsxs("li", { children: [
4498
- FIELD_LABELS[field.field],
4499
- ": ",
4500
- fieldValueText(field.before),
4501
- " →",
4502
- " ",
4503
- fieldValueText(field.after)
4504
- ] }, field.field))
4505
- })]
4506
- }, change.revision))
4182
+ function PreparedBrief({ onChangeSelection, prepared, title }) {
4183
+ const { copy, exportZip } = useDelivery({
4184
+ prepared,
4185
+ title
4507
4186
  });
4508
- }
4509
- function HistoryExtracts({ evidence, quotes }) {
4510
- return /* @__PURE__ */ jsxs(Fragment, { children: [quotes.map((quote) => /* @__PURE__ */ jsxs("blockquote", {
4511
- className: "dt-ses-moment-quote",
4512
- children: [
4513
- /* @__PURE__ */ jsx("span", {
4514
- className: "dt-ses-time",
4515
- children: formatClock(quote.startMs)
4516
- }),
4517
- ` “${quote.text}”`,
4518
- quote.context.map((line) => /* @__PURE__ */ jsx("span", {
4519
- className: "dt-meta dt-ses-context-line",
4520
- children: line
4521
- }, line))
4522
- ]
4523
- }, quote.segmentId)), evidence.length > 0 && /* @__PURE__ */ jsx("div", {
4524
- className: "dt-ses-thumbs",
4525
- children: evidence.map((item) => /* @__PURE__ */ jsx(EvidenceThumb, { evidence: item }, item.id))
4526
- })] });
4527
- }
4528
- function entryTitle(entry) {
4529
- const { session } = entry;
4530
- return entry.origin === "continuation" ? `Continued in “${session.title}” · ${formatDate(session.startedAt)}` : `Added from “${session.title}” · ${formatDate(session.startedAt)}`;
4531
- }
4532
- function HistoryEntry({ context, entry }) {
4533
- const evidenceIds = new Set(entry.evidenceIds);
4534
- const evidence = context.detail.contributedEvidence.filter((item) => evidenceIds.has(item.id));
4535
- return /* @__PURE__ */ jsxs("li", {
4536
- className: "dt-ses-history",
4537
- "data-removed": entry.detachedAt !== null,
4187
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("textarea", {
4188
+ "aria-label": "Brief",
4189
+ className: "dt-field dt-ses-brief",
4190
+ readOnly: true,
4191
+ rows: 12,
4192
+ value: prepared.handoff.brief
4193
+ }), /* @__PURE__ */ jsxs("div", {
4194
+ className: "dt-actions",
4538
4195
  children: [
4539
- /* @__PURE__ */ jsx("p", {
4540
- className: "dt-ses-subhead",
4541
- children: entryTitle(entry)
4542
- }),
4543
- entry.detachedAt !== null && /* @__PURE__ */ jsxs("p", {
4544
- className: "dt-meta",
4545
- children: [
4546
- "Removed on ",
4547
- formatDate(entry.detachedAt),
4548
- ": what it added was taken back."
4549
- ]
4196
+ /* @__PURE__ */ jsx("button", {
4197
+ className: "dt-btn",
4198
+ "data-variant": "primary",
4199
+ onClick: copy,
4200
+ type: "button",
4201
+ children: "Copy brief"
4550
4202
  }),
4551
- /* @__PURE__ */ jsx(AppliedChanges, { applied: entry.applied }),
4552
- entry.extractsHidden ? /* @__PURE__ */ jsx("p", {
4553
- className: "dt-meta",
4554
- children: "Extracts are private to the author"
4555
- }) : /* @__PURE__ */ jsx(HistoryExtracts, {
4556
- evidence,
4557
- quotes: entry.quotes
4203
+ /* @__PURE__ */ jsx("button", {
4204
+ className: "dt-btn",
4205
+ onClick: exportZip,
4206
+ type: "button",
4207
+ children: "Download .zip"
4558
4208
  }),
4559
- entry.session.canOpen && /* @__PURE__ */ jsx("button", {
4560
- className: "dt-ses-link",
4561
- onClick: () => context.navigation.onOpenSession(entry.session.id),
4209
+ /* @__PURE__ */ jsx("button", {
4210
+ className: "dt-btn",
4211
+ onClick: onChangeSelection,
4562
4212
  type: "button",
4563
- children: "Open session"
4213
+ children: "Edit selection"
4564
4214
  })
4565
4215
  ]
4566
- });
4567
- }
4568
- function IntentionHistory({ context, intentionId }) {
4569
- const entries = context.detail.history.filter((entry) => entry.intentionId === intentionId);
4570
- if (entries.length === 0) return null;
4571
- return /* @__PURE__ */ jsxs("section", { children: [/* @__PURE__ */ jsx("h4", {
4572
- className: "dt-ses-label",
4573
- children: "History"
4574
- }), /* @__PURE__ */ jsx("ul", {
4575
- className: "dt-ses-rows",
4576
- children: entries.map((entry) => /* @__PURE__ */ jsx(HistoryEntry, {
4577
- context,
4578
- entry
4579
- }, entry.id))
4580
- })] });
4581
- }
4582
- //#endregion
4583
- //#region src/sessions/ui/handoff/handoff-changes.tsx
4584
- function differences(proposal, last) {
4585
- return [
4586
- {
4587
- after: proposal.need,
4588
- before: last.need,
4589
- label: "Need"
4590
- },
4591
- {
4592
- after: fieldValueText(proposal.response),
4593
- before: fieldValueText(last.response),
4594
- label: "Considered response"
4595
- },
4596
- {
4597
- after: fieldValueText(proposal.unknowns),
4598
- before: last.unknowns === null ? null : fieldValueText(last.unknowns),
4599
- label: "To check"
4600
- }
4601
- ].flatMap(({ after, before, label }) => before === null || before === after ? [] : [{
4602
- after,
4603
- before,
4604
- label
4605
- }]);
4606
- }
4607
- function ChangeSummary({ last, proposal }) {
4608
- return /* @__PURE__ */ jsxs("li", {
4609
- className: "dt-ses-row",
4610
- children: [
4611
- /* @__PURE__ */ jsx("p", {
4612
- className: "dt-ses-subhead",
4613
- children: proposal.need
4614
- }),
4615
- /* @__PURE__ */ jsxs("p", {
4616
- className: "dt-meta",
4617
- children: [
4618
- "Changed since the brief of ",
4619
- formatDate(last.at),
4620
- " (revision",
4621
- " ",
4622
- last.revision,
4623
- " → ",
4624
- proposal.revision,
4625
- ")"
4626
- ]
4627
- }),
4628
- /* @__PURE__ */ jsx("ul", {
4629
- className: "dt-ses-list",
4630
- children: differences(proposal, last).map((difference) => /* @__PURE__ */ jsxs("li", { children: [
4631
- difference.label,
4632
- ": ",
4633
- difference.before,
4634
- " → ",
4635
- difference.after
4636
- ] }, difference.label))
4637
- })
4638
- ]
4639
- });
4640
- }
4641
- function ChangedSinceBriefs({ detail, proposalIds }) {
4642
- const selected = new Set(proposalIds);
4643
- const changed = detail.proposals.flatMap((proposal) => {
4644
- const last = proposal.lastHandoff;
4645
- return selected.has(proposal.id) && last && last.revision < proposal.revision ? [{
4646
- last,
4647
- proposal
4648
- }] : [];
4649
- });
4650
- if (changed.length === 0) return null;
4651
- return /* @__PURE__ */ jsx("ul", {
4652
- className: "dt-ses-rows",
4653
- children: changed.map(({ last, proposal }) => /* @__PURE__ */ jsx(ChangeSummary, {
4654
- last,
4655
- proposal
4656
- }, proposal.id))
4657
- });
4658
- }
4659
- function deliveryLine(handoff) {
4660
- const kinds = new Set(handoff.deliveries.map((delivery) => delivery.kind));
4661
- if (kinds.size === 0) return "not copied or exported";
4662
- return [...kinds].join(" and ");
4663
- }
4664
- function intentionsLine(handoff) {
4665
- const [only, ...others] = handoff.proposals;
4666
- if (only && others.length === 0) return `“${only.need}” revision ${only.revision}`;
4667
- return countLabel(handoff.proposals.length, "intention");
4668
- }
4669
- function PreviousBriefs({ handoffs }) {
4670
- if (handoffs.length === 0) return null;
4671
- return /* @__PURE__ */ jsxs("section", {
4672
- className: "dt-ses-form",
4673
- children: [/* @__PURE__ */ jsx("h3", {
4674
- className: "dt-ses-label",
4675
- children: "Previous briefs"
4676
- }), /* @__PURE__ */ jsx("ul", {
4677
- className: "dt-ses-rows",
4678
- children: handoffs.map((handoff) => {
4679
- const date = formatDate(handoff.createdAt);
4680
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("details", { children: [/* @__PURE__ */ jsxs("summary", { children: [
4681
- date,
4682
- " · ",
4683
- intentionsLine(handoff),
4684
- " · ",
4685
- deliveryLine(handoff)
4686
- ] }), /* @__PURE__ */ jsx("textarea", {
4687
- "aria-label": `Brief of ${date}`,
4688
- className: "dt-field dt-ses-brief",
4689
- readOnly: true,
4690
- rows: 8,
4691
- value: handoff.brief
4692
- })] }) }, handoff.id);
4693
- })
4694
- })]
4695
- });
4696
- }
4697
- //#endregion
4698
- //#region src/sessions/ui/handoff/handoff-selection.tsx
4699
- function keptProposals(detail) {
4700
- return detail.proposals.filter((proposal) => proposal.decision === "kept" && proposal.mergedIntoId === null);
4701
- }
4702
- function evidenceOf(detail, proposals) {
4703
- const intentionIds = new Set(proposals.map((proposal) => proposal.id));
4704
- const contributed = detail.history.filter((entry) => intentionIds.has(entry.intentionId) && entry.detachedAt === null);
4705
- const cited = /* @__PURE__ */ new Set([...proposals.flatMap((proposal) => proposal.evidenceIds), ...contributed.flatMap((entry) => entry.evidenceIds)]);
4706
- return [...detail.evidence, ...detail.contributedEvidence].filter((item) => cited.has(item.id) && item.status === "available");
4707
- }
4708
- function initialSelection(detail) {
4709
- const proposals = keptProposals(detail);
4710
- return {
4711
- evidenceIds: evidenceOf(detail, proposals).map((item) => item.id),
4712
- proposalIds: proposals.map((proposal) => proposal.id)
4713
- };
4714
- }
4715
- function toggled(ids, id) {
4716
- return ids.includes(id) ? ids.filter((other) => other !== id) : [...ids, id];
4717
- }
4718
- function CheckRow({ isChecked, label, onToggle }) {
4719
- return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("label", {
4720
- className: "dt-ses-check",
4721
- children: [/* @__PURE__ */ jsx("input", {
4722
- checked: isChecked,
4723
- onChange: onToggle,
4724
- type: "checkbox"
4725
- }), /* @__PURE__ */ jsx("span", { children: label })]
4726
- }) });
4727
- }
4728
- function ChoiceGroup({ children, legend }) {
4729
- return /* @__PURE__ */ jsxs("fieldset", {
4730
- className: "dt-ses-choices",
4731
- children: [/* @__PURE__ */ jsx("legend", {
4732
- className: "dt-ses-label",
4733
- children: legend
4734
- }), /* @__PURE__ */ jsx("ul", {
4735
- className: "dt-ses-list",
4736
- children
4737
- })]
4738
- });
4739
- }
4740
- function HandoffChoices({ detail, onChange, selection }) {
4741
- const proposals = keptProposals(detail);
4742
- const evidence = evidenceOf(detail, proposals);
4743
- const toggleProposal = (id) => onChange({
4744
- ...selection,
4745
- proposalIds: toggled(selection.proposalIds, id)
4746
- });
4747
- const toggleEvidence = (id) => onChange({
4748
- ...selection,
4749
- evidenceIds: toggled(selection.evidenceIds, id)
4750
- });
4751
- const chosenProposalIds = new Set(selection.proposalIds);
4752
- const chosenEvidenceIds = new Set(selection.evidenceIds);
4753
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ChoiceGroup, {
4754
- legend: "Intentions",
4755
- children: proposals.map((proposal) => /* @__PURE__ */ jsx(CheckRow, {
4756
- isChecked: chosenProposalIds.has(proposal.id),
4757
- label: proposal.need,
4758
- onToggle: () => toggleProposal(proposal.id)
4759
- }, proposal.id))
4760
- }), evidence.length > 0 && /* @__PURE__ */ jsx(ChoiceGroup, {
4761
- legend: "Evidence",
4762
- children: evidence.map((item) => /* @__PURE__ */ jsx(CheckRow, {
4763
- isChecked: chosenEvidenceIds.has(item.id),
4764
- label: `${formatClock(item.atMs)} · ${evidenceCaption(item)}`,
4765
- onToggle: () => toggleEvidence(item.id)
4766
- }, item.id))
4767
- })] });
4768
- }
4769
- //#endregion
4770
- //#region src/sessions/ui/handoff/handoff-view.tsx
4771
- function useDelivery(options) {
4772
- const { api } = useSessionWorkspace();
4773
- const run = useSessionAction();
4774
- const { files, handoff } = options.prepared;
4775
- const deliver = async (kind, send) => {
4776
- try {
4777
- await send();
4778
- } catch (error) {
4779
- reportFailure(`The brief was not ${kind}`, error);
4780
- return;
4781
- }
4782
- announce(kind === "copied" ? "Brief copied" : "Brief exported");
4783
- await run("The delivery was not recorded", () => api.markHandoffDelivered({
4784
- handoffId: handoff.id,
4785
- kind
4786
- }));
4787
- };
4788
- return {
4789
- copy: () => deliver("copied", () => copyToClipboard(Promise.resolve(handoff.brief))),
4790
- exportZip: () => deliver("exported", async () => {
4791
- saveBlob({
4792
- blob: await buildBriefZip({
4793
- brief: handoff.brief,
4794
- files
4795
- }),
4796
- fileName: zipFileName(options.title)
4797
- });
4798
- })
4799
- };
4800
- }
4801
- function PreparedBrief({ onChangeSelection, prepared, title }) {
4802
- const { copy, exportZip } = useDelivery({
4803
- prepared,
4804
- title
4805
- });
4806
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("textarea", {
4807
- "aria-label": "Brief",
4808
- className: "dt-field dt-ses-brief",
4809
- readOnly: true,
4810
- rows: 12,
4811
- value: prepared.handoff.brief
4812
- }), /* @__PURE__ */ jsxs("div", {
4813
- className: "dt-actions",
4814
- children: [
4815
- /* @__PURE__ */ jsx("button", {
4816
- className: "dt-btn",
4817
- "data-variant": "primary",
4818
- onClick: copy,
4819
- type: "button",
4820
- children: "Copy brief"
4821
- }),
4822
- /* @__PURE__ */ jsx("button", {
4823
- className: "dt-btn",
4824
- onClick: exportZip,
4825
- type: "button",
4826
- children: "Export .zip"
4827
- }),
4828
- /* @__PURE__ */ jsx("button", {
4829
- className: "dt-btn",
4830
- onClick: onChangeSelection,
4831
- type: "button",
4832
- children: "Change selection"
4833
- })
4834
- ]
4835
- })] });
4836
- }
4837
- function useInternalFeedback(proposalIds) {
4838
- const { api } = useSessionWorkspace();
4839
- const run = useSessionAction();
4840
- return async () => {
4841
- if ((await Promise.all(proposalIds.map((proposalId) => run("The feedback was not created", () => api.createFeedback(proposalId))))).every((result) => result !== null)) announce("Internal feedback created — agents don't pick it up from the queue; hand off the brief instead.");
4842
- };
4216
+ })] });
4843
4217
  }
4844
4218
  function usePrepareHandoff(options) {
4845
4219
  const { api } = useSessionWorkspace();
@@ -4876,12 +4250,11 @@ function usePrepareHandoff(options) {
4876
4250
  };
4877
4251
  }
4878
4252
  function SelectionStep({ detail, handoff }) {
4879
- const createFeedback = useInternalFeedback(handoff.selection.proposalIds);
4880
- const hasProposals = handoff.selection.proposalIds.length > 0;
4253
+ const count = handoff.selection.proposalIds.length;
4881
4254
  return /* @__PURE__ */ jsxs(Fragment, { children: [
4882
4255
  /* @__PURE__ */ jsx("p", {
4883
4256
  className: "dt-hint",
4884
- children: "Coding agents get exactly the brief and files you pick here."
4257
+ children: "Tick what your agent should get. Nothing leaves Reflet until you copy or download the brief."
4885
4258
  }),
4886
4259
  /* @__PURE__ */ jsx(HandoffChoices, {
4887
4260
  detail,
@@ -4892,26 +4265,20 @@ function SelectionStep({ detail, handoff }) {
4892
4265
  detail,
4893
4266
  proposalIds: handoff.selection.proposalIds
4894
4267
  }),
4895
- /* @__PURE__ */ jsxs("div", {
4268
+ /* @__PURE__ */ jsx("div", {
4896
4269
  className: "dt-actions",
4897
- children: [/* @__PURE__ */ jsx("button", {
4270
+ children: /* @__PURE__ */ jsx("button", {
4898
4271
  className: "dt-btn",
4899
4272
  "data-variant": "primary",
4900
- disabled: !hasProposals || handoff.isPreparing,
4273
+ disabled: count === 0 || handoff.isPreparing,
4901
4274
  onClick: handoff.prepare,
4902
4275
  type: "button",
4903
- children: "Prepare brief"
4904
- }), /* @__PURE__ */ jsx("button", {
4905
- className: "dt-btn",
4906
- disabled: !hasProposals,
4907
- onClick: createFeedback,
4908
- type: "button",
4909
- children: "Create internal feedback"
4910
- })]
4276
+ children: "Build brief"
4277
+ })
4911
4278
  })
4912
4279
  ] });
4913
4280
  }
4914
- function HandoffView({ detail, onClose, review }) {
4281
+ function HandoffView({ detail, review }) {
4915
4282
  const handoff = usePrepareHandoff({
4916
4283
  detail,
4917
4284
  review
@@ -4920,16 +4287,11 @@ function HandoffView({ detail, onClose, review }) {
4920
4287
  "aria-labelledby": "rf-handoff-title",
4921
4288
  className: "dt-ses-handoff",
4922
4289
  children: [
4923
- /* @__PURE__ */ jsx("button", {
4924
- className: "dt-ses-link",
4925
- onClick: onClose,
4926
- type: "button",
4927
- children: "← Back to review"
4928
- }),
4929
4290
  /* @__PURE__ */ jsx("h2", {
4930
4291
  className: "dt-ses-title",
4931
4292
  id: "rf-handoff-title",
4932
- children: "Hand off"
4293
+ tabIndex: -1,
4294
+ children: "Brief for your agent"
4933
4295
  }),
4934
4296
  handoff.prepared ? /* @__PURE__ */ jsx(PreparedBrief, {
4935
4297
  onChangeSelection: handoff.clear,
@@ -4939,11 +4301,7 @@ function HandoffView({ detail, onClose, review }) {
4939
4301
  detail,
4940
4302
  handoff
4941
4303
  }),
4942
- /* @__PURE__ */ jsx("p", {
4943
- className: "dt-meta",
4944
- children: "Copied or exported briefs can't be recalled."
4945
- }),
4946
- /* @__PURE__ */ jsx(PreviousBriefs, { handoffs: detail.handoffs })
4304
+ /* @__PURE__ */ jsx(EarlierBriefs, { handoffs: detail.handoffs })
4947
4305
  ]
4948
4306
  });
4949
4307
  }
@@ -5880,7 +5238,7 @@ function UpdateCard({ context, continuation }) {
5880
5238
  }
5881
5239
  //#endregion
5882
5240
  //#region src/sessions/ui/review/transcript/remove-evidence.tsx
5883
- const REMOVE_COPY = "Remove this screenshot? The image is deleted for good. Briefs you already copied or exported can't be recalled.";
5241
+ const REMOVE_COPY = "Remove this screenshot? The image is deleted for good. Briefs you already downloaded keep their copy.";
5884
5242
  function useFocusWhen(shouldFocus) {
5885
5243
  const ref = useRef(null);
5886
5244
  useEffect(() => {
@@ -6412,7 +5770,7 @@ function MomentHeading({ onClose, segment }) {
6412
5770
  })]
6413
5771
  });
6414
5772
  }
6415
- function useEscapeToClose(onClose) {
5773
+ function useEscapeToClose$1(onClose) {
6416
5774
  const sectionRef = useRef(null);
6417
5775
  const onCloseRef = useRef(onClose);
6418
5776
  useLayoutEffect(() => {
@@ -6434,7 +5792,7 @@ function useEscapeToClose(onClose) {
6434
5792
  function MomentView({ handlers, segmentId, session }) {
6435
5793
  const { detail, review } = session;
6436
5794
  const segment = detail.segments.find((item) => item.id === segmentId);
6437
- const sectionRef = useEscapeToClose(handlers.onClose);
5795
+ const sectionRef = useEscapeToClose$1(handlers.onClose);
6438
5796
  return /* @__PURE__ */ jsxs("section", {
6439
5797
  "aria-label": "Moment",
6440
5798
  className: "dt-ses-moment",
@@ -6873,6 +6231,50 @@ function ScreenReport({ context, report }) {
6873
6231
  })] });
6874
6232
  }
6875
6233
  //#endregion
6234
+ //#region src/devtools/client/devtools-icons.tsx
6235
+ function NotesIcon() {
6236
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01" }) });
6237
+ }
6238
+ function InboxIcon() {
6239
+ return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("path", { d: "M22 12h-6l-2 3h-4l-2-3H2" }), /* @__PURE__ */ jsx("path", { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" })] });
6240
+ }
6241
+ function PanelIcon() {
6242
+ return /* @__PURE__ */ jsxs(Svg, { children: [/* @__PURE__ */ jsx("rect", {
6243
+ height: "16",
6244
+ rx: "2.5",
6245
+ width: "18",
6246
+ x: "3",
6247
+ y: "4"
6248
+ }), /* @__PURE__ */ jsx("path", { d: "M14 4v16" })] });
6249
+ }
6250
+ function CodeIcon() {
6251
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m8 7-5 5 5 5M16 7l5 5-5 5" }) });
6252
+ }
6253
+ function RefreshIcon() {
6254
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M20 11a8 8 0 0 0-14.5-4.5L4 8M4 4v4h4M4 13a8 8 0 0 0 14.5 4.5L20 16M20 20v-4h-4" }) });
6255
+ }
6256
+ function ChevronLeftIcon() {
6257
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m15 6-6 6 6 6" }) });
6258
+ }
6259
+ function ChevronUpDownIcon() {
6260
+ return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "m8 10 4-4 4 4M8 14l4 4 4-4" }) });
6261
+ }
6262
+ function ViewOptionsIcon() {
6263
+ return /* @__PURE__ */ jsxs(Svg, { children: [
6264
+ /* @__PURE__ */ jsx("path", { d: "M4 7h10M18 7h2M4 17h2M10 17h10" }),
6265
+ /* @__PURE__ */ jsx("circle", {
6266
+ cx: "16",
6267
+ cy: "7",
6268
+ r: "2"
6269
+ }),
6270
+ /* @__PURE__ */ jsx("circle", {
6271
+ cx: "8",
6272
+ cy: "17",
6273
+ r: "2"
6274
+ })
6275
+ ] });
6276
+ }
6277
+ //#endregion
6876
6278
  //#region src/sessions/ui/start/session-disclosure.tsx
6877
6279
  const SEEN_KEY_PREFIX = "reflet-sessions-disclosure:";
6878
6280
  function hasSeenDisclosure(accountKey) {
@@ -6960,21 +6362,22 @@ function StartProblem({ failure, onRetry, onWriteNote }) {
6960
6362
  })]
6961
6363
  });
6962
6364
  }
6963
- function StartActions({ isBusy, onStart, onWriteNote }) {
6365
+ function StartActions({ isBusy, onStart }) {
6964
6366
  return /* @__PURE__ */ jsxs("div", {
6965
- className: "dt-actions",
6966
- children: [/* @__PURE__ */ jsx("button", {
6967
- className: "dt-btn",
6968
- "data-variant": "primary",
6969
- disabled: isBusy,
6970
- onClick: onStart,
6971
- type: "button",
6972
- children: "Start session"
6973
- }), onWriteNote && /* @__PURE__ */ jsx("button", {
6974
- className: "dt-btn",
6975
- onClick: onWriteNote,
6976
- type: "button",
6977
- children: "Write a note instead"
6367
+ className: "dt-ses-start",
6368
+ children: [/* @__PURE__ */ jsx("p", {
6369
+ className: "dt-meta",
6370
+ children: "Talk while you use the app. Reflet turns what you say and show into intentions you can brief an agent with."
6371
+ }), /* @__PURE__ */ jsx("div", {
6372
+ className: "dt-actions",
6373
+ children: /* @__PURE__ */ jsxs("button", {
6374
+ className: "dt-btn",
6375
+ "data-variant": "primary",
6376
+ disabled: isBusy,
6377
+ onClick: onStart,
6378
+ type: "button",
6379
+ children: [/* @__PURE__ */ jsx(MicIcon, {}), "Start session"]
6380
+ })
6978
6381
  })]
6979
6382
  });
6980
6383
  }
@@ -7006,19 +6409,21 @@ function MicrophonePrompt({ isRequesting }) {
7006
6409
  function StartSession({ onStarted, onWriteNote }) {
7007
6410
  const { account } = useSessionWorkspace();
7008
6411
  const { failure, isBusy, isRequesting, start } = useRecorderStart(onStarted);
6412
+ const [isFirstVisit] = useState(() => !hasSeenDisclosure(account.key));
7009
6413
  const startBlank = () => start({});
6414
+ const disclosure = /* @__PURE__ */ jsx(SessionDisclosure, { accountKey: account.key });
7010
6415
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7011
- /* @__PURE__ */ jsx(SessionDisclosure, { accountKey: account.key }),
7012
- /* @__PURE__ */ jsx(MicrophonePrompt, { isRequesting }),
6416
+ isFirstVisit && disclosure,
7013
6417
  failure ? /* @__PURE__ */ jsx(StartProblem, {
7014
6418
  failure,
7015
6419
  onRetry: startBlank,
7016
6420
  onWriteNote
7017
6421
  }) : /* @__PURE__ */ jsx(StartActions, {
7018
6422
  isBusy,
7019
- onStart: startBlank,
7020
- onWriteNote
7021
- })
6423
+ onStart: startBlank
6424
+ }),
6425
+ /* @__PURE__ */ jsx(MicrophonePrompt, { isRequesting }),
6426
+ !isFirstVisit && disclosure
7022
6427
  ] });
7023
6428
  }
7024
6429
  //#endregion
@@ -7103,15 +6508,44 @@ function useDecide(review) {
7103
6508
  });
7104
6509
  };
7105
6510
  }
6511
+ function withFeedbackIds(feedbackIds) {
6512
+ return (detail) => ({
6513
+ ...detail,
6514
+ proposals: detail.proposals.map((item) => {
6515
+ const feedbackId = feedbackIds.get(item.id);
6516
+ return feedbackId ? {
6517
+ ...item,
6518
+ feedbackId
6519
+ } : item;
6520
+ })
6521
+ });
6522
+ }
6523
+ function useAddToBoard(review) {
6524
+ const { api } = useSessionWorkspace();
6525
+ const run = useSessionAction();
6526
+ return async (proposals) => {
6527
+ const created = await Promise.all(proposals.map(async (proposal) => {
6528
+ const result = await run(`${quotedNeed(proposal.need)} was not added to the board`, () => api.createFeedback(proposal.id));
6529
+ return result ? [proposal.id, result.feedbackId] : null;
6530
+ }));
6531
+ const added = new Map(created.flatMap((entry) => entry ? [entry] : []));
6532
+ if (added.size === 0) return;
6533
+ review.patch(withFeedbackIds(added));
6534
+ const onlyAdded = added.size === 1 ? proposals.find((proposal) => added.has(proposal.id)) : void 0;
6535
+ announce(onlyAdded ? `Added ${quotedNeed(onlyAdded.need)} to the board` : `Added ${countLabel(added.size, "intention")} to the board`);
6536
+ };
6537
+ }
7106
6538
  function useProposalActions(review) {
7107
6539
  const save = useProposalSave(review);
6540
+ const decide = useDecide(review);
7108
6541
  return {
6542
+ addToBoard: useAddToBoard(review),
7109
6543
  clarify: (proposal, text) => save("The precision was not sent", (client) => client.clarifyProposal({
7110
6544
  expectedRevision: proposal.revision,
7111
6545
  proposalId: proposal.id,
7112
6546
  text
7113
6547
  })),
7114
- decide: useDecide(review),
6548
+ decide,
7115
6549
  merge: async (targetId, sourceIds) => {
7116
6550
  if (await save("The intentions were not merged", (client) => client.mergeProposals({
7117
6551
  sourceIds,
@@ -7129,7 +6563,7 @@ function useProposalActions(review) {
7129
6563
  }
7130
6564
  //#endregion
7131
6565
  //#region src/sessions/ui/review/proposals/delete-intention.tsx
7132
- const DELETE_INTENTION_COPY = "Delete this intention? What later sessions added to it is deleted too, along with Reflet's copies of the briefs that included it. Briefs already copied or exported can't be recalled.";
6566
+ 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.";
7133
6567
  function DeleteConfirmation$1({ context, onCancel, proposal }) {
7134
6568
  const { api } = useSessionWorkspace();
7135
6569
  const run = useSessionAction();
@@ -7640,8 +7074,8 @@ const ICON_DECISIONS = [{
7640
7074
  function ProposalChips({ proposal }) {
7641
7075
  const decisionChip = DECISION_CHIPS[proposal.decision];
7642
7076
  const { handedOffRevision } = proposal;
7643
- let handoffChip = null;
7644
- if (handedOffRevision !== null) handoffChip = proposal.revision > handedOffRevision ? "Changed since handoff" : `Handed off rev ${handedOffRevision}`;
7077
+ let briefChip = null;
7078
+ if (handedOffRevision !== null) briefChip = proposal.revision > handedOffRevision ? "Changed since brief" : "In a brief";
7645
7079
  return /* @__PURE__ */ jsxs("div", {
7646
7080
  className: "dt-ses-chips",
7647
7081
  children: [
@@ -7662,9 +7096,13 @@ function ProposalChips({ proposal }) {
7662
7096
  "data-tone": "accent",
7663
7097
  children: "To re-examine"
7664
7098
  }),
7665
- handoffChip && /* @__PURE__ */ jsx("span", {
7099
+ briefChip && /* @__PURE__ */ jsx("span", {
7100
+ className: "dt-chip",
7101
+ children: briefChip
7102
+ }),
7103
+ proposal.feedbackId !== null && /* @__PURE__ */ jsx("span", {
7666
7104
  className: "dt-chip",
7667
- children: handoffChip
7105
+ children: "On the board"
7668
7106
  })
7669
7107
  ]
7670
7108
  });
@@ -7741,6 +7179,15 @@ function DecisionButtons({ context, onEdit, proposal }) {
7741
7179
  title: "Edit",
7742
7180
  type: "button",
7743
7181
  children: /* @__PURE__ */ jsx(PencilIcon, {})
7182
+ }),
7183
+ proposal.feedbackId === null && proposal.decision !== "dismissed" && /* @__PURE__ */ jsx("button", {
7184
+ "aria-label": "Add to board",
7185
+ className: "dt-btn",
7186
+ "data-icon-only": true,
7187
+ onClick: () => context.actions.addToBoard([proposal]),
7188
+ title: "Add to board",
7189
+ type: "button",
7190
+ children: /* @__PURE__ */ jsx(InboxIcon, {})
7744
7191
  })
7745
7192
  ]
7746
7193
  });
@@ -7896,13 +7343,6 @@ function ProposalCard({ context, proposal }) {
7896
7343
  }
7897
7344
  //#endregion
7898
7345
  //#region src/sessions/ui/review/proposals/proposals-tab.tsx
7899
- const VIEW_OPTIONS = [{
7900
- label: "On screen",
7901
- value: "screens"
7902
- }, {
7903
- label: "Details",
7904
- value: "cards"
7905
- }];
7906
7346
  function emptyCopy(detail) {
7907
7347
  const { session } = detail;
7908
7348
  if (session.collection !== "ended") return "Intentions appear here once the recording has uploaded and Reflet has read it.";
@@ -7971,25 +7411,12 @@ function AddProposalForm({ context, onDone }) {
7971
7411
  ]
7972
7412
  });
7973
7413
  }
7974
- function ViewSwitch({ onSelect, view }) {
7975
- return /* @__PURE__ */ jsx("div", {
7976
- className: "dt-tabs",
7977
- children: VIEW_OPTIONS.map((option) => /* @__PURE__ */ jsx("button", {
7978
- "aria-pressed": option.value === view,
7979
- className: "dt-tab",
7980
- onClick: () => onSelect(option.value),
7981
- type: "button",
7982
- children: option.label
7983
- }, option.value))
7984
- });
7985
- }
7986
- function ProposalsTab({ context, onSelectView, view }) {
7414
+ function ProposalsTab({ context, view }) {
7987
7415
  const [isAdding, setIsAdding] = useState(false);
7988
7416
  const proposals = context.detail.proposals.filter((proposal) => proposal.mergedIntoId === null);
7989
7417
  const { continuation } = context.detail;
7990
7418
  const report = calloutReport(context.detail);
7991
- const hasScreens = report.screens.length > 0;
7992
- const showsScreens = hasScreens && view === "screens";
7419
+ const showsScreens = report.screens.length > 0 && view === "screens";
7993
7420
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7994
7421
  continuation && /* @__PURE__ */ jsx(UpdateCard, {
7995
7422
  context,
@@ -7999,10 +7426,6 @@ function ProposalsTab({ context, onSelectView, view }) {
7999
7426
  className: "dt-empty",
8000
7427
  children: emptyCopy(context.detail)
8001
7428
  }),
8002
- hasScreens && /* @__PURE__ */ jsx(ViewSwitch, {
8003
- onSelect: onSelectView,
8004
- view
8005
- }),
8006
7429
  showsScreens && /* @__PURE__ */ jsx(ScreenReport, {
8007
7430
  context,
8008
7431
  report
@@ -8026,8 +7449,199 @@ function ProposalsTab({ context, onSelectView, view }) {
8026
7449
  ] });
8027
7450
  }
8028
7451
  //#endregion
7452
+ //#region src/sessions/ui/review/review-footer.tsx
7453
+ function ReviewFooter({ context, onOpenBrief }) {
7454
+ const confirmed = keptProposals(context.detail);
7455
+ const offBoard = confirmed.filter((proposal) => proposal.feedbackId === null);
7456
+ return /* @__PURE__ */ jsxs("div", {
7457
+ className: "dt-sheet-foot",
7458
+ children: [
7459
+ /* @__PURE__ */ jsxs("button", {
7460
+ "aria-describedby": confirmed.length === 0 ? "rf-brief-hint" : void 0,
7461
+ className: "dt-btn",
7462
+ "data-variant": "primary",
7463
+ disabled: confirmed.length === 0,
7464
+ onClick: onOpenBrief,
7465
+ type: "button",
7466
+ children: ["Prepare brief", confirmed.length > 0 && /* @__PURE__ */ jsx("span", {
7467
+ className: "dt-btn-count",
7468
+ children: confirmed.length
7469
+ })]
7470
+ }),
7471
+ offBoard.length > 0 && /* @__PURE__ */ jsxs("button", {
7472
+ className: "dt-btn",
7473
+ onClick: () => context.actions.addToBoard(offBoard),
7474
+ type: "button",
7475
+ children: [
7476
+ "Add ",
7477
+ offBoard.length,
7478
+ " to board"
7479
+ ]
7480
+ }),
7481
+ confirmed.length === 0 && /* @__PURE__ */ jsx("span", {
7482
+ className: "dt-status",
7483
+ id: "rf-brief-hint",
7484
+ children: "Confirm an intention to brief your agent or add it to the board"
7485
+ })
7486
+ ]
7487
+ });
7488
+ }
7489
+ //#endregion
7490
+ //#region src/devtools/client/focused-element.ts
7491
+ function focusedElementAround(node) {
7492
+ const scope = node.getRootNode();
7493
+ return scope instanceof ShadowRoot || scope instanceof Document ? scope.activeElement : null;
7494
+ }
7495
+ //#endregion
7496
+ //#region src/devtools/client/menu.tsx
7497
+ const MENU_ITEM_SELECTOR = "[role^=\"menuitem\"]";
7498
+ function nextIndex(key, current, count) {
7499
+ if (key === "ArrowDown") return (current + 1) % count;
7500
+ if (key === "ArrowUp") return (current - 1 + count) % count;
7501
+ if (key === "Home") return 0;
7502
+ if (key === "End") return count - 1;
7503
+ return null;
7504
+ }
7505
+ function useCloseOnOutsidePointer(containerRef, options) {
7506
+ const closeRef = useRef(options.close);
7507
+ useLayoutEffect(() => {
7508
+ closeRef.current = options.close;
7509
+ });
7510
+ const { isOpen } = options;
7511
+ useEffect(() => {
7512
+ if (!isOpen) return;
7513
+ const closeOnOutsidePointer = (event) => {
7514
+ const container = containerRef.current;
7515
+ if (container && !event.composedPath().includes(container)) closeRef.current();
7516
+ };
7517
+ document.addEventListener("pointerdown", closeOnOutsidePointer, true);
7518
+ return () => document.removeEventListener("pointerdown", closeOnOutsidePointer, true);
7519
+ }, [containerRef, isOpen]);
7520
+ }
7521
+ function useFocusOnOpen(listRef, isOpen) {
7522
+ useLayoutEffect(() => {
7523
+ const list = listRef.current;
7524
+ if (!(isOpen && list)) return;
7525
+ const items = [...list.querySelectorAll(MENU_ITEM_SELECTOR)];
7526
+ (items.find((item) => item.getAttribute("aria-checked") === "true") ?? items[0])?.focus({ preventScroll: true });
7527
+ }, [isOpen, listRef]);
7528
+ }
7529
+ /** Native: the panel's Escape listener shares the shadow root with React's, and runs before React's handler. */
7530
+ function useEscapeToClose(listRef, options) {
7531
+ const { isOpen, onEscape } = options;
7532
+ useEffect(() => {
7533
+ const list = listRef.current;
7534
+ if (!(isOpen && list)) return;
7535
+ const closeOnEscape = (event) => {
7536
+ if (event.key === "Escape" && !event.isComposing) {
7537
+ event.stopPropagation();
7538
+ onEscape();
7539
+ }
7540
+ };
7541
+ list.addEventListener("keydown", closeOnEscape);
7542
+ return () => list.removeEventListener("keydown", closeOnEscape);
7543
+ }, [
7544
+ isOpen,
7545
+ listRef,
7546
+ onEscape
7547
+ ]);
7548
+ }
7549
+ function MenuItemButton({ hasChoices, item, onChosen }) {
7550
+ const select = () => {
7551
+ onChosen();
7552
+ item.onSelect();
7553
+ };
7554
+ if (!hasChoices) return /* @__PURE__ */ jsx("button", {
7555
+ className: "dt-menu-item",
7556
+ "data-variant": item.variant,
7557
+ onClick: select,
7558
+ role: "menuitem",
7559
+ tabIndex: -1,
7560
+ type: "button",
7561
+ children: item.label
7562
+ });
7563
+ return /* @__PURE__ */ jsxs("button", {
7564
+ "aria-checked": item.checked === true,
7565
+ className: "dt-menu-item",
7566
+ onClick: select,
7567
+ role: "menuitemradio",
7568
+ tabIndex: -1,
7569
+ type: "button",
7570
+ children: [/* @__PURE__ */ jsx("span", {
7571
+ className: "dt-menu-check",
7572
+ children: item.checked && /* @__PURE__ */ jsx(CheckIcon, { size: 14 })
7573
+ }), item.label]
7574
+ });
7575
+ }
7576
+ function Menu({ items, placement = "below", trigger }) {
7577
+ const [isOpen, setIsOpen] = useState(false);
7578
+ const menuId = useId();
7579
+ const containerRef = useRef(null);
7580
+ const triggerRef = useRef(null);
7581
+ const listRef = useRef(null);
7582
+ const close = () => setIsOpen(false);
7583
+ const closeAndRefocus = () => {
7584
+ close();
7585
+ triggerRef.current?.focus({ preventScroll: true });
7586
+ };
7587
+ useCloseOnOutsidePointer(containerRef, {
7588
+ close,
7589
+ isOpen
7590
+ });
7591
+ useFocusOnOpen(listRef, isOpen);
7592
+ useEscapeToClose(listRef, {
7593
+ isOpen,
7594
+ onEscape: closeAndRefocus
7595
+ });
7596
+ const hasChoices = items.some((item) => item.checked !== void 0);
7597
+ const moveWithKeys = (event) => {
7598
+ if (event.key === "Tab") {
7599
+ close();
7600
+ return;
7601
+ }
7602
+ const list = event.currentTarget;
7603
+ const options = [...list.querySelectorAll(MENU_ITEM_SELECTOR)];
7604
+ const focused = focusedElementAround(list);
7605
+ const current = focused instanceof HTMLElement ? options.indexOf(focused) : -1;
7606
+ const index = nextIndex(event.key, current, options.length);
7607
+ if (index !== null) {
7608
+ event.preventDefault();
7609
+ options[index]?.focus();
7610
+ }
7611
+ };
7612
+ return /* @__PURE__ */ jsxs("div", {
7613
+ className: "dt-menu",
7614
+ ref: containerRef,
7615
+ children: [/* @__PURE__ */ jsx("button", {
7616
+ "aria-controls": isOpen ? menuId : void 0,
7617
+ "aria-expanded": isOpen,
7618
+ "aria-haspopup": "menu",
7619
+ "aria-label": trigger.label,
7620
+ className: trigger.className,
7621
+ onClick: () => setIsOpen(!isOpen),
7622
+ ref: triggerRef,
7623
+ title: trigger.label,
7624
+ type: "button",
7625
+ children: trigger.content
7626
+ }), isOpen && /* @__PURE__ */ jsx("div", {
7627
+ "aria-label": trigger.label,
7628
+ className: "dt-menu-list",
7629
+ "data-placement": placement,
7630
+ id: menuId,
7631
+ onKeyDown: moveWithKeys,
7632
+ ref: listRef,
7633
+ role: "menu",
7634
+ children: items.map((item) => /* @__PURE__ */ jsx(MenuItemButton, {
7635
+ hasChoices,
7636
+ item,
7637
+ onChosen: closeAndRefocus
7638
+ }, item.label))
7639
+ })]
7640
+ });
7641
+ }
7642
+ //#endregion
8029
7643
  //#region src/sessions/ui/review/review-header.tsx
8030
- const DELETE_COPY = "Delete this session? Its recordings, transcript and intentions are removed, including what later sessions added to those intentions. Briefs you already copied or exported can't be recalled.";
7644
+ const DELETE_COPY = "Delete this session? Its recording, transcript, screenshots and intentions go, along with what later sessions added to those intentions.";
8031
7645
  function withSessionFields(change) {
8032
7646
  return (detail) => ({
8033
7647
  ...detail,
@@ -8124,94 +7738,29 @@ function DeleteConfirmation({ onCancel, onDeleted, sessionId }) {
8124
7738
  })]
8125
7739
  });
8126
7740
  }
8127
- function useDismissOnOutside(isOpen, close) {
8128
- const containerRef = useRef(null);
8129
- const closeRef = useRef(close);
8130
- useLayoutEffect(() => {
8131
- closeRef.current = close;
8132
- });
8133
- useEffect(() => {
8134
- if (!isOpen) return;
8135
- const container = containerRef.current;
8136
- const closeOnOutsidePointer = (event) => {
8137
- if (container && !event.composedPath().includes(container)) closeRef.current();
8138
- };
8139
- const closeOnEscape = (event) => {
8140
- if (event instanceof KeyboardEvent && event.key === "Escape") {
8141
- event.stopPropagation();
8142
- closeRef.current();
8143
- }
8144
- };
8145
- document.addEventListener("pointerdown", closeOnOutsidePointer, true);
8146
- container?.addEventListener("keydown", closeOnEscape);
8147
- return () => {
8148
- document.removeEventListener("pointerdown", closeOnOutsidePointer, true);
8149
- container?.removeEventListener("keydown", closeOnEscape);
8150
- };
8151
- }, [isOpen]);
8152
- return containerRef;
8153
- }
8154
- function SessionMenu({ actions, isShared }) {
8155
- const [isOpen, setIsOpen] = useState(false);
8156
- const menuId = useId();
8157
- const close = () => setIsOpen(false);
8158
- const containerRef = useDismissOnOutside(isOpen, close);
8159
- const choose = (action) => () => {
8160
- close();
8161
- action();
8162
- };
8163
- return /* @__PURE__ */ jsxs("div", {
8164
- className: "dt-ses-menu",
8165
- ref: containerRef,
8166
- children: [/* @__PURE__ */ jsx("button", {
8167
- "aria-controls": menuId,
8168
- "aria-expanded": isOpen,
8169
- "aria-label": "Session options",
8170
- className: "icon-btn",
8171
- onClick: () => setIsOpen(!isOpen),
8172
- type: "button",
8173
- children: /* @__PURE__ */ jsx(MoreIcon, {})
8174
- }), isOpen && /* @__PURE__ */ jsxs("div", {
8175
- className: "dt-ses-menu-list",
8176
- id: menuId,
8177
- children: [
8178
- /* @__PURE__ */ jsx("button", {
8179
- className: "dt-ses-menu-item",
8180
- onClick: choose(actions.onRename),
8181
- type: "button",
8182
- children: "Rename"
8183
- }),
8184
- /* @__PURE__ */ jsx("button", {
8185
- className: "dt-ses-menu-item",
8186
- onClick: choose(actions.onToggleSharing),
8187
- type: "button",
8188
- children: isShared ? "Make private" : "Share with team"
8189
- }),
8190
- /* @__PURE__ */ jsx("button", {
8191
- className: "dt-ses-menu-item",
8192
- "data-variant": "danger",
8193
- onClick: choose(actions.onDelete),
8194
- type: "button",
8195
- children: "Delete session"
8196
- })
8197
- ]
8198
- })]
8199
- });
8200
- }
8201
- function ReviewHeader({ onBack, review, session }) {
7741
+ function ReviewHeader({ onDeleted, review, session }) {
8202
7742
  const [isRenaming, setIsRenaming] = useState(false);
8203
7743
  const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);
8204
7744
  const update = useSessionUpdate(review, session);
8205
7745
  const isShared = session.visibility === "team";
7746
+ const menuItems = [
7747
+ {
7748
+ label: "Rename",
7749
+ onSelect: () => setIsRenaming(true)
7750
+ },
7751
+ {
7752
+ label: isShared ? "Make private" : "Share with team",
7753
+ onSelect: () => update("The sharing was not changed", { visibility: isShared ? "private" : "team" })
7754
+ },
7755
+ {
7756
+ label: "Delete session",
7757
+ onSelect: () => setIsConfirmingDelete(true),
7758
+ variant: "danger"
7759
+ }
7760
+ ];
8206
7761
  return /* @__PURE__ */ jsxs("header", {
8207
7762
  className: "dt-ses-head",
8208
7763
  children: [
8209
- /* @__PURE__ */ jsx("button", {
8210
- className: "dt-ses-link",
8211
- onClick: onBack,
8212
- type: "button",
8213
- children: "← All sessions"
8214
- }),
8215
7764
  isRenaming ? /* @__PURE__ */ jsx(TitleEditor, {
8216
7765
  onDone: () => setIsRenaming(false),
8217
7766
  review,
@@ -8220,14 +7769,15 @@ function ReviewHeader({ onBack, review, session }) {
8220
7769
  className: "dt-card-head",
8221
7770
  children: [/* @__PURE__ */ jsx("h2", {
8222
7771
  className: "dt-ses-title",
7772
+ tabIndex: -1,
8223
7773
  children: session.title
8224
- }), session.canEdit && /* @__PURE__ */ jsx(SessionMenu, {
8225
- actions: {
8226
- onDelete: () => setIsConfirmingDelete(true),
8227
- onRename: () => setIsRenaming(true),
8228
- onToggleSharing: () => update("The sharing was not changed", { visibility: isShared ? "private" : "team" })
8229
- },
8230
- isShared
7774
+ }), session.canEdit && /* @__PURE__ */ jsx(Menu, {
7775
+ items: menuItems,
7776
+ trigger: {
7777
+ className: "icon-btn",
7778
+ content: /* @__PURE__ */ jsx(MoreIcon, {}),
7779
+ label: "Session options"
7780
+ }
8231
7781
  })]
8232
7782
  }),
8233
7783
  /* @__PURE__ */ jsxs("p", {
@@ -8244,13 +7794,83 @@ function ReviewHeader({ onBack, review, session }) {
8244
7794
  }),
8245
7795
  isConfirmingDelete && session.canEdit && /* @__PURE__ */ jsx(DeleteConfirmation, {
8246
7796
  onCancel: () => setIsConfirmingDelete(false),
8247
- onDeleted: onBack,
7797
+ onDeleted,
8248
7798
  sessionId: session.id
8249
7799
  })
8250
7800
  ]
8251
7801
  });
8252
7802
  }
8253
7803
  //#endregion
7804
+ //#region src/sessions/ui/review/review-tabs.tsx
7805
+ const INTENTION_VIEWS = [{
7806
+ label: "On screen",
7807
+ value: "screens"
7808
+ }, {
7809
+ label: "Details",
7810
+ value: "cards"
7811
+ }];
7812
+ function IntentionViewMenu({ place }) {
7813
+ return /* @__PURE__ */ jsx(Menu, {
7814
+ items: INTENTION_VIEWS.map((option) => ({
7815
+ checked: option.value === place.intentionView,
7816
+ label: option.label,
7817
+ onSelect: () => place.selectIntentionView(option.value)
7818
+ })),
7819
+ trigger: {
7820
+ className: "icon-btn",
7821
+ content: /* @__PURE__ */ jsx(ViewOptionsIcon, {}),
7822
+ label: "Show intentions"
7823
+ }
7824
+ });
7825
+ }
7826
+ function tabOptions(detail) {
7827
+ return [
7828
+ {
7829
+ count: detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null).length,
7830
+ icon: /* @__PURE__ */ jsx(IntentionIcon, {}),
7831
+ label: "Intentions",
7832
+ value: "proposals"
7833
+ },
7834
+ {
7835
+ count: null,
7836
+ icon: /* @__PURE__ */ jsx(TranscriptIcon, {}),
7837
+ label: "Transcript",
7838
+ value: "transcript"
7839
+ },
7840
+ {
7841
+ count: detail.evidence.length,
7842
+ icon: /* @__PURE__ */ jsx(ImageIcon, {}),
7843
+ label: "Evidence",
7844
+ value: "evidence"
7845
+ }
7846
+ ];
7847
+ }
7848
+ function ReviewTabs({ detail, place }) {
7849
+ const canPickView = place.tab === "proposals" && calloutReport(detail).screens.length > 0;
7850
+ return /* @__PURE__ */ jsxs("div", {
7851
+ className: "dt-toolbar",
7852
+ children: [/* @__PURE__ */ jsx("div", {
7853
+ className: "dt-tabs",
7854
+ role: "tablist",
7855
+ children: tabOptions(detail).map((option) => /* @__PURE__ */ jsxs("button", {
7856
+ "aria-selected": option.value === place.tab,
7857
+ className: "dt-tab",
7858
+ onClick: () => place.selectTab(option.value),
7859
+ role: "tab",
7860
+ type: "button",
7861
+ children: [
7862
+ option.icon,
7863
+ option.label,
7864
+ option.count !== null && /* @__PURE__ */ jsx("span", {
7865
+ className: "dt-tab-count",
7866
+ children: option.count
7867
+ })
7868
+ ]
7869
+ }, option.value))
7870
+ }), canPickView && /* @__PURE__ */ jsx(IntentionViewMenu, { place })]
7871
+ });
7872
+ }
7873
+ //#endregion
8254
7874
  //#region src/sessions/ui/review/status-strip.tsx
8255
7875
  function transcribingLine(detail) {
8256
7876
  const voice = detail.segments.filter((segment) => segment.source === "voice");
@@ -8995,67 +8615,21 @@ function TranscriptTab({ detail, onShowMoment, review }) {
8995
8615
  }
8996
8616
  //#endregion
8997
8617
  //#region src/sessions/ui/review/session-review.tsx
8998
- function HandoffFooter({ detail, onHandOff }) {
8999
- const confirmed = detail.proposals.filter((proposal) => proposal.decision === "kept" && proposal.mergedIntoId === null).length;
9000
- return /* @__PURE__ */ jsxs("div", {
9001
- className: "dt-sheet-foot",
9002
- children: [/* @__PURE__ */ jsx("button", {
9003
- "aria-describedby": confirmed === 0 ? "rf-handoff-hint" : void 0,
9004
- className: "dt-btn",
9005
- "data-variant": "primary",
9006
- disabled: confirmed === 0,
9007
- onClick: onHandOff,
9008
- type: "button",
9009
- children: confirmed === 0 ? "Hand off" : `Hand off ${confirmed} confirmed`
9010
- }), confirmed === 0 && /* @__PURE__ */ jsx("span", {
9011
- className: "dt-status",
9012
- id: "rf-handoff-hint",
9013
- children: "Confirm an intention to hand it off"
9014
- })]
9015
- });
9016
- }
9017
- function ReviewTabs({ detail, onSelect, tab }) {
9018
- const options = [
9019
- {
9020
- label: `Intentions (${detail.proposals.filter((proposal) => proposal.mergedIntoId === null && proposal.attachment === null).length})`,
9021
- value: "proposals"
9022
- },
9023
- {
9024
- label: "Transcript",
9025
- value: "transcript"
9026
- },
9027
- {
9028
- label: `Evidence (${detail.evidence.length})`,
9029
- value: "evidence"
9030
- }
9031
- ];
9032
- return /* @__PURE__ */ jsx("div", {
9033
- className: "dt-tabs dt-ses-tabs",
9034
- children: options.map((option) => /* @__PURE__ */ jsx("button", {
9035
- "aria-pressed": option.value === tab,
9036
- className: "dt-tab",
9037
- onClick: () => onSelect(option.value),
9038
- type: "button",
9039
- children: option.label
9040
- }, option.value))
9041
- });
9042
- }
9043
- function ReviewTabPanel({ context, place }) {
9044
- const { detail, navigation, review } = context;
9045
- if (place.tab === "proposals") return /* @__PURE__ */ jsx(ProposalsTab, {
9046
- context,
9047
- onSelectView: place.selectIntentionView,
9048
- view: place.intentionView
9049
- });
9050
- if (place.tab === "transcript") return /* @__PURE__ */ jsx(TranscriptTab, {
9051
- detail,
9052
- onShowMoment: navigation.onShowMoment,
9053
- review
9054
- });
9055
- return /* @__PURE__ */ jsx(EvidenceTab, {
9056
- detail,
9057
- onShowMoment: navigation.onShowMoment,
9058
- review
8618
+ function ReviewTabPanel({ context, place }) {
8619
+ const { detail, navigation, review } = context;
8620
+ if (place.tab === "proposals") return /* @__PURE__ */ jsx(ProposalsTab, {
8621
+ context,
8622
+ view: place.intentionView
8623
+ });
8624
+ if (place.tab === "transcript") return /* @__PURE__ */ jsx(TranscriptTab, {
8625
+ detail,
8626
+ onShowMoment: navigation.onShowMoment,
8627
+ review
8628
+ });
8629
+ return /* @__PURE__ */ jsx(EvidenceTab, {
8630
+ detail,
8631
+ onShowMoment: navigation.onShowMoment,
8632
+ review
9059
8633
  });
9060
8634
  }
9061
8635
  function useReviewPlace() {
@@ -9103,8 +8677,7 @@ function ReviewPanels({ context, place }) {
9103
8677
  return /* @__PURE__ */ jsxs(Fragment, { children: [
9104
8678
  /* @__PURE__ */ jsx(ReviewTabs, {
9105
8679
  detail,
9106
- onSelect: place.selectTab,
9107
- tab: place.tab
8680
+ place
9108
8681
  }),
9109
8682
  /* @__PURE__ */ jsx("div", {
9110
8683
  className: "dt-ses-panel",
@@ -9132,7 +8705,7 @@ function ReviewTop({ context, links }) {
9132
8705
  const { detail, review } = context;
9133
8706
  return /* @__PURE__ */ jsxs(Fragment, { children: [
9134
8707
  /* @__PURE__ */ jsx(ReviewHeader, {
9135
- onBack: links.onBack,
8708
+ onDeleted: links.onDeleted,
9136
8709
  review,
9137
8710
  session: detail.session
9138
8711
  }),
@@ -9146,9 +8719,8 @@ function ReviewTop({ context, links }) {
9146
8719
  })
9147
8720
  ] });
9148
8721
  }
9149
- function ReviewScreen({ detail, links, review }) {
8722
+ function ReviewScreen({ detail, isBriefOpen, links, review }) {
9150
8723
  const place = useReviewPlace();
9151
- const [isHandingOff, setIsHandingOff] = useState(false);
9152
8724
  const context = {
9153
8725
  actions: useProposalActions(review),
9154
8726
  canEdit: detail.session.canEdit,
@@ -9162,9 +8734,8 @@ function ReviewScreen({ detail, links, review }) {
9162
8734
  },
9163
8735
  review
9164
8736
  };
9165
- if (isHandingOff) return /* @__PURE__ */ jsx(HandoffView, {
8737
+ if (isBriefOpen) return /* @__PURE__ */ jsx(HandoffView, {
9166
8738
  detail,
9167
- onClose: () => setIsHandingOff(false),
9168
8739
  review
9169
8740
  });
9170
8741
  return /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -9176,13 +8747,13 @@ function ReviewScreen({ detail, links, review }) {
9176
8747
  context,
9177
8748
  place
9178
8749
  }),
9179
- context.canEdit && /* @__PURE__ */ jsx(HandoffFooter, {
9180
- detail,
9181
- onHandOff: () => setIsHandingOff(true)
8750
+ context.canEdit && /* @__PURE__ */ jsx(ReviewFooter, {
8751
+ context,
8752
+ onOpenBrief: links.onOpenBrief
9182
8753
  })
9183
8754
  ] });
9184
8755
  }
9185
- function SessionReview({ links, sessionId }) {
8756
+ function SessionReview({ isBriefOpen, links, sessionId }) {
9186
8757
  const handle = useSessionDetail(sessionId);
9187
8758
  const { state } = handle;
9188
8759
  if (state.kind === "loading") return /* @__PURE__ */ jsx("p", {
@@ -9191,24 +8762,22 @@ function SessionReview({ links, sessionId }) {
9191
8762
  });
9192
8763
  if (state.kind === "ready") return /* @__PURE__ */ jsx(ReviewScreen, {
9193
8764
  detail: state.detail,
8765
+ isBriefOpen,
9194
8766
  links,
9195
8767
  review: handle
9196
8768
  });
8769
+ if (state.kind === "missing") return /* @__PURE__ */ jsx("p", {
8770
+ className: "dt-empty",
8771
+ children: "This session is gone: it was deleted or is no longer shared with you."
8772
+ });
9197
8773
  return /* @__PURE__ */ jsxs("div", {
9198
8774
  className: "dt-empty",
9199
- children: [/* @__PURE__ */ jsx("p", { children: state.kind === "missing" ? "This session is gone: it was deleted or is no longer shared with you." : `The session did not load: ${state.message}` }), /* @__PURE__ */ jsxs("div", {
9200
- className: "dt-actions",
9201
- children: [state.kind === "failed" && /* @__PURE__ */ jsx("button", {
9202
- className: "dt-btn",
9203
- onClick: handle.reload,
9204
- type: "button",
9205
- children: "Try again"
9206
- }), /* @__PURE__ */ jsx("button", {
9207
- className: "dt-btn",
9208
- onClick: links.onBack,
9209
- type: "button",
9210
- children: "All sessions"
9211
- })]
8775
+ role: "alert",
8776
+ children: [/* @__PURE__ */ jsxs("p", { children: ["The session did not load: ", state.message] }), /* @__PURE__ */ jsx("button", {
8777
+ className: "dt-btn",
8778
+ onClick: handle.reload,
8779
+ type: "button",
8780
+ children: "Try again"
9212
8781
  })]
9213
8782
  });
9214
8783
  }
@@ -9335,7 +8904,7 @@ function SessionList({ drafts, onOpen }) {
9335
8904
  interruptedIds,
9336
8905
  onOpen,
9337
8906
  sessions: mine,
9338
- title: "Mine"
8907
+ title: "Your sessions"
9339
8908
  }),
9340
8909
  /* @__PURE__ */ jsx(SessionGroup, {
9341
8910
  interruptedIds,
@@ -9398,17 +8967,7 @@ function SessionsHome({ onOpenSession, onStarted, onWriteNote }) {
9398
8967
  const { account } = useSessionWorkspace();
9399
8968
  const drafts = useLocalDrafts(account.key);
9400
8969
  const interrupted = drafts.filter((draft) => draft.state === "interrupted");
9401
- const name = account.userName ?? account.userEmail ?? "you";
9402
8970
  return /* @__PURE__ */ jsxs(Fragment, { children: [
9403
- /* @__PURE__ */ jsxs("p", {
9404
- className: "dt-meta",
9405
- children: [
9406
- "Recording as ",
9407
- name,
9408
- " · ",
9409
- account.organizationName
9410
- ]
9411
- }),
9412
8971
  interrupted.map((draft) => /* @__PURE__ */ jsx(InterruptedDraft, {
9413
8972
  draft,
9414
8973
  onOpenSession,
@@ -9426,6 +8985,32 @@ function SessionsHome({ onOpenSession, onStarted, onWriteNote }) {
9426
8985
  }
9427
8986
  //#endregion
9428
8987
  //#region src/sessions/ui/sessions-panel.tsx
8988
+ const SESSIONS_HOME = { view: "home" };
8989
+ function useSessionNavigation() {
8990
+ const [place, setPlace] = useState(SESSIONS_HOME);
8991
+ return {
8992
+ go: setPlace,
8993
+ place
8994
+ };
8995
+ }
8996
+ /** Where the panel header's back button leads; null on the sessions home. */
8997
+ function sessionSheetPlace({ go, place }) {
8998
+ if (place.view === "home") return null;
8999
+ const sessionRef = place.target.sessionId ?? place.target.sessionKey ?? "";
9000
+ if (place.view === "review") return {
9001
+ backLabel: "Sessions",
9002
+ id: `review:${sessionRef}`,
9003
+ onBack: () => go(SESSIONS_HOME)
9004
+ };
9005
+ return {
9006
+ backLabel: "Session",
9007
+ id: `brief:${sessionRef}`,
9008
+ onBack: () => go({
9009
+ target: place.target,
9010
+ view: "review"
9011
+ })
9012
+ };
9013
+ }
9429
9014
  function useResolvedSessionId(target) {
9430
9015
  const { account } = useSessionWorkspace();
9431
9016
  const drafts = useLocalDrafts(account.key);
@@ -9434,36 +9019,822 @@ function useResolvedSessionId(target) {
9434
9019
  }
9435
9020
  /** A just-finished session may not have reached Reflet yet: it opens once its id is known. */
9436
9021
  function SessionsPanel({ navigation, onStarted, onWriteNote }) {
9437
- const sessionId = useResolvedSessionId(navigation.open);
9438
- const back = () => navigation.onOpen(null);
9439
- if (sessionId) return /* @__PURE__ */ jsx(SessionReview, {
9022
+ const { go, place } = navigation;
9023
+ const target = place.view === "home" ? null : place.target;
9024
+ const sessionId = useResolvedSessionId(target);
9025
+ if (!target) return /* @__PURE__ */ jsx(SessionsHome, {
9026
+ onOpenSession: (opened) => go({
9027
+ target: opened,
9028
+ view: "review"
9029
+ }),
9030
+ onStarted,
9031
+ onWriteNote
9032
+ });
9033
+ if (!sessionId) return /* @__PURE__ */ jsx("p", {
9034
+ className: "dt-empty",
9035
+ role: "status",
9036
+ children: "This session is still uploading. It opens here once it reaches Reflet."
9037
+ });
9038
+ return /* @__PURE__ */ jsx(SessionReview, {
9039
+ isBriefOpen: place.view === "brief",
9440
9040
  links: {
9441
- onBack: back,
9442
- onOpenSession: (openedId) => navigation.onOpen({
9443
- sessionId: openedId,
9444
- sessionKey: null
9041
+ onDeleted: () => go(SESSIONS_HOME),
9042
+ onOpenBrief: () => go({
9043
+ target,
9044
+ view: "brief"
9045
+ }),
9046
+ onOpenSession: (openedId) => go({
9047
+ target: {
9048
+ sessionId: openedId,
9049
+ sessionKey: null
9050
+ },
9051
+ view: "review"
9445
9052
  }),
9446
9053
  onStarted
9447
9054
  },
9448
9055
  sessionId
9449
9056
  }, sessionId);
9450
- if (navigation.open) return /* @__PURE__ */ jsxs("div", {
9451
- className: "dt-empty",
9452
- role: "status",
9453
- children: [/* @__PURE__ */ jsx("p", { children: "This session is still uploading. It opens here once it reaches Reflet." }), /* @__PURE__ */ jsx("button", {
9454
- className: "dt-btn",
9455
- onClick: back,
9456
- type: "button",
9457
- children: "All sessions"
9057
+ }
9058
+ //#endregion
9059
+ //#region src/sessions/ui/intention-styles.ts
9060
+ const INTENTION_STYLES = `
9061
+ .dt-ses-panel { display: contents; }
9062
+ .dt-ses-panel[hidden] { display: none; }
9063
+ .dt-ses-moment, .dt-ses-moment-section, .dt-ses-block, .dt-ses-change, .dt-ses-history { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
9064
+ .dt-ses-moment-item { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); }
9065
+ .dt-ses-moment-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0; overflow-wrap: anywhere; }
9066
+ .dt-ses-moment-quote { display: flex; flex-direction: column; gap: 2px; margin: 0; padding-left: 10px; border-left: 2px solid var(--rf-border); overflow-wrap: anywhere; }
9067
+ .dt-ses-moment-notices { color: var(--rf-text-muted); font-size: 12px; }
9068
+ .dt-ses-moment-shot { display: flex; flex-direction: column; gap: 4px; margin: 0; max-width: 100%; }
9069
+ .dt-ses-shot-mark[data-match="approximate"] { border-style: dashed; }
9070
+ .dt-ses-shot-pointer {
9071
+ position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px; border-radius: 50%;
9072
+ background: var(--rf-accent); box-shadow: 0 0 0 2px var(--rf-bg); pointer-events: none;
9073
+ }
9074
+ .dt-ses-source { margin: 0; font-size: 12px; overflow-wrap: anywhere; }
9075
+ .dt-ses-context-line { display: block; }
9076
+ .dt-ses-attached { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px; }
9077
+ .dt-ses-attached p { margin: 0; }
9078
+ .dt-ses-history[data-removed="true"] > .dt-ses-subhead { color: var(--rf-text-muted); text-decoration: line-through; }
9079
+ .dt-ses-change { padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); list-style: none; }
9080
+ .dt-ses-change[data-decision="rejected"] .dt-ses-diff { color: var(--rf-text-muted); text-decoration: line-through; }
9081
+ .dt-ses-diff { display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; margin: 0; overflow-wrap: anywhere; }
9082
+ p.dt-ses-diff { display: block; }
9083
+ .dt-ses-diff dd { margin: 0; }
9084
+ .dt-card:focus-visible { outline: 2px solid var(--rf-accent); outline-offset: 2px; }
9085
+ `;
9086
+ //#endregion
9087
+ //#region src/sessions/ui/review/screens/report-styles.ts
9088
+ const REPORT_STYLES = `
9089
+ .dt-ses-report { display: flex; flex-direction: column; gap: 4px; width: 100%; min-width: 0; margin: 0; }
9090
+ .dt-ses-report-canvas { position: relative; width: 100%; }
9091
+ .dt-ses-report-canvas img { display: block; width: 100%; max-width: 100%; height: auto; border-radius: 8px; box-shadow: var(--rf-shadow-inset); }
9092
+ .dt-ses-report-canvas[data-measured="false"] .dt-ses-callout { visibility: hidden; }
9093
+ .dt-ses-report-mark { --dt-mark: var(--rf-accent); position: absolute; border: 2px solid var(--dt-mark); border-radius: 4px; pointer-events: none; }
9094
+ .dt-ses-report-lines { position: absolute; inset: 0; overflow: visible; pointer-events: none; }
9095
+ .dt-ses-report-lines g { color: var(--rf-accent); }
9096
+ .dt-ses-report-lines line { stroke: currentColor; stroke-width: 1.5; }
9097
+ .dt-ses-report-lines circle { fill: currentColor; }
9098
+ .dt-ses-report-mark[data-decision="kept"], .dt-ses-report-lines g[data-decision="kept"] { --dt-mark: var(--rf-success); color: var(--rf-success); }
9099
+ .dt-ses-report-mark[data-decision="dismissed"], .dt-ses-report-lines g[data-decision="dismissed"] { opacity: .35; }
9100
+ .dt-ses-callouts { margin: 0; padding: 0; list-style: none; }
9101
+ .dt-ses-callouts[data-flow="list"] { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
9102
+ .dt-ses-callout {
9103
+ display: flex; align-items: center; gap: 4px; max-width: 100%; padding: 3px; border-radius: var(--rf-pill);
9104
+ background: var(--rf-bg); box-shadow: var(--rf-shadow-inset), var(--rf-shadow-low); font-size: 12px; line-height: 1.3;
9105
+ }
9106
+ .dt-ses-report-canvas .dt-ses-callout { position: absolute; top: 0; left: 0; width: max-content; max-width: min(280px, 100%); }
9107
+ .dt-ses-callout-number {
9108
+ display: grid; place-items: center; flex: none; min-width: 20px; height: 20px; padding: 0 5px; border-radius: var(--rf-pill);
9109
+ background: var(--rf-accent); color: var(--rf-accent-text, #fff); font-size: 11px; font-weight: 650; font-variant-numeric: tabular-nums;
9110
+ }
9111
+ .dt-ses-callout[data-decision="kept"] .dt-ses-callout-number { background: var(--rf-success); }
9112
+ .dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-number { background: var(--rf-text-muted); }
9113
+ .dt-ses-callout-label { min-width: 0; padding: 0 4px; font-weight: 550; text-align: left; overflow-wrap: anywhere; }
9114
+ .dt-ses-callout-label:hover { color: var(--rf-accent); }
9115
+ .dt-ses-callout[data-decision="dismissed"] .dt-ses-callout-label { color: var(--rf-text-muted); text-decoration: line-through; }
9116
+ .dt-ses-callout-btn {
9117
+ display: grid; place-items: center; flex: none; width: 22px; height: 22px; border-radius: 50%; color: var(--rf-text-muted);
9118
+ transition: background-color 120ms var(--rf-ease), color 120ms var(--rf-ease);
9119
+ }
9120
+ .dt-ses-callout-btn svg { width: 13px; height: 13px; }
9121
+ .dt-ses-callout-btn:hover { background: var(--rf-bg-hover); color: var(--rf-text); }
9122
+ .dt-ses-callout-btn[data-decision="kept"][aria-pressed="true"] { background: var(--rf-success); color: #fff; }
9123
+ .dt-ses-callout-btn[data-decision="dismissed"][aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
9124
+ `;
9125
+ //#endregion
9126
+ //#region src/sessions/ui/sessions-styles.ts
9127
+ const MONO_FONT$1 = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
9128
+ const SESSIONS_STYLES = `${`
9129
+ .dt-rec {
9130
+ position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar}; left: 50%; translate: -50% 0;
9131
+ bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + 16px);
9132
+ display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px;
9133
+ max-width: calc(100vw - 24px); padding: 6px 6px 6px 12px; border-radius: var(--dt-radius-surface);
9134
+ animation: dt-rec-in 240ms var(--rf-ease);
9135
+ }
9136
+ @keyframes dt-rec-in { from { transform: translateY(8px); opacity: 0; } }
9137
+ .dt-rec-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; background: var(--rf-danger); }
9138
+ .dt-rec[data-phase="recording"] .dt-rec-dot { animation: dt-rec-pulse 1.6s ease-in-out infinite; }
9139
+ .dt-rec[data-phase="paused"] .dt-rec-dot, .dt-rec[data-phase="finishing"] .dt-rec-dot { background: var(--rf-text-muted); }
9140
+ @keyframes dt-rec-pulse { 50% { opacity: .35; } }
9141
+ .dt-rec-meter { display: inline-flex; align-items: center; gap: 2px; height: 16px; }
9142
+ .dt-rec-meter span {
9143
+ width: 3px; height: 100%; border-radius: 2px; background: var(--rf-text-muted);
9144
+ scale: 1 calc(.2 + var(--dt-level, 0) * .8); transition: scale 90ms linear;
9145
+ }
9146
+ .dt-rec-meter span:nth-child(2) { scale: 1 calc(.2 + var(--dt-level, 0)); }
9147
+ .dt-rec-time { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 5ch; }
9148
+ .dt-rec-status { color: var(--rf-text-muted); font-size: 12px; overflow-wrap: anywhere; }
9149
+ .dt-rec-status:empty { display: none; }
9150
+ .dt-rec-note { display: flex; align-items: center; gap: 6px; flex: 1 1 100%; font-size: 12px; color: var(--rf-text-muted); }
9151
+ .dt-rec-note .dt-field { flex: 1; min-width: 0; padding: 4px 8px; }
9152
+ `}
9153
+ .dt-ses-subhead, .dt-ses-label { margin: 0 0 4px; font-size: 12px; font-weight: 600; color: var(--rf-text); }
9154
+ .dt-ses-label { font-size: 11px; color: var(--rf-text-muted); }
9155
+ .dt-ses-list { margin: 0; padding-left: 18px; overflow-wrap: anywhere; }
9156
+ .dt-ses-list li + li { margin-top: 3px; }
9157
+ .dt-ses-link { align-self: flex-start; padding: 0; color: var(--rf-accent); font-size: 12px; text-align: left; }
9158
+ .dt-ses-link:hover { text-decoration: underline; }
9159
+ .dt-ses-problem, .dt-ses-banner, .dt-ses-form, .dt-ses-head, .dt-ses-strip, .dt-ses-handoff { display: flex; flex-direction: column; gap: 6px; }
9160
+ .dt-ses-banner { padding: 8px 10px; border-radius: var(--dt-radius-card); background: color-mix(in srgb, var(--rf-accent) 10%, transparent); }
9161
+ .dt-ses-banner p { margin: 0; overflow-wrap: anywhere; }
9162
+ .dt-ses-strip:empty { display: none; }
9163
+ .dt-ses-rows { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
9164
+ .dt-ses-row {
9165
+ display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%;
9166
+ padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); text-align: left;
9167
+ }
9168
+ button.dt-ses-row:hover { background: var(--rf-bg-hover); }
9169
+ .dt-ses-row .dt-card-title { max-width: 100%; }
9170
+ ${`
9171
+ .dt-ses-title { flex: 1; min-width: 0; margin: 0; font-size: 15px; font-weight: 650; overflow-wrap: anywhere; }
9172
+ .dt-ses-head .dt-menu > .icon-btn { width: 30px; height: 30px; }
9173
+ .dt-btn[data-icon-only] { justify-content: center; width: 28px; padding: 0; }
9174
+ .dt-ses-field { display: flex; flex-direction: column; gap: 3px; font-size: 11.5px; color: var(--rf-text-muted); }
9175
+ .dt-ses-need {
9176
+ margin: 0; font-weight: 600; overflow-wrap: anywhere;
9177
+ display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
9178
+ }
9179
+ .dt-ses-need[data-expanded="true"] { display: block; }
9180
+ .dt-card[data-decision="dismissed"] .dt-ses-need { color: var(--rf-text-muted); text-decoration: line-through; }
9181
+ .dt-ses-chips { display: flex; flex-wrap: wrap; gap: 4px; }
9182
+ .dt-ses-chips:empty { display: none; }
9183
+ .dt-ses-details { display: flex; flex-direction: column; gap: 8px; }
9184
+ .dt-ses-details[hidden] { display: none; }
9185
+ .dt-ses-details section { display: flex; flex-direction: column; gap: 2px; }
9186
+ .dt-ses-quote { padding: 2px 0; font-size: 12px; text-align: left; overflow-wrap: anywhere; }
9187
+ .dt-ses-quote:hover { color: var(--rf-accent); }
9188
+ .dt-ses-time { flex: none; color: var(--rf-text-muted); font: 11.5px ${MONO_FONT$1}; font-variant-numeric: tabular-nums; }
9189
+ .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); }
9190
+ .dt-ses-timeline { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
9191
+ .dt-ses-seg { display: flex; gap: 8px; padding: 6px 8px; border-radius: var(--dt-radius-field); }
9192
+ .dt-ses-seg:focus { outline: 2px solid var(--rf-accent); outline-offset: -2px; }
9193
+ .dt-ses-seg[data-kind="note"] { padding-block: 2px; }
9194
+ .dt-ses-seg-body { display: flex; flex: 1; flex-direction: column; gap: 4px; min-width: 0; }
9195
+ `}${`
9196
+ .dt-ses-thumbs { display: flex; flex-wrap: wrap; gap: 6px; }
9197
+ .dt-ses-shot { position: relative; display: inline-block; max-width: 100%; line-height: 0; }
9198
+ .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); }
9199
+ .dt-ses-shot[data-large="true"] img { max-height: none; }
9200
+ .dt-ses-shot-mark { position: absolute; border: 2px solid var(--rf-accent); border-radius: var(--dt-radius-tiny); pointer-events: none; }
9201
+ .dt-ses-shot-btn { align-self: flex-start; padding: 0; border-radius: var(--dt-radius-field); }
9202
+ .dt-ses-start { display: flex; flex-direction: column; gap: 8px; }
9203
+ .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; }
9204
+ .dt-ses-brief-entry > summary::-webkit-details-marker { display: none; }
9205
+ .dt-ses-brief-entry > summary:hover { background: var(--rf-bg-hover); }
9206
+ .dt-ses-brief-entry[open] > summary { margin-bottom: 6px; }
9207
+ .dt-ses-check { display: flex; align-items: flex-start; gap: 6px; overflow-wrap: anywhere; }
9208
+ .dt-ses-check input { flex: none; margin-top: 3px; accent-color: var(--rf-accent); }
9209
+ .dt-ses-choices { margin: 0; padding: 0; border: 0; }
9210
+ .dt-ses-choices .dt-ses-list { padding-left: 0; list-style: none; }
9211
+ .dt-ses-brief {
9212
+ max-height: 50vh; overflow: auto; margin: 0; padding: 10px; border-radius: var(--dt-radius-card);
9213
+ background: var(--rf-bg-subtle); font: 11.5px/1.55 ${MONO_FONT$1}; white-space: pre-wrap; overflow-wrap: anywhere;
9214
+ }
9215
+ `}${`
9216
+ .dt-team-anchor {
9217
+ --dt-edge-gap: calc(max(var(--rf-offset), 12px) + 48px);
9218
+ position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar}; width: min(300px, calc(100vw - 24px));
9219
+ }
9220
+ .dt-team-anchor[data-side="left"] { left: calc(var(--rf-viewport-left, 0px) + max(12px, var(--rf-offset))); }
9221
+ .dt-team-anchor[data-side="right"] { right: calc(var(--rf-viewport-right, 0px) + max(12px, var(--rf-offset))); }
9222
+ .dt-team-anchor[data-edge="bottom"] { bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + var(--dt-edge-gap)); }
9223
+ .dt-team-anchor[data-edge="top"] { top: calc(var(--rf-viewport-top, 0px) + env(safe-area-inset-top) + var(--dt-edge-gap)); }
9224
+ .dt-team-card { display: flex; flex-direction: column; gap: 6px; padding: 12px 14px; border-radius: var(--dt-radius-surface); animation: dt-popover-in 200ms var(--rf-ease); }
9225
+ .dt-bar > .dt-btn { margin-inline: 2px 4px; }
9226
+ `}${INTENTION_STYLES}${REPORT_STYLES}`;
9227
+ //#endregion
9228
+ //#region src/devtools/client/account/account-footer.tsx
9229
+ const WHITESPACE = /\s+/;
9230
+ function initials(text) {
9231
+ return text.split(WHITESPACE).filter(Boolean).slice(0, 2).map((word) => word.charAt(0).toUpperCase()).join("");
9232
+ }
9233
+ function Avatar({ image, name }) {
9234
+ return /* @__PURE__ */ jsxs("span", {
9235
+ "aria-hidden": "true",
9236
+ className: "dt-avatar",
9237
+ children: [initials(name), image && /* @__PURE__ */ jsx("img", {
9238
+ alt: "",
9239
+ height: 28,
9240
+ referrerPolicy: "no-referrer",
9241
+ src: image,
9242
+ width: 28
9458
9243
  })]
9459
9244
  });
9460
- return /* @__PURE__ */ jsx(SessionsHome, {
9461
- onOpenSession: navigation.onOpen,
9462
- onStarted,
9463
- onWriteNote
9245
+ }
9246
+ function AccountFooter({ actions, organizationName, person }) {
9247
+ const name = person?.name ?? person?.email ?? null;
9248
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
9249
+ /* @__PURE__ */ jsx(Avatar, {
9250
+ image: person?.image ?? null,
9251
+ name: name ?? organizationName
9252
+ }),
9253
+ /* @__PURE__ */ jsxs("span", {
9254
+ className: "dt-account-text",
9255
+ children: [/* @__PURE__ */ jsx("span", {
9256
+ className: "dt-account-name",
9257
+ children: name ?? organizationName
9258
+ }), name && /* @__PURE__ */ jsx("span", {
9259
+ className: "dt-meta",
9260
+ children: organizationName
9261
+ })]
9262
+ }),
9263
+ /* @__PURE__ */ jsx(ChevronUpDownIcon, {})
9264
+ ] });
9265
+ return /* @__PURE__ */ jsx("div", {
9266
+ className: "dt-sheet-account",
9267
+ children: /* @__PURE__ */ jsx(Menu, {
9268
+ items: actions,
9269
+ placement: "above",
9270
+ trigger: {
9271
+ className: "dt-account",
9272
+ content,
9273
+ label: name ? `${name}, ${organizationName}` : organizationName
9274
+ }
9275
+ })
9276
+ });
9277
+ }
9278
+ function AccountNote({ children }) {
9279
+ return /* @__PURE__ */ jsx("div", {
9280
+ className: "dt-sheet-account",
9281
+ children: /* @__PURE__ */ jsx("p", {
9282
+ className: "dt-meta",
9283
+ children
9284
+ })
9464
9285
  });
9465
9286
  }
9466
9287
  //#endregion
9467
- export { Notices as A, searchCode as C, openDatabase as D, describeStorageFailure as E, reportFailure as M, settle as O, probeDevRoute as S, committed as T, teamTransport as _, DevtoolsBar as a, fetchBoardFeedback as b, copyToClipboard as c, PersonIcon as d, SessionWorkspaceProvider as f, devtoolsTransport as g, purgeAccount as h, BarButton as i, announce as j, sessionApiErrorSchema as k, SESSIONS_STYLES as l, useWorkspaceLoader as m, DEVTOOLS_STYLES as n, DevtoolsBarButtons as o, failureMessage as p, DevtoolsSheet as r, SessionsBarButton as s, SessionsPanel as t, RecordingLayer as u, callReflet as v, startBoardConnect as w, fetchSourceFile as x, disconnectBoard as y };
9288
+ //#region src/devtools/client/devtools-bar.tsx
9289
+ const BAR_POSITION_KEY = "reflet-devtools-bar-position";
9290
+ const PICK_BUTTON_LABEL = "Pick an element: note it, copy it, or Shift+click to open its code";
9291
+ const SHEET_TABS = [
9292
+ "inbox",
9293
+ "notes",
9294
+ "sessions",
9295
+ "code"
9296
+ ];
9297
+ function BarButton({ children, label, onClick }) {
9298
+ return /* @__PURE__ */ jsx("button", {
9299
+ "aria-label": label,
9300
+ className: "icon-btn",
9301
+ onClick,
9302
+ title: label,
9303
+ type: "button",
9304
+ children
9305
+ });
9306
+ }
9307
+ function DevtoolsBar({ children, edge, label = "Reflet devtools", side }) {
9308
+ const floating = useFloatingPosition(`${edge}-${side}`, false, {
9309
+ anchorSelector: ".dt-bar",
9310
+ persistKey: BAR_POSITION_KEY
9311
+ });
9312
+ return /* @__PURE__ */ jsxs("div", {
9313
+ "aria-label": label,
9314
+ className: "dt-bar glass",
9315
+ "data-edge": edge,
9316
+ "data-moved": floating.position !== null,
9317
+ "data-side": side,
9318
+ ref: floating.rootRef,
9319
+ role: "toolbar",
9320
+ children: [
9321
+ /* @__PURE__ */ jsx("style", { children: floating.styles }),
9322
+ /* @__PURE__ */ jsx("button", {
9323
+ "aria-label": `Move ${label} (double-click to reset)`,
9324
+ className: "icon-btn drag-handle dt-grip",
9325
+ onDoubleClick: floating.resetPosition,
9326
+ title: "Drag to move · double-click to reset",
9327
+ type: "button",
9328
+ ...floating.handleProps,
9329
+ children: /* @__PURE__ */ jsx(GripIcon, {})
9330
+ }),
9331
+ children
9332
+ ]
9333
+ });
9334
+ }
9335
+ function SessionsBarButton({ onOpen }) {
9336
+ return /* @__PURE__ */ jsx(BarButton, {
9337
+ label: "Sessions",
9338
+ onClick: onOpen,
9339
+ children: /* @__PURE__ */ jsx(MicIcon, {})
9340
+ });
9341
+ }
9342
+ function panelLabel(unsentNoteCount) {
9343
+ if (unsentNoteCount === 0) return "Open the Reflet panel";
9344
+ return `Open the Reflet panel (${unsentNoteCount} ${unsentNoteCount === 1 ? "note" : "notes"} not on the board)`;
9345
+ }
9346
+ function DevtoolsBarButtons({ onOpen, onPick, unsentNoteCount }) {
9347
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
9348
+ /* @__PURE__ */ jsx(BarButton, {
9349
+ label: PICK_BUTTON_LABEL,
9350
+ onClick: onPick,
9351
+ children: /* @__PURE__ */ jsx(TargetIcon, {})
9352
+ }),
9353
+ /* @__PURE__ */ jsx(SessionsBarButton, { onOpen: () => onOpen("sessions") }),
9354
+ /* @__PURE__ */ jsxs(BarButton, {
9355
+ label: panelLabel(unsentNoteCount),
9356
+ onClick: () => onOpen(null),
9357
+ children: [/* @__PURE__ */ jsx(PanelIcon, {}), unsentNoteCount > 0 && /* @__PURE__ */ jsx("span", {
9358
+ className: "dt-count",
9359
+ children: unsentNoteCount
9360
+ })]
9361
+ })
9362
+ ] });
9363
+ }
9364
+ //#endregion
9365
+ //#region src/devtools/client/use-sheet-size.ts
9366
+ const SIZE_KEY = "reflet-devtools-sheet-size";
9367
+ const DEFAULT_WIDTH = 480;
9368
+ const MIN_WIDTH = 320;
9369
+ const MIN_HEIGHT = 240;
9370
+ const KEYBOARD_STEP = 24;
9371
+ const ARROW_DELTAS = {
9372
+ ArrowDown: {
9373
+ dx: 0,
9374
+ dy: KEYBOARD_STEP
9375
+ },
9376
+ ArrowLeft: {
9377
+ dx: -24,
9378
+ dy: 0
9379
+ },
9380
+ ArrowRight: {
9381
+ dx: KEYBOARD_STEP,
9382
+ dy: 0
9383
+ },
9384
+ ArrowUp: {
9385
+ dx: 0,
9386
+ dy: -24
9387
+ }
9388
+ };
9389
+ function readStoredSize() {
9390
+ const fallback = {
9391
+ height: null,
9392
+ width: DEFAULT_WIDTH
9393
+ };
9394
+ try {
9395
+ const stored = JSON.parse(localStorage.getItem(SIZE_KEY) ?? "null");
9396
+ if (typeof stored === "object" && stored !== null && "width" in stored && typeof stored.width === "number" && "height" in stored && (stored.height === null || typeof stored.height === "number")) return {
9397
+ height: stored.height,
9398
+ width: stored.width
9399
+ };
9400
+ } catch {
9401
+ return fallback;
9402
+ }
9403
+ return fallback;
9404
+ }
9405
+ /**
9406
+ * The stored size is the preference; what renders is that preference fitted
9407
+ * to today's window, so shrinking the window never loses the chosen size.
9408
+ */
9409
+ function fitted(size, viewport) {
9410
+ const maxWidth = viewport.width - 24;
9411
+ const maxHeight = viewport.height - 24;
9412
+ return {
9413
+ height: size.height === null || size.height >= maxHeight ? null : Math.max(Math.min(MIN_HEIGHT, maxHeight), size.height),
9414
+ width: Math.min(maxWidth, Math.max(MIN_WIDTH, size.width))
9415
+ };
9416
+ }
9417
+ /**
9418
+ * Width grows away from the docked side, height grows downward from the top;
9419
+ * a height reaching the bottom of the window snaps back to full height.
9420
+ */
9421
+ function useSheetSize(side) {
9422
+ const [preference, setPreference] = useState(readStoredSize);
9423
+ const [viewport, setViewport] = useState(visibleViewport);
9424
+ const drag = useRef(null);
9425
+ const widthDirection = side === "right" ? -1 : 1;
9426
+ const size = fitted(preference, viewport);
9427
+ useEffect(() => onViewportChange(() => setViewport(visibleViewport())), []);
9428
+ useEffect(() => {
9429
+ try {
9430
+ localStorage.setItem(SIZE_KEY, JSON.stringify(preference));
9431
+ } catch {}
9432
+ }, [preference]);
9433
+ const resize = (axis, from, dx, dy) => {
9434
+ const fullHeight = viewport.height - 24;
9435
+ setPreference(fitted({
9436
+ height: axis === "width" ? from.height : (from.height ?? fullHeight) + dy,
9437
+ width: axis === "height" ? from.width : from.width + dx * widthDirection
9438
+ }, viewport));
9439
+ };
9440
+ const handleProps = (axis) => ({
9441
+ onDoubleClick: () => setPreference((current) => ({
9442
+ height: axis === "width" ? current.height : null,
9443
+ width: axis === "height" ? current.width : DEFAULT_WIDTH
9444
+ })),
9445
+ onKeyDown: (event) => {
9446
+ const delta = ARROW_DELTAS[event.key];
9447
+ if (!delta) return;
9448
+ event.preventDefault();
9449
+ const sheetHeight = event.currentTarget.parentElement?.getBoundingClientRect().height ?? null;
9450
+ resize(axis, {
9451
+ height: size.height ?? sheetHeight,
9452
+ width: size.width
9453
+ }, delta.dx, delta.dy);
9454
+ },
9455
+ onLostPointerCapture: () => {
9456
+ drag.current = null;
9457
+ },
9458
+ onPointerDown: (event) => {
9459
+ if (event.button !== 0) return;
9460
+ const sheet = event.currentTarget.parentElement?.getBoundingClientRect();
9461
+ event.currentTarget.setPointerCapture(event.pointerId);
9462
+ drag.current = {
9463
+ axis,
9464
+ size: {
9465
+ height: sheet?.height ?? size.height,
9466
+ width: size.width
9467
+ },
9468
+ x: event.clientX,
9469
+ y: event.clientY
9470
+ };
9471
+ },
9472
+ onPointerMove: (event) => {
9473
+ const start = drag.current;
9474
+ if (start && event.currentTarget.hasPointerCapture(event.pointerId)) resize(start.axis, start.size, event.clientX - start.x, event.clientY - start.y);
9475
+ }
9476
+ });
9477
+ return {
9478
+ handleProps,
9479
+ size
9480
+ };
9481
+ }
9482
+ //#endregion
9483
+ //#region src/devtools/client/devtools-sheet.tsx
9484
+ const FULL_HEIGHT = "calc(100dvh - 24px)";
9485
+ const TABS = {
9486
+ code: {
9487
+ icon: /* @__PURE__ */ jsx(CodeIcon, {}),
9488
+ label: "Code"
9489
+ },
9490
+ inbox: {
9491
+ icon: /* @__PURE__ */ jsx(InboxIcon, {}),
9492
+ label: "Board"
9493
+ },
9494
+ notes: {
9495
+ icon: /* @__PURE__ */ jsx(NotesIcon, {}),
9496
+ label: "Notes"
9497
+ },
9498
+ sessions: {
9499
+ icon: /* @__PURE__ */ jsx(MicIcon, {}),
9500
+ label: "Sessions"
9501
+ }
9502
+ };
9503
+ const SHEET_POSITION_KEY = "reflet-devtools-sheet-position";
9504
+ function useSheetFocus(rootRef) {
9505
+ useLayoutEffect(() => {
9506
+ const root = rootRef.current;
9507
+ const opener = root ? focusedElementAround(root) : null;
9508
+ if (opener instanceof HTMLElement && typeof opener.checkVisibility === "function" && !opener.checkVisibility({ visibilityProperty: true })) root?.querySelector("[role=\"tab\"][aria-selected=\"true\"], .dt-sheet-back, .dt-sheet-title")?.focus({ preventScroll: true });
9509
+ return () => {
9510
+ queueMicrotask(() => {
9511
+ if (opener instanceof HTMLElement && opener.isConnected) opener.focus({ preventScroll: true });
9512
+ });
9513
+ };
9514
+ }, [rootRef]);
9515
+ }
9516
+ const ROOT_FOCUS_SELECTOR = "[role=\"tab\"][aria-selected=\"true\"], .dt-sheet-title";
9517
+ function nestedViewStart(root) {
9518
+ return root.querySelector(".dt-sheet-body h2") ?? root.querySelector(".dt-sheet-body");
9519
+ }
9520
+ /** Lands keyboard users at the top of the view they moved to, never on a control that changed meaning. */
9521
+ function useFocusOnNavigation(rootRef, placeId) {
9522
+ const shownPlaceId = useRef(placeId);
9523
+ useLayoutEffect(() => {
9524
+ const root = rootRef.current;
9525
+ if (shownPlaceId.current === placeId || !root) return;
9526
+ shownPlaceId.current = placeId;
9527
+ const body = root.querySelector(".dt-sheet-body");
9528
+ if (body) body.scrollTop = 0;
9529
+ (placeId === null ? root.querySelector(ROOT_FOCUS_SELECTOR) : nestedViewStart(root))?.focus({ preventScroll: true });
9530
+ }, [placeId, rootRef]);
9531
+ }
9532
+ function SheetNavigation({ onSelectTab, place, tab, tabs }) {
9533
+ if (place) return /* @__PURE__ */ jsxs("button", {
9534
+ "aria-label": `Back to ${place.backLabel}`,
9535
+ className: "dt-sheet-back",
9536
+ onClick: place.onBack,
9537
+ type: "button",
9538
+ children: [/* @__PURE__ */ jsx(ChevronLeftIcon, {}), place.backLabel]
9539
+ });
9540
+ if (tabs.length === 1) return /* @__PURE__ */ jsxs("h2", {
9541
+ className: "dt-sheet-title",
9542
+ tabIndex: -1,
9543
+ children: [TABS[tab].icon, TABS[tab].label]
9544
+ });
9545
+ return /* @__PURE__ */ jsx("div", {
9546
+ className: "dt-tabs",
9547
+ role: "tablist",
9548
+ children: tabs.map((option) => /* @__PURE__ */ jsxs("button", {
9549
+ "aria-selected": option === tab,
9550
+ className: "dt-tab",
9551
+ onClick: () => onSelectTab(option),
9552
+ role: "tab",
9553
+ type: "button",
9554
+ children: [TABS[option].icon, TABS[option].label]
9555
+ }, option))
9556
+ });
9557
+ }
9558
+ function DevtoolsSheet({ children, footer, label = "Reflet devtools", navigation, onClose, side }) {
9559
+ const { handleProps, size } = useSheetSize(side);
9560
+ const floating = useFloatingPosition(`top-${side}`, false, {
9561
+ anchorSelector: ".dt-sheet",
9562
+ persistKey: SHEET_POSITION_KEY
9563
+ });
9564
+ const isMoved = floating.position !== null;
9565
+ const { rootRef } = floating;
9566
+ useSheetFocus(rootRef);
9567
+ useFocusOnNavigation(rootRef, navigation.place?.id ?? null);
9568
+ useEffect(() => listenToKeydown(rootRef.current, (event) => {
9569
+ if (event.key === "Escape" && !event.isComposing) onClose();
9570
+ }), [onClose, rootRef]);
9571
+ return /* @__PURE__ */ jsxs("aside", {
9572
+ "aria-label": label,
9573
+ className: "dt-sheet glass",
9574
+ "data-moved": isMoved,
9575
+ "data-side": side,
9576
+ ref: rootRef,
9577
+ style: {
9578
+ height: size.height ?? (isMoved ? FULL_HEIGHT : void 0),
9579
+ width: size.width
9580
+ },
9581
+ children: [
9582
+ /* @__PURE__ */ jsx("style", { children: floating.styles }),
9583
+ /* @__PURE__ */ jsx("button", {
9584
+ "aria-label": "Resize panel width (double-click to reset)",
9585
+ className: "dt-resize",
9586
+ "data-axis": "width",
9587
+ type: "button",
9588
+ ...handleProps("width")
9589
+ }),
9590
+ /* @__PURE__ */ jsx("button", {
9591
+ "aria-label": "Resize panel height (double-click for full height)",
9592
+ className: "dt-resize",
9593
+ "data-axis": "height",
9594
+ type: "button",
9595
+ ...handleProps("height")
9596
+ }),
9597
+ /* @__PURE__ */ jsx("button", {
9598
+ "aria-label": "Resize panel (double-click to reset)",
9599
+ className: "dt-resize",
9600
+ "data-axis": "both",
9601
+ type: "button",
9602
+ ...handleProps("both")
9603
+ }),
9604
+ /* @__PURE__ */ jsxs("div", {
9605
+ className: "dt-sheet-head",
9606
+ children: [
9607
+ /* @__PURE__ */ jsx("button", {
9608
+ "aria-label": "Move panel (double-click to dock it back)",
9609
+ className: "dt-sheet-drag",
9610
+ onDoubleClick: floating.resetPosition,
9611
+ title: "Drag to move · double-click to dock",
9612
+ type: "button",
9613
+ ...floating.handleProps
9614
+ }),
9615
+ /* @__PURE__ */ jsx(SheetNavigation, { ...navigation }),
9616
+ /* @__PURE__ */ jsx("button", {
9617
+ "aria-label": `Close ${label} panel`,
9618
+ className: "icon-btn dt-sheet-close",
9619
+ onClick: onClose,
9620
+ title: "Close",
9621
+ type: "button",
9622
+ children: /* @__PURE__ */ jsx(CloseIcon, {})
9623
+ })
9624
+ ]
9625
+ }),
9626
+ /* @__PURE__ */ jsx("div", {
9627
+ className: "dt-sheet-body",
9628
+ role: navigation.place === null && navigation.tabs.length > 1 ? "tabpanel" : void 0,
9629
+ tabIndex: navigation.place === null ? void 0 : -1,
9630
+ children
9631
+ }, `${navigation.tab}:${navigation.place === null ? "root" : "nested"}`),
9632
+ footer
9633
+ ]
9634
+ });
9635
+ }
9636
+ //#endregion
9637
+ //#region src/devtools/client/devtools-styles.ts
9638
+ const MONO_FONT = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
9639
+ const DEVTOOLS_STYLES = `
9640
+ .dt {
9641
+ --dt-radius-surface: 16px; --dt-radius-card: 12px; --dt-radius-field: 8px; --dt-radius-tiny: 4px;
9642
+ font: 13px/1.45 var(--rf-font, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif); color: var(--rf-text);
9643
+ }
9644
+ .dt button:active:not(:disabled, .dt-btn, .icon-btn) { scale: 1; }
9645
+ .dt[data-recessed="true"] .dt-bar, .dt[data-recessed="true"] .dt-sheet, .dt-sheet ~ .dt-bar { visibility: hidden; pointer-events: none; }
9646
+ .dt-pin {
9647
+ position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 0; left: 0;
9648
+ display: flex; align-items: center; justify-content: center;
9649
+ min-width: 24px; height: 24px; padding: 0 7px;
9650
+ border-radius: 12px 12px 12px 3px; background: var(--rf-accent); color: var(--rf-accent-text);
9651
+ font-size: 11.5px; font-weight: 650; box-shadow: 0 0 0 2px var(--rf-bg), var(--rf-shadow-low);
9652
+ animation: dt-pin-in 260ms var(--rf-ease);
9653
+ transition: scale 140ms var(--rf-ease), opacity 160ms var(--rf-ease);
9654
+ }
9655
+ .dt-pin:hover, .dt-pin[aria-expanded="true"] { scale: 1.12; }
9656
+ .dt-pin[data-sent="true"] { background: var(--rf-text-muted); }
9657
+ .dt-pin-badge { display: inline-flex; flex: none; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px 10px 10px 3px; background: var(--rf-accent); color: var(--rf-accent-text); font-size: 10.5px; font-weight: 650; }
9658
+ .dt-card[data-sent="true"] .dt-pin-badge { background: var(--rf-text-muted); }
9659
+ .dt-pin[data-locating="true"] { animation: dt-pin-in 260ms var(--rf-ease), dt-pin-pulse 1.2s ease-in-out infinite; }
9660
+ @keyframes dt-pin-in { from { scale: .4; opacity: 0; } }
9661
+ @keyframes dt-pin-pulse { 50% { box-shadow: 0 0 0 2px var(--rf-bg), 0 0 0 6px color-mix(in srgb, var(--rf-accent) 30%, transparent); } }
9662
+ .dt-popover {
9663
+ position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; width: 320px; max-height: calc(100dvh - 24px); overflow: auto;
9664
+ inset-inline-end: auto; margin: 0; padding: 6px; border-radius: var(--dt-radius-surface); background: transparent; color: inherit;
9665
+ animation: dt-popover-in 200ms var(--rf-ease);
9666
+ }
9667
+ .dt-popover.glass::before { animation: rf-fade 180ms var(--rf-ease); }
9668
+ .dt-popover .dt-card { background: transparent; }
9669
+ @keyframes dt-popover-in { from { transform: translateY(4px); } }
9670
+ @keyframes dt-notice-in { from { transform: translateY(-6px); } }
9671
+ .dt-notices {
9672
+ position: fixed; z-index: ${REFLET_Z_INDEX.annotatorOverlay}; left: 50%; top: 16px; translate: -50% 0;
9673
+ display: flex; flex-direction: column; gap: 6px; width: min(460px, calc(100vw - 24px));
9674
+ }
9675
+ .dt-notice {
9676
+ display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 14px; border-radius: var(--dt-radius-card);
9677
+ color: var(--rf-danger); font-size: 12.5px; animation: dt-notice-in 220ms var(--rf-ease);
9678
+ }
9679
+ .dt-notice[data-kind="status"] { color: var(--rf-text); }
9680
+ .dt-notice p { flex: 1; margin: 0; overflow-wrap: anywhere; }
9681
+ .dt-notice .icon-btn { width: 30px; height: 30px; }
9682
+ .dt-bar {
9683
+ --dt-edge-gap: calc(max(var(--rf-offset), 12px) + 48px);
9684
+ position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar};
9685
+ display: flex; align-items: center; gap: 2px; padding: 3px;
9686
+ border-radius: var(--rf-pill);
9687
+ }
9688
+ .dt-bar[data-side="left"] { left: calc(var(--rf-viewport-left, 0px) + max(12px, var(--rf-offset))); }
9689
+ .dt-bar[data-side="right"] { right: calc(var(--rf-viewport-right, 0px) + max(12px, var(--rf-offset))); }
9690
+ .dt-bar[data-edge="bottom"] { bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + var(--dt-edge-gap)); }
9691
+ .dt-bar[data-edge="top"] { top: calc(var(--rf-viewport-top, 0px) + env(safe-area-inset-top) + var(--dt-edge-gap)); }
9692
+ .dt-bar .icon-btn { position: relative; width: 34px; height: 34px; }
9693
+ .dt-bar .icon-btn.dt-grip { width: 22px; color: var(--rf-text-muted); opacity: .6; }
9694
+ .dt-bar .icon-btn.dt-grip:hover { opacity: 1; }
9695
+ .dt-count {
9696
+ position: absolute; top: 1px; right: 1px; display: flex; align-items: center; justify-content: center;
9697
+ min-width: 15px; height: 15px; padding: 0 4px; border-radius: var(--rf-pill);
9698
+ background: var(--rf-accent); color: var(--rf-accent-text); font-size: 9.5px; font-weight: 600;
9699
+ }
9700
+ .dt-sheet {
9701
+ --dt-enter-x: 20px;
9702
+ position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 12px; bottom: 12px;
9703
+ display: flex; flex-direction: column; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px);
9704
+ border-radius: var(--dt-radius-surface); overflow: hidden;
9705
+ animation: dt-sheet-slide 320ms var(--rf-ease);
9706
+ }
9707
+ .dt-sheet[data-side="left"] { --dt-enter-x: -20px; left: 12px; }
9708
+ .dt-sheet[data-side="right"] { right: 12px; }
9709
+ .dt-sheet.glass::before { animation: rf-fade 220ms var(--rf-ease); }
9710
+ .dt-sheet-head, .dt-sheet-body { animation: rf-fade 260ms 60ms var(--rf-ease) both; }
9711
+ @keyframes dt-sheet-slide { from { transform: translateX(var(--dt-enter-x)); box-shadow: none; } }
9712
+ .dt-resize { position: absolute; z-index: 2; padding: 0; touch-action: none; }
9713
+ .dt-resize:active:not(:disabled) { scale: 1; }
9714
+ .dt-resize:focus-visible { outline: none; }
9715
+ .dt-resize[data-axis="width"] { top: 16px; bottom: 16px; width: 10px; cursor: ew-resize; }
9716
+ .dt-sheet[data-side="right"] .dt-resize[data-axis="width"] { left: 0; }
9717
+ .dt-sheet[data-side="left"] .dt-resize[data-axis="width"] { right: 0; }
9718
+ .dt-resize[data-axis="height"] { left: 16px; right: 16px; bottom: 0; height: 10px; cursor: ns-resize; }
9719
+ .dt-resize[data-axis="both"] { bottom: 0; width: 16px; height: 16px; }
9720
+ .dt-sheet[data-side="right"] .dt-resize[data-axis="both"] { left: 0; cursor: nesw-resize; }
9721
+ .dt-sheet[data-side="left"] .dt-resize[data-axis="both"] { right: 0; cursor: nwse-resize; }
9722
+ .dt-resize[data-axis="width"]::after, .dt-resize[data-axis="height"]::after {
9723
+ content: ""; position: absolute; border-radius: var(--rf-pill); background: var(--rf-text-muted);
9724
+ opacity: 0; transition: opacity 160ms var(--rf-ease);
9725
+ }
9726
+ .dt-resize[data-axis="width"]::after { top: 50%; left: 3px; width: 4px; height: 36px; translate: 0 -50%; }
9727
+ .dt-resize[data-axis="height"]::after { left: 50%; top: 3px; width: 36px; height: 4px; translate: -50% 0; }
9728
+ .dt-resize:hover::after, .dt-resize:focus-visible::after, .dt-resize:active::after { opacity: .5; }
9729
+ .dt-sheet-head { position: relative; display: flex; align-items: center; gap: 4px; padding: 8px 8px 8px 6px; border-bottom: 1px solid var(--rf-border); }
9730
+ .dt-sheet-head > :not(.dt-sheet-drag) { position: relative; z-index: 1; }
9731
+ .dt-sheet-head .icon-btn { width: 32px; height: 32px; }
9732
+ .dt-sheet .dt-sheet-drag { position: absolute; inset: 0; z-index: 0; padding: 0; border-radius: 0; cursor: grab; touch-action: none; }
9733
+ .dt-sheet .dt-sheet-drag::before { content: ""; position: absolute; top: 4px; left: 50%; width: 36px; height: 4px; translate: -50% 0; border-radius: var(--rf-pill); background: var(--rf-text-muted); opacity: .3; transition: opacity 160ms var(--rf-ease); }
9734
+ .dt-sheet .dt-sheet-drag:hover::before, .dt-sheet .dt-sheet-drag:active::before { opacity: .7; }
9735
+ .dt-sheet .dt-sheet-drag:active:not(:disabled) { cursor: grabbing; scale: 1; }
9736
+ .dt-sheet .dt-sheet-drag:focus-visible { outline-offset: -2px; }
9737
+ .dt-tabs { display: flex; gap: 2px; min-width: 0; margin-right: auto; overflow-x: auto; scrollbar-width: none; }
9738
+ .dt-tab { display: inline-flex; flex: none; align-items: center; gap: 6px; height: 30px; padding: 0 11px 0 9px; border-radius: var(--rf-pill); color: var(--rf-text-muted); font-weight: 500; }
9739
+ .dt-tab svg { flex: none; width: 15px; height: 15px; }
9740
+ .dt-tab:hover { color: var(--rf-text); }
9741
+ .dt-tab[aria-selected="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
9742
+ .dt-tab-count { min-width: 18px; padding: 0 5px; border-radius: var(--rf-pill); background: var(--rf-bg-hover); font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
9743
+ .dt-tab[aria-selected="true"] .dt-tab-count { background: var(--rf-bg); }
9744
+ .dt-sheet-back { display: inline-flex; align-items: center; gap: 4px; height: 32px; margin-right: auto; padding: 0 12px 0 6px; border-radius: var(--rf-pill); font-weight: 500; }
9745
+ .dt-sheet-back:hover { background: var(--rf-bg-hover); }
9746
+ .dt-sheet-back svg, .dt-sheet-title svg { flex: none; width: 16px; height: 16px; }
9747
+ .dt-sheet-title { display: inline-flex; align-items: center; gap: 6px; margin: 0 auto 0 8px; font-size: 13px; font-weight: 600; }
9748
+ .dt-sheet-title:focus { outline: none; }
9749
+ .dt-toolbar { display: flex; align-items: center; gap: 2px; min-height: 30px; }
9750
+ .dt-toolbar .icon-btn, .dt-toolbar .dt-menu > .icon-btn { width: 30px; height: 30px; }
9751
+ .dt-sheet-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 8px; padding: 10px; }
9752
+ .dt-sheet-foot {
9753
+ position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
9754
+ margin: auto -10px -10px; padding: 10px; border-top: 1px solid var(--rf-border); background: var(--rf-bg);
9755
+ }
9756
+ .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); }
9757
+ .dt-archived .dt-meta { flex: 1 1 100%; }
9758
+ .dt-group { margin: 6px 2px 0; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--rf-text-muted); }
9759
+ .dt-card { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); }
9760
+ .dt-card[data-sent="true"] { opacity: .7; }
9761
+ .dt-card-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
9762
+ .dt-card-title { min-width: 0; margin: 0; overflow: hidden; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
9763
+ .dt-meta { margin: 0; font-size: 11.5px; color: var(--rf-text-muted); overflow-wrap: anywhere; }
9764
+ .dt-note-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
9765
+ .dt-chip { flex: none; padding: 1px 7px; border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text-muted); font-size: 10.5px; font-weight: 600; }
9766
+ .dt-chip[data-tone="accent"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); color: var(--rf-accent); }
9767
+ .dt-source { padding: 0; font: 11.5px/1.4 ${MONO_FONT}; color: var(--rf-accent); text-align: left; overflow-wrap: anywhere; }
9768
+ .dt-source:hover { text-decoration: underline; }
9769
+ .dt-thumb { display: block; align-self: flex-start; max-width: 100%; height: auto; max-height: 132px; object-fit: contain; border-radius: var(--dt-radius-field); box-shadow: var(--rf-shadow-inset); }
9770
+ .dt-actions { display: flex; flex-wrap: wrap; gap: 4px; }
9771
+ .dt-btn {
9772
+ display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
9773
+ border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text);
9774
+ font-size: 12px; font-weight: 500; text-decoration: none;
9775
+ }
9776
+ .dt-btn:hover { background: color-mix(in srgb, var(--rf-text) 12%, transparent); }
9777
+ .dt-btn[data-variant="primary"] { background: var(--rf-primary); color: var(--rf-primary-text); }
9778
+ .dt-btn[data-variant="danger"] { color: var(--rf-danger); }
9779
+ .dt-btn svg { flex: none; width: 14px; height: 14px; }
9780
+ .dt-btn-count { padding: 0 6px; border-radius: var(--rf-pill); background: color-mix(in srgb, currentColor 18%, transparent); font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
9781
+ .dt-empty { margin: auto; padding: 24px 16px; color: var(--rf-text-muted); font-size: 12.5px; text-align: center; text-wrap: pretty; }
9782
+ .dt-empty p { margin: 0 0 8px; }
9783
+ .dt-empty-title { margin: 0 0 4px; color: var(--rf-text); font-size: 13.5px; font-weight: 600; text-wrap: balance; }
9784
+ .dt-empty .dt-btn { margin-top: 4px; }
9785
+ .dt-empty code, .dt-hint code { padding: 1px 4px; border-radius: var(--dt-radius-tiny); background: var(--rf-bg-hover); font: 11.5px ${MONO_FONT}; overflow-wrap: anywhere; }
9786
+ .dt-hint { margin: 0; padding: 8px 10px; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); color: var(--rf-text-muted); font-size: 12px; }
9787
+ .dt-error { margin: 0; color: var(--rf-danger); font-size: 12px; }
9788
+ .dt-status { margin: 0 auto 0 0; color: var(--rf-text-muted); font-size: 12px; }
9789
+ .dt-field {
9790
+ width: 100%; padding: 7px 9px; border: 1px solid var(--rf-border); border-radius: var(--dt-radius-field);
9791
+ background: var(--rf-bg); color: var(--rf-text); font: inherit; resize: vertical;
9792
+ }
9793
+ .dt-field:focus { outline: 2px solid var(--rf-accent); outline-offset: 0; }
9794
+ .dt-search { display: flex; gap: 6px; }
9795
+ .dt-matches { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
9796
+ .dt-match { display: flex; flex-direction: column; gap: 1px; width: 100%; padding: 6px 8px; border-radius: var(--dt-radius-field); text-align: left; }
9797
+ .dt-match:hover { background: var(--rf-bg-hover); }
9798
+ .dt-match code { font: 11.5px ${MONO_FONT}; color: var(--rf-accent); }
9799
+ .dt-match span { font: 11.5px ${MONO_FONT}; color: var(--rf-text-muted); white-space: pre; overflow: hidden; text-overflow: ellipsis; }
9800
+ .dt-code-head { display: flex; flex-direction: column; gap: 6px; }
9801
+ .dt-code-path { margin: 0; font: 12px/1.4 ${MONO_FONT}; overflow-wrap: anywhere; }
9802
+ .dt-code { flex: 1; min-height: 0; overflow: auto; margin: 0; padding: 8px 0; border-radius: var(--dt-radius-card); background: var(--rf-bg-subtle); font: 12px/1.6 ${MONO_FONT}; }
9803
+ .dt-code-lines { display: table; min-width: 100%; }
9804
+ .dt-line { display: table-row; white-space: pre; }
9805
+ .dt-line > span { display: table-cell; padding-right: 14px; }
9806
+ .dt-line > .dt-line-number { width: 1%; padding: 0 12px 0 10px; color: var(--rf-text-muted); text-align: right; user-select: none; }
9807
+ .dt-line[data-marked="true"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); }
9808
+ .dt-line[data-marked="true"] > .dt-line-number { color: var(--rf-accent); }
9809
+ .dt-menu { position: relative; flex: none; }
9810
+ .dt-menu-list {
9811
+ position: absolute; right: 0; z-index: 3; display: flex; flex-direction: column; min-width: 176px; padding: 4px;
9812
+ border: 1px solid var(--rf-border); border-radius: var(--dt-radius-card); background: var(--rf-bg); box-shadow: var(--rf-shadow);
9813
+ animation: rf-fade 120ms var(--rf-ease);
9814
+ }
9815
+ .dt-menu-list[data-placement="below"] { top: calc(100% + 4px); }
9816
+ .dt-menu-list[data-placement="above"] { bottom: calc(100% + 4px); }
9817
+ .dt-menu-item { display: flex; align-items: center; gap: 6px; height: 30px; padding: 0 10px; border-radius: var(--dt-radius-field); color: var(--rf-text); font-size: 12.5px; text-align: left; white-space: nowrap; }
9818
+ .dt-menu-item:hover, .dt-menu-item:focus-visible { background: var(--rf-bg-hover); outline: none; }
9819
+ .dt-menu-item[data-variant="danger"] { color: var(--rf-danger); }
9820
+ .dt-menu-check { display: inline-flex; width: 14px; color: var(--rf-accent); }
9821
+ .dt-sheet-account { display: flex; flex: none; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px; border-top: 1px solid var(--rf-border); }
9822
+ .dt-sheet-account > .dt-meta { flex: 1 1 160px; padding-inline: 4px; }
9823
+ .dt-sheet-account > .dt-menu { flex: 1; min-width: 0; }
9824
+ .dt-sheet-account .dt-menu-list { left: 0; }
9825
+ .dt-account { display: flex; align-items: center; gap: 8px; width: 100%; padding: 4px 8px 4px 4px; border-radius: var(--dt-radius-card); text-align: left; }
9826
+ .dt-account:hover, .dt-account[aria-expanded="true"] { background: var(--rf-bg-hover); }
9827
+ .dt-account > svg { flex: none; width: 14px; height: 14px; color: var(--rf-text-muted); }
9828
+ .dt-account-text { display: flex; flex: 1; flex-direction: column; min-width: 0; line-height: 1.3; }
9829
+ .dt-account-name, .dt-account-text .dt-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
9830
+ .dt-account-name { font-size: 12.5px; font-weight: 600; }
9831
+ .dt-avatar {
9832
+ position: relative; display: flex; flex: none; align-items: center; justify-content: center; width: 28px; height: 28px; overflow: hidden;
9833
+ border-radius: 50%; background: color-mix(in srgb, var(--rf-accent) 18%, transparent); color: var(--rf-accent); font-size: 11px; font-weight: 650;
9834
+ }
9835
+ .dt-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
9836
+ `;
9837
+ //#endregion
9838
+ export { searchCode as A, devtoolsTransport as C, fetchBoardFeedback as D, disconnectBoard as E, settle as F, sessionApiErrorSchema as I, Notices as L, committed as M, describeStorageFailure as N, fetchSourceFile as O, openDatabase as P, announce as R, purgeAccount as S, callReflet as T, RecordingLayer as _, SHEET_TABS as a, failureMessage as b, AccountNote as c, sessionSheetPlace as d, useSessionNavigation as f, copyToClipboard as g, ViewOptionsIcon as h, DevtoolsBarButtons as i, startBoardConnect as j, probeDevRoute as k, SESSIONS_STYLES as l, RefreshIcon as m, DevtoolsSheet as n, SessionsBarButton as o, Menu as p, DevtoolsBar as r, AccountFooter as s, DEVTOOLS_STYLES as t, SessionsPanel as u, countLabel as v, teamTransport as w, useWorkspaceLoader as x, SessionWorkspaceProvider as y, reportFailure as z };
9468
9839
 
9469
- //# sourceMappingURL=sessions-panel-BYM0UUgT.js.map
9840
+ //# sourceMappingURL=devtools-styles-teRMfSzW.js.map