@luckydraw/cumulus 0.28.3 → 0.28.4
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.
|
@@ -1226,6 +1226,10 @@
|
|
|
1226
1226
|
// WeakMap: DOM element -> BlockHandle[] for lifecycle management
|
|
1227
1227
|
var blexHandles = typeof WeakMap !== 'undefined' ? new WeakMap() : null;
|
|
1228
1228
|
|
|
1229
|
+
// Per-thread store for blex interaction values (poll answers, confirm clicks, etc.)
|
|
1230
|
+
// Key: "threadName" → Map<"msgTimestamp:blockIdx", interactionValue>
|
|
1231
|
+
var blexInteractionStore = {};
|
|
1232
|
+
|
|
1229
1233
|
// Extract ~~~blex:TYPE\n{json}\n~~~ fences from text
|
|
1230
1234
|
// Returns { text: string (with placeholders), blocks: Array<{type, json, idx}> }
|
|
1231
1235
|
function extractBlexBlocks(text) {
|
|
@@ -1240,18 +1244,23 @@
|
|
|
1240
1244
|
}
|
|
1241
1245
|
|
|
1242
1246
|
// Render blex blocks into placeholder divs within a message element
|
|
1243
|
-
function renderBlexBlocks(el, blexBlocks, isStreaming) {
|
|
1247
|
+
function renderBlexBlocks(el, blexBlocks, isStreaming, msgKey, threadName) {
|
|
1244
1248
|
if (!blexBlocks || blexBlocks.length === 0) return;
|
|
1245
1249
|
if (typeof Blex === 'undefined') {
|
|
1246
1250
|
console.warn('[blex] Blex library not loaded — blex.min.js may have failed to fetch');
|
|
1247
1251
|
return;
|
|
1248
1252
|
}
|
|
1249
1253
|
var placeholders = el.querySelectorAll('.blex-block-container');
|
|
1254
|
+
// Get the interaction store for this thread
|
|
1255
|
+
var store = threadName
|
|
1256
|
+
? blexInteractionStore[threadName] || (blexInteractionStore[threadName] = {})
|
|
1257
|
+
: null;
|
|
1250
1258
|
|
|
1251
1259
|
placeholders.forEach(function (container) {
|
|
1252
1260
|
var idx = parseInt(container.getAttribute('data-blex-idx'), 10);
|
|
1253
1261
|
if (isNaN(idx) || idx >= blexBlocks.length) return;
|
|
1254
1262
|
var block = blexBlocks[idx];
|
|
1263
|
+
var storeKey = msgKey ? msgKey + ':' + idx : null;
|
|
1255
1264
|
|
|
1256
1265
|
try {
|
|
1257
1266
|
var data = JSON.parse(block.json);
|
|
@@ -1261,8 +1270,14 @@
|
|
|
1261
1270
|
// During streaming, show placeholder skeleton
|
|
1262
1271
|
Blex.renderPlaceholder(block.type, container);
|
|
1263
1272
|
} else {
|
|
1273
|
+
// Check for previously stored interaction value
|
|
1274
|
+
var renderOpts = {};
|
|
1275
|
+
if (store && storeKey && store[storeKey]) {
|
|
1276
|
+
renderOpts.previousValue = store[storeKey];
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1264
1279
|
// Final render — full interactive block
|
|
1265
|
-
Blex.renderBlock(blockObj, container)
|
|
1280
|
+
Blex.renderBlock(blockObj, container, renderOpts)
|
|
1266
1281
|
.then(function (handle) {
|
|
1267
1282
|
// Store handle immediately (inside async callback, not after sync loop)
|
|
1268
1283
|
if (blexHandles) {
|
|
@@ -1273,6 +1288,11 @@
|
|
|
1273
1288
|
if (handle && handle.onInteraction) {
|
|
1274
1289
|
// Wire interaction handler
|
|
1275
1290
|
handle.onInteraction(function (interaction) {
|
|
1291
|
+
// Store the interaction value for persistence across re-renders
|
|
1292
|
+
if (store && storeKey && interaction.value !== undefined) {
|
|
1293
|
+
store[storeKey] = interaction.value;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1276
1296
|
// Find the panel — try el.closest first, fall back to document query
|
|
1277
1297
|
// (el may be detached from DOM if renderPanelMessages rebuilt the message list)
|
|
1278
1298
|
var panel = el.closest('.cumulus-thread-panel') || el.closest('.cumulus-panel');
|
|
@@ -1287,8 +1307,11 @@
|
|
|
1287
1307
|
|
|
1288
1308
|
if (inputEl && interaction.serialized) {
|
|
1289
1309
|
if (interaction.immediate) {
|
|
1290
|
-
// Immediate:
|
|
1291
|
-
|
|
1310
|
+
// Immediate: prepend any existing input text, then auto-send
|
|
1311
|
+
var existingText = inputEl.value.trim();
|
|
1312
|
+
inputEl.value = existingText
|
|
1313
|
+
? existingText + '\n\n' + interaction.serialized
|
|
1314
|
+
: interaction.serialized;
|
|
1292
1315
|
inputEl.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1293
1316
|
// Find and click the send button
|
|
1294
1317
|
var sendBtn = panel ? panel.querySelector('[data-testid*="send"]') : null;
|
|
@@ -1389,14 +1412,25 @@
|
|
|
1389
1412
|
// ── Inline Annotations (Google Docs-style highlight + comment) ──────────
|
|
1390
1413
|
|
|
1391
1414
|
// Add an annotation chip to the chip tray
|
|
1392
|
-
function addAnnotationChip(panel, quote, comment) {
|
|
1415
|
+
function addAnnotationChip(panel, quote, comment, isCodeBlock, codeLang) {
|
|
1393
1416
|
if (!panel) return;
|
|
1394
1417
|
var tray = panel.querySelector('.cumulus-chip-tray');
|
|
1395
1418
|
if (!tray) return;
|
|
1396
1419
|
|
|
1397
1420
|
var truncatedQuote = quote.length > 30 ? quote.substring(0, 30) + '\u2026' : quote;
|
|
1398
1421
|
var truncatedComment = comment.length > 30 ? comment.substring(0, 30) + '\u2026' : comment;
|
|
1399
|
-
var serialized
|
|
1422
|
+
var serialized;
|
|
1423
|
+
if (isCodeBlock) {
|
|
1424
|
+
serialized =
|
|
1425
|
+
'```' +
|
|
1426
|
+
(codeLang && codeLang !== 'text' ? codeLang : '') +
|
|
1427
|
+
'\n' +
|
|
1428
|
+
quote +
|
|
1429
|
+
'\n```\n' +
|
|
1430
|
+
comment;
|
|
1431
|
+
} else {
|
|
1432
|
+
serialized = '> ' + quote.replace(/\n/g, '\n> ') + '\n' + comment;
|
|
1433
|
+
}
|
|
1400
1434
|
|
|
1401
1435
|
var chip = document.createElement('span');
|
|
1402
1436
|
chip.className = 'cumulus-annotation-chip';
|
|
@@ -1420,7 +1454,7 @@
|
|
|
1420
1454
|
}
|
|
1421
1455
|
|
|
1422
1456
|
// Show the annotation popover near a text selection
|
|
1423
|
-
function showAnnotationPopover(panel, selectedText, anchorRect) {
|
|
1457
|
+
function showAnnotationPopover(panel, selectedText, anchorRect, isCodeBlock, codeLang) {
|
|
1424
1458
|
// Remove any existing popover
|
|
1425
1459
|
dismissAnnotationPopover(panel);
|
|
1426
1460
|
|
|
@@ -1469,7 +1503,7 @@
|
|
|
1469
1503
|
submitBtn.addEventListener('click', function () {
|
|
1470
1504
|
var comment = textarea.value.trim();
|
|
1471
1505
|
if (comment) {
|
|
1472
|
-
addAnnotationChip(panel, selectedText, comment);
|
|
1506
|
+
addAnnotationChip(panel, selectedText, comment, isCodeBlock, codeLang);
|
|
1473
1507
|
}
|
|
1474
1508
|
dismissAnnotationPopover(panel);
|
|
1475
1509
|
window.getSelection().removeAllRanges();
|
|
@@ -1519,10 +1553,22 @@
|
|
|
1519
1553
|
var msgEl = container.closest ? container.closest('.cumulus-msg.assistant') : null;
|
|
1520
1554
|
if (!msgEl) return;
|
|
1521
1555
|
|
|
1556
|
+
// Detect if selection is inside a code block
|
|
1557
|
+
var codeAncestor = container.closest ? container.closest('pre, code') : null;
|
|
1558
|
+
var isCodeBlock = !!codeAncestor;
|
|
1559
|
+
var codeLang = '';
|
|
1560
|
+
if (isCodeBlock) {
|
|
1561
|
+
var wrapper = container.closest ? container.closest('.code-block-wrapper') : null;
|
|
1562
|
+
if (wrapper) {
|
|
1563
|
+
var langEl = wrapper.querySelector('.code-block-language');
|
|
1564
|
+
if (langEl) codeLang = langEl.textContent.trim();
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1522
1568
|
// Get anchor position for the popover
|
|
1523
1569
|
var rect = range.getBoundingClientRect();
|
|
1524
1570
|
|
|
1525
|
-
showAnnotationPopover(panel, selectedText, rect);
|
|
1571
|
+
showAnnotationPopover(panel, selectedText, rect, isCodeBlock, codeLang);
|
|
1526
1572
|
}, 10);
|
|
1527
1573
|
});
|
|
1528
1574
|
|
|
@@ -2168,7 +2214,8 @@
|
|
|
2168
2214
|
function buildUserMsgEl(msg) {
|
|
2169
2215
|
var el = document.createElement('div');
|
|
2170
2216
|
el.className = 'cumulus-msg user' + (isWideUserMessage(msg.content) ? ' wide' : '');
|
|
2171
|
-
|
|
2217
|
+
var mdResult = renderMarkdown(msg.content);
|
|
2218
|
+
el.innerHTML = mdResult.html;
|
|
2172
2219
|
if (msg.attachments && msg.attachments.length > 0) {
|
|
2173
2220
|
var attRow = document.createElement('div');
|
|
2174
2221
|
attRow.className = 'cumulus-msg-attachments';
|
|
@@ -2193,7 +2240,7 @@
|
|
|
2193
2240
|
return el;
|
|
2194
2241
|
}
|
|
2195
2242
|
|
|
2196
|
-
function buildAssistantMsgEl(content, isStreaming) {
|
|
2243
|
+
function buildAssistantMsgEl(content, isStreaming, msgKey) {
|
|
2197
2244
|
var el = document.createElement('div');
|
|
2198
2245
|
el.className = 'cumulus-msg assistant';
|
|
2199
2246
|
if (isStreaming) el.setAttribute('data-testid', 'webchat-streaming');
|
|
@@ -2205,7 +2252,7 @@
|
|
|
2205
2252
|
}
|
|
2206
2253
|
// Render blex blocks — skip during streaming to avoid destroy/recreate churn
|
|
2207
2254
|
if (!isStreaming) {
|
|
2208
|
-
renderBlexBlocks(el, mdResult.blexBlocks, false);
|
|
2255
|
+
renderBlexBlocks(el, mdResult.blexBlocks, false, msgKey, threadName);
|
|
2209
2256
|
}
|
|
2210
2257
|
el.querySelectorAll('.code-block-copy-btn').forEach(function (btn) {
|
|
2211
2258
|
btn.addEventListener('click', function () {
|
|
@@ -2296,7 +2343,7 @@
|
|
|
2296
2343
|
row.appendChild(buildUserMsgEl(msg));
|
|
2297
2344
|
}
|
|
2298
2345
|
} else {
|
|
2299
|
-
row.appendChild(buildAssistantMsgEl(msg.content, false));
|
|
2346
|
+
row.appendChild(buildAssistantMsgEl(msg.content, false, String(msg.timestamp)));
|
|
2300
2347
|
}
|
|
2301
2348
|
appendTimestamp(row, msg.timestamp);
|
|
2302
2349
|
messagesEl.appendChild(row);
|
|
@@ -3733,7 +3780,8 @@
|
|
|
3733
3780
|
function buildUserMsgEl(msg) {
|
|
3734
3781
|
var el = document.createElement('div');
|
|
3735
3782
|
el.className = 'cumulus-msg user' + (isWideUserMessage(msg.content) ? ' wide' : '');
|
|
3736
|
-
|
|
3783
|
+
var mdResult = renderMarkdown(msg.content);
|
|
3784
|
+
el.innerHTML = mdResult.html;
|
|
3737
3785
|
if (msg.attachments && msg.attachments.length > 0) {
|
|
3738
3786
|
var attRow = document.createElement('div');
|
|
3739
3787
|
attRow.className = 'cumulus-msg-attachments';
|
|
@@ -3758,7 +3806,7 @@
|
|
|
3758
3806
|
return el;
|
|
3759
3807
|
}
|
|
3760
3808
|
|
|
3761
|
-
function buildAssistantMsgEl(content, isStreaming) {
|
|
3809
|
+
function buildAssistantMsgEl(content, isStreaming, msgKey) {
|
|
3762
3810
|
var el = document.createElement('div');
|
|
3763
3811
|
el.className = 'cumulus-msg assistant';
|
|
3764
3812
|
if (isStreaming) el.setAttribute('data-testid', 'webchat-streaming');
|
|
@@ -3770,7 +3818,7 @@
|
|
|
3770
3818
|
}
|
|
3771
3819
|
// Render blex blocks — skip during streaming to avoid destroy/recreate churn
|
|
3772
3820
|
if (!isStreaming) {
|
|
3773
|
-
renderBlexBlocks(el, mdResult.blexBlocks, false);
|
|
3821
|
+
renderBlexBlocks(el, mdResult.blexBlocks, false, msgKey, threadName);
|
|
3774
3822
|
}
|
|
3775
3823
|
el.querySelectorAll('.code-block-copy-btn').forEach(function (btn) {
|
|
3776
3824
|
btn.addEventListener('click', function () {
|
|
@@ -3853,7 +3901,7 @@
|
|
|
3853
3901
|
row.appendChild(buildUserMsgEl(msg));
|
|
3854
3902
|
}
|
|
3855
3903
|
} else {
|
|
3856
|
-
row.appendChild(buildAssistantMsgEl(msg.content, false));
|
|
3904
|
+
row.appendChild(buildAssistantMsgEl(msg.content, false, String(msg.timestamp)));
|
|
3857
3905
|
}
|
|
3858
3906
|
appendTimestamp(row, msg.timestamp);
|
|
3859
3907
|
messagesEl.appendChild(row);
|
|
@@ -4129,13 +4177,15 @@
|
|
|
4129
4177
|
return;
|
|
4130
4178
|
}
|
|
4131
4179
|
|
|
4132
|
-
// Freeze the partial assistant response as a message
|
|
4180
|
+
// Freeze the partial assistant response as a message so the interjection
|
|
4181
|
+
// appears AFTER the (partial) LLM response, not next to the prior user message
|
|
4133
4182
|
if (state.streamBuffer) {
|
|
4134
4183
|
state.messages.push({ role: 'assistant', content: state.streamBuffer });
|
|
4135
4184
|
}
|
|
4136
4185
|
// Reset streaming state — the server will send 'interjected' for the old stream
|
|
4137
4186
|
state.streamBuffer = '';
|
|
4138
4187
|
state.interjecting = true;
|
|
4188
|
+
state.interjectionStreamId = (state.interjectionStreamId || 0) + 1;
|
|
4139
4189
|
// Don't set streaming=false — the new message will keep streaming
|
|
4140
4190
|
}
|
|
4141
4191
|
|