@struggler/smart-ai-renderer

Vue renderer for Smart AI markdown and structured render blocks
1,078
Install
npm install @struggler/smart-ai-renderer
Latest Version:0.3.3
Vue:3
License:MIT
Last Updated:Sep 22, 2026
Links: npm
Maintainer: struggler

@struggler/smart-ai-renderer

Vue 3 renderer for Smart AI chat messages. It renders ordinary Markdown and the platform's fenced structured output, including cards, forms, tables, charts, reports, citations, and maps.

Install

For an existing Vue 3.5+ application:

npm install @struggler/smart-ai-renderer

For a new application, install the Vue peer dependency as well:

npm install vue@^3.5 @struggler/smart-ai-renderer

With pnpm:

pnpm add vue@^3.5 @struggler/smart-ai-renderer

The renderer installs its own runtime dependencies, including Naive UI, ECharts, marked, DOMPurify, highlight.js, jsonrepair, and Vue Router. Consumers do not need to install those packages separately. Vue is a peer dependency and must be provided by the host application.

Local example

cd packages/smart-ai-renderer
make install
make example

The example provides a gallery covering every registered component, editable Markdown/render JSON, live preview, theme switching, streaming-state switching, and a host event log.

Use

<script setup>
import { ChatMessageRenderer, configureRenderer } from "@struggler/smart-ai-renderer"
import "@struggler/smart-ai-renderer/style.css"

configureRenderer({
  onOpenLink(url) {
    // Return false to let the built-in smartai:// and HTTP handlers continue.
    console.log("open", url)
    return false
  },
  async amapKeyProvider() {
    return { key: "YOUR_AMAP_KEY", security_code: "YOUR_SECURITY_CODE" }
  },
})

function sendFollowUp(text) {
  console.log("send to agent", text)
}
</script>

<template>
  <ChatMessageRenderer
    :content="message.content"
    :streaming="message.streaming"
    :theme="{ brand: '#087f5b', radiusMd: '6px' }"
    @follow-up="sendFollowUp"
  />
</template>

Styling and themes

Use the theme prop for common design tokens:

<ChatMessageRenderer
  :content="content"
  :theme="{
    brand: '#087f5b',
    brandStrong: '#066649',
    brandSoft: '#e6fcf5',
    text: '#172033',
    textMuted: '#667085',
    surface: '#ffffff',
    surfaceSunken: '#f6f8fa',
    border: '#d9e2e8',
    radiusSm: '4px',
    radiusMd: '6px',
    fontFamily: 'Inter, sans-serif'
  }"
/>

For advanced customization, add a class and override the same CSS variables from the host stylesheet:

<ChatMessageRenderer class="finance-theme" :content="content" />
.finance-theme {
  --c-brand: #0057b8;
  --c-brand-strong: #003f87;
  --c-brand-soft: #eaf2fb;
  --c-brand-text: #004b9b;
  --c-brand-border: #9fc3e8;
  --c-text: #172033;
  --c-text-muted: #667085;
  --c-surface: #ffffff;
  --c-surface-sunken: #f6f8fa;
  --c-border: #d9e2e8;
  --r-sm: 4px;
  --r-md: 6px;
  --sar-font-family: Inter, sans-serif;
}

The package stylesheet is required and should be imported once:

import "@struggler/smart-ai-renderer/style.css"

Use an external stylesheet

You can keep all renderer overrides in a separate CSS file. Import it after the package stylesheet so that your rules take precedence:

import "@struggler/smart-ai-renderer/style.css"
import "./smart-ai-theme.css"

Add a class to the renderer to keep the overrides scoped to this instance:

<ChatMessageRenderer
  class="custom-renderer"
  :content="content"
/>
/* smart-ai-theme.css */
.custom-renderer {
  --c-brand: #087f5b;
  --c-brand-strong: #066649;
  --c-brand-soft: #e6fcf5;
  --c-text: #172033;
  --c-text-muted: #667085;
  --c-surface: #ffffff;
  --c-border: #d9e2e8;
  --r-md: 4px;
}

.custom-renderer .rt-action {
  min-height: 40px;
  border-radius: 4px;
}

.custom-renderer .rt-subcard {
  box-shadow: none;
}

Prefer CSS variables for colors, typography, spacing, and radii. Use internal classes only for targeted layout changes, because template markup may evolve between major versions.

Render protocol

The component accepts Markdown mixed with fenced render JSON:

这是普通 Markdown 正文。

```render
{
  "type": "link_card",
  "title": "查看办理页面",
  "description": "打开业务办理入口",
  "url": "https://example.com"
}
```

The package currently includes 33 registered templates across business cards, interactive controls, information displays, charts, tables, maps, and citations. Run the local example and open the All components tab to inspect every template with working sample data.

Location request template

location_request renders a user-consented geolocation card. It is useful when an agent asks for “near me”, “current location”, or “within N km” searches.

{
  "type": "location_request",
  "intro": "To search nearby services, we need your current location.",
  "title": "Share current location",
  "desc": "Only used for this nearby search.",
  "button_text": "Allow location",
  "purpose": "nearby_search",
  "radius_km": 3,
  "submit_action": "location_request.submit"
}

When the user clicks the button, the component calls the browser Geolocation API and emits a follow-up string with hidden comments:

已授权当前位置<!-- {"action":"location_request.submit","data":{"status":"granted","latitude":22.543096,"longitude":114.057865,"accuracy":35}} --><!-- smartai:location
{"status":"granted","latitude":22.543096,"longitude":114.057865,"accuracy":35}
-->

If the user denies permission, the browser does not support geolocation, or the request times out, the component still emits a structured status such as denied, unavailable, or timeout. The host should send this follow-up back to the agent so it can ask for a manual address fallback. The card uses the same submitted-state restore mechanism as other interactive templates: after refresh, it stays locked and shows the previous status when the paired follow-up comment is present. Browser geolocation requires HTTPS or localhost in most modern browsers.

Custom render templates

Register a Vue component in the host application, then let render JSON select it by type:

<script setup>
import StatusCard from "./StatusCard.vue"
import {
  ChatMessageRenderer,
  registerRenderTemplate,
} from "@struggler/smart-ai-renderer"

registerRenderTemplate("status_card", StatusCard, {
  validate(data) {
    if (!data.title) return "title is required"
    if (!data.status) return "status is required"
    return true
  },
})
</script>

<template>
  <ChatMessageRenderer :content="content" />
</template>

The custom component receives a data prop containing the complete render object. It can emit follow-up with a string, just like a built-in interactive template:

<script setup>
defineProps({ data: { type: Object, required: true } })
const emit = defineEmits(["follow-up"])
</script>

<template>
  <section class="status-card">
    <strong>{{ data.title }}</strong>
    <span>{{ data.status }}</span>
    <button @click="emit('follow-up', data.action)">继续</button>
  </section>
</template>

The Agent can now return ordinary render JSON:

```render
{
  "type": "status_card",
  "title": "数据准备完成",
  "status": "可生成",
  "action": "请继续生成报告"
}
```

Available registry APIs:

import {
  getRenderTemplateTypes,
  hasRenderTemplate,
  registerRenderTemplate,
  registerRenderTemplates,
  unregisterRenderTemplate,
} from "@struggler/smart-ai-renderer"

registerRenderTemplates({
  status_card: StatusCard,
  result_card: {
    component: ResultCard,
    validate: (data) => data.items?.length > 0 || "items is required",
  },
})

hasRenderTemplate("status_card")
getRenderTemplateTypes()
unregisterRenderTemplate("status_card")

Duplicate types are rejected. Use { override: true } only when intentionally replacing an existing template. Unregistering an override restores the built-in component.

Registration is supported after mount and updates active renderers. Registering templates during application startup is still recommended so the first message never flashes the fallback.

Custom fallback

Use a host component when JSON has an unknown type, fails custom validation, or a template throws while rendering:

import { configureRenderer } from "@struggler/smart-ai-renderer"
import UnknownRenderCard from "./UnknownRenderCard.vue"

configureRenderer({ fallbackRenderer: UnknownRenderCard })

The fallback receives data, reason, and validationError props. Set fallbackRenderer: null to restore the built-in JSON fallback.

Security boundary

Render JSON is data only. It may select a registered type and provide fields consumed by that component, but it cannot contain executable code, import paths, component URLs, or arbitrary JavaScript. Components and validators must always be registered by trusted host code.

Host integration

  • onOpenLink(url): intercept HTTP or smartai:// navigation. Return false to use the built-in handler.
  • onUnsupportedLink(action, params, message): show a toast or log unsupported protocol actions.
  • router: optional Vue Router-compatible object used by internal chat/back actions.
  • webviewPagePath: host mini-program WebView page path.
  • amapKeyProvider(): load the AMap key at runtime without embedding it in the npm package.
  • fallbackRenderer: optional Vue component used when a render type is unknown or invalid.

Interactive templates emit follow-up text. The host decides how to send it to the Agent API.

Parser-only use

import { tryParseRenderable, splitMixedRenderable } from "@struggler/smart-ai-renderer"

Adding and distributing templates

Adding template metadata, registering a host-specific component, and publishing a new built-in SDK template are different workflows. Read TEMPLATE-DEVELOPMENT-GUIDE.md before changing the registry or publishing a new package version.

The guide includes the decision matrix, implementation paths, SemVer rules, release commands, consumer upgrade steps, security constraints, and completion checklists. It is included in the npm tarball so consuming teams and coding agents can follow the same process without access to the administration application source.

The npm tarball contains compiled assets, type declarations, styles, documentation, and the license. It does not include the Smart AI administration application source.

Release

make pack
make publish

Both commands use https://registry.npmjs.org/; make pack performs the same checks without publishing.

Related Packages

@huyooo/ai-chat-frontend-vue

AI Chat Frontend - Vue components with adapter pattern

12,303
punine-design

一个基于 Vue 3 的 AI 聊天插件,通过Langchain.js、DeepSeek API Key快速构建自定义...

3,987
zen-ai-chat-ui

一个精美的大模型对话 UI 组件库,基于 Vue 3 + TypeScript。支持流式输出、思考过程...

3,306 0
ai-assistant-plugin

Vue2 AI 助手浮层插件:无 Element UI 依赖,支持主动唤起、多轮对话、部门数据透传

1,125
@ai-slot/adapter-vue

Vue 3 renderer adapter for ai-slot: render validated component trees as real Vue...

399 0

Version History

Version Released Vue License
0.3.3 >=3.5.0 <4 MIT
0.3.2 >=3.5.0 <4 MIT
0.3.1 >=3.5.0 <4 MIT
0.3.0 >=3.5.0 <4 MIT
0.2.0 >=3.5.0 <4 MIT