{
  "id": "terminal-ledger-diff",
  "name": "Tree and Diff",
  "theme": "terminal-ledger",
  "category": "App Shell",
  "description": "A file tree beside a unified diff, with the change counts carried on the tree so you can see where the work is before opening anything.",
  "layout": "A 260px tree with per-file +/− counts and a diff pane using a two-gutter layout — old line number, new line number — then the content, all in one grid.",
  "modules": [
    {
      "key": "tree",
      "name": "File tree",
      "html": "\n<aside class=\"tr\">\n  <div class=\"tr-h\"><b>CHANGED</b><span>6 files</span></div>\n  <ul>\n    <li class=\"dir\">src/</li>\n    <li class=\"dir sub\">ingest/</li>\n    <li class=\"f sub2 on\">consumer.ts <em class=\"a\">+41</em><em class=\"d\">−12</em></li>\n    <li class=\"f sub2\">rebalance.ts <em class=\"a\">+88</em><em class=\"d\">−4</em></li>\n    <li class=\"f sub2\">checkpoint.ts <em class=\"a\">+2</em><em class=\"d\">−2</em></li>\n    <li class=\"dir sub\">api/</li>\n    <li class=\"f sub2\">routes.ts <em class=\"a\">+6</em><em class=\"d\">−0</em></li>\n    <li class=\"dir\">test/</li>\n    <li class=\"f sub\">consumer.test.ts <em class=\"a\">+114</em><em class=\"d\">−31</em></li>\n    <li class=\"f sub\">fixtures.ts <em class=\"a\">+9</em><em class=\"d\">−0</em></li>\n  </ul>\n</aside>"
    },
    {
      "key": "header",
      "name": "Diff header",
      "html": "\n<div class=\"dh\"><b>src/ingest/consumer.ts</b><span class=\"cnt\"><em class=\"a\">+41</em><em class=\"d\">−12</em></span><span class=\"dh-sp\"></span><button type=\"button\">UNIFIED</button><button type=\"button\" class=\"off\">SPLIT</button><button type=\"button\" class=\"off\">WHITESPACE</button></div>"
    },
    {
      "key": "diff",
      "name": "Unified diff",
      "html": "\n<div class=\"df\">\n  <div class=\"hunk\">@@ -108,9 +108,14 @@ export class Consumer {</div>\n  <div class=\"dl\"><span class=\"o\">108</span><span class=\"n\">108</span><code>   async resume(): Promise&lt;void&gt; {</code></div>\n  <div class=\"dl\"><span class=\"o\">109</span><span class=\"n\">109</span><code>     const cp = await this.store.read(this.group)</code></div>\n  <div class=\"dl del\"><span class=\"o\">110</span><span class=\"n\"></span><code>-    this.offset = cp?.offset ?? 0</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">110</span><code>+    if (!cp) {</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">111</span><code>+      throw new MissingCheckpoint(this.group)</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">112</span><code>+    }</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">113</span><code>+    this.offset = cp.offset</code></div>\n  <div class=\"dl\"><span class=\"o\">111</span><span class=\"n\">114</span><code>     this.state = 'RUNNING'</code></div>\n  <div class=\"dl\"><span class=\"o\">112</span><span class=\"n\">115</span><code>   }</code></div>\n</div>"
    },
    {
      "key": "review",
      "name": "Review bar",
      "html": "\n<footer class=\"rb\"><span>2 of 6 files reviewed</span><span class=\"rb-sp\"></span><button type=\"button\">COMMENT</button><button type=\"button\">REQUEST CHANGES</button><button type=\"button\" class=\"ok\">APPROVE</button></footer>"
    }
  ],
  "moduleCount": 4,
  "reorderable": false,
  "meta": {
    "id": "terminal-ledger-diff",
    "model": "Claude Opus 5",
    "author": "Claude Code",
    "createdAt": "2026-08-29T18:41:55+09:00",
    "updatedAt": "2026-08-29T18:41:55+09:00",
    "version": "1.0.0",
    "isNew": false
  },
  "link": "/api/v1/designs/terminal-ledger-diff.json",
  "structure": "A 260px tree with per-file +/− counts and a diff pane using a two-gutter layout — old line number, new line number — then the content, all in one grid.",
  "html": "<div class=\"dz dz-terminal-ledger-diff\" data-design=\"terminal-ledger-diff\" data-theme=\"terminal-ledger\">\n<section class=\"dz-mod\" data-mod=\"tree\" aria-label=\"File tree\">\n<aside class=\"tr\">\n  <div class=\"tr-h\"><b>CHANGED</b><span>6 files</span></div>\n  <ul>\n    <li class=\"dir\">src/</li>\n    <li class=\"dir sub\">ingest/</li>\n    <li class=\"f sub2 on\">consumer.ts <em class=\"a\">+41</em><em class=\"d\">−12</em></li>\n    <li class=\"f sub2\">rebalance.ts <em class=\"a\">+88</em><em class=\"d\">−4</em></li>\n    <li class=\"f sub2\">checkpoint.ts <em class=\"a\">+2</em><em class=\"d\">−2</em></li>\n    <li class=\"dir sub\">api/</li>\n    <li class=\"f sub2\">routes.ts <em class=\"a\">+6</em><em class=\"d\">−0</em></li>\n    <li class=\"dir\">test/</li>\n    <li class=\"f sub\">consumer.test.ts <em class=\"a\">+114</em><em class=\"d\">−31</em></li>\n    <li class=\"f sub\">fixtures.ts <em class=\"a\">+9</em><em class=\"d\">−0</em></li>\n  </ul>\n</aside></section><section class=\"dz-mod\" data-mod=\"header\" aria-label=\"Diff header\">\n<div class=\"dh\"><b>src/ingest/consumer.ts</b><span class=\"cnt\"><em class=\"a\">+41</em><em class=\"d\">−12</em></span><span class=\"dh-sp\"></span><button type=\"button\">UNIFIED</button><button type=\"button\" class=\"off\">SPLIT</button><button type=\"button\" class=\"off\">WHITESPACE</button></div></section><section class=\"dz-mod\" data-mod=\"diff\" aria-label=\"Unified diff\">\n<div class=\"df\">\n  <div class=\"hunk\">@@ -108,9 +108,14 @@ export class Consumer {</div>\n  <div class=\"dl\"><span class=\"o\">108</span><span class=\"n\">108</span><code>   async resume(): Promise&lt;void&gt; {</code></div>\n  <div class=\"dl\"><span class=\"o\">109</span><span class=\"n\">109</span><code>     const cp = await this.store.read(this.group)</code></div>\n  <div class=\"dl del\"><span class=\"o\">110</span><span class=\"n\"></span><code>-    this.offset = cp?.offset ?? 0</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">110</span><code>+    if (!cp) {</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">111</span><code>+      throw new MissingCheckpoint(this.group)</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">112</span><code>+    }</code></div>\n  <div class=\"dl add\"><span class=\"o\"></span><span class=\"n\">113</span><code>+    this.offset = cp.offset</code></div>\n  <div class=\"dl\"><span class=\"o\">111</span><span class=\"n\">114</span><code>     this.state = 'RUNNING'</code></div>\n  <div class=\"dl\"><span class=\"o\">112</span><span class=\"n\">115</span><code>   }</code></div>\n</div></section><section class=\"dz-mod\" data-mod=\"review\" aria-label=\"Review bar\">\n<footer class=\"rb\"><span>2 of 6 files reviewed</span><span class=\"rb-sp\"></span><button type=\"button\">COMMENT</button><button type=\"button\">REQUEST CHANGES</button><button type=\"button\" class=\"ok\">APPROVE</button></footer></section>\n</div>",
  "css": "\n.dz-terminal-ledger-diff { background: var(--background); color: var(--foreground); font-family: var(--pv-mono); font-size: 12.5px; display: grid; grid-template-columns: 260px minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr) auto; min-height: 520px; }\n.dz-terminal-ledger-diff .tr { grid-row: 1 / 3; border-right: var(--pv-bw) solid var(--border); overflow-y: auto; }\n.dz-terminal-ledger-diff .tr-h { display: flex; justify-content: space-between; padding: 9px 12px; border-bottom: var(--pv-bw) solid var(--border); font-size: 11px; letter-spacing: .12em; }\n.dz-terminal-ledger-diff .tr-h span { color: var(--muted-foreground); letter-spacing: 0; }\n.dz-terminal-ledger-diff .tr ul { list-style: none; margin: 0; padding: 6px 0; }\n.dz-terminal-ledger-diff .tr li { padding: 4px 12px; font-size: 12px; }\n.dz-terminal-ledger-diff .dir { color: var(--muted-foreground); }\n.dz-terminal-ledger-diff .sub { padding-left: 24px !important; }\n.dz-terminal-ledger-diff .sub2 { padding-left: 36px !important; }\n.dz-terminal-ledger-diff .f { display: flex; gap: 8px; cursor: pointer; transition: background var(--dur) var(--ease); }\n.dz-terminal-ledger-diff .f:hover { background: var(--muted); }\n.dz-terminal-ledger-diff .f.on { background: var(--foreground); color: var(--background); }\n.dz-terminal-ledger-diff .f em { font-style: normal; font-size: 11px; margin-left: auto; }\n.dz-terminal-ledger-diff .f em + em { margin-left: 6px; }\n.dz-terminal-ledger-diff .a { color: var(--chart-1); }\n.dz-terminal-ledger-diff .d { color: var(--destructive); }\n.dz-terminal-ledger-diff .f.on .a, .dz-terminal-ledger-diff .f.on .d { color: var(--background); opacity: .8; }\n.dz-terminal-ledger-diff .dh { display: flex; align-items: center; gap: 12px; padding: 9px 14px; border-bottom: var(--pv-bw) solid var(--border); }\n.dz-terminal-ledger-diff .dh b { font-weight: 400; }\n.dz-terminal-ledger-diff .cnt em { font-style: normal; font-size: 11px; margin-right: 6px; }\n.dz-terminal-ledger-diff .dh-sp { flex: 1; }\n.dz-terminal-ledger-diff .dh button { font: inherit; font-size: 10.5px; letter-spacing: .08em; padding: 4px 10px; border: var(--pv-bw) solid var(--foreground); background: var(--foreground); color: var(--background); cursor: pointer; }\n.dz-terminal-ledger-diff .dh .off { background: var(--background); color: var(--muted-foreground); border-color: var(--border); }\n.dz-terminal-ledger-diff .df { overflow: auto; }\n.dz-terminal-ledger-diff .hunk { padding: 5px 14px; background: var(--muted); color: var(--muted-foreground); font-size: 11.5px; }\n.dz-terminal-ledger-diff .dl { display: grid; grid-template-columns: 48px 48px minmax(0, 1fr); align-items: baseline; }\n.dz-terminal-ledger-diff .dl span { padding: 2px 8px; text-align: right; font-size: 11px; color: var(--muted-foreground); border-right: var(--pv-bw) solid var(--border); }\n.dz-terminal-ledger-diff .dl code { font-family: inherit; padding: 2px 12px; white-space: pre; }\n.dz-terminal-ledger-diff .add { background: color-mix(in srgb, var(--chart-1) 16%, transparent); }\n.dz-terminal-ledger-diff .add code { color: var(--chart-1); }\n.dz-terminal-ledger-diff .del { background: color-mix(in srgb, var(--destructive) 16%, transparent); }\n.dz-terminal-ledger-diff .del code { color: var(--destructive); }\n.dz-terminal-ledger-diff .rb { grid-column: 1 / -1; display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-top: var(--pv-bw) solid var(--border); font-size: 11.5px; color: var(--muted-foreground); }\n.dz-terminal-ledger-diff .rb-sp { flex: 1; }\n.dz-terminal-ledger-diff .rb button { font: inherit; font-size: 10.5px; letter-spacing: .1em; padding: 6px 13px; border: var(--pv-bw) solid var(--border); background: var(--background); color: var(--foreground); cursor: pointer; transition: background var(--dur) var(--ease), color var(--dur) var(--ease); }\n.dz-terminal-ledger-diff .rb button:hover { background: var(--foreground); color: var(--background); }\n.dz-terminal-ledger-diff .rb .ok { border-color: var(--chart-1); color: var(--chart-1); }\n@media (prefers-reduced-motion: reduce) { .dz-terminal-ledger-diff *, .dz-terminal-ledger-diff *::before, .dz-terminal-ledger-diff *::after { transition-duration: 1ms !important; animation-duration: 1ms !important; } }",
  "requiredTheme": "/api/v1/themes/terminal-ledger.json",
  "googleFontsHref": "https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap"
}