Merge pull request #1363 from pikasTech/fix/1361-route-conversation-detail

fix: hydrate routed workbench conversation detail
This commit is contained in:
Lyon
2026-06-17 07:22:43 +08:00
committed by GitHub
3 changed files with 58 additions and 5 deletions
+29 -1
View File
@@ -25,7 +25,11 @@ if (explicitAnyCount > 0) {
process.exit(2);
}
const vueTscBin = path.join(rootDir, "node_modules/.bin/vue-tsc");
const vueTscBin = resolveVueTscBin(rootDir);
if (!vueTscBin) {
console.error("hwlab-cloud-web tsc-check: vue-tsc not found in ancestor node_modules/.bin or PATH");
process.exit(2);
}
const result = spawnSync(vueTscBin, ["--noEmit", "--project", path.join(rootDir, "tsconfig.json")], { cwd: rootDir, encoding: "utf8" });
if (result.stdout) process.stdout.write(result.stdout);
if (result.stderr) process.stderr.write(result.stderr);
@@ -36,6 +40,30 @@ if (result.status !== 0) {
}
console.log("hwlab-cloud-web tsc-check: passed (strict Vue TS, 0 explicit any)");
function resolveVueTscBin(startDir: string): string | null {
let current = startDir;
while (true) {
const candidate = path.join(current, "node_modules/.bin/vue-tsc");
if (isRunnableFile(candidate)) return candidate;
const parent = path.dirname(current);
if (parent === current) break;
current = parent;
}
for (const dir of (process.env.PATH ?? "").split(path.delimiter).filter(Boolean)) {
const candidate = path.join(dir, process.platform === "win32" ? "vue-tsc.cmd" : "vue-tsc");
if (isRunnableFile(candidate)) return candidate;
}
return null;
}
function isRunnableFile(candidate: string): boolean {
try {
return statSync(candidate).isFile();
} catch {
return false;
}
}
function collectSourceFiles(dir: string): string[] {
const out: string[] = [];
for (const entry of readdirSync(dir)) {
+28 -3
View File
@@ -13,6 +13,10 @@ const TRACE_HYDRATION_MAX_PAGES = 60;
const TRACE_HYDRATION_MAX_ATTEMPTS = 3;
const TRACE_HYDRATION_RETRY_DELAY_MS = 700;
interface HydrateOptions {
conversationId?: string | null;
}
export const useWorkbenchStore = defineStore("workbench", () => {
const projectId = ref(resolveInitialWorkbenchProjectId());
const workspace = ref<WorkspaceRecord | null>(window.HWLAB_CLOUD_WEB_WORKSPACE_BOOTSTRAP?.workspace ?? null);
@@ -52,7 +56,7 @@ export const useWorkbenchStore = defineStore("workbench", () => {
activityRef.value = { lastActivityAt: now, lastActivityIso: new Date(now).toISOString(), waitingFor: "code-agent", lastEventLabel: label };
}
async function hydrate(): Promise<void> {
async function hydrate(options: HydrateOptions = {}): Promise<void> {
const requestEpoch = workspaceSelectionEpoch.value;
const previousConversationId = activeConversationId.value;
conversationsReady.value = conversations.value.length > 0;
@@ -67,7 +71,8 @@ export const useWorkbenchStore = defineStore("workbench", () => {
const nextWorkspace = workspaceResult.data?.workspace ?? null;
const nextProjectId = workspaceProjectId(nextWorkspace, projectId.value);
projectId.value = nextProjectId;
const selectedConversationId = selectedConversationIdFromWorkspace(nextWorkspace);
const routeConversationId = normalizeWorkbenchConversationId(options.conversationId);
const selectedConversationId = routeConversationId ?? selectedConversationIdFromWorkspace(nextWorkspace);
const [conversationsResult, selectedConversationResult] = await Promise.all([
api.workbench.conversations(nextProjectId, { includeConversationId: selectedConversationId }),
selectedConversationId ? api.workbench.conversation(selectedConversationId, { projectId: nextProjectId }) : Promise.resolve(null)
@@ -89,7 +94,11 @@ export const useWorkbenchStore = defineStore("workbench", () => {
error.value = conversations.value.length > 0 ? null : conversationsResult.error ?? "session list unavailable";
}
if (selectedConversationId && selectedConversationResult && !selectedConversationResult.ok) error.value = selectedConversationResult.error ?? "conversation unavailable";
const workspaceSnapshot = selectedConversation ? workspaceWithSelectedConversation(nextWorkspace, selectedConversation, conversationProjectId(selectedConversation, nextProjectId)) : nextWorkspace;
const workspaceSnapshot = selectedConversation
? workspaceWithSelectedConversation(nextWorkspace, selectedConversation, conversationProjectId(selectedConversation, nextProjectId))
: routeConversationId
? workspaceWithSelectedConversationId(nextWorkspace, routeConversationId, nextProjectId)
: nextWorkspace;
if (shouldApplyWorkspaceSnapshot({ requestEpoch, currentEpoch: workspaceSelectionEpoch.value, currentConversationId: activeConversationId.value, workspace: workspaceSnapshot })) {
workspace.value = workspaceSnapshot;
providerProfile.value = firstNonEmptyString(workspace.value?.providerProfile, workspace.value?.workspace?.providerProfile, providerProfile.value) ?? providerProfile.value;
@@ -733,6 +742,22 @@ function workspaceWithSelectedConversation(current: WorkspaceRecord | null, conv
};
}
function workspaceWithSelectedConversationId(current: WorkspaceRecord | null, conversationId: string, projectId: string): WorkspaceRecord | null {
if (!current) return current;
const selectedConversation = current.selectedConversation?.conversationId === conversationId ? current.selectedConversation : undefined;
return {
...current,
projectId,
selectedConversationId: conversationId,
selectedConversation,
workspace: {
...(current.workspace ?? {}),
projectId,
selectedConversationId: conversationId
}
};
}
function conversationProjectId(conversation: ConversationRecord | null | undefined, fallback: string): string {
return firstNonEmptyString(conversation?.projectId, fallback) ?? DEFAULT_WORKBENCH_PROJECT_ID;
}
@@ -21,7 +21,7 @@ onMounted(async () => {
componentActive.value = true;
applyingRouteConversation.value = Boolean(routeConversationId.value);
try {
await workbench.hydrate();
await workbench.hydrate({ conversationId: routeConversationId.value });
await applyRouteConversation();
} finally {
applyingRouteConversation.value = false;