Skip to content

Plugins

@aparte/core is presentational and carries no third-party dependency: it renders raw text, exposes seams, and asks nothing of you. Plugins fill those seams — each is an opt-in @aparte/plugin-* package that you install and wire in one call, so core stays small and you pay only for what you use.

You want…PackageSeam it fills
Render finished Markdown messagesmarkedsetMarkdownProvider
Stream Markdown token-by-tokenstreaming-markdownsetStreamingMarkdownProvider
Highlight code blocksshikisetHighlightProvider
Let the user pick a provider + modelmodel-selector<aparte-model-selector> element
Let the AI ask the user a questionask-userregisterTool + elicitation
Switch approval modes — plan, ask, auto-edit, autoapprovalsetApprovalPolicy + <aparte-approval-mode>
Let the AI produce a document — a page, a component, a spreadsheet — shown as a Code/Preview cardartifactsregisterTool + registerToolRenderer + registerStreamBlock + registerSegmentRenderer
Summarise a long conversation so it fits the model’s windowcompactionthe aparte-compact command + the compaction: true notice
Title each conversation from its first message, with a 40–133 KB model in the browsertitlerthe conversation manager’s setTitleProvider

Every plugin lists @aparte/core as a peer dependency and, where it wraps a third-party library (marked, streaming-markdown, shiki), that library too — so you control its version and it is never bundled into core.

Most plugins register something on the config once, at startup:

import { setupMarkedProvider } from '@aparte/plugin-marked';
import { setupShikiProvider } from '@aparte/plugin-shiki';
setupMarkedProvider(); // finished messages → HTML
await setupShikiProvider(); // code blocks → highlighted HTML

The two Markdown plugins are complementary: streaming-markdown renders each token as it arrives, and marked re-renders the finished message. Register both for the best of both.

Every setup* takes the config instance as its last argument, defaulting to the global aparteGlobalConfig. Pass your own instance and the plugin registers there instead — which is what makes two independently configured chats on one page actually work:

import { AparteConfig } from '@aparte/core';
import { setupMarkedProvider } from '@aparte/plugin-marked';
// Markdown for the support chat only; the other chat on the page keeps plain text.
const supportConfig = new AparteConfig();
setupMarkedProvider(undefined, supportConfig);

Then hand that same instance to the chat — the config prop on the React/Vue/Svelte/Angular wrappers, or new AparteClient({ config: supportConfig }) in vanilla.

Translating the built-in UI strings is its own seam — see the Localization guide and the @aparte/locale-fr package.