Skip to content

Commit 1c519fd

Browse files
Make title field draggable
Mark the session title shell as a Tauri drag region and let the top-strip drag handler honor explicit drag-region targets while preserving regular input behavior.
1 parent 0876e82 commit 1c519fd

4 files changed

Lines changed: 77 additions & 4 deletions

File tree

apps/desktop/src/main/body.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ function isInteractiveMainAreaDragTarget(target: EventTarget | null): boolean {
255255
return false;
256256
}
257257

258+
if (isTauriDragRegionTarget(target)) {
259+
return false;
260+
}
261+
258262
return Boolean(
259263
target.closest(
260264
[
@@ -271,6 +275,14 @@ function isInteractiveMainAreaDragTarget(target: EventTarget | null): boolean {
271275
);
272276
}
273277

278+
function isTauriDragRegionTarget(target: Element): boolean {
279+
const dragRegion = target.closest("[data-tauri-drag-region]");
280+
281+
return Boolean(
282+
dragRegion && dragRegion.getAttribute("data-tauri-drag-region") !== "false",
283+
);
284+
}
285+
274286
function isMainAreaWindowDrag(
275287
start: { clientX: number; clientY: number },
276288
current: { clientX: number; clientY: number },

apps/desktop/src/session/components/title-input.test.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,28 @@ describe("TitleInput", () => {
109109
).toBeNull();
110110
});
111111

112+
it("marks the title field as a Tauri drag region until it is focused", () => {
113+
renderTitleInput();
114+
115+
const input = screen.getByPlaceholderText("Untitled");
116+
const titleInputShell = input.parentElement;
117+
118+
expect(titleInputShell?.hasAttribute("data-tauri-drag-region")).toBe(true);
119+
expect(input.hasAttribute("data-tauri-drag-region")).toBe(false);
120+
121+
fireEvent.focus(input);
122+
123+
expect(titleInputShell?.getAttribute("data-tauri-drag-region")).toBe(
124+
"false",
125+
);
126+
expect(input.hasAttribute("data-tauri-drag-region")).toBe(false);
127+
128+
fireEvent.blur(input);
129+
130+
expect(titleInputShell?.hasAttribute("data-tauri-drag-region")).toBe(true);
131+
expect(input.hasAttribute("data-tauri-drag-region")).toBe(false);
132+
});
133+
112134
it("reveals overflowing titles with a hover scroll overlay", () => {
113135
const title =
114136
"Product Discovery Pace and Headless Agent Usage Strategy Review";

apps/desktop/src/session/components/title-input.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ export const TitleInput = forwardRef<
7777

7878
if (isGenerating) {
7979
return (
80-
<div className="flex h-8 w-full items-center justify-start">
80+
<div
81+
data-tauri-drag-region
82+
className="flex h-8 w-full items-center justify-start"
83+
>
8184
<span className="text-muted-foreground animate-pulse text-xl font-semibold">
8285
Generating title...
8386
</span>
@@ -87,7 +90,10 @@ export const TitleInput = forwardRef<
8790

8891
if (showRevealAnimation && generatedTitle) {
8992
return (
90-
<div className="flex h-8 w-full items-center justify-start overflow-hidden">
93+
<div
94+
data-tauri-drag-region
95+
className="flex h-8 w-full items-center justify-start overflow-hidden"
96+
>
9197
<span className="animate-reveal-left text-xl font-semibold whitespace-nowrap">
9298
{generatedTitle}
9399
</span>
@@ -341,6 +347,7 @@ const TitleInputInner = memo(
341347

342348
return (
343349
<div
350+
data-tauri-drag-region={isTitleFocused ? "false" : true}
344351
style={titleFadeStyle}
345352
className="group/title-input relative flex h-8 w-full items-center overflow-hidden"
346353
>

apps/desktop/src/shared/main/body.test.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ vi.mock("~/main/tab-content", () => ({
5858
ClassicMainTabContent: ({ tab }: { tab: { type: string } }) =>
5959
tab.type === "sessions" ? (
6060
<div data-testid="main-tab-content">
61-
<input aria-label="Session title" />
61+
<div data-tauri-drag-region>
62+
<input aria-label="Session title" />
63+
</div>
64+
<input aria-label="Session note field" />
6265
</div>
6366
) : (
6467
<div data-testid="main-tab-content">{tab.type}</div>
@@ -393,7 +396,7 @@ describe("ClassicMainBody", () => {
393396
expect(mocks.startDragging).toHaveBeenCalledTimes(1);
394397
});
395398

396-
it("does not start window dragging from an input in the top drag strip", () => {
399+
it("starts window dragging from an input inside a Tauri drag region", () => {
397400
mocks.currentTab = {
398401
active: true,
399402
pinned: false,
@@ -417,6 +420,35 @@ describe("ClassicMainBody", () => {
417420
pointerId: 1,
418421
});
419422

423+
expect(mocks.startDragging).toHaveBeenCalledTimes(1);
424+
});
425+
426+
it("does not start window dragging from a regular input in the top drag strip", () => {
427+
mocks.currentTab = {
428+
active: true,
429+
pinned: false,
430+
slotId: "slot-1",
431+
type: "sessions",
432+
};
433+
434+
render(<ClassicMainBody />);
435+
436+
const noteInput = screen.getByRole("textbox", {
437+
name: "Session note field",
438+
});
439+
440+
fireEvent.pointerDown(noteInput, {
441+
button: 0,
442+
clientX: 240,
443+
clientY: 12,
444+
pointerId: 1,
445+
});
446+
fireEvent.pointerMove(noteInput, {
447+
clientX: 248,
448+
clientY: 12,
449+
pointerId: 1,
450+
});
451+
420452
expect(mocks.startDragging).not.toHaveBeenCalled();
421453
});
422454

0 commit comments

Comments
 (0)