@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/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