lynx-console 0.0.0

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.
Files changed (60) hide show
  1. package/dist/assets/src/components/BottomSheet.css.ts.vanilla-D-1A77Ik.css +83 -0
  2. package/dist/assets/src/components/ConsolePanel.css.ts.vanilla-B3avfSlI.css +246 -0
  3. package/dist/assets/src/components/FloatingButton.css.ts.vanilla-rPj35oLW.css +55 -0
  4. package/dist/assets/src/components/NetworkPanel.css.ts.vanilla-DFMduT0T.css +247 -0
  5. package/dist/assets/src/components/PerformancePanel.css.ts.vanilla-D35LuXlW.css +216 -0
  6. package/dist/assets/src/components/Tabs.css.ts.vanilla-DD7L2oXt.css +50 -0
  7. package/dist/index.cjs +1058 -0
  8. package/dist/index.css +466 -0
  9. package/dist/index.css.map +1 -0
  10. package/dist/index.d.cts +17 -0
  11. package/dist/index.d.cts.map +1 -0
  12. package/dist/index.d.mts +17 -0
  13. package/dist/index.d.mts.map +1 -0
  14. package/dist/index.mjs +1059 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/setup.cjs +377 -0
  17. package/dist/setup.d.cts +15 -0
  18. package/dist/setup.d.cts.map +1 -0
  19. package/dist/setup.d.mts +15 -0
  20. package/dist/setup.d.mts.map +1 -0
  21. package/dist/setup.mjs +374 -0
  22. package/dist/setup.mjs.map +1 -0
  23. package/package.json +51 -0
  24. package/src/components/BottomSheet.css.ts +93 -0
  25. package/src/components/BottomSheet.tsx +142 -0
  26. package/src/components/ConsolePanel.css.ts +261 -0
  27. package/src/components/ConsolePanel.tsx +41 -0
  28. package/src/components/FloatingButton.css.ts +62 -0
  29. package/src/components/FloatingButton.tsx +37 -0
  30. package/src/components/LogPanel.tsx +241 -0
  31. package/src/components/NetworkDetailSection.tsx +42 -0
  32. package/src/components/NetworkPanel.css.ts +280 -0
  33. package/src/components/NetworkPanel.tsx +222 -0
  34. package/src/components/PerformancePanel.css.ts +224 -0
  35. package/src/components/PerformancePanel.tsx +209 -0
  36. package/src/components/Tabs.css.ts +66 -0
  37. package/src/components/Tabs.tsx +81 -0
  38. package/src/globals.d.ts +9 -0
  39. package/src/hooks/index.ts +3 -0
  40. package/src/hooks/useConsole.ts +35 -0
  41. package/src/hooks/useNetwork.ts +36 -0
  42. package/src/hooks/usePerformance.ts +39 -0
  43. package/src/index.tsx +110 -0
  44. package/src/setup/_setupMainThreadConsole.ts +80 -0
  45. package/src/setup/index.ts +4 -0
  46. package/src/setup/setupLogMonitor.ts +78 -0
  47. package/src/setup/setupMainThreadConsole.ts +34 -0
  48. package/src/setup/setupNetworkMonitor.ts +247 -0
  49. package/src/setup/setupPerformanceMonitor.ts +70 -0
  50. package/src/shared/ensureConsoleStructure.ts +20 -0
  51. package/src/styles/getDimensionValue.ts +7 -0
  52. package/src/styles/global.css.ts +10 -0
  53. package/src/styles/tokens.json +80 -0
  54. package/src/styles/typography.ts +25 -0
  55. package/src/styles/vars/color.ts +228 -0
  56. package/src/styles/vars/dimension.ts +79 -0
  57. package/src/styles/vars/index.css +463 -0
  58. package/src/styles/vars/index.ts +22 -0
  59. package/src/styles/vars/radius.ts +12 -0
  60. package/src/types.ts +96 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,1059 @@
1
+ import "./index.css";
2
+ import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from "@lynx-js/react";
3
+ import "./assets/src/components/BottomSheet.css.ts.vanilla-D-1A77Ik.css";
4
+ import "./assets/src/components/ConsolePanel.css.ts.vanilla-B3avfSlI.css";
5
+ import { stringify } from "javascript-stringify";
6
+ import "./assets/src/components/NetworkPanel.css.ts.vanilla-DFMduT0T.css";
7
+ import "./assets/src/components/PerformancePanel.css.ts.vanilla-D35LuXlW.css";
8
+ import "./assets/src/components/Tabs.css.ts.vanilla-DD7L2oXt.css";
9
+ import "./assets/src/components/FloatingButton.css.ts.vanilla-rPj35oLW.css";
10
+
11
+ //#region src/components/BottomSheet.css.ts
12
+ var backdrop = "BottomSheet_backdrop__5pjw6y1";
13
+ var body = "BottomSheet_body__5pjw6y7";
14
+ var content = "BottomSheet_content__5pjw6y2";
15
+ var footer = "BottomSheet_footer__5pjw6y8";
16
+ var handle = "BottomSheet_handle__5pjw6y4";
17
+ var handleContainer = "BottomSheet_handleContainer__5pjw6y3";
18
+ var header$2 = "BottomSheet_header__5pjw6y5";
19
+ var overlay = "BottomSheet_overlay__5pjw6y0";
20
+ var title$1 = "BottomSheet_title__5pjw6y6";
21
+
22
+ //#endregion
23
+ //#region src/components/BottomSheet.tsx
24
+ const MIN_HEIGHT = 200;
25
+ const MAX_HEIGHT = 700;
26
+ const DEFAULT_HEIGHT = 500;
27
+ const CLOSE_DRAG_THRESHOLD = 30;
28
+ let savedHeight = null;
29
+ function BottomSheet({ children, title, footer: footer$1, onClose, isOpen, shouldClose = false, safeAreaInsetBottom = "25px" }) {
30
+ const [sheetHeight, setSheetHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);
31
+ const [tempHeight, setTempHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);
32
+ const [isDragging, setIsDragging] = useState(false);
33
+ const [dragStartY, setDragStartY] = useState(0);
34
+ const [dragStartHeight, setDragStartHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);
35
+ const [isOpening, setIsOpening] = useState(true);
36
+ const [isClosing, setIsClosing] = useState(false);
37
+ const handleClose = () => {
38
+ setIsClosing(true);
39
+ setTimeout(() => {
40
+ onClose();
41
+ }, 300);
42
+ };
43
+ useEffect(() => {
44
+ requestAnimationFrame(() => {
45
+ setIsOpening(false);
46
+ });
47
+ }, []);
48
+ useEffect(() => {
49
+ if (shouldClose && !isClosing) handleClose();
50
+ }, [shouldClose, isClosing]);
51
+ useEffect(() => {
52
+ savedHeight = sheetHeight;
53
+ }, [sheetHeight]);
54
+ if (!isOpen) return null;
55
+ const handleTouchStart = (e) => {
56
+ setIsDragging(true);
57
+ setDragStartY(e.detail.y);
58
+ setDragStartHeight(sheetHeight);
59
+ setTempHeight(sheetHeight);
60
+ };
61
+ const handleTouchMove = (e) => {
62
+ if (!isDragging) return;
63
+ const deltaY = dragStartY - e.detail.y;
64
+ setTempHeight(Math.min(Math.max(dragStartHeight + deltaY, MIN_HEIGHT), MAX_HEIGHT));
65
+ };
66
+ const handleTouchEnd = () => {
67
+ setIsDragging(false);
68
+ const dragDistance = dragStartHeight - tempHeight;
69
+ setSheetHeight(tempHeight);
70
+ if (dragDistance > CLOSE_DRAG_THRESHOLD) handleClose();
71
+ };
72
+ return <scroll-view className={backdrop} style={{ opacity: isOpening || isClosing ? 0 : 1 }}>
73
+ <view className={overlay} bindtap={handleClose}>
74
+ <view className={content} catchtap={() => {}} style={{
75
+ height: `${isDragging ? tempHeight : sheetHeight}px`,
76
+ transform: isOpening || isClosing ? "translateY(100%)" : "translateY(0)",
77
+ transition: isDragging ? "none" : void 0
78
+ }}>
79
+ {}
80
+ <view className={handleContainer} bindtouchstart={handleTouchStart} bindtouchmove={handleTouchMove} bindtouchend={handleTouchEnd}>
81
+ <view className={handle} />
82
+ </view>
83
+ <view className={header$2}>
84
+ {title && <text className={title$1}>{title}</text>}
85
+ </view>
86
+ <view className={body} style={{ paddingBottom: safeAreaInsetBottom }}>
87
+ {children}
88
+ </view>
89
+ {footer$1 && <view className={footer}>{footer$1}</view>}
90
+ </view>
91
+ </view>
92
+ </scroll-view>;
93
+ }
94
+
95
+ //#endregion
96
+ //#region src/hooks/useConsole.ts
97
+ const useConsole = () => {
98
+ const [logs, setLogs] = useState([]);
99
+ useEffect(() => {
100
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs === "undefined") {
101
+ console.warn("[LynxConsole] Log monitoring not initialized");
102
+ return;
103
+ }
104
+ const state = globalThis.__LYNX_CONSOLE__.state;
105
+ setLogs([...state.logs ?? []]);
106
+ const updateLogs = (_entry) => {
107
+ setLogs([...state.logs ?? []]);
108
+ };
109
+ return state.logSubscribe?.(updateLogs);
110
+ }, []);
111
+ const clearLogs = () => {
112
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs !== "undefined") {
113
+ const state = globalThis.__LYNX_CONSOLE__.state;
114
+ state.logs = [];
115
+ setLogs([]);
116
+ }
117
+ };
118
+ return {
119
+ logs,
120
+ clearLogs
121
+ };
122
+ };
123
+
124
+ //#endregion
125
+ //#region src/hooks/useNetwork.ts
126
+ const useNetwork = () => {
127
+ const [networks, setNetworks] = useState([]);
128
+ useEffect(() => {
129
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks === "undefined") {
130
+ console.warn("[LynxConsole] Network monitoring not initialized");
131
+ return;
132
+ }
133
+ const state = globalThis.__LYNX_CONSOLE__.state;
134
+ setNetworks([...state.networks ?? []]);
135
+ const updateNetworks = (_entry) => {
136
+ setNetworks([...state.networks ?? []]);
137
+ };
138
+ return state.subscribeNetwork?.(updateNetworks);
139
+ }, []);
140
+ const clearNetworks = () => {
141
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks !== "undefined") {
142
+ const state = globalThis.__LYNX_CONSOLE__.state;
143
+ state.networks = [];
144
+ state.networksMap?.clear();
145
+ setNetworks([]);
146
+ }
147
+ };
148
+ return {
149
+ networks,
150
+ clearNetworks
151
+ };
152
+ };
153
+
154
+ //#endregion
155
+ //#region src/hooks/usePerformance.ts
156
+ const usePerformance = () => {
157
+ const [performances, setPerformances] = useState([]);
158
+ useEffect(() => {
159
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.performances === "undefined") {
160
+ console.warn("[LynxConsole] Performance monitoring not initialized");
161
+ return;
162
+ }
163
+ const state = globalThis.__LYNX_CONSOLE__.state;
164
+ setPerformances([...state.performances ?? []]);
165
+ const updatePerformances = (_entry) => {
166
+ setPerformances([...state.performances ?? []]);
167
+ };
168
+ return state.subscribePerformance?.(updatePerformances);
169
+ }, []);
170
+ const clearPerformances = () => {
171
+ if (typeof globalThis.__LYNX_CONSOLE__?.state?.performances !== "undefined") {
172
+ const state = globalThis.__LYNX_CONSOLE__.state;
173
+ state.performances = [];
174
+ setPerformances([]);
175
+ }
176
+ };
177
+ return {
178
+ performances,
179
+ clearPerformances
180
+ };
181
+ };
182
+
183
+ //#endregion
184
+ //#region ../node_modules/@vanilla-extract/recipes/dist/createRuntimeFn-62c9670f.esm.js
185
+ function toPrimitive(t, r) {
186
+ if ("object" != typeof t || !t) return t;
187
+ var e = t[Symbol.toPrimitive];
188
+ if (void 0 !== e) {
189
+ var i = e.call(t, r || "default");
190
+ if ("object" != typeof i) return i;
191
+ throw new TypeError("@@toPrimitive must return a primitive value.");
192
+ }
193
+ return ("string" === r ? String : Number)(t);
194
+ }
195
+ function toPropertyKey(t) {
196
+ var i = toPrimitive(t, "string");
197
+ return "symbol" == typeof i ? i : String(i);
198
+ }
199
+ function _defineProperty(obj, key, value) {
200
+ key = toPropertyKey(key);
201
+ if (key in obj) Object.defineProperty(obj, key, {
202
+ value,
203
+ enumerable: true,
204
+ configurable: true,
205
+ writable: true
206
+ });
207
+ else obj[key] = value;
208
+ return obj;
209
+ }
210
+ function ownKeys(e, r) {
211
+ var t = Object.keys(e);
212
+ if (Object.getOwnPropertySymbols) {
213
+ var o = Object.getOwnPropertySymbols(e);
214
+ r && (o = o.filter(function(r) {
215
+ return Object.getOwnPropertyDescriptor(e, r).enumerable;
216
+ })), t.push.apply(t, o);
217
+ }
218
+ return t;
219
+ }
220
+ function _objectSpread2(e) {
221
+ for (var r = 1; r < arguments.length; r++) {
222
+ var t = null != arguments[r] ? arguments[r] : {};
223
+ r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
224
+ _defineProperty(e, r, t[r]);
225
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
226
+ Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
227
+ });
228
+ }
229
+ return e;
230
+ }
231
+ function mapValues(input, fn) {
232
+ var result = {};
233
+ for (var _key in input) result[_key] = fn(input[_key], _key);
234
+ return result;
235
+ }
236
+ var shouldApplyCompound = (compoundCheck, selections, defaultVariants) => {
237
+ for (var key of Object.keys(compoundCheck)) {
238
+ var _selections$key;
239
+ if (compoundCheck[key] !== ((_selections$key = selections[key]) !== null && _selections$key !== void 0 ? _selections$key : defaultVariants[key])) return false;
240
+ }
241
+ return true;
242
+ };
243
+ var createRuntimeFn = (config) => {
244
+ var runtimeFn = (options) => {
245
+ var className = config.defaultClassName;
246
+ var selections = _objectSpread2(_objectSpread2({}, config.defaultVariants), options);
247
+ for (var variantName in selections) {
248
+ var _selections$variantNa;
249
+ var variantSelection = (_selections$variantNa = selections[variantName]) !== null && _selections$variantNa !== void 0 ? _selections$variantNa : config.defaultVariants[variantName];
250
+ if (variantSelection != null) {
251
+ var selection = variantSelection;
252
+ if (typeof selection === "boolean") selection = selection === true ? "true" : "false";
253
+ var selectionClassName = config.variantClassNames[variantName][selection];
254
+ if (selectionClassName) className += " " + selectionClassName;
255
+ }
256
+ }
257
+ for (var [compoundCheck, compoundClassName] of config.compoundVariants) if (shouldApplyCompound(compoundCheck, selections, config.defaultVariants)) className += " " + compoundClassName;
258
+ return className;
259
+ };
260
+ runtimeFn.variants = () => Object.keys(config.variantClassNames);
261
+ runtimeFn.classNames = {
262
+ get base() {
263
+ return config.defaultClassName.split(" ")[0];
264
+ },
265
+ get variants() {
266
+ return mapValues(config.variantClassNames, (classNames) => mapValues(classNames, (className) => className.split(" ")[0]));
267
+ }
268
+ };
269
+ return runtimeFn;
270
+ };
271
+
272
+ //#endregion
273
+ //#region src/components/ConsolePanel.css.ts
274
+ var argNull = "ConsolePanel_argNull__db6kuup";
275
+ var argObject = "ConsolePanel_argObject__db6kuu11";
276
+ var argObjectContent = "ConsolePanel_argObjectContent__db6kuu14";
277
+ var argObjectHeader = "ConsolePanel_argObjectHeader__db6kuu12";
278
+ var argObjectJson = "ConsolePanel_argObjectJson__db6kuu17";
279
+ var argObjectPreview = "ConsolePanel_argObjectPreview__db6kuu13";
280
+ var argPrimitive = createRuntimeFn({
281
+ defaultClassName: "ConsolePanel_argPrimitive__db6kuuw",
282
+ variantClassNames: { level: {
283
+ log: "ConsolePanel_argPrimitive_level_log__db6kuux",
284
+ info: "ConsolePanel_argPrimitive_level_info__db6kuuy",
285
+ warn: "ConsolePanel_argPrimitive_level_warn__db6kuuz",
286
+ error: "ConsolePanel_argPrimitive_level_error__db6kuu10"
287
+ } },
288
+ defaultVariants: {},
289
+ compoundVariants: []
290
+ });
291
+ var argString = createRuntimeFn({
292
+ defaultClassName: "ConsolePanel_argString__db6kuur",
293
+ variantClassNames: { level: {
294
+ log: "ConsolePanel_argString_level_log__db6kuus",
295
+ info: "ConsolePanel_argString_level_info__db6kuut",
296
+ warn: "ConsolePanel_argString_level_warn__db6kuuu",
297
+ error: "ConsolePanel_argString_level_error__db6kuuv"
298
+ } },
299
+ defaultVariants: {},
300
+ compoundVariants: []
301
+ });
302
+ var argUndefined = "ConsolePanel_argUndefined__db6kuuq";
303
+ var clearButton$2 = "ConsolePanel_clearButton__db6kuu6";
304
+ var clearButtonText$2 = "ConsolePanel_clearButtonText__db6kuu7";
305
+ var container$3 = "ConsolePanel_container__db6kuu0";
306
+ var logArgItem = "ConsolePanel_logArgItem__db6kuuo";
307
+ var logArgsContainer = "ConsolePanel_logArgsContainer__db6kuun";
308
+ var logContainer = "ConsolePanel_logContainer__db6kuu3";
309
+ var logCount = "ConsolePanel_logCount__db6kuu5";
310
+ var logHeader = "ConsolePanel_logHeader__db6kuu4";
311
+ var logItem = createRuntimeFn({
312
+ defaultClassName: "ConsolePanel_logItem__db6kuu9",
313
+ variantClassNames: { level: {
314
+ log: "ConsolePanel_logItem_level_log__db6kuua",
315
+ info: "ConsolePanel_logItem_level_info__db6kuub",
316
+ warn: "ConsolePanel_logItem_level_warn__db6kuuc",
317
+ error: "ConsolePanel_logItem_level_error__db6kuud"
318
+ } },
319
+ defaultVariants: {},
320
+ compoundVariants: []
321
+ });
322
+ var logItemHeader = "ConsolePanel_logItemHeader__db6kuue";
323
+ var logLevel = createRuntimeFn({
324
+ defaultClassName: "ConsolePanel_logLevel__db6kuuf",
325
+ variantClassNames: { level: {
326
+ log: "ConsolePanel_logLevel_level_log__db6kuug",
327
+ info: "ConsolePanel_logLevel_level_info__db6kuuh",
328
+ warn: "ConsolePanel_logLevel_level_warn__db6kuui",
329
+ error: "ConsolePanel_logLevel_level_error__db6kuuj"
330
+ } },
331
+ defaultVariants: {},
332
+ compoundVariants: []
333
+ });
334
+ var logList = "ConsolePanel_logList__db6kuu8";
335
+ var logTime = "ConsolePanel_logTime__db6kuuk";
336
+ var placeholder$2 = "ConsolePanel_placeholder__db6kuu1";
337
+ var placeholderText$2 = "ConsolePanel_placeholderText__db6kuu2";
338
+ var replInput = "ConsolePanel_replInput__db6kuu1a";
339
+ var replInputRow = "ConsolePanel_replInputRow__db6kuu18";
340
+ var replPrompt = "ConsolePanel_replPrompt__db6kuu19";
341
+ var replRunButton = "ConsolePanel_replRunButton__db6kuu1b";
342
+ var replRunButtonText = "ConsolePanel_replRunButtonText__db6kuu1c";
343
+ var toggleIndicator = "ConsolePanel_toggleIndicator__db6kuul";
344
+
345
+ //#endregion
346
+ //#region src/components/LogPanel.tsx
347
+ const runCode = (code) => {
348
+ try {
349
+ const result = eval(code);
350
+ if (result instanceof Promise) result.then((r) => console.log(r)).catch((e) => console.error(e));
351
+ else console.log(result);
352
+ } catch (e) {
353
+ console.error(e);
354
+ }
355
+ };
356
+ const LogPanel = ({ logs, clearLogs }) => {
357
+ const [expandedArgs, setExpandedArgs] = useState(/* @__PURE__ */ new Set());
358
+ const [code, setCode] = useState("");
359
+ const inputRef = useRef(null);
360
+ const listRef = useRef(null);
361
+ const logsRef = useRef(logs);
362
+ logsRef.current = logs;
363
+ const scrollToBottom = (smooth) => {
364
+ if (logsRef.current.length === 0) return;
365
+ listRef.current?.invoke({
366
+ method: "scrollToPosition",
367
+ params: {
368
+ position: logsRef.current.length - 1,
369
+ smooth
370
+ }
371
+ }).exec();
372
+ };
373
+ useEffect(() => {
374
+ scrollToBottom(true);
375
+ }, [logs]);
376
+ const toggleArg = (key) => {
377
+ setExpandedArgs((prev) => {
378
+ const next = new Set(prev);
379
+ if (next.has(key)) next.delete(key);
380
+ else next.add(key);
381
+ return next;
382
+ });
383
+ };
384
+ const handleRun = () => {
385
+ const trimmed = code.trim();
386
+ if (!trimmed) return;
387
+ setCode("");
388
+ inputRef.current?.invoke({
389
+ method: "setValue",
390
+ params: { value: "" }
391
+ }).exec();
392
+ runCode(trimmed);
393
+ setTimeout(() => scrollToBottom(false), 100);
394
+ };
395
+ const renderArg = (arg, parentKey, level) => {
396
+ const key = parentKey;
397
+ const isExpanded = expandedArgs.has(key);
398
+ if (arg === null) return <text className={argNull}>null</text>;
399
+ if (arg === void 0) return <text className={argUndefined}>undefined</text>;
400
+ if (typeof arg === "string") {
401
+ const MAX_LENGTH = 80;
402
+ if (!(arg.length > MAX_LENGTH)) return <text className={argString({ level })}>{arg}</text>;
403
+ return <view className={argObject}>
404
+ <view className={argObjectHeader} bindtap={() => toggleArg(key)}>
405
+ <text className={toggleIndicator}>
406
+ {isExpanded ? "▼" : "▶"}
407
+ </text>
408
+ <text className={argString({ level })}>
409
+ {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}
410
+ </text>
411
+ </view>
412
+ </view>;
413
+ }
414
+ if (typeof arg === "number" || typeof arg === "boolean") return <text className={argPrimitive({ level })}>{String(arg)}</text>;
415
+ if (typeof arg === "object") {
416
+ let preview = "Object";
417
+ if (Array.isArray(arg)) preview = `Array(${arg.length})`;
418
+ else if (arg instanceof Map) preview = `Map(${arg.size})`;
419
+ else if (arg instanceof Set) preview = `Set(${arg.size})`;
420
+ else if (arg instanceof Date) preview = `Date`;
421
+ else if (arg instanceof RegExp) preview = `RegExp`;
422
+ else if (arg instanceof Error) preview = `${arg.constructor.name}`;
423
+ else if (arg?.constructor?.name && arg.constructor.name !== "Object") preview = arg.constructor.name;
424
+ let jsonString;
425
+ if (arg instanceof Map) jsonString = `{\n${Array.from(arg.entries()).map(([k, v]) => ` [${stringify(k)}, ${stringify(v)}]`).join(",\n")}\n}`;
426
+ else if (arg instanceof Set) jsonString = `{\n${Array.from(arg.values()).map((v) => stringify(v)).join(", ")}\n}`;
427
+ else jsonString = stringify(arg, null, 2, { references: true }) ?? String(arg);
428
+ return <view className={argObject}>
429
+ <view className={argObjectHeader} bindtap={() => toggleArg(key)}>
430
+ <text className={toggleIndicator}>
431
+ {isExpanded ? "▼" : "▶"}
432
+ </text>
433
+ <text className={argObjectPreview}>{preview}</text>
434
+ </view>
435
+ {isExpanded && <view className={argObjectContent}>
436
+ <text className={argObjectJson}>{jsonString}</text>
437
+ </view>}
438
+ </view>;
439
+ }
440
+ return <text className={argPrimitive({ level })}>{String(arg)}</text>;
441
+ };
442
+ return <view className={logContainer}>
443
+ <view className={logHeader}>
444
+ <text className={logCount}>Total {logs.length} logs</text>
445
+ <view style={{
446
+ display: "flex",
447
+ flexDirection: "row",
448
+ gap: 8
449
+ }}>
450
+ <view className={clearButton$2} bindtap={clearLogs}>
451
+ <text className={clearButtonText$2}>Clear</text>
452
+ </view>
453
+ </view>
454
+ </view>
455
+ <list ref={listRef} scroll-orientation="vertical" className={logList} initial-scroll-index={Math.max(0, logs.length - 1)}>
456
+ {logs.length === 0 ? <list-item item-key="empty-state">
457
+ <view className={placeholder$2}>
458
+ <text className={placeholderText$2}>
459
+ No logs yet. Try console.log("Hello!")
460
+ </text>
461
+ </view>
462
+ </list-item> : logs.map((log) => {
463
+ return <list-item key={log.id} item-key={log.id}>
464
+ <view className={logItem({ level: log.level })}>
465
+ <view className={logItemHeader}>
466
+ <text className={logLevel({ level: log.level })}>
467
+ {log.level.toUpperCase()}
468
+ </text>
469
+ <text className={logTime}>
470
+ {new Date(log.timestamp).toISOString()}
471
+ </text>
472
+ </view>
473
+ <view className={logArgsContainer}>
474
+ {log.args.map((arg, index) => <view key={`${log.id}-${index.toString()}`} className={logArgItem}>
475
+ {renderArg(arg, `${log.id}-${index.toString()}`, log.level)}
476
+ </view>)}
477
+ </view>
478
+ </view>
479
+ </list-item>;
480
+ })}
481
+ </list>
482
+ <view className={replInputRow}>
483
+ <text className={replPrompt}>{"›"}</text>
484
+ <input ref={inputRef} className={replInput} placeholder="enter code..." bindinput={(e) => setCode(e.detail.value)} bindconfirm={handleRun} />
485
+ <view className={replRunButton} bindtap={handleRun}>
486
+ <text className={replRunButtonText}>Run</text>
487
+ </view>
488
+ </view>
489
+ </view>;
490
+ };
491
+
492
+ //#endregion
493
+ //#region src/components/NetworkPanel.css.ts
494
+ var bodyText = "NetworkPanel_bodyText__1kf6i8q16";
495
+ var clearButton$1 = "NetworkPanel_clearButton__1kf6i8q3";
496
+ var clearButtonText$1 = "NetworkPanel_clearButtonText__1kf6i8q4";
497
+ var container$2 = "NetworkPanel_container__1kf6i8q0";
498
+ var count$1 = "NetworkPanel_count__1kf6i8q2";
499
+ var detailSection = "NetworkPanel_detailSection__1kf6i8q10";
500
+ var detailSectionTitle = "NetworkPanel_detailSectionTitle__1kf6i8q11";
501
+ var detailsContainer$1 = "NetworkPanel_detailsContainer__1kf6i8qr";
502
+ var emptyText = "NetworkPanel_emptyText__1kf6i8q18";
503
+ var errorText = "NetworkPanel_errorText__1kf6i8q17";
504
+ var header$1 = "NetworkPanel_header__1kf6i8q1";
505
+ var item$1 = createRuntimeFn({
506
+ defaultClassName: "NetworkPanel_item__1kf6i8q8",
507
+ variantClassNames: { status: {
508
+ pending: "NetworkPanel_item_status_pending__1kf6i8q9",
509
+ success: "NetworkPanel_item_status_success__1kf6i8qa",
510
+ error: "NetworkPanel_item_status_error__1kf6i8qb"
511
+ } },
512
+ defaultVariants: {},
513
+ compoundVariants: []
514
+ });
515
+ var itemHeader$1 = "NetworkPanel_itemHeader__1kf6i8qc";
516
+ var list$1 = "NetworkPanel_list__1kf6i8q5";
517
+ var method = createRuntimeFn({
518
+ defaultClassName: "NetworkPanel_method__1kf6i8qd",
519
+ variantClassNames: { type: {
520
+ GET: "NetworkPanel_method_type_GET__1kf6i8qe",
521
+ POST: "NetworkPanel_method_type_POST__1kf6i8qf",
522
+ PUT: "NetworkPanel_method_type_PUT__1kf6i8qg",
523
+ PATCH: "NetworkPanel_method_type_PATCH__1kf6i8qh",
524
+ DELETE: "NetworkPanel_method_type_DELETE__1kf6i8qi"
525
+ } },
526
+ defaultVariants: {},
527
+ compoundVariants: []
528
+ });
529
+ var path = "NetworkPanel_path__1kf6i8qp";
530
+ var placeholder$1 = "NetworkPanel_placeholder__1kf6i8q6";
531
+ var placeholderText$1 = "NetworkPanel_placeholderText__1kf6i8q7";
532
+ var statusCode = createRuntimeFn({
533
+ defaultClassName: "NetworkPanel_statusCode__1kf6i8qj",
534
+ variantClassNames: { type: {
535
+ success: "NetworkPanel_statusCode_type_success__1kf6i8qk",
536
+ error: "NetworkPanel_statusCode_type_error__1kf6i8ql",
537
+ pending: "NetworkPanel_statusCode_type_pending__1kf6i8qm"
538
+ } },
539
+ defaultVariants: {},
540
+ compoundVariants: []
541
+ });
542
+ var tab = createRuntimeFn({
543
+ defaultClassName: "NetworkPanel_tab__1kf6i8qt",
544
+ variantClassNames: { active: {
545
+ true: "NetworkPanel_tab_active_true__1kf6i8qu",
546
+ false: "NetworkPanel_tab_active_false__1kf6i8qv"
547
+ } },
548
+ defaultVariants: {},
549
+ compoundVariants: []
550
+ });
551
+ var tabContent$1 = "NetworkPanel_tabContent__1kf6i8qz";
552
+ var tabText = createRuntimeFn({
553
+ defaultClassName: "NetworkPanel_tabText__1kf6i8qw",
554
+ variantClassNames: { active: {
555
+ true: "NetworkPanel_tabText_active_true__1kf6i8qx",
556
+ false: "NetworkPanel_tabText_active_false__1kf6i8qy"
557
+ } },
558
+ defaultVariants: {},
559
+ compoundVariants: []
560
+ });
561
+ var table = "NetworkPanel_table__1kf6i8q12";
562
+ var tableKey = "NetworkPanel_tableKey__1kf6i8q14";
563
+ var tableRow = "NetworkPanel_tableRow__1kf6i8q13";
564
+ var tableValue = "NetworkPanel_tableValue__1kf6i8q15";
565
+ var tabs$1 = "NetworkPanel_tabs__1kf6i8qs";
566
+ var time = "NetworkPanel_time__1kf6i8qn";
567
+
568
+ //#endregion
569
+ //#region src/components/NetworkDetailSection.tsx
570
+ const NetworkDetailSection = ({ headers = {}, body = "", error = "" }) => {
571
+ return <>
572
+ {}
573
+ <view className={detailSection}>
574
+ <text className={detailSectionTitle}>Headers</text>
575
+ {headers && Object.keys(headers).length > 0 ? <view className={table}>
576
+ {Object.entries(headers).map(([key, value]) => <view key={key} className={tableRow}>
577
+ <text className={tableKey}>{key}</text>
578
+ <text className={tableValue}>{value}</text>
579
+ </view>)}
580
+ </view> : <text className={emptyText}>No headers</text>}
581
+ </view>
582
+
583
+ {}
584
+ <view className={detailSection}>
585
+ <text className={detailSectionTitle}>Body</text>
586
+ {error && <text className={errorText}>{error}</text>}
587
+ {body && <text className={bodyText}>{body}</text>}
588
+ {!error && !body && <text className={emptyText}>No body</text>}
589
+ </view>
590
+ </>;
591
+ };
592
+
593
+ //#endregion
594
+ //#region src/components/NetworkPanel.tsx
595
+ const NetworkPanel = ({ networks, clearNetworks }) => {
596
+ const [selectedId, setSelectedId] = useState(null);
597
+ const [activeTab, setActiveTab] = useState("general");
598
+ const formatDuration = (duration) => {
599
+ if (!duration) return "-";
600
+ if (duration < 1e3) return `${duration}ms`;
601
+ return `${(duration / 1e3).toFixed(2)}s`;
602
+ };
603
+ const extractPath = (url) => {
604
+ const pathMatch = url.match(/^https?:\/\/[^/]+(.*)$/);
605
+ if (pathMatch?.[1]) return pathMatch[1].startsWith("/") ? pathMatch[1].slice(1) : pathMatch[1];
606
+ return url;
607
+ };
608
+ const getGeneralInfo = (network) => {
609
+ return [
610
+ {
611
+ key: "URL",
612
+ value: network.url
613
+ },
614
+ {
615
+ key: "Method",
616
+ value: network.method
617
+ },
618
+ network.statusCode ? {
619
+ key: "Status",
620
+ value: String(network.statusCode)
621
+ } : null,
622
+ {
623
+ key: "Request Time",
624
+ value: new Date(network.startTime).toISOString()
625
+ },
626
+ network.endTime ? {
627
+ key: "Response Time",
628
+ value: new Date(network.endTime).toISOString()
629
+ } : null,
630
+ network.duration ? {
631
+ key: "Duration",
632
+ value: formatDuration(network.duration)
633
+ } : null
634
+ ].filter((item) => item !== null);
635
+ };
636
+ const getStatusCodeVariant = (status, statusCode) => {
637
+ if (status === "pending") return "pending";
638
+ if (status === "error") return "error";
639
+ if (statusCode && statusCode >= 200 && statusCode < 300) return "success";
640
+ return "error";
641
+ };
642
+ return <view className={container$2}>
643
+ <view className={header$1}>
644
+ <text className={count$1}>Total: {networks.length} requests</text>
645
+ <view className={clearButton$1} bindtap={clearNetworks}>
646
+ <text className={clearButtonText$1}>Clear</text>
647
+ </view>
648
+ </view>
649
+
650
+ {networks.length === 0 ? <view className={placeholder$1}>
651
+ <text className={placeholderText$1}>No network requests yet</text>
652
+ </view> : <list className={list$1}>
653
+ {networks.map((network) => <list-item key={network.id} item-key={network.id}>
654
+ <view className={item$1({ status: network.status })}>
655
+ <view className={itemHeader$1} bindtap={() => setSelectedId(selectedId === network.id ? null : network.id)}>
656
+ <text className={method({ type: network.method })}>
657
+ {network.method}
658
+ </text>
659
+ {network.statusCode && <text className={statusCode({ type: getStatusCodeVariant(network.status, network.statusCode) })}>
660
+ {network.statusCode}
661
+ </text>}
662
+ {network.status === "pending" && <text className={statusCode({ type: "pending" })}>
663
+ Pending...
664
+ </text>}
665
+ <text className={time}>
666
+ {formatDuration(network.duration)}
667
+ </text>
668
+ <text className={time}>
669
+ {new Date(network.startTime).toISOString()}
670
+ </text>
671
+ </view>
672
+
673
+ <text className={path} bindtap={() => setSelectedId(selectedId === network.id ? null : network.id)}>
674
+ {extractPath(network.url)}
675
+ </text>
676
+
677
+ {selectedId === network.id && <view className={detailsContainer$1}>
678
+ {}
679
+ <view className={tabs$1}>
680
+ <view className={tab({ active: activeTab === "general" })} bindtap={() => setActiveTab("general")}>
681
+ <text className={tabText({ active: activeTab === "general" })}>
682
+ General
683
+ </text>
684
+ </view>
685
+ <view className={tab({ active: activeTab === "request" })} bindtap={() => setActiveTab("request")}>
686
+ <text className={tabText({ active: activeTab === "request" })}>
687
+ Request
688
+ </text>
689
+ </view>
690
+ <view className={tab({ active: activeTab === "response" })} bindtap={() => setActiveTab("response")}>
691
+ <text className={tabText({ active: activeTab === "response" })}>
692
+ Response
693
+ </text>
694
+ </view>
695
+ </view>
696
+
697
+ {}
698
+ <view className={tabContent$1}>
699
+ {activeTab === "general" && <view className={table}>
700
+ {getGeneralInfo(network).map((item) => <view key={item.key} className={tableRow}>
701
+ <text className={tableKey}>{item.key}</text>
702
+ <text className={tableValue}>
703
+ {item.value}
704
+ </text>
705
+ </view>)}
706
+ </view>}
707
+
708
+ {activeTab === "request" && <NetworkDetailSection headers={network.requestHeaders} body={network.requestBody} />}
709
+
710
+ {activeTab === "response" && <NetworkDetailSection headers={network.responseHeaders} body={network.responseBody} error={network.error} />}
711
+ </view>
712
+ </view>}
713
+ </view>
714
+ </list-item>)}
715
+ </list>}
716
+ </view>;
717
+ };
718
+
719
+ //#endregion
720
+ //#region src/components/PerformancePanel.css.ts
721
+ var clearButton = "PerformancePanel_clearButton__19p8bog3";
722
+ var clearButtonText = "PerformancePanel_clearButtonText__19p8bog4";
723
+ var container$1 = "PerformancePanel_container__19p8bog0";
724
+ var count = "PerformancePanel_count__19p8bog2";
725
+ var detailTitle = "PerformancePanel_detailTitle__19p8bogw";
726
+ var detailsContainer = "PerformancePanel_detailsContainer__19p8bogn";
727
+ var entryName = "PerformancePanel_entryName__19p8bogf";
728
+ var entryType = createRuntimeFn({
729
+ defaultClassName: "PerformancePanel_entryType__19p8boga",
730
+ variantClassNames: { type: {
731
+ init: "PerformancePanel_entryType_type_init__19p8bogb",
732
+ metric: "PerformancePanel_entryType_type_metric__19p8bogc",
733
+ pipeline: "PerformancePanel_entryType_type_pipeline__19p8bogd",
734
+ resource: "PerformancePanel_entryType_type_resource__19p8boge"
735
+ } },
736
+ defaultVariants: {},
737
+ compoundVariants: []
738
+ });
739
+ var fcpHighlight = "PerformancePanel_fcpHighlight__19p8bogi";
740
+ var fcpMetric = "PerformancePanel_fcpMetric__19p8bogq";
741
+ var fcpMetricDescription = "PerformancePanel_fcpMetricDescription__19p8bogt";
742
+ var fcpMetricHeader = "PerformancePanel_fcpMetricHeader__19p8bogh";
743
+ var fcpMetricName = "PerformancePanel_fcpMetricName__19p8bogr";
744
+ var fcpMetricValue = "PerformancePanel_fcpMetricValue__19p8bogs";
745
+ var fcpSection = "PerformancePanel_fcpSection__19p8bogo";
746
+ var header = "PerformancePanel_header__19p8bog1";
747
+ var item = "PerformancePanel_item__19p8bog8";
748
+ var itemHeader = "PerformancePanel_itemHeader__19p8bog9";
749
+ var list = "PerformancePanel_list__19p8bog5";
750
+ var placeholder = "PerformancePanel_placeholder__19p8bog6";
751
+ var placeholderText = "PerformancePanel_placeholderText__19p8bog7";
752
+ var rawEntry = "PerformancePanel_rawEntry__19p8bogx";
753
+ var rawEntrySection = "PerformancePanel_rawEntrySection__19p8bogv";
754
+ var timestamp = "PerformancePanel_timestamp__19p8bogg";
755
+
756
+ //#endregion
757
+ //#region src/components/PerformancePanel.tsx
758
+ const isMetricFcpEntry = (entry) => {
759
+ return entry.entryType === "metric" && entry.name === "fcp";
760
+ };
761
+ const extractFcpMetrics = (entry) => {
762
+ if (!isMetricFcpEntry(entry) || !entry.rawEntry) return null;
763
+ const metricEntry = entry.rawEntry;
764
+ return {
765
+ totalFcp: metricEntry.totalFcp ?? void 0,
766
+ lynxFcp: metricEntry.lynxFcp ?? void 0,
767
+ fcp: metricEntry.fcp ?? void 0
768
+ };
769
+ };
770
+ const formatDuration = (ms) => {
771
+ if (ms === void 0) return "-";
772
+ return `${ms.toFixed(2)}ms`;
773
+ };
774
+ const getPrimaryFcpLabel = (entry) => {
775
+ const fcpMetrics = extractFcpMetrics(entry);
776
+ if (!fcpMetrics) return "";
777
+ const { totalFcp, lynxFcp, fcp } = fcpMetrics;
778
+ if (totalFcp?.duration !== void 0) return `totalFcp: ${formatDuration(totalFcp.duration)}`;
779
+ if (lynxFcp?.duration !== void 0) return `lynxFcp: ${formatDuration(lynxFcp.duration)}`;
780
+ if (fcp?.duration !== void 0) return `fcp: ${formatDuration(fcp.duration)}`;
781
+ return "";
782
+ };
783
+ const PerformancePanel = ({ performances, clearPerformances }) => {
784
+ const [selectedId, setSelectedId] = useState(null);
785
+ if (performances.length === 0) return <view className={container$1}>
786
+ <view className={header}>
787
+ <text className={count}>0 entries</text>
788
+ <view bindtap={() => {
789
+ console.log("[PerformancePanel] performances", performances);
790
+ }} style={{
791
+ padding: "10px",
792
+ backgroundColor: "red"
793
+ }}>
794
+ <text>Log</text>
795
+ </view>
796
+ <view bindtap={clearPerformances} className={clearButton}>
797
+ <text className={clearButtonText}>Clear</text>
798
+ </view>
799
+ </view>
800
+ <view className={placeholder}>
801
+ <text className={placeholderText}>
802
+ No performance data yet...
803
+ </text>
804
+ </view>
805
+ </view>;
806
+ return <view className={container$1}>
807
+ <view className={header}>
808
+ <text className={count}>{performances.length} entries</text>
809
+ <view bindtap={clearPerformances} className={clearButton}>
810
+ <text className={clearButtonText}>Clear</text>
811
+ </view>
812
+ </view>
813
+
814
+ <list className={list}>
815
+ {performances.map((perf) => {
816
+ const isMetricFcp = isMetricFcpEntry(perf);
817
+ const fcpMetrics = extractFcpMetrics(perf);
818
+ const primaryFcp = getPrimaryFcpLabel(perf);
819
+ const { totalFcp, lynxFcp, fcp } = fcpMetrics ?? {};
820
+ return <list-item key={perf.id} item-key={perf.id}>
821
+ <view className={item}>
822
+ <view className={itemHeader} bindtap={() => setSelectedId(selectedId === perf.id ? null : perf.id)}>
823
+ <text className={entryType({ type: perf.entryType })}>
824
+ {perf.entryType}
825
+ </text>
826
+ <text className={entryName}>{perf.name}</text>
827
+ <text className={timestamp}>
828
+ {new Date(perf.timestamp).toISOString()}
829
+ </text>
830
+ </view>
831
+
832
+ <view bindtap={() => setSelectedId(selectedId === perf.id ? null : perf.id)}>
833
+ {isMetricFcp && primaryFcp && <text className={fcpHighlight}>{primaryFcp}</text>}
834
+ </view>
835
+
836
+ {selectedId === perf.id && <view className={detailsContainer}>
837
+ {isMetricFcp && fcpMetrics && <view className={fcpSection}>
838
+ {totalFcp !== void 0 && <view className={fcpMetric}>
839
+ <view className={fcpMetricHeader}>
840
+ <text className={fcpMetricName}>
841
+ 전체 FCP
842
+ </text>
843
+ <text className={fcpMetricValue}>
844
+ {formatDuration(totalFcp.duration)}
845
+ </text>
846
+ </view>
847
+ <text className={fcpMetricDescription}>
848
+ PrepareTemplate Start부터 Paint End 까지 걸리는
849
+ 시간
850
+ </text>
851
+ </view>}
852
+
853
+ {lynxFcp !== void 0 && <view className={fcpMetric}>
854
+ <view className={fcpMetricHeader}>
855
+ <text className={fcpMetricName}>LynxFCP</text>
856
+ <text className={fcpMetricValue}>
857
+ {formatDuration(lynxFcp.duration)}
858
+ </text>
859
+ </view>
860
+ <text className={fcpMetricDescription}>
861
+ Bundle Load 시작부터 Paint End 까지 걸리는 시간
862
+ </text>
863
+ </view>}
864
+
865
+ {fcp !== void 0 && <view className={fcpMetric}>
866
+ <view className={fcpMetricHeader}>
867
+ <text className={fcpMetricName}>
868
+ 렌더링 FCP
869
+ </text>
870
+ <text className={fcpMetricValue}>
871
+ {formatDuration(fcp.duration)}
872
+ </text>
873
+ </view>
874
+ <text className={fcpMetricDescription}>
875
+ TemplateBundle 준비부터 Paint End 까지 걸리는 시간
876
+ </text>
877
+ </view>}
878
+ </view>}
879
+
880
+ {!!perf.rawEntry && <view className={rawEntrySection}>
881
+ <text className={detailTitle}>Raw Entry</text>
882
+ <text className={rawEntry}>
883
+ {String(stringify(perf.rawEntry, null, 2, { references: true }))}
884
+ </text>
885
+ </view>}
886
+ </view>}
887
+ </view>
888
+ </list-item>;
889
+ })}
890
+ </list>
891
+ </view>;
892
+ };
893
+
894
+ //#endregion
895
+ //#region src/components/Tabs.css.ts
896
+ var tabContent = "Tabs_tabContent__ud1j9z8";
897
+ var tabContents = "Tabs_tabContents__ud1j9z7";
898
+ var tabHeader = "Tabs_tabHeader__ud1j9z1";
899
+ var tabTriggerButton = "Tabs_tabTriggerButton__ud1j9z2";
900
+ var tabTriggerButtonText = createRuntimeFn({
901
+ defaultClassName: "Tabs_tabTriggerButtonText__ud1j9z3",
902
+ variantClassNames: { active: { true: "Tabs_tabTriggerButtonText_active_true__ud1j9z4" } },
903
+ defaultVariants: {},
904
+ compoundVariants: []
905
+ });
906
+ var tabTriggerIndicator = "Tabs_tabTriggerIndicator__ud1j9z5";
907
+ var tabTriggerIndicatorLine = "Tabs_tabTriggerIndicatorLine__ud1j9z6";
908
+ var tabs = "Tabs_tabs__ud1j9z0";
909
+
910
+ //#endregion
911
+ //#region src/components/Tabs.tsx
912
+ function Tabs(props) {
913
+ const tabContentsRef = useRef(null);
914
+ const [activeIndex, setActiveIndex] = useState(0);
915
+ return <view className={tabs}>
916
+ <view className={tabHeader}>
917
+ {props.items.map((item, i) => <view key={item.key} className={tabTriggerButton} bindtap={() => {
918
+ setActiveIndex(i);
919
+ tabContentsRef.current?.invoke({
920
+ method: "scrollToPosition",
921
+ params: {
922
+ position: i,
923
+ smooth: true
924
+ }
925
+ }).exec();
926
+ }}>
927
+ <text className={tabTriggerButtonText({ active: i === activeIndex })}>
928
+ {item.label}
929
+ </text>
930
+ {i === 0 && <view className={tabTriggerIndicator} style={{ transform: `translateX(${activeIndex * 100}%)` }}>
931
+ <view className={tabTriggerIndicatorLine} />
932
+ </view>}
933
+ </view>)}
934
+ </view>
935
+
936
+ <list ref={tabContentsRef} className={tabContents} scroll-orientation="horizontal" item-snap={{
937
+ factor: 0,
938
+ offset: 0
939
+ }} bindsnap={(e) => {
940
+ setActiveIndex(e.detail.position);
941
+ }} bounces={false} preload-buffer-count={props.items.length}>
942
+ {props.items.map((item) => <list-item key={item.key} item-key={item.key} recyclable={false} className={tabContent}>
943
+ {item.renderContent()}
944
+ </list-item>)}
945
+ </list>
946
+ </view>;
947
+ }
948
+
949
+ //#endregion
950
+ //#region src/components/ConsolePanel.tsx
951
+ const ConsolePanel = () => {
952
+ const { logs, clearLogs } = useConsole();
953
+ const { networks, clearNetworks } = useNetwork();
954
+ const { performances, clearPerformances } = usePerformance();
955
+ return <view className={container$3}>
956
+ <Tabs items={[
957
+ {
958
+ key: "log",
959
+ label: "Log",
960
+ renderContent: () => <LogPanel logs={logs} clearLogs={clearLogs} />
961
+ },
962
+ {
963
+ key: "network",
964
+ label: "Network",
965
+ renderContent: () => <NetworkPanel networks={networks} clearNetworks={clearNetworks} />
966
+ },
967
+ {
968
+ key: "performance",
969
+ label: "Performance",
970
+ renderContent: () => <PerformancePanel performances={performances} clearPerformances={clearPerformances} />
971
+ }
972
+ ]} />
973
+ </view>;
974
+ };
975
+
976
+ //#endregion
977
+ //#region src/components/FloatingButton.css.ts
978
+ var button = "FloatingButton_button__1homwpu2";
979
+ var container = "FloatingButton_container__1homwpu1";
980
+ var reloadButton = "FloatingButton_reloadButton__1homwpu5";
981
+ var reloadIcon = "FloatingButton_reloadIcon__1homwpu6";
982
+ var subtitle = "FloatingButton_subtitle__1homwpu4";
983
+ var title = "FloatingButton_title__1homwpu3";
984
+ var wrapper = "FloatingButton_wrapper__1homwpu0";
985
+
986
+ //#endregion
987
+ //#region src/components/FloatingButton.tsx
988
+ const FloatingButton = ({ bindtap, isVisible, children }) => {
989
+ if (!isVisible) return null;
990
+ const handleReload = () => {
991
+ try {
992
+ lynx.reload({}, () => {
993
+ console.log("reloaded!");
994
+ });
995
+ } catch (e) {
996
+ console.error("[LynxConsole] reload failed:", e);
997
+ }
998
+ };
999
+ return <view className={wrapper}>
1000
+ <view className={container} bindtap={bindtap}>
1001
+ <view className={button}>{children}</view>
1002
+ </view>
1003
+ <view className={reloadButton} bindtap={handleReload}>
1004
+ <text className={reloadIcon}>{"↻"}</text>
1005
+ </view>
1006
+ </view>;
1007
+ };
1008
+
1009
+ //#endregion
1010
+ //#region src/index.tsx
1011
+ const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px" }, ref) => {
1012
+ const [isOpen, setIsOpen] = useState(false);
1013
+ const [shouldClose, setShouldClose] = useState(false);
1014
+ const { performances } = usePerformance();
1015
+ const latestFcp = useMemo(() => {
1016
+ for (let i = performances.length - 1; i >= 0; i--) {
1017
+ const perf = performances[i];
1018
+ if (perf && perf.entryType === "metric" && perf.name === "fcp") {
1019
+ const metricEntry = perf.rawEntry;
1020
+ if (metricEntry?.totalFcp?.duration !== void 0) return metricEntry.totalFcp;
1021
+ if (metricEntry?.lynxFcp?.duration !== void 0) return metricEntry.lynxFcp;
1022
+ }
1023
+ }
1024
+ }, [performances]);
1025
+ useImperativeHandle(ref, () => ({
1026
+ open: () => {
1027
+ setIsOpen(true);
1028
+ setShouldClose(false);
1029
+ },
1030
+ close: () => {
1031
+ setShouldClose(true);
1032
+ },
1033
+ isOpen: () => isOpen
1034
+ }));
1035
+ const handleOpenBottomSheet = () => {
1036
+ setIsOpen(true);
1037
+ setShouldClose(false);
1038
+ };
1039
+ const handleCloseBottomSheet = () => {
1040
+ setIsOpen(false);
1041
+ setShouldClose(false);
1042
+ };
1043
+ const themeClass = `data-seed-color-mode__${theme}-only`;
1044
+ return <view className={themeClass}>
1045
+ <FloatingButton bindtap={handleOpenBottomSheet} isVisible={!isOpen}>
1046
+ <text className={title}>LynxConsole</text>
1047
+ <text className={subtitle}>
1048
+ {`${latestFcp?.name ?? "FCP"}: ${latestFcp?.duration ? latestFcp.duration.toFixed(2) : "--"}ms`}
1049
+ </text>
1050
+ </FloatingButton>
1051
+ {isOpen && <BottomSheet isOpen={isOpen} shouldClose={shouldClose} onClose={handleCloseBottomSheet} title="Lynx Console" safeAreaInsetBottom={safeAreaInsetBottom}>
1052
+ <ConsolePanel />
1053
+ </BottomSheet>}
1054
+ </view>;
1055
+ });
1056
+
1057
+ //#endregion
1058
+ export { LynxConsole as default };
1059
+ //# sourceMappingURL=index.mjs.map