@foxglove/extension 2.52.0 → 2.53.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/experimental.ts +148 -3
- package/src/stable.ts +64 -6
package/package.json
CHANGED
package/src/experimental.ts
CHANGED
|
@@ -5,6 +5,8 @@ import type {
|
|
|
5
5
|
MessageEvent as BaseMessageEvent,
|
|
6
6
|
PanelExtensionContext as BasePanelExtensionContext,
|
|
7
7
|
RenderState as BaseRenderState,
|
|
8
|
+
Subscription as BaseSubscription,
|
|
9
|
+
Time,
|
|
8
10
|
} from "./stable";
|
|
9
11
|
|
|
10
12
|
/**
|
|
@@ -20,20 +22,35 @@ import type {
|
|
|
20
22
|
export namespace Experimental {
|
|
21
23
|
export type DataSourceId = string & { __brand: "DataSourceId" };
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Requested start position for a source on the compare timeline.
|
|
27
|
+
* Manual values are in compare-time seconds and must be non-negative.
|
|
28
|
+
*/
|
|
29
|
+
export type RequestedDataSourceTimeOffset = { mode: "auto" } | { mode: "manual"; offset: Time };
|
|
30
|
+
|
|
23
31
|
export type DataSourceInfo = {
|
|
24
32
|
/** ID of the data source (e.g. "A", "B", "C", ...) used to identify the data source. */
|
|
25
33
|
id: DataSourceId;
|
|
26
34
|
/** The name of the data source e.g. the name of the file or URL. */
|
|
27
35
|
name: string;
|
|
36
|
+
/** User-requested compare-time start configuration. */
|
|
37
|
+
requestedTimeOffset: RequestedDataSourceTimeOffset;
|
|
38
|
+
/** Effective time offset applied to source timestamps. */
|
|
39
|
+
timeOffset: Time;
|
|
28
40
|
};
|
|
29
41
|
|
|
30
42
|
export type DataSourceMap = ReadonlyMap<DataSourceId, Immutable<DataSourceInfo>>;
|
|
31
43
|
|
|
32
|
-
export type MessageEvent = BaseMessageEvent & {
|
|
44
|
+
export type MessageEvent<T = unknown> = BaseMessageEvent<T> & {
|
|
33
45
|
/** The data source that the message stems from. */
|
|
34
46
|
dataSourceId?: DataSourceId;
|
|
35
47
|
};
|
|
36
48
|
|
|
49
|
+
export type Subscription = BaseSubscription & {
|
|
50
|
+
/** The data source that the subscription stems from. */
|
|
51
|
+
dataSourceId?: DataSourceId;
|
|
52
|
+
};
|
|
53
|
+
|
|
37
54
|
/**
|
|
38
55
|
* This type represents the arguments you pass to {@link ExtensionContext.registerDataLoader}
|
|
39
56
|
* when you want to register a data loader.
|
|
@@ -287,11 +304,57 @@ export namespace Experimental {
|
|
|
287
304
|
*/
|
|
288
305
|
export type PanelToolbarItem = PanelToolbarButton | PanelToolbarLink | PanelToolbarDivider;
|
|
289
306
|
|
|
307
|
+
/** A metadata record from an MCAP recording. @category Custom panels */
|
|
308
|
+
export type MetadataRecord = {
|
|
309
|
+
name: string;
|
|
310
|
+
metadata: Record<string, string>;
|
|
311
|
+
/**
|
|
312
|
+
* Which source this came from in multiplexed playback; `undefined` for single-source.
|
|
313
|
+
* In merge mode all records share the same `dataSourceId` (the default source's id) —
|
|
314
|
+
* use `dataSources` to detect compare mode.
|
|
315
|
+
*/
|
|
316
|
+
dataSourceId?: DataSourceId;
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Information about an attachment in an MCAP recording — name, size, and timing metadata,
|
|
321
|
+
* but not the bytes. Use `context.fetchAttachment(id)` to load the bytes on demand.
|
|
322
|
+
* @category Custom panels
|
|
323
|
+
*/
|
|
324
|
+
export type AttachmentInfo = {
|
|
325
|
+
/** Session-scoped id; stable for the playback session but not persistent or globally unique. */
|
|
326
|
+
id: string;
|
|
327
|
+
name: string;
|
|
328
|
+
mediaType: string;
|
|
329
|
+
logTime: Time;
|
|
330
|
+
createTime: Time;
|
|
331
|
+
sizeBytes: number;
|
|
332
|
+
/**
|
|
333
|
+
* Which source this came from in multiplexed playback; `undefined` for single-source.
|
|
334
|
+
* In merge mode all attachments share the same `dataSourceId` (the default source's id),
|
|
335
|
+
* distinct from the attachment's own `id` above — use `dataSources` to detect compare mode.
|
|
336
|
+
*/
|
|
337
|
+
dataSourceId?: DataSourceId;
|
|
338
|
+
};
|
|
339
|
+
|
|
290
340
|
export type RenderState = BaseRenderState & {
|
|
291
341
|
/**
|
|
292
342
|
* Map of available individual data sources, keyed by their ID.
|
|
293
343
|
*/
|
|
294
344
|
dataSources?: DataSourceMap;
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* MCAP metadata records from all active sources. In compare mode use `dataSourceId`
|
|
348
|
+
* to distinguish per-source records.
|
|
349
|
+
*/
|
|
350
|
+
metadata?: MetadataRecord[];
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* MCAP attachment references from all active sources (name/size/timing only, no bytes).
|
|
354
|
+
* In compare mode use `dataSourceId` to distinguish per-source entries.
|
|
355
|
+
* Call `context.fetchAttachment(id)` to load bytes.
|
|
356
|
+
*/
|
|
357
|
+
attachments?: AttachmentInfo[];
|
|
295
358
|
};
|
|
296
359
|
|
|
297
360
|
export interface PanelExtensionContext extends BasePanelExtensionContext {
|
|
@@ -300,21 +363,103 @@ export namespace Experimental {
|
|
|
300
363
|
* this field changes.
|
|
301
364
|
*
|
|
302
365
|
* Use `context.watch` to indicate which fields in {@link RenderState} (e.g. `currentFrame`,
|
|
303
|
-
* `currentTime`, `previewTime`, `parameters`, `topics`) should
|
|
304
|
-
* their contained values change.
|
|
366
|
+
* `currentTime`, `previewTime`, `parameters`, `topics`, `metadata`, `attachments`) should
|
|
367
|
+
* trigger panel re-renders when their contained values change.
|
|
305
368
|
*
|
|
306
369
|
* ```ts
|
|
307
370
|
* context.watch("topics");
|
|
308
371
|
* context.watch("currentFrame");
|
|
309
372
|
* context.watch("parameters");
|
|
310
373
|
* context.watch("currentTime");
|
|
374
|
+
* context.watch("metadata");
|
|
375
|
+
* context.watch("attachments");
|
|
311
376
|
* ```
|
|
312
377
|
*/
|
|
313
378
|
watch(field: keyof RenderState): void;
|
|
314
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Process render events for the panel. Each render event receives a render state and a done
|
|
382
|
+
* callback. This override surfaces the experimental {@link RenderState} fields
|
|
383
|
+
* (`dataSources`, `metadata`, `attachments`) so panels can read them after calling `watch`.
|
|
384
|
+
*/
|
|
385
|
+
onRender?: (renderState: Immutable<RenderState>, done: () => void) => void;
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Use `context.subscribe` to indicate the topics your panel wants to receive messages for. The
|
|
389
|
+
* messages are provided during render in {@link RenderState.currentFrame}.
|
|
390
|
+
*
|
|
391
|
+
* @remarks
|
|
392
|
+
*
|
|
393
|
+
* This method will update the current subscriptions to the new list of Subscriptions and
|
|
394
|
+
* unsubscribe from any previously subscribed topics no longer in the Subscription list. Passing
|
|
395
|
+
* an empty array will unsubscribe from all topics.
|
|
396
|
+
*
|
|
397
|
+
* ```ts
|
|
398
|
+
* context.subscribe([{ topic: "/some/topic" }, { topic: "/another/topic" }]);
|
|
399
|
+
* ```
|
|
400
|
+
*
|
|
401
|
+
* `context.subscribe([])` will unsubscribe from all topics, and is equivalent to
|
|
402
|
+
* `unsubscribeAll`.
|
|
403
|
+
*
|
|
404
|
+
* #### Range loading
|
|
405
|
+
*
|
|
406
|
+
* Most panels display data from the current frame; examples of built-in panels that display the
|
|
407
|
+
* current frame are 3D, Image, and Raw Message, however some panels can display data for multiple
|
|
408
|
+
* messages or even the entire dataset duration (Plot, Map, State Transitions).
|
|
409
|
+
*
|
|
410
|
+
* Subscriptions will provide only the messages for the current frame. If your panel would like to
|
|
411
|
+
* process all the available messages on a topic, use
|
|
412
|
+
* {@link @PanelExtensionContext.subscribeMessageRange | subscribeMessageRange} instead.
|
|
413
|
+
*
|
|
414
|
+
* > NOTE: Message range loading is done on a best-effort basis. If your range-loaded messages
|
|
415
|
+
* > exceed available memory limits for the browser or desktop app, then the data may not
|
|
416
|
+
* > represent the full dataset range. Range loading results in more data transfer and memory use
|
|
417
|
+
* > and is recommended only for panels which require access to the entire dataset.
|
|
418
|
+
*
|
|
419
|
+
* #### Message converters
|
|
420
|
+
*
|
|
421
|
+
* Message converters can convert messages from one schema to another – for example, a user might
|
|
422
|
+
* convert custom GPS message into
|
|
423
|
+
* [`foxglove.LocationFix`](https://docs.foxglove.dev/docs/visualization/message-schemas/location-fix)
|
|
424
|
+
* messages for visualization in the [Map
|
|
425
|
+
* panel](https://docs.foxglove.dev/docs/visualization/panels/map). Users may have one or more
|
|
426
|
+
* message converters registered.
|
|
427
|
+
*
|
|
428
|
+
* If your panel expects messages with specific schema names, you can leverage registered message
|
|
429
|
+
* converters to convert from one schema to another.
|
|
430
|
+
*
|
|
431
|
+
* Specify the `convertTo` option to enable message conversion on a topic. When conversion is
|
|
432
|
+
* enabled for a subscription, the {@link MessageEvent}s will contain `message` entries with the
|
|
433
|
+
* converted message rather than the original message on the topic. The original message is
|
|
434
|
+
* available in the `originalMessageEvent` field in the message event.
|
|
435
|
+
*
|
|
436
|
+
* ```ts
|
|
437
|
+
* context.subscribe([{ topic: "/some/topic", convertTo: "foxglove.LocationFix" }]);
|
|
438
|
+
* ```
|
|
439
|
+
*
|
|
440
|
+
* The {@link Topic.convertibleTo | convertibleTo} field within {@link RenderState.topics} will
|
|
441
|
+
* contain the names of schemas you can convert this topic into.
|
|
442
|
+
*/
|
|
443
|
+
subscribe(subscriptions: Subscription[]): void;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* @deprecated Use `subscribe` with an array of Subscription objects instead.
|
|
447
|
+
*/
|
|
448
|
+
subscribe(topics: string[]): void;
|
|
449
|
+
|
|
315
450
|
/**
|
|
316
451
|
* Set the items in the panel toolbar.
|
|
317
452
|
*/
|
|
318
453
|
setPanelToolbarItems: (items: Immutable<PanelToolbarItem[]>) => void;
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Load raw bytes for an attachment by `id` (from `RenderState.attachments`). `undefined` when
|
|
457
|
+
* no attachments are available on the current recording — feature-detect with a truthy check.
|
|
458
|
+
*
|
|
459
|
+
* Concurrent calls for the same `id` are deduplicated to a single in-flight request, but
|
|
460
|
+
* resolved bytes are not cached: a subsequent call after the previous one settles will
|
|
461
|
+
* re-fetch. Panels that want to keep bytes across renders should memoize the result.
|
|
462
|
+
*/
|
|
463
|
+
fetchAttachment?: (id: string) => Promise<Uint8Array>;
|
|
319
464
|
}
|
|
320
465
|
}
|
package/src/stable.ts
CHANGED
|
@@ -770,6 +770,12 @@ export type PanelExtensionContext = {
|
|
|
770
770
|
* case "perform-node-action":
|
|
771
771
|
* // Handle user-defined actions for nodes in the settings tree
|
|
772
772
|
* break;
|
|
773
|
+
* case "reorder-children":
|
|
774
|
+
* if (action.payload.path[0] === "rules") {
|
|
775
|
+
* // Move the child identified by action.payload.fromKey before or after
|
|
776
|
+
* // action.payload.toKey within the parent node at action.payload.path.
|
|
777
|
+
* }
|
|
778
|
+
* break;
|
|
773
779
|
* case "update":
|
|
774
780
|
* if (action.payload.path[0] === "general" && action.payload.path[1] === "title") {
|
|
775
781
|
* // Read action.payload.value for the new panel title value
|
|
@@ -787,14 +793,20 @@ export type PanelExtensionContext = {
|
|
|
787
793
|
*
|
|
788
794
|
* #### `SettingsTreeAction`
|
|
789
795
|
*
|
|
790
|
-
* A {@link SettingsTreeAction}
|
|
791
|
-
* with
|
|
796
|
+
* A {@link SettingsTreeAction} is a discriminated union describing how the settings UI should
|
|
797
|
+
* update when a user interacts with it.
|
|
792
798
|
*
|
|
793
|
-
*
|
|
794
|
-
* `["general", "title"]`).
|
|
799
|
+
* All actions include a `payload.path` that identifies the target node or field.
|
|
795
800
|
*
|
|
796
|
-
*
|
|
797
|
-
* title"
|
|
801
|
+
* - `update` corresponds to a user setting a new field value. Its path points to the field, for
|
|
802
|
+
* example `["general", "title"]`.
|
|
803
|
+
* - `perform-node-action` corresponds to a user invoking a custom node action from `actions`.
|
|
804
|
+
* - `reorder-children` corresponds to a user reordering the children of a node whose
|
|
805
|
+
* `childrenReorderable` property is enabled. Its path points to the parent node, and the payload
|
|
806
|
+
* identifies the moved child and destination child by key.
|
|
807
|
+
*
|
|
808
|
+
* This union is extended additively over time. To stay forward-compatible, action handlers should
|
|
809
|
+
* ignore unknown `action` values at runtime instead of throwing from a `default` branch.
|
|
798
810
|
*
|
|
799
811
|
* #### Special node properties
|
|
800
812
|
*
|
|
@@ -807,6 +819,10 @@ export type PanelExtensionContext = {
|
|
|
807
819
|
* editor will provide a button to toggle the visibility of the node and you will receive an
|
|
808
820
|
* `update` action with `visibility` as the final element in the path.
|
|
809
821
|
*
|
|
822
|
+
* If you set `childrenReorderable` on a node with at least two children, the settings editor lets
|
|
823
|
+
* users drag and drop that node's children and dispatches `reorder-children` actions when the
|
|
824
|
+
* user changes their order.
|
|
825
|
+
*
|
|
810
826
|
* For an example of how to use these special properties, check out the [panel settings example
|
|
811
827
|
* extension](https://github.com/foxglove/create-foxglove-extension/tree/main/examples/panel-settings).
|
|
812
828
|
*
|
|
@@ -1635,6 +1651,12 @@ export type SettingsTreeNode = {
|
|
|
1635
1651
|
*/
|
|
1636
1652
|
renamable?: boolean;
|
|
1637
1653
|
|
|
1654
|
+
/**
|
|
1655
|
+
* Set to `false` on a child of a `childrenReorderable` node to keep this node rendered in place
|
|
1656
|
+
* without drag-and-drop controls or default "Move up" / "Move down" action menu items.
|
|
1657
|
+
*/
|
|
1658
|
+
reorderable?: boolean;
|
|
1659
|
+
|
|
1638
1660
|
/**
|
|
1639
1661
|
* Optional sort order to override natural object ordering. All nodes
|
|
1640
1662
|
* with a sort order will be rendered before nodes all with no sort order.
|
|
@@ -1646,6 +1668,23 @@ export type SettingsTreeNode = {
|
|
|
1646
1668
|
*/
|
|
1647
1669
|
order?: number | string;
|
|
1648
1670
|
|
|
1671
|
+
/**
|
|
1672
|
+
* When set to `true`, the renderer treats this node's reorderable children as a
|
|
1673
|
+
* user-reorderable list with drag-and-drop controls and default "Move up" / "Move down" action
|
|
1674
|
+
* menu items when at least two reorderable children are present. Both reorder methods dispatch
|
|
1675
|
+
* "reorder-children" actions whose `path` is the path of this parent node.
|
|
1676
|
+
*
|
|
1677
|
+
* When a visibility filter or tree-wide text filter hides any of this node's children,
|
|
1678
|
+
* drag-and-drop affordances are suppressed until the filter is cleared so that the user is never
|
|
1679
|
+
* dragging against a subset of the list. The action menu items remain available because they
|
|
1680
|
+
* resolve against sibling keys in the complete list.
|
|
1681
|
+
*
|
|
1682
|
+
* When this option is enabled, child render order is driven by the order of entries in
|
|
1683
|
+
* `children`. Explicit `order` values on children are ignored and should not be used for
|
|
1684
|
+
* reorderable children.
|
|
1685
|
+
*/
|
|
1686
|
+
childrenReorderable?: boolean;
|
|
1687
|
+
|
|
1649
1688
|
/**
|
|
1650
1689
|
* An optional visibility status. If this is not undefined, the node
|
|
1651
1690
|
* editor will display a visibility toggle button and send update actions
|
|
@@ -1684,6 +1723,25 @@ export type SettingsTreeAction =
|
|
|
1684
1723
|
"input" | "value"
|
|
1685
1724
|
>;
|
|
1686
1725
|
}
|
|
1726
|
+
| {
|
|
1727
|
+
action: "reorder-children";
|
|
1728
|
+
payload: {
|
|
1729
|
+
/** Path to the parent node whose children are being reordered. */
|
|
1730
|
+
path: readonly string[];
|
|
1731
|
+
/**
|
|
1732
|
+
* Key of the child being moved. If this key no longer exists under `path` (for example
|
|
1733
|
+
* because the list changed since the drag started), handlers should ignore the action.
|
|
1734
|
+
*/
|
|
1735
|
+
fromKey: string;
|
|
1736
|
+
/**
|
|
1737
|
+
* Key of the child currently occupying the destination slot. If this key no longer
|
|
1738
|
+
* exists under `path`, handlers should ignore the action.
|
|
1739
|
+
*/
|
|
1740
|
+
toKey: string;
|
|
1741
|
+
/** Whether the moved child should land before or after `toKey`. */
|
|
1742
|
+
position: "before" | "after";
|
|
1743
|
+
};
|
|
1744
|
+
}
|
|
1687
1745
|
| {
|
|
1688
1746
|
action: "perform-node-action";
|
|
1689
1747
|
payload: { id: string; path: readonly string[] };
|