Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ function App() {

const clientInitialized = useRef(false);
const hapticsRuntimeRef = useRef<HapticsRuntime | null>(null);
const preferences = usePreferences();
const midiEnabled = usePreferences((state) => state.midi.enabled);
const hapticsEnabled = usePreferences((state) => state.haptics.enabled);
const connected = useConnectionStore((state) => state.connected);
const sessionReady = useConnectionStore((state) => state.sessionReady);
useFileTransferNotifications(client);
Expand Down Expand Up @@ -334,7 +335,7 @@ function App() {
}, [handleAppKeyDown]);

useEffect(() => {
if (!preferences.midi.enabled) return;
if (!midiEnabled) return;

let cancelled = false;
import("./VirtualMidiService")
Expand All @@ -355,7 +356,7 @@ function App() {
return () => {
cancelled = true;
};
}, [preferences.midi.enabled]);
}, [midiEnabled]);

// Window subtitle tracks the current room from the room store. On disconnect
// the client resets the store, which clears roomInfo and so clears the subtitle.
Expand All @@ -372,8 +373,8 @@ function App() {
}, [client, roomInfo]);

useEffect(() => {
hapticsRuntimeRef.current?.setEnabled(preferences.haptics.enabled);
}, [preferences.haptics.enabled]);
hapticsRuntimeRef.current?.setEnabled(hapticsEnabled);
}, [hapticsEnabled]);

const handleCommand = useCallback(
(text: string) => {
Expand Down
9 changes: 6 additions & 3 deletions src/audio/MediaService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,12 @@ export class MediaService {
window.addEventListener('blur', this.handleWindowBlur);
}

this.unsubscribePreferences = usePreferences.subscribe(() => {
this.updateBackgroundMuteState();
});
this.unsubscribePreferences = usePreferences.subscribe(
(state) => state.sound.muteInBackground,
() => {
this.updateBackgroundMuteState();
},
);
}

get muted(): boolean {
Expand Down
6 changes: 3 additions & 3 deletions src/components/HapticsStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ interface HapticsStatusProps {
}

const HapticsStatus: React.FC<HapticsStatusProps> = () => {
const preferences = usePreferences();
const hapticsPreferences = usePreferences((state) => state.haptics);
const [capabilities, setCapabilities] = useState<HapticsCapabilities>(
hapticsService.getCapabilities()
);
Expand Down Expand Up @@ -61,7 +61,7 @@ const HapticsStatus: React.FC<HapticsStatusProps> = () => {
}
};

if (!preferences.haptics.enabled) {
if (!hapticsPreferences.enabled) {
return (
<div style={{ padding: "10px" }}>
<p>Haptics is disabled. Enable it in preferences to use haptics features.</p>
Expand Down Expand Up @@ -252,7 +252,7 @@ const HapticsStatus: React.FC<HapticsStatusProps> = () => {
<p>Bluetooth devices are discovered via in-browser WebBluetooth (Chromium browsers only).</p>
<p>The server can send haptic commands when the Client.Haptics GMCP package is active.</p>
<p>Press Escape or use the Emergency Stop button to immediately halt all haptic output.</p>
<p>Intensity cap: {(preferences.haptics.intensityCap * 100).toFixed(0)}% | Auto-stop: {preferences.haptics.autoStopTimeout}s</p>
<p>Intensity cap: {(hapticsPreferences.intensityCap * 100).toFixed(0)}% | Auto-stop: {hapticsPreferences.autoStopTimeout}s</p>
</div>
</details>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/components/MidiStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface DeviceChangeEvent {
}

const MidiStatus: React.FC<MidiStatusProps> = ({ client }) => {
const preferences = usePreferences();
const midiPreferences = usePreferences((state) => state.midi);
const [midiPackage, setMidiPackage] = useState<GMCPClientMidi | null>(null);
const [inputDevices, setInputDevices] = useState<MidiDevice[]>([]);
const [outputDevices, setOutputDevices] = useState<MidiDevice[]>([]);
Expand Down Expand Up @@ -58,7 +58,7 @@ const MidiStatus: React.FC<MidiStatusProps> = ({ client }) => {

// Load devices when MIDI is enabled
const loadDevices = async () => {
if (!preferences.midi.enabled) return;
if (!midiPreferences.enabled) return;

// Ensure virtual synthesizer is initialized
if (!virtualMidiService.initialized) {
Expand Down Expand Up @@ -146,7 +146,7 @@ const MidiStatus: React.FC<MidiStatusProps> = ({ client }) => {
};

useEffect(() => {
if (preferences.midi.enabled) {
if (midiPreferences.enabled) {
// Initialize MIDI if not already done
if (!midiService.isInitialized && midiPackage) {
midiPackage.ensureInitialized().then(() => {
Expand Down Expand Up @@ -227,7 +227,7 @@ const MidiStatus: React.FC<MidiStatusProps> = ({ client }) => {
});
setReconnectableDevices({});
}
}, [preferences.midi.enabled, midiService.isInitialized]);
}, [midiPreferences.enabled, midiService.isInitialized]);

// Update reconnectable devices when connection state or device lists change
useEffect(() => {
Expand Down Expand Up @@ -363,7 +363,7 @@ const MidiStatus: React.FC<MidiStatusProps> = ({ client }) => {
return `${noteName}${octave}`;
};

if (!preferences.midi.enabled) {
if (!midiPreferences.enabled) {
return (
<div style={{ padding: "10px" }}>
<p>MIDI is disabled. Enable it in preferences to use MIDI features.</p>
Expand Down
20 changes: 14 additions & 6 deletions src/components/editor/editorWindow.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,20 @@ vi.mock('@react-aria/live-announcer', () => ({
}));

vi.mock('../../stores/preferencesStore', () => ({
usePreferences: () => ({
editor: {
accessibilityMode: false,
autocompleteEnabled: true,
},
}),
usePreferences: (
selector: (state: {
editor: {
accessibilityMode: boolean;
autocompleteEnabled: boolean;
};
}) => unknown,
) =>
selector({
editor: {
accessibilityMode: false,
autocompleteEnabled: true,
},
}),
}));

vi.mock('../../editor/monacoLoader', () => ({
Expand Down
6 changes: 3 additions & 3 deletions src/components/editor/editorWindow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ function EditorWindow() {
const handleEditorBeforeMount = (monaco: Monaco) => {
registerMooLanguage(monaco);
};
const prefState = usePreferences();
const accessibilityMode = prefState.editor.accessibilityMode;
const autocompleteEnabled = prefState.editor.autocompleteEnabled;
const editorPreferences = usePreferences((state) => state.editor);
const accessibilityMode = editorPreferences.accessibilityMode;
const autocompleteEnabled = editorPreferences.autocompleteEnabled;
const editorLanguage = getEditorLanguageForSessionType(session.type);
const updateMooDiagnostics = React.useCallback((markers: MonacoEditor.IMarkerData[]) => {
setMooDiagnosticMarkers(markers);
Expand Down
11 changes: 6 additions & 5 deletions src/components/output.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,8 @@ class Output extends React.Component<Props, State> {
};
}

handlePreferencesChange = () => {
this.setState({
localEchoActive: usePreferences.getState().general.localEcho,
});
handlePreferencesChange = (localEchoActive: boolean) => {
this.setState({ localEchoActive });
};

saveOutput = () => {
Expand Down Expand Up @@ -481,7 +479,10 @@ componentDidUpdate(
};

componentDidMount() {
this.unsubscribePrefs = usePreferences.subscribe(this.handlePreferencesChange);
this.unsubscribePrefs = usePreferences.subscribe(
(state) => state.general.localEcho,
this.handlePreferencesChange,
);
this.unsubscribeUserlist = useUserlistStore.subscribe(this.handleUserlistVisibility);
this.unsubscribeConnection = useConnectionStore.subscribe(this.handleConnectionStateChange);
this.unsubscribeOutputStore = useOutputStore.subscribe(this.handleOutputStoreEntries);
Expand Down
Loading