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>
{{ vite_assets('body')|safe }}
<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='shared-helpers.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='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='shell-bridge.js', v=static_v) }}"></script>
<!-- Notification bell + floating helper toggle — always accessible above modals -->
<!-- Ambient glow under the global search bar. Radial gradient, brightest
directly under the bar, tapering out toward the window corners.

View file

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

View file

@ -329,50 +329,6 @@ function activatePage(pageId, options = {}) {
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) {
// Renders avatar as image (if avatar_url set) or colored initial fallback
// 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));