kiokuko-dsh 0.2.3 → 0.2.4
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/dist/client.cjs +223 -157
- package/dist/dsh/models/profiles.d.ts +2 -0
- package/dist/dsh/models/profiles.d.ts.map +1 -1
- package/dist/dsh/models/profiles.js +9 -5
- package/dist/dsh/models/profiles.js.map +1 -1
- package/dist/dsh/models/service.d.ts +24 -1
- package/dist/dsh/models/service.d.ts.map +1 -1
- package/dist/dsh/models/service.js +85 -42
- package/dist/dsh/models/service.js.map +1 -1
- package/dist/locale/en.json +1 -1
- package/dist/locale/ja.json +1 -1
- package/dist/locale/ko.json +1 -1
- package/dist/locale/zh-CN.json +1 -1
- package/dist/models-client.cjs +223 -157
- package/dist/models-client.d.ts.map +1 -1
- package/dist/models-client.js +261 -166
- package/dist/models-client.js.map +1 -1
- package/docs/kiokuko-models.md +25 -13
- package/package.json +1 -1
package/dist/models-client.js
CHANGED
|
@@ -32,6 +32,8 @@ const MODEL_CONNECTION_DEFAULTS = {
|
|
|
32
32
|
ollama: ["Ollama", "http://localhost:11434/v1", "chat-completions"],
|
|
33
33
|
custom: ["Custom connection", "http://localhost:8080/v1", "chat-completions"],
|
|
34
34
|
};
|
|
35
|
+
/** Providers whose stored credential can also be an OAuth sign-in. */
|
|
36
|
+
const OAUTH_KINDS = ["codex", "claude", "xai", "openai", "openrouter", "nous"];
|
|
35
37
|
async function modelsApi(body, query = "") {
|
|
36
38
|
const response = await fetch("/api/kiokuko.models" + query, {
|
|
37
39
|
...(body
|
|
@@ -59,6 +61,10 @@ function KiokukoModelsPanel(props) {
|
|
|
59
61
|
reasoning: false,
|
|
60
62
|
image: false,
|
|
61
63
|
});
|
|
64
|
+
// Both disclosures are state-held: a rerender that shifts the leading
|
|
65
|
+
// status paragraphs remounts the editor, and a DOM-held `open` would snap
|
|
66
|
+
// shut under the user mid-task.
|
|
67
|
+
const [advanced, setAdvanced] = useState(false), [manualOpen, setManualOpen] = useState(false);
|
|
62
68
|
const operation = useRef(undefined), mounted = useRef(true), working = useRef(false);
|
|
63
69
|
const sessionId = props.sessionId;
|
|
64
70
|
const load = async () => {
|
|
@@ -102,13 +108,17 @@ function KiokukoModelsPanel(props) {
|
|
|
102
108
|
setError("");
|
|
103
109
|
setModels([]);
|
|
104
110
|
try {
|
|
105
|
-
|
|
111
|
+
const list = (await modelsApi(undefined, "?op=models&id=" + encodeURIComponent(c.id)));
|
|
112
|
+
setModels(list);
|
|
113
|
+
return list;
|
|
106
114
|
}
|
|
107
115
|
catch (e) {
|
|
108
116
|
setError(messageOf(e));
|
|
117
|
+
return [];
|
|
109
118
|
}
|
|
110
119
|
};
|
|
111
|
-
|
|
120
|
+
/** Store the editor's current fields. Every action below needs this first. */
|
|
121
|
+
const persist = async () => {
|
|
112
122
|
await modelsApi({
|
|
113
123
|
op: "save",
|
|
114
124
|
connection: {
|
|
@@ -120,11 +130,43 @@ function KiokukoModelsPanel(props) {
|
|
|
120
130
|
serviceTier: connection.serviceTier,
|
|
121
131
|
models: connection.models,
|
|
122
132
|
},
|
|
123
|
-
revision:
|
|
133
|
+
revision: (await load()).revision,
|
|
124
134
|
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
135
|
+
};
|
|
136
|
+
const reopen = async () => {
|
|
137
|
+
const saved = await load();
|
|
138
|
+
return choose(saved.connections.find((c) => c.id === connection.id) ?? connection);
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* The whole add-a-provider flow in one action: store the connection, store the
|
|
142
|
+
* typed key, then ask the adapter which models it serves. A discovery that
|
|
143
|
+
* fails still leaves the connection and key stored, so the panel reports the
|
|
144
|
+
* cause and keeps the manual registration path instead of discarding work.
|
|
145
|
+
*/
|
|
146
|
+
const connect = async () => {
|
|
147
|
+
const draft = { ...connection };
|
|
148
|
+
await persist();
|
|
149
|
+
const typed = key.trim();
|
|
150
|
+
if (typed) {
|
|
151
|
+
await modelsApi({ op: "key", id: draft.id, key: typed });
|
|
152
|
+
setKey("");
|
|
153
|
+
}
|
|
154
|
+
let failure = "";
|
|
155
|
+
try {
|
|
156
|
+
await modelsApi({ op: "refresh", id: draft.id });
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
failure = messageOf(e);
|
|
160
|
+
}
|
|
161
|
+
const list = await reopen();
|
|
162
|
+
if (failure) {
|
|
163
|
+
setNotice(typed ? "Connection and key saved" : "Connection saved");
|
|
164
|
+
setError(failure + " Register a model manually if this endpoint stays unusable.");
|
|
165
|
+
}
|
|
166
|
+
else
|
|
167
|
+
setNotice(list.length
|
|
168
|
+
? "Connected · " + list.length + " model(s)"
|
|
169
|
+
: "Connected; no models discovered yet");
|
|
128
170
|
};
|
|
129
171
|
const newConnection = (kind) => {
|
|
130
172
|
const spec = MODEL_CONNECTION_DEFAULTS[kind];
|
|
@@ -140,10 +182,11 @@ function KiokukoModelsPanel(props) {
|
|
|
140
182
|
setModels([]);
|
|
141
183
|
setKey("");
|
|
142
184
|
setError("");
|
|
185
|
+
setNotice("");
|
|
143
186
|
};
|
|
144
187
|
const update = (field, value) => setConnection({ ...connection, [field]: value });
|
|
145
188
|
const startLogin = async () => {
|
|
146
|
-
await
|
|
189
|
+
await persist();
|
|
147
190
|
const result = await modelsApi({ op: "login", id: connection.id });
|
|
148
191
|
operation.current = result.operationId;
|
|
149
192
|
setAuth({ id: result.operationId, status: "pending", events: [] });
|
|
@@ -159,11 +202,10 @@ function KiokukoModelsPanel(props) {
|
|
|
159
202
|
setAuth(next);
|
|
160
203
|
if (next.status !== "pending") {
|
|
161
204
|
operation.current = undefined;
|
|
162
|
-
void
|
|
163
|
-
.then((
|
|
164
|
-
if (next.status
|
|
165
|
-
|
|
166
|
-
setError(next.error ?? "Authentication cancelled");
|
|
205
|
+
void reopen()
|
|
206
|
+
.then(() => {
|
|
207
|
+
if (next.status !== "success")
|
|
208
|
+
setError(next.error ?? "Authentication cancelled");
|
|
167
209
|
})
|
|
168
210
|
.catch((e) => setError(messageOf(e)));
|
|
169
211
|
}
|
|
@@ -177,7 +219,7 @@ function KiokukoModelsPanel(props) {
|
|
|
177
219
|
clearInterval(timer);
|
|
178
220
|
};
|
|
179
221
|
}, [auth?.id, auth?.status]);
|
|
180
|
-
const input = (label, value, onChange, type = "text") => jsx("label", {
|
|
222
|
+
const input = (label, value, onChange, type = "text", submit) => jsx("label", {
|
|
181
223
|
children: jsxs(Fragment, {
|
|
182
224
|
children: [
|
|
183
225
|
jsx("span", { children: label }),
|
|
@@ -189,6 +231,16 @@ function KiokukoModelsPanel(props) {
|
|
|
189
231
|
(label === "Base URL" &&
|
|
190
232
|
["codex", "opencode", "opencode-go"].includes(connection?.kind)),
|
|
191
233
|
onChange: (e) => onChange(e.target.value),
|
|
234
|
+
...(submit
|
|
235
|
+
? {
|
|
236
|
+
onKeyDown: (e) => {
|
|
237
|
+
if (e.key !== "Enter")
|
|
238
|
+
return;
|
|
239
|
+
e.preventDefault();
|
|
240
|
+
submit();
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
: {}),
|
|
192
244
|
autoComplete: type === "password" ? "new-password" : "off",
|
|
193
245
|
}),
|
|
194
246
|
],
|
|
@@ -209,9 +261,15 @@ function KiokukoModelsPanel(props) {
|
|
|
209
261
|
jsx("p", {
|
|
210
262
|
children: "Connect directly for chat, Enno and Deep. Credentials are shared with dsh-cli. Signing out also signs dsh-cli out.",
|
|
211
263
|
}),
|
|
212
|
-
...(error
|
|
213
|
-
|
|
214
|
-
|
|
264
|
+
...(error
|
|
265
|
+
? [jsx("p", { role: "alert", children: error }, "error")]
|
|
266
|
+
: []),
|
|
267
|
+
...(notice
|
|
268
|
+
? [jsx("p", { role: "status", children: notice }, "notice")]
|
|
269
|
+
: []),
|
|
270
|
+
...(busy
|
|
271
|
+
? [jsx("p", { role: "status", children: "Working…" }, "busy")]
|
|
272
|
+
: []),
|
|
215
273
|
button("Reload", () => void run(load)),
|
|
216
274
|
jsx("label", {
|
|
217
275
|
children: jsxs(Fragment, {
|
|
@@ -239,112 +297,41 @@ function KiokukoModelsPanel(props) {
|
|
|
239
297
|
? [
|
|
240
298
|
jsxs("div", {
|
|
241
299
|
className: "kiokuko-models-editor",
|
|
300
|
+
key: "editor",
|
|
242
301
|
children: [
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
"
|
|
249
|
-
jsx("select", {
|
|
250
|
-
"aria-label": "Protocol",
|
|
251
|
-
value: connection.protocol,
|
|
252
|
-
disabled: busy ||
|
|
253
|
-
[
|
|
254
|
-
"codex",
|
|
255
|
-
"infron",
|
|
256
|
-
"opencode",
|
|
257
|
-
"opencode-go",
|
|
258
|
-
].includes(connection.kind),
|
|
259
|
-
onChange: (e) => update("protocol", e.target.value),
|
|
260
|
-
children: [
|
|
261
|
-
["chat-completions", "Chat Completions"],
|
|
262
|
-
["responses", "Responses"],
|
|
263
|
-
["messages", "Messages"],
|
|
264
|
-
].map(([id, name]) => jsx("option", { value: id, children: name }, id)),
|
|
265
|
-
}),
|
|
266
|
-
],
|
|
267
|
-
}),
|
|
268
|
-
}),
|
|
269
|
-
...(connection.kind === "infron"
|
|
270
|
-
? [
|
|
271
|
-
jsx("label", {
|
|
272
|
-
children: jsxs(Fragment, {
|
|
273
|
-
children: [
|
|
274
|
-
"Service tier",
|
|
275
|
-
jsx("select", {
|
|
276
|
-
"aria-label": "Service tier",
|
|
277
|
-
value: connection.serviceTier,
|
|
278
|
-
disabled: busy,
|
|
279
|
-
onChange: (e) => update("serviceTier", e.target.value),
|
|
280
|
-
children: [
|
|
281
|
-
jsx("option", {
|
|
282
|
-
value: "standard",
|
|
283
|
-
children: "Standard",
|
|
284
|
-
}),
|
|
285
|
-
jsx("option", {
|
|
286
|
-
value: "flex",
|
|
287
|
-
children: "Flex",
|
|
288
|
-
}),
|
|
289
|
-
],
|
|
290
|
-
}),
|
|
291
|
-
],
|
|
292
|
-
}),
|
|
302
|
+
jsxs("h3", {
|
|
303
|
+
children: [
|
|
304
|
+
connection.name,
|
|
305
|
+
jsx("span", {
|
|
306
|
+
className: "kiokuko-models-state",
|
|
307
|
+
children: " · " + (connection.authState ?? "not saved yet"),
|
|
293
308
|
}),
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
connection.serviceTier +
|
|
297
|
-
". Last request: " +
|
|
298
|
-
(connection.lastRequestedTier ?? "none") +
|
|
299
|
-
". Actual served tier: " +
|
|
300
|
-
(connection.actualTier ?? "unknown") +
|
|
301
|
-
". Infron may serve Standard for a Flex request.",
|
|
302
|
-
}),
|
|
303
|
-
]
|
|
304
|
-
: []),
|
|
305
|
-
button("Save connection", () => void run(save)),
|
|
309
|
+
],
|
|
310
|
+
}),
|
|
306
311
|
...(connection.kind !== "codex" && connection.kind !== "ollama"
|
|
307
312
|
? [
|
|
308
|
-
input("API key / bearer token", key, setKey, "password")
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
op: "key",
|
|
313
|
-
id: connection.id,
|
|
314
|
-
key,
|
|
315
|
-
});
|
|
316
|
-
setKey("");
|
|
317
|
-
await load();
|
|
318
|
-
await choose(connection);
|
|
319
|
-
setNotice("Key saved");
|
|
320
|
-
}), !key),
|
|
321
|
-
]
|
|
322
|
-
: []),
|
|
323
|
-
...([
|
|
324
|
-
"codex",
|
|
325
|
-
"claude",
|
|
326
|
-
"xai",
|
|
327
|
-
"openai",
|
|
328
|
-
"openrouter",
|
|
329
|
-
"nous",
|
|
330
|
-
].includes(connection.kind)
|
|
331
|
-
? [
|
|
332
|
-
button("Log in", () => void run(startLogin), auth?.status === "pending"),
|
|
313
|
+
input("API key / bearer token", key, setKey, "password", () => {
|
|
314
|
+
if (!busy && key.trim())
|
|
315
|
+
void run(connect);
|
|
316
|
+
}),
|
|
333
317
|
]
|
|
334
318
|
: []),
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
319
|
+
jsxs("div", {
|
|
320
|
+
className: "kiokuko-models-actions",
|
|
321
|
+
children: [
|
|
322
|
+
button("Save and connect", () => void run(connect)),
|
|
323
|
+
...(OAUTH_KINDS.includes(connection.kind)
|
|
324
|
+
? [
|
|
325
|
+
button("Log in", () => void run(startLogin), auth?.status === "pending"),
|
|
326
|
+
]
|
|
327
|
+
: []),
|
|
328
|
+
],
|
|
329
|
+
}),
|
|
330
|
+
jsx("p", {
|
|
331
|
+
children: connection.kind === "ollama"
|
|
332
|
+
? "Ollama needs no key. Save and connect to list its models."
|
|
333
|
+
: "Paste the API key and press Enter, or use Save and connect. The key is stored with dsh-cli's shared credentials.",
|
|
334
|
+
}),
|
|
348
335
|
...(auth?.status === "pending"
|
|
349
336
|
? [
|
|
350
337
|
jsxs("div", {
|
|
@@ -445,62 +432,170 @@ function KiokukoModelsPanel(props) {
|
|
|
445
432
|
}),
|
|
446
433
|
]
|
|
447
434
|
: []),
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
435
|
+
jsxs("details", {
|
|
436
|
+
open: advanced,
|
|
437
|
+
children: [
|
|
438
|
+
jsx("summary", {
|
|
439
|
+
onClick: (e) => {
|
|
440
|
+
e.preventDefault();
|
|
441
|
+
setAdvanced(!advanced);
|
|
442
|
+
},
|
|
443
|
+
children: "Advanced settings",
|
|
444
|
+
}),
|
|
445
|
+
input("Connection name", connection.name, (value) => update("name", value)),
|
|
446
|
+
input("Base URL", connection.baseURL, (value) => update("baseURL", value)),
|
|
447
|
+
jsx("label", {
|
|
451
448
|
children: jsxs(Fragment, {
|
|
452
449
|
children: [
|
|
453
|
-
|
|
454
|
-
|
|
450
|
+
"Protocol",
|
|
451
|
+
jsx("select", {
|
|
452
|
+
"aria-label": "Protocol",
|
|
453
|
+
value: connection.protocol,
|
|
454
|
+
disabled: busy ||
|
|
455
|
+
[
|
|
456
|
+
"codex",
|
|
457
|
+
"infron",
|
|
458
|
+
"opencode",
|
|
459
|
+
"opencode-go",
|
|
460
|
+
].includes(connection.kind),
|
|
461
|
+
onChange: (e) => update("protocol", e.target.value),
|
|
462
|
+
children: [
|
|
463
|
+
["chat-completions", "Chat Completions"],
|
|
464
|
+
["responses", "Responses"],
|
|
465
|
+
["messages", "Messages"],
|
|
466
|
+
].map(([id, name]) => jsx("option", { value: id, children: name }, id)),
|
|
455
467
|
}),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
468
|
+
],
|
|
469
|
+
}),
|
|
470
|
+
}),
|
|
471
|
+
...(connection.kind === "infron"
|
|
472
|
+
? [
|
|
473
|
+
jsx("label", {
|
|
474
|
+
children: jsxs(Fragment, {
|
|
475
|
+
children: [
|
|
476
|
+
"Service tier",
|
|
477
|
+
jsx("select", {
|
|
478
|
+
"aria-label": "Service tier",
|
|
479
|
+
value: connection.serviceTier,
|
|
480
|
+
disabled: busy,
|
|
481
|
+
onChange: (e) => update("serviceTier", e.target.value),
|
|
482
|
+
children: [
|
|
483
|
+
jsx("option", {
|
|
484
|
+
value: "standard",
|
|
485
|
+
children: "Standard",
|
|
474
486
|
}),
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}),
|
|
479
|
-
}),
|
|
480
|
-
jsx("label", {
|
|
481
|
-
children: jsxs(Fragment, {
|
|
482
|
-
children: [
|
|
483
|
-
jsx("input", {
|
|
484
|
-
type: "checkbox",
|
|
485
|
-
checked: manual.image,
|
|
486
|
-
onChange: (e) => setManual({
|
|
487
|
-
...manual,
|
|
488
|
-
image: e.target.checked,
|
|
487
|
+
jsx("option", {
|
|
488
|
+
value: "flex",
|
|
489
|
+
children: "Flex",
|
|
489
490
|
}),
|
|
491
|
+
],
|
|
492
|
+
}),
|
|
493
|
+
],
|
|
494
|
+
}),
|
|
495
|
+
}),
|
|
496
|
+
jsx("p", {
|
|
497
|
+
children: "Requested tier: " +
|
|
498
|
+
connection.serviceTier +
|
|
499
|
+
". Last request: " +
|
|
500
|
+
(connection.lastRequestedTier ?? "none") +
|
|
501
|
+
". Actual served tier: " +
|
|
502
|
+
(connection.actualTier ?? "unknown") +
|
|
503
|
+
". Infron may serve Standard for a Flex request.",
|
|
504
|
+
}),
|
|
505
|
+
]
|
|
506
|
+
: []),
|
|
507
|
+
button("Save connection", () => void run(async () => {
|
|
508
|
+
await persist();
|
|
509
|
+
await reopen();
|
|
510
|
+
setNotice("Connection saved");
|
|
511
|
+
})),
|
|
512
|
+
button("Refresh models", () => void run(async () => {
|
|
513
|
+
await persist();
|
|
514
|
+
await modelsApi({ op: "refresh", id: connection.id });
|
|
515
|
+
const list = await reopen();
|
|
516
|
+
setNotice(list.length
|
|
517
|
+
? "Models refreshed"
|
|
518
|
+
: "Models refreshed; none discovered");
|
|
519
|
+
})),
|
|
520
|
+
button("Log out (also dsh-cli)", () => void run(async () => {
|
|
521
|
+
await persist();
|
|
522
|
+
await modelsApi({ op: "logout", id: connection.id });
|
|
523
|
+
setKey("");
|
|
524
|
+
setModels([]);
|
|
525
|
+
await load();
|
|
526
|
+
setNotice("Signed out");
|
|
527
|
+
})),
|
|
528
|
+
...(!["opencode", "opencode-go"].includes(connection.kind)
|
|
529
|
+
? [
|
|
530
|
+
jsx("details", {
|
|
531
|
+
open: manualOpen,
|
|
532
|
+
children: jsxs(Fragment, {
|
|
533
|
+
children: [
|
|
534
|
+
jsx("summary", {
|
|
535
|
+
onClick: (e) => {
|
|
536
|
+
e.preventDefault();
|
|
537
|
+
setManualOpen(!manualOpen);
|
|
538
|
+
},
|
|
539
|
+
children: "Register model manually",
|
|
540
|
+
}),
|
|
541
|
+
input("Model ID", manual.id, (value) => setManual({ ...manual, id: value })),
|
|
542
|
+
input("Context window", manual.contextWindow, (value) => setManual({
|
|
543
|
+
...manual,
|
|
544
|
+
contextWindow: Number(value),
|
|
545
|
+
}), "number"),
|
|
546
|
+
input("Maximum output tokens", manual.maxTokens, (value) => setManual({
|
|
547
|
+
...manual,
|
|
548
|
+
maxTokens: Number(value),
|
|
549
|
+
}), "number"),
|
|
550
|
+
jsx("label", {
|
|
551
|
+
children: jsxs(Fragment, {
|
|
552
|
+
children: [
|
|
553
|
+
jsx("input", {
|
|
554
|
+
type: "checkbox",
|
|
555
|
+
checked: manual.reasoning,
|
|
556
|
+
onChange: (e) => setManual({
|
|
557
|
+
...manual,
|
|
558
|
+
reasoning: e.target.checked,
|
|
559
|
+
}),
|
|
560
|
+
}),
|
|
561
|
+
"Reasoning supported",
|
|
562
|
+
],
|
|
490
563
|
}),
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
564
|
+
}),
|
|
565
|
+
jsx("label", {
|
|
566
|
+
children: jsxs(Fragment, {
|
|
567
|
+
children: [
|
|
568
|
+
jsx("input", {
|
|
569
|
+
type: "checkbox",
|
|
570
|
+
checked: manual.image,
|
|
571
|
+
onChange: (e) => setManual({
|
|
572
|
+
...manual,
|
|
573
|
+
image: e.target.checked,
|
|
574
|
+
}),
|
|
575
|
+
}),
|
|
576
|
+
"Image input supported",
|
|
577
|
+
],
|
|
578
|
+
}),
|
|
579
|
+
}),
|
|
580
|
+
button("Add manual model", () => {
|
|
581
|
+
update("models", [
|
|
582
|
+
...connection.models.filter((m) => m.id !== manual.id),
|
|
583
|
+
{ ...manual, name: manual.id },
|
|
584
|
+
]);
|
|
585
|
+
setNotice("Model added to draft. Save connection to register it.");
|
|
586
|
+
}, !manual.id),
|
|
587
|
+
],
|
|
494
588
|
}),
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
589
|
+
}),
|
|
590
|
+
]
|
|
591
|
+
: []),
|
|
592
|
+
],
|
|
593
|
+
}),
|
|
594
|
+
jsx("h4", { children: "Models" }),
|
|
595
|
+
...(models.length === 0
|
|
596
|
+
? [
|
|
597
|
+
jsx("p", {
|
|
598
|
+
children: "No models yet. Save and connect to discover them, or register one under Advanced settings.",
|
|
504
599
|
}),
|
|
505
600
|
]
|
|
506
601
|
: []),
|
|
@@ -600,7 +695,7 @@ export function mountModelsClient(ctx) {
|
|
|
600
695
|
ctx.effect(() => {
|
|
601
696
|
const style = document.createElement("style");
|
|
602
697
|
style.textContent =
|
|
603
|
-
".kiokuko-models-modal{width:min(900px,calc(100vw - 32px))}.kiokuko-models-panel{box-sizing:border-box;width:100%;max-width:900px;padding:16px;overflow:auto;max-height:75vh}.kiokuko-models-panel label{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:12px 0}.kiokuko-models-panel input,.kiokuko-models-panel select{max-width:100%;box-sizing:border-box;border:1px solid #888;border-radius:6px;padding:8px;color:inherit;background:transparent}.kiokuko-models-panel button{border:1px solid #888;border-radius:6px;padding:8px;margin:4px;color:inherit}.kiokuko-models-panel button:disabled{opacity:.5}.kiokuko-models-panel li{margin:8px 0}.kiokuko-models-panel :focus-visible{outline:2px solid #3286ff;outline-offset:3px}";
|
|
698
|
+
".kiokuko-models-modal{width:min(900px,calc(100vw - 32px))}.kiokuko-models-panel{box-sizing:border-box;width:100%;max-width:900px;padding:16px;overflow:auto;max-height:75vh}.kiokuko-models-panel label{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin:12px 0}.kiokuko-models-panel input,.kiokuko-models-panel select{max-width:100%;box-sizing:border-box;border:1px solid #888;border-radius:6px;padding:8px;color:inherit;background:transparent}.kiokuko-models-panel button{border:1px solid #888;border-radius:6px;padding:8px;margin:4px;color:inherit}.kiokuko-models-panel button:disabled{opacity:.5}.kiokuko-models-panel li{margin:8px 0}.kiokuko-models-panel :focus-visible{outline:2px solid #3286ff;outline-offset:3px}.kiokuko-models-panel summary{cursor:pointer;margin:12px 0}.kiokuko-models-panel h3,.kiokuko-models-panel h4{margin:12px 0 0}.kiokuko-models-state{font-weight:400;font-size:.85em;opacity:.75}.kiokuko-models-actions{display:flex;gap:8px;align-items:center;flex-wrap:wrap}";
|
|
604
699
|
document.head.append(style);
|
|
605
700
|
return () => style.remove();
|
|
606
701
|
}, "kiokuko models style");
|