mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-14 17:41:22 +08:00
Add unit tests for various components and stores in the terminal dashboard
- Implement tests for AssociationHighlight, DashboardToolbar, QueuePanel, SessionGroupTree, and TerminalDashboardPage to ensure proper functionality and state management. - Create tests for cliSessionStore, issueQueueIntegrationStore, queueExecutionStore, queueSchedulerStore, sessionManagerStore, and terminalGridStore to validate state resets and workspace scoping. - Mock necessary dependencies and state management hooks to isolate tests and ensure accurate behavior.
This commit is contained in:
39
ccw/frontend/src/stores/cliSessionStore.test.ts
Normal file
39
ccw/frontend/src/stores/cliSessionStore.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
// ========================================
|
||||
// CLI Session Store Tests
|
||||
// ========================================
|
||||
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useCliSessionStore } from './cliSessionStore';
|
||||
|
||||
describe('cliSessionStore', () => {
|
||||
beforeEach(() => {
|
||||
useCliSessionStore.getState().resetState();
|
||||
});
|
||||
|
||||
it('resetState clears workspace-scoped sessions and output buffers', () => {
|
||||
const store = useCliSessionStore.getState();
|
||||
|
||||
store.setSessions([
|
||||
{
|
||||
sessionKey: 'session-1',
|
||||
shellKind: 'bash',
|
||||
workingDir: 'D:/workspace-a',
|
||||
tool: 'codex',
|
||||
createdAt: '2026-03-08T12:00:00.000Z',
|
||||
updatedAt: '2026-03-08T12:00:00.000Z',
|
||||
isPaused: false,
|
||||
},
|
||||
]);
|
||||
store.appendOutput('session-1', 'hello world', 1_741_430_000_000);
|
||||
|
||||
expect(useCliSessionStore.getState().sessions['session-1']).toBeDefined();
|
||||
expect(useCliSessionStore.getState().outputChunks['session-1']).toHaveLength(1);
|
||||
|
||||
store.resetState();
|
||||
|
||||
const nextState = useCliSessionStore.getState();
|
||||
expect(nextState.sessions).toEqual({});
|
||||
expect(nextState.outputChunks).toEqual({});
|
||||
expect(nextState.outputBytes).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -34,6 +34,7 @@ interface CliSessionState {
|
||||
upsertSession: (session: CliSessionMeta) => void;
|
||||
removeSession: (sessionKey: string) => void;
|
||||
updateSessionPausedState: (sessionKey: string, isPaused: boolean) => void;
|
||||
resetState: () => void;
|
||||
|
||||
setBuffer: (sessionKey: string, buffer: string) => void;
|
||||
appendOutput: (sessionKey: string, data: string, timestamp?: number) => void;
|
||||
@@ -48,12 +49,16 @@ function utf8ByteLength(value: string): number {
|
||||
return utf8Encoder.encode(value).length;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
sessions: {},
|
||||
outputChunks: {},
|
||||
outputBytes: {},
|
||||
};
|
||||
|
||||
export const useCliSessionStore = create<CliSessionState>()(
|
||||
devtools(
|
||||
(set, get) => ({
|
||||
sessions: {},
|
||||
outputChunks: {},
|
||||
outputBytes: {},
|
||||
...initialState,
|
||||
|
||||
setSessions: (sessions) =>
|
||||
set((state) => {
|
||||
@@ -103,6 +108,8 @@ export const useCliSessionStore = create<CliSessionState>()(
|
||||
};
|
||||
}),
|
||||
|
||||
resetState: () => set({ ...initialState }),
|
||||
|
||||
setBuffer: (sessionKey, buffer) =>
|
||||
set((state) => ({
|
||||
outputChunks: {
|
||||
|
||||
@@ -52,7 +52,8 @@ export interface ExecutionWSMessage {
|
||||
payload: {
|
||||
executionId: string;
|
||||
flowId: string;
|
||||
sessionKey: string;
|
||||
sessionKey?: string;
|
||||
projectPath?: string;
|
||||
stepId?: string;
|
||||
stepName?: string;
|
||||
totalSteps?: number;
|
||||
@@ -117,7 +118,7 @@ export const useExecutionMonitorStore = create<ExecutionMonitorStore>()(
|
||||
executionId,
|
||||
flowId,
|
||||
flowName: stepName || 'Workflow',
|
||||
sessionKey,
|
||||
sessionKey: sessionKey ?? '',
|
||||
status: 'running',
|
||||
totalSteps: totalSteps || 0,
|
||||
completedSteps: 0,
|
||||
|
||||
45
ccw/frontend/src/stores/issueQueueIntegrationStore.test.ts
Normal file
45
ccw/frontend/src/stores/issueQueueIntegrationStore.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
// ========================================
|
||||
// Issue Queue Integration Store Tests
|
||||
// ========================================
|
||||
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useIssueQueueIntegrationStore } from './issueQueueIntegrationStore';
|
||||
import { useQueueExecutionStore } from './queueExecutionStore';
|
||||
|
||||
describe('issueQueueIntegrationStore', () => {
|
||||
beforeEach(() => {
|
||||
useIssueQueueIntegrationStore.getState().resetState();
|
||||
useQueueExecutionStore.getState().resetState();
|
||||
});
|
||||
|
||||
it('resetState clears selected issue and association chain', () => {
|
||||
useQueueExecutionStore.getState().addExecution({
|
||||
id: 'queue-exec-1',
|
||||
queueItemId: 'Q-1',
|
||||
issueId: 'ISSUE-1',
|
||||
solutionId: 'SOL-1',
|
||||
type: 'session',
|
||||
sessionKey: 'session-1',
|
||||
tool: 'codex',
|
||||
mode: 'analysis',
|
||||
status: 'running',
|
||||
startedAt: '2026-03-08T12:00:00.000Z',
|
||||
});
|
||||
|
||||
const store = useIssueQueueIntegrationStore.getState();
|
||||
store.buildAssociationChain('ISSUE-1', 'issue');
|
||||
|
||||
expect(useIssueQueueIntegrationStore.getState().selectedIssueId).toBe('ISSUE-1');
|
||||
expect(useIssueQueueIntegrationStore.getState().associationChain).toEqual({
|
||||
issueId: 'ISSUE-1',
|
||||
queueItemId: 'Q-1',
|
||||
sessionId: 'session-1',
|
||||
});
|
||||
|
||||
store.resetState();
|
||||
|
||||
const nextState = useIssueQueueIntegrationStore.getState();
|
||||
expect(nextState.selectedIssueId).toBeNull();
|
||||
expect(nextState.associationChain).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -85,6 +85,10 @@ export const useIssueQueueIntegrationStore = create<IssueQueueIntegrationStore>(
|
||||
);
|
||||
},
|
||||
|
||||
resetState: () => {
|
||||
set({ ...initialState }, false, 'resetState');
|
||||
},
|
||||
|
||||
// ========== Queue Status Bridge ==========
|
||||
|
||||
_updateQueueItemStatus: (
|
||||
|
||||
35
ccw/frontend/src/stores/queueExecutionStore.test.ts
Normal file
35
ccw/frontend/src/stores/queueExecutionStore.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// ========================================
|
||||
// Queue Execution Store Tests
|
||||
// ========================================
|
||||
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useQueueExecutionStore } from './queueExecutionStore';
|
||||
|
||||
describe('queueExecutionStore', () => {
|
||||
beforeEach(() => {
|
||||
useQueueExecutionStore.getState().resetState();
|
||||
});
|
||||
|
||||
it('resetState clears workspace-scoped queue execution tracking', () => {
|
||||
const store = useQueueExecutionStore.getState();
|
||||
|
||||
store.addExecution({
|
||||
id: 'queue-exec-1',
|
||||
queueItemId: 'Q-1',
|
||||
issueId: 'ISSUE-1',
|
||||
solutionId: 'SOL-1',
|
||||
type: 'session',
|
||||
sessionKey: 'session-1',
|
||||
tool: 'codex',
|
||||
mode: 'analysis',
|
||||
status: 'running',
|
||||
startedAt: '2026-03-08T12:00:00.000Z',
|
||||
});
|
||||
|
||||
expect(useQueueExecutionStore.getState().executions['queue-exec-1']).toBeDefined();
|
||||
|
||||
store.resetState();
|
||||
|
||||
expect(useQueueExecutionStore.getState().executions).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -68,6 +68,8 @@ export interface QueueExecutionActions {
|
||||
removeExecution: (id: string) => void;
|
||||
/** Remove all completed and failed executions */
|
||||
clearCompleted: () => void;
|
||||
/** Reset workspace-scoped queue execution state */
|
||||
resetState: () => void;
|
||||
}
|
||||
|
||||
export type QueueExecutionStore = QueueExecutionState & QueueExecutionActions;
|
||||
@@ -150,6 +152,10 @@ export const useQueueExecutionStore = create<QueueExecutionStore>()(
|
||||
'clearCompleted'
|
||||
);
|
||||
},
|
||||
|
||||
resetState: () => {
|
||||
set({ ...initialState }, false, 'resetState');
|
||||
},
|
||||
}),
|
||||
{ name: 'QueueExecutionStore' }
|
||||
)
|
||||
|
||||
131
ccw/frontend/src/stores/queueSchedulerStore.test.ts
Normal file
131
ccw/frontend/src/stores/queueSchedulerStore.test.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
// ========================================
|
||||
// Queue Scheduler Store Tests
|
||||
// ========================================
|
||||
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { QueueSchedulerState } from '@/types/queue-frontend-types';
|
||||
|
||||
type QueueSchedulerModule = typeof import('./queueSchedulerStore');
|
||||
|
||||
type Deferred<T> = {
|
||||
promise: Promise<T>;
|
||||
resolve: (value: T) => void;
|
||||
reject: (reason?: unknown) => void;
|
||||
};
|
||||
|
||||
function createDeferred<T>(): Deferred<T> {
|
||||
let resolve!: (value: T) => void;
|
||||
let reject!: (reason?: unknown) => void;
|
||||
const promise = new Promise<T>((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
return { promise, resolve, reject };
|
||||
}
|
||||
|
||||
function createState(status: QueueSchedulerState['status'], issueId: string): QueueSchedulerState {
|
||||
return {
|
||||
status,
|
||||
items: [
|
||||
{
|
||||
item_id: `${issueId}-Q1`,
|
||||
issue_id: issueId,
|
||||
status: status === 'running' ? 'executing' : 'pending',
|
||||
tool: 'codex',
|
||||
prompt: `Handle ${issueId}`,
|
||||
depends_on: [],
|
||||
execution_order: 1,
|
||||
execution_group: 'wave-1',
|
||||
createdAt: '2026-03-08T12:00:00.000Z',
|
||||
},
|
||||
],
|
||||
sessionPool: {},
|
||||
config: {
|
||||
maxConcurrentSessions: 3,
|
||||
sessionIdleTimeoutMs: 60_000,
|
||||
resumeKeySessionBindingTimeoutMs: 300_000,
|
||||
},
|
||||
currentConcurrency: status === 'running' ? 1 : 0,
|
||||
lastActivityAt: '2026-03-08T12:00:00.000Z',
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function createFetchResponse(state: QueueSchedulerState) {
|
||||
return {
|
||||
ok: true,
|
||||
json: vi.fn().mockResolvedValue(state),
|
||||
};
|
||||
}
|
||||
|
||||
describe('queueSchedulerStore', () => {
|
||||
let useQueueSchedulerStore: QueueSchedulerModule['useQueueSchedulerStore'];
|
||||
let fetchMock: ReturnType<typeof vi.fn>;
|
||||
const originalFetch = global.fetch;
|
||||
|
||||
beforeAll(async () => {
|
||||
vi.useFakeTimers();
|
||||
fetchMock = vi.fn();
|
||||
global.fetch = fetchMock as unknown as typeof fetch;
|
||||
({ useQueueSchedulerStore } = await import('./queueSchedulerStore'));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
vi.clearAllTimers();
|
||||
vi.useRealTimers();
|
||||
global.fetch = originalFetch;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.clearAllTimers();
|
||||
useQueueSchedulerStore.getState().resetState();
|
||||
});
|
||||
|
||||
it('resetState clears workspace-scoped scheduler state', () => {
|
||||
useQueueSchedulerStore.getState().handleSchedulerMessage({
|
||||
type: 'QUEUE_SCHEDULER_STATE_UPDATE',
|
||||
state: createState('running', 'ISSUE-1'),
|
||||
timestamp: '2026-03-08T12:00:00.000Z',
|
||||
});
|
||||
|
||||
expect(useQueueSchedulerStore.getState().status).toBe('running');
|
||||
expect(useQueueSchedulerStore.getState().items).toHaveLength(1);
|
||||
|
||||
useQueueSchedulerStore.getState().resetState();
|
||||
|
||||
const nextState = useQueueSchedulerStore.getState();
|
||||
expect(nextState.status).toBe('idle');
|
||||
expect(nextState.items).toEqual([]);
|
||||
expect(nextState.sessionPool).toEqual({});
|
||||
expect(nextState.currentConcurrency).toBe(0);
|
||||
expect(nextState.error).toBeNull();
|
||||
});
|
||||
|
||||
it('ignores stale loadInitialState responses after workspace reset', async () => {
|
||||
const staleResponse = createDeferred<ReturnType<typeof createFetchResponse>>();
|
||||
const freshResponse = createDeferred<ReturnType<typeof createFetchResponse>>();
|
||||
|
||||
fetchMock
|
||||
.mockImplementationOnce(() => staleResponse.promise)
|
||||
.mockImplementationOnce(() => freshResponse.promise);
|
||||
|
||||
const firstLoad = useQueueSchedulerStore.getState().loadInitialState();
|
||||
|
||||
useQueueSchedulerStore.getState().resetState();
|
||||
|
||||
const secondLoad = useQueueSchedulerStore.getState().loadInitialState();
|
||||
|
||||
freshResponse.resolve(createFetchResponse(createState('paused', 'ISSUE-NEW')));
|
||||
await secondLoad;
|
||||
|
||||
expect(useQueueSchedulerStore.getState().status).toBe('paused');
|
||||
expect(useQueueSchedulerStore.getState().items[0]?.issue_id).toBe('ISSUE-NEW');
|
||||
|
||||
staleResponse.resolve(createFetchResponse(createState('running', 'ISSUE-OLD')));
|
||||
await firstLoad;
|
||||
|
||||
expect(useQueueSchedulerStore.getState().status).toBe('paused');
|
||||
expect(useQueueSchedulerStore.getState().items[0]?.issue_id).toBe('ISSUE-NEW');
|
||||
});
|
||||
});
|
||||
@@ -57,6 +57,8 @@ interface QueueSchedulerActions {
|
||||
stopQueue: () => Promise<void>;
|
||||
/** Reset the queue scheduler via POST /api/queue/scheduler/reset */
|
||||
resetQueue: () => Promise<void>;
|
||||
/** Clear workspace-scoped scheduler state and invalidate stale loads */
|
||||
resetState: () => void;
|
||||
/** Update scheduler config via POST /api/queue/scheduler/config */
|
||||
updateConfig: (config: Partial<QueueSchedulerConfig>) => Promise<void>;
|
||||
}
|
||||
@@ -75,6 +77,8 @@ const initialState: QueueSchedulerStoreState = {
|
||||
error: null,
|
||||
};
|
||||
|
||||
let loadInitialStateRequestVersion = 0;
|
||||
|
||||
// ========== Store ==========
|
||||
|
||||
export const useQueueSchedulerStore = create<QueueSchedulerStore>()(
|
||||
@@ -173,6 +177,8 @@ export const useQueueSchedulerStore = create<QueueSchedulerStore>()(
|
||||
},
|
||||
|
||||
loadInitialState: async () => {
|
||||
const requestVersion = ++loadInitialStateRequestVersion;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/queue/scheduler/state', {
|
||||
credentials: 'same-origin',
|
||||
@@ -181,6 +187,11 @@ export const useQueueSchedulerStore = create<QueueSchedulerStore>()(
|
||||
throw new Error(`Failed to load scheduler state: ${response.statusText}`);
|
||||
}
|
||||
const data: QueueSchedulerState = await response.json();
|
||||
|
||||
if (requestVersion !== loadInitialStateRequestVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
set(
|
||||
{
|
||||
status: data.status,
|
||||
@@ -195,6 +206,10 @@ export const useQueueSchedulerStore = create<QueueSchedulerStore>()(
|
||||
'loadInitialState'
|
||||
);
|
||||
} catch (error) {
|
||||
if (requestVersion !== loadInitialStateRequestVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Silently ignore network errors (backend not connected)
|
||||
// Only log non-network errors
|
||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||
@@ -287,6 +302,11 @@ export const useQueueSchedulerStore = create<QueueSchedulerStore>()(
|
||||
}
|
||||
},
|
||||
|
||||
resetState: () => {
|
||||
loadInitialStateRequestVersion += 1;
|
||||
set({ ...initialState }, false, 'resetState');
|
||||
},
|
||||
|
||||
updateConfig: async (config: Partial<QueueSchedulerConfig>) => {
|
||||
try {
|
||||
const response = await fetch('/api/queue/scheduler/config', {
|
||||
|
||||
37
ccw/frontend/src/stores/sessionManagerStore.test.ts
Normal file
37
ccw/frontend/src/stores/sessionManagerStore.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// ========================================
|
||||
// Session Manager Store Tests
|
||||
// ========================================
|
||||
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useSessionManagerStore } from './sessionManagerStore';
|
||||
|
||||
describe('sessionManagerStore', () => {
|
||||
beforeEach(() => {
|
||||
useSessionManagerStore.getState().resetState();
|
||||
});
|
||||
|
||||
it('resetState clears workspace-scoped terminal metadata and selection', () => {
|
||||
const store = useSessionManagerStore.getState();
|
||||
|
||||
store.createGroup('Workspace Group');
|
||||
store.setActiveTerminal('session-1');
|
||||
store.updateTerminalMeta('session-1', {
|
||||
title: 'Session 1',
|
||||
status: 'active',
|
||||
alertCount: 2,
|
||||
tag: 'workspace-a',
|
||||
});
|
||||
|
||||
const activeState = useSessionManagerStore.getState();
|
||||
expect(activeState.groups).toHaveLength(1);
|
||||
expect(activeState.activeTerminalId).toBe('session-1');
|
||||
expect(activeState.terminalMetas['session-1']?.status).toBe('active');
|
||||
|
||||
store.resetState();
|
||||
|
||||
const nextState = useSessionManagerStore.getState();
|
||||
expect(nextState.groups).toEqual([]);
|
||||
expect(nextState.activeTerminalId).toBeNull();
|
||||
expect(nextState.terminalMetas).toEqual({});
|
||||
});
|
||||
});
|
||||
@@ -182,6 +182,14 @@ export const useSessionManagerStore = create<SessionManagerStore>()(
|
||||
);
|
||||
},
|
||||
|
||||
resetState: () => {
|
||||
if (_workerRef) {
|
||||
_workerRef.terminate();
|
||||
_workerRef = null;
|
||||
}
|
||||
set({ ...initialState }, false, 'resetState');
|
||||
},
|
||||
|
||||
// ========== Layout Management ==========
|
||||
|
||||
setGroupLayout: (layout: SessionLayout) => {
|
||||
|
||||
38
ccw/frontend/src/stores/terminalGridStore.test.ts
Normal file
38
ccw/frontend/src/stores/terminalGridStore.test.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
// ========================================
|
||||
// Terminal Grid Store Tests
|
||||
// ========================================
|
||||
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useTerminalGridStore } from './terminalGridStore';
|
||||
|
||||
describe('terminalGridStore', () => {
|
||||
beforeEach(() => {
|
||||
useTerminalGridStore.getState().resetLayout('single');
|
||||
});
|
||||
|
||||
it('resetWorkspaceState clears pane session bindings while preserving layout', () => {
|
||||
const store = useTerminalGridStore.getState();
|
||||
|
||||
store.resetLayout('split-h');
|
||||
const configuredState = useTerminalGridStore.getState();
|
||||
const paneIds = Object.keys(configuredState.panes);
|
||||
const originalLayout = configuredState.layout;
|
||||
|
||||
store.assignSession(paneIds[0], 'session-a', 'codex');
|
||||
store.showFileInPane(paneIds[1], 'D:/workspace-a/file.ts');
|
||||
store.setFocused(paneIds[1]);
|
||||
|
||||
store.resetWorkspaceState();
|
||||
|
||||
const nextState = useTerminalGridStore.getState();
|
||||
expect(nextState.layout).toEqual(originalLayout);
|
||||
expect(Object.keys(nextState.panes)).toEqual(paneIds);
|
||||
expect(nextState.focusedPaneId).toBe(paneIds[1]);
|
||||
for (const paneId of paneIds) {
|
||||
expect(nextState.panes[paneId]?.sessionId).toBeNull();
|
||||
expect(nextState.panes[paneId]?.cliTool).toBeNull();
|
||||
expect(nextState.panes[paneId]?.displayMode).toBe('terminal');
|
||||
expect(nextState.panes[paneId]?.filePath).toBeNull();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -45,6 +45,8 @@ export interface TerminalGridActions {
|
||||
assignSession: (paneId: PaneId, sessionId: string | null, cliTool?: string | null) => void;
|
||||
setFocused: (paneId: PaneId) => void;
|
||||
resetLayout: (preset: 'single' | 'split-h' | 'split-v' | 'grid-2x2') => void;
|
||||
/** Clear workspace-scoped pane bindings while preserving layout */
|
||||
resetWorkspaceState: () => void;
|
||||
/** Create a new CLI session and assign it to a new pane (auto-split from specified pane) */
|
||||
createSessionAndAssign: (
|
||||
paneId: PaneId,
|
||||
@@ -302,6 +304,42 @@ export const useTerminalGridStore = create<TerminalGridStore>()(
|
||||
);
|
||||
},
|
||||
|
||||
resetWorkspaceState: () => {
|
||||
const state = get();
|
||||
const paneIds = Object.keys(state.panes) as PaneId[];
|
||||
if (paneIds.length === 0) {
|
||||
set({ ...initialState }, false, 'terminalGrid/resetWorkspaceState');
|
||||
return;
|
||||
}
|
||||
|
||||
const nextPanes = paneIds.reduce<Record<PaneId, TerminalPaneState>>((acc, paneId) => {
|
||||
const pane = state.panes[paneId];
|
||||
acc[paneId] = {
|
||||
...pane,
|
||||
sessionId: null,
|
||||
cliTool: null,
|
||||
displayMode: 'terminal',
|
||||
filePath: null,
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<PaneId, TerminalPaneState>);
|
||||
|
||||
const nextFocusedPaneId = state.focusedPaneId && nextPanes[state.focusedPaneId]
|
||||
? state.focusedPaneId
|
||||
: paneIds[0] ?? null;
|
||||
|
||||
set(
|
||||
{
|
||||
layout: state.layout,
|
||||
panes: nextPanes,
|
||||
focusedPaneId: nextFocusedPaneId,
|
||||
nextPaneIdCounter: state.nextPaneIdCounter,
|
||||
},
|
||||
false,
|
||||
'terminalGrid/resetWorkspaceState'
|
||||
);
|
||||
},
|
||||
|
||||
createSessionAndAssign: async (paneId, config, projectPath) => {
|
||||
try {
|
||||
// 1. Create the CLI session via API
|
||||
|
||||
Reference in New Issue
Block a user