@mmerterden/multi-agent-pipeline 20.36.0 → 20.37.1
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/CHANGELOG.md +37 -0
- package/docs/facts.json +2 -2
- package/docs/features.md +1 -1
- package/docs/recovery-guide.md +1 -1
- package/install/catalog-history.json +1 -1
- package/manifest.json +225 -148
- package/package.json +1 -1
- package/pipeline/commands/multi-agent/SKILL.md +33 -154
- package/pipeline/commands/multi-agent/analysis/SKILL.md +14 -68
- package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +5 -8
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +38 -109
- package/pipeline/commands/multi-agent/channels/SKILL.md +38 -167
- package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +37 -144
- package/pipeline/commands/multi-agent/design-check/SKILL.md +27 -131
- package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +22 -48
- package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +37 -180
- package/pipeline/commands/multi-agent/jira/SKILL.md +16 -46
- package/pipeline/commands/multi-agent/manual-test/SKILL.md +12 -7
- package/pipeline/commands/multi-agent/refactor/SKILL.md +23 -184
- package/pipeline/commands/multi-agent/resume/SKILL.md +30 -136
- package/pipeline/commands/multi-agent/review/SKILL.md +24 -199
- package/pipeline/commands/multi-agent/setup/SKILL.md +25 -181
- package/pipeline/commands/multi-agent/stack/SKILL.md +9 -53
- package/pipeline/commands/multi-agent/status/SKILL.md +13 -2
- package/pipeline/commands/multi-agent/store-ready/SKILL.md +27 -226
- package/pipeline/commands/multi-agent/sync/SKILL.md +35 -164
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +2 -4
- package/pipeline/commands/multi-agent/update/SKILL.md +18 -60
- package/pipeline/contract/CHANGELOG.md +9 -0
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +1 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +1 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +1 -1
- package/pipeline/contract/fixtures/runs-running.json +1 -1
- package/pipeline/contract/manifest.json +5 -5
- package/pipeline/contract/types/index.d.ts +3 -2
- package/pipeline/multi-agent-refs/audit-guide.md +20 -0
- package/pipeline/multi-agent-refs/autopilot-on/details.md +148 -0
- package/pipeline/multi-agent-refs/channels/generator.md +14 -0
- package/pipeline/multi-agent-refs/channels/preview.md +13 -0
- package/pipeline/multi-agent-refs/channels/reference.md +46 -0
- package/pipeline/multi-agent-refs/complaint-analysis/phases.md +159 -0
- package/pipeline/multi-agent-refs/design-check/export.md +1 -0
- package/pipeline/multi-agent-refs/design-check/init-inventory.md +1 -1
- package/pipeline/multi-agent-refs/design-check/scope-and-ledger.md +64 -0
- package/pipeline/multi-agent-refs/features/analysis-command-notes.md +53 -0
- package/pipeline/multi-agent-refs/features/commit-multi-repo.md +63 -0
- package/pipeline/multi-agent-refs/features/dev-multi-repo.md +51 -0
- package/pipeline/multi-agent-refs/features/test-gap.md +40 -0
- package/pipeline/multi-agent-refs/features/worktree-finalize.md +1 -1
- package/pipeline/multi-agent-refs/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/plan.md +71 -0
- package/pipeline/multi-agent-refs/ios-coding-standard/resolve-and-scan.md +135 -0
- package/pipeline/multi-agent-refs/jira/picker-detail.md +71 -0
- package/pipeline/multi-agent-refs/orchestrator/copilot-pipeline.md +88 -0
- package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +96 -1
- package/pipeline/multi-agent-refs/orchestrator/phase-3-user-test.md +12 -9
- package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -4
- package/pipeline/multi-agent-refs/phases/phase-2-dev.md +8 -47
- package/pipeline/multi-agent-refs/phases/phase-3-review.md +21 -65
- package/pipeline/multi-agent-refs/phases/phase-4-commit.md +9 -59
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +0 -31
- package/pipeline/multi-agent-refs/refactor/sweep.md +190 -0
- package/pipeline/multi-agent-refs/resume/details.md +115 -2
- package/pipeline/multi-agent-refs/review/inputs.md +33 -1
- package/pipeline/multi-agent-refs/review/module-guides.md +9 -1
- package/pipeline/multi-agent-refs/review/panel.md +75 -0
- package/pipeline/multi-agent-refs/review/valven.md +5 -1
- package/pipeline/multi-agent-refs/rules.md +1 -1
- package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +4 -0
- package/pipeline/multi-agent-refs/setup/token-save-flow.md +13 -0
- package/pipeline/multi-agent-refs/stack/background.md +72 -0
- package/pipeline/multi-agent-refs/store-ready/gates.md +139 -0
- package/pipeline/multi-agent-refs/store-ready/report.md +40 -2
- package/pipeline/multi-agent-refs/sync/office.md +10 -0
- package/pipeline/multi-agent-refs/sync/overview.md +61 -0
- package/pipeline/multi-agent-refs/sync/repo.md +91 -0
- package/pipeline/multi-agent-refs/update/details.md +83 -0
- package/pipeline/office/index.html +6 -3
- package/pipeline/office/toolkit.json +1 -1
- package/pipeline/rules/figma-pipeline.md +6 -3
- package/pipeline/schemas/prefs.schema.json +1 -1
- package/pipeline/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/_memory-policy.mjs +27 -5
- package/pipeline/scripts/checkpoint-fold.sh +59 -0
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/gate-ledger.mjs +1 -1
- package/pipeline/scripts/office-control.mjs +41 -15
- package/pipeline/scripts/offload-ref.sh +1 -1
- package/pipeline/scripts/rework-rulings.mjs +5 -3
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/scope-check-gate.mjs +15 -2
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/scripts/user-test-handoff.sh +203 -0
- package/pipeline/scripts/validate-planning.mjs +5 -3
- package/pipeline/scripts/validate-triage.mjs +2 -1
- package/pipeline/scripts/write-state.mjs +13 -4
- package/pipeline/skills/.skill-manifest.json +64 -64
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +7 -70
- package/pipeline/skills/shared/core/apple-archive-compliance/references/examples.md +67 -0
- package/pipeline/skills/shared/core/apple-archive-compliance/references/version-history.md +8 -0
- package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +10 -72
- package/pipeline/skills/shared/core/google-play-compliance/references/examples.md +69 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/notes.md +7 -0
- package/pipeline/skills/shared/core/google-play-compliance/references/version-history.md +5 -0
- package/pipeline/skills/shared/core/multi-agent/SKILL.md +39 -262
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +35 -73
- package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +37 -337
- package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +27 -122
- package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +8 -340
- package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +37 -180
- package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +12 -7
- package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +23 -184
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +14 -82
- package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +25 -277
- package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +13 -2
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +73 -217
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +11 -162
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-code.md +257 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +7 -58
- package/pipeline/skills/shared/external/android-architecture/references/code-examples.md +68 -0
- package/pipeline/skills/shared/external/app-intents/SKILL.md +3 -45
- package/pipeline/skills/shared/external/app-intents/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +5 -81
- package/pipeline/skills/shared/external/app-store-optimization/references/engagement-and-localization.md +96 -0
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +6 -112
- package/pipeline/skills/shared/external/app-store-review/references/monetization-and-submission.md +138 -0
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +10 -117
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models-code.md +186 -0
- package/pipeline/skills/shared/external/authentication/SKILL.md +3 -50
- package/pipeline/skills/shared/external/authentication/references/code-examples.md +59 -0
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +3 -84
- package/pipeline/skills/shared/external/callkit-voip/references/code-examples.md +93 -0
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +7 -263
- package/pipeline/skills/shared/external/ci-cd-pipelines/references/pipeline-examples.md +281 -0
- package/pipeline/skills/shared/external/clean-code/SKILL.md +8 -41
- package/pipeline/skills/shared/external/clean-code/references/code-examples.md +56 -0
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +8 -146
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-code.md +214 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +6 -80
- package/pipeline/skills/shared/external/compose-components/references/code-examples.md +89 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +5 -64
- package/pipeline/skills/shared/external/compose-navigation/references/code-examples.md +70 -0
- package/pipeline/skills/shared/external/core-data/SKILL.md +9 -157
- package/pipeline/skills/shared/external/core-data/references/core-data-code.md +221 -0
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +3 -89
- package/pipeline/skills/shared/external/core-nfc/references/code-examples.md +98 -0
- package/pipeline/skills/shared/external/coreml/SKILL.md +17 -133
- package/pipeline/skills/shared/external/coreml/references/coreml-code.md +208 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +8 -131
- package/pipeline/skills/shared/external/css-modern/references/code-examples.md +142 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +6 -103
- package/pipeline/skills/shared/external/database-patterns/references/orm-examples.md +112 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +7 -48
- package/pipeline/skills/shared/external/html-semantic/references/code-examples.md +54 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +6 -19
- package/pipeline/skills/shared/external/humanizer/references/voice.md +24 -0
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +13 -170
- package/pipeline/skills/shared/external/ios-accessibility/references/assistive-tech-review.md +224 -0
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +2 -24
- package/pipeline/skills/shared/external/ios-localization/references/code-examples.md +30 -0
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +3 -58
- package/pipeline/skills/shared/external/ios-networking/references/code-examples.md +67 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +4 -64
- package/pipeline/skills/shared/external/ios-security/references/code-examples.md +74 -0
- package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -90
- package/pipeline/skills/shared/external/live-activities/references/code-examples.md +108 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +9 -23
- package/pipeline/skills/shared/external/localization-reuse-map/references/project-settings.md +22 -0
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +5 -95
- package/pipeline/skills/shared/external/mapkit-location/references/code-examples.md +117 -0
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +7 -106
- package/pipeline/skills/shared/external/nextjs-app-router/references/app-router-examples.md +124 -0
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -31
- package/pipeline/skills/shared/external/permissionkit/references/code-examples.md +37 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +6 -74
- package/pipeline/skills/shared/external/play-store-review/references/signing-and-policies.md +83 -0
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -125
- package/pipeline/skills/shared/external/push-notifications/references/notification-code.md +176 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +7 -158
- package/pipeline/skills/shared/external/python-patterns/references/python-examples.md +176 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +7 -157
- package/pipeline/skills/shared/external/rest-api-design/references/api-examples.md +175 -0
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +11 -230
- package/pipeline/skills/shared/external/swift-architecture/references/pattern-examples.md +295 -0
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +17 -149
- package/pipeline/skills/shared/external/swift-codable/references/codable-recipes.md +207 -0
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +12 -99
- package/pipeline/skills/shared/external/swift-concurrency/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/swift-concurrency/references/swift-6-2-additions.md +18 -0
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +9 -78
- package/pipeline/skills/shared/external/swift-formatstyle/references/formatstyle-examples.md +113 -0
- package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -56
- package/pipeline/skills/shared/external/swift-language/references/code-examples.md +65 -0
- package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +1 -0
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +6 -122
- package/pipeline/skills/shared/external/swiftdata/references/code-examples.md +140 -0
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +12 -139
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/interop-code.md +198 -0
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +9 -81
- package/pipeline/skills/shared/external/tailwind-css/references/code-examples.md +91 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +7 -207
- package/pipeline/skills/shared/external/testing-backend/references/testing-examples.md +225 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +8 -89
- package/pipeline/skills/shared/external/typescript-patterns/references/code-examples.md +100 -0
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -20
- package/pipeline/skills/shared/external/vision-framework/references/code-examples.md +26 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +7 -169
- package/pipeline/skills/shared/external/vue-composition/references/vue-examples.md +187 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +7 -174
- package/pipeline/skills/shared/external/web-accessibility/references/a11y-examples.md +192 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +8 -66
- package/pipeline/skills/shared/external/web-performance/references/code-examples.md +77 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +11 -186
- package/pipeline/skills/shared/external/web-testing/references/testing-examples.md +213 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/format-and-output.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/publish-and-snapshot.md +0 -0
- /package/pipeline/skills/shared/external/localization-reuse-map/{reference → references}/sources-and-recipes.md +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# SwiftData Code Examples
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [Defining models](#defining-models)
|
|
5
|
+
- [Container setup](#container-setup)
|
|
6
|
+
- [Schema versions and migration](#schema-versions-and-migration)
|
|
7
|
+
<!-- /toc -->
|
|
8
|
+
|
|
9
|
+
Full code samples for the SKILL.md sections that link here. The rules and the decision guidance stay in SKILL.md; each heading below matches the SKILL.md section the code belongs to.
|
|
10
|
+
|
|
11
|
+
## Defining models
|
|
12
|
+
|
|
13
|
+
```swift
|
|
14
|
+
import SwiftData
|
|
15
|
+
import Foundation
|
|
16
|
+
|
|
17
|
+
@Model
|
|
18
|
+
class Plant {
|
|
19
|
+
var commonName: String
|
|
20
|
+
var acquiredOn: Date
|
|
21
|
+
var needsRepotting: Bool = false
|
|
22
|
+
@Attribute(.externalStorage) var photo: Data?
|
|
23
|
+
@Relationship(deleteRule: .cascade, inverse: \CareEvent.plant)
|
|
24
|
+
var careEvents: [CareEvent] = []
|
|
25
|
+
var bed: GardenBed?
|
|
26
|
+
@Transient var isHighlighted: Bool = false
|
|
27
|
+
|
|
28
|
+
init(commonName: String, acquiredOn: Date = .now) {
|
|
29
|
+
self.commonName = commonName
|
|
30
|
+
self.acquiredOn = acquiredOn
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Model
|
|
35
|
+
final class GardenBed {
|
|
36
|
+
var label: String
|
|
37
|
+
@Relationship(deleteRule: .nullify, inverse: \Plant.bed)
|
|
38
|
+
var plants: [Plant] = []
|
|
39
|
+
init(label: String) { self.label = label }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Model
|
|
43
|
+
final class CareEvent {
|
|
44
|
+
var kind: String
|
|
45
|
+
var loggedAt: Date
|
|
46
|
+
var plant: Plant?
|
|
47
|
+
|
|
48
|
+
init(kind: String, loggedAt: Date) {
|
|
49
|
+
self.kind = kind
|
|
50
|
+
self.loggedAt = loggedAt
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Container setup
|
|
56
|
+
|
|
57
|
+
```swift
|
|
58
|
+
// Every model type the app stores, listed once.
|
|
59
|
+
let simple = try ModelContainer(for: Plant.self, CareEvent.self, GardenBed.self)
|
|
60
|
+
|
|
61
|
+
// Named store, shared with extensions, synced to a private CloudKit database.
|
|
62
|
+
let config = ModelConfiguration(
|
|
63
|
+
"Garden",
|
|
64
|
+
isStoredInMemoryOnly: false,
|
|
65
|
+
groupContainer: .identifier("group.example.garden"),
|
|
66
|
+
cloudKitDatabase: .private("iCloud.example.garden")
|
|
67
|
+
)
|
|
68
|
+
let configured = try ModelContainer(for: Plant.self, configurations: config)
|
|
69
|
+
|
|
70
|
+
// Versioned schema with a migration plan.
|
|
71
|
+
let migrating = try ModelContainer(
|
|
72
|
+
for: GardenSchemaV3.Plant.self,
|
|
73
|
+
migrationPlan: GardenMigrationPlan.self
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// Tests and previews: nothing touches disk.
|
|
77
|
+
let scratch = try ModelContainer(
|
|
78
|
+
for: Plant.self,
|
|
79
|
+
configurations: ModelConfiguration(isStoredInMemoryOnly: true)
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Schema versions and migration
|
|
84
|
+
|
|
85
|
+
```swift
|
|
86
|
+
enum GardenSchemaV1: VersionedSchema {
|
|
87
|
+
static let versionIdentifier = Schema.Version(1, 0, 0)
|
|
88
|
+
static var models: [any PersistentModel.Type] { [Plant.self] }
|
|
89
|
+
|
|
90
|
+
@Model final class Plant {
|
|
91
|
+
var name: String
|
|
92
|
+
init(name: String) { self.name = name }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
enum GardenSchemaV2: VersionedSchema {
|
|
97
|
+
static let versionIdentifier = Schema.Version(2, 0, 0)
|
|
98
|
+
static var models: [any PersistentModel.Type] { [Plant.self] }
|
|
99
|
+
|
|
100
|
+
@Model final class Plant {
|
|
101
|
+
@Attribute(originalName: "name") var commonName: String
|
|
102
|
+
var sunlight: String = "partial"
|
|
103
|
+
init(commonName: String) { self.commonName = commonName }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
enum GardenSchemaV3: VersionedSchema {
|
|
108
|
+
static let versionIdentifier = Schema.Version(3, 0, 0)
|
|
109
|
+
static var models: [any PersistentModel.Type] { [Plant.self] }
|
|
110
|
+
|
|
111
|
+
@Model final class Plant {
|
|
112
|
+
var commonName: String
|
|
113
|
+
var sunlight: String = "partial"
|
|
114
|
+
var sunlightHours: Int = 4
|
|
115
|
+
init(commonName: String) { self.commonName = commonName }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
enum GardenMigrationPlan: SchemaMigrationPlan {
|
|
120
|
+
static var schemas: [any VersionedSchema.Type] {
|
|
121
|
+
[GardenSchemaV1.self, GardenSchemaV2.self, GardenSchemaV3.self]
|
|
122
|
+
}
|
|
123
|
+
static var stages: [MigrationStage] {
|
|
124
|
+
[
|
|
125
|
+
.lightweight(fromVersion: GardenSchemaV1.self, toVersion: GardenSchemaV2.self),
|
|
126
|
+
.custom(
|
|
127
|
+
fromVersion: GardenSchemaV2.self, toVersion: GardenSchemaV3.self,
|
|
128
|
+
willMigrate: nil,
|
|
129
|
+
didMigrate: { context in
|
|
130
|
+
let plants = try context.fetch(FetchDescriptor<GardenSchemaV3.Plant>())
|
|
131
|
+
for plant in plants {
|
|
132
|
+
plant.sunlightHours = plant.sunlight == "full" ? 8 : 4
|
|
133
|
+
}
|
|
134
|
+
try context.save()
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
@@ -52,39 +52,7 @@ are required:
|
|
|
52
52
|
the view already holds can trigger a delegate callback that writes the binding
|
|
53
53
|
that calls update again.
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
import UIKit
|
|
57
|
-
import SwiftUI
|
|
58
|
-
|
|
59
|
-
struct PlainTextEditor: UIViewRepresentable {
|
|
60
|
-
@Binding var draft: String
|
|
61
|
-
|
|
62
|
-
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
63
|
-
|
|
64
|
-
func makeUIView(context: Context) -> UITextView {
|
|
65
|
-
let editor = UITextView(frame: .zero)
|
|
66
|
-
editor.delegate = context.coordinator
|
|
67
|
-
editor.font = .preferredFont(forTextStyle: .body)
|
|
68
|
-
return editor
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
func updateUIView(_ editor: UITextView, context: Context) {
|
|
72
|
-
context.coordinator.parent = self
|
|
73
|
-
guard editor.text != draft else { return }
|
|
74
|
-
editor.text = draft
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@MainActor
|
|
78
|
-
final class Coordinator: NSObject, UITextViewDelegate {
|
|
79
|
-
init(parent: PlainTextEditor) { self.parent = parent }
|
|
80
|
-
var parent: PlainTextEditor
|
|
81
|
-
|
|
82
|
-
func textViewDidChange(_ editor: UITextView) {
|
|
83
|
-
parent.draft = editor.text
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
```
|
|
55
|
+
Code for this section: [Interop code](references/interop-code.md).
|
|
88
56
|
|
|
89
57
|
### Lifecycle order
|
|
90
58
|
|
|
@@ -141,28 +109,7 @@ and `makeCoordinator()`. For a modal controller the update method usually
|
|
|
141
109
|
has nothing to push after presentation; it only refreshes the coordinator's
|
|
142
110
|
`parent`.
|
|
143
111
|
|
|
144
|
-
|
|
145
|
-
import VisionKit
|
|
146
|
-
import SwiftUI
|
|
147
|
-
|
|
148
|
-
struct PageScannerView: UIViewControllerRepresentable {
|
|
149
|
-
@Environment(\.dismiss) var close
|
|
150
|
-
@Binding var pages: [UIImage]
|
|
151
|
-
|
|
152
|
-
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
153
|
-
|
|
154
|
-
func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
|
|
155
|
-
let camera = VNDocumentCameraViewController()
|
|
156
|
-
camera.delegate = context.coordinator
|
|
157
|
-
return camera
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
func updateUIViewController(_ camera: VNDocumentCameraViewController,
|
|
161
|
-
context: Context) {
|
|
162
|
-
context.coordinator.parent = self
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
112
|
+
Code for this section: [Interop code](references/interop-code.md).
|
|
166
113
|
|
|
167
114
|
### Returning results from a presented controller
|
|
168
115
|
|
|
@@ -170,35 +117,12 @@ Delegate callbacks land in the Coordinator, which passes results on by writing
|
|
|
170
117
|
a `@Binding` or calling a closure stored on `parent`. Every exit path, success, cancel
|
|
171
118
|
and failure, ends by dismissing.
|
|
172
119
|
|
|
173
|
-
```swift
|
|
174
|
-
extension PageScannerView {
|
|
175
|
-
@MainActor
|
|
176
|
-
final class Coordinator: NSObject, @preconcurrency VNDocumentCameraViewControllerDelegate {
|
|
177
|
-
init(parent: PageScannerView) { self.parent = parent }
|
|
178
|
-
var parent: PageScannerView
|
|
179
|
-
|
|
180
|
-
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
181
|
-
didFinishWith result: VNDocumentCameraScan) {
|
|
182
|
-
parent.pages = (0..<result.pageCount).map { index in result.imageOfPage(at: index) }
|
|
183
|
-
parent.close()
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
|
|
187
|
-
parent.close()
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
191
|
-
didFailWithError failure: Error) {
|
|
192
|
-
parent.close()
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
120
|
The coordinator is a `final class` that subclasses `NSObject`, adopts the
|
|
199
121
|
delegate protocol, stores `parent`, and is built from `makeCoordinator()` by
|
|
200
122
|
passing `self`.
|
|
201
123
|
|
|
124
|
+
Code for this section: [Interop code](references/interop-code.md).
|
|
125
|
+
|
|
202
126
|
## 3. The Coordinator Pattern
|
|
203
127
|
|
|
204
128
|
### Why it exists
|
|
@@ -214,40 +138,7 @@ Declare the Coordinator as a nested type of the representable, directly or
|
|
|
214
138
|
through an extension. Give it a `parent` property holding the representable
|
|
215
139
|
struct; that is how it writes to `@Binding` properties and calls closures.
|
|
216
140
|
|
|
217
|
-
|
|
218
|
-
struct LegacySearchField: UIViewRepresentable {
|
|
219
|
-
@Binding var query: String
|
|
220
|
-
var onSubmit: (String) -> Void
|
|
221
|
-
|
|
222
|
-
func makeCoordinator() -> Coordinator { .init(parent: self) }
|
|
223
|
-
|
|
224
|
-
func makeUIView(context: Context) -> UISearchBar {
|
|
225
|
-
let bar = UISearchBar(frame: .zero)
|
|
226
|
-
bar.delegate = context.coordinator
|
|
227
|
-
return bar
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
func updateUIView(_ bar: UISearchBar, context: Context) {
|
|
231
|
-
context.coordinator.parent = self
|
|
232
|
-
if bar.text != query { bar.text = query }
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
@MainActor
|
|
236
|
-
final class Coordinator: NSObject, UISearchBarDelegate {
|
|
237
|
-
init(parent: LegacySearchField) { self.parent = parent }
|
|
238
|
-
var parent: LegacySearchField
|
|
239
|
-
|
|
240
|
-
func searchBar(_ bar: UISearchBar, textDidChange newText: String) {
|
|
241
|
-
parent.query = newText
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
func searchBarSearchButtonClicked(_ bar: UISearchBar) {
|
|
245
|
-
parent.onSubmit(parent.query)
|
|
246
|
-
bar.resignFirstResponder()
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
```
|
|
141
|
+
Code for this section: [Interop code](references/interop-code.md).
|
|
251
142
|
|
|
252
143
|
### Rules
|
|
253
144
|
|
|
@@ -267,34 +158,15 @@ struct LegacySearchField: UIViewRepresentable {
|
|
|
267
158
|
### Basic embedding
|
|
268
159
|
|
|
269
160
|
`UIHostingController(rootView:)` puts a SwiftUI view inside a UIKit
|
|
270
|
-
controller. Containment has three steps and the order is fixed:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
final class ProfileScreenController: UIViewController {
|
|
274
|
-
let summaryHost = UIHostingController(rootView: ProfileSummary(profile: .empty))
|
|
275
|
-
|
|
276
|
-
override func viewDidLoad() {
|
|
277
|
-
super.viewDidLoad()
|
|
278
|
-
addChild(summaryHost) // 1
|
|
279
|
-
|
|
280
|
-
let hosted: UIView = summaryHost.view
|
|
281
|
-
hosted.translatesAutoresizingMaskIntoConstraints = false // 2
|
|
282
|
-
view.addSubview(hosted)
|
|
283
|
-
NSLayoutConstraint.activate([
|
|
284
|
-
hosted.topAnchor.constraint(equalTo: view.topAnchor),
|
|
285
|
-
hosted.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
|
286
|
-
hosted.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
287
|
-
hosted.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
|
288
|
-
])
|
|
289
|
-
|
|
290
|
-
summaryHost.didMove(toParent: self) // 3
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
```
|
|
161
|
+
controller. Containment has three steps and the order is fixed: `addChild(_:)`, add the
|
|
162
|
+
hosting view to the hierarchy with its constraints, then
|
|
163
|
+
`didMove(toParent:)`.
|
|
294
164
|
|
|
295
165
|
Leave out any step and containment callbacks, appearance transitions and trait
|
|
296
166
|
propagation stop working correctly.
|
|
297
167
|
|
|
168
|
+
Code for this section: [Interop code](references/interop-code.md).
|
|
169
|
+
|
|
298
170
|
### Sizing options (iOS 16+)
|
|
299
171
|
|
|
300
172
|
`sizingOptions` is an option set on the hosting controller:
|
|
@@ -353,7 +225,7 @@ depend on this.
|
|
|
353
225
|
|
|
354
226
|
**Two-way with `@Binding`.** The coordinator writes `parent.value` from a
|
|
355
227
|
delegate method; `updateUIView` reads the binding and applies it to the view.
|
|
356
|
-
The `PlainTextEditor`
|
|
228
|
+
The `PlainTextEditor` wrapper in the interop code reference does both halves (`textViewDidChange(_:)` writes,
|
|
357
229
|
the update method applies).
|
|
358
230
|
|
|
359
231
|
**One-way with closures.** For events that flow only from UIKit to SwiftUI
|
|
@@ -431,6 +303,7 @@ is what ends that cycle.
|
|
|
431
303
|
|
|
432
304
|
## References
|
|
433
305
|
|
|
306
|
+
- [Interop code](references/interop-code.md): open for the code behind a representable wrapper, a coordinator, a presented controller that returns a result, or embedding SwiftUI in UIKit.
|
|
434
307
|
- [wrapper recipes](references/representable-recipes.md): complete wrapper recipes
|
|
435
308
|
- [migration guide](references/hosting-migration.md): incremental UIKit to SwiftUI migration
|
|
436
309
|
- Apple: [UIViewRepresentable](https://developer.apple.com/documentation/swiftui/uiviewrepresentable),
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Interop code
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [1. UIViewRepresentable](#1-uiviewrepresentable)
|
|
5
|
+
- [2. UIViewControllerRepresentable](#2-uiviewcontrollerrepresentable)
|
|
6
|
+
- [Returning results from a presented controller](#returning-results-from-a-presented-controller)
|
|
7
|
+
- [Shape](#shape)
|
|
8
|
+
- [Basic embedding](#basic-embedding)
|
|
9
|
+
<!-- /toc -->
|
|
10
|
+
|
|
11
|
+
Code for the representable protocols, returning results from a presented controller, the coordinator shape and basic hosting. SKILL.md keeps lifecycle, rules and review guidance.
|
|
12
|
+
|
|
13
|
+
## 1. UIViewRepresentable
|
|
14
|
+
|
|
15
|
+
`UIViewRepresentable` lets SwiftUI display any `UIView` subclass. Two methods
|
|
16
|
+
are required:
|
|
17
|
+
|
|
18
|
+
- `makeUIView(context:)` builds and returns the UIKit view. SwiftUI calls it a
|
|
19
|
+
single time, when the representable is inserted. Put one-time setup here:
|
|
20
|
+
delegate assignment, fonts, static configuration.
|
|
21
|
+
- `updateUIView(_:context:)` pushes SwiftUI state into the view. It runs again
|
|
22
|
+
for every relevant state change, so compare before you assign. Writing a value
|
|
23
|
+
the view already holds can trigger a delegate callback that writes the binding
|
|
24
|
+
that calls update again.
|
|
25
|
+
|
|
26
|
+
```swift
|
|
27
|
+
import UIKit
|
|
28
|
+
import SwiftUI
|
|
29
|
+
|
|
30
|
+
struct PlainTextEditor: UIViewRepresentable {
|
|
31
|
+
@Binding var draft: String
|
|
32
|
+
|
|
33
|
+
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
34
|
+
|
|
35
|
+
func makeUIView(context: Context) -> UITextView {
|
|
36
|
+
let editor = UITextView(frame: .zero)
|
|
37
|
+
editor.delegate = context.coordinator
|
|
38
|
+
editor.font = .preferredFont(forTextStyle: .body)
|
|
39
|
+
return editor
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func updateUIView(_ editor: UITextView, context: Context) {
|
|
43
|
+
context.coordinator.parent = self
|
|
44
|
+
guard editor.text != draft else { return }
|
|
45
|
+
editor.text = draft
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@MainActor
|
|
49
|
+
final class Coordinator: NSObject, UITextViewDelegate {
|
|
50
|
+
init(parent: PlainTextEditor) { self.parent = parent }
|
|
51
|
+
var parent: PlainTextEditor
|
|
52
|
+
|
|
53
|
+
func textViewDidChange(_ editor: UITextView) {
|
|
54
|
+
parent.draft = editor.text
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 2. UIViewControllerRepresentable
|
|
61
|
+
|
|
62
|
+
Wrap a `UIViewController` subclass with `UIViewControllerRepresentable`. It is
|
|
63
|
+
the right choice for pickers the system provides, a scanner for documents, the
|
|
64
|
+
mail composer, and other controllers that are normally presented modally.
|
|
65
|
+
The methods mirror the view version: `makeUIViewController(context:)`, `updateUIViewController(_:context:)`
|
|
66
|
+
and `makeCoordinator()`. For a modal controller the update method usually
|
|
67
|
+
has nothing to push after presentation; it only refreshes the coordinator's
|
|
68
|
+
`parent`.
|
|
69
|
+
|
|
70
|
+
```swift
|
|
71
|
+
import VisionKit
|
|
72
|
+
import SwiftUI
|
|
73
|
+
|
|
74
|
+
struct PageScannerView: UIViewControllerRepresentable {
|
|
75
|
+
@Environment(\.dismiss) var close
|
|
76
|
+
@Binding var pages: [UIImage]
|
|
77
|
+
|
|
78
|
+
func makeCoordinator() -> Coordinator { Coordinator(parent: self) }
|
|
79
|
+
|
|
80
|
+
func makeUIViewController(context: Context) -> VNDocumentCameraViewController {
|
|
81
|
+
let camera = VNDocumentCameraViewController()
|
|
82
|
+
camera.delegate = context.coordinator
|
|
83
|
+
return camera
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func updateUIViewController(_ camera: VNDocumentCameraViewController,
|
|
87
|
+
context: Context) {
|
|
88
|
+
context.coordinator.parent = self
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Returning results from a presented controller
|
|
94
|
+
|
|
95
|
+
Delegate callbacks land in the Coordinator, which passes results on by writing
|
|
96
|
+
a `@Binding` or calling a closure stored on `parent`. Every exit path, success, cancel
|
|
97
|
+
and failure, ends by dismissing.
|
|
98
|
+
|
|
99
|
+
```swift
|
|
100
|
+
extension PageScannerView {
|
|
101
|
+
@MainActor
|
|
102
|
+
final class Coordinator: NSObject, @preconcurrency VNDocumentCameraViewControllerDelegate {
|
|
103
|
+
init(parent: PageScannerView) { self.parent = parent }
|
|
104
|
+
var parent: PageScannerView
|
|
105
|
+
|
|
106
|
+
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
107
|
+
didFinishWith result: VNDocumentCameraScan) {
|
|
108
|
+
parent.pages = (0..<result.pageCount).map { index in result.imageOfPage(at: index) }
|
|
109
|
+
parent.close()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
func documentCameraViewControllerDidCancel(_ camera: VNDocumentCameraViewController) {
|
|
113
|
+
parent.close()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func documentCameraViewController(_ camera: VNDocumentCameraViewController,
|
|
117
|
+
didFailWithError failure: Error) {
|
|
118
|
+
parent.close()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The coordinator is a `final class` that subclasses `NSObject`, adopts the
|
|
125
|
+
delegate protocol, stores `parent`, and is built from `makeCoordinator()` by
|
|
126
|
+
passing `self`.
|
|
127
|
+
|
|
128
|
+
### Shape
|
|
129
|
+
|
|
130
|
+
Declare the Coordinator as a nested type of the representable, directly or
|
|
131
|
+
through an extension. Give it a `parent` property holding the representable
|
|
132
|
+
struct; that is how it writes to `@Binding` properties and calls closures.
|
|
133
|
+
|
|
134
|
+
```swift
|
|
135
|
+
struct LegacySearchField: UIViewRepresentable {
|
|
136
|
+
@Binding var query: String
|
|
137
|
+
var onSubmit: (String) -> Void
|
|
138
|
+
|
|
139
|
+
func makeCoordinator() -> Coordinator { .init(parent: self) }
|
|
140
|
+
|
|
141
|
+
func makeUIView(context: Context) -> UISearchBar {
|
|
142
|
+
let bar = UISearchBar(frame: .zero)
|
|
143
|
+
bar.delegate = context.coordinator
|
|
144
|
+
return bar
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
func updateUIView(_ bar: UISearchBar, context: Context) {
|
|
148
|
+
context.coordinator.parent = self
|
|
149
|
+
if bar.text != query { bar.text = query }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@MainActor
|
|
153
|
+
final class Coordinator: NSObject, UISearchBarDelegate {
|
|
154
|
+
init(parent: LegacySearchField) { self.parent = parent }
|
|
155
|
+
var parent: LegacySearchField
|
|
156
|
+
|
|
157
|
+
func searchBar(_ bar: UISearchBar, textDidChange newText: String) {
|
|
158
|
+
parent.query = newText
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
func searchBarSearchButtonClicked(_ bar: UISearchBar) {
|
|
162
|
+
parent.onSubmit(parent.query)
|
|
163
|
+
bar.resignFirstResponder()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Basic embedding
|
|
170
|
+
|
|
171
|
+
`UIHostingController(rootView:)` puts a SwiftUI view inside a UIKit
|
|
172
|
+
controller. Containment has three steps and the order is fixed:
|
|
173
|
+
|
|
174
|
+
```swift
|
|
175
|
+
final class ProfileScreenController: UIViewController {
|
|
176
|
+
let summaryHost = UIHostingController(rootView: ProfileSummary(profile: .empty))
|
|
177
|
+
|
|
178
|
+
override func viewDidLoad() {
|
|
179
|
+
super.viewDidLoad()
|
|
180
|
+
addChild(summaryHost) // 1
|
|
181
|
+
|
|
182
|
+
let hosted: UIView = summaryHost.view
|
|
183
|
+
hosted.translatesAutoresizingMaskIntoConstraints = false // 2
|
|
184
|
+
view.addSubview(hosted)
|
|
185
|
+
NSLayoutConstraint.activate([
|
|
186
|
+
hosted.topAnchor.constraint(equalTo: view.topAnchor),
|
|
187
|
+
hosted.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
|
188
|
+
hosted.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
189
|
+
hosted.trailingAnchor.constraint(equalTo: view.trailingAnchor)
|
|
190
|
+
])
|
|
191
|
+
|
|
192
|
+
summaryHost.didMove(toParent: self) // 3
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Leave out any step and containment callbacks, appearance transitions and trait
|
|
198
|
+
propagation stop working correctly.
|
|
@@ -123,91 +123,15 @@ Content detection is automatic with no `content` array needed.
|
|
|
123
123
|
|
|
124
124
|
### Form
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
<form class="space-y-6 max-w-md mx-auto">
|
|
128
|
-
<div>
|
|
129
|
-
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
130
|
-
Email
|
|
131
|
-
</label>
|
|
132
|
-
<input
|
|
133
|
-
type="email"
|
|
134
|
-
id="email"
|
|
135
|
-
class="
|
|
136
|
-
mt-1 block w-full rounded-lg border border-gray-300
|
|
137
|
-
px-4 py-2.5 text-gray-900
|
|
138
|
-
shadow-sm
|
|
139
|
-
placeholder:text-gray-400
|
|
140
|
-
focus:border-brand focus:ring-2 focus:ring-brand/20 focus:outline-none
|
|
141
|
-
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100
|
|
142
|
-
dark:focus:border-brand-light
|
|
143
|
-
"
|
|
144
|
-
placeholder="you@example.com"
|
|
145
|
-
/>
|
|
146
|
-
</div>
|
|
126
|
+
Every input has a `<label for>`, focus styles use `focus:ring` plus `focus:outline-none`, each state has a `dark:` variant, helper text sits under the field, and the submit button carries `disabled:` styles.
|
|
147
127
|
|
|
148
|
-
|
|
149
|
-
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
|
|
150
|
-
Password
|
|
151
|
-
</label>
|
|
152
|
-
<input
|
|
153
|
-
type="password"
|
|
154
|
-
id="password"
|
|
155
|
-
class="
|
|
156
|
-
mt-1 block w-full rounded-lg border border-gray-300
|
|
157
|
-
px-4 py-2.5 text-gray-900
|
|
158
|
-
shadow-sm
|
|
159
|
-
focus:border-brand focus:ring-2 focus:ring-brand/20 focus:outline-none
|
|
160
|
-
dark:border-gray-600 dark:bg-gray-800 dark:text-gray-100
|
|
161
|
-
"
|
|
162
|
-
/>
|
|
163
|
-
<p class="mt-1 text-xs text-gray-500">Must be at least 8 characters.</p>
|
|
164
|
-
</div>
|
|
165
|
-
|
|
166
|
-
<button
|
|
167
|
-
type="submit"
|
|
168
|
-
class="
|
|
169
|
-
w-full rounded-lg bg-brand px-4 py-2.5
|
|
170
|
-
text-sm font-semibold text-white
|
|
171
|
-
shadow-sm
|
|
172
|
-
hover:bg-brand-dark focus:ring-2 focus:ring-brand/50 focus:outline-none
|
|
173
|
-
disabled:opacity-50 disabled:cursor-not-allowed
|
|
174
|
-
transition-colors
|
|
175
|
-
"
|
|
176
|
-
>
|
|
177
|
-
Sign in
|
|
178
|
-
</button>
|
|
179
|
-
</form>
|
|
180
|
-
```
|
|
128
|
+
Code: [Form](references/code-examples.md#form).
|
|
181
129
|
|
|
182
130
|
### Navigation
|
|
183
131
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
<div class="flex h-16 items-center justify-between">
|
|
188
|
-
<div class="flex items-center gap-8">
|
|
189
|
-
<a href="/" class="text-xl font-bold text-brand">Logo</a>
|
|
190
|
-
<div class="hidden md:flex gap-6">
|
|
191
|
-
<a href="#" class="text-sm font-medium text-gray-900 dark:text-gray-100 hover:text-brand">
|
|
192
|
-
Home
|
|
193
|
-
</a>
|
|
194
|
-
<a href="#" class="text-sm font-medium text-gray-500 dark:text-gray-400 hover:text-brand">
|
|
195
|
-
Products
|
|
196
|
-
</a>
|
|
197
|
-
<a href="#" class="text-sm font-medium text-gray-500 dark:text-gray-400 hover:text-brand">
|
|
198
|
-
About
|
|
199
|
-
</a>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
<button class="md:hidden p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800">
|
|
203
|
-
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
204
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
205
|
-
</svg>
|
|
206
|
-
</button>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</nav>
|
|
210
|
-
```
|
|
132
|
+
A bordered `<nav>` with a centered `max-w-7xl` container, inline links shown from `md:` up, and a menu button visible only below `md:`.
|
|
133
|
+
|
|
134
|
+
Code: [Navigation](references/code-examples.md#navigation).
|
|
211
135
|
|
|
212
136
|
## The cn() Utility
|
|
213
137
|
|
|
@@ -310,3 +234,7 @@ function Button({ variant = 'primary', size = 'md', className, children }: Butto
|
|
|
310
234
|
| Custom color not found | Missing `@theme` definition | Define custom values in `@theme` block |
|
|
311
235
|
| Container queries not working | Missing `@container` parent | Wrap parent element with `@container` class |
|
|
312
236
|
| Arbitrary value not compiled | Syntax error in brackets | Check for spaces: `w-[calc(100%-2rem)]` |
|
|
237
|
+
|
|
238
|
+
## References
|
|
239
|
+
|
|
240
|
+
- [Code examples](references/code-examples.md): open for the full code behind Form, Navigation.
|