create-interview-cockpit 0.3.0 → 0.5.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 +42 -0
- package/template/client/package.json +5 -0
- package/template/client/src/App.tsx +45 -12
- package/template/client/src/api.ts +174 -0
- package/template/client/src/components/AiSettingsModal.tsx +1041 -0
- package/template/client/src/components/AnnotationDialog.tsx +3 -9
- package/template/client/src/components/ChatMessage.tsx +110 -27
- package/template/client/src/components/ChatView.tsx +239 -137
- package/template/client/src/components/CodeContextPanel.tsx +297 -0
- package/template/client/src/components/CodeLineAnnotationPopup.tsx +179 -0
- package/template/client/src/components/CodeRunnerModal.tsx +1549 -0
- package/template/client/src/components/DocRefModal.tsx +502 -0
- package/template/client/src/components/FileAttachments.tsx +109 -9
- package/template/client/src/components/FilePickerModal.tsx +181 -0
- package/template/client/src/components/FileViewerModal.tsx +406 -28
- package/template/client/src/components/MarkdownRenderer.tsx +210 -2
- package/template/client/src/components/Sidebar.tsx +213 -125
- package/template/client/src/components/TextAnnotator.tsx +8 -15
- package/template/client/src/components/VizCraftEmbed.tsx +645 -0
- package/template/client/src/store.ts +275 -0
- package/template/client/src/types.ts +9 -0
- package/template/cockpit.json +1 -1
- package/template/data/ai-settings.json +49 -0
- package/template/server/src/google-drive.ts +109 -1
- package/template/server/src/index.ts +1187 -76
- package/template/server/src/storage.ts +359 -2
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
|
@@ -7,14 +7,19 @@
|
|
|
7
7
|
"name": "interview-cockpit-client",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@ai-sdk/react": "^3.0.170",
|
|
10
|
+
"@types/prismjs": "^1.26.6",
|
|
10
11
|
"ai": "^6.0.168",
|
|
11
12
|
"lucide-react": "^0.460.0",
|
|
12
13
|
"mermaid": "^11.4.0",
|
|
14
|
+
"prismjs": "^1.30.0",
|
|
13
15
|
"react": "^19.0.0",
|
|
14
16
|
"react-dom": "^19.0.0",
|
|
15
17
|
"react-markdown": "^9.0.0",
|
|
18
|
+
"react-simple-code-editor": "^0.14.1",
|
|
16
19
|
"react-syntax-highlighter": "^15.6.1",
|
|
17
20
|
"remark-gfm": "^4.0.0",
|
|
21
|
+
"vizcraft": "^1.17.0",
|
|
22
|
+
"yaml": "^2.8.3",
|
|
18
23
|
"zustand": "^5.0.0"
|
|
19
24
|
},
|
|
20
25
|
"devDependencies": {
|
|
@@ -1747,6 +1752,12 @@
|
|
|
1747
1752
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
|
1748
1753
|
"license": "MIT"
|
|
1749
1754
|
},
|
|
1755
|
+
"node_modules/@types/prismjs": {
|
|
1756
|
+
"version": "1.26.6",
|
|
1757
|
+
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz",
|
|
1758
|
+
"integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==",
|
|
1759
|
+
"license": "MIT"
|
|
1760
|
+
},
|
|
1750
1761
|
"node_modules/@types/react": {
|
|
1751
1762
|
"version": "19.2.14",
|
|
1752
1763
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
|
@@ -4913,6 +4924,16 @@
|
|
|
4913
4924
|
"node": ">=0.10.0"
|
|
4914
4925
|
}
|
|
4915
4926
|
},
|
|
4927
|
+
"node_modules/react-simple-code-editor": {
|
|
4928
|
+
"version": "0.14.1",
|
|
4929
|
+
"resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.14.1.tgz",
|
|
4930
|
+
"integrity": "sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==",
|
|
4931
|
+
"license": "MIT",
|
|
4932
|
+
"peerDependencies": {
|
|
4933
|
+
"react": ">=16.8.0",
|
|
4934
|
+
"react-dom": ">=16.8.0"
|
|
4935
|
+
}
|
|
4936
|
+
},
|
|
4916
4937
|
"node_modules/react-syntax-highlighter": {
|
|
4917
4938
|
"version": "15.6.6",
|
|
4918
4939
|
"resolved": "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.6.6.tgz",
|
|
@@ -5894,6 +5915,12 @@
|
|
|
5894
5915
|
"url": "https://github.com/sponsors/jonschlinkert"
|
|
5895
5916
|
}
|
|
5896
5917
|
},
|
|
5918
|
+
"node_modules/vizcraft": {
|
|
5919
|
+
"version": "1.17.0",
|
|
5920
|
+
"resolved": "https://registry.npmjs.org/vizcraft/-/vizcraft-1.17.0.tgz",
|
|
5921
|
+
"integrity": "sha512-BCXjnWWEfMHWnAYD6DQvVXJFz0Q/Da1ukULXdMKIrZiNfblVVyYO/b/MWPO54cRzJESJZle4U52G1v2U/zRqQg==",
|
|
5922
|
+
"license": "MIT"
|
|
5923
|
+
},
|
|
5897
5924
|
"node_modules/vscode-jsonrpc": {
|
|
5898
5925
|
"version": "8.2.0",
|
|
5899
5926
|
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
|
|
@@ -5959,6 +5986,21 @@
|
|
|
5959
5986
|
"dev": true,
|
|
5960
5987
|
"license": "ISC"
|
|
5961
5988
|
},
|
|
5989
|
+
"node_modules/yaml": {
|
|
5990
|
+
"version": "2.8.3",
|
|
5991
|
+
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
|
5992
|
+
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
|
5993
|
+
"license": "ISC",
|
|
5994
|
+
"bin": {
|
|
5995
|
+
"yaml": "bin.mjs"
|
|
5996
|
+
},
|
|
5997
|
+
"engines": {
|
|
5998
|
+
"node": ">= 14.6"
|
|
5999
|
+
},
|
|
6000
|
+
"funding": {
|
|
6001
|
+
"url": "https://github.com/sponsors/eemeli"
|
|
6002
|
+
}
|
|
6003
|
+
},
|
|
5962
6004
|
"node_modules/zod": {
|
|
5963
6005
|
"version": "4.3.6",
|
|
5964
6006
|
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
|
|
@@ -9,14 +9,19 @@
|
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@ai-sdk/react": "^3.0.170",
|
|
12
|
+
"@types/prismjs": "^1.26.6",
|
|
12
13
|
"ai": "^6.0.168",
|
|
13
14
|
"lucide-react": "^0.460.0",
|
|
14
15
|
"mermaid": "^11.4.0",
|
|
16
|
+
"prismjs": "^1.30.0",
|
|
15
17
|
"react": "^19.0.0",
|
|
16
18
|
"react-dom": "^19.0.0",
|
|
17
19
|
"react-markdown": "^9.0.0",
|
|
20
|
+
"react-simple-code-editor": "^0.14.1",
|
|
18
21
|
"react-syntax-highlighter": "^15.6.1",
|
|
19
22
|
"remark-gfm": "^4.0.0",
|
|
23
|
+
"vizcraft": "^1.17.0",
|
|
24
|
+
"yaml": "^2.8.3",
|
|
20
25
|
"zustand": "^5.0.0"
|
|
21
26
|
},
|
|
22
27
|
"devDependencies": {
|
|
@@ -4,12 +4,17 @@ 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 DocRefModal from "./components/DocRefModal";
|
|
8
|
+
import AiSettingsModal from "./components/AiSettingsModal";
|
|
9
|
+
import CodeRunnerModal from "./components/CodeRunnerModal";
|
|
10
|
+
import { Code, Plane, PanelLeftClose, PanelLeft, Settings } from "lucide-react";
|
|
8
11
|
|
|
9
12
|
export default function App() {
|
|
10
13
|
const {
|
|
11
14
|
fetchTopics,
|
|
12
15
|
fetchWorkspaces,
|
|
16
|
+
fetchAiSettings,
|
|
17
|
+
fetchWorkspaceFiles,
|
|
13
18
|
fetchQuestions,
|
|
14
19
|
selectQuestion,
|
|
15
20
|
currentQuestion,
|
|
@@ -19,12 +24,21 @@ export default function App() {
|
|
|
19
24
|
toggleSidebar,
|
|
20
25
|
viewingFile,
|
|
21
26
|
closeFileViewer,
|
|
27
|
+
viewingDoc,
|
|
28
|
+
closeDocViewer,
|
|
29
|
+
showSettings,
|
|
30
|
+
openSettings,
|
|
31
|
+
closeSettings,
|
|
32
|
+
showCodeRunner,
|
|
33
|
+
closeCodeRunner,
|
|
22
34
|
} = useStore();
|
|
23
35
|
|
|
24
36
|
useEffect(() => {
|
|
25
37
|
const init = async () => {
|
|
26
38
|
await fetchWorkspaces();
|
|
27
39
|
await fetchTopics();
|
|
40
|
+
fetchAiSettings();
|
|
41
|
+
fetchWorkspaceFiles();
|
|
28
42
|
// Restore last-viewed question after page refresh
|
|
29
43
|
const topicId = sessionStorage.getItem("lastTopicId");
|
|
30
44
|
const questionId = sessionStorage.getItem("lastQuestionId");
|
|
@@ -71,17 +85,26 @@ export default function App() {
|
|
|
71
85
|
{currentQuestion ? currentQuestion.title : "Interview Cockpit"}
|
|
72
86
|
</span>
|
|
73
87
|
</div>
|
|
74
|
-
<
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
88
|
+
<div className="flex items-center gap-1">
|
|
89
|
+
<button
|
|
90
|
+
onClick={openSettings}
|
|
91
|
+
className="p-1.5 rounded transition-colors text-slate-500 hover:text-slate-300 hover:bg-slate-800"
|
|
92
|
+
title="AI Settings"
|
|
93
|
+
>
|
|
94
|
+
<Settings className="w-4 h-4" />
|
|
95
|
+
</button>
|
|
96
|
+
<button
|
|
97
|
+
onClick={toggleCodePanel}
|
|
98
|
+
className={`p-1.5 rounded transition-colors ${
|
|
99
|
+
showCodePanel
|
|
100
|
+
? "bg-cyan-500/20 text-cyan-400"
|
|
101
|
+
: "text-slate-500 hover:text-slate-300"
|
|
102
|
+
}`}
|
|
103
|
+
title="Toggle code context"
|
|
104
|
+
>
|
|
105
|
+
<Code className="w-4 h-4" />
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
85
108
|
</header>
|
|
86
109
|
|
|
87
110
|
{/* Content area */}
|
|
@@ -124,6 +147,16 @@ export default function App() {
|
|
|
124
147
|
{viewingFile && (
|
|
125
148
|
<FileViewerModal filePath={viewingFile} onClose={closeFileViewer} />
|
|
126
149
|
)}
|
|
150
|
+
{viewingDoc && (
|
|
151
|
+
<DocRefModal
|
|
152
|
+
fileId={viewingDoc.fileId}
|
|
153
|
+
quote={viewingDoc.quote}
|
|
154
|
+
fileName={viewingDoc.fileName}
|
|
155
|
+
onClose={closeDocViewer}
|
|
156
|
+
/>
|
|
157
|
+
)}
|
|
158
|
+
{showSettings && <AiSettingsModal />}
|
|
159
|
+
{showCodeRunner && <CodeRunnerModal />}
|
|
127
160
|
</div>
|
|
128
161
|
);
|
|
129
162
|
}
|
|
@@ -2,6 +2,52 @@ 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
|
+
/** When true, preference prompt texts are appended to every message (not just on change). */
|
|
26
|
+
alwaysSendPrefsDefault?: boolean;
|
|
27
|
+
/** Gemini thinking budget in tokens. 0 = disabled. Only applies when provider is google/gemini. */
|
|
28
|
+
thinkingBudget?: number;
|
|
29
|
+
/** Read-only: current AI provider from .env (openai | google | anthropic). */
|
|
30
|
+
provider?: string;
|
|
31
|
+
/** Read-only: current model name from .env. */
|
|
32
|
+
model?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function fetchAiSettings(): Promise<AiSettings> {
|
|
36
|
+
const res = await fetch(`${BASE}/settings`);
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function saveAiSettings(
|
|
41
|
+
patch: Partial<AiSettings>,
|
|
42
|
+
): Promise<AiSettings> {
|
|
43
|
+
const res = await fetch(`${BASE}/settings`, {
|
|
44
|
+
method: "PATCH",
|
|
45
|
+
headers: { "Content-Type": "application/json" },
|
|
46
|
+
body: JSON.stringify(patch),
|
|
47
|
+
});
|
|
48
|
+
return res.json();
|
|
49
|
+
}
|
|
50
|
+
|
|
5
51
|
export async function fetchTopics(): Promise<Topic[]> {
|
|
6
52
|
const res = await fetch(`${BASE}/topics`);
|
|
7
53
|
return res.json();
|
|
@@ -44,6 +90,10 @@ export async function uploadTopicFiles(
|
|
|
44
90
|
method: "POST",
|
|
45
91
|
body: form,
|
|
46
92
|
});
|
|
93
|
+
if (!res.ok) {
|
|
94
|
+
const err = await res.json().catch(() => ({ error: "Upload failed" }));
|
|
95
|
+
throw new Error(err.error ?? "Upload failed");
|
|
96
|
+
}
|
|
47
97
|
return res.json();
|
|
48
98
|
}
|
|
49
99
|
|
|
@@ -56,6 +106,35 @@ export async function deleteTopicFile(
|
|
|
56
106
|
});
|
|
57
107
|
}
|
|
58
108
|
|
|
109
|
+
// --- Workspace Context Files ---
|
|
110
|
+
|
|
111
|
+
export async function fetchWorkspaceFiles(): Promise<ContextFile[]> {
|
|
112
|
+
const res = await fetch(`${BASE}/workspace/context-files`);
|
|
113
|
+
return res.json();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function uploadWorkspaceFiles(
|
|
117
|
+
files: FileList | File[],
|
|
118
|
+
): Promise<ContextFile[]> {
|
|
119
|
+
const form = new FormData();
|
|
120
|
+
for (const file of files) form.append("files", file);
|
|
121
|
+
const res = await fetch(`${BASE}/workspace/context-files`, {
|
|
122
|
+
method: "POST",
|
|
123
|
+
body: form,
|
|
124
|
+
});
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
const err = await res.json().catch(() => ({ error: "Upload failed" }));
|
|
127
|
+
throw new Error(err.error ?? "Upload failed");
|
|
128
|
+
}
|
|
129
|
+
return res.json();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function deleteWorkspaceFile(fileId: string): Promise<void> {
|
|
133
|
+
await fetch(`${BASE}/workspace/context-files/${fileId}`, {
|
|
134
|
+
method: "DELETE",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
59
138
|
// --- Questions ---
|
|
60
139
|
|
|
61
140
|
export async function fetchQuestions(topicId: string): Promise<Question[]> {
|
|
@@ -81,6 +160,7 @@ export async function createQuestion(
|
|
|
81
160
|
|
|
82
161
|
export async function fetchQuestion(id: string): Promise<Question> {
|
|
83
162
|
const res = await fetch(`${BASE}/questions/${id}`);
|
|
163
|
+
if (!res.ok) throw new Error(`Question not found: ${id}`);
|
|
84
164
|
return res.json();
|
|
85
165
|
}
|
|
86
166
|
|
|
@@ -112,6 +192,10 @@ export async function uploadQuestionFiles(
|
|
|
112
192
|
method: "POST",
|
|
113
193
|
body: form,
|
|
114
194
|
});
|
|
195
|
+
if (!res.ok) {
|
|
196
|
+
const err = await res.json().catch(() => ({ error: "Upload failed" }));
|
|
197
|
+
throw new Error(err.error ?? "Upload failed");
|
|
198
|
+
}
|
|
115
199
|
return res.json();
|
|
116
200
|
}
|
|
117
201
|
|
|
@@ -124,6 +208,96 @@ export async function deleteQuestionFile(
|
|
|
124
208
|
});
|
|
125
209
|
}
|
|
126
210
|
|
|
211
|
+
export interface PickableFile {
|
|
212
|
+
fileId: string;
|
|
213
|
+
originalName: string;
|
|
214
|
+
source: "workspace" | "topic" | "question";
|
|
215
|
+
sourceName: string;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function fetchAllContextFiles(): Promise<PickableFile[]> {
|
|
219
|
+
const res = await fetch(`${BASE}/context-files/all`);
|
|
220
|
+
return res.json();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function linkFileToTopic(
|
|
224
|
+
topicId: string,
|
|
225
|
+
fileId: string,
|
|
226
|
+
originalName: string,
|
|
227
|
+
): Promise<ContextFile> {
|
|
228
|
+
const res = await fetch(`${BASE}/topics/${topicId}/context-files/link`, {
|
|
229
|
+
method: "POST",
|
|
230
|
+
headers: { "Content-Type": "application/json" },
|
|
231
|
+
body: JSON.stringify({ fileId, originalName }),
|
|
232
|
+
});
|
|
233
|
+
return res.json();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function linkFileToQuestion(
|
|
237
|
+
questionId: string,
|
|
238
|
+
fileId: string,
|
|
239
|
+
originalName: string,
|
|
240
|
+
): Promise<ContextFile> {
|
|
241
|
+
const res = await fetch(
|
|
242
|
+
`${BASE}/questions/${questionId}/context-files/link`,
|
|
243
|
+
{
|
|
244
|
+
method: "POST",
|
|
245
|
+
headers: { "Content-Type": "application/json" },
|
|
246
|
+
body: JSON.stringify({ fileId, originalName }),
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
return res.json();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export async function saveCodeSnippet(
|
|
253
|
+
questionId: string,
|
|
254
|
+
code: string,
|
|
255
|
+
language: string,
|
|
256
|
+
label: string,
|
|
257
|
+
origin: "user" | "ai" | "sandbox",
|
|
258
|
+
): Promise<ContextFile> {
|
|
259
|
+
const res = await fetch(`${BASE}/questions/${questionId}/save-code-snippet`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: { "Content-Type": "application/json" },
|
|
262
|
+
body: JSON.stringify({ code, language, label, origin }),
|
|
263
|
+
});
|
|
264
|
+
if (!res.ok) throw new Error(await res.text());
|
|
265
|
+
return res.json();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export async function overwriteContextFileContent(
|
|
269
|
+
questionId: string,
|
|
270
|
+
fileId: string,
|
|
271
|
+
content: string,
|
|
272
|
+
): Promise<void> {
|
|
273
|
+
const res = await fetch(
|
|
274
|
+
`${BASE}/questions/${questionId}/context-files/${fileId}/content`,
|
|
275
|
+
{
|
|
276
|
+
method: "PUT",
|
|
277
|
+
headers: { "Content-Type": "application/json" },
|
|
278
|
+
body: JSON.stringify({ code: content }),
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
if (!res.ok) throw new Error(await res.text());
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export async function renameContextFile(
|
|
285
|
+
questionId: string,
|
|
286
|
+
fileId: string,
|
|
287
|
+
label: string,
|
|
288
|
+
): Promise<ContextFile> {
|
|
289
|
+
const res = await fetch(
|
|
290
|
+
`${BASE}/questions/${questionId}/context-files/${fileId}`,
|
|
291
|
+
{
|
|
292
|
+
method: "PATCH",
|
|
293
|
+
headers: { "Content-Type": "application/json" },
|
|
294
|
+
body: JSON.stringify({ label }),
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
if (!res.ok) throw new Error(await res.text());
|
|
298
|
+
return res.json();
|
|
299
|
+
}
|
|
300
|
+
|
|
127
301
|
// --- Code Context ---
|
|
128
302
|
|
|
129
303
|
export async function fetchCodeContextTree(): Promise<string[]> {
|