Skip to content

Latest commit

 

History

History
476 lines (356 loc) · 15.7 KB

File metadata and controls

476 lines (356 loc) · 15.7 KB

API 参考 — @ioai/rosview

English: API.md

本文档描述 @ioai/rosview v1.2.0 的完整公开 API。

在使用该包前,请先在宿主应用安装 peer 依赖:reactreact-domthree@react-three/fiber@react-three/drei


目录

  1. RosViewer
  2. RosViewProvider
  3. TypeScript 类型
  4. 偏好设置工具
  5. 数据集工具
  6. 布局工具
  7. 扩展 API
  8. MessagePipeline Hook
  9. URL 参数(SPA)

RosViewer

主嵌入组件,渲染完整查看器 UI(导航栏、面板、播放控制)。

import { RosViewer } from '@ioai/rosview';
import '@ioai/rosview/style.css';

Props

数据源

Prop 类型 默认值 说明
url string 单个远程文件 URL(HTTP/HTTPS)。大文件支持 HTTP Range。
urls string[] 多个远程文件 URL;每个在侧边栏中作为可选数据集。
file File 单个本地 File(例如来自 <input type="file">)。
files File[] 多个本地 File
fileManifest string | FileListItem[] 远程 manifest 的 JSON URL FileListItem[] 行数组。与 url/urls 合并;拉取错误会记录日志且不阻塞其他源。独立 SPA 不会从查询串读取 manifest,请在嵌入时使用此 prop。

所有数据源类 prop 可组合使用;重复项会自动去重。展示顺序上文件优先于 URL。

外观

Prop 类型 默认值 说明
theme 'light' | 'dark' | 'system' 'system' 配色主题;'system' 跟随系统。
language 'en' | 'zh' | 'ja' 'en' UI 语言。
className string 应用到最外层容器的 CSS class。
style React.CSSProperties 应用到最外层容器的内联样式。

持久化

Prop 类型 默认值 说明
preferencePersistence 'localStorage' | 'off' 'localStorage' 控制主题、语言、侧栏宽度等 UI 偏好;'off' 表示不自动写入 localStorage。
layoutPersistence 'localStorage' | 'off' | 'inherit' 'inherit' 面板布局持久化;'inherit' 跟随 preferencePersistence'off' 不读写 ioai.rosview.layout
layoutStorageKey string 'ioai.rosview.layout' 布局 localStorage 键,供多 embed 实例隔离。
urlState 'spa' | 'off' 'off' 'spa':与独立 SPA 一致,用 history.pushState 同步地址栏 ?url=;加载时从 IndexedDB 最近打开 恢复 file:// / folder://,或通过示例清单解析 sample://'off':npm 嵌入默认 — 不写地址栏,上述自定义定位符不会自动恢复。

嵌入 / 工具模式(v1.2.0)

Prop 类型 默认值 说明
mode 'viewer' | 'tool' 'viewer' 'tool':无 MCAP 也可进入工作区(内部使用 MinimalPlayer),默认 panels-only chrome。
requireSource boolean mode !== 'tool' false 时无 url/file 仍挂载面板区。
chrome 'full' | 'minimal' | 'panels-only' mode Chrome 预设;可被下方细粒度 props 覆盖。
showNavbar / showSidebar / showPlaybackBar boolean chrome 显式控制 Navbar、侧栏、播放条。
hideOpenFileMenus boolean false 隐藏 Navbar 打开文件菜单并禁用录制文件拖放。
initialLayout FoxgloveLayoutData mount 时优先于 localStorage 的布局。
defaultPanel OpenPanelInput 单面板语法糖(与 initialLayout 二选一,后者优先)。
suppressWelcomePanel boolean mode === 'tool' 跳过 Dockview Welcome 占位面板。

事件回调

Prop 类型 说明
onFatalError (error: Error) => void 文件加载失败且无可用回退时调用。
onThemeChange (theme: 'light' | 'dark' | 'system') => void 用户通过导航栏切换主题时调用。
onLanguageChange (language: 'en' | 'zh' | 'ja') => void 用户通过导航栏切换语言时调用。
onPlayerReady ({ player, hasSource }) => void 播放器 presence 首次变为 ready 时调用。
onLayoutReady ({ panelCount }) => void Dockview 布局 hydration 完成时调用。
onSourceLoadingChange (loading: boolean) => void 远程/本地源正在初始化时通知宿主。
extensions RosViewExtension[] 可选宿主扩展:侧边栏 Tab、播放条/时间轴叠加层等。
hostContext unknown 不透明上下文,原样出现在 context.hostContext(如数据集 id、权限标记)。

最小示例

<RosViewer url="https://cdn.example.com/recording.mcap" />

受控主题 + 语言

const [theme, setTheme] = React.useState<'light' | 'dark' | 'system'>('dark');
const [lang, setLang] = React.useState<'en' | 'zh' | 'ja'>('en');

<RosViewer
  url="https://cdn.example.com/recording.mcap"
  theme={theme}
  language={lang}
  preferencePersistence="off"
  onThemeChange={setTheme}
  onLanguageChange={setLang}
/>

工具模式:单 UrdfDebug 面板(无 MCAP)

import {
  RosViewer,
  createSinglePanelLayout,
} from '@ioai/rosview';
import '@ioai/rosview/style.css';

<RosViewer
  mode="tool"
  preferencePersistence="off"
  layoutPersistence="off"
  initialLayout={createSinglePanelLayout({ type: 'UrdfDebug', id: 'UrdfDebug!embed' })}
  theme="dark"
  language="zh"
/>

RosViewProvider

为主题上下文与 i18n 的 IntlProvider 提供封装,便于在 ROSView 主题下渲染自定义 UI。

import { RosViewProvider, useRosViewTheme } from '@ioai/rosview';

RosViewProviderProps

Prop 类型 默认值 说明
theme 'light' | 'dark' | 'system' 'system' 子组件主题。
language 'en' | 'zh' | 'ja' 'en' react-intl 消息语言。
children React.ReactNode (必填) 子节点。

useRosViewTheme

const { theme, resolvedTheme } = useRosViewTheme();
// theme: 'light' | 'dark' | 'system'
// resolvedTheme: 'light' | 'dark'  (system 已解析为实际值)

TypeScript 类型

RosViewLanguageCode

type RosViewLanguageCode = 'en' | 'zh' | 'ja';

RosViewUiTheme

type RosViewUiTheme = 'light' | 'dark' | 'system';

RosViewPersistedTheme

type RosViewPersistedTheme = 'light' | 'dark';  // 'system' 不会持久化

PreferencePersistence

type PreferencePersistence = 'localStorage' | 'off';

DatasetItem

表示已规范化、供播放器使用的单个数据源(文件或 URL)。

interface DatasetItem {
  id: string;
  kind: 'file' | 'url';
  name: string;
  file?: File;
  url?: string;
  sizeBytes?: number;
  durationSec?: number;
  topicCount?: number;
}

FileListItem

远程 manifest JSON 中的一行。

interface FileListItem {
  url: string;
  name?: string;
  sizeBytes?: number;
  durationSec?: number;
  topicCount?: number;
}

Time

ROS 兼容时间戳(纳秒精度)。

interface Time {
  sec: number;
  nsec: number;
}

MessageEvent

播放器解码后的一条消息。

interface MessageEvent<T = unknown> {
  topic: string;
  receiveTime: Time;
  message: T;
  sizeInBytes: number;
  schemaName?: string;
}

PlayerPresence

type PlayerPresence = 'preinit' | 'initializing' | 'ready' | 'closed';

偏好设置工具

用于在宿主侧管理偏好(preferencePersistence="off" 时)。

import { readPreferences, writePreferences } from '@ioai/rosview';
import { ROS_VIEW_PREFERENCES_STORAGE_KEY, ROS_VIEW_LAYOUT_STORAGE_KEY } from '@ioai/rosview';

readPreferences()

返回已保存的偏好对象;若无则 null

const prefs = readPreferences();
// { theme: 'dark', language: 'zh', ... } | null

writePreferences(patch)

将部分字段合并写入已存储的偏好。

writePreferences({ theme: 'dark', language: 'en' });

存储键

常量 内容
ROS_VIEW_PREFERENCES_STORAGE_KEY 'ioai.rosview.prefs' UI 偏好 JSON
ROS_VIEW_LAYOUT_STORAGE_KEY 'ioai.rosview.layout' 面板布局 JSON

数据集工具

import { parseRemoteDatasetListJson, datasetItemsFromListItems } from '@ioai/rosview';

parseRemoteDatasetListJson(json)

fetch 得到的原始 JSON 解析为 FileListItem[];无效行会被跳过。

const res = await fetch('/manifest.json');
const items: FileListItem[] = parseRemoteDatasetListJson(await res.json());

datasetItemsFromListItems(items)

FileListItem[] 转为 DatasetItem[]RosViewer 内部使用的格式)。

const datasets = datasetItemsFromListItems(items);
<RosViewer files={[]} fileManifest={items} />

布局工具

布局持久化

import {
  readSavedDockviewLayout,
  saveDockviewLayoutToStorage,
  clearSavedDockviewLayout,
} from '@ioai/rosview';
函数 说明
readSavedDockviewLayout() 从 localStorage 读取已保存布局;返回 FoxgloveLayoutData | null
saveDockviewLayoutToStorage(layout) 将面板布局写入 localStorage。
clearSavedDockviewLayout() 清除已保存的面板布局。

Foxglove 布局互操作

import { importFoxgloveLayout, buildFoxgloveLayout, parseFoxgloveLayout } from '@ioai/rosview';
import { exportDockviewLayout, importDockviewLayout, openDockviewPanel } from '@ioai/rosview';
函数 说明
parseFoxgloveLayout(raw) 将原始 Foxglove 布局 JSON 解析为 FoxgloveLayoutData
importFoxgloveLayout(data, options?) FoxgloveLayoutData 转为 DockView 兼容布局并应用;返回 ImportFoxgloveLayoutResult
buildFoxgloveLayout(input) 由面板配置对象构建 FoxgloveLayoutData
exportDockviewLayout() 导出当前面板布局为 FoxgloveLayoutData;无活动布局时返回 null
importDockviewLayout(layout) 将先前导出的布局应用到当前 DockView。
openDockviewPanel(input) 按类型程序化打开面板;返回新面板 ID 或 null
createSinglePanelLayout(input) OpenPanelInput 构建单面板 FoxgloveLayoutData
MinimalPlayer 无录制源时的 stub Player(工具模式内部使用,也可供高级宿主直接使用)。

扩展 API

通过向 RosViewer 传入 extensions 扩展 @ioai/rosview

import type {
  RosViewExtension,
  RosViewExtensionContext,
  SidebarTabContribution,
  PlaybackOverlayContribution,
  PlaybackControlsApi,
  PlaybackSnapshot,
} from '@ioai/rosview';

核心类型

类型 说明
RosViewExtension 一个扩展包;可含 sidebarTabsplaybackOverlaystimelineOverlays(与前者同形,排在 playbackOverlays 之后合并)。
SidebarTabContribution 注册侧边栏 Tab(idtitle、可选 iconorderrender(context))。
PlaybackOverlayContribution 在播放条上方注册一块区域(id、可选 orderheightrender(context))。
RosViewExtensionContext 传给扩展渲染器的稳定上下文(含 playbacktimelinemessageshostContext)。
PlaybackControlsApi 播放控制:seekplaypausesetSpeedsetLoopingstepBystepMessageplayUntilsubscribeCurrentTimegetCurrentTimegetSnapshot
PlaybackSnapshot 低频播放状态快照;包含 currentTimestartTimeendTimeisPlayingspeed,可含 progressPercentbufferingproblems 等。
TimelineApi 与主 scrubber 对齐:getTimeBoundstimeToPercentpercentToTime
MessageAccessApi 只读 getMessagesInTimeRange(播放器支持时)。

示例

import { RosViewer } from '@ioai/rosview';
import type { RosViewExtension } from '@ioai/rosview';

const annotationExtension: RosViewExtension = {
  id: 'my-annotation-tool',
  sidebarTabs: [
    {
      id: 'annotations',
      title: 'Annotations',
      order: 20,
      render: ({ playback }) => (
        <MyAnnotationSidebar
          getPlaybackSnapshot={playback.getSnapshot}
          onSeek={playback.seek}
        />
      ),
    },
  ],
  playbackOverlays: [
    {
      id: 'annotation-ranges',
      order: 10,
      render: ({ playback }) => (
        <MyAnnotationTimeline
          getPlaybackSnapshot={playback.getSnapshot}
          subscribeCurrentTime={playback.subscribeCurrentTime}
          onSeek={playback.seek}
        />
      ),
    },
  ],
};

<RosViewer url="/demo.mcap" extensions={[annotationExtension]} />;

最佳实践

  • 高频视觉更新优先用 playback.subscribeCurrentTime(),避免每帧 setState
  • 一次性读取实时播放头时用 playback.getCurrentTime()
  • 低频状态检查(如 isPlayingstartTimeendTime)用 playback.getSnapshot();其中的 currentTime 是兼容快照,不适合作为 React 驱动的实时播放头来源。
  • 扩展渲染器应容错;运行时错误与核心播放控制隔离。

MessagePipeline Hook

高级用法:在查看器内嵌的自定义 React 组件中订阅播放状态与解码消息。

useMessagePipeline 适合订阅低频元数据,例如 presence、topics、bounds、progress、speed 和解码消息可用性。实时播放 UI 不应依赖 playerState.activeData.currentTime;请使用 playback.subscribeCurrentTime()playback.getCurrentTime()

import { useMessagePipeline } from '@ioai/rosview';
function MyCustomPanel() {
  const presence = useMessagePipeline(s => s.playerState.presence);
  const topics = useMessagePipeline(s => s.sortedTopics);

  if (presence !== 'ready') return <div>Loading…</div>;

  return (
    <ul>
      {topics.map(t => <li key={t.name}>{t.name} ({t.schemaName})</li>)}
    </ul>
  );
}

选择器接收 MessagePipelineState,应返回稳定(可记忆化)的值,以减少不必要重渲染。


URL 参数(SPA)

rosview.com 或自托管 SPA 中,地址栏使用单一数据源参数 url(以及可选的 UI 参数)。打开或切换数据源会通过 history.pushState 更新 ?url=,刷新后按该参数恢复。

参数 示例值 说明
url https://cdn.example.com/recording.mcap 远程录制(HTTP(S) 或同源路径,如 /examples/run.mcap)。
url file://test.mcap 本地文件:按显示名匹配 最近打开 中最近一条可重放的 FileSystemFileHandle 记录(重名时取最近)。
url folder://dataset 本地文件夹:按文件夹名匹配 最近打开 中最近一条可重放的目录句柄。
url sample://franka_stack 内置示例:用 id 在构建时配置的 JSON 清单中解析(VITE_SAMPLE_DATASETS_MANIFEST_URLVITE_SAMPLES_BASE_URL,见 src/services/sampleDatasets.ts)。
theme dark 初始主题(light / dark / system)。
language zh 初始语言(en / zh / ja)。

此处的 file:// / folder:// / sample:// 为应用自定义定位符,不是浏览器原生的 file: URL。

多远程文件或远程 JSON manifest 请在集成 @ioai/rosview 时使用 React 的 urls / fileManifest props;独立 SPA 的查询串只跟踪单个 url

示例深链:

https://rosview.com?url=https://cdn.example.com/run1.mcap&theme=dark&language=en