litvc 1.3.1

Lit - The agentic-first distributed version control system. A complete Git replacement designed for AI agents first and humans second.
Documentation
<script lang="ts">
  import type { StatusData } from "../types";
  import { createCommit, amendCommit, errMsg } from "../tauri";

  let {
    status,
    onRefresh,
    onError,
  }: {
    status: StatusData | null;
    onRefresh: () => void;
    onError?: (message: string) => void;
  } = $props();

  let message = $state("");
  let isAmend = $state(false);
  let committing = $state(false);

  let canCommit = $derived(
    message.trim().length > 0 && (status?.staged?.length ?? 0) > 0 && !committing
  );

  let summary = $derived(message.split("\n")[0] ?? "");
  let charCount = $derived(summary.length);
  let charColor = $derived(
    charCount > 72 ? "var(--danger)" : charCount > 50 ? "var(--warn)" : "var(--text-mute)"
  );

  async function handleCommit() {
    if (!canCommit) return;
    committing = true;
    try {
      if (isAmend) {
        await amendCommit(message.trim());
      } else {
        await createCommit(message.trim());
      }
      message = "";
      isAmend = false;
      onRefresh();
    } catch (e) {
      onError?.(errMsg(e));
    } finally {
      committing = false;
    }
  }
</script>

<div class="commit-panel">
  <div class="panel-header">
    <span class="panel-title">Commit</span>
    <label class="amend-toggle">
      <input type="checkbox" bind:checked={isAmend} />
      <span>Amend</span>
    </label>
  </div>

  <div class="commit-form">
    <div class="message-area">
      <textarea
        class="commit-message"
        placeholder="Commit message..."
        bind:value={message}
        rows={4}
        onkeydown={(e) => {
          if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
            handleCommit();
          }
        }}
      ></textarea>
      <span class="char-count" style="color: {charColor}">{charCount}</span>
    </div>

    <button
      class="btn-commit"
      disabled={!canCommit}
      onclick={handleCommit}
    >
      {#if committing}
        Committing...
      {:else if isAmend}
        Amend Commit
      {:else}
        Commit {#if status?.staged?.length}({status.staged.length} file{status.staged.length !== 1 ? "s" : ""}){/if}
      {/if}
    </button>
  </div>

  <div class="commit-info">
    {#if status}
      <div class="info-row">
        <span class="info-label">Staged</span>
        <span class="info-value">{status.staged.length}</span>
      </div>
      <div class="info-row">
        <span class="info-label">Modified</span>
        <span class="info-value">{status.modified.length}</span>
      </div>
      <div class="info-row">
        <span class="info-label">Untracked</span>
        <span class="info-value">{status.untracked.length}</span>
      </div>
    {/if}
    {#if status?.head}
      <div class="info-row" style="margin-top: var(--space-8);">
        <span class="info-label">HEAD</span>
        <span class="info-value mono" title={status.head}>{status.head.slice(0, 12)}</span>
      </div>
    {/if}
  </div>

  <div class="shortcuts">
    <span class="shortcut-hint">Ctrl+Enter to commit</span>
  </div>
</div>

<style>
  .commit-panel {
    height: 100%;
    display: flex;
    flex-direction: column;
    background: var(--bg-1);
  }

  .panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-10) var(--space-12);
    border-bottom: 1px solid var(--border-1);
    flex-shrink: 0;
  }
  .panel-title {
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-3);
  }

  .amend-toggle {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--text-xs);
    color: var(--text-3);
    cursor: pointer;
  }
  .amend-toggle input {
    width: 14px;
    height: 14px;
    accent-color: var(--accent);
  }

  .commit-form {
    padding: var(--space-12);
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
  }

  .message-area {
    position: relative;
  }

  .commit-message {
    width: 100%;
    resize: vertical;
    min-height: 80px;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: 1.5;
    padding: var(--space-8) var(--space-10);
    background: var(--bg-2);
    border: 1px solid var(--border-1);
    border-radius: var(--radius-md);
    color: var(--text-1);
  }
  .commit-message:focus {
    border-color: var(--accent);
    outline: none;
  }
  .commit-message::placeholder {
    color: var(--text-mute);
  }

  .char-count {
    position: absolute;
    bottom: var(--space-6);
    right: var(--space-8);
    font-size: 10px;
    font-family: var(--font-mono);
  }

  .btn-commit {
    padding: var(--space-8) var(--space-16);
    background: var(--accent);
    color: #fff;
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: 600;
    transition: all var(--transition-fast);
  }
  .btn-commit:hover:not(:disabled) {
    background: var(--accent-hover);
  }
  .btn-commit:disabled {
    opacity: 0.4;
    cursor: not-allowed;
  }

  .commit-info {
    padding: var(--space-12);
    border-top: 1px solid var(--border-1);
    flex: 1;
  }

  .info-row {
    display: flex;
    justify-content: space-between;
    padding: var(--space-4) 0;
    font-size: var(--text-xs);
  }
  .info-label {
    color: var(--text-3);
  }
  .info-value {
    color: var(--text-2);
    font-weight: 500;
  }

  .shortcuts {
    padding: var(--space-8) var(--space-12);
    border-top: 1px solid var(--border-1);
    flex-shrink: 0;
  }
  .shortcut-hint {
    font-size: 10px;
    color: var(--text-mute);
  }
</style>