@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,307 @@
|
|
|
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/detail-panel.ts
|
|
21
|
+
var detail_panel_exports = {};
|
|
22
|
+
__export(detail_panel_exports, {
|
|
23
|
+
DETAIL_PANEL_CSS: () => DETAIL_PANEL_CSS,
|
|
24
|
+
createDetailPanel: () => createDetailPanel
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(detail_panel_exports);
|
|
27
|
+
|
|
28
|
+
// src/copy.ts
|
|
29
|
+
function confirmOn(button, said = "copied") {
|
|
30
|
+
if (!button) return;
|
|
31
|
+
const was = button.textContent;
|
|
32
|
+
button.textContent = said;
|
|
33
|
+
setTimeout(() => {
|
|
34
|
+
button.textContent = was;
|
|
35
|
+
}, 1200);
|
|
36
|
+
}
|
|
37
|
+
function fallback(text, button) {
|
|
38
|
+
try {
|
|
39
|
+
const doc = button?.ownerDocument ?? document;
|
|
40
|
+
const area = doc.createElement("textarea");
|
|
41
|
+
area.value = text;
|
|
42
|
+
area.setAttribute("readonly", "");
|
|
43
|
+
area.style.position = "fixed";
|
|
44
|
+
area.style.opacity = "0";
|
|
45
|
+
doc.body.appendChild(area);
|
|
46
|
+
area.select();
|
|
47
|
+
doc.execCommand("copy");
|
|
48
|
+
area.remove();
|
|
49
|
+
confirmOn(button);
|
|
50
|
+
return true;
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function copyText(text, button) {
|
|
56
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
57
|
+
if (clipboard?.writeText) {
|
|
58
|
+
try {
|
|
59
|
+
await clipboard.writeText(text);
|
|
60
|
+
confirmOn(button);
|
|
61
|
+
return true;
|
|
62
|
+
} catch {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return fallback(text, button);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/change-view.ts
|
|
69
|
+
var escape = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
70
|
+
var SIGN = { added: "+", removed: "−", changed: "~" };
|
|
71
|
+
function pretty(json) {
|
|
72
|
+
if (!json) return void 0;
|
|
73
|
+
try {
|
|
74
|
+
return JSON.stringify(JSON.parse(json), null, 2);
|
|
75
|
+
} catch {
|
|
76
|
+
return void 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function sideOf(text, json, cls) {
|
|
80
|
+
const block = pretty(json);
|
|
81
|
+
if (!block) return `<span class="ch-${cls}">${escape(text ?? "")}</span>`;
|
|
82
|
+
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>`;
|
|
83
|
+
}
|
|
84
|
+
function changeRows(change, limit = Infinity) {
|
|
85
|
+
if (!change?.entries?.length) return "";
|
|
86
|
+
const shown = change.entries.slice(0, limit);
|
|
87
|
+
const hidden = change.entries.length - shown.length + (change.more ?? 0);
|
|
88
|
+
const rows = shown.map((entry) => {
|
|
89
|
+
const from = sideOf(entry.from, entry.fromJson, "from");
|
|
90
|
+
const to = sideOf(entry.to, entry.toJson, "to");
|
|
91
|
+
const sides = entry.op === "changed" ? `${from}<span class="ch-arrow">→</span>${to}` : entry.op === "added" ? to : from;
|
|
92
|
+
const stacked = entry.fromJson || entry.toJson ? " ch-stacked" : "";
|
|
93
|
+
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>`;
|
|
94
|
+
}).join("");
|
|
95
|
+
const size = change.length ? `<div class="ch-size">${change.length.from} → ${change.length.to} items</div>` : "";
|
|
96
|
+
return `<div class="ch-list">${size}${rows}${hidden > 0 ? `<div class="ch-more">and ${hidden} more</div>` : ""}</div>`;
|
|
97
|
+
}
|
|
98
|
+
function handleCopyClick(event) {
|
|
99
|
+
const button = event.target?.closest?.(".ch-copy");
|
|
100
|
+
if (!button) return false;
|
|
101
|
+
void copyText(button.dataset.copy ?? "", button);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
var CHANGE_CSS = `
|
|
105
|
+
.ch-list { margin-top: 4px; font-family: ui-monospace, monospace; font-size: 11px; }
|
|
106
|
+
.ch-size { color: var(--fx-muted, #8a90a2); margin-bottom: 2px; }
|
|
107
|
+
.ch-row { display: flex; gap: 6px; align-items: baseline; padding: 1px 0; }
|
|
108
|
+
.ch-row.ch-stacked { align-items: flex-start; padding: 3px 0; }
|
|
109
|
+
.ch-sign { flex: none; width: 9px; text-align: center; }
|
|
110
|
+
.ch-added .ch-sign { color: var(--state, #34d399); }
|
|
111
|
+
.ch-removed .ch-sign { color: var(--fx-write, #fb7185); }
|
|
112
|
+
.ch-changed .ch-sign { color: var(--effect, #fbbf24); }
|
|
113
|
+
.ch-key { flex: none; color: var(--fx-muted, #8a90a2); }
|
|
114
|
+
.ch-sides { min-width: 0; flex: 1; overflow-wrap: anywhere; }
|
|
115
|
+
.ch-stacked .ch-sides { display: flex; flex-direction: column; gap: 2px; }
|
|
116
|
+
.ch-from { color: var(--fx-muted, #8a90a2); }
|
|
117
|
+
.ch-row:not(.ch-stacked) .ch-from { text-decoration: line-through; }
|
|
118
|
+
.ch-arrow { color: var(--fx-muted, #8a90a2); margin: 0 4px; }
|
|
119
|
+
.ch-to { color: var(--fx-label, #e7e9ee); }
|
|
120
|
+
.ch-more { color: var(--fx-muted, #8a90a2); margin-top: 2px; }
|
|
121
|
+
/* In the corner: an object can be tall, and a button under it is a scroll away. */
|
|
122
|
+
.ch-block { position: relative; border-radius: 4px; border: 1px solid var(--fx-line, #ffffff14); }
|
|
123
|
+
.ch-block.ch-from { opacity: .72; }
|
|
124
|
+
.ch-json { margin: 0; padding: 5px 7px; overflow-x: auto; white-space: pre; font: inherit; }
|
|
125
|
+
.ch-copy {
|
|
126
|
+
position: absolute; top: 3px; right: 3px; padding: 1px 6px; cursor: pointer;
|
|
127
|
+
font: inherit; font-size: 9.5px; opacity: 0; transition: opacity .12s;
|
|
128
|
+
border: 1px solid var(--fx-line, #ffffff26); border-radius: 3px;
|
|
129
|
+
background: var(--fx-panel, #14161c); color: var(--fx-muted, #8a90a2);
|
|
130
|
+
}
|
|
131
|
+
.ch-block:hover .ch-copy, .ch-copy:focus { opacity: 1; }
|
|
132
|
+
.ch-copy:hover { color: var(--fx-label, #e7e9ee); }
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
// src/detail-panel.ts
|
|
136
|
+
var escape2 = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
137
|
+
var clock = (t) => {
|
|
138
|
+
const d = new Date(t);
|
|
139
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
140
|
+
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
|
|
141
|
+
};
|
|
142
|
+
var VERBS = {
|
|
143
|
+
write: "written",
|
|
144
|
+
ran: "ran",
|
|
145
|
+
created: "made",
|
|
146
|
+
disposed: "gone"
|
|
147
|
+
};
|
|
148
|
+
var MIN_W = 320;
|
|
149
|
+
var MIN_H = 200;
|
|
150
|
+
function draggable(handle, onMove) {
|
|
151
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
152
|
+
if (event.button !== 0) return;
|
|
153
|
+
if (event.target.closest("button")) return;
|
|
154
|
+
event.preventDefault();
|
|
155
|
+
event.stopPropagation();
|
|
156
|
+
handle.setPointerCapture(event.pointerId);
|
|
157
|
+
let lastX = event.clientX;
|
|
158
|
+
let lastY = event.clientY;
|
|
159
|
+
const move = (e) => {
|
|
160
|
+
onMove(e.clientX - lastX, e.clientY - lastY);
|
|
161
|
+
lastX = e.clientX;
|
|
162
|
+
lastY = e.clientY;
|
|
163
|
+
};
|
|
164
|
+
const stop = () => {
|
|
165
|
+
handle.removeEventListener("pointermove", move);
|
|
166
|
+
handle.removeEventListener("pointerup", stop);
|
|
167
|
+
handle.removeEventListener("pointercancel", stop);
|
|
168
|
+
handle.removeEventListener("lostpointercapture", stop);
|
|
169
|
+
if (handle.hasPointerCapture(event.pointerId)) handle.releasePointerCapture(event.pointerId);
|
|
170
|
+
};
|
|
171
|
+
handle.addEventListener("pointermove", move);
|
|
172
|
+
handle.addEventListener("pointerup", stop);
|
|
173
|
+
handle.addEventListener("pointercancel", stop);
|
|
174
|
+
handle.addEventListener("lostpointercapture", stop);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function createDetailPanel(host, options = {}) {
|
|
178
|
+
const panel = host.ownerDocument.createElement("div");
|
|
179
|
+
panel.className = "dp-panel";
|
|
180
|
+
panel.hidden = true;
|
|
181
|
+
panel.innerHTML = '<div class="dp-bar"><span class="dp-title"></span><button type="button" class="dp-close" title="Close" aria-label="Close">✕</button></div><div class="dp-body"></div><div class="dp-grip" title="Drag to resize"></div>';
|
|
182
|
+
host.appendChild(panel);
|
|
183
|
+
const title = panel.querySelector(".dp-title");
|
|
184
|
+
const body = panel.querySelector(".dp-body");
|
|
185
|
+
const bar = panel.querySelector(".dp-bar");
|
|
186
|
+
const grip = panel.querySelector(".dp-grip");
|
|
187
|
+
let x = 0;
|
|
188
|
+
let y = 0;
|
|
189
|
+
let width = 520;
|
|
190
|
+
let height = 380;
|
|
191
|
+
let open = false;
|
|
192
|
+
const place = () => {
|
|
193
|
+
panel.style.left = `${Math.round(x)}px`;
|
|
194
|
+
panel.style.top = `${Math.round(y)}px`;
|
|
195
|
+
panel.style.width = `${Math.round(width)}px`;
|
|
196
|
+
panel.style.height = `${Math.round(height)}px`;
|
|
197
|
+
};
|
|
198
|
+
draggable(bar, (dx, dy) => {
|
|
199
|
+
const view = panel.ownerDocument.defaultView;
|
|
200
|
+
x = Math.max(0, Math.min((view?.innerWidth ?? width) - 60, x + dx));
|
|
201
|
+
y = Math.max(0, Math.min((view?.innerHeight ?? height) - 30, y + dy));
|
|
202
|
+
place();
|
|
203
|
+
});
|
|
204
|
+
draggable(grip, (dx, dy) => {
|
|
205
|
+
width = Math.max(MIN_W, width + dx);
|
|
206
|
+
height = Math.max(MIN_H, height + dy);
|
|
207
|
+
place();
|
|
208
|
+
});
|
|
209
|
+
panel.querySelector(".dp-close")?.addEventListener("click", () => close());
|
|
210
|
+
body.addEventListener("click", (e) => {
|
|
211
|
+
if (handleCopyClick(e)) return;
|
|
212
|
+
const row = e.target.closest(".dp-node");
|
|
213
|
+
if (row?.dataset.id) options.onPick?.(Number(row.dataset.id));
|
|
214
|
+
});
|
|
215
|
+
function close() {
|
|
216
|
+
open = false;
|
|
217
|
+
panel.hidden = true;
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
open(commit, graph) {
|
|
221
|
+
const nameOf = (e) => graph.get(e.id)?.label ?? e.label ?? `#${e.id}`;
|
|
222
|
+
const cause = commit.cause;
|
|
223
|
+
title.textContent = cause ? `${nameOf(cause)} · ${clock(commit.t)}` : `${commit.commit === 0 ? "first render" : `commit ${commit.commit}`} · ${clock(commit.t)}`;
|
|
224
|
+
const headline = cause && cause.from !== void 0 && cause.to !== void 0 ? `<div class="dp-change"><span class="ch-from">${escape2(cause.from)}</span><span class="ch-arrow">→</span><span class="ch-to">${escape2(cause.to)}</span></div>` : "";
|
|
225
|
+
const groups = ["write", "ran", "created", "disposed"].map((kind) => {
|
|
226
|
+
const rows = commit.events.filter((e) => e.kind === kind && e !== cause);
|
|
227
|
+
if (!rows.length) return "";
|
|
228
|
+
const seen = /* @__PURE__ */ new Map();
|
|
229
|
+
for (const event of rows) {
|
|
230
|
+
const found = seen.get(event.id);
|
|
231
|
+
if (found) found.count++;
|
|
232
|
+
else seen.set(event.id, { name: nameOf(event), count: 1 });
|
|
233
|
+
}
|
|
234
|
+
const items = [...seen].map(
|
|
235
|
+
([id, { name, count }]) => `<button type="button" class="dp-node" data-id="${id}">${escape2(name)}${count > 1 ? ` ×${count}` : ""}</button>`
|
|
236
|
+
).join("");
|
|
237
|
+
return `<div class="dp-group"><div class="dp-verb">${VERBS[kind]}</div><div class="dp-nodes">${items}</div></div>`;
|
|
238
|
+
}).join("");
|
|
239
|
+
const node = cause ? graph.get(cause.id) : void 0;
|
|
240
|
+
const where = (t) => {
|
|
241
|
+
if (!t.source) return "";
|
|
242
|
+
const file = (t.source.file ?? "").split("/").pop() ?? "";
|
|
243
|
+
const out = t.source.compiled;
|
|
244
|
+
const tail = out && out.line !== t.source.line ? ` · out ${out.line}` : "";
|
|
245
|
+
return ` — ${file}:${t.source.line ?? ""}${tail}`;
|
|
246
|
+
};
|
|
247
|
+
const targets = node?.targets?.length ? `<div class="dp-group"><div class="dp-verb">updates</div><div class="dp-targets">${node.targets.map(
|
|
248
|
+
(t) => `<div>${escape2(t.name ? `${t.name} of ${t.element}` : t.element)}<span class="dp-where">${escape2(where(t))}</span></div>`
|
|
249
|
+
).join("")}</div></div>` : "";
|
|
250
|
+
body.innerHTML = headline + changeRows(cause?.change) + groups + targets;
|
|
251
|
+
if (!open) {
|
|
252
|
+
const view = panel.ownerDocument.defaultView;
|
|
253
|
+
x = Math.max(12, ((view?.innerWidth ?? 900) - width) / 2);
|
|
254
|
+
y = Math.max(12, ((view?.innerHeight ?? 600) - height) / 3);
|
|
255
|
+
place();
|
|
256
|
+
}
|
|
257
|
+
open = true;
|
|
258
|
+
panel.hidden = false;
|
|
259
|
+
},
|
|
260
|
+
close,
|
|
261
|
+
get isOpen() {
|
|
262
|
+
return open;
|
|
263
|
+
},
|
|
264
|
+
destroy() {
|
|
265
|
+
panel.remove();
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
var DETAIL_PANEL_CSS = `
|
|
270
|
+
.dp-panel {
|
|
271
|
+
position: fixed; z-index: 40; display: flex; flex-direction: column;
|
|
272
|
+
border: 1px solid var(--fx-line, #ffffff26); border-radius: 8px;
|
|
273
|
+
background: var(--fx-panel, #14161c); color: var(--fx-label, #e7e9ee);
|
|
274
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, .45); overflow: hidden;
|
|
275
|
+
}
|
|
276
|
+
.dp-panel[hidden] { display: none; }
|
|
277
|
+
.dp-bar {
|
|
278
|
+
display: flex; align-items: center; gap: 8px; padding: 6px 8px 6px 12px;
|
|
279
|
+
cursor: move; user-select: none;
|
|
280
|
+
border-bottom: 1px solid var(--fx-line, #ffffff1f);
|
|
281
|
+
}
|
|
282
|
+
.dp-title { font-family: ui-monospace, monospace; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
283
|
+
.dp-close {
|
|
284
|
+
margin-left: auto; width: 22px; height: 20px; padding: 0; cursor: pointer; font: inherit; font-size: 11px;
|
|
285
|
+
border: 1px solid var(--fx-line, #ffffff1f); border-radius: 4px;
|
|
286
|
+
background: transparent; color: var(--fx-muted, #8a90a2);
|
|
287
|
+
}
|
|
288
|
+
.dp-close:hover { color: var(--fx-label, #e7e9ee); }
|
|
289
|
+
.dp-body { flex: 1; min-height: 0; overflow: auto; padding: 10px 12px; }
|
|
290
|
+
.dp-change { font-family: ui-monospace, monospace; font-size: 12px; overflow-wrap: anywhere; }
|
|
291
|
+
.dp-group { margin-top: 10px; }
|
|
292
|
+
.dp-verb { font-size: 10.5px; text-transform: uppercase; letter-spacing: 1px; color: var(--fx-muted, #8a90a2); }
|
|
293
|
+
.dp-nodes { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
294
|
+
.dp-node {
|
|
295
|
+
padding: 2px 7px; cursor: pointer; font: inherit; font-family: ui-monospace, monospace; font-size: 11px;
|
|
296
|
+
border: 1px solid var(--fx-line, #ffffff1f); border-radius: 999px;
|
|
297
|
+
background: transparent; color: var(--fx-label, #e7e9ee);
|
|
298
|
+
}
|
|
299
|
+
.dp-node:hover { border-color: var(--fx-accent, #5eead4); }
|
|
300
|
+
.dp-targets { margin-top: 4px; font-family: ui-monospace, monospace; font-size: 11px; color: var(--fx-label, #e7e9ee); }
|
|
301
|
+
.dp-where { color: var(--fx-muted, #8a90a2); }
|
|
302
|
+
/* Bottom-right, where every desktop puts it. */
|
|
303
|
+
.dp-grip {
|
|
304
|
+
position: absolute; right: 0; bottom: 0; width: 16px; height: 16px; cursor: nwse-resize;
|
|
305
|
+
background: linear-gradient(135deg, transparent 50%, var(--fx-line, #ffffff40) 50%);
|
|
306
|
+
}
|
|
307
|
+
${CHANGE_CSS}`;
|
package/dist/detail-panel.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export interface DetailPanel {
|
|
|
9
9
|
destroy(): void;
|
|
10
10
|
}
|
|
11
11
|
export interface DetailPanelOptions {
|
|
12
|
-
/** A node in the panel was clicked
|
|
12
|
+
/** A node in the panel was clicked: the host can select it on the graph. */
|
|
13
13
|
onPick?(id: number): void;
|
|
14
14
|
}
|
|
15
15
|
export declare function createDetailPanel(host: HTMLElement, options?: DetailPanelOptions): DetailPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../src/detail-panel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;IACtD,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,
|
|
1
|
+
{"version":3,"file":"detail-panel.d.ts","sourceRoot":"","sources":["../src/detail-panel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,cAAc,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,WAAW,WAAW;IAC1B,mFAAmF;IACnF,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;IACtD,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,0CAA0C;IAC1C,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,MAAM,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA2DD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAwIlG;AAED,eAAO,MAAM,gBAAgB,0vHAsCf,CAAC"}
|
package/dist/detail-panel.js
CHANGED
|
@@ -138,7 +138,7 @@ export function createDetailPanel(host, options = {}) {
|
|
|
138
138
|
return `<div class="dp-group"><div class="dp-verb">${VERBS[kind]}</div><div class="dp-nodes">${items}</div></div>`;
|
|
139
139
|
})
|
|
140
140
|
.join('');
|
|
141
|
-
// Where the value reaches the page
|
|
141
|
+
// Where the value reaches the page: the last step of the chain.
|
|
142
142
|
const node = cause ? graph.get(cause.id) : undefined;
|
|
143
143
|
// Both: a template's holes can share one original position, and then only the compiled
|
|
144
144
|
// line tells them apart.
|
|
@@ -0,0 +1,284 @@
|
|
|
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
|
+
|
|
108
|
+
// src/detail-panel.ts
|
|
109
|
+
var escape2 = (text) => text.replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
|
|
110
|
+
var clock = (t) => {
|
|
111
|
+
const d = new Date(t);
|
|
112
|
+
const pad = (n, w = 2) => String(n).padStart(w, "0");
|
|
113
|
+
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
|
|
114
|
+
};
|
|
115
|
+
var VERBS = {
|
|
116
|
+
write: "written",
|
|
117
|
+
ran: "ran",
|
|
118
|
+
created: "made",
|
|
119
|
+
disposed: "gone"
|
|
120
|
+
};
|
|
121
|
+
var MIN_W = 320;
|
|
122
|
+
var MIN_H = 200;
|
|
123
|
+
function draggable(handle, onMove) {
|
|
124
|
+
handle.addEventListener("pointerdown", (event) => {
|
|
125
|
+
if (event.button !== 0) return;
|
|
126
|
+
if (event.target.closest("button")) return;
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
event.stopPropagation();
|
|
129
|
+
handle.setPointerCapture(event.pointerId);
|
|
130
|
+
let lastX = event.clientX;
|
|
131
|
+
let lastY = event.clientY;
|
|
132
|
+
const move = (e) => {
|
|
133
|
+
onMove(e.clientX - lastX, e.clientY - lastY);
|
|
134
|
+
lastX = e.clientX;
|
|
135
|
+
lastY = e.clientY;
|
|
136
|
+
};
|
|
137
|
+
const stop = () => {
|
|
138
|
+
handle.removeEventListener("pointermove", move);
|
|
139
|
+
handle.removeEventListener("pointerup", stop);
|
|
140
|
+
handle.removeEventListener("pointercancel", stop);
|
|
141
|
+
handle.removeEventListener("lostpointercapture", stop);
|
|
142
|
+
if (handle.hasPointerCapture(event.pointerId)) handle.releasePointerCapture(event.pointerId);
|
|
143
|
+
};
|
|
144
|
+
handle.addEventListener("pointermove", move);
|
|
145
|
+
handle.addEventListener("pointerup", stop);
|
|
146
|
+
handle.addEventListener("pointercancel", stop);
|
|
147
|
+
handle.addEventListener("lostpointercapture", stop);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function createDetailPanel(host, options = {}) {
|
|
151
|
+
const panel = host.ownerDocument.createElement("div");
|
|
152
|
+
panel.className = "dp-panel";
|
|
153
|
+
panel.hidden = true;
|
|
154
|
+
panel.innerHTML = '<div class="dp-bar"><span class="dp-title"></span><button type="button" class="dp-close" title="Close" aria-label="Close">✕</button></div><div class="dp-body"></div><div class="dp-grip" title="Drag to resize"></div>';
|
|
155
|
+
host.appendChild(panel);
|
|
156
|
+
const title = panel.querySelector(".dp-title");
|
|
157
|
+
const body = panel.querySelector(".dp-body");
|
|
158
|
+
const bar = panel.querySelector(".dp-bar");
|
|
159
|
+
const grip = panel.querySelector(".dp-grip");
|
|
160
|
+
let x = 0;
|
|
161
|
+
let y = 0;
|
|
162
|
+
let width = 520;
|
|
163
|
+
let height = 380;
|
|
164
|
+
let open = false;
|
|
165
|
+
const place = () => {
|
|
166
|
+
panel.style.left = `${Math.round(x)}px`;
|
|
167
|
+
panel.style.top = `${Math.round(y)}px`;
|
|
168
|
+
panel.style.width = `${Math.round(width)}px`;
|
|
169
|
+
panel.style.height = `${Math.round(height)}px`;
|
|
170
|
+
};
|
|
171
|
+
draggable(bar, (dx, dy) => {
|
|
172
|
+
const view = panel.ownerDocument.defaultView;
|
|
173
|
+
x = Math.max(0, Math.min((view?.innerWidth ?? width) - 60, x + dx));
|
|
174
|
+
y = Math.max(0, Math.min((view?.innerHeight ?? height) - 30, y + dy));
|
|
175
|
+
place();
|
|
176
|
+
});
|
|
177
|
+
draggable(grip, (dx, dy) => {
|
|
178
|
+
width = Math.max(MIN_W, width + dx);
|
|
179
|
+
height = Math.max(MIN_H, height + dy);
|
|
180
|
+
place();
|
|
181
|
+
});
|
|
182
|
+
panel.querySelector(".dp-close")?.addEventListener("click", () => close());
|
|
183
|
+
body.addEventListener("click", (e) => {
|
|
184
|
+
if (handleCopyClick(e)) return;
|
|
185
|
+
const row = e.target.closest(".dp-node");
|
|
186
|
+
if (row?.dataset.id) options.onPick?.(Number(row.dataset.id));
|
|
187
|
+
});
|
|
188
|
+
function close() {
|
|
189
|
+
open = false;
|
|
190
|
+
panel.hidden = true;
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
open(commit, graph) {
|
|
194
|
+
const nameOf = (e) => graph.get(e.id)?.label ?? e.label ?? `#${e.id}`;
|
|
195
|
+
const cause = commit.cause;
|
|
196
|
+
title.textContent = cause ? `${nameOf(cause)} · ${clock(commit.t)}` : `${commit.commit === 0 ? "first render" : `commit ${commit.commit}`} · ${clock(commit.t)}`;
|
|
197
|
+
const headline = cause && cause.from !== void 0 && cause.to !== void 0 ? `<div class="dp-change"><span class="ch-from">${escape2(cause.from)}</span><span class="ch-arrow">→</span><span class="ch-to">${escape2(cause.to)}</span></div>` : "";
|
|
198
|
+
const groups = ["write", "ran", "created", "disposed"].map((kind) => {
|
|
199
|
+
const rows = commit.events.filter((e) => e.kind === kind && e !== cause);
|
|
200
|
+
if (!rows.length) return "";
|
|
201
|
+
const seen = /* @__PURE__ */ new Map();
|
|
202
|
+
for (const event of rows) {
|
|
203
|
+
const found = seen.get(event.id);
|
|
204
|
+
if (found) found.count++;
|
|
205
|
+
else seen.set(event.id, { name: nameOf(event), count: 1 });
|
|
206
|
+
}
|
|
207
|
+
const items = [...seen].map(
|
|
208
|
+
([id, { name, count }]) => `<button type="button" class="dp-node" data-id="${id}">${escape2(name)}${count > 1 ? ` ×${count}` : ""}</button>`
|
|
209
|
+
).join("");
|
|
210
|
+
return `<div class="dp-group"><div class="dp-verb">${VERBS[kind]}</div><div class="dp-nodes">${items}</div></div>`;
|
|
211
|
+
}).join("");
|
|
212
|
+
const node = cause ? graph.get(cause.id) : void 0;
|
|
213
|
+
const where = (t) => {
|
|
214
|
+
if (!t.source) return "";
|
|
215
|
+
const file = (t.source.file ?? "").split("/").pop() ?? "";
|
|
216
|
+
const out = t.source.compiled;
|
|
217
|
+
const tail = out && out.line !== t.source.line ? ` · out ${out.line}` : "";
|
|
218
|
+
return ` — ${file}:${t.source.line ?? ""}${tail}`;
|
|
219
|
+
};
|
|
220
|
+
const targets = node?.targets?.length ? `<div class="dp-group"><div class="dp-verb">updates</div><div class="dp-targets">${node.targets.map(
|
|
221
|
+
(t) => `<div>${escape2(t.name ? `${t.name} of ${t.element}` : t.element)}<span class="dp-where">${escape2(where(t))}</span></div>`
|
|
222
|
+
).join("")}</div></div>` : "";
|
|
223
|
+
body.innerHTML = headline + changeRows(cause?.change) + groups + targets;
|
|
224
|
+
if (!open) {
|
|
225
|
+
const view = panel.ownerDocument.defaultView;
|
|
226
|
+
x = Math.max(12, ((view?.innerWidth ?? 900) - width) / 2);
|
|
227
|
+
y = Math.max(12, ((view?.innerHeight ?? 600) - height) / 3);
|
|
228
|
+
place();
|
|
229
|
+
}
|
|
230
|
+
open = true;
|
|
231
|
+
panel.hidden = false;
|
|
232
|
+
},
|
|
233
|
+
close,
|
|
234
|
+
get isOpen() {
|
|
235
|
+
return open;
|
|
236
|
+
},
|
|
237
|
+
destroy() {
|
|
238
|
+
panel.remove();
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
var DETAIL_PANEL_CSS = `
|
|
243
|
+
.dp-panel {
|
|
244
|
+
position: fixed; z-index: 40; display: flex; flex-direction: column;
|
|
245
|
+
border: 1px solid var(--fx-line, #ffffff26); border-radius: 8px;
|
|
246
|
+
background: var(--fx-panel, #14161c); color: var(--fx-label, #e7e9ee);
|
|
247
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, .45); overflow: hidden;
|
|
248
|
+
}
|
|
249
|
+
.dp-panel[hidden] { display: none; }
|
|
250
|
+
.dp-bar {
|
|
251
|
+
display: flex; align-items: center; gap: 8px; padding: 6px 8px 6px 12px;
|
|
252
|
+
cursor: move; user-select: none;
|
|
253
|
+
border-bottom: 1px solid var(--fx-line, #ffffff1f);
|
|
254
|
+
}
|
|
255
|
+
.dp-title { font-family: ui-monospace, monospace; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
256
|
+
.dp-close {
|
|
257
|
+
margin-left: auto; width: 22px; height: 20px; padding: 0; cursor: pointer; font: inherit; font-size: 11px;
|
|
258
|
+
border: 1px solid var(--fx-line, #ffffff1f); border-radius: 4px;
|
|
259
|
+
background: transparent; color: var(--fx-muted, #8a90a2);
|
|
260
|
+
}
|
|
261
|
+
.dp-close:hover { color: var(--fx-label, #e7e9ee); }
|
|
262
|
+
.dp-body { flex: 1; min-height: 0; overflow: auto; padding: 10px 12px; }
|
|
263
|
+
.dp-change { font-family: ui-monospace, monospace; font-size: 12px; overflow-wrap: anywhere; }
|
|
264
|
+
.dp-group { margin-top: 10px; }
|
|
265
|
+
.dp-verb { font-size: 10.5px; text-transform: uppercase; letter-spacing: 1px; color: var(--fx-muted, #8a90a2); }
|
|
266
|
+
.dp-nodes { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
|
|
267
|
+
.dp-node {
|
|
268
|
+
padding: 2px 7px; cursor: pointer; font: inherit; font-family: ui-monospace, monospace; font-size: 11px;
|
|
269
|
+
border: 1px solid var(--fx-line, #ffffff1f); border-radius: 999px;
|
|
270
|
+
background: transparent; color: var(--fx-label, #e7e9ee);
|
|
271
|
+
}
|
|
272
|
+
.dp-node:hover { border-color: var(--fx-accent, #5eead4); }
|
|
273
|
+
.dp-targets { margin-top: 4px; font-family: ui-monospace, monospace; font-size: 11px; color: var(--fx-label, #e7e9ee); }
|
|
274
|
+
.dp-where { color: var(--fx-muted, #8a90a2); }
|
|
275
|
+
/* Bottom-right, where every desktop puts it. */
|
|
276
|
+
.dp-grip {
|
|
277
|
+
position: absolute; right: 0; bottom: 0; width: 16px; height: 16px; cursor: nwse-resize;
|
|
278
|
+
background: linear-gradient(135deg, transparent 50%, var(--fx-line, #ffffff40) 50%);
|
|
279
|
+
}
|
|
280
|
+
${CHANGE_CSS}`;
|
|
281
|
+
export {
|
|
282
|
+
DETAIL_PANEL_CSS,
|
|
283
|
+
createDetailPanel
|
|
284
|
+
};
|