diff options
| author | Adam Malczewski <[email protected]> | 2026-10-24 17:09:17 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-10-24 17:09:17 +0900 |
| commit | 0ba54b4455cb560cf7a82baf71b31ed129cfc29b (patch) | |
| tree | 424ac806b3822247c0b835ee38ffabdf87f5016e /src/features/tabs/ui | |
| parent | 83442d00d9d9c7f0ca200d6a2e8b42195708493f (diff) | |
| download | dispatch-web-0ba54b4455cb560cf7a82baf71b31ed129cfc29b.tar.gz dispatch-web-0ba54b4455cb560cf7a82baf71b31ed129cfc29b.zip | |
fix(tabs): finish accessible cascade confirmation and verify active closes
Diffstat (limited to 'src/features/tabs/ui')
| -rw-r--r-- | src/features/tabs/ui/CloseSubagentsConfirm.svelte | 44 | ||||
| -rw-r--r-- | src/features/tabs/ui/TabList.svelte | 12 |
2 files changed, 47 insertions, 9 deletions
diff --git a/src/features/tabs/ui/CloseSubagentsConfirm.svelte b/src/features/tabs/ui/CloseSubagentsConfirm.svelte index cbbec79..7ffda6c 100644 --- a/src/features/tabs/ui/CloseSubagentsConfirm.svelte +++ b/src/features/tabs/ui/CloseSubagentsConfirm.svelte @@ -7,7 +7,8 @@ * the whole subtree — "Yes" cascades the close to every transitive * descendant (sub-sub-agents included, no further prompts); "No" closes * only the tab itself. Escape / backdrop dismisses without closing anything. - * daisyUI `modal` (controlled: `modal-open`). + * Native modal dialog with daisyUI chrome: top-layer rendering, focus + * containment, and inert background even inside the animated sidebar. */ let { tab, @@ -27,14 +28,31 @@ /** Escape / backdrop — close nothing. */ onDismiss: () => void; } = $props(); + function openModal(node: HTMLDialogElement) { + const opener = document.activeElement; + node.showModal(); + return { + destroy() { + node.close(); + if (opener instanceof HTMLElement && opener.isConnected) opener.focus(); + }, + }; + } </script> -<svelte:window onkeydown={(e) => e.key === "Escape" && onDismiss()} /> - -<div class="modal modal-open" role="dialog" aria-label="Close subagents too?"> +<dialog + use:openModal + class="modal" + aria-label="Close subagents too?" + aria-describedby="close-subagents-description" + oncancel={(event) => { + event.preventDefault(); + onDismiss(); + }} +> <div class="modal-box max-w-md"> <h3 class="text-lg font-bold">Close subagents too?</h3> - <div class="flex flex-col gap-1 py-3"> + <div id="close-subagents-description" class="flex flex-col gap-1 py-3"> <p class="text-sm"> "{tab.title}" has {subagentCount} {subagentCount === 1 ? "subagent tab" : "subagent tabs"} (including nested ones). @@ -44,7 +62,7 @@ stopped. </p> </div> - <div class="modal-action"> + <div class="modal-action flex-wrap"> <button type="button" class="btn btn-ghost btn-sm" onclick={onDismiss}>Cancel</button> <button type="button" class="btn btn-sm" onclick={onKeep}>No, keep them open</button> <button type="button" class="btn btn-warning btn-sm" onclick={onCloseAll}> @@ -52,6 +70,14 @@ </button> </div> </div> - <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions --> - <div class="modal-backdrop" onclick={onDismiss}></div> -</div> + <form + method="dialog" + class="modal-backdrop" + onsubmit={(event) => { + event.preventDefault(); + onDismiss(); + }} + > + <button type="submit" aria-label="Dismiss close confirmation">close</button> + </form> +</dialog> diff --git a/src/features/tabs/ui/TabList.svelte b/src/features/tabs/ui/TabList.svelte index 0493b5c..8b388f0 100644 --- a/src/features/tabs/ui/TabList.svelte +++ b/src/features/tabs/ui/TabList.svelte @@ -98,7 +98,19 @@ // in the meantime is a no-op for `onClose`. A plain tab closes directly. let closeConfirm = $state<{ tab: Tab; cascadeIds: readonly string[] } | null>(null); + // A remote close or workspace switch must not leave an actionable dialog + // for a tab that is no longer in this list. + $effect(() => { + if ( + closeConfirm !== null && + !tabs.some((tab) => tab.conversationId === closeConfirm?.tab.conversationId) + ) { + closeConfirm = null; + } + }); + function requestClose(conversationId: string): void { + menu = null; const cascadeIds = cascadeCloseIds(tabs, conversationId); if (cascadeIds.length <= 1) { onClose(conversationId); |
