create-interview-cockpit 0.2.0 → 0.4.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/README.md +23 -0
- package/package.json +1 -1
- package/template/client/package-lock.json +23 -0
- package/template/client/package.json +2 -0
- package/template/client/src/App.tsx +48 -12
- package/template/client/src/api.ts +39 -0
- package/template/client/src/components/AiSettingsModal.tsx +827 -0
- package/template/client/src/components/ChatView.tsx +173 -136
- package/template/client/src/components/MarkdownRenderer.tsx +5 -0
- package/template/client/src/components/Sidebar.tsx +3 -1
- package/template/client/src/components/VizCraftEmbed.tsx +502 -0
- package/template/client/src/store.ts +76 -0
- package/template/client/src/types.ts +1 -0
- package/template/cockpit.json +1 -1
- package/template/data/ai-settings.json +49 -0
- package/template/package.json +1 -1
- package/template/server/src/index.ts +84 -34
- package/template/server/src/storage.ts +96 -0
package/README.md
CHANGED
|
@@ -57,6 +57,29 @@ AI_MODEL=gemini-2.5-flash
|
|
|
57
57
|
GOOGLE_API_KEY=your-key-here
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
## Upgrading
|
|
61
|
+
|
|
62
|
+
When a new version of the template is released, run this from inside your cockpit project directory:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx create-interview-cockpit upgrade
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The CLI will:
|
|
69
|
+
- Compare your installed version (`cockpit.json`) against the latest
|
|
70
|
+
- Overwrite `client/` and `server/` with the updated template files
|
|
71
|
+
- Merge any new scripts and dependencies into your `package.json` (your pinned versions are preserved)
|
|
72
|
+
- **Leave untouched:** `.env`, `data/` (your topics, questions, and context files), and `node_modules/`
|
|
73
|
+
|
|
74
|
+
After upgrading, reinstall dependencies and restart:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm install
|
|
78
|
+
cd client && npm install && cd ..
|
|
79
|
+
cd server && npm install && cd ..
|
|
80
|
+
npm run dev
|
|
81
|
+
```
|
|
82
|
+
|
|
60
83
|
## License
|
|
61
84
|
|
|
62
85
|
MIT
|
package/package.json
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"react-markdown": "^9.0.0",
|
|
16
16
|
"react-syntax-highlighter": "^15.6.1",
|
|
17
17
|
"remark-gfm": "^4.0.0",
|
|
18
|
+
"vizcraft": "^1.17.0",
|
|
19
|
+
"yaml": "^2.8.3",
|
|
18
20
|
"zustand": "^5.0.0"
|
|
19
21
|
},
|
|
20
22
|
"devDependencies": {
|
|
@@ -5894,6 +5896,12 @@
|
|
|
5894
5896
|
"url": "https://github.com/sponsors/jonschlinkert"
|
|
5895
5897
|
}
|
|
5896
5898
|
},
|
|
5899
|
+
"node_modules/vizcraft": {
|
|
5900
|
+
"version": "1.17.0",
|
|
5901
|
+
"resolved": "https://registry.npmjs.org/vizcraft/-/vizcraft-1.17.0.tgz",
|
|
5902
|
+
"integrity": "sha512-BCXjnWWEfMHWnAYD6DQvVXJFz0Q/Da1ukULXdMKIrZiNfblVVyYO/b/MWPO54cRzJESJZle4U52G1v2U/zRqQg==",
|
|
5903
|
+
"license": "MIT"
|
|
5904
|
+
},
|
|
5897
5905
|
"node_modules/vscode-jsonrpc": {
|
|
5898
5906
|
"version": "8.2.0",
|
|
5899
5907
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
|
@@ -5959,6 +5967,21 @@
|
|
|
5959
5967
|
"dev": true,
|
|
5960
5968
|
"license": "ISC"
|
|
5961
5969
|
},
|
|
5970
|
+
"node_modules/yaml": {
|
|
5971
|
+
"version": "2.8.3",
|
|
5972
|
+
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
|
5973
|
+
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
|
5974
|
+
"license": "ISC",
|
|
5975
|
+
"bin": {
|
|
5976
|
+
"yaml": "bin.mjs"
|
|
5977
|
+
},
|
|
5978
|
+
"engines": {
|
|
5979
|
+
"node": ">= 14.6"
|
|
5980
|
+
},
|
|
5981
|
+
"funding": {
|
|
5982
|
+
"url": "https://github.com/sponsors/eemeli"
|
|
5983
|
+
}
|
|
5984
|
+
},
|
|
5962
5985
|
"node_modules/zod": {
|
|
5963
5986
|
"version": "4.3.6",
|
|
5964
5987
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
|
@@ -4,12 +4,16 @@ import Sidebar from "./components/Sidebar";
|
|
|
4
4
|
import ChatView from "./components/ChatView";
|
|
5
5
|
import CodeContextPanel from "./components/CodeContextPanel";
|
|
6
6
|
import FileViewerModal from "./components/FileViewerModal";
|
|
7
|
-
import
|
|
7
|
+
import AiSettingsModal from "./components/AiSettingsModal";
|
|
8
|
+
import { Code, Plane, PanelLeftClose, PanelLeft, Settings } from "lucide-react";
|
|
8
9
|
|
|
9
10
|
export default function App() {
|
|
10
11
|
const {
|
|
11
12
|
fetchTopics,
|
|
12
13
|
fetchWorkspaces,
|
|
14
|
+
fetchAiSettings,
|
|
15
|
+
fetchQuestions,
|
|
16
|
+
selectQuestion,
|
|
13
17
|
currentQuestion,
|
|
14
18
|
showCodePanel,
|
|
15
19
|
toggleCodePanel,
|
|
@@ -17,12 +21,34 @@ export default function App() {
|
|
|
17
21
|
toggleSidebar,
|
|
18
22
|
viewingFile,
|
|
19
23
|
closeFileViewer,
|
|
24
|
+
showSettings,
|
|
25
|
+
openSettings,
|
|
26
|
+
closeSettings,
|
|
20
27
|
} = useStore();
|
|
21
28
|
|
|
22
29
|
useEffect(() => {
|
|
23
30
|
const init = async () => {
|
|
24
31
|
await fetchWorkspaces();
|
|
25
32
|
await fetchTopics();
|
|
33
|
+
fetchAiSettings();
|
|
34
|
+
// Restore last-viewed question after page refresh
|
|
35
|
+
const topicId = sessionStorage.getItem("lastTopicId");
|
|
36
|
+
const questionId = sessionStorage.getItem("lastQuestionId");
|
|
37
|
+
if (topicId && questionId) {
|
|
38
|
+
try {
|
|
39
|
+
await fetchQuestions(topicId);
|
|
40
|
+
await selectQuestion(topicId, questionId);
|
|
41
|
+
// Expand the topic so it's visible in the sidebar
|
|
42
|
+
const { expandedTopics } = useStore.getState();
|
|
43
|
+
if (!expandedTopics.includes(topicId)) {
|
|
44
|
+
useStore.getState().toggleTopic(topicId);
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// Question may no longer exist — ignore
|
|
48
|
+
sessionStorage.removeItem("lastTopicId");
|
|
49
|
+
sessionStorage.removeItem("lastQuestionId");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
26
52
|
};
|
|
27
53
|
init();
|
|
28
54
|
}, []);
|
|
@@ -51,17 +77,26 @@ export default function App() {
|
|
|
51
77
|
{currentQuestion ? currentQuestion.title : "Interview Cockpit"}
|
|
52
78
|
</span>
|
|
53
79
|
</div>
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
<div className="flex items-center gap-1">
|
|
81
|
+
<button
|
|
82
|
+
onClick={openSettings}
|
|
83
|
+
className="p-1.5 rounded transition-colors text-slate-500 hover:text-slate-300 hover:bg-slate-800"
|
|
84
|
+
title="AI Settings"
|
|
85
|
+
>
|
|
86
|
+
<Settings className="w-4 h-4" />
|
|
87
|
+
</button>
|
|
88
|
+
<button
|
|
89
|
+
onClick={toggleCodePanel}
|
|
90
|
+
className={`p-1.5 rounded transition-colors ${
|
|
91
|
+
showCodePanel
|
|
92
|
+
? "bg-cyan-500/20 text-cyan-400"
|
|
93
|
+
: "text-slate-500 hover:text-slate-300"
|
|
94
|
+
}`}
|
|
95
|
+
title="Toggle code context"
|
|
96
|
+
>
|
|
97
|
+
<Code className="w-4 h-4" />
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
65
100
|
</header>
|
|
66
101
|
|
|
67
102
|
{/* Content area */}
|
|
@@ -104,6 +139,7 @@ export default function App() {
|
|
|
104
139
|
{viewingFile && (
|
|
105
140
|
<FileViewerModal filePath={viewingFile} onClose={closeFileViewer} />
|
|
106
141
|
)}
|
|
142
|
+
{showSettings && <AiSettingsModal />}
|
|
107
143
|
</div>
|
|
108
144
|
);
|
|
109
145
|
}
|
|
@@ -2,6 +2,44 @@ import type { Topic, Question, ContextFile, WorkspacesRegistry } from "./types";
|
|
|
2
2
|
|
|
3
3
|
const BASE = "/api";
|
|
4
4
|
|
|
5
|
+
export interface PromptGroup {
|
|
6
|
+
/** Display name shown in the settings UI. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Optional helper text shown below the section header. */
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Which option key is active by default in new chats. */
|
|
11
|
+
default: string;
|
|
12
|
+
/** Map of option key -> prompt text appended to the user message. */
|
|
13
|
+
options: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AiSettings {
|
|
17
|
+
systemPrompt: string;
|
|
18
|
+
responseProfiles: Record<
|
|
19
|
+
string,
|
|
20
|
+
{ maxOutputTokens: number; maxSteps: number }
|
|
21
|
+
>;
|
|
22
|
+
vizGuide: string;
|
|
23
|
+
/** All user-selectable prompt groups. Add new entries here to extend the UI. */
|
|
24
|
+
promptGroups: Record<string, PromptGroup>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function fetchAiSettings(): Promise<AiSettings> {
|
|
28
|
+
const res = await fetch(`${BASE}/settings`);
|
|
29
|
+
return res.json();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function saveAiSettings(
|
|
33
|
+
patch: Partial<AiSettings>,
|
|
34
|
+
): Promise<AiSettings> {
|
|
35
|
+
const res = await fetch(`${BASE}/settings`, {
|
|
36
|
+
method: "PATCH",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: JSON.stringify(patch),
|
|
39
|
+
});
|
|
40
|
+
return res.json();
|
|
41
|
+
}
|
|
42
|
+
|
|
5
43
|
export async function fetchTopics(): Promise<Topic[]> {
|
|
6
44
|
const res = await fetch(`${BASE}/topics`);
|
|
7
45
|
return res.json();
|
|
@@ -81,6 +119,7 @@ export async function createQuestion(
|
|
|
81
119
|
|
|
82
120
|
export async function fetchQuestion(id: string): Promise<Question> {
|
|
83
121
|
const res = await fetch(`${BASE}/questions/${id}`);
|
|
122
|
+
if (!res.ok) throw new Error(`Question not found: ${id}`);
|
|
84
123
|
return res.json();
|
|
85
124
|
}
|
|
86
125
|
|