fix(settings): react to anchor deep-links when already mounted

When clicking "KI-Einstellungen öffnen" from the companion chat while
settings is already open on a different tab, the settings panel now
correctly switches to the right tab and scrolls to the anchor.

The workbench deep-link $effect dispatches a custom
workbench:navigate-anchor event after opening/focusing the target panel.
The settings ListView listens for both this event and native hashchange,
then calls navigateToHash() to switch activeCategory and scroll.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-17 15:17:30 +02:00
parent 1cfd05939e
commit 4aafbf6f6d
2 changed files with 39 additions and 2 deletions

View file

@ -20,8 +20,7 @@
let activeCategory = $state<CategoryId>('general');
onMount(() => {
const hash = window.location.hash?.slice(1);
function navigateToHash(hash: string) {
if (!hash) return;
const cat = categories.find((c) => c.anchors.includes(hash));
if (cat) activeCategory = cat.id;
@ -29,6 +28,33 @@
const el = document.getElementById(hash);
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
});
}
onMount(() => {
const hash = window.location.hash?.slice(1);
if (hash) navigateToHash(hash);
});
// React to anchor navigations while already mounted (e.g. deep-link
// from companion chat "KI-Einstellungen öffnen" when settings is
// already open on a different tab). Listens for both native hashchange
// and the custom workbench:navigate-anchor event dispatched by the
// workbench deep-link handler.
$effect(() => {
const onHashChange = () => {
const hash = window.location.hash?.slice(1);
if (hash) navigateToHash(hash);
};
const onAnchor = (e: Event) => {
const anchor = (e as CustomEvent<{ anchor: string }>).detail?.anchor;
if (anchor) navigateToHash(anchor);
};
window.addEventListener('hashchange', onHashChange);
window.addEventListener('workbench:navigate-anchor', onAnchor);
return () => {
window.removeEventListener('hashchange', onHashChange);
window.removeEventListener('workbench:navigate-anchor', onAnchor);
};
});
function jumpTo(entry: SearchEntry) {
@ -68,6 +94,7 @@
gap: 1rem;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
}
.settings-content {

View file

@ -89,15 +89,25 @@
$effect(() => {
const target = $page.url.searchParams.get('app');
if (!target || !getApp(target)) return;
const hash = $page.url.hash?.slice(1) || '';
// Use queueMicrotask so we don't mutate state during the effect's first run
queueMicrotask(async () => {
const already = workbenchScenesStore.openApps.find((a) => a.appId === target);
if (!already) await workbenchScenesStore.addApp(target);
await tick();
scrollToPage(target);
// Clean the ?app= param but preserve the hash for the target panel
const clean = new URL($page.url);
clean.searchParams.delete('app');
history.replaceState({}, '', clean);
// Notify the target panel about the hash anchor (e.g. settings
// needs to switch to the right tab and scroll to the section)
if (hash) {
await tick();
window.dispatchEvent(
new CustomEvent('workbench:navigate-anchor', { detail: { anchor: hash } })
);
}
});
});