reflet-sdk 0.6.5 → 0.6.7

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +100 -2
  3. package/dist/{client-DAdWdIFY.js → client-oFFfRxAM.js} +135 -59
  4. package/dist/client-oFFfRxAM.js.map +1 -0
  5. package/dist/client-transport-B7BdVU7J.d.ts +150 -0
  6. package/dist/client-transport-B7BdVU7J.d.ts.map +1 -0
  7. package/dist/{devtools-layer-CEjzYGBB.js → devtools-layer-OjTo9_eb.js} +417 -916
  8. package/dist/devtools-layer-OjTo9_eb.js.map +1 -0
  9. package/dist/devtools-next.js +1 -1
  10. package/dist/devtools-vite.js +2 -2
  11. package/dist/feedback.d.ts +7 -1
  12. package/dist/feedback.d.ts.map +1 -1
  13. package/dist/feedback.js +105 -57
  14. package/dist/feedback.js.map +1 -1
  15. package/dist/{handler-BSNmdHXv.js → handler-yA_knIf3.js} +4 -4
  16. package/dist/{handler-BSNmdHXv.js.map → handler-yA_knIf3.js.map} +1 -1
  17. package/dist/index-Buum9tIY.d.ts +230 -0
  18. package/dist/index-Buum9tIY.d.ts.map +1 -0
  19. package/dist/index.d.ts +3 -3
  20. package/dist/index.js +1 -1
  21. package/dist/options-CIBazDPp.d.ts.map +1 -1
  22. package/dist/protocol-vJhQHTcB.js +390 -0
  23. package/dist/protocol-vJhQHTcB.js.map +1 -0
  24. package/dist/{react-context-Caq1fodf.js → react-context-Kazo_JCz.js} +3 -14
  25. package/dist/react-context-Kazo_JCz.js.map +1 -0
  26. package/dist/react.d.ts +43 -3
  27. package/dist/react.d.ts.map +1 -1
  28. package/dist/react.js +389 -3
  29. package/dist/react.js.map +1 -1
  30. package/dist/selection-outline-NzAAAZXR.js +51 -0
  31. package/dist/selection-outline-NzAAAZXR.js.map +1 -0
  32. package/dist/server.js +1 -1
  33. package/dist/server.js.map +1 -1
  34. package/dist/sessions-panel-CkxIekZX.js +6010 -0
  35. package/dist/sessions-panel-CkxIekZX.js.map +1 -0
  36. package/dist/{submit-MvVjNdYc.js → submit-DZX0f2_0.js} +44 -70
  37. package/dist/submit-DZX0f2_0.js.map +1 -0
  38. package/dist/surveys.d.ts +3 -0
  39. package/dist/surveys.js +2 -0
  40. package/dist/team-activation-DCqh71bl.js +24 -0
  41. package/dist/team-activation-DCqh71bl.js.map +1 -0
  42. package/dist/team-layer-DyyGqmxs.js +423 -0
  43. package/dist/team-layer-DyyGqmxs.js.map +1 -0
  44. package/dist/{types-DnaDEN9Y.d.ts → types-B3XiHPqy.d.ts} +4 -9
  45. package/dist/types-B3XiHPqy.d.ts.map +1 -0
  46. package/dist/use-survey-session-LK6sN1Fg.js +1247 -0
  47. package/dist/use-survey-session-LK6sN1Fg.js.map +1 -0
  48. package/dist/z-index-CwokqVq_.js +15 -0
  49. package/dist/z-index-CwokqVq_.js.map +1 -0
  50. package/package.json +6 -2
  51. package/dist/client-DAdWdIFY.js.map +0 -1
  52. package/dist/client-DF1fdsSC.d.ts +0 -131
  53. package/dist/client-DF1fdsSC.d.ts.map +0 -1
  54. package/dist/devtools-layer-CEjzYGBB.js.map +0 -1
  55. package/dist/protocol-ClqTFzwR.js +0 -35
  56. package/dist/protocol-ClqTFzwR.js.map +0 -1
  57. package/dist/react-context-Caq1fodf.js.map +0 -1
  58. package/dist/submit-MvVjNdYc.js.map +0 -1
  59. package/dist/types-DnaDEN9Y.d.ts.map +0 -1
@@ -1,93 +1,9 @@
1
- import { a as EDITORS, n as DEVTOOLS_REQUEST_HEADER, r as DEVTOOLS_ROUTE_BASE, t as DEVTOOLS_ENDPOINTS } from "./protocol-ClqTFzwR.js";
2
- import { B as collectPageContext, G as resolveComponentStack, H as buildElementSelection, J as captureViewport, K as shortenSourcePath, M as TargetIcon, R as DEFAULT_WIDGET_LABELS, T as GripIcon, U as describeElement, W as getFiberFromNode, X as capturedViewportSize, Y as capturedFromCanvas, Z as releaseCapture, a as uploadImageBlob, c as useFloatingPosition, d as SelectionOutline, g as isSubmitEnter, j as Svg, l as onViewportChange, m as listenToKeydown, o as ElementPicker, p as listenOnPageAndWidget, r as deriveTitle, s as isWidgetOwned, u as visibleViewport, w as CloseIcon, z as SDK_VERSION } from "./submit-MvVjNdYc.js";
3
- import { a as REFLET_Z_INDEX } from "./react-context-Caq1fodf.js";
1
+ import { $ as shortenSourcePath, G as describeElement, Q as resolveComponentStack, R as DEFAULT_WIDGET_LABELS, U as buildElementSelection, V as collectPageContext, X as getFiberFromNode, a as uploadBlob, f as listenOnPageAndWidget, g as isSubmitEnter, it as releaseCapture, nt as capturedFromCanvas, o as ElementPicker, p as listenToKeydown, r as deriveTitle, rt as capturedViewportSize, s as isWidgetOwned, tt as captureViewport, z as SDK_VERSION } from "./submit-DZX0f2_0.js";
2
+ import { t as SelectionOutline } from "./selection-outline-NzAAAZXR.js";
3
+ import { A as announce, C as searchCode, D as openDatabase, E as describeStorageFailure, O as settle, S as probeDevRoute, T as committed, a as DevtoolsBar, b as fetchBoardFeedback, c as copyToClipboard, f as SessionWorkspaceProvider, g as devtoolsTransport, j as reportFailure, k as Notices, l as SESSIONS_STYLES, m as useWorkspaceLoader, n as DEVTOOLS_STYLES, o as DevtoolsBarButtons, r as DevtoolsSheet, t as SessionsPanel, u as RecordingLayer, v as callReflet, w as startBoardConnect, x as fetchSourceFile, y as disconnectBoard } from "./sessions-panel-CkxIekZX.js";
4
4
  import * as G$1 from "react";
5
5
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
- //#region src/devtools/client/route/dev-route.ts
8
- const INBOX_PAGE_SIZE = 50;
9
- var DevRouteError = class extends Error {
10
- status;
11
- constructor(message, status) {
12
- super(message);
13
- this.name = "DevRouteError";
14
- this.status = status;
15
- }
16
- };
17
- function isBoardAccess(value) {
18
- if (typeof value !== "object" || value === null || !("kind" in value)) return false;
19
- switch (value.kind) {
20
- case "secretKey": return true;
21
- case "connected": return "organizationName" in value && typeof value.organizationName === "string";
22
- case "disconnected": return "canConnect" in value && typeof value.canConnect === "boolean";
23
- default: return false;
24
- }
25
- }
26
- function isDevtoolsStatus(value) {
27
- return typeof value === "object" && value !== null && "marker" in value && value.marker === "reflet-devtools" && "board" in value && isBoardAccess(value.board) && "editor" in value && EDITORS.some((known) => known === value.editor);
28
- }
29
- async function callDevRoute(endpoint, init = {}) {
30
- const search = init.query ? `?${new URLSearchParams(init.query)}` : "";
31
- const response = await fetch(`${DEVTOOLS_ROUTE_BASE}${endpoint}${search}`, {
32
- body: init.body === void 0 ? void 0 : JSON.stringify(init.body),
33
- headers: {
34
- "Content-Type": "application/json",
35
- [DEVTOOLS_REQUEST_HEADER]: "1"
36
- },
37
- method: init.method ?? "GET"
38
- });
39
- if (!response.ok) {
40
- const body = await response.json().catch(() => null);
41
- const routeError = typeof body === "object" && body !== null && "error" in body ? body.error : null;
42
- throw new DevRouteError(typeof routeError === "string" ? routeError : `The Reflet dev route answered ${response.status}.`, response.status);
43
- }
44
- return await response.json();
45
- }
46
- async function probeDevRoute() {
47
- try {
48
- const status = await callDevRoute(DEVTOOLS_ENDPOINTS.status);
49
- return isDevtoolsStatus(status) ? status : null;
50
- } catch {
51
- return null;
52
- }
53
- }
54
- function startBoardConnect(publicKey) {
55
- return callDevRoute(DEVTOOLS_ENDPOINTS.connectStart, {
56
- body: { publicKey },
57
- method: "POST"
58
- });
59
- }
60
- function disconnectBoard() {
61
- return callDevRoute(DEVTOOLS_ENDPOINTS.disconnect, {
62
- body: {},
63
- method: "POST"
64
- });
65
- }
66
- function fetchSourceFile(request) {
67
- const query = { file: request.fileName };
68
- if (request.line !== null) query.line = String(request.line);
69
- if (request.column !== null) query.column = String(request.column);
70
- return callDevRoute(DEVTOOLS_ENDPOINTS.source, { query });
71
- }
72
- function searchCode(text) {
73
- return callDevRoute(DEVTOOLS_ENDPOINTS.search, { query: { q: text } });
74
- }
75
- function callReflet(apiPath, init = {}) {
76
- return callDevRoute(`${DEVTOOLS_ENDPOINTS.proxy}${apiPath}`, {
77
- body: init.body,
78
- method: init.body === void 0 ? "GET" : "POST",
79
- query: init.query
80
- });
81
- }
82
- function fetchBoardFeedback(pagePath) {
83
- const query = {
84
- limit: String(INBOX_PAGE_SIZE),
85
- sortBy: "newest"
86
- };
87
- if (pagePath !== null) query.pagePath = pagePath;
88
- return callReflet("/api/v1/feedback/list", { query });
89
- }
90
- //#endregion
91
7
  //#region src/devtools/client/copy-button.tsx
92
8
  const COPIED_FEEDBACK_MS = 1600;
93
9
  function CopyButton({ label, text, variant }) {
@@ -455,73 +371,6 @@ function InboxPanel({ onOpenCode, onShowSelector }) {
455
371
  }, scope)] });
456
372
  }
457
373
  //#endregion
458
- //#region src/devtools/client/notices.tsx
459
- const STATUS_VISIBLE_MS = 2400;
460
- let notices = [];
461
- let nextNoticeId = 1;
462
- const listeners = /* @__PURE__ */ new Set();
463
- function publish(next) {
464
- notices = next;
465
- for (const listener of listeners) listener();
466
- }
467
- function dismissNotice(id) {
468
- publish(notices.filter((notice) => notice.id !== id));
469
- }
470
- /** Same message twice replaces the first, so a failing retry never stacks up. */
471
- function reportFailure(action, error, retry) {
472
- const message = `${action}: ${error instanceof Error ? error.message : "Unknown error."}`;
473
- publish([...notices.filter((notice) => notice.message !== message), {
474
- id: nextNoticeId++,
475
- kind: "failure",
476
- message,
477
- retry
478
- }]);
479
- }
480
- function announce(message) {
481
- const id = nextNoticeId++;
482
- publish([...notices.filter((notice) => notice.message !== message), {
483
- id,
484
- kind: "status",
485
- message
486
- }]);
487
- setTimeout(() => dismissNotice(id), STATUS_VISIBLE_MS);
488
- }
489
- function subscribe(listener) {
490
- listeners.add(listener);
491
- return () => listeners.delete(listener);
492
- }
493
- function Notices() {
494
- const current = useSyncExternalStore(subscribe, () => notices, () => notices);
495
- if (current.length === 0) return null;
496
- return /* @__PURE__ */ jsx("div", {
497
- className: "dt-notices",
498
- children: current.map((notice) => /* @__PURE__ */ jsxs("div", {
499
- className: "dt-notice glass",
500
- "data-kind": notice.kind,
501
- role: notice.kind === "failure" ? "alert" : "status",
502
- children: [
503
- /* @__PURE__ */ jsx("p", { children: notice.message }),
504
- notice.retry && /* @__PURE__ */ jsx("button", {
505
- className: "dt-btn",
506
- onClick: () => {
507
- dismissNotice(notice.id);
508
- notice.retry?.();
509
- },
510
- type: "button",
511
- children: "Retry"
512
- }),
513
- /* @__PURE__ */ jsx("button", {
514
- "aria-label": "Dismiss",
515
- className: "icon-btn",
516
- onClick: () => dismissNotice(notice.id),
517
- type: "button",
518
- children: /* @__PURE__ */ jsx(CloseIcon, {})
519
- })
520
- ]
521
- }, notice.id))
522
- });
523
- }
524
- //#endregion
525
374
  //#region src/devtools/client/route/route-hint.tsx
526
375
  function MissingRouteHint({ purpose }) {
527
376
  return /* @__PURE__ */ jsxs("div", {
@@ -652,586 +501,6 @@ function BoardTab({ canConnect, onOpenCode, onShowSelector, publicKey, refresh,
652
501
  refresh
653
502
  }), inbox] });
654
503
  }
655
- //#endregion
656
- //#region src/devtools/client/clipboard.ts
657
- /**
658
- * Safari only lets a page write the clipboard inside the key press or click
659
- * itself, while the text waits on a source-map lookup: the write starts now
660
- * and the text follows.
661
- */
662
- async function copyToClipboard(text) {
663
- if (!window.isSecureContext) throw new Error("The clipboard needs HTTPS or localhost.");
664
- const blob = text.then((value) => new Blob([value], { type: "text/plain" }));
665
- await navigator.clipboard.write([new ClipboardItem({ "text/plain": blob })]);
666
- }
667
- //#endregion
668
- //#region src/devtools/client/code/editor-url.ts
669
- /** VS Code-family links want `/c:/dir/file.tsx` on Windows, `/dir/file.tsx` elsewhere. */
670
- const fileSchemeLink = (scheme) => (absolutePath, line, column) => {
671
- const slashed = absolutePath.replaceAll("\\", "/");
672
- const rooted = slashed.startsWith("/") ? slashed : `/${slashed}`;
673
- return `${scheme}://file${encodeURI(rooted)}:${line}:${column}`;
674
- };
675
- const EDITOR_LINKS = {
676
- cursor: fileSchemeLink("cursor"),
677
- vscode: fileSchemeLink("vscode"),
678
- webstorm: (absolutePath, line, column) => `webstorm://open?${new URLSearchParams({
679
- column: String(column),
680
- file: absolutePath,
681
- line: String(line)
682
- })}`,
683
- windsurf: fileSchemeLink("windsurf"),
684
- zed: fileSchemeLink("zed")
685
- };
686
- function editorUrl(editor, absolutePath, line, column) {
687
- return EDITOR_LINKS[editor](absolutePath, line ?? 1, (column ?? 0) + 1);
688
- }
689
- //#endregion
690
- //#region src/devtools/client/code/code-panel.tsx
691
- function markedRange(file) {
692
- if (file.elementLines) return file.elementLines;
693
- return file.line === null ? null : {
694
- end: file.line,
695
- start: file.line
696
- };
697
- }
698
- function SourceView({ file }) {
699
- const firstMarkedRef = useRef(null);
700
- const marked = markedRange(file);
701
- const lines = file.code.split("\n").map((text, index) => ({
702
- number: index + 1,
703
- text
704
- }));
705
- useEffect(() => {
706
- firstMarkedRef.current?.scrollIntoView({ block: "center" });
707
- }, []);
708
- return /* @__PURE__ */ jsx("pre", {
709
- className: "dt-code",
710
- children: /* @__PURE__ */ jsx("code", {
711
- className: "dt-code-lines",
712
- children: lines.map(({ number, text }) => {
713
- const isMarked = marked !== null && number >= marked.start && number <= marked.end;
714
- return /* @__PURE__ */ jsxs("span", {
715
- className: "dt-line",
716
- "data-marked": isMarked,
717
- ref: number === marked?.start ? firstMarkedRef : void 0,
718
- children: [/* @__PURE__ */ jsx("span", {
719
- className: "dt-line-number",
720
- children: number
721
- }), /* @__PURE__ */ jsx("span", { children: text || " " })]
722
- }, number);
723
- })
724
- })
725
- });
726
- }
727
- function CodePanel({ editor, target }) {
728
- const [state, setState] = useState({ kind: "loading" });
729
- const { request } = target;
730
- useEffect(() => {
731
- let active = true;
732
- fetchSourceFile(request).then((file) => active && setState({
733
- file,
734
- kind: "ready"
735
- })).catch((error) => active && setState({
736
- kind: "failed",
737
- message: error instanceof Error ? error.message : "Could not read the file."
738
- }));
739
- return () => {
740
- active = false;
741
- };
742
- }, [request]);
743
- if (state.kind === "loading") return /* @__PURE__ */ jsxs("p", {
744
- className: "dt-status",
745
- children: [
746
- "Opening ",
747
- request.fileName,
748
- "…"
749
- ]
750
- });
751
- if (state.kind === "failed") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
752
- className: "dt-code-path",
753
- children: request.fileName
754
- }), /* @__PURE__ */ jsx("p", {
755
- className: "dt-error",
756
- children: state.message
757
- })] });
758
- const { file } = state;
759
- const location = file.line === null ? file.path : `${file.path}:${file.line}`;
760
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
761
- className: "dt-code-head",
762
- children: [
763
- /* @__PURE__ */ jsx("p", {
764
- className: "dt-card-title",
765
- children: target.title
766
- }),
767
- /* @__PURE__ */ jsx("p", {
768
- className: "dt-code-path",
769
- children: location
770
- }),
771
- /* @__PURE__ */ jsxs("div", {
772
- className: "dt-actions",
773
- children: [/* @__PURE__ */ jsx("a", {
774
- className: "dt-btn",
775
- "data-variant": "primary",
776
- href: editorUrl(editor, file.absolutePath, file.line, file.column),
777
- children: "Open in editor"
778
- }), /* @__PURE__ */ jsx(CopyButton, {
779
- label: "Copy path",
780
- text: location
781
- })]
782
- })
783
- ]
784
- }), /* @__PURE__ */ jsx(SourceView, { file })] });
785
- }
786
- //#endregion
787
- //#region src/devtools/client/devtools-icons.tsx
788
- function NotesIcon() {
789
- return /* @__PURE__ */ jsx(Svg, { children: /* @__PURE__ */ jsx("path", { d: "M8 6h13M8 12h13M8 18h13M3.5 6h.01M3.5 12h.01M3.5 18h.01" }) });
790
- }
791
- function InboxIcon() {
792
- 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" })] });
793
- }
794
- //#endregion
795
- //#region src/devtools/client/devtools-bar.tsx
796
- const BAR_POSITION_KEY = "reflet-devtools-bar-position";
797
- const PICK_BUTTON_LABEL = "Pick an element: note it, copy it, or Shift+click to open its code";
798
- function BarButton({ children, label, onClick, pressed }) {
799
- return /* @__PURE__ */ jsx("button", {
800
- "aria-label": label,
801
- "aria-pressed": pressed,
802
- className: "icon-btn",
803
- onClick,
804
- title: label,
805
- type: "button",
806
- children
807
- });
808
- }
809
- function DevtoolsBar({ edge, noteCount, onPick, onToggleSheet, openTab, side }) {
810
- const floating = useFloatingPosition(`${edge}-${side}`, false, {
811
- anchorSelector: ".dt-bar",
812
- persistKey: BAR_POSITION_KEY
813
- });
814
- return /* @__PURE__ */ jsxs("div", {
815
- "aria-label": "Reflet devtools",
816
- className: "dt-bar glass",
817
- "data-edge": edge,
818
- "data-moved": floating.position !== null,
819
- "data-side": side,
820
- ref: floating.rootRef,
821
- role: "toolbar",
822
- children: [
823
- /* @__PURE__ */ jsx("style", { children: floating.styles }),
824
- /* @__PURE__ */ jsx("button", {
825
- "aria-label": "Move devtools (double-click to reset)",
826
- className: "icon-btn drag-handle dt-grip",
827
- onDoubleClick: floating.resetPosition,
828
- title: "Drag to move · double-click to reset",
829
- type: "button",
830
- ...floating.handleProps,
831
- children: /* @__PURE__ */ jsx(GripIcon, {})
832
- }),
833
- /* @__PURE__ */ jsx(BarButton, {
834
- label: PICK_BUTTON_LABEL,
835
- onClick: onPick,
836
- pressed: false,
837
- children: /* @__PURE__ */ jsx(TargetIcon, {})
838
- }),
839
- /* @__PURE__ */ jsxs(BarButton, {
840
- label: "Dev notes",
841
- onClick: () => onToggleSheet("notes"),
842
- pressed: openTab === "notes",
843
- children: [/* @__PURE__ */ jsx(NotesIcon, {}), noteCount > 0 && /* @__PURE__ */ jsx("span", {
844
- className: "dt-count",
845
- children: noteCount
846
- })]
847
- }),
848
- /* @__PURE__ */ jsx(BarButton, {
849
- label: "Board feedback",
850
- onClick: () => onToggleSheet("inbox"),
851
- pressed: openTab === "inbox",
852
- children: /* @__PURE__ */ jsx(InboxIcon, {})
853
- })
854
- ]
855
- });
856
- }
857
- //#endregion
858
- //#region src/devtools/client/use-sheet-size.ts
859
- const SIZE_KEY = "reflet-devtools-sheet-size";
860
- const DEFAULT_WIDTH = 440;
861
- const MIN_WIDTH = 320;
862
- const MIN_HEIGHT = 240;
863
- const KEYBOARD_STEP = 24;
864
- const ARROW_DELTAS = {
865
- ArrowDown: {
866
- dx: 0,
867
- dy: KEYBOARD_STEP
868
- },
869
- ArrowLeft: {
870
- dx: -24,
871
- dy: 0
872
- },
873
- ArrowRight: {
874
- dx: KEYBOARD_STEP,
875
- dy: 0
876
- },
877
- ArrowUp: {
878
- dx: 0,
879
- dy: -24
880
- }
881
- };
882
- function readStoredSize() {
883
- const fallback = {
884
- height: null,
885
- width: DEFAULT_WIDTH
886
- };
887
- try {
888
- const stored = JSON.parse(localStorage.getItem(SIZE_KEY) ?? "null");
889
- if (typeof stored === "object" && stored !== null && "width" in stored && typeof stored.width === "number" && "height" in stored && (stored.height === null || typeof stored.height === "number")) return {
890
- height: stored.height,
891
- width: stored.width
892
- };
893
- } catch {
894
- return fallback;
895
- }
896
- return fallback;
897
- }
898
- /**
899
- * The stored size is the preference; what renders is that preference fitted
900
- * to today's window, so shrinking the window never loses the chosen size.
901
- */
902
- function fitted(size, viewport) {
903
- const maxWidth = viewport.width - 24;
904
- const maxHeight = viewport.height - 24;
905
- return {
906
- height: size.height === null || size.height >= maxHeight ? null : Math.max(Math.min(MIN_HEIGHT, maxHeight), size.height),
907
- width: Math.min(maxWidth, Math.max(MIN_WIDTH, size.width))
908
- };
909
- }
910
- /**
911
- * Width grows away from the docked side, height grows downward from the top;
912
- * a height reaching the bottom of the window snaps back to full height.
913
- */
914
- function useSheetSize(side) {
915
- const [preference, setPreference] = useState(readStoredSize);
916
- const [viewport, setViewport] = useState(visibleViewport);
917
- const drag = useRef(null);
918
- const widthDirection = side === "right" ? -1 : 1;
919
- const size = fitted(preference, viewport);
920
- useEffect(() => onViewportChange(() => setViewport(visibleViewport())), []);
921
- useEffect(() => {
922
- try {
923
- localStorage.setItem(SIZE_KEY, JSON.stringify(preference));
924
- } catch {}
925
- }, [preference]);
926
- const resize = (axis, from, dx, dy) => {
927
- const fullHeight = viewport.height - 24;
928
- setPreference(fitted({
929
- height: axis === "width" ? from.height : (from.height ?? fullHeight) + dy,
930
- width: axis === "height" ? from.width : from.width + dx * widthDirection
931
- }, viewport));
932
- };
933
- const handleProps = (axis) => ({
934
- onDoubleClick: () => setPreference((current) => ({
935
- height: axis === "width" ? current.height : null,
936
- width: axis === "height" ? current.width : DEFAULT_WIDTH
937
- })),
938
- onKeyDown: (event) => {
939
- const delta = ARROW_DELTAS[event.key];
940
- if (!delta) return;
941
- event.preventDefault();
942
- const sheetHeight = event.currentTarget.parentElement?.getBoundingClientRect().height ?? null;
943
- resize(axis, {
944
- height: size.height ?? sheetHeight,
945
- width: size.width
946
- }, delta.dx, delta.dy);
947
- },
948
- onLostPointerCapture: () => {
949
- drag.current = null;
950
- },
951
- onPointerDown: (event) => {
952
- if (event.button !== 0) return;
953
- const sheet = event.currentTarget.parentElement?.getBoundingClientRect();
954
- event.currentTarget.setPointerCapture(event.pointerId);
955
- drag.current = {
956
- axis,
957
- size: {
958
- height: sheet?.height ?? size.height,
959
- width: size.width
960
- },
961
- x: event.clientX,
962
- y: event.clientY
963
- };
964
- },
965
- onPointerMove: (event) => {
966
- const start = drag.current;
967
- if (start && event.currentTarget.hasPointerCapture(event.pointerId)) resize(start.axis, start.size, event.clientX - start.x, event.clientY - start.y);
968
- }
969
- });
970
- return {
971
- handleProps,
972
- size
973
- };
974
- }
975
- //#endregion
976
- //#region src/devtools/client/devtools-sheet.tsx
977
- const FULL_HEIGHT = "calc(100dvh - 24px)";
978
- const TAB_LABELS = {
979
- code: "Code",
980
- inbox: "Board",
981
- notes: "Notes"
982
- };
983
- const SHEET_POSITION_KEY = "reflet-devtools-sheet-position";
984
- function DevtoolsSheet({ children, onClose, onPick, onSelectTab, side, tab, tabs }) {
985
- const { handleProps, size } = useSheetSize(side);
986
- const floating = useFloatingPosition(`top-${side}`, false, {
987
- anchorSelector: ".dt-sheet",
988
- persistKey: SHEET_POSITION_KEY
989
- });
990
- const isMoved = floating.position !== null;
991
- const { rootRef } = floating;
992
- useEffect(() => listenToKeydown(rootRef.current, (event) => {
993
- if (event.key === "Escape" && !event.isComposing) onClose();
994
- }), [onClose, rootRef]);
995
- return /* @__PURE__ */ jsxs("aside", {
996
- "aria-label": "Reflet devtools",
997
- className: "dt-sheet glass",
998
- "data-moved": isMoved,
999
- "data-side": side,
1000
- ref: rootRef,
1001
- style: {
1002
- height: size.height ?? (isMoved ? FULL_HEIGHT : void 0),
1003
- width: size.width
1004
- },
1005
- children: [
1006
- /* @__PURE__ */ jsxs("style", { children: [floating.styles, isMoved ? "" : `.dt-bar[data-moved="false"][data-side="${side}"] { ${side}: ${size.width + 24}px; }`] }),
1007
- /* @__PURE__ */ jsx("button", {
1008
- "aria-label": "Resize panel width (double-click to reset)",
1009
- className: "dt-resize",
1010
- "data-axis": "width",
1011
- type: "button",
1012
- ...handleProps("width")
1013
- }),
1014
- /* @__PURE__ */ jsx("button", {
1015
- "aria-label": "Resize panel height (double-click for full height)",
1016
- className: "dt-resize",
1017
- "data-axis": "height",
1018
- type: "button",
1019
- ...handleProps("height")
1020
- }),
1021
- /* @__PURE__ */ jsx("button", {
1022
- "aria-label": "Resize panel (double-click to reset)",
1023
- className: "dt-resize",
1024
- "data-axis": "both",
1025
- type: "button",
1026
- ...handleProps("both")
1027
- }),
1028
- /* @__PURE__ */ jsxs("div", {
1029
- className: "dt-sheet-head",
1030
- children: [
1031
- /* @__PURE__ */ jsx("button", {
1032
- "aria-label": "Move panel (double-click to dock it back)",
1033
- className: "dt-sheet-drag",
1034
- onDoubleClick: floating.resetPosition,
1035
- title: "Drag to move · double-click to dock",
1036
- type: "button",
1037
- ...floating.handleProps
1038
- }),
1039
- /* @__PURE__ */ jsx("button", {
1040
- "aria-label": PICK_BUTTON_LABEL,
1041
- className: "icon-btn",
1042
- onClick: onPick,
1043
- title: "Pick an element",
1044
- type: "button",
1045
- children: /* @__PURE__ */ jsx(TargetIcon, {})
1046
- }),
1047
- /* @__PURE__ */ jsx("div", {
1048
- className: "dt-tabs",
1049
- role: "tablist",
1050
- children: tabs.map((option) => /* @__PURE__ */ jsx("button", {
1051
- "aria-selected": option === tab,
1052
- className: "dt-tab",
1053
- onClick: () => onSelectTab(option),
1054
- role: "tab",
1055
- type: "button",
1056
- children: TAB_LABELS[option]
1057
- }, option))
1058
- }),
1059
- /* @__PURE__ */ jsx("button", {
1060
- "aria-label": "Close devtools panel",
1061
- className: "icon-btn",
1062
- onClick: onClose,
1063
- title: "Close",
1064
- type: "button",
1065
- children: /* @__PURE__ */ jsx(CloseIcon, {})
1066
- })
1067
- ]
1068
- }),
1069
- /* @__PURE__ */ jsx("div", {
1070
- className: "dt-sheet-body",
1071
- role: "tabpanel",
1072
- children
1073
- }, tab)
1074
- ]
1075
- });
1076
- }
1077
- //#endregion
1078
- //#region src/devtools/client/devtools-styles.ts
1079
- const MONO_FONT = "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace";
1080
- const DEVTOOLS_STYLES = `
1081
- .dt { font: 13px/1.45 var(--rf-font, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif); color: var(--rf-text); }
1082
- .dt[data-recessed="true"] .dt-bar, .dt[data-recessed="true"] .dt-sheet { visibility: hidden; pointer-events: none; }
1083
- .dt-pin {
1084
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 0; left: 0;
1085
- display: flex; align-items: center; justify-content: center;
1086
- min-width: 24px; height: 24px; padding: 0 7px;
1087
- border-radius: 12px 12px 12px 3px; background: var(--rf-accent); color: var(--rf-accent-text);
1088
- font-size: 11.5px; font-weight: 650; box-shadow: 0 0 0 2px var(--rf-bg), var(--rf-shadow-low);
1089
- animation: dt-pin-in 260ms var(--rf-ease);
1090
- transition: scale 140ms var(--rf-ease), opacity 160ms var(--rf-ease);
1091
- }
1092
- .dt-pin:hover, .dt-pin[aria-expanded="true"] { scale: 1.12; }
1093
- .dt-pin[data-sent="true"] { background: var(--rf-text-muted); }
1094
- .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; }
1095
- .dt-card[data-sent="true"] .dt-pin-badge { background: var(--rf-text-muted); }
1096
- .dt-pin[data-locating="true"] { animation: dt-pin-in 260ms var(--rf-ease), dt-pin-pulse 1.2s ease-in-out infinite; }
1097
- @keyframes dt-pin-in { from { scale: .4; opacity: 0; } }
1098
- @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); } }
1099
- .dt-popover {
1100
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; width: 320px; max-height: calc(100dvh - 24px); overflow: auto;
1101
- inset-inline-end: auto; margin: 0; padding: 6px; border-radius: 14px; background: transparent; color: inherit;
1102
- animation: dt-popover-in 200ms var(--rf-ease);
1103
- }
1104
- .dt-popover.glass::before { animation: rf-fade 180ms var(--rf-ease); }
1105
- .dt-popover .dt-card { background: transparent; }
1106
- @keyframes dt-popover-in { from { transform: translateY(4px); } }
1107
- @keyframes dt-notice-in { from { transform: translateY(-6px); } }
1108
- .dt-notices {
1109
- position: fixed; z-index: ${REFLET_Z_INDEX.annotatorOverlay}; left: 50%; top: 16px; translate: -50% 0;
1110
- display: flex; flex-direction: column; gap: 6px; width: min(460px, calc(100vw - 24px));
1111
- }
1112
- .dt-notice {
1113
- display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 14px; border-radius: 12px;
1114
- color: var(--rf-danger); font-size: 12.5px; animation: dt-notice-in 220ms var(--rf-ease);
1115
- }
1116
- .dt-notice[data-kind="status"] { color: var(--rf-text); }
1117
- .dt-notice p { flex: 1; margin: 0; overflow-wrap: anywhere; }
1118
- .dt-notice .icon-btn { width: 30px; height: 30px; }
1119
- .dt-bar {
1120
- --dt-edge-gap: calc(max(var(--rf-offset), 12px) + 48px);
1121
- position: fixed; z-index: ${REFLET_Z_INDEX.devtoolsBar};
1122
- display: flex; align-items: center; gap: 2px; padding: 3px;
1123
- border-radius: var(--rf-pill);
1124
- }
1125
- .dt-bar[data-moved="false"] { transition: left 260ms var(--rf-ease), right 260ms var(--rf-ease); }
1126
- .dt-bar[data-side="left"] { left: calc(var(--rf-viewport-left, 0px) + max(12px, var(--rf-offset))); }
1127
- .dt-bar[data-side="right"] { right: calc(var(--rf-viewport-right, 0px) + max(12px, var(--rf-offset))); }
1128
- .dt-bar[data-edge="bottom"] { bottom: calc(var(--rf-viewport-bottom, 0px) + env(safe-area-inset-bottom) + var(--dt-edge-gap)); }
1129
- .dt-bar[data-edge="top"] { top: calc(var(--rf-viewport-top, 0px) + env(safe-area-inset-top) + var(--dt-edge-gap)); }
1130
- .dt-bar .icon-btn { position: relative; width: 34px; height: 34px; }
1131
- .dt-bar .icon-btn.dt-grip { width: 22px; color: var(--rf-text-muted); opacity: .6; }
1132
- .dt-bar .icon-btn.dt-grip:hover { opacity: 1; }
1133
- .dt-bar .icon-btn[aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
1134
- .dt-count {
1135
- position: absolute; top: 1px; right: 1px; display: flex; align-items: center; justify-content: center;
1136
- min-width: 15px; height: 15px; padding: 0 4px; border-radius: var(--rf-pill);
1137
- background: var(--rf-accent); color: var(--rf-accent-text); font-size: 9.5px; font-weight: 600;
1138
- }
1139
- .dt-sheet {
1140
- --dt-enter-x: 20px;
1141
- position: fixed; z-index: ${REFLET_Z_INDEX.widgetRoot}; top: 12px; bottom: 12px;
1142
- display: flex; flex-direction: column; max-width: calc(100vw - 24px); max-height: calc(100dvh - 24px);
1143
- border-radius: 16px; overflow: hidden;
1144
- animation: dt-sheet-slide 320ms var(--rf-ease);
1145
- }
1146
- .dt-sheet[data-side="left"] { --dt-enter-x: -20px; left: 12px; }
1147
- .dt-sheet[data-side="right"] { right: 12px; }
1148
- .dt-sheet.glass::before { animation: rf-fade 220ms var(--rf-ease); }
1149
- .dt-sheet-head, .dt-sheet-body { animation: rf-fade 260ms 60ms var(--rf-ease) both; }
1150
- @keyframes dt-sheet-slide { from { transform: translateX(var(--dt-enter-x)); box-shadow: none; } }
1151
- .dt-resize { position: absolute; z-index: 2; padding: 0; touch-action: none; }
1152
- .dt-resize:active:not(:disabled) { scale: 1; }
1153
- .dt-resize:focus-visible { outline: none; }
1154
- .dt-resize[data-axis="width"] { top: 16px; bottom: 16px; width: 10px; cursor: ew-resize; }
1155
- .dt-sheet[data-side="right"] .dt-resize[data-axis="width"] { left: 0; }
1156
- .dt-sheet[data-side="left"] .dt-resize[data-axis="width"] { right: 0; }
1157
- .dt-resize[data-axis="height"] { left: 16px; right: 16px; bottom: 0; height: 10px; cursor: ns-resize; }
1158
- .dt-resize[data-axis="both"] { bottom: 0; width: 16px; height: 16px; }
1159
- .dt-sheet[data-side="right"] .dt-resize[data-axis="both"] { left: 0; cursor: nesw-resize; }
1160
- .dt-sheet[data-side="left"] .dt-resize[data-axis="both"] { right: 0; cursor: nwse-resize; }
1161
- .dt-resize[data-axis="width"]::after, .dt-resize[data-axis="height"]::after {
1162
- content: ""; position: absolute; border-radius: var(--rf-pill); background: var(--rf-text-muted);
1163
- opacity: 0; transition: opacity 160ms var(--rf-ease);
1164
- }
1165
- .dt-resize[data-axis="width"]::after { top: 50%; left: 3px; width: 4px; height: 36px; translate: 0 -50%; }
1166
- .dt-resize[data-axis="height"]::after { left: 50%; top: 3px; width: 36px; height: 4px; translate: -50% 0; }
1167
- .dt-resize:hover::after, .dt-resize:focus-visible::after, .dt-resize:active::after { opacity: .5; }
1168
- .dt-sheet-head { position: relative; display: flex; align-items: center; gap: 4px; padding: 8px 8px 8px 6px; border-bottom: 1px solid var(--rf-border); }
1169
- .dt-sheet-head > :not(.dt-sheet-drag) { position: relative; z-index: 1; }
1170
- .dt-sheet-head .icon-btn { width: 32px; height: 32px; }
1171
- .dt-sheet .dt-sheet-drag { position: absolute; inset: 0; z-index: 0; padding: 0; border-radius: 0; cursor: grab; touch-action: none; }
1172
- .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); }
1173
- .dt-sheet .dt-sheet-drag:hover::before, .dt-sheet .dt-sheet-drag:active::before { opacity: .7; }
1174
- .dt-sheet .dt-sheet-drag:active:not(:disabled) { cursor: grabbing; scale: 1; }
1175
- .dt-sheet .dt-sheet-drag:focus-visible { outline-offset: -2px; }
1176
- .dt-tabs { display: flex; gap: 2px; margin-right: auto; }
1177
- .dt-tab { height: 30px; padding: 0 11px; border-radius: var(--rf-pill); color: var(--rf-text-muted); font-weight: 500; }
1178
- .dt-tab:hover { color: var(--rf-text); }
1179
- .dt-tab[aria-selected="true"], .dt-tab[aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
1180
- .dt-sheet-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 8px; padding: 10px; }
1181
- .dt-sheet-foot {
1182
- position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
1183
- margin: auto -10px -10px; padding: 10px; border-top: 1px solid var(--rf-border); background: var(--rf-bg);
1184
- }
1185
- .dt-archived { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 8px 10px; border: 1px dashed var(--rf-border); border-radius: 12px; }
1186
- .dt-archived .dt-meta { flex: 1 1 100%; }
1187
- .dt-group { margin: 6px 2px 0; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--rf-text-muted); }
1188
- .dt-card { display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: 12px; background: var(--rf-bg-subtle); }
1189
- .dt-card[data-sent="true"] { opacity: .7; }
1190
- .dt-card-head { display: flex; align-items: center; gap: 6px; min-width: 0; }
1191
- .dt-card-title { min-width: 0; margin: 0; overflow: hidden; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
1192
- .dt-meta { margin: 0; font-size: 11.5px; color: var(--rf-text-muted); overflow-wrap: anywhere; }
1193
- .dt-note-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
1194
- .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; }
1195
- .dt-chip[data-tone="accent"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); color: var(--rf-accent); }
1196
- .dt-source { padding: 0; font: 11.5px/1.4 ${MONO_FONT}; color: var(--rf-accent); text-align: left; overflow-wrap: anywhere; }
1197
- .dt-source:hover { text-decoration: underline; }
1198
- .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); }
1199
- .dt-actions { display: flex; flex-wrap: wrap; gap: 4px; }
1200
- .dt-btn {
1201
- display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
1202
- border-radius: var(--rf-pill); background: var(--rf-bg-hover); color: var(--rf-text);
1203
- font-size: 12px; font-weight: 500; text-decoration: none;
1204
- }
1205
- .dt-btn:hover { background: color-mix(in srgb, var(--rf-text) 12%, transparent); }
1206
- .dt-btn[data-variant="primary"] { background: var(--rf-primary); color: var(--rf-primary-text); }
1207
- .dt-btn[data-variant="danger"] { color: var(--rf-danger); }
1208
- .dt-empty { margin: auto; padding: 24px 16px; color: var(--rf-text-muted); font-size: 12.5px; text-align: center; }
1209
- .dt-empty p { margin: 0 0 8px; }
1210
- .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; }
1211
- .dt-hint { margin: 0; padding: 8px 10px; border-radius: 10px; background: var(--rf-bg-subtle); color: var(--rf-text-muted); font-size: 12px; }
1212
- .dt-error { margin: 0; color: var(--rf-danger); font-size: 12px; }
1213
- .dt-status { margin: 0 auto 0 0; color: var(--rf-text-muted); font-size: 12px; }
1214
- .dt-field {
1215
- width: 100%; padding: 7px 9px; border: 1px solid var(--rf-border); border-radius: 8px;
1216
- background: var(--rf-bg); color: var(--rf-text); font: inherit; resize: vertical;
1217
- }
1218
- .dt-field:focus { outline: 2px solid var(--rf-accent); outline-offset: 0; }
1219
- .dt-search { display: flex; gap: 6px; }
1220
- .dt-matches { display: flex; flex-direction: column; gap: 2px; margin: 0; padding: 0; list-style: none; }
1221
- .dt-match { display: flex; flex-direction: column; gap: 1px; width: 100%; padding: 6px 8px; border-radius: 8px; text-align: left; }
1222
- .dt-match:hover { background: var(--rf-bg-hover); }
1223
- .dt-match code { font: 11.5px ${MONO_FONT}; color: var(--rf-accent); }
1224
- .dt-match span { font: 11.5px ${MONO_FONT}; color: var(--rf-text-muted); white-space: pre; overflow: hidden; text-overflow: ellipsis; }
1225
- .dt-code-head { display: flex; flex-direction: column; gap: 6px; }
1226
- .dt-code-path { margin: 0; font: 12px/1.4 ${MONO_FONT}; overflow-wrap: anywhere; }
1227
- .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}; }
1228
- .dt-code-lines { display: table; min-width: 100%; }
1229
- .dt-line { display: table-row; white-space: pre; }
1230
- .dt-line > span { display: table-cell; padding-right: 14px; }
1231
- .dt-line > .dt-line-number { width: 1%; padding: 0 12px 0 10px; color: var(--rf-text-muted); text-align: right; user-select: none; }
1232
- .dt-line[data-marked="true"] { background: color-mix(in srgb, var(--rf-accent) 16%, transparent); }
1233
- .dt-line[data-marked="true"] > .dt-line-number { color: var(--rf-accent); }
1234
- `;
1235
504
  /**
1236
505
  * @license bippy
1237
506
  *
@@ -2998,50 +2267,228 @@ const $ = (e) => {
2998
2267
  };
2999
2268
  new Set([Z, ...Object.values(Q)].map((e) => $(e.name)));
3000
2269
  //#endregion
3001
- //#region src/devtools/client/element-source.ts
3002
- const MAX_FIBER_ASCENT = 12;
3003
- const DEPENDENCY_FILE = /[\\/]node_modules[\\/]|^node_modules[\\/]/;
3004
- const ROOTED_OR_BUNDLER_NAME = /^(?:\/|\[|[a-z][\w+.-]*:)/i;
3005
- /**
3006
- * Vite's maps list sources relative to the served module (`main.tsx` for
3007
- * `/src/main.tsx`) and bippy hands them back as-is.
3008
- */
3009
- function resolveAgainstModule(fileName, moduleUrl) {
3010
- if (!moduleUrl || ROOTED_OR_BUNDLER_NAME.test(fileName)) return fileName;
3011
- try {
3012
- return new URL(fileName, moduleUrl).pathname;
3013
- } catch {
3014
- return fileName;
3015
- }
2270
+ //#region src/devtools/client/element-source.ts
2271
+ const MAX_FIBER_ASCENT = 12;
2272
+ const DEPENDENCY_FILE = /[\\/]node_modules[\\/]|^node_modules[\\/]/;
2273
+ const ROOTED_OR_BUNDLER_NAME = /^(?:\/|\[|[a-z][\w+.-]*:)/i;
2274
+ /**
2275
+ * Vite's maps list sources relative to the served module (`main.tsx` for
2276
+ * `/src/main.tsx`) and bippy hands them back as-is.
2277
+ */
2278
+ function resolveAgainstModule(fileName, moduleUrl) {
2279
+ if (!moduleUrl || ROOTED_OR_BUNDLER_NAME.test(fileName)) return fileName;
2280
+ try {
2281
+ return new URL(fileName, moduleUrl).pathname;
2282
+ } catch {
2283
+ return fileName;
2284
+ }
2285
+ }
2286
+ const SERVER_CREATION_FRAME = /\(((?:about|rsc):\/\/React\/Server\/[^\s)]+):(\d+):(\d+)\)/;
2287
+ /** The browser cannot map these; the dev route reads the server chunk's map from disk. */
2288
+ function serverCreationSite(fiber) {
2289
+ const stack = fiber._debugStack instanceof Error ? fiber._debugStack.stack : void 0;
2290
+ const match = stack ? SERVER_CREATION_FRAME.exec(stack) : null;
2291
+ if (!match?.[1]) return null;
2292
+ return {
2293
+ column: Number(match[3]) - 1,
2294
+ fileName: match[1],
2295
+ line: Number(match[2])
2296
+ };
2297
+ }
2298
+ /** Source-mapped through the dev server: React's own debug stacks point at bundled chunks. */
2299
+ async function locateElementSource(element) {
2300
+ const hostFiber = getFiberFromNode(element);
2301
+ let fiber = T$1(hostFiber) ? hostFiber : null;
2302
+ for (let depth = 0; fiber && depth < MAX_FIBER_ASCENT; depth++) {
2303
+ const serverSite = serverCreationSite(fiber);
2304
+ if (serverSite) return serverSite;
2305
+ const source = await Rt(fiber).catch(() => null);
2306
+ if (source && Ht(source.fileName) && !DEPENDENCY_FILE.test(source.fileName)) return {
2307
+ column: source.columnNumber ?? null,
2308
+ fileName: resolveAgainstModule(source.fileName, It(fiber)?.fileName),
2309
+ line: source.lineNumber ?? null
2310
+ };
2311
+ fiber = fiber.return;
2312
+ }
2313
+ return null;
2314
+ }
2315
+ //#endregion
2316
+ //#region src/devtools/client/code/editor-url.ts
2317
+ /** VS Code-family links want `/c:/dir/file.tsx` on Windows, `/dir/file.tsx` elsewhere. */
2318
+ const fileSchemeLink = (scheme) => (absolutePath, line, column) => {
2319
+ const slashed = absolutePath.replaceAll("\\", "/");
2320
+ const rooted = slashed.startsWith("/") ? slashed : `/${slashed}`;
2321
+ return `${scheme}://file${encodeURI(rooted)}:${line}:${column}`;
2322
+ };
2323
+ const EDITOR_LINKS = {
2324
+ cursor: fileSchemeLink("cursor"),
2325
+ vscode: fileSchemeLink("vscode"),
2326
+ webstorm: (absolutePath, line, column) => `webstorm://open?${new URLSearchParams({
2327
+ column: String(column),
2328
+ file: absolutePath,
2329
+ line: String(line)
2330
+ })}`,
2331
+ windsurf: fileSchemeLink("windsurf"),
2332
+ zed: fileSchemeLink("zed")
2333
+ };
2334
+ function editorUrl(editor, absolutePath, line, column) {
2335
+ return EDITOR_LINKS[editor](absolutePath, line ?? 1, (column ?? 0) + 1);
2336
+ }
2337
+ //#endregion
2338
+ //#region src/devtools/client/code/code-panel.tsx
2339
+ function markedRange(file) {
2340
+ if (file.elementLines) return file.elementLines;
2341
+ return file.line === null ? null : {
2342
+ end: file.line,
2343
+ start: file.line
2344
+ };
2345
+ }
2346
+ function SourceView({ file }) {
2347
+ const firstMarkedRef = useRef(null);
2348
+ const marked = markedRange(file);
2349
+ const lines = file.code.split("\n").map((text, index) => ({
2350
+ number: index + 1,
2351
+ text
2352
+ }));
2353
+ useEffect(() => {
2354
+ firstMarkedRef.current?.scrollIntoView({ block: "center" });
2355
+ }, []);
2356
+ return /* @__PURE__ */ jsx("pre", {
2357
+ className: "dt-code",
2358
+ children: /* @__PURE__ */ jsx("code", {
2359
+ className: "dt-code-lines",
2360
+ children: lines.map(({ number, text }) => {
2361
+ const isMarked = marked !== null && number >= marked.start && number <= marked.end;
2362
+ return /* @__PURE__ */ jsxs("span", {
2363
+ className: "dt-line",
2364
+ "data-marked": isMarked,
2365
+ ref: number === marked?.start ? firstMarkedRef : void 0,
2366
+ children: [/* @__PURE__ */ jsx("span", {
2367
+ className: "dt-line-number",
2368
+ children: number
2369
+ }), /* @__PURE__ */ jsx("span", { children: text || " " })]
2370
+ }, number);
2371
+ })
2372
+ })
2373
+ });
2374
+ }
2375
+ function CodePanel({ editor, target }) {
2376
+ const [state, setState] = useState({ kind: "loading" });
2377
+ const { request } = target;
2378
+ useEffect(() => {
2379
+ let active = true;
2380
+ fetchSourceFile(request).then((file) => active && setState({
2381
+ file,
2382
+ kind: "ready"
2383
+ })).catch((error) => active && setState({
2384
+ kind: "failed",
2385
+ message: error instanceof Error ? error.message : "Could not read the file."
2386
+ }));
2387
+ return () => {
2388
+ active = false;
2389
+ };
2390
+ }, [request]);
2391
+ if (state.kind === "loading") return /* @__PURE__ */ jsxs("p", {
2392
+ className: "dt-status",
2393
+ children: [
2394
+ "Opening ",
2395
+ request.fileName,
2396
+ "…"
2397
+ ]
2398
+ });
2399
+ if (state.kind === "failed") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("p", {
2400
+ className: "dt-code-path",
2401
+ children: request.fileName
2402
+ }), /* @__PURE__ */ jsx("p", {
2403
+ className: "dt-error",
2404
+ children: state.message
2405
+ })] });
2406
+ const { file } = state;
2407
+ const location = file.line === null ? file.path : `${file.path}:${file.line}`;
2408
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
2409
+ className: "dt-code-head",
2410
+ children: [
2411
+ /* @__PURE__ */ jsx("p", {
2412
+ className: "dt-card-title",
2413
+ children: target.title
2414
+ }),
2415
+ /* @__PURE__ */ jsx("p", {
2416
+ className: "dt-code-path",
2417
+ children: location
2418
+ }),
2419
+ /* @__PURE__ */ jsxs("div", {
2420
+ className: "dt-actions",
2421
+ children: [/* @__PURE__ */ jsx("a", {
2422
+ className: "dt-btn",
2423
+ "data-variant": "primary",
2424
+ href: editorUrl(editor, file.absolutePath, file.line, file.column),
2425
+ children: "Open in editor"
2426
+ }), /* @__PURE__ */ jsx(CopyButton, {
2427
+ label: "Copy path",
2428
+ text: location
2429
+ })]
2430
+ })
2431
+ ]
2432
+ }), /* @__PURE__ */ jsx(SourceView, { file })] });
2433
+ }
2434
+ //#endregion
2435
+ //#region src/devtools/client/code/code-tab.tsx
2436
+ function titleFor(element) {
2437
+ const owner = resolveComponentStack(getFiberFromNode(element))[0];
2438
+ const label = describeElement(element);
2439
+ return owner ? `<${owner}> ${label}` : label;
3016
2440
  }
3017
- const SERVER_CREATION_FRAME = /\(((?:about|rsc):\/\/React\/Server\/[^\s)]+):(\d+):(\d+)\)/;
3018
- /** The browser cannot map these; the dev route reads the server chunk's map from disk. */
3019
- function serverCreationSite(fiber) {
3020
- const stack = fiber._debugStack instanceof Error ? fiber._debugStack.stack : void 0;
3021
- const match = stack ? SERVER_CREATION_FRAME.exec(stack) : null;
3022
- if (!match?.[1]) return null;
2441
+ function useCodeLookup(showCodeTab) {
2442
+ const [codeLookup, setCodeLookup] = useState(null);
2443
+ const openCode = useCallback((target) => {
2444
+ setCodeLookup({
2445
+ kind: "located",
2446
+ target
2447
+ });
2448
+ showCodeTab("code");
2449
+ }, [showCodeTab]);
3023
2450
  return {
3024
- column: Number(match[3]) - 1,
3025
- fileName: match[1],
3026
- line: Number(match[2])
2451
+ codeLookup,
2452
+ inspectElement: useCallback((element) => {
2453
+ const title = titleFor(element);
2454
+ setCodeLookup({
2455
+ element,
2456
+ kind: "locating",
2457
+ title
2458
+ });
2459
+ showCodeTab("code");
2460
+ locateElementSource(element).catch(() => null).then((request) => setCodeLookup((current) => {
2461
+ if (!(current?.kind === "locating" && current.element === element)) return current;
2462
+ return request ? {
2463
+ kind: "located",
2464
+ target: {
2465
+ request,
2466
+ title
2467
+ }
2468
+ } : {
2469
+ kind: "unlocated",
2470
+ title
2471
+ };
2472
+ }));
2473
+ }, [showCodeTab]),
2474
+ openCode
3027
2475
  };
3028
2476
  }
3029
- /** Source-mapped through the dev server: React's own debug stacks point at bundled chunks. */
3030
- async function locateElementSource(element) {
3031
- const hostFiber = getFiberFromNode(element);
3032
- let fiber = T$1(hostFiber) ? hostFiber : null;
3033
- for (let depth = 0; fiber && depth < MAX_FIBER_ASCENT; depth++) {
3034
- const serverSite = serverCreationSite(fiber);
3035
- if (serverSite) return serverSite;
3036
- const source = await Rt(fiber).catch(() => null);
3037
- if (source && Ht(source.fileName) && !DEPENDENCY_FILE.test(source.fileName)) return {
3038
- column: source.columnNumber ?? null,
3039
- fileName: resolveAgainstModule(source.fileName, It(fiber)?.fileName),
3040
- line: source.lineNumber ?? null
3041
- };
3042
- fiber = fiber.return;
3043
- }
3044
- return null;
2477
+ function CodeTab({ lookup, route }) {
2478
+ if (route.kind === "missing") return /* @__PURE__ */ jsx(MissingRouteHint, { purpose: "read your source files here" });
2479
+ if (lookup.kind === "locating" || route.kind === "checking") return /* @__PURE__ */ jsx("p", {
2480
+ className: "dt-status",
2481
+ children: "Locating the source…"
2482
+ });
2483
+ if (lookup.kind === "unlocated") return /* @__PURE__ */ jsxs("div", {
2484
+ className: "dt-empty",
2485
+ children: [/* @__PURE__ */ jsx("p", { children: lookup.title }), /* @__PURE__ */ jsx("p", { children: "React did not record where this element was written: it comes from a library, or this page is a production build." })]
2486
+ });
2487
+ const { request } = lookup.target;
2488
+ return /* @__PURE__ */ jsx(CodePanel, {
2489
+ editor: route.status.editor,
2490
+ target: lookup.target
2491
+ }, `${request.fileName}:${request.line}:${request.column}`);
3045
2492
  }
3046
2493
  //#endregion
3047
2494
  //#region src/devtools/client/find-on-page.ts
@@ -3137,28 +2584,16 @@ const SERVER_SNAPSHOT = {
3137
2584
  notes: [],
3138
2585
  status: "loading"
3139
2586
  };
3140
- /** Executor form: the SDK targets ES2022, before `Promise.withResolvers`. */
3141
- function settle(request) {
3142
- return new Promise((resolve, reject) => {
3143
- request.onsuccess = () => resolve(request.result);
3144
- request.onerror = () => reject(request.error);
3145
- });
3146
- }
3147
- function committed(transaction) {
3148
- return new Promise((resolve, reject) => {
3149
- transaction.oncomplete = () => resolve();
3150
- transaction.onerror = () => reject(transaction.error);
3151
- transaction.onabort = () => reject(transaction.error);
2587
+ function openNotesDatabase() {
2588
+ return openDatabase({
2589
+ name: DATABASE_NAME,
2590
+ upgrade: (database) => {
2591
+ database.createObjectStore(NOTES, { keyPath: "id" });
2592
+ database.createObjectStore(PAYLOADS, { keyPath: "noteId" });
2593
+ },
2594
+ version: DATABASE_VERSION
3152
2595
  });
3153
2596
  }
3154
- function openDatabase() {
3155
- const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
3156
- request.onupgradeneeded = () => {
3157
- request.result.createObjectStore(NOTES, { keyPath: "id" });
3158
- request.result.createObjectStore(PAYLOADS, { keyPath: "noteId" });
3159
- };
3160
- return settle(request);
3161
- }
3162
2597
  function newestFirst(notes) {
3163
2598
  return [...notes].sort((a, b) => b.createdAt - a.createdAt);
3164
2599
  }
@@ -3174,9 +2609,6 @@ function pinNumbers(notes, pathname) {
3174
2609
  const onPage = notes.filter((note) => isActiveNote(note) && isNoteOnPage(note, pathname)).sort((a, b) => a.createdAt - b.createdAt);
3175
2610
  return new Map(onPage.map((note, index) => [note.id, index + 1]));
3176
2611
  }
3177
- function describeFailure(error) {
3178
- return error instanceof Error ? error.message : "IndexedDB is unavailable in this browser.";
3179
- }
3180
2612
  /**
3181
2613
  * The list is what IndexedDB holds with every in-flight change laid on top:
3182
2614
  * a change shows at once, a reload from another tab never drops it, and a
@@ -3205,7 +2637,7 @@ function createNoteStore() {
3205
2637
  for (const listener of listeners) listener();
3206
2638
  };
3207
2639
  const connect = () => {
3208
- database ??= openDatabase();
2640
+ database ??= openNotesDatabase();
3209
2641
  return database;
3210
2642
  };
3211
2643
  /** Loads the notes and drops archives past their day, in one readwrite pass. */
@@ -3228,7 +2660,7 @@ function createNoteStore() {
3228
2660
  } catch (error) {
3229
2661
  stored = [];
3230
2662
  publish({
3231
- error: describeFailure(error),
2663
+ error: describeStorageFailure(error),
3232
2664
  status: "failed"
3233
2665
  });
3234
2666
  }
@@ -3253,7 +2685,7 @@ function createNoteStore() {
3253
2685
  channel?.postMessage("changed");
3254
2686
  } catch (error) {
3255
2687
  settleChange(null);
3256
- throw new Error(describeFailure(error));
2688
+ throw new Error(describeStorageFailure(error));
3257
2689
  }
3258
2690
  };
3259
2691
  const patchStoredNote = (transaction, noteId, patch) => {
@@ -3296,7 +2728,8 @@ function createNoteStore() {
3296
2728
  });
3297
2729
  },
3298
2730
  readCloseUp: async (noteId) => {
3299
- return (await settle((await connect()).transaction(PAYLOADS, "readonly").objectStore(PAYLOADS).get(noteId)))?.closeUp ?? null;
2731
+ const store = (await connect()).transaction(PAYLOADS, "readonly").objectStore(PAYLOADS);
2732
+ return (await settle(store.get(noteId)))?.closeUp ?? null;
3300
2733
  },
3301
2734
  remove: (noteIds) => {
3302
2735
  const removedIds = new Set(noteIds);
@@ -3631,7 +3064,7 @@ function reportContext(note) {
3631
3064
  }
3632
3065
  async function attachCloseUp(note, feedbackId, closeUp) {
3633
3066
  const { uploadUrl } = await callReflet("/api/v1/feedback/screenshot/upload-url", { body: {} });
3634
- const storageId = await uploadImageBlob(uploadUrl, closeUp);
3067
+ const storageId = await uploadBlob(uploadUrl, closeUp);
3635
3068
  await callReflet("/api/v1/feedback/screenshot/save", { body: {
3636
3069
  captureSource: "element",
3637
3070
  feedbackId,
@@ -3992,6 +3425,84 @@ function useDevRoute() {
3992
3425
  };
3993
3426
  }
3994
3427
  //#endregion
3428
+ //#region src/devtools/client/sessions/devtools-sessions.tsx
3429
+ const DEVTOOLS_TRANSPORT = devtoolsTransport();
3430
+ /** Sessions go through the dev route with the developer's own Connect token, never the secret key. */
3431
+ function useDevtoolsSessions(options) {
3432
+ const { route } = options;
3433
+ const isConnected = route.kind === "ready" && route.status.board.kind === "connected";
3434
+ const state = useWorkspaceLoader({
3435
+ onUnauthorized: options.onUnauthorized,
3436
+ surface: "devtools",
3437
+ transport: isConnected ? DEVTOOLS_TRANSPORT : null
3438
+ });
3439
+ const [open, setOpen] = useState(null);
3440
+ return {
3441
+ navigation: {
3442
+ onOpen: setOpen,
3443
+ open
3444
+ },
3445
+ state
3446
+ };
3447
+ }
3448
+ function SessionsScope({ children, sessions }) {
3449
+ const workspace = sessions.state.kind === "ready" ? sessions.state.workspace : null;
3450
+ return /* @__PURE__ */ jsx(SessionWorkspaceProvider, {
3451
+ workspace,
3452
+ children
3453
+ });
3454
+ }
3455
+ function ConnectHint({ onConnect }) {
3456
+ return /* @__PURE__ */ jsxs("div", {
3457
+ className: "dt-empty",
3458
+ children: [/* @__PURE__ */ jsx("p", { children: "Sessions are saved under your Reflet account. Connect in the Board tab." }), /* @__PURE__ */ jsx("button", {
3459
+ className: "dt-btn",
3460
+ onClick: onConnect,
3461
+ type: "button",
3462
+ children: "Open the Board tab"
3463
+ })]
3464
+ });
3465
+ }
3466
+ function WorkspaceGate({ actions, sessions }) {
3467
+ const { state } = sessions;
3468
+ if (state.kind === "ready") return /* @__PURE__ */ jsx(SessionsPanel, {
3469
+ navigation: sessions.navigation,
3470
+ onStarted: actions.onStarted,
3471
+ onWriteNote: actions.onWriteNote
3472
+ });
3473
+ if (state.kind === "failed") return /* @__PURE__ */ jsxs("div", {
3474
+ className: "dt-empty",
3475
+ role: "alert",
3476
+ children: [/* @__PURE__ */ jsxs("p", { children: ["Sessions did not open: ", state.message] }), /* @__PURE__ */ jsx("button", {
3477
+ className: "dt-btn",
3478
+ onClick: state.retry,
3479
+ type: "button",
3480
+ children: "Try again"
3481
+ })]
3482
+ });
3483
+ return /* @__PURE__ */ jsx("p", {
3484
+ className: "dt-status",
3485
+ children: "Opening your sessions…"
3486
+ });
3487
+ }
3488
+ function SessionsTab({ actions, route, sessions }) {
3489
+ if (route.kind === "checking") return /* @__PURE__ */ jsx("p", {
3490
+ className: "dt-status",
3491
+ children: "Connecting to the Reflet dev route…"
3492
+ });
3493
+ if (route.kind === "missing") return /* @__PURE__ */ jsx(MissingRouteHint, { purpose: "record sessions under your Reflet account" });
3494
+ const { board } = route.status;
3495
+ if (board.kind === "disconnected") return /* @__PURE__ */ jsx(ConnectHint, { onConnect: actions.onConnect });
3496
+ if (board.kind === "secretKey") return /* @__PURE__ */ jsx("p", {
3497
+ className: "dt-empty",
3498
+ children: "Sessions are recorded under your own name, so they need Connect in the Board tab rather than REFLET_SECRET_KEY."
3499
+ });
3500
+ return /* @__PURE__ */ jsx(WorkspaceGate, {
3501
+ actions,
3502
+ sessions
3503
+ });
3504
+ }
3505
+ //#endregion
3995
3506
  //#region src/devtools/client/devtools-layer.tsx
3996
3507
  const PICKER_LABELS = {
3997
3508
  ...DEFAULT_WIDGET_LABELS,
@@ -4000,32 +3511,17 @@ const PICKER_LABELS = {
4000
3511
  elementNotePlaceholder: "What should change here?",
4001
3512
  pickElementHint: "Pick an element to note or copy it"
4002
3513
  };
4003
- function titleFor(element) {
4004
- const owner = resolveComponentStack(getFiberFromNode(element))[0];
4005
- const label = describeElement(element);
4006
- return owner ? `<${owner}> ${label}` : label;
4007
- }
4008
3514
  function sendBlockedReason(route, canConnect) {
4009
3515
  if (route.kind === "checking") return "Connecting to the Reflet dev route…";
4010
3516
  if (route.kind === "missing") return "Add the Reflet dev route to send notes to your board.";
4011
3517
  if (route.status.board.kind !== "disconnected") return null;
4012
3518
  return canConnect ? "Connect to Reflet in the Board tab to send notes." : MISSING_SECRET_KEY_HINT;
4013
3519
  }
4014
- function CodeTab({ lookup, route }) {
4015
- if (route.kind === "missing") return /* @__PURE__ */ jsx(MissingRouteHint, { purpose: "read your source files here" });
4016
- if (lookup.kind === "locating" || route.kind === "checking") return /* @__PURE__ */ jsx("p", {
4017
- className: "dt-status",
4018
- children: "Locating the source…"
4019
- });
4020
- if (lookup.kind === "unlocated") return /* @__PURE__ */ jsxs("div", {
4021
- className: "dt-empty",
4022
- children: [/* @__PURE__ */ jsx("p", { children: lookup.title }), /* @__PURE__ */ jsx("p", { children: "React did not record where this element was written: it comes from a library, or this page is a production build." })]
4023
- });
4024
- const { request } = lookup.target;
4025
- return /* @__PURE__ */ jsx(CodePanel, {
4026
- editor: route.status.editor,
4027
- target: lookup.target
4028
- }, `${request.fileName}:${request.line}:${request.column}`);
3520
+ function canConnectBoard(route, publicKey) {
3521
+ return route.kind === "ready" && route.status.board.kind === "disconnected" && route.status.board.canConnect && publicKey !== null;
3522
+ }
3523
+ function unsentNoteCount(notes) {
3524
+ return notes.notes.filter((note) => isActiveNote(note) && !note.sent).length;
4029
3525
  }
4030
3526
  /** Shown the moment it is added; source and close-up follow, best effort. */
4031
3527
  async function saveNote(note, element, canReadSource) {
@@ -4055,14 +3551,18 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
4055
3551
  const { refresh, route } = useDevRoute();
4056
3552
  const notes = useNotes();
4057
3553
  useResumeLocating(notes, route);
3554
+ const sessions = useDevtoolsSessions({
3555
+ onUnauthorized: refresh,
3556
+ route
3557
+ });
4058
3558
  const [isPicking, setIsPicking] = useState(false);
4059
3559
  const [openTab, setOpenTab] = useState(null);
4060
- const [codeLookup, setCodeLookup] = useState(null);
4061
3560
  const [highlighted, setHighlighted] = useState(null);
3561
+ const { codeLookup, inspectElement, openCode } = useCodeLookup(setOpenTab);
4062
3562
  const edge = position.startsWith("top") ? "top" : "bottom";
4063
3563
  const side = position.endsWith("right") ? "right" : "left";
4064
3564
  const canReadSource = route.kind === "ready";
4065
- const canConnect = route.kind === "ready" && route.status.board.kind === "disconnected" && route.status.board.canConnect && publicKey !== null;
3565
+ const canConnect = canConnectBoard(route, publicKey);
4066
3566
  const isRecessed = isWidgetOpen || isPicking;
4067
3567
  const closeSheet = useCallback(() => {
4068
3568
  setOpenTab(null);
@@ -4070,13 +3570,6 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
4070
3570
  }, []);
4071
3571
  const startPick = useCallback(() => setIsPicking(true), []);
4072
3572
  const cancelPick = useCallback(() => setIsPicking(false), []);
4073
- const openCode = useCallback((target) => {
4074
- setCodeLookup({
4075
- kind: "located",
4076
- target
4077
- });
4078
- setOpenTab("code");
4079
- }, []);
4080
3573
  const showSelector = useCallback((selector) => {
4081
3574
  const element = findOnPage(selector);
4082
3575
  setHighlighted(element);
@@ -4086,32 +3579,12 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
4086
3579
  });
4087
3580
  return element !== null;
4088
3581
  }, []);
4089
- const inspectElement = useCallback((element) => {
4090
- setIsPicking(false);
4091
- const title = titleFor(element);
4092
- setCodeLookup({
4093
- element,
4094
- kind: "locating",
4095
- title
4096
- });
4097
- setOpenTab("code");
4098
- locateElementSource(element).catch(() => null).then((request) => setCodeLookup((current) => {
4099
- if (!(current?.kind === "locating" && current.element === element)) return current;
4100
- return request ? {
4101
- kind: "located",
4102
- target: {
4103
- request,
4104
- title
4105
- }
4106
- } : {
4107
- kind: "unlocated",
4108
- title
4109
- };
4110
- }));
4111
- }, []);
4112
3582
  const inspect = useMemo(() => ({
4113
3583
  label: "View code",
4114
- onInspect: inspectElement
3584
+ onInspect: (element) => {
3585
+ setIsPicking(false);
3586
+ inspectElement(element);
3587
+ }
4115
3588
  }), [inspectElement]);
4116
3589
  const writeNote = useCallback((element, text) => {
4117
3590
  setIsPicking(false);
@@ -4125,70 +3598,98 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
4125
3598
  copyToClipboard(draftLocatedNote(element, text, canReadSource).then(noteAsContext)).then(() => announce(`Copied ${title}`)).catch((error) => reportFailure(`${title} was not copied`, error));
4126
3599
  }
4127
3600
  }), [canReadSource]);
4128
- return /* @__PURE__ */ jsxs("div", {
4129
- className: "dt",
4130
- "data-recessed": isRecessed,
4131
- "data-sheet-open": openTab !== null,
4132
- children: [
4133
- /* @__PURE__ */ jsx("style", { children: DEVTOOLS_STYLES }),
4134
- /* @__PURE__ */ jsx(NotePins, {
4135
- isVisible: !isRecessed,
4136
- notes,
4137
- onOpenCode: openCode
4138
- }),
4139
- openTab && /* @__PURE__ */ jsxs(DevtoolsSheet, {
4140
- onClose: closeSheet,
4141
- onPick: startPick,
4142
- onSelectTab: setOpenTab,
4143
- side,
4144
- tab: openTab,
4145
- tabs: codeLookup ? [
4146
- "notes",
4147
- "inbox",
4148
- "code"
4149
- ] : ["notes", "inbox"],
4150
- children: [
4151
- openTab === "notes" && /* @__PURE__ */ jsx(NotesPanel, {
4152
- notes,
4153
- onOpenCode: openCode,
4154
- onShowSelector: showSelector,
4155
- sendBlockedReason: sendBlockedReason(route, canConnect)
4156
- }),
4157
- openTab === "inbox" && /* @__PURE__ */ jsx(BoardTab, {
4158
- canConnect,
4159
- onOpenCode: openCode,
4160
- onShowSelector: showSelector,
4161
- publicKey,
4162
- refresh,
4163
- route
4164
- }),
4165
- openTab === "code" && codeLookup && /* @__PURE__ */ jsx(CodeTab, {
4166
- lookup: codeLookup,
4167
- route
3601
+ const tabs = codeLookup ? [
3602
+ "notes",
3603
+ "sessions",
3604
+ "inbox",
3605
+ "code"
3606
+ ] : [
3607
+ "notes",
3608
+ "sessions",
3609
+ "inbox"
3610
+ ];
3611
+ const writeNoteInstead = () => {
3612
+ closeSheet();
3613
+ startPick();
3614
+ };
3615
+ return /* @__PURE__ */ jsx(SessionsScope, {
3616
+ sessions,
3617
+ children: /* @__PURE__ */ jsxs("div", {
3618
+ className: "dt",
3619
+ "data-recessed": isRecessed,
3620
+ "data-sheet-open": openTab !== null,
3621
+ children: [
3622
+ /* @__PURE__ */ jsx("style", { children: `${DEVTOOLS_STYLES}${SESSIONS_STYLES}` }),
3623
+ /* @__PURE__ */ jsx(NotePins, {
3624
+ isVisible: !isRecessed,
3625
+ notes,
3626
+ onOpenCode: openCode
3627
+ }),
3628
+ openTab && /* @__PURE__ */ jsxs(DevtoolsSheet, {
3629
+ onClose: closeSheet,
3630
+ onPick: startPick,
3631
+ onSelectTab: setOpenTab,
3632
+ side,
3633
+ tab: openTab,
3634
+ tabs,
3635
+ children: [
3636
+ openTab === "notes" && /* @__PURE__ */ jsx(NotesPanel, {
3637
+ notes,
3638
+ onOpenCode: openCode,
3639
+ onShowSelector: showSelector,
3640
+ sendBlockedReason: sendBlockedReason(route, canConnect)
3641
+ }),
3642
+ openTab === "inbox" && /* @__PURE__ */ jsx(BoardTab, {
3643
+ canConnect,
3644
+ onOpenCode: openCode,
3645
+ onShowSelector: showSelector,
3646
+ publicKey,
3647
+ refresh,
3648
+ route
3649
+ }),
3650
+ openTab === "sessions" && /* @__PURE__ */ jsx(SessionsTab, {
3651
+ actions: {
3652
+ onConnect: () => setOpenTab("inbox"),
3653
+ onStarted: closeSheet,
3654
+ onWriteNote: writeNoteInstead
3655
+ },
3656
+ route,
3657
+ sessions
3658
+ }),
3659
+ openTab === "code" && codeLookup && /* @__PURE__ */ jsx(CodeTab, {
3660
+ lookup: codeLookup,
3661
+ route
3662
+ })
3663
+ ]
3664
+ }),
3665
+ /* @__PURE__ */ jsx(DevtoolsBar, {
3666
+ edge,
3667
+ side,
3668
+ children: /* @__PURE__ */ jsx(DevtoolsBarButtons, {
3669
+ noteCount: unsentNoteCount(notes),
3670
+ onPick: startPick,
3671
+ onToggleSheet: (tab) => setOpenTab((current) => current === tab ? null : tab),
3672
+ openTab
4168
3673
  })
4169
- ]
4170
- }),
4171
- /* @__PURE__ */ jsx(DevtoolsBar, {
4172
- edge,
4173
- noteCount: notes.notes.filter((note) => isActiveNote(note) && !note.sent).length,
4174
- onPick: startPick,
4175
- onToggleSheet: (tab) => setOpenTab((current) => current === tab ? null : tab),
4176
- openTab,
4177
- side
4178
- }),
4179
- openTab && /* @__PURE__ */ jsx(SelectionOutline, { node: highlighted }),
4180
- /* @__PURE__ */ jsx(Notices, {}),
4181
- isPicking && /* @__PURE__ */ jsx(ElementPicker, {
4182
- copy,
4183
- inspect,
4184
- labels: PICKER_LABELS,
4185
- onCancel: cancelPick,
4186
- onPick: writeNote
4187
- })
4188
- ]
3674
+ }),
3675
+ openTab && /* @__PURE__ */ jsx(SelectionOutline, { node: highlighted }),
3676
+ /* @__PURE__ */ jsx(Notices, {}),
3677
+ sessions.state.kind === "ready" && /* @__PURE__ */ jsx(RecordingLayer, { onFinished: (target) => {
3678
+ sessions.navigation.onOpen(target);
3679
+ setOpenTab("sessions");
3680
+ } }),
3681
+ isPicking && /* @__PURE__ */ jsx(ElementPicker, {
3682
+ copy,
3683
+ inspect,
3684
+ labels: PICKER_LABELS,
3685
+ onCancel: cancelPick,
3686
+ onPick: writeNote
3687
+ })
3688
+ ]
3689
+ })
4189
3690
  });
4190
3691
  }
4191
3692
  //#endregion
4192
3693
  export { DevtoolsLayer };
4193
3694
 
4194
- //# sourceMappingURL=devtools-layer-CEjzYGBB.js.map
3695
+ //# sourceMappingURL=devtools-layer-OjTo9_eb.js.map