@swiftbrowser/web 0.1.0 → 0.2.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.
Files changed (98) hide show
  1. package/README.md +43 -6
  2. package/app/index.html +11 -0
  3. package/node/dist/node/apps.d.ts +16 -0
  4. package/node/dist/node/apps.d.ts.map +1 -1
  5. package/node/dist/node/apps.js +9 -0
  6. package/node/dist/node/apps.js.map +1 -1
  7. package/node/dist/node/config.d.ts +14 -1
  8. package/node/dist/node/config.d.ts.map +1 -1
  9. package/node/dist/node/config.js +20 -3
  10. package/node/dist/node/config.js.map +1 -1
  11. package/node/dist/node/examples.d.ts +2 -1
  12. package/node/dist/node/examples.d.ts.map +1 -1
  13. package/node/dist/node/examples.js +11 -2
  14. package/node/dist/node/examples.js.map +1 -1
  15. package/node/dist/plugins/app-manifests.d.ts +4 -1
  16. package/node/dist/plugins/app-manifests.d.ts.map +1 -1
  17. package/node/dist/plugins/app-manifests.js +3 -2
  18. package/node/dist/plugins/app-manifests.js.map +1 -1
  19. package/node/dist/plugins/asset-catalogs.d.ts +15 -3
  20. package/node/dist/plugins/asset-catalogs.d.ts.map +1 -1
  21. package/node/dist/plugins/asset-catalogs.js +111 -13
  22. package/node/dist/plugins/asset-catalogs.js.map +1 -1
  23. package/node/dist/plugins/included-sites.d.ts +26 -0
  24. package/node/dist/plugins/included-sites.d.ts.map +1 -0
  25. package/node/dist/plugins/included-sites.js +70 -0
  26. package/node/dist/plugins/included-sites.js.map +1 -0
  27. package/node/dist/plugins/pdfImages.d.ts +27 -0
  28. package/node/dist/plugins/pdfImages.d.ts.map +1 -0
  29. package/node/dist/plugins/pdfImages.js +145 -0
  30. package/node/dist/plugins/pdfImages.js.map +1 -0
  31. package/node/dist/plugins/swift-wasm.d.ts +19 -1
  32. package/node/dist/plugins/swift-wasm.d.ts.map +1 -1
  33. package/node/dist/plugins/swift-wasm.js +45 -7
  34. package/node/dist/plugins/swift-wasm.js.map +1 -1
  35. package/node/dist/plugins/symbolTemplates.d.ts +22 -0
  36. package/node/dist/plugins/symbolTemplates.d.ts.map +1 -0
  37. package/node/dist/plugins/symbolTemplates.js +83 -0
  38. package/node/dist/plugins/symbolTemplates.js.map +1 -0
  39. package/package.json +4 -2
  40. package/plugins/app-manifests.ts +3 -2
  41. package/plugins/asset-catalogs.ts +119 -14
  42. package/plugins/included-sites.ts +74 -0
  43. package/plugins/pdfImages.ts +163 -0
  44. package/plugins/swift-wasm.ts +52 -6
  45. package/plugins/symbolTemplates.ts +88 -0
  46. package/public/_headers +7 -0
  47. package/public/app/oauth-callback.html +62 -0
  48. package/src/animation.ts +235 -8
  49. package/src/appFiles.ts +201 -0
  50. package/src/base64.ts +43 -0
  51. package/src/bitmapPlan.ts +143 -0
  52. package/src/bitmapStore.ts +206 -0
  53. package/src/bitmapWorker.ts +41 -0
  54. package/src/bitmapWorkerProtocol.ts +15 -0
  55. package/src/bitmaps.ts +283 -0
  56. package/src/blockedHosts.ts +29 -0
  57. package/src/canvasDrawing.ts +361 -0
  58. package/src/chartDom.ts +2 -2
  59. package/src/chromeColor.ts +179 -0
  60. package/src/clipboard.ts +78 -0
  61. package/src/colorMultiply.ts +93 -0
  62. package/src/colorPicker.ts +44 -0
  63. package/src/corsProxy.ts +45 -0
  64. package/src/gaugeRing.ts +74 -0
  65. package/src/gif.ts +297 -0
  66. package/src/hostRequests.ts +426 -0
  67. package/src/imageOps.ts +592 -0
  68. package/src/interaction.ts +18 -1
  69. package/src/landing.ts +2 -2
  70. package/src/layout/chart.ts +9 -3
  71. package/src/layout/engine.ts +279 -25
  72. package/src/layout/index.ts +3 -1
  73. package/src/layout/text.ts +217 -1
  74. package/src/layout/types.ts +31 -0
  75. package/src/layout/typography.ts +24 -7
  76. package/src/layout/video.ts +24 -0
  77. package/src/layoutDom.ts +53 -3
  78. package/src/main.ts +124 -4
  79. package/src/media.ts +132 -0
  80. package/src/mock.ts +2 -1
  81. package/src/notifications.ts +109 -0
  82. package/src/pathShape.ts +156 -0
  83. package/src/protocol.ts +761 -12
  84. package/src/renderer.ts +584 -36
  85. package/src/richText.ts +65 -0
  86. package/src/scanner.ts +234 -0
  87. package/src/scrollTo.ts +123 -0
  88. package/src/styles.css +212 -7
  89. package/src/styles.ts +31 -0
  90. package/src/swiftData.ts +128 -0
  91. package/src/symbols.ts +564 -21
  92. package/src/translation.ts +126 -0
  93. package/src/video.ts +299 -0
  94. package/src/vite-env.d.ts +6 -0
  95. package/src/wasm.ts +227 -32
  96. package/src/webAuth.ts +267 -0
  97. package/src/webSocket.ts +148 -0
  98. package/src/zoneinfo.ts +269 -0
package/src/protocol.ts CHANGED
@@ -26,7 +26,44 @@ export type SemanticColorName =
26
26
  | 'accent'
27
27
  | 'systemBackground'
28
28
  | 'secondarySystemBackground'
29
- | 'clear';
29
+ /** UIKit's semantic colors, by their iOS names (`Color(uiColor:)`). */
30
+ | 'tertiarySystemBackground'
31
+ | 'systemGroupedBackground'
32
+ | 'secondarySystemGroupedBackground'
33
+ | 'tertiarySystemGroupedBackground'
34
+ | 'label'
35
+ | 'secondaryLabel'
36
+ | 'tertiaryLabel'
37
+ | 'quaternaryLabel'
38
+ | 'placeholderText'
39
+ | 'separator'
40
+ | 'opaqueSeparator'
41
+ | 'systemFill'
42
+ | 'secondarySystemFill'
43
+ | 'tertiarySystemFill'
44
+ | 'quaternarySystemFill'
45
+ | 'systemGray2'
46
+ | 'systemGray3'
47
+ | 'systemGray4'
48
+ | 'systemGray5'
49
+ | 'systemGray6'
50
+ /** The system palette (`Color.red`, `UIColor.systemRed`…): `--sb-color-<name>`. */
51
+ | 'red'
52
+ | 'orange'
53
+ | 'yellow'
54
+ | 'green'
55
+ | 'mint'
56
+ | 'teal'
57
+ | 'cyan'
58
+ | 'blue'
59
+ | 'indigo'
60
+ | 'purple'
61
+ | 'pink'
62
+ | 'brown'
63
+ | 'gray'
64
+ | 'clear'
65
+ /** `TintShapeStyle` (`.tint` as a style): the nearest `.tint(_:)`, else the accent color. */
66
+ | 'tint';
30
67
 
31
68
  /**
32
69
  * Semantic color the renderer resolves for the current color scheme. `a`
@@ -275,6 +312,11 @@ export interface Style {
275
312
  background?: ShapeStyle;
276
313
  frame?: Frame;
277
314
  cornerRadius?: number;
315
+ /**
316
+ * `.background(_:)` in a container shape (`.containerShape`): the
317
+ * background's corner radius. Unlike `cornerRadius` it does not clip.
318
+ */
319
+ backgroundCornerRadius?: number;
278
320
  opacity?: number;
279
321
  /** Dims the subtree (`opacity: 0.4`) and blocks input (`pointer-events: none`). */
280
322
  disabled?: boolean;
@@ -293,6 +335,8 @@ export interface Style {
293
335
  */
294
336
  animation?: Animation;
295
337
  animationToken?: number;
338
+ /** This box is a `Label`'s icon: a list row tints it (its title keeps the row's color). */
339
+ labelIcon?: boolean;
296
340
  /** How this subtree appears and disappears inside an animated commit. */
297
341
  transition?: Transition;
298
342
  // Phase 5
@@ -364,6 +408,34 @@ export type HStackAlignment = 'top' | 'center' | 'bottom';
364
408
 
365
409
  export interface TextProps {
366
410
  text: string;
411
+ /**
412
+ * Rich text (`Text + Text`, `Text(AttributedString)`, an interpolated
413
+ * image, a Text-only modifier such as `.italic()`): the text as runs, each
414
+ * styled on its own. `text` is then the plain content.
415
+ */
416
+ runs?: TextRun[];
417
+ }
418
+ /**
419
+ * One run of a rich text: its `text`, or an inline `image` (an `image`
420
+ * element's props). Unset fields take the text's own style.
421
+ */
422
+ export interface TextRun {
423
+ text?: string;
424
+ image?: ImageProps;
425
+ /** A Font JSON object applied over the text's font (as `styled` `font`). */
426
+ font?: Record<string, unknown>;
427
+ italic?: boolean;
428
+ /** A ShapeStyle JSON object (as `styled` `foreground`). */
429
+ foreground?: unknown;
430
+ /** A line through or under the run; `color` defaults to the run's own. */
431
+ strikethrough?: { color?: unknown };
432
+ underline?: { color?: unknown };
433
+ /** Extra space after each character, in points. */
434
+ kerning?: number;
435
+ /** Points to raise the run by (negative lowers it). */
436
+ baselineOffset?: number;
437
+ /** A tap on the run sends `link` with this URL. */
438
+ link?: string;
367
439
  }
368
440
  export interface VStackProps {
369
441
  spacing: number | null;
@@ -399,6 +471,12 @@ export interface StyledProps {
399
471
  /** Same alignment names as `Frame.alignment`; later children draw on top. */
400
472
  export interface ZStackProps {
401
473
  alignment: FrameAlignment;
474
+ /**
475
+ * Report the element's laid-out size with `geometry` events, as a
476
+ * `geometry` element does (an anchor preference's wrapper; see
477
+ * docs/ops-protocol.md, "Preferences").
478
+ */
479
+ reportsSize?: boolean;
402
480
  }
403
481
  /** `systemName` is an SF Symbol name; the renderer picks the closest glyph. */
404
482
  export interface SymbolImageProps {
@@ -415,7 +493,69 @@ export interface AssetImageProps {
415
493
  name: string;
416
494
  resizable: boolean;
417
495
  }
418
- export type ImageProps = SymbolImageProps | AssetImageProps;
496
+ /**
497
+ * Phase 13: a remote image (`AsyncImage`). The page loaded it already (an
498
+ * `image` request op) and told the app its pixel size; `width` / `height`
499
+ * are that size in points, the intrinsic size the layout engine uses unless
500
+ * `resizable`.
501
+ */
502
+ export interface UrlImageProps {
503
+ url: string;
504
+ resizable: boolean;
505
+ width: number;
506
+ height: number;
507
+ }
508
+ /**
509
+ * A `UIImage` / `CGImage` bitmap (`Image(uiImage:)`): a recipe the page draws
510
+ * (src/bitmaps.ts). `width` / `height` are the image's size in points, the
511
+ * intrinsic size unless `resizable`. See docs/ops-protocol.md, "Bitmaps and
512
+ * Core Image".
513
+ */
514
+ export interface BitmapImageProps {
515
+ bitmap: BitmapRecipe;
516
+ resizable: boolean;
517
+ width: number;
518
+ height: number;
519
+ /** `.interpolation(.none)`: scaled without smoothing. */
520
+ interpolation?: 'none';
521
+ }
522
+ export type ImageProps = SymbolImageProps | AssetImageProps | UrlImageProps | BitmapImageProps;
523
+
524
+ /**
525
+ * How to draw a bitmap: the `imagedata` source named by `source` (null: an
526
+ * empty image) with `steps` applied in order, in Core Image's coordinate
527
+ * space (units of source pixels, origin at the bottom left). The result is
528
+ * `width` × `height` pixels.
529
+ */
530
+ export interface BitmapRecipe {
531
+ source: string | null;
532
+ /** A frame of an animated source (a GIF), composed as the frames before it leave the canvas; absent: the file as it is. */
533
+ frame?: number;
534
+ steps: BitmapStep[];
535
+ width: number;
536
+ height: number;
537
+ }
538
+
539
+ /** A point in Core Image's space. */
540
+ export type BitmapPoint = [number, number];
541
+
542
+ /** One step of a recipe; the formulas are in docs/ops-protocol.md. */
543
+ export type BitmapStep =
544
+ | { op: 'crop' | 'render'; x: number; y: number; width: number; height: number }
545
+ | { op: 'clamp' }
546
+ | { op: 'sepiaTone'; intensity: number }
547
+ | { op: 'edges'; intensity: number }
548
+ | { op: 'crystallize'; radius: number; center: BitmapPoint }
549
+ | { op: 'pixellate'; scale: number; center: BitmapPoint }
550
+ | { op: 'gaussianBlur'; radius: number }
551
+ | { op: 'unsharpMask'; radius: number; intensity: number }
552
+ | { op: 'vignette'; intensity: number; radius: number; center: BitmapPoint; size: number }
553
+ /** Core Graphics drawing an image in a rectangle: every point p goes to (p.x × sx + tx, p.y × sy + ty). */
554
+ | { op: 'place'; sx: number; sy: number; tx: number; ty: number }
555
+ /** A clip to a rounded rectangle (an ellipse when the radii are half its sides). */
556
+ | { op: 'roundedClip'; x: number; y: number; width: number; height: number; rx: number; ry: number }
557
+ /** `UIImage.withTintColor`: every pixel the color (straight RGBA, 0…1), at the pixel's alpha times the color's. */
558
+ | { op: 'tint'; r: number; g: number; b: number; a: number };
419
559
  export type DividerProps = Record<string, never>;
420
560
  export type ScrollAxes = 'vertical' | 'horizontal' | 'both';
421
561
  export interface ScrollViewProps {
@@ -469,7 +609,9 @@ export interface ColorProps {
469
609
  color: Color;
470
610
  }
471
611
  /** Phase 6 adds `containerRelative` (`ContainerRelativeShape`): a rounded rectangle with `cornerRadius`. */
472
- export type ShapeKind = 'rectangle' | 'roundedRectangle' | 'circle' | 'capsule' | 'ellipse' | 'containerRelative';
612
+ export type ShapeKind = 'rectangle' | 'roundedRectangle' | 'circle' | 'capsule' | 'ellipse' | 'containerRelative'
613
+ /** A custom shape drawn from its path (`ShapeProps.path`). */
614
+ | 'path';
473
615
  export interface Stroke {
474
616
  /** Phase 6: any ShapeStyle; a gradient stroke falls back to its first stop. */
475
617
  color: ShapeStyle;
@@ -480,6 +622,12 @@ export interface Stroke {
480
622
  * `data-sb-stroke`.
481
623
  */
482
624
  inset?: boolean;
625
+ /** A custom shape's `StrokeStyle` (a built-in shape's border has none of these). */
626
+ lineCap?: 'butt' | 'round' | 'square';
627
+ lineJoin?: 'miter' | 'round' | 'bevel';
628
+ miterLimit?: number;
629
+ dash?: number[];
630
+ dashPhase?: number;
483
631
  }
484
632
  /** `fill: null` means the current foreground color; fills its proposal. */
485
633
  export interface ShapeProps {
@@ -488,9 +636,33 @@ export interface ShapeProps {
488
636
  /** Phase 6: any ShapeStyle. */
489
637
  fill: ShapeStyle | null;
490
638
  stroke: Stroke | null;
639
+ /**
640
+ * `.trim(from:to:)`: the part of the outline between the fractions, drawn
641
+ * as a conic mask around the center from 3 o'clock, clockwise (exact for a
642
+ * circle). Absent when the shape is not trimmed.
643
+ */
644
+ trim?: { from: number; to: number };
645
+ /**
646
+ * A custom shape (`shape: "path"`, src/pathShape.ts): SVG path data for a
647
+ * rectangle of `size` at the origin, the size the element reported
648
+ * (`reportsSize`). Empty until the first report.
649
+ */
650
+ path?: string;
651
+ size?: { width: number; height: number };
652
+ /** `FillStyle(eoFill: true)`: overlapping parts of the path cancel out. */
653
+ fillRule?: 'evenodd';
654
+ /** The element reports its laid-out size (a custom shape draws its path for it). */
655
+ reportsSize?: boolean;
656
+ /**
657
+ * An animated change of the shape's `animatableData`: the path at each
658
+ * fraction of the change (`paths[index[i]]` at `progress[i]`), played in
659
+ * the commit's animation.
660
+ */
661
+ frames?: { progress: number[]; index: number[]; paths: string[] };
491
662
  }
492
663
  /** Phase 8: `full` is `.fullScreenCover` (the card fills the screen). */
493
- export type Detent = 'medium' | 'large' | 'full';
664
+ /** `height:<points>` / `fraction:<f>`: `.presentationDetents([.height(300)])`, `.fraction(0.4)`. */
665
+ export type Detent = 'medium' | 'large' | 'full' | `height:${number}` | `fraction:${number}`;
494
666
  /**
495
667
  * Always a root-level element (child of id 0) placed after the app's tree. A
496
668
  * `tap` with the sheet's own id is a dismiss request.
@@ -510,6 +682,10 @@ export interface TabViewProps {
510
682
  export interface TabProps {
511
683
  title: string;
512
684
  systemImage: string | null;
685
+ /** iOS 18 `Tab(…).badge(…)`: a red badge on the bar item (absent when none). */
686
+ badge?: string;
687
+ /** iOS 18 `Tab(role: .search)` (absent otherwise); Swift already put it last. */
688
+ role?: 'search';
513
689
  }
514
690
  export type PickerStyle = 'segmented' | 'menu' | 'inline';
515
691
  /**
@@ -615,7 +791,7 @@ export type GaugeStyle =
615
791
  /**
616
792
  * `Gauge`: `value` is normalized to 0...1. Children in order: the label (when
617
793
  * `hasLabel`) and the current value label (when `hasCurrentValueLabel`).
618
- * Circular styles render as linear for now.
794
+ * Circular styles draw a ring with one of the labels in the middle.
619
795
  */
620
796
  export interface GaugeProps {
621
797
  value: number;
@@ -641,7 +817,16 @@ export interface DecoratedProps {
641
817
  * a popover built from the current items (`button`/`navlink` rows that send
642
818
  * `tap`, `picker` sections that send `select`, `divider`, `section`, `text`).
643
819
  */
644
- export type MenuProps = Record<string, never>;
820
+ /**
821
+ * Lazy menus: `deferred: true` while the items are not built (only the label
822
+ * is a child). The page sends a `menu` event with `open: true` when the
823
+ * viewer opens it and shows the popover once the items arrive (`deferred:
824
+ * false`), and `open: false` when it closes. Absent when the app builds the
825
+ * items eagerly (headless runs, the mocks).
826
+ */
827
+ export interface MenuProps {
828
+ deferred?: boolean;
829
+ }
645
830
  export type ToolbarPlacement = 'trailing' | 'leading' | 'principal' | 'bottom';
646
831
  /**
647
832
  * `.toolbar { ToolbarItem(placement:) { ... } }`, an extra child of a
@@ -662,8 +847,13 @@ export interface SearchFieldProps {
662
847
  /** Defaults to "Search". */
663
848
  placeholder: string;
664
849
  }
665
- /** `.onTapGesture`: one child, the child's size; a click anywhere on it sends `tap`. */
666
- export type TapGestureProps = Record<string, never>;
850
+ /**
851
+ * `.onTapGesture`: one child, the child's size; a click anywhere on it sends
852
+ * `tap`. `location` (`onTapGesture { point in }`) adds the click's point.
853
+ */
854
+ export interface TapGestureProps {
855
+ location?: boolean;
856
+ }
667
857
 
668
858
  export interface ElementPropsByKind {
669
859
  text: TextProps;
@@ -758,7 +948,238 @@ export interface DefaultsOp {
758
948
  values: Record<string, unknown>;
759
949
  }
760
950
 
761
- export type Op = CreateOp | UpdateOp | InsertOp | RemoveOp | CommitOp | DefaultsOp;
951
+ /**
952
+ * Phase 13: something only the page can do for the app. The loader performs
953
+ * it (src/hostRequests.ts) and answers with a `ResponseEvent` of the same
954
+ * `id`; the renderer never sees it.
955
+ */
956
+ export interface RequestOp {
957
+ op: 'request';
958
+ id: number;
959
+ kind: 'fetch' | 'image' | 'share' | 'pickFiles' | 'notification' | 'scanner' | 'websocket' | 'openURL' | 'webAuth' | 'media' | 'download' | 'clipboard' | 'translate' | 'pickColor';
960
+ params:
961
+ | FetchRequestParams
962
+ | ImageRequestParams
963
+ | ShareRequestParams
964
+ | PickFilesRequestParams
965
+ | NotificationRequestParams
966
+ | ScannerRequestParams
967
+ | WebSocketRequestParams
968
+ | OpenURLRequestParams
969
+ | WebAuthRequestParams
970
+ | MediaRequestParams
971
+ | DownloadRequestParams
972
+ | ClipboardRequestParams
973
+ | TranslateRequestParams
974
+ | PickColorRequestParams;
975
+ }
976
+ /**
977
+ * `translate` (Translation; src/translation.ts): `availability` of a pair
978
+ * (`source` null: detected from `text`), or `translate` the `texts` from
979
+ * `source` (null: detected) to `target`, BCP 47 tags. `allowDownload`: the
980
+ * session may download the pair's model.
981
+ */
982
+ export interface TranslateRequestParams {
983
+ action: 'availability' | 'translate';
984
+ source?: string | null;
985
+ target: string;
986
+ text?: string;
987
+ texts?: string[];
988
+ allowDownload?: boolean;
989
+ }
990
+ /** `pickColor` (`ColorPicker`; src/colorPicker.ts): the chooser opens at `color` (`#rrggbb`). */
991
+ export interface PickColorRequestParams {
992
+ color?: string;
993
+ }
994
+ /**
995
+ * `URLSessionWebSocketTask`: one call on a socket (src/webSocket.ts). `open`
996
+ * takes `url` and `protocols`; the others the `socket` id `open` answered.
997
+ */
998
+ /** `openURL` (`OpenURLAction`, a link in a `Text`, `UIApplication.open`): the page opens `url` in a new tab; the value is null. */
999
+ /**
1000
+ * `webAuth` (AuthenticationServices; docs/ops-protocol.md, "Signing in"):
1001
+ * `start` opens `url`, the provider's authorize page, in a popup window and
1002
+ * answers `{ url }`, the callback page URL the window ended at; `cancel` closes
1003
+ * the window, and the pending `start` fails with `cancelled: …`.
1004
+ * `callbackScheme` is the app's scheme the redirect stands for, `callbackPage`
1005
+ * the callback page the app put in its place; `ephemeral` is the app's wish
1006
+ * for a private session, which a popup cannot grant.
1007
+ */
1008
+ export interface WebAuthRequestParams {
1009
+ action?: 'start' | 'cancel';
1010
+ url?: string;
1011
+ callbackScheme?: string;
1012
+ callbackPage?: string;
1013
+ ephemeral?: boolean;
1014
+ }
1015
+ export interface OpenURLRequestParams {
1016
+ url: string;
1017
+ }
1018
+ export interface WebSocketRequestParams {
1019
+ action: 'open' | 'send' | 'receive' | 'close';
1020
+ url?: string;
1021
+ protocols?: string[];
1022
+ socket?: number;
1023
+ /** `send`: a text message. */
1024
+ string?: string;
1025
+ /** `send`: a binary message, base64. */
1026
+ data?: string;
1027
+ /** `close`: the close code and reason. */
1028
+ code?: number;
1029
+ reason?: string;
1030
+ }
1031
+ /** `URLSession`: one HTTP request. `body` is base64; `timeout` in ms (0: none). */
1032
+ export interface FetchRequestParams {
1033
+ url: string;
1034
+ method: string;
1035
+ headers: Record<string, string>;
1036
+ body: string | null;
1037
+ timeout?: number;
1038
+ }
1039
+ /** `AsyncImage`: decode the image at `url` and report its pixel size. */
1040
+ export interface ImageRequestParams {
1041
+ url: string;
1042
+ }
1043
+
1044
+ /**
1045
+ * `ShareLink`: share through the Web Share API, or download where the browser
1046
+ * cannot. `images` are asset-catalog names, shared (or downloaded) as their
1047
+ * files. The response value is a `ShareResult`.
1048
+ */
1049
+ export interface ShareRequestParams {
1050
+ title?: string;
1051
+ text?: string;
1052
+ url?: string;
1053
+ images?: string[];
1054
+ /** `Image(uiImage:)` items: recipes the page draws into PNG files. */
1055
+ bitmaps?: BitmapRecipe[];
1056
+ /** Files the app made: a `Transferable` item's exported bytes, a `UIImage`'s own file. */
1057
+ files?: SharedFile[];
1058
+ }
1059
+
1060
+ /** A file the app hands to the page whole: its name, MIME type and bytes (base64). */
1061
+ export interface SharedFile {
1062
+ name: string;
1063
+ type: string;
1064
+ data: string;
1065
+ }
1066
+
1067
+ /**
1068
+ * Saving (Photos' change requests, `UIImageWriteToSavedPhotosAlbum`): the
1069
+ * page downloads each file, catalog image and bitmap (a PNG named after
1070
+ * `title`). The response value is a `ShareResult` (`downloaded`).
1071
+ */
1072
+ export interface DownloadRequestParams {
1073
+ title?: string;
1074
+ files?: SharedFile[];
1075
+ images?: string[];
1076
+ bitmaps?: BitmapRecipe[];
1077
+ }
1078
+
1079
+ /**
1080
+ * `UIPasteboard.general`: `write` copies `text`, or the first picture (a
1081
+ * file, catalog image or bitmap, as PNG) to the system clipboard and answers
1082
+ * `{}`; `readText` answers `{ text }`.
1083
+ */
1084
+ export interface ClipboardRequestParams {
1085
+ action: 'write' | 'readText';
1086
+ text?: string;
1087
+ files?: SharedFile[];
1088
+ images?: string[];
1089
+ bitmaps?: BitmapRecipe[];
1090
+ }
1091
+
1092
+ /**
1093
+ * `PhotosPicker`: let the viewer choose files with the browser's file
1094
+ * chooser. `accept` is the input's `accept` (`image/*,video/*`; empty: any
1095
+ * file). The response value is a `PickFilesResult`; a dismissed chooser
1096
+ * answers with no items.
1097
+ */
1098
+ export interface PickFilesRequestParams {
1099
+ accept: string;
1100
+ multiple: boolean;
1101
+ }
1102
+
1103
+ /**
1104
+ * UserNotifications (docs/ops-protocol.md, "Notifications"): `status` reads
1105
+ * `Notification.permission`, `request` asks for it, `show` presents a
1106
+ * notification that came due (`title` absent: the badge only), `close`
1107
+ * closes delivered ones by identifier, `badge` sets the app badge. The
1108
+ * response value is a `NotificationResult`.
1109
+ */
1110
+ export interface NotificationRequestParams {
1111
+ action: 'status' | 'request' | 'show' | 'close' | 'badge';
1112
+ /** `show`: the request's identifier, the notification's `tag`. */
1113
+ id?: string;
1114
+ title?: string;
1115
+ subtitle?: string;
1116
+ body?: string;
1117
+ silent?: boolean;
1118
+ /** `show` and `badge`: the app badge (0 clears it). */
1119
+ badge?: number;
1120
+ /** `close`: the identifiers to close. */
1121
+ ids?: string[];
1122
+ }
1123
+
1124
+ /**
1125
+ * AVFoundation's capture sessions (docs/ops-protocol.md, "Code scanning"):
1126
+ * `probe` asks whether the page can scan `formats` (`BarcodeDetector` names)
1127
+ * with a camera; `scan` opens the camera over the page until a code is found
1128
+ * or the viewer closes it, or, where the browser cannot read codes, asks the
1129
+ * viewer to type one. The response value is a `ScannerResult`.
1130
+ */
1131
+ export interface ScannerRequestParams {
1132
+ action: 'probe' | 'scan';
1133
+ formats: string[];
1134
+ torch?: boolean;
1135
+ vibrate?: boolean;
1136
+ /** Which camera: the back one (`environment`, the default) or the front one (`user`). */
1137
+ facing?: 'environment' | 'user';
1138
+ }
1139
+
1140
+ /**
1141
+ * Phase 18: one save of a SwiftData store (`ModelContext.save()`, or the main
1142
+ * context's autosave at the end of an event). The loader hands it to the page,
1143
+ * which writes it to the app's IndexedDB database (src/swiftData.ts) and
1144
+ * passes every store back at the next launch as `SB_SWIFTDATA`. `clear`
1145
+ * (`ModelContainer.deleteAllData()`) empties the store before `put`.
1146
+ */
1147
+ export interface SwiftDataOp {
1148
+ op: 'swiftdata';
1149
+ store: string;
1150
+ put: SwiftDataRecord[];
1151
+ delete: number[];
1152
+ clear?: boolean;
1153
+ }
1154
+
1155
+ /** A saved model: its key in the store, its entity (class) name and its properties' JSON. */
1156
+ export interface SwiftDataRecord {
1157
+ id: number;
1158
+ entity: string;
1159
+ values: Record<string, unknown>;
1160
+ }
1161
+
1162
+ /**
1163
+ * A bitmap's source, sent once (by content `key`) before the first element
1164
+ * that draws it: an encoded image file (`mime`, `data`) or a pixel buffer
1165
+ * (`format`, `pixels`: one gray or four RGBA bytes per pixel, rows from the
1166
+ * top). The bytes are base64 in printed ops; from a module with the
1167
+ * `sb_bytes_*` exports, src/wasm.ts reads them out of linear memory
1168
+ * (docs/ops-protocol.md, "Bytes"). `width` × `height` is its size in pixels, as
1169
+ * drawn (a JPEG's EXIF rotation applied). The renderer keeps it for the run.
1170
+ */
1171
+ export interface ImageDataOp {
1172
+ op: 'imagedata';
1173
+ key: string;
1174
+ width: number;
1175
+ height: number;
1176
+ mime?: string;
1177
+ data?: string | Uint8Array;
1178
+ format?: 'gray' | 'rgba';
1179
+ pixels?: string | Uint8Array;
1180
+ }
1181
+
1182
+ export type Op = CreateOp | UpdateOp | InsertOp | RemoveOp | CommitOp | DefaultsOp | RequestOp | SwiftDataOp | ImageDataOp;
762
1183
 
763
1184
  // ---------------------------------------------------------------------------
764
1185
  // Events (renderer → Swift)
@@ -806,6 +1227,15 @@ export function isDynamicTypeSize(value: unknown): value is DynamicTypeSize {
806
1227
  export interface TapEvent {
807
1228
  type: 'tap';
808
1229
  id: ElementId;
1230
+ /** A `tapgesture` with `location`: the tap's point in the element's own coordinates. */
1231
+ x?: number;
1232
+ y?: number;
1233
+ }
1234
+ /** A link run (`TextRun.link`) of a `text` was tapped; `value` is its URL. */
1235
+ export interface LinkEvent {
1236
+ type: 'link';
1237
+ id: ElementId;
1238
+ value: string;
809
1239
  }
810
1240
  /** A `textfield` or `searchfield` received input. */
811
1241
  export interface TextEvent {
@@ -875,13 +1305,32 @@ export type RendererEvent =
875
1305
  | ToggleEvent
876
1306
  | EnvironmentEvent
877
1307
  | SelectEvent
1308
+ | LinkEvent
878
1309
  | GeometryEvent
879
1310
  | LayoutEvent
880
1311
  | BatchEvent
881
1312
  // Phase 8: gestures, rows, navigation (see the block at the end of this file)
882
1313
  | InteractionEvent
883
1314
  // Phase 8: alerts, controls (see the block at the end of this file)
884
- | Phase8ControlEvent;
1315
+ | Phase8ControlEvent
1316
+ // Phase 13: the answer to a `request` op
1317
+ | ResponseEvent
1318
+ // A `video` element's playback (src/video.ts)
1319
+ | VideoEvent
1320
+ | LazyEvent
1321
+ | MenuEvent;
1322
+
1323
+ /**
1324
+ * Phase 13: the page's answer to the `request` op with this `id`: `value`
1325
+ * (a `FetchResult` or `ImageResult` from src/hostRequests.ts) on success,
1326
+ * `error` (a short message the app maps to a `URLError`) on failure.
1327
+ */
1328
+ export interface ResponseEvent {
1329
+ type: 'response';
1330
+ id: number;
1331
+ value?: unknown;
1332
+ error?: string;
1333
+ }
885
1334
 
886
1335
  /**
887
1336
  * Several events delivered in one `sb_event_json` call: Swift applies each
@@ -948,12 +1397,31 @@ export function isAssetImage(props: ImageProps): props is AssetImageProps {
948
1397
  return typeof (props as AssetImageProps).name === 'string';
949
1398
  }
950
1399
 
1400
+ /** True for the bitmap form of `image` props (`{ bitmap, resizable, width, height }`). */
1401
+ export function isBitmapImage(props: ImageProps): props is BitmapImageProps {
1402
+ const bitmap = (props as BitmapImageProps).bitmap;
1403
+ return typeof bitmap === 'object' && bitmap !== null;
1404
+ }
1405
+
1406
+ /** True for the remote form of `image` props (`{ url, resizable, width, height }`, Phase 13). */
1407
+ export function isUrlImage(props: ImageProps): props is UrlImageProps {
1408
+ return typeof (props as UrlImageProps).url === 'string';
1409
+ }
1410
+
951
1411
  /** Cheap structural check used when parsing an untrusted JSON buffer. */
952
1412
  export function isOp(value: unknown): value is Op {
953
1413
  if (typeof value !== 'object' || value === null) return false;
954
1414
  const op = (value as { op?: unknown }).op;
955
1415
  return (
956
- op === 'create' || op === 'update' || op === 'insert' || op === 'remove' || op === 'commit' || op === 'defaults'
1416
+ op === 'create' ||
1417
+ op === 'update' ||
1418
+ op === 'insert' ||
1419
+ op === 'remove' ||
1420
+ op === 'commit' ||
1421
+ op === 'defaults' ||
1422
+ op === 'request' ||
1423
+ op === 'swiftdata' ||
1424
+ op === 'imagedata'
957
1425
  );
958
1426
  }
959
1427
 
@@ -1095,6 +1563,8 @@ export interface ChartAxisLabel {
1095
1563
  /** Unit position along the axis (y: 0 is the top). */
1096
1564
  at: number;
1097
1565
  text: string;
1566
+ /** `leading`: an x label that starts at its tick (a date's) instead of centering on it. */
1567
+ anchor?: 'leading';
1098
1568
  }
1099
1569
  /** `null` for the whole axis means hidden. Positions are unit values along the axis. */
1100
1570
  export interface ChartAxis {
@@ -1192,7 +1662,10 @@ export interface SwipeActionsProps {
1192
1662
  }
1193
1663
 
1194
1664
  /** `.contextMenu { }`: `[content, ...items]`; a long press or right click lists the items (as in a `menu`) in a popover. */
1195
- export type ContextMenuProps = Record<string, never>;
1665
+ /** As `MenuProps`: `deferred` while the items (every child but the content) are not built. */
1666
+ export interface ContextMenuProps {
1667
+ deferred?: boolean;
1668
+ }
1196
1669
 
1197
1670
  /**
1198
1671
  * A `List` row that can be selected (`selectable`, `selected`) or deleted
@@ -1235,6 +1708,19 @@ export interface Style {
1235
1708
  hitTesting?: boolean;
1236
1709
  }
1237
1710
 
1711
+ export interface Style {
1712
+ /** `.blendMode`: the CSS `mix-blend-mode` the box is composited with. */
1713
+ blendMode?: string;
1714
+ /** `.zIndex(_:)`: the box's CSS `z-index` among its siblings. */
1715
+ zIndex?: number;
1716
+ /**
1717
+ * `.alignmentGuide(_:computeValue:)`: the box's guide `name` on `axis` is
1718
+ * `a + b·width + c·height` (`value: [a, b, c]`), which the containers that
1719
+ * align it by that guide use instead of the default (layout/engine.ts).
1720
+ */
1721
+ alignmentGuide?: { axis: 'horizontal' | 'vertical'; name: string; value: [number, number, number] };
1722
+ }
1723
+
1238
1724
  export type DragPhase = 'began' | 'changed' | 'ended' | 'cancelled';
1239
1725
 
1240
1726
  /**
@@ -1325,6 +1811,8 @@ export interface DatePickerProps {
1325
1811
  /** A `<textarea>` filling the proposal; input sends `text` like a `textfield`. */
1326
1812
  export interface TextEditorProps {
1327
1813
  text: string;
1814
+ /** Bound focus (UIKit's `becomeFirstResponder`): the editor takes or gives up focus; `focus` events report changes. */
1815
+ focused?: boolean;
1328
1816
  }
1329
1817
 
1330
1818
  /** A `stepper` half was tapped: +1 or −1. */
@@ -1394,6 +1882,29 @@ export interface Style {
1394
1882
  rotation3D?: Rotation3D;
1395
1883
  /** `.blur(radius:)`: `filter: blur(<radius>px)`, combined with `shadow` in one `filter`. */
1396
1884
  blur?: number;
1885
+ /** `.brightness(_:)`: SwiftUI's amount (-1…1), drawn as CSS `brightness(1 + amount)`. */
1886
+ brightness?: number;
1887
+ /** `.saturation(_:)`: CSS `saturate()`. */
1888
+ saturation?: number;
1889
+ /** `.contrast(_:)`: CSS `contrast()`. */
1890
+ contrast?: number;
1891
+ /** `.grayscale(_:)`: CSS `grayscale()` (0…1). */
1892
+ grayscale?: number;
1893
+ /** `.hueRotation(_:)`: degrees, CSS `hue-rotate()`. */
1894
+ hueRotation?: number;
1895
+ /** `.colorInvert()`: CSS `invert(1)`. */
1896
+ colorInvert?: boolean;
1897
+ /**
1898
+ * `.colorMultiply(_:)`: each component (and the alpha) multiplied by the
1899
+ * color's, drawn with an SVG `feColorMatrix` filter (colorMultiply.ts).
1900
+ */
1901
+ colorMultiply?: Color;
1902
+ /** View `.underline(_:)`: underlines the texts below (false turns an ancestor's off). */
1903
+ underline?: boolean;
1904
+ /** View `.strikethrough(_:)`: strikes the texts below through (false turns it off). */
1905
+ strikethrough?: boolean;
1906
+ /** View `.italic(_:)`: slants the texts below (false turns it off). */
1907
+ italic?: boolean;
1397
1908
  /**
1398
1909
  * `.ignoresSafeArea(edges:)`: the engine extends the box to the screen
1399
1910
  * edge on each listed side when the box touches that safe-area edge. The
@@ -1419,3 +1930,241 @@ export type ScenePhase = 'active' | 'inactive' | 'background';
1419
1930
  export interface EnvironmentEvent {
1420
1931
  scenePhase?: ScenePhase;
1421
1932
  }
1933
+
1934
+ // ---------------------------------------------------------------------------
1935
+ // Multi-line text fields, view section headers, scrolling to a view
1936
+ // ---------------------------------------------------------------------------
1937
+
1938
+ export interface TextFieldProps {
1939
+ /**
1940
+ * `TextField(_:text:axis: .vertical)`: a `<textarea>` that grows with its
1941
+ * text between the enclosing `lineLimitMin` (1) and `lineLimit` (none)
1942
+ * lines and scrolls past that; Return breaks the line unless the field has
1943
+ * `submit` (then Shift-Return does). Absent for a one-line field.
1944
+ */
1945
+ axis?: 'vertical';
1946
+ }
1947
+
1948
+ export interface ListProps {
1949
+ /** `.scrollContentBackground(.hidden)`: the list draws no background of its own (rows keep theirs). */
1950
+ background?: 'hidden';
1951
+ /** `.scrollDismissesKeyboard(.immediately | .interactively)`: scrolling blurs the text input being edited inside. */
1952
+ dismissesKeyboard?: boolean;
1953
+ }
1954
+
1955
+ export interface ScrollViewProps {
1956
+ /** As for `list`. */
1957
+ dismissesKeyboard?: boolean;
1958
+ }
1959
+
1960
+ /**
1961
+ * A `Section`'s header or footer that is a view rather than a `Text`: the
1962
+ * first (`sectionheader`) or last (`sectionfooter`) child of the `section`,
1963
+ * holding the view. In a list it sits above / below the section's card in
1964
+ * the content width (footnote font, secondary color); elsewhere it stacks
1965
+ * with the rows.
1966
+ */
1967
+ export type SectionSupplementProps = Record<string, never>;
1968
+
1969
+ export interface ElementPropsByKind {
1970
+ sectionheader: SectionSupplementProps;
1971
+ sectionfooter: SectionSupplementProps;
1972
+ }
1973
+
1974
+ /** `ScrollViewProxy.scrollTo(_:anchor:)`: where the target should end up (null: as little scrolling as shows it). */
1975
+ export interface ScrollToCommand {
1976
+ anchor: UnitPoint | null;
1977
+ /** Increases with every request (across readers); the page carries out each serial once. */
1978
+ serial: number;
1979
+ }
1980
+
1981
+ export interface Style {
1982
+ /** `.id(_:)` inside a `ScrollViewReader`: this box is a scroll target. */
1983
+ scrollID?: boolean;
1984
+ /**
1985
+ * Set in the pass in which a `ScrollViewProxy` asks to scroll to this box
1986
+ * (and kept until the reader re-runs): after laying out a pass that brings
1987
+ * a serial above every one it handled, the page scrolls the scroll views
1988
+ * and lists around it (smoothly when the pass's commit is animated).
1989
+ */
1990
+ scrollTo?: ScrollToCommand;
1991
+ /** The lower bound of a `.lineLimit(2...4)` range (absent for 1): a vertical text field's minimum lines. */
1992
+ lineLimitMin?: number;
1993
+ }
1994
+
1995
+ // ---------------------------------------------------------------------------
1996
+ // Video and audio (docs/ops-protocol.md, "Video and audio")
1997
+ // ---------------------------------------------------------------------------
1998
+
1999
+ /**
2000
+ * A `video` element: an `AVPlayer` shown by a view (AVKit's `VideoPlayer`, an
2001
+ * `AVPlayerLayer`), as an HTML `<video>` (src/video.ts). No children.
2002
+ */
2003
+ /**
2004
+ * Lazy rows: the rows of a `List` or lazy stack that are not built yet
2005
+ * (`remaining` of them, after the built ones). It takes no space; when it
2006
+ * comes within a screen of its scroll view's visible area the page sends a
2007
+ * `lazy` event, and Swift builds the next rows (a new spacer follows them).
2008
+ */
2009
+ export interface LazySpacerProps {
2010
+ axis: 'vertical' | 'horizontal';
2011
+ remaining: number;
2012
+ }
2013
+
2014
+ /** Lazy menus: the viewer opened (`open: true`) or closed a deferred `menu` / `contextmenu`. */
2015
+ export interface MenuEvent {
2016
+ type: 'menu';
2017
+ id: ElementId;
2018
+ open: boolean;
2019
+ }
2020
+
2021
+ /** A `lazyspacer` came near the visible area: build the next rows. */
2022
+ export interface LazyEvent {
2023
+ type: 'lazy';
2024
+ id: ElementId;
2025
+ }
2026
+
2027
+ export interface VideoProps {
2028
+ /** The media URL (remote, data or blob); null while there is none. */
2029
+ url: string | null;
2030
+ /** Whether the player plays here (only the newest view of a player plays it). */
2031
+ playing: boolean;
2032
+ muted: boolean;
2033
+ /** 0…1. */
2034
+ volume: number;
2035
+ /** The playback rate (positive; the player's default rate while paused). */
2036
+ rate: number;
2037
+ /** An `AVPlayerLooper` loops the item. */
2038
+ loop: boolean;
2039
+ /** CSS `object-fit`: `contain`, `cover`, `fill`. */
2040
+ gravity: 'resizeAspect' | 'resizeAspectFill' | 'resize';
2041
+ /** The browser's native playback controls (`VideoPlayer`, `AVPlayerViewController`). */
2042
+ controls: boolean;
2043
+ /** Seconds; the page seeks once per serial (and reports `seeked` with it). */
2044
+ seek: { time: number; serial: number };
2045
+ /** Report the position this often (seconds) while playing, when shorter than `timeupdate`'s pace. */
2046
+ report?: number;
2047
+ /** The video's size in pixels, once known: the aspect ratio of an axis left open. */
2048
+ width?: number;
2049
+ height?: number;
2050
+ }
2051
+
2052
+ export interface ElementPropsByKind {
2053
+ video: VideoProps;
2054
+ lazyspacer: LazySpacerProps;
2055
+ }
2056
+
2057
+ /**
2058
+ * A gradient of a `Canvas` (`GraphicsContext.Shading`), in the drawing's own
2059
+ * coordinates (points, after the command's `transform`) rather than the
2060
+ * unit square of a view's box.
2061
+ */
2062
+ export interface CanvasLinearGradientShading {
2063
+ canvasLinearGradient: { stops: GradientStop[]; start: { x: number; y: number }; end: { x: number; y: number } };
2064
+ }
2065
+ export interface CanvasRadialGradientShading {
2066
+ canvasRadialGradient: { stops: GradientStop[]; center: { x: number; y: number }; startRadius: number; endRadius: number };
2067
+ }
2068
+ export type CanvasShading = ShapeStyle | CanvasLinearGradientShading | CanvasRadialGradientShading;
2069
+
2070
+ /** A `GraphicsContext.Filter`: one key, its amount (degrees for `hueRotation`). */
2071
+ export type CanvasFilter =
2072
+ | { blur: number }
2073
+ | { shadow: { color: Color; radius: number; x: number; y: number } }
2074
+ | { grayscale: number }
2075
+ | { saturation: number }
2076
+ | { brightness: number }
2077
+ | { contrast: number }
2078
+ | { hueRotation: number }
2079
+ | { invert: number };
2080
+
2081
+ /** The context's state when a command was drawn. */
2082
+ export interface CanvasState {
2083
+ /** `[a, b, c, d, tx, ty]`, as `CGAffineTransform` (and SVG's `matrix()`); absent for the identity. */
2084
+ transform?: [number, number, number, number, number, number];
2085
+ opacity?: number;
2086
+ /** CSS `mix-blend-mode`. */
2087
+ blendMode?: string;
2088
+ /** SVG path data, in the canvas's coordinates; the command draws inside all of them. */
2089
+ clips?: string[];
2090
+ filters?: CanvasFilter[];
2091
+ }
2092
+
2093
+ export type CanvasCommand = CanvasState &
2094
+ (
2095
+ | { op: 'fill'; path: string; shading: CanvasShading; evenOdd?: boolean }
2096
+ | {
2097
+ op: 'stroke';
2098
+ path: string;
2099
+ shading: CanvasShading;
2100
+ lineWidth: number;
2101
+ lineCap?: 'butt' | 'round' | 'square';
2102
+ lineJoin?: 'miter' | 'round' | 'bevel';
2103
+ miterLimit?: number;
2104
+ dash?: number[];
2105
+ dashPhase?: number;
2106
+ }
2107
+ | {
2108
+ op: 'image';
2109
+ image: ImageProps;
2110
+ /** The point `anchor` (a unit point of the image) is placed at. */
2111
+ x: number;
2112
+ y: number;
2113
+ anchor: UnitPoint;
2114
+ /** `draw(_:in:)`: the rectangle's size; otherwise the image's own size. */
2115
+ width?: number;
2116
+ height?: number;
2117
+ /** A resolved image's `shading`: a template image is painted with it. */
2118
+ shading?: CanvasShading;
2119
+ }
2120
+ | { op: 'text'; text: string; x: number; y: number; anchor: UnitPoint; shading: CanvasShading; font?: Font }
2121
+ );
2122
+
2123
+ /**
2124
+ * A `Canvas` at one size (src/canvasDrawing.ts): the commands its renderer
2125
+ * drew for `width` × `height`, the size the canvas reported. It fills its
2126
+ * proposal and clips the drawing to its box.
2127
+ */
2128
+ export interface CanvasProps {
2129
+ width: number;
2130
+ height: number;
2131
+ commands: CanvasCommand[];
2132
+ }
2133
+
2134
+ export interface ElementPropsByKind {
2135
+ canvas: CanvasProps;
2136
+ }
2137
+
2138
+ /**
2139
+ * What a `video` element reports: `ready` (metadata), `time`, `status`
2140
+ * (playing, waiting, paused), `seeked` (a `seek` serial done), `play` /
2141
+ * `pause` (the viewer used the controls; `reason: "blocked"` when the browser
2142
+ * refused to start), `volume`, `rate`, `ended`, `error` (a `MediaError` code).
2143
+ */
2144
+ export interface VideoEvent {
2145
+ type: 'video';
2146
+ id: ElementId;
2147
+ event: 'ready' | 'time' | 'status' | 'seeked' | 'play' | 'pause' | 'volume' | 'rate' | 'ended' | 'error';
2148
+ url?: string;
2149
+ time?: number;
2150
+ duration?: number | null;
2151
+ width?: number;
2152
+ height?: number;
2153
+ status?: 'playing' | 'waiting' | 'paused';
2154
+ serial?: number;
2155
+ reason?: 'blocked';
2156
+ muted?: boolean;
2157
+ volume?: number;
2158
+ rate?: number;
2159
+ code?: number;
2160
+ message?: string;
2161
+ }
2162
+
2163
+ /** `media` requests (src/media.ts): AVFoundation's files and probes, QuickLook's previews. */
2164
+ export interface MediaRequestParams {
2165
+ action: 'file' | 'probe' | 'release' | 'open';
2166
+ url?: string;
2167
+ name?: string;
2168
+ type?: string;
2169
+ data?: string;
2170
+ }