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.
- package/package.json +1 -1
- package/template/client/src/api.ts +67 -0
- package/template/client/src/components/ChatView.tsx +2 -0
- package/template/client/src/components/CodeContextPanel.tsx +28 -1
- package/template/client/src/components/FileViewerModal.tsx +1 -0
- package/template/client/src/components/GitDiffPanel.tsx +403 -0
- package/template/client/src/components/GitDiffViewerModal.tsx +124 -0
- package/template/client/src/store.ts +14 -0
- package/template/client/src/types.ts +23 -0
- package/template/client/tsconfig.tsbuildinfo +1 -1
- package/template/cockpit.json +1 -1
- package/template/server/src/index.ts +624 -0
- package/template/server/src/storage.ts +12 -0
|
@@ -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"}
|
package/template/cockpit.json
CHANGED