@pmcai/codex-chat-widget 0.1.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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/index.cjs +276 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.css +476 -0
- package/dist/index.css.map +7 -0
- package/dist/index.js +246 -0
- package/dist/index.js.map +7 -0
- package/dist/styles.css +476 -0
- package/package.json +54 -0
- package/src/CodexChatWidget.jsx +84 -0
- package/src/styles.css +3 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// src/CodexChatWidget.jsx
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import ReactMarkdown from "react-markdown";
|
|
4
|
+
import remarkGfm from "remark-gfm";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
var defaultSettings = { provider: "server", model: "cohere/north-mini-code:free", apiKey: "", baseUrl: "" };
|
|
7
|
+
function CodexChatWidget({
|
|
8
|
+
apiBase = "",
|
|
9
|
+
storageKey = "codex-chat",
|
|
10
|
+
initialSessionId,
|
|
11
|
+
title = "Codex Harness"
|
|
12
|
+
}) {
|
|
13
|
+
const apiBaseUrl = String(apiBase || "").replace(/\/$/, "");
|
|
14
|
+
const [sessions, setSessions] = useState([]);
|
|
15
|
+
const [session, setSession] = useState(null);
|
|
16
|
+
const [prompt, setPrompt] = useState("");
|
|
17
|
+
const [activityBySession, setActivityBySession] = useState({});
|
|
18
|
+
const [runningBySession, setRunningBySession] = useState({});
|
|
19
|
+
const [settings, setSettings] = useState(defaultSettings);
|
|
20
|
+
const [models, setModels] = useState([]);
|
|
21
|
+
const [modelsState, setModelsState] = useState("");
|
|
22
|
+
const [showSettings, setShowSettings] = useState(false);
|
|
23
|
+
const [showSessions, setShowSessions] = useState(false);
|
|
24
|
+
const [homeLabel, setHomeLabel] = useState("~/.pmai/workspace");
|
|
25
|
+
async function refreshSessions(selectId) {
|
|
26
|
+
let items = await (await fetch(apiBaseUrl + "/api/sessions")).json();
|
|
27
|
+
if (!items.length) {
|
|
28
|
+
const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
|
|
29
|
+
items = [created];
|
|
30
|
+
}
|
|
31
|
+
setSessions(items);
|
|
32
|
+
const wanted = selectId || session?.id;
|
|
33
|
+
const id = items.some((item) => item.id === wanted) ? wanted : items[0].id;
|
|
34
|
+
const response = await fetch(apiBaseUrl + `/api/sessions?id=${id}`);
|
|
35
|
+
if (response.ok) {
|
|
36
|
+
setSession(await response.json());
|
|
37
|
+
localStorage.setItem(`${storageKey}-active-session`, id);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const savedSession = localStorage.getItem(`${storageKey}-active-session`);
|
|
42
|
+
refreshSessions(initialSessionId || savedSession || void 0);
|
|
43
|
+
fetch(apiBaseUrl + "/api/settings").then((r) => r.json()).then(setSettings);
|
|
44
|
+
fetch(apiBaseUrl + "/api/health").then((r) => r.json()).then((h) => {
|
|
45
|
+
if (h.workspace) setHomeLabel("~/.pmai" + String(h.workspace).replace(/^\/pmai/, ""));
|
|
46
|
+
}).catch(() => {
|
|
47
|
+
});
|
|
48
|
+
}, []);
|
|
49
|
+
async function loadModels(nextSettings = settings) {
|
|
50
|
+
setModelsState("\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 model list\u2026");
|
|
51
|
+
const data = await (await fetch(apiBaseUrl + "/api/models", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(nextSettings), cache: "no-store" })).json();
|
|
52
|
+
setModels(data.models || []);
|
|
53
|
+
setModelsState(data.error || data.message || data.label || `${(data.models || []).length} models`);
|
|
54
|
+
}
|
|
55
|
+
async function openSettings() {
|
|
56
|
+
setShowSettings(true);
|
|
57
|
+
await loadModels();
|
|
58
|
+
}
|
|
59
|
+
async function saveGlobalSettings() {
|
|
60
|
+
const previous = `${settings.provider}/${settings.model}`;
|
|
61
|
+
const saved = await (await fetch(apiBaseUrl + "/api/settings", { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify(settings) })).json();
|
|
62
|
+
setSettings(saved);
|
|
63
|
+
if (session && `${saved.provider}/${saved.model}` !== previous) setSession((current) => ({ ...current, messages: [...current.messages, { role: "system", text: `Model changed: ${saved.model} (${saved.provider})` }] }));
|
|
64
|
+
setShowSettings(false);
|
|
65
|
+
}
|
|
66
|
+
async function newSession() {
|
|
67
|
+
const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
|
|
68
|
+
await refreshSessions(created.id);
|
|
69
|
+
}
|
|
70
|
+
async function choose(id) {
|
|
71
|
+
await refreshSessions(id);
|
|
72
|
+
}
|
|
73
|
+
async function archive(id) {
|
|
74
|
+
await fetch(apiBaseUrl + "/api/sessions", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ id, archived: true }) });
|
|
75
|
+
await refreshSessions();
|
|
76
|
+
}
|
|
77
|
+
async function remove(id) {
|
|
78
|
+
if (!confirm("Delete this chat permanently?")) return;
|
|
79
|
+
await fetch(apiBaseUrl + `/api/sessions?id=${id}`, { method: "DELETE" });
|
|
80
|
+
if (session?.id === id) setSession(null);
|
|
81
|
+
await refreshSessions();
|
|
82
|
+
}
|
|
83
|
+
async function send(event) {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
const text = prompt.trim();
|
|
86
|
+
const sessionId = session?.id;
|
|
87
|
+
if (!text || runningBySession[sessionId] || !sessionId) return;
|
|
88
|
+
setSession((x) => ({ ...x, messages: [...x.messages, { role: "user", text }] }));
|
|
89
|
+
setPrompt("");
|
|
90
|
+
setActivityBySession((x) => ({ ...x, [sessionId]: [{ text: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E15\u0E23\u0E35\u0E22\u0E21\u0E07\u0E32\u0E19" }] }));
|
|
91
|
+
setRunningBySession((x) => ({ ...x, [sessionId]: true }));
|
|
92
|
+
try {
|
|
93
|
+
const response = await fetch(apiBaseUrl + "/api/chat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ prompt: text, sessionId, settings }) });
|
|
94
|
+
if (!response.ok || !response.body) {
|
|
95
|
+
const error = await response.json();
|
|
96
|
+
throw new Error(error.error || `Request failed (${response.status})`);
|
|
97
|
+
}
|
|
98
|
+
const reader = response.body.getReader();
|
|
99
|
+
const decoder = new TextDecoder();
|
|
100
|
+
let buffer = "";
|
|
101
|
+
let finalText = "";
|
|
102
|
+
while (true) {
|
|
103
|
+
const { done, value } = await reader.read();
|
|
104
|
+
if (done) break;
|
|
105
|
+
buffer += decoder.decode(value, { stream: true });
|
|
106
|
+
const frames = buffer.split("\n\n");
|
|
107
|
+
buffer = frames.pop() || "";
|
|
108
|
+
for (const frame of frames) {
|
|
109
|
+
const line = frame.split("\n").find((x) => x.startsWith("data: "));
|
|
110
|
+
if (!line) continue;
|
|
111
|
+
const data = JSON.parse(line.slice(6));
|
|
112
|
+
if (data.type === "status") setActivityBySession((x) => ({ ...x, [sessionId]: [...x[sessionId] || [], { text: data.text }] }));
|
|
113
|
+
if (data.type === "final") finalText = data.text;
|
|
114
|
+
if (data.type === "error") throw new Error(data.text);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: "assistant", text: finalText || "\u0E07\u0E32\u0E19\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E41\u0E25\u0E49\u0E27 \u0E41\u0E15\u0E48\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E2A\u0E23\u0E38\u0E1B" }] }));
|
|
118
|
+
await refreshSessions(sessionId);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: "assistant", text: `\u0E40\u0E01\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14: ${error.message}` }] }));
|
|
121
|
+
} finally {
|
|
122
|
+
setActivityBySession((x) => ({ ...x, [sessionId]: [] }));
|
|
123
|
+
setRunningBySession((x) => ({ ...x, [sessionId]: false }));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return /* @__PURE__ */ jsxs("main", { className: "codex-chat-app", children: [
|
|
127
|
+
/* @__PURE__ */ jsxs("aside", { className: "codex-chat-sidebar", children: [
|
|
128
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-new", onClick: newSession, children: "\uFF0B New chat" }),
|
|
129
|
+
/* @__PURE__ */ jsx("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ jsxs("div", { className: "codex-chat-sessionRow", children: [
|
|
130
|
+
/* @__PURE__ */ jsxs("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: () => choose(item.id), children: [
|
|
131
|
+
item.title,
|
|
132
|
+
/* @__PURE__ */ jsx("small", { children: new Date(item.updatedAt).toLocaleString() })
|
|
133
|
+
] }),
|
|
134
|
+
/* @__PURE__ */ jsxs("div", { className: "codex-chat-sessionActions", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("button", { title: "Archive", onClick: () => archive(item.id), children: "\u232B" }),
|
|
136
|
+
/* @__PURE__ */ jsx("button", { title: "Delete", onClick: () => remove(item.id), children: "\xD7" })
|
|
137
|
+
] })
|
|
138
|
+
] }, item.id)) }),
|
|
139
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-settingsBtn", onClick: openSettings, children: "\u2699 Settings" })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsxs("section", { className: "codex-chat-shell", children: [
|
|
142
|
+
/* @__PURE__ */ jsxs("header", { children: [
|
|
143
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
144
|
+
/* @__PURE__ */ jsx("b", { children: title }),
|
|
145
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
146
|
+
settings.model,
|
|
147
|
+
" \xB7 ",
|
|
148
|
+
homeLabel
|
|
149
|
+
] })
|
|
150
|
+
] }),
|
|
151
|
+
/* @__PURE__ */ jsxs("div", { className: "codex-chat-headerActions", children: [
|
|
152
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-sessionsBtn", onClick: () => setShowSessions(true), children: "\u2630 Chats" }),
|
|
153
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-modelBtn", onClick: openSettings, children: "\u2304 Model" }),
|
|
154
|
+
/* @__PURE__ */ jsx("span", { className: "codex-chat-status", children: "\u25CF Ready" })
|
|
155
|
+
] })
|
|
156
|
+
] }),
|
|
157
|
+
/* @__PURE__ */ jsx("section", { className: "codex-chat-messages", "aria-live": "polite", children: !session ? /* @__PURE__ */ jsx("div", { className: "codex-chat-welcome", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 session\u2026" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
158
|
+
session.messages.length === 0 && /* @__PURE__ */ jsxs("div", { className: "codex-chat-welcome", children: [
|
|
159
|
+
/* @__PURE__ */ jsx("h1", { children: "\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E38\u0E22\u0E01\u0E31\u0E1A Codex" }),
|
|
160
|
+
/* @__PURE__ */ jsx("p", { children: "\u0E40\u0E25\u0E37\u0E2D\u0E01 model \u0E2B\u0E23\u0E37\u0E2D provider \u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13\u0E44\u0E14\u0E49\u0E08\u0E32\u0E01 Settings" })
|
|
161
|
+
] }),
|
|
162
|
+
session.messages.map((message, index) => message.role === "system" ? /* @__PURE__ */ jsx("div", { className: "codex-chat-systemMessage", children: message.text }, index) : /* @__PURE__ */ jsxs("article", { className: message.role, children: [
|
|
163
|
+
/* @__PURE__ */ jsx("label", { children: message.role === "user" ? "\u0E04\u0E38\u0E13" : "\u0E04\u0E33\u0E15\u0E2D\u0E1A" }),
|
|
164
|
+
message.role === "assistant" ? /* @__PURE__ */ jsx("div", { className: "codex-chat-markdown", children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: message.text }) }) : /* @__PURE__ */ jsx("pre", { children: message.text })
|
|
165
|
+
] }, index)),
|
|
166
|
+
runningBySession[session.id] && /* @__PURE__ */ jsxs("article", { className: "codex-chat-assistant progress", children: [
|
|
167
|
+
/* @__PURE__ */ jsx("label", { children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19" }),
|
|
168
|
+
/* @__PURE__ */ jsx("ol", { children: (activityBySession[session.id] || []).map((item, index) => /* @__PURE__ */ jsx("li", { className: index === (activityBySession[session.id] || []).length - 1 ? "active" : "", children: item.text }, index)) })
|
|
169
|
+
] })
|
|
170
|
+
] }) }),
|
|
171
|
+
/* @__PURE__ */ jsxs("div", { className: "codex-chat-composer", children: [
|
|
172
|
+
/* @__PURE__ */ jsxs("div", { className: "codex-chat-modelFooter", children: [
|
|
173
|
+
settings.provider,
|
|
174
|
+
" \xB7 ",
|
|
175
|
+
settings.model
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsxs("form", { onSubmit: send, children: [
|
|
178
|
+
/* @__PURE__ */ jsx("textarea", { value: prompt, onChange: (e) => setPrompt(e.target.value), placeholder: "\u0E40\u0E0A\u0E48\u0E19 \u0E15\u0E23\u0E27\u0E08\u0E44\u0E1F\u0E25\u0E4C\u0E43\u0E19 workspace \u0E41\u0E25\u0E49\u0E27\u0E2A\u0E23\u0E38\u0E1B\u0E42\u0E04\u0E23\u0E07\u0E2A\u0E23\u0E49\u0E32\u0E07", rows: "3", maxLength: "8000" }),
|
|
179
|
+
/* @__PURE__ */ jsx("button", { disabled: runningBySession[session?.id] || !prompt.trim() || !session, children: runningBySession[session?.id] ? "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19" : "\u0E2A\u0E48\u0E07" })
|
|
180
|
+
] })
|
|
181
|
+
] })
|
|
182
|
+
] }),
|
|
183
|
+
showSessions && /* @__PURE__ */ jsx("div", { className: "codex-chat-sessionDrawer", children: /* @__PURE__ */ jsxs("section", { children: [
|
|
184
|
+
/* @__PURE__ */ jsxs("header", { children: [
|
|
185
|
+
/* @__PURE__ */ jsx("b", { children: "Chats" }),
|
|
186
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setShowSessions(false), children: "\xD7" })
|
|
187
|
+
] }),
|
|
188
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-new", onClick: async () => {
|
|
189
|
+
await newSession();
|
|
190
|
+
setShowSessions(false);
|
|
191
|
+
}, children: "\uFF0B New chat" }),
|
|
192
|
+
/* @__PURE__ */ jsx("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ jsxs("div", { className: "codex-chat-sessionRow", children: [
|
|
193
|
+
/* @__PURE__ */ jsx("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: async () => {
|
|
194
|
+
await choose(item.id);
|
|
195
|
+
setShowSessions(false);
|
|
196
|
+
}, children: item.title }),
|
|
197
|
+
/* @__PURE__ */ jsxs("div", { className: "codex-chat-sessionActions", children: [
|
|
198
|
+
/* @__PURE__ */ jsx("button", { onClick: () => archive(item.id), children: "\u232B" }),
|
|
199
|
+
/* @__PURE__ */ jsx("button", { onClick: () => remove(item.id), children: "\xD7" })
|
|
200
|
+
] })
|
|
201
|
+
] }, item.id)) })
|
|
202
|
+
] }) }),
|
|
203
|
+
showSettings && /* @__PURE__ */ jsx("div", { className: "codex-chat-modal", children: /* @__PURE__ */ jsxs("section", { className: "codex-chat-settings", children: [
|
|
204
|
+
/* @__PURE__ */ jsxs("header", { children: [
|
|
205
|
+
/* @__PURE__ */ jsx("b", { children: "Settings" }),
|
|
206
|
+
/* @__PURE__ */ jsx("button", { onClick: () => setShowSettings(false), children: "\xD7" })
|
|
207
|
+
] }),
|
|
208
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
209
|
+
"Provider",
|
|
210
|
+
/* @__PURE__ */ jsxs("select", { value: settings.provider, onChange: (e) => {
|
|
211
|
+
const next = { ...settings, provider: e.target.value };
|
|
212
|
+
setSettings(next);
|
|
213
|
+
loadModels(next);
|
|
214
|
+
}, children: [
|
|
215
|
+
/* @__PURE__ */ jsx("option", { value: "server", children: "OpenRouter key \u0E2B\u0E25\u0E31\u0E01\u0E02\u0E2D\u0E07\u0E23\u0E30\u0E1A\u0E1A" }),
|
|
216
|
+
/* @__PURE__ */ jsx("option", { value: "openrouter", children: "OpenRouter key \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
|
|
217
|
+
/* @__PURE__ */ jsx("option", { value: "compatible", children: "OpenAI-compatible provider \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
|
|
218
|
+
/* @__PURE__ */ jsx("option", { value: "google-ai-proxy", children: "Google AI (server key, Responses adapter)" })
|
|
219
|
+
] })
|
|
220
|
+
] }),
|
|
221
|
+
settings.provider !== "server" && /* @__PURE__ */ jsxs("label", { children: [
|
|
222
|
+
"API key",
|
|
223
|
+
/* @__PURE__ */ jsx("input", { type: "password", value: settings.apiKey, placeholder: settings.provider === "google-ai-proxy" ? "Google AI API key (override server key)" : "API key \u0E08\u0E30\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E43\u0E19 global settings", onChange: (e) => setSettings({ ...settings, apiKey: e.target.value }) })
|
|
224
|
+
] }),
|
|
225
|
+
settings.provider === "compatible" && /* @__PURE__ */ jsxs("label", { children: [
|
|
226
|
+
"Base URL",
|
|
227
|
+
/* @__PURE__ */ jsx("input", { value: settings.baseUrl, placeholder: "https://provider.example/v1", onChange: (e) => setSettings({ ...settings, baseUrl: e.target.value }) })
|
|
228
|
+
] }),
|
|
229
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
230
|
+
"Model",
|
|
231
|
+
settings.provider === "compatible" ? /* @__PURE__ */ jsx("input", { value: settings.model, placeholder: "provider/model-id", onChange: (e) => setSettings({ ...settings, model: e.target.value }) }) : /* @__PURE__ */ jsx("select", { value: settings.model, onChange: (e) => setSettings({ ...settings, model: e.target.value }), children: models.length ? models.map((model) => /* @__PURE__ */ jsxs("option", { value: model.id, children: [
|
|
232
|
+
model.name,
|
|
233
|
+
" \u2014 ",
|
|
234
|
+
model.id
|
|
235
|
+
] }, model.id)) : /* @__PURE__ */ jsx("option", { value: settings.model, children: modelsState || settings.model }) }),
|
|
236
|
+
/* @__PURE__ */ jsx("small", { className: "codex-chat-catalogStatus", children: modelsState })
|
|
237
|
+
] }),
|
|
238
|
+
/* @__PURE__ */ jsx("p", { children: "Settings \u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14\u0E40\u0E1B\u0E47\u0E19 global \u0E41\u0E25\u0E30\u0E40\u0E01\u0E47\u0E1A\u0E43\u0E19 SQLite volume \u0E02\u0E2D\u0E07 service" }),
|
|
239
|
+
/* @__PURE__ */ jsx("button", { className: "codex-chat-save", onClick: saveGlobalSettings, children: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01" })
|
|
240
|
+
] }) })
|
|
241
|
+
] });
|
|
242
|
+
}
|
|
243
|
+
export {
|
|
244
|
+
CodexChatWidget as default
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/CodexChatWidget.jsx"],
|
|
4
|
+
"sourcesContent": ["import { useEffect, useState } from 'react'\nimport './styles.css'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\n\nconst defaultSettings = { provider: 'server', model: 'cohere/north-mini-code:free', apiKey: '', baseUrl: '' }\n\nexport default function CodexChatWidget({\n apiBase = '',\n storageKey = 'codex-chat',\n initialSessionId,\n title = 'Codex Harness'\n}) {\n const apiBaseUrl = String(apiBase || '').replace(/\\/$/, '')\n const [sessions, setSessions] = useState([])\n const [session, setSession] = useState(null)\n const [prompt, setPrompt] = useState('')\n const [activityBySession, setActivityBySession] = useState({})\n const [runningBySession, setRunningBySession] = useState({})\n const [settings, setSettings] = useState(defaultSettings)\n const [models, setModels] = useState([])\n const [modelsState, setModelsState] = useState('')\n const [showSettings, setShowSettings] = useState(false)\n const [showSessions, setShowSessions] = useState(false)\n const [homeLabel, setHomeLabel] = useState('~/.pmai/workspace')\n\n async function refreshSessions(selectId) {\n let items = await (await fetch(apiBaseUrl + '/api/sessions')).json()\n if (!items.length) {\n const created = await (await fetch(apiBaseUrl + '/api/sessions', { method: 'POST' })).json()\n items = [created]\n }\n setSessions(items)\n const wanted = selectId || session?.id\n const id = items.some((item) => item.id === wanted) ? wanted : items[0].id\n const response = await fetch(apiBaseUrl + `/api/sessions?id=${id}`)\n if (response.ok) { setSession(await response.json()); localStorage.setItem(`${storageKey}-active-session`, id) }\n }\n useEffect(() => { const savedSession = localStorage.getItem(`${storageKey}-active-session`); refreshSessions(initialSessionId || savedSession || undefined); fetch(apiBaseUrl + '/api/settings').then((r) => r.json()).then(setSettings); fetch(apiBaseUrl + '/api/health').then((r) => r.json()).then((h) => { if (h.workspace) setHomeLabel('~/.pmai' + String(h.workspace).replace(/^\\/pmai/, '')) }).catch(() => {}) }, [])\n\n async function loadModels(nextSettings = settings) {\n setModelsState('\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 model list\u2026')\n const data = await (await fetch(apiBaseUrl + '/api/models', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(nextSettings), cache: 'no-store' })).json()\n setModels(data.models || []); setModelsState(data.error || data.message || data.label || `${(data.models || []).length} models`)\n }\n async function openSettings() { setShowSettings(true); await loadModels() }\n\n async function saveGlobalSettings() {\n const previous = `${settings.provider}/${settings.model}`\n const saved = await (await fetch(apiBaseUrl + '/api/settings', { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify(settings) })).json()\n setSettings(saved)\n if (session && `${saved.provider}/${saved.model}` !== previous) setSession((current) => ({ ...current, messages: [...current.messages, { role: 'system', text: `Model changed: ${saved.model} (${saved.provider})` }] }))\n setShowSettings(false)\n }\n\n async function newSession() { const created = await (await fetch(apiBaseUrl + '/api/sessions', { method: 'POST' })).json(); await refreshSessions(created.id) }\n async function choose(id) { await refreshSessions(id) }\n async function archive(id) { await fetch(apiBaseUrl + '/api/sessions', { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, archived: true }) }); await refreshSessions() }\n async function remove(id) { if (!confirm('Delete this chat permanently?')) return; await fetch(apiBaseUrl + `/api/sessions?id=${id}`, { method: 'DELETE' }); if (session?.id === id) setSession(null); await refreshSessions() }\n\n async function send(event) {\n event.preventDefault(); const text = prompt.trim(); const sessionId = session?.id\n if (!text || runningBySession[sessionId] || !sessionId) return\n setSession((x) => ({ ...x, messages: [...x.messages, { role: 'user', text }] }))\n setPrompt(''); setActivityBySession((x) => ({ ...x, [sessionId]: [{ text: '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E15\u0E23\u0E35\u0E22\u0E21\u0E07\u0E32\u0E19' }] })); setRunningBySession((x) => ({ ...x, [sessionId]: true }))\n try {\n const response = await fetch(apiBaseUrl + '/api/chat', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ prompt: text, sessionId, settings }) })\n if (!response.ok || !response.body) { const error = await response.json(); throw new Error(error.error || `Request failed (${response.status})`) }\n const reader = response.body.getReader(); const decoder = new TextDecoder(); let buffer = ''; let finalText = ''\n while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const frames = buffer.split('\\n\\n'); buffer = frames.pop() || ''\n for (const frame of frames) { const line = frame.split('\\n').find((x) => x.startsWith('data: ')); if (!line) continue; const data = JSON.parse(line.slice(6)); if (data.type === 'status') setActivityBySession((x) => ({ ...x, [sessionId]: [...(x[sessionId] || []), { text: data.text }] })); if (data.type === 'final') finalText = data.text; if (data.type === 'error') throw new Error(data.text) }\n }\n if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: 'assistant', text: finalText || '\u0E07\u0E32\u0E19\u0E40\u0E2A\u0E23\u0E47\u0E08\u0E41\u0E25\u0E49\u0E27 \u0E41\u0E15\u0E48\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E04\u0E27\u0E32\u0E21\u0E2A\u0E23\u0E38\u0E1B' }] })); await refreshSessions(sessionId)\n } catch (error) { if (session?.id === sessionId) setSession((x) => ({ ...x, messages: [...x.messages, { role: 'assistant', text: `\u0E40\u0E01\u0E34\u0E14\u0E02\u0E49\u0E2D\u0E1C\u0E34\u0E14\u0E1E\u0E25\u0E32\u0E14: ${error.message}` }] })) }\n finally { setActivityBySession((x) => ({ ...x, [sessionId]: [] })); setRunningBySession((x) => ({ ...x, [sessionId]: false })) }\n }\n\n return <main className=\"codex-chat-app\">\n <aside className=\"codex-chat-sidebar\"><button className=\"codex-chat-new\" onClick={newSession}>\uFF0B New chat</button><div className=\"codex-chat-sessionList\">{sessions.map((item) => <div className=\"codex-chat-sessionRow\" key={item.id}><button className={item.id === session?.id ? 'session activeSession' : 'session'} onClick={() => choose(item.id)}>{item.title}<small>{new Date(item.updatedAt).toLocaleString()}</small></button><div className=\"codex-chat-sessionActions\"><button title=\"Archive\" onClick={() => archive(item.id)}>\u232B</button><button title=\"Delete\" onClick={() => remove(item.id)}>\u00D7</button></div></div>)}</div><button className=\"codex-chat-settingsBtn\" onClick={openSettings}>\u2699 Settings</button></aside>\n <section className=\"codex-chat-shell\"><header><div><b>{title}</b><span>{settings.model} \u00B7 {homeLabel}</span></div><div className=\"codex-chat-headerActions\"><button className=\"codex-chat-sessionsBtn\" onClick={() => setShowSessions(true)}>\u2630 Chats</button><button className=\"codex-chat-modelBtn\" onClick={openSettings}>\u2304 Model</button><span className=\"codex-chat-status\">\u25CF Ready</span></div></header><section className=\"codex-chat-messages\" aria-live=\"polite\">{!session ? <div className=\"codex-chat-welcome\">\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 session\u2026</div> : <>{session.messages.length === 0 && <div className=\"codex-chat-welcome\"><h1>\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E38\u0E22\u0E01\u0E31\u0E1A Codex</h1><p>\u0E40\u0E25\u0E37\u0E2D\u0E01 model \u0E2B\u0E23\u0E37\u0E2D provider \u0E02\u0E2D\u0E07\u0E04\u0E38\u0E13\u0E44\u0E14\u0E49\u0E08\u0E32\u0E01 Settings</p></div>}{session.messages.map((message, index) => message.role === 'system' ? <div className=\"codex-chat-systemMessage\" key={index}>{message.text}</div> : <article className={message.role} key={index}><label>{message.role === 'user' ? '\u0E04\u0E38\u0E13' : '\u0E04\u0E33\u0E15\u0E2D\u0E1A'}</label>{message.role === 'assistant' ? <div className=\"codex-chat-markdown\"><ReactMarkdown remarkPlugins={[remarkGfm]}>{message.text}</ReactMarkdown></div> : <pre>{message.text}</pre>}</article>)}{runningBySession[session.id] && <article className=\"codex-chat-assistant progress\"><label>\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19</label><ol>{(activityBySession[session.id] || []).map((item, index) => <li key={index} className={index === (activityBySession[session.id] || []).length - 1 ? 'active' : ''}>{item.text}</li>)}</ol></article>}</>}</section><div className=\"codex-chat-composer\"><div className=\"codex-chat-modelFooter\">{settings.provider} \u00B7 {settings.model}</div><form onSubmit={send}><textarea value={prompt} onChange={(e) => setPrompt(e.target.value)} placeholder=\"\u0E40\u0E0A\u0E48\u0E19 \u0E15\u0E23\u0E27\u0E08\u0E44\u0E1F\u0E25\u0E4C\u0E43\u0E19 workspace \u0E41\u0E25\u0E49\u0E27\u0E2A\u0E23\u0E38\u0E1B\u0E42\u0E04\u0E23\u0E07\u0E2A\u0E23\u0E49\u0E32\u0E07\" rows=\"3\" maxLength=\"8000\" /><button disabled={runningBySession[session?.id] || !prompt.trim() || !session}>{runningBySession[session?.id] ? '\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19' : '\u0E2A\u0E48\u0E07'}</button></form></div></section>\n {showSessions && <div className=\"codex-chat-sessionDrawer\"><section><header><b>Chats</b><button onClick={() => setShowSessions(false)}>\u00D7</button></header><button className=\"codex-chat-new\" onClick={async () => { await newSession(); setShowSessions(false) }}>\uFF0B New chat</button><div className=\"codex-chat-sessionList\">{sessions.map((item) => <div className=\"codex-chat-sessionRow\" key={item.id}><button className={item.id === session?.id ? 'session activeSession' : 'session'} onClick={async () => { await choose(item.id); setShowSessions(false) }}>{item.title}</button><div className=\"codex-chat-sessionActions\"><button onClick={() => archive(item.id)}>\u232B</button><button onClick={() => remove(item.id)}>\u00D7</button></div></div>)}</div></section></div>}\n {showSettings && <div className=\"codex-chat-modal\"><section className=\"codex-chat-settings\"><header><b>Settings</b><button onClick={() => setShowSettings(false)}>\u00D7</button></header><label>Provider<select value={settings.provider} onChange={(e) => { const next = { ...settings, provider: e.target.value }; setSettings(next); loadModels(next) }}><option value=\"server\">OpenRouter key \u0E2B\u0E25\u0E31\u0E01\u0E02\u0E2D\u0E07\u0E23\u0E30\u0E1A\u0E1A</option><option value=\"openrouter\">OpenRouter key \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19</option><option value=\"compatible\">OpenAI-compatible provider \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19</option><option value=\"google-ai-proxy\">Google AI (server key, Responses adapter)</option></select></label>{settings.provider !== 'server' && <label>API key<input type=\"password\" value={settings.apiKey} placeholder={settings.provider === 'google-ai-proxy' ? 'Google AI API key (override server key)' : 'API key \u0E08\u0E30\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E43\u0E19 global settings'} onChange={(e) => setSettings({ ...settings, apiKey: e.target.value })}/></label>}{settings.provider === 'compatible' && <label>Base URL<input value={settings.baseUrl} placeholder=\"https://provider.example/v1\" onChange={(e) => setSettings({ ...settings, baseUrl: e.target.value })}/></label>}<label>Model{settings.provider === 'compatible' ? <input value={settings.model} placeholder=\"provider/model-id\" onChange={(e) => setSettings({ ...settings, model: e.target.value })}/> : <select value={settings.model} onChange={(e) => setSettings({ ...settings, model: e.target.value })}>{models.length ? models.map((model) => <option key={model.id} value={model.id}>{model.name} \u2014 {model.id}</option>) : <option value={settings.model}>{modelsState || settings.model}</option>}</select>}<small className=\"codex-chat-catalogStatus\">{modelsState}</small></label><p>Settings \u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14\u0E40\u0E1B\u0E47\u0E19 global \u0E41\u0E25\u0E30\u0E40\u0E01\u0E47\u0E1A\u0E43\u0E19 SQLite volume \u0E02\u0E2D\u0E07 service</p><button className=\"codex-chat-save\" onClick={saveGlobalSettings}>\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01</button></section></div>}\n </main>\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|