{"_id":"@blueyerobotics/multibeam","_rev":"2-5e9c3a7ecab33a8d5bdda9e49c0b37a5","name":"@blueyerobotics/multibeam","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@blueyerobotics/multibeam","version":"0.1.0","keywords":["blueye","multibeam","sonar","webgl","react"],"license":"LGPL-3.0-only","_id":"@blueyerobotics/multibeam@0.1.0","maintainers":[{"name":"juanpi96","email":"juanpi.96.b@gmail.com"},{"name":"arechi","email":"aglimbenli@gmail.com"},{"name":"follesoe","email":"jonas@follesoe.no"}],"homepage":"https://github.com/BluEye-Robotics/p2-django#readme","bugs":{"url":"https://github.com/BluEye-Robotics/p2-django/issues"},"dist":{"shasum":"e6878855fa4842a0e3dadde3040be2e927076def","tarball":"https://registry.npmjs.org/@blueyerobotics/multibeam/-/multibeam-0.1.0.tgz","fileCount":13,"integrity":"sha512-VpK5Un/Az5iU+wy7uad2Mi5P4xK6cmLE1f5XGH7tZIFXfD+j62jshmTWZ4f/saWWdtHO2qRA0KXnSHtdAzc2IQ==","signatures":[{"sig":"MEQCIGAWo9wioiQ6blcqFJmRNX8sOBOuKhrHrAap1GUCE5fiAiAavtz2i90edqXu7LNR6CqsgXVOWRU89eYvbPb1odB/hg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":124500},"type":"module","_from":"file:blueyerobotics-multibeam-0.1.0.tgz","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/multibeam.js"}},"scripts":{"build":"vite build && tsc --emitDeclarationOnly --noEmit false","format":"prettier --write .","format:check":"prettier --check ."},"_npmUser":{"name":"juanpi96","email":"juanpi.96.b@gmail.com"},"_resolved":"/private/var/folders/gk/rnmlqs8s3bd2dltqm39s_s7r0000gn/T/6639edac94dad1af0763d1c10bf62659/blueyerobotics-multibeam-0.1.0.tgz","_integrity":"sha512-VpK5Un/Az5iU+wy7uad2Mi5P4xK6cmLE1f5XGH7tZIFXfD+j62jshmTWZ4f/saWWdtHO2qRA0KXnSHtdAzc2IQ==","repository":{"url":"git+https://github.com/BluEye-Robotics/p2-django.git","type":"git","directory":"multibeam"},"_npmVersion":"10.9.2","description":"Reusable multibeam sonar viewer components for Blueye drones.","directories":{},"_nodeVersion":"22.17.0","dependencies":{"clsx":"^2.1.1","tailwind-merge":"^3.0.0"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.0.0","react":"^19.0.0","prettier":"^3.0.0","typescript":"^5.0.0","@types/react":"^19.0.0","vite-plugin-dts":"^4.0.0","@vitejs/plugin-react-swc":"^4.0.0"},"peerDependencies":{"jszmq":">=0.1","react":">=18","consola":">=3","strict-event-emitter":">=0.5","@blueyerobotics/blueye-ts":">=3"},"_npmOperationalInternal":{"tmp":"tmp/multibeam_0.1.0_1774541194771_0.18910109692403276","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@blueyerobotics/multibeam","version":"0.1.1","description":"Reusable multibeam sonar viewer components for Blueye drones.","type":"module","exports":{".":{"import":"./dist/multibeam.js","types":"./dist/index.d.ts"}},"peerDependencies":{"@blueyerobotics/blueye-ts":">=3","consola":">=3","jszmq":">=0.1","react":">=18","strict-event-emitter":">=0.5"},"dependencies":{"clsx":"^2.1.1","tailwind-merge":"^3.0.0"},"devDependencies":{"prettier":"^3.0.0","@types/react":"^19.0.0","@vitejs/plugin-react-swc":"^4.0.0","react":"^19.0.0","typescript":"^5.0.0","vite":"^7.0.0","vite-plugin-dts":"^4.0.0"},"license":"LGPL-3.0-only","repository":{"type":"git","url":"git+https://github.com/BluEye-Robotics/p2-django.git","directory":"multibeam"},"keywords":["blueye","multibeam","sonar","webgl","react"],"scripts":{"build":"vite build && tsc --emitDeclarationOnly --noEmit false","format":"prettier --write .","format:check":"prettier --check ."},"_id":"@blueyerobotics/multibeam@0.1.1","bugs":{"url":"https://github.com/BluEye-Robotics/p2-django/issues"},"homepage":"https://github.com/BluEye-Robotics/p2-django#readme","_integrity":"sha512-SbdR2OXPnVTLM2D2+BmWc8j9SqR2EQgzTfR52LBrfuttvPaKuYRJ041d1UH02RQB2RKLOcjS30uxbw07TTC4YQ==","_resolved":"/private/var/folders/gk/rnmlqs8s3bd2dltqm39s_s7r0000gn/T/468129d008a93ffbbc13e9cbbc0f3838/blueyerobotics-multibeam-0.1.1.tgz","_from":"file:blueyerobotics-multibeam-0.1.1.tgz","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-SbdR2OXPnVTLM2D2+BmWc8j9SqR2EQgzTfR52LBrfuttvPaKuYRJ041d1UH02RQB2RKLOcjS30uxbw07TTC4YQ==","shasum":"9a81be8f5ac642d64fc2c530abb0e5ca755a04eb","tarball":"https://registry.npmjs.org/@blueyerobotics/multibeam/-/multibeam-0.1.1.tgz","fileCount":13,"unpackedSize":122774,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICaWEPstByISvR8524FT2FT054lj7DPXUnbiCIeHXDCkAiEAsJzZJCF0WpKU0Sw2WFKgLqsVUTzW2+nkOIBOdVKAwDY="}]},"_npmUser":{"name":"juanpi96","email":"juanpi.96.b@gmail.com"},"directories":{},"maintainers":[{"name":"juanpi96","email":"juanpi.96.b@gmail.com"},{"name":"arechi","email":"aglimbenli@gmail.com"},{"name":"follesoe","email":"jonas@follesoe.no"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/multibeam_0.1.1_1775652412104_0.1263582665195544"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-26T16:06:34.600Z","modified":"2026-04-08T12:46:52.377Z","0.1.0":"2026-03-26T16:06:34.948Z","0.1.1":"2026-04-08T12:46:52.230Z"},"bugs":{"url":"https://github.com/BluEye-Robotics/p2-django/issues"},"license":"LGPL-3.0-only","homepage":"https://github.com/BluEye-Robotics/p2-django#readme","keywords":["blueye","multibeam","sonar","webgl","react"],"repository":{"type":"git","url":"git+https://github.com/BluEye-Robotics/p2-django.git","directory":"multibeam"},"description":"Reusable multibeam sonar viewer components for Blueye drones.","maintainers":[{"name":"juanpi96","email":"juanpi.96.b@gmail.com"},{"name":"arechi","email":"aglimbenli@gmail.com"},{"name":"follesoe","email":"jonas@follesoe.no"}],"readme":"# @blueyerobotics/multibeam\n\nReact components and utilities for rendering and playing back Blueye multibeam sonar (`.mbez`) files in the browser via WebGL.\n\n## Installation\n\n```bash\nnpm install @blueyerobotics/multibeam @blueyerobotics/blueye-ts\n```\n\nPeer dependencies: `react >=18`, `@blueyerobotics/blueye-ts >=3`\n\n---\n\n## Palette image\n\nAll rendering functions require a palette image that maps sonar intensity values to colours. The image must be exactly **256 pixels wide**. Each palette occupies a **10-pixel-high row** — the image height determines the number of available palettes (e.g. the bundled `Palettes.png` is 256 × 90 px, providing 9 palettes).\n\nThe palette image is **not bundled with the package** — it must be supplied by the application. The Blunux web app ships one at `frontend/public/Palettes.png`. You can use that file directly, or provide your own image following the same format.\n\nCall `loadPaletteFromUrl` once at app startup before rendering any frames:\n\n```ts\nimport { loadPaletteFromUrl } from \"@blueyerobotics/multibeam\";\n\n// The image must be served from your app — it is not bundled with the package.\n// The Blunux app's Palettes.png can be found at frontend/public/Palettes.png.\nloadPaletteFromUrl(\"/Palettes.png\");\n```\n\nThe palette is loaded into a shared singleton WebGL context so it only needs to be called once.\n\n---\n\n## Components\n\n### `MultibeamPlayer`\n\nHigh-level component that handles file loading, decompression, parsing, and timing-accurate playback. It renders the canvas and exposes playback state to your UI via a **render prop**.\n\n```tsx\nimport { MultibeamPlayer } from \"@blueyerobotics/multibeam\";\n\n<MultibeamPlayer\n  file={blob} // Blob — the .mbez file\n  paletteIndex={0} // number — which palette row to use (default: 0)\n  overlayOptions={{\n    // optional range/sector overlay\n    rangeLines: true,\n    sectors: true,\n    labels: true,\n  }}\n  fadeFarEdge={false} // fade the far edge of the fan (default: false)\n  className=\"w-full h-full\" // optional — outer wrapper class\n  rendererClassName=\"drop-shadow-xl\" // optional — passed to inner MultibeamRenderer\n  onDiscovery={(d) => {\n    // fired if a MultibeamDiscoveryTel message exists\n    console.log(\"sonar model:\", d.modelName);\n  }}\n  onReady={() => {\n    // fired after parsing — use to hide loading spinners\n    setLoading(false);\n  }}\n>\n  {(state) => (\n    // state: PlayerState — see type below\n    <MyControls state={state} />\n  )}\n</MultibeamPlayer>;\n```\n\n#### `PlayerState`\n\n| Field         | Type                  | Description                               |\n| ------------- | --------------------- | ----------------------------------------- |\n| `messages`    | `Message[]`           | Decoded ping frames                       |\n| `index`       | `number`              | Currently displayed frame index           |\n| `playing`     | `boolean`             | Whether playback is running               |\n| `loading`     | `boolean`             | Whether the file is still loading/parsing |\n| `error`       | `string \\| null`      | Parse or fetch error message              |\n| `play()`      | `() => void`          | Start playback                            |\n| `pause()`     | `() => void`          | Pause playback                            |\n| `toggle()`    | `() => void`          | Toggle play/pause                         |\n| `seek(index)` | `(n: number) => void` | Jump to a specific frame                  |\n| `reset()`     | `() => void`          | Return to frame 0 and pause               |\n\n---\n\n### `MultibeamRenderer`\n\nLow-level component that renders a **single frame** to a canvas. Each instance owns its own WebGL context and renders directly — no shared state or blit overhead. Overlays are drawn on a separate 2D canvas layered on top. Palette URL is sourced automatically from `MultibeamPaletteProvider` (or an optional `paletteUrl` prop override).\n\n```tsx\nimport { MultibeamRenderer } from \"@blueyerobotics/multibeam\";\n\n<MultibeamRenderer\n  frame={message} // Message | null — a decoded ping frame\n  paletteIndex={0}\n  overlayOptions={{ rangeLines: true, sectors: true, labels: true }}\n  fadeFarEdge={false}\n  paletteUrl=\"/Palettes.png\" // optional — overrides MultibeamPaletteProvider\n  className=\"w-full h-full\"\n/>;\n```\n\n---\n\n### `QueuedMultibeamRenderer`\n\nThumbnail renderer that uses a **shared off-screen WebGL context** with a serialised render queue. Uses `IntersectionObserver` to lazy-load thumbnails when they scroll into view. Ideal for media grids with many sonar thumbnails.\n\n```tsx\nimport { QueuedMultibeamRenderer } from \"@blueyerobotics/multibeam\";\n\n<QueuedMultibeamRenderer\n  url=\"https://device/webdav/dive_001_thumbnail.mbez\"\n  paletteIndex={0}\n  width={600} // optional — canvas width in px (default: 600)\n  height={400} // optional — canvas height in px (default: 400)\n  fallback={<span>No preview</span>}\n  className=\"w-full h-full\"\n/>;\n```\n\n---\n\n### `MultibeamPaletteProvider`\n\nContext provider that loads a palette image and makes the URL available to all `MultibeamRenderer` and `QueuedMultibeamRenderer` instances in the tree. Also loads the palette into the shared WebGL context for thumbnail rendering. Place near the app root.\n\n```tsx\nimport { MultibeamPaletteProvider } from \"@blueyerobotics/multibeam\";\n\n<MultibeamPaletteProvider url=\"/Palettes.png\">\n  <App />\n</MultibeamPaletteProvider>;\n```\n\nUse the `useMultibeamPaletteUrl()` hook to read the current palette URL from context.\n\n---\n\n## Utilities\n\n### `loadPaletteFromUrl(url: string): void`\n\nLoads a palette image into the shared WebGL context. Must be called before any frame is rendered. See the [Palette image](#palette-image) section above. Not needed if you use `MultibeamPaletteProvider`.\n\n### `getPaletteSwatchStyle(imageUrl, index, count, swatchHeight): PaletteSwatchStyle`\n\nReturns CSS background properties (`backgroundImage`, `backgroundSize`, `backgroundPosition`, `backgroundRepeat`) needed to display a single palette swatch from the palette sprite image. Useful for building palette selector UIs.\n\n### `paletteCount`\n\nThe number of palettes available in the loaded palette sprite image.\n\n---\n\n## Device utilities\n\n### `SONAR_DEVICE_INFO: Record<number, SonarDeviceInfo>`\n\nDevice capability data for all supported multibeam sonar models, keyed by device ID. Includes name, frequency ranges, valid beam counts, and feature support flags.\n\n### `MULTIBEAM_DEVICE_IDS: Set<number>`\n\nSet of all known multibeam device IDs. Use for quick membership checks (e.g. to determine whether a guest-port device is a multibeam sonar).\n\n### `MULTIBEAM_DEVICE_NAMES: Record<number, string>`\n\nMap from device ID to a human-readable model name (e.g. `16 → \"Gemini 720im\"`).\n\n### `DEFAULT_MULTIBEAM_CONFIG`\n\nSensible default values for a `MultibeamConfig` protobuf message. Useful as a starting point when the drone hasn't reported its current configuration yet.\n\n### `getRangeInfo(deviceId, frequencyMode): SonarRangeInfo`\n\nReturns the min/max range for a given device and frequency mode.\n\n### `getValidBeams(deviceId): number[]`\n\nReturns the valid beam count enum values for a given device.\n\n### `getConnectedMultibeam(droneInfo): ConnectedMultibeam | null`\n\nExtracts the connected multibeam device from a `DroneInfoTel` telemetry message.\n\n---\n\n## `OverlayOptions`\n\n```ts\ntype OverlayOptions = {\n  rangeLines?: boolean; // draw concentric range arcs (default: true)\n  sectors?: boolean; // draw sector boundary lines (default: true)\n  labels?: boolean; // draw distance labels on range arcs (default: true)\n  formatRange?: (meters: number) => string; // custom label formatter\n  labelFontSize?: number; // override auto-scaled label font size in pixels\n  labelColor?: string; // label text fill colour (default: \"rgba(255,255,255,0.8)\")\n  labelStrokeColor?: string | null; // label stroke colour, or null to disable (default: \"rgba(0,0,0,0.6)\")\n};\n```\n\n---\n\n## Examples\n\nReady-to-use example components are provided in `src/examples/`:\n\n### `ThumbnailExample`\n\nLazy-loading thumbnail using `QueuedMultibeamRenderer`. Only fetches the `_thumbnail.mbez` when the element scrolls into view. Requires a `MultibeamPaletteProvider` ancestor.\n\n```tsx\nimport ThumbnailExample from \"@blueyerobotics/multibeam/examples/ThumbnailExample\";\n\n<div style={{ width: 320, height: 213 }}>\n  <ThumbnailExample\n    url=\"https://device/webdav/dive_001_thumbnail.mbez\"\n    paletteIndex={0}\n    fallback={<span>No preview</span>}\n  />\n</div>;\n```\n\n### `PlayerExample`\n\nMinimal playback UI with scrubber, play/pause, reset, overlay lines, and far-edge fade. Use as a starting point for a custom player.\n\n```tsx\nimport PlayerExample from \"@blueyerobotics/multibeam/examples/PlayerExample\";\n\n// file is a Blob — e.g. from a fetch or <input type=\"file\">\n<PlayerExample file={blob} paletteIndex={0} onDiscovery={(d) => console.log(d)} />;\n```\n\n---\n\n## Publishing\n\nThe package is published automatically from CI when a tag matching `multibeam/vX.Y.Z` is pushed:\n\n```bash\ngit tag multibeam/v0.2.0\ngit push origin multibeam/v0.2.0\n```\n\nThe `publishConfig.exports` field in `package.json` remaps the entry point from `src/index.ts` (used in the workspace) to `dist/multibeam.js` (used by npm consumers).\n","readmeFilename":"README.md"}