reflet-sdk 0.6.1 → 0.6.3
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 +17 -0
- package/README.md +6 -3
- package/dist/{devtools-layer-Cry67xS8.js → devtools-layer-DtO89RlJ.js} +128 -37
- package/dist/devtools-layer-DtO89RlJ.js.map +1 -0
- package/dist/feedback.js +10 -8
- package/dist/feedback.js.map +1 -1
- package/dist/{picker-C_apeUEq.js → picker-Rh-hsxyX.js} +75 -23
- package/dist/{picker-C_apeUEq.js.map → picker-Rh-hsxyX.js.map} +1 -1
- package/dist/react.js +1 -1
- package/dist/{z-index-BL0SGZEP.js → z-index-CIb4-3Fy.js} +2 -1
- package/dist/{z-index-BL0SGZEP.js.map → z-index-CIb4-3Fy.js.map} +1 -1
- package/package.json +1 -1
- package/dist/devtools-layer-Cry67xS8.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ All notable changes to `reflet-sdk` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.3] - 2026-10-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Copy an element straight from the devtools picker, React Grab style: aim at it and press ⌘C (Ctrl+C), or press Copy on the note card to include your note. The clipboard gets the element, its source location, owner components, selector and HTML.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- The element picker and its hover outline stay above every layer of the page, so elements inside a fixed sidebar or header are outlined instead of hidden under it.
|
|
17
|
+
- The note card and hover label stay on screen when the picked element is taller than the window: the card tries below, above, then beside the element, and overlaps it only when nothing else fits.
|
|
18
|
+
|
|
19
|
+
## [0.6.2] - 2026-10-04
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- The devtools Board tab no longer dead-ends when nothing points at the current page: switch between "This page" and "Whole board", or click "Show the whole board" from the empty state, so feedback filed on other routes or other tenants' URLs stays reachable.
|
|
24
|
+
|
|
8
25
|
## [0.6.1] - 2026-10-04
|
|
9
26
|
|
|
10
27
|
### Fixed
|
package/README.md
CHANGED
|
@@ -190,7 +190,9 @@ turn it off.
|
|
|
190
190
|
"View code" button, and Shift+click (or Shift+Enter) skips the note and opens
|
|
191
191
|
the source file with the JSX element highlighted, ready to jump to in your
|
|
192
192
|
editor. Locations come from React's debug stacks resolved through the dev
|
|
193
|
-
server's source maps, Server Components included.
|
|
193
|
+
server's source maps, Server Components included. Press ⌘C (Ctrl+C) on the
|
|
194
|
+
aimed element, or Copy on the note card, to put the element and its source on
|
|
195
|
+
the clipboard for a coding agent, without saving a note.
|
|
194
196
|
- **Notes** — a note appears the moment you press Enter, as a numbered pin on
|
|
195
197
|
its element, Figma-style; click the pin to read, edit or delete it, and find
|
|
196
198
|
the same number on its card in the panel. Its source location and a
|
|
@@ -203,8 +205,9 @@ turn it off.
|
|
|
203
205
|
- **Send to board** — notes become internal feedback: members see them with an
|
|
204
206
|
"Internal" badge, the public board never does, and an admin can make one
|
|
205
207
|
public from the dashboard.
|
|
206
|
-
- **Board** — the board's feedback for the current route,
|
|
207
|
-
the reporter's source location, and a code search for
|
|
208
|
+
- **Board** — the board's feedback for the current route, or the whole board,
|
|
209
|
+
with "Show on page", the reporter's source location, and a code search for
|
|
210
|
+
the element's text.
|
|
208
211
|
|
|
209
212
|
Code view and the board go through one dev-only route that reads files from
|
|
210
213
|
your repository and holds the board credential server-side.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
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 { A as TargetIcon, B as buildElementSelection, C as GripIcon, H as getFiberFromNode, I as DEFAULT_WIDGET_LABELS, J as releaseCapture, K as captureViewport, L as SDK_VERSION, R as collectPageContext, S as CloseIcon, U as resolveComponentStack, V as describeElement, W as shortenSourcePath, a as deriveTitle, c as useFloatingPosition, d as SelectionOutline, k as Svg, l as onViewportChange, m as listenToKeydown, n as isWidgetOwned, p as listenOnPageAndWidget, q as capturedFromCanvas, s as uploadImageBlob, t as ElementPicker, u as visibleViewport } from "./picker-
|
|
3
|
-
import { t as REFLET_Z_INDEX } from "./z-index-
|
|
2
|
+
import { A as TargetIcon, B as buildElementSelection, C as GripIcon, H as getFiberFromNode, I as DEFAULT_WIDGET_LABELS, J as releaseCapture, K as captureViewport, L as SDK_VERSION, R as collectPageContext, S as CloseIcon, U as resolveComponentStack, V as describeElement, W as shortenSourcePath, a as deriveTitle, c as useFloatingPosition, d as SelectionOutline, k as Svg, l as onViewportChange, m as listenToKeydown, n as isWidgetOwned, p as listenOnPageAndWidget, q as capturedFromCanvas, s as uploadImageBlob, t as ElementPicker, u as visibleViewport } from "./picker-Rh-hsxyX.js";
|
|
3
|
+
import { t as REFLET_Z_INDEX } from "./z-index-CIb4-3Fy.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";
|
|
@@ -79,12 +79,13 @@ function callReflet(apiPath, init = {}) {
|
|
|
79
79
|
query: init.query
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
function fetchBoardFeedback(pagePath) {
|
|
83
|
+
const query = {
|
|
84
84
|
limit: String(INBOX_PAGE_SIZE),
|
|
85
|
-
pagePath,
|
|
86
85
|
sortBy: "newest"
|
|
87
|
-
}
|
|
86
|
+
};
|
|
87
|
+
if (pagePath !== null) query.pagePath = pagePath;
|
|
88
|
+
return callReflet("/api/v1/feedback/list", { query });
|
|
88
89
|
}
|
|
89
90
|
//#endregion
|
|
90
91
|
//#region src/devtools/client/copy-button.tsx
|
|
@@ -141,16 +142,23 @@ function selectionLines(selection) {
|
|
|
141
142
|
if (selection.html) lines.push("HTML:", ...fenced("html", selection.html));
|
|
142
143
|
return lines;
|
|
143
144
|
}
|
|
145
|
+
function elementContext(note) {
|
|
146
|
+
return [`Page: ${note.capturedContext.url ?? "unknown"}`, ...selectionLines(note.selection)];
|
|
147
|
+
}
|
|
144
148
|
function notesToPrompt(notes) {
|
|
145
149
|
const sections = notes.map((note, index) => [
|
|
146
150
|
`## Note ${index + 1}`,
|
|
147
151
|
note.note || "(no comment)",
|
|
148
152
|
"",
|
|
149
|
-
|
|
150
|
-
...selectionLines(note.selection)
|
|
153
|
+
...elementContext(note)
|
|
151
154
|
].join("\n"));
|
|
152
155
|
return [NOTES_BRIEF, ...sections].join("\n\n");
|
|
153
156
|
}
|
|
157
|
+
/** No brief: pasted into a prompt the developer writes around it. */
|
|
158
|
+
function noteAsContext(note) {
|
|
159
|
+
const context = elementContext(note).join("\n");
|
|
160
|
+
return note.note ? `${note.note}\n\n${context}` : context;
|
|
161
|
+
}
|
|
154
162
|
function boardFeedbackToPrompt(item) {
|
|
155
163
|
const selections = item.context?.selections ?? [];
|
|
156
164
|
const comments = selections.map((selection) => selection.comment).filter(Boolean);
|
|
@@ -352,17 +360,26 @@ function FeedbackCard({ item, onOpenCode, onShowSelector }) {
|
|
|
352
360
|
}
|
|
353
361
|
//#endregion
|
|
354
362
|
//#region src/devtools/client/inbox/inbox-panel.tsx
|
|
355
|
-
|
|
363
|
+
const INBOX_SCOPES = ["page", "board"];
|
|
364
|
+
const SCOPE_LABELS = {
|
|
365
|
+
board: "Whole board",
|
|
366
|
+
page: "This page"
|
|
367
|
+
};
|
|
368
|
+
const EMPTY_SCOPE_MESSAGES = {
|
|
369
|
+
board: "The board has no feedback yet.",
|
|
370
|
+
page: "Nothing on the board points at this page yet."
|
|
371
|
+
};
|
|
372
|
+
function useBoardFeedback(scope) {
|
|
356
373
|
const [state, setState] = useState({ kind: "loading" });
|
|
357
374
|
const load = useCallback(async () => {
|
|
358
|
-
const pagePath = window.location.pathname;
|
|
375
|
+
const pagePath = scope === "page" ? window.location.pathname : null;
|
|
359
376
|
setState({ kind: "loading" });
|
|
360
377
|
try {
|
|
361
|
-
const
|
|
378
|
+
const page = await fetchBoardFeedback(pagePath);
|
|
362
379
|
setState({
|
|
363
|
-
items,
|
|
364
380
|
kind: "ready",
|
|
365
|
-
|
|
381
|
+
page,
|
|
382
|
+
place: pagePath ?? "the board"
|
|
366
383
|
});
|
|
367
384
|
} catch (error) {
|
|
368
385
|
setState({
|
|
@@ -370,7 +387,7 @@ function useBoardFeedbackForPage() {
|
|
|
370
387
|
message: error instanceof Error ? error.message : "Could not reach the board."
|
|
371
388
|
});
|
|
372
389
|
}
|
|
373
|
-
}, []);
|
|
390
|
+
}, [scope]);
|
|
374
391
|
useEffect(() => {
|
|
375
392
|
load();
|
|
376
393
|
}, [load]);
|
|
@@ -379,14 +396,14 @@ function useBoardFeedbackForPage() {
|
|
|
379
396
|
state
|
|
380
397
|
};
|
|
381
398
|
}
|
|
382
|
-
function
|
|
383
|
-
const { load, state } =
|
|
399
|
+
function BoardFeedbackList({ onOpenCode, onShowSelector, onShowWholeBoard, scope }) {
|
|
400
|
+
const { load, state } = useBoardFeedback(scope);
|
|
384
401
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
385
402
|
/* @__PURE__ */ jsxs("div", {
|
|
386
403
|
className: "dt-card-head",
|
|
387
404
|
children: [/* @__PURE__ */ jsx("p", {
|
|
388
405
|
className: "dt-status",
|
|
389
|
-
children: state.kind === "ready" ? `${state.
|
|
406
|
+
children: state.kind === "ready" ? `${state.page.total} on ${state.place}` : "Board feedback"
|
|
390
407
|
}), /* @__PURE__ */ jsx("button", {
|
|
391
408
|
className: "dt-btn",
|
|
392
409
|
disabled: state.kind === "loading",
|
|
@@ -403,19 +420,43 @@ function InboxPanel({ onOpenCode, onShowSelector }) {
|
|
|
403
420
|
className: "dt-error",
|
|
404
421
|
children: state.message
|
|
405
422
|
}),
|
|
406
|
-
state.kind === "ready" && state.items.length === 0 && /* @__PURE__ */
|
|
423
|
+
state.kind === "ready" && state.page.items.length === 0 && /* @__PURE__ */ jsxs("div", {
|
|
407
424
|
className: "dt-empty",
|
|
408
|
-
children: /* @__PURE__ */ jsx("p", { children:
|
|
425
|
+
children: [/* @__PURE__ */ jsx("p", { children: EMPTY_SCOPE_MESSAGES[scope] }), scope === "page" && /* @__PURE__ */ jsx("button", {
|
|
426
|
+
className: "dt-btn",
|
|
427
|
+
onClick: onShowWholeBoard,
|
|
428
|
+
type: "button",
|
|
429
|
+
children: "Show the whole board"
|
|
430
|
+
})]
|
|
409
431
|
}),
|
|
410
|
-
state.kind === "ready" && state.items.map((item) => /* @__PURE__ */ jsx(FeedbackCard, {
|
|
432
|
+
state.kind === "ready" && state.page.items.map((item) => /* @__PURE__ */ jsx(FeedbackCard, {
|
|
411
433
|
item,
|
|
412
434
|
onOpenCode,
|
|
413
435
|
onShowSelector
|
|
414
436
|
}, item.id))
|
|
415
437
|
] });
|
|
416
438
|
}
|
|
439
|
+
function InboxPanel({ onOpenCode, onShowSelector }) {
|
|
440
|
+
const [scope, setScope] = useState("page");
|
|
441
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
|
|
442
|
+
className: "dt-tabs",
|
|
443
|
+
children: INBOX_SCOPES.map((option) => /* @__PURE__ */ jsx("button", {
|
|
444
|
+
"aria-pressed": option === scope,
|
|
445
|
+
className: "dt-tab",
|
|
446
|
+
onClick: () => setScope(option),
|
|
447
|
+
type: "button",
|
|
448
|
+
children: SCOPE_LABELS[option]
|
|
449
|
+
}, option))
|
|
450
|
+
}), /* @__PURE__ */ jsx(BoardFeedbackList, {
|
|
451
|
+
onOpenCode,
|
|
452
|
+
onShowSelector,
|
|
453
|
+
onShowWholeBoard: () => setScope("board"),
|
|
454
|
+
scope
|
|
455
|
+
}, scope)] });
|
|
456
|
+
}
|
|
417
457
|
//#endregion
|
|
418
458
|
//#region src/devtools/client/notices.tsx
|
|
459
|
+
const STATUS_VISIBLE_MS = 2400;
|
|
419
460
|
let notices = [];
|
|
420
461
|
let nextNoticeId = 1;
|
|
421
462
|
const listeners = /* @__PURE__ */ new Set();
|
|
@@ -431,10 +472,20 @@ function reportFailure(action, error, retry) {
|
|
|
431
472
|
const message = `${action}: ${error instanceof Error ? error.message : "Unknown error."}`;
|
|
432
473
|
publish([...notices.filter((notice) => notice.message !== message), {
|
|
433
474
|
id: nextNoticeId++,
|
|
475
|
+
kind: "failure",
|
|
434
476
|
message,
|
|
435
477
|
retry
|
|
436
478
|
}]);
|
|
437
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
|
+
}
|
|
438
489
|
function subscribe(listener) {
|
|
439
490
|
listeners.add(listener);
|
|
440
491
|
return () => listeners.delete(listener);
|
|
@@ -446,7 +497,8 @@ function Notices() {
|
|
|
446
497
|
className: "dt-notices",
|
|
447
498
|
children: current.map((notice) => /* @__PURE__ */ jsxs("div", {
|
|
448
499
|
className: "dt-notice glass",
|
|
449
|
-
|
|
500
|
+
"data-kind": notice.kind,
|
|
501
|
+
role: notice.kind === "failure" ? "alert" : "status",
|
|
450
502
|
children: [
|
|
451
503
|
/* @__PURE__ */ jsx("p", { children: notice.message }),
|
|
452
504
|
notice.retry && /* @__PURE__ */ jsx("button", {
|
|
@@ -601,6 +653,18 @@ function BoardTab({ canConnect, onOpenCode, onShowSelector, publicKey, refresh,
|
|
|
601
653
|
}), inbox] });
|
|
602
654
|
}
|
|
603
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
|
|
604
668
|
//#region src/devtools/client/code/editor-url.ts
|
|
605
669
|
const fileSchemeLink = (scheme) => (absolutePath, line, column) => `${scheme}://file${encodeURI(absolutePath)}:${line}:${column}`;
|
|
606
670
|
const EDITOR_LINKS = {
|
|
@@ -725,6 +789,7 @@ function InboxIcon() {
|
|
|
725
789
|
//#endregion
|
|
726
790
|
//#region src/devtools/client/devtools-bar.tsx
|
|
727
791
|
const BAR_POSITION_KEY = "reflet-devtools-bar-position";
|
|
792
|
+
const PICK_BUTTON_LABEL = "Pick an element: note it, copy it, or Shift+click to open its code";
|
|
728
793
|
function BarButton({ children, label, onClick, pressed }) {
|
|
729
794
|
return /* @__PURE__ */ jsx("button", {
|
|
730
795
|
"aria-label": label,
|
|
@@ -761,7 +826,7 @@ function DevtoolsBar({ edge, noteCount, onPick, onToggleSheet, openTab, side })
|
|
|
761
826
|
children: /* @__PURE__ */ jsx(GripIcon, {})
|
|
762
827
|
}),
|
|
763
828
|
/* @__PURE__ */ jsx(BarButton, {
|
|
764
|
-
label:
|
|
829
|
+
label: PICK_BUTTON_LABEL,
|
|
765
830
|
onClick: onPick,
|
|
766
831
|
pressed: false,
|
|
767
832
|
children: /* @__PURE__ */ jsx(TargetIcon, {})
|
|
@@ -776,7 +841,7 @@ function DevtoolsBar({ edge, noteCount, onPick, onToggleSheet, openTab, side })
|
|
|
776
841
|
})]
|
|
777
842
|
}),
|
|
778
843
|
/* @__PURE__ */ jsx(BarButton, {
|
|
779
|
-
label: "Board feedback
|
|
844
|
+
label: "Board feedback",
|
|
780
845
|
onClick: () => onToggleSheet("inbox"),
|
|
781
846
|
pressed: openTab === "inbox",
|
|
782
847
|
children: /* @__PURE__ */ jsx(InboxIcon, {})
|
|
@@ -967,7 +1032,7 @@ function DevtoolsSheet({ children, onClose, onPick, onSelectTab, side, tab, tabs
|
|
|
967
1032
|
...floating.handleProps
|
|
968
1033
|
}),
|
|
969
1034
|
/* @__PURE__ */ jsx("button", {
|
|
970
|
-
"aria-label":
|
|
1035
|
+
"aria-label": PICK_BUTTON_LABEL,
|
|
971
1036
|
className: "icon-btn",
|
|
972
1037
|
onClick: onPick,
|
|
973
1038
|
title: "Pick an element",
|
|
@@ -1043,6 +1108,7 @@ const DEVTOOLS_STYLES = `
|
|
|
1043
1108
|
display: flex; align-items: center; gap: 6px; padding: 6px 6px 6px 14px; border-radius: 12px;
|
|
1044
1109
|
color: var(--rf-danger); font-size: 12.5px; animation: dt-notice-in 220ms var(--rf-ease);
|
|
1045
1110
|
}
|
|
1111
|
+
.dt-notice[data-kind="status"] { color: var(--rf-text); }
|
|
1046
1112
|
.dt-notice p { flex: 1; margin: 0; overflow-wrap: anywhere; }
|
|
1047
1113
|
.dt-notice .icon-btn { width: 30px; height: 30px; }
|
|
1048
1114
|
.dt-bar {
|
|
@@ -1105,7 +1171,7 @@ const DEVTOOLS_STYLES = `
|
|
|
1105
1171
|
.dt-tabs { display: flex; gap: 2px; margin-right: auto; }
|
|
1106
1172
|
.dt-tab { height: 30px; padding: 0 11px; border-radius: var(--rf-pill); color: var(--rf-text-muted); font-weight: 500; }
|
|
1107
1173
|
.dt-tab:hover { color: var(--rf-text); }
|
|
1108
|
-
.dt-tab[aria-selected="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
|
|
1174
|
+
.dt-tab[aria-selected="true"], .dt-tab[aria-pressed="true"] { background: var(--rf-bg-hover); color: var(--rf-text); }
|
|
1109
1175
|
.dt-sheet-body { flex: 1; min-height: 0; overflow: auto; display: flex; flex-direction: column; gap: 8px; padding: 10px; }
|
|
1110
1176
|
.dt-sheet-foot {
|
|
1111
1177
|
position: sticky; bottom: -10px; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
|
|
@@ -3268,6 +3334,24 @@ async function describeSource(source, canReadSource) {
|
|
|
3268
3334
|
}
|
|
3269
3335
|
return withPosition(shortenSourcePath(source.fileName), source);
|
|
3270
3336
|
}
|
|
3337
|
+
async function locateSource(element, canReadSource) {
|
|
3338
|
+
const source = await locateElementSource(element).catch(() => null);
|
|
3339
|
+
if (!source) return null;
|
|
3340
|
+
return {
|
|
3341
|
+
source,
|
|
3342
|
+
sourceLocation: await describeSource(source, canReadSource)
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
function withSource(note, located) {
|
|
3346
|
+
return {
|
|
3347
|
+
...note,
|
|
3348
|
+
selection: {
|
|
3349
|
+
...note.selection,
|
|
3350
|
+
sourceLocation: located.sourceLocation
|
|
3351
|
+
},
|
|
3352
|
+
source: located.source
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3271
3355
|
/** `crypto.randomUUID` only exists in secure contexts; a phone on the LAN IP is not one. */
|
|
3272
3356
|
function newNoteId() {
|
|
3273
3357
|
const randomBytes = crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(8));
|
|
@@ -3289,6 +3373,12 @@ function draftNote(element, note) {
|
|
|
3289
3373
|
source: null
|
|
3290
3374
|
};
|
|
3291
3375
|
}
|
|
3376
|
+
/** The whole note in one go, source included: a copy has no stored note to fill in later. */
|
|
3377
|
+
async function draftLocatedNote(element, note, canReadSource) {
|
|
3378
|
+
const draft = draftNote(element, note);
|
|
3379
|
+
const located = await locateSource(element, canReadSource);
|
|
3380
|
+
return located ? withSource(draft, located) : draft;
|
|
3381
|
+
}
|
|
3292
3382
|
const locateAttempts = /* @__PURE__ */ new Set();
|
|
3293
3383
|
/** Once per note per page load: a note whose source cannot be found is not retried in a loop. */
|
|
3294
3384
|
function hasTriedToLocate(noteId) {
|
|
@@ -3303,17 +3393,9 @@ async function enrichNote(noteId, element, canReadSource) {
|
|
|
3303
3393
|
noteStore.setActivity(noteId, "locating");
|
|
3304
3394
|
let closeUp = null;
|
|
3305
3395
|
try {
|
|
3306
|
-
const [
|
|
3396
|
+
const [located, snapshot] = await Promise.all([locateSource(element, canReadSource), captureElementSnapshot(element).catch(() => null)]);
|
|
3307
3397
|
closeUp = snapshot;
|
|
3308
|
-
|
|
3309
|
-
if (source && sourceLocation) await noteStore.patch(noteId, (note) => ({
|
|
3310
|
-
...note,
|
|
3311
|
-
selection: {
|
|
3312
|
-
...note.selection,
|
|
3313
|
-
sourceLocation
|
|
3314
|
-
},
|
|
3315
|
-
source
|
|
3316
|
-
}));
|
|
3398
|
+
if (located) await noteStore.patch(noteId, (note) => withSource(note, located));
|
|
3317
3399
|
if (closeUp) await noteStore.attachCloseUp(noteId, {
|
|
3318
3400
|
blob: closeUp.blob,
|
|
3319
3401
|
height: closeUp.height,
|
|
@@ -3910,7 +3992,7 @@ const PICKER_LABELS = {
|
|
|
3910
3992
|
attachElement: "Save note",
|
|
3911
3993
|
elementNote: "Dev note",
|
|
3912
3994
|
elementNotePlaceholder: "What should change here?",
|
|
3913
|
-
pickElementHint: "Pick an element to note it"
|
|
3995
|
+
pickElementHint: "Pick an element to note or copy it"
|
|
3914
3996
|
};
|
|
3915
3997
|
function titleFor(element) {
|
|
3916
3998
|
const owner = resolveComponentStack(getFiberFromNode(element))[0];
|
|
@@ -4029,6 +4111,14 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
|
|
|
4029
4111
|
setIsPicking(false);
|
|
4030
4112
|
saveNote(draftNote(element, text), element, canReadSource);
|
|
4031
4113
|
}, [canReadSource]);
|
|
4114
|
+
const copy = useMemo(() => ({
|
|
4115
|
+
label: "Copy",
|
|
4116
|
+
onCopy: (element, text) => {
|
|
4117
|
+
setIsPicking(false);
|
|
4118
|
+
const title = titleFor(element);
|
|
4119
|
+
copyToClipboard(draftLocatedNote(element, text, canReadSource).then(noteAsContext)).then(() => announce(`Copied ${title}`)).catch((error) => reportFailure(`${title} was not copied`, error));
|
|
4120
|
+
}
|
|
4121
|
+
}), [canReadSource]);
|
|
4032
4122
|
return /* @__PURE__ */ jsxs("div", {
|
|
4033
4123
|
className: "dt",
|
|
4034
4124
|
"data-recessed": isRecessed,
|
|
@@ -4083,6 +4173,7 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
|
|
|
4083
4173
|
openTab && /* @__PURE__ */ jsx(SelectionOutline, { node: highlighted }),
|
|
4084
4174
|
/* @__PURE__ */ jsx(Notices, {}),
|
|
4085
4175
|
isPicking && /* @__PURE__ */ jsx(ElementPicker, {
|
|
4176
|
+
copy,
|
|
4086
4177
|
inspect,
|
|
4087
4178
|
labels: PICKER_LABELS,
|
|
4088
4179
|
onCancel: cancelPick,
|
|
@@ -4094,4 +4185,4 @@ function DevtoolsLayer({ isWidgetOpen, position, publicKey }) {
|
|
|
4094
4185
|
//#endregion
|
|
4095
4186
|
export { DevtoolsLayer };
|
|
4096
4187
|
|
|
4097
|
-
//# sourceMappingURL=devtools-layer-
|
|
4188
|
+
//# sourceMappingURL=devtools-layer-DtO89RlJ.js.map
|