@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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Suphot (pemo)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# @pmcai/codex-chat-widget
|
|
2
|
+
|
|
3
|
+
Embeddable React chat UI for a **Codex harness** server (the Next.js + `codex exec` service
|
|
4
|
+
in this repo). Drop it into any React / Next.js / Vite site and it drives the existing API:
|
|
5
|
+
|
|
6
|
+
```
|
|
7
|
+
GET /api/health
|
|
8
|
+
GET /api/sessions POST /api/sessions PATCH/DELETE /api/sessions?id=
|
|
9
|
+
GET /api/settings PUT /api/settings
|
|
10
|
+
POST /api/models
|
|
11
|
+
POST /api/chat (SSE stream: status / final / error)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The widget is **frontend only**: it never holds API keys and never runs Codex itself.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @pmcai/codex-chat-widget
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Peer dependencies (install in your app):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install react react-dom react-markdown remark-gfm
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Use
|
|
29
|
+
|
|
30
|
+
```jsx
|
|
31
|
+
import CodexChatWidget from '@pmcai/codex-chat-widget'
|
|
32
|
+
import '@pmcai/codex-chat-widget/styles.css'
|
|
33
|
+
|
|
34
|
+
export default function Page() {
|
|
35
|
+
return (
|
|
36
|
+
<div style={{ height: '100dvh' }}>
|
|
37
|
+
<CodexChatWidget
|
|
38
|
+
apiBase="https://f90f-124-120-29-18.ngrok-free.app"
|
|
39
|
+
storageKey="my-site"
|
|
40
|
+
title="Codex Harness"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Without a bundler (CDN / plain HTML) — see `examples/static.html` in this repo:
|
|
48
|
+
|
|
49
|
+
```html
|
|
50
|
+
<link rel="stylesheet" href="https://unpkg.com/@pmcai/codex-chat-widget/dist/styles.css" />
|
|
51
|
+
<script type="module">
|
|
52
|
+
import CodexChatWidget from 'https://unpkg.com/@pmcai/codex-chat-widget/dist/index.js'
|
|
53
|
+
// mount with your own React root
|
|
54
|
+
</script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Props
|
|
58
|
+
|
|
59
|
+
| Prop | Type | Default | Description |
|
|
60
|
+
|------|------|---------|-------------|
|
|
61
|
+
| `apiBase` | `string` | `''` (same origin) | Base URL of the Codex harness server. Trailing `/` is stripped. |
|
|
62
|
+
| `storageKey` | `string` | `'codex-chat'` | Namespace for the remembered active session in `localStorage`. Use a unique value per host site. |
|
|
63
|
+
| `initialSessionId` | `string` | — | Session to open on first mount (overrides the remembered one). |
|
|
64
|
+
| `title` | `string` | `'Codex Harness'` | Header title. |
|
|
65
|
+
|
|
66
|
+
## Behaviour
|
|
67
|
+
|
|
68
|
+
- **Parallel sessions** — each session streams independently; starting a new chat does not
|
|
69
|
+
block a running one. Progress steps render per session.
|
|
70
|
+
- **Session memory** — the active session id is stored under
|
|
71
|
+
`<storageKey>-active-session`, scoped per host site.
|
|
72
|
+
- **Model settings** — the Settings modal reads providers/models from `/api/models` and saves
|
|
73
|
+
globally through `PUT /api/settings`.
|
|
74
|
+
- **Styles** are fully scoped under `.codex-chat-app`, so the widget will not leak CSS into
|
|
75
|
+
your site. Global `html`/`body` resets from the standalone app are not included.
|
|
76
|
+
|
|
77
|
+
## Server contract
|
|
78
|
+
|
|
79
|
+
The widget expects the harness API to answer with these shapes:
|
|
80
|
+
|
|
81
|
+
```jsonc
|
|
82
|
+
// GET /api/sessions -> [{ id, title, updatedAt }]
|
|
83
|
+
// GET /api/sessions?id=... -> { id, title, messages: [{ role: "user"|"assistant"|"system", text }] }
|
|
84
|
+
// POST /api/chat { sessionId, prompt, settings } -> text/event-stream
|
|
85
|
+
// data: {"type":"status","text":"..."} // progress step
|
|
86
|
+
// data: {"type":"final","text":"..."} // final answer
|
|
87
|
+
// data: {"type":"error","text":"..."} // failure
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Build from source
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run build # esbuild -> dist/index.js, dist/index.cjs, dist/styles.css
|
|
94
|
+
npm test # react-dom/server smoke render
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Publish
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm login
|
|
101
|
+
npm publish --access public
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
|
|
106
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/CodexChatWidget.jsx
|
|
30
|
+
var CodexChatWidget_exports = {};
|
|
31
|
+
__export(CodexChatWidget_exports, {
|
|
32
|
+
default: () => CodexChatWidget
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(CodexChatWidget_exports);
|
|
35
|
+
var import_react = require("react");
|
|
36
|
+
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
37
|
+
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
38
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
var defaultSettings = { provider: "server", model: "cohere/north-mini-code:free", apiKey: "", baseUrl: "" };
|
|
40
|
+
function CodexChatWidget({
|
|
41
|
+
apiBase = "",
|
|
42
|
+
storageKey = "codex-chat",
|
|
43
|
+
initialSessionId,
|
|
44
|
+
title = "Codex Harness"
|
|
45
|
+
}) {
|
|
46
|
+
const apiBaseUrl = String(apiBase || "").replace(/\/$/, "");
|
|
47
|
+
const [sessions, setSessions] = (0, import_react.useState)([]);
|
|
48
|
+
const [session, setSession] = (0, import_react.useState)(null);
|
|
49
|
+
const [prompt, setPrompt] = (0, import_react.useState)("");
|
|
50
|
+
const [activityBySession, setActivityBySession] = (0, import_react.useState)({});
|
|
51
|
+
const [runningBySession, setRunningBySession] = (0, import_react.useState)({});
|
|
52
|
+
const [settings, setSettings] = (0, import_react.useState)(defaultSettings);
|
|
53
|
+
const [models, setModels] = (0, import_react.useState)([]);
|
|
54
|
+
const [modelsState, setModelsState] = (0, import_react.useState)("");
|
|
55
|
+
const [showSettings, setShowSettings] = (0, import_react.useState)(false);
|
|
56
|
+
const [showSessions, setShowSessions] = (0, import_react.useState)(false);
|
|
57
|
+
const [homeLabel, setHomeLabel] = (0, import_react.useState)("~/.pmai/workspace");
|
|
58
|
+
async function refreshSessions(selectId) {
|
|
59
|
+
let items = await (await fetch(apiBaseUrl + "/api/sessions")).json();
|
|
60
|
+
if (!items.length) {
|
|
61
|
+
const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
|
|
62
|
+
items = [created];
|
|
63
|
+
}
|
|
64
|
+
setSessions(items);
|
|
65
|
+
const wanted = selectId || session?.id;
|
|
66
|
+
const id = items.some((item) => item.id === wanted) ? wanted : items[0].id;
|
|
67
|
+
const response = await fetch(apiBaseUrl + `/api/sessions?id=${id}`);
|
|
68
|
+
if (response.ok) {
|
|
69
|
+
setSession(await response.json());
|
|
70
|
+
localStorage.setItem(`${storageKey}-active-session`, id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
(0, import_react.useEffect)(() => {
|
|
74
|
+
const savedSession = localStorage.getItem(`${storageKey}-active-session`);
|
|
75
|
+
refreshSessions(initialSessionId || savedSession || void 0);
|
|
76
|
+
fetch(apiBaseUrl + "/api/settings").then((r) => r.json()).then(setSettings);
|
|
77
|
+
fetch(apiBaseUrl + "/api/health").then((r) => r.json()).then((h) => {
|
|
78
|
+
if (h.workspace) setHomeLabel("~/.pmai" + String(h.workspace).replace(/^\/pmai/, ""));
|
|
79
|
+
}).catch(() => {
|
|
80
|
+
});
|
|
81
|
+
}, []);
|
|
82
|
+
async function loadModels(nextSettings = settings) {
|
|
83
|
+
setModelsState("\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 model list\u2026");
|
|
84
|
+
const data = await (await fetch(apiBaseUrl + "/api/models", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(nextSettings), cache: "no-store" })).json();
|
|
85
|
+
setModels(data.models || []);
|
|
86
|
+
setModelsState(data.error || data.message || data.label || `${(data.models || []).length} models`);
|
|
87
|
+
}
|
|
88
|
+
async function openSettings() {
|
|
89
|
+
setShowSettings(true);
|
|
90
|
+
await loadModels();
|
|
91
|
+
}
|
|
92
|
+
async function saveGlobalSettings() {
|
|
93
|
+
const previous = `${settings.provider}/${settings.model}`;
|
|
94
|
+
const saved = await (await fetch(apiBaseUrl + "/api/settings", { method: "PUT", headers: { "content-type": "application/json" }, body: JSON.stringify(settings) })).json();
|
|
95
|
+
setSettings(saved);
|
|
96
|
+
if (session && `${saved.provider}/${saved.model}` !== previous) setSession((current) => ({ ...current, messages: [...current.messages, { role: "system", text: `Model changed: ${saved.model} (${saved.provider})` }] }));
|
|
97
|
+
setShowSettings(false);
|
|
98
|
+
}
|
|
99
|
+
async function newSession() {
|
|
100
|
+
const created = await (await fetch(apiBaseUrl + "/api/sessions", { method: "POST" })).json();
|
|
101
|
+
await refreshSessions(created.id);
|
|
102
|
+
}
|
|
103
|
+
async function choose(id) {
|
|
104
|
+
await refreshSessions(id);
|
|
105
|
+
}
|
|
106
|
+
async function archive(id) {
|
|
107
|
+
await fetch(apiBaseUrl + "/api/sessions", { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ id, archived: true }) });
|
|
108
|
+
await refreshSessions();
|
|
109
|
+
}
|
|
110
|
+
async function remove(id) {
|
|
111
|
+
if (!confirm("Delete this chat permanently?")) return;
|
|
112
|
+
await fetch(apiBaseUrl + `/api/sessions?id=${id}`, { method: "DELETE" });
|
|
113
|
+
if (session?.id === id) setSession(null);
|
|
114
|
+
await refreshSessions();
|
|
115
|
+
}
|
|
116
|
+
async function send(event) {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
const text = prompt.trim();
|
|
119
|
+
const sessionId = session?.id;
|
|
120
|
+
if (!text || runningBySession[sessionId] || !sessionId) return;
|
|
121
|
+
setSession((x) => ({ ...x, messages: [...x.messages, { role: "user", text }] }));
|
|
122
|
+
setPrompt("");
|
|
123
|
+
setActivityBySession((x) => ({ ...x, [sessionId]: [{ text: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E40\u0E15\u0E23\u0E35\u0E22\u0E21\u0E07\u0E32\u0E19" }] }));
|
|
124
|
+
setRunningBySession((x) => ({ ...x, [sessionId]: true }));
|
|
125
|
+
try {
|
|
126
|
+
const response = await fetch(apiBaseUrl + "/api/chat", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ prompt: text, sessionId, settings }) });
|
|
127
|
+
if (!response.ok || !response.body) {
|
|
128
|
+
const error = await response.json();
|
|
129
|
+
throw new Error(error.error || `Request failed (${response.status})`);
|
|
130
|
+
}
|
|
131
|
+
const reader = response.body.getReader();
|
|
132
|
+
const decoder = new TextDecoder();
|
|
133
|
+
let buffer = "";
|
|
134
|
+
let finalText = "";
|
|
135
|
+
while (true) {
|
|
136
|
+
const { done, value } = await reader.read();
|
|
137
|
+
if (done) break;
|
|
138
|
+
buffer += decoder.decode(value, { stream: true });
|
|
139
|
+
const frames = buffer.split("\n\n");
|
|
140
|
+
buffer = frames.pop() || "";
|
|
141
|
+
for (const frame of frames) {
|
|
142
|
+
const line = frame.split("\n").find((x) => x.startsWith("data: "));
|
|
143
|
+
if (!line) continue;
|
|
144
|
+
const data = JSON.parse(line.slice(6));
|
|
145
|
+
if (data.type === "status") setActivityBySession((x) => ({ ...x, [sessionId]: [...x[sessionId] || [], { text: data.text }] }));
|
|
146
|
+
if (data.type === "final") finalText = data.text;
|
|
147
|
+
if (data.type === "error") throw new Error(data.text);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
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" }] }));
|
|
151
|
+
await refreshSessions(sessionId);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
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}` }] }));
|
|
154
|
+
} finally {
|
|
155
|
+
setActivityBySession((x) => ({ ...x, [sessionId]: [] }));
|
|
156
|
+
setRunningBySession((x) => ({ ...x, [sessionId]: false }));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { className: "codex-chat-app", children: [
|
|
160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("aside", { className: "codex-chat-sidebar", children: [
|
|
161
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-new", onClick: newSession, children: "\uFF0B New chat" }),
|
|
162
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionRow", children: [
|
|
163
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: () => choose(item.id), children: [
|
|
164
|
+
item.title,
|
|
165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { children: new Date(item.updatedAt).toLocaleString() })
|
|
166
|
+
] }),
|
|
167
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionActions", children: [
|
|
168
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { title: "Archive", onClick: () => archive(item.id), children: "\u232B" }),
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { title: "Delete", onClick: () => remove(item.id), children: "\xD7" })
|
|
170
|
+
] })
|
|
171
|
+
] }, item.id)) }),
|
|
172
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-settingsBtn", onClick: openSettings, children: "\u2699 Settings" })
|
|
173
|
+
] }),
|
|
174
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "codex-chat-shell", children: [
|
|
175
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
|
|
176
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
177
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: title }),
|
|
178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [
|
|
179
|
+
settings.model,
|
|
180
|
+
" \xB7 ",
|
|
181
|
+
homeLabel
|
|
182
|
+
] })
|
|
183
|
+
] }),
|
|
184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-headerActions", children: [
|
|
185
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-sessionsBtn", onClick: () => setShowSessions(true), children: "\u2630 Chats" }),
|
|
186
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-modelBtn", onClick: openSettings, children: "\u2304 Model" }),
|
|
187
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "codex-chat-status", children: "\u25CF Ready" })
|
|
188
|
+
] })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("section", { className: "codex-chat-messages", "aria-live": "polite", children: !session ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-welcome", children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14 session\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
191
|
+
session.messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-welcome", children: [
|
|
192
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { children: "\u0E40\u0E23\u0E34\u0E48\u0E21\u0E04\u0E38\u0E22\u0E01\u0E31\u0E1A Codex" }),
|
|
193
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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" })
|
|
194
|
+
] }),
|
|
195
|
+
session.messages.map((message, index) => message.role === "system" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-systemMessage", children: message.text }, index) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: message.role, children: [
|
|
196
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: message.role === "user" ? "\u0E04\u0E38\u0E13" : "\u0E04\u0E33\u0E15\u0E2D\u0E1A" }),
|
|
197
|
+
message.role === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-markdown", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], children: message.text }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { children: message.text })
|
|
198
|
+
] }, index)),
|
|
199
|
+
runningBySession[session.id] && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: "codex-chat-assistant progress", children: [
|
|
200
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { children: "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E17\u0E33\u0E07\u0E32\u0E19" }),
|
|
201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { children: (activityBySession[session.id] || []).map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: index === (activityBySession[session.id] || []).length - 1 ? "active" : "", children: item.text }, index)) })
|
|
202
|
+
] })
|
|
203
|
+
] }) }),
|
|
204
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-composer", children: [
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-modelFooter", children: [
|
|
206
|
+
settings.provider,
|
|
207
|
+
" \xB7 ",
|
|
208
|
+
settings.model
|
|
209
|
+
] }),
|
|
210
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", { onSubmit: send, children: [
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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" }),
|
|
212
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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" })
|
|
213
|
+
] })
|
|
214
|
+
] })
|
|
215
|
+
] }),
|
|
216
|
+
showSessions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionDrawer", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { children: [
|
|
217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
|
|
218
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Chats" }),
|
|
219
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setShowSessions(false), children: "\xD7" })
|
|
220
|
+
] }),
|
|
221
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-new", onClick: async () => {
|
|
222
|
+
await newSession();
|
|
223
|
+
setShowSessions(false);
|
|
224
|
+
}, children: "\uFF0B New chat" }),
|
|
225
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-sessionList", children: sessions.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionRow", children: [
|
|
226
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: item.id === session?.id ? "session activeSession" : "session", onClick: async () => {
|
|
227
|
+
await choose(item.id);
|
|
228
|
+
setShowSessions(false);
|
|
229
|
+
}, children: item.title }),
|
|
230
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "codex-chat-sessionActions", children: [
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => archive(item.id), children: "\u232B" }),
|
|
232
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => remove(item.id), children: "\xD7" })
|
|
233
|
+
] })
|
|
234
|
+
] }, item.id)) })
|
|
235
|
+
] }) }),
|
|
236
|
+
showSettings && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "codex-chat-modal", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "codex-chat-settings", children: [
|
|
237
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", { children: [
|
|
238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("b", { children: "Settings" }),
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: () => setShowSettings(false), children: "\xD7" })
|
|
240
|
+
] }),
|
|
241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
242
|
+
"Provider",
|
|
243
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("select", { value: settings.provider, onChange: (e) => {
|
|
244
|
+
const next = { ...settings, provider: e.target.value };
|
|
245
|
+
setSettings(next);
|
|
246
|
+
loadModels(next);
|
|
247
|
+
}, children: [
|
|
248
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "server", children: "OpenRouter key \u0E2B\u0E25\u0E31\u0E01\u0E02\u0E2D\u0E07\u0E23\u0E30\u0E1A\u0E1A" }),
|
|
249
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "openrouter", children: "OpenRouter key \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
|
|
250
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "compatible", children: "OpenAI-compatible provider \u0E02\u0E2D\u0E07\u0E09\u0E31\u0E19" }),
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: "google-ai-proxy", children: "Google AI (server key, Responses adapter)" })
|
|
252
|
+
] })
|
|
253
|
+
] }),
|
|
254
|
+
settings.provider !== "server" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
255
|
+
"API key",
|
|
256
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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 }) })
|
|
257
|
+
] }),
|
|
258
|
+
settings.provider === "compatible" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
259
|
+
"Base URL",
|
|
260
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: settings.baseUrl, placeholder: "https://provider.example/v1", onChange: (e) => setSettings({ ...settings, baseUrl: e.target.value }) })
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { children: [
|
|
263
|
+
"Model",
|
|
264
|
+
settings.provider === "compatible" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { value: settings.model, placeholder: "provider/model-id", onChange: (e) => setSettings({ ...settings, model: e.target.value }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { value: settings.model, onChange: (e) => setSettings({ ...settings, model: e.target.value }), children: models.length ? models.map((model) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("option", { value: model.id, children: [
|
|
265
|
+
model.name,
|
|
266
|
+
" \u2014 ",
|
|
267
|
+
model.id
|
|
268
|
+
] }, model.id)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: settings.model, children: modelsState || settings.model }) }),
|
|
269
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("small", { className: "codex-chat-catalogStatus", children: modelsState })
|
|
270
|
+
] }),
|
|
271
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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" }),
|
|
272
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "codex-chat-save", onClick: saveGlobalSettings, children: "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01" })
|
|
273
|
+
] }) })
|
|
274
|
+
] });
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=index.cjs.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": ["ReactMarkdown", "remarkGfm"]
|
|
7
|
+
}
|