mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
Add API error monitoring tests and error context snapshots for various browsers
- Created error context snapshots for Firefox, WebKit, and Chromium to capture UI state during API error monitoring. - Implemented e2e tests for API error detection, including console errors, failed API requests, and proxy errors. - Added functionality to ignore specific API patterns in monitoring assertions. - Ensured tests validate the monitoring system's ability to detect and report errors effectively.
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { persist, devtools } from 'zustand/middleware';
|
||||
import type { AppStore, Theme, Locale, ViewMode, SessionFilter, LiteTaskType } from '../types/store';
|
||||
import type { AppStore, Theme, ColorScheme, Locale, ViewMode, SessionFilter, LiteTaskType } from '../types/store';
|
||||
import { getInitialLocale, updateIntl } from '../lib/i18n';
|
||||
import { getThemeId } from '../lib/theme';
|
||||
|
||||
// Helper to resolve system theme
|
||||
const getSystemTheme = (): 'light' | 'dark' => {
|
||||
@@ -27,6 +28,7 @@ const initialState = {
|
||||
// Theme
|
||||
theme: 'system' as Theme,
|
||||
resolvedTheme: 'light' as 'light' | 'dark',
|
||||
colorScheme: 'blue' as ColorScheme, // New: default to blue scheme
|
||||
|
||||
// Locale
|
||||
locale: getInitialLocale() as Locale,
|
||||
@@ -61,9 +63,23 @@ export const useAppStore = create<AppStore>()(
|
||||
|
||||
// Apply theme to document
|
||||
if (typeof document !== 'undefined') {
|
||||
const { colorScheme } = get();
|
||||
const themeId = getThemeId(colorScheme, resolved);
|
||||
document.documentElement.classList.remove('light', 'dark');
|
||||
document.documentElement.classList.add(resolved);
|
||||
document.documentElement.setAttribute('data-theme', resolved);
|
||||
document.documentElement.setAttribute('data-theme', themeId);
|
||||
}
|
||||
},
|
||||
|
||||
setColorScheme: (colorScheme: ColorScheme) => {
|
||||
set({ colorScheme }, false, 'setColorScheme');
|
||||
|
||||
// Apply color scheme to document
|
||||
if (typeof document !== 'undefined') {
|
||||
const { resolvedTheme } = get();
|
||||
const themeId = getThemeId(colorScheme, resolvedTheme);
|
||||
document.documentElement.setAttribute('data-theme', themeId);
|
||||
document.documentElement.setAttribute('data-color-scheme', colorScheme);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -131,6 +147,7 @@ export const useAppStore = create<AppStore>()(
|
||||
// Only persist theme and locale preferences
|
||||
partialize: (state) => ({
|
||||
theme: state.theme,
|
||||
colorScheme: state.colorScheme,
|
||||
locale: state.locale,
|
||||
sidebarCollapsed: state.sidebarCollapsed,
|
||||
}),
|
||||
@@ -139,10 +156,11 @@ export const useAppStore = create<AppStore>()(
|
||||
if (state) {
|
||||
const resolved = resolveTheme(state.theme);
|
||||
state.resolvedTheme = resolved;
|
||||
const themeId = getThemeId(state.colorScheme, resolved);
|
||||
if (typeof document !== 'undefined') {
|
||||
document.documentElement.classList.remove('light', 'dark');
|
||||
document.documentElement.classList.add(resolved);
|
||||
document.documentElement.setAttribute('data-theme', resolved);
|
||||
document.documentElement.setAttribute('data-theme', themeId);
|
||||
}
|
||||
}
|
||||
// Apply locale on rehydration
|
||||
@@ -164,9 +182,10 @@ if (typeof window !== 'undefined') {
|
||||
if (state.theme === 'system') {
|
||||
const resolved = getSystemTheme();
|
||||
useAppStore.setState({ resolvedTheme: resolved });
|
||||
const themeId = getThemeId(state.colorScheme, resolved);
|
||||
document.documentElement.classList.remove('light', 'dark');
|
||||
document.documentElement.classList.add(resolved);
|
||||
document.documentElement.setAttribute('data-theme', resolved);
|
||||
document.documentElement.setAttribute('data-theme', themeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
223
ccw/frontend/src/stores/cliStreamStore.ts
Normal file
223
ccw/frontend/src/stores/cliStreamStore.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
// ========================================
|
||||
// CLI Stream Store
|
||||
// ========================================
|
||||
// Zustand store for managing CLI streaming output
|
||||
|
||||
import { create } from 'zustand';
|
||||
import { devtools } from 'zustand/middleware';
|
||||
|
||||
// ========== Types ==========
|
||||
|
||||
/**
|
||||
* Output line type for CLI streaming
|
||||
*/
|
||||
export interface CliOutputLine {
|
||||
type: 'stdout' | 'stderr' | 'metadata' | 'thought' | 'system' | 'tool_call';
|
||||
content: string;
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI execution status
|
||||
*/
|
||||
export type CliExecutionStatus = 'running' | 'completed' | 'error';
|
||||
|
||||
/**
|
||||
* CLI execution state
|
||||
*/
|
||||
export interface CliExecutionState {
|
||||
tool: string;
|
||||
mode: string;
|
||||
status: CliExecutionStatus;
|
||||
output: CliOutputLine[];
|
||||
startTime: number;
|
||||
endTime?: number;
|
||||
recovered?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* CLI stream state interface
|
||||
*/
|
||||
interface CliStreamState {
|
||||
outputs: Record<string, CliOutputLine[]>;
|
||||
executions: Record<string, CliExecutionState>;
|
||||
currentExecutionId: string | null;
|
||||
|
||||
// Legacy methods
|
||||
addOutput: (executionId: string, line: CliOutputLine) => void;
|
||||
clearOutputs: (executionId: string) => void;
|
||||
getOutputs: (executionId: string) => CliOutputLine[];
|
||||
|
||||
// Multi-execution methods
|
||||
getAllExecutions: () => CliExecutionState[];
|
||||
upsertExecution: (executionId: string, exec: Partial<CliExecutionState> & { tool?: string; mode?: string }) => void;
|
||||
removeExecution: (executionId: string) => void;
|
||||
setCurrentExecution: (executionId: string | null) => void;
|
||||
}
|
||||
|
||||
// ========== Constants ==========
|
||||
|
||||
/**
|
||||
* Maximum number of output lines to keep per execution
|
||||
* Prevents memory issues for long-running executions
|
||||
*/
|
||||
const MAX_OUTPUT_LINES = 5000;
|
||||
|
||||
// ========== Store ==========
|
||||
|
||||
/**
|
||||
* Zustand store for CLI streaming output
|
||||
*
|
||||
* @remarks
|
||||
* Manages streaming output from CLI executions in memory.
|
||||
* Each execution has its own output array, accessible by executionId.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const addOutput = useCliStreamStore(state => state.addOutput);
|
||||
* addOutput('exec-123', { type: 'stdout', content: 'Hello', timestamp: Date.now() });
|
||||
* ```
|
||||
*/
|
||||
export const useCliStreamStore = create<CliStreamState>()(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
outputs: {},
|
||||
executions: {},
|
||||
currentExecutionId: null,
|
||||
|
||||
addOutput: (executionId: string, line: CliOutputLine) => {
|
||||
set((state) => {
|
||||
const current = state.outputs[executionId] || [];
|
||||
const updated = [...current, line];
|
||||
|
||||
// Trim if too long to prevent memory issues
|
||||
if (updated.length > MAX_OUTPUT_LINES) {
|
||||
return {
|
||||
outputs: {
|
||||
...state.outputs,
|
||||
[executionId]: updated.slice(-MAX_OUTPUT_LINES),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
outputs: {
|
||||
...state.outputs,
|
||||
[executionId]: updated,
|
||||
},
|
||||
};
|
||||
}, false, 'cliStream/addOutput');
|
||||
|
||||
// Also update in executions
|
||||
const state = get();
|
||||
if (state.executions[executionId]) {
|
||||
set((state) => ({
|
||||
executions: {
|
||||
...state.executions,
|
||||
[executionId]: {
|
||||
...state.executions[executionId],
|
||||
output: [...state.executions[executionId].output, line],
|
||||
},
|
||||
},
|
||||
}), false, 'cliStream/updateExecutionOutput');
|
||||
}
|
||||
},
|
||||
|
||||
clearOutputs: (executionId: string) => {
|
||||
set(
|
||||
(state) => ({
|
||||
outputs: {
|
||||
...state.outputs,
|
||||
[executionId]: [],
|
||||
},
|
||||
}),
|
||||
false,
|
||||
'cliStream/clearOutputs'
|
||||
);
|
||||
},
|
||||
|
||||
getOutputs: (executionId: string) => {
|
||||
return get().outputs[executionId] || [];
|
||||
},
|
||||
|
||||
// Multi-execution methods
|
||||
getAllExecutions: () => {
|
||||
return Object.values(get().executions);
|
||||
},
|
||||
|
||||
upsertExecution: (executionId: string, exec: Partial<CliExecutionState> & { tool?: string; mode?: string }) => {
|
||||
set((state) => {
|
||||
const existing = state.executions[executionId];
|
||||
const updated: CliExecutionState = existing
|
||||
? { ...existing, ...exec }
|
||||
: {
|
||||
tool: exec.tool || 'cli',
|
||||
mode: exec.mode || 'analysis',
|
||||
status: exec.status || 'running',
|
||||
output: exec.output || [],
|
||||
startTime: exec.startTime || Date.now(),
|
||||
endTime: exec.endTime,
|
||||
recovered: exec.recovered,
|
||||
};
|
||||
|
||||
return {
|
||||
executions: {
|
||||
...state.executions,
|
||||
[executionId]: updated,
|
||||
},
|
||||
};
|
||||
}, false, 'cliStream/upsertExecution');
|
||||
},
|
||||
|
||||
removeExecution: (executionId: string) => {
|
||||
set((state) => {
|
||||
const newExecutions = { ...state.executions };
|
||||
delete newExecutions[executionId];
|
||||
return {
|
||||
executions: newExecutions,
|
||||
currentExecutionId: state.currentExecutionId === executionId ? null : state.currentExecutionId,
|
||||
};
|
||||
}, false, 'cliStream/removeExecution');
|
||||
},
|
||||
|
||||
setCurrentExecution: (executionId: string | null) => {
|
||||
set({ currentExecutionId: executionId }, false, 'cliStream/setCurrentExecution');
|
||||
},
|
||||
}),
|
||||
{ name: 'CliStreamStore' }
|
||||
)
|
||||
);
|
||||
|
||||
// ========== Selectors ==========
|
||||
|
||||
/**
|
||||
* Selector for getting outputs by execution ID
|
||||
*/
|
||||
export const selectOutputs = (state: CliStreamState, executionId: string) =>
|
||||
state.outputs[executionId] || [];
|
||||
|
||||
/**
|
||||
* Selector for getting addOutput action
|
||||
*/
|
||||
export const selectAddOutput = (state: CliStreamState) => state.addOutput;
|
||||
|
||||
/**
|
||||
* Selector for getting clearOutputs action
|
||||
*/
|
||||
export const selectClearOutputs = (state: CliStreamState) => state.clearOutputs;
|
||||
|
||||
/**
|
||||
* Selector for getting all executions
|
||||
*/
|
||||
export const selectAllExecutions = (state: CliStreamState) => state.executions;
|
||||
|
||||
/**
|
||||
* Selector for getting current execution ID
|
||||
*/
|
||||
export const selectCurrentExecutionId = (state: CliStreamState) => state.currentExecutionId;
|
||||
|
||||
/**
|
||||
* Selector for getting active execution count
|
||||
*/
|
||||
export const selectActiveExecutionCount = (state: CliStreamState) =>
|
||||
Object.values(state.executions).filter(e => e.status === 'running').length;
|
||||
Reference in New Issue
Block a user