@yemi33/minions 0.1.1973 → 0.1.1974

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.
@@ -485,17 +485,103 @@ function ccRenderTabBar() {
485
485
  for (var i = 0; i < _ccTabs.length; i++) {
486
486
  var t = _ccTabs[i];
487
487
  var isActive = t.id === _ccActiveTabId;
488
- html += '<div class="cc-tab' + (isActive ? ' active' : '') + (t._sending ? ' working' : '') + '" onclick="ccSwitchTab(\'' + t.id + '\')" title="' + escHtml(t.title) + '">';
488
+ // draggable="true" + DnD handlers enable click-and-drag reorder of tabs.
489
+ // The handlers below splice _ccTabs in place (preserving per-tab in-flight
490
+ // state: _sending, _queue, _abortController, _streamedText, _toolsUsed)
491
+ // and persist via ccSaveState. The close X / new-tab + opt out of drag
492
+ // with draggable="false" + ondragstart preventDefault so the affordances
493
+ // don't accidentally start a drag.
494
+ html += '<div class="cc-tab' + (isActive ? ' active' : '') + (t._sending ? ' working' : '') + '" draggable="true"'
495
+ + ' ondragstart="ccTabDragStart(event, \'' + t.id + '\')"'
496
+ + ' ondragover="ccTabDragOver(event, \'' + t.id + '\')"'
497
+ + ' ondragleave="ccTabDragLeave(event)"'
498
+ + ' ondrop="ccTabDrop(event, \'' + t.id + '\')"'
499
+ + ' ondragend="ccTabDragEnd(event)"'
500
+ + ' onclick="ccSwitchTab(\'' + t.id + '\')" title="' + escHtml(t.title) + '">';
489
501
  html += '<span class="cc-tab-text">' + escHtml(t.title) + '</span>';
490
502
  if (t._unread) html += '<span class="notif-badge done"></span>';
491
- html += '<span class="cc-tab-close" onclick="event.stopPropagation();ccCloseTab(\'' + t.id + '\')">&times;</span>';
503
+ html += '<span class="cc-tab-close" draggable="false" ondragstart="event.preventDefault();event.stopPropagation();" onmousedown="event.stopPropagation();" onclick="event.stopPropagation();ccCloseTab(\'' + t.id + '\')">&times;</span>';
492
504
  html += '</div>';
493
505
  }
494
- html += '<div class="cc-tab cc-tab-new" onclick="ccNewTab()" title="New tab">+</div>';
506
+ html += '<div class="cc-tab cc-tab-new" draggable="false" ondragstart="event.preventDefault();event.stopPropagation();" onclick="ccNewTab()" title="New tab">+</div>';
495
507
  html += '</div>';
496
508
  bar.innerHTML = html;
497
509
  }
498
510
 
511
+ // ── Tab drag-to-reorder ─────────────────────────────────────────────────────
512
+ // Native HTML5 DnD: dragstart records the source tab id, dragover on a peer
513
+ // tab calls preventDefault to allow drop and adds a drop-target indicator,
514
+ // drop splices _ccTabs in place so per-tab references (and their in-flight
515
+ // state) are preserved, and dragend clears any leftover visual state.
516
+ var _ccDragSourceId = null;
517
+
518
+ function ccTabDragStart(ev, id) {
519
+ _ccDragSourceId = id;
520
+ try {
521
+ ev.dataTransfer.effectAllowed = 'move';
522
+ // setData is required for Firefox to actually start the drag.
523
+ ev.dataTransfer.setData('text/plain', id);
524
+ } catch {}
525
+ var el = ev.currentTarget;
526
+ if (el && el.classList) el.classList.add('cc-tab-dragging');
527
+ }
528
+
529
+ function ccTabDragOver(ev, id) {
530
+ if (!_ccDragSourceId) return;
531
+ // preventDefault must fire on every dragover (including the source) so the
532
+ // drop event actually fires — required to detect "released on same tab".
533
+ ev.preventDefault();
534
+ try { ev.dataTransfer.dropEffect = 'move'; } catch {}
535
+ if (_ccDragSourceId === id) return;
536
+ var el = ev.currentTarget;
537
+ if (el && el.classList) el.classList.add('cc-tab-drop-target');
538
+ }
539
+
540
+ function ccTabDragLeave(ev) {
541
+ var el = ev.currentTarget;
542
+ if (el && el.classList) el.classList.remove('cc-tab-drop-target');
543
+ }
544
+
545
+ function ccTabDrop(ev, targetId) {
546
+ ev.preventDefault();
547
+ var srcId = _ccDragSourceId;
548
+ // Clear all visual indicators before any early-return.
549
+ var nodes = document.querySelectorAll('.cc-tab-drop-target, .cc-tab-dragging');
550
+ for (var i = 0; i < nodes.length; i++) {
551
+ nodes[i].classList.remove('cc-tab-drop-target');
552
+ nodes[i].classList.remove('cc-tab-dragging');
553
+ }
554
+ // Released on the source tab (or no source): treat as a click — activate
555
+ // the tab and skip any reorder/state churn. ccSwitchTab is a no-op when
556
+ // the target is already active.
557
+ if (!srcId || srcId === targetId) {
558
+ if (targetId) ccSwitchTab(targetId);
559
+ return;
560
+ }
561
+ var fromIdx = _ccTabs.findIndex(function(t) { return t.id === srcId; });
562
+ var toIdx = _ccTabs.findIndex(function(t) { return t.id === targetId; });
563
+ if (fromIdx === -1 || toIdx === -1) return;
564
+ // Splice the existing tab reference so per-tab in-flight state survives
565
+ // (_sending, _queue, _abortController, _streamedText, _toolsUsed,
566
+ // _retryRequests, _sendStartedAt, etc.). Do NOT clone/replace the object.
567
+ var moved = _ccTabs.splice(fromIdx, 1)[0];
568
+ _ccTabs.splice(toIdx, 0, moved);
569
+ // _ccActiveTabId is intentionally unchanged: the same tab stays active,
570
+ // just at a new position.
571
+ ccRenderTabBar();
572
+ ccSaveState();
573
+ }
574
+
575
+ function ccTabDragEnd(_ev) {
576
+ _ccDragSourceId = null;
577
+ // Defensive cleanup: dragend always fires, even if drop didn't.
578
+ var nodes = document.querySelectorAll('.cc-tab-dragging, .cc-tab-drop-target');
579
+ for (var i = 0; i < nodes.length; i++) {
580
+ nodes[i].classList.remove('cc-tab-dragging');
581
+ nodes[i].classList.remove('cc-tab-drop-target');
582
+ }
583
+ }
584
+
499
585
  function ccRestoreMessages() {
500
586
  var el = document.getElementById('cc-messages');
501
587
  var tab = _ccActiveTab();
@@ -654,8 +654,11 @@
654
654
 
655
655
  /* Command Center tab bar */
656
656
  .cc-tab-scroll { display: flex; gap: 4px; align-items: center; overflow-x: auto; overflow-y: hidden; flex: 1 1 auto; min-width: 0; scrollbar-width: thin; }
657
- .cc-tab { padding: 4px 10px; font-size: 10px; border: 1px solid var(--border); border-bottom: none; border-radius: 6px 6px 0 0; background: var(--surface2); color: var(--muted); cursor: pointer; white-space: nowrap; max-width: 140px; display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; margin-bottom: -1px; position: relative; }
658
- .cc-tab-new { color: var(--muted); padding: 4px 8px; }
657
+ .cc-tab { padding: 4px 10px; font-size: 10px; border: 1px solid var(--border); border-bottom: none; border-radius: 6px 6px 0 0; background: var(--surface2); color: var(--muted); cursor: grab; white-space: nowrap; max-width: 140px; display: inline-flex; align-items: center; gap: 2px; flex-shrink: 0; margin-bottom: -1px; position: relative; }
658
+ .cc-tab:active { cursor: grabbing; }
659
+ .cc-tab.cc-tab-dragging { opacity: 0.5; cursor: grabbing; }
660
+ .cc-tab.cc-tab-drop-target { box-shadow: inset 2px 0 0 0 var(--blue); }
661
+ .cc-tab-new { color: var(--muted); padding: 4px 8px; cursor: pointer; }
659
662
  .cc-tab-text { overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; }
660
663
  .cc-tab.active { color: var(--text); background: var(--bg); border-color: var(--border); z-index: 1; font-weight: 600; }
661
664
  .cc-tab.working { border-color: var(--blue); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1973",
3
+ "version": "0.1.1974",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"