@fluixi/devtools 0.2.0-alpha.3 → 0.2.0-alpha.5
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/dist/callsite.cjs +72 -0
- package/dist/callsite.d.ts +1 -1
- package/dist/callsite.js +1 -1
- package/dist/callsite.mjs +51 -0
- package/dist/change-view.cjs +134 -0
- package/dist/change-view.js +2 -2
- package/dist/change-view.mjs +111 -0
- package/dist/copy.cjs +63 -0
- package/dist/copy.mjs +42 -0
- package/dist/describe.cjs +55 -0
- package/dist/describe.js +2 -2
- package/dist/describe.mjs +34 -0
- package/dist/detail-panel.cjs +307 -0
- package/dist/detail-panel.d.ts +1 -1
- package/dist/detail-panel.d.ts.map +1 -1
- package/dist/detail-panel.js +1 -1
- package/dist/detail-panel.mjs +284 -0
- package/dist/diff.cjs +225 -0
- package/dist/diff.js +1 -1
- package/dist/diff.mjs +202 -0
- package/dist/dom.cjs +204 -0
- package/dist/dom.mjs +183 -0
- package/dist/export.cjs +221 -0
- package/dist/export.d.ts +2 -2
- package/dist/export.d.ts.map +1 -1
- package/dist/export.js +1 -1
- package/dist/export.mjs +209 -0
- package/dist/float.cjs +232 -0
- package/dist/float.d.ts +1 -1
- package/dist/float.js +3 -3
- package/dist/float.mjs +211 -0
- package/dist/format.cjs +175 -0
- package/dist/format.js +3 -3
- package/dist/format.mjs +154 -0
- package/dist/graph-view.cjs +893 -0
- package/dist/graph-view.mjs +870 -0
- package/dist/highlight.cjs +140 -0
- package/dist/highlight.mjs +119 -0
- package/dist/history.cjs +95 -0
- package/dist/history.d.ts +2 -2
- package/dist/history.js +2 -2
- package/dist/history.mjs +74 -0
- package/dist/hook.cjs +1562 -0
- package/dist/hook.d.ts +3 -3
- package/dist/hook.js +6 -6
- package/dist/hook.mjs +1553 -0
- package/dist/index.cjs +4552 -0
- package/dist/index.mjs +4544 -0
- package/dist/inspector.cjs +688 -0
- package/dist/inspector.d.ts +2 -2
- package/dist/inspector.js +7 -7
- package/dist/inspector.mjs +676 -0
- package/dist/instrument.cjs +497 -0
- package/dist/instrument.mjs +487 -0
- package/dist/kind-filter.cjs +172 -0
- package/dist/kind-filter.d.ts +2 -2
- package/dist/kind-filter.js +1 -1
- package/dist/kind-filter.mjs +151 -0
- package/dist/menu.cjs +141 -0
- package/dist/menu.mjs +120 -0
- package/dist/observe.cjs +1352 -0
- package/dist/observe.mjs +1345 -0
- package/dist/palette.cjs +52 -0
- package/dist/palette.mjs +31 -0
- package/dist/path.cjs +45 -0
- package/dist/path.d.ts +2 -2
- package/dist/path.js +2 -2
- package/dist/path.mjs +24 -0
- package/dist/plugin.cjs +87 -0
- package/dist/plugin.mjs +66 -0
- package/dist/plugins/context.cjs +98 -0
- package/dist/plugins/context.mjs +75 -0
- package/dist/plugins/directives.cjs +98 -0
- package/dist/plugins/directives.mjs +75 -0
- package/dist/plugins/index.cjs +162 -0
- package/dist/plugins/index.mjs +140 -0
- package/dist/source.cjs +111 -0
- package/dist/source.d.ts +2 -2
- package/dist/source.js +4 -4
- package/dist/source.mjs +90 -0
- package/dist/timeline.cjs +350 -0
- package/dist/timeline.js +3 -3
- package/dist/timeline.mjs +327 -0
- package/dist/tree.cjs +574 -0
- package/dist/tree.mjs +551 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/value-pane.cjs +223 -0
- package/dist/value-pane.mjs +200 -0
- package/dist/wire.cjs +109 -0
- package/dist/wire.mjs +89 -0
- package/package.json +19 -6
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/callsite.ts
|
|
21
|
+
var callsite_exports = {};
|
|
22
|
+
__export(callsite_exports, {
|
|
23
|
+
callSite: () => callSite,
|
|
24
|
+
currentCallSite: () => currentCallSite
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(callsite_exports);
|
|
27
|
+
var EXTENSION = ["chrome-extension://", "moz-extension://", "safari-web-extension://", "extension://"];
|
|
28
|
+
var INTERNAL = [
|
|
29
|
+
"/packages/reactive/",
|
|
30
|
+
"/packages/dom/",
|
|
31
|
+
"/packages/core/",
|
|
32
|
+
"/packages/devtools/",
|
|
33
|
+
"/@fluixi/",
|
|
34
|
+
"node_modules",
|
|
35
|
+
"fluixi-devtools",
|
|
36
|
+
...EXTENSION
|
|
37
|
+
];
|
|
38
|
+
var REPORTING = [
|
|
39
|
+
"/packages/reactive/src/lib/signal/graph/",
|
|
40
|
+
"/packages/devtools/",
|
|
41
|
+
"fluixi-devtools",
|
|
42
|
+
// Not a fallback either: naming the agent as where a node came from is worse than nothing.
|
|
43
|
+
...EXTENSION
|
|
44
|
+
];
|
|
45
|
+
var FRAME = /(?:at\s+(?:[^(]*\()?)([^()\s]+):(\d+):(\d+)\)?$/;
|
|
46
|
+
var isInternal = (frame) => INTERNAL.some((part) => frame.includes(part));
|
|
47
|
+
function locate(frame, origin) {
|
|
48
|
+
const match = FRAME.exec(frame);
|
|
49
|
+
if (!match) return void 0;
|
|
50
|
+
let file = match[1];
|
|
51
|
+
if (origin && file.startsWith(origin)) file = file.slice(origin.length);
|
|
52
|
+
file = file.replace(/^\/@fs\//, "/").replace(/\?.*$/, "").replace(/^\//, "");
|
|
53
|
+
return file ? { file, line: Number(match[2]), column: Number(match[3]) } : void 0;
|
|
54
|
+
}
|
|
55
|
+
function callSite(stack, origin) {
|
|
56
|
+
if (!stack) return void 0;
|
|
57
|
+
let fallback;
|
|
58
|
+
for (const line of stack.split("\n")) {
|
|
59
|
+
const frame = line.trim();
|
|
60
|
+
if (!frame.startsWith("at ")) continue;
|
|
61
|
+
if (!isInternal(frame)) {
|
|
62
|
+
const location = locate(frame, origin);
|
|
63
|
+
if (location) return { location, external: false };
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (!fallback && !REPORTING.some((part) => frame.includes(part))) fallback = locate(frame, origin);
|
|
67
|
+
}
|
|
68
|
+
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
69
|
+
}
|
|
70
|
+
function currentCallSite(origin) {
|
|
71
|
+
return callSite(new Error().stack, origin);
|
|
72
|
+
}
|
package/dist/callsite.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { ReactiveSourceLocation } from './source.js';
|
|
|
3
3
|
* Where a node came from, and whether that is the application or something it depends on.
|
|
4
4
|
*
|
|
5
5
|
* The application's own frame is the useful one, but a node made while a package was loading
|
|
6
|
-
* has no application frame above it at all
|
|
6
|
+
* has no application frame above it at all: a router's revalidation counter, made when its
|
|
7
7
|
* module is evaluated. Falling back to the package's own frame is what stops that node being
|
|
8
8
|
* a card with no name, no origin and no explanation.
|
|
9
9
|
*/
|
package/dist/callsite.js
CHANGED
|
@@ -49,7 +49,7 @@ function locate(frame, origin) {
|
|
|
49
49
|
* Where a node came from, and whether that is the application or something it depends on.
|
|
50
50
|
*
|
|
51
51
|
* The application's own frame is the useful one, but a node made while a package was loading
|
|
52
|
-
* has no application frame above it at all
|
|
52
|
+
* has no application frame above it at all: a router's revalidation counter, made when its
|
|
53
53
|
* module is evaluated. Falling back to the package's own frame is what stops that node being
|
|
54
54
|
* a card with no name, no origin and no explanation.
|
|
55
55
|
*/
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/callsite.ts
|
|
2
|
+
var EXTENSION = ["chrome-extension://", "moz-extension://", "safari-web-extension://", "extension://"];
|
|
3
|
+
var INTERNAL = [
|
|
4
|
+
"/packages/reactive/",
|
|
5
|
+
"/packages/dom/",
|
|
6
|
+
"/packages/core/",
|
|
7
|
+
"/packages/devtools/",
|
|
8
|
+
"/@fluixi/",
|
|
9
|
+
"node_modules",
|
|
10
|
+
"fluixi-devtools",
|
|
11
|
+
...EXTENSION
|
|
12
|
+
];
|
|
13
|
+
var REPORTING = [
|
|
14
|
+
"/packages/reactive/src/lib/signal/graph/",
|
|
15
|
+
"/packages/devtools/",
|
|
16
|
+
"fluixi-devtools",
|
|
17
|
+
// Not a fallback either: naming the agent as where a node came from is worse than nothing.
|
|
18
|
+
...EXTENSION
|
|
19
|
+
];
|
|
20
|
+
var FRAME = /(?:at\s+(?:[^(]*\()?)([^()\s]+):(\d+):(\d+)\)?$/;
|
|
21
|
+
var isInternal = (frame) => INTERNAL.some((part) => frame.includes(part));
|
|
22
|
+
function locate(frame, origin) {
|
|
23
|
+
const match = FRAME.exec(frame);
|
|
24
|
+
if (!match) return void 0;
|
|
25
|
+
let file = match[1];
|
|
26
|
+
if (origin && file.startsWith(origin)) file = file.slice(origin.length);
|
|
27
|
+
file = file.replace(/^\/@fs\//, "/").replace(/\?.*$/, "").replace(/^\//, "");
|
|
28
|
+
return file ? { file, line: Number(match[2]), column: Number(match[3]) } : void 0;
|
|
29
|
+
}
|
|
30
|
+
function callSite(stack, origin) {
|
|
31
|
+
if (!stack) return void 0;
|
|
32
|
+
let fallback;
|
|
33
|
+
for (const line of stack.split("\n")) {
|
|
34
|
+
const frame = line.trim();
|
|
35
|
+
if (!frame.startsWith("at ")) continue;
|
|
36
|
+
if (!isInternal(frame)) {
|
|
37
|
+
const location = locate(frame, origin);
|
|
38
|
+
if (location) return { location, external: false };
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (!fallback && !REPORTING.some((part) => frame.includes(part))) fallback = locate(frame, origin);
|
|
42
|
+
}
|
|
43
|
+
return fallback ? { location: { ...fallback, external: true }, external: true } : void 0;
|
|
44
|
+
}
|
|
45
|
+
function currentCallSite(origin) {
|
|
46
|
+
return callSite(new Error().stack, origin);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
callSite,
|
|
50
|
+
currentCallSite
|
|
51
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/change-view.ts
|
|
21
|
+
var change_view_exports = {};
|
|
22
|
+
__export(change_view_exports, {
|
|
23
|
+
CHANGE_CSS: () => CHANGE_CSS,
|
|
24
|
+
changeRows: () => changeRows,
|
|
25
|
+
handleCopyClick: () => handleCopyClick
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(change_view_exports);
|
|
28
|
+
|
|
29
|
+
// src/copy.ts
|
|
30
|
+
function confirmOn(button, said = "copied") {
|
|
31
|
+
if (!button) return;
|
|
32
|
+
const was = button.textContent;
|
|
33
|
+
button.textContent = said;
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
button.textContent = was;
|
|
36
|
+
}, 1200);
|
|
37
|
+
}
|
|
38
|
+
function fallback(text, button) {
|
|
39
|
+
try {
|
|
40
|
+
const doc = button?.ownerDocument ?? document;
|
|
41
|
+
const area = doc.createElement("textarea");
|
|
42
|
+
area.value = text;
|
|
43
|
+
area.setAttribute("readonly", "");
|
|
44
|
+
area.style.position = "fixed";
|
|
45
|
+
area.style.opacity = "0";
|
|
46
|
+
doc.body.appendChild(area);
|
|
47
|
+
area.select();
|
|
48
|
+
doc.execCommand("copy");
|
|
49
|
+
area.remove();
|
|
50
|
+
confirmOn(button);
|
|
51
|
+
return true;
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async function copyText(text, button) {
|
|
57
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
58
|
+
if (clipboard?.writeText) {
|
|
59
|
+
try {
|
|
60
|
+
await clipboard.writeText(text);
|
|
61
|
+
confirmOn(button);
|
|
62
|
+
return true;
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return fallback(text, button);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/change-view.ts
|
|
70
|
+
var escape = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
71
|
+
var SIGN = { added: "+", removed: "−", changed: "~" };
|
|
72
|
+
function pretty(json) {
|
|
73
|
+
if (!json) return void 0;
|
|
74
|
+
try {
|
|
75
|
+
return JSON.stringify(JSON.parse(json), null, 2);
|
|
76
|
+
} catch {
|
|
77
|
+
return void 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function sideOf(text, json, cls) {
|
|
81
|
+
const block = pretty(json);
|
|
82
|
+
if (!block) return `<span class="ch-${cls}">${escape(text ?? "")}</span>`;
|
|
83
|
+
return `<div class="ch-block ch-${cls}"><button type="button" class="ch-copy" title="Copy" data-copy="${escape(block)}">copy</button><pre class="ch-json">${escape(block)}</pre></div>`;
|
|
84
|
+
}
|
|
85
|
+
function changeRows(change, limit = Infinity) {
|
|
86
|
+
if (!change?.entries?.length) return "";
|
|
87
|
+
const shown = change.entries.slice(0, limit);
|
|
88
|
+
const hidden = change.entries.length - shown.length + (change.more ?? 0);
|
|
89
|
+
const rows = shown.map((entry) => {
|
|
90
|
+
const from = sideOf(entry.from, entry.fromJson, "from");
|
|
91
|
+
const to = sideOf(entry.to, entry.toJson, "to");
|
|
92
|
+
const sides = entry.op === "changed" ? `${from}<span class="ch-arrow">→</span>${to}` : entry.op === "added" ? to : from;
|
|
93
|
+
const stacked = entry.fromJson || entry.toJson ? " ch-stacked" : "";
|
|
94
|
+
return `<div class="ch-row ch-${entry.op}${stacked}"><span class="ch-sign">${SIGN[entry.op] ?? "·"}</span><span class="ch-key">${escape(entry.key)}</span><span class="ch-sides">${sides}</span></div>`;
|
|
95
|
+
}).join("");
|
|
96
|
+
const size = change.length ? `<div class="ch-size">${change.length.from} → ${change.length.to} items</div>` : "";
|
|
97
|
+
return `<div class="ch-list">${size}${rows}${hidden > 0 ? `<div class="ch-more">and ${hidden} more</div>` : ""}</div>`;
|
|
98
|
+
}
|
|
99
|
+
function handleCopyClick(event) {
|
|
100
|
+
const button = event.target?.closest?.(".ch-copy");
|
|
101
|
+
if (!button) return false;
|
|
102
|
+
void copyText(button.dataset.copy ?? "", button);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
var CHANGE_CSS = `
|
|
106
|
+
.ch-list { margin-top: 4px; font-family: ui-monospace, monospace; font-size: 11px; }
|
|
107
|
+
.ch-size { color: var(--fx-muted, #8a90a2); margin-bottom: 2px; }
|
|
108
|
+
.ch-row { display: flex; gap: 6px; align-items: baseline; padding: 1px 0; }
|
|
109
|
+
.ch-row.ch-stacked { align-items: flex-start; padding: 3px 0; }
|
|
110
|
+
.ch-sign { flex: none; width: 9px; text-align: center; }
|
|
111
|
+
.ch-added .ch-sign { color: var(--state, #34d399); }
|
|
112
|
+
.ch-removed .ch-sign { color: var(--fx-write, #fb7185); }
|
|
113
|
+
.ch-changed .ch-sign { color: var(--effect, #fbbf24); }
|
|
114
|
+
.ch-key { flex: none; color: var(--fx-muted, #8a90a2); }
|
|
115
|
+
.ch-sides { min-width: 0; flex: 1; overflow-wrap: anywhere; }
|
|
116
|
+
.ch-stacked .ch-sides { display: flex; flex-direction: column; gap: 2px; }
|
|
117
|
+
.ch-from { color: var(--fx-muted, #8a90a2); }
|
|
118
|
+
.ch-row:not(.ch-stacked) .ch-from { text-decoration: line-through; }
|
|
119
|
+
.ch-arrow { color: var(--fx-muted, #8a90a2); margin: 0 4px; }
|
|
120
|
+
.ch-to { color: var(--fx-label, #e7e9ee); }
|
|
121
|
+
.ch-more { color: var(--fx-muted, #8a90a2); margin-top: 2px; }
|
|
122
|
+
/* In the corner: an object can be tall, and a button under it is a scroll away. */
|
|
123
|
+
.ch-block { position: relative; border-radius: 4px; border: 1px solid var(--fx-line, #ffffff14); }
|
|
124
|
+
.ch-block.ch-from { opacity: .72; }
|
|
125
|
+
.ch-json { margin: 0; padding: 5px 7px; overflow-x: auto; white-space: pre; font: inherit; }
|
|
126
|
+
.ch-copy {
|
|
127
|
+
position: absolute; top: 3px; right: 3px; padding: 1px 6px; cursor: pointer;
|
|
128
|
+
font: inherit; font-size: 9.5px; opacity: 0; transition: opacity .12s;
|
|
129
|
+
border: 1px solid var(--fx-line, #ffffff26); border-radius: 3px;
|
|
130
|
+
background: var(--fx-panel, #14161c); color: var(--fx-muted, #8a90a2);
|
|
131
|
+
}
|
|
132
|
+
.ch-block:hover .ch-copy, .ch-copy:focus { opacity: 1; }
|
|
133
|
+
.ch-copy:hover { color: var(--fx-label, #e7e9ee); }
|
|
134
|
+
`;
|
package/dist/change-view.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// A diff, drawn. Shared between the timeline's open row and the panel that opens from it
|
|
1
|
+
// A diff, drawn. Shared between the timeline's open row and the panel that opens from it ,
|
|
2
2
|
// same rows, cut to a handful in the sidebar and shown in full when there is room.
|
|
3
3
|
//
|
|
4
4
|
// An object is drawn as an object: `{id: 1, text: "go"}` on one line is what the cards
|
|
@@ -14,7 +14,7 @@ function pretty(json) {
|
|
|
14
14
|
return JSON.stringify(JSON.parse(json), null, 2);
|
|
15
15
|
}
|
|
16
16
|
catch {
|
|
17
|
-
// It came from JSON.stringify, so this should not happen
|
|
17
|
+
// It came from JSON.stringify, so this should not happen, but a malformed payload is
|
|
18
18
|
// not worth losing the row over.
|
|
19
19
|
return undefined;
|
|
20
20
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/copy.ts
|
|
2
|
+
function confirmOn(button, said = "copied") {
|
|
3
|
+
if (!button) return;
|
|
4
|
+
const was = button.textContent;
|
|
5
|
+
button.textContent = said;
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
button.textContent = was;
|
|
8
|
+
}, 1200);
|
|
9
|
+
}
|
|
10
|
+
function fallback(text, button) {
|
|
11
|
+
try {
|
|
12
|
+
const doc = button?.ownerDocument ?? document;
|
|
13
|
+
const area = doc.createElement("textarea");
|
|
14
|
+
area.value = text;
|
|
15
|
+
area.setAttribute("readonly", "");
|
|
16
|
+
area.style.position = "fixed";
|
|
17
|
+
area.style.opacity = "0";
|
|
18
|
+
doc.body.appendChild(area);
|
|
19
|
+
area.select();
|
|
20
|
+
doc.execCommand("copy");
|
|
21
|
+
area.remove();
|
|
22
|
+
confirmOn(button);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function copyText(text, button) {
|
|
29
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
30
|
+
if (clipboard?.writeText) {
|
|
31
|
+
try {
|
|
32
|
+
await clipboard.writeText(text);
|
|
33
|
+
confirmOn(button);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return fallback(text, button);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/change-view.ts
|
|
42
|
+
var escape = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
43
|
+
var SIGN = { added: "+", removed: "−", changed: "~" };
|
|
44
|
+
function pretty(json) {
|
|
45
|
+
if (!json) return void 0;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.stringify(JSON.parse(json), null, 2);
|
|
48
|
+
} catch {
|
|
49
|
+
return void 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function sideOf(text, json, cls) {
|
|
53
|
+
const block = pretty(json);
|
|
54
|
+
if (!block) return `<span class="ch-${cls}">${escape(text ?? "")}</span>`;
|
|
55
|
+
return `<div class="ch-block ch-${cls}"><button type="button" class="ch-copy" title="Copy" data-copy="${escape(block)}">copy</button><pre class="ch-json">${escape(block)}</pre></div>`;
|
|
56
|
+
}
|
|
57
|
+
function changeRows(change, limit = Infinity) {
|
|
58
|
+
if (!change?.entries?.length) return "";
|
|
59
|
+
const shown = change.entries.slice(0, limit);
|
|
60
|
+
const hidden = change.entries.length - shown.length + (change.more ?? 0);
|
|
61
|
+
const rows = shown.map((entry) => {
|
|
62
|
+
const from = sideOf(entry.from, entry.fromJson, "from");
|
|
63
|
+
const to = sideOf(entry.to, entry.toJson, "to");
|
|
64
|
+
const sides = entry.op === "changed" ? `${from}<span class="ch-arrow">→</span>${to}` : entry.op === "added" ? to : from;
|
|
65
|
+
const stacked = entry.fromJson || entry.toJson ? " ch-stacked" : "";
|
|
66
|
+
return `<div class="ch-row ch-${entry.op}${stacked}"><span class="ch-sign">${SIGN[entry.op] ?? "·"}</span><span class="ch-key">${escape(entry.key)}</span><span class="ch-sides">${sides}</span></div>`;
|
|
67
|
+
}).join("");
|
|
68
|
+
const size = change.length ? `<div class="ch-size">${change.length.from} → ${change.length.to} items</div>` : "";
|
|
69
|
+
return `<div class="ch-list">${size}${rows}${hidden > 0 ? `<div class="ch-more">and ${hidden} more</div>` : ""}</div>`;
|
|
70
|
+
}
|
|
71
|
+
function handleCopyClick(event) {
|
|
72
|
+
const button = event.target?.closest?.(".ch-copy");
|
|
73
|
+
if (!button) return false;
|
|
74
|
+
void copyText(button.dataset.copy ?? "", button);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
var CHANGE_CSS = `
|
|
78
|
+
.ch-list { margin-top: 4px; font-family: ui-monospace, monospace; font-size: 11px; }
|
|
79
|
+
.ch-size { color: var(--fx-muted, #8a90a2); margin-bottom: 2px; }
|
|
80
|
+
.ch-row { display: flex; gap: 6px; align-items: baseline; padding: 1px 0; }
|
|
81
|
+
.ch-row.ch-stacked { align-items: flex-start; padding: 3px 0; }
|
|
82
|
+
.ch-sign { flex: none; width: 9px; text-align: center; }
|
|
83
|
+
.ch-added .ch-sign { color: var(--state, #34d399); }
|
|
84
|
+
.ch-removed .ch-sign { color: var(--fx-write, #fb7185); }
|
|
85
|
+
.ch-changed .ch-sign { color: var(--effect, #fbbf24); }
|
|
86
|
+
.ch-key { flex: none; color: var(--fx-muted, #8a90a2); }
|
|
87
|
+
.ch-sides { min-width: 0; flex: 1; overflow-wrap: anywhere; }
|
|
88
|
+
.ch-stacked .ch-sides { display: flex; flex-direction: column; gap: 2px; }
|
|
89
|
+
.ch-from { color: var(--fx-muted, #8a90a2); }
|
|
90
|
+
.ch-row:not(.ch-stacked) .ch-from { text-decoration: line-through; }
|
|
91
|
+
.ch-arrow { color: var(--fx-muted, #8a90a2); margin: 0 4px; }
|
|
92
|
+
.ch-to { color: var(--fx-label, #e7e9ee); }
|
|
93
|
+
.ch-more { color: var(--fx-muted, #8a90a2); margin-top: 2px; }
|
|
94
|
+
/* In the corner: an object can be tall, and a button under it is a scroll away. */
|
|
95
|
+
.ch-block { position: relative; border-radius: 4px; border: 1px solid var(--fx-line, #ffffff14); }
|
|
96
|
+
.ch-block.ch-from { opacity: .72; }
|
|
97
|
+
.ch-json { margin: 0; padding: 5px 7px; overflow-x: auto; white-space: pre; font: inherit; }
|
|
98
|
+
.ch-copy {
|
|
99
|
+
position: absolute; top: 3px; right: 3px; padding: 1px 6px; cursor: pointer;
|
|
100
|
+
font: inherit; font-size: 9.5px; opacity: 0; transition: opacity .12s;
|
|
101
|
+
border: 1px solid var(--fx-line, #ffffff26); border-radius: 3px;
|
|
102
|
+
background: var(--fx-panel, #14161c); color: var(--fx-muted, #8a90a2);
|
|
103
|
+
}
|
|
104
|
+
.ch-block:hover .ch-copy, .ch-copy:focus { opacity: 1; }
|
|
105
|
+
.ch-copy:hover { color: var(--fx-label, #e7e9ee); }
|
|
106
|
+
`;
|
|
107
|
+
export {
|
|
108
|
+
CHANGE_CSS,
|
|
109
|
+
changeRows,
|
|
110
|
+
handleCopyClick
|
|
111
|
+
};
|
package/dist/copy.cjs
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/copy.ts
|
|
21
|
+
var copy_exports = {};
|
|
22
|
+
__export(copy_exports, {
|
|
23
|
+
copyText: () => copyText
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(copy_exports);
|
|
26
|
+
function confirmOn(button, said = "copied") {
|
|
27
|
+
if (!button) return;
|
|
28
|
+
const was = button.textContent;
|
|
29
|
+
button.textContent = said;
|
|
30
|
+
setTimeout(() => {
|
|
31
|
+
button.textContent = was;
|
|
32
|
+
}, 1200);
|
|
33
|
+
}
|
|
34
|
+
function fallback(text, button) {
|
|
35
|
+
try {
|
|
36
|
+
const doc = button?.ownerDocument ?? document;
|
|
37
|
+
const area = doc.createElement("textarea");
|
|
38
|
+
area.value = text;
|
|
39
|
+
area.setAttribute("readonly", "");
|
|
40
|
+
area.style.position = "fixed";
|
|
41
|
+
area.style.opacity = "0";
|
|
42
|
+
doc.body.appendChild(area);
|
|
43
|
+
area.select();
|
|
44
|
+
doc.execCommand("copy");
|
|
45
|
+
area.remove();
|
|
46
|
+
confirmOn(button);
|
|
47
|
+
return true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function copyText(text, button) {
|
|
53
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
54
|
+
if (clipboard?.writeText) {
|
|
55
|
+
try {
|
|
56
|
+
await clipboard.writeText(text);
|
|
57
|
+
confirmOn(button);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return fallback(text, button);
|
|
63
|
+
}
|
package/dist/copy.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/copy.ts
|
|
2
|
+
function confirmOn(button, said = "copied") {
|
|
3
|
+
if (!button) return;
|
|
4
|
+
const was = button.textContent;
|
|
5
|
+
button.textContent = said;
|
|
6
|
+
setTimeout(() => {
|
|
7
|
+
button.textContent = was;
|
|
8
|
+
}, 1200);
|
|
9
|
+
}
|
|
10
|
+
function fallback(text, button) {
|
|
11
|
+
try {
|
|
12
|
+
const doc = button?.ownerDocument ?? document;
|
|
13
|
+
const area = doc.createElement("textarea");
|
|
14
|
+
area.value = text;
|
|
15
|
+
area.setAttribute("readonly", "");
|
|
16
|
+
area.style.position = "fixed";
|
|
17
|
+
area.style.opacity = "0";
|
|
18
|
+
doc.body.appendChild(area);
|
|
19
|
+
area.select();
|
|
20
|
+
doc.execCommand("copy");
|
|
21
|
+
area.remove();
|
|
22
|
+
confirmOn(button);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async function copyText(text, button) {
|
|
29
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
30
|
+
if (clipboard?.writeText) {
|
|
31
|
+
try {
|
|
32
|
+
await clipboard.writeText(text);
|
|
33
|
+
confirmOn(button);
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return fallback(text, button);
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
copyText
|
|
42
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/describe.ts
|
|
21
|
+
var describe_exports = {};
|
|
22
|
+
__export(describe_exports, {
|
|
23
|
+
describeNode: () => describeNode
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(describe_exports);
|
|
26
|
+
var MAX_CLASSES = 2;
|
|
27
|
+
function textOwner(node) {
|
|
28
|
+
const parent = node.parentElement;
|
|
29
|
+
return parent ? `text in ${describeElement(parent)}` : "text";
|
|
30
|
+
}
|
|
31
|
+
function describeElement(el) {
|
|
32
|
+
const tag = el.tagName.toLowerCase();
|
|
33
|
+
const id = el.id ? `#${el.id}` : "";
|
|
34
|
+
const raw = typeof el.className === "string" ? el.className : el.getAttribute("class") ?? "";
|
|
35
|
+
const classes = raw.split(/\s+/).filter(Boolean).slice(0, MAX_CLASSES).map((c) => `.${c}`).join("");
|
|
36
|
+
return `${tag}${id}${classes}`;
|
|
37
|
+
}
|
|
38
|
+
function describeNode(target) {
|
|
39
|
+
const node = target;
|
|
40
|
+
if (!node || typeof node.nodeType !== "number") return "detached";
|
|
41
|
+
switch (node.nodeType) {
|
|
42
|
+
case 1:
|
|
43
|
+
return describeElement(node);
|
|
44
|
+
case 3:
|
|
45
|
+
return textOwner(node);
|
|
46
|
+
case 8: {
|
|
47
|
+
const parent = node.parentElement;
|
|
48
|
+
return parent ? `slot in ${describeElement(parent)}` : "slot";
|
|
49
|
+
}
|
|
50
|
+
case 11:
|
|
51
|
+
return "fragment";
|
|
52
|
+
default:
|
|
53
|
+
return node.nodeName?.toLowerCase() ?? "node";
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/describe.js
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
// The element is described at the moment it is reported and the reference dropped: holding
|
|
4
4
|
// one would keep a removed subtree alive for as long as the panel is open.
|
|
5
5
|
//
|
|
6
|
-
// Enough to find it on the page
|
|
6
|
+
// Enough to find it on the page: tag, id, a class or two, without being a selector.
|
|
7
7
|
/** More than this is a selector, not a label. */
|
|
8
8
|
const MAX_CLASSES = 2;
|
|
9
|
-
/** `text in li
|
|
9
|
+
/** `text in li`: the element it sits in is what locates it. */
|
|
10
10
|
function textOwner(node) {
|
|
11
11
|
const parent = node.parentElement;
|
|
12
12
|
return parent ? `text in ${describeElement(parent)}` : 'text';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/describe.ts
|
|
2
|
+
var MAX_CLASSES = 2;
|
|
3
|
+
function textOwner(node) {
|
|
4
|
+
const parent = node.parentElement;
|
|
5
|
+
return parent ? `text in ${describeElement(parent)}` : "text";
|
|
6
|
+
}
|
|
7
|
+
function describeElement(el) {
|
|
8
|
+
const tag = el.tagName.toLowerCase();
|
|
9
|
+
const id = el.id ? `#${el.id}` : "";
|
|
10
|
+
const raw = typeof el.className === "string" ? el.className : el.getAttribute("class") ?? "";
|
|
11
|
+
const classes = raw.split(/\s+/).filter(Boolean).slice(0, MAX_CLASSES).map((c) => `.${c}`).join("");
|
|
12
|
+
return `${tag}${id}${classes}`;
|
|
13
|
+
}
|
|
14
|
+
function describeNode(target) {
|
|
15
|
+
const node = target;
|
|
16
|
+
if (!node || typeof node.nodeType !== "number") return "detached";
|
|
17
|
+
switch (node.nodeType) {
|
|
18
|
+
case 1:
|
|
19
|
+
return describeElement(node);
|
|
20
|
+
case 3:
|
|
21
|
+
return textOwner(node);
|
|
22
|
+
case 8: {
|
|
23
|
+
const parent = node.parentElement;
|
|
24
|
+
return parent ? `slot in ${describeElement(parent)}` : "slot";
|
|
25
|
+
}
|
|
26
|
+
case 11:
|
|
27
|
+
return "fragment";
|
|
28
|
+
default:
|
|
29
|
+
return node.nodeName?.toLowerCase() ?? "node";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
describeNode
|
|
34
|
+
};
|