diff --git a/src/App.svelte b/src/App.svelte index d0df54f..71aa218 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -27,6 +27,7 @@ import ExplorerPanel from "./lib/components/ExplorerPanel.svelte"; import HistoryPanel from "./lib/components/HistoryPanel.svelte"; import InitRepositoryDialog from "./lib/components/InitRepositoryDialog.svelte"; + import MergeBranchDialog from "./lib/components/MergeBranchDialog.svelte"; import NewBranchDialog from "./lib/components/NewBranchDialog.svelte"; import RenameBranchDialog from "./lib/components/RenameBranchDialog.svelte"; import ReflogDialog from "./lib/components/ReflogDialog.svelte"; @@ -169,6 +170,7 @@ GitFileStatus, GitIgnoreKind, GitLfsStatus, + MergeStrategy, GitRepositoryFile, GitRemote, PullStrategy, @@ -391,6 +393,7 @@ let comparisonFromLabel = ""; let comparisonToLabel = ""; let newBranchCommit: GitCommit | null = null; + let mergeBranchTarget: GitBranchInfo | null = null; let renameBranchTarget: GitBranchInfo | null = null; let deleteBranchTarget: GitBranchInfo | null = null; let deleteBranchForce = false; @@ -3207,11 +3210,15 @@ async function merge(branch: GitBranchInfo) { if (!activeRepoPath || branch.current) return; - const strategy = (window.prompt("Merge strategy: default, squash, ff-only, or no-ff", "default") ?? "").trim(); - if (!strategy) return; - if (!["default", "squash", "ff-only", "no-ff"].includes(strategy)) { errorMessage = "Unknown merge strategy."; return; } + mergeBranchTarget = branch; + } + + async function confirmMerge(strategy: MergeStrategy) { + const branch = mergeBranchTarget; + if (!activeRepoPath || !branch || branch.current) return; await runOperation(`Merging ${branch.name}`, async () => { - applyStatus(await mergeBranch(activeRepoPath, branch.name, strategy as import("./lib/types").MergeStrategy)); + applyStatus(await mergeBranch(activeRepoPath, branch.name, strategy)); + mergeBranchTarget = null; await refreshRepositoryViews(activeRepoPath); trackEvent("branch_merged", { remote: branch.remote ? 1 : 0, @@ -5926,6 +5933,17 @@ /> {/if} +{#if mergeBranchTarget} + { if (!isBusy) mergeBranchTarget = null; }} + /> +{/if} + {#if commitNoteTarget} button { font-size: 11.5px; } + .merge-branch-header { padding: 15px 17px; } + .merge-branch-heading { display: flex; align-items: center; gap: 11px; } + .merge-branch-heading h2 { margin: 2px 0 0; color: var(--color-ink); font-size: 15px; line-height: 1.25; } + .merge-branch-icon { + display: grid; + place-items: center; + width: 36px; + height: 36px; + flex: 0 0 auto; + border: 1px solid color-mix(in srgb, var(--color-accent) 34%, var(--color-border)); + border-radius: 9px; + color: var(--color-accent); + background: color-mix(in srgb, var(--color-accent) 10%, var(--color-surface-raised)); + } + .merge-branch-form { display: flex; flex-direction: column; gap: 15px; padding: 17px; } + .merge-branch-route { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 12px; padding: 11px 12px; border: 1px solid var(--color-border-subtle); border-radius: 9px; background: var(--color-surface-raised); } + .merge-branch-route > div { display: grid; gap: 3px; min-width: 0; } + .merge-branch-route > div:last-child { text-align: right; } + .merge-branch-route span { color: var(--color-ink-faint); font-size: 9px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; } + .merge-branch-route strong { overflow: hidden; color: var(--color-ink); font-family: var(--font-mono); font-size: 11.5px; text-overflow: ellipsis; white-space: nowrap; } + .merge-branch-route > svg { color: var(--color-accent); } + .merge-strategy-fieldset { min-width: 0; margin: 0; padding: 0; border: 0; } + .merge-strategy-fieldset legend { margin-bottom: 7px; color: var(--color-ink-faint); font-size: 9.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; } + .merge-strategy-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; } + .merge-strategy-option { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 9px; min-width: 0; padding: 11px; border: 1px solid var(--color-border-subtle); border-radius: 8px; background: var(--color-surface-dim); cursor: pointer; } + .merge-strategy-option:hover { border-color: var(--color-border); background: var(--color-surface-hover); } + .merge-strategy-option.active { border-color: color-mix(in srgb, var(--color-accent) 52%, var(--color-border)); background: color-mix(in srgb, var(--color-accent) 8%, var(--color-surface-raised)); box-shadow: inset 2px 0 0 var(--color-accent); } + .merge-strategy-option > input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; } + .merge-strategy-check { display: grid; place-items: center; width: 17px; height: 17px; margin-top: 1px; border: 1px solid var(--color-border-input); border-radius: 50%; color: #fff; background: var(--color-surface-raised); } + .merge-strategy-option.active .merge-strategy-check { border-color: var(--color-accent); background: var(--color-accent); } + .merge-strategy-copy { display: grid; gap: 3px; min-width: 0; } + .merge-strategy-copy strong { color: var(--color-ink); font-size: 11.5px; } + .merge-strategy-copy small { color: var(--color-ink-faint); font-size: 9.5px; line-height: 1.4; } + .merge-branch-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 1px; } + .merge-branch-actions > button { font-size: 11.5px; } .rename-branch-form { display: flex; flex-direction: column; diff --git a/src/lib/components/MergeBranchDialog.svelte b/src/lib/components/MergeBranchDialog.svelte new file mode 100644 index 0000000..0bcafe8 --- /dev/null +++ b/src/lib/components/MergeBranchDialog.svelte @@ -0,0 +1,119 @@ + + +