mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-28 02:21:15 +00:00
feat(watch): inbox mascot with Always-On-safe rendering (#109365)
The watch inbox's empty and waiting surface gets a 72pt Clawd: sleepy (nightcap and all) when nothing is waiting, thinking while the watch waits for iPhone sync, attentive when an approval needs a decision. The Always-On display renders a static pose so the animation loop never runs dimmed, and the active loop is throttled to 15fps for battery. The shared mascot gains a defaulted minimum frame interval; mascot sources compile directly into the watch target following the TalkWaveformView pattern.
This commit is contained in:
committed by
GitHub
parent
56d0721eb7
commit
c6ebd6be34
@@ -26347,7 +26347,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 150,
|
||||
"line": 159,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Caught up",
|
||||
"surface": "apple",
|
||||
@@ -26355,7 +26355,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 153,
|
||||
"line": 162,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "No chats or approvals need you",
|
||||
"surface": "apple",
|
||||
@@ -26363,7 +26363,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 154,
|
||||
"line": 163,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Waiting for iPhone sync",
|
||||
"surface": "apple",
|
||||
@@ -26371,7 +26371,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 161,
|
||||
"line": 170,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Continue on iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26379,7 +26379,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 202,
|
||||
"line": 211,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Latest",
|
||||
"surface": "apple",
|
||||
@@ -26387,7 +26387,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 238,
|
||||
"line": 247,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval waiting",
|
||||
"surface": "apple",
|
||||
@@ -26395,7 +26395,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 244,
|
||||
"line": 253,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Latest update",
|
||||
"surface": "apple",
|
||||
@@ -26403,7 +26403,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 247,
|
||||
"line": 256,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Nothing waiting",
|
||||
"surface": "apple",
|
||||
@@ -26411,7 +26411,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 263,
|
||||
"line": 272,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval needed",
|
||||
"surface": "apple",
|
||||
@@ -26419,7 +26419,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-modifier",
|
||||
"line": 286,
|
||||
"line": 295,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Opens the full command before decisions are available",
|
||||
"surface": "apple",
|
||||
@@ -26427,7 +26427,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 290,
|
||||
"line": 299,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Loading",
|
||||
"surface": "apple",
|
||||
@@ -26435,7 +26435,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 291,
|
||||
"line": 300,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Loading approval",
|
||||
"surface": "apple",
|
||||
@@ -26443,7 +26443,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 294,
|
||||
"line": 303,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Waiting for your iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26451,7 +26451,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 295,
|
||||
"line": 304,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Syncing",
|
||||
"surface": "apple",
|
||||
@@ -26459,7 +26459,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 298,
|
||||
"line": 307,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Unavailable",
|
||||
"surface": "apple",
|
||||
@@ -26467,7 +26467,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 299,
|
||||
"line": 308,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval not loaded",
|
||||
"surface": "apple",
|
||||
@@ -26475,7 +26475,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 302,
|
||||
"line": 311,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval details have not loaded",
|
||||
"surface": "apple",
|
||||
@@ -26483,7 +26483,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 303,
|
||||
"line": 312,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Retry",
|
||||
"surface": "apple",
|
||||
@@ -26491,7 +26491,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 305,
|
||||
"line": 314,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Review again",
|
||||
"surface": "apple",
|
||||
@@ -26499,7 +26499,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 322,
|
||||
"line": 331,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Open all approvals",
|
||||
"surface": "apple",
|
||||
@@ -26507,7 +26507,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 333,
|
||||
"line": 342,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Connection",
|
||||
"surface": "apple",
|
||||
@@ -26515,7 +26515,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 335,
|
||||
"line": 344,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Watch node online",
|
||||
"surface": "apple",
|
||||
@@ -26523,7 +26523,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 336,
|
||||
"line": 345,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Direct Gateway",
|
||||
"surface": "apple",
|
||||
@@ -26531,7 +26531,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 343,
|
||||
"line": 352,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Direct",
|
||||
"surface": "apple",
|
||||
@@ -26539,7 +26539,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 344,
|
||||
"line": 353,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Setup",
|
||||
"surface": "apple",
|
||||
@@ -26547,7 +26547,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 346,
|
||||
"line": 355,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Enable from iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26555,7 +26555,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 349,
|
||||
"line": 358,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Uses Wi-Fi or cellular while OpenClaw is active",
|
||||
"surface": "apple",
|
||||
@@ -26563,7 +26563,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 350,
|
||||
"line": 359,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Open iPhone Settings → Apple Watch",
|
||||
"surface": "apple",
|
||||
@@ -26571,7 +26571,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 352,
|
||||
"line": 361,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Online",
|
||||
"surface": "apple",
|
||||
@@ -26579,7 +26579,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 353,
|
||||
"line": 362,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Offline",
|
||||
"surface": "apple",
|
||||
@@ -26587,7 +26587,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call-multiline",
|
||||
"line": 356,
|
||||
"line": 365,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Direct mode supports device info, status, and notifications. Chat, Talk, and approvals still use the iPhone.",
|
||||
"surface": "apple",
|
||||
@@ -26595,7 +26595,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 366,
|
||||
"line": 375,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Direct connection",
|
||||
"surface": "apple",
|
||||
@@ -26603,7 +26603,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 372,
|
||||
"line": 381,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Forget direct setup",
|
||||
"surface": "apple",
|
||||
@@ -26611,7 +26611,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 378,
|
||||
"line": 387,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "The iPhone securely sends a one-time setup code. Existing relay features stay available.",
|
||||
"surface": "apple",
|
||||
@@ -26619,7 +26619,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 430,
|
||||
"line": 439,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "AI agent online",
|
||||
"surface": "apple",
|
||||
@@ -26627,7 +26627,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 431,
|
||||
"line": 440,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Reconnect on iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26635,7 +26635,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 433,
|
||||
"line": 442,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Pair iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26643,7 +26643,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 437,
|
||||
"line": 446,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Next up",
|
||||
"surface": "apple",
|
||||
@@ -26651,7 +26651,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 439,
|
||||
"line": 448,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Running",
|
||||
"surface": "apple",
|
||||
@@ -26659,7 +26659,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 440,
|
||||
"line": 449,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Pairing",
|
||||
"surface": "apple",
|
||||
@@ -26667,7 +26667,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 455,
|
||||
"line": 464,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval waiting on your wrist",
|
||||
"surface": "apple",
|
||||
@@ -26675,7 +26675,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 461,
|
||||
"line": 470,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Ready for quick actions",
|
||||
"surface": "apple",
|
||||
@@ -26683,7 +26683,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 468,
|
||||
"line": 477,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "^[\\(count) approval](inflect: true) waiting",
|
||||
"surface": "apple",
|
||||
@@ -26691,7 +26691,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 480,
|
||||
"line": 489,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Decide from watch",
|
||||
"surface": "apple",
|
||||
@@ -26699,7 +26699,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 481,
|
||||
"line": 490,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "No approvals",
|
||||
"surface": "apple",
|
||||
@@ -26707,7 +26707,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 489,
|
||||
"line": 498,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Expires in %@",
|
||||
"surface": "apple",
|
||||
@@ -26715,7 +26715,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 496,
|
||||
"line": 505,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Review before it runs",
|
||||
"surface": "apple",
|
||||
@@ -26723,7 +26723,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 503,
|
||||
"line": 512,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Sending",
|
||||
"surface": "apple",
|
||||
@@ -26731,7 +26731,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 552,
|
||||
"line": 561,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Good morning",
|
||||
"surface": "apple",
|
||||
@@ -26739,7 +26739,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 553,
|
||||
"line": 562,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Good afternoon",
|
||||
"surface": "apple",
|
||||
@@ -26747,7 +26747,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 554,
|
||||
"line": 563,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Good evening",
|
||||
"surface": "apple",
|
||||
@@ -26755,7 +26755,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 568,
|
||||
"line": 577,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Synced",
|
||||
"surface": "apple",
|
||||
@@ -26763,7 +26763,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 569,
|
||||
"line": 578,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Waiting for iPhone",
|
||||
"surface": "apple",
|
||||
@@ -26771,7 +26771,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 573,
|
||||
"line": 582,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Just now",
|
||||
"surface": "apple",
|
||||
@@ -26779,7 +26779,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 591,
|
||||
"line": 600,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Requires confirmation",
|
||||
"surface": "apple",
|
||||
@@ -26787,7 +26787,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 593,
|
||||
"line": 602,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Dismiss this update",
|
||||
"surface": "apple",
|
||||
@@ -26795,7 +26795,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 595,
|
||||
"line": 604,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Send from watch",
|
||||
"surface": "apple",
|
||||
@@ -26803,7 +26803,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 603,
|
||||
"line": 612,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "less than 1 min",
|
||||
"surface": "apple",
|
||||
@@ -26811,7 +26811,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 606,
|
||||
"line": 615,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "%@ min",
|
||||
"surface": "apple",
|
||||
@@ -26819,7 +26819,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 836,
|
||||
"line": 845,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Inbox",
|
||||
"surface": "apple",
|
||||
@@ -26827,7 +26827,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 1072,
|
||||
"line": 1081,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Command",
|
||||
"surface": "apple",
|
||||
@@ -26835,7 +26835,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-modifier",
|
||||
"line": 1091,
|
||||
"line": 1100,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Command to review",
|
||||
"surface": "apple",
|
||||
@@ -26843,7 +26843,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1108,
|
||||
"line": 1117,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Sending decision...",
|
||||
"surface": "apple",
|
||||
@@ -26851,7 +26851,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1162,
|
||||
"line": 1171,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "System",
|
||||
"surface": "apple",
|
||||
@@ -26859,7 +26859,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "conditional-branch",
|
||||
"line": 1164,
|
||||
"line": 1173,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "OpenClaw",
|
||||
"surface": "apple",
|
||||
@@ -26867,7 +26867,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 1214,
|
||||
"line": 1223,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Refresh",
|
||||
"surface": "apple",
|
||||
@@ -26875,7 +26875,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-modifier",
|
||||
"line": 1244,
|
||||
"line": 1253,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Chat",
|
||||
"surface": "apple",
|
||||
@@ -26883,7 +26883,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1262,
|
||||
"line": 1271,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Speaking reply…",
|
||||
"surface": "apple",
|
||||
@@ -26891,7 +26891,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1265,
|
||||
"line": 1274,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Waiting for spoken reply…",
|
||||
"surface": "apple",
|
||||
@@ -26899,7 +26899,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 1309,
|
||||
"line": 1318,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "No chat synced",
|
||||
"surface": "apple",
|
||||
@@ -26907,7 +26907,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 1316,
|
||||
"line": 1325,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Tap the message pill below to start from your watch.",
|
||||
"surface": "apple",
|
||||
@@ -26915,7 +26915,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 1337,
|
||||
"line": 1346,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "You",
|
||||
"surface": "apple",
|
||||
@@ -26923,7 +26923,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 1364,
|
||||
"line": 1373,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Message OpenClaw",
|
||||
"surface": "apple",
|
||||
@@ -26931,7 +26931,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1422,
|
||||
"line": 1431,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Stop speaking",
|
||||
"surface": "apple",
|
||||
@@ -26939,7 +26939,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1425,
|
||||
"line": 1434,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Cancel voice turn",
|
||||
"surface": "apple",
|
||||
@@ -26947,7 +26947,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1427,
|
||||
"line": 1436,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Start voice turn",
|
||||
"surface": "apple",
|
||||
@@ -26955,7 +26955,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 1467,
|
||||
"line": 1476,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approvals",
|
||||
"surface": "apple",
|
||||
@@ -26963,7 +26963,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1470,
|
||||
"line": 1479,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Clear",
|
||||
"surface": "apple",
|
||||
@@ -26971,7 +26971,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1471,
|
||||
"line": 1480,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "No approvals waiting",
|
||||
"surface": "apple",
|
||||
@@ -26979,7 +26979,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1474,
|
||||
"line": 1483,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "You are caught up",
|
||||
"surface": "apple",
|
||||
@@ -26987,7 +26987,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1475,
|
||||
"line": 1484,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Ready",
|
||||
"surface": "apple",
|
||||
@@ -26995,7 +26995,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1485,
|
||||
"line": 1494,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Approval",
|
||||
"surface": "apple",
|
||||
@@ -27003,7 +27003,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1514,
|
||||
"line": 1523,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Pending review",
|
||||
"surface": "apple",
|
||||
@@ -27011,7 +27011,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1521,
|
||||
"line": 1530,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Expires in less than 1 min",
|
||||
"surface": "apple",
|
||||
@@ -27019,7 +27019,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1524,
|
||||
"line": 1533,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Expires in %@ min",
|
||||
"surface": "apple",
|
||||
@@ -27027,7 +27027,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 1535,
|
||||
"line": 1544,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Review Command",
|
||||
"surface": "apple",
|
||||
@@ -27035,7 +27035,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1539,
|
||||
"line": 1548,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Review",
|
||||
"surface": "apple",
|
||||
@@ -27043,7 +27043,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1540,
|
||||
"line": 1549,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Command execution",
|
||||
"surface": "apple",
|
||||
@@ -27051,7 +27051,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1545,
|
||||
"line": 1554,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Now",
|
||||
"surface": "apple",
|
||||
@@ -27059,7 +27059,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 1563,
|
||||
"line": 1572,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Allow Once",
|
||||
"surface": "apple",
|
||||
@@ -27067,7 +27067,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-named-argument",
|
||||
"line": 1572,
|
||||
"line": 1581,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Deny",
|
||||
"surface": "apple",
|
||||
@@ -27075,7 +27075,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1616,
|
||||
"line": 1625,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Review command below",
|
||||
"surface": "apple",
|
||||
@@ -27083,7 +27083,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1623,
|
||||
"line": 1632,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "High risk",
|
||||
"surface": "apple",
|
||||
@@ -27091,7 +27091,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1625,
|
||||
"line": 1634,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Medium risk",
|
||||
"surface": "apple",
|
||||
@@ -27099,7 +27099,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1627,
|
||||
"line": 1636,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "Low risk",
|
||||
"surface": "apple",
|
||||
@@ -27107,7 +27107,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1637,
|
||||
"line": 1646,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "less than 1 minute",
|
||||
"surface": "apple",
|
||||
@@ -27115,7 +27115,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-localized-call",
|
||||
"line": 1641,
|
||||
"line": 1650,
|
||||
"path": "apps/ios/WatchApp/Sources/WatchInboxView.swift",
|
||||
"source": "^[\\(minutes) minute](inflect: true)",
|
||||
"surface": "apple",
|
||||
@@ -36915,7 +36915,7 @@
|
||||
},
|
||||
{
|
||||
"kind": "ui-call",
|
||||
"line": 588,
|
||||
"line": 592,
|
||||
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotView.swift",
|
||||
"source": "z",
|
||||
"surface": "apple",
|
||||
|
||||
42
apps/ios/Tests/Logic/WatchMascotTests.swift
Normal file
42
apps/ios/Tests/Logic/WatchMascotTests.swift
Normal file
@@ -0,0 +1,42 @@
|
||||
import Testing
|
||||
|
||||
struct WatchMascotTests {
|
||||
@Test func `approvals are attentive`() {
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: true,
|
||||
hasApprovals: true,
|
||||
hasChats: false) == .attentive)
|
||||
}
|
||||
|
||||
@Test func `missing snapshot is thinking`() {
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: false,
|
||||
hasApprovals: false,
|
||||
hasChats: false) == .thinking)
|
||||
}
|
||||
|
||||
@Test func `empty synchronized inbox is sleepy`() {
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: true,
|
||||
hasApprovals: false,
|
||||
hasChats: false) == .sleepy)
|
||||
}
|
||||
|
||||
@Test func `synchronized chats are idle`() {
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: true,
|
||||
hasApprovals: false,
|
||||
hasChats: true) == .idle)
|
||||
}
|
||||
|
||||
@Test func `approval and snapshot precedence wins over chats`() {
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: false,
|
||||
hasApprovals: true,
|
||||
hasChats: true) == .attentive)
|
||||
#expect(watchInboxMascotMood(
|
||||
hasSnapshot: false,
|
||||
hasApprovals: false,
|
||||
hasChats: true) == .thinking)
|
||||
}
|
||||
}
|
||||
@@ -145,6 +145,15 @@ private struct WatchControlSurfaceView: View {
|
||||
}
|
||||
self.inboxPromptBlock
|
||||
} else {
|
||||
// The decorative mascot belongs only to the empty or waiting inbox surface.
|
||||
WatchMascot(
|
||||
mood: watchInboxMascotMood(
|
||||
hasSnapshot: self.store.hasAppSnapshot,
|
||||
hasApprovals: self.approvalCount > 0,
|
||||
hasChats: self.chatCount > 0),
|
||||
size: 72)
|
||||
.frame(maxWidth: .infinity)
|
||||
|
||||
WatchHeroCard(
|
||||
label: .localized("Clear"),
|
||||
title: .localized("Caught up"),
|
||||
|
||||
42
apps/ios/WatchApp/Sources/WatchMascot.swift
Normal file
42
apps/ios/WatchApp/Sources/WatchMascot.swift
Normal file
@@ -0,0 +1,42 @@
|
||||
import SwiftUI
|
||||
|
||||
struct WatchMascot: View {
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@Environment(\.isLuminanceReduced) private var isLuminanceReduced
|
||||
|
||||
let mood: OpenClawMascotMood
|
||||
let size: CGFloat
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if self.isLuminanceReduced {
|
||||
// Always-On display must stay static so the animation loop never consumes background power.
|
||||
OpenClawMascotCanvas(
|
||||
pose: .staticPose(for: self.mood),
|
||||
palette: .forScheme(self.colorScheme))
|
||||
} else {
|
||||
// Fifteen frames per second keeps the tiny watch animation expressive without needless battery cost.
|
||||
OpenClawMascotView(
|
||||
mood: self.mood,
|
||||
minimumFrameInterval: 1.0 / 15.0)
|
||||
}
|
||||
}
|
||||
.frame(width: self.size, height: self.size)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
func watchInboxMascotMood(
|
||||
hasSnapshot: Bool,
|
||||
hasApprovals: Bool,
|
||||
hasChats: Bool) -> OpenClawMascotMood
|
||||
{
|
||||
// Approval decisions demand attention above every other inbox state.
|
||||
if hasApprovals { return .attentive }
|
||||
// No snapshot means the watch is still waiting and thinking.
|
||||
if !hasSnapshot { return .thinking }
|
||||
// A synchronized inbox with nothing waiting can settle into sleep.
|
||||
if !hasChats { return .sleepy }
|
||||
// Existing chats keep the mascot present but neutral.
|
||||
return .idle
|
||||
}
|
||||
@@ -317,6 +317,15 @@ targets:
|
||||
# Universal talk waveform, compiled directly to keep the watch styling local.
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/TalkWaveformView.swift
|
||||
group: Shared
|
||||
# Universal mascot, compiled directly to keep the watch styling local.
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotView.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotAnimator.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotCanvas+Accessories.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/ChatTypography.swift
|
||||
group: Shared
|
||||
- path: Sources/Fonts
|
||||
buildPhase: resources
|
||||
- path: Resources/Localizable.xcstrings
|
||||
@@ -428,6 +437,16 @@ targets:
|
||||
group: WatchApp/Sources
|
||||
- path: WatchApp/Sources/WatchInboxMessages.swift
|
||||
group: WatchApp/Sources
|
||||
- path: WatchApp/Sources/WatchMascot.swift
|
||||
group: WatchApp/Sources
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotView.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotAnimator.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/OpenClawMascotCanvas+Accessories.swift
|
||||
group: Shared
|
||||
- path: ../shared/OpenClawKit/Sources/OpenClawChatUI/ChatTypography.swift
|
||||
group: Shared
|
||||
- path: Sources/Services/ExactOpaqueIdentifier.swift
|
||||
group: Sources/Services
|
||||
dependencies:
|
||||
|
||||
@@ -24,6 +24,7 @@ public struct OpenClawMascotView: View {
|
||||
private let mood: OpenClawMascotMood
|
||||
private let accessory: OpenClawMascotAccessory
|
||||
private let interactive: Bool
|
||||
private let minimumFrameInterval: TimeInterval
|
||||
|
||||
private var staticPose: OpenClawMascotPose {
|
||||
var pose = OpenClawMascotPose.staticPose(for: self.mood)
|
||||
@@ -44,16 +45,19 @@ public struct OpenClawMascotView: View {
|
||||
/// - interactive: enables click reactions and auto-sleep (waking takes
|
||||
/// a click). Off by default so the mascot never swallows taps meant
|
||||
/// for an enclosing control.
|
||||
/// - minimumFrameInterval: minimum redraw interval for animated poses.
|
||||
public init(
|
||||
floats: Bool = true,
|
||||
mood: OpenClawMascotMood = .idle,
|
||||
accessory: OpenClawMascotAccessory = .none,
|
||||
interactive: Bool = false)
|
||||
interactive: Bool = false,
|
||||
minimumFrameInterval: TimeInterval = 1.0 / 30.0)
|
||||
{
|
||||
self.floats = floats
|
||||
self.mood = mood
|
||||
self.accessory = accessory
|
||||
self.interactive = interactive
|
||||
self.minimumFrameInterval = minimumFrameInterval
|
||||
self._animator = State(initialValue: OpenClawMascotAnimator(allowsAutoSleep: interactive))
|
||||
}
|
||||
|
||||
@@ -68,7 +72,7 @@ public struct OpenClawMascotView: View {
|
||||
|
||||
@ViewBuilder
|
||||
private func animatedMascot(palette: OpenClawMascotPalette) -> some View {
|
||||
let core = TimelineView(.animation(minimumInterval: 1.0 / 30.0)) { timeline in
|
||||
let core = TimelineView(.animation(minimumInterval: self.minimumFrameInterval)) { timeline in
|
||||
let pose = self.animator.pose(at: timeline.date.timeIntervalSinceReferenceDate)
|
||||
// Float translates the whole canvas like the site floats the hero
|
||||
// container; drawing the offset inside the canvas would clip the
|
||||
|
||||
Reference in New Issue
Block a user