import { scopeProjectRef } from "@t3tools/client-runtime/environment";import type { EnvironmentId, ProjectId, ScopedProjectRef } from "@t3tools/contracts";import type { DraftThreadEnvMode } from "../composerDraftStore"; interface ThreadContextLike { environmentId: EnvironmentId; projectId: ProjectId; branch: string | null; worktreePath: string | null;} interface DraftThreadContextLike extends ThreadContextLike { envMode: DraftThreadEnvMode; startFromOrigin: boolean;} interface NewThreadHandler { ( projectRef: ScopedProjectRef, options?: { branch?: string | null; worktreePath?: string | null; envMode?: DraftThreadEnvMode; startFromOrigin?: boolean; }, ): Promise<void>;} type NewThreadOptions = NonNullable<Parameters<NewThreadHandler>[1]>; export interface ChatThreadActionContext { readonly activeDraftThread: DraftThreadContextLike | null; readonly activeThread: ThreadContextLike | undefined; readonly defaultProjectRef: ScopedProjectRef | null; readonly handleNewThread: NewThreadHandler;} export function resolveNewDraftStartFromOrigin(input: { envMode: DraftThreadEnvMode; newWorktreesStartFromOrigin: boolean;}): boolean { return input.envMode === "worktree" && input.newWorktreesStartFromOrigin;} export function resolveThreadActionProjectRef( context: ChatThreadActionContext,): ScopedProjectRef | null { if (context.activeThread) { return scopeProjectRef(context.activeThread.environmentId, context.activeThread.projectId); } if (context.activeDraftThread) { return scopeProjectRef( context.activeDraftThread.environmentId, context.activeDraftThread.projectId, ); } return context.defaultProjectRef;} function buildContextualThreadOptions(context: ChatThreadActionContext): NewThreadOptions { return { branch: context.activeThread?.branch ?? context.activeDraftThread?.branch ?? null, worktreePath: context.activeThread?.worktreePath ?? context.activeDraftThread?.worktreePath ?? null, envMode: context.activeDraftThread?.envMode ?? (context.activeThread?.worktreePath ? "worktree" : "local"), ...(context.activeDraftThread ? { startFromOrigin: context.activeDraftThread.startFromOrigin } : {}), };} export async function startNewThreadInProjectFromContext( context: ChatThreadActionContext, projectRef: ScopedProjectRef,): Promise<void> { await context.handleNewThread(projectRef, buildContextualThreadOptions(context));} export async function startNewThreadFromContext( context: ChatThreadActionContext,): Promise<boolean> { const projectRef = resolveThreadActionProjectRef(context); if (!projectRef) { return false; } await startNewThreadInProjectFromContext(context, projectRef); return true;} export async function startNewLocalThreadFromContext( context: ChatThreadActionContext,): Promise<boolean> { const projectRef = resolveThreadActionProjectRef(context); if (!projectRef) { return false; } await context.handleNewThread(projectRef); return true;}
import { scopeProjectRef } from "@t3tools/client-runtime/environment";
import type { EnvironmentId, ProjectId, ScopedProjectRef } from "@t3tools/contracts";
import type { DraftThreadEnvMode } from "../composerDraftStore";
interface ThreadContextLike {
environmentId: EnvironmentId;
projectId: ProjectId;
branch: string | null;
worktreePath: string | null;
}
interface DraftThreadContextLike extends ThreadContextLike {
envMode: DraftThreadEnvMode;
startFromOrigin: boolean;
}
interface NewThreadHandler {
(
projectRef: ScopedProjectRef,
options?: {
branch?: string | null;
worktreePath?: string | null;
envMode?: DraftThreadEnvMode;
startFromOrigin?: boolean;
},
): Promise<void>;
}
type NewThreadOptions = NonNullable<Parameters<NewThreadHandler>[1]>;
export interface ChatThreadActionContext {
readonly activeDraftThread: DraftThreadContextLike | null;
readonly activeThread: ThreadContextLike | undefined;
readonly defaultProjectRef: ScopedProjectRef | null;
readonly handleNewThread: NewThreadHandler;
}
export function resolveNewDraftStartFromOrigin(input: {
envMode: DraftThreadEnvMode;
newWorktreesStartFromOrigin: boolean;
}): boolean {
return input.envMode === "worktree" && input.newWorktreesStartFromOrigin;
}
export function resolveThreadActionProjectRef(
context: ChatThreadActionContext,
): ScopedProjectRef | null {
if (context.activeThread) {
return scopeProjectRef(context.activeThread.environmentId, context.activeThread.projectId);
}
if (context.activeDraftThread) {
return scopeProjectRef(
context.activeDraftThread.environmentId,
context.activeDraftThread.projectId,
);
}
return context.defaultProjectRef;
}
function buildContextualThreadOptions(context: ChatThreadActionContext): NewThreadOptions {
return {
branch: context.activeThread?.branch ?? context.activeDraftThread?.branch ?? null,
worktreePath:
context.activeThread?.worktreePath ?? context.activeDraftThread?.worktreePath ?? null,
envMode:
context.activeDraftThread?.envMode ??
(context.activeThread?.worktreePath ? "worktree" : "local"),
...(context.activeDraftThread
? { startFromOrigin: context.activeDraftThread.startFromOrigin }
: {}),
};
}
export async function startNewThreadInProjectFromContext(
context: ChatThreadActionContext,
projectRef: ScopedProjectRef,
): Promise<void> {
await context.handleNewThread(projectRef, buildContextualThreadOptions(context));
}
export async function startNewThreadFromContext(
context: ChatThreadActionContext,
): Promise<boolean> {
const projectRef = resolveThreadActionProjectRef(context);
if (!projectRef) {
return false;
}
await startNewThreadInProjectFromContext(context, projectRef);
return true;
}
export async function startNewLocalThreadFromContext(
context: ChatThreadActionContext,
): Promise<boolean> {
const projectRef = resolveThreadActionProjectRef(context);
if (!projectRef) {
return false;
}
await context.handleNewThread(projectRef);
return true;
}