mcpField
L
lightweight-charts-plugin-authoring

lightweight-charts-plugin-authoring

github.com/tradingview开发17.7k218 次安装6 天前更新
代码仓库

关于 lightweight-charts-plugin-authoring

lightweight-charts-plugin-authoring 是用于开发 Lightweight Charts™ 插件的 skill,面向插件开发者。它涵盖插件类型选择、本地 API 查阅、脚手架生成、参考实现及验证流程。

主要功能

  1. 01指导选择插件类型
  2. 02依据本地类型定义查找 API
  3. 03通过向导生成插件脚手架
  4. 04提供工具包与参考实现指引
  5. 05按步骤构建和测试

适用场景

  1. 01规划自定义序列或图表原语
  2. 02实现前核对可用 API
  3. 03生成插件项目并进行验证

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tradingview/lightweight-charts --skill lightweight-charts-plugin-authoring

安装后在 Claude Code 中输入/lightweight-charts-plugin-authoring确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
official-plugins.md
package-contract.md
toolkit.md

详细介绍

编写 Lightweight Charts™ 插件

本技能介绍如何编写插件。关于如何使用图表本身——系列、刻度、标记、数据、封装器——请使用配套的 lightweight-charts 技能;这两项技能专为搭配安装而设计。

当前所在位置及源文件所在位置

本技能适用于两种场景,同一个问题在不同场景下的答案也不同。查找任何内容前,请先判断当前属于哪种场景:

# upstream checkout of lightweight-charts itself?
test -f pnpm-workspace.yaml && test -d packages/lwc-toolkit && echo upstream || echo "a plugin project or other repo"

在插件项目或其他任何仓库中(最常见的情况):

查找内容位置
库的 APInode_modules/lightweight-charts/dist/typings.d.ts;如果尚未安装,可在 https://tradingview.github.io/lightweight-charts/lightweight-charts.d.ts 获取已发布的类型定义
工具包的 APInode_modules/@tradingview/lwc-toolkit/dist/<module>.d.ts——.d.ts 文件包含文档注释,因此可将其作为文档阅读
参考插件的源代码不在 node_modules 中(软件包只包含 dist/)。请从 GitHub 获取:https://raw.githubusercontent.com/tradingview/lightweight-charts/master/packages/lwc-plugin-<name>/src/<file>,或者克隆一次后在本地阅读:git clone --depth 1 https://github.com/tradingview/lightweight-charts /tmp/lightweight-charts
文档每个文档页面都提供 Markdown 格式,只需在 URL 后追加 .md 即可,例如 https://tradingview.github.io/lightweight-charts/docs/plugins/custom_series.md。https://tradingview.github.io/lightweight-charts/llms.txt 列出了所有页面;其旁边的 docs_map.md 还列出了各页面的标题

**在上游代码库中:**库的类型定义位于 dist/typings.d.ts(或 src/),工具包位于 packages/lwc-toolkit/src/,插件位于 packages/lwc-plugin-*/src/,文档位于 website/docs/plugins/*.md,概念验证示例位于 plugin-examples/。

# what version is installed, and does the hook I am about to rely on exist in it?
node -p "require('lightweight-charts/package.json').version"
rg -n "hitTest|conflationReducer|isHovered|ICustomSeriesPaneView|IPrimitivePaneRenderer" node_modules/lightweight-charts/dist/typings.d.ts
# the toolkit's custom-series surface
rg -n "export (declare )?(function|class|interface|type)" node_modules/@tradingview/lwc-toolkit/dist/custom-series/*.d.ts
# a reference implementation, without a checkout
curl -fsSL https://raw.githubusercontent.com/tradingview/lightweight-charts/master/packages/lwc-plugin-stacked-bars-series/src/renderer.ts

如果本地类型定义与文档不一致,请以本地类型定义为准:文档描述的是当前版本,而类型定义描述的是用户实际安装的版本。如果缺少文件,请说明哪些内容无法验证。切勿臆造选项、钩子或工具包导出项——无法通过臆想的 API 编译的插件会导致用户的类型检查失败,而通过图表会忽略的选项编译的插件则会在运行时静默失效。

从想法到插件类型

这是影响最大、也应该最先做出的决定。思考这个想法是什么,而不是它看起来怎样:

想法是……类型原因
以新方式绘制系列自身的数据(柱、带、堆叠、蜡烛)自定义系列它需要自己的数据、自动缩放、价格线、最新值和时间刻度集成
与某个系列关联的装饰(标签、标记、围绕它的带、基于其数据的绘图工具)系列基元它会读取该系列的坐标,并被限制在该系列范围内
与图格关联的装饰(水印、图例、标题、覆盖所有系列的叠加层)图格基元它覆盖整个图格,且其生命周期不受任何单个系列影响
基于 DOM 的无障碍、工具提示或控件层带有 DOM 叠加层的图格基元画布并非合适的媒介;请将其附加到 paneContentElement(pane)

默认选择基元。自定义系列是更复杂的构造——它必须实现 priceValueBuilder、isWhitespace、renderer 和 update,并且其渲染器会接收系列的绘图数据。只有当这个想法本身就是一个系列时才选择它。需要处理指针事件的绘图工具仍然属于基元:它在基元中绘图,并通过 subscribeClick、subscribeCrosshairMove 和坐标转换 API 响应事件。

选择时还要考虑悬停和交互:基元提供 hitTest,以便图表在十字光标和点击事件中报告它们;自定义系列只有在 Lightweight Charts™ 5.2 及更高版本中,才会在其渲染器上提供 hitTest,这使得 draw 的 isHovered 和 hitTestData 参数能够报告悬停状态。

工作方式

按以下顺序构建。每一步都是发现上一步错误的最低成本时机。

  1. 先写下数据项和选项,再编写代码。数据项决定 isWhitespace(哪些字段会使一个点可绘制),选项决定 defaultOptions。两者分别放在各自的文件中(data.ts、options.ts),因为 README、类型定义和演示都会读取它们。
  2. 搭建脚手架(见下文)。不要从空白文件开始。
  3. 先让渲染器使用脚手架生成的示例数据完成绘制,只绘制可见范围,然后再处理选项或交互。绘制所有内容或什么都不绘制的渲染器,在规模还小时更容易修复。
  4. 通过 applyOptions 接入选项;使用 requestUpdate() 重新渲染。检查每个选项在演示页面中都能生效。
  5. 仅自定义系列:自动缩放和空白数据。 priceValueBuilder 会影响价格刻度和最新值标签;空白数据则决定折线在哪里断开。两者都有容易踩的坑(请查看下方的检查清单)。
  6. 交互:hitTest、悬停、拖动。将交互逻辑与绘制逻辑分开。
  7. 测试,然后是 README,最后是演示和预览页面。软件包约定(references/package-contract.md)说明了每项必须包含的内容。

使用脚手架,不要从空白开始

npm create lwc-plugin@latest

该向导支持交互操作。它会先询问是否安装此技能(如果已经安装,或用户退出向导并将操作交给你,请选择否),然后询问类型、名称、一句话描述、软件包名称、类名、作者、许可证、最低库版本、标签、是否保留提示注释,以及目标文件夹。在运行向导前,先与用户确定这些答案——上文的类型选择最重要——然后让用户在终端中运行向导;如果你的环境提供了 TTY,也可以自行回答提示。向导会生成一个 Vite 项目,其中包含:

  • src/<entry>.ts — 插件类(对于自定义系列,还包含一个工厂);src/options.ts、src/data.ts,以及按类型生成的渲染器或视图文件
  • src/example/index.html + example.ts — 开发演示,运行 pnpm dev 即可提供该页面
  • package.json,包含 dev、build、typecheck 和 check-package 脚本、lightweight-charts peer 范围、作为 devDependency 的 @tradingview/lwc-toolkit,以及用于目录的 lwcPlugin 配置块
  • README,其结构已符合目录的渲染格式

对于“hint comments”,请选择是:这些 //* 行会解释模板的各个部分;否则向导会将它们剥离。阅读这些注释,然后删除已处理的部分。使用脚手架自带的脚本进行构建(pnpm typecheck、pnpm build、pnpm check-package)——CI 和发布工具会运行这些脚本。

基于工具包构建

官方插件均基于 @tradingview/lwc-toolkit 构建。它从各个示例过去手动复制的辅助函数中提取而来。使用它并不是为了少写几行代码,而是因为这些辅助函数封装了很容易出错的图表行为——给定柱间距时内置蜡烛图的精确宽度、适用于各种设备像素比的像素级精确线条位置、LineStyle 的虚线样式,以及数据合并如何改变柱的逻辑步长。通过子路径分别导入各个辅助函数:

import { CustomSeriesRendererBase } from '@tradingview/lwc-toolkit/custom-series/renderer-base';
import { visibleSegments, extendRange, barCoordinate } from '@tradingview/lwc-toolkit/custom-series/visible-bars';
import { PluginBase } from '@tradingview/lwc-toolkit/plugin-base';

常见情况下,可按问题选择对应模块。references/toolkit.md 介绍了其余内容,包括各个 API 的结构,以及使用它的官方插件。

你需要……使用
编写自定义序列渲染器,而不必重新实现数据 / 范围检查custom-series/renderer-base — 扩展 CustomSeriesRendererBase,实现 drawImpl(scope, args)
创建序列或面板图元,并自动处理图表引用和 requestUpdate()plugin-base(PluginBase)/ pane-plugin-base(PanePluginBase)
只绘制屏幕上可见的内容,并用线条延伸至面板边缘custom-series/visible-bars — forEachVisibleBar、mapVisibleBars、extendRange、barCoordinate
在明确的空白处断开线条,而不是在其他序列的时间戳处断开createWhitespaceSeries(custom-series/options-aware-series)+ visibleSegments
在 priceValueBuilder 中读取选项(使自动缩放跟随选项变化)使用会影响绘图值的选项名称调用 createOptionsAwareSeries
处理堆叠值:带状区域边缘和自动缩放极值custom-series/stacking — stackLevels、stackedPlotValues
绘制与内置图表宽度相同且像素对齐的柱形、蜡烛图或线条dimensions/* — calculateColumnPositions、candlestickWidth、positionsLine、positionsBox
绘制带内嵌边框的圆角矩形canvas/round-rect
绘制折线、阶梯线、两条线之间的区域,以及颜色变化的线条custom-series/line-paths
在价格轴或时间轴上添加标签axis-label-view(AxisLabelView)
在面板内添加 DOM 覆盖层dom/pane-element(paneContentElement)
处理简单事件,或将 Time 转换为时间戳或显示字符串delegate、time

如果脚手架的模板已导入某个工具包辅助函数,请保留它;该模板就是对应类型中最精简且正确的插件。

阅读参考实现

官方包是规范示例:持续维护、经过测试、已发布,并且完全按照本指南所述的方式构建。开始设计之前,先阅读最接近你想法的示例——大多数问题(“如何获取悬停的项目?”“如何自动缩放堆叠图?”)都能在其中一个示例的文件里找到答案。在上游仓库中,它们位于 packages/lwc-plugin-*/src/。在其他仓库中,已发布的 @tradingview/lwc-plugin-* 包提供 README 和 dist/*.d.ts,但不包含源代码——请从 GitHub 获取源代码(使用原始 URL 或浅克隆,见上文);每个插件都只有少量文件。

正在构建……阅读
任意自定义序列stacked-bars-series(最简单的完整示例),然后是 pretty-histogram-series
每个数据点包含多个值的序列stacked-area-series、stacked-bars-series
必须在间隙处断开或能够应对合并的折线/面积图hlc-area-series、brushable-area-series
带有自身指针交互的序列brushable-area-series(BrushableAreaInteraction)
替代内置序列的实现rounded-candles-series(与烛台选项和宽度匹配)
固定像素高度的覆盖序列dual-range-histogram-series(scaleMode、keepPixelSeriesInView)
带有轴标签和拖动功能的序列图元vertical-line
位于序列或窗格后方的图像或装饰元素image-watermark(同时包含序列和窗格图元)
锚定在窗格边缘或角落的文本标签anchored-text(共用一个核心的最精简序列与窗格组合)
窗格上的 DOM 层,多窗格生命周期accessibility

references/official-plugins.md 详细介绍了每个示例的演示内容。 仓库中的 plugin-examples/src/plugins/ (https://github.com/tradingview/lightweight-charts/tree/master/plugin-examples)还包含一组概念验证插件——工具提示、绘图工具、时段高亮、热力图等——适合拓展思路、获取灵感,但它们尚未发布,只是起步示例,不是应当遵循的标准。已正式发布的插件在此处的文件夹会重定向到对应的软件包。

按以下顺序阅读文档

以 Markdown 格式获取页面——在 URL 后附加 .md——而不是抓取 HTML:https://tradingview.github.io/lightweight-charts/docs/plugins/<page>.md。 https://tradingview.github.io/lightweight-charts/llms.txt 是所有页面的索引,docs_map.md 是带标题的同一索引,旁边的 lightweight-charts.d.ts 则是已发布的类型定义。

  1. intro——三种插件类型及其附加方式。
  2. series-primitives 或 pane-primitives——适用于对应类型的视图/渲染器/生命周期约定:attached / detached、updateAllViews、paneViews、priceAxisViews、timeAxisViews、hitTest。
  3. custom_series——ICustomSeriesPaneView:priceValueBuilder、isWhitespace、renderer、update、defaultOptions、5.1 版的合并钩子以及 5.2 版的 hitTest。
  4. canvas-rendering-target——你要在其上绘制的 CanvasRenderingTarget2D 及其两种坐标空间。
  5. pixel-perfect-rendering——为什么宽度和位置要通过 dimensions/* 辅助函数处理。

对于文档未涵盖的内容,请查阅类型定义,而不是生成的 API 参考——类型定义内容完整,而且你的代码正是根据 .d.ts 编译的。文档描述的是当前版本;如果你的对等依赖范围从更早版本开始,则实际可用的是已安装版本中的类型定义。

正确性检查清单

以下每一点都曾给已发布的插件带来问题。此处说明原因,是为了便于在新的场景中识别问题,而不只是避免旧场景中的问题。

绘图
  • 需要清晰显示的内容(线条、柱形、边框)应在 useBitmapCoordinateSpace 中绘制,并按 horizontalPixelRatio / verticalPixelRatio 缩放宽度;文本和布局应使用 useMediaCoordinateSpace,单位为 CSS 像素。在 HiDPI 屏幕上混用两种坐标空间会导致模糊。dimensions/* 辅助函数正是为此返回位图坐标。
  • CanvasRenderingTarget2D 来自 fancy-canvas,而非该库;工具包从 custom-series/renderer-base 重新导出此类型,因此自定义序列无需依赖它。
  • 恢复对上下文所做的任何设置(使用 save/restore,或重置 globalAlpha、lineCap、虚线设置)。下一个渲染器会使用同一个上下文绘制。
  • priceToCoordinate 返回 null 表示价格超出刻度范围。跳过该点或在此处断开线条;若绘制到 NaN,整条路径都会丢失。
可见范围与坐标
  • 只绘制 data.visibleRange;一个序列可能包含百万个点。对于线图和面积图,使用 extendRange 将范围向两端各扩展一根柱,这样首尾线段会延伸出窗格,而不是停在边缘。
  • 可见范围之外的柱可能带有过期或缺失的 x。使用 barCoordinate(bar, anchor, barSpacing) 从可见锚点重新计算坐标——bar.time 是逻辑索引,间距按索引计算。
  • 只要另一个序列更早开始,或数据存在间隙,bar.time(逻辑索引)就会与数组索引 i 不同。按索引查询选项和画笔范围时,使用逻辑索引。
自定义序列须知
  • priceValueBuilder 返回图表用于自动缩放的绘图值,并将最后一个值作为当前值(价格线、最后值标签)。对于堆叠图,返回 [min, max, total](stackedPlotValues);对于带状图,返回极值;对于单个值,返回 [value]。
  • 它会在 setData 时运行,早于任何渲染,因此普通视图此时还没有选项——图表尚未调用 update——首次导入数据时会忽略 percent、base 或 scaleMode。createOptionsAwareSeries 会为构建器提供实时选项 getter,并在命名选项变化时重新导入数据;因此,官方序列将 createXSeries 工厂作为受支持的入口,并保留导出的类以供组合使用。
  • 宿主不会向自定义渲染器提供空白数据:柱形只会跳过间隙,逻辑索引的跳跃也可能只是来自其他序列的时间戳。仅在序列自身的空白数据处断开线条,判断依据是序列接受的输入:createWhitespaceSeries 提供了一个 GapCheck;将 getConflationFactor(data) 作为其 minGap 传入,这样宽度小于一个合并桶的空白区段会被吸收,而不会将每个桶都拆成单点线段。
  • conflationReducer 和 conflationFactor 从 5.1 开始提供,渲染器的 hitTest 从 5.2 开始提供:序列命中测试(通过 isHovered / hitTestData 报告悬停的自定义序列,draw 参数从 5.0 开始提供)也随之加入。将它们实现为可选扩展,除非确实需要,否则保持 peer 范围为 ^5.0.0,并在 README 中说明。
  • 当同一项持续处于悬停状态时,hitTest 必须返回同一个 hitTestData 对象:图表会按引用比较该对象;若每次都返回新对象,指针每移动一次,窗格就会重绘一次。
  • conflationReducer(a, b) 必须返回属于你自己的类型、且能合理表示两项合并结果的数据项:堆叠值求和,OHLC 取最高价/最低价/最后值,汇率则取较晚的值。
基元细节
  • 每当图表无法自行察觉更改时(例如选项、时间更新或已解码的图片),都要调用 requestUpdate()。图表不会轮询你的状态。
  • detached() 必须撤销 attached() 执行的所有操作:监听器、DOM 节点、计时器和待处理的图片加载。附加和分离操作很常见(例如 React effect、窗格重排),泄漏会导致出现幽灵绘图和抛出“已释放”错误。
  • 窗格基元只会绘制在其所附加的窗格上;传给构造函数的窗格索引只是一个提示,可能不准确。应延迟解析实际窗格(首次绘制时,context.canvas 位于该窗格内),并按对象身份持有它——添加、移除或移动窗格时,索引会发生变化。
  • 对于 DOM 覆盖层,请将其附加到 paneContentElement(pane),并确保覆盖层的布局独立于图表自身的表格单元格。

测试

分为三个层次,每个层次都能发现其他层次无法发现的问题。官方包在 tests/ 下都包含这三层;脚手架会搭建第一层。

  • 单元测试(tests/unit/*.spec.ts,node:test + chai):纯函数测试,例如堆叠计算、选项合并、数据填充和间隙检测。运行速度快,可通过仓库的 pnpm test 运行。
  • 图形测试(tests/graphics/*.js):绘制一种状态的页面;CI 会将截图与前一个构建版本进行比较。每个值得展示的选项都应对应一个用例,此外还应覆盖边缘情况:可见范围为空、内容已滚出视野、一段空白数据、数据合并、反转刻度。
  • 交互测试(tests/interactions/*.js):页面会通过异步的 beforeInteractions(container) 驱动图表,并在结果不符合预期时抛出错误——包括“是否已绘制”的像素采样、DOM 检查和命中测试。空白数据、更新、pop 和指针行为都在此处测试。

在上游运行测试:先运行 pnpm build:prod 和 pnpm --filter "@tradingview/lwc-plugin-*" build,然后运行 pnpm test、pnpm e2e:graphics:plugins 和 GREP="lwc-plugin-<name>/" pnpm e2e:interactions。

发布和目录

在上游通过 pnpm plugins:validate 后,插件即可发布;也可以手动确认以下条件:名称采用 lwc-plugin-* 格式,设置 publishConfig.access: public,包含 lightweight-charts 同级依赖版本范围、lightweight-charts-plugin 关键字、完整的 lwcPlugin 块、含有 ## Installation(包含 ### npm 和 ### CDN 选项卡)及 ## Usage 的 README,以及 README 声明的演示页面。脚手架已满足所有条件。文档网站的插件目录会呈现 README,并为基于软件包构建的预览页面创建框架;references/package-contract.md 列出了相关字段、页面以及首个版本的变更日志规范(简短即可:列出 Added,不写 Fixed——因为目前还没有任何需要修复的问题)。

答复规范

协助开发插件时:

  1. 用一句话说明插件类型及其原因,或解释用户的想法如果采用另一种类型会失去什么。
  2. 指出所依据的工具包模块和官方插件。引用其模式前,先阅读该插件的文件。
  3. 根据本地类型定义核实每个库和工具包的名称;说明哪些名称无法核实。
  4. 编写脚手架自身脚本能够通过的代码:pnpm typecheck、pnpm build,并且可通过 pnpm dev 在演示页面中查看。
  5. 如果更改涉及自动缩放、空白数据、可见范围或悬停,请指出能够发现回归的测试,并编写该测试。

评论

支持 Markdown