@kolbo/mcp 1.82.1 → 1.82.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kolbo/mcp",
3
- "version": "1.82.1",
3
+ "version": "1.82.2",
4
4
  "description": "Kolbo AI MCP Server - Generate images, videos, music, speech, and sound effects from Claude Code",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -441,6 +441,47 @@ function iconFor(kind) {
441
441
  /* ---------- generating ---------- */
442
442
  function isBatch(sc) { return !!(sc && sc.generation_ids && sc.generation_ids.length > 1); }
443
443
 
444
+ // Columns for a batch grid. Video caps at 2 per row: a 16:9 cell in a 4-up
445
+ // grid lands around 200px inside a chat card, which is too small to judge a
446
+ // shot and far too small to scrub once <video controls> appears. Images stay
447
+ // 4-up — they read fine as contact-sheet tiles.
448
+ function gridCols(count, kind) {
449
+ return Math.min(count, kind === 'video' ? 2 : 4);
450
+ }
451
+
452
+ // Batch prompts routinely share a long identical preamble (a locked global
453
+ // look, style rules, duration). Clamping from the left then renders EVERY
454
+ // caption as the same string — four cells all reading "Total: 8s [GLOBAL LOOK
455
+ // - LOCKED]…" identify nothing. Drop the shared prefix so the caption shows
456
+ // the part that actually differs. Full text stays in the tooltip.
457
+ function batchCaptions(prompts) {
458
+ var list = (prompts || []).map(function (p) { return String(p == null ? '' : p); });
459
+ if (list.length < 2) return list;
460
+ var first = list[0], n = first.length;
461
+ for (var i = 1; i < list.length && n > 0; i++) {
462
+ var j = 0;
463
+ while (j < n && j < list[i].length && list[i].charAt(j) === first.charAt(j)) j++;
464
+ n = j;
465
+ }
466
+ // Short shared runs are just coincidence ("A ", "The "), not a preamble.
467
+ if (n < 12) return list;
468
+ // Back up to a word boundary so a label never starts mid-word.
469
+ var cut = n;
470
+ while (cut > 0 && !/\s/.test(first.charAt(cut - 1))) cut--;
471
+ if (!cut) cut = n;
472
+ return list.map(function (p) {
473
+ var rest = p.slice(cut).trim();
474
+ // A prompt identical to the prefix has nothing distinct to show — keep it
475
+ // whole rather than rendering an empty chip.
476
+ return rest ? '… ' + rest : p;
477
+ });
478
+ }
479
+ function capAt(sc, i) {
480
+ if (!sc || !sc.prompts || !sc.prompts[i]) return '';
481
+ if (!sc._caps) sc._caps = batchCaptions(sc.prompts);
482
+ return sc._caps[i] || sc.prompts[i];
483
+ }
484
+
444
485
  function renderGenerating(sc) {
445
486
  setPhaseChip('Generating', true);
446
487
  var n = Math.min(sc.count || 1, isBatch(sc) ? 8 : 4);
@@ -449,11 +490,11 @@ function renderGenerating(sc) {
449
490
  var cells = '';
450
491
  for (var i = 0; i < n; i++) {
451
492
  var cap = (sc.prompts && sc.prompts[i])
452
- ? '<span class="k-skel-cap" title="' + esc(sc.prompts[i]) + '">' + esc(sc.prompts[i]) + '</span>' : '';
493
+ ? '<span class="k-skel-cap" title="' + esc(sc.prompts[i]) + '">' + esc(capAt(sc, i)) + '</span>' : '';
453
494
  cells += '<div class="k-skel ' + shape + '" data-cell="' + i + '">' + cap + '</div>';
454
495
  }
455
496
  // Grid class caps at n4 — the auto-fill rule handles any larger batch count.
456
- el('stage').innerHTML = '<div class="k-gen-grid n' + Math.min(n, 4) + '">' + cells + '</div>';
497
+ el('stage').innerHTML = '<div class="k-gen-grid n' + gridCols(n, kind) + '">' + cells + '</div>';
457
498
  renderStopButton(sc);
458
499
  schedulePoll(sc);
459
500
  }
@@ -709,7 +750,7 @@ function handleBatchStatus(sc, st) {
709
750
  }
710
751
  scenes.push({
711
752
  scene_number: i + 1,
712
- title: (sc.prompts && sc.prompts[i]) || '',
753
+ title: capAt(sc, i),
713
754
  image_urls: sc.kind === 'video' ? [] : urls,
714
755
  video_urls: sc.kind === 'video' ? urls : []
715
756
  });
@@ -743,7 +784,7 @@ function fillBatchCell(sc, i, g) {
743
784
  cell.setAttribute('data-done', '1');
744
785
  cell.classList.add('done');
745
786
  var cap = (sc.prompts && sc.prompts[i])
746
- ? '<span class="k-skel-cap" title="' + esc(sc.prompts[i]) + '">' + esc(sc.prompts[i]) + '</span>' : '';
787
+ ? '<span class="k-skel-cap" title="' + esc(sc.prompts[i]) + '">' + esc(capAt(sc, i)) + '</span>' : '';
747
788
  if (sc.prompts && sc.prompts[i]) cell.title = sc.prompts[i];
748
789
  // The controls attribute is not optional here: this cell is a FINISHED result
749
790
  // the user is meant to watch, and without it the tile was a muted poster with
@@ -939,7 +980,7 @@ function renderBatchGrid(sc) {
939
980
  var items = sceneItems(sc);
940
981
  if (!items.length) return renderError('No completed results received');
941
982
  var shape = items[0].type === 'video' ? 'video' : 'square';
942
- el('stage').innerHTML = '<div class="k-gen-grid n' + Math.min(items.length, 4) + '">' +
983
+ el('stage').innerHTML = '<div class="k-gen-grid n' + gridCols(items.length, items[0].type) + '">' +
943
984
  items.map(function (it, i) {
944
985
  return '<div class="k-skel done ' + shape + '" data-focus="' + i + '"' +
945
986
  (it.label ? ' title="' + esc(it.label) + '"' : '') + '>' +