@luckydraw/cumulus 0.27.21 → 0.27.24

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.
@@ -18,6 +18,7 @@
18
18
  </style>
19
19
  </head>
20
20
  <body>
21
+ <script src="/blex.min.js"></script>
21
22
  <script src="/widget.js" data-standalone="true" data-api-key=""></script>
22
23
  </body>
23
24
  </html>
@@ -198,6 +198,17 @@
198
198
  '.cumulus-msg em { font-style: italic; }',
199
199
  '.cumulus-msg del { text-decoration: line-through; color: #888; }',
200
200
 
201
+ /* Blex block containers */
202
+ '.blex-block-container {',
203
+ ' margin: 0.6em 0;',
204
+ ' border-radius: 8px;',
205
+ ' overflow: hidden;',
206
+ ' min-height: 2em;',
207
+ '}',
208
+ '.blex-block-container[data-blex-ready="true"] {',
209
+ ' min-height: auto;',
210
+ '}',
211
+
201
212
  /* Headings */
202
213
  '.cumulus-msg h1,.cumulus-msg h2,.cumulus-msg h3,.cumulus-msg h4,.cumulus-msg h5,.cumulus-msg h6 {',
203
214
  ' font-weight: 600; margin: 0.7em 0 0.3em; line-height: 1.3; color: #e8e8e8;',
@@ -1101,14 +1112,116 @@
1101
1112
  el.appendChild(header);
1102
1113
 
1103
1114
  var body = document.createElement('div');
1104
- body.innerHTML = renderMarkdown(agent.body);
1115
+ var mdResult = renderMarkdown(agent.body);
1116
+ body.innerHTML = mdResult.html;
1117
+ renderBlexBlocks(body, mdResult.blexBlocks, null);
1105
1118
  el.appendChild(body);
1106
1119
  return el;
1107
1120
  }
1108
1121
 
1109
1122
  // ── Markdown ──────────────────────────────────────────────────────────────
1110
1123
 
1124
+ // ─── Blex block handle tracking ─────────────────────────────────────────────
1125
+ // Map: msgEl → Array<BlockHandle> — for cleanup on message delete/revert
1126
+ var blexHandles = new WeakMap();
1127
+
1128
+ /**
1129
+ * Parse blex fenced blocks from markdown text.
1130
+ * Returns { text: processedText, blocks: [{type, json, id}] }
1131
+ * Fences: ~~~blex:TYPE\n{json}\n~~~ or ```blex:TYPE\n{json}\n```
1132
+ */
1133
+ function extractBlexBlocks(text) {
1134
+ var blocks = [];
1135
+ var BLEX_PREFIX = '\x00BLEX_';
1136
+ var processed = text.replace(
1137
+ /(?:~~~|```)blex:(\w[\w-]*)\n([\s\S]*?)(?:~~~|```)/g,
1138
+ function (_, type, json) {
1139
+ var idx = blocks.length;
1140
+ var id = 'blex-' + idx + '-' + Date.now();
1141
+ blocks.push({ type: type, json: json.trim(), id: id });
1142
+ return BLEX_PREFIX + idx + '\x00';
1143
+ }
1144
+ );
1145
+ return { text: processed, blocks: blocks };
1146
+ }
1147
+
1148
+ /**
1149
+ * After innerHTML is set, find blex placeholder divs and render blocks.
1150
+ * Returns array of BlockHandles for lifecycle management.
1151
+ */
1152
+ function renderBlexBlocks(container, blocks, inputEl) {
1153
+ if (!blocks || !blocks.length || typeof window.Blex === 'undefined') return;
1154
+ var handles = [];
1155
+ var placeholders = container.querySelectorAll('[data-blex-idx]');
1156
+ placeholders.forEach(function (ph) {
1157
+ var idx = parseInt(ph.getAttribute('data-blex-idx'), 10);
1158
+ var block = blocks[idx];
1159
+ if (!block) return;
1160
+
1161
+ // Try to parse JSON
1162
+ var data;
1163
+ try {
1164
+ data = JSON.parse(block.json);
1165
+ } catch (e) {
1166
+ // Show parse error in a fallback
1167
+ ph.innerHTML =
1168
+ '<pre style="color:#f44;font-size:0.85em;">blex:' +
1169
+ escapeHtml(block.type) +
1170
+ ' — invalid JSON: ' +
1171
+ escapeHtml(e.message) +
1172
+ '</pre>';
1173
+ return;
1174
+ }
1175
+
1176
+ var contentBlock = { type: block.type, id: block.id, data: data };
1177
+
1178
+ // Render the block
1179
+ window.Blex.renderBlock(contentBlock, ph)
1180
+ .then(function (handle) {
1181
+ handles.push(handle);
1182
+ // Wire interaction → inject into input
1183
+ if (handle && handle.onInteraction && inputEl) {
1184
+ handle.onInteraction(function (interaction) {
1185
+ if (interaction && interaction.serialized) {
1186
+ inputEl.value =
1187
+ (inputEl.value ? inputEl.value + '\n' : '') + interaction.serialized;
1188
+ inputEl.focus();
1189
+ // Trigger input event so send button updates
1190
+ inputEl.dispatchEvent(new Event('input', { bubbles: true }));
1191
+ }
1192
+ });
1193
+ }
1194
+ })
1195
+ .catch(function (err) {
1196
+ ph.innerHTML =
1197
+ '<pre style="color:#f44;font-size:0.85em;">blex:' +
1198
+ escapeHtml(block.type) +
1199
+ ' render error: ' +
1200
+ escapeHtml(err.message) +
1201
+ '</pre>';
1202
+ });
1203
+ });
1204
+ blexHandles.set(container, handles);
1205
+ }
1206
+
1207
+ /**
1208
+ * Destroy all blex block handles for a message element.
1209
+ */
1210
+ function destroyBlexBlocks(container) {
1211
+ var handles = blexHandles.get(container);
1212
+ if (handles) {
1213
+ handles.forEach(function (h) {
1214
+ if (h && h.destroy) h.destroy();
1215
+ });
1216
+ blexHandles.delete(container);
1217
+ }
1218
+ }
1219
+
1111
1220
  function renderMarkdown(text) {
1221
+ // Phase 0.5: Extract blex fenced blocks — replace with unique tokens
1222
+ var blexResult = extractBlexBlocks(text);
1223
+ text = blexResult.text;
1224
+
1112
1225
  // Phase 1: Extract fenced code blocks — replace with unique tokens
1113
1226
  var codeBlocks = [];
1114
1227
  var TOKEN_PREFIX = '\x00CODEBLOCK_';
@@ -1181,7 +1294,20 @@
1181
1294
  return codeBlocks[parseInt(idx, 10)];
1182
1295
  });
1183
1296
 
1184
- return html;
1297
+ // Phase 7.5: Reinsert blex block placeholders
1298
+ if (blexResult.blocks.length > 0) {
1299
+ html = html.replace(/\x00BLEX_(\d+)\x00/g, function (_, idx) {
1300
+ return (
1301
+ '<div class="blex-block-container" data-blex-idx="' +
1302
+ idx +
1303
+ '" data-testid="blex-block-' +
1304
+ idx +
1305
+ '"></div>'
1306
+ );
1307
+ });
1308
+ }
1309
+
1310
+ return { html: html, blexBlocks: blexResult.blocks };
1185
1311
  }
1186
1312
 
1187
1313
  function buildCodeBlock(lang, escapedCode, tokenId) {
@@ -1688,10 +1814,29 @@
1688
1814
  el.className = 'cumulus-msg assistant';
1689
1815
  if (isStreaming) el.setAttribute('data-testid', 'webchat-streaming');
1690
1816
  if (content) {
1691
- el.innerHTML = renderMarkdown(content);
1817
+ var mdResult = renderMarkdown(content);
1818
+ el.innerHTML = mdResult.html;
1692
1819
  if (isStreaming) {
1693
1820
  el.innerHTML += '<span class="cumulus-cursor"></span>';
1694
1821
  }
1822
+ // Render blex blocks
1823
+ if (mdResult.blexBlocks.length > 0) {
1824
+ if (isStreaming && typeof window.Blex !== 'undefined') {
1825
+ // During streaming: show placeholders
1826
+ el.querySelectorAll('[data-blex-idx]').forEach(function (ph) {
1827
+ var idx = parseInt(ph.getAttribute('data-blex-idx'), 10);
1828
+ var block = mdResult.blexBlocks[idx];
1829
+ if (block) window.Blex.renderPlaceholder(block.type, ph);
1830
+ });
1831
+ } else if (!isStreaming) {
1832
+ // Final render: full blex blocks with interaction wiring
1833
+ var inputEl = el.closest('.cumulus-panel, .cumulus-widget');
1834
+ var blexInputEl = inputEl
1835
+ ? inputEl.querySelector('.cumulus-input, .cumulus-standalone-input')
1836
+ : null;
1837
+ renderBlexBlocks(el, mdResult.blexBlocks, blexInputEl);
1838
+ }
1839
+ }
1695
1840
  el.querySelectorAll('.code-block-copy-btn').forEach(function (btn) {
1696
1841
  btn.addEventListener('click', function () {
1697
1842
  var targetId = btn.getAttribute('data-copy-target');
@@ -1714,6 +1859,10 @@
1714
1859
  }
1715
1860
 
1716
1861
  function renderMessages() {
1862
+ // Destroy existing blex block handles before clearing DOM
1863
+ messagesEl.querySelectorAll('.cumulus-msg.assistant').forEach(function (m) {
1864
+ destroyBlexBlocks(m);
1865
+ });
1717
1866
  messagesEl.innerHTML = '';
1718
1867
  // Apply selection mode class
1719
1868
  if (selectionMode) {
@@ -1974,7 +2123,10 @@
1974
2123
  }
1975
2124
  streaming = false;
1976
2125
  stopRequested = false;
1977
- if (embeddedStreamTimer) { clearTimeout(embeddedStreamTimer); embeddedStreamTimer = null; }
2126
+ if (embeddedStreamTimer) {
2127
+ clearTimeout(embeddedStreamTimer);
2128
+ embeddedStreamTimer = null;
2129
+ }
1978
2130
  updateSendBtn();
1979
2131
  }
1980
2132
  showChatView();
@@ -2004,14 +2156,18 @@
2004
2156
  if (streaming) {
2005
2157
  console.warn('[Cumulus] Streaming timeout — resetting');
2006
2158
  if (streamBuffer) {
2007
- messages.push({ role: 'assistant', content: streamBuffer + '\n\n*(response may be incomplete)*' });
2159
+ messages.push({
2160
+ role: 'assistant',
2161
+ content: streamBuffer + '\n\n*(response may be incomplete)*',
2162
+ });
2008
2163
  }
2009
2164
  streaming = false;
2010
2165
  streamBuffer = '';
2011
2166
  updateSendBtn();
2012
2167
  renderMessages();
2013
2168
  // Reload history
2014
- if (connection) connection.send({ type: 'history', threadName: sessionId, limit: 50 });
2169
+ if (connection)
2170
+ connection.send({ type: 'history', threadName: sessionId, limit: 50 });
2015
2171
  }
2016
2172
  embeddedStreamTimer = null;
2017
2173
  }, EMBEDDED_STREAM_TIMEOUT);
@@ -2020,7 +2176,10 @@
2020
2176
  case 'segment':
2021
2177
  break;
2022
2178
  case 'done':
2023
- if (embeddedStreamTimer) { clearTimeout(embeddedStreamTimer); embeddedStreamTimer = null; }
2179
+ if (embeddedStreamTimer) {
2180
+ clearTimeout(embeddedStreamTimer);
2181
+ embeddedStreamTimer = null;
2182
+ }
2024
2183
  if (stopRequested) {
2025
2184
  stopRequested = false;
2026
2185
  streaming = false;
@@ -2040,10 +2199,16 @@
2040
2199
  break;
2041
2200
  case 'interjected':
2042
2201
  // Server confirms old stream was aborted. Partial already frozen client-side.
2043
- if (embeddedStreamTimer) { clearTimeout(embeddedStreamTimer); embeddedStreamTimer = null; }
2202
+ if (embeddedStreamTimer) {
2203
+ clearTimeout(embeddedStreamTimer);
2204
+ embeddedStreamTimer = null;
2205
+ }
2044
2206
  break;
2045
2207
  case 'error':
2046
- if (embeddedStreamTimer) { clearTimeout(embeddedStreamTimer); embeddedStreamTimer = null; }
2208
+ if (embeddedStreamTimer) {
2209
+ clearTimeout(embeddedStreamTimer);
2210
+ embeddedStreamTimer = null;
2211
+ }
2047
2212
  stopRequested = false;
2048
2213
  streaming = false;
2049
2214
  if (streamBuffer) {
@@ -2265,7 +2430,10 @@
2265
2430
  if (s && s.streaming) {
2266
2431
  console.warn('[CumulusChat] Streaming timeout for thread:', threadName);
2267
2432
  if (s.streamBuffer) {
2268
- s.messages.push({ role: 'assistant', content: s.streamBuffer + '\n\n*(response may be incomplete — connection issue)*' });
2433
+ s.messages.push({
2434
+ role: 'assistant',
2435
+ content: s.streamBuffer + '\n\n*(response may be incomplete — connection issue)*',
2436
+ });
2269
2437
  }
2270
2438
  s.streaming = false;
2271
2439
  s.streamBuffer = '';
@@ -3002,10 +3170,29 @@
3002
3170
  el.className = 'cumulus-msg assistant';
3003
3171
  if (isStreaming) el.setAttribute('data-testid', 'webchat-streaming');
3004
3172
  if (content) {
3005
- el.innerHTML = renderMarkdown(content);
3173
+ var mdResult = renderMarkdown(content);
3174
+ el.innerHTML = mdResult.html;
3006
3175
  if (isStreaming) {
3007
3176
  el.innerHTML += '<span class="cumulus-cursor"></span>';
3008
3177
  }
3178
+ // Render blex blocks
3179
+ if (mdResult.blexBlocks.length > 0) {
3180
+ if (isStreaming && typeof window.Blex !== 'undefined') {
3181
+ // During streaming: show placeholders
3182
+ el.querySelectorAll('[data-blex-idx]').forEach(function (ph) {
3183
+ var idx = parseInt(ph.getAttribute('data-blex-idx'), 10);
3184
+ var block = mdResult.blexBlocks[idx];
3185
+ if (block) window.Blex.renderPlaceholder(block.type, ph);
3186
+ });
3187
+ } else if (!isStreaming) {
3188
+ // Final render: full blex blocks with interaction wiring
3189
+ var inputEl = el.closest('.cumulus-panel, .cumulus-widget');
3190
+ var blexInputEl = inputEl
3191
+ ? inputEl.querySelector('.cumulus-input, .cumulus-standalone-input')
3192
+ : null;
3193
+ renderBlexBlocks(el, mdResult.blexBlocks, blexInputEl);
3194
+ }
3195
+ }
3009
3196
  el.querySelectorAll('.code-block-copy-btn').forEach(function (btn) {
3010
3197
  btn.addEventListener('click', function () {
3011
3198
  var targetId = btn.getAttribute('data-copy-target');
@@ -3028,6 +3215,10 @@
3028
3215
  }
3029
3216
 
3030
3217
  function renderPanelMessages() {
3218
+ // Destroy existing blex block handles before clearing DOM
3219
+ messagesEl.querySelectorAll('.cumulus-msg.assistant').forEach(function (m) {
3220
+ destroyBlexBlocks(m);
3221
+ });
3031
3222
  messagesEl.innerHTML = '';
3032
3223
  // Apply selection mode class to messages container
3033
3224
  if (state.selectionMode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luckydraw/cumulus",
3
- "version": "0.27.21",
3
+ "version": "0.27.24",
4
4
  "description": "RLM-based CLI chat wrapper for Claude with external history context management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,7 @@
32
32
  "cumulus-gateway": "./dist/gateway/daemon.js"
33
33
  },
34
34
  "scripts": {
35
- "build": "tsc && rm -rf dist/gateway/static && cp -r src/gateway/static dist/gateway/static",
35
+ "build": "tsc && rm -rf dist/gateway/static && cp -r src/gateway/static dist/gateway/static && cp node_modules/@luckydraw/blex/dist/blex.min.global.js dist/gateway/static/blex.min.js && cp node_modules/@luckydraw/blex/dist/blex-chart.min.global.js dist/gateway/static/blex-chart.min.js",
36
36
  "dev": "tsc --watch",
37
37
  "lint": "eslint src",
38
38
  "lint:fix": "eslint src --fix",
@@ -89,6 +89,7 @@
89
89
  },
90
90
  "dependencies": {
91
91
  "@huggingface/transformers": "^3.8.1",
92
+ "@luckydraw/blex": "^0.1.0",
92
93
  "@modelcontextprotocol/sdk": "^1.25.3",
93
94
  "@slack/bolt": "^4.6.0",
94
95
  "commander": "^14.0.2",