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
15 changes: 11 additions & 4 deletions apps/web/src/layout/SurfaceHost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,16 @@ const NO_GHOSTS: ReadonlyMap<string, ClosingTab> = new Map();
* Pane edges land on whole pixels, so borders and text stay crisp at any split.
* Both panes round the same boundary, so the gap between them stays exact.
*/
function slotStyle(slot: LayoutPaneSlot): React.CSSProperties {
function slotStyle(slot: LayoutPaneSlot, stripPx?: number): React.CSSProperties {
const edge = (has: boolean) => (has ? HALF_PANE_GAP : "0px");
const at = (fraction: number, offset: string) =>
`round(calc(${fraction * 100}% + ${offset}), 1px)`;
const left = at(slot.rect.left, edge(slot.hasLeft));
const right = at(slot.rect.left + slot.rect.width, `-1 * ${edge(slot.hasRight)}`);
const across = (fraction: number, offset: string) =>
stripPx === undefined
? at(fraction, offset)
: `round(calc(${fraction * stripPx}px + ${offset}), 1px)`;
const left = across(slot.rect.left, edge(slot.hasLeft));
const right = across(slot.rect.left + slot.rect.width, `-1 * ${edge(slot.hasRight)}`);
const top = at(slot.rect.top, edge(slot.hasAbove));
const bottom = at(slot.rect.top + slot.rect.height, `-1 * ${edge(slot.hasBelow)}`);
return {
Expand Down Expand Up @@ -1056,6 +1060,7 @@ export function LayoutSurfaceHost({
chats: chatTabsById,
launchers: launcherTabsById,
placed: slotByTabId,
stripPx,
motionKey: paneMotionKey,
framed: paneCount > 1 || maximizedTabId !== null,
});
Expand All @@ -1068,6 +1073,7 @@ export function LayoutSurfaceHost({
chats: chatTabsById,
launchers: launcherTabsById,
placed: slotByTabId,
stripPx,
motionKey: paneMotionKey,
framed: paneCount > 1 || maximizedTabId !== null,
});
Expand All @@ -1080,8 +1086,9 @@ export function LayoutSurfaceHost({
seen.motionKey === paneMotionKey
) {
const closed: [string, ClosingTab][] = [];
for (const [tabId, placed] of seen.placed) {
for (const [tabId, was] of seen.placed) {
if (presentTabIds.has(tabId) || ghosts.has(tabId)) continue;
const placed = { slot: was.slot, style: slotStyle(was.slot, seen.stripPx) };
const chat = seen.chats.get(tabId);
const launcher = seen.launchers.get(tabId);
if (chat?.tab.content.kind === "chat") {
Expand Down
16 changes: 16 additions & 0 deletions apps/web/src/motion/paneMotion.browser.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { assert, describe, it } from "vite-plus/test";

import { findDonor } from "./paneMotion";

const column = (x: number, width: number) => ({ x, y: 0, width, height: 880 });

describe("findDonor", () => {
it("takes only a neighbour across the gutter, never one with panes between", () => {
const closed = column(872, 416);
const far = { box: column(0, 442), other: column(0, 1288) };
const beside = { box: column(450, 414), other: column(450, 838) };

assert.isNull(findDonor(closed, [far]));
assert.strictEqual(findDonor(closed, [far, beside])?.neighbour, beside);
});
});
6 changes: 4 additions & 2 deletions apps/web/src/motion/paneMotion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const translate = (dx: number, dy: number): Keyframe => ({
/** How far a new pane slides out from under its donor, in px. */
const OPEN_TRAVEL_PX = 48;

const ADJACENT_GAP_PX = 16;

/** The Panel animations setting's default, which plays niri's springs as they are. */
const DEFAULT_PANEL_MS = 150;

Expand Down Expand Up @@ -123,7 +125,7 @@ interface Neighbour {
* donor. One that changed size along that side wins; then left, above,
* right, below, the nearest on each side.
*/
function findDonor(
export function findDonor(
box: Box,
neighbours: Iterable<Neighbour>,
): { neighbour: Neighbour; side: Side; gap: number } | null {
Expand All @@ -145,7 +147,7 @@ function findDonor(
);
}
for (const [side, gap] of gaps) {
if (gap < -1) continue;
if (gap < -1 || gap > ADJACENT_GAP_PX) continue;
const rank = (candidate: { neighbour: Neighbour; side: Side }) =>
(candidate.neighbour.other &&
resized(candidate.side, candidate.neighbour.box, candidate.neighbour.other)
Expand Down
Loading