@swiftbrowser/web 0.17.8 → 0.18.0

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/protocol.ts CHANGED
@@ -455,6 +455,8 @@ export interface TextRun {
455
455
  italic?: boolean;
456
456
  /** A ShapeStyle JSON object (as `styled` `foreground`). */
457
457
  foreground?: unknown;
458
+ /** A Color JSON object behind the run's glyphs (`AttributedString`'s `backgroundColor`). */
459
+ background?: unknown;
458
460
  /** A line through or under the run; `color` defaults to the run's own. */
459
461
  strikethrough?: { color?: unknown };
460
462
  underline?: { color?: unknown };
@@ -479,7 +481,8 @@ export interface HStackProps {
479
481
  export interface SpacerProps {
480
482
  minLength: number | null;
481
483
  }
482
- export type ButtonStyle = 'automatic' | 'plain' | 'bordered' | 'borderedProminent' | 'glass';
484
+ /** `borderless` is `.borderless`: text-only in the tint, as `automatic`, and SwiftUI's own natively. */
485
+ export type ButtonStyle = 'automatic' | 'plain' | 'borderless' | 'bordered' | 'borderedProminent' | 'glass';
483
486
  /** Phase 8: `cancel` is the role an `alert` draws apart (bold, or in its own card). */
484
487
  export type ButtonRole = 'destructive' | 'cancel' | null;
485
488
  /**
@@ -623,10 +626,22 @@ export interface TextFieldProps {
623
626
  placeholder: string;
624
627
  style: TextFieldStyle;
625
628
  secure: boolean;
626
- /** Phase 12 `.focused(...)`: whether the field should have focus; present only on bound fields, which report `focus` events. */
629
+ /**
630
+ * Phase 12 `.focused(...)`: whether the field should have focus; present
631
+ * only on bound fields, which report `focus` events. The value the app's
632
+ * pass for a `submit` leaves (true again, or another field's turned true)
633
+ * wins over the platform's dismissal of the keyboard, and a field turning
634
+ * true is scrolled into view ("Focus across a submit").
635
+ */
627
636
  focused?: boolean;
628
637
  /** Phase 12 `.onSubmit`: the Enter key sends a `submit` event. */
629
638
  submit?: boolean;
639
+ /**
640
+ * `onDeleteBackward`: Backspace with the caret at the start of the text (an
641
+ * empty text included) sends `deletebackward` instead of deleting nothing
642
+ * (docs/ops-protocol.md, "The delete key at the start of a field").
643
+ */
644
+ deleteBackward?: boolean;
630
645
  /** Phase 12 `.submitLabel`: the return key's label (`enterkeyhint`). */
631
646
  submitLabel?: string;
632
647
  /** `.keyboardType(_:)`: a `UIKeyboardType` case name (`emailAddress`, `URL`, `numberPad`…), the on-screen keyboard (`inputmode`). */
@@ -638,8 +653,12 @@ export interface TextFieldProps {
638
653
  /** `.autocorrectionDisabled()` (`autocorrect="off"`). */
639
654
  autocorrectionDisabled?: boolean;
640
655
  }
656
+ /** `.toggleStyle`: the switch (`automatic`, `switch`) or `button`, a button tinted while on. */
657
+ export type ToggleStyle = 'automatic' | 'switch' | 'button';
641
658
  export interface ToggleProps {
642
659
  isOn: boolean;
660
+ /** `.toggleStyle(_:)`; absent is `automatic` (the switch). */
661
+ style?: ToggleStyle;
643
662
  }
644
663
  export type NavStackProps = Record<string, never>;
645
664
  export type NavDisplayMode = 'automatic' | 'large' | 'inline';
@@ -918,9 +937,12 @@ export interface MenuProps {
918
937
  * `largeTitle` (iOS 26 `.largeTitle`): the items take the large title row in
919
938
  * place of the title's text, scrolling with the content as the title does;
920
939
  * the bar shows the title once the row has gone under it. Hidden while the
921
- * screen's title is inline.
940
+ * screen's title is inline. `keyboard` (`.keyboard`): a 44 bar across the
941
+ * screen above the on-screen keyboard while a text input of the screen is
942
+ * focused (the input accessory view), its items in a row from the leading
943
+ * edge, a `spacer` child flexible; hidden otherwise.
922
944
  */
923
- export type ToolbarPlacement = 'trailing' | 'leading' | 'principal' | 'bottom' | 'secondary' | 'largeTitle';
945
+ export type ToolbarPlacement = 'trailing' | 'leading' | 'principal' | 'bottom' | 'secondary' | 'largeTitle' | 'keyboard';
924
946
  /**
925
947
  * `.toolbar { ToolbarItem(placement:) { ... } }`, an extra child of a
926
948
  * `navscreen` after its content. The children are the items, laid out at
@@ -958,6 +980,15 @@ export interface SearchFieldProps {
958
980
  */
959
981
  export interface TapGestureProps {
960
982
  location?: boolean;
983
+ /**
984
+ * `.onTapGesture(count:)`: the taps required, 1 when absent. With 2 the
985
+ * element sends one `tap` for two clicks within the double-tap interval
986
+ * (300 ms); a `tapgesture` of one tap around or inside it waits that
987
+ * interval before sending its own, as iOS delays a single tap while a
988
+ * double tap could still land (docs/ops-protocol.md, "Gesture spaces and
989
+ * composition").
990
+ */
991
+ count?: number;
961
992
  }
962
993
 
963
994
  /**
@@ -1076,7 +1107,7 @@ export interface DefaultsOp {
1076
1107
  export interface RequestOp {
1077
1108
  op: 'request';
1078
1109
  id: number;
1079
- kind: 'fetch' | 'image' | 'share' | 'pickFiles' | 'notification' | 'scanner' | 'websocket' | 'openURL' | 'webAuth' | 'media' | 'download' | 'clipboard' | 'translate' | 'pickColor' | 'mediaSession';
1110
+ kind: 'fetch' | 'image' | 'share' | 'pickFiles' | 'notification' | 'scanner' | 'websocket' | 'openURL' | 'webAuth' | 'media' | 'download' | 'clipboard' | 'translate' | 'pickColor' | 'mediaSession' | 'undo';
1080
1111
  params:
1081
1112
  | FetchRequestParams
1082
1113
  | ImageRequestParams
@@ -1092,7 +1123,21 @@ export interface RequestOp {
1092
1123
  | ClipboardRequestParams
1093
1124
  | TranslateRequestParams
1094
1125
  | PickColorRequestParams
1095
- | MediaSessionRequestParams;
1126
+ | MediaSessionRequestParams
1127
+ | UndoRequestParams;
1128
+ }
1129
+ /**
1130
+ * `undo` (docs/ops-protocol.md, "Undo"): what the Swift app's `UndoManager`
1131
+ * can do, after a turn that changed it, for the page's undo host
1132
+ * (src/undo.ts). Answered `{}`; the app does not wait for it. The page's
1133
+ * ⌘Z and ⇧⌘Z come back as `UndoEvent`s.
1134
+ */
1135
+ export interface UndoRequestParams {
1136
+ canUndo: boolean;
1137
+ canRedo: boolean;
1138
+ /** The name of what undo takes back, '' for none. */
1139
+ undoActionName: string;
1140
+ redoActionName: string;
1096
1141
  }
1097
1142
  /**
1098
1143
  * `translate` (Translation; src/translation.ts): `availability` of a pair
@@ -1452,7 +1497,12 @@ export type RendererEvent =
1452
1497
  | VideoEvent
1453
1498
  | LazyEvent
1454
1499
  | MenuEvent
1455
- | HoverEvent;
1500
+ | HoverEvent
1501
+ // Scroll geometry and animation completion (the block at the end of this file)
1502
+ | ScrollGeometryEvent
1503
+ | AnimationCompleteEvent
1504
+ // The page's undo and redo keys, for the app's undo manager (src/undo.ts)
1505
+ | UndoEvent;
1456
1506
 
1457
1507
  /**
1458
1508
  * Phase 13: the page's answer to the `request` op with this `id`: `value`
@@ -1810,8 +1860,28 @@ export interface GestureProps {
1810
1860
  rotate?: boolean;
1811
1861
  minimumScaleDelta?: number;
1812
1862
  minimumAngleDelta?: number;
1863
+ /**
1864
+ * `DragGesture(coordinateSpace:)`: the space of a `drag` event's `location`
1865
+ * and `startLocation`. `local` (the default) is the element's own box;
1866
+ * `global` the root's coordinates, scrolling included, the same a
1867
+ * `reportsFrame` geometry event reports, so a named space is the global
1868
+ * point less that space's reported origin.
1869
+ */
1870
+ coordinateSpace?: GestureCoordinateSpace;
1871
+ /**
1872
+ * How the gesture composes with the others on the pointer's way to the
1873
+ * root (src/interaction.ts). Absent, `.gesture`: the innermost gesture
1874
+ * whose motion matches wins. `highPriority` (`.highPriorityGesture`): asked
1875
+ * before the gestures of the views inside it. `simultaneous`
1876
+ * (`.simultaneousGesture`): begins when its own distance is met and runs
1877
+ * beside whatever else owns the pointer, never claiming it from them.
1878
+ */
1879
+ mode?: GestureMode;
1813
1880
  }
1814
1881
 
1882
+ export type GestureCoordinateSpace = 'local' | 'global';
1883
+ export type GestureMode = 'simultaneous' | 'highPriority';
1884
+
1815
1885
  /**
1816
1886
  * `.swipeActions`: children are `[content, ...trailing actions, ...leading
1817
1887
  * actions]` (`trailing` and `leading` count them); the actions are `button`s
@@ -1895,6 +1965,11 @@ export interface ListProps {
1895
1965
  export interface ScrollViewProps {
1896
1966
  refreshable?: boolean;
1897
1967
  refreshing?: boolean;
1968
+ /**
1969
+ * `.scrollIndicators(.hidden)` (or `ScrollView(showsIndicators: false)`):
1970
+ * false hides the scroll bars natively; the page draws none either way.
1971
+ */
1972
+ showsIndicators?: boolean;
1898
1973
  /** Phase 12 `.scrollTargetBehavior`: CSS scroll snapping on the axis. */
1899
1974
  snap?: 'viewAligned' | 'paging';
1900
1975
  /**
@@ -1916,6 +1991,13 @@ export interface ScrollViewProps {
1916
1991
  export interface Style {
1917
1992
  /** `.allowsHitTesting(false)`: `pointer-events: none` on the subtree. */
1918
1993
  hitTesting?: boolean;
1994
+ /**
1995
+ * `.contentShape(Rectangle())`: the whole box takes taps and gestures,
1996
+ * whatever is drawn in it (a clear fill, a stroke, the gaps between
1997
+ * views). Natively the modifier itself; the page's boxes already take
1998
+ * input across their area, so it only marks the box (`data-sb-content-shape`).
1999
+ */
2000
+ contentShape?: 'rectangle';
1919
2001
  }
1920
2002
 
1921
2003
  export interface Style {
@@ -1928,6 +2010,54 @@ export interface Style {
1928
2010
  scrollVisibility?: { threshold: number };
1929
2011
  }
1930
2012
 
2013
+ /**
2014
+ * The effects a scroll transition draws in one phase: the visual-effect
2015
+ * fields of a `Style` (none of them changes the layout).
2016
+ */
2017
+ export type ScrollTransitionEffect = Pick<
2018
+ Style,
2019
+ | 'offset'
2020
+ | 'scale'
2021
+ | 'scaleAnchor'
2022
+ | 'rotation'
2023
+ | 'rotationAnchor'
2024
+ | 'rotation3D'
2025
+ | 'opacity'
2026
+ | 'blur'
2027
+ | 'brightness'
2028
+ | 'contrast'
2029
+ | 'saturation'
2030
+ | 'grayscale'
2031
+ | 'hueRotation'
2032
+ >;
2033
+
2034
+ /**
2035
+ * `.scrollTransition(_:axis:transition:)`: the transition's effects in its
2036
+ * three phases (its closure called once for each), which the renderer draws
2037
+ * as the box moves through its scroll view, without a round trip.
2038
+ */
2039
+ export interface ScrollTransition {
2040
+ /**
2041
+ * `interactive`: between phases as the box crosses the scroll view's edge,
2042
+ * by how far it is out (0 wholly in view, 1 wholly out); `animated`: the
2043
+ * phase's effects, animated, once the box is partly out; `identity`: the
2044
+ * identity phase's always.
2045
+ */
2046
+ configuration: 'interactive' | 'animated' | 'identity';
2047
+ /** The axis it follows; the scroll view's own when absent. */
2048
+ axis?: 'horizontal' | 'vertical';
2049
+ identity: ScrollTransitionEffect;
2050
+ /** Going out past the top or leading edge. */
2051
+ topLeading: ScrollTransitionEffect;
2052
+ /** Going out past the bottom or trailing edge. */
2053
+ bottomTrailing: ScrollTransitionEffect;
2054
+ }
2055
+
2056
+ export interface Style {
2057
+ /** `.scrollTransition`: effects drawn as the box scrolls in and out of view (scrollTransition.ts). */
2058
+ scrollTransition?: ScrollTransition;
2059
+ }
2060
+
1931
2061
  export interface Style {
1932
2062
  /** `.blendMode`: the CSS `mix-blend-mode` the box is composited with. */
1933
2063
  blendMode?: string;
@@ -1945,8 +2075,9 @@ export type DragPhase = 'began' | 'changed' | 'ended' | 'cancelled';
1945
2075
 
1946
2076
  /**
1947
2077
  * A `gesture` with `drag`: coalesced to one per animation frame while
1948
- * `changed`. Coordinates are points relative to the element's box;
1949
- * `velocity` is in points per second (`ended` only, else 0).
2078
+ * `changed`. Coordinates are points relative to the element's box (the
2079
+ * root's with `coordinateSpace: "global"`); `velocity` is in points per
2080
+ * second (`ended` only, else 0).
1950
2081
  */
1951
2082
  export interface DragEvent {
1952
2083
  type: 'drag';
@@ -1964,7 +2095,10 @@ export interface DragEvent {
1964
2095
  * `rotation` the turn of the line between them, in degrees, clockwise
1965
2096
  * positive; the velocities are per second. `startLocation` is where the
1966
2097
  * fingers' midpoint began, in points from the element's top leading corner;
1967
- * `startAnchor` the same as a fraction of the element's size.
2098
+ * `startAnchor` the same as a fraction of the element's size. `location` is
2099
+ * where the midpoint is now (a SwiftBrowser addition: SwiftUI's values stop
2100
+ * at the start), so two fingers can pan as they zoom; a trackpad pinch keeps
2101
+ * its start. A renderer that cannot tell leaves it out.
1968
2102
  */
1969
2103
  export interface PinchEvent {
1970
2104
  type: 'pinch';
@@ -1976,6 +2110,7 @@ export interface PinchEvent {
1976
2110
  rotationVelocity: number;
1977
2111
  startLocation: Point;
1978
2112
  startAnchor: Point;
2113
+ location?: Point;
1979
2114
  }
1980
2115
  /** A `gesture` with `longPress` was held `minimumDuration` without moving 10 px. */
1981
2116
  export interface LongPressEvent {
@@ -2067,7 +2202,9 @@ export type AlertStyle = 'alert' | 'dialog';
2067
2202
  * A root-level element (child of id 0) placed after the tree, like `sheet`.
2068
2203
  * The children are the `button`s in order; a `cancel` role is drawn apart.
2069
2204
  * With no button children the renderer draws an "OK" that sends `tap` with
2070
- * the alert's own id; so does a tap on a dialog's scrim.
2205
+ * the alert's own id; so does a tap on a dialog's scrim. `textfield`
2206
+ * children (`.alert { TextField(...); Button(...) }`) are drawn in the card
2207
+ * between the message and the buttons, in their order.
2071
2208
  */
2072
2209
  export interface AlertProps {
2073
2210
  title: string;
@@ -2115,8 +2252,48 @@ export interface DatePickerProps {
2115
2252
  /** A `<textarea>` filling the proposal; input sends `text` like a `textfield`. */
2116
2253
  export interface TextEditorProps {
2117
2254
  text: string;
2118
- /** Bound focus (UIKit's `becomeFirstResponder`): the editor takes or gives up focus; `focus` events report changes. */
2255
+ /** Bound focus (UIKit's `becomeFirstResponder`): the editor takes or gives up focus (scrolled into view as it does); `focus` events report changes. */
2119
2256
  focused?: boolean;
2257
+ /** As a `textfield`'s: Backspace at the start of the text sends `deletebackward`. */
2258
+ deleteBackward?: boolean;
2259
+ /**
2260
+ * Attributed text (`TextEditor(text: $attributedString)`): the runs `text`
2261
+ * is made of, drawn with each run's weight, slant, lines and color. An
2262
+ * editor with runs (an empty array included) is the rich editor for its
2263
+ * life; it still reports plain `text`, and the app keeps the runs.
2264
+ */
2265
+ runs?: TextRun[];
2266
+ /**
2267
+ * A bound selection (`selection:`), on a plain editor or a rich one: the
2268
+ * editor reports the viewer's with `selection` events, and moves to one
2269
+ * the app sets (when the app focuses the editor, if it is not focused).
2270
+ */
2271
+ selection?: TextSelectionRange;
2272
+ }
2273
+
2274
+ /** A range of an editor's text in UTF-16 offsets; `start === end` is the caret. */
2275
+ export interface TextSelectionRange {
2276
+ start: number;
2277
+ end: number;
2278
+ }
2279
+
2280
+ /** A `texteditor` with `selection` in its props: the viewer moved the caret or selected. */
2281
+ export interface SelectionEvent {
2282
+ type: 'selection';
2283
+ id: ElementId;
2284
+ value: TextSelectionRange;
2285
+ }
2286
+
2287
+ /**
2288
+ * A rich `texteditor`'s runs after an edit of their attributes alone (the
2289
+ * native editor's own Bold, Italic and Underline, iOS 26's edit menu): the
2290
+ * text unchanged, `value` its runs as the editor now shows them, each run's
2291
+ * other attributes (a color, a link) as the app sent them.
2292
+ */
2293
+ export interface RunsEvent {
2294
+ type: 'runs';
2295
+ id: ElementId;
2296
+ value: TextRun[];
2120
2297
  }
2121
2298
 
2122
2299
  /** A `stepper` half was tapped: +1 or −1. */
@@ -2139,7 +2316,18 @@ export interface DateEvent {
2139
2316
  id: ElementId;
2140
2317
  value: number;
2141
2318
  }
2142
- export type Phase8ControlEvent = StepEvent | SlideEvent | DateEvent | FocusEvent | SubmitEvent;
2319
+ export type Phase8ControlEvent = StepEvent | SlideEvent | DateEvent | FocusEvent | SubmitEvent | SelectionEvent | RunsEvent | DeleteBackwardEvent;
2320
+
2321
+ /**
2322
+ * Backspace in a `textfield` or `texteditor` with `deleteBackward`, with
2323
+ * nothing selected and the caret at offset 0 (so nothing to delete): the
2324
+ * app merges the field into what comes before it, say. The key deletes
2325
+ * nothing of its own.
2326
+ */
2327
+ export interface DeleteBackwardEvent {
2328
+ type: 'deletebackward';
2329
+ id: ElementId;
2330
+ }
2143
2331
 
2144
2332
  /** Phase 12: a bound `textfield` (one with `focused` in its props) gained or lost focus. */
2145
2333
  export interface FocusEvent {
@@ -2211,6 +2399,12 @@ export interface Style {
2211
2399
  strikethrough?: boolean;
2212
2400
  /** View `.italic(_:)`: slants the texts below (false turns it off). */
2213
2401
  italic?: boolean;
2402
+ /**
2403
+ * `.scrollDisabled(_:)`, inherited: the views below do not scroll; a
2404
+ * `texteditor` is then as tall as its lines (a text view that does not
2405
+ * scroll), whatever the proposal. False turns an ancestor's off.
2406
+ */
2407
+ scrollDisabled?: boolean;
2214
2408
  /**
2215
2409
  * `.ignoresSafeArea(edges:)`: the engine extends the box to the screen
2216
2410
  * edge on each listed side when the box touches that safe-area edge. The
@@ -2592,3 +2786,100 @@ export interface MediaRequestParams {
2592
2786
  /** `file`: a resource's path inside the app's bundle, in place of `data`: the page answers the URL it serves the resource at. */
2593
2787
  path?: string;
2594
2788
  }
2789
+
2790
+ // ---------------------------------------------------------------------------
2791
+ // Scroll geometry, settable offsets, pinned views, animation completion
2792
+ // (docs/ops-protocol.md, "Scroll geometry, pinned views and animation completion")
2793
+ // ---------------------------------------------------------------------------
2794
+
2795
+ export interface ScrollViewProps {
2796
+ /**
2797
+ * `.onScrollGeometryChange(for:of:action:)`: the page sends `scrollgeometry`
2798
+ * to the view after each layout pass and, while it scrolls, once per
2799
+ * animation frame, whenever a field changed (src/scrollGeometry.ts).
2800
+ */
2801
+ reportsGeometry?: boolean;
2802
+ /**
2803
+ * `ScrollPosition.scrollTo(x:y:)` (a `.scrollPosition` bound to a point):
2804
+ * the content offset to scroll to, carried out once per `serial` after the
2805
+ * pass's layout, smoothly when the commit is animated. An axis left out
2806
+ * keeps its offset; the browser clamps the ends.
2807
+ */
2808
+ scrollOffset?: ScrollOffsetCommand;
2809
+ }
2810
+
2811
+ export interface ScrollOffsetCommand {
2812
+ x?: number;
2813
+ y?: number;
2814
+ /** Increases with every request; the page carries out each serial once. */
2815
+ serial: number;
2816
+ }
2817
+
2818
+ /**
2819
+ * SwiftUI's `ScrollGeometry` for a `scrollview` with `reportsGeometry`:
2820
+ * where its visible area is in its content and how big both are, in points
2821
+ * (2 decimals). Sent after a layout pass and, while the view scrolls, once
2822
+ * per animation frame, when any field changed since the last report.
2823
+ */
2824
+ export interface ScrollGeometryEvent {
2825
+ type: 'scrollgeometry';
2826
+ id: ElementId;
2827
+ /** The visible area's origin in the content (`scrollLeft`, `scrollTop`). */
2828
+ offset: { x: number; y: number };
2829
+ /** The content's laid-out size. */
2830
+ contentSize: { width: number; height: number };
2831
+ /** The scroll view's own size. */
2832
+ containerSize: { width: number; height: number };
2833
+ /** Space the view scrolls into past its content (under a bar covering its bottom); zeros on the web otherwise. */
2834
+ contentInsets: { top: number; leading: number; bottom: number; trailing: number };
2835
+ }
2836
+
2837
+ /** `PinnedScrollableViews`: what a lazy stack or grid keeps in view as its sections scroll. */
2838
+ export type PinnedView = 'sectionHeaders' | 'sectionFooters';
2839
+
2840
+ export interface VStackProps {
2841
+ /**
2842
+ * `LazyVStack(pinnedViews:)`: the `sectionheader` (`sectionfooter`) of each
2843
+ * `section` child stays at the scroll view's edge while its section
2844
+ * passes, pushed out by the next section's (src/pinnedViews.ts).
2845
+ */
2846
+ pinned?: PinnedView[];
2847
+ }
2848
+ export interface HStackProps {
2849
+ /** `LazyHStack(pinnedViews:)`: as for `vstack`, along the leading edge. */
2850
+ pinned?: PinnedView[];
2851
+ }
2852
+ export interface LazyVGridProps {
2853
+ /** `LazyVGrid(pinnedViews:)`: as for `vstack`. */
2854
+ pinned?: PinnedView[];
2855
+ }
2856
+ export interface LazyHGridProps {
2857
+ /** `LazyHGrid(pinnedViews:)`: as for `hstack`. */
2858
+ pinned?: PinnedView[];
2859
+ }
2860
+
2861
+ export interface CommitOp {
2862
+ /**
2863
+ * `withAnimation(_:completionCriteria:_:completion:)`: the page answers
2864
+ * `animationcomplete` with this serial once the pass's animations have
2865
+ * ended (at once, after the pass, when nothing animated).
2866
+ */
2867
+ completion?: number;
2868
+ /** `logicallyComplete` (the default) ends with the animation's duration; `removed` once every effect is gone. */
2869
+ completionCriteria?: 'logicallyComplete' | 'removed';
2870
+ }
2871
+
2872
+ /** The pass whose `commit` carried `completion` has finished animating. Not addressed to an element. */
2873
+ export interface AnimationCompleteEvent {
2874
+ type: 'animationcomplete';
2875
+ serial: number;
2876
+ }
2877
+
2878
+ /**
2879
+ * The system's undo or redo (⌘Z, ⇧⌘Z outside a text input; src/undo.ts),
2880
+ * for the app's `UndoManager`, which takes a step back or forward. Not
2881
+ * addressed to an element; sent once the app has reported an `undo` request.
2882
+ */
2883
+ export interface UndoEvent {
2884
+ type: 'undo' | 'redo';
2885
+ }