sandlot web <branch>

This commit is contained in:
Chris Wanstrath 2026-02-23 21:14:29 -08:00
parent 1d7f60b50c
commit 33f820f124
4 changed files with 15 additions and 25 deletions

View File

@ -16,7 +16,7 @@ import { action as rebaseAction } from "./commands/rebase.ts"
import { action as saveAction } from "./commands/save.ts" import { action as saveAction } from "./commands/save.ts"
import { action as diffAction } from "./commands/diff.ts" import { action as diffAction } from "./commands/diff.ts"
import { action as showAction } from "./commands/show.ts" import { action as showAction } from "./commands/show.ts"
import { action as browseAction } from "./commands/browse.ts" import { action as webAction } from "./commands/web.ts"
import { action as logAction } from "./commands/log.ts" import { action as logAction } from "./commands/log.ts"
import { action as dirAction } from "./commands/dir.ts" import { action as dirAction } from "./commands/dir.ts"
import { action as editAction } from "./commands/edit.ts" import { action as editAction } from "./commands/edit.ts"
@ -104,10 +104,10 @@ program
.action(showAction) .action(showAction)
program program
.command("browse") .command("web")
.argument("<branch>", "branch name") .argument("<branch>", "branch name")
.description("Open the branch diff in a web browser") .description("Open the branch diff in a web browser")
.action(browseAction) .action(webAction)
program program
.command("save") .command("save")

View File

@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>{{BRANCH}} — sandlot diff</title> <title>{{BRANCH}} — sandlot diff</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/styles/github-dark.min.css">
<style> <style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; margin: 0; padding: 0; background: #0d1117; color: #e6edf3; } body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; margin: 0; padding: 0; background: #0d1117; color: #e6edf3; }
.header { padding: 24px 32px; border-bottom: 1px solid #30363d; } .header { padding: 24px 32px; border-bottom: 1px solid #30363d; }
@ -14,21 +15,8 @@
.meta-section h3 { margin: 0 0 6px; font-size: 13px; text-transform: uppercase; color: #8b949e; letter-spacing: 0.05em; } .meta-section h3 { margin: 0 0 6px; font-size: 13px; text-transform: uppercase; color: #8b949e; letter-spacing: 0.05em; }
.meta-section pre { margin: 0; font-size: 13px; line-height: 1.5; color: #c9d1d9; white-space: pre-wrap; } .meta-section pre { margin: 0; font-size: 13px; line-height: 1.5; color: #c9d1d9; white-space: pre-wrap; }
.diff-container { padding: 16px 32px; } .diff-container { padding: 16px 32px; }
/* Override diff2html for dark theme */ .d2h-file-wrapper { margin-bottom: 16px; border-radius: 6px; overflow: hidden; }
.d2h-wrapper { background: #0d1117; } .d2h-code-line-ctn { background: transparent; }
.d2h-file-header { background: #161b22; border-color: #30363d; color: #e6edf3; }
.d2h-file-wrapper { border-color: #30363d; margin-bottom: 16px; border-radius: 6px; overflow: hidden; }
.d2h-code-linenumber { background: #161b22; color: #8b949e; border-color: #30363d; }
.d2h-code-line { background: #0d1117; color: #e6edf3; }
.d2h-code-side-line { background: #0d1117; }
.d2h-del { background: rgba(248,81,73,0.1); }
.d2h-ins { background: rgba(63,185,80,0.1); }
.d2h-del .d2h-code-line-ctn { background: rgba(248,81,73,0.15); }
.d2h-ins .d2h-code-line-ctn { background: rgba(63,185,80,0.15); }
.d2h-code-line-ctn { color: #e6edf3; }
.d2h-info { background: #161b22; color: #8b949e; border-color: #30363d; }
.d2h-tag { background: #1f6feb; color: #fff; }
.d2h-file-stats-wrapper { display: flex; }
</style> </style>
</head> </head>
<body> <body>
@ -41,6 +29,7 @@
</div> </div>
</div> </div>
<div class="diff-container" id="diff"></div> <div class="diff-container" id="diff"></div>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release/build/highlight.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
<script> <script>
const diffString = {{DIFF_JSON}}; const diffString = {{DIFF_JSON}};

View File

@ -3,7 +3,7 @@ import * as git from "../git.ts"
import { die } from "../fmt.ts" import { die } from "../fmt.ts"
import { requireSession } from "./helpers.ts" import { requireSession } from "./helpers.ts"
const template = await Bun.file(new URL("browse.html", import.meta.url).pathname).text() const template = await Bun.file(new URL("diff.html", import.meta.url).pathname).text()
export async function action(branch: string) { export async function action(branch: string) {
const { session } = await requireSession(branch) const { session } = await requireSession(branch)
@ -20,14 +20,15 @@ export async function action(branch: string) {
die(`No changes on branch "${branch}" compared to ${main}.`) die(`No changes on branch "${branch}" compared to ${main}.`)
} }
const diffJson = JSON.stringify(diff).replaceAll("<", "\\u003c")
const html = template const html = template
.replaceAll("{{BRANCH}}", escapeHtml(branch)) .replaceAll("{{BRANCH}}", escapeHtml(branch))
.replace("{{PROMPT_SECTION}}", session.prompt ? `<p class="prompt">${escapeHtml(session.prompt)}</p>` : "") .replace("{{PROMPT_SECTION}}", () => session.prompt ? `<p class="prompt">${escapeHtml(session.prompt)}</p>` : "")
.replace("{{LOG_SECTION}}", log ? `<div class="meta-section"><h3>Commits</h3><pre>${escapeHtml(log)}</pre></div>` : "") .replace("{{LOG_SECTION}}", () => log ? `<div class="meta-section"><h3>Commits</h3><pre>${escapeHtml(log)}</pre></div>` : "")
.replace("{{STAT_SECTION}}", stat ? `<div class="meta-section"><h3>Stats</h3><pre>${escapeHtml(stat)}</pre></div>` : "") .replace("{{STAT_SECTION}}", () => stat ? `<div class="meta-section"><h3>Stats</h3><pre>${escapeHtml(stat)}</pre></div>` : "")
.replace("{{DIFF_JSON}}", JSON.stringify(diff)) .replace("{{DIFF_JSON}}", () => diffJson)
const tmpPath = `/tmp/sandlot-browse-${branch}.html` const tmpPath = `/tmp/sandlot-${branch}.html`
await Bun.write(tmpPath, html) await Bun.write(tmpPath, html)
await $`open ${tmpPath}`.nothrow().quiet() await $`open ${tmpPath}`.nothrow().quiet()
} }

View File

@ -220,7 +220,7 @@ export async function hasNewCommits(worktreePath: string): Promise<boolean> {
/** Get the full unified diff of a branch vs main as a string. */ /** Get the full unified diff of a branch vs main as a string. */
export async function branchDiff(branch: string, main: string, cwd: string): Promise<string> { export async function branchDiff(branch: string, main: string, cwd: string): Promise<string> {
const result = await $`git diff ${main}...${branch}`.cwd(cwd).nothrow().quiet() const result = await $`git diff --no-ext-diff ${main}...${branch}`.cwd(cwd).nothrow().quiet()
if (result.exitCode !== 0) return "" if (result.exitCode !== 0) return ""
return result.text() return result.text()
} }