Skip to content

feat(core): ChartController 暴露渲染器插件注册 + preview 外部渲染器加载点 - #266

Open
EliteOtaku wants to merge 5 commits into
363045841:mainfrom
EliteOtaku:pr/external-renderer-loader
Open

EliteOtaku wants to merge 5 commits into
363045841:mainfrom
EliteOtaku:pr/external-renderer-loader

Conversation

@EliteOtaku

@EliteOtaku EliteOtaku commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

动机

宿主(业务前端)需要在 K 线上叠加自己的 canvas 自绘图层(指标扩展、业务标记等)。目前 useRenderer 只在 core Chart 实例上可用:

  • vue/agent 层拿到的 ChartController 没有渲染器注册入口,只能依赖 dev-only 的 window.__chart 自挂实例(production 构建下不存在);
  • preview 工作台没有外部插件加载机制,宿主能力无法以插件形态接入。

方案(纯增量 API,两段)

1. core:ChartController 暴露渲染器插件注册

interface ChartController {
  // ---- Renderer plugins ----
  useRenderer(plugin: RendererPlugin | RendererPluginWithHost, config?: Record<string, unknown>): void
  removeRenderer(name: string): void
  getRenderer<T extends RendererPlugin = RendererPlugin>(name: string): T | undefined
}
  • 实现为对核心 Chart 实例的透传,幂等语义与 Chart.useRenderer 一致(按 plugin.name 保留首注册实例);
  • 补 scheduleDraw(level?) 透传:外部插件经轮询/WS 取到新数据后需要显式触发重绘(引擎仅在交互/数据变更时自绘);
  • 根导出补充 RendererPlugin / RendererPluginWithHost 类型,宿主无需深路径 import。

2. preview:外部渲染器插件加载点

controller-ready 时加载宿主声明的插件模块:

  • 声明通道:localStorage['kcq_external_renderers'](JSON URL 数组)或查询参数 ?externalRenderers=url1,url2;
  • 模块契约:default / renderers / renderer 命名导出 RendererPlugin 或其数组;default 亦可为工厂形态 (host: { controller: ChartController }) => RendererPlugin | RendererPlugin[],供插件读取品种(controller.symbols)与触发重绘(controller.scheduleDraw);
  • 相对路径归一为绝对 URL 后动态 import(dev 管线对相对路径的动态 import 会重写 ?import 进模块图,public/ 资产不在图内会 404——实测踩坑后修正);
  • 单模块失败仅 console.warn,插件之间、插件与工作台互相隔离;
  • 附演示插件 preview/public/external-demo-renderer.js(主图中部虚线参考线),访问 ?externalRenderers=/external-demo-renderer.js 即可复现。

验证

  • 新增 createChartController.renderers.test.ts 3 用例:注册返回同实例 / 同名重复注册幂等(保留首注册)/ removeRenderer 移除;
  • core 全量:248 文件 / 2662 用例全绿;
  • preview 实机(vite dev):[preview] external renderer registered: kcq_demo_external (/external-demo-renderer.js) 注册日志实证,工作台与 agent 面板无回归。

兼容性

纯增量:不改任何既有接口行为;preview 改动仅限 demo 工作台加载逻辑。

…/getRenderer)

宿主业务 overlay(指标扩展/业务标记层)此前只能经 dev-only 的 window.__chart
自挂实例触达 useRenderer;vue/agent 层 ChartController 无注册入口。

- ChartController 新增 useRenderer/removeRenderer/getRenderer,透传核心 Chart
  实例(幂等语义一致:按 plugin.name 保留首注册实例)
- 根导出补充 RendererPlugin/RendererPluginWithHost 类型
- 新增 createChartController.renderers.test.ts 3 用例(注册/幂等/移除)
预览工作台启动(controller-ready)时加载宿主声明的渲染器插件模块:
- 声明通道:localStorage['kcq_external_renderers'](JSON URL 数组)或
  ?externalRenderers=url1,url2
- 模块契约:default/renderers/renderer 导出 RendererPlugin 或其数组
- 相对路径归一为绝对 URL 后动态 import(dev 管线只放行外部协议的动态
  import,相对路径会被重写 ?import 进模块图致 public 资产 404——实测坑)
- 单模块失败仅 console 告警,插件间与工作台互相隔离
- demo:preview/public/external-demo-renderer.js(主图中部虚线参考线)
useRenderer/removeRenderer/getRenderer 之外补 scheduleDraw(level?):外部渲染器
插件经轮询/WS 取到新数据后需要显式触发重绘(引擎仅在交互/数据变更时自绘)。
委托核心 Chart.scheduleDraw,缺省 UpdateLevel.All;测试补 1 用例(注册前后
调用均不抛)。
插件可经 host.controller 读取品种(controller.symbols 信号)与触发重绘
(controller.scheduleDraw),与 core API 面配套。
事件→Agent 通知通道的最小接线:外部插件工厂宿主在 controller 之外
增加 agent 窄面,插件侧离散事件(如指标预判确认)可程序化触发一轮
模型运行(readOnly 语义由调用方经 StartRunInput.readOnly 控制),
回合内经已注册的 chart tools 拉取数据出解读。

- startRun(input): StartRunInput 透传 bridge(sessionId 必填,插件经
  listSessions 解析目标会话)
- listSessions(): 会话列表
- 刻意不暴露整个 bridge(会话/Provider 管理面不外放)
- chartTools 工厂返回值通道保持不变(向后兼容)
@EliteOtaku

Copy link
Copy Markdown
Contributor Author

补充:工厂宿主注入 agent 窄面(99a2a23a)——事件→Agent 通知通道

本 PR 补一个 commit:loadExternalRenderers 的工厂宿主参数在 controller 之外注入 agent 窄面:

factory({
  controller,
  agent: {
    startRun(input: StartRunInput): Promise<{ runId: string }>
    listSessions(): SessionView[]
  },
})

用例(真实需求驱动)

外部指标插件的离散事件需要通知 Agent——以一目均衡表预判为例:插件本地维持「联合触发价」状态机,收盘确认(价格穿越触发价,非 tick 级)时经 agent.startRun({ sessionId, prompt: '一目预判已确认…请读取分析数据', readOnly: true }) 触发一轮只读模型运行;回合内 Agent 经已注册的 chart tools(#274 通道)拉取插件的预判数据出解读。确认是收盘级低频事件,符合通知语义;oneShot/冷却由插件侧控制。

设计边界

  • 最小窄面:只暴露 startRun/listSessions,不外放整个 bridge(会话管理、Provider 凭据面不进插件)。readOnly 语义由调用方经 StartRunInput.readOnly 控制。
  • 向后兼容:chartTools 工厂返回值通道不变;host 参数新增字段,旧插件(只解构 controller)零感知。
  • 会话解析:StartRunInput.sessionId 必填,插件经 listSessions() 解析目标会话;如需「面板当前会话」语义,后续可在 bridge 侧补 getCurrentSessionId(openSession/createSession 已是会话切换必经点,追踪成本极低),本 PR 先不扩。

与其它 PR 的关系

type-check:本 commit 在 App.vue 零新增错误(worktree 需先 build core/agent-runtime dist,否则 TS2307 属环境性误报)。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

1 participant