Skip to content

Commit 67c0aed

Browse files
fix: keep provider controls current across rerenders
1 parent bc246a9 commit 67c0aed

3 files changed

Lines changed: 14 additions & 6 deletions

File tree

‎src/renderer/nextView.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ function taskRow(it: TaskWithProject, now: number): HTMLElement {
9494
const open = createProviderOpenControl({
9595
path: projectPath,
9696
historyAgentIds: project?.agentIds ?? [],
97-
liveAgentIds: liveProjectProviders(projectPath),
97+
liveAgentIds: () => liveProjectProviders(projectPath),
9898
compact: true,
9999
onOpen: (intent) => openInTerminal([{
100100
...intent, name: projectName, staleLevel: 'neutral', branch: null, dirty: 0,

‎src/renderer/projectsView.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ function providerOpenControl(p: ProjectViewModel, compact = false): HTMLElement
9393
return createProviderOpenControl({
9494
path: p.path,
9595
historyAgentIds: p.agentIds,
96-
liveAgentIds: liveProjectProviders(p.path),
96+
liveAgentIds: () => liveProjectProviders(p.path),
9797
compact,
9898
onOpen: (intent) => openInTerminal([{
9999
...intent, name: p.name, staleLevel: p.stale.level, branch: p.branch, dirty: p.uncommitted,

‎src/renderer/providerOpenControl.ts‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { createProviderLogo, providerName } from './providerLogo';
77
export interface ProviderOpenControlOptions {
88
path: string;
99
historyAgentIds: readonly AgentId[];
10-
liveAgentIds: readonly AgentId[];
10+
liveAgentIds(): readonly AgentId[];
1111
compact?: boolean;
1212
onOpen(intent: ProjectOpenIntent): void;
1313
}
@@ -18,6 +18,15 @@ const outcomeKey: Record<ProviderOpenOutcome, string> = {
1818
new: 'open.status_new',
1919
};
2020

21+
// One shared selection subscription updates only controls that are still in the document. Keeping
22+
// renderers in a WeakMap avoids retaining every control ever replaced by a view re-render.
23+
const primaryRenderers = new WeakMap<HTMLElement, () => void>();
24+
subscribeAgentSelection(() => {
25+
for (const root of Array.from(document.querySelectorAll<HTMLElement>('.provider-open'))) {
26+
primaryRenderers.get(root)?.();
27+
}
28+
});
29+
2130
/** One provider-aware Open control shared by project cards, project rows, and task rows. */
2231
export function createProviderOpenControl(opts: ProviderOpenControlOptions): HTMLElement {
2332
const root = document.createElement('span');
@@ -86,7 +95,7 @@ export function createProviderOpenControl(opts: ProviderOpenControlOptions): HTM
8695

8796
const renderMenu = (): void => {
8897
menu.replaceChildren();
89-
for (const option of providerOpenOptions(installedAgents(), selectedAgent(), opts.historyAgentIds, opts.liveAgentIds)) {
98+
for (const option of providerOpenOptions(installedAgents(), selectedAgent(), opts.historyAgentIds, opts.liveAgentIds())) {
9099
const row = document.createElement('div'); row.className = 'provider-open-row'; row.setAttribute('role', 'none');
91100
const automatic = document.createElement('button');
92101
automatic.type = 'button'; automatic.className = 'provider-open-option'; automatic.setAttribute('role', 'menuitem');
@@ -145,8 +154,7 @@ export function createProviderOpenControl(opts: ProviderOpenControlOptions): HTM
145154
menu.addEventListener('keydown', onMenuKey);
146155

147156
renderPrimary();
148-
const unsubscribe = subscribeAgentSelection(() => {
149-
if (!root.isConnected) { unsubscribe(); return; }
157+
primaryRenderers.set(root, () => {
150158
renderPrimary();
151159
if (open) renderMenu();
152160
});

0 commit comments

Comments
 (0)