| 79 | |
| 80 | /** Mount a view by id into the shadow island. */ |
| 81 | export async function show(routeId) { |
| 82 | if (!shadow) return null; |
| 83 | const id = VIEWS[routeId] ? routeId : 'dashboard'; |
| 84 | // Idempotent: re-showing the route that's already mounted must NOT tear the |
| 85 | // view down and rebuild it. The old behaviour wiped innerHTML and reset |
| 86 | // scroll (scrollTo(0,0)) on every call, so any stray re-invocation of |
| 87 | // init()/show() for the current route produced a visible twitch + jump to |
| 88 | // top. `activeRoute` is set synchronously so rapid double-calls also no-op. |
| 89 | if (id === activeRoute) return id; |
| 90 | activeRoute = id; |
| 91 | if (current && current.cleanup) { |
| 92 | try { current.cleanup(); } catch (e) { console.warn('[rusense] cleanup', e); } |
| 93 | } |
| 94 | viewEl.innerHTML = ''; |
| 95 | viewEl.scrollTo && viewEl.scrollTo(0, 0); |
| 96 | let cleanup = null; |
| 97 | try { |
| 98 | cleanup = (await VIEWS[id].mount(viewEl)) || null; |
| 99 | } catch (err) { |
| 100 | console.error(`[rusense] view "${id}" failed:`, err); |
| 101 | viewEl.appendChild( |
| 102 | html`<div class="card card-pad text-bad">View failed to load: ${err && err.message}</div>` |
| 103 | ); |
| 104 | } |
| 105 | current = { route: id, cleanup }; |
| 106 | return id; |
| 107 | } |
| 108 | |
| 109 | /** Open the RuSense island and show `route`. Idempotent (safe to call repeatedly). */ |
| 110 | export function init(host, route) { |