@swiftbrowser/web 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +600 -0
  2. package/app/index.html +125 -0
  3. package/index.html +56 -0
  4. package/node/dist/node/apps.d.ts +58 -0
  5. package/node/dist/node/apps.d.ts.map +1 -0
  6. package/node/dist/node/apps.js +60 -0
  7. package/node/dist/node/apps.js.map +1 -0
  8. package/node/dist/node/config.d.ts +42 -0
  9. package/node/dist/node/config.d.ts.map +1 -0
  10. package/node/dist/node/config.js +99 -0
  11. package/node/dist/node/config.js.map +1 -0
  12. package/node/dist/node/examples.d.ts +39 -0
  13. package/node/dist/node/examples.d.ts.map +1 -0
  14. package/node/dist/node/examples.js +116 -0
  15. package/node/dist/node/examples.js.map +1 -0
  16. package/node/dist/node/index.d.ts +47 -0
  17. package/node/dist/node/index.d.ts.map +1 -0
  18. package/node/dist/node/index.js +52 -0
  19. package/node/dist/node/index.js.map +1 -0
  20. package/node/dist/plugins/app-manifests.d.ts +61 -0
  21. package/node/dist/plugins/app-manifests.d.ts.map +1 -0
  22. package/node/dist/plugins/app-manifests.js +168 -0
  23. package/node/dist/plugins/app-manifests.js.map +1 -0
  24. package/node/dist/plugins/asset-catalogs.d.ts +119 -0
  25. package/node/dist/plugins/asset-catalogs.d.ts.map +1 -0
  26. package/node/dist/plugins/asset-catalogs.js +488 -0
  27. package/node/dist/plugins/asset-catalogs.js.map +1 -0
  28. package/node/dist/plugins/bundle-resources.d.ts +49 -0
  29. package/node/dist/plugins/bundle-resources.d.ts.map +1 -0
  30. package/node/dist/plugins/bundle-resources.js +172 -0
  31. package/node/dist/plugins/bundle-resources.js.map +1 -0
  32. package/node/dist/plugins/swift-wasm.d.ts +33 -0
  33. package/node/dist/plugins/swift-wasm.d.ts.map +1 -0
  34. package/node/dist/plugins/swift-wasm.js +241 -0
  35. package/node/dist/plugins/swift-wasm.js.map +1 -0
  36. package/node/dist/src/assets.d.ts +60 -0
  37. package/node/dist/src/assets.d.ts.map +1 -0
  38. package/node/dist/src/assets.js +54 -0
  39. package/node/dist/src/assets.js.map +1 -0
  40. package/node/dist/src/devEvents.d.ts +31 -0
  41. package/node/dist/src/devEvents.d.ts.map +1 -0
  42. package/node/dist/src/devEvents.js +9 -0
  43. package/node/dist/src/devEvents.js.map +1 -0
  44. package/package.json +56 -0
  45. package/plugins/app-manifests.ts +192 -0
  46. package/plugins/asset-catalogs.ts +558 -0
  47. package/plugins/bundle-resources.ts +195 -0
  48. package/plugins/swift-wasm.ts +259 -0
  49. package/public/_headers +23 -0
  50. package/public/apple-touch-icon.png +0 -0
  51. package/public/icon.svg +14 -0
  52. package/public/manifest.webmanifest +14 -0
  53. package/src/animation.ts +521 -0
  54. package/src/assets.ts +86 -0
  55. package/src/chartDom.ts +481 -0
  56. package/src/devEvents.ts +35 -0
  57. package/src/deviceMode.ts +80 -0
  58. package/src/ghost.ts +25 -0
  59. package/src/interaction.ts +1113 -0
  60. package/src/landing.css +261 -0
  61. package/src/landing.ts +141 -0
  62. package/src/layout/chart.ts +477 -0
  63. package/src/layout/controls.ts +116 -0
  64. package/src/layout/engine.ts +3581 -0
  65. package/src/layout/index.ts +91 -0
  66. package/src/layout/measure.ts +86 -0
  67. package/src/layout/text.ts +124 -0
  68. package/src/layout/types.ts +153 -0
  69. package/src/layout/typography.ts +188 -0
  70. package/src/layoutDom.ts +549 -0
  71. package/src/layoutEvents.ts +110 -0
  72. package/src/locale.ts +64 -0
  73. package/src/main.ts +554 -0
  74. package/src/mock.ts +752 -0
  75. package/src/nodeQueries.ts +60 -0
  76. package/src/protocol.ts +1421 -0
  77. package/src/renderer.ts +2882 -0
  78. package/src/styles.css +2834 -0
  79. package/src/styles.ts +373 -0
  80. package/src/swipe.ts +110 -0
  81. package/src/symbols.ts +595 -0
  82. package/src/typography.ts +76 -0
  83. package/src/vite-env.d.ts +26 -0
  84. package/src/wasm.ts +400 -0
package/app/index.html ADDED
@@ -0,0 +1,125 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <!-- viewport-fit=cover lets the app draw under the real notch / home
6
+ indicator in device mode; the layout engine gets the insets from
7
+ env(safe-area-inset-*) (see --sb-inset-* in styles.css). -->
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, user-scalable=no" />
9
+ <meta name="color-scheme" content="light dark" />
10
+ <meta name="theme-color" media="(prefers-color-scheme: light)" content="#ffffff" />
11
+ <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000000" />
12
+ <!-- Safari "Add to Home Screen": full screen. black-translucent keeps the
13
+ OS status bar transparent so the app's own background shows under it
14
+ (the layout engine gets the bar as a safe-area inset); `default` would
15
+ draw an opaque light bar whatever the app's color scheme. -->
16
+ <meta name="apple-mobile-web-app-capable" content="yes" />
17
+ <meta name="mobile-web-app-capable" content="yes" />
18
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
19
+ <meta name="apple-mobile-web-app-title" content="SwiftBrowser" />
20
+ <title>SwiftBrowser</title>
21
+ <!-- Replaced below with the manifest of the app named by ?app= (see
22
+ plugins/app-manifests.ts); the home-screen icon must reopen that app. -->
23
+ <link rel="manifest" href="/manifest.webmanifest" />
24
+ <link rel="icon" href="/icon.svg" type="image/svg+xml" />
25
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
26
+ <link rel="stylesheet" href="/src/styles.css" />
27
+ <script>
28
+ // Decide frame vs device mode before the first paint so a phone never
29
+ // flashes the fake bezel. src/deviceMode.ts holds the same rule and is
30
+ // what main.ts trusts; this only pre-sets the attribute the CSS keys on.
31
+ // A coarse pointer means device mode at any width (Phase 7).
32
+ (function () {
33
+ var mode = new URLSearchParams(location.search).get('mode');
34
+ var coarse = typeof matchMedia === 'function' && matchMedia('(pointer: coarse)').matches;
35
+ var device = mode === 'device' || (mode !== 'frame' && coarse);
36
+ document.documentElement.setAttribute('data-sb-mode', device ? 'device' : 'frame');
37
+ })();
38
+ // Point the manifest and the home-screen title at the requested app, so
39
+ // Share -> Add to Home Screen (with that app open) makes an icon that
40
+ // launches it, not the default app. Same name rule as main.ts. Apps that
41
+ // bring their own icon are listed in window.__SB_APP_ICONS__ (injected
42
+ // by plugins/app-manifests.ts); Safari reads the apple-touch-icon link,
43
+ // not the manifest, for the home-screen image.
44
+ (function () {
45
+ var app = (new URLSearchParams(location.search).get('app') || '').replace(/[^A-Za-z0-9_-]/g, '');
46
+ if (!app) return;
47
+ var manifest = document.querySelector('link[rel="manifest"]');
48
+ if (manifest) manifest.setAttribute('href', '/manifests/' + app + '.webmanifest');
49
+ var icons = window.__SB_APP_ICONS__ || {};
50
+ var touchIcon = document.querySelector('link[rel="apple-touch-icon"]');
51
+ if (touchIcon && icons[app]) touchIcon.setAttribute('href', icons[app]);
52
+ var title = document.querySelector('meta[name="apple-mobile-web-app-title"]');
53
+ if (title) title.setAttribute('content', app);
54
+ document.title = app + ' \u00b7 SwiftBrowser';
55
+ })();
56
+ </script>
57
+ </head>
58
+ <body>
59
+ <header class="toolbar" role="toolbar" aria-label="SwiftBrowser controls">
60
+ <span class="toolbar-brand">SwiftBrowser</span>
61
+ <span class="toolbar-sep" aria-hidden="true"></span>
62
+ <span class="toolbar-label">App</span>
63
+ <span id="app-name" class="toolbar-value">Loading…</span>
64
+ <span class="toolbar-spacer"></span>
65
+ <span id="build-status" class="toolbar-build" role="status" aria-live="polite" hidden>Rebuilding…</span>
66
+ <label class="toolbar-label" for="type-size">Text size</label>
67
+ <select id="type-size" class="toolbar-select" aria-label="Text size">
68
+ <option value="xSmall">xSmall</option>
69
+ <option value="small">Small</option>
70
+ <option value="medium">Medium</option>
71
+ <option value="large" selected>Large (default)</option>
72
+ <option value="xLarge">xLarge</option>
73
+ <option value="xxLarge">xxLarge</option>
74
+ <option value="xxxLarge">xxxLarge</option>
75
+ <option value="accessibility1">Accessibility 1</option>
76
+ <option value="accessibility2">Accessibility 2</option>
77
+ <option value="accessibility3">Accessibility 3</option>
78
+ <option value="accessibility4">Accessibility 4</option>
79
+ <option value="accessibility5">Accessibility 5</option>
80
+ </select>
81
+ <button id="theme-toggle" class="toolbar-button" type="button" aria-pressed="false">Dark mode</button>
82
+ </header>
83
+
84
+ <main class="stage">
85
+ <div id="device" class="device" data-theme="light" data-state="loading">
86
+ <div class="device-screen">
87
+ <div class="dynamic-island" aria-hidden="true"></div>
88
+
89
+ <div class="status-bar" aria-hidden="true">
90
+ <span class="status-time">9:41</span>
91
+ <span class="status-glyphs">
92
+ <!-- Cellular -->
93
+ <svg class="glyph glyph-cellular" viewBox="0 0 18 12" width="18" height="12">
94
+ <rect x="0" y="8" width="3" height="4" rx="0.8" />
95
+ <rect x="5" y="5.5" width="3" height="6.5" rx="0.8" />
96
+ <rect x="10" y="3" width="3" height="9" rx="0.8" />
97
+ <rect x="15" y="0" width="3" height="12" rx="0.8" />
98
+ </svg>
99
+ <!-- Wi-Fi -->
100
+ <svg class="glyph glyph-wifi" viewBox="0 0 16 12" width="16" height="12">
101
+ <path d="M8 2.6c2.4 0 4.6.9 6.3 2.5l1.1-1.2A10.6 10.6 0 0 0 8 .9 10.6 10.6 0 0 0 .6 3.9l1.1 1.2A9 9 0 0 1 8 2.6Z" />
102
+ <path d="M8 6.2c1.5 0 2.9.6 3.9 1.5l1.1-1.2A7.2 7.2 0 0 0 8 4.5a7.2 7.2 0 0 0-5 2l1.1 1.2A5.6 5.6 0 0 1 8 6.2Z" />
103
+ <path d="M8 9.8c.7 0 1.3.3 1.7.7L8 12.3l-1.7-1.8c.4-.4 1-.7 1.7-.7Z" />
104
+ </svg>
105
+ <!-- Battery -->
106
+ <svg class="glyph glyph-battery" viewBox="0 0 28 13" width="28" height="13">
107
+ <rect class="battery-shell" x="0.5" y="0.5" width="24" height="12" rx="3.5" />
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.9-.3 1.5-1.1 1.5-2.2S26.9 4.6 26 4.3Z" />
110
+ </svg>
111
+ </span>
112
+ </div>
113
+
114
+ <!-- Root container: element id 0. -->
115
+ <div id="screen" class="screen" data-sb-id="0"></div>
116
+
117
+ <div class="home-indicator" aria-hidden="true"></div>
118
+ </div>
119
+ </div>
120
+ <p id="status" class="status" role="alert" hidden></p>
121
+ </main>
122
+
123
+ <script type="module" src="/src/main.ts"></script>
124
+ </body>
125
+ </html>
package/index.html ADDED
@@ -0,0 +1,56 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
+ <meta name="color-scheme" content="light dark" />
7
+ <meta name="theme-color" media="(prefers-color-scheme: light)" content="#f2f2f7" />
8
+ <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000000" />
9
+ <meta name="description" content="SwiftUI example apps compiled to WebAssembly and rendered by SwiftBrowser. Open one on a phone or scan its QR code." />
10
+ <title>SwiftBrowser examples</title>
11
+ <link rel="manifest" href="/manifest.webmanifest" />
12
+ <link rel="icon" href="/icon.svg" type="image/svg+xml" />
13
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
14
+ <link rel="stylesheet" href="/src/landing.css" />
15
+ </head>
16
+ <body>
17
+ <header class="hero">
18
+ <img class="hero-icon" src="/icon.svg" width="56" height="56" alt="" />
19
+ <h1>SwiftBrowser examples</h1>
20
+ <p class="hero-lead">
21
+ SwiftUI apps compiled to WebAssembly and rendered in the browser. Open one here, or
22
+ scan its code with a phone.
23
+ </p>
24
+ </header>
25
+
26
+ <main>
27
+ <section aria-labelledby="apps-heading">
28
+ <h2 id="apps-heading" class="visually-hidden">Apps</h2>
29
+ <ul id="apps" class="app-grid" aria-live="polite"></ul>
30
+ </section>
31
+
32
+ <section class="phone-help" aria-labelledby="phone-heading">
33
+ <h2 id="phone-heading">Testing on a phone</h2>
34
+ <ol>
35
+ <li>Open an app link on the phone, or scan its QR code from this page on a desktop.</li>
36
+ <li>
37
+ In Safari, tap <strong>Share</strong> → <strong>Add to Home Screen</strong> and open the
38
+ app from there. It runs full screen with the real status bar, so safe areas and
39
+ Dynamic Type match a native app.
40
+ </li>
41
+ <li>
42
+ On a phone the page runs the app without the fake iPhone frame (device mode). Add
43
+ <code>&amp;mode=frame</code> to the URL to see the frame anyway, or
44
+ <code>&amp;mode=device</code> on a desktop to preview device mode.
45
+ </li>
46
+ </ol>
47
+ </section>
48
+ </main>
49
+
50
+ <footer id="build-info" class="build-info">
51
+ <!-- Filled by landing.ts from the build-time defines. -->
52
+ </footer>
53
+
54
+ <script type="module" src="/src/landing.ts"></script>
55
+ </body>
56
+ </html>
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The app registry: what the dev server and the site builder know about each
3
+ * app they serve. The example apps under `../Examples` are described this way
4
+ * by `vite.config.ts` (see ./examples.ts); the CLI describes the user's app.
5
+ *
6
+ * Node-side only (paths are absolute paths on disk).
7
+ */
8
+ export interface AppSpec {
9
+ /**
10
+ * URL-safe name: `?app=<name>`, `/<name>.wasm`, `/catalog/<name>.json`,
11
+ * `/manifests/<name>.webmanifest`. `[A-Za-z0-9_-]+`
12
+ */
13
+ name: string;
14
+ /** Shown on the landing page and in the web manifest; defaults to `name`. */
15
+ displayName?: string;
16
+ /** One line for the landing page card. */
17
+ description?: string;
18
+ /**
19
+ * Absolute path of the built wasm module to serve at `/<name>.wasm` (dev)
20
+ * and copy into the site (build). May not exist yet when the dev server
21
+ * starts.
22
+ */
23
+ wasm: string;
24
+ /** Absolute paths of `*.xcassets` directories (served as `/catalog/<name>.json` + `/catalog/<name>/<file>`). */
25
+ catalogs: string[];
26
+ /** Absolute directories whose `**\/*.swift` changes trigger `build` in the dev server. */
27
+ watch?: string[];
28
+ /**
29
+ * Rebuilds `wasm` (dev server, on source changes and on first request when
30
+ * the file is missing). Rejects with an Error whose message is the compiler
31
+ * output on failure.
32
+ */
33
+ build?: () => Promise<void>;
34
+ /** Optional path of a PNG app icon (any size) for the manifest and landing card; the default icon otherwise. */
35
+ icon?: string;
36
+ /**
37
+ * Absolute paths of the app's resource files and folders (the Xcode target's
38
+ * Copy Bundle Resources), served at `/bundle/<name>/…` and mounted at
39
+ * `/bundle` for `Bundle.main`. Folders keep their name.
40
+ */
41
+ resources?: string[];
42
+ /**
43
+ * The languages whose locale data the module carries (BCP 47 tags; `*` for
44
+ * all). The page runs the app in the viewer's language when it is among them,
45
+ * else in the first; default `["en"]`.
46
+ */
47
+ locales?: string[];
48
+ }
49
+ /** The name rule `?app=` in src/main.ts and every plugin URL applies. */
50
+ export declare const APP_NAME: RegExp;
51
+ /** `displayName` with its default. */
52
+ export declare function displayNameOf(spec: Pick<AppSpec, 'name' | 'displayName'>): string;
53
+ /**
54
+ * Checks a registry: valid, unique names and absolute paths. Throws an Error
55
+ * naming the first problem.
56
+ */
57
+ export declare function validateAppSpecs(specs: readonly AppSpec[]): void;
58
+ //# sourceMappingURL=apps.d.ts.map
@@ -0,0 +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;CACpB;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,CAsChE"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The app registry: what the dev server and the site builder know about each
3
+ * app they serve. The example apps under `../Examples` are described this way
4
+ * by `vite.config.ts` (see ./examples.ts); the CLI describes the user's app.
5
+ *
6
+ * Node-side only (paths are absolute paths on disk).
7
+ */
8
+ import path from 'node:path';
9
+ /** The name rule `?app=` in src/main.ts and every plugin URL applies. */
10
+ export const APP_NAME = /^[A-Za-z0-9_-]+$/;
11
+ /** `displayName` with its default. */
12
+ export function displayNameOf(spec) {
13
+ return spec.displayName ?? spec.name;
14
+ }
15
+ /**
16
+ * Checks a registry: valid, unique names and absolute paths. Throws an Error
17
+ * naming the first problem.
18
+ */
19
+ export function validateAppSpecs(specs) {
20
+ const seen = new Set();
21
+ for (const spec of specs) {
22
+ if (typeof spec.name !== 'string' || !APP_NAME.test(spec.name)) {
23
+ throw new Error(`Invalid app name ${JSON.stringify(spec.name)}: use letters, digits, "_" and "-" only`);
24
+ }
25
+ if (seen.has(spec.name))
26
+ throw new Error(`Duplicate app name ${JSON.stringify(spec.name)}`);
27
+ seen.add(spec.name);
28
+ const label = `app ${JSON.stringify(spec.name)}`;
29
+ if (typeof spec.wasm !== 'string' || !path.isAbsolute(spec.wasm)) {
30
+ throw new Error(`${label}: "wasm" must be an absolute path (got ${JSON.stringify(spec.wasm)})`);
31
+ }
32
+ if (!Array.isArray(spec.catalogs))
33
+ throw new Error(`${label}: "catalogs" must be an array of absolute paths`);
34
+ for (const dir of spec.catalogs) {
35
+ if (typeof dir !== 'string' || !path.isAbsolute(dir)) {
36
+ throw new Error(`${label}: catalog ${JSON.stringify(dir)} is not an absolute path`);
37
+ }
38
+ }
39
+ for (const dir of spec.watch ?? []) {
40
+ if (typeof dir !== 'string' || !path.isAbsolute(dir)) {
41
+ throw new Error(`${label}: watch directory ${JSON.stringify(dir)} is not an absolute path`);
42
+ }
43
+ }
44
+ if (spec.icon !== undefined && !path.isAbsolute(spec.icon)) {
45
+ throw new Error(`${label}: "icon" must be an absolute path (got ${JSON.stringify(spec.icon)})`);
46
+ }
47
+ for (const resource of spec.resources ?? []) {
48
+ if (typeof resource !== 'string' || !path.isAbsolute(resource)) {
49
+ throw new Error(`${label}: resource ${JSON.stringify(resource)} is not an absolute path`);
50
+ }
51
+ }
52
+ if (spec.locales !== undefined && (!Array.isArray(spec.locales) || !spec.locales.every((l) => typeof l === 'string' && l.length > 0))) {
53
+ throw new Error(`${label}: "locales" must be an array of language tags`);
54
+ }
55
+ if (spec.build !== undefined && typeof spec.build !== 'function') {
56
+ throw new Error(`${label}: "build" must be a function`);
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=apps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apps.js","sourceRoot":"","sources":["../../apps.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Builds the Vite config for a set of apps: the registry plugins
3
+ * (plugins/swift-wasm.ts, asset-catalogs.ts, app-manifests.ts), the two
4
+ * pages (landing + app) and the `define`s the landing page reads.
5
+ *
6
+ * `vite.config.ts` calls this with the example apps; the programmatic API
7
+ * (./index.ts) with whatever the caller registered.
8
+ */
9
+ import type { UserConfig } from 'vite';
10
+ import { type AppSpec } from './apps.js';
11
+ export interface SiteOptions {
12
+ apps: AppSpec[];
13
+ /** Build info shown on the landing page: the commit sha (`dev` when unknown). */
14
+ commit?: string;
15
+ /** Build info shown on the landing page: the branch name ("" when unknown). */
16
+ branch?: string;
17
+ }
18
+ /** One entry of `__SB_APPS__`, what the landing page renders a card from (see src/vite-env.d.ts). */
19
+ export interface LandingApp {
20
+ name: string;
21
+ displayName: string;
22
+ description?: string;
23
+ /** URL of the app's own icon, when its spec has one. */
24
+ icon?: string;
25
+ /** The languages the module carries locale data for (`AppSpec.locales`); the page picks the viewer's among them. */
26
+ locales?: string[];
27
+ }
28
+ /**
29
+ * The Vite root: the `Web/` directory of this package (holds `index.html`,
30
+ * `app/index.html`, `src/` and `public/`). Found by walking up from this
31
+ * module, so it works from the TypeScript sources, from `node/dist/` and from
32
+ * `node_modules/@swiftbrowser/web`.
33
+ */
34
+ export declare function webRoot(): string;
35
+ /** What the landing page gets for each spec. */
36
+ export declare function landingApps(specs: readonly AppSpec[]): LandingApp[];
37
+ /**
38
+ * The Vite config for `options.apps`. Pass it to `vite.createServer` /
39
+ * `vite.build` with `configFile: false`, or export it from a config file.
40
+ */
41
+ export declare function createViteConfig(options: SiteOptions): UserConfig;
42
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAKvC,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;CACjB;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;CACpB;AAOD;;;;;GAKG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAoBhC;AAED,gDAAgD;AAChD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,EAAE,CAQnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CA+BjE"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Builds the Vite config for a set of apps: the registry plugins
3
+ * (plugins/swift-wasm.ts, asset-catalogs.ts, app-manifests.ts), the two
4
+ * pages (landing + app) and the `define`s the landing page reads.
5
+ *
6
+ * `vite.config.ts` calls this with the example apps; the programmatic API
7
+ * (./index.ts) with whatever the caller registered.
8
+ */
9
+ import { existsSync, readFileSync } from 'node:fs';
10
+ import path from 'node:path';
11
+ import { fileURLToPath } from 'node:url';
12
+ import { appManifests } from '../plugins/app-manifests.js';
13
+ import { assetCatalogs } from '../plugins/asset-catalogs.js';
14
+ import { bundleResources } from '../plugins/bundle-resources.js';
15
+ import { swiftWasm } from '../plugins/swift-wasm.js';
16
+ import { displayNameOf, validateAppSpecs } from './apps.js';
17
+ /** The package name that marks the Vite root. */
18
+ const PACKAGE_NAME = '@swiftbrowser/web';
19
+ let cachedRoot;
20
+ /**
21
+ * The Vite root: the `Web/` directory of this package (holds `index.html`,
22
+ * `app/index.html`, `src/` and `public/`). Found by walking up from this
23
+ * module, so it works from the TypeScript sources, from `node/dist/` and from
24
+ * `node_modules/@swiftbrowser/web`.
25
+ */
26
+ export function webRoot() {
27
+ if (cachedRoot)
28
+ return cachedRoot;
29
+ let dir = path.dirname(fileURLToPath(import.meta.url));
30
+ for (;;) {
31
+ const file = path.join(dir, 'package.json');
32
+ if (existsSync(file)) {
33
+ try {
34
+ const pkg = JSON.parse(readFileSync(file, 'utf8'));
35
+ if (pkg.name === PACKAGE_NAME) {
36
+ cachedRoot = dir;
37
+ return dir;
38
+ }
39
+ }
40
+ catch {
41
+ // Not ours; keep looking.
42
+ }
43
+ }
44
+ const parent = path.dirname(dir);
45
+ if (parent === dir)
46
+ throw new Error(`Could not find the ${PACKAGE_NAME} package root above ${import.meta.url}`);
47
+ dir = parent;
48
+ }
49
+ }
50
+ /** What the landing page gets for each spec. */
51
+ export function landingApps(specs) {
52
+ return specs.map((spec) => {
53
+ const entry = { name: spec.name, displayName: displayNameOf(spec) };
54
+ if (spec.description !== undefined)
55
+ entry.description = spec.description;
56
+ if (spec.icon)
57
+ entry.icon = `/manifests/${spec.name}-icon.png`;
58
+ if (spec.locales !== undefined)
59
+ entry.locales = [...spec.locales];
60
+ return entry;
61
+ });
62
+ }
63
+ /**
64
+ * The Vite config for `options.apps`. Pass it to `vite.createServer` /
65
+ * `vite.build` with `configFile: false`, or export it from a config file.
66
+ */
67
+ export function createViteConfig(options) {
68
+ validateAppSpecs(options.apps);
69
+ const root = webRoot();
70
+ const apps = options.apps;
71
+ return {
72
+ root,
73
+ plugins: [swiftWasm(apps), appManifests(apps), assetCatalogs(apps), bundleResources(apps)],
74
+ // Two pages: the landing page at `/` and the app at `/app/`. `mpa` keeps the
75
+ // dev server from answering unknown URLs with the landing page.
76
+ appType: 'mpa',
77
+ define: {
78
+ __SB_APPS__: JSON.stringify(landingApps(apps)),
79
+ __SB_COMMIT__: JSON.stringify(options.commit ?? 'dev'),
80
+ __SB_BRANCH__: JSON.stringify(options.branch ?? ''),
81
+ __SB_BUILT_AT__: JSON.stringify(new Date().toISOString()),
82
+ },
83
+ server: {
84
+ port: 5173,
85
+ strictPort: true,
86
+ },
87
+ build: {
88
+ target: 'es2022',
89
+ sourcemap: true,
90
+ rollupOptions: {
91
+ input: {
92
+ landing: path.resolve(root, 'index.html'),
93
+ app: path.resolve(root, 'app/index.html'),
94
+ },
95
+ },
96
+ },
97
+ };
98
+ }
99
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The example apps (`../Examples/<Name>`) as app specs, for `vite.config.ts`:
3
+ * each is built by `bash scripts/build-wasm.sh <Name>` in the repo root into
4
+ * `Web/public/<Name>.wasm`, carries every `*.xcassets` under its directory,
5
+ * and rebuilds when `Sources/` or its own directory changes.
6
+ */
7
+ import type { AppSpec } from './apps.js';
8
+ /** What each known example demonstrates (the landing page card line). */
9
+ export declare const EXAMPLE_DESCRIPTIONS: Readonly<Record<string, string>>;
10
+ /**
11
+ * Directory prepended to `PATH` for the build so `swift` resolves even when
12
+ * the dev server was started from a shell without the toolchain:
13
+ * `$SWIFT_TOOLCHAIN_BIN`, else the swiftly 6.4.0 location if it exists.
14
+ */
15
+ export declare function toolchainBin(): string | undefined;
16
+ export interface BuildScriptOptions {
17
+ /** Called with every line the script prints (stdout and stderr). */
18
+ onOutput?: (line: string) => void;
19
+ }
20
+ /**
21
+ * `bash scripts/build-wasm.sh <app>` in `repoRoot` with `SB_WASM_OPT=0`: hot
22
+ * rebuilds skip wasm-opt, which costs 15-30 s per module and strips the names
23
+ * that make stack traces readable while developing. Rejects with an Error
24
+ * whose message is the script's combined output when it fails.
25
+ */
26
+ export declare function runBuildScript(repoRoot: string, app: string, options?: BuildScriptOptions): Promise<void>;
27
+ /** The names of the directories under `<repoRoot>/Examples`, sorted. */
28
+ export declare function exampleNames(repoRoot: string): string[];
29
+ /** One spec per example. `repoRoot` defaults to the parent of the Vite root. */
30
+ export declare function exampleAppSpecs(repoRoot?: string): AppSpec[];
31
+ /**
32
+ * Build info for the landing page. CI (GitHub Actions) sets `GITHUB_SHA` /
33
+ * `GITHUB_REF_NAME`; a local build asks git; a checkout without git says "dev".
34
+ */
35
+ export declare function buildInfo(repoRoot: string): {
36
+ commit: string;
37
+ branch: string;
38
+ };
39
+ //# sourceMappingURL=examples.d.ts.map
@@ -0,0 +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,CAOjE,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;;;;;GAKG;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,CAkBnF;AAYD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAK9E"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The example apps (`../Examples/<Name>`) as app specs, for `vite.config.ts`:
3
+ * each is built by `bash scripts/build-wasm.sh <Name>` in the repo root into
4
+ * `Web/public/<Name>.wasm`, carries every `*.xcassets` under its directory,
5
+ * and rebuilds when `Sources/` or its own directory changes.
6
+ */
7
+ import { execFileSync, spawn } from 'node:child_process';
8
+ import { existsSync, readdirSync } from 'node:fs';
9
+ import path from 'node:path';
10
+ import { findAssetCatalogs } from '../plugins/asset-catalogs.js';
11
+ import { webRoot } from './config.js';
12
+ /** What each known example demonstrates (the landing page card line). */
13
+ export const EXAMPLE_DESCRIPTIONS = {
14
+ Counter: '@State and buttons',
15
+ Todos: 'navigation, keyed lists, text input, @Observable',
16
+ Gallery: 'layout engine, animation, sheets, Dynamic Type',
17
+ Settings: 'tabs, forms, pickers, path navigation, tasks, GeometryReader',
18
+ FoodTruck: "Apple's Food Truck sample: FoundationEssentials, asset catalogs, ObservableObject",
19
+ HackingWithSwift: 'Five Hacking with SwiftUI projects: gestures, swipe actions, edit mode, alerts, controls',
20
+ };
21
+ const DEFAULT_TOOLCHAIN_BIN = '/root/.local/share/swiftly/toolchains/6.4.0/usr/bin';
22
+ /**
23
+ * Directory prepended to `PATH` for the build so `swift` resolves even when
24
+ * the dev server was started from a shell without the toolchain:
25
+ * `$SWIFT_TOOLCHAIN_BIN`, else the swiftly 6.4.0 location if it exists.
26
+ */
27
+ export function toolchainBin() {
28
+ const explicit = process.env['SWIFT_TOOLCHAIN_BIN'];
29
+ if (explicit)
30
+ return explicit;
31
+ return existsSync(DEFAULT_TOOLCHAIN_BIN) ? DEFAULT_TOOLCHAIN_BIN : undefined;
32
+ }
33
+ /**
34
+ * `bash scripts/build-wasm.sh <app>` in `repoRoot` with `SB_WASM_OPT=0`: hot
35
+ * rebuilds skip wasm-opt, which costs 15-30 s per module and strips the names
36
+ * that make stack traces readable while developing. Rejects with an Error
37
+ * whose message is the script's combined output when it fails.
38
+ */
39
+ export function runBuildScript(repoRoot, app, options = {}) {
40
+ return new Promise((resolve, reject) => {
41
+ const bin = toolchainBin();
42
+ const env = { ...process.env, SB_WASM_OPT: '0' };
43
+ if (bin)
44
+ env['PATH'] = `${bin}${path.delimiter}${env['PATH'] ?? ''}`;
45
+ const child = spawn('bash', ['scripts/build-wasm.sh', app], { cwd: repoRoot, env });
46
+ let output = '';
47
+ const collect = (chunk) => {
48
+ const text = chunk.toString();
49
+ output += text;
50
+ if (options.onOutput)
51
+ for (const line of text.split('\n'))
52
+ if (line.trim())
53
+ options.onOutput(line);
54
+ };
55
+ child.stdout.on('data', collect);
56
+ child.stderr.on('data', collect);
57
+ child.on('error', (error) => reject(new Error(`${output}\n${error.message}`)));
58
+ child.on('close', (code) => {
59
+ if (code === 0)
60
+ resolve();
61
+ else
62
+ reject(new Error(output || `scripts/build-wasm.sh ${app} exited with ${code}`));
63
+ });
64
+ });
65
+ }
66
+ /** The names of the directories under `<repoRoot>/Examples`, sorted. */
67
+ export function exampleNames(repoRoot) {
68
+ const dir = path.join(repoRoot, 'Examples');
69
+ if (!existsSync(dir))
70
+ return [];
71
+ return readdirSync(dir, { withFileTypes: true })
72
+ .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))
73
+ .map((entry) => entry.name)
74
+ .sort();
75
+ }
76
+ /** One spec per example. `repoRoot` defaults to the parent of the Vite root. */
77
+ export function exampleAppSpecs(repoRoot = path.resolve(webRoot(), '..')) {
78
+ const publicDir = path.join(webRoot(), 'public');
79
+ return exampleNames(repoRoot).map((name) => {
80
+ const dir = path.join(repoRoot, 'Examples', name);
81
+ const spec = {
82
+ name,
83
+ wasm: path.join(publicDir, `${name}.wasm`),
84
+ catalogs: findAssetCatalogs(dir),
85
+ watch: [path.join(repoRoot, 'Sources'), dir],
86
+ build: () => runBuildScript(repoRoot, name, {
87
+ onOutput: (line) => console.log(` ${line}`),
88
+ }),
89
+ };
90
+ const description = EXAMPLE_DESCRIPTIONS[name];
91
+ if (description !== undefined)
92
+ spec.description = description;
93
+ return spec;
94
+ });
95
+ }
96
+ /** `git <args>` in `repoRoot`, or null when git / the repo is unavailable. */
97
+ function git(repoRoot, ...args) {
98
+ try {
99
+ const out = execFileSync('git', args, { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
100
+ return out.trim() || null;
101
+ }
102
+ catch {
103
+ return null;
104
+ }
105
+ }
106
+ /**
107
+ * Build info for the landing page. CI (GitHub Actions) sets `GITHUB_SHA` /
108
+ * `GITHUB_REF_NAME`; a local build asks git; a checkout without git says "dev".
109
+ */
110
+ export function buildInfo(repoRoot) {
111
+ return {
112
+ commit: process.env['GITHUB_SHA'] || git(repoRoot, 'rev-parse', '--short', 'HEAD') || 'dev',
113
+ branch: process.env['GITHUB_REF_NAME'] || git(repoRoot, 'rev-parse', '--abbrev-ref', 'HEAD') || '',
114
+ };
115
+ }
116
+ //# sourceMappingURL=examples.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"examples.js","sourceRoot":"","sources":["../../examples.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,yEAAyE;AACzE,MAAM,CAAC,MAAM,oBAAoB,GAAqC;IACpE,OAAO,EAAE,oBAAoB;IAC7B,KAAK,EAAE,kDAAkD;IACzD,OAAO,EAAE,gDAAgD;IACzD,QAAQ,EAAE,8DAA8D;IACxE,SAAS,EAAE,mFAAmF;IAC9F,gBAAgB,EAAE,0FAA0F;CAC7G,CAAC;AAEF,MAAM,qBAAqB,GAAG,qDAAqD,CAAC;AAEpF;;;;GAIG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACpD,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,UAAU,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/E,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,GAAW,EAAE,UAA8B,EAAE;IAC5F,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,YAAY,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAsB,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC;QACpE,IAAI,GAAG;YAAE,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,uBAAuB,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACpF,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,CAAC,KAAa,EAAQ,EAAE;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC9B,MAAM,IAAI,IAAI,CAAC;YACf,IAAI,OAAO,CAAC,QAAQ;gBAAE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;oBAAE,IAAI,IAAI,CAAC,IAAI,EAAE;wBAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACrG,CAAC,CAAC;QACF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,IAAI,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;;gBACrB,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,yBAAyB,GAAG,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,OAAO,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;SAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;SACrE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,IAAI,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IACjD,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QAClD,MAAM,IAAI,GAAY;YACpB,IAAI;YACJ,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,OAAO,CAAC;YAC1C,QAAQ,EAAE,iBAAiB,CAAC,GAAG,CAAC;YAChC,KAAK,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC;YAC5C,KAAK,EAAE,GAAG,EAAE,CACV,cAAc,CAAC,QAAQ,EAAE,IAAI,EAAE;gBAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;aAC7C,CAAC;SACL,CAAC;QACF,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,SAAS,GAAG,CAAC,QAAgB,EAAE,GAAG,IAAc;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAChH,OAAO,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,QAAgB;IACxC,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,IAAI,KAAK;QAC3F,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,CAAC,IAAI,EAAE;KACnG,CAAC;AACJ,CAAC"}