[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:
201
application/state/terminalSettingsStore.ts
Normal file
201
application/state/terminalSettingsStore.ts
Normal file
@@ -0,0 +1,201 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
|
||||
import type {
|
||||
HotkeyScheme,
|
||||
KeyBinding,
|
||||
SessionLogFormat,
|
||||
TerminalSettings,
|
||||
} from '../../domain/models';
|
||||
import { normalizeTerminalSettings } from '../../domain/models';
|
||||
import type { useSettingsState } from './useSettingsState';
|
||||
|
||||
type SettingsState = ReturnType<typeof useSettingsState>;
|
||||
|
||||
type Listener = () => void;
|
||||
|
||||
/**
|
||||
* Terminal-facing settings slice. Mirrors `settingsChromeStore`: values and
|
||||
* actions live on separate slots so setter identity churn never invalidates
|
||||
* the value snapshot TerminalHost / terminal domain bags subscribe to.
|
||||
*/
|
||||
export type TerminalSettingsSnapshot = {
|
||||
terminalThemeId: string;
|
||||
terminalThemeDarkId: string;
|
||||
terminalThemeLightId: string;
|
||||
followAppTerminalTheme: boolean;
|
||||
terminalFontFamilyId: string;
|
||||
terminalFontSize: number;
|
||||
terminalSettings: TerminalSettings;
|
||||
hotkeyScheme: HotkeyScheme;
|
||||
keyBindings: readonly KeyBinding[];
|
||||
isHotkeyRecording: boolean;
|
||||
sftpDoubleClickBehavior: 'open' | 'transfer';
|
||||
sftpAutoSync: boolean;
|
||||
sftpShowHiddenFiles: boolean;
|
||||
sftpUseCompressedUpload: boolean;
|
||||
sftpAutoOpenSidebar: boolean;
|
||||
sftpFollowTerminalCwd: boolean;
|
||||
sftpDefaultViewMode: 'list' | 'tree';
|
||||
editorWordWrap: boolean;
|
||||
sessionLogsEnabled: boolean;
|
||||
sessionLogsDir: string;
|
||||
sessionLogsFormat: SessionLogFormat;
|
||||
sessionLogsTimestampsEnabled: boolean;
|
||||
sshDebugLogsEnabled: boolean;
|
||||
};
|
||||
|
||||
export type TerminalSettingsActions = Pick<
|
||||
SettingsState,
|
||||
| 'setTerminalThemeId'
|
||||
| 'setTerminalThemeDarkId'
|
||||
| 'setTerminalThemeLightId'
|
||||
| 'setFollowAppTerminalTheme'
|
||||
| 'setTerminalFontFamilyId'
|
||||
| 'setTerminalFontSize'
|
||||
| 'updateTerminalSetting'
|
||||
| 'setSftpFollowTerminalCwd'
|
||||
| 'setEditorWordWrap'
|
||||
| 'applyAppTheme'
|
||||
>;
|
||||
|
||||
export const DEFAULT_TERMINAL_SETTINGS_SNAPSHOT: TerminalSettingsSnapshot = Object.freeze({
|
||||
terminalThemeId: 'default',
|
||||
terminalThemeDarkId: 'default',
|
||||
terminalThemeLightId: 'default',
|
||||
followAppTerminalTheme: true,
|
||||
terminalFontFamilyId: 'default',
|
||||
terminalFontSize: 14,
|
||||
terminalSettings: normalizeTerminalSettings(),
|
||||
hotkeyScheme: 'mac',
|
||||
keyBindings: Object.freeze([]) as readonly KeyBinding[],
|
||||
isHotkeyRecording: false,
|
||||
sftpDoubleClickBehavior: 'open',
|
||||
sftpAutoSync: false,
|
||||
sftpShowHiddenFiles: false,
|
||||
sftpUseCompressedUpload: false,
|
||||
sftpAutoOpenSidebar: false,
|
||||
sftpFollowTerminalCwd: false,
|
||||
sftpDefaultViewMode: 'list',
|
||||
editorWordWrap: true,
|
||||
sessionLogsEnabled: false,
|
||||
sessionLogsDir: '',
|
||||
sessionLogsFormat: 'txt',
|
||||
sessionLogsTimestampsEnabled: false,
|
||||
sshDebugLogsEnabled: false,
|
||||
});
|
||||
|
||||
export function terminalSettingsSnapshotsEqual(
|
||||
a: TerminalSettingsSnapshot,
|
||||
b: TerminalSettingsSnapshot,
|
||||
): boolean {
|
||||
return a.terminalThemeId === b.terminalThemeId
|
||||
&& a.terminalThemeDarkId === b.terminalThemeDarkId
|
||||
&& a.terminalThemeLightId === b.terminalThemeLightId
|
||||
&& a.followAppTerminalTheme === b.followAppTerminalTheme
|
||||
&& a.terminalFontFamilyId === b.terminalFontFamilyId
|
||||
&& a.terminalFontSize === b.terminalFontSize
|
||||
&& a.terminalSettings === b.terminalSettings
|
||||
&& a.hotkeyScheme === b.hotkeyScheme
|
||||
&& a.keyBindings === b.keyBindings
|
||||
&& a.isHotkeyRecording === b.isHotkeyRecording
|
||||
&& a.sftpDoubleClickBehavior === b.sftpDoubleClickBehavior
|
||||
&& a.sftpAutoSync === b.sftpAutoSync
|
||||
&& a.sftpShowHiddenFiles === b.sftpShowHiddenFiles
|
||||
&& a.sftpUseCompressedUpload === b.sftpUseCompressedUpload
|
||||
&& a.sftpAutoOpenSidebar === b.sftpAutoOpenSidebar
|
||||
&& a.sftpFollowTerminalCwd === b.sftpFollowTerminalCwd
|
||||
&& a.sftpDefaultViewMode === b.sftpDefaultViewMode
|
||||
&& a.editorWordWrap === b.editorWordWrap
|
||||
&& a.sessionLogsEnabled === b.sessionLogsEnabled
|
||||
&& a.sessionLogsDir === b.sessionLogsDir
|
||||
&& a.sessionLogsFormat === b.sessionLogsFormat
|
||||
&& a.sessionLogsTimestampsEnabled === b.sessionLogsTimestampsEnabled
|
||||
&& a.sshDebugLogsEnabled === b.sshDebugLogsEnabled;
|
||||
}
|
||||
|
||||
class TerminalSettingsStore {
|
||||
private snapshot: TerminalSettingsSnapshot = DEFAULT_TERMINAL_SETTINGS_SNAPSHOT;
|
||||
private actions: TerminalSettingsActions | null = null;
|
||||
private listeners = new Set<Listener>();
|
||||
private actionListeners = new Set<Listener>();
|
||||
|
||||
getSnapshot = (): TerminalSettingsSnapshot => this.snapshot;
|
||||
|
||||
subscribe = (listener: Listener): (() => void) => {
|
||||
this.listeners.add(listener);
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
setSnapshot(next: TerminalSettingsSnapshot): void {
|
||||
if (terminalSettingsSnapshotsEqual(this.snapshot, next)) return;
|
||||
this.snapshot = next;
|
||||
for (const listener of this.listeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
getActions = (): TerminalSettingsActions | null => this.actions;
|
||||
|
||||
subscribeActions = (listener: Listener): (() => void) => {
|
||||
this.actionListeners.add(listener);
|
||||
return () => {
|
||||
this.actionListeners.delete(listener);
|
||||
};
|
||||
};
|
||||
|
||||
setActions(next: TerminalSettingsActions | null): void {
|
||||
if (this.actions === next) return;
|
||||
this.actions = next;
|
||||
for (const listener of this.actionListeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const terminalSettingsStore = new TerminalSettingsStore();
|
||||
|
||||
export function publishTerminalSettingsSnapshot(
|
||||
snapshot: TerminalSettingsSnapshot,
|
||||
): void {
|
||||
terminalSettingsStore.setSnapshot(snapshot);
|
||||
}
|
||||
|
||||
export function getTerminalSettingsSnapshot(): TerminalSettingsSnapshot {
|
||||
return terminalSettingsStore.getSnapshot();
|
||||
}
|
||||
|
||||
export function subscribeTerminalSettings(listener: Listener): () => void {
|
||||
return terminalSettingsStore.subscribe(listener);
|
||||
}
|
||||
|
||||
export function registerTerminalSettingsActions(
|
||||
actions: TerminalSettingsActions | null,
|
||||
): void {
|
||||
terminalSettingsStore.setActions(actions);
|
||||
}
|
||||
|
||||
export function getTerminalSettingsActions(): TerminalSettingsActions | null {
|
||||
return terminalSettingsStore.getActions();
|
||||
}
|
||||
|
||||
export function subscribeTerminalSettingsActions(listener: Listener): () => void {
|
||||
return terminalSettingsStore.subscribeActions(listener);
|
||||
}
|
||||
|
||||
export function useTerminalSettingsStore(): TerminalSettingsSnapshot {
|
||||
return useSyncExternalStore(
|
||||
subscribeTerminalSettings,
|
||||
getTerminalSettingsSnapshot,
|
||||
getTerminalSettingsSnapshot,
|
||||
);
|
||||
}
|
||||
|
||||
export function useTerminalSettingsActions(): TerminalSettingsActions | null {
|
||||
return useSyncExternalStore(
|
||||
subscribeTerminalSettingsActions,
|
||||
getTerminalSettingsActions,
|
||||
getTerminalSettingsActions,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user