@react-trace-enhancer/plugin-preview 0.0.1
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/README.md +79 -0
- package/dist/index.cjs +720 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +691 -0
- package/dist/index.js.map +1 -0
- package/dist/index.prod.cjs +13 -0
- package/dist/index.prod.cjs.map +1 -0
- package/dist/index.prod.d.cts +14 -0
- package/dist/index.prod.d.cts.map +1 -0
- package/dist/index.prod.d.ts +14 -0
- package/dist/index.prod.d.ts.map +1 -0
- package/dist/index.prod.js +11 -0
- package/dist/index.prod.js.map +1 -0
- package/dist/types-DM7rB7J1.d.cts +12 -0
- package/dist/types-DM7rB7J1.d.cts.map +1 -0
- package/dist/types-uxyW_RVb.d.ts +12 -0
- package/dist/types-uxyW_RVb.d.ts.map +1 -0
- package/package.json +90 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
+
__defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
+
value: mod,
|
|
25
|
+
enumerable: true
|
|
26
|
+
}) : target, mod));
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
let _react_trace_enhancer_core = require("@react-trace-enhancer/core");
|
|
30
|
+
let _react_trace_enhancer_ui_components = require("@react-trace-enhancer/ui-components");
|
|
31
|
+
let react = require("react");
|
|
32
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
+
let jotai = require("jotai");
|
|
34
|
+
let shiki = require("shiki");
|
|
35
|
+
let _monaco_editor_react = require("@monaco-editor/react");
|
|
36
|
+
_monaco_editor_react = __toESM(_monaco_editor_react);
|
|
37
|
+
let _shikijs_monaco = require("@shikijs/monaco");
|
|
38
|
+
|
|
39
|
+
//#region src/FolderAccessPrompt.tsx
|
|
40
|
+
function FolderAccessPrompt({ root, onGrant, onCancel }) {
|
|
41
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
42
|
+
style: {
|
|
43
|
+
display: "flex",
|
|
44
|
+
flexDirection: "column",
|
|
45
|
+
alignItems: "center",
|
|
46
|
+
justifyContent: "center",
|
|
47
|
+
gap: 12,
|
|
48
|
+
padding: "20px 16px",
|
|
49
|
+
height: "100%",
|
|
50
|
+
boxSizing: "border-box",
|
|
51
|
+
textAlign: "center",
|
|
52
|
+
fontFamily: "system-ui, sans-serif"
|
|
53
|
+
},
|
|
54
|
+
children: [
|
|
55
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
56
|
+
style: { color: "#52525b" },
|
|
57
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.FolderIcon, {})
|
|
58
|
+
}),
|
|
59
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
60
|
+
style: {
|
|
61
|
+
display: "flex",
|
|
62
|
+
flexDirection: "column",
|
|
63
|
+
gap: 12
|
|
64
|
+
},
|
|
65
|
+
children: [
|
|
66
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
67
|
+
style: {
|
|
68
|
+
fontSize: 13,
|
|
69
|
+
fontWeight: 600,
|
|
70
|
+
color: "#fafafa"
|
|
71
|
+
},
|
|
72
|
+
children: "Folder access needed"
|
|
73
|
+
}),
|
|
74
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
75
|
+
style: {
|
|
76
|
+
fontSize: 12,
|
|
77
|
+
color: "#71717a",
|
|
78
|
+
lineHeight: 1.5
|
|
79
|
+
},
|
|
80
|
+
children: root ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
81
|
+
style: {
|
|
82
|
+
display: "flex",
|
|
83
|
+
flexDirection: "column",
|
|
84
|
+
gap: 8
|
|
85
|
+
},
|
|
86
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "The project root path will be copied to your clipboard. Navigate to it in the folder picker." }), _react_trace_enhancer_core.IS_MAC && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_trace_enhancer_ui_components.KbdGroup, { children: [
|
|
87
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Kbd, { children: "⌘" }),
|
|
88
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Kbd, { children: "⇧" }),
|
|
89
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Kbd, { children: "G" })
|
|
90
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: " to paste the path directly." })] })]
|
|
91
|
+
}) : "Grant access to your project folder to preview source files."
|
|
92
|
+
}),
|
|
93
|
+
root && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
94
|
+
style: {
|
|
95
|
+
fontSize: 11,
|
|
96
|
+
fontFamily: "ui-monospace, monospace",
|
|
97
|
+
color: "#3b82f6",
|
|
98
|
+
wordBreak: "break-all"
|
|
99
|
+
},
|
|
100
|
+
children: root
|
|
101
|
+
})
|
|
102
|
+
]
|
|
103
|
+
}),
|
|
104
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
105
|
+
style: {
|
|
106
|
+
display: "flex",
|
|
107
|
+
gap: 8
|
|
108
|
+
},
|
|
109
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Button, {
|
|
110
|
+
variant: "secondary",
|
|
111
|
+
onClick: onCancel,
|
|
112
|
+
children: "Cancel"
|
|
113
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Button, {
|
|
114
|
+
variant: "primary",
|
|
115
|
+
onClick: onGrant,
|
|
116
|
+
children: "Grant access"
|
|
117
|
+
})]
|
|
118
|
+
})
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Copies the root path to clipboard then calls requestAccess().
|
|
124
|
+
*/
|
|
125
|
+
async function handleGrantAccess(root, requestAccess) {
|
|
126
|
+
await navigator.clipboard.writeText(root).catch(() => {});
|
|
127
|
+
return requestAccess();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/fs.ts
|
|
132
|
+
const IDB_NAME = "react-trace";
|
|
133
|
+
const IDB_STORE = "handles";
|
|
134
|
+
const IDB_KEY = "root-directory";
|
|
135
|
+
function openDB() {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
const req = indexedDB.open(IDB_NAME, 1);
|
|
138
|
+
req.onupgradeneeded = () => req.result.createObjectStore(IDB_STORE);
|
|
139
|
+
req.onsuccess = () => resolve(req.result);
|
|
140
|
+
req.onerror = () => reject(req.error);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async function saveHandle(handle) {
|
|
144
|
+
const db = await openDB();
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
const tx = db.transaction(IDB_STORE, "readwrite");
|
|
147
|
+
tx.objectStore(IDB_STORE).put(handle, IDB_KEY);
|
|
148
|
+
tx.oncomplete = () => resolve();
|
|
149
|
+
tx.onerror = () => reject(tx.error);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function loadHandle() {
|
|
153
|
+
try {
|
|
154
|
+
const db = await openDB();
|
|
155
|
+
return new Promise((resolve, reject) => {
|
|
156
|
+
const req = db.transaction(IDB_STORE, "readonly").objectStore(IDB_STORE).get(IDB_KEY);
|
|
157
|
+
req.onsuccess = () => resolve(req.result ?? null);
|
|
158
|
+
req.onerror = () => reject(req.error);
|
|
159
|
+
});
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Traverses the directory handle tree to reach the target file.
|
|
166
|
+
* Returns null if any segment of the path doesn't exist.
|
|
167
|
+
*/
|
|
168
|
+
async function getFileHandle(dir, relativePath, create = false) {
|
|
169
|
+
const parts = relativePath.split("/").filter(Boolean);
|
|
170
|
+
if (parts.length === 0) return null;
|
|
171
|
+
let current = dir;
|
|
172
|
+
for (let i = 0; i < parts.length - 1; i++) try {
|
|
173
|
+
current = await current.getDirectoryHandle(parts[i], { create });
|
|
174
|
+
} catch {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return await current.getFileHandle(parts.at(-1), { create });
|
|
179
|
+
} catch {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
var FileSystemServiceImpl = class {
|
|
184
|
+
_handle = null;
|
|
185
|
+
_listeners = /* @__PURE__ */ new Set();
|
|
186
|
+
get isSupported() {
|
|
187
|
+
return typeof window !== "undefined" && "showDirectoryPicker" in window;
|
|
188
|
+
}
|
|
189
|
+
get hasAccess() {
|
|
190
|
+
return this._handle !== null;
|
|
191
|
+
}
|
|
192
|
+
subscribe(listener) {
|
|
193
|
+
this._listeners.add(listener);
|
|
194
|
+
return () => this._listeners.delete(listener);
|
|
195
|
+
}
|
|
196
|
+
notify() {
|
|
197
|
+
this._listeners.forEach((l) => l());
|
|
198
|
+
}
|
|
199
|
+
async tryRestore() {
|
|
200
|
+
if (!this.isSupported) return false;
|
|
201
|
+
try {
|
|
202
|
+
const handle = await loadHandle();
|
|
203
|
+
if (!handle) return false;
|
|
204
|
+
if (await handle.requestPermission({ mode: "readwrite" }) === "granted") {
|
|
205
|
+
this._handle = handle;
|
|
206
|
+
this.notify();
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
} catch {}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
async requestAccess() {
|
|
213
|
+
if (!this.isSupported) return false;
|
|
214
|
+
try {
|
|
215
|
+
const handle = await window.showDirectoryPicker({ mode: "readwrite" });
|
|
216
|
+
await saveHandle(handle);
|
|
217
|
+
this._handle = handle;
|
|
218
|
+
this.notify();
|
|
219
|
+
return true;
|
|
220
|
+
} catch {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Ensure we have access — try restore silently first, then prompt. */
|
|
225
|
+
async ensureAccess() {
|
|
226
|
+
if (this._handle) return true;
|
|
227
|
+
if (await this.tryRestore()) return true;
|
|
228
|
+
return this.requestAccess();
|
|
229
|
+
}
|
|
230
|
+
async read(relativePath) {
|
|
231
|
+
if (!await this.ensureAccess() || !this._handle) throw new Error("[react-trace] File system access denied");
|
|
232
|
+
const file = await getFileHandle(this._handle, relativePath);
|
|
233
|
+
if (!file) throw new Error(`[react-trace] File not found: ${relativePath}`);
|
|
234
|
+
return (await file.getFile()).text();
|
|
235
|
+
}
|
|
236
|
+
async write(relativePath, content) {
|
|
237
|
+
if (!await this.ensureAccess() || !this._handle) throw new Error("[react-trace] File system access denied");
|
|
238
|
+
const file = await getFileHandle(this._handle, relativePath, true);
|
|
239
|
+
if (!file) throw new Error(`[react-trace] Cannot open file for writing: ${relativePath}`);
|
|
240
|
+
const writable = await file.createWritable();
|
|
241
|
+
await writable.write(content);
|
|
242
|
+
await writable.close();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
const fileSystemService = new FileSystemServiceImpl();
|
|
246
|
+
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/store.ts
|
|
249
|
+
const previewSettingsAtom = (0, _react_trace_enhancer_core.settingsPluginAtom)("preview");
|
|
250
|
+
|
|
251
|
+
//#endregion
|
|
252
|
+
//#region src/PreviewSettings.tsx
|
|
253
|
+
const themes = shiki.bundledThemesInfo.map((theme) => ({
|
|
254
|
+
value: theme.id,
|
|
255
|
+
label: theme.displayName
|
|
256
|
+
}));
|
|
257
|
+
const themesLookup = themes.reduce((acc, theme) => {
|
|
258
|
+
acc[theme.value] = theme;
|
|
259
|
+
return acc;
|
|
260
|
+
}, {});
|
|
261
|
+
const LABEL_STYLE = {
|
|
262
|
+
fontSize: 12,
|
|
263
|
+
color: "#d4d4d8",
|
|
264
|
+
fontFamily: "system-ui, sans-serif"
|
|
265
|
+
};
|
|
266
|
+
function PreviewSettings() {
|
|
267
|
+
const portalContainer = (0, _react_trace_enhancer_core.useWidgetPortalContainer)();
|
|
268
|
+
const [themesOpen, setThemesOpen] = (0, react.useState)(false);
|
|
269
|
+
const [previewSettings = {
|
|
270
|
+
disabled: false,
|
|
271
|
+
theme: "one-dark-pro"
|
|
272
|
+
}, setPreviewSettings] = (0, jotai.useAtom)(previewSettingsAtom);
|
|
273
|
+
const anchorRef = (0, react.useRef)(null);
|
|
274
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
275
|
+
style: {
|
|
276
|
+
display: "flex",
|
|
277
|
+
flexDirection: "column",
|
|
278
|
+
gap: 12
|
|
279
|
+
},
|
|
280
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
281
|
+
style: {
|
|
282
|
+
display: "flex",
|
|
283
|
+
flexDirection: "column",
|
|
284
|
+
gap: 6
|
|
285
|
+
},
|
|
286
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
287
|
+
style: LABEL_STYLE,
|
|
288
|
+
children: [
|
|
289
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
290
|
+
type: "checkbox",
|
|
291
|
+
checked: previewSettings.disabled ?? false,
|
|
292
|
+
onChange: (e) => setPreviewSettings({
|
|
293
|
+
...previewSettings,
|
|
294
|
+
disabled: e.target.checked
|
|
295
|
+
})
|
|
296
|
+
}),
|
|
297
|
+
" ",
|
|
298
|
+
"Code editing disabled"
|
|
299
|
+
]
|
|
300
|
+
})
|
|
301
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
302
|
+
style: {
|
|
303
|
+
display: "flex",
|
|
304
|
+
flexDirection: "column",
|
|
305
|
+
gap: 6
|
|
306
|
+
},
|
|
307
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
308
|
+
style: LABEL_STYLE,
|
|
309
|
+
children: "Theme"
|
|
310
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_trace_enhancer_ui_components.Combobox.Root, {
|
|
311
|
+
value: themesLookup[previewSettings.theme],
|
|
312
|
+
onValueChange: (theme) => {
|
|
313
|
+
if (theme) setPreviewSettings({
|
|
314
|
+
...previewSettings,
|
|
315
|
+
theme: theme.value
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
items: themes,
|
|
319
|
+
open: themesOpen,
|
|
320
|
+
onOpenChange: setThemesOpen,
|
|
321
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
322
|
+
ref: anchorRef,
|
|
323
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Trigger, {
|
|
324
|
+
onClick: (e) => e.stopPropagation(),
|
|
325
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
326
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Input, {})
|
|
327
|
+
})
|
|
328
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Portal, {
|
|
329
|
+
container: portalContainer,
|
|
330
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Positioner, {
|
|
331
|
+
style: {
|
|
332
|
+
zIndex: 1e8,
|
|
333
|
+
pointerEvents: "auto"
|
|
334
|
+
},
|
|
335
|
+
anchor: anchorRef,
|
|
336
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_trace_enhancer_ui_components.Combobox.Popup, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Empty, { children: "No themes found" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.List, {
|
|
337
|
+
style: {
|
|
338
|
+
overscrollBehavior: "contain",
|
|
339
|
+
maxHeight: 500,
|
|
340
|
+
overflowY: "auto"
|
|
341
|
+
},
|
|
342
|
+
children: (option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Combobox.Item, {
|
|
343
|
+
value: option,
|
|
344
|
+
onClick: (e) => e.stopPropagation(),
|
|
345
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: option.label })
|
|
346
|
+
}, option.value)
|
|
347
|
+
})] })
|
|
348
|
+
})
|
|
349
|
+
})]
|
|
350
|
+
})]
|
|
351
|
+
})]
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/highlight.ts
|
|
357
|
+
/** Injects the CSS animation for the highlighted source line — idempotent. */
|
|
358
|
+
function ensureHighlightStyle() {
|
|
359
|
+
if (document.getElementById("react-trace-highlighted-line")) return;
|
|
360
|
+
const style = document.createElement("style");
|
|
361
|
+
style.id = "react-trace-highlighted-line";
|
|
362
|
+
style.textContent = `
|
|
363
|
+
.react-trace-highlighted-line {
|
|
364
|
+
background-color: rgba(0, 200, 255, 0.25);
|
|
365
|
+
animation: react-trace-highlight-flash 1.2s ease-out;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
@keyframes react-trace-highlight-flash {
|
|
369
|
+
from { background-color: rgba(0, 200, 255, 0.6); }
|
|
370
|
+
to { background-color: rgba(0, 200, 255, 0.25); }
|
|
371
|
+
}
|
|
372
|
+
`;
|
|
373
|
+
document.head.appendChild(style);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/monaco.ts
|
|
378
|
+
_monaco_editor_react.loader.init();
|
|
379
|
+
const langs = [
|
|
380
|
+
"typescript",
|
|
381
|
+
"javascript",
|
|
382
|
+
"graphql",
|
|
383
|
+
"css"
|
|
384
|
+
];
|
|
385
|
+
const highlighterPromise = (0, shiki.createHighlighter)({
|
|
386
|
+
themes: Object.values(shiki.bundledThemes),
|
|
387
|
+
langs
|
|
388
|
+
});
|
|
389
|
+
/**
|
|
390
|
+
* Monaco `beforeMount` callback — disables the built-in TS/JS language server
|
|
391
|
+
* (no project types available in the browser) and wires Shiki for highlighting.
|
|
392
|
+
*/
|
|
393
|
+
function configureBefore(monaco) {
|
|
394
|
+
const noValidation = {
|
|
395
|
+
noSemanticValidation: true,
|
|
396
|
+
noSyntaxValidation: true
|
|
397
|
+
};
|
|
398
|
+
const { typescriptDefaults, javascriptDefaults } = monaco.languages.typescript;
|
|
399
|
+
typescriptDefaults.setDiagnosticsOptions(noValidation);
|
|
400
|
+
javascriptDefaults.setDiagnosticsOptions(noValidation);
|
|
401
|
+
const compilerOptions = {
|
|
402
|
+
jsx: monaco.languages.typescript.JsxEmit.ReactJSX,
|
|
403
|
+
allowJs: true
|
|
404
|
+
};
|
|
405
|
+
typescriptDefaults.setCompilerOptions(compilerOptions);
|
|
406
|
+
javascriptDefaults.setCompilerOptions(compilerOptions);
|
|
407
|
+
for (const id of langs) monaco.languages.register({ id });
|
|
408
|
+
highlighterPromise.then((h) => (0, _shikijs_monaco.shikiToMonaco)(h, monaco));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
//#endregion
|
|
412
|
+
//#region src/styles.ts
|
|
413
|
+
const LINE_HEIGHT = 19;
|
|
414
|
+
const INLINE_LINES = 12;
|
|
415
|
+
const INLINE_HEIGHT = INLINE_LINES * LINE_HEIGHT;
|
|
416
|
+
const EDITOR_WIDTH = 480;
|
|
417
|
+
const TOOLBAR_HEIGHT = 33;
|
|
418
|
+
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/utils.ts
|
|
421
|
+
function detectLanguage(fileName) {
|
|
422
|
+
return {
|
|
423
|
+
ts: "typescript",
|
|
424
|
+
tsx: "typescript",
|
|
425
|
+
js: "javascript",
|
|
426
|
+
jsx: "javascript",
|
|
427
|
+
mjs: "javascript",
|
|
428
|
+
cjs: "javascript"
|
|
429
|
+
}[fileName.split(".").pop()?.toLowerCase() ?? ""] ?? "plaintext";
|
|
430
|
+
}
|
|
431
|
+
/** Convert a file path (URL or absolute) to a monaco file:// URI. */
|
|
432
|
+
function pathToUri(monaco, path) {
|
|
433
|
+
try {
|
|
434
|
+
const { pathname } = new URL(path);
|
|
435
|
+
return monaco.Uri.parse(`file://${pathname}`);
|
|
436
|
+
} catch {
|
|
437
|
+
const normalized = path.replace(/\\/g, "/");
|
|
438
|
+
return monaco.Uri.parse(normalized.startsWith("/") ? `file://${normalized}` : `file:///${normalized}`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/SourcePreview.tsx
|
|
444
|
+
function SourcePreview({ options }) {
|
|
445
|
+
let { theme = "one-dark-pro", disabled = false } = options;
|
|
446
|
+
const [persistedOptions, setPreviewSettings] = (0, jotai.useAtom)(previewSettingsAtom);
|
|
447
|
+
if (!persistedOptions) setPreviewSettings({
|
|
448
|
+
theme,
|
|
449
|
+
disabled
|
|
450
|
+
});
|
|
451
|
+
theme = persistedOptions?.theme || theme;
|
|
452
|
+
disabled = persistedOptions?.disabled ?? disabled;
|
|
453
|
+
const root = (0, _react_trace_enhancer_core.useProjectRoot)();
|
|
454
|
+
const source = (0, _react_trace_enhancer_core.useSelectedSource)();
|
|
455
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
456
|
+
const [dirty, setDirty] = (0, react.useState)(false);
|
|
457
|
+
const [loadState, setLoadState] = (0, react.useState)(fileSystemService.hasAccess ? "loading" : "needs-access");
|
|
458
|
+
const editorRef = (0, react.useRef)(null);
|
|
459
|
+
if (!source) return null;
|
|
460
|
+
const currentPath = source.relativePath;
|
|
461
|
+
const handleGrant = async () => {
|
|
462
|
+
if (await handleGrantAccess(root, () => fileSystemService.requestAccess())) setLoadState("loading");
|
|
463
|
+
};
|
|
464
|
+
const handleSave = () => {
|
|
465
|
+
const val = editorRef.current?.getValue();
|
|
466
|
+
if (val != null) fileSystemService.write(currentPath, val).then(() => setDirty(false)).catch(() => {});
|
|
467
|
+
};
|
|
468
|
+
const editorHeight = expanded ? `calc(80vh - ${!disabled ? TOOLBAR_HEIGHT : 0}px)` : INLINE_HEIGHT - (!disabled ? TOOLBAR_HEIGHT : 0);
|
|
469
|
+
const toolbar = !disabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
470
|
+
style: {
|
|
471
|
+
display: "flex",
|
|
472
|
+
alignItems: "center",
|
|
473
|
+
justifyContent: "space-between",
|
|
474
|
+
height: TOOLBAR_HEIGHT,
|
|
475
|
+
padding: "0 10px",
|
|
476
|
+
borderBottom: "1px solid #27272a",
|
|
477
|
+
flexShrink: 0
|
|
478
|
+
},
|
|
479
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
480
|
+
style: {
|
|
481
|
+
fontSize: 11,
|
|
482
|
+
fontFamily: "ui-monospace, monospace",
|
|
483
|
+
color: "#52525b"
|
|
484
|
+
},
|
|
485
|
+
children: [source.relativePath.split("/").slice(-2).join("/"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
486
|
+
style: { color: "#3f3f46" },
|
|
487
|
+
children: [":", source.lineNumber]
|
|
488
|
+
})]
|
|
489
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
490
|
+
style: {
|
|
491
|
+
display: "flex",
|
|
492
|
+
alignItems: "center",
|
|
493
|
+
gap: 4
|
|
494
|
+
},
|
|
495
|
+
children: [dirty && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_react_trace_enhancer_ui_components.Button, {
|
|
496
|
+
variant: "accent",
|
|
497
|
+
onClick: handleSave,
|
|
498
|
+
title: "Save (⌘S)",
|
|
499
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.SaveIcon, {}), " Save"]
|
|
500
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.IconButton, {
|
|
501
|
+
onClick: () => setExpanded((prev) => !prev),
|
|
502
|
+
title: expanded ? "Collapse (Esc)" : "Expand",
|
|
503
|
+
children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.CollapseIcon, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.ExpandIcon, {})
|
|
504
|
+
})]
|
|
505
|
+
})]
|
|
506
|
+
}) : null;
|
|
507
|
+
const [dismissed, setDismissed] = (0, react.useState)(false);
|
|
508
|
+
const panel = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
509
|
+
style: {
|
|
510
|
+
display: "flex",
|
|
511
|
+
flexDirection: "column",
|
|
512
|
+
width: "100%",
|
|
513
|
+
height: "100%",
|
|
514
|
+
background: "#18181b",
|
|
515
|
+
overflow: "hidden"
|
|
516
|
+
},
|
|
517
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
518
|
+
children: [toolbar, loadState === "needs-access" && !dismissed ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderAccessPrompt, {
|
|
519
|
+
root,
|
|
520
|
+
onGrant: handleGrant,
|
|
521
|
+
onCancel: () => setDismissed(true)
|
|
522
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_monaco_editor_react.default, {
|
|
523
|
+
height: editorHeight,
|
|
524
|
+
width: "100%",
|
|
525
|
+
theme,
|
|
526
|
+
options: {
|
|
527
|
+
readOnly: disabled,
|
|
528
|
+
minimap: { enabled: false },
|
|
529
|
+
lineNumbers: "on",
|
|
530
|
+
scrollBeyondLastLine: false,
|
|
531
|
+
fontSize: 12,
|
|
532
|
+
lineHeight: LINE_HEIGHT,
|
|
533
|
+
folding: false,
|
|
534
|
+
glyphMargin: false,
|
|
535
|
+
automaticLayout: true,
|
|
536
|
+
padding: {
|
|
537
|
+
top: 6,
|
|
538
|
+
bottom: 6
|
|
539
|
+
},
|
|
540
|
+
fixedOverflowWidgets: true
|
|
541
|
+
},
|
|
542
|
+
beforeMount: configureBefore,
|
|
543
|
+
onMount: (editor, monaco) => {
|
|
544
|
+
editorRef.current = editor;
|
|
545
|
+
if (!disabled) {
|
|
546
|
+
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, handleSave);
|
|
547
|
+
editor.addCommand(monaco.KeyCode.Escape, () => setExpanded(false));
|
|
548
|
+
}
|
|
549
|
+
editor.updateOptions({ theme });
|
|
550
|
+
const key = source.relativePath;
|
|
551
|
+
const uri = pathToUri(monaco, key);
|
|
552
|
+
fileSystemService.read(key).then((fileContent) => {
|
|
553
|
+
if (!monaco.editor.getModel(uri)) monaco.editor.createModel(fileContent, detectLanguage(key), uri);
|
|
554
|
+
const model = monaco.editor.getModel(uri);
|
|
555
|
+
if (model) {
|
|
556
|
+
const { lineNumber } = source;
|
|
557
|
+
editor.setModel(model);
|
|
558
|
+
editor.revealLineInCenter(lineNumber);
|
|
559
|
+
ensureHighlightStyle();
|
|
560
|
+
editor.createDecorationsCollection().set([{
|
|
561
|
+
range: new monaco.Range(lineNumber, 1, lineNumber, 1),
|
|
562
|
+
options: {
|
|
563
|
+
isWholeLine: true,
|
|
564
|
+
className: "react-trace-highlighted-line"
|
|
565
|
+
}
|
|
566
|
+
}]);
|
|
567
|
+
}
|
|
568
|
+
}).catch(() => {});
|
|
569
|
+
},
|
|
570
|
+
onChange: () => setDirty(true),
|
|
571
|
+
loading: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
572
|
+
style: {
|
|
573
|
+
padding: "8px 12px",
|
|
574
|
+
color: "#52525b",
|
|
575
|
+
fontSize: 11
|
|
576
|
+
},
|
|
577
|
+
children: "Loading…"
|
|
578
|
+
})
|
|
579
|
+
})]
|
|
580
|
+
});
|
|
581
|
+
if (!expanded) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
582
|
+
onClick: (e) => e.stopPropagation(),
|
|
583
|
+
style: { width: EDITOR_WIDTH },
|
|
584
|
+
children: panel
|
|
585
|
+
});
|
|
586
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
587
|
+
ref: (el) => {
|
|
588
|
+
const parentElement = el?.parentElement?.parentElement;
|
|
589
|
+
if (!parentElement) return;
|
|
590
|
+
const transform = parentElement.style.transform;
|
|
591
|
+
parentElement.style.transform = "";
|
|
592
|
+
return () => {
|
|
593
|
+
parentElement.style.transform = transform;
|
|
594
|
+
};
|
|
595
|
+
},
|
|
596
|
+
style: {
|
|
597
|
+
position: "absolute",
|
|
598
|
+
inset: 0,
|
|
599
|
+
zIndex: 9999999,
|
|
600
|
+
display: "flex",
|
|
601
|
+
alignItems: "center",
|
|
602
|
+
justifyContent: "center"
|
|
603
|
+
},
|
|
604
|
+
onClick: () => setExpanded(false),
|
|
605
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
606
|
+
style: {
|
|
607
|
+
..._react_trace_enhancer_ui_components.panelPopupStyle,
|
|
608
|
+
position: "fixed",
|
|
609
|
+
top: "10vw",
|
|
610
|
+
left: "10vw",
|
|
611
|
+
overflow: "hidden",
|
|
612
|
+
width: "80vw",
|
|
613
|
+
height: "80vh"
|
|
614
|
+
},
|
|
615
|
+
onClick: (e) => e.stopPropagation(),
|
|
616
|
+
children: panel
|
|
617
|
+
})
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region src/index.tsx
|
|
623
|
+
function FolderToolbarIcon({ hasAccess }) {
|
|
624
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
625
|
+
style: {
|
|
626
|
+
position: "relative",
|
|
627
|
+
display: "inline-flex"
|
|
628
|
+
},
|
|
629
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.FolderIcon, {}), hasAccess && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
630
|
+
position: "absolute",
|
|
631
|
+
top: -4,
|
|
632
|
+
right: -4,
|
|
633
|
+
width: 6,
|
|
634
|
+
height: 6,
|
|
635
|
+
borderRadius: "50%",
|
|
636
|
+
background: "#22c55e",
|
|
637
|
+
border: "1.5px solid #18181b",
|
|
638
|
+
pointerEvents: "none"
|
|
639
|
+
} })]
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
function PreviewToolbar() {
|
|
643
|
+
const projectRoot = (0, _react_trace_enhancer_core.useProjectRoot)();
|
|
644
|
+
const portalContainer = (0, _react_trace_enhancer_core.useWidgetPortalContainer)();
|
|
645
|
+
const deactivateInspector = (0, _react_trace_enhancer_core.useDeactivateInspector)();
|
|
646
|
+
const buttonRef = (0, react.useRef)(null);
|
|
647
|
+
const [isPromptOpen, setIsPromptOpen] = (0, react.useState)(false);
|
|
648
|
+
const hasAccess = (0, react.useSyncExternalStore)(fileSystemService.subscribe.bind(fileSystemService), () => fileSystemService.hasAccess, () => false);
|
|
649
|
+
(0, react.useEffect)(() => {
|
|
650
|
+
fileSystemService.tryRestore().catch(() => {});
|
|
651
|
+
}, []);
|
|
652
|
+
const handleClick = () => {
|
|
653
|
+
deactivateInspector();
|
|
654
|
+
if (fileSystemService.hasAccess) {
|
|
655
|
+
fileSystemService.requestAccess().catch(() => {});
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
setIsPromptOpen(true);
|
|
659
|
+
};
|
|
660
|
+
const handleGrant = async () => {
|
|
661
|
+
if (await handleGrantAccess(projectRoot, () => fileSystemService.requestAccess())) setIsPromptOpen(false);
|
|
662
|
+
};
|
|
663
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Tooltip, {
|
|
664
|
+
label: hasAccess ? "Project folder connected" : "Select project folder",
|
|
665
|
+
container: portalContainer,
|
|
666
|
+
render: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.ToolbarButton, { ref: buttonRef }),
|
|
667
|
+
"aria-label": "Project folder",
|
|
668
|
+
style: { color: hasAccess ? "#22c55e" : "#52525b" },
|
|
669
|
+
onClick: handleClick,
|
|
670
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderToolbarIcon, { hasAccess })
|
|
671
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Popover.Root, {
|
|
672
|
+
open: isPromptOpen,
|
|
673
|
+
onOpenChange: (open) => {
|
|
674
|
+
if (!open) setIsPromptOpen(false);
|
|
675
|
+
},
|
|
676
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Popover.Portal, {
|
|
677
|
+
container: portalContainer,
|
|
678
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Popover.Positioner, {
|
|
679
|
+
anchor: buttonRef.current,
|
|
680
|
+
side: "top",
|
|
681
|
+
align: "end",
|
|
682
|
+
sideOffset: 8,
|
|
683
|
+
collisionPadding: 8,
|
|
684
|
+
positionMethod: "fixed",
|
|
685
|
+
style: {
|
|
686
|
+
zIndex: 9999999,
|
|
687
|
+
pointerEvents: "auto"
|
|
688
|
+
},
|
|
689
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_trace_enhancer_ui_components.Popover.Popup, {
|
|
690
|
+
style: {
|
|
691
|
+
width: 280,
|
|
692
|
+
overflow: "hidden"
|
|
693
|
+
},
|
|
694
|
+
onClick: (event) => event.stopPropagation(),
|
|
695
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
696
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FolderAccessPrompt, {
|
|
697
|
+
root: projectRoot,
|
|
698
|
+
onGrant: handleGrant,
|
|
699
|
+
onCancel: () => setIsPromptOpen(false)
|
|
700
|
+
})
|
|
701
|
+
})
|
|
702
|
+
})
|
|
703
|
+
})
|
|
704
|
+
})] });
|
|
705
|
+
}
|
|
706
|
+
function PreviewPlugin(options = {}) {
|
|
707
|
+
function PreviewActionPanel() {
|
|
708
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SourcePreview, { options });
|
|
709
|
+
}
|
|
710
|
+
return {
|
|
711
|
+
name: "preview",
|
|
712
|
+
toolbar: PreviewToolbar,
|
|
713
|
+
actionPanel: PreviewActionPanel,
|
|
714
|
+
settings: PreviewSettings
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
//#endregion
|
|
719
|
+
exports.PreviewPlugin = PreviewPlugin;
|
|
720
|
+
//# sourceMappingURL=index.cjs.map
|