@ng-native/expo 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +2 -0
- package/android/src/main/java/dev/ngnative/expo/SearchFieldModule.kt +111 -0
- package/dist/app-info.d.ts +18 -15
- package/dist/app-info.js +17 -5
- package/dist/app-info.js.map +1 -1
- package/dist/apple-sign-in.js +5 -4
- package/dist/apple-sign-in.js.map +1 -1
- package/dist/assets.js +2 -2
- package/dist/assets.js.map +1 -1
- package/dist/async-storage.js +4 -1
- package/dist/async-storage.js.map +1 -1
- package/dist/audio.js +2 -2
- package/dist/audio.js.map +1 -1
- package/dist/battery.js +4 -1
- package/dist/battery.js.map +1 -1
- package/dist/biometrics.js +5 -4
- package/dist/biometrics.js.map +1 -1
- package/dist/brightness.js +7 -2
- package/dist/brightness.js.map +1 -1
- package/dist/camera.js +4 -1
- package/dist/camera.js.map +1 -1
- package/dist/clipboard.js +5 -2
- package/dist/clipboard.js.map +1 -1
- package/dist/database.js +2 -2
- package/dist/database.js.map +1 -1
- package/dist/expo-ui-components.d.ts +101 -9
- package/dist/expo-ui-components.js +305 -43
- package/dist/expo-ui-components.js.map +1 -1
- package/dist/expo-ui.js +7 -1
- package/dist/expo-ui.js.map +1 -1
- package/dist/foldable.d.ts +2 -0
- package/dist/foldable.js +15 -5
- package/dist/foldable.js.map +1 -1
- package/dist/fonts.d.ts +2 -0
- package/dist/fonts.js +12 -14
- package/dist/fonts.js.map +1 -1
- package/dist/haptics.js +2 -2
- package/dist/haptics.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/language-model.js +7 -3
- package/dist/language-model.js.map +1 -1
- package/dist/native.d.ts +17 -2
- package/dist/native.js +61 -3
- package/dist/native.js.map +1 -1
- package/dist/network.js +4 -1
- package/dist/network.js.map +1 -1
- package/dist/notifications.js +35 -8
- package/dist/notifications.js.map +1 -1
- package/dist/observed.js +5 -1
- package/dist/observed.js.map +1 -1
- package/dist/orientation.js +4 -1
- package/dist/orientation.js.map +1 -1
- package/dist/register-expo-view.d.ts +19 -0
- package/dist/register-expo-view.js +1 -0
- package/dist/register-expo-view.js.map +1 -1
- package/dist/screen-capture.js +2 -2
- package/dist/screen-capture.js.map +1 -1
- package/dist/search-field.d.ts +83 -0
- package/dist/search-field.js +147 -0
- package/dist/search-field.js.map +1 -0
- package/dist/secure-store.js +4 -1
- package/dist/secure-store.js.map +1 -1
- package/dist/sensors.js +6 -3
- package/dist/sensors.js.map +1 -1
- package/dist/splash-screen.js +2 -2
- package/dist/splash-screen.js.map +1 -1
- package/dist/store.js +4 -0
- package/dist/store.js.map +1 -1
- package/dist/tracking.js +2 -2
- package/dist/tracking.js.map +1 -1
- package/dist/ui-font-names.d.ts +16 -0
- package/dist/ui-font-names.js +71 -0
- package/dist/ui-font-names.js.map +1 -0
- package/dist/updates.js +4 -1
- package/dist/updates.js.map +1 -1
- package/dist/video.js +2 -2
- package/dist/video.js.map +1 -1
- package/dist/watch.js +6 -5
- package/dist/watch.js.map +1 -1
- package/expo-module.config.json +5 -2
- package/ios/ExpoUIModifiersModule.swift +25 -0
- package/ios/InlineNavigationTitle.swift +12 -0
- package/ios/NgNativeExpo.podspec +3 -0
- package/ios/SearchField.swift +130 -0
- package/ios/SegmentedFont.swift +113 -0
- package/package.json +6 -2
package/dist/expo-ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expo-ui.js","sourceRoot":"","sources":["../src/expo-ui.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `@expo/ui`: real SwiftUI and Jetpack Compose views, as elements.\n *\n * This is the closest thing in the ecosystem to what this project is for. Every one of these is a\n * platform control - a real `Picker`, a real `BottomSheet`, a real `Gauge` - rather than something\n * drawn to look like one, and `@expo/ui` reaches them through `requireNativeView('ExpoUI', ...)`,\n * which is the same derivation `registerExpoView` already mirrors. So the whole surface is\n * available here for the price of a table of names.\n *\n * The element names are platform-neutral where both platforms have the control, because an app\n * should write `<ui-slider>` once. Where only one platform has it - a `Gauge` is SwiftUI's, a\n * `SearchBar` is Compose's - the element exists only there, and registering for the wrong platform\n * would be an element that commits as nothing.\n *\n * ```ts\n * registerExpoUiViews(Platform.OS);\n * ```\n * ```html\n * <ui-host style=\"height: 44\">\n * <ui-slider [value]=\"volume()\" (valueChanged)=\"volume.set($event.value)\" />\n * </ui-host>\n * ```\n *\n * **A `<ui-host>` is required around any of these.** SwiftUI and Compose lay out their own\n * subtrees, and the host view is what bridges Yoga's layout to theirs; without one the control has\n * no size and does not appear - which looks exactly like a module that failed to install.\n *\n * Names, not components. There are ninety-odd of these, each with its own props and its own\n * modifier system, and a component per view prop-for-prop would be a second place for every one of\n * them to be wrong. The module's own documentation describes the props; this makes them reachable.\n */\nimport type { PlatformOSType } from 'react-native';\nimport { registerExpoView } from './register-expo-view.ts';\n\n/** Element name -> the `ExpoUI` view on iOS and on Android. Null where the platform has no such. */\nexport const EXPO_UI_VIEWS: Readonly<Record<string, readonly [string | null, string | null]>> = {\n 'accessory-widget-background': ['AccessoryWidgetBackgroundView', null],\n alert: ['AlertView', 'AlertDialogView'],\n 'animated-visibility': [null, 'AnimatedVisibilityView'],\n 'assist-chip': [null, 'AssistChipView'],\n background: ['BackgroundView', null],\n badge: [null, 'BadgeView'],\n 'badged-box': [null, 'BadgedBoxView'],\n 'basic-alert-dialog': [null, 'BasicAlertDialogView'],\n 'bottom-sheet': ['BottomSheetView', 'ModalBottomSheetView'],\n /**\n * The same sheet under a second name, for `UiBottomSheet`: its own element is `ui-bottom-sheet`,\n * and a component's template takes its own selector to be the component again.\n */\n 'bottom-sheet-view': ['BottomSheetView', 'ModalBottomSheetView'],\n button: ['Button', 'Button'],\n capsule: ['CapsuleView', null],\n chart: ['ChartView', null],\n checkbox: [null, 'CheckboxView'],\n circle: ['CircleView', null],\n 'color-picker': ['ColorPickerView', null],\n 'confirmation-dialog': ['ConfirmationDialogView', null],\n 'content-unavailable': ['ContentUnavailableView', null],\n 'context-menu': ['ContextMenu', null],\n 'control-group': ['ControlGroupView', null],\n 'date-picker': ['DatePickerView', 'DateTimePickerView'],\n 'date-picker-dialog': [null, 'DatePickerDialogView'],\n 'disclosure-group': ['DisclosureGroupView', null],\n divider: ['DividerView', 'HorizontalDividerView'],\n 'docked-search-bar': [null, 'DockedSearchBarView'],\n ellipse: ['EllipseView', null],\n 'exposed-dropdown-menu-box': [null, 'ExposedDropdownMenuBoxView'],\n 'filter-chip': [null, 'FilterChipView'],\n 'floating-action-button': [null, 'FloatingActionButtonView'],\n 'flow-row': [null, 'FlowRowView'],\n form: ['FormView', null],\n gauge: ['GaugeView', null],\n 'glass-effect-container': ['GlassEffectContainerView', null],\n grid: ['GridView', null],\n 'grid-row': ['GridRowView', null],\n group: ['GroupView', null],\n 'horizontal-floating-toolbar': [null, 'HorizontalFloatingToolbarView'],\n host: ['HostView', 'HostView'],\n hstack: ['HStackView', 'RowView'],\n icon: [null, 'IconView'],\n image: ['ImageView', 'ImageView'],\n 'input-chip': [null, 'InputChipView'],\n label: ['LabelView', null],\n list: ['ListView', null],\n 'labeled-content': ['LabeledContentView', null],\n 'lazy-hstack': ['LazyHStackView', 'LazyRowView'],\n 'lazy-vstack': ['LazyVStackView', 'LazyColumnView'],\n link: ['LinkView', null],\n 'list-item': [null, 'ListItemView'],\n mask: ['MaskView', null],\n menu: ['MenuView', 'DropdownMenuView'],\n 'multi-choice-segmented-button-row': [null, 'MultiChoiceSegmentedButtonRowView'],\n 'navigation-bar': [null, 'NavigationBarView'],\n 'navigation-bar-item': [null, 'NavigationBarItemView'],\n 'navigation-stack': ['NavigationStackView', null],\n overlay: ['OverlayView', null],\n pager: [null, 'HorizontalPagerView'],\n picker: ['PickerView', null],\n 'plain-tooltip': [null, 'PlainTooltipView'],\n popover: ['PopoverView', null],\n progress: ['ProgressView', 'LinearProgressIndicatorView'],\n 'pull-to-refresh-box': [null, 'PullToRefreshBoxView'],\n 'radio-button': [null, 'RadioButtonView'],\n rectangle: ['RectangleView', null],\n 'rich-tooltip': [null, 'RichTooltipView'],\n 'rounded-rectangle': ['RoundedRectangleView', null],\n 'scroll-view-component': ['ScrollViewComponent', null],\n 'search-bar': [null, 'SearchBarView'],\n section: ['SectionView', null],\n 'secure-field': ['SecureFieldView', null],\n 'segmented-button': [null, 'SegmentedButtonView'],\n shape: [null, 'ShapeView'],\n 'share-link': ['ShareLinkView', null],\n 'single-choice-segmented-button-row': [null, 'SingleChoiceSegmentedButtonRowView'],\n slider: ['SliderView', 'SliderView'],\n /**\n * Both platforms, though it looks Android-only at a glance.\n *\n * SwiftUI's `SlotView` lives in a top-level `SlotView.tsx` rather than in a directory of its\n * own, which is how it was missed. Twenty SwiftUI components put their children through one:\n * a picker's options, a section's rows, a labelled content's value. Without it those children\n * mount into a plain `UIView` and SwiftUI refuses them, which is a red screen on iOS and an\n * empty control everywhere else.\n */\n slot: ['SlotView', 'SlotView'],\n snackbar: [null, 'SnackbarView'],\n 'snackbar-host': [null, 'SnackbarHostView'],\n spacer: ['SpacerView', 'SpacerView'],\n stepper: ['StepperView', null],\n 'suggestion-chip': [null, 'SuggestionChipView'],\n surface: [null, 'SurfaceView'],\n 'swipe-actions': ['SwipeActionsView', null],\n 'sync-toggle': ['SyncToggleView', 'SyncSwitchView'],\n tab: ['TabView', null],\n text: ['TextView', 'TextView'],\n 'text-field': ['TextFieldView', null],\n 'time-picker-dialog': [null, 'TimePickerDialogView'],\n toggle: ['ToggleView', 'SwitchView'],\n toolbar: ['ToolbarView', null],\n /** A slot under another name: SwiftUI's toolbar reads each of its items from a slot named `item`. */\n 'toolbar-item': ['SlotView', null],\n 'tooltip-box': [null, 'TooltipBoxView'],\n 'tri-state-checkbox': [null, 'TriStateCheckboxView'],\n 'uneven-rounded-rectangle': ['UnevenRoundedRectangleView', null],\n 'vertical-slider': [null, 'VerticalSliderView'],\n /**\n * The other way round from `host`: views of the app's own inside SwiftUI or Compose content,\n * as a context menu's trigger or a sheet's body. `@expo/ui` calls it `RNHostView`.\n */\n 'view-host': ['RNHostView', 'RNHostView'],\n vstack: ['VStackView', 'ColumnView'],\n zstack: ['ZStackView', 'BoxView'],\n};\n\n/**\n * Register every `@expo/ui` view the platform has.\n *\n * All of them at once, unlike the other registrations here, because they are all one module: an\n * app that has `@expo/ui` installed has all of these, and an app that does not has none of them.\n *\n * Takes `Platform.OS` itself - `'ios' | 'android' | 'macos' | 'windows' | 'web'` - rather than the\n * two platforms `@expo/ui` actually ships for, so a canary app can pass it straight through\n * without narrowing. Anything other than iOS or Android registers nothing, the same \"not\n * installed\" outcome as never calling this at all.\n */\nexport function registerExpoUiViews(platform: PlatformOSType): void {\n if (platform !== 'ios' && platform !== 'android') return;\n const index = platform === 'ios' ? 0 : 1;\n for (const [element, views] of Object.entries(EXPO_UI_VIEWS)) {\n const viewName = views[index];\n if (!viewName) continue;\n // SwiftUI's and Compose's `Text` read their text from `text`, and take no child views.\n const textContent = element === 'text' ? 'text' : undefined;\n // `ui-` is a prefix a design system picks too: a component of the app's own keeps its host.\n registerExpoView(`ui-${element}`, 'ExpoUI', {\n viewName,\n textContent,\n yieldsToComponents: true,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"expo-ui.js","sourceRoot":"","sources":["../src/expo-ui.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `@expo/ui`: real SwiftUI and Jetpack Compose views, as elements.\n *\n * This is the closest thing in the ecosystem to what this project is for. Every one of these is a\n * platform control - a real `Picker`, a real `BottomSheet`, a real `Gauge` - rather than something\n * drawn to look like one, and `@expo/ui` reaches them through `requireNativeView('ExpoUI', ...)`,\n * which is the same derivation `registerExpoView` already mirrors. So the whole surface is\n * available here for the price of a table of names.\n *\n * The element names are platform-neutral where both platforms have the control, because an app\n * should write `<ui-slider>` once. Where only one platform has it - a `Gauge` is SwiftUI's, a\n * `SearchBar` is Compose's - the element exists only there, and registering for the wrong platform\n * would be an element that commits as nothing.\n *\n * ```ts\n * registerExpoUiViews(Platform.OS);\n * ```\n * ```html\n * <ui-host style=\"height: 44\">\n * <ui-slider [value]=\"volume()\" (valueChanged)=\"volume.set($event.value)\" />\n * </ui-host>\n * ```\n *\n * **A `<ui-host>` is required around any of these.** SwiftUI and Compose lay out their own\n * subtrees, and the host view is what bridges Yoga's layout to theirs; without one the control has\n * no size and does not appear - which looks exactly like a module that failed to install.\n *\n * Names, not components. There are ninety-odd of these, each with its own props and its own\n * modifier system, and a component per view prop-for-prop would be a second place for every one of\n * them to be wrong. The module's own documentation describes the props; this makes them reachable.\n */\nimport type { PlatformOSType } from 'react-native';\nimport { registerExpoView } from './register-expo-view.ts';\nimport { fontModifierNames } from './ui-font-names.ts';\n\n/** Element name -> the `ExpoUI` view on iOS and on Android. Null where the platform has no such. */\nexport const EXPO_UI_VIEWS: Readonly<Record<string, readonly [string | null, string | null]>> = {\n 'accessory-widget-background': ['AccessoryWidgetBackgroundView', null],\n alert: ['AlertView', 'AlertDialogView'],\n 'animated-visibility': [null, 'AnimatedVisibilityView'],\n 'assist-chip': [null, 'AssistChipView'],\n background: ['BackgroundView', null],\n badge: [null, 'BadgeView'],\n 'badged-box': [null, 'BadgedBoxView'],\n 'basic-alert-dialog': [null, 'BasicAlertDialogView'],\n 'bottom-sheet': ['BottomSheetView', 'ModalBottomSheetView'],\n /**\n * The same sheet under a second name, for `UiBottomSheet`: its own element is `ui-bottom-sheet`,\n * and a component's template takes its own selector to be the component again.\n */\n 'bottom-sheet-view': ['BottomSheetView', 'ModalBottomSheetView'],\n button: ['Button', 'Button'],\n capsule: ['CapsuleView', null],\n chart: ['ChartView', null],\n checkbox: [null, 'CheckboxView'],\n circle: ['CircleView', null],\n 'color-picker': ['ColorPickerView', null],\n 'confirmation-dialog': ['ConfirmationDialogView', null],\n 'content-unavailable': ['ContentUnavailableView', null],\n 'context-menu': ['ContextMenu', null],\n 'control-group': ['ControlGroupView', null],\n 'date-picker': ['DatePickerView', 'DateTimePickerView'],\n 'date-picker-dialog': [null, 'DatePickerDialogView'],\n 'disclosure-group': ['DisclosureGroupView', null],\n divider: ['DividerView', 'HorizontalDividerView'],\n 'docked-search-bar': [null, 'DockedSearchBarView'],\n ellipse: ['EllipseView', null],\n 'dropdown-menu-item': [null, 'DropdownMenuItemView'],\n 'exposed-dropdown-menu': [null, 'ExposedDropdownMenuView'],\n 'exposed-dropdown-menu-box': [null, 'ExposedDropdownMenuBoxView'],\n 'filter-chip': [null, 'FilterChipView'],\n 'floating-action-button': [null, 'FloatingActionButtonView'],\n 'flow-row': [null, 'FlowRowView'],\n form: ['FormView', null],\n gauge: ['GaugeView', null],\n 'glass-effect-container': ['GlassEffectContainerView', null],\n grid: ['GridView', null],\n 'grid-row': ['GridRowView', null],\n group: ['GroupView', null],\n 'horizontal-floating-toolbar': [null, 'HorizontalFloatingToolbarView'],\n host: ['HostView', 'HostView'],\n hstack: ['HStackView', 'RowView'],\n icon: [null, 'IconView'],\n image: ['ImageView', 'ImageView'],\n 'input-chip': [null, 'InputChipView'],\n label: ['LabelView', null],\n list: ['ListView', null],\n 'labeled-content': ['LabeledContentView', null],\n 'lazy-hstack': ['LazyHStackView', 'LazyRowView'],\n 'lazy-vstack': ['LazyVStackView', 'LazyColumnView'],\n link: ['LinkView', null],\n 'list-item': [null, 'ListItemView'],\n mask: ['MaskView', null],\n menu: ['MenuView', 'DropdownMenuView'],\n 'multi-choice-segmented-button-row': [null, 'MultiChoiceSegmentedButtonRowView'],\n 'navigation-bar': [null, 'NavigationBarView'],\n 'navigation-bar-item': [null, 'NavigationBarItemView'],\n 'navigation-stack': ['NavigationStackView', null],\n overlay: ['OverlayView', null],\n pager: [null, 'HorizontalPagerView'],\n /** On Android, the box `UiPicker` draws Compose's controls in: see there. */\n picker: ['PickerView', 'BoxView'],\n 'plain-tooltip': [null, 'PlainTooltipView'],\n popover: ['PopoverView', null],\n progress: ['ProgressView', 'LinearProgressIndicatorView'],\n 'pull-to-refresh-box': [null, 'PullToRefreshBoxView'],\n 'radio-button': [null, 'RadioButtonView'],\n rectangle: ['RectangleView', null],\n 'rich-tooltip': [null, 'RichTooltipView'],\n 'rounded-rectangle': ['RoundedRectangleView', null],\n 'scroll-view-component': ['ScrollViewComponent', null],\n 'search-bar': [null, 'SearchBarView'],\n section: ['SectionView', null],\n 'secure-field': ['SecureFieldView', null],\n 'segmented-button': [null, 'SegmentedButtonView'],\n shape: [null, 'ShapeView'],\n 'share-link': ['ShareLinkView', null],\n 'single-choice-segmented-button-row': [null, 'SingleChoiceSegmentedButtonRowView'],\n slider: ['SliderView', 'SliderView'],\n /**\n * Both platforms, though it looks Android-only at a glance.\n *\n * SwiftUI's `SlotView` lives in a top-level `SlotView.tsx` rather than in a directory of its\n * own, which is how it was missed. Twenty SwiftUI components put their children through one:\n * a picker's options, a section's rows, a labelled content's value. Without it those children\n * mount into a plain `UIView` and SwiftUI refuses them, which is a red screen on iOS and an\n * empty control everywhere else.\n */\n slot: ['SlotView', 'SlotView'],\n snackbar: [null, 'SnackbarView'],\n 'snackbar-host': [null, 'SnackbarHostView'],\n spacer: ['SpacerView', 'SpacerView'],\n stepper: ['StepperView', null],\n 'suggestion-chip': [null, 'SuggestionChipView'],\n surface: [null, 'SurfaceView'],\n 'swipe-actions': ['SwipeActionsView', null],\n 'sync-toggle': ['SyncToggleView', 'SyncSwitchView'],\n tab: ['TabView', null],\n text: ['TextView', 'TextView'],\n 'text-field': ['TextFieldView', null],\n 'time-picker-dialog': [null, 'TimePickerDialogView'],\n toggle: ['ToggleView', 'SwitchView'],\n toolbar: ['ToolbarView', null],\n /** A slot under another name: SwiftUI's toolbar reads each of its items from a slot named `item`. */\n 'toolbar-item': ['SlotView', null],\n 'tooltip-box': [null, 'TooltipBoxView'],\n 'tri-state-checkbox': [null, 'TriStateCheckboxView'],\n 'uneven-rounded-rectangle': ['UnevenRoundedRectangleView', null],\n 'vertical-slider': [null, 'VerticalSliderView'],\n /**\n * The other way round from `host`: views of the app's own inside SwiftUI or Compose content,\n * as a context menu's trigger or a sheet's body. `@expo/ui` calls it `RNHostView`.\n */\n 'view-host': ['RNHostView', 'RNHostView'],\n vstack: ['VStackView', 'ColumnView'],\n zstack: ['ZStackView', 'BoxView'],\n};\n\n/**\n * Register every `@expo/ui` view the platform has.\n *\n * All of them at once, unlike the other registrations here, because they are all one module: an\n * app that has `@expo/ui` installed has all of these, and an app that does not has none of them.\n *\n * Takes `Platform.OS` itself - `'ios' | 'android' | 'macos' | 'windows' | 'web'` - rather than the\n * two platforms `@expo/ui` actually ships for, so a canary app can pass it straight through\n * without narrowing. Anything other than iOS or Android registers nothing, the same \"not\n * installed\" outcome as never calling this at all.\n */\nexport function registerExpoUiViews(platform: PlatformOSType): void {\n if (platform !== 'ios' && platform !== 'android') return;\n const index = platform === 'ios' ? 0 : 1;\n for (const [element, views] of Object.entries(EXPO_UI_VIEWS)) {\n const viewName = views[index];\n if (!viewName) continue;\n // SwiftUI's and Compose's `Text` read their text from `text`, and take no child views.\n const textContent = element === 'text' ? 'text' : undefined;\n // `ui-` is a prefix a design system picks too: a component of the app's own keeps its host.\n registerExpoView(`ui-${element}`, 'ExpoUI', {\n viewName,\n textContent,\n yieldsToComponents: true,\n // SwiftUI finds a face by its PostScript name alone; Compose by the name it registered as.\n fontNames: platform === 'ios' ? fontModifierNames : undefined,\n });\n }\n}\n"]}
|
package/dist/foldable.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export declare class Foldable {
|
|
|
68
68
|
/** Half open with the fold running across, like a laptop. */
|
|
69
69
|
readonly tabletop: Signal<boolean>;
|
|
70
70
|
constructor();
|
|
71
|
+
/** The hinge's state, read as a use: a module missing from the build says what to install. */
|
|
72
|
+
private known;
|
|
71
73
|
private update;
|
|
72
74
|
private stopFollowingAngle;
|
|
73
75
|
private followAngle;
|
package/dist/foldable.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* `'unknown'`, no fold and no angle.
|
|
7
7
|
*/
|
|
8
8
|
import { DestroyRef, InjectionToken, Service, computed, inject, signal, } from '@angular/core';
|
|
9
|
-
import { expoModule } from "./native.js";
|
|
9
|
+
import { expoModule, present } from "./native.js";
|
|
10
10
|
import * as i0 from "@angular/core";
|
|
11
11
|
export class Foldable {
|
|
12
12
|
static SOURCE = new InjectionToken('angular-native.hingeSource', {
|
|
@@ -21,17 +21,21 @@ export class Foldable {
|
|
|
21
21
|
/** Whether the device reports a hinge. It can turn true shortly after launch. */
|
|
22
22
|
available = computed(() => this.state() !== undefined, /* @ts-ignore */
|
|
23
23
|
...(ngDevMode ? [{ debugName: "available" }] : /* istanbul ignore next */ []));
|
|
24
|
-
posture = computed(() => this.
|
|
24
|
+
posture = computed(() => this.known()?.posture ?? 'unknown', /* @ts-ignore */
|
|
25
25
|
...(ngDevMode ? [{ debugName: "posture" }] : /* istanbul ignore next */ []));
|
|
26
26
|
/**
|
|
27
27
|
* Where the fold crosses the window, in window coordinates: a view that does not start at the
|
|
28
28
|
* window's left edge subtracts its own offset. Null while it crosses no window, as on a closed
|
|
29
29
|
* phone's cover display.
|
|
30
30
|
*/
|
|
31
|
-
fold = computed(() => this.
|
|
31
|
+
fold = computed(() => this.known()?.fold ?? null, /* @ts-ignore */
|
|
32
32
|
...(ngDevMode ? [{ debugName: "fold" }] : /* istanbul ignore next */ []));
|
|
33
33
|
/** The hinge angle in degrees, 0 shut and 180 flat. Null without an angle sensor. */
|
|
34
|
-
angle =
|
|
34
|
+
angle = computed(() => {
|
|
35
|
+
this.known();
|
|
36
|
+
return this.angleState();
|
|
37
|
+
}, /* @ts-ignore */
|
|
38
|
+
...(ngDevMode ? [{ debugName: "angle" }] : /* istanbul ignore next */ []));
|
|
35
39
|
/** Whether the fold splits the window, so content should stay off it. */
|
|
36
40
|
separating = computed(() => this.fold()?.isSeparating ?? false, /* @ts-ignore */
|
|
37
41
|
...(ngDevMode ? [{ debugName: "separating" }] : /* istanbul ignore next */ []));
|
|
@@ -42,7 +46,7 @@ export class Foldable {
|
|
|
42
46
|
tabletop = computed(() => this.halfOpen('horizontal'), /* @ts-ignore */
|
|
43
47
|
...(ngDevMode ? [{ debugName: "tabletop" }] : /* istanbul ignore next */ []));
|
|
44
48
|
constructor() {
|
|
45
|
-
const native = this.native;
|
|
49
|
+
const native = present(this.native);
|
|
46
50
|
if (!native)
|
|
47
51
|
return;
|
|
48
52
|
const stateSubscription = native.addOnStateChangeListener((state) => this.update(state));
|
|
@@ -52,6 +56,12 @@ export class Foldable {
|
|
|
52
56
|
this.angleSubscription?.remove();
|
|
53
57
|
});
|
|
54
58
|
}
|
|
59
|
+
/** The hinge's state, read as a use: a module missing from the build says what to install. */
|
|
60
|
+
known() {
|
|
61
|
+
if (this.native && !present(this.native))
|
|
62
|
+
this.native.getState();
|
|
63
|
+
return this.state();
|
|
64
|
+
}
|
|
55
65
|
update(state) {
|
|
56
66
|
this.state.set(state);
|
|
57
67
|
if (state === undefined) {
|
package/dist/foldable.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foldable.js","sourceRoot":"","sources":["../src/foldable.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AA+
|
|
1
|
+
{"version":3,"file":"foldable.js","sourceRoot":"","sources":["../src/foldable.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EACL,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;AA+ClD,MAAM,OAAO,QAAQ;IACnB,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CAAqB,4BAA4B,EAAE;QAC5F,OAAO,EAAE,GAAG,EAAE,CACZ,UAAU,CACR,gBAAgB,EAChB,GAAG,EAAE,CAAE,OAAO,CAAC,gBAAgB,CAA4B,CAAC,KAAK,CAClE;KACJ,CAAC,CAAC;IAEc,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACjC,KAAK,GAAG,MAAM,CAAyB,SAAS;8EAAC,CAAC;IAClD,UAAU,GAAG,MAAM,CAAgB,IAAI;mFAAC,CAAC;IAClD,iBAAiB,GAAwB,IAAI,CAAC;IAEtD,iFAAiF;IACxE,SAAS,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,SAAS;kFAAC,CAAC;IACxE,OAAO,GAAyB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,OAAO,IAAI,SAAS;gFAAC,CAAC;IAC5F;;;;OAIG;IACM,IAAI,GAAwB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,IAAI,IAAI;6EAAC,CAAC;IAChF,qFAAqF;IAC5E,KAAK,GAA0B,QAAQ,CAAC,GAAG,EAAE;QACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;IAC3B,CAAC;8EAAC,CAAC;IACH,yEAAyE;IAChE,UAAU,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,YAAY,IAAI,KAAK;mFAAC,CAAC;IAC1F,oEAAoE;IAC3D,IAAI,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;6EAAC,CAAC;IAC3E,6DAA6D;IACpD,QAAQ,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;iFAAC,CAAC;IAEjF;QACE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,iBAAiB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAChC,iBAAiB,CAAC,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,iBAAiB,EAAE,MAAM,EAAE,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8FAA8F;IACtF,KAAK;QACX,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAEO,MAAM,CAAC,KAA6B;QAC1C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAEO,kBAAkB;QACxB,IAAI,CAAC,iBAAiB,EAAE,MAAM,EAAE,CAAC;QACjC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAEO,WAAW;QACjB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,iBAAiB,IAAI,CAAC,MAAM,CAAC,gBAAgB;YAAE,OAAO;QAC1E,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,wBAAwB,CACtD,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,EACrC,EAAE,eAAe,EAAE,GAAG,EAAE,CACzB,CAAC;IACJ,CAAC;IAEO,QAAQ,CAAC,WAAgC;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,OAAO,CACL,IAAI,CAAC,OAAO,EAAE,KAAK,gBAAgB;YACnC,IAAI,EAAE,YAAY,KAAK,IAAI;YAC3B,IAAI,CAAC,WAAW,KAAK,WAAW,CACjC,CAAC;IACJ,CAAC;uGAnFU,QAAQ;wGAAR,QAAQ;;2FAAR,QAAQ;kBADpB,OAAO","sourcesContent":["/**\n * `Foldable`, bound to `expo-foldables`: the hinge of a foldable device, an iPhone Duo or an\n * Android foldable.\n *\n * On a phone that does not fold, on the web and in a test, it answers like a phone: not available,\n * `'unknown'`, no fold and no angle.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\n/** How far the hinge is open. `'unknown'` without a hinge, or while the platform cannot say. */\nexport type HingePosture = 'closed' | 'partially-open' | 'fully-open' | 'unknown';\n\n/** A rectangle in window coordinates, in points: measured from the window's top left corner. */\nexport interface FoldRect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/** Where the fold crosses the app's window. */\nexport interface Fold {\n /** The fold's area, in window coordinates. Zero wide or tall where the fold is a line. */\n readonly bounds: FoldRect;\n readonly orientation: 'horizontal' | 'vertical';\n /** Whether the fold splits the window in two, so content should stay off it. */\n readonly isSeparating: boolean;\n /** `'full'` where the fold area shows nothing, as with a physical gap between two screens. */\n readonly occlusion: 'none' | 'full';\n}\n\n/** What `expo-foldables` reports: the posture, and the fold while it crosses the window. */\nexport interface HingeState {\n readonly posture: HingePosture;\n readonly fold?: Fold;\n}\n\ninterface Subscription {\n remove(): void;\n}\n\n/** `expo-foldables`' `Hinge`, as `Foldable.SOURCE` provides it. A test passes a stand-in. */\nexport interface NativeHinge {\n readonly isAvailable: boolean;\n readonly isAngleAvailable: boolean;\n getState(): HingeState | undefined;\n addOnStateChangeListener(listener: (state: HingeState | undefined) => void): Subscription;\n addOnAngleChangeListener(\n listener: (angle: number) => void,\n options?: { minDeltaDegrees?: number },\n ): Subscription;\n}\n\n@Service()\nexport class Foldable {\n static readonly SOURCE = new InjectionToken<NativeHinge | null>('angular-native.hingeSource', {\n factory: () =>\n expoModule(\n 'expo-foldables',\n () => (require('expo-foldables') as { Hinge: NativeHinge }).Hinge,\n ),\n });\n\n private readonly native = inject(Foldable.SOURCE);\n private readonly state = signal<HingeState | undefined>(undefined);\n private readonly angleState = signal<number | null>(null);\n private angleSubscription: Subscription | null = null;\n\n /** Whether the device reports a hinge. It can turn true shortly after launch. */\n readonly available: Signal<boolean> = computed(() => this.state() !== undefined);\n readonly posture: Signal<HingePosture> = computed(() => this.known()?.posture ?? 'unknown');\n /**\n * Where the fold crosses the window, in window coordinates: a view that does not start at the\n * window's left edge subtracts its own offset. Null while it crosses no window, as on a closed\n * phone's cover display.\n */\n readonly fold: Signal<Fold | null> = computed(() => this.known()?.fold ?? null);\n /** The hinge angle in degrees, 0 shut and 180 flat. Null without an angle sensor. */\n readonly angle: Signal<number | null> = computed(() => {\n this.known();\n return this.angleState();\n });\n /** Whether the fold splits the window, so content should stay off it. */\n readonly separating: Signal<boolean> = computed(() => this.fold()?.isSeparating ?? false);\n /** Half open with the fold running down the middle, like a book. */\n readonly book: Signal<boolean> = computed(() => this.halfOpen('vertical'));\n /** Half open with the fold running across, like a laptop. */\n readonly tabletop: Signal<boolean> = computed(() => this.halfOpen('horizontal'));\n\n constructor() {\n const native = present(this.native);\n if (!native) return;\n const stateSubscription = native.addOnStateChangeListener((state) => this.update(state));\n this.update(native.getState());\n inject(DestroyRef).onDestroy(() => {\n stateSubscription.remove();\n this.angleSubscription?.remove();\n });\n }\n\n /** The hinge's state, read as a use: a module missing from the build says what to install. */\n private known(): HingeState | undefined {\n if (this.native && !present(this.native)) this.native.getState();\n return this.state();\n }\n\n private update(state: HingeState | undefined): void {\n this.state.set(state);\n if (state === undefined) {\n this.stopFollowingAngle();\n } else {\n this.followAngle();\n }\n }\n\n private stopFollowingAngle(): void {\n this.angleSubscription?.remove();\n this.angleSubscription = null;\n this.angleState.set(null);\n }\n\n private followAngle(): void {\n const native = this.native;\n if (!native || this.angleSubscription || !native.isAngleAvailable) return;\n this.angleSubscription = native.addOnAngleChangeListener(\n (angle) => this.angleState.set(angle),\n { minDeltaDegrees: 0.5 },\n );\n }\n\n private halfOpen(orientation: Fold['orientation']): boolean {\n const fold = this.fold();\n return (\n this.posture() === 'partially-open' &&\n fold?.isSeparating === true &&\n fold.orientation === orientation\n );\n }\n}\n"]}
|
package/dist/fonts.d.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface FontFace {
|
|
|
28
28
|
readonly source: unknown;
|
|
29
29
|
readonly weight?: number;
|
|
30
30
|
readonly style?: string;
|
|
31
|
+
/** The name inside the face's file, which SwiftUI finds it by. Read by the build. */
|
|
32
|
+
readonly postScriptName?: string;
|
|
31
33
|
}
|
|
32
34
|
/** Just enough of a compiled stylesheet to find the faces in it. */
|
|
33
35
|
export interface SheetWithFonts {
|
package/dist/fonts.js
CHANGED
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
* here would be copying out its internals rather than using it.
|
|
22
22
|
*/
|
|
23
23
|
import { InjectionToken, computed, signal } from '@angular/core';
|
|
24
|
-
import {
|
|
25
|
-
import { expoModule } from "./native.js";
|
|
24
|
+
import { fontsLoading, fontsRegistered, fontsSettled, onFontsRegistered, registeredFaces, } from '@ng-native/fabric';
|
|
25
|
+
import { expoModule, present } from "./native.js";
|
|
26
|
+
import { noteRegisteredNames } from "./ui-font-names.js";
|
|
26
27
|
/**
|
|
27
28
|
* Loading is by family name, which is also all native matches on.
|
|
28
29
|
*
|
|
@@ -36,22 +37,17 @@ import { expoModule } from "./native.js";
|
|
|
36
37
|
*/
|
|
37
38
|
export function registrationsFor(faces) {
|
|
38
39
|
const map = {};
|
|
39
|
-
for (const face of faces)
|
|
40
|
-
map[
|
|
41
|
-
map[faceName(face)] = face.source;
|
|
42
|
-
}
|
|
43
|
-
for (const face of faces) {
|
|
44
|
-
if (face.weight === undefined || !face.style)
|
|
45
|
-
continue;
|
|
46
|
-
map[`${face.family}-${face.weight}`] ??= face.source;
|
|
47
|
-
map[`${face.family}-${face.style}`] ??= face.source;
|
|
48
|
-
}
|
|
40
|
+
for (const [name, face] of registeredFaces(faces))
|
|
41
|
+
map[name] = face.source;
|
|
49
42
|
return map;
|
|
50
43
|
}
|
|
51
44
|
export function expoFonts() {
|
|
52
45
|
const expo = expoModule('expo-font', () => require('expo-font'));
|
|
53
46
|
if (!expo)
|
|
54
47
|
return null;
|
|
48
|
+
// Missing, the stand-in itself: `available` reads false, and a load says what to install.
|
|
49
|
+
if (!present(expo))
|
|
50
|
+
return expo;
|
|
55
51
|
return {
|
|
56
52
|
loadAsync: (map) => expo.loadAsync(map),
|
|
57
53
|
isLoaded: (family) => expo.isLoaded(family),
|
|
@@ -77,7 +73,7 @@ export class FontRegistry {
|
|
|
77
73
|
}
|
|
78
74
|
/** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */
|
|
79
75
|
get available() {
|
|
80
|
-
return this.native !== null;
|
|
76
|
+
return present(this.native) !== null;
|
|
81
77
|
}
|
|
82
78
|
/** Every family the platform can currently find, custom or bundled. */
|
|
83
79
|
families = computed(() => {
|
|
@@ -118,7 +114,9 @@ export class FontRegistry {
|
|
|
118
114
|
fontsSettled(families);
|
|
119
115
|
// Text already laid out in the fallback face keeps it until it is laid out again. Only the
|
|
120
116
|
// faces that registered: one failing fails the whole load, not the others in it.
|
|
121
|
-
|
|
117
|
+
const registered = families.filter((family) => native.isLoaded(family));
|
|
118
|
+
noteRegisteredNames(registered);
|
|
119
|
+
fontsRegistered(registered);
|
|
122
120
|
}
|
|
123
121
|
}
|
|
124
122
|
}
|
package/dist/fonts.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport {\n faceName,\n fontsLoading,\n fontsRegistered,\n fontsSettled,\n onFontsRegistered,\n} from '@ng-native/fabric';\nimport { expoModule } from './native.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a\n * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,\n * `Inter-700-italic`), which is the name the engine points a matching text at.\n *\n * A face with both a weight and a style also answers to each alone, as it did before it had a\n * name of its own, but only where no face declared exactly that.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const face of faces) {\n map[face.family] ??= face.source;\n map[faceName(face)] = face.source;\n }\n for (const face of faces) {\n if (face.weight === undefined || !face.style) continue;\n map[`${face.family}-${face.weight}`] ??= face.source;\n map[`${face.family}-${face.style}`] ??= face.source;\n }\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\n/**\n * Bumped whenever faces are registered, by any registry.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list. Shared by every registry, because `loadFonts()` registers through its own\n * and a screen reads through the injected one.\n */\nconst generation = signal(0);\nonFontsRegistered(() => generation.update((n) => n + 1));\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return this.native !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Called before `mount` in an app that uses one. Text laid out before a face registers is in\n * the fallback face until it does, and is laid out again then. Sheets with no faces resolve\n * immediately, so calling it unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n const native = this.native;\n if (!native) return;\n // Before the first await, so text the app lays out meanwhile waits for the face by name.\n const families = Object.keys(map);\n fontsLoading(families);\n try {\n await native.loadAsync(map);\n } finally {\n fontsSettled(families);\n // Text already laid out in the fallback face keeps it until it is laid out again. Only the\n // faces that registered: one failing fails the whole load, not the others in it.\n fontsRegistered(Object.keys(map).filter((family) => native.isLoaded(family)));\n }\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n *\n * A missing `expo-font` rejects the promise rather than throwing, so a caller's `.catch` can mount\n * the app in the fallback face. Sheets that declare no face resolve without reaching for it.\n */\nexport async function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n if (!sheets.some((sheet) => sheet?.fonts?.length)) return;\n await new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `Fonts`, bound to `expo-font`.\n *\n * A font on a phone is not fetched when the text engine first needs it. It has to be registered\n * with the platform up front, or the first paint is in the fallback face and reflows when the\n * real one lands - which is why the interesting call here is the one an app makes *before* it\n * mounts:\n *\n * ```ts\n * import { loadFonts } from '@ng-native/expo/fonts';\n *\n * await loadFonts(styleSheetOf(GlobalStyles));\n * mount(rootTag, App, getFabricUIManager(), { globalStyles: styleSheetOf(GlobalStyles) });\n * ```\n *\n * The class takes its platform rather than injecting it, because that call happens before there\n * is an injector to inject from. `inject(Fonts)` is for a screen that wants to ask what loaded.\n *\n * Unlike the other services here this goes through `expo-font`'s JavaScript rather than its\n * native module: the module takes descriptors the JS builds out of an asset, and rebuilding them\n * here would be copying out its internals rather than using it.\n */\nimport { InjectionToken, computed, signal, type Signal } from '@angular/core';\nimport {\n fontsLoading,\n fontsRegistered,\n fontsSettled,\n onFontsRegistered,\n registeredFaces,\n} from '@ng-native/fabric';\nimport { expoModule, present } from './native.ts';\nimport { noteRegisteredNames } from './ui-font-names.ts';\n\n/** A face a stylesheet declared, as the compiler collected it. */\nexport interface FontFace {\n readonly family: string;\n /** What `require('./Inter.ttf')` returned: an asset id, or a module the bundler resolved. */\n readonly source: unknown;\n readonly weight?: number;\n readonly style?: string;\n /** The name inside the face's file, which SwiftUI finds it by. Read by the build. */\n readonly postScriptName?: string;\n}\n\n/** Just enough of a compiled stylesheet to find the faces in it. */\nexport interface SheetWithFonts {\n readonly fonts?: readonly FontFace[];\n}\n\n/** The slice of `expo-font` this needs. */\nexport interface NativeFonts {\n loadAsync(map: Record<string, unknown>): Promise<void>;\n isLoaded(family: string): boolean;\n getLoadedFonts(): string[];\n}\n\n/**\n * Loading is by family name, which is also all native matches on.\n *\n * There is no weight matching on a device: `font-family: Inter` finds the face registered under\n * exactly that name, and a bold cut is a family of its own. A face that declares a weight or a\n * style is therefore registered under a composed name as well (`Inter-700`, `Inter-italic`,\n * `Inter-700-italic`), which is the name the engine points a matching text at.\n *\n * A face with both a weight and a style also answers to each alone, as it did before it had a\n * name of its own, but only where no face declared exactly that.\n */\nexport function registrationsFor(faces: readonly FontFace[]): Record<string, unknown> {\n const map: Record<string, unknown> = {};\n for (const [name, face] of registeredFaces(faces)) map[name] = face.source;\n return map;\n}\n\nexport function expoFonts(): NativeFonts | null {\n const expo = expoModule('expo-font', () => require('expo-font') as typeof import('expo-font'));\n if (!expo) return null;\n // Missing, the stand-in itself: `available` reads false, and a load says what to install.\n if (!present(expo)) return expo as unknown as NativeFonts;\n return {\n loadAsync: (map) => expo.loadAsync(map as Parameters<typeof expo.loadAsync>[0]),\n isLoaded: (family) => expo.isLoaded(family),\n getLoadedFonts: () => expo.getLoadedFonts(),\n };\n}\n\n/**\n * Bumped whenever faces are registered, by any registry.\n *\n * The platform's own list is a plain array that changes underneath us, so a template binding to\n * it would render once and never again - and the one moment it changes is exactly the moment a\n * screen waiting on a font wants to hear about. This makes both readers reactive without\n * mirroring the list. Shared by every registry, because `loadFonts()` registers through its own\n * and a screen reads through the injected one.\n */\nconst generation = signal(0);\nonFontsRegistered(() => generation.update((n) => n + 1));\n\nexport class FontRegistry {\n private readonly native: NativeFonts | null;\n\n constructor(native: NativeFonts | null) {\n this.native = native;\n }\n\n /** Whether `expo-font` is installed at all. Without it a custom face simply never appears. */\n get available(): boolean {\n return present(this.native) !== null;\n }\n\n /** Every family the platform can currently find, custom or bundled. */\n readonly families: Signal<readonly string[]> = computed(() => {\n generation();\n return this.native?.getLoadedFonts() ?? [];\n });\n\n /** Whether a family is registered. Reactive: re-read after a `load` that adds one. */\n has(family: string): boolean {\n generation();\n return this.native?.isLoaded(family) ?? false;\n }\n\n /**\n * Register every `@font-face` a compiled sheet declares.\n *\n * Called before `mount` in an app that uses one. Text laid out before a face registers is in\n * the fallback face until it does, and is laid out again then. Sheets with no faces resolve\n * immediately, so calling it unconditionally at bootstrap costs nothing.\n */\n async loadSheet(...sheets: readonly (SheetWithFonts | null | undefined)[]): Promise<void> {\n const faces = sheets.flatMap((sheet) => [...(sheet?.fonts ?? [])]);\n if (!faces.length) return;\n await this.load(registrationsFor(faces));\n }\n\n /** Register faces by name, for a font that did not come from a stylesheet. */\n async load(map: Record<string, unknown>): Promise<void> {\n const native = this.native;\n if (!native) return;\n // Before the first await, so text the app lays out meanwhile waits for the face by name.\n const families = Object.keys(map);\n fontsLoading(families);\n try {\n await native.loadAsync(map);\n } finally {\n fontsSettled(families);\n // Text already laid out in the fallback face keeps it until it is laid out again. Only the\n // faces that registered: one failing fails the whole load, not the others in it.\n const registered = families.filter((family) => native.isLoaded(family));\n noteRegisteredNames(registered);\n fontsRegistered(registered);\n }\n }\n}\n\n/** Injected where an app wants to ask what is loaded. */\nexport const Fonts = new InjectionToken<FontRegistry>('angular-native.fonts', {\n factory: () => new FontRegistry(expoFonts()),\n});\nexport type Fonts = FontRegistry;\n\n/**\n * Register every face the given sheets declare, before the app mounts.\n *\n * A function rather than a service because that is when it is needed: there is no injector yet.\n *\n * A missing `expo-font` rejects the promise rather than throwing, so a caller's `.catch` can mount\n * the app in the fallback face. Sheets that declare no face resolve without reaching for it.\n */\nexport async function loadFonts(\n ...sheets: readonly (SheetWithFonts | null | undefined)[]\n): Promise<void> {\n if (!sheets.some((sheet) => sheet?.fonts?.length)) return;\n await new FontRegistry(expoFonts()).loadSheet(...sheets);\n}\n"]}
|
package/dist/haptics.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* to be installed - that is what links the native side.
|
|
11
11
|
*/
|
|
12
12
|
import { InjectionToken, Service, inject } from '@angular/core';
|
|
13
|
-
import { expoModule, optional } from "./native.js";
|
|
13
|
+
import { expoModule, optional, present } from "./native.js";
|
|
14
14
|
import * as i0 from "@angular/core";
|
|
15
15
|
/**
|
|
16
16
|
* Every method returns nothing and swallows failures.
|
|
@@ -33,7 +33,7 @@ export class Haptics {
|
|
|
33
33
|
native = inject(Haptics.SOURCE);
|
|
34
34
|
/** Whether the native module is installed at all. */
|
|
35
35
|
get available() {
|
|
36
|
-
return this.native !== null;
|
|
36
|
+
return present(this.native) !== null;
|
|
37
37
|
}
|
|
38
38
|
/** A collision between interface elements. */
|
|
39
39
|
impact(style = 'medium') {
|
package/dist/haptics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;;
|
|
1
|
+
{"version":3,"file":"haptics.js","sourceRoot":"","sources":["../src/haptics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;;AAe5D;;;;;;;;;GASG;AAEH,MAAM,OAAO,OAAO;IAClB,kFAAkF;IAClF,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,8BAA8B,EAC9B;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,MAAM,IAAI,GAAG,QAAQ,CACnB,GAAG,EAAE,CAAC,OAAO,CAAC,mBAAmB,CAAuC,CACzE,CAAC;YACF,OAAO,UAAU,CAAC,cAAc,EAAE,GAAG,EAAE,CACrC,IAAI,EAAE,2BAA2B,CAAgB,aAAa,CAAC,CAChE,CAAC;QACJ,CAAC;KACF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAEjD,qDAAqD;IACrD,IAAI,SAAS;QACX,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IACvC,CAAC;IAED,8CAA8C;IAC9C,MAAM,CAAC,QAAqB,QAAQ;QAClC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,CAAC;IAED,kCAAkC;IAClC,MAAM,CAAC,IAAsB;QAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,sDAAsD;IACtD,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IACjD,CAAC;IAEO,IAAI,CAAC,QAAyC;QACpD,IAAI,CAAC;YACH,KAAK,QAAQ,EAAE,EAAE,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;IACH,CAAC;uGA5CU,OAAO;wGAAP,OAAO;;2FAAP,OAAO;kBADnB,OAAO","sourcesContent":["/**\n * `Haptics`, bound to Expo's native module.\n *\n * Injected, not imported as functions: `inject(Haptics).impact()`. Nothing needs providing - the\n * source token carries its own factory, and a service nobody injects is never constructed.\n *\n * The native module is used directly rather than through `expo-haptics`'s JavaScript, which adds\n * one thing: a throw when the module is missing. Asking for it optionally answers the same\n * question without the throw, and sidesteps the enum its functions take. The package still has\n * to be installed - that is what links the native side.\n */\nimport { InjectionToken, Service, inject } from '@angular/core';\nimport { expoModule, optional, present } from './native.ts';\n\n/** How hard the collision felt. `rigid` and `soft` are iOS 13+. */\nexport type ImpactStyle = 'light' | 'medium' | 'heavy' | 'rigid' | 'soft';\n\n/** What happened, rather than how it felt. iOS plays a distinct pattern for each. */\nexport type NotificationType = 'success' | 'warning' | 'error';\n\n/** The slice of `expo-haptics` this needs. */\nexport interface NativeHaptics {\n impactAsync(style: ImpactStyle): Promise<void>;\n notificationAsync(type: NotificationType): Promise<void>;\n selectionAsync(): Promise<void>;\n}\n\n/**\n * Every method returns nothing and swallows failures.\n *\n * Expo's are all promises, and a caller who does not await one gets an unhandled rejection on a\n * device with no Taptic Engine, in a simulator, or on an Android build with vibration permission\n * off - none of which is a reason for anything to go wrong on screen. Nobody awaits a vibration.\n *\n * ponytail: silence means a broken haptic looks like a working one. If that ever needs\n * debugging, log under `__DEV__` rather than making callers handle a rejection.\n */\n@Service()\nexport class Haptics {\n /** Overridden in a test to record feedback that never reaches a Taptic Engine. */\n static readonly SOURCE = new InjectionToken<NativeHaptics | null>(\n 'angular-native.hapticsSource',\n {\n factory: () => {\n const core = optional(\n () => require('expo-modules-core') as typeof import('expo-modules-core'),\n );\n return expoModule('expo-haptics', () =>\n core?.requireOptionalNativeModule<NativeHaptics>('ExpoHaptics'),\n );\n },\n },\n );\n\n private readonly native = inject(Haptics.SOURCE);\n\n /** Whether the native module is installed at all. */\n get available(): boolean {\n return present(this.native) !== null;\n }\n\n /** A collision between interface elements. */\n impact(style: ImpactStyle = 'medium'): void {\n this.play(() => this.native?.impactAsync(style));\n }\n\n /** A task succeeded or failed. */\n notify(type: NotificationType): void {\n this.play(() => this.native?.notificationAsync(type));\n }\n\n /** A selection changed: the lightest of the three. */\n select(): void {\n this.play(() => this.native?.selectionAsync());\n }\n\n private play(feedback: () => Promise<void> | undefined): void {\n try {\n void feedback()?.catch(() => {});\n } catch {\n // A module that is present but not linked throws synchronously.\n }\n }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';
|
|
|
13
13
|
export { registerExpoUiViews } from './expo-ui.ts';
|
|
14
14
|
export { nativeState, type NativeState } from './native-state.ts';
|
|
15
15
|
export { EXPO_VIEWS, expoViewName, registerExpoView, registerExpoViews, type ExpoViewOptions, } from './register-expo-view.ts';
|
|
16
|
-
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, type UiZStackAlignment, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, type UiColorChangeEvent, UiForm, UiChart, type UiChartAreaStyle, type UiChartBarStyle, type UiChartDataPoint, type UiChartLineStyle, type UiChartPieStyle, type UiChartPointStyle, type UiChartPointSymbol, type UiChartRectangleStyle, type UiChartRuleStyle, type UiChartType, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, type UiStepperChangeEvent, UiTextField, type UiTextFieldChangeEvent, UiToggle, type UiToggleChangeEvent, UiSlider, type UiSliderChangeEvent, UiSlot, UiSwipeActions, UiText, UiVStack, type UiDate, type UiDateChangeEvent, type UiTimerInterval, type UiPickerOption, type UiModifier, UiBottomSheet, type UiBottomSheetViewFunctions, type UiPresentationDetent, UiGroup, UiNavigationStack, UiToolbar, UiToolbarItem, type UiToolbarItemPlacement, } from './expo-ui-components.ts';
|
|
16
|
+
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, type UiZStackAlignment, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiDropdownMenuItem, UiExposedDropdownMenu, UiExposedDropdownMenuBox, UiSegmentedButton, UiSingleChoiceSegmentedButtonRow, UiColorPicker, type UiColorChangeEvent, UiForm, UiChart, type UiChartAreaStyle, type UiChartBarStyle, type UiChartDataPoint, type UiChartLineStyle, type UiChartPieStyle, type UiChartPointStyle, type UiChartPointSymbol, type UiChartRectangleStyle, type UiChartRuleStyle, type UiChartType, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, type UiStepperChangeEvent, UiTextField, type UiTextFieldChangeEvent, UiToggle, type UiToggleChangeEvent, UiSlider, type UiSliderChangeEvent, UiSlot, UiSwipeActions, UiText, UiVStack, type UiDate, type UiDateChangeEvent, type UiTimerInterval, type UiPickerOption, type UiModifier, UiBottomSheet, type UiBottomSheetViewFunctions, type UiPresentationDetent, UiGroup, UiNavigationStack, UiToolbar, UiToolbarItem, type UiToolbarItemPlacement, } from './expo-ui-components.ts';
|
|
17
17
|
export { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';
|
|
18
18
|
export { ExpoGlass, ExpoGlassContainer, liquidGlassAvailable, type GlassEffectStyleConfig, type GlassStyle, } from './glass.ts';
|
|
19
19
|
export { ExpoSymbol, type SymbolAnimationSpec, type SymbolResizeMode, type SymbolScale, type SymbolType, type SymbolWeight, } from './symbol.ts';
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ export { NATIVE_VIEWS, registerNativeViews } from "./community-views.js";
|
|
|
13
13
|
export { registerExpoUiViews } from "./expo-ui.js";
|
|
14
14
|
export { nativeState } from "./native-state.js";
|
|
15
15
|
export { EXPO_VIEWS, expoViewName, registerExpoView, registerExpoViews, } from "./register-expo-view.js";
|
|
16
|
-
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiColorPicker, UiForm, UiChart, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, UiTextField, UiToggle, UiSlider, UiSlot, UiSwipeActions, UiText, UiVStack, UiBottomSheet, UiGroup, UiNavigationStack, UiToolbar, UiToolbarItem, } from "./expo-ui-components.js";
|
|
16
|
+
export { UiAccessoryWidgetBackground, UiButton, UiCapsule, UiCircle, UiEllipse, UiLabel, UiLink, UiRectangle, UiRoundedRectangle, UiUnevenRoundedRectangle, UiZStack, UiDatePicker, UiDivider, UiHost, UiImage, UiMenu, UiPicker, UiDropdownMenuItem, UiExposedDropdownMenu, UiExposedDropdownMenuBox, UiSegmentedButton, UiSingleChoiceSegmentedButtonRow, UiColorPicker, UiForm, UiChart, UiGauge, UiHStack, UiLabeledContent, UiList, UiProgress, UiSection, UiSpacer, UiStepper, UiTextField, UiToggle, UiSlider, UiSlot, UiSwipeActions, UiText, UiVStack, UiBottomSheet, UiGroup, UiNavigationStack, UiToolbar, UiToolbarItem, } from "./expo-ui-components.js";
|
|
17
17
|
export { ExpoImage } from "./expo-image.js";
|
|
18
18
|
export { ExpoGlass, ExpoGlassContainer, liquidGlassAvailable, } from "./glass.js";
|
|
19
19
|
export { ExpoSymbol, } from "./symbol.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,wBAAwB,EACxB,QAAQ,EAER,YAAY,EACZ,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,aAAa,EAEb,MAAM,EACN,OAAO,EAWP,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EAET,WAAW,EAEX,QAAQ,EAER,QAAQ,EAER,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EAMR,aAAa,EAGb,OAAO,EACP,iBAAiB,EACjB,SAAS,EACT,aAAa,GAEd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,GAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAkC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * What the package exports without naming a module.\n *\n * Everything with an Expo module behind it has its own entry point - `@ng-native/expo/\n * battery.ts` and friends - so that importing haptics does not make an app install the video\n * player. What is left here is the shared machinery: the permission shape every module spells\n * identically, the signal-from-a-listener those services are built out of, and the view names.\n */\nexport { observed, type Observed } from './observed.ts';\nexport {\n Permission,\n type ForegroundEvents,\n type PermissionApi,\n type PermissionResponse,\n} from './permissions.ts';\nexport {\n MissingModuleError,\n expoModule,\n optional,\n unavailable,\n type ModulePlatform,\n} from './native.ts';\nexport { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';\nexport { registerExpoUiViews } from './expo-ui.ts';\nexport { nativeState, type NativeState } from './native-state.ts';\nexport {\n EXPO_VIEWS,\n expoViewName,\n registerExpoView,\n registerExpoViews,\n type ExpoViewOptions,\n} from './register-expo-view.ts';\nexport {\n UiAccessoryWidgetBackground,\n UiButton,\n UiCapsule,\n UiCircle,\n UiEllipse,\n UiLabel,\n UiLink,\n UiRectangle,\n UiRoundedRectangle,\n UiUnevenRoundedRectangle,\n UiZStack,\n type UiZStackAlignment,\n UiDatePicker,\n UiDivider,\n UiHost,\n UiImage,\n UiMenu,\n UiPicker,\n UiColorPicker,\n type UiColorChangeEvent,\n UiForm,\n UiChart,\n type UiChartAreaStyle,\n type UiChartBarStyle,\n type UiChartDataPoint,\n type UiChartLineStyle,\n type UiChartPieStyle,\n type UiChartPointStyle,\n type UiChartPointSymbol,\n type UiChartRectangleStyle,\n type UiChartRuleStyle,\n type UiChartType,\n UiGauge,\n UiHStack,\n UiLabeledContent,\n UiList,\n UiProgress,\n UiSection,\n UiSpacer,\n UiStepper,\n type UiStepperChangeEvent,\n UiTextField,\n type UiTextFieldChangeEvent,\n UiToggle,\n type UiToggleChangeEvent,\n UiSlider,\n type UiSliderChangeEvent,\n UiSlot,\n UiSwipeActions,\n UiText,\n UiVStack,\n type UiDate,\n type UiDateChangeEvent,\n type UiTimerInterval,\n type UiPickerOption,\n type UiModifier,\n UiBottomSheet,\n type UiBottomSheetViewFunctions,\n type UiPresentationDetent,\n UiGroup,\n UiNavigationStack,\n UiToolbar,\n UiToolbarItem,\n type UiToolbarItemPlacement,\n} from './expo-ui-components.ts';\nexport { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';\nexport {\n ExpoGlass,\n ExpoGlassContainer,\n liquidGlassAvailable,\n type GlassEffectStyleConfig,\n type GlassStyle,\n} from './glass.ts';\nexport {\n ExpoSymbol,\n type SymbolAnimationSpec,\n type SymbolResizeMode,\n type SymbolScale,\n type SymbolType,\n type SymbolWeight,\n} from './symbol.ts';\nexport { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';\nexport {\n SegmentedControl,\n type SegmentedControlChangeEvent,\n type SegmentedControlFont,\n} from './segmented-control.ts';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAiB,MAAM,eAAe,CAAC;AACxD,OAAO,EACL,UAAU,GAIX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,WAAW,GAEZ,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,2BAA2B,EAC3B,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,wBAAwB,EACxB,QAAQ,EAER,YAAY,EACZ,SAAS,EACT,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,EACjB,gCAAgC,EAChC,aAAa,EAEb,MAAM,EACN,OAAO,EAWP,OAAO,EACP,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EAET,WAAW,EAEX,QAAQ,EAER,QAAQ,EAER,MAAM,EACN,cAAc,EACd,MAAM,EACN,QAAQ,EAMR,aAAa,EAGb,OAAO,EACP,iBAAiB,EACjB,SAAS,EACT,aAAa,GAEd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,SAAS,EAAkD,MAAM,iBAAiB,CAAC;AAC5F,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,oBAAoB,GAGrB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,UAAU,GAMX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,cAAc,EAAkC,MAAM,uBAAuB,CAAC;AACvF,OAAO,EACL,gBAAgB,GAGjB,MAAM,wBAAwB,CAAC","sourcesContent":["/**\n * What the package exports without naming a module.\n *\n * Everything with an Expo module behind it has its own entry point - `@ng-native/expo/\n * battery.ts` and friends - so that importing haptics does not make an app install the video\n * player. What is left here is the shared machinery: the permission shape every module spells\n * identically, the signal-from-a-listener those services are built out of, and the view names.\n */\nexport { observed, type Observed } from './observed.ts';\nexport {\n Permission,\n type ForegroundEvents,\n type PermissionApi,\n type PermissionResponse,\n} from './permissions.ts';\nexport {\n MissingModuleError,\n expoModule,\n optional,\n unavailable,\n type ModulePlatform,\n} from './native.ts';\nexport { NATIVE_VIEWS, registerNativeViews } from './community-views.ts';\nexport { registerExpoUiViews } from './expo-ui.ts';\nexport { nativeState, type NativeState } from './native-state.ts';\nexport {\n EXPO_VIEWS,\n expoViewName,\n registerExpoView,\n registerExpoViews,\n type ExpoViewOptions,\n} from './register-expo-view.ts';\nexport {\n UiAccessoryWidgetBackground,\n UiButton,\n UiCapsule,\n UiCircle,\n UiEllipse,\n UiLabel,\n UiLink,\n UiRectangle,\n UiRoundedRectangle,\n UiUnevenRoundedRectangle,\n UiZStack,\n type UiZStackAlignment,\n UiDatePicker,\n UiDivider,\n UiHost,\n UiImage,\n UiMenu,\n UiPicker,\n UiDropdownMenuItem,\n UiExposedDropdownMenu,\n UiExposedDropdownMenuBox,\n UiSegmentedButton,\n UiSingleChoiceSegmentedButtonRow,\n UiColorPicker,\n type UiColorChangeEvent,\n UiForm,\n UiChart,\n type UiChartAreaStyle,\n type UiChartBarStyle,\n type UiChartDataPoint,\n type UiChartLineStyle,\n type UiChartPieStyle,\n type UiChartPointStyle,\n type UiChartPointSymbol,\n type UiChartRectangleStyle,\n type UiChartRuleStyle,\n type UiChartType,\n UiGauge,\n UiHStack,\n UiLabeledContent,\n UiList,\n UiProgress,\n UiSection,\n UiSpacer,\n UiStepper,\n type UiStepperChangeEvent,\n UiTextField,\n type UiTextFieldChangeEvent,\n UiToggle,\n type UiToggleChangeEvent,\n UiSlider,\n type UiSliderChangeEvent,\n UiSlot,\n UiSwipeActions,\n UiText,\n UiVStack,\n type UiDate,\n type UiDateChangeEvent,\n type UiTimerInterval,\n type UiPickerOption,\n type UiModifier,\n UiBottomSheet,\n type UiBottomSheetViewFunctions,\n type UiPresentationDetent,\n UiGroup,\n UiNavigationStack,\n UiToolbar,\n UiToolbarItem,\n type UiToolbarItemPlacement,\n} from './expo-ui-components.ts';\nexport { ExpoImage, type ExpoImageContentFit, type ExpoImageSource } from './expo-image.ts';\nexport {\n ExpoGlass,\n ExpoGlassContainer,\n liquidGlassAvailable,\n type GlassEffectStyleConfig,\n type GlassStyle,\n} from './glass.ts';\nexport {\n ExpoSymbol,\n type SymbolAnimationSpec,\n type SymbolResizeMode,\n type SymbolScale,\n type SymbolType,\n type SymbolWeight,\n} from './symbol.ts';\nexport { DateTimePicker, type DateTimePickerChangeEvent } from './date-time-picker.ts';\nexport {\n SegmentedControl,\n type SegmentedControlChangeEvent,\n type SegmentedControlFont,\n} from './segmented-control.ts';\n"]}
|
package/dist/language-model.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* cancels the one before it.
|
|
17
17
|
*/
|
|
18
18
|
import { DestroyRef, InjectionToken, Service, computed, inject, signal, } from '@angular/core';
|
|
19
|
-
import { expoModule } from "./native.js";
|
|
19
|
+
import { expoModule, present } from "./native.js";
|
|
20
20
|
import * as i0 from "@angular/core";
|
|
21
21
|
const REPORTED = new Set([
|
|
22
22
|
'available',
|
|
@@ -49,6 +49,9 @@ export class LanguageModel {
|
|
|
49
49
|
const loaded = require('expo-local-llm');
|
|
50
50
|
return loaded.ExpoLocalLlmModule ? loaded : null;
|
|
51
51
|
}, ['ios']);
|
|
52
|
+
// Missing from the build: the stand-in, to say so when it is used.
|
|
53
|
+
if (llm && !present(llm))
|
|
54
|
+
return llm;
|
|
52
55
|
const module = llm?.ExpoLocalLlmModule;
|
|
53
56
|
if (!llm || !module)
|
|
54
57
|
return null;
|
|
@@ -90,7 +93,7 @@ export class LanguageModel {
|
|
|
90
93
|
/** Android's download of Gemini Nano, 0 to 1. Null until a download reports; iOS never does. */
|
|
91
94
|
downloadProgress = this.progress.asReadonly();
|
|
92
95
|
constructor() {
|
|
93
|
-
const native = this.native;
|
|
96
|
+
const native = present(this.native);
|
|
94
97
|
if (!native)
|
|
95
98
|
return;
|
|
96
99
|
const availability = native.onAvailabilityChange((value) => this.state.set(known(value)));
|
|
@@ -203,7 +206,8 @@ export class LanguageModel {
|
|
|
203
206
|
throw new LanguageModelUnavailableError(availability);
|
|
204
207
|
}
|
|
205
208
|
read() {
|
|
206
|
-
|
|
209
|
+
const native = present(this.native);
|
|
210
|
+
return native ? known(native.availability()) : 'notInstalled';
|
|
207
211
|
}
|
|
208
212
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.0", ngImport: i0, type: LanguageModel, deps: [], target: i0.ɵɵFactoryTarget.Service });
|
|
209
213
|
static ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.0", ngImport: i0, type: LanguageModel });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language-model.js","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EACL,UAAU,EACV,cAAc,EACd,OAAO,EACP,QAAQ,EACR,MAAM,EACN,MAAM,GAEP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;;AA0BzC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAS;IAC/B,WAAW;IACX,aAAa;IACb,YAAY;IACZ,UAAU;IACV,kBAAkB;IAClB,aAAa;IACb,SAAS;CACV,CAAC,CAAC;AAkCH,0DAA0D;AAC1D,MAAM,OAAO,6BAA8B,SAAQ,KAAK;IAC7C,MAAM,CAAkD;IAEjE,YAAY,MAAuD;QACjE,KAAK,CAAC,gDAAgD,MAAM,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAiBD;;;GAGG;AAEH,MAAM,OAAO,aAAa;IACxB,sDAAsD;IACtD,MAAM,CAAU,MAAM,GAAG,IAAI,cAAc,CACzC,oCAAoC,EACpC;QACE,OAAO,EAAE,GAAG,EAAE;YACZ,2EAA2E;YAC3E,MAAM,GAAG,GAAG,UAAU,CACpB,gBAAgB,EAChB,GAAG,EAAE;gBACH,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAoC,CAAC;gBAC5E,OAAO,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YACnD,CAAC,EACD,CAAC,KAAK,CAAC,CACR,CAAC;YACF,MAAM,MAAM,GAAG,GAAG,EAAE,kBAAkB,CAAC;YACvC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC;YACjC,OAAO;gBACL,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,EAAE;gBAC5C,oBAAoB,EAAE,CAAC,QAAQ,EAAE,EAAE,CACjC,MAAM,CAAC,WAAW,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;gBACnF,kBAAkB,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC/B,MAAM,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAC7E,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE;gBACtC,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE;oBAClB,MAAM,OAAO,GAAG,GAAG,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;oBAC7C,OAAO;wBACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;wBAC5C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;4BAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;4BAClF,IAAI,CAAC;gCACH,OAAO,MAAM,OAAO,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;4BAC9C,CAAC;oCAAS,CAAC;gCACT,MAAM,CAAC,MAAM,EAAE,CAAC;4BAClB,CAAC;wBACH,CAAC;wBACD,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE;wBACpC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;qBACjC,CAAC;gBACJ,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CACF,CAAC;IAEe,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,GAAG,MAAM,CAA4B,IAAI,CAAC,IAAI,EAAE;8EAAC,CAAC;IACvD,QAAQ,GAAG,MAAM,CAAgB,IAAI;iFAAC,CAAC;IAChD,SAAS,GAA+B,IAAI,CAAC;IAErD,uEAAuE;IAC9D,YAAY,GAAsC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;IAE1E,SAAS,GAAoB,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,WAAW;kFAAC,CAAC;IAEnF,gGAAgG;IACvF,gBAAgB,GAA0B,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IAE9E;QACE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,MAAM,YAAY,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC1F,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAChC,YAAY,CAAC,MAAM,EAAE,CAAC;YACtB,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,OAAO;QACL,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,iGAAiG;IACjG,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC;IAChC,CAAC;IAED,kGAAkG;IAClG,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,UAA2B,EAAE;QAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,cAAc,CAClB,MAAc,EACd,MAA2B,EAC3B,UAA2B,EAAE;QAE7B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7F,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAc,EAAE,UAA2B,EAAE;QAClD,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC;QAEzB,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE;iFAAC,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,CAA4B,WAAW;mFAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,MAAM,CAAgB,IAAI;kFAAC,CAAC;QAC1C,IAAI,OAAO,GAAiC,IAAI,CAAC;QACjD,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,MAAM,IAAI,GAAG,CAAC,OAAgB,EAAS,EAAE;YACvC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACrB,KAAK,CAAC,GAAG,CAAC,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACxE,MAAM,OAAO,CAAC;QAChB,CAAC,CAAC;QAEF,IAAI,MAAuB,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,MAAO,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YACtD,OAAO,GAAG,OAAO,CAAC;YAClB,MAAM,GAAG,OAAO;iBACb,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;iBAC1C,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;gBACd,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAChB,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;gBAC7C,OAAO,KAAK,CAAC;YACf,CAAC,EAAE,IAAI,CAAC;iBACP,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,OAAO,EAAE,CAAC;YACjB,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACjC,IAAI,CAAC;gBACH,IAAI,CAAC,OAAO,CAAC,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACP,uFAAuF;YACzF,CAAC;QACH,CAAC;QACD,4FAA4F;QAC5F,4CAA4C;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAEvB,MAAM,MAAM,GAAwB;YAClC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;YACvB,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE;YAC3B,KAAK,EAAE,KAAK,CAAC,UAAU,EAAE;YACzB,MAAM;YACN,MAAM,EAAE,GAAG,EAAE;gBACX,IAAI,MAAM,EAAE,KAAK,WAAW,IAAI,CAAC,OAAO;oBAAE,OAAO;gBACjD,SAAS,GAAG,IAAI,CAAC;gBACjB,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;YACxB,CAAC;SACF,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACzC,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,aAA4B,EAAE,MAAc;QAC7D,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,MAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACpD,IAAI,CAAC;YACH,OAAO,MAAM,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAED,iGAAiG;IACzF,qBAAqB;QAC3B,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,YAAY,KAAK,WAAW;YAAE,MAAM,IAAI,6BAA6B,CAAC,YAAY,CAAC,CAAC;IAC1F,CAAC;IAEO,IAAI;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IAC1E,CAAC;uGApLU,aAAa;wGAAb,aAAa;;2FAAb,aAAa;kBADzB,OAAO;;AAwLR,SAAS,KAAK,CAAC,KAAa;IAC1B,OAAO,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAmC,CAAC,CAAC,CAAC,SAAS,CAAC;AAChF,CAAC;AAED,4FAA4F;AAC5F,SAAS,MAAM,CAAC,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAmB;IAC5D,MAAM,OAAO,GAAG,MAAM,CAAC,WAAW,CAChC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CACpE,CAAC;IACF,OAAO;QACL,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;KAC1D,CAAC;AACJ,CAAC","sourcesContent":["/**\n * `LanguageModel`, bound to `expo-local-llm`.\n *\n * The model the operating system already has: Apple's Foundation Models on iOS 26 with Apple\n * Intelligence, Gemini Nano on the Android phones that ship it. Nothing is bundled and nothing is\n * sent anywhere; the prompt and the answer stay on the device.\n *\n * ```ts\n * private readonly model = inject(LanguageModel);\n * protected readonly answer = signal<LanguageModelStream | null>(null);\n * ask(prompt: string) { this.answer.set(this.model.stream(prompt)); }\n * ```\n *\n * Every call is a conversation of its own: a session is made, asked once and released, so one\n * prompt never sees the last one. The module runs one stream at a time, so starting a stream\n * cancels the one before it.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule } from './native.ts';\n\n/**\n * What `expo-local-llm` reports, plus `notInstalled` for an app without the module or a build\n * without its native code (Expo Go, or installed without a rebuild).\n *\n * - `available`: ready to answer.\n * - `notEligible`: this device or OS version cannot run it (before iOS 26, older iPhones, most\n * Android phones).\n * - `notEnabled`: the device can, but Apple Intelligence is switched off in Settings.\n * - `notReady`: the model is still being prepared by the system, usually downloading after\n * Apple Intelligence was switched on.\n * - `downloadRequired` / `downloading`: Android, where the app asks for Gemini Nano with\n * `download()`.\n * - `unknown`: the platform said something the module does not name.\n */\nexport type LanguageModelAvailability =\n | 'available'\n | 'notEligible'\n | 'notEnabled'\n | 'notReady'\n | 'downloadRequired'\n | 'downloading'\n | 'unknown'\n | 'notInstalled';\n\nconst REPORTED = new Set<string>([\n 'available',\n 'notEligible',\n 'notEnabled',\n 'notReady',\n 'downloadRequired',\n 'downloading',\n 'unknown',\n]);\n\nexport interface GenerateOptions {\n /** System instructions: who the model is and how it answers. Kept apart from the prompt. */\n readonly instructions?: string;\n readonly temperature?: number;\n /** Capped at 256 on Android. */\n readonly maxTokens?: number;\n readonly topK?: number;\n}\n\n/** A structured answer's shape: `expo-local-llm`'s own schema, a field per property. */\nexport type LanguageModelSchema = import('expo-local-llm').Schema;\n\ntype SessionConfig = import('expo-local-llm').SessionConfig;\n\n/** The slice of an `expo-local-llm` session this needs, with its token event unwrapped. */\nexport interface NativeLanguageSession {\n respond(prompt: string): Promise<string>;\n /** Resolves with the whole text, or the text so far when cancelled; rejects on failure. */\n stream(prompt: string, onText: (textSoFar: string) => void): Promise<string>;\n cancel(): Promise<void>;\n release(): void;\n}\n\n/** The slice of `expo-local-llm` this needs. */\nexport interface NativeLanguageModel {\n availability(): string;\n onAvailabilityChange(listener: (availability: string) => void): { remove(): void };\n onDownloadProgress(listener: (progress: number) => void): { remove(): void };\n download(): Promise<void>;\n session(config: SessionConfig): NativeLanguageSession;\n}\n\n/** Why a call was refused before it reached the model. */\nexport class LanguageModelUnavailableError extends Error {\n readonly reason: Exclude<LanguageModelAvailability, 'available'>;\n\n constructor(reason: Exclude<LanguageModelAvailability, 'available'>) {\n super(`The on-device language model is unavailable: ${reason}`);\n this.name = 'LanguageModelUnavailableError';\n this.reason = reason;\n }\n}\n\nexport type LanguageModelStreamStatus = 'streaming' | 'done' | 'cancelled' | 'failed';\n\n/** One streamed answer, as signals a template binds. */\nexport interface LanguageModelStream {\n /** The text so far; the whole answer once `status` is `done`. */\n readonly text: Signal<string>;\n readonly status: Signal<LanguageModelStreamStatus>;\n /** The failure's message while `status` is `failed`, otherwise null. */\n readonly error: Signal<string | null>;\n /** The final text (the partial text if cancelled). Rejects if the stream fails. */\n readonly result: Promise<string>;\n /** Stop generating. The text so far is kept. Does nothing once the stream has ended. */\n cancel(): void;\n}\n\n/**\n * The availability and download listeners follow the model for the life of the app, and are\n * removed when the app is destroyed.\n */\n@Service()\nexport class LanguageModel {\n /** Overridden in a test to answer without a model. */\n static readonly SOURCE = new InjectionToken<NativeLanguageModel | null>(\n 'angular-native.languageModelSource',\n {\n factory: () => {\n // The package loads without its native half, so that half is what decides.\n const llm = expoModule(\n 'expo-local-llm',\n () => {\n const loaded = require('expo-local-llm') as typeof import('expo-local-llm');\n return loaded.ExpoLocalLlmModule ? loaded : null;\n },\n ['ios'],\n );\n const module = llm?.ExpoLocalLlmModule;\n if (!llm || !module) return null;\n return {\n availability: () => module.getAvailability(),\n onAvailabilityChange: (listener) =>\n module.addListener('availabilityChange', (event) => listener(event.availability)),\n onDownloadProgress: (listener) =>\n module.addListener('downloadProgress', (event) => listener(event.progress)),\n download: () => module.downloadModel(),\n session: (config) => {\n const session = llm.createLLMSession(config);\n return {\n respond: (prompt) => session.respond(prompt),\n stream: async (prompt, onText) => {\n const tokens = session.addListener('token', (event) => onText(event.accumulated));\n try {\n return await session.streamResponse(prompt);\n } finally {\n tokens.remove();\n }\n },\n cancel: () => session.cancelStream(),\n release: () => session.release(),\n };\n },\n };\n },\n },\n );\n\n private readonly native = inject(LanguageModel.SOURCE);\n private readonly state = signal<LanguageModelAvailability>(this.read());\n private readonly progress = signal<number | null>(null);\n private streaming: LanguageModelStream | null = null;\n\n /** Whether the model can answer, and if not, the platform's reason. */\n readonly availability: Signal<LanguageModelAvailability> = this.state.asReadonly();\n\n readonly available: Signal<boolean> = computed(() => this.state() === 'available');\n\n /** Android's download of Gemini Nano, 0 to 1. Null until a download reports; iOS never does. */\n readonly downloadProgress: Signal<number | null> = this.progress.asReadonly();\n\n constructor() {\n const native = this.native;\n if (!native) return;\n const availability = native.onAvailabilityChange((value) => this.state.set(known(value)));\n const progress = native.onDownloadProgress((value) => this.progress.set(value));\n inject(DestroyRef).onDestroy(() => {\n availability.remove();\n progress.remove();\n });\n }\n\n /**\n * Ask the platform again. iOS announces a change when the app comes back to the front, which\n * covers Apple Intelligence being switched on in Settings; it does not announce the model\n * finishing its preparation while the app is open.\n */\n refresh(): LanguageModelAvailability {\n this.state.set(this.read());\n return this.state();\n }\n\n /** Ask Android to fetch Gemini Nano. Does nothing on iOS, where the system manages the model. */\n async download(): Promise<void> {\n await this.native?.download();\n }\n\n /** The whole answer at once. Rejects with the platform's reason when the model is unavailable. */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<string> {\n return this.once(config(options), prompt);\n }\n\n /**\n * An answer shaped by `schema`, parsed. iOS constrains the model's decoding to the schema, so\n * the answer always fits it; Android only asks the model to follow it, so check what comes back.\n */\n async generateObject<T = unknown>(\n prompt: string,\n schema: LanguageModelSchema,\n options: GenerateOptions = {},\n ): Promise<T> {\n const text = await this.once({ ...config(options), responseFormat: 'json', schema }, prompt);\n return JSON.parse(text) as T;\n }\n\n /**\n * The answer as it is written, into a signal. Cancels any stream still running, because the\n * module runs one at a time. Never throws: an unavailable model is a stream that has already\n * failed, with the reason as its error.\n */\n stream(prompt: string, options: GenerateOptions = {}): LanguageModelStream {\n this.streaming?.cancel();\n\n const text = signal('');\n const status = signal<LanguageModelStreamStatus>('streaming');\n const error = signal<string | null>(null);\n let session: NativeLanguageSession | null = null;\n let cancelled = false;\n\n const fail = (failure: unknown): never => {\n status.set('failed');\n error.set(failure instanceof Error ? failure.message : String(failure));\n throw failure;\n };\n\n let result: Promise<string>;\n try {\n this.refuseUnlessAvailable();\n const running = this.native!.session(config(options));\n session = running;\n result = running\n .stream(prompt, (soFar) => text.set(soFar))\n .then((final) => {\n text.set(final);\n status.set(cancelled ? 'cancelled' : 'done');\n return final;\n }, fail)\n .finally(() => running.release());\n } catch (refusal) {\n result = Promise.reject(refusal);\n try {\n fail(refusal);\n } catch {\n // Recorded on the signals at once, so a template shows the reason on its first render.\n }\n }\n // Handled here so a stream whose result nobody awaits is not an unhandled rejection; anyone\n // who does await it still sees the failure.\n result.catch(() => {});\n\n const stream: LanguageModelStream = {\n text: text.asReadonly(),\n status: status.asReadonly(),\n error: error.asReadonly(),\n result,\n cancel: () => {\n if (status() !== 'streaming' || !session) return;\n cancelled = true;\n void session.cancel();\n },\n };\n this.streaming = session ? stream : null;\n return stream;\n }\n\n private async once(sessionConfig: SessionConfig, prompt: string): Promise<string> {\n this.refuseUnlessAvailable();\n const session = this.native!.session(sessionConfig);\n try {\n return await session.respond(prompt);\n } finally {\n session.release();\n }\n }\n\n /** Re-reads rather than trusting the signal, which only moves when the platform announces it. */\n private refuseUnlessAvailable(): void {\n const availability = this.refresh();\n if (availability !== 'available') throw new LanguageModelUnavailableError(availability);\n }\n\n private read(): LanguageModelAvailability {\n return this.native ? known(this.native.availability()) : 'notInstalled';\n }\n}\n\nfunction known(value: string): LanguageModelAvailability {\n return REPORTED.has(value) ? (value as LanguageModelAvailability) : 'unknown';\n}\n\n/** Only the keys that were given, so the native side sees its own defaults for the rest. */\nfunction config({ instructions, ...sampling }: GenerateOptions): SessionConfig {\n const options = Object.fromEntries(\n Object.entries(sampling).filter(([, value]) => value !== undefined),\n );\n return {\n ...(instructions === undefined ? {} : { instructions }),\n ...(Object.keys(options).length === 0 ? {} : { options }),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"language-model.js","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `LanguageModel`, bound to `expo-local-llm`.\n *\n * The model the operating system already has: Apple's Foundation Models on iOS 26 with Apple\n * Intelligence, Gemini Nano on the Android phones that ship it. Nothing is bundled and nothing is\n * sent anywhere; the prompt and the answer stay on the device.\n *\n * ```ts\n * private readonly model = inject(LanguageModel);\n * protected readonly answer = signal<LanguageModelStream | null>(null);\n * ask(prompt: string) { this.answer.set(this.model.stream(prompt)); }\n * ```\n *\n * Every call is a conversation of its own: a session is made, asked once and released, so one\n * prompt never sees the last one. The module runs one stream at a time, so starting a stream\n * cancels the one before it.\n */\nimport {\n DestroyRef,\n InjectionToken,\n Service,\n computed,\n inject,\n signal,\n type Signal,\n} from '@angular/core';\nimport { expoModule, present } from './native.ts';\n\n/**\n * What `expo-local-llm` reports, plus `notInstalled` for an app without the module or a build\n * without its native code (Expo Go, or installed without a rebuild).\n *\n * - `available`: ready to answer.\n * - `notEligible`: this device or OS version cannot run it (before iOS 26, older iPhones, most\n * Android phones).\n * - `notEnabled`: the device can, but Apple Intelligence is switched off in Settings.\n * - `notReady`: the model is still being prepared by the system, usually downloading after\n * Apple Intelligence was switched on.\n * - `downloadRequired` / `downloading`: Android, where the app asks for Gemini Nano with\n * `download()`.\n * - `unknown`: the platform said something the module does not name.\n */\nexport type LanguageModelAvailability =\n | 'available'\n | 'notEligible'\n | 'notEnabled'\n | 'notReady'\n | 'downloadRequired'\n | 'downloading'\n | 'unknown'\n | 'notInstalled';\n\nconst REPORTED = new Set<string>([\n 'available',\n 'notEligible',\n 'notEnabled',\n 'notReady',\n 'downloadRequired',\n 'downloading',\n 'unknown',\n]);\n\nexport interface GenerateOptions {\n /** System instructions: who the model is and how it answers. Kept apart from the prompt. */\n readonly instructions?: string;\n readonly temperature?: number;\n /** Capped at 256 on Android. */\n readonly maxTokens?: number;\n readonly topK?: number;\n}\n\n/** A structured answer's shape: `expo-local-llm`'s own schema, a field per property. */\nexport type LanguageModelSchema = import('expo-local-llm').Schema;\n\ntype SessionConfig = import('expo-local-llm').SessionConfig;\n\n/** The slice of an `expo-local-llm` session this needs, with its token event unwrapped. */\nexport interface NativeLanguageSession {\n respond(prompt: string): Promise<string>;\n /** Resolves with the whole text, or the text so far when cancelled; rejects on failure. */\n stream(prompt: string, onText: (textSoFar: string) => void): Promise<string>;\n cancel(): Promise<void>;\n release(): void;\n}\n\n/** The slice of `expo-local-llm` this needs. */\nexport interface NativeLanguageModel {\n availability(): string;\n onAvailabilityChange(listener: (availability: string) => void): { remove(): void };\n onDownloadProgress(listener: (progress: number) => void): { remove(): void };\n download(): Promise<void>;\n session(config: SessionConfig): NativeLanguageSession;\n}\n\n/** Why a call was refused before it reached the model. */\nexport class LanguageModelUnavailableError extends Error {\n readonly reason: Exclude<LanguageModelAvailability, 'available'>;\n\n constructor(reason: Exclude<LanguageModelAvailability, 'available'>) {\n super(`The on-device language model is unavailable: ${reason}`);\n this.name = 'LanguageModelUnavailableError';\n this.reason = reason;\n }\n}\n\nexport type LanguageModelStreamStatus = 'streaming' | 'done' | 'cancelled' | 'failed';\n\n/** One streamed answer, as signals a template binds. */\nexport interface LanguageModelStream {\n /** The text so far; the whole answer once `status` is `done`. */\n readonly text: Signal<string>;\n readonly status: Signal<LanguageModelStreamStatus>;\n /** The failure's message while `status` is `failed`, otherwise null. */\n readonly error: Signal<string | null>;\n /** The final text (the partial text if cancelled). Rejects if the stream fails. */\n readonly result: Promise<string>;\n /** Stop generating. The text so far is kept. Does nothing once the stream has ended. */\n cancel(): void;\n}\n\n/**\n * The availability and download listeners follow the model for the life of the app, and are\n * removed when the app is destroyed.\n */\n@Service()\nexport class LanguageModel {\n /** Overridden in a test to answer without a model. */\n static readonly SOURCE = new InjectionToken<NativeLanguageModel | null>(\n 'angular-native.languageModelSource',\n {\n factory: () => {\n // The package loads without its native half, so that half is what decides.\n const llm = expoModule(\n 'expo-local-llm',\n () => {\n const loaded = require('expo-local-llm') as typeof import('expo-local-llm');\n return loaded.ExpoLocalLlmModule ? loaded : null;\n },\n ['ios'],\n );\n // Missing from the build: the stand-in, to say so when it is used.\n if (llm && !present(llm)) return llm as never;\n const module = llm?.ExpoLocalLlmModule;\n if (!llm || !module) return null;\n return {\n availability: () => module.getAvailability(),\n onAvailabilityChange: (listener) =>\n module.addListener('availabilityChange', (event) => listener(event.availability)),\n onDownloadProgress: (listener) =>\n module.addListener('downloadProgress', (event) => listener(event.progress)),\n download: () => module.downloadModel(),\n session: (config) => {\n const session = llm.createLLMSession(config);\n return {\n respond: (prompt) => session.respond(prompt),\n stream: async (prompt, onText) => {\n const tokens = session.addListener('token', (event) => onText(event.accumulated));\n try {\n return await session.streamResponse(prompt);\n } finally {\n tokens.remove();\n }\n },\n cancel: () => session.cancelStream(),\n release: () => session.release(),\n };\n },\n };\n },\n },\n );\n\n private readonly native = inject(LanguageModel.SOURCE);\n private readonly state = signal<LanguageModelAvailability>(this.read());\n private readonly progress = signal<number | null>(null);\n private streaming: LanguageModelStream | null = null;\n\n /** Whether the model can answer, and if not, the platform's reason. */\n readonly availability: Signal<LanguageModelAvailability> = this.state.asReadonly();\n\n readonly available: Signal<boolean> = computed(() => this.state() === 'available');\n\n /** Android's download of Gemini Nano, 0 to 1. Null until a download reports; iOS never does. */\n readonly downloadProgress: Signal<number | null> = this.progress.asReadonly();\n\n constructor() {\n const native = present(this.native);\n if (!native) return;\n const availability = native.onAvailabilityChange((value) => this.state.set(known(value)));\n const progress = native.onDownloadProgress((value) => this.progress.set(value));\n inject(DestroyRef).onDestroy(() => {\n availability.remove();\n progress.remove();\n });\n }\n\n /**\n * Ask the platform again. iOS announces a change when the app comes back to the front, which\n * covers Apple Intelligence being switched on in Settings; it does not announce the model\n * finishing its preparation while the app is open.\n */\n refresh(): LanguageModelAvailability {\n this.state.set(this.read());\n return this.state();\n }\n\n /** Ask Android to fetch Gemini Nano. Does nothing on iOS, where the system manages the model. */\n async download(): Promise<void> {\n await this.native?.download();\n }\n\n /** The whole answer at once. Rejects with the platform's reason when the model is unavailable. */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<string> {\n return this.once(config(options), prompt);\n }\n\n /**\n * An answer shaped by `schema`, parsed. iOS constrains the model's decoding to the schema, so\n * the answer always fits it; Android only asks the model to follow it, so check what comes back.\n */\n async generateObject<T = unknown>(\n prompt: string,\n schema: LanguageModelSchema,\n options: GenerateOptions = {},\n ): Promise<T> {\n const text = await this.once({ ...config(options), responseFormat: 'json', schema }, prompt);\n return JSON.parse(text) as T;\n }\n\n /**\n * The answer as it is written, into a signal. Cancels any stream still running, because the\n * module runs one at a time. Never throws: an unavailable model is a stream that has already\n * failed, with the reason as its error.\n */\n stream(prompt: string, options: GenerateOptions = {}): LanguageModelStream {\n this.streaming?.cancel();\n\n const text = signal('');\n const status = signal<LanguageModelStreamStatus>('streaming');\n const error = signal<string | null>(null);\n let session: NativeLanguageSession | null = null;\n let cancelled = false;\n\n const fail = (failure: unknown): never => {\n status.set('failed');\n error.set(failure instanceof Error ? failure.message : String(failure));\n throw failure;\n };\n\n let result: Promise<string>;\n try {\n this.refuseUnlessAvailable();\n const running = this.native!.session(config(options));\n session = running;\n result = running\n .stream(prompt, (soFar) => text.set(soFar))\n .then((final) => {\n text.set(final);\n status.set(cancelled ? 'cancelled' : 'done');\n return final;\n }, fail)\n .finally(() => running.release());\n } catch (refusal) {\n result = Promise.reject(refusal);\n try {\n fail(refusal);\n } catch {\n // Recorded on the signals at once, so a template shows the reason on its first render.\n }\n }\n // Handled here so a stream whose result nobody awaits is not an unhandled rejection; anyone\n // who does await it still sees the failure.\n result.catch(() => {});\n\n const stream: LanguageModelStream = {\n text: text.asReadonly(),\n status: status.asReadonly(),\n error: error.asReadonly(),\n result,\n cancel: () => {\n if (status() !== 'streaming' || !session) return;\n cancelled = true;\n void session.cancel();\n },\n };\n this.streaming = session ? stream : null;\n return stream;\n }\n\n private async once(sessionConfig: SessionConfig, prompt: string): Promise<string> {\n this.refuseUnlessAvailable();\n const session = this.native!.session(sessionConfig);\n try {\n return await session.respond(prompt);\n } finally {\n session.release();\n }\n }\n\n /** Re-reads rather than trusting the signal, which only moves when the platform announces it. */\n private refuseUnlessAvailable(): void {\n const availability = this.refresh();\n if (availability !== 'available') throw new LanguageModelUnavailableError(availability);\n }\n\n private read(): LanguageModelAvailability {\n const native = present(this.native);\n return native ? known(native.availability()) : 'notInstalled';\n }\n}\n\nfunction known(value: string): LanguageModelAvailability {\n return REPORTED.has(value) ? (value as LanguageModelAvailability) : 'unknown';\n}\n\n/** Only the keys that were given, so the native side sees its own defaults for the rest. */\nfunction config({ instructions, ...sampling }: GenerateOptions): SessionConfig {\n const options = Object.fromEntries(\n Object.entries(sampling).filter(([, value]) => value !== undefined),\n );\n return {\n ...(instructions === undefined ? {} : { instructions }),\n ...(Object.keys(options).length === 0 ? {} : { options }),\n };\n}\n"]}
|
package/dist/native.d.ts
CHANGED
|
@@ -45,8 +45,9 @@ export declare function currentPlatform(): ModulePlatform | null;
|
|
|
45
45
|
* The Expo module a service needs.
|
|
46
46
|
*
|
|
47
47
|
* Where the module exists for the platform the app is on, a module that is missing is a mistake
|
|
48
|
-
* the developer can fix, so this throws a `MissingModuleError` that says how -
|
|
49
|
-
* feature that silently does nothing.
|
|
48
|
+
* the developer can fix, so using what this answers throws a `MissingModuleError` that says how -
|
|
49
|
+
* rather than leave a feature that silently does nothing. Using it, not asking for it: see
|
|
50
|
+
* `missing`. Anywhere it cannot exist - an iOS-only module on Android, the
|
|
50
51
|
* web, and Node, where a test runs with no platform at all - this answers null, and the service
|
|
51
52
|
* goes inert, as code shared across platforms needs it to.
|
|
52
53
|
*
|
|
@@ -55,3 +56,17 @@ export declare function currentPlatform(): ModulePlatform | null;
|
|
|
55
56
|
* is not called for: see `nativeHalfMissing`.
|
|
56
57
|
*/
|
|
57
58
|
export declare function expoModule<T>(module: string, load: () => T | null | undefined, platforms?: readonly ModulePlatform[]): T | null;
|
|
59
|
+
/**
|
|
60
|
+
* The module, or null where it is missing from the build: for what a service does as it is made,
|
|
61
|
+
* a listener or a first read, which is skipped then rather than thrown, so the service is made and
|
|
62
|
+
* what the app calls on it is what says the module is missing. See `missing`. A factory that
|
|
63
|
+
* builds over the module hands the stand-in on where it is missing (`if (!present(expo)) return
|
|
64
|
+
* expo as never`), for the same reason.
|
|
65
|
+
*/
|
|
66
|
+
export declare function present<T>(value: T | null | undefined): T | null;
|
|
67
|
+
/**
|
|
68
|
+
* As `expoModule`, throwing at once where the module is missing: for a function an app calls
|
|
69
|
+
* itself, a player, a database or the splash screen, where no token is left marked and a failure
|
|
70
|
+
* after the call would be caught and lost.
|
|
71
|
+
*/
|
|
72
|
+
export declare function expoModuleNow<T>(module: string, load: () => T | null | undefined, platforms?: readonly ModulePlatform[]): T | null;
|