All versions since 0.17.1
0.17.1
Every @aparte/* package ships at this version (they are released in lockstep).
Patch Changes
-
8b7ccb4: With
@aparte/plugin-streaming-markdownas your only Markdown renderer, a finished reply now stays rendered when your framework re-renders the chat, instead of turning back into raw Markdown a frame after the turn ends. Nothing to change on your side; the one-shot provider registered as a workaround can go (#89).0.17.0 kept what the incremental parser drew on the settling update, but that update is not the last one a finished message receives. After every render the wrappers reconcile the last message, and for a finished one that sends each text segment
{ content, isStreaming: false }again. The parser was gone by then, so the second settle fell through to core’s escape-and-<br>default. The same happened to a reply streamed as plaincontentrather than segments, throughsetContent.writeStreamedMarkdownnow remembers, per content element, the content it settled on from the parser’s DOM. A later non-streaming update carrying that same content leaves the element alone. Changed content, a one-shot provider registered since, or a new stream into the same element render as before.@aparte/core -
a8776d2:
<aparte-suggestions empty-only>now shows its starters while the conversation is empty, instead of hiding them for good on the first send: they stay hidden over a restored thread and come back after a new chat.<aparte-chat>now reflectsdata-emptywhether or notcenter-emptyis set (#90).If you counted messages to hide or re-show the starters yourself, you can drop that code. If you relied on
aparte-chat[data-empty]only appearing withcenter-empty, add[center-empty]to your selector — core’s own rules already carry it, so no built-in layout moves.empty-onlyused to read the composer’saparte-sendalone. It did not look at the conversation, so a thread loaded throughsetMessages, a wrapper’smessagesor a conversation manager showed the starters on top of its transcript (nothing had been sent yet), and after a new chat cleared the thread they never came back. The row now follows the transcript of the chat it serves — found around its composer, around itself, or bytarget— with the same test<aparte-chat>uses: no<aparte-chat-bubble>, and notloading, so a thread being fetched counts as not empty. The send is kept as the other half. It hides the row on the click rather than a frame later when the bubble lands, and it is all there is to read when the messages are not<aparte-chat-bubble>s (React’srenderBubble) or when the composer sits outside any chat. In both of those cases the row behaves exactly as before. The element lifts only ahiddenit set itself, and removingempty-onlyshows the row again.data-emptyon<aparte-chat>was a by-product ofcenter-empty, so the chat’s one statement of “this conversation is empty” was only available to apps that also wanted a centred composer. It now follows the conversation on every<aparte-chat>, including Angular’sframework-managedhost once its viewport renders. React, Vue and Svelte render a[data-aparte-chat]div rather than the element and are unchanged.@aparte/core -
a3350fc: The README says beta where it said alpha: the package installs with
@aparte/core@beta, and its declared surface is frozen as of 0.17.0. No code change.@aparte/core -
1f7107f: New:
aparteGlobalConfig.registerToolSummary(toolName, (segment) => string)shows what a call targets after the tool’s name in the default row —read_file skills.jsoninstead ofread_file— and keeps the row’s states, spinner and disclosure (#91).Until now the only way to add that line was
registerToolRenderer, which replaces the whole row: its six states, the spinner, the disclosure and the relabel contract, re-implemented and kept in step with core for one line of text. The summary is keyed by the tool’s name, like a renderer, so a display-only chat that registers noAparteToolgets it too.The text is written as text, never parsed as HTML;
''shows nothing, and a summary that throws shows nothing rather than breaking the row. It is read again on every update of the call, so it follows arguments that are still arriving, and on a config change, so one registered after a row is on screen reaches it. A tool with its own renderer draws its own row and its summary is not read.unregisterToolSummaryandgetToolSummarycomplete the pair,reset()clears them, and theAparteToolSummarytype is exported. The new.aparte-tool-targetelement is muted, in the code font, and cut with an ellipsis, so a long path gives way before the state word does.@aparte/core
Version-only bumps (no changes of their own): @aparte/engine, @aparte/provider-ai-sdk, @aparte/provider-openai-compat, @aparte/provider-scenario, @aparte/provider-transformers, @aparte/plugin-approval, @aparte/plugin-artifacts, @aparte/plugin-ask-user, @aparte/plugin-compaction, @aparte/plugin-marked, @aparte/plugin-model-selector, @aparte/plugin-shiki, @aparte/plugin-streaming-markdown, @aparte/plugin-titler, @aparte/angular, @aparte/react, @aparte/svelte, @aparte/vue, @aparte/locale-fr, @aparte/docs-mcp.
0.17.2
Every @aparte/* package ships at this version (they are released in lockstep).
Patch Changes
-
2e4f9ee: With
@aparte/plugin-streaming-markdownas your only Markdown renderer, a reply that arrives complete — a conversation restored after a reload, a non-streaming provider’s answer — now renders as Markdown instead of its raw source. Nothing to change on your side; a one-shot provider registered as a workaround can go (#93).The plugin rendered a reply while it streamed, and a reply that never streamed went through
renderMarkdown, which knew only the one-shot provider and fell back to core’s escape-and-<br>default. So the same text rendered two ways depending on how it arrived, and the plugin page’s “that is all you need” held for half of them.renderMarkdownnow runs a complete string through the incremental provider when no one-shot provider is registered — into a detached element, then through the sanitizer, the same two steps a streamed reply takes. A registered one-shot provider still takes precedence, a server render with nodocumentstill gets the escaping default, and a provider that throws on the whole string falls back to it too.@aparte/core,@aparte/plugin-streaming-markdown -
7513253: The README follows
@aparte/titler1.2: a single-language model file is 41 KB. No code change — the plugin accepts any titler, and 1.2 keeps the sametitle(message, budget?)API.@aparte/plugin-titler
Version-only bumps (no changes of their own): @aparte/engine, @aparte/provider-ai-sdk, @aparte/provider-openai-compat, @aparte/provider-scenario, @aparte/provider-transformers, @aparte/plugin-approval, @aparte/plugin-artifacts, @aparte/plugin-ask-user, @aparte/plugin-compaction, @aparte/plugin-marked, @aparte/plugin-model-selector, @aparte/plugin-shiki, @aparte/angular, @aparte/react, @aparte/svelte, @aparte/vue, @aparte/locale-fr, @aparte/docs-mcp.
0.17.3 Latest
Every @aparte/* package ships at this version (they are released in lockstep).
Patch Changes
-
@aparte/engine@0.17.3
@aparte/core -
5ec1e8a: Fixed: in controlled mode (
messageswithonMessagesChange), a finished reply could roll back to an earlier state — its text cut mid-word, its tool rows gone. It happened when the parent re-rendered late, which React does in Safari.The chat emits its list as it streams, the parent stores it, and the prop comes back. When the parent’s render lands after the chat’s next write, the prop that arrives is an EARLIER emit, and the wrapper took it for the parent’s word: it rolled the list back, the chat built its next writes on the stale copy, and the reply stayed broken. Measured in the examples’ browser suite at about one run in five on React + WebKit. The wrappers now recognise their own emits when they come back — current or stale — and ignore them; a list the parent builds itself is still applied. Angular is unaffected: its list is one
model()signal both sides write in step.@aparte/react,@aparte/svelte,@aparte/vue
Version-only bumps (no changes of their own): @aparte/engine, @aparte/provider-ai-sdk, @aparte/provider-openai-compat, @aparte/provider-scenario, @aparte/provider-transformers, @aparte/plugin-approval, @aparte/plugin-artifacts, @aparte/plugin-ask-user, @aparte/plugin-compaction, @aparte/plugin-marked, @aparte/plugin-model-selector, @aparte/plugin-shiki, @aparte/plugin-streaming-markdown, @aparte/plugin-titler, @aparte/angular, @aparte/locale-fr, @aparte/docs-mcp.