@buoy-gg/console 7.0.29 → 7.0.30
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 +5 -6
- package/src/components/ConsoleIcon.tsx +0 -9
- package/src/components/ConsoleLogList.tsx +0 -135
- package/src/components/ConsoleModal.tsx +0 -48
- package/src/components/ConsoleRoot.tsx +0 -25
- package/src/devtools/ConsoleBody.tsx +0 -228
- package/src/devtools/ConsoleHeader.tsx +0 -149
- package/src/devtools/ConsoleMessageRow.tsx +0 -388
- package/src/devtools/ConsoleValue.tsx +0 -265
- package/src/devtools/ConsoleView.tsx +0 -43
- package/src/devtools/consoleFilter.ts +0 -123
- package/src/devtools/consoleFormat.ts +0 -314
- package/src/devtools/origin.ts +0 -190
- package/src/devtools/theme.ts +0 -148
- package/src/devtools/useConsoleController.ts +0 -104
- package/src/devtools/useConsoleMessages.ts +0 -337
- package/src/hooks/useConsoleLogs.ts +0 -26
- package/src/index.tsx +0 -70
- package/src/preset.tsx +0 -67
- package/src/store/__tests__/consoleSyncAdapter.test.ts +0 -156
- package/src/store/consoleLogStore.ts +0 -721
- package/src/store/sanitize.ts +0 -83
- package/src/sync/consoleSyncAdapter.ts +0 -115
package/package.json
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/console",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.30",
|
|
4
4
|
"description": "Chrome DevTools-style console for React Native — captures every console.* on device, even in release builds. Part of Buoy devtools.",
|
|
5
|
-
"main": "lib/commonjs/index",
|
|
6
|
-
"module": "lib/module/index",
|
|
5
|
+
"main": "lib/commonjs/index.js",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
7
|
"types": "lib/typescript/index.d.ts",
|
|
8
|
-
"react-native": "
|
|
8
|
+
"react-native": "lib/module/index.js",
|
|
9
9
|
"source": "src/index.tsx",
|
|
10
10
|
"files": [
|
|
11
|
-
"src",
|
|
12
11
|
"lib"
|
|
13
12
|
],
|
|
14
13
|
"sideEffects": false,
|
|
15
14
|
"dependencies": {
|
|
16
|
-
"@buoy-gg/shared-ui": "^7.0.
|
|
15
|
+
"@buoy-gg/shared-ui": "^7.0.30"
|
|
17
16
|
},
|
|
18
17
|
"peerDependencies": {
|
|
19
18
|
"react": "*",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConsoleIcon — re-exported from the Buoy Icon Format.
|
|
3
|
-
*
|
|
4
|
-
* The artwork lives in shared/icons/console.json and is generated into
|
|
5
|
-
* @buoy-gg/floating-tools-core, so React Native, the desktop dashboard and
|
|
6
|
-
* Flutter all render the same drawing. Edit the JSON, then `pnpm icons`.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export { ConsoleIcon, CONSOLE_ICON_COLOR } from "@buoy-gg/shared-ui";
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
View,
|
|
4
|
-
Text,
|
|
5
|
-
FlatList,
|
|
6
|
-
TouchableOpacity,
|
|
7
|
-
StyleSheet,
|
|
8
|
-
type ListRenderItem,
|
|
9
|
-
} from "react-native";
|
|
10
|
-
import { useConsoleLogs } from "../hooks/useConsoleLogs";
|
|
11
|
-
import { night } from "@buoy-gg/shared-ui";
|
|
12
|
-
import { clearEntries, type ConsoleLogEntry, type ConsoleLevel } from "../store/consoleLogStore";
|
|
13
|
-
|
|
14
|
-
const LEVEL_COLOR: Record<ConsoleLevel, string> = {
|
|
15
|
-
verbose: "#9b9b9b",
|
|
16
|
-
info: night.color.text,
|
|
17
|
-
warning: "#e2c08d",
|
|
18
|
-
error: "#f48771",
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function formatTime(ts: number): string {
|
|
22
|
-
const d = new Date(ts);
|
|
23
|
-
const pad = (n: number, len = 2) => String(n).padStart(len, "0");
|
|
24
|
-
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(
|
|
25
|
-
d.getMilliseconds(),
|
|
26
|
-
3
|
|
27
|
-
)}`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Basic console log list — a flat, newest-last stream of captured entries.
|
|
32
|
-
* This is the proof-of-concept view; it will be replaced by a 1:1 port of
|
|
33
|
-
* Chrome DevTools' console once capture is verified.
|
|
34
|
-
*/
|
|
35
|
-
export function ConsoleLogList() {
|
|
36
|
-
const { logs } = useConsoleLogs();
|
|
37
|
-
|
|
38
|
-
const renderItem: ListRenderItem<ConsoleLogEntry> = ({ item }) => (
|
|
39
|
-
<View style={styles.row}>
|
|
40
|
-
<Text style={styles.time}>{formatTime(item.timestamp)}</Text>
|
|
41
|
-
<Text style={[styles.level, { color: LEVEL_COLOR[item.level] }]}>
|
|
42
|
-
{item.level.toUpperCase()}
|
|
43
|
-
</Text>
|
|
44
|
-
<Text style={[styles.message, { color: LEVEL_COLOR[item.level] }]} numberOfLines={3}>
|
|
45
|
-
{item.message}
|
|
46
|
-
</Text>
|
|
47
|
-
</View>
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<View style={styles.container}>
|
|
52
|
-
<View style={styles.header}>
|
|
53
|
-
<Text style={styles.headerTitle}>Console ({logs.length})</Text>
|
|
54
|
-
<TouchableOpacity onPress={() => clearEntries()} style={styles.clearButton}>
|
|
55
|
-
<Text style={styles.clearText}>Clear</Text>
|
|
56
|
-
</TouchableOpacity>
|
|
57
|
-
</View>
|
|
58
|
-
<FlatList
|
|
59
|
-
data={logs}
|
|
60
|
-
keyExtractor={(item) => String(item.id)}
|
|
61
|
-
renderItem={renderItem}
|
|
62
|
-
ListEmptyComponent={
|
|
63
|
-
<Text style={styles.empty}>No console logs captured yet.</Text>
|
|
64
|
-
}
|
|
65
|
-
contentContainerStyle={logs.length === 0 ? styles.emptyContainer : undefined}
|
|
66
|
-
/>
|
|
67
|
-
</View>
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const styles = StyleSheet.create({
|
|
72
|
-
container: {
|
|
73
|
-
flex: 1,
|
|
74
|
-
// Transparent so the night sheet's star field shows through.
|
|
75
|
-
backgroundColor: "transparent",
|
|
76
|
-
},
|
|
77
|
-
header: {
|
|
78
|
-
flexDirection: "row",
|
|
79
|
-
alignItems: "center",
|
|
80
|
-
justifyContent: "space-between",
|
|
81
|
-
paddingHorizontal: 12,
|
|
82
|
-
paddingVertical: 8,
|
|
83
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
84
|
-
borderBottomColor: night.color.border,
|
|
85
|
-
},
|
|
86
|
-
headerTitle: {
|
|
87
|
-
color: night.color.text,
|
|
88
|
-
fontSize: 13,
|
|
89
|
-
fontWeight: "600",
|
|
90
|
-
},
|
|
91
|
-
clearButton: {
|
|
92
|
-
paddingHorizontal: 10,
|
|
93
|
-
paddingVertical: 4,
|
|
94
|
-
borderRadius: 4,
|
|
95
|
-
backgroundColor: night.color.surfaceElevated,
|
|
96
|
-
},
|
|
97
|
-
clearText: {
|
|
98
|
-
color: night.color.text,
|
|
99
|
-
fontSize: 12,
|
|
100
|
-
},
|
|
101
|
-
row: {
|
|
102
|
-
flexDirection: "row",
|
|
103
|
-
paddingHorizontal: 12,
|
|
104
|
-
paddingVertical: 4,
|
|
105
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
106
|
-
borderBottomColor: night.color.separator,
|
|
107
|
-
gap: 8,
|
|
108
|
-
},
|
|
109
|
-
time: {
|
|
110
|
-
color: night.color.textTertiary,
|
|
111
|
-
fontSize: 11,
|
|
112
|
-
fontVariant: ["tabular-nums"],
|
|
113
|
-
minWidth: 86,
|
|
114
|
-
},
|
|
115
|
-
level: {
|
|
116
|
-
fontSize: 11,
|
|
117
|
-
fontWeight: "700",
|
|
118
|
-
minWidth: 44,
|
|
119
|
-
},
|
|
120
|
-
message: {
|
|
121
|
-
flex: 1,
|
|
122
|
-
fontSize: 12,
|
|
123
|
-
fontFamily: "monospace",
|
|
124
|
-
},
|
|
125
|
-
empty: {
|
|
126
|
-
color: night.color.textTertiary,
|
|
127
|
-
fontSize: 13,
|
|
128
|
-
textAlign: "center",
|
|
129
|
-
},
|
|
130
|
-
emptyContainer: {
|
|
131
|
-
flexGrow: 1,
|
|
132
|
-
alignItems: "center",
|
|
133
|
-
justifyContent: "center",
|
|
134
|
-
},
|
|
135
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConsoleModal
|
|
3
|
-
*
|
|
4
|
-
* Bottom-sheet wrapper for the console, rendered by FloatingDevTools when the
|
|
5
|
-
* Console tool is opened. The controls live in the shared ModalHeader
|
|
6
|
-
* (`customContent`) — matching Network/Events — and the body is the log list.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { JsModal } from "@buoy-gg/shared-ui";
|
|
10
|
-
import type { ModalState } from "@buoy-gg/shared-ui";
|
|
11
|
-
import { useConsoleController } from "../devtools/useConsoleController";
|
|
12
|
-
import { ConsoleHeader } from "../devtools/ConsoleHeader";
|
|
13
|
-
import { ConsoleBody } from "../devtools/ConsoleBody";
|
|
14
|
-
|
|
15
|
-
interface ConsoleModalProps {
|
|
16
|
-
visible: boolean;
|
|
17
|
-
onClose: () => void;
|
|
18
|
-
onBack?: () => void;
|
|
19
|
-
onMinimize?: (modalState: ModalState) => void;
|
|
20
|
-
persistenceKey?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function ConsoleModal({
|
|
24
|
-
visible,
|
|
25
|
-
onClose,
|
|
26
|
-
onBack,
|
|
27
|
-
onMinimize,
|
|
28
|
-
persistenceKey,
|
|
29
|
-
}: ConsoleModalProps) {
|
|
30
|
-
const c = useConsoleController();
|
|
31
|
-
return (
|
|
32
|
-
<JsModal
|
|
33
|
-
visible={visible}
|
|
34
|
-
onClose={onClose}
|
|
35
|
-
onMinimize={onMinimize}
|
|
36
|
-
persistenceKey={persistenceKey}
|
|
37
|
-
header={{
|
|
38
|
-
showToggleButton: true,
|
|
39
|
-
customContent: <ConsoleHeader c={c} onBack={onBack} />,
|
|
40
|
-
}}
|
|
41
|
-
enablePersistence
|
|
42
|
-
initialMode="bottomSheet"
|
|
43
|
-
disableScrollWrapper
|
|
44
|
-
>
|
|
45
|
-
<ConsoleBody c={c} />
|
|
46
|
-
</JsModal>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { useEffect } from "react";
|
|
2
|
-
import {
|
|
3
|
-
installConsoleCapture,
|
|
4
|
-
initConsolePersistence,
|
|
5
|
-
} from "../store/consoleLogStore";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Mount once near the app root to start capturing console output immediately,
|
|
9
|
-
* so logs emitted before the Console tool is opened are retained.
|
|
10
|
-
*
|
|
11
|
-
* Also restores a preserved buffer (or clears any stale one) before capture
|
|
12
|
-
* starts, so the session honors the "Preserve log" setting from the first log.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```tsx
|
|
16
|
-
* <ConsoleRoot />
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export function ConsoleRoot(): null {
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
// Restore/clear the persisted buffer first, then start capturing.
|
|
22
|
-
void initConsolePersistence().finally(() => installConsoleCapture());
|
|
23
|
-
}, []);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConsoleBody — the modal body. Renders the virtualized log list, or (when a
|
|
3
|
-
* header action selected it) a back-navigable Log levels / Console settings
|
|
4
|
-
* sub-view, mirroring how Network swaps its body to a filter sub-view.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React, { useCallback, useRef, useSyncExternalStore } from "react";
|
|
8
|
-
import {
|
|
9
|
-
View,
|
|
10
|
-
Text,
|
|
11
|
-
FlatList,
|
|
12
|
-
TouchableOpacity,
|
|
13
|
-
ScrollView,
|
|
14
|
-
StyleSheet,
|
|
15
|
-
type ListRenderItem,
|
|
16
|
-
} from "react-native";
|
|
17
|
-
import { night, Check } from "@buoy-gg/shared-ui";
|
|
18
|
-
import { consoleTheme } from "./theme";
|
|
19
|
-
import { ConsoleMessageRow } from "./ConsoleMessageRow";
|
|
20
|
-
import type { DisplayRow } from "./useConsoleMessages";
|
|
21
|
-
import {
|
|
22
|
-
ALL_LEVELS,
|
|
23
|
-
DEFAULT_LEVELS,
|
|
24
|
-
LEVEL_LABEL,
|
|
25
|
-
LEVEL_ORDER,
|
|
26
|
-
type LevelsMask,
|
|
27
|
-
} from "./consoleFilter";
|
|
28
|
-
import {
|
|
29
|
-
getPreserveLog,
|
|
30
|
-
setPreserveLog,
|
|
31
|
-
subscribePreserveLog,
|
|
32
|
-
} from "../store/consoleLogStore";
|
|
33
|
-
import type { ConsoleController } from "./useConsoleController";
|
|
34
|
-
|
|
35
|
-
export function ConsoleBody({ c }: { c: ConsoleController }) {
|
|
36
|
-
if (c.view === "filters") return <FiltersView c={c} />;
|
|
37
|
-
return <ConsoleList c={c} />;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ─── Log list ────────────────────────────────────────────────────────────────
|
|
41
|
-
|
|
42
|
-
function ConsoleList({ c }: { c: ConsoleController }) {
|
|
43
|
-
const listRef = useRef<FlatList<DisplayRow>>(null);
|
|
44
|
-
const stickToBottom = useRef(true);
|
|
45
|
-
|
|
46
|
-
const renderItem: ListRenderItem<DisplayRow> = useCallback(
|
|
47
|
-
({ item }) => (
|
|
48
|
-
<ConsoleMessageRow
|
|
49
|
-
row={item}
|
|
50
|
-
showTimestamps={c.showTimestamps}
|
|
51
|
-
onToggleGroup={c.onToggleGroup}
|
|
52
|
-
onToggleCluster={c.onToggleCluster}
|
|
53
|
-
/>
|
|
54
|
-
),
|
|
55
|
-
[c.showTimestamps, c.onToggleGroup, c.onToggleCluster]
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return (
|
|
59
|
-
<FlatList
|
|
60
|
-
ref={listRef}
|
|
61
|
-
data={c.rows}
|
|
62
|
-
keyExtractor={(item) => item.key}
|
|
63
|
-
renderItem={renderItem}
|
|
64
|
-
style={styles.list}
|
|
65
|
-
initialNumToRender={30}
|
|
66
|
-
maxToRenderPerBatch={30}
|
|
67
|
-
windowSize={21}
|
|
68
|
-
removeClippedSubviews
|
|
69
|
-
onContentSizeChange={() => {
|
|
70
|
-
if (stickToBottom.current) listRef.current?.scrollToEnd({ animated: false });
|
|
71
|
-
}}
|
|
72
|
-
onScroll={(e) => {
|
|
73
|
-
const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
|
|
74
|
-
const distanceFromBottom =
|
|
75
|
-
contentSize.height - (contentOffset.y + layoutMeasurement.height);
|
|
76
|
-
stickToBottom.current = distanceFromBottom < 40;
|
|
77
|
-
}}
|
|
78
|
-
scrollEventThrottle={100}
|
|
79
|
-
/>
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// ─── Sub-views ───────────────────────────────────────────────────────────────
|
|
84
|
-
|
|
85
|
-
function CheckRow({
|
|
86
|
-
on,
|
|
87
|
-
label,
|
|
88
|
-
subtext,
|
|
89
|
-
onPress,
|
|
90
|
-
}: {
|
|
91
|
-
on: boolean;
|
|
92
|
-
label: string;
|
|
93
|
-
subtext?: string;
|
|
94
|
-
onPress: () => void;
|
|
95
|
-
}) {
|
|
96
|
-
return (
|
|
97
|
-
<TouchableOpacity style={styles.row} onPress={onPress} activeOpacity={0.7}>
|
|
98
|
-
<View style={[styles.checkbox, on && styles.checkboxOn]}>
|
|
99
|
-
{on ? <Check size={12} color={night.color.onAccent} strokeWidth={3} /> : null}
|
|
100
|
-
</View>
|
|
101
|
-
<View style={styles.rowTextCol}>
|
|
102
|
-
<Text style={styles.rowLabel}>{label}</Text>
|
|
103
|
-
{subtext ? <Text style={styles.rowSubtext}>{subtext}</Text> : null}
|
|
104
|
-
</View>
|
|
105
|
-
</TouchableOpacity>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function SectionLabel({ text }: { text: string }) {
|
|
110
|
-
return <Text style={styles.sectionLabel}>{text}</Text>;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function FiltersView({ c }: { c: ConsoleController }) {
|
|
114
|
-
const preserveLog = useSyncExternalStore(subscribePreserveLog, getPreserveLog, getPreserveLog);
|
|
115
|
-
const toggle = (level: keyof LevelsMask) =>
|
|
116
|
-
c.setLevelsMask({ ...c.levelsMask, [level]: !c.levelsMask[level] });
|
|
117
|
-
|
|
118
|
-
return (
|
|
119
|
-
<ScrollView style={styles.subView} contentContainerStyle={styles.subViewContent}>
|
|
120
|
-
<SectionLabel text="Log levels" />
|
|
121
|
-
<TouchableOpacity
|
|
122
|
-
style={styles.presetRow}
|
|
123
|
-
onPress={() => c.setLevelsMask({ ...DEFAULT_LEVELS })}
|
|
124
|
-
>
|
|
125
|
-
<Text style={styles.presetText}>Default (hide verbose)</Text>
|
|
126
|
-
</TouchableOpacity>
|
|
127
|
-
<TouchableOpacity style={styles.presetRow} onPress={() => c.setLevelsMask({ ...ALL_LEVELS })}>
|
|
128
|
-
<Text style={styles.presetText}>All levels</Text>
|
|
129
|
-
</TouchableOpacity>
|
|
130
|
-
{LEVEL_ORDER.map((level) => (
|
|
131
|
-
<CheckRow
|
|
132
|
-
key={level}
|
|
133
|
-
on={c.levelsMask[level]}
|
|
134
|
-
label={LEVEL_LABEL[level]}
|
|
135
|
-
onPress={() => toggle(level)}
|
|
136
|
-
/>
|
|
137
|
-
))}
|
|
138
|
-
|
|
139
|
-
<View style={styles.divider} />
|
|
140
|
-
|
|
141
|
-
<SectionLabel text="Settings" />
|
|
142
|
-
<CheckRow
|
|
143
|
-
on={c.showTimestamps}
|
|
144
|
-
label="Show timestamps"
|
|
145
|
-
onPress={() => c.setShowTimestamps((s) => !s)}
|
|
146
|
-
/>
|
|
147
|
-
<CheckRow
|
|
148
|
-
on={preserveLog}
|
|
149
|
-
label="Preserve log"
|
|
150
|
-
subtext="Do not clear log on reload"
|
|
151
|
-
onPress={() => setPreserveLog(!preserveLog)}
|
|
152
|
-
/>
|
|
153
|
-
</ScrollView>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const styles = StyleSheet.create({
|
|
158
|
-
list: {
|
|
159
|
-
flex: 1,
|
|
160
|
-
// Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
|
|
161
|
-
backgroundColor: "transparent",
|
|
162
|
-
},
|
|
163
|
-
subView: {
|
|
164
|
-
flex: 1,
|
|
165
|
-
// Transparent so the night sheet's star field shows through.
|
|
166
|
-
backgroundColor: "transparent",
|
|
167
|
-
},
|
|
168
|
-
subViewContent: {
|
|
169
|
-
padding: 12,
|
|
170
|
-
},
|
|
171
|
-
sectionLabel: {
|
|
172
|
-
color: night.color.textTertiary,
|
|
173
|
-
fontSize: 11,
|
|
174
|
-
textTransform: "uppercase",
|
|
175
|
-
letterSpacing: 0.5,
|
|
176
|
-
marginTop: 4,
|
|
177
|
-
marginBottom: 4,
|
|
178
|
-
paddingHorizontal: 8,
|
|
179
|
-
},
|
|
180
|
-
presetRow: {
|
|
181
|
-
paddingVertical: 10,
|
|
182
|
-
paddingHorizontal: 8,
|
|
183
|
-
},
|
|
184
|
-
presetText: {
|
|
185
|
-
color: night.color.accent,
|
|
186
|
-
fontSize: 14,
|
|
187
|
-
fontWeight: "600",
|
|
188
|
-
},
|
|
189
|
-
divider: {
|
|
190
|
-
height: StyleSheet.hairlineWidth,
|
|
191
|
-
backgroundColor: night.color.border,
|
|
192
|
-
marginVertical: 6,
|
|
193
|
-
},
|
|
194
|
-
row: {
|
|
195
|
-
flexDirection: "row",
|
|
196
|
-
alignItems: "center",
|
|
197
|
-
paddingVertical: 10,
|
|
198
|
-
paddingHorizontal: 8,
|
|
199
|
-
gap: 12,
|
|
200
|
-
},
|
|
201
|
-
rowTextCol: {
|
|
202
|
-
flexDirection: "column",
|
|
203
|
-
flex: 1,
|
|
204
|
-
},
|
|
205
|
-
rowLabel: {
|
|
206
|
-
color: night.color.text,
|
|
207
|
-
fontSize: 14,
|
|
208
|
-
},
|
|
209
|
-
rowSubtext: {
|
|
210
|
-
color: night.color.textTertiary,
|
|
211
|
-
fontSize: 12,
|
|
212
|
-
marginTop: 1,
|
|
213
|
-
},
|
|
214
|
-
checkbox: {
|
|
215
|
-
width: 18,
|
|
216
|
-
height: 18,
|
|
217
|
-
borderRadius: 4,
|
|
218
|
-
borderWidth: 1,
|
|
219
|
-
borderColor: night.color.fillTertiary,
|
|
220
|
-
alignItems: "center",
|
|
221
|
-
justifyContent: "center",
|
|
222
|
-
},
|
|
223
|
-
// Solid accent (not accentSoft) so the onAccent check glyph stays legible.
|
|
224
|
-
checkboxOn: {
|
|
225
|
-
backgroundColor: night.color.accent,
|
|
226
|
-
borderColor: night.color.accent,
|
|
227
|
-
},
|
|
228
|
-
});
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ConsoleHeader — the Console tool's header, built from the shared ModalHeader
|
|
3
|
-
* pattern so it matches Network/Events 1:1. Controls live here: the
|
|
4
|
-
* Timeline/Grouped TabSelector (or the search field when active), plus action
|
|
5
|
-
* icons for search, level filter, clear, and settings. Tapping the level/filter
|
|
6
|
-
* or settings icon swaps the body to a back-navigable sub-view.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import React from "react";
|
|
10
|
-
import { View, TextInput, TouchableOpacity, StyleSheet } from "react-native";
|
|
11
|
-
import {
|
|
12
|
-
ModalHeader,
|
|
13
|
-
TabSelector,
|
|
14
|
-
night,
|
|
15
|
-
Search,
|
|
16
|
-
Filter,
|
|
17
|
-
Trash2,
|
|
18
|
-
X,
|
|
19
|
-
} from "@buoy-gg/shared-ui";
|
|
20
|
-
import type { ConsoleController } from "./useConsoleController";
|
|
21
|
-
import type { LayoutMode } from "./useConsoleMessages";
|
|
22
|
-
|
|
23
|
-
const LAYOUT_TABS = [
|
|
24
|
-
{ key: "chrono", label: "Timeline" },
|
|
25
|
-
{ key: "cluster", label: "Grouped" },
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
interface ConsoleHeaderProps {
|
|
29
|
-
c: ConsoleController;
|
|
30
|
-
/** App-stack back (from the floating menu's AppHost), list view only. */
|
|
31
|
-
onBack?: () => void;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function ConsoleHeader({ c, onBack }: ConsoleHeaderProps) {
|
|
35
|
-
// Filters sub-view: a back chevron returns to the list.
|
|
36
|
-
if (c.view === "filters") {
|
|
37
|
-
return (
|
|
38
|
-
<ModalHeader>
|
|
39
|
-
<ModalHeader.Navigation onBack={() => c.setView("list")} />
|
|
40
|
-
<ModalHeader.Content title="Filters" />
|
|
41
|
-
</ModalHeader>
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const hasRows = c.rows.length > 0;
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<ModalHeader>
|
|
49
|
-
{onBack ? <ModalHeader.Navigation onBack={onBack} /> : null}
|
|
50
|
-
<ModalHeader.Content title="" noMargin>
|
|
51
|
-
{c.searchActive ? (
|
|
52
|
-
<View style={styles.searchContainer}>
|
|
53
|
-
<Search size={14} color={night.color.textSecondary} />
|
|
54
|
-
<TextInput
|
|
55
|
-
style={styles.searchInput}
|
|
56
|
-
placeholder="Filter logs… /regex/ -exclude"
|
|
57
|
-
placeholderTextColor={night.color.placeholder}
|
|
58
|
-
value={c.query}
|
|
59
|
-
onChangeText={c.setQuery}
|
|
60
|
-
autoCapitalize="none"
|
|
61
|
-
autoCorrect={false}
|
|
62
|
-
returnKeyType="search"
|
|
63
|
-
onSubmitEditing={() => c.setSearchActive(false)}
|
|
64
|
-
/>
|
|
65
|
-
{c.query.length > 0 ? (
|
|
66
|
-
<TouchableOpacity onPress={() => c.setQuery("")} style={styles.searchClear}>
|
|
67
|
-
<X size={14} color={night.color.textSecondary} />
|
|
68
|
-
</TouchableOpacity>
|
|
69
|
-
) : null}
|
|
70
|
-
</View>
|
|
71
|
-
) : (
|
|
72
|
-
<TabSelector
|
|
73
|
-
tabs={LAYOUT_TABS}
|
|
74
|
-
activeTab={c.layoutMode}
|
|
75
|
-
onTabChange={(key) => c.setLayoutMode(key as LayoutMode)}
|
|
76
|
-
/>
|
|
77
|
-
)}
|
|
78
|
-
</ModalHeader.Content>
|
|
79
|
-
|
|
80
|
-
<ModalHeader.Actions>
|
|
81
|
-
<TouchableOpacity
|
|
82
|
-
onPress={() => c.setSearchActive((a) => !a)}
|
|
83
|
-
style={[styles.actionBtn, c.searchActive && styles.actionBtnActive]}
|
|
84
|
-
accessibilityLabel="Search logs"
|
|
85
|
-
>
|
|
86
|
-
<Search size={14} color={c.searchActive ? night.color.accent : night.color.textSecondary} />
|
|
87
|
-
</TouchableOpacity>
|
|
88
|
-
|
|
89
|
-
<TouchableOpacity
|
|
90
|
-
onPress={() => c.setView("filters")}
|
|
91
|
-
style={[styles.actionBtn, c.isLevelCustom && styles.actionBtnActive]}
|
|
92
|
-
accessibilityLabel="Filters"
|
|
93
|
-
>
|
|
94
|
-
<Filter size={14} color={c.isLevelCustom ? night.color.accent : night.color.textSecondary} />
|
|
95
|
-
</TouchableOpacity>
|
|
96
|
-
|
|
97
|
-
<TouchableOpacity
|
|
98
|
-
onPress={c.clear}
|
|
99
|
-
disabled={!hasRows}
|
|
100
|
-
style={[styles.actionBtn, !hasRows && styles.actionBtnDisabled]}
|
|
101
|
-
accessibilityLabel="Clear console"
|
|
102
|
-
>
|
|
103
|
-
<Trash2 size={14} color={hasRows ? night.color.textSecondary : night.color.textTertiary} />
|
|
104
|
-
</TouchableOpacity>
|
|
105
|
-
</ModalHeader.Actions>
|
|
106
|
-
</ModalHeader>
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const styles = StyleSheet.create({
|
|
111
|
-
actionBtn: {
|
|
112
|
-
width: 32,
|
|
113
|
-
height: 32,
|
|
114
|
-
borderRadius: 8,
|
|
115
|
-
backgroundColor: night.color.buttonSurface,
|
|
116
|
-
borderWidth: 1,
|
|
117
|
-
borderColor: night.color.border,
|
|
118
|
-
alignItems: "center",
|
|
119
|
-
justifyContent: "center",
|
|
120
|
-
},
|
|
121
|
-
actionBtnActive: {
|
|
122
|
-
backgroundColor: night.color.accentSoft,
|
|
123
|
-
borderColor: night.color.accentBorderStrong,
|
|
124
|
-
},
|
|
125
|
-
actionBtnDisabled: {
|
|
126
|
-
opacity: 0.55,
|
|
127
|
-
},
|
|
128
|
-
searchContainer: {
|
|
129
|
-
flexDirection: "row",
|
|
130
|
-
alignItems: "center",
|
|
131
|
-
backgroundColor: night.color.surfaceElevated,
|
|
132
|
-
borderRadius: night.radius.row,
|
|
133
|
-
borderWidth: 1,
|
|
134
|
-
borderColor: night.color.border,
|
|
135
|
-
paddingHorizontal: 12,
|
|
136
|
-
paddingVertical: 5,
|
|
137
|
-
},
|
|
138
|
-
searchInput: {
|
|
139
|
-
flex: 1,
|
|
140
|
-
color: night.color.text,
|
|
141
|
-
fontSize: 13,
|
|
142
|
-
marginLeft: 6,
|
|
143
|
-
paddingVertical: 2,
|
|
144
|
-
},
|
|
145
|
-
searchClear: {
|
|
146
|
-
marginLeft: 6,
|
|
147
|
-
padding: 4,
|
|
148
|
-
},
|
|
149
|
-
});
|