feat: improve task creation (#220)

This commit is contained in:
Carlos Florêncio
2026-02-13 17:26:38 +00:00
committed by GitHub
parent e2ba35b3bd
commit ea751d3ecc
32 changed files with 1177 additions and 699 deletions
+1
View File
@@ -39,6 +39,7 @@ export async function createTask(
}>;
state?: Task['state'];
start_agent?: boolean;
prepare_session?: boolean;
agent_profile_id?: string;
executor_id?: string;
plan_mode?: boolean;
+58 -1
View File
@@ -12,6 +12,21 @@ export type FileEditorState = {
isBinary?: boolean;
};
/** Direction relative to a reference panel or group. */
export type PanelDirection = 'left' | 'right' | 'above' | 'below';
/** A deferred panel operation applied after the next layout build / restore. */
export type DeferredPanelAction = {
id: string;
component: string;
title: string;
/** Where to place the panel. 'tab' adds as a tab in the reference group. */
placement: 'tab' | PanelDirection;
/** Panel ID or group alias to position relative to. Defaults to 'chat'. */
referencePanel?: string;
params?: Record<string, unknown>;
};
type DockviewStore = {
api: DockviewApi | null;
setApi: (api: DockviewApi | null) => void;
@@ -54,6 +69,10 @@ type DockviewStore = {
isRestoringLayout: boolean;
currentLayoutSessionId: string | null;
switchSessionLayout: (oldSessionId: string | null, newSessionId: string) => void;
// Deferred panel actions — queued before navigation, applied after next layout build / restore
deferredPanelActions: DeferredPanelAction[];
queuePanelAction: (action: DeferredPanelAction) => void;
};
const SIDEBAR_GROUP = 'group-sidebar';
@@ -82,6 +101,32 @@ function focusOrAddPanel(api: DockviewApi, options: AddPanelOptions & { id: stri
}
}
/**
* Drain the deferred panel action queue, applying each action to the dockview API.
* Call this after a layout build or restore so queued panels appear in the new layout.
*/
function applyDeferredPanelActions(api: DockviewApi, actions: DeferredPanelAction[]): void {
for (const action of actions) {
const ref = action.referencePanel ?? 'chat';
let position: AddPanelOptions['position'];
if (action.placement === 'tab') {
const groupId = api.getPanel(ref)?.group?.id;
if (groupId) {
position = { referenceGroup: groupId };
}
} else {
position = { referencePanel: ref, direction: action.placement };
}
focusOrAddPanel(api, {
id: action.id,
component: action.component,
title: action.title,
position,
...(action.params ? { params: action.params } : {}),
});
}
}
/**
* After fromJSON() restores a layout, apply fixups: re-lock sidebar,
* fix old panel aliases, and return computed group IDs.
@@ -165,6 +210,11 @@ export const useDockviewStore = create<DockviewStore>((set, get) => ({
isRestoringLayout: false,
currentLayoutSessionId: null,
deferredPanelActions: [],
queuePanelAction: (action) => set((prev) => ({
deferredPanelActions: [...prev.deferredPanelActions, action],
})),
switchSessionLayout: (oldSessionId, newSessionId) => {
const { api, currentLayoutSessionId } = get();
if (!api) return;
@@ -254,7 +304,7 @@ export const useDockviewStore = create<DockviewStore>((set, get) => ({
id: 'terminal-default',
component: 'terminal',
title: 'Terminal',
params: { terminalId: 'default' },
params: { terminalId: 'shell-default' },
position: { direction: 'below', referencePanel: 'changes' },
});
@@ -268,6 +318,13 @@ export const useDockviewStore = create<DockviewStore>((set, get) => ({
rightBottomGroupId: terminalPanel?.group?.id ?? RIGHT_BOTTOM_GROUP,
sidebarGroupId: sidebarGroup.id,
});
// Apply any queued panel actions (e.g., plan panel from plan mode task creation)
const pending = get().deferredPanelActions;
if (pending.length > 0) {
set({ deferredPanelActions: [] });
applyDeferredPanelActions(api, pending);
}
},
resetLayout: () => {
@@ -50,6 +50,7 @@ export type WorkspaceSliceActions = {
setRepositoryScripts: (repositoryId: string, scripts: RepositoryScript[]) => void;
setRepositoryScriptsLoading: (repositoryId: string, loading: boolean) => void;
clearRepositoryScripts: (repositoryId: string) => void;
invalidateRepositories: (workspaceId: string) => void;
};
export type WorkspaceSlice = WorkspaceSliceState & WorkspaceSliceActions;
@@ -66,4 +66,8 @@ export const createWorkspaceSlice: StateCreator<
delete draft.repositoryScripts.loadingByRepositoryId[repositoryId];
delete draft.repositoryScripts.loadedByRepositoryId[repositoryId];
}),
invalidateRepositories: (workspaceId) =>
set((draft) => {
draft.repositories.loadedByWorkspaceId[workspaceId] = false;
}),
});
+1
View File
@@ -176,6 +176,7 @@ export type AppState = {
setRepositoryScripts: (repositoryId: string, scripts: RepositoryScript[]) => void;
setRepositoryScriptsLoading: (repositoryId: string, loading: boolean) => void;
clearRepositoryScripts: (repositoryId: string) => void;
invalidateRepositories: (workspaceId: string) => void;
setSettingsData: (next: Partial<SettingsDataState>) => void;
setEditors: (editors: EditorsState['items']) => void;
setEditorsLoading: (loading: boolean) => void;
+2 -1
View File
@@ -7,7 +7,7 @@ type KanbanTask = KanbanState['tasks'][number];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function buildTaskFromPayload(payload: any, existing?: KanbanTask): KanbanTask {
return {
const task = {
id: payload.task_id,
workflowStepId: payload.workflow_step_id,
title: payload.title,
@@ -20,6 +20,7 @@ function buildTaskFromPayload(payload: any, existing?: KanbanTask): KanbanTask {
reviewStatus: payload.review_status ?? existing?.reviewStatus,
updatedAt: payload.updated_at ?? existing?.updatedAt,
};
return task;
}
function upsertTask(tasks: KanbanTask[], nextTask: KanbanTask): KanbanTask[] {