@axhub/annotation
v1.0.18
Published
Read-only annotation runtime for Axhub prototype pages.
Downloads
3,090
Maintainers
Readme
@axhub/annotation
@axhub/annotation 是一个面向原型页面的只读标注展示包,负责加载标注数据并用页面 marker 与点击后的标注气泡渲染出来。
它可以脱离 axhub-make 单独集成到宿主页面:React 18 项目使用 AnnotationViewer 或 createAnnotationViewer 的 ESM 入口;普通 HTML、React 17、Vue、Angular 或托管注入场景使用包内自带的 standalone browser bundle。
功能边界
适合:
- 已经有标注数据,希望在页面运行时展示
- 需要同时支持 React 组件接入和实例化接入
- 需要一套统一的 JSON 数据源,同时兼容不同加载方式
不适合:
- 浏览器内编辑、保存、回写标注
- 扫描页面自动生成标注数据
- 把 editor / devtools 能力当成稳定公开接口
快速开始
最小 React 示例,直接把同一份 JSON 导入到 TS:
import { AnnotationViewer, type AnnotationSourceDocument } from '@axhub/annotation';
import annotationSource from './annotation-source.json';
const source = annotationSource as AnnotationSourceDocument;
export function DemoPage() {
return (
<AnnotationViewer
source={source}
defaultVisible
/>
);
}如果宿主需要延迟组装数据,可以传函数:
import { AnnotationViewer, type AnnotationSourceDocument } from '@axhub/annotation';
import annotationSource from './annotation-source.json';
function loadSource(): AnnotationSourceDocument {
return annotationSource as AnnotationSourceDocument;
}
export function DemoPage() {
return <AnnotationViewer source={loadSource} defaultVisible />;
}本地演示
包内提供两个可预览示例,统一放在 examples/:
examples/react:React 宿主示例,使用AnnotationViewer组件接入。examples/html:普通 HTML 宿主示例,页面主体用原生 DOM,通过window.AxhubAnnotation.createAnnotationViewer接入自托管 browser bundle。
启动命令:
pnpm examples:react
pnpm examples:html两个示例共享 examples/shared/annotation-source.json,覆盖 Markdown 标注、短标注、多颜色筛选、同一节点多条标注、状态标注 controls、原型目录 route/markdown/link 等能力。
ESM 运行时依赖:
- 宿主提供 React 18
- 宿主提供 ReactDOM 18
- Ant Design 运行时样式与组件依赖由
@axhub/annotation包内打包携带 - 页面里能通过
ElementLocator找到目标元素
standalone browser bundle 不要求宿主提供 React、ReactDOM 或 antd。pnpm build 会生成 dist/browser/axhub-annotation.global.js 和 sourcemap,用户可以把该文件从 npm 包中复制到自己的静态资源服务器、内网 CDN、OSS 或任意 web server 后用普通 <script> 注入。
<script src="/static/vendor/axhub-annotation/axhub-annotation.global.js"></script>
<script>
const viewer = window.AxhubAnnotation.createAnnotationViewer({
source: () => fetch('/annotation-source.json').then((response) => response.json()),
});
void viewer.start();
</script>推荐接入方式
推荐只维护一套 JSON 数据源,推荐 wire format 为 AnnotationSourceDocument。运行时不再按 URL 请求 JSON;请把 annotation-source.json 通过静态 import 或宿主内部函数组装进页面代码。
推荐目录结构:
demo-page/
├── index.tsx
├── annotation-source.json
└── assets/
├── hero-card.png
└── order-table.png推荐 JSON 示例:
{
"documentVersion": 1,
"format": "axhub-annotation-source",
"presentation": {
"layerSelectors": [".example-modal-layer[data-open=\"true\"]"]
},
"data": {
"version": 2,
"prototypeName": "ref-app-home",
"pageId": "home",
"updatedAt": 1710000000000,
"nodes": [
{
"id": "hero-card",
"index": 1,
"title": "Hero Card",
"locator": {
"selectors": ["[data-testid=\"hero-card\"]"],
"fingerprint": "div|hero-card",
"path": [0, 1, 0]
},
"aiPrompt": "说明首屏卡片的结构和状态",
"annotationText": "",
"hasMarkdown": true,
"color": "#1677ff",
"images": [],
"createdAt": 1710000000000,
"updatedAt": 1710000000000
}
]
},
"markdownMap": {
"hero-card": "# Hero Card\n\n这里放该节点的详细说明。"
},
"assetMap": {},
"directory": {
"nodes": [
{
"type": "folder",
"id": "guide",
"title": "演示目录",
"defaultExpanded": true,
"children": [
{
"type": "route",
"id": "route-empty",
"title": "切换缺省态",
"route": "orders:empty",
"payload": { "status": "empty" }
},
{
"type": "link",
"id": "external-doc",
"title": "外部文档",
"href": "https://example.com/docs",
"target": "blank"
},
{
"type": "markdown",
"id": "readme",
"title": "目录说明",
"markdown": "# 目录说明\n\n这里会在目录左侧阅读面板中展示。"
}
]
}
]
}
}presentation.layerSelectors 可声明当前页面的前景层根节点;打开层存在时,仅展示该层内部目标的 marker 和已选标注气泡。选择器应匹配包含弹窗内容的活动容器。
AnnotationNode 关键字段建议这样理解:
id:节点稳定标识,建议长期不变annotationText:短标注,仅用于hasMarkdown: false的节点hasMarkdown:是否使用 Markdown 文档作为唯一标注正文title:节点标题,建议直接写入数据源,不依赖运行时补全
JSON 目录
directory.nodes 用于驱动“原型目录”面板。目录支持四类节点:
folder:多级文件夹,defaultExpanded控制初始展开。route:点击时调用options.onDirectoryRoute(node),运行时不强制跳转。link:target: "blank"新窗口打开,target: "self"当前页打开。markdown:使用内联markdown,点击后在目录左侧打开阅读面板。
markdownPath 可以作为 Make 原型的源文件约定,例如 docs/prd-03-status.md,但 @axhub/annotation 运行时只消费已经内联好的 markdown 正文,不会按路径 fetch .md 文件,也不会内置文档编辑按钮。Make 的文档编辑入口由批注工具按当前选中的目录 Markdown 正文元素注入到批注气泡卡片。
路由节点的推荐做法是让宿主自行决定行为:可以切 SPA 路由、改 URL、切换页面状态,或替换数据源展示缺省/异常/活动态。
controls:节点关联的属性调整项,属于标注内容的一部分markdownMap[nodeId]:Markdown 标注正文,仅用于hasMarkdown: true的节点images:节点引用的资源文件名,仅用于hasMarkdown: false的节点assetMap[filename]:资源文件名到 URL 的映射
hasMarkdown: true 与短标注/图片展示互斥:运行时会直接显示 markdownMap[nodeId] 的内容,不再显示 annotationText,也不会展示该节点的 images。需要配图时请把图片语法写进 Markdown 正文。
更详细的目录、字段和加载说明见 推荐 JSON 加载。
全局数据快照
运行时加载成功后,会在页面全局输出 window.__AXHUB_ANNOTATION_SOURCE__:
interface AnnotationSourceRuntimeSnapshot {
directory: AnnotationDirectory | null;
nodes: AnnotationNode[];
presentation?: AnnotationSourcePresentation;
source?: {
root?: string;
manifest?: string;
};
}这个快照用于给宿主、调试工具或 Agent 读取当前原型目录和全部标注节点。它会在 viewer 停止、卸载或没有可展示内容时清除;消费方应把它当作只读数据,不要依赖修改该对象来影响运行时。
当发布 HTML 勾选“带源码”且页面实际使用了 @axhub/annotation 时,发布产物会注入 sourceReference,快照会额外包含 source,其中 manifest 指向发布包内的 source/manifest.json。普通宿主不需要手写这个字段。
运行时控制器与侧边栏注入
viewer 启动后会同时暴露 window.__AXHUB_ANNOTATION_RUNTIME__,用于宿主或托管页注入脚本在页面运行后读取运行时状态、调整工具栏配置,并向侧边栏注入自定义动作。
const runtime = window.__AXHUB_ANNOTATION_RUNTIME__;
const action = document.querySelector('#my-annotation-action');
if (action) {
runtime?.configure({
toolbarAutoHide: false,
toolbarActions: {
title: '安全提示',
content: action,
},
});
}自定义动作固定显示在侧边栏“标注配置”按钮之后、“设置”按钮之前。toolbarActions 只接受 { title?, content } 或数组;title 可选,传入时运行时统一套和“原型目录 / 设置”一致的 Tooltip,不传时只渲染 content。content 可以是 DOM Element 或 React 节点,宿主自己负责图标、点击和业务弹窗。React 宿主如果需要根据当前页面或标注数量渲染,可继续使用 AnnotationViewerOptions.renderToolbarActions(context) 或全局 configure({ renderToolbarActions })。
runtime.metadata / runtime.getMetadata() 会返回 packageName、runtimeVersion、features、当前页面、当前页标注数量、侧边栏显示状态与是否已注册自定义动作,可用于发布前检测或注入脚本识别。该控制器只提供通用运行时协议;Axhub 托管安全提示、举报、业务 URL、埋点和弹窗内容应由宿主或注入脚本自行实现。
批注适配约定
标注运行时使用 #__axhub_annotation_host__ 的 open Shadow Root。为了让 axhub-genie-editor 可以稳定批注标注组件内部内容,标注组件会在明确可批注的内部元素上声明:
data-axhub-annotation-comment-target="true"当前声明范围包括目录节点按钮、目录阅读器的正文 Markdown、目录阅读器右侧 TOC 按钮、标注气泡正文 Markdown。批注侧只在 #__axhub_annotation_host__ 的 Shadow 命中栈内识别这个属性;如果命中的是被声明元素的子节点,会回到最近的声明祖先。不要依赖批注侧按尺寸、样式或 DOM 层级猜测具体目标。
兼容接入方式
当前包仍兼容现有对象直传和 AnnotationStorage 模式。
legacy AnnotationStorage 示例:
import { createAnnotationViewer, type AnnotationStorage } from '@axhub/annotation';
const storage: AnnotationStorage = {
load: async () => null,
loadAnnotationsMd: async () => null,
loadMarkdown: async (_nodeId) => null,
getAssetUrl: (filename) => `/runtime/assets/${filename}`,
};
const viewer = createAnnotationViewer({
prototypeName: 'ref-app-home',
pageId: 'home',
storage,
});
void viewer.start();兼容模式仍可继续使用,但不再是主推荐路径。字段映射和迁移方式见 Legacy MD / Storage 兼容说明。
公开接口导航
当前“只读标注能力”建议直接使用这些根入口接口:
AnnotationViewercreateAnnotationViewerAnnotationSourceInputAnnotationSourceAnnotationSourceDocumentAnnotationDataAnnotationNodeAnnotationSourceRuntimeSnapshotAnnotationRuntimeControllerAnnotationRuntimeMetadataAnnotationRuntimeConfigureOptionsAnnotationToolbarActionItemAnnotationToolbarActionsContentAnnotationToolbarActionsRendererAnnotationToolbarActionsContextAnnotationViewerOptionsAnnotationViewerApiElementLocatorparseAnnotationsMarkdownparseAnnotationMarkdownRecordsserializeAnnotationsMarkdownbuildAnnotationPromptAnnotationStorage
详细签名和最小示例见 API Reference。
