@buoy-gg/console 7.0.29 → 7.0.31

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.
@@ -1,148 +0,0 @@
1
- /**
2
- * Console palette — a dark theme whose CHROME entries (surfaces, dividers,
3
- * default/secondary text) come from Buoy's `night` tokens so the console reads
4
- * as part of the night suite, while the Chrome-DevTools DATA colours (level
5
- * rows, value types, links, ANSI) stay classic — they tell the user something
6
- * about the logged data.
7
- * `lightTheme` below holds the exact DevTools light tokens and `consoleTheme`
8
- * picks between the two from the host app's surface. Every shipped theme is
9
- * dark today, so the light branch is currently unreachable — it is kept because
10
- * the probe is what makes this correct if a light surface ever returns.
11
- *
12
- * Not themed: `ansiColors` below is the dark-theme ANSI palette, so ANSI
13
- * escape sequences in captured output keep their dark-console colours.
14
- */
15
-
16
- import { Platform } from "react-native";
17
- import { night } from "@buoy-gg/shared-ui";
18
-
19
- /**
20
- * Monospace font. On the desktop dashboard (react-native-web) the generic
21
- * `"monospace"` resolves to a thin, poorly-hinted browser default that's hard
22
- * to read at small sizes — so we use a curated mono stack there. Native (mobile)
23
- * keeps its system monospace, which already renders well.
24
- */
25
- export const monoFont: string =
26
- Platform.select({
27
- web: 'ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Monaco, "Cascadia Code", "Roboto Mono", Consolas, "Liberation Mono", monospace',
28
- default: "monospace",
29
- }) ?? "monospace";
30
-
31
- export interface ConsoleColors {
32
- baseContainer: string;
33
- surfaceError: string;
34
- surfaceYellow: string;
35
- divider: string;
36
- inputBackground: string;
37
- menuBackground: string;
38
- onSurface: string;
39
- onSurfaceYellow: string;
40
- onErrorContainer: string;
41
- tokenSubtle: string;
42
- primary: string;
43
- valueNumber: string;
44
- valueString: string;
45
- valueBigint: string;
46
- valueNode: string;
47
- valueNullish: string;
48
- valuePreview: string;
49
- }
50
-
51
- /** Resolved DevTools light-theme tokens. Unreachable today — see the header. */
52
- export const lightTheme: ConsoleColors = {
53
- baseContainer: "#ffffff", // --sys-color-cdt-base-container (neutral99 ≈ white)
54
- surfaceError: "#fceaea", // --sys-color-surface-error rgb(252 235 235)
55
- surfaceYellow: "#fef6d5", // --sys-color-surface-yellow rgb(254 246 213)
56
- divider: "#e8eaed", // --sys-color-divider (primary90, lightened for contrast)
57
- inputBackground: "#f1f3f4",
58
- menuBackground: "#ffffff",
59
- onSurface: "#1f1f1f", // neutral10
60
- onSurfaceYellow: "#3e2f00", // yellow20
61
- onErrorContainer: "#410e0b", // error10
62
- tokenSubtle: "#757575", // neutral50
63
- primary: "#0b57d0", // primary40 (links)
64
- valueNumber: "#0842a0", // token-attribute-value (blue30)
65
- valueString: "#c5221f", // token-property-special (error50, slightly deepened)
66
- valueBigint: "#146c2e", // token-comment (green40)
67
- valueNode: "#1f1f1f", // token-variable (= on-surface)
68
- valueNullish: "rgba(31,31,31,0.5)", // state-disabled
69
- valuePreview: "rgba(31,31,31,0.62)",
70
- };
71
-
72
- /** Dark theme (active) — night chrome, classic DevTools data colours. */
73
- export const darkTheme: ConsoleColors = {
74
- baseContainer: "#0A0A0C", // desktop inline shell base (modal path is transparent)
75
- surfaceError: "rgba(255,69,58,0.15)", // semantic.errorBackground
76
- surfaceYellow: "rgba(255,235,59,0.13)", // semantic.warningBackground
77
- divider: night.color.fillTertiary, // row hairlines + nesting guides (strong enough to stay visible)
78
- inputBackground: night.color.surfaceElevated,
79
- menuBackground: night.color.surface,
80
- onSurface: night.color.text,
81
- onSurfaceYellow: "#FFE9A3", // light warm yellow on dark warning row
82
- onErrorContainer: "#FFB4AB", // light red on dark error row
83
- tokenSubtle: night.color.textSecondary, // timestamps, previews
84
- primary: "#40CCFF", // semantic.infoLight (links, ~9:1)
85
- valueNumber: "#FF9F1C", // orange (number + boolean)
86
- valueString: "#7EE787", // green (string/regexp/symbol)
87
- valueBigint: "#A1A1A6", // muted (bigint)
88
- valueNode: "#F5F5F7", // default text (DOM node / variable)
89
- valueNullish: "#8E8E93", // text.muted (null/undefined)
90
- valuePreview: "#A1A1A6", // object preview (italic)
91
- };
92
-
93
- /**
94
- * The active console palette.
95
- *
96
- * `darkTheme`, always. `macOSColors` is the dark palette on every surface Buoy
97
- * renders on, so there is nothing to pick between — this used to probe the host
98
- * surface's luminance for a light dashboard theme that no longer exists.
99
- * `lightTheme` above is kept because it is the real Chrome DevTools light token
100
- * set and predates that experiment; swap it in here if a light surface returns.
101
- */
102
- export const consoleTheme: ConsoleColors = darkTheme;
103
-
104
- /** ANSI 16-color palette (dark theme), from consoleView.css `--console-color-*`. */
105
- export const ansiColors: Record<string, string> = {
106
- black: "#000",
107
- red: "rgb(237 78 76)",
108
- green: "rgb(1 200 1)",
109
- yellow: "rgb(210 192 87)",
110
- blue: "rgb(39 116 240)",
111
- magenta: "rgb(161 66 244)",
112
- cyan: "rgb(18 181 203)",
113
- gray: "rgb(207 208 208)",
114
- darkgray: "rgb(137 137 137)",
115
- lightred: "rgb(242 139 130)",
116
- lightgreen: "rgb(161 247 181)",
117
- lightyellow: "rgb(221 251 85)",
118
- lightblue: "rgb(102 157 246)",
119
- lightmagenta: "rgb(214 112 214)",
120
- lightcyan: "rgb(132 240 255)",
121
- white: "#fff",
122
- };
123
-
124
- export type ConsoleLevel = "verbose" | "info" | "warning" | "error";
125
-
126
- export interface LevelStyle {
127
- /** Row background (undefined = base container). */
128
- background?: string;
129
- /** Message text color. */
130
- text: string;
131
- /** Material icon name analog (rendered as a glyph). */
132
- icon?: "warning" | "error";
133
- }
134
-
135
- export const levelStyles: Record<ConsoleLevel, LevelStyle> = {
136
- verbose: { text: consoleTheme.tokenSubtle },
137
- info: { text: consoleTheme.onSurface },
138
- warning: {
139
- background: consoleTheme.surfaceYellow,
140
- text: consoleTheme.onSurfaceYellow,
141
- icon: "warning",
142
- },
143
- error: {
144
- background: consoleTheme.surfaceError,
145
- text: consoleTheme.onErrorContainer,
146
- icon: "error",
147
- },
148
- };
@@ -1,104 +0,0 @@
1
- /**
2
- * useConsoleController — owns all Console view state so the header and the body
3
- * can be composed separately (the shared ModalHeader pattern: controls live in
4
- * the modal header's `customContent`, the list/sub-views live in the body).
5
- */
6
-
7
- import { useCallback, useState, type Dispatch, type SetStateAction } from "react";
8
- import {
9
- useConsoleMessages,
10
- type DisplayRow,
11
- type LayoutMode,
12
- } from "./useConsoleMessages";
13
- import {
14
- DEFAULT_LEVELS,
15
- levelMenuSummary,
16
- type LevelsMask,
17
- } from "./consoleFilter";
18
- import { clearEntries } from "../store/consoleLogStore";
19
-
20
- /** Which screen the modal body shows (list, or the back-navigable filters page). */
21
- export type ConsoleViewState = "list" | "filters";
22
-
23
- export interface ConsoleController {
24
- query: string;
25
- setQuery: Dispatch<SetStateAction<string>>;
26
- searchActive: boolean;
27
- setSearchActive: Dispatch<SetStateAction<boolean>>;
28
- levelsMask: LevelsMask;
29
- setLevelsMask: Dispatch<SetStateAction<LevelsMask>>;
30
- showTimestamps: boolean;
31
- setShowTimestamps: Dispatch<SetStateAction<boolean>>;
32
- layoutMode: LayoutMode;
33
- setLayoutMode: Dispatch<SetStateAction<LayoutMode>>;
34
- view: ConsoleViewState;
35
- setView: Dispatch<SetStateAction<ConsoleViewState>>;
36
- rows: DisplayRow[];
37
- hiddenCount: number;
38
- onToggleGroup: (groupId: number, currentlyCollapsed: boolean) => void;
39
- onToggleCluster: (key: string) => void;
40
- clear: () => void;
41
- /** Level menu summary ("Default levels" / "Errors only" / …). */
42
- levelSummary: string;
43
- /** Whether the level mask differs from all/default (drives the active tint). */
44
- isLevelCustom: boolean;
45
- }
46
-
47
- export function useConsoleController(): ConsoleController {
48
- const [query, setQuery] = useState("");
49
- const [searchActive, setSearchActive] = useState(false);
50
- const [levelsMask, setLevelsMask] = useState<LevelsMask>({ ...DEFAULT_LEVELS });
51
- const [showTimestamps, setShowTimestamps] = useState(false);
52
- const [collapsedOverrides, setCollapsedOverrides] = useState<Record<number, boolean>>({});
53
- const [layoutMode, setLayoutMode] = useState<LayoutMode>("chrono");
54
- const [view, setView] = useState<ConsoleViewState>("list");
55
- const [clusterExpanded, setClusterExpanded] = useState<Record<string, boolean>>({});
56
-
57
- const { rows, hiddenCount } = useConsoleMessages({
58
- levelsMask,
59
- query,
60
- showTimestamps,
61
- collapsedOverrides,
62
- groupDimension: "function",
63
- layoutMode,
64
- clusterExpanded,
65
- });
66
-
67
- const onToggleGroup = useCallback((groupId: number, currentlyCollapsed: boolean) => {
68
- setCollapsedOverrides((prev) => ({ ...prev, [groupId]: !currentlyCollapsed }));
69
- }, []);
70
-
71
- const onToggleCluster = useCallback((key: string) => {
72
- setClusterExpanded((prev) => ({ ...prev, [key]: !(prev[key] ?? false) }));
73
- }, []);
74
-
75
- const clear = useCallback(() => {
76
- clearEntries();
77
- setCollapsedOverrides({});
78
- }, []);
79
-
80
- const levelSummary = levelMenuSummary(levelsMask);
81
- const isLevelCustom = levelSummary !== "All levels" && levelSummary !== "Default levels";
82
-
83
- return {
84
- query,
85
- setQuery,
86
- searchActive,
87
- setSearchActive,
88
- levelsMask,
89
- setLevelsMask,
90
- showTimestamps,
91
- setShowTimestamps,
92
- layoutMode,
93
- setLayoutMode,
94
- view,
95
- setView,
96
- rows,
97
- hiddenCount,
98
- onToggleGroup,
99
- onToggleCluster,
100
- clear,
101
- levelSummary,
102
- isLevelCustom,
103
- };
104
- }
@@ -1,337 +0,0 @@
1
- /**
2
- * useConsoleMessages — derives the displayed row model from raw captured
3
- * entries, mirroring DevTools `ConsoleView.updateMessageList`:
4
- *
5
- * - level + text filtering (consoleFilter)
6
- * - consecutive-identical collapse into a repeat-count badge (when timestamps
7
- * are off, matching `tryToCollapseMessages`)
8
- * - console.group / groupCollapsed nesting + collapse/expand
9
- *
10
- * Returns the visible rows plus the count of messages hidden by filters
11
- * ("N hidden"), and per-level totals for the toolbar.
12
- */
13
-
14
- import { useMemo, useSyncExternalStore } from "react";
15
- import {
16
- subscribe,
17
- getEntries,
18
- type ConsoleLogEntry,
19
- type ConsoleLevel,
20
- } from "../store/consoleLogStore";
21
- import {
22
- parseFilterQuery,
23
- shouldBeVisible,
24
- type LevelsMask,
25
- } from "./consoleFilter";
26
- import {
27
- sourceKey,
28
- sourceLabel,
29
- colorForKey,
30
- bracketGlyph,
31
- type GroupDimension,
32
- type Bracket,
33
- } from "./origin";
34
-
35
- /** How the origin gutter is laid out. */
36
- export type LayoutMode = "chrono" | "swimlane" | "cluster";
37
-
38
- /** One swimlane gutter cell. */
39
- export interface LaneCell {
40
- glyph: string;
41
- color?: string;
42
- }
43
-
44
- export interface DisplayRow {
45
- key: string;
46
- entry: ConsoleLogEntry;
47
- /** Group-nesting indentation level. */
48
- depth: number;
49
- /** >1 when consecutive identical messages were collapsed. */
50
- repeatCount: number;
51
- /** True for console.group / groupCollapsed header rows. */
52
- isGroupHeader: boolean;
53
- /** For headers: the group id (the start entry's id). */
54
- groupId?: number;
55
- /** For headers: whether the group is currently collapsed. */
56
- groupCollapsed?: boolean;
57
- /** The grouping key for this row's origin (per the active dimension). */
58
- sourceKey?: string | null;
59
- /** lnav-style single-column gutter glyph (┌ │ └ ─) — chrono/cluster modes. */
60
- bracketGlyph?: string;
61
- /** Multi-column swimlane gutter cells — swimlane mode. */
62
- lanes?: LaneCell[];
63
- /** Color for this row's origin bracket + label. */
64
- originColor?: string;
65
- /** Human label for the row's origin (component / function / call site). */
66
- originLabel?: string;
67
- /** Set on the synthetic header of a collapsed/large cluster (Grouped mode). */
68
- clusterHeader?: { key: string; count: number; expanded: boolean };
69
- }
70
-
71
- export interface ConsoleMessagesResult {
72
- rows: DisplayRow[];
73
- hiddenCount: number;
74
- levelCounts: Record<ConsoleLevel, number>;
75
- totalCount: number;
76
- }
77
-
78
- interface BuildOptions {
79
- levelsMask: LevelsMask;
80
- query: string;
81
- showTimestamps: boolean;
82
- /** groupId → user-overridden collapsed state. */
83
- collapsedOverrides: Record<number, boolean>;
84
- /** Which origin dimension to group/color rows by. */
85
- groupDimension: GroupDimension;
86
- /** Gutter layout: chronological brackets, swimlanes, or clustered. */
87
- layoutMode: LayoutMode;
88
- /** Grouped mode: which large clusters the user has expanded (key → true). */
89
- clusterExpanded: Record<string, boolean>;
90
- }
91
-
92
- const MAX_LANES = 5;
93
-
94
- /** Grouped mode collapses any source cluster larger than this into a header. */
95
- const CLUSTER_THRESHOLD = 5;
96
-
97
- /** First pass: tag each row with its origin source key, color, and label. */
98
- function computeSourceInfo(rows: DisplayRow[], dim: GroupDimension): void {
99
- for (const row of rows) {
100
- const key = sourceKey(row.entry.origin, dim);
101
- row.sourceKey = key;
102
- row.originColor = key ? colorForKey(key) : undefined;
103
- row.originLabel = dim === "off" ? undefined : sourceLabel(row.entry.origin, dim);
104
- }
105
- }
106
-
107
- /** lnav single-column gutter: bracket consecutive same-source runs. */
108
- function attachContiguousBrackets(rows: DisplayRow[]): void {
109
- for (let i = 0; i < rows.length; i++) {
110
- const key = rows[i].sourceKey;
111
- if (key == null) {
112
- rows[i].bracketGlyph = undefined;
113
- continue;
114
- }
115
- const sameAsPrev = i > 0 && rows[i - 1].sourceKey === key;
116
- const sameAsNext = i < rows.length - 1 && rows[i + 1].sourceKey === key;
117
- const bracket: Bracket =
118
- !sameAsPrev && !sameAsNext
119
- ? "single"
120
- : !sameAsPrev
121
- ? "top"
122
- : !sameAsNext
123
- ? "bottom"
124
- : "mid";
125
- rows[i].bracketGlyph = bracketGlyph(bracket);
126
- }
127
- }
128
-
129
- /**
130
- * Re-order rows so all rows of one source are contiguous (groups ordered by
131
- * first appearance, chronological within each). Clusters larger than
132
- * CLUSTER_THRESHOLD collapse into a single expandable header (Chrome-style),
133
- * unless the user has expanded that cluster.
134
- */
135
- function clusterRows(rows: DisplayRow[], expanded: Record<string, boolean>): DisplayRow[] {
136
- const order: string[] = [];
137
- const groups = new Map<string, DisplayRow[]>();
138
- for (const r of rows) {
139
- const k = r.sourceKey ?? "—";
140
- let g = groups.get(k);
141
- if (!g) {
142
- g = [];
143
- groups.set(k, g);
144
- order.push(k);
145
- }
146
- g.push(r);
147
- }
148
-
149
- const out: DisplayRow[] = [];
150
- for (const k of order) {
151
- const group = groups.get(k)!;
152
- for (const r of group) r.depth = 0; // clustering ignores console.group nesting
153
-
154
- const isLarge = group.length > CLUSTER_THRESHOLD;
155
- const isExpanded = expanded[k] ?? false;
156
-
157
- if (isLarge) {
158
- out.push({
159
- key: `cluster:${k}`,
160
- entry: group[0].entry,
161
- depth: 0,
162
- repeatCount: 1,
163
- isGroupHeader: false,
164
- sourceKey: k,
165
- originColor: group[0].originColor,
166
- originLabel: group[0].originLabel,
167
- clusterHeader: { key: k, count: group.length, expanded: isExpanded },
168
- });
169
- if (!isExpanded) continue;
170
- }
171
-
172
- group.forEach((r, i) => {
173
- const single = group.length === 1;
174
- const bracket: Bracket = single
175
- ? "single"
176
- : i === 0
177
- ? "top"
178
- : i === group.length - 1
179
- ? "bottom"
180
- : "mid";
181
- r.bracketGlyph = bracketGlyph(bracket);
182
- out.push(r);
183
- });
184
- }
185
- return out;
186
- }
187
-
188
- /** Git-graph swimlanes: each source gets a continuous vertical track so its
189
- * line stays unbroken when other sources interrupt it. */
190
- function attachSwimlanes(rows: DisplayRow[]): void {
191
- const first = new Map<string, number>();
192
- const last = new Map<string, number>();
193
- rows.forEach((r, i) => {
194
- const k = r.sourceKey;
195
- if (k == null) return;
196
- if (!first.has(k)) first.set(k, i);
197
- last.set(k, i);
198
- });
199
-
200
- // Assign each source a lane, reusing a lane once its occupant's run ended.
201
- const sources = [...first.keys()].sort((a, b) => first.get(a)! - first.get(b)!);
202
- const laneOf = new Map<string, number>();
203
- const laneEnd: number[] = [];
204
- for (const s of sources) {
205
- let lane = laneEnd.findIndex((end) => end < first.get(s)!);
206
- if (lane === -1) {
207
- lane = laneEnd.length < MAX_LANES ? laneEnd.length : MAX_LANES - 1;
208
- if (lane >= laneEnd.length) laneEnd.push(0);
209
- }
210
- laneOf.set(s, lane);
211
- laneEnd[lane] = Math.max(laneEnd[lane] ?? 0, last.get(s)!);
212
- }
213
- const laneCount = Math.max(1, Math.min(laneEnd.length, MAX_LANES));
214
-
215
- rows.forEach((r, i) => {
216
- const cells: LaneCell[] = Array.from({ length: laneCount }, () => ({ glyph: " " }));
217
- for (const s of sources) {
218
- const lane = laneOf.get(s)!;
219
- if (lane >= laneCount) continue;
220
- if (i >= first.get(s)! && i <= last.get(s)!) {
221
- cells[lane] = { glyph: r.sourceKey === s ? "●" : "│", color: colorForKey(s) };
222
- }
223
- }
224
- r.lanes = cells;
225
- });
226
- }
227
-
228
- interface OpenGroup {
229
- id: number;
230
- collapsed: boolean;
231
- }
232
-
233
- const GROUP_STARTS = new Set(["group", "groupCollapsed"]);
234
-
235
- function buildDisplayRows(
236
- entries: ConsoleLogEntry[],
237
- opts: BuildOptions
238
- ): ConsoleMessagesResult {
239
- const { levelsMask, query, showTimestamps, collapsedOverrides } = opts;
240
- const parsed = parseFilterQuery(query.trim());
241
-
242
- const rows: DisplayRow[] = [];
243
- const levelCounts: Record<ConsoleLevel, number> = {
244
- verbose: 0,
245
- info: 0,
246
- warning: 0,
247
- error: 0,
248
- };
249
- let hiddenCount = 0;
250
- const stack: OpenGroup[] = [];
251
-
252
- const anyAncestorCollapsed = () => stack.some((g) => g.collapsed);
253
-
254
- for (const entry of entries) {
255
- levelCounts[entry.level]++;
256
-
257
- if (entry.method === "groupEnd") {
258
- stack.pop();
259
- continue;
260
- }
261
-
262
- const isHeader = GROUP_STARTS.has(entry.method);
263
- const hiddenByGroup = anyAncestorCollapsed();
264
- const passesFilter = shouldBeVisible(entry, levelsMask, parsed);
265
-
266
- if (!passesFilter) {
267
- hiddenCount++;
268
- } else if (!hiddenByGroup) {
269
- // Repeat-count collapse: only when timestamps are hidden and the previous
270
- // visible row is an identical, non-header message at the same depth.
271
- const prev = rows[rows.length - 1];
272
- if (
273
- !showTimestamps &&
274
- !isHeader &&
275
- prev &&
276
- !prev.isGroupHeader &&
277
- prev.depth === stack.length &&
278
- prev.entry.method === entry.method &&
279
- prev.entry.message === entry.message
280
- ) {
281
- prev.repeatCount++;
282
- } else {
283
- rows.push({
284
- key: String(entry.id),
285
- entry,
286
- depth: stack.length,
287
- repeatCount: 1,
288
- isGroupHeader: isHeader,
289
- groupId: isHeader ? entry.id : undefined,
290
- groupCollapsed: undefined,
291
- });
292
- }
293
- }
294
-
295
- if (isHeader) {
296
- const defaultCollapsed = entry.method === "groupCollapsed";
297
- const collapsed = collapsedOverrides[entry.id] ?? defaultCollapsed;
298
- // Record the collapse state on the just-pushed header row (if visible).
299
- const headerRow = rows[rows.length - 1];
300
- if (headerRow && headerRow.groupId === entry.id) {
301
- headerRow.groupCollapsed = collapsed;
302
- }
303
- stack.push({ id: entry.id, collapsed: collapsed || hiddenByGroup });
304
- }
305
- }
306
-
307
- computeSourceInfo(rows, opts.groupDimension);
308
- let finalRows = rows;
309
- if (opts.groupDimension !== "off") {
310
- if (opts.layoutMode === "cluster") {
311
- finalRows = clusterRows(rows, opts.clusterExpanded);
312
- } else if (opts.layoutMode === "swimlane") {
313
- attachSwimlanes(rows);
314
- } else {
315
- attachContiguousBrackets(rows);
316
- }
317
- }
318
-
319
- return { rows: finalRows, hiddenCount, levelCounts, totalCount: entries.length };
320
- }
321
-
322
- export function useConsoleMessages(opts: BuildOptions): ConsoleMessagesResult {
323
- const entries = useSyncExternalStore(subscribe, getEntries, getEntries);
324
- return useMemo(
325
- () => buildDisplayRows(entries, opts),
326
- [
327
- entries,
328
- opts.levelsMask,
329
- opts.query,
330
- opts.showTimestamps,
331
- opts.collapsedOverrides,
332
- opts.groupDimension,
333
- opts.layoutMode,
334
- opts.clusterExpanded,
335
- ]
336
- );
337
- }
@@ -1,26 +0,0 @@
1
- import { useSyncExternalStore, useEffect } from "react";
2
- import {
3
- subscribe,
4
- getEntries,
5
- installConsoleCapture,
6
- type ConsoleLogEntry,
7
- } from "../store/consoleLogStore";
8
-
9
- export interface UseConsoleLogsResult {
10
- logs: ConsoleLogEntry[];
11
- }
12
-
13
- /**
14
- * Subscribe to captured console logs. Installs the console interceptor on mount
15
- * (idempotent) and re-renders whenever a new entry is captured or the buffer is
16
- * cleared.
17
- */
18
- export function useConsoleLogs(): UseConsoleLogsResult {
19
- useEffect(() => {
20
- installConsoleCapture();
21
- }, []);
22
-
23
- const logs = useSyncExternalStore(subscribe, getEntries, getEntries);
24
-
25
- return { logs };
26
- }
package/src/index.tsx DELETED
@@ -1,70 +0,0 @@
1
- /**
2
- * @buoy-gg/console
3
- *
4
- * Captures global `console.*` output and renders it as a Buoy devtool.
5
- *
6
- * This is the BASIC version: direct console patching + a flat list, used to
7
- * verify the capture pipeline end to end. Next step is a 1:1 port of Chrome
8
- * DevTools' console view.
9
- */
10
-
11
- // =============================================================================
12
- // PRESET (Primary entry point — pass to <FloatingDevTools apps={[...]} />)
13
- // =============================================================================
14
- export { consoleToolPreset, createConsoleTool } from "./preset";
15
-
16
- // =============================================================================
17
- // ROOT (Mount once at app root to capture logs from startup)
18
- // =============================================================================
19
- export { ConsoleRoot } from "./components/ConsoleRoot";
20
-
21
- // =============================================================================
22
- // COMPONENTS
23
- // =============================================================================
24
- export { ConsoleModal } from "./components/ConsoleModal";
25
- export { ConsoleIcon, CONSOLE_ICON_COLOR } from "./components/ConsoleIcon";
26
-
27
- // DevTools-style console panel (the 1:1 Chrome console port)
28
- export { ConsoleView } from "./devtools/ConsoleView";
29
- export { ConsoleMessageRow } from "./devtools/ConsoleMessageRow";
30
- export { ConsoleValue } from "./devtools/ConsoleValue";
31
- export { useConsoleMessages } from "./devtools/useConsoleMessages";
32
- export type { DisplayRow, ConsoleMessagesResult } from "./devtools/useConsoleMessages";
33
- export { consoleTheme, levelStyles } from "./devtools/theme";
34
-
35
- // Basic flat list (kept for simple/embedded use)
36
- export { ConsoleLogList } from "./components/ConsoleLogList";
37
-
38
- // =============================================================================
39
- // HOOK
40
- // =============================================================================
41
- export { useConsoleLogs } from "./hooks/useConsoleLogs";
42
- export type { UseConsoleLogsResult } from "./hooks/useConsoleLogs";
43
-
44
- // =============================================================================
45
- // EXTERNAL SYNC (device → desktop dashboard)
46
- // =============================================================================
47
- export { consoleSyncAdapter } from "./sync/consoleSyncAdapter";
48
-
49
- // =============================================================================
50
- // CAPTURE STORE (install/teardown + types)
51
- // =============================================================================
52
- export {
53
- installConsoleCapture,
54
- installFatalErrorCapture,
55
- uninstallConsoleCapture,
56
- clearEntries,
57
- getEntries,
58
- subscribe,
59
- replaceEntries,
60
- onClear,
61
- initConsolePersistence,
62
- getPreserveLog,
63
- setPreserveLog,
64
- subscribePreserveLog,
65
- } from "./store/consoleLogStore";
66
- export type {
67
- ConsoleLogEntry,
68
- ConsoleLevel,
69
- ConsoleMethod,
70
- } from "./store/consoleLogStore";