Skip to content
Open
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
14 changes: 5 additions & 9 deletions src/components/command-palette/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function CommandPalette({
notesFolder,
} = useNotes();
const { setTheme } = useTheme();
const { status, gitAvailable, gitEnabled, commit, sync, isSyncing } = useGit();
const { status, gitAvailable, gitEnabled, sync, isSyncing } = useGit();
const [query, setQuery] = useState("");
const [selectedIndex, setSelectedIndex] = useState(0);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
Expand Down Expand Up @@ -376,15 +376,12 @@ export function CommandPalette({
if (hasChanges) {
baseCommands.push({
id: "git-commit",
label: "Git: Quick Commit",
label: "Git: Commit Changes",
icon: <GitCommitIcon className="w-4.5 h-4.5 stroke-[1.5]" />,
action: async () => {
const success = await commit("Quick commit from Scratch");
if (success) {
toast.success("Changes committed");
} else {
toast.error("Failed to commit");
}
window.dispatchEvent(
new CustomEvent("open-commit-panel")
)
onClose();
},
});
Expand Down Expand Up @@ -514,7 +511,6 @@ export function CommandPalette({
gitEnabled,
gitAvailable,
status,
commit,
sync,
isSyncing,
selectNote,
Expand Down
118 changes: 118 additions & 0 deletions src/components/git/CommitPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
type KeyboardEvent,
} from "react";
import { toast } from "sonner";
import { useGit } from "../../context/GitContext";
import { Button, Textarea } from "../ui";

const DEFAULT_COMMIT_MESSAGE = "Quick Commit from Scratch";

interface CommitPanelProps {
open: boolean;
onClose: () => void;
}

export function CommitPanel({ open, onClose }: CommitPanelProps) {
const { commit, isCommitting } = useGit();

const [message, setMessage] = useState(DEFAULT_COMMIT_MESSAGE);

const textareaRef = useRef<HTMLTextAreaElement>(null);

useEffect(() => {
if (!open) {
return;
}

setMessage(DEFAULT_COMMIT_MESSAGE);

requestAnimationFrame(() => {
textareaRef.current?.focus();
textareaRef.current?.select();
});
}, [open]);

const canCommit = useMemo(() => message.trim().length > 0, [message]);

const handleCommit = useCallback(async () => {
const commitMessage = message.trim();

if (!commitMessage || isCommitting) {
return;
}

try {
const success = await commit(commitMessage);

if (success) {
toast.success("Changes committed");
onClose();
} else {
toast.error("Failed to commit");
}
} catch {
toast.error("Failed to commit");
}
}, [commit, isCommitting, message, onClose]);

const handleKeyDown = useCallback(
async (event: KeyboardEvent<HTMLTextAreaElement>) => {
if (event.key === "Escape") {
event.preventDefault();
onClose();
return;
}

if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault();
await handleCommit();
}
},
[handleCommit, onClose],
);

if (!open) {
return null;
}

return (
<div className="border-t border-border bg-bg-secondary px-3 py-3">
<div className="mb-2">
<h3 className="text-sm font-medium text-text">Commit Changes</h3>

<p className="mt-1 text-xs text-text-muted">
Enter to commit • Shift+Enter for newline
</p>
</div>

<Textarea
ref={textareaRef}
value={message}
onChange={(event) => setMessage(event.target.value)}
onKeyDown={handleKeyDown}
rows={4}
placeholder="Commit message"
className="min-h-24"
/>

<div className="mt-3 flex items-center justify-end gap-2">
<Button variant="ghost" onClick={onClose} disabled={isCommitting}>
Cancel
</Button>

<Button
variant="primary"
onClick={handleCommit}
disabled={!canCommit || isCommitting}
>
{isCommitting ? "Committing..." : "Commit"}
</Button>
</div>
</div>
);
}
25 changes: 9 additions & 16 deletions src/components/layout/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@ import { mod, isMac } from "../../lib/platform";

interface FooterProps {
onOpenSettings?: () => void;
onOpenCommit?: () => void;
}

export const Footer = memo(function Footer({ onOpenSettings }: FooterProps) {
export const Footer = memo(function Footer({
onOpenSettings,
onOpenCommit,
}: FooterProps) {
const {
status,
isLoading,
Expand All @@ -27,24 +31,13 @@ export const Footer = memo(function Footer({ onOpenSettings }: FooterProps) {
gitEnabled,
sync,
initRepo,
commit,
lastError,
clearError,
} = useGit();

const handleCommit = useCallback(async () => {
if (isCommitting) return;
try {
const success = await commit("Quick commit from Scratch");
if (success) {
toast.success("Changes committed");
} else {
toast.error("Failed to commit");
}
} catch {
toast.error("Failed to commit");
}
}, [commit, isCommitting]);
const handleCommit = useCallback(() => {
onOpenCommit?.();
}, [onOpenCommit]);

const handleSync = useCallback(async () => {
if (isSyncing) return;
Expand Down Expand Up @@ -209,7 +202,7 @@ export const Footer = memo(function Footer({ onOpenSettings }: FooterProps) {
<IconButton
onClick={handleCommit}
disabled={isCommitting}
title="Quick commit"
title="Commit Changes"
>
{isCommitting ? (
<SpinnerIcon className="w-4.5 h-4.5 stroke-[1.5] animate-spin" />
Expand Down
28 changes: 27 additions & 1 deletion src/components/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { mod, shift, isMac } from "../../lib/platform";
import * as notesService from "../../services/notes";
import { FolderNameDialog } from "../notes/FolderNameDialog";
import { CommitPanel } from "../git/CommitPanel";

interface SidebarProps {
onOpenSettings?: () => void;
Expand Down Expand Up @@ -53,6 +54,7 @@ export function Sidebar({ onOpenSettings }: SidebarProps) {
const [dragCount, setDragCount] = useState(1);
const [multiSelectedNoteIds, setMultiSelectedNoteIds] = useState<Set<string>>(new Set());
const [lastClickedNoteId, setLastClickedNoteId] = useState<string | null>(null);
const [commitPanelOpen, setCommitPanelOpen] = useState(false);
const debounceRef = useRef<number | null>(null);
const searchInputRef = useRef<HTMLInputElement>(null);
const multiSelectedRef = useRef(multiSelectedNoteIds) as RefObject<Set<string>>;
Expand Down Expand Up @@ -303,6 +305,22 @@ export function Sidebar({ onOpenSettings }: SidebarProps) {
window.removeEventListener("create-new-folder", handleCreateFolder);
}, [selectedNoteId]);

const handleOpenCommitPanel = useCallback(() => {
setCommitPanelOpen(true);
}, []);

const handleCloseCommitPanel = useCallback(() => {
setCommitPanelOpen(false);
}, []);

useEffect(() => {
window.addEventListener("open-commit-panel", handleOpenCommitPanel);

return () => {
window.removeEventListener("open-commit-panel", handleOpenCommitPanel);
};
}, [handleOpenCommitPanel]);

return (
<DndContext
sensors={sensors}
Expand Down Expand Up @@ -420,8 +438,16 @@ export function Sidebar({ onOpenSettings }: SidebarProps) {
/>
</div>

<CommitPanel
open={commitPanelOpen}
onClose={handleCloseCommitPanel}
/>

{/* Footer with git status, commit, and settings */}
<Footer onOpenSettings={onOpenSettings} />
<Footer
onOpenSettings={onOpenSettings}
onOpenCommit={handleOpenCommitPanel}
/>

{/* Folder name dialog */}
<FolderNameDialog
Expand Down
27 changes: 27 additions & 0 deletions src/components/ui/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as React from "react";
import { cn } from "../../lib/utils";

export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {
return (
<textarea
ref={ref}
className={cn(
"flex w-full rounded-md border border-border bg-bg px-3 py-2 text-sm text-text",
"placeholder:text-text-muted",
"focus-visible:outline-none focus-visible:border-accent",
"disabled:cursor-not-allowed disabled:opacity-50",
"resize-none",
className,
)}
{...props}
/>
);
},
);

Textarea.displayName = "Textarea";

export { Textarea };
Loading