@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.
package/dist/index.cjs CHANGED
@@ -2770,6 +2770,7 @@ const appRouter: NavigatorParam = {
2770
2770
  > - Layout props do NOT include \`children\` \u2014 content renders via \`<Outlet />\`
2771
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
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}\`
2773
2774
 
2774
2775
  ## GeneralLayout Component
2775
2776
 
@@ -3140,17 +3141,23 @@ navigator.navigate({ path: '/new-location', replace: true });
3140
3141
 
3141
3142
  ## useParams Hook
3142
3143
 
3143
- Access current route path parameters. Returns \`Record<string, string>\`.
3144
+ Access current route path parameters. Returns \`Record<string, string | undefined>\`.
3144
3145
 
3145
3146
  > **WARNING:** \`useParams()\` does NOT accept generic type arguments. Do NOT write \`useParams<{ id: string }>()\` \u2014 this causes TS2558. Access params by key from the returned record instead.
3146
3147
 
3148
+ > **IMPORTANT:** Param values are \`string | undefined\`, NOT \`string\`. When passing a param to a function that expects \`string\`, you MUST handle the undefined case \u2014 either with a fallback (\`params.id ?? ''\`), a guard (\`if (!params.id) return null\`), or a non-null assertion (\`params.id!\`) if you are certain it exists. Without this, TypeScript will report TS2345.
3149
+
3147
3150
  \`\`\`tsx
3148
3151
  import { useParams } from '@idealyst/navigation';
3149
3152
 
3150
3153
  function UserScreen() {
3151
3154
  // CORRECT \u2014 no type argument
3152
3155
  const params = useParams();
3153
- const userId = params.id; // Path param from /user/:id
3156
+ const userId = params.id; // string | undefined from /user/:id
3157
+
3158
+ // Handle undefined before using in typed contexts:
3159
+ if (!userId) return <Text>User not found</Text>;
3160
+ // Now userId is narrowed to string
3154
3161
 
3155
3162
  // WRONG \u2014 useParams does NOT accept generics
3156
3163
  // const params = useParams<{ id: string }>(); // TS2558 error!
@@ -3161,26 +3168,37 @@ function UserScreen() {
3161
3168
 
3162
3169
  ## useNavigationState Hook
3163
3170
 
3164
- Access navigation state passed via the \`state\` property:
3171
+ Access navigation state passed via the \`state\` property.
3172
+
3173
+ > **IMPORTANT \u2014 Type constraints:**
3174
+ > - \`navigate({ state: {...} })\` requires all values to be \`string | number | boolean\` \u2014 NO \`undefined\` or \`null\`. Do NOT spread objects that may contain undefined values.
3175
+ > - \`useNavigationState<T>()\` requires \`T extends Record<string, unknown>\`. Use \`Record<string, string | number | boolean>\` for the type parameter, or a specific interface with NON-optional fields.
3176
+ > - **Do NOT use optional fields (\`?\`) in the type parameter** \u2014 values passed via state are always present (they were explicitly set by navigate()). Use required fields instead.
3165
3177
 
3166
3178
  \`\`\`tsx
3167
3179
  import { useNavigationState } from '@idealyst/navigation';
3168
3180
 
3169
- // When navigating:
3181
+ // When navigating \u2014 all values must be string | number | boolean:
3170
3182
  navigator.navigate({
3171
3183
  path: '/recording',
3172
3184
  state: { autostart: true, source: 'home' }
3173
3185
  });
3174
3186
 
3175
- // In destination screen:
3187
+ // In destination screen \u2014 use REQUIRED fields (not optional):
3176
3188
  function RecordingScreen() {
3177
- const { autostart, source } = useNavigationState<{
3178
- autostart?: boolean;
3179
- source?: string;
3189
+ const state = useNavigationState<{
3190
+ autostart: boolean;
3191
+ source: string;
3180
3192
  }>();
3181
3193
 
3182
- // autostart = true, source = 'home'
3194
+ // state.autostart = true, state.source = 'home'
3183
3195
  }
3196
+
3197
+ // For multi-step wizards, pass ALL accumulated data as required fields:
3198
+ navigator.navigate({
3199
+ path: '/step3',
3200
+ state: { firstName: name, lastName: last, theme: selectedTheme }
3201
+ });
3184
3202
  \`\`\`
3185
3203
 
3186
3204
  ### Consuming State (Web)
@@ -7458,6 +7476,52 @@ await end(info.id, { dismissalPolicy: 'default' });`,
7458
7476
  "deliveryActivity() / timerActivity() / mediaActivity() / progressActivity() - Template presets"
7459
7477
  ],
7460
7478
  relatedPackages: ["notifications"]
7479
+ },
7480
+ network: {
7481
+ name: "Network",
7482
+ npmName: "@idealyst/network",
7483
+ 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.",
7484
+ category: "utility",
7485
+ platforms: ["web", "native"],
7486
+ documentationStatus: "full",
7487
+ installation: "yarn add @idealyst/network",
7488
+ peerDependencies: [
7489
+ "@react-native-community/netinfo (native)"
7490
+ ],
7491
+ features: [
7492
+ "useNetwork hook \u2014 real-time isConnected, connection type, effective speed",
7493
+ "Network state listener for non-React code",
7494
+ "Connection type detection \u2014 WiFi, cellular, ethernet, bluetooth, VPN",
7495
+ "Effective connection speed \u2014 slow-2g, 2g, 3g, 4g",
7496
+ "Cellular generation \u2014 2G, 3G, 4G, 5G (native)",
7497
+ "Internet reachability \u2014 distinguish connected from reachable",
7498
+ "fetchWithTimeout \u2014 fetch with configurable auto-abort",
7499
+ "retry \u2014 exponential backoff with network-aware gating",
7500
+ "waitForNetwork \u2014 promise that resolves when back online",
7501
+ "Data saver detection (web)",
7502
+ "Downlink speed and RTT estimation (web)"
7503
+ ],
7504
+ quickStart: `import { useNetwork } from '@idealyst/network';
7505
+
7506
+ function App() {
7507
+ const { isConnected, type, effectiveType } = useNetwork();
7508
+
7509
+ if (!isConnected) {
7510
+ return <Text>You are offline</Text>;
7511
+ }
7512
+
7513
+ return <Text>Connected via {type} ({effectiveType})</Text>;
7514
+ }`,
7515
+ apiHighlights: [
7516
+ "useNetwork(options?) - React hook for real-time network state",
7517
+ "getNetworkState() - One-time state snapshot (sync on web, async on native)",
7518
+ "addNetworkStateListener(cb) - Subscribe to changes (returns unsubscribe)",
7519
+ "fetchWithTimeout(url, options) - Fetch with auto-abort timeout",
7520
+ "retry(fn, options) - Exponential backoff retry",
7521
+ "waitForNetwork(options) - Promise that resolves when online",
7522
+ "NetworkState { isConnected, isInternetReachable, type, effectiveType, cellularGeneration, downlink, rtt, isDataSaving }"
7523
+ ],
7524
+ relatedPackages: ["storage", "config"]
7461
7525
  }
7462
7526
  };
7463
7527
  function getPackagesByCategory() {
@@ -10949,6 +11013,21 @@ var getLiveActivityGuideDefinition = {
10949
11013
  required: ["topic"]
10950
11014
  }
10951
11015
  };
11016
+ var getNetworkGuideDefinition = {
11017
+ name: "get_network_guide",
11018
+ description: "Get documentation for @idealyst/network cross-platform network connectivity and utilities package. Covers useNetwork hook, fetchWithTimeout, retry, waitForNetwork, and examples.",
11019
+ inputSchema: {
11020
+ type: "object",
11021
+ properties: {
11022
+ topic: {
11023
+ type: "string",
11024
+ description: "Topic to get docs for: 'overview', 'api', 'examples'",
11025
+ enum: ["overview", "api", "examples"]
11026
+ }
11027
+ },
11028
+ required: ["topic"]
11029
+ }
11030
+ };
10952
11031
  var toolDefinitions = [
10953
11032
  // Component tools
10954
11033
  listComponentsDefinition,
@@ -10984,6 +11063,7 @@ var toolDefinitions = [
10984
11063
  getPaymentsGuideDefinition,
10985
11064
  getNotificationsGuideDefinition,
10986
11065
  getLiveActivityGuideDefinition,
11066
+ getNetworkGuideDefinition,
10987
11067
  // Package tools
10988
11068
  listPackagesDefinition,
10989
11069
  getPackageDocsDefinition,
@@ -11176,13 +11256,17 @@ var componentMetadata = {
11176
11256
  "Standard, alert, and confirmation types",
11177
11257
  "Title and close button",
11178
11258
  "Backdrop click to close",
11179
- "Animation types (slide, fade)"
11259
+ "Animation types (slide, fade)",
11260
+ "avoidKeyboard prop shifts dialog when keyboard opens (native)",
11261
+ "height prop gives dialog a definite height so children can use flex: 1",
11262
+ "Content area has flex: 1 \u2014 children can flex into available space"
11180
11263
  ],
11181
11264
  bestPractices: [
11182
11265
  "Use sparingly for important interactions",
11183
11266
  "Provide clear actions for dismissal",
11184
11267
  "Keep dialog content focused",
11185
- "Use confirmation dialogs for destructive actions"
11268
+ "Use confirmation dialogs for destructive actions",
11269
+ "Use height or maxContentHeight when children need flex-based sizing"
11186
11270
  ]
11187
11271
  },
11188
11272
  Divider: {
@@ -11236,9 +11320,10 @@ var componentMetadata = {
11236
11320
  },
11237
11321
  Image: {
11238
11322
  category: "display",
11239
- description: "Cross-platform image component with loading and error states",
11323
+ description: "Cross-platform image component for displaying photos, pictures, and remote/local images with loading and error states",
11240
11324
  features: [
11241
- "Multiple resize modes",
11325
+ "Display photos, pictures, and media from URLs or local sources",
11326
+ "Multiple resize modes (cover, contain, stretch, center)",
11242
11327
  "Loading placeholder",
11243
11328
  "Error fallback",
11244
11329
  "Lazy loading",
@@ -11526,12 +11611,14 @@ var componentMetadata = {
11526
11611
  "Multiple sizes",
11527
11612
  "Character count",
11528
11613
  "Auto-resize",
11529
- "Error state"
11614
+ "Error state",
11615
+ "fill prop for flex-based sizing (fills available space)"
11530
11616
  ],
11531
11617
  bestPractices: [
11532
11618
  "Use for multi-line input",
11533
11619
  "Show character limits when applicable",
11534
- "Provide appropriate placeholder text"
11620
+ "Provide appropriate placeholder text",
11621
+ "Use fill prop inside Dialog with avoidKeyboard to shrink with keyboard"
11535
11622
  ]
11536
11623
  },
11537
11624
  TextInput: {
@@ -11608,7 +11695,11 @@ var componentAliases = {
11608
11695
  fab: "IconButton",
11609
11696
  modal: "Dialog",
11610
11697
  tooltip: "Tooltip",
11611
- snackbar: "Toast"
11698
+ snackbar: "Toast",
11699
+ photo: "Image",
11700
+ picture: "Image",
11701
+ img: "Image",
11702
+ thumbnail: "Image"
11612
11703
  };
11613
11704
  function findComponentName(componentName) {
11614
11705
  if (componentMetadata[componentName]) {
@@ -11777,7 +11868,8 @@ yarn add @idealyst/audio
11777
11868
  2. **Audio Session** \u2014 On iOS/Android, configure the audio session category before recording/playback
11778
11869
  3. **Audio Profiles** \u2014 Pre-configured \`AudioConfig\` presets: \`speech\`, \`highQuality\`, \`studio\`, \`phone\`
11779
11870
  4. **Session Presets** \u2014 Pre-configured \`AudioSessionConfig\` presets: \`playback\`, \`record\`, \`voiceChat\`, \`ambient\`, \`default\`, \`backgroundRecord\`
11780
- 5. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
11871
+ 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).
11872
+ 6. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
11781
11873
 
11782
11874
  ## Exports
11783
11875
 
@@ -11875,6 +11967,22 @@ interface UsePlayerOptions {
11875
11967
  | toggleMute | () => void | Toggle mute |
11876
11968
 
11877
11969
  > **Critical:** \`feedPCMData()\` accepts \`ArrayBufferLike | Int16Array\` \u2014 **NOT strings or base64**.
11970
+ > If you receive base64-encoded audio from an external API and need to play it back, you MUST decode it to binary first.
11971
+ > **Cross-platform base64 decode** (do NOT use \`atob()\` \u2014 it's browser-only):
11972
+ > \`\`\`typescript
11973
+ > // Cross-platform: works on web AND React Native
11974
+ > function base64ToArrayBuffer(base64: string): ArrayBuffer {
11975
+ > const binaryString = typeof atob !== 'undefined'
11976
+ > ? atob(base64)
11977
+ > : Buffer.from(base64, 'base64').toString('binary');
11978
+ > const bytes = new Uint8Array(binaryString.length);
11979
+ > for (let i = 0; i < binaryString.length; i++) {
11980
+ > bytes[i] = binaryString.charCodeAt(i);
11981
+ > }
11982
+ > return bytes.buffer;
11983
+ > }
11984
+ > // Then: player.feedPCMData(base64ToArrayBuffer(encodedAudio));
11985
+ > \`\`\`
11878
11986
 
11879
11987
  ---
11880
11988
 
@@ -12324,7 +12432,7 @@ function BackgroundTranscriber() {
12324
12432
 
12325
12433
  \`\`\`tsx
12326
12434
  import React from 'react';
12327
- import { View, Text } from '@idealyst/components';
12435
+ import { View, Text, Progress } from '@idealyst/components';
12328
12436
  import { useRecorder, AUDIO_PROFILES } from '@idealyst/audio';
12329
12437
 
12330
12438
  function AudioLevelMeter() {
@@ -12338,22 +12446,10 @@ function AudioLevelMeter() {
12338
12446
  <Text>Level: {Math.round(recorder.level.current * 100)}%</Text>
12339
12447
  <Text>Peak: {Math.round(recorder.level.peak * 100)}%</Text>
12340
12448
  <Text>dB: {recorder.level.db.toFixed(1)}</Text>
12341
- <View
12342
- style={{
12343
- height: 20,
12344
- backgroundColor: '#e0e0e0',
12345
- borderRadius: 10,
12346
- overflow: 'hidden',
12347
- }}
12348
- >
12349
- <View
12350
- style={{
12351
- width: \`\${recorder.level.current * 100}%\`,
12352
- height: '100%',
12353
- backgroundColor: recorder.level.current > 0.8 ? 'red' : 'green',
12354
- }}
12355
- />
12356
- </View>
12449
+ <Progress
12450
+ value={recorder.level.current * 100}
12451
+ intent={recorder.level.current > 0.8 ? 'danger' : 'success'}
12452
+ />
12357
12453
  </View>
12358
12454
  );
12359
12455
  }
@@ -12427,6 +12523,8 @@ function CameraScreen() {
12427
12523
 
12428
12524
  Renders the camera preview. Must receive a camera instance from \`useCamera().cameraRef.current\`.
12429
12525
 
12526
+ > **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" />\`.
12527
+
12430
12528
  \`\`\`typescript
12431
12529
  interface CameraPreviewProps {
12432
12530
  camera: ICamera | null; // Camera instance from useCamera().cameraRef.current
@@ -12808,7 +12906,9 @@ import {
12808
12906
  \`\`\`
12809
12907
 
12810
12908
  > **Common mistakes:**
12909
+ > - **\`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[] }\`.
12811
12910
  > - The method is \`pick()\`, NOT \`pickFiles()\`
12911
+ > - \`pick()\` accepts overrides DIRECTLY: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\`. The \`{ config: ... }\` wrapper is ONLY for \`useFilePicker()\` initialization.
12812
12912
  > - \`FileType\` values are: \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`
12813
12913
  > - \`PickedFile\` has \`uri\`, \`name\`, \`size\`, \`type\`, \`extension\` \u2014 dimensions are in optional \`dimensions?: { width, height }\`, NOT top-level \`width\`/\`height\`
12814
12914
 
@@ -12863,7 +12963,7 @@ interface UseFilePickerOptions {
12863
12963
  | permission | PermissionResult \\| null | Permission result |
12864
12964
  | error | FilePickerError \\| null | Current error |
12865
12965
  | files | PickedFile[] | Last picked files |
12866
- | pick | (config?) => Promise<FilePickerResult> | **Open file picker** |
12966
+ | 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'] } })\` |
12867
12967
  | captureFromCamera | (options?) => Promise<FilePickerResult> | Open camera to capture |
12868
12968
  | clear | () => void | Clear picked files |
12869
12969
  | checkPermission | () => Promise<PermissionResult> | Check permission |
@@ -13000,6 +13100,27 @@ interface PickedFile {
13000
13100
 
13001
13101
  > **Note:** \`dimensions\` is an optional nested object \u2014 NOT top-level \`width\`/\`height\` properties.
13002
13102
 
13103
+ ### FilePickerResult (returned by pick())
13104
+
13105
+ \`\`\`typescript
13106
+ interface FilePickerResult {
13107
+ cancelled: boolean; // Whether user cancelled the picker
13108
+ files: PickedFile[]; // Picked files (empty if cancelled)
13109
+ rejected: RejectedFile[]; // Files rejected by validation
13110
+ error?: FilePickerError; // Error if picker failed
13111
+ }
13112
+ \`\`\`
13113
+
13114
+ > **CRITICAL:** \`pick()\` returns \`FilePickerResult\`, NOT an array. Always access \`result.files\` to get the array of picked files:
13115
+ > \`\`\`typescript
13116
+ > const result = await picker.pick();
13117
+ > if (!result.cancelled && result.files.length > 0) {
13118
+ > const file = result.files[0]; // \u2705 Correct
13119
+ > console.log(file.uri, file.name);
13120
+ > }
13121
+ > // \u274C WRONG: result.length, result[0] \u2014 FilePickerResult is NOT an array
13122
+ > \`\`\`
13123
+
13003
13124
  ### FilePickerConfig
13004
13125
 
13005
13126
  \`\`\`typescript
@@ -13648,9 +13769,9 @@ Animate any style property changes. Returns an animated style object.
13648
13769
 
13649
13770
  \`\`\`typescript
13650
13771
  interface AnimationOptions {
13651
- duration?: Duration; // ms or theme token key
13652
- easing?: EasingKey; // Theme easing key
13653
- delay?: number; // Delay before animation (ms)
13772
+ duration?: Duration; // ms or theme token key (e.g., 300, 500)
13773
+ easing?: EasingKey; // Theme easing key (e.g., 'easeOut', 'spring')
13774
+ delay?: number; // Delay in ms before animation starts (e.g., 100, 200). Useful for staggered entrance animations.
13654
13775
  }
13655
13776
 
13656
13777
  interface UseAnimatedStyleOptions extends AnimationOptions {
@@ -13677,6 +13798,20 @@ const entranceStyle = useAnimatedStyle(
13677
13798
  { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13678
13799
  { duration: 400, easing: 'easeOut' }
13679
13800
  );
13801
+
13802
+ // Staggered entrance: use delay for each item
13803
+ const style1 = useAnimatedStyle(
13804
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13805
+ { duration: 300, easing: 'easeOut', delay: 0 }
13806
+ );
13807
+ const style2 = useAnimatedStyle(
13808
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13809
+ { duration: 300, easing: 'easeOut', delay: 100 }
13810
+ );
13811
+ const style3 = useAnimatedStyle(
13812
+ { opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
13813
+ { duration: 300, easing: 'easeOut', delay: 200 }
13814
+ );
13680
13815
  \`\`\`
13681
13816
 
13682
13817
  ---
@@ -13828,16 +13963,18 @@ For expand/collapse, animate \`opacity\` + \`maxHeight\` together. Do NOT includ
13828
13963
 
13829
13964
  ## Transform Syntax
13830
13965
 
13831
- Use simplified object syntax instead of React Native arrays:
13966
+ **ALWAYS use object syntax** for transforms in useAnimatedStyle/usePresence:
13832
13967
 
13833
13968
  \`\`\`typescript
13834
- // Recommended: object syntax
13969
+ // CORRECT: object syntax (required for animations)
13835
13970
  transform: { x: 10, y: 20, scale: 1.2, rotate: 45 }
13836
13971
 
13837
- // Legacy: array syntax (still supported)
13838
- transform: [{ translateX: 10 }, { translateY: 20 }, { scale: 1.2 }]
13972
+ // WRONG in useAnimatedStyle: array syntax does NOT animate correctly
13973
+ // transform: [{ translateX: 10 }, { translateY: 20 }] // \u274C won't animate
13839
13974
  \`\`\`
13840
13975
 
13976
+ > **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.
13977
+
13841
13978
  | Property | Type | Maps to |
13842
13979
  |----------|------|---------|
13843
13980
  | x | number | translateX |
@@ -18775,6 +18912,396 @@ function MyScreen() {
18775
18912
  `
18776
18913
  };
18777
18914
 
18915
+ // src/data/network-guides.ts
18916
+ var networkGuides = {
18917
+ "idealyst://network/overview": `# @idealyst/network Overview
18918
+
18919
+ 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.
18920
+
18921
+ ## Features
18922
+
18923
+ - **useNetwork Hook** \u2014 Real-time network state with isConnected, connection type, effective speed
18924
+ - **Network State Listener** \u2014 Subscribe to connectivity changes outside React
18925
+ - **Connection Details** \u2014 WiFi, cellular (2G/3G/4G/5G), ethernet, bluetooth, VPN detection
18926
+ - **Effective Connection Type** \u2014 slow-2g, 2g, 3g, 4g speed categories
18927
+ - **Internet Reachability** \u2014 Distinguish "connected to network" from "can reach internet"
18928
+ - **fetchWithTimeout** \u2014 Fetch wrapper with configurable timeout and auto-abort
18929
+ - **retry** \u2014 Exponential backoff retry with optional network-aware gating
18930
+ - **waitForNetwork** \u2014 Promise that resolves when the device comes back online
18931
+ - **Data Saver Detection** \u2014 Detect if user has enabled data-saving mode (web)
18932
+ - **Cross-Platform** \u2014 navigator.onLine + Network Information API (web), @react-native-community/netinfo (native)
18933
+ - **TypeScript** \u2014 Full type safety and IntelliSense
18934
+
18935
+ ## Installation
18936
+
18937
+ \`\`\`bash
18938
+ yarn add @idealyst/network
18939
+
18940
+ # React Native also needs:
18941
+ yarn add @react-native-community/netinfo
18942
+ cd ios && pod install
18943
+ \`\`\`
18944
+
18945
+ ## Quick Start
18946
+
18947
+ \`\`\`tsx
18948
+ import { useNetwork } from '@idealyst/network';
18949
+
18950
+ function App() {
18951
+ const { isConnected, type, effectiveType } = useNetwork();
18952
+
18953
+ if (!isConnected) {
18954
+ return <Text>You are offline</Text>;
18955
+ }
18956
+
18957
+ return (
18958
+ <Text>
18959
+ Connected via {type} ({effectiveType})
18960
+ </Text>
18961
+ );
18962
+ }
18963
+ \`\`\`
18964
+
18965
+ ## Platform Details
18966
+
18967
+ - **Web**: Uses \`navigator.onLine\`, \`online\`/\`offline\` events, and the Network Information API (\`navigator.connection\`) for connection type, downlink speed, RTT, and data saver detection
18968
+ - **React Native**: Uses \`@react-native-community/netinfo\` for connection type, cellular generation, internet reachability, and real-time state changes
18969
+ - **SSR**: Safe for server-side rendering \u2014 returns sensible defaults when \`navigator\` is unavailable
18970
+ `,
18971
+ "idealyst://network/api": `# Network API Reference
18972
+
18973
+ Complete API reference for @idealyst/network.
18974
+
18975
+ ## useNetwork
18976
+
18977
+ React hook for monitoring network connectivity. Returns reactive state that updates automatically.
18978
+
18979
+ \`\`\`tsx
18980
+ const {
18981
+ state, // NetworkState \u2014 full state object
18982
+ isConnected, // boolean \u2014 device has active network
18983
+ isInternetReachable,// boolean | null \u2014 can reach internet (null = unknown)
18984
+ type, // NetworkConnectionType \u2014 'wifi' | 'cellular' | 'ethernet' | ...
18985
+ effectiveType, // EffectiveConnectionType \u2014 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
18986
+ refresh, // () => Promise<NetworkState> \u2014 manually refresh state
18987
+ } = useNetwork(options?: UseNetworkOptions);
18988
+ \`\`\`
18989
+
18990
+ ### UseNetworkOptions
18991
+
18992
+ | Option | Type | Default | Description |
18993
+ |--------|------|---------|-------------|
18994
+ | fetchOnMount | boolean | true | Fetch initial state on mount |
18995
+ | reachabilityUrl | string | (google 204) | URL for reachability pings (web only) |
18996
+ | reachabilityPollInterval | number | 0 | Polling interval in ms for reachability checks (web only). 0 = disabled |
18997
+
18998
+ ---
18999
+
19000
+ ## getNetworkState
19001
+
19002
+ Get a one-time snapshot of the current network state.
19003
+
19004
+ \`\`\`tsx
19005
+ // Web (synchronous)
19006
+ import { getNetworkState } from '@idealyst/network';
19007
+ const state: NetworkState = getNetworkState();
19008
+
19009
+ // Native (async \u2014 requires NetInfo fetch)
19010
+ import { getNetworkState } from '@idealyst/network';
19011
+ const state: NetworkState = await getNetworkState();
19012
+ \`\`\`
19013
+
19014
+ **Note:** On web, \`getNetworkState()\` is synchronous. On native, it returns a Promise.
19015
+
19016
+ ---
19017
+
19018
+ ## addNetworkStateListener
19019
+
19020
+ Subscribe to network state changes outside of React.
19021
+
19022
+ \`\`\`tsx
19023
+ import { addNetworkStateListener } from '@idealyst/network';
19024
+
19025
+ const unsubscribe = addNetworkStateListener((state) => {
19026
+ console.log('Network changed:', state.isConnected, state.type);
19027
+ });
19028
+
19029
+ // Later:
19030
+ unsubscribe();
19031
+ \`\`\`
19032
+
19033
+ ---
19034
+
19035
+ ## fetchWithTimeout
19036
+
19037
+ Fetch wrapper that automatically aborts if the request exceeds a timeout.
19038
+
19039
+ \`\`\`tsx
19040
+ import { fetchWithTimeout } from '@idealyst/network';
19041
+
19042
+ const response = await fetchWithTimeout('https://api.example.com/data', {
19043
+ timeout: 5000, // 5 seconds (default: 10000)
19044
+ method: 'GET',
19045
+ headers: { 'Authorization': 'Bearer token' },
19046
+ });
19047
+ \`\`\`
19048
+
19049
+ ### FetchWithTimeoutOptions
19050
+
19051
+ Extends standard \`RequestInit\` with:
19052
+
19053
+ | Option | Type | Default | Description |
19054
+ |--------|------|---------|-------------|
19055
+ | timeout | number | 10000 | Timeout in milliseconds |
19056
+
19057
+ ---
19058
+
19059
+ ## retry
19060
+
19061
+ Retry a function with exponential backoff. Optionally waits for network before retrying.
19062
+
19063
+ \`\`\`tsx
19064
+ import { retry } from '@idealyst/network';
19065
+
19066
+ const data = await retry(
19067
+ () => fetch('https://api.example.com/data').then(r => r.json()),
19068
+ {
19069
+ maxRetries: 3,
19070
+ baseDelay: 1000,
19071
+ maxDelay: 30000,
19072
+ retryOnlyWhenConnected: true,
19073
+ },
19074
+ );
19075
+ \`\`\`
19076
+
19077
+ ### RetryOptions
19078
+
19079
+ | Option | Type | Default | Description |
19080
+ |--------|------|---------|-------------|
19081
+ | maxRetries | number | 3 | Maximum retry attempts |
19082
+ | baseDelay | number | 1000 | Base delay in ms (doubles each attempt) |
19083
+ | maxDelay | number | 30000 | Maximum delay cap in ms |
19084
+ | retryOnlyWhenConnected | boolean | true | Wait for network before retrying |
19085
+
19086
+ ---
19087
+
19088
+ ## waitForNetwork
19089
+
19090
+ Returns a promise that resolves when the device comes back online.
19091
+
19092
+ \`\`\`tsx
19093
+ import { waitForNetwork } from '@idealyst/network';
19094
+
19095
+ // Wait up to 30 seconds for connectivity
19096
+ await waitForNetwork({ timeout: 30000 });
19097
+ console.log('Back online!');
19098
+ \`\`\`
19099
+
19100
+ If already online, resolves immediately. Rejects with an error if the timeout is exceeded.
19101
+
19102
+ ### WaitForNetworkOptions
19103
+
19104
+ | Option | Type | Default | Description |
19105
+ |--------|------|---------|-------------|
19106
+ | timeout | number | 30000 | Max wait time in ms before rejecting |
19107
+
19108
+ ---
19109
+
19110
+ ## NetworkState
19111
+
19112
+ Full network state object returned by the hook and listeners.
19113
+
19114
+ \`\`\`tsx
19115
+ interface NetworkState {
19116
+ isConnected: boolean; // Device has active network
19117
+ isInternetReachable: boolean | null; // Internet reachable (null = unknown)
19118
+ type: NetworkConnectionType; // 'wifi' | 'cellular' | 'ethernet' | ...
19119
+ effectiveType: EffectiveConnectionType; // 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
19120
+ cellularGeneration: CellularGeneration; // '2g' | '3g' | '4g' | '5g' | null (native only)
19121
+ downlink: number | null; // Mbps (web only, from Network Info API)
19122
+ rtt: number | null; // Round-trip time ms (web only)
19123
+ isDataSaving: boolean | null; // Data saver enabled (web only)
19124
+ }
19125
+ \`\`\`
19126
+
19127
+ ## Type Aliases
19128
+
19129
+ \`\`\`tsx
19130
+ type NetworkConnectionType = 'wifi' | 'cellular' | 'ethernet' | 'bluetooth' | 'vpn' | 'other' | 'none' | 'unknown';
19131
+ type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
19132
+ type CellularGeneration = '2g' | '3g' | '4g' | '5g' | null;
19133
+ \`\`\`
19134
+ `,
19135
+ "idealyst://network/examples": `# Network Examples
19136
+
19137
+ Complete code examples for common @idealyst/network patterns.
19138
+
19139
+ ## Offline Banner
19140
+
19141
+ \`\`\`tsx
19142
+ import { useNetwork } from '@idealyst/network';
19143
+ import { View, Text } from '@idealyst/components';
19144
+
19145
+ function OfflineBanner() {
19146
+ const { isConnected } = useNetwork();
19147
+
19148
+ if (isConnected) return null;
19149
+
19150
+ return (
19151
+ <View style={{ backgroundColor: '#f44336', padding: 8 }}>
19152
+ <Text style={{ color: '#fff', textAlign: 'center' }}>
19153
+ No internet connection
19154
+ </Text>
19155
+ </View>
19156
+ );
19157
+ }
19158
+ \`\`\`
19159
+
19160
+ ## Adaptive Quality Based on Connection
19161
+
19162
+ \`\`\`tsx
19163
+ import { useNetwork } from '@idealyst/network';
19164
+
19165
+ function MediaPlayer({ videoId }: { videoId: string }) {
19166
+ const { effectiveType, isDataSaving } = useNetwork().state;
19167
+
19168
+ const quality = (() => {
19169
+ if (isDataSaving) return 'low';
19170
+ switch (effectiveType) {
19171
+ case 'slow-2g':
19172
+ case '2g':
19173
+ return 'low';
19174
+ case '3g':
19175
+ return 'medium';
19176
+ case '4g':
19177
+ default:
19178
+ return 'high';
19179
+ }
19180
+ })();
19181
+
19182
+ return <VideoPlayer videoId={videoId} quality={quality} />;
19183
+ }
19184
+ \`\`\`
19185
+
19186
+ ## Retry API Calls
19187
+
19188
+ \`\`\`tsx
19189
+ import { retry, fetchWithTimeout } from '@idealyst/network';
19190
+
19191
+ async function fetchUserProfile(userId: string) {
19192
+ const response = await retry(
19193
+ () => fetchWithTimeout(\`https://api.example.com/users/\${userId}\`, {
19194
+ timeout: 5000,
19195
+ }),
19196
+ { maxRetries: 3, baseDelay: 1000 },
19197
+ );
19198
+
19199
+ if (!response.ok) {
19200
+ throw new Error(\`Failed to fetch user: \${response.status}\`);
19201
+ }
19202
+
19203
+ return response.json();
19204
+ }
19205
+ \`\`\`
19206
+
19207
+ ## Wait for Network Before Action
19208
+
19209
+ \`\`\`tsx
19210
+ import { waitForNetwork } from '@idealyst/network';
19211
+
19212
+ async function syncData() {
19213
+ // If offline, wait up to 60 seconds for connectivity
19214
+ await waitForNetwork({ timeout: 60000 });
19215
+
19216
+ // Now we're online \u2014 sync
19217
+ const response = await fetch('https://api.example.com/sync', {
19218
+ method: 'POST',
19219
+ body: JSON.stringify(pendingChanges),
19220
+ });
19221
+
19222
+ return response.json();
19223
+ }
19224
+ \`\`\`
19225
+
19226
+ ## Network State Listener (Outside React)
19227
+
19228
+ \`\`\`tsx
19229
+ import { addNetworkStateListener } from '@idealyst/network';
19230
+
19231
+ // Subscribe to changes (e.g., in a service or module)
19232
+ const unsubscribe = addNetworkStateListener((state) => {
19233
+ if (!state.isConnected) {
19234
+ queueManager.pause();
19235
+ } else {
19236
+ queueManager.resume();
19237
+ }
19238
+ });
19239
+
19240
+ // Cleanup when done
19241
+ unsubscribe();
19242
+ \`\`\`
19243
+
19244
+ ## Connection Type Display
19245
+
19246
+ \`\`\`tsx
19247
+ import { useNetwork } from '@idealyst/network';
19248
+ import { View, Text } from '@idealyst/components';
19249
+
19250
+ function NetworkInfo() {
19251
+ const { state } = useNetwork();
19252
+
19253
+ return (
19254
+ <View>
19255
+ <Text>Status: {state.isConnected ? 'Online' : 'Offline'}</Text>
19256
+ <Text>Type: {state.type}</Text>
19257
+ <Text>Speed: {state.effectiveType}</Text>
19258
+ {state.cellularGeneration && (
19259
+ <Text>Cellular: {state.cellularGeneration}</Text>
19260
+ )}
19261
+ {state.downlink != null && (
19262
+ <Text>Downlink: {state.downlink} Mbps</Text>
19263
+ )}
19264
+ {state.rtt != null && (
19265
+ <Text>RTT: {state.rtt} ms</Text>
19266
+ )}
19267
+ {state.isDataSaving != null && (
19268
+ <Text>Data Saver: {state.isDataSaving ? 'On' : 'Off'}</Text>
19269
+ )}
19270
+ </View>
19271
+ );
19272
+ }
19273
+ \`\`\`
19274
+
19275
+ ## Fetch with Timeout
19276
+
19277
+ \`\`\`tsx
19278
+ import { fetchWithTimeout } from '@idealyst/network';
19279
+
19280
+ async function quickHealthCheck() {
19281
+ try {
19282
+ const response = await fetchWithTimeout('https://api.example.com/health', {
19283
+ timeout: 3000,
19284
+ method: 'HEAD',
19285
+ });
19286
+ return response.ok;
19287
+ } catch {
19288
+ return false;
19289
+ }
19290
+ }
19291
+ \`\`\`
19292
+
19293
+ ## Best Practices
19294
+
19295
+ 1. **Use \`useNetwork\` for UI** \u2014 The hook handles subscriptions and cleanup automatically
19296
+ 2. **Use \`addNetworkStateListener\` for services** \u2014 For non-React code (queue managers, sync services)
19297
+ 3. **Combine retry + fetchWithTimeout** \u2014 For resilient API calls
19298
+ 4. **Check \`isInternetReachable\`** \u2014 Being "connected" to WiFi doesn't mean you have internet
19299
+ 5. **Respect data saver** \u2014 Reduce payload sizes and avoid autoplay when \`isDataSaving\` is true
19300
+ 6. **Don't poll too aggressively** \u2014 If using \`reachabilityPollInterval\`, keep it >= 15000ms
19301
+ 7. **Use \`waitForNetwork\` for offline-first** \u2014 Queue operations and wait for connectivity to sync
19302
+ `
19303
+ };
19304
+
18778
19305
  // src/data/install-guides.ts
18779
19306
  var installGuides = {
18780
19307
  // ============================================================================
@@ -20259,14 +20786,19 @@ This server has tools for every aspect of the framework. **Look up APIs as you n
20259
20786
 
20260
20787
  ### Workflow
20261
20788
 
20262
- Write code iteratively. Look up each API right before you use it:
20789
+ Write code iteratively \u2014 **look up one API, write one file, repeat**:
20790
+
20791
+ 1. Call \`get_intro\` (this response)
20792
+ 2. Look up the API for your FIRST file only (e.g., \`get_component_types("Button,Card,Text")\`)
20793
+ 3. **Write that file immediately** using the Write tool
20794
+ 4. Then look up the next API you need, write the next file, and so on
20263
20795
 
20264
20796
  - **Before using a component** \u2014 call \`get_component_types\` for its props (supports batching: \`get_component_types("Button,Card,Text")\`)
20265
20797
  - **Before using a package** \u2014 call its dedicated \`get_*_guide\` tool with topic \`api\`
20266
20798
  - **Search icons once** \u2014 batch all terms into one call: \`search_icons("home settings check timer")\`
20267
20799
  - **Check recipes** \u2014 \`search_recipes\` for ready-made patterns you can adapt
20268
20800
 
20269
- Do NOT try to read all documentation before writing code. Start building, and look things up as you go.
20801
+ > **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**.
20270
20802
 
20271
20803
  ### Component Tools
20272
20804
  - \`list_components\` / \`search_components\` \u2014 Discover available components
@@ -20341,6 +20873,10 @@ const tabs: { icon: IconName }[] = [{ icon: 'home' }, { icon: 'search' }]; // NO
20341
20873
 
20342
20874
  > **CRITICAL:** Icon names do NOT have an \`mdi:\` prefix. Use bare names like \`'delete'\`, \`'home'\`, \`'check-circle'\` \u2014 NOT \`'mdi:delete'\`, \`'mdi:home'\`, etc.
20343
20875
  > **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.
20876
+ > **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\`.
20877
+ > **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.
20878
+
20879
+ **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.
20344
20880
 
20345
20881
  ---
20346
20882
 
@@ -20350,26 +20886,27 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
20350
20886
 
20351
20887
  ### Component Props
20352
20888
  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"\`.
20353
- 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\`.
20889
+ 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.
20354
20890
  3. **Button/IconButton** \`type\` is \`'contained' | 'outlined' | 'text'\` \u2014 NOT \`'ghost'\`, \`'solid'\`, \`'default'\`. Button has \`leftIcon\` and \`rightIcon\` \u2014 NOT \`icon\`.
20355
20891
  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'\`.
20356
- 5. **Badge** \`type\` is \`'filled' | 'outlined' | 'dot'\` \u2014 NOT \`'soft'\`, \`'subtle'\`, \`'solid'\`.
20892
+ 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.
20357
20893
  6. **Avatar** uses \`src\` for image URL, \`fallback\` for initials \u2014 NOT \`name\`, \`initials\`, \`label\`. Shape: \`'circle' | 'square'\`.
20358
20894
  7. **Link** requires a \`to\` prop (path string) \u2014 it's a navigation link, NOT pressable text.
20359
20895
  8. **List** takes \`children\` \u2014 NOT \`data\`, \`renderItem\`, or \`keyExtractor\`. Map your data: \`<List>{items.map(item => <View key={item.id}>...</View>)}</List>\`
20360
20896
  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\`.
20361
20897
  10. The component is **TextInput**, NOT \`Input\`.
20362
- 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>\`.
20898
+ 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"\`.
20899
+ 12. **Switch** uses \`checked\` and \`onChange\` \u2014 NOT \`value\` and \`onValueChange\` (React Native convention). Also has \`label\`, \`labelPosition\`, \`disabled\`.
20363
20900
 
20364
20901
  ### Navigation
20365
- 11. Use \`NavigatorProvider\` \u2014 there is NO \`Router\` export.
20902
+ 11. **NavigatorProvider** takes a \`route\` prop (SINGULAR) \u2014 NOT \`routes\`: \`<NavigatorProvider route={routeConfig} />\`. There is NO \`Router\` export.
20366
20903
  12. Use \`useNavigator()\` \u2014 NOT \`useNavigate()\`.
20367
20904
  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).
20368
- 14. \`useNavigationState\` returns \`Record<string, unknown>\` by default. Always provide a type parameter: \`useNavigationState<{ title?: string }>()\`.
20369
- 15. \`useParams()\` does NOT accept generic type arguments. It returns \`Record<string, string>\`. Do NOT write \`useParams<{ id: string }>()\` \u2014 that causes TS2558.
20905
+ 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.
20906
+ 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;\`.
20370
20907
 
20371
20908
  ### Imports & Styling
20372
- 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\`).
20909
+ 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.
20373
20910
  17. **Never** import from \`react-native-unistyles\` \u2014 use \`@idealyst/theme\` (\`configureThemes\`, \`ThemeSettings\`, \`useTheme\`).
20374
20911
  18. **useTheme()** returns the Theme object **directly** (NOT wrapped): \`const theme = useTheme();\`. Do NOT destructure: \`const { theme } = useTheme()\` \u2014 causes TS2339.
20375
20912
  19. **Spacing & Layout**: Use component shorthand props for spacing \u2014 NOT \`theme.spacing\` (which does NOT exist). The correct patterns:
@@ -20384,13 +20921,28 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
20384
20921
  - **WRONG**: \`theme.colors.intent.danger\` \u2014 does NOT exist; intents are at \`theme.intents.danger\`
20385
20922
  - **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\`
20386
20923
  - **CORRECT**: \`theme.intents.primary.primary\` for the color, \`theme.intents.primary.contrast\` for text on that color
20924
+ - **Color key reference** (all keys available):
20925
+ - \`theme.colors.surface\`: \`screen\`, \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
20926
+ - \`theme.colors.text\`: \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
20927
+ - \`theme.colors.border\`: \`primary\`, \`secondary\`, \`tertiary\`, \`disabled\`
20928
+ - \`theme.intents\`: \`primary\`, \`secondary\`, \`success\`, \`warning\`, \`danger\`, \`info\`, \`neutral\` (each has \`.primary\`, \`.contrast\`, \`.light\`, \`.dark\`)
20387
20929
 
20388
20930
  ### Cross-Platform (CRITICAL)
20389
20931
  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.
20390
- 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).
20932
+ 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.
20933
+ 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.
20934
+ 22. **Platform file barrel exports**: When creating \`Component.web.tsx\` + \`Component.native.tsx\`, you need THREE index files:
20935
+ - \`index.web.ts\` \u2014 \`export { default as Component } from './Component.web';\`
20936
+ - \`index.native.ts\` \u2014 \`export { default as Component } from './Component.native';\`
20937
+ - \`index.ts\` \u2014 \`export { default as Component } from './Component.web';\` (base file for TypeScript resolution)
20938
+ - **WRONG**: \`export { default } from './Component';\` \u2014 no bare \`Component.ts\` exists, only \`.web.tsx\`/\`.native.tsx\`. This causes TS2307.
20939
+ - **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.
20391
20940
 
20392
20941
  ### React 19 TypeScript
20393
20942
  - **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[]>([])\`.
20943
+ - **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.
20944
+ - **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.
20945
+ - **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).
20394
20946
 
20395
20947
  ### Scaffolded Project Layout
20396
20948
  When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`idealyst create\`), files go in specific locations:
@@ -20407,11 +20959,11 @@ When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`i
20407
20959
  > **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.
20408
20960
 
20409
20961
  ### Package-Specific (call guide tools for full API)
20410
- 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\`.
20962
+ 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\`.
20411
20963
  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\`.
20412
- 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\`.
20964
+ 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\`.
20413
20965
  21. **Storage** (\`@idealyst/storage\`): Methods are \`getItem()\`/\`setItem()\`, NOT \`get()\`/\`set()\`. Values are string-only. Call \`get_storage_guide\` topic \`api\`.
20414
- 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\`.
20966
+ 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\`.
20415
20967
  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\`.
20416
20968
  `;
20417
20969
 
@@ -27877,7 +28429,7 @@ var import_url = require("url");
27877
28429
  // src/generated/types.json
27878
28430
  var types_default = {
27879
28431
  version: "1.0.93",
27880
- extractedAt: "2026-02-24T16:32:56.796Z",
28432
+ extractedAt: "2026-02-24T21:11:17.058Z",
27881
28433
  components: {
27882
28434
  Accordion: {
27883
28435
  name: "Accordion",
@@ -50178,7 +50730,7 @@ var types_default = {
50178
50730
  navigation: {
50179
50731
  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',
50180
50732
  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}",
50181
- 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;",
50733
+ 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;",
50182
50734
  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}",
50183
50735
  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}",
50184
50736
  TabNavigatorParam: "export type TabNavigatorParam = {\n layout: 'tab'\n routes: RouteParam<TabBarScreenOptions>[]\n layoutComponent?: TabLayoutComponent\n} & BaseNavigatorParam",
@@ -69813,12 +70365,32 @@ function postProcessComponentTypes(componentName, result) {
69813
70365
  }
69814
70366
  if (componentName.toLowerCase() === "card") {
69815
70367
  if (typeof result === "object" && result !== null) {
69816
- 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>';
70368
+ 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 }}.";
69817
70369
  }
69818
70370
  }
69819
70371
  if (componentName.toLowerCase() === "view") {
69820
70372
  if (typeof result === "object" && result !== null) {
69821
- 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 }}.";
70373
+ 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.";
70374
+ }
70375
+ }
70376
+ if (componentName.toLowerCase() === "textarea") {
70377
+ if (typeof result === "object" && result !== null) {
70378
+ 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.";
70379
+ }
70380
+ }
70381
+ if (componentName.toLowerCase() === "image") {
70382
+ if (typeof result === "object" && result !== null) {
70383
+ 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} />";
70384
+ }
70385
+ }
70386
+ if (componentName.toLowerCase() === "icon") {
70387
+ if (typeof result === "object" && result !== null) {
70388
+ 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`.";
70389
+ }
70390
+ }
70391
+ if (componentName.toLowerCase() === "badge") {
70392
+ if (typeof result === "object" && result !== null) {
70393
+ 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.";
69822
70394
  }
69823
70395
  }
69824
70396
  return result;
@@ -70093,6 +70665,8 @@ function getNavigationTypes2(args = {}) {
70093
70665
  const result = getNavigationTypes(format);
70094
70666
  if (typeof result === "object" && result !== null) {
70095
70667
  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.`;
70668
+ 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.";
70669
+ 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```";
70096
70670
  }
70097
70671
  return jsonResponse(result);
70098
70672
  } catch (error) {
@@ -70302,6 +70876,17 @@ function getLiveActivityGuide(args) {
70302
70876
  }
70303
70877
  return textResponse(guide);
70304
70878
  }
70879
+ function getNetworkGuide(args) {
70880
+ const topic = args.topic;
70881
+ const uri = `idealyst://network/${topic}`;
70882
+ const guide = networkGuides[uri];
70883
+ if (!guide) {
70884
+ return textResponse(
70885
+ `Topic "${topic}" not found. Available topics: overview, api, examples`
70886
+ );
70887
+ }
70888
+ return textResponse(guide);
70889
+ }
70305
70890
  function listPackages(args = {}) {
70306
70891
  const category = args.category;
70307
70892
  if (category) {
@@ -70552,6 +71137,7 @@ var toolHandlers = {
70552
71137
  get_payments_guide: getPaymentsGuide,
70553
71138
  get_notifications_guide: getNotificationsGuide,
70554
71139
  get_live_activity_guide: getLiveActivityGuide,
71140
+ get_network_guide: getNetworkGuide,
70555
71141
  list_packages: listPackages,
70556
71142
  get_package_docs: getPackageDocs,
70557
71143
  search_packages: searchPackages2,