@kolbo/mcp 1.88.3 → 1.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/package.json +1 -1
- package/skill/GENERATED.md +1 -1
- package/skill/SKILL.md +6 -4
- package/skill/VERSION +1 -1
- package/skill/references/models/gpt-image.md +4 -3
- package/skill/references/workflows/cost-and-validation.md +4 -0
- package/skill/references/workflows/media-library.md +4 -0
- package/src/apps/bridge.js +53 -19
- package/src/apps/html.js +2 -0
- package/src/apps/index.js +8 -8
- package/src/apps/theme.js +58 -10
- package/src/apps/widgets/generation.js +13 -4
- package/src/apps/widgets/list.js +67 -25
- package/src/apps/widgets/mediaGrid.js +148 -79
- package/src/index.js +269 -267
- package/src/toolAnnotations.js +11 -9
- package/src/tools/_shared.js +10 -5
- package/src/tools/agents.js +51 -25
- package/src/tools/chat.js +3 -1
- package/src/tools/editor.js +22 -0
- package/src/tools/generate.js +18 -6
- package/src/tools/media.js +10 -10
- package/src/tools/models.js +11 -5
- package/src/tools/visual_dna.js +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ Use `list_fonts` / `get_font` to discover My Fonts. Local stdio clients upload o
|
|
|
8
8
|
|
|
9
9
|
Only models reporting `supports_custom_fonts: true` accept these selections. Fonts and backend-generated specimens never go through media upload. See [Personal Fonts](https://docs.kolbo.ai/developer-api/personal-fonts). Availability requires a published client and deployed backend containing these tools; source changes alone do not update installed clients.
|
|
10
10
|
|
|
11
|
+
## No-background images
|
|
12
|
+
|
|
13
|
+
`generate_image` and `generate_image_edit` accept `background: "transparent"` for models whose `list_models` record reports `supports_transparent_background: true`. Kolbo also appends the exact phrase `no background` once to the effective prompt, while the structured setting requests the native alpha channel through the configured provider route. Use `output_format: "png"` or `"webp"` (PNG is selected automatically when omitted). Prompt wording alone is not sufficient, and unsupported models reject the request instead of silently returning an opaque image.
|
|
14
|
+
|
|
11
15
|
Use [Kolbo AI](https://kolbo.ai) as native tools in Claude Code and Claude Desktop via MCP (Model Context Protocol).
|
|
12
16
|
|
|
13
17
|
Generate images, videos, music, speech, sound effects, multi-scene campaigns, and conversational chat — all from natural language in your coding environment. 100+ AI models behind Smart Select routing, with reusable Visual DNA profiles for character/style consistency.
|
|
@@ -278,7 +282,9 @@ Every generation tool also accepts an optional `project_id` arg that routes the
|
|
|
278
282
|
| `generate_character_sheet` | Generate a multi-angle character sheet from reference images (credits) → pass URL to create_visual_dna or update_visual_dna |
|
|
279
283
|
| `list_visual_dna_folders` / `create_visual_dna_folder` / `update_visual_dna_folder` / `delete_visual_dna_folder` / `move_visual_dna_to_folder` | Organize Visual DNA characters into user folders (create/rename/recolor/delete + move DNAs in/out) |
|
|
280
284
|
| `create_project` / `update_project` / `archive_project` / `unarchive_project` | Project lifecycle (create/rename/describe/archive; deletion stays in-app) |
|
|
281
|
-
| `
|
|
285
|
+
| `list_skills` / `create_skill` / `update_skill` / `delete_skill` | Skills — reusable named personas for the chat tool; `description` is the system instruction |
|
|
286
|
+
| `list_agents` / `create_agent` / `update_agent` / `delete_agent` | Same four tools under their original names. Still supported and unchanged; new integrations should use the `*_skill` names |
|
|
287
|
+
| `create_video_editor_session` / `export_video_editor_session` | Build an editable timeline from Kolbo-hosted clips, audio and text, then export it to MP4. No new media is generated; identical snapshots reuse the same export job |
|
|
282
288
|
| `get_creative_director_status` | Re-check a Creative Director batch by generation_id until all parallel scenes finish (use after a `_timed_out` Director run) |
|
|
283
289
|
| `list_sessions` | Enumerate sessions across all types, filterable by project, `type`, and `types[]` |
|
|
284
290
|
| `rename_session` / `delete_session` / `restore_session` | Rename a session; soft-delete leftovers after a move; restore from trash |
|
|
@@ -307,4 +313,4 @@ Both are optional — the local install logs in via the browser on first use.
|
|
|
307
313
|
|
|
308
314
|
### Chat thinking level
|
|
309
315
|
|
|
310
|
-
`chat_send_message` accepts optional `thinking_level`, using an ID from `list_models` with `type: "text"`. The server validates it against the resolved model; omitted or invalid values use `thinkingDefault`. Existing safeguards and legacy `deep_think` take precedence. Discover allowed levels through `thinkingLevels`; no package update is required when the server changes a model capability.
|
|
316
|
+
`chat_send_message` accepts optional `thinking_level`, using an ID from `list_models` with `type: "text"`. The server validates it against the resolved model; omitted or invalid values use `thinkingDefault`. Existing safeguards and legacy `deep_think` take precedence. Discover allowed levels through `thinkingLevels`; no package update is required when the server changes a model capability. For the Auto model, optional `routing_mode` accepts `fast`, `balanced`, or `smart`; omitted uses the server default of `balanced`.
|
package/package.json
CHANGED
package/skill/GENERATED.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AUTO-GENERATED — do not edit
|
|
2
2
|
|
|
3
|
-
This tree is mirrored from kolbo-code@
|
|
3
|
+
This tree is mirrored from kolbo-code@1dc58c1, the single source of truth.
|
|
4
4
|
Canonical source: packages/opencode/skills/kolbo/
|
|
5
5
|
Distribution: .github/workflows/sync-skill-to-plugin.yml
|
|
6
6
|
|
package/skill/SKILL.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
version: 0.9.
|
|
2
|
+
version: 0.9.16
|
|
3
3
|
name: kolbo
|
|
4
4
|
description: |
|
|
5
5
|
Generate, edit, analyze, and direct creative media through Kolbo AI: images,
|
|
@@ -72,7 +72,8 @@ For multi-scene / batch work this pairs with `generate_creative_director` (see b
|
|
|
72
72
|
| Build, inspect, animate, light, render, or edit a **Blender scene through Kolbo Blender MCP** | `references/workflows/blender-mcp.md` |
|
|
73
73
|
| Generate a **Seedance 2.5** video | `skill` `elements-prompting` + `references/models/seedance25.md` + Locked Intro in `references/models/seedance.md`. For narrative/continuity also load `references/workflows/filmmaking.md` — but compile the prompt as Locked Intro, NOT the SCENE CONTEXT / OPTICS / ACTION pack |
|
|
74
74
|
| Generate a **Seedance 2 / WAN / MiniMax H3 / Gemini / Elements** video (`generate_elements` or Visual DNA) | `skill` `elements-prompting` + `references/models/seedance.md` — same Locked Intro. Elements is NOT a different prompt language |
|
|
75
|
-
| Generate a **GPT Image 2** image | `references/models/gpt-image.md` |
|
|
75
|
+
| Generate a **GPT Image 2 / 2.5** image | `references/models/gpt-image.md` |
|
|
76
|
+
| **No background / transparent PNG / cutout** on any image | `references/models/gpt-image.md` |
|
|
76
77
|
| Generate a **Nano Banana / Gemini** image | `references/models/nano-banana.md` |
|
|
77
78
|
| Generate a **Veo 3 / 3.1** video | `references/models/veo.md` |
|
|
78
79
|
| Build a **multi-scene set** (Creative Director, storyboard, campaign batch, 4+ angles) | `references/models/creative-director.md` |
|
|
@@ -113,8 +114,8 @@ Font tools (when exposed by the installed MCP): `list_fonts`, `get_font`, `uploa
|
|
|
113
114
|
### Generation
|
|
114
115
|
| Tool | Description |
|
|
115
116
|
|------|-------------|
|
|
116
|
-
| `generate_image` | Single image from a text prompt. Supports Visual DNA, moodboards, image presets (custom instructions live here), reference images, web-search grounding. Named sheets/styles: `list_presets({ type: "image", search: "headless" })` then `preset_id`. |
|
|
117
|
-
| `generate_image_edit` | Edit/transform an existing image. Pass `source_images` + edit prompt. Image-editing presets are supported through `preset_id` from `list_presets({ type: "image_edit" })`. |
|
|
117
|
+
| `generate_image` | Single image from a text prompt. Supports Visual DNA, moodboards, image presets (custom instructions live here), reference images, web-search grounding. Named sheets/styles: `list_presets({ type: "image", search: "headless" })` then `preset_id`. NO BACKGROUND: require `supports_transparent_background: true` from `list_models`, then pass `background: "transparent"` with PNG/WebP — Kolbo appends the phrase `no background` once; prompt wording alone does not replace the setting. |
|
|
118
|
+
| `generate_image_edit` | Edit/transform an existing image. Pass `source_images` + edit prompt. Image-editing presets are supported through `preset_id` from `list_presets({ type: "image_edit" })`. Native no-background output uses the same capability gate and `background: "transparent"` contract as `generate_image`. |
|
|
118
119
|
| `generate_creative_director` | **2–8 related images or videos as one coherent set.** Use INSTEAD of multiple `generate_image` calls for any related multi-output. |
|
|
119
120
|
| `generate_video` | Text-to-video. Accepts `visual_dna_ids` and `sound_enabled`; `generate_elements` is still the primary reference-driven route for a DNA-anchored film. |
|
|
120
121
|
| `generate_video_from_image` | Animate a still. Prompt describes motion, not subject. |
|
|
@@ -274,6 +275,7 @@ A user-named tool — in any language — overrides every other rule. Recognized
|
|
|
274
275
|
| "Modify THIS one image" — change bg, remove object, recolor | `generate_image_edit` | ❌ Not for multi-output |
|
|
275
276
|
| "4 angles / poses / views of this character" / "variations of this character" | `generate_creative_director` with `visual_dna_ids` | ❌ Don't loop `generate_image_edit` |
|
|
276
277
|
| "4 variations of THIS exact image" (same prompt, different seeds) | `generate_image` with `num_images=4` | ❌ Not `generate_image_edit` |
|
|
278
|
+
| "No background" / transparent PNG / cutout, **בלי רקע** / **רקע שקוף** — new image or existing one | `generate_image` / `generate_image_edit` with `background: "transparent"` on a GPT Image 2 / 2.5 model (gate on `supports_transparent_background: true`) | ❌ Prompt wording alone — it returns an opaque image. ❌ `edit_image` `removebg` unless they want a purely mechanical cutout of an existing photo |
|
|
277
279
|
|
|
278
280
|
## Core Workflow
|
|
279
281
|
|
package/skill/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.16
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
kolbo-api/src/config/systemPrompt.js (lines ~858–965).
|
|
3
3
|
When that function changes, update this file in the same session. -->
|
|
4
4
|
|
|
5
|
-
# GPT Image 2 — Prompt Rules
|
|
5
|
+
# GPT Image 2 / 2.5 — Prompt Rules
|
|
6
6
|
|
|
7
|
-
Load this file when the user wants a **GPT Image 2
|
|
7
|
+
Load this file when the user wants a **GPT Image 2 or GPT Image 2.5** image (OpenAI). The live family is `gpt-image-2`, `gpt-image-2.5-sunburst` and `gpt-image-2.5-flare` — all three are the ONLY Kolbo image models that can output a native transparent background. (`gpt-image/1.5-text-to-image` is the older row and cannot.) For other image models see `models/nano-banana.md`, `models/creative-director.md`, or `models/prompt-copilot.md`.
|
|
8
8
|
|
|
9
|
-
**Kolbo MCP routing:** call `generate_image` (text-to-image) or `generate_image_edit` (edits with `source_images`). Pass `
|
|
9
|
+
**Kolbo MCP routing:** call `generate_image` (text-to-image) or `generate_image_edit` (edits with `source_images`). Pass the exact identifier the user named (`gpt-image-2`, `gpt-image-2.5-sunburst`, `gpt-image-2.5-flare`); otherwise consult `list_models({ type: "text_to_img" })`. On `generate_image_edit` the same families appear as their `/edit` rows under `list_models({ type: "image_editing" })`.
|
|
10
10
|
|
|
11
11
|
## CRITICAL Kolbo Platform Rules
|
|
12
12
|
|
|
13
13
|
- **Resolution and aspect ratio are MCP-tool params** (`aspect_ratio`, `resolution`) — NEVER include `size=`, `1024x1536`, aspect-ratio tags, or any resolution syntax inside the `prompt` field.
|
|
14
14
|
- Pass aspect / resolution as separate tool parameters. Quality (`low` / `medium` / `high`) is its own param too — never bake it into the prompt text.
|
|
15
|
+
- **No background uses both a tool parameter and a prompt cue.** If the user asks for no background / a transparent cutout, call `list_models` for the chosen image type and require `supports_transparent_background: true`. Then pass `background: "transparent"` and `output_format: "png"` (or `"webp"`). Kolbo appends the exact phrase `no background` once to the effective prompt automatically. The phrase alone is not sufficient; if the model does not advertise the capability, choose a supported model with the user.
|
|
15
16
|
- Do not write Python, `client.images.generate`, OpenAI SDK code, or `size=` keyword arguments. The user is generating through Kolbo's MCP tools.
|
|
16
17
|
|
|
17
18
|
## Universal Prompting Rules (apply to EVERY prompt)
|
|
@@ -119,3 +119,7 @@ Normal cost formula: `final_cost = credit × output_seconds × resolution_multip
|
|
|
119
119
|
## Log Approved Resolution / Duration / Sound Choices
|
|
120
120
|
|
|
121
121
|
After the user approves the actual result, log its `credits_used`, resolution, duration, and sound state. Pending and rejected outputs stay out; use the format in `production-log.md`.
|
|
122
|
+
|
|
123
|
+
## Reference evidence in generation status
|
|
124
|
+
|
|
125
|
+
For a reference audit, inspect the persisted status result's `visual_dna` and reference image fields. The widget's `visual_dnas` is display metadata, not the original submitted request. Missing widget metadata means unknown, not that no reference was used. Server-side @mention resolution can attach references beyond the caller’s explicit fields. Keep submitted inputs, persisted references, and observed visual fidelity distinct; attaching a DNA does not prove identity fidelity.
|
|
@@ -4,6 +4,10 @@ Load this file when the user wants to browse, list, organize, delete, restore, m
|
|
|
4
4
|
|
|
5
5
|
The library covers both **uploaded files** and **AI-generated outputs the user has saved**. Tools fall into five groups: ingest, browse, lifecycle (delete/restore/move), folders, and favorites.
|
|
6
6
|
|
|
7
|
+
`list_media` returns the complete requested page in both text and widget results. Follow `pagination.has_next` using the returned page size; use smaller pages (for example 20) when reviewing detailed assets to keep context manageable. `project_id` restricts SDK results to that project's recorded membership, not the project-looking segment in a CDN URL (files can be moved). A folder filter takes precedence over the project filter.
|
|
8
|
+
|
|
9
|
+
For production research, `get_visual_dna` provides the full stored description and references; list descriptions are compact previews. Internal extraction system prompts are intentionally private. `get_project_profile` reads the stored synthesized brief; it is not a substitute for the original project documents or session history.
|
|
10
|
+
|
|
7
11
|
## ⚠️ Already-hosted URLs — never re-upload
|
|
8
12
|
|
|
9
13
|
`generate_*` / `list_media` / `get_media` / a prior `upload_media` already return a Kolbo CDN URL (`media.kolbo.ai`, `*.kolbo.ai`, Spaces). Pass that exact URL into the next generation tool. Calling `upload_media` on it duplicates the file.
|
package/src/apps/bridge.js
CHANGED
|
@@ -36,6 +36,22 @@ const BRIDGE_JS = `
|
|
|
36
36
|
var queue = []; // deferred host-bound sends until initialized
|
|
37
37
|
var hostContext = null;
|
|
38
38
|
var readyFns = [], toolResultFns = [], themeFns = [], toolInputFns = [];
|
|
39
|
+
var latestResult, latestInput;
|
|
40
|
+
function deliver(f, args) {
|
|
41
|
+
try { f.apply(null, args); }
|
|
42
|
+
catch (e) {
|
|
43
|
+
console.error('Kolbo widget render failed', e);
|
|
44
|
+
var card = document.querySelector('.k-card');
|
|
45
|
+
var notice = document.getElementById('kolbo-render-error');
|
|
46
|
+
if (card && !notice) {
|
|
47
|
+
notice = document.createElement('div');
|
|
48
|
+
notice.id = 'kolbo-render-error';
|
|
49
|
+
notice.setAttribute('role', 'alert');
|
|
50
|
+
notice.textContent = 'This preview could not update. Your generation may still be running. Reopen this card or check your Kolbo library.';
|
|
51
|
+
card.appendChild(notice);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
39
55
|
|
|
40
56
|
function post(msg) { window.parent.postMessage(msg, '*'); }
|
|
41
57
|
|
|
@@ -67,11 +83,13 @@ const BRIDGE_JS = `
|
|
|
67
83
|
return;
|
|
68
84
|
}
|
|
69
85
|
if (m.method === 'ui/notifications/tool-result') {
|
|
70
|
-
|
|
86
|
+
latestResult = m.params || {};
|
|
87
|
+
toolResultFns.forEach(function (f) { deliver(f, [latestResult]); });
|
|
71
88
|
} else if (m.method === 'ui/notifications/tool-input' || m.method === 'ui/notifications/tool-input-partial') {
|
|
72
89
|
// Fires while the tool is still RUNNING — lets widgets show a real
|
|
73
90
|
// "preparing" state instead of a blank card until the result lands.
|
|
74
|
-
|
|
91
|
+
latestInput = [(m.params && m.params.arguments) || {}, m.params || {}];
|
|
92
|
+
toolInputFns.forEach(function (f) { deliver(f, latestInput); });
|
|
75
93
|
} else if (m.method === 'ui/notifications/host-context-changed') {
|
|
76
94
|
hostContext = (m.params && m.params.hostContext) || m.params || hostContext;
|
|
77
95
|
themeFns.forEach(function (f) { try { f(hostContext); } catch (e) {} });
|
|
@@ -88,12 +106,13 @@ const BRIDGE_JS = `
|
|
|
88
106
|
appInfo: { name: 'kolbo-widget', version: '1.0.0' },
|
|
89
107
|
appCapabilities: {}
|
|
90
108
|
}).then(function (res) {
|
|
109
|
+
if (oa) return; // A late JSON-RPC ack must not replace OpenAI host context.
|
|
91
110
|
hostContext = (res && res.hostContext) || null;
|
|
92
111
|
notify('ui/notifications/initialized');
|
|
93
112
|
initialized = true;
|
|
94
113
|
queue.forEach(post);
|
|
95
114
|
queue = [];
|
|
96
|
-
readyFns.forEach(function (f) {
|
|
115
|
+
readyFns.forEach(function (f) { deliver(f, [hostContext]); });
|
|
97
116
|
}).catch(function () { /* host without apps support — widget stays static */ });
|
|
98
117
|
|
|
99
118
|
var fsMode = false; // fullscreen: the HOST owns layout — size reports there
|
|
@@ -176,9 +195,9 @@ const BRIDGE_JS = `
|
|
|
176
195
|
document.addEventListener('loadedmetadata', function () { queueSize(60); }, true);
|
|
177
196
|
|
|
178
197
|
window.kolbo = {
|
|
179
|
-
ready: function (f) { if (initialized) f
|
|
180
|
-
onToolResult: function (f) { toolResultFns.push(f); },
|
|
181
|
-
onToolInput: function (f) { toolInputFns.push(f); },
|
|
198
|
+
ready: function (f) { if (initialized) deliver(f, [hostContext]); else readyFns.push(f); },
|
|
199
|
+
onToolResult: function (f) { toolResultFns.push(f); if (latestResult) deliver(f, [latestResult]); },
|
|
200
|
+
onToolInput: function (f) { toolInputFns.push(f); if (latestInput) deliver(f, latestInput); },
|
|
182
201
|
onThemeChange: function (f) { themeFns.push(f); },
|
|
183
202
|
callTool: function (name, args) { return request('tools/call', { name: name, arguments: args || {} }); },
|
|
184
203
|
sendMessage: function (text) {
|
|
@@ -218,9 +237,13 @@ const BRIDGE_JS = `
|
|
|
218
237
|
// came, and sat on "Preparing" with no prompt, no settings and no result —
|
|
219
238
|
// even after the tool had returned. Everything here maps onto the same
|
|
220
239
|
// callbacks the JSON-RPC path drives, so widgets need no host-specific code.
|
|
221
|
-
var oa =
|
|
222
|
-
|
|
223
|
-
|
|
240
|
+
var oa = null;
|
|
241
|
+
var oaInputSeen, oaOutputSeen;
|
|
242
|
+
function attachOpenAI(event) {
|
|
243
|
+
var current = window.openai;
|
|
244
|
+
if (!current || typeof current !== 'object') return;
|
|
245
|
+
oa = current;
|
|
246
|
+
var globals = event && event.detail && event.detail.globals;
|
|
224
247
|
var oaName = function () {
|
|
225
248
|
var meta = oa.toolResponseMetadata || {};
|
|
226
249
|
return oa.toolName || meta['kolbo/tool'] || (oa.toolOutput && oa.toolOutput.tool) || undefined;
|
|
@@ -232,18 +255,28 @@ const BRIDGE_JS = `
|
|
|
232
255
|
if (!initialized) {
|
|
233
256
|
initialized = true;
|
|
234
257
|
queue = [];
|
|
235
|
-
readyFns.forEach(function (f) {
|
|
258
|
+
readyFns.forEach(function (f) { deliver(f, [hostContext]); });
|
|
236
259
|
} else {
|
|
237
260
|
themeFns.forEach(function (f) { try { f(hostContext); } catch (e) {} });
|
|
238
261
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
262
|
+
var input = globals && Object.prototype.hasOwnProperty.call(globals, 'toolInput') ? globals.toolInput : oa.toolInput;
|
|
263
|
+
var output = globals && Object.prototype.hasOwnProperty.call(globals, 'toolOutput') ? globals.toolOutput : oa.toolOutput;
|
|
264
|
+
var inputKey = JSON.stringify(input);
|
|
265
|
+
var metadata = globals && globals.toolResponseMetadata || oa.toolResponseMetadata || {};
|
|
266
|
+
var envelope = metadata.call_tool_result || metadata.mcp_tool_result;
|
|
267
|
+
var result = envelope && typeof envelope === 'object' ? Object.assign({}, envelope) : {};
|
|
268
|
+
if (output != null) result.structuredContent = output;
|
|
269
|
+
result._meta = Object.assign({}, result._meta || {}, metadata);
|
|
270
|
+
var outputKey = JSON.stringify(result);
|
|
271
|
+
if (input && inputKey !== oaInputSeen) {
|
|
272
|
+
oaInputSeen = inputKey;
|
|
273
|
+
latestInput = [input, { name: oaName(), arguments: input }];
|
|
274
|
+
toolInputFns.forEach(function (f) { deliver(f, latestInput); });
|
|
242
275
|
}
|
|
243
|
-
if (
|
|
244
|
-
oaOutputSeen =
|
|
245
|
-
|
|
246
|
-
toolResultFns.forEach(function (f) {
|
|
276
|
+
if ((output || envelope) && outputKey !== oaOutputSeen) {
|
|
277
|
+
oaOutputSeen = outputKey;
|
|
278
|
+
latestResult = result;
|
|
279
|
+
toolResultFns.forEach(function (f) { deliver(f, [latestResult]); });
|
|
247
280
|
}
|
|
248
281
|
};
|
|
249
282
|
var oaCall = function (method) { return typeof oa[method] === 'function'; };
|
|
@@ -272,13 +305,14 @@ const BRIDGE_JS = `
|
|
|
272
305
|
if (!oaCall('requestDisplayMode')) return Promise.resolve({ mode: 'inline' });
|
|
273
306
|
return Promise.resolve(oa.requestDisplayMode({ mode: mode })).then(function (r) { return { mode: (r && r.mode) || mode }; });
|
|
274
307
|
};
|
|
275
|
-
window.addEventListener('openai:set_globals', oaSync);
|
|
276
308
|
// Deferred: the widget's own script (which registers ready/onToolInput/
|
|
277
309
|
// onToolResult) runs AFTER this bridge block in the same document, and a
|
|
278
310
|
// synchronous sync here fired into empty listener lists and marked the
|
|
279
311
|
// input/output as seen.
|
|
280
|
-
|
|
312
|
+
oaSync();
|
|
281
313
|
}
|
|
314
|
+
window.addEventListener('openai:set_globals', attachOpenAI);
|
|
315
|
+
setTimeout(attachOpenAI, 0);
|
|
282
316
|
})();
|
|
283
317
|
`;
|
|
284
318
|
|
package/src/apps/html.js
CHANGED
|
@@ -60,6 +60,8 @@ var ICONS = {
|
|
|
60
60
|
file: _svg('<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/>'),
|
|
61
61
|
copy: _svg('<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>'),
|
|
62
62
|
chevronDown: _svg('<path d="M6 9l6 6 6-6"/>'),
|
|
63
|
+
chevronLeft: _svg('<path d="M15 18l-6-6 6-6"/>'),
|
|
64
|
+
chevronRight: _svg('<path d="M9 18l6-6-6-6"/>'),
|
|
63
65
|
chevronUp: _svg('<path d="M18 15l-6-6-6 6"/>')
|
|
64
66
|
};
|
|
65
67
|
// Media-kind → icon (accepts model kind / media_type strings).
|
package/src/apps/index.js
CHANGED
|
@@ -20,8 +20,8 @@ const { generationWidgetHtml } = require('./widgets/generation');
|
|
|
20
20
|
const { mediaGridWidgetHtml } = require('./widgets/mediaGrid');
|
|
21
21
|
const { catalogWidgetHtml } = require('./widgets/catalog');
|
|
22
22
|
const { transcriptWidgetHtml } = require('./widgets/transcript');
|
|
23
|
-
const { uploadWidgetHtml } = require('./widgets/upload');
|
|
24
|
-
const { fontUploadWidgetHtml } = require('./widgets/fontUpload');
|
|
23
|
+
const { uploadWidgetHtml } = require('./widgets/upload');
|
|
24
|
+
const { fontUploadWidgetHtml } = require('./widgets/fontUpload');
|
|
25
25
|
const { listWidgetHtml } = require('./widgets/list');
|
|
26
26
|
const { HOST_MAP } = require('../cdn');
|
|
27
27
|
const { plansWidgetHtml } = require('./widgets/plans');
|
|
@@ -31,8 +31,8 @@ const UI = {
|
|
|
31
31
|
mediaGrid: 'ui://kolbo/media-grid.html',
|
|
32
32
|
catalog: 'ui://kolbo/catalog.html',
|
|
33
33
|
transcript: 'ui://kolbo/transcript.html',
|
|
34
|
-
upload: 'ui://kolbo/upload.html',
|
|
35
|
-
fontUpload: 'ui://kolbo/font-upload.html',
|
|
34
|
+
upload: 'ui://kolbo/upload.html',
|
|
35
|
+
fontUpload: 'ui://kolbo/font-upload.html',
|
|
36
36
|
list: 'ui://kolbo/list.html',
|
|
37
37
|
plans: 'ui://kolbo/plans.html',
|
|
38
38
|
};
|
|
@@ -42,8 +42,8 @@ const WIDGET_BUILDERS = {
|
|
|
42
42
|
[UI.mediaGrid]: mediaGridWidgetHtml,
|
|
43
43
|
[UI.catalog]: catalogWidgetHtml,
|
|
44
44
|
[UI.transcript]: transcriptWidgetHtml,
|
|
45
|
-
[UI.upload]: uploadWidgetHtml,
|
|
46
|
-
[UI.fontUpload]: fontUploadWidgetHtml,
|
|
45
|
+
[UI.upload]: uploadWidgetHtml,
|
|
46
|
+
[UI.fontUpload]: fontUploadWidgetHtml,
|
|
47
47
|
[UI.list]: listWidgetHtml,
|
|
48
48
|
[UI.plans]: plansWidgetHtml,
|
|
49
49
|
};
|
|
@@ -734,8 +734,8 @@ const TOOL_WIDGETS = {
|
|
|
734
734
|
// never saw it as widget-carrying. Result-level _meta alone isn't enough.
|
|
735
735
|
list_color_palettes: UI.mediaGrid,
|
|
736
736
|
// upload widget
|
|
737
|
-
media_upload_widget: UI.upload,
|
|
738
|
-
font_upload_widget: UI.fontUpload,
|
|
737
|
+
media_upload_widget: UI.upload,
|
|
738
|
+
font_upload_widget: UI.fontUpload,
|
|
739
739
|
// generic list widget — flat record lists with no natural thumbnail
|
|
740
740
|
list_projects: UI.list,
|
|
741
741
|
// Must stay list.html — mapping this to generation.html mounts "Kolbo Generation /
|
package/src/apps/theme.js
CHANGED
|
@@ -354,18 +354,64 @@ html.k-fullscreen .k-actions { flex: none; padding-top: 8px; }
|
|
|
354
354
|
.k-input:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
|
|
355
355
|
.k-input::placeholder { color: var(--text-faint); }
|
|
356
356
|
|
|
357
|
-
/* ----
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.k-
|
|
362
|
-
|
|
363
|
-
.k-
|
|
364
|
-
|
|
365
|
-
|
|
357
|
+
/* ---- Paged media grid (library / stock / presets / DNAs / moodboards) ----
|
|
358
|
+
Media-first tiles: the artwork IS the row. A title rides a gradient scrim
|
|
359
|
+
INSIDE the tile instead of a text block under it, so a page of results reads
|
|
360
|
+
as a contact sheet rather than a list of captions. No backdrop-filter on the
|
|
361
|
+
scrim for the same reason .k-gen-badge has none — a dozen blurred strips over
|
|
362
|
+
a dozen images forces a per-frame backdrop re-sample on phones. */
|
|
363
|
+
.k-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
|
|
364
|
+
/* Narrow hosts (Kolbo Code sidebar, phone): step down instead of shrinking
|
|
365
|
+
tiles below a thumbnail worth looking at. The 520px block further down wins
|
|
366
|
+
under 520 and takes it to 2. */
|
|
367
|
+
@media (max-width: 620px) { .k-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
|
368
|
+
.k-tile { position: relative; aspect-ratio: 1; border-radius: 12px; overflow: hidden;
|
|
369
|
+
border: 1px solid var(--border); background: var(--surface); cursor: pointer;
|
|
370
|
+
transition: transform 250ms var(--spring), box-shadow 250ms var(--smooth), border-color 150ms var(--smooth); }
|
|
371
|
+
.k-tile:hover { transform: translateY(-2px); border-color: var(--border-strong);
|
|
372
|
+
box-shadow: 0 10px 26px rgba(0, 0, 0, 0.42); }
|
|
373
|
+
.k-tile img, .k-tile video { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
374
|
+
.k-tile-fb { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
|
375
|
+
color: var(--text-faint); font-size: 24px; }
|
|
376
|
+
.k-tile-cap { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; pointer-events: none;
|
|
377
|
+
padding: 20px 8px 7px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.78)); }
|
|
378
|
+
.k-tile-t { font-size: 11px; font-weight: 600; color: #fff;
|
|
366
379
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
367
|
-
.k-
|
|
380
|
+
.k-tile-s { font-size: 10px; color: rgba(255, 255, 255, 0.66);
|
|
368
381
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
382
|
+
/* Hover actions, revealed on pointer devices and always-on where there is no
|
|
383
|
+
hover — a touch host must never hide the only Use affordance. */
|
|
384
|
+
.k-tile-acts { position: absolute; top: 6px; right: 6px; z-index: 3; display: flex; gap: 4px;
|
|
385
|
+
opacity: 0; transition: opacity 150ms var(--smooth); }
|
|
386
|
+
.k-tile:hover .k-tile-acts, .k-tile:focus-within .k-tile-acts { opacity: 1; }
|
|
387
|
+
@media (hover: none) { .k-tile-acts { opacity: 1; } }
|
|
388
|
+
.k-tile-act { width: 26px; height: 26px; padding: 0; border-radius: 7px;
|
|
389
|
+
border: 1px solid rgba(255, 255, 255, 0.18); background: rgba(0, 0, 0, 0.62); color: #fff;
|
|
390
|
+
display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
|
|
391
|
+
transition: background 150ms var(--smooth); }
|
|
392
|
+
.k-tile-act:hover { background: var(--brand); border-color: var(--brand); }
|
|
393
|
+
.k-tile-play { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 2; }
|
|
394
|
+
|
|
395
|
+
/* ---- Pager (media grid + list) ----
|
|
396
|
+
A page of results at a fixed card height, instead of a card that grows a row
|
|
397
|
+
every time you ask for more. Forward past the last loaded page still goes
|
|
398
|
+
through the server's page_tool + next_args contract. */
|
|
399
|
+
.k-pager { display: flex; align-items: center; justify-content: center; gap: 12px; margin-top: 12px; }
|
|
400
|
+
.k-pager-btn { width: 28px; height: 28px; padding: 0; border-radius: 8px;
|
|
401
|
+
border: 1px solid var(--border); background: var(--surface-2); color: var(--text);
|
|
402
|
+
box-shadow: var(--specular); display: inline-flex; align-items: center; justify-content: center;
|
|
403
|
+
cursor: pointer; transition: background 150ms var(--smooth); }
|
|
404
|
+
.k-pager-btn:hover:not(:disabled) { background: var(--border-strong); }
|
|
405
|
+
.k-pager-btn:disabled { opacity: 0.3; cursor: default; }
|
|
406
|
+
.k-dots { display: flex; align-items: center; gap: 6px; }
|
|
407
|
+
.k-dot { width: 7px; height: 7px; padding: 0; border: 0; border-radius: 999px; cursor: pointer;
|
|
408
|
+
background: var(--text); opacity: 0.28;
|
|
409
|
+
transition: width 220ms var(--spring), opacity 150ms var(--smooth); }
|
|
410
|
+
.k-dot:hover { opacity: 0.6; }
|
|
411
|
+
.k-dot.on { width: 22px; opacity: 0.95; cursor: default; }
|
|
412
|
+
.k-dot.ghost { cursor: default; opacity: 0.14; }
|
|
413
|
+
.k-pager-label { font-size: 11px; color: var(--text-faint);
|
|
414
|
+
font-family: 'JetBrains Mono', ui-monospace, monospace; min-width: 58px; text-align: center; }
|
|
369
415
|
|
|
370
416
|
/* ---- Audio rows ---- */
|
|
371
417
|
.k-audio-row { display: flex; align-items: center; gap: 10px; padding: 9px 10px;
|
|
@@ -405,6 +451,8 @@ html.k-fullscreen .k-actions { flex: none; padding-top: 8px; }
|
|
|
405
451
|
.k-body { padding: 10px 12px 12px; }
|
|
406
452
|
.k-footer { padding: 8px 12px 12px; }
|
|
407
453
|
.k-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
454
|
+
.k-tile-act { width: 30px; height: 30px; }
|
|
455
|
+
.k-pager-btn { width: 34px; height: 34px; }
|
|
408
456
|
.k-actions { flex-direction: column; align-items: stretch; }
|
|
409
457
|
.k-actions .k-btn { width: 100%; min-height: 44px; justify-content: center; }
|
|
410
458
|
.k-btn { min-height: 40px; padding: 10px 14px; }
|
|
@@ -659,6 +659,7 @@ function renderCancelled(creditsRefunded) {
|
|
|
659
659
|
var MAX_POLL_MS = 35 * 60 * 1000;
|
|
660
660
|
var MAX_POLL_ERRORS = 30;
|
|
661
661
|
var pollStart = 0, pollErrors = 0;
|
|
662
|
+
var pollInFlight = false;
|
|
662
663
|
var cancelRequested = false; // set by the Stop button; freezes the poll loop
|
|
663
664
|
|
|
664
665
|
/* ---------- offscreen gate ----------
|
|
@@ -696,6 +697,7 @@ function releaseSeen() {
|
|
|
696
697
|
|
|
697
698
|
function schedulePoll(sc) {
|
|
698
699
|
if (cancelRequested) return;
|
|
700
|
+
if (!sc.generation_id && !(sc.generation_ids && sc.generation_ids.length) && !sc.status_args) return;
|
|
699
701
|
if (!seen) { whenSeenFns.push(function () { schedulePoll(sc); }); return; }
|
|
700
702
|
// The call itself long-waits server-side, for one transport-safe window
|
|
701
703
|
// (~45s over the remote connector — see WAIT_WINDOW_MS in tools/generate.js).
|
|
@@ -707,12 +709,15 @@ function schedulePoll(sc) {
|
|
|
707
709
|
pollTimer = setTimeout(function () { poll(sc); }, delay);
|
|
708
710
|
}
|
|
709
711
|
function poll(sc) {
|
|
710
|
-
if (cancelRequested) return;
|
|
712
|
+
if (cancelRequested || pollInFlight) return;
|
|
711
713
|
if (pollStart && (Date.now() - pollStart) > MAX_POLL_MS) {
|
|
712
714
|
return renderTrackingIssue('This is still running longer than the tracking window. Do not retry it — any completed result will appear in your Kolbo library.');
|
|
713
715
|
}
|
|
714
716
|
var args = sc.status_args || { generation_id: sc.generation_id, wait: true };
|
|
717
|
+
pollInFlight = true;
|
|
715
718
|
window.kolbo.callTool(sc.poll_tool || 'get_generation_status', args).then(function (res) {
|
|
719
|
+
pollInFlight = false;
|
|
720
|
+
if (cancelRequested || (state && (state.phase === 'completed' || state.phase === 'failed'))) return;
|
|
716
721
|
var st = structured(res) || {};
|
|
717
722
|
var stateName = st.state || st.phase || st.status;
|
|
718
723
|
// A failed status CALL (tool error / not-found / {success:false}) is not a
|
|
@@ -766,6 +771,8 @@ function poll(sc) {
|
|
|
766
771
|
schedulePoll(sc);
|
|
767
772
|
}
|
|
768
773
|
}).catch(function () {
|
|
774
|
+
pollInFlight = false;
|
|
775
|
+
if (cancelRequested || (state && (state.phase === 'completed' || state.phase === 'failed'))) return;
|
|
769
776
|
if (++pollErrors >= MAX_POLL_ERRORS) return renderTrackingIssue('Tracking paused after repeated connection errors. The generation may still be running.');
|
|
770
777
|
schedulePoll(sc);
|
|
771
778
|
});
|
|
@@ -1340,20 +1347,20 @@ var PRE_VIDEO_KEYS = ['source_video', 'reference_videos'];
|
|
|
1340
1347
|
var PRE_AUDIO_KEYS = ['audio', 'audio_url', 'reference_audio_urls', 'seed_reference_audio_urls'];
|
|
1341
1348
|
|
|
1342
1349
|
// The card mounts the moment the tool is CALLED, so the only thing it knows is
|
|
1343
|
-
// the raw tool input
|
|
1350
|
+
// the raw tool input — and for an edit/elements call the submit that follows is
|
|
1344
1351
|
// the LONGEST wait on the card (local files are re-hosted first). Map the input
|
|
1345
1352
|
// onto the same shape renderChips already reads for a server payload so the
|
|
1346
1353
|
// references, DNA count and settings are on screen immediately instead of after
|
|
1347
1354
|
// a minute of blank skeleton.
|
|
1348
1355
|
// Deliberately NOT shown here: model, voice, DNA and moodboard NAMES. Those are
|
|
1349
1356
|
// resolved server-side and arrive with the result (which overwrites all of
|
|
1350
|
-
// this)
|
|
1357
|
+
// this) — rendering the raw identifier the caller passed would put an id on the
|
|
1351
1358
|
// card, which is never allowed.
|
|
1352
1359
|
function preRefSc(toolName, a) {
|
|
1353
1360
|
var img = [], vid = [], aud = [];
|
|
1354
1361
|
var take = function (v, bucket) {
|
|
1355
1362
|
if (typeof v === 'string') {
|
|
1356
|
-
// http(s) only
|
|
1363
|
+
// http(s) only — an absolute local path is not loadable from the iframe.
|
|
1357
1364
|
if (/^https?:/i.test(v) && bucket.indexOf(v) < 0) bucket.push(v);
|
|
1358
1365
|
return;
|
|
1359
1366
|
}
|
|
@@ -1484,6 +1491,8 @@ function completedFromPlain(sc) {
|
|
|
1484
1491
|
}
|
|
1485
1492
|
|
|
1486
1493
|
/* ---------- wire host events ---------- */
|
|
1494
|
+
// Paint before the host handshake: tool input may arrive late or be withheld.
|
|
1495
|
+
bootPre(null, null);
|
|
1487
1496
|
window.kolbo.onToolResult(function (result) {
|
|
1488
1497
|
var sc = result.structuredContent || structured(result);
|
|
1489
1498
|
var list = listPayload(sc);
|