try to use local ai to generate commit message

This commit is contained in:
Christoph Brandau
2026-07-02 19:59:16 +02:00
parent 2a96e79d27
commit f2aa48d2ec
13 changed files with 3889 additions and 62 deletions
+43 -9
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { Check, LoaderCircle } from "@lucide/svelte";
import { Check, LoaderCircle, Sparkles } from "@lucide/svelte";
import type { CommitAiPhase } from "../types";
interface Props {
commitMessage: string;
@@ -9,8 +10,11 @@
isBusy: boolean;
operation: string;
stagedCount: number;
commitAiPhase: CommitAiPhase;
commitAiGenerating: boolean;
onCommit: () => void;
onCommitMessageChange: (msg: string) => void;
onGenerateCommitMessage: () => void;
}
let {
@@ -21,14 +25,28 @@
isBusy = false,
operation = "",
stagedCount = 0,
commitAiPhase = "idle",
commitAiGenerating = false,
onCommit = () => {},
onCommitMessageChange = () => {},
onGenerateCommitMessage = () => {},
}: Props = $props();
function handleSubmit(event: SubmitEvent) {
event.preventDefault();
onCommit();
}
function aiButtonTitle(phase: CommitAiPhase, staged: number): string {
if (phase === "loading") return "AI model is downloading/loading — this happens once";
if (phase === "error") return "AI model failed to load";
if (staged === 0) return "Stage changes first";
return "Generate commit message with AI from the staged diff";
}
let canGenerate = $derived(
hasRepository && !isBusy && !commitAiGenerating && commitAiPhase === "ready" && stagedCount > 0,
);
</script>
<section class="panel flex flex-col" aria-label="Commit">
@@ -50,13 +68,29 @@
{#if commitBlockReason}
<p class="commit-block-reason">{commitBlockReason}</p>
{/if}
<button class="btn-primary w-full flex-shrink-0" type="submit" disabled={!canCommit} title={commitBlockReason || "Commit staged changes"}>
{#if operation === "Committing"}
<LoaderCircle class="spin" size={16} aria-hidden="true" />
{:else}
<Check size={16} aria-hidden="true" />
{/if}
Commit
</button>
<div class="commit-actions-row flex-shrink-0">
<button class="btn-primary flex-1" type="submit" disabled={!canCommit} title={commitBlockReason || "Commit staged changes"}>
{#if operation === "Committing"}
<LoaderCircle class="spin" size={16} aria-hidden="true" />
{:else}
<Check size={16} aria-hidden="true" />
{/if}
Commit
</button>
<button
class="btn-secondary commit-ai-button"
type="button"
onclick={onGenerateCommitMessage}
disabled={!canGenerate}
title={aiButtonTitle(commitAiPhase, stagedCount)}
>
{#if commitAiGenerating || commitAiPhase === "loading"}
<LoaderCircle class="spin" size={16} aria-hidden="true" />
{:else}
<Sparkles size={16} aria-hidden="true" />
{/if}
AI
</button>
</div>
</form>
</section>
+9
View File
@@ -1,6 +1,7 @@
import { invoke } from "@tauri-apps/api/core";
import type {
CommitAiStatus,
ConflictFile,
GitBranch,
GitCommit,
@@ -94,6 +95,14 @@ export function commit(path: string, message: string): Promise<GitStatus> {
return invoke<GitStatus>("commit", { path, message });
}
export function commitAiStatus(): Promise<CommitAiStatus> {
return invoke<CommitAiStatus>("commit_ai_status");
}
export function commitAiGenerate(path: string, notes?: string): Promise<string> {
return invoke<string>("commit_ai_generate", { path, notes });
}
export function pull(path: string, username?: string, password?: string): Promise<GitStatus> {
return invoke<GitStatus>("pull", { path, username: username ?? null, password: password ?? null });
}
+7
View File
@@ -7,6 +7,13 @@ export type FileStatusKind =
| "conflicted"
| "unknown";
export type CommitAiPhase = "idle" | "loading" | "ready" | "error";
export interface CommitAiStatus {
phase: CommitAiPhase;
error: string | null;
}
export interface GitStatus {
repo_path: string;
current_branch: string | null;