craft-native 0.0.37 → 0.0.49

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.
Files changed (89) hide show
  1. package/dist/android/src/index.js +268 -0
  2. package/dist/android/src/index.js.map +10 -0
  3. package/dist/android/templates/AndroidManifest.xml.template +69 -0
  4. package/dist/android/templates/CraftBridge.kt.template +3748 -0
  5. package/dist/android/templates/CraftBridgeExtensions.kt.template +383 -0
  6. package/dist/android/templates/CraftWidgetProvider.kt.template +246 -0
  7. package/dist/android/templates/MainActivity.kt.template +109 -0
  8. package/dist/android/templates/build.gradle.kts.app.template +80 -0
  9. package/dist/android/templates/build.gradle.kts.project.template +5 -0
  10. package/dist/android/templates/fastlane/Appfile +25 -0
  11. package/dist/android/templates/fastlane/Fastfile +192 -0
  12. package/dist/android/templates/fastlane/Gemfile +7 -0
  13. package/dist/android/templates/github-workflow-android.yml +248 -0
  14. package/dist/android/templates/github-workflow-test.yml +329 -0
  15. package/dist/android/templates/settings.gradle.kts.template +18 -0
  16. package/dist/android/templates/test-bridges.html +1463 -0
  17. package/dist/api/window.d.ts +18 -0
  18. package/dist/api/window.d.ts.map +1 -1
  19. package/dist/assets/index.d.ts.map +1 -1
  20. package/dist/bridge/core.d.ts +2 -0
  21. package/dist/bridge/core.d.ts.map +1 -1
  22. package/dist/cli.js +4168 -0
  23. package/dist/components/sidebar.d.ts +5 -1
  24. package/dist/components/sidebar.d.ts.map +1 -1
  25. package/dist/dev/devtools.d.ts +1 -1
  26. package/dist/dev/devtools.d.ts.map +1 -1
  27. package/dist/dev/hot-reload.d.ts +12 -3
  28. package/dist/dev/hot-reload.d.ts.map +1 -1
  29. package/dist/index.cjs +573 -208
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +585 -216
  32. package/dist/index.js.map +45 -0
  33. package/dist/ios/src/index.js +186 -0
  34. package/dist/ios/src/index.js.map +10 -0
  35. package/dist/ios/templates/CraftApp.swift +4241 -0
  36. package/dist/ios/templates/CraftAppExtensions.swift +361 -0
  37. package/dist/ios/templates/CraftWidget.swift +247 -0
  38. package/dist/ios/templates/Info.plist.template +65 -0
  39. package/dist/ios/templates/fastlane/Appfile +45 -0
  40. package/dist/ios/templates/fastlane/Fastfile +189 -0
  41. package/dist/ios/templates/fastlane/Gemfile +7 -0
  42. package/dist/ios/templates/fastlane/Matchfile +58 -0
  43. package/dist/ios/templates/github-workflow-ios.yml +192 -0
  44. package/dist/ios/templates/github-workflow-release.yml +175 -0
  45. package/dist/ios/templates/github-workflow-test.yml +272 -0
  46. package/dist/ios/templates/project.yml.template +25 -0
  47. package/dist/ios/templates/test-bridges.html +1463 -0
  48. package/dist/package.d.ts +106 -2
  49. package/dist/package.d.ts.map +1 -1
  50. package/dist/package.test.d.ts +2 -0
  51. package/dist/package.test.d.ts.map +1 -0
  52. package/dist/styles/sidebar-templates.d.ts.map +1 -1
  53. package/dist/styles/sidebars.d.ts +12 -8
  54. package/dist/styles/sidebars.d.ts.map +1 -1
  55. package/dist/templates/projects/blank/craft.config.ts +19 -0
  56. package/dist/templates/projects/blank/index.html +27 -0
  57. package/dist/templates/projects/blank/package.json +19 -0
  58. package/dist/templates/projects/blank/src/main.ts +26 -0
  59. package/dist/templates/projects/dashboard/craft.config.ts +42 -0
  60. package/dist/templates/projects/dashboard/index.html +13 -0
  61. package/dist/templates/projects/dashboard/package.json +20 -0
  62. package/dist/templates/projects/dashboard/src/main.ts +163 -0
  63. package/dist/templates/projects/dashboard/src/styles.css +280 -0
  64. package/dist/templates/projects/drawer/craft.config.ts +47 -0
  65. package/dist/templates/projects/drawer/index.html +13 -0
  66. package/dist/templates/projects/drawer/package.json +21 -0
  67. package/dist/templates/projects/drawer/src/main.ts +406 -0
  68. package/dist/templates/projects/drawer/src/styles.css +468 -0
  69. package/dist/templates/projects/ecommerce/craft.config.ts +32 -0
  70. package/dist/templates/projects/ecommerce/index.html +13 -0
  71. package/dist/templates/projects/ecommerce/package.json +19 -0
  72. package/dist/templates/projects/ecommerce/src/main.ts +194 -0
  73. package/dist/templates/projects/ecommerce/src/styles.css +169 -0
  74. package/dist/templates/projects/social/craft.config.ts +32 -0
  75. package/dist/templates/projects/social/index.html +13 -0
  76. package/dist/templates/projects/social/package.json +19 -0
  77. package/dist/templates/projects/social/src/main.ts +270 -0
  78. package/dist/templates/projects/social/src/styles.css +216 -0
  79. package/dist/templates/projects/tabs/craft.config.ts +47 -0
  80. package/dist/templates/projects/tabs/index.html +13 -0
  81. package/dist/templates/projects/tabs/package.json +21 -0
  82. package/dist/templates/projects/tabs/src/main.ts +177 -0
  83. package/dist/templates/projects/tabs/src/styles.css +163 -0
  84. package/dist/types.d.ts +29 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/updater/index.d.ts +1 -0
  87. package/dist/updater/index.d.ts.map +1 -1
  88. package/package.json +5 -12
  89. package/bin/cli.ts +0 -937
@@ -0,0 +1,361 @@
1
+ import SwiftUI
2
+ import ActivityKit
3
+ import AppIntents
4
+ import TipKit
5
+
6
+ // MARK: - iOS 16+ Advanced Features
7
+
8
+ /// Live Activities (Dynamic Island) Support
9
+ @available(iOS 16.1, *)
10
+ struct CraftLiveActivity: ActivityAttributes {
11
+ public struct ContentState: Codable, Hashable {
12
+ var status: String
13
+ var progress: Double
14
+ }
15
+
16
+ var name: String
17
+ }
18
+
19
+ @available(iOS 16.1, *)
20
+ class LiveActivityManager {
21
+ static let shared = LiveActivityManager()
22
+
23
+ private var currentActivity: Activity<CraftLiveActivity>?
24
+
25
+ func startActivity(name: String, status: String, progress: Double) async throws {
26
+ let attributes = CraftLiveActivity(name: name)
27
+ let contentState = CraftLiveActivity.ContentState(status: status, progress: progress)
28
+
29
+ currentActivity = try Activity.request(
30
+ attributes: attributes,
31
+ content: .init(state: contentState, staleDate: nil)
32
+ )
33
+ }
34
+
35
+ func updateActivity(status: String, progress: Double) async {
36
+ guard let activity = currentActivity else { return }
37
+
38
+ let contentState = CraftLiveActivity.ContentState(status: status, progress: progress)
39
+ await activity.update(.init(state: contentState, staleDate: nil))
40
+ }
41
+
42
+ func endActivity() async {
43
+ guard let activity = currentActivity else { return }
44
+ await activity.end(nil, dismissalPolicy: .immediate)
45
+ currentActivity = nil
46
+ }
47
+ }
48
+
49
+ // MARK: - App Intents (iOS 16+)
50
+
51
+ @available(iOS 16.0, *)
52
+ struct OpenCraftIntent: AppIntent {
53
+ static var title: LocalizedStringResource = "Open Craft App"
54
+ static var description = IntentDescription("Opens the Craft application")
55
+
56
+ func perform() async throws -> some IntentResult {
57
+ // Open app logic
58
+ return .result()
59
+ }
60
+ }
61
+
62
+ @available(iOS 16.0, *)
63
+ struct CraftShortcuts: AppShortcutsProvider {
64
+ static var appShortcuts: [AppShortcut] {
65
+ AppShortcut(
66
+ intent: OpenCraftIntent(),
67
+ phrases: [
68
+ "Open \(.applicationName)",
69
+ "Launch \(.applicationName)"
70
+ ],
71
+ shortTitle: "Open App",
72
+ systemImageName: "app.fill"
73
+ )
74
+ }
75
+ }
76
+
77
+ // MARK: - TipKit Integration (iOS 17+)
78
+
79
+ @available(iOS 17.0, *)
80
+ struct CraftWelcomeTip: Tip {
81
+ var title: Text {
82
+ Text("Welcome to Craft")
83
+ }
84
+
85
+ var message: Text? {
86
+ Text("Get started by exploring the features")
87
+ }
88
+
89
+ var image: Image? {
90
+ Image(systemName: "star.fill")
91
+ }
92
+ }
93
+
94
+ @available(iOS 17.0, *)
95
+ class TipKitManager {
96
+ static let shared = TipKitManager()
97
+
98
+ func configure() {
99
+ try? Tips.configure([
100
+ .displayFrequency(.immediate),
101
+ .datastoreLocation(.applicationDefault)
102
+ ])
103
+ }
104
+ }
105
+
106
+ // MARK: - SharePlay Integration
107
+
108
+ @available(iOS 15.0, *)
109
+ class SharePlayManager: ObservableObject {
110
+ static let shared = SharePlayManager()
111
+
112
+ @Published var isActive = false
113
+
114
+ func startSharePlay() async throws {
115
+ // SharePlay session logic
116
+ isActive = true
117
+ }
118
+
119
+ func endSharePlay() {
120
+ isActive = false
121
+ }
122
+ }
123
+
124
+ // MARK: - App Clips Support
125
+
126
+ class AppClipManager {
127
+ static let shared = AppClipManager()
128
+
129
+ var isRunningInAppClip: Bool {
130
+ #if APPCLIP
131
+ return true
132
+ #else
133
+ return false
134
+ #endif
135
+ }
136
+
137
+ func configureAppClip() {
138
+ // App Clip specific configuration
139
+ }
140
+ }
141
+
142
+ // MARK: - Focus Filters (iOS 16+)
143
+
144
+ @available(iOS 16.0, *)
145
+ class FocusFilterManager {
146
+ static let shared = FocusFilterManager()
147
+
148
+ func getCurrentFocus() -> String? {
149
+ // Return current Focus mode if available
150
+ return nil
151
+ }
152
+
153
+ func registerFocusFilter() {
154
+ // Register app-specific Focus filter
155
+ }
156
+ }
157
+
158
+ // MARK: - StoreKit 2 Full Implementation
159
+
160
+ @available(iOS 15.0, *)
161
+ class StoreKitManager: ObservableObject {
162
+ static let shared = StoreKitManager()
163
+
164
+ @Published var products: [Product] = []
165
+ @Published var purchasedProductIDs: Set<String> = []
166
+
167
+ private var updateListenerTask: Task<Void, Error>?
168
+
169
+ func loadProducts(productIDs: [String]) async throws {
170
+ products = try await Product.products(for: productIDs)
171
+ }
172
+
173
+ func purchase(_ product: Product) async throws -> Transaction? {
174
+ let result = try await product.purchase()
175
+
176
+ switch result {
177
+ case .success(let verification):
178
+ let transaction = try checkVerified(verification)
179
+ await transaction.finish()
180
+ await updatePurchasedProducts()
181
+ return transaction
182
+
183
+ case .userCancelled, .pending:
184
+ return nil
185
+
186
+ @unknown default:
187
+ return nil
188
+ }
189
+ }
190
+
191
+ func restorePurchases() async throws {
192
+ for await result in Transaction.currentEntitlements {
193
+ let transaction = try checkVerified(result)
194
+ await updatePurchasedProducts()
195
+ }
196
+ }
197
+
198
+ func startObservingTransactions() {
199
+ updateListenerTask = Task.detached {
200
+ for await result in Transaction.updates {
201
+ do {
202
+ let transaction = try self.checkVerified(result)
203
+ await transaction.finish()
204
+ await self.updatePurchasedProducts()
205
+ } catch {
206
+ print("Transaction verification failed: \(error)")
207
+ }
208
+ }
209
+ }
210
+ }
211
+
212
+ func stopObservingTransactions() {
213
+ updateListenerTask?.cancel()
214
+ }
215
+
216
+ private func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
217
+ switch result {
218
+ case .unverified:
219
+ throw StoreError.failedVerification
220
+ case .verified(let safe):
221
+ return safe
222
+ }
223
+ }
224
+
225
+ @MainActor
226
+ private func updatePurchasedProducts() async {
227
+ for await result in Transaction.currentEntitlements {
228
+ guard case .verified(let transaction) = result else {
229
+ continue
230
+ }
231
+
232
+ if transaction.revocationDate == nil {
233
+ purchasedProductIDs.insert(transaction.productID)
234
+ } else {
235
+ purchasedProductIDs.remove(transaction.productID)
236
+ }
237
+ }
238
+ }
239
+
240
+ enum StoreError: Error {
241
+ case failedVerification
242
+ }
243
+ }
244
+
245
+ // MARK: - CarPlay Support
246
+
247
+ #if canImport(CarPlay)
248
+ import CarPlay
249
+
250
+ @available(iOS 12.0, *)
251
+ class CarPlayManager: NSObject, CPApplicationDelegate {
252
+ static let shared = CarPlayManager()
253
+
254
+ private var interfaceController: CPInterfaceController?
255
+
256
+ func application(_ application: UIApplication, didConnectCarInterfaceController interfaceController: CPInterfaceController, to window: CPWindow) {
257
+ self.interfaceController = interfaceController
258
+
259
+ let template = CPListTemplate(title: "Craft", sections: [
260
+ CPListSection(items: [
261
+ CPListItem(text: "Home", detailText: "Go to home screen")
262
+ ])
263
+ ])
264
+
265
+ interfaceController.setRootTemplate(template, animated: true)
266
+ }
267
+
268
+ func application(_ application: UIApplication, didDisconnectCarInterfaceController interfaceController: CPInterfaceController, from window: CPWindow) {
269
+ self.interfaceController = nil
270
+ }
271
+ }
272
+ #endif
273
+
274
+ // MARK: - Bridge Integration Extensions
275
+
276
+ extension CraftWebView.Coordinator {
277
+
278
+ @available(iOS 16.1, *)
279
+ func startLiveActivity(name: String, status: String, progress: Double, callbackId: String?) {
280
+ Task {
281
+ do {
282
+ try await LiveActivityManager.shared.startActivity(name: name, status: status, progress: progress)
283
+ resolveCallback(callbackId, result: ["started": true])
284
+ } catch {
285
+ rejectCallback(callbackId, error: error.localizedDescription)
286
+ }
287
+ }
288
+ }
289
+
290
+ @available(iOS 16.1, *)
291
+ func updateLiveActivity(status: String, progress: Double, callbackId: String?) {
292
+ Task {
293
+ await LiveActivityManager.shared.updateActivity(status: status, progress: progress)
294
+ resolveCallback(callbackId, result: ["updated": true])
295
+ }
296
+ }
297
+
298
+ @available(iOS 16.1, *)
299
+ func endLiveActivity(callbackId: String?) {
300
+ Task {
301
+ await LiveActivityManager.shared.endActivity()
302
+ resolveCallback(callbackId, result: ["ended": true])
303
+ }
304
+ }
305
+
306
+ @available(iOS 15.0, *)
307
+ func loadStoreProducts(productIDs: [String], callbackId: String?) {
308
+ Task {
309
+ do {
310
+ try await StoreKitManager.shared.loadProducts(productIDs: productIDs)
311
+ let products = StoreKitManager.shared.products.map { product in
312
+ [
313
+ "id": product.id,
314
+ "displayName": product.displayName,
315
+ "description": product.description,
316
+ "price": product.displayPrice
317
+ ]
318
+ }
319
+ resolveCallback(callbackId, result: products)
320
+ } catch {
321
+ rejectCallback(callbackId, error: error.localizedDescription)
322
+ }
323
+ }
324
+ }
325
+
326
+ @available(iOS 15.0, *)
327
+ func purchaseProduct(productId: String, callbackId: String?) {
328
+ Task {
329
+ do {
330
+ guard let product = StoreKitManager.shared.products.first(where: { $0.id == productId }) else {
331
+ rejectCallback(callbackId, error: "Product not found")
332
+ return
333
+ }
334
+
335
+ if let transaction = try await StoreKitManager.shared.purchase(product) {
336
+ resolveCallback(callbackId, result: [
337
+ "transactionId": transaction.id,
338
+ "productId": transaction.productID,
339
+ "purchased": true
340
+ ])
341
+ } else {
342
+ resolveCallback(callbackId, result: ["purchased": false])
343
+ }
344
+ } catch {
345
+ rejectCallback(callbackId, error: error.localizedDescription)
346
+ }
347
+ }
348
+ }
349
+
350
+ @available(iOS 15.0, *)
351
+ func restorePurchases(callbackId: String?) {
352
+ Task {
353
+ do {
354
+ try await StoreKitManager.shared.restorePurchases()
355
+ resolveCallback(callbackId, result: ["restored": true])
356
+ } catch {
357
+ rejectCallback(callbackId, error: error.localizedDescription)
358
+ }
359
+ }
360
+ }
361
+ }
@@ -0,0 +1,247 @@
1
+ import WidgetKit
2
+ import SwiftUI
3
+
4
+ // MARK: - Widget Entry
5
+ struct CraftWidgetEntry: TimelineEntry {
6
+ let date: Date
7
+ let title: String
8
+ let subtitle: String
9
+ let value: String
10
+ let iconName: String?
11
+ let configuration: ConfigurationIntent?
12
+ }
13
+
14
+ // MARK: - Widget Provider
15
+ struct CraftWidgetProvider: IntentTimelineProvider {
16
+ typealias Entry = CraftWidgetEntry
17
+ typealias Intent = ConfigurationIntent
18
+
19
+ // Shared UserDefaults for app-to-widget communication
20
+ private let sharedDefaults = UserDefaults(suiteName: "group.{{BUNDLE_ID}}.widget")
21
+
22
+ func placeholder(in context: Context) -> CraftWidgetEntry {
23
+ CraftWidgetEntry(
24
+ date: Date(),
25
+ title: "Craft Widget",
26
+ subtitle: "Loading...",
27
+ value: "",
28
+ iconName: nil,
29
+ configuration: nil
30
+ )
31
+ }
32
+
33
+ func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (CraftWidgetEntry) -> Void) {
34
+ let entry = loadWidgetData(configuration: configuration)
35
+ completion(entry)
36
+ }
37
+
38
+ func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<CraftWidgetEntry>) -> Void) {
39
+ let entry = loadWidgetData(configuration: configuration)
40
+
41
+ // Refresh every 15 minutes
42
+ let nextUpdate = Calendar.current.date(byAdding: .minute, value: 15, to: Date())!
43
+ let timeline = Timeline(entries: [entry], policy: .after(nextUpdate))
44
+ completion(timeline)
45
+ }
46
+
47
+ private func loadWidgetData(configuration: ConfigurationIntent?) -> CraftWidgetEntry {
48
+ let title = sharedDefaults?.string(forKey: "widget_title") ?? "Craft Widget"
49
+ let subtitle = sharedDefaults?.string(forKey: "widget_subtitle") ?? ""
50
+ let value = sharedDefaults?.string(forKey: "widget_value") ?? ""
51
+ let iconName = sharedDefaults?.string(forKey: "widget_icon")
52
+
53
+ return CraftWidgetEntry(
54
+ date: Date(),
55
+ title: title,
56
+ subtitle: subtitle,
57
+ value: value,
58
+ iconName: iconName,
59
+ configuration: configuration
60
+ )
61
+ }
62
+ }
63
+
64
+ // MARK: - Small Widget View
65
+ struct CraftWidgetSmallView: View {
66
+ var entry: CraftWidgetEntry
67
+
68
+ var body: some View {
69
+ VStack(alignment: .leading, spacing: 4) {
70
+ if let iconName = entry.iconName {
71
+ Image(systemName: iconName)
72
+ .font(.title2)
73
+ .foregroundColor(.accentColor)
74
+ }
75
+
76
+ Text(entry.title)
77
+ .font(.headline)
78
+ .lineLimit(2)
79
+
80
+ if !entry.subtitle.isEmpty {
81
+ Text(entry.subtitle)
82
+ .font(.caption)
83
+ .foregroundColor(.secondary)
84
+ .lineLimit(1)
85
+ }
86
+
87
+ Spacer()
88
+
89
+ if !entry.value.isEmpty {
90
+ Text(entry.value)
91
+ .font(.title)
92
+ .fontWeight(.bold)
93
+ .foregroundColor(.accentColor)
94
+ }
95
+ }
96
+ .padding()
97
+ .widgetBackground(Color(.systemBackground))
98
+ }
99
+ }
100
+
101
+ // MARK: - Medium Widget View
102
+ struct CraftWidgetMediumView: View {
103
+ var entry: CraftWidgetEntry
104
+
105
+ var body: some View {
106
+ HStack {
107
+ VStack(alignment: .leading, spacing: 4) {
108
+ if let iconName = entry.iconName {
109
+ Image(systemName: iconName)
110
+ .font(.title)
111
+ .foregroundColor(.accentColor)
112
+ }
113
+
114
+ Text(entry.title)
115
+ .font(.headline)
116
+
117
+ if !entry.subtitle.isEmpty {
118
+ Text(entry.subtitle)
119
+ .font(.subheadline)
120
+ .foregroundColor(.secondary)
121
+ }
122
+ }
123
+
124
+ Spacer()
125
+
126
+ if !entry.value.isEmpty {
127
+ Text(entry.value)
128
+ .font(.largeTitle)
129
+ .fontWeight(.bold)
130
+ .foregroundColor(.accentColor)
131
+ }
132
+ }
133
+ .padding()
134
+ .widgetBackground(Color(.systemBackground))
135
+ }
136
+ }
137
+
138
+ // MARK: - Large Widget View
139
+ struct CraftWidgetLargeView: View {
140
+ var entry: CraftWidgetEntry
141
+
142
+ var body: some View {
143
+ VStack(alignment: .leading, spacing: 8) {
144
+ HStack {
145
+ if let iconName = entry.iconName {
146
+ Image(systemName: iconName)
147
+ .font(.title)
148
+ .foregroundColor(.accentColor)
149
+ }
150
+
151
+ VStack(alignment: .leading) {
152
+ Text(entry.title)
153
+ .font(.headline)
154
+
155
+ if !entry.subtitle.isEmpty {
156
+ Text(entry.subtitle)
157
+ .font(.subheadline)
158
+ .foregroundColor(.secondary)
159
+ }
160
+ }
161
+
162
+ Spacer()
163
+ }
164
+
165
+ Divider()
166
+
167
+ if !entry.value.isEmpty {
168
+ Text(entry.value)
169
+ .font(.system(size: 48, weight: .bold))
170
+ .foregroundColor(.accentColor)
171
+ }
172
+
173
+ Spacer()
174
+
175
+ Text("Updated: \(entry.date, style: .time)")
176
+ .font(.caption2)
177
+ .foregroundColor(.secondary)
178
+ }
179
+ .padding()
180
+ .widgetBackground(Color(.systemBackground))
181
+ }
182
+ }
183
+
184
+ // MARK: - Widget Entry View
185
+ struct CraftWidgetEntryView: View {
186
+ var entry: CraftWidgetEntry
187
+ @Environment(\.widgetFamily) var family
188
+
189
+ var body: some View {
190
+ switch family {
191
+ case .systemSmall:
192
+ CraftWidgetSmallView(entry: entry)
193
+ case .systemMedium:
194
+ CraftWidgetMediumView(entry: entry)
195
+ case .systemLarge:
196
+ CraftWidgetLargeView(entry: entry)
197
+ default:
198
+ CraftWidgetSmallView(entry: entry)
199
+ }
200
+ }
201
+ }
202
+
203
+ // MARK: - Widget Configuration
204
+ @main
205
+ struct CraftWidget: Widget {
206
+ let kind: String = "CraftWidget"
207
+
208
+ var body: some WidgetConfiguration {
209
+ IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: CraftWidgetProvider()) { entry in
210
+ CraftWidgetEntryView(entry: entry)
211
+ }
212
+ .configurationDisplayName("{{APP_NAME}} Widget")
213
+ .description("Display information from {{APP_NAME}}")
214
+ .supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
215
+ }
216
+ }
217
+
218
+ // MARK: - Widget Background Extension
219
+ extension View {
220
+ func widgetBackground(_ color: Color) -> some View {
221
+ if #available(iOS 17.0, *) {
222
+ return containerBackground(color, for: .widget)
223
+ } else {
224
+ return background(color)
225
+ }
226
+ }
227
+ }
228
+
229
+ // MARK: - Configuration Intent (placeholder)
230
+ class ConfigurationIntent: INIntent {
231
+ // Add configuration properties here
232
+ }
233
+
234
+ // MARK: - Preview
235
+ struct CraftWidget_Previews: PreviewProvider {
236
+ static var previews: some View {
237
+ CraftWidgetEntryView(entry: CraftWidgetEntry(
238
+ date: Date(),
239
+ title: "My Widget",
240
+ subtitle: "Subtitle text",
241
+ value: "42",
242
+ iconName: "star.fill",
243
+ configuration: nil
244
+ ))
245
+ .previewContext(WidgetPreviewContext(family: .systemSmall))
246
+ }
247
+ }
@@ -0,0 +1,65 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>en</string>
7
+ <key>CFBundleDisplayName</key>
8
+ <string>{{APP_NAME}}</string>
9
+ <key>CFBundleExecutable</key>
10
+ <string>$(EXECUTABLE_NAME)</string>
11
+ <key>CFBundleIdentifier</key>
12
+ <string>{{BUNDLE_ID}}</string>
13
+ <key>CFBundleInfoDictionaryVersion</key>
14
+ <string>6.0</string>
15
+ <key>CFBundleName</key>
16
+ <string>{{APP_NAME}}</string>
17
+ <key>CFBundlePackageType</key>
18
+ <string>APPL</string>
19
+ <key>CFBundleShortVersionString</key>
20
+ <string>{{VERSION}}</string>
21
+ <key>CFBundleVersion</key>
22
+ <string>{{BUILD_NUMBER}}</string>
23
+ <key>LSRequiresIPhoneOS</key>
24
+ <true/>
25
+ <key>UILaunchStoryboardName</key>
26
+ <string>LaunchScreen</string>
27
+ <key>UIRequiredDeviceCapabilities</key>
28
+ <array>
29
+ <string>arm64</string>
30
+ </array>
31
+ <key>UISupportedInterfaceOrientations</key>
32
+ <array>
33
+ <string>UIInterfaceOrientationPortrait</string>
34
+ <string>UIInterfaceOrientationLandscapeLeft</string>
35
+ <string>UIInterfaceOrientationLandscapeRight</string>
36
+ </array>
37
+ <key>UIUserInterfaceStyle</key>
38
+ <string>{{UI_STYLE}}</string>
39
+ <key>UIStatusBarStyle</key>
40
+ <string>UIStatusBarStyleLightContent</string>
41
+ <key>UIViewControllerBasedStatusBarAppearance</key>
42
+ <false/>
43
+ <key>NSSpeechRecognitionUsageDescription</key>
44
+ <string>{{APP_NAME}} uses speech recognition for voice commands.</string>
45
+ <key>NSMicrophoneUsageDescription</key>
46
+ <string>{{APP_NAME}} needs microphone access for voice input.</string>
47
+ <key>NSAppTransportSecurity</key>
48
+ <dict>
49
+ <key>NSAllowsLocalNetworking</key>
50
+ <true/>
51
+ <key>NSAllowsArbitraryLoads</key>
52
+ <false/>
53
+ <key>NSExceptionDomains</key>
54
+ <dict>
55
+ <key>localhost</key>
56
+ <dict>
57
+ <key>NSExceptionAllowsInsecureHTTPLoads</key>
58
+ <true/>
59
+ <key>NSIncludesSubdomains</key>
60
+ <true/>
61
+ </dict>
62
+ </dict>
63
+ </dict>
64
+ </dict>
65
+ </plist>