Skip to main content

fission_core/
lib.rs

1//! # fission-core
2//!
3//! The runtime, widget system, and action/reducer architecture for the Fission UI
4//! framework.
5//!
6//! `fission-core` provides:
7//!
8//! - A **declarative widget tree** built from composable primitives ([`Widget`]).
9//! - A **unidirectional data-flow** pipeline: [`Action`] -> [`Runtime::dispatch`] -> reducer
10//!   -> mutated [`GlobalState`].
11//! - An **effect system** for async side-effects ([`Effect`], [`RuntimeEffect`]).
12//! - Built-in widgets: [`ui::Button`], [`ui::Text`], [`ui::TextInput`],
13//!   [`ui::Container`], [`ui::Row`], [`ui::Column`], [`ui::Scroll`],
14//!   [`ui::ZStack`], [`ui::Grid`], [`ui::LazyColumn`], and more.
15//!
16//! ## Getting started
17//!
18//! ```rust,ignore
19//! use fission_core::*;
20//! use fission_core::ui::*;
21//!
22//! // Define application state
23//! #[derive(Debug, Default)]
24//! struct MyState { value: String }
25//! impl GlobalState for MyState {}
26//!
27//! // Build a widget tree value
28//! struct MyWidget;
29//! impl From<MyWidget> for Widget {
30//!     fn from(_: MyWidget) -> Widget {
31//!         let (_, view) = fission_core::build::current::<MyState>();
32//!         Text::new(&*view.state().value).into()
33//!     }
34//! }
35//! ```
36
37use anyhow::Result;
38use lazy_static::lazy_static;
39use std::any::TypeId;
40use std::collections::HashMap;
41use std::sync::{Arc, Mutex};
42
43extern crate self as fission_core;
44
45pub mod action;
46pub mod async_runtime;
47pub mod build;
48mod build_context;
49pub mod capability; // New
50pub mod context; // New
51pub mod data_stream;
52pub mod diff;
53pub mod effect; // New
54pub mod env;
55pub mod event;
56pub mod hit_test;
57pub mod input;
58pub(crate) mod lowering;
59pub mod media;
60pub mod motion;
61pub mod platform;
62pub mod platform_barcode;
63pub mod platform_biometric;
64pub mod platform_bluetooth;
65pub mod platform_camera;
66pub mod platform_clipboard;
67pub mod platform_geolocation;
68pub mod platform_haptics;
69pub mod platform_microphone;
70pub mod platform_nfc;
71pub mod platform_passkey;
72pub mod platform_volume;
73pub mod platform_wifi;
74pub mod registry;
75pub mod runtime;
76pub mod scoped_action_handlers;
77pub mod scrollbar;
78pub mod state;
79pub mod time;
80pub mod ui;
81
82pub mod view;
83
84#[doc(hidden)]
85/// Framework integration boundary for first-party shells, renderers, test
86/// harnesses, and generated widget implementations.
87///
88/// This module is not part of the application authoring API. Application code
89/// should construct `Widget` values from widget structs and components instead
90/// of calling lowering helpers directly.
91pub mod internal {
92    pub use crate::build_context::BuildCtx;
93    pub use crate::lowering::{
94        build_layout_tree, wrap_zstack_child, InternalIrBuilder, InternalLoweringCx,
95    };
96    use crate::Widget;
97    use fission_ir::WidgetId;
98
99    pub fn custom_render_widget(node: InternalRenderNode) -> Widget {
100        Widget::custom(node)
101    }
102
103    pub fn lower_widget(widget: &Widget, cx: &mut InternalLoweringCx) -> WidgetId {
104        widget.lower(cx)
105    }
106
107    pub fn lower_widget_to_ir(widget: &Widget) -> fission_ir::CoreIR {
108        let env = crate::Env::default();
109        let runtime_state = crate::RuntimeState::default();
110        let mut cx = InternalLoweringCx::new(&env, &runtime_state, None, None);
111        widget.lower(&mut cx);
112        cx.ir
113    }
114
115    pub fn widget_kind_name(widget: &Widget) -> &'static str {
116        widget.kind_name()
117    }
118
119    pub fn widget_as_row(widget: &Widget) -> Option<&crate::ui::Row> {
120        widget.as_row()
121    }
122
123    pub fn widget_as_column(widget: &Widget) -> Option<&crate::ui::Column> {
124        widget.as_column()
125    }
126
127    pub fn widget_as_container(widget: &Widget) -> Option<&crate::ui::Container> {
128        widget.as_container()
129    }
130
131    pub fn widget_as_scroll(widget: &Widget) -> Option<&crate::ui::Scroll> {
132        widget.as_scroll()
133    }
134
135    pub fn widget_as_rich_text(widget: &Widget) -> Option<&crate::ui::RichText> {
136        widget.as_rich_text()
137    }
138
139    pub fn widget_as_text(widget: &Widget) -> Option<&crate::ui::Text> {
140        widget.as_text()
141    }
142
143    pub fn widget_as_text_input(widget: &Widget) -> Option<&crate::ui::TextInput> {
144        widget.as_text_input()
145    }
146
147    pub fn widget_as_button(widget: &Widget) -> Option<&crate::ui::Button> {
148        widget.as_button()
149    }
150
151    pub fn widget_as_gesture_detector(widget: &Widget) -> Option<&crate::ui::GestureDetector> {
152        widget.as_gesture_detector()
153    }
154
155    pub fn widget_as_zstack(widget: &Widget) -> Option<&crate::ui::ZStack> {
156        widget.as_zstack()
157    }
158
159    pub use crate::ui::custom_render::{
160        downcast_render_object, CustomEventResult, CustomHitResult, CustomRender,
161        CustomRenderObject,
162    };
163    pub use crate::ui::node::{CustomWidget, InternalRenderNode};
164    pub use crate::ui::traits::{InternalLower, InternalLowerer};
165}
166
167pub mod public {
168    pub mod action {
169        pub use crate::action::*;
170    }
171    pub mod env {
172        pub use crate::env::*;
173    }
174    pub mod event {
175        pub use crate::event::*;
176    }
177    pub mod hit_test {
178        pub use crate::hit_test::*;
179    }
180    pub mod registry {
181        pub use crate::registry::*;
182    }
183    pub mod scoped_action_handlers {
184        pub use crate::scoped_action_handlers::*;
185    }
186    pub mod ui {
187        pub use crate::ui::widgets::*;
188        pub use crate::ui::Widget;
189
190        pub mod widgets {
191            pub use crate::ui::widgets::*;
192        }
193    }
194    pub mod view {
195        pub use crate::view::*;
196    }
197
198    pub use crate::action::{Action, ActionEnvelope, ActionId, ActionScopeId, GlobalState};
199    pub use crate::async_runtime::{
200        BoxFuture, JobCtx, JobRef, JobSpec, ResourceExecutionContext, ServiceBindings, ServiceCtx,
201        ServiceRunner, ServiceSlot, ServiceSpec, ServiceType,
202    };
203    pub use crate::capability::{
204        CapabilityCtx, CapabilityInvocationPayload, CapabilityType, OpenUrlCapability,
205        OpenUrlRequest, OperationCapability, PickOpenFilesCapability, PickOpenFilesError,
206        PickOpenFilesRequest, PickOpenFilesResult, PickedFile, OPEN_URL, PICK_OPEN_FILES,
207    };
208    pub use crate::context::{
209        BarcodeScannerEffects, BiometricEffects, BluetoothEffects, CameraEffects, ClipboardEffects,
210        Effects, GeolocationEffects, HapticEffects, MicrophoneEffects, NfcEffects,
211        NotificationEffects, PasskeyEffects, ReducerContext, VolumeEffects, WifiEffects,
212    }; // New
213    pub use crate::data_stream::{
214        collect_data_stream, empty_data_stream, single_chunk_data_stream, BoxFissionDataStream,
215        DataStreamId, DataStreamRegistry, FissionDataStream, FissionDataStreamError,
216        FissionDataStreamErrorKind,
217    };
218    pub use crate::effect::{
219        ActionInput, Effect, EffectEnvelope, RuntimeEffect, ScrollAlignment, ScrollAxis,
220        ScrollBehavior, ScrollIntoViewRequest,
221    };
222    pub use crate::env::{
223        Clipboard, DragSessionPayload, DragSessionState, Env, ImeHandler, InteractionStateMap,
224        RuntimeState, ScrollStateMap, WindowEnv, WindowTitle,
225    };
226    pub use crate::runtime::Runtime;
227    pub use crate::state::{LocalStateKey, LocalStateStore, StateField};
228    pub use bytes::Bytes;
229
230    pub use crate::build::{BuildCtxHandle, ViewHandle};
231    pub use crate::event::{
232        ExternalDragEvent, InputEvent, KeyCode, KeyEvent, LifecycleEvent, PointerButton,
233        PointerEvent,
234    };
235    pub use crate::motion::*;
236    pub use crate::platform::{
237        CancelAllNotificationsCapability, CancelNotificationCapability, CancelNotificationRequest,
238        DeepLink, DeepLinkConfig, DeepLinkReceived, DeepLinkSource,
239        GetNotificationSettingsCapability, NotificationActionButton, NotificationError,
240        NotificationId, NotificationPermission, NotificationPermissionRequest, NotificationReceipt,
241        NotificationRequest, NotificationResponse, NotificationResponseReceived,
242        NotificationSchedule, NotificationSettings, NotificationSound, PushPlatform,
243        PushRegistration, PushRegistrationRequest, RegisterPushNotificationsCapability,
244        RequestNotificationPermissionCapability, ScheduleNotificationCapability,
245        SetBadgeCountCapability, SetBadgeCountRequest, ShowNotificationCapability,
246        UnregisterPushNotificationsCapability, CANCEL_ALL_NOTIFICATIONS, CANCEL_NOTIFICATION,
247        GET_NOTIFICATION_SETTINGS, REGISTER_PUSH_NOTIFICATIONS, REQUEST_NOTIFICATION_PERMISSION,
248        SCHEDULE_NOTIFICATION, SET_BADGE_COUNT, SHOW_NOTIFICATION, UNREGISTER_PUSH_NOTIFICATIONS,
249    };
250    pub use crate::platform_barcode::{
251        BarcodeFormat, BarcodeImageDecodeRequest, BarcodePoint, BarcodeScanRequest,
252        BarcodeScanResult, BarcodeScanResults, BarcodeScannerError, CancelBarcodeScanCapability,
253        DecodeBarcodeImageCapability, ScanBarcodeCapability, CANCEL_BARCODE_SCAN,
254        DECODE_BARCODE_IMAGE, SCAN_BARCODE,
255    };
256    pub use crate::platform_biometric::{
257        AuthenticateBiometricCapability, BiometricAuthenticateRequest, BiometricAuthenticateResult,
258        BiometricAvailability, BiometricError, BiometricKind, BiometricStrength,
259        CancelBiometricAuthenticationCapability, GetBiometricAvailabilityCapability,
260        AUTHENTICATE_BIOMETRIC, CANCEL_BIOMETRIC_AUTHENTICATION, GET_BIOMETRIC_AVAILABILITY,
261    };
262    pub use crate::platform_bluetooth::{
263        BluetoothAdvertiseReceipt, BluetoothAdvertiseRequest, BluetoothAvailability,
264        BluetoothConnectRequest, BluetoothConnection, BluetoothDevice, BluetoothDisconnectRequest,
265        BluetoothError, BluetoothMode, BluetoothPermission, BluetoothPermissionRequest,
266        BluetoothReadRequest, BluetoothReadResult, BluetoothScanRequest, BluetoothScanResult,
267        BluetoothStopAdvertiseRequest, BluetoothWriteRequest, ConnectBluetoothDeviceCapability,
268        DisconnectBluetoothDeviceCapability, GetBluetoothAvailabilityCapability,
269        ReadBluetoothCharacteristicCapability, RequestBluetoothPermissionCapability,
270        ScanBluetoothDevicesCapability, StartBluetoothAdvertisingCapability,
271        StopBluetoothAdvertisingCapability, WriteBluetoothCharacteristicCapability,
272        CONNECT_BLUETOOTH_DEVICE, DISCONNECT_BLUETOOTH_DEVICE, GET_BLUETOOTH_AVAILABILITY,
273        READ_BLUETOOTH_CHARACTERISTIC, REQUEST_BLUETOOTH_PERMISSION, SCAN_BLUETOOTH_DEVICES,
274        START_BLUETOOTH_ADVERTISING, STOP_BLUETOOTH_ADVERTISING, WRITE_BLUETOOTH_CHARACTERISTIC,
275    };
276    pub use crate::platform_camera::{
277        CameraAvailability, CameraCapture, CameraCaptureRequest, CameraDevice, CameraError,
278        CameraFacing, CameraFlashMode, CameraFlashlightRequest, CameraImageFormat,
279        CameraPermission, CameraPermissionRequest, CameraResolution, CancelCameraCaptureCapability,
280        CapturePhotoCapability, GetCameraAvailabilityCapability, RequestCameraPermissionCapability,
281        SetCameraFlashlightCapability, CANCEL_CAMERA_CAPTURE, CAPTURE_PHOTO,
282        GET_CAMERA_AVAILABILITY, REQUEST_CAMERA_PERMISSION, SET_CAMERA_FLASHLIGHT,
283    };
284    pub use crate::platform_clipboard::{
285        ClearClipboardCapability, ClipboardContent, ClipboardError, ClipboardItem, ClipboardText,
286        ClipboardWriteTextRequest, ReadClipboardContentCapability, ReadClipboardTextCapability,
287        WriteClipboardContentCapability, WriteClipboardTextCapability, CLEAR_CLIPBOARD,
288        READ_CLIPBOARD_CONTENT, READ_CLIPBOARD_TEXT, WRITE_CLIPBOARD_CONTENT, WRITE_CLIPBOARD_TEXT,
289    };
290    pub use crate::platform_geolocation::{
291        GeolocationError, GeolocationPermission, GeolocationPermissionRequest, GeolocationPosition,
292        GeolocationPositionRequest, GetCurrentPositionCapability,
293        GetGeolocationPermissionCapability, RequestGeolocationPermissionCapability,
294        GET_CURRENT_POSITION, GET_GEOLOCATION_PERMISSION, REQUEST_GEOLOCATION_PERMISSION,
295    };
296    pub use crate::platform_haptics::{
297        HapticError, HapticImpactCapability, HapticImpactRequest, HapticImpactStyle,
298        HapticNotificationCapability, HapticNotificationKind, HapticNotificationRequest,
299        HapticPatternCapability, HapticPatternRequest, HapticPatternStep,
300        HapticSelectionCapability, HAPTIC_IMPACT, HAPTIC_NOTIFICATION, HAPTIC_PATTERN,
301        HAPTIC_SELECTION,
302    };
303    pub use crate::platform_microphone::{
304        AudioSampleFormat, CancelMicrophoneCaptureCapability, CaptureMicrophoneAudioCapability,
305        GetMicrophoneAvailabilityCapability, MicrophoneAvailability, MicrophoneCapture,
306        MicrophoneCaptureRequest, MicrophoneDevice, MicrophoneError, MicrophonePermission,
307        MicrophonePermissionRequest, RequestMicrophonePermissionCapability,
308        CANCEL_MICROPHONE_CAPTURE, CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY,
309        REQUEST_MICROPHONE_PERMISSION,
310    };
311    pub use crate::platform_nfc::{
312        CancelNfcSessionCapability, EmulateNfcTagCapability, GetNfcAvailabilityCapability,
313        NfcAvailability, NfcEmulationRequest, NfcError, NfcRecord, NfcRecordTypeNameFormat,
314        NfcScanRequest, NfcSessionReceipt, NfcTag, NfcTagDiscovered, NfcTechnology,
315        NfcWriteRequest, ScanNfcTagCapability, WriteNfcTagCapability, CANCEL_NFC_SESSION,
316        EMULATE_NFC_TAG, GET_NFC_AVAILABILITY, SCAN_NFC_TAG, WRITE_NFC_TAG,
317    };
318    pub use crate::platform_passkey::{
319        AuthenticatePasskeyCapability, CancelPasskeyOperationCapability,
320        GetPasskeyAvailabilityCapability, PasskeyAlgorithm, PasskeyAttestationConveyance,
321        PasskeyAuthenticationRequest, PasskeyAuthenticationResult, PasskeyAuthenticatorAttachment,
322        PasskeyAuthenticatorSelection, PasskeyAvailability, PasskeyCredentialDescriptor,
323        PasskeyError, PasskeyMediation, PasskeyRegistrationRequest, PasskeyRegistrationResult,
324        PasskeyRelyingParty, PasskeyResidentKeyRequirement, PasskeyTransport, PasskeyUser,
325        PasskeyUserVerification, RegisterPasskeyCapability, AUTHENTICATE_PASSKEY,
326        CANCEL_PASSKEY_OPERATION, GET_PASSKEY_AVAILABILITY, REGISTER_PASSKEY,
327    };
328    pub use crate::platform_volume::{
329        AdjustVolumeLevelCapability, GetVolumeLevelCapability, SetVolumeLevelCapability,
330        VolumeAdjustDirection, VolumeAdjustRequest, VolumeError, VolumeLevel, VolumeSetRequest,
331        VolumeStream, ADJUST_VOLUME_LEVEL, GET_VOLUME_LEVEL, SET_VOLUME_LEVEL,
332    };
333    pub use crate::platform_wifi::{
334        ConnectWifiNetworkCapability, DisconnectWifiNetworkCapability,
335        GetWifiAvailabilityCapability, RequestWifiPermissionCapability, ScanWifiNetworksCapability,
336        WifiAvailability, WifiConnectRequest, WifiConnection, WifiDisconnectRequest, WifiError,
337        WifiNetwork, WifiPermission, WifiPermissionRequest, WifiScanRequest, WifiScanResult,
338        WifiSecurity, CONNECT_WIFI_NETWORK, DISCONNECT_WIFI_NETWORK, GET_WIFI_AVAILABILITY,
339        REQUEST_WIFI_PERMISSION, SCAN_WIFI_NETWORKS,
340    };
341    pub use crate::registry::{
342        ActionRegistry, Handler, JobResource, PortalLayer, ResourceKey, ResourcePolicy,
343        ResourceRegistry, RuntimeResourceDeclaration, RuntimeResourceKind, ServiceResource,
344        TimerResource, VideoRegistration,
345    };
346    pub use crate::time::{Clock, CurrentTime};
347    pub use crate::ui::{
348        provider, ActionScope, BadgeTone, Button, ButtonHierarchy, ButtonMotion, CardPattern,
349        Column, ComponentSize, ComponentState, CustomWidget, IosAudioSessionCategory,
350        IosAudioSessionCategoryOption, IosAudioSessionMode, IosVideoAudioOptions, Pressable,
351        PressableRole, PressableStyle, Provider, Responsive, ResponsiveCase, ResponsiveQuery, Row,
352        Text, Video, VideoAudioActivation, VideoAudioOptions, VideoAudioPolicy, VideoSource,
353        Widget, WidgetIdExt,
354    };
355    pub use crate::view::{ComputedView, FissionViewField, Selector, ValueView, View};
356    pub use crate::{
357        reduce, reduce_with, video_asset, video_file, video_network, widgets, with_reducer,
358    };
359    pub use fission_ir::op;
360    pub use fission_ir::op::{
361        BoxAlignment, BoxGridPlacement, BoxPosition, BoxStyle, GridPlacement, GridTrack, Length,
362        Overflow,
363    };
364    pub use fission_ir::{EmbedKind, FocusPolicy, Op, Role, Semantics, WidgetId};
365    pub use fission_layout::{
366        BoxConstraints, FlexDirection, LayoutEngine, LayoutInspection, LayoutNodeGeometry,
367        LayoutOp, LayoutPoint, LayoutRect, LayoutSize, LayoutSnapshot, LayoutUnit, TextMeasurer,
368    };
369}
370
371#[cfg(test)]
372mod tests;
373
374pub use action::{Action, ActionEnvelope, ActionId, ActionScopeId, GlobalState, ShellRouteChanged};
375pub use async_runtime::{
376    BoxFuture, JobCtx, JobRef, JobSpec, ResourceExecutionContext, ServiceBindings, ServiceCtx,
377    ServiceRunner, ServiceSlot, ServiceSpec, ServiceType,
378};
379pub use bytes::Bytes;
380pub use capability::{
381    CapabilityCtx, CapabilityInvocationPayload, CapabilityType, OpenUrlCapability, OpenUrlRequest,
382    OperationCapability, PickOpenFilesCapability, PickOpenFilesError, PickOpenFilesRequest,
383    PickOpenFilesResult, PickedFile, OPEN_URL, PICK_OPEN_FILES,
384};
385pub use context::{
386    BarcodeScannerEffects, BiometricEffects, BluetoothEffects, CameraEffects, ClipboardEffects,
387    Effects, GeolocationEffects, HapticEffects, MicrophoneEffects, NfcEffects, NotificationEffects,
388    PasskeyEffects, ReducerContext, VolumeEffects, WifiEffects,
389}; // New
390pub use data_stream::{
391    collect_data_stream, empty_data_stream, single_chunk_data_stream, BoxFissionDataStream,
392    DataStreamId, DataStreamRegistry, FissionDataStream, FissionDataStreamError,
393    FissionDataStreamErrorKind,
394};
395pub use effect::{
396    ActionInput, Effect, EffectEnvelope, RuntimeEffect, ScrollAlignment, ScrollAxis,
397    ScrollBehavior, ScrollIntoViewRequest,
398};
399pub use env::{
400    Clipboard, DragSessionPayload, DragSessionState, Env, ImeHandler, InteractionStateMap,
401    RouteLocation, RuntimeState, ScrollStateMap, WindowEnv, WindowTitle,
402};
403pub use motion::*;
404pub use runtime::Runtime;
405pub use state::{LocalStateKey, LocalStateStore, StateField};
406
407pub use build::{BuildCtxHandle, ViewHandle};
408pub use event::{
409    ExternalDragEvent, InputEvent, KeyCode, KeyEvent, LifecycleEvent, PointerButton, PointerEvent,
410};
411pub use fission_ir::op;
412pub use fission_ir::op::{
413    BoxAlignment, BoxGridPlacement, BoxPosition, BoxStyle, GridPlacement, GridTrack, Length,
414    Overflow,
415};
416pub use fission_ir::{EmbedKind, FocusPolicy, Op, Role, Semantics, WidgetId};
417pub use fission_layout::{
418    BoxConstraints, FlexDirection, LayoutEngine, LayoutInspection, LayoutNodeGeometry, LayoutOp,
419    LayoutPoint, LayoutRect, LayoutSize, LayoutSnapshot, LayoutUnit, TextMeasurer,
420};
421pub use platform::{
422    CancelAllNotificationsCapability, CancelNotificationCapability, CancelNotificationRequest,
423    DeepLink, DeepLinkConfig, DeepLinkReceived, DeepLinkSource, GetNotificationSettingsCapability,
424    NotificationActionButton, NotificationError, NotificationId, NotificationPermission,
425    NotificationPermissionRequest, NotificationReceipt, NotificationRequest, NotificationResponse,
426    NotificationResponseReceived, NotificationSchedule, NotificationSettings, NotificationSound,
427    PushPlatform, PushRegistration, PushRegistrationRequest, RegisterPushNotificationsCapability,
428    RequestNotificationPermissionCapability, ScheduleNotificationCapability,
429    SetBadgeCountCapability, SetBadgeCountRequest, ShowNotificationCapability,
430    UnregisterPushNotificationsCapability, CANCEL_ALL_NOTIFICATIONS, CANCEL_NOTIFICATION,
431    GET_NOTIFICATION_SETTINGS, REGISTER_PUSH_NOTIFICATIONS, REQUEST_NOTIFICATION_PERMISSION,
432    SCHEDULE_NOTIFICATION, SET_BADGE_COUNT, SHOW_NOTIFICATION, UNREGISTER_PUSH_NOTIFICATIONS,
433};
434pub use platform_barcode::{
435    BarcodeFormat, BarcodeImageDecodeRequest, BarcodePoint, BarcodeScanRequest, BarcodeScanResult,
436    BarcodeScanResults, BarcodeScannerError, CancelBarcodeScanCapability,
437    DecodeBarcodeImageCapability, ScanBarcodeCapability, CANCEL_BARCODE_SCAN, DECODE_BARCODE_IMAGE,
438    SCAN_BARCODE,
439};
440pub use platform_biometric::{
441    AuthenticateBiometricCapability, BiometricAuthenticateRequest, BiometricAuthenticateResult,
442    BiometricAvailability, BiometricError, BiometricKind, BiometricStrength,
443    CancelBiometricAuthenticationCapability, GetBiometricAvailabilityCapability,
444    AUTHENTICATE_BIOMETRIC, CANCEL_BIOMETRIC_AUTHENTICATION, GET_BIOMETRIC_AVAILABILITY,
445};
446pub use platform_bluetooth::{
447    BluetoothAdvertiseReceipt, BluetoothAdvertiseRequest, BluetoothAvailability,
448    BluetoothConnectRequest, BluetoothConnection, BluetoothDevice, BluetoothDisconnectRequest,
449    BluetoothError, BluetoothMode, BluetoothPermission, BluetoothPermissionRequest,
450    BluetoothReadRequest, BluetoothReadResult, BluetoothScanRequest, BluetoothScanResult,
451    BluetoothStopAdvertiseRequest, BluetoothWriteRequest, ConnectBluetoothDeviceCapability,
452    DisconnectBluetoothDeviceCapability, GetBluetoothAvailabilityCapability,
453    ReadBluetoothCharacteristicCapability, RequestBluetoothPermissionCapability,
454    ScanBluetoothDevicesCapability, StartBluetoothAdvertisingCapability,
455    StopBluetoothAdvertisingCapability, WriteBluetoothCharacteristicCapability,
456    CONNECT_BLUETOOTH_DEVICE, DISCONNECT_BLUETOOTH_DEVICE, GET_BLUETOOTH_AVAILABILITY,
457    READ_BLUETOOTH_CHARACTERISTIC, REQUEST_BLUETOOTH_PERMISSION, SCAN_BLUETOOTH_DEVICES,
458    START_BLUETOOTH_ADVERTISING, STOP_BLUETOOTH_ADVERTISING, WRITE_BLUETOOTH_CHARACTERISTIC,
459};
460pub use platform_camera::{
461    CameraAvailability, CameraCapture, CameraCaptureRequest, CameraDevice, CameraError,
462    CameraFacing, CameraFlashMode, CameraFlashlightRequest, CameraImageFormat, CameraPermission,
463    CameraPermissionRequest, CameraResolution, CancelCameraCaptureCapability,
464    CapturePhotoCapability, GetCameraAvailabilityCapability, RequestCameraPermissionCapability,
465    SetCameraFlashlightCapability, CANCEL_CAMERA_CAPTURE, CAPTURE_PHOTO, GET_CAMERA_AVAILABILITY,
466    REQUEST_CAMERA_PERMISSION, SET_CAMERA_FLASHLIGHT,
467};
468pub use platform_clipboard::{
469    ClearClipboardCapability, ClipboardContent, ClipboardError, ClipboardItem, ClipboardText,
470    ClipboardWriteTextRequest, ReadClipboardContentCapability, ReadClipboardTextCapability,
471    WriteClipboardContentCapability, WriteClipboardTextCapability, CLEAR_CLIPBOARD,
472    READ_CLIPBOARD_CONTENT, READ_CLIPBOARD_TEXT, WRITE_CLIPBOARD_CONTENT, WRITE_CLIPBOARD_TEXT,
473};
474pub use platform_geolocation::{
475    GeolocationError, GeolocationPermission, GeolocationPermissionRequest, GeolocationPosition,
476    GeolocationPositionRequest, GetCurrentPositionCapability, GetGeolocationPermissionCapability,
477    RequestGeolocationPermissionCapability, GET_CURRENT_POSITION, GET_GEOLOCATION_PERMISSION,
478    REQUEST_GEOLOCATION_PERMISSION,
479};
480pub use platform_haptics::{
481    HapticError, HapticImpactCapability, HapticImpactRequest, HapticImpactStyle,
482    HapticNotificationCapability, HapticNotificationKind, HapticNotificationRequest,
483    HapticPatternCapability, HapticPatternRequest, HapticPatternStep, HapticSelectionCapability,
484    HAPTIC_IMPACT, HAPTIC_NOTIFICATION, HAPTIC_PATTERN, HAPTIC_SELECTION,
485};
486pub use platform_microphone::{
487    AudioSampleFormat, CancelMicrophoneCaptureCapability, CaptureMicrophoneAudioCapability,
488    GetMicrophoneAvailabilityCapability, MicrophoneAvailability, MicrophoneCapture,
489    MicrophoneCaptureRequest, MicrophoneDevice, MicrophoneError, MicrophonePermission,
490    MicrophonePermissionRequest, RequestMicrophonePermissionCapability, CANCEL_MICROPHONE_CAPTURE,
491    CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY, REQUEST_MICROPHONE_PERMISSION,
492};
493pub use platform_nfc::{
494    CancelNfcSessionCapability, EmulateNfcTagCapability, GetNfcAvailabilityCapability,
495    NfcAvailability, NfcEmulationRequest, NfcError, NfcRecord, NfcRecordTypeNameFormat,
496    NfcScanRequest, NfcSessionReceipt, NfcTag, NfcTagDiscovered, NfcTechnology, NfcWriteRequest,
497    ScanNfcTagCapability, WriteNfcTagCapability, CANCEL_NFC_SESSION, EMULATE_NFC_TAG,
498    GET_NFC_AVAILABILITY, SCAN_NFC_TAG, WRITE_NFC_TAG,
499};
500pub use platform_passkey::{
501    AuthenticatePasskeyCapability, CancelPasskeyOperationCapability,
502    GetPasskeyAvailabilityCapability, PasskeyAlgorithm, PasskeyAttestationConveyance,
503    PasskeyAuthenticationRequest, PasskeyAuthenticationResult, PasskeyAuthenticatorAttachment,
504    PasskeyAuthenticatorSelection, PasskeyAvailability, PasskeyCredentialDescriptor, PasskeyError,
505    PasskeyMediation, PasskeyRegistrationRequest, PasskeyRegistrationResult, PasskeyRelyingParty,
506    PasskeyResidentKeyRequirement, PasskeyTransport, PasskeyUser, PasskeyUserVerification,
507    RegisterPasskeyCapability, AUTHENTICATE_PASSKEY, CANCEL_PASSKEY_OPERATION,
508    GET_PASSKEY_AVAILABILITY, REGISTER_PASSKEY,
509};
510pub use platform_volume::{
511    AdjustVolumeLevelCapability, GetVolumeLevelCapability, SetVolumeLevelCapability,
512    VolumeAdjustDirection, VolumeAdjustRequest, VolumeError, VolumeLevel, VolumeSetRequest,
513    VolumeStream, ADJUST_VOLUME_LEVEL, GET_VOLUME_LEVEL, SET_VOLUME_LEVEL,
514};
515pub use platform_wifi::{
516    ConnectWifiNetworkCapability, DisconnectWifiNetworkCapability, GetWifiAvailabilityCapability,
517    RequestWifiPermissionCapability, ScanWifiNetworksCapability, WifiAvailability,
518    WifiConnectRequest, WifiConnection, WifiDisconnectRequest, WifiError, WifiNetwork,
519    WifiPermission, WifiPermissionRequest, WifiScanRequest, WifiScanResult, WifiSecurity,
520    CONNECT_WIFI_NETWORK, DISCONNECT_WIFI_NETWORK, GET_WIFI_AVAILABILITY, REQUEST_WIFI_PERMISSION,
521    SCAN_WIFI_NETWORKS,
522};
523pub use registry::{
524    ActionRegistry, Handler, JobResource, PortalLayer, ResourceKey, ResourcePolicy,
525    ResourceRegistry, RuntimeResourceDeclaration, RuntimeResourceKind, ServiceResource,
526    TimerResource, VideoRegistration,
527};
528pub use time::{Clock, CurrentTime};
529pub use ui::{
530    provider, ActionScope, BadgeTone, Button, ButtonHierarchy, ButtonMotion, CardPattern, Column,
531    ComponentSize, ComponentState, CustomWidget, IosAudioSessionCategory,
532    IosAudioSessionCategoryOption, IosAudioSessionMode, IosVideoAudioOptions, Pressable,
533    PressableRole, PressableStyle, Provider, Responsive, ResponsiveCase, ResponsiveQuery, Row,
534    Text, Video, VideoAudioActivation, VideoAudioOptions, VideoAudioPolicy, VideoSource, Widget,
535    WidgetIdExt,
536};
537pub use view::{ComputedView, FissionViewField, Selector, ValueView, View};
538
539/// Coerces a reducer function item or non-capturing closure to the handler
540/// function-pointer type Rust can infer from the surrounding `ctx.bind(...)`
541/// call.
542///
543/// ```rust,ignore
544/// use fission::prelude::*;
545///
546/// let on_press = with_reducer!(ctx, Increment, on_increment);
547/// ```
548#[macro_export]
549macro_rules! reduce_with {
550    ($handler:expr $(,)?) => {
551        $handler as $crate::Handler<_, _>
552    };
553}
554
555/// Short alias for [`reduce_with!`].
556#[macro_export]
557macro_rules! reduce {
558    ($handler:expr $(,)?) => {
559        $crate::reduce_with!($handler)
560    };
561}
562
563/// Builds a `Vec<Widget>` from widget expressions without repeated `.into()` calls.
564///
565/// Dynamic children may still be produced with normal iterators and
566/// `collect::<Vec<Widget>>()`; this macro is only syntax sugar for
567/// literal child lists.
568#[macro_export]
569macro_rules! widgets {
570    ($($widget:expr),* $(,)?) => {
571        {
572            let mut widgets = ::std::vec::Vec::<$crate::Widget>::new();
573            $(
574                widgets.push($crate::Widget::from($widget));
575            )*
576            widgets
577        }
578    };
579}
580
581/// Creates a [`Video`](crate::ui::Video) from an app asset literal and fails
582/// compilation when the asset does not exist under `CARGO_MANIFEST_DIR`.
583///
584/// Use [`Video::asset`](crate::ui::Video::asset) when the path is computed at
585/// runtime and cannot be checked by the compiler.
586#[macro_export]
587macro_rules! video_asset {
588    ($path:literal $(,)?) => {{
589        const _: &[u8] = include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path));
590        $crate::ui::Video::asset($path)
591    }};
592}
593
594/// Creates a [`Video`](crate::ui::Video) from a compile-time local file path
595/// and fails compilation when that path cannot be resolved by `include_bytes!`.
596///
597/// Relative paths are resolved the same way as `include_bytes!`: relative to the
598/// source file that invokes this macro.
599#[macro_export]
600macro_rules! video_file {
601    ($path:expr $(,)?) => {{
602        const _: &[u8] = include_bytes!($path);
603        $crate::ui::Video::file($path)
604    }};
605}
606
607/// Creates a [`Video`](crate::ui::Video) from a network URL literal.
608///
609/// Network playback support is shell-specific; use this helper for literal URLs
610/// and [`Video::network`](crate::ui::Video::network) when the URL is computed at
611/// runtime.
612#[macro_export]
613macro_rules! video_network {
614    ($url:literal $(,)?) => {{
615        $crate::ui::Video::network($url)
616    }};
617}
618
619/// Binds an action to a reducer in one expression.
620///
621/// ```rust,ignore
622/// use fission::prelude::*;
623///
624/// let on_press = with_reducer!(ctx, Increment, on_increment);
625/// ```
626#[macro_export]
627macro_rules! with_reducer {
628    ($ctx:expr, $action:expr, $handler:expr $(,)?) => {
629        $ctx.bind($action, $crate::reduce_with!($handler))
630    };
631}
632
633/// A frame-tick action that advances the runtime clock by a delta.
634///
635/// The platform shell dispatches `Tick` once per frame so that animations,
636/// timers, and other time-dependent logic can progress.
637///
638/// # Example
639///
640/// ```rust,ignore
641/// // Advance the runtime by 16 ms (~60 fps)
642/// runtime.tick(16)?;
643/// ```
644#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
645pub struct Tick {
646    /// Delta time in milliseconds since the last tick.
647    pub dt: CurrentTime,
648}
649
650impl Action for Tick {
651    fn static_id() -> ActionId {
652        *TICK_ACTION_ID
653    }
654}
655
656lazy_static! {
657    pub static ref TICK_ACTION_ID: ActionId = ActionId::from_name("fission_core::Tick");
658}
659
660/// An action that sets the runtime clock to an absolute timestamp.
661///
662/// Unlike [`Tick`] which advances by a delta, `AdvanceTo` jumps directly to
663/// the given time. Useful for testing and deterministic replay.
664///
665/// # Example
666///
667/// ```rust,ignore
668/// let envelope: ActionEnvelope = AdvanceTo { time: 5000 }.into();
669/// runtime.dispatch(envelope, WidgetId::from_u128(0))?;
670/// ```
671#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
672pub struct AdvanceTo {
673    /// The absolute time (in milliseconds) to set the clock to.
674    pub time: CurrentTime,
675}
676
677impl Action for AdvanceTo {
678    fn static_id() -> ActionId {
679        *ADVANCE_TO_ACTION_ID
680    }
681}
682
683lazy_static! {
684    pub static ref ADVANCE_TO_ACTION_ID: ActionId = ActionId::from_name("fission_core::AdvanceTo");
685}
686
687/// A type-erased reducer function stored in the [`Runtime`].
688///
689/// `BoxedReducer` is the internal representation used by the runtime to invoke
690/// reducers without knowing the concrete `GlobalState` or `Action` types.
691pub(crate) type BoxedReducer = Box<
692    dyn FnMut(
693            &mut HashMap<TypeId, Box<dyn GlobalState>>,
694            &ActionEnvelope,
695            WidgetId,
696            &mut Vec<EffectEnvelope>,
697            &ActionInput,
698            &Arc<EffectCallbackRegistry>,
699        ) -> Result<()>
700        + Send
701        + Sync,
702>;
703
704/// One-shot reducers bound to async effect completion actions.
705///
706/// These reducers are separate from the per-frame widget registry so a
707/// completion remains deliverable after the frame that issued the effect.
708pub(crate) struct EffectCallbackRegistry {
709    reducers: Mutex<HashMap<ActionId, Vec<BoxedReducer>>>,
710}
711
712impl EffectCallbackRegistry {
713    pub(crate) fn new() -> Self {
714        Self {
715            reducers: Mutex::new(HashMap::new()),
716        }
717    }
718
719    pub(crate) fn register(&self, action_id: ActionId, reducer: BoxedReducer) {
720        self.reducers
721            .lock()
722            .unwrap_or_else(std::sync::PoisonError::into_inner)
723            .entry(action_id)
724            .or_default()
725            .push(reducer);
726    }
727
728    pub(crate) fn take(&self, action_id: ActionId) -> Vec<BoxedReducer> {
729        self.reducers
730            .lock()
731            .unwrap_or_else(std::sync::PoisonError::into_inner)
732            .remove(&action_id)
733            .unwrap_or_default()
734    }
735}