@devicechain/widget-sdk

中文文档
1.4.0发布于 2026/11/12Apache-2.0TypeScriptES Module零依赖构建来源已验证近 30 天下载 4907

The DeviceChain widget SDK: the typed contract for writing a dashboard widget as a React component, and the sealed registry that makes it available to a host. Declare a widget once (portable descriptor plus trusted component) and register it beside the bu

原始使用文档
文档目录5 节
  1. What is in the box
  2. Declare a widget
  3. Register it
  4. Offering a download from the frame
  5. Upgrading from 0.21

@devicechain/widget-sdk

The typed contract for writing a DeviceChain dashboard widget as a React component, and the sealed registry that makes it available to a host application. The built-in widgets are declared through exactly this interface.

npm install @devicechain/widget-sdk @devicechain/dashboards react

@devicechain/dashboards and react are peer dependencies: the SDK defines the React contexts a widget reads (selection, candidate lookup, time zone, theme root, time range, density, freshness), and a context is identified by the object that created it, so every widget package and the host must resolve the same single copy of this package. Declare it as a peer in your own widget package too, never as a dependency.

What is in the box

The root entry is chart-free and map-free: the card frame (WidgetFrame), theme-variable references (css), formatters, the change-flash cue, option readers (optString, optNumber, ...) and the React hooks that bind a hub channel to widget state. A widget that only draws DOM imports from here and pulls in neither Apache ECharts nor MapLibre nor the built-in widgets.

A stream hook never returns one subscription's snapshot for another. Since 0.22, useAlarmStream and useLocationStream return loading: true with no rows from the very render whose subscription changed (a re-pointed slot, a new filter), until the new subscription answers. Before 0.22 that one render still carried the previous subscription's alarms or positions.

Chart helpers live on a separate entry, @devicechain/widget-sdk/echarts: the EChart wrapper, buildLineOption / buildGaugeOption and the chart colour theme. echarts is an optional peer dependency, needed only by a widget that imports that entry.

The chart engine itself is not part of that entry's static imports. EChart loads it on demand, as one chunk, the first time a chart mounts, so a host bundler keeps ECharts out of the first download. Until the engine arrives, the chart's box is empty. If the engine cannot be fetched, EChart throws the error to the nearest error boundary, and the next mount fetches it again. A host that wants charts drawn on its first frame awaits loadChartEngine() before rendering. A widget that draws a chart can set MyWidget.preload = loadChartEngine on its component, and then preloadWidgets from @devicechain/widgets fetches the engine for any board that contains it.

Declare a widget

import { defineWidget } from '@devicechain/widget-sdk';

export const tankLevelWidget = defineWidget({
  descriptor: {
    id: 'acme/tank-level',
    version: '1.0.0',
    sdkApiVersion: 1,
    optionsVersion: 1,
    display: { title: 'Tank level', category: 'Telemetry' },
    channel: 'measurement',
    datasource: { mode: 'required', selectors: ['device', 'slot'] },
    binding: { window: { option: 'window', min: 1 } },
    options: {
      unit: { kind: 'string', doc: 'Unit shown beside the level.' },
      window: { kind: 'number', integer: true, min: 1, doc: 'Samples retained.' },
    },
    defaults: { unit: '%' },
    interactions: {},
  },
  implementation: {
    kind: 'trusted-react',
    // `data` is typed from the channel: a measurement widget receives measurement data.
    component: ({ data }) => <span>{Object.values(data.latest)[0]?.value}</span>,
  },
});

The descriptor is plain JSON-serializable data: no component, callback or URL. A descriptor that disagrees with its channel, or whose defaults or bindings disagree with its option schema, fails type checking, and createWidgetRegistry checks the same rules again at runtime.

Register it

import { createWidgetRegistry } from '@devicechain/widget-sdk';

const registry = createWidgetRegistry([tankLevelWidget]);

A registry is validated and sealed when it is created: duplicate ids, malformed descriptors, an unsupported sdkApiVersion and invalid bindings or defaults are all reported together in one WidgetRegistryError. There is no global registration; two registries on one page are independent. Extension ids are namespaced (scope/name); the un-namespaced ids are reserved for the built-in widgets.

Trusted widget code runs with the host page's privileges. The declarations in a descriptor shape the API; they are not a security boundary.

Offering a download from the frame

A widget can let the viewer save the data it shows. Pass exportRows to WidgetFrame: a function that returns a WidgetExportTable (a file name without its extension, headings, and rows of strings, numbers, booleans or nulls), or a promise of one. The frame then draws a download button in its title bar, and Download CSV calls your function when the viewer picks it, so the table is built on demand from what the widget is showing. The prop is optional and feature-detected: a widget that passes none is unchanged, and the frame draws no button when its title bar is hidden.

The CSV encoder is the frame's: RFC 4180 quoting, a leading apostrophe on a text cell that begins with =, +, -, @, tab or carriage return (so a spreadsheet never runs it as a formula), and UTF-8 with a byte-order mark so Excel reads units such as °C. Put the unit in the heading, write instants as ISO 8601 with a zone, and say in the file name what the file covers (the span and the bucket on screen). A chart drawn with <EChart> also gets Download image (PNG) with no code of yours.

Upgrading from 0.21

0.22 removes no export. A TypeScript build can notice three changes:

  • ChartTheme has a required severity field. This matters only if you build a theme by hand instead of calling useChartTheme or resolveChartTheme.
  • The unions WidgetChannel (now with 'aggregate'), WidgetId, BuiltinWidgetId, ThemeVar and OptionSpec (now with ListOptionSpec) gained members, so an exhaustive switch over one of them needs a new case.
  • formatValue with no precision rounds to 3 decimals, or 4 significant digits below 1, where it used to print the number unrounded.

Additions, which need no change:

  • WidgetFrame takes flush, which removes the content padding for a widget that fills its box edge to edge, and sampleTime, the time of the one sample a single-value widget shows, so its age readout ages that sample rather than the newest one the host holds.
  • useElementSize takes an optional step that rounds each dimension down to a multiple of it, so a caller that needs only a coarse size does not re-render for every pixel.
  • New exports for size and density tokens (fontVar, spaceVar, tokenStyle, useWidgetScale, fitValueFont), sample age (useFreshness, Freshness, StaleBadge, formatAge), alarm severity (SEVERITY_LEVELS, severityVar, useSeverityColors), thresholds (parseThresholds, thresholdBands, bandAt), the board's time range (useWidgetTimeRange) and the hub connection (useHubConnectionState, HubConnectionBadge).

SDK_API_VERSION is unchanged. See If you build against the npm packages in the release's upgrade guide.

版本与发布记录

@devicechain/widget-sdk 共发布 35 个版本,首次发布于 2026/10/13;过去 12 个月发布了 35 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.4.0latest2026/11/12今天
1.4.0-rc.2next2026/11/11今天
1.4.0-rc.12026/11/111 天前
1.3.02026/11/092 天前
1.3.0-rc.12026/11/092 天前
1.2.02026/11/083 天前

其他发布通道:bootstrapcd → 0.0.0-bootstrap.0,next → 1.4.0-rc.2。

关于 @devicechain/widget-sdk 的常见问题

@devicechain/widget-sdk 是什么?

The DeviceChain widget SDK: the typed contract for writing a dashboard widget as a React component, and the sealed registry that makes it available to a host. Declare a widget once (portable descriptor plus trusted component) and register it beside the bu

如何安装 @devicechain/widget-sdk?

在项目目录运行 npm install @devicechain/widget-sdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.3.0)、@devicechain/dashboards(1.4.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@devicechain/widget-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@devicechain/widget-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@devicechain/widget-sdk 可以商用吗?

@devicechain/widget-sdk 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@devicechain/widget-sdk 的下载量有多少?

根据 npm 官方下载统计,@devicechain/widget-sdk 在 2026/10/12 – 2026/11/10 这 30 天内被下载 4,907 次,日均约 164 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@devicechain/widget-sdk 还在维护吗?

最新版本 1.4.0 发布于 2026/11/12(今天);至今共发布 35 个版本,其中过去 12 个月发布了 35 个;首次发布于 2026/10/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。