@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/README.md
CHANGED
|
@@ -14,6 +14,7 @@ app/index.html The app page (served at /app/): toolbar, iPhone frame, #scre
|
|
|
14
14
|
src/landing.ts Landing page script: app cards (qrcode SVGs), build info from the `define`s
|
|
15
15
|
src/landing.css Landing page styles
|
|
16
16
|
src/deviceMode.ts Frame vs device mode decision, env(safe-area-inset-*) reader, viewport watcher
|
|
17
|
+
src/chromeColor.ts Device mode: the browser's status bar strip and safe areas take the app's top-edge color
|
|
17
18
|
src/vite-env.d.ts Types of the build-time defines (__SB_APPS__, __SB_COMMIT__, …)
|
|
18
19
|
src/protocol.ts TypeScript mirror of the ops protocol (Op, element kinds, Style, Color, Animation, events)
|
|
19
20
|
src/renderer.ts Renderer: Map<id, HTMLElement> + LayoutNode tree, ops, navigation, sheets, layout on commit
|
|
@@ -26,6 +27,11 @@ src/wasm.ts loadApp(): WASI shim, _start, sb_ops_*, sb_event, sb_alloc +
|
|
|
26
27
|
src/mock.ts Counter, Todos, Gallery and Settings fixtures + in-page mock "Swift core" for ?mock=1
|
|
27
28
|
src/main.ts App page wiring: mode, theme toggle, Text size control, app label, boot, hot-reload state stash
|
|
28
29
|
src/devEvents.ts Payload types for the sb:build-* websocket events
|
|
30
|
+
src/hostRequests.ts Phase 13: performs the app's `request` ops (URLSession → fetch, AsyncImage → an Image() probe) and answers with `response` events
|
|
31
|
+
src/bitmaps.ts UIImage bitmaps: draws recipes on each image's <canvas>, through the store in a worker or on the page
|
|
32
|
+
src/bitmapStore.ts The bitmap store: keeps `imagedata` sources, decodes them, draws recipes (bitmapPlan.ts, imageOps.ts); no DOM
|
|
33
|
+
src/bitmapWorker.ts The store in a worker, answering drawings with ImageBitmaps
|
|
34
|
+
src/imageOps.ts Core Image's steps (sepia, crystallize, edges, blur, pixellate, unsharp mask, vignette, crop) on rasters; no DOM
|
|
29
35
|
src/styles.css iOS tokens, device frame, device mode, element styles
|
|
30
36
|
node/ Node side: apps.ts (the AppSpec registry type), config.ts (Vite config from specs),
|
|
31
37
|
examples.ts (Examples/* as specs, build-wasm.sh runner, descriptions),
|
|
@@ -86,6 +92,14 @@ from `displayName`, `description` and `icon`.
|
|
|
86
92
|
`SB_WASM_OPT=0`, `watch` = `Sources/` + the example's directory), which is
|
|
87
93
|
what `npm run dev` / `vite build` use.
|
|
88
94
|
|
|
95
|
+
A built site lists its apps in `apps.json`, and other built sites can join a
|
|
96
|
+
build (`SiteOptions.sites`; `SB_SITES` for `vite build`, directories separated
|
|
97
|
+
like `PATH`): `plugins/included-sites.ts` adds their apps to the landing page
|
|
98
|
+
and copies their files (modules, catalogs, bundles, manifests) next to this
|
|
99
|
+
site's, keeping this site's pages and any file it already has. CI's deploy
|
|
100
|
+
adds Apple's Backyard Birds this way, built from its repository with
|
|
101
|
+
`swiftbrowser build`.
|
|
102
|
+
|
|
89
103
|
Other packages (the CLI) import the same machinery through the package entry,
|
|
90
104
|
compiled by `npm run build:node` (`tsconfig.node-build.json` → `node/dist/`):
|
|
91
105
|
|
|
@@ -165,6 +179,9 @@ The app page's query parameters:
|
|
|
165
179
|
|
|
166
180
|
- `?mode=device` / `?mode=frame` forces device mode or the iPhone frame (see
|
|
167
181
|
"Device mode" below).
|
|
182
|
+
- `?arg=<value>` (repeatable) adds launch arguments after the app's name,
|
|
183
|
+
as `simctl launch` passes them: `?app=Fidelity&arg=-screen&arg=form` opens
|
|
184
|
+
that screen of Examples/Fidelity (`ProcessInfo.processInfo.arguments`).
|
|
168
185
|
|
|
169
186
|
The light/dark toggle in the toolbar flips `data-theme` on the device frame and
|
|
170
187
|
is remembered in `localStorage` (`sb-theme`). It only affects the phone; the
|
|
@@ -210,11 +227,28 @@ job scheduler are unaffected. `window.__sb.mode` reports the mode.
|
|
|
210
227
|
`apple-mobile-web-app-status-bar-style: black-translucent` (the OS status bar
|
|
211
228
|
stays transparent, the app's own background shows under it and the layout
|
|
212
229
|
engine gets the bar as a safe-area inset; `default` draws an opaque light bar
|
|
213
|
-
whatever the app's color scheme), light/dark `theme-color`s
|
|
214
|
-
manifest and the icons `public/icon.svg` + `public/apple-touch-icon.png`
|
|
230
|
+
whatever the app's color scheme), light/dark `theme-color`s (the defaults
|
|
231
|
+
until the app draws), a web app manifest and the icons `public/icon.svg` + `public/apple-touch-icon.png`
|
|
215
232
|
(180×180, drawn with ImageMagick to match the SVG). In Safari, Share → Add to
|
|
216
233
|
Home Screen then runs an app full screen.
|
|
217
234
|
|
|
235
|
+
In the browser itself (not standalone), Safari tints its status bar strip and
|
|
236
|
+
the safe areas from `theme-color`, the page background and (in recent Safari)
|
|
237
|
+
the fixed element at the top edge. `src/chromeColor.ts` keeps those in step
|
|
238
|
+
with the app: after each layout pass, a theme change and the end of each push,
|
|
239
|
+
pop or sheet animation (at most four times a second), it composites the
|
|
240
|
+
backgrounds the app paints at the middle of its top edge, walking only the
|
|
241
|
+
elements whose box holds that point (about 270 on Ice Cubes' timeline, under a
|
|
242
|
+
millisecond). The
|
|
243
|
+
result becomes the one `theme-color`, the `html`, `body` and `#device`
|
|
244
|
+
backgrounds, and `html`'s `color-scheme` is the app's scheme, so an app's own
|
|
245
|
+
bar color, its `.preferredColorScheme` or a sheet's dimming reach the strip
|
|
246
|
+
instead of the OS appearance's defaults. Safari seems to take the strip's
|
|
247
|
+
color from the first paint and not follow later changes, so in device mode the
|
|
248
|
+
first paint already has the OS appearance: an inline script after `#device`
|
|
249
|
+
sets its `data-theme` from `prefers-color-scheme`, and the page background is
|
|
250
|
+
the system background for it (white / black) before `main.ts` runs.
|
|
251
|
+
|
|
218
252
|
Safari takes a home-screen icon's launch URL and label from the manifest, not
|
|
219
253
|
from the page URL, so there is one manifest per example: `plugins/app-manifests.ts`
|
|
220
254
|
serves `/manifests/<Name>.webmanifest` (`start_url: /app/?app=<Name>`,
|
|
@@ -244,9 +278,9 @@ Those files are build products and are git-ignored (`public/*.wasm`); only
|
|
|
244
278
|
From the repo root:
|
|
245
279
|
|
|
246
280
|
```sh
|
|
247
|
-
scripts/build-wasm.sh #
|
|
248
|
-
#
|
|
249
|
-
scripts/build-wasm.sh Todos #
|
|
281
|
+
scripts/build-wasm.sh # every example through the CLI (swiftbrowser build: release, wasm-opt -Oz
|
|
282
|
+
# when installed), each <App>.wasm copied into Web/public/
|
|
283
|
+
scripts/build-wasm.sh Todos # the Todos example only
|
|
250
284
|
```
|
|
251
285
|
|
|
252
286
|
## Hot reload
|
|
@@ -373,20 +407,34 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
373
407
|
content inset, so the last row can scroll clear of the home indicator (the
|
|
374
408
|
engine's frames are unchanged).
|
|
375
409
|
- Compound kinds use the engine's `chrome` rects: `navscreen` {bar (status bar
|
|
376
|
-
+ 44, padded so the 44px row is at the bottom), largeTitle (
|
|
377
|
-
from `chromeText`), back, content}, `sheet` {grabber}, `toggle`
|
|
378
|
-
`section` {rows (the inset card), header, footer}, `navlink`
|
|
379
|
-
`tabview` {bar, item0 … itemN-1}, `picker` {segment0 … /
|
|
410
|
+
+ 44, padded so the 44px row is at the bottom), largeTitle (62px row, text
|
|
411
|
+
from `chromeText`), back, content}, `sheet` {grabber, floating}, `toggle`
|
|
412
|
+
{switch}, `section` {rows (the inset card), header, footer}, `navlink`
|
|
413
|
+
rows {chevron}, `tabview` {bar, item0 … itemN-1}, `picker` {segment0 … /
|
|
414
|
+
label, value}.
|
|
380
415
|
A `list` or `scrollview` under a tab bar gets a `bottomInset` rect (the
|
|
381
416
|
part of its frame the bar covers): its height replaces the 34px safe-area
|
|
382
417
|
content inset.
|
|
383
|
-
|
|
384
|
-
|
|
418
|
+
A `list` or `scrollview` with a `largeTitle` rect carries its screen's
|
|
419
|
+
large title (src/largeTitle.ts): the title follows its scroll offset and
|
|
420
|
+
fades out over 16px at the bar's edge, as the scrolled content does
|
|
421
|
+
(`data-sb-scrolled`, a mask standing in for iOS 26's scroll edge effect);
|
|
422
|
+
past 44 the screen gets `data-sb-collapsed`, which fades the inline title
|
|
423
|
+
in (its text is always set; CSS hides it on a large screen until then). On
|
|
424
|
+
an overscroll the title moves down with the content (iOS 26 no longer
|
|
425
|
+
grows it).
|
|
426
|
+
Section headers keep their case (iOS 26) and come from the engine in the
|
|
427
|
+
headline font (17 semibold), secondary color; footers are drawn from the
|
|
428
|
+
section's own `footer` prop in the engine's footnote font and rect.
|
|
385
429
|
- A list row's DOM box is the engine's full-width `row` rect (so the cell
|
|
386
|
-
background, the
|
|
430
|
+
background, the separator and the hit area match iOS); leaf rows
|
|
387
431
|
(`text`, `image`, `textfield`) pad their content frame back into place.
|
|
388
|
-
|
|
389
|
-
|
|
432
|
+
The separator (`--sb-color-row-separator`, light `rgba(0,0,0,0.09)`)
|
|
433
|
+
starts at the row's first text (the engine's row chrome `separator`, set
|
|
434
|
+
as `--sb-separator-leading`, else 16px) and ends 16px before the trailing
|
|
435
|
+
edge. Rows whose own chrome replaced the `row` key (`toggle`, bordered
|
|
436
|
+
`button`) rebuild it from the card and the engine's rule
|
|
437
|
+
`max(52, content + 2 × 13)`.
|
|
390
438
|
- Layout-only style fields (`padding`, `frame`, `layoutPriority`, `fixedSize`,
|
|
391
439
|
`lineLimit`, `multilineTextAlignment`) leave data attributes
|
|
392
440
|
(`data-sb-padding`, `data-sb-frame`, `data-sb-line-limit`, …) and are read by
|
|
@@ -394,8 +442,10 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
394
442
|
(+ `overflow: hidden`), `opacity` and `disabled` are inline CSS as before.
|
|
395
443
|
- Expected geometry with the real modules: Counter's three buttons are 88×44
|
|
396
444
|
at x = 48 / 152 / 256, y = 521 (16px gaps, centered); Todos' bar spans
|
|
397
|
-
y 0–103, the large title 103–
|
|
398
|
-
|
|
445
|
+
y 0–103, the large title 103–165 (scrolling with the list, which starts
|
|
446
|
+
under the bar at 103 with its content 62 down, so its first card is at
|
|
447
|
+
183), and its rows are 52 high and inset 16; a pushed inline screen's bar
|
|
448
|
+
row is y 59–103.
|
|
399
449
|
|
|
400
450
|
### Fonts and Dynamic Type
|
|
401
451
|
|
|
@@ -416,14 +466,21 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
416
466
|
- Semantic colors resolve to CSS variables (`--sb-color-primary`, `-secondary`,
|
|
417
467
|
`-accent`, `-system-background`, `-secondary-system-background`); `clear` is
|
|
418
468
|
`transparent`. Values for both schemes live in `styles.css` under
|
|
419
|
-
`.device[data-theme]
|
|
420
|
-
|
|
469
|
+
`.device[data-theme]`: iOS 26's palette (accent and blue `#0088ff`, dark
|
|
470
|
+
`#0091ff`; red `#ff383c`, orange `#ff8d28`, …). An `a` on a semantic
|
|
471
|
+
color is an opacity multiplier, rendered as
|
|
472
|
+
`color-mix(in srgb, var(--token) <a*100>%, transparent)`.
|
|
421
473
|
- `Style.disabled` multiplies the box's opacity by 0.4 and sets
|
|
422
474
|
`pointer-events: none` (plus `data-sb-disabled` / `aria-disabled`).
|
|
423
475
|
- `button.style` and `button.role` become `data-sb-style` / `data-sb-role`;
|
|
424
|
-
`bordered` is a
|
|
425
|
-
(
|
|
426
|
-
|
|
476
|
+
`bordered` is a capsule in the secondary fill, or in the view's `tint` at
|
|
477
|
+
18% (25% in dark mode, `--sb-tint-fill`, which the renderer sets beside
|
|
478
|
+
`--sb-tint`), `borderedProminent` a filled accent capsule
|
|
479
|
+
(label + 12/6 padding, 34px minimum, from the engine; with a `controlSize`
|
|
480
|
+
prop: `small` 28, `mini` 24, `large` 50, `extraLarge` 58), `destructive`
|
|
481
|
+
red text; a disabled bordered button keeps the gray fill and dims its label
|
|
482
|
+
rather than fading. Phase 1
|
|
483
|
+
modules send `{}`, which reads as `automatic`.
|
|
427
484
|
- `image`: `systemName` is looked up in `src/symbols.ts` (SF Symbol name →
|
|
428
485
|
lucide glyph, including `.fill` / `.circle` variants) and rendered as an
|
|
429
486
|
inline `<svg>` in `currentColor`. Unknown names draw a dashed square with
|
|
@@ -433,14 +490,15 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
433
490
|
(`50%`) and `capsule` (`9999px`). `fill` is the background; `fill: null`
|
|
434
491
|
without a stroke paints `currentColor` (a bare `Circle()`), with a stroke it
|
|
435
492
|
paints nothing (`Shape.stroke(_:)`); `stroke` is a solid border of
|
|
436
|
-
`lineWidth` (inside the frame, `box-sizing: border-box`)
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
trailing
|
|
493
|
+
`lineWidth` (inside the frame, `box-sizing: border-box`), dashed when it
|
|
494
|
+
carries a `dash` pattern (the dash lengths are the browser's). Both take
|
|
495
|
+
exactly the size the engine proposes (10pt on an unconstrained axis).
|
|
496
|
+
- `list` / `section` render iOS 26 inset-grouped: grouped background on the
|
|
497
|
+
list, white (dark: `#1C1C1E`) 26px-rounded cards inset 16px, rows at least
|
|
498
|
+
52px with 16px content insets and separators from the first text to 16px
|
|
499
|
+
before the trailing edge. Every direct child of a section (and every
|
|
500
|
+
non-`section` child of a list) is a row; a `navlink` row shows a trailing
|
|
501
|
+
chevron, a `toggle` row puts the switch at the trailing inset.
|
|
444
502
|
- Compound kinds (`navstack`, `navscreen`, `section`, `toggle`, `navlink`,
|
|
445
503
|
`list`, `scrollview`, `sheet`) own some chrome. Their protocol children go
|
|
446
504
|
into a slot element (`.sb-slot`), so `insert` indices are exact;
|
|
@@ -449,26 +507,30 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
449
507
|
(`data-sb-nav-position`, `inert`); a new screen slides in from the right
|
|
450
508
|
over 0.35s and the one below parks at `translateX(-30%)`. A removed top
|
|
451
509
|
screen animates out through a visual clone stripped of element ids. Screens
|
|
452
|
-
at `depth > 0` show a back button
|
|
453
|
-
|
|
454
|
-
`
|
|
510
|
+
at `depth > 0` show a back button that sends a `tap` with the
|
|
511
|
+
`navscreen`'s own id: iOS 26's 44×44 glass circle at x 16 with
|
|
512
|
+
`chevron.left` alone, in the label color (the engine's back label is
|
|
513
|
+
empty; the previous screen's title shows only until the engine has run).
|
|
455
514
|
- `textfield` is an `<input>` (34px `roundedBorder`, 22px `plain`); `input`
|
|
456
515
|
events send `{"type":"text"}`. An `update` whose `text` equals the current
|
|
457
|
-
value leaves `.value` (and the caret) alone. `toggle` renders a
|
|
458
|
-
(`role="switch"`); clicking it flips the
|
|
459
|
-
`{"type":"toggle"}`; Swift's `update`
|
|
516
|
+
value leaves `.value` (and the caret) alone. `toggle` renders a 63×28
|
|
517
|
+
switch with a 37×24 pill knob (`role="switch"`); clicking it flips the
|
|
518
|
+
switch optimistically and sends `{"type":"toggle"}`; Swift's `update`
|
|
519
|
+
confirms the state.
|
|
460
520
|
|
|
461
521
|
### Tab views, pickers, list styles and GeometryReader (Phase 4)
|
|
462
522
|
|
|
463
523
|
- `tabview` fills the screen like a `navstack` (safe areas ignored). Its
|
|
464
524
|
`tab` children go into `.sb-tabview-content` and all share the tab view's
|
|
465
|
-
frame; the bar (`.sb-tabbar`, the engine's `bar` rect
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
`
|
|
471
|
-
|
|
525
|
+
frame; the bar (`.sb-tabbar`, the engine's `bar` rect) is iOS 26's
|
|
526
|
+
floating glass capsule drawn on top: 62 high, x 21…372 on a 393 screen,
|
|
527
|
+
22 above the screen bottom (8 above the bottom of a tab view that ends
|
|
528
|
+
higher), `--sb-glass-background` with `--sb-glass-shadow` and a backdrop
|
|
529
|
+
blur, 31px radius. One `.sb-tabbar-item` per tab sits at the engine's
|
|
530
|
+
`item<i>` rect (equal widths inside a 4 inset, 54 high): the tab's
|
|
531
|
+
`systemImage` through the symbol table at 24px over a 10px semibold title,
|
|
532
|
+
in the label color, the selected one (`aria-selected`) in the accent color
|
|
533
|
+
on a gray glass pill (`--sb-glass-selected`). Tapping an item sends
|
|
472
534
|
`{"type":"select","id":<tabview id>,"value":<index>}` and nothing else:
|
|
473
535
|
the `selected` prop is authoritative, the `update` switches tabs. Only the
|
|
474
536
|
selected tab's content is laid out; the other tabs keep their DOM (and
|
|
@@ -476,19 +538,21 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
476
538
|
`inert`, `aria-hidden`), driven by `LayoutResult.hidden`.
|
|
477
539
|
- Tab content is placed like a navscreen's: a `navstack` fills the whole tab
|
|
478
540
|
(its screens reserve the status bar and their lists run under the
|
|
479
|
-
|
|
541
|
+
floating bar); a `list`/`scrollview` starts below the status bar and
|
|
480
542
|
also runs to the bottom; anything else is centered between the status bar
|
|
481
|
-
and the bar. Scrolling views under the bar get the bar
|
|
482
|
-
|
|
483
|
-
`bottomInset`), so the last row scrolls
|
|
543
|
+
and the bar. Scrolling views under the bar get what the bar covers (the
|
|
544
|
+
capsule and the gap under it, 84) as scrollable content inset instead of
|
|
545
|
+
the 34px safe area (engine chrome `bottomInset`), so the last row scrolls
|
|
546
|
+
clear of the bar like iOS.
|
|
484
547
|
- `picker` `segmented`: an iOS segmented control, 32 high, width = proposal
|
|
485
|
-
(or every label + 20): `.sb-segment-track` (`rgba(118,118,128,0.12)`,
|
|
486
|
-
|
|
548
|
+
(or every label + 20): `.sb-segment-track` (`rgba(118,118,128,0.12)`, a
|
|
549
|
+
capsule; dark `rgba(118,118,128,0.24)`) at the control's frame and one
|
|
487
550
|
`.sb-segment` radio button per option at the engine's `segment<i>` rect
|
|
488
|
-
(13px semibold); the checked one carries a white
|
|
551
|
+
(13px semibold); the checked one carries a white capsule (dark `#5a5a5f`)
|
|
489
552
|
inset 2px with a soft shadow. Tapping a segment moves the pill
|
|
490
553
|
optimistically and sends `select` with the index; Swift's `update`
|
|
491
|
-
confirms. In a list row the row
|
|
554
|
+
confirms. In a list row it gets the 13px row padding (a 58 row) with the
|
|
555
|
+
control centered.
|
|
492
556
|
- `picker` `menu`: 44 high in a list row, 34 elsewhere; `role="button"` with
|
|
493
557
|
`.sb-picker-label` at the engine's `label` rect (leading) and
|
|
494
558
|
`.sb-picker-value` (the selected option plus `chevron.up.chevron.down`,
|
|
@@ -501,12 +565,12 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
501
565
|
closes it (a tap inside the screen is swallowed so the control under it
|
|
502
566
|
does not fire); an `update` or removal of the picker closes it too.
|
|
503
567
|
`renderer.openMenuElement` exposes the open menu.
|
|
504
|
-
- `list.style`: `insetGrouped` (default
|
|
505
|
-
existing card layout; `grouped`
|
|
506
|
-
corner radius, a hairline above and below each card and
|
|
507
|
-
content inset and 35px between sections; `plain` drops the
|
|
508
|
-
gap and the gaps between sections and uses the system
|
|
509
|
-
list carries `data-sb-list-style`.
|
|
568
|
+
- `list.style`: `insetGrouped` (default, and what `Form` sends; Phase 2
|
|
569
|
+
modules send `{}`) is the existing card layout; `grouped` makes sections
|
|
570
|
+
full width with no corner radius, a hairline above and below each card and
|
|
571
|
+
the usual 16px content inset and 35px between sections; `plain` drops the
|
|
572
|
+
cards, the top gap and the gaps between sections and uses the system
|
|
573
|
+
background. The list carries `data-sb-list-style`.
|
|
510
574
|
- `geometry` (`GeometryReader`) is a plain box the engine sizes to its
|
|
511
575
|
proposal (10 on an unconstrained axis, so one in a list row needs a frame
|
|
512
576
|
height) and whose child is proposed that size and placed top leading.
|
|
@@ -522,18 +586,26 @@ color scheme or the fonts change (`renderer.setEnvironment`,
|
|
|
522
586
|
|
|
523
587
|
A root-level `sheet` (child of id 0) is a full-screen layer (`role="dialog"`):
|
|
524
588
|
a scrim (`rgba(0,0,0,0.4)`) over the presenting tree and a card at the
|
|
525
|
-
engine's frame (large: screen height − status bar − 10
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
(
|
|
589
|
+
engine's frame (large: screen height − status bar − 10, 10px top corners;
|
|
590
|
+
medium: 433 high) and the sheet background (`#FFFFFF`, dark: `#1C1C1E`, the
|
|
591
|
+
elevated background). A 36×5 grabber sits 8px from the top only when the
|
|
592
|
+
sheet has more than one detent (iOS 26). A medium, height or fraction sheet
|
|
593
|
+
floats (engine chrome `floating`, `data-sb-floating`): a card inset 8px from
|
|
594
|
+
the screen sides and 15px above its bottom, 38px corners all round, the
|
|
595
|
+
sheet background at 55% over a backdrop blur, a lighter scrim
|
|
596
|
+
(`rgba(0,0,0,0.22)`); navigation screens in it are transparent, so the
|
|
597
|
+
glass shows through them. A navigation stack in a sheet fills the card from
|
|
598
|
+
its top, its bar taking 16px (20 with a grabber) above the 44px row. The card slides up over 0.4s on insert
|
|
599
|
+
(`sb-sheet-in`); on remove a clone (`.sb-sheet--out`, no element ids) slides
|
|
600
|
+
down and fades its scrim, then goes.
|
|
530
601
|
While the top sheet is `large`, `#screen` carries `data-sb-sheet="large"` and
|
|
531
602
|
the other root children scale to 0.92 behind the scrim; a `medium` sheet only
|
|
532
603
|
dims. Sheets stack: only the last is interactive (`data-sb-sheet-position`,
|
|
533
604
|
`inert`), sheets below park slightly scaled. A tap on the scrim, or a
|
|
534
605
|
downward drag of the card past 100px (pointer events; shorter drags spring
|
|
535
606
|
back), sends `{"type":"tap","id":<sheet id>}`: the dismiss request Swift
|
|
536
|
-
answers by removing the element. `detents`
|
|
607
|
+
answers by removing the element. `detents` matters by its first entry (and
|
|
608
|
+
its count, for the grabber).
|
|
537
609
|
|
|
538
610
|
### Animation (Phase 3)
|
|
539
611
|
|
|
@@ -579,7 +651,13 @@ transaction override); everything else in an animated commit uses the commit's.
|
|
|
579
651
|
## Testing
|
|
580
652
|
|
|
581
653
|
`npm run test:unit` runs the layout engine's vitest suite (Node, no browser).
|
|
582
|
-
`npm run test:e2e` runs Playwright with Chromium
|
|
654
|
+
`npm run test:e2e` runs Playwright with Chromium, in three projects: `mock`
|
|
655
|
+
(the specs listed in `MOCK_SPECS` in `playwright.config.ts`, which load no
|
|
656
|
+
compiled module), `wasm-heavy` (the slowest specs against real modules,
|
|
657
|
+
`HEAVY_SPECS`) and `wasm` (every other spec; these need `public/<App>.wasm`).
|
|
658
|
+
`--project=mock` runs without any module built; CI runs it while the examples
|
|
659
|
+
build, and runs `wasm-heavy` and `wasm` in two jobs of about the same length.
|
|
660
|
+
The config boots
|
|
583
661
|
`npm run dev -- --port 5173 --strictPort` itself. `e2e/renderer.spec.ts`
|
|
584
662
|
covers the Counter mock and op semantics, `e2e/phase2.spec.ts` the Phase 2
|
|
585
663
|
kinds against the Todos mock, `e2e/phase3.spec.ts` Dynamic Type, color/shape,
|
package/app/index.html
CHANGED
|
@@ -90,23 +90,23 @@
|
|
|
90
90
|
<span class="status-time">9:41</span>
|
|
91
91
|
<span class="status-glyphs">
|
|
92
92
|
<!-- Cellular -->
|
|
93
|
-
<svg class="glyph glyph-cellular" viewBox="0 0
|
|
94
|
-
<rect x="0" y="
|
|
95
|
-
<rect x="5" y="5.
|
|
96
|
-
<rect x="10" y="
|
|
97
|
-
<rect x="
|
|
93
|
+
<svg class="glyph glyph-cellular" viewBox="0 0 19.33 12.33" width="19.33" height="12.33">
|
|
94
|
+
<rect x="0" y="7.67" width="3.33" height="4.67" rx="1" />
|
|
95
|
+
<rect x="5.33" y="5.33" width="3.33" height="7" rx="1" />
|
|
96
|
+
<rect x="10.67" y="2.67" width="3.33" height="9.67" rx="1" />
|
|
97
|
+
<rect x="16" y="0" width="3.33" height="12.33" rx="1" />
|
|
98
98
|
</svg>
|
|
99
99
|
<!-- Wi-Fi -->
|
|
100
|
-
<svg class="glyph glyph-wifi" viewBox="0 0 16 12" width="16" height="12">
|
|
101
|
-
<path d="
|
|
102
|
-
<path d="
|
|
103
|
-
<path d="M8
|
|
100
|
+
<svg class="glyph glyph-wifi" viewBox="0 0 16.3 12.33" width="16.3" height="12.33">
|
|
101
|
+
<path class="wifi-band" d="M1.08 3.87A11.0 11.0 0 0 1 15.22 3.87" stroke-width="2.6" />
|
|
102
|
+
<path class="wifi-band" d="M3.91 7.24A6.6 6.6 0 0 1 12.39 7.24" stroke-width="2.9" />
|
|
103
|
+
<path d="M8.15 12.3L5.64 9.31A3.9 3.9 0 0 1 10.66 9.31Z" />
|
|
104
104
|
</svg>
|
|
105
105
|
<!-- Battery -->
|
|
106
106
|
<svg class="glyph glyph-battery" viewBox="0 0 28 13" width="28" height="13">
|
|
107
107
|
<rect class="battery-shell" x="0.5" y="0.5" width="24" height="12" rx="3.5" />
|
|
108
108
|
<rect class="battery-fill" x="2" y="2" width="21" height="9" rx="2" />
|
|
109
|
-
<path class="battery-cap" d="M26 4.3v4.4c.
|
|
109
|
+
<path class="battery-cap" d="M26 4.3v4.4c.8-.3 1.33-1.1 1.33-2.2S26.8 4.6 26 4.3Z" />
|
|
110
110
|
</svg>
|
|
111
111
|
</span>
|
|
112
112
|
</div>
|
|
@@ -117,6 +117,17 @@
|
|
|
117
117
|
<div class="home-indicator" aria-hidden="true"></div>
|
|
118
118
|
</div>
|
|
119
119
|
</div>
|
|
120
|
+
<script>
|
|
121
|
+
// Device mode follows the OS appearance (main.ts); set it before the
|
|
122
|
+
// first paint. Safari tints its status bar strip and safe areas from
|
|
123
|
+
// the first paint and does not follow later changes, so a light device
|
|
124
|
+
// here kept the strip white under a dark app.
|
|
125
|
+
(function () {
|
|
126
|
+
if (document.documentElement.getAttribute('data-sb-mode') !== 'device') return;
|
|
127
|
+
var dark = typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches;
|
|
128
|
+
document.getElementById('device').setAttribute('data-theme', dark ? 'dark' : 'light');
|
|
129
|
+
})();
|
|
130
|
+
</script>
|
|
120
131
|
<p id="status" class="status" role="alert" hidden></p>
|
|
121
132
|
</main>
|
|
122
133
|
|
package/node/dist/node/apps.d.ts
CHANGED
|
@@ -45,6 +45,22 @@ export interface AppSpec {
|
|
|
45
45
|
* else in the first; default `["en"]`.
|
|
46
46
|
*/
|
|
47
47
|
locales?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* The app's URL schemes (`CFBundleURLSchemes` of its Info.plist's
|
|
50
|
+
* `CFBundleURLTypes`). OAuth redirects in these schemes come back through the
|
|
51
|
+
* page's callback page (docs/ops-protocol.md, "Signing in").
|
|
52
|
+
*/
|
|
53
|
+
urlSchemes?: string[];
|
|
54
|
+
/**
|
|
55
|
+
* A CORS proxy (packages/cors-proxy): the page retries a cross-origin GET or
|
|
56
|
+
* HEAD the browser refused through `<corsProxy>?url=<target>` (src/corsProxy.ts).
|
|
57
|
+
*/
|
|
58
|
+
corsProxy?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Hosts (and their subdomains) the app's requests may not reach: they fail
|
|
61
|
+
* as a network error does (src/blockedHosts.ts).
|
|
62
|
+
*/
|
|
63
|
+
blockedHosts?: string[];
|
|
48
64
|
}
|
|
49
65
|
/** The name rule `?app=` in src/main.ts and every plugin URL applies. */
|
|
50
66
|
export declare const APP_NAME: RegExp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,gHAAgH;IAChH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,gHAAgH;IAChH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"apps.d.ts","sourceRoot":"","sources":["../../apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,gHAAgH;IAChH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,gHAAgH;IAChH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,yEAAyE;AACzE,eAAO,MAAM,QAAQ,QAAqB,CAAC;AAE3C,sCAAsC;AACtC,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,MAAM,CAEjF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CA+ChE"}
|
package/node/dist/node/apps.js
CHANGED
|
@@ -52,6 +52,15 @@ export function validateAppSpecs(specs) {
|
|
|
52
52
|
if (spec.locales !== undefined && (!Array.isArray(spec.locales) || !spec.locales.every((l) => typeof l === 'string' && l.length > 0))) {
|
|
53
53
|
throw new Error(`${label}: "locales" must be an array of language tags`);
|
|
54
54
|
}
|
|
55
|
+
if (spec.urlSchemes !== undefined && (!Array.isArray(spec.urlSchemes) || !spec.urlSchemes.every((u) => typeof u === 'string' && /^[A-Za-z][A-Za-z0-9+.-]*$/.test(u)))) {
|
|
56
|
+
throw new Error(`${label}: "urlSchemes" must be an array of URL schemes`);
|
|
57
|
+
}
|
|
58
|
+
if (spec.corsProxy !== undefined && (typeof spec.corsProxy !== 'string' || !/^https?:\/\/[^\s]+$/.test(spec.corsProxy))) {
|
|
59
|
+
throw new Error(`${label}: "corsProxy" must be an http(s) URL`);
|
|
60
|
+
}
|
|
61
|
+
if (spec.blockedHosts !== undefined && (!Array.isArray(spec.blockedHosts) || !spec.blockedHosts.every((h) => typeof h === 'string' && /^[A-Za-z0-9.-]+$/.test(h)))) {
|
|
62
|
+
throw new Error(`${label}: "blockedHosts" must be an array of host names`);
|
|
63
|
+
}
|
|
55
64
|
if (spec.build !== undefined && typeof spec.build !== 'function') {
|
|
56
65
|
throw new Error(`${label}: "build" must be a function`);
|
|
57
66
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.js","sourceRoot":"","sources":["../../apps.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"apps.js","sourceRoot":"","sources":["../../apps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the Vite config for a set of apps: the registry plugins
|
|
3
3
|
* (plugins/swift-wasm.ts, asset-catalogs.ts, app-manifests.ts), the two
|
|
4
|
-
* pages (landing + app)
|
|
4
|
+
* pages (landing + app), the `define`s the landing page reads, and the apps
|
|
5
|
+
* of other built sites joining this one (plugins/included-sites.ts).
|
|
5
6
|
*
|
|
6
7
|
* `vite.config.ts` calls this with the example apps; the programmatic API
|
|
7
8
|
* (./index.ts) with whatever the caller registered.
|
|
@@ -14,6 +15,12 @@ export interface SiteOptions {
|
|
|
14
15
|
commit?: string;
|
|
15
16
|
/** Build info shown on the landing page: the branch name ("" when unknown). */
|
|
16
17
|
branch?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Directories of sites built before (`buildSite`, `swiftbrowser build`)
|
|
20
|
+
* whose apps join this one's: their cards on the landing page, their files
|
|
21
|
+
* copied into the build (plugins/included-sites.ts). Build only.
|
|
22
|
+
*/
|
|
23
|
+
sites?: string[];
|
|
17
24
|
}
|
|
18
25
|
/** One entry of `__SB_APPS__`, what the landing page renders a card from (see src/vite-env.d.ts). */
|
|
19
26
|
export interface LandingApp {
|
|
@@ -24,6 +31,12 @@ export interface LandingApp {
|
|
|
24
31
|
icon?: string;
|
|
25
32
|
/** The languages the module carries locale data for (`AppSpec.locales`); the page picks the viewer's among them. */
|
|
26
33
|
locales?: string[];
|
|
34
|
+
/** The app's URL schemes (`AppSpec.urlSchemes`); the page passes them to the app as `SB_URL_SCHEMES`. */
|
|
35
|
+
urlSchemes?: string[];
|
|
36
|
+
/** The app's CORS proxy (`AppSpec.corsProxy`); the page retries refused cross-origin reads through it. */
|
|
37
|
+
corsProxy?: string;
|
|
38
|
+
/** Hosts the app's requests may not reach (`AppSpec.blockedHosts`). */
|
|
39
|
+
blockedHosts?: string[];
|
|
27
40
|
}
|
|
28
41
|
/**
|
|
29
42
|
* The Vite root: the `Web/` directory of this package (holds `index.html`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAMvC,OAAO,EAAmC,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,qGAAqG;AACrG,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oHAAoH;IACpH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yGAAyG;IACzG,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0GAA0G;IAC1G,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAOD;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAoBhC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,EAAE,CAWnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAuCjE"}
|
package/node/dist/node/config.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Builds the Vite config for a set of apps: the registry plugins
|
|
3
3
|
* (plugins/swift-wasm.ts, asset-catalogs.ts, app-manifests.ts), the two
|
|
4
|
-
* pages (landing + app)
|
|
4
|
+
* pages (landing + app), the `define`s the landing page reads, and the apps
|
|
5
|
+
* of other built sites joining this one (plugins/included-sites.ts).
|
|
5
6
|
*
|
|
6
7
|
* `vite.config.ts` calls this with the example apps; the programmatic API
|
|
7
8
|
* (./index.ts) with whatever the caller registered.
|
|
@@ -12,6 +13,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
12
13
|
import { appManifests } from '../plugins/app-manifests.js';
|
|
13
14
|
import { assetCatalogs } from '../plugins/asset-catalogs.js';
|
|
14
15
|
import { bundleResources } from '../plugins/bundle-resources.js';
|
|
16
|
+
import { includedSites, readSiteApps } from '../plugins/included-sites.js';
|
|
15
17
|
import { swiftWasm } from '../plugins/swift-wasm.js';
|
|
16
18
|
import { displayNameOf, validateAppSpecs } from './apps.js';
|
|
17
19
|
/** The package name that marks the Vite root. */
|
|
@@ -57,6 +59,12 @@ export function landingApps(specs) {
|
|
|
57
59
|
entry.icon = `/manifests/${spec.name}-icon.png`;
|
|
58
60
|
if (spec.locales !== undefined)
|
|
59
61
|
entry.locales = [...spec.locales];
|
|
62
|
+
if (spec.urlSchemes?.length)
|
|
63
|
+
entry.urlSchemes = [...spec.urlSchemes];
|
|
64
|
+
if (spec.corsProxy)
|
|
65
|
+
entry.corsProxy = spec.corsProxy;
|
|
66
|
+
if (spec.blockedHosts?.length)
|
|
67
|
+
entry.blockedHosts = [...spec.blockedHosts];
|
|
60
68
|
return entry;
|
|
61
69
|
});
|
|
62
70
|
}
|
|
@@ -68,14 +76,23 @@ export function createViteConfig(options) {
|
|
|
68
76
|
validateAppSpecs(options.apps);
|
|
69
77
|
const root = webRoot();
|
|
70
78
|
const apps = options.apps;
|
|
79
|
+
const sites = (options.sites ?? []).map((dir) => path.resolve(dir));
|
|
80
|
+
const included = sites.flatMap(readSiteApps);
|
|
81
|
+
const landing = [...landingApps(apps), ...included];
|
|
82
|
+
const names = new Set();
|
|
83
|
+
for (const app of landing) {
|
|
84
|
+
if (names.has(app.name))
|
|
85
|
+
throw new Error(`Duplicate app name ${JSON.stringify(app.name)} in the included sites`);
|
|
86
|
+
names.add(app.name);
|
|
87
|
+
}
|
|
71
88
|
return {
|
|
72
89
|
root,
|
|
73
|
-
plugins: [swiftWasm(apps), appManifests(apps), assetCatalogs(apps), bundleResources(apps)],
|
|
90
|
+
plugins: [swiftWasm(apps), appManifests(apps, included), assetCatalogs(apps), bundleResources(apps), includedSites(landing, sites)],
|
|
74
91
|
// Two pages: the landing page at `/` and the app at `/app/`. `mpa` keeps the
|
|
75
92
|
// dev server from answering unknown URLs with the landing page.
|
|
76
93
|
appType: 'mpa',
|
|
77
94
|
define: {
|
|
78
|
-
__SB_APPS__: JSON.stringify(
|
|
95
|
+
__SB_APPS__: JSON.stringify(landing),
|
|
79
96
|
__SB_COMMIT__: JSON.stringify(options.commit ?? 'dev'),
|
|
80
97
|
__SB_BRANCH__: JSON.stringify(options.branch ?? ''),
|
|
81
98
|
__SB_BUILT_AT__: JSON.stringify(new Date().toISOString()),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,8 @@ export interface BuildScriptOptions {
|
|
|
20
20
|
/**
|
|
21
21
|
* `bash scripts/build-wasm.sh <app>` in `repoRoot` with `SB_WASM_OPT=0`: hot
|
|
22
22
|
* rebuilds skip wasm-opt, which costs 15-30 s per module and strips the names
|
|
23
|
-
* that make stack traces readable while developing.
|
|
23
|
+
* that make stack traces readable while developing. The script builds through
|
|
24
|
+
* the CLI, so the workspace packages must be built (`npm run build:packages`). Rejects with an Error
|
|
24
25
|
* whose message is the script's combined output when it fails.
|
|
25
26
|
*/
|
|
26
27
|
export declare function runBuildScript(repoRoot: string, app: string, options?: BuildScriptOptions): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../../examples.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../../examples.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcjE,CAAC;AAIF;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAIjD;AAED,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB7G;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAOvD;AAED,gFAAgF;AAChF,wBAAgB,eAAe,CAAC,QAAQ,SAAgC,GAAG,OAAO,EAAE,CAoBnF;AAYD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAK9E"}
|