create-interview-cockpit 0.4.0 → 0.6.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/package-lock.json +753 -1
- package/template/client/package.json +4 -0
- package/template/client/src/App.tsx +20 -0
- package/template/client/src/api.ts +455 -3
- package/template/client/src/components/AiSettingsModal.tsx +855 -248
- package/template/client/src/components/AnnotationDialog.tsx +3 -9
- package/template/client/src/components/ChatMessage.tsx +132 -27
- package/template/client/src/components/ChatView.tsx +365 -123
- package/template/client/src/components/CodeContextPanel.tsx +714 -0
- package/template/client/src/components/CodeLineAnnotationPopup.tsx +179 -0
- package/template/client/src/components/CodeRunnerModal.tsx +3030 -0
- package/template/client/src/components/DocRefModal.tsx +551 -0
- package/template/client/src/components/FileAttachments.tsx +128 -12
- package/template/client/src/components/FilePickerModal.tsx +181 -0
- package/template/client/src/components/FileViewerModal.tsx +406 -28
- package/template/client/src/components/InfraLabModal.tsx +1706 -0
- package/template/client/src/components/LinkedConvosPicker.tsx +128 -0
- package/template/client/src/components/MarkdownRenderer.tsx +219 -2
- package/template/client/src/components/NotesModal.tsx +977 -0
- package/template/client/src/components/PlotEmbed.tsx +173 -0
- package/template/client/src/components/Sidebar.tsx +397 -127
- package/template/client/src/components/TextAnnotator.tsx +8 -15
- package/template/client/src/components/VizCraftEmbed.tsx +412 -25
- package/template/client/src/components/WorkspaceSwitcher.tsx +4 -0
- package/template/client/src/infraLab.ts +124 -0
- package/template/client/src/reactLab.ts +477 -0
- package/template/client/src/store.ts +416 -2
- package/template/client/src/types.ts +41 -1
- package/template/client/tsconfig.tsbuildinfo +1 -1
- package/template/cockpit.json +1 -1
- package/template/package.json +1 -1
- package/template/server/src/google-drive.ts +144 -2
- package/template/server/src/index.ts +1890 -188
- package/template/server/src/infra-runner.ts +1104 -0
- package/template/server/src/storage.ts +274 -3
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from "react";
|
|
2
|
+
import { X, Code2 } from "lucide-react";
|
|
3
|
+
import MarkdownRenderer from "./MarkdownRenderer";
|
|
4
|
+
|
|
5
|
+
export interface CodeAnnotation {
|
|
6
|
+
id: string;
|
|
7
|
+
lineNumber: number;
|
|
8
|
+
lineContent: string;
|
|
9
|
+
prompt: string;
|
|
10
|
+
response: string;
|
|
11
|
+
filePath: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
annotation: CodeAnnotation;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
initialPos?: { x: number; y: number };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const DEFAULT_W = 400;
|
|
21
|
+
const DEFAULT_H = 300;
|
|
22
|
+
const MIN_W = 280;
|
|
23
|
+
const MIN_H = 160;
|
|
24
|
+
|
|
25
|
+
export default function CodeLineAnnotationPopup({
|
|
26
|
+
annotation,
|
|
27
|
+
onClose,
|
|
28
|
+
initialPos,
|
|
29
|
+
}: Props) {
|
|
30
|
+
const [pos, setPos] = useState(() => ({
|
|
31
|
+
x:
|
|
32
|
+
initialPos?.x ??
|
|
33
|
+
Math.min(window.innerWidth - DEFAULT_W - 16, window.innerWidth * 0.6),
|
|
34
|
+
y: initialPos?.y ?? Math.max(8, window.innerHeight * 0.3),
|
|
35
|
+
}));
|
|
36
|
+
const [size, setSize] = useState({ w: DEFAULT_W, h: DEFAULT_H });
|
|
37
|
+
|
|
38
|
+
const dragStart = useRef<{
|
|
39
|
+
mx: number;
|
|
40
|
+
my: number;
|
|
41
|
+
ox: number;
|
|
42
|
+
oy: number;
|
|
43
|
+
} | null>(null);
|
|
44
|
+
const resizeStart = useRef<{
|
|
45
|
+
mx: number;
|
|
46
|
+
my: number;
|
|
47
|
+
ox: number;
|
|
48
|
+
oy: number;
|
|
49
|
+
ow: number;
|
|
50
|
+
oh: number;
|
|
51
|
+
} | null>(null);
|
|
52
|
+
const resizeDir = useRef<"e" | "s" | "se" | null>(null);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const onMove = (e: MouseEvent) => {
|
|
56
|
+
if (dragStart.current) {
|
|
57
|
+
setPos({
|
|
58
|
+
x: Math.max(
|
|
59
|
+
0,
|
|
60
|
+
dragStart.current.ox + (e.clientX - dragStart.current.mx),
|
|
61
|
+
),
|
|
62
|
+
y: Math.max(
|
|
63
|
+
0,
|
|
64
|
+
dragStart.current.oy + (e.clientY - dragStart.current.my),
|
|
65
|
+
),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (resizeStart.current && resizeDir.current) {
|
|
69
|
+
const dx = e.clientX - resizeStart.current.mx;
|
|
70
|
+
const dy = e.clientY - resizeStart.current.my;
|
|
71
|
+
setSize((prev) => ({
|
|
72
|
+
w: resizeDir.current?.includes("e")
|
|
73
|
+
? Math.max(MIN_W, resizeStart.current!.ow + dx)
|
|
74
|
+
: prev.w,
|
|
75
|
+
h: resizeDir.current?.includes("s")
|
|
76
|
+
? Math.max(MIN_H, resizeStart.current!.oh + dy)
|
|
77
|
+
: prev.h,
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const onUp = () => {
|
|
82
|
+
dragStart.current = null;
|
|
83
|
+
resizeStart.current = null;
|
|
84
|
+
resizeDir.current = null;
|
|
85
|
+
};
|
|
86
|
+
document.addEventListener("mousemove", onMove);
|
|
87
|
+
document.addEventListener("mouseup", onUp);
|
|
88
|
+
return () => {
|
|
89
|
+
document.removeEventListener("mousemove", onMove);
|
|
90
|
+
document.removeEventListener("mouseup", onUp);
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const onTitleMouseDown = useCallback(
|
|
95
|
+
(e: React.MouseEvent) => {
|
|
96
|
+
e.preventDefault();
|
|
97
|
+
dragStart.current = {
|
|
98
|
+
mx: e.clientX,
|
|
99
|
+
my: e.clientY,
|
|
100
|
+
ox: pos.x,
|
|
101
|
+
oy: pos.y,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
[pos],
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const startResize = (dir: "e" | "s" | "se") => (e: React.MouseEvent) => {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
resizeDir.current = dir;
|
|
111
|
+
resizeStart.current = {
|
|
112
|
+
mx: e.clientX,
|
|
113
|
+
my: e.clientY,
|
|
114
|
+
ox: pos.x,
|
|
115
|
+
oy: pos.y,
|
|
116
|
+
ow: size.w,
|
|
117
|
+
oh: size.h,
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const fileName = annotation.filePath.split("/").pop() ?? annotation.filePath;
|
|
122
|
+
const linePreview =
|
|
123
|
+
annotation.lineContent.trim().length > 55
|
|
124
|
+
? annotation.lineContent.trim().slice(0, 55) + "…"
|
|
125
|
+
: annotation.lineContent.trim();
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
className="fixed z-[65] flex flex-col bg-slate-900 border border-violet-500/40 rounded-xl shadow-2xl overflow-hidden"
|
|
130
|
+
style={{ left: pos.x, top: pos.y, width: size.w, height: size.h }}
|
|
131
|
+
>
|
|
132
|
+
{/* Title bar */}
|
|
133
|
+
<div
|
|
134
|
+
className="flex items-start gap-2 px-3 py-2 bg-slate-800/90 border-b border-violet-700/30 cursor-move select-none shrink-0"
|
|
135
|
+
onMouseDown={onTitleMouseDown}
|
|
136
|
+
>
|
|
137
|
+
<Code2 className="w-3.5 h-3.5 text-violet-400 shrink-0 mt-0.5" />
|
|
138
|
+
<div className="flex-1 min-w-0">
|
|
139
|
+
<p className="text-xs font-medium text-slate-200 truncate">
|
|
140
|
+
{annotation.prompt}
|
|
141
|
+
</p>
|
|
142
|
+
<p className="text-[10px] text-slate-500 font-mono truncate mt-0.5">
|
|
143
|
+
{fileName}
|
|
144
|
+
<span className="text-violet-500/70">:{annotation.lineNumber}</span>
|
|
145
|
+
{linePreview && (
|
|
146
|
+
<span className="text-slate-600"> — {linePreview}</span>
|
|
147
|
+
)}
|
|
148
|
+
</p>
|
|
149
|
+
</div>
|
|
150
|
+
<button
|
|
151
|
+
onMouseDown={(e) => e.stopPropagation()}
|
|
152
|
+
onClick={onClose}
|
|
153
|
+
className="p-1 rounded hover:bg-slate-700 text-slate-500 hover:text-slate-300 transition-colors shrink-0"
|
|
154
|
+
>
|
|
155
|
+
<X className="w-3.5 h-3.5" />
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
{/* Body */}
|
|
160
|
+
<div className="flex-1 overflow-y-auto px-4 py-3 text-sm min-h-0">
|
|
161
|
+
<MarkdownRenderer content={annotation.response} />
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Resize handles */}
|
|
165
|
+
<div
|
|
166
|
+
className="absolute inset-y-0 right-0 w-1.5 cursor-ew-resize"
|
|
167
|
+
onMouseDown={startResize("e")}
|
|
168
|
+
/>
|
|
169
|
+
<div
|
|
170
|
+
className="absolute inset-x-0 bottom-0 h-1.5 cursor-ns-resize"
|
|
171
|
+
onMouseDown={startResize("s")}
|
|
172
|
+
/>
|
|
173
|
+
<div
|
|
174
|
+
className="absolute bottom-0 right-0 w-3 h-3 cursor-se-resize z-10"
|
|
175
|
+
onMouseDown={startResize("se")}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|