Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions src/lib/components/TabList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,20 @@
items: [],
});

function selectTab(tab: TabData) {
if (justDragged) return;
tabManager.setActive(tab.id);
ontabclick?.();
}

function handleMouseDown(e: MouseEvent, tab: TabData, element: HTMLElement) {
if (e.button !== 0) return;
e.stopPropagation();
e.preventDefault();

const rect = element.getBoundingClientRect();
// The wrapper is as tall as the title bar; the drag proxy has to line up
// with the tab drawn inside it.
const rect = (element.firstElementChild ?? element).getBoundingClientRect();
dragState = {
startX: e.clientX,
currentX: e.clientX,
Expand Down Expand Up @@ -204,22 +212,24 @@
}}>
{#each tabManager.tabs as tab, i (tab.id)}
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
class="tab-item-wrapper"
animate:flip={{ duration: 200 }}
role="listitem"
class:drag-opacity={draggingId === tab.id}
onmousedown={(e) => handleMouseDown(e, tab, e.currentTarget as HTMLElement)}>
onmousedown={(e) => handleMouseDown(e, tab, e.currentTarget as HTMLElement)}
onclick={(e) => {
// Only reachable when maximized, where the wrapper is taller
// than the tab (#823). Clicks inside the tab are the tab's own.
if (e.target === e.currentTarget) selectTab(tab);
}}>
<Tab
{tab}
folderSuffix={folderSuffixes.get(tab.id)}
isActive={!showHome && tabManager.activeTabId === tab.id}
isLast={i === tabManager.tabs.length - 1}
onclick={() => {
if (justDragged) return;
tabManager.setActive(tab.id);
ontabclick?.();
}}
onclick={() => selectTab(tab)}
onclose={() => oncloseTab?.(tab.id)} />
</div>
{/each}
Expand Down Expand Up @@ -340,6 +350,16 @@
transition: opacity 0.1s;
}

/* Maximized on Windows/Linux, the strip above a tab is the screen's top
edge. Give it to the tab, as Chrome does, so a flick to the edge picks the
tab instead of dragging the window (#823). Restored windows keep the strip
as drag region. */
:global(.custom-title-bar.maximized) .tab-item-wrapper {
display: flex;
align-items: center;
height: 100%;
}

.tab-item-wrapper.drag-opacity {
opacity: 0;
pointer-events: none;
Expand Down
15 changes: 14 additions & 1 deletion src/lib/components/TitleBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,19 @@
});
});

// Windows and Linux only: on macOS the top of the screen is the menu bar, so
// a maximized window's tabs never sit on the screen edge (#823).
let isMaximized = $state(false);
$effect(() => {
if (isMac) return;
const sync = () => appWindow.isMaximized().then((v) => (isMaximized = v)).catch(() => {});
sync();
const unlisten = appWindow.onResized(sync);
return () => {
unlisten.then((f) => f());
};
});

let tooltip = $state({
visible: false,
text: '',
Expand Down Expand Up @@ -402,7 +415,7 @@

<svelte:window bind:innerWidth />

<div class="custom-title-bar {isScrolled ? 'scrolled' : ''} {!isMac ? 'windows' : ''} {useNativeMacChrome ? 'native-mac' : ''}">
<div class="custom-title-bar {isScrolled ? 'scrolled' : ''} {!isMac ? 'windows' : ''} {useNativeMacChrome ? 'native-mac' : ''}" class:maximized={isMaximized}>
{#if !isMac && !isWin11}
<div class="window-top-border"></div>
{/if}
Expand Down
Loading