lynx-console 0.3.0 → 0.3.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 CHANGED
@@ -1,16 +1,37 @@
1
+ [한국어](https://github.com/daangn/lynx-console/blob/main/package/README_ko.md) | English
2
+
1
3
  # lynx-console
2
4
 
3
5
  An in-app developer console that can be embedded in Lynx apps. View console logs, network requests, and performance metrics in real time.
4
6
 
7
+ ## Demo
8
+
9
+ https://github.com/user-attachments/assets/dcd874bf-ff2e-4a98-ae03-d83de5fae31c
10
+
11
+ <img width="450" height="450" alt="lynx_bundle_qrcode_fullscreen" src="https://github.com/user-attachments/assets/8bbb9bfe-df2b-436d-ad17-6e4eb4b672c9" />
12
+
13
+ Scan the QR code above with the [Lynx Explorer](https://lynxjs.org/guide/start/quick-start.html#via-lynx-explorer-app) app to try the demo.
14
+
5
15
  ## Features
6
16
 
7
- - **Console Logs** — View output from `console.log`, `console.error`, and more in real time
8
- - **Main Thread Console** — Capture logs from the main thread
9
- - **Network Monitor** — Inspect method, headers, body, and response of `fetch` requests
10
- - **Performance Monitor** — Track performance metrics such as FCP (First Contentful Paint)
11
- - **Floating Button** — Open and close the console with a floating button that displays the FCP value
17
+ - **Console Logs** — View output from `console.log`, `console.error`, and more in real time. Supports level filtering, keyword search, log clearing, and a built-in REPL
18
+ - **Main Thread Console** — Capture logs from the main thread alongside background thread logs
19
+
20
+ https://github.com/user-attachments/assets/539fe31a-aca4-468d-b673-3b070b21cd08
21
+
22
+ - **Network Monitor** — Inspect method, status, headers, request body, and response of `fetch` requests
23
+
24
+ https://github.com/user-attachments/assets/edda4778-ab8d-4cb9-a3c5-bd8c42c81bde
25
+
26
+ - **Performance Monitor** — Track FCP (First Contentful Paint) and other performance metrics with raw entry details
27
+
28
+ https://github.com/user-attachments/assets/d231bdf5-71bb-483f-9bdb-5843279c1308
29
+
30
+ - **Floating Button** — Displays the latest FCP value; tap to open the console, long-press and drag to reposition it
31
+ - **Resizable Panel** — Drag the handle to resize the console panel (200–700px); swipe down to dismiss
32
+ - **Tab Visibility** — Only tabs for initialized monitors are shown; uninitialized monitors are automatically hidden
33
+ - **Custom Tabs** — Add your own tabs to the console via the `customTabs` prop
12
34
  - **Light/Dark Theme** support
13
- - **Seed Design** based UI
14
35
 
15
36
  ## Installation
16
37
 
@@ -22,10 +43,29 @@ yarn add lynx-console
22
43
 
23
44
  ```bash
24
45
  yarn add @lynx-js/react @lynx-js/types
46
+ yarn add -D @types/react
25
47
  ```
26
48
 
49
+ > **Note:** Each monitor requires the corresponding Lynx API to be available at runtime. If `lynx.fetch` is not present, `initNetworkMonitor()` will be skipped with a warning. Likewise, `initPerformanceMonitor()` requires `lynx.performance`.
50
+
27
51
  ## Usage
28
52
 
53
+ ### 0. Configure Build (Required for iOS)
54
+
55
+ On iOS, the Lynx runtime (JSC) injects a separate `console` object that is different from `globalThis.console`. This means patches applied by `initLogMonitor()` won't take effect on iOS unless you explicitly replace the `console` identifier with `globalThis.console` at build time.
56
+
57
+ Add the following to your `lynx.config.ts`:
58
+
59
+ ```typescript
60
+ export default defineConfig({
61
+ source: {
62
+ define: {
63
+ console: "globalThis.console",
64
+ },
65
+ },
66
+ });
67
+ ```
68
+
29
69
  ### 1. Initialize Monitors
30
70
 
31
71
  Call the monitoring functions at your app's entry point. This setup must run **before** the `LynxConsole` component is rendered.
@@ -44,6 +84,8 @@ initNetworkMonitor();
44
84
  initPerformanceMonitor();
45
85
  ```
46
86
 
87
+ > **Note:** `initLogMonitor()` must be called before `initMainThreadConsole()`, as the main thread console depends on the log monitor being initialized first.
88
+
47
89
  ### 2. Render the Component
48
90
 
49
91
  ```tsx
@@ -68,7 +110,31 @@ function App() {
68
110
  {/* Your app content */}
69
111
  <Suspense>
70
112
  <LynxConsole theme="light" safeAreaInsetBottom="34px" />
71
- <Suspense>
113
+ </Suspense>
114
+ </view>
115
+ );
116
+ }
117
+ ```
118
+
119
+ ### Adding Custom Tabs
120
+
121
+ You can add your own tabs to the console using the `customTabs` prop.
122
+
123
+ ```tsx
124
+ import LynxConsole, { type CustomTab } from "lynx-console";
125
+
126
+ const customTabs: CustomTab[] = [
127
+ {
128
+ key: "debug",
129
+ label: "Debug",
130
+ renderContent: () => <text>Custom debug content</text>,
131
+ },
132
+ ];
133
+
134
+ function App() {
135
+ return (
136
+ <view>
137
+ <LynxConsole customTabs={customTabs} />
72
138
  </view>
73
139
  );
74
140
  }
@@ -99,34 +165,45 @@ function App() {
99
165
  <view>
100
166
  <Suspense>
101
167
  <LynxConsole ref={consoleRef} />
102
- <Suspense>
168
+ </Suspense>
103
169
  </view>
104
170
  );
105
171
  }
106
172
  ```
107
173
 
174
+ You can also integrate it with a back press handler so that the console closes when the back button is pressed.
175
+
108
176
  ## API
109
177
 
110
178
  ### `LynxConsole` Props
111
179
 
112
- | Prop | Type | Default | Description |
113
- |------|------|---------|-------------|
114
- | `theme` | `"light" \| "dark"` | `"light"` | Console UI theme |
115
- | `safeAreaInsetBottom` | `string` | `"50px"` | Bottom safe area inset |
180
+ | Prop | Type | Default | Description |
181
+ | --------------------- | ------------------- | ----------- | ------------------------------------------------ |
182
+ | `theme` | `"light" \| "dark"` | `"light"` | Console UI theme |
183
+ | `safeAreaInsetBottom` | `string` | `"50px"` | Bottom safe area inset |
184
+ | `customTabs` | `CustomTab[]` | `undefined` | Additional custom tabs to display in the console |
185
+
186
+ ### `CustomTab`
187
+
188
+ | Property | Type | Description |
189
+ | --------------- | ----------------- | ------------------------------------- |
190
+ | `key` | `string` | Unique identifier for the tab |
191
+ | `label` | `string` | Tab label text |
192
+ | `renderContent` | `() => ReactNode` | Function that renders the tab content |
116
193
 
117
194
  ### `LynxConsoleHandle`
118
195
 
119
- | Method | Description |
120
- |--------|-------------|
121
- | `open()` | Opens the console |
122
- | `close()` | Closes the console |
196
+ | Method | Description |
197
+ | ---------- | ----------------------------------- |
198
+ | `open()` | Opens the console |
199
+ | `close()` | Closes the console |
123
200
  | `isOpen()` | Returns whether the console is open |
124
201
 
125
202
  ### Monitor Initialization Functions
126
203
 
127
- | Function | Description |
128
- |----------|-------------|
129
- | `initLogMonitor()` | Captures `console.log`, `console.error`, etc. |
130
- | `initMainThreadConsole()` | Captures console output from the main thread |
131
- | `initNetworkMonitor()` | Intercepts and records `fetch` requests |
132
- | `initPerformanceMonitor()` | Collects performance metrics |
204
+ | Function | Description |
205
+ | -------------------------- | --------------------------------------------- |
206
+ | `initLogMonitor()` | Captures `console.log`, `console.error`, etc. |
207
+ | `initMainThreadConsole()` | Captures console output from the main thread |
208
+ | `initNetworkMonitor()` | Intercepts and records `fetch` requests |
209
+ | `initPerformanceMonitor()` | Collects performance metrics |
package/dist/index.cjs CHANGED
@@ -400,7 +400,6 @@ const LogPanel = ({ logs, clearLogs }) => {
400
400
  params: { value: "" }
401
401
  }).exec();
402
402
  runCode(trimmed);
403
- setTimeout(() => scrollToBottom(false), 100);
404
403
  };
405
404
  const renderArg = (arg, parentKey, level) => {
406
405
  const key = parentKey;
package/dist/index.mjs CHANGED
@@ -400,7 +400,6 @@ const LogPanel = ({ logs, clearLogs }) => {
400
400
  params: { value: "" }
401
401
  }).exec();
402
402
  runCode(trimmed);
403
- setTimeout(() => scrollToBottom(false), 100);
404
403
  };
405
404
  const renderArg = (arg, parentKey, level) => {
406
405
  const key = parentKey;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/styles/theme.ts","../src/styles/ThemeContext.ts","../src/components/BottomSheet.tsx","../src/hooks/useConsole.ts","../src/hooks/useNetwork.ts","../src/hooks/usePerformance.ts","../src/components/LogPanel.tsx","../src/components/NetworkDetailSection.tsx","../src/components/NetworkPanel.tsx","../src/components/PerformancePanel.tsx","../src/components/Tabs.tsx","../src/components/ConsolePanel.tsx","../src/hooks/useLongPressDrag.ts","../src/components/FloatingButton.tsx","../src/index.tsx"],"sourcesContent":["export type Theme = \"light\" | \"dark\";\n\nconst lightColors = {\n palette: {\n blue100: \"#eff6ff\",\n blue600: \"#5e98fe\",\n gray100: \"#f7f8f9\",\n gray400: \"#dcdee3\",\n green100: \"#edfaf6\",\n green600: \"#10ab7d\",\n purple100: \"#f5f3fe\",\n purple600: \"#9f84fb\",\n red100: \"#fdf0f0\",\n red600: \"#fc6a66\",\n red900: \"#921708\",\n staticWhite: \"#ffffff\",\n yellow100: \"#fff7de\",\n yellow600: \"#c49725\",\n yellow900: \"#4f3e1f\",\n },\n fg: {\n neutral: \"#1a1c20\",\n placeholder: \"#b0b3ba\",\n disabled: \"#d1d3d8\",\n neutralMuted: \"#555d6d\",\n neutralSubtle: \"#868b94\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#ffffff\",\n layerFloating: \"#ffffff\",\n neutralWeak: \"#f3f4f5\",\n },\n stroke: {\n neutralSubtle: \"#0000000c\",\n neutralWeak: \"#dcdee3\",\n },\n} as const;\n\nconst darkColors = {\n palette: {\n blue100: \"#202742\",\n blue600: \"#1e82eb\",\n gray100: \"#16171b\",\n gray400: \"#393d46\",\n green100: \"#202926\",\n green600: \"#1b946d\",\n purple100: \"#28213b\",\n purple600: \"#8e6bee\",\n red100: \"#322323\",\n red600: \"#f73526\",\n red900: \"#f8c5c3\",\n staticWhite: \"#ffffff\",\n yellow100: \"#302819\",\n yellow600: \"#b6720d\",\n yellow900: \"#e5d49b\",\n },\n fg: {\n neutral: \"#f3f4f5\",\n placeholder: \"#868b94\",\n disabled: \"#5b606a\",\n neutralMuted: \"#dcdee3\",\n neutralSubtle: \"#b0b3ba\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#16171b\",\n layerFloating: \"#1d2025\",\n neutralWeak: \"#2b2e35\",\n },\n stroke: {\n neutralSubtle: \"#ffffff0d\",\n neutralWeak: \"#393d46\",\n },\n} as const;\n\nconst colorMap = {\n light: lightColors,\n dark: darkColors,\n} as const;\n\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n bold: \"700\",\n} as const;\n\nexport const duration = {\n d4: \"200ms\",\n d6: \"300ms\",\n} as const;\n\nexport function getColors(theme: Theme) {\n return colorMap[theme];\n}\n\nexport type ThemeColors = ReturnType<typeof getColors>;\n","import { createContext, useContext } from \"@lynx-js/react\";\nimport { getColors, type ThemeColors } from \"./theme\";\n\nconst ThemeContext = createContext<ThemeColors>(getColors(\"light\"));\n\nexport const ThemeProvider = ThemeContext.Provider;\n\nexport function useThemeColors(): ThemeColors {\n return useContext(ThemeContext);\n}\n","import { type ReactNode, useEffect, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration, fontWeight } from \"../styles/theme\";\nimport \"./BottomSheet.css\";\n\ninterface BottomSheetProps {\n children: ReactNode;\n title?: string;\n footer?: ReactNode;\n onClose: () => void;\n isOpen: boolean;\n shouldClose?: boolean;\n safeAreaInsetBottom?: string;\n}\n\nconst MIN_HEIGHT = 200;\nconst MAX_HEIGHT = 700;\nconst DEFAULT_HEIGHT = 500;\nconst CLOSE_DRAG_THRESHOLD = 30; // 30px 이상 아래로 드래그하면 닫힘\n\n// 마지막 높이 저장\nlet savedHeight: number | null = null;\n\nexport default function BottomSheet({\n children,\n title,\n footer,\n onClose,\n isOpen,\n shouldClose = false,\n safeAreaInsetBottom = \"25px\",\n}: BottomSheetProps) {\n const colors = useThemeColors();\n const [sheetHeight, setSheetHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [tempHeight, setTempHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [isDragging, setIsDragging] = useState(false);\n const [dragStartY, setDragStartY] = useState(0);\n const [dragStartHeight, setDragStartHeight] = useState(\n savedHeight ?? DEFAULT_HEIGHT,\n );\n const [isOpening, setIsOpening] = useState(true);\n const [isClosing, setIsClosing] = useState(false);\n\n // 닫기 애니메이션 처리\n const handleClose = () => {\n setIsClosing(true);\n setTimeout(() => {\n onClose();\n }, 300);\n };\n\n // 아래에서 올라오는 애니메이션\n useEffect(() => {\n requestAnimationFrame(() => {\n setIsOpening(false);\n });\n }, []);\n\n // 외부에서 닫기 요청 시 애니메이션 처리\n useEffect(() => {\n if (shouldClose && !isClosing) {\n handleClose();\n }\n }, [shouldClose, isClosing]);\n\n // 높이 변경 시 저장\n useEffect(() => {\n savedHeight = sheetHeight;\n }, [sheetHeight]);\n\n if (!isOpen) return null;\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n setIsDragging(true);\n setDragStartY(e.detail.y);\n setDragStartHeight(sheetHeight);\n setTempHeight(sheetHeight);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n if (!isDragging) return;\n const deltaY = dragStartY - e.detail.y;\n const newHeight = Math.min(\n Math.max(dragStartHeight + deltaY, MIN_HEIGHT),\n MAX_HEIGHT,\n );\n setTempHeight(newHeight);\n };\n\n const handleTouchEnd = () => {\n setIsDragging(false);\n\n // 아래로 일정 30px 이상 드래그하면 닫기\n const dragDistance = dragStartHeight - tempHeight;\n setSheetHeight(tempHeight);\n if (dragDistance > CLOSE_DRAG_THRESHOLD) {\n handleClose();\n }\n };\n\n return (\n <scroll-view\n className=\"bs-backdrop\"\n style={{\n background: colors.bg.overlay,\n opacity: isOpening || isClosing ? 0 : 1,\n transition: `opacity ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n <view className=\"bs-overlay\" bindtap={handleClose}>\n <view\n className=\"bs-content\"\n catchtap={() => {}}\n style={{\n background: colors.bg.layerFloating,\n height: `${isDragging ? tempHeight : sheetHeight}px`,\n transform:\n isOpening || isClosing ? \"translateY(100%)\" : \"translateY(0)\",\n transition: isDragging\n ? \"none\"\n : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n {/* catchtap: 이벤트 버블링 차단 */}\n <view\n className=\"bs-handleContainer\"\n bindtouchstart={handleTouchStart}\n bindtouchmove={handleTouchMove}\n bindtouchend={handleTouchEnd}\n >\n <view\n className=\"bs-handle\"\n style={{ backgroundColor: colors.palette.gray400 }}\n />\n </view>\n <view className=\"bs-header\">\n {title && (\n <text\n className=\"bs-title t7\"\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n {title}\n </text>\n )}\n </view>\n <view\n className=\"bs-body\"\n style={{\n paddingBottom: safeAreaInsetBottom,\n }}\n >\n {children}\n </view>\n {footer && <view className=\"bs-footer\">{footer}</view>}\n </view>\n </view>\n </scroll-view>\n );\n}\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { LogEntry } from \"../types\";\n\nexport const useConsole = () => {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs === \"undefined\") {\n console.warn(\"[LynxConsole] Log monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setLogs([...(state.logs ?? [])]);\n\n const updateLogs = (_entry: LogEntry) => {\n setLogs([...(state.logs ?? [])]);\n };\n\n const unsubscribe = state.logSubscribe?.(updateLogs);\n\n return unsubscribe;\n }, []);\n\n const clearLogs = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.logs = [];\n setLogs([]);\n }\n };\n\n return { logs, clearLogs };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { NetworkEntry } from \"../types\";\n\nexport const useNetwork = () => {\n const [networks, setNetworks] = useState<NetworkEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks === \"undefined\") {\n console.warn(\"[LynxConsole] Network monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setNetworks([...(state.networks ?? [])]);\n\n const updateNetworks = (_entry: NetworkEntry) => {\n setNetworks([...(state.networks ?? [])]);\n };\n\n const unsubscribe = state.subscribeNetwork?.(updateNetworks);\n\n return unsubscribe;\n }, []);\n\n const clearNetworks = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.networks = [];\n state.networksMap?.clear();\n setNetworks([]);\n }\n };\n\n return { networks, clearNetworks };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { PerformanceEntryData } from \"../types\";\n\nexport const usePerformance = () => {\n const [performances, setPerformances] = useState<PerformanceEntryData[]>([]);\n\n useEffect(() => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances === \"undefined\"\n ) {\n console.warn(\"[LynxConsole] Performance monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setPerformances([...(state.performances ?? [])]);\n\n const updatePerformances = (_entry: PerformanceEntryData) => {\n setPerformances([...(state.performances ?? [])]);\n };\n\n const unsubscribe = state.subscribePerformance?.(updatePerformances);\n\n return unsubscribe;\n }, []);\n\n const clearPerformances = () => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances !== \"undefined\"\n ) {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.performances = [];\n setPerformances([]);\n }\n };\n\n return { performances, clearPerformances };\n};\n","import { useEffect, useMemo, useRef, useState } from \"@lynx-js/react\";\nimport type { BaseEvent, InputInputEvent, NodesRef } from \"@lynx-js/types\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { LogEntry, LogLevel } from \"../types\";\nimport \"./ConsolePanel.css\";\n\nconst LOG_LEVELS: LogLevel[] = [\"log\", \"info\", \"warn\", \"error\"];\n\nlet savedEnabledLevels: Set<LogLevel> | null = null;\nlet savedSearchQuery = \"\";\nlet closeFilterDropdown: (() => void) | null = null;\n\nexport const dismissFilterDropdown = () => closeFilterDropdown?.();\n\ninterface LogPanelProps {\n logs: LogEntry[];\n clearLogs: () => void;\n}\n\nconst runCode = (code: string) => {\n try {\n // biome-ignore lint: intentional REPL tool\n const result = eval(code);\n if (result instanceof Promise) {\n result.then((r) => console.log(r)).catch((e) => console.error(e));\n } else {\n console.log(result);\n }\n } catch (e) {\n console.error(e);\n }\n};\n\nfunction getLevelColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"log\":\n return colors.palette.green600;\n case \"info\":\n return colors.palette.blue600;\n case \"warn\":\n return colors.palette.yellow600;\n case \"error\":\n return colors.palette.red600;\n }\n}\n\nfunction getLogItemBg(\n colors: ThemeColors,\n level: LogLevel,\n): string | undefined {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nfunction getStringColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.fg.neutral;\n }\n}\n\nfunction getPrimitiveColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.palette.blue600;\n }\n}\n\nexport const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {\n const colors = useThemeColors();\n const [expandedArgs, setExpandedArgs] = useState(new Set());\n const [code, setCode] = useState(\"\");\n const [enabledLevels, setEnabledLevels] = useState<Set<LogLevel>>(\n () => savedEnabledLevels ?? new Set(LOG_LEVELS),\n );\n const [filterOpen, setFilterOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(savedSearchQuery);\n const inputRef = useRef<NodesRef>(null);\n const searchInputRef = useRef<NodesRef>(null);\n const listRef = useRef<NodesRef>(null);\n\n useEffect(() => {\n savedEnabledLevels = enabledLevels;\n }, [enabledLevels]);\n\n useEffect(() => {\n savedSearchQuery = searchQuery;\n }, [searchQuery]);\n\n useEffect(() => {\n if (savedSearchQuery) {\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: savedSearchQuery } })\n .exec();\n }\n }, []);\n\n useEffect(() => {\n closeFilterDropdown = () => setFilterOpen(false);\n return () => {\n closeFilterDropdown = null;\n };\n }, []);\n\n const filteredLogs = useMemo(\n () =>\n logs.filter((log) => {\n if (!enabledLevels.has(log.level)) return false;\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return log.args.some((arg) =>\n String(arg).toLowerCase().includes(query),\n );\n }\n return true;\n }),\n [logs, enabledLevels, searchQuery],\n );\n const logsRef = useRef(filteredLogs);\n logsRef.current = filteredLogs;\n\n const toggleLevel = (level: LogLevel) => {\n setEnabledLevels((prev) => {\n const next = new Set(prev);\n if (next.has(level)) {\n next.delete(level);\n } else {\n next.add(level);\n }\n return next;\n });\n };\n\n const scrollToBottom = (smooth: boolean) => {\n if (logsRef.current.length === 0) return;\n listRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: { position: logsRef.current.length - 1, smooth },\n })\n .exec();\n };\n\n useEffect(() => {\n scrollToBottom(true);\n }, [filteredLogs]);\n\n const toggleArg = (key: string) => {\n setExpandedArgs((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n };\n\n const handleRun = () => {\n const trimmed = code.trim();\n if (!trimmed) return;\n\n setCode(\"\");\n inputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n runCode(trimmed);\n setTimeout(() => scrollToBottom(false), 100);\n };\n\n const renderArg = (\n arg: unknown,\n parentKey: string,\n level: \"log\" | \"info\" | \"warn\" | \"error\",\n ): React.ReactNode => {\n const key = parentKey;\n const isExpanded = expandedArgs.has(key);\n\n if (arg === null) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n null\n </text>\n );\n }\n\n if (arg === undefined) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n undefined\n </text>\n );\n }\n\n if (typeof arg === \"string\") {\n const MAX_LENGTH = 80;\n const shouldTruncate = arg.length > MAX_LENGTH;\n const strColor = getStringColor(colors, level);\n\n if (!shouldTruncate) {\n return (\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {arg}\n </text>\n );\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}\n </text>\n </view>\n </view>\n );\n }\n\n if (typeof arg === \"number\" || typeof arg === \"boolean\") {\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n }\n\n if (typeof arg === \"object\") {\n let preview = \"Object\";\n if (Array.isArray(arg)) {\n preview = `Array(${arg.length})`;\n } else if (arg instanceof Map) {\n preview = `Map(${arg.size})`;\n } else if (arg instanceof Set) {\n preview = `Set(${arg.size})`;\n } else if (arg instanceof Date) {\n preview = `Date`;\n } else if (arg instanceof RegExp) {\n preview = `RegExp`;\n } else if (arg instanceof Error) {\n preview = `${arg.constructor.name}`;\n } else if (arg?.constructor?.name && arg.constructor.name !== \"Object\") {\n preview = arg.constructor.name;\n }\n\n let jsonString: string;\n if (arg instanceof Map) {\n const entries = Array.from(arg.entries()).map(\n ([k, v]) => ` [${stringify(k)}, ${stringify(v)}]`,\n );\n jsonString = `{\\n${entries.join(\",\\n\")}\\n}`;\n } else if (arg instanceof Set) {\n const values = Array.from(arg.values()).map((v) => stringify(v));\n jsonString = `{\\n${values.join(\", \")}\\n}`;\n } else {\n jsonString =\n stringify(arg, null, 2, { references: true }) ?? String(arg);\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argObjectPreview t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {preview}\n </text>\n </view>\n {isExpanded && (\n <view className={\"cp-argObjectContent\"}>\n <text\n className={\"cp-argObjectJson t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {jsonString}\n </text>\n </view>\n )}\n </view>\n );\n }\n\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n };\n\n return (\n <view\n className={\"cp-logContainer\"}\n bindtap={() => {\n if (filterOpen) setFilterOpen(false);\n }}\n >\n <view className={\"cp-logHeader\"}>\n <view className={\"cp-filterWrapper\"}>\n <view\n className={\"cp-filterButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n catchtap={() => setFilterOpen((v) => !v)}\n >\n <text\n className={\"cp-filterButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n Filter ▼\n </text>\n </view>\n {filterOpen && (\n <view\n className={\"cp-filterDropdown\"}\n style={{\n backgroundColor: colors.bg.layerFloating,\n borderColor: colors.stroke.neutralSubtle,\n }}\n catchtap={() => {}}\n >\n {LOG_LEVELS.map((level) => (\n <view\n key={level}\n className={\"cp-filterOption\"}\n bindtap={() => toggleLevel(level)}\n >\n <text\n className={\"cp-filterCheckbox t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {enabledLevels.has(level) ? \"✅\" : \"⬜\"}\n </text>\n <text\n className={\"cp-filterLabel t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {level.toUpperCase()}\n </text>\n </view>\n ))}\n </view>\n )}\n </view>\n <view\n className={\"cp-searchWrapper\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"cp-searchPrompt t6\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={searchInputRef}\n className={\"cp-searchInput t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"Search logs...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setSearchQuery(e.detail.value)\n }\n />\n {searchQuery.length > 0 && (\n <view\n className={\"cp-searchClear\"}\n bindtap={() => {\n setSearchQuery(\"\");\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n }}\n >\n <text\n className={\"cp-searchClearText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n ✕\n </text>\n </view>\n )}\n </view>\n <view style={{ display: \"flex\", flexDirection: \"row\", gap: 8 }}>\n <view\n className={\"cp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearLogs}\n >\n <text\n className={\"cp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n </view>\n <list\n ref={listRef}\n scroll-orientation=\"vertical\"\n className={\"cp-logList\"}\n preload-buffer-count={10}\n initial-scroll-index={Math.max(0, filteredLogs.length - 1)}\n >\n {filteredLogs.length === 0 ? (\n <list-item item-key=\"empty-state\">\n <view className={\"cp-placeholder\"}>\n <text\n className={\"cp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No logs yet. Try console.log(\"Hello!\")\n </text>\n </view>\n </list-item>\n ) : (\n filteredLogs.map((log) => {\n return (\n <list-item key={log.id} item-key={log.id}>\n <view\n className={\"cp-logItem\"}\n style={{\n backgroundColor: getLogItemBg(colors, log.level),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view className={\"cp-logItemHeader\"}>\n <text\n className={\"cp-logLevel t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getLevelColor(colors, log.level),\n }}\n >\n {log.level.toUpperCase()}\n </text>\n <text\n className={\"cp-logTime t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(log.timestamp).toISOString()}\n </text>\n </view>\n <view className={\"cp-logArgsContainer\"}>\n {log.args.map((arg, index) => (\n <view\n key={`${log.id}-${index.toString()}`}\n className={\"cp-logArgItem\"}\n style={{ fontWeight: fontWeight.regular }}\n >\n {renderArg(\n arg,\n `${log.id}-${index.toString()}`,\n log.level,\n )}\n </view>\n ))}\n </view>\n </view>\n </list-item>\n );\n })\n )}\n </list>\n <view className={\"cp-replInputRow\"}>\n <text\n className={\"cp-replPrompt t10\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={inputRef}\n className={\"cp-replInput t5\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"enter code...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setCode(e.detail.value)\n }\n bindconfirm={handleRun}\n />\n <view\n className={\"cp-replRunButton\"}\n style={{ backgroundColor: colors.palette.green100 }}\n bindtap={handleRun}\n >\n <text\n className={\"cp-replRunButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.palette.green600,\n }}\n >\n Run\n </text>\n </view>\n </view>\n </view>\n );\n};\n","import { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkDetailSectionProps {\n headers?: Record<string, string> | undefined;\n body?: string | undefined;\n error?: string | undefined;\n}\n\nexport const NetworkDetailSection = ({\n headers = {},\n body = \"\",\n error = \"\",\n}: NetworkDetailSectionProps) => {\n const colors = useThemeColors();\n\n return (\n <>\n {/* Headers */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Headers\n </text>\n {headers && Object.keys(headers).length > 0 ? (\n <view className={\"np-table\"}>\n {Object.entries(headers).map(([key, value]) => (\n <view\n key={key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {value}\n </text>\n </view>\n ))}\n </view>\n ) : (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No headers\n </text>\n )}\n </view>\n\n {/* Body */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Body\n </text>\n {error && (\n <text\n className={\"np-errorText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n }}\n >\n {error}\n </text>\n )}\n {body && (\n <text\n className={\"np-bodyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n }}\n >\n {body}\n </text>\n )}\n {!error && !body && (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No body\n </text>\n )}\n </view>\n </>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { NetworkEntry } from \"../types\";\nimport { NetworkDetailSection } from \"./NetworkDetailSection\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkPanelProps {\n networks: NetworkEntry[];\n clearNetworks: () => void;\n}\n\ntype TabType = \"general\" | \"request\" | \"response\";\n\nfunction getMethodColors(colors: ThemeColors, method: string) {\n switch (method) {\n case \"GET\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"POST\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"PUT\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n case \"PATCH\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"DELETE\":\n return {\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nfunction getStatusCodeColor(\n colors: ThemeColors,\n variant: \"success\" | \"error\" | \"pending\",\n): string {\n switch (variant) {\n case \"success\":\n return colors.palette.green600;\n case \"error\":\n return colors.palette.red600;\n case \"pending\":\n return colors.fg.neutralSubtle;\n }\n}\n\nfunction getItemBg(colors: ThemeColors, status: string): string | undefined {\n switch (status) {\n case \"pending\":\n return colors.palette.gray100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nexport const NetworkPanel = ({\n networks,\n clearNetworks,\n}: NetworkPanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabType>(\"general\");\n const formatDuration = (duration?: number): string => {\n if (!duration) return \"-\";\n if (duration < 1000) return `${duration}ms`;\n return `${(duration / 1000).toFixed(2)}s`;\n };\n\n const extractPath = (url: string): string => {\n const pathMatch = url.match(/^https?:\\/\\/[^/]+(.*)$/);\n if (pathMatch?.[1]) {\n return pathMatch[1].startsWith(\"/\")\n ? pathMatch[1].slice(1)\n : pathMatch[1];\n }\n return url;\n };\n\n const getGeneralInfo = (network: NetworkEntry) => {\n return [\n { key: \"URL\", value: network.url },\n { key: \"Method\", value: network.method },\n network.statusCode\n ? { key: \"Status\", value: String(network.statusCode) }\n : null,\n {\n key: \"Request Time\",\n value: new Date(network.startTime).toISOString(),\n },\n network.endTime\n ? {\n key: \"Response Time\",\n value: new Date(network.endTime).toISOString(),\n }\n : null,\n network.duration\n ? { key: \"Duration\", value: formatDuration(network.duration) }\n : null,\n ].filter((item) => item !== null);\n };\n\n const getStatusCodeVariant = (\n status: string,\n statusCode?: number,\n ): \"success\" | \"error\" | \"pending\" => {\n if (status === \"pending\") return \"pending\";\n if (status === \"error\") return \"error\";\n if (statusCode && statusCode >= 200 && statusCode < 300) return \"success\";\n return \"error\";\n };\n\n return (\n <view className={\"np-container\"}>\n <view className={\"np-header\"}>\n <text\n className={\"np-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Total: {networks.length} requests\n </text>\n <view\n className={\"np-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearNetworks}\n >\n <text\n className={\"np-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n {networks.length === 0 ? (\n <view className={\"np-placeholder\"}>\n <text\n className={\"np-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No network requests yet\n </text>\n </view>\n ) : (\n <list scroll-orientation=\"vertical\" className={\"np-list\"}>\n {networks.map((network) => (\n <list-item key={network.id} item-key={network.id}>\n <view\n className={\"np-item\"}\n style={{\n backgroundColor: getItemBg(colors, network.status),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view\n className={\"np-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n <text\n className={\"np-method t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getMethodColors(colors, network.method),\n }}\n >\n {network.method}\n </text>\n {network.statusCode && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getStatusCodeColor(\n colors,\n getStatusCodeVariant(\n network.status,\n network.statusCode,\n ),\n ),\n }}\n >\n {network.statusCode}\n </text>\n )}\n {network.status === \"pending\" && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n Pending...\n </text>\n )}\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {formatDuration(network.duration)}\n </text>\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(network.startTime).toISOString()}\n </text>\n </view>\n\n <text\n className={\"np-path t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n {extractPath(network.url)}\n </text>\n\n {selectedId === network.id && (\n <view\n className={\"np-detailsContainer\"}\n style={{ borderTopColor: colors.stroke.neutralSubtle }}\n >\n {/* Tabs */}\n <view className={\"np-tabs\"}>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"general\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"general\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"general\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n General\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"request\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"request\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"request\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Request\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"response\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"response\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"response\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Response\n </text>\n </view>\n </view>\n\n {/* Tab Content */}\n <view className={\"np-tabContent\"}>\n {activeTab === \"general\" && (\n <view className={\"np-table\"}>\n {getGeneralInfo(network).map((item) => (\n <view\n key={item.key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {item.key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {item.value}\n </text>\n </view>\n ))}\n </view>\n )}\n\n {activeTab === \"request\" && (\n <NetworkDetailSection\n headers={network.requestHeaders}\n body={network.requestBody}\n />\n )}\n\n {activeTab === \"response\" && (\n <NetworkDetailSection\n headers={network.responseHeaders}\n body={network.responseBody}\n error={network.error}\n />\n )}\n </view>\n </view>\n )}\n </view>\n </list-item>\n ))}\n </list>\n )}\n </view>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { PerformanceEntryData } from \"../types\";\nimport \"./PerformancePanel.css\";\n\ninterface PerformancePanelProps {\n performances: PerformanceEntryData[];\n clearPerformances: () => void;\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst isMetricFcpEntry = (entry: PerformanceEntryData): boolean => {\n return entry.entryType === \"metric\" && entry.name === \"fcp\";\n};\n\nconst extractFcpMetrics = (entry: PerformanceEntryData) => {\n if (!isMetricFcpEntry(entry) || !entry.rawEntry) {\n return null;\n }\n\n const metricEntry = entry.rawEntry as MetricFcpEntry;\n\n return {\n totalFcp: metricEntry.totalFcp ?? undefined,\n lynxFcp: metricEntry.lynxFcp ?? undefined,\n fcp: metricEntry.fcp ?? undefined,\n };\n};\n\nconst formatDuration = (ms?: number): string => {\n if (ms === undefined) return \"-\";\n return `${ms.toFixed(2)}ms`;\n};\n\nconst getPrimaryFcpLabel = (entry: PerformanceEntryData): string => {\n const fcpMetrics = extractFcpMetrics(entry);\n if (!fcpMetrics) return \"\";\n\n const { totalFcp, lynxFcp, fcp } = fcpMetrics;\n\n if (totalFcp?.duration !== undefined) {\n return `totalFcp: ${formatDuration(totalFcp.duration)}`;\n }\n if (lynxFcp?.duration !== undefined) {\n return `lynxFcp: ${formatDuration(lynxFcp.duration)}`;\n }\n if (fcp?.duration !== undefined) {\n return `fcp: ${formatDuration(fcp.duration)}`;\n }\n return \"\";\n};\n\nfunction getEntryTypeColors(colors: ThemeColors, entryType: string) {\n switch (entryType) {\n case \"init\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"metric\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"pipeline\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"resource\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nexport const PerformancePanel = ({\n performances,\n clearPerformances,\n}: PerformancePanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n if (performances.length === 0) {\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n 0 entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n <view className={\"pp-placeholder\"}>\n <text\n className={\"pp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No performance data yet...\n </text>\n </view>\n </view>\n );\n }\n\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {performances.length} entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n <list scroll-orientation=\"vertical\" className={\"pp-list\"}>\n {performances.map((perf) => {\n const isMetricFcp = isMetricFcpEntry(perf);\n const fcpMetrics = extractFcpMetrics(perf);\n const primaryFcp = getPrimaryFcpLabel(perf);\n const { totalFcp, lynxFcp, fcp } = fcpMetrics ?? {};\n\n return (\n <list-item key={perf.id} item-key={perf.id}>\n <view\n className={\"pp-item\"}\n style={{ borderBottomColor: colors.stroke.neutralWeak }}\n >\n <view\n className={\"pp-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n <text\n className={\"pp-entryType t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getEntryTypeColors(colors, perf.entryType),\n }}\n >\n {perf.entryType}\n </text>\n <text\n className={\"pp-entryName t2\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {perf.name}\n </text>\n <text\n className={\"pp-timestamp t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(perf.timestamp).toISOString()}\n </text>\n </view>\n\n <view\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n {isMetricFcp && primaryFcp && (\n <text\n className={\"pp-fcpHighlight t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n }}\n >\n {primaryFcp}\n </text>\n )}\n </view>\n\n {selectedId === perf.id && (\n <view className={\"pp-detailsContainer\"}>\n {isMetricFcp && fcpMetrics && (\n <view className={\"pp-fcpSection\"}>\n {totalFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 전체 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(totalFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n PrepareTemplate Start부터 Paint End 까지 걸리는\n 시간\n </text>\n </view>\n )}\n\n {lynxFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n LynxFCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(lynxFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Bundle Load 시작부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n\n {fcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 렌더링 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(fcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n TemplateBundle 준비부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n </view>\n )}\n\n {!!perf.rawEntry && (\n <view\n className={\"pp-rawEntrySection\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-detailTitle t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n Raw Entry\n </text>\n <text\n className={\"pp-rawEntry t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {String(\n stringify(perf.rawEntry, null, 2, {\n references: true,\n }),\n )}\n </text>\n </view>\n )}\n </view>\n )}\n </view>\n </list-item>\n );\n })}\n </list>\n </view>\n );\n};\n","import { type ReactNode, useRef, useState } from \"@lynx-js/react\";\nimport type { ListSnapEvent, NodesRef } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./Tabs.css\";\n\ntype TabsProps = {\n items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }>;\n onTabChange?: () => void;\n};\n\nexport default function Tabs(props: TabsProps) {\n const colors = useThemeColors();\n const tabContentsRef = useRef<NodesRef>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const tabSize =\n props.items.length < 4\n ? undefined\n : `t${Math.max(1, 5 - (props.items.length - 3))}`;\n\n return (\n <view className={\"tabs-root\"}>\n <view\n className={\"tabs-header\"}\n style={{\n boxShadow: `inset 0 -1px 0 0 ${colors.stroke.neutralSubtle}`,\n }}\n >\n {props.items.map((item, i) => (\n <view\n key={item.key}\n className={\"tabs-triggerButton\"}\n bindtap={() => {\n setActiveIndex(i);\n props.onTabChange?.();\n\n tabContentsRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: {\n position: i,\n smooth: true,\n },\n })\n .exec();\n }}\n >\n <text\n className={`tabs-triggerButtonText${tabSize ? ` ${tabSize}` : \"\"}`}\n style={{\n fontWeight: fontWeight.bold,\n color:\n i === activeIndex\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n {item.label}\n </text>\n {i === 0 && (\n <view\n className={\"tabs-triggerIndicator\"}\n style={{ transform: `translateX(${activeIndex * 100}%)` }}\n >\n <view\n className={\"tabs-triggerIndicatorLine\"}\n style={{ backgroundColor: colors.fg.neutral }}\n />\n </view>\n )}\n </view>\n ))}\n </view>\n\n <list\n ref={tabContentsRef}\n className={\"tabs-contents\"}\n scroll-orientation=\"horizontal\"\n item-snap={{ factor: 0, offset: 0 }}\n bindscroll={() => props.onTabChange?.()}\n bindsnap={(e: ListSnapEvent) => {\n setActiveIndex(e.detail.position);\n }}\n bounces={false}\n preload-buffer-count={props.items.length}\n >\n {props.items.map((item) => (\n <list-item\n key={item.key}\n item-key={item.key}\n recyclable={false}\n className={\"tabs-content\"}\n >\n {item.renderContent()}\n </list-item>\n ))}\n </list>\n </view>\n );\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useConsole, useNetwork, usePerformance } from \"../hooks\";\nimport type { CustomTab } from \"../types\";\nimport \"./ConsolePanel.css\";\nimport { dismissFilterDropdown, LogPanel } from \"./LogPanel\";\nimport { NetworkPanel } from \"./NetworkPanel\";\nimport { PerformancePanel } from \"./PerformancePanel\";\nimport Tabs from \"./Tabs\";\n\ninterface ConsolePanelProps {\n customTabs?: CustomTab[];\n}\n\nexport const ConsolePanel = ({ customTabs }: ConsolePanelProps) => {\n const { logs, clearLogs } = useConsole();\n const { networks, clearNetworks } = useNetwork();\n const { performances, clearPerformances } = usePerformance();\n\n const state = globalThis.__LYNX_CONSOLE__?.state;\n\n const items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }> = [];\n\n if (state?.logs) {\n items.push({\n key: \"log\",\n label: \"Log\",\n renderContent: () => <LogPanel logs={logs} clearLogs={clearLogs} />,\n });\n }\n\n if (state?.networks) {\n items.push({\n key: \"network\",\n label: \"Network\",\n renderContent: () => (\n <NetworkPanel networks={networks} clearNetworks={clearNetworks} />\n ),\n });\n }\n\n if (state?.performances) {\n items.push({\n key: \"performance\",\n label: \"Perf\",\n renderContent: () => (\n <PerformancePanel\n performances={performances}\n clearPerformances={clearPerformances}\n />\n ),\n });\n }\n\n if (customTabs) {\n for (const tab of customTabs) {\n items.push({\n key: tab.key,\n label: tab.label,\n renderContent: tab.renderContent,\n });\n }\n }\n\n if (items.length === 0) {\n return null;\n }\n\n return (\n <view className=\"cp-container\">\n <Tabs onTabChange={dismissFilterDropdown} items={items} />\n </view>\n );\n};\n","import { useRef, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\n\nconst LONG_PRESS_DURATION = 400;\nconst MOVE_THRESHOLD = 5;\n\nconst DEFAULT_RIGHT = 16;\nconst DEFAULT_BOTTOM = 84;\n\nlet savedRight = DEFAULT_RIGHT;\nlet savedBottom = DEFAULT_BOTTOM;\n\nexport function useLongPressDrag(onTap: () => void) {\n const [right, setRight] = useState(savedRight);\n const [bottom, setBottom] = useState(savedBottom);\n const [phase, setPhase] = useState<\"idle\" | \"dragging\" | \"releasing\">(\"idle\");\n const [tempRight, setTempRight] = useState(savedRight);\n const [tempBottom, setTempBottom] = useState(savedBottom);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const draggingRef = useRef(false);\n const startRef = useRef({ x: 0, y: 0, r: 0, b: 0 });\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n startRef.current = {\n x: e.detail.x,\n y: e.detail.y,\n r: right,\n b: bottom,\n };\n draggingRef.current = false;\n\n timerRef.current = setTimeout(() => {\n draggingRef.current = true;\n setPhase(\"dragging\");\n setTempRight(right);\n setTempBottom(bottom);\n }, LONG_PRESS_DURATION);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n const dx = e.detail.x - startRef.current.x;\n const dy = e.detail.y - startRef.current.y;\n\n if (\n !draggingRef.current &&\n (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)\n ) {\n clearTimer();\n }\n\n if (!draggingRef.current) return;\n\n // right/bottom 기준이므로 방향 반전\n setTempRight(startRef.current.r - dx);\n setTempBottom(startRef.current.b - dy);\n };\n\n const handleTouchEnd = () => {\n clearTimer();\n\n if (draggingRef.current) {\n setRight(tempRight);\n setBottom(tempBottom);\n savedRight = tempRight;\n savedBottom = tempBottom;\n setPhase(\"releasing\");\n draggingRef.current = false;\n setTimeout(() => setPhase(\"idle\"), 300);\n } else {\n onTap();\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return {\n phase,\n right: isDragging ? tempRight : right,\n bottom: isDragging ? tempBottom : bottom,\n clearTimer,\n handlers: {\n catchtouchstart: handleTouchStart,\n catchtouchmove: handleTouchMove,\n catchtouchend: handleTouchEnd,\n },\n };\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useLongPressDrag } from \"../hooks/useLongPressDrag\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration } from \"../styles/theme\";\nimport \"./FloatingButton.css\";\n\ninterface FloatingButtonProps {\n bindtap: () => void;\n children: ReactNode;\n}\n\nconst SHINE_STYLES = {\n idle: {\n transform: \"scale(0)\",\n opacity: 0,\n },\n dragging: {\n transform: \"scale(1)\",\n opacity: 1,\n transition: \"transform 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n releasing: {\n transform: \"scale(1)\",\n opacity: 0,\n transition: \"opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n} as const;\n\nexport const FloatingButton = ({ bindtap, children }: FloatingButtonProps) => {\n const colors = useThemeColors();\n const { phase, right, bottom, clearTimer, handlers } =\n useLongPressDrag(bindtap);\n\n const handleReload = () => {\n try {\n lynx.reload({}, () => {\n console.log(\"reloaded!\");\n });\n } catch (e) {\n console.error(\"[LynxConsole] reload failed:\", e);\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return (\n <view\n className={\"fb-wrapper\"}\n consume-slide-event={[[-180, 180]]}\n style={{\n right: `${right}px`,\n bottom: `${bottom}px`,\n transform: isDragging ? \"scale(1.05)\" : \"scale(1)\",\n transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n {...handlers}\n >\n <view\n className={\"fb-button\"}\n style={{ backgroundColor: colors.palette.green600 }}\n >\n {children}\n <view className={\"fb-shineOverlay\"} style={SHINE_STYLES[phase]} />\n </view>\n <view\n className={\"fb-reloadButton\"}\n style={{ backgroundColor: colors.palette.green600 }}\n catchtouchstart={() => clearTimer()}\n bindtap={handleReload}\n >\n <text\n className={\"fb-reloadIcon\"}\n style={{ color: colors.palette.staticWhite }}\n >\n {\"\\u21BB\"}\n </text>\n </view>\n </view>\n );\n};\n","import {\n type ForwardedRef,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useState,\n} from \"@lynx-js/react\";\nimport BottomSheet from \"./components/BottomSheet.jsx\";\nimport { ConsolePanel } from \"./components/ConsolePanel.jsx\";\nimport \"./components/FloatingButton.css\";\nimport \"./styles/tokens.css\";\nimport { FloatingButton } from \"./components/FloatingButton.jsx\";\nimport { usePerformance } from \"./hooks/usePerformance\";\nimport { ThemeProvider } from \"./styles/ThemeContext\";\nimport { getColors } from \"./styles/theme\";\nimport type { CustomTab } from \"./types\";\n\nexport interface LynxConsoleHandle {\n open: () => void;\n close: () => void;\n isOpen: () => boolean;\n}\n\nexport interface LynxConsoleProps {\n theme?: \"light\" | \"dark\";\n safeAreaInsetBottom?: string;\n customTabs?: CustomTab[];\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(\n (\n { theme = \"light\", safeAreaInsetBottom = \"50px\", customTabs },\n ref: ForwardedRef<LynxConsoleHandle>,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [shouldClose, setShouldClose] = useState(false);\n const { performances } = usePerformance();\n const colors = useMemo(() => getColors(theme), [theme]);\n\n const latestFcp = useMemo(() => {\n for (let i = performances.length - 1; i >= 0; i--) {\n const perf = performances[i];\n if (perf && perf.entryType === \"metric\" && perf.name === \"fcp\") {\n const metricEntry = perf.rawEntry as MetricFcpEntry | undefined;\n // totalFcp를 먼저 시도하고, 없으면 lynxFcp 반환\n if (metricEntry?.totalFcp?.duration !== undefined) {\n return metricEntry.totalFcp;\n }\n if (metricEntry?.lynxFcp?.duration !== undefined) {\n return metricEntry.lynxFcp;\n }\n }\n }\n return undefined;\n }, [performances]);\n\n useImperativeHandle(ref, () => ({\n open: () => {\n setIsOpen(true);\n setShouldClose(false);\n },\n close: () => {\n setShouldClose(true);\n },\n isOpen: () => isOpen,\n }));\n\n const handleOpenBottomSheet = () => {\n setIsOpen(true);\n setShouldClose(false);\n };\n\n const handleCloseBottomSheet = () => {\n setIsOpen(false);\n setShouldClose(false);\n };\n\n return (\n <ThemeProvider value={colors}>\n <view\n style={{\n backgroundColor: colors.bg.layerDefault,\n color: colors.fg.neutral,\n }}\n >\n <FloatingButton bindtap={handleOpenBottomSheet}>\n <text\n className=\"fb-title t4\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n LynxConsole\n </text>\n <text\n className=\"fb-subtitle t3\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n {`${latestFcp?.name ?? \"FCP\"}: ${latestFcp?.duration ? latestFcp.duration.toFixed(2) : \"--\"}ms`}\n </text>\n </FloatingButton>\n {isOpen && (\n <BottomSheet\n isOpen={isOpen}\n shouldClose={shouldClose}\n onClose={handleCloseBottomSheet}\n title=\"Lynx Console\"\n safeAreaInsetBottom={safeAreaInsetBottom}\n >\n <ConsolePanel customTabs={customTabs} />\n </BottomSheet>\n )}\n </view>\n </ThemeProvider>\n );\n },\n);\n\nexport type { CustomTab } from \"./types\";\nexport default LynxConsole;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/styles/theme.ts","../src/styles/ThemeContext.ts","../src/components/BottomSheet.tsx","../src/hooks/useConsole.ts","../src/hooks/useNetwork.ts","../src/hooks/usePerformance.ts","../src/components/LogPanel.tsx","../src/components/NetworkDetailSection.tsx","../src/components/NetworkPanel.tsx","../src/components/PerformancePanel.tsx","../src/components/Tabs.tsx","../src/components/ConsolePanel.tsx","../src/hooks/useLongPressDrag.ts","../src/components/FloatingButton.tsx","../src/index.tsx"],"sourcesContent":["export type Theme = \"light\" | \"dark\";\n\nconst lightColors = {\n palette: {\n blue100: \"#eff6ff\",\n blue600: \"#5e98fe\",\n gray100: \"#f7f8f9\",\n gray400: \"#dcdee3\",\n green100: \"#edfaf6\",\n green600: \"#10ab7d\",\n purple100: \"#f5f3fe\",\n purple600: \"#9f84fb\",\n red100: \"#fdf0f0\",\n red600: \"#fc6a66\",\n red900: \"#921708\",\n staticWhite: \"#ffffff\",\n yellow100: \"#fff7de\",\n yellow600: \"#c49725\",\n yellow900: \"#4f3e1f\",\n },\n fg: {\n neutral: \"#1a1c20\",\n placeholder: \"#b0b3ba\",\n disabled: \"#d1d3d8\",\n neutralMuted: \"#555d6d\",\n neutralSubtle: \"#868b94\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#ffffff\",\n layerFloating: \"#ffffff\",\n neutralWeak: \"#f3f4f5\",\n },\n stroke: {\n neutralSubtle: \"#0000000c\",\n neutralWeak: \"#dcdee3\",\n },\n} as const;\n\nconst darkColors = {\n palette: {\n blue100: \"#202742\",\n blue600: \"#1e82eb\",\n gray100: \"#16171b\",\n gray400: \"#393d46\",\n green100: \"#202926\",\n green600: \"#1b946d\",\n purple100: \"#28213b\",\n purple600: \"#8e6bee\",\n red100: \"#322323\",\n red600: \"#f73526\",\n red900: \"#f8c5c3\",\n staticWhite: \"#ffffff\",\n yellow100: \"#302819\",\n yellow600: \"#b6720d\",\n yellow900: \"#e5d49b\",\n },\n fg: {\n neutral: \"#f3f4f5\",\n placeholder: \"#868b94\",\n disabled: \"#5b606a\",\n neutralMuted: \"#dcdee3\",\n neutralSubtle: \"#b0b3ba\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#16171b\",\n layerFloating: \"#1d2025\",\n neutralWeak: \"#2b2e35\",\n },\n stroke: {\n neutralSubtle: \"#ffffff0d\",\n neutralWeak: \"#393d46\",\n },\n} as const;\n\nconst colorMap = {\n light: lightColors,\n dark: darkColors,\n} as const;\n\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n bold: \"700\",\n} as const;\n\nexport const duration = {\n d4: \"200ms\",\n d6: \"300ms\",\n} as const;\n\nexport function getColors(theme: Theme) {\n return colorMap[theme];\n}\n\nexport type ThemeColors = ReturnType<typeof getColors>;\n","import { createContext, useContext } from \"@lynx-js/react\";\nimport { getColors, type ThemeColors } from \"./theme\";\n\nconst ThemeContext = createContext<ThemeColors>(getColors(\"light\"));\n\nexport const ThemeProvider = ThemeContext.Provider;\n\nexport function useThemeColors(): ThemeColors {\n return useContext(ThemeContext);\n}\n","import { type ReactNode, useEffect, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration, fontWeight } from \"../styles/theme\";\nimport \"./BottomSheet.css\";\n\ninterface BottomSheetProps {\n children: ReactNode;\n title?: string;\n footer?: ReactNode;\n onClose: () => void;\n isOpen: boolean;\n shouldClose?: boolean;\n safeAreaInsetBottom?: string;\n}\n\nconst MIN_HEIGHT = 200;\nconst MAX_HEIGHT = 700;\nconst DEFAULT_HEIGHT = 500;\nconst CLOSE_DRAG_THRESHOLD = 30; // 30px 이상 아래로 드래그하면 닫힘\n\n// 마지막 높이 저장\nlet savedHeight: number | null = null;\n\nexport default function BottomSheet({\n children,\n title,\n footer,\n onClose,\n isOpen,\n shouldClose = false,\n safeAreaInsetBottom = \"25px\",\n}: BottomSheetProps) {\n const colors = useThemeColors();\n const [sheetHeight, setSheetHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [tempHeight, setTempHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [isDragging, setIsDragging] = useState(false);\n const [dragStartY, setDragStartY] = useState(0);\n const [dragStartHeight, setDragStartHeight] = useState(\n savedHeight ?? DEFAULT_HEIGHT,\n );\n const [isOpening, setIsOpening] = useState(true);\n const [isClosing, setIsClosing] = useState(false);\n\n // 닫기 애니메이션 처리\n const handleClose = () => {\n setIsClosing(true);\n setTimeout(() => {\n onClose();\n }, 300);\n };\n\n // 아래에서 올라오는 애니메이션\n useEffect(() => {\n requestAnimationFrame(() => {\n setIsOpening(false);\n });\n }, []);\n\n // 외부에서 닫기 요청 시 애니메이션 처리\n useEffect(() => {\n if (shouldClose && !isClosing) {\n handleClose();\n }\n }, [shouldClose, isClosing]);\n\n // 높이 변경 시 저장\n useEffect(() => {\n savedHeight = sheetHeight;\n }, [sheetHeight]);\n\n if (!isOpen) return null;\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n setIsDragging(true);\n setDragStartY(e.detail.y);\n setDragStartHeight(sheetHeight);\n setTempHeight(sheetHeight);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n if (!isDragging) return;\n const deltaY = dragStartY - e.detail.y;\n const newHeight = Math.min(\n Math.max(dragStartHeight + deltaY, MIN_HEIGHT),\n MAX_HEIGHT,\n );\n setTempHeight(newHeight);\n };\n\n const handleTouchEnd = () => {\n setIsDragging(false);\n\n // 아래로 일정 30px 이상 드래그하면 닫기\n const dragDistance = dragStartHeight - tempHeight;\n setSheetHeight(tempHeight);\n if (dragDistance > CLOSE_DRAG_THRESHOLD) {\n handleClose();\n }\n };\n\n return (\n <scroll-view\n className=\"bs-backdrop\"\n style={{\n background: colors.bg.overlay,\n opacity: isOpening || isClosing ? 0 : 1,\n transition: `opacity ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n <view className=\"bs-overlay\" bindtap={handleClose}>\n <view\n className=\"bs-content\"\n catchtap={() => {}}\n style={{\n background: colors.bg.layerFloating,\n height: `${isDragging ? tempHeight : sheetHeight}px`,\n transform:\n isOpening || isClosing ? \"translateY(100%)\" : \"translateY(0)\",\n transition: isDragging\n ? \"none\"\n : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n {/* catchtap: 이벤트 버블링 차단 */}\n <view\n className=\"bs-handleContainer\"\n bindtouchstart={handleTouchStart}\n bindtouchmove={handleTouchMove}\n bindtouchend={handleTouchEnd}\n >\n <view\n className=\"bs-handle\"\n style={{ backgroundColor: colors.palette.gray400 }}\n />\n </view>\n <view className=\"bs-header\">\n {title && (\n <text\n className=\"bs-title t7\"\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n {title}\n </text>\n )}\n </view>\n <view\n className=\"bs-body\"\n style={{\n paddingBottom: safeAreaInsetBottom,\n }}\n >\n {children}\n </view>\n {footer && <view className=\"bs-footer\">{footer}</view>}\n </view>\n </view>\n </scroll-view>\n );\n}\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { LogEntry } from \"../types\";\n\nexport const useConsole = () => {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs === \"undefined\") {\n console.warn(\"[LynxConsole] Log monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setLogs([...(state.logs ?? [])]);\n\n const updateLogs = (_entry: LogEntry) => {\n setLogs([...(state.logs ?? [])]);\n };\n\n const unsubscribe = state.logSubscribe?.(updateLogs);\n\n return unsubscribe;\n }, []);\n\n const clearLogs = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.logs = [];\n setLogs([]);\n }\n };\n\n return { logs, clearLogs };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { NetworkEntry } from \"../types\";\n\nexport const useNetwork = () => {\n const [networks, setNetworks] = useState<NetworkEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks === \"undefined\") {\n console.warn(\"[LynxConsole] Network monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setNetworks([...(state.networks ?? [])]);\n\n const updateNetworks = (_entry: NetworkEntry) => {\n setNetworks([...(state.networks ?? [])]);\n };\n\n const unsubscribe = state.subscribeNetwork?.(updateNetworks);\n\n return unsubscribe;\n }, []);\n\n const clearNetworks = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.networks = [];\n state.networksMap?.clear();\n setNetworks([]);\n }\n };\n\n return { networks, clearNetworks };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { PerformanceEntryData } from \"../types\";\n\nexport const usePerformance = () => {\n const [performances, setPerformances] = useState<PerformanceEntryData[]>([]);\n\n useEffect(() => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances === \"undefined\"\n ) {\n console.warn(\"[LynxConsole] Performance monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setPerformances([...(state.performances ?? [])]);\n\n const updatePerformances = (_entry: PerformanceEntryData) => {\n setPerformances([...(state.performances ?? [])]);\n };\n\n const unsubscribe = state.subscribePerformance?.(updatePerformances);\n\n return unsubscribe;\n }, []);\n\n const clearPerformances = () => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances !== \"undefined\"\n ) {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.performances = [];\n setPerformances([]);\n }\n };\n\n return { performances, clearPerformances };\n};\n","import { useEffect, useMemo, useRef, useState } from \"@lynx-js/react\";\nimport type { BaseEvent, InputInputEvent, NodesRef } from \"@lynx-js/types\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { LogEntry, LogLevel } from \"../types\";\nimport \"./ConsolePanel.css\";\n\nconst LOG_LEVELS: LogLevel[] = [\"log\", \"info\", \"warn\", \"error\"];\n\nlet savedEnabledLevels: Set<LogLevel> | null = null;\nlet savedSearchQuery = \"\";\nlet closeFilterDropdown: (() => void) | null = null;\n\nexport const dismissFilterDropdown = () => closeFilterDropdown?.();\n\ninterface LogPanelProps {\n logs: LogEntry[];\n clearLogs: () => void;\n}\n\nconst runCode = (code: string) => {\n try {\n // biome-ignore lint: intentional REPL tool\n const result = eval(code);\n if (result instanceof Promise) {\n result.then((r) => console.log(r)).catch((e) => console.error(e));\n } else {\n console.log(result);\n }\n } catch (e) {\n console.error(e);\n }\n};\n\nfunction getLevelColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"log\":\n return colors.palette.green600;\n case \"info\":\n return colors.palette.blue600;\n case \"warn\":\n return colors.palette.yellow600;\n case \"error\":\n return colors.palette.red600;\n }\n}\n\nfunction getLogItemBg(\n colors: ThemeColors,\n level: LogLevel,\n): string | undefined {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nfunction getStringColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.fg.neutral;\n }\n}\n\nfunction getPrimitiveColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.palette.blue600;\n }\n}\n\nexport const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {\n const colors = useThemeColors();\n const [expandedArgs, setExpandedArgs] = useState(new Set());\n const [code, setCode] = useState(\"\");\n const [enabledLevels, setEnabledLevels] = useState<Set<LogLevel>>(\n () => savedEnabledLevels ?? new Set(LOG_LEVELS),\n );\n const [filterOpen, setFilterOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(savedSearchQuery);\n const inputRef = useRef<NodesRef>(null);\n const searchInputRef = useRef<NodesRef>(null);\n const listRef = useRef<NodesRef>(null);\n\n useEffect(() => {\n savedEnabledLevels = enabledLevels;\n }, [enabledLevels]);\n\n useEffect(() => {\n savedSearchQuery = searchQuery;\n }, [searchQuery]);\n\n useEffect(() => {\n if (savedSearchQuery) {\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: savedSearchQuery } })\n .exec();\n }\n }, []);\n\n useEffect(() => {\n closeFilterDropdown = () => setFilterOpen(false);\n return () => {\n closeFilterDropdown = null;\n };\n }, []);\n\n const filteredLogs = useMemo(\n () =>\n logs.filter((log) => {\n if (!enabledLevels.has(log.level)) return false;\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return log.args.some((arg) =>\n String(arg).toLowerCase().includes(query),\n );\n }\n return true;\n }),\n [logs, enabledLevels, searchQuery],\n );\n const logsRef = useRef(filteredLogs);\n logsRef.current = filteredLogs;\n\n const toggleLevel = (level: LogLevel) => {\n setEnabledLevels((prev) => {\n const next = new Set(prev);\n if (next.has(level)) {\n next.delete(level);\n } else {\n next.add(level);\n }\n return next;\n });\n };\n\n const scrollToBottom = (smooth: boolean) => {\n if (logsRef.current.length === 0) return;\n listRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: { position: logsRef.current.length - 1, smooth },\n })\n .exec();\n };\n\n useEffect(() => {\n scrollToBottom(true);\n }, [filteredLogs]);\n\n const toggleArg = (key: string) => {\n setExpandedArgs((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n };\n\n const handleRun = () => {\n const trimmed = code.trim();\n if (!trimmed) return;\n\n setCode(\"\");\n inputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n runCode(trimmed);\n };\n\n const renderArg = (\n arg: unknown,\n parentKey: string,\n level: \"log\" | \"info\" | \"warn\" | \"error\",\n ): React.ReactNode => {\n const key = parentKey;\n const isExpanded = expandedArgs.has(key);\n\n if (arg === null) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n null\n </text>\n );\n }\n\n if (arg === undefined) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n undefined\n </text>\n );\n }\n\n if (typeof arg === \"string\") {\n const MAX_LENGTH = 80;\n const shouldTruncate = arg.length > MAX_LENGTH;\n const strColor = getStringColor(colors, level);\n\n if (!shouldTruncate) {\n return (\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {arg}\n </text>\n );\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}\n </text>\n </view>\n </view>\n );\n }\n\n if (typeof arg === \"number\" || typeof arg === \"boolean\") {\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n }\n\n if (typeof arg === \"object\") {\n let preview = \"Object\";\n if (Array.isArray(arg)) {\n preview = `Array(${arg.length})`;\n } else if (arg instanceof Map) {\n preview = `Map(${arg.size})`;\n } else if (arg instanceof Set) {\n preview = `Set(${arg.size})`;\n } else if (arg instanceof Date) {\n preview = `Date`;\n } else if (arg instanceof RegExp) {\n preview = `RegExp`;\n } else if (arg instanceof Error) {\n preview = `${arg.constructor.name}`;\n } else if (arg?.constructor?.name && arg.constructor.name !== \"Object\") {\n preview = arg.constructor.name;\n }\n\n let jsonString: string;\n if (arg instanceof Map) {\n const entries = Array.from(arg.entries()).map(\n ([k, v]) => ` [${stringify(k)}, ${stringify(v)}]`,\n );\n jsonString = `{\\n${entries.join(\",\\n\")}\\n}`;\n } else if (arg instanceof Set) {\n const values = Array.from(arg.values()).map((v) => stringify(v));\n jsonString = `{\\n${values.join(\", \")}\\n}`;\n } else {\n jsonString =\n stringify(arg, null, 2, { references: true }) ?? String(arg);\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argObjectPreview t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {preview}\n </text>\n </view>\n {isExpanded && (\n <view className={\"cp-argObjectContent\"}>\n <text\n className={\"cp-argObjectJson t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {jsonString}\n </text>\n </view>\n )}\n </view>\n );\n }\n\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n };\n\n return (\n <view\n className={\"cp-logContainer\"}\n bindtap={() => {\n if (filterOpen) setFilterOpen(false);\n }}\n >\n <view className={\"cp-logHeader\"}>\n <view className={\"cp-filterWrapper\"}>\n <view\n className={\"cp-filterButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n catchtap={() => setFilterOpen((v) => !v)}\n >\n <text\n className={\"cp-filterButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n Filter ▼\n </text>\n </view>\n {filterOpen && (\n <view\n className={\"cp-filterDropdown\"}\n style={{\n backgroundColor: colors.bg.layerFloating,\n borderColor: colors.stroke.neutralSubtle,\n }}\n catchtap={() => {}}\n >\n {LOG_LEVELS.map((level) => (\n <view\n key={level}\n className={\"cp-filterOption\"}\n bindtap={() => toggleLevel(level)}\n >\n <text\n className={\"cp-filterCheckbox t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {enabledLevels.has(level) ? \"✅\" : \"⬜\"}\n </text>\n <text\n className={\"cp-filterLabel t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {level.toUpperCase()}\n </text>\n </view>\n ))}\n </view>\n )}\n </view>\n <view\n className={\"cp-searchWrapper\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"cp-searchPrompt t6\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={searchInputRef}\n className={\"cp-searchInput t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"Search logs...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setSearchQuery(e.detail.value)\n }\n />\n {searchQuery.length > 0 && (\n <view\n className={\"cp-searchClear\"}\n bindtap={() => {\n setSearchQuery(\"\");\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n }}\n >\n <text\n className={\"cp-searchClearText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n ✕\n </text>\n </view>\n )}\n </view>\n <view style={{ display: \"flex\", flexDirection: \"row\", gap: 8 }}>\n <view\n className={\"cp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearLogs}\n >\n <text\n className={\"cp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n </view>\n <list\n ref={listRef}\n scroll-orientation=\"vertical\"\n className={\"cp-logList\"}\n preload-buffer-count={10}\n initial-scroll-index={Math.max(0, filteredLogs.length - 1)}\n >\n {filteredLogs.length === 0 ? (\n <list-item item-key=\"empty-state\">\n <view className={\"cp-placeholder\"}>\n <text\n className={\"cp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No logs yet. Try console.log(\"Hello!\")\n </text>\n </view>\n </list-item>\n ) : (\n filteredLogs.map((log) => {\n return (\n <list-item key={log.id} item-key={log.id}>\n <view\n className={\"cp-logItem\"}\n style={{\n backgroundColor: getLogItemBg(colors, log.level),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view className={\"cp-logItemHeader\"}>\n <text\n className={\"cp-logLevel t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getLevelColor(colors, log.level),\n }}\n >\n {log.level.toUpperCase()}\n </text>\n <text\n className={\"cp-logTime t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(log.timestamp).toISOString()}\n </text>\n </view>\n <view className={\"cp-logArgsContainer\"}>\n {log.args.map((arg, index) => (\n <view\n key={`${log.id}-${index.toString()}`}\n className={\"cp-logArgItem\"}\n style={{ fontWeight: fontWeight.regular }}\n >\n {renderArg(\n arg,\n `${log.id}-${index.toString()}`,\n log.level,\n )}\n </view>\n ))}\n </view>\n </view>\n </list-item>\n );\n })\n )}\n </list>\n <view className={\"cp-replInputRow\"}>\n <text\n className={\"cp-replPrompt t10\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={inputRef}\n className={\"cp-replInput t5\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"enter code...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setCode(e.detail.value)\n }\n bindconfirm={handleRun}\n />\n <view\n className={\"cp-replRunButton\"}\n style={{ backgroundColor: colors.palette.green100 }}\n bindtap={handleRun}\n >\n <text\n className={\"cp-replRunButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.palette.green600,\n }}\n >\n Run\n </text>\n </view>\n </view>\n </view>\n );\n};\n","import { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkDetailSectionProps {\n headers?: Record<string, string> | undefined;\n body?: string | undefined;\n error?: string | undefined;\n}\n\nexport const NetworkDetailSection = ({\n headers = {},\n body = \"\",\n error = \"\",\n}: NetworkDetailSectionProps) => {\n const colors = useThemeColors();\n\n return (\n <>\n {/* Headers */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Headers\n </text>\n {headers && Object.keys(headers).length > 0 ? (\n <view className={\"np-table\"}>\n {Object.entries(headers).map(([key, value]) => (\n <view\n key={key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {value}\n </text>\n </view>\n ))}\n </view>\n ) : (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No headers\n </text>\n )}\n </view>\n\n {/* Body */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Body\n </text>\n {error && (\n <text\n className={\"np-errorText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n }}\n >\n {error}\n </text>\n )}\n {body && (\n <text\n className={\"np-bodyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n }}\n >\n {body}\n </text>\n )}\n {!error && !body && (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No body\n </text>\n )}\n </view>\n </>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { NetworkEntry } from \"../types\";\nimport { NetworkDetailSection } from \"./NetworkDetailSection\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkPanelProps {\n networks: NetworkEntry[];\n clearNetworks: () => void;\n}\n\ntype TabType = \"general\" | \"request\" | \"response\";\n\nfunction getMethodColors(colors: ThemeColors, method: string) {\n switch (method) {\n case \"GET\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"POST\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"PUT\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n case \"PATCH\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"DELETE\":\n return {\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nfunction getStatusCodeColor(\n colors: ThemeColors,\n variant: \"success\" | \"error\" | \"pending\",\n): string {\n switch (variant) {\n case \"success\":\n return colors.palette.green600;\n case \"error\":\n return colors.palette.red600;\n case \"pending\":\n return colors.fg.neutralSubtle;\n }\n}\n\nfunction getItemBg(colors: ThemeColors, status: string): string | undefined {\n switch (status) {\n case \"pending\":\n return colors.palette.gray100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nexport const NetworkPanel = ({\n networks,\n clearNetworks,\n}: NetworkPanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabType>(\"general\");\n const formatDuration = (duration?: number): string => {\n if (!duration) return \"-\";\n if (duration < 1000) return `${duration}ms`;\n return `${(duration / 1000).toFixed(2)}s`;\n };\n\n const extractPath = (url: string): string => {\n const pathMatch = url.match(/^https?:\\/\\/[^/]+(.*)$/);\n if (pathMatch?.[1]) {\n return pathMatch[1].startsWith(\"/\")\n ? pathMatch[1].slice(1)\n : pathMatch[1];\n }\n return url;\n };\n\n const getGeneralInfo = (network: NetworkEntry) => {\n return [\n { key: \"URL\", value: network.url },\n { key: \"Method\", value: network.method },\n network.statusCode\n ? { key: \"Status\", value: String(network.statusCode) }\n : null,\n {\n key: \"Request Time\",\n value: new Date(network.startTime).toISOString(),\n },\n network.endTime\n ? {\n key: \"Response Time\",\n value: new Date(network.endTime).toISOString(),\n }\n : null,\n network.duration\n ? { key: \"Duration\", value: formatDuration(network.duration) }\n : null,\n ].filter((item) => item !== null);\n };\n\n const getStatusCodeVariant = (\n status: string,\n statusCode?: number,\n ): \"success\" | \"error\" | \"pending\" => {\n if (status === \"pending\") return \"pending\";\n if (status === \"error\") return \"error\";\n if (statusCode && statusCode >= 200 && statusCode < 300) return \"success\";\n return \"error\";\n };\n\n return (\n <view className={\"np-container\"}>\n <view className={\"np-header\"}>\n <text\n className={\"np-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Total: {networks.length} requests\n </text>\n <view\n className={\"np-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearNetworks}\n >\n <text\n className={\"np-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n {networks.length === 0 ? (\n <view className={\"np-placeholder\"}>\n <text\n className={\"np-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No network requests yet\n </text>\n </view>\n ) : (\n <list scroll-orientation=\"vertical\" className={\"np-list\"}>\n {networks.map((network) => (\n <list-item key={network.id} item-key={network.id}>\n <view\n className={\"np-item\"}\n style={{\n backgroundColor: getItemBg(colors, network.status),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view\n className={\"np-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n <text\n className={\"np-method t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getMethodColors(colors, network.method),\n }}\n >\n {network.method}\n </text>\n {network.statusCode && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getStatusCodeColor(\n colors,\n getStatusCodeVariant(\n network.status,\n network.statusCode,\n ),\n ),\n }}\n >\n {network.statusCode}\n </text>\n )}\n {network.status === \"pending\" && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n Pending...\n </text>\n )}\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {formatDuration(network.duration)}\n </text>\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(network.startTime).toISOString()}\n </text>\n </view>\n\n <text\n className={\"np-path t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n {extractPath(network.url)}\n </text>\n\n {selectedId === network.id && (\n <view\n className={\"np-detailsContainer\"}\n style={{ borderTopColor: colors.stroke.neutralSubtle }}\n >\n {/* Tabs */}\n <view className={\"np-tabs\"}>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"general\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"general\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"general\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n General\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"request\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"request\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"request\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Request\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"response\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"response\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"response\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Response\n </text>\n </view>\n </view>\n\n {/* Tab Content */}\n <view className={\"np-tabContent\"}>\n {activeTab === \"general\" && (\n <view className={\"np-table\"}>\n {getGeneralInfo(network).map((item) => (\n <view\n key={item.key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {item.key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {item.value}\n </text>\n </view>\n ))}\n </view>\n )}\n\n {activeTab === \"request\" && (\n <NetworkDetailSection\n headers={network.requestHeaders}\n body={network.requestBody}\n />\n )}\n\n {activeTab === \"response\" && (\n <NetworkDetailSection\n headers={network.responseHeaders}\n body={network.responseBody}\n error={network.error}\n />\n )}\n </view>\n </view>\n )}\n </view>\n </list-item>\n ))}\n </list>\n )}\n </view>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { PerformanceEntryData } from \"../types\";\nimport \"./PerformancePanel.css\";\n\ninterface PerformancePanelProps {\n performances: PerformanceEntryData[];\n clearPerformances: () => void;\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst isMetricFcpEntry = (entry: PerformanceEntryData): boolean => {\n return entry.entryType === \"metric\" && entry.name === \"fcp\";\n};\n\nconst extractFcpMetrics = (entry: PerformanceEntryData) => {\n if (!isMetricFcpEntry(entry) || !entry.rawEntry) {\n return null;\n }\n\n const metricEntry = entry.rawEntry as MetricFcpEntry;\n\n return {\n totalFcp: metricEntry.totalFcp ?? undefined,\n lynxFcp: metricEntry.lynxFcp ?? undefined,\n fcp: metricEntry.fcp ?? undefined,\n };\n};\n\nconst formatDuration = (ms?: number): string => {\n if (ms === undefined) return \"-\";\n return `${ms.toFixed(2)}ms`;\n};\n\nconst getPrimaryFcpLabel = (entry: PerformanceEntryData): string => {\n const fcpMetrics = extractFcpMetrics(entry);\n if (!fcpMetrics) return \"\";\n\n const { totalFcp, lynxFcp, fcp } = fcpMetrics;\n\n if (totalFcp?.duration !== undefined) {\n return `totalFcp: ${formatDuration(totalFcp.duration)}`;\n }\n if (lynxFcp?.duration !== undefined) {\n return `lynxFcp: ${formatDuration(lynxFcp.duration)}`;\n }\n if (fcp?.duration !== undefined) {\n return `fcp: ${formatDuration(fcp.duration)}`;\n }\n return \"\";\n};\n\nfunction getEntryTypeColors(colors: ThemeColors, entryType: string) {\n switch (entryType) {\n case \"init\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"metric\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"pipeline\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"resource\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nexport const PerformancePanel = ({\n performances,\n clearPerformances,\n}: PerformancePanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n if (performances.length === 0) {\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n 0 entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n <view className={\"pp-placeholder\"}>\n <text\n className={\"pp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No performance data yet...\n </text>\n </view>\n </view>\n );\n }\n\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {performances.length} entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n <list scroll-orientation=\"vertical\" className={\"pp-list\"}>\n {performances.map((perf) => {\n const isMetricFcp = isMetricFcpEntry(perf);\n const fcpMetrics = extractFcpMetrics(perf);\n const primaryFcp = getPrimaryFcpLabel(perf);\n const { totalFcp, lynxFcp, fcp } = fcpMetrics ?? {};\n\n return (\n <list-item key={perf.id} item-key={perf.id}>\n <view\n className={\"pp-item\"}\n style={{ borderBottomColor: colors.stroke.neutralWeak }}\n >\n <view\n className={\"pp-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n <text\n className={\"pp-entryType t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getEntryTypeColors(colors, perf.entryType),\n }}\n >\n {perf.entryType}\n </text>\n <text\n className={\"pp-entryName t2\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {perf.name}\n </text>\n <text\n className={\"pp-timestamp t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(perf.timestamp).toISOString()}\n </text>\n </view>\n\n <view\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n {isMetricFcp && primaryFcp && (\n <text\n className={\"pp-fcpHighlight t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n }}\n >\n {primaryFcp}\n </text>\n )}\n </view>\n\n {selectedId === perf.id && (\n <view className={\"pp-detailsContainer\"}>\n {isMetricFcp && fcpMetrics && (\n <view className={\"pp-fcpSection\"}>\n {totalFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 전체 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(totalFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n PrepareTemplate Start부터 Paint End 까지 걸리는\n 시간\n </text>\n </view>\n )}\n\n {lynxFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n LynxFCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(lynxFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Bundle Load 시작부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n\n {fcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 렌더링 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(fcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n TemplateBundle 준비부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n </view>\n )}\n\n {!!perf.rawEntry && (\n <view\n className={\"pp-rawEntrySection\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-detailTitle t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n Raw Entry\n </text>\n <text\n className={\"pp-rawEntry t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {String(\n stringify(perf.rawEntry, null, 2, {\n references: true,\n }),\n )}\n </text>\n </view>\n )}\n </view>\n )}\n </view>\n </list-item>\n );\n })}\n </list>\n </view>\n );\n};\n","import { type ReactNode, useRef, useState } from \"@lynx-js/react\";\nimport type { ListSnapEvent, NodesRef } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./Tabs.css\";\n\ntype TabsProps = {\n items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }>;\n onTabChange?: () => void;\n};\n\nexport default function Tabs(props: TabsProps) {\n const colors = useThemeColors();\n const tabContentsRef = useRef<NodesRef>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const tabSize =\n props.items.length < 4\n ? undefined\n : `t${Math.max(1, 5 - (props.items.length - 3))}`;\n\n return (\n <view className={\"tabs-root\"}>\n <view\n className={\"tabs-header\"}\n style={{\n boxShadow: `inset 0 -1px 0 0 ${colors.stroke.neutralSubtle}`,\n }}\n >\n {props.items.map((item, i) => (\n <view\n key={item.key}\n className={\"tabs-triggerButton\"}\n bindtap={() => {\n setActiveIndex(i);\n props.onTabChange?.();\n\n tabContentsRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: {\n position: i,\n smooth: true,\n },\n })\n .exec();\n }}\n >\n <text\n className={`tabs-triggerButtonText${tabSize ? ` ${tabSize}` : \"\"}`}\n style={{\n fontWeight: fontWeight.bold,\n color:\n i === activeIndex\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n {item.label}\n </text>\n {i === 0 && (\n <view\n className={\"tabs-triggerIndicator\"}\n style={{ transform: `translateX(${activeIndex * 100}%)` }}\n >\n <view\n className={\"tabs-triggerIndicatorLine\"}\n style={{ backgroundColor: colors.fg.neutral }}\n />\n </view>\n )}\n </view>\n ))}\n </view>\n\n <list\n ref={tabContentsRef}\n className={\"tabs-contents\"}\n scroll-orientation=\"horizontal\"\n item-snap={{ factor: 0, offset: 0 }}\n bindscroll={() => props.onTabChange?.()}\n bindsnap={(e: ListSnapEvent) => {\n setActiveIndex(e.detail.position);\n }}\n bounces={false}\n preload-buffer-count={props.items.length}\n >\n {props.items.map((item) => (\n <list-item\n key={item.key}\n item-key={item.key}\n recyclable={false}\n className={\"tabs-content\"}\n >\n {item.renderContent()}\n </list-item>\n ))}\n </list>\n </view>\n );\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useConsole, useNetwork, usePerformance } from \"../hooks\";\nimport type { CustomTab } from \"../types\";\nimport \"./ConsolePanel.css\";\nimport { dismissFilterDropdown, LogPanel } from \"./LogPanel\";\nimport { NetworkPanel } from \"./NetworkPanel\";\nimport { PerformancePanel } from \"./PerformancePanel\";\nimport Tabs from \"./Tabs\";\n\ninterface ConsolePanelProps {\n customTabs?: CustomTab[];\n}\n\nexport const ConsolePanel = ({ customTabs }: ConsolePanelProps) => {\n const { logs, clearLogs } = useConsole();\n const { networks, clearNetworks } = useNetwork();\n const { performances, clearPerformances } = usePerformance();\n\n const state = globalThis.__LYNX_CONSOLE__?.state;\n\n const items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }> = [];\n\n if (state?.logs) {\n items.push({\n key: \"log\",\n label: \"Log\",\n renderContent: () => <LogPanel logs={logs} clearLogs={clearLogs} />,\n });\n }\n\n if (state?.networks) {\n items.push({\n key: \"network\",\n label: \"Network\",\n renderContent: () => (\n <NetworkPanel networks={networks} clearNetworks={clearNetworks} />\n ),\n });\n }\n\n if (state?.performances) {\n items.push({\n key: \"performance\",\n label: \"Perf\",\n renderContent: () => (\n <PerformancePanel\n performances={performances}\n clearPerformances={clearPerformances}\n />\n ),\n });\n }\n\n if (customTabs) {\n for (const tab of customTabs) {\n items.push({\n key: tab.key,\n label: tab.label,\n renderContent: tab.renderContent,\n });\n }\n }\n\n if (items.length === 0) {\n return null;\n }\n\n return (\n <view className=\"cp-container\">\n <Tabs onTabChange={dismissFilterDropdown} items={items} />\n </view>\n );\n};\n","import { useRef, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\n\nconst LONG_PRESS_DURATION = 400;\nconst MOVE_THRESHOLD = 5;\n\nconst DEFAULT_RIGHT = 16;\nconst DEFAULT_BOTTOM = 84;\n\nlet savedRight = DEFAULT_RIGHT;\nlet savedBottom = DEFAULT_BOTTOM;\n\nexport function useLongPressDrag(onTap: () => void) {\n const [right, setRight] = useState(savedRight);\n const [bottom, setBottom] = useState(savedBottom);\n const [phase, setPhase] = useState<\"idle\" | \"dragging\" | \"releasing\">(\"idle\");\n const [tempRight, setTempRight] = useState(savedRight);\n const [tempBottom, setTempBottom] = useState(savedBottom);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const draggingRef = useRef(false);\n const startRef = useRef({ x: 0, y: 0, r: 0, b: 0 });\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n startRef.current = {\n x: e.detail.x,\n y: e.detail.y,\n r: right,\n b: bottom,\n };\n draggingRef.current = false;\n\n timerRef.current = setTimeout(() => {\n draggingRef.current = true;\n setPhase(\"dragging\");\n setTempRight(right);\n setTempBottom(bottom);\n }, LONG_PRESS_DURATION);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n const dx = e.detail.x - startRef.current.x;\n const dy = e.detail.y - startRef.current.y;\n\n if (\n !draggingRef.current &&\n (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)\n ) {\n clearTimer();\n }\n\n if (!draggingRef.current) return;\n\n // right/bottom 기준이므로 방향 반전\n setTempRight(startRef.current.r - dx);\n setTempBottom(startRef.current.b - dy);\n };\n\n const handleTouchEnd = () => {\n clearTimer();\n\n if (draggingRef.current) {\n setRight(tempRight);\n setBottom(tempBottom);\n savedRight = tempRight;\n savedBottom = tempBottom;\n setPhase(\"releasing\");\n draggingRef.current = false;\n setTimeout(() => setPhase(\"idle\"), 300);\n } else {\n onTap();\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return {\n phase,\n right: isDragging ? tempRight : right,\n bottom: isDragging ? tempBottom : bottom,\n clearTimer,\n handlers: {\n catchtouchstart: handleTouchStart,\n catchtouchmove: handleTouchMove,\n catchtouchend: handleTouchEnd,\n },\n };\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useLongPressDrag } from \"../hooks/useLongPressDrag\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration } from \"../styles/theme\";\nimport \"./FloatingButton.css\";\n\ninterface FloatingButtonProps {\n bindtap: () => void;\n children: ReactNode;\n}\n\nconst SHINE_STYLES = {\n idle: {\n transform: \"scale(0)\",\n opacity: 0,\n },\n dragging: {\n transform: \"scale(1)\",\n opacity: 1,\n transition: \"transform 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n releasing: {\n transform: \"scale(1)\",\n opacity: 0,\n transition: \"opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n} as const;\n\nexport const FloatingButton = ({ bindtap, children }: FloatingButtonProps) => {\n const colors = useThemeColors();\n const { phase, right, bottom, clearTimer, handlers } =\n useLongPressDrag(bindtap);\n\n const handleReload = () => {\n try {\n lynx.reload({}, () => {\n console.log(\"reloaded!\");\n });\n } catch (e) {\n console.error(\"[LynxConsole] reload failed:\", e);\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return (\n <view\n className={\"fb-wrapper\"}\n consume-slide-event={[[-180, 180]]}\n style={{\n right: `${right}px`,\n bottom: `${bottom}px`,\n transform: isDragging ? \"scale(1.05)\" : \"scale(1)\",\n transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n {...handlers}\n >\n <view\n className={\"fb-button\"}\n style={{ backgroundColor: colors.palette.green600 }}\n >\n {children}\n <view className={\"fb-shineOverlay\"} style={SHINE_STYLES[phase]} />\n </view>\n <view\n className={\"fb-reloadButton\"}\n style={{ backgroundColor: colors.palette.green600 }}\n catchtouchstart={() => clearTimer()}\n bindtap={handleReload}\n >\n <text\n className={\"fb-reloadIcon\"}\n style={{ color: colors.palette.staticWhite }}\n >\n {\"\\u21BB\"}\n </text>\n </view>\n </view>\n );\n};\n","import {\n type ForwardedRef,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useState,\n} from \"@lynx-js/react\";\nimport BottomSheet from \"./components/BottomSheet.jsx\";\nimport { ConsolePanel } from \"./components/ConsolePanel.jsx\";\nimport \"./components/FloatingButton.css\";\nimport \"./styles/tokens.css\";\nimport { FloatingButton } from \"./components/FloatingButton.jsx\";\nimport { usePerformance } from \"./hooks/usePerformance\";\nimport { ThemeProvider } from \"./styles/ThemeContext\";\nimport { getColors } from \"./styles/theme\";\nimport type { CustomTab } from \"./types\";\n\nexport interface LynxConsoleHandle {\n open: () => void;\n close: () => void;\n isOpen: () => boolean;\n}\n\nexport interface LynxConsoleProps {\n theme?: \"light\" | \"dark\";\n safeAreaInsetBottom?: string;\n customTabs?: CustomTab[];\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(\n (\n { theme = \"light\", safeAreaInsetBottom = \"50px\", customTabs },\n ref: ForwardedRef<LynxConsoleHandle>,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [shouldClose, setShouldClose] = useState(false);\n const { performances } = usePerformance();\n const colors = useMemo(() => getColors(theme), [theme]);\n\n const latestFcp = useMemo(() => {\n for (let i = performances.length - 1; i >= 0; i--) {\n const perf = performances[i];\n if (perf && perf.entryType === \"metric\" && perf.name === \"fcp\") {\n const metricEntry = perf.rawEntry as MetricFcpEntry | undefined;\n // totalFcp를 먼저 시도하고, 없으면 lynxFcp 반환\n if (metricEntry?.totalFcp?.duration !== undefined) {\n return metricEntry.totalFcp;\n }\n if (metricEntry?.lynxFcp?.duration !== undefined) {\n return metricEntry.lynxFcp;\n }\n }\n }\n return undefined;\n }, [performances]);\n\n useImperativeHandle(ref, () => ({\n open: () => {\n setIsOpen(true);\n setShouldClose(false);\n },\n close: () => {\n setShouldClose(true);\n },\n isOpen: () => isOpen,\n }));\n\n const handleOpenBottomSheet = () => {\n setIsOpen(true);\n setShouldClose(false);\n };\n\n const handleCloseBottomSheet = () => {\n setIsOpen(false);\n setShouldClose(false);\n };\n\n return (\n <ThemeProvider value={colors}>\n <view\n style={{\n backgroundColor: colors.bg.layerDefault,\n color: colors.fg.neutral,\n }}\n >\n <FloatingButton bindtap={handleOpenBottomSheet}>\n <text\n className=\"fb-title t4\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n LynxConsole\n </text>\n <text\n className=\"fb-subtitle t3\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n {`${latestFcp?.name ?? \"FCP\"}: ${latestFcp?.duration ? latestFcp.duration.toFixed(2) : \"--\"}ms`}\n </text>\n </FloatingButton>\n {isOpen && (\n <BottomSheet\n isOpen={isOpen}\n shouldClose={shouldClose}\n onClose={handleCloseBottomSheet}\n title=\"Lynx Console\"\n safeAreaInsetBottom={safeAreaInsetBottom}\n >\n <ConsolePanel customTabs={customTabs} />\n </BottomSheet>\n )}\n </view>\n </ThemeProvider>\n );\n },\n);\n\nexport type { CustomTab } from \"./types\";\nexport default LynxConsole;\n"],"mappings":"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"}
package/dist/setup.cjs CHANGED
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  let _lynx_js_react = require("@lynx-js/react");
3
+ let devalue = require("devalue");
3
4
  let javascript_stringify = require("javascript-stringify");
4
5
 
5
6
  //#region src/shared/ensureConsoleStructure.ts
@@ -95,10 +96,9 @@ const _setupMainThreadConsole = () => {
95
96
  const serializeArgs = (args) => {
96
97
  return args.map((arg) => {
97
98
  try {
98
- JSON.stringify(arg);
99
- return arg;
99
+ return (0, devalue.stringify)(arg);
100
100
  } catch {
101
- return String(arg);
101
+ return (0, devalue.stringify)(String(arg));
102
102
  }
103
103
  });
104
104
  };
@@ -108,6 +108,10 @@ const _setupMainThreadConsole = () => {
108
108
  const sendLogToBackground = (0, _lynx_js_react.runOnBackground)((entry) => {
109
109
  const state = globalThis.__LYNX_CONSOLE__?.state;
110
110
  if (!state) return;
111
+ entry.args = entry.args.map((arg) => {
112
+ if (typeof arg !== "string") throw new Error(`[LynxConsole] Expected devalue string, got ${typeof arg}`);
113
+ return (0, devalue.parse)(arg);
114
+ });
111
115
  state.logs?.push(entry);
112
116
  state.logListeners?.forEach((listener) => {
113
117
  listener(entry);
package/dist/setup.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { runOnBackground, runOnMainThread } from "@lynx-js/react";
2
- import { stringify } from "javascript-stringify";
2
+ import { parse, stringify } from "devalue" with { runtime: "shared" };
3
+ import { stringify as stringify$1 } from "javascript-stringify";
3
4
 
4
5
  //#region src/shared/ensureConsoleStructure.ts
5
6
  const ensureConsoleStructure = () => {
@@ -94,10 +95,9 @@ const _setupMainThreadConsole = () => {
94
95
  const serializeArgs = (args) => {
95
96
  return args.map((arg) => {
96
97
  try {
97
- JSON.stringify(arg);
98
- return arg;
98
+ return stringify(arg);
99
99
  } catch {
100
- return String(arg);
100
+ return stringify(String(arg));
101
101
  }
102
102
  });
103
103
  };
@@ -107,6 +107,10 @@ const _setupMainThreadConsole = () => {
107
107
  const sendLogToBackground = runOnBackground((entry) => {
108
108
  const state = globalThis.__LYNX_CONSOLE__?.state;
109
109
  if (!state) return;
110
+ entry.args = entry.args.map((arg) => {
111
+ if (typeof arg !== "string") throw new Error(`[LynxConsole] Expected devalue string, got ${typeof arg}`);
112
+ return parse(arg);
113
+ });
110
114
  state.logs?.push(entry);
111
115
  state.logListeners?.forEach((listener) => {
112
116
  listener(entry);
@@ -285,7 +289,7 @@ const initNetworkMonitor = () => {
285
289
  headerMap[key.toLowerCase()] = value;
286
290
  });
287
291
  const contentType = headerMap["content-type"];
288
- if (contentType?.includes("application/json")) responseBody = stringify(await clonedResponse.json(), null, 2, { references: true }) ?? "";
292
+ if (contentType?.includes("application/json")) responseBody = stringify$1(await clonedResponse.json(), null, 2, { references: true }) ?? "";
289
293
  else if (contentType?.includes("text")) responseBody = await clonedResponse.text();
290
294
  } catch (error) {
291
295
  responseBody = `[Unable to read response body] ${error}`;
@@ -1 +1 @@
1
- {"version":3,"file":"setup.mjs","names":[],"sources":["../src/shared/ensureConsoleStructure.ts","../src/setup/setupLogMonitor.ts","../src/setup/_setupMainThreadConsole.ts","../src/setup/setupMainThreadConsole.ts","../src/setup/setupNetworkMonitor.ts","../src/setup/setupPerformanceMonitor.ts"],"sourcesContent":["type LynxConsole = NonNullable<typeof globalThis.__LYNX_CONSOLE__>;\ntype ConsoleState = NonNullable<LynxConsole[\"state\"]>;\n\nexport const ensureConsoleStructure = (): {\n lynxConsole: LynxConsole;\n state: ConsoleState;\n} => {\n if (!globalThis.__LYNX_CONSOLE__) {\n globalThis.__LYNX_CONSOLE__ = {};\n }\n\n if (!globalThis.__LYNX_CONSOLE__.state) {\n globalThis.__LYNX_CONSOLE__.state = {};\n }\n\n return {\n lynxConsole: globalThis.__LYNX_CONSOLE__,\n state: globalThis.__LYNX_CONSOLE__.state,\n };\n};\n","import { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\ntype LogListener = (entry: LogEntry) => void;\n\nconst LOG_METHODS: LogLevel[] = [\"log\", \"warn\", \"error\", \"info\"];\nconst LOG_ID_PREFIX = \"background-thread\";\n\nconst generateLogId = (): string => {\n return `${LOG_ID_PREFIX}-${Date.now()}-${Math.random()}`;\n};\n\nconst createLogEntry = (method: LogLevel, args: unknown[]): LogEntry => {\n return {\n id: generateLogId(),\n level: method,\n message: \"\",\n timestamp: Date.now(),\n args,\n };\n};\n\nconst addLogEntry = (entry: LogEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.logs || !state?.logListeners) {\n console.error(\n \"[LynxConsole] Cannot add log entry: Log monitor not initialized. Call initLogMonitor() first.\",\n );\n return;\n }\n\n state.logs.push(entry);\n state.logListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\n// Background Thread: Log monitoring 초기화\nexport const initLogMonitor = () => {\n \"background only\";\n\n const { lynxConsole, state } = ensureConsoleStructure();\n\n if (lynxConsole.originalConsole) {\n console.warn(\"[LynxConsole] Log monitor already initialized\");\n return;\n }\n\n const originalConsole = globalThis.console;\n lynxConsole.originalConsole = {\n log: originalConsole.log.bind(originalConsole),\n warn: originalConsole.warn.bind(originalConsole),\n error: originalConsole.error.bind(originalConsole),\n info: originalConsole.info.bind(originalConsole),\n };\n\n state.logs = [];\n state.logListeners = new Set();\n state.logSubscribe = (listener: LogListener) => {\n state.logListeners?.add(listener);\n return () => {\n state.logListeners?.delete(listener);\n };\n };\n\n // Background Thread console 오버라이드\n LOG_METHODS.forEach((method) => {\n globalThis.console[method] = ((...args: unknown[]) => {\n lynxConsole.originalConsole?.[method](...args);\n const entry = createLogEntry(method, args);\n addLogEntry(entry);\n }).bind(globalThis.console);\n });\n\n lynxConsole.originalConsole?.log(\n \"[LynxConsole] ✅ Log monitoring initialized\",\n );\n};\n","import { runOnBackground } from \"@lynx-js/react\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\nconst _setupMainThreadConsole = (): void => {\n \"main thread\";\n\n //IMPORTANT: do not use external functions in main thread\n if (!globalThis.__LYNX_CONSOLE__) globalThis.__LYNX_CONSOLE__ = {};\n const lynxConsole = globalThis.__LYNX_CONSOLE__;\n\n if (lynxConsole.mainThreadInitialized) {\n console.warn(\"[LynxConsole] Main thread console already initialized\");\n return;\n }\n\n const LOG_METHODS: LogLevel[] = [\"log\", \"warn\", \"error\", \"info\"];\n const LOG_ID_PREFIX = \"main-thread\";\n\n const serializeArgs = (args: unknown[]): unknown[] => {\n return args.map((arg) => {\n try {\n JSON.stringify(arg);\n return arg;\n } catch {\n return String(arg);\n }\n });\n };\n\n const generateLogId = (): string => {\n return `${LOG_ID_PREFIX}-${Date.now()}-${Math.random()}`;\n };\n\n // Main Thread에서 Background Thread로 로그 전송하는 함수\n const sendLogToBackground = runOnBackground((entry: LogEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state) return;\n\n state.logs?.push(entry);\n state.logListeners?.forEach((listener) => {\n listener(entry);\n });\n });\n\n const originalConsole = globalThis.console;\n\n const originalMethods = {\n log: originalConsole.log.bind(originalConsole),\n warn: originalConsole.warn.bind(originalConsole),\n error: originalConsole.error.bind(originalConsole),\n info: originalConsole.info.bind(originalConsole),\n };\n\n // Main Thread console 오버라이드\n LOG_METHODS.forEach((method) => {\n const original = originalMethods[method];\n originalConsole[method] = ((...args: unknown[]) => {\n // 원본 console 호출\n original(...args);\n\n const serializedArgs = serializeArgs(args);\n const timestamp = Date.now();\n const id = generateLogId();\n\n sendLogToBackground({\n id,\n level: method,\n message: \"\",\n timestamp,\n args: serializedArgs,\n });\n }).bind(originalConsole);\n });\n\n lynxConsole.mainThreadInitialized = true;\n\n originalConsole.log(\"[LynxConsole] ✅ Main thread console initialized\");\n};\n\nexport default _setupMainThreadConsole;\n","// both thread;\nimport \"./_setupMainThreadConsole\";\n\nimport { runOnMainThread } from \"@lynx-js/react\";\nimport { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport _setupMainThreadConsole from \"./_setupMainThreadConsole\";\n\n// Main Thread: Console 초기화\n\nexport const initMainThreadConsole = async (): Promise<void> => {\n \"background only\";\n\n const { lynxConsole, state } = ensureConsoleStructure();\n\n if (lynxConsole.mainThreadInitialized) {\n console.error(\"[LynxConsole] Main thread console already initialized\");\n return;\n }\n\n if (!state.logs) {\n console.error(\"[LynxConsole] Background thread console not initialized\");\n return;\n }\n\n try {\n const setupOnMainThread = runOnMainThread(_setupMainThreadConsole);\n await setupOnMainThread();\n } catch (error) {\n console.error(\n \"[LynxConsole] Failed to initialize main thread console:\",\n error,\n );\n }\n};\n","import { stringify } from \"javascript-stringify\";\nimport { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { NetworkEntry } from \"../types\";\n\nconst generateNetworkId = (): string => {\n return `network-${Date.now()}-${Math.random()}`;\n};\n\nconst extractUrl = (input: RequestInfo | URL): string => {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return (input as Request).url;\n};\n\nconst extractMethod = (\n input: RequestInfo | URL,\n init?: RequestInit,\n): string | undefined => {\n if (init?.method) return init.method;\n if (typeof input === \"object\" && \"method\" in input) {\n return (input as Request).method;\n }\n return \"GET\";\n};\n\nconst extractHeaders = (\n headers: HeadersInit | undefined,\n): Record<string, string> => {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n try {\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n result[key] = value;\n });\n } else if (Array.isArray(headers)) {\n headers.forEach(([key, value]) => {\n result[key] = value;\n });\n } else {\n Object.entries(headers).forEach(([key, value]) => {\n result[key] = value;\n });\n }\n } catch (error) {\n console.error(\"[LynxConsole] Failed to extract headers:\", error);\n }\n\n return result;\n};\n\nconst mergeRequestHeaders = (\n input: RequestInfo | URL,\n init?: RequestInit,\n): Record<string, string> => {\n const merged: Record<string, string> = {};\n\n // Request 객체나 URL 객체에서 헤더 추출\n if (typeof input === \"object\" && \"headers\" in input) {\n Object.assign(merged, extractHeaders(input.headers as HeadersInit));\n }\n\n // RequestInit에서 헤더 추출 (나중 값이 우선)\n if (init?.headers) {\n Object.assign(merged, extractHeaders(init.headers));\n }\n\n return merged;\n};\n\nconst addNetworkEntry = (entry: NetworkEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.networks || !state?.networksMap || !state?.networkListeners) {\n console.error(\n \"[LynxConsole] Cannot add network entry: Network monitor not initialized. Call initNetworkMonitor() first.\",\n );\n return;\n }\n\n state.networks.push(entry);\n state.networksMap.set(entry.id, entry);\n\n state.networkListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\nconst updateNetworkEntry = (\n id: string,\n updates: Partial<NetworkEntry>,\n): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.networks || !state?.networksMap || !state?.networkListeners) {\n console.error(\n \"[LynxConsole] Cannot update network entry: Network monitor not initialized. Call initNetworkMonitor() first.\",\n );\n return;\n }\n\n const existingEntry = state.networksMap.get(id);\n if (!existingEntry) {\n console.error(\n `[LynxConsole] Cannot update network entry: Entry with id '${id}' not found.`,\n );\n return;\n }\n\n const updatedEntry = { ...existingEntry, ...updates };\n\n const index = state.networks.findIndex((entry) => entry.id === id);\n if (index !== -1) {\n state.networks[index] = updatedEntry;\n }\n\n state.networksMap.set(id, updatedEntry);\n\n state.networkListeners.forEach((listener) => {\n listener(updatedEntry);\n });\n};\n\nexport const initNetworkMonitor = () => {\n if (!lynx.fetch) {\n console.warn(\n \"[LynxConsole] lynx.fetch not available, skipping network monitor\",\n );\n return;\n }\n\n const { state } = ensureConsoleStructure();\n\n if (state.networks !== undefined) {\n console.warn(\"[LynxConsole] Network monitor already initialized\");\n return;\n }\n\n type NetworkListener = (entry: NetworkEntry) => void;\n state.networks = [];\n state.networksMap = new Map();\n state.networkListeners = new Set();\n state.subscribeNetwork = (listener: NetworkListener) => {\n state.networkListeners?.add(listener);\n return () => {\n state.networkListeners?.delete(listener);\n };\n };\n\n const originalFetch = fetch.bind(lynx);\n\n const monitoredFetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const id = generateNetworkId();\n const startTime = Date.now();\n const url = extractUrl(input);\n const method = extractMethod(input, init);\n\n const requestHeaders = mergeRequestHeaders(input, init);\n\n // Request body 처리\n let requestBody: string | undefined;\n if (init?.body) {\n if (typeof init.body === \"string\") {\n requestBody = init.body;\n } else if (init.body instanceof URLSearchParams) {\n requestBody = init.body.toString();\n } else {\n // Lynx가 지원하지 않는 타입이거나 알 수 없는 타입\n requestBody = String(init.body);\n }\n }\n\n addNetworkEntry({\n id,\n url,\n method: method || \"default\",\n status: \"pending\",\n startTime,\n requestHeaders,\n requestBody: requestBody ?? \"\",\n });\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const clonedResponse = response.clone();\n let responseBody: string | undefined;\n\n try {\n const headerMap: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headerMap[key.toLowerCase()] = value;\n });\n\n const contentType = headerMap[\"content-type\"];\n if (contentType?.includes(\"application/json\")) {\n const json = await clonedResponse.json();\n responseBody = stringify(json, null, 2, { references: true }) ?? \"\";\n } else if (contentType?.includes(\"text\")) {\n responseBody = await clonedResponse.text();\n }\n } catch (error) {\n responseBody = `[Unable to read response body] ${error}`;\n console.error(\"[LynxConsole] Error reading response body:\", error);\n }\n\n updateNetworkEntry(id, {\n status: \"success\",\n statusCode: response.status,\n statusText: response.statusText,\n endTime,\n duration: endTime - startTime,\n responseHeaders,\n responseBody: responseBody ?? \"\",\n });\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n updateNetworkEntry(id, {\n status: \"error\",\n endTime,\n duration: endTime - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n };\n\n // biome-ignore lint/suspicious/noTsIgnore: to assign fetch to global functionfetch\n // @ts-ignore\n // biome-ignore lint/suspicious/noGlobalAssign: to assign fetch to global functionfetch fetch\n fetch = monitoredFetch as typeof fetch;\n\n //fetch 대신 lynx.fetch를 사용하는 경우에도 모니터링 되도록 설정\n lynx.fetch = monitoredFetch as typeof lynx.fetch;\n\n console.log(\"[LynxConsole] ✅ Network monitoring initialized\");\n};\n","import { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { PerformanceEntryData, PerformanceEntryType } from \"../types\";\n\ntype PerformanceListener = (entry: PerformanceEntryData) => void;\n\nconst generatePerformanceId = (): string => {\n return `performance-${Date.now()}-${Math.random()}`;\n};\n\nconst addPerformanceEntry = (entry: PerformanceEntryData): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.performances || !state?.performanceListeners) {\n console.error(\n \"[LynxConsole] Cannot add performance entry: Performance monitor not initialized. Call initPerformanceMonitor() first.\",\n );\n return;\n }\n\n state.performances.push(entry);\n state.performanceListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\nexport const initPerformanceMonitor = () => {\n \"background only\";\n\n if (!lynx.performance) {\n console.warn(\n \"[LynxConsole] lynx.performance not available, skipping performance monitor\",\n );\n return;\n }\n\n const { state } = ensureConsoleStructure();\n\n if (state.performances !== undefined) {\n console.warn(\"[LynxConsole] Performance monitor already initialized\");\n return;\n }\n\n state.performances = [];\n state.performanceListeners = new Set();\n state.subscribePerformance = (listener: PerformanceListener) => {\n state.performanceListeners?.add(listener);\n return () => {\n state.performanceListeners?.delete(listener);\n };\n };\n\n const observer = lynx.performance.createObserver((entry) => {\n const performanceEntry: PerformanceEntryData = {\n id: generatePerformanceId(),\n entryType: entry.entryType as PerformanceEntryType,\n name: entry.name,\n timestamp: Date.now(),\n rawEntry: entry,\n };\n\n addPerformanceEntry(performanceEntry);\n });\n\n observer.observe([\n \"pipeline\", // LoadBundleEntry\n \"init\", // InitLynxviewEntry\n \"metric\", // MetricEntry\n ]);\n\n console.log(\"[LynxConsole] ✅ Performance monitoring initialized\");\n};\n"],"mappings":";;;;AAGA,MAAa,+BAGR;AACH,KAAI,CAAC,WAAW,iBACd,YAAW,mBAAmB,EAAE;AAGlC,KAAI,CAAC,WAAW,iBAAiB,MAC/B,YAAW,iBAAiB,QAAQ,EAAE;AAGxC,QAAO;EACL,aAAa,WAAW;EACxB,OAAO,WAAW,iBAAiB;EACpC;;;;;ACbH,MAAM,cAA0B;CAAC;CAAO;CAAQ;CAAS;CAAO;AAChE,MAAM,gBAAgB;AAEtB,MAAM,sBAA8B;AAClC,QAAO,GAAG,cAAc,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ;;AAGxD,MAAM,kBAAkB,QAAkB,SAA8B;AACtE,QAAO;EACL,IAAI,eAAe;EACnB,OAAO;EACP,SAAS;EACT,WAAW,KAAK,KAAK;EACrB;EACD;;AAGH,MAAM,eAAe,UAA0B;CAC7C,MAAM,QAAQ,WAAW,kBAAkB;AAC3C,KAAI,CAAC,OAAO,QAAQ,CAAC,OAAO,cAAc;AACxC,UAAQ,MACN,gGACD;AACD;;AAGF,OAAM,KAAK,KAAK,MAAM;AACtB,OAAM,aAAa,SAAS,aAAa;AACvC,WAAS,MAAM;GACf;;AAIJ,MAAa,uBAAuB;AAClC;CAEA,MAAM,EAAE,aAAa,UAAU,wBAAwB;AAEvD,KAAI,YAAY,iBAAiB;AAC/B,UAAQ,KAAK,gDAAgD;AAC7D;;CAGF,MAAM,kBAAkB,WAAW;AACnC,aAAY,kBAAkB;EAC5B,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;EAC9C,MAAM,gBAAgB,KAAK,KAAK,gBAAgB;EAChD,OAAO,gBAAgB,MAAM,KAAK,gBAAgB;EAClD,MAAM,gBAAgB,KAAK,KAAK,gBAAgB;EACjD;AAED,OAAM,OAAO,EAAE;AACf,OAAM,+BAAe,IAAI,KAAK;AAC9B,OAAM,gBAAgB,aAA0B;AAC9C,QAAM,cAAc,IAAI,SAAS;AACjC,eAAa;AACX,SAAM,cAAc,OAAO,SAAS;;;AAKxC,aAAY,SAAS,WAAW;AAC9B,aAAW,QAAQ,YAAY,GAAG,SAAoB;AACpD,eAAY,kBAAkB,QAAQ,GAAG,KAAK;AAE9C,eADc,eAAe,QAAQ,KAAK,CACxB;KACjB,KAAK,WAAW,QAAQ;GAC3B;AAEF,aAAY,iBAAiB,IAC3B,6CACD;;;;;ACzEH,MAAM,gCAAsC;AAC1C;AAGA,KAAI,CAAC,WAAW,iBAAkB,YAAW,mBAAmB,EAAE;CAClE,MAAM,cAAc,WAAW;AAE/B,KAAI,YAAY,uBAAuB;AACrC,UAAQ,KAAK,wDAAwD;AACrE;;CAGF,MAAM,cAA0B;EAAC;EAAO;EAAQ;EAAS;EAAO;CAChE,MAAM,gBAAgB;CAEtB,MAAM,iBAAiB,SAA+B;AACpD,SAAO,KAAK,KAAK,QAAQ;AACvB,OAAI;AACF,SAAK,UAAU,IAAI;AACnB,WAAO;WACD;AACN,WAAO,OAAO,IAAI;;IAEpB;;CAGJ,MAAM,sBAA8B;AAClC,SAAO,GAAG,cAAc,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ;;CAIxD,MAAM,sBAAsB,iBAAiB,UAA0B;EACrE,MAAM,QAAQ,WAAW,kBAAkB;AAC3C,MAAI,CAAC,MAAO;AAEZ,QAAM,MAAM,KAAK,MAAM;AACvB,QAAM,cAAc,SAAS,aAAa;AACxC,YAAS,MAAM;IACf;GACF;CAEF,MAAM,kBAAkB,WAAW;CAEnC,MAAM,kBAAkB;EACtB,KAAK,gBAAgB,IAAI,KAAK,gBAAgB;EAC9C,MAAM,gBAAgB,KAAK,KAAK,gBAAgB;EAChD,OAAO,gBAAgB,MAAM,KAAK,gBAAgB;EAClD,MAAM,gBAAgB,KAAK,KAAK,gBAAgB;EACjD;AAGD,aAAY,SAAS,WAAW;EAC9B,MAAM,WAAW,gBAAgB;AACjC,kBAAgB,YAAY,GAAG,SAAoB;AAEjD,YAAS,GAAG,KAAK;GAEjB,MAAM,iBAAiB,cAAc,KAAK;GAC1C,MAAM,YAAY,KAAK,KAAK;AAG5B,uBAAoB;IAClB,IAHS,eAAe;IAIxB,OAAO;IACP,SAAS;IACT;IACA,MAAM;IACP,CAAC;KACD,KAAK,gBAAgB;GACxB;AAEF,aAAY,wBAAwB;AAEpC,iBAAgB,IAAI,kDAAkD;;;;;ACnExE,MAAa,wBAAwB,YAA2B;AAC9D;CAEA,MAAM,EAAE,aAAa,UAAU,wBAAwB;AAEvD,KAAI,YAAY,uBAAuB;AACrC,UAAQ,MAAM,wDAAwD;AACtE;;AAGF,KAAI,CAAC,MAAM,MAAM;AACf,UAAQ,MAAM,0DAA0D;AACxE;;AAGF,KAAI;AAEF,QAD0B,gBAAgB,wBAAwB,EACzC;UAClB,OAAO;AACd,UAAQ,MACN,2DACA,MACD;;;;;;AC3BL,MAAM,0BAAkC;AACtC,QAAO,WAAW,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ;;AAG/C,MAAM,cAAc,UAAqC;AACvD,KAAI,OAAO,UAAU,SAAU,QAAO;AACtC,KAAI,iBAAiB,IAAK,QAAO,MAAM;AACvC,QAAQ,MAAkB;;AAG5B,MAAM,iBACJ,OACA,SACuB;AACvB,KAAI,MAAM,OAAQ,QAAO,KAAK;AAC9B,KAAI,OAAO,UAAU,YAAY,YAAY,MAC3C,QAAQ,MAAkB;AAE5B,QAAO;;AAGT,MAAM,kBACJ,YAC2B;CAC3B,MAAM,SAAiC,EAAE;AACzC,KAAI,CAAC,QAAS,QAAO;AAErB,KAAI;AACF,MAAI,mBAAmB,QACrB,SAAQ,SAAS,OAAO,QAAQ;AAC9B,UAAO,OAAO;IACd;WACO,MAAM,QAAQ,QAAQ,CAC/B,SAAQ,SAAS,CAAC,KAAK,WAAW;AAChC,UAAO,OAAO;IACd;MAEF,QAAO,QAAQ,QAAQ,CAAC,SAAS,CAAC,KAAK,WAAW;AAChD,UAAO,OAAO;IACd;UAEG,OAAO;AACd,UAAQ,MAAM,4CAA4C,MAAM;;AAGlE,QAAO;;AAGT,MAAM,uBACJ,OACA,SAC2B;CAC3B,MAAM,SAAiC,EAAE;AAGzC,KAAI,OAAO,UAAU,YAAY,aAAa,MAC5C,QAAO,OAAO,QAAQ,eAAe,MAAM,QAAuB,CAAC;AAIrE,KAAI,MAAM,QACR,QAAO,OAAO,QAAQ,eAAe,KAAK,QAAQ,CAAC;AAGrD,QAAO;;AAGT,MAAM,mBAAmB,UAA8B;CACrD,MAAM,QAAQ,WAAW,kBAAkB;AAC3C,KAAI,CAAC,OAAO,YAAY,CAAC,OAAO,eAAe,CAAC,OAAO,kBAAkB;AACvE,UAAQ,MACN,4GACD;AACD;;AAGF,OAAM,SAAS,KAAK,MAAM;AAC1B,OAAM,YAAY,IAAI,MAAM,IAAI,MAAM;AAEtC,OAAM,iBAAiB,SAAS,aAAa;AAC3C,WAAS,MAAM;GACf;;AAGJ,MAAM,sBACJ,IACA,YACS;CACT,MAAM,QAAQ,WAAW,kBAAkB;AAC3C,KAAI,CAAC,OAAO,YAAY,CAAC,OAAO,eAAe,CAAC,OAAO,kBAAkB;AACvE,UAAQ,MACN,+GACD;AACD;;CAGF,MAAM,gBAAgB,MAAM,YAAY,IAAI,GAAG;AAC/C,KAAI,CAAC,eAAe;AAClB,UAAQ,MACN,6DAA6D,GAAG,cACjE;AACD;;CAGF,MAAM,eAAe;EAAE,GAAG;EAAe,GAAG;EAAS;CAErD,MAAM,QAAQ,MAAM,SAAS,WAAW,UAAU,MAAM,OAAO,GAAG;AAClE,KAAI,UAAU,GACZ,OAAM,SAAS,SAAS;AAG1B,OAAM,YAAY,IAAI,IAAI,aAAa;AAEvC,OAAM,iBAAiB,SAAS,aAAa;AAC3C,WAAS,aAAa;GACtB;;AAGJ,MAAa,2BAA2B;AACtC,KAAI,CAAC,KAAK,OAAO;AACf,UAAQ,KACN,mEACD;AACD;;CAGF,MAAM,EAAE,UAAU,wBAAwB;AAE1C,KAAI,MAAM,aAAa,QAAW;AAChC,UAAQ,KAAK,oDAAoD;AACjE;;AAIF,OAAM,WAAW,EAAE;AACnB,OAAM,8BAAc,IAAI,KAAK;AAC7B,OAAM,mCAAmB,IAAI,KAAK;AAClC,OAAM,oBAAoB,aAA8B;AACtD,QAAM,kBAAkB,IAAI,SAAS;AACrC,eAAa;AACX,SAAM,kBAAkB,OAAO,SAAS;;;CAI5C,MAAM,gBAAgB,MAAM,KAAK,KAAK;CAEtC,MAAM,iBAAiB,OACrB,OACA,SACsB;EACtB,MAAM,KAAK,mBAAmB;EAC9B,MAAM,YAAY,KAAK,KAAK;EAC5B,MAAM,MAAM,WAAW,MAAM;EAC7B,MAAM,SAAS,cAAc,OAAO,KAAK;EAEzC,MAAM,iBAAiB,oBAAoB,OAAO,KAAK;EAGvD,IAAI;AACJ,MAAI,MAAM,KACR,KAAI,OAAO,KAAK,SAAS,SACvB,eAAc,KAAK;WACV,KAAK,gBAAgB,gBAC9B,eAAc,KAAK,KAAK,UAAU;MAGlC,eAAc,OAAO,KAAK,KAAK;AAInC,kBAAgB;GACd;GACA;GACA,QAAQ,UAAU;GAClB,QAAQ;GACR;GACA;GACA,aAAa,eAAe;GAC7B,CAAC;AAEF,MAAI;GACF,MAAM,WAAW,MAAM,cAAc,OAAO,KAAK;GACjD,MAAM,UAAU,KAAK,KAAK;GAE1B,MAAM,kBAA0C,EAAE;AAClD,YAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,oBAAgB,OAAO;KACvB;GAEF,MAAM,iBAAiB,SAAS,OAAO;GACvC,IAAI;AAEJ,OAAI;IACF,MAAM,YAAoC,EAAE;AAC5C,aAAS,QAAQ,SAAS,OAAO,QAAQ;AACvC,eAAU,IAAI,aAAa,IAAI;MAC/B;IAEF,MAAM,cAAc,UAAU;AAC9B,QAAI,aAAa,SAAS,mBAAmB,CAE3C,gBAAe,UADF,MAAM,eAAe,MAAM,EACT,MAAM,GAAG,EAAE,YAAY,MAAM,CAAC,IAAI;aACxD,aAAa,SAAS,OAAO,CACtC,gBAAe,MAAM,eAAe,MAAM;YAErC,OAAO;AACd,mBAAe,kCAAkC;AACjD,YAAQ,MAAM,8CAA8C,MAAM;;AAGpE,sBAAmB,IAAI;IACrB,QAAQ;IACR,YAAY,SAAS;IACrB,YAAY,SAAS;IACrB;IACA,UAAU,UAAU;IACpB;IACA,cAAc,gBAAgB;IAC/B,CAAC;AAEF,UAAO;WACA,OAAO;GACd,MAAM,UAAU,KAAK,KAAK;AAC1B,sBAAmB,IAAI;IACrB,QAAQ;IACR;IACA,UAAU,UAAU;IACpB,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;IAC9D,CAAC;AACF,SAAM;;;AAOV,SAAQ;AAGR,MAAK,QAAQ;AAEb,SAAQ,IAAI,iDAAiD;;;;;AChP/D,MAAM,8BAAsC;AAC1C,QAAO,eAAe,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ;;AAGnD,MAAM,uBAAuB,UAAsC;CACjE,MAAM,QAAQ,WAAW,kBAAkB;AAC3C,KAAI,CAAC,OAAO,gBAAgB,CAAC,OAAO,sBAAsB;AACxD,UAAQ,MACN,wHACD;AACD;;AAGF,OAAM,aAAa,KAAK,MAAM;AAC9B,OAAM,qBAAqB,SAAS,aAAa;AAC/C,WAAS,MAAM;GACf;;AAGJ,MAAa,+BAA+B;AAC1C;AAEA,KAAI,CAAC,KAAK,aAAa;AACrB,UAAQ,KACN,6EACD;AACD;;CAGF,MAAM,EAAE,UAAU,wBAAwB;AAE1C,KAAI,MAAM,iBAAiB,QAAW;AACpC,UAAQ,KAAK,wDAAwD;AACrE;;AAGF,OAAM,eAAe,EAAE;AACvB,OAAM,uCAAuB,IAAI,KAAK;AACtC,OAAM,wBAAwB,aAAkC;AAC9D,QAAM,sBAAsB,IAAI,SAAS;AACzC,eAAa;AACX,SAAM,sBAAsB,OAAO,SAAS;;;AAgBhD,CAZiB,KAAK,YAAY,gBAAgB,UAAU;AAS1D,sBAR+C;GAC7C,IAAI,uBAAuB;GAC3B,WAAW,MAAM;GACjB,MAAM,MAAM;GACZ,WAAW,KAAK,KAAK;GACrB,UAAU;GACX,CAEoC;GACrC,CAEO,QAAQ;EACf;EACA;EACA;EACD,CAAC;AAEF,SAAQ,IAAI,qDAAqD"}
1
+ {"version":3,"file":"setup.mjs","names":["devalueStringify","devalueParse","stringify"],"sources":["../src/shared/ensureConsoleStructure.ts","../src/setup/setupLogMonitor.ts","../src/setup/_setupMainThreadConsole.ts","../src/setup/setupMainThreadConsole.ts","../src/setup/setupNetworkMonitor.ts","../src/setup/setupPerformanceMonitor.ts"],"sourcesContent":["type LynxConsole = NonNullable<typeof globalThis.__LYNX_CONSOLE__>;\ntype ConsoleState = NonNullable<LynxConsole[\"state\"]>;\n\nexport const ensureConsoleStructure = (): {\n lynxConsole: LynxConsole;\n state: ConsoleState;\n} => {\n if (!globalThis.__LYNX_CONSOLE__) {\n globalThis.__LYNX_CONSOLE__ = {};\n }\n\n if (!globalThis.__LYNX_CONSOLE__.state) {\n globalThis.__LYNX_CONSOLE__.state = {};\n }\n\n return {\n lynxConsole: globalThis.__LYNX_CONSOLE__,\n state: globalThis.__LYNX_CONSOLE__.state,\n };\n};\n","import { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\ntype LogListener = (entry: LogEntry) => void;\n\nconst LOG_METHODS: LogLevel[] = [\"log\", \"warn\", \"error\", \"info\"];\nconst LOG_ID_PREFIX = \"background-thread\";\n\nconst generateLogId = (): string => {\n return `${LOG_ID_PREFIX}-${Date.now()}-${Math.random()}`;\n};\n\nconst createLogEntry = (method: LogLevel, args: unknown[]): LogEntry => {\n return {\n id: generateLogId(),\n level: method,\n message: \"\",\n timestamp: Date.now(),\n args,\n };\n};\n\nconst addLogEntry = (entry: LogEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.logs || !state?.logListeners) {\n console.error(\n \"[LynxConsole] Cannot add log entry: Log monitor not initialized. Call initLogMonitor() first.\",\n );\n return;\n }\n\n state.logs.push(entry);\n state.logListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\n// Background Thread: Log monitoring 초기화\nexport const initLogMonitor = () => {\n \"background only\";\n\n const { lynxConsole, state } = ensureConsoleStructure();\n\n if (lynxConsole.originalConsole) {\n console.warn(\"[LynxConsole] Log monitor already initialized\");\n return;\n }\n\n const originalConsole = globalThis.console;\n lynxConsole.originalConsole = {\n log: originalConsole.log.bind(originalConsole),\n warn: originalConsole.warn.bind(originalConsole),\n error: originalConsole.error.bind(originalConsole),\n info: originalConsole.info.bind(originalConsole),\n };\n\n state.logs = [];\n state.logListeners = new Set();\n state.logSubscribe = (listener: LogListener) => {\n state.logListeners?.add(listener);\n return () => {\n state.logListeners?.delete(listener);\n };\n };\n\n // Background Thread console 오버라이드\n LOG_METHODS.forEach((method) => {\n globalThis.console[method] = ((...args: unknown[]) => {\n lynxConsole.originalConsole?.[method](...args);\n const entry = createLogEntry(method, args);\n addLogEntry(entry);\n }).bind(globalThis.console);\n });\n\n lynxConsole.originalConsole?.log(\n \"[LynxConsole] ✅ Log monitoring initialized\",\n );\n};\n","import { runOnBackground } from \"@lynx-js/react\";\nimport { stringify as devalueStringify } from \"devalue\" with {\n runtime: \"shared\",\n};\nimport { parse as devalueParse } from \"devalue\";\nimport type { LogEntry, LogLevel } from \"../types\";\n\nconst _setupMainThreadConsole = (): void => {\n \"main thread\";\n\n //IMPORTANT: do not use external functions in main thread\n if (!globalThis.__LYNX_CONSOLE__) globalThis.__LYNX_CONSOLE__ = {};\n const lynxConsole = globalThis.__LYNX_CONSOLE__;\n\n if (lynxConsole.mainThreadInitialized) {\n console.warn(\"[LynxConsole] Main thread console already initialized\");\n return;\n }\n\n const LOG_METHODS: LogLevel[] = [\"log\", \"warn\", \"error\", \"info\"];\n const LOG_ID_PREFIX = \"main-thread\";\n\n const serializeArgs = (args: unknown[]): string[] => {\n return args.map((arg) => {\n try {\n return devalueStringify(arg);\n } catch {\n return devalueStringify(String(arg));\n }\n });\n };\n\n const generateLogId = (): string => {\n return `${LOG_ID_PREFIX}-${Date.now()}-${Math.random()}`;\n };\n\n // Main Thread에서 Background Thread로 로그 전송하는 함수\n const sendLogToBackground = runOnBackground((entry: LogEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state) return;\n\n // devalue 문자열을 원래 타입으로 복원\n entry.args = entry.args.map((arg) => {\n if (typeof arg !== \"string\") {\n throw new Error(\n `[LynxConsole] Expected devalue string, got ${typeof arg}`,\n );\n }\n return devalueParse(arg);\n });\n\n state.logs?.push(entry);\n state.logListeners?.forEach((listener) => {\n listener(entry);\n });\n });\n\n const originalConsole = globalThis.console;\n\n const originalMethods = {\n log: originalConsole.log.bind(originalConsole),\n warn: originalConsole.warn.bind(originalConsole),\n error: originalConsole.error.bind(originalConsole),\n info: originalConsole.info.bind(originalConsole),\n };\n\n // Main Thread console 오버라이드\n LOG_METHODS.forEach((method) => {\n const original = originalMethods[method];\n originalConsole[method] = ((...args: unknown[]) => {\n // 원본 console 호출\n original(...args);\n\n const serializedArgs = serializeArgs(args);\n const timestamp = Date.now();\n const id = generateLogId();\n\n sendLogToBackground({\n id,\n level: method,\n message: \"\",\n timestamp,\n args: serializedArgs,\n });\n }).bind(originalConsole);\n });\n\n lynxConsole.mainThreadInitialized = true;\n\n originalConsole.log(\"[LynxConsole] ✅ Main thread console initialized\");\n};\n\nexport default _setupMainThreadConsole;\n","// both thread;\nimport \"./_setupMainThreadConsole\";\n\nimport { runOnMainThread } from \"@lynx-js/react\";\nimport { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport _setupMainThreadConsole from \"./_setupMainThreadConsole\";\n\n// Main Thread: Console 초기화\n\nexport const initMainThreadConsole = async (): Promise<void> => {\n \"background only\";\n\n const { lynxConsole, state } = ensureConsoleStructure();\n\n if (lynxConsole.mainThreadInitialized) {\n console.error(\"[LynxConsole] Main thread console already initialized\");\n return;\n }\n\n if (!state.logs) {\n console.error(\"[LynxConsole] Background thread console not initialized\");\n return;\n }\n\n try {\n const setupOnMainThread = runOnMainThread(_setupMainThreadConsole);\n await setupOnMainThread();\n } catch (error) {\n console.error(\n \"[LynxConsole] Failed to initialize main thread console:\",\n error,\n );\n }\n};\n","import { stringify } from \"javascript-stringify\";\nimport { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { NetworkEntry } from \"../types\";\n\nconst generateNetworkId = (): string => {\n return `network-${Date.now()}-${Math.random()}`;\n};\n\nconst extractUrl = (input: RequestInfo | URL): string => {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.href;\n return (input as Request).url;\n};\n\nconst extractMethod = (\n input: RequestInfo | URL,\n init?: RequestInit,\n): string | undefined => {\n if (init?.method) return init.method;\n if (typeof input === \"object\" && \"method\" in input) {\n return (input as Request).method;\n }\n return \"GET\";\n};\n\nconst extractHeaders = (\n headers: HeadersInit | undefined,\n): Record<string, string> => {\n const result: Record<string, string> = {};\n if (!headers) return result;\n\n try {\n if (headers instanceof Headers) {\n headers.forEach((value, key) => {\n result[key] = value;\n });\n } else if (Array.isArray(headers)) {\n headers.forEach(([key, value]) => {\n result[key] = value;\n });\n } else {\n Object.entries(headers).forEach(([key, value]) => {\n result[key] = value;\n });\n }\n } catch (error) {\n console.error(\"[LynxConsole] Failed to extract headers:\", error);\n }\n\n return result;\n};\n\nconst mergeRequestHeaders = (\n input: RequestInfo | URL,\n init?: RequestInit,\n): Record<string, string> => {\n const merged: Record<string, string> = {};\n\n // Request 객체나 URL 객체에서 헤더 추출\n if (typeof input === \"object\" && \"headers\" in input) {\n Object.assign(merged, extractHeaders(input.headers as HeadersInit));\n }\n\n // RequestInit에서 헤더 추출 (나중 값이 우선)\n if (init?.headers) {\n Object.assign(merged, extractHeaders(init.headers));\n }\n\n return merged;\n};\n\nconst addNetworkEntry = (entry: NetworkEntry): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.networks || !state?.networksMap || !state?.networkListeners) {\n console.error(\n \"[LynxConsole] Cannot add network entry: Network monitor not initialized. Call initNetworkMonitor() first.\",\n );\n return;\n }\n\n state.networks.push(entry);\n state.networksMap.set(entry.id, entry);\n\n state.networkListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\nconst updateNetworkEntry = (\n id: string,\n updates: Partial<NetworkEntry>,\n): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.networks || !state?.networksMap || !state?.networkListeners) {\n console.error(\n \"[LynxConsole] Cannot update network entry: Network monitor not initialized. Call initNetworkMonitor() first.\",\n );\n return;\n }\n\n const existingEntry = state.networksMap.get(id);\n if (!existingEntry) {\n console.error(\n `[LynxConsole] Cannot update network entry: Entry with id '${id}' not found.`,\n );\n return;\n }\n\n const updatedEntry = { ...existingEntry, ...updates };\n\n const index = state.networks.findIndex((entry) => entry.id === id);\n if (index !== -1) {\n state.networks[index] = updatedEntry;\n }\n\n state.networksMap.set(id, updatedEntry);\n\n state.networkListeners.forEach((listener) => {\n listener(updatedEntry);\n });\n};\n\nexport const initNetworkMonitor = () => {\n if (!lynx.fetch) {\n console.warn(\n \"[LynxConsole] lynx.fetch not available, skipping network monitor\",\n );\n return;\n }\n\n const { state } = ensureConsoleStructure();\n\n if (state.networks !== undefined) {\n console.warn(\"[LynxConsole] Network monitor already initialized\");\n return;\n }\n\n type NetworkListener = (entry: NetworkEntry) => void;\n state.networks = [];\n state.networksMap = new Map();\n state.networkListeners = new Set();\n state.subscribeNetwork = (listener: NetworkListener) => {\n state.networkListeners?.add(listener);\n return () => {\n state.networkListeners?.delete(listener);\n };\n };\n\n const originalFetch = fetch.bind(lynx);\n\n const monitoredFetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const id = generateNetworkId();\n const startTime = Date.now();\n const url = extractUrl(input);\n const method = extractMethod(input, init);\n\n const requestHeaders = mergeRequestHeaders(input, init);\n\n // Request body 처리\n let requestBody: string | undefined;\n if (init?.body) {\n if (typeof init.body === \"string\") {\n requestBody = init.body;\n } else if (init.body instanceof URLSearchParams) {\n requestBody = init.body.toString();\n } else {\n // Lynx가 지원하지 않는 타입이거나 알 수 없는 타입\n requestBody = String(init.body);\n }\n }\n\n addNetworkEntry({\n id,\n url,\n method: method || \"default\",\n status: \"pending\",\n startTime,\n requestHeaders,\n requestBody: requestBody ?? \"\",\n });\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const clonedResponse = response.clone();\n let responseBody: string | undefined;\n\n try {\n const headerMap: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headerMap[key.toLowerCase()] = value;\n });\n\n const contentType = headerMap[\"content-type\"];\n if (contentType?.includes(\"application/json\")) {\n const json = await clonedResponse.json();\n responseBody = stringify(json, null, 2, { references: true }) ?? \"\";\n } else if (contentType?.includes(\"text\")) {\n responseBody = await clonedResponse.text();\n }\n } catch (error) {\n responseBody = `[Unable to read response body] ${error}`;\n console.error(\"[LynxConsole] Error reading response body:\", error);\n }\n\n updateNetworkEntry(id, {\n status: \"success\",\n statusCode: response.status,\n statusText: response.statusText,\n endTime,\n duration: endTime - startTime,\n responseHeaders,\n responseBody: responseBody ?? \"\",\n });\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n updateNetworkEntry(id, {\n status: \"error\",\n endTime,\n duration: endTime - startTime,\n error: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n };\n\n // biome-ignore lint/suspicious/noTsIgnore: to assign fetch to global functionfetch\n // @ts-ignore\n // biome-ignore lint/suspicious/noGlobalAssign: to assign fetch to global functionfetch fetch\n fetch = monitoredFetch as typeof fetch;\n\n //fetch 대신 lynx.fetch를 사용하는 경우에도 모니터링 되도록 설정\n lynx.fetch = monitoredFetch as typeof lynx.fetch;\n\n console.log(\"[LynxConsole] ✅ Network monitoring initialized\");\n};\n","import { ensureConsoleStructure } from \"../shared/ensureConsoleStructure\";\nimport type { PerformanceEntryData, PerformanceEntryType } from \"../types\";\n\ntype PerformanceListener = (entry: PerformanceEntryData) => void;\n\nconst generatePerformanceId = (): string => {\n return `performance-${Date.now()}-${Math.random()}`;\n};\n\nconst addPerformanceEntry = (entry: PerformanceEntryData): void => {\n const state = globalThis.__LYNX_CONSOLE__?.state;\n if (!state?.performances || !state?.performanceListeners) {\n console.error(\n \"[LynxConsole] Cannot add performance entry: Performance monitor not initialized. Call initPerformanceMonitor() first.\",\n );\n return;\n }\n\n state.performances.push(entry);\n state.performanceListeners.forEach((listener) => {\n listener(entry);\n });\n};\n\nexport const initPerformanceMonitor = () => {\n \"background only\";\n\n if (!lynx.performance) {\n console.warn(\n \"[LynxConsole] lynx.performance not available, skipping performance monitor\",\n );\n return;\n }\n\n const { state } = ensureConsoleStructure();\n\n if (state.performances !== undefined) {\n console.warn(\"[LynxConsole] Performance monitor already initialized\");\n return;\n }\n\n state.performances = [];\n state.performanceListeners = new Set();\n state.subscribePerformance = (listener: PerformanceListener) => {\n state.performanceListeners?.add(listener);\n return () => {\n state.performanceListeners?.delete(listener);\n };\n };\n\n const observer = lynx.performance.createObserver((entry) => {\n const performanceEntry: PerformanceEntryData = {\n id: generatePerformanceId(),\n entryType: entry.entryType as PerformanceEntryType,\n name: entry.name,\n timestamp: Date.now(),\n rawEntry: entry,\n };\n\n addPerformanceEntry(performanceEntry);\n });\n\n observer.observe([\n \"pipeline\", // LoadBundleEntry\n \"init\", // InitLynxviewEntry\n \"metric\", // MetricEntry\n ]);\n\n console.log(\"[LynxConsole] ✅ Performance monitoring initialized\");\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "lynx-console",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
- "**/*.css",
7
- "dist/index.css"
6
+ "**/*.css"
8
7
  ],
9
8
  "exports": {
10
9
  ".": {
@@ -16,8 +15,7 @@
16
15
  "types": "./dist/setup.d.mts",
17
16
  "import": "./dist/setup.mjs",
18
17
  "require": "./dist/setup.cjs"
19
- },
20
- "./style.css": "./dist/index.css"
18
+ }
21
19
  },
22
20
  "main": "dist/index.mjs",
23
21
  "types": "dist/index.d.mts",
@@ -38,11 +36,12 @@
38
36
  "typescript": "^5.9.3"
39
37
  },
40
38
  "peerDependencies": {
41
- "@lynx-js/react": "^0.116.0",
42
- "@lynx-js/types": "^3.7.0",
39
+ "@lynx-js/react": "^0.110.0",
40
+ "@lynx-js/types": "^3.6.0",
43
41
  "@types/react": "^18"
44
42
  },
45
43
  "dependencies": {
44
+ "devalue": "^5.6.4",
46
45
  "javascript-stringify": "^2.1.0"
47
46
  },
48
47
  "repository": {
@@ -182,7 +182,6 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
182
182
  ?.invoke({ method: "setValue", params: { value: "" } })
183
183
  .exec();
184
184
  runCode(trimmed);
185
- setTimeout(() => scrollToBottom(false), 100);
186
185
  };
187
186
 
188
187
  const renderArg = (
@@ -1,4 +1,8 @@
1
1
  import { runOnBackground } from "@lynx-js/react";
2
+ import { stringify as devalueStringify } from "devalue" with {
3
+ runtime: "shared",
4
+ };
5
+ import { parse as devalueParse } from "devalue";
2
6
  import type { LogEntry, LogLevel } from "../types";
3
7
 
4
8
  const _setupMainThreadConsole = (): void => {
@@ -16,13 +20,12 @@ const _setupMainThreadConsole = (): void => {
16
20
  const LOG_METHODS: LogLevel[] = ["log", "warn", "error", "info"];
17
21
  const LOG_ID_PREFIX = "main-thread";
18
22
 
19
- const serializeArgs = (args: unknown[]): unknown[] => {
23
+ const serializeArgs = (args: unknown[]): string[] => {
20
24
  return args.map((arg) => {
21
25
  try {
22
- JSON.stringify(arg);
23
- return arg;
26
+ return devalueStringify(arg);
24
27
  } catch {
25
- return String(arg);
28
+ return devalueStringify(String(arg));
26
29
  }
27
30
  });
28
31
  };
@@ -36,6 +39,16 @@ const _setupMainThreadConsole = (): void => {
36
39
  const state = globalThis.__LYNX_CONSOLE__?.state;
37
40
  if (!state) return;
38
41
 
42
+ // devalue 문자열을 원래 타입으로 복원
43
+ entry.args = entry.args.map((arg) => {
44
+ if (typeof arg !== "string") {
45
+ throw new Error(
46
+ `[LynxConsole] Expected devalue string, got ${typeof arg}`,
47
+ );
48
+ }
49
+ return devalueParse(arg);
50
+ });
51
+
39
52
  state.logs?.push(entry);
40
53
  state.logListeners?.forEach((listener) => {
41
54
  listener(entry);
@@ -1,80 +0,0 @@
1
- {
2
- "dimensions": {
3
- "$dimension.x0_5": "2px",
4
- "$dimension.x1": "4px",
5
- "$dimension.x1_5": "6px",
6
- "$dimension.x2": "8px",
7
- "$dimension.x2_5": "10px",
8
- "$dimension.x3": "12px",
9
- "$dimension.x3_5": "14px",
10
- "$dimension.x4": "16px",
11
- "$dimension.x4_5": "18px",
12
- "$dimension.x5": "20px",
13
- "$dimension.x6": "24px",
14
- "$dimension.x7": "28px",
15
- "$dimension.x8": "32px",
16
- "$dimension.x9": "36px",
17
- "$dimension.x10": "40px",
18
- "$dimension.x12": "48px",
19
- "$dimension.x13": "52px",
20
- "$dimension.x14": "56px",
21
- "$dimension.x16": "64px",
22
- "$dimension.spacing-x.between-chips": "8px",
23
- "$dimension.spacing-x.global-gutter": "16px",
24
- "$dimension.spacing-y.component-default": "12px",
25
- "$dimension.spacing-y.nav-to-title": "20px",
26
- "$dimension.spacing-y.screen-bottom": "56px",
27
- "$dimension.spacing-y.between-text": "6px",
28
- "$font-size.t1": "0.6875rem",
29
- "$font-size.t2": "0.75rem",
30
- "$font-size.t3": "0.8125rem",
31
- "$font-size.t4": "0.875rem",
32
- "$font-size.t5": "1rem",
33
- "$font-size.t6": "1.125rem",
34
- "$font-size.t7": "1.25rem",
35
- "$font-size.t8": "1.375rem",
36
- "$font-size.t9": "1.5rem",
37
- "$font-size.t10": "1.625rem",
38
- "$font-size.t1-static": "11px",
39
- "$font-size.t2-static": "12px",
40
- "$font-size.t3-static": "13px",
41
- "$font-size.t4-static": "14px",
42
- "$font-size.t5-static": "16px",
43
- "$font-size.t6-static": "18px",
44
- "$font-size.t7-static": "20px",
45
- "$font-size.t8-static": "22px",
46
- "$font-size.t9-static": "24px",
47
- "$font-size.t10-static": "26px",
48
- "$line-height.t1": "0.9375rem",
49
- "$line-height.t2": "1rem",
50
- "$line-height.t3": "1.125rem",
51
- "$line-height.t4": "1.1875rem",
52
- "$line-height.t5": "1.375rem",
53
- "$line-height.t6": "1.5rem",
54
- "$line-height.t7": "1.6875rem",
55
- "$line-height.t8": "1.875rem",
56
- "$line-height.t9": "2rem",
57
- "$line-height.t10": "2.1875rem",
58
- "$line-height.t1-static": "15px",
59
- "$line-height.t2-static": "16px",
60
- "$line-height.t3-static": "18px",
61
- "$line-height.t4-static": "19px",
62
- "$line-height.t5-static": "22px",
63
- "$line-height.t6-static": "24px",
64
- "$line-height.t7-static": "27px",
65
- "$line-height.t8-static": "30px",
66
- "$line-height.t9-static": "32px",
67
- "$line-height.t10-static": "35px",
68
- "$radius.r0_5": "2px",
69
- "$radius.r1": "4px",
70
- "$radius.r1_5": "6px",
71
- "$radius.r2": "8px",
72
- "$radius.r2_5": "10px",
73
- "$radius.r3": "12px",
74
- "$radius.r3_5": "14px",
75
- "$radius.r4": "16px",
76
- "$radius.r5": "20px",
77
- "$radius.r6": "24px",
78
- "$radius.full": "9999px"
79
- }
80
- }