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
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ export const createStatusProcedures = () => {

const resolveIsUnnamed = () => {
if (input.patch.isUnnamed !== undefined) return input.patch.isUnnamed;
if (input.patch.name !== undefined && !input.patch.preserveUnnamedStatus)
if (
input.patch.name !== undefined &&
!input.patch.preserveUnnamedStatus
)
return false;
return undefined;
};
Expand Down
5 changes: 5 additions & 0 deletions apps/desktop/src/renderer/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@
@apply border-border antialiased;
}

button,
[role="button"] {
cursor: pointer;
}

body {
@apply bg-background text-foreground;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export function SlugDialog({
slug: slugValue,
});

setSlugAvailable(result.data?.available ?? null);
setSlugAvailable(result.data?.status ?? null);
} catch (error) {
console.error("[slug-dialog] Slug check failed:", error);
setSlugAvailable(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export function CreateOrganization() {
slug: slugValue,
});

setSlugAvailable(result.data?.available ?? null);
setSlugAvailable(result.data?.status ?? null);
} catch (error) {
console.error("[create-org] Slug check failed:", error);
setSlugAvailable(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Terminal as XTerm } from "@xterm/xterm";
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
import type { Terminal as XTerm } from "@xterm/xterm";

// Mock localStorage for Node.js test environment
const mockStorage = new Map<string, string>();
Expand Down Expand Up @@ -31,11 +31,8 @@ mock.module("renderer/lib/trpc-client", () => ({
}));

// Import after mocks are set up
const {
getDefaultTerminalBg,
getDefaultTerminalTheme,
setupKeyboardHandler,
} = await import("./helpers");
const { getDefaultTerminalBg, getDefaultTerminalTheme, setupKeyboardHandler } =
await import("./helpers");

describe("getDefaultTerminalTheme", () => {
beforeEach(() => {
Expand Down Expand Up @@ -117,33 +114,36 @@ describe("setupKeyboardHandler", () => {

afterEach(() => {
// Restore navigator between tests
// @ts-expect-error - resetting global navigator for tests
globalThis.navigator = originalNavigator;
});

it("maps Option+Left/Right to Meta+B/F on macOS", () => {
// @ts-expect-error - mocking navigator for tests
globalThis.navigator = { platform: "MacIntel" };

let handler: ((event: KeyboardEvent) => boolean) | null = null;
const captured: { handler: ((event: KeyboardEvent) => boolean) | null } = {
handler: null,
};
const xterm = {
attachCustomKeyEventHandler: (next: (event: KeyboardEvent) => boolean) => {
handler = next;
attachCustomKeyEventHandler: (
next: (event: KeyboardEvent) => boolean,
) => {
captured.handler = next;
},
};

const onWrite = mock(() => {});
setupKeyboardHandler(xterm as unknown as XTerm, { onWrite });

handler?.({
captured.handler?.({
type: "keydown",
key: "ArrowLeft",
altKey: true,
metaKey: false,
ctrlKey: false,
shiftKey: false,
} as KeyboardEvent);
handler?.({
captured.handler?.({
type: "keydown",
key: "ArrowRight",
altKey: true,
Expand All @@ -160,25 +160,29 @@ describe("setupKeyboardHandler", () => {
// @ts-expect-error - mocking navigator for tests
globalThis.navigator = { platform: "Win32" };

let handler: ((event: KeyboardEvent) => boolean) | null = null;
const captured: { handler: ((event: KeyboardEvent) => boolean) | null } = {
handler: null,
};
const xterm = {
attachCustomKeyEventHandler: (next: (event: KeyboardEvent) => boolean) => {
handler = next;
attachCustomKeyEventHandler: (
next: (event: KeyboardEvent) => boolean,
) => {
captured.handler = next;
},
};

const onWrite = mock(() => {});
setupKeyboardHandler(xterm as unknown as XTerm, { onWrite });

handler?.({
captured.handler?.({
type: "keydown",
key: "ArrowLeft",
altKey: false,
metaKey: false,
ctrlKey: true,
shiftKey: false,
} as KeyboardEvent);
handler?.({
captured.handler?.({
type: "keydown",
key: "ArrowRight",
altKey: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,11 @@ export function CommitInput({
onChange={(e) => setCommitMessage(e.target.value)}
className="min-h-[52px] resize-none text-[10px] bg-background"
onKeyDown={(e) => {
if (e.key === "Enter" && (e.metaKey || e.ctrlKey) && !primary.disabled) {
if (
e.key === "Enter" &&
(e.metaKey || e.ctrlKey) &&
!primary.disabled
) {
e.preventDefault();
primary.handler();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,73 +295,30 @@ export function FilesView() {
<ContextMenu>
<ContextMenuTrigger asChild className="h-full">
<div className="h-full overflow-auto">
{newItemMode && newItemParentPath === worktreePath && (
<NewItemInput
mode={newItemMode}
parentPath={newItemParentPath}
onSubmit={handleNewItemSubmit}
onCancel={handleNewItemCancel}
/>
)}

{isSearching ? (
searchResultEntries.length > 0 ? (
<div className="flex flex-col">
{searchResultEntries.map((entry) =>
renameEntry?.path === entry.path ? (
<RenameInput
key={entry.id}
entry={entry}
onSubmit={handleRenameSubmit}
onCancel={handleRenameCancel}
/>
) : (
<FileSearchResultItem
key={entry.id}
entry={entry}
worktreePath={worktreePath}
onActivate={handleFileActivate}
onOpenInEditor={handleOpenInEditor}
onNewFile={handleNewFile}
onNewFolder={handleNewFolder}
onRename={handleRename}
onDelete={handleDeleteRequest}
/>
),
)}
</div>
) : (
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm p-4">
{isSearchFetching
? "Searching files..."
: "No matching files"}
</div>
)
) : (
<div {...tree.getContainerProps()} className="outline-none">
{tree.getItems().map((item) => {
const data = item.getItemData();
if (!data || item.getId() === "root") return null;
const showNewItemInput =
newItemMode &&
data.isDirectory &&
data.path === newItemParentPath;
const isRenaming = renameEntry?.path === data.path;
return (
<div key={item.getId()}>
{isRenaming ? (
{newItemMode && newItemParentPath === worktreePath && (
<NewItemInput
mode={newItemMode}
parentPath={newItemParentPath}
onSubmit={handleNewItemSubmit}
onCancel={handleNewItemCancel}
/>
)}

{isSearching ? (
searchResultEntries.length > 0 ? (
<div className="flex flex-col">
{searchResultEntries.map((entry) =>
renameEntry?.path === entry.path ? (
<RenameInput
entry={data}
key={entry.id}
entry={entry}
onSubmit={handleRenameSubmit}
onCancel={handleRenameCancel}
level={item.getItemMeta().level}
/>
) : (
<FileTreeItem
item={item}
entry={data}
rowHeight={ROW_HEIGHT}
indent={TREE_INDENT}
<FileSearchResultItem
key={entry.id}
entry={entry}
worktreePath={worktreePath}
onActivate={handleFileActivate}
onOpenInEditor={handleOpenInEditor}
Expand All @@ -370,22 +327,65 @@ export function FilesView() {
onRename={handleRename}
onDelete={handleDeleteRequest}
/>
)}
{showNewItemInput && (
<NewItemInput
mode={newItemMode}
parentPath={newItemParentPath}
onSubmit={handleNewItemSubmit}
onCancel={handleNewItemCancel}
level={item.getItemMeta().level + 1}
/>
)}
</div>
);
})}
</div>
)}
</div>
),
)}
</div>
) : (
<div className="flex-1 flex items-center justify-center text-muted-foreground text-sm p-4">
{isSearchFetching
? "Searching files..."
: "No matching files"}
</div>
)
) : (
<div {...tree.getContainerProps()} className="outline-none">
{tree.getItems().map((item) => {
const data = item.getItemData();
if (!data || item.getId() === "root") return null;
const showNewItemInput =
newItemMode &&
data.isDirectory &&
data.path === newItemParentPath;
const isRenaming = renameEntry?.path === data.path;
return (
<div key={item.getId()}>
{isRenaming ? (
<RenameInput
entry={data}
onSubmit={handleRenameSubmit}
onCancel={handleRenameCancel}
level={item.getItemMeta().level}
/>
) : (
<FileTreeItem
item={item}
entry={data}
rowHeight={ROW_HEIGHT}
indent={TREE_INDENT}
worktreePath={worktreePath}
onActivate={handleFileActivate}
onOpenInEditor={handleOpenInEditor}
onNewFile={handleNewFile}
onNewFolder={handleNewFolder}
onRename={handleRename}
onDelete={handleDeleteRequest}
/>
)}
{showNewItemInput && (
<NewItemInput
mode={newItemMode}
parentPath={newItemParentPath}
onSubmit={handleNewItemSubmit}
onCancel={handleNewItemCancel}
level={item.getItemMeta().level + 1}
/>
)}
</div>
);
})}
</div>
)}
</div>
</ContextMenuTrigger>
<ContextMenuContent className="w-48">
<ContextMenuItem onClick={() => handleNewFile(worktreePath)}>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type * as React from "react";
import { cn } from "../../lib/utils";

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all cursor-pointer disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
{
variants: {
variant: {
Expand Down