micro-models-agent 2.16.0 → 2.16.1
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/CHANGELOG.md +6 -0
- package/dist/assets/js/feed.js +43 -4
- package/dist/assets/md.js +14 -0
- package/dist/assets/styles.css +6 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -21,6 +21,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
21
21
|
- **Web UI boot was dead on 2.15.0** (`src/modules/webui/assets/js/settings.js`): a stray `}` in `setStaleBar` pushed `return` to top level (`Uncaught SyntaxError: Illegal return statement`), killing the whole `app.js` import graph — blank page, no feed, selectors stuck at `–`. One-line brace fix.
|
|
22
22
|
- **Web e2e boot smoke** (`tests/e2e/web-boot.e2e-test.ts`): `mma web` boots in a real Chromium with zero console/page errors (live session tab, chrome skeleton, settings modal) and every served JS module in the live `/app.js` import graph answers 200; `web-helpers.launch` now names collected console errors when the feed never boots, so the next broken module fails with its `SyntaxError` in the message instead of a bare timeout.
|
|
23
23
|
|
|
24
|
+
## [2.16.1] - 2026-10-01
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **Smooth streamed-text cascade** (`src/modules/webui/assets/js/feed.js`, `md.js`, `styles.css`): live markdown chunks are buffered and flushed once per animation frame (`requestAnimationFrame`, multi-feed safe) instead of write+scroll per SSE event, and each streamed text run renders in a `span.tk` with a short opacity-only `tkIn` fade (staggered `(n%4)*20ms`, `.15s`). History render and code blocks skip the animation; killed by `prefers-reduced-motion`.
|
|
29
|
+
|
|
24
30
|
## [2.16.0] - 2026-10-01
|
|
25
31
|
|
|
26
32
|
### Added
|
package/dist/assets/js/feed.js
CHANGED
|
@@ -246,16 +246,20 @@ function resetRun() {
|
|
|
246
246
|
|
|
247
247
|
// Options passed to the streaming Markdown renderer: localized code-block
|
|
248
248
|
// copy labels. Highlighting is applied by `md.js` once a fence closes.
|
|
249
|
-
|
|
249
|
+
// `animate` wraps live-streamed text chunks in `span.tk` (CSS `tkIn` fade);
|
|
250
|
+
// history render and code blocks skip it.
|
|
251
|
+
function mdOpts(animate) {
|
|
250
252
|
return {
|
|
251
253
|
copyLabel: t("web.code.copy"),
|
|
252
254
|
copiedLabel: t("web.code.copied"),
|
|
253
255
|
copyFailedLabel: t("web.code.copy_failed"),
|
|
256
|
+
animate: animate === true,
|
|
254
257
|
};
|
|
255
258
|
}
|
|
256
259
|
|
|
257
260
|
// Close the active Markdown segment (if any) and clear the text S.state.
|
|
258
261
|
export function finishMd() {
|
|
262
|
+
flushText();
|
|
259
263
|
if (S.curMd) {
|
|
260
264
|
S.curMd.root.classList.remove("streaming");
|
|
261
265
|
S.curMd.end();
|
|
@@ -264,6 +268,38 @@ export function finishMd() {
|
|
|
264
268
|
S.curText = null;
|
|
265
269
|
}
|
|
266
270
|
|
|
271
|
+
// Text chunks arrive in bursts (several SSE `text` events per frame); writing
|
|
272
|
+
// and scrolling on every one thrashes layout and stacks overlapping `tkIn`
|
|
273
|
+
// fades ("рвано"). Buffer them and flush once per animation frame instead.
|
|
274
|
+
let textBuf = "";
|
|
275
|
+
let textFeed = null;
|
|
276
|
+
let textScheduled = false;
|
|
277
|
+
|
|
278
|
+
function flushText() {
|
|
279
|
+
textScheduled = false;
|
|
280
|
+
if (!textBuf || !S.curMd) {
|
|
281
|
+
textBuf = "";
|
|
282
|
+
textFeed = null;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const buf = textBuf;
|
|
286
|
+
const feed = textFeed;
|
|
287
|
+
textBuf = "";
|
|
288
|
+
textFeed = null;
|
|
289
|
+
S.curMd.write(buf);
|
|
290
|
+
if (feed && feed === activeFeed()) scroll();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function scheduleText() {
|
|
294
|
+
if (textScheduled) return;
|
|
295
|
+
textScheduled = true;
|
|
296
|
+
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
|
|
297
|
+
window.requestAnimationFrame(flushText);
|
|
298
|
+
} else {
|
|
299
|
+
flushText();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
267
303
|
export function onText(chunk, sid) {
|
|
268
304
|
const feed = feedForEvent(sid);
|
|
269
305
|
clearBusy(feed);
|
|
@@ -275,14 +311,17 @@ export function onText(chunk, sid) {
|
|
|
275
311
|
if (S.curText == null) {
|
|
276
312
|
flushRun();
|
|
277
313
|
S.curText = "";
|
|
278
|
-
S.curMd = mountMarkdown(feed, mdOpts());
|
|
314
|
+
S.curMd = mountMarkdown(feed, mdOpts(true));
|
|
279
315
|
S.curMd.root.classList.add("streaming");
|
|
280
316
|
S.turnRoots.push(S.curMd.root);
|
|
281
317
|
}
|
|
282
318
|
S.curText += chunk;
|
|
283
319
|
S.turnMd += chunk;
|
|
284
|
-
|
|
285
|
-
if (
|
|
320
|
+
// A second session streaming concurrently must not merge into another feed's buffer.
|
|
321
|
+
if (textFeed && textFeed !== feed) flushText();
|
|
322
|
+
textFeed = feed;
|
|
323
|
+
textBuf += chunk;
|
|
324
|
+
scheduleText();
|
|
286
325
|
}
|
|
287
326
|
|
|
288
327
|
export function finishThink() {
|
package/dist/assets/md.js
CHANGED
|
@@ -198,6 +198,20 @@ function addText(data, text) {
|
|
|
198
198
|
data.code.codeEl.appendChild(document.createTextNode(text));
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
+
if (data.opts.animate) {
|
|
202
|
+
// Stagger cascade within a flush: each span starts before the previous
|
|
203
|
+
// one finishes ((n%4)*20ms delay, .15s duration). Modulo keeps long
|
|
204
|
+
// outputs cascading instead of settling on a permanent max delay.
|
|
205
|
+
const n = data.animN || 0;
|
|
206
|
+
data.animN = n + 1;
|
|
207
|
+
const span = document.createElement("span");
|
|
208
|
+
span.className = "tk";
|
|
209
|
+
const delay = (n % 4) * 20;
|
|
210
|
+
if (delay) span.style.animationDelay = delay + "ms";
|
|
211
|
+
span.appendChild(document.createTextNode(text));
|
|
212
|
+
data.nodes[data.index].appendChild(span);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
201
215
|
smd.default_add_text(data, text);
|
|
202
216
|
}
|
|
203
217
|
|
package/dist/assets/styles.css
CHANGED
|
@@ -656,6 +656,12 @@ body.light .sel > button { border: 1px solid var(--border); }
|
|
|
656
656
|
content: "▌"; color: var(--accent); margin-left: 2px;
|
|
657
657
|
animation: blink 1s steps(2, start) infinite;
|
|
658
658
|
}
|
|
659
|
+
/* per-chunk typewriter fade for live markdown (md.js wraps each streamed
|
|
660
|
+
text chunk in `span.tk` when `opts.animate` is set; history render and
|
|
661
|
+
code blocks skip it). Opacity-only: `.tk` stays inline so wrapping is
|
|
662
|
+
untouched. Killed by the `prefers-reduced-motion` block below. */
|
|
663
|
+
@keyframes tkIn { from { opacity: 0; } }
|
|
664
|
+
.md.streaming .tk { animation: tkIn .15s ease-out backwards; }
|
|
659
665
|
/* alive labels: in-feed spinner, the *live* think block, rail phase,
|
|
660
666
|
a running tool. Finished blocks stay static — a pulsing "думаю" after
|
|
661
667
|
the turn reads as hung. */
|