@swiftbrowser/react 0.18.1 → 0.18.2

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/dist/index.js CHANGED
@@ -4,10 +4,12 @@ import Reconciler from "react-reconciler";
4
4
  import { horizontalSizeClassFor } from "@swiftbrowser/web/src/protocol";
5
5
  import { BrowserHistory } from "@swiftbrowser/web/src/browserHistory";
6
6
  import { watchKeyboard } from "@swiftbrowser/web/src/deviceMode";
7
- import { browserEnvironment, loadableURL, performHostRequest } from "@swiftbrowser/web/src/hostRequests";
7
+ import { browserEnvironment, exportFiles, loadableURL, performHostRequest } from "@swiftbrowser/web/src/hostRequests";
8
8
  import { Renderer } from "@swiftbrowser/web/src/renderer";
9
+ import { currentScenePhase, watchScenePhase } from "@swiftbrowser/web/src/scenePhase";
9
10
  import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
10
11
  import { applyTypeSizeVariables } from "@swiftbrowser/web/src/typography";
12
+ import { configureSignInWithApple } from "@swiftbrowser/web/src/appleSignIn";
11
13
  import { fileURL } from "@swiftbrowser/web/src/files";
12
14
  import { registerHostContent, notifyHostContentChange, unregisterHostContent } from "@swiftbrowser/web/src/hostContent";
13
15
  import { createRoot } from "react-dom/client";
@@ -28,7 +30,8 @@ function EnvironmentExtender({ values, children }) {
28
30
  const EnvironmentContext = createContext({
29
31
  colorScheme: "light",
30
32
  dynamicTypeSize: "large",
31
- horizontalSizeClass: "compact"
33
+ horizontalSizeClass: "compact",
34
+ scenePhase: "active"
32
35
  });
33
36
  function EnvironmentProvider({ value, children }) {
34
37
  return createElement(EnvironmentContext.Provider, { value }, children);
@@ -48,6 +51,9 @@ function useDismiss() {
48
51
  function useHorizontalSizeClass() {
49
52
  return useContext(EnvironmentContext).horizontalSizeClass;
50
53
  }
54
+ function useScenePhase() {
55
+ return useContext(EnvironmentContext).scenePhase;
56
+ }
51
57
  function useRedacted() {
52
58
  return useContext(ViewEnvironmentContext).redacted === true;
53
59
  }
@@ -392,7 +398,7 @@ function deepEqual(a, b) {
392
398
  if (keys.length !== Object.keys(right).length) return false;
393
399
  return keys.every((key) => key in right && deepEqual(left[key], right[key]));
394
400
  }
395
- const DEFAULT_ENVIRONMENT = { colorScheme: "light", dynamicTypeSize: "large", horizontalSizeClass: "compact" };
401
+ const DEFAULT_ENVIRONMENT = { colorScheme: "light", dynamicTypeSize: "large", horizontalSizeClass: "compact", scenePhase: "active" };
396
402
  const roots = /* @__PURE__ */ new Set();
397
403
  function createSwiftUIRoot(sink) {
398
404
  const tree = new HostTree(sink);
@@ -647,7 +653,8 @@ function mount(screen, element, options = {}) {
647
653
  const environment = {
648
654
  colorScheme: pageScheme,
649
655
  dynamicTypeSize: options.dynamicTypeSize ?? "large",
650
- horizontalSizeClass: horizontalSizeClassFor(screen.clientWidth || 393)
656
+ horizontalSizeClass: horizontalSizeClassFor(screen.clientWidth || 393),
657
+ scenePhase: typeof document !== "undefined" ? currentScenePhase() : "active"
651
658
  };
652
659
  renderer.contentHeight = options.contentHeight ?? false;
653
660
  if (options.assets) renderer.setAssetCatalog(options.assets);
@@ -679,6 +686,7 @@ function mount(screen, element, options = {}) {
679
686
  root.setEnvironment({ horizontalSizeClass: next });
680
687
  }) : null;
681
688
  observer?.observe(screen);
689
+ const stopScenePhase = typeof document !== "undefined" ? watchScenePhase((scenePhase) => root.setEnvironment({ scenePhase })) : null;
682
690
  root.render(element);
683
691
  const mounted = {
684
692
  root,
@@ -691,6 +699,7 @@ function mount(screen, element, options = {}) {
691
699
  },
692
700
  unmount() {
693
701
  observer?.disconnect();
702
+ stopScenePhase?.();
694
703
  stopKeyboard?.();
695
704
  browserHistory?.dispose();
696
705
  screen.removeEventListener("sb:color-scheme", onPreferredScheme);
@@ -1112,6 +1121,61 @@ function FileImporterPresenter(spec) {
1112
1121
  }, [presented, setPresented]);
1113
1122
  return null;
1114
1123
  }
1124
+ const EXTENSIONS = {
1125
+ "text/plain": "txt",
1126
+ "text/csv": "csv",
1127
+ "text/markdown": "md",
1128
+ "text/html": "html",
1129
+ "application/json": "json",
1130
+ "application/pdf": "pdf",
1131
+ "image/png": "png",
1132
+ "image/jpeg": "jpg",
1133
+ "image/svg+xml": "svg",
1134
+ "audio/mp4": "m4a",
1135
+ "video/mp4": "mp4"
1136
+ };
1137
+ function exportFile(document2, contentType, defaultFilename, index = 0) {
1138
+ const type = document2 instanceof Blob && document2.type || contentType || "application/octet-stream";
1139
+ let name = defaultFilename ?? (document2 instanceof File ? document2.name : index === 0 ? "Untitled" : `Untitled ${index + 1}`);
1140
+ name = name.replace(/[\\/:*?"<>|]+/g, "-").trim() || "Untitled";
1141
+ const extension = EXTENSIONS[type.split(";")[0].trim()];
1142
+ if (extension && !/\.[A-Za-z0-9]{1,8}$/.test(name)) name = `${name}.${extension}`;
1143
+ if (document2 instanceof File && document2.name === name && document2.type === type) return document2;
1144
+ const bytes = typeof document2 === "string" ? new TextEncoder().encode(document2) : document2;
1145
+ return new File([bytes], name, { type });
1146
+ }
1147
+ function FileExporterPresenter(spec) {
1148
+ const [presented, setPresented] = readBinding(spec.isPresented);
1149
+ const latest = useRef(spec);
1150
+ latest.current = spec;
1151
+ const exporting = useRef(false);
1152
+ useLayoutEffect(() => {
1153
+ if (!presented || exporting.current) return;
1154
+ const { document: document2, contentType, defaultFilename } = latest.current;
1155
+ const documents = document2 === null ? [] : Array.isArray(document2) ? document2 : [document2];
1156
+ if (documents.length === 0) {
1157
+ setPresented(false);
1158
+ latest.current.onCancellation?.();
1159
+ return;
1160
+ }
1161
+ exporting.current = true;
1162
+ const files = documents.map((item, index) => exportFile(item, contentType, defaultFilename, index));
1163
+ exportFiles(files).then(
1164
+ (result) => {
1165
+ exporting.current = false;
1166
+ setPresented(false);
1167
+ if (result.outcome === "cancelled") latest.current.onCancellation?.();
1168
+ else latest.current.onCompletion?.(result);
1169
+ },
1170
+ (error) => {
1171
+ exporting.current = false;
1172
+ setPresented(false);
1173
+ latest.current.onError?.(error);
1174
+ }
1175
+ );
1176
+ }, [presented, setPresented]);
1177
+ return null;
1178
+ }
1115
1179
  const isPoint = (value) => typeof value === "object" && value !== null && ("x" in value || "y" in value);
1116
1180
  function command(request) {
1117
1181
  return request.axes ? { anchor: request.anchor, serial: request.serial, axes: request.axes } : { anchor: request.anchor, serial: request.serial };
@@ -2136,6 +2200,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
2136
2200
  "magnifyGesture",
2137
2201
  "rotateGesture",
2138
2202
  "onLongPressGesture",
2203
+ "onTouches",
2139
2204
  "simultaneousGesture",
2140
2205
  "highPriorityGesture",
2141
2206
  "contextMenu",
@@ -2188,6 +2253,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
2188
2253
  "safeAreaInset",
2189
2254
  "popover",
2190
2255
  "fileImporter",
2256
+ "fileExporter",
2191
2257
  "redacted",
2192
2258
  "keyboardShortcut",
2193
2259
  "onKeyPress",
@@ -2577,6 +2643,10 @@ function apply(key, value, node, sides) {
2577
2643
  case "rotateGesture":
2578
2644
  case "onLongPressGesture":
2579
2645
  return gesture(key, value, node);
2646
+ case "onTouches": {
2647
+ const handler = value;
2648
+ return sb("touches", {}, { touches: (event) => handler(event.touches, event.phase) }, node);
2649
+ }
2580
2650
  case "simultaneousGesture":
2581
2651
  case "highPriorityGesture": {
2582
2652
  const mode = key === "simultaneousGesture" ? "simultaneous" : "highPriority";
@@ -2747,6 +2817,9 @@ function apply(key, value, node, sides) {
2747
2817
  case "fileImporter":
2748
2818
  sides.push(createElement(FileImporterPresenter, value));
2749
2819
  return node;
2820
+ case "fileExporter":
2821
+ sides.push(createElement(FileExporterPresenter, value));
2822
+ return node;
2750
2823
  case "redacted":
2751
2824
  return withEnvironment({ redacted: Boolean(value) }, node);
2752
2825
  case "keyboardShortcut":
@@ -2880,10 +2953,10 @@ function GeometryChangeView({ handler, children }) {
2880
2953
  });
2881
2954
  const heard = reported !== NO_GEOMETRY;
2882
2955
  const value = heard && typeof handler !== "function" ? handler.of(proxy) : void 0;
2883
- useEffect(() => {
2956
+ useLayoutEffect(() => {
2884
2957
  if (readFrame && !needsFrame) setNeedsFrame(true);
2885
2958
  });
2886
- useEffect(() => {
2959
+ useLayoutEffect(() => {
2887
2960
  if (!heard) return;
2888
2961
  if (typeof handler === "function") {
2889
2962
  handler(proxy);
@@ -3501,175 +3574,53 @@ function useAudioRecorder(options = {}) {
3501
3574
  );
3502
3575
  return { available: host !== void 0, status, time, levels, error, record, pause, resume, stop, cancel, discard, trim };
3503
3576
  }
3504
- const stores = /* @__PURE__ */ new WeakMap();
3505
- function entryFor(store, key) {
3506
- let entries = stores.get(store);
3507
- if (!entries) {
3508
- entries = /* @__PURE__ */ new Map();
3509
- stores.set(store, entries);
3510
- if (store === pageStorage) watchOtherTabs();
3511
- }
3512
- let entry = entries.get(key);
3513
- if (!entry) {
3514
- let raw = null;
3515
- try {
3516
- raw = store.getItem(key);
3517
- } catch {
3518
- }
3519
- entry = { raw, value: void 0, decoded: false, decode: void 0, listeners: /* @__PURE__ */ new Set() };
3520
- entries.set(key, entry);
3521
- }
3522
- return entry;
3523
- }
3524
- function valueOf(entry, absent) {
3525
- if (entry.raw === null) return absent;
3526
- if (!entry.decoded) {
3527
- try {
3528
- const parsed = JSON.parse(entry.raw);
3529
- entry.value = entry.decode ? entry.decode(parsed) : parsed;
3530
- } catch {
3531
- entry.raw = null;
3532
- return absent;
3533
- }
3534
- entry.decoded = true;
3535
- }
3536
- return entry.value;
3537
- }
3538
- function notify(entry) {
3539
- for (const listener of [...entry.listeners]) listener();
3540
- }
3541
- function invalidate(entry, raw) {
3542
- if (entry.raw === raw && entry.decoded) return;
3543
- entry.raw = raw;
3544
- entry.decoded = false;
3545
- notify(entry);
3546
- }
3547
- function readPageStorage() {
3548
- try {
3549
- const storage = globalThis.localStorage;
3550
- return storage && typeof storage.getItem === "function" ? storage : null;
3551
- } catch {
3552
- return null;
3553
- }
3577
+ function wakeLockAPI() {
3578
+ const nav = globalThis.navigator;
3579
+ return nav?.wakeLock ?? null;
3554
3580
  }
3555
- const memory = /* @__PURE__ */ new Map();
3556
- const memoryStorage = {
3557
- getItem: (key) => memory.get(key) ?? null,
3558
- setItem: (key, value) => void memory.set(key, value),
3559
- removeItem: (key) => void memory.delete(key)
3560
- };
3561
- const pageStorage = readPageStorage();
3562
- const appStorage = pageStorage ?? memoryStorage;
3563
- let watching = false;
3564
- function watchOtherTabs() {
3565
- if (watching || !pageStorage) return;
3566
- watching = true;
3567
- const target = globalThis;
3568
- if (typeof target.addEventListener !== "function") return;
3569
- target.addEventListener("storage", (event) => {
3570
- if (event.storageArea !== pageStorage) return;
3571
- const entries = stores.get(pageStorage);
3572
- if (!entries) return;
3573
- if (event.key === null) {
3574
- for (const entry of entries.values()) invalidate(entry, null);
3575
- } else {
3576
- const entry = entries.get(event.key);
3577
- if (entry) invalidate(entry, event.newValue);
3581
+ function useWakeLock(active2 = true) {
3582
+ const [held, setHeld] = useState(false);
3583
+ useEffect(() => {
3584
+ const api = wakeLockAPI();
3585
+ if (!active2 || !api) {
3586
+ setHeld(false);
3587
+ return;
3578
3588
  }
3579
- });
3580
- }
3581
- function useAppStorage(key, defaultValue, options = {}) {
3582
- const { store = appStorage, decode, onError } = options;
3583
- const [fallback] = useState(defaultValue);
3584
- const entry = entryFor(store, key);
3585
- entry.decode = decode;
3586
- const subscribe = useCallback(
3587
- (listener) => {
3588
- const subscribed = entryFor(store, key);
3589
- subscribed.listeners.add(listener);
3590
- return () => void subscribed.listeners.delete(listener);
3591
- },
3592
- [store, key]
3593
- );
3594
- const read = useCallback(() => valueOf(entryFor(store, key), fallback), [store, key, fallback]);
3595
- const value = useSyncExternalStore(subscribe, read, read);
3596
- const set = useCallback(
3597
- (next) => {
3598
- const target = entryFor(store, key);
3599
- const current = valueOf(target, fallback);
3600
- const value2 = typeof next === "function" ? next(current) : next;
3601
- const raw = value2 === void 0 ? null : JSON.stringify(value2);
3602
- try {
3603
- if (raw === null) store.removeItem(key);
3604
- else store.setItem(key, raw);
3605
- } catch (error) {
3606
- onError?.(error);
3607
- }
3608
- target.raw = raw;
3609
- target.value = value2;
3610
- target.decoded = raw !== null;
3611
- notify(target);
3612
- },
3613
- [store, key, fallback, onError]
3614
- );
3615
- return [value, set];
3616
- }
3617
- const NONE = {
3618
- documents: "file:///data/Documents",
3619
- applicationSupport: "file:///data/Library/Application%20Support",
3620
- caches: "file:///data/Library/Caches",
3621
- temporary: "file:///tmp"
3622
- };
3623
- async function bytesOf(contents) {
3624
- if (typeof contents === "string") return new TextEncoder().encode(contents);
3625
- if (contents instanceof Uint8Array) return contents;
3626
- if (contents instanceof ArrayBuffer) return new Uint8Array(contents);
3627
- const blob = contents;
3628
- if (typeof blob.bytes === "function") return await blob.bytes();
3629
- return new Uint8Array(await blob.arrayBuffer());
3630
- }
3631
- function createFileManager(host) {
3632
- const required = () => {
3633
- const files = host();
3634
- if (!files) throw new Error("unsupported: this page keeps no files");
3635
- return files;
3636
- };
3637
- const directories = () => host()?.directories ?? NONE;
3638
- return {
3639
- get available() {
3640
- return host() !== void 0;
3641
- },
3642
- get documentsDirectory() {
3643
- return directories().documents;
3644
- },
3645
- get applicationSupportDirectory() {
3646
- return directories().applicationSupport;
3647
- },
3648
- get cachesDirectory() {
3649
- return directories().caches;
3650
- },
3651
- get temporaryDirectory() {
3652
- return directories().temporary;
3653
- },
3654
- url: (directory, path) => fileURL(directory, path),
3655
- contents: async (url) => required().read(url),
3656
- text: async (url) => new TextDecoder().decode(await required().read(url)),
3657
- write: async (url, contents) => required().write(url, await bytesOf(contents)),
3658
- contentsOfDirectory: async (url) => required().list(url),
3659
- createDirectory: async (url) => required().createDirectory(url),
3660
- fileExists: async (url) => await required().attributes(url) !== null,
3661
- attributesOfItem: async (url) => {
3662
- const attributes = await required().attributes(url);
3663
- if (!attributes) throw new Error(`notFound: nothing at ${url}`);
3664
- return attributes;
3665
- },
3666
- moveItem: async (from, to) => required().move(from, to),
3667
- copyItem: async (from, to) => required().copy(from, to),
3668
- removeItem: async (url) => required().remove(url),
3669
- loadURL: async (url) => required().loadURL(url)
3670
- };
3589
+ let sentinel = null;
3590
+ let stopped = false;
3591
+ const acquire = () => {
3592
+ if (stopped || sentinel && !sentinel.released) return;
3593
+ api.request("screen").then(
3594
+ (next) => {
3595
+ if (stopped) {
3596
+ void next.release();
3597
+ return;
3598
+ }
3599
+ sentinel = next;
3600
+ setHeld(true);
3601
+ next.addEventListener("release", () => {
3602
+ if (sentinel === next) setHeld(false);
3603
+ });
3604
+ },
3605
+ () => setHeld(false)
3606
+ );
3607
+ };
3608
+ const onVisible = () => {
3609
+ if (typeof document === "undefined" || document.visibilityState === "visible") acquire();
3610
+ };
3611
+ acquire();
3612
+ const doc = typeof document !== "undefined" ? document : null;
3613
+ doc?.addEventListener("visibilitychange", onVisible);
3614
+ return () => {
3615
+ stopped = true;
3616
+ doc?.removeEventListener("visibilitychange", onVisible);
3617
+ if (sentinel && !sentinel.released) void sentinel.release();
3618
+ sentinel = null;
3619
+ setHeld(false);
3620
+ };
3621
+ }, [active2]);
3622
+ return held;
3671
3623
  }
3672
- const fileManager = createFileManager(() => browserEnvironment.files);
3673
3624
  function Button(props) {
3674
3625
  const { own, modifiers } = splitProps(props);
3675
3626
  const environment = useViewEnvironment();
@@ -3932,140 +3883,19 @@ function TextEditor(props) {
3932
3883
  function buttonDisabled(modifiers) {
3933
3884
  return modifiers.some(([key, value]) => key === "disabled" && value === true);
3934
3885
  }
3935
- const NO_CLEAR = () => {
3936
- };
3937
- function List(props) {
3938
- const { own, modifiers } = splitProps(props);
3939
- const [editing] = own.editing !== void 0 ? readBinding(own.editing) : [false];
3940
- const listProps = { style: own.listStyle ?? "insetGrouped" };
3941
- if (editing) listProps["editing"] = true;
3942
- let selection = null;
3943
- const split = useContext(SplitSelectionContext);
3944
- let selected = false;
3945
- let clearSelection = NO_CLEAR;
3946
- if (own.selection !== void 0) {
3947
- const [value, setValue] = readBinding(own.selection, own.onSelectionChange);
3948
- if (value instanceof Set) listProps["multiSelect"] = true;
3949
- selected = value instanceof Set ? value.size > 0 : value !== null && value !== void 0;
3950
- clearSelection = () => setValue(value instanceof Set ? /* @__PURE__ */ new Set() : null);
3951
- selection = {
3952
- editing,
3953
- isSelected: (tag) => value instanceof Set ? value.has(tag) : value !== null && Object.is(value, tag),
3954
- select: (tag) => {
3955
- if (value instanceof Set) {
3956
- const next = new Set(value);
3957
- if (next.has(tag)) next.delete(tag);
3958
- else next.add(tag);
3959
- setValue(next);
3960
- } else {
3961
- setValue(editing && Object.is(value, tag) ? null : tag);
3962
- }
3963
- }
3964
- };
3965
- }
3966
- useReportSplitSelection(split, selected, clearSelection, own.selection !== void 0);
3967
- const refresh = useRefreshable(takeModifier(modifiers, "refreshable"));
3968
- Object.assign(listProps, refresh.props);
3969
- const ownSettings = takeScrollSettings(modifiers);
3970
- const edgeEffect = edgeEffectProp(useScrollSettings(ownSettings));
3971
- if (edgeEffect) listProps["edgeEffect"] = edgeEffect;
3972
- const element = /* @__PURE__ */ jsx(LazyContext.Provider, { value: "vertical", children: sb("list", listProps, refresh.handlers, providingScrollSettings(ownSettings, wrapRows(own.children))) });
3973
- return applyModifiers(
3974
- selection ? /* @__PURE__ */ jsx(ListSelectionContext.Provider, { value: selection, children: element }) : element,
3975
- modifiers
3976
- );
3977
- }
3978
- function Form(props) {
3979
- return /* @__PURE__ */ jsx(List, { ...props });
3980
- }
3981
- function Section(props) {
3982
- const { own, modifiers } = splitProps(props);
3983
- const pinned = useContext(PinnedContext);
3984
- let header = supplement(own.header);
3985
- let footer = supplement(own.footer);
3986
- if (pinned?.includes("sectionHeaders") && header.text !== null && !header.view) header = { text: null, view: /* @__PURE__ */ jsx(Text, { children: header.text }) };
3987
- if (pinned?.includes("sectionFooters") && footer.text !== null && !footer.view) footer = { text: null, view: /* @__PURE__ */ jsx(Text, { children: footer.text }) };
3988
- const children = [];
3989
- if (header.view) children.push(sb("sectionheader", {}, void 0, header.view));
3990
- if (own.children !== void 0) children.push(wrapRows(own.children));
3991
- if (footer.view) children.push(sb("sectionfooter", {}, void 0, footer.view));
3992
- return applyModifiers(sb("section", { header: header.text, footer: footer.text }, void 0, children), modifiers);
3993
- }
3994
- function wrapRows(children) {
3995
- let offset = 0;
3996
- return flattenChildren(children, { keepLazy: true }).map((element, index) => {
3997
- const at = index + offset;
3998
- if (element.type === LazyGroup) {
3999
- const group = element.props;
4000
- offset += group.rows.length - 1;
4001
- return { ...withKey(element, index), props: { ...group, start: at, wrap: wrapRow } };
4002
- }
4003
- return wrapRow(withKey(element, index), at);
4004
- });
4005
- }
4006
- function wrapRow(row, containerIndex) {
4007
- const inner = row.type === Fragment ? flattenChildren(row) : null;
4008
- const element = inner?.length === 1 ? { ...inner[0], key: row.key } : row;
4009
- if (element.type === Section) return element;
4010
- if (element.type === Row) return { ...element, props: { ...element.props, containerIndex } };
4011
- const props = element.props;
4012
- const tag = props.tag !== void 0 ? props.tag : element.type === NavigationLink ? props.value : void 0;
4013
- if (tag === void 0) return element;
4014
- return /* @__PURE__ */ jsx(Row, { tag, containerIndex, children: element }, element.key ?? containerIndex);
4015
- }
4016
- function withKey(element, index) {
4017
- return element.key !== null ? element : { ...element, key: String(index) };
4018
- }
4019
- function supplement(node) {
4020
- if (node === void 0 || node === null || node === false) return { text: null, view: null };
4021
- if (typeof node === "string" || typeof node === "number") return { text: String(node), view: null };
4022
- if (isValidElement(node) && node.type === Text) {
4023
- const props = node.props;
4024
- const only = Object.keys(props).every((key) => key === "children");
4025
- if (only) return { text: plainText(node), view: null };
4026
- }
4027
- return { text: null, view: isValidElement(node) ? node : null };
4028
- }
4029
- function TabView(props) {
4030
- const { own, modifiers } = splitProps(props);
4031
- const tabs = flattenChildren(own.children);
4032
- const tags = tabs.map((tab, index) => {
4033
- const tag = tab.props.tag;
4034
- return tag !== void 0 ? tag : index;
4035
- });
4036
- const [internal, setInternal] = useState(tags[0]);
4037
- const [selection, setSelection] = own.selection !== void 0 ? readBinding(own.selection, own.onChange) : [internal, setInternal];
4038
- const selected = Math.max(
4039
- 0,
4040
- tags.findIndex((tag) => Object.is(tag, selection))
4041
- );
4042
- return applyModifiers(
4043
- sb("tabview", { selected }, { select: (event) => tags[event.value] !== void 0 && setSelection(tags[event.value]) }, tabs),
4044
- modifiers
4045
- );
4046
- }
4047
- function Tab(props) {
4048
- const { own, modifiers } = splitProps(props);
4049
- const badge = takeModifier(modifiers, "badge");
4050
- const tabProps = { title: own.title, systemImage: own.systemImage ?? null };
4051
- if (badge !== void 0 && badge !== null) tabProps["badge"] = String(badge);
4052
- if (own.role) tabProps["role"] = own.role;
4053
- if (own.image && !own.systemImage) tabProps["image"] = own.image;
4054
- return applyModifiers(sb("tab", tabProps, void 0, own.children), modifiers);
4055
- }
4056
- const STYLE_KEYS = ["r", "name", "named", "hierarchical", "linearGradient", "radialGradient", "angularGradient", "colorGradient", "material"];
4057
- function strokeOf(value, inset) {
4058
- if (value === void 0) return null;
4059
- const isSpec = typeof value === "object" && value !== null && !STYLE_KEYS.some((key) => key in value);
4060
- const spec = isSpec ? value : { style: value };
4061
- const stroke = { color: shapeStyle(spec.style ?? "primary"), lineWidth: spec.lineWidth ?? 1 };
4062
- if (inset) stroke.inset = true;
4063
- if (spec.dash) stroke.dash = spec.dash;
4064
- if (spec.dashPhase !== void 0) stroke.dashPhase = spec.dashPhase;
4065
- if (spec.lineCap) stroke.lineCap = spec.lineCap;
4066
- if (spec.lineJoin) stroke.lineJoin = spec.lineJoin;
4067
- if (spec.miterLimit !== void 0) stroke.miterLimit = spec.miterLimit;
4068
- return stroke;
3886
+ const STYLE_KEYS = ["r", "name", "named", "hierarchical", "linearGradient", "radialGradient", "angularGradient", "colorGradient", "material"];
3887
+ function strokeOf(value, inset) {
3888
+ if (value === void 0) return null;
3889
+ const isSpec = typeof value === "object" && value !== null && !STYLE_KEYS.some((key) => key in value);
3890
+ const spec = isSpec ? value : { style: value };
3891
+ const stroke = { color: shapeStyle(spec.style ?? "primary"), lineWidth: spec.lineWidth ?? 1 };
3892
+ if (inset) stroke.inset = true;
3893
+ if (spec.dash) stroke.dash = spec.dash;
3894
+ if (spec.dashPhase !== void 0) stroke.dashPhase = spec.dashPhase;
3895
+ if (spec.lineCap) stroke.lineCap = spec.lineCap;
3896
+ if (spec.lineJoin) stroke.lineJoin = spec.lineJoin;
3897
+ if (spec.miterLimit !== void 0) stroke.miterLimit = spec.miterLimit;
3898
+ return stroke;
4069
3899
  }
4070
3900
  function shape(kind, cornerRadius, props) {
4071
3901
  const { own, modifiers } = splitProps(props);
@@ -4154,6 +3984,595 @@ function ColorView(props) {
4154
3984
  return applyModifiers(sb("color", { color: color(own.color) }), modifiers);
4155
3985
  }
4156
3986
  const Color = Object.assign(ColorView, ColorValues);
3987
+ function canSignInWithApple() {
3988
+ return browserEnvironment.appleSignIn !== void 0;
3989
+ }
3990
+ async function signInWithApple(options = {}) {
3991
+ const host = browserEnvironment.appleSignIn;
3992
+ if (!host) throw new Error("unsupported: Sign in with Apple is not available here (configureSignInWithApple on a page)");
3993
+ return host.signIn({ scopes: options.scopes ?? ["fullName", "email"], ...options.nonce ? { nonce: options.nonce } : {}, ...options.state ? { state: options.state } : {} });
3994
+ }
3995
+ async function appleCredentialState(user) {
3996
+ const host = browserEnvironment.appleSignIn;
3997
+ if (!host) return "unknown";
3998
+ return host.credentialState(user);
3999
+ }
4000
+ const LABELS = { signIn: "Sign in with Apple", signUp: "Sign up with Apple", continue: "Continue with Apple" };
4001
+ function SignInWithAppleButton(props) {
4002
+ const { own, modifiers } = splitProps(props);
4003
+ const style = own.style ?? "black";
4004
+ const background = style === "black" ? "black" : "white";
4005
+ const foreground = style === "black" ? "white" : "black";
4006
+ const action = () => {
4007
+ signInWithApple({ ...own.scopes ? { scopes: own.scopes } : {}, ...own.nonce ? { nonce: own.nonce } : {}, ...own.state ? { state: own.state } : {} }).then(
4008
+ (credential) => own.onCompletion({ credential }),
4009
+ (error) => own.onCompletion({ error: error instanceof Error ? error : new Error(String(error)) })
4010
+ );
4011
+ };
4012
+ return /* @__PURE__ */ jsx(
4013
+ Button,
4014
+ {
4015
+ action,
4016
+ buttonStyle: "plain",
4017
+ foregroundStyle: foreground,
4018
+ background: /* @__PURE__ */ jsx(RoundedRectangle, { cornerRadius: 8, fill: background }),
4019
+ ...style === "whiteOutline" ? { overlay: /* @__PURE__ */ jsx(RoundedRectangle, { cornerRadius: 8, strokeBorder: { style: "black", lineWidth: 1 } }) } : {},
4020
+ ...Object.fromEntries(modifiers),
4021
+ children: /* @__PURE__ */ jsxs(HStack, { spacing: 6, frame: { maxWidth: Infinity, height: 50 }, font: system(19, "medium"), children: [
4022
+ /* @__PURE__ */ jsx(Image$1, { systemName: "apple.logo" }),
4023
+ /* @__PURE__ */ jsx(Text, { children: LABELS[own.label ?? "signIn"] })
4024
+ ] })
4025
+ }
4026
+ );
4027
+ }
4028
+ function geolocationAPI() {
4029
+ const nav = globalThis.navigator;
4030
+ return nav?.geolocation ?? null;
4031
+ }
4032
+ function useGeolocation(options = {}) {
4033
+ const { watch = false, immediate = false, enableHighAccuracy = false, timeout, maximumAge } = options;
4034
+ const api = geolocationAPI();
4035
+ const [status, setStatus] = useState("idle");
4036
+ const [position, setPosition] = useState(null);
4037
+ const [error, setError] = useState(null);
4038
+ const watching2 = useRef(null);
4039
+ const mounted = useRef(true);
4040
+ const stop = useCallback(() => {
4041
+ if (watching2.current !== null) {
4042
+ geolocationAPI()?.clearWatch(watching2.current);
4043
+ watching2.current = null;
4044
+ }
4045
+ }, []);
4046
+ const request = useCallback(() => {
4047
+ const geo = geolocationAPI();
4048
+ if (!geo) {
4049
+ setStatus("failed");
4050
+ setError("geolocation: not available");
4051
+ return;
4052
+ }
4053
+ stop();
4054
+ setStatus("locating");
4055
+ setError(null);
4056
+ const positionOptions = { enableHighAccuracy, ...timeout !== void 0 ? { timeout } : {}, ...maximumAge !== void 0 ? { maximumAge } : {} };
4057
+ const onFix = (p) => {
4058
+ if (!mounted.current) return;
4059
+ setPosition({
4060
+ latitude: p.coords.latitude,
4061
+ longitude: p.coords.longitude,
4062
+ accuracy: p.coords.accuracy,
4063
+ altitude: p.coords.altitude ?? null,
4064
+ heading: p.coords.heading ?? null,
4065
+ speed: p.coords.speed ?? null,
4066
+ timestamp: p.timestamp
4067
+ });
4068
+ setStatus("located");
4069
+ setError(null);
4070
+ };
4071
+ const onError = (e) => {
4072
+ if (!mounted.current) return;
4073
+ setStatus(e.code === 1 ? "denied" : "failed");
4074
+ setError(e.message);
4075
+ if (e.code === 1) stop();
4076
+ };
4077
+ if (watch) watching2.current = geo.watchPosition(onFix, onError, positionOptions);
4078
+ else geo.getCurrentPosition(onFix, onError, positionOptions);
4079
+ }, [watch, enableHighAccuracy, timeout, maximumAge, stop]);
4080
+ useEffect(() => {
4081
+ mounted.current = true;
4082
+ if (immediate) request();
4083
+ return () => {
4084
+ mounted.current = false;
4085
+ stop();
4086
+ };
4087
+ }, [immediate, request, stop]);
4088
+ return { available: api !== null, status, position, error, request, stop };
4089
+ }
4090
+ function synthesisAPI() {
4091
+ const g = globalThis;
4092
+ return g.speechSynthesis && g.SpeechSynthesisUtterance ? { synthesis: g.speechSynthesis, Utterance: g.SpeechSynthesisUtterance } : null;
4093
+ }
4094
+ function useSpeechSynthesis() {
4095
+ const api = synthesisAPI();
4096
+ const [speaking, setSpeaking] = useState(false);
4097
+ const [paused, setPaused] = useState(false);
4098
+ const mounted = useRef(true);
4099
+ const queued = useRef(0);
4100
+ useEffect(() => {
4101
+ mounted.current = true;
4102
+ return () => {
4103
+ mounted.current = false;
4104
+ if (queued.current > 0) synthesisAPI()?.synthesis.cancel();
4105
+ };
4106
+ }, []);
4107
+ const speak = useCallback((text, options = {}) => {
4108
+ const current = synthesisAPI();
4109
+ if (!current) return;
4110
+ const utterance = new current.Utterance(text);
4111
+ if (options.lang) utterance.lang = options.lang;
4112
+ if (options.rate !== void 0) utterance.rate = options.rate;
4113
+ if (options.pitch !== void 0) utterance.pitch = options.pitch;
4114
+ if (options.volume !== void 0) utterance.volume = options.volume;
4115
+ queued.current++;
4116
+ const done = () => {
4117
+ queued.current = Math.max(0, queued.current - 1);
4118
+ if (!mounted.current) return;
4119
+ if (queued.current === 0) {
4120
+ setSpeaking(false);
4121
+ setPaused(false);
4122
+ }
4123
+ };
4124
+ utterance.onstart = () => {
4125
+ if (!mounted.current) return;
4126
+ setSpeaking(true);
4127
+ setPaused(false);
4128
+ };
4129
+ utterance.onpause = () => mounted.current && setPaused(true);
4130
+ utterance.onresume = () => mounted.current && setPaused(false);
4131
+ utterance.onend = done;
4132
+ utterance.onerror = done;
4133
+ current.synthesis.speak(utterance);
4134
+ }, []);
4135
+ const cancel = useCallback(() => {
4136
+ synthesisAPI()?.synthesis.cancel();
4137
+ }, []);
4138
+ const pause = useCallback(() => synthesisAPI()?.synthesis.pause(), []);
4139
+ const resume = useCallback(() => synthesisAPI()?.synthesis.resume(), []);
4140
+ return { available: api !== null, speaking, paused, speak, cancel, pause, resume };
4141
+ }
4142
+ function recognitionAPI() {
4143
+ const g = globalThis;
4144
+ return g.SpeechRecognition ?? g.webkitSpeechRecognition ?? null;
4145
+ }
4146
+ function useSpeechRecognition(options = {}) {
4147
+ const { lang, continuous = false, interimResults = true } = options;
4148
+ const api = recognitionAPI();
4149
+ const [listening, setListening] = useState(false);
4150
+ const [transcript, setTranscript] = useState("");
4151
+ const [interim, setInterim] = useState("");
4152
+ const [error, setError] = useState(null);
4153
+ const current = useRef(null);
4154
+ const mounted = useRef(true);
4155
+ useEffect(() => {
4156
+ mounted.current = true;
4157
+ return () => {
4158
+ mounted.current = false;
4159
+ current.current?.abort();
4160
+ current.current = null;
4161
+ };
4162
+ }, []);
4163
+ const start = useCallback(() => {
4164
+ const Recognition = recognitionAPI();
4165
+ if (!Recognition) {
4166
+ setError("not-allowed");
4167
+ return;
4168
+ }
4169
+ if (current.current) return;
4170
+ const recognition = new Recognition();
4171
+ if (lang) recognition.lang = lang;
4172
+ recognition.continuous = continuous;
4173
+ recognition.interimResults = interimResults;
4174
+ setTranscript("");
4175
+ setInterim("");
4176
+ setError(null);
4177
+ recognition.onstart = () => mounted.current && setListening(true);
4178
+ recognition.onresult = (e) => {
4179
+ if (!mounted.current) return;
4180
+ const finals = [];
4181
+ let heard = "";
4182
+ for (let i = 0; i < e.results.length; i++) {
4183
+ const result = e.results[i];
4184
+ if (result.isFinal) finals.push(result[0].transcript.trim());
4185
+ else heard = result[0].transcript;
4186
+ }
4187
+ setTranscript(finals.filter(Boolean).join(" "));
4188
+ setInterim(heard);
4189
+ };
4190
+ recognition.onerror = (e) => mounted.current && setError(e.error);
4191
+ recognition.onend = () => {
4192
+ if (current.current === recognition) current.current = null;
4193
+ if (!mounted.current) return;
4194
+ setListening(false);
4195
+ setInterim("");
4196
+ };
4197
+ current.current = recognition;
4198
+ recognition.start();
4199
+ }, [lang, continuous, interimResults]);
4200
+ const stop = useCallback(() => current.current?.stop(), []);
4201
+ const abort = useCallback(() => current.current?.abort(), []);
4202
+ return { available: api !== null, listening, transcript, interim, error, start, stop, abort };
4203
+ }
4204
+ const stores = /* @__PURE__ */ new WeakMap();
4205
+ function entryFor(store, key) {
4206
+ let entries = stores.get(store);
4207
+ if (!entries) {
4208
+ entries = /* @__PURE__ */ new Map();
4209
+ stores.set(store, entries);
4210
+ if (store === pageStorage) watchOtherTabs();
4211
+ }
4212
+ let entry = entries.get(key);
4213
+ if (!entry) {
4214
+ let raw = null;
4215
+ try {
4216
+ raw = store.getItem(key);
4217
+ } catch {
4218
+ }
4219
+ entry = { raw, value: void 0, decoded: false, decode: void 0, listeners: /* @__PURE__ */ new Set() };
4220
+ entries.set(key, entry);
4221
+ }
4222
+ return entry;
4223
+ }
4224
+ function valueOf(entry, absent) {
4225
+ if (entry.raw === null) return absent;
4226
+ if (!entry.decoded) {
4227
+ try {
4228
+ const parsed = JSON.parse(entry.raw);
4229
+ entry.value = entry.decode ? entry.decode(parsed) : parsed;
4230
+ } catch {
4231
+ entry.raw = null;
4232
+ return absent;
4233
+ }
4234
+ entry.decoded = true;
4235
+ }
4236
+ return entry.value;
4237
+ }
4238
+ function notify(entry) {
4239
+ for (const listener of [...entry.listeners]) listener();
4240
+ }
4241
+ function invalidate(entry, raw) {
4242
+ if (entry.raw === raw && entry.decoded) return;
4243
+ entry.raw = raw;
4244
+ entry.decoded = false;
4245
+ notify(entry);
4246
+ }
4247
+ function readPageStorage() {
4248
+ try {
4249
+ const storage = globalThis.localStorage;
4250
+ return storage && typeof storage.getItem === "function" ? storage : null;
4251
+ } catch {
4252
+ return null;
4253
+ }
4254
+ }
4255
+ const memory = /* @__PURE__ */ new Map();
4256
+ const memoryStorage = {
4257
+ getItem: (key) => memory.get(key) ?? null,
4258
+ setItem: (key, value) => void memory.set(key, value),
4259
+ removeItem: (key) => void memory.delete(key)
4260
+ };
4261
+ const pageStorage = readPageStorage();
4262
+ const appStorage = pageStorage ?? memoryStorage;
4263
+ let watching = false;
4264
+ function watchOtherTabs() {
4265
+ if (watching || !pageStorage) return;
4266
+ watching = true;
4267
+ const target = globalThis;
4268
+ if (typeof target.addEventListener !== "function") return;
4269
+ target.addEventListener("storage", (event) => {
4270
+ if (event.storageArea !== pageStorage) return;
4271
+ const entries = stores.get(pageStorage);
4272
+ if (!entries) return;
4273
+ if (event.key === null) {
4274
+ for (const entry of entries.values()) invalidate(entry, null);
4275
+ } else {
4276
+ const entry = entries.get(event.key);
4277
+ if (entry) invalidate(entry, event.newValue);
4278
+ }
4279
+ });
4280
+ }
4281
+ function useAppStorage(key, defaultValue, options = {}) {
4282
+ const { store = appStorage, decode, onError } = options;
4283
+ const [fallback] = useState(defaultValue);
4284
+ const entry = entryFor(store, key);
4285
+ entry.decode = decode;
4286
+ const subscribe = useCallback(
4287
+ (listener) => {
4288
+ const subscribed = entryFor(store, key);
4289
+ subscribed.listeners.add(listener);
4290
+ return () => void subscribed.listeners.delete(listener);
4291
+ },
4292
+ [store, key]
4293
+ );
4294
+ const read = useCallback(() => valueOf(entryFor(store, key), fallback), [store, key, fallback]);
4295
+ const value = useSyncExternalStore(subscribe, read, read);
4296
+ const set = useCallback(
4297
+ (next) => {
4298
+ const target = entryFor(store, key);
4299
+ const current = valueOf(target, fallback);
4300
+ const value2 = typeof next === "function" ? next(current) : next;
4301
+ const raw = value2 === void 0 ? null : JSON.stringify(value2);
4302
+ try {
4303
+ if (raw === null) store.removeItem(key);
4304
+ else store.setItem(key, raw);
4305
+ } catch (error) {
4306
+ onError?.(error);
4307
+ }
4308
+ target.raw = raw;
4309
+ target.value = value2;
4310
+ target.decoded = raw !== null;
4311
+ notify(target);
4312
+ },
4313
+ [store, key, fallback, onError]
4314
+ );
4315
+ return [value, set];
4316
+ }
4317
+ const NONE = {
4318
+ documents: "file:///data/Documents",
4319
+ applicationSupport: "file:///data/Library/Application%20Support",
4320
+ caches: "file:///data/Library/Caches",
4321
+ temporary: "file:///tmp"
4322
+ };
4323
+ async function bytesOf(contents) {
4324
+ if (typeof contents === "string") return new TextEncoder().encode(contents);
4325
+ if (contents instanceof Uint8Array) return contents;
4326
+ if (contents instanceof ArrayBuffer) return new Uint8Array(contents);
4327
+ const blob = contents;
4328
+ if (typeof blob.bytes === "function") return await blob.bytes();
4329
+ return new Uint8Array(await blob.arrayBuffer());
4330
+ }
4331
+ function createFileManager(host) {
4332
+ const required = () => {
4333
+ const files = host();
4334
+ if (!files) throw new Error("unsupported: this page keeps no files");
4335
+ return files;
4336
+ };
4337
+ const directories = () => host()?.directories ?? NONE;
4338
+ return {
4339
+ get available() {
4340
+ return host() !== void 0;
4341
+ },
4342
+ get documentsDirectory() {
4343
+ return directories().documents;
4344
+ },
4345
+ get applicationSupportDirectory() {
4346
+ return directories().applicationSupport;
4347
+ },
4348
+ get cachesDirectory() {
4349
+ return directories().caches;
4350
+ },
4351
+ get temporaryDirectory() {
4352
+ return directories().temporary;
4353
+ },
4354
+ url: (directory, path) => fileURL(directory, path),
4355
+ contents: async (url) => required().read(url),
4356
+ text: async (url) => new TextDecoder().decode(await required().read(url)),
4357
+ write: async (url, contents) => required().write(url, await bytesOf(contents)),
4358
+ contentsOfDirectory: async (url) => required().list(url),
4359
+ createDirectory: async (url) => required().createDirectory(url),
4360
+ fileExists: async (url) => await required().attributes(url) !== null,
4361
+ attributesOfItem: async (url) => {
4362
+ const attributes = await required().attributes(url);
4363
+ if (!attributes) throw new Error(`notFound: nothing at ${url}`);
4364
+ return attributes;
4365
+ },
4366
+ moveItem: async (from, to) => required().move(from, to),
4367
+ copyItem: async (from, to) => required().copy(from, to),
4368
+ removeItem: async (url) => required().remove(url),
4369
+ loadURL: async (url) => required().loadURL(url),
4370
+ iCloudDirectory: async () => await host()?.iCloud?.() ?? null,
4371
+ watch: (listener) => host()?.watch?.(listener) ?? (() => {
4372
+ })
4373
+ };
4374
+ }
4375
+ const fileManager = createFileManager(() => browserEnvironment.files);
4376
+ function useFileStorage(url, defaultValue, options = {}) {
4377
+ const manager = options.manager ?? fileManager;
4378
+ const [value, setValue] = useState(defaultValue);
4379
+ const [status, setStatus] = useState(url === null ? "ready" : "loading");
4380
+ const latest = useRef({ value, options, url, defaultValue });
4381
+ latest.current = { value, options, url, defaultValue };
4382
+ const shown = useRef(url);
4383
+ const known = useRef(null);
4384
+ const writing = useRef(Promise.resolve());
4385
+ const load = useCallback(
4386
+ async (target) => {
4387
+ let text;
4388
+ try {
4389
+ text = await manager.text(target);
4390
+ } catch (error) {
4391
+ if (latest.current.url !== target) return;
4392
+ if (error instanceof Error && error.message.startsWith("notFound:")) {
4393
+ known.current = null;
4394
+ setStatus("ready");
4395
+ return;
4396
+ }
4397
+ latest.current.options.onError?.(error);
4398
+ setStatus("error");
4399
+ return;
4400
+ }
4401
+ if (latest.current.url !== target) return;
4402
+ if (text !== known.current) {
4403
+ known.current = text;
4404
+ try {
4405
+ const stored = JSON.parse(text);
4406
+ setValue(latest.current.options.decode ? latest.current.options.decode(stored) : stored);
4407
+ } catch (error) {
4408
+ latest.current.options.onError?.(error);
4409
+ }
4410
+ }
4411
+ setStatus("ready");
4412
+ },
4413
+ [manager]
4414
+ );
4415
+ useEffect(() => {
4416
+ known.current = null;
4417
+ if (shown.current !== url) {
4418
+ shown.current = url;
4419
+ const fresh = latest.current.defaultValue;
4420
+ const value2 = typeof fresh === "function" ? fresh() : fresh;
4421
+ latest.current.value = value2;
4422
+ setValue(value2);
4423
+ }
4424
+ if (url === null) {
4425
+ setStatus("ready");
4426
+ return;
4427
+ }
4428
+ setStatus("loading");
4429
+ void load(url);
4430
+ return manager.watch((urls) => {
4431
+ if (urls.includes(url)) void load(url);
4432
+ });
4433
+ }, [url, load, manager]);
4434
+ const set = useCallback(
4435
+ (next) => {
4436
+ const resolved = typeof next === "function" ? next(latest.current.value) : next;
4437
+ latest.current.value = resolved;
4438
+ setValue(resolved);
4439
+ const target = latest.current.url;
4440
+ if (target === null) return;
4441
+ const text = JSON.stringify(resolved);
4442
+ known.current = text;
4443
+ writing.current = writing.current.then(async () => {
4444
+ try {
4445
+ await manager.write(target, text);
4446
+ } catch (error) {
4447
+ latest.current.options.onError?.(error);
4448
+ }
4449
+ });
4450
+ },
4451
+ [manager]
4452
+ );
4453
+ return [value, set, status];
4454
+ }
4455
+ const NO_CLEAR = () => {
4456
+ };
4457
+ function List(props) {
4458
+ const { own, modifiers } = splitProps(props);
4459
+ const [editing] = own.editing !== void 0 ? readBinding(own.editing) : [false];
4460
+ const listProps = { style: own.listStyle ?? "insetGrouped" };
4461
+ if (editing) listProps["editing"] = true;
4462
+ let selection = null;
4463
+ const split = useContext(SplitSelectionContext);
4464
+ let selected = false;
4465
+ let clearSelection = NO_CLEAR;
4466
+ if (own.selection !== void 0) {
4467
+ const [value, setValue] = readBinding(own.selection, own.onSelectionChange);
4468
+ if (value instanceof Set) listProps["multiSelect"] = true;
4469
+ selected = value instanceof Set ? value.size > 0 : value !== null && value !== void 0;
4470
+ clearSelection = () => setValue(value instanceof Set ? /* @__PURE__ */ new Set() : null);
4471
+ selection = {
4472
+ editing,
4473
+ isSelected: (tag) => value instanceof Set ? value.has(tag) : value !== null && Object.is(value, tag),
4474
+ select: (tag) => {
4475
+ if (value instanceof Set) {
4476
+ const next = new Set(value);
4477
+ if (next.has(tag)) next.delete(tag);
4478
+ else next.add(tag);
4479
+ setValue(next);
4480
+ } else {
4481
+ setValue(editing && Object.is(value, tag) ? null : tag);
4482
+ }
4483
+ }
4484
+ };
4485
+ }
4486
+ useReportSplitSelection(split, selected, clearSelection, own.selection !== void 0);
4487
+ const refresh = useRefreshable(takeModifier(modifiers, "refreshable"));
4488
+ Object.assign(listProps, refresh.props);
4489
+ const ownSettings = takeScrollSettings(modifiers);
4490
+ const edgeEffect = edgeEffectProp(useScrollSettings(ownSettings));
4491
+ if (edgeEffect) listProps["edgeEffect"] = edgeEffect;
4492
+ const element = /* @__PURE__ */ jsx(LazyContext.Provider, { value: "vertical", children: sb("list", listProps, refresh.handlers, providingScrollSettings(ownSettings, wrapRows(own.children))) });
4493
+ return applyModifiers(
4494
+ selection ? /* @__PURE__ */ jsx(ListSelectionContext.Provider, { value: selection, children: element }) : element,
4495
+ modifiers
4496
+ );
4497
+ }
4498
+ function Form(props) {
4499
+ return /* @__PURE__ */ jsx(List, { ...props });
4500
+ }
4501
+ function Section(props) {
4502
+ const { own, modifiers } = splitProps(props);
4503
+ const pinned = useContext(PinnedContext);
4504
+ let header = supplement(own.header);
4505
+ let footer = supplement(own.footer);
4506
+ if (pinned?.includes("sectionHeaders") && header.text !== null && !header.view) header = { text: null, view: /* @__PURE__ */ jsx(Text, { children: header.text }) };
4507
+ if (pinned?.includes("sectionFooters") && footer.text !== null && !footer.view) footer = { text: null, view: /* @__PURE__ */ jsx(Text, { children: footer.text }) };
4508
+ const children = [];
4509
+ if (header.view) children.push(sb("sectionheader", {}, void 0, header.view));
4510
+ if (own.children !== void 0) children.push(wrapRows(own.children));
4511
+ if (footer.view) children.push(sb("sectionfooter", {}, void 0, footer.view));
4512
+ return applyModifiers(sb("section", { header: header.text, footer: footer.text }, void 0, children), modifiers);
4513
+ }
4514
+ function wrapRows(children) {
4515
+ let offset = 0;
4516
+ return flattenChildren(children, { keepLazy: true }).map((element, index) => {
4517
+ const at = index + offset;
4518
+ if (element.type === LazyGroup) {
4519
+ const group = element.props;
4520
+ offset += group.rows.length - 1;
4521
+ return { ...withKey(element, index), props: { ...group, start: at, wrap: wrapRow } };
4522
+ }
4523
+ return wrapRow(withKey(element, index), at);
4524
+ });
4525
+ }
4526
+ function wrapRow(row, containerIndex) {
4527
+ const inner = row.type === Fragment ? flattenChildren(row) : null;
4528
+ const element = inner?.length === 1 ? { ...inner[0], key: row.key } : row;
4529
+ if (element.type === Section) return element;
4530
+ if (element.type === Row) return { ...element, props: { ...element.props, containerIndex } };
4531
+ const props = element.props;
4532
+ const tag = props.tag !== void 0 ? props.tag : element.type === NavigationLink ? props.value : void 0;
4533
+ if (tag === void 0) return element;
4534
+ return /* @__PURE__ */ jsx(Row, { tag, containerIndex, children: element }, element.key ?? containerIndex);
4535
+ }
4536
+ function withKey(element, index) {
4537
+ return element.key !== null ? element : { ...element, key: String(index) };
4538
+ }
4539
+ function supplement(node) {
4540
+ if (node === void 0 || node === null || node === false) return { text: null, view: null };
4541
+ if (typeof node === "string" || typeof node === "number") return { text: String(node), view: null };
4542
+ if (isValidElement(node) && node.type === Text) {
4543
+ const props = node.props;
4544
+ const only = Object.keys(props).every((key) => key === "children");
4545
+ if (only) return { text: plainText(node), view: null };
4546
+ }
4547
+ return { text: null, view: isValidElement(node) ? node : null };
4548
+ }
4549
+ function TabView(props) {
4550
+ const { own, modifiers } = splitProps(props);
4551
+ const tabs = flattenChildren(own.children);
4552
+ const tags = tabs.map((tab, index) => {
4553
+ const tag = tab.props.tag;
4554
+ return tag !== void 0 ? tag : index;
4555
+ });
4556
+ const [internal, setInternal] = useState(tags[0]);
4557
+ const [selection, setSelection] = own.selection !== void 0 ? readBinding(own.selection, own.onChange) : [internal, setInternal];
4558
+ const selected = Math.max(
4559
+ 0,
4560
+ tags.findIndex((tag) => Object.is(tag, selection))
4561
+ );
4562
+ return applyModifiers(
4563
+ sb("tabview", { selected }, { select: (event) => tags[event.value] !== void 0 && setSelection(tags[event.value]) }, tabs),
4564
+ modifiers
4565
+ );
4566
+ }
4567
+ function Tab(props) {
4568
+ const { own, modifiers } = splitProps(props);
4569
+ const badge = takeModifier(modifiers, "badge");
4570
+ const tabProps = { title: own.title, systemImage: own.systemImage ?? null };
4571
+ if (badge !== void 0 && badge !== null) tabProps["badge"] = String(badge);
4572
+ if (own.role) tabProps["role"] = own.role;
4573
+ if (own.image && !own.systemImage) tabProps["image"] = own.image;
4574
+ return applyModifiers(sb("tab", tabProps, void 0, own.children), modifiers);
4575
+ }
4157
4576
  function Host(props) {
4158
4577
  const { own, modifiers } = splitProps(props);
4159
4578
  const key = useId();
@@ -5579,6 +5998,7 @@ export {
5579
5998
  SecureField,
5580
5999
  Shape,
5581
6000
  ShareLink,
6001
+ SignInWithAppleButton,
5582
6002
  Slider,
5583
6003
  Spacer,
5584
6004
  SplitSelectionContext,
@@ -5605,10 +6025,14 @@ export {
5605
6025
  ZStack,
5606
6026
  answerLayout,
5607
6027
  appStorage,
6028
+ appleCredentialState,
5608
6029
  canMeasureText,
6030
+ canSignInWithApple,
5609
6031
  color,
6032
+ configureSignInWithApple,
5610
6033
  createFileManager,
5611
6034
  createSwiftUIRoot,
6035
+ exportFile,
5612
6036
  fileManager,
5613
6037
  flattenChildren,
5614
6038
  focusBinding,
@@ -5636,6 +6060,7 @@ export {
5636
6060
  rgb,
5637
6061
  scrollGeometryOf,
5638
6062
  shapeStyle,
6063
+ signInWithApple,
5639
6064
  system,
5640
6065
  takeScrollSettings,
5641
6066
  timeTicks,
@@ -5648,14 +6073,20 @@ export {
5648
6073
  useCoordinateSpaces,
5649
6074
  useDismiss,
5650
6075
  useDynamicTypeSize,
6076
+ useFileStorage,
6077
+ useGeolocation,
5651
6078
  useHorizontalSizeClass,
5652
6079
  useNamespace,
5653
6080
  useNavigation,
5654
6081
  useRedacted,
5655
6082
  useRefreshable,
5656
6083
  useReportSplitSelection,
6084
+ useScenePhase,
6085
+ useSpeechRecognition,
6086
+ useSpeechSynthesis,
5657
6087
  useUndoManager,
5658
6088
  useViewEnvironment,
6089
+ useWakeLock,
5659
6090
  weighted,
5660
6091
  withAnimation
5661
6092
  };