@tokentop/ttop 0.2.2 → 0.4.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.
- package/README.md +2 -5
- package/package.json +8 -5
- package/src/agents/aggregator.ts +39 -19
- package/src/agents/costing.ts +11 -9
- package/src/agents/index.ts +7 -9
- package/src/agents/types.ts +2 -2
- package/src/cli.ts +28 -23
- package/src/config/schema.test.ts +42 -40
- package/src/config/schema.ts +26 -26
- package/src/demo/simulator.ts +161 -122
- package/src/plugins/agents/index.ts +2 -2
- package/src/plugins/auth-sources.ts +42 -38
- package/src/plugins/lifecycle.ts +42 -38
- package/src/plugins/loader.test.ts +49 -49
- package/src/plugins/loader.ts +67 -63
- package/src/plugins/notification-bus.ts +21 -16
- package/src/plugins/notifications/index.ts +2 -2
- package/src/plugins/notifications/terminal-bell.ts +36 -23
- package/src/plugins/notifications/visual-flash.ts +31 -19
- package/src/plugins/npm-installer.ts +23 -25
- package/src/plugins/plugin-context-factory.ts +10 -10
- package/src/plugins/plugin-host.ts +12 -5
- package/src/plugins/providers/anthropic.ts +64 -57
- package/src/plugins/providers/antigravity.ts +81 -69
- package/src/plugins/providers/codex.ts +42 -42
- package/src/plugins/providers/gemini.ts +106 -87
- package/src/plugins/providers/github-copilot.ts +49 -45
- package/src/plugins/providers/index.ts +10 -12
- package/src/plugins/providers/minimax.ts +50 -44
- package/src/plugins/providers/openai-api.ts +82 -62
- package/src/plugins/providers/opencode-zen.ts +46 -39
- package/src/plugins/providers/perplexity.ts +55 -46
- package/src/plugins/providers/zai.ts +68 -57
- package/src/plugins/registry.ts +80 -71
- package/src/plugins/sandbox-guard.test.ts +45 -48
- package/src/plugins/sandbox-guard.ts +20 -25
- package/src/plugins/sandbox.ts +26 -48
- package/src/plugins/themes/catppuccin-latte.ts +29 -29
- package/src/plugins/themes/catppuccin-mocha.ts +29 -29
- package/src/plugins/themes/claude-code.ts +29 -29
- package/src/plugins/themes/dracula.ts +29 -29
- package/src/plugins/themes/github-light.ts +29 -29
- package/src/plugins/themes/gruvbox-dark.ts +29 -29
- package/src/plugins/themes/gruvbox-light.ts +29 -29
- package/src/plugins/themes/index.ts +15 -15
- package/src/plugins/themes/kanagawa.ts +29 -29
- package/src/plugins/themes/nord.ts +29 -29
- package/src/plugins/themes/one-dark.ts +29 -29
- package/src/plugins/themes/opencode.ts +31 -31
- package/src/plugins/themes/rose-pine-dawn.ts +29 -29
- package/src/plugins/themes/rose-pine.ts +29 -29
- package/src/plugins/themes/solarized-light.ts +29 -29
- package/src/plugins/themes/tokyo-night.ts +29 -29
- package/src/plugins/types/agent.ts +5 -5
- package/src/plugins/types/base.ts +35 -24
- package/src/plugins/types/index.ts +9 -9
- package/src/plugins/types/notification.ts +12 -12
- package/src/plugins/types/provider.ts +12 -8
- package/src/plugins/types/theme.ts +31 -23
- package/src/plugins/update-checker.ts +6 -6
- package/src/pricing/estimator.test.ts +39 -44
- package/src/pricing/estimator.ts +7 -13
- package/src/pricing/fallback.test.ts +30 -34
- package/src/pricing/fallback.ts +54 -27
- package/src/pricing/index.ts +18 -12
- package/src/pricing/models-dev.ts +6 -6
- package/src/storage/database.ts +56 -46
- package/src/storage/db.ts +12 -12
- package/src/storage/index.ts +44 -45
- package/src/storage/migrations/index.ts +2 -2
- package/src/storage/paths.ts +13 -13
- package/src/storage/persistence-service.ts +206 -0
- package/src/storage/repos/agentSessions.ts +33 -23
- package/src/storage/repos/providerSnapshots.ts +5 -5
- package/src/storage/repos/usageEvents.ts +40 -28
- package/src/storage/retention.ts +146 -0
- package/src/storage/types.ts +5 -5
- package/src/tui/App.tsx +180 -137
- package/src/tui/components/CommandPalette.tsx +46 -34
- package/src/tui/components/DebugConsole.tsx +39 -33
- package/src/tui/components/DebugPanel.tsx +314 -183
- package/src/tui/components/FooterHints.tsx +2 -2
- package/src/tui/components/GhostProviderCard.tsx +37 -35
- package/src/tui/components/Header.tsx +68 -35
- package/src/tui/components/HelpOverlay.tsx +99 -29
- package/src/tui/components/InlineGauge.tsx +10 -7
- package/src/tui/components/InlineSparkline.tsx +17 -11
- package/src/tui/components/KpiStrip.tsx +128 -98
- package/src/tui/components/LimitGauge.tsx +96 -59
- package/src/tui/components/ModalBackdrop.tsx +4 -4
- package/src/tui/components/ProviderAggregateStrip.tsx +69 -38
- package/src/tui/components/ProviderCard.tsx +232 -226
- package/src/tui/components/ProviderDetailPanel.tsx +133 -58
- package/src/tui/components/ProviderLimitsPanel.tsx +126 -89
- package/src/tui/components/ProvidersList.tsx +215 -126
- package/src/tui/components/SessionDetailsDrawer.tsx +191 -94
- package/src/tui/components/SessionsTable.tsx +320 -169
- package/src/tui/components/SettingsModal.tsx +593 -269
- package/src/tui/components/SidebarBreakdown.tsx +30 -16
- package/src/tui/components/Skeleton.tsx +24 -17
- package/src/tui/components/SmartSidebar.tsx +360 -258
- package/src/tui/components/Sparkline.tsx +64 -52
- package/src/tui/components/Spinner.tsx +3 -3
- package/src/tui/components/StatusBar.tsx +27 -27
- package/src/tui/components/ThemePicker.tsx +67 -66
- package/src/tui/components/Toast.tsx +9 -6
- package/src/tui/components/UsageGauge.tsx +10 -13
- package/src/tui/components/index.ts +5 -5
- package/src/tui/contexts/AgentSessionContext.tsx +164 -151
- package/src/tui/contexts/ConfigContext.tsx +15 -13
- package/src/tui/contexts/DashboardRuntimeContext.tsx +26 -26
- package/src/tui/contexts/DemoModeContext.tsx +22 -15
- package/src/tui/contexts/DrawerContext.tsx +11 -9
- package/src/tui/contexts/InputContext.tsx +2 -2
- package/src/tui/contexts/LogContext.tsx +62 -47
- package/src/tui/contexts/PluginContext.tsx +238 -183
- package/src/tui/contexts/RealTimeActivityContext.tsx +70 -36
- package/src/tui/contexts/StorageContext.tsx +95 -149
- package/src/tui/contexts/ThemeContext.tsx +21 -16
- package/src/tui/contexts/TimeWindowContext.tsx +72 -61
- package/src/tui/contexts/ToastContext.tsx +10 -7
- package/src/tui/contexts/index.ts +9 -5
- package/src/tui/createApp.tsx +8 -12
- package/src/tui/debug/captureFrame.ts +25 -28
- package/src/tui/debug/snapshot.tsx +179 -162
- package/src/tui/driver/assertions.ts +30 -22
- package/src/tui/driver/cli.ts +231 -221
- package/src/tui/driver/coverage.ts +27 -44
- package/src/tui/driver/demo-snapshot.ts +33 -33
- package/src/tui/driver/diff.ts +26 -32
- package/src/tui/driver/driver.ts +43 -48
- package/src/tui/driver/index.ts +36 -40
- package/src/tui/driver/recorder.ts +37 -48
- package/src/tui/driver/test-workflow.ts +51 -51
- package/src/tui/driver/test.ts +25 -25
- package/src/tui/hooks/index.ts +24 -20
- package/src/tui/hooks/useAnimatedValue.ts +13 -18
- package/src/tui/hooks/useDashboardKeyboard.ts +118 -98
- package/src/tui/hooks/useDashboardState.ts +26 -24
- package/src/tui/hooks/useEmaActivity.ts +82 -74
- package/src/tui/hooks/useEntranceAnimation.ts +11 -11
- package/src/tui/hooks/useExitAnimation.ts +7 -3
- package/src/tui/hooks/usePulse.ts +4 -4
- package/src/tui/hooks/useSafeRenderer.ts +5 -5
- package/src/tui/hooks/useSessionEnrichedProviders.ts +150 -0
- package/src/tui/hooks/useValueFlash.ts +10 -14
- package/src/tui/index.tsx +29 -6
- package/src/tui/shutdown.ts +19 -0
- package/src/tui/utils/providerColor.ts +1 -1
- package/src/tui/views/Dashboard.tsx +164 -102
- package/src/tui/views/HistoricalTrendsView.tsx +579 -250
- package/src/tui/views/ProjectsView.tsx +495 -243
- package/src/tui/views/RealTimeDashboard.tsx +176 -116
- package/src/tui/views/SettingsView.tsx +225 -189
- package/src/tui/views/index.ts +2 -2
- package/src/types/opentui-test-utils.d.ts +7 -7
- package/src/utils/clipboard.ts +29 -29
- package/src/utils/currency.test.ts +53 -53
- package/src/utils/currency.ts +35 -32
- package/src/version.ts +1 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import { createContext,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { createContext, type ReactNode, useContext, useEffect, useRef, useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
type ActivityDebugData,
|
|
4
|
+
type ActivityState,
|
|
5
|
+
useEmaActivity,
|
|
6
|
+
} from "../hooks/useEmaActivity.ts";
|
|
7
|
+
import { useAgentSessions } from "./AgentSessionContext.tsx";
|
|
8
|
+
import { useRealTimeActivity } from "./RealTimeActivityContext.tsx";
|
|
9
|
+
import { useTimeWindow } from "./TimeWindowContext.tsx";
|
|
6
10
|
|
|
7
11
|
interface Deltas {
|
|
8
12
|
cost: number;
|
|
@@ -13,10 +17,10 @@ interface Deltas {
|
|
|
13
17
|
interface DashboardRuntimeContextValue {
|
|
14
18
|
activity: ActivityState;
|
|
15
19
|
sparkData: number[];
|
|
16
|
-
|
|
20
|
+
|
|
17
21
|
deltas: Deltas;
|
|
18
|
-
history: Array<{time: number
|
|
19
|
-
|
|
22
|
+
history: Array<{ time: number; cost: number; tokens: number }>;
|
|
23
|
+
|
|
20
24
|
debugDataRef: React.MutableRefObject<ActivityDebugData>;
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -26,48 +30,46 @@ export function DashboardRuntimeProvider({ children }: { children: ReactNode })
|
|
|
26
30
|
const { sessions: agentSessions, isLoading } = useAgentSessions();
|
|
27
31
|
const { windowMs } = useTimeWindow();
|
|
28
32
|
const { subscribe } = useRealTimeActivity();
|
|
29
|
-
|
|
33
|
+
|
|
30
34
|
const totalTokens = agentSessions.reduce((sum, s) => sum + s.totals.input + s.totals.output, 0);
|
|
31
35
|
const { activity, sparkData, debugDataRef, injectDelta } = useEmaActivity(totalTokens);
|
|
32
|
-
|
|
36
|
+
|
|
33
37
|
useEffect(() => {
|
|
34
38
|
return subscribe((delta, _timestamp) => {
|
|
35
39
|
injectDelta(delta);
|
|
36
40
|
});
|
|
37
41
|
}, [subscribe, injectDelta]);
|
|
38
42
|
|
|
39
|
-
const historyRef = useRef<{time: number
|
|
43
|
+
const historyRef = useRef<{ time: number; cost: number; tokens: number }[]>([]);
|
|
40
44
|
const [deltas, setDeltas] = useState<Deltas>({ cost: 0, tokens: 0, windowSec: 0 });
|
|
41
45
|
|
|
42
46
|
useEffect(() => {
|
|
43
47
|
if (isLoading || agentSessions.length === 0) return;
|
|
44
|
-
|
|
48
|
+
|
|
45
49
|
const totalCost = agentSessions.reduce((sum, s) => sum + (s.totalCostUsd ?? 0), 0);
|
|
46
50
|
const currentTime = Date.now();
|
|
47
|
-
|
|
51
|
+
|
|
48
52
|
const lastEntry = historyRef.current[historyRef.current.length - 1];
|
|
49
|
-
const isDiscontinuity =
|
|
50
|
-
totalTokens < lastEntry.tokens ||
|
|
51
|
-
totalTokens > lastEntry.tokens * 2
|
|
52
|
-
);
|
|
53
|
+
const isDiscontinuity =
|
|
54
|
+
lastEntry && (totalTokens < lastEntry.tokens || totalTokens > lastEntry.tokens * 2);
|
|
53
55
|
if (isDiscontinuity) {
|
|
54
56
|
historyRef.current = [];
|
|
55
57
|
}
|
|
56
|
-
|
|
58
|
+
|
|
57
59
|
historyRef.current.push({ time: currentTime, cost: totalCost, tokens: totalTokens });
|
|
58
|
-
|
|
60
|
+
|
|
59
61
|
if (historyRef.current.length > 300) historyRef.current.shift();
|
|
60
62
|
|
|
61
63
|
const targetTime = windowMs !== null ? currentTime - windowMs : 0;
|
|
62
64
|
let baseline = historyRef.current[0];
|
|
63
|
-
|
|
65
|
+
|
|
64
66
|
for (let i = historyRef.current.length - 1; i >= 0; i--) {
|
|
65
67
|
if (historyRef.current[i]!.time <= targetTime) {
|
|
66
68
|
baseline = historyRef.current[i];
|
|
67
69
|
break;
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
|
-
|
|
72
|
+
|
|
71
73
|
if (baseline) {
|
|
72
74
|
const windowSec = (currentTime - baseline.time) / 1000;
|
|
73
75
|
setDeltas({
|
|
@@ -83,20 +85,18 @@ export function DashboardRuntimeProvider({ children }: { children: ReactNode })
|
|
|
83
85
|
sparkData,
|
|
84
86
|
deltas,
|
|
85
87
|
history: historyRef.current,
|
|
86
|
-
debugDataRef
|
|
88
|
+
debugDataRef,
|
|
87
89
|
};
|
|
88
90
|
|
|
89
91
|
return (
|
|
90
|
-
<DashboardRuntimeContext.Provider value={value}>
|
|
91
|
-
{children}
|
|
92
|
-
</DashboardRuntimeContext.Provider>
|
|
92
|
+
<DashboardRuntimeContext.Provider value={value}>{children}</DashboardRuntimeContext.Provider>
|
|
93
93
|
);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
export function useDashboardRuntime() {
|
|
97
97
|
const context = useContext(DashboardRuntimeContext);
|
|
98
98
|
if (!context) {
|
|
99
|
-
throw new Error(
|
|
99
|
+
throw new Error("useDashboardRuntime must be used within DashboardRuntimeProvider");
|
|
100
100
|
}
|
|
101
101
|
return context;
|
|
102
102
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext, useContext, useMemo
|
|
2
|
-
import {
|
|
1
|
+
import { createContext, type ReactNode, useContext, useMemo } from "react";
|
|
2
|
+
import { type DemoPreset, DemoSimulator, type DemoSimulatorOptions } from "@/demo/simulator.ts";
|
|
3
3
|
|
|
4
4
|
interface DemoModeContextValue {
|
|
5
5
|
demoMode: boolean;
|
|
@@ -22,7 +22,10 @@ interface DemoModeProviderProps {
|
|
|
22
22
|
children: ReactNode;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
function buildSimulatorOptions(
|
|
25
|
+
function buildSimulatorOptions(
|
|
26
|
+
seed: number | undefined,
|
|
27
|
+
preset: DemoPreset | undefined,
|
|
28
|
+
): DemoSimulatorOptions {
|
|
26
29
|
if (seed !== undefined && preset !== undefined) {
|
|
27
30
|
return { seed, preset };
|
|
28
31
|
}
|
|
@@ -35,24 +38,28 @@ function buildSimulatorOptions(seed: number | undefined, preset: DemoPreset | un
|
|
|
35
38
|
return {};
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
export function DemoModeProvider({
|
|
41
|
+
export function DemoModeProvider({
|
|
42
|
+
demoMode,
|
|
43
|
+
demoSeed,
|
|
44
|
+
demoPreset,
|
|
45
|
+
children,
|
|
46
|
+
}: DemoModeProviderProps) {
|
|
39
47
|
const simulator = useMemo(() => {
|
|
40
48
|
if (!demoMode) return null;
|
|
41
49
|
return new DemoSimulator(buildSimulatorOptions(demoSeed, demoPreset));
|
|
42
50
|
}, [demoMode, demoSeed, demoPreset]);
|
|
43
51
|
|
|
44
|
-
const value = useMemo<DemoModeContextValue>(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
<DemoModeContext.Provider value={value}>
|
|
53
|
-
{children}
|
|
54
|
-
</DemoModeContext.Provider>
|
|
52
|
+
const value = useMemo<DemoModeContextValue>(
|
|
53
|
+
() => ({
|
|
54
|
+
demoMode,
|
|
55
|
+
simulator,
|
|
56
|
+
seed: simulator?.getSeed() ?? null,
|
|
57
|
+
preset: simulator?.getPreset() ?? null,
|
|
58
|
+
}),
|
|
59
|
+
[demoMode, simulator],
|
|
55
60
|
);
|
|
61
|
+
|
|
62
|
+
return <DemoModeContext.Provider value={value}>{children}</DemoModeContext.Provider>;
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
export function useDemoMode(): DemoModeContextValue {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createContext,
|
|
2
|
-
import type { AgentSessionAggregate } from
|
|
1
|
+
import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
|
|
2
|
+
import type { AgentSessionAggregate } from "@/agents/types.ts";
|
|
3
3
|
|
|
4
4
|
interface DrawerContextValue {
|
|
5
5
|
selectedSession: AgentSessionAggregate | null;
|
|
@@ -22,12 +22,14 @@ export function DrawerProvider({ children }: { children: ReactNode }) {
|
|
|
22
22
|
}, []);
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
<DrawerContext.Provider
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
<DrawerContext.Provider
|
|
26
|
+
value={{
|
|
27
|
+
selectedSession,
|
|
28
|
+
showDrawer,
|
|
29
|
+
hideDrawer,
|
|
30
|
+
isOpen: selectedSession !== null,
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
31
33
|
{children}
|
|
32
34
|
</DrawerContext.Provider>
|
|
33
35
|
);
|
|
@@ -36,7 +38,7 @@ export function DrawerProvider({ children }: { children: ReactNode }) {
|
|
|
36
38
|
export function useDrawer(): DrawerContextValue {
|
|
37
39
|
const context = useContext(DrawerContext);
|
|
38
40
|
if (!context) {
|
|
39
|
-
throw new Error(
|
|
41
|
+
throw new Error("useDrawer must be used within DrawerProvider");
|
|
40
42
|
}
|
|
41
43
|
return context;
|
|
42
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
|
|
2
2
|
|
|
3
3
|
interface InputContextType {
|
|
4
4
|
isInputFocused: boolean;
|
|
@@ -24,7 +24,7 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
|
|
24
24
|
export function useInputFocus() {
|
|
25
25
|
const context = useContext(InputContext);
|
|
26
26
|
if (context === undefined) {
|
|
27
|
-
throw new Error(
|
|
27
|
+
throw new Error("useInputFocus must be used within an InputProvider");
|
|
28
28
|
}
|
|
29
29
|
return context;
|
|
30
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, type ReactNode, useCallback, useContext, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export type LogLevel =
|
|
3
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
4
4
|
|
|
5
5
|
export interface LogEntry {
|
|
6
6
|
id: number;
|
|
@@ -40,46 +40,64 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
|
|
|
40
40
|
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
41
41
|
const [isConsoleOpen, setIsConsoleOpen] = useState(false);
|
|
42
42
|
|
|
43
|
-
const addLog = useCallback(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
const addLog = useCallback(
|
|
44
|
+
(level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
|
|
45
|
+
if (level === "debug" && !debugEnabled) return;
|
|
46
|
+
|
|
47
|
+
const entry: LogEntry = {
|
|
48
|
+
id: ++logIdCounter,
|
|
49
|
+
level,
|
|
50
|
+
message,
|
|
51
|
+
timestamp: Date.now(),
|
|
52
|
+
...(data !== undefined ? { data } : {}),
|
|
53
|
+
...(source !== undefined ? { source } : {}),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
setLogs((prev) => {
|
|
57
|
+
const next = [...prev, entry];
|
|
58
|
+
if (next.length > MAX_LOGS) {
|
|
59
|
+
return next.slice(-MAX_LOGS);
|
|
60
|
+
}
|
|
61
|
+
return next;
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
[debugEnabled],
|
|
65
|
+
);
|
|
63
66
|
|
|
64
|
-
const log = useCallback(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
const log = useCallback(
|
|
68
|
+
(level: LogLevel, message: string, data?: Record<string, unknown>, source?: string) => {
|
|
69
|
+
addLog(level, message, data, source);
|
|
70
|
+
},
|
|
71
|
+
[addLog],
|
|
72
|
+
);
|
|
67
73
|
|
|
68
|
-
const debug = useCallback(
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
const debug = useCallback(
|
|
75
|
+
(message: string, data?: Record<string, unknown>, source?: string) => {
|
|
76
|
+
addLog("debug", message, data, source);
|
|
77
|
+
},
|
|
78
|
+
[addLog],
|
|
79
|
+
);
|
|
71
80
|
|
|
72
|
-
const info = useCallback(
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
const info = useCallback(
|
|
82
|
+
(message: string, data?: Record<string, unknown>, source?: string) => {
|
|
83
|
+
addLog("info", message, data, source);
|
|
84
|
+
},
|
|
85
|
+
[addLog],
|
|
86
|
+
);
|
|
75
87
|
|
|
76
|
-
const warn = useCallback(
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
const warn = useCallback(
|
|
89
|
+
(message: string, data?: Record<string, unknown>, source?: string) => {
|
|
90
|
+
addLog("warn", message, data, source);
|
|
91
|
+
},
|
|
92
|
+
[addLog],
|
|
93
|
+
);
|
|
79
94
|
|
|
80
|
-
const error = useCallback(
|
|
81
|
-
|
|
82
|
-
|
|
95
|
+
const error = useCallback(
|
|
96
|
+
(message: string, data?: Record<string, unknown>, source?: string) => {
|
|
97
|
+
addLog("error", message, data, source);
|
|
98
|
+
},
|
|
99
|
+
[addLog],
|
|
100
|
+
);
|
|
83
101
|
|
|
84
102
|
const clearLogs = useCallback(() => {
|
|
85
103
|
setLogs([]);
|
|
@@ -100,11 +118,11 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
|
|
|
100
118
|
const exportLogs = useCallback(() => {
|
|
101
119
|
const lines = logs.map((entry) => {
|
|
102
120
|
const time = new Date(entry.timestamp).toISOString();
|
|
103
|
-
const src = entry.source ? `[${entry.source}]` :
|
|
104
|
-
const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` :
|
|
121
|
+
const src = entry.source ? `[${entry.source}]` : "";
|
|
122
|
+
const dataStr = entry.data ? ` ${JSON.stringify(entry.data)}` : "";
|
|
105
123
|
return `${time} ${entry.level.toUpperCase().padEnd(5)} ${src} ${entry.message}${dataStr}`;
|
|
106
124
|
});
|
|
107
|
-
return lines.join(
|
|
125
|
+
return lines.join("\n");
|
|
108
126
|
}, [logs]);
|
|
109
127
|
|
|
110
128
|
const value: LogContextValue = {
|
|
@@ -122,17 +140,13 @@ export function LogProvider({ children, debugEnabled = true }: LogProviderProps)
|
|
|
122
140
|
exportLogs,
|
|
123
141
|
};
|
|
124
142
|
|
|
125
|
-
return
|
|
126
|
-
<LogContext.Provider value={value}>
|
|
127
|
-
{children}
|
|
128
|
-
</LogContext.Provider>
|
|
129
|
-
);
|
|
143
|
+
return <LogContext.Provider value={value}>{children}</LogContext.Provider>;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
export function useLogs(): LogContextValue {
|
|
133
147
|
const context = useContext(LogContext);
|
|
134
148
|
if (!context) {
|
|
135
|
-
throw new Error(
|
|
149
|
+
throw new Error("useLogs must be used within LogProvider");
|
|
136
150
|
}
|
|
137
151
|
return context;
|
|
138
152
|
}
|
|
@@ -141,7 +155,8 @@ export function useLogger(source: string) {
|
|
|
141
155
|
const { log, debug, info, warn, error } = useLogs();
|
|
142
156
|
|
|
143
157
|
return {
|
|
144
|
-
log: (level: LogLevel, message: string, data?: Record<string, unknown>) =>
|
|
158
|
+
log: (level: LogLevel, message: string, data?: Record<string, unknown>) =>
|
|
159
|
+
log(level, message, data, source),
|
|
145
160
|
debug: (message: string, data?: Record<string, unknown>) => debug(message, data, source),
|
|
146
161
|
info: (message: string, data?: Record<string, unknown>) => info(message, data, source),
|
|
147
162
|
warn: (message: string, data?: Record<string, unknown>) => warn(message, data, source),
|