@kolbo/mcp 1.81.2 → 1.81.5
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 +5 -2
- package/package.json +1 -1
- package/skill/GENERATED.md +1 -1
- package/skill/SKILL.md +68 -89
- package/skill/VERSION +1 -1
- package/skill/references/models/creative-director.md +2 -8
- package/skill/references/models/music.md +1 -1
- package/skill/references/models/prompt-copilot.md +1 -1
- package/skill/references/models/seedance.md +5 -21
- package/skill/references/models/seedance25.md +1 -1
- package/skill/references/models/veo.md +1 -1
- package/skill/references/workflows/color-dna.md +12 -0
- package/skill/references/workflows/cost-and-validation.md +6 -26
- package/skill/references/workflows/dtc-ads.md +1 -1
- package/skill/references/workflows/filmmaking.md +1 -25
- package/skill/references/workflows/marketing-studio.md +14 -14
- package/skill/references/workflows/media-library.md +11 -1
- package/skill/references/workflows/product-photoshoot.md +1 -1
- package/skill/references/workflows/production-log.md +2 -14
- package/skill/references/workflows/production-planning.md +6 -5
- package/skill/references/workflows/review-collections.md +98 -0
- package/skill/references/workflows/troubleshooting.md +2 -8
- package/skill/references/workflows/visual-dna.md +5 -34
- package/src/apps/bridge.js +4 -0
- package/src/apps/html.js +2 -1
- package/src/apps/index.js +1 -0
- package/src/apps/theme.js +2 -1
- package/src/apps/widgets/catalog.js +1 -1
- package/src/apps/widgets/generation.js +67 -17
- package/src/apps/widgets/list.js +6 -6
- package/src/apps/widgets/mediaGrid.js +3 -6
- package/src/tools/_shared.js +55 -9
- package/src/tools/generate.js +21 -9
- package/src/tools/projects.js +41 -9
package/src/apps/widgets/list.js
CHANGED
|
@@ -20,7 +20,7 @@ const { widgetPage } = require('../html');
|
|
|
20
20
|
* badge, // small pill, e.g. role/status ("owner", "default", "shared")
|
|
21
21
|
* meta, // small trailing text, e.g. a date or count
|
|
22
22
|
* open_url, // optional — renders an "Open" button (openLink)
|
|
23
|
-
* use_hint //
|
|
23
|
+
* use_hint // unused — a click pastes `id` into the composer
|
|
24
24
|
* }],
|
|
25
25
|
* total
|
|
26
26
|
* }
|
|
@@ -72,7 +72,7 @@ function apply(result) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
function itemHTML(item, i) {
|
|
75
|
-
var clickable = !!item.
|
|
75
|
+
var clickable = !!item.id;
|
|
76
76
|
var avatar = item.thumbnail
|
|
77
77
|
? '<img class="k-audio-art k-peek-hit" src="' + esc(item.thumbnail) + '" alt="" loading="lazy"'
|
|
78
78
|
+ peekAttrs(item.thumbnail, 'image', item.title)
|
|
@@ -98,10 +98,10 @@ function wire() {
|
|
|
98
98
|
});
|
|
99
99
|
Array.prototype.forEach.call(document.querySelectorAll('.k-audio-row'), function (row) {
|
|
100
100
|
var item = state.items[+row.getAttribute('data-i')];
|
|
101
|
-
if (!item || !item.
|
|
102
|
-
row.onclick = function () {
|
|
103
|
-
|
|
104
|
-
window.kolbo.
|
|
101
|
+
if (!item || !item.id) return;
|
|
102
|
+
row.onclick = function (e) {
|
|
103
|
+
if (e.target && e.target.closest && e.target.closest('[data-open],[data-peek]')) return;
|
|
104
|
+
window.kolbo.insertText(String(item.id));
|
|
105
105
|
};
|
|
106
106
|
});
|
|
107
107
|
}
|
|
@@ -14,7 +14,7 @@ const { widgetPage } = require('../html');
|
|
|
14
14
|
* id, title, subtitle, thumbnail, media_type: 'image'|'video'|'audio'|'3d',
|
|
15
15
|
* url, // full asset / playback URL
|
|
16
16
|
* preview_audio, // audio preview URL (voices, music)
|
|
17
|
-
* use_hint //
|
|
17
|
+
* use_hint // unused — Use pastes `id` into the composer
|
|
18
18
|
* }],
|
|
19
19
|
* total, has_more,
|
|
20
20
|
* import_tool_hint // e.g. 'import via import_stock_asset' — shown on Use
|
|
@@ -154,11 +154,8 @@ function wire() {
|
|
|
154
154
|
|
|
155
155
|
function useItem(i) {
|
|
156
156
|
var item = state.items[i];
|
|
157
|
-
if (!item) return;
|
|
158
|
-
|
|
159
|
-
? item.use_hint.replace('{URL}', item.url || '').replace('{ID}', item.id || '').replace('{TITLE}', item.title || '')
|
|
160
|
-
: 'Use this asset: "' + (item.title || item.id) + '"\\nURL: ' + (item.url || '') + (item.id ? '\\nID: ' + item.id : '');
|
|
161
|
-
window.kolbo.sendMessage(msg);
|
|
157
|
+
if (!item || !item.id) return;
|
|
158
|
+
window.kolbo.insertText(String(item.id));
|
|
162
159
|
}
|
|
163
160
|
|
|
164
161
|
window.kolbo.onToolResult(function (result) {
|
package/src/tools/_shared.js
CHANGED
|
@@ -458,8 +458,8 @@ const OPEN_URL_ROUTES = {
|
|
|
458
458
|
edit_image: { path: '/image-tools', tool: 'image-editing' },
|
|
459
459
|
generate_video: { path: '/video-tools', tool: 'text-to-video' },
|
|
460
460
|
generate_video_from_image: { path: '/video-tools', tool: 'image-to-video' },
|
|
461
|
-
generate_elements: { path: '/video-tools', tool: 'image-to-video' },
|
|
462
|
-
generate_first_last_frame: { path: '/video-tools', tool: 'image-to-video' },
|
|
461
|
+
generate_elements: { path: '/video-tools', tool: 'image-to-video', mode: 'elements' },
|
|
462
|
+
generate_first_last_frame: { path: '/video-tools', tool: 'image-to-video', mode: 'first-last' },
|
|
463
463
|
generate_video_from_video: { path: '/video-tools', tool: 'video-to-video' },
|
|
464
464
|
generate_lipsync: { path: '/video-tools', tool: 'lipsync' },
|
|
465
465
|
generate_music: { path: '/audio-tools', tool: 'music-generator' },
|
|
@@ -468,6 +468,35 @@ const OPEN_URL_ROUTES = {
|
|
|
468
468
|
transcribe_audio: { path: '/audio-tools', tool: 'speech-to-text' },
|
|
469
469
|
generate_creative_director: { path: '/creative-director' },
|
|
470
470
|
};
|
|
471
|
+
// SDK tracking `type` on get_generation_status — same session model as the
|
|
472
|
+
// generate_* tool that created the job, so "Open in Kolbo" still deep-links
|
|
473
|
+
// after a status poll overwrites the live card.
|
|
474
|
+
const TYPE_TO_TOOL = {
|
|
475
|
+
image: 'generate_image',
|
|
476
|
+
image_edit: 'generate_image_edit',
|
|
477
|
+
global_image_edit: 'edit_image',
|
|
478
|
+
video: 'generate_video',
|
|
479
|
+
video_from_image: 'generate_video_from_image',
|
|
480
|
+
elements: 'generate_elements',
|
|
481
|
+
first_last_frame: 'generate_first_last_frame',
|
|
482
|
+
video_from_video: 'generate_video_from_video',
|
|
483
|
+
lipsync: 'generate_lipsync',
|
|
484
|
+
music: 'generate_music',
|
|
485
|
+
speech: 'generate_speech',
|
|
486
|
+
sound: 'generate_sound',
|
|
487
|
+
transcription: 'transcribe_audio',
|
|
488
|
+
creative_director: 'generate_creative_director',
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
function sessionOf(gen) {
|
|
492
|
+
if (!gen || typeof gen !== 'object') return undefined;
|
|
493
|
+
return gen.session_id || gen.sessionId || undefined;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function projectOf(gen) {
|
|
497
|
+
if (!gen || typeof gen !== 'object') return undefined;
|
|
498
|
+
return gen.project_id || gen.projectId || undefined;
|
|
499
|
+
}
|
|
471
500
|
|
|
472
501
|
/**
|
|
473
502
|
* Build the "Open in Kolbo" deep link for a generation's actual session.
|
|
@@ -476,14 +505,30 @@ const OPEN_URL_ROUTES = {
|
|
|
476
505
|
* kolbo-api, shorts render, 3D).
|
|
477
506
|
*/
|
|
478
507
|
function buildOpenUrl(tool, gen) {
|
|
479
|
-
const
|
|
480
|
-
if (!
|
|
481
|
-
|
|
508
|
+
const sid = sessionOf(gen);
|
|
509
|
+
if (!sid) return undefined;
|
|
510
|
+
const key = OPEN_URL_ROUTES[tool] ? tool : (TYPE_TO_TOOL[gen && gen.type] || TYPE_TO_TOOL[tool]);
|
|
511
|
+
const route = OPEN_URL_ROUTES[key];
|
|
512
|
+
if (!route) return undefined;
|
|
513
|
+
let url = `${APP_BASE_URL}${route.path}?session=${encodeURIComponent(sid)}`;
|
|
482
514
|
if (route.tool) url += `&tool=${route.tool}`;
|
|
483
|
-
if (
|
|
515
|
+
if (route.mode) url += `&mode=${route.mode}`;
|
|
516
|
+
const pid = projectOf(gen);
|
|
517
|
+
if (pid) url += `&project=${encodeURIComponent(pid)}`;
|
|
484
518
|
return url;
|
|
485
519
|
}
|
|
486
520
|
|
|
521
|
+
function linkFields(tool, gen) {
|
|
522
|
+
const sid = sessionOf(gen);
|
|
523
|
+
const pid = projectOf(gen);
|
|
524
|
+
const href = buildOpenUrl(tool, gen);
|
|
525
|
+
return {
|
|
526
|
+
...(sid ? { session_id: String(sid) } : {}),
|
|
527
|
+
...(pid ? { project_id: String(pid) } : {}),
|
|
528
|
+
...(href ? { open_url: href } : {}),
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
|
|
487
532
|
/**
|
|
488
533
|
* Build the "Open in Kolbo" deep link for a PROJECT. Lands on the Media hub
|
|
489
534
|
* (all-assets view) pre-filtered to this project, where the user sees every
|
|
@@ -723,7 +768,7 @@ async function uiGenerating(p) {
|
|
|
723
768
|
// `reference_image` is retained for older widget builds. New widgets render
|
|
724
769
|
// every browser-loadable image supplied to the generation.
|
|
725
770
|
...mediaRefs(p),
|
|
726
|
-
|
|
771
|
+
...linkFields(p.tool, p.gen),
|
|
727
772
|
};
|
|
728
773
|
// Batch mode (prompts[] fan-out): ONE widget tracks every id in the set.
|
|
729
774
|
if (Array.isArray(p.generation_ids) && p.generation_ids.length > 1) {
|
|
@@ -741,7 +786,7 @@ async function uiGenerating(p) {
|
|
|
741
786
|
...(p.failed_submissions && p.failed_submissions.length
|
|
742
787
|
? { failed_submissions: p.failed_submissions } : {}),
|
|
743
788
|
...(p.warning ? { _warning: p.warning } : {}),
|
|
744
|
-
_widget_note: 'A live Kolbo widget is rendering this generation for the user (progress + final result + action buttons). Tell the user it is generating and the card above will update — do NOT poll in a loop. If you need the output URLs
|
|
789
|
+
_widget_note: 'A live Kolbo widget is rendering this generation for the user (progress + final result + action buttons). Tell the user it is generating and the card above will update. CREDIT GUARD: END YOUR TURN now — do not keep Thinking, writing skills/files, or planning while the card spins (that burns coding credits). Do NOT poll in a loop. If you need the output URLs for a follow-up step, call get_generation_status ONCE with wait=true as the only next tool — it blocks until done. Tracking several generations? Pass ALL their ids in generation_ids in that one call.',
|
|
745
790
|
_paid_note: 'This generation is RUNNING and the user is paying for it. If you now realize the tool, model, or parameters were wrong, call cancel_generation with this generation_id FIRST, then start the replacement — never leave a wrong generation running alongside its retry (the user gets two cards and two charges).',
|
|
746
791
|
}, null, 2);
|
|
747
792
|
return uiResult(UI.generation, text, structured);
|
|
@@ -819,7 +864,7 @@ async function uiCompleted(p, textPayload, extraContent) {
|
|
|
819
864
|
// resolved as completed-with-no-media and painted a red failure card.
|
|
820
865
|
...(p.state ? { state: p.state } : {}),
|
|
821
866
|
credits_used: p.credits_used,
|
|
822
|
-
|
|
867
|
+
...linkFields(p.tool, p.gen),
|
|
823
868
|
};
|
|
824
869
|
const out = uiResult(UI.generation, textPayload, structured);
|
|
825
870
|
if (Array.isArray(extraContent) && extraContent.length) {
|
|
@@ -913,6 +958,7 @@ module.exports = {
|
|
|
913
958
|
projectScopeReadField,
|
|
914
959
|
inlineImageBlocks,
|
|
915
960
|
buildOpenUrl,
|
|
961
|
+
linkFields,
|
|
916
962
|
buildProjectUrl,
|
|
917
963
|
uiGenerating,
|
|
918
964
|
uiCompleted,
|
package/src/tools/generate.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
7
|
const FormData = require('form-data');
|
|
8
8
|
const { pollUntilDone, waitWindowMs } = require('../polling');
|
|
9
|
-
const { resolveToBuffer, pollOrTimedOut, creditFields, projectIdField, sessionIdField, inlineImageBlocks,
|
|
9
|
+
const { resolveToBuffer, pollOrTimedOut, creditFields, projectIdField, sessionIdField, inlineImageBlocks, linkFields, uiGenerating, uiCompleted, appsEnabled } = require('./_shared');
|
|
10
10
|
const { ownedUrl } = require('./owned-url');
|
|
11
11
|
const { UI, uiResult, canonicalModelId, modelInfo, voiceInfo, resolveCatalogAspectRatio } = require('../apps');
|
|
12
12
|
|
|
@@ -1086,7 +1086,13 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1086
1086
|
// The model that ACTUALLY ran. A single-id check resolves one
|
|
1087
1087
|
// generation, so 'Generations' here overwrote the real model name on
|
|
1088
1088
|
// the finished card the moment the live widget merged this payload in.
|
|
1089
|
-
model: res.model || 'Generations',
|
|
1089
|
+
model: res.model || 'Generations',
|
|
1090
|
+
gen: {
|
|
1091
|
+
generation_id: single.generation_id,
|
|
1092
|
+
session_id: single.session_id,
|
|
1093
|
+
project_id: single.project_id,
|
|
1094
|
+
type: single.type,
|
|
1095
|
+
},
|
|
1090
1096
|
state: single.state,
|
|
1091
1097
|
urls: done ? urls : undefined,
|
|
1092
1098
|
thumbnail_url: res.thumbnail_url,
|
|
@@ -1132,7 +1138,13 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1132
1138
|
// are unaffected — only the fallback quality changes.
|
|
1133
1139
|
return uiCompleted({
|
|
1134
1140
|
tool: 'get_generation_status', kind: 'status', client,
|
|
1135
|
-
model: 'Generations',
|
|
1141
|
+
model: 'Generations',
|
|
1142
|
+
gen: {
|
|
1143
|
+
generation_id: ids[0],
|
|
1144
|
+
session_id: results[0] && results[0].session_id,
|
|
1145
|
+
project_id: results[0] && results[0].project_id,
|
|
1146
|
+
type: results[0] && results[0].type,
|
|
1147
|
+
},
|
|
1136
1148
|
settings: {},
|
|
1137
1149
|
items: results.map(r => {
|
|
1138
1150
|
const res = r.result || {};
|
|
@@ -1786,7 +1798,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
1786
1798
|
poll_tool: 'get_generation_status',
|
|
1787
1799
|
status_args: { generation_id: startResponse.generation_id, wait: true },
|
|
1788
1800
|
audio_url: isUrl ? source : undefined,
|
|
1789
|
-
|
|
1801
|
+
...linkFields('transcribe_audio', startResponse),
|
|
1790
1802
|
});
|
|
1791
1803
|
}
|
|
1792
1804
|
|
|
@@ -2056,7 +2068,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2056
2068
|
'inpaint', 'retake'
|
|
2057
2069
|
]).describe([
|
|
2058
2070
|
'Edit operation:',
|
|
2059
|
-
'"upscale" — boost to 4K/2K resolution (use `scale` for factor, `resolution` for target, `target_fps` for frame rate).',
|
|
2071
|
+
'"upscale" — boost to 4K/2K resolution (use `scale` for factor, `resolution` for target, `target_fps` for frame rate). On model "blackforestlabs/flux-video-upscale" (Flux Video Upscale): `scale` is 1.5-3x (no named `resolution` target — billed on the OUTPUT tier it lands in), `mode` is "precise" (source-faithful, default) or "creative" (reimagines detail — a pricier tier), and `prompt` optionally guides the creative-mode enhancement.',
|
|
2060
2072
|
'"reframe" — change aspect ratio (requires `aspect_ratio`; use `grid_position_x`/`grid_position_y` to control where the original sits).',
|
|
2061
2073
|
'"generate_audio" — add AI-generated audio from `prompt`. Optionally split into `sound_effect_prompt` and `background_music_prompt`. Set `original_sound=true` to keep original audio alongside.',
|
|
2062
2074
|
'"remove_watermark" — AI-powered watermark removal.',
|
|
@@ -2074,9 +2086,9 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2074
2086
|
|
|
2075
2087
|
// ── upscale ────────────────────────────────────────────
|
|
2076
2088
|
scale: z.number().optional()
|
|
2077
|
-
.describe('Upscale factor (e.g. 2, 4). Used with operation="upscale".'),
|
|
2089
|
+
.describe('Upscale factor (e.g. 2, 4). Used with operation="upscale". On "blackforestlabs/flux-video-upscale" the valid range is 1.5-3 (default 2).'),
|
|
2078
2090
|
resolution: z.string().optional()
|
|
2079
|
-
.describe('Target resolution (e.g. "4k", "2k", "1080p"). Used with "upscale" and "reframe".'),
|
|
2091
|
+
.describe('Target resolution (e.g. "4k", "2k", "1080p"). Used with "upscale" and "reframe". Not used by "blackforestlabs/flux-video-upscale" — use `scale` instead.'),
|
|
2080
2092
|
target_fps: z.number().optional()
|
|
2081
2093
|
.describe('Target frame rate (e.g. 24, 30, 60). Used with operation="upscale".'),
|
|
2082
2094
|
enhancement_model: z.string().optional()
|
|
@@ -2092,7 +2104,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2092
2104
|
|
|
2093
2105
|
// ── generate_audio ─────────────────────────────────────
|
|
2094
2106
|
prompt: z.string().optional()
|
|
2095
|
-
.describe('Text prompt. Required for "magic_edit". Used with "generate_audio", "extend", "inpaint", "retake",
|
|
2107
|
+
.describe('Text prompt. Required for "magic_edit". Used with "generate_audio", "extend", "inpaint", "retake", optionally "lipsync" (for text-to-speech instead of audio_url), and optionally "upscale" on model "blackforestlabs/flux-video-upscale" (guides creative-mode detail enhancement).'),
|
|
2096
2108
|
sound_effect_prompt: z.string().optional()
|
|
2097
2109
|
.describe('Separate prompt for sound effects layer. Used with operation="generate_audio" when you want to specify SFX and music independently.'),
|
|
2098
2110
|
background_music_prompt: z.string().optional()
|
|
@@ -2110,7 +2122,7 @@ function registerGenerateTools(server, client, options = {}) {
|
|
|
2110
2122
|
duration: z.number().optional()
|
|
2111
2123
|
.describe('Seconds of content to generate. Used with "extend" (required) and "retake" (optional). Typical range: 1–20.'),
|
|
2112
2124
|
mode: z.string().optional()
|
|
2113
|
-
.describe('Extension direction: "start" or "end". Used with "extend". For "retake": replacement mode (default: "replace_audio_and_video"). Default: "end".'),
|
|
2125
|
+
.describe('Extension direction: "start" or "end". Used with "extend". For "retake": replacement mode (default: "replace_audio_and_video"). For "upscale" on model "blackforestlabs/flux-video-upscale": "precise" (source-faithful, default) or "creative" (reimagines detail — pricier). Default: "end".'),
|
|
2114
2126
|
context: z.string().optional()
|
|
2115
2127
|
.describe('Additional context to guide what gets generated in the extended segment. Used with "extend".'),
|
|
2116
2128
|
|
package/src/tools/projects.js
CHANGED
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
* new OPTIONAL args only. Full rules: ../index.js top-of-file and CLAUDE.md. */
|
|
5
5
|
|
|
6
6
|
const { z } = require('zod');
|
|
7
|
-
const { buildProjectUrl } = require('./_shared');
|
|
7
|
+
const { buildProjectUrl, compactList } = require('./_shared');
|
|
8
8
|
const { listResult } = require('../apps');
|
|
9
9
|
|
|
10
|
+
function blurb(text) {
|
|
11
|
+
const s = String(text || '').replace(/\s+/g, ' ').trim();
|
|
12
|
+
if (s.length <= 96) return s;
|
|
13
|
+
return s.slice(0, 96).replace(/\s+\S*$/, '') + '…';
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
function registerProjectTools(server, client) {
|
|
11
17
|
// ─── list_projects ─────────────────────────────────────────
|
|
12
18
|
server.tool(
|
|
@@ -465,11 +471,33 @@ function registerProjectTools(server, client) {
|
|
|
465
471
|
const result = await client.get(`/v1/projects/${encodeURIComponent(project_id)}/assets`);
|
|
466
472
|
const dnas = result.visual_dnas || [];
|
|
467
473
|
const moodboards = result.moodboards || [];
|
|
468
|
-
const text =
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
474
|
+
const text = compactList(
|
|
475
|
+
[
|
|
476
|
+
...dnas.map((d) => ({
|
|
477
|
+
id: d.id,
|
|
478
|
+
name: d.name,
|
|
479
|
+
dna_type: d.dna_type,
|
|
480
|
+
thumbnail: d.thumbnail_url || d.thumbnail,
|
|
481
|
+
note: blurb(d.note),
|
|
482
|
+
description: blurb(d.description),
|
|
483
|
+
})),
|
|
484
|
+
...moodboards.map((m) => ({
|
|
485
|
+
id: m.id,
|
|
486
|
+
name: m.name,
|
|
487
|
+
dna_type: 'moodboard',
|
|
488
|
+
thumbnail: m.thumbnail_url || m.thumbnail,
|
|
489
|
+
note: blurb(m.note),
|
|
490
|
+
description: blurb(m.summary),
|
|
491
|
+
})),
|
|
492
|
+
],
|
|
493
|
+
{
|
|
494
|
+
fields: ['id', 'name', 'dna_type', 'thumbnail', 'note', 'description'],
|
|
495
|
+
cap: 80,
|
|
496
|
+
total: dnas.length + moodboards.length,
|
|
497
|
+
extra: { visual_dna_count: dnas.length, moodboard_count: moodboards.length },
|
|
498
|
+
note: 'Edit identity text with update_project_asset (description) or update_visual_dna. `note` is project-scoped purpose. Link missing assets with link_project_asset — do not delete+recreate.',
|
|
499
|
+
},
|
|
500
|
+
);
|
|
473
501
|
return listResult(text, {
|
|
474
502
|
widget: 'list',
|
|
475
503
|
title: 'Project cast',
|
|
@@ -477,13 +505,17 @@ function registerProjectTools(server, client) {
|
|
|
477
505
|
...dnas.map((d) => ({
|
|
478
506
|
id: d.id,
|
|
479
507
|
title: '@' + (d.name || d.id),
|
|
480
|
-
subtitle:
|
|
508
|
+
subtitle: blurb(d.note || d.description),
|
|
509
|
+
thumbnail: d.thumbnail_url || d.thumbnail,
|
|
510
|
+
badge: d.dna_type,
|
|
481
511
|
})),
|
|
482
512
|
...moodboards.map((m) => ({
|
|
483
513
|
id: m.id,
|
|
484
514
|
title: '#' + (m.name || m.id),
|
|
485
|
-
subtitle:
|
|
486
|
-
|
|
515
|
+
subtitle: blurb(m.note || m.summary),
|
|
516
|
+
thumbnail: m.thumbnail_url || m.thumbnail,
|
|
517
|
+
badge: 'moodboard',
|
|
518
|
+
})),
|
|
487
519
|
],
|
|
488
520
|
total: dnas.length + moodboards.length
|
|
489
521
|
});
|