@swiftbrowser/react 0.18.2 → 0.19.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/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { registerHostContent, notifyHostContentChange, unregisterHostContent } f
15
15
  import { createRoot } from "react-dom/client";
16
16
  import { flushSync } from "react-dom";
17
17
  import { chooseColor } from "@swiftbrowser/web/src/colorPicker";
18
+ import { base64Encode } from "@swiftbrowser/web/src/base64";
18
19
  import { timingFor } from "@swiftbrowser/web/src/animation";
19
20
  const ViewEnvironmentContext = createContext({});
20
21
  function useViewEnvironment() {
@@ -1433,7 +1434,8 @@ function ScrollView(props) {
1433
1434
  const scroll = { axes, ...refresh.props, ...geometry.props };
1434
1435
  const handlers = refresh.handlers || geometry.handlers ? { ...refresh.handlers, ...geometry.handlers } : void 0;
1435
1436
  if (own.scrollTargetBehavior) scroll["snap"] = own.scrollTargetBehavior;
1436
- if (own.scrollDismissesKeyboard) scroll["dismissesKeyboard"] = true;
1437
+ if (own.scrollDismissesKeyboard === true || own.scrollDismissesKeyboard === "immediately") scroll["dismissesKeyboard"] = true;
1438
+ else if (own.scrollDismissesKeyboard === "interactively") scroll["dismissesKeyboard"] = "interactively";
1437
1439
  if (own.scrollIndicators === "hidden") scroll["showsIndicators"] = false;
1438
1440
  if (own.defaultScrollAnchor !== void 0) scroll["defaultScrollAnchor"] = unitPoint(own.defaultScrollAnchor);
1439
1441
  const edgeEffect = edgeEffectProp(settings);
@@ -1717,7 +1719,7 @@ function Label(props) {
1717
1719
  if (style === "titleOnly") return applyModifiers(title, modifiers);
1718
1720
  return applyModifiers(
1719
1721
  /* @__PURE__ */ jsxs(HStack, { spacing: 8, children: [
1720
- styled({ labelIcon: true }, icon),
1722
+ style === "titleAndIcon" ? icon : styled({ labelIcon: true }, icon),
1721
1723
  title
1722
1724
  ] }),
1723
1725
  modifiers
@@ -2185,6 +2187,8 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
2185
2187
  "navigationTitle",
2186
2188
  "navigationBarTitleDisplayMode",
2187
2189
  "toolbarRole",
2190
+ "toolbarVisibility",
2191
+ "statusBarHidden",
2188
2192
  "toolbar",
2189
2193
  "searchable",
2190
2194
  "navigationDestination",
@@ -2259,6 +2263,7 @@ const MODIFIER_KEYS = /* @__PURE__ */ new Set([
2259
2263
  "onKeyPress",
2260
2264
  "sensoryFeedback",
2261
2265
  "textCase",
2266
+ "textSelection",
2262
2267
  "coordinateSpace",
2263
2268
  "contentTransition",
2264
2269
  "scrollDisabled",
@@ -2446,7 +2451,8 @@ const STYLED_KEYS = /* @__PURE__ */ new Set([
2446
2451
  "matchedGeometryEffect",
2447
2452
  "containerRelativeFrame",
2448
2453
  "contentTransition",
2449
- "scrollDisabled"
2454
+ "scrollDisabled",
2455
+ "textSelection"
2450
2456
  ]);
2451
2457
  const ENVIRONMENT_KEYS = /* @__PURE__ */ new Set([
2452
2458
  "buttonStyle",
@@ -2598,6 +2604,17 @@ function apply(key, value, node, sides) {
2598
2604
  case "toolbarRole":
2599
2605
  sides.push(sb(PREF_KIND, { toolbarRole: value }, void 0, void 0, "navscreen"));
2600
2606
  return node;
2607
+ case "toolbarVisibility": {
2608
+ const bars = value;
2609
+ const props = {};
2610
+ if (bars.navigationBar && bars.navigationBar !== "automatic") props["navigationBarHidden"] = bars.navigationBar === "hidden";
2611
+ if (bars.bottomBar && bars.bottomBar !== "automatic") props["bottomBarHidden"] = bars.bottomBar === "hidden";
2612
+ sides.push(sb(PREF_KIND, props, void 0, void 0, "navscreen"));
2613
+ return node;
2614
+ }
2615
+ case "statusBarHidden":
2616
+ sides.push(sb(PREF_KIND, { statusBarHidden: Boolean(value) }, void 0, void 0, "navscreen"));
2617
+ return node;
2601
2618
  case "toolbar":
2602
2619
  sides.push(sb(HOIST_KIND, void 0, void 0, value, "navscreen"));
2603
2620
  return node;
@@ -2631,8 +2648,8 @@ function apply(key, value, node, sides) {
2631
2648
  const count = Math.floor(spec.count ?? 1);
2632
2649
  const props = count > 1 ? { count } : {};
2633
2650
  if ("perform" in spec) {
2634
- const { perform } = spec;
2635
- return sb("tapgesture", { ...props, location: true }, { tap: (event) => perform({ x: event.x ?? 0, y: event.y ?? 0 }) }, node);
2651
+ const { perform: perform2 } = spec;
2652
+ return sb("tapgesture", { ...props, location: true }, { tap: (event) => perform2({ x: event.x ?? 0, y: event.y ?? 0 }) }, node);
2636
2653
  }
2637
2654
  return sb("tapgesture", props, { tap: () => spec.action() }, node);
2638
2655
  }
@@ -2852,6 +2869,8 @@ function apply(key, value, node, sides) {
2852
2869
  }
2853
2870
  case "scrollDisabled":
2854
2871
  return styled({ scrollDisabled: value === true }, node);
2872
+ case "textSelection":
2873
+ return styled({ textSelection: value === "enabled" }, node);
2855
2874
  case "contentTransition": {
2856
2875
  if (value === "identity") return node;
2857
2876
  if (typeof value === "object") {
@@ -3574,6 +3593,848 @@ function useAudioRecorder(options = {}) {
3574
3593
  );
3575
3594
  return { available: host !== void 0, status, time, levels, error, record, pause, resume, stop, cancel, discard, trim };
3576
3595
  }
3596
+ let nextId$1 = 1;
3597
+ const newId = () => nextId$1++;
3598
+ const hostNow$1 = () => browserEnvironment.audioEngine;
3599
+ function unsupported() {
3600
+ return new Error("unsupported: this page or app has no audio engine");
3601
+ }
3602
+ function perform$1(command2) {
3603
+ const host = hostNow$1();
3604
+ if (!host) return Promise.reject(unsupported());
3605
+ return host.perform(command2);
3606
+ }
3607
+ class AVAudioTime {
3608
+ spec;
3609
+ constructor(sampleTime, atRate) {
3610
+ this.spec = { sampleTime: Math.round(sampleTime), sampleRate: atRate };
3611
+ }
3612
+ static secondsFromNow(seconds) {
3613
+ const time = Object.create(AVAudioTime.prototype);
3614
+ time.spec = { delay: Math.max(0, seconds) };
3615
+ return time;
3616
+ }
3617
+ get isSampleTimeValid() {
3618
+ return "sampleTime" in this.spec;
3619
+ }
3620
+ get sampleTime() {
3621
+ return "sampleTime" in this.spec ? this.spec.sampleTime : 0;
3622
+ }
3623
+ get sampleRate() {
3624
+ return "sampleTime" in this.spec ? this.spec.sampleRate : 0;
3625
+ }
3626
+ /** The time in seconds: a sample time's place on its timeline, or the delay. */
3627
+ get seconds() {
3628
+ return "sampleTime" in this.spec ? this.spec.sampleTime / this.spec.sampleRate : this.spec.delay;
3629
+ }
3630
+ }
3631
+ const released = typeof FinalizationRegistry !== "undefined" ? new FinalizationRegistry((id) => void perform$1({ action: "release", id }).catch(() => void 0)) : null;
3632
+ class AVAudioFile {
3633
+ constructor(id, url, length, processingFormat) {
3634
+ this.id = id;
3635
+ this.url = url;
3636
+ this.length = length;
3637
+ this.processingFormat = processingFormat;
3638
+ released?.register(this, id, this);
3639
+ }
3640
+ id;
3641
+ url;
3642
+ length;
3643
+ processingFormat;
3644
+ /** Where the next `read` starts, in frames. */
3645
+ framePosition = 0;
3646
+ /** Opens a file for reading; rejects with `notFound: …` or `decode: …`. */
3647
+ static async open(url) {
3648
+ const id = newId();
3649
+ const info = await perform$1({ action: "openFile", file: id, url });
3650
+ return new AVAudioFile(id, url, info.length, { sampleRate: info.sampleRate, channelCount: info.channelCount });
3651
+ }
3652
+ /** Seconds. */
3653
+ get duration() {
3654
+ return this.length / this.processingFormat.sampleRate;
3655
+ }
3656
+ /** Reads frames from `framePosition` into `buffer` (up to its capacity, or `frameCount`) and moves past them. */
3657
+ async read(into, frameCount) {
3658
+ const frames = Math.max(0, Math.min(frameCount ?? into.frameCapacity, into.frameCapacity, this.length - this.framePosition));
3659
+ const info = await perform$1({ action: "readFile", file: this.id, buffer: into.id, start: this.framePosition, frames });
3660
+ into.frameLength = info.frameLength;
3661
+ into.format = { sampleRate: info.sampleRate, channelCount: info.channelCount };
3662
+ this.framePosition += info.frameLength;
3663
+ }
3664
+ /** Lets the host free the decoded audio (what a scheduled segment still plays keeps playing). */
3665
+ close() {
3666
+ released?.unregister(this);
3667
+ void perform$1({ action: "release", id: this.id }).catch(() => void 0);
3668
+ }
3669
+ }
3670
+ class AVAudioPCMBuffer {
3671
+ constructor(format, frameCapacity) {
3672
+ this.format = format;
3673
+ this.frameCapacity = frameCapacity;
3674
+ released?.register(this, this.id, this);
3675
+ }
3676
+ format;
3677
+ frameCapacity;
3678
+ id = newId();
3679
+ frameLength = 0;
3680
+ /** Seconds of audio it holds. */
3681
+ get duration() {
3682
+ return this.frameLength / this.format.sampleRate;
3683
+ }
3684
+ /** Fills the buffer with the app's samples, one array per channel (up to its capacity). */
3685
+ async write(channels) {
3686
+ const frames = Math.min(this.frameCapacity, ...channels.map((data) => data.length));
3687
+ await perform$1({
3688
+ action: "writeBuffer",
3689
+ buffer: this.id,
3690
+ sampleRate: this.format.sampleRate,
3691
+ channels: channels.map((data) => data.length === frames ? data : data.subarray(0, frames))
3692
+ });
3693
+ this.frameLength = frames;
3694
+ this.format = { sampleRate: this.format.sampleRate, channelCount: channels.length };
3695
+ }
3696
+ /** Lets the host free the audio. */
3697
+ close() {
3698
+ released?.unregister(this);
3699
+ void perform$1({ action: "release", id: this.id }).catch(() => void 0);
3700
+ }
3701
+ }
3702
+ class AVAudioNode {
3703
+ id = newId();
3704
+ /** The engine it is attached to, or null. */
3705
+ engine = null;
3706
+ values = {};
3707
+ get(key, fallback) {
3708
+ return this.values[key] ?? fallback;
3709
+ }
3710
+ set(key, value) {
3711
+ this.values[key] = value;
3712
+ this.engine?.fire({ action: "set", node: this.id, values: { [key]: value } });
3713
+ }
3714
+ /** The properties to attach it with. */
3715
+ attachValues() {
3716
+ return { ...this.values };
3717
+ }
3718
+ /** How many EQ bands it has (only an EQ has any). */
3719
+ get bandCount() {
3720
+ return void 0;
3721
+ }
3722
+ /** What its engine must be for a call to reach it. */
3723
+ attached() {
3724
+ if (!this.engine) throw new Error("notAttached: attach the node to an engine first");
3725
+ return this.engine;
3726
+ }
3727
+ /** Its output's level now (0 silent … 1 at 0 dB, from -50 dB), as a tap on it would measure; 0 when not attached. */
3728
+ async meter() {
3729
+ if (!this.engine) return 0;
3730
+ const result = await this.engine.send({ action: "meter", node: this.id });
3731
+ return typeof result?.level === "number" ? result.level : 0;
3732
+ }
3733
+ }
3734
+ class MixingNode extends AVAudioNode {
3735
+ get volume() {
3736
+ return this.get("volume", 1);
3737
+ }
3738
+ set volume(value) {
3739
+ this.set("volume", value);
3740
+ }
3741
+ get pan() {
3742
+ return this.get("pan", 0);
3743
+ }
3744
+ set pan(value) {
3745
+ this.set("pan", value);
3746
+ }
3747
+ }
3748
+ class AVAudioMixerNode extends MixingNode {
3749
+ kind = "mixer";
3750
+ get outputVolume() {
3751
+ return this.get("outputVolume", 1);
3752
+ }
3753
+ set outputVolume(value) {
3754
+ this.set("outputVolume", value);
3755
+ }
3756
+ }
3757
+ class AVAudioOutputNode extends AVAudioNode {
3758
+ kind = null;
3759
+ }
3760
+ class AVAudioInputNode extends MixingNode {
3761
+ kind = null;
3762
+ }
3763
+ class AVAudioPlayerNode extends MixingNode {
3764
+ kind = "player";
3765
+ playing = false;
3766
+ get isPlaying() {
3767
+ return this.playing && this.engine?.isRunning === true;
3768
+ }
3769
+ scheduleFile(file, at = null, completionHandler) {
3770
+ return this.schedule({ action: "scheduleFile", node: this.id, file: file.id, ...at ? { at: at.spec } : {} }, completionHandler);
3771
+ }
3772
+ scheduleSegment(file, startingFrame, frameCount, at = null, completionHandler) {
3773
+ return this.schedule(
3774
+ {
3775
+ action: "scheduleFile",
3776
+ node: this.id,
3777
+ file: file.id,
3778
+ start: Math.max(0, Math.floor(startingFrame)),
3779
+ frames: Math.max(0, Math.floor(frameCount)),
3780
+ ...at ? { at: at.spec } : {}
3781
+ },
3782
+ completionHandler
3783
+ );
3784
+ }
3785
+ scheduleBuffer(buffer, at = null, options = {}, completionHandler) {
3786
+ const command2 = {
3787
+ action: "scheduleBuffer",
3788
+ node: this.id,
3789
+ buffer: buffer.id,
3790
+ ...at ? { at: at.spec } : {}
3791
+ };
3792
+ if (options.loops) command2.loops = true;
3793
+ if (options.interruptsAtLoop) command2.interruptsAtLoop = true;
3794
+ else if (options.interrupts) command2.interrupts = true;
3795
+ return this.schedule(command2, completionHandler);
3796
+ }
3797
+ schedule(command2, completionHandler) {
3798
+ const engine = this.attached();
3799
+ return engine.send(command2).then(() => completionHandler?.());
3800
+ }
3801
+ /** Starts (or resumes) the timeline, now or at `at`; the engine must be running. */
3802
+ play(at = null) {
3803
+ const engine = this.attached();
3804
+ this.playing = true;
3805
+ engine.fire({ action: "play", node: this.id, ...at ? { at: at.spec } : {} });
3806
+ }
3807
+ /** Holds the timeline where it is; `play()` goes on from there. */
3808
+ pause() {
3809
+ this.playing = false;
3810
+ this.engine?.fire({ action: "pausePlayer", node: this.id });
3811
+ }
3812
+ /** Drops everything scheduled (each completion called) and returns the timeline to 0. */
3813
+ stop() {
3814
+ this.playing = false;
3815
+ this.engine?.fire({ action: "stopPlayer", node: this.id });
3816
+ }
3817
+ /** Where the timeline is now (`playerTime(forNodeTime: lastRenderTime)`), or null before it plays. */
3818
+ async playerTime() {
3819
+ const time = await this.attached().send({ action: "playerTime", node: this.id });
3820
+ return time && typeof time.sampleTime === "number" ? new AVAudioTime(time.sampleTime, time.sampleRate) : null;
3821
+ }
3822
+ /** Called by the engine when it stops: the player stops with it. */
3823
+ engineStopped() {
3824
+ this.playing = false;
3825
+ }
3826
+ }
3827
+ const NOTE_CLASSES = { c: 0, d: 2, e: 4, f: 5, g: 7, a: 9, b: 11 };
3828
+ function noteInName(url) {
3829
+ const name = decodeURIComponent(url.split(/[/\\]/).pop() ?? "").replace(/\.[^.]*$/, "");
3830
+ const named = [...name.matchAll(/(?:^|[^A-Za-z])([A-Ga-g])([#♯]|b|♭)?(-?\d)(?!\d)/g)].pop();
3831
+ if (named) {
3832
+ const pitchClass = NOTE_CLASSES[named[1].toLowerCase()];
3833
+ const accidental = named[2] === "#" || named[2] === "♯" ? 1 : named[2] ? -1 : 0;
3834
+ const note = (Number(named[3]) + 1) * 12 + pitchClass + accidental;
3835
+ return note >= 0 && note <= 127 ? note : null;
3836
+ }
3837
+ const numbered = [...name.matchAll(/(?:^|\D)(\d{1,3})(?!\d)/g)].pop();
3838
+ if (numbered) {
3839
+ const note = Number(numbered[1]);
3840
+ return note <= 127 ? note : null;
3841
+ }
3842
+ return null;
3843
+ }
3844
+ function samplerZones(samples) {
3845
+ const sorted = [...samples].sort((a, b) => a.root - b.root).filter((sample, i, all) => i === 0 || sample.root !== all[i - 1].root);
3846
+ return sorted.map((sample, i) => {
3847
+ const below = sorted[i - 1];
3848
+ const above = sorted[i + 1];
3849
+ return {
3850
+ file: sample.file,
3851
+ root: sample.root,
3852
+ low: below ? Math.floor((below.root + sample.root) / 2) + 1 : 0,
3853
+ high: above ? Math.floor((sample.root + above.root) / 2) : 127
3854
+ };
3855
+ });
3856
+ }
3857
+ class AVAudioUnitSampler extends MixingNode {
3858
+ kind = "sampler";
3859
+ files = [];
3860
+ /** Decibels, -90 … 12. */
3861
+ get overallGain() {
3862
+ return this.get("overallGain", 0);
3863
+ }
3864
+ set overallGain(value) {
3865
+ this.set("overallGain", value);
3866
+ }
3867
+ /** Cents, -2400 … 2400. */
3868
+ get globalTuning() {
3869
+ return this.get("globalTuning", 0);
3870
+ }
3871
+ set globalTuning(value) {
3872
+ this.set("globalTuning", value);
3873
+ }
3874
+ /** -100 left … 100 right. */
3875
+ get stereoPan() {
3876
+ return this.get("stereoPan", 0);
3877
+ }
3878
+ set stereoPan(value) {
3879
+ this.set("stereoPan", value);
3880
+ }
3881
+ async loadAudioFiles(urls, options = {}) {
3882
+ const engine = this.attached();
3883
+ const roots2 = urls.map((url, i) => options.rootNotes?.[i] ?? noteInName(url) ?? 60);
3884
+ const files = await Promise.all(urls.map((url) => AVAudioFile.open(url)));
3885
+ const zones = samplerZones(files.map((file, i) => ({ file: file.id, root: roots2[i] })));
3886
+ await engine.send({ action: "loadZones", node: this.id, zones, attack: options.attack ?? 2e-3, release: options.release ?? 0.15 });
3887
+ const previous = this.files;
3888
+ this.files = files;
3889
+ for (const file of previous) file.close();
3890
+ }
3891
+ /** Loads an `.aupreset`, a DLS or a SoundFont's first instrument (natively; a page rejects with `unsupported: …`). */
3892
+ async loadInstrument(url) {
3893
+ await this.attached().send({ action: "loadInstrument", node: this.id, url });
3894
+ }
3895
+ /** Loads one instrument of a SoundFont or DLS bank by program and bank (natively; a page rejects with `unsupported: …`). */
3896
+ async loadSoundBankInstrument(url, program, bankMSB = 121, bankLSB = 0) {
3897
+ await this.attached().send({ action: "loadInstrument", node: this.id, url, program, bankMSB, bankLSB });
3898
+ }
3899
+ startNote(note, withVelocity, onChannel = 0) {
3900
+ this.attached().fire({ action: "startNote", node: this.id, note: Math.round(note), velocity: Math.round(withVelocity), channel: onChannel });
3901
+ }
3902
+ stopNote(note, onChannel = 0) {
3903
+ this.attached().fire({ action: "stopNote", node: this.id, note: Math.round(note), channel: onChannel });
3904
+ }
3905
+ /** A MIDI controller: 64 the sustain pedal, 7 volume, 11 expression, 10 pan, 123 all notes off. */
3906
+ sendController(controller, withValue, onChannel = 0) {
3907
+ this.attached().fire({ action: "controller", node: this.id, controller, value: Math.round(withValue), channel: onChannel });
3908
+ }
3909
+ /** The pitch wheel: 0 … 16383, 8192 centered (±2 semitones). */
3910
+ sendPitchBend(pitchbend, onChannel = 0) {
3911
+ this.attached().fire({ action: "pitchBend", node: this.id, value: Math.round(pitchbend), channel: onChannel });
3912
+ }
3913
+ }
3914
+ class AVAudioUnitEffect extends AVAudioNode {
3915
+ get bypass() {
3916
+ return this.get("bypass", false);
3917
+ }
3918
+ set bypass(value) {
3919
+ this.set("bypass", value);
3920
+ }
3921
+ }
3922
+ class AVAudioUnitReverb extends AVAudioUnitEffect {
3923
+ kind = "reverb";
3924
+ constructor() {
3925
+ super();
3926
+ this.values["preset"] = "mediumHall";
3927
+ this.values["wetDryMix"] = 100;
3928
+ }
3929
+ get wetDryMix() {
3930
+ return this.get("wetDryMix", 100);
3931
+ }
3932
+ set wetDryMix(value) {
3933
+ this.set("wetDryMix", value);
3934
+ }
3935
+ loadFactoryPreset(preset) {
3936
+ this.values["preset"] = preset;
3937
+ this.engine?.fire({ action: "preset", node: this.id, preset });
3938
+ }
3939
+ }
3940
+ class AVAudioUnitDelay extends AVAudioUnitEffect {
3941
+ kind = "delay";
3942
+ constructor() {
3943
+ super();
3944
+ Object.assign(this.values, { delayTime: 1, feedback: 50, lowPassCutoff: 15e3, wetDryMix: 100 });
3945
+ }
3946
+ get delayTime() {
3947
+ return this.get("delayTime", 1);
3948
+ }
3949
+ set delayTime(value) {
3950
+ this.set("delayTime", value);
3951
+ }
3952
+ get feedback() {
3953
+ return this.get("feedback", 50);
3954
+ }
3955
+ set feedback(value) {
3956
+ this.set("feedback", value);
3957
+ }
3958
+ get lowPassCutoff() {
3959
+ return this.get("lowPassCutoff", 15e3);
3960
+ }
3961
+ set lowPassCutoff(value) {
3962
+ this.set("lowPassCutoff", value);
3963
+ }
3964
+ get wetDryMix() {
3965
+ return this.get("wetDryMix", 100);
3966
+ }
3967
+ set wetDryMix(value) {
3968
+ this.set("wetDryMix", value);
3969
+ }
3970
+ }
3971
+ class AVAudioUnitDistortion extends AVAudioUnitEffect {
3972
+ kind = "distortion";
3973
+ constructor() {
3974
+ super();
3975
+ Object.assign(this.values, { preGain: -6, wetDryMix: 50 });
3976
+ }
3977
+ get preGain() {
3978
+ return this.get("preGain", -6);
3979
+ }
3980
+ set preGain(value) {
3981
+ this.set("preGain", value);
3982
+ }
3983
+ get wetDryMix() {
3984
+ return this.get("wetDryMix", 50);
3985
+ }
3986
+ set wetDryMix(value) {
3987
+ this.set("wetDryMix", value);
3988
+ }
3989
+ loadFactoryPreset(preset) {
3990
+ this.values["preset"] = preset;
3991
+ this.engine?.fire({ action: "preset", node: this.id, preset });
3992
+ }
3993
+ }
3994
+ class AVAudioUnitEQFilterParameters {
3995
+ constructor(eq, index) {
3996
+ this.eq = eq;
3997
+ this.index = index;
3998
+ }
3999
+ eq;
4000
+ index;
4001
+ key(field) {
4002
+ return `bands.${this.index}.${field}`;
4003
+ }
4004
+ get filterType() {
4005
+ return this.eq.band(this.key("filterType"), "parametric");
4006
+ }
4007
+ set filterType(value) {
4008
+ this.eq.setBand(this.key("filterType"), value);
4009
+ }
4010
+ /** Hz. */
4011
+ get frequency() {
4012
+ return this.eq.band(this.key("frequency"), 1e3);
4013
+ }
4014
+ set frequency(value) {
4015
+ this.eq.setBand(this.key("frequency"), value);
4016
+ }
4017
+ /** Octaves (0.05 … 5). */
4018
+ get bandwidth() {
4019
+ return this.eq.band(this.key("bandwidth"), 0.5);
4020
+ }
4021
+ set bandwidth(value) {
4022
+ this.eq.setBand(this.key("bandwidth"), value);
4023
+ }
4024
+ /** Decibels (-96 … 24). */
4025
+ get gain() {
4026
+ return this.eq.band(this.key("gain"), 0);
4027
+ }
4028
+ set gain(value) {
4029
+ this.eq.setBand(this.key("gain"), value);
4030
+ }
4031
+ get bypass() {
4032
+ return this.eq.band(this.key("bypass"), true);
4033
+ }
4034
+ set bypass(value) {
4035
+ this.eq.setBand(this.key("bypass"), value);
4036
+ }
4037
+ }
4038
+ class AVAudioUnitEQ extends AVAudioUnitEffect {
4039
+ kind = "eq";
4040
+ bands;
4041
+ constructor(numberOfBands = 1) {
4042
+ super();
4043
+ const count = Math.max(1, Math.min(32, Math.floor(numberOfBands)));
4044
+ this.bands = Array.from({ length: count }, (_, i) => new AVAudioUnitEQFilterParameters(this, i));
4045
+ for (let i = 0; i < count; i += 1) {
4046
+ Object.assign(this.values, {
4047
+ [`bands.${i}.filterType`]: "parametric",
4048
+ [`bands.${i}.frequency`]: 1e3,
4049
+ [`bands.${i}.bandwidth`]: 0.5,
4050
+ [`bands.${i}.gain`]: 0,
4051
+ [`bands.${i}.bypass`]: true
4052
+ });
4053
+ }
4054
+ }
4055
+ get bandCount() {
4056
+ return this.bands.length;
4057
+ }
4058
+ get globalGain() {
4059
+ return this.get("globalGain", 0);
4060
+ }
4061
+ set globalGain(value) {
4062
+ this.set("globalGain", value);
4063
+ }
4064
+ /** A band's field (for `AVAudioUnitEQFilterParameters`). */
4065
+ band(key, fallback) {
4066
+ return this.values[key] ?? fallback;
4067
+ }
4068
+ setBand(key, value) {
4069
+ this.set(key, value);
4070
+ }
4071
+ }
4072
+ class AVAudioUnitTimePitch extends AVAudioUnitEffect {
4073
+ kind = "timePitch";
4074
+ get pitch() {
4075
+ return this.get("pitch", 0);
4076
+ }
4077
+ set pitch(value) {
4078
+ this.set("pitch", value);
4079
+ }
4080
+ get rate() {
4081
+ return this.get("rate", 1);
4082
+ }
4083
+ set rate(value) {
4084
+ this.set("rate", value);
4085
+ }
4086
+ get overlap() {
4087
+ return this.get("overlap", 8);
4088
+ }
4089
+ set overlap(value) {
4090
+ this.set("overlap", value);
4091
+ }
4092
+ }
4093
+ const engines = typeof FinalizationRegistry !== "undefined" ? new FinalizationRegistry((id) => void perform$1({ action: "destroy", engine: id }).catch(() => void 0)) : null;
4094
+ class AVAudioEngine {
4095
+ id = newId();
4096
+ mainMixerNode = new AVAudioMixerNode();
4097
+ outputNode = new AVAudioOutputNode();
4098
+ inputNode = new AVAudioInputNode();
4099
+ attachedNodes = /* @__PURE__ */ new Set();
4100
+ /** Where failures of calls that answer nothing go (a console warning by default). */
4101
+ onError = (error) => console.warn(`AVAudioEngine: ${error.message}`);
4102
+ running = false;
4103
+ disposed = false;
4104
+ /** Whether this page or app has an audio engine. */
4105
+ static get isAvailable() {
4106
+ return hostNow$1() !== void 0;
4107
+ }
4108
+ constructor() {
4109
+ for (const node of [this.mainMixerNode, this.outputNode, this.inputNode]) {
4110
+ node.engine = this;
4111
+ this.attachedNodes.add(node);
4112
+ }
4113
+ this.fire({ action: "create", engine: this.id, mixer: this.mainMixerNode.id, output: this.outputNode.id, input: this.inputNode.id });
4114
+ engines?.register(this, this.id, this);
4115
+ }
4116
+ get isRunning() {
4117
+ return this.running;
4118
+ }
4119
+ /** Sends a command and answers its result. */
4120
+ send(command2) {
4121
+ if (this.disposed) return Promise.reject(new Error("disposed: the engine was disposed"));
4122
+ return perform$1(command2);
4123
+ }
4124
+ /** Sends a command that answers nothing; a failure goes to `onError`. */
4125
+ fire(command2) {
4126
+ this.send(command2).catch((error) => this.onError(error instanceof Error ? error : new Error(String(error))));
4127
+ }
4128
+ attach(node) {
4129
+ if (node.engine === this) return;
4130
+ if (node.engine) throw new Error("attached: the node is attached to another engine");
4131
+ if (!node.kind) throw new Error("unsupported: an engine makes its own mixer, output and input");
4132
+ node.engine = this;
4133
+ this.attachedNodes.add(node);
4134
+ const bands = node.bandCount;
4135
+ this.fire({ action: "attach", engine: this.id, node: node.id, kind: node.kind, values: node.attachValues(), ...bands !== void 0 ? { bands } : {} });
4136
+ }
4137
+ detach(node) {
4138
+ if (node.engine !== this || !node.kind) return;
4139
+ this.fire({ action: "detach", engine: this.id, node: node.id });
4140
+ if (node instanceof AVAudioPlayerNode) node.engineStopped();
4141
+ node.engine = null;
4142
+ this.attachedNodes.delete(node);
4143
+ }
4144
+ /**
4145
+ * Connects `from`'s output to `to` (an array: to each of them), replacing
4146
+ * where it went before; a node that is not a mixer takes one input, so
4147
+ * what was connected into it is let go. `format` is accepted for Swift's
4148
+ * shape: the host picks one that fits both ends.
4149
+ */
4150
+ connect(from, to, format = null) {
4151
+ const destinations = Array.isArray(to) ? to : [to];
4152
+ for (const node of [from, ...destinations]) {
4153
+ if (node.engine !== this) throw new Error("notAttached: attach both nodes to the engine before connecting them");
4154
+ }
4155
+ this.fire({ action: "connect", engine: this.id, from: from.id, to: destinations.map((node) => node.id) });
4156
+ }
4157
+ disconnectNodeOutput(node) {
4158
+ this.fire({ action: "disconnectOutput", engine: this.id, node: node.id });
4159
+ }
4160
+ disconnectNodeInput(node) {
4161
+ this.fire({ action: "disconnectInput", engine: this.id, node: node.id });
4162
+ }
4163
+ prepare() {
4164
+ this.fire({ action: "prepare", engine: this.id });
4165
+ }
4166
+ /** Starts rendering; with the input connected, asks for the microphone (rejecting `permission: …` when refused). */
4167
+ async start() {
4168
+ await this.send({ action: "start", engine: this.id });
4169
+ this.running = true;
4170
+ }
4171
+ /** Pauses rendering; players hold their place and play on at `start()`. */
4172
+ pause() {
4173
+ this.running = false;
4174
+ this.fire({ action: "pause", engine: this.id });
4175
+ }
4176
+ /** Stops rendering; players stop and drop what they had scheduled. */
4177
+ stop() {
4178
+ this.running = false;
4179
+ for (const node of this.attachedNodes) if (node instanceof AVAudioPlayerNode) node.engineStopped();
4180
+ this.fire({ action: "stop", engine: this.id });
4181
+ }
4182
+ /** Silences every node (players stop, notes end) without stopping the engine. */
4183
+ reset() {
4184
+ for (const node of this.attachedNodes) if (node instanceof AVAudioPlayerNode) node.engineStopped();
4185
+ this.fire({ action: "reset", engine: this.id });
4186
+ }
4187
+ /** Stops the engine and frees it on the host; it cannot be used after. */
4188
+ dispose() {
4189
+ if (this.disposed) return;
4190
+ engines?.unregister(this);
4191
+ void perform$1({ action: "destroy", engine: this.id }).catch(() => void 0);
4192
+ this.disposed = true;
4193
+ this.running = false;
4194
+ for (const node of this.attachedNodes) node.engine = null;
4195
+ this.attachedNodes.clear();
4196
+ }
4197
+ }
4198
+ function useAudioEngine(build) {
4199
+ const [engine] = useState(() => {
4200
+ if (!AVAudioEngine.isAvailable) return null;
4201
+ const made = new AVAudioEngine();
4202
+ build?.(made);
4203
+ return made;
4204
+ });
4205
+ useEffect(() => () => engine?.dispose(), [engine]);
4206
+ return engine;
4207
+ }
4208
+ let nextId = 1;
4209
+ const hostNow = () => browserEnvironment.languageModel;
4210
+ function perform(command2) {
4211
+ const host = hostNow();
4212
+ if (!host) return Promise.reject(new GenerationError("assetsUnavailable", "This page or app has no language model."));
4213
+ return host.perform(command2).catch((error) => {
4214
+ throw GenerationError.from(error);
4215
+ });
4216
+ }
4217
+ const KINDS = {
4218
+ unavailable: "assetsUnavailable",
4219
+ context: "exceededContextWindowSize",
4220
+ guardrail: "guardrailViolation",
4221
+ language: "unsupportedLanguageOrLocale",
4222
+ concurrent: "concurrentRequests",
4223
+ rateLimited: "rateLimited",
4224
+ refusal: "refusal"
4225
+ };
4226
+ class GenerationError extends Error {
4227
+ constructor(kind, message) {
4228
+ super(message);
4229
+ this.kind = kind;
4230
+ this.name = "GenerationError";
4231
+ }
4232
+ kind;
4233
+ /** The error a host's tagged message (`context: …`) stands for. */
4234
+ static from(error) {
4235
+ if (error instanceof GenerationError) return error;
4236
+ const message = error instanceof Error ? error.message : String(error);
4237
+ const match = /^(\w+): ?([\s\S]*)$/.exec(message);
4238
+ const kind = match ? KINDS[match[1]] : void 0;
4239
+ return kind ? new GenerationError(kind, match[2]) : new GenerationError("unknown", message);
4240
+ }
4241
+ }
4242
+ class SystemLanguageModel {
4243
+ static default = new SystemLanguageModel();
4244
+ constructor() {
4245
+ }
4246
+ /** Views showing the availability, asked again when a download starts or the model goes. */
4247
+ listeners = /* @__PURE__ */ new Set();
4248
+ /** Whether the model can answer now, or why not. */
4249
+ async availability() {
4250
+ if (!hostNow()) return { status: "unavailable", reason: "deviceNotEligible" };
4251
+ try {
4252
+ return await perform({ action: "availability" });
4253
+ } catch {
4254
+ return { status: "unavailable", reason: "deviceNotEligible" };
4255
+ }
4256
+ }
4257
+ async isAvailable() {
4258
+ return (await this.availability()).status === "available";
4259
+ }
4260
+ /**
4261
+ * Starts fetching a model the app fetches itself (availability's
4262
+ * `download`), which the viewer asked for: show its size first. Not part
4263
+ * of FoundationModels, whose model the system manages.
4264
+ */
4265
+ async download() {
4266
+ await perform({ action: "download" });
4267
+ this.changed();
4268
+ }
4269
+ /**
4270
+ * Makes the model with this `id` (one of availability's `models`) the one
4271
+ * that answers, from the next session on; remembered across launches.
4272
+ */
4273
+ async selectModel(id) {
4274
+ await perform({ action: "select", id });
4275
+ this.changed();
4276
+ }
4277
+ /** Stops such a download, or removes the model from the device to free its space. */
4278
+ async deleteDownload() {
4279
+ await perform({ action: "deleteDownload" });
4280
+ this.changed();
4281
+ }
4282
+ /** @internal `useSystemLanguageModel` hears downloads start and models go. */
4283
+ listen(listener) {
4284
+ this.listeners.add(listener);
4285
+ return () => this.listeners.delete(listener);
4286
+ }
4287
+ changed() {
4288
+ for (const listener of this.listeners) listener();
4289
+ }
4290
+ }
4291
+ function useSystemLanguageModel(model = SystemLanguageModel.default) {
4292
+ const [availability, setAvailability] = useState(null);
4293
+ useEffect(() => {
4294
+ let live = true;
4295
+ let timer;
4296
+ const check = () => {
4297
+ if (timer !== void 0) clearTimeout(timer);
4298
+ timer = void 0;
4299
+ void model.availability().then((next) => {
4300
+ if (!live) return;
4301
+ setAvailability((current) => JSON.stringify(current) === JSON.stringify(next) ? current : next);
4302
+ if (timer !== void 0) return;
4303
+ if (next.download?.progress !== void 0) timer = setTimeout(check, 1e3);
4304
+ else if (next.status === "unavailable" && next.reason === "modelNotReady" && !next.download) timer = setTimeout(check, 5e3);
4305
+ });
4306
+ };
4307
+ check();
4308
+ const stop = model.listen(check);
4309
+ return () => {
4310
+ live = false;
4311
+ stop();
4312
+ if (timer !== void 0) clearTimeout(timer);
4313
+ };
4314
+ }, [model]);
4315
+ return availability;
4316
+ }
4317
+ class ResponseStream {
4318
+ /** @internal Made by `LanguageModelSession.streamResponse`. */
4319
+ constructor(session, opened, prompt, options, ended) {
4320
+ this.ended = ended;
4321
+ this.started = opened.then(
4322
+ () => perform({ action: "stream", session: session.id, stream: this.id, prompt, ...options ? { options } : {} })
4323
+ );
4324
+ this.started.catch(() => this.end());
4325
+ }
4326
+ ended;
4327
+ id = nextId++;
4328
+ started;
4329
+ finished = false;
4330
+ last = { content: "" };
4331
+ async *[Symbol.asyncIterator]() {
4332
+ try {
4333
+ await this.started;
4334
+ while (!this.finished) {
4335
+ const { content, done } = await perform({ action: "next", stream: this.id });
4336
+ if (done) this.end();
4337
+ if (content !== this.last.content) {
4338
+ this.last = { content };
4339
+ yield this.last;
4340
+ }
4341
+ }
4342
+ } catch (error) {
4343
+ this.end();
4344
+ throw error;
4345
+ } finally {
4346
+ this.cancel();
4347
+ }
4348
+ }
4349
+ /** The whole response, once it has ended. */
4350
+ async collect() {
4351
+ for await (const snapshot of this) this.last = snapshot;
4352
+ return this.last;
4353
+ }
4354
+ /** Stops the response; the iteration ends with what came so far. */
4355
+ cancel() {
4356
+ if (this.finished) return;
4357
+ this.end();
4358
+ void this.started.then(() => perform({ action: "cancel", stream: this.id })).catch(() => {
4359
+ });
4360
+ }
4361
+ end() {
4362
+ if (this.finished) return;
4363
+ this.finished = true;
4364
+ this.ended();
4365
+ }
4366
+ }
4367
+ class LanguageModelSession {
4368
+ id = nextId++;
4369
+ instructions;
4370
+ tools;
4371
+ opened;
4372
+ responses = 0;
4373
+ closed = false;
4374
+ constructor(options = {}) {
4375
+ this.instructions = options.instructions;
4376
+ this.tools = options.tools ?? [];
4377
+ const tools = this.tools.map(({ name, description, parameters }) => ({ name, description, parameters }));
4378
+ this.opened = perform({
4379
+ action: "open",
4380
+ session: this.id,
4381
+ ...options.instructions ? { instructions: options.instructions } : {},
4382
+ ...tools.length > 0 ? { tools } : {}
4383
+ });
4384
+ this.opened.catch(() => {
4385
+ });
4386
+ if (tools.length > 0) void this.opened.then(() => this.serveTools()).catch(() => {
4387
+ });
4388
+ }
4389
+ /** Runs the model's tool calls as they come, until the session closes. */
4390
+ async serveTools() {
4391
+ while (!this.closed) {
4392
+ const request = await perform({ action: "toolCall", session: this.id });
4393
+ if (!request || request.closed || request.call === void 0) return;
4394
+ const tool = this.tools.find((candidate) => candidate.name === request.name);
4395
+ let answer;
4396
+ try {
4397
+ if (!tool) throw new Error(`no tool named ${String(request.name)}`);
4398
+ const output = await tool.call(request.arguments ?? {});
4399
+ answer = { output: typeof output === "string" ? output : JSON.stringify(output ?? null) };
4400
+ } catch (error) {
4401
+ answer = { error: error instanceof Error ? error.message : String(error) };
4402
+ }
4403
+ await perform({ action: "toolResult", session: this.id, call: request.call, ...answer });
4404
+ }
4405
+ }
4406
+ /** Whether a response is under way. */
4407
+ get isResponding() {
4408
+ return this.responses > 0;
4409
+ }
4410
+ /** Loads the model ahead of the first prompt, so it answers sooner. */
4411
+ prewarm() {
4412
+ void this.opened.then(() => perform({ action: "prewarm", session: this.id })).catch(() => {
4413
+ });
4414
+ }
4415
+ async respond(to, options) {
4416
+ this.responses += 1;
4417
+ try {
4418
+ await this.opened;
4419
+ return await perform({ action: "respond", session: this.id, prompt: to, ...options ? { options } : {} });
4420
+ } finally {
4421
+ this.responses -= 1;
4422
+ }
4423
+ }
4424
+ streamResponse(to, options) {
4425
+ this.responses += 1;
4426
+ return new ResponseStream(this, this.opened, to, options, () => {
4427
+ this.responses -= 1;
4428
+ });
4429
+ }
4430
+ /** Ends the session: the host lets its conversation go. */
4431
+ close() {
4432
+ if (this.closed) return;
4433
+ this.closed = true;
4434
+ void this.opened.then(() => perform({ action: "close", session: this.id })).catch(() => {
4435
+ });
4436
+ }
4437
+ }
3577
4438
  function wakeLockAPI() {
3578
4439
  const nav = globalThis.navigator;
3579
4440
  return nav?.wakeLock ?? null;
@@ -3832,7 +4693,8 @@ function ProgressView(props) {
3832
4693
  }
3833
4694
  function ToolbarItem(props) {
3834
4695
  const hidden = props.sharedBackgroundVisibility === "hidden";
3835
- return sb("toolbar", hidden ? { placement: toolbarPlacement(props.placement), sharedBackground: "hidden" } : { placement: toolbarPlacement(props.placement) }, void 0, props.children);
4696
+ const items = withEnvironment({ labelStyle: "iconOnly" }, props.children);
4697
+ return sb("toolbar", hidden ? { placement: toolbarPlacement(props.placement), sharedBackground: "hidden" } : { placement: toolbarPlacement(props.placement) }, void 0, items);
3836
4698
  }
3837
4699
  const ToolbarItemGroup = ToolbarItem;
3838
4700
  function ToolbarSpacer(props) {
@@ -4501,6 +5363,9 @@ function Form(props) {
4501
5363
  function Section(props) {
4502
5364
  const { own, modifiers } = splitProps(props);
4503
5365
  const pinned = useContext(PinnedContext);
5366
+ const [expanded, setExpanded] = own.isExpanded !== void 0 ? readBinding(own.isExpanded, own.onExpandedChange) : [true, () => {
5367
+ }];
5368
+ const collapsible = own.isExpanded !== void 0;
4504
5369
  let header = supplement(own.header);
4505
5370
  let footer = supplement(own.footer);
4506
5371
  if (pinned?.includes("sectionHeaders") && header.text !== null && !header.view) header = { text: null, view: /* @__PURE__ */ jsx(Text, { children: header.text }) };
@@ -4509,7 +5374,15 @@ function Section(props) {
4509
5374
  if (header.view) children.push(sb("sectionheader", {}, void 0, header.view));
4510
5375
  if (own.children !== void 0) children.push(wrapRows(own.children));
4511
5376
  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);
5377
+ return applyModifiers(
5378
+ sb(
5379
+ "section",
5380
+ collapsible ? { header: header.text, footer: footer.text, collapsible: true, expanded } : { header: header.text, footer: footer.text },
5381
+ collapsible ? { tap: () => setExpanded(!expanded) } : void 0,
5382
+ children
5383
+ ),
5384
+ modifiers
5385
+ );
4513
5386
  }
4514
5387
  function wrapRows(children) {
4515
5388
  let offset = 0;
@@ -4620,10 +5493,11 @@ function Host(props) {
4620
5493
  function Menu(props) {
4621
5494
  const { own, modifiers } = splitProps(props);
4622
5495
  const label = own.title !== void 0 ? /* @__PURE__ */ jsx(Text, { children: own.title }) : own.label;
5496
+ const items = withEnvironment({ labelStyle: "automatic" }, own.children);
4623
5497
  return applyModifiers(
4624
5498
  sb("menu", {}, void 0, [
4625
5499
  /* @__PURE__ */ jsx(Fragment, { children: sb("zstack", { alignment: "center" }, void 0, label) }, "label"),
4626
- /* @__PURE__ */ jsx(Fragment, { children: own.children }, "items")
5500
+ /* @__PURE__ */ jsx(Fragment, { children: items }, "items")
4627
5501
  ]),
4628
5502
  modifiers
4629
5503
  );
@@ -5180,22 +6054,10 @@ function DisclosureGroup(props) {
5180
6054
  const label = typeof own.label === "string" ? /* @__PURE__ */ jsx(Text, { children: own.label }) : own.label;
5181
6055
  const toggle = () => withAnimation({ type: "easeInOut", duration: 0.25 }, () => setExpanded(!expanded));
5182
6056
  return applyModifiers(
5183
- /* @__PURE__ */ jsxs(VStack, { alignment: "leading", spacing: 0, children: [
5184
- /* @__PURE__ */ jsx(Button, { action: toggle, buttonStyle: "plain", children: /* @__PURE__ */ jsxs(HStack, { children: [
5185
- styled({ foreground: { hierarchical: 1 } }, label),
5186
- /* @__PURE__ */ jsx(Spacer, {}),
5187
- /* @__PURE__ */ jsx(
5188
- Image$1,
5189
- {
5190
- systemName: "chevron.right",
5191
- font: { textStyle: "footnote", weight: "semibold" },
5192
- foregroundStyle: "tint",
5193
- rotationEffect: expanded ? 90 : 0
5194
- }
5195
- )
5196
- ] }) }),
5197
- expanded && /* @__PURE__ */ jsx(VStack, { alignment: "leading", spacing: 8, padding: ["top", 8], transition: { kind: "opacity" }, children: own.children })
5198
- ] }),
6057
+ sb("disclosuregroup", { expanded }, { tap: toggle }, [
6058
+ /* @__PURE__ */ jsx(Fragment, { children: sb("zstack", { alignment: "center" }, void 0, label) }, "label"),
6059
+ /* @__PURE__ */ jsx(Fragment, { children: own.children }, "content")
6060
+ ]),
5199
6061
  modifiers
5200
6062
  );
5201
6063
  }
@@ -5291,16 +6153,46 @@ function ColorPicker(props) {
5291
6153
  modifiers
5292
6154
  );
5293
6155
  }
6156
+ const isFile = (item) => typeof item === "object" && !(item instanceof URL) && "data" in item;
6157
+ const MIME_BY_EXTENSION = {
6158
+ txt: "text/plain",
6159
+ md: "text/markdown",
6160
+ csv: "text/csv",
6161
+ json: "application/json",
6162
+ html: "text/html",
6163
+ pdf: "application/pdf",
6164
+ png: "image/png",
6165
+ jpg: "image/jpeg",
6166
+ jpeg: "image/jpeg",
6167
+ gif: "image/gif",
6168
+ svg: "image/svg+xml",
6169
+ m4a: "audio/mp4",
6170
+ mp3: "audio/mpeg",
6171
+ mp4: "video/mp4",
6172
+ mov: "video/quicktime",
6173
+ zip: "application/zip"
6174
+ };
6175
+ async function sharedFile(file) {
6176
+ const bytes = typeof file.data === "string" ? new TextEncoder().encode(file.data) : file.data instanceof Uint8Array ? file.data : file.data instanceof ArrayBuffer ? new Uint8Array(file.data) : new Uint8Array(await file.data.arrayBuffer());
6177
+ const extension = file.name.includes(".") ? file.name.slice(file.name.lastIndexOf(".") + 1).toLowerCase() : "";
6178
+ return { name: file.name, type: file.type ?? MIME_BY_EXTENSION[extension] ?? "application/octet-stream", data: base64Encode(bytes) };
6179
+ }
6180
+ async function shareParamsOf(props) {
6181
+ const item = props.item;
6182
+ const isURL = item instanceof URL || typeof item === "string" && /^[a-z][a-z0-9+.-]*:\/\//i.test(item);
6183
+ const params = {};
6184
+ if (props.subject) params.title = props.subject;
6185
+ if (isURL) params.url = String(item);
6186
+ const text = [props.message, typeof item === "string" && !isURL ? item : null].filter(Boolean).join("\n");
6187
+ if (text) params.text = text;
6188
+ const files = [...isFile(item) ? [item] : [], ...props.files ?? []];
6189
+ if (files.length) params.files = await Promise.all(files.map(sharedFile));
6190
+ return params;
6191
+ }
5294
6192
  function ShareLink(props) {
5295
6193
  const { own, modifiers } = splitProps(props);
5296
6194
  const share = () => {
5297
- const isURL = own.item instanceof URL || /^[a-z][a-z0-9+.-]*:\/\//i.test(own.item);
5298
- const params = {};
5299
- if (own.subject) params["title"] = own.subject;
5300
- if (isURL) params["url"] = String(own.item);
5301
- const text = [own.message, isURL ? null : String(own.item)].filter(Boolean).join("\n");
5302
- if (text) params["text"] = text;
5303
- performHostRequest({ op: "request", id: 0, kind: "share", params }, browserEnvironment).catch((error) => console.error(error));
6195
+ shareParamsOf(own).then((params) => performHostRequest({ op: "request", id: 0, kind: "share", params }, browserEnvironment)).catch((error) => console.error(error));
5304
6196
  };
5305
6197
  return applyModifiers(
5306
6198
  /* @__PURE__ */ jsx(Button, { action: share, children: own.children ?? /* @__PURE__ */ jsx(Label, { title: own.title ?? "Share", systemImage: "square.and.arrow.up" }) }),
@@ -5921,6 +6813,23 @@ function KeyframeAnimator({
5921
6813
  return children(value);
5922
6814
  }
5923
6815
  export {
6816
+ AVAudioEngine,
6817
+ AVAudioFile,
6818
+ AVAudioInputNode,
6819
+ AVAudioMixerNode,
6820
+ AVAudioNode,
6821
+ AVAudioOutputNode,
6822
+ AVAudioPCMBuffer,
6823
+ AVAudioPlayerNode,
6824
+ AVAudioTime,
6825
+ AVAudioUnitDelay,
6826
+ AVAudioUnitDistortion,
6827
+ AVAudioUnitEQ,
6828
+ AVAudioUnitEQFilterParameters,
6829
+ AVAudioUnitEffect,
6830
+ AVAudioUnitReverb,
6831
+ AVAudioUnitSampler,
6832
+ AVAudioUnitTimePitch,
5924
6833
  AVPlayer,
5925
6834
  AVQueuePlayer,
5926
6835
  AngularGradient,
@@ -5951,6 +6860,7 @@ export {
5951
6860
  ForEach,
5952
6861
  Form,
5953
6862
  Gauge,
6863
+ GenerationError,
5954
6864
  GeometryChangeView,
5955
6865
  GeometryReader,
5956
6866
  GraphicsContext,
@@ -5966,6 +6876,7 @@ export {
5966
6876
  LOADING_INDICATOR_DELAY_MS,
5967
6877
  Label,
5968
6878
  LabeledContent,
6879
+ LanguageModelSession,
5969
6880
  Layout,
5970
6881
  LazyHGrid,
5971
6882
  LazyHStack,
@@ -5989,6 +6900,7 @@ export {
5989
6900
  ProgressView,
5990
6901
  RadialGradient,
5991
6902
  Rectangle,
6903
+ ResponseStream,
5992
6904
  RoundedRectangle,
5993
6905
  Row,
5994
6906
  RuleMark,
@@ -6004,6 +6916,7 @@ export {
6004
6916
  SplitSelectionContext,
6005
6917
  Stepper,
6006
6918
  SwiftUIView,
6919
+ SystemLanguageModel,
6007
6920
  Tab,
6008
6921
  TabView,
6009
6922
  Table,
@@ -6050,6 +6963,7 @@ export {
6050
6963
  moveItems,
6051
6964
  nextGeometry,
6052
6965
  niceStep,
6966
+ noteInName,
6053
6967
  opacity,
6054
6968
  openURL,
6055
6969
  padding,
@@ -6058,8 +6972,10 @@ export {
6058
6972
  providingScrollSettings,
6059
6973
  readBinding,
6060
6974
  rgb,
6975
+ samplerZones,
6061
6976
  scrollGeometryOf,
6062
6977
  shapeStyle,
6978
+ shareParamsOf,
6063
6979
  signInWithApple,
6064
6980
  system,
6065
6981
  takeScrollSettings,
@@ -6067,6 +6983,7 @@ export {
6067
6983
  unitPoint,
6068
6984
  useAVPlayer,
6069
6985
  useAppStorage,
6986
+ useAudioEngine,
6070
6987
  useAudioRecorder,
6071
6988
  useColorScheme,
6072
6989
  useContextBridge,
@@ -6084,6 +7001,7 @@ export {
6084
7001
  useScenePhase,
6085
7002
  useSpeechRecognition,
6086
7003
  useSpeechSynthesis,
7004
+ useSystemLanguageModel,
6087
7005
  useUndoManager,
6088
7006
  useViewEnvironment,
6089
7007
  useWakeLock,