[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
143
application/state/notesStore.ts
Normal file
143
application/state/notesStore.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
|
||||
import type { VaultNote } from '../../domain/models';
|
||||
|
||||
type Listener = () => void;
|
||||
|
||||
export type NotesSnapshot = {
|
||||
notes: readonly VaultNote[];
|
||||
noteGroups: readonly string[];
|
||||
};
|
||||
|
||||
export type NotesActions = {
|
||||
updateNotes: (notes: VaultNote[]) => boolean | void;
|
||||
updateNoteGroups: (groups: string[]) => void;
|
||||
};
|
||||
|
||||
const EMPTY_NOTES: readonly VaultNote[] = Object.freeze([]);
|
||||
const EMPTY_NOTE_GROUPS: readonly string[] = Object.freeze([]);
|
||||
|
||||
export const EMPTY_NOTES_SNAPSHOT: NotesSnapshot = Object.freeze({
|
||||
notes: EMPTY_NOTES,
|
||||
noteGroups: EMPTY_NOTE_GROUPS,
|
||||
});
|
||||
|
||||
/**
|
||||
* External store for vault notes so Notes / AI side-panel consumers can
|
||||
* subscribe without forcing TerminalLayer re-renders on every note edit.
|
||||
*/
|
||||
class NotesStore {
|
||||
private snapshot: NotesSnapshot = EMPTY_NOTES_SNAPSHOT;
|
||||
private actions: NotesActions | null = null;
|
||||
private listeners = new Set<Listener>();
|
||||
private actionListeners = new Set<Listener>();
|
||||
|
||||
getSnapshot = (): NotesSnapshot => this.snapshot;
|
||||
|
||||
subscribe = (listener: Listener): (() => void) => {
|
||||
this.listeners.add(listener);
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
setSnapshot(next: NotesSnapshot): void {
|
||||
if (
|
||||
this.snapshot.notes === next.notes
|
||||
&& this.snapshot.noteGroups === next.noteGroups
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.snapshot = next;
|
||||
for (const listener of this.listeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
getActions = (): NotesActions | null => this.actions;
|
||||
|
||||
subscribeActions = (listener: Listener): (() => void) => {
|
||||
this.actionListeners.add(listener);
|
||||
return () => {
|
||||
this.actionListeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
setActions(next: NotesActions | null): void {
|
||||
if (this.actions === next) return;
|
||||
this.actions = next;
|
||||
for (const listener of this.actionListeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const notesStore = new NotesStore();
|
||||
|
||||
export function publishNotesSnapshot(snapshot: NotesSnapshot): void {
|
||||
notesStore.setSnapshot(snapshot);
|
||||
}
|
||||
|
||||
export function getNotesSnapshot(): NotesSnapshot {
|
||||
return notesStore.getSnapshot();
|
||||
}
|
||||
|
||||
export function subscribeNotes(listener: Listener): () => void {
|
||||
return notesStore.subscribe(listener);
|
||||
}
|
||||
|
||||
/** No-op subscribe for gated (hidden) panel mounts. */
|
||||
export function subscribeNotesNoop(_listener: Listener): () => void {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
export function getEmptyNotesSnapshot(): NotesSnapshot {
|
||||
return EMPTY_NOTES_SNAPSHOT;
|
||||
}
|
||||
|
||||
export function registerNotesActions(actions: NotesActions | null): void {
|
||||
notesStore.setActions(actions);
|
||||
}
|
||||
|
||||
export function getNotesActions(): NotesActions | null {
|
||||
return notesStore.getActions();
|
||||
}
|
||||
|
||||
export function subscribeNotesActions(listener: Listener): () => void {
|
||||
return notesStore.subscribeActions(listener);
|
||||
}
|
||||
|
||||
const noopUpdateNotes: NotesActions['updateNotes'] = () => {};
|
||||
const noopUpdateNoteGroups: NotesActions['updateNoteGroups'] = () => {};
|
||||
|
||||
/**
|
||||
* Subscribe to notes catalog + vault mutation actions for Notes / AI panels.
|
||||
*
|
||||
* Pass `{ enabled: false }` for retained-but-hidden mounts (e.g. terminal
|
||||
* notes side panel) so vault note publishes do not re-render them. Snapshot
|
||||
* reads still use the live store so reopen does not flash empty data.
|
||||
*/
|
||||
export function useNotesStore(options?: { enabled?: boolean }): {
|
||||
notes: VaultNote[];
|
||||
noteGroups: string[];
|
||||
updateNotes: NotesActions['updateNotes'];
|
||||
updateNoteGroups: NotesActions['updateNoteGroups'];
|
||||
} {
|
||||
const enabled = options?.enabled !== false;
|
||||
const snapshot = useSyncExternalStore(
|
||||
enabled ? subscribeNotes : subscribeNotesNoop,
|
||||
getNotesSnapshot,
|
||||
getNotesSnapshot,
|
||||
);
|
||||
const actions = useSyncExternalStore(
|
||||
enabled ? subscribeNotesActions : subscribeNotesNoop,
|
||||
getNotesActions,
|
||||
getNotesActions,
|
||||
);
|
||||
return {
|
||||
notes: snapshot.notes as VaultNote[],
|
||||
noteGroups: snapshot.noteGroups as string[],
|
||||
updateNotes: actions?.updateNotes ?? noopUpdateNotes,
|
||||
updateNoteGroups: actions?.updateNoteGroups ?? noopUpdateNoteGroups,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user