@foxglove/extension 2.24.0 → 2.25.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/src/index.ts CHANGED
@@ -1,1423 +1,3 @@
1
- import type { Immutable } from "./immutable";
2
-
1
+ export * from "./stable";
3
2
  export type { Immutable } from "./immutable";
4
-
5
- /** Valid types for parameter data (such as rosparams) */
6
- export type ParameterValue =
7
- | undefined
8
- | boolean
9
- | number
10
- | string
11
- | Date
12
- | Uint8Array
13
- | ParameterValue[]
14
- | { [key: string]: ParameterValue };
15
-
16
- /** Valid types for [variables](https://docs.foxglove.dev/docs/visualization/variables) */
17
- export type VariableValue =
18
- | undefined
19
- | boolean
20
- | number
21
- | string
22
- | VariableValue[]
23
- | { [key: string]: VariableValue };
24
-
25
- export type VariableStruct = { [key: string]: VariableValue };
26
-
27
- /** Valid types for application settings */
28
- export type AppSettingValue = string | number | boolean | undefined;
29
-
30
- /**
31
- * A timestamp with nanosecond precision.
32
- *
33
- * The timestamp is often nanoseconds since the UNIX epoch, but may be
34
- * relative to another event such as system boot time or simulation start
35
- * time depending on the context.
36
- */
37
- export type Time = {
38
- sec: number;
39
- nsec: number;
40
- };
41
-
42
- /**
43
- * A Topic is a named channel of messages.
44
- */
45
- export type Topic = {
46
- /**
47
- * topic name i.e. "/some/topic"
48
- */
49
- name: string;
50
- /**
51
- * @deprecated Renamed to `schemaName`. `datatype` will be removed in a future release.
52
- */
53
- datatype: string;
54
- /**
55
- * The schema name is an identifier for the types of messages on this topic. Typically this is the
56
- * fully-qualified name of the message schema. The fully-qualified name depends on the data source
57
- * and data loaded by the data source.
58
- *
59
- * i.e. `package.Message` in protobuf-like serialization or `pkg/Msg` in ROS systems.
60
- */
61
- schemaName: string;
62
-
63
- /**
64
- * Lists any additional schema names available for subscribers on the topic. When subscribing to
65
- * a topic, the panel can request messages be automatically converted from schemaName into one
66
- * of the convertibleTo schemas using the {@link Subscription.convertTo} option.
67
- */
68
- convertibleTo?: readonly string[];
69
- };
70
-
71
- /**
72
- * A single subscription passed to {@link PanelExtensionContext.subscribe}.
73
- *
74
- * @category Custom panels
75
- */
76
- export type Subscription = {
77
- topic: string;
78
-
79
- /**
80
- * If a topic has additional schema names, specifying a schema name will convert messages on that
81
- * topic to the convertTo schema using a registered message converter. MessageEvents for the
82
- * subscription will contain the converted message and an originalMessageEvent field with the
83
- * original message event.
84
- */
85
- convertTo?: string;
86
-
87
- /**
88
- * Setting preload to _true_ hints to the data source that it should attempt to load all available
89
- * messages for the topic. The default behavior is to only load messages for the current frame.
90
- *
91
- * **Only** topics with `preload: true` are available in the `allFrames` render state.
92
- *
93
- * @deprecated Please use {@link PanelExtensionContext.subscribeMessageRange} instead.
94
- */
95
- preload?: boolean;
96
- };
97
-
98
- /**
99
- * A MessageEvent represents a single message along with metadata about the message.
100
- *
101
- * Remember to import MessageEvent from `@foxglove/extension`. This is not the same as the DOM [MessageEvent](https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent) class.
102
- */
103
- export type MessageEvent<T = unknown> = {
104
- /** The topic name this message was received on, i.e. "/some/topic" */
105
- topic: string;
106
- /**
107
- * The schema name is an identifier for the schema of the message within the message event.
108
- */
109
- schemaName: string;
110
- /**
111
- * The time in nanoseconds this message was received. This may be set by the
112
- * local system clock or the data source, depending on the data source used
113
- * and whether time is simulated via a /clock topic or similar mechanism.
114
- * The timestamp is often nanoseconds since the UNIX epoch, but may be
115
- * relative to another event such as system boot time or simulation start
116
- * time depending on the context.
117
- */
118
- receiveTime: Time;
119
- /**
120
- * The time in nanoseconds this message was originally published. This is
121
- * only available for some data sources. The timestamp is often nanoseconds
122
- * since the UNIX epoch, but may be relative to another event such as system
123
- * boot time or simulation start time depending on the context.
124
- */
125
- publishTime?: Time;
126
- /** The deserialized message as a JavaScript object. */
127
- message: T;
128
- /**
129
- * The approximate size of this message event in its deserialized form. This can be
130
- * useful for statistics tracking and cache eviction.
131
- */
132
- sizeInBytes: number;
133
-
134
- /**
135
- * When subscribing to a topic using the `convertTo` option, the message event `message`
136
- * contains the converted message and the `originalMessageEvent` field contains the original
137
- * un-converted message event.
138
- */
139
- originalMessageEvent?: MessageEvent;
140
- };
141
-
142
- /**
143
- * Actions the panel may perform related to the user's current layout via {@link PanelExtensionContext.layout | context.layout}.
144
- *
145
- * @category Custom panels
146
- */
147
- export interface LayoutActions {
148
- /**
149
- * Use `context.layout.addPanel` to add a panel adjacent to the current panel in the layout.
150
- *
151
- * The value of `position` must be set to `"sibling"`.
152
- *
153
- * The value of `type` can refer to a panel from a custom extension as `extensionname.panelname`,
154
- * where `extensionname` is the extension name from `package.json` and `panelname` is the name
155
- * provided when the extension registers a panel.
156
- *
157
- * `getState` is set to a function that returns the state (also known as panel settings) for the
158
- * new panel, or return `undefined` to use the new panel's default settings.
159
- *
160
- * ```ts
161
- * // Add new panel
162
- * context.layout.addPanel({
163
- * position: "sibling",
164
- * type: "MyExtension.MyPanel",
165
- * getState: () => ({}),
166
- * });
167
- * ```
168
- */
169
- addPanel(params: {
170
- /**
171
- * Where to position the panel. Currently, only "sibling" is supported which indicates the
172
- * new panel will be adjacent to the calling panel.
173
- */
174
- position: "sibling";
175
-
176
- /**
177
- * The type of panel to open. For extension panels, this `"extensionName.panelName"` where
178
- * extensionName is the `name` field from the extension's package.json, and panelName is the
179
- * name provided to `registerPanel()`.
180
- */
181
- type: string;
182
-
183
- /**
184
- * Whether to update an existing sibling panel of the same type, if it already exists. If false
185
- * or omitted, a new panel will always be added.
186
- *
187
- * @deprecated This parameter is only supported for built-in panels at this time.
188
- */
189
- updateIfExists?: boolean;
190
-
191
- /**
192
- * A function that returns the state for the new panel. If updating an existing panel, the
193
- * existing state will be passed in.
194
- * @see `updateIfExists`
195
- */
196
- getState(existingState?: unknown): unknown;
197
- }): void;
198
- }
199
-
200
- /**
201
- * RenderState is the information passed to your panel's
202
- * {@link PanelExtensionContext.onRender | onRender} function.
203
- *
204
- * To receive updates for a particular part of RenderState, you must first call
205
- * {@link PanelExtensionContext.watch | watch} with the field name. For example, call
206
- * `watch("currentTime")` to receive updates for `currentTime`.
207
- *
208
- * If a field is missing from RenderState, either the value has not changed since the last call to
209
- * `onRender`, or you did not `watch` the field.
210
- *
211
- * @category Custom panels
212
- */
213
- export type RenderState = {
214
- /**
215
- * The latest messages for the current render frame. These are new messages since the last render frame.
216
- */
217
- currentFrame?: MessageEvent[];
218
-
219
- /**
220
- * True if the data source performed a seek. This indicates that some data may have been skipped
221
- * (never appeared in the `currentFrame`), so panels should clear out any stale state to avoid
222
- * displaying incorrect data.
223
- */
224
- didSeek?: boolean;
225
-
226
- /**
227
- * All available messages. Best-effort list of all available messages.
228
- *
229
- * @deprecated Please use {@link PanelExtensionContext.subscribeMessageRange} instead.
230
- */
231
- allFrames?: MessageEvent[];
232
-
233
- /**
234
- * Map of current parameter values. Parameters are key/value pairs associated with the data
235
- * source, and may not be available for all data sources. For example, ROS 1 live connections
236
- * support parameters through the Parameter Server <http://wiki.ros.org/Parameter%20Server>.
237
- */
238
- parameters?: Map<string, ParameterValue>;
239
-
240
- /**
241
- * Transient panel state shared between panels of the same type. This can be any data a
242
- * panel author wishes to share between panels.
243
- */
244
- sharedPanelState?: Record<string, unknown>;
245
-
246
- /**
247
- * Map of current Studio variables. Variables are key/value pairs that are globally accessible
248
- * to panels and scripts in the current layout. See
249
- * <https://docs.foxglove.dev/docs/visualization/variables> for more information.
250
- */
251
- variables?: Map<string, VariableValue>;
252
-
253
- /**
254
- * List of available topics. This list includes subscribed and unsubscribed topics.
255
- */
256
- topics?: Topic[];
257
-
258
- /**
259
- * A timestamp value indicating the current playback time.
260
- */
261
- currentTime?: Time;
262
-
263
- /**
264
- * The start timestamp of the playback range for the current data source. For offline files it
265
- * is expected to be present. For live connections, the start time may or may not be present
266
- * depending on the data source.
267
- */
268
- startTime?: Time;
269
-
270
- /**
271
- * The end timestamp of the playback range for the current data source. For offline files it
272
- * is expected to be present. For live connections, the end time may or may not be present
273
- * depending on the data source.
274
- */
275
- endTime?: Time;
276
-
277
- /**
278
- * A seconds value indicating a preview time. The preview time is set when a user hovers
279
- * over the seek bar or when a panel sets the preview time explicitly. The preview time
280
- * is a seconds value within the playback range.
281
- *
282
- * i.e. A plot panel may set the preview time when a user is hovering over the plot to signal
283
- * to other panels where the user is currently hovering and allow them to render accordingly.
284
- */
285
- previewTime?: number | undefined;
286
-
287
- /** The color scheme currently in use throughout the app. */
288
- colorScheme?: "dark" | "light";
289
-
290
- /** Application settings. This will only contain keys/values that were subscribed to using {@link @PanelExtensionContext.subscribeAppSettings} */
291
- appSettings?: Map<string, AppSettingValue>;
292
- };
293
-
294
- /**
295
- * This type represents the arguments you pass to {@link PanelExtensionContext.subscribeMessageRange}.
296
- *
297
- * @category Custom panels
298
- */
299
- export type SubscribeMessageRangeArgs = {
300
- /**
301
- * Topic to be subscribed to.
302
- */
303
- topic: string;
304
- /**
305
- * Convert messages to this schema before delivering to the subscriber.
306
- *
307
- * MessageEvents for the subscription will contain the converted message and an
308
- * `originalMessageEvent` field with the original message event. If no `convertTo` schema is
309
- * specified, then no message converters will be used. If no message converter exists for
310
- * converting the original schema to the `convertTo` schema, then no messages are delivered for
311
- * this subscription.
312
- */
313
- convertTo?: string;
314
- /**
315
- * `onNewRangeIterator` is a function that receives an async iterable when there is message data available on
316
- * the subscription.
317
- *
318
- * To read messages, your function should iterate through the provided async iterable. Each item
319
- * of the iterable is a batch of message events for the subscription's topic. These batches and
320
- * messages are in _log time_ order. When there are no more messages to read the iterator will
321
- * finish.
322
- *
323
- * ```typescript
324
- * async function onNewRangeIterator(batchIterator) {
325
- * for await (const batch of batchIterator) {
326
- * //...
327
- * }
328
- * }
329
- * ```
330
- *
331
- * `onNewRangeIterator` is called again when the upstream topic data changes. I.E subscribing to a
332
- * user-script output topic and the user script changes, or subscribing to an aliased topic and
333
- * the alias changes. When topic data changes, the previous iterator will end, and its data is no
334
- * longer valid. When `onNewRangeIterator` is called, you should discard previously received data.
335
- *
336
- * If your `onNewRangeIterator` function throws an error, the iterator will end and you will not receive any
337
- * more messages until `onNewRangeIterator` is called again. Your error will appear in the problems sidebar
338
- * for user visibility.
339
- */
340
- onNewRangeIterator: (batchIterator: AsyncIterable<Immutable<MessageEvent[]>>) => Promise<void>;
341
-
342
- /**
343
- * @deprecated This method has been renamed. Use `onNewRangeIterator`.
344
- */
345
- onReset?: (batchIterator: AsyncIterable<Immutable<MessageEvent[]>>) => Promise<void>;
346
- };
347
-
348
- /**
349
- * The `PanelExtensionContext` exposes properties and methods for writing a custom panel. The
350
- * context has methods to subscribe for messages, receive updates, configure your panel's settings,
351
- * and render your panel to the UI.
352
- *
353
- * The {@link ExtensionPanelRegistration.initPanel | initPanel} function used in
354
- * {@link ExtensionContext.registerPanel | registerPanel} accepts a {@link PanelExtensionContext}
355
- * argument. This argument contains properties and methods for accessing panel data and rendering UI
356
- * updates. The `initPanel` function also returns an optional cleanup function to run when the
357
- * extension `panelElement` unmounts.
358
- *
359
- * See the [Creating a custom
360
- * panel](https://docs.foxglove.dev/docs/visualization/extensions/guides/create-custom-panel) guide
361
- * for more details.
362
- *
363
- * @category Custom panels
364
- */
365
- export type PanelExtensionContext = {
366
- /**
367
- * The root element for the panel. Add your panel elements as children under this element.
368
- */
369
- readonly panelElement: HTMLDivElement;
370
-
371
- /**
372
- * Initial panel state
373
- */
374
- readonly initialState: unknown;
375
-
376
- /** Actions the panel may perform related to the user's current layout. See {@link LayoutActions} for details. */
377
- readonly layout: LayoutActions;
378
-
379
- /**
380
- * Identifies the semantics of the data being played back, such as which topics or parameters
381
- * are semantically meaningful or normalization conventions to use. This typically maps to a
382
- * shorthand identifier for a robotics framework such as "ros1", "ros2", or "ulog". See the MCAP
383
- * profiles concept at <https://github.com/foxglove/mcap/blob/main/docs/specification/appendix.md#well-known-profiles>.
384
- */
385
- readonly dataSourceProfile?: string;
386
-
387
- /**
388
- * Subscribe to updates on this field within the render state. Render will only be invoked when
389
- * this field changes.
390
- *
391
- * Use `context.watch` to indicate which fields in {@link RenderState} (e.g. `currentFrame`,
392
- * `currentTime`, `previewTime`, `parameters`, `topics`) should trigger panel re-renders when
393
- * their contained values change.
394
- *
395
- * ```ts
396
- * context.watch("topics");
397
- * context.watch("currentFrame");
398
- * context.watch("parameters");
399
- * context.watch("currentTime");
400
- * ```
401
- */
402
- watch(field: keyof RenderState): void;
403
-
404
- /**
405
- * Subscribe to updates on this field within the render state. Render will only be invoked when
406
- * this field changes.
407
- *
408
- * @deprecated Calling `watch` with `allFrames` is deprecated. Use {@link PanelExtensionContext.subscribeMessageRange} instead.
409
- */
410
- watch(field: "allFrames"): void;
411
-
412
- /**
413
- * Use `context.saveState` to save an arbitrary object as persisted panel state (also known as
414
- * panel settings) in the current layout. You can view the current panel state using
415
- * [Import/export settings](https://docs.foxglove.dev/docs/visualization/panels/introduction#importexport-settings).
416
- *
417
- * ```ts
418
- * context.initialState = undefined; // your panel's initial state
419
- *
420
- * context.saveState({ myNum: 2, myBool: false, myStr: "abc" });
421
- * ```
422
- *
423
- * @param state The state to save. This value should be JSON serializable.
424
- */
425
- saveState(state: Partial<unknown>): void;
426
-
427
- /**
428
- * Use `context.setParameter` to set a parameter `name` to any valid `value` (i.e. primitives, dates, `Uint8Array`s, and arrays or objects containing these values).
429
- *
430
- * ```ts
431
- * context.setParameter("/param1", "value1");
432
- * ```
433
- *
434
- * @param name The name of the parameter to set.
435
- * @param value The new value of the parameter.
436
- */
437
- setParameter(name: string, value: ParameterValue): void;
438
-
439
- /**
440
- * Set the transient state shared by panels of the same type as the caller of this function.
441
- * This will not be persisted in the layout.
442
- */
443
- setSharedPanelState(state: undefined | Record<string, unknown>): void;
444
-
445
- /**
446
- * Use `context.setVariable` to set a
447
- * [variable](https://docs.foxglove.dev/docs/visualization/variables) `name` to any valid variable
448
- * `value`.
449
- *
450
- * ```ts
451
- * context.setVariable("myVar", 55);
452
- *
453
- * context.onRender = (renderState: RenderState, done) => {
454
- * // Read variable values from the renderState
455
- * const variableValues = renderState.variables;
456
- * const myVarValue = variableValues.myVar;
457
- *
458
- * // Call done when you've rendered all the UI for this renderState. If your UI framework delays rendering, call done when rendering has actaully happened.
459
- * done();
460
- * };
461
- * ```
462
- *
463
- * @param name The name of the variable to set.
464
- * @param value The new value of the variable.
465
- */
466
- setVariable(name: string, value: VariableValue): void;
467
-
468
- /**
469
- * Set the active preview time. Setting the preview time to undefined clears the preview time.
470
- */
471
- setPreviewTime(time: number | undefined): void;
472
-
473
- /**
474
- * Seek playback to the given time. Behaves as if the user had clicked the playback bar
475
- * to seek.
476
- *
477
- * Clients can pass a number or alternatively a Time object for greater precision.
478
- *
479
- * This property may be `undefined` if the current data source does not support seeking.
480
- */
481
- seekPlayback?(time: number | Time): void;
482
-
483
- /**
484
- * Use `context.subscribe` to indicate the topics your panel wants to receive messages for. The
485
- * messages are provided during render in {@link RenderState.currentFrame}.
486
- *
487
- * @remarks
488
- *
489
- * This method will update the current subscriptions to the new list of Subscriptions and
490
- * unsubscribe from any previously subscribed topics no longer in the Subscription list. Passing
491
- * an empty array will unsubscribe from all topics.
492
- *
493
- * ```ts
494
- * context.subscribe([{ topic: "/some/topic" }, { topic: "/another/topic" }]);
495
- * ```
496
- *
497
- * `context.subscribe([])` will unsubscribe from all topics, and is equivalent to
498
- * `unsubscribeAll`.
499
- *
500
- * #### Range loading
501
- *
502
- * Most panels display data from the current frame; examples of built-in panels that display the
503
- * current frame are 3D, Image, and Raw Message, however some panels can display data for multiple
504
- * messages or even the entire dataset duration (Plot, Map, State Transitions).
505
- *
506
- * Subscriptions will provide only the messages for the current frame. If your panel would like to
507
- * process all the available messages on a topic, use
508
- * {@link @PanelExtensionContext.subscribeMessageRange | subscribeMessageRange} instead.
509
- *
510
- * > NOTE: Message range loading is done on a best-effort basis. If your range-loaded messages
511
- * > exceed available memory limits for the browser or desktop app, then the data may not
512
- * > represent the full dataset range. Range loading results in more data transfer and memory use
513
- * > and is recommended only for panels which require access to the entire dataset.
514
- *
515
- * #### Message converters
516
- *
517
- * Message converters can convert messages from one schema to another – for example, a user might
518
- * convert custom GPS message into
519
- * [`foxglove.LocationFix`](https://docs.foxglove.dev/docs/visualization/message-schemas/location-fix)
520
- * messages for visualization in the [Map
521
- * panel](https://docs.foxglove.dev/docs/visualization/panels/map). Users may have one or more
522
- * message converters registered.
523
- *
524
- * If your panel expects messages with specific schema names, you can leverage registered message
525
- * converters to convert from one schema to another.
526
- *
527
- * Specify the `convertTo` option to enable message conversion on a topic. When conversion is
528
- * enabled for a subscription, the {@link MessageEvent}s will contain `message` entries with the
529
- * converted message rather than the original message on the topic. The original message is
530
- * available in the `originalMessageEvent` field in the message event.
531
- *
532
- * ```ts
533
- * context.subscribe([{ topic: "/some/topic", convertTo: "foxglove.LocationFix" }]);
534
- * ```
535
- *
536
- * The {@link Topic.convertibleTo | convertibleTo} field within {@link RenderState.topics} will
537
- * contain the names of schemas you can convert this topic into.
538
- */
539
- subscribe(subscriptions: Subscription[]): void;
540
-
541
- /**
542
- * @deprecated Use `subscribe` with an array of Subscription objects instead.
543
- */
544
- subscribe(topics: string[]): void;
545
-
546
- /**
547
- * Unsubscribe from all topics.
548
- *
549
- * Note: This is analagous to calling `subscribe([])` with an empty array of topics.
550
- */
551
- unsubscribeAll(): void;
552
-
553
- /**
554
- * Subscribe to any changes in application settings for an array of setting keys.
555
- *
556
- * The keys and their corresponding values are not currently documented and are subject to change.
557
- */
558
- subscribeAppSettings(settings: string[]): void;
559
-
560
- /**
561
- * Use `context.advertise` to indicate an intent to publish a specific datatype on a topic. A
562
- * panel must call `context.advertise` before being able to publish on the topic
563
- * (`context.publish`). Options are specific to the data source - some make use of options; others
564
- * do not.
565
- *
566
- * This property may be `undefined` if the current data source does not support publishing.
567
- *
568
- * ```ts
569
- * context.advertise("/my_image_topic", "sensor_msgs/Image");
570
- * ```
571
- *
572
- * `options` are specific to each data source - see documentation below for supported data
573
- * sources.
574
- *
575
- * @param topic The topic on which the extension will publish messages.
576
- * @param schemaName The name of the schema that the published messages will conform to.
577
- * @param options Options passed to the current data source for additional configuration.
578
- *
579
- * @remarks
580
- *
581
- * #### [Native (ROS 1)](https://docs.foxglove.dev/docs/connecting-to-data/frameworks/ros1#native)
582
- *
583
- * | field | type | description |
584
- * | --------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
585
- * | datatypes | `Map<string, T>` | JavaScript map of datatype names and [`MessageDefinition`](https://github.com/foxglove/message-definition/blob/main/src/types.ts#L185) definitions for a given topic |
586
- *
587
- * Common datatype definitions are available in the `@foxglove/rosmsg-msgs-common` package.
588
- *
589
- * ```ts
590
- * import { ros1 } from "@foxglove/rosmsg-msgs-common";
591
- *
592
- * context.advertise?.(currentTopic, "sensor_msgs/Joy", {
593
- * datatypes: new Map([
594
- * ["std_msgs/Header", ros1["std_msgs/Header"]],
595
- * ["std_msgs/Float32", ros1["std_msgs/Float32"]],
596
- * ["std_msgs/Int32", ros1["std_msgs/Int32"]],
597
- * ["sensor_msgs/Joy", ros1["sensor_msgs/Joy"]],
598
- * ]),
599
- * });
600
- * ```
601
- *
602
- * #### [Foxglove WebSocket](https://docs.foxglove.dev/docs/connecting-to-data/frameworks/custom#foxglove-websocket)
603
- *
604
- * `options` depend on the server implementation.
605
- *
606
- * ##### [Foxglove Bridge](https://docs.foxglove.dev/docs/connecting-to-data/ros-foxglove-bridge)
607
- *
608
- * When using the Foxglove Bridge with ROS data, use `context.dataSourceProfile` to determine the
609
- * ROS version.
610
- *
611
- * For ROS 1 data, pass datatypes the same way you would for a [native ROS 1](#native-ros-1)
612
- * connection.
613
- *
614
- * For ROS 2 data, pass datatypes using the following fields:
615
- *
616
- * | field | type | description |
617
- * | --------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
618
- * | datatypes | `Map<string, T>` | JavaScript map of datatype names and [`MessageDefinition`](https://github.com/foxglove/message-definition/blob/main/src/types.ts#L185) definitions for a given topic |
619
- *
620
- * Common datatype definitions are available in the `@foxglove/rosmsg-msgs-common` package.
621
- *
622
- * ```ts
623
- * import { ros2humble as ros2 } from "@foxglove/rosmsg-msgs-common";
624
- * // For Galactic and lower, use:
625
- * // import { ros2galactic as ros2 } from "@foxglove/rosmsg-msgs-common";
626
- * ```
627
- *
628
- * ```ts
629
- * context.advertise?.(currentTopic, "sensor_msgs/Joy", {
630
- * datatypes: new Map([
631
- * ["std_msgs/Header", ros2["std_msgs/Header"]],
632
- * ["std_msgs/Float32", ros2["std_msgs/Float32"]],
633
- * ["std_msgs/Int32", ros2["std_msgs/Int32"]],
634
- * ["sensor_msgs/Joy", ros2["sensor_msgs/Joy"]],
635
- * ]),
636
- * });
637
- * ```
638
- *
639
- * #### Rosbridge ([ROS 1](https://docs.foxglove.dev/docs/connecting-to-data/frameworks/ros1#rosbridge), [ROS 2](https://docs.foxglove.dev/docs/connecting-to-data/frameworks/ros2#rosbridge))
640
- *
641
- * No `options` required. Simply publish a JSON message with fields conforming to the advertised
642
- * datatype, and the bridge node will serialize it according to the datatype.
643
- */
644
- advertise?(topic: string, schemaName: string, options?: Record<string, unknown>): void;
645
-
646
- /**
647
- * Indicate that you no longer want to advertise on this topic.
648
- *
649
- * ```ts
650
- * context.unadvertise("/my_image_topic");
651
- * ```
652
- *
653
- * This property may be `undefined` if the current data source does not support publishing.
654
- */
655
- unadvertise?(topic: string): void;
656
-
657
- /**
658
- * Use `context.publish` to publish a message on a previously advertised topic. (You must first
659
- * call {@link PanelExtensionContext.advertise | advertise} to advertise the topic before
660
- * publishing.) If the topic is not advertised or otherwise malformed, the function will throw an
661
- * error.
662
- *
663
- * ```ts
664
- * context.advertise("/my_color_topic", "std_msgs/ColorRGBA");
665
- * context.publish("/my_color_topic", { r: 0, g: 1, b: 0, a: 1 });
666
- * ```
667
- *
668
- * This property may be `undefined` if the current data source does not support publishing.
669
- *
670
- * @param topic The name of the topic to publish the message on
671
- * @param message The message to publish
672
- */
673
- publish?(topic: string, message: unknown): void;
674
-
675
- /**
676
- * Use `context.callService` to make a service call to the specified `service` with a request payload.
677
- *
678
- * ```ts
679
- * context.callService("my_service", { foo: "bar" });
680
- * ```
681
- *
682
- * This property may be `undefined` if the current data source does not support services.
683
- *
684
- * @param service The name of the service to call
685
- * @param request The request payload for the service call
686
- * @returns A promise that resolves when the result is available or rejected with an error
687
- */
688
- callService?(service: string, request: unknown): Promise<unknown>;
689
-
690
- /**
691
- * Set this property to a function during your panel's
692
- * {@link ExtensionPanelRegistration.initPanel | initialization}.
693
- *
694
- * Foxglove will run `context.onRender` whenever your panel needs to re-render during playback.
695
- * The function accepts `renderState` and a `done` callback as its arguments. Render events occur
696
- * frequently (60hz, 30hz, etc).
697
- *
698
- * **Note**: Your `onRender` function **must** call `done` after rendering to indicate that the
699
- * panel is ready to render the next set of data. The exact placement of this `done` invocation
700
- * will vary between frameworks and different extensions' logic.
701
- *
702
- * ```ts
703
- * context.onRender = (renderState, done) => {
704
- * // Render your UI updates with fields from RenderState
705
- *
706
- * // Call done when you've rendered all the UI for this renderState.
707
- * // If your UI framework delays rendering, call done when rendering has actually happened.
708
- * done();
709
- * };
710
- * ```
711
- */
712
- onRender?: (renderState: Immutable<RenderState>, done: () => void) => void;
713
-
714
- /**
715
- * Call the `updatePanelSettingsEditor` method on your panel's {@link PanelExtensionContext}
716
- * instance to define or update its settings.
717
- *
718
- * ```ts
719
- * const panelSettings: SettingsTree = {
720
- * nodes: { ... },
721
- * actionHandler: (action: SettingsTreeAction) => { ... }
722
- * };
723
- *
724
- * context.updatePanelSettingsEditor(panelSettings);
725
- * ```
726
- *
727
- * The `settings` argument must be a valid {@link SettingsTree} and include 2 mandatory properties
728
- * – `nodes` and `actionHandler`:
729
- *
730
- * - `nodes` - Hierarchical structure where each node can contain input fields, display fields, or
731
- * even other nodes
732
- * - `actionHandler` - Function that is invoked when the user interacts with the settings UI;
733
- * contains logic to process the interactions and update the panel or settings tree
734
- *
735
- * It can also include the following optional properties:
736
- *
737
- * - `enableFilter` – Whether the settings should show the filter control
738
- * - `focusedPath` – Node to scroll to (transient one-time effect)
739
- *
740
- * The example tree below has a `title` text input field inside a `General` section along with an
741
- * `actionHandler` to respond to updates for the `title` field.
742
- *
743
- * ```ts
744
- * const panelSettings: SettingsTree = {
745
- * nodes: {
746
- * general: {
747
- * label: "General",
748
- * fields: {
749
- * title: "{
750
- * label: "Title",
751
- * input: "string",
752
- * // `panelTitle` refers to a value in your extension panel's config
753
- * value: panelTitle,
754
- * },
755
- * },
756
- * },
757
- * },
758
- * actionHandler: (action: SettingsTreeAction) => {
759
- * switch (action.action) {
760
- * case "perform-node-action":
761
- * // Handle user-defined actions for nodes in the settings tree
762
- * break;
763
- * case "update":
764
- * if (action.payload.path[0] === "general" && action.payload.path[1] === "title") {
765
- * // Read action.payload.value for the new panel title value
766
- * panelTitle = action.payload.value;
767
- *
768
- * // Update your panel's state accordingly
769
- * }
770
- * break;
771
- * }
772
- * },
773
- * }
774
- *
775
- * context.updatePanelSettingsEditor(panelSettings);
776
- * ```
777
- *
778
- * #### `SettingsTreeAction`
779
- *
780
- * A {@link SettingsTreeAction} describes how the settings UI should update when a user interacts
781
- * with its fields.
782
- *
783
- * Each `SettingsTreeAction` has a `payload` with a `path` to the settings field to update (e.g.
784
- * `["general", "title"]`).
785
- *
786
- * The `update` action corresponds to a user setting a new value for a field (e.g. "My new
787
- * title").
788
- *
789
- * #### Special node properties
790
- *
791
- * There are two special {@link SettingsTreeNode} properties, `label` and `visibility`. The value
792
- * you specify for `label` will control the label displayed in the settings editor. If you set the
793
- * `renamable` node property to `true`, the user can edit the node `label` – you will receive a
794
- * `SettingsTreeAction` of `update` with a path ending in `label`.
795
- *
796
- * In addition, if you specify a boolean value for `visibility` of the node then the settings
797
- * editor will provide a button to toggle the visibility of the node and you will receive an
798
- * `update` action with `visibility` as the final element in the path.
799
- *
800
- * For an example of how to use these special properties, check out the [panel settings example
801
- * extension](https://github.com/foxglove/create-foxglove-extension/tree/main/examples/panel-settings).
802
- *
803
- * #### Input types
804
- *
805
- * In addition to the `string` input type in the example above, the panel API provides a wide
806
- * array of types for your extension panel input fields.
807
- *
808
- * Each input type has different properties that you can configure:
809
- *
810
- * - `autocomplete`
811
- * - `boolean`
812
- * - `rgb`
813
- * - `rgba`
814
- * - `gradient`
815
- * - `messagepath`
816
- * - `select`
817
- * - `string`
818
- * - `toggle`
819
- * - `vec3`
820
- * - `vec2`
821
- */
822
- updatePanelSettingsEditor(settings: Immutable<SettingsTree>): void;
823
-
824
- /**
825
- * Use `context.setDefaultPanelTitle` to override a panel's default title. Users can always
826
- * override the default title by editing it manually. If no override or default title is set, the
827
- * panel will simply display its type (e.g. "Image").
828
- *
829
- * ```ts
830
- * // Override the default panel title
831
- * context.setDefaultPanelTitle(`Plot of ${config.topicName}`);
832
- * ```
833
- */
834
- setDefaultPanelTitle(defaultTitle: string | undefined): void;
835
- /**
836
- * Subscribe to receive the entire time range of messages for a given topic for the current data source.
837
- *
838
- * See {@link SubscribeMessageRangeArgs} for more information on behavior.
839
- *
840
- * Note: This will not read messages for live sources, like foxglove_bridge, rosbridge, or ROS 1
841
- * native connections. For those messages you will still need to use `context.subscribe()` and
842
- * `watch("currentFrame")`.
843
- *
844
- * @returns A function that will unsubscribe from the topic, cancel the active async iterator,
845
- * and prevent {@link SubscribeMessageRangeArgs.onNewRangeIterator | onNewRangeIterator} from being called again.
846
- */
847
- subscribeMessageRange?: (args: SubscribeMessageRangeArgs) => () => void;
848
-
849
- /**
850
- * @deprecated Renamed to `subscribeMessageRange`. Please use that method instead.
851
- */
852
- UNSTABLE_subscribeMessageRange?: (args: SubscribeMessageRangeArgs) => () => void;
853
- };
854
-
855
- /**
856
- * This type represents the arguments you pass to {@link ExtensionContext.registerPanel}.
857
- *
858
- * @category Custom panels
859
- */
860
- export type ExtensionPanelRegistration = {
861
- /**
862
- * Unique name of the panel within your extension
863
- *
864
- * NOTE: Panel names within your extension must be unique. The panel name identifies this panel
865
- * within a layout. Changing the panel name will cause layouts using the old name unable to load
866
- * your panel.
867
- */
868
- name: string;
869
-
870
- /**
871
- * This function is invoked when your panel is initialized
872
- *
873
- * @returns (optional) A function which will be called when the panel is removed or replaced.
874
- * Perform any cleanup logic here to gracefully tear down your panel.
875
- */
876
- initPanel: (context: PanelExtensionContext) => void | (() => void);
877
- };
878
-
879
- /**
880
- * This type represents the arguments you pass to {@link ExtensionContext.registerMessageConverter}.
881
- *
882
- * @category Message converters
883
- */
884
- export type RegisterMessageConverterArgs<Src> = {
885
- fromSchemaName: string;
886
- toSchemaName: string;
887
- converter: (msg: Src, event: Immutable<MessageEvent<Src>>) => unknown;
888
- };
889
-
890
- /** @category Topic aliases */
891
- export type BaseTopic = { name: string; schemaName?: string };
892
- /** @category Topic aliases */
893
- export type TopicAlias = { name: string; sourceTopicName: string };
894
-
895
- /**
896
- * A TopicAliasFunction takes a list of data source topics and variables and outputs
897
- * a list of aliased topics. Register this function using {@link ExtensionContext.registerTopicAliases}.
898
- *
899
- * @category Topic aliases
900
- */
901
- export type TopicAliasFunction = (
902
- args: Immutable<{
903
- topics: BaseTopic[];
904
- globalVariables: Readonly<Record<string, VariableValue>>;
905
- }>,
906
- ) => TopicAlias[];
907
-
908
- /**
909
- * The {@link ExtensionModule.activate | activate} function's first argument is an
910
- * `ExtensionContext` — this context allows you to extend Foxglove for your custom workflows.
911
- *
912
- * ```typescript
913
- * export function activate(extensionContext: ExtensionContext) {
914
- * // ... call methods on the extensionContext to extend Foxglove
915
- * }
916
- * ```
917
- *
918
- * @category Entry point
919
- */
920
- export interface ExtensionContext {
921
- /**
922
- * @deprecated This field is no longer used.
923
- * @hidden
924
- */
925
- readonly mode: "production" | "development" | "test";
926
-
927
- /**
928
- * `registerPanel` adds a new panel to the Foxglove interface. To register a panel you provide a
929
- * `name` and an `initPanel` function.
930
- *
931
- * The `initPanel` function accepts a {@link PanelExtensionContext} argument, which contains
932
- * properties and methods for accessing panel data and rendering UI updates. It also returns an
933
- * optional cleanup function to run when the extension `panelElement` unmounts.
934
- *
935
- * See the [Creating a custom
936
- * panel](https://docs.foxglove.dev/docs/visualization/extensions/guides/create-custom-panel)
937
- * guide for more details.
938
- */
939
- registerPanel(params: ExtensionPanelRegistration): void;
940
-
941
- /**
942
- * `registerMessageConverter` registers a function to convert messages from one schema to another.
943
- *
944
- * Message converters allow you to leverage Foxglove's built-in visualization panels by
945
- * transforming messages to adhere to Foxglove-supported schemas — for example, you can convert
946
- * your custom GPS messages to
947
- * [`foxglove.LocationFix`](https://docs.foxglove.dev/docs/visualization/message-schemas/location-fix)
948
- * messages for visualization in the [Map
949
- * panel](https://docs.foxglove.dev/docs/visualization/panels/map).
950
- *
951
- * Whenever a panel subscribes to a topic with the
952
- * [`convertTo`](https://docs.foxglove.dev/docs/visualization/extensions/api/panel#message-converters)
953
- * option, the converter function runs on the original message and outputs the converted message,
954
- * which it then provides it to the panel. If the function returns `undefined`, the output is
955
- * ignored, and no message is provided to the panel. This is useful if you want to selectively
956
- * output converted messages depending on the input messages' contents.
957
- *
958
- * See the [Creating a message
959
- * converter](https://docs.foxglove.dev/docs/visualization/extensions/guides/create-message-converter)
960
- * guide for more details.
961
-
962
- */
963
- registerMessageConverter<Src>(args: RegisterMessageConverterArgs<Src>): void;
964
-
965
- /**
966
- * `registerTopicAliases` registers a function to compute topic aliases. The provided alias
967
- * function should accept an argument with two fields – `topics` with the data source's original
968
- * topics and `globalVariables` with the current layout's variables – and return a list of aliased
969
- * topics.
970
- *
971
- * Your alias function runs whenever there are changes to the data source topics or variables. Any
972
- * aliases it returns are added to the data source topics (replacing any previously returned
973
- * aliases) and available for subscribing or use within message paths as if they were real topics.
974
- */
975
- registerTopicAliases(aliasFunction: TopicAliasFunction): void;
976
- }
977
-
978
- /**
979
- * @inline
980
- * @hidden
981
- */
982
- export type ExtensionActivate = (extensionContext: ExtensionContext) => void;
983
-
984
- /**
985
- * ExtensionModule describes the interface your extension module must export. This typically corresponds to your `index.ts` file.
986
- *
987
- * You may use either a `default` export or named export syntax:
988
- *
989
- * ```typescript
990
- * export function activate(context: ExtensionContext) {
991
- * // ... call methods on the extensionContext to extend Foxglove
992
- * }
993
- * ```
994
- *
995
- * ```typescript
996
- * function activate(context: ExtensionContext) {
997
- * // ... call methods on the extensionContext to extend Foxglove
998
- * }
999
- * export default { activate };
1000
- * ```
1001
- *
1002
- * @category Entry point
1003
- */
1004
- export interface ExtensionModule {
1005
- /**
1006
- * This function will be called when your extension is loaded. In this function, you can register
1007
- * your custom panels or other types of extension features.
1008
- */
1009
- activate: ExtensionActivate;
1010
- }
1011
-
1012
- /**
1013
- * Icons that can be displayed in the settings tree. Most icon names come from the Material Design
1014
- * icon set, but the exact icons displayed may change in the future.
1015
- *
1016
- * @category Custom panels
1017
- */
1018
- export type SettingsIcon =
1019
- | "Add"
1020
- | "Addchart"
1021
- | "AutoAwesome"
1022
- | "Background"
1023
- | "Camera"
1024
- | "Cells"
1025
- | "Check"
1026
- | "Circle"
1027
- | "Clear"
1028
- | "Clock"
1029
- | "Collapse"
1030
- | "Cube"
1031
- | "Delete"
1032
- | "Expand"
1033
- | "Flag"
1034
- | "Folder"
1035
- | "FolderOpen"
1036
- | "Grid"
1037
- | "Hive"
1038
- | "ImageProjection"
1039
- | "Map"
1040
- | "Move"
1041
- | "MoveDown"
1042
- | "MoveUp"
1043
- | "NorthWest"
1044
- | "Note"
1045
- | "NoteFilled"
1046
- | "Points"
1047
- | "PrecisionManufacturing"
1048
- | "Radar"
1049
- | "Settings"
1050
- | "Shapes"
1051
- | "Share"
1052
- | "Star"
1053
- | "SouthEast"
1054
- | "Timeline"
1055
- | "Topic"
1056
- | "Walk"
1057
- | "World";
1058
-
1059
- /**
1060
- * A settings tree field specifies the input type and the value of a field
1061
- * in the settings editor.
1062
- *
1063
- * @category Custom panels
1064
- */
1065
- export type SettingsTreeFieldValue =
1066
- | {
1067
- input: "autocomplete";
1068
- value?: string;
1069
- items: string[];
1070
-
1071
- /**
1072
- * Optional placeholder text displayed in the field input when value is undefined
1073
- */
1074
- placeholder?: string;
1075
- }
1076
- | { input: "boolean"; value?: boolean }
1077
- | {
1078
- input: "rgb";
1079
- value?: string;
1080
-
1081
- /**
1082
- * Optional placeholder text displayed in the field input when value is undefined
1083
- */
1084
- placeholder?: string;
1085
-
1086
- /**
1087
- * Optional field that's true if the clear button should be hidden.
1088
- */
1089
- hideClearButton?: boolean;
1090
- }
1091
- | {
1092
- input: "rgba";
1093
- value?: string;
1094
-
1095
- /**
1096
- * Optional placeholder text displayed in the field input when value is undefined
1097
- */
1098
- placeholder?: string;
1099
-
1100
- /**
1101
- * Optional field that's true if the clear button should be hidden.
1102
- */
1103
- hideClearButton?: boolean;
1104
- }
1105
- | { input: "gradient"; value?: [string, string] }
1106
- | {
1107
- input: "messagepath";
1108
- value?: string;
1109
- validTypes?: string[];
1110
- /** True if the input should allow math modifiers like @abs. */
1111
- supportsMathModifiers?: boolean;
1112
- }
1113
- | {
1114
- input: "number";
1115
- value?: number;
1116
- step?: number;
1117
- max?: number;
1118
- min?: number;
1119
- precision?: number;
1120
-
1121
- /**
1122
- * Optional placeholder text displayed in the field input when value is undefined
1123
- */
1124
- placeholder?: string;
1125
- }
1126
- | {
1127
- input: "select";
1128
- value?: number | number[];
1129
- options: Array<{ label: string; value: undefined | number; disabled?: boolean }>;
1130
- }
1131
- | {
1132
- input: "select";
1133
- value?: string | string[];
1134
- options: Array<{ label: string; value: undefined | string; disabled?: boolean }>;
1135
- }
1136
- | {
1137
- input: "string";
1138
- value?: string;
1139
-
1140
- /**
1141
- * Optional placeholder text displayed in the field input when value is undefined
1142
- */
1143
- placeholder?: string;
1144
- }
1145
- | {
1146
- input: "toggle";
1147
- value?: string;
1148
- options: string[] | Array<{ label: string; value: undefined | string }>;
1149
- }
1150
- | {
1151
- input: "toggle";
1152
- value?: number;
1153
- options: number[] | Array<{ label: string; value: undefined | number }>;
1154
- }
1155
- | {
1156
- input: "vec3";
1157
- value?: [undefined | number, undefined | number, undefined | number];
1158
- placeholder?: [undefined | string, undefined | string, undefined | string];
1159
- step?: number;
1160
- precision?: number;
1161
- labels?: [string, string, string];
1162
- max?: number;
1163
- min?: number;
1164
- }
1165
- | {
1166
- input: "vec2";
1167
- value?: [undefined | number, undefined | number];
1168
- placeholder?: [undefined | string, undefined | string];
1169
- step?: number;
1170
- precision?: number;
1171
- labels?: [string, string];
1172
- max?: number;
1173
- min?: number;
1174
- };
1175
-
1176
- /**
1177
- * A settings tree field specifies the input type and the value of a field
1178
- * in the settings editor.
1179
- *
1180
- * @category Custom panels
1181
- */
1182
- export type SettingsTreeField = SettingsTreeFieldValue & {
1183
- /**
1184
- * True if the field is disabled.
1185
- */
1186
- disabled?: boolean;
1187
-
1188
- /**
1189
- * Optional help text to explain the purpose of the field.
1190
- */
1191
- help?: string;
1192
-
1193
- /**
1194
- * The label displayed alongside the field.
1195
- */
1196
- label: string;
1197
-
1198
- /**
1199
- * True if the field is readonly.
1200
- */
1201
- readonly?: boolean;
1202
-
1203
- /**
1204
- * Optional message indicating any error state for the field.
1205
- */
1206
- error?: string;
1207
- };
1208
-
1209
- /**
1210
- * @category Custom panels
1211
- */
1212
- export type SettingsTreeFields = Record<string, undefined | SettingsTreeField>;
1213
-
1214
- /**
1215
- * @category Custom panels
1216
- */
1217
- export type SettingsTreeChildren = Record<string, undefined | SettingsTreeNode>;
1218
-
1219
- /**
1220
- * An action included in the action menu for a settings node.
1221
- *
1222
- * @category Custom panels
1223
- */
1224
- export type SettingsTreeNodeActionItem = {
1225
- type: "action";
1226
-
1227
- /**
1228
- * A unique idenfier for the action.
1229
- */
1230
- id: string;
1231
-
1232
- /**
1233
- * A descriptive label for the action.
1234
- */
1235
- label: string;
1236
-
1237
- /**
1238
- * Optional icon to display with the action.
1239
- */
1240
- icon?: SettingsIcon;
1241
-
1242
- /**
1243
- * Specifies whether the item is rendered as an inline action or as an item in the
1244
- * context menu. Defaults to "menu" if not specified. Inline items will be rendered
1245
- * as an icon only if their icon is specified.
1246
- */
1247
- display?: "menu" | "inline";
1248
- };
1249
-
1250
- /**
1251
- * @category Custom panels
1252
- */
1253
- export type SettingsTreeNodeActionDivider = { type: "divider" };
1254
-
1255
- /**
1256
- * An action included in the action menu for a settings node.
1257
- *
1258
- * @category Custom panels
1259
- */
1260
- export type SettingsTreeNodeAction = SettingsTreeNodeActionItem | SettingsTreeNodeActionDivider;
1261
-
1262
- /**
1263
- * A node represents a single item or group of items in the settings tree.
1264
- *
1265
- * @category Custom panels
1266
- */
1267
- export type SettingsTreeNode = {
1268
- /**
1269
- * An array of actions that can be performed on this node.
1270
- */
1271
- actions?: SettingsTreeNodeAction[];
1272
-
1273
- /**
1274
- * Other settings tree nodes nested under this node.
1275
- */
1276
- children?: SettingsTreeChildren;
1277
-
1278
- /**
1279
- * Set to collapsed if the node should be initially collapsed.
1280
- */
1281
- defaultExpansionState?: "collapsed" | "expanded";
1282
-
1283
- /**
1284
- * Optional message indicating any error state for the node.
1285
- */
1286
- error?: string;
1287
-
1288
- /**
1289
- * Field inputs attached directly to this node.
1290
- */
1291
- fields?: SettingsTreeFields;
1292
-
1293
- /**
1294
- * Optional icon to display next to the node label.
1295
- */
1296
- icon?: SettingsIcon;
1297
-
1298
- /**
1299
- * An optional label shown at the top of this node.
1300
- */
1301
- label?: string;
1302
-
1303
- /**
1304
- * True if the node label can be edited by the user.
1305
- */
1306
- renamable?: boolean;
1307
-
1308
- /**
1309
- * Optional sort order to override natural object ordering. All nodes
1310
- * with a sort order will be rendered before nodes all with no sort order.
1311
- *
1312
- * Nodes without an explicit order will be ordered according to ECMA
1313
- * object ordering rules.
1314
- *
1315
- * https://262.ecma-international.org/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
1316
- */
1317
- order?: number | string;
1318
-
1319
- /**
1320
- * An optional visibility status. If this is not undefined, the node
1321
- * editor will display a visiblity toggle button and send update actions
1322
- * to the action handler.
1323
- **/
1324
- visible?: boolean;
1325
-
1326
- /**
1327
- * Filter Children by visibility status
1328
- */
1329
- enableVisibilityFilter?: boolean;
1330
- };
1331
-
1332
- /**
1333
- * Distributes Pick<T, K> across all members of a union, used for extracting structured
1334
- * subtypes.
1335
- */
1336
- type DistributivePick<T, K extends keyof T> = T extends unknown ? Pick<T, K> : never;
1337
-
1338
- /**
1339
- * Represents actions that can be dispatched to source of the SettingsTree to implement
1340
- * edits and updates.
1341
- *
1342
- * @category Custom panels
1343
- */
1344
- export type SettingsTreeAction =
1345
- | {
1346
- action: "update";
1347
- payload: { path: readonly string[] } & DistributivePick<
1348
- SettingsTreeFieldValue,
1349
- "input" | "value"
1350
- >;
1351
- }
1352
- | {
1353
- action: "perform-node-action";
1354
- payload: { id: string; path: readonly string[] };
1355
- };
1356
-
1357
- /**
1358
- * @inline
1359
- * @hidden
1360
- */
1361
- export type SettingsTreeNodes = Record<string, undefined | SettingsTreeNode>;
1362
-
1363
- /**
1364
- * A settings tree is a tree of panel settings that can be displayed and edited in
1365
- * the panel settings sidebar.
1366
- *
1367
- * Nodes and fields in the tree can be referred to by a string path, which collects
1368
- * the keys of each node on the path from the root to the child node or field.
1369
- *
1370
- * For example, for the following tree:
1371
- *
1372
- * ```json
1373
- * root: {
1374
- * children: {
1375
- * a: {
1376
- * children: {
1377
- * b: {
1378
- * fields: {
1379
- * toggleMe: {
1380
- * label: "Toggle me",
1381
- * input: "boolean",
1382
- * value: false,
1383
- * },
1384
- * },
1385
- * },
1386
- * },
1387
- * },
1388
- * },
1389
- * },
1390
- * ```
1391
- *
1392
- * the path to the node at b would be `["a", "b"]` and the path to the toggleMe
1393
- * field would be `["a", "b", "toggleMe"]`. These paths are used in the
1394
- * actionHandler, which responds to updates to values in the tree, and also in
1395
- * the focusedPath, which is used to focus the editor UI at a particular node
1396
- * in the tree.
1397
- *
1398
- * @category Custom panels
1399
- */
1400
- export type SettingsTree = {
1401
- /**
1402
- * Handler to process all actions on the settings tree initiated by the UI.
1403
- */
1404
- actionHandler: (action: SettingsTreeAction) => void;
1405
-
1406
- /**
1407
- * True if the settings editor should show the filter control.
1408
- */
1409
- enableFilter?: boolean;
1410
-
1411
- /**
1412
- * Setting this will have a one-time effect of scrolling the editor to the
1413
- * node at the path and highlighting it. This is a transient effect so it is
1414
- * not necessary to subsequently unset this.
1415
- */
1416
- focusedPath?: readonly string[];
1417
-
1418
- /**
1419
- * The settings tree root nodes. Updates to these will automatically be
1420
- * reflected in the editor UI.
1421
- */
1422
- nodes: SettingsTreeNodes;
1423
- };
3
+ export type { Experimental } from "./experimental";