From 5f64372ae63089ca0e95fc08bec8b7464bb907ef Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Wed, 1 Jul 2026 20:45:46 +0900 Subject: [PATCH] slop Signed-off-by: Seongmin Lee --- appview/pages/pages.go | 42 ++++- .../templates/repo/pulls/fragments/diff.html | 85 +++++++++- .../pages/templates/repo/pulls/single.html | 78 ++++++++- appview/pulls/diff_hunks_test.go | 77 ++------- appview/pulls/pull2.go | 156 +++++++++--------- input.css | 23 +++ 6 files changed, 309 insertions(+), 152 deletions(-) diff --git a/appview/pages/pages.go b/appview/pages/pages.go index e6b52a8e..8fbe82fe 100644 --- a/appview/pages/pages.go +++ b/appview/pages/pages.go @@ -1432,7 +1432,7 @@ type PullPageBaseParams struct { Pull *models.Pull2 Backlinks []models.RichReferenceLink - Comments []models.Comment + Comments []*models.Comment Commits []types.Commit // all commits between .. LabelDefs map[string]*models.LabelDefinition @@ -1464,8 +1464,46 @@ type PullInterdiffParams struct { ErrorMsg string } +// DiffLine is one row of a unified (inline) diff. Old/New are 1-based line numbers into the +// base/head blob, or -1 when that side has no line here. Content is pre-rendered, safe HTML. +type DiffLine struct { + Op string // " " context, "-" removed, "+" added + Old int + New int + Content template.HTML +} + +// DiffCell is one side of a side-by-side row. Kind is "ctx", "del", "add", or "empty" (a +// blank padding cell). Num is the 1-based line number, or -1 when empty. +type DiffCell struct { + Kind string + Num int + Content template.HTML +} + +// DiffRow is one side-by-side row: the left (base) and right (head) cells. +type DiffRow struct { + Left DiffCell + Right DiffCell +} + +// DiffHunk holds a hunk's rows; exactly one of Lines (unified) / Rows (split) is populated, +// depending on PullDiffFragmentParams.Split. +type DiffHunk struct { + Lines []DiffLine + Rows []DiffRow +} + +// DiffFile is one changed file. Note is set (and Hunks empty) for binary/submodule files. +type DiffFile struct { + Path string + Note string + Hunks []DiffHunk +} + type PullDiffFragmentParams struct { - Diff string + Files []DiffFile + Split bool // side-by-side when true, unified when false } func (p *Pages) PullDiff(w io.Writer, params PullDiffParams) error { diff --git a/appview/pages/templates/repo/pulls/fragments/diff.html b/appview/pages/templates/repo/pulls/fragments/diff.html index 035758da..f63ddd64 100644 --- a/appview/pages/templates/repo/pulls/fragments/diff.html +++ b/appview/pages/templates/repo/pulls/fragments/diff.html @@ -1,5 +1,84 @@ {{ define "repo/pulls/fragments/diff" }} -
-
{{ .Diff }}
-
+
+ {{ range .Files }} +
+
{{ .Path }}
+ {{ if .Note }} +
{{ .Note }}
+ {{ else if .Split }} + {{ range .Hunks }}{{ template "pullDiffSplitHunk" . }}{{ end }} + {{ else }} + {{ range .Hunks }}{{ template "pullDiffUnifiedHunk" . }}{{ end }} + {{ end }} +
+ {{ end }} +
+{{ end }} + +{{ define "pullDiffUnifiedHunk" }} + {{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right px-2" -}} + {{- $sepStyle := "border-r border-gray-200 dark:border-gray-700" -}} + {{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}} + {{- $contentStyle := "px-2 whitespace-pre" -}} + {{- $rowStyle := "inline-flex w-full items-center" -}} + {{- $addStyle := "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400" -}} + {{- $delStyle := "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400" -}} + {{- $ctxStyle := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}} +
+ ··· +
+ {{- range .Lines -}} + {{- $style := $ctxStyle -}} + {{- if eq .Op "+" -}}{{- $style = $addStyle -}}{{- else if eq .Op "-" -}}{{- $style = $delStyle -}}{{- end -}} + + {{ if ge .Old 0 }}{{ .Old }}{{ end }} + {{ if ge .New 0 }}{{ .New }}{{ end }} + {{ .Op }} + {{ .Content }} + + {{- end -}} +
+
+{{ end }} + +{{ define "pullDiffSplitHunk" }} +
+
+ ··· +
+ {{- range .Rows -}}{{- template "pullDiffSplitCell" .Left -}}{{- end -}} +
+
+
+ ··· +
+ {{- range .Rows -}}{{- template "pullDiffSplitCell" .Right -}}{{- end -}} +
+
+
+{{ end }} + +{{/* pullDiffSplitCell renders one side of a split row. Dot is a DiffCell. */}} +{{ define "pullDiffSplitCell" }} + {{- $lineNrStyle := "min-w-[3.5rem] flex-shrink-0 select-none text-right px-2 border-r border-gray-200 dark:border-gray-700" -}} + {{- $opStyle := "w-5 flex-shrink-0 select-none text-center" -}} + {{- $contentStyle := "px-2 whitespace-pre" -}} + {{- $rowStyle := "inline-flex w-full items-center" -}} + {{- if eq .Kind "empty" -}} + +   + +   + + {{- else -}} + {{- $style := "bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400" -}} + {{- $op := " " -}} + {{- if eq .Kind "add" -}}{{- $style = "bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400" -}}{{- $op = "+" -}}{{- end -}} + {{- if eq .Kind "del" -}}{{- $style = "bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400" -}}{{- $op = "-" -}}{{- end -}} + + {{ if ge .Num 0 }}{{ .Num }}{{ end }} + {{ $op }} + {{ .Content }} + + {{- end -}} {{ end }} diff --git a/appview/pages/templates/repo/pulls/single.html b/appview/pages/templates/repo/pulls/single.html index d1b1eba8..b92d3a54 100644 --- a/appview/pages/templates/repo/pulls/single.html +++ b/appview/pages/templates/repo/pulls/single.html @@ -45,7 +45,25 @@ diff tree
- diff body +
+ {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} + {{ template "block" }} +
loading...
@@ -64,3 +82,61 @@

repo content

{{ end }} + +{{ define "block" }} +
+
+ +
+
+ {{ i "chevron-right" "w-4 h-4" }} + + {{ template "repo/fragments/diffStatPill" (dict "Insertions" 27 "Deletions" 27) }} + api/tangled/cbor_gen.go +
+
+
+
+
+ unified + + + + + + + + + + + + + + + + + + + +
···
665func (t *CiDefs_Pipeline) MarshalCBOR(w io.Writer) error {
665func (t *CiPipeline) MarshalCBOR(w io.Writer) error {
+ split + + + + + + + + + + + + + + + + +
···
1thingsome1something
+
+
+{{ end }} diff --git a/appview/pulls/diff_hunks_test.go b/appview/pulls/diff_hunks_test.go index 2180d112..ee7caff6 100644 --- a/appview/pulls/diff_hunks_test.go +++ b/appview/pulls/diff_hunks_test.go @@ -1,7 +1,7 @@ package pulls import ( - "strings" + "html/template" "testing" gitmirrorv1 "tangled.org/core/gitmirror/proto/gen" @@ -10,13 +10,13 @@ import ( func u32(v uint32) *uint32 { return &v } // lines returns n lines named l0..l{n-1}, optionally overriding some by index. -func lines(n int, override map[int]string) []string { - out := make([]string, n) +func lines(n int, override map[int]string) []template.HTML { + out := make([]template.HTML, n) for i := range out { if v, ok := override[i]; ok { - out[i] = v + out[i] = template.HTML(v) } else { - out[i] = "l" + string(rune('0'+i%10)) + out[i] = template.HTML("l" + string(rune('0'+i%10))) } } return out @@ -155,11 +155,11 @@ func TestBuildHunks_MergedKeepsInteriorContext(t *testing.T) { // Base: 20 lines; delete base line 5. Head: base with line 5 removed and a new line // inserted at head index 8 (a few lines after the deletion point). base := lines(20, nil) - head := make([]string, 0, 20) - head = append(head, base[:5]...) // head 0..4 == base 0..4 - head = append(head, base[6:9]...) // head 5..7 == base 6..8 - head = append(head, "INSERTED") // head 8 (new) - head = append(head, base[9:]...) // head 9.. == base 9.. + head := make([]template.HTML, 0, 20) + head = append(head, base[:5]...) // head 0..4 == base 0..4 + head = append(head, base[6:9]...) // head 5..7 == base 6..8 + head = append(head, template.HTML("INSERTED")) // head 8 (new) + head = append(head, base[9:]...) // head 9.. == base 9.. hunks := []*gitmirrorv1.Hunk{ hunk(&gitmirrorv1.LinePair{Lhs: u32(5)}), // delete base line 5 @@ -204,60 +204,3 @@ func TestIndexesForHunk_SliceAndClamp(t *testing.T) { } } -func TestRenderInline_Ordering(t *testing.T) { - base := lines(10, map[int]string{5: "old"}) - head := lines(10, map[int]string{5: "new"}) - h := buildHunks(base, head, []*gitmirrorv1.Hunk{hunk(&gitmirrorv1.LinePair{Lhs: u32(5), Rhs: u32(5)})})[0] - - var b strings.Builder - renderInline(&b, h, base, head) - out := b.String() - - // "old" (removed, LHS) must appear before "new" (added, RHS). - io, in := strings.Index(out, "old"), strings.Index(out, "new") - if io < 0 || in < 0 || io > in { - t.Fatalf("inline ordering wrong (old=%d new=%d):\n%s", io, in, out) - } - // Removed line carries '-', added line carries '+', context ' '. - for line := range strings.SplitSeq(strings.TrimRight(out, "\n"), "\n") { - switch { - case strings.Contains(line, "old") && !strings.Contains(line, "- old"): - t.Fatalf("removed line missing '-' marker: %q", line) - case strings.Contains(line, "new") && !strings.Contains(line, "+ new"): - t.Fatalf("added line missing '+' marker: %q", line) - } - } -} - -func TestRenderInline_MergedKeepsInteriorContextOnce(t *testing.T) { - // Two changes 5 lines apart merge into one hunk. The interior context line (index 7, - // "l7") must appear exactly once, with no +/- marker (not duplicated into both blocks). - base := lines(20, map[int]string{5: "old5", 10: "old10"}) - head := lines(20, map[int]string{5: "new5", 10: "new10"}) - hunks := []*gitmirrorv1.Hunk{ - hunk(&gitmirrorv1.LinePair{Lhs: u32(5), Rhs: u32(5)}), - hunk(&gitmirrorv1.LinePair{Lhs: u32(10), Rhs: u32(10)}), - } - built := buildHunks(base, head, hunks) - if len(built) != 1 { - t.Fatalf("want 1 merged hunk, got %d", len(built)) - } - - var b strings.Builder - renderInline(&b, built[0], base, head) - out := b.String() - - if n := strings.Count(out, "l7"); n != 1 { - t.Fatalf("interior context 'l7' should appear once, got %d:\n%s", n, out) - } - for line := range strings.SplitSeq(strings.TrimRight(out, "\n"), "\n") { - if strings.Contains(line, "l7") && (strings.Contains(line, "- l7") || strings.Contains(line, "+ l7")) { - t.Fatalf("interior context should have no +/- marker: %q", line) - } - } - for _, want := range []string{"- old5", "+ new5", "- old10", "+ new10"} { - if !strings.Contains(out, want) { - t.Fatalf("missing %q in:\n%s", want, out) - } - } -} diff --git a/appview/pulls/pull2.go b/appview/pulls/pull2.go index 74962750..0d923dc1 100644 --- a/appview/pulls/pull2.go +++ b/appview/pulls/pull2.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "html/template" "io" "net/http" "slices" @@ -198,6 +199,7 @@ func (s *Pulls) PullDiff(w http.ResponseWriter, r *http.Request) { var params pages.PullDiffParams params.Pull = pull params.Version = version + params.Comments = comments defer s.pages.PullDiff(w, params) // 1. resolve target branch -> (branch, commit) @@ -232,8 +234,8 @@ func (s *Pulls) PullDiff(w http.ResponseWriter, r *http.Request) { // fileDiff pairs a streamed FileDiff with the line content of its base/head blobs. type fileDiff struct { diff *gitmirrorv1.FileDiff - baseLines []string // lines of the base (lhs) blob; nil for binary/submodule/absent - headLines []string // lines of the head (rhs) blob; nil for binary/submodule/absent + baseLines []template.HTML // lines of the base (lhs) blob; nil for binary/submodule/absent + headLines []template.HTML // lines of the head (rhs) blob; nil for binary/submodule/absent } // htmx fragment. render diff between commits @@ -301,8 +303,18 @@ func (s *Pulls) PullDiffFragment(w http.ResponseWriter, r *http.Request) { if err != nil { return err } - f.baseLines = strings.Split(strings.TrimSuffix(string(baseBlob), "\n"), "\n") - f.headLines = strings.Split(strings.TrimSuffix(string(headBlob), "\n"), "\n") + + // TODO: highlight each blobs + // pass lhs_positions & rhs_positions so highlighter can apply diff highlights + for line := range strings.SplitSeq(strings.TrimSuffix(string(baseBlob), "\n"), "\n") { + f.baseLines = append(f.baseLines, template.HTML(fmt.Sprintf("
%s
", template.HTMLEscapeString(line)))) + } + for line := range strings.SplitSeq(strings.TrimSuffix(string(headBlob), "\n"), "\n") { + f.headLines = append(f.headLines, template.HTML(fmt.Sprintf("
%s
", template.HTMLEscapeString(line)))) + } + + // f.baseLines = strings.Split(strings.TrimSuffix(string(baseBlob), "\n"), "\n") + // f.headLines = strings.Split(strings.TrimSuffix(string(headBlob), "\n"), "\n") return nil }) } @@ -310,32 +322,30 @@ func (s *Pulls) PullDiffFragment(w http.ResponseWriter, r *http.Request) { panic(err) } - //
code
- - // c. render each file's diff, following difftastic's display layout - // (github.com/Wilfred/difftastic src/display): side-by-side by default, inline - // when ?view=unified. Word/token highlighting and line wrapping are omitted. - var b strings.Builder + // c. build the render model, following difftastic's display layout + // (github.com/Wilfred/difftastic src/display): side-by-side by default, inline when + // ?view=unified. All markup/styling lives in the template; here we only assemble data. + params.Split = !unified for _, f := range files { - b.WriteString(f.diff.GetRhsSrc().GetPath()) - b.WriteByte('\n') + df := pages.DiffFile{Path: f.diff.GetRhsSrc().GetPath()} if isBinaryOrSubmodule(f.diff.GetLhsSrc()) || isBinaryOrSubmodule(f.diff.GetRhsSrc()) { - b.WriteString(" (binary or submodule)\n\n") + df.Note = "binary or submodule" + params.Files = append(params.Files, df) continue } for _, h := range buildHunks(f.baseLines, f.headLines, f.diff.Hunks) { - b.WriteString("@@\n") + var dh pages.DiffHunk if unified { - renderInline(&b, h, f.baseLines, f.headLines) + dh.Lines = buildInlineLines(h, f.baseLines, f.headLines) } else { - renderSideBySide(&b, h, f.baseLines, f.headLines) + dh.Rows = buildSplitRows(h, f.baseLines, f.headLines) } + df.Hunks = append(df.Hunks, dh) } - b.WriteByte('\n') + params.Files = append(params.Files, df) } - params.Diff = b.String() } // numContextLines is difftastic's default (`num_context_lines`); pad_before/pad_after add @@ -373,7 +383,7 @@ const maxDistance = 4 // gitmirror's Hunk.Lines are difftastic's novel line pairs; we lack its token-level // MatchedPos, but a line-granular diff's unchanged lines form a 1:1 bijection, which stands // in for difftastic's opposite_positions map (buildOpposites). -func buildHunks(baseLines, headLines []string, hunks []*gitmirrorv1.Hunk) []displayHunk { +func buildHunks(baseLines, headLines []template.HTML, hunks []*gitmirrorv1.Hunk) []displayHunk { oppLhs, oppRhs, maxLhs, maxRhs := buildOpposites(baseLines, headLines, hunks) var flat []linePair @@ -412,7 +422,7 @@ func buildHunks(baseLines, headLines []string, hunks []*gitmirrorv1.Hunk) []disp // specialized to a line-granular diff): every displayed line as a pair, plus a parallel // `changed` flag for pairs that came from a gitmirror hunk. Unchanged lines are a 1:1 // bijection, so the two cursors advance together across gaps. -func alignFile(baseLines, headLines []string, hunks []*gitmirrorv1.Hunk) (pairs []linePair, changed []bool) { +func alignFile(baseLines, headLines []template.HTML, hunks []*gitmirrorv1.Hunk) (pairs []linePair, changed []bool) { li, ri := 0, 0 emitContext := func(n int) { for k := range n { @@ -519,7 +529,7 @@ func toPair(lp *gitmirrorv1.LinePair) linePair { // between unchanged base and head lines. It walks the gitmirror hunks with two cursors — // unchanged runs advance both sides together — recording each unchanged line's counterpart. // maxLhs/maxRhs are the 0-based last line numbers of each blob. -func buildOpposites(baseLines, headLines []string, hunks []*gitmirrorv1.Hunk) (oppLhs, oppRhs map[int]int, maxLhs, maxRhs int) { +func buildOpposites(baseLines, headLines []template.HTML, hunks []*gitmirrorv1.Hunk) (oppLhs, oppRhs map[int]int, maxLhs, maxRhs int) { oppLhs, oppRhs = map[int]int{}, map[int]int{} li, ri := 0, 0 @@ -935,86 +945,83 @@ func unionInto(dst, src map[int]struct{}) { } } -// renderSideBySide lays out a hunk's rows in two columns: -// -// [lhsNum][- ]lhsContent [rhsNum][+ ]rhsContent -// -// A changed side gets a `-` (left) or `+` (right) marker; context gets a space. An absent -// side (-1) renders as a blank gutter, blank marker, and empty content. -func renderSideBySide(b *strings.Builder, h displayHunk, baseLines, headLines []string) { - const spacer = " " - contentWidth := 0 - for _, r := range h.rows { - if r.lhs >= 0 && len(baseLines[r.lhs]) > contentWidth { - contentWidth = len(baseLines[r.lhs]) - } +// dispNum maps a 0-based line number to its 1-based display value, passing through the -1 +// absent sentinel unchanged. +func dispNum(n int) int { + if n < 0 { + return -1 } + return n + 1 +} + +// buildSplitRows turns a hunk into side-by-side rows (difftastic's SideBySideShowBoth): each +// row always has a left (base) and right (head) cell. Unchanged rows show context on both +// sides; a changed row shows a deletion on the left and/or an addition on the right, padding +// the missing side with an empty cell. +func buildSplitRows(h displayHunk, baseLines, headLines []template.HTML) []pages.DiffRow { + rows := make([]pages.DiffRow, 0, len(h.rows)) for _, r := range h.rows { - lhs, rhs := "", "" - if r.lhs >= 0 { - lhs = baseLines[r.lhs] - } - if r.rhs >= 0 { - rhs = headLines[r.rhs] + var row pages.DiffRow + if !r.changed { + row.Left = pages.DiffCell{Kind: "ctx", Num: dispNum(r.lhs), Content: lineAt(baseLines, r.lhs)} + row.Right = pages.DiffCell{Kind: "ctx", Num: dispNum(r.rhs), Content: lineAt(headLines, r.rhs)} + } else { + if r.lhs >= 0 { + row.Left = pages.DiffCell{Kind: "del", Num: dispNum(r.lhs), Content: lineAt(baseLines, r.lhs)} + } else { + row.Left = pages.DiffCell{Kind: "empty", Num: -1} + } + if r.rhs >= 0 { + row.Right = pages.DiffCell{Kind: "add", Num: dispNum(r.rhs), Content: lineAt(headLines, r.rhs)} + } else { + row.Right = pages.DiffCell{Kind: "empty", Num: -1} + } } - lmark := mark(r.changed && r.lhs >= 0, '-') - rmark := mark(r.changed && r.rhs >= 0, '+') - fmt.Fprintf(b, "%s%s%-*s%s|%s%s%s\n", - gutter(r.lhs), lmark, contentWidth, lhs, spacer, gutter(r.rhs), rmark, rhs) + rows = append(rows, row) } + return rows } -// renderInline lays out a hunk as a unified diff: context lines shown once (no marker), -// and each contiguous run of changed rows printed as all removed (LHS, `-`) then all added -// (RHS, `+`). Interior context between merged changes stays a single unmarked line. -func renderInline(b *strings.Builder, h displayHunk, baseLines, headLines []string) { +// buildInlineLines turns a hunk into unified-diff lines: context lines shown once (no +// marker), and each contiguous run of changed rows emitted as all removed (LHS, "-") then +// all added (RHS, "+"). Interior context between merged changes stays a single line. Removed +// lines carry only the old number, added lines only the new number. +func buildInlineLines(h displayHunk, baseLines, headLines []template.HTML) []pages.DiffLine { + var out []pages.DiffLine for i := 0; i < len(h.rows); { r := h.rows[i] if !r.changed { if r.lhs >= 0 { - fmt.Fprintf(b, "%s%s%s%s\n", gutter(r.lhs), gutter(r.rhs), mark(false, ' '), baseLines[r.lhs]) + out = append(out, pages.DiffLine{Op: " ", Old: dispNum(r.lhs), New: dispNum(r.rhs), Content: lineAt(baseLines, r.lhs)}) } i++ continue } - // Contiguous run of changed rows: all removed, then all added. j := i for j < len(h.rows) && h.rows[j].changed { j++ } - // Removed lines carry only the lhs number; the rhs gutter is blank (the paired rhs - // belongs to the added line printed below, not to this removed line). for _, cr := range h.rows[i:j] { if cr.lhs >= 0 { - fmt.Fprintf(b, "%s%s%s%s\n", gutter(cr.lhs), gutter(-1), mark(true, '-'), baseLines[cr.lhs]) + out = append(out, pages.DiffLine{Op: "-", Old: dispNum(cr.lhs), New: -1, Content: lineAt(baseLines, cr.lhs)}) } } - // Added lines carry only the rhs number; the lhs gutter is blank. for _, cr := range h.rows[i:j] { if cr.rhs >= 0 { - fmt.Fprintf(b, "%s%s%s%s\n", gutter(-1), gutter(cr.rhs), mark(true, '+'), headLines[cr.rhs]) + out = append(out, pages.DiffLine{Op: "+", Old: -1, New: dispNum(cr.rhs), Content: lineAt(headLines, cr.rhs)}) } } i = j } + return out } -// mark returns the 2-char marker cell placed between the line-number gutter and content: -// " " when set, " " otherwise. -func mark(set bool, sign byte) string { - if set { - return string(sign) + " " - } - return " " -} - -// gutter renders a 1-based line number right-aligned to width w plus a trailing space, or -// blanks when the line is absent (-1). -func gutter(n int) string { - if n < 0 { - return strings.Repeat(" ", 5) +// lineAt returns lines[n], or "" when n is the -1 absent sentinel or out of range. +func lineAt(lines []template.HTML, n int) template.HTML { + if n < 0 || n >= len(lines) { + return "" } - return fmt.Sprintf("%*d ", 4, n+1) + return lines[n] } // getBlob streams a blob's bytes from gitmirror by OID and concatenates them. @@ -1058,15 +1065,6 @@ func isNullOid(oid string) bool { return true } -// splitLines splits blob bytes into lines, dropping the trailing empty element -// produced by a final newline. -func splitLines(b []byte) []string { - if len(b) == 0 { - return nil - } - return strings.Split(strings.TrimSuffix(string(b), "\n"), "\n") -} - // htmx fragment. render interdiff between changes func (s *Pulls) PullInterdiffFragment(w http.ResponseWriter, r *http.Request) { // var ( diff --git a/input.css b/input.css index fe4cead8..378ce4bd 100644 --- a/input.css +++ b/input.css @@ -306,6 +306,29 @@ } } + table.diff { + @apply w-full border-collapse font-mono; + } + table.diff tr { + @apply bg-white dark:bg-gray-800 text-gray-500 dark:text-gray-400; + } + table.diff .added { + @apply bg-green-100 dark:bg-green-800/30 text-green-700 dark:text-green-400; + } + table.diff .deleted { + @apply bg-red-100 dark:bg-red-800/30 text-red-700 dark:text-red-400; + } + table.diff tr.splitter { + @apply bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 select-none text-center; + } + table.diff td.num { + @apply min-w-10 text-right select-none align-top whitespace-nowrap; + } + table.diff td.content::before { + padding: 0 0.5rem; + content: attr(data-diff-marker); + } + .prose { overflow-wrap: anywhere; } -- 2.51.2