@idealyst/mcp-server 1.2.118 → 11.2.120

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.
@@ -551,6 +551,21 @@ var getLiveActivityGuideDefinition = {
551
551
  required: ["topic"]
552
552
  }
553
553
  };
554
+ var getNetworkGuideDefinition = {
555
+ name: "get_network_guide",
556
+ description: "Get documentation for @idealyst/network cross-platform network connectivity and utilities package. Covers useNetwork hook, fetchWithTimeout, retry, waitForNetwork, and examples.",
557
+ inputSchema: {
558
+ type: "object",
559
+ properties: {
560
+ topic: {
561
+ type: "string",
562
+ description: "Topic to get docs for: 'overview', 'api', 'examples'",
563
+ enum: ["overview", "api", "examples"]
564
+ }
565
+ },
566
+ required: ["topic"]
567
+ }
568
+ };
554
569
  var toolDefinitions = [
555
570
  // Component tools
556
571
  listComponentsDefinition,
@@ -586,6 +601,7 @@ var toolDefinitions = [
586
601
  getPaymentsGuideDefinition,
587
602
  getNotificationsGuideDefinition,
588
603
  getLiveActivityGuideDefinition,
604
+ getNetworkGuideDefinition,
589
605
  // Package tools
590
606
  listPackagesDefinition,
591
607
  getPackageDocsDefinition,
@@ -778,13 +794,17 @@ var componentMetadata = {
778
794
  "Standard, alert, and confirmation types",
779
795
  "Title and close button",
780
796
  "Backdrop click to close",
781
- "Animation types (slide, fade)"
797
+ "Animation types (slide, fade)",
798
+ "avoidKeyboard prop shifts dialog when keyboard opens (native)",
799
+ "height prop gives dialog a definite height so children can use flex: 1",
800
+ "Content area has flex: 1 \u2014 children can flex into available space"
782
801
  ],
783
802
  bestPractices: [
784
803
  "Use sparingly for important interactions",
785
804
  "Provide clear actions for dismissal",
786
805
  "Keep dialog content focused",
787
- "Use confirmation dialogs for destructive actions"
806
+ "Use confirmation dialogs for destructive actions",
807
+ "Use height or maxContentHeight when children need flex-based sizing"
788
808
  ]
789
809
  },
790
810
  Divider: {
@@ -838,9 +858,10 @@ var componentMetadata = {
838
858
  },
839
859
  Image: {
840
860
  category: "display",
841
- description: "Cross-platform image component with loading and error states",
861
+ description: "Cross-platform image component for displaying photos, pictures, and remote/local images with loading and error states",
842
862
  features: [
843
- "Multiple resize modes",
863
+ "Display photos, pictures, and media from URLs or local sources",
864
+ "Multiple resize modes (cover, contain, stretch, center)",
844
865
  "Loading placeholder",
845
866
  "Error fallback",
846
867
  "Lazy loading",
@@ -1128,12 +1149,14 @@ var componentMetadata = {
1128
1149
  "Multiple sizes",
1129
1150
  "Character count",
1130
1151
  "Auto-resize",
1131
- "Error state"
1152
+ "Error state",
1153
+ "fill prop for flex-based sizing (fills available space)"
1132
1154
  ],
1133
1155
  bestPractices: [
1134
1156
  "Use for multi-line input",
1135
1157
  "Show character limits when applicable",
1136
- "Provide appropriate placeholder text"
1158
+ "Provide appropriate placeholder text",
1159
+ "Use fill prop inside Dialog with avoidKeyboard to shrink with keyboard"
1137
1160
  ]
1138
1161
  },
1139
1162
  TextInput: {
@@ -1210,7 +1233,11 @@ var componentAliases = {
1210
1233
  fab: "IconButton",
1211
1234
  modal: "Dialog",
1212
1235
  tooltip: "Tooltip",
1213
- snackbar: "Toast"
1236
+ snackbar: "Toast",
1237
+ photo: "Image",
1238
+ picture: "Image",
1239
+ img: "Image",
1240
+ thumbnail: "Image"
1214
1241
  };
1215
1242
  function findComponentName(componentName) {
1216
1243
  if (componentMetadata[componentName]) {
@@ -2965,7 +2992,8 @@ yarn add @idealyst/audio
2965
2992
  2. **Audio Session** \u2014 On iOS/Android, configure the audio session category before recording/playback
2966
2993
  3. **Audio Profiles** \u2014 Pre-configured \`AudioConfig\` presets: \`speech\`, \`highQuality\`, \`studio\`, \`phone\`
2967
2994
  4. **Session Presets** \u2014 Pre-configured \`AudioSessionConfig\` presets: \`playback\`, \`record\`, \`voiceChat\`, \`ambient\`, \`default\`, \`backgroundRecord\`
2968
- 5. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
2995
+ 5. **Audio Level** \u2014 \`recorder.level\` returns \`AudioLevel { current: number, peak: number, rms: number, db: number }\`. All values are 0.0-1.0 except \`db\` (-Infinity to 0).
2996
+ 6. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
2969
2997
 
2970
2998
  ## Exports
2971
2999
 
@@ -3063,6 +3091,22 @@ interface UsePlayerOptions {
3063
3091
  | toggleMute | () => void | Toggle mute |
3064
3092
 
3065
3093
  > **Critical:** \`feedPCMData()\` accepts \`ArrayBufferLike | Int16Array\` \u2014 **NOT strings or base64**.
3094
+ > If you receive base64-encoded audio from an external API and need to play it back, you MUST decode it to binary first.
3095
+ > **Cross-platform base64 decode** (do NOT use \`atob()\` \u2014 it's browser-only):
3096
+ > \`\`\`typescript
3097
+ > // Cross-platform: works on web AND React Native
3098
+ > function base64ToArrayBuffer(base64: string): ArrayBuffer {
3099
+ > const binaryString = typeof atob !== 'undefined'
3100
+ > ? atob(base64)
3101
+ > : Buffer.from(base64, 'base64').toString('binary');
3102
+ > const bytes = new Uint8Array(binaryString.length);
3103
+ > for (let i = 0; i < binaryString.length; i++) {
3104
+ > bytes[i] = binaryString.charCodeAt(i);
3105
+ > }
3106
+ > return bytes.buffer;
3107
+ > }
3108
+ > // Then: player.feedPCMData(base64ToArrayBuffer(encodedAudio));
3109
+ > \`\`\`
3066
3110
 
3067
3111
  ---
3068
3112
 
@@ -3512,7 +3556,7 @@ function BackgroundTranscriber() {
3512
3556
 
3513
3557
  \`\`\`tsx
3514
3558
  import React from 'react';
3515
- import { View, Text } from '@idealyst/components';
3559
+ import { View, Text, Progress } from '@idealyst/components';
3516
3560
  import { useRecorder, AUDIO_PROFILES } from '@idealyst/audio';
3517
3561
 
3518
3562
  function AudioLevelMeter() {
@@ -3526,22 +3570,10 @@ function AudioLevelMeter() {
3526
3570
  <Text>Level: {Math.round(recorder.level.current * 100)}%</Text>
3527
3571
  <Text>Peak: {Math.round(recorder.level.peak * 100)}%</Text>
3528
3572
  <Text>dB: {recorder.level.db.toFixed(1)}</Text>
3529
- <View
3530
- style={{
3531
- height: 20,
3532
- backgroundColor: '#e0e0e0',
3533
- borderRadius: 10,
3534
- overflow: 'hidden',
3535
- }}
3536
- >
3537
- <View
3538
- style={{
3539
- width: \`\${recorder.level.current * 100}%\`,
3540
- height: '100%',
3541
- backgroundColor: recorder.level.current > 0.8 ? 'red' : 'green',
3542
- }}
3543
- />
3544
- </View>
3573
+ <Progress
3574
+ value={recorder.level.current * 100}
3575
+ intent={recorder.level.current > 0.8 ? 'danger' : 'success'}
3576
+ />
3545
3577
  </View>
3546
3578
  );
3547
3579
  }
@@ -3615,6 +3647,8 @@ function CameraScreen() {
3615
3647
 
3616
3648
  Renders the camera preview. Must receive a camera instance from \`useCamera().cameraRef.current\`.
3617
3649
 
3650
+ > **IMPORTANT:** CameraPreview uses \`resizeMode\` ('cover' | 'contain'). But when displaying captured photos, use the **Image** component with \`objectFit\` \u2014 NOT \`resizeMode\`. Image does NOT have a \`resizeMode\` prop. Example: \`<Image source={photo.uri} objectFit="cover" />\`.
3651
+
3618
3652
  \`\`\`typescript
3619
3653
  interface CameraPreviewProps {
3620
3654
  camera: ICamera | null; // Camera instance from useCamera().cameraRef.current
@@ -3996,7 +4030,9 @@ import {
3996
4030
  \`\`\`
3997
4031
 
3998
4032
  > **Common mistakes:**
4033
+ > - **\`pick()\` returns \`FilePickerResult\`, NOT an array.** Access files via \`result.files\`: \`result.files.length\`, \`result.files[0].uri\`. Do NOT write \`result.length\` or \`result[0]\` \u2014 \`FilePickerResult\` is an object: \`{ cancelled: boolean, files: PickedFile[], rejected: RejectedFile[] }\`.
3999
4034
  > - The method is \`pick()\`, NOT \`pickFiles()\`
4035
+ > - \`pick()\` accepts overrides DIRECTLY: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\`. The \`{ config: ... }\` wrapper is ONLY for \`useFilePicker()\` initialization.
4000
4036
  > - \`FileType\` values are: \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`
4001
4037
  > - \`PickedFile\` has \`uri\`, \`name\`, \`size\`, \`type\`, \`extension\` \u2014 dimensions are in optional \`dimensions?: { width, height }\`, NOT top-level \`width\`/\`height\`
4002
4038
 
@@ -4051,7 +4087,7 @@ interface UseFilePickerOptions {
4051
4087
  | permission | PermissionResult \\| null | Permission result |
4052
4088
  | error | FilePickerError \\| null | Current error |
4053
4089
  | files | PickedFile[] | Last picked files |
4054
- | pick | (config?) => Promise<FilePickerResult> | **Open file picker** |
4090
+ | pick | (overrides?: Partial\\<FilePickerConfig\\>) => Promise<FilePickerResult> | **Open file picker. Overrides go DIRECTLY \u2014 NOT wrapped in \`{ config }\`.** Example: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\` |
4055
4091
  | captureFromCamera | (options?) => Promise<FilePickerResult> | Open camera to capture |
4056
4092
  | clear | () => void | Clear picked files |
4057
4093
  | checkPermission | () => Promise<PermissionResult> | Check permission |
@@ -4188,6 +4224,27 @@ interface PickedFile {
4188
4224
 
4189
4225
  > **Note:** \`dimensions\` is an optional nested object \u2014 NOT top-level \`width\`/\`height\` properties.
4190
4226
 
4227
+ ### FilePickerResult (returned by pick())
4228
+
4229
+ \`\`\`typescript
4230
+ interface FilePickerResult {
4231
+ cancelled: boolean; // Whether user cancelled the picker
4232
+ files: PickedFile[]; // Picked files (empty if cancelled)
4233
+ rejected: RejectedFile[]; // Files rejected by validation
4234
+ error?: FilePickerError; // Error if picker failed
4235
+ }
4236
+ \`\`\`
4237
+
4238
+ > **CRITICAL:** \`pick()\` returns \`FilePickerResult\`, NOT an array. Always access \`result.files\` to get the array of picked files:
4239
+ > \`\`\`typescript
4240
+ > const result = await picker.pick();
4241
+ > if (!result.cancelled && result.files.length > 0) {
4242
+ > const file = result.files[0]; // \u2705 Correct
4243
+ > console.log(file.uri, file.name);
4244
+ > }
4245
+ > // \u274C WRONG: result.length, result[0] \u2014 FilePickerResult is NOT an array
4246
+ > \`\`\`
4247
+
4191
4248
  ### FilePickerConfig
4192
4249
 
4193
4250
  \`\`\`typescript
@@ -4836,9 +4893,9 @@ Animate any style property changes. Returns an animated style object.
4836
4893
 
4837
4894
  \`\`\`typescript
4838
4895
  interface AnimationOptions {
4839
- duration?: Duration; // ms or theme token key
4840
- easing?: EasingKey; // Theme easing key
4841
- delay?: number; // Delay before animation (ms)
4896
+ duration?: Duration; // ms or theme token key (e.g., 300, 500)
4897
+ easing?: EasingKey; // Theme easing key (e.g., 'easeOut', 'spring')
4898
+ delay?: number; // Delay in ms before animation starts (e.g., 100, 200). Useful for staggered entrance animations.
4842
4899
  }
4843
4900
 
4844
4901
  interface UseAnimatedStyleOptions extends AnimationOptions {
@@ -4865,6 +4922,20 @@ const entranceStyle = useAnimatedStyle(
4865
4922
  { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
4866
4923
  { duration: 400, easing: 'easeOut' }
4867
4924
  );
4925
+
4926
+ // Staggered entrance: use delay for each item
4927
+ const style1 = useAnimatedStyle(
4928
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
4929
+ { duration: 300, easing: 'easeOut', delay: 0 }
4930
+ );
4931
+ const style2 = useAnimatedStyle(
4932
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
4933
+ { duration: 300, easing: 'easeOut', delay: 100 }
4934
+ );
4935
+ const style3 = useAnimatedStyle(
4936
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
4937
+ { duration: 300, easing: 'easeOut', delay: 200 }
4938
+ );
4868
4939
  \`\`\`
4869
4940
 
4870
4941
  ---
@@ -5016,16 +5087,18 @@ For expand/collapse, animate \`opacity\` + \`maxHeight\` together. Do NOT includ
5016
5087
 
5017
5088
  ## Transform Syntax
5018
5089
 
5019
- Use simplified object syntax instead of React Native arrays:
5090
+ **ALWAYS use object syntax** for transforms in useAnimatedStyle/usePresence:
5020
5091
 
5021
5092
  \`\`\`typescript
5022
- // Recommended: object syntax
5093
+ // CORRECT: object syntax (required for animations)
5023
5094
  transform: { x: 10, y: 20, scale: 1.2, rotate: 45 }
5024
5095
 
5025
- // Legacy: array syntax (still supported)
5026
- transform: [{ translateX: 10 }, { translateY: 20 }, { scale: 1.2 }]
5096
+ // WRONG in useAnimatedStyle: array syntax does NOT animate correctly
5097
+ // transform: [{ translateX: 10 }, { translateY: 20 }] // \u274C won't animate
5027
5098
  \`\`\`
5028
5099
 
5100
+ > **IMPORTANT:** Array-syntax transforms (React Native style) do NOT work with \`useAnimatedStyle\` or \`usePresence\`. Always use the simplified object syntax: \`transform: { x, y, scale, rotate }\`. Array syntax is only valid in static \`style\` props on non-animated elements.
5101
+
5029
5102
  | Property | Type | Maps to |
5030
5103
  |----------|------|---------|
5031
5104
  | x | number | translateX |
@@ -9963,6 +10036,396 @@ function MyScreen() {
9963
10036
  `
9964
10037
  };
9965
10038
 
10039
+ // src/data/network-guides.ts
10040
+ var networkGuides = {
10041
+ "idealyst://network/overview": `# @idealyst/network Overview
10042
+
10043
+ Cross-platform network connectivity and utilities for React and React Native. Know when the user has network, what kind, and react to changes in real time.
10044
+
10045
+ ## Features
10046
+
10047
+ - **useNetwork Hook** \u2014 Real-time network state with isConnected, connection type, effective speed
10048
+ - **Network State Listener** \u2014 Subscribe to connectivity changes outside React
10049
+ - **Connection Details** \u2014 WiFi, cellular (2G/3G/4G/5G), ethernet, bluetooth, VPN detection
10050
+ - **Effective Connection Type** \u2014 slow-2g, 2g, 3g, 4g speed categories
10051
+ - **Internet Reachability** \u2014 Distinguish "connected to network" from "can reach internet"
10052
+ - **fetchWithTimeout** \u2014 Fetch wrapper with configurable timeout and auto-abort
10053
+ - **retry** \u2014 Exponential backoff retry with optional network-aware gating
10054
+ - **waitForNetwork** \u2014 Promise that resolves when the device comes back online
10055
+ - **Data Saver Detection** \u2014 Detect if user has enabled data-saving mode (web)
10056
+ - **Cross-Platform** \u2014 navigator.onLine + Network Information API (web), @react-native-community/netinfo (native)
10057
+ - **TypeScript** \u2014 Full type safety and IntelliSense
10058
+
10059
+ ## Installation
10060
+
10061
+ \`\`\`bash
10062
+ yarn add @idealyst/network
10063
+
10064
+ # React Native also needs:
10065
+ yarn add @react-native-community/netinfo
10066
+ cd ios && pod install
10067
+ \`\`\`
10068
+
10069
+ ## Quick Start
10070
+
10071
+ \`\`\`tsx
10072
+ import { useNetwork } from '@idealyst/network';
10073
+
10074
+ function App() {
10075
+ const { isConnected, type, effectiveType } = useNetwork();
10076
+
10077
+ if (!isConnected) {
10078
+ return <Text>You are offline</Text>;
10079
+ }
10080
+
10081
+ return (
10082
+ <Text>
10083
+ Connected via {type} ({effectiveType})
10084
+ </Text>
10085
+ );
10086
+ }
10087
+ \`\`\`
10088
+
10089
+ ## Platform Details
10090
+
10091
+ - **Web**: Uses \`navigator.onLine\`, \`online\`/\`offline\` events, and the Network Information API (\`navigator.connection\`) for connection type, downlink speed, RTT, and data saver detection
10092
+ - **React Native**: Uses \`@react-native-community/netinfo\` for connection type, cellular generation, internet reachability, and real-time state changes
10093
+ - **SSR**: Safe for server-side rendering \u2014 returns sensible defaults when \`navigator\` is unavailable
10094
+ `,
10095
+ "idealyst://network/api": `# Network API Reference
10096
+
10097
+ Complete API reference for @idealyst/network.
10098
+
10099
+ ## useNetwork
10100
+
10101
+ React hook for monitoring network connectivity. Returns reactive state that updates automatically.
10102
+
10103
+ \`\`\`tsx
10104
+ const {
10105
+ state, // NetworkState \u2014 full state object
10106
+ isConnected, // boolean \u2014 device has active network
10107
+ isInternetReachable,// boolean | null \u2014 can reach internet (null = unknown)
10108
+ type, // NetworkConnectionType \u2014 'wifi' | 'cellular' | 'ethernet' | ...
10109
+ effectiveType, // EffectiveConnectionType \u2014 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
10110
+ refresh, // () => Promise<NetworkState> \u2014 manually refresh state
10111
+ } = useNetwork(options?: UseNetworkOptions);
10112
+ \`\`\`
10113
+
10114
+ ### UseNetworkOptions
10115
+
10116
+ | Option | Type | Default | Description |
10117
+ |--------|------|---------|-------------|
10118
+ | fetchOnMount | boolean | true | Fetch initial state on mount |
10119
+ | reachabilityUrl | string | (google 204) | URL for reachability pings (web only) |
10120
+ | reachabilityPollInterval | number | 0 | Polling interval in ms for reachability checks (web only). 0 = disabled |
10121
+
10122
+ ---
10123
+
10124
+ ## getNetworkState
10125
+
10126
+ Get a one-time snapshot of the current network state.
10127
+
10128
+ \`\`\`tsx
10129
+ // Web (synchronous)
10130
+ import { getNetworkState } from '@idealyst/network';
10131
+ const state: NetworkState = getNetworkState();
10132
+
10133
+ // Native (async \u2014 requires NetInfo fetch)
10134
+ import { getNetworkState } from '@idealyst/network';
10135
+ const state: NetworkState = await getNetworkState();
10136
+ \`\`\`
10137
+
10138
+ **Note:** On web, \`getNetworkState()\` is synchronous. On native, it returns a Promise.
10139
+
10140
+ ---
10141
+
10142
+ ## addNetworkStateListener
10143
+
10144
+ Subscribe to network state changes outside of React.
10145
+
10146
+ \`\`\`tsx
10147
+ import { addNetworkStateListener } from '@idealyst/network';
10148
+
10149
+ const unsubscribe = addNetworkStateListener((state) => {
10150
+ console.log('Network changed:', state.isConnected, state.type);
10151
+ });
10152
+
10153
+ // Later:
10154
+ unsubscribe();
10155
+ \`\`\`
10156
+
10157
+ ---
10158
+
10159
+ ## fetchWithTimeout
10160
+
10161
+ Fetch wrapper that automatically aborts if the request exceeds a timeout.
10162
+
10163
+ \`\`\`tsx
10164
+ import { fetchWithTimeout } from '@idealyst/network';
10165
+
10166
+ const response = await fetchWithTimeout('https://api.example.com/data', {
10167
+ timeout: 5000, // 5 seconds (default: 10000)
10168
+ method: 'GET',
10169
+ headers: { 'Authorization': 'Bearer token' },
10170
+ });
10171
+ \`\`\`
10172
+
10173
+ ### FetchWithTimeoutOptions
10174
+
10175
+ Extends standard \`RequestInit\` with:
10176
+
10177
+ | Option | Type | Default | Description |
10178
+ |--------|------|---------|-------------|
10179
+ | timeout | number | 10000 | Timeout in milliseconds |
10180
+
10181
+ ---
10182
+
10183
+ ## retry
10184
+
10185
+ Retry a function with exponential backoff. Optionally waits for network before retrying.
10186
+
10187
+ \`\`\`tsx
10188
+ import { retry } from '@idealyst/network';
10189
+
10190
+ const data = await retry(
10191
+ () => fetch('https://api.example.com/data').then(r => r.json()),
10192
+ {
10193
+ maxRetries: 3,
10194
+ baseDelay: 1000,
10195
+ maxDelay: 30000,
10196
+ retryOnlyWhenConnected: true,
10197
+ },
10198
+ );
10199
+ \`\`\`
10200
+
10201
+ ### RetryOptions
10202
+
10203
+ | Option | Type | Default | Description |
10204
+ |--------|------|---------|-------------|
10205
+ | maxRetries | number | 3 | Maximum retry attempts |
10206
+ | baseDelay | number | 1000 | Base delay in ms (doubles each attempt) |
10207
+ | maxDelay | number | 30000 | Maximum delay cap in ms |
10208
+ | retryOnlyWhenConnected | boolean | true | Wait for network before retrying |
10209
+
10210
+ ---
10211
+
10212
+ ## waitForNetwork
10213
+
10214
+ Returns a promise that resolves when the device comes back online.
10215
+
10216
+ \`\`\`tsx
10217
+ import { waitForNetwork } from '@idealyst/network';
10218
+
10219
+ // Wait up to 30 seconds for connectivity
10220
+ await waitForNetwork({ timeout: 30000 });
10221
+ console.log('Back online!');
10222
+ \`\`\`
10223
+
10224
+ If already online, resolves immediately. Rejects with an error if the timeout is exceeded.
10225
+
10226
+ ### WaitForNetworkOptions
10227
+
10228
+ | Option | Type | Default | Description |
10229
+ |--------|------|---------|-------------|
10230
+ | timeout | number | 30000 | Max wait time in ms before rejecting |
10231
+
10232
+ ---
10233
+
10234
+ ## NetworkState
10235
+
10236
+ Full network state object returned by the hook and listeners.
10237
+
10238
+ \`\`\`tsx
10239
+ interface NetworkState {
10240
+ isConnected: boolean; // Device has active network
10241
+ isInternetReachable: boolean | null; // Internet reachable (null = unknown)
10242
+ type: NetworkConnectionType; // 'wifi' | 'cellular' | 'ethernet' | ...
10243
+ effectiveType: EffectiveConnectionType; // 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
10244
+ cellularGeneration: CellularGeneration; // '2g' | '3g' | '4g' | '5g' | null (native only)
10245
+ downlink: number | null; // Mbps (web only, from Network Info API)
10246
+ rtt: number | null; // Round-trip time ms (web only)
10247
+ isDataSaving: boolean | null; // Data saver enabled (web only)
10248
+ }
10249
+ \`\`\`
10250
+
10251
+ ## Type Aliases
10252
+
10253
+ \`\`\`tsx
10254
+ type NetworkConnectionType = 'wifi' | 'cellular' | 'ethernet' | 'bluetooth' | 'vpn' | 'other' | 'none' | 'unknown';
10255
+ type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
10256
+ type CellularGeneration = '2g' | '3g' | '4g' | '5g' | null;
10257
+ \`\`\`
10258
+ `,
10259
+ "idealyst://network/examples": `# Network Examples
10260
+
10261
+ Complete code examples for common @idealyst/network patterns.
10262
+
10263
+ ## Offline Banner
10264
+
10265
+ \`\`\`tsx
10266
+ import { useNetwork } from '@idealyst/network';
10267
+ import { View, Text } from '@idealyst/components';
10268
+
10269
+ function OfflineBanner() {
10270
+ const { isConnected } = useNetwork();
10271
+
10272
+ if (isConnected) return null;
10273
+
10274
+ return (
10275
+ <View style={{ backgroundColor: '#f44336', padding: 8 }}>
10276
+ <Text style={{ color: '#fff', textAlign: 'center' }}>
10277
+ No internet connection
10278
+ </Text>
10279
+ </View>
10280
+ );
10281
+ }
10282
+ \`\`\`
10283
+
10284
+ ## Adaptive Quality Based on Connection
10285
+
10286
+ \`\`\`tsx
10287
+ import { useNetwork } from '@idealyst/network';
10288
+
10289
+ function MediaPlayer({ videoId }: { videoId: string }) {
10290
+ const { effectiveType, isDataSaving } = useNetwork().state;
10291
+
10292
+ const quality = (() => {
10293
+ if (isDataSaving) return 'low';
10294
+ switch (effectiveType) {
10295
+ case 'slow-2g':
10296
+ case '2g':
10297
+ return 'low';
10298
+ case '3g':
10299
+ return 'medium';
10300
+ case '4g':
10301
+ default:
10302
+ return 'high';
10303
+ }
10304
+ })();
10305
+
10306
+ return <VideoPlayer videoId={videoId} quality={quality} />;
10307
+ }
10308
+ \`\`\`
10309
+
10310
+ ## Retry API Calls
10311
+
10312
+ \`\`\`tsx
10313
+ import { retry, fetchWithTimeout } from '@idealyst/network';
10314
+
10315
+ async function fetchUserProfile(userId: string) {
10316
+ const response = await retry(
10317
+ () => fetchWithTimeout(\`https://api.example.com/users/\${userId}\`, {
10318
+ timeout: 5000,
10319
+ }),
10320
+ { maxRetries: 3, baseDelay: 1000 },
10321
+ );
10322
+
10323
+ if (!response.ok) {
10324
+ throw new Error(\`Failed to fetch user: \${response.status}\`);
10325
+ }
10326
+
10327
+ return response.json();
10328
+ }
10329
+ \`\`\`
10330
+
10331
+ ## Wait for Network Before Action
10332
+
10333
+ \`\`\`tsx
10334
+ import { waitForNetwork } from '@idealyst/network';
10335
+
10336
+ async function syncData() {
10337
+ // If offline, wait up to 60 seconds for connectivity
10338
+ await waitForNetwork({ timeout: 60000 });
10339
+
10340
+ // Now we're online \u2014 sync
10341
+ const response = await fetch('https://api.example.com/sync', {
10342
+ method: 'POST',
10343
+ body: JSON.stringify(pendingChanges),
10344
+ });
10345
+
10346
+ return response.json();
10347
+ }
10348
+ \`\`\`
10349
+
10350
+ ## Network State Listener (Outside React)
10351
+
10352
+ \`\`\`tsx
10353
+ import { addNetworkStateListener } from '@idealyst/network';
10354
+
10355
+ // Subscribe to changes (e.g., in a service or module)
10356
+ const unsubscribe = addNetworkStateListener((state) => {
10357
+ if (!state.isConnected) {
10358
+ queueManager.pause();
10359
+ } else {
10360
+ queueManager.resume();
10361
+ }
10362
+ });
10363
+
10364
+ // Cleanup when done
10365
+ unsubscribe();
10366
+ \`\`\`
10367
+
10368
+ ## Connection Type Display
10369
+
10370
+ \`\`\`tsx
10371
+ import { useNetwork } from '@idealyst/network';
10372
+ import { View, Text } from '@idealyst/components';
10373
+
10374
+ function NetworkInfo() {
10375
+ const { state } = useNetwork();
10376
+
10377
+ return (
10378
+ <View>
10379
+ <Text>Status: {state.isConnected ? 'Online' : 'Offline'}</Text>
10380
+ <Text>Type: {state.type}</Text>
10381
+ <Text>Speed: {state.effectiveType}</Text>
10382
+ {state.cellularGeneration && (
10383
+ <Text>Cellular: {state.cellularGeneration}</Text>
10384
+ )}
10385
+ {state.downlink != null && (
10386
+ <Text>Downlink: {state.downlink} Mbps</Text>
10387
+ )}
10388
+ {state.rtt != null && (
10389
+ <Text>RTT: {state.rtt} ms</Text>
10390
+ )}
10391
+ {state.isDataSaving != null && (
10392
+ <Text>Data Saver: {state.isDataSaving ? 'On' : 'Off'}</Text>
10393
+ )}
10394
+ </View>
10395
+ );
10396
+ }
10397
+ \`\`\`
10398
+
10399
+ ## Fetch with Timeout
10400
+
10401
+ \`\`\`tsx
10402
+ import { fetchWithTimeout } from '@idealyst/network';
10403
+
10404
+ async function quickHealthCheck() {
10405
+ try {
10406
+ const response = await fetchWithTimeout('https://api.example.com/health', {
10407
+ timeout: 3000,
10408
+ method: 'HEAD',
10409
+ });
10410
+ return response.ok;
10411
+ } catch {
10412
+ return false;
10413
+ }
10414
+ }
10415
+ \`\`\`
10416
+
10417
+ ## Best Practices
10418
+
10419
+ 1. **Use \`useNetwork\` for UI** \u2014 The hook handles subscriptions and cleanup automatically
10420
+ 2. **Use \`addNetworkStateListener\` for services** \u2014 For non-React code (queue managers, sync services)
10421
+ 3. **Combine retry + fetchWithTimeout** \u2014 For resilient API calls
10422
+ 4. **Check \`isInternetReachable\`** \u2014 Being "connected" to WiFi doesn't mean you have internet
10423
+ 5. **Respect data saver** \u2014 Reduce payload sizes and avoid autoplay when \`isDataSaving\` is true
10424
+ 6. **Don't poll too aggressively** \u2014 If using \`reachabilityPollInterval\`, keep it >= 15000ms
10425
+ 7. **Use \`waitForNetwork\` for offline-first** \u2014 Queue operations and wait for connectivity to sync
10426
+ `
10427
+ };
10428
+
9966
10429
  // src/data/packages.ts
9967
10430
  var packages = {
9968
10431
  components: {
@@ -11145,6 +11608,52 @@ await end(info.id, { dismissalPolicy: 'default' });`,
11145
11608
  "deliveryActivity() / timerActivity() / mediaActivity() / progressActivity() - Template presets"
11146
11609
  ],
11147
11610
  relatedPackages: ["notifications"]
11611
+ },
11612
+ network: {
11613
+ name: "Network",
11614
+ npmName: "@idealyst/network",
11615
+ description: "Cross-platform network connectivity and utilities for React and React Native. Real-time connection monitoring, fetch with timeout, retry with exponential backoff, and wait-for-network primitives.",
11616
+ category: "utility",
11617
+ platforms: ["web", "native"],
11618
+ documentationStatus: "full",
11619
+ installation: "yarn add @idealyst/network",
11620
+ peerDependencies: [
11621
+ "@react-native-community/netinfo (native)"
11622
+ ],
11623
+ features: [
11624
+ "useNetwork hook \u2014 real-time isConnected, connection type, effective speed",
11625
+ "Network state listener for non-React code",
11626
+ "Connection type detection \u2014 WiFi, cellular, ethernet, bluetooth, VPN",
11627
+ "Effective connection speed \u2014 slow-2g, 2g, 3g, 4g",
11628
+ "Cellular generation \u2014 2G, 3G, 4G, 5G (native)",
11629
+ "Internet reachability \u2014 distinguish connected from reachable",
11630
+ "fetchWithTimeout \u2014 fetch with configurable auto-abort",
11631
+ "retry \u2014 exponential backoff with network-aware gating",
11632
+ "waitForNetwork \u2014 promise that resolves when back online",
11633
+ "Data saver detection (web)",
11634
+ "Downlink speed and RTT estimation (web)"
11635
+ ],
11636
+ quickStart: `import { useNetwork } from '@idealyst/network';
11637
+
11638
+ function App() {
11639
+ const { isConnected, type, effectiveType } = useNetwork();
11640
+
11641
+ if (!isConnected) {
11642
+ return <Text>You are offline</Text>;
11643
+ }
11644
+
11645
+ return <Text>Connected via {type} ({effectiveType})</Text>;
11646
+ }`,
11647
+ apiHighlights: [
11648
+ "useNetwork(options?) - React hook for real-time network state",
11649
+ "getNetworkState() - One-time state snapshot (sync on web, async on native)",
11650
+ "addNetworkStateListener(cb) - Subscribe to changes (returns unsubscribe)",
11651
+ "fetchWithTimeout(url, options) - Fetch with auto-abort timeout",
11652
+ "retry(fn, options) - Exponential backoff retry",
11653
+ "waitForNetwork(options) - Promise that resolves when online",
11654
+ "NetworkState { isConnected, isInternetReachable, type, effectiveType, cellularGeneration, downlink, rtt, isDataSaving }"
11655
+ ],
11656
+ relatedPackages: ["storage", "config"]
11148
11657
  }
11149
11658
  };
11150
11659
  function getPackagesByCategory() {
@@ -15567,14 +16076,19 @@ This server has tools for every aspect of the framework. **Look up APIs as you n
15567
16076
 
15568
16077
  ### Workflow
15569
16078
 
15570
- Write code iteratively. Look up each API right before you use it:
16079
+ Write code iteratively \u2014 **look up one API, write one file, repeat**:
16080
+
16081
+ 1. Call \`get_intro\` (this response)
16082
+ 2. Look up the API for your FIRST file only (e.g., \`get_component_types("Button,Card,Text")\`)
16083
+ 3. **Write that file immediately** using the Write tool
16084
+ 4. Then look up the next API you need, write the next file, and so on
15571
16085
 
15572
16086
  - **Before using a component** \u2014 call \`get_component_types\` for its props (supports batching: \`get_component_types("Button,Card,Text")\`)
15573
16087
  - **Before using a package** \u2014 call its dedicated \`get_*_guide\` tool with topic \`api\`
15574
16088
  - **Search icons once** \u2014 batch all terms into one call: \`search_icons("home settings check timer")\`
15575
16089
  - **Check recipes** \u2014 \`search_recipes\` for ready-made patterns you can adapt
15576
16090
 
15577
- Do NOT try to read all documentation before writing code. Start building, and look things up as you go.
16091
+ > **WARNING: Do NOT load multiple package guides, component types, AND icon searches all before writing your first file.** Each tool response is large. If you accumulate 4+ tool responses without writing any code, you WILL exhaust your output token budget and produce zero files. The correct pattern is: **research \u2192 write \u2192 research \u2192 write**, NOT **research \u2192 research \u2192 research \u2192 write**.
15578
16092
 
15579
16093
  ### Component Tools
15580
16094
  - \`list_components\` / \`search_components\` \u2014 Discover available components
@@ -15649,6 +16163,10 @@ const tabs: { icon: IconName }[] = [{ icon: 'home' }, { icon: 'search' }]; // NO
15649
16163
 
15650
16164
  > **CRITICAL:** Icon names do NOT have an \`mdi:\` prefix. Use bare names like \`'delete'\`, \`'home'\`, \`'check-circle'\` \u2014 NOT \`'mdi:delete'\`, \`'mdi:home'\`, etc.
15651
16165
  > **CRITICAL:** When defining arrays or objects with icon fields, type them as \`IconName\` \u2014 never \`string\`. Using \`string\` will cause TS2322 when passed to component props.
16166
+ > **CRITICAL:** Helper functions that return icon names MUST have return type \`IconName\`, not \`string\`. Example: \`function getIcon(type: string): IconName { return type === 'pdf' ? 'file-pdf-box' : 'file-document-outline'; }\` \u2014 NOT \`function getIcon(type: string): string\`.
16167
+ > **CRITICAL:** ONLY use icon names returned by \`search_icons\`. Do NOT guess or invent icon names \u2014 unverified names render as blank with runtime warnings. If you need an icon, search for it first.
16168
+
16169
+ **Common verified icon names** (use these without searching): \`home\`, \`cog\`, \`account\`, \`magnify\`, \`plus\`, \`close\`, \`check\`, \`chevron-left\`, \`chevron-right\`, \`chevron-down\`, \`chevron-up\`, \`arrow-left\`, \`arrow-right\`, \`menu\`, \`dots-vertical\`, \`pencil\`, \`delete\`, \`heart\`, \`star\`, \`bell\`, \`email\`, \`phone\`, \`camera\`, \`camera-flip\`, \`flash\`, \`flash-off\`, \`image\`, \`send\`, \`share\`, \`download\`, \`upload\`, \`eye\`, \`eye-off\`, \`lock\`, \`logout\`, \`refresh\`, \`calendar\`, \`clock\`, \`map-marker\`, \`chart-line\`, \`view-dashboard\`, \`account-group\`, \`message\`, \`information\`, \`alert\`, \`check-circle\`, \`close-circle\`, \`play\`, \`pause\`, \`stop\`, \`microphone\`, \`microphone-off\`, \`file-document-outline\`. For any icon NOT on this list, use \`search_icons\` to verify.
15652
16170
 
15653
16171
  ---
15654
16172
 
@@ -15658,26 +16176,27 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
15658
16176
 
15659
16177
  ### Component Props
15660
16178
  1. **Text** does NOT have \`variant\`, \`intent\`, \`size\`, \`fontSize\`, \`numberOfLines\`, \`ellipsizeMode\`, \`selectable\`, \`textColor\`, or \`onPress\`. Use \`typography\` (\`h1\`\u2013\`h6\`, \`subtitle1\`, \`subtitle2\`, \`body1\`, \`body2\`, \`caption\`), \`weight\` (\`light\`, \`normal\`, \`medium\`, \`semibold\`, \`bold\`), and \`color\` (\`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`). **\`textColor\` is an Icon-only prop** \u2014 Text uses \`color\`. For pressable text, wrap in \`Pressable\` or use \`Button type="text"\`.
15661
- 2. **TextInput** does NOT have \`label\`, \`error\`, \`editable\`, \`autoComplete\`, \`keyboardType\`, or \`onChange\`. Compose labels/errors with \`Text\` + \`View\`. Use \`onChangeText\`, \`inputMode\` (\`'text' | 'email' | 'password' | 'number'\` \u2014 NOT \`'decimal'\`, \`'numeric'\`, \`'tel'\`, \`'url'\`), and \`textContentType\`. TextArea is different \u2014 it DOES support \`label\`, \`error\`, \`rows\`, \`onChange\`.
16179
+ 2. **TextInput** does NOT have \`label\`, \`error\`, \`editable\`, \`autoComplete\`, \`keyboardType\`, or \`onChange\`. Compose labels/errors with \`Text\` + \`View\`. Use \`onChangeText\`, \`inputMode\` (\`'text' | 'email' | 'password' | 'number'\` \u2014 NOT \`'decimal'\`, \`'numeric'\`, \`'tel'\`, \`'url'\`), and \`textContentType\`. **TextArea has a DIFFERENT API** \u2014 it DOES support \`label\`, \`error\`, \`rows\`, \`onChange\`, but does NOT have \`onBlur\` or \`onChangeText\`. Always look up TextArea types separately.
15662
16180
  3. **Button/IconButton** \`type\` is \`'contained' | 'outlined' | 'text'\` \u2014 NOT \`'ghost'\`, \`'solid'\`, \`'default'\`. Button has \`leftIcon\` and \`rightIcon\` \u2014 NOT \`icon\`.
15663
16181
  4. **View** does NOT have \`direction\`, \`align\`, or \`onPress\` props. For touch handling, wrap content in \`Pressable\` from \`@idealyst/components\` (NOT from \`react-native\`): \`<Pressable onPress={handlePress}><View>...</View></Pressable>\`. For horizontal layout use \`style={{ flexDirection: 'row' }}\`. View spacing shorthand props (all accept Size: \`xs\`|\`sm\`|\`md\`|\`lg\`|\`xl\`): \`padding\`, \`paddingVertical\`, \`paddingHorizontal\`, \`margin\`, \`marginVertical\`, \`marginHorizontal\`, \`gap\`/\`spacing\`. Do NOT use \`paddingTop\`, \`paddingBottom\`, \`paddingLeft\`, \`paddingRight\`, \`marginTop\`, \`marginBottom\`, \`marginLeft\`, \`marginRight\` as shorthand props \u2014 they do NOT exist and will cause TS2353. For single-side spacing, use \`style={{ paddingTop: 16 }}\`. Other View props: \`background\`, \`radius\`, \`border\`, \`scrollable\`. \`border\` is \`'none' | 'thin' | 'thick'\` \u2014 NOT \`'outline'\`, \`'solid'\`.
15664
- 5. **Badge** \`type\` is \`'filled' | 'outlined' | 'dot'\` \u2014 NOT \`'soft'\`, \`'subtle'\`, \`'solid'\`.
16182
+ 5. **Badge** \`type\` is \`'filled' | 'outlined' | 'dot'\` \u2014 NOT \`'soft'\`, \`'subtle'\`, \`'solid'\`. **Intent type narrowing (CRITICAL):** TS widens string literals in objects/arrays to \`string\`, which fails \`intent\` props. Rules: (a) Simple ternaries in JSX work fine: \`<Badge intent={cond ? 'success' : 'danger'}>\`. (b) **Do NOT use \`as const\` on ternary expressions** \u2014 \`(cond ? 'a' : 'b') as const\` causes TS1355. (c) **Arrays/objects with intent values MUST use \`as const\`**: \`const items = [{ intent: 'success' as const, label: 'OK' }]\` or \`const items = [{ intent: 'success', label: 'OK' }] as const\`. Without \`as const\`, \`item.intent\` becomes \`string\` which fails. (d) For computed values, use typed variable: \`const intent: Intent = ...;\` (import Intent from @idealyst/theme). This applies to ALL components with intent/type props.
15665
16183
  6. **Avatar** uses \`src\` for image URL, \`fallback\` for initials \u2014 NOT \`name\`, \`initials\`, \`label\`. Shape: \`'circle' | 'square'\`.
15666
16184
  7. **Link** requires a \`to\` prop (path string) \u2014 it's a navigation link, NOT pressable text.
15667
16185
  8. **List** takes \`children\` \u2014 NOT \`data\`, \`renderItem\`, or \`keyExtractor\`. Map your data: \`<List>{items.map(item => <View key={item.id}>...</View>)}</List>\`
15668
16186
  9. **Skeleton** uses \`shape\` prop (\`'rectangle' | 'circle' | 'rounded'\`) \u2014 NOT \`variant\`. Props: \`width\`, \`height\`, \`shape\`, \`animation\` (\`'pulse' | 'wave' | 'none'\`). Do NOT build custom skeletons with react-native \`Animated\`.
15669
16187
  10. The component is **TextInput**, NOT \`Input\`.
15670
- 11. **Card** is a simple container \u2014 there are NO compound components like \`Card.Content\`, \`Card.Header\`, \`Card.Body\`, \`Card.Footer\`, \`Card.Title\`. Just put children directly inside \`<Card>...</Card>\`.
16188
+ 11. **Card** is a simple container \u2014 there are NO compound components like \`Card.Content\`, \`Card.Header\`, \`Card.Body\`, \`Card.Footer\`, \`Card.Title\`. Just put children directly inside \`<Card>...</Card>\`. **Card does NOT have \`border\`, \`scrollable\`, or \`backgroundColor\` props** (those are View-only). Card styling props: \`type\` (\`'default'|'outlined'|'elevated'|'filled'\`), \`radius\`, \`intent\`, \`background\`, plus spacing (\`padding\`, \`margin\`, \`gap\`). For borders use \`type="outlined"\`.
16189
+ 12. **Switch** uses \`checked\` and \`onChange\` \u2014 NOT \`value\` and \`onValueChange\` (React Native convention). Also has \`label\`, \`labelPosition\`, \`disabled\`.
15671
16190
 
15672
16191
  ### Navigation
15673
- 11. Use \`NavigatorProvider\` \u2014 there is NO \`Router\` export.
16192
+ 11. **NavigatorProvider** takes a \`route\` prop (SINGULAR) \u2014 NOT \`routes\`: \`<NavigatorProvider route={routeConfig} />\`. There is NO \`Router\` export.
15674
16193
  12. Use \`useNavigator()\` \u2014 NOT \`useNavigate()\`.
15675
16194
  13. \`navigate()\` takes an **object**: \`navigate({ path: '/settings' })\` \u2014 NOT \`navigate('/settings')\` or \`navigate('routeName', params)\`. To pass data use \`vars\`: \`navigate({ path: '/detail/:id', vars: { id: '123' } })\` \u2014 NOT \`params\`: \`navigate({ path: '/detail', params: { id: '123' } })\` (no \`params\` property exists).
15676
- 14. \`useNavigationState\` returns \`Record<string, unknown>\` by default. Always provide a type parameter: \`useNavigationState<{ title?: string }>()\`.
15677
- 15. \`useParams()\` does NOT accept generic type arguments. It returns \`Record<string, string>\`. Do NOT write \`useParams<{ id: string }>()\` \u2014 that causes TS2558.
16195
+ 14. \`navigate({ state: {...} })\` values must be \`string | number | boolean\` \u2014 NO \`undefined\`, \`null\`, or objects. When forwarding state across wizard steps, ensure all values are defined before passing: \`navigate({ path: '/step2', state: { firstName: name, age: 25 } })\`. Do NOT use optional fields in state types.
16196
+ 15. \`useParams()\` does NOT accept generic type arguments. It returns \`Record<string, string | undefined>\`. Do NOT write \`useParams<{ id: string }>()\` \u2014 that causes TS2558. Always guard for undefined: \`const id = params.id; if (!id) return null;\`.
15678
16197
 
15679
16198
  ### Imports & Styling
15680
- 16. **Never** import from \`react-native\` \u2014 no \`TouchableOpacity\`, \`FlatList\`, \`ScrollView\`, \`Animated\`, \`Dimensions\`, \`Linking\`, \`Platform\`. Idealyst provides cross-platform alternatives for all of these (e.g., \`openExternalLinks\` on Markdown, \`Pressable\` from \`@idealyst/components\`).
16199
+ 16. **Never** import from \`react-native\` \u2014 no \`TouchableOpacity\`, \`FlatList\`, \`ScrollView\`, \`Animated\`, \`Dimensions\`, \`Linking\`, \`Platform\`. Idealyst provides cross-platform alternatives for all of these (e.g., \`openExternalLinks\` on Markdown, \`Pressable\` from \`@idealyst/components\`). **\`ScrollView\` is NOT exported from \`@idealyst/components\`** \u2014 use \`<View scrollable>\` instead. Do NOT \`import { ScrollView } from '@idealyst/components'\` \u2014 it will cause a TS import error.
15681
16200
  17. **Never** import from \`react-native-unistyles\` \u2014 use \`@idealyst/theme\` (\`configureThemes\`, \`ThemeSettings\`, \`useTheme\`).
15682
16201
  18. **useTheme()** returns the Theme object **directly** (NOT wrapped): \`const theme = useTheme();\`. Do NOT destructure: \`const { theme } = useTheme()\` \u2014 causes TS2339.
15683
16202
  19. **Spacing & Layout**: Use component shorthand props for spacing \u2014 NOT \`theme.spacing\` (which does NOT exist). The correct patterns:
@@ -15692,13 +16211,28 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
15692
16211
  - **WRONG**: \`theme.colors.intent.danger\` \u2014 does NOT exist; intents are at \`theme.intents.danger\`
15693
16212
  - **WRONG**: \`theme.intents.primary.bg\` \u2014 IntentValue does NOT have \`bg\` or \`text\`. IntentValue has: \`primary\` (main color string), \`contrast\` (text-on-bg color), \`light\`, \`dark\`
15694
16213
  - **CORRECT**: \`theme.intents.primary.primary\` for the color, \`theme.intents.primary.contrast\` for text on that color
16214
+ - **Color key reference** (all keys available):
16215
+ - \`theme.colors.surface\`: \`screen\`, \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
16216
+ - \`theme.colors.text\`: \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
16217
+ - \`theme.colors.border\`: \`primary\`, \`secondary\`, \`tertiary\`, \`disabled\`
16218
+ - \`theme.intents\`: \`primary\`, \`secondary\`, \`success\`, \`warning\`, \`danger\`, \`info\`, \`neutral\` (each has \`.primary\`, \`.contrast\`, \`.light\`, \`.dark\`)
15695
16219
 
15696
16220
  ### Cross-Platform (CRITICAL)
15697
16221
  20. **Never use raw HTML/SVG elements** (\`<svg>\`, \`<circle>\`, \`<canvas>\`, \`<div>\`, \`<span>\`, \`<input>\`) \u2014 they are web-only and will crash on React Native, just like react-native primitives crash on web. Also never use CSS \`transition\` or \`animation\` properties in styles \u2014 they don't exist on native. If you need custom drawing or circular progress, use \`@idealyst/animate\` hooks or \`@idealyst/charts\` \u2014 never raw SVG.
15698
- 21. **Avoid web-only CSS** like \`cursor: 'pointer'\` in shared styles \u2014 not valid on native. Use \`Pressable\` or \`Button\` for interactive elements (they handle cursor automatically on web).
16222
+ 21. **Avoid web-only CSS** like \`cursor: 'pointer'\` in shared styles \u2014 not valid on native. Use \`Pressable\` or \`Button\` for interactive elements (they handle cursor automatically on web). Percentage widths (\`width: '50%'\`) do NOT work reliably on React Native \u2014 use \`flex\` instead.
16223
+ 23. **Use theme colors, never hardcoded hex** \u2014 import \`useTheme\` from \`@idealyst/theme\` and use \`theme.colors.surface.secondary\` (not \`'#f5f5f5'\`), \`theme.colors.border.primary\` (not \`'#e0e0e0'\`), \`theme.intents.primary.primary\` (not \`'#1976d2'\`). Hardcoded colors break dark mode and branding customization. **Exception:** Semi-transparent overlays for camera/media UIs may use \`rgba(0,0,0,0.5)\` since the theme has no overlay color. For other overlays, prefer \`theme.colors.surface.inverse\` with opacity.
16224
+ 22. **Platform file barrel exports**: When creating \`Component.web.tsx\` + \`Component.native.tsx\`, you need THREE index files:
16225
+ - \`index.web.ts\` \u2014 \`export { default as Component } from './Component.web';\`
16226
+ - \`index.native.ts\` \u2014 \`export { default as Component } from './Component.native';\`
16227
+ - \`index.ts\` \u2014 \`export { default as Component } from './Component.web';\` (base file for TypeScript resolution)
16228
+ - **WRONG**: \`export { default } from './Component';\` \u2014 no bare \`Component.ts\` exists, only \`.web.tsx\`/\`.native.tsx\`. This causes TS2307.
16229
+ - **Web-only styles in .web.tsx files**: Cast CSS-only properties with \`as any\`, NOT \`as React.CSSProperties\`. Example: \`style={{ position: 'fixed', transition: 'width 0.2s' } as any}\`. \`React.CSSProperties\` is incompatible with \`StyleProp<ViewStyle>\` \u2014 this causes TS2322.
15699
16230
 
15700
16231
  ### React 19 TypeScript
15701
16232
  - **useRef** requires an initial argument: \`useRef<T>(null)\` \u2014 NOT \`useRef<T>()\`. Omitting the argument causes TS2554. For non-null refs use: \`useRef<number>(0)\`, \`useRef<string[]>([])\`.
16233
+ - **onLayout** callback: The event shape is \`e.nativeEvent.layout.{x, y, width, height}\` \u2014 NOT \`e.layout.width\`. Always destructure: \`onLayout={(e) => { const { width, height } = e.nativeEvent.layout; }}\`. Writing \`e.layout\` causes TS2339.
16234
+ - **Badge children** accepts \`ReactNode\` including numbers \u2014 \`<Badge>{count}</Badge>\` is valid. But if you pass a number to a **string** prop, wrap it: \`<Badge>{String(count)}</Badge>\` or use template literal.
16235
+ - **String literals with apostrophes**: Use backticks or double quotes for strings containing apostrophes \u2014 \`\`I'll be back\`\` or \`"I'll be back"\` \u2014 NOT \`'I'll be back'\` (unescaped \`'\` breaks the string and causes cascading TS errors).
15702
16236
 
15703
16237
  ### Scaffolded Project Layout
15704
16238
  When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`idealyst create\`), files go in specific locations:
@@ -15715,11 +16249,11 @@ When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`i
15715
16249
  > **TIP:** Before writing backend code, call \`search_recipes({ query: "trpc" })\` to get the \`trpc-feature\` recipe \u2014 it has the complete step-by-step pattern with correct file paths.
15716
16250
 
15717
16251
  ### Package-Specific (call guide tools for full API)
15718
- 18. **Audio** (\`@idealyst/audio\`) is **PCM streaming**, NOT file-based. \`recorder.stop()\` returns \`void\`. Data is \`ArrayBufferLike\`, NOT strings. Call \`get_audio_guide\` topic \`api\`.
16252
+ 18. **Audio** (\`@idealyst/audio\`) is **PCM streaming**, NOT file-based. \`recorder.stop()\` returns \`void\`. Data is \`ArrayBufferLike\`, NOT strings. Do NOT use \`atob()\` for base64 decoding \u2014 it's browser-only. See the guide for cross-platform patterns. Call \`get_audio_guide\` topic \`api\`.
15719
16253
  19. **Camera** (\`@idealyst/camera\`): Component is \`CameraPreview\`, NOT \`Camera\`. Permission is \`requestPermission()\`, NOT \`requestCameraPermission\`. \`CameraStatus\` is an interface (\`.state\`, \`.permission\`), NOT a string. Call \`get_camera_guide\` topic \`api\`.
15720
- 20. **Files** (\`@idealyst/files\`): Method is \`pick()\`, NOT \`pickFiles()\`. \`useFilePicker()\` takes \`{ config: { allowedTypes: ['image'], multiple: true } }\` \u2014 NOT \`{ type: 'image' }\` (no \`type\` prop on options). \`FileType\` is \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`. Call \`get_files_guide\` topic \`api\`.
16254
+ 20. **Files** (\`@idealyst/files\`): \`pick()\` returns \`FilePickerResult\` (an **object**, NOT an array). Access files via \`result.files\`: \`result.files.length\`, \`result.files[0].uri\`. Do NOT write \`result.length\` or \`result[0]\`. \`useFilePicker()\` takes \`{ config: { allowedTypes: ['image'], multiple: true } }\` \u2014 NOT \`{ type: 'image' }\`. \`FileType\` is \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`. Call \`get_files_guide\` topic \`api\`.
15721
16255
  21. **Storage** (\`@idealyst/storage\`): Methods are \`getItem()\`/\`setItem()\`, NOT \`get()\`/\`set()\`. Values are string-only. Call \`get_storage_guide\` topic \`api\`.
15722
- 22. **Animate** (\`@idealyst/animate\`): There is NO \`useSequence\` or \`useKeyframes\` \u2014 only \`useAnimatedStyle\`, \`useAnimatedValue\`, \`usePresence\`, \`useGradientBorder\`. Easing values are **camelCase**: \`'easeOut'\` NOT \`'ease-out'\`. \`useAnimatedValue\` REQUIRES an initial number: \`useAnimatedValue(0)\` \u2014 NOT \`useAnimatedValue()\`. Call \`get_animate_guide\` topic \`api\`.
16256
+ 22. **Animate** (\`@idealyst/animate\`): There is NO \`useSequence\` or \`useKeyframes\` \u2014 only \`useAnimatedStyle\`, \`useAnimatedValue\`, \`usePresence\`, \`useGradientBorder\`. Easing values are **camelCase**: \`'easeOut'\` NOT \`'ease-out'\`. \`useAnimatedValue\` REQUIRES an initial number: \`useAnimatedValue(0)\` \u2014 NOT \`useAnimatedValue()\`. \`useAnimatedStyle\` supports a \`delay\` option (ms) for staggered entrances. Transform syntax: use **object** format \`{ x, y, scale, rotate }\` \u2014 NOT array format \`[{ translateX }]\` which doesn't animate. Call \`get_animate_guide\` topic \`api\`.
15723
16257
  23. **Charts** (\`@idealyst/charts\`): \`ChartDataSeries\` requires \`id\` and \`name\` (NOT \`label\`). \`AxisConfig\` uses \`show\` (NOT \`visible\`). \`tickFormat\` type is \`(value: number | string | Date) => string\`. Call \`get_charts_guide\` topic \`api\`.
15724
16258
  `;
15725
16259
 
@@ -23185,7 +23719,7 @@ import { fileURLToPath } from "url";
23185
23719
  // src/generated/types.json
23186
23720
  var types_default = {
23187
23721
  version: "1.0.93",
23188
- extractedAt: "2026-02-24T16:32:56.796Z",
23722
+ extractedAt: "2026-02-24T21:11:17.058Z",
23189
23723
  components: {
23190
23724
  Accordion: {
23191
23725
  name: "Accordion",
@@ -45486,7 +46020,7 @@ var types_default = {
45486
46020
  navigation: {
45487
46021
  TabBarScreenOptions: 'export type TabBarScreenOptions = {\n /**\n * Icon for tab/drawer navigation.\n *\n * Can be:\n * - A **string** (icon name) \u2014 e.g. `"home"`, `"cog"`. The default layout renders\n * `<Icon name={tabBarIcon} size="sm" />` automatically.\n * - A **render function** \u2014 receives `{ focused, color, size }`. The `size` param is\n * a number (from native tab bars); **ignore it** and use a Size token instead:\n * `tabBarIcon: ({ focused }) => <Icon name={focused ? \'home\' : \'home-outline\'} size="sm" />`\n */\n tabBarIcon?: string | ((props: { focused: boolean; color: string; size: string | number }) => React.ReactElement)\n\n /**\n * Label for tab/drawer navigation\n */\n tabBarLabel?: string;\n \n /**\n * Badge for tab navigation\n */\n tabBarBadge?: string | number;\n \n /**\n * Whether to show the tab bar for this screen\n */\n tabBarVisible?: boolean;\n} & ScreenOptions',
45488
46022
  NavigatorOptions: "export type NavigatorOptions = {\n\n \n /**\n * Custom header title component or string\n */\n headerTitle?: React.ComponentType | React.ReactElement | string;\n \n /**\n * Custom header left component (overrides back button)\n */\n headerLeft?: React.ComponentType | React.ReactElement;\n \n /**\n * Whether to show header back button\n */\n headerBackVisible?: boolean;\n \n /**\n * Custom header right component\n */\n headerRight?: React.ComponentType | React.ReactElement;\n \n /**\n * Whether to hide the native React Navigation header (mobile only)\n */\n headerShown?: boolean;\n}",
45489
- ScreenOptions: "export type ScreenOptions = {\n /**\n * Screen title for navigation headers\n */\n title?: string;\n headerShown?: boolean;\n /**\n * When true, renders the screen outside of parent layout wrappers.\n * Useful for fullscreen modals, onboarding flows, or any screen that\n * should not inherit the parent navigator's layout (header, sidebar, tabs, etc.)\n *\n * Web: Screen renders as a sibling route without the parent LayoutComponent\n * Native: Screen uses fullScreenModal presentation\n */\n fullScreen?: boolean;\n\n} & NavigatorOptions;",
46023
+ ScreenOptions: "export type ScreenOptions = {\n /**\n * Screen title for navigation headers\n */\n title?: string;\n headerShown?: boolean;\n /**\n * Icon name for this screen (used by custom layout components like sidebars, drawers, etc.)\n */\n icon?: string;\n /**\n * When true, renders the screen outside of parent layout wrappers.\n * Useful for fullscreen modals, onboarding flows, or any screen that\n * should not inherit the parent navigator's layout (header, sidebar, tabs, etc.)\n *\n * Web: Screen renders as a sibling route without the parent LayoutComponent\n * Native: Screen uses fullScreenModal presentation\n */\n fullScreen?: boolean;\n\n} & NavigatorOptions;",
45490
46024
  NotFoundComponentProps: "export type NotFoundComponentProps = {\n /** The full path that was attempted */\n path: string\n /** Any route parameters that were parsed from the path */\n params?: Record<string, string>\n}",
45491
46025
  BaseNavigatorParam: "export type BaseNavigatorParam = {\n path: string\n type: 'navigator'\n /**\n * Navigator options. When this navigator is nested inside a tab or drawer,\n * you can include TabBarScreenOptions (tabBarIcon, tabBarLabel, tabBarBadge)\n * so the parent layout can render the tab/drawer entry for this navigator.\n */\n options?: TabBarScreenOptions\n /**\n * Handler called when an invalid route is accessed.\n * - Return NavigateParams to redirect to a different route\n * - Return undefined to show the notFoundComponent (if set)\n * If not defined, bubbles up to parent navigator.\n *\n * @param invalidPath - The path that was attempted but not found\n * @returns NavigateParams to redirect, or undefined to use notFoundComponent\n */\n onInvalidRoute?: (invalidPath: string) => NavigateParams | undefined\n /**\n * Component to render/navigate to when route is invalid and onInvalidRoute returns undefined.\n * - Web: Renders at the current URL via catch-all route\n * - Native: Navigated to as a screen\n * - Optional: If not set and nothing handles the route, a warning is logged\n */\n notFoundComponent?: React.ComponentType<NotFoundComponentProps>\n}",
45492
46026
  TabNavigatorParam: "export type TabNavigatorParam = {\n layout: 'tab'\n routes: RouteParam<TabBarScreenOptions>[]\n layoutComponent?: TabLayoutComponent\n} & BaseNavigatorParam",
@@ -65129,12 +65663,32 @@ function postProcessComponentTypes(componentName, result) {
65129
65663
  }
65130
65664
  if (componentName.toLowerCase() === "card") {
65131
65665
  if (typeof result === "object" && result !== null) {
65132
- result.usageNote = 'Card is a SIMPLE CONTAINER \u2014 there are NO compound components. Do NOT use Card.Content, Card.Header, Card.Body, Card.Footer, Card.Title \u2014 they do NOT exist and will cause TS2339. Just put children directly inside <Card>...</Card>. Example: <Card padding="md"><Text>Title</Text><Text>Body</Text></Card>';
65666
+ result.usageNote = "Card is a SIMPLE CONTAINER \u2014 there are NO compound components. Do NOT use Card.Content, Card.Header, Card.Body, Card.Footer, Card.Title \u2014 they do NOT exist and will cause TS2339. Just put children directly inside <Card>...</Card>. Example: <Card padding=\"md\"><Text>Title</Text><Text>Body</Text></Card>. **Card does NOT have `border`, `scrollable`, or `backgroundColor` props** \u2014 those are View-only props. Using `border` on Card causes TS2322. For borders use `type='outlined'`. For elevated cards use `type='elevated'`. Card styling props: padding, paddingVertical, paddingHorizontal, margin, marginVertical, marginHorizontal, gap/spacing, radius, type ('default'|'outlined'|'elevated'|'filled'), intent, background, style, onPress, disabled. For custom shadows use style={{ ...theme.shadows.md }}.";
65133
65667
  }
65134
65668
  }
65135
65669
  if (componentName.toLowerCase() === "view") {
65136
65670
  if (typeof result === "object" && result !== null) {
65137
- result.usageNote = "View spacing shorthand props: padding, paddingVertical, paddingHorizontal, margin, marginVertical, marginHorizontal, gap/spacing. These accept Size values (xs, sm, md, lg, xl). Do NOT use paddingTop, paddingBottom, paddingLeft, paddingRight, marginTop, marginBottom, marginLeft, marginRight as shorthand props \u2014 they do NOT exist and will cause TS2353. For single-side spacing use style={{ paddingTop: 16 }}.";
65671
+ result.usageNote = "View spacing shorthand props: padding, paddingVertical, paddingHorizontal, margin, marginVertical, marginHorizontal, gap/spacing. These accept Size values (xs, sm, md, lg, xl). Do NOT use paddingTop, paddingBottom, paddingLeft, paddingRight, marginTop, marginBottom, marginLeft, marginRight as shorthand props \u2014 they do NOT exist and will cause TS2353. For single-side spacing use style={{ paddingTop: 16 }}. View does NOT have a `pointerEvents` JSX prop. Use style={{ pointerEvents: 'none' }} instead.";
65672
+ }
65673
+ }
65674
+ if (componentName.toLowerCase() === "textarea") {
65675
+ if (typeof result === "object" && result !== null) {
65676
+ result.usageNote = "TextArea has a DIFFERENT API from TextInput. TextArea uses onChange (not onChangeText) and does NOT have onBlur. TextArea DOES support label, error, and rows props (TextInput does NOT support label/error). TextArea supports `fill` prop: when true, all container layers get flex: 1 so the textarea expands to fill available vertical space (useful inside Dialog with avoidKeyboard). Always call get_component_types('TextArea') separately \u2014 do NOT assume it shares TextInput's props.";
65677
+ }
65678
+ }
65679
+ if (componentName.toLowerCase() === "image") {
65680
+ if (typeof result === "object" && result !== null) {
65681
+ result.usageNote = "Image uses `objectFit` (CSS convention) \u2014 NOT `resizeMode` (React Native convention). Valid objectFit values: 'contain', 'cover', 'fill', 'none', 'scale-down'. Image uses `source` prop (accepts URL string or ImageSourcePropType) \u2014 NOT `src`. Example: <Image source=\"https://example.com/photo.jpg\" objectFit=\"cover\" width={200} height={200} />";
65682
+ }
65683
+ }
65684
+ if (componentName.toLowerCase() === "icon") {
65685
+ if (typeof result === "object" && result !== null) {
65686
+ result.usageNote = "Icon color props: Use `intent` for semantic coloring (primary, danger, success, etc.) \u2014 renders the icon in the intent's primary color. Use `textColor` for text-semantic coloring (primary, secondary, tertiary, inverse) \u2014 renders the icon using theme.colors.text[textColor]. Use `color` for arbitrary hex/rgb colors. If the icon represents an action or status, use `intent`. If it should match surrounding text color, use `textColor`.";
65687
+ }
65688
+ }
65689
+ if (componentName.toLowerCase() === "badge") {
65690
+ if (typeof result === "object" && result !== null) {
65691
+ result.usageNote = "Badge `intent` expects Intent: 'primary' | 'success' | 'danger' | 'warning' | 'neutral' | 'info'. Simple ternaries in JSX work fine: `<Badge intent={x > 5 ? 'success' : 'danger'}>` \u2014 TS infers the union. **Do NOT use `as const` on ternary expressions** \u2014 `(cond ? 'a' : 'b') as const` causes TS1355. For arrays of objects, use `as const` on each property value: `{ intent: 'success' as const }`, or use `as const` on the entire array literal. For complex logic, declare a typed variable: `const intent: Intent = cond ? 'success' : 'danger';` (import Intent from @idealyst/theme). This applies to all intent/type props on Badge, Button, Card, Icon, etc.";
65138
65692
  }
65139
65693
  }
65140
65694
  return result;
@@ -65409,6 +65963,8 @@ function getNavigationTypes2(args = {}) {
65409
65963
  const result = getNavigationTypes(format);
65410
65964
  if (typeof result === "object" && result !== null) {
65411
65965
  result.tabBarIconNote = `IMPORTANT: tabBarIcon can be a string (icon name) or a render function. String form (simplest): tabBarIcon: 'home' \u2014 the layout renders <Icon name="home" size="sm" /> automatically. Function form: tabBarIcon: ({ focused }) => <Icon name={focused ? 'home' : 'home-outline'} size="sm" /> WARNING: The function receives { size: number } from native tab bars, but Icon expects a Size token ('xs'|'sm'|'md'|'lg'|'xl'). Do NOT pass the size param to Icon. Use a fixed size token like 'sm' or 'md' instead.`;
65966
+ result.layoutNote = "IMPORTANT: For custom layouts (sidebar, drawer), import Outlet from @idealyst/navigation \u2014 NOT from react-router-dom. Outlet renders the active route's content inside your layout. Example: import { Outlet, useNavigator } from '@idealyst/navigation'; ScreenOptions has: title, headerShown, icon (string), fullScreen. StackLayoutProps has: options, routes (RouteWithFullPath<ScreenOptions>[]), currentPath. Access route metadata: route.options?.icon, route.options?.title, route.fullPath.";
65967
+ result.usageExample = "## NavigatorProvider Usage\n\n```tsx\nimport { NavigatorProvider } from '@idealyst/navigation';\nimport type { NavigatorParam } from '@idealyst/navigation';\n\nconst routeConfig: NavigatorParam = {\n path: '/',\n type: 'navigator',\n layout: 'tab',\n routes: [\n { path: '/home', type: 'screen', component: HomeScreen, options: { title: 'Home', tabBarIcon: 'home' } },\n { path: '/settings', type: 'screen', component: SettingsScreen, options: { title: 'Settings', tabBarIcon: 'cog' } },\n ],\n};\n\n// CRITICAL: The prop is \"route\" (SINGULAR), NOT \"routes\"\nexport const App = () => <NavigatorProvider route={routeConfig} />;\n```";
65412
65968
  }
65413
65969
  return jsonResponse(result);
65414
65970
  } catch (error) {
@@ -65618,6 +66174,17 @@ function getLiveActivityGuide(args) {
65618
66174
  }
65619
66175
  return textResponse(guide);
65620
66176
  }
66177
+ function getNetworkGuide(args) {
66178
+ const topic = args.topic;
66179
+ const uri = `idealyst://network/${topic}`;
66180
+ const guide = networkGuides[uri];
66181
+ if (!guide) {
66182
+ return textResponse(
66183
+ `Topic "${topic}" not found. Available topics: overview, api, examples`
66184
+ );
66185
+ }
66186
+ return textResponse(guide);
66187
+ }
65621
66188
  function listPackages(args = {}) {
65622
66189
  const category = args.category;
65623
66190
  if (category) {
@@ -65868,6 +66435,7 @@ var toolHandlers = {
65868
66435
  get_payments_guide: getPaymentsGuide,
65869
66436
  get_notifications_guide: getNotificationsGuide,
65870
66437
  get_live_activity_guide: getLiveActivityGuide,
66438
+ get_network_guide: getNetworkGuide,
65871
66439
  list_packages: listPackages,
65872
66440
  get_package_docs: getPackageDocs,
65873
66441
  search_packages: searchPackages2,
@@ -65926,6 +66494,8 @@ export {
65926
66494
  searchRecipesDefinition,
65927
66495
  getInstallGuideDefinition,
65928
66496
  getIntroDefinition,
66497
+ getLiveActivityGuideDefinition,
66498
+ getNetworkGuideDefinition,
65929
66499
  toolDefinitions,
65930
66500
  toolDefinitionMap,
65931
66501
  getAvailableComponents,
@@ -65958,6 +66528,8 @@ export {
65958
66528
  getBiometricsGuide,
65959
66529
  getPaymentsGuide,
65960
66530
  getNotificationsGuide,
66531
+ getLiveActivityGuide,
66532
+ getNetworkGuide,
65961
66533
  listPackages,
65962
66534
  getPackageDocs,
65963
66535
  searchPackages2 as searchPackages,
@@ -65969,4 +66541,4 @@ export {
65969
66541
  toolHandlers,
65970
66542
  callTool
65971
66543
  };
65972
- //# sourceMappingURL=chunk-O2B33ZYL.js.map
66544
+ //# sourceMappingURL=chunk-RVMPULO4.js.map