lynx-console 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/index.cjs +205 -953
  2. package/dist/index.css +1248 -461
  3. package/dist/index.css.map +1 -1
  4. package/dist/index.d.cts +79 -1
  5. package/dist/index.d.cts.map +1 -1
  6. package/dist/index.d.mts +79 -1
  7. package/dist/index.d.mts.map +1 -1
  8. package/dist/index.mjs +205 -953
  9. package/dist/index.mjs.map +1 -1
  10. package/package.json +1 -4
  11. package/src/components/BottomSheet.css +91 -0
  12. package/src/components/BottomSheet.tsx +10 -10
  13. package/src/components/ConsolePanel.css +402 -0
  14. package/src/components/ConsolePanel.tsx +62 -28
  15. package/src/components/FadeList.tsx +31 -0
  16. package/{dist/assets/src/components/FloatingButton.css.ts.vanilla-BaG0OI6p.css → src/components/FloatingButton.css} +22 -16
  17. package/src/components/FloatingButton.tsx +22 -19
  18. package/src/components/LogPanel.tsx +61 -79
  19. package/src/components/NetworkDetailSection.tsx +13 -13
  20. package/src/components/NetworkPanel.css +280 -0
  21. package/src/components/NetworkPanel.tsx +32 -52
  22. package/src/components/PerformancePanel.css +249 -0
  23. package/src/components/PerformancePanel.tsx +42 -42
  24. package/src/components/Tabs.css +78 -0
  25. package/src/components/Tabs.tsx +13 -11
  26. package/src/globals.d.ts +0 -5
  27. package/src/hooks/useLongPressDrag.ts +3 -3
  28. package/src/index.tsx +9 -6
  29. package/src/styles/global.css +8 -0
  30. package/src/styles/vars/color.ts +26 -213
  31. package/src/styles/vars/dimension.ts +5 -74
  32. package/src/styles/vars/index.css +65 -457
  33. package/src/styles/vars/index.ts +5 -9
  34. package/src/styles/vars/radius.ts +1 -11
  35. package/src/types.ts +8 -0
  36. package/dist/assets/src/components/BottomSheet.css.ts.vanilla-CulwSDhG.css +0 -83
  37. package/dist/assets/src/components/ConsolePanel.css.ts.vanilla-DFvHPEyg.css +0 -340
  38. package/dist/assets/src/components/NetworkPanel.css.ts.vanilla-DFMduT0T.css +0 -247
  39. package/dist/assets/src/components/PerformancePanel.css.ts.vanilla-D35LuXlW.css +0 -216
  40. package/dist/assets/src/components/Tabs.css.ts.vanilla-DD7L2oXt.css +0 -50
  41. package/src/components/BottomSheet.css.ts +0 -93
  42. package/src/components/ConsolePanel.css.ts +0 -354
  43. package/src/components/FloatingButton.css.ts +0 -73
  44. package/src/components/NetworkPanel.css.ts +0 -280
  45. package/src/components/PerformancePanel.css.ts +0 -224
  46. package/src/components/Tabs.css.ts +0 -66
  47. package/src/styles/global.css.ts +0 -10
  48. package/src/styles/typography.ts +0 -25
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from "@lynx-js/react";
2
2
  import { useLongPressDrag } from "../hooks/useLongPressDrag";
3
- import * as css from "./FloatingButton.css";
3
+ import "./FloatingButton.css";
4
4
 
5
5
  interface FloatingButtonProps {
6
6
  bindtap: () => void;
@@ -45,26 +45,29 @@ export const FloatingButton = ({
45
45
  const isDragging = phase === "dragging";
46
46
 
47
47
  return (
48
- <view
49
- className={css.wrapper}
50
- style={{
51
- right: `${right}px`,
52
- bottom: `${bottom}px`,
53
- transform: isDragging ? "scale(1.05)" : "scale(1)",
54
- }}
55
- {...handlers}
56
- >
57
- <view className={css.button}>
58
- {children}
59
- <view className={css.shineOverlay} style={SHINE_STYLES[phase]} />
60
- </view>
48
+ <>
61
49
  <view
62
- className={css.reloadButton}
63
- catchtouchstart={() => clearTimer()}
64
- bindtap={handleReload}
50
+ className={"fb-wrapper"}
51
+ consume-slide-event={[[-180, 180]]}
52
+ style={{
53
+ right: `${right}px`,
54
+ bottom: `${bottom}px`,
55
+ transform: isDragging ? "scale(1.05)" : "scale(1)",
56
+ }}
57
+ {...handlers}
65
58
  >
66
- <text className={css.reloadIcon}>{"\u21BB"}</text>
59
+ <view className={"fb-button"}>
60
+ {children}
61
+ <view className={"fb-shineOverlay"} style={SHINE_STYLES[phase]} />
62
+ </view>
63
+ <view
64
+ className={"fb-reloadButton"}
65
+ catchtouchstart={() => clearTimer()}
66
+ bindtap={handleReload}
67
+ >
68
+ <text className={"fb-reloadIcon"}>{"\u21BB"}</text>
69
+ </view>
67
70
  </view>
68
- </view>
71
+ </>
69
72
  );
70
73
  };
@@ -2,8 +2,8 @@ import { useEffect, useMemo, useRef, useState } from "@lynx-js/react";
2
2
  import type { BaseEvent, InputInputEvent, NodesRef } from "@lynx-js/types";
3
3
  import { stringify } from "javascript-stringify";
4
4
  import type { LogEntry, LogLevel } from "../types";
5
- import { vars } from "../styles/vars";
6
- import * as css from "./ConsolePanel.css";
5
+ import "./ConsolePanel.css";
6
+ import { FadeList } from "./FadeList";
7
7
 
8
8
  const LOG_LEVELS: LogLevel[] = ["log", "info", "warn", "error"];
9
9
 
@@ -40,8 +40,6 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
40
40
  );
41
41
  const [filterOpen, setFilterOpen] = useState(false);
42
42
  const [searchQuery, setSearchQuery] = useState(savedSearchQuery);
43
- const [fadeState, setFadeState] = useState({ atTop: true, atBottom: true });
44
- const fadeRef = useRef({ atTop: true, atBottom: true });
45
43
  const inputRef = useRef<NodesRef>(null);
46
44
  const searchInputRef = useRef<NodesRef>(null);
47
45
  const listRef = useRef<NodesRef>(null);
@@ -141,11 +139,11 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
141
139
  const isExpanded = expandedArgs.has(key);
142
140
 
143
141
  if (arg === null) {
144
- return <text className={css.argNull}>null</text>;
142
+ return <text className={"cp-argNull"}>null</text>;
145
143
  }
146
144
 
147
145
  if (arg === undefined) {
148
- return <text className={css.argUndefined}>undefined</text>;
146
+ return <text className={"cp-argUndefined"}>undefined</text>;
149
147
  }
150
148
 
151
149
  if (typeof arg === "string") {
@@ -153,17 +151,16 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
153
151
  const shouldTruncate = arg.length > MAX_LENGTH;
154
152
 
155
153
  if (!shouldTruncate) {
156
- return <text className={css.argString({ level })}>{arg}</text>;
154
+ return <text className={`cp-argString cp-argString--${level}`}>{arg}</text>;
157
155
  }
158
156
 
159
- // 문자열이 길 경우 토글 버튼 추가
160
157
  return (
161
- <view className={css.argObject}>
162
- <view className={css.argObjectHeader} bindtap={() => toggleArg(key)}>
163
- <text className={css.toggleIndicator}>
158
+ <view className={"cp-argObject"}>
159
+ <view className={"cp-argObjectHeader"} bindtap={() => toggleArg(key)}>
160
+ <text className={"cp-toggleIndicator"}>
164
161
  {isExpanded ? "▼" : "▶"}
165
162
  </text>
166
- <text className={css.argString({ level })}>
163
+ <text className={`cp-argString cp-argString--${level}`}>
167
164
  {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}
168
165
  </text>
169
166
  </view>
@@ -172,7 +169,7 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
172
169
  }
173
170
 
174
171
  if (typeof arg === "number" || typeof arg === "boolean") {
175
- return <text className={css.argPrimitive({ level })}>{String(arg)}</text>;
172
+ return <text className={`cp-argPrimitive cp-argPrimitive--${level}`}>{String(arg)}</text>;
176
173
  }
177
174
 
178
175
  if (typeof arg === "object") {
@@ -208,50 +205,50 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
208
205
  }
209
206
 
210
207
  return (
211
- <view className={css.argObject}>
212
- <view className={css.argObjectHeader} bindtap={() => toggleArg(key)}>
213
- <text className={css.toggleIndicator}>
208
+ <view className={"cp-argObject"}>
209
+ <view className={"cp-argObjectHeader"} bindtap={() => toggleArg(key)}>
210
+ <text className={"cp-toggleIndicator"}>
214
211
  {isExpanded ? "▼" : "▶"}
215
212
  </text>
216
- <text className={css.argObjectPreview}>{preview}</text>
213
+ <text className={"cp-argObjectPreview"}>{preview}</text>
217
214
  </view>
218
215
  {isExpanded && (
219
- <view className={css.argObjectContent}>
220
- <text className={css.argObjectJson}>{jsonString}</text>
216
+ <view className={"cp-argObjectContent"}>
217
+ <text className={"cp-argObjectJson"}>{jsonString}</text>
221
218
  </view>
222
219
  )}
223
220
  </view>
224
221
  );
225
222
  }
226
223
 
227
- return <text className={css.argPrimitive({ level })}>{String(arg)}</text>;
224
+ return <text className={`cp-argPrimitive cp-argPrimitive--${level}`}>{String(arg)}</text>;
228
225
  };
229
226
 
230
227
  return (
231
228
  <view
232
- className={css.logContainer}
229
+ className={"cp-logContainer"}
233
230
  bindtap={() => { if (filterOpen) setFilterOpen(false); }}
234
231
  >
235
- <view className={css.logHeader}>
236
- <view className={css.filterWrapper}>
232
+ <view className={"cp-logHeader"}>
233
+ <view className={"cp-filterWrapper"}>
237
234
  <view
238
- className={css.filterButton}
235
+ className={"cp-filterButton"}
239
236
  catchtap={() => setFilterOpen((v) => !v)}
240
237
  >
241
- <text className={css.filterButtonText}>Filter ▼</text>
238
+ <text className={"cp-filterButtonText"}>Filter ▼</text>
242
239
  </view>
243
240
  {filterOpen && (
244
- <view className={css.filterDropdown} catchtap={() => {}}>
241
+ <view className={"cp-filterDropdown"} catchtap={() => {}}>
245
242
  {LOG_LEVELS.map((level) => (
246
243
  <view
247
244
  key={level}
248
- className={css.filterOption}
245
+ className={"cp-filterOption"}
249
246
  bindtap={() => toggleLevel(level)}
250
247
  >
251
- <text className={css.filterCheckbox({ level })}>
248
+ <text className={`cp-filterCheckbox cp-filterCheckbox--${level}`}>
252
249
  {enabledLevels.has(level) ? "✅" : "⬜"}
253
250
  </text>
254
- <text className={css.filterLabel({ level })}>
251
+ <text className={`cp-filterLabel cp-filterLabel--${level}`}>
255
252
  {level.toUpperCase()}
256
253
  </text>
257
254
  </view>
@@ -259,53 +256,46 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
259
256
  </view>
260
257
  )}
261
258
  </view>
262
- <view className={css.searchWrapper}>
263
- <text className={css.searchPrompt}>{"›"}</text>
259
+ <view className={"cp-searchWrapper"}>
260
+ <text className={"cp-searchPrompt"}>{"›"}</text>
264
261
  <input
265
262
  ref={searchInputRef}
266
- className={css.searchInput}
263
+ className={"cp-searchInput"}
267
264
  placeholder="Search logs..."
268
265
  bindinput={(e: BaseEvent<"bindinput", InputInputEvent>) =>
269
266
  setSearchQuery(e.detail.value)
270
267
  }
271
268
  />
269
+ {searchQuery.length > 0 && (
270
+ <view
271
+ className={"cp-searchClear"}
272
+ bindtap={() => {
273
+ setSearchQuery("");
274
+ searchInputRef.current
275
+ ?.invoke({ method: "setValue", params: { value: "" } })
276
+ .exec();
277
+ }}
278
+ >
279
+ <text className={"cp-searchClearText"}>✕</text>
280
+ </view>
281
+ )}
272
282
  </view>
273
283
  <view style={{ display: "flex", flexDirection: "row", gap: 8 }}>
274
- <view className={css.clearButton} bindtap={clearLogs}>
275
- <text className={css.clearButtonText}>Clear</text>
284
+ <view className={"cp-clearButton"} bindtap={clearLogs}>
285
+ <text className={"cp-clearButtonText"}>🗑</text>
276
286
  </view>
277
287
  </view>
278
288
  </view>
279
- <view
280
- className={css.fadeTop}
281
- style={{
282
- background: fadeState.atTop
283
- ? `linear-gradient(to bottom, #ffffff00, #ffffff00)`
284
- : `linear-gradient(to bottom, ${vars.$color.bg.layerDefault}, #ffffff00)`,
285
- }}
286
- />
287
- <list
288
- ref={listRef}
289
- scroll-orientation="vertical"
290
- className={css.logList}
289
+ <FadeList
290
+ listRef={listRef}
291
+ className={"cp-logList"}
291
292
  preload-buffer-count={10}
292
293
  initial-scroll-index={Math.max(0, filteredLogs.length - 1)}
293
- scroll-event-throttle={16}
294
- bindscroll={(e: BaseEvent<"bindscroll", { scrollTop: number; scrollHeight: number; listHeight: number }>) => {
295
- const { scrollTop, scrollHeight, listHeight } = e.detail;
296
- const atTop = scrollTop <= 10;
297
- const atBottom = scrollTop + listHeight >= scrollHeight - 10;
298
- if (atTop !== fadeRef.current.atTop || atBottom !== fadeRef.current.atBottom) {
299
- fadeRef.current.atTop = atTop;
300
- fadeRef.current.atBottom = atBottom;
301
- setFadeState({ atTop, atBottom });
302
- }
303
- }}
304
294
  >
305
295
  {filteredLogs.length === 0 ? (
306
296
  <list-item item-key="empty-state">
307
- <view className={css.placeholder}>
308
- <text className={css.placeholderText}>
297
+ <view className={"cp-placeholder"}>
298
+ <text className={"cp-placeholderText"}>
309
299
  No logs yet. Try console.log("Hello!")
310
300
  </text>
311
301
  </view>
@@ -314,20 +304,20 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
314
304
  filteredLogs.map((log) => {
315
305
  return (
316
306
  <list-item key={log.id} item-key={log.id}>
317
- <view className={css.logItem({ level: log.level })}>
318
- <view className={css.logItemHeader}>
319
- <text className={css.logLevel({ level: log.level })}>
307
+ <view className={`cp-logItem cp-logItem--${log.level}`}>
308
+ <view className={"cp-logItemHeader"}>
309
+ <text className={`cp-logLevel cp-logLevel--${log.level}`}>
320
310
  {log.level.toUpperCase()}
321
311
  </text>
322
- <text className={css.logTime}>
312
+ <text className={"cp-logTime"}>
323
313
  {new Date(log.timestamp).toISOString()}
324
314
  </text>
325
315
  </view>
326
- <view className={css.logArgsContainer}>
316
+ <view className={"cp-logArgsContainer"}>
327
317
  {log.args.map((arg, index) => (
328
318
  <view
329
319
  key={`${log.id}-${index.toString()}`}
330
- className={css.logArgItem}
320
+ className={"cp-logArgItem"}
331
321
  >
332
322
  {renderArg(
333
323
  arg,
@@ -342,28 +332,20 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
342
332
  );
343
333
  })
344
334
  )}
345
- </list>
346
- <view
347
- className={css.fadeBottom}
348
- style={{
349
- background: fadeState.atBottom
350
- ? `linear-gradient(to top, #ffffff00, #ffffff00)`
351
- : `linear-gradient(to top, ${vars.$color.bg.layerDefault}, #ffffff00)`,
352
- }}
353
- />
354
- <view className={css.replInputRow}>
355
- <text className={css.replPrompt}>{"›"}</text>
335
+ </FadeList>
336
+ <view className={"cp-replInputRow"}>
337
+ <text className={"cp-replPrompt"}>{"›"}</text>
356
338
  <input
357
339
  ref={inputRef}
358
- className={css.replInput}
340
+ className={"cp-replInput"}
359
341
  placeholder="enter code..."
360
342
  bindinput={(e: BaseEvent<"bindinput", InputInputEvent>) =>
361
343
  setCode(e.detail.value)
362
344
  }
363
345
  bindconfirm={handleRun}
364
346
  />
365
- <view className={css.replRunButton} bindtap={handleRun}>
366
- <text className={css.replRunButtonText}>Run</text>
347
+ <view className={"cp-replRunButton"} bindtap={handleRun}>
348
+ <text className={"cp-replRunButtonText"}>Run</text>
367
349
  </view>
368
350
  </view>
369
351
  </view>
@@ -1,4 +1,4 @@
1
- import * as css from "./NetworkPanel.css";
1
+ import "./NetworkPanel.css";
2
2
 
3
3
  interface NetworkDetailSectionProps {
4
4
  headers?: Record<string, string> | undefined;
@@ -14,28 +14,28 @@ export const NetworkDetailSection = ({
14
14
  return (
15
15
  <>
16
16
  {/* Headers */}
17
- <view className={css.detailSection}>
18
- <text className={css.detailSectionTitle}>Headers</text>
17
+ <view className={"np-detailSection"}>
18
+ <text className={"np-detailSectionTitle"}>Headers</text>
19
19
  {headers && Object.keys(headers).length > 0 ? (
20
- <view className={css.table}>
20
+ <view className={"np-table"}>
21
21
  {Object.entries(headers).map(([key, value]) => (
22
- <view key={key} className={css.tableRow}>
23
- <text className={css.tableKey}>{key}</text>
24
- <text className={css.tableValue}>{value}</text>
22
+ <view key={key} className={"np-tableRow"}>
23
+ <text className={"np-tableKey"}>{key}</text>
24
+ <text className={"np-tableValue"}>{value}</text>
25
25
  </view>
26
26
  ))}
27
27
  </view>
28
28
  ) : (
29
- <text className={css.emptyText}>No headers</text>
29
+ <text className={"np-emptyText"}>No headers</text>
30
30
  )}
31
31
  </view>
32
32
 
33
33
  {/* Body */}
34
- <view className={css.detailSection}>
35
- <text className={css.detailSectionTitle}>Body</text>
36
- {error && <text className={css.errorText}>{error}</text>}
37
- {body && <text className={css.bodyText}>{body}</text>}
38
- {!error && !body && <text className={css.emptyText}>No body</text>}
34
+ <view className={"np-detailSection"}>
35
+ <text className={"np-detailSectionTitle"}>Body</text>
36
+ {error && <text className={"np-errorText"}>{error}</text>}
37
+ {body && <text className={"np-bodyText"}>{body}</text>}
38
+ {!error && !body && <text className={"np-emptyText"}>No body</text>}
39
39
  </view>
40
40
  </>
41
41
  );
@@ -0,0 +1,280 @@
1
+ .np-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ flex: 1;
5
+ padding-top: 4px;
6
+ }
7
+
8
+ .np-header {
9
+ display: flex;
10
+ flex-direction: row;
11
+ align-items: center;
12
+ justify-content: space-between;
13
+ margin-bottom: 8px;
14
+ padding-bottom: 4px;
15
+ }
16
+
17
+ .np-count {
18
+ font-size: 0.8125rem;
19
+ line-height: 1.125rem;
20
+ font-weight: var(--lynx-console-font-weight-regular);
21
+ color: var(--lynx-console-color-fg-neutral-subtle);
22
+ }
23
+
24
+ .np-clearButton {
25
+ padding: 3px 6px;
26
+ background-color: var(--lynx-console-color-bg-neutral-weak);
27
+ border-radius: 4px;
28
+ }
29
+
30
+ .np-clearButtonText {
31
+ font-size: 0.8125rem;
32
+ line-height: 1.125rem;
33
+ font-weight: var(--lynx-console-font-weight-medium);
34
+ color: var(--lynx-console-color-fg-neutral-muted);
35
+ }
36
+
37
+ .np-list {
38
+ flex: 1;
39
+ }
40
+
41
+ .np-placeholder {
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ height: 100%;
46
+ }
47
+
48
+ .np-placeholderText {
49
+ font-size: 0.875rem;
50
+ line-height: 1.1875rem;
51
+ font-weight: var(--lynx-console-font-weight-regular);
52
+ color: var(--lynx-console-color-fg-disabled);
53
+ }
54
+
55
+ .np-item {
56
+ padding: 8px;
57
+ border-bottom-width: 1px;
58
+ border-bottom-color: var(--lynx-console-color-stroke-neutral-weak);
59
+ border-bottom-style: solid;
60
+ }
61
+
62
+ .np-item--pending {
63
+ background-color: var(--lynx-console-color-palette-gray-100);
64
+ }
65
+
66
+ .np-item--error {
67
+ background-color: var(--lynx-console-color-palette-red-100);
68
+ }
69
+
70
+ .np-itemHeader {
71
+ display: flex;
72
+ flex-direction: row;
73
+ align-items: center;
74
+ margin-bottom: 4px;
75
+ gap: 8px;
76
+ }
77
+
78
+ .np-method {
79
+ font-size: 0.75rem;
80
+ line-height: 1rem;
81
+ font-weight: var(--lynx-console-font-weight-bold);
82
+ padding: 0 4px;
83
+ border-radius: 2px;
84
+ color: var(--lynx-console-color-fg-neutral);
85
+ background-color: var(--lynx-console-color-bg-neutral-weak);
86
+ }
87
+
88
+ .np-method--GET {
89
+ color: var(--lynx-console-color-palette-blue-600);
90
+ background-color: var(--lynx-console-color-palette-blue-100);
91
+ }
92
+
93
+ .np-method--POST {
94
+ color: var(--lynx-console-color-palette-green-600);
95
+ background-color: var(--lynx-console-color-palette-green-100);
96
+ }
97
+
98
+ .np-method--PUT {
99
+ color: var(--lynx-console-color-palette-yellow-600);
100
+ background-color: var(--lynx-console-color-palette-yellow-100);
101
+ }
102
+
103
+ .np-method--PATCH {
104
+ color: var(--lynx-console-color-palette-purple-600);
105
+ background-color: var(--lynx-console-color-palette-purple-100);
106
+ }
107
+
108
+ .np-method--DELETE {
109
+ color: var(--lynx-console-color-palette-red-600);
110
+ background-color: var(--lynx-console-color-palette-red-100);
111
+ }
112
+
113
+ .np-statusCode {
114
+ font-size: 0.75rem;
115
+ line-height: 1rem;
116
+ font-weight: var(--lynx-console-font-weight-bold);
117
+ }
118
+
119
+ .np-statusCode--success {
120
+ color: var(--lynx-console-color-palette-green-600);
121
+ }
122
+
123
+ .np-statusCode--error {
124
+ color: var(--lynx-console-color-palette-red-600);
125
+ }
126
+
127
+ .np-statusCode--pending {
128
+ color: var(--lynx-console-color-fg-neutral-subtle);
129
+ }
130
+
131
+ .np-time {
132
+ font-size: 0.75rem;
133
+ line-height: 1rem;
134
+ font-weight: var(--lynx-console-font-weight-regular);
135
+ color: var(--lynx-console-color-fg-neutral-subtle);
136
+ }
137
+
138
+ .np-url {
139
+ font-size: 0.8125rem;
140
+ line-height: 1.125rem;
141
+ font-weight: var(--lynx-console-font-weight-regular);
142
+ color: var(--lynx-console-color-fg-neutral);
143
+ word-break: break-all;
144
+ margin-bottom: 4px;
145
+ }
146
+
147
+ .np-path {
148
+ font-size: 0.8125rem;
149
+ line-height: 1.125rem;
150
+ font-weight: var(--lynx-console-font-weight-regular);
151
+ color: var(--lynx-console-color-fg-neutral);
152
+ word-break: break-all;
153
+ white-space: pre-wrap;
154
+ overflow: visible;
155
+ margin-bottom: 4px;
156
+ }
157
+
158
+ .np-details {
159
+ font-size: 0.8125rem;
160
+ line-height: 1.125rem;
161
+ font-weight: var(--lynx-console-font-weight-regular);
162
+ color: var(--lynx-console-color-fg-neutral-subtle);
163
+ }
164
+
165
+ .np-detailsContainer {
166
+ margin-top: 12px;
167
+ padding-top: 12px;
168
+ border-top-width: 1px;
169
+ border-top-color: var(--lynx-console-color-stroke-neutral-subtle);
170
+ border-top-style: solid;
171
+ }
172
+
173
+ .np-tabs {
174
+ display: flex;
175
+ flex-direction: row;
176
+ gap: 4px;
177
+ padding-bottom: 6px;
178
+ }
179
+
180
+ .np-tab {
181
+ padding: 4px 8px;
182
+ border-radius: 4px;
183
+ cursor: pointer;
184
+ }
185
+
186
+ .np-tab--active {
187
+ background-color: var(--lynx-console-color-bg-neutral-weak);
188
+ }
189
+
190
+ .np-tabText {
191
+ font-size: 0.875rem;
192
+ line-height: 1.1875rem;
193
+ font-weight: var(--lynx-console-font-weight-medium);
194
+ color: var(--lynx-console-color-fg-neutral-subtle);
195
+ }
196
+
197
+ .np-tabText--active {
198
+ color: var(--lynx-console-color-fg-neutral);
199
+ }
200
+
201
+ .np-tabContent {
202
+ padding-top: 8px;
203
+ }
204
+
205
+ .np-detailSection {
206
+ margin-bottom: 12px;
207
+ }
208
+
209
+ .np-detailSectionTitle {
210
+ font-size: 0.8125rem;
211
+ line-height: 1.125rem;
212
+ font-weight: var(--lynx-console-font-weight-bold);
213
+ color: var(--lynx-console-color-fg-neutral);
214
+ margin-bottom: 8px;
215
+ }
216
+
217
+ .np-table {
218
+ display: flex;
219
+ flex-direction: column;
220
+ gap: 4px;
221
+ }
222
+
223
+ .np-tableRow {
224
+ display: flex;
225
+ flex-direction: row;
226
+ gap: 8px;
227
+ padding: 4px 8px;
228
+ background-color: var(--lynx-console-color-bg-neutral-weak);
229
+ border-radius: 2px;
230
+ }
231
+
232
+ .np-tableKey {
233
+ font-size: 0.8125rem;
234
+ line-height: 1.125rem;
235
+ font-weight: var(--lynx-console-font-weight-bold);
236
+ color: var(--lynx-console-color-fg-neutral-subtle);
237
+ min-width: 70px;
238
+ flex-shrink: 0;
239
+ }
240
+
241
+ .np-tableValue {
242
+ font-size: 0.8125rem;
243
+ line-height: 1.125rem;
244
+ font-weight: var(--lynx-console-font-weight-regular);
245
+ color: var(--lynx-console-color-fg-neutral);
246
+ word-break: break-all;
247
+ flex: 1;
248
+ }
249
+
250
+ .np-bodyText {
251
+ font-size: 0.8125rem;
252
+ line-height: 1.125rem;
253
+ font-weight: var(--lynx-console-font-weight-regular);
254
+ color: var(--lynx-console-color-fg-neutral);
255
+ padding: 8px;
256
+ background-color: var(--lynx-console-color-bg-neutral-weak);
257
+ border-radius: 4px;
258
+ word-break: break-all;
259
+ white-space: pre-wrap;
260
+ }
261
+
262
+ .np-errorText {
263
+ font-size: 0.8125rem;
264
+ line-height: 1.125rem;
265
+ font-weight: var(--lynx-console-font-weight-regular);
266
+ color: var(--lynx-console-color-palette-red-600);
267
+ padding: 8px;
268
+ background-color: var(--lynx-console-color-palette-red-100);
269
+ border-radius: 4px;
270
+ word-break: break-all;
271
+ }
272
+
273
+ .np-emptyText {
274
+ font-size: 0.8125rem;
275
+ line-height: 1.125rem;
276
+ font-weight: var(--lynx-console-font-weight-regular);
277
+ color: var(--lynx-console-color-fg-disabled);
278
+ text-align: center;
279
+ padding: 16px 0;
280
+ }