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