create-interview-cockpit 0.20.0 → 0.22.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.
@@ -0,0 +1,124 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
3
+ import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism";
4
+ import { X, GitBranch } from "lucide-react";
5
+ import { fetchGitDiffFile } from "../api";
6
+ import type { GitDiffContext } from "../types";
7
+
8
+ interface Props {
9
+ ctx: GitDiffContext;
10
+ filePath: string;
11
+ onClose: () => void;
12
+ }
13
+
14
+ type View = "patch" | "before" | "after";
15
+
16
+ // Lightweight viewer — fetches the same patch/blob the LLM would lazy-load via readFile.
17
+ export default function GitDiffViewerModal({ ctx, filePath, onClose }: Props) {
18
+ const [view, setView] = useState<View>("patch");
19
+ const [content, setContent] = useState<string>("");
20
+ const [loading, setLoading] = useState(false);
21
+ const [error, setError] = useState<string | null>(null);
22
+
23
+ useEffect(() => {
24
+ let cancelled = false;
25
+ setLoading(true);
26
+ setError(null);
27
+ fetchGitDiffFile({
28
+ base: ctx.baseRef,
29
+ head: ctx.headRef,
30
+ mode: ctx.mode,
31
+ path: filePath,
32
+ view,
33
+ })
34
+ .then((d) => {
35
+ if (cancelled) return;
36
+ setContent(d.content || "");
37
+ })
38
+ .catch((e) => {
39
+ if (cancelled) return;
40
+ setError(e?.message || "Failed to load diff");
41
+ })
42
+ .finally(() => !cancelled && setLoading(false));
43
+ return () => {
44
+ cancelled = true;
45
+ };
46
+ }, [ctx.baseRef, ctx.headRef, ctx.mode, filePath, view]);
47
+
48
+ const language =
49
+ view === "patch"
50
+ ? "diff"
51
+ : (filePath.split(".").pop() || "text").toLowerCase();
52
+
53
+ const headLabel = ctx.mode === "working-tree" ? "working tree" : ctx.headRef;
54
+
55
+ return (
56
+ <div
57
+ className="fixed inset-0 z-50 bg-black/70 flex items-center justify-center p-4"
58
+ onClick={onClose}
59
+ >
60
+ <div
61
+ className="bg-slate-900 border border-slate-700 rounded-lg w-full max-w-5xl max-h-[90vh] flex flex-col overflow-hidden"
62
+ onClick={(e) => e.stopPropagation()}
63
+ >
64
+ <div className="flex items-center gap-2 border-b border-slate-800 px-3 py-2">
65
+ <GitBranch className="w-4 h-4 text-cyan-400/70" />
66
+ <div className="flex-1 min-w-0">
67
+ <div className="text-sm text-slate-200 truncate">{filePath}</div>
68
+ <div className="text-[10px] text-slate-500 truncate">
69
+ {ctx.baseRef}{" "}
70
+ {ctx.mode === "two-dot"
71
+ ? ".."
72
+ : ctx.mode === "working-tree"
73
+ ? "→"
74
+ : "..."}{" "}
75
+ {headLabel}
76
+ </div>
77
+ </div>
78
+ <div className="flex bg-slate-800 rounded overflow-hidden text-xs">
79
+ {(["patch", "before", "after"] as View[]).map((v) => (
80
+ <button
81
+ key={v}
82
+ onClick={() => setView(v)}
83
+ className={`px-2 py-1 ${
84
+ view === v
85
+ ? "bg-cyan-500/20 text-cyan-300"
86
+ : "text-slate-400 hover:text-slate-200"
87
+ }`}
88
+ >
89
+ {v}
90
+ </button>
91
+ ))}
92
+ </div>
93
+ <button
94
+ onClick={onClose}
95
+ className="text-slate-500 hover:text-slate-200 p-1"
96
+ >
97
+ <X className="w-4 h-4" />
98
+ </button>
99
+ </div>
100
+ <div className="flex-1 min-h-0 overflow-auto">
101
+ {loading && (
102
+ <div className="p-4 text-xs text-slate-500">Loading…</div>
103
+ )}
104
+ {error && <div className="p-4 text-xs text-red-400">{error}</div>}
105
+ {!loading && !error && (
106
+ <SyntaxHighlighter
107
+ language={language}
108
+ style={oneDark}
109
+ customStyle={{
110
+ margin: 0,
111
+ background: "transparent",
112
+ fontSize: "12px",
113
+ padding: "12px",
114
+ }}
115
+ showLineNumbers={view !== "patch"}
116
+ >
117
+ {content || "(empty)"}
118
+ </SyntaxHighlighter>
119
+ )}
120
+ </div>
121
+ </div>
122
+ </div>
123
+ );
124
+ }
@@ -200,6 +200,10 @@ interface Store {
200
200
  toggleSidebar: () => void;
201
201
  fetchAvailableFiles: () => Promise<void>;
202
202
  updateCodeContext: (questionId: string, files: string[]) => Promise<void>;
203
+ updateGitDiffContext: (
204
+ questionId: string,
205
+ ctx: import("./types").GitDiffContext | null,
206
+ ) => Promise<void>;
203
207
  refreshCurrentQuestion: () => Promise<void>;
204
208
  uploadTopicFiles: (
205
209
  topicId: string,
@@ -830,6 +834,16 @@ export const useStore = create<Store>((set, get) => ({
830
834
  }));
831
835
  },
832
836
 
837
+ updateGitDiffContext: async (questionId, ctx) => {
838
+ // Send null explicitly so the server can clear the field; PATCH ignores undefined.
839
+ await api.updateQuestion(questionId, { gitDiffContext: ctx } as any);
840
+ set((s) => ({
841
+ currentQuestion: s.currentQuestion
842
+ ? { ...s.currentQuestion, gitDiffContext: ctx ?? undefined }
843
+ : null,
844
+ }));
845
+ },
846
+
833
847
  refreshCurrentQuestion: async () => {
834
848
  const { selectedQuestionId } = get();
835
849
  if (selectedQuestionId) {
@@ -151,6 +151,8 @@ export interface Question {
151
151
  title: string;
152
152
  systemContext: string;
153
153
  codeContextFiles: string[];
154
+ /** Optional git diff selection that the chat LLM can lazy-fetch via readFile. */
155
+ gitDiffContext?: GitDiffContext;
154
156
  contextFiles: ContextFile[];
155
157
  messages: Message[];
156
158
  annotations?: Annotation[];
@@ -161,3 +163,24 @@ export interface Question {
161
163
  linkedConversationIds?: string[];
162
164
  createdAt: string;
163
165
  }
166
+
167
+ export type GitDiffMode = "two-dot" | "three-dot" | "working-tree";
168
+
169
+ export interface GitDiffContext {
170
+ baseRef: string;
171
+ /** Branch / ref / tag. Empty when mode is working-tree. */
172
+ headRef: string;
173
+ mode: GitDiffMode;
174
+ selectedFiles: string[];
175
+ }
176
+
177
+ export type GitDiffStatus = "A" | "M" | "D" | "R" | "C" | "T" | "U" | "?";
178
+
179
+ export interface GitDiffFileEntry {
180
+ path: string;
181
+ oldPath?: string;
182
+ status: GitDiffStatus;
183
+ additions: number;
184
+ deletions: number;
185
+ binary: boolean;
186
+ }
@@ -1 +1 @@
1
- {"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.ts","./src/enterpriselocallab.ts","./src/githubactionslab.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/ghahistorypanel.tsx","./src/components/ghajobspanel.tsx","./src/components/githubactionslabmodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
1
+ {"root":["./src/app.tsx","./src/api.ts","./src/browsersecuritytemplates.ts","./src/enterpriselocallab.ts","./src/githubactionslab.ts","./src/infralab.ts","./src/main.tsx","./src/reactlab.ts","./src/store.ts","./src/types.ts","./src/vite-env.d.ts","./src/components/aisettingsmodal.tsx","./src/components/annotationdialog.tsx","./src/components/browsersecuritylabmodal.tsx","./src/components/canvaslabmodal.tsx","./src/components/chatmessage.tsx","./src/components/chatview.tsx","./src/components/codecontextpanel.tsx","./src/components/codelineannotationpopup.tsx","./src/components/coderunnermodal.tsx","./src/components/deploymentlabmodal.tsx","./src/components/docrefmodal.tsx","./src/components/fileattachments.tsx","./src/components/filepickermodal.tsx","./src/components/fileviewermodal.tsx","./src/components/ghahistorypanel.tsx","./src/components/ghajobspanel.tsx","./src/components/gitdiffpanel.tsx","./src/components/gitdiffviewermodal.tsx","./src/components/githubactionslabmodal.tsx","./src/components/infralabmodal.tsx","./src/components/labspanel.tsx","./src/components/linkedconvospicker.tsx","./src/components/markdownrenderer.tsx","./src/components/mermaiddiagram.tsx","./src/components/notesmodal.tsx","./src/components/plotembed.tsx","./src/components/sidebar.tsx","./src/components/textannotator.tsx","./src/components/vizcraftembed.tsx","./src/components/workspaceswitcher.tsx"],"version":"5.9.3"}
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.18.0"
2
+ "version": "0.20.0"
3
3
  }