Skip to content

Commit 1836b02

Browse files
tomymaritanoclaude
andauthored
fix: gate built-in plugins until enabled state loads + fix IPC listener cleanup (#239)
## Summary This commit was pushed to #237 after the squash-merge, so it didn't make it into develop. Cherry-picked here. - **P1: gate built-in plugins until enabled state loads** — `builtInEnabledMap` started as `{}` so `!== false` passed for every plugin, briefly activating disabled plugins on launch. Now starts as `null` and built-in plugins are excluded from `PluginHost` until `listState()` resolves - **P2: fix IPC listener cleanup** — `ipc.on()` cleanup used `removeAllListeners(channel)` which nuked other listeners on the same channel (e.g. `pluginRuntimeStore`). Changed to `removeListener` with the specific handler reference ## Test plan - [x] `pnpm typecheck` — clean - [x] `pnpm test` — all pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 272c85f commit 1836b02

2 files changed

Lines changed: 9 additions & 4 deletions

File tree

apps/desktop/src/preload/api/settings.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ export function createSettingsApi(): SettingsAPI {
3636
export function createIpcApi(): IpcAPI {
3737
return {
3838
on: (channel: string, listener: (...args: unknown[]) => void) => {
39-
ipcRenderer.on(channel, (_event, ...args) => listener(...args));
39+
const handler = (_event: Electron.IpcRendererEvent, ...args: unknown[]) => listener(...args);
40+
ipcRenderer.on(channel, handler);
4041
return () => {
41-
ipcRenderer.removeAllListeners(channel);
42+
ipcRenderer.removeListener(channel, handler);
4243
};
4344
},
4445
};

apps/desktop/src/renderer/App.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ function NotesApp() {
438438
// Plugin runtime: init once, React observes
439439
const discoveredPlugins = useStore(pluginRuntimeStore, s => s.plugins);
440440
const pluginErrors = useStore(pluginRuntimeStore, s => s.errors);
441-
const [builtInEnabledMap, setBuiltInEnabledMap] = useState<Record<string, boolean>>({});
441+
const [builtInEnabledMap, setBuiltInEnabledMap] = useState<Record<string, boolean> | null>(null);
442442

443443
useEffect(() => {
444444
void pluginRuntimeStore.getState().init();
@@ -469,7 +469,11 @@ function NotesApp() {
469469
}, []);
470470

471471
const allPlugins = useMemo(() => {
472-
const enabledBuiltIn = builtInPlugins.filter(p => builtInEnabledMap[p.id] !== false);
472+
// Don't mount built-in plugins until the enabled state is loaded
473+
// to avoid activating disabled plugins on the initial render
474+
const enabledBuiltIn = builtInEnabledMap
475+
? builtInPlugins.filter(p => builtInEnabledMap[p.id] !== false)
476+
: [];
473477
return [...enabledBuiltIn, ...discoveredPlugins];
474478
}, [discoveredPlugins, builtInEnabledMap]);
475479

0 commit comments

Comments
 (0)