@luckydraw/cumulus 0.18.2 → 0.20.0
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/dist/gateway/adapters/discord.d.ts.map +1 -1
- package/dist/gateway/adapters/discord.js +3 -3
- package/dist/gateway/adapters/discord.js.map +1 -1
- package/dist/gateway/adapters/slack.d.ts.map +1 -1
- package/dist/gateway/adapters/slack.js +2 -2
- package/dist/gateway/adapters/slack.js.map +1 -1
- package/dist/gateway/adapters/webchat.d.ts +3 -0
- package/dist/gateway/adapters/webchat.d.ts.map +1 -1
- package/dist/gateway/adapters/webchat.js +129 -10
- package/dist/gateway/adapters/webchat.js.map +1 -1
- package/dist/gateway/cli.js +1 -1
- package/dist/gateway/cli.js.map +1 -1
- package/dist/gateway/config.d.ts +15 -0
- package/dist/gateway/config.d.ts.map +1 -1
- package/dist/gateway/config.js +18 -1
- package/dist/gateway/config.js.map +1 -1
- package/dist/gateway/daemon.d.ts.map +1 -1
- package/dist/gateway/daemon.js +11 -11
- package/dist/gateway/daemon.js.map +1 -1
- package/dist/gateway/logger.js +1 -1
- package/dist/gateway/logger.js.map +1 -1
- package/dist/gateway/server.d.ts +3 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +220 -12
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/static/widget.js +520 -89
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/gateway.d.ts +1 -1
- package/dist/lib/gateway.d.ts.map +1 -1
- package/dist/lib/gateway.js +48 -30
- package/dist/lib/gateway.js.map +1 -1
- package/dist/lib/projects.d.ts +67 -0
- package/dist/lib/projects.d.ts.map +1 -0
- package/dist/lib/projects.js +229 -0
- package/dist/lib/projects.js.map +1 -0
- package/dist/lib/retriever.d.ts.map +1 -1
- package/dist/lib/retriever.js +1 -3
- package/dist/lib/retriever.js.map +1 -1
- package/dist/lib/segments.d.ts.map +1 -1
- package/dist/lib/segments.js.map +1 -1
- package/dist/lib/summaries.d.ts.map +1 -1
- package/dist/lib/summaries.js +7 -10
- package/dist/lib/summaries.js.map +1 -1
- package/dist/lib/templates.d.ts +25 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +102 -0
- package/dist/lib/templates.js.map +1 -0
- package/package.json +1 -1
|
@@ -502,6 +502,84 @@
|
|
|
502
502
|
' border-top: 1px solid #333;',
|
|
503
503
|
' flex-shrink: 0;',
|
|
504
504
|
'}',
|
|
505
|
+
|
|
506
|
+
/* ── Project items (in sidebar) ── */
|
|
507
|
+
'.cumulus-project-item {',
|
|
508
|
+
' display: flex; align-items: center; gap: 8px;',
|
|
509
|
+
' padding: 7px 12px;',
|
|
510
|
+
' cursor: pointer;',
|
|
511
|
+
' font-size: 13px; color: #ccc;',
|
|
512
|
+
' border-left: 2px solid transparent;',
|
|
513
|
+
' user-select: none;',
|
|
514
|
+
' transition: background 0.1s ease;',
|
|
515
|
+
'}',
|
|
516
|
+
'.cumulus-project-item:hover { background: #2a2a2a; }',
|
|
517
|
+
'.cumulus-project-item.selected {',
|
|
518
|
+
' border-left-color: #7c3aed;',
|
|
519
|
+
' background: #1e1a2e;',
|
|
520
|
+
' color: #e0e0e0;',
|
|
521
|
+
'}',
|
|
522
|
+
'.cumulus-project-icon {',
|
|
523
|
+
' width: 7px; height: 7px; border-radius: 2px;',
|
|
524
|
+
' background: #7c3aed; flex-shrink: 0;',
|
|
525
|
+
'}',
|
|
526
|
+
'.cumulus-project-name {',
|
|
527
|
+
' flex: 1; overflow: hidden;',
|
|
528
|
+
' text-overflow: ellipsis; white-space: nowrap;',
|
|
529
|
+
'}',
|
|
530
|
+
'.cumulus-project-badge {',
|
|
531
|
+
' font-size: 10px; color: #888;',
|
|
532
|
+
' background: #2a2a2a; border: 1px solid #3a3a3a;',
|
|
533
|
+
' border-radius: 8px; padding: 0 5px;',
|
|
534
|
+
' flex-shrink: 0; min-width: 1.6em; text-align: center;',
|
|
535
|
+
'}',
|
|
536
|
+
'.cumulus-new-project-btn {',
|
|
537
|
+
' width: 100%;',
|
|
538
|
+
' background: none; border: none;',
|
|
539
|
+
' color: #666; padding: 5px 12px;',
|
|
540
|
+
' font-size: 12px; cursor: pointer;',
|
|
541
|
+
' font-family: inherit; text-align: left;',
|
|
542
|
+
' border-top: 1px solid #2a2a2a;',
|
|
543
|
+
'}',
|
|
544
|
+
'.cumulus-new-project-btn:hover { color: #a78bfa; }',
|
|
545
|
+
'.cumulus-new-project-form {',
|
|
546
|
+
' padding: 6px 10px;',
|
|
547
|
+
' display: flex; flex-direction: column; gap: 5px;',
|
|
548
|
+
' border-top: 1px solid #2a2a2a;',
|
|
549
|
+
'}',
|
|
550
|
+
'.cumulus-new-project-input {',
|
|
551
|
+
' width: 100%;',
|
|
552
|
+
' background: #3d3d3d; border: 1px solid #7c3aed;',
|
|
553
|
+
' border-radius: 0.45em; color: #e0e0e0;',
|
|
554
|
+
' padding: 6px 8px; font-size: 12px;',
|
|
555
|
+
' font-family: inherit; outline: none;',
|
|
556
|
+
'}',
|
|
557
|
+
'.cumulus-new-project-select {',
|
|
558
|
+
' width: 100%;',
|
|
559
|
+
' background: #3d3d3d; border: 1px solid #4a4a4a;',
|
|
560
|
+
' border-radius: 0.45em; color: #ccc;',
|
|
561
|
+
' padding: 5px 7px; font-size: 12px;',
|
|
562
|
+
' font-family: inherit; outline: none; cursor: pointer;',
|
|
563
|
+
'}',
|
|
564
|
+
'.cumulus-new-project-select:focus { border-color: #7c3aed; }',
|
|
565
|
+
'.cumulus-new-project-actions {',
|
|
566
|
+
' display: flex; gap: 5px;',
|
|
567
|
+
'}',
|
|
568
|
+
'.cumulus-new-project-create-btn {',
|
|
569
|
+
' flex: 1; background: #7c3aed; border: none;',
|
|
570
|
+
' border-radius: 0.45em; color: white;',
|
|
571
|
+
' padding: 5px 8px; font-size: 12px;',
|
|
572
|
+
' cursor: pointer; font-family: inherit; font-weight: 600;',
|
|
573
|
+
'}',
|
|
574
|
+
'.cumulus-new-project-create-btn:hover { background: #6d28d9; }',
|
|
575
|
+
'.cumulus-new-project-cancel-btn {',
|
|
576
|
+
' background: #2a2a2a; border: 1px solid #3a3a3a;',
|
|
577
|
+
' border-radius: 0.45em; color: #888;',
|
|
578
|
+
' padding: 5px 8px; font-size: 12px;',
|
|
579
|
+
' cursor: pointer; font-family: inherit;',
|
|
580
|
+
'}',
|
|
581
|
+
'.cumulus-new-project-cancel-btn:hover { background: #333; color: #ccc; }',
|
|
582
|
+
|
|
505
583
|
'.cumulus-new-thread-btn {',
|
|
506
584
|
' width: 100%;',
|
|
507
585
|
' background: #2a2a2a; border: 1px solid #3a3a3a;',
|
|
@@ -804,7 +882,6 @@
|
|
|
804
882
|
' .cumulus-standalone-empty-hint { display: none; }',
|
|
805
883
|
|
|
806
884
|
'}',
|
|
807
|
-
|
|
808
885
|
].join('\n');
|
|
809
886
|
|
|
810
887
|
// ─── HTML Escaping ───────────────────────────────────────────────────────────
|
|
@@ -898,11 +975,19 @@
|
|
|
898
975
|
function buildCodeBlock(lang, escapedCode, tokenId) {
|
|
899
976
|
return (
|
|
900
977
|
'<div class="code-block-wrapper">' +
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
978
|
+
'<div class="code-block-header">' +
|
|
979
|
+
'<span class="code-block-language">' +
|
|
980
|
+
escapeHtml(lang) +
|
|
981
|
+
'</span>' +
|
|
982
|
+
'<button class="code-block-copy-btn" data-copy-target="' +
|
|
983
|
+
tokenId +
|
|
984
|
+
'" data-testid="webchat-copy-code">Copy</button>' +
|
|
985
|
+
'</div>' +
|
|
986
|
+
'<pre><code data-code-id="' +
|
|
987
|
+
tokenId +
|
|
988
|
+
'">' +
|
|
989
|
+
escapedCode +
|
|
990
|
+
'</code></pre>' +
|
|
906
991
|
'</div>'
|
|
907
992
|
);
|
|
908
993
|
}
|
|
@@ -942,16 +1027,32 @@
|
|
|
942
1027
|
var headerCells = parseTableRow(lines[0]);
|
|
943
1028
|
var dataRows = lines.slice(2);
|
|
944
1029
|
|
|
945
|
-
var thead =
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1030
|
+
var thead =
|
|
1031
|
+
'<thead><tr>' +
|
|
1032
|
+
headerCells
|
|
1033
|
+
.map(function (c) {
|
|
1034
|
+
return '<th>' + c + '</th>';
|
|
1035
|
+
})
|
|
1036
|
+
.join('') +
|
|
1037
|
+
'</tr></thead>';
|
|
1038
|
+
|
|
1039
|
+
var tbody =
|
|
1040
|
+
'<tbody>' +
|
|
1041
|
+
dataRows
|
|
1042
|
+
.map(function (row) {
|
|
1043
|
+
var cells = parseTableRow(row);
|
|
1044
|
+
return (
|
|
1045
|
+
'<tr>' +
|
|
1046
|
+
cells
|
|
1047
|
+
.map(function (c) {
|
|
1048
|
+
return '<td>' + c + '</td>';
|
|
1049
|
+
})
|
|
1050
|
+
.join('') +
|
|
1051
|
+
'</tr>'
|
|
1052
|
+
);
|
|
1053
|
+
})
|
|
1054
|
+
.join('') +
|
|
1055
|
+
'</tbody>';
|
|
955
1056
|
|
|
956
1057
|
return '<table>' + thead + tbody + '</table>';
|
|
957
1058
|
}
|
|
@@ -962,7 +1063,9 @@
|
|
|
962
1063
|
// Remove first and last if empty (from leading/trailing |)
|
|
963
1064
|
if (parts.length > 0 && parts[0].trim() === '') parts = parts.slice(1);
|
|
964
1065
|
if (parts.length > 0 && parts[parts.length - 1].trim() === '') parts = parts.slice(0, -1);
|
|
965
|
-
return parts.map(function (c) {
|
|
1066
|
+
return parts.map(function (c) {
|
|
1067
|
+
return c.trim();
|
|
1068
|
+
});
|
|
966
1069
|
}
|
|
967
1070
|
|
|
968
1071
|
function renderBlockquotes(html) {
|
|
@@ -1001,7 +1104,15 @@
|
|
|
1001
1104
|
listLines.push(lines[i].replace(/^[ \t]*[-*+][ \t]+/, ''));
|
|
1002
1105
|
i++;
|
|
1003
1106
|
}
|
|
1004
|
-
result.push(
|
|
1107
|
+
result.push(
|
|
1108
|
+
'<ul>' +
|
|
1109
|
+
listLines
|
|
1110
|
+
.map(function (l) {
|
|
1111
|
+
return '<li>' + l + '</li>';
|
|
1112
|
+
})
|
|
1113
|
+
.join('') +
|
|
1114
|
+
'</ul>'
|
|
1115
|
+
);
|
|
1005
1116
|
}
|
|
1006
1117
|
// Ordered list item: "1. " "2. " etc.
|
|
1007
1118
|
else if (/^[ \t]*\d+\.[ \t]+/.test(lines[i])) {
|
|
@@ -1010,9 +1121,16 @@
|
|
|
1010
1121
|
olLines.push(lines[i].replace(/^[ \t]*\d+\.[ \t]+/, ''));
|
|
1011
1122
|
i++;
|
|
1012
1123
|
}
|
|
1013
|
-
result.push(
|
|
1014
|
-
|
|
1015
|
-
|
|
1124
|
+
result.push(
|
|
1125
|
+
'<ol>' +
|
|
1126
|
+
olLines
|
|
1127
|
+
.map(function (l) {
|
|
1128
|
+
return '<li>' + l + '</li>';
|
|
1129
|
+
})
|
|
1130
|
+
.join('') +
|
|
1131
|
+
'</ol>'
|
|
1132
|
+
);
|
|
1133
|
+
} else {
|
|
1016
1134
|
result.push(lines[i]);
|
|
1017
1135
|
i++;
|
|
1018
1136
|
}
|
|
@@ -1038,7 +1156,11 @@
|
|
|
1038
1156
|
return '<p>' + inner + '</p>';
|
|
1039
1157
|
});
|
|
1040
1158
|
|
|
1041
|
-
return parts
|
|
1159
|
+
return parts
|
|
1160
|
+
.filter(function (p) {
|
|
1161
|
+
return p !== '';
|
|
1162
|
+
})
|
|
1163
|
+
.join('\n');
|
|
1042
1164
|
}
|
|
1043
1165
|
|
|
1044
1166
|
// ─── Attachment helpers ──────────────────────────────────────────────────────
|
|
@@ -1054,7 +1176,11 @@
|
|
|
1054
1176
|
// result is "data:mime/type;base64,XXXX" — strip the prefix
|
|
1055
1177
|
var result = e.target.result;
|
|
1056
1178
|
var comma = result.indexOf(',');
|
|
1057
|
-
resolve({
|
|
1179
|
+
resolve({
|
|
1180
|
+
base64: result.slice(comma + 1),
|
|
1181
|
+
mimeType: file.type || 'application/octet-stream',
|
|
1182
|
+
name: file.name,
|
|
1183
|
+
});
|
|
1058
1184
|
};
|
|
1059
1185
|
reader.onerror = reject;
|
|
1060
1186
|
reader.readAsDataURL(file);
|
|
@@ -1064,7 +1190,9 @@
|
|
|
1064
1190
|
function readFileAsDataUrl(file) {
|
|
1065
1191
|
return new Promise(function (resolve, reject) {
|
|
1066
1192
|
var reader = new FileReader();
|
|
1067
|
-
reader.onload = function (e) {
|
|
1193
|
+
reader.onload = function (e) {
|
|
1194
|
+
resolve(e.target.result);
|
|
1195
|
+
};
|
|
1068
1196
|
reader.onerror = reject;
|
|
1069
1197
|
reader.readAsDataURL(file);
|
|
1070
1198
|
});
|
|
@@ -1090,7 +1218,10 @@
|
|
|
1090
1218
|
ws = new WebSocket(wsUrl);
|
|
1091
1219
|
|
|
1092
1220
|
ws.onopen = function () {
|
|
1093
|
-
if (destroyed) {
|
|
1221
|
+
if (destroyed) {
|
|
1222
|
+
ws.close();
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1094
1225
|
onStatus('connected');
|
|
1095
1226
|
reconnectDelay = 1000;
|
|
1096
1227
|
ws.send(JSON.stringify({ type: 'auth', apiKey: apiKey }));
|
|
@@ -1193,8 +1324,8 @@
|
|
|
1193
1324
|
header.innerHTML =
|
|
1194
1325
|
'<span class="cumulus-header-title">Cumulus</span>' +
|
|
1195
1326
|
'<span class="cumulus-header-status">' +
|
|
1196
|
-
|
|
1197
|
-
|
|
1327
|
+
'<span class="cumulus-status-dot" data-testid="webchat-status-dot"></span>' +
|
|
1328
|
+
'<span data-testid="webchat-status-text">Disconnected</span>' +
|
|
1198
1329
|
'</span>' +
|
|
1199
1330
|
'<button class="cumulus-close-btn" data-testid="webchat-close">×</button>';
|
|
1200
1331
|
panel.appendChild(header);
|
|
@@ -1347,7 +1478,9 @@
|
|
|
1347
1478
|
if (codeEl && navigator.clipboard) {
|
|
1348
1479
|
navigator.clipboard.writeText(codeEl.textContent || '').then(function () {
|
|
1349
1480
|
btn.textContent = 'Copied!';
|
|
1350
|
-
setTimeout(function () {
|
|
1481
|
+
setTimeout(function () {
|
|
1482
|
+
btn.textContent = 'Copy';
|
|
1483
|
+
}, 2000);
|
|
1351
1484
|
});
|
|
1352
1485
|
}
|
|
1353
1486
|
});
|
|
@@ -1363,7 +1496,8 @@
|
|
|
1363
1496
|
function renderMessages() {
|
|
1364
1497
|
messagesEl.innerHTML = '';
|
|
1365
1498
|
if (messages.length === 0 && !streaming) {
|
|
1366
|
-
messagesEl.innerHTML =
|
|
1499
|
+
messagesEl.innerHTML =
|
|
1500
|
+
'<div class="cumulus-empty">Send a message to start chatting</div>';
|
|
1367
1501
|
return;
|
|
1368
1502
|
}
|
|
1369
1503
|
for (var i = 0; i < messages.length; i++) {
|
|
@@ -1519,9 +1653,15 @@
|
|
|
1519
1653
|
stopRequested = false;
|
|
1520
1654
|
streaming = false;
|
|
1521
1655
|
if (streamBuffer) {
|
|
1522
|
-
messages.push({
|
|
1656
|
+
messages.push({
|
|
1657
|
+
role: 'assistant',
|
|
1658
|
+
content: streamBuffer + '\n\n[Error: ' + (data.error || 'Unknown error') + ']',
|
|
1659
|
+
});
|
|
1523
1660
|
} else {
|
|
1524
|
-
messages.push({
|
|
1661
|
+
messages.push({
|
|
1662
|
+
role: 'assistant',
|
|
1663
|
+
content: '[Error: ' + (data.error || 'Unknown error') + ']',
|
|
1664
|
+
});
|
|
1525
1665
|
}
|
|
1526
1666
|
streamBuffer = '';
|
|
1527
1667
|
updateSendBtn();
|
|
@@ -1558,8 +1698,12 @@
|
|
|
1558
1698
|
updateSendBtn();
|
|
1559
1699
|
renderMessages();
|
|
1560
1700
|
var imagePayload = attachSnapshot
|
|
1561
|
-
.filter(function (a) {
|
|
1562
|
-
|
|
1701
|
+
.filter(function (a) {
|
|
1702
|
+
return a.isImage;
|
|
1703
|
+
})
|
|
1704
|
+
.map(function (a) {
|
|
1705
|
+
return { mimeType: a.mimeType, base64: a.base64 };
|
|
1706
|
+
});
|
|
1563
1707
|
var payload = {
|
|
1564
1708
|
type: 'message',
|
|
1565
1709
|
threadName: sessionId,
|
|
@@ -1628,7 +1772,10 @@
|
|
|
1628
1772
|
|
|
1629
1773
|
// Embedded mode: no auth panel handling needed (API key from attribute)
|
|
1630
1774
|
function startConnection() {
|
|
1631
|
-
if (connection) {
|
|
1775
|
+
if (connection) {
|
|
1776
|
+
connection.close();
|
|
1777
|
+
connection = null;
|
|
1778
|
+
}
|
|
1632
1779
|
connection = createConnection({
|
|
1633
1780
|
wsUrl: wsUrl,
|
|
1634
1781
|
apiKey: activeApiKey,
|
|
@@ -1666,6 +1813,9 @@
|
|
|
1666
1813
|
// Thread list from server: [{ name, messageCount, lastActivity }]
|
|
1667
1814
|
var allThreads = [];
|
|
1668
1815
|
|
|
1816
|
+
// Project list from server: [{ name, path, template, threadCount, primaryThread, lastActivity, createdAt }]
|
|
1817
|
+
var allProjects = [];
|
|
1818
|
+
|
|
1669
1819
|
// Currently visible panel names (ordered, max 3)
|
|
1670
1820
|
var visibleThreads = [];
|
|
1671
1821
|
|
|
@@ -1704,8 +1854,8 @@
|
|
|
1704
1854
|
authHeader.innerHTML =
|
|
1705
1855
|
'<span class="cumulus-header-title">Cumulus</span>' +
|
|
1706
1856
|
'<span class="cumulus-header-status">' +
|
|
1707
|
-
|
|
1708
|
-
|
|
1857
|
+
'<span class="cumulus-status-dot" data-testid="webchat-status-dot"></span>' +
|
|
1858
|
+
'<span data-testid="webchat-status-text">Disconnected</span>' +
|
|
1709
1859
|
'</span>';
|
|
1710
1860
|
authWrapper.appendChild(authHeader);
|
|
1711
1861
|
|
|
@@ -1722,7 +1872,8 @@
|
|
|
1722
1872
|
|
|
1723
1873
|
// App layout — shown after auth
|
|
1724
1874
|
var appLayout = document.createElement('div');
|
|
1725
|
-
appLayout.style.cssText =
|
|
1875
|
+
appLayout.style.cssText =
|
|
1876
|
+
'display:none; flex-direction:column; width:100%; max-width:100vw; height:100vh; height:100dvh; overflow:hidden;';
|
|
1726
1877
|
|
|
1727
1878
|
// Top bar
|
|
1728
1879
|
var topbar = document.createElement('div');
|
|
@@ -1731,11 +1882,11 @@
|
|
|
1731
1882
|
'<button class="cumulus-sidebar-toggle" data-testid="webchat-sidebar-toggle" aria-label="Toggle sidebar">☰</button>' +
|
|
1732
1883
|
'<span class="cumulus-topbar-title">Cumulus</span>' +
|
|
1733
1884
|
'<div class="cumulus-topbar-right">' +
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1885
|
+
'<span class="cumulus-header-status">' +
|
|
1886
|
+
'<span class="cumulus-status-dot" data-testid="webchat-status-dot-app"></span>' +
|
|
1887
|
+
'<span data-testid="webchat-status-text-app">Disconnected</span>' +
|
|
1888
|
+
'</span>' +
|
|
1889
|
+
'<button class="cumulus-auth-logout" data-testid="webchat-logout">Logout</button>' +
|
|
1739
1890
|
'</div>';
|
|
1740
1891
|
appLayout.appendChild(topbar);
|
|
1741
1892
|
|
|
@@ -1778,6 +1929,52 @@
|
|
|
1778
1929
|
sidebarFooter.appendChild(newThreadInput);
|
|
1779
1930
|
sidebar.appendChild(sidebarFooter);
|
|
1780
1931
|
|
|
1932
|
+
// ── New project form nodes (persistent; re-appended into projects section on render) ──
|
|
1933
|
+
var newProjectBtn = document.createElement('button');
|
|
1934
|
+
newProjectBtn.className = 'cumulus-new-project-btn';
|
|
1935
|
+
newProjectBtn.setAttribute('data-testid', 'webchat-project-new');
|
|
1936
|
+
newProjectBtn.textContent = '+ New project';
|
|
1937
|
+
|
|
1938
|
+
var newProjectForm = document.createElement('div');
|
|
1939
|
+
newProjectForm.className = 'cumulus-new-project-form';
|
|
1940
|
+
newProjectForm.style.display = 'none';
|
|
1941
|
+
newProjectForm.setAttribute('data-testid', 'webchat-project-new-form');
|
|
1942
|
+
|
|
1943
|
+
var newProjectNameInput = document.createElement('input');
|
|
1944
|
+
newProjectNameInput.className = 'cumulus-new-project-input';
|
|
1945
|
+
newProjectNameInput.setAttribute('data-testid', 'webchat-project-new-name');
|
|
1946
|
+
newProjectNameInput.placeholder = 'Project name\u2026';
|
|
1947
|
+
newProjectNameInput.type = 'text';
|
|
1948
|
+
|
|
1949
|
+
var newProjectTemplateSelect = document.createElement('select');
|
|
1950
|
+
newProjectTemplateSelect.className = 'cumulus-new-project-select';
|
|
1951
|
+
newProjectTemplateSelect.setAttribute('data-testid', 'webchat-project-new-template');
|
|
1952
|
+
[['default', 'Default'], ['web-app', 'Web App']].forEach(function (opt) {
|
|
1953
|
+
var el = document.createElement('option');
|
|
1954
|
+
el.value = opt[0];
|
|
1955
|
+
el.textContent = opt[1];
|
|
1956
|
+
newProjectTemplateSelect.appendChild(el);
|
|
1957
|
+
});
|
|
1958
|
+
|
|
1959
|
+
var newProjectActions = document.createElement('div');
|
|
1960
|
+
newProjectActions.className = 'cumulus-new-project-actions';
|
|
1961
|
+
|
|
1962
|
+
var newProjectCreateBtn = document.createElement('button');
|
|
1963
|
+
newProjectCreateBtn.className = 'cumulus-new-project-create-btn';
|
|
1964
|
+
newProjectCreateBtn.setAttribute('data-testid', 'webchat-project-create');
|
|
1965
|
+
newProjectCreateBtn.textContent = 'Create';
|
|
1966
|
+
|
|
1967
|
+
var newProjectCancelBtn = document.createElement('button');
|
|
1968
|
+
newProjectCancelBtn.className = 'cumulus-new-project-cancel-btn';
|
|
1969
|
+
newProjectCancelBtn.setAttribute('data-testid', 'webchat-project-cancel');
|
|
1970
|
+
newProjectCancelBtn.textContent = 'Cancel';
|
|
1971
|
+
|
|
1972
|
+
newProjectActions.appendChild(newProjectCreateBtn);
|
|
1973
|
+
newProjectActions.appendChild(newProjectCancelBtn);
|
|
1974
|
+
newProjectForm.appendChild(newProjectNameInput);
|
|
1975
|
+
newProjectForm.appendChild(newProjectTemplateSelect);
|
|
1976
|
+
newProjectForm.appendChild(newProjectActions);
|
|
1977
|
+
|
|
1781
1978
|
// ── Content area ──
|
|
1782
1979
|
var contentArea = document.createElement('div');
|
|
1783
1980
|
contentArea.className = 'cumulus-content-area';
|
|
@@ -1793,7 +1990,9 @@
|
|
|
1793
1990
|
appLayout.appendChild(appRow);
|
|
1794
1991
|
|
|
1795
1992
|
// ── Mobile sidebar toggle ──
|
|
1796
|
-
var isMobile = function () {
|
|
1993
|
+
var isMobile = function () {
|
|
1994
|
+
return window.innerWidth <= 768;
|
|
1995
|
+
};
|
|
1797
1996
|
|
|
1798
1997
|
function openSidebar() {
|
|
1799
1998
|
sidebar.classList.add('open');
|
|
@@ -1841,7 +2040,11 @@
|
|
|
1841
2040
|
visibleThreads = [];
|
|
1842
2041
|
threadStates = {};
|
|
1843
2042
|
allThreads = [];
|
|
1844
|
-
|
|
2043
|
+
allProjects = [];
|
|
2044
|
+
if (refreshTimer) {
|
|
2045
|
+
clearInterval(refreshTimer);
|
|
2046
|
+
refreshTimer = null;
|
|
2047
|
+
}
|
|
1845
2048
|
var errEl = authPanel.querySelector('[data-testid="webchat-auth-error"]');
|
|
1846
2049
|
if (errEl) errEl.textContent = '';
|
|
1847
2050
|
}
|
|
@@ -1861,6 +2064,14 @@
|
|
|
1861
2064
|
function requestThreadList() {
|
|
1862
2065
|
if (connection) {
|
|
1863
2066
|
connection.send({ type: 'threads' });
|
|
2067
|
+
connection.send({ type: 'projects' });
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
// ── Project list ──
|
|
2072
|
+
function requestProjectList() {
|
|
2073
|
+
if (connection) {
|
|
2074
|
+
connection.send({ type: 'projects' });
|
|
1864
2075
|
}
|
|
1865
2076
|
}
|
|
1866
2077
|
|
|
@@ -1873,6 +2084,39 @@
|
|
|
1873
2084
|
visibleSet[visibleThreads[i]] = true;
|
|
1874
2085
|
}
|
|
1875
2086
|
|
|
2087
|
+
// Projects section: shown above active threads
|
|
2088
|
+
var projectsLabel = document.createElement('div');
|
|
2089
|
+
projectsLabel.className = 'cumulus-sidebar-section-label';
|
|
2090
|
+
projectsLabel.textContent = 'Projects';
|
|
2091
|
+
sidebarScroll.appendChild(projectsLabel);
|
|
2092
|
+
|
|
2093
|
+
var projectsSection = document.createElement('div');
|
|
2094
|
+
projectsSection.setAttribute('data-testid', 'webchat-sidebar-projects');
|
|
2095
|
+
|
|
2096
|
+
if (allProjects.length === 0) {
|
|
2097
|
+
var projectsEmptyNote = document.createElement('div');
|
|
2098
|
+
projectsEmptyNote.style.cssText =
|
|
2099
|
+
'padding: 4px 12px 2px; font-size: 12px; color: #555; font-style: italic;';
|
|
2100
|
+
projectsEmptyNote.textContent = 'No projects yet';
|
|
2101
|
+
projectsSection.appendChild(projectsEmptyNote);
|
|
2102
|
+
} else {
|
|
2103
|
+
allProjects.forEach(function (p) {
|
|
2104
|
+
projectsSection.appendChild(buildProjectItem(p, visibleSet));
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
// + New project button / form (persistent nodes re-appended each render)
|
|
2109
|
+
newProjectBtn.style.display = newProjectCreating ? 'none' : 'block';
|
|
2110
|
+
newProjectForm.style.display = newProjectCreating ? 'flex' : 'none';
|
|
2111
|
+
projectsSection.appendChild(newProjectBtn);
|
|
2112
|
+
projectsSection.appendChild(newProjectForm);
|
|
2113
|
+
|
|
2114
|
+
sidebarScroll.appendChild(projectsSection);
|
|
2115
|
+
|
|
2116
|
+
var projectsDivider = document.createElement('hr');
|
|
2117
|
+
projectsDivider.className = 'cumulus-sidebar-divider';
|
|
2118
|
+
sidebarScroll.appendChild(projectsDivider);
|
|
2119
|
+
|
|
1876
2120
|
// Active section: threads currently visible as panels
|
|
1877
2121
|
var activeThreads = visibleThreads.slice(); // preserve order
|
|
1878
2122
|
if (activeThreads.length > 0) {
|
|
@@ -1904,7 +2148,8 @@
|
|
|
1904
2148
|
|
|
1905
2149
|
if (allThreads.length === 0) {
|
|
1906
2150
|
var emptyNote = document.createElement('div');
|
|
1907
|
-
emptyNote.style.cssText =
|
|
2151
|
+
emptyNote.style.cssText =
|
|
2152
|
+
'padding: 8px 12px; font-size: 12px; color: #555; font-style: italic;';
|
|
1908
2153
|
emptyNote.textContent = 'No threads yet';
|
|
1909
2154
|
allSection.appendChild(emptyNote);
|
|
1910
2155
|
} else {
|
|
@@ -1913,7 +2158,9 @@
|
|
|
1913
2158
|
return (b.lastActivity || 0) - (a.lastActivity || 0);
|
|
1914
2159
|
});
|
|
1915
2160
|
sorted.forEach(function (t) {
|
|
1916
|
-
allSection.appendChild(
|
|
2161
|
+
allSection.appendChild(
|
|
2162
|
+
buildThreadItem(t.name, visibleSet[t.name] || false, false, t.messageCount)
|
|
2163
|
+
);
|
|
1917
2164
|
});
|
|
1918
2165
|
}
|
|
1919
2166
|
sidebarScroll.appendChild(allSection);
|
|
@@ -1954,6 +2201,41 @@
|
|
|
1954
2201
|
return item;
|
|
1955
2202
|
}
|
|
1956
2203
|
|
|
2204
|
+
function buildProjectItem(project, visibleSet) {
|
|
2205
|
+
var primaryThread = project.primaryThread;
|
|
2206
|
+
var isSelected = primaryThread ? (visibleSet[primaryThread] || false) : false;
|
|
2207
|
+
|
|
2208
|
+
var item = document.createElement('div');
|
|
2209
|
+
item.className = 'cumulus-project-item' + (isSelected ? ' selected' : '');
|
|
2210
|
+
item.setAttribute('data-testid', 'webchat-project-item');
|
|
2211
|
+
item.setAttribute('data-project-name', project.name);
|
|
2212
|
+
|
|
2213
|
+
var icon = document.createElement('span');
|
|
2214
|
+
icon.className = 'cumulus-project-icon';
|
|
2215
|
+
item.appendChild(icon);
|
|
2216
|
+
|
|
2217
|
+
var nameEl = document.createElement('span');
|
|
2218
|
+
nameEl.className = 'cumulus-project-name';
|
|
2219
|
+
nameEl.textContent = project.name;
|
|
2220
|
+
nameEl.setAttribute('title', project.name);
|
|
2221
|
+
item.appendChild(nameEl);
|
|
2222
|
+
|
|
2223
|
+
if (project.threadCount !== undefined && project.threadCount !== null) {
|
|
2224
|
+
var badge = document.createElement('span');
|
|
2225
|
+
badge.className = 'cumulus-project-badge';
|
|
2226
|
+
badge.textContent = project.threadCount;
|
|
2227
|
+
item.appendChild(badge);
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
item.addEventListener('click', function () {
|
|
2231
|
+
if (primaryThread) {
|
|
2232
|
+
soloThread(primaryThread);
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2235
|
+
|
|
2236
|
+
return item;
|
|
2237
|
+
}
|
|
2238
|
+
|
|
1957
2239
|
// ── Panel management ──
|
|
1958
2240
|
function soloThread(name) {
|
|
1959
2241
|
visibleThreads = [name];
|
|
@@ -1996,7 +2278,10 @@
|
|
|
1996
2278
|
visibleThreads.splice(idx, 1);
|
|
1997
2279
|
// Clear stale DOM render hooks so background messages don't update detached nodes
|
|
1998
2280
|
var state = threadStates[name];
|
|
1999
|
-
if (state) {
|
|
2281
|
+
if (state) {
|
|
2282
|
+
state._renderMessages = null;
|
|
2283
|
+
state._updateSendBtn = null;
|
|
2284
|
+
}
|
|
2000
2285
|
renderSidebar();
|
|
2001
2286
|
renderContentArea();
|
|
2002
2287
|
}
|
|
@@ -2015,7 +2300,10 @@
|
|
|
2015
2300
|
// Clear stale render hooks before wiping the DOM
|
|
2016
2301
|
for (var _ci = 0; _ci < visibleThreads.length; _ci++) {
|
|
2017
2302
|
var _cs = threadStates[visibleThreads[_ci]];
|
|
2018
|
-
if (_cs) {
|
|
2303
|
+
if (_cs) {
|
|
2304
|
+
_cs._renderMessages = null;
|
|
2305
|
+
_cs._updateSendBtn = null;
|
|
2306
|
+
}
|
|
2019
2307
|
}
|
|
2020
2308
|
contentArea.innerHTML = '';
|
|
2021
2309
|
|
|
@@ -2209,7 +2497,9 @@
|
|
|
2209
2497
|
if (codeEl && navigator.clipboard) {
|
|
2210
2498
|
navigator.clipboard.writeText(codeEl.textContent || '').then(function () {
|
|
2211
2499
|
btn.textContent = 'Copied!';
|
|
2212
|
-
setTimeout(function () {
|
|
2500
|
+
setTimeout(function () {
|
|
2501
|
+
btn.textContent = 'Copy';
|
|
2502
|
+
}, 2000);
|
|
2213
2503
|
});
|
|
2214
2504
|
}
|
|
2215
2505
|
});
|
|
@@ -2225,7 +2515,8 @@
|
|
|
2225
2515
|
function renderPanelMessages() {
|
|
2226
2516
|
messagesEl.innerHTML = '';
|
|
2227
2517
|
if (state.messages.length === 0 && !state.streaming) {
|
|
2228
|
-
messagesEl.innerHTML =
|
|
2518
|
+
messagesEl.innerHTML =
|
|
2519
|
+
'<div class="cumulus-empty">Send a message to start chatting</div>';
|
|
2229
2520
|
return;
|
|
2230
2521
|
}
|
|
2231
2522
|
for (var i = 0; i < state.messages.length; i++) {
|
|
@@ -2358,8 +2649,12 @@
|
|
|
2358
2649
|
renderPanelMessages();
|
|
2359
2650
|
|
|
2360
2651
|
var imagePayload = attachSnapshot
|
|
2361
|
-
.filter(function (a) {
|
|
2362
|
-
|
|
2652
|
+
.filter(function (a) {
|
|
2653
|
+
return a.isImage;
|
|
2654
|
+
})
|
|
2655
|
+
.map(function (a) {
|
|
2656
|
+
return { mimeType: a.mimeType, base64: a.base64 };
|
|
2657
|
+
});
|
|
2363
2658
|
|
|
2364
2659
|
var payload = {
|
|
2365
2660
|
type: 'message',
|
|
@@ -2439,7 +2734,10 @@
|
|
|
2439
2734
|
function openIncludeOverlay(panel, threadName) {
|
|
2440
2735
|
// Remove existing overlay if any
|
|
2441
2736
|
var existing = panel.querySelector('.cumulus-overlay');
|
|
2442
|
-
if (existing) {
|
|
2737
|
+
if (existing) {
|
|
2738
|
+
existing.remove();
|
|
2739
|
+
return;
|
|
2740
|
+
}
|
|
2443
2741
|
|
|
2444
2742
|
var overlay = document.createElement('div');
|
|
2445
2743
|
overlay.className = 'cumulus-overlay';
|
|
@@ -2453,7 +2751,9 @@
|
|
|
2453
2751
|
var closeBtn = document.createElement('button');
|
|
2454
2752
|
closeBtn.className = 'cumulus-overlay-close';
|
|
2455
2753
|
closeBtn.textContent = '\xD7';
|
|
2456
|
-
closeBtn.addEventListener('click', function () {
|
|
2754
|
+
closeBtn.addEventListener('click', function () {
|
|
2755
|
+
overlay.remove();
|
|
2756
|
+
});
|
|
2457
2757
|
header.appendChild(closeBtn);
|
|
2458
2758
|
overlay.appendChild(header);
|
|
2459
2759
|
|
|
@@ -2490,15 +2790,22 @@
|
|
|
2490
2790
|
addBtn.addEventListener('click', function () {
|
|
2491
2791
|
var filePath = addInput.value.trim();
|
|
2492
2792
|
if (!filePath) return;
|
|
2493
|
-
connection &&
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2793
|
+
connection &&
|
|
2794
|
+
connection.send(
|
|
2795
|
+
JSON.stringify({
|
|
2796
|
+
type: 'include_add',
|
|
2797
|
+
threadName: threadName,
|
|
2798
|
+
filePath: filePath,
|
|
2799
|
+
scope: currentScope,
|
|
2800
|
+
})
|
|
2801
|
+
);
|
|
2497
2802
|
addInput.value = '';
|
|
2498
2803
|
});
|
|
2499
2804
|
|
|
2500
2805
|
addInput.addEventListener('keydown', function (e) {
|
|
2501
|
-
if (e.key === 'Enter') {
|
|
2806
|
+
if (e.key === 'Enter') {
|
|
2807
|
+
addBtn.click();
|
|
2808
|
+
}
|
|
2502
2809
|
});
|
|
2503
2810
|
|
|
2504
2811
|
addRow.appendChild(addInput);
|
|
@@ -2514,7 +2821,8 @@
|
|
|
2514
2821
|
panel.appendChild(overlay);
|
|
2515
2822
|
|
|
2516
2823
|
// Request the list
|
|
2517
|
-
connection &&
|
|
2824
|
+
connection &&
|
|
2825
|
+
connection.send(JSON.stringify({ type: 'include_list', threadName: threadName }));
|
|
2518
2826
|
}
|
|
2519
2827
|
|
|
2520
2828
|
function renderIncludeList(panel, threadName, files) {
|
|
@@ -2525,7 +2833,8 @@
|
|
|
2525
2833
|
|
|
2526
2834
|
body.innerHTML = '';
|
|
2527
2835
|
if (files.length === 0) {
|
|
2528
|
-
body.innerHTML =
|
|
2836
|
+
body.innerHTML =
|
|
2837
|
+
'<div class="cumulus-overlay-empty">No always-include files configured.</div>';
|
|
2529
2838
|
return;
|
|
2530
2839
|
}
|
|
2531
2840
|
|
|
@@ -2549,9 +2858,14 @@
|
|
|
2549
2858
|
removeBtn.textContent = '\xD7';
|
|
2550
2859
|
removeBtn.setAttribute('title', 'Remove');
|
|
2551
2860
|
removeBtn.addEventListener('click', function () {
|
|
2552
|
-
connection &&
|
|
2553
|
-
|
|
2554
|
-
|
|
2861
|
+
connection &&
|
|
2862
|
+
connection.send(
|
|
2863
|
+
JSON.stringify({
|
|
2864
|
+
type: 'include_remove',
|
|
2865
|
+
threadName: threadName,
|
|
2866
|
+
filePath: f.path,
|
|
2867
|
+
})
|
|
2868
|
+
);
|
|
2555
2869
|
});
|
|
2556
2870
|
item.appendChild(removeBtn);
|
|
2557
2871
|
|
|
@@ -2562,7 +2876,10 @@
|
|
|
2562
2876
|
// ── Revert overlay ──
|
|
2563
2877
|
function openRevertOverlay(panel, threadName) {
|
|
2564
2878
|
var existing = panel.querySelector('.cumulus-overlay');
|
|
2565
|
-
if (existing) {
|
|
2879
|
+
if (existing) {
|
|
2880
|
+
existing.remove();
|
|
2881
|
+
return;
|
|
2882
|
+
}
|
|
2566
2883
|
|
|
2567
2884
|
var overlay = document.createElement('div');
|
|
2568
2885
|
overlay.className = 'cumulus-overlay';
|
|
@@ -2576,7 +2893,9 @@
|
|
|
2576
2893
|
var closeBtn = document.createElement('button');
|
|
2577
2894
|
closeBtn.className = 'cumulus-overlay-close';
|
|
2578
2895
|
closeBtn.textContent = '\xD7';
|
|
2579
|
-
closeBtn.addEventListener('click', function () {
|
|
2896
|
+
closeBtn.addEventListener('click', function () {
|
|
2897
|
+
overlay.remove();
|
|
2898
|
+
});
|
|
2580
2899
|
header.appendChild(closeBtn);
|
|
2581
2900
|
overlay.appendChild(header);
|
|
2582
2901
|
|
|
@@ -2591,7 +2910,8 @@
|
|
|
2591
2910
|
panel.appendChild(overlay);
|
|
2592
2911
|
|
|
2593
2912
|
// Request the turn list
|
|
2594
|
-
connection &&
|
|
2913
|
+
connection &&
|
|
2914
|
+
connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
|
|
2595
2915
|
}
|
|
2596
2916
|
|
|
2597
2917
|
function renderRevertList(panel, threadName, turns) {
|
|
@@ -2670,7 +2990,8 @@
|
|
|
2670
2990
|
cancelBtn.textContent = 'Cancel';
|
|
2671
2991
|
cancelBtn.addEventListener('click', function () {
|
|
2672
2992
|
// Re-request list
|
|
2673
|
-
connection &&
|
|
2993
|
+
connection &&
|
|
2994
|
+
connection.send(JSON.stringify({ type: 'revert_list', threadName: threadName }));
|
|
2674
2995
|
});
|
|
2675
2996
|
actions.appendChild(cancelBtn);
|
|
2676
2997
|
|
|
@@ -2681,10 +3002,14 @@
|
|
|
2681
3002
|
revertBtn.addEventListener('click', function () {
|
|
2682
3003
|
revertBtn.disabled = true;
|
|
2683
3004
|
revertBtn.textContent = 'Reverting\u2026';
|
|
2684
|
-
connection &&
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
3005
|
+
connection &&
|
|
3006
|
+
connection.send(
|
|
3007
|
+
JSON.stringify({
|
|
3008
|
+
type: 'revert_execute',
|
|
3009
|
+
threadName: threadName,
|
|
3010
|
+
targetMessageId: turn.userMessageId,
|
|
3011
|
+
})
|
|
3012
|
+
);
|
|
2688
3013
|
});
|
|
2689
3014
|
actions.appendChild(revertBtn);
|
|
2690
3015
|
|
|
@@ -2712,7 +3037,10 @@
|
|
|
2712
3037
|
console.error('[Cumulus] Auth failed:', data.error);
|
|
2713
3038
|
clearStoredApiKey();
|
|
2714
3039
|
activeApiKey = '';
|
|
2715
|
-
if (connection) {
|
|
3040
|
+
if (connection) {
|
|
3041
|
+
connection.close();
|
|
3042
|
+
connection = null;
|
|
3043
|
+
}
|
|
2716
3044
|
showAuthView();
|
|
2717
3045
|
var errEl = authPanel.querySelector('[data-testid="webchat-auth-error"]');
|
|
2718
3046
|
if (errEl) errEl.textContent = 'Authentication failed. Check your API key.';
|
|
@@ -2726,13 +3054,34 @@
|
|
|
2726
3054
|
}
|
|
2727
3055
|
break;
|
|
2728
3056
|
|
|
3057
|
+
case 'projects':
|
|
3058
|
+
// Server response to { type: 'projects' } request
|
|
3059
|
+
if (data.projects) {
|
|
3060
|
+
allProjects = data.projects;
|
|
3061
|
+
renderSidebar();
|
|
3062
|
+
}
|
|
3063
|
+
break;
|
|
3064
|
+
|
|
3065
|
+
case 'project_created':
|
|
3066
|
+
// Server confirms new project; refresh project list and open primary thread
|
|
3067
|
+
if (data.project) {
|
|
3068
|
+
requestProjectList();
|
|
3069
|
+
if (data.project.primaryThread) {
|
|
3070
|
+
soloThread(data.project.primaryThread);
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
break;
|
|
3074
|
+
|
|
2729
3075
|
case 'thread_created':
|
|
2730
3076
|
// Server confirms new thread; add to list and select it
|
|
2731
3077
|
if (data.threadName) {
|
|
2732
3078
|
// Add to allThreads if not already present
|
|
2733
3079
|
var exists = false;
|
|
2734
3080
|
for (var i = 0; i < allThreads.length; i++) {
|
|
2735
|
-
if (allThreads[i].name === data.threadName) {
|
|
3081
|
+
if (allThreads[i].name === data.threadName) {
|
|
3082
|
+
exists = true;
|
|
3083
|
+
break;
|
|
3084
|
+
}
|
|
2736
3085
|
}
|
|
2737
3086
|
if (!exists) {
|
|
2738
3087
|
allThreads.push({ name: data.threadName, messageCount: 0, lastActivity: Date.now() });
|
|
@@ -2787,7 +3136,10 @@
|
|
|
2787
3136
|
break;
|
|
2788
3137
|
}
|
|
2789
3138
|
state.streaming = false;
|
|
2790
|
-
state.messages.push({
|
|
3139
|
+
state.messages.push({
|
|
3140
|
+
role: 'assistant',
|
|
3141
|
+
content: data.response || state.streamBuffer,
|
|
3142
|
+
});
|
|
2791
3143
|
state.streamBuffer = '';
|
|
2792
3144
|
updateThreadActivity(data.threadName);
|
|
2793
3145
|
refreshThreadPanel(data.threadName);
|
|
@@ -2800,9 +3152,16 @@
|
|
|
2800
3152
|
state.stopRequested = false;
|
|
2801
3153
|
state.streaming = false;
|
|
2802
3154
|
if (state.streamBuffer) {
|
|
2803
|
-
state.messages.push({
|
|
3155
|
+
state.messages.push({
|
|
3156
|
+
role: 'assistant',
|
|
3157
|
+
content:
|
|
3158
|
+
state.streamBuffer + '\n\n[Error: ' + (data.error || 'Unknown error') + ']',
|
|
3159
|
+
});
|
|
2804
3160
|
} else {
|
|
2805
|
-
state.messages.push({
|
|
3161
|
+
state.messages.push({
|
|
3162
|
+
role: 'assistant',
|
|
3163
|
+
content: '[Error: ' + (data.error || 'Unknown error') + ']',
|
|
3164
|
+
});
|
|
2806
3165
|
}
|
|
2807
3166
|
state.streamBuffer = '';
|
|
2808
3167
|
refreshThreadPanel(data.threadName);
|
|
@@ -2840,15 +3199,22 @@
|
|
|
2840
3199
|
tState.messages = [];
|
|
2841
3200
|
tState.historyLoaded = false;
|
|
2842
3201
|
// Re-request history
|
|
2843
|
-
connection &&
|
|
2844
|
-
|
|
2845
|
-
|
|
3202
|
+
connection &&
|
|
3203
|
+
connection.send(
|
|
3204
|
+
JSON.stringify({
|
|
3205
|
+
type: 'history',
|
|
3206
|
+
threadName: data.threadName,
|
|
3207
|
+
limit: 200,
|
|
3208
|
+
})
|
|
3209
|
+
);
|
|
2846
3210
|
if (overlay) overlay.remove();
|
|
2847
3211
|
} else {
|
|
2848
3212
|
// Show error in overlay
|
|
2849
3213
|
if (overlay && overlay._revertBody) {
|
|
2850
|
-
overlay._revertBody.innerHTML =
|
|
2851
|
-
|
|
3214
|
+
overlay._revertBody.innerHTML =
|
|
3215
|
+
'<div class="cumulus-overlay-status error">' +
|
|
3216
|
+
escapeHtml(data.error || 'Revert failed') +
|
|
3217
|
+
'</div>';
|
|
2852
3218
|
}
|
|
2853
3219
|
}
|
|
2854
3220
|
}
|
|
@@ -2889,7 +3255,10 @@
|
|
|
2889
3255
|
|
|
2890
3256
|
function submitNewThread() {
|
|
2891
3257
|
var name = newThreadInput.value.trim();
|
|
2892
|
-
if (!name) {
|
|
3258
|
+
if (!name) {
|
|
3259
|
+
hideNewThreadInput();
|
|
3260
|
+
return;
|
|
3261
|
+
}
|
|
2893
3262
|
if (connection) {
|
|
2894
3263
|
connection.send({ type: 'create_thread', threadName: name });
|
|
2895
3264
|
}
|
|
@@ -2897,7 +3266,10 @@
|
|
|
2897
3266
|
// Optimistically add and show (server will confirm with thread_created)
|
|
2898
3267
|
var exists = false;
|
|
2899
3268
|
for (var i = 0; i < allThreads.length; i++) {
|
|
2900
|
-
if (allThreads[i].name === name) {
|
|
3269
|
+
if (allThreads[i].name === name) {
|
|
3270
|
+
exists = true;
|
|
3271
|
+
break;
|
|
3272
|
+
}
|
|
2901
3273
|
}
|
|
2902
3274
|
if (!exists) {
|
|
2903
3275
|
allThreads.push({ name: name, messageCount: 0, lastActivity: Date.now() });
|
|
@@ -2908,8 +3280,13 @@
|
|
|
2908
3280
|
newThreadBtn.addEventListener('click', showNewThreadInput);
|
|
2909
3281
|
|
|
2910
3282
|
newThreadInput.addEventListener('keydown', function (e) {
|
|
2911
|
-
if (e.key === 'Enter') {
|
|
2912
|
-
|
|
3283
|
+
if (e.key === 'Enter') {
|
|
3284
|
+
e.preventDefault();
|
|
3285
|
+
submitNewThread();
|
|
3286
|
+
}
|
|
3287
|
+
if (e.key === 'Escape') {
|
|
3288
|
+
hideNewThreadInput();
|
|
3289
|
+
}
|
|
2913
3290
|
});
|
|
2914
3291
|
|
|
2915
3292
|
newThreadInput.addEventListener('blur', function () {
|
|
@@ -2919,6 +3296,51 @@
|
|
|
2919
3296
|
}, 150);
|
|
2920
3297
|
});
|
|
2921
3298
|
|
|
3299
|
+
// ── New project creation ──
|
|
3300
|
+
var newProjectCreating = false;
|
|
3301
|
+
|
|
3302
|
+
function showNewProjectForm() {
|
|
3303
|
+
if (newProjectCreating) return;
|
|
3304
|
+
newProjectCreating = true;
|
|
3305
|
+
newProjectNameInput.value = '';
|
|
3306
|
+
newProjectTemplateSelect.value = 'default';
|
|
3307
|
+
renderSidebar();
|
|
3308
|
+
newProjectNameInput.focus();
|
|
3309
|
+
}
|
|
3310
|
+
|
|
3311
|
+
function hideNewProjectForm() {
|
|
3312
|
+
newProjectCreating = false;
|
|
3313
|
+
newProjectNameInput.value = '';
|
|
3314
|
+
renderSidebar();
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
function submitNewProject() {
|
|
3318
|
+
var name = newProjectNameInput.value.trim();
|
|
3319
|
+
if (!name) {
|
|
3320
|
+
hideNewProjectForm();
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
3323
|
+
var template = newProjectTemplateSelect.value || 'default';
|
|
3324
|
+
if (connection) {
|
|
3325
|
+
connection.send({ type: 'create_project', name: name, template: template });
|
|
3326
|
+
}
|
|
3327
|
+
hideNewProjectForm();
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
newProjectBtn.addEventListener('click', showNewProjectForm);
|
|
3331
|
+
newProjectCancelBtn.addEventListener('click', hideNewProjectForm);
|
|
3332
|
+
newProjectCreateBtn.addEventListener('click', submitNewProject);
|
|
3333
|
+
|
|
3334
|
+
newProjectNameInput.addEventListener('keydown', function (e) {
|
|
3335
|
+
if (e.key === 'Enter') {
|
|
3336
|
+
e.preventDefault();
|
|
3337
|
+
submitNewProject();
|
|
3338
|
+
}
|
|
3339
|
+
if (e.key === 'Escape') {
|
|
3340
|
+
hideNewProjectForm();
|
|
3341
|
+
}
|
|
3342
|
+
});
|
|
3343
|
+
|
|
2922
3344
|
// ── Auth events ──
|
|
2923
3345
|
var authInput = authPanel.querySelector('[data-testid="webchat-auth-input"]');
|
|
2924
3346
|
var authSubmitBtn = authPanel.querySelector('[data-testid="webchat-auth-submit"]');
|
|
@@ -2935,7 +3357,10 @@
|
|
|
2935
3357
|
|
|
2936
3358
|
authSubmitBtn.addEventListener('click', submitAuth);
|
|
2937
3359
|
authInput.addEventListener('keydown', function (e) {
|
|
2938
|
-
if (e.key === 'Enter') {
|
|
3360
|
+
if (e.key === 'Enter') {
|
|
3361
|
+
e.preventDefault();
|
|
3362
|
+
submitAuth();
|
|
3363
|
+
}
|
|
2939
3364
|
});
|
|
2940
3365
|
|
|
2941
3366
|
// ── Logout ──
|
|
@@ -2944,7 +3369,10 @@
|
|
|
2944
3369
|
logoutBtn.addEventListener('click', function () {
|
|
2945
3370
|
clearStoredApiKey();
|
|
2946
3371
|
activeApiKey = '';
|
|
2947
|
-
if (connection) {
|
|
3372
|
+
if (connection) {
|
|
3373
|
+
connection.close();
|
|
3374
|
+
connection = null;
|
|
3375
|
+
}
|
|
2948
3376
|
showAuthView();
|
|
2949
3377
|
authInput.value = '';
|
|
2950
3378
|
});
|
|
@@ -2952,7 +3380,10 @@
|
|
|
2952
3380
|
|
|
2953
3381
|
// ── Connection ──
|
|
2954
3382
|
function startConnection() {
|
|
2955
|
-
if (connection) {
|
|
3383
|
+
if (connection) {
|
|
3384
|
+
connection.close();
|
|
3385
|
+
connection = null;
|
|
3386
|
+
}
|
|
2956
3387
|
connection = createConnection({
|
|
2957
3388
|
wsUrl: wsUrl,
|
|
2958
3389
|
apiKey: activeApiKey,
|