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 ([`Node`], [`Widget`]).
9//! - A **unidirectional data-flow** pipeline: [`Action`] -> [`Runtime::dispatch`] -> reducer
10//!   -> mutated [`AppState`].
11//! - An **effect system** for async side-effects ([`Effect`], [`RuntimeEffect`]).
12//! - Built-in widgets: [`Button`], [`Text`], [`TextInput`], [`Container`], [`Row`],
13//!   [`Column`], [`Scroll`], [`ZStack`], [`Grid`], [`LazyColumn`], and more.
14//!
15//! ## Getting started
16//!
17//! ```rust,ignore
18//! use fission_core::*;
19//! use fission_core::ui::*;
20//!
21//! // Define application state
22//! #[derive(Debug, Default)]
23//! struct MyState { value: String }
24//! impl AppState for MyState {}
25//!
26//! // Build a widget
27//! struct MyWidget;
28//! impl Widget<MyState> for MyWidget {
29//!     fn build(&self, ctx: &mut BuildCtx<MyState>, view: &View<MyState>) -> Node {
30//!         Text::new(&*view.state.value).into_node()
31//!     }
32//! }
33//! ```
34
35use anyhow::Result;
36use lazy_static::lazy_static;
37use std::any::TypeId;
38use std::collections::HashMap;
39
40extern crate self as fission_core;
41
42pub mod action;
43pub mod async_runtime;
44pub mod capability; // New
45pub mod context; // New
46pub mod diff;
47pub mod effect; // New
48pub mod env;
49pub mod event;
50pub mod hit_test;
51pub mod input;
52pub mod lowering;
53pub mod media;
54pub mod platform;
55pub mod platform_barcode;
56pub mod platform_biometric;
57pub mod platform_bluetooth;
58pub mod platform_camera;
59pub mod platform_clipboard;
60pub mod platform_geolocation;
61pub mod platform_haptics;
62pub mod platform_microphone;
63pub mod platform_nfc;
64pub mod platform_passkey;
65pub mod platform_volume;
66pub mod platform_wifi;
67pub mod registry;
68pub mod runtime;
69pub mod scrollbar;
70pub mod time;
71pub mod ui;
72
73pub mod view;
74
75#[cfg(test)]
76mod tests;
77
78pub use action::{Action, ActionEnvelope, ActionId, ActionScopeId, AppState};
79pub use async_runtime::{
80    BoxFuture, JobCtx, JobRef, JobSpec, ResourceExecutionContext, ServiceBindings, ServiceCtx,
81    ServiceRunner, ServiceSlot, ServiceSpec, ServiceType,
82};
83pub use capability::{
84    CapabilityCtx, CapabilityInvocationPayload, CapabilityType, OpenUrlCapability, OpenUrlRequest,
85    OperationCapability, PickOpenFilesCapability, PickOpenFilesError, PickOpenFilesRequest,
86    PickOpenFilesResult, PickedFile, OPEN_URL, PICK_OPEN_FILES,
87};
88pub use context::{
89    BarcodeScannerEffects, BiometricEffects, BluetoothEffects, CameraEffects, ClipboardEffects,
90    Effects, GeolocationEffects, HapticEffects, MicrophoneEffects, NfcEffects, NotificationEffects,
91    PasskeyEffects, ReducerContext, VolumeEffects, WifiEffects,
92}; // New
93pub use effect::{ActionInput, Effect, EffectEnvelope, RuntimeEffect};
94pub use env::{
95    Clipboard, Env, ImeHandler, InteractionStateMap, RuntimeState, ScrollStateMap, WindowEnv,
96    WindowTitle,
97};
98pub use runtime::Runtime;
99
100pub use event::{InputEvent, KeyCode, KeyEvent, LifecycleEvent, PointerButton, PointerEvent};
101pub use fission_ir::op;
102pub use fission_ir::{EmbedKind, NodeId, Op, WidgetNodeId};
103pub use fission_layout::{
104    BoxConstraints, FlexDirection, LayoutEngine, LayoutOp, LayoutPoint, LayoutRect, LayoutSize,
105    LayoutSnapshot, LayoutUnit, TextMeasurer,
106};
107pub use lowering::{LoweringContext, NodeBuilder};
108pub use platform::{
109    CancelAllNotificationsCapability, CancelNotificationCapability, CancelNotificationRequest,
110    DeepLink, DeepLinkConfig, DeepLinkReceived, DeepLinkSource, GetNotificationSettingsCapability,
111    NotificationActionButton, NotificationError, NotificationId, NotificationPermission,
112    NotificationPermissionRequest, NotificationReceipt, NotificationRequest, NotificationResponse,
113    NotificationResponseReceived, NotificationSchedule, NotificationSettings, NotificationSound,
114    PushPlatform, PushRegistration, PushRegistrationRequest, RegisterPushNotificationsCapability,
115    RequestNotificationPermissionCapability, ScheduleNotificationCapability,
116    SetBadgeCountCapability, SetBadgeCountRequest, ShowNotificationCapability,
117    UnregisterPushNotificationsCapability, CANCEL_ALL_NOTIFICATIONS, CANCEL_NOTIFICATION,
118    GET_NOTIFICATION_SETTINGS, REGISTER_PUSH_NOTIFICATIONS, REQUEST_NOTIFICATION_PERMISSION,
119    SCHEDULE_NOTIFICATION, SET_BADGE_COUNT, SHOW_NOTIFICATION, UNREGISTER_PUSH_NOTIFICATIONS,
120};
121pub use platform_barcode::{
122    BarcodeFormat, BarcodeImageDecodeRequest, BarcodePoint, BarcodeScanRequest, BarcodeScanResult,
123    BarcodeScanResults, BarcodeScannerError, CancelBarcodeScanCapability,
124    DecodeBarcodeImageCapability, ScanBarcodeCapability, CANCEL_BARCODE_SCAN, DECODE_BARCODE_IMAGE,
125    SCAN_BARCODE,
126};
127pub use platform_biometric::{
128    AuthenticateBiometricCapability, BiometricAuthenticateRequest, BiometricAuthenticateResult,
129    BiometricAvailability, BiometricError, BiometricKind, BiometricStrength,
130    CancelBiometricAuthenticationCapability, GetBiometricAvailabilityCapability,
131    AUTHENTICATE_BIOMETRIC, CANCEL_BIOMETRIC_AUTHENTICATION, GET_BIOMETRIC_AVAILABILITY,
132};
133pub use platform_bluetooth::{
134    BluetoothAdvertiseReceipt, BluetoothAdvertiseRequest, BluetoothAvailability,
135    BluetoothConnectRequest, BluetoothConnection, BluetoothDevice, BluetoothDisconnectRequest,
136    BluetoothError, BluetoothMode, BluetoothPermission, BluetoothPermissionRequest,
137    BluetoothReadRequest, BluetoothReadResult, BluetoothScanRequest, BluetoothScanResult,
138    BluetoothStopAdvertiseRequest, BluetoothWriteRequest, ConnectBluetoothDeviceCapability,
139    DisconnectBluetoothDeviceCapability, GetBluetoothAvailabilityCapability,
140    ReadBluetoothCharacteristicCapability, RequestBluetoothPermissionCapability,
141    ScanBluetoothDevicesCapability, StartBluetoothAdvertisingCapability,
142    StopBluetoothAdvertisingCapability, WriteBluetoothCharacteristicCapability,
143    CONNECT_BLUETOOTH_DEVICE, DISCONNECT_BLUETOOTH_DEVICE, GET_BLUETOOTH_AVAILABILITY,
144    READ_BLUETOOTH_CHARACTERISTIC, REQUEST_BLUETOOTH_PERMISSION, SCAN_BLUETOOTH_DEVICES,
145    START_BLUETOOTH_ADVERTISING, STOP_BLUETOOTH_ADVERTISING, WRITE_BLUETOOTH_CHARACTERISTIC,
146};
147pub use platform_camera::{
148    CameraAvailability, CameraCapture, CameraCaptureRequest, CameraDevice, CameraError,
149    CameraFacing, CameraFlashMode, CameraFlashlightRequest, CameraImageFormat, CameraPermission,
150    CameraPermissionRequest, CameraResolution, CancelCameraCaptureCapability,
151    CapturePhotoCapability, GetCameraAvailabilityCapability, RequestCameraPermissionCapability,
152    SetCameraFlashlightCapability, CANCEL_CAMERA_CAPTURE, CAPTURE_PHOTO, GET_CAMERA_AVAILABILITY,
153    REQUEST_CAMERA_PERMISSION, SET_CAMERA_FLASHLIGHT,
154};
155pub use platform_clipboard::{
156    ClearClipboardCapability, ClipboardContent, ClipboardError, ClipboardItem, ClipboardText,
157    ClipboardWriteTextRequest, ReadClipboardContentCapability, ReadClipboardTextCapability,
158    WriteClipboardContentCapability, WriteClipboardTextCapability, CLEAR_CLIPBOARD,
159    READ_CLIPBOARD_CONTENT, READ_CLIPBOARD_TEXT, WRITE_CLIPBOARD_CONTENT, WRITE_CLIPBOARD_TEXT,
160};
161pub use platform_geolocation::{
162    GeolocationError, GeolocationPermission, GeolocationPermissionRequest, GeolocationPosition,
163    GeolocationPositionRequest, GetCurrentPositionCapability, GetGeolocationPermissionCapability,
164    RequestGeolocationPermissionCapability, GET_CURRENT_POSITION, GET_GEOLOCATION_PERMISSION,
165    REQUEST_GEOLOCATION_PERMISSION,
166};
167pub use platform_haptics::{
168    HapticError, HapticImpactCapability, HapticImpactRequest, HapticImpactStyle,
169    HapticNotificationCapability, HapticNotificationKind, HapticNotificationRequest,
170    HapticPatternCapability, HapticPatternRequest, HapticPatternStep, HapticSelectionCapability,
171    HAPTIC_IMPACT, HAPTIC_NOTIFICATION, HAPTIC_PATTERN, HAPTIC_SELECTION,
172};
173pub use platform_microphone::{
174    AudioSampleFormat, CancelMicrophoneCaptureCapability, CaptureMicrophoneAudioCapability,
175    GetMicrophoneAvailabilityCapability, MicrophoneAvailability, MicrophoneCapture,
176    MicrophoneCaptureRequest, MicrophoneDevice, MicrophoneError, MicrophonePermission,
177    MicrophonePermissionRequest, RequestMicrophonePermissionCapability, CANCEL_MICROPHONE_CAPTURE,
178    CAPTURE_MICROPHONE_AUDIO, GET_MICROPHONE_AVAILABILITY, REQUEST_MICROPHONE_PERMISSION,
179};
180pub use platform_nfc::{
181    CancelNfcSessionCapability, EmulateNfcTagCapability, GetNfcAvailabilityCapability,
182    NfcAvailability, NfcEmulationRequest, NfcError, NfcRecord, NfcRecordTypeNameFormat,
183    NfcScanRequest, NfcSessionReceipt, NfcTag, NfcTagDiscovered, NfcTechnology, NfcWriteRequest,
184    ScanNfcTagCapability, WriteNfcTagCapability, CANCEL_NFC_SESSION, EMULATE_NFC_TAG,
185    GET_NFC_AVAILABILITY, SCAN_NFC_TAG, WRITE_NFC_TAG,
186};
187pub use platform_passkey::{
188    AuthenticatePasskeyCapability, CancelPasskeyOperationCapability,
189    GetPasskeyAvailabilityCapability, PasskeyAlgorithm, PasskeyAttestationConveyance,
190    PasskeyAuthenticationRequest, PasskeyAuthenticationResult, PasskeyAuthenticatorAttachment,
191    PasskeyAuthenticatorSelection, PasskeyAvailability, PasskeyCredentialDescriptor, PasskeyError,
192    PasskeyMediation, PasskeyRegistrationRequest, PasskeyRegistrationResult, PasskeyRelyingParty,
193    PasskeyResidentKeyRequirement, PasskeyTransport, PasskeyUser, PasskeyUserVerification,
194    RegisterPasskeyCapability, AUTHENTICATE_PASSKEY, CANCEL_PASSKEY_OPERATION,
195    GET_PASSKEY_AVAILABILITY, REGISTER_PASSKEY,
196};
197pub use platform_volume::{
198    AdjustVolumeLevelCapability, GetVolumeLevelCapability, SetVolumeLevelCapability,
199    VolumeAdjustDirection, VolumeAdjustRequest, VolumeError, VolumeLevel, VolumeSetRequest,
200    VolumeStream, ADJUST_VOLUME_LEVEL, GET_VOLUME_LEVEL, SET_VOLUME_LEVEL,
201};
202pub use platform_wifi::{
203    ConnectWifiNetworkCapability, DisconnectWifiNetworkCapability, GetWifiAvailabilityCapability,
204    RequestWifiPermissionCapability, ScanWifiNetworksCapability, WifiAvailability,
205    WifiConnectRequest, WifiConnection, WifiDisconnectRequest, WifiError, WifiNetwork,
206    WifiPermission, WifiPermissionRequest, WifiScanRequest, WifiScanResult, WifiSecurity,
207    CONNECT_WIFI_NETWORK, DISCONNECT_WIFI_NETWORK, GET_WIFI_AVAILABILITY, REQUEST_WIFI_PERMISSION,
208    SCAN_WIFI_NETWORKS,
209};
210pub use registry::{
211    ActionRegistry, AnimationPropertyId, AnimationRequest, AnimationStartValue, BuildCtx,
212    EasingFunction, Handler, JobResource, PortalLayer, RawActionHandler, ResourceKey,
213    ResourcePolicy, ResourceRegistry, RuntimeResourceDeclaration, RuntimeResourceKind,
214    ServiceResource, TimerResource, VideoRegistration,
215};
216pub use time::{Clock, CurrentTime};
217pub use ui::{
218    ActionScope, BadgeTone, Builder, Button, ButtonHierarchy, CardPattern, Column, ComponentSize,
219    ComponentState, CustomEventResult, CustomHitResult, CustomNode, CustomRenderObject,
220    LayoutBuilder, Lower, LowerDyn, Node, Row, Text,
221};
222pub use view::{Selector, View, Widget};
223
224/// Coerces a reducer function item or non-capturing closure to the handler
225/// function-pointer type Rust can infer from the surrounding `ctx.bind(...)`
226/// call.
227///
228/// ```rust,ignore
229/// use fission::prelude::*;
230///
231/// let on_press = with_reducer!(ctx, Increment, on_increment);
232/// ```
233#[macro_export]
234macro_rules! reduce_with {
235    ($handler:expr $(,)?) => {
236        $handler as $crate::Handler<_, _>
237    };
238}
239
240/// Short alias for [`reduce_with!`].
241#[macro_export]
242macro_rules! reduce {
243    ($handler:expr $(,)?) => {
244        $crate::reduce_with!($handler)
245    };
246}
247
248/// Binds an action to a reducer in one expression.
249///
250/// ```rust,ignore
251/// use fission::prelude::*;
252///
253/// let on_press = with_reducer!(ctx, Increment, on_increment);
254/// ```
255#[macro_export]
256macro_rules! with_reducer {
257    ($ctx:expr, $action:expr, $handler:expr $(,)?) => {
258        $ctx.bind($action, $crate::reduce_with!($handler))
259    };
260}
261
262/// A frame-tick action that advances the runtime clock by a delta.
263///
264/// The platform shell dispatches `Tick` once per frame so that animations,
265/// timers, and other time-dependent logic can progress.
266///
267/// # Example
268///
269/// ```rust,ignore
270/// // Advance the runtime by 16 ms (~60 fps)
271/// runtime.tick(16)?;
272/// ```
273#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
274pub struct Tick {
275    /// Delta time in milliseconds since the last tick.
276    pub dt: CurrentTime,
277}
278
279impl Action for Tick {
280    fn static_id() -> ActionId {
281        *TICK_ACTION_ID
282    }
283}
284
285lazy_static! {
286    pub static ref TICK_ACTION_ID: ActionId = ActionId::from_name("fission_core::Tick");
287}
288
289/// An action that sets the runtime clock to an absolute timestamp.
290///
291/// Unlike [`Tick`] which advances by a delta, `AdvanceTo` jumps directly to
292/// the given time. Useful for testing and deterministic replay.
293///
294/// # Example
295///
296/// ```rust,ignore
297/// let envelope: ActionEnvelope = AdvanceTo { time: 5000 }.into();
298/// runtime.dispatch(envelope, NodeId::derived(0, &[0]))?;
299/// ```
300#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
301pub struct AdvanceTo {
302    /// The absolute time (in milliseconds) to set the clock to.
303    pub time: CurrentTime,
304}
305
306impl Action for AdvanceTo {
307    fn static_id() -> ActionId {
308        *ADVANCE_TO_ACTION_ID
309    }
310}
311
312lazy_static! {
313    pub static ref ADVANCE_TO_ACTION_ID: ActionId = ActionId::from_name("fission_core::AdvanceTo");
314}
315
316/// A type-erased reducer function stored in the [`Runtime`].
317///
318/// `BoxedReducer` is the internal representation used by the runtime to invoke
319/// reducers without knowing the concrete `AppState` or `Action` types. You
320/// rarely need to interact with this type directly -- use [`BuildCtx::bind`] or
321/// [`ActionRegistry::register`] instead.
322pub type BoxedReducer = Box<
323    dyn FnMut(
324            &mut HashMap<TypeId, Box<dyn AppState>>,
325            &ActionEnvelope,
326            NodeId,
327            &mut Vec<EffectEnvelope>,
328            &ActionInput,
329        ) -> Result<()>
330        + Send
331        + Sync,
332>;