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.
- package/CHANGELOG.md +23 -0
- package/README.md +100 -2
- package/dist/{client-DAdWdIFY.js → client-oFFfRxAM.js} +135 -59
- package/dist/client-oFFfRxAM.js.map +1 -0
- package/dist/client-transport-B7BdVU7J.d.ts +150 -0
- package/dist/client-transport-B7BdVU7J.d.ts.map +1 -0
- package/dist/{devtools-layer-CEjzYGBB.js → devtools-layer-OjTo9_eb.js} +417 -916
- package/dist/devtools-layer-OjTo9_eb.js.map +1 -0
- package/dist/devtools-next.js +1 -1
- package/dist/devtools-vite.js +2 -2
- package/dist/feedback.d.ts +7 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +105 -57
- package/dist/feedback.js.map +1 -1
- package/dist/{handler-BSNmdHXv.js → handler-yA_knIf3.js} +4 -4
- package/dist/{handler-BSNmdHXv.js.map → handler-yA_knIf3.js.map} +1 -1
- package/dist/index-Buum9tIY.d.ts +230 -0
- package/dist/index-Buum9tIY.d.ts.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/options-CIBazDPp.d.ts.map +1 -1
- package/dist/protocol-vJhQHTcB.js +390 -0
- package/dist/protocol-vJhQHTcB.js.map +1 -0
- package/dist/{react-context-Caq1fodf.js → react-context-Kazo_JCz.js} +3 -14
- package/dist/react-context-Kazo_JCz.js.map +1 -0
- package/dist/react.d.ts +43 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +389 -3
- package/dist/react.js.map +1 -1
- package/dist/selection-outline-NzAAAZXR.js +51 -0
- package/dist/selection-outline-NzAAAZXR.js.map +1 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/sessions-panel-CkxIekZX.js +6010 -0
- package/dist/sessions-panel-CkxIekZX.js.map +1 -0
- package/dist/{submit-MvVjNdYc.js → submit-DZX0f2_0.js} +44 -70
- package/dist/submit-DZX0f2_0.js.map +1 -0
- package/dist/surveys.d.ts +3 -0
- package/dist/surveys.js +2 -0
- package/dist/team-activation-DCqh71bl.js +24 -0
- package/dist/team-activation-DCqh71bl.js.map +1 -0
- package/dist/team-layer-DyyGqmxs.js +423 -0
- package/dist/team-layer-DyyGqmxs.js.map +1 -0
- package/dist/{types-DnaDEN9Y.d.ts → types-B3XiHPqy.d.ts} +4 -9
- package/dist/types-B3XiHPqy.d.ts.map +1 -0
- package/dist/use-survey-session-LK6sN1Fg.js +1247 -0
- package/dist/use-survey-session-LK6sN1Fg.js.map +1 -0
- package/dist/z-index-CwokqVq_.js +15 -0
- package/dist/z-index-CwokqVq_.js.map +1 -0
- package/package.json +6 -2
- package/dist/client-DAdWdIFY.js.map +0 -1
- package/dist/client-DF1fdsSC.d.ts +0 -131
- package/dist/client-DF1fdsSC.d.ts.map +0 -1
- package/dist/devtools-layer-CEjzYGBB.js.map +0 -1
- package/dist/protocol-ClqTFzwR.js +0 -35
- package/dist/protocol-ClqTFzwR.js.map +0 -1
- package/dist/react-context-Caq1fodf.js.map +0 -1
- package/dist/submit-MvVjNdYc.js.map +0 -1
- package/dist/types-DnaDEN9Y.d.ts.map +0 -1
|
@@ -1,93 +1,9 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
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
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
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
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
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
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
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
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
}
|
|
3147
|
-
|
|
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 ??=
|
|
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:
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
4129
|
-
|
|
4130
|
-
"
|
|
4131
|
-
"
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
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
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
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-
|
|
3695
|
+
//# sourceMappingURL=devtools-layer-OjTo9_eb.js.map
|