lynx-console 0.1.0 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lynx-console",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css",
@@ -36,13 +36,6 @@ export const logHeader = style({
36
36
  paddingBottom: 3,
37
37
  });
38
38
 
39
- export const fadeTop = style({
40
- height: 20,
41
- marginBottom: -20,
42
- zIndex: 1,
43
- background: `linear-gradient(to bottom, ${vars.$color.bg.layerDefault}, #ffffff00)`,
44
- });
45
-
46
39
  export const filterWrapper = style({
47
40
  position: "relative",
48
41
  });
@@ -58,7 +51,7 @@ export const filterButton = style({
58
51
 
59
52
  export const filterButtonText = style({
60
53
  ...typography("t3", "medium"),
61
- color: vars.$color.fg.neutral,
54
+ color: vars.$color.fg.neutralMuted,
62
55
  });
63
56
 
64
57
  export const filterDropdown = style({
@@ -66,7 +59,7 @@ export const filterDropdown = style({
66
59
  top: "100%",
67
60
  left: 0,
68
61
  marginTop: 4,
69
- backgroundColor: vars.$color.bg.layerDefault,
62
+ backgroundColor: vars.$color.bg.layerFloating,
70
63
  borderWidth: 1,
71
64
  borderColor: vars.$color.stroke.neutralSubtle,
72
65
  borderStyle: "solid",
@@ -138,6 +131,15 @@ export const searchInput = style({
138
131
  caretColor: vars.$color.palette.green600,
139
132
  });
140
133
 
134
+ export const searchClear = style({
135
+ padding: "2px 4px",
136
+ });
137
+
138
+ export const searchClearText = style({
139
+ ...typography("t3", "medium"),
140
+ color: vars.$color.fg.placeholder,
141
+ });
142
+
141
143
  export const clearButton = style({
142
144
  padding: "3px 6px",
143
145
  backgroundColor: vars.$color.bg.neutralWeak,
@@ -146,7 +148,7 @@ export const clearButton = style({
146
148
 
147
149
  export const clearButtonText = style({
148
150
  ...typography("t3", "medium"),
149
- color: vars.$color.fg.neutral,
151
+ color: vars.$color.fg.neutralMuted,
150
152
  });
151
153
 
152
154
  export const logList = style({
@@ -311,13 +313,6 @@ export const argObjectJson = style({
311
313
  color: vars.$color.fg.neutral,
312
314
  });
313
315
 
314
- export const fadeBottom = style({
315
- height: 20,
316
- marginTop: -20,
317
- zIndex: 1,
318
- background: `linear-gradient(to top, ${vars.$color.bg.layerDefault}, #ffffff00)`,
319
- });
320
-
321
316
  export const replInputRow = style({
322
317
  display: "flex",
323
318
  flexDirection: "row",
@@ -0,0 +1,16 @@
1
+ import { style } from "@vanilla-extract/css";
2
+ import { vars } from "../styles/vars";
3
+
4
+ export const fadeTop = style({
5
+ height: 20,
6
+ marginBottom: -20,
7
+ zIndex: 1,
8
+ background: `linear-gradient(to bottom, ${vars.$color.bg.layerFloating}, #ffffff00)`,
9
+ });
10
+
11
+ export const fadeBottom = style({
12
+ height: 20,
13
+ marginTop: -20,
14
+ zIndex: 1,
15
+ background: `linear-gradient(to top, ${vars.$color.bg.layerFloating}, #ffffff00)`,
16
+ });
@@ -0,0 +1,76 @@
1
+ import { useRef, useState } from "@lynx-js/react";
2
+ import type { BaseEvent, NodesRef } from "@lynx-js/types";
3
+ import { vars } from "../styles/vars";
4
+ import * as css from "./FadeList.css";
5
+
6
+ interface FadeListProps {
7
+ className?: string;
8
+ listRef?: React.RefObject<NodesRef>;
9
+ children: React.ReactNode;
10
+ "preload-buffer-count"?: number;
11
+ "initial-scroll-index"?: number;
12
+ }
13
+
14
+ export const FadeList = ({
15
+ className,
16
+ listRef: externalListRef,
17
+ children,
18
+ ...listProps
19
+ }: FadeListProps) => {
20
+ const [fadeState, setFadeState] = useState({ atTop: true, atBottom: true });
21
+ const fadeRef = useRef({ atTop: true, atBottom: true });
22
+ const internalListRef = useRef<NodesRef>(null);
23
+ const listRef = externalListRef ?? internalListRef;
24
+
25
+ return (
26
+ <>
27
+ <view
28
+ className={css.fadeTop}
29
+ style={{
30
+ background: fadeState.atTop
31
+ ? "linear-gradient(to bottom, #ffffff00, #ffffff00)"
32
+ : `linear-gradient(to bottom, ${vars.$color.bg.layerFloating}, #ffffff00)`,
33
+ }}
34
+ />
35
+ <list
36
+ ref={listRef}
37
+ scroll-orientation="vertical"
38
+ className={className}
39
+ scroll-event-throttle={16}
40
+ bindscroll={(
41
+ e: BaseEvent<
42
+ "bindscroll",
43
+ {
44
+ scrollTop: number;
45
+ scrollHeight: number;
46
+ listHeight: number;
47
+ }
48
+ >,
49
+ ) => {
50
+ const { scrollTop, scrollHeight, listHeight } = e.detail;
51
+ const atTop = scrollTop <= 10;
52
+ const atBottom = scrollTop + listHeight >= scrollHeight - 10;
53
+ if (
54
+ atTop !== fadeRef.current.atTop ||
55
+ atBottom !== fadeRef.current.atBottom
56
+ ) {
57
+ fadeRef.current.atTop = atTop;
58
+ fadeRef.current.atBottom = atBottom;
59
+ setFadeState({ atTop, atBottom });
60
+ }
61
+ }}
62
+ {...listProps}
63
+ >
64
+ {children}
65
+ </list>
66
+ <view
67
+ className={css.fadeBottom}
68
+ style={{
69
+ background: fadeState.atBottom
70
+ ? "linear-gradient(to top, #ffffff00, #ffffff00)"
71
+ : `linear-gradient(to top, ${vars.$color.bg.layerFloating}, #ffffff00)`,
72
+ }}
73
+ />
74
+ </>
75
+ );
76
+ };
@@ -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={css.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={css.button}>
60
+ {children}
61
+ <view className={css.shineOverlay} style={SHINE_STYLES[phase]} />
62
+ </view>
63
+ <view
64
+ className={css.reloadButton}
65
+ catchtouchstart={() => clearTimer()}
66
+ bindtap={handleReload}
67
+ >
68
+ <text className={css.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
5
  import * as css from "./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);
@@ -269,38 +267,31 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
269
267
  setSearchQuery(e.detail.value)
270
268
  }
271
269
  />
270
+ {searchQuery.length > 0 && (
271
+ <view
272
+ className={css.searchClear}
273
+ bindtap={() => {
274
+ setSearchQuery("");
275
+ searchInputRef.current
276
+ ?.invoke({ method: "setValue", params: { value: "" } })
277
+ .exec();
278
+ }}
279
+ >
280
+ <text className={css.searchClearText}>✕</text>
281
+ </view>
282
+ )}
272
283
  </view>
273
284
  <view style={{ display: "flex", flexDirection: "row", gap: 8 }}>
274
285
  <view className={css.clearButton} bindtap={clearLogs}>
275
- <text className={css.clearButtonText}>Clear</text>
286
+ <text className={css.clearButtonText}>🗑</text>
276
287
  </view>
277
288
  </view>
278
289
  </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
+ <FadeList
291
+ listRef={listRef}
290
292
  className={css.logList}
291
293
  preload-buffer-count={10}
292
294
  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
295
  >
305
296
  {filteredLogs.length === 0 ? (
306
297
  <list-item item-key="empty-state">
@@ -342,15 +333,7 @@ export const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {
342
333
  );
343
334
  })
344
335
  )}
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
- />
336
+ </FadeList>
354
337
  <view className={css.replInputRow}>
355
338
  <text className={css.replPrompt}>{"›"}</text>
356
339
  <input
@@ -17,9 +17,6 @@ export const header = style({
17
17
  justifyContent: "space-between",
18
18
  marginBottom: 8,
19
19
  paddingBottom: 4,
20
- borderBottomWidth: 1,
21
- borderBottomColor: vars.$color.stroke.neutralSubtle,
22
- borderBottomStyle: "solid",
23
20
  });
24
21
 
25
22
  export const count = style({
@@ -28,14 +25,14 @@ export const count = style({
28
25
  });
29
26
 
30
27
  export const clearButton = style({
31
- padding: "6px 12px",
28
+ padding: "3px 6px",
32
29
  backgroundColor: vars.$color.bg.neutralWeak,
33
30
  borderRadius: 4,
34
31
  });
35
32
 
36
33
  export const clearButtonText = style({
37
34
  ...typography("t3", "medium"),
38
- color: vars.$color.fg.neutral,
35
+ color: vars.$color.fg.neutralMuted,
39
36
  });
40
37
 
41
38
  export const list = style({
@@ -1,5 +1,6 @@
1
1
  import { useState } from "@lynx-js/react";
2
2
  import type { NetworkEntry } from "../types";
3
+ import { FadeList } from "./FadeList";
3
4
  import { NetworkDetailSection } from "./NetworkDetailSection";
4
5
  import * as css from "./NetworkPanel.css";
5
6
 
@@ -16,7 +17,6 @@ export const NetworkPanel = ({
16
17
  }: NetworkPanelProps) => {
17
18
  const [selectedId, setSelectedId] = useState<string | null>(null);
18
19
  const [activeTab, setActiveTab] = useState<TabType>("general");
19
-
20
20
  const formatDuration = (duration?: number): string => {
21
21
  if (!duration) return "-";
22
22
  if (duration < 1000) return `${duration}ms`;
@@ -71,7 +71,7 @@ export const NetworkPanel = ({
71
71
  <view className={css.header}>
72
72
  <text className={css.count}>Total: {networks.length} requests</text>
73
73
  <view className={css.clearButton} bindtap={clearNetworks}>
74
- <text className={css.clearButtonText}>Clear</text>
74
+ <text className={css.clearButtonText}>🗑</text>
75
75
  </view>
76
76
  </view>
77
77
 
@@ -80,7 +80,7 @@ export const NetworkPanel = ({
80
80
  <text className={css.placeholderText}>No network requests yet</text>
81
81
  </view>
82
82
  ) : (
83
- <list className={css.list}>
83
+ <FadeList className={css.list}>
84
84
  {networks.map((network) => (
85
85
  <list-item key={network.id} item-key={network.id}>
86
86
  <view className={css.item({ status: network.status })}>
@@ -215,7 +215,7 @@ export const NetworkPanel = ({
215
215
  </view>
216
216
  </list-item>
217
217
  ))}
218
- </list>
218
+ </FadeList>
219
219
  )}
220
220
  </view>
221
221
  );
@@ -28,14 +28,14 @@ export const count = style({
28
28
  });
29
29
 
30
30
  export const clearButton = style({
31
- padding: "6px 12px",
31
+ padding: "3px 6px",
32
32
  backgroundColor: vars.$color.bg.neutralWeak,
33
33
  borderRadius: 4,
34
34
  });
35
35
 
36
36
  export const clearButtonText = style({
37
37
  ...typography("t3", "medium"),
38
- color: vars.$color.fg.neutral,
38
+ color: vars.$color.fg.neutralMuted,
39
39
  });
40
40
 
41
41
  export const list = style({
@@ -1,6 +1,7 @@
1
1
  import { useState } from "@lynx-js/react";
2
2
  import { stringify } from "javascript-stringify";
3
3
  import type { PerformanceEntryData } from "../types";
4
+ import { FadeList } from "./FadeList";
4
5
  import * as css from "./PerformancePanel.css";
5
6
 
6
7
  interface PerformancePanelProps {
@@ -65,7 +66,6 @@ export const PerformancePanel = ({
65
66
  clearPerformances,
66
67
  }: PerformancePanelProps) => {
67
68
  const [selectedId, setSelectedId] = useState<string | null>(null);
68
-
69
69
  if (performances.length === 0) {
70
70
  return (
71
71
  <view className={css.container}>
@@ -80,7 +80,7 @@ export const PerformancePanel = ({
80
80
  <text>Log</text>
81
81
  </view>
82
82
  <view bindtap={clearPerformances} className={css.clearButton}>
83
- <text className={css.clearButtonText}>Clear</text>
83
+ <text className={css.clearButtonText}>🗑</text>
84
84
  </view>
85
85
  </view>
86
86
  <view className={css.placeholder}>
@@ -97,11 +97,11 @@ export const PerformancePanel = ({
97
97
  <view className={css.header}>
98
98
  <text className={css.count}>{performances.length} entries</text>
99
99
  <view bindtap={clearPerformances} className={css.clearButton}>
100
- <text className={css.clearButtonText}>Clear</text>
100
+ <text className={css.clearButtonText}>🗑</text>
101
101
  </view>
102
102
  </view>
103
103
 
104
- <list className={css.list}>
104
+ <FadeList className={css.list}>
105
105
  {performances.map((perf) => {
106
106
  const isMetricFcp = isMetricFcpEntry(perf);
107
107
  const fcpMetrics = extractFcpMetrics(perf);
@@ -203,7 +203,7 @@ export const PerformancePanel = ({
203
203
  </list-item>
204
204
  );
205
205
  })}
206
- </list>
206
+ </FadeList>
207
207
  </view>
208
208
  );
209
209
  };
@@ -87,9 +87,9 @@ export function useLongPressDrag(onTap: () => void) {
87
87
  bottom: isDragging ? tempBottom : bottom,
88
88
  clearTimer,
89
89
  handlers: {
90
- bindtouchstart: handleTouchStart,
91
- bindtouchmove: handleTouchMove,
92
- bindtouchend: handleTouchEnd,
90
+ catchtouchstart: handleTouchStart,
91
+ catchtouchmove: handleTouchMove,
92
+ catchtouchend: handleTouchEnd,
93
93
  },
94
94
  };
95
95
  }