Files
Netcatty/application/app/AppPluginKeybindingHost.tsx
T
GOUKI9999 98d75c13fe feat(ai): anchor workspace AI chats by id, not position
Phase 2 of #4098: every remaining positional fallback in the workspace
AI scope is replaced with id-based resolution.

- Workspace gains anchorChatId: pinned by the AI side panel whenever a
  chat becomes active in the workspace scope, persisted through the
  session restore snapshot, and validated lazily by consumers.
- resolveDisplayedPanelView: the auto-resume fallback now resolves the
  anchored chat first, then the most recently updated chat (updatedAt),
  else draft; sessions[0] is gone.
- retargetWorkspaceActiveChatAfterMemberLoss: when the focused member
  did not survive, the retarget lands on the most-recently-active
  survivor by chat recency, not the first positional member.
- plugin keybinding context: focused member, then the terminal member
  owning the anchored chat, then the most-recently-active member; a
  single-member workspace uses its only member.
- New domain/aiSessionRecency helper shared by all survivor rankings.
- /rebind and session_recover semantics (#4019) are unchanged.
2026-11-06 15:34:15 +08:00

51 lines
1.7 KiB
TypeScript

import React, { useMemo } from 'react';
import { PluginContributionHost } from '../../components/plugins/PluginContributionHost';
import type { TerminalSession, Workspace } from '../../types';
import { useActiveTabId } from '../state/activeTabStore';
import { resolveActivePluginKeybindingContext } from '../state/pluginContributionContexts';
import { getAISessionsSnapshot } from '../state/aiSessionsStore';
import { getPersistedAiSessionsSnapshot } from '../state/aiStateSnapshots';
/**
* Leaf host for plugin keybindings so AppView does not subscribe to activeTabId.
* Tab switches only re-render this small surface (and plugin lifecycle), not the shell.
*/
export function AppPluginKeybindingHost({
locale,
theme,
themeTokens,
sessions,
workspaces,
}: {
locale: string;
theme: string;
themeTokens?: Record<string, string>;
sessions: TerminalSession[];
workspaces: Workspace[];
}) {
const activeTabId = useActiveTabId();
const keybindingContext = useMemo(() => {
// AI chat recency drives the workspace anchor fallback (#4098 phase 2).
// Read without subscribing: keybinding context is best-effort and refreshes
// on every tab/session/workspaces change that matters here.
const live = getAISessionsSnapshot().sessions;
const aiSessions = live.length > 0 ? live : getPersistedAiSessionsSnapshot();
return resolveActivePluginKeybindingContext({
activeTabId,
sessions,
workspaces,
aiSessions,
});
}, [activeTabId, sessions, workspaces]);
return (
<PluginContributionHost
locale={locale}
theme={theme}
themeTokens={themeTokens}
keybindingContext={keybindingContext}
/>
);
}