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, Provider, Row, Text, Widget,
348        WidgetIdExt,
349    };
350    pub use crate::view::{ComputedView, FissionViewField, Selector, ValueView, View};
351    pub use crate::{reduce, reduce_with, widgets, with_reducer};
352    pub use fission_ir::op;
353    pub use fission_ir::{EmbedKind, FocusPolicy, Op, Role, Semantics, WidgetId};
354    pub use fission_layout::{
355        BoxConstraints, FlexDirection, LayoutEngine, LayoutOp, LayoutPoint, LayoutRect, LayoutSize,
356        LayoutSnapshot, LayoutUnit, TextMeasurer,
357    };
358}
359
360#[cfg(test)]
361mod tests;
362
363pub use action::{Action, ActionEnvelope, ActionId, ActionScopeId, GlobalState, ShellRouteChanged};
364pub use async_runtime::{
365    BoxFuture, JobCtx, JobRef, JobSpec, ResourceExecutionContext, ServiceBindings, ServiceCtx,
366    ServiceRunner, ServiceSlot, ServiceSpec, ServiceType,
367};
368pub use bytes::Bytes;
369pub use capability::{
370    CapabilityCtx, CapabilityInvocationPayload, CapabilityType, OpenUrlCapability, OpenUrlRequest,
371    OperationCapability, PickOpenFilesCapability, PickOpenFilesError, PickOpenFilesRequest,
372    PickOpenFilesResult, PickedFile, OPEN_URL, PICK_OPEN_FILES,
373};
374pub use context::{
375    BarcodeScannerEffects, BiometricEffects, BluetoothEffects, CameraEffects, ClipboardEffects,
376    Effects, GeolocationEffects, HapticEffects, MicrophoneEffects, NfcEffects, NotificationEffects,
377    PasskeyEffects, ReducerContext, VolumeEffects, WifiEffects,
378}; // New
379pub use data_stream::{
380    collect_data_stream, empty_data_stream, single_chunk_data_stream, BoxFissionDataStream,
381    DataStreamId, DataStreamRegistry, FissionDataStream, FissionDataStreamError,
382    FissionDataStreamErrorKind,
383};
384pub use effect::{
385    ActionInput, Effect, EffectEnvelope, RuntimeEffect, ScrollAlignment, ScrollAxis,
386    ScrollBehavior, ScrollIntoViewRequest,
387};
388pub use env::{
389    Clipboard, Env, ImeHandler, InteractionStateMap, RouteLocation, RuntimeState, ScrollStateMap,
390    WindowEnv, WindowTitle,
391};
392pub use motion::*;
393pub use runtime::Runtime;
394pub use state::{LocalStateKey, LocalStateStore, StateField};
395
396pub use build::{BuildCtxHandle, ViewHandle};
397pub use event::{InputEvent, KeyCode, KeyEvent, LifecycleEvent, PointerButton, PointerEvent};
398pub use fission_ir::op;
399pub use fission_ir::{EmbedKind, FocusPolicy, Op, Role, Semantics, WidgetId};
400pub use fission_layout::{
401    BoxConstraints, FlexDirection, LayoutEngine, LayoutOp, LayoutPoint, LayoutRect, LayoutSize,
402    LayoutSnapshot, LayoutUnit, TextMeasurer,
403};
404pub use platform::{
405    CancelAllNotificationsCapability, CancelNotificationCapability, CancelNotificationRequest,
406    DeepLink, DeepLinkConfig, DeepLinkReceived, DeepLinkSource, GetNotificationSettingsCapability,
407    NotificationActionButton, NotificationError, NotificationId, NotificationPermission,
408    NotificationPermissionRequest, NotificationReceipt, NotificationRequest, NotificationResponse,
409    NotificationResponseReceived, NotificationSchedule, NotificationSettings, NotificationSound,
410    PushPlatform, PushRegistration, PushRegistrationRequest, RegisterPushNotificationsCapability,
411    RequestNotificationPermissionCapability, ScheduleNotificationCapability,
412    SetBadgeCountCapability, SetBadgeCountRequest, ShowNotificationCapability,
413    UnregisterPushNotificationsCapability, CANCEL_ALL_NOTIFICATIONS, CANCEL_NOTIFICATION,
414    GET_NOTIFICATION_SETTINGS, REGISTER_PUSH_NOTIFICATIONS, REQUEST_NOTIFICATION_PERMISSION,
415    SCHEDULE_NOTIFICATION, SET_BADGE_COUNT, SHOW_NOTIFICATION, UNREGISTER_PUSH_NOTIFICATIONS,
416};
417pub use platform_barcode::{
418    BarcodeFormat, BarcodeImageDecodeRequest, BarcodePoint, BarcodeScanRequest, BarcodeScanResult,
419    BarcodeScanResults, BarcodeScannerError, CancelBarcodeScanCapability,
420    DecodeBarcodeImageCapability, ScanBarcodeCapability, CANCEL_BARCODE_SCAN, DECODE_BARCODE_IMAGE,
421    SCAN_BARCODE,
422};
423pub use platform_biometric::{
424    AuthenticateBiometricCapability, BiometricAuthenticateRequest, BiometricAuthenticateResult,
425    BiometricAvailability, BiometricError, BiometricKind, BiometricStrength,
426    CancelBiometricAuthenticationCapability, GetBiometricAvailabilityCapability,
427    AUTHENTICATE_BIOMETRIC, CANCEL_BIOMETRIC_AUTHENTICATION, GET_BIOMETRIC_AVAILABILITY,
428};
429pub use platform_bluetooth::{
430    BluetoothAdvertiseReceipt, BluetoothAdvertiseRequest, BluetoothAvailability,
431    BluetoothConnectRequest, BluetoothConnection, BluetoothDevice, BluetoothDisconnectRequest,
432    BluetoothError, BluetoothMode, BluetoothPermission, BluetoothPermissionRequest,
433    BluetoothReadRequest, BluetoothReadResult, BluetoothScanRequest, BluetoothScanResult,
434    BluetoothStopAdvertiseRequest, BluetoothWriteRequest, ConnectBluetoothDeviceCapability,
435    DisconnectBluetoothDeviceCapability, GetBluetoothAvailabilityCapability,
436    ReadBluetoothCharacteristicCapability, RequestBluetoothPermissionCapability,
437    ScanBluetoothDevicesCapability, StartBluetoothAdvertisingCapability,
438    StopBluetoothAdvertisingCapability, WriteBluetoothCharacteristicCapability,
439    CONNECT_BLUETOOTH_DEVICE, DISCONNECT_BLUETOOTH_DEVICE, GET_BLUETOOTH_AVAILABILITY,
440    READ_BLUETOOTH_CHARACTERISTIC, REQUEST_BLUETOOTH_PERMISSION, SCAN_BLUETOOTH_DEVICES,
441    START_BLUETOOTH_ADVERTISING, STOP_BLUETOOTH_ADVERTISING, WRITE_BLUETOOTH_CHARACTERISTIC,
442};
443pub use platform_camera::{
444    CameraAvailability, CameraCapture, CameraCaptureRequest, CameraDevice, CameraError,
445    CameraFacing, CameraFlashMode, CameraFlashlightRequest, CameraImageFormat, CameraPermission,
446    CameraPermissionRequest, CameraResolution, CancelCameraCaptureCapability,
447    CapturePhotoCapability, GetCameraAvailabilityCapability, RequestCameraPermissionCapability,
448    SetCameraFlashlightCapability, CANCEL_CAMERA_CAPTURE, CAPTURE_PHOTO, GET_CAMERA_AVAILABILITY,
449    REQUEST_CAMERA_PERMISSION, SET_CAMERA_FLASHLIGHT,
450};
451pub use platform_clipboard::{
452    ClearClipboardCapability, ClipboardContent, ClipboardError, ClipboardItem, ClipboardText,
453    ClipboardWriteTextRequest, ReadClipboardContentCapability, ReadClipboardTextCapability,
454    WriteClipboardContentCapability, WriteClipboardTextCapability, CLEAR_CLIPBOARD,
455    READ_CLIPBOARD_CONTENT, READ_CLIPBOARD_TEXT, WRITE_CLIPBOARD_CONTENT, WRITE_CLIPBOARD_TEXT,
456};
457pub use platform_geolocation::{
458    GeolocationError, GeolocationPermission, GeolocationPermissionRequest, GeolocationPosition,
459    GeolocationPositionRequest, GetCurrentPositionCapability, GetGeolocationPermissionCapability,
460    RequestGeolocationPermissionCapability, GET_CURRENT_POSITION, GET_GEOLOCATION_PERMISSION,
461    REQUEST_GEOLOCATION_PERMISSION,
462};
463pub use platform_haptics::{
464    HapticError, HapticImpactCapability, HapticImpactRequest, HapticImpactStyle,
465    HapticNotificationCapability, HapticNotificationKind, HapticNotificationRequest,
466    HapticPatternCapability, HapticPatternRequest, HapticPatternStep, HapticSelectionCapability,
467    HAPTIC_IMPACT, HAPTIC_NOTIFICATION, HAPTIC_PATTERN, HAPTIC_SELECTION,
468};
469pub use platform_microphone::{
470    AudioSampleFormat, CancelMicrophoneCaptureCapability, CaptureMicrophoneAudioCapability,
471    GetMicrophoneAvailabilityCapability, MicrophoneAvailability, MicrophoneCapture,
472    MicrophoneCaptureRequest, MicrophoneDevice, MicrophoneError, MicrophonePermission,
473    MicrophonePermissionRequest, RequestMicrophonePermissionCapability, CANCEL_MICROPHONE_CAPTURE,
474    CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY, REQUEST_MICROPHONE_PERMISSION,
475};
476pub use platform_nfc::{
477    CancelNfcSessionCapability, EmulateNfcTagCapability, GetNfcAvailabilityCapability,
478    NfcAvailability, NfcEmulationRequest, NfcError, NfcRecord, NfcRecordTypeNameFormat,
479    NfcScanRequest, NfcSessionReceipt, NfcTag, NfcTagDiscovered, NfcTechnology, NfcWriteRequest,
480    ScanNfcTagCapability, WriteNfcTagCapability, CANCEL_NFC_SESSION, EMULATE_NFC_TAG,
481    GET_NFC_AVAILABILITY, SCAN_NFC_TAG, WRITE_NFC_TAG,
482};
483pub use platform_passkey::{
484    AuthenticatePasskeyCapability, CancelPasskeyOperationCapability,
485    GetPasskeyAvailabilityCapability, PasskeyAlgorithm, PasskeyAttestationConveyance,
486    PasskeyAuthenticationRequest, PasskeyAuthenticationResult, PasskeyAuthenticatorAttachment,
487    PasskeyAuthenticatorSelection, PasskeyAvailability, PasskeyCredentialDescriptor, PasskeyError,
488    PasskeyMediation, PasskeyRegistrationRequest, PasskeyRegistrationResult, PasskeyRelyingParty,
489    PasskeyResidentKeyRequirement, PasskeyTransport, PasskeyUser, PasskeyUserVerification,
490    RegisterPasskeyCapability, AUTHENTICATE_PASSKEY, CANCEL_PASSKEY_OPERATION,
491    GET_PASSKEY_AVAILABILITY, REGISTER_PASSKEY,
492};
493pub use platform_volume::{
494    AdjustVolumeLevelCapability, GetVolumeLevelCapability, SetVolumeLevelCapability,
495    VolumeAdjustDirection, VolumeAdjustRequest, VolumeError, VolumeLevel, VolumeSetRequest,
496    VolumeStream, ADJUST_VOLUME_LEVEL, GET_VOLUME_LEVEL, SET_VOLUME_LEVEL,
497};
498pub use platform_wifi::{
499    ConnectWifiNetworkCapability, DisconnectWifiNetworkCapability, GetWifiAvailabilityCapability,
500    RequestWifiPermissionCapability, ScanWifiNetworksCapability, WifiAvailability,
501    WifiConnectRequest, WifiConnection, WifiDisconnectRequest, WifiError, WifiNetwork,
502    WifiPermission, WifiPermissionRequest, WifiScanRequest, WifiScanResult, WifiSecurity,
503    CONNECT_WIFI_NETWORK, DISCONNECT_WIFI_NETWORK, GET_WIFI_AVAILABILITY, REQUEST_WIFI_PERMISSION,
504    SCAN_WIFI_NETWORKS,
505};
506pub use registry::{
507    ActionRegistry, Handler, JobResource, PortalLayer, ResourceKey, ResourcePolicy,
508    ResourceRegistry, RuntimeResourceDeclaration, RuntimeResourceKind, ServiceResource,
509    TimerResource, VideoRegistration,
510};
511pub use time::{Clock, CurrentTime};
512pub use ui::{
513    provider, ActionScope, BadgeTone, Button, ButtonHierarchy, ButtonMotion, CardPattern, Column,
514    ComponentSize, ComponentState, CustomWidget, Provider, Row, Text, Widget, WidgetIdExt,
515};
516pub use view::{ComputedView, FissionViewField, Selector, ValueView, View};
517
518/// Coerces a reducer function item or non-capturing closure to the handler
519/// function-pointer type Rust can infer from the surrounding `ctx.bind(...)`
520/// call.
521///
522/// ```rust,ignore
523/// use fission::prelude::*;
524///
525/// let on_press = with_reducer!(ctx, Increment, on_increment);
526/// ```
527#[macro_export]
528macro_rules! reduce_with {
529    ($handler:expr $(,)?) => {
530        $handler as $crate::Handler<_, _>
531    };
532}
533
534/// Short alias for [`reduce_with!`].
535#[macro_export]
536macro_rules! reduce {
537    ($handler:expr $(,)?) => {
538        $crate::reduce_with!($handler)
539    };
540}
541
542/// Builds a `Vec<Widget>` from widget expressions without repeated `.into()` calls.
543///
544/// Dynamic children may still be produced with normal iterators and
545/// `collect::<Vec<Widget>>()`; this macro is only syntax sugar for
546/// literal child lists.
547#[macro_export]
548macro_rules! widgets {
549    ($($widget:expr),* $(,)?) => {
550        {
551            let mut widgets = ::std::vec::Vec::<$crate::Widget>::new();
552            $(
553                widgets.push($crate::Widget::from($widget));
554            )*
555            widgets
556        }
557    };
558}
559
560/// Binds an action to a reducer in one expression.
561///
562/// ```rust,ignore
563/// use fission::prelude::*;
564///
565/// let on_press = with_reducer!(ctx, Increment, on_increment);
566/// ```
567#[macro_export]
568macro_rules! with_reducer {
569    ($ctx:expr, $action:expr, $handler:expr $(,)?) => {
570        $ctx.bind($action, $crate::reduce_with!($handler))
571    };
572}
573
574/// A frame-tick action that advances the runtime clock by a delta.
575///
576/// The platform shell dispatches `Tick` once per frame so that animations,
577/// timers, and other time-dependent logic can progress.
578///
579/// # Example
580///
581/// ```rust,ignore
582/// // Advance the runtime by 16 ms (~60 fps)
583/// runtime.tick(16)?;
584/// ```
585#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
586pub struct Tick {
587    /// Delta time in milliseconds since the last tick.
588    pub dt: CurrentTime,
589}
590
591impl Action for Tick {
592    fn static_id() -> ActionId {
593        *TICK_ACTION_ID
594    }
595}
596
597lazy_static! {
598    pub static ref TICK_ACTION_ID: ActionId = ActionId::from_name("fission_core::Tick");
599}
600
601/// An action that sets the runtime clock to an absolute timestamp.
602///
603/// Unlike [`Tick`] which advances by a delta, `AdvanceTo` jumps directly to
604/// the given time. Useful for testing and deterministic replay.
605///
606/// # Example
607///
608/// ```rust,ignore
609/// let envelope: ActionEnvelope = AdvanceTo { time: 5000 }.into();
610/// runtime.dispatch(envelope, WidgetId::from_u128(0))?;
611/// ```
612#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
613pub struct AdvanceTo {
614    /// The absolute time (in milliseconds) to set the clock to.
615    pub time: CurrentTime,
616}
617
618impl Action for AdvanceTo {
619    fn static_id() -> ActionId {
620        *ADVANCE_TO_ACTION_ID
621    }
622}
623
624lazy_static! {
625    pub static ref ADVANCE_TO_ACTION_ID: ActionId = ActionId::from_name("fission_core::AdvanceTo");
626}
627
628/// A type-erased reducer function stored in the [`Runtime`].
629///
630/// `BoxedReducer` is the internal representation used by the runtime to invoke
631/// reducers without knowing the concrete `GlobalState` or `Action` types.
632pub(crate) type BoxedReducer = Box<
633    dyn FnMut(
634            &mut HashMap<TypeId, Box<dyn GlobalState>>,
635            &ActionEnvelope,
636            WidgetId,
637            &mut Vec<EffectEnvelope>,
638            &ActionInput,
639        ) -> Result<()>
640        + Send
641        + Sync,
642>;