({ children }: { children: ReactNode })
| 92 | const EMPTY_AI_SETTING: AISetting = createProto(AISettingSchema, {}); |
| 93 | |
| 94 | export function AIContextProvider({ children }: { children: ReactNode }) { |
| 95 | // ---- AI setting -------------------------------------------------------- |
| 96 | |
| 97 | // The original Vue ProvideAIContext fetched the AI setting on mount. |
| 98 | // Mirror that here. `getOrFetchSettingByName` is idempotent; firing it |
| 99 | // again on remount is cheap. |
| 100 | useEffect(() => { |
| 101 | void useAppStore |
| 102 | .getState() |
| 103 | .getOrFetchSettingByName(Setting_SettingName.AI, /* silent */ true); |
| 104 | }, []); |
| 105 | |
| 106 | // Subscribe to the setting cache so this re-resolves once the AI setting |
| 107 | // loads (selector returns the AI value or the stable empty fallback). |
| 108 | const settingsByName = useAppStore((s) => s.settingsByName); |
| 109 | const aiSetting = useMemo<AISetting>(() => { |
| 110 | const setting = useAppStore |
| 111 | .getState() |
| 112 | .getSettingByName(Setting_SettingName.AI); |
| 113 | if (setting?.value?.value?.case === "ai") { |
| 114 | return setting.value.value.value; |
| 115 | } |
| 116 | return EMPTY_AI_SETTING; |
| 117 | }, [settingsByName]); |
| 118 | |
| 119 | const currentTab = useCurrentSQLEditorTab(); |
| 120 | |
| 121 | const { instance, database } = useConnectionOfCurrentSQLEditorTab(); |
| 122 | const engine: Engine | undefined = instance.engine; |
| 123 | |
| 124 | // `useAppDatabaseMetadata` self-fetches and subscribes; the app-store |
| 125 | // dbSchema slice dedupes concurrent fetches, so this stays cheap even |
| 126 | // if another mount triggers the same fetch in parallel. |
| 127 | const databaseName = database.name; |
| 128 | const fetchedMetadata = useAppDatabaseMetadata(databaseName ?? ""); |
| 129 | const databaseMetadata = databaseName ? fetchedMetadata : undefined; |
| 130 | |
| 131 | const schema: string | undefined = currentTab?.connection.schema; |
| 132 | |
| 133 | // ---- Per-tab chat info ------------------------------------------------- |
| 134 | // |
| 135 | // Conversations live in the Zustand `useConversationStore`. We subscribe to |
| 136 | // the conversation map + per-connection ready flags and derive the current |
| 137 | // tab's chat (list / ready / selected) with plain React state — no Vue refs. |
| 138 | const tabInstance = currentTab?.connection.instance; |
| 139 | const tabDatabase = currentTab?.connection.database; |
| 140 | const connKey = |
| 141 | tabInstance && tabDatabase ? `${tabInstance}/${tabDatabase}` : ""; |
| 142 | |
| 143 | const conversationsById = useConversationStore((s) => s.conversationsById); |
| 144 | const readyByConnection = useConversationStore((s) => s.readyByConnection); |
| 145 | |
| 146 | // Fetch the tab's conversations on connection change; the store sets the |
| 147 | // ready flag and merges results. |
| 148 | useEffect(() => { |
| 149 | if (!tabInstance || !tabDatabase) return; |
| 150 | void useConversationStore.getState().fetchConversationListByConnection({ |
| 151 | instance: tabInstance, |
nothing calls this directly
no test coverage detected