diffprism 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  startGlobalServer,
7
7
  startReview,
8
8
  startWatch
9
- } from "./chunk-4VXA6GCO.js";
9
+ } from "./chunk-3CQCLFF3.js";
10
10
 
11
11
  // cli/src/index.ts
12
12
  import { Command } from "commander";
@@ -144,6 +144,12 @@ You can also check for feedback without blocking by calling \`get_review_result\
144
144
  `;
145
145
 
146
146
  // cli/src/commands/setup.ts
147
+ var GITIGNORE_ENTRIES = [
148
+ ".diffprism",
149
+ ".mcp.json",
150
+ ".claude/settings.json",
151
+ ".claude/skills/review/"
152
+ ];
147
153
  function findGitRoot(from) {
148
154
  let dir = path.resolve(from);
149
155
  while (true) {
@@ -306,27 +312,28 @@ async function promptUser(question) {
306
312
  }
307
313
  async function setupGitignore(gitRoot) {
308
314
  const filePath = path.join(gitRoot, ".gitignore");
309
- const entry = ".diffprism";
310
315
  if (fs.existsSync(filePath)) {
311
316
  const content = fs.readFileSync(filePath, "utf-8");
312
317
  const lines = content.split("\n").map((l) => l.trim());
313
- if (lines.includes(entry)) {
318
+ const missing = GITIGNORE_ENTRIES.filter((e) => !lines.includes(e));
319
+ if (missing.length === 0) {
314
320
  return { action: "skipped", filePath };
315
321
  }
316
- const newContent = content.endsWith("\n") ? content + entry + "\n" : content + "\n" + entry + "\n";
322
+ const suffix = missing.map((e) => e + "\n").join("");
323
+ const newContent = content.endsWith("\n") ? content + suffix : content + "\n" + suffix;
317
324
  fs.writeFileSync(filePath, newContent);
318
325
  return { action: "updated", filePath };
319
326
  }
320
327
  const confirmed = await promptUser(
321
- "No .gitignore found. Create one with .diffprism entry? (Y/n) "
328
+ "No .gitignore found. Create one with DiffPrism entries? (Y/n) "
322
329
  );
323
330
  if (!confirmed) {
324
331
  console.log(
325
- " Warning: .diffprism directory will appear in git status and may trigger watch-mode loops."
332
+ " Warning: DiffPrism files will appear in git status and may be accidentally committed."
326
333
  );
327
334
  return { action: "skipped", filePath };
328
335
  }
329
- fs.writeFileSync(filePath, entry + "\n");
336
+ fs.writeFileSync(filePath, GITIGNORE_ENTRIES.map((e) => e + "\n").join(""));
330
337
  return { action: "created", filePath };
331
338
  }
332
339
  async function setup(flags) {
@@ -534,7 +541,8 @@ function teardownGitignore(gitRoot) {
534
541
  }
535
542
  const content = fs2.readFileSync(filePath, "utf-8");
536
543
  const lines = content.split("\n");
537
- const filtered = lines.filter((l) => l.trim() !== ".diffprism");
544
+ const entrySet = new Set(GITIGNORE_ENTRIES);
545
+ const filtered = lines.filter((l) => !entrySet.has(l.trim()));
538
546
  if (filtered.length === lines.length) {
539
547
  return { action: "skipped", filePath };
540
548
  }
@@ -824,7 +832,7 @@ async function serverStop() {
824
832
 
825
833
  // cli/src/index.ts
826
834
  var program = new Command();
827
- program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.20.0" : "0.0.0-dev");
835
+ program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.20.2" : "0.0.0-dev");
828
836
  program.command("review [ref]").description("Open a browser-based diff review").option("--staged", "Review staged changes").option("--unstaged", "Review unstaged changes").option("-t, --title <title>", "Review title").option("--dev", "Use Vite dev server with HMR instead of static files").action(review);
829
837
  program.command("start [ref]").description("Set up DiffPrism and start watching for changes").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(start);
830
838
  program.command("watch [ref]").description("Start a persistent diff watcher with live-updating browser UI").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").action(watch);
@@ -1132,7 +1132,6 @@ async function isServerAlive() {
1132
1132
  }
1133
1133
 
1134
1134
  // packages/core/src/watch.ts
1135
- import { createHash } from "crypto";
1136
1135
  import getPort2 from "get-port";
1137
1136
  import open2 from "open";
1138
1137
 
@@ -1265,7 +1264,8 @@ function createWatchBridge(port, callbacks) {
1265
1264
  });
1266
1265
  }
1267
1266
 
1268
- // packages/core/src/watch.ts
1267
+ // packages/core/src/diff-utils.ts
1268
+ import { createHash } from "crypto";
1269
1269
  function hashDiff(rawDiff) {
1270
1270
  return createHash("sha256").update(rawDiff).digest("hex");
1271
1271
  }
@@ -1296,6 +1296,8 @@ function detectChangedFiles(oldDiffSet, newDiffSet) {
1296
1296
  }
1297
1297
  return changed;
1298
1298
  }
1299
+
1300
+ // packages/core/src/watch.ts
1299
1301
  async function startWatch(options) {
1300
1302
  const {
1301
1303
  diffRef,
@@ -1447,6 +1449,8 @@ import open3 from "open";
1447
1449
  import { WebSocketServer as WebSocketServer3, WebSocket as WebSocket3 } from "ws";
1448
1450
  var sessions2 = /* @__PURE__ */ new Map();
1449
1451
  var clientSessions = /* @__PURE__ */ new Map();
1452
+ var sessionWatchers = /* @__PURE__ */ new Map();
1453
+ var serverPollInterval = 2e3;
1450
1454
  var reopenBrowserIfNeeded = null;
1451
1455
  function toSummary(session) {
1452
1456
  const { payload } = session;
@@ -1466,7 +1470,9 @@ function toSummary(session) {
1466
1470
  additions,
1467
1471
  deletions,
1468
1472
  status: session.status,
1469
- createdAt: session.createdAt
1473
+ decision: session.result?.decision,
1474
+ createdAt: session.createdAt,
1475
+ hasNewChanges: session.hasNewChanges
1470
1476
  };
1471
1477
  }
1472
1478
  function readBody(req) {
@@ -1517,6 +1523,93 @@ function sendToSessionClients(sessionId, msg) {
1517
1523
  }
1518
1524
  }
1519
1525
  }
1526
+ function hasViewersForSession(sessionId) {
1527
+ for (const [client, sid] of clientSessions.entries()) {
1528
+ if (sid === sessionId && client.readyState === WebSocket3.OPEN) {
1529
+ return true;
1530
+ }
1531
+ }
1532
+ return false;
1533
+ }
1534
+ function startSessionWatcher(sessionId) {
1535
+ if (sessionWatchers.has(sessionId)) return;
1536
+ const session = sessions2.get(sessionId);
1537
+ if (!session?.diffRef) return;
1538
+ const interval = setInterval(() => {
1539
+ const s = sessions2.get(sessionId);
1540
+ if (!s?.diffRef) {
1541
+ stopSessionWatcher(sessionId);
1542
+ return;
1543
+ }
1544
+ try {
1545
+ const { diffSet: newDiffSet, rawDiff: newRawDiff } = getDiff(s.diffRef, {
1546
+ cwd: s.projectPath
1547
+ });
1548
+ const newHash = hashDiff(newRawDiff);
1549
+ if (newHash !== s.lastDiffHash) {
1550
+ const newBriefing = analyze(newDiffSet);
1551
+ const changedFiles = detectChangedFiles(s.lastDiffSet ?? null, newDiffSet);
1552
+ s.payload = {
1553
+ ...s.payload,
1554
+ diffSet: newDiffSet,
1555
+ rawDiff: newRawDiff,
1556
+ briefing: newBriefing
1557
+ };
1558
+ s.lastDiffHash = newHash;
1559
+ s.lastDiffSet = newDiffSet;
1560
+ if (hasViewersForSession(sessionId)) {
1561
+ sendToSessionClients(sessionId, {
1562
+ type: "diff:update",
1563
+ payload: {
1564
+ diffSet: newDiffSet,
1565
+ rawDiff: newRawDiff,
1566
+ briefing: newBriefing,
1567
+ changedFiles,
1568
+ timestamp: Date.now()
1569
+ }
1570
+ });
1571
+ s.hasNewChanges = false;
1572
+ } else {
1573
+ s.hasNewChanges = true;
1574
+ broadcastSessionList();
1575
+ }
1576
+ }
1577
+ } catch {
1578
+ }
1579
+ }, serverPollInterval);
1580
+ sessionWatchers.set(sessionId, interval);
1581
+ }
1582
+ function stopSessionWatcher(sessionId) {
1583
+ const interval = sessionWatchers.get(sessionId);
1584
+ if (interval) {
1585
+ clearInterval(interval);
1586
+ sessionWatchers.delete(sessionId);
1587
+ }
1588
+ }
1589
+ function startAllWatchers() {
1590
+ for (const [id, session] of sessions2.entries()) {
1591
+ if (session.diffRef && !sessionWatchers.has(id)) {
1592
+ startSessionWatcher(id);
1593
+ }
1594
+ }
1595
+ }
1596
+ function stopAllWatchers() {
1597
+ for (const [id, interval] of sessionWatchers.entries()) {
1598
+ clearInterval(interval);
1599
+ }
1600
+ sessionWatchers.clear();
1601
+ }
1602
+ function hasConnectedClients() {
1603
+ if (!wss) return false;
1604
+ for (const client of wss.clients) {
1605
+ if (client.readyState === WebSocket3.OPEN) return true;
1606
+ }
1607
+ return false;
1608
+ }
1609
+ function broadcastSessionList() {
1610
+ const summaries = Array.from(sessions2.values()).map(toSummary);
1611
+ broadcastToAll({ type: "session:list", payload: summaries });
1612
+ }
1520
1613
  async function handleApiRequest(req, res) {
1521
1614
  const method = req.method ?? "GET";
1522
1615
  const url = (req.url ?? "/").split("?")[0];
@@ -1543,18 +1636,28 @@ async function handleApiRequest(req, res) {
1543
1636
  if (method === "POST" && url === "/api/reviews") {
1544
1637
  try {
1545
1638
  const body = await readBody(req);
1546
- const { payload, projectPath } = JSON.parse(body);
1639
+ const { payload, projectPath, diffRef } = JSON.parse(body);
1547
1640
  const sessionId = `session-${randomUUID().slice(0, 8)}`;
1548
1641
  payload.reviewId = sessionId;
1642
+ if (diffRef) {
1643
+ payload.watchMode = true;
1644
+ }
1549
1645
  const session = {
1550
1646
  id: sessionId,
1551
1647
  payload,
1552
1648
  projectPath,
1553
1649
  status: "pending",
1554
1650
  createdAt: Date.now(),
1555
- result: null
1651
+ result: null,
1652
+ diffRef,
1653
+ lastDiffHash: diffRef ? hashDiff(payload.rawDiff) : void 0,
1654
+ lastDiffSet: diffRef ? payload.diffSet : void 0,
1655
+ hasNewChanges: false
1556
1656
  };
1557
1657
  sessions2.set(sessionId, session);
1658
+ if (diffRef && hasConnectedClients()) {
1659
+ startSessionWatcher(sessionId);
1660
+ }
1558
1661
  broadcastToAll({
1559
1662
  type: "session:added",
1560
1663
  payload: toSummary(session)
@@ -1593,6 +1696,7 @@ async function handleApiRequest(req, res) {
1593
1696
  const result = JSON.parse(body);
1594
1697
  session.result = result;
1595
1698
  session.status = "submitted";
1699
+ broadcastToAll({ type: "session:updated", payload: toSummary(session) });
1596
1700
  jsonResponse(res, 200, { ok: true });
1597
1701
  } catch {
1598
1702
  jsonResponse(res, 400, { error: "Invalid request body" });
@@ -1644,6 +1748,7 @@ async function handleApiRequest(req, res) {
1644
1748
  }
1645
1749
  const deleteParams = matchRoute(method, url, "DELETE", "/api/reviews/:id");
1646
1750
  if (deleteParams) {
1751
+ stopSessionWatcher(deleteParams.id);
1647
1752
  if (sessions2.delete(deleteParams.id)) {
1648
1753
  jsonResponse(res, 200, { ok: true });
1649
1754
  } else {
@@ -1659,8 +1764,10 @@ async function startGlobalServer(options = {}) {
1659
1764
  httpPort: preferredHttpPort = 24680,
1660
1765
  wsPort: preferredWsPort = 24681,
1661
1766
  silent = false,
1662
- dev = false
1767
+ dev = false,
1768
+ pollInterval = 2e3
1663
1769
  } = options;
1770
+ serverPollInterval = pollInterval;
1664
1771
  const [httpPort, wsPort] = await Promise.all([
1665
1772
  getPort3({ port: preferredHttpPort }),
1666
1773
  getPort3({ port: preferredWsPort })
@@ -1686,6 +1793,7 @@ async function startGlobalServer(options = {}) {
1686
1793
  });
1687
1794
  wss = new WebSocketServer3({ port: wsPort });
1688
1795
  wss.on("connection", (ws, req) => {
1796
+ startAllWatchers();
1689
1797
  const url = new URL(req.url ?? "/", `http://localhost:${wsPort}`);
1690
1798
  const sessionId = url.searchParams.get("sessionId");
1691
1799
  if (sessionId) {
@@ -1693,6 +1801,8 @@ async function startGlobalServer(options = {}) {
1693
1801
  const session = sessions2.get(sessionId);
1694
1802
  if (session) {
1695
1803
  session.status = "in_review";
1804
+ session.hasNewChanges = false;
1805
+ broadcastToAll({ type: "session:updated", payload: toSummary(session) });
1696
1806
  const msg = {
1697
1807
  type: "review:init",
1698
1808
  payload: session.payload
@@ -1711,6 +1821,8 @@ async function startGlobalServer(options = {}) {
1711
1821
  if (session) {
1712
1822
  clientSessions.set(ws, session.id);
1713
1823
  session.status = "in_review";
1824
+ session.hasNewChanges = false;
1825
+ broadcastToAll({ type: "session:updated", payload: toSummary(session) });
1714
1826
  ws.send(JSON.stringify({
1715
1827
  type: "review:init",
1716
1828
  payload: session.payload
@@ -1728,6 +1840,7 @@ async function startGlobalServer(options = {}) {
1728
1840
  if (session) {
1729
1841
  session.result = msg.payload;
1730
1842
  session.status = "submitted";
1843
+ broadcastToAll({ type: "session:updated", payload: toSummary(session) });
1731
1844
  }
1732
1845
  }
1733
1846
  } else if (msg.type === "session:select") {
@@ -1735,17 +1848,33 @@ async function startGlobalServer(options = {}) {
1735
1848
  if (session) {
1736
1849
  clientSessions.set(ws, session.id);
1737
1850
  session.status = "in_review";
1851
+ session.hasNewChanges = false;
1852
+ startSessionWatcher(session.id);
1853
+ broadcastToAll({ type: "session:updated", payload: toSummary(session) });
1738
1854
  ws.send(JSON.stringify({
1739
1855
  type: "review:init",
1740
1856
  payload: session.payload
1741
1857
  }));
1742
1858
  }
1859
+ } else if (msg.type === "session:close") {
1860
+ const closedId = msg.payload.sessionId;
1861
+ stopSessionWatcher(closedId);
1862
+ sessions2.delete(closedId);
1863
+ for (const [client, sid] of clientSessions.entries()) {
1864
+ if (sid === closedId) {
1865
+ clientSessions.delete(client);
1866
+ }
1867
+ }
1868
+ broadcastSessionList();
1743
1869
  }
1744
1870
  } catch {
1745
1871
  }
1746
1872
  });
1747
1873
  ws.on("close", () => {
1748
1874
  clientSessions.delete(ws);
1875
+ if (!hasConnectedClients()) {
1876
+ stopAllWatchers();
1877
+ }
1749
1878
  });
1750
1879
  });
1751
1880
  await new Promise((resolve, reject) => {
@@ -1772,19 +1901,13 @@ Waiting for reviews...
1772
1901
  }
1773
1902
  const uiUrl = `http://localhost:${uiPort}?wsPort=${wsPort}&serverMode=true`;
1774
1903
  await open3(uiUrl);
1775
- function hasConnectedClients() {
1776
- if (!wss) return false;
1777
- for (const client of wss.clients) {
1778
- if (client.readyState === WebSocket3.OPEN) return true;
1779
- }
1780
- return false;
1781
- }
1782
1904
  reopenBrowserIfNeeded = () => {
1783
1905
  if (!hasConnectedClients()) {
1784
1906
  open3(uiUrl);
1785
1907
  }
1786
1908
  };
1787
1909
  async function stop() {
1910
+ stopAllWatchers();
1788
1911
  if (wss) {
1789
1912
  for (const client of wss.clients) {
1790
1913
  client.close();
@@ -7,7 +7,7 @@ import {
7
7
  readReviewResult,
8
8
  readWatchFile,
9
9
  startReview
10
- } from "./chunk-4VXA6GCO.js";
10
+ } from "./chunk-3CQCLFF3.js";
11
11
 
12
12
  // packages/mcp-server/src/index.ts
13
13
  import fs from "fs";
@@ -47,7 +47,7 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
47
47
  {
48
48
  method: "POST",
49
49
  headers: { "Content-Type": "application/json" },
50
- body: JSON.stringify({ payload, projectPath: cwd })
50
+ body: JSON.stringify({ payload, projectPath: cwd, diffRef })
51
51
  }
52
52
  );
53
53
  if (!createResponse.ok) {
@@ -76,7 +76,7 @@ async function reviewViaGlobalServer(serverInfo, diffRef, options) {
76
76
  async function startMcpServer() {
77
77
  const server = new McpServer({
78
78
  name: "diffprism",
79
- version: true ? "0.20.0" : "0.0.0-dev"
79
+ version: true ? "0.20.2" : "0.0.0-dev"
80
80
  });
81
81
  server.tool(
82
82
  "open_review",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffprism",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "type": "module",
5
5
  "description": "Local-first code review tool for agent-generated code changes",
6
6
  "bin": {
@@ -0,0 +1 @@
1
+ :root{--diff-background-color:initial;--diff-text-color:initial;--diff-font-family:Consolas,Courier,monospace;--diff-selection-background-color:#b3d7ff;--diff-selection-text-color:var(--diff-text-color);--diff-gutter-insert-background-color:#d6fedb;--diff-gutter-insert-text-color:var(--diff-text-color);--diff-gutter-delete-background-color:#fadde0;--diff-gutter-delete-text-color:var(--diff-text-color);--diff-gutter-selected-background-color:#fffce0;--diff-gutter-selected-text-color:var(--diff-text-color);--diff-code-insert-background-color:#eaffee;--diff-code-insert-text-color:var(--diff-text-color);--diff-code-delete-background-color:#fdeff0;--diff-code-delete-text-color:var(--diff-text-color);--diff-code-insert-edit-background-color:#c0dc91;--diff-code-insert-edit-text-color:var(--diff-text-color);--diff-code-delete-edit-background-color:#f39ea2;--diff-code-delete-edit-text-color:var(--diff-text-color);--diff-code-selected-background-color:#fffce0;--diff-code-selected-text-color:var(--diff-text-color);--diff-omit-gutter-line-color:#cb2a1d}.diff{background-color:var(--diff-background-color);border-collapse:collapse;color:var(--diff-text-color);table-layout:fixed;width:100%}.diff::-moz-selection{background-color:#b3d7ff;background-color:var(--diff-selection-background-color);color:var(--diff-text-color);color:var(--diff-selection-text-color)}.diff::selection{background-color:#b3d7ff;background-color:var(--diff-selection-background-color);color:var(--diff-text-color);color:var(--diff-selection-text-color)}.diff td{padding-bottom:0;padding-top:0;vertical-align:top}.diff-line{font-family:Consolas,Courier,monospace;font-family:var(--diff-font-family);line-height:1.5}.diff-gutter>a{color:inherit;display:block}.diff-gutter{cursor:pointer;padding:0 1ch;text-align:right;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diff-gutter-insert{background-color:#d6fedb;background-color:var(--diff-gutter-insert-background-color);color:var(--diff-text-color);color:var(--diff-gutter-insert-text-color)}.diff-gutter-delete{background-color:#fadde0;background-color:var(--diff-gutter-delete-background-color);color:var(--diff-text-color);color:var(--diff-gutter-delete-text-color)}.diff-gutter-omit{cursor:default}.diff-gutter-selected{background-color:#fffce0;background-color:var(--diff-gutter-selected-background-color);color:var(--diff-text-color);color:var(--diff-gutter-selected-text-color)}.diff-code{word-wrap:break-word;padding:0 0 0 .5em;white-space:pre-wrap;word-break:break-all}.diff-code-edit{color:inherit}.diff-code-insert{background-color:#eaffee;background-color:var(--diff-code-insert-background-color);color:var(--diff-text-color);color:var(--diff-code-insert-text-color)}.diff-code-insert .diff-code-edit{background-color:#c0dc91;background-color:var(--diff-code-insert-edit-background-color);color:var(--diff-text-color);color:var(--diff-code-insert-edit-text-color)}.diff-code-delete{background-color:#fdeff0;background-color:var(--diff-code-delete-background-color);color:var(--diff-text-color);color:var(--diff-code-delete-text-color)}.diff-code-delete .diff-code-edit{background-color:#f39ea2;background-color:var(--diff-code-delete-edit-background-color);color:var(--diff-text-color);color:var(--diff-code-delete-edit-text-color)}.diff-code-selected{background-color:#fffce0;background-color:var(--diff-code-selected-background-color);color:var(--diff-text-color);color:var(--diff-code-selected-text-color)}.diff-widget-content{vertical-align:top}.diff-gutter-col{width:7ch}.diff-gutter-omit{height:0}.diff-gutter-omit:before{background-color:#cb2a1d;background-color:var(--diff-omit-gutter-line-color);content:" ";display:block;height:100%;margin-left:4.6ch;overflow:hidden;white-space:pre;width:2px}.diff-decoration{line-height:1.5;-webkit-user-select:none;-moz-user-select:none;user-select:none}.diff-decoration-content{font-family:Consolas,Courier,monospace;font-family:var(--diff-font-family);padding:0}*,:before,:after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / .5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.right-2{right:.5rem}.top-2{top:.5rem}.z-50{z-index:50}.col-span-2{grid-column:span 2 / span 2}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:0px}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-\[280px\]{width:280px}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-3{row-gap:.75rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-accent{border-color:var(--color-accent)}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1))}.border-border{border-color:var(--color-border)}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-green-300{--tw-border-opacity:1;border-color:rgb(134 239 172 / var(--tw-border-opacity, 1))}.border-green-500\/30{border-color:#22c55e4d}.border-orange-300{--tw-border-opacity:1;border-color:rgb(253 186 116 / var(--tw-border-opacity, 1))}.border-purple-300{--tw-border-opacity:1;border-color:rgb(216 180 254 / var(--tw-border-opacity, 1))}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165 / var(--tw-border-opacity, 1))}.border-red-500\/30{border-color:#ef44444d}.border-transparent{border-color:transparent}.border-yellow-300{--tw-border-opacity:1;border-color:rgb(253 224 71 / var(--tw-border-opacity, 1))}.border-yellow-500\/30{border-color:#eab3084d}.border-t-accent{border-top-color:var(--color-accent)}.bg-background{background-color:var(--color-background)}.bg-black\/50{background-color:#00000080}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254 / var(--tw-bg-opacity, 1))}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231 / var(--tw-bg-opacity, 1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-600\/20{background-color:#16a34a33}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213 / var(--tw-bg-opacity, 1))}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255 / var(--tw-bg-opacity, 1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226 / var(--tw-bg-opacity, 1))}.bg-red-600\/20{background-color:#dc262633}.bg-surface{background-color:var(--color-surface)}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195 / var(--tw-bg-opacity, 1))}.bg-yellow-600\/20{background-color:#ca8a0433}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-3{padding-bottom:.75rem}.pr-6{padding-right:1.5rem}.pt-3{padding-top:.75rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wide{letter-spacing:.025em}.text-accent{color:var(--color-accent)}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61 / var(--tw-text-opacity, 1))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12 / var(--tw-text-opacity, 1))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7 / var(--tw-text-opacity, 1))}.opacity-0{opacity:0}.opacity-75{opacity:.75}.shadow-xl{--tw-shadow:0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-100{transition-duration:.1s}:root{--color-background: #ffffff;--color-surface: #f6f8fa;--color-border: #d0d7de;--color-text-primary: #1f2328;--color-text-secondary: #656d76;--color-accent: #0969da;--color-diff-bg: #ffffff;--color-diff-text: #1f2328;--color-gutter-bg: #f6f8fa;--color-gutter-text: #656d76;--color-gutter-border: #d0d7de;--color-diff-insert-bg: rgba(46, 160, 67, .15);--color-diff-insert-gutter-bg: rgba(46, 160, 67, .2);--color-diff-insert-gutter-text: #1a7f37;--color-diff-delete-bg: rgba(248, 81, 73, .15);--color-diff-delete-gutter-bg: rgba(248, 81, 73, .2);--color-diff-delete-gutter-text: #cf222e;--color-hunk-bg: rgba(9, 105, 218, .1);--color-hunk-border: #d0d7de;--color-hunk-gutter-bg: rgba(9, 105, 218, .15);--color-hunk-gutter-text: #0969da;--color-hunk-content-text: #656d76;--color-diff-edit-insert: rgba(46, 160, 67, .4);--color-diff-edit-delete: rgba(248, 81, 73, .4);--color-split-divider: #d0d7de;--color-widget-bg: #f6f8fa;--color-comment-btn-bg: #238636;--color-comment-indicator: #0969da;--color-scrollbar-track: #ffffff;--color-scrollbar-thumb: #d0d7de;--color-scrollbar-thumb-hover: #afb8c1;--color-token-comment: #6e7781;--color-token-punctuation: #1f2328;--color-token-property: #0550ae;--color-token-string: #0a3069;--color-token-operator: #cf222e;--color-token-keyword: #cf222e;--color-token-function: #8250df;--color-token-variable: #953800;--color-added: rgba(46, 160, 67, .44);--color-deleted: rgba(248, 81, 73, .44)}.dark{--color-background: #0d1117;--color-surface: #161b22;--color-border: #30363d;--color-text-primary: #e6edf3;--color-text-secondary: #8b949e;--color-accent: #58a6ff;--color-diff-bg: #0d1117;--color-diff-text: #e6edf3;--color-gutter-bg: #161b22;--color-gutter-text: #8b949e;--color-gutter-border: #30363d;--color-diff-insert-bg: rgba(46, 160, 67, .15);--color-diff-insert-gutter-bg: rgba(46, 160, 67, .2);--color-diff-insert-gutter-text: #7ee787;--color-diff-delete-bg: rgba(248, 81, 73, .15);--color-diff-delete-gutter-bg: rgba(248, 81, 73, .2);--color-diff-delete-gutter-text: #f85149;--color-hunk-bg: rgba(88, 166, 255, .1);--color-hunk-border: #30363d;--color-hunk-gutter-bg: rgba(88, 166, 255, .15);--color-hunk-gutter-text: #58a6ff;--color-hunk-content-text: #8b949e;--color-diff-edit-insert: rgba(46, 160, 67, .4);--color-diff-edit-delete: rgba(248, 81, 73, .4);--color-split-divider: #30363d;--color-widget-bg: #161b22;--color-comment-btn-bg: #238636;--color-comment-indicator: #58a6ff;--color-scrollbar-track: #0d1117;--color-scrollbar-thumb: #30363d;--color-scrollbar-thumb-hover: #484f58;--color-token-comment: #8b949e;--color-token-punctuation: #e6edf3;--color-token-property: #79c0ff;--color-token-string: #a5d6ff;--color-token-operator: #ff7b72;--color-token-keyword: #ff7b72;--color-token-function: #d2a8ff;--color-token-variable: #ffa657;--color-added: rgba(46, 160, 67, .44);--color-deleted: rgba(248, 81, 73, .44)}.diff-unified,.diff-split{background-color:var(--color-diff-bg);color:var(--color-diff-text);font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;font-size:13px;line-height:20px}.diff-unified .diff-gutter,.diff-split .diff-gutter{background-color:var(--color-gutter-bg);color:var(--color-gutter-text);border-right:1px solid var(--color-gutter-border);padding:0 8px;min-width:50px;text-align:right;-webkit-user-select:none;-moz-user-select:none;user-select:none;cursor:default}.diff-unified .diff-gutter-col,.diff-split .diff-gutter-col{width:60px;min-width:60px}.diff-unified .diff-code,.diff-split .diff-code{padding:0 12px;white-space:pre}.diff-unified .diff-code-insert,.diff-split .diff-code-insert{background-color:var(--color-diff-insert-bg)}.diff-unified .diff-code-insert .diff-code-text,.diff-split .diff-code-insert .diff-code-text{background-color:transparent}.diff-unified .diff-gutter-insert,.diff-split .diff-gutter-insert{background-color:var(--color-diff-insert-gutter-bg);color:var(--color-diff-insert-gutter-text)}.diff-unified .diff-code-delete,.diff-split .diff-code-delete{background-color:var(--color-diff-delete-bg)}.diff-unified .diff-code-delete .diff-code-text,.diff-split .diff-code-delete .diff-code-text{background-color:transparent}.diff-unified .diff-gutter-delete,.diff-split .diff-gutter-delete{background-color:var(--color-diff-delete-gutter-bg);color:var(--color-diff-delete-gutter-text)}.diff-unified .diff-code-normal,.diff-split .diff-code-normal{background-color:transparent}.diff-unified .diff-gutter-normal,.diff-split .diff-gutter-normal{background-color:var(--color-gutter-bg)}.diff-unified .diff-hunk-header,.diff-split .diff-hunk-header{background-color:var(--color-hunk-bg);border-top:1px solid var(--color-hunk-border);border-bottom:1px solid var(--color-hunk-border)}.diff-unified .diff-hunk-header-gutter,.diff-split .diff-hunk-header-gutter{background-color:var(--color-hunk-gutter-bg);color:var(--color-hunk-gutter-text)}.diff-unified .diff-hunk-header-content,.diff-split .diff-hunk-header-content{color:var(--color-hunk-content-text);padding:4px 12px;font-style:italic}.diff-unified .diff-code-edit .diff-code-text .diff-code-edit-text,.diff-split .diff-code-edit .diff-code-text .diff-code-edit-text{background-color:var(--color-diff-edit-insert);border-radius:2px}.diff-unified .diff-code-delete .diff-code-text .diff-code-edit-text,.diff-split .diff-code-delete .diff-code-text .diff-code-edit-text{background-color:var(--color-diff-edit-delete);border-radius:2px}.diff-unified table,.diff-split table{width:100%;border-collapse:collapse;table-layout:fixed}.diff-unified td,.diff-split td{vertical-align:top}.diff-split .diff-split-side-new .diff-gutter{border-left:1px solid var(--color-split-divider)}.diff-unified .token.comment,.diff-unified .token.prolog,.diff-unified .token.doctype,.diff-unified .token.cdata,.diff-split .token.comment,.diff-split .token.prolog,.diff-split .token.doctype,.diff-split .token.cdata{color:var(--color-token-comment)}.diff-unified .token.punctuation,.diff-split .token.punctuation{color:var(--color-token-punctuation)}.diff-unified .token.property,.diff-unified .token.tag,.diff-unified .token.boolean,.diff-unified .token.number,.diff-unified .token.constant,.diff-unified .token.symbol,.diff-split .token.property,.diff-split .token.tag,.diff-split .token.boolean,.diff-split .token.number,.diff-split .token.constant,.diff-split .token.symbol{color:var(--color-token-property)}.diff-unified .token.selector,.diff-unified .token.attr-name,.diff-unified .token.string,.diff-unified .token.char,.diff-unified .token.builtin,.diff-split .token.selector,.diff-split .token.attr-name,.diff-split .token.string,.diff-split .token.char,.diff-split .token.builtin{color:var(--color-token-string)}.diff-unified .token.operator,.diff-unified .token.entity,.diff-unified .token.url,.diff-split .token.operator,.diff-split .token.entity,.diff-split .token.url{color:var(--color-token-operator)}.diff-unified .token.atrule,.diff-unified .token.attr-value,.diff-unified .token.keyword,.diff-split .token.atrule,.diff-split .token.attr-value,.diff-split .token.keyword{color:var(--color-token-keyword)}.diff-unified .token.function,.diff-unified .token.class-name,.diff-split .token.function,.diff-split .token.class-name{color:var(--color-token-function)}.diff-unified .token.regex,.diff-unified .token.important,.diff-unified .token.variable,.diff-split .token.regex,.diff-split .token.important,.diff-split .token.variable{color:var(--color-token-variable)}.diff-unified .token.string,.diff-split .token.string{color:var(--color-token-string)}.diff-unified .diff-gutter,.diff-split .diff-gutter{cursor:pointer;position:relative}.diff-gutter-add-comment{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:3px;background-color:var(--color-comment-btn-bg);color:#fff;font-size:12px;font-weight:700;line-height:1;position:absolute;left:2px;top:50%;transform:translateY(-50%)}.diff-comment-indicator{display:inline-block;width:6px;height:6px;border-radius:50%;background-color:var(--color-comment-indicator);position:absolute;left:4px;top:50%;transform:translateY(-50%)}.diff-widget{background-color:var(--color-widget-bg)}.diff-widget-content{padding:0}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:var(--color-scrollbar-track)}::-webkit-scrollbar-thumb{background:var(--color-scrollbar-thumb);border-radius:4px}::-webkit-scrollbar-thumb:hover{background:var(--color-scrollbar-thumb-hover)}.hover\:border-blue-400:hover{--tw-border-opacity:1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.hover\:border-green-400:hover{--tw-border-opacity:1;border-color:rgb(74 222 128 / var(--tw-border-opacity, 1))}.hover\:border-red-400:hover{--tw-border-opacity:1;border-color:rgb(248 113 113 / var(--tw-border-opacity, 1))}.hover\:bg-blue-200:hover{--tw-bg-opacity:1;background-color:rgb(191 219 254 / var(--tw-bg-opacity, 1))}.hover\:bg-green-200:hover{--tw-bg-opacity:1;background-color:rgb(187 247 208 / var(--tw-bg-opacity, 1))}.hover\:bg-red-200:hover{--tw-bg-opacity:1;background-color:rgb(254 202 202 / var(--tw-bg-opacity, 1))}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-red-700:hover{--tw-text-opacity:1;color:rgb(185 28 28 / var(--tw-text-opacity, 1))}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.focus\:border-accent:focus{border-color:var(--color-accent)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-accent:focus{--tw-ring-color:var(--color-accent)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.group:hover .group-hover\:text-text-primary{color:var(--color-text-primary)}.group\/comment:hover .group-hover\/comment\:opacity-100{opacity:1}.group:hover .group-hover\:opacity-40{opacity:.4}.dark\:border-blue-500\/30:is(.dark *){border-color:#3b82f64d}.dark\:border-gray-500\/30:is(.dark *){border-color:#6b72804d}.dark\:border-green-500\/30:is(.dark *){border-color:#22c55e4d}.dark\:border-orange-500\/30:is(.dark *){border-color:#f973164d}.dark\:border-purple-500\/30:is(.dark *){border-color:#a855f74d}.dark\:border-red-500\/30:is(.dark *){border-color:#ef44444d}.dark\:border-yellow-500\/30:is(.dark *){border-color:#eab3084d}.dark\:bg-blue-600\/20:is(.dark *){background-color:#2563eb33}.dark\:bg-gray-600\/20:is(.dark *){background-color:#4b556333}.dark\:bg-green-600\/20:is(.dark *){background-color:#16a34a33}.dark\:bg-orange-600\/20:is(.dark *){background-color:#ea580c33}.dark\:bg-purple-600\/20:is(.dark *){background-color:#9333ea33}.dark\:bg-red-600\/20:is(.dark *){background-color:#dc262633}.dark\:bg-yellow-600\/20:is(.dark *){background-color:#ca8a0433}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity:1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.dark\:text-orange-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity:1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.dark\:text-yellow-400:is(.dark *){--tw-text-opacity:1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.dark\:hover\:border-blue-500\/50:hover:is(.dark *){border-color:#3b82f680}.dark\:hover\:border-green-500\/50:hover:is(.dark *){border-color:#22c55e80}.dark\:hover\:border-red-500\/50:hover:is(.dark *){border-color:#ef444480}.dark\:hover\:bg-blue-600\/30:hover:is(.dark *){background-color:#2563eb4d}.dark\:hover\:bg-green-600\/30:hover:is(.dark *){background-color:#16a34a4d}.dark\:hover\:bg-red-600\/30:hover:is(.dark *){background-color:#dc26264d}.dark\:hover\:text-red-400:hover:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}