@luckydraw/cumulus 0.27.15 → 0.27.17

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.
@@ -720,16 +720,32 @@
720
720
  '.cumulus-panel-action-btn:hover { color: #ddd; border-color: #555; }',
721
721
  '.cumulus-panel-action-btn.active { color: #e0e0e0; border-color: #0066cc; background: rgba(0,102,204,0.12); }',
722
722
 
723
- /* ── Sidebar thread delete button ── */
724
- '.cumulus-thread-item { position: relative; }',
725
- '.cumulus-thread-delete-btn {',
726
- ' display: none; position: absolute; right: 6px; top: 50%; transform: translateY(-50%);',
727
- ' background: none; border: none; color: #666;',
728
- ' cursor: pointer; font-size: 14px; padding: 2px 5px; line-height: 1;',
729
- ' border-radius: 3px; z-index: 1;',
723
+ /* ── Context menu (right-click on thread) ── */
724
+ '.cumulus-context-menu {',
725
+ ' position: fixed; z-index: 300; background: #2a2a2a; border: 1px solid #444;',
726
+ ' border-radius: 6px; padding: 4px 0; min-width: 140px;',
727
+ ' box-shadow: 0 4px 12px rgba(0,0,0,0.5); font-size: 13px;',
728
+ '}',
729
+ '.cumulus-context-menu-item {',
730
+ ' padding: 6px 14px; color: #ccc; cursor: pointer; display: block;',
731
+ ' width: 100%; border: none; background: none; text-align: left;',
732
+ ' font-family: inherit; font-size: inherit;',
733
+ '}',
734
+ '.cumulus-context-menu-item:hover { background: #3a3a3a; color: #fff; }',
735
+ '.cumulus-context-menu-item.danger { color: #ef4444; }',
736
+ '.cumulus-context-menu-item.danger:hover { background: rgba(239,68,68,0.15); color: #ff6b6b; }',
737
+
738
+ /* ── Unread dot (glowing) ── */
739
+ '.cumulus-thread-unread {',
740
+ ' width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0;',
741
+ ' background: #3b82f6;',
742
+ ' box-shadow: 0 0 4px #3b82f6, 0 0 8px rgba(59,130,246,0.4);',
743
+ ' animation: cumulus-unread-pulse 2s ease-in-out infinite;',
744
+ '}',
745
+ '@keyframes cumulus-unread-pulse {',
746
+ ' 0%, 100% { box-shadow: 0 0 4px #3b82f6, 0 0 8px rgba(59,130,246,0.4); }',
747
+ ' 50% { box-shadow: 0 0 6px #3b82f6, 0 0 14px rgba(59,130,246,0.6); }',
730
748
  '}',
731
- '.cumulus-thread-item:hover .cumulus-thread-delete-btn { display: block; }',
732
- '.cumulus-thread-delete-btn:hover { color: #ef4444; background: rgba(239,68,68,0.1); }',
733
749
 
734
750
  /* ── Message checkbox (selection mode) ── */
735
751
  '.cumulus-msg-checkbox {',
@@ -2201,6 +2217,9 @@
2201
2217
  // Thread list from server: [{ name, messageCount, lastActivity }]
2202
2218
  var allThreads = [];
2203
2219
 
2220
+ // Unread threads: threadName -> true
2221
+ var unreadThreads = {};
2222
+
2204
2223
  // Currently visible panel names (ordered, max 3)
2205
2224
  var visibleThreads = [];
2206
2225
 
@@ -2500,6 +2519,13 @@
2500
2519
  item.appendChild(folderEl);
2501
2520
  }
2502
2521
 
2522
+ // Unread indicator
2523
+ if (unreadThreads[name]) {
2524
+ var unreadDot = document.createElement('span');
2525
+ unreadDot.className = 'cumulus-thread-unread';
2526
+ item.appendChild(unreadDot);
2527
+ }
2528
+
2503
2529
  if (messageCount !== undefined && messageCount !== null) {
2504
2530
  var countEl = document.createElement('span');
2505
2531
  countEl.className = 'cumulus-thread-count';
@@ -2507,17 +2533,12 @@
2507
2533
  item.appendChild(countEl);
2508
2534
  }
2509
2535
 
2510
- // Delete button (revealed on hover via CSS)
2511
- var deleteBtn = document.createElement('button');
2512
- deleteBtn.className = 'cumulus-thread-delete-btn';
2513
- deleteBtn.setAttribute('title', 'Delete thread');
2514
- deleteBtn.setAttribute('data-testid', 'webchat-thread-delete');
2515
- deleteBtn.textContent = '\u2715';
2516
- deleteBtn.addEventListener('click', function (e) {
2536
+ // Right-click context menu
2537
+ item.addEventListener('contextmenu', function (e) {
2538
+ e.preventDefault();
2517
2539
  e.stopPropagation();
2518
- showThreadDeleteConfirm(name, item);
2540
+ showThreadContextMenu(name, e.clientX, e.clientY);
2519
2541
  });
2520
- item.appendChild(deleteBtn);
2521
2542
 
2522
2543
  item.addEventListener('click', function (e) {
2523
2544
  var multiSelect = e.metaKey || e.ctrlKey;
@@ -2531,8 +2552,48 @@
2531
2552
  return item;
2532
2553
  }
2533
2554
 
2555
+ // ── Thread context menu (right-click) ──
2556
+ function dismissContextMenu() {
2557
+ var existing = document.querySelector('.cumulus-context-menu');
2558
+ if (existing) existing.remove();
2559
+ document.removeEventListener('click', dismissContextMenu);
2560
+ document.removeEventListener('contextmenu', dismissContextMenu);
2561
+ }
2562
+
2563
+ function showThreadContextMenu(threadName, x, y) {
2564
+ dismissContextMenu();
2565
+
2566
+ var menu = document.createElement('div');
2567
+ menu.className = 'cumulus-context-menu';
2568
+ menu.style.left = x + 'px';
2569
+ menu.style.top = y + 'px';
2570
+
2571
+ var deleteItem = document.createElement('button');
2572
+ deleteItem.className = 'cumulus-context-menu-item danger';
2573
+ deleteItem.textContent = 'Delete thread';
2574
+ deleteItem.setAttribute('data-testid', 'webchat-thread-delete');
2575
+ deleteItem.addEventListener('click', function () {
2576
+ dismissContextMenu();
2577
+ showThreadDeleteConfirm(threadName);
2578
+ });
2579
+ menu.appendChild(deleteItem);
2580
+
2581
+ document.body.appendChild(menu);
2582
+
2583
+ // Clamp to viewport
2584
+ var rect = menu.getBoundingClientRect();
2585
+ if (rect.right > window.innerWidth) menu.style.left = (window.innerWidth - rect.width - 4) + 'px';
2586
+ if (rect.bottom > window.innerHeight) menu.style.top = (window.innerHeight - rect.height - 4) + 'px';
2587
+
2588
+ // Dismiss on next click or right-click anywhere
2589
+ setTimeout(function () {
2590
+ document.addEventListener('click', dismissContextMenu);
2591
+ document.addEventListener('contextmenu', dismissContextMenu);
2592
+ }, 0);
2593
+ }
2594
+
2534
2595
  // ── Thread delete confirmation ──
2535
- function showThreadDeleteConfirm(threadName, anchorEl) {
2596
+ function showThreadDeleteConfirm(threadName) {
2536
2597
  // Remove any existing confirm dialogs
2537
2598
  var existing = document.querySelector('.cumulus-confirm-dialog[data-context="thread-delete"]');
2538
2599
  if (existing) {
@@ -2602,6 +2663,7 @@
2602
2663
 
2603
2664
  // ── Panel management ──
2604
2665
  function soloThread(name) {
2666
+ delete unreadThreads[name];
2605
2667
  visibleThreads = [name];
2606
2668
  renderSidebar();
2607
2669
  renderContentArea();
@@ -2610,6 +2672,7 @@
2610
2672
  }
2611
2673
 
2612
2674
  function togglePanelThread(name) {
2675
+ delete unreadThreads[name];
2613
2676
  // On mobile, always solo (no side-by-side)
2614
2677
  if (isMobile()) {
2615
2678
  soloThread(name);
@@ -3352,14 +3415,12 @@
3352
3415
  var filePath = addInput.value.trim();
3353
3416
  if (!filePath) return;
3354
3417
  connection &&
3355
- connection.send(
3356
- JSON.stringify({
3357
- type: 'include_add',
3358
- threadName: threadName,
3359
- filePath: filePath,
3360
- scope: currentScope,
3361
- })
3362
- );
3418
+ connection.send({
3419
+ type: 'include_add',
3420
+ threadName: threadName,
3421
+ filePath: filePath,
3422
+ scope: currentScope,
3423
+ });
3363
3424
  addInput.value = '';
3364
3425
  });
3365
3426
 
@@ -3383,7 +3444,7 @@
3383
3444
 
3384
3445
  // Request the list
3385
3446
  connection &&
3386
- connection.send(JSON.stringify({ type: 'include_list', threadName: threadName }));
3447
+ connection.send({ type: 'include_list', threadName: threadName });
3387
3448
  }
3388
3449
 
3389
3450
  function renderIncludeList(panel, threadName, files) {
@@ -3420,13 +3481,11 @@
3420
3481
  removeBtn.setAttribute('title', 'Remove');
3421
3482
  removeBtn.addEventListener('click', function () {
3422
3483
  connection &&
3423
- connection.send(
3424
- JSON.stringify({
3425
- type: 'include_remove',
3426
- threadName: threadName,
3427
- filePath: f.path,
3428
- })
3429
- );
3484
+ connection.send({
3485
+ type: 'include_remove',
3486
+ threadName: threadName,
3487
+ filePath: f.path,
3488
+ });
3430
3489
  });
3431
3490
  item.appendChild(removeBtn);
3432
3491
 
@@ -3472,7 +3531,7 @@
3472
3531
 
3473
3532
  // Request the turn list
3474
3533
  connection &&
3475
- connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
3534
+ connection.send({ type: 'revert_list', threadName: threadName });
3476
3535
  }
3477
3536
 
3478
3537
  function renderRevertList(panel, threadName, turns) {
@@ -3552,7 +3611,7 @@
3552
3611
  cancelBtn.addEventListener('click', function () {
3553
3612
  // Re-request list
3554
3613
  connection &&
3555
- connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
3614
+ connection.send({ type: 'revert_list', threadName: threadName });
3556
3615
  });
3557
3616
  actions.appendChild(cancelBtn);
3558
3617
 
@@ -3564,13 +3623,11 @@
3564
3623
  revertBtn.disabled = true;
3565
3624
  revertBtn.textContent = 'Reverting\u2026';
3566
3625
  connection &&
3567
- connection.send(
3568
- JSON.stringify({
3569
- type: 'revert_execute',
3570
- threadName: threadName,
3571
- targetMessageId: turn.userMessageId,
3572
- })
3573
- );
3626
+ connection.send({
3627
+ type: 'revert_execute',
3628
+ threadName: threadName,
3629
+ targetMessageId: turn.userMessageId,
3630
+ });
3574
3631
  });
3575
3632
  actions.appendChild(revertBtn);
3576
3633
 
@@ -3806,13 +3863,11 @@
3806
3863
  tState.historyLoaded = false;
3807
3864
  // Re-request history
3808
3865
  connection &&
3809
- connection.send(
3810
- JSON.stringify({
3811
- type: 'history',
3812
- threadName: data.threadName,
3813
- limit: 200,
3814
- })
3815
- );
3866
+ connection.send({
3867
+ type: 'history',
3868
+ threadName: data.threadName,
3869
+ limit: 200,
3870
+ });
3816
3871
  if (overlay) overlay.remove();
3817
3872
  } else {
3818
3873
  // Show error in overlay
@@ -3836,6 +3891,10 @@
3836
3891
  break;
3837
3892
  }
3838
3893
  }
3894
+ // Mark unread if thread is not currently visible
3895
+ if (visibleThreads.indexOf(threadName) === -1) {
3896
+ unreadThreads[threadName] = true;
3897
+ }
3839
3898
  // Re-render sidebar to reflect new sort order / move thread up
3840
3899
  renderSidebar();
3841
3900
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckydraw/cumulus",
3
- "version": "0.27.15",
3
+ "version": "0.27.17",
4
4
  "description": "RLM-based CLI chat wrapper for Claude with external history context management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",