Centralize shell bridge glue

This commit is contained in:
Antti Kettunen 2026-04-27 07:49:55 +03:00
parent 147a09035c
commit a9976c54ae
No known key found for this signature in database
GPG key ID: C6B2A3D250359BD7
4 changed files with 103 additions and 46 deletions

View file

@ -7777,7 +7777,7 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
{{ vite_assets('body')|safe }} {{ vite_assets('body')|safe }}
<script src="{{ url_for('static', filename='setup-wizard.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='setup-wizard.js', v=static_v) }}"></script>
<!-- Split modules (was: script.js) — core.js must load first, init.js last --> <!-- Split modules (was: script.js) — core.js must load first, init.js before shell-bridge.js -->
<script src="{{ url_for('static', filename='core.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='core.js', v=static_v) }}"></script>
<script src="{{ url_for('static', filename='shared-helpers.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='shared-helpers.js', v=static_v) }}"></script>
<script src="{{ url_for('static', filename='media-player.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='media-player.js', v=static_v) }}"></script>
@ -7796,6 +7796,7 @@
<script src="{{ url_for('static', filename='stats-automations.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='stats-automations.js', v=static_v) }}"></script>
<script src="{{ url_for('static', filename='pages-extra.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='pages-extra.js', v=static_v) }}"></script>
<script src="{{ url_for('static', filename='init.js', v=static_v) }}"></script> <script src="{{ url_for('static', filename='init.js', v=static_v) }}"></script>
<script src="{{ url_for('static', filename='shell-bridge.js', v=static_v) }}"></script>
<!-- Notification bell + floating helper toggle — always accessible above modals --> <!-- Notification bell + floating helper toggle — always accessible above modals -->
<!-- Ambient glow under the global search bar. Radial gradient, brightest <!-- Ambient glow under the global search bar. Radial gradient, brightest
directly under the bar, tapering out toward the window corners. directly under the bar, tapering out toward the window corners.

View file

@ -30,7 +30,7 @@ declare global {
}; };
SoulSyncWebShellBridge?: { SoulSyncWebShellBridge?: {
getCurrentPageId: () => ShellPageId; getCurrentPageId: () => ShellPageId;
getCurrentProfileContext: () => ShellProfileContext; getCurrentProfileContext: () => ShellProfileContext | null;
isPageAllowed: (pageId: ShellPageId) => boolean; isPageAllowed: (pageId: ShellPageId) => boolean;
getProfileHomePage: () => ShellPageId; getProfileHomePage: () => ShellPageId;
resolveLegacyPath: (pathname: string) => ShellPageId | null; resolveLegacyPath: (pathname: string) => ShellPageId | null;

View file

@ -329,50 +329,6 @@ function activatePage(pageId, options = {}) {
loadPageData(pageId); loadPageData(pageId);
} }
function activateLegacyPath(pathname) {
const router = getWebRouter();
const targetPage = router?.resolvePageId?.(pathname) || _getPageFromPath(pathname);
if (!targetPage) return;
if (!isPageAllowed(targetPage)) {
const home = getProfileHomePage();
if (home !== targetPage) {
navigateToPage(home, { replace: true });
}
return;
}
activatePage(targetPage, { forceReload: true });
}
const SHELL_BRIDGE_READY_EVENT = 'ss:webui-shell-bridge-ready';
window.SoulSyncWebShellBridge = {
getCurrentPageId() {
return currentPage || getWebRouter()?.resolvePageId?.(window.location.pathname) || _getPageFromPath();
},
getCurrentProfileContext() {
return getCurrentProfileContext();
},
isPageAllowed(pageId) {
return isPageAllowed(pageId);
},
getProfileHomePage() {
return getProfileHomePage();
},
setActivePageChrome(pageId) {
setActivePageChrome(pageId);
},
activateLegacyPath(pathname) {
activateLegacyPath(pathname);
},
showReactHost(pageId) {
showReactHost(pageId);
},
};
window.dispatchEvent(new CustomEvent(SHELL_BRIDGE_READY_EVENT));
function renderProfileAvatar(el, profile) { function renderProfileAvatar(el, profile) {
// Renders avatar as image (if avatar_url set) or colored initial fallback // Renders avatar as image (if avatar_url set) or colored initial fallback
// Preserves existing classes, ensures 'profile-avatar' is present // Preserves existing classes, ensures 'profile-avatar' is present

View file

@ -0,0 +1,100 @@
// SoulSync shell bridge glue
// Keep this file loaded after init.js so the legacy shell helpers it wraps
// have already been defined.
function getWebRouter() {
return window.SoulSyncWebRouter ?? null;
}
function showLegacyPage(pageId) {
document.querySelectorAll('.page').forEach(page => {
page.classList.remove('active');
});
const page = document.getElementById(`${pageId}-page`);
if (page) {
page.classList.add('active');
}
const reactHost = document.getElementById('webui-react-root');
if (reactHost) {
reactHost.classList.remove('active');
}
}
function setActivePageChrome(pageId) {
document.querySelectorAll('.nav-button').forEach(btn => {
btn.classList.remove('active');
});
const navButton = document.querySelector(`[data-page="${pageId}"]`);
if (navButton) {
navButton.classList.add('active');
}
currentPage = pageId;
if (typeof _gsUpdateVisibility === 'function') _gsUpdateVisibility();
if (window.pageParticles && window._particlesEnabled !== false) window.pageParticles.setPage(pageId);
if (window.workerOrbs) window.workerOrbs.setPage(pageId);
}
function showReactHost(pageId) {
document.querySelectorAll('.page').forEach(page => {
page.classList.remove('active');
});
const host = document.getElementById('webui-react-root');
if (host) {
host.classList.add('active');
}
currentPage = pageId;
if (typeof _gsUpdateVisibility === 'function') _gsUpdateVisibility();
if (window.pageParticles && window._particlesEnabled !== false) window.pageParticles.setPage(pageId);
if (window.workerOrbs) window.workerOrbs.setPage(pageId);
}
function activateLegacyPath(pathname) {
const router = getWebRouter();
const targetPage = router?.resolvePageId?.(pathname) || _getPageFromPath(pathname);
if (!targetPage) return;
if (!isPageAllowed(targetPage)) {
const home = getProfileHomePage();
if (home !== targetPage) {
navigateToPage(home, { replace: true });
}
return;
}
activatePage(targetPage, { forceReload: true });
}
const SHELL_BRIDGE_READY_EVENT = 'ss:webui-shell-bridge-ready';
window.SoulSyncWebShellBridge = {
getCurrentPageId() {
return currentPage || getWebRouter()?.resolvePageId?.(window.location.pathname) || _getPageFromPath();
},
getCurrentProfileContext() {
if (!currentProfile) return null;
return {
profileId: currentProfile.id,
isAdmin: !!currentProfile.is_admin,
};
},
isPageAllowed(pageId) {
return isPageAllowed(pageId);
},
getProfileHomePage() {
return getProfileHomePage();
},
resolveLegacyPath(pathname) {
return getWebRouter()?.resolvePageId?.(pathname) ?? null;
},
setActivePageChrome(pageId) {
setActivePageChrome(pageId);
},
activateLegacyPath(pathname) {
activateLegacyPath(pathname);
},
showReactHost(pageId) {
showReactHost(pageId);
},
};
window.dispatchEvent(new CustomEvent(SHELL_BRIDGE_READY_EVENT));