@idealyst/mcp-server 1.2.117 → 1.2.119

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/dist/index.cjs CHANGED
@@ -2689,10 +2689,13 @@ Layout components are **web-only**. Native ignores \`layoutComponent\` entirely.
2689
2689
  layouts/
2690
2690
  AppLayout.web.tsx \u2190 Real layout with Outlet from @idealyst/navigation
2691
2691
  AppLayout.native.tsx \u2190 No-op mock (return null)
2692
+ index.ts \u2190 REQUIRED: base export for TypeScript resolution
2692
2693
  index.web.ts \u2190 export { AppLayout } from './AppLayout.web'
2693
2694
  index.native.ts \u2190 export { AppLayout } from './AppLayout.native'
2694
2695
  \`\`\`
2695
2696
 
2697
+ > **CRITICAL:** You MUST create a base \`index.ts\` alongside \`index.web.ts\` and \`index.native.ts\`. Without it, TypeScript cannot resolve \`import { AppLayout } from './layouts'\` and you get TS2307. The base \`index.ts\` re-exports from the web version (bundlers pick the platform-specific file at runtime).
2698
+
2696
2699
  ### Example Files
2697
2700
 
2698
2701
  **\`AppLayout.web.tsx\`** \u2014 The real layout:
@@ -2730,6 +2733,11 @@ export function AppLayout() {
2730
2733
  }
2731
2734
  \`\`\`
2732
2735
 
2736
+ **\`index.ts\`** \u2014 Base export (REQUIRED for TypeScript module resolution):
2737
+ \`\`\`ts
2738
+ export { AppLayout } from './AppLayout.web';
2739
+ \`\`\`
2740
+
2733
2741
  **\`index.web.ts\`**:
2734
2742
  \`\`\`ts
2735
2743
  export { AppLayout } from './AppLayout.web';
@@ -2760,7 +2768,9 @@ const appRouter: NavigatorParam = {
2760
2768
  > **Key rules:**
2761
2769
  > - Import \`Outlet\` from \`@idealyst/navigation\` (NOT from \`react-router-dom\`)
2762
2770
  > - Layout props do NOT include \`children\` \u2014 content renders via \`<Outlet />\`
2763
- > - Always create both \`.web.tsx\` and \`.native.tsx\` files with platform index files
2771
+ > - Always create \`.web.tsx\`, \`.native.tsx\`, AND a base \`index.ts\` \u2014 without the base \`index.ts\`, TypeScript reports TS2307 (cannot find module)
2772
+ > - The base \`index.ts\` re-exports from the \`.web\` version; bundlers pick the right platform file at runtime
2773
+ > - **Web-only CSS** in \`.web.tsx\`: cast with \`as any\`, NOT \`as React.CSSProperties\` \u2014 \`CSSProperties\` is incompatible with \`StyleProp<ViewStyle>\` (TS2322). Example: \`style={{ position: 'fixed', transition: 'width 0.2s' } as any}\`
2764
2774
 
2765
2775
  ## GeneralLayout Component
2766
2776
 
@@ -7449,6 +7459,52 @@ await end(info.id, { dismissalPolicy: 'default' });`,
7449
7459
  "deliveryActivity() / timerActivity() / mediaActivity() / progressActivity() - Template presets"
7450
7460
  ],
7451
7461
  relatedPackages: ["notifications"]
7462
+ },
7463
+ network: {
7464
+ name: "Network",
7465
+ npmName: "@idealyst/network",
7466
+ 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.",
7467
+ category: "utility",
7468
+ platforms: ["web", "native"],
7469
+ documentationStatus: "full",
7470
+ installation: "yarn add @idealyst/network",
7471
+ peerDependencies: [
7472
+ "@react-native-community/netinfo (native)"
7473
+ ],
7474
+ features: [
7475
+ "useNetwork hook \u2014 real-time isConnected, connection type, effective speed",
7476
+ "Network state listener for non-React code",
7477
+ "Connection type detection \u2014 WiFi, cellular, ethernet, bluetooth, VPN",
7478
+ "Effective connection speed \u2014 slow-2g, 2g, 3g, 4g",
7479
+ "Cellular generation \u2014 2G, 3G, 4G, 5G (native)",
7480
+ "Internet reachability \u2014 distinguish connected from reachable",
7481
+ "fetchWithTimeout \u2014 fetch with configurable auto-abort",
7482
+ "retry \u2014 exponential backoff with network-aware gating",
7483
+ "waitForNetwork \u2014 promise that resolves when back online",
7484
+ "Data saver detection (web)",
7485
+ "Downlink speed and RTT estimation (web)"
7486
+ ],
7487
+ quickStart: `import { useNetwork } from '@idealyst/network';
7488
+
7489
+ function App() {
7490
+ const { isConnected, type, effectiveType } = useNetwork();
7491
+
7492
+ if (!isConnected) {
7493
+ return <Text>You are offline</Text>;
7494
+ }
7495
+
7496
+ return <Text>Connected via {type} ({effectiveType})</Text>;
7497
+ }`,
7498
+ apiHighlights: [
7499
+ "useNetwork(options?) - React hook for real-time network state",
7500
+ "getNetworkState() - One-time state snapshot (sync on web, async on native)",
7501
+ "addNetworkStateListener(cb) - Subscribe to changes (returns unsubscribe)",
7502
+ "fetchWithTimeout(url, options) - Fetch with auto-abort timeout",
7503
+ "retry(fn, options) - Exponential backoff retry",
7504
+ "waitForNetwork(options) - Promise that resolves when online",
7505
+ "NetworkState { isConnected, isInternetReachable, type, effectiveType, cellularGeneration, downlink, rtt, isDataSaving }"
7506
+ ],
7507
+ relatedPackages: ["storage", "config"]
7452
7508
  }
7453
7509
  };
7454
7510
  function getPackagesByCategory() {
@@ -10940,6 +10996,21 @@ var getLiveActivityGuideDefinition = {
10940
10996
  required: ["topic"]
10941
10997
  }
10942
10998
  };
10999
+ var getNetworkGuideDefinition = {
11000
+ name: "get_network_guide",
11001
+ description: "Get documentation for @idealyst/network cross-platform network connectivity and utilities package. Covers useNetwork hook, fetchWithTimeout, retry, waitForNetwork, and examples.",
11002
+ inputSchema: {
11003
+ type: "object",
11004
+ properties: {
11005
+ topic: {
11006
+ type: "string",
11007
+ description: "Topic to get docs for: 'overview', 'api', 'examples'",
11008
+ enum: ["overview", "api", "examples"]
11009
+ }
11010
+ },
11011
+ required: ["topic"]
11012
+ }
11013
+ };
10943
11014
  var toolDefinitions = [
10944
11015
  // Component tools
10945
11016
  listComponentsDefinition,
@@ -10975,6 +11046,7 @@ var toolDefinitions = [
10975
11046
  getPaymentsGuideDefinition,
10976
11047
  getNotificationsGuideDefinition,
10977
11048
  getLiveActivityGuideDefinition,
11049
+ getNetworkGuideDefinition,
10978
11050
  // Package tools
10979
11051
  listPackagesDefinition,
10980
11052
  getPackageDocsDefinition,
@@ -11227,9 +11299,10 @@ var componentMetadata = {
11227
11299
  },
11228
11300
  Image: {
11229
11301
  category: "display",
11230
- description: "Cross-platform image component with loading and error states",
11302
+ description: "Cross-platform image component for displaying photos, pictures, and remote/local images with loading and error states",
11231
11303
  features: [
11232
- "Multiple resize modes",
11304
+ "Display photos, pictures, and media from URLs or local sources",
11305
+ "Multiple resize modes (cover, contain, stretch, center)",
11233
11306
  "Loading placeholder",
11234
11307
  "Error fallback",
11235
11308
  "Lazy loading",
@@ -11599,7 +11672,11 @@ var componentAliases = {
11599
11672
  fab: "IconButton",
11600
11673
  modal: "Dialog",
11601
11674
  tooltip: "Tooltip",
11602
- snackbar: "Toast"
11675
+ snackbar: "Toast",
11676
+ photo: "Image",
11677
+ picture: "Image",
11678
+ img: "Image",
11679
+ thumbnail: "Image"
11603
11680
  };
11604
11681
  function findComponentName(componentName) {
11605
11682
  if (componentMetadata[componentName]) {
@@ -11768,7 +11845,8 @@ yarn add @idealyst/audio
11768
11845
  2. **Audio Session** \u2014 On iOS/Android, configure the audio session category before recording/playback
11769
11846
  3. **Audio Profiles** \u2014 Pre-configured \`AudioConfig\` presets: \`speech\`, \`highQuality\`, \`studio\`, \`phone\`
11770
11847
  4. **Session Presets** \u2014 Pre-configured \`AudioSessionConfig\` presets: \`playback\`, \`record\`, \`voiceChat\`, \`ambient\`, \`default\`, \`backgroundRecord\`
11771
- 5. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
11848
+ 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).
11849
+ 6. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
11772
11850
 
11773
11851
  ## Exports
11774
11852
 
@@ -11866,6 +11944,22 @@ interface UsePlayerOptions {
11866
11944
  | toggleMute | () => void | Toggle mute |
11867
11945
 
11868
11946
  > **Critical:** \`feedPCMData()\` accepts \`ArrayBufferLike | Int16Array\` \u2014 **NOT strings or base64**.
11947
+ > If you receive base64-encoded audio from an external API and need to play it back, you MUST decode it to binary first.
11948
+ > **Cross-platform base64 decode** (do NOT use \`atob()\` \u2014 it's browser-only):
11949
+ > \`\`\`typescript
11950
+ > // Cross-platform: works on web AND React Native
11951
+ > function base64ToArrayBuffer(base64: string): ArrayBuffer {
11952
+ > const binaryString = typeof atob !== 'undefined'
11953
+ > ? atob(base64)
11954
+ > : Buffer.from(base64, 'base64').toString('binary');
11955
+ > const bytes = new Uint8Array(binaryString.length);
11956
+ > for (let i = 0; i < binaryString.length; i++) {
11957
+ > bytes[i] = binaryString.charCodeAt(i);
11958
+ > }
11959
+ > return bytes.buffer;
11960
+ > }
11961
+ > // Then: player.feedPCMData(base64ToArrayBuffer(encodedAudio));
11962
+ > \`\`\`
11869
11963
 
11870
11964
  ---
11871
11965
 
@@ -12315,7 +12409,7 @@ function BackgroundTranscriber() {
12315
12409
 
12316
12410
  \`\`\`tsx
12317
12411
  import React from 'react';
12318
- import { View, Text } from '@idealyst/components';
12412
+ import { View, Text, Progress } from '@idealyst/components';
12319
12413
  import { useRecorder, AUDIO_PROFILES } from '@idealyst/audio';
12320
12414
 
12321
12415
  function AudioLevelMeter() {
@@ -12329,22 +12423,10 @@ function AudioLevelMeter() {
12329
12423
  <Text>Level: {Math.round(recorder.level.current * 100)}%</Text>
12330
12424
  <Text>Peak: {Math.round(recorder.level.peak * 100)}%</Text>
12331
12425
  <Text>dB: {recorder.level.db.toFixed(1)}</Text>
12332
- <View
12333
- style={{
12334
- height: 20,
12335
- backgroundColor: '#e0e0e0',
12336
- borderRadius: 10,
12337
- overflow: 'hidden',
12338
- }}
12339
- >
12340
- <View
12341
- style={{
12342
- width: \`\${recorder.level.current * 100}%\`,
12343
- height: '100%',
12344
- backgroundColor: recorder.level.current > 0.8 ? 'red' : 'green',
12345
- }}
12346
- />
12347
- </View>
12426
+ <Progress
12427
+ value={recorder.level.current * 100}
12428
+ intent={recorder.level.current > 0.8 ? 'danger' : 'success'}
12429
+ />
12348
12430
  </View>
12349
12431
  );
12350
12432
  }
@@ -12418,6 +12500,8 @@ function CameraScreen() {
12418
12500
 
12419
12501
  Renders the camera preview. Must receive a camera instance from \`useCamera().cameraRef.current\`.
12420
12502
 
12503
+ > **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" />\`.
12504
+
12421
12505
  \`\`\`typescript
12422
12506
  interface CameraPreviewProps {
12423
12507
  camera: ICamera | null; // Camera instance from useCamera().cameraRef.current
@@ -12799,7 +12883,9 @@ import {
12799
12883
  \`\`\`
12800
12884
 
12801
12885
  > **Common mistakes:**
12886
+ > - **\`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[] }\`.
12802
12887
  > - The method is \`pick()\`, NOT \`pickFiles()\`
12888
+ > - \`pick()\` accepts overrides DIRECTLY: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\`. The \`{ config: ... }\` wrapper is ONLY for \`useFilePicker()\` initialization.
12803
12889
  > - \`FileType\` values are: \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`
12804
12890
  > - \`PickedFile\` has \`uri\`, \`name\`, \`size\`, \`type\`, \`extension\` \u2014 dimensions are in optional \`dimensions?: { width, height }\`, NOT top-level \`width\`/\`height\`
12805
12891
 
@@ -12854,7 +12940,7 @@ interface UseFilePickerOptions {
12854
12940
  | permission | PermissionResult \\| null | Permission result |
12855
12941
  | error | FilePickerError \\| null | Current error |
12856
12942
  | files | PickedFile[] | Last picked files |
12857
- | pick | (config?) => Promise<FilePickerResult> | **Open file picker** |
12943
+ | 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'] } })\` |
12858
12944
  | captureFromCamera | (options?) => Promise<FilePickerResult> | Open camera to capture |
12859
12945
  | clear | () => void | Clear picked files |
12860
12946
  | checkPermission | () => Promise<PermissionResult> | Check permission |
@@ -12991,6 +13077,27 @@ interface PickedFile {
12991
13077
 
12992
13078
  > **Note:** \`dimensions\` is an optional nested object \u2014 NOT top-level \`width\`/\`height\` properties.
12993
13079
 
13080
+ ### FilePickerResult (returned by pick())
13081
+
13082
+ \`\`\`typescript
13083
+ interface FilePickerResult {
13084
+ cancelled: boolean; // Whether user cancelled the picker
13085
+ files: PickedFile[]; // Picked files (empty if cancelled)
13086
+ rejected: RejectedFile[]; // Files rejected by validation
13087
+ error?: FilePickerError; // Error if picker failed
13088
+ }
13089
+ \`\`\`
13090
+
13091
+ > **CRITICAL:** \`pick()\` returns \`FilePickerResult\`, NOT an array. Always access \`result.files\` to get the array of picked files:
13092
+ > \`\`\`typescript
13093
+ > const result = await picker.pick();
13094
+ > if (!result.cancelled && result.files.length > 0) {
13095
+ > const file = result.files[0]; // \u2705 Correct
13096
+ > console.log(file.uri, file.name);
13097
+ > }
13098
+ > // \u274C WRONG: result.length, result[0] \u2014 FilePickerResult is NOT an array
13099
+ > \`\`\`
13100
+
12994
13101
  ### FilePickerConfig
12995
13102
 
12996
13103
  \`\`\`typescript
@@ -13639,9 +13746,9 @@ Animate any style property changes. Returns an animated style object.
13639
13746
 
13640
13747
  \`\`\`typescript
13641
13748
  interface AnimationOptions {
13642
- duration?: Duration; // ms or theme token key
13643
- easing?: EasingKey; // Theme easing key
13644
- delay?: number; // Delay before animation (ms)
13749
+ duration?: Duration; // ms or theme token key (e.g., 300, 500)
13750
+ easing?: EasingKey; // Theme easing key (e.g., 'easeOut', 'spring')
13751
+ delay?: number; // Delay in ms before animation starts (e.g., 100, 200). Useful for staggered entrance animations.
13645
13752
  }
13646
13753
 
13647
13754
  interface UseAnimatedStyleOptions extends AnimationOptions {
@@ -13668,6 +13775,20 @@ const entranceStyle = useAnimatedStyle(
13668
13775
  { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13669
13776
  { duration: 400, easing: 'easeOut' }
13670
13777
  );
13778
+
13779
+ // Staggered entrance: use delay for each item
13780
+ const style1 = useAnimatedStyle(
13781
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13782
+ { duration: 300, easing: 'easeOut', delay: 0 }
13783
+ );
13784
+ const style2 = useAnimatedStyle(
13785
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13786
+ { duration: 300, easing: 'easeOut', delay: 100 }
13787
+ );
13788
+ const style3 = useAnimatedStyle(
13789
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13790
+ { duration: 300, easing: 'easeOut', delay: 200 }
13791
+ );
13671
13792
  \`\`\`
13672
13793
 
13673
13794
  ---
@@ -13819,16 +13940,18 @@ For expand/collapse, animate \`opacity\` + \`maxHeight\` together. Do NOT includ
13819
13940
 
13820
13941
  ## Transform Syntax
13821
13942
 
13822
- Use simplified object syntax instead of React Native arrays:
13943
+ **ALWAYS use object syntax** for transforms in useAnimatedStyle/usePresence:
13823
13944
 
13824
13945
  \`\`\`typescript
13825
- // Recommended: object syntax
13946
+ // CORRECT: object syntax (required for animations)
13826
13947
  transform: { x: 10, y: 20, scale: 1.2, rotate: 45 }
13827
13948
 
13828
- // Legacy: array syntax (still supported)
13829
- transform: [{ translateX: 10 }, { translateY: 20 }, { scale: 1.2 }]
13949
+ // WRONG in useAnimatedStyle: array syntax does NOT animate correctly
13950
+ // transform: [{ translateX: 10 }, { translateY: 20 }] // \u274C won't animate
13830
13951
  \`\`\`
13831
13952
 
13953
+ > **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.
13954
+
13832
13955
  | Property | Type | Maps to |
13833
13956
  |----------|------|---------|
13834
13957
  | x | number | translateX |
@@ -18766,6 +18889,396 @@ function MyScreen() {
18766
18889
  `
18767
18890
  };
18768
18891
 
18892
+ // src/data/network-guides.ts
18893
+ var networkGuides = {
18894
+ "idealyst://network/overview": `# @idealyst/network Overview
18895
+
18896
+ 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.
18897
+
18898
+ ## Features
18899
+
18900
+ - **useNetwork Hook** \u2014 Real-time network state with isConnected, connection type, effective speed
18901
+ - **Network State Listener** \u2014 Subscribe to connectivity changes outside React
18902
+ - **Connection Details** \u2014 WiFi, cellular (2G/3G/4G/5G), ethernet, bluetooth, VPN detection
18903
+ - **Effective Connection Type** \u2014 slow-2g, 2g, 3g, 4g speed categories
18904
+ - **Internet Reachability** \u2014 Distinguish "connected to network" from "can reach internet"
18905
+ - **fetchWithTimeout** \u2014 Fetch wrapper with configurable timeout and auto-abort
18906
+ - **retry** \u2014 Exponential backoff retry with optional network-aware gating
18907
+ - **waitForNetwork** \u2014 Promise that resolves when the device comes back online
18908
+ - **Data Saver Detection** \u2014 Detect if user has enabled data-saving mode (web)
18909
+ - **Cross-Platform** \u2014 navigator.onLine + Network Information API (web), @react-native-community/netinfo (native)
18910
+ - **TypeScript** \u2014 Full type safety and IntelliSense
18911
+
18912
+ ## Installation
18913
+
18914
+ \`\`\`bash
18915
+ yarn add @idealyst/network
18916
+
18917
+ # React Native also needs:
18918
+ yarn add @react-native-community/netinfo
18919
+ cd ios && pod install
18920
+ \`\`\`
18921
+
18922
+ ## Quick Start
18923
+
18924
+ \`\`\`tsx
18925
+ import { useNetwork } from '@idealyst/network';
18926
+
18927
+ function App() {
18928
+ const { isConnected, type, effectiveType } = useNetwork();
18929
+
18930
+ if (!isConnected) {
18931
+ return <Text>You are offline</Text>;
18932
+ }
18933
+
18934
+ return (
18935
+ <Text>
18936
+ Connected via {type} ({effectiveType})
18937
+ </Text>
18938
+ );
18939
+ }
18940
+ \`\`\`
18941
+
18942
+ ## Platform Details
18943
+
18944
+ - **Web**: Uses \`navigator.onLine\`, \`online\`/\`offline\` events, and the Network Information API (\`navigator.connection\`) for connection type, downlink speed, RTT, and data saver detection
18945
+ - **React Native**: Uses \`@react-native-community/netinfo\` for connection type, cellular generation, internet reachability, and real-time state changes
18946
+ - **SSR**: Safe for server-side rendering \u2014 returns sensible defaults when \`navigator\` is unavailable
18947
+ `,
18948
+ "idealyst://network/api": `# Network API Reference
18949
+
18950
+ Complete API reference for @idealyst/network.
18951
+
18952
+ ## useNetwork
18953
+
18954
+ React hook for monitoring network connectivity. Returns reactive state that updates automatically.
18955
+
18956
+ \`\`\`tsx
18957
+ const {
18958
+ state, // NetworkState \u2014 full state object
18959
+ isConnected, // boolean \u2014 device has active network
18960
+ isInternetReachable,// boolean | null \u2014 can reach internet (null = unknown)
18961
+ type, // NetworkConnectionType \u2014 'wifi' | 'cellular' | 'ethernet' | ...
18962
+ effectiveType, // EffectiveConnectionType \u2014 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
18963
+ refresh, // () => Promise<NetworkState> \u2014 manually refresh state
18964
+ } = useNetwork(options?: UseNetworkOptions);
18965
+ \`\`\`
18966
+
18967
+ ### UseNetworkOptions
18968
+
18969
+ | Option | Type | Default | Description |
18970
+ |--------|------|---------|-------------|
18971
+ | fetchOnMount | boolean | true | Fetch initial state on mount |
18972
+ | reachabilityUrl | string | (google 204) | URL for reachability pings (web only) |
18973
+ | reachabilityPollInterval | number | 0 | Polling interval in ms for reachability checks (web only). 0 = disabled |
18974
+
18975
+ ---
18976
+
18977
+ ## getNetworkState
18978
+
18979
+ Get a one-time snapshot of the current network state.
18980
+
18981
+ \`\`\`tsx
18982
+ // Web (synchronous)
18983
+ import { getNetworkState } from '@idealyst/network';
18984
+ const state: NetworkState = getNetworkState();
18985
+
18986
+ // Native (async \u2014 requires NetInfo fetch)
18987
+ import { getNetworkState } from '@idealyst/network';
18988
+ const state: NetworkState = await getNetworkState();
18989
+ \`\`\`
18990
+
18991
+ **Note:** On web, \`getNetworkState()\` is synchronous. On native, it returns a Promise.
18992
+
18993
+ ---
18994
+
18995
+ ## addNetworkStateListener
18996
+
18997
+ Subscribe to network state changes outside of React.
18998
+
18999
+ \`\`\`tsx
19000
+ import { addNetworkStateListener } from '@idealyst/network';
19001
+
19002
+ const unsubscribe = addNetworkStateListener((state) => {
19003
+ console.log('Network changed:', state.isConnected, state.type);
19004
+ });
19005
+
19006
+ // Later:
19007
+ unsubscribe();
19008
+ \`\`\`
19009
+
19010
+ ---
19011
+
19012
+ ## fetchWithTimeout
19013
+
19014
+ Fetch wrapper that automatically aborts if the request exceeds a timeout.
19015
+
19016
+ \`\`\`tsx
19017
+ import { fetchWithTimeout } from '@idealyst/network';
19018
+
19019
+ const response = await fetchWithTimeout('https://api.example.com/data', {
19020
+ timeout: 5000, // 5 seconds (default: 10000)
19021
+ method: 'GET',
19022
+ headers: { 'Authorization': 'Bearer token' },
19023
+ });
19024
+ \`\`\`
19025
+
19026
+ ### FetchWithTimeoutOptions
19027
+
19028
+ Extends standard \`RequestInit\` with:
19029
+
19030
+ | Option | Type | Default | Description |
19031
+ |--------|------|---------|-------------|
19032
+ | timeout | number | 10000 | Timeout in milliseconds |
19033
+
19034
+ ---
19035
+
19036
+ ## retry
19037
+
19038
+ Retry a function with exponential backoff. Optionally waits for network before retrying.
19039
+
19040
+ \`\`\`tsx
19041
+ import { retry } from '@idealyst/network';
19042
+
19043
+ const data = await retry(
19044
+ () => fetch('https://api.example.com/data').then(r => r.json()),
19045
+ {
19046
+ maxRetries: 3,
19047
+ baseDelay: 1000,
19048
+ maxDelay: 30000,
19049
+ retryOnlyWhenConnected: true,
19050
+ },
19051
+ );
19052
+ \`\`\`
19053
+
19054
+ ### RetryOptions
19055
+
19056
+ | Option | Type | Default | Description |
19057
+ |--------|------|---------|-------------|
19058
+ | maxRetries | number | 3 | Maximum retry attempts |
19059
+ | baseDelay | number | 1000 | Base delay in ms (doubles each attempt) |
19060
+ | maxDelay | number | 30000 | Maximum delay cap in ms |
19061
+ | retryOnlyWhenConnected | boolean | true | Wait for network before retrying |
19062
+
19063
+ ---
19064
+
19065
+ ## waitForNetwork
19066
+
19067
+ Returns a promise that resolves when the device comes back online.
19068
+
19069
+ \`\`\`tsx
19070
+ import { waitForNetwork } from '@idealyst/network';
19071
+
19072
+ // Wait up to 30 seconds for connectivity
19073
+ await waitForNetwork({ timeout: 30000 });
19074
+ console.log('Back online!');
19075
+ \`\`\`
19076
+
19077
+ If already online, resolves immediately. Rejects with an error if the timeout is exceeded.
19078
+
19079
+ ### WaitForNetworkOptions
19080
+
19081
+ | Option | Type | Default | Description |
19082
+ |--------|------|---------|-------------|
19083
+ | timeout | number | 30000 | Max wait time in ms before rejecting |
19084
+
19085
+ ---
19086
+
19087
+ ## NetworkState
19088
+
19089
+ Full network state object returned by the hook and listeners.
19090
+
19091
+ \`\`\`tsx
19092
+ interface NetworkState {
19093
+ isConnected: boolean; // Device has active network
19094
+ isInternetReachable: boolean | null; // Internet reachable (null = unknown)
19095
+ type: NetworkConnectionType; // 'wifi' | 'cellular' | 'ethernet' | ...
19096
+ effectiveType: EffectiveConnectionType; // 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
19097
+ cellularGeneration: CellularGeneration; // '2g' | '3g' | '4g' | '5g' | null (native only)
19098
+ downlink: number | null; // Mbps (web only, from Network Info API)
19099
+ rtt: number | null; // Round-trip time ms (web only)
19100
+ isDataSaving: boolean | null; // Data saver enabled (web only)
19101
+ }
19102
+ \`\`\`
19103
+
19104
+ ## Type Aliases
19105
+
19106
+ \`\`\`tsx
19107
+ type NetworkConnectionType = 'wifi' | 'cellular' | 'ethernet' | 'bluetooth' | 'vpn' | 'other' | 'none' | 'unknown';
19108
+ type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
19109
+ type CellularGeneration = '2g' | '3g' | '4g' | '5g' | null;
19110
+ \`\`\`
19111
+ `,
19112
+ "idealyst://network/examples": `# Network Examples
19113
+
19114
+ Complete code examples for common @idealyst/network patterns.
19115
+
19116
+ ## Offline Banner
19117
+
19118
+ \`\`\`tsx
19119
+ import { useNetwork } from '@idealyst/network';
19120
+ import { View, Text } from '@idealyst/components';
19121
+
19122
+ function OfflineBanner() {
19123
+ const { isConnected } = useNetwork();
19124
+
19125
+ if (isConnected) return null;
19126
+
19127
+ return (
19128
+ <View style={{ backgroundColor: '#f44336', padding: 8 }}>
19129
+ <Text style={{ color: '#fff', textAlign: 'center' }}>
19130
+ No internet connection
19131
+ </Text>
19132
+ </View>
19133
+ );
19134
+ }
19135
+ \`\`\`
19136
+
19137
+ ## Adaptive Quality Based on Connection
19138
+
19139
+ \`\`\`tsx
19140
+ import { useNetwork } from '@idealyst/network';
19141
+
19142
+ function MediaPlayer({ videoId }: { videoId: string }) {
19143
+ const { effectiveType, isDataSaving } = useNetwork().state;
19144
+
19145
+ const quality = (() => {
19146
+ if (isDataSaving) return 'low';
19147
+ switch (effectiveType) {
19148
+ case 'slow-2g':
19149
+ case '2g':
19150
+ return 'low';
19151
+ case '3g':
19152
+ return 'medium';
19153
+ case '4g':
19154
+ default:
19155
+ return 'high';
19156
+ }
19157
+ })();
19158
+
19159
+ return <VideoPlayer videoId={videoId} quality={quality} />;
19160
+ }
19161
+ \`\`\`
19162
+
19163
+ ## Retry API Calls
19164
+
19165
+ \`\`\`tsx
19166
+ import { retry, fetchWithTimeout } from '@idealyst/network';
19167
+
19168
+ async function fetchUserProfile(userId: string) {
19169
+ const response = await retry(
19170
+ () => fetchWithTimeout(\`https://api.example.com/users/\${userId}\`, {
19171
+ timeout: 5000,
19172
+ }),
19173
+ { maxRetries: 3, baseDelay: 1000 },
19174
+ );
19175
+
19176
+ if (!response.ok) {
19177
+ throw new Error(\`Failed to fetch user: \${response.status}\`);
19178
+ }
19179
+
19180
+ return response.json();
19181
+ }
19182
+ \`\`\`
19183
+
19184
+ ## Wait for Network Before Action
19185
+
19186
+ \`\`\`tsx
19187
+ import { waitForNetwork } from '@idealyst/network';
19188
+
19189
+ async function syncData() {
19190
+ // If offline, wait up to 60 seconds for connectivity
19191
+ await waitForNetwork({ timeout: 60000 });
19192
+
19193
+ // Now we're online \u2014 sync
19194
+ const response = await fetch('https://api.example.com/sync', {
19195
+ method: 'POST',
19196
+ body: JSON.stringify(pendingChanges),
19197
+ });
19198
+
19199
+ return response.json();
19200
+ }
19201
+ \`\`\`
19202
+
19203
+ ## Network State Listener (Outside React)
19204
+
19205
+ \`\`\`tsx
19206
+ import { addNetworkStateListener } from '@idealyst/network';
19207
+
19208
+ // Subscribe to changes (e.g., in a service or module)
19209
+ const unsubscribe = addNetworkStateListener((state) => {
19210
+ if (!state.isConnected) {
19211
+ queueManager.pause();
19212
+ } else {
19213
+ queueManager.resume();
19214
+ }
19215
+ });
19216
+
19217
+ // Cleanup when done
19218
+ unsubscribe();
19219
+ \`\`\`
19220
+
19221
+ ## Connection Type Display
19222
+
19223
+ \`\`\`tsx
19224
+ import { useNetwork } from '@idealyst/network';
19225
+ import { View, Text } from '@idealyst/components';
19226
+
19227
+ function NetworkInfo() {
19228
+ const { state } = useNetwork();
19229
+
19230
+ return (
19231
+ <View>
19232
+ <Text>Status: {state.isConnected ? 'Online' : 'Offline'}</Text>
19233
+ <Text>Type: {state.type}</Text>
19234
+ <Text>Speed: {state.effectiveType}</Text>
19235
+ {state.cellularGeneration && (
19236
+ <Text>Cellular: {state.cellularGeneration}</Text>
19237
+ )}
19238
+ {state.downlink != null && (
19239
+ <Text>Downlink: {state.downlink} Mbps</Text>
19240
+ )}
19241
+ {state.rtt != null && (
19242
+ <Text>RTT: {state.rtt} ms</Text>
19243
+ )}
19244
+ {state.isDataSaving != null && (
19245
+ <Text>Data Saver: {state.isDataSaving ? 'On' : 'Off'}</Text>
19246
+ )}
19247
+ </View>
19248
+ );
19249
+ }
19250
+ \`\`\`
19251
+
19252
+ ## Fetch with Timeout
19253
+
19254
+ \`\`\`tsx
19255
+ import { fetchWithTimeout } from '@idealyst/network';
19256
+
19257
+ async function quickHealthCheck() {
19258
+ try {
19259
+ const response = await fetchWithTimeout('https://api.example.com/health', {
19260
+ timeout: 3000,
19261
+ method: 'HEAD',
19262
+ });
19263
+ return response.ok;
19264
+ } catch {
19265
+ return false;
19266
+ }
19267
+ }
19268
+ \`\`\`
19269
+
19270
+ ## Best Practices
19271
+
19272
+ 1. **Use \`useNetwork\` for UI** \u2014 The hook handles subscriptions and cleanup automatically
19273
+ 2. **Use \`addNetworkStateListener\` for services** \u2014 For non-React code (queue managers, sync services)
19274
+ 3. **Combine retry + fetchWithTimeout** \u2014 For resilient API calls
19275
+ 4. **Check \`isInternetReachable\`** \u2014 Being "connected" to WiFi doesn't mean you have internet
19276
+ 5. **Respect data saver** \u2014 Reduce payload sizes and avoid autoplay when \`isDataSaving\` is true
19277
+ 6. **Don't poll too aggressively** \u2014 If using \`reachabilityPollInterval\`, keep it >= 15000ms
19278
+ 7. **Use \`waitForNetwork\` for offline-first** \u2014 Queue operations and wait for connectivity to sync
19279
+ `
19280
+ };
19281
+
18769
19282
  // src/data/install-guides.ts
18770
19283
  var installGuides = {
18771
19284
  // ============================================================================
@@ -20246,16 +20759,23 @@ import { View, Text, Button, TextInput, Card, Icon, ... } from '@idealyst/compon
20246
20759
 
20247
20760
  ## How to Use This MCP Server
20248
20761
 
20249
- This server has tools for every aspect of the framework. **Always look up the API before writing code.**
20762
+ This server has tools for every aspect of the framework. **Look up APIs as you need them \u2014 don't research everything upfront.**
20763
+
20764
+ ### Workflow
20765
+
20766
+ Write code iteratively \u2014 **look up one API, write one file, repeat**:
20767
+
20768
+ 1. Call \`get_intro\` (this response)
20769
+ 2. Look up the API for your FIRST file only (e.g., \`get_component_types("Button,Card,Text")\`)
20770
+ 3. **Write that file immediately** using the Write tool
20771
+ 4. Then look up the next API you need, write the next file, and so on
20250
20772
 
20251
- ### Workflow (be efficient \u2014 minimize tool calls)
20773
+ - **Before using a component** \u2014 call \`get_component_types\` for its props (supports batching: \`get_component_types("Button,Card,Text")\`)
20774
+ - **Before using a package** \u2014 call its dedicated \`get_*_guide\` tool with topic \`api\`
20775
+ - **Search icons once** \u2014 batch all terms into one call: \`search_icons("home settings check timer")\`
20776
+ - **Check recipes** \u2014 \`search_recipes\` for ready-made patterns you can adapt
20252
20777
 
20253
- 1. **Start here** \u2014 this intro covers conventions and gotchas
20254
- 2. **Look up components** \u2014 call \`get_component_types\` for EVERY component you plan to use. Call it for each component you need. (Card has NO compound components \u2014 no Card.Content/Card.Header)
20255
- 3. **Look up packages** \u2014 call the dedicated \`get_*_guide\` tool with topic \`api\` for each \`@idealyst/*\` package
20256
- 4. **Check recipes** \u2014 call \`search_recipes\` to find ready-made patterns for common screens
20257
- 5. **Search icons ONCE** \u2014 combine all needed icon terms into ONE \`search_icons\` call (e.g., \`"home settings check timer calendar"\`)
20258
- 6. **Then write code** \u2014 only after reading the types and guides. Do NOT make additional icon searches \u2014 pick from what you found
20778
+ > **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**.
20259
20779
 
20260
20780
  ### Component Tools
20261
20781
  - \`list_components\` / \`search_components\` \u2014 Discover available components
@@ -20265,7 +20785,7 @@ This server has tools for every aspect of the framework. **Always look up the AP
20265
20785
  - \`search_icons\` \u2014 Find Material Design icon names (7,447 available). **Batch your needs**: search once with multiple terms like \`"home settings user check"\` rather than making separate calls per icon
20266
20786
 
20267
20787
  ### Package Guide Tools
20268
- **For any \`@idealyst/*\` package, call its dedicated guide tool with topic \`api\` BEFORE writing code.** These return complete TypeScript interfaces, return types, and correct usage. The generic \`get_package_docs\` tool only has summaries.
20788
+ Each \`@idealyst/*\` package has a dedicated guide tool that returns complete TypeScript interfaces and correct usage. The generic \`get_package_docs\` tool only has summaries.
20269
20789
 
20270
20790
  | Package | Guide Tool |
20271
20791
  |---------|-----------|
@@ -20330,6 +20850,10 @@ const tabs: { icon: IconName }[] = [{ icon: 'home' }, { icon: 'search' }]; // NO
20330
20850
 
20331
20851
  > **CRITICAL:** Icon names do NOT have an \`mdi:\` prefix. Use bare names like \`'delete'\`, \`'home'\`, \`'check-circle'\` \u2014 NOT \`'mdi:delete'\`, \`'mdi:home'\`, etc.
20332
20852
  > **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.
20853
+ > **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\`.
20854
+ > **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.
20855
+
20856
+ **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.
20333
20857
 
20334
20858
  ---
20335
20859
 
@@ -20339,26 +20863,27 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
20339
20863
 
20340
20864
  ### Component Props
20341
20865
  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"\`.
20342
- 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\`), and \`textContentType\`. TextArea is different \u2014 it DOES support \`label\`, \`error\`, \`rows\`, \`onChange\`.
20866
+ 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.
20343
20867
  3. **Button/IconButton** \`type\` is \`'contained' | 'outlined' | 'text'\` \u2014 NOT \`'ghost'\`, \`'solid'\`, \`'default'\`. Button has \`leftIcon\` and \`rightIcon\` \u2014 NOT \`icon\`.
20344
20868
  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'\`.
20345
- 5. **Badge** \`type\` is \`'filled' | 'outlined' | 'dot'\` \u2014 NOT \`'soft'\`, \`'subtle'\`, \`'solid'\`.
20869
+ 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.
20346
20870
  6. **Avatar** uses \`src\` for image URL, \`fallback\` for initials \u2014 NOT \`name\`, \`initials\`, \`label\`. Shape: \`'circle' | 'square'\`.
20347
20871
  7. **Link** requires a \`to\` prop (path string) \u2014 it's a navigation link, NOT pressable text.
20348
20872
  8. **List** takes \`children\` \u2014 NOT \`data\`, \`renderItem\`, or \`keyExtractor\`. Map your data: \`<List>{items.map(item => <View key={item.id}>...</View>)}</List>\`
20349
20873
  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\`.
20350
20874
  10. The component is **TextInput**, NOT \`Input\`.
20351
- 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>\`.
20875
+ 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"\`.
20876
+ 12. **Switch** uses \`checked\` and \`onChange\` \u2014 NOT \`value\` and \`onValueChange\` (React Native convention). Also has \`label\`, \`labelPosition\`, \`disabled\`.
20352
20877
 
20353
20878
  ### Navigation
20354
- 11. Use \`NavigatorProvider\` \u2014 there is NO \`Router\` export.
20879
+ 11. **NavigatorProvider** takes a \`route\` prop (SINGULAR) \u2014 NOT \`routes\`: \`<NavigatorProvider route={routeConfig} />\`. There is NO \`Router\` export.
20355
20880
  12. Use \`useNavigator()\` \u2014 NOT \`useNavigate()\`.
20356
- 13. \`navigate()\` takes an **object**: \`navigate({ path: '/settings' })\` \u2014 NOT \`navigate('/settings')\` or \`navigate('routeName', params)\`.
20881
+ 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).
20357
20882
  14. \`useNavigationState\` returns \`Record<string, unknown>\` by default. Always provide a type parameter: \`useNavigationState<{ title?: string }>()\`.
20358
20883
  15. \`useParams()\` does NOT accept generic type arguments. It returns \`Record<string, string>\`. Do NOT write \`useParams<{ id: string }>()\` \u2014 that causes TS2558.
20359
20884
 
20360
20885
  ### Imports & Styling
20361
- 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\`).
20886
+ 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.
20362
20887
  17. **Never** import from \`react-native-unistyles\` \u2014 use \`@idealyst/theme\` (\`configureThemes\`, \`ThemeSettings\`, \`useTheme\`).
20363
20888
  18. **useTheme()** returns the Theme object **directly** (NOT wrapped): \`const theme = useTheme();\`. Do NOT destructure: \`const { theme } = useTheme()\` \u2014 causes TS2339.
20364
20889
  19. **Spacing & Layout**: Use component shorthand props for spacing \u2014 NOT \`theme.spacing\` (which does NOT exist). The correct patterns:
@@ -20369,14 +20894,32 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
20369
20894
  - \`theme.radii.md\` \u2014 border radius values (this DOES exist)
20370
20895
  - **WRONG**: \`<View paddingTop="md">\` \u2014 does NOT exist as a shorthand prop; use \`style={{ paddingTop: 16 }}\`
20371
20896
  - **WRONG**: \`theme.spacing.md\` \u2014 does NOT exist, causes TS2339
20897
+ - **WRONG**: \`theme.colors.background\` \u2014 does NOT exist. \`theme.colors\` has: \`pallet\`, \`surface\`, \`text\`, \`border\`. Use \`theme.colors.surface.primary\` for background colors.
20372
20898
  - **WRONG**: \`theme.colors.intent.danger\` \u2014 does NOT exist; intents are at \`theme.intents.danger\`
20899
+ - **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\`
20900
+ - **CORRECT**: \`theme.intents.primary.primary\` for the color, \`theme.intents.primary.contrast\` for text on that color
20901
+ - **Color key reference** (all keys available):
20902
+ - \`theme.colors.surface\`: \`screen\`, \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
20903
+ - \`theme.colors.text\`: \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
20904
+ - \`theme.colors.border\`: \`primary\`, \`secondary\`, \`tertiary\`, \`disabled\`
20905
+ - \`theme.intents\`: \`primary\`, \`secondary\`, \`success\`, \`warning\`, \`danger\`, \`info\`, \`neutral\` (each has \`.primary\`, \`.contrast\`, \`.light\`, \`.dark\`)
20373
20906
 
20374
20907
  ### Cross-Platform (CRITICAL)
20375
20908
  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.
20376
- 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).
20909
+ 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.
20910
+ 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.
20911
+ 22. **Platform file barrel exports**: When creating \`Component.web.tsx\` + \`Component.native.tsx\`, you need THREE index files:
20912
+ - \`index.web.ts\` \u2014 \`export { default as Component } from './Component.web';\`
20913
+ - \`index.native.ts\` \u2014 \`export { default as Component } from './Component.native';\`
20914
+ - \`index.ts\` \u2014 \`export { default as Component } from './Component.web';\` (base file for TypeScript resolution)
20915
+ - **WRONG**: \`export { default } from './Component';\` \u2014 no bare \`Component.ts\` exists, only \`.web.tsx\`/\`.native.tsx\`. This causes TS2307.
20916
+ - **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.
20377
20917
 
20378
20918
  ### React 19 TypeScript
20379
20919
  - **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[]>([])\`.
20920
+ - **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.
20921
+ - **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.
20922
+ - **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).
20380
20923
 
20381
20924
  ### Scaffolded Project Layout
20382
20925
  When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`idealyst create\`), files go in specific locations:
@@ -20393,11 +20936,11 @@ When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`i
20393
20936
  > **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.
20394
20937
 
20395
20938
  ### Package-Specific (call guide tools for full API)
20396
- 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\`.
20939
+ 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\`.
20397
20940
  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\`.
20398
- 20. **Files** (\`@idealyst/files\`): Method is \`pick()\`, NOT \`pickFiles()\`. \`FileType\` is \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`. Call \`get_files_guide\` topic \`api\`.
20941
+ 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\`.
20399
20942
  21. **Storage** (\`@idealyst/storage\`): Methods are \`getItem()\`/\`setItem()\`, NOT \`get()\`/\`set()\`. Values are string-only. Call \`get_storage_guide\` topic \`api\`.
20400
- 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\`.
20943
+ 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\`.
20401
20944
  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\`.
20402
20945
  `;
20403
20946
 
@@ -27863,7 +28406,7 @@ var import_url = require("url");
27863
28406
  // src/generated/types.json
27864
28407
  var types_default = {
27865
28408
  version: "1.0.93",
27866
- extractedAt: "2026-02-24T16:32:56.796Z",
28409
+ extractedAt: "2026-02-24T21:11:17.058Z",
27867
28410
  components: {
27868
28411
  Accordion: {
27869
28412
  name: "Accordion",
@@ -50164,7 +50707,7 @@ var types_default = {
50164
50707
  navigation: {
50165
50708
  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',
50166
50709
  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}",
50167
- 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;",
50710
+ 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;",
50168
50711
  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}",
50169
50712
  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}",
50170
50713
  TabNavigatorParam: "export type TabNavigatorParam = {\n layout: 'tab'\n routes: RouteParam<TabBarScreenOptions>[]\n layoutComponent?: TabLayoutComponent\n} & BaseNavigatorParam",
@@ -69799,12 +70342,32 @@ function postProcessComponentTypes(componentName, result) {
69799
70342
  }
69800
70343
  if (componentName.toLowerCase() === "card") {
69801
70344
  if (typeof result === "object" && result !== null) {
69802
- 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>';
70345
+ 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 }}.";
69803
70346
  }
69804
70347
  }
69805
70348
  if (componentName.toLowerCase() === "view") {
69806
70349
  if (typeof result === "object" && result !== null) {
69807
- 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 }}.";
70350
+ 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.";
70351
+ }
70352
+ }
70353
+ if (componentName.toLowerCase() === "textarea") {
70354
+ if (typeof result === "object" && result !== null) {
70355
+ 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). Always call get_component_types('TextArea') separately \u2014 do NOT assume it shares TextInput's props.";
70356
+ }
70357
+ }
70358
+ if (componentName.toLowerCase() === "image") {
70359
+ if (typeof result === "object" && result !== null) {
70360
+ 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} />";
70361
+ }
70362
+ }
70363
+ if (componentName.toLowerCase() === "icon") {
70364
+ if (typeof result === "object" && result !== null) {
70365
+ 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`.";
70366
+ }
70367
+ }
70368
+ if (componentName.toLowerCase() === "badge") {
70369
+ if (typeof result === "object" && result !== null) {
70370
+ 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.";
69808
70371
  }
69809
70372
  }
69810
70373
  return result;
@@ -70062,8 +70625,8 @@ function getThemeTypes2(args = {}) {
70062
70625
  const result = getThemeTypes(format);
70063
70626
  if (typeof result === "object" && result !== null) {
70064
70627
  const r = result;
70065
- r.useThemeNote = 'IMPORTANT: useTheme() returns Theme directly \u2014 NOT wrapped in an object. Correct: `const theme = useTheme();` WRONG: `const { theme } = useTheme();` (causes TS2339). Theme top-level keys: intents, radii, shadows, colors, sizes, interaction, breakpoints. For spacing, use component props (padding="md", gap="md") \u2014 NOT theme.spacing (does NOT exist). For colors: `style={{ backgroundColor: theme.colors.surface.primary }}`. For radii: `style={{ borderRadius: theme.radii.md }}`. For intents: `theme.intents.primary` (NOT theme.colors.intent).';
70066
- r.themeAccessPatterns = "## Theme Access Patterns\n\n```typescript\nimport { useTheme } from '@idealyst/theme';\nconst theme = useTheme();\n\n// Colors (surface, text, border sub-groups)\ntheme.colors.surface.primary // main background\ntheme.colors.surface.secondary // card/section background\ntheme.colors.text.primary // main text color\ntheme.colors.text.secondary // muted text\ntheme.colors.text.tertiary // subtle text\ntheme.colors.text.inverse // text on dark backgrounds\ntheme.colors.border.primary // standard border\ntheme.colors.border.secondary // subtle border\n\n// Intents (semantic colors with bg, text, border)\ntheme.intents.primary.bg // primary background\ntheme.intents.primary.text // primary text color\ntheme.intents.danger.bg // danger/error background\n// Available: primary, secondary, success, warning, danger, info, neutral\n\n// Radii\ntheme.radii.none // 0\ntheme.radii.sm // small radius\ntheme.radii.md // medium radius\ntheme.radii.lg // large radius\ntheme.radii.xl // extra large\ntheme.radii.full // fully rounded\n\n// Shadows\ntheme.shadows.sm // subtle shadow\ntheme.shadows.md // medium shadow\ntheme.shadows.lg // prominent shadow\n\n// Breakpoints (responsive)\ntheme.breakpoints.xs // 0\ntheme.breakpoints.sm // small screens\ntheme.breakpoints.md // medium screens\ntheme.breakpoints.lg // large screens\n```";
70628
+ r.useThemeNote = "IMPORTANT: useTheme() returns Theme directly \u2014 NOT wrapped in an object. Correct: `const theme = useTheme();` WRONG: `const { theme } = useTheme();` (causes TS2339). Theme top-level keys: intents, radii, shadows, colors, sizes, interaction, breakpoints. For spacing, use component props (padding=\"md\", gap=\"md\") \u2014 NOT theme.spacing (does NOT exist). For colors: `style={{ backgroundColor: theme.colors.surface.primary }}`. For radii: `style={{ borderRadius: theme.radii.md }}`. For intents: `theme.intents.primary.primary` (main color), `.contrast`, `.light`, `.dark`. WRONG: `theme.intents.primary.bg` \u2014 'bg' does NOT exist. WRONG: `theme.intents.primary.text` \u2014 'text' does NOT exist. WRONG: `theme.colors.intent.danger` \u2014 intents are at theme.intents, NOT theme.colors.intent.";
70629
+ r.themeAccessPatterns = "## Theme Access Patterns\n\n```typescript\nimport { useTheme } from '@idealyst/theme';\nconst theme = useTheme();\n\n// Colors (surface, text, border sub-groups)\ntheme.colors.surface.primary // main background\ntheme.colors.surface.secondary // card/section background\ntheme.colors.text.primary // main text color\ntheme.colors.text.secondary // muted text\ntheme.colors.text.tertiary // subtle text\ntheme.colors.text.inverse // text on dark backgrounds\ntheme.colors.border.primary // standard border\ntheme.colors.border.secondary // subtle border\n\n// Intents \u2014 IntentValue has: primary, contrast, light, dark\ntheme.intents.primary.primary // primary intent main color (string)\ntheme.intents.primary.contrast // contrast color for text on primary bg\ntheme.intents.primary.light // lighter variant\ntheme.intents.primary.dark // darker variant\ntheme.intents.danger.primary // danger intent main color\n// Available intents: primary, secondary, success, warning, danger, info, neutral\n// WRONG: theme.intents.primary.bg \u2014 'bg' does NOT exist on IntentValue\n// WRONG: theme.intents.primary.text \u2014 'text' does NOT exist on IntentValue\n\n// Radii\ntheme.radii.none // 0\ntheme.radii.sm // small radius\ntheme.radii.md // medium radius\ntheme.radii.lg // large radius\ntheme.radii.xl // extra large\ntheme.radii.full // fully rounded\n\n// Shadows\ntheme.shadows.sm // subtle shadow\ntheme.shadows.md // medium shadow\ntheme.shadows.lg // prominent shadow\n\n// Breakpoints (responsive)\ntheme.breakpoints.xs // 0\ntheme.breakpoints.sm // small screens\ntheme.breakpoints.md // medium screens\ntheme.breakpoints.lg // large screens\n```";
70067
70630
  r.themeSetup = "## Theme Setup (app initialization)\n\n```typescript\nimport { configureThemes, lightTheme, darkTheme, fromTheme } from '@idealyst/theme';\n\n// Build themes from defaults\nconst light = fromTheme(lightTheme).build();\nconst dark = fromTheme(darkTheme).build();\n\n// Configure at app startup (call once, before any component renders)\nconfigureThemes({ themes: { light, dark } });\n```\n\n## Theme Switching at Runtime\n\n```typescript\nimport { ThemeSettings, getColorScheme } from '@idealyst/theme';\n\n// Switch theme\nThemeSettings.setTheme('dark', 'dark'); // (themeName, contentColor)\nThemeSettings.setTheme('light', 'light', true); // animated transition\n\n// Get current theme name\nconst current = ThemeSettings.getThemeName(); // 'light' or 'dark'\n\n// Follow system light/dark preference\nThemeSettings.setAdaptiveThemes(true);\n\n// Get device color scheme\nconst scheme = getColorScheme(); // 'light' | 'dark' | null\n```\n\nIMPORTANT: Do NOT import from 'react-native-unistyles' directly. Use configureThemes (NOT StyleSheet.configure or UnistylesRegistry). Use ThemeSettings (NOT UnistylesRuntime).";
70068
70631
  }
70069
70632
  return jsonResponse(result);
@@ -70079,6 +70642,8 @@ function getNavigationTypes2(args = {}) {
70079
70642
  const result = getNavigationTypes(format);
70080
70643
  if (typeof result === "object" && result !== null) {
70081
70644
  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.`;
70645
+ 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.";
70646
+ 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```";
70082
70647
  }
70083
70648
  return jsonResponse(result);
70084
70649
  } catch (error) {
@@ -70288,6 +70853,17 @@ function getLiveActivityGuide(args) {
70288
70853
  }
70289
70854
  return textResponse(guide);
70290
70855
  }
70856
+ function getNetworkGuide(args) {
70857
+ const topic = args.topic;
70858
+ const uri = `idealyst://network/${topic}`;
70859
+ const guide = networkGuides[uri];
70860
+ if (!guide) {
70861
+ return textResponse(
70862
+ `Topic "${topic}" not found. Available topics: overview, api, examples`
70863
+ );
70864
+ }
70865
+ return textResponse(guide);
70866
+ }
70291
70867
  function listPackages(args = {}) {
70292
70868
  const category = args.category;
70293
70869
  if (category) {
@@ -70538,6 +71114,7 @@ var toolHandlers = {
70538
71114
  get_payments_guide: getPaymentsGuide,
70539
71115
  get_notifications_guide: getNotificationsGuide,
70540
71116
  get_live_activity_guide: getLiveActivityGuide,
71117
+ get_network_guide: getNetworkGuide,
70541
71118
  list_packages: listPackages,
70542
71119
  get_package_docs: getPackageDocs,
70543
71120
  search_packages: searchPackages2,