@swiftbrowser/web 0.1.0 → 0.3.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/README.md +141 -63
- package/app/index.html +21 -10
- package/node/dist/node/apps.d.ts +16 -0
- package/node/dist/node/apps.d.ts.map +1 -1
- package/node/dist/node/apps.js +9 -0
- package/node/dist/node/apps.js.map +1 -1
- package/node/dist/node/config.d.ts +14 -1
- package/node/dist/node/config.d.ts.map +1 -1
- package/node/dist/node/config.js +20 -3
- package/node/dist/node/config.js.map +1 -1
- package/node/dist/node/examples.d.ts +2 -1
- package/node/dist/node/examples.d.ts.map +1 -1
- package/node/dist/node/examples.js +12 -2
- package/node/dist/node/examples.js.map +1 -1
- package/node/dist/plugins/app-manifests.d.ts +4 -1
- package/node/dist/plugins/app-manifests.d.ts.map +1 -1
- package/node/dist/plugins/app-manifests.js +3 -2
- package/node/dist/plugins/app-manifests.js.map +1 -1
- package/node/dist/plugins/asset-catalogs.d.ts +15 -3
- package/node/dist/plugins/asset-catalogs.d.ts.map +1 -1
- package/node/dist/plugins/asset-catalogs.js +111 -13
- package/node/dist/plugins/asset-catalogs.js.map +1 -1
- package/node/dist/plugins/included-sites.d.ts +26 -0
- package/node/dist/plugins/included-sites.d.ts.map +1 -0
- package/node/dist/plugins/included-sites.js +70 -0
- package/node/dist/plugins/included-sites.js.map +1 -0
- package/node/dist/plugins/pdfImages.d.ts +27 -0
- package/node/dist/plugins/pdfImages.d.ts.map +1 -0
- package/node/dist/plugins/pdfImages.js +145 -0
- package/node/dist/plugins/pdfImages.js.map +1 -0
- package/node/dist/plugins/swift-wasm.d.ts +19 -1
- package/node/dist/plugins/swift-wasm.d.ts.map +1 -1
- package/node/dist/plugins/swift-wasm.js +45 -7
- package/node/dist/plugins/swift-wasm.js.map +1 -1
- package/node/dist/plugins/symbolTemplates.d.ts +22 -0
- package/node/dist/plugins/symbolTemplates.d.ts.map +1 -0
- package/node/dist/plugins/symbolTemplates.js +83 -0
- package/node/dist/plugins/symbolTemplates.js.map +1 -0
- package/package.json +4 -2
- package/plugins/app-manifests.ts +3 -2
- package/plugins/asset-catalogs.ts +119 -14
- package/plugins/included-sites.ts +74 -0
- package/plugins/pdfImages.ts +163 -0
- package/plugins/swift-wasm.ts +52 -6
- package/plugins/symbolTemplates.ts +88 -0
- package/public/_headers +7 -0
- package/public/app/oauth-callback.html +62 -0
- package/src/animation.ts +235 -8
- package/src/appFiles.ts +201 -0
- package/src/base64.ts +43 -0
- package/src/bitmapPlan.ts +143 -0
- package/src/bitmapStore.ts +206 -0
- package/src/bitmapWorker.ts +41 -0
- package/src/bitmapWorkerProtocol.ts +15 -0
- package/src/bitmaps.ts +283 -0
- package/src/blockedHosts.ts +29 -0
- package/src/canvasDrawing.ts +361 -0
- package/src/chartDom.ts +2 -2
- package/src/chromeColor.ts +179 -0
- package/src/clipboard.ts +78 -0
- package/src/colorMultiply.ts +93 -0
- package/src/colorPicker.ts +44 -0
- package/src/corsProxy.ts +45 -0
- package/src/gaugeRing.ts +74 -0
- package/src/gif.ts +297 -0
- package/src/hostRequests.ts +426 -0
- package/src/imageOps.ts +592 -0
- package/src/interaction.ts +68 -3
- package/src/landing.ts +2 -2
- package/src/largeTitle.ts +79 -0
- package/src/layout/chart.ts +9 -3
- package/src/layout/engine.ts +681 -174
- package/src/layout/index.ts +3 -1
- package/src/layout/text.ts +217 -1
- package/src/layout/types.ts +31 -0
- package/src/layout/typography.ts +24 -7
- package/src/layout/video.ts +24 -0
- package/src/layoutDom.ts +66 -6
- package/src/main.ts +127 -4
- package/src/media.ts +132 -0
- package/src/mock.ts +4 -3
- package/src/notifications.ts +109 -0
- package/src/pathShape.ts +156 -0
- package/src/protocol.ts +782 -13
- package/src/renderer.ts +603 -44
- package/src/richText.ts +65 -0
- package/src/scanner.ts +234 -0
- package/src/scrollTo.ts +123 -0
- package/src/styles.css +521 -100
- package/src/styles.ts +61 -0
- package/src/swiftData.ts +128 -0
- package/src/symbols.ts +658 -28
- package/src/translation.ts +126 -0
- package/src/video.ts +299 -0
- package/src/vite-env.d.ts +6 -0
- package/src/wasm.ts +227 -32
- package/src/webAuth.ts +267 -0
- package/src/webSocket.ts +148 -0
- 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
|
-
|
|
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`
|
|
@@ -103,6 +140,17 @@ export interface RadialGradientStyle extends StyleOpacity {
|
|
|
103
140
|
radialGradient: RadialGradient;
|
|
104
141
|
}
|
|
105
142
|
|
|
143
|
+
export interface AngularGradient {
|
|
144
|
+
stops: GradientStop[];
|
|
145
|
+
center: UnitPoint;
|
|
146
|
+
/** Degrees, clockwise from the positive x axis (3 o'clock); the stops span start...end. */
|
|
147
|
+
startAngle: number;
|
|
148
|
+
endAngle: number;
|
|
149
|
+
}
|
|
150
|
+
export interface AngularGradientStyle extends StyleOpacity {
|
|
151
|
+
angularGradient: AngularGradient;
|
|
152
|
+
}
|
|
153
|
+
|
|
106
154
|
/** `Color.gradient`: a subtle vertical gradient derived from one color. */
|
|
107
155
|
export interface ColorGradientStyle extends StyleOpacity {
|
|
108
156
|
colorGradient: Color;
|
|
@@ -119,6 +167,7 @@ export type ShapeStyle =
|
|
|
119
167
|
| HierarchicalStyle
|
|
120
168
|
| LinearGradientStyle
|
|
121
169
|
| RadialGradientStyle
|
|
170
|
+
| AngularGradientStyle
|
|
122
171
|
| ColorGradientStyle
|
|
123
172
|
| MaterialStyle;
|
|
124
173
|
|
|
@@ -275,6 +324,11 @@ export interface Style {
|
|
|
275
324
|
background?: ShapeStyle;
|
|
276
325
|
frame?: Frame;
|
|
277
326
|
cornerRadius?: number;
|
|
327
|
+
/**
|
|
328
|
+
* `.background(_:)` in a container shape (`.containerShape`): the
|
|
329
|
+
* background's corner radius. Unlike `cornerRadius` it does not clip.
|
|
330
|
+
*/
|
|
331
|
+
backgroundCornerRadius?: number;
|
|
278
332
|
opacity?: number;
|
|
279
333
|
/** Dims the subtree (`opacity: 0.4`) and blocks input (`pointer-events: none`). */
|
|
280
334
|
disabled?: boolean;
|
|
@@ -293,6 +347,8 @@ export interface Style {
|
|
|
293
347
|
*/
|
|
294
348
|
animation?: Animation;
|
|
295
349
|
animationToken?: number;
|
|
350
|
+
/** This box is a `Label`'s icon: a list row tints it (its title keeps the row's color). */
|
|
351
|
+
labelIcon?: boolean;
|
|
296
352
|
/** How this subtree appears and disappears inside an animated commit. */
|
|
297
353
|
transition?: Transition;
|
|
298
354
|
// Phase 5
|
|
@@ -364,6 +420,34 @@ export type HStackAlignment = 'top' | 'center' | 'bottom';
|
|
|
364
420
|
|
|
365
421
|
export interface TextProps {
|
|
366
422
|
text: string;
|
|
423
|
+
/**
|
|
424
|
+
* Rich text (`Text + Text`, `Text(AttributedString)`, an interpolated
|
|
425
|
+
* image, a Text-only modifier such as `.italic()`): the text as runs, each
|
|
426
|
+
* styled on its own. `text` is then the plain content.
|
|
427
|
+
*/
|
|
428
|
+
runs?: TextRun[];
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* One run of a rich text: its `text`, or an inline `image` (an `image`
|
|
432
|
+
* element's props). Unset fields take the text's own style.
|
|
433
|
+
*/
|
|
434
|
+
export interface TextRun {
|
|
435
|
+
text?: string;
|
|
436
|
+
image?: ImageProps;
|
|
437
|
+
/** A Font JSON object applied over the text's font (as `styled` `font`). */
|
|
438
|
+
font?: Record<string, unknown>;
|
|
439
|
+
italic?: boolean;
|
|
440
|
+
/** A ShapeStyle JSON object (as `styled` `foreground`). */
|
|
441
|
+
foreground?: unknown;
|
|
442
|
+
/** A line through or under the run; `color` defaults to the run's own. */
|
|
443
|
+
strikethrough?: { color?: unknown };
|
|
444
|
+
underline?: { color?: unknown };
|
|
445
|
+
/** Extra space after each character, in points. */
|
|
446
|
+
kerning?: number;
|
|
447
|
+
/** Points to raise the run by (negative lowers it). */
|
|
448
|
+
baselineOffset?: number;
|
|
449
|
+
/** A tap on the run sends `link` with this URL. */
|
|
450
|
+
link?: string;
|
|
367
451
|
}
|
|
368
452
|
export interface VStackProps {
|
|
369
453
|
spacing: number | null;
|
|
@@ -399,6 +483,12 @@ export interface StyledProps {
|
|
|
399
483
|
/** Same alignment names as `Frame.alignment`; later children draw on top. */
|
|
400
484
|
export interface ZStackProps {
|
|
401
485
|
alignment: FrameAlignment;
|
|
486
|
+
/**
|
|
487
|
+
* Report the element's laid-out size with `geometry` events, as a
|
|
488
|
+
* `geometry` element does (an anchor preference's wrapper; see
|
|
489
|
+
* docs/ops-protocol.md, "Preferences").
|
|
490
|
+
*/
|
|
491
|
+
reportsSize?: boolean;
|
|
402
492
|
}
|
|
403
493
|
/** `systemName` is an SF Symbol name; the renderer picks the closest glyph. */
|
|
404
494
|
export interface SymbolImageProps {
|
|
@@ -415,7 +505,69 @@ export interface AssetImageProps {
|
|
|
415
505
|
name: string;
|
|
416
506
|
resizable: boolean;
|
|
417
507
|
}
|
|
418
|
-
|
|
508
|
+
/**
|
|
509
|
+
* Phase 13: a remote image (`AsyncImage`). The page loaded it already (an
|
|
510
|
+
* `image` request op) and told the app its pixel size; `width` / `height`
|
|
511
|
+
* are that size in points, the intrinsic size the layout engine uses unless
|
|
512
|
+
* `resizable`.
|
|
513
|
+
*/
|
|
514
|
+
export interface UrlImageProps {
|
|
515
|
+
url: string;
|
|
516
|
+
resizable: boolean;
|
|
517
|
+
width: number;
|
|
518
|
+
height: number;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* A `UIImage` / `CGImage` bitmap (`Image(uiImage:)`): a recipe the page draws
|
|
522
|
+
* (src/bitmaps.ts). `width` / `height` are the image's size in points, the
|
|
523
|
+
* intrinsic size unless `resizable`. See docs/ops-protocol.md, "Bitmaps and
|
|
524
|
+
* Core Image".
|
|
525
|
+
*/
|
|
526
|
+
export interface BitmapImageProps {
|
|
527
|
+
bitmap: BitmapRecipe;
|
|
528
|
+
resizable: boolean;
|
|
529
|
+
width: number;
|
|
530
|
+
height: number;
|
|
531
|
+
/** `.interpolation(.none)`: scaled without smoothing. */
|
|
532
|
+
interpolation?: 'none';
|
|
533
|
+
}
|
|
534
|
+
export type ImageProps = SymbolImageProps | AssetImageProps | UrlImageProps | BitmapImageProps;
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* How to draw a bitmap: the `imagedata` source named by `source` (null: an
|
|
538
|
+
* empty image) with `steps` applied in order, in Core Image's coordinate
|
|
539
|
+
* space (units of source pixels, origin at the bottom left). The result is
|
|
540
|
+
* `width` × `height` pixels.
|
|
541
|
+
*/
|
|
542
|
+
export interface BitmapRecipe {
|
|
543
|
+
source: string | null;
|
|
544
|
+
/** A frame of an animated source (a GIF), composed as the frames before it leave the canvas; absent: the file as it is. */
|
|
545
|
+
frame?: number;
|
|
546
|
+
steps: BitmapStep[];
|
|
547
|
+
width: number;
|
|
548
|
+
height: number;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** A point in Core Image's space. */
|
|
552
|
+
export type BitmapPoint = [number, number];
|
|
553
|
+
|
|
554
|
+
/** One step of a recipe; the formulas are in docs/ops-protocol.md. */
|
|
555
|
+
export type BitmapStep =
|
|
556
|
+
| { op: 'crop' | 'render'; x: number; y: number; width: number; height: number }
|
|
557
|
+
| { op: 'clamp' }
|
|
558
|
+
| { op: 'sepiaTone'; intensity: number }
|
|
559
|
+
| { op: 'edges'; intensity: number }
|
|
560
|
+
| { op: 'crystallize'; radius: number; center: BitmapPoint }
|
|
561
|
+
| { op: 'pixellate'; scale: number; center: BitmapPoint }
|
|
562
|
+
| { op: 'gaussianBlur'; radius: number }
|
|
563
|
+
| { op: 'unsharpMask'; radius: number; intensity: number }
|
|
564
|
+
| { op: 'vignette'; intensity: number; radius: number; center: BitmapPoint; size: number }
|
|
565
|
+
/** Core Graphics drawing an image in a rectangle: every point p goes to (p.x × sx + tx, p.y × sy + ty). */
|
|
566
|
+
| { op: 'place'; sx: number; sy: number; tx: number; ty: number }
|
|
567
|
+
/** A clip to a rounded rectangle (an ellipse when the radii are half its sides). */
|
|
568
|
+
| { op: 'roundedClip'; x: number; y: number; width: number; height: number; rx: number; ry: number }
|
|
569
|
+
/** `UIImage.withTintColor`: every pixel the color (straight RGBA, 0…1), at the pixel's alpha times the color's. */
|
|
570
|
+
| { op: 'tint'; r: number; g: number; b: number; a: number };
|
|
419
571
|
export type DividerProps = Record<string, never>;
|
|
420
572
|
export type ScrollAxes = 'vertical' | 'horizontal' | 'both';
|
|
421
573
|
export interface ScrollViewProps {
|
|
@@ -469,7 +621,9 @@ export interface ColorProps {
|
|
|
469
621
|
color: Color;
|
|
470
622
|
}
|
|
471
623
|
/** Phase 6 adds `containerRelative` (`ContainerRelativeShape`): a rounded rectangle with `cornerRadius`. */
|
|
472
|
-
export type ShapeKind = 'rectangle' | 'roundedRectangle' | 'circle' | 'capsule' | 'ellipse' | 'containerRelative'
|
|
624
|
+
export type ShapeKind = 'rectangle' | 'roundedRectangle' | 'circle' | 'capsule' | 'ellipse' | 'containerRelative'
|
|
625
|
+
/** A custom shape drawn from its path (`ShapeProps.path`). */
|
|
626
|
+
| 'path';
|
|
473
627
|
export interface Stroke {
|
|
474
628
|
/** Phase 6: any ShapeStyle; a gradient stroke falls back to its first stop. */
|
|
475
629
|
color: ShapeStyle;
|
|
@@ -480,6 +634,13 @@ export interface Stroke {
|
|
|
480
634
|
* `data-sb-stroke`.
|
|
481
635
|
*/
|
|
482
636
|
inset?: boolean;
|
|
637
|
+
/** A custom shape's `StrokeStyle` (a built-in shape's border has none of these but `dash`). */
|
|
638
|
+
lineCap?: 'butt' | 'round' | 'square';
|
|
639
|
+
lineJoin?: 'miter' | 'round' | 'bevel';
|
|
640
|
+
miterLimit?: number;
|
|
641
|
+
/** A built-in shape draws any dash pattern as CSS dashes. */
|
|
642
|
+
dash?: number[];
|
|
643
|
+
dashPhase?: number;
|
|
483
644
|
}
|
|
484
645
|
/** `fill: null` means the current foreground color; fills its proposal. */
|
|
485
646
|
export interface ShapeProps {
|
|
@@ -488,9 +649,33 @@ export interface ShapeProps {
|
|
|
488
649
|
/** Phase 6: any ShapeStyle. */
|
|
489
650
|
fill: ShapeStyle | null;
|
|
490
651
|
stroke: Stroke | null;
|
|
652
|
+
/**
|
|
653
|
+
* `.trim(from:to:)`: the part of the outline between the fractions, drawn
|
|
654
|
+
* as a conic mask around the center from 3 o'clock, clockwise (exact for a
|
|
655
|
+
* circle). Absent when the shape is not trimmed.
|
|
656
|
+
*/
|
|
657
|
+
trim?: { from: number; to: number };
|
|
658
|
+
/**
|
|
659
|
+
* A custom shape (`shape: "path"`, src/pathShape.ts): SVG path data for a
|
|
660
|
+
* rectangle of `size` at the origin, the size the element reported
|
|
661
|
+
* (`reportsSize`). Empty until the first report.
|
|
662
|
+
*/
|
|
663
|
+
path?: string;
|
|
664
|
+
size?: { width: number; height: number };
|
|
665
|
+
/** `FillStyle(eoFill: true)`: overlapping parts of the path cancel out. */
|
|
666
|
+
fillRule?: 'evenodd';
|
|
667
|
+
/** The element reports its laid-out size (a custom shape draws its path for it). */
|
|
668
|
+
reportsSize?: boolean;
|
|
669
|
+
/**
|
|
670
|
+
* An animated change of the shape's `animatableData`: the path at each
|
|
671
|
+
* fraction of the change (`paths[index[i]]` at `progress[i]`), played in
|
|
672
|
+
* the commit's animation.
|
|
673
|
+
*/
|
|
674
|
+
frames?: { progress: number[]; index: number[]; paths: string[] };
|
|
491
675
|
}
|
|
492
676
|
/** Phase 8: `full` is `.fullScreenCover` (the card fills the screen). */
|
|
493
|
-
|
|
677
|
+
/** `height:<points>` / `fraction:<f>`: `.presentationDetents([.height(300)])`, `.fraction(0.4)`. */
|
|
678
|
+
export type Detent = 'medium' | 'large' | 'full' | `height:${number}` | `fraction:${number}`;
|
|
494
679
|
/**
|
|
495
680
|
* Always a root-level element (child of id 0) placed after the app's tree. A
|
|
496
681
|
* `tap` with the sheet's own id is a dismiss request.
|
|
@@ -510,6 +695,10 @@ export interface TabViewProps {
|
|
|
510
695
|
export interface TabProps {
|
|
511
696
|
title: string;
|
|
512
697
|
systemImage: string | null;
|
|
698
|
+
/** iOS 18 `Tab(…).badge(…)`: a red badge on the bar item (absent when none). */
|
|
699
|
+
badge?: string;
|
|
700
|
+
/** iOS 18 `Tab(role: .search)` (absent otherwise); Swift already put it last. */
|
|
701
|
+
role?: 'search';
|
|
513
702
|
}
|
|
514
703
|
export type PickerStyle = 'segmented' | 'menu' | 'inline';
|
|
515
704
|
/**
|
|
@@ -611,11 +800,13 @@ export type GaugeStyle =
|
|
|
611
800
|
| 'linearCapacity'
|
|
612
801
|
| 'accessoryLinear'
|
|
613
802
|
| 'accessoryCircular'
|
|
614
|
-
| 'accessoryCircularCapacity'
|
|
803
|
+
| 'accessoryCircularCapacity'
|
|
804
|
+
/** A determinate `ProgressView`: the label above the bar, the current value label under it. */
|
|
805
|
+
| 'progress';
|
|
615
806
|
/**
|
|
616
807
|
* `Gauge`: `value` is normalized to 0...1. Children in order: the label (when
|
|
617
808
|
* `hasLabel`) and the current value label (when `hasCurrentValueLabel`).
|
|
618
|
-
* Circular styles
|
|
809
|
+
* Circular styles draw a ring with one of the labels in the middle.
|
|
619
810
|
*/
|
|
620
811
|
export interface GaugeProps {
|
|
621
812
|
value: number;
|
|
@@ -641,7 +832,16 @@ export interface DecoratedProps {
|
|
|
641
832
|
* a popover built from the current items (`button`/`navlink` rows that send
|
|
642
833
|
* `tap`, `picker` sections that send `select`, `divider`, `section`, `text`).
|
|
643
834
|
*/
|
|
644
|
-
|
|
835
|
+
/**
|
|
836
|
+
* Lazy menus: `deferred: true` while the items are not built (only the label
|
|
837
|
+
* is a child). The page sends a `menu` event with `open: true` when the
|
|
838
|
+
* viewer opens it and shows the popover once the items arrive (`deferred:
|
|
839
|
+
* false`), and `open: false` when it closes. Absent when the app builds the
|
|
840
|
+
* items eagerly (headless runs, the mocks).
|
|
841
|
+
*/
|
|
842
|
+
export interface MenuProps {
|
|
843
|
+
deferred?: boolean;
|
|
844
|
+
}
|
|
645
845
|
export type ToolbarPlacement = 'trailing' | 'leading' | 'principal' | 'bottom';
|
|
646
846
|
/**
|
|
647
847
|
* `.toolbar { ToolbarItem(placement:) { ... } }`, an extra child of a
|
|
@@ -662,8 +862,13 @@ export interface SearchFieldProps {
|
|
|
662
862
|
/** Defaults to "Search". */
|
|
663
863
|
placeholder: string;
|
|
664
864
|
}
|
|
665
|
-
/**
|
|
666
|
-
|
|
865
|
+
/**
|
|
866
|
+
* `.onTapGesture`: one child, the child's size; a click anywhere on it sends
|
|
867
|
+
* `tap`. `location` (`onTapGesture { point in }`) adds the click's point.
|
|
868
|
+
*/
|
|
869
|
+
export interface TapGestureProps {
|
|
870
|
+
location?: boolean;
|
|
871
|
+
}
|
|
667
872
|
|
|
668
873
|
export interface ElementPropsByKind {
|
|
669
874
|
text: TextProps;
|
|
@@ -758,7 +963,238 @@ export interface DefaultsOp {
|
|
|
758
963
|
values: Record<string, unknown>;
|
|
759
964
|
}
|
|
760
965
|
|
|
761
|
-
|
|
966
|
+
/**
|
|
967
|
+
* Phase 13: something only the page can do for the app. The loader performs
|
|
968
|
+
* it (src/hostRequests.ts) and answers with a `ResponseEvent` of the same
|
|
969
|
+
* `id`; the renderer never sees it.
|
|
970
|
+
*/
|
|
971
|
+
export interface RequestOp {
|
|
972
|
+
op: 'request';
|
|
973
|
+
id: number;
|
|
974
|
+
kind: 'fetch' | 'image' | 'share' | 'pickFiles' | 'notification' | 'scanner' | 'websocket' | 'openURL' | 'webAuth' | 'media' | 'download' | 'clipboard' | 'translate' | 'pickColor';
|
|
975
|
+
params:
|
|
976
|
+
| FetchRequestParams
|
|
977
|
+
| ImageRequestParams
|
|
978
|
+
| ShareRequestParams
|
|
979
|
+
| PickFilesRequestParams
|
|
980
|
+
| NotificationRequestParams
|
|
981
|
+
| ScannerRequestParams
|
|
982
|
+
| WebSocketRequestParams
|
|
983
|
+
| OpenURLRequestParams
|
|
984
|
+
| WebAuthRequestParams
|
|
985
|
+
| MediaRequestParams
|
|
986
|
+
| DownloadRequestParams
|
|
987
|
+
| ClipboardRequestParams
|
|
988
|
+
| TranslateRequestParams
|
|
989
|
+
| PickColorRequestParams;
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* `translate` (Translation; src/translation.ts): `availability` of a pair
|
|
993
|
+
* (`source` null: detected from `text`), or `translate` the `texts` from
|
|
994
|
+
* `source` (null: detected) to `target`, BCP 47 tags. `allowDownload`: the
|
|
995
|
+
* session may download the pair's model.
|
|
996
|
+
*/
|
|
997
|
+
export interface TranslateRequestParams {
|
|
998
|
+
action: 'availability' | 'translate';
|
|
999
|
+
source?: string | null;
|
|
1000
|
+
target: string;
|
|
1001
|
+
text?: string;
|
|
1002
|
+
texts?: string[];
|
|
1003
|
+
allowDownload?: boolean;
|
|
1004
|
+
}
|
|
1005
|
+
/** `pickColor` (`ColorPicker`; src/colorPicker.ts): the chooser opens at `color` (`#rrggbb`). */
|
|
1006
|
+
export interface PickColorRequestParams {
|
|
1007
|
+
color?: string;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* `URLSessionWebSocketTask`: one call on a socket (src/webSocket.ts). `open`
|
|
1011
|
+
* takes `url` and `protocols`; the others the `socket` id `open` answered.
|
|
1012
|
+
*/
|
|
1013
|
+
/** `openURL` (`OpenURLAction`, a link in a `Text`, `UIApplication.open`): the page opens `url` in a new tab; the value is null. */
|
|
1014
|
+
/**
|
|
1015
|
+
* `webAuth` (AuthenticationServices; docs/ops-protocol.md, "Signing in"):
|
|
1016
|
+
* `start` opens `url`, the provider's authorize page, in a popup window and
|
|
1017
|
+
* answers `{ url }`, the callback page URL the window ended at; `cancel` closes
|
|
1018
|
+
* the window, and the pending `start` fails with `cancelled: …`.
|
|
1019
|
+
* `callbackScheme` is the app's scheme the redirect stands for, `callbackPage`
|
|
1020
|
+
* the callback page the app put in its place; `ephemeral` is the app's wish
|
|
1021
|
+
* for a private session, which a popup cannot grant.
|
|
1022
|
+
*/
|
|
1023
|
+
export interface WebAuthRequestParams {
|
|
1024
|
+
action?: 'start' | 'cancel';
|
|
1025
|
+
url?: string;
|
|
1026
|
+
callbackScheme?: string;
|
|
1027
|
+
callbackPage?: string;
|
|
1028
|
+
ephemeral?: boolean;
|
|
1029
|
+
}
|
|
1030
|
+
export interface OpenURLRequestParams {
|
|
1031
|
+
url: string;
|
|
1032
|
+
}
|
|
1033
|
+
export interface WebSocketRequestParams {
|
|
1034
|
+
action: 'open' | 'send' | 'receive' | 'close';
|
|
1035
|
+
url?: string;
|
|
1036
|
+
protocols?: string[];
|
|
1037
|
+
socket?: number;
|
|
1038
|
+
/** `send`: a text message. */
|
|
1039
|
+
string?: string;
|
|
1040
|
+
/** `send`: a binary message, base64. */
|
|
1041
|
+
data?: string;
|
|
1042
|
+
/** `close`: the close code and reason. */
|
|
1043
|
+
code?: number;
|
|
1044
|
+
reason?: string;
|
|
1045
|
+
}
|
|
1046
|
+
/** `URLSession`: one HTTP request. `body` is base64; `timeout` in ms (0: none). */
|
|
1047
|
+
export interface FetchRequestParams {
|
|
1048
|
+
url: string;
|
|
1049
|
+
method: string;
|
|
1050
|
+
headers: Record<string, string>;
|
|
1051
|
+
body: string | null;
|
|
1052
|
+
timeout?: number;
|
|
1053
|
+
}
|
|
1054
|
+
/** `AsyncImage`: decode the image at `url` and report its pixel size. */
|
|
1055
|
+
export interface ImageRequestParams {
|
|
1056
|
+
url: string;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
/**
|
|
1060
|
+
* `ShareLink`: share through the Web Share API, or download where the browser
|
|
1061
|
+
* cannot. `images` are asset-catalog names, shared (or downloaded) as their
|
|
1062
|
+
* files. The response value is a `ShareResult`.
|
|
1063
|
+
*/
|
|
1064
|
+
export interface ShareRequestParams {
|
|
1065
|
+
title?: string;
|
|
1066
|
+
text?: string;
|
|
1067
|
+
url?: string;
|
|
1068
|
+
images?: string[];
|
|
1069
|
+
/** `Image(uiImage:)` items: recipes the page draws into PNG files. */
|
|
1070
|
+
bitmaps?: BitmapRecipe[];
|
|
1071
|
+
/** Files the app made: a `Transferable` item's exported bytes, a `UIImage`'s own file. */
|
|
1072
|
+
files?: SharedFile[];
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** A file the app hands to the page whole: its name, MIME type and bytes (base64). */
|
|
1076
|
+
export interface SharedFile {
|
|
1077
|
+
name: string;
|
|
1078
|
+
type: string;
|
|
1079
|
+
data: string;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/**
|
|
1083
|
+
* Saving (Photos' change requests, `UIImageWriteToSavedPhotosAlbum`): the
|
|
1084
|
+
* page downloads each file, catalog image and bitmap (a PNG named after
|
|
1085
|
+
* `title`). The response value is a `ShareResult` (`downloaded`).
|
|
1086
|
+
*/
|
|
1087
|
+
export interface DownloadRequestParams {
|
|
1088
|
+
title?: string;
|
|
1089
|
+
files?: SharedFile[];
|
|
1090
|
+
images?: string[];
|
|
1091
|
+
bitmaps?: BitmapRecipe[];
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* `UIPasteboard.general`: `write` copies `text`, or the first picture (a
|
|
1096
|
+
* file, catalog image or bitmap, as PNG) to the system clipboard and answers
|
|
1097
|
+
* `{}`; `readText` answers `{ text }`.
|
|
1098
|
+
*/
|
|
1099
|
+
export interface ClipboardRequestParams {
|
|
1100
|
+
action: 'write' | 'readText';
|
|
1101
|
+
text?: string;
|
|
1102
|
+
files?: SharedFile[];
|
|
1103
|
+
images?: string[];
|
|
1104
|
+
bitmaps?: BitmapRecipe[];
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* `PhotosPicker`: let the viewer choose files with the browser's file
|
|
1109
|
+
* chooser. `accept` is the input's `accept` (`image/*,video/*`; empty: any
|
|
1110
|
+
* file). The response value is a `PickFilesResult`; a dismissed chooser
|
|
1111
|
+
* answers with no items.
|
|
1112
|
+
*/
|
|
1113
|
+
export interface PickFilesRequestParams {
|
|
1114
|
+
accept: string;
|
|
1115
|
+
multiple: boolean;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* UserNotifications (docs/ops-protocol.md, "Notifications"): `status` reads
|
|
1120
|
+
* `Notification.permission`, `request` asks for it, `show` presents a
|
|
1121
|
+
* notification that came due (`title` absent: the badge only), `close`
|
|
1122
|
+
* closes delivered ones by identifier, `badge` sets the app badge. The
|
|
1123
|
+
* response value is a `NotificationResult`.
|
|
1124
|
+
*/
|
|
1125
|
+
export interface NotificationRequestParams {
|
|
1126
|
+
action: 'status' | 'request' | 'show' | 'close' | 'badge';
|
|
1127
|
+
/** `show`: the request's identifier, the notification's `tag`. */
|
|
1128
|
+
id?: string;
|
|
1129
|
+
title?: string;
|
|
1130
|
+
subtitle?: string;
|
|
1131
|
+
body?: string;
|
|
1132
|
+
silent?: boolean;
|
|
1133
|
+
/** `show` and `badge`: the app badge (0 clears it). */
|
|
1134
|
+
badge?: number;
|
|
1135
|
+
/** `close`: the identifiers to close. */
|
|
1136
|
+
ids?: string[];
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* AVFoundation's capture sessions (docs/ops-protocol.md, "Code scanning"):
|
|
1141
|
+
* `probe` asks whether the page can scan `formats` (`BarcodeDetector` names)
|
|
1142
|
+
* with a camera; `scan` opens the camera over the page until a code is found
|
|
1143
|
+
* or the viewer closes it, or, where the browser cannot read codes, asks the
|
|
1144
|
+
* viewer to type one. The response value is a `ScannerResult`.
|
|
1145
|
+
*/
|
|
1146
|
+
export interface ScannerRequestParams {
|
|
1147
|
+
action: 'probe' | 'scan';
|
|
1148
|
+
formats: string[];
|
|
1149
|
+
torch?: boolean;
|
|
1150
|
+
vibrate?: boolean;
|
|
1151
|
+
/** Which camera: the back one (`environment`, the default) or the front one (`user`). */
|
|
1152
|
+
facing?: 'environment' | 'user';
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Phase 18: one save of a SwiftData store (`ModelContext.save()`, or the main
|
|
1157
|
+
* context's autosave at the end of an event). The loader hands it to the page,
|
|
1158
|
+
* which writes it to the app's IndexedDB database (src/swiftData.ts) and
|
|
1159
|
+
* passes every store back at the next launch as `SB_SWIFTDATA`. `clear`
|
|
1160
|
+
* (`ModelContainer.deleteAllData()`) empties the store before `put`.
|
|
1161
|
+
*/
|
|
1162
|
+
export interface SwiftDataOp {
|
|
1163
|
+
op: 'swiftdata';
|
|
1164
|
+
store: string;
|
|
1165
|
+
put: SwiftDataRecord[];
|
|
1166
|
+
delete: number[];
|
|
1167
|
+
clear?: boolean;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/** A saved model: its key in the store, its entity (class) name and its properties' JSON. */
|
|
1171
|
+
export interface SwiftDataRecord {
|
|
1172
|
+
id: number;
|
|
1173
|
+
entity: string;
|
|
1174
|
+
values: Record<string, unknown>;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* A bitmap's source, sent once (by content `key`) before the first element
|
|
1179
|
+
* that draws it: an encoded image file (`mime`, `data`) or a pixel buffer
|
|
1180
|
+
* (`format`, `pixels`: one gray or four RGBA bytes per pixel, rows from the
|
|
1181
|
+
* top). The bytes are base64 in printed ops; from a module with the
|
|
1182
|
+
* `sb_bytes_*` exports, src/wasm.ts reads them out of linear memory
|
|
1183
|
+
* (docs/ops-protocol.md, "Bytes"). `width` × `height` is its size in pixels, as
|
|
1184
|
+
* drawn (a JPEG's EXIF rotation applied). The renderer keeps it for the run.
|
|
1185
|
+
*/
|
|
1186
|
+
export interface ImageDataOp {
|
|
1187
|
+
op: 'imagedata';
|
|
1188
|
+
key: string;
|
|
1189
|
+
width: number;
|
|
1190
|
+
height: number;
|
|
1191
|
+
mime?: string;
|
|
1192
|
+
data?: string | Uint8Array;
|
|
1193
|
+
format?: 'gray' | 'rgba';
|
|
1194
|
+
pixels?: string | Uint8Array;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
export type Op = CreateOp | UpdateOp | InsertOp | RemoveOp | CommitOp | DefaultsOp | RequestOp | SwiftDataOp | ImageDataOp;
|
|
762
1198
|
|
|
763
1199
|
// ---------------------------------------------------------------------------
|
|
764
1200
|
// Events (renderer → Swift)
|
|
@@ -806,6 +1242,15 @@ export function isDynamicTypeSize(value: unknown): value is DynamicTypeSize {
|
|
|
806
1242
|
export interface TapEvent {
|
|
807
1243
|
type: 'tap';
|
|
808
1244
|
id: ElementId;
|
|
1245
|
+
/** A `tapgesture` with `location`: the tap's point in the element's own coordinates. */
|
|
1246
|
+
x?: number;
|
|
1247
|
+
y?: number;
|
|
1248
|
+
}
|
|
1249
|
+
/** A link run (`TextRun.link`) of a `text` was tapped; `value` is its URL. */
|
|
1250
|
+
export interface LinkEvent {
|
|
1251
|
+
type: 'link';
|
|
1252
|
+
id: ElementId;
|
|
1253
|
+
value: string;
|
|
809
1254
|
}
|
|
810
1255
|
/** A `textfield` or `searchfield` received input. */
|
|
811
1256
|
export interface TextEvent {
|
|
@@ -875,13 +1320,32 @@ export type RendererEvent =
|
|
|
875
1320
|
| ToggleEvent
|
|
876
1321
|
| EnvironmentEvent
|
|
877
1322
|
| SelectEvent
|
|
1323
|
+
| LinkEvent
|
|
878
1324
|
| GeometryEvent
|
|
879
1325
|
| LayoutEvent
|
|
880
1326
|
| BatchEvent
|
|
881
1327
|
// Phase 8: gestures, rows, navigation (see the block at the end of this file)
|
|
882
1328
|
| InteractionEvent
|
|
883
1329
|
// Phase 8: alerts, controls (see the block at the end of this file)
|
|
884
|
-
| Phase8ControlEvent
|
|
1330
|
+
| Phase8ControlEvent
|
|
1331
|
+
// Phase 13: the answer to a `request` op
|
|
1332
|
+
| ResponseEvent
|
|
1333
|
+
// A `video` element's playback (src/video.ts)
|
|
1334
|
+
| VideoEvent
|
|
1335
|
+
| LazyEvent
|
|
1336
|
+
| MenuEvent;
|
|
1337
|
+
|
|
1338
|
+
/**
|
|
1339
|
+
* Phase 13: the page's answer to the `request` op with this `id`: `value`
|
|
1340
|
+
* (a `FetchResult` or `ImageResult` from src/hostRequests.ts) on success,
|
|
1341
|
+
* `error` (a short message the app maps to a `URLError`) on failure.
|
|
1342
|
+
*/
|
|
1343
|
+
export interface ResponseEvent {
|
|
1344
|
+
type: 'response';
|
|
1345
|
+
id: number;
|
|
1346
|
+
value?: unknown;
|
|
1347
|
+
error?: string;
|
|
1348
|
+
}
|
|
885
1349
|
|
|
886
1350
|
/**
|
|
887
1351
|
* Several events delivered in one `sb_event_json` call: Swift applies each
|
|
@@ -934,6 +1398,11 @@ export function isRadialGradientStyle(style: ShapeStyle): style is RadialGradien
|
|
|
934
1398
|
return typeof g === 'object' && g !== null;
|
|
935
1399
|
}
|
|
936
1400
|
|
|
1401
|
+
export function isAngularGradientStyle(style: ShapeStyle): style is AngularGradientStyle {
|
|
1402
|
+
const g = (style as AngularGradientStyle).angularGradient;
|
|
1403
|
+
return typeof g === 'object' && g !== null;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
937
1406
|
export function isColorGradientStyle(style: ShapeStyle): style is ColorGradientStyle {
|
|
938
1407
|
const c = (style as ColorGradientStyle).colorGradient;
|
|
939
1408
|
return typeof c === 'object' && c !== null;
|
|
@@ -948,12 +1417,31 @@ export function isAssetImage(props: ImageProps): props is AssetImageProps {
|
|
|
948
1417
|
return typeof (props as AssetImageProps).name === 'string';
|
|
949
1418
|
}
|
|
950
1419
|
|
|
1420
|
+
/** True for the bitmap form of `image` props (`{ bitmap, resizable, width, height }`). */
|
|
1421
|
+
export function isBitmapImage(props: ImageProps): props is BitmapImageProps {
|
|
1422
|
+
const bitmap = (props as BitmapImageProps).bitmap;
|
|
1423
|
+
return typeof bitmap === 'object' && bitmap !== null;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/** True for the remote form of `image` props (`{ url, resizable, width, height }`, Phase 13). */
|
|
1427
|
+
export function isUrlImage(props: ImageProps): props is UrlImageProps {
|
|
1428
|
+
return typeof (props as UrlImageProps).url === 'string';
|
|
1429
|
+
}
|
|
1430
|
+
|
|
951
1431
|
/** Cheap structural check used when parsing an untrusted JSON buffer. */
|
|
952
1432
|
export function isOp(value: unknown): value is Op {
|
|
953
1433
|
if (typeof value !== 'object' || value === null) return false;
|
|
954
1434
|
const op = (value as { op?: unknown }).op;
|
|
955
1435
|
return (
|
|
956
|
-
op === 'create' ||
|
|
1436
|
+
op === 'create' ||
|
|
1437
|
+
op === 'update' ||
|
|
1438
|
+
op === 'insert' ||
|
|
1439
|
+
op === 'remove' ||
|
|
1440
|
+
op === 'commit' ||
|
|
1441
|
+
op === 'defaults' ||
|
|
1442
|
+
op === 'request' ||
|
|
1443
|
+
op === 'swiftdata' ||
|
|
1444
|
+
op === 'imagedata'
|
|
957
1445
|
);
|
|
958
1446
|
}
|
|
959
1447
|
|
|
@@ -1095,6 +1583,8 @@ export interface ChartAxisLabel {
|
|
|
1095
1583
|
/** Unit position along the axis (y: 0 is the top). */
|
|
1096
1584
|
at: number;
|
|
1097
1585
|
text: string;
|
|
1586
|
+
/** `leading`: an x label that starts at its tick (a date's) instead of centering on it. */
|
|
1587
|
+
anchor?: 'leading';
|
|
1098
1588
|
}
|
|
1099
1589
|
/** `null` for the whole axis means hidden. Positions are unit values along the axis. */
|
|
1100
1590
|
export interface ChartAxis {
|
|
@@ -1192,7 +1682,10 @@ export interface SwipeActionsProps {
|
|
|
1192
1682
|
}
|
|
1193
1683
|
|
|
1194
1684
|
/** `.contextMenu { }`: `[content, ...items]`; a long press or right click lists the items (as in a `menu`) in a popover. */
|
|
1195
|
-
|
|
1685
|
+
/** As `MenuProps`: `deferred` while the items (every child but the content) are not built. */
|
|
1686
|
+
export interface ContextMenuProps {
|
|
1687
|
+
deferred?: boolean;
|
|
1688
|
+
}
|
|
1196
1689
|
|
|
1197
1690
|
/**
|
|
1198
1691
|
* A `List` row that can be selected (`selectable`, `selected`) or deleted
|
|
@@ -1235,6 +1728,19 @@ export interface Style {
|
|
|
1235
1728
|
hitTesting?: boolean;
|
|
1236
1729
|
}
|
|
1237
1730
|
|
|
1731
|
+
export interface Style {
|
|
1732
|
+
/** `.blendMode`: the CSS `mix-blend-mode` the box is composited with. */
|
|
1733
|
+
blendMode?: string;
|
|
1734
|
+
/** `.zIndex(_:)`: the box's CSS `z-index` among its siblings. */
|
|
1735
|
+
zIndex?: number;
|
|
1736
|
+
/**
|
|
1737
|
+
* `.alignmentGuide(_:computeValue:)`: the box's guide `name` on `axis` is
|
|
1738
|
+
* `a + b·width + c·height` (`value: [a, b, c]`), which the containers that
|
|
1739
|
+
* align it by that guide use instead of the default (layout/engine.ts).
|
|
1740
|
+
*/
|
|
1741
|
+
alignmentGuide?: { axis: 'horizontal' | 'vertical'; name: string; value: [number, number, number] };
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1238
1744
|
export type DragPhase = 'began' | 'changed' | 'ended' | 'cancelled';
|
|
1239
1745
|
|
|
1240
1746
|
/**
|
|
@@ -1325,6 +1831,8 @@ export interface DatePickerProps {
|
|
|
1325
1831
|
/** A `<textarea>` filling the proposal; input sends `text` like a `textfield`. */
|
|
1326
1832
|
export interface TextEditorProps {
|
|
1327
1833
|
text: string;
|
|
1834
|
+
/** Bound focus (UIKit's `becomeFirstResponder`): the editor takes or gives up focus; `focus` events report changes. */
|
|
1835
|
+
focused?: boolean;
|
|
1328
1836
|
}
|
|
1329
1837
|
|
|
1330
1838
|
/** A `stepper` half was tapped: +1 or −1. */
|
|
@@ -1394,6 +1902,29 @@ export interface Style {
|
|
|
1394
1902
|
rotation3D?: Rotation3D;
|
|
1395
1903
|
/** `.blur(radius:)`: `filter: blur(<radius>px)`, combined with `shadow` in one `filter`. */
|
|
1396
1904
|
blur?: number;
|
|
1905
|
+
/** `.brightness(_:)`: SwiftUI's amount (-1…1), drawn as CSS `brightness(1 + amount)`. */
|
|
1906
|
+
brightness?: number;
|
|
1907
|
+
/** `.saturation(_:)`: CSS `saturate()`. */
|
|
1908
|
+
saturation?: number;
|
|
1909
|
+
/** `.contrast(_:)`: CSS `contrast()`. */
|
|
1910
|
+
contrast?: number;
|
|
1911
|
+
/** `.grayscale(_:)`: CSS `grayscale()` (0…1). */
|
|
1912
|
+
grayscale?: number;
|
|
1913
|
+
/** `.hueRotation(_:)`: degrees, CSS `hue-rotate()`. */
|
|
1914
|
+
hueRotation?: number;
|
|
1915
|
+
/** `.colorInvert()`: CSS `invert(1)`. */
|
|
1916
|
+
colorInvert?: boolean;
|
|
1917
|
+
/**
|
|
1918
|
+
* `.colorMultiply(_:)`: each component (and the alpha) multiplied by the
|
|
1919
|
+
* color's, drawn with an SVG `feColorMatrix` filter (colorMultiply.ts).
|
|
1920
|
+
*/
|
|
1921
|
+
colorMultiply?: Color;
|
|
1922
|
+
/** View `.underline(_:)`: underlines the texts below (false turns an ancestor's off). */
|
|
1923
|
+
underline?: boolean;
|
|
1924
|
+
/** View `.strikethrough(_:)`: strikes the texts below through (false turns it off). */
|
|
1925
|
+
strikethrough?: boolean;
|
|
1926
|
+
/** View `.italic(_:)`: slants the texts below (false turns it off). */
|
|
1927
|
+
italic?: boolean;
|
|
1397
1928
|
/**
|
|
1398
1929
|
* `.ignoresSafeArea(edges:)`: the engine extends the box to the screen
|
|
1399
1930
|
* edge on each listed side when the box touches that safe-area edge. The
|
|
@@ -1419,3 +1950,241 @@ export type ScenePhase = 'active' | 'inactive' | 'background';
|
|
|
1419
1950
|
export interface EnvironmentEvent {
|
|
1420
1951
|
scenePhase?: ScenePhase;
|
|
1421
1952
|
}
|
|
1953
|
+
|
|
1954
|
+
// ---------------------------------------------------------------------------
|
|
1955
|
+
// Multi-line text fields, view section headers, scrolling to a view
|
|
1956
|
+
// ---------------------------------------------------------------------------
|
|
1957
|
+
|
|
1958
|
+
export interface TextFieldProps {
|
|
1959
|
+
/**
|
|
1960
|
+
* `TextField(_:text:axis: .vertical)`: a `<textarea>` that grows with its
|
|
1961
|
+
* text between the enclosing `lineLimitMin` (1) and `lineLimit` (none)
|
|
1962
|
+
* lines and scrolls past that; Return breaks the line unless the field has
|
|
1963
|
+
* `submit` (then Shift-Return does). Absent for a one-line field.
|
|
1964
|
+
*/
|
|
1965
|
+
axis?: 'vertical';
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
export interface ListProps {
|
|
1969
|
+
/** `.scrollContentBackground(.hidden)`: the list draws no background of its own (rows keep theirs). */
|
|
1970
|
+
background?: 'hidden';
|
|
1971
|
+
/** `.scrollDismissesKeyboard(.immediately | .interactively)`: scrolling blurs the text input being edited inside. */
|
|
1972
|
+
dismissesKeyboard?: boolean;
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
export interface ScrollViewProps {
|
|
1976
|
+
/** As for `list`. */
|
|
1977
|
+
dismissesKeyboard?: boolean;
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
/**
|
|
1981
|
+
* A `Section`'s header or footer that is a view rather than a `Text`: the
|
|
1982
|
+
* first (`sectionheader`) or last (`sectionfooter`) child of the `section`,
|
|
1983
|
+
* holding the view. In a list it sits above / below the section's card in
|
|
1984
|
+
* the content width (footnote font, secondary color); elsewhere it stacks
|
|
1985
|
+
* with the rows.
|
|
1986
|
+
*/
|
|
1987
|
+
export type SectionSupplementProps = Record<string, never>;
|
|
1988
|
+
|
|
1989
|
+
export interface ElementPropsByKind {
|
|
1990
|
+
sectionheader: SectionSupplementProps;
|
|
1991
|
+
sectionfooter: SectionSupplementProps;
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
/** `ScrollViewProxy.scrollTo(_:anchor:)`: where the target should end up (null: as little scrolling as shows it). */
|
|
1995
|
+
export interface ScrollToCommand {
|
|
1996
|
+
anchor: UnitPoint | null;
|
|
1997
|
+
/** Increases with every request (across readers); the page carries out each serial once. */
|
|
1998
|
+
serial: number;
|
|
1999
|
+
}
|
|
2000
|
+
|
|
2001
|
+
export interface Style {
|
|
2002
|
+
/** `.id(_:)` inside a `ScrollViewReader`: this box is a scroll target. */
|
|
2003
|
+
scrollID?: boolean;
|
|
2004
|
+
/**
|
|
2005
|
+
* Set in the pass in which a `ScrollViewProxy` asks to scroll to this box
|
|
2006
|
+
* (and kept until the reader re-runs): after laying out a pass that brings
|
|
2007
|
+
* a serial above every one it handled, the page scrolls the scroll views
|
|
2008
|
+
* and lists around it (smoothly when the pass's commit is animated).
|
|
2009
|
+
*/
|
|
2010
|
+
scrollTo?: ScrollToCommand;
|
|
2011
|
+
/** The lower bound of a `.lineLimit(2...4)` range (absent for 1): a vertical text field's minimum lines. */
|
|
2012
|
+
lineLimitMin?: number;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
// ---------------------------------------------------------------------------
|
|
2016
|
+
// Video and audio (docs/ops-protocol.md, "Video and audio")
|
|
2017
|
+
// ---------------------------------------------------------------------------
|
|
2018
|
+
|
|
2019
|
+
/**
|
|
2020
|
+
* A `video` element: an `AVPlayer` shown by a view (AVKit's `VideoPlayer`, an
|
|
2021
|
+
* `AVPlayerLayer`), as an HTML `<video>` (src/video.ts). No children.
|
|
2022
|
+
*/
|
|
2023
|
+
/**
|
|
2024
|
+
* Lazy rows: the rows of a `List` or lazy stack that are not built yet
|
|
2025
|
+
* (`remaining` of them, after the built ones). It takes no space; when it
|
|
2026
|
+
* comes within a screen of its scroll view's visible area the page sends a
|
|
2027
|
+
* `lazy` event, and Swift builds the next rows (a new spacer follows them).
|
|
2028
|
+
*/
|
|
2029
|
+
export interface LazySpacerProps {
|
|
2030
|
+
axis: 'vertical' | 'horizontal';
|
|
2031
|
+
remaining: number;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/** Lazy menus: the viewer opened (`open: true`) or closed a deferred `menu` / `contextmenu`. */
|
|
2035
|
+
export interface MenuEvent {
|
|
2036
|
+
type: 'menu';
|
|
2037
|
+
id: ElementId;
|
|
2038
|
+
open: boolean;
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/** A `lazyspacer` came near the visible area: build the next rows. */
|
|
2042
|
+
export interface LazyEvent {
|
|
2043
|
+
type: 'lazy';
|
|
2044
|
+
id: ElementId;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
export interface VideoProps {
|
|
2048
|
+
/** The media URL (remote, data or blob); null while there is none. */
|
|
2049
|
+
url: string | null;
|
|
2050
|
+
/** Whether the player plays here (only the newest view of a player plays it). */
|
|
2051
|
+
playing: boolean;
|
|
2052
|
+
muted: boolean;
|
|
2053
|
+
/** 0…1. */
|
|
2054
|
+
volume: number;
|
|
2055
|
+
/** The playback rate (positive; the player's default rate while paused). */
|
|
2056
|
+
rate: number;
|
|
2057
|
+
/** An `AVPlayerLooper` loops the item. */
|
|
2058
|
+
loop: boolean;
|
|
2059
|
+
/** CSS `object-fit`: `contain`, `cover`, `fill`. */
|
|
2060
|
+
gravity: 'resizeAspect' | 'resizeAspectFill' | 'resize';
|
|
2061
|
+
/** The browser's native playback controls (`VideoPlayer`, `AVPlayerViewController`). */
|
|
2062
|
+
controls: boolean;
|
|
2063
|
+
/** Seconds; the page seeks once per serial (and reports `seeked` with it). */
|
|
2064
|
+
seek: { time: number; serial: number };
|
|
2065
|
+
/** Report the position this often (seconds) while playing, when shorter than `timeupdate`'s pace. */
|
|
2066
|
+
report?: number;
|
|
2067
|
+
/** The video's size in pixels, once known: the aspect ratio of an axis left open. */
|
|
2068
|
+
width?: number;
|
|
2069
|
+
height?: number;
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
export interface ElementPropsByKind {
|
|
2073
|
+
video: VideoProps;
|
|
2074
|
+
lazyspacer: LazySpacerProps;
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
/**
|
|
2078
|
+
* A gradient of a `Canvas` (`GraphicsContext.Shading`), in the drawing's own
|
|
2079
|
+
* coordinates (points, after the command's `transform`) rather than the
|
|
2080
|
+
* unit square of a view's box.
|
|
2081
|
+
*/
|
|
2082
|
+
export interface CanvasLinearGradientShading {
|
|
2083
|
+
canvasLinearGradient: { stops: GradientStop[]; start: { x: number; y: number }; end: { x: number; y: number } };
|
|
2084
|
+
}
|
|
2085
|
+
export interface CanvasRadialGradientShading {
|
|
2086
|
+
canvasRadialGradient: { stops: GradientStop[]; center: { x: number; y: number }; startRadius: number; endRadius: number };
|
|
2087
|
+
}
|
|
2088
|
+
export type CanvasShading = ShapeStyle | CanvasLinearGradientShading | CanvasRadialGradientShading;
|
|
2089
|
+
|
|
2090
|
+
/** A `GraphicsContext.Filter`: one key, its amount (degrees for `hueRotation`). */
|
|
2091
|
+
export type CanvasFilter =
|
|
2092
|
+
| { blur: number }
|
|
2093
|
+
| { shadow: { color: Color; radius: number; x: number; y: number } }
|
|
2094
|
+
| { grayscale: number }
|
|
2095
|
+
| { saturation: number }
|
|
2096
|
+
| { brightness: number }
|
|
2097
|
+
| { contrast: number }
|
|
2098
|
+
| { hueRotation: number }
|
|
2099
|
+
| { invert: number };
|
|
2100
|
+
|
|
2101
|
+
/** The context's state when a command was drawn. */
|
|
2102
|
+
export interface CanvasState {
|
|
2103
|
+
/** `[a, b, c, d, tx, ty]`, as `CGAffineTransform` (and SVG's `matrix()`); absent for the identity. */
|
|
2104
|
+
transform?: [number, number, number, number, number, number];
|
|
2105
|
+
opacity?: number;
|
|
2106
|
+
/** CSS `mix-blend-mode`. */
|
|
2107
|
+
blendMode?: string;
|
|
2108
|
+
/** SVG path data, in the canvas's coordinates; the command draws inside all of them. */
|
|
2109
|
+
clips?: string[];
|
|
2110
|
+
filters?: CanvasFilter[];
|
|
2111
|
+
}
|
|
2112
|
+
|
|
2113
|
+
export type CanvasCommand = CanvasState &
|
|
2114
|
+
(
|
|
2115
|
+
| { op: 'fill'; path: string; shading: CanvasShading; evenOdd?: boolean }
|
|
2116
|
+
| {
|
|
2117
|
+
op: 'stroke';
|
|
2118
|
+
path: string;
|
|
2119
|
+
shading: CanvasShading;
|
|
2120
|
+
lineWidth: number;
|
|
2121
|
+
lineCap?: 'butt' | 'round' | 'square';
|
|
2122
|
+
lineJoin?: 'miter' | 'round' | 'bevel';
|
|
2123
|
+
miterLimit?: number;
|
|
2124
|
+
dash?: number[];
|
|
2125
|
+
dashPhase?: number;
|
|
2126
|
+
}
|
|
2127
|
+
| {
|
|
2128
|
+
op: 'image';
|
|
2129
|
+
image: ImageProps;
|
|
2130
|
+
/** The point `anchor` (a unit point of the image) is placed at. */
|
|
2131
|
+
x: number;
|
|
2132
|
+
y: number;
|
|
2133
|
+
anchor: UnitPoint;
|
|
2134
|
+
/** `draw(_:in:)`: the rectangle's size; otherwise the image's own size. */
|
|
2135
|
+
width?: number;
|
|
2136
|
+
height?: number;
|
|
2137
|
+
/** A resolved image's `shading`: a template image is painted with it. */
|
|
2138
|
+
shading?: CanvasShading;
|
|
2139
|
+
}
|
|
2140
|
+
| { op: 'text'; text: string; x: number; y: number; anchor: UnitPoint; shading: CanvasShading; font?: Font }
|
|
2141
|
+
);
|
|
2142
|
+
|
|
2143
|
+
/**
|
|
2144
|
+
* A `Canvas` at one size (src/canvasDrawing.ts): the commands its renderer
|
|
2145
|
+
* drew for `width` × `height`, the size the canvas reported. It fills its
|
|
2146
|
+
* proposal and clips the drawing to its box.
|
|
2147
|
+
*/
|
|
2148
|
+
export interface CanvasProps {
|
|
2149
|
+
width: number;
|
|
2150
|
+
height: number;
|
|
2151
|
+
commands: CanvasCommand[];
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
export interface ElementPropsByKind {
|
|
2155
|
+
canvas: CanvasProps;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
/**
|
|
2159
|
+
* What a `video` element reports: `ready` (metadata), `time`, `status`
|
|
2160
|
+
* (playing, waiting, paused), `seeked` (a `seek` serial done), `play` /
|
|
2161
|
+
* `pause` (the viewer used the controls; `reason: "blocked"` when the browser
|
|
2162
|
+
* refused to start), `volume`, `rate`, `ended`, `error` (a `MediaError` code).
|
|
2163
|
+
*/
|
|
2164
|
+
export interface VideoEvent {
|
|
2165
|
+
type: 'video';
|
|
2166
|
+
id: ElementId;
|
|
2167
|
+
event: 'ready' | 'time' | 'status' | 'seeked' | 'play' | 'pause' | 'volume' | 'rate' | 'ended' | 'error';
|
|
2168
|
+
url?: string;
|
|
2169
|
+
time?: number;
|
|
2170
|
+
duration?: number | null;
|
|
2171
|
+
width?: number;
|
|
2172
|
+
height?: number;
|
|
2173
|
+
status?: 'playing' | 'waiting' | 'paused';
|
|
2174
|
+
serial?: number;
|
|
2175
|
+
reason?: 'blocked';
|
|
2176
|
+
muted?: boolean;
|
|
2177
|
+
volume?: number;
|
|
2178
|
+
rate?: number;
|
|
2179
|
+
code?: number;
|
|
2180
|
+
message?: string;
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/** `media` requests (src/media.ts): AVFoundation's files and probes, QuickLook's previews. */
|
|
2184
|
+
export interface MediaRequestParams {
|
|
2185
|
+
action: 'file' | 'probe' | 'release' | 'open';
|
|
2186
|
+
url?: string;
|
|
2187
|
+
name?: string;
|
|
2188
|
+
type?: string;
|
|
2189
|
+
data?: string;
|
|
2190
|
+
}
|