@mmerterden/multi-agent-pipeline 20.35.0 → 20.37.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/CHANGELOG.md +26 -0
- package/README.md +6 -5
- package/README.tr.md +6 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +4 -4
- package/install/catalog-history.json +1 -1
- package/manifest.json +210 -136
- package/package.json +5 -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/office-pair/SKILL.md +66 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +1 -1
- 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 +38 -167
- 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/lib/qr-code.mjs +26 -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/cross-cli-contract.md +4 -4
- 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/garbage-collect/phases.md +60 -0
- package/pipeline/multi-agent-refs/help/en.md +1 -0
- package/pipeline/multi-agent-refs/help/tr.md +1 -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/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/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/schemas/runs-index.schema.json +2 -1
- package/pipeline/scripts/contract-server.mjs +1 -1
- package/pipeline/scripts/office-pair.mjs +40 -9
- package/pipeline/scripts/runs-index.mjs +34 -7
- package/pipeline/scripts/status-overview.mjs +401 -0
- package/pipeline/skills/.skill-manifest.json +69 -65
- package/pipeline/skills/.skills-index.json +15 -4
- package/pipeline/skills/shared/README.md +4 -3
- package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +8 -71
- 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 +11 -73
- 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-office-pair/SKILL.md +19 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +1 -1
- 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 +75 -219
- 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/skills-index.md +2 -1
- /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,98 @@
|
|
|
1
|
+
# Core NFC Code Examples
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Reading NDEF data
|
|
6
|
+
|
|
7
|
+
```swift
|
|
8
|
+
import CoreNFC
|
|
9
|
+
|
|
10
|
+
final class BadgeReader: NSObject, NFCNDEFReaderSessionDelegate {
|
|
11
|
+
private var session: NFCNDEFReaderSession?
|
|
12
|
+
|
|
13
|
+
func scan() {
|
|
14
|
+
guard NFCNDEFReaderSession.readingAvailable else { return }
|
|
15
|
+
session = NFCNDEFReaderSession(
|
|
16
|
+
delegate: self,
|
|
17
|
+
queue: nil,
|
|
18
|
+
invalidateAfterFirstRead: false
|
|
19
|
+
)
|
|
20
|
+
session?.alertMessage = "Hold the top of your iPhone near the badge."
|
|
21
|
+
session?.begin()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func readerSessionDidBecomeActive(_ session: NFCNDEFReaderSession) {}
|
|
25
|
+
|
|
26
|
+
func readerSession(_ reader: NFCNDEFReaderSession, didDetectNDEFs found: [NFCNDEFMessage]) {
|
|
27
|
+
for message in found {
|
|
28
|
+
for record in message.records {
|
|
29
|
+
handle(record)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func readerSession(_ reader: NFCNDEFReaderSession, didInvalidateWithError error: any Error) {
|
|
35
|
+
if let nfcError = error as? NFCReaderError,
|
|
36
|
+
nfcError.code == .readerSessionInvalidationErrorFirstNDEFTagRead
|
|
37
|
+
|| nfcError.code == .readerSessionInvalidationErrorUserCanceled {
|
|
38
|
+
// Normal end of a scan, not a failure.
|
|
39
|
+
} else {
|
|
40
|
+
report(error)
|
|
41
|
+
}
|
|
42
|
+
self.session = nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private func handle(_ record: NFCNDEFPayload) {}
|
|
46
|
+
private func report(_ error: Error) {}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Native tag access
|
|
51
|
+
|
|
52
|
+
```swift
|
|
53
|
+
final class CardReader: NSObject, NFCTagReaderSessionDelegate {
|
|
54
|
+
private var session: NFCTagReaderSession?
|
|
55
|
+
|
|
56
|
+
func scan() {
|
|
57
|
+
guard NFCTagReaderSession.readingAvailable else { return }
|
|
58
|
+
session = NFCTagReaderSession(pollingOption: [.iso14443, .iso15693], delegate: self, queue: nil)
|
|
59
|
+
session?.alertMessage = "Hold your transit card near the phone."
|
|
60
|
+
session?.begin()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func tagReaderSessionDidBecomeActive(_ session: NFCTagReaderSession) {}
|
|
64
|
+
|
|
65
|
+
func tagReaderSession(_ reader: NFCTagReaderSession, didDetect found: [NFCTag]) {
|
|
66
|
+
if found.count > 1 {
|
|
67
|
+
retryAfterMultipleTags(reader)
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
guard !found.isEmpty else {
|
|
71
|
+
reader.restartPolling()
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
nonisolated(unsafe) let reader = reader
|
|
75
|
+
nonisolated(unsafe) let tags = found
|
|
76
|
+
Task {
|
|
77
|
+
let tag = tags[0]
|
|
78
|
+
do {
|
|
79
|
+
try await reader.connect(to: tag)
|
|
80
|
+
} catch {
|
|
81
|
+
reader.invalidate(errorMessage: error.localizedDescription)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
switch tag {
|
|
85
|
+
case .iso7816(let card): await select(card, in: reader)
|
|
86
|
+
case .miFare(let chip): await inspect(chip, in: reader)
|
|
87
|
+
case .iso15693(let label): await readBlocks(label, in: reader)
|
|
88
|
+
case .feliCa(let card): await readSystemCodes(card, in: reader)
|
|
89
|
+
@unknown default: reader.invalidate(errorMessage: "This tag type is not handled.")
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func tagReaderSession(_ reader: NFCTagReaderSession, didInvalidateWithError error: any Error) {
|
|
95
|
+
session = nil
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
@@ -28,29 +28,8 @@ NaturalLanguage, compute plans, lifecycle, errors, tests) is in
|
|
|
28
28
|
generates a Swift class with typed inputs and outputs. Use it whenever it
|
|
29
29
|
exists.
|
|
30
30
|
|
|
31
|
-
```swift
|
|
32
|
-
import CoreML
|
|
33
|
-
|
|
34
|
-
func makeDenoiser() throws -> PhotoDenoiser {
|
|
35
|
-
let settings = MLModelConfiguration()
|
|
36
|
-
settings.computeUnits = .all
|
|
37
|
-
return try PhotoDenoiser(configuration: settings)
|
|
38
|
-
}
|
|
39
|
-
```
|
|
40
|
-
|
|
41
31
|
**From a URL.** For models that are downloaded or stored outside the bundle:
|
|
42
32
|
|
|
43
|
-
```swift
|
|
44
|
-
import CoreML
|
|
45
|
-
|
|
46
|
-
func bundledModel(named name: String) throws -> MLModel {
|
|
47
|
-
guard let url = Bundle.main.url(forResource: name, withExtension: "mlmodelc") else {
|
|
48
|
-
throw CocoaError(.fileNoSuchFile)
|
|
49
|
-
}
|
|
50
|
-
return try MLModel(contentsOf: url, configuration: MLModelConfiguration())
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
33
|
**Asynchronously.** `MLModel.load(contentsOf:configuration:)` (async, iOS 15+)
|
|
55
34
|
keeps large models from stalling the main thread and is the preferred loader.
|
|
56
35
|
|
|
@@ -66,6 +45,8 @@ how server-delivered models become loadable. Two rules travel together:
|
|
|
66
45
|
When reviewing runtime-loaded models, state both: the async compile API needs
|
|
67
46
|
iOS 16, and its output must be cached.
|
|
68
47
|
|
|
48
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
49
|
+
|
|
69
50
|
## Configuration and compute units
|
|
70
51
|
|
|
71
52
|
`MLModelConfiguration` chooses hardware, GPU precision and model parameters.
|
|
@@ -93,34 +74,12 @@ Choose `.cpuOnly` only after profiling says so.
|
|
|
93
74
|
|
|
94
75
|
## Predictions
|
|
95
76
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
```swift
|
|
99
|
-
import CoreML
|
|
100
|
-
|
|
101
|
-
func tagScene(_ frame: CVPixelBuffer, with model: SceneTagger) throws -> (String, Double) {
|
|
102
|
-
let result = try model.prediction(input: SceneTaggerInput(image: frame))
|
|
103
|
-
return (result.classLabel, result.classLabelProbs[result.classLabel] ?? 0)
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
`classLabel` is the winning label and `classLabelProbs` maps every label to
|
|
77
|
+
With the generated class, `classLabel` is the winning label and `classLabelProbs` maps every label to
|
|
108
78
|
its probability.
|
|
109
79
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
```swift
|
|
113
|
-
import CoreML
|
|
80
|
+
A dictionary feature provider covers inputs only known at run time.
|
|
114
81
|
|
|
115
|
-
|
|
116
|
-
let inputs = try MLDictionaryFeatureProvider(dictionary: [
|
|
117
|
-
"image": MLFeatureValue(pixelBuffer: frame),
|
|
118
|
-
"threshold": MLFeatureValue(double: cutoff)
|
|
119
|
-
])
|
|
120
|
-
let outputs = try await model.prediction(from: inputs)
|
|
121
|
-
return outputs.featureValue(for: "label")?.stringValue
|
|
122
|
-
}
|
|
123
|
-
```
|
|
82
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
124
83
|
|
|
125
84
|
### Sync or async prediction
|
|
126
85
|
|
|
@@ -215,59 +174,21 @@ tensors.
|
|
|
215
174
|
`MLMultiArray` is the exchange type for non-image inputs and outputs; generated
|
|
216
175
|
classes use it for array features.
|
|
217
176
|
|
|
218
|
-
```swift
|
|
219
|
-
import CoreML
|
|
220
|
-
|
|
221
|
-
func embeddingInput(tokens: Int, width: Int) throws -> MLMultiArray {
|
|
222
|
-
let grid = try MLMultiArray(shape: [1, NSNumber(value: tokens), NSNumber(value: width)], dataType: .float32)
|
|
223
|
-
grid[[0, 0, 0] as [NSNumber]] = 0.25
|
|
224
|
-
return grid
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
func firstValue(of grid: MLMultiArray) -> Float {
|
|
228
|
-
grid[[0, 0, 0] as [NSNumber]].floatValue
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
func packed(_ values: [Float]) -> MLMultiArray {
|
|
232
|
-
MLMultiArray(MLShapedArray<Float>(scalars: values, shape: [1, values.count]))
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
177
|
The shape reads batch, sequence, features. Tokenized text and audio feature
|
|
237
178
|
patterns are in the reference.
|
|
238
179
|
|
|
180
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
181
|
+
|
|
239
182
|
## Image input
|
|
240
183
|
|
|
241
184
|
Image models take a `CVPixelBuffer`. When Vision drives the model
|
|
242
185
|
(`CoreMLRequest` or `VNCoreMLRequest`), conversion happens automatically; the
|
|
243
186
|
manual path is only for direct `MLModel` predictions.
|
|
244
187
|
|
|
245
|
-
```swift
|
|
246
|
-
import CoreGraphics
|
|
247
|
-
import CoreVideo
|
|
248
|
-
|
|
249
|
-
func pixelBuffer(from picture: CGImage, side: Int) -> CVPixelBuffer? {
|
|
250
|
-
let options = [
|
|
251
|
-
kCVPixelBufferCGImageCompatibilityKey: true,
|
|
252
|
-
kCVPixelBufferCGBitmapContextCompatibilityKey: true
|
|
253
|
-
] as CFDictionary
|
|
254
|
-
var buffer: CVPixelBuffer?
|
|
255
|
-
guard CVPixelBufferCreate(kCFAllocatorDefault, side, side, kCVPixelFormatType_32ARGB, options, &buffer) == kCVReturnSuccess,
|
|
256
|
-
let buffer else { return nil }
|
|
257
|
-
CVPixelBufferLockBaseAddress(buffer, [])
|
|
258
|
-
defer { CVPixelBufferUnlockBaseAddress(buffer, []) }
|
|
259
|
-
let context = CGContext(
|
|
260
|
-
data: CVPixelBufferGetBaseAddress(buffer), width: side, height: side,
|
|
261
|
-
bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
|
|
262
|
-
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
|
|
263
|
-
)
|
|
264
|
-
context?.draw(picture, in: CGRect(x: 0, y: 0, width: side, height: side))
|
|
265
|
-
return buffer
|
|
266
|
-
}
|
|
267
|
-
```
|
|
268
|
-
|
|
269
188
|
Normalization and center-crop helpers are in the reference.
|
|
270
189
|
|
|
190
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
191
|
+
|
|
271
192
|
## Chaining models
|
|
272
193
|
|
|
273
194
|
When pre- or post-processing is its own model, call them in order:
|
|
@@ -292,64 +213,26 @@ each sub-model then runs on the compute unit that suits it.
|
|
|
292
213
|
Vision takes care of resizing, cropping, normalization, color space and
|
|
293
214
|
orientation.
|
|
294
215
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
import Vision
|
|
298
|
-
|
|
299
|
-
func dominantLabel(in photo: CGImage, model: MLModel) async throws -> (String, Float)? {
|
|
300
|
-
let request = CoreMLRequest(model: try CoreMLModelContainer(model: model))
|
|
301
|
-
let observations = try await request.perform(on: photo)
|
|
302
|
-
guard let top = observations.first as? ClassificationObservation else { return nil }
|
|
303
|
-
return (top.identifier, top.confidence)
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
`CoreMLRequest` needs iOS 18. The legacy route for detection models:
|
|
308
|
-
|
|
309
|
-
```swift
|
|
310
|
-
import CoreML
|
|
311
|
-
import Vision
|
|
312
|
-
|
|
313
|
-
func legacyDetections(in frame: CVPixelBuffer, model: MLModel) throws -> [(String, Float, CGRect)] {
|
|
314
|
-
let request = VNCoreMLRequest(model: try VNCoreMLModel(for: model))
|
|
315
|
-
request.imageCropAndScaleOption = .scaleFill
|
|
316
|
-
try VNImageRequestHandler(cvPixelBuffer: frame).perform([request])
|
|
317
|
-
let objects = request.results as? [VNRecognizedObjectObservation] ?? []
|
|
318
|
-
return objects.compactMap { object in
|
|
319
|
-
guard let label = object.labels.first else { return nil }
|
|
320
|
-
return (label.identifier, label.confidence, object.boundingBox)
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
```
|
|
216
|
+
`CoreMLRequest` needs iOS 18. Detection models also have a legacy route through
|
|
217
|
+
`VNCoreMLRequest` with a `VNCoreMLModel`.
|
|
324
218
|
|
|
325
219
|
`boundingBox` is normalized. Everything else about Vision (OCR, barcodes,
|
|
326
220
|
documents) is in `vision-framework`.
|
|
327
221
|
|
|
222
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
223
|
+
|
|
328
224
|
## Profiling
|
|
329
225
|
|
|
330
226
|
`MLComputePlan` (iOS 17.4+) tells you, before any prediction, which device
|
|
331
|
-
each operation prefers and how expensive it is
|
|
332
|
-
|
|
333
|
-
```swift
|
|
334
|
-
import CoreML
|
|
335
|
-
|
|
336
|
-
func opsOffAccelerators(modelURL: URL) async throws -> [String] {
|
|
337
|
-
let plan = try await MLComputePlan.load(contentsOf: modelURL, configuration: MLModelConfiguration())
|
|
338
|
-
guard case .program(let program) = plan.modelStructure,
|
|
339
|
-
let entry = program.functions["main"] else { return [] }
|
|
340
|
-
return entry.block.operations.compactMap { op in
|
|
341
|
-
guard let usage = plan.deviceUsage(for: op), case .cpu = usage.preferred else { return nil }
|
|
342
|
-
let cost = plan.estimatedCost(of: op)?.weight ?? 0
|
|
343
|
-
return "\(op.operatorName) cost \(cost)"
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
```
|
|
227
|
+
each operation prefers and how expensive it is.
|
|
347
228
|
|
|
348
229
|
The Instruments Core ML template shows load time, per-prediction latency with a
|
|
349
230
|
per-operation breakdown, which device each operation ran on, and memory.
|
|
350
231
|
Profile with Product > Profile, not under the debugger, or the numbers are
|
|
351
232
|
not representative.
|
|
352
233
|
|
|
234
|
+
Code for this section: [Core ML code](references/coreml-code.md).
|
|
235
|
+
|
|
353
236
|
## Deployment
|
|
354
237
|
|
|
355
238
|
| Strategy | Strength | Cost |
|
|
@@ -447,6 +330,7 @@ Keep the returned request alive while the model is in use and call
|
|
|
447
330
|
|
|
448
331
|
## References
|
|
449
332
|
|
|
333
|
+
- [Core ML code](references/coreml-code.md): open for the code behind loading a model, predictions, MLMultiArray, image input, Vision and profiling.
|
|
450
334
|
- `references/coreml-swift-integration.md`
|
|
451
335
|
- `apple-on-device-ai` for conversion and compression in Python
|
|
452
336
|
- https://developer.apple.com/documentation/coreml
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Core ML code
|
|
2
|
+
|
|
3
|
+
<!-- toc -->
|
|
4
|
+
- [Loading a model](#loading-a-model)
|
|
5
|
+
- [Predictions](#predictions)
|
|
6
|
+
- [MLMultiArray](#mlmultiarray)
|
|
7
|
+
- [Image input](#image-input)
|
|
8
|
+
- [Through Vision](#through-vision)
|
|
9
|
+
- [Profiling](#profiling)
|
|
10
|
+
<!-- /toc -->
|
|
11
|
+
|
|
12
|
+
Code for loading, predictions, MLMultiArray, image input, Vision requests and profiling. SKILL.md keeps the rules and review guidance.
|
|
13
|
+
|
|
14
|
+
## Loading a model
|
|
15
|
+
|
|
16
|
+
**Generated class.** Drop a `.mlmodel` or `.mlpackage` into a target and Xcode
|
|
17
|
+
generates a Swift class with typed inputs and outputs. Use it whenever it
|
|
18
|
+
exists.
|
|
19
|
+
|
|
20
|
+
```swift
|
|
21
|
+
import CoreML
|
|
22
|
+
|
|
23
|
+
func makeDenoiser() throws -> PhotoDenoiser {
|
|
24
|
+
let settings = MLModelConfiguration()
|
|
25
|
+
settings.computeUnits = .all
|
|
26
|
+
return try PhotoDenoiser(configuration: settings)
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**From a URL.** For models that are downloaded or stored outside the bundle:
|
|
31
|
+
|
|
32
|
+
```swift
|
|
33
|
+
import CoreML
|
|
34
|
+
|
|
35
|
+
func bundledModel(named name: String) throws -> MLModel {
|
|
36
|
+
guard let url = Bundle.main.url(forResource: name, withExtension: "mlmodelc") else {
|
|
37
|
+
throw CocoaError(.fileNoSuchFile)
|
|
38
|
+
}
|
|
39
|
+
return try MLModel(contentsOf: url, configuration: MLModelConfiguration())
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Asynchronously.** `MLModel.load(contentsOf:configuration:)` (async, iOS 15+)
|
|
44
|
+
keeps large models from stalling the main thread and is the preferred loader.
|
|
45
|
+
|
|
46
|
+
**Compiling on device.** `MLModel.compileModel(at:)` (async form, iOS 16+)
|
|
47
|
+
turns a `.mlmodel` or `.mlpackage` into a `.mlmodelc` on the device, which is
|
|
48
|
+
how server-delivered models become loadable. Two rules travel together:
|
|
49
|
+
|
|
50
|
+
1. Compile once per model version, never on every launch; recompiling at each
|
|
51
|
+
start is a bug.
|
|
52
|
+
2. The returned URL points into a temporary location. Move the `.mlmodelc`
|
|
53
|
+
somewhere durable such as Application Support and reuse it.
|
|
54
|
+
|
|
55
|
+
When reviewing runtime-loaded models, state both: the async compile API needs
|
|
56
|
+
iOS 16, and its output must be cached.
|
|
57
|
+
|
|
58
|
+
## Predictions
|
|
59
|
+
|
|
60
|
+
**Generated class:**
|
|
61
|
+
|
|
62
|
+
```swift
|
|
63
|
+
import CoreML
|
|
64
|
+
|
|
65
|
+
func tagScene(_ frame: CVPixelBuffer, with model: SceneTagger) throws -> (String, Double) {
|
|
66
|
+
let result = try model.prediction(input: SceneTaggerInput(image: frame))
|
|
67
|
+
return (result.classLabel, result.classLabelProbs[result.classLabel] ?? 0)
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`classLabel` is the winning label and `classLabelProbs` maps every label to
|
|
72
|
+
its probability.
|
|
73
|
+
|
|
74
|
+
**Dictionary provider**, for inputs only known at run time:
|
|
75
|
+
|
|
76
|
+
```swift
|
|
77
|
+
import CoreML
|
|
78
|
+
|
|
79
|
+
func tagWithThreshold(_ frame: CVPixelBuffer, cutoff: Double, model: MLModel) async throws -> String? {
|
|
80
|
+
let inputs = try MLDictionaryFeatureProvider(dictionary: [
|
|
81
|
+
"image": MLFeatureValue(pixelBuffer: frame),
|
|
82
|
+
"threshold": MLFeatureValue(double: cutoff)
|
|
83
|
+
])
|
|
84
|
+
let outputs = try await model.prediction(from: inputs)
|
|
85
|
+
return outputs.featureValue(for: "label")?.stringValue
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## MLMultiArray
|
|
90
|
+
|
|
91
|
+
`MLMultiArray` is the exchange type for non-image inputs and outputs; generated
|
|
92
|
+
classes use it for array features.
|
|
93
|
+
|
|
94
|
+
```swift
|
|
95
|
+
import CoreML
|
|
96
|
+
|
|
97
|
+
func embeddingInput(tokens: Int, width: Int) throws -> MLMultiArray {
|
|
98
|
+
let grid = try MLMultiArray(shape: [1, NSNumber(value: tokens), NSNumber(value: width)], dataType: .float32)
|
|
99
|
+
grid[[0, 0, 0] as [NSNumber]] = 0.25
|
|
100
|
+
return grid
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func firstValue(of grid: MLMultiArray) -> Float {
|
|
104
|
+
grid[[0, 0, 0] as [NSNumber]].floatValue
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
func packed(_ values: [Float]) -> MLMultiArray {
|
|
108
|
+
MLMultiArray(MLShapedArray<Float>(scalars: values, shape: [1, values.count]))
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The shape reads batch, sequence, features. Tokenized text and audio feature
|
|
113
|
+
patterns are in the reference.
|
|
114
|
+
|
|
115
|
+
## Image input
|
|
116
|
+
|
|
117
|
+
Image models take a `CVPixelBuffer`. When Vision drives the model
|
|
118
|
+
(`CoreMLRequest` or `VNCoreMLRequest`), conversion happens automatically; the
|
|
119
|
+
manual path is only for direct `MLModel` predictions.
|
|
120
|
+
|
|
121
|
+
```swift
|
|
122
|
+
import CoreGraphics
|
|
123
|
+
import CoreVideo
|
|
124
|
+
|
|
125
|
+
func pixelBuffer(from picture: CGImage, side: Int) -> CVPixelBuffer? {
|
|
126
|
+
let options = [
|
|
127
|
+
kCVPixelBufferCGImageCompatibilityKey: true,
|
|
128
|
+
kCVPixelBufferCGBitmapContextCompatibilityKey: true
|
|
129
|
+
] as CFDictionary
|
|
130
|
+
var buffer: CVPixelBuffer?
|
|
131
|
+
guard CVPixelBufferCreate(kCFAllocatorDefault, side, side, kCVPixelFormatType_32ARGB, options, &buffer) == kCVReturnSuccess,
|
|
132
|
+
let buffer else { return nil }
|
|
133
|
+
CVPixelBufferLockBaseAddress(buffer, [])
|
|
134
|
+
defer { CVPixelBufferUnlockBaseAddress(buffer, []) }
|
|
135
|
+
let context = CGContext(
|
|
136
|
+
data: CVPixelBufferGetBaseAddress(buffer), width: side, height: side,
|
|
137
|
+
bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(buffer),
|
|
138
|
+
space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
|
|
139
|
+
)
|
|
140
|
+
context?.draw(picture, in: CGRect(x: 0, y: 0, width: side, height: side))
|
|
141
|
+
return buffer
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Normalization and center-crop helpers are in the reference.
|
|
146
|
+
|
|
147
|
+
## Through Vision
|
|
148
|
+
|
|
149
|
+
Vision takes care of resizing, cropping, normalization, color space and
|
|
150
|
+
orientation.
|
|
151
|
+
|
|
152
|
+
```swift
|
|
153
|
+
import CoreML
|
|
154
|
+
import Vision
|
|
155
|
+
|
|
156
|
+
func dominantLabel(in photo: CGImage, model: MLModel) async throws -> (String, Float)? {
|
|
157
|
+
let request = CoreMLRequest(model: try CoreMLModelContainer(model: model))
|
|
158
|
+
let observations = try await request.perform(on: photo)
|
|
159
|
+
guard let top = observations.first as? ClassificationObservation else { return nil }
|
|
160
|
+
return (top.identifier, top.confidence)
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
`CoreMLRequest` needs iOS 18. The legacy route for detection models:
|
|
165
|
+
|
|
166
|
+
```swift
|
|
167
|
+
import CoreML
|
|
168
|
+
import Vision
|
|
169
|
+
|
|
170
|
+
func legacyDetections(in frame: CVPixelBuffer, model: MLModel) throws -> [(String, Float, CGRect)] {
|
|
171
|
+
let request = VNCoreMLRequest(model: try VNCoreMLModel(for: model))
|
|
172
|
+
request.imageCropAndScaleOption = .scaleFill
|
|
173
|
+
try VNImageRequestHandler(cvPixelBuffer: frame).perform([request])
|
|
174
|
+
let objects = request.results as? [VNRecognizedObjectObservation] ?? []
|
|
175
|
+
return objects.compactMap { object in
|
|
176
|
+
guard let label = object.labels.first else { return nil }
|
|
177
|
+
return (label.identifier, label.confidence, object.boundingBox)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`boundingBox` is normalized. Everything else about Vision (OCR, barcodes,
|
|
183
|
+
documents) is in `vision-framework`.
|
|
184
|
+
|
|
185
|
+
## Profiling
|
|
186
|
+
|
|
187
|
+
`MLComputePlan` (iOS 17.4+) tells you, before any prediction, which device
|
|
188
|
+
each operation prefers and how expensive it is:
|
|
189
|
+
|
|
190
|
+
```swift
|
|
191
|
+
import CoreML
|
|
192
|
+
|
|
193
|
+
func opsOffAccelerators(modelURL: URL) async throws -> [String] {
|
|
194
|
+
let plan = try await MLComputePlan.load(contentsOf: modelURL, configuration: MLModelConfiguration())
|
|
195
|
+
guard case .program(let program) = plan.modelStructure,
|
|
196
|
+
let entry = program.functions["main"] else { return [] }
|
|
197
|
+
return entry.block.operations.compactMap { op in
|
|
198
|
+
guard let usage = plan.deviceUsage(for: op), case .cpu = usage.preferred else { return nil }
|
|
199
|
+
let cost = plan.estimatedCost(of: op)?.weight ?? 0
|
|
200
|
+
return "\(op.operatorName) cost \(cost)"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
The Instruments Core ML template shows load time, per-prediction latency with a
|
|
206
|
+
per-operation breakdown, which device each operation ran on, and memory.
|
|
207
|
+
Profile with Product > Profile, not under the debugger, or the numbers are
|
|
208
|
+
not representative.
|
|
@@ -299,142 +299,15 @@ Cascade layers give explicit control over specificity order.
|
|
|
299
299
|
|
|
300
300
|
## CSS Custom Properties (Variables)
|
|
301
301
|
|
|
302
|
-
|
|
303
|
-
:root {
|
|
304
|
-
/* Color system */
|
|
305
|
-
--color-brand: oklch(55% 0.25 265);
|
|
306
|
-
--color-brand-light: oklch(70% 0.2 265);
|
|
307
|
-
--color-brand-dark: oklch(40% 0.25 265);
|
|
308
|
-
|
|
309
|
-
--color-surface: #ffffff;
|
|
310
|
-
--color-surface-elevated: #f9fafb;
|
|
311
|
-
--color-text: #111827;
|
|
312
|
-
--color-text-muted: #6b7280;
|
|
313
|
-
--color-border: #e5e7eb;
|
|
314
|
-
|
|
315
|
-
/* Spacing scale */
|
|
316
|
-
--space-xs: 0.25rem;
|
|
317
|
-
--space-sm: 0.5rem;
|
|
318
|
-
--space-md: 1rem;
|
|
319
|
-
--space-lg: 1.5rem;
|
|
320
|
-
--space-xl: 2rem;
|
|
321
|
-
--space-2xl: 3rem;
|
|
322
|
-
|
|
323
|
-
/* Typography */
|
|
324
|
-
--font-sans: 'Inter', system-ui, sans-serif;
|
|
325
|
-
--font-mono: 'JetBrains Mono', monospace;
|
|
326
|
-
|
|
327
|
-
/* Transitions */
|
|
328
|
-
--transition-fast: 150ms ease;
|
|
329
|
-
--transition-normal: 250ms ease;
|
|
330
|
-
--transition-slow: 400ms ease;
|
|
331
|
-
|
|
332
|
-
/* Shadows */
|
|
333
|
-
--shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
|
|
334
|
-
--shadow-md: 0 4px 6px rgb(0 0 0 / 0.1);
|
|
335
|
-
--shadow-lg: 0 10px 15px rgb(0 0 0 / 0.1);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/* Dark theme override */
|
|
339
|
-
@media (prefers-color-scheme: dark) {
|
|
340
|
-
:root {
|
|
341
|
-
--color-surface: #111827;
|
|
342
|
-
--color-surface-elevated: #1f2937;
|
|
343
|
-
--color-text: #f9fafb;
|
|
344
|
-
--color-text-muted: #9ca3af;
|
|
345
|
-
--color-border: #374151;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
302
|
+
Define the design tokens (colors, spacing scale, typography, transitions, shadows) once on `:root`, override them in a `prefers-color-scheme: dark` block, and consume them in components with `var()`.
|
|
348
303
|
|
|
349
|
-
|
|
350
|
-
.card {
|
|
351
|
-
background: var(--color-surface-elevated);
|
|
352
|
-
color: var(--color-text);
|
|
353
|
-
border: 1px solid var(--color-border);
|
|
354
|
-
border-radius: 0.75rem;
|
|
355
|
-
padding: var(--space-lg);
|
|
356
|
-
box-shadow: var(--shadow-sm);
|
|
357
|
-
transition: box-shadow var(--transition-normal);
|
|
358
|
-
|
|
359
|
-
&:hover {
|
|
360
|
-
box-shadow: var(--shadow-md);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
```
|
|
304
|
+
Code: [CSS Custom Properties (Variables)](references/code-examples.md#css-custom-properties-variables).
|
|
364
305
|
|
|
365
306
|
## Animations and Transitions
|
|
366
307
|
|
|
367
|
-
|
|
368
|
-
/* View Transitions API */
|
|
369
|
-
@view-transition {
|
|
370
|
-
navigation: auto;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
.hero-image {
|
|
374
|
-
view-transition-name: hero;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
::view-transition-old(hero) {
|
|
378
|
-
animation: fade-out 300ms ease;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
::view-transition-new(hero) {
|
|
382
|
-
animation: fade-in 300ms ease;
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
/* Scroll-driven animations */
|
|
386
|
-
@keyframes slide-in {
|
|
387
|
-
from { opacity: 0; transform: translateY(50px); }
|
|
388
|
-
to { opacity: 1; transform: translateY(0); }
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
.scroll-reveal {
|
|
392
|
-
animation: slide-in linear both;
|
|
393
|
-
animation-timeline: view();
|
|
394
|
-
animation-range: entry 0% entry 100%;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/* Transition with discrete properties */
|
|
398
|
-
.dialog {
|
|
399
|
-
transition: opacity 300ms ease, display 300ms ease allow-discrete;
|
|
400
|
-
opacity: 0;
|
|
401
|
-
display: none;
|
|
402
|
-
|
|
403
|
-
&[open] {
|
|
404
|
-
opacity: 1;
|
|
405
|
-
display: block;
|
|
406
|
-
|
|
407
|
-
@starting-style {
|
|
408
|
-
opacity: 0;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/* Keyframe animation */
|
|
414
|
-
@keyframes shimmer {
|
|
415
|
-
0% { background-position: -200% 0; }
|
|
416
|
-
100% { background-position: 200% 0; }
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
.skeleton {
|
|
420
|
-
background: linear-gradient(
|
|
421
|
-
90deg,
|
|
422
|
-
#f0f0f0 25%,
|
|
423
|
-
#e0e0e0 50%,
|
|
424
|
-
#f0f0f0 75%
|
|
425
|
-
);
|
|
426
|
-
background-size: 200% 100%;
|
|
427
|
-
animation: shimmer 1.5s infinite ease-in-out;
|
|
428
|
-
}
|
|
308
|
+
Use transitions for state changes (with `allow-discrete` when `display` animates), `@keyframes` for multi-step motion, and a `prefers-reduced-motion: reduce` block that turns motion off.
|
|
429
309
|
|
|
430
|
-
|
|
431
|
-
@media (prefers-reduced-motion: reduce) {
|
|
432
|
-
.scroll-reveal {
|
|
433
|
-
animation: none;
|
|
434
|
-
opacity: 1;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
```
|
|
310
|
+
Code: [Animations and Transitions](references/code-examples.md#animations-and-transitions).
|
|
438
311
|
|
|
439
312
|
## Do's
|
|
440
313
|
|
|
@@ -468,3 +341,7 @@ Cascade layers give explicit control over specificity order.
|
|
|
468
341
|
| Grid item overflowing | Long word or fixed-width content | Add `min-width: 0` or `overflow: hidden` to grid item |
|
|
469
342
|
| Subgrid not aligning | Missing `grid-row: span N` | Set span count to match number of sub-rows |
|
|
470
343
|
| Custom property not applying | Scope issue or typo | Check spelling and ensure variable is defined in the right scope |
|
|
344
|
+
|
|
345
|
+
## References
|
|
346
|
+
|
|
347
|
+
- [Code examples](references/code-examples.md): open for the full code behind CSS Custom Properties (Variables), Animations and Transitions.
|