@luckydraw/cumulus 0.27.17 → 0.27.19

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.
Files changed (58) hide show
  1. package/dist/cli/cumulus.js +0 -0
  2. package/dist/gateway/adapters/webchat.d.ts +1 -3
  3. package/dist/gateway/adapters/webchat.d.ts.map +1 -1
  4. package/dist/gateway/adapters/webchat.js +7 -150
  5. package/dist/gateway/adapters/webchat.js.map +1 -1
  6. package/dist/gateway/config.d.ts +7 -0
  7. package/dist/gateway/config.d.ts.map +1 -1
  8. package/dist/gateway/config.js +13 -0
  9. package/dist/gateway/config.js.map +1 -1
  10. package/dist/gateway/daemon.js +26 -8
  11. package/dist/gateway/daemon.js.map +1 -1
  12. package/dist/gateway/gateway-agents-mcp.js +49 -0
  13. package/dist/gateway/gateway-agents-mcp.js.map +1 -1
  14. package/dist/gateway/push.d.ts +66 -0
  15. package/dist/gateway/push.d.ts.map +1 -0
  16. package/dist/gateway/push.js +131 -0
  17. package/dist/gateway/push.js.map +1 -0
  18. package/dist/gateway/server.d.ts +3 -9
  19. package/dist/gateway/server.d.ts.map +1 -1
  20. package/dist/gateway/server.js +112 -243
  21. package/dist/gateway/server.js.map +1 -1
  22. package/dist/gateway/static/sw.js +73 -0
  23. package/dist/gateway/static/widget.js +125 -759
  24. package/dist/index.d.ts +3 -5
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/lib/config.d.ts +0 -8
  29. package/dist/lib/config.d.ts.map +1 -1
  30. package/dist/lib/config.js +1 -61
  31. package/dist/lib/config.js.map +1 -1
  32. package/dist/lib/content-detector.d.ts.map +1 -1
  33. package/dist/lib/content-detector.js +2 -9
  34. package/dist/lib/content-detector.js.map +1 -1
  35. package/dist/lib/gateway.d.ts +2 -14
  36. package/dist/lib/gateway.d.ts.map +1 -1
  37. package/dist/lib/gateway.js +103 -234
  38. package/dist/lib/gateway.js.map +1 -1
  39. package/dist/lib/summaries.d.ts.map +1 -1
  40. package/dist/lib/summaries.js +2 -9
  41. package/dist/lib/summaries.js.map +1 -1
  42. package/dist/mcp/index.js +0 -0
  43. package/dist/mcp/tool-handler.d.ts.map +1 -1
  44. package/dist/mcp/tool-handler.js +3 -10
  45. package/dist/mcp/tool-handler.js.map +1 -1
  46. package/dist/tui/hooks/useClaudeProcess.d.ts.map +1 -1
  47. package/dist/tui/hooks/useClaudeProcess.js +23 -18
  48. package/dist/tui/hooks/useClaudeProcess.js.map +1 -1
  49. package/package.json +3 -1
  50. package/dist/gateway/device-mcp.d.ts +0 -21
  51. package/dist/gateway/device-mcp.d.ts.map +0 -1
  52. package/dist/gateway/device-mcp.js +0 -165
  53. package/dist/gateway/device-mcp.js.map +0 -1
  54. package/dist/lib/executor.d.ts +0 -168
  55. package/dist/lib/executor.d.ts.map +0 -1
  56. package/dist/lib/executor.js +0 -284
  57. package/dist/lib/executor.js.map +0 -1
  58. package/dist/lib/projects.d.ts.map +0 -1
@@ -2,7 +2,6 @@
2
2
  * Cumulus Web Chat Widget — self-contained, no external dependencies.
3
3
  * Embeddable via <script src="/widget.js" data-api-key="..."></script>
4
4
  * or usable standalone at /chat.
5
- * @version 0.27.15
6
5
  *
7
6
  * < 80KB unminified.
8
7
  */
@@ -155,38 +154,6 @@
155
154
  ' word-wrap: break-word;',
156
155
  '}',
157
156
 
158
- /* ── Timestamps ── */
159
- '.cumulus-msg-time {',
160
- ' font-size: 0.75em; color: #555; margin-top: 0.3em; padding: 0 0.3em;',
161
- '}',
162
- '.cumulus-msg-row:has(.user) .cumulus-msg-time { align-self: flex-end; }',
163
- '.cumulus-msg-row:has(.assistant) .cumulus-msg-time { align-self: flex-start; }',
164
-
165
- /* ── Agent messages ── */
166
- '.cumulus-msg.agent {',
167
- ' align-self: flex-start;',
168
- ' background: transparent;',
169
- ' color: #e0e0e0;',
170
- ' padding: 0.4em 0 0.4em 0.75em;',
171
- ' width: 100%;',
172
- ' word-wrap: break-word;',
173
- ' border-left: 2px solid var(--agent-color, #9966cc);',
174
- '}',
175
- '.cumulus-agent-header {',
176
- ' display: flex; align-items: center; gap: 0.4em;',
177
- ' margin-bottom: 0.3em; font-size: 0.82em;',
178
- '}',
179
- '.cumulus-agent-name {',
180
- ' font-family: "SF Mono", "Fira Code", Consolas, monospace;',
181
- ' font-weight: 600; color: var(--agent-color, #9966cc);',
182
- '}',
183
- '.cumulus-agent-name::before { content: "["; opacity: 0.5; }',
184
- '.cumulus-agent-name::after { content: "]"; opacity: 0.5; }',
185
- '.cumulus-agent-context {',
186
- ' font-size: 0.9em; color: #666;',
187
- ' font-family: "SF Mono", "Fira Code", Consolas, monospace;',
188
- '}',
189
-
190
157
  /* ── Markdown rendered inside assistant messages ── */
191
158
  '.cumulus-msg.assistant p { margin: 0.4em 0; }',
192
159
  '.cumulus-msg.assistant p:first-child { margin-top: 0; }',
@@ -718,97 +685,6 @@
718
685
  ' border-radius: 3px; font-family: inherit; white-space: nowrap;',
719
686
  '}',
720
687
  '.cumulus-panel-action-btn:hover { color: #ddd; border-color: #555; }',
721
- '.cumulus-panel-action-btn.active { color: #e0e0e0; border-color: #0066cc; background: rgba(0,102,204,0.12); }',
722
-
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); }',
748
- '}',
749
-
750
- /* ── Message checkbox (selection mode) ── */
751
- '.cumulus-msg-checkbox {',
752
- ' display: none; flex-shrink: 0; align-self: flex-start; margin-top: 0.4em;',
753
- ' width: 16px; height: 16px; cursor: pointer; accent-color: #0066cc;',
754
- '}',
755
- '.cumulus-selection-mode .cumulus-msg-checkbox { display: block; }',
756
- '.cumulus-selection-mode .cumulus-msg-row {',
757
- ' flex-direction: row; align-items: flex-start; gap: 8px;',
758
- '}',
759
- '.cumulus-selection-mode .cumulus-msg-row > *:not(.cumulus-msg-checkbox) { flex: 1; min-width: 0; }',
760
-
761
- /* ── Selection action bar (floats at bottom of messages area) ── */
762
- '.cumulus-selection-bar {',
763
- ' display: flex; align-items: center; gap: 8px;',
764
- ' padding: 8px 12px;',
765
- ' background: #2d2d2d; border-top: 1px solid #3a3a3a;',
766
- ' flex-shrink: 0;',
767
- '}',
768
- '.cumulus-selection-count { font-size: 13px; color: #aaa; flex: 1; }',
769
- '.cumulus-selection-select-all-btn {',
770
- ' background: none; border: 1px solid #555; color: #ccc;',
771
- ' padding: 4px 10px; border-radius: 4px; font-size: 12px;',
772
- ' cursor: pointer; font-family: inherit;',
773
- '}',
774
- '.cumulus-selection-select-all-btn:hover { border-color: #888; color: #e0e0e0; }',
775
- '.cumulus-selection-delete-btn {',
776
- ' background: #8b2222; border: none; color: white;',
777
- ' padding: 4px 12px; border-radius: 4px; font-size: 12px;',
778
- ' cursor: pointer; font-family: inherit; font-weight: 600;',
779
- '}',
780
- '.cumulus-selection-delete-btn:hover:not(:disabled) { background: #a33; }',
781
- '.cumulus-selection-delete-btn:disabled { background: #3a2a2a; color: #666; cursor: not-allowed; }',
782
-
783
- /* ── Confirm dialog overlay ── */
784
- '.cumulus-confirm-dialog {',
785
- ' position: absolute; top: 0; left: 0; right: 0; bottom: 0;',
786
- ' background: rgba(0,0,0,0.6); z-index: 20;',
787
- ' display: flex; align-items: center; justify-content: center;',
788
- '}',
789
- '.cumulus-confirm-dialog-box {',
790
- ' background: #2d2d2d; border: 1px solid #4a4a4a; border-radius: 8px;',
791
- ' padding: 1.5em; max-width: 320px; width: 90%;',
792
- ' display: flex; flex-direction: column; gap: 1em; text-align: center;',
793
- ' box-shadow: 0 8px 32px rgba(0,0,0,0.5);',
794
- '}',
795
- '.cumulus-confirm-dialog-title { font-weight: 600; font-size: 15px; color: #e0e0e0; }',
796
- '.cumulus-confirm-dialog-text { font-size: 13px; color: #aaa; }',
797
- '.cumulus-confirm-dialog-warn { font-size: 12px; color: #f59e0b; }',
798
- '.cumulus-confirm-dialog-actions { display: flex; gap: 8px; justify-content: center; }',
799
- '.cumulus-confirm-dialog-cancel {',
800
- ' padding: 0.4em 1.2em; border-radius: 4px; cursor: pointer;',
801
- ' font-size: 13px; border: 1px solid #3a3a3a; background: #2a2a2a; color: #ccc;',
802
- ' font-family: inherit;',
803
- '}',
804
- '.cumulus-confirm-dialog-cancel:hover { background: #3a3a3a; }',
805
- '.cumulus-confirm-dialog-confirm {',
806
- ' padding: 0.4em 1.2em; border-radius: 4px; cursor: pointer;',
807
- ' font-size: 13px; border: none; background: #8b2222; color: white;',
808
- ' font-family: inherit; font-weight: 600;',
809
- '}',
810
- '.cumulus-confirm-dialog-confirm:hover { background: #a33; }',
811
- '.cumulus-confirm-dialog-confirm:disabled { background: #3a2a2a; color: #666; cursor: not-allowed; }',
812
688
 
813
689
  /* ── Overlay (covers the thread panel) ── */
814
690
  '.cumulus-overlay {',
@@ -1044,67 +920,6 @@
1044
920
  // else, then reinsert code blocks. This prevents markdown patterns inside
1045
921
  // code from being processed.
1046
922
 
1047
- // ── Agent message detection + timestamps ──────────────────────────────────
1048
- var AGENT_MSG_RE = /^\[([^\]]+)\s*\u2192\s*([^\]]+)\]:\s*/; // [sender → recipient]:
1049
- var AGENT_COLORS = ['#9966cc', '#2aa198', '#e69500', '#d33682', '#85c025', '#ff6b6b'];
1050
-
1051
- function getAgentColor(name) {
1052
- var hash = 0;
1053
- for (var i = 0; i < name.length; i++) hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0;
1054
- return AGENT_COLORS[Math.abs(hash) % AGENT_COLORS.length];
1055
- }
1056
-
1057
- function parseAgentMessage(content) {
1058
- var match = content.match(AGENT_MSG_RE);
1059
- if (!match) return null;
1060
- var body = content.slice(match[0].length).replace(/\n\n\(Reply using send_to_agent\([^)]*\)[^)]*\)\s*$/, '').trim();
1061
- // Also strip CC/broadcast reply hints
1062
- body = body.replace(/\n\n\(You are CC'd on this message[^)]*\)\s*$/, '').trim();
1063
- body = body.replace(/\n\n\(Review the above messages[^)]*\)\s*$/, '').trim();
1064
- return { sender: match[1].trim(), recipient: match[2].trim(), body: body };
1065
- }
1066
-
1067
- function formatTime(timestamp) {
1068
- if (!timestamp) return null;
1069
- var d = new Date(timestamp);
1070
- return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
1071
- }
1072
-
1073
- function appendTimestamp(parent, timestamp) {
1074
- var time = formatTime(timestamp);
1075
- if (time) {
1076
- var timeEl = document.createElement('div');
1077
- timeEl.className = 'cumulus-msg-time';
1078
- timeEl.textContent = time;
1079
- parent.appendChild(timeEl);
1080
- }
1081
- }
1082
-
1083
- function buildAgentMsgEl(agent) {
1084
- var el = document.createElement('div');
1085
- el.className = 'cumulus-msg agent';
1086
- el.style.setProperty('--agent-color', getAgentColor(agent.sender));
1087
-
1088
- var header = document.createElement('div');
1089
- header.className = 'cumulus-agent-header';
1090
- var nameEl = document.createElement('span');
1091
- nameEl.className = 'cumulus-agent-name';
1092
- nameEl.textContent = agent.sender;
1093
- header.appendChild(nameEl);
1094
- var ctx = document.createElement('span');
1095
- ctx.className = 'cumulus-agent-context';
1096
- ctx.textContent = '\u2192 ' + agent.recipient;
1097
- header.appendChild(ctx);
1098
- el.appendChild(header);
1099
-
1100
- var body = document.createElement('div');
1101
- body.innerHTML = renderMarkdown(agent.body);
1102
- el.appendChild(body);
1103
- return el;
1104
- }
1105
-
1106
- // ── Markdown ──────────────────────────────────────────────────────────────
1107
-
1108
923
  function renderMarkdown(text) {
1109
924
  // Phase 1: Extract fenced code blocks — replace with unique tokens
1110
925
  var codeBlocks = [];
@@ -1520,8 +1335,6 @@
1520
1335
  var connectionStatus = 'disconnected';
1521
1336
  var authenticated = false;
1522
1337
  var pendingAttachments = [];
1523
- var selectionMode = false;
1524
- var selectedIds = {};
1525
1338
 
1526
1339
  // ── Panel ──
1527
1340
  var panel = document.createElement('div');
@@ -1538,9 +1351,6 @@
1538
1351
  '<span class="cumulus-status-dot" data-testid="webchat-status-dot"></span>' +
1539
1352
  '<span data-testid="webchat-status-text">Disconnected</span>' +
1540
1353
  '</span>' +
1541
- '<div class="cumulus-panel-actions">' +
1542
- '<button class="cumulus-panel-action-btn" data-testid="webchat-select-btn" title="Select messages to delete">Select</button>' +
1543
- '</div>' +
1544
1354
  '<button class="cumulus-close-btn" data-testid="webchat-close">&times;</button>';
1545
1355
  panel.appendChild(header);
1546
1356
 
@@ -1639,9 +1449,7 @@
1639
1449
 
1640
1450
  // ── Rendering ──
1641
1451
  function scrollToBottom() {
1642
- requestAnimationFrame(function () {
1643
- messagesEl.scrollTop = messagesEl.scrollHeight;
1644
- });
1452
+ messagesEl.scrollTop = messagesEl.scrollHeight;
1645
1453
  }
1646
1454
 
1647
1455
  function isWideUserMessage(content) {
@@ -1710,51 +1518,20 @@
1710
1518
 
1711
1519
  function renderMessages() {
1712
1520
  messagesEl.innerHTML = '';
1713
- // Apply selection mode class
1714
- if (selectionMode) {
1715
- messagesEl.classList.add('cumulus-selection-mode');
1716
- } else {
1717
- messagesEl.classList.remove('cumulus-selection-mode');
1718
- }
1719
1521
  if (messages.length === 0 && !streaming) {
1720
1522
  messagesEl.innerHTML =
1721
1523
  '<div class="cumulus-empty">Send a message to start chatting</div>';
1722
- updateEmbeddedSelectionBar();
1723
1524
  return;
1724
1525
  }
1725
1526
  for (var i = 0; i < messages.length; i++) {
1726
1527
  var msg = messages[i];
1727
1528
  var row = document.createElement('div');
1728
1529
  row.className = 'cumulus-msg-row';
1729
- // Add checkbox in selection mode
1730
- if (selectionMode && msg.id) {
1731
- var cb = document.createElement('input');
1732
- cb.type = 'checkbox';
1733
- cb.className = 'cumulus-msg-checkbox';
1734
- cb.checked = !!selectedIds[msg.id];
1735
- (function (msgId) {
1736
- cb.addEventListener('change', function () {
1737
- if (cb.checked) {
1738
- selectedIds[msgId] = true;
1739
- } else {
1740
- delete selectedIds[msgId];
1741
- }
1742
- updateEmbeddedSelectionBar();
1743
- });
1744
- })(msg.id);
1745
- row.appendChild(cb);
1746
- }
1747
1530
  if (msg.role === 'user') {
1748
- var agent = parseAgentMessage(msg.content);
1749
- if (agent) {
1750
- row.appendChild(buildAgentMsgEl(agent));
1751
- } else {
1752
- row.appendChild(buildUserMsgEl(msg));
1753
- }
1531
+ row.appendChild(buildUserMsgEl(msg));
1754
1532
  } else {
1755
1533
  row.appendChild(buildAssistantMsgEl(msg.content, false));
1756
1534
  }
1757
- appendTimestamp(row, msg.timestamp);
1758
1535
  messagesEl.appendChild(row);
1759
1536
  }
1760
1537
  if (streaming) {
@@ -1764,109 +1541,6 @@
1764
1541
  messagesEl.appendChild(row);
1765
1542
  }
1766
1543
  scrollToBottom();
1767
- updateEmbeddedSelectionBar();
1768
- }
1769
-
1770
- function updateEmbeddedSelectionBar() {
1771
- var existingBar = panel.querySelector('.cumulus-selection-bar');
1772
- if (existingBar) existingBar.remove();
1773
- if (!selectionMode) return;
1774
-
1775
- var selectedCount = Object.keys(selectedIds).length;
1776
- var selectableCount = messages.filter(function (m) { return !!m.id; }).length;
1777
-
1778
- var bar = document.createElement('div');
1779
- bar.className = 'cumulus-selection-bar';
1780
-
1781
- var countEl = document.createElement('span');
1782
- countEl.className = 'cumulus-selection-count';
1783
- countEl.textContent = selectedCount + ' selected';
1784
- bar.appendChild(countEl);
1785
-
1786
- var selectAllBtn = document.createElement('button');
1787
- selectAllBtn.className = 'cumulus-selection-select-all-btn';
1788
- selectAllBtn.textContent = selectedCount === selectableCount && selectableCount > 0 ? 'Deselect All' : 'Select All';
1789
- selectAllBtn.addEventListener('click', function () {
1790
- if (selectedCount === selectableCount && selectableCount > 0) {
1791
- selectedIds = {};
1792
- } else {
1793
- messages.forEach(function (m) {
1794
- if (m.id) selectedIds[m.id] = true;
1795
- });
1796
- }
1797
- renderMessages();
1798
- });
1799
- bar.appendChild(selectAllBtn);
1800
-
1801
- var deleteBtn = document.createElement('button');
1802
- deleteBtn.className = 'cumulus-selection-delete-btn';
1803
- deleteBtn.textContent = 'Delete';
1804
- deleteBtn.disabled = selectedCount === 0;
1805
- deleteBtn.addEventListener('click', function () {
1806
- if (selectedCount === 0) return;
1807
- showEmbeddedDeleteMessagesConfirm(Object.keys(selectedIds));
1808
- });
1809
- bar.appendChild(deleteBtn);
1810
-
1811
- var inputAreaEl = panel.querySelector('.cumulus-input-area');
1812
- if (inputAreaEl) {
1813
- panel.insertBefore(bar, inputAreaEl);
1814
- } else {
1815
- panel.appendChild(bar);
1816
- }
1817
- }
1818
-
1819
- function showEmbeddedDeleteMessagesConfirm(ids) {
1820
- var existing = panel.querySelector('.cumulus-confirm-dialog');
1821
- if (existing) existing.remove();
1822
-
1823
- var dialog = document.createElement('div');
1824
- dialog.className = 'cumulus-confirm-dialog';
1825
-
1826
- var box = document.createElement('div');
1827
- box.className = 'cumulus-confirm-dialog-box';
1828
-
1829
- var title = document.createElement('div');
1830
- title.className = 'cumulus-confirm-dialog-title';
1831
- title.textContent = 'Delete Messages';
1832
- box.appendChild(title);
1833
-
1834
- var text = document.createElement('div');
1835
- text.className = 'cumulus-confirm-dialog-text';
1836
- text.textContent = ids.length + ' message' + (ids.length === 1 ? '' : 's') + ' will be permanently deleted.';
1837
- box.appendChild(text);
1838
-
1839
- var warn = document.createElement('div');
1840
- warn.className = 'cumulus-confirm-dialog-warn';
1841
- warn.textContent = 'This cannot be undone.';
1842
- box.appendChild(warn);
1843
-
1844
- var actions = document.createElement('div');
1845
- actions.className = 'cumulus-confirm-dialog-actions';
1846
-
1847
- var cancelBtn = document.createElement('button');
1848
- cancelBtn.className = 'cumulus-confirm-dialog-cancel';
1849
- cancelBtn.textContent = 'Cancel';
1850
- cancelBtn.addEventListener('click', function () { dialog.remove(); });
1851
- actions.appendChild(cancelBtn);
1852
-
1853
- var confirmBtn = document.createElement('button');
1854
- confirmBtn.className = 'cumulus-confirm-dialog-confirm';
1855
- confirmBtn.setAttribute('data-testid', 'webchat-delete-messages-confirm');
1856
- confirmBtn.textContent = 'Delete';
1857
- confirmBtn.addEventListener('click', function () {
1858
- confirmBtn.disabled = true;
1859
- confirmBtn.textContent = 'Deleting\u2026';
1860
- if (connection) {
1861
- connection.send({ type: 'delete_messages', threadName: sessionId, messageIds: ids });
1862
- }
1863
- dialog.remove();
1864
- });
1865
- actions.appendChild(confirmBtn);
1866
-
1867
- box.appendChild(actions);
1868
- dialog.appendChild(box);
1869
- panel.appendChild(dialog);
1870
1544
  }
1871
1545
 
1872
1546
  function updateSendBtn() {
@@ -1963,7 +1637,7 @@
1963
1637
  case 'history':
1964
1638
  if (data.messages && data.messages.length > 0) {
1965
1639
  messages = data.messages.map(function (m) {
1966
- return { id: m.id, role: m.role, content: m.content, timestamp: m.timestamp };
1640
+ return { role: m.role, content: m.content };
1967
1641
  });
1968
1642
  renderMessages();
1969
1643
  }
@@ -2019,30 +1693,6 @@
2019
1693
  updateSendBtn();
2020
1694
  renderMessages();
2021
1695
  break;
2022
- case 'new_messages':
2023
- // Agent inject completed — append new messages and re-render
2024
- if (data.messages && data.messages.length > 0) {
2025
- for (var nm = 0; nm < data.messages.length; nm++) {
2026
- messages.push({ role: data.messages[nm].role, content: data.messages[nm].content });
2027
- }
2028
- renderMessages();
2029
- }
2030
- break;
2031
- case 'messages_deleted':
2032
- // Server confirmed deletion — reload history and exit selection mode
2033
- selectionMode = false;
2034
- selectedIds = {};
2035
- var embSelectBtn = panel.querySelector('[data-testid="webchat-select-btn"]');
2036
- if (embSelectBtn) {
2037
- embSelectBtn.textContent = 'Select';
2038
- embSelectBtn.classList.remove('active');
2039
- }
2040
- messages = [];
2041
- // Re-request history
2042
- if (connection) {
2043
- connection.send({ type: 'history', threadName: sessionId, limit: 200 });
2044
- }
2045
- break;
2046
1696
  }
2047
1697
  }
2048
1698
 
@@ -2158,22 +1808,6 @@
2158
1808
  });
2159
1809
  }
2160
1810
 
2161
- // Select button for embedded mode
2162
- var embSelectBtn = panel.querySelector('[data-testid="webchat-select-btn"]');
2163
- if (embSelectBtn) {
2164
- embSelectBtn.addEventListener('click', function () {
2165
- selectionMode = !selectionMode;
2166
- selectedIds = {};
2167
- embSelectBtn.textContent = selectionMode ? 'Done' : 'Select';
2168
- if (selectionMode) {
2169
- embSelectBtn.classList.add('active');
2170
- } else {
2171
- embSelectBtn.classList.remove('active');
2172
- }
2173
- renderMessages();
2174
- });
2175
- }
2176
-
2177
1811
  // Embedded mode: no auth panel handling needed (API key from attribute)
2178
1812
  function startConnection() {
2179
1813
  if (connection) {
@@ -2217,9 +1851,6 @@
2217
1851
  // Thread list from server: [{ name, messageCount, lastActivity }]
2218
1852
  var allThreads = [];
2219
1853
 
2220
- // Unread threads: threadName -> true
2221
- var unreadThreads = {};
2222
-
2223
1854
  // Currently visible panel names (ordered, max 3)
2224
1855
  var visibleThreads = [];
2225
1856
 
@@ -2237,8 +1868,6 @@
2237
1868
  interjecting: false,
2238
1869
  pendingAttachments: [],
2239
1870
  historyLoaded: false,
2240
- selectionMode: false,
2241
- selectedIds: {},
2242
1871
  };
2243
1872
  }
2244
1873
  return threadStates[threadName];
@@ -2519,13 +2148,6 @@
2519
2148
  item.appendChild(folderEl);
2520
2149
  }
2521
2150
 
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
-
2529
2151
  if (messageCount !== undefined && messageCount !== null) {
2530
2152
  var countEl = document.createElement('span');
2531
2153
  countEl.className = 'cumulus-thread-count';
@@ -2533,13 +2155,6 @@
2533
2155
  item.appendChild(countEl);
2534
2156
  }
2535
2157
 
2536
- // Right-click context menu
2537
- item.addEventListener('contextmenu', function (e) {
2538
- e.preventDefault();
2539
- e.stopPropagation();
2540
- showThreadContextMenu(name, e.clientX, e.clientY);
2541
- });
2542
-
2543
2158
  item.addEventListener('click', function (e) {
2544
2159
  var multiSelect = e.metaKey || e.ctrlKey;
2545
2160
  if (multiSelect) {
@@ -2552,118 +2167,8 @@
2552
2167
  return item;
2553
2168
  }
2554
2169
 
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
-
2595
- // ── Thread delete confirmation ──
2596
- function showThreadDeleteConfirm(threadName) {
2597
- // Remove any existing confirm dialogs
2598
- var existing = document.querySelector('.cumulus-confirm-dialog[data-context="thread-delete"]');
2599
- if (existing) {
2600
- existing.remove();
2601
- return;
2602
- }
2603
-
2604
- // Build inline confirm dialog attached to the sidebar scroll area
2605
- var dialog = document.createElement('div');
2606
- dialog.className = 'cumulus-confirm-dialog';
2607
- dialog.setAttribute('data-context', 'thread-delete');
2608
- dialog.style.cssText = 'position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); z-index: 200; display: flex; align-items: center; justify-content: center;';
2609
-
2610
- var box = document.createElement('div');
2611
- box.className = 'cumulus-confirm-dialog-box';
2612
-
2613
- var title = document.createElement('div');
2614
- title.className = 'cumulus-confirm-dialog-title';
2615
- title.textContent = 'Delete Thread';
2616
- box.appendChild(title);
2617
-
2618
- var text = document.createElement('div');
2619
- text.className = 'cumulus-confirm-dialog-text';
2620
- text.textContent = 'Delete "' + threadName + '"?';
2621
- box.appendChild(text);
2622
-
2623
- var warn = document.createElement('div');
2624
- warn.className = 'cumulus-confirm-dialog-warn';
2625
- warn.textContent = 'All messages and history will be permanently removed.';
2626
- box.appendChild(warn);
2627
-
2628
- var actions = document.createElement('div');
2629
- actions.className = 'cumulus-confirm-dialog-actions';
2630
-
2631
- var cancelBtn = document.createElement('button');
2632
- cancelBtn.className = 'cumulus-confirm-dialog-cancel';
2633
- cancelBtn.textContent = 'Cancel';
2634
- cancelBtn.addEventListener('click', function () {
2635
- dialog.remove();
2636
- });
2637
- actions.appendChild(cancelBtn);
2638
-
2639
- var confirmBtn = document.createElement('button');
2640
- confirmBtn.className = 'cumulus-confirm-dialog-confirm';
2641
- confirmBtn.setAttribute('data-testid', 'webchat-thread-delete-confirm');
2642
- confirmBtn.textContent = 'Delete';
2643
- confirmBtn.addEventListener('click', function () {
2644
- confirmBtn.disabled = true;
2645
- confirmBtn.textContent = 'Deleting\u2026';
2646
- if (connection) {
2647
- connection.send({ type: 'delete_thread', threadName: threadName });
2648
- }
2649
- dialog.remove();
2650
- });
2651
- actions.appendChild(confirmBtn);
2652
-
2653
- box.appendChild(actions);
2654
- dialog.appendChild(box);
2655
-
2656
- // Dismiss on backdrop click
2657
- dialog.addEventListener('click', function (e) {
2658
- if (e.target === dialog) dialog.remove();
2659
- });
2660
-
2661
- document.body.appendChild(dialog);
2662
- }
2663
-
2664
2170
  // ── Panel management ──
2665
2171
  function soloThread(name) {
2666
- delete unreadThreads[name];
2667
2172
  visibleThreads = [name];
2668
2173
  renderSidebar();
2669
2174
  renderContentArea();
@@ -2672,7 +2177,6 @@
2672
2177
  }
2673
2178
 
2674
2179
  function togglePanelThread(name) {
2675
- delete unreadThreads[name];
2676
2180
  // On mobile, always solo (no side-by-side)
2677
2181
  if (isMobile()) {
2678
2182
  soloThread(name);
@@ -2803,16 +2307,6 @@
2803
2307
  });
2804
2308
  actions.appendChild(revertBtn);
2805
2309
 
2806
- var selectBtn = document.createElement('button');
2807
- selectBtn.className = 'cumulus-panel-action-btn';
2808
- selectBtn.setAttribute('data-testid', 'webchat-select-btn');
2809
- selectBtn.setAttribute('title', 'Select messages to delete');
2810
- selectBtn.textContent = 'Select';
2811
- selectBtn.addEventListener('click', function () {
2812
- toggleSelectionMode();
2813
- });
2814
- actions.appendChild(selectBtn);
2815
-
2816
2310
  panelHeader.appendChild(actions);
2817
2311
 
2818
2312
  var closeBtn = document.createElement('button');
@@ -2881,11 +2375,7 @@
2881
2375
 
2882
2376
  // ── Panel-local render functions ──
2883
2377
  function scrollToBottom() {
2884
- requestAnimationFrame(function () {
2885
- requestAnimationFrame(function () {
2886
- messagesEl.scrollTop = messagesEl.scrollHeight;
2887
- });
2888
- });
2378
+ messagesEl.scrollTop = messagesEl.scrollHeight;
2889
2379
  }
2890
2380
 
2891
2381
  function isWideUserMessage(content) {
@@ -2954,51 +2444,20 @@
2954
2444
 
2955
2445
  function renderPanelMessages() {
2956
2446
  messagesEl.innerHTML = '';
2957
- // Apply selection mode class to messages container
2958
- if (state.selectionMode) {
2959
- messagesEl.classList.add('cumulus-selection-mode');
2960
- } else {
2961
- messagesEl.classList.remove('cumulus-selection-mode');
2962
- }
2963
2447
  if (state.messages.length === 0 && !state.streaming) {
2964
2448
  messagesEl.innerHTML =
2965
2449
  '<div class="cumulus-empty">Send a message to start chatting</div>';
2966
- updateSelectionBar();
2967
2450
  return;
2968
2451
  }
2969
2452
  for (var i = 0; i < state.messages.length; i++) {
2970
2453
  var msg = state.messages[i];
2971
2454
  var row = document.createElement('div');
2972
2455
  row.className = 'cumulus-msg-row';
2973
- // Add checkbox if in selection mode and message has an id
2974
- if (state.selectionMode && msg.id) {
2975
- var cb = document.createElement('input');
2976
- cb.type = 'checkbox';
2977
- cb.className = 'cumulus-msg-checkbox';
2978
- cb.checked = !!state.selectedIds[msg.id];
2979
- (function (msgId) {
2980
- cb.addEventListener('change', function () {
2981
- if (cb.checked) {
2982
- state.selectedIds[msgId] = true;
2983
- } else {
2984
- delete state.selectedIds[msgId];
2985
- }
2986
- updateSelectionBar();
2987
- });
2988
- })(msg.id);
2989
- row.appendChild(cb);
2990
- }
2991
2456
  if (msg.role === 'user') {
2992
- var agent = parseAgentMessage(msg.content);
2993
- if (agent) {
2994
- row.appendChild(buildAgentMsgEl(agent));
2995
- } else {
2996
- row.appendChild(buildUserMsgEl(msg));
2997
- }
2457
+ row.appendChild(buildUserMsgEl(msg));
2998
2458
  } else {
2999
2459
  row.appendChild(buildAssistantMsgEl(msg.content, false));
3000
2460
  }
3001
- appendTimestamp(row, msg.timestamp);
3002
2461
  messagesEl.appendChild(row);
3003
2462
  }
3004
2463
  if (state.streaming) {
@@ -3008,128 +2467,6 @@
3008
2467
  messagesEl.appendChild(row);
3009
2468
  }
3010
2469
  scrollToBottom();
3011
- updateSelectionBar();
3012
- }
3013
-
3014
- function updateSelectionBar() {
3015
- // Remove existing bar
3016
- var existingBar = panel.querySelector('.cumulus-selection-bar');
3017
- if (existingBar) existingBar.remove();
3018
- if (!state.selectionMode) return;
3019
-
3020
- var selectedCount = Object.keys(state.selectedIds).length;
3021
- var selectableCount = state.messages.filter(function (m) { return !!m.id; }).length;
3022
-
3023
- var bar = document.createElement('div');
3024
- bar.className = 'cumulus-selection-bar';
3025
-
3026
- var countEl = document.createElement('span');
3027
- countEl.className = 'cumulus-selection-count';
3028
- countEl.textContent = selectedCount + ' selected';
3029
- bar.appendChild(countEl);
3030
-
3031
- var selectAllBtn = document.createElement('button');
3032
- selectAllBtn.className = 'cumulus-selection-select-all-btn';
3033
- selectAllBtn.textContent = selectedCount === selectableCount && selectableCount > 0 ? 'Deselect All' : 'Select All';
3034
- selectAllBtn.addEventListener('click', function () {
3035
- if (selectedCount === selectableCount && selectableCount > 0) {
3036
- // Deselect all
3037
- state.selectedIds = {};
3038
- } else {
3039
- // Select all with IDs
3040
- state.messages.forEach(function (m) {
3041
- if (m.id) state.selectedIds[m.id] = true;
3042
- });
3043
- }
3044
- renderPanelMessages();
3045
- });
3046
- bar.appendChild(selectAllBtn);
3047
-
3048
- var deleteBtn = document.createElement('button');
3049
- deleteBtn.className = 'cumulus-selection-delete-btn';
3050
- deleteBtn.textContent = 'Delete';
3051
- deleteBtn.disabled = selectedCount === 0;
3052
- deleteBtn.addEventListener('click', function () {
3053
- if (selectedCount === 0) return;
3054
- showDeleteMessagesConfirm(threadName, panel, Object.keys(state.selectedIds));
3055
- });
3056
- bar.appendChild(deleteBtn);
3057
-
3058
- // Insert before input area
3059
- var inputAreaEl = panel.querySelector('.cumulus-input-area');
3060
- if (inputAreaEl) {
3061
- panel.insertBefore(bar, inputAreaEl);
3062
- } else {
3063
- panel.appendChild(bar);
3064
- }
3065
- }
3066
-
3067
- function toggleSelectionMode() {
3068
- state.selectionMode = !state.selectionMode;
3069
- state.selectedIds = {};
3070
- selectBtn.textContent = state.selectionMode ? 'Done' : 'Select';
3071
- if (state.selectionMode) {
3072
- selectBtn.classList.add('active');
3073
- } else {
3074
- selectBtn.classList.remove('active');
3075
- }
3076
- renderPanelMessages();
3077
- }
3078
-
3079
- function showDeleteMessagesConfirm(tName, panelEl, ids) {
3080
- // Remove any existing confirm dialog
3081
- var existing = panelEl.querySelector('.cumulus-confirm-dialog');
3082
- if (existing) existing.remove();
3083
-
3084
- var dialog = document.createElement('div');
3085
- dialog.className = 'cumulus-confirm-dialog';
3086
-
3087
- var box = document.createElement('div');
3088
- box.className = 'cumulus-confirm-dialog-box';
3089
-
3090
- var title = document.createElement('div');
3091
- title.className = 'cumulus-confirm-dialog-title';
3092
- title.textContent = 'Delete Messages';
3093
- box.appendChild(title);
3094
-
3095
- var text = document.createElement('div');
3096
- text.className = 'cumulus-confirm-dialog-text';
3097
- text.textContent = ids.length + ' message' + (ids.length === 1 ? '' : 's') + ' will be permanently deleted.';
3098
- box.appendChild(text);
3099
-
3100
- var warn = document.createElement('div');
3101
- warn.className = 'cumulus-confirm-dialog-warn';
3102
- warn.textContent = 'This cannot be undone.';
3103
- box.appendChild(warn);
3104
-
3105
- var actions = document.createElement('div');
3106
- actions.className = 'cumulus-confirm-dialog-actions';
3107
-
3108
- var cancelBtn = document.createElement('button');
3109
- cancelBtn.className = 'cumulus-confirm-dialog-cancel';
3110
- cancelBtn.textContent = 'Cancel';
3111
- cancelBtn.addEventListener('click', function () {
3112
- dialog.remove();
3113
- });
3114
- actions.appendChild(cancelBtn);
3115
-
3116
- var confirmBtn = document.createElement('button');
3117
- confirmBtn.className = 'cumulus-confirm-dialog-confirm';
3118
- confirmBtn.setAttribute('data-testid', 'webchat-delete-messages-confirm');
3119
- confirmBtn.textContent = 'Delete';
3120
- confirmBtn.addEventListener('click', function () {
3121
- confirmBtn.disabled = true;
3122
- confirmBtn.textContent = 'Deleting\u2026';
3123
- if (connection) {
3124
- connection.send({ type: 'delete_messages', threadName: tName, messageIds: ids });
3125
- }
3126
- dialog.remove();
3127
- });
3128
- actions.appendChild(confirmBtn);
3129
-
3130
- box.appendChild(actions);
3131
- dialog.appendChild(box);
3132
- panelEl.appendChild(dialog);
3133
2470
  }
3134
2471
 
3135
2472
  function updatePanelSendBtn() {
@@ -3220,9 +2557,6 @@
3220
2557
  state._renderMessages = renderPanelMessages;
3221
2558
  state._updateSendBtn = updatePanelSendBtn;
3222
2559
 
3223
- // Initial render (messages may already be loaded from a previous view)
3224
- renderPanelMessages();
3225
-
3226
2560
  // ── Send message for this panel ──
3227
2561
  function sendPanelMessage() {
3228
2562
  var text = inputEl.value.trim();
@@ -3415,12 +2749,14 @@
3415
2749
  var filePath = addInput.value.trim();
3416
2750
  if (!filePath) return;
3417
2751
  connection &&
3418
- connection.send({
3419
- type: 'include_add',
3420
- threadName: threadName,
3421
- filePath: filePath,
3422
- scope: currentScope,
3423
- });
2752
+ connection.send(
2753
+ JSON.stringify({
2754
+ type: 'include_add',
2755
+ threadName: threadName,
2756
+ filePath: filePath,
2757
+ scope: currentScope,
2758
+ })
2759
+ );
3424
2760
  addInput.value = '';
3425
2761
  });
3426
2762
 
@@ -3444,7 +2780,7 @@
3444
2780
 
3445
2781
  // Request the list
3446
2782
  connection &&
3447
- connection.send({ type: 'include_list', threadName: threadName });
2783
+ connection.send(JSON.stringify({ type: 'include_list', threadName: threadName }));
3448
2784
  }
3449
2785
 
3450
2786
  function renderIncludeList(panel, threadName, files) {
@@ -3481,11 +2817,13 @@
3481
2817
  removeBtn.setAttribute('title', 'Remove');
3482
2818
  removeBtn.addEventListener('click', function () {
3483
2819
  connection &&
3484
- connection.send({
3485
- type: 'include_remove',
3486
- threadName: threadName,
3487
- filePath: f.path,
3488
- });
2820
+ connection.send(
2821
+ JSON.stringify({
2822
+ type: 'include_remove',
2823
+ threadName: threadName,
2824
+ filePath: f.path,
2825
+ })
2826
+ );
3489
2827
  });
3490
2828
  item.appendChild(removeBtn);
3491
2829
 
@@ -3531,7 +2869,7 @@
3531
2869
 
3532
2870
  // Request the turn list
3533
2871
  connection &&
3534
- connection.send({ type: 'revert_list', threadName: threadName });
2872
+ connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
3535
2873
  }
3536
2874
 
3537
2875
  function renderRevertList(panel, threadName, turns) {
@@ -3611,7 +2949,7 @@
3611
2949
  cancelBtn.addEventListener('click', function () {
3612
2950
  // Re-request list
3613
2951
  connection &&
3614
- connection.send({ type: 'revert_list', threadName: threadName });
2952
+ connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
3615
2953
  });
3616
2954
  actions.appendChild(cancelBtn);
3617
2955
 
@@ -3623,11 +2961,13 @@
3623
2961
  revertBtn.disabled = true;
3624
2962
  revertBtn.textContent = 'Reverting\u2026';
3625
2963
  connection &&
3626
- connection.send({
3627
- type: 'revert_execute',
3628
- threadName: threadName,
3629
- targetMessageId: turn.userMessageId,
3630
- });
2964
+ connection.send(
2965
+ JSON.stringify({
2966
+ type: 'revert_execute',
2967
+ threadName: threadName,
2968
+ targetMessageId: turn.userMessageId,
2969
+ })
2970
+ );
3631
2971
  });
3632
2972
  actions.appendChild(revertBtn);
3633
2973
 
@@ -3644,14 +2984,98 @@
3644
2984
  return h + ':' + (m < 10 ? '0' : '') + m + ' ' + ampm;
3645
2985
  }
3646
2986
 
2987
+ // ── Push notification subscription ──
2988
+ function registerPushNotifications() {
2989
+ if (!('serviceWorker' in navigator) || !('PushManager' in window)) {
2990
+ console.log('[Cumulus] Push notifications not supported');
2991
+ return;
2992
+ }
2993
+
2994
+ navigator.serviceWorker
2995
+ .register('/sw.js')
2996
+ .then(function (registration) {
2997
+ console.log('[Cumulus] Service worker registered');
2998
+
2999
+ // Check existing subscription
3000
+ return registration.pushManager.getSubscription().then(function (existing) {
3001
+ if (existing) {
3002
+ // Already subscribed — send to server in case it's new/different
3003
+ sendPushSubscription(existing);
3004
+ return;
3005
+ }
3006
+
3007
+ // Get VAPID key from server
3008
+ var loc = window.location;
3009
+ var apiUrl = loc.protocol + '//' + loc.host;
3010
+ fetch(apiUrl + '/api/push/vapid-key')
3011
+ .then(function (r) {
3012
+ return r.json();
3013
+ })
3014
+ .then(function (data) {
3015
+ if (!data.publicKey) {
3016
+ console.log('[Cumulus] Push not configured on server');
3017
+ return;
3018
+ }
3019
+
3020
+ // Request permission and subscribe
3021
+ return registration.pushManager
3022
+ .subscribe({
3023
+ userVisibleOnly: true,
3024
+ applicationServerKey: urlBase64ToUint8Array(data.publicKey),
3025
+ })
3026
+ .then(function (subscription) {
3027
+ console.log('[Cumulus] Push subscription created');
3028
+ sendPushSubscription(subscription);
3029
+ });
3030
+ })
3031
+ .catch(function (err) {
3032
+ console.warn('[Cumulus] Push subscription failed:', err);
3033
+ });
3034
+ });
3035
+ })
3036
+ .catch(function (err) {
3037
+ console.warn('[Cumulus] Service worker registration failed:', err);
3038
+ });
3039
+ }
3040
+
3041
+ function sendPushSubscription(subscription) {
3042
+ var loc = window.location;
3043
+ var apiUrl = loc.protocol + '//' + loc.host;
3044
+ var raw = subscription.toJSON();
3045
+
3046
+ fetch(apiUrl + '/api/push/subscribe', {
3047
+ method: 'POST',
3048
+ headers: {
3049
+ 'Content-Type': 'application/json',
3050
+ 'X-API-Key': activeApiKey,
3051
+ },
3052
+ body: JSON.stringify({
3053
+ endpoint: raw.endpoint,
3054
+ keys: raw.keys,
3055
+ label: navigator.userAgent.indexOf('iPhone') !== -1 ? 'iPhone' : 'Browser',
3056
+ }),
3057
+ }).catch(function (err) {
3058
+ console.warn('[Cumulus] Failed to send push subscription:', err);
3059
+ });
3060
+ }
3061
+
3062
+ function urlBase64ToUint8Array(base64String) {
3063
+ var padding = '='.repeat((4 - (base64String.length % 4)) % 4);
3064
+ var base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
3065
+ var rawData = atob(base64);
3066
+ var outputArray = new Uint8Array(rawData.length);
3067
+ for (var i = 0; i < rawData.length; ++i) {
3068
+ outputArray[i] = rawData.charCodeAt(i);
3069
+ }
3070
+ return outputArray;
3071
+ }
3072
+
3647
3073
  // ── Message router ──
3648
3074
  function handleServerMessage(data) {
3649
- if (data.type !== 'token') {
3650
- console.log('[CumulusChat] WS recv:', data.type, data.threadName || '', data);
3651
- }
3652
3075
  switch (data.type) {
3653
3076
  case 'auth_ok':
3654
3077
  showAppView();
3078
+ registerPushNotifications();
3655
3079
  break;
3656
3080
 
3657
3081
  case 'auth_error':
@@ -3693,50 +3117,12 @@
3693
3117
  }
3694
3118
  break;
3695
3119
 
3696
- case 'thread_deleted':
3697
- // Server confirms thread deleted; remove from sidebar and panels
3698
- if (data.threadName) {
3699
- allThreads = allThreads.filter(function (t) {
3700
- return t.name !== data.threadName;
3701
- });
3702
- delete threadStates[data.threadName];
3703
- removeThreadFromView(data.threadName);
3704
- renderSidebar();
3705
- }
3706
- break;
3707
-
3708
- case 'messages_deleted':
3709
- // Server confirms messages deleted; reload history for that thread
3710
- if (data.threadName) {
3711
- var dState = getThreadState(data.threadName);
3712
- dState.messages = [];
3713
- dState.historyLoaded = false;
3714
- dState.selectionMode = false;
3715
- dState.selectedIds = {};
3716
- // Exit selection mode on visible panel
3717
- var dPanel = document.querySelector('[data-thread-panel="' + data.threadName + '"]');
3718
- if (dPanel) {
3719
- var dMsgs = dPanel.querySelector('.cumulus-messages');
3720
- if (dMsgs) dMsgs.classList.remove('cumulus-selection-mode');
3721
- var dBar = dPanel.querySelector('.cumulus-selection-bar');
3722
- if (dBar) dBar.remove();
3723
- var dSelectBtn = dPanel.querySelector('[data-testid="webchat-select-btn"]');
3724
- if (dSelectBtn) {
3725
- dSelectBtn.textContent = 'Select';
3726
- dSelectBtn.classList.remove('active');
3727
- }
3728
- }
3729
- connection &&
3730
- connection.send({ type: 'history', threadName: data.threadName, limit: 200 });
3731
- }
3732
- break;
3733
-
3734
3120
  case 'history':
3735
3121
  if (data.threadName) {
3736
3122
  var state = getThreadState(data.threadName);
3737
3123
  if (data.messages && data.messages.length > 0) {
3738
3124
  state.messages = data.messages.map(function (m) {
3739
- return { id: m.id, role: m.role, content: m.content, timestamp: m.timestamp };
3125
+ return { id: m.id, role: m.role, content: m.content };
3740
3126
  });
3741
3127
  }
3742
3128
  refreshThreadPanel(data.threadName);
@@ -3815,22 +3201,6 @@
3815
3201
  }
3816
3202
  break;
3817
3203
 
3818
- case 'new_messages':
3819
- // Agent inject completed — append new messages to the thread and re-render
3820
- if (data.threadName && data.messages && data.messages.length > 0) {
3821
- var state = getThreadState(data.threadName);
3822
- for (var nm = 0; nm < data.messages.length; nm++) {
3823
- state.messages.push({
3824
- id: data.messages[nm].id,
3825
- role: data.messages[nm].role,
3826
- content: data.messages[nm].content,
3827
- });
3828
- }
3829
- updateThreadActivity(data.threadName);
3830
- refreshThreadPanel(data.threadName);
3831
- }
3832
- break;
3833
-
3834
3204
  // ── Include/Revert responses ──
3835
3205
  case 'include_list':
3836
3206
  if (data.threadName && data.files) {
@@ -3863,11 +3233,13 @@
3863
3233
  tState.historyLoaded = false;
3864
3234
  // Re-request history
3865
3235
  connection &&
3866
- connection.send({
3867
- type: 'history',
3868
- threadName: data.threadName,
3869
- limit: 200,
3870
- });
3236
+ connection.send(
3237
+ JSON.stringify({
3238
+ type: 'history',
3239
+ threadName: data.threadName,
3240
+ limit: 200,
3241
+ })
3242
+ );
3871
3243
  if (overlay) overlay.remove();
3872
3244
  } else {
3873
3245
  // Show error in overlay
@@ -3891,10 +3263,6 @@
3891
3263
  break;
3892
3264
  }
3893
3265
  }
3894
- // Mark unread if thread is not currently visible
3895
- if (visibleThreads.indexOf(threadName) === -1) {
3896
- unreadThreads[threadName] = true;
3897
- }
3898
3266
  // Re-render sidebar to reflect new sort order / move thread up
3899
3267
  renderSidebar();
3900
3268
  }
@@ -4047,7 +3415,5 @@
4047
3415
  }
4048
3416
 
4049
3417
  window.CumulusChat = widget;
4050
- window.CumulusChat.version = '0.27.15';
4051
- console.log('[CumulusChat] Widget loaded, version ' + window.CumulusChat.version);
4052
3418
  }
4053
3419
  })();