@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/{chunk-TLA6ZBC5.js → chunk-UD56HXN3.js} +626 -55
- package/dist/chunk-UD56HXN3.js.map +1 -0
- package/dist/index.cjs +632 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/tools/index.cjs +629 -54
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.d.cts +16 -3
- package/dist/tools/index.d.ts +16 -3
- package/dist/tools/index.js +9 -1
- package/package.json +5 -5
- package/dist/chunk-TLA6ZBC5.js.map +0 -1
|
@@ -551,6 +551,21 @@ var getLiveActivityGuideDefinition = {
|
|
|
551
551
|
required: ["topic"]
|
|
552
552
|
}
|
|
553
553
|
};
|
|
554
|
+
var getNetworkGuideDefinition = {
|
|
555
|
+
name: "get_network_guide",
|
|
556
|
+
description: "Get documentation for @idealyst/network cross-platform network connectivity and utilities package. Covers useNetwork hook, fetchWithTimeout, retry, waitForNetwork, and examples.",
|
|
557
|
+
inputSchema: {
|
|
558
|
+
type: "object",
|
|
559
|
+
properties: {
|
|
560
|
+
topic: {
|
|
561
|
+
type: "string",
|
|
562
|
+
description: "Topic to get docs for: 'overview', 'api', 'examples'",
|
|
563
|
+
enum: ["overview", "api", "examples"]
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
required: ["topic"]
|
|
567
|
+
}
|
|
568
|
+
};
|
|
554
569
|
var toolDefinitions = [
|
|
555
570
|
// Component tools
|
|
556
571
|
listComponentsDefinition,
|
|
@@ -586,6 +601,7 @@ var toolDefinitions = [
|
|
|
586
601
|
getPaymentsGuideDefinition,
|
|
587
602
|
getNotificationsGuideDefinition,
|
|
588
603
|
getLiveActivityGuideDefinition,
|
|
604
|
+
getNetworkGuideDefinition,
|
|
589
605
|
// Package tools
|
|
590
606
|
listPackagesDefinition,
|
|
591
607
|
getPackageDocsDefinition,
|
|
@@ -838,9 +854,10 @@ var componentMetadata = {
|
|
|
838
854
|
},
|
|
839
855
|
Image: {
|
|
840
856
|
category: "display",
|
|
841
|
-
description: "Cross-platform image component with loading and error states",
|
|
857
|
+
description: "Cross-platform image component for displaying photos, pictures, and remote/local images with loading and error states",
|
|
842
858
|
features: [
|
|
843
|
-
"
|
|
859
|
+
"Display photos, pictures, and media from URLs or local sources",
|
|
860
|
+
"Multiple resize modes (cover, contain, stretch, center)",
|
|
844
861
|
"Loading placeholder",
|
|
845
862
|
"Error fallback",
|
|
846
863
|
"Lazy loading",
|
|
@@ -1210,7 +1227,11 @@ var componentAliases = {
|
|
|
1210
1227
|
fab: "IconButton",
|
|
1211
1228
|
modal: "Dialog",
|
|
1212
1229
|
tooltip: "Tooltip",
|
|
1213
|
-
snackbar: "Toast"
|
|
1230
|
+
snackbar: "Toast",
|
|
1231
|
+
photo: "Image",
|
|
1232
|
+
picture: "Image",
|
|
1233
|
+
img: "Image",
|
|
1234
|
+
thumbnail: "Image"
|
|
1214
1235
|
};
|
|
1215
1236
|
function findComponentName(componentName) {
|
|
1216
1237
|
if (componentMetadata[componentName]) {
|
|
@@ -2965,7 +2986,8 @@ yarn add @idealyst/audio
|
|
|
2965
2986
|
2. **Audio Session** \u2014 On iOS/Android, configure the audio session category before recording/playback
|
|
2966
2987
|
3. **Audio Profiles** \u2014 Pre-configured \`AudioConfig\` presets: \`speech\`, \`highQuality\`, \`studio\`, \`phone\`
|
|
2967
2988
|
4. **Session Presets** \u2014 Pre-configured \`AudioSessionConfig\` presets: \`playback\`, \`record\`, \`voiceChat\`, \`ambient\`, \`default\`, \`backgroundRecord\`
|
|
2968
|
-
5. **
|
|
2989
|
+
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).
|
|
2990
|
+
6. **Background Recording** \u2014 \`useBackgroundRecorder\` hook for recording that continues when the app is backgrounded (iOS/Android). Requires app-level native entitlements.
|
|
2969
2991
|
|
|
2970
2992
|
## Exports
|
|
2971
2993
|
|
|
@@ -3063,6 +3085,22 @@ interface UsePlayerOptions {
|
|
|
3063
3085
|
| toggleMute | () => void | Toggle mute |
|
|
3064
3086
|
|
|
3065
3087
|
> **Critical:** \`feedPCMData()\` accepts \`ArrayBufferLike | Int16Array\` \u2014 **NOT strings or base64**.
|
|
3088
|
+
> If you receive base64-encoded audio from an external API and need to play it back, you MUST decode it to binary first.
|
|
3089
|
+
> **Cross-platform base64 decode** (do NOT use \`atob()\` \u2014 it's browser-only):
|
|
3090
|
+
> \`\`\`typescript
|
|
3091
|
+
> // Cross-platform: works on web AND React Native
|
|
3092
|
+
> function base64ToArrayBuffer(base64: string): ArrayBuffer {
|
|
3093
|
+
> const binaryString = typeof atob !== 'undefined'
|
|
3094
|
+
> ? atob(base64)
|
|
3095
|
+
> : Buffer.from(base64, 'base64').toString('binary');
|
|
3096
|
+
> const bytes = new Uint8Array(binaryString.length);
|
|
3097
|
+
> for (let i = 0; i < binaryString.length; i++) {
|
|
3098
|
+
> bytes[i] = binaryString.charCodeAt(i);
|
|
3099
|
+
> }
|
|
3100
|
+
> return bytes.buffer;
|
|
3101
|
+
> }
|
|
3102
|
+
> // Then: player.feedPCMData(base64ToArrayBuffer(encodedAudio));
|
|
3103
|
+
> \`\`\`
|
|
3066
3104
|
|
|
3067
3105
|
---
|
|
3068
3106
|
|
|
@@ -3512,7 +3550,7 @@ function BackgroundTranscriber() {
|
|
|
3512
3550
|
|
|
3513
3551
|
\`\`\`tsx
|
|
3514
3552
|
import React from 'react';
|
|
3515
|
-
import { View, Text } from '@idealyst/components';
|
|
3553
|
+
import { View, Text, Progress } from '@idealyst/components';
|
|
3516
3554
|
import { useRecorder, AUDIO_PROFILES } from '@idealyst/audio';
|
|
3517
3555
|
|
|
3518
3556
|
function AudioLevelMeter() {
|
|
@@ -3526,22 +3564,10 @@ function AudioLevelMeter() {
|
|
|
3526
3564
|
<Text>Level: {Math.round(recorder.level.current * 100)}%</Text>
|
|
3527
3565
|
<Text>Peak: {Math.round(recorder.level.peak * 100)}%</Text>
|
|
3528
3566
|
<Text>dB: {recorder.level.db.toFixed(1)}</Text>
|
|
3529
|
-
<
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
borderRadius: 10,
|
|
3534
|
-
overflow: 'hidden',
|
|
3535
|
-
}}
|
|
3536
|
-
>
|
|
3537
|
-
<View
|
|
3538
|
-
style={{
|
|
3539
|
-
width: \`\${recorder.level.current * 100}%\`,
|
|
3540
|
-
height: '100%',
|
|
3541
|
-
backgroundColor: recorder.level.current > 0.8 ? 'red' : 'green',
|
|
3542
|
-
}}
|
|
3543
|
-
/>
|
|
3544
|
-
</View>
|
|
3567
|
+
<Progress
|
|
3568
|
+
value={recorder.level.current * 100}
|
|
3569
|
+
intent={recorder.level.current > 0.8 ? 'danger' : 'success'}
|
|
3570
|
+
/>
|
|
3545
3571
|
</View>
|
|
3546
3572
|
);
|
|
3547
3573
|
}
|
|
@@ -3615,6 +3641,8 @@ function CameraScreen() {
|
|
|
3615
3641
|
|
|
3616
3642
|
Renders the camera preview. Must receive a camera instance from \`useCamera().cameraRef.current\`.
|
|
3617
3643
|
|
|
3644
|
+
> **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" />\`.
|
|
3645
|
+
|
|
3618
3646
|
\`\`\`typescript
|
|
3619
3647
|
interface CameraPreviewProps {
|
|
3620
3648
|
camera: ICamera | null; // Camera instance from useCamera().cameraRef.current
|
|
@@ -3996,7 +4024,9 @@ import {
|
|
|
3996
4024
|
\`\`\`
|
|
3997
4025
|
|
|
3998
4026
|
> **Common mistakes:**
|
|
4027
|
+
> - **\`pick()\` returns \`FilePickerResult\`, NOT an array.** Access files via \`result.files\`: \`result.files.length\`, \`result.files[0].uri\`. Do NOT write \`result.length\` or \`result[0]\` \u2014 \`FilePickerResult\` is an object: \`{ cancelled: boolean, files: PickedFile[], rejected: RejectedFile[] }\`.
|
|
3999
4028
|
> - The method is \`pick()\`, NOT \`pickFiles()\`
|
|
4029
|
+
> - \`pick()\` accepts overrides DIRECTLY: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\`. The \`{ config: ... }\` wrapper is ONLY for \`useFilePicker()\` initialization.
|
|
4000
4030
|
> - \`FileType\` values are: \`'image' | 'video' | 'audio' | 'document' | 'archive' | 'any'\` \u2014 NOT \`'pdf'\` or \`'doc'\`
|
|
4001
4031
|
> - \`PickedFile\` has \`uri\`, \`name\`, \`size\`, \`type\`, \`extension\` \u2014 dimensions are in optional \`dimensions?: { width, height }\`, NOT top-level \`width\`/\`height\`
|
|
4002
4032
|
|
|
@@ -4051,7 +4081,7 @@ interface UseFilePickerOptions {
|
|
|
4051
4081
|
| permission | PermissionResult \\| null | Permission result |
|
|
4052
4082
|
| error | FilePickerError \\| null | Current error |
|
|
4053
4083
|
| files | PickedFile[] | Last picked files |
|
|
4054
|
-
| pick | (
|
|
4084
|
+
| pick | (overrides?: Partial\\<FilePickerConfig\\>) => Promise<FilePickerResult> | **Open file picker. Overrides go DIRECTLY \u2014 NOT wrapped in \`{ config }\`.** Example: \`picker.pick({ allowedTypes: ['image'] })\` \u2014 NOT \`picker.pick({ config: { allowedTypes: ['image'] } })\` |
|
|
4055
4085
|
| captureFromCamera | (options?) => Promise<FilePickerResult> | Open camera to capture |
|
|
4056
4086
|
| clear | () => void | Clear picked files |
|
|
4057
4087
|
| checkPermission | () => Promise<PermissionResult> | Check permission |
|
|
@@ -4188,6 +4218,27 @@ interface PickedFile {
|
|
|
4188
4218
|
|
|
4189
4219
|
> **Note:** \`dimensions\` is an optional nested object \u2014 NOT top-level \`width\`/\`height\` properties.
|
|
4190
4220
|
|
|
4221
|
+
### FilePickerResult (returned by pick())
|
|
4222
|
+
|
|
4223
|
+
\`\`\`typescript
|
|
4224
|
+
interface FilePickerResult {
|
|
4225
|
+
cancelled: boolean; // Whether user cancelled the picker
|
|
4226
|
+
files: PickedFile[]; // Picked files (empty if cancelled)
|
|
4227
|
+
rejected: RejectedFile[]; // Files rejected by validation
|
|
4228
|
+
error?: FilePickerError; // Error if picker failed
|
|
4229
|
+
}
|
|
4230
|
+
\`\`\`
|
|
4231
|
+
|
|
4232
|
+
> **CRITICAL:** \`pick()\` returns \`FilePickerResult\`, NOT an array. Always access \`result.files\` to get the array of picked files:
|
|
4233
|
+
> \`\`\`typescript
|
|
4234
|
+
> const result = await picker.pick();
|
|
4235
|
+
> if (!result.cancelled && result.files.length > 0) {
|
|
4236
|
+
> const file = result.files[0]; // \u2705 Correct
|
|
4237
|
+
> console.log(file.uri, file.name);
|
|
4238
|
+
> }
|
|
4239
|
+
> // \u274C WRONG: result.length, result[0] \u2014 FilePickerResult is NOT an array
|
|
4240
|
+
> \`\`\`
|
|
4241
|
+
|
|
4191
4242
|
### FilePickerConfig
|
|
4192
4243
|
|
|
4193
4244
|
\`\`\`typescript
|
|
@@ -4836,9 +4887,9 @@ Animate any style property changes. Returns an animated style object.
|
|
|
4836
4887
|
|
|
4837
4888
|
\`\`\`typescript
|
|
4838
4889
|
interface AnimationOptions {
|
|
4839
|
-
duration?: Duration; // ms or theme token key
|
|
4840
|
-
easing?: EasingKey; // Theme easing key
|
|
4841
|
-
delay?: number; // Delay before animation (
|
|
4890
|
+
duration?: Duration; // ms or theme token key (e.g., 300, 500)
|
|
4891
|
+
easing?: EasingKey; // Theme easing key (e.g., 'easeOut', 'spring')
|
|
4892
|
+
delay?: number; // Delay in ms before animation starts (e.g., 100, 200). Useful for staggered entrance animations.
|
|
4842
4893
|
}
|
|
4843
4894
|
|
|
4844
4895
|
interface UseAnimatedStyleOptions extends AnimationOptions {
|
|
@@ -4865,6 +4916,20 @@ const entranceStyle = useAnimatedStyle(
|
|
|
4865
4916
|
{ opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
|
|
4866
4917
|
{ duration: 400, easing: 'easeOut' }
|
|
4867
4918
|
);
|
|
4919
|
+
|
|
4920
|
+
// Staggered entrance: use delay for each item
|
|
4921
|
+
const style1 = useAnimatedStyle(
|
|
4922
|
+
{ opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
|
|
4923
|
+
{ duration: 300, easing: 'easeOut', delay: 0 }
|
|
4924
|
+
);
|
|
4925
|
+
const style2 = useAnimatedStyle(
|
|
4926
|
+
{ opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
|
|
4927
|
+
{ duration: 300, easing: 'easeOut', delay: 100 }
|
|
4928
|
+
);
|
|
4929
|
+
const style3 = useAnimatedStyle(
|
|
4930
|
+
{ opacity: ready ? 1 : 0, transform: { y: ready ? 0 : 20 } },
|
|
4931
|
+
{ duration: 300, easing: 'easeOut', delay: 200 }
|
|
4932
|
+
);
|
|
4868
4933
|
\`\`\`
|
|
4869
4934
|
|
|
4870
4935
|
---
|
|
@@ -5016,16 +5081,18 @@ For expand/collapse, animate \`opacity\` + \`maxHeight\` together. Do NOT includ
|
|
|
5016
5081
|
|
|
5017
5082
|
## Transform Syntax
|
|
5018
5083
|
|
|
5019
|
-
|
|
5084
|
+
**ALWAYS use object syntax** for transforms in useAnimatedStyle/usePresence:
|
|
5020
5085
|
|
|
5021
5086
|
\`\`\`typescript
|
|
5022
|
-
//
|
|
5087
|
+
// CORRECT: object syntax (required for animations)
|
|
5023
5088
|
transform: { x: 10, y: 20, scale: 1.2, rotate: 45 }
|
|
5024
5089
|
|
|
5025
|
-
//
|
|
5026
|
-
transform: [{ translateX: 10 }, { translateY: 20 }
|
|
5090
|
+
// WRONG in useAnimatedStyle: array syntax does NOT animate correctly
|
|
5091
|
+
// transform: [{ translateX: 10 }, { translateY: 20 }] // \u274C won't animate
|
|
5027
5092
|
\`\`\`
|
|
5028
5093
|
|
|
5094
|
+
> **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.
|
|
5095
|
+
|
|
5029
5096
|
| Property | Type | Maps to |
|
|
5030
5097
|
|----------|------|---------|
|
|
5031
5098
|
| x | number | translateX |
|
|
@@ -9963,6 +10030,396 @@ function MyScreen() {
|
|
|
9963
10030
|
`
|
|
9964
10031
|
};
|
|
9965
10032
|
|
|
10033
|
+
// src/data/network-guides.ts
|
|
10034
|
+
var networkGuides = {
|
|
10035
|
+
"idealyst://network/overview": `# @idealyst/network Overview
|
|
10036
|
+
|
|
10037
|
+
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.
|
|
10038
|
+
|
|
10039
|
+
## Features
|
|
10040
|
+
|
|
10041
|
+
- **useNetwork Hook** \u2014 Real-time network state with isConnected, connection type, effective speed
|
|
10042
|
+
- **Network State Listener** \u2014 Subscribe to connectivity changes outside React
|
|
10043
|
+
- **Connection Details** \u2014 WiFi, cellular (2G/3G/4G/5G), ethernet, bluetooth, VPN detection
|
|
10044
|
+
- **Effective Connection Type** \u2014 slow-2g, 2g, 3g, 4g speed categories
|
|
10045
|
+
- **Internet Reachability** \u2014 Distinguish "connected to network" from "can reach internet"
|
|
10046
|
+
- **fetchWithTimeout** \u2014 Fetch wrapper with configurable timeout and auto-abort
|
|
10047
|
+
- **retry** \u2014 Exponential backoff retry with optional network-aware gating
|
|
10048
|
+
- **waitForNetwork** \u2014 Promise that resolves when the device comes back online
|
|
10049
|
+
- **Data Saver Detection** \u2014 Detect if user has enabled data-saving mode (web)
|
|
10050
|
+
- **Cross-Platform** \u2014 navigator.onLine + Network Information API (web), @react-native-community/netinfo (native)
|
|
10051
|
+
- **TypeScript** \u2014 Full type safety and IntelliSense
|
|
10052
|
+
|
|
10053
|
+
## Installation
|
|
10054
|
+
|
|
10055
|
+
\`\`\`bash
|
|
10056
|
+
yarn add @idealyst/network
|
|
10057
|
+
|
|
10058
|
+
# React Native also needs:
|
|
10059
|
+
yarn add @react-native-community/netinfo
|
|
10060
|
+
cd ios && pod install
|
|
10061
|
+
\`\`\`
|
|
10062
|
+
|
|
10063
|
+
## Quick Start
|
|
10064
|
+
|
|
10065
|
+
\`\`\`tsx
|
|
10066
|
+
import { useNetwork } from '@idealyst/network';
|
|
10067
|
+
|
|
10068
|
+
function App() {
|
|
10069
|
+
const { isConnected, type, effectiveType } = useNetwork();
|
|
10070
|
+
|
|
10071
|
+
if (!isConnected) {
|
|
10072
|
+
return <Text>You are offline</Text>;
|
|
10073
|
+
}
|
|
10074
|
+
|
|
10075
|
+
return (
|
|
10076
|
+
<Text>
|
|
10077
|
+
Connected via {type} ({effectiveType})
|
|
10078
|
+
</Text>
|
|
10079
|
+
);
|
|
10080
|
+
}
|
|
10081
|
+
\`\`\`
|
|
10082
|
+
|
|
10083
|
+
## Platform Details
|
|
10084
|
+
|
|
10085
|
+
- **Web**: Uses \`navigator.onLine\`, \`online\`/\`offline\` events, and the Network Information API (\`navigator.connection\`) for connection type, downlink speed, RTT, and data saver detection
|
|
10086
|
+
- **React Native**: Uses \`@react-native-community/netinfo\` for connection type, cellular generation, internet reachability, and real-time state changes
|
|
10087
|
+
- **SSR**: Safe for server-side rendering \u2014 returns sensible defaults when \`navigator\` is unavailable
|
|
10088
|
+
`,
|
|
10089
|
+
"idealyst://network/api": `# Network API Reference
|
|
10090
|
+
|
|
10091
|
+
Complete API reference for @idealyst/network.
|
|
10092
|
+
|
|
10093
|
+
## useNetwork
|
|
10094
|
+
|
|
10095
|
+
React hook for monitoring network connectivity. Returns reactive state that updates automatically.
|
|
10096
|
+
|
|
10097
|
+
\`\`\`tsx
|
|
10098
|
+
const {
|
|
10099
|
+
state, // NetworkState \u2014 full state object
|
|
10100
|
+
isConnected, // boolean \u2014 device has active network
|
|
10101
|
+
isInternetReachable,// boolean | null \u2014 can reach internet (null = unknown)
|
|
10102
|
+
type, // NetworkConnectionType \u2014 'wifi' | 'cellular' | 'ethernet' | ...
|
|
10103
|
+
effectiveType, // EffectiveConnectionType \u2014 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
|
|
10104
|
+
refresh, // () => Promise<NetworkState> \u2014 manually refresh state
|
|
10105
|
+
} = useNetwork(options?: UseNetworkOptions);
|
|
10106
|
+
\`\`\`
|
|
10107
|
+
|
|
10108
|
+
### UseNetworkOptions
|
|
10109
|
+
|
|
10110
|
+
| Option | Type | Default | Description |
|
|
10111
|
+
|--------|------|---------|-------------|
|
|
10112
|
+
| fetchOnMount | boolean | true | Fetch initial state on mount |
|
|
10113
|
+
| reachabilityUrl | string | (google 204) | URL for reachability pings (web only) |
|
|
10114
|
+
| reachabilityPollInterval | number | 0 | Polling interval in ms for reachability checks (web only). 0 = disabled |
|
|
10115
|
+
|
|
10116
|
+
---
|
|
10117
|
+
|
|
10118
|
+
## getNetworkState
|
|
10119
|
+
|
|
10120
|
+
Get a one-time snapshot of the current network state.
|
|
10121
|
+
|
|
10122
|
+
\`\`\`tsx
|
|
10123
|
+
// Web (synchronous)
|
|
10124
|
+
import { getNetworkState } from '@idealyst/network';
|
|
10125
|
+
const state: NetworkState = getNetworkState();
|
|
10126
|
+
|
|
10127
|
+
// Native (async \u2014 requires NetInfo fetch)
|
|
10128
|
+
import { getNetworkState } from '@idealyst/network';
|
|
10129
|
+
const state: NetworkState = await getNetworkState();
|
|
10130
|
+
\`\`\`
|
|
10131
|
+
|
|
10132
|
+
**Note:** On web, \`getNetworkState()\` is synchronous. On native, it returns a Promise.
|
|
10133
|
+
|
|
10134
|
+
---
|
|
10135
|
+
|
|
10136
|
+
## addNetworkStateListener
|
|
10137
|
+
|
|
10138
|
+
Subscribe to network state changes outside of React.
|
|
10139
|
+
|
|
10140
|
+
\`\`\`tsx
|
|
10141
|
+
import { addNetworkStateListener } from '@idealyst/network';
|
|
10142
|
+
|
|
10143
|
+
const unsubscribe = addNetworkStateListener((state) => {
|
|
10144
|
+
console.log('Network changed:', state.isConnected, state.type);
|
|
10145
|
+
});
|
|
10146
|
+
|
|
10147
|
+
// Later:
|
|
10148
|
+
unsubscribe();
|
|
10149
|
+
\`\`\`
|
|
10150
|
+
|
|
10151
|
+
---
|
|
10152
|
+
|
|
10153
|
+
## fetchWithTimeout
|
|
10154
|
+
|
|
10155
|
+
Fetch wrapper that automatically aborts if the request exceeds a timeout.
|
|
10156
|
+
|
|
10157
|
+
\`\`\`tsx
|
|
10158
|
+
import { fetchWithTimeout } from '@idealyst/network';
|
|
10159
|
+
|
|
10160
|
+
const response = await fetchWithTimeout('https://api.example.com/data', {
|
|
10161
|
+
timeout: 5000, // 5 seconds (default: 10000)
|
|
10162
|
+
method: 'GET',
|
|
10163
|
+
headers: { 'Authorization': 'Bearer token' },
|
|
10164
|
+
});
|
|
10165
|
+
\`\`\`
|
|
10166
|
+
|
|
10167
|
+
### FetchWithTimeoutOptions
|
|
10168
|
+
|
|
10169
|
+
Extends standard \`RequestInit\` with:
|
|
10170
|
+
|
|
10171
|
+
| Option | Type | Default | Description |
|
|
10172
|
+
|--------|------|---------|-------------|
|
|
10173
|
+
| timeout | number | 10000 | Timeout in milliseconds |
|
|
10174
|
+
|
|
10175
|
+
---
|
|
10176
|
+
|
|
10177
|
+
## retry
|
|
10178
|
+
|
|
10179
|
+
Retry a function with exponential backoff. Optionally waits for network before retrying.
|
|
10180
|
+
|
|
10181
|
+
\`\`\`tsx
|
|
10182
|
+
import { retry } from '@idealyst/network';
|
|
10183
|
+
|
|
10184
|
+
const data = await retry(
|
|
10185
|
+
() => fetch('https://api.example.com/data').then(r => r.json()),
|
|
10186
|
+
{
|
|
10187
|
+
maxRetries: 3,
|
|
10188
|
+
baseDelay: 1000,
|
|
10189
|
+
maxDelay: 30000,
|
|
10190
|
+
retryOnlyWhenConnected: true,
|
|
10191
|
+
},
|
|
10192
|
+
);
|
|
10193
|
+
\`\`\`
|
|
10194
|
+
|
|
10195
|
+
### RetryOptions
|
|
10196
|
+
|
|
10197
|
+
| Option | Type | Default | Description |
|
|
10198
|
+
|--------|------|---------|-------------|
|
|
10199
|
+
| maxRetries | number | 3 | Maximum retry attempts |
|
|
10200
|
+
| baseDelay | number | 1000 | Base delay in ms (doubles each attempt) |
|
|
10201
|
+
| maxDelay | number | 30000 | Maximum delay cap in ms |
|
|
10202
|
+
| retryOnlyWhenConnected | boolean | true | Wait for network before retrying |
|
|
10203
|
+
|
|
10204
|
+
---
|
|
10205
|
+
|
|
10206
|
+
## waitForNetwork
|
|
10207
|
+
|
|
10208
|
+
Returns a promise that resolves when the device comes back online.
|
|
10209
|
+
|
|
10210
|
+
\`\`\`tsx
|
|
10211
|
+
import { waitForNetwork } from '@idealyst/network';
|
|
10212
|
+
|
|
10213
|
+
// Wait up to 30 seconds for connectivity
|
|
10214
|
+
await waitForNetwork({ timeout: 30000 });
|
|
10215
|
+
console.log('Back online!');
|
|
10216
|
+
\`\`\`
|
|
10217
|
+
|
|
10218
|
+
If already online, resolves immediately. Rejects with an error if the timeout is exceeded.
|
|
10219
|
+
|
|
10220
|
+
### WaitForNetworkOptions
|
|
10221
|
+
|
|
10222
|
+
| Option | Type | Default | Description |
|
|
10223
|
+
|--------|------|---------|-------------|
|
|
10224
|
+
| timeout | number | 30000 | Max wait time in ms before rejecting |
|
|
10225
|
+
|
|
10226
|
+
---
|
|
10227
|
+
|
|
10228
|
+
## NetworkState
|
|
10229
|
+
|
|
10230
|
+
Full network state object returned by the hook and listeners.
|
|
10231
|
+
|
|
10232
|
+
\`\`\`tsx
|
|
10233
|
+
interface NetworkState {
|
|
10234
|
+
isConnected: boolean; // Device has active network
|
|
10235
|
+
isInternetReachable: boolean | null; // Internet reachable (null = unknown)
|
|
10236
|
+
type: NetworkConnectionType; // 'wifi' | 'cellular' | 'ethernet' | ...
|
|
10237
|
+
effectiveType: EffectiveConnectionType; // 'slow-2g' | '2g' | '3g' | '4g' | 'unknown'
|
|
10238
|
+
cellularGeneration: CellularGeneration; // '2g' | '3g' | '4g' | '5g' | null (native only)
|
|
10239
|
+
downlink: number | null; // Mbps (web only, from Network Info API)
|
|
10240
|
+
rtt: number | null; // Round-trip time ms (web only)
|
|
10241
|
+
isDataSaving: boolean | null; // Data saver enabled (web only)
|
|
10242
|
+
}
|
|
10243
|
+
\`\`\`
|
|
10244
|
+
|
|
10245
|
+
## Type Aliases
|
|
10246
|
+
|
|
10247
|
+
\`\`\`tsx
|
|
10248
|
+
type NetworkConnectionType = 'wifi' | 'cellular' | 'ethernet' | 'bluetooth' | 'vpn' | 'other' | 'none' | 'unknown';
|
|
10249
|
+
type EffectiveConnectionType = 'slow-2g' | '2g' | '3g' | '4g' | 'unknown';
|
|
10250
|
+
type CellularGeneration = '2g' | '3g' | '4g' | '5g' | null;
|
|
10251
|
+
\`\`\`
|
|
10252
|
+
`,
|
|
10253
|
+
"idealyst://network/examples": `# Network Examples
|
|
10254
|
+
|
|
10255
|
+
Complete code examples for common @idealyst/network patterns.
|
|
10256
|
+
|
|
10257
|
+
## Offline Banner
|
|
10258
|
+
|
|
10259
|
+
\`\`\`tsx
|
|
10260
|
+
import { useNetwork } from '@idealyst/network';
|
|
10261
|
+
import { View, Text } from '@idealyst/components';
|
|
10262
|
+
|
|
10263
|
+
function OfflineBanner() {
|
|
10264
|
+
const { isConnected } = useNetwork();
|
|
10265
|
+
|
|
10266
|
+
if (isConnected) return null;
|
|
10267
|
+
|
|
10268
|
+
return (
|
|
10269
|
+
<View style={{ backgroundColor: '#f44336', padding: 8 }}>
|
|
10270
|
+
<Text style={{ color: '#fff', textAlign: 'center' }}>
|
|
10271
|
+
No internet connection
|
|
10272
|
+
</Text>
|
|
10273
|
+
</View>
|
|
10274
|
+
);
|
|
10275
|
+
}
|
|
10276
|
+
\`\`\`
|
|
10277
|
+
|
|
10278
|
+
## Adaptive Quality Based on Connection
|
|
10279
|
+
|
|
10280
|
+
\`\`\`tsx
|
|
10281
|
+
import { useNetwork } from '@idealyst/network';
|
|
10282
|
+
|
|
10283
|
+
function MediaPlayer({ videoId }: { videoId: string }) {
|
|
10284
|
+
const { effectiveType, isDataSaving } = useNetwork().state;
|
|
10285
|
+
|
|
10286
|
+
const quality = (() => {
|
|
10287
|
+
if (isDataSaving) return 'low';
|
|
10288
|
+
switch (effectiveType) {
|
|
10289
|
+
case 'slow-2g':
|
|
10290
|
+
case '2g':
|
|
10291
|
+
return 'low';
|
|
10292
|
+
case '3g':
|
|
10293
|
+
return 'medium';
|
|
10294
|
+
case '4g':
|
|
10295
|
+
default:
|
|
10296
|
+
return 'high';
|
|
10297
|
+
}
|
|
10298
|
+
})();
|
|
10299
|
+
|
|
10300
|
+
return <VideoPlayer videoId={videoId} quality={quality} />;
|
|
10301
|
+
}
|
|
10302
|
+
\`\`\`
|
|
10303
|
+
|
|
10304
|
+
## Retry API Calls
|
|
10305
|
+
|
|
10306
|
+
\`\`\`tsx
|
|
10307
|
+
import { retry, fetchWithTimeout } from '@idealyst/network';
|
|
10308
|
+
|
|
10309
|
+
async function fetchUserProfile(userId: string) {
|
|
10310
|
+
const response = await retry(
|
|
10311
|
+
() => fetchWithTimeout(\`https://api.example.com/users/\${userId}\`, {
|
|
10312
|
+
timeout: 5000,
|
|
10313
|
+
}),
|
|
10314
|
+
{ maxRetries: 3, baseDelay: 1000 },
|
|
10315
|
+
);
|
|
10316
|
+
|
|
10317
|
+
if (!response.ok) {
|
|
10318
|
+
throw new Error(\`Failed to fetch user: \${response.status}\`);
|
|
10319
|
+
}
|
|
10320
|
+
|
|
10321
|
+
return response.json();
|
|
10322
|
+
}
|
|
10323
|
+
\`\`\`
|
|
10324
|
+
|
|
10325
|
+
## Wait for Network Before Action
|
|
10326
|
+
|
|
10327
|
+
\`\`\`tsx
|
|
10328
|
+
import { waitForNetwork } from '@idealyst/network';
|
|
10329
|
+
|
|
10330
|
+
async function syncData() {
|
|
10331
|
+
// If offline, wait up to 60 seconds for connectivity
|
|
10332
|
+
await waitForNetwork({ timeout: 60000 });
|
|
10333
|
+
|
|
10334
|
+
// Now we're online \u2014 sync
|
|
10335
|
+
const response = await fetch('https://api.example.com/sync', {
|
|
10336
|
+
method: 'POST',
|
|
10337
|
+
body: JSON.stringify(pendingChanges),
|
|
10338
|
+
});
|
|
10339
|
+
|
|
10340
|
+
return response.json();
|
|
10341
|
+
}
|
|
10342
|
+
\`\`\`
|
|
10343
|
+
|
|
10344
|
+
## Network State Listener (Outside React)
|
|
10345
|
+
|
|
10346
|
+
\`\`\`tsx
|
|
10347
|
+
import { addNetworkStateListener } from '@idealyst/network';
|
|
10348
|
+
|
|
10349
|
+
// Subscribe to changes (e.g., in a service or module)
|
|
10350
|
+
const unsubscribe = addNetworkStateListener((state) => {
|
|
10351
|
+
if (!state.isConnected) {
|
|
10352
|
+
queueManager.pause();
|
|
10353
|
+
} else {
|
|
10354
|
+
queueManager.resume();
|
|
10355
|
+
}
|
|
10356
|
+
});
|
|
10357
|
+
|
|
10358
|
+
// Cleanup when done
|
|
10359
|
+
unsubscribe();
|
|
10360
|
+
\`\`\`
|
|
10361
|
+
|
|
10362
|
+
## Connection Type Display
|
|
10363
|
+
|
|
10364
|
+
\`\`\`tsx
|
|
10365
|
+
import { useNetwork } from '@idealyst/network';
|
|
10366
|
+
import { View, Text } from '@idealyst/components';
|
|
10367
|
+
|
|
10368
|
+
function NetworkInfo() {
|
|
10369
|
+
const { state } = useNetwork();
|
|
10370
|
+
|
|
10371
|
+
return (
|
|
10372
|
+
<View>
|
|
10373
|
+
<Text>Status: {state.isConnected ? 'Online' : 'Offline'}</Text>
|
|
10374
|
+
<Text>Type: {state.type}</Text>
|
|
10375
|
+
<Text>Speed: {state.effectiveType}</Text>
|
|
10376
|
+
{state.cellularGeneration && (
|
|
10377
|
+
<Text>Cellular: {state.cellularGeneration}</Text>
|
|
10378
|
+
)}
|
|
10379
|
+
{state.downlink != null && (
|
|
10380
|
+
<Text>Downlink: {state.downlink} Mbps</Text>
|
|
10381
|
+
)}
|
|
10382
|
+
{state.rtt != null && (
|
|
10383
|
+
<Text>RTT: {state.rtt} ms</Text>
|
|
10384
|
+
)}
|
|
10385
|
+
{state.isDataSaving != null && (
|
|
10386
|
+
<Text>Data Saver: {state.isDataSaving ? 'On' : 'Off'}</Text>
|
|
10387
|
+
)}
|
|
10388
|
+
</View>
|
|
10389
|
+
);
|
|
10390
|
+
}
|
|
10391
|
+
\`\`\`
|
|
10392
|
+
|
|
10393
|
+
## Fetch with Timeout
|
|
10394
|
+
|
|
10395
|
+
\`\`\`tsx
|
|
10396
|
+
import { fetchWithTimeout } from '@idealyst/network';
|
|
10397
|
+
|
|
10398
|
+
async function quickHealthCheck() {
|
|
10399
|
+
try {
|
|
10400
|
+
const response = await fetchWithTimeout('https://api.example.com/health', {
|
|
10401
|
+
timeout: 3000,
|
|
10402
|
+
method: 'HEAD',
|
|
10403
|
+
});
|
|
10404
|
+
return response.ok;
|
|
10405
|
+
} catch {
|
|
10406
|
+
return false;
|
|
10407
|
+
}
|
|
10408
|
+
}
|
|
10409
|
+
\`\`\`
|
|
10410
|
+
|
|
10411
|
+
## Best Practices
|
|
10412
|
+
|
|
10413
|
+
1. **Use \`useNetwork\` for UI** \u2014 The hook handles subscriptions and cleanup automatically
|
|
10414
|
+
2. **Use \`addNetworkStateListener\` for services** \u2014 For non-React code (queue managers, sync services)
|
|
10415
|
+
3. **Combine retry + fetchWithTimeout** \u2014 For resilient API calls
|
|
10416
|
+
4. **Check \`isInternetReachable\`** \u2014 Being "connected" to WiFi doesn't mean you have internet
|
|
10417
|
+
5. **Respect data saver** \u2014 Reduce payload sizes and avoid autoplay when \`isDataSaving\` is true
|
|
10418
|
+
6. **Don't poll too aggressively** \u2014 If using \`reachabilityPollInterval\`, keep it >= 15000ms
|
|
10419
|
+
7. **Use \`waitForNetwork\` for offline-first** \u2014 Queue operations and wait for connectivity to sync
|
|
10420
|
+
`
|
|
10421
|
+
};
|
|
10422
|
+
|
|
9966
10423
|
// src/data/packages.ts
|
|
9967
10424
|
var packages = {
|
|
9968
10425
|
components: {
|
|
@@ -11145,6 +11602,52 @@ await end(info.id, { dismissalPolicy: 'default' });`,
|
|
|
11145
11602
|
"deliveryActivity() / timerActivity() / mediaActivity() / progressActivity() - Template presets"
|
|
11146
11603
|
],
|
|
11147
11604
|
relatedPackages: ["notifications"]
|
|
11605
|
+
},
|
|
11606
|
+
network: {
|
|
11607
|
+
name: "Network",
|
|
11608
|
+
npmName: "@idealyst/network",
|
|
11609
|
+
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.",
|
|
11610
|
+
category: "utility",
|
|
11611
|
+
platforms: ["web", "native"],
|
|
11612
|
+
documentationStatus: "full",
|
|
11613
|
+
installation: "yarn add @idealyst/network",
|
|
11614
|
+
peerDependencies: [
|
|
11615
|
+
"@react-native-community/netinfo (native)"
|
|
11616
|
+
],
|
|
11617
|
+
features: [
|
|
11618
|
+
"useNetwork hook \u2014 real-time isConnected, connection type, effective speed",
|
|
11619
|
+
"Network state listener for non-React code",
|
|
11620
|
+
"Connection type detection \u2014 WiFi, cellular, ethernet, bluetooth, VPN",
|
|
11621
|
+
"Effective connection speed \u2014 slow-2g, 2g, 3g, 4g",
|
|
11622
|
+
"Cellular generation \u2014 2G, 3G, 4G, 5G (native)",
|
|
11623
|
+
"Internet reachability \u2014 distinguish connected from reachable",
|
|
11624
|
+
"fetchWithTimeout \u2014 fetch with configurable auto-abort",
|
|
11625
|
+
"retry \u2014 exponential backoff with network-aware gating",
|
|
11626
|
+
"waitForNetwork \u2014 promise that resolves when back online",
|
|
11627
|
+
"Data saver detection (web)",
|
|
11628
|
+
"Downlink speed and RTT estimation (web)"
|
|
11629
|
+
],
|
|
11630
|
+
quickStart: `import { useNetwork } from '@idealyst/network';
|
|
11631
|
+
|
|
11632
|
+
function App() {
|
|
11633
|
+
const { isConnected, type, effectiveType } = useNetwork();
|
|
11634
|
+
|
|
11635
|
+
if (!isConnected) {
|
|
11636
|
+
return <Text>You are offline</Text>;
|
|
11637
|
+
}
|
|
11638
|
+
|
|
11639
|
+
return <Text>Connected via {type} ({effectiveType})</Text>;
|
|
11640
|
+
}`,
|
|
11641
|
+
apiHighlights: [
|
|
11642
|
+
"useNetwork(options?) - React hook for real-time network state",
|
|
11643
|
+
"getNetworkState() - One-time state snapshot (sync on web, async on native)",
|
|
11644
|
+
"addNetworkStateListener(cb) - Subscribe to changes (returns unsubscribe)",
|
|
11645
|
+
"fetchWithTimeout(url, options) - Fetch with auto-abort timeout",
|
|
11646
|
+
"retry(fn, options) - Exponential backoff retry",
|
|
11647
|
+
"waitForNetwork(options) - Promise that resolves when online",
|
|
11648
|
+
"NetworkState { isConnected, isInternetReachable, type, effectiveType, cellularGeneration, downlink, rtt, isDataSaving }"
|
|
11649
|
+
],
|
|
11650
|
+
relatedPackages: ["storage", "config"]
|
|
11148
11651
|
}
|
|
11149
11652
|
};
|
|
11150
11653
|
function getPackagesByCategory() {
|
|
@@ -15563,16 +16066,23 @@ import { View, Text, Button, TextInput, Card, Icon, ... } from '@idealyst/compon
|
|
|
15563
16066
|
|
|
15564
16067
|
## How to Use This MCP Server
|
|
15565
16068
|
|
|
15566
|
-
This server has tools for every aspect of the framework. **
|
|
16069
|
+
This server has tools for every aspect of the framework. **Look up APIs as you need them \u2014 don't research everything upfront.**
|
|
15567
16070
|
|
|
15568
|
-
### Workflow
|
|
16071
|
+
### Workflow
|
|
15569
16072
|
|
|
15570
|
-
|
|
15571
|
-
|
|
15572
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
15575
|
-
|
|
16073
|
+
Write code iteratively \u2014 **look up one API, write one file, repeat**:
|
|
16074
|
+
|
|
16075
|
+
1. Call \`get_intro\` (this response)
|
|
16076
|
+
2. Look up the API for your FIRST file only (e.g., \`get_component_types("Button,Card,Text")\`)
|
|
16077
|
+
3. **Write that file immediately** using the Write tool
|
|
16078
|
+
4. Then look up the next API you need, write the next file, and so on
|
|
16079
|
+
|
|
16080
|
+
- **Before using a component** \u2014 call \`get_component_types\` for its props (supports batching: \`get_component_types("Button,Card,Text")\`)
|
|
16081
|
+
- **Before using a package** \u2014 call its dedicated \`get_*_guide\` tool with topic \`api\`
|
|
16082
|
+
- **Search icons once** \u2014 batch all terms into one call: \`search_icons("home settings check timer")\`
|
|
16083
|
+
- **Check recipes** \u2014 \`search_recipes\` for ready-made patterns you can adapt
|
|
16084
|
+
|
|
16085
|
+
> **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**.
|
|
15576
16086
|
|
|
15577
16087
|
### Component Tools
|
|
15578
16088
|
- \`list_components\` / \`search_components\` \u2014 Discover available components
|
|
@@ -15582,7 +16092,7 @@ This server has tools for every aspect of the framework. **Always look up the AP
|
|
|
15582
16092
|
- \`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
|
|
15583
16093
|
|
|
15584
16094
|
### Package Guide Tools
|
|
15585
|
-
|
|
16095
|
+
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.
|
|
15586
16096
|
|
|
15587
16097
|
| Package | Guide Tool |
|
|
15588
16098
|
|---------|-----------|
|
|
@@ -15647,6 +16157,10 @@ const tabs: { icon: IconName }[] = [{ icon: 'home' }, { icon: 'search' }]; // NO
|
|
|
15647
16157
|
|
|
15648
16158
|
> **CRITICAL:** Icon names do NOT have an \`mdi:\` prefix. Use bare names like \`'delete'\`, \`'home'\`, \`'check-circle'\` \u2014 NOT \`'mdi:delete'\`, \`'mdi:home'\`, etc.
|
|
15649
16159
|
> **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.
|
|
16160
|
+
> **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\`.
|
|
16161
|
+
> **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.
|
|
16162
|
+
|
|
16163
|
+
**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.
|
|
15650
16164
|
|
|
15651
16165
|
---
|
|
15652
16166
|
|
|
@@ -15656,26 +16170,27 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
|
|
|
15656
16170
|
|
|
15657
16171
|
### Component Props
|
|
15658
16172
|
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"\`.
|
|
15659
|
-
2. **TextInput** does NOT have \`label\`, \`error\`, \`editable\`, \`autoComplete\`, \`keyboardType\`, or \`onChange\`. Compose labels/errors with \`Text\` + \`View\`. Use \`onChangeText\`, \`inputMode\` (\`text\`, \`
|
|
16173
|
+
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.
|
|
15660
16174
|
3. **Button/IconButton** \`type\` is \`'contained' | 'outlined' | 'text'\` \u2014 NOT \`'ghost'\`, \`'solid'\`, \`'default'\`. Button has \`leftIcon\` and \`rightIcon\` \u2014 NOT \`icon\`.
|
|
15661
16175
|
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'\`.
|
|
15662
|
-
5. **Badge** \`type\` is \`'filled' | 'outlined' | 'dot'\` \u2014 NOT \`'soft'\`, \`'subtle'\`, \`'solid'\`.
|
|
16176
|
+
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.
|
|
15663
16177
|
6. **Avatar** uses \`src\` for image URL, \`fallback\` for initials \u2014 NOT \`name\`, \`initials\`, \`label\`. Shape: \`'circle' | 'square'\`.
|
|
15664
16178
|
7. **Link** requires a \`to\` prop (path string) \u2014 it's a navigation link, NOT pressable text.
|
|
15665
16179
|
8. **List** takes \`children\` \u2014 NOT \`data\`, \`renderItem\`, or \`keyExtractor\`. Map your data: \`<List>{items.map(item => <View key={item.id}>...</View>)}</List>\`
|
|
15666
16180
|
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\`.
|
|
15667
16181
|
10. The component is **TextInput**, NOT \`Input\`.
|
|
15668
|
-
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>\`.
|
|
16182
|
+
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"\`.
|
|
16183
|
+
12. **Switch** uses \`checked\` and \`onChange\` \u2014 NOT \`value\` and \`onValueChange\` (React Native convention). Also has \`label\`, \`labelPosition\`, \`disabled\`.
|
|
15669
16184
|
|
|
15670
16185
|
### Navigation
|
|
15671
|
-
11.
|
|
16186
|
+
11. **NavigatorProvider** takes a \`route\` prop (SINGULAR) \u2014 NOT \`routes\`: \`<NavigatorProvider route={routeConfig} />\`. There is NO \`Router\` export.
|
|
15672
16187
|
12. Use \`useNavigator()\` \u2014 NOT \`useNavigate()\`.
|
|
15673
|
-
13. \`navigate()\` takes an **object**: \`navigate({ path: '/settings' })\` \u2014 NOT \`navigate('/settings')\` or \`navigate('routeName', params)\`.
|
|
16188
|
+
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).
|
|
15674
16189
|
14. \`useNavigationState\` returns \`Record<string, unknown>\` by default. Always provide a type parameter: \`useNavigationState<{ title?: string }>()\`.
|
|
15675
16190
|
15. \`useParams()\` does NOT accept generic type arguments. It returns \`Record<string, string>\`. Do NOT write \`useParams<{ id: string }>()\` \u2014 that causes TS2558.
|
|
15676
16191
|
|
|
15677
16192
|
### Imports & Styling
|
|
15678
|
-
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\`).
|
|
16193
|
+
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.
|
|
15679
16194
|
17. **Never** import from \`react-native-unistyles\` \u2014 use \`@idealyst/theme\` (\`configureThemes\`, \`ThemeSettings\`, \`useTheme\`).
|
|
15680
16195
|
18. **useTheme()** returns the Theme object **directly** (NOT wrapped): \`const theme = useTheme();\`. Do NOT destructure: \`const { theme } = useTheme()\` \u2014 causes TS2339.
|
|
15681
16196
|
19. **Spacing & Layout**: Use component shorthand props for spacing \u2014 NOT \`theme.spacing\` (which does NOT exist). The correct patterns:
|
|
@@ -15686,14 +16201,32 @@ These are mistakes agents make repeatedly. Each one causes TypeScript compilatio
|
|
|
15686
16201
|
- \`theme.radii.md\` \u2014 border radius values (this DOES exist)
|
|
15687
16202
|
- **WRONG**: \`<View paddingTop="md">\` \u2014 does NOT exist as a shorthand prop; use \`style={{ paddingTop: 16 }}\`
|
|
15688
16203
|
- **WRONG**: \`theme.spacing.md\` \u2014 does NOT exist, causes TS2339
|
|
16204
|
+
- **WRONG**: \`theme.colors.background\` \u2014 does NOT exist. \`theme.colors\` has: \`pallet\`, \`surface\`, \`text\`, \`border\`. Use \`theme.colors.surface.primary\` for background colors.
|
|
15689
16205
|
- **WRONG**: \`theme.colors.intent.danger\` \u2014 does NOT exist; intents are at \`theme.intents.danger\`
|
|
16206
|
+
- **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\`
|
|
16207
|
+
- **CORRECT**: \`theme.intents.primary.primary\` for the color, \`theme.intents.primary.contrast\` for text on that color
|
|
16208
|
+
- **Color key reference** (all keys available):
|
|
16209
|
+
- \`theme.colors.surface\`: \`screen\`, \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
|
|
16210
|
+
- \`theme.colors.text\`: \`primary\`, \`secondary\`, \`tertiary\`, \`inverse\`, \`inverse-secondary\`, \`inverse-tertiary\`
|
|
16211
|
+
- \`theme.colors.border\`: \`primary\`, \`secondary\`, \`tertiary\`, \`disabled\`
|
|
16212
|
+
- \`theme.intents\`: \`primary\`, \`secondary\`, \`success\`, \`warning\`, \`danger\`, \`info\`, \`neutral\` (each has \`.primary\`, \`.contrast\`, \`.light\`, \`.dark\`)
|
|
15690
16213
|
|
|
15691
16214
|
### Cross-Platform (CRITICAL)
|
|
15692
16215
|
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.
|
|
15693
|
-
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).
|
|
16216
|
+
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.
|
|
16217
|
+
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.
|
|
16218
|
+
22. **Platform file barrel exports**: When creating \`Component.web.tsx\` + \`Component.native.tsx\`, you need THREE index files:
|
|
16219
|
+
- \`index.web.ts\` \u2014 \`export { default as Component } from './Component.web';\`
|
|
16220
|
+
- \`index.native.ts\` \u2014 \`export { default as Component } from './Component.native';\`
|
|
16221
|
+
- \`index.ts\` \u2014 \`export { default as Component } from './Component.web';\` (base file for TypeScript resolution)
|
|
16222
|
+
- **WRONG**: \`export { default } from './Component';\` \u2014 no bare \`Component.ts\` exists, only \`.web.tsx\`/\`.native.tsx\`. This causes TS2307.
|
|
16223
|
+
- **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.
|
|
15694
16224
|
|
|
15695
16225
|
### React 19 TypeScript
|
|
15696
16226
|
- **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[]>([])\`.
|
|
16227
|
+
- **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.
|
|
16228
|
+
- **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.
|
|
16229
|
+
- **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).
|
|
15697
16230
|
|
|
15698
16231
|
### Scaffolded Project Layout
|
|
15699
16232
|
When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`idealyst create\`), files go in specific locations:
|
|
@@ -15710,11 +16243,11 @@ When working in a CLI-scaffolded workspace (created with \`idealyst init\` + \`i
|
|
|
15710
16243
|
> **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.
|
|
15711
16244
|
|
|
15712
16245
|
### Package-Specific (call guide tools for full API)
|
|
15713
|
-
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\`.
|
|
16246
|
+
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\`.
|
|
15714
16247
|
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\`.
|
|
15715
|
-
20. **Files** (\`@idealyst/files\`):
|
|
16248
|
+
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\`.
|
|
15716
16249
|
21. **Storage** (\`@idealyst/storage\`): Methods are \`getItem()\`/\`setItem()\`, NOT \`get()\`/\`set()\`. Values are string-only. Call \`get_storage_guide\` topic \`api\`.
|
|
15717
|
-
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\`.
|
|
16250
|
+
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\`.
|
|
15718
16251
|
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\`.
|
|
15719
16252
|
`;
|
|
15720
16253
|
|
|
@@ -23180,7 +23713,7 @@ import { fileURLToPath } from "url";
|
|
|
23180
23713
|
// src/generated/types.json
|
|
23181
23714
|
var types_default = {
|
|
23182
23715
|
version: "1.0.93",
|
|
23183
|
-
extractedAt: "2026-02-
|
|
23716
|
+
extractedAt: "2026-02-24T21:11:17.058Z",
|
|
23184
23717
|
components: {
|
|
23185
23718
|
Accordion: {
|
|
23186
23719
|
name: "Accordion",
|
|
@@ -45481,7 +46014,7 @@ var types_default = {
|
|
|
45481
46014
|
navigation: {
|
|
45482
46015
|
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',
|
|
45483
46016
|
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}",
|
|
45484
|
-
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;",
|
|
46017
|
+
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;",
|
|
45485
46018
|
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}",
|
|
45486
46019
|
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}",
|
|
45487
46020
|
TabNavigatorParam: "export type TabNavigatorParam = {\n layout: 'tab'\n routes: RouteParam<TabBarScreenOptions>[]\n layoutComponent?: TabLayoutComponent\n} & BaseNavigatorParam",
|
|
@@ -65124,12 +65657,32 @@ function postProcessComponentTypes(componentName, result) {
|
|
|
65124
65657
|
}
|
|
65125
65658
|
if (componentName.toLowerCase() === "card") {
|
|
65126
65659
|
if (typeof result === "object" && result !== null) {
|
|
65127
|
-
result.usageNote =
|
|
65660
|
+
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 }}.";
|
|
65128
65661
|
}
|
|
65129
65662
|
}
|
|
65130
65663
|
if (componentName.toLowerCase() === "view") {
|
|
65131
65664
|
if (typeof result === "object" && result !== null) {
|
|
65132
|
-
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 }}.";
|
|
65665
|
+
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.";
|
|
65666
|
+
}
|
|
65667
|
+
}
|
|
65668
|
+
if (componentName.toLowerCase() === "textarea") {
|
|
65669
|
+
if (typeof result === "object" && result !== null) {
|
|
65670
|
+
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.";
|
|
65671
|
+
}
|
|
65672
|
+
}
|
|
65673
|
+
if (componentName.toLowerCase() === "image") {
|
|
65674
|
+
if (typeof result === "object" && result !== null) {
|
|
65675
|
+
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} />";
|
|
65676
|
+
}
|
|
65677
|
+
}
|
|
65678
|
+
if (componentName.toLowerCase() === "icon") {
|
|
65679
|
+
if (typeof result === "object" && result !== null) {
|
|
65680
|
+
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`.";
|
|
65681
|
+
}
|
|
65682
|
+
}
|
|
65683
|
+
if (componentName.toLowerCase() === "badge") {
|
|
65684
|
+
if (typeof result === "object" && result !== null) {
|
|
65685
|
+
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.";
|
|
65133
65686
|
}
|
|
65134
65687
|
}
|
|
65135
65688
|
return result;
|
|
@@ -65387,8 +65940,8 @@ function getThemeTypes2(args = {}) {
|
|
|
65387
65940
|
const result = getThemeTypes(format);
|
|
65388
65941
|
if (typeof result === "object" && result !== null) {
|
|
65389
65942
|
const r = result;
|
|
65390
|
-
r.useThemeNote =
|
|
65391
|
-
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
|
|
65943
|
+
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.";
|
|
65944
|
+
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```";
|
|
65392
65945
|
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).";
|
|
65393
65946
|
}
|
|
65394
65947
|
return jsonResponse(result);
|
|
@@ -65404,6 +65957,8 @@ function getNavigationTypes2(args = {}) {
|
|
|
65404
65957
|
const result = getNavigationTypes(format);
|
|
65405
65958
|
if (typeof result === "object" && result !== null) {
|
|
65406
65959
|
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.`;
|
|
65960
|
+
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.";
|
|
65961
|
+
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```";
|
|
65407
65962
|
}
|
|
65408
65963
|
return jsonResponse(result);
|
|
65409
65964
|
} catch (error) {
|
|
@@ -65613,6 +66168,17 @@ function getLiveActivityGuide(args) {
|
|
|
65613
66168
|
}
|
|
65614
66169
|
return textResponse(guide);
|
|
65615
66170
|
}
|
|
66171
|
+
function getNetworkGuide(args) {
|
|
66172
|
+
const topic = args.topic;
|
|
66173
|
+
const uri = `idealyst://network/${topic}`;
|
|
66174
|
+
const guide = networkGuides[uri];
|
|
66175
|
+
if (!guide) {
|
|
66176
|
+
return textResponse(
|
|
66177
|
+
`Topic "${topic}" not found. Available topics: overview, api, examples`
|
|
66178
|
+
);
|
|
66179
|
+
}
|
|
66180
|
+
return textResponse(guide);
|
|
66181
|
+
}
|
|
65616
66182
|
function listPackages(args = {}) {
|
|
65617
66183
|
const category = args.category;
|
|
65618
66184
|
if (category) {
|
|
@@ -65863,6 +66429,7 @@ var toolHandlers = {
|
|
|
65863
66429
|
get_payments_guide: getPaymentsGuide,
|
|
65864
66430
|
get_notifications_guide: getNotificationsGuide,
|
|
65865
66431
|
get_live_activity_guide: getLiveActivityGuide,
|
|
66432
|
+
get_network_guide: getNetworkGuide,
|
|
65866
66433
|
list_packages: listPackages,
|
|
65867
66434
|
get_package_docs: getPackageDocs,
|
|
65868
66435
|
search_packages: searchPackages2,
|
|
@@ -65921,6 +66488,8 @@ export {
|
|
|
65921
66488
|
searchRecipesDefinition,
|
|
65922
66489
|
getInstallGuideDefinition,
|
|
65923
66490
|
getIntroDefinition,
|
|
66491
|
+
getLiveActivityGuideDefinition,
|
|
66492
|
+
getNetworkGuideDefinition,
|
|
65924
66493
|
toolDefinitions,
|
|
65925
66494
|
toolDefinitionMap,
|
|
65926
66495
|
getAvailableComponents,
|
|
@@ -65953,6 +66522,8 @@ export {
|
|
|
65953
66522
|
getBiometricsGuide,
|
|
65954
66523
|
getPaymentsGuide,
|
|
65955
66524
|
getNotificationsGuide,
|
|
66525
|
+
getLiveActivityGuide,
|
|
66526
|
+
getNetworkGuide,
|
|
65956
66527
|
listPackages,
|
|
65957
66528
|
getPackageDocs,
|
|
65958
66529
|
searchPackages2 as searchPackages,
|
|
@@ -65964,4 +66535,4 @@ export {
|
|
|
65964
66535
|
toolHandlers,
|
|
65965
66536
|
callTool
|
|
65966
66537
|
};
|
|
65967
|
-
//# sourceMappingURL=chunk-
|
|
66538
|
+
//# sourceMappingURL=chunk-UD56HXN3.js.map
|