diffprism 0.43.0 → 0.45.0

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.
@@ -1,18 +1,12 @@
1
1
  import {
2
- createGitHubClient,
3
- fetchPullRequest,
4
- fetchPullRequestDiff,
5
2
  isPrRef,
6
- normalizePr,
7
- parsePrRef,
8
- resolveGitHubToken,
9
- submitGitHubReview
10
- } from "./chunk-6J6PSBL2.js";
3
+ parsePrRef
4
+ } from "./chunk-24B33UN6.js";
11
5
  import {
12
6
  ensureServer,
13
7
  isServerAlive,
14
8
  submitReviewToServer
15
- } from "./chunk-VR2C6WTQ.js";
9
+ } from "./chunk-RICVPPTE.js";
16
10
  import {
17
11
  getDiff
18
12
  } from "./chunk-QGWYCEJN.js";
@@ -27,6 +21,24 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
27
21
  import { z } from "zod";
28
22
  var lastGlobalSessionId = null;
29
23
  var lastGlobalServerInfo = null;
24
+ async function resolveSessionId(explicitId, serverInfo) {
25
+ if (explicitId) return explicitId;
26
+ if (lastGlobalSessionId) return lastGlobalSessionId;
27
+ try {
28
+ const response = await fetch(
29
+ `http://localhost:${serverInfo.httpPort}/api/reviews`
30
+ );
31
+ if (response.ok) {
32
+ const data = await response.json();
33
+ if (data.sessions.length > 0) {
34
+ const sorted = [...data.sessions].sort((a, b) => b.createdAt - a.createdAt);
35
+ return sorted[0].id;
36
+ }
37
+ }
38
+ } catch {
39
+ }
40
+ return null;
41
+ }
30
42
  async function handleLocalReview(diffRef, options) {
31
43
  const serverInfo = await ensureServer({ silent: true });
32
44
  const { result, sessionId } = await submitReviewToServer(
@@ -67,83 +79,63 @@ async function handleLocalReview(diffRef, options) {
67
79
  };
68
80
  }
69
81
  async function handlePrReview(pr, options) {
70
- const token = resolveGitHubToken();
71
82
  const { owner, repo, number } = parsePrRef(pr);
72
- const client = createGitHubClient(token);
73
- const [prMetadata, rawDiff] = await Promise.all([
74
- fetchPullRequest(client, owner, repo, number),
75
- fetchPullRequestDiff(client, owner, repo, number)
76
- ]);
77
- if (!rawDiff.trim()) {
78
- return {
79
- mcpResult: {
80
- content: [{
81
- type: "text",
82
- text: JSON.stringify({
83
- decision: "approved",
84
- comments: [],
85
- summary: "PR has no changes to review."
86
- }, null, 2)
87
- }]
88
- },
89
- sessionId: "",
90
- serverInfo: { httpPort: 0, wsPort: 0, pid: 0, startedAt: 0 }
91
- };
92
- }
93
- const { payload } = normalizePr(rawDiff, prMetadata, {
94
- title: options.title,
95
- reasoning: options.reasoning
96
- });
97
83
  const serverInfo = await ensureServer({ silent: true });
98
- const { result, sessionId } = await submitReviewToServer(
99
- serverInfo,
100
- `PR #${number}`,
84
+ const response = await fetch(
85
+ `http://localhost:${serverInfo.httpPort}/api/pr/open`,
101
86
  {
102
- injectedPayload: payload,
103
- projectPath: `github:${owner}/${repo}`,
104
- diffRef: `PR #${number}`,
105
- timeoutMs: options.timeoutMs ?? 0
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/json" },
89
+ body: JSON.stringify({ prUrl: pr })
106
90
  }
107
91
  );
108
- if (!result) {
92
+ const data = await response.json();
93
+ if (!response.ok || !data.sessionId) {
109
94
  return {
110
95
  mcpResult: {
111
- content: [{
112
- type: "text",
113
- text: JSON.stringify({
114
- status: "session_created",
115
- sessionId,
116
- pr: `${owner}/${repo}#${number}`,
117
- message: "Review session opened in DiffPrism dashboard. Use get_review_result to check for a decision."
118
- }, null, 2)
119
- }]
96
+ content: [{ type: "text", text: `Error: ${data.error ?? "Failed to open PR"}` }],
97
+ isError: true
120
98
  },
121
- sessionId,
99
+ sessionId: "",
122
100
  serverInfo
123
101
  };
124
102
  }
125
- if ((options.post_to_github || result.postToGithub) && result.decision !== "dismissed") {
126
- const posted = await submitGitHubReview(client, owner, repo, number, result);
127
- if (posted) {
128
- return {
129
- mcpResult: {
130
- content: [{
131
- type: "text",
132
- text: JSON.stringify({
133
- ...result,
134
- githubReviewId: posted.reviewId,
135
- githubReviewUrl: `${prMetadata.url}#pullrequestreview-${posted.reviewId}`
136
- }, null, 2)
137
- }]
138
- },
139
- sessionId,
140
- serverInfo
141
- };
103
+ const sessionId = data.sessionId;
104
+ if (options.timeoutMs && options.timeoutMs > 0) {
105
+ const start = Date.now();
106
+ while (Date.now() - start < options.timeoutMs) {
107
+ const resultResponse = await fetch(
108
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/result`
109
+ );
110
+ if (resultResponse.ok) {
111
+ const resultData = await resultResponse.json();
112
+ if (resultData.result) {
113
+ return {
114
+ mcpResult: {
115
+ content: [{ type: "text", text: JSON.stringify(resultData.result, null, 2) }]
116
+ },
117
+ sessionId,
118
+ serverInfo
119
+ };
120
+ }
121
+ }
122
+ await new Promise((resolve) => setTimeout(resolve, 2e3));
142
123
  }
143
124
  }
144
125
  return {
145
126
  mcpResult: {
146
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
127
+ content: [{
128
+ type: "text",
129
+ text: JSON.stringify({
130
+ status: "session_created",
131
+ sessionId,
132
+ pr: `${owner}/${repo}#${number}`,
133
+ fileCount: data.fileCount,
134
+ localRepoConnected: !!data.localRepoPath,
135
+ localRepoPath: data.localRepoPath,
136
+ message: "PR review session opened in DiffPrism. Use get_pr_context, get_file_diff, get_file_context to explore the changes. Use add_review_comment to post findings."
137
+ }, null, 2)
138
+ }]
147
139
  },
148
140
  sessionId,
149
141
  serverInfo
@@ -152,7 +144,7 @@ async function handlePrReview(pr, options) {
152
144
  async function startMcpServer() {
153
145
  const server = new McpServer({
154
146
  name: "diffprism",
155
- version: true ? "0.43.0" : "0.0.0-dev"
147
+ version: true ? "0.45.0" : "0.0.0-dev"
156
148
  });
157
149
  server.tool(
158
150
  "open_review",
@@ -743,6 +735,372 @@ async function startMcpServer() {
743
735
  }
744
736
  }
745
737
  );
738
+ server.tool(
739
+ "get_pr_context",
740
+ "Get a high-level overview of the active PR review session. Returns PR metadata (title, author, branches, URL), review briefing summary, file list with stats, and local repo path. Use this to orient yourself before diving into specific files.",
741
+ {
742
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
743
+ },
744
+ async ({ session_id }) => {
745
+ try {
746
+ const serverInfo = await isServerAlive();
747
+ if (!serverInfo) {
748
+ return {
749
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
750
+ isError: true
751
+ };
752
+ }
753
+ const sessionId = await resolveSessionId(session_id, serverInfo);
754
+ if (!sessionId) {
755
+ return {
756
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
757
+ isError: true
758
+ };
759
+ }
760
+ const response = await fetch(
761
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
762
+ );
763
+ if (!response.ok) {
764
+ return {
765
+ content: [{ type: "text", text: `Session not found: ${sessionId}` }],
766
+ isError: true
767
+ };
768
+ }
769
+ const data = await response.json();
770
+ const { payload, projectPath } = data;
771
+ const result = {
772
+ sessionId,
773
+ projectPath,
774
+ localRepoConnected: !projectPath.startsWith("github:"),
775
+ pr: payload.metadata.githubPr ?? null,
776
+ title: payload.metadata.title,
777
+ description: payload.metadata.description,
778
+ briefingSummary: payload.briefing.summary,
779
+ triage: payload.briefing.triage,
780
+ files: payload.diffSet.files.map((f) => ({
781
+ path: f.path,
782
+ status: f.status,
783
+ additions: f.additions,
784
+ deletions: f.deletions,
785
+ language: f.language
786
+ })),
787
+ totalFiles: payload.diffSet.files.length
788
+ };
789
+ return {
790
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
791
+ };
792
+ } catch (err) {
793
+ const message = err instanceof Error ? err.message : String(err);
794
+ return {
795
+ content: [{ type: "text", text: `Error: ${message}` }],
796
+ isError: true
797
+ };
798
+ }
799
+ }
800
+ );
801
+ server.tool(
802
+ "get_file_diff",
803
+ "Get the diff hunks for a specific file in the active review session. Returns the file's changes (additions, deletions, hunks with line-level detail) and its briefing categorization. Use this to focus on one file at a time.",
804
+ {
805
+ file: z.string().describe("File path within the diff (e.g., 'src/index.ts')"),
806
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
807
+ },
808
+ async ({ file, session_id }) => {
809
+ try {
810
+ const serverInfo = await isServerAlive();
811
+ if (!serverInfo) {
812
+ return {
813
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
814
+ isError: true
815
+ };
816
+ }
817
+ const sessionId = await resolveSessionId(session_id, serverInfo);
818
+ if (!sessionId) {
819
+ return {
820
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
821
+ isError: true
822
+ };
823
+ }
824
+ const response = await fetch(
825
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
826
+ );
827
+ if (!response.ok) {
828
+ return {
829
+ content: [{ type: "text", text: `Session not found: ${sessionId}` }],
830
+ isError: true
831
+ };
832
+ }
833
+ const data = await response.json();
834
+ const diffFile = data.payload.diffSet.files.find((f) => f.path === file);
835
+ if (!diffFile) {
836
+ const available = data.payload.diffSet.files.map((f) => f.path);
837
+ return {
838
+ content: [{ type: "text", text: `File not found in diff: "${file}". Available files:
839
+ ${available.join("\n")}` }],
840
+ isError: true
841
+ };
842
+ }
843
+ const { triage } = data.payload.briefing;
844
+ let category = "mechanical";
845
+ if (triage.critical.some((c) => c.file === file)) category = "critical";
846
+ else if (triage.notable.some((n) => n.file === file)) category = "notable";
847
+ return {
848
+ content: [{ type: "text", text: JSON.stringify({
849
+ path: diffFile.path,
850
+ oldPath: diffFile.oldPath,
851
+ status: diffFile.status,
852
+ language: diffFile.language,
853
+ additions: diffFile.additions,
854
+ deletions: diffFile.deletions,
855
+ triageCategory: category,
856
+ hunks: diffFile.hunks
857
+ }, null, 2) }]
858
+ };
859
+ } catch (err) {
860
+ const message = err instanceof Error ? err.message : String(err);
861
+ return {
862
+ content: [{ type: "text", text: `Error: ${message}` }],
863
+ isError: true
864
+ };
865
+ }
866
+ }
867
+ );
868
+ server.tool(
869
+ "get_file_context",
870
+ "Get the full content of a file from the local repository. Uses `git show` to read the file at the PR's head branch without switching branches. Requires the review session to be connected to a local repo (server must be running from within the repo clone).",
871
+ {
872
+ file: z.string().describe("File path relative to repo root (e.g., 'src/index.ts')"),
873
+ ref: z.string().optional().describe("Git ref to read from (e.g., 'origin/main', 'HEAD'). Defaults to the PR's head branch if available, otherwise HEAD."),
874
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
875
+ },
876
+ async ({ file, ref, session_id }) => {
877
+ try {
878
+ const serverInfo = await isServerAlive();
879
+ if (!serverInfo) {
880
+ return {
881
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
882
+ isError: true
883
+ };
884
+ }
885
+ const sessionId = await resolveSessionId(session_id, serverInfo);
886
+ if (!sessionId) {
887
+ return {
888
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
889
+ isError: true
890
+ };
891
+ }
892
+ const response = await fetch(
893
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/payload`
894
+ );
895
+ if (!response.ok) {
896
+ return {
897
+ content: [{ type: "text", text: `Session not found: ${sessionId}` }],
898
+ isError: true
899
+ };
900
+ }
901
+ const data = await response.json();
902
+ if (data.projectPath.startsWith("github:")) {
903
+ return {
904
+ content: [{ type: "text", text: "No local repo connected. Run the server from within a local clone of the repository to enable file context." }],
905
+ isError: true
906
+ };
907
+ }
908
+ const gitRef = ref ?? (data.payload.metadata.githubPr?.headBranch ? `origin/${data.payload.metadata.githubPr.headBranch}` : "HEAD");
909
+ const { execSync } = await import("child_process");
910
+ let content;
911
+ try {
912
+ content = execSync(`git show ${gitRef}:${file}`, {
913
+ cwd: data.projectPath,
914
+ encoding: "utf-8",
915
+ stdio: ["pipe", "pipe", "pipe"],
916
+ maxBuffer: 10 * 1024 * 1024
917
+ // 10MB
918
+ });
919
+ } catch {
920
+ const fs = await import("fs");
921
+ const path = await import("path");
922
+ const filePath = path.join(data.projectPath, file);
923
+ try {
924
+ content = fs.readFileSync(filePath, "utf-8");
925
+ } catch {
926
+ return {
927
+ content: [{ type: "text", text: `File not found: "${file}" (tried git show ${gitRef}:${file} and working tree)` }],
928
+ isError: true
929
+ };
930
+ }
931
+ }
932
+ return {
933
+ content: [{ type: "text", text: JSON.stringify({
934
+ file,
935
+ ref: gitRef,
936
+ projectPath: data.projectPath,
937
+ content,
938
+ lineCount: content.split("\n").length
939
+ }, null, 2) }]
940
+ };
941
+ } catch (err) {
942
+ const message = err instanceof Error ? err.message : String(err);
943
+ return {
944
+ content: [{ type: "text", text: `Error: ${message}` }],
945
+ isError: true
946
+ };
947
+ }
948
+ }
949
+ );
950
+ server.tool(
951
+ "add_review_comment",
952
+ "Post a review comment to the active session. The comment appears in the DiffPrism browser UI in real-time as an inline annotation on the diff. Use this to leave findings, suggestions, or questions about specific lines of code.",
953
+ {
954
+ file: z.string().describe("File path within the diff"),
955
+ line: z.number().describe("Line number to comment on"),
956
+ body: z.string().describe("The comment text"),
957
+ type: z.enum(["comment", "suggestion", "concern"]).optional().describe("Type of comment (default: 'comment')"),
958
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
959
+ },
960
+ async ({ file, line, body, type, session_id }) => {
961
+ try {
962
+ const serverInfo = await isServerAlive();
963
+ if (!serverInfo) {
964
+ return {
965
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
966
+ isError: true
967
+ };
968
+ }
969
+ const sessionId = await resolveSessionId(session_id, serverInfo);
970
+ if (!sessionId) {
971
+ return {
972
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
973
+ isError: true
974
+ };
975
+ }
976
+ const annotationType = type === "concern" ? "warning" : type === "suggestion" ? "suggestion" : "finding";
977
+ const response = await fetch(
978
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`,
979
+ {
980
+ method: "POST",
981
+ headers: { "Content-Type": "application/json" },
982
+ body: JSON.stringify({
983
+ file,
984
+ line,
985
+ body,
986
+ type: annotationType,
987
+ confidence: 1,
988
+ category: "other",
989
+ source: {
990
+ agent: "ai-reviewer",
991
+ tool: "add_review_comment"
992
+ }
993
+ })
994
+ }
995
+ );
996
+ if (!response.ok) {
997
+ const errorData = await response.json().catch(() => ({}));
998
+ return {
999
+ content: [{ type: "text", text: `Error: ${errorData.error ?? `Server returned ${response.status}`}` }],
1000
+ isError: true
1001
+ };
1002
+ }
1003
+ const data = await response.json();
1004
+ return {
1005
+ content: [{ type: "text", text: JSON.stringify({ annotationId: data.annotationId, sessionId }, null, 2) }]
1006
+ };
1007
+ } catch (err) {
1008
+ const message = err instanceof Error ? err.message : String(err);
1009
+ return {
1010
+ content: [{ type: "text", text: `Error: ${message}` }],
1011
+ isError: true
1012
+ };
1013
+ }
1014
+ }
1015
+ );
1016
+ server.tool(
1017
+ "get_review_comments",
1018
+ "Get all comments and annotations on the active review session. Returns findings from agents and inline comments from human reviewers. Use this to see what has already been noted before adding your own comments.",
1019
+ {
1020
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
1021
+ },
1022
+ async ({ session_id }) => {
1023
+ try {
1024
+ const serverInfo = await isServerAlive();
1025
+ if (!serverInfo) {
1026
+ return {
1027
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
1028
+ isError: true
1029
+ };
1030
+ }
1031
+ const sessionId = await resolveSessionId(session_id, serverInfo);
1032
+ if (!sessionId) {
1033
+ return {
1034
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
1035
+ isError: true
1036
+ };
1037
+ }
1038
+ const response = await fetch(
1039
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/annotations`
1040
+ );
1041
+ if (!response.ok) {
1042
+ return {
1043
+ content: [{ type: "text", text: `Session not found: ${sessionId}` }],
1044
+ isError: true
1045
+ };
1046
+ }
1047
+ const data = await response.json();
1048
+ return {
1049
+ content: [{ type: "text", text: JSON.stringify({ sessionId, annotations: data.annotations }, null, 2) }]
1050
+ };
1051
+ } catch (err) {
1052
+ const message = err instanceof Error ? err.message : String(err);
1053
+ return {
1054
+ content: [{ type: "text", text: `Error: ${message}` }],
1055
+ isError: true
1056
+ };
1057
+ }
1058
+ }
1059
+ );
1060
+ server.tool(
1061
+ "get_user_focus",
1062
+ "Get what the user is currently looking at in the DiffPrism review UI. Returns the file they have selected and any line range they are focused on. Use this to provide context-aware help \u2014 answer questions about the code the user is actively reviewing.",
1063
+ {
1064
+ session_id: z.string().optional().describe("Review session ID. If omitted, uses the most recently created session.")
1065
+ },
1066
+ async ({ session_id }) => {
1067
+ try {
1068
+ const serverInfo = await isServerAlive();
1069
+ if (!serverInfo) {
1070
+ return {
1071
+ content: [{ type: "text", text: "No global server running. Start one with `diffprism server`." }],
1072
+ isError: true
1073
+ };
1074
+ }
1075
+ const sessionId = await resolveSessionId(session_id, serverInfo);
1076
+ if (!sessionId) {
1077
+ return {
1078
+ content: [{ type: "text", text: "No review session found. Open a PR review first with `diffprism review <PR URL>`." }],
1079
+ isError: true
1080
+ };
1081
+ }
1082
+ const response = await fetch(
1083
+ `http://localhost:${serverInfo.httpPort}/api/reviews/${sessionId}/focus`
1084
+ );
1085
+ if (!response.ok) {
1086
+ return {
1087
+ content: [{ type: "text", text: `Session not found: ${sessionId}` }],
1088
+ isError: true
1089
+ };
1090
+ }
1091
+ const data = await response.json();
1092
+ return {
1093
+ content: [{ type: "text", text: JSON.stringify({ sessionId, ...data }, null, 2) }]
1094
+ };
1095
+ } catch (err) {
1096
+ const message = err instanceof Error ? err.message : String(err);
1097
+ return {
1098
+ content: [{ type: "text", text: `Error: ${message}` }],
1099
+ isError: true
1100
+ };
1101
+ }
1102
+ }
1103
+ );
746
1104
  const transport = new StdioServerTransport();
747
1105
  await server.connect(transport);
748
1106
  }
@@ -0,0 +1,23 @@
1
+ import {
2
+ createGitHubClient,
3
+ fetchPullRequest,
4
+ fetchPullRequestDiff,
5
+ isPrRef,
6
+ normalizePr,
7
+ parsePrRef,
8
+ resolveGitHubToken,
9
+ submitGitHubReview
10
+ } from "./chunk-24B33UN6.js";
11
+ import "./chunk-QGWYCEJN.js";
12
+ import "./chunk-DHCVZGHE.js";
13
+ import "./chunk-JSBRDJBE.js";
14
+ export {
15
+ createGitHubClient,
16
+ fetchPullRequest,
17
+ fetchPullRequestDiff,
18
+ isPrRef,
19
+ normalizePr,
20
+ parsePrRef,
21
+ resolveGitHubToken,
22
+ submitGitHubReview
23
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffprism",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
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}.\!container{width:100%!important}.container{width:100%}@media(min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media(min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media(min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media(min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media(min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.left-3{left:.75rem}.right-0{right:0}.right-1\.5{right:.375rem}.right-2{right:.5rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-full{top:100%}.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-0\.5{margin-bottom:.125rem}.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}.mb-5{margin-bottom:1.25rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-1\.5{margin-right:.375rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-full{height:100%}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-\[280px\]{max-height:280px}.max-h-\[40\%\]{max-height:40%}.max-h-\[80vh\]{max-height:80vh}.min-h-0{min-height:0px}.min-h-\[20px\]{min-height:20px}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-52{width:13rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-80{width:20rem}.w-\[260px\]{width:260px}.w-\[280px\]{width:280px}.w-full{width:100%}.w-px{width:1px}.min-w-0{min-width:0px}.max-w-\[180px\]{max-width:180px}.max-w-md{max-width:28rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.-translate-y-1\/2{--tw-translate-y:-50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@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-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.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-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * 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))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-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-border{border-color:var(--color-border)}.border-transparent{border-color:transparent}.border-l-accent{border-left-color:var(--color-accent)}.border-l-transparent{border-left-color:transparent}.border-l-warning{border-left-color:var(--color-warning)}.border-r-border{border-right-color:var(--color-border)}.border-t-accent{border-top-color:var(--color-accent)}.bg-accent{background-color:var(--color-accent)}.bg-background{background-color:var(--color-background)}.bg-black\/50{background-color:#00000080}.bg-border{background-color:var(--color-border)}.bg-info{background-color:var(--color-info)}.bg-success{background-color:var(--color-success)}.bg-surface{background-color:var(--color-surface)}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.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}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-3{padding-bottom:.75rem}.pl-6{padding-left:1.5rem}.pl-9{padding-left:2.25rem}.pr-6{padding-right:1.5rem}.pr-7{padding-right:1.75rem}.pt-3{padding-top:.75rem}.pt-4{padding-top:1rem}.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-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.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}.leading-snug{line-height:1.375}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-accent{color:var(--color-accent)}.text-danger{color:var(--color-danger)}.text-info{color:var(--color-info)}.text-neutral{color:var(--color-neutral)}.text-perf{color:var(--color-perf)}.text-success{color:var(--color-success)}.text-text-primary{color:var(--color-text-primary)}.text-text-secondary{color:var(--color-text-secondary)}.text-warning{color:var(--color-warning)}.text-white{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.accent-accent{accent-color:var(--color-accent)}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.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: #e5e9f2;--color-surface: #d8dde9;--color-border: #b3bbd0;--color-text-primary: #141828;--color-text-secondary: #434e65;--color-accent: #4f3fba;--color-diff-bg: #e5e9f2;--color-diff-text: #141828;--color-gutter-bg: #d8dde9;--color-gutter-text: #434e65;--color-gutter-border: #b3bbd0;--color-diff-insert-bg: rgba(30, 160, 120, .15);--color-diff-insert-gutter-bg: rgba(30, 160, 120, .22);--color-diff-insert-gutter-text: #14704f;--color-diff-delete-bg: rgba(210, 75, 65, .15);--color-diff-delete-gutter-bg: rgba(210, 75, 65, .22);--color-diff-delete-gutter-text: #b02e26;--color-hunk-bg: rgba(90, 72, 201, .1);--color-hunk-border: #b3bbd0;--color-hunk-gutter-bg: rgba(79, 63, 186, .15);--color-hunk-gutter-text: #4f3fba;--color-hunk-content-text: #434e65;--color-diff-edit-insert: rgba(30, 160, 120, .42);--color-diff-edit-delete: rgba(210, 75, 65, .42);--color-split-divider: #b3bbd0;--color-widget-bg: #d8dde9;--color-comment-btn-bg: #167a5c;--color-comment-indicator: #4f3fba;--color-annotation-indicator: #c07820;--color-scrollbar-track: #e5e9f2;--color-scrollbar-thumb: #b3bbd0;--color-scrollbar-thumb-hover: #98a2b8;--color-token-comment: #6b7a8f;--color-token-punctuation: #1a1f2e;--color-token-property: #0b6e82;--color-token-string: #0e7a5c;--color-token-operator: #c93c33;--color-token-keyword: #6e45c9;--color-token-function: #2870c9;--color-token-variable: #b06e18;--color-success: #0e7a5c;--color-danger: #c93c33;--color-warning: #b06e18;--color-info: #2870c9;--color-neutral: #6b7a8f;--color-perf: #c07820;--color-added: rgba(30, 160, 120, .4);--color-deleted: rgba(210, 75, 65, .4)}.dark{--color-background: #0b0f1a;--color-surface: #131829;--color-border: #252d3f;--color-text-primary: #dfe5f1;--color-text-secondary: #7585a3;--color-accent: #7c6bf0;--color-diff-bg: #0b0f1a;--color-diff-text: #dfe5f1;--color-gutter-bg: #111623;--color-gutter-text: #7585a3;--color-gutter-border: #252d3f;--color-diff-insert-bg: rgba(45, 185, 140, .12);--color-diff-insert-gutter-bg: rgba(45, 185, 140, .18);--color-diff-insert-gutter-text: #5ee0b2;--color-diff-delete-bg: rgba(235, 100, 90, .12);--color-diff-delete-gutter-bg: rgba(235, 100, 90, .18);--color-diff-delete-gutter-text: #f07068;--color-hunk-bg: rgba(124, 107, 240, .08);--color-hunk-border: #252d3f;--color-hunk-gutter-bg: rgba(124, 107, 240, .12);--color-hunk-gutter-text: #9688f0;--color-hunk-content-text: #7585a3;--color-diff-edit-insert: rgba(45, 185, 140, .35);--color-diff-edit-delete: rgba(235, 100, 90, .35);--color-split-divider: #252d3f;--color-widget-bg: #131829;--color-comment-btn-bg: #2db98c;--color-comment-indicator: #7c6bf0;--color-annotation-indicator: #e0a050;--color-scrollbar-track: #0b0f1a;--color-scrollbar-thumb: #252d3f;--color-scrollbar-thumb-hover: #3a4560;--color-token-comment: #5f6f8a;--color-token-punctuation: #bec8db;--color-token-property: #56c8e0;--color-token-string: #42c9a2;--color-token-operator: #f07a6e;--color-token-keyword: #b08df0;--color-token-function: #6aabf7;--color-token-variable: #e0a050;--color-success: #42c9a2;--color-danger: #f07a6e;--color-warning: #e0a050;--color-info: #6aabf7;--color-neutral: #5f6f8a;--color-perf: #e89040;--color-added: rgba(45, 185, 140, .4);--color-deleted: rgba(235, 100, 90, .4)}.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-hunk-focused{outline:2px solid var(--color-accent);outline-offset:-2px}.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-annotation-indicator{display:inline-block;width:6px;height:6px;border-radius:50%;background-color:var(--color-annotation-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)}.placeholder\:text-text-secondary::-moz-placeholder{color:var(--color-text-secondary)}.placeholder\:text-text-secondary::placeholder{color:var(--color-text-secondary)}.hover\:text-accent:hover{color:var(--color-accent)}.hover\:text-danger:hover{color:var(--color-danger)}.hover\:text-text-primary:hover{color:var(--color-text-primary)}.hover\:underline:hover{text-decoration-line:underline}.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}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:text-text-primary{color:var(--color-text-primary)}.group\/annotation:hover .group-hover\/annotation\:opacity-100,.group\/comment:hover .group-hover\/comment\:opacity-100,.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:opacity-40{opacity:.4}