diff --git a/apps/.i18n/native-source.json b/apps/.i18n/native-source.json index 5427a0cf556b..b9b5e751ba40 100644 --- a/apps/.i18n/native-source.json +++ b/apps/.i18n/native-source.json @@ -6123,7 +6123,7 @@ }, { "kind": "ui-named-argument", - "line": 343, + "line": 345, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "Connect a gateway to load dreaming entries.", "surface": "apple", @@ -6131,7 +6131,7 @@ }, { "kind": "ui-call", - "line": 378, + "line": 380, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "\\(entry.totalSignalCount)", "surface": "apple", @@ -6139,7 +6139,7 @@ }, { "kind": "ui-named-argument", - "line": 389, + "line": 391, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "Phases", "surface": "apple", @@ -6147,7 +6147,7 @@ }, { "kind": "conditional-branch", - "line": 395, + "line": 397, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "Dreaming unavailable", "surface": "apple", @@ -6155,7 +6155,7 @@ }, { "kind": "conditional-branch", - "line": 395, + "line": 397, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "No phase status", "surface": "apple", @@ -6163,7 +6163,7 @@ }, { "kind": "conditional-branch", - "line": 397, + "line": 399, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "Connect a gateway to load dreaming phases.", "surface": "apple", @@ -6171,7 +6171,7 @@ }, { "kind": "conditional-branch", - "line": 397, + "line": 399, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "The gateway did not return dreaming phase details.", "surface": "apple", @@ -6179,7 +6179,7 @@ }, { "kind": "conditional-branch", - "line": 549, + "line": 553, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "artifacts repaired", "surface": "apple", @@ -6187,7 +6187,7 @@ }, { "kind": "conditional-branch", - "line": 549, + "line": 553, "path": "apps/ios/Sources/Design/AgentProDreamingDestination.swift", "source": "no repair needed", "surface": "apple", @@ -6979,7 +6979,7 @@ }, { "kind": "ui-call", - "line": 523, + "line": 510, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Setup", "surface": "apple", @@ -6987,7 +6987,7 @@ }, { "kind": "ui-call", - "line": 526, + "line": 513, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Missing: \\(missing)", "surface": "apple", @@ -6995,7 +6995,7 @@ }, { "kind": "ui-call", - "line": 530, + "line": 517, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "No missing requirements reported.", "surface": "apple", @@ -7003,7 +7003,7 @@ }, { "kind": "ui-named-argument", - "line": 552, + "line": 539, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Key", "surface": "apple", @@ -7011,7 +7011,7 @@ }, { "kind": "ui-named-argument", - "line": 553, + "line": 540, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Source", "surface": "apple", @@ -7019,7 +7019,7 @@ }, { "kind": "conditional-branch", - "line": 636, + "line": 623, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Skill policy reset.", "surface": "apple", @@ -7027,7 +7027,7 @@ }, { "kind": "conditional-branch", - "line": 636, + "line": 623, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Skill policy saved.", "surface": "apple", @@ -7035,7 +7035,7 @@ }, { "kind": "conditional-branch", - "line": 649, + "line": 636, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Skill disabled.", "surface": "apple", @@ -7043,7 +7043,7 @@ }, { "kind": "conditional-branch", - "line": 649, + "line": 636, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "Skill enabled.", "surface": "apple", @@ -7051,7 +7051,7 @@ }, { "kind": "conditional-branch", - "line": 661, + "line": 648, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "API key cleared.", "surface": "apple", @@ -7059,7 +7059,7 @@ }, { "kind": "conditional-branch", - "line": 661, + "line": 648, "path": "apps/ios/Sources/Design/AgentProTab+Skills.swift", "source": "API key saved.", "surface": "apple", @@ -7203,7 +7203,7 @@ }, { "kind": "ui-modifier", - "line": 207, + "line": 208, "path": "apps/ios/Sources/Design/ChatProTab.swift", "source": "Opens Settings / Gateway", "surface": "apple", @@ -7211,7 +7211,7 @@ }, { "kind": "conditional-branch", - "line": 266, + "line": 267, "path": "apps/ios/Sources/Design/ChatProTab.swift", "source": "Connected", "surface": "apple", @@ -7219,7 +7219,7 @@ }, { "kind": "conditional-branch", - "line": 266, + "line": 267, "path": "apps/ios/Sources/Design/ChatProTab.swift", "source": "Unavailable", "surface": "apple", @@ -7227,7 +7227,7 @@ }, { "kind": "conditional-branch", - "line": 277, + "line": 278, "path": "apps/ios/Sources/Design/ChatProTab.swift", "source": "Connect to a gateway", "surface": "apple", @@ -7235,7 +7235,7 @@ }, { "kind": "conditional-branch", - "line": 277, + "line": 278, "path": "apps/ios/Sources/Design/ChatProTab.swift", "source": "Message \\(self.agentDisplayName)...", "surface": "apple", @@ -7883,7 +7883,7 @@ }, { "kind": "ui-call", - "line": 293, + "line": 295, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "active", "surface": "apple", @@ -7891,7 +7891,7 @@ }, { "kind": "ui-modifier", - "line": 336, + "line": 338, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Show \\(IPadWorkboardDefaults.label(for: status)) cards", "surface": "apple", @@ -7899,7 +7899,7 @@ }, { "kind": "ui-call", - "line": 341, + "line": 343, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Board", "surface": "apple", @@ -7907,7 +7907,7 @@ }, { "kind": "ui-call", - "line": 345, + "line": 347, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "All boards", "surface": "apple", @@ -7915,7 +7915,7 @@ }, { "kind": "ui-modifier", - "line": 366, + "line": 368, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Workboard board scope", "surface": "apple", @@ -7923,7 +7923,7 @@ }, { "kind": "ui-call", - "line": 376, + "line": 378, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Active", "surface": "apple", @@ -7931,7 +7931,7 @@ }, { "kind": "ui-named-argument", - "line": 437, + "line": 439, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Queue", "surface": "apple", @@ -7939,7 +7939,7 @@ }, { "kind": "conditional-branch", - "line": 444, + "line": 446, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "No cards", "surface": "apple", @@ -7947,7 +7947,7 @@ }, { "kind": "conditional-branch", - "line": 444, + "line": 446, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "No cards loaded", "surface": "apple", @@ -7955,7 +7955,7 @@ }, { "kind": "conditional-branch", - "line": 446, + "line": 448, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Connect from Settings to load workboard cards.", "surface": "apple", @@ -7963,7 +7963,7 @@ }, { "kind": "conditional-branch", - "line": 446, + "line": 448, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Create a card or change the filter.", "surface": "apple", @@ -7971,7 +7971,7 @@ }, { "kind": "ui-call", - "line": 486, + "line": 488, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Notes", "surface": "apple", @@ -7979,7 +7979,7 @@ }, { "kind": "ui-call", - "line": 501, + "line": 503, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Cancel", "surface": "apple", @@ -7987,7 +7987,7 @@ }, { "kind": "conditional-branch", - "line": 513, + "line": 515, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Create", "surface": "apple", @@ -7995,7 +7995,7 @@ }, { "kind": "conditional-branch", - "line": 513, + "line": 515, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Creating...", "surface": "apple", @@ -8003,7 +8003,7 @@ }, { "kind": "conditional-branch", - "line": 593, + "line": 595, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Connect from Settings to create, move, and dispatch cards.", "surface": "apple", @@ -8011,7 +8011,7 @@ }, { "kind": "conditional-branch", - "line": 593, + "line": 595, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Read-only gateway.", "surface": "apple", @@ -8019,7 +8019,7 @@ }, { "kind": "ui-named-argument", - "line": 927, + "line": 929, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "No \\(IPadWorkboardDefaults.label(for: self.status).lowercased()) cards", "surface": "apple", @@ -8027,7 +8027,7 @@ }, { "kind": "ui-named-argument", - "line": 928, + "line": 930, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Cards moved into this lane appear here.", "surface": "apple", @@ -8035,7 +8035,7 @@ }, { "kind": "ui-modifier", - "line": 1107, + "line": 1109, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Card Actions", "surface": "apple", @@ -8043,7 +8043,7 @@ }, { "kind": "ui-call", - "line": 1118, + "line": 1120, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Open", "surface": "apple", @@ -8051,7 +8051,7 @@ }, { "kind": "ui-call", - "line": 1139, + "line": 1141, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Inspect", "surface": "apple", @@ -8059,7 +8059,7 @@ }, { "kind": "ui-call", - "line": 1141, + "line": 1143, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Move to \\(IPadWorkboardDefaults.label(for: status))", "surface": "apple", @@ -8067,7 +8067,7 @@ }, { "kind": "conditional-branch", - "line": 1185, + "line": 1187, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Default agent", "surface": "apple", @@ -8075,7 +8075,7 @@ }, { "kind": "ui-call", - "line": 1203, + "line": 1205, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Card", "surface": "apple", @@ -8083,7 +8083,7 @@ }, { "kind": "ui-call", - "line": 1204, + "line": 1206, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Title", "surface": "apple", @@ -8091,7 +8091,7 @@ }, { "kind": "ui-call", - "line": 1205, + "line": 1207, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Status", "surface": "apple", @@ -8099,7 +8099,7 @@ }, { "kind": "ui-call", - "line": 1211, + "line": 1213, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Actions", "surface": "apple", @@ -8107,7 +8107,7 @@ }, { "kind": "ui-call", - "line": 1213, + "line": 1215, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Open Session", "surface": "apple", @@ -8115,7 +8115,7 @@ }, { "kind": "ui-call", - "line": 1215, + "line": 1217, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Move", "surface": "apple", @@ -8123,7 +8123,7 @@ }, { "kind": "conditional-branch", - "line": 1223, + "line": 1225, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Archive", "surface": "apple", @@ -8131,7 +8131,7 @@ }, { "kind": "conditional-branch", - "line": 1223, + "line": 1225, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Unarchive", "surface": "apple", @@ -8139,7 +8139,7 @@ }, { "kind": "ui-call", - "line": 1231, + "line": 1233, "path": "apps/ios/Sources/Design/IPadWorkboardScreen.swift", "source": "Done", "surface": "apple", @@ -8251,7 +8251,7 @@ }, { "kind": "ui-call", - "line": 374, + "line": 391, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Request ID: \\(value)", "surface": "apple", @@ -8259,7 +8259,7 @@ }, { "kind": "ui-modifier", - "line": 507, + "line": 635, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "OpenClaw", "surface": "apple", @@ -8267,7 +8267,7 @@ }, { "kind": "ui-modifier", - "line": 545, + "line": 663, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Gateway \\(self.title)", "surface": "apple", @@ -8275,7 +8275,7 @@ }, { "kind": "conditional-branch", - "line": 550, + "line": 668, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Online", "surface": "apple", @@ -8283,7 +8283,7 @@ }, { "kind": "conditional-branch", - "line": 552, + "line": 670, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Connecting", "surface": "apple", @@ -8291,7 +8291,7 @@ }, { "kind": "conditional-branch", - "line": 554, + "line": 672, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Attention", "surface": "apple", @@ -8299,7 +8299,7 @@ }, { "kind": "conditional-branch", - "line": 556, + "line": 674, "path": "apps/ios/Sources/Design/OpenClawProComponents.swift", "source": "Offline", "surface": "apple", @@ -8339,7 +8339,7 @@ }, { "kind": "ui-named-argument", - "line": 130, + "line": 132, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Overview", "surface": "apple", @@ -8347,7 +8347,7 @@ }, { "kind": "ui-named-argument", - "line": 152, + "line": 154, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Instances", "surface": "apple", @@ -8355,7 +8355,7 @@ }, { "kind": "ui-named-argument", - "line": 161, + "line": 163, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Dreaming", "surface": "apple", @@ -8363,7 +8363,7 @@ }, { "kind": "ui-named-argument", - "line": 166, + "line": 168, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Usage", "surface": "apple", @@ -8371,7 +8371,7 @@ }, { "kind": "ui-named-argument", - "line": 171, + "line": 173, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Cron Jobs", "surface": "apple", @@ -8379,7 +8379,7 @@ }, { "kind": "conditional-branch", - "line": 241, + "line": 243, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Online", "surface": "apple", @@ -8387,7 +8387,7 @@ }, { "kind": "conditional-branch", - "line": 242, + "line": 244, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Connecting", "surface": "apple", @@ -8395,7 +8395,7 @@ }, { "kind": "conditional-branch", - "line": 243, + "line": 245, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Attention", "surface": "apple", @@ -8403,7 +8403,7 @@ }, { "kind": "conditional-branch", - "line": 244, + "line": 246, "path": "apps/ios/Sources/Design/RootTabsPhoneControlHub.swift", "source": "Offline", "surface": "apple", @@ -8747,7 +8747,7 @@ }, { "kind": "conditional-branch", - "line": 652, + "line": 656, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Configured", "surface": "apple", @@ -8755,7 +8755,7 @@ }, { "kind": "conditional-branch", - "line": 652, + "line": 656, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Not configured", "surface": "apple", @@ -8763,7 +8763,7 @@ }, { "kind": "conditional-branch", - "line": 717, + "line": 721, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Connect to the gateway.", "surface": "apple", @@ -8771,7 +8771,7 @@ }, { "kind": "conditional-branch", - "line": 717, + "line": 721, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Gateway requests will appear here.", "surface": "apple", @@ -8779,7 +8779,7 @@ }, { "kind": "conditional-branch", - "line": 772, + "line": 776, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "High", "surface": "apple", @@ -8787,7 +8787,7 @@ }, { "kind": "conditional-branch", - "line": 772, + "line": 776, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Resolving", "surface": "apple", @@ -8795,7 +8795,7 @@ }, { "kind": "conditional-branch", - "line": 777, + "line": 781, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "One-time approval", "surface": "apple", @@ -8803,7 +8803,7 @@ }, { "kind": "conditional-branch", - "line": 777, + "line": 781, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Permission can be saved", "surface": "apple", @@ -8811,7 +8811,7 @@ }, { "kind": "conditional-branch", - "line": 779, + "line": 783, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Medium", "surface": "apple", @@ -8819,7 +8819,7 @@ }, { "kind": "conditional-branch", - "line": 779, + "line": 783, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Review", "surface": "apple", @@ -8827,7 +8827,7 @@ }, { "kind": "conditional-branch", - "line": 804, + "line": 808, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "\\(diagnosticsIssueCount)", "surface": "apple", @@ -8835,7 +8835,7 @@ }, { "kind": "conditional-branch", - "line": 815, + "line": 819, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location off", "surface": "apple", @@ -8843,7 +8843,7 @@ }, { "kind": "conditional-branch", - "line": 817, + "line": 821, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location While Using", "surface": "apple", @@ -8851,7 +8851,7 @@ }, { "kind": "conditional-branch", - "line": 819, + "line": 823, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location While Using, effective Off", "surface": "apple", @@ -8859,7 +8859,7 @@ }, { "kind": "conditional-branch", - "line": 821, + "line": 825, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location While Using, effective Always", "surface": "apple", @@ -8867,7 +8867,7 @@ }, { "kind": "conditional-branch", - "line": 823, + "line": 827, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location Always", "surface": "apple", @@ -8875,7 +8875,7 @@ }, { "kind": "conditional-branch", - "line": 825, + "line": 829, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location Always, effective While Using", "surface": "apple", @@ -8883,7 +8883,7 @@ }, { "kind": "conditional-branch", - "line": 827, + "line": 831, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Location Always, effective Off", "surface": "apple", @@ -8891,7 +8891,7 @@ }, { "kind": "conditional-branch", - "line": 855, + "line": 859, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Checking iOS notification permission.", "surface": "apple", @@ -8899,7 +8899,7 @@ }, { "kind": "conditional-branch", - "line": 857, + "line": 861, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "OpenClaw can show approval prompts and event alerts when the app is not active.", "surface": "apple", @@ -8907,7 +8907,7 @@ }, { "kind": "conditional-branch", - "line": 859, + "line": 863, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Notifications have been denied. Enable them in iOS Settings.", "surface": "apple", @@ -8915,7 +8915,7 @@ }, { "kind": "conditional-branch", - "line": 861, + "line": 865, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "Enable notifications to receive approval prompts and event alerts outside the app.", "surface": "apple", @@ -8923,7 +8923,7 @@ }, { "kind": "conditional-branch", - "line": 863, + "line": 867, "path": "apps/ios/Sources/Design/SettingsProTabActions.swift", "source": "OpenClaw cannot determine the current notification permission state.", "surface": "apple", @@ -9371,7 +9371,55 @@ }, { "kind": "ui-call", - "line": 734, + "line": 741, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": "Always Allow is active.", + "surface": "apple", + "id": "native.apple.d3a3ecb29929e892" + }, + { + "kind": "ui-call", + "line": 750, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": "iOS is set to While Using.", + "surface": "apple", + "id": "native.apple.6a31419e940a9e08" + }, + { + "kind": "ui-call", + "line": 751, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": " Change to Always in Settings for reliable background automations.", + "surface": "apple", + "id": "native.apple.281edd9f59c17d04" + }, + { + "kind": "ui-call", + "line": 762, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": "Location access is denied.", + "surface": "apple", + "id": "native.apple.80a23b88e118fc2b" + }, + { + "kind": "ui-call", + "line": 763, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": " Enable it in Settings.", + "surface": "apple", + "id": "native.apple.5da8dfd796cfe6e7" + }, + { + "kind": "ui-call", + "line": 777, + "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", + "source": "Open Settings", + "surface": "apple", + "id": "native.apple.34f6841d2e9d13da" + }, + { + "kind": "ui-call", + "line": 789, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Default Agent", "surface": "apple", @@ -9379,7 +9427,7 @@ }, { "kind": "ui-call", - "line": 736, + "line": 791, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Agent", "surface": "apple", @@ -9387,7 +9435,7 @@ }, { "kind": "ui-call", - "line": 737, + "line": 792, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Default", "surface": "apple", @@ -9395,7 +9443,7 @@ }, { "kind": "ui-call", - "line": 745, + "line": 800, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Used for new Chat and Talk sessions.", "surface": "apple", @@ -9403,7 +9451,7 @@ }, { "kind": "ui-call", - "line": 756, + "line": 811, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Setup Code", "surface": "apple", @@ -9411,7 +9459,7 @@ }, { "kind": "ui-call", - "line": 758, + "line": 813, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Paste setup code", "surface": "apple", @@ -9419,7 +9467,7 @@ }, { "kind": "ui-named-argument", - "line": 764, + "line": 819, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Scan QR", "surface": "apple", @@ -9427,7 +9475,7 @@ }, { "kind": "ui-named-argument", - "line": 772, + "line": 827, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Connect", "surface": "apple", @@ -9435,7 +9483,7 @@ }, { "kind": "ui-call", - "line": 800, + "line": 855, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Discovered Gateways", "surface": "apple", @@ -9443,7 +9491,7 @@ }, { "kind": "ui-call", - "line": 803, + "line": 858, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "No gateways found yet. Use manual setup if Bonjour is blocked.", "surface": "apple", @@ -9451,7 +9499,7 @@ }, { "kind": "ui-call", - "line": 847, + "line": 902, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Use Manual Gateway", "surface": "apple", @@ -9459,7 +9507,7 @@ }, { "kind": "ui-call", - "line": 848, + "line": 903, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Host", "surface": "apple", @@ -9467,7 +9515,7 @@ }, { "kind": "ui-call", - "line": 852, + "line": 907, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Port", "surface": "apple", @@ -9475,7 +9523,7 @@ }, { "kind": "ui-call", - "line": 855, + "line": 910, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Use TLS", "surface": "apple", @@ -9483,7 +9531,7 @@ }, { "kind": "ui-named-argument", - "line": 857, + "line": 912, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Connect Manual", "surface": "apple", @@ -9491,7 +9539,7 @@ }, { "kind": "ui-call", - "line": 874, + "line": 929, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Auto-connect on launch", "surface": "apple", @@ -9499,7 +9547,7 @@ }, { "kind": "ui-call", - "line": 875, + "line": 930, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Gateway Auth Token", "surface": "apple", @@ -9507,7 +9555,7 @@ }, { "kind": "ui-call", - "line": 879, + "line": 934, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Gateway Password", "surface": "apple", @@ -9515,7 +9563,7 @@ }, { "kind": "ui-call", - "line": 884, + "line": 939, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Reset Onboarding", "surface": "apple", @@ -9523,7 +9571,7 @@ }, { "kind": "ui-call", - "line": 897, + "line": 952, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Voice Wake", "surface": "apple", @@ -9531,7 +9579,7 @@ }, { "kind": "ui-call", - "line": 900, + "line": 955, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Talk Mode", "surface": "apple", @@ -9539,7 +9587,7 @@ }, { "kind": "ui-call", - "line": 908, + "line": 963, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Speech Language", "surface": "apple", @@ -9547,7 +9595,7 @@ }, { "kind": "ui-call", - "line": 914, + "line": 969, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Speakerphone", "surface": "apple", @@ -9555,7 +9603,7 @@ }, { "kind": "ui-named-argument", - "line": 919, + "line": 974, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Wake Words", "surface": "apple", @@ -9563,7 +9611,7 @@ }, { "kind": "ui-call", - "line": 941, + "line": 996, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Provider", "surface": "apple", @@ -9571,7 +9619,7 @@ }, { "kind": "ui-call", - "line": 947, + "line": 1002, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Realtime Voice", "surface": "apple", @@ -9579,7 +9627,7 @@ }, { "kind": "ui-call", - "line": 948, + "line": 1003, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Gateway Default", "surface": "apple", @@ -9587,7 +9635,7 @@ }, { "kind": "ui-call", - "line": 954, + "line": 1009, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Voice Mode", "surface": "apple", @@ -9595,7 +9643,7 @@ }, { "kind": "ui-call", - "line": 956, + "line": 1011, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Active Voice", "surface": "apple", @@ -9603,7 +9651,7 @@ }, { "kind": "ui-call", - "line": 959, + "line": 1014, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Last Voice Issue", "surface": "apple", @@ -9611,7 +9659,7 @@ }, { "kind": "ui-call", - "line": 962, + "line": 1017, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Transport", "surface": "apple", @@ -9619,7 +9667,7 @@ }, { "kind": "ui-call", - "line": 964, + "line": 1019, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "API Key", "surface": "apple", @@ -9627,7 +9675,7 @@ }, { "kind": "ui-call", - "line": 974, + "line": 1029, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Show Talk Control", "surface": "apple", @@ -9635,7 +9683,7 @@ }, { "kind": "ui-call", - "line": 975, + "line": 1030, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Default Share Instruction", "surface": "apple", @@ -9643,7 +9691,7 @@ }, { "kind": "ui-call", - "line": 982, + "line": 1037, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Run Share Self-Test", "surface": "apple", @@ -9651,7 +9699,7 @@ }, { "kind": "ui-call", - "line": 1003, + "line": 1058, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Discovery Debug Logs", "surface": "apple", @@ -9659,7 +9707,7 @@ }, { "kind": "ui-call", - "line": 1006, + "line": 1061, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Debug Screen Status", "surface": "apple", @@ -9667,7 +9715,7 @@ }, { "kind": "ui-named-argument", - "line": 1010, + "line": 1065, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Discovery Logs", "surface": "apple", @@ -9675,7 +9723,7 @@ }, { "kind": "ui-call", - "line": 1020, + "line": 1075, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Device Name", "surface": "apple", @@ -9683,7 +9731,7 @@ }, { "kind": "ui-call", - "line": 1022, + "line": 1077, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "Instance ID", "surface": "apple", @@ -9691,7 +9739,7 @@ }, { "kind": "conditional-branch", - "line": 1058, + "line": 1113, "path": "apps/ios/Sources/Design/SettingsProTabSections.swift", "source": "On", "surface": "apple", @@ -10211,7 +10259,7 @@ }, { "kind": "ui-named-argument", - "line": 77, + "line": 79, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Host", "surface": "apple", @@ -10219,7 +10267,7 @@ }, { "kind": "ui-named-argument", - "line": 80, + "line": 82, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Node", "surface": "apple", @@ -10227,7 +10275,7 @@ }, { "kind": "ui-named-argument", - "line": 83, + "line": 85, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Agent", "surface": "apple", @@ -10235,7 +10283,7 @@ }, { "kind": "ui-named-argument", - "line": 86, + "line": 88, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Expires", "surface": "apple", @@ -10243,7 +10291,7 @@ }, { "kind": "ui-call", - "line": 100, + "line": 102, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Resolving…", "surface": "apple", @@ -10251,7 +10299,7 @@ }, { "kind": "ui-call", - "line": 110, + "line": 112, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Allow Once", "surface": "apple", @@ -10259,7 +10307,7 @@ }, { "kind": "ui-call", - "line": 120, + "line": 122, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Allow Always", "surface": "apple", @@ -10267,7 +10315,7 @@ }, { "kind": "ui-call", - "line": 131, + "line": 133, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Deny", "surface": "apple", @@ -10275,7 +10323,7 @@ }, { "kind": "ui-call", - "line": 140, + "line": 142, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "Cancel", "surface": "apple", @@ -10283,7 +10331,7 @@ }, { "kind": "conditional-branch", - "line": 170, + "line": 172, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "about 1 minute", "surface": "apple", @@ -10291,7 +10339,7 @@ }, { "kind": "conditional-branch", - "line": 170, + "line": 172, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "about \\(minutes) minutes", "surface": "apple", @@ -10299,7 +10347,7 @@ }, { "kind": "conditional-branch", - "line": 173, + "line": 175, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "about 1 hour", "surface": "apple", @@ -10307,7 +10355,7 @@ }, { "kind": "conditional-branch", - "line": 173, + "line": 175, "path": "apps/ios/Sources/Gateway/ExecApprovalPromptDialog.swift", "source": "about \\(hours) hours", "surface": "apple", @@ -10475,7 +10523,7 @@ }, { "kind": "ui-call", - "line": 57, + "line": 56, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Connecting…", "surface": "apple", @@ -10483,7 +10531,7 @@ }, { "kind": "ui-call", - "line": 60, + "line": 59, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Connect", "surface": "apple", @@ -10491,7 +10539,7 @@ }, { "kind": "ui-call", - "line": 78, + "line": 76, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Not now", "surface": "apple", @@ -10499,7 +10547,7 @@ }, { "kind": "ui-call", - "line": 84, + "line": 81, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Don’t show this again", "surface": "apple", @@ -10507,7 +10555,7 @@ }, { "kind": "ui-call", - "line": 87, + "line": 84, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "No gateways found yet. Make sure your gateway is running and Bonjour discovery is enabled.", "surface": "apple", @@ -10515,7 +10563,7 @@ }, { "kind": "ui-modifier", - "line": 94, + "line": 91, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Quick Setup", "surface": "apple", @@ -10523,7 +10571,7 @@ }, { "kind": "ui-call", - "line": 102, + "line": 99, "path": "apps/ios/Sources/Gateway/GatewayQuickSetupSheet.swift", "source": "Close", "surface": "apple", @@ -10955,7 +11003,7 @@ }, { "kind": "ui-call", - "line": 18, + "line": 19, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Turn this device into a secure OpenClaw node for chat, voice, camera, and device tools.", "surface": "apple", @@ -10963,7 +11011,7 @@ }, { "kind": "ui-call", - "line": 26, + "line": 27, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Connect to your gateway", "surface": "apple", @@ -10971,7 +11019,7 @@ }, { "kind": "ui-call", - "line": 27, + "line": 28, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Choose device permissions", "surface": "apple", @@ -10979,7 +11027,7 @@ }, { "kind": "ui-call", - "line": 28, + "line": 29, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Use OpenClaw from your phone", "surface": "apple", @@ -10987,7 +11035,7 @@ }, { "kind": "ui-call", - "line": 48, + "line": 49, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Security notice", "surface": "apple", @@ -10995,7 +11043,7 @@ }, { "kind": "ui-call-concatenated", - "line": 50, + "line": 52, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "The connected OpenClaw agent can use device capabilities you enable, such as camera, microphone, photos, contacts, calendar, and location. Continue only if you trust the gateway and agent you connect to.", "surface": "apple", @@ -11003,7 +11051,7 @@ }, { "kind": "ui-call", - "line": 72, + "line": 74, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Continue", "surface": "apple", @@ -11019,7 +11067,7 @@ }, { "kind": "ui-call", - "line": 101, + "line": 102, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Scan a QR code from your OpenClaw gateway or continue with manual setup.", "surface": "apple", @@ -11027,7 +11075,7 @@ }, { "kind": "ui-call", - "line": 108, + "line": 109, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "How to pair", "surface": "apple", @@ -11035,7 +11083,7 @@ }, { "kind": "ui-call", - "line": 110, + "line": 111, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "In your OpenClaw chat, run", "surface": "apple", @@ -11043,7 +11091,7 @@ }, { "kind": "ui-call", - "line": 113, + "line": 114, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "/pair qr", "surface": "apple", @@ -11051,7 +11099,7 @@ }, { "kind": "ui-call", - "line": 115, + "line": 116, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Then scan the QR code here to connect this device.", "surface": "apple", @@ -11059,7 +11107,7 @@ }, { "kind": "ui-call", - "line": 134, + "line": 135, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Scan QR Code", "surface": "apple", @@ -11067,7 +11115,7 @@ }, { "kind": "ui-call", - "line": 143, + "line": 142, "path": "apps/ios/Sources/Onboarding/OnboardingWizardSteps.swift", "source": "Set Up Manually", "surface": "apple", @@ -11243,7 +11291,7 @@ }, { "kind": "ui-call", - "line": 403, + "line": 390, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Mode", "surface": "apple", @@ -11251,7 +11299,7 @@ }, { "kind": "ui-call", - "line": 404, + "line": 391, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Discovery", "surface": "apple", @@ -11259,7 +11307,7 @@ }, { "kind": "ui-call", - "line": 406, + "line": 393, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Progress", "surface": "apple", @@ -11267,7 +11315,7 @@ }, { "kind": "ui-call", - "line": 408, + "line": 395, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Status", "surface": "apple", @@ -11275,7 +11323,7 @@ }, { "kind": "ui-call", - "line": 425, + "line": 412, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Choose a mode first.", "surface": "apple", @@ -11283,7 +11331,7 @@ }, { "kind": "ui-call", - "line": 426, + "line": 413, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Back to Mode Selection", "surface": "apple", @@ -11291,7 +11339,7 @@ }, { "kind": "ui-call", - "line": 435, + "line": 422, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Discovered Gateways", "surface": "apple", @@ -11299,7 +11347,7 @@ }, { "kind": "ui-call", - "line": 437, + "line": 424, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "No gateways found yet.", "surface": "apple", @@ -11307,7 +11355,7 @@ }, { "kind": "ui-call", - "line": 460, + "line": 447, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Resolving…", "surface": "apple", @@ -11315,7 +11363,7 @@ }, { "kind": "ui-call", - "line": 470, + "line": 457, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Restart Discovery", "surface": "apple", @@ -11323,7 +11371,7 @@ }, { "kind": "ui-named-argument", - "line": 476, + "line": 463, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Manual Fallback", "surface": "apple", @@ -11331,7 +11379,7 @@ }, { "kind": "ui-named-argument", - "line": 481, + "line": 468, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Domain Settings", "surface": "apple", @@ -11339,7 +11387,7 @@ }, { "kind": "ui-call", - "line": 494, + "line": 481, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Developer Local", "surface": "apple", @@ -11347,7 +11395,7 @@ }, { "kind": "ui-call", - "line": 496, + "line": 483, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Default host is localhost. Use your Mac LAN IP if simulator networking requires it.", "surface": "apple", @@ -11355,7 +11403,7 @@ }, { "kind": "ui-call", - "line": 519, + "line": 506, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Gateway rejected credentials. Scan a fresh QR code or update token/password.", "surface": "apple", @@ -11363,7 +11411,7 @@ }, { "kind": "ui-call", - "line": 523, + "line": 510, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Auth token looks valid.", "surface": "apple", @@ -11371,7 +11419,7 @@ }, { "kind": "ui-call", - "line": 534, + "line": 521, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Resume After Approval", "surface": "apple", @@ -11379,7 +11427,7 @@ }, { "kind": "ui-call", - "line": 538, + "line": 525, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Pairing Approval", "surface": "apple", @@ -11387,7 +11435,7 @@ }, { "kind": "ui-call-concatenated", - "line": 547, + "line": 534, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Approve this device on the gateway.\n1) `\\(commandLine)`\n2) `/pair approve` in your OpenClaw chat\n\\(requestLine)\nOpenClaw will also retry automatically when you return to this app.", "surface": "apple", @@ -11395,7 +11443,7 @@ }, { "kind": "ui-call", - "line": 560, + "line": 547, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Scan QR Code Again", "surface": "apple", @@ -11403,7 +11451,7 @@ }, { "kind": "ui-call", - "line": 571, + "line": 558, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Retry Connection", "surface": "apple", @@ -11411,7 +11459,7 @@ }, { "kind": "ui-call", - "line": 609, + "line": 597, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Open OpenClaw", "surface": "apple", @@ -11419,7 +11467,7 @@ }, { "kind": "ui-call", - "line": 623, + "line": 609, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Paste setup code", "surface": "apple", @@ -11427,7 +11475,7 @@ }, { "kind": "ui-call", - "line": 637, + "line": 623, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Applying...", "surface": "apple", @@ -11435,7 +11483,7 @@ }, { "kind": "ui-call", - "line": 640, + "line": 626, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Apply Setup Code", "surface": "apple", @@ -11443,7 +11491,7 @@ }, { "kind": "ui-call", - "line": 653, + "line": 639, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Setup Code", "surface": "apple", @@ -11451,7 +11499,7 @@ }, { "kind": "ui-call", - "line": 655, + "line": 641, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Use this if you received a setup code instead of a QR code.", "surface": "apple", @@ -11459,7 +11507,7 @@ }, { "kind": "ui-call", - "line": 661, + "line": 647, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Host", "surface": "apple", @@ -11467,7 +11515,7 @@ }, { "kind": "ui-call", - "line": 664, + "line": 650, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Port", "surface": "apple", @@ -11475,7 +11523,7 @@ }, { "kind": "ui-call", - "line": 666, + "line": 652, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Use TLS", "surface": "apple", @@ -11483,7 +11531,7 @@ }, { "kind": "ui-call", - "line": 667, + "line": 653, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Discovery Domain (optional)", "surface": "apple", @@ -11491,7 +11539,7 @@ }, { "kind": "ui-call", - "line": 671, + "line": 657, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Gateway Auth Token", "surface": "apple", @@ -11499,7 +11547,7 @@ }, { "kind": "ui-call", - "line": 674, + "line": 660, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Gateway Password", "surface": "apple", @@ -11507,7 +11555,7 @@ }, { "kind": "ui-call", - "line": 688, + "line": 674, "path": "apps/ios/Sources/Onboarding/OnboardingWizardView.swift", "source": "Connecting…", "surface": "apple", @@ -11907,7 +11955,7 @@ }, { "kind": "conditional-branch", - "line": 137, + "line": 135, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Limited", "surface": "apple", @@ -11915,7 +11963,7 @@ }, { "kind": "conditional-branch", - "line": 150, + "line": 148, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Read photos you select for the assistant.", "surface": "apple", @@ -11923,7 +11971,7 @@ }, { "kind": "conditional-branch", - "line": 150, + "line": 148, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Read recent photos for the assistant.", "surface": "apple", @@ -11931,7 +11979,7 @@ }, { "kind": "conditional-branch", - "line": 297, + "line": 295, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Allowed", "surface": "apple", @@ -11939,7 +11987,7 @@ }, { "kind": "conditional-branch", - "line": 299, + "line": 297, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Add-Only", "surface": "apple", @@ -11947,7 +11995,7 @@ }, { "kind": "conditional-branch", - "line": 301, + "line": 299, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Not Set", "surface": "apple", @@ -11955,7 +12003,7 @@ }, { "kind": "conditional-branch", - "line": 303, + "line": 301, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Not Allowed", "surface": "apple", @@ -11963,7 +12011,7 @@ }, { "kind": "conditional-branch", - "line": 305, + "line": 303, "path": "apps/ios/Sources/Settings/PrivacyAccessSectionView.swift", "source": "Unknown", "surface": "apple", diff --git a/apps/ios/Resources/Licenses/DM Sans.txt b/apps/ios/Resources/Licenses/Inter.txt similarity index 93% rename from apps/ios/Resources/Licenses/DM Sans.txt rename to apps/ios/Resources/Licenses/Inter.txt index 49b0aa492cd3..6ecd18772a31 100644 --- a/apps/ios/Resources/Licenses/DM Sans.txt +++ b/apps/ios/Resources/Licenses/Inter.txt @@ -1,17 +1,14 @@ -DM Sans +Inter -Copyright 2014 The DM Sans Project Authors +Copyright 2016 The Inter Project Authors -Source: https://github.com/google/fonts/tree/main/ofl/dmsans -Version: 2.100, static instances extracted from variable fonts +Source: https://github.com/google/fonts/tree/main/ofl/inter +Version: Google Fonts variable release License: SIL Open Font License 1.1 Bundled files: -- DMSans-Light.ttf -- DMSans-Regular.ttf -- DMSans-Italic.ttf -- DMSans-Medium.ttf -- DMSans-SemiBold.ttf +- Inter[opsz,wght].ttf +- Inter-Italic[opsz,wght].ttf SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 diff --git a/apps/ios/Resources/Licenses/Plus Jakarta Sans.txt b/apps/ios/Resources/Licenses/Red Hat Display.txt similarity index 92% rename from apps/ios/Resources/Licenses/Plus Jakarta Sans.txt rename to apps/ios/Resources/Licenses/Red Hat Display.txt index 8a5d1bb6101d..dbbe90124b5c 100644 --- a/apps/ios/Resources/Licenses/Plus Jakarta Sans.txt +++ b/apps/ios/Resources/Licenses/Red Hat Display.txt @@ -1,18 +1,13 @@ -Plus Jakarta Sans +Red Hat Display -Copyright 2020 The Plus Jakarta Sans Project Authors +Copyright 2024 The Red Hat Project Authors (https://github.com/RedHatOfficial/RedHatFont) -Source: https://github.com/tokotype/PlusJakartaSans -Version: 2.7.1 +Source: https://github.com/google/fonts/tree/main/ofl/redhatdisplay +Version: Google Fonts variable release License: SIL Open Font License 1.1 Bundled files: -- PlusJakartaSans-Light.ttf -- PlusJakartaSans-Regular.ttf -- PlusJakartaSans-Medium.ttf -- PlusJakartaSans-SemiBold.ttf -- PlusJakartaSans-Bold.ttf -- PlusJakartaSans-ExtraBold.ttf +- RedHatDisplay[wght].ttf SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 diff --git a/apps/ios/Sources/Design/AgentProTab+Overview.swift b/apps/ios/Sources/Design/AgentProTab+Overview.swift index a5fe1810716f..7d6116816b3d 100644 --- a/apps/ios/Sources/Design/AgentProTab+Overview.swift +++ b/apps/ios/Sources/Design/AgentProTab+Overview.swift @@ -77,7 +77,7 @@ extension AgentProTab { } } label: { Image(systemName: "xmark.circle.fill") - .font(OpenClawType.title3) + .font(.title3) .foregroundStyle(.secondary) .frame(width: 44, height: 44) .contentShape(Circle()) @@ -313,7 +313,7 @@ extension AgentProTab { func agentAvatar(_ agent: AgentSummary, state: AgentRosterState) -> some View { ZStack(alignment: .bottomTrailing) { Text(self.agentBadge(for: agent)) - .font(.system(size: self.agentBadge(for: agent).count > 2 ? 14 : 18, weight: .bold, design: .rounded)) + .font(OpenClawType.avatar(size: self.agentBadge(for: agent).count > 2 ? 14 : 18)) .foregroundStyle(.white) .minimumScaleFactor(0.62) .lineLimit(1) diff --git a/apps/ios/Sources/Design/AgentProTab+Skills.swift b/apps/ios/Sources/Design/AgentProTab+Skills.swift index 828cb2828785..7bbd3505db73 100644 --- a/apps/ios/Sources/Design/AgentProTab+Skills.swift +++ b/apps/ios/Sources/Design/AgentProTab+Skills.swift @@ -540,7 +540,7 @@ extension AgentProTab { self.detailMetric(label: "Source", value: self.normalized(skill.source) ?? "unknown") if let filePath = self.normalized(skill.filePath) { Text(filePath) - .font(OpenClawType.monoSmall) + .font(OpenClawType.monoCaption2) .foregroundStyle(.secondary) .textSelection(.enabled) } diff --git a/apps/ios/Sources/Design/ChatProTab.swift b/apps/ios/Sources/Design/ChatProTab.swift index 57a088c61c19..117a844e3055 100644 --- a/apps/ios/Sources/Design/ChatProTab.swift +++ b/apps/ios/Sources/Design/ChatProTab.swift @@ -129,7 +129,7 @@ struct ChatProTab: View { private var headerIdentityBadge: some View { if self.showsAgentBadge { Text(self.agentBadge) - .font(.system(size: self.agentBadge.count > 2 ? 13 : 16, weight: .bold, design: .rounded)) + .font(OpenClawType.avatar(size: self.agentBadge.count > 2 ? 13 : 16)) .foregroundStyle(.white) .minimumScaleFactor(0.6) .lineLimit(1) diff --git a/apps/ios/Sources/Design/CommandCenterSupport.swift b/apps/ios/Sources/Design/CommandCenterSupport.swift index ea7b041fd373..b3466c341e84 100644 --- a/apps/ios/Sources/Design/CommandCenterSupport.swift +++ b/apps/ios/Sources/Design/CommandCenterSupport.swift @@ -97,7 +97,7 @@ struct CommandSessionRow: View { struct CommandViewMoreRow: View { var body: some View { Label("View More", systemImage: "chevron.right") - .font(OpenClawType.subheadSemiBold) + .font(OpenClawType.subheadBold) .foregroundStyle(OpenClawBrand.accent) .frame(maxWidth: .infinity) .padding(.vertical, 10) @@ -113,7 +113,7 @@ struct CommandEmptyStateRow: View { var body: some View { HStack(spacing: 10) { Image(systemName: self.icon) - .font(OpenClawType.captionSemiBold) + .font(OpenClawType.captionBold) .foregroundStyle(OpenClawBrand.ok) .frame(width: 30, height: 30) .background { diff --git a/apps/ios/Sources/Design/CommandCenterTab.swift b/apps/ios/Sources/Design/CommandCenterTab.swift index 7cd175d8475f..370a0ee64c09 100644 --- a/apps/ios/Sources/Design/CommandCenterTab.swift +++ b/apps/ios/Sources/Design/CommandCenterTab.swift @@ -673,7 +673,7 @@ struct CommandSessionsScreen: View { VStack(spacing: 0) { HStack(spacing: 8) { Text("Recent sessions") - .font(OpenClawType.subheadSemiBold) + .font(OpenClawType.subheadBold) Spacer(minLength: 8) if self.isLoading { ProgressView() diff --git a/apps/ios/Sources/Design/IPadSidebarFeaturePreviews.swift b/apps/ios/Sources/Design/IPadSidebarFeaturePreviews.swift index 30e3d13b4b1f..377d05c937a7 100644 --- a/apps/ios/Sources/Design/IPadSidebarFeaturePreviews.swift +++ b/apps/ios/Sources/Design/IPadSidebarFeaturePreviews.swift @@ -103,9 +103,9 @@ private struct IPadWorkboardCompactRowsPreview: View { private var previewHeader: some View { VStack(alignment: .leading, spacing: 4) { Text("Phone queue") - .font(.headline) + .font(OpenClawType.headline) Text("Tap for detail, swipe or long-press for card actions.") - .font(.caption) + .font(OpenClawType.caption) .foregroundStyle(.secondary) } } @@ -161,9 +161,9 @@ private struct IPadSkillWorkshopCompactRowsPreview: View { private var previewHeader: some View { VStack(alignment: .leading, spacing: 4) { Text("Phone proposals") - .font(.headline) + .font(OpenClawType.headline) Text("Tap for detail, swipe or long-press for proposal actions.") - .font(.caption) + .font(OpenClawType.caption) .foregroundStyle(.secondary) } } @@ -277,7 +277,7 @@ private struct IPadActivityStatesPreview: View { private func previewHeader(_ title: String) -> some View { Text(title) - .font(.caption.weight(.semibold)) + .font(OpenClawType.captionSemiBold) .foregroundStyle(.secondary) .textCase(.uppercase) } @@ -423,7 +423,7 @@ private struct IPadWorkboardStatesPreview: View { private func previewHeader(_ title: String) -> some View { Text(title) - .font(.caption.weight(.semibold)) + .font(OpenClawType.captionSemiBold) .foregroundStyle(.secondary) .textCase(.uppercase) } @@ -525,7 +525,7 @@ private struct IPadSkillWorkshopStatesPreview: View { private func previewHeader(_ title: String) -> some View { Text(title) - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) .foregroundStyle(.secondary) } @@ -593,9 +593,9 @@ private struct IPadSkillWorkshopKanbanPreview: View { private var previewHeader: some View { VStack(alignment: .leading, spacing: 4) { Text("iPad kanban") - .font(.headline) + .font(OpenClawType.headline) Text("Wide layout with populated, empty, held, and custom proposal lanes.") - .font(.caption) + .font(OpenClawType.caption) .foregroundStyle(.secondary) } .padding(.horizontal, OpenClawProMetric.pagePadding) diff --git a/apps/ios/Sources/Design/IPadSidebarScreenChrome.swift b/apps/ios/Sources/Design/IPadSidebarScreenChrome.swift index 2eef2d1e11fc..5e80c82b7eaa 100644 --- a/apps/ios/Sources/Design/IPadSidebarScreenChrome.swift +++ b/apps/ios/Sources/Design/IPadSidebarScreenChrome.swift @@ -34,7 +34,7 @@ struct IPadSidebarScreenChrome: View { OpenClawAdaptiveHeaderRow( title: self.title, subtitle: self.subtitle, - titleFont: self.isCompactHeight ? .headline.weight(.semibold) : .title2.weight(.semibold), + titleFont: self.isCompactHeight ? OpenClawType.headline : OpenClawType.title2SemiBold, subtitleLineLimit: self.isCompactHeight ? 1 : 2) { if let headerLeadingAction { diff --git a/apps/ios/Sources/Design/IPadSkillWorkshopScreen.swift b/apps/ios/Sources/Design/IPadSkillWorkshopScreen.swift index e93abff60c7c..3b0111b6a3d4 100644 --- a/apps/ios/Sources/Design/IPadSkillWorkshopScreen.swift +++ b/apps/ios/Sources/Design/IPadSkillWorkshopScreen.swift @@ -448,7 +448,7 @@ struct IPadSkillWorkshopScreen: View { .font(OpenClawType.subheadSemiBold) ForEach(proposal.supportFiles, id: \.path) { file in Text(file.path) - .font(OpenClawType.monoSmall) + .font(OpenClawType.monoCaption2) .foregroundStyle(.secondary) .lineLimit(1) } diff --git a/apps/ios/Sources/Design/IPadWorkboardScreen.swift b/apps/ios/Sources/Design/IPadWorkboardScreen.swift index 319db227fe3f..929374c8abb4 100644 --- a/apps/ios/Sources/Design/IPadWorkboardScreen.swift +++ b/apps/ios/Sources/Design/IPadWorkboardScreen.swift @@ -1099,7 +1099,7 @@ struct IPadWorkboardQueueRow: View { self.actionMenuItems } label: { Image(systemName: self.isBusy ? "hourglass" : "ellipsis.circle") - .font(OpenClawType.headline) + .font(.system(size: 19, weight: .semibold)) .frame(width: 36, height: 36) .contentShape(Rectangle()) } diff --git a/apps/ios/Sources/Design/LicenseDocuments.swift b/apps/ios/Sources/Design/LicenseDocuments.swift index 486bbc78c9b1..042c2c4bafcb 100644 --- a/apps/ios/Sources/Design/LicenseDocuments.swift +++ b/apps/ios/Sources/Design/LicenseDocuments.swift @@ -75,7 +75,7 @@ struct LicenseDocumentDetailView: View { var body: some View { ScrollView { Text(verbatim: self.document.body) - .font(.system(.footnote, design: .monospaced)) + .font(OpenClawType.monoFootnote) .foregroundStyle(.primary) .textSelection(.enabled) .accessibilityIdentifier("licenses-detail-text") diff --git a/apps/ios/Sources/Design/OpenClawDocsScreen.swift b/apps/ios/Sources/Design/OpenClawDocsScreen.swift index a2808f5b94b2..eb5f9eb83705 100644 --- a/apps/ios/Sources/Design/OpenClawDocsScreen.swift +++ b/apps/ios/Sources/Design/OpenClawDocsScreen.swift @@ -120,7 +120,7 @@ struct OpenClawDocsScreen: View { } Spacer(minLength: 8) Image(systemName: "arrow.up.right") - .font(OpenClawType.captionSemiBold) + .font(OpenClawType.captionBold) .foregroundStyle(.secondary) } .padding(.horizontal, 14) diff --git a/apps/ios/Sources/Design/OpenClawProComponents.swift b/apps/ios/Sources/Design/OpenClawProComponents.swift index e3298b75a3c7..614e8f28e7ed 100644 --- a/apps/ios/Sources/Design/OpenClawProComponents.swift +++ b/apps/ios/Sources/Design/OpenClawProComponents.swift @@ -274,7 +274,7 @@ struct OpenClawSidebarRevealButton: View { var body: some View { let button = Button(action: headerAction.action) { Image(systemName: self.headerAction.systemName) - .font(OpenClawType.subhead) + .font(OpenClawType.subheadSemiBold) .frame( width: OpenClawProMetric.compactControlSize, height: OpenClawProMetric.compactControlSize) @@ -389,7 +389,7 @@ struct OpenClawNoticeBanner: View { .fixedSize(horizontal: false, vertical: true) case let .requestID(value): Text("Request ID: \(value)") - .font(OpenClawType.monoSmall) + .font(OpenClawType.monoSmallMedium) .foregroundStyle(.secondary) .textSelection(.enabled) } @@ -400,7 +400,7 @@ struct OpenClawNoticeBanner: View { struct OpenClawAdaptiveHeaderRow: View { let title: String let subtitle: String? - var titleFont: Font = OpenClawType.title3 + var titleFont: Font = OpenClawType.title3SemiBold var subtitleFont: Font = OpenClawType.subhead var subtitleLineLimit: Int? = 2 @ViewBuilder let leading: Leading @@ -409,7 +409,7 @@ struct OpenClawAdaptiveHeaderRow: View { init( title: String, subtitle: String? = nil, - titleFont: Font = OpenClawType.title3, + titleFont: Font = OpenClawType.title3SemiBold, subtitleFont: Font = OpenClawType.subhead, subtitleLineLimit: Int? = 2, @ViewBuilder leading: () -> Leading, @@ -710,7 +710,7 @@ struct ProMetricTile: View { VStack(alignment: .leading, spacing: 2) { Text(self.value) - .font(OpenClawType.headline) + .font(OpenClawType.headlineBold) .lineLimit(1) .minimumScaleFactor(0.72) Text(self.title) diff --git a/apps/ios/Sources/Design/OpenClawTypography.swift b/apps/ios/Sources/Design/OpenClawTypography.swift index 1bf48322ba73..b4ecfc442574 100644 --- a/apps/ios/Sources/Design/OpenClawTypography.swift +++ b/apps/ios/Sources/Design/OpenClawTypography.swift @@ -1,85 +1,118 @@ +import CoreText import SwiftUI import UIKit enum OpenClawType { - // MARK: - Display — Plus Jakarta Sans + // MARK: - Display — Red Hat Display static var title1: Font { - scaledDisplay(name: Display.extraBold, size: 34, relativeTo: .largeTitle) + scaledDisplay(weight: Display.heavyTitle, size: 34, relativeTo: .largeTitle) } static var title2: Font { - scaledDisplay(name: Display.bold, size: 28, relativeTo: .title1) + scaledDisplay(weight: Display.heavyTitle, size: 28, relativeTo: .title1) } static var title3: Font { - scaledDisplay(name: Display.bold, size: 22, relativeTo: .title2) + scaledDisplay(weight: Display.opticalBold, size: 22, relativeTo: .title2) + } + + static var title3SemiBold: Font { + scaledDisplay(weight: Display.opticalSemiBold, size: 22, relativeTo: .title2) } static var headline: Font { - scaledDisplay(name: Display.semiBold, size: 17, relativeTo: .headline) + scaledDisplay(weight: Display.opticalSemiBold, size: 17, relativeTo: .headline) } - // MARK: - Body — DM Sans + static var headlineBold: Font { + scaledDisplay(weight: Display.opticalBold, size: 17, relativeTo: .headline) + } + + static func display(size: CGFloat, weight: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font { + self.scaledDisplay(weight: weight, size: size, relativeTo: textStyle) + } + + // MARK: - Body — Inter static var body: Font { - scaledBody(name: Body.regular, size: 17, relativeTo: .body) + scaledBody(weight: Body.regular, size: 17, relativeTo: .body) } static var callout: Font { - scaledBody(name: Body.regular, size: 16, relativeTo: .callout) + scaledBody(weight: Body.regular, size: 16, relativeTo: .callout) } static var subhead: Font { - scaledBody(name: Body.medium, size: 15, relativeTo: .subheadline) + scaledBody(weight: Body.regular, size: 15, relativeTo: .subheadline) + } + + static var subheadMedium: Font { + scaledBody(weight: Body.medium, size: 15, relativeTo: .subheadline) } static var subheadSemiBold: Font { - scaledDisplay(name: Display.semiBold, size: 15, relativeTo: .subheadline) + scaledDisplay(weight: Display.opticalSemiBold, size: 15, relativeTo: .subheadline) + } + + static var subheadBold: Font { + scaledDisplay(weight: Display.opticalBold, size: 15, relativeTo: .subheadline) } static var footnote: Font { - scaledBody(name: Body.regular, size: 13, relativeTo: .footnote) + scaledBody(weight: Body.regular, size: 13, relativeTo: .footnote) } static var footnoteMedium: Font { - scaledBody(name: Body.medium, size: 13, relativeTo: .footnote) + scaledBody(weight: Body.medium, size: 13, relativeTo: .footnote) } static var footnoteSemiBold: Font { - scaledBody(name: Body.semiBold, size: 13, relativeTo: .footnote) + scaledBody(weight: Body.semiBold, size: 13, relativeTo: .footnote) } static var caption: Font { - scaledBody(name: Body.regular, size: 12, relativeTo: .caption1) + scaledBody(weight: Body.regular, size: 12, relativeTo: .caption1) } static var captionMedium: Font { - scaledBody(name: Body.medium, size: 12, relativeTo: .caption1) + scaledBody(weight: Body.medium, size: 12, relativeTo: .caption1) } static var captionSemiBold: Font { - scaledBody(name: Body.semiBold, size: 12, relativeTo: .caption1) + scaledBody(weight: Body.semiBold, size: 12, relativeTo: .caption1) + } + + static var captionBold: Font { + scaledBody(weight: Body.bold, size: 12, relativeTo: .caption1) + } + + static func body(size: CGFloat, weight: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font { + self.scaledBody(weight: weight, size: size, relativeTo: textStyle) + } + + static func avatar(size: CGFloat) -> Font { + self.scaledBody(weight: Body.bold, size: size, relativeTo: .caption1) } static var caption2: Font { - scaledBody(name: Body.regular, size: 11, relativeTo: .caption2) + scaledBody(weight: Body.regular, size: 11, relativeTo: .caption2) } static var caption2Medium: Font { - scaledBody(name: Body.medium, size: 11, relativeTo: .caption2) + scaledBody(weight: Body.medium, size: 11, relativeTo: .caption2) } static var caption2SemiBold: Font { - scaledBody(name: Body.semiBold, size: 11, relativeTo: .caption2) + scaledBody(weight: Body.semiBold, size: 11, relativeTo: .caption2) } static var caption2Bold: Font { - scaledDisplay(name: Display.bold, size: 11, relativeTo: .caption2) + scaledDisplay(weight: Display.opticalBold, size: 11, relativeTo: .caption2) } static var title2SemiBold: Font { - scaledDisplay(name: Display.semiBold, size: 28, relativeTo: .title1) + scaledDisplay(weight: Display.opticalSemiBold, size: 28, relativeTo: .title1) } // MARK: - Mono — JetBrains Mono @@ -92,6 +125,14 @@ enum OpenClawType { scaledMono(name: Mono.regular, size: 12, relativeTo: .caption1) } + static var monoSmallMedium: Font { + scaledMono(name: Mono.medium, size: 12, relativeTo: .caption1) + } + + static var monoCaption2: Font { + scaledMono(name: Mono.regular, size: 11, relativeTo: .caption2) + } + static var monoFootnote: Font { scaledMono(name: Mono.regular, size: 13, relativeTo: .footnote) } @@ -102,37 +143,28 @@ enum OpenClawType { /// PostScript names for bundled fonts. Keep aligned with `UIAppFonts` in `project.yml`. static let registeredPostScriptNames: [String] = [ - Display.light, - Display.regular, - Display.medium, - Display.semiBold, - Display.bold, - Display.extraBold, - Body.light, - Body.regular, - Body.italic, - Body.medium, - Body.semiBold, + Display.postScriptName, + Body.postScriptName, + Body.italicPostScriptName, Mono.regular, Mono.medium, Mono.semiBold, ] private enum Display { - static let light = "PlusJakartaSans-Light" - static let regular = "PlusJakartaSans-Regular" - static let medium = "PlusJakartaSans-Medium" - static let semiBold = "PlusJakartaSans-SemiBold" - static let bold = "PlusJakartaSans-Bold" - static let extraBold = "PlusJakartaSans-ExtraBold" + static let postScriptName = "RedHatDisplay-Regular" + static let opticalSemiBold: CGFloat = 650 + static let opticalBold: CGFloat = 750 + static let heavyTitle: CGFloat = 800 } private enum Body { - static let light = "DMSans-Light" - static let regular = "DMSans-Regular" - static let italic = "DMSans-Italic" - static let medium = "DMSans-Medium" - static let semiBold = "DMSans-SemiBold" + static let postScriptName = "Inter-Regular" + static let italicPostScriptName = "Inter-Italic" + static let regular: CGFloat = 400 + static let medium: CGFloat = 500 + static let semiBold: CGFloat = 600 + static let bold: CGFloat = 700 } private enum Mono { @@ -141,20 +173,34 @@ enum OpenClawType { static let semiBold = "JetBrainsMono-SemiBold" } + private static let fontWeightAxis = NSNumber(value: 2_003_265_652) // "wght" + private static let opticalSizeAxis = NSNumber(value: 1_869_640_570) // "opsz" + private static func scaledDisplay( - name: String, + weight: CGFloat, size: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font { - self.scaledFont(name: name, size: size, relativeTo: textStyle) + self.scaledVariableFont( + name: Display.postScriptName, + size: size, + relativeTo: textStyle, + variations: [self.fontWeightAxis: weight]) } private static func scaledBody( - name: String, + weight: CGFloat, size: CGFloat, relativeTo textStyle: UIFont.TextStyle) -> Font { - self.scaledFont(name: name, size: size, relativeTo: textStyle) + self.scaledVariableFont( + name: Body.postScriptName, + size: size, + relativeTo: textStyle, + variations: [ + self.fontWeightAxis: weight, + self.opticalSizeAxis: min(max(size, 14), 32), + ]) } private static func scaledMono( @@ -165,6 +211,27 @@ enum OpenClawType { self.scaledFont(name: name, size: size, relativeTo: textStyle) } + private static func scaledVariableFont( + name: String, + size: CGFloat, + relativeTo textStyle: UIFont.TextStyle, + variations: [NSNumber: CGFloat]) -> Font + { + guard UIFont(name: name, size: size) != nil else { + let fallback = UIFont.systemFont(ofSize: size) + let scaledFallback = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: fallback) + return Font(scaledFallback) + } + + let descriptor = UIFontDescriptor(fontAttributes: [ + .name: name, + kCTFontVariationAttribute as UIFontDescriptor.AttributeName: variations, + ]) + let base = UIFont(descriptor: descriptor, size: size) + let scaled = UIFontMetrics(forTextStyle: textStyle).scaledFont(for: base) + return Font(scaled) + } + private static func scaledFont( name: String, size: CGFloat, diff --git a/apps/ios/Sources/Design/RootTabsPhoneControlHub.swift b/apps/ios/Sources/Design/RootTabsPhoneControlHub.swift index 2283e88cd0d3..e97a983a9117 100644 --- a/apps/ios/Sources/Design/RootTabsPhoneControlHub.swift +++ b/apps/ios/Sources/Design/RootTabsPhoneControlHub.swift @@ -60,10 +60,10 @@ struct RootTabsPhoneControlHub: View { color: self.gatewayStateColor) VStack(alignment: .leading, spacing: 2) { Text("Gateway") - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) .foregroundStyle(.primary) Text(self.sidebarActiveAgentTitle) - .font(.footnote) + .font(OpenClawType.footnote) .foregroundStyle(.secondary) .lineLimit(1) } @@ -71,10 +71,10 @@ struct RootTabsPhoneControlHub: View { HStack(spacing: 6) { ProStatusDot(color: self.gatewayStateColor) Text(self.gatewayStateText) - .font(.footnote.weight(.semibold)) + .font(OpenClawType.footnoteSemiBold) .foregroundStyle(self.gatewayStateColor) Image(systemName: "chevron.right") - .font(.caption.weight(.semibold)) + .font(OpenClawType.captionSemiBold) .foregroundStyle(.secondary) } } @@ -95,7 +95,7 @@ struct RootTabsPhoneControlHub: View { self.rowLabel(destination) Spacer(minLength: 8) Image(systemName: "chevron.right") - .font(.caption.weight(.semibold)) + .font(OpenClawType.captionSemiBold) .foregroundStyle(.secondary) } .contentShape(Rectangle()) @@ -111,6 +111,8 @@ struct RootTabsPhoneControlHub: View { private func rowLabel(_ destination: RootTabs.SidebarDestination) -> some View { Label { Text(destination.title) + .font(OpenClawType.subheadSemiBold) + .foregroundStyle(.primary) } icon: { ProIconBadge(systemName: destination.systemImage, color: .secondary) } diff --git a/apps/ios/Sources/Design/SettingsProTab.swift b/apps/ios/Sources/Design/SettingsProTab.swift index db2845b9add1..324880a23f2e 100644 --- a/apps/ios/Sources/Design/SettingsProTab.swift +++ b/apps/ios/Sources/Design/SettingsProTab.swift @@ -288,7 +288,7 @@ struct HostedPushRelayDisclosureSheet: View { .font(OpenClawType.title2SemiBold) .foregroundStyle(OpenClawBrand.accentForeground) Text("Enable OpenClaw Hosted Push Relay?") - .font(OpenClawType.title3) + .font(OpenClawType.title3SemiBold) Text(self.message) .font(OpenClawType.body) .foregroundStyle(.secondary) diff --git a/apps/ios/Sources/Design/SettingsProTabSections.swift b/apps/ios/Sources/Design/SettingsProTabSections.swift index 74546b00d879..736315acf6cc 100644 --- a/apps/ios/Sources/Design/SettingsProTabSections.swift +++ b/apps/ios/Sources/Design/SettingsProTabSections.swift @@ -42,16 +42,16 @@ extension SettingsProTab { color: .secondary) Text("Appearance") - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) .foregroundStyle(.primary) Spacer(minLength: 8) HStack(spacing: 5) { Text(self.currentAppearancePreference.label) - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) Image(systemName: "chevron.up.chevron.down") - .font(.caption2.weight(.bold)) + .font(OpenClawType.caption2Bold) } .foregroundStyle(OpenClawBrand.accent) } @@ -576,9 +576,9 @@ extension SettingsProTab { ProIconBadge(systemName: "doc.text", color: .secondary) VStack(alignment: .leading, spacing: 3) { Text("No licenses bundled") - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) Text("License files are not available in this build.") - .font(.caption) + .font(OpenClawType.caption) .foregroundStyle(.secondary) .lineLimit(2) } @@ -589,7 +589,7 @@ extension SettingsProTab { let lastDocumentID = documents.last?.id Text("OpenClaw appreciates its partners in the open-source community.") - .font(.footnote) + .font(OpenClawType.footnote) .foregroundStyle(.secondary) .multilineTextAlignment(.center) .fixedSize(horizontal: false, vertical: true) @@ -619,11 +619,11 @@ extension SettingsProTab { HStack(spacing: 12) { ProIconBadge(systemName: "doc.text", color: .secondary) Text(document.title) - .font(.subheadline.weight(.semibold)) + .font(OpenClawType.subheadSemiBold) .foregroundStyle(.primary) Spacer(minLength: 8) Image(systemName: "chevron.right") - .font(.caption.weight(.semibold)) + .font(OpenClawType.captionSemiBold) .foregroundStyle(.tertiary) } .padding(.horizontal, 14) diff --git a/apps/ios/Sources/Design/SettingsProTabSupport.swift b/apps/ios/Sources/Design/SettingsProTabSupport.swift index 4b2e7b9ff776..b816b6395509 100644 --- a/apps/ios/Sources/Design/SettingsProTabSupport.swift +++ b/apps/ios/Sources/Design/SettingsProTabSupport.swift @@ -36,7 +36,7 @@ struct SettingsApprovalRow: View { var body: some View { HStack(spacing: 10) { Image(systemName: self.item.icon) - .font(OpenClawType.captionSemiBold) + .font(OpenClawType.captionBold) .foregroundStyle(.white) .frame(width: 30, height: 30) .background { @@ -54,7 +54,7 @@ struct SettingsApprovalRow: View { } Spacer(minLength: 8) Text(self.item.priority) - .font(OpenClawType.captionSemiBold) + .font(OpenClawType.captionBold) .foregroundStyle(self.item.color) .padding(.horizontal, 9) .padding(.vertical, 5) diff --git a/apps/ios/Sources/Design/TalkProTab.swift b/apps/ios/Sources/Design/TalkProTab.swift index 89f4c3583be4..fb8c5f2235b5 100644 --- a/apps/ios/Sources/Design/TalkProTab.swift +++ b/apps/ios/Sources/Design/TalkProTab.swift @@ -109,8 +109,8 @@ struct TalkProTab: View { OpenClawAdaptiveHeaderRow( title: "Talk", subtitle: self.headerSubtitle, - titleFont: .system(size: 30, weight: .bold), - subtitleFont: .caption.weight(.medium), + titleFont: OpenClawType.display(size: 30, weight: 750, relativeTo: .title1), + subtitleFont: OpenClawType.captionMedium, subtitleLineLimit: 1) { if let headerLeadingAction { @@ -137,14 +137,14 @@ struct TalkProTab: View { .font(OpenClawType.title3) .multilineTextAlignment(.center) Text(self.heroSubtitle) - .font(OpenClawType.subhead) + .font(OpenClawType.subheadMedium) .foregroundStyle(.secondary) .multilineTextAlignment(.center) } Button(action: self.handlePrimaryAction) { Label(self.state.primaryButtonTitle, systemImage: self.state.primaryButtonIcon) - .font(OpenClawType.subheadSemiBold) + .font(OpenClawType.subheadBold) .frame(maxWidth: .infinity) .frame(height: 50) } diff --git a/apps/ios/Sources/Design/TalkRuntimeIssueBanner.swift b/apps/ios/Sources/Design/TalkRuntimeIssueBanner.swift index 201527b11d09..1644dc37d384 100644 --- a/apps/ios/Sources/Design/TalkRuntimeIssueBanner.swift +++ b/apps/ios/Sources/Design/TalkRuntimeIssueBanner.swift @@ -43,7 +43,7 @@ struct TalkRuntimeIssueDetailsSheet: View { Section { VStack(alignment: .leading, spacing: 10) { Text(self.issue.fallbackBannerTitle) - .font(OpenClawType.title3) + .font(OpenClawType.title3SemiBold) Text(self.issue.fallbackBannerMessage) .font(OpenClawType.body) .foregroundStyle(.secondary) diff --git a/apps/ios/Sources/Fonts/DMSans-Italic.ttf b/apps/ios/Sources/Fonts/DMSans-Italic.ttf deleted file mode 100644 index e592ba02c12e..000000000000 Binary files a/apps/ios/Sources/Fonts/DMSans-Italic.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/DMSans-Light.ttf b/apps/ios/Sources/Fonts/DMSans-Light.ttf deleted file mode 100644 index 2ca205897323..000000000000 Binary files a/apps/ios/Sources/Fonts/DMSans-Light.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/DMSans-Medium.ttf b/apps/ios/Sources/Fonts/DMSans-Medium.ttf deleted file mode 100644 index 39ddfe4c3612..000000000000 Binary files a/apps/ios/Sources/Fonts/DMSans-Medium.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/DMSans-Regular.ttf b/apps/ios/Sources/Fonts/DMSans-Regular.ttf deleted file mode 100644 index 9b1d3099a7b4..000000000000 Binary files a/apps/ios/Sources/Fonts/DMSans-Regular.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/DMSans-SemiBold.ttf b/apps/ios/Sources/Fonts/DMSans-SemiBold.ttf deleted file mode 100644 index ccb4b63ebc35..000000000000 Binary files a/apps/ios/Sources/Fonts/DMSans-SemiBold.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/Inter-Italic[opsz,wght].ttf b/apps/ios/Sources/Fonts/Inter-Italic[opsz,wght].ttf new file mode 100644 index 000000000000..c17757842824 Binary files /dev/null and b/apps/ios/Sources/Fonts/Inter-Italic[opsz,wght].ttf differ diff --git a/apps/ios/Sources/Fonts/Inter[opsz,wght].ttf b/apps/ios/Sources/Fonts/Inter[opsz,wght].ttf new file mode 100644 index 000000000000..047c92f6e221 Binary files /dev/null and b/apps/ios/Sources/Fonts/Inter[opsz,wght].ttf differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-Bold.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-Bold.ttf deleted file mode 100644 index 63cb1235b9e5..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-Bold.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-ExtraBold.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-ExtraBold.ttf deleted file mode 100644 index bc6f1d582295..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-ExtraBold.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-Light.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-Light.ttf deleted file mode 100644 index 164085698830..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-Light.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-Medium.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-Medium.ttf deleted file mode 100644 index 7576ff2609c4..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-Medium.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-Regular.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-Regular.ttf deleted file mode 100644 index 95fde720f0c3..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-Regular.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/PlusJakartaSans-SemiBold.ttf b/apps/ios/Sources/Fonts/PlusJakartaSans-SemiBold.ttf deleted file mode 100644 index 2d4615537c74..000000000000 Binary files a/apps/ios/Sources/Fonts/PlusJakartaSans-SemiBold.ttf and /dev/null differ diff --git a/apps/ios/Sources/Fonts/RedHatDisplay[wght].ttf b/apps/ios/Sources/Fonts/RedHatDisplay[wght].ttf new file mode 100644 index 000000000000..b160aefd0198 Binary files /dev/null and b/apps/ios/Sources/Fonts/RedHatDisplay[wght].ttf differ diff --git a/apps/ios/Sources/Info.plist b/apps/ios/Sources/Info.plist index 676093a7a020..78c14bcdadd9 100644 --- a/apps/ios/Sources/Info.plist +++ b/apps/ios/Sources/Info.plist @@ -90,17 +90,9 @@ $(OPENCLAW_PUSH_RELAY_BASE_URL) UIAppFonts - PlusJakartaSans-Light.ttf - PlusJakartaSans-Regular.ttf - PlusJakartaSans-Medium.ttf - PlusJakartaSans-SemiBold.ttf - PlusJakartaSans-Bold.ttf - PlusJakartaSans-ExtraBold.ttf - DMSans-Light.ttf - DMSans-Regular.ttf - DMSans-Italic.ttf - DMSans-Medium.ttf - DMSans-SemiBold.ttf + RedHatDisplay[wght].ttf + Inter[opsz,wght].ttf + Inter-Italic[opsz,wght].ttf JetBrainsMono-Regular.ttf JetBrainsMono-Medium.ttf JetBrainsMono-SemiBold.ttf diff --git a/apps/ios/Sources/RootTabs.swift b/apps/ios/Sources/RootTabs.swift index ff3cd544531e..1f539b24e247 100644 --- a/apps/ios/Sources/RootTabs.swift +++ b/apps/ios/Sources/RootTabs.swift @@ -306,7 +306,7 @@ struct RootTabs: View { VStack(alignment: .leading, spacing: 2) { Text("OpenClaw") - .font(.headline.weight(.semibold)) + .font(OpenClawType.headline) .foregroundStyle(.primary) .lineLimit(1) @@ -317,7 +317,7 @@ struct RootTabs: View { Text(self.sidebarGatewayStatusTitle) .lineLimit(1) } - .font(.caption.weight(.medium)) + .font(OpenClawType.captionMedium) .foregroundStyle(.secondary) } @@ -608,7 +608,7 @@ struct RootTabs: View { self.hideSidebar() } label: { Image(systemName: self.isSidebarDrawerLayout ? "xmark" : "sidebar.left") - .font(.system(size: 15, weight: .semibold)) + .font(OpenClawType.subheadSemiBold) } .frame(width: 44, height: 44) .contentShape(Rectangle()) diff --git a/apps/ios/Tests/OpenClawTypographyTests.swift b/apps/ios/Tests/OpenClawTypographyTests.swift index bd0f8bc98a4c..7668964ab51b 100644 --- a/apps/ios/Tests/OpenClawTypographyTests.swift +++ b/apps/ios/Tests/OpenClawTypographyTests.swift @@ -1,3 +1,4 @@ +import CoreText import Testing import UIKit @testable import OpenClaw @@ -10,8 +11,8 @@ struct OpenClawTypographyTests { } @Test func `dynamic type scales display fonts`() { - guard let base = UIFont(name: "PlusJakartaSans-ExtraBold", size: 34) else { - Issue.record("PlusJakartaSans-ExtraBold should be bundled") + guard let base = UIFont(name: "RedHatDisplay-Regular", size: 34) else { + Issue.record("RedHatDisplay-Regular should be bundled") return } @@ -24,4 +25,23 @@ struct OpenClawTypographyTests { #expect(largeSize > defaultSize) } + + @Test func `display variable weight axis instantiates heavy weights`() { + guard UIFont(name: "RedHatDisplay-Regular", size: 15) != nil else { + Issue.record("RedHatDisplay-Regular should be bundled") + return + } + + let weightAxis = NSNumber(value: 2_003_265_652) // "wght" + let descriptor = UIFontDescriptor(fontAttributes: [ + .name: "RedHatDisplay-Regular", + kCTFontVariationAttribute as UIFontDescriptor.AttributeName: [weightAxis: 900], + ]) + let font = UIFont(descriptor: descriptor, size: 15) + let variations = font.fontDescriptor.object( + forKey: kCTFontVariationAttribute as UIFontDescriptor.AttributeName) as? [NSNumber: Any] + let weightValue = variations?[weightAxis] as? NSNumber + + #expect(weightValue?.doubleValue == 900) + } } diff --git a/apps/ios/project.yml b/apps/ios/project.yml index a6796d08da39..7ca95e381f57 100644 --- a/apps/ios/project.yml +++ b/apps/ios/project.yml @@ -179,17 +179,9 @@ targets: NSSupportsLiveActivities: true ITSAppUsesNonExemptEncryption: false UIAppFonts: - - PlusJakartaSans-Light.ttf - - PlusJakartaSans-Regular.ttf - - PlusJakartaSans-Medium.ttf - - PlusJakartaSans-SemiBold.ttf - - PlusJakartaSans-Bold.ttf - - PlusJakartaSans-ExtraBold.ttf - - DMSans-Light.ttf - - DMSans-Regular.ttf - - DMSans-Italic.ttf - - DMSans-Medium.ttf - - DMSans-SemiBold.ttf + - RedHatDisplay[wght].ttf + - Inter[opsz,wght].ttf + - Inter-Italic[opsz,wght].ttf - JetBrainsMono-Regular.ttf - JetBrainsMono-Medium.ttf - JetBrainsMono-SemiBold.ttf diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift index 42feb0464a56..b202ae1f8f5e 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift @@ -217,7 +217,7 @@ struct OpenClawChatComposer: View { { ForEach(self.viewModel.sessionChoices, id: \.key) { session in Text(session.displayName ?? session.key) - .font(.system(.caption, design: .monospaced)) + .font(OpenClawChatTypography.mono(size: 12, relativeTo: .caption)) .tag(session.key) } } @@ -287,7 +287,7 @@ struct OpenClawChatComposer: View { private var compactAttachmentLabel: some View { Image(systemName: "paperclip") - .font(.system(size: 15, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 15, weight: .semibold, relativeTo: .subheadline)) .foregroundStyle(.secondary) .frame(width: self.cleanControlHeight, height: self.cleanControlHeight) .contentShape(Rectangle()) @@ -407,9 +407,9 @@ struct OpenClawChatComposer: View { } label: { HStack(spacing: 6) { Image(systemName: talkControl.isEnabled ? "stop.fill" : "waveform") - .font(.caption.weight(.semibold)) + .font(OpenClawChatTypography.captionSemiBold) Text(talkControl.isEnabled ? "Stop" : "Talk") - .font(.caption.weight(.semibold)) + .font(OpenClawChatTypography.captionSemiBold) .lineLimit(1) } .foregroundStyle(talkControl.isEnabled ? .white : .primary) @@ -437,7 +437,7 @@ struct OpenClawChatComposer: View { talkControl.toggle(self.viewModel.sessionKey) } label: { Image(systemName: talkControl.isEnabled ? "stop.fill" : "waveform") - .font(.system(size: 14, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 14, weight: .semibold, relativeTo: .subheadline)) .foregroundStyle(talkControl.isEnabled ? .white : .secondary) .frame(width: self.cleanIconControlSize, height: self.cleanIconControlSize) .background { @@ -500,7 +500,7 @@ struct OpenClawChatComposer: View { .fill(self.connectionOK ? .green : .orange) .frame(width: 7, height: 7) Text(self.connectionStatusText) - .font(.caption2) + .font(OpenClawChatTypography.caption2) .foregroundStyle(.secondary) } .padding(.horizontal, self.composerChrome == .clean ? 0 : 8) @@ -523,7 +523,7 @@ struct OpenClawChatComposer: View { ZStack(alignment: self.editorOverlayAlignment) { if self.viewModel.input.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { Text(self.placeholderText) - .font(.body) + .font(OpenClawChatTypography.body) .foregroundStyle(.tertiary) .padding(.horizontal, self.cleanFieldTextInset) .padding(.vertical, self.composerChrome == .clean ? 0 : 4) @@ -549,7 +549,7 @@ struct OpenClawChatComposer: View { "", text: self.$viewModel.input, axis: .vertical) - .font(.body) + .font(OpenClawChatTypography.body) .textFieldStyle(.plain) .lineLimit(1...4) .fixedSize(horizontal: false, vertical: true) @@ -576,7 +576,7 @@ struct OpenClawChatComposer: View { ProgressView().controlSize(.mini) } else { Image(systemName: "stop.fill") - .font(.system(size: 13, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 13, weight: .semibold, relativeTo: .caption)) } } .buttonStyle(.plain) @@ -597,7 +597,7 @@ struct OpenClawChatComposer: View { ProgressView().controlSize(.mini) } else { Image(systemName: "arrow.up") - .font(.system(size: 13, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 13, weight: .semibold, relativeTo: .caption)) } } .buttonStyle(.plain) diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift index d197f87fef8a..74e0e93fbc1f 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMarkdownRenderer.swift @@ -233,7 +233,7 @@ private struct InlineImageList: View { .strokeBorder(Color.white.opacity(0.12), lineWidth: 1)) } else { Text(item.label.isEmpty ? "Image" : item.label) - .font(.footnote) + .font(OpenClawChatTypography.footnote) .foregroundStyle(.secondary) } } diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMessageViews.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMessageViews.swift index c4e561159c8a..1483509fe829 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMessageViews.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatMessageViews.swift @@ -15,7 +15,7 @@ struct ChatAgentAvatar: View { var body: some View { Text(self.displayText) - .font(.system(size: self.fontSize, weight: .bold, design: .rounded)) + .font(OpenClawChatTypography.avatar(size: self.fontSize)) .foregroundStyle(.white) .minimumScaleFactor(0.6) .lineLimit(1) @@ -468,7 +468,7 @@ private struct AttachmentRow: View { HStack(spacing: 8) { Image(systemName: "paperclip") Text(self.att.fileName ?? "Attachment") - .font(.footnote) + .font(OpenClawChatTypography.footnote) .lineLimit(1) .foregroundStyle(self.isUser ? OpenClawChatTheme.userText : OpenClawChatTheme.assistantText) Spacer() @@ -487,13 +487,13 @@ private struct ToolCallCard: View { VStack(alignment: .leading, spacing: 6) { HStack(spacing: 6) { Text(self.toolName) - .font(.footnote.weight(.semibold)) + .font(OpenClawChatTypography.footnoteSemiBold) Spacer(minLength: 0) } if let summary = self.summary, !summary.isEmpty { Text(summary) - .font(.footnote.monospaced()) + .font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote)) .foregroundStyle(.secondary) .lineLimit(2) } @@ -532,12 +532,12 @@ private struct ToolResultCard: View { VStack(alignment: .leading, spacing: 8) { HStack(spacing: 6) { Text(self.title) - .font(.footnote.weight(.semibold)) + .font(OpenClawChatTypography.footnoteSemiBold) Spacer(minLength: 0) } Text(self.displayText) - .font(.footnote.monospaced()) + .font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote)) .foregroundStyle(self.isUser ? OpenClawChatTheme.userText : OpenClawChatTheme.assistantText) .lineLimit(self.expanded ? nil : Self.previewLineLimit) @@ -546,7 +546,7 @@ private struct ToolResultCard: View { self.expanded.toggle() } .buttonStyle(.plain) - .font(.caption) + .font(OpenClawChatTypography.caption) .foregroundStyle(.secondary) } } @@ -602,7 +602,7 @@ struct ChatTypingIndicatorBubble: View { HStack(spacing: 9) { TypingDots() Text("Writing") - .font(.caption.weight(.semibold)) + .font(OpenClawChatTypography.captionSemiBold) .foregroundStyle(.secondary) } .padding(.vertical, self.isClean ? 5 : (self.style == .standard ? 10 : 9)) @@ -694,7 +694,7 @@ struct ChatPendingToolsBubble: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Label("Running tools…", systemImage: "hammer") - .font(.caption) + .font(OpenClawChatTypography.caption) .foregroundStyle(.secondary) ForEach(self.toolCalls) { call in @@ -702,14 +702,14 @@ struct ChatPendingToolsBubble: View { VStack(alignment: .leading, spacing: 4) { HStack(alignment: .firstTextBaseline, spacing: 8) { Text("\(display.emoji) \(display.label)") - .font(.footnote.monospaced()) + .font(OpenClawChatTypography.mono(size: 13, relativeTo: .footnote)) .lineLimit(1) Spacer(minLength: 0) ProgressView().controlSize(.mini) } if let detail = display.detailLine, !detail.isEmpty { Text(detail) - .font(.caption.monospaced()) + .font(OpenClawChatTypography.mono(size: 12, relativeTo: .caption)) .foregroundStyle(.secondary) .lineLimit(2) } diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSheets.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSheets.swift index 678000d2cea4..93551b7a845f 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSheets.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatSheets.swift @@ -15,13 +15,13 @@ struct ChatSessionsSheet: View { } label: { VStack(alignment: .leading, spacing: 4) { Text(session.displayName ?? session.key) - .font(.system(.body, design: .monospaced)) + .font(OpenClawChatTypography.mono(size: 17, relativeTo: .body)) .lineLimit(1) if let updatedAt = session.updatedAt, updatedAt > 0 { Text(Date(timeIntervalSince1970: updatedAt / 1000).formatted( date: .abbreviated, time: .shortened)) - .font(.caption) + .font(OpenClawChatTypography.caption) .foregroundStyle(.secondary) } } diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatTypography.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatTypography.swift new file mode 100644 index 000000000000..715b4f4e2c4d --- /dev/null +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatTypography.swift @@ -0,0 +1,78 @@ +import Foundation +import SwiftUI + +enum OpenClawChatTypography { + static var title3: Font { + display(size: 22, weight: .bold, relativeTo: .title2) + } + + static var title3SemiBold: Font { + display(size: 22, weight: .semibold, relativeTo: .title2) + } + + static var headline: Font { + display(size: 17, weight: .semibold, relativeTo: .headline) + } + + static var callout: Font { + body(size: 16, weight: .regular, relativeTo: .callout) + } + + static var body: Font { + body(size: 17, weight: .regular, relativeTo: .body) + } + + static var footnote: Font { + body(size: 13, weight: .regular, relativeTo: .footnote) + } + + static var footnoteSemiBold: Font { + body(size: 13, weight: .semibold, relativeTo: .footnote) + } + + static var caption: Font { + body(size: 12, weight: .regular, relativeTo: .caption) + } + + static var captionSemiBold: Font { + body(size: 12, weight: .semibold, relativeTo: .caption) + } + + static var caption2: Font { + body(size: 11, weight: .regular, relativeTo: .caption2) + } + + static func avatar(size: CGFloat) -> Font { + body(size: size, weight: .bold, relativeTo: .caption) + } + + static func body(size: CGFloat, weight: Font.Weight, relativeTo textStyle: Font.TextStyle) -> Font { + #if os(iOS) + Font.custom(Self.bodyPostScriptName, size: size, relativeTo: textStyle).weight(weight) + #else + Font.system(size: size, weight: weight) + #endif + } + + static func display(size: CGFloat, weight: Font.Weight, relativeTo textStyle: Font.TextStyle) -> Font { + #if os(iOS) + Font.custom(Self.displayPostScriptName, size: size, relativeTo: textStyle).weight(weight) + #else + Font.system(size: size, weight: weight) + #endif + } + + static func mono(size: CGFloat, weight: Font.Weight = .regular, relativeTo textStyle: Font.TextStyle) -> Font { + #if os(iOS) + let name = weight == .semibold ? Self.monoSemiBoldPostScriptName : Self.monoPostScriptName + return Font.custom(name, size: size, relativeTo: textStyle) + #else + return Font.system(size: size, weight: weight, design: .monospaced) + #endif + } + + private static let displayPostScriptName = "RedHatDisplay-Regular" + private static let bodyPostScriptName = "Inter-Regular" + private static let monoPostScriptName = "JetBrainsMono-Regular" + private static let monoSemiBoldPostScriptName = "JetBrainsMono-SemiBold" +} diff --git a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift index e742a777d2b5..b4deaa8a900c 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatView.swift @@ -829,12 +829,12 @@ private struct ChatAssistantIntroCard: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Image(systemName: "sparkles") - .font(.title3.weight(.medium)) + .font(OpenClawChatTypography.title3) .foregroundStyle(OpenClawChatTheme.accent) .accessibilityHidden(true) Text(self.text) - .font(.title3.weight(.semibold)) + .font(OpenClawChatTypography.title3SemiBold) .foregroundStyle(OpenClawChatTheme.assistantText) .multilineTextAlignment(.leading) } @@ -851,7 +851,7 @@ private struct ChatLoadingBubble: View { ProgressView() .controlSize(.small) Text("Loading chat") - .font(.caption.weight(.semibold)) + .font(OpenClawChatTypography.captionSemiBold) .foregroundStyle(.secondary) } .padding(.vertical, 9) @@ -874,17 +874,17 @@ private struct ChatNoticeCard: View { var body: some View { HStack(alignment: .center, spacing: 14) { Image(systemName: self.systemImage) - .font(.system(size: 19, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 19, weight: .semibold, relativeTo: .headline)) .foregroundStyle(self.tint) .frame(width: 42, height: 42) .background(self.tint.opacity(0.14), in: Circle()) VStack(alignment: .leading, spacing: 4) { Text(self.title) - .font(.headline) + .font(OpenClawChatTypography.headline) Text(self.message) - .font(.callout) + .font(OpenClawChatTypography.callout) .foregroundStyle(.secondary) .multilineTextAlignment(.leading) .lineLimit(3) @@ -920,16 +920,16 @@ private struct ChatNoticeBanner: View { var body: some View { HStack(alignment: .top, spacing: 10) { Image(systemName: self.systemImage) - .font(.system(size: 15, weight: .semibold)) + .font(OpenClawChatTypography.display(size: 15, weight: .semibold, relativeTo: .subheadline)) .foregroundStyle(self.tint) .padding(.top, 1) VStack(alignment: .leading, spacing: 3) { Text(self.title) - .font(.caption.weight(.semibold)) + .font(OpenClawChatTypography.captionSemiBold) Text(self.message) - .font(.caption) + .font(OpenClawChatTypography.caption) .foregroundStyle(.secondary) .lineLimit(2) }