@mobileai/react-native 0.9.17 → 0.9.18
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/package.json +2 -5
- package/lib/module/__cli_tmp__.js.map +0 -1
- package/lib/module/components/AIAgent.js.map +0 -1
- package/lib/module/components/AIZone.js.map +0 -1
- package/lib/module/components/AgentChatBar.js.map +0 -1
- package/lib/module/components/AgentErrorBoundary.js.map +0 -1
- package/lib/module/components/AgentOverlay.js.map +0 -1
- package/lib/module/components/DiscoveryTooltip.js.map +0 -1
- package/lib/module/components/HighlightOverlay.js.map +0 -1
- package/lib/module/components/Icons.js.map +0 -1
- package/lib/module/components/ProactiveHint.js.map +0 -1
- package/lib/module/components/cards/InfoCard.js.map +0 -1
- package/lib/module/components/cards/ReviewSummary.js.map +0 -1
- package/lib/module/config/endpoints.js.map +0 -1
- package/lib/module/core/ActionRegistry.js.map +0 -1
- package/lib/module/core/AgentRuntime.js.map +0 -1
- package/lib/module/core/FiberTreeWalker.js.map +0 -1
- package/lib/module/core/IdleDetector.js.map +0 -1
- package/lib/module/core/MCPBridge.js.map +0 -1
- package/lib/module/core/ScreenDehydrator.js.map +0 -1
- package/lib/module/core/ZoneRegistry.js.map +0 -1
- package/lib/module/core/systemPrompt.js.map +0 -1
- package/lib/module/core/types.js.map +0 -1
- package/lib/module/hooks/useAction.js.map +0 -1
- package/lib/module/index.js.map +0 -1
- package/lib/module/plugin/withAppIntents.js.map +0 -1
- package/lib/module/providers/GeminiProvider.js.map +0 -1
- package/lib/module/providers/OpenAIProvider.js.map +0 -1
- package/lib/module/providers/ProviderFactory.js.map +0 -1
- package/lib/module/services/AudioInputService.js.map +0 -1
- package/lib/module/services/AudioOutputService.js.map +0 -1
- package/lib/module/services/KnowledgeBaseService.js.map +0 -1
- package/lib/module/services/VoiceService.js.map +0 -1
- package/lib/module/services/flags/FlagService.js.map +0 -1
- package/lib/module/services/telemetry/MobileAI.js.map +0 -1
- package/lib/module/services/telemetry/PiiScrubber.js.map +0 -1
- package/lib/module/services/telemetry/TelemetryService.js.map +0 -1
- package/lib/module/services/telemetry/TouchAutoCapture.js.map +0 -1
- package/lib/module/services/telemetry/device.js.map +0 -1
- package/lib/module/services/telemetry/deviceMetadata.js.map +0 -1
- package/lib/module/services/telemetry/index.js.map +0 -1
- package/lib/module/services/telemetry/types.js.map +0 -1
- package/lib/module/support/CSATSurvey.js.map +0 -1
- package/lib/module/support/EscalationEventSource.js.map +0 -1
- package/lib/module/support/EscalationSocket.js.map +0 -1
- package/lib/module/support/SupportChatModal.js.map +0 -1
- package/lib/module/support/SupportGreeting.js.map +0 -1
- package/lib/module/support/TicketStore.js.map +0 -1
- package/lib/module/support/escalateTool.js.map +0 -1
- package/lib/module/support/index.js.map +0 -1
- package/lib/module/support/supportPrompt.js.map +0 -1
- package/lib/module/support/types.js.map +0 -1
- package/lib/module/tools/datePickerTool.js.map +0 -1
- package/lib/module/tools/guideTool.js.map +0 -1
- package/lib/module/tools/index.js.map +0 -1
- package/lib/module/tools/keyboardTool.js.map +0 -1
- package/lib/module/tools/longPressTool.js.map +0 -1
- package/lib/module/tools/pickerTool.js.map +0 -1
- package/lib/module/tools/restoreTool.js.map +0 -1
- package/lib/module/tools/scrollTool.js.map +0 -1
- package/lib/module/tools/simplifyTool.js.map +0 -1
- package/lib/module/tools/sliderTool.js.map +0 -1
- package/lib/module/tools/tapTool.js.map +0 -1
- package/lib/module/tools/typeTool.js.map +0 -1
- package/lib/module/tools/types.js.map +0 -1
- package/lib/module/types/jsx.d.js.map +0 -1
- package/lib/module/utils/audioUtils.js.map +0 -1
- package/lib/module/utils/logger.js.map +0 -1
- package/lib/typescript/babel.config.d.ts.map +0 -1
- package/lib/typescript/bin/generate-map.d.cts.map +0 -1
- package/lib/typescript/eslint.config.d.mts.map +0 -1
- package/lib/typescript/generate-map.d.ts.map +0 -1
- package/lib/typescript/src/__cli_tmp__.d.ts.map +0 -1
- package/lib/typescript/src/components/AIAgent.d.ts.map +0 -1
- package/lib/typescript/src/components/AIZone.d.ts.map +0 -1
- package/lib/typescript/src/components/AgentChatBar.d.ts.map +0 -1
- package/lib/typescript/src/components/AgentErrorBoundary.d.ts.map +0 -1
- package/lib/typescript/src/components/AgentOverlay.d.ts.map +0 -1
- package/lib/typescript/src/components/DiscoveryTooltip.d.ts.map +0 -1
- package/lib/typescript/src/components/HighlightOverlay.d.ts.map +0 -1
- package/lib/typescript/src/components/Icons.d.ts.map +0 -1
- package/lib/typescript/src/components/ProactiveHint.d.ts.map +0 -1
- package/lib/typescript/src/components/cards/InfoCard.d.ts.map +0 -1
- package/lib/typescript/src/components/cards/ReviewSummary.d.ts.map +0 -1
- package/lib/typescript/src/config/endpoints.d.ts.map +0 -1
- package/lib/typescript/src/core/ActionRegistry.d.ts.map +0 -1
- package/lib/typescript/src/core/AgentRuntime.d.ts.map +0 -1
- package/lib/typescript/src/core/FiberTreeWalker.d.ts.map +0 -1
- package/lib/typescript/src/core/IdleDetector.d.ts.map +0 -1
- package/lib/typescript/src/core/MCPBridge.d.ts.map +0 -1
- package/lib/typescript/src/core/ScreenDehydrator.d.ts.map +0 -1
- package/lib/typescript/src/core/ZoneRegistry.d.ts.map +0 -1
- package/lib/typescript/src/core/systemPrompt.d.ts.map +0 -1
- package/lib/typescript/src/core/types.d.ts.map +0 -1
- package/lib/typescript/src/hooks/useAction.d.ts.map +0 -1
- package/lib/typescript/src/index.d.ts.map +0 -1
- package/lib/typescript/src/plugin/withAppIntents.d.ts.map +0 -1
- package/lib/typescript/src/providers/GeminiProvider.d.ts.map +0 -1
- package/lib/typescript/src/providers/OpenAIProvider.d.ts.map +0 -1
- package/lib/typescript/src/providers/ProviderFactory.d.ts.map +0 -1
- package/lib/typescript/src/services/AudioInputService.d.ts.map +0 -1
- package/lib/typescript/src/services/AudioOutputService.d.ts.map +0 -1
- package/lib/typescript/src/services/KnowledgeBaseService.d.ts.map +0 -1
- package/lib/typescript/src/services/VoiceService.d.ts.map +0 -1
- package/lib/typescript/src/services/flags/FlagService.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/MobileAI.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/PiiScrubber.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/TelemetryService.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/TouchAutoCapture.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/device.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/deviceMetadata.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/index.d.ts.map +0 -1
- package/lib/typescript/src/services/telemetry/types.d.ts.map +0 -1
- package/lib/typescript/src/support/CSATSurvey.d.ts.map +0 -1
- package/lib/typescript/src/support/EscalationEventSource.d.ts.map +0 -1
- package/lib/typescript/src/support/EscalationSocket.d.ts.map +0 -1
- package/lib/typescript/src/support/SupportChatModal.d.ts.map +0 -1
- package/lib/typescript/src/support/SupportGreeting.d.ts.map +0 -1
- package/lib/typescript/src/support/TicketStore.d.ts.map +0 -1
- package/lib/typescript/src/support/escalateTool.d.ts.map +0 -1
- package/lib/typescript/src/support/index.d.ts.map +0 -1
- package/lib/typescript/src/support/supportPrompt.d.ts.map +0 -1
- package/lib/typescript/src/support/types.d.ts.map +0 -1
- package/lib/typescript/src/tools/datePickerTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/guideTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/index.d.ts.map +0 -1
- package/lib/typescript/src/tools/keyboardTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/longPressTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/pickerTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/restoreTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/scrollTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/simplifyTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/sliderTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/tapTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/typeTool.d.ts.map +0 -1
- package/lib/typescript/src/tools/types.d.ts.map +0 -1
- package/lib/typescript/src/utils/audioUtils.d.ts.map +0 -1
- package/lib/typescript/src/utils/logger.d.ts.map +0 -1
- package/src/__cli_tmp__.tsx +0 -9
- package/src/cli/analyzers/chain-analyzer.ts +0 -183
- package/src/cli/extractors/ai-extractor.ts +0 -6
- package/src/cli/extractors/ast-extractor.ts +0 -551
- package/src/cli/generate-intents.ts +0 -140
- package/src/cli/generate-map.ts +0 -121
- package/src/cli/generate-swift.ts +0 -116
- package/src/cli/scanners/expo-scanner.ts +0 -203
- package/src/cli/scanners/rn-scanner.ts +0 -445
- package/src/components/AIAgent.tsx +0 -1716
- package/src/components/AIZone.tsx +0 -147
- package/src/components/AgentChatBar.tsx +0 -1143
- package/src/components/AgentErrorBoundary.tsx +0 -78
- package/src/components/AgentOverlay.tsx +0 -73
- package/src/components/DiscoveryTooltip.tsx +0 -148
- package/src/components/HighlightOverlay.tsx +0 -136
- package/src/components/Icons.tsx +0 -253
- package/src/components/ProactiveHint.tsx +0 -145
- package/src/components/cards/InfoCard.tsx +0 -58
- package/src/components/cards/ReviewSummary.tsx +0 -76
- package/src/config/endpoints.ts +0 -22
- package/src/core/ActionRegistry.ts +0 -105
- package/src/core/AgentRuntime.ts +0 -1471
- package/src/core/FiberTreeWalker.ts +0 -930
- package/src/core/IdleDetector.ts +0 -72
- package/src/core/MCPBridge.ts +0 -163
- package/src/core/ScreenDehydrator.ts +0 -53
- package/src/core/ZoneRegistry.ts +0 -44
- package/src/core/systemPrompt.ts +0 -431
- package/src/core/types.ts +0 -521
- package/src/hooks/useAction.ts +0 -182
- package/src/index.ts +0 -83
- package/src/plugin/withAppIntents.ts +0 -98
- package/src/providers/GeminiProvider.ts +0 -357
- package/src/providers/OpenAIProvider.ts +0 -379
- package/src/providers/ProviderFactory.ts +0 -36
- package/src/services/AudioInputService.ts +0 -226
- package/src/services/AudioOutputService.ts +0 -236
- package/src/services/KnowledgeBaseService.ts +0 -156
- package/src/services/VoiceService.ts +0 -451
- package/src/services/flags/FlagService.ts +0 -137
- package/src/services/telemetry/MobileAI.ts +0 -66
- package/src/services/telemetry/PiiScrubber.ts +0 -17
- package/src/services/telemetry/TelemetryService.ts +0 -323
- package/src/services/telemetry/TouchAutoCapture.ts +0 -165
- package/src/services/telemetry/device.ts +0 -93
- package/src/services/telemetry/deviceMetadata.ts +0 -13
- package/src/services/telemetry/index.ts +0 -13
- package/src/services/telemetry/types.ts +0 -75
- package/src/support/CSATSurvey.tsx +0 -304
- package/src/support/EscalationEventSource.ts +0 -190
- package/src/support/EscalationSocket.ts +0 -152
- package/src/support/SupportChatModal.tsx +0 -563
- package/src/support/SupportGreeting.tsx +0 -161
- package/src/support/TicketStore.ts +0 -100
- package/src/support/escalateTool.ts +0 -174
- package/src/support/index.ts +0 -29
- package/src/support/supportPrompt.ts +0 -55
- package/src/support/types.ts +0 -155
- package/src/tools/datePickerTool.ts +0 -60
- package/src/tools/guideTool.ts +0 -76
- package/src/tools/index.ts +0 -20
- package/src/tools/keyboardTool.ts +0 -30
- package/src/tools/longPressTool.ts +0 -61
- package/src/tools/pickerTool.ts +0 -115
- package/src/tools/restoreTool.ts +0 -33
- package/src/tools/scrollTool.ts +0 -156
- package/src/tools/simplifyTool.ts +0 -33
- package/src/tools/sliderTool.ts +0 -65
- package/src/tools/tapTool.ts +0 -93
- package/src/tools/typeTool.ts +0 -113
- package/src/tools/types.ts +0 -58
- package/src/types/jsx.d.ts +0 -20
- package/src/utils/audioUtils.ts +0 -54
- package/src/utils/logger.ts +0 -38
package/src/cli/generate-map.ts
DELETED
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Screen Map Generator CLI
|
|
5
|
-
*
|
|
6
|
-
* Scans React Native screen files and generates a JSON map
|
|
7
|
-
* that the AI agent uses for intelligent navigation.
|
|
8
|
-
*
|
|
9
|
-
* Pure AST — no LLM calls, no API key needed. Runs in ~2 seconds.
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* npx react-native-ai-agent generate-map
|
|
13
|
-
* npx react-native-ai-agent generate-map --watch
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as fs from 'fs';
|
|
17
|
-
import * as path from 'path';
|
|
18
|
-
import { scanExpoRouterApp } from './scanners/expo-scanner';
|
|
19
|
-
import { scanReactNavigationApp } from './scanners/rn-scanner';
|
|
20
|
-
|
|
21
|
-
interface ScreenMapEntry {
|
|
22
|
-
title?: string;
|
|
23
|
-
description: string;
|
|
24
|
-
navigatesTo?: string[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface ScreenMap {
|
|
28
|
-
generatedAt: string;
|
|
29
|
-
framework: 'expo-router' | 'react-navigation';
|
|
30
|
-
screens: Record<string, ScreenMapEntry>;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function main() {
|
|
34
|
-
const args = parseArgs(process.argv.slice(2));
|
|
35
|
-
const projectRoot = args.dir || process.cwd();
|
|
36
|
-
|
|
37
|
-
console.log('🗺️ Screen Map Generator');
|
|
38
|
-
console.log('━'.repeat(40));
|
|
39
|
-
|
|
40
|
-
// Detect framework
|
|
41
|
-
const framework = detectFramework(projectRoot);
|
|
42
|
-
console.log(`📱 Framework: ${framework}`);
|
|
43
|
-
|
|
44
|
-
// Scan screens
|
|
45
|
-
const scannedScreens = framework === 'expo-router'
|
|
46
|
-
? scanExpoRouterApp(projectRoot)
|
|
47
|
-
: scanReactNavigationApp(projectRoot);
|
|
48
|
-
|
|
49
|
-
console.log(`📄 Found ${scannedScreens.length} screen(s)`);
|
|
50
|
-
|
|
51
|
-
// Build output — per-screen navigatesTo, filtered to known routes only
|
|
52
|
-
const allRouteSet = new Set(scannedScreens.map(s => s.routeName));
|
|
53
|
-
const screenMap: ScreenMap = {
|
|
54
|
-
generatedAt: new Date().toISOString(),
|
|
55
|
-
framework,
|
|
56
|
-
screens: {},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
for (const screen of scannedScreens) {
|
|
60
|
-
const validLinks = screen.navigationLinks.filter(link => allRouteSet.has(link));
|
|
61
|
-
if (screen.navigationLinks.length > 0) {
|
|
62
|
-
const noise = screen.navigationLinks.length - validLinks.length;
|
|
63
|
-
console.log(` 🔗 ${screen.routeName} → ${validLinks.join(', ')}${noise > 0 ? ` (${noise} noise filtered)` : ''}`);
|
|
64
|
-
}
|
|
65
|
-
screenMap.screens[screen.routeName] = {
|
|
66
|
-
title: screen.title || undefined,
|
|
67
|
-
description: screen.description,
|
|
68
|
-
navigatesTo: validLinks.length > 0 ? validLinks : undefined,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Write output
|
|
73
|
-
const outputPath = path.join(projectRoot, 'ai-screen-map.json');
|
|
74
|
-
fs.writeFileSync(outputPath, JSON.stringify(screenMap, null, 2));
|
|
75
|
-
|
|
76
|
-
const linkedCount = scannedScreens.filter(s => s.navigationLinks.some(l => allRouteSet.has(l))).length;
|
|
77
|
-
console.log('━'.repeat(40));
|
|
78
|
-
console.log(`✅ Generated ${outputPath}`);
|
|
79
|
-
console.log(` ${Object.keys(screenMap.screens).length} screens, ${linkedCount} with navigation links`);
|
|
80
|
-
|
|
81
|
-
console.log('\n💡 Import in your app:');
|
|
82
|
-
console.log(' import screenMap from \'./ai-screen-map.json\';');
|
|
83
|
-
console.log(' <AIAgent screenMap={screenMap} />');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function detectFramework(projectRoot: string): 'expo-router' | 'react-navigation' {
|
|
87
|
-
const srcApp = path.join(projectRoot, 'src', 'app');
|
|
88
|
-
const appDir = path.join(projectRoot, 'app');
|
|
89
|
-
|
|
90
|
-
if (fs.existsSync(srcApp) || fs.existsSync(appDir)) {
|
|
91
|
-
const layoutInSrc = path.join(srcApp, '_layout.tsx');
|
|
92
|
-
const layoutInApp = path.join(appDir, '_layout.tsx');
|
|
93
|
-
if (fs.existsSync(layoutInSrc) || fs.existsSync(layoutInApp)) {
|
|
94
|
-
return 'expo-router';
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return 'react-navigation';
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
interface CLIArgs {
|
|
102
|
-
dir: string;
|
|
103
|
-
watch: boolean;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function parseArgs(argv: string[]): CLIArgs {
|
|
107
|
-
const args: CLIArgs = {
|
|
108
|
-
dir: '',
|
|
109
|
-
watch: false,
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
for (const arg of argv) {
|
|
113
|
-
if (arg === '--watch' || arg === '-w') args.watch = true;
|
|
114
|
-
if (arg.startsWith('--dir=')) args.dir = arg.split('=')[1]!;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return args;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Run
|
|
121
|
-
main();
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import * as fs from 'fs';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
|
|
6
|
-
export function generateSwiftCode(manifestPath: string, appScheme: string): string {
|
|
7
|
-
const manifests = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
8
|
-
|
|
9
|
-
let swiftCode = `import AppIntents
|
|
10
|
-
import UIKit
|
|
11
|
-
|
|
12
|
-
// Auto-generated by react-native-ai-agent
|
|
13
|
-
// Do not edit manually.
|
|
14
|
-
|
|
15
|
-
`;
|
|
16
|
-
|
|
17
|
-
for (const intent of manifests) {
|
|
18
|
-
// Convert snake_case to CamelCaseIntent
|
|
19
|
-
const structName = intent.name
|
|
20
|
-
.split('_')
|
|
21
|
-
.map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
22
|
-
.join('') + 'Intent';
|
|
23
|
-
|
|
24
|
-
swiftCode += `@available(iOS 16.0, *)\n`;
|
|
25
|
-
swiftCode += `struct ${structName}: AppIntent {\n`;
|
|
26
|
-
swiftCode += ` static var title: LocalizedStringResource = "${intent.name}"\n`;
|
|
27
|
-
swiftCode += ` static var description = IntentDescription("${intent.description.replace(/"/g, '\\"')}")\n`;
|
|
28
|
-
swiftCode += ` static var openAppWhenRun: Bool = true\n\n`;
|
|
29
|
-
|
|
30
|
-
// Parameters
|
|
31
|
-
const params = intent.parameters || {};
|
|
32
|
-
for (const [key, param] of Object.entries<any>(params)) {
|
|
33
|
-
let swiftType = 'String';
|
|
34
|
-
if (param.type === 'number') swiftType = 'Double';
|
|
35
|
-
if (param.type === 'boolean') swiftType = 'Bool';
|
|
36
|
-
|
|
37
|
-
swiftCode += ` @Parameter(title: "${param.description || key}")\n`;
|
|
38
|
-
swiftCode += ` var ${key}: ${swiftType}?\n\n`;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Perform function
|
|
42
|
-
swiftCode += ` @MainActor\n`;
|
|
43
|
-
swiftCode += ` func perform() async throws -> some IntentResult {\n`;
|
|
44
|
-
swiftCode += ` var components = URLComponents()\n`;
|
|
45
|
-
swiftCode += ` components.scheme = "${appScheme}"\n`;
|
|
46
|
-
swiftCode += ` components.host = "ai-action"\n`;
|
|
47
|
-
swiftCode += ` components.path = "/${intent.name}"\n`;
|
|
48
|
-
|
|
49
|
-
if (Object.keys(params).length > 0) {
|
|
50
|
-
swiftCode += ` var queryItems: [URLQueryItem] = []\n`;
|
|
51
|
-
for (const [key, _param] of Object.entries<any>(params)) {
|
|
52
|
-
swiftCode += ` if let val = ${key} {\n`;
|
|
53
|
-
swiftCode += ` queryItems.append(URLQueryItem(name: "${key}", value: String(describing: val)))\n`;
|
|
54
|
-
swiftCode += ` }\n`;
|
|
55
|
-
}
|
|
56
|
-
swiftCode += ` components.queryItems = queryItems\n`;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
swiftCode += ` if let url = components.url {\n`;
|
|
60
|
-
swiftCode += ` await UIApplication.shared.open(url)\n`;
|
|
61
|
-
swiftCode += ` }\n`;
|
|
62
|
-
swiftCode += ` return .result()\n`;
|
|
63
|
-
swiftCode += ` }\n`;
|
|
64
|
-
swiftCode += `}\n\n`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// AppShortcuts provider (exposes intents to Siri automatically)
|
|
68
|
-
if (manifests.length > 0) {
|
|
69
|
-
swiftCode += `@available(iOS 16.0, *)\n`;
|
|
70
|
-
swiftCode += `struct MobileAIAppShortcuts: AppShortcutsProvider {\n`;
|
|
71
|
-
swiftCode += ` static var appShortcuts: [AppShortcut] {\n`;
|
|
72
|
-
for (const intent of manifests) {
|
|
73
|
-
const structName = intent.name.split('_').map((w: string) => w.charAt(0).toUpperCase() + w.slice(1)).join('') + 'Intent';
|
|
74
|
-
// Generate a basic short phrase mapping
|
|
75
|
-
swiftCode += ` AppShortcut(intent: ${structName}(), phrases: ["\\\\(.applicationName) ${intent.name.replace(/_/g, ' ')}"])\n`;
|
|
76
|
-
}
|
|
77
|
-
swiftCode += ` }\n`;
|
|
78
|
-
swiftCode += `}\n`;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return swiftCode;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function main() {
|
|
85
|
-
const manifestPath = process.argv[2] || 'intent-manifest.json';
|
|
86
|
-
const appScheme = process.argv[3] || 'mobileai';
|
|
87
|
-
const outPath = process.argv[4] || 'ios/MobileAIPilotIntents.swift';
|
|
88
|
-
|
|
89
|
-
const absoluteManifest = path.resolve(process.cwd(), manifestPath);
|
|
90
|
-
const absoluteOut = path.resolve(process.cwd(), outPath);
|
|
91
|
-
|
|
92
|
-
if (!fs.existsSync(absoluteManifest)) {
|
|
93
|
-
console.error(`❌ Manifest file not found at ${absoluteManifest}`);
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
console.log(`Generating Swift code for schema ${appScheme}...`);
|
|
98
|
-
const swiftCode = generateSwiftCode(absoluteManifest, appScheme);
|
|
99
|
-
|
|
100
|
-
// Ensure dir exists
|
|
101
|
-
const targetDir = path.dirname(absoluteOut);
|
|
102
|
-
if (!fs.existsSync(targetDir)) {
|
|
103
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
fs.writeFileSync(absoluteOut, swiftCode);
|
|
107
|
-
console.log(`✅ Wrote Swift AppIntents to ${outPath}`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Run if called directly
|
|
111
|
-
if (require.main === module) {
|
|
112
|
-
main().catch(err => {
|
|
113
|
-
console.error(err);
|
|
114
|
-
process.exit(1);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Expo Router scanner.
|
|
3
|
-
* Discovers screens by walking the app/ or src/app/ directory.
|
|
4
|
-
* Maps file paths to route names following Expo Router conventions.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { extractContentFromAST, buildDescription } from '../extractors/ast-extractor';
|
|
10
|
-
import { parse } from '@babel/parser';
|
|
11
|
-
import * as _traverse from '@babel/traverse';
|
|
12
|
-
import * as t from '@babel/types';
|
|
13
|
-
|
|
14
|
-
const traverse = (_traverse as any).default || _traverse;
|
|
15
|
-
|
|
16
|
-
export interface ScannedScreen {
|
|
17
|
-
routeName: string;
|
|
18
|
-
filePath: string;
|
|
19
|
-
title?: string;
|
|
20
|
-
description: string;
|
|
21
|
-
navigationLinks: string[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Scan an Expo Router app directory and return all screens.
|
|
26
|
-
*/
|
|
27
|
-
export function scanExpoRouterApp(projectRoot: string): ScannedScreen[] {
|
|
28
|
-
// Find app directory: src/app/ first, then app/
|
|
29
|
-
const appDir = findAppDir(projectRoot);
|
|
30
|
-
if (!appDir) {
|
|
31
|
-
throw new Error('Could not find app/ or src/app/ directory. Is this an Expo Router project?');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
console.log(`[generate-map] Scanning Expo Router app: ${appDir}`);
|
|
35
|
-
|
|
36
|
-
const screens: ScannedScreen[] = [];
|
|
37
|
-
const layoutTitles = new Map<string, string>(); // routeName → title from _layout.tsx
|
|
38
|
-
|
|
39
|
-
// First pass: extract titles from _layout.tsx files
|
|
40
|
-
extractLayoutTitles(appDir, appDir, layoutTitles);
|
|
41
|
-
|
|
42
|
-
// Second pass: scan screen files
|
|
43
|
-
scanDirectory(appDir, appDir, screens, layoutTitles);
|
|
44
|
-
|
|
45
|
-
return screens;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function findAppDir(projectRoot: string): string | null {
|
|
49
|
-
const srcApp = path.join(projectRoot, 'src', 'app');
|
|
50
|
-
if (fs.existsSync(srcApp)) return srcApp;
|
|
51
|
-
|
|
52
|
-
const app = path.join(projectRoot, 'app');
|
|
53
|
-
if (fs.existsSync(app)) return app;
|
|
54
|
-
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function scanDirectory(
|
|
59
|
-
dir: string,
|
|
60
|
-
appRoot: string,
|
|
61
|
-
screens: ScannedScreen[],
|
|
62
|
-
layoutTitles: Map<string, string>
|
|
63
|
-
) {
|
|
64
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
65
|
-
|
|
66
|
-
for (const entry of entries) {
|
|
67
|
-
const fullPath = path.join(dir, entry.name);
|
|
68
|
-
|
|
69
|
-
if (entry.isDirectory()) {
|
|
70
|
-
// Skip hidden directories and node_modules
|
|
71
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
72
|
-
scanDirectory(fullPath, appRoot, screens, layoutTitles);
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Only process .tsx and .ts files (not .js — Expo Router uses TS by default)
|
|
77
|
-
if (!entry.name.match(/\.(tsx?|jsx?)$/)) continue;
|
|
78
|
-
|
|
79
|
-
// Skip layout files, special files, and API routes
|
|
80
|
-
if (entry.name === '_layout.tsx' || entry.name === '_layout.ts') continue;
|
|
81
|
-
if (entry.name.startsWith('+')) continue; // +not-found, +html, +api, etc.
|
|
82
|
-
if (entry.name.startsWith('_')) continue; // Other special files
|
|
83
|
-
|
|
84
|
-
const routeName = filePathToRouteName(fullPath, appRoot);
|
|
85
|
-
const sourceCode = fs.readFileSync(fullPath, 'utf-8');
|
|
86
|
-
const extracted = extractContentFromAST(sourceCode, fullPath);
|
|
87
|
-
const title = layoutTitles.get(routeName);
|
|
88
|
-
|
|
89
|
-
screens.push({
|
|
90
|
-
routeName,
|
|
91
|
-
filePath: fullPath,
|
|
92
|
-
title,
|
|
93
|
-
description: buildDescription(extracted),
|
|
94
|
-
navigationLinks: extracted.navigationLinks,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Convert a file path to an Expo Router route name.
|
|
101
|
-
* Examples:
|
|
102
|
-
* app/categories.tsx → categories
|
|
103
|
-
* app/item-reviews/[id].tsx → item-reviews/[id]
|
|
104
|
-
* app/(tabs)/index.tsx → (tabs)/index
|
|
105
|
-
* app/product/[id].tsx → product/[id]
|
|
106
|
-
*/
|
|
107
|
-
function filePathToRouteName(filePath: string, appRoot: string): string {
|
|
108
|
-
let relative = path.relative(appRoot, filePath);
|
|
109
|
-
|
|
110
|
-
// Remove extension
|
|
111
|
-
relative = relative.replace(/\.(tsx?|jsx?)$/, '');
|
|
112
|
-
|
|
113
|
-
// Normalize path separators (Windows compat)
|
|
114
|
-
relative = relative.split(path.sep).join('/');
|
|
115
|
-
|
|
116
|
-
return relative;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Parse _layout.tsx files to extract screen titles from Stack.Screen options.
|
|
121
|
-
*/
|
|
122
|
-
function extractLayoutTitles(
|
|
123
|
-
dir: string,
|
|
124
|
-
appRoot: string,
|
|
125
|
-
titles: Map<string, string>
|
|
126
|
-
) {
|
|
127
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
128
|
-
|
|
129
|
-
for (const entry of entries) {
|
|
130
|
-
const fullPath = path.join(dir, entry.name);
|
|
131
|
-
|
|
132
|
-
if (entry.isDirectory()) {
|
|
133
|
-
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue;
|
|
134
|
-
extractLayoutTitles(fullPath, appRoot, titles);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (entry.name === '_layout.tsx' || entry.name === '_layout.ts') {
|
|
139
|
-
const sourceCode = fs.readFileSync(fullPath, 'utf-8');
|
|
140
|
-
const dirRelative = path.relative(appRoot, dir);
|
|
141
|
-
const prefix = dirRelative ? dirRelative.split(path.sep).join('/') + '/' : '';
|
|
142
|
-
|
|
143
|
-
extractTitlesFromLayout(sourceCode, prefix, titles);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Extract screen names and titles from a layout file's JSX.
|
|
150
|
-
* Looks for patterns like: <Stack.Screen name="categories" options={{ title: 'Categories' }} />
|
|
151
|
-
*/
|
|
152
|
-
function extractTitlesFromLayout(sourceCode: string, routePrefix: string, titles: Map<string, string>) {
|
|
153
|
-
let ast: ReturnType<typeof parse>;
|
|
154
|
-
try {
|
|
155
|
-
ast = parse(sourceCode, {
|
|
156
|
-
sourceType: 'module',
|
|
157
|
-
plugins: ['jsx', 'typescript', 'decorators-legacy'],
|
|
158
|
-
});
|
|
159
|
-
} catch {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
traverse(ast, {
|
|
164
|
-
JSXOpeningElement(path: any) {
|
|
165
|
-
const nameNode = path.node.name;
|
|
166
|
-
// Match *.Screen
|
|
167
|
-
if (!t.isJSXMemberExpression(nameNode)) return;
|
|
168
|
-
if (!t.isJSXIdentifier(nameNode.property) || nameNode.property.name !== 'Screen') return;
|
|
169
|
-
|
|
170
|
-
// Get name prop
|
|
171
|
-
let screenName: string | null = null;
|
|
172
|
-
let screenTitle: string | null = null;
|
|
173
|
-
|
|
174
|
-
for (const attr of path.node.attributes) {
|
|
175
|
-
if (!t.isJSXAttribute(attr) || !t.isJSXIdentifier(attr.name)) continue;
|
|
176
|
-
|
|
177
|
-
if (attr.name.name === 'name' && t.isStringLiteral(attr.value)) {
|
|
178
|
-
screenName = attr.value.value;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (attr.name.name === 'options' && t.isJSXExpressionContainer(attr.value)) {
|
|
182
|
-
const expr = attr.value.expression;
|
|
183
|
-
if (t.isObjectExpression(expr)) {
|
|
184
|
-
for (const prop of expr.properties) {
|
|
185
|
-
if (
|
|
186
|
-
t.isObjectProperty(prop) &&
|
|
187
|
-
t.isIdentifier(prop.key) &&
|
|
188
|
-
prop.key.name === 'title' &&
|
|
189
|
-
t.isStringLiteral(prop.value)
|
|
190
|
-
) {
|
|
191
|
-
screenTitle = prop.value.value;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (screenName && screenTitle) {
|
|
199
|
-
titles.set(routePrefix + screenName, screenTitle);
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
});
|
|
203
|
-
}
|