Archived
feat(ui): add model and thinking selector actions
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@jmfederico/pi-web": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add action-palette commands for selecting a session's model and thinking level, with support for assigning custom shortcuts in Settings.
|
||||||
@@ -1791,6 +1791,8 @@ export class PiWebApp extends LitElement {
|
|||||||
configureAuth: () => this.auth.openLogin(),
|
configureAuth: () => this.auth.openLogin(),
|
||||||
logoutAuth: () => this.auth.openLogout(),
|
logoutAuth: () => this.auth.openLogout(),
|
||||||
openThemePicker: () => { this.openThemeDialog(); },
|
openThemePicker: () => { this.openThemeDialog(); },
|
||||||
|
openModelPicker: () => this.openModelDialog(),
|
||||||
|
openThinkingLevelPicker: () => this.openThinkingDialog(),
|
||||||
selectMainView: (view) => { this.selectMainView(view); },
|
selectMainView: (view) => { this.selectMainView(view); },
|
||||||
selectWorkspaceTool: (tool) => { this.openWorkspaceTool(tool); },
|
selectWorkspaceTool: (tool) => { this.openWorkspaceTool(tool); },
|
||||||
openTerminal: (options) => { this.openTerminal(options); },
|
openTerminal: (options) => { this.openTerminal(options); },
|
||||||
|
|||||||
@@ -167,6 +167,22 @@ export function createCoreActions(): PluginAction[] {
|
|||||||
enabled: hasWorkspace,
|
enabled: hasWorkspace,
|
||||||
run: (context) => context.startSession(),
|
run: (context) => context.startSession(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "model.select",
|
||||||
|
title: "Select Model",
|
||||||
|
description: "Choose the model for the selected session",
|
||||||
|
group: "Session",
|
||||||
|
enabled: hasSelectableSession,
|
||||||
|
run: (context) => context.openModelPicker(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "thinking.select",
|
||||||
|
title: "Select Thinking Level",
|
||||||
|
description: "Choose the thinking level for the selected session",
|
||||||
|
group: "Session",
|
||||||
|
enabled: hasSelectableSession,
|
||||||
|
run: (context) => context.openThinkingLevelPicker(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "session.archive",
|
id: "session.archive",
|
||||||
title: "Archive Session",
|
title: "Archive Session",
|
||||||
@@ -216,6 +232,11 @@ function hasDeletableWorkspace(context: { state: AppState }): boolean {
|
|||||||
return workspace !== undefined && workspace.isGitWorktree && !workspace.isMain && !isWorkspaceDeletionPending(context.state, workspace);
|
return workspace !== undefined && workspace.isGitWorktree && !workspace.isMain && !isWorkspaceDeletionPending(context.state, workspace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasSelectableSession(context: { state: AppState }): boolean {
|
||||||
|
const session = context.state.selectedSession;
|
||||||
|
return session !== undefined && session.archived !== true;
|
||||||
|
}
|
||||||
|
|
||||||
function hasArchivableSession(context: { state: AppState }): boolean {
|
function hasArchivableSession(context: { state: AppState }): boolean {
|
||||||
return isArchivableSessionInfo(context.state.selectedSession, context.state.status, sessionPersistenceOptions(context.state));
|
return isArchivableSessionInfo(context.state.selectedSession, context.state.status, sessionPersistenceOptions(context.state));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ function createContext(statePatch: Partial<AppState> = {}) {
|
|||||||
configureAuth: vi.fn(() => { calls.push("configureAuth"); }),
|
configureAuth: vi.fn(() => { calls.push("configureAuth"); }),
|
||||||
logoutAuth: vi.fn(() => { calls.push("logoutAuth"); }),
|
logoutAuth: vi.fn(() => { calls.push("logoutAuth"); }),
|
||||||
openThemePicker: vi.fn(() => { calls.push("openThemePicker"); }),
|
openThemePicker: vi.fn(() => { calls.push("openThemePicker"); }),
|
||||||
|
openModelPicker: vi.fn(() => { calls.push("openModelPicker"); }),
|
||||||
|
openThinkingLevelPicker: vi.fn(() => { calls.push("openThinkingLevelPicker"); }),
|
||||||
selectMainView: vi.fn((view: AppState["mainView"]) => { calls.push(`selectMainView:${view}`); }),
|
selectMainView: vi.fn((view: AppState["mainView"]) => { calls.push(`selectMainView:${view}`); }),
|
||||||
selectWorkspaceTool: vi.fn((tool: AppState["workspaceTool"]) => { calls.push(`selectWorkspaceTool:${tool}`); }),
|
selectWorkspaceTool: vi.fn((tool: AppState["workspaceTool"]) => { calls.push(`selectWorkspaceTool:${tool}`); }),
|
||||||
openTerminal: vi.fn((options?: { terminalId?: string | undefined }) => { calls.push(`openTerminal:${options?.terminalId ?? ""}`); }),
|
openTerminal: vi.fn((options?: { terminalId?: string | undefined }) => { calls.push(`openTerminal:${options?.terminalId ?? ""}`); }),
|
||||||
@@ -290,6 +292,34 @@ describe("PluginRegistry", () => {
|
|||||||
expect(calls).toEqual(["deleteCachedNewSession"]);
|
expect(calls).toEqual(["deleteCachedNewSession"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exposes model and thinking selectors as configurable actions for writable sessions", () => {
|
||||||
|
const registry = new PluginRegistry();
|
||||||
|
registry.register({ id: "core", plugin: corePlugin });
|
||||||
|
|
||||||
|
const unavailable = registry.getActions(createContext().context);
|
||||||
|
expect(unavailable.find((action) => action.id === "core:model.select")?.enabled).toBe(false);
|
||||||
|
expect(unavailable.find((action) => action.id === "core:thinking.select")?.enabled).toBe(false);
|
||||||
|
|
||||||
|
const archivedSession = { ...testSession(), archived: true, archivedAt: "2026-05-20T00:00:00.000Z" };
|
||||||
|
const archived = registry.getActions(createContext({ selectedSession: archivedSession }).context);
|
||||||
|
expect(archived.find((action) => action.id === "core:model.select")?.enabled).toBe(false);
|
||||||
|
expect(archived.find((action) => action.id === "core:thinking.select")?.enabled).toBe(false);
|
||||||
|
|
||||||
|
const { context, calls } = createContext({ selectedSession: testSession() });
|
||||||
|
const actions = registry.getActions(context);
|
||||||
|
const modelAction = actions.find((action) => action.id === "core:model.select");
|
||||||
|
const thinkingAction = actions.find((action) => action.id === "core:thinking.select");
|
||||||
|
expect(modelAction).toMatchObject({ title: "Select Model", enabled: true });
|
||||||
|
expect(modelAction?.shortcut).toBeUndefined();
|
||||||
|
expect(thinkingAction).toMatchObject({ title: "Select Thinking Level", enabled: true });
|
||||||
|
expect(thinkingAction?.shortcut).toBeUndefined();
|
||||||
|
|
||||||
|
if (modelAction !== undefined) void modelAction.run();
|
||||||
|
if (thinkingAction !== undefined) void thinkingAction.run();
|
||||||
|
|
||||||
|
expect(calls).toEqual(["openModelPicker", "openThinkingLevelPicker"]);
|
||||||
|
});
|
||||||
|
|
||||||
it("routes refresh current to the active core workspace panel", () => {
|
it("routes refresh current to the active core workspace panel", () => {
|
||||||
const registry = new PluginRegistry();
|
const registry = new PluginRegistry();
|
||||||
registry.register({ id: "core", plugin: corePlugin });
|
registry.register({ id: "core", plugin: corePlugin });
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ export interface PluginRuntimeContext {
|
|||||||
configureAuth: () => void | Promise<void>;
|
configureAuth: () => void | Promise<void>;
|
||||||
logoutAuth: () => void | Promise<void>;
|
logoutAuth: () => void | Promise<void>;
|
||||||
openThemePicker: () => void;
|
openThemePicker: () => void;
|
||||||
|
openModelPicker: () => void | Promise<void>;
|
||||||
|
openThinkingLevelPicker: () => void | Promise<void>;
|
||||||
selectMainView: (view: AppState["mainView"]) => void;
|
selectMainView: (view: AppState["mainView"]) => void;
|
||||||
selectWorkspaceTool: (tool: QualifiedContributionId) => void;
|
selectWorkspaceTool: (tool: QualifiedContributionId) => void;
|
||||||
openTerminal: (options?: { terminalId?: string | undefined }) => void;
|
openTerminal: (options?: { terminalId?: string | undefined }) => void;
|
||||||
|
|||||||
Reference in New Issue
Block a user