diff --git a/src/app/plugins/react-custom-html-parser.tsx b/src/app/plugins/react-custom-html-parser.tsx
index 60aead019..17cbb84fb 100644
--- a/src/app/plugins/react-custom-html-parser.tsx
+++ b/src/app/plugins/react-custom-html-parser.tsx
@@ -125,7 +125,7 @@ function KatexRenderer({
useEffect(() => {
let mounted = true;
- void Promise.all([import('katex'), import('katex/dist/katex.min.css')]).then(([katex]) => {
+ void import('katex').then((katex) => {
if (mounted) {
setHtml(katex.default.renderToString(math, { throwOnError: false, displayMode }));
}
diff --git a/src/app/utils/mediaTransport.test.ts b/src/app/utils/mediaTransport.test.ts
index 9d599313c..de6caf937 100644
--- a/src/app/utils/mediaTransport.test.ts
+++ b/src/app/utils/mediaTransport.test.ts
@@ -114,6 +114,36 @@ describe('fetchMediaBlob', () => {
TEST_TIMEOUT
);
+ it('selects the JSON-encoded active session instead of falling back to the first account', async () => {
+ const { getActiveMediaSession, getCurrentMediaSessionScope } = await import('./mediaTransport');
+
+ localStorage.setItem(
+ 'matrixSessions',
+ JSON.stringify([
+ {
+ baseUrl: 'https://matrix.example.org',
+ userId: '@alice:example.org',
+ deviceId: 'ALICE',
+ accessToken: 'alice-token',
+ },
+ {
+ baseUrl: 'https://other.example.org',
+ userId: '@bob:example.org',
+ deviceId: 'BOB',
+ accessToken: 'bob-token',
+ },
+ ])
+ );
+ localStorage.setItem('matrixActiveSession', JSON.stringify('@bob:example.org'));
+
+ expect(getActiveMediaSession()).toEqual({
+ baseUrl: 'https://other.example.org',
+ accessToken: 'bob-token',
+ userId: '@bob:example.org',
+ });
+ expect(getCurrentMediaSessionScope()).toBe('@bob:example.org');
+ });
+
it(
'uses caller-provided auth and cache scope when present',
async () => {
diff --git a/src/app/utils/mediaTransport.ts b/src/app/utils/mediaTransport.ts
index e4a93b710..34083eb76 100644
--- a/src/app/utils/mediaTransport.ts
+++ b/src/app/utils/mediaTransport.ts
@@ -65,11 +65,24 @@ function parseStoredSessions(): StoredSession[] {
}
}
+function getStoredActiveSessionId(): string | undefined {
+ const raw = localStorage.getItem(ACTIVE_SESSION_KEY);
+ if (!raw) return undefined;
+
+ try {
+ const parsed = JSON.parse(raw);
+ return typeof parsed === 'string' ? parsed : undefined;
+ } catch {
+ // Keep reading pre-multi-account values written without JSON encoding.
+ return raw;
+ }
+}
+
function getActiveStoredSession(): StoredSession | undefined {
if (typeof localStorage === 'undefined') return undefined;
const sessions = parseStoredSessions();
- const activeSessionId = localStorage.getItem(ACTIVE_SESSION_KEY);
+ const activeSessionId = getStoredActiveSessionId();
return (
(activeSessionId
? sessions.find((session) => session.userId === activeSessionId)
diff --git a/src/app/utils/tauriMediaAuth.test.ts b/src/app/utils/tauriMediaAuth.test.ts
index db576446e..2c5713f71 100644
--- a/src/app/utils/tauriMediaAuth.test.ts
+++ b/src/app/utils/tauriMediaAuth.test.ts
@@ -112,4 +112,30 @@ describe('Tauri media session coordinator', () => {
const scopes = commands.setMediaSession.mock.calls.map(([params]) => params.scope);
expect(scopes).toEqual(['@a:matrix.example', '@a:matrix.example']);
});
+
+ it('syncs the native session when the active account changes in this window', async () => {
+ mediaTransport.getActiveMediaSession
+ .mockReturnValueOnce({
+ baseUrl: 'https://one.example',
+ accessToken: 'one',
+ userId: '@a:one.example',
+ })
+ .mockReturnValue({
+ baseUrl: 'https://two.example',
+ accessToken: 'two',
+ userId: '@b:two.example',
+ });
+
+ await initTauriMediaSession();
+ commands.setMediaSession.mockClear();
+
+ window.dispatchEvent(new Event('sable-session-changed'));
+ await vi.waitFor(() => expect(commands.setMediaSession).toHaveBeenCalledTimes(1));
+
+ expect(commands.setMediaSession).toHaveBeenCalledWith({
+ baseUrl: 'https://two.example',
+ token: 'two',
+ scope: '@b:two.example',
+ });
+ });
});
diff --git a/src/app/utils/tauriMediaAuth.ts b/src/app/utils/tauriMediaAuth.ts
index 40da92901..6ca4877d0 100644
--- a/src/app/utils/tauriMediaAuth.ts
+++ b/src/app/utils/tauriMediaAuth.ts
@@ -6,6 +6,7 @@ import { getActiveMediaSession } from './mediaTransport';
const log = createLogger('tauri-media-auth');
let pendingNativeWrite: Promise = Promise.resolve();
+let tauriMediaSessionListenersInstalled = false;
export const updateTauriMediaSession = (
baseUrl?: string,
@@ -41,9 +42,14 @@ const syncTauriMediaSession = (): Promise => {
export const initTauriMediaSession = (): Promise => {
if (!isTauri()) return Promise.resolve();
- const initialSync = syncTauriMediaSession();
- window.addEventListener('storage', () => {
- void syncTauriMediaSession();
- });
- return initialSync;
+ if (!tauriMediaSessionListenersInstalled) {
+ const sync = () => {
+ void syncTauriMediaSession();
+ };
+ window.addEventListener('storage', sync);
+ window.addEventListener('sable-session-changed', sync);
+ tauriMediaSessionListenersInstalled = true;
+ }
+
+ return syncTauriMediaSession();
};
diff --git a/src/index.tsx b/src/index.tsx
index 1b67abe5b..ded943743 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -7,6 +7,7 @@ import '@fontsource/space-mono/400.css';
import '@fontsource/space-mono/700.css';
import '@fontsource/space-mono/400-italic.css';
import '@fontsource/space-mono/700-italic.css';
+import 'katex/dist/katex.min.css';
import 'folds/dist/style.css';
import { configClass, varsClass } from 'folds';
import App from './app/pages/App';
diff --git a/vite.config.ts b/vite.config.ts
index f980a90c8..e1dd090b7 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -143,7 +143,7 @@ export default defineConfig(({ command }) => {
clearScreen: false,
appType: 'spa',
publicDir: false,
- base: buildConfig.base,
+ base: command === 'build' && isTauriBuild ? './' : buildConfig.base,
envPrefix: ['VITE_', 'TAURI_ENV_*'],
define: {
APP_VERSION: JSON.stringify(appVersion),