feat(app): improve workspace service controls
Keep service route choices independent of daemon transport and persist the selected route per host. Use the shared web overlay stack so menus, tooltips, and toasts layer predictably. Track proxy route changes from the Git branch rather than the workspace title to avoid transient service health changes.
This commit is contained in:
+28
-3
@@ -26,6 +26,7 @@ function makeDescriptor(overrides: {
|
||||
projectKind?: string;
|
||||
workspaceKind?: "directory" | "local_checkout" | "worktree";
|
||||
name?: string | null;
|
||||
currentBranch?: string | null;
|
||||
diffStat?: { additions: number; deletions: number } | null;
|
||||
}): WorkspaceDescriptorPayload {
|
||||
return {
|
||||
@@ -34,6 +35,9 @@ function makeDescriptor(overrides: {
|
||||
projectKind: overrides.projectKind ?? "git",
|
||||
workspaceKind: overrides.workspaceKind ?? "local_checkout",
|
||||
name: overrides.name ?? null,
|
||||
...(overrides.currentBranch !== undefined
|
||||
? { gitRuntime: { currentBranch: overrides.currentBranch } }
|
||||
: {}),
|
||||
diffStat: overrides.diffStat ?? null,
|
||||
} as unknown as WorkspaceDescriptorPayload;
|
||||
}
|
||||
@@ -259,10 +263,31 @@ describe("shouldSkipUpdate", () => {
|
||||
});
|
||||
|
||||
describe("recordDescriptorState", () => {
|
||||
test("does not treat a workspace title as a branch change", () => {
|
||||
const h = buildHarness();
|
||||
const titledMain = makeDescriptor({
|
||||
id: "ws1",
|
||||
workspaceDirectory: WS1,
|
||||
name: "Paseo main",
|
||||
currentBranch: "main",
|
||||
});
|
||||
h.service.syncObservers([titledMain]);
|
||||
|
||||
h.emitSnapshot(WS1, "main");
|
||||
h.service.recordDescriptorState("ws1", titledMain);
|
||||
|
||||
expect(h.branchChanges).toEqual([]);
|
||||
});
|
||||
|
||||
test("fires onBranchChanged once per branch name transition", () => {
|
||||
const h = buildHarness();
|
||||
h.service.syncObservers([makeDescriptor({ id: "ws1", workspaceDirectory: WS1 })]);
|
||||
const feature = makeDescriptor({ id: "ws1", workspaceDirectory: WS1, name: "feature" });
|
||||
const feature = makeDescriptor({
|
||||
id: "ws1",
|
||||
workspaceDirectory: WS1,
|
||||
name: "Feature workspace",
|
||||
currentBranch: "feature",
|
||||
});
|
||||
h.service.recordDescriptorState("ws1", feature);
|
||||
h.service.recordDescriptorState("ws1", feature);
|
||||
expect(h.branchChanges).toEqual([["ws1", null, "feature"]]);
|
||||
@@ -296,8 +321,8 @@ describe("teardown", () => {
|
||||
test("keeps a shared cwd subscription until its last workspace is removed", () => {
|
||||
const h = buildHarness();
|
||||
h.service.syncObservers([
|
||||
makeDescriptor({ id: "ws1", workspaceDirectory: WS1, name: "main" }),
|
||||
makeDescriptor({ id: "ws2", workspaceDirectory: WS1, name: "main" }),
|
||||
makeDescriptor({ id: "ws1", workspaceDirectory: WS1, name: "Main", currentBranch: "main" }),
|
||||
makeDescriptor({ id: "ws2", workspaceDirectory: WS1, name: "Main", currentBranch: "main" }),
|
||||
]);
|
||||
|
||||
h.emitSnapshot(WS1, "feature");
|
||||
|
||||
+8
-5
@@ -28,8 +28,8 @@ interface WorkspaceGitWatchState {
|
||||
* watch without sharing identity or teardown lifetime.
|
||||
*
|
||||
* Branch changes reach `onBranchChanged` from two paths that share `lastBranchName`: the
|
||||
* on-disk snapshot listener (handleBranchSnapshot) and the workspace-emit loop
|
||||
* (recordDescriptorState). Both stay inside this module so the shared state is coherent.
|
||||
* on-disk snapshot listener (handleBranchSnapshot) and the workspace-emit loop's Git runtime
|
||||
* projection (recordDescriptorState). Both stay inside this module so the shared state is coherent.
|
||||
*/
|
||||
export interface WorkspaceGitObserverService {
|
||||
syncObservers(workspaces: Iterable<WorkspaceDescriptorPayload>): void;
|
||||
@@ -92,7 +92,10 @@ export function createWorkspaceGitObserverService(deps: {
|
||||
return;
|
||||
}
|
||||
state.latestDescriptorStateKey = descriptorStateKey(workspace);
|
||||
state.lastBranchName = workspace?.name ?? null;
|
||||
const currentBranch = workspace?.gitRuntime?.currentBranch;
|
||||
if (currentBranch !== undefined) {
|
||||
state.lastBranchName = currentBranch;
|
||||
}
|
||||
}
|
||||
|
||||
function removeForCwd(cwd: string): void {
|
||||
@@ -225,8 +228,8 @@ export function createWorkspaceGitObserverService(deps: {
|
||||
|
||||
recordDescriptorState(workspaceId, nextWorkspace) {
|
||||
const state = workspaceStates.get(workspaceId);
|
||||
if (state && onBranchChanged) {
|
||||
const newBranchName = nextWorkspace?.name ?? null;
|
||||
const newBranchName = nextWorkspace?.gitRuntime?.currentBranch;
|
||||
if (state && onBranchChanged && newBranchName !== undefined) {
|
||||
if (newBranchName !== state.lastBranchName) {
|
||||
onBranchChanged(workspaceId, state.lastBranchName, newBranchName);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user