apple-intelligence-example 1.0.0

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/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # Apple Intelligence Plugin - Example App
2
+
3
+ This is a minimal Capacitor app that demonstrates how to use the `capacitor-apple-intelligence` plugin.
4
+
5
+ ## Prerequisites
6
+
7
+ - **iOS 26+** (required for Apple Intelligence APIs)
8
+ - **Xcode** (latest version recommended)
9
+ - **Node.js** and **npm**
10
+ - An iOS device or simulator running iOS 26+
11
+
12
+ ## Setup
13
+
14
+ 1. **Install dependencies:**
15
+ ```bash
16
+ npm install
17
+ ```
18
+
19
+ 2. **Add iOS platform:**
20
+ ```bash
21
+ npm run add:ios
22
+ ```
23
+
24
+ 3. **Sync Capacitor:**
25
+ ```bash
26
+ npm run sync
27
+ ```
28
+
29
+ 4. **Open in Xcode:**
30
+ ```bash
31
+ npm run open:ios
32
+ ```
33
+
34
+ 5. **Build and Run:**
35
+ - Select your target device/simulator in Xcode
36
+ - Click the Run button (or press Cmd+R)
37
+ - The app should launch on your device
38
+
39
+ ## Features
40
+
41
+ The example app provides a simple interface to test all three plugin methods:
42
+
43
+ ### 1. Generate JSON
44
+ - **Method:** `generateJSON()`
45
+ - **Input:** Prompt + JSON Schema
46
+ - **Output:** Structured JSON data matching the schema
47
+ - **Example:** Generate a user profile with name, age, and occupation
48
+
49
+ ### 2. Generate Text
50
+ - **Method:** `generateText()`
51
+ - **Input:** Prompt only
52
+ - **Output:** Plain text response
53
+ - **Example:** Generate a creative story or description
54
+
55
+ ### 3. Generate Text with Language
56
+ - **Method:** `generateTextWithLanguage()`
57
+ - **Input:** Prompt + Target Language
58
+ - **Output:** Plain text in the specified language
59
+ - **Example:** Generate text in Spanish, French, German, etc.
60
+
61
+ ## Usage
62
+
63
+ 1. Enter a prompt in the text field
64
+ 2. For JSON generation, modify the schema if needed
65
+ 3. For language-specific generation, select your target language
66
+ 4. Click the appropriate button to test the method
67
+ 5. View the results in the output section below
68
+
69
+ ## Troubleshooting
70
+
71
+ ### Plugin not found
72
+ - Make sure you ran `npm install` in the example directory
73
+ - Verify the parent plugin is built: `cd .. && npm run build`
74
+ - Re-sync Capacitor: `npm run sync`
75
+
76
+ ### iOS 26+ requirement
77
+ - The Apple Intelligence APIs require iOS 26 or later
78
+ - Check your simulator/device iOS version
79
+ - Update to the latest iOS beta if needed
80
+
81
+ ### Build errors in Xcode
82
+ - Clean build folder: Product → Clean Build Folder
83
+ - Delete derived data
84
+ - Ensure you have the latest Xcode version
85
+
86
+ ## Project Structure
87
+
88
+ ```
89
+ example/
90
+ ├── www/
91
+ │ ├── index.html # Main UI
92
+ │ └── js/
93
+ │ └── app.js # Plugin integration logic
94
+ ├── capacitor.config.ts # Capacitor configuration
95
+ ├── package.json # Dependencies
96
+ └── README.md # This file
97
+ ```
98
+
99
+ ## Learn More
100
+
101
+ - [Plugin Documentation](../README.md)
102
+ - [Capacitor Documentation](https://capacitorjs.com/docs)
103
+ - [Apple Intelligence APIs](https://developer.apple.com/documentation/foundation/foundationmodels)
@@ -0,0 +1,9 @@
1
+ import type { CapacitorConfig } from '@capacitor/cli';
2
+
3
+ const config: CapacitorConfig = {
4
+ appId: 'com.example.appleintelligence.capacitor',
5
+ appName: 'Apple Intelligence Example',
6
+ webDir: 'www'
7
+ };
8
+
9
+ export default config;
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "apple-intelligence-example",
3
+ "version": "1.0.0",
4
+ "description": "Example app for testing Capacitor Apple Intelligence plugin",
5
+ "scripts": {
6
+ "build": "echo 'No build step needed for static HTML'",
7
+ "open:ios": "npx cap open ios",
8
+ "sync": "npx cap sync",
9
+ "add:ios": "npx cap add ios"
10
+ },
11
+ "dependencies": {
12
+ "@capacitor/core": "^8.0.0",
13
+ "capacitor-apple-intelligence": "file:.."
14
+ },
15
+ "devDependencies": {
16
+ "@capacitor/cli": "^8.0.0",
17
+ "@capacitor/ios": "^8.0.0"
18
+ }
19
+ }
package/www/index.html ADDED
@@ -0,0 +1,296 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Apple Intelligence Example</title>
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
17
+ background: #f5f5f5;
18
+ min-height: 100vh;
19
+ padding: 20px;
20
+ color: #333;
21
+ }
22
+
23
+ .container {
24
+ max-width: 800px;
25
+ margin: 0 auto;
26
+ background: white;
27
+ border-radius: 20px;
28
+ padding: 30px;
29
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
30
+ }
31
+
32
+ h1 {
33
+ color: #333;
34
+ margin-bottom: 5px;
35
+ font-size: 28px;
36
+ }
37
+
38
+ .subtitle {
39
+ color: #666;
40
+ margin-bottom: 10px;
41
+ font-size: 14px;
42
+ }
43
+
44
+ .status-badge {
45
+ display: inline-block;
46
+ padding: 6px 12px;
47
+ border-radius: 6px;
48
+ font-size: 12px;
49
+ font-weight: 600;
50
+ margin-bottom: 20px;
51
+ }
52
+
53
+ .status-available {
54
+ background: #d4edda;
55
+ color: #155724;
56
+ border: 1px solid #c3e6cb;
57
+ }
58
+
59
+ .status-unavailable {
60
+ background: #f8d7da;
61
+ color: #721c24;
62
+ border: 1px solid #f5c6cb;
63
+ }
64
+
65
+ .section {
66
+ margin-bottom: 25px;
67
+ }
68
+
69
+ label {
70
+ display: block;
71
+ font-weight: 600;
72
+ margin-bottom: 8px;
73
+ color: #444;
74
+ font-size: 14px;
75
+ }
76
+
77
+ input[type="text"],
78
+ textarea,
79
+ select {
80
+ width: 100%;
81
+ padding: 12px;
82
+ border: 2px solid #e0e0e0;
83
+ border-radius: 8px;
84
+ font-size: 14px;
85
+ font-family: inherit;
86
+ transition: border-color 0.3s;
87
+ }
88
+
89
+ input[type="text"]:focus,
90
+ textarea:focus,
91
+ select:focus {
92
+ outline: none;
93
+ border-color: #667eea;
94
+ }
95
+
96
+ textarea {
97
+ resize: vertical;
98
+ min-height: 100px;
99
+ font-family: 'Courier New', monospace;
100
+ }
101
+
102
+ .button-group {
103
+ display: grid;
104
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
105
+ gap: 10px;
106
+ margin-top: 20px;
107
+ }
108
+
109
+ button {
110
+ padding: 14px 24px;
111
+ background: #007aff;
112
+ color: white;
113
+ border: none;
114
+ border-radius: 8px;
115
+ font-size: 14px;
116
+ font-weight: 600;
117
+ cursor: pointer;
118
+ transition: background 0.2s;
119
+ }
120
+
121
+ button:hover {
122
+ background: #0051d5;
123
+ }
124
+
125
+ button:active {
126
+ background: #004bb5;
127
+ }
128
+
129
+ button:disabled {
130
+ opacity: 0.5;
131
+ cursor: not-allowed;
132
+ background: #007aff;
133
+ }
134
+
135
+ .output {
136
+ margin-top: 30px;
137
+ padding: 20px;
138
+ background: #f8f9fa;
139
+ border-radius: 8px;
140
+ border-left: 4px solid #007aff;
141
+ min-height: 100px;
142
+ }
143
+
144
+ .output h3 {
145
+ margin-bottom: 10px;
146
+ color: #007aff;
147
+ font-size: 16px;
148
+ }
149
+
150
+ .output pre {
151
+ background: white;
152
+ padding: 15px;
153
+ border-radius: 6px;
154
+ overflow-x: auto;
155
+ font-size: 13px;
156
+ line-height: 1.5;
157
+ }
158
+
159
+ .error {
160
+ border-left-color: #e74c3c;
161
+ }
162
+
163
+ .error h3 {
164
+ color: #e74c3c;
165
+ }
166
+
167
+ .loading {
168
+ display: inline-block;
169
+ width: 16px;
170
+ height: 16px;
171
+ border: 3px solid rgba(255, 255, 255, 0.3);
172
+ border-radius: 50%;
173
+ border-top-color: white;
174
+ animation: spin 1s linear infinite;
175
+ margin-left: 8px;
176
+ }
177
+
178
+ @keyframes spin {
179
+ to {
180
+ transform: rotate(360deg);
181
+ }
182
+ }
183
+
184
+ .info-box {
185
+ background: #e3f2fd;
186
+ padding: 15px;
187
+ border-radius: 8px;
188
+ margin-bottom: 20px;
189
+ border-left: 4px solid #2196f3;
190
+ }
191
+
192
+ .info-box p {
193
+ margin: 5px 0;
194
+ font-size: 13px;
195
+ color: #1565c0;
196
+ }
197
+ </style>
198
+ </head>
199
+
200
+ <body>
201
+ <div class="container">
202
+ <h1>🧠 Apple Intelligence Plugin</h1>
203
+ <p class="subtitle">Test the Capacitor Apple Intelligence plugin methods</p>
204
+ <div id="status-badge" class="status-badge status-unavailable">Checking availability...</div>
205
+
206
+ <div class="info-box">
207
+ <p><strong>Requirements:</strong> iOS 26+ device or simulator</p>
208
+ <p><strong>Note:</strong> This plugin uses on-device Apple Intelligence APIs</p>
209
+ </div>
210
+
211
+ <div class="section">
212
+ <label for="prompt">Prompt</label>
213
+ <input type="text" id="prompt" placeholder="Enter your prompt here..."
214
+ value="Generate a user profile with name, age, and occupation">
215
+ </div>
216
+
217
+ <div class="section">
218
+ <label for="schema">JSON Schema (for generateJSON)</label>
219
+ <textarea id="schema" placeholder='{"type": "object", "properties": {...}}'>{
220
+ "type": "object",
221
+ "properties": {
222
+ "name": { "type": "string" },
223
+ "age": { "type": "number" },
224
+ "occupation": { "type": "string" }
225
+ },
226
+ "required": ["name", "age", "occupation"]
227
+ }</textarea>
228
+ </div>
229
+
230
+ <div class="section">
231
+ <label for="language">Target Language (for generateTextWithLanguage)</label>
232
+ <select id="language">
233
+ <option value="en">English</option>
234
+ <option value="es">Spanish</option>
235
+ <option value="fr">French</option>
236
+ <option value="de">German</option>
237
+ <option value="ja">Japanese</option>
238
+ <option value="zh">Chinese</option>
239
+ </select>
240
+ </div>
241
+
242
+ <div class="section">
243
+ <label for="imageStyle">Image Style (for generateImage)</label>
244
+ <select id="imageStyle">
245
+ <option value="animation">Animation (Default)</option>
246
+ <option value="illustration">Illustration</option>
247
+ <option value="sketch">Sketch</option>
248
+ </select>
249
+ <p class="info-text" style="margin-top: 6px; font-size: 12px; color: #666;">
250
+ Note: Apple's on-device Image Playground only supports these 3 styles.
251
+ </p>
252
+ </div>
253
+
254
+ <div class="section">
255
+ <label for="sourceImage">Source Image (Optional - for face-based generation)</label>
256
+ <input type="file" id="sourceImage" accept="image/*" onchange="previewSourceImage(event)"
257
+ style="padding: 8px; background: #f8f9fa;">
258
+ <p class="info-text" style="margin-top: 6px; font-size: 12px; color: #666;">
259
+ Required when generating images of people. Select a photo with a visible face.
260
+ </p>
261
+ <div id="sourceImagePreview" style="margin-top: 10px; display: none;">
262
+ <img id="sourceImagePreviewImg"
263
+ style="max-width: 150px; max-height: 150px; border-radius: 8px; border: 2px solid #e0e0e0;">
264
+ <button onclick="clearSourceImage()"
265
+ style="display: block; margin-top: 8px; padding: 6px 12px; font-size: 12px; background: #dc3545;">
266
+ ✕ Clear Image
267
+ </button>
268
+ </div>
269
+ </div>
270
+
271
+ <div class="button-group">
272
+ <button onclick="testGenerateJSON()">
273
+ 📋 Generate JSON
274
+ </button>
275
+ <button onclick="testGenerateText()">
276
+ 📝 Generate Text
277
+ </button>
278
+ <button onclick="testGenerateTextWithLanguage()">
279
+ 🌍 Generate Text (Language)
280
+ </button>
281
+ <button onclick="testGenerateImage()">
282
+ 🎨 Generate Image
283
+ </button>
284
+ </div>
285
+
286
+ <div id="output" class="output" style="display: none;">
287
+ <h3>Result</h3>
288
+ <pre id="result"></pre>
289
+ <div id="imageResult" style="margin-top: 15px; display: none;"></div>
290
+ </div>
291
+ </div>
292
+
293
+ <script src="js/app.js"></script>
294
+ </body>
295
+
296
+ </html>
package/www/js/app.js ADDED
@@ -0,0 +1,280 @@
1
+ // Access the plugin through Capacitor's Plugins API
2
+ const { AppleIntelligence } = Capacitor.Plugins;
3
+
4
+ // Helper function to display results
5
+ function displayResult(result, isError = false) {
6
+ const outputDiv = document.getElementById('output');
7
+ const resultPre = document.getElementById('result');
8
+
9
+ outputDiv.style.display = 'block';
10
+ outputDiv.className = isError ? 'output error' : 'output';
11
+
12
+ if (isError) {
13
+ resultPre.textContent = `Error: ${result}`;
14
+ } else {
15
+ resultPre.textContent = JSON.stringify(result, null, 2);
16
+ }
17
+ }
18
+
19
+ // Helper function to get input values
20
+ function getInputs() {
21
+ return {
22
+ prompt: document.getElementById('prompt').value,
23
+ schema: document.getElementById('schema').value,
24
+ language: document.getElementById('language').value,
25
+ imageStyle: document.getElementById('imageStyle').value,
26
+ sourceImageBase64: window.selectedSourceImageBase64 || null
27
+ };
28
+ }
29
+
30
+ // Store selected source image as base64
31
+ window.selectedSourceImageBase64 = null;
32
+
33
+ // Preview source image when selected
34
+ function previewSourceImage(event) {
35
+ const file = event.target.files[0];
36
+ if (!file) return;
37
+
38
+ const reader = new FileReader();
39
+ reader.onload = function (e) {
40
+ const base64Full = e.target.result;
41
+ // Extract just the base64 data (remove data:image/...;base64, prefix)
42
+ const base64Data = base64Full.split(',')[1];
43
+ window.selectedSourceImageBase64 = base64Data;
44
+
45
+ // Show preview
46
+ const preview = document.getElementById('sourceImagePreview');
47
+ const previewImg = document.getElementById('sourceImagePreviewImg');
48
+ previewImg.src = base64Full;
49
+ preview.style.display = 'block';
50
+ };
51
+ reader.readAsDataURL(file);
52
+ }
53
+
54
+ // Clear source image
55
+ function clearSourceImage() {
56
+ window.selectedSourceImageBase64 = null;
57
+ document.getElementById('sourceImage').value = '';
58
+ document.getElementById('sourceImagePreview').style.display = 'none';
59
+ }
60
+
61
+ // Helper function to disable/enable buttons
62
+ function setButtonsDisabled(disabled) {
63
+ const buttons = document.querySelectorAll('button');
64
+ buttons.forEach(button => button.disabled = disabled);
65
+ }
66
+
67
+ // Test generateJSON method
68
+ async function testGenerateJSON() {
69
+ const { prompt, schema } = getInputs();
70
+
71
+ if (!prompt || !schema) {
72
+ displayResult('Please enter both a prompt and a schema', true);
73
+ return;
74
+ }
75
+
76
+ try {
77
+ setButtonsDisabled(true);
78
+ displayResult('Generating JSON...', false);
79
+
80
+ // Parse the schema to validate it
81
+ let parsedSchema;
82
+ try {
83
+ parsedSchema = JSON.parse(schema);
84
+ } catch (e) {
85
+ throw new Error('Invalid JSON schema: ' + e.message);
86
+ }
87
+
88
+ const result = await AppleIntelligence.generate({
89
+ messages: [
90
+ { role: 'user', content: prompt }
91
+ ],
92
+ response_format: {
93
+ type: 'json_schema',
94
+ schema: parsedSchema
95
+ }
96
+ });
97
+
98
+ if (result.success) {
99
+ displayResult({
100
+ method: 'generate',
101
+ prompt: prompt,
102
+ schema: parsedSchema,
103
+ result: result.data
104
+ }, false);
105
+ } else {
106
+ throw new Error(result.error?.message || 'Generation failed');
107
+ }
108
+ } catch (error) {
109
+ displayResult(error.message || error.toString(), true);
110
+ } finally {
111
+ setButtonsDisabled(false);
112
+ }
113
+ }
114
+
115
+ // Test generateText method
116
+ async function testGenerateText() {
117
+ const { prompt } = getInputs();
118
+
119
+ if (!prompt) {
120
+ displayResult('Please enter a prompt', true);
121
+ return;
122
+ }
123
+
124
+ try {
125
+ setButtonsDisabled(true);
126
+ displayResult('Generating text...', false);
127
+
128
+ const result = await AppleIntelligence.generateText({
129
+ messages: [
130
+ { role: 'user', content: prompt }
131
+ ]
132
+ });
133
+
134
+ if (result.success) {
135
+ displayResult({
136
+ method: 'generateText',
137
+ prompt: prompt,
138
+ result: result.content
139
+ }, false);
140
+ } else {
141
+ throw new Error(result.error?.message || 'Generation failed');
142
+ }
143
+ } catch (error) {
144
+ displayResult(error.message || error.toString(), true);
145
+ } finally {
146
+ setButtonsDisabled(false);
147
+ }
148
+ }
149
+
150
+ // Test generateTextWithLanguage method
151
+ async function testGenerateTextWithLanguage() {
152
+ const { prompt, language } = getInputs();
153
+
154
+ if (!prompt) {
155
+ displayResult('Please enter a prompt', true);
156
+ return;
157
+ }
158
+
159
+ try {
160
+ setButtonsDisabled(true);
161
+ displayResult(`Generating text in ${language}...`, false);
162
+
163
+ const result = await AppleIntelligence.generateTextWithLanguage({
164
+ messages: [
165
+ { role: 'user', content: prompt }
166
+ ],
167
+ language: language
168
+ });
169
+
170
+ if (result.success) {
171
+ displayResult({
172
+ method: 'generateTextWithLanguage',
173
+ prompt: prompt,
174
+ language: language,
175
+ result: result.content
176
+ }, false);
177
+ } else {
178
+ throw new Error(result.error?.message || 'Generation failed');
179
+ }
180
+ } catch (error) {
181
+ displayResult(error.message || error.toString(), true);
182
+ } finally {
183
+ setButtonsDisabled(false);
184
+ }
185
+ }
186
+
187
+ // Test generateImage method
188
+ async function testGenerateImage() {
189
+ const { prompt, imageStyle, sourceImageBase64 } = getInputs();
190
+
191
+ if (!prompt) {
192
+ displayResult('Please enter a prompt', true);
193
+ return;
194
+ }
195
+
196
+ // Clear previous images
197
+ const imageResultDiv = document.getElementById('imageResult');
198
+ imageResultDiv.innerHTML = '';
199
+ imageResultDiv.style.display = 'none';
200
+
201
+ try {
202
+ setButtonsDisabled(true);
203
+ const statusMsg = sourceImageBase64
204
+ ? 'Generating image with source face...'
205
+ : 'Generating image...';
206
+ displayResult(statusMsg, false);
207
+
208
+ const request = {
209
+ prompt: prompt,
210
+ style: imageStyle || undefined,
211
+ count: 1
212
+ };
213
+
214
+ // Add source image if provided
215
+ if (sourceImageBase64) {
216
+ request.sourceImage = sourceImageBase64;
217
+ }
218
+
219
+ const result = await AppleIntelligence.generateImage(request);
220
+
221
+ if (result.success && result.images) {
222
+ displayResult({
223
+ method: 'generateImage',
224
+ prompt: prompt,
225
+ style: imageStyle,
226
+ hasSourceImage: !!sourceImageBase64,
227
+ imageCount: result.images.length
228
+ }, false);
229
+
230
+ // Display images
231
+ imageResultDiv.style.display = 'block';
232
+ result.images.forEach(base64 => {
233
+ const img = document.createElement('img');
234
+ img.src = `data:image/jpeg;base64,${base64}`;
235
+ img.style.maxWidth = '100%';
236
+ img.style.borderRadius = '8px';
237
+ img.style.marginTop = '10px';
238
+ img.style.border = '1px solid #ddd';
239
+ imageResultDiv.appendChild(img);
240
+ });
241
+
242
+ } else {
243
+ throw new Error(result.error?.message || 'Generation failed');
244
+ }
245
+ } catch (error) {
246
+ displayResult(error.message || error.toString(), true);
247
+ } finally {
248
+ setButtonsDisabled(false);
249
+ }
250
+ }
251
+
252
+
253
+
254
+ // Display ready message on load
255
+ document.addEventListener('DOMContentLoaded', async () => {
256
+ console.log('Apple Intelligence Example App loaded');
257
+ console.log('Plugin available:', typeof AppleIntelligence !== 'undefined');
258
+
259
+ // Check Apple Intelligence availability
260
+ const statusBadge = document.getElementById('status-badge');
261
+ try {
262
+ const result = await AppleIntelligence.checkAvailability();
263
+ console.log('Availability result:', result);
264
+
265
+ if (result && result.available) {
266
+ statusBadge.textContent = '✓ Apple Intelligence Available';
267
+ statusBadge.className = 'status-badge status-available';
268
+ } else {
269
+ statusBadge.textContent = '✗ Apple Intelligence Unavailable';
270
+ statusBadge.className = 'status-badge status-unavailable';
271
+ if (result && result.error) {
272
+ console.log('Availability error:', result.error);
273
+ }
274
+ }
275
+ } catch (error) {
276
+ statusBadge.textContent = '✗ Apple Intelligence Unavailable';
277
+ statusBadge.className = 'status-badge status-unavailable';
278
+ console.error('Availability check failed:', error);
279
+ }
280
+ });