@pheem49/mint 1.17.0 → 1.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +2 -2
  3. package/README.md +8 -2
  4. package/Release_Note.md +222 -71
  5. package/crates/mint-cli/src/agent/diff_render.rs +66 -44
  6. package/crates/mint-cli/src/agent/live_status.rs +21 -4
  7. package/crates/mint-cli/src/agent/markdown_render.rs +342 -5
  8. package/crates/mint-cli/src/agent.rs +25 -10
  9. package/crates/mint-cli/src/commands/agent.rs +4 -92
  10. package/crates/mint-cli/src/commands/browser.rs +2 -15
  11. package/crates/mint-cli/src/commands/integrations.rs +3 -3
  12. package/crates/mint-cli/src/commands/mod.rs +0 -3
  13. package/crates/mint-cli/src/commands/tailnet_web.rs +1 -0
  14. package/crates/mint-cli/src/interactive/chat_tui/dialog.rs +572 -0
  15. package/crates/mint-cli/src/interactive/chat_tui/mod.rs +1427 -0
  16. package/crates/mint-cli/src/interactive/chat_tui/render.rs +1524 -0
  17. package/crates/mint-cli/src/interactive/chat_tui/selection.rs +401 -0
  18. package/crates/mint-cli/src/interactive/chat_tui/state.rs +1833 -0
  19. package/crates/mint-cli/src/interactive/chat_tui/tests.rs +3414 -0
  20. package/crates/mint-cli/src/interactive/chat_tui/thought.rs +202 -0
  21. package/crates/mint-cli/src/interactive/chat_tui/transcript.rs +728 -0
  22. package/crates/mint-cli/src/interactive/chat_tui/watermark.rs +148 -0
  23. package/crates/mint-cli/src/interactive/command_ui.rs +53 -5
  24. package/crates/mint-cli/src/interactive/composer.rs +186 -0
  25. package/crates/mint-cli/src/interactive/format.rs +78 -3
  26. package/crates/mint-cli/src/interactive/input_box.rs +61 -1
  27. package/crates/mint-cli/src/interactive/resume_picker.rs +367 -25
  28. package/crates/mint-cli/src/interactive/slash_commands.rs +648 -371
  29. package/crates/mint-cli/src/interactive/tool_activity.rs +93 -20
  30. package/crates/mint-cli/src/interactive.rs +37 -0
  31. package/crates/mint-cli/src/main.rs +6 -3
  32. package/crates/mint-cli/src/markdown.rs +2 -37
  33. package/crates/mint-cli/src/onboard.rs +700 -282
  34. package/crates/mint-cli/src/setup.rs +1342 -624
  35. package/crates/mint-cli/src/terminal_theme.rs +159 -35
  36. package/crates/mint-cli/tests/library_intake.rs +1 -0
  37. package/crates/mint-core/src/agent/chat/anthropic.rs +318 -0
  38. package/crates/mint-core/src/agent/chat/custom.rs +149 -0
  39. package/crates/mint-core/src/agent/chat/gemini.rs +518 -0
  40. package/crates/mint-core/src/agent/chat/mod.rs +857 -0
  41. package/crates/mint-core/src/agent/chat/ollama.rs +321 -0
  42. package/crates/mint-core/src/agent/chat/openai.rs +582 -0
  43. package/crates/mint-core/src/agent/chat/streaming.rs +469 -0
  44. package/crates/mint-core/src/agent/chat/tests.rs +1479 -0
  45. package/crates/mint-core/src/agent/memory.rs +56 -2
  46. package/crates/mint-core/src/agent/tasks.rs +2 -2
  47. package/crates/mint-core/src/api_server/routes/chat.rs +24 -2
  48. package/crates/mint-core/src/api_server/routes/gemini_live.rs +14 -1
  49. package/crates/mint-core/src/api_server/routes/html_preview.rs +1 -8
  50. package/crates/mint-core/src/api_server/routes/sessions.rs +21 -0
  51. package/crates/mint-core/src/avatar_bridge.rs +0 -22
  52. package/crates/mint-core/src/browser/cdp.rs +10 -4
  53. package/crates/mint-core/src/browser/headless.rs +582 -0
  54. package/crates/mint-core/src/browser/input.rs +7 -62
  55. package/crates/mint-core/src/browser/interact.rs +0 -14
  56. package/crates/mint-core/src/browser/mod.rs +8 -29
  57. package/crates/mint-core/src/browser/navigate.rs +2 -36
  58. package/crates/mint-core/src/browser/runtime.rs +2 -4
  59. package/crates/mint-core/src/browser/session.rs +50 -466
  60. package/crates/mint-core/src/browser/workflow.rs +142 -32
  61. package/crates/mint-core/src/cron/store.rs +2 -2
  62. package/crates/mint-core/src/integrations/mcp.rs +6 -8
  63. package/crates/mint-core/src/lib.rs +0 -5
  64. package/crates/mint-core/src/media/gemini_live.rs +636 -86
  65. package/crates/mint-core/src/media/library/migration.rs +26 -0
  66. package/crates/mint-core/src/media/library/tests.rs +1 -1
  67. package/crates/mint-core/src/media/library/thumbnails.rs +8 -2
  68. package/crates/mint-core/src/media/pictures.rs +1 -1
  69. package/crates/mint-core/src/media/tool_images.rs +92 -1
  70. package/crates/mint-core/src/mods/manager.rs +101 -35
  71. package/crates/mint-core/src/orchestration/context_render.rs +441 -24
  72. package/crates/mint-core/src/orchestration/decision_parsing.rs +531 -16
  73. package/crates/mint-core/src/orchestration/full_skill_result_tests.rs +12 -0
  74. package/crates/mint-core/src/orchestration/loop_guard.rs +145 -0
  75. package/crates/mint-core/src/orchestration/mcp_outcome_tests.rs +864 -0
  76. package/crates/mint-core/src/orchestration/memory_skill.rs +88 -28
  77. package/crates/mint-core/src/orchestration/mod.rs +320 -4249
  78. package/crates/mint-core/src/orchestration/reliability.rs +67 -27
  79. package/crates/mint-core/src/orchestration/review_regressions.rs +5 -0
  80. package/crates/mint-core/src/orchestration/tests.rs +1665 -0
  81. package/crates/mint-core/src/orchestration/tools/dispatch.rs +967 -0
  82. package/crates/mint-core/src/orchestration/tools/files.rs +1 -0
  83. package/crates/mint-core/src/orchestration/tools/mod.rs +5 -1
  84. package/crates/mint-core/src/orchestration/tools/planning.rs +102 -42
  85. package/crates/mint-core/src/orchestration/tools/shell.rs +129 -58
  86. package/crates/mint-core/src/orchestration/truncation.rs +170 -13
  87. package/crates/mint-core/src/orchestration/types.rs +648 -0
  88. package/crates/mint-core/src/orchestration/verification.rs +0 -3
  89. package/crates/mint-core/src/orchestration/workspace_helpers.rs +24 -6
  90. package/crates/mint-core/src/prompts/agent.rs +52 -76
  91. package/crates/mint-core/src/prompts/tool_catalog.rs +8 -151
  92. package/crates/mint-core/src/search/files.rs +20 -0
  93. package/crates/mint-core/src/slash/mod.rs +228 -265
  94. package/crates/mint-core/src/slash/models.rs +6 -1
  95. package/crates/mint-core/src/system/bg_shell.rs +1 -0
  96. package/crates/mint-core/src/system/config.rs +176 -34
  97. package/crates/mint-mod-runtime/src/lib.rs +37 -23
  98. package/out/web/assets/ArtifactPreviewPanel-Cx6BVOOF.js +2 -0
  99. package/out/web/assets/CalculationCard-D2E74u5q.js +1 -0
  100. package/out/web/assets/CliSessionsView-ZbdtSjvK.css +1 -0
  101. package/out/web/assets/{CliSessionsView-MI8kXMAb.js → CliSessionsView-z5i6qGyb.js} +1 -1
  102. package/out/web/assets/CodeReviewPage-BK2bOScd.css +1 -0
  103. package/out/web/assets/CodeReviewPage-Dp6zHuHu.js +2 -0
  104. package/out/web/assets/{CommandPalette-BD8SlTE5.js → CommandPalette-DaKlzbKs.js} +1 -1
  105. package/out/web/assets/ImageGenCard-BMCJHP9O.js +1 -0
  106. package/out/web/assets/ImageSearchCard-Cld_SOUg.js +1 -0
  107. package/out/web/assets/{ImageStudioPanel-BP7N4rLr.js → ImageStudioPanel-B5N7TjeB.js} +1 -1
  108. package/out/web/assets/LibraryPage-CMB89rJ0.css +1 -0
  109. package/out/web/assets/LibraryPage-DbZEi4W_.js +2 -0
  110. package/out/web/assets/LinkedFoldersView-9B9DIPoZ.js +1 -0
  111. package/out/web/assets/{McpRegistryPicker-CoS83vtc.js → McpRegistryPicker-DnXPlzI7.js} +1 -1
  112. package/out/web/assets/McpServersView-CiNBNUl4.js +1 -0
  113. package/out/web/assets/MermaidCard-BBDNEXbY.js +2 -0
  114. package/out/web/assets/MintDashboard-C_UYPoNV.js +119 -0
  115. package/out/web/assets/MintDashboard-DTd8C_c8.css +1 -0
  116. package/out/web/assets/PlanCard-C2x5pajq.js +1 -0
  117. package/out/web/assets/{PluginsView-jBYmcKh4.js → PluginsView-DoTg9MVa.js} +1 -1
  118. package/out/web/assets/RewindModal-B5oowrR0.js +1 -0
  119. package/out/web/assets/{ScheduledTasksView-5uw9-V9H.js → ScheduledTasksView-BD5DVPkZ.js} +1 -1
  120. package/out/web/assets/{SelectField-DXBQtrUK.js → SelectField-Cy6CghrU.js} +1 -1
  121. package/out/web/assets/SettingsWindow-BYl_oCFh.js +4 -0
  122. package/out/web/assets/SettingsWindow-Ckcgnjt8.css +1 -0
  123. package/out/web/assets/{SkillsView-C-gp9J7m.js → SkillsView-CIeWwq5z.js} +1 -1
  124. package/out/web/assets/StockCard-DxUciKyp.js +1 -0
  125. package/out/web/assets/ToolSurfacePage-qBgs_VHq.css +1 -0
  126. package/out/web/assets/ToolSurfacePage-saVbVrkE.js +38 -0
  127. package/out/web/assets/{UiFeatureCard-6BwxTOCe.js → UiFeatureCard-CdY0hGWy.js} +1 -1
  128. package/out/web/assets/{UiGridCard-DvZcLNxb.js → UiGridCard-BBK3I5hC.js} +1 -1
  129. package/out/web/assets/UiMockupWidget-CpR9EDuc.js +1 -0
  130. package/out/web/assets/{VeoStudioPanel-DOj7_WR3.css → VeoStudioPanel-9kU9YLEN.css} +1 -1
  131. package/out/web/assets/{VeoStudioPanel-DfsOJKv0.js → VeoStudioPanel-Co6-wEWM.js} +2 -2
  132. package/out/web/assets/WeatherCard-Buzi7Vfu.js +1 -0
  133. package/out/web/assets/WorkspacePdfPreview-Bi8Zzyir.js +2 -0
  134. package/out/web/assets/{abnfDiagram-VCTEODGH-BOo3Ckng.js → abnfDiagram-VCTEODGH-CMfR95N2.js} +1 -1
  135. package/out/web/assets/{architecture-7GRP2DOG-ZOwheW3n.js → architecture-7GRP2DOG-DhAM4HyO.js} +1 -1
  136. package/out/web/assets/{architectureDiagram-5GKGNRK7-DC2FDzVd.js → architectureDiagram-5GKGNRK7-Yk3TUJ_F.js} +1 -1
  137. package/out/web/assets/{blockDiagram-I7D4REHJ-BrKFWdAH.js → blockDiagram-I7D4REHJ-X9fBgJik.js} +1 -1
  138. package/out/web/assets/{c4Diagram-7LVT6UL2-BHTHK1Yd.js → c4Diagram-7LVT6UL2-Bk9x7N-8.js} +1 -1
  139. package/out/web/assets/{chunk-4HAMMTFA-BYFkZwNj.js → chunk-4HAMMTFA-Dx5fk1_0.js} +1 -1
  140. package/out/web/assets/{chunk-75Z2AOVW-BSKfSKSA.js → chunk-75Z2AOVW-BszTQoM4.js} +1 -1
  141. package/out/web/assets/{chunk-DU6HZSFF-vIhsiW6n.js → chunk-DU6HZSFF-DK-As3rk.js} +1 -1
  142. package/out/web/assets/{chunk-F27PBJKO-BlYpW9xq.js → chunk-F27PBJKO-LmncZZIS.js} +1 -1
  143. package/out/web/assets/{chunk-GMAD6QVW-BIBz_Ug5.js → chunk-GMAD6QVW-DSQ_sDRJ.js} +1 -1
  144. package/out/web/assets/{chunk-GVQU2GXP-NPSHxeqC.js → chunk-GVQU2GXP-Cno3ulje.js} +1 -1
  145. package/out/web/assets/{chunk-IMKFNOWR-YlJK8_Fp.js → chunk-IMKFNOWR-bIzfY9Jc.js} +1 -1
  146. package/out/web/assets/{chunk-L3NEJ4N5-DMYi4-af.js → chunk-L3NEJ4N5-Dl6eOa0D.js} +1 -1
  147. package/out/web/assets/{chunk-OSK3NFVY-Begd3-jA.js → chunk-OSK3NFVY-BaUGJ-cM.js} +1 -1
  148. package/out/web/assets/{chunk-P2QGCYS3-D1I4xFHN.js → chunk-P2QGCYS3-_066ndhH.js} +1 -1
  149. package/out/web/assets/{chunk-POPQ4Y6H-CafZZzNE.js → chunk-POPQ4Y6H-YT1AKhTv.js} +1 -1
  150. package/out/web/assets/{chunk-PWAF6VOD-Dwh6_Lnt.js → chunk-PWAF6VOD-CrEgDsxh.js} +1 -1
  151. package/out/web/assets/{chunk-SHT3W25Y-B0XrCSEb.js → chunk-SHT3W25Y-_KwaRLOl.js} +1 -1
  152. package/out/web/assets/{chunk-SVP7TREG-CxLsddxC.js → chunk-SVP7TREG-BQ1qb32L.js} +1 -1
  153. package/out/web/assets/{chunk-TICWLB2K-y156QInb.js → chunk-TICWLB2K-BDyehCtg.js} +1 -1
  154. package/out/web/assets/classDiagram-ZZMXUADV-BoSpzRCJ.js +1 -0
  155. package/out/web/assets/classDiagram-v2-VYDZK3BY-BoSpzRCJ.js +1 -0
  156. package/out/web/assets/{config-DqXJXeZM.css → config-2suVFPZg.css} +1 -1
  157. package/out/web/assets/config-CkRCdkxn.js +2 -0
  158. package/out/web/assets/{cynefin-OW5HDTMX-SMVvrLf-.js → cynefin-OW5HDTMX-Dl_31aaN.js} +1 -1
  159. package/out/web/assets/{cynefinDiagram-5FMLGOSQ-rZznN24T.js → cynefinDiagram-5FMLGOSQ-Bpir8jha.js} +1 -1
  160. package/out/web/assets/{dagre-GXQ25YYZ-BOToydFu.js → dagre-GXQ25YYZ-D9xu3nsq.js} +1 -1
  161. package/out/web/assets/{diagram-S7CK7UJ4-TMVaGQEJ.js → diagram-S7CK7UJ4-DeveoEeB.js} +1 -1
  162. package/out/web/assets/{diagram-UQ7AKVKN-C2JK-GP2.js → diagram-UQ7AKVKN-p4xWRESV.js} +1 -1
  163. package/out/web/assets/{diagram-VSXAHHWV-Cw8VSi2K.js → diagram-VSXAHHWV-C7qz9NDF.js} +1 -1
  164. package/out/web/assets/{diagram-VX7I27RA-DXp4fKk9.js → diagram-VX7I27RA-DRiCa3Vd.js} +1 -1
  165. package/out/web/assets/{diagram-Z3DM3KII-CzSicYiW.js → diagram-Z3DM3KII-Bt7zduQX.js} +1 -1
  166. package/out/web/assets/{ebnfDiagram-PWID7BFC-Dqj-zqPu.js → ebnfDiagram-PWID7BFC-DV9WpwdR.js} +1 -1
  167. package/out/web/assets/{erDiagram-RLTQ6QDP-DZwFZAuD.js → erDiagram-RLTQ6QDP-BJ_nwG2m.js} +1 -1
  168. package/out/web/assets/{eventmodeling-NTZA5JFV-C0O-Lvgp.js → eventmodeling-NTZA5JFV-BTBMrfVa.js} +1 -1
  169. package/out/web/assets/flowDiagram-HODETNUW-Cdlq3cJL.js +1 -0
  170. package/out/web/assets/{ganttDiagram-EL5Y4UJY-Da4ULXTl.js → ganttDiagram-EL5Y4UJY-DQLdsXrC.js} +1 -1
  171. package/out/web/assets/{gitGraph-4MIJSDKK-B9wNFZIP.js → gitGraph-4MIJSDKK-CL1OEOQ_.js} +1 -1
  172. package/out/web/assets/{gitGraphDiagram-WWUBYQGX-RjrhB0MP.js → gitGraphDiagram-WWUBYQGX-8cSi42H7.js} +1 -1
  173. package/out/web/assets/index-BocxnrDG.js +168 -0
  174. package/out/web/assets/index-yeh_r55a.css +1 -0
  175. package/out/web/assets/{info-A6RAGUB7-B3dfCpeY.js → info-A6RAGUB7-C1zsX5sc.js} +1 -1
  176. package/out/web/assets/{infoDiagram-27XIBGKW-QVWkLVx7.js → infoDiagram-27XIBGKW-DsRYtIyI.js} +1 -1
  177. package/out/web/assets/{ishikawaDiagram-5VMMS53U-CLFNIRI9.js → ishikawaDiagram-5VMMS53U-B2crwIXj.js} +1 -1
  178. package/out/web/assets/{journeyDiagram-3NMN7TZE-fgOQadYF.js → journeyDiagram-3NMN7TZE-BTH-GyVR.js} +1 -1
  179. package/out/web/assets/{kanban-definition-UXKFOSKX-G3Q_9g9O.js → kanban-definition-UXKFOSKX-BeRfCnuA.js} +1 -1
  180. package/out/web/assets/{mermaid-parser.core-v3Jm7nco.js → mermaid-parser.core-BJwgCBBc.js} +3 -3
  181. package/out/web/assets/{mermaid.core-DTEjLUA1.js → mermaid.core-CsDDodN8.js} +4 -4
  182. package/out/web/assets/{mindmap-definition-YA3MSWOX-C_D42_fF.js → mindmap-definition-YA3MSWOX-B42efOQ1.js} +1 -1
  183. package/out/web/assets/{modelManager-BPLycQrN.js → modelManager-B3ZFMh8v.js} +2 -2
  184. package/out/web/assets/{oauthManager-BUK7ic7q.js → oauthManager-Ch5Du2fF.js} +1 -1
  185. package/out/web/assets/{packet-AYTQ26CC-B-R-SHlF.js → packet-AYTQ26CC-B6xUyE6r.js} +1 -1
  186. package/out/web/assets/{pegDiagram-XKGWAZYB-Da2q_giz.js → pegDiagram-XKGWAZYB-1LabVycL.js} +1 -1
  187. package/out/web/assets/{pie-WAS4IAKB-BC8F0N7-.js → pie-WAS4IAKB-DJwo-nf_.js} +1 -1
  188. package/out/web/assets/{pieDiagram-E7YTZNPT-Iy9REJNC.js → pieDiagram-E7YTZNPT-icmAqzy4.js} +1 -1
  189. package/out/web/assets/{quadrantDiagram-AXDQQJYC-ztsiYw7h.js → quadrantDiagram-AXDQQJYC-CZ-wEx41.js} +1 -1
  190. package/out/web/assets/{radar-RG4KPBEZ-B_1P8Z_z.js → radar-RG4KPBEZ-D7d-Xriw.js} +1 -1
  191. package/out/web/assets/{railroad-74A4TZTK-DuTqczSo.js → railroad-74A4TZTK-viuhdYbs.js} +1 -1
  192. package/out/web/assets/{railroad-abnf-HS5TGJTU-Df29WLmp.js → railroad-abnf-HS5TGJTU-DNXiCNea.js} +1 -1
  193. package/out/web/assets/{railroad-ebnf-LZEXJU2U-ePFMo-qp.js → railroad-ebnf-LZEXJU2U-nkSu1-0C.js} +1 -1
  194. package/out/web/assets/{railroad-peg-WCYAUIDC-DYXLW4_E.js → railroad-peg-WCYAUIDC-BhbMeQzz.js} +1 -1
  195. package/out/web/assets/{railroadDiagram-O6MQD6OU-LStnZf2S.js → railroadDiagram-O6MQD6OU-CLpG1jUX.js} +1 -1
  196. package/out/web/assets/{requirementDiagram-BXWQKSXE-DmzonMH-.js → requirementDiagram-BXWQKSXE-kRDrvWrc.js} +1 -1
  197. package/out/web/assets/{sankeyDiagram-P5KCCOFB-B8vjmUU1.js → sankeyDiagram-P5KCCOFB-DxAwmq4x.js} +1 -1
  198. package/out/web/assets/{sequenceDiagram-WJ2MYXX4-CTnplOAX.js → sequenceDiagram-WJ2MYXX4-aLgY-6Wz.js} +1 -1
  199. package/out/web/assets/{stateDiagram-D77RDMKH-BFrIaLdI.js → stateDiagram-D77RDMKH-D5QWfOhp.js} +1 -1
  200. package/out/web/assets/stateDiagram-v2-MP3YSRHH-D6OiZoAP.js +1 -0
  201. package/out/web/assets/{swimlanes-42K2YHIH-BXGngUNU.js → swimlanes-42K2YHIH-CRs5UyA-.js} +1 -1
  202. package/out/web/assets/swimlanesDiagram-VR7AAH4N-UgEXIsV0.js +8 -0
  203. package/out/web/assets/syntaxHighlight-C-wAGT84.js +3 -0
  204. package/out/web/assets/{timeline-definition-24CTP7MA-CegH7n43.js → timeline-definition-24CTP7MA-Denqd9qb.js} +1 -1
  205. package/out/web/assets/{treeView-Q6P3EWNA-DTpJv2Tw.js → treeView-Q6P3EWNA-0my3Z3s4.js} +1 -1
  206. package/out/web/assets/{treemap-WGGIJYW6-i8M9iJU6.js → treemap-WGGIJYW6-B_EjkLYq.js} +1 -1
  207. package/out/web/assets/{vennDiagram-4TSXK5OY-Dpzo1S8U.js → vennDiagram-4TSXK5OY-DxVg3Ewr.js} +1 -1
  208. package/out/web/assets/{wardley-WFR3VGLG-BrNsgtfw.js → wardley-WFR3VGLG-CGD-Tj4p.js} +1 -1
  209. package/out/web/assets/{wardleyDiagram-VM6X3IG4-lQ92LFE1.js → wardleyDiagram-VM6X3IG4-CmfS39Xm.js} +1 -1
  210. package/out/web/assets/{xychartDiagram-S5SC5T6Z-BaOaEoBd.js → xychartDiagram-S5SC5T6Z-BVM9bKis.js} +1 -1
  211. package/out/web/index-web.html +2 -2
  212. package/out/web/index.html +2 -2
  213. package/package.json +2 -2
  214. package/shared-modules/CODEOWNERS +3 -3
  215. package/shared-modules/SDL2/SDL2_mixer.json +27 -0
  216. package/shared-modules/appstream/README.md +13 -0
  217. package/shared-modules/appstream/appstream-compose.json +111 -0
  218. package/shared-modules/dbus-glib/dbus-glib.json +2 -2
  219. package/shared-modules/libportal/libportal.json +23 -0
  220. package/shared-modules/linux-audio/fluidsynth2-static.json +2 -2
  221. package/shared-modules/linux-audio/fluidsynth2.json +2 -2
  222. package/shared-modules/linux-audio/libinstpatch.json +2 -2
  223. package/shared-modules/linux-audio/liblo-static.json +3 -0
  224. package/shared-modules/luajit/luajit.json +2 -2
  225. package/shared-modules/squashfs-tools/squashfs-tools.json +0 -3
  226. package/slash-commands.json +7 -2
  227. package/src/renderer/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  228. package/src/renderer/shared/agentProgress.ts +51 -3
  229. package/src/renderer/shared/components/Settings/AutomationTab.tsx +2 -56
  230. package/src/renderer/shared/components/Settings/AvatarCropModal.tsx +15 -1
  231. package/src/renderer/shared/components/Settings/GeneralTab.tsx +25 -12
  232. package/src/renderer/shared/components/Settings/ThemeTab.test.tsx +45 -0
  233. package/src/renderer/shared/components/Settings/ThemeTab.tsx +106 -3
  234. package/src/renderer/shared/components/activity/AgentActivityDrawer.tsx +67 -17
  235. package/src/renderer/shared/components/activity/AgentActivityTable.tsx +376 -134
  236. package/src/renderer/shared/components/activity/AskUserActivityCard.test.tsx +122 -0
  237. package/src/renderer/shared/components/activity/AskUserActivityCard.tsx +70 -0
  238. package/src/renderer/shared/components/activity/McpToolResults.tsx +39 -2
  239. package/src/renderer/shared/components/activity/SubagentDagView.tsx +7 -7
  240. package/src/renderer/shared/components/activity/ViewedImages.tsx +41 -2
  241. package/src/renderer/shared/components/app/ChunkErrorBoundary.tsx +3 -3
  242. package/src/renderer/shared/components/app/CommandPalette.tsx +7 -7
  243. package/src/renderer/shared/components/app/DesktopTitlebar.tsx +2 -2
  244. package/src/renderer/shared/components/app/MintDashboard.tsx +27 -17
  245. package/src/renderer/shared/components/app/ModalErrorBoundary.tsx +3 -3
  246. package/src/renderer/shared/components/cards/CalculationCard.tsx +3 -3
  247. package/src/renderer/shared/components/cards/ImageGenCard.tsx +2 -2
  248. package/src/renderer/shared/components/cards/ImageSearchCard.tsx +2 -2
  249. package/src/renderer/shared/components/cards/MermaidCard.tsx +1 -1
  250. package/src/renderer/shared/components/cards/PlanCard.test.tsx +41 -0
  251. package/src/renderer/shared/components/cards/PlanCard.tsx +32 -0
  252. package/src/renderer/shared/components/cards/ReleaseNotesCard.test.tsx +29 -0
  253. package/src/renderer/shared/components/cards/ReleaseNotesCard.tsx +137 -0
  254. package/src/renderer/shared/components/cards/StockCard.tsx +7 -7
  255. package/src/renderer/shared/components/cards/ToolResultCards.tsx +11 -3
  256. package/src/renderer/shared/components/cards/UiMockupWidget.tsx +4 -4
  257. package/src/renderer/shared/components/cards/WeatherCard.tsx +5 -5
  258. package/src/renderer/shared/components/chat/ApprovalCard.test.tsx +11 -0
  259. package/src/renderer/shared/components/chat/ApprovalCard.tsx +21 -2
  260. package/src/renderer/shared/components/chat/ArtifactCards.test.tsx +69 -0
  261. package/src/renderer/shared/components/chat/ArtifactCards.tsx +238 -0
  262. package/src/renderer/shared/components/chat/AttachedDocumentCard.test.tsx +39 -0
  263. package/src/renderer/shared/components/chat/AttachedDocumentCard.tsx +220 -0
  264. package/src/renderer/shared/components/chat/ChatCodeBlock.test.tsx +33 -0
  265. package/src/renderer/shared/components/chat/ChatCodeBlock.tsx +15 -1
  266. package/src/renderer/shared/components/chat/ChatMessageItem.test.tsx +40 -2
  267. package/src/renderer/shared/components/chat/ChatMessageItem.tsx +180 -34
  268. package/src/renderer/shared/components/chat/ChatModeSelector.css +227 -0
  269. package/src/renderer/shared/components/chat/ChatModeSelector.test.tsx +45 -0
  270. package/src/renderer/shared/components/chat/ChatModeSelector.tsx +198 -0
  271. package/src/renderer/shared/components/chat/ChatPanel.test.tsx +22 -0
  272. package/src/renderer/shared/components/chat/ChatPanel.tsx +227 -53
  273. package/src/renderer/shared/components/chat/DiffReviewModal.tsx +6 -6
  274. package/src/renderer/shared/components/chat/GeminiLiveOverlay.test.tsx +142 -1
  275. package/src/renderer/shared/components/chat/GeminiLiveOverlay.tsx +45 -3
  276. package/src/renderer/shared/components/chat/ModelSelectorPopover.tsx +42 -14
  277. package/src/renderer/shared/components/chat/RewindModal.tsx +10 -10
  278. package/src/renderer/shared/components/chat/SlashSuggestions.test.tsx +14 -0
  279. package/src/renderer/shared/components/chat/SourcesBlock.test.tsx +53 -0
  280. package/src/renderer/shared/components/chat/SourcesBlock.tsx +236 -90
  281. package/src/renderer/shared/components/chat/TurnFileChanges.test.tsx +45 -1
  282. package/src/renderer/shared/components/chat/TurnFileChanges.tsx +202 -169
  283. package/src/renderer/shared/components/extensions/CliSessionsView.tsx +5 -4
  284. package/src/renderer/shared/components/extensions/McpRegistryPicker.tsx +3 -3
  285. package/src/renderer/shared/components/studio/LibraryPage.tsx +48 -5
  286. package/src/renderer/shared/components/workspace/ArtifactPreviewPanel.tsx +125 -13
  287. package/src/renderer/shared/components/workspace/CodeReviewPage.test.tsx +50 -0
  288. package/src/renderer/shared/components/workspace/CodeReviewPage.tsx +77 -16
  289. package/src/renderer/shared/components/workspace/GitBranchSelector.tsx +78 -22
  290. package/src/renderer/shared/components/workspace/ToolSurfacePage.test.tsx +13 -0
  291. package/src/renderer/shared/components/workspace/ToolSurfacePage.tsx +48 -28
  292. package/src/renderer/shared/components/workspace/WorkspacePdfPreview.tsx +266 -47
  293. package/src/renderer/shared/components/workspace/WorkspacePreviewActions.test.tsx +18 -6
  294. package/src/renderer/shared/components/workspace/background-terminals.css +11 -11
  295. package/src/renderer/shared/constants/config.ts +1 -5
  296. package/src/renderer/shared/constants/models.test.ts +51 -0
  297. package/src/renderer/shared/constants/models.ts +139 -32
  298. package/src/renderer/shared/conversation/lifecycle.test.ts +91 -3
  299. package/src/renderer/shared/conversation/lifecycle.ts +104 -5
  300. package/src/renderer/shared/conversation/progressiveStream.test.ts +83 -0
  301. package/src/renderer/shared/conversation/progressiveStream.ts +219 -0
  302. package/src/renderer/shared/css/agent-observability.css +133 -0
  303. package/src/renderer/shared/css/chat-ui-cards.css +13 -13
  304. package/src/renderer/shared/css/code-review.css +63 -34
  305. package/src/renderer/shared/css/code-sessions.css +47 -18
  306. package/src/renderer/shared/css/collapsed-paste.css +1 -1
  307. package/src/renderer/shared/css/error-notice.css +1 -1
  308. package/src/renderer/shared/css/file-changes.css +165 -80
  309. package/src/renderer/shared/css/library.css +105 -10
  310. package/src/renderer/shared/css/management-views.css +1 -1
  311. package/src/renderer/shared/css/mcp-results.css +5 -5
  312. package/src/renderer/shared/css/model-selector.css +18 -18
  313. package/src/renderer/shared/css/mods.css +7 -7
  314. package/src/renderer/shared/css/notification-notice.css +2 -2
  315. package/src/renderer/shared/css/queued-messages.css +5 -5
  316. package/src/renderer/shared/css/settings/general.css +18 -13
  317. package/src/renderer/shared/css/settings/profile.css +5 -5
  318. package/src/renderer/shared/css/settings/theme.css +5 -5
  319. package/src/renderer/shared/css/slash-choice.css +2 -2
  320. package/src/renderer/shared/css/slash-suggestions.css +3 -3
  321. package/src/renderer/shared/css/sources-block.css +41 -2
  322. package/src/renderer/shared/css/tools-panel.css +2 -2
  323. package/src/renderer/shared/css/ui-tailwind.css +8 -0
  324. package/src/renderer/shared/css/veo-studio.css +2 -2
  325. package/src/renderer/shared/css/viewed-images.css +7 -7
  326. package/src/renderer/shared/platform.ts +2 -1
  327. package/src/renderer/shared/theme/themeManager.test.ts +17 -0
  328. package/src/renderer/shared/theme/themeManager.ts +21 -4
  329. package/src/renderer/shared/theme/themes.ts +54 -0
  330. package/src/renderer/shared/theme/tokens.css +6 -4
  331. package/src/renderer/shared/types.ts +1 -1
  332. package/src/renderer/shared/utils/agentActivity.test.ts +468 -2
  333. package/src/renderer/shared/utils/agentActivity.ts +602 -35
  334. package/src/renderer/shared/utils/artifactBrowser.test.ts +37 -32
  335. package/src/renderer/shared/utils/artifactBrowser.ts +17 -19
  336. package/src/renderer/shared/utils/attachedDocument.test.ts +73 -0
  337. package/src/renderer/shared/utils/attachedDocument.ts +49 -0
  338. package/src/renderer/shared/utils/fileIcons.test.ts +59 -0
  339. package/src/renderer/shared/utils/fileIcons.ts +149 -16
  340. package/src/renderer/shared/utils/markdown.tsx +72 -19
  341. package/src/renderer/shared/utils/markdown.webImages.test.tsx +16 -0
  342. package/src/renderer/shared/utils/mcpResults.test.tsx +29 -0
  343. package/src/renderer/shared/utils/slashCommandProcessor.ts +4 -1
  344. package/src/renderer/shared/utils/syntaxHighlight.test.tsx +11 -1
  345. package/src/renderer/shared/utils/syntaxHighlight.tsx +51 -3
  346. package/src/renderer/shared/utils/useGeminiLiveVoice.ts +324 -51
  347. package/src/renderer/shared/utils/workspaceDrag.test.ts +153 -0
  348. package/src/renderer/shared/utils/workspaceDrag.ts +142 -0
  349. package/src/renderer/shared/utils/workspaceMedia.ts +10 -2
  350. package/src/renderer/src/App.tsx +14 -8
  351. package/src/renderer/src/agentProgress.ts +1 -0
  352. package/src/renderer/src/components/ScreenPicker.css +1 -1
  353. package/src/renderer/src/components/WorkspacePanel.tsx +6 -5
  354. package/src/renderer/src/css/app-shell.css +4 -4
  355. package/src/renderer/src/css/chat.css +1261 -241
  356. package/src/renderer/src/css/desktop-tools.css +11 -11
  357. package/src/renderer/src/css/sidebar-workspace.css +2 -2
  358. package/src/renderer/src/css/tool-surfaces.css +12 -12
  359. package/src/renderer/src/index.css +38 -20
  360. package/src/renderer/src/tauri.ts +22 -2
  361. package/src/renderer/src-web/App.tsx +14 -0
  362. package/src/renderer/src-web/agentProgress.ts +1 -0
  363. package/src/renderer/src-web/css/app-shell.css +4 -4
  364. package/src/renderer/src-web/css/chat.css +1278 -190
  365. package/src/renderer/src-web/css/mobile.css +29 -13
  366. package/src/renderer/src-web/css/settings/mobile.css +2 -2
  367. package/src/renderer/src-web/css/sidebar-workspace.css +1 -1
  368. package/src/renderer/src-web/index.css +38 -20
  369. package/src/renderer/src-web/tauri.ts +22 -2
  370. package/src/renderer/tests/browserArtifacts.browser.tsx +1 -1
  371. package/src/renderer/tests/library.browser.tsx +3 -0
  372. package/src/renderer/tests/liveVoice.browser.html +1 -0
  373. package/src/renderer/tests/workspaceDrop.browser.tsx +1 -1
  374. package/src-tauri/src/app/lifecycle.rs +1 -14
  375. package/src-tauri/src/background/headless.rs +14 -8
  376. package/src-tauri/src/commands/conversation.rs +38 -0
  377. package/src-tauri/src/native/mod.rs +0 -1
  378. package/src-tauri/src/native/system.rs +4 -47
  379. package/crates/mint-cli/src/interactive/chat_tui.rs +0 -8855
  380. package/crates/mint-core/src/agent/chat.rs +0 -4394
  381. package/crates/mint-core/src/browser/lifecycle.rs +0 -291
  382. package/crates/mint-core/src/browser/logging.rs +0 -20
  383. package/crates/mint-core/src/orchestration/tools/browser.rs +0 -31
  384. package/out/web/assets/ArtifactPreviewPanel-S0dPfYFx.js +0 -2
  385. package/out/web/assets/CalculationCard-CRjP3E1j.js +0 -1
  386. package/out/web/assets/CliSessionsView-C3Br2v5Y.css +0 -1
  387. package/out/web/assets/CodeReviewPage-CUJceQme.css +0 -1
  388. package/out/web/assets/CodeReviewPage-VCUgPla_.js +0 -2
  389. package/out/web/assets/ImageGenCard-BCXxkVJX.js +0 -1
  390. package/out/web/assets/ImageSearchCard-N3yQOr8Z.js +0 -1
  391. package/out/web/assets/LibraryPage-DAxUTAg7.css +0 -1
  392. package/out/web/assets/LibraryPage-WlviRdR8.js +0 -2
  393. package/out/web/assets/LinkedFoldersView-CKRgroWK.js +0 -1
  394. package/out/web/assets/McpServersView-D3wHnuHg.js +0 -1
  395. package/out/web/assets/MermaidCard-NqrHdyJW.js +0 -2
  396. package/out/web/assets/MintDashboard-B2DJsDDO.css +0 -1
  397. package/out/web/assets/MintDashboard-COw_xol7.js +0 -63
  398. package/out/web/assets/RewindModal-DempuDod.js +0 -1
  399. package/out/web/assets/SettingsWindow-2YP3yj7s.js +0 -4
  400. package/out/web/assets/SettingsWindow-Dz8LQVgD.css +0 -1
  401. package/out/web/assets/StockCard-BDMCe7mx.js +0 -1
  402. package/out/web/assets/ToolSurfacePage-B0J_q0Jl.js +0 -38
  403. package/out/web/assets/ToolSurfacePage-CRIQ1owD.css +0 -1
  404. package/out/web/assets/UiMockupWidget-DD73QWg1.js +0 -1
  405. package/out/web/assets/WeatherCard-CPucIfq8.js +0 -1
  406. package/out/web/assets/WorkspacePdfPreview-Dx_lkjNu.js +0 -2
  407. package/out/web/assets/classDiagram-ZZMXUADV-BgJ5WtSp.js +0 -1
  408. package/out/web/assets/classDiagram-v2-VYDZK3BY-BgJ5WtSp.js +0 -1
  409. package/out/web/assets/config-CvIO5hkn.js +0 -2
  410. package/out/web/assets/flowDiagram-HODETNUW-uqCHJond.js +0 -1
  411. package/out/web/assets/index-BmpulIkg.js +0 -168
  412. package/out/web/assets/index-CkvBwhX_.css +0 -1
  413. package/out/web/assets/stateDiagram-v2-MP3YSRHH-uf3kdNW4.js +0 -1
  414. package/out/web/assets/swimlanesDiagram-VR7AAH4N-Y8q5bQce.js +0 -8
  415. package/shared-modules/libsecret/libsecret.json +0 -23
  416. package/shared-modules/libusb/libusb.json +0 -27
  417. package/shared-modules/lzo/lzo.json +0 -27
  418. package/src/renderer/src/components/BrowserShell.tsx +0 -108
  419. package/src/renderer/src/components/BrowserSuggestionsPopup.tsx +0 -91
  420. package/src/renderer/src/components/browser-shell.css +0 -185
  421. package/src-tauri/src/native/browser.rs +0 -252
@@ -18,8 +18,11 @@ use std::net::{TcpStream, ToSocketAddrs};
18
18
  use std::process::Command;
19
19
  use std::sync::mpsc;
20
20
  use std::time::Duration;
21
+ use unicode_width::UnicodeWidthStr;
21
22
 
22
- use crate::terminal_theme::{self, ACCENT, BLUE, MUTED, SUBTLE, TEXT};
23
+ use crate::terminal_theme::{
24
+ self, ACCENT, BLUE, MUTED, SELECTION_BACKGROUND, SELECTION_TEXT, SUBTLE, TEXT,
25
+ };
23
26
  use mint_core::slash::models::HUGGINGFACE_MODEL_PRESETS;
24
27
  use mint_core::{MintConfig, load_config, save_config};
25
28
 
@@ -2232,10 +2235,10 @@ impl OnboardWizard {
2232
2235
  let block = Block::default()
2233
2236
  .borders(Borders::ALL)
2234
2237
  .border_type(BorderType::Rounded)
2235
- .border_style(Style::default().fg(BLUE))
2238
+ .border_style(Style::default().fg(SUBTLE))
2236
2239
  .title(Span::styled(
2237
2240
  " Mint Setup Wizard ",
2238
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
2241
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2239
2242
  ));
2240
2243
 
2241
2244
  let paragraph = Paragraph::new(header_line)
@@ -2250,14 +2253,14 @@ impl OnboardWizard {
2250
2253
  Span::styled(
2251
2254
  format!(" [{title}] "),
2252
2255
  Style::default()
2253
- .fg(Color::Black)
2254
- .bg(ACCENT)
2256
+ .fg(SELECTION_TEXT)
2257
+ .bg(SELECTION_BACKGROUND)
2255
2258
  .add_modifier(Modifier::BOLD),
2256
2259
  )
2257
2260
  } else if is_done {
2258
2261
  Span::styled(
2259
2262
  format!(" ✔ {title} "),
2260
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
2263
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2261
2264
  )
2262
2265
  } else {
2263
2266
  Span::styled(format!(" {title} "), Style::default().fg(MUTED))
@@ -2275,7 +2278,7 @@ impl OnboardWizard {
2275
2278
  .border_style(Style::default().fg(SUBTLE))
2276
2279
  .title(Span::styled(
2277
2280
  " Step 1: Core AI (Google Gemini) ",
2278
- Style::default().fg(BLUE).add_modifier(Modifier::BOLD),
2281
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2279
2282
  ));
2280
2283
 
2281
2284
  let inner = block.inner(area);
@@ -2301,19 +2304,25 @@ impl OnboardWizard {
2301
2304
  frame.render_widget(subtitle, chunks[0]);
2302
2305
 
2303
2306
  // Key Input Box
2304
- let key_disp = if self.gemini_key.is_empty() {
2305
- Span::styled(
2307
+ let is_key_focused = self.gemini_focus == 0;
2308
+ let mut key_spans = Vec::new();
2309
+ if self.gemini_key.is_empty() {
2310
+ key_spans.push(Span::styled(
2306
2311
  "(Paste your Gemini API key here with Ctrl+V)",
2307
2312
  Style::default().fg(MUTED),
2308
- )
2313
+ ));
2309
2314
  } else if self.gemini_masked {
2310
- Span::styled(
2315
+ key_spans.push(Span::styled(
2311
2316
  "•".repeat(self.gemini_key.chars().count().min(50)),
2312
2317
  Style::default().fg(TEXT),
2313
- )
2318
+ ));
2314
2319
  } else {
2315
- Span::styled(&self.gemini_key, Style::default().fg(TEXT))
2316
- };
2320
+ key_spans.push(Span::styled(&self.gemini_key, Style::default().fg(TEXT)));
2321
+ }
2322
+ if is_key_focused {
2323
+ key_spans.push(Span::styled("█", Style::default().fg(TEXT)));
2324
+ }
2325
+ let key_disp = Line::from(key_spans);
2317
2326
 
2318
2327
  let mask_hint = if self.gemini_masked {
2319
2328
  "[Ctrl+H: Show]"
@@ -2321,38 +2330,60 @@ impl OnboardWizard {
2321
2330
  "[Ctrl+H: Hide]"
2322
2331
  };
2323
2332
 
2324
- let key_border_color = if self.gemini_focus == 0 {
2325
- ACCENT
2326
- } else {
2327
- SUBTLE
2328
- };
2329
- let key_block = Block::default()
2330
- .borders(Borders::ALL)
2331
- .border_type(BorderType::Rounded)
2332
- .border_style(Style::default().fg(key_border_color))
2333
- .title(Line::from(vec![
2333
+ let key_border_color = if is_key_focused { TEXT } else { SUBTLE };
2334
+ let key_title = if is_key_focused {
2335
+ Line::from(vec![
2334
2336
  Span::styled(
2335
2337
  " API Key ",
2338
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2339
+ ),
2340
+ Span::styled(
2341
+ " [ EDITING ] ",
2336
2342
  Style::default()
2337
- .fg(if self.gemini_focus == 0 { ACCENT } else { TEXT })
2343
+ .fg(SELECTION_TEXT)
2344
+ .bg(SELECTION_BACKGROUND)
2338
2345
  .add_modifier(Modifier::BOLD),
2339
2346
  ),
2347
+ Span::raw(" "),
2340
2348
  Span::styled(mask_hint, Style::default().fg(BLUE)),
2341
- ]));
2349
+ ])
2350
+ } else if !self.gemini_key.is_empty() {
2351
+ Line::from(vec![
2352
+ Span::styled(
2353
+ " ✔ API Key (Configured · Tab to edit) ",
2354
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2355
+ ),
2356
+ Span::styled(mask_hint, Style::default().fg(MUTED)),
2357
+ ])
2358
+ } else {
2359
+ Line::from(vec![
2360
+ Span::styled(" API Key (Tab to edit) ", Style::default().fg(MUTED)),
2361
+ Span::styled(mask_hint, Style::default().fg(MUTED)),
2362
+ ])
2363
+ };
2364
+
2365
+ let key_block = Block::default()
2366
+ .borders(Borders::ALL)
2367
+ .border_type(BorderType::Rounded)
2368
+ .border_style(Style::default().fg(key_border_color))
2369
+ .title(key_title);
2342
2370
 
2343
2371
  frame.render_widget(Paragraph::new(key_disp).block(key_block), chunks[1]);
2344
2372
 
2345
2373
  // Tip line
2374
+ let tip_text = if is_key_focused {
2375
+ "Paste with Ctrl+V. Press [Enter] or [Tab] to switch to model list."
2376
+ } else {
2377
+ "Use [↑/↓] to select a model. Press [Enter] to continue, or [Tab] to edit API key."
2378
+ };
2346
2379
  let tip_line = Line::from(vec![
2347
2380
  Span::styled("• Tip: ", Style::default().fg(MUTED)),
2348
- Span::styled(
2349
- "You can paste from clipboard with Ctrl+V. Press Tab or ↓ to jump to model list.",
2350
- Style::default().fg(MUTED),
2351
- ),
2381
+ Span::styled(tip_text, Style::default().fg(MUTED)),
2352
2382
  ]);
2353
2383
  frame.render_widget(Paragraph::new(tip_line), chunks[2]);
2354
2384
 
2355
2385
  // Vertical Model List
2386
+ let is_model_focused = self.gemini_focus == 1;
2356
2387
  let model_items: Vec<ListItem> = self
2357
2388
  .gemini_models
2358
2389
  .iter()
@@ -2360,7 +2391,7 @@ impl OnboardWizard {
2360
2391
  .map(|(i, model)| {
2361
2392
  let is_sel = i == self.gemini_model_idx;
2362
2393
  let radio = if is_sel { " ◉ " } else { " ○ " };
2363
- let cursor = if is_sel { " ❯ " } else { " " };
2394
+ let cursor = if is_sel { " › " } else { " " };
2364
2395
 
2365
2396
  let desc = match model.as_str() {
2366
2397
  "gemini-2.5-flash" => "Recommended · Ultra-fast, multipurpose, thinking",
@@ -2373,25 +2404,58 @@ impl OnboardWizard {
2373
2404
  _ => "",
2374
2405
  };
2375
2406
 
2376
- let name_style = if is_sel {
2377
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD)
2407
+ if is_model_focused && is_sel {
2408
+ let line = Line::from(vec![
2409
+ Span::styled(
2410
+ cursor,
2411
+ Style::default()
2412
+ .fg(SELECTION_TEXT)
2413
+ .bg(SELECTION_BACKGROUND)
2414
+ .add_modifier(Modifier::BOLD),
2415
+ ),
2416
+ Span::styled(
2417
+ radio,
2418
+ Style::default()
2419
+ .fg(SELECTION_TEXT)
2420
+ .bg(SELECTION_BACKGROUND)
2421
+ .add_modifier(Modifier::BOLD),
2422
+ ),
2423
+ Span::styled(
2424
+ format!("{:<20}", model),
2425
+ Style::default()
2426
+ .fg(SELECTION_TEXT)
2427
+ .bg(SELECTION_BACKGROUND)
2428
+ .add_modifier(Modifier::BOLD),
2429
+ ),
2430
+ Span::styled(
2431
+ " ",
2432
+ Style::default().fg(SELECTION_TEXT).bg(SELECTION_BACKGROUND),
2433
+ ),
2434
+ Span::styled(
2435
+ desc,
2436
+ Style::default().fg(SELECTION_TEXT).bg(SELECTION_BACKGROUND),
2437
+ ),
2438
+ ]);
2439
+ ListItem::new(line).style(Style::default().bg(SELECTION_BACKGROUND))
2440
+ } else if is_sel {
2441
+ let line = Line::from(vec![
2442
+ Span::styled(" › ", Style::default().fg(SUBTLE)),
2443
+ Span::styled(radio, Style::default().fg(SUBTLE)),
2444
+ Span::styled(format!("{:<20}", model), Style::default().fg(TEXT)),
2445
+ Span::raw(" "),
2446
+ Span::styled(format!("{desc} (selected)"), Style::default().fg(MUTED)),
2447
+ ]);
2448
+ ListItem::new(line)
2378
2449
  } else {
2379
- Style::default().fg(TEXT)
2380
- };
2381
-
2382
- let line = Line::from(vec![
2383
- Span::styled(cursor, Style::default().fg(ACCENT)),
2384
- Span::styled(
2385
- radio,
2386
- Style::default()
2387
- .fg(if is_sel { ACCENT } else { MUTED })
2388
- .add_modifier(Modifier::BOLD),
2389
- ),
2390
- Span::styled(format!("{:<20}", model), name_style),
2391
- Span::raw(" "),
2392
- Span::styled(desc, Style::default().fg(MUTED)),
2393
- ]);
2394
- ListItem::new(line)
2450
+ let line = Line::from(vec![
2451
+ Span::raw(" "),
2452
+ Span::styled(radio, Style::default().fg(MUTED)),
2453
+ Span::styled(format!("{:<20}", model), Style::default().fg(TEXT)),
2454
+ Span::raw(" "),
2455
+ Span::styled(desc, Style::default().fg(MUTED)),
2456
+ ]);
2457
+ ListItem::new(line)
2458
+ }
2395
2459
  })
2396
2460
  .collect();
2397
2461
 
@@ -2408,27 +2472,44 @@ impl OnboardWizard {
2408
2472
  )
2409
2473
  };
2410
2474
 
2411
- let model_border_color = if self.gemini_focus == 1 {
2412
- ACCENT
2413
- } else {
2414
- SUBTLE
2415
- };
2416
- let list_block = Block::default()
2417
- .borders(Borders::ALL)
2418
- .border_type(BorderType::Rounded)
2419
- .border_style(Style::default().fg(model_border_color))
2420
- .title(Line::from(vec![
2475
+ let model_border_color = if is_model_focused { TEXT } else { SUBTLE };
2476
+ let model_title = if is_model_focused {
2477
+ Line::from(vec![
2478
+ Span::styled(
2479
+ " Default Model ",
2480
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2481
+ ),
2421
2482
  Span::styled(
2422
- " Default Model (↑/↓ to select) ",
2483
+ " [ SELECTING ] ",
2423
2484
  Style::default()
2424
- .fg(if self.gemini_focus == 1 { ACCENT } else { TEXT })
2485
+ .fg(SELECTION_TEXT)
2486
+ .bg(SELECTION_BACKGROUND)
2425
2487
  .add_modifier(Modifier::BOLD),
2426
2488
  ),
2489
+ Span::styled(" (↑/↓ to choose) ", Style::default().fg(MUTED)),
2427
2490
  Span::styled(
2428
2491
  format!(" {model_status} "),
2429
2492
  Style::default().fg(if self.is_verifying { ACCENT } else { BLUE }),
2430
2493
  ),
2431
- ]));
2494
+ ])
2495
+ } else {
2496
+ Line::from(vec![
2497
+ Span::styled(
2498
+ " Default Model (Press Tab or ↓ to select) ",
2499
+ Style::default().fg(MUTED),
2500
+ ),
2501
+ Span::styled(
2502
+ format!(" {model_status} "),
2503
+ Style::default().fg(if self.is_verifying { ACCENT } else { BLUE }),
2504
+ ),
2505
+ ])
2506
+ };
2507
+
2508
+ let list_block = Block::default()
2509
+ .borders(Borders::ALL)
2510
+ .border_type(BorderType::Rounded)
2511
+ .border_style(Style::default().fg(model_border_color))
2512
+ .title(model_title);
2432
2513
 
2433
2514
  let mut list_state = ListState::default();
2434
2515
  list_state.select(Some(self.gemini_model_idx));
@@ -2440,38 +2521,79 @@ impl OnboardWizard {
2440
2521
 
2441
2522
  // Custom Model Input Box
2442
2523
  if self.is_custom_gemini_model {
2524
+ let is_custom_focused = self.gemini_focus == 1;
2525
+ let custom_border_color = if is_custom_focused { TEXT } else { SUBTLE };
2526
+ let custom_title = if is_custom_focused {
2527
+ Line::from(vec![
2528
+ Span::styled(
2529
+ " Custom Model Identifier ",
2530
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2531
+ ),
2532
+ Span::styled(
2533
+ " [ EDITING ] ",
2534
+ Style::default()
2535
+ .fg(SELECTION_TEXT)
2536
+ .bg(SELECTION_BACKGROUND)
2537
+ .add_modifier(Modifier::BOLD),
2538
+ ),
2539
+ ])
2540
+ } else {
2541
+ Line::from(vec![Span::styled(
2542
+ " Custom Model Identifier ",
2543
+ Style::default().fg(MUTED),
2544
+ )])
2545
+ };
2443
2546
  let custom_block = Block::default()
2444
2547
  .borders(Borders::ALL)
2445
2548
  .border_type(BorderType::Rounded)
2446
- .border_style(Style::default().fg(ACCENT))
2447
- .title(Span::styled(
2448
- " Custom Model Identifier ",
2449
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
2450
- ));
2451
- let custom_val = if self.custom_gemini_model.is_empty() {
2452
- Span::styled(
2549
+ .border_style(Style::default().fg(custom_border_color))
2550
+ .title(custom_title);
2551
+ let mut custom_spans = Vec::new();
2552
+ if self.custom_gemini_model.is_empty() {
2553
+ custom_spans.push(Span::styled(
2453
2554
  "(Type custom model identifier, e.g. gemini-2.0-flash-exp)",
2454
2555
  Style::default().fg(MUTED),
2455
- )
2556
+ ));
2456
2557
  } else {
2457
- Span::styled(&self.custom_gemini_model, Style::default().fg(TEXT))
2458
- };
2459
- frame.render_widget(Paragraph::new(custom_val).block(custom_block), chunks[4]);
2558
+ custom_spans.push(Span::styled(
2559
+ &self.custom_gemini_model,
2560
+ Style::default().fg(TEXT),
2561
+ ));
2562
+ }
2563
+ if is_custom_focused {
2564
+ custom_spans.push(Span::styled("█", Style::default().fg(TEXT)));
2565
+ }
2566
+ frame.render_widget(
2567
+ Paragraph::new(Line::from(custom_spans)).block(custom_block),
2568
+ chunks[4],
2569
+ );
2460
2570
  }
2461
2571
 
2462
2572
  // Bottom Action Hint
2463
- let action_hint = Paragraph::new(Line::from(vec![
2464
- Span::styled(" [ Esc: Cancel ] ", Style::default().fg(MUTED)),
2465
- Span::raw(" "),
2466
- Span::styled(
2467
- " [ Enter: Continue to Services ➔ ] ",
2468
- Style::default()
2469
- .fg(Color::Black)
2470
- .bg(ACCENT)
2471
- .add_modifier(Modifier::BOLD),
2472
- ),
2473
- ]))
2474
- .alignment(Alignment::Right);
2573
+ let action_hint = if is_key_focused {
2574
+ Paragraph::new(Line::from(vec![
2575
+ Span::styled(" [ Esc: Cancel ] ", Style::default().fg(MUTED)),
2576
+ Span::raw(" "),
2577
+ Span::styled(
2578
+ " [ Enter / Tab: Select Model ➔ ] ",
2579
+ Style::default().fg(TEXT),
2580
+ ),
2581
+ ]))
2582
+ .alignment(Alignment::Right)
2583
+ } else {
2584
+ Paragraph::new(Line::from(vec![
2585
+ Span::styled(" [ Tab: Back to API Key ] ", Style::default().fg(MUTED)),
2586
+ Span::raw(" "),
2587
+ Span::styled(
2588
+ " [ Enter: Continue to Services ➔ ] ",
2589
+ Style::default()
2590
+ .fg(SELECTION_TEXT)
2591
+ .bg(SELECTION_BACKGROUND)
2592
+ .add_modifier(Modifier::BOLD),
2593
+ ),
2594
+ ]))
2595
+ .alignment(Alignment::Right)
2596
+ };
2475
2597
  frame.render_widget(action_hint, chunks[5]);
2476
2598
  }
2477
2599
 
@@ -2486,7 +2608,7 @@ impl OnboardWizard {
2486
2608
  .border_style(Style::default().fg(SUBTLE))
2487
2609
  .title(Span::styled(
2488
2610
  " Step 2: QuickStart Services & Providers ",
2489
- Style::default().fg(BLUE).add_modifier(Modifier::BOLD),
2611
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2490
2612
  ));
2491
2613
 
2492
2614
  let inner = block.inner(area);
@@ -2511,20 +2633,12 @@ impl OnboardWizard {
2511
2633
  // Build list items with category headers
2512
2634
  let mut list_items = Vec::new();
2513
2635
  let mut cur_category = "";
2636
+ let list_inner_width = chunks[1].width.saturating_sub(2) as usize;
2514
2637
 
2515
2638
  for (i, svc) in self.services.iter().enumerate() {
2516
2639
  let is_cursor = i == self.service_cursor;
2517
2640
  let check_icon = if svc.enabled { " ◉ " } else { " ○ " };
2518
- let check_color = if svc.enabled { ACCENT } else { MUTED };
2519
-
2520
- let cursor_prefix = if is_cursor { " ❯ " } else { " " };
2521
- let name_style = if is_cursor {
2522
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD)
2523
- } else if svc.enabled {
2524
- Style::default().fg(TEXT)
2525
- } else {
2526
- Style::default().fg(MUTED)
2527
- };
2641
+ let cursor_prefix = if is_cursor { " › " } else { " " };
2528
2642
 
2529
2643
  let mut lines = Vec::new();
2530
2644
  if svc.category != cur_category {
@@ -2535,33 +2649,95 @@ impl OnboardWizard {
2535
2649
  )]));
2536
2650
  }
2537
2651
 
2538
- lines.push(Line::from(vec![
2539
- Span::styled(cursor_prefix, Style::default().fg(ACCENT)),
2652
+ if is_cursor {
2653
+ let check_style = Style::default()
2654
+ .fg(if svc.enabled {
2655
+ Color::Black
2656
+ } else {
2657
+ Color::DarkGray
2658
+ })
2659
+ .bg(SELECTION_BACKGROUND)
2660
+ .add_modifier(Modifier::BOLD);
2661
+
2662
+ let desc_part = format!(" — {}", svc.description);
2663
+ let full_row_text = format!("{cursor_prefix}{check_icon}{}{desc_part}", svc.name);
2664
+ let visual_width = UnicodeWidthStr::width(full_row_text.as_str());
2665
+ let pad_len = list_inner_width.saturating_sub(visual_width);
2666
+
2667
+ lines.push(Line::from(vec![
2668
+ Span::styled(
2669
+ cursor_prefix,
2670
+ Style::default()
2671
+ .fg(SELECTION_TEXT)
2672
+ .bg(SELECTION_BACKGROUND)
2673
+ .add_modifier(Modifier::BOLD),
2674
+ ),
2675
+ Span::styled(check_icon, check_style),
2676
+ Span::styled(
2677
+ svc.name,
2678
+ Style::default()
2679
+ .fg(SELECTION_TEXT)
2680
+ .bg(SELECTION_BACKGROUND)
2681
+ .add_modifier(Modifier::BOLD),
2682
+ ),
2683
+ Span::styled(
2684
+ desc_part,
2685
+ Style::default().fg(SELECTION_TEXT).bg(SELECTION_BACKGROUND),
2686
+ ),
2687
+ Span::styled(
2688
+ " ".repeat(pad_len),
2689
+ Style::default().bg(SELECTION_BACKGROUND),
2690
+ ),
2691
+ ]));
2692
+ list_items.push(ListItem::new(lines));
2693
+ } else {
2694
+ let name_style = if svc.enabled {
2695
+ Style::default().fg(TEXT)
2696
+ } else {
2697
+ Style::default().fg(MUTED)
2698
+ };
2699
+ let check_color = if svc.enabled { ACCENT } else { MUTED };
2700
+
2701
+ lines.push(Line::from(vec![
2702
+ Span::raw(cursor_prefix),
2703
+ Span::styled(
2704
+ check_icon,
2705
+ Style::default()
2706
+ .fg(check_color)
2707
+ .add_modifier(Modifier::BOLD),
2708
+ ),
2709
+ Span::styled(svc.name, name_style),
2710
+ Span::styled(
2711
+ format!(" — {}", svc.description),
2712
+ Style::default().fg(MUTED),
2713
+ ),
2714
+ ]));
2715
+ list_items.push(ListItem::new(lines));
2716
+ }
2717
+ }
2718
+
2719
+ let list_block = Block::default()
2720
+ .borders(Borders::ALL)
2721
+ .border_type(BorderType::Rounded)
2722
+ .border_style(Style::default().fg(TEXT))
2723
+ .title(Line::from(vec![
2724
+ Span::styled(
2725
+ " Available Services ",
2726
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2727
+ ),
2540
2728
  Span::styled(
2541
- check_icon,
2729
+ " [ SELECTING ] ",
2542
2730
  Style::default()
2543
- .fg(check_color)
2731
+ .fg(SELECTION_TEXT)
2732
+ .bg(SELECTION_BACKGROUND)
2544
2733
  .add_modifier(Modifier::BOLD),
2545
2734
  ),
2546
- Span::styled(svc.name, name_style),
2547
2735
  Span::styled(
2548
- format!(" — {}", svc.description),
2736
+ " [Space: Toggle | a: All | i: Invert] ",
2549
2737
  Style::default().fg(MUTED),
2550
2738
  ),
2551
2739
  ]));
2552
2740
 
2553
- list_items.push(ListItem::new(lines));
2554
- }
2555
-
2556
- let list_block = Block::default()
2557
- .borders(Borders::ALL)
2558
- .border_type(BorderType::Rounded)
2559
- .border_style(Style::default().fg(ACCENT))
2560
- .title(Span::styled(
2561
- " Available Services [Space: Toggle | a: All | i: Invert] ",
2562
- Style::default().fg(ACCENT),
2563
- ));
2564
-
2565
2741
  let mut list_state = ListState::default();
2566
2742
  list_state.select(Some(self.service_cursor));
2567
2743
  frame.render_stateful_widget(
@@ -2582,8 +2758,8 @@ impl OnboardWizard {
2582
2758
  Span::styled(
2583
2759
  " [ Enter: Configure Selected Services ➔ ] ",
2584
2760
  Style::default()
2585
- .fg(Color::Black)
2586
- .bg(ACCENT)
2761
+ .fg(SELECTION_TEXT)
2762
+ .bg(SELECTION_BACKGROUND)
2587
2763
  .add_modifier(Modifier::BOLD),
2588
2764
  ),
2589
2765
  ]))
@@ -2603,14 +2779,14 @@ impl OnboardWizard {
2603
2779
  .border_style(Style::default().fg(SUBTLE))
2604
2780
  .title(Span::styled(
2605
2781
  " Step 3: Service Configurations ",
2606
- Style::default().fg(BLUE).add_modifier(Modifier::BOLD),
2782
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2607
2783
  ));
2608
2784
  let inner = block.inner(area);
2609
2785
  frame.render_widget(block, area);
2610
2786
  let notice = Paragraph::new(
2611
2787
  "\n ✔ Core AI (Gemini) is configured.\n\n No additional services were selected in Step 2.\n Press [Enter] to proceed to Summary and Save.",
2612
2788
  )
2613
- .style(Style::default().fg(ACCENT));
2789
+ .style(Style::default().fg(TEXT));
2614
2790
  frame.render_widget(notice, inner);
2615
2791
  return;
2616
2792
  }
@@ -2630,7 +2806,7 @@ impl OnboardWizard {
2630
2806
  .border_style(Style::default().fg(SUBTLE))
2631
2807
  .title(Span::styled(
2632
2808
  title,
2633
- Style::default().fg(BLUE).add_modifier(Modifier::BOLD),
2809
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2634
2810
  ));
2635
2811
 
2636
2812
  let inner = block.inner(area);
@@ -2648,6 +2824,9 @@ impl OnboardWizard {
2648
2824
  {
2649
2825
  // Unified: Key on top, Model picker below
2650
2826
  let is_custom = f1.is_custom;
2827
+ let is_f0_focused = focused_rel == 0;
2828
+ let is_f1_focused = focused_rel == 1;
2829
+
2651
2830
  let chunks = Layout::default()
2652
2831
  .direction(Direction::Vertical)
2653
2832
  .margin(1)
@@ -2669,19 +2848,23 @@ impl OnboardWizard {
2669
2848
  frame.render_widget(subtitle, chunks[0]);
2670
2849
 
2671
2850
  // Field 0: Text Input
2672
- let val_disp = if f0.value.is_empty() {
2673
- Span::styled(
2851
+ let mut val_spans = Vec::new();
2852
+ if f0.value.is_empty() {
2853
+ val_spans.push(Span::styled(
2674
2854
  format!("(Type or paste {} here)", f0.label),
2675
2855
  Style::default().fg(MUTED),
2676
- )
2856
+ ));
2677
2857
  } else if f0.is_secret && !f0.show_secret {
2678
- Span::styled(
2858
+ val_spans.push(Span::styled(
2679
2859
  "•".repeat(f0.value.chars().count().min(50)),
2680
2860
  Style::default().fg(TEXT),
2681
- )
2861
+ ));
2682
2862
  } else {
2683
- Span::styled(&f0.value, Style::default().fg(TEXT))
2684
- };
2863
+ val_spans.push(Span::styled(&f0.value, Style::default().fg(TEXT)));
2864
+ }
2865
+ if is_f0_focused {
2866
+ val_spans.push(Span::styled("█", Style::default().fg(TEXT)));
2867
+ }
2685
2868
 
2686
2869
  let mask_badge = if f0.is_secret {
2687
2870
  if f0.show_secret {
@@ -2693,29 +2876,60 @@ impl OnboardWizard {
2693
2876
  ""
2694
2877
  };
2695
2878
 
2696
- let f0_border = if focused_rel == 0 { ACCENT } else { SUBTLE };
2697
- let f0_block = Block::default()
2698
- .borders(Borders::ALL)
2699
- .border_type(BorderType::Rounded)
2700
- .border_style(Style::default().fg(f0_border))
2701
- .title(Line::from(vec![
2879
+ let f0_border = if is_f0_focused { TEXT } else { SUBTLE };
2880
+ let f0_title = if is_f0_focused {
2881
+ Line::from(vec![
2702
2882
  Span::styled(
2703
2883
  format!(" {} ", f0.label),
2884
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2885
+ ),
2886
+ Span::styled(
2887
+ " [ EDITING ] ",
2704
2888
  Style::default()
2705
- .fg(if focused_rel == 0 { ACCENT } else { TEXT })
2889
+ .fg(SELECTION_TEXT)
2890
+ .bg(SELECTION_BACKGROUND)
2706
2891
  .add_modifier(Modifier::BOLD),
2707
2892
  ),
2893
+ Span::raw(" "),
2708
2894
  Span::styled(mask_badge, Style::default().fg(BLUE)),
2709
- ]));
2895
+ ])
2896
+ } else if !f0.value.is_empty() {
2897
+ Line::from(vec![
2898
+ Span::styled(
2899
+ format!(" ✔ {} (Configured · Tab to edit) ", f0.label),
2900
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
2901
+ ),
2902
+ Span::styled(mask_badge, Style::default().fg(MUTED)),
2903
+ ])
2904
+ } else {
2905
+ Line::from(vec![
2906
+ Span::styled(
2907
+ format!(" {} (Tab to edit) ", f0.label),
2908
+ Style::default().fg(MUTED),
2909
+ ),
2910
+ Span::styled(mask_badge, Style::default().fg(MUTED)),
2911
+ ])
2912
+ };
2913
+
2914
+ let f0_block = Block::default()
2915
+ .borders(Borders::ALL)
2916
+ .border_type(BorderType::Rounded)
2917
+ .border_style(Style::default().fg(f0_border))
2918
+ .title(f0_title);
2710
2919
 
2711
- frame.render_widget(Paragraph::new(val_disp).block(f0_block), chunks[1]);
2920
+ frame.render_widget(
2921
+ Paragraph::new(Line::from(val_spans)).block(f0_block),
2922
+ chunks[1],
2923
+ );
2712
2924
 
2925
+ let tip_text = if is_f0_focused {
2926
+ "Press [Enter] or [Tab] to switch to model selection."
2927
+ } else {
2928
+ "Press [↑/↓] to change model. Press [Enter] to continue, or [Tab] to edit key."
2929
+ };
2713
2930
  let tip = Line::from(vec![
2714
2931
  Span::styled("• Tip: ", Style::default().fg(MUTED)),
2715
- Span::styled(
2716
- "Press [Tab] or [↓] to switch between API key and model selection.",
2717
- Style::default().fg(MUTED),
2718
- ),
2932
+ Span::styled(tip_text, Style::default().fg(MUTED)),
2719
2933
  ]);
2720
2934
  frame.render_widget(Paragraph::new(tip), chunks[2]);
2721
2935
 
@@ -2731,25 +2945,49 @@ impl OnboardWizard {
2731
2945
  .map(|(i, opt)| {
2732
2946
  let is_sel = i == selected;
2733
2947
  let radio = if is_sel { " ◉ " } else { " ○ " };
2734
- let cursor = if is_sel { " ❯ " } else { " " };
2735
-
2736
- let name_style = if is_sel {
2737
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD)
2948
+ let cursor = if is_sel { " › " } else { " " };
2949
+
2950
+ if is_f1_focused && is_sel {
2951
+ let line = Line::from(vec![
2952
+ Span::styled(
2953
+ cursor,
2954
+ Style::default()
2955
+ .fg(SELECTION_TEXT)
2956
+ .bg(SELECTION_BACKGROUND)
2957
+ .add_modifier(Modifier::BOLD),
2958
+ ),
2959
+ Span::styled(
2960
+ radio,
2961
+ Style::default()
2962
+ .fg(SELECTION_TEXT)
2963
+ .bg(SELECTION_BACKGROUND)
2964
+ .add_modifier(Modifier::BOLD),
2965
+ ),
2966
+ Span::styled(
2967
+ opt,
2968
+ Style::default()
2969
+ .fg(SELECTION_TEXT)
2970
+ .bg(SELECTION_BACKGROUND)
2971
+ .add_modifier(Modifier::BOLD),
2972
+ ),
2973
+ ]);
2974
+ ListItem::new(line).style(Style::default().bg(SELECTION_BACKGROUND))
2975
+ } else if is_sel {
2976
+ let line = Line::from(vec![
2977
+ Span::styled(" › ", Style::default().fg(SUBTLE)),
2978
+ Span::styled(radio, Style::default().fg(SUBTLE)),
2979
+ Span::styled(opt, Style::default().fg(TEXT)),
2980
+ Span::styled(" (selected)", Style::default().fg(MUTED)),
2981
+ ]);
2982
+ ListItem::new(line)
2738
2983
  } else {
2739
- Style::default().fg(TEXT)
2740
- };
2741
-
2742
- let line = Line::from(vec![
2743
- Span::styled(cursor, Style::default().fg(ACCENT)),
2744
- Span::styled(
2745
- radio,
2746
- Style::default()
2747
- .fg(if is_sel { ACCENT } else { MUTED })
2748
- .add_modifier(Modifier::BOLD),
2749
- ),
2750
- Span::styled(opt, name_style),
2751
- ]);
2752
- ListItem::new(line)
2984
+ let line = Line::from(vec![
2985
+ Span::raw(" "),
2986
+ Span::styled(radio, Style::default().fg(MUTED)),
2987
+ Span::styled(opt, Style::default().fg(TEXT)),
2988
+ ]);
2989
+ ListItem::new(line)
2990
+ }
2753
2991
  })
2754
2992
  .collect();
2755
2993
 
@@ -2764,18 +3002,21 @@ impl OnboardWizard {
2764
3002
  )
2765
3003
  };
2766
3004
 
2767
- let f1_border = if focused_rel == 1 { ACCENT } else { SUBTLE };
2768
- let list_block = Block::default()
2769
- .borders(Borders::ALL)
2770
- .border_type(BorderType::Rounded)
2771
- .border_style(Style::default().fg(f1_border))
2772
- .title(Line::from(vec![
3005
+ let f1_border = if is_f1_focused { TEXT } else { SUBTLE };
3006
+ let f1_title = if is_f1_focused {
3007
+ Line::from(vec![
3008
+ Span::styled(
3009
+ format!(" Default {} ", f1.label),
3010
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3011
+ ),
2773
3012
  Span::styled(
2774
- format!(" Default {} (↑/↓ to select) ", f1.label),
3013
+ " [ SELECTING ] ",
2775
3014
  Style::default()
2776
- .fg(if focused_rel == 1 { ACCENT } else { TEXT })
3015
+ .fg(SELECTION_TEXT)
3016
+ .bg(SELECTION_BACKGROUND)
2777
3017
  .add_modifier(Modifier::BOLD),
2778
3018
  ),
3019
+ Span::styled(" (↑/↓ to select) ", Style::default().fg(MUTED)),
2779
3020
  Span::styled(
2780
3021
  format!(" {status_badge} "),
2781
3022
  Style::default().fg(if self.is_fetching_service_models {
@@ -2784,7 +3025,29 @@ impl OnboardWizard {
2784
3025
  BLUE
2785
3026
  }),
2786
3027
  ),
2787
- ]));
3028
+ ])
3029
+ } else {
3030
+ Line::from(vec![
3031
+ Span::styled(
3032
+ format!(" Default {} (Press Tab or ↓ to select) ", f1.label),
3033
+ Style::default().fg(MUTED),
3034
+ ),
3035
+ Span::styled(
3036
+ format!(" {status_badge} "),
3037
+ Style::default().fg(if self.is_fetching_service_models {
3038
+ ACCENT
3039
+ } else {
3040
+ BLUE
3041
+ }),
3042
+ ),
3043
+ ])
3044
+ };
3045
+
3046
+ let list_block = Block::default()
3047
+ .borders(Borders::ALL)
3048
+ .border_type(BorderType::Rounded)
3049
+ .border_style(Style::default().fg(f1_border))
3050
+ .title(f1_title);
2788
3051
 
2789
3052
  let mut list_state = ListState::default();
2790
3053
  list_state.select(Some(selected));
@@ -2795,24 +3058,49 @@ impl OnboardWizard {
2795
3058
  );
2796
3059
 
2797
3060
  if is_custom {
3061
+ let custom_border = if is_f1_focused { TEXT } else { SUBTLE };
3062
+ let custom_title = if is_f1_focused {
3063
+ Line::from(vec![
3064
+ Span::styled(
3065
+ format!(" Custom {} Identifier ", f1.label),
3066
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3067
+ ),
3068
+ Span::styled(
3069
+ " [ EDITING ] ",
3070
+ Style::default()
3071
+ .fg(SELECTION_TEXT)
3072
+ .bg(SELECTION_BACKGROUND)
3073
+ .add_modifier(Modifier::BOLD),
3074
+ ),
3075
+ ])
3076
+ } else {
3077
+ Line::from(vec![Span::styled(
3078
+ format!(" Custom {} Identifier ", f1.label),
3079
+ Style::default().fg(MUTED),
3080
+ )])
3081
+ };
3082
+
2798
3083
  let custom_block = Block::default()
2799
3084
  .borders(Borders::ALL)
2800
3085
  .border_type(BorderType::Rounded)
2801
- .border_style(Style::default().fg(ACCENT))
2802
- .title(Span::styled(
2803
- format!(" Custom {} Identifier ", f1.label),
2804
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
2805
- ));
2806
- let custom_val = if f1.custom_value.is_empty() {
2807
- Span::styled(
3086
+ .border_style(Style::default().fg(custom_border))
3087
+ .title(custom_title);
3088
+
3089
+ let mut custom_spans = Vec::new();
3090
+ if f1.custom_value.is_empty() {
3091
+ custom_spans.push(Span::styled(
2808
3092
  "(Type custom model identifier)",
2809
3093
  Style::default().fg(MUTED),
2810
- )
3094
+ ));
2811
3095
  } else {
2812
- Span::styled(&f1.custom_value, Style::default().fg(TEXT))
2813
- };
3096
+ custom_spans
3097
+ .push(Span::styled(&f1.custom_value, Style::default().fg(TEXT)));
3098
+ }
3099
+ if is_f1_focused {
3100
+ custom_spans.push(Span::styled("█", Style::default().fg(TEXT)));
3101
+ }
2814
3102
  frame.render_widget(
2815
- Paragraph::new(custom_val).block(custom_block),
3103
+ Paragraph::new(Line::from(custom_spans)).block(custom_block),
2816
3104
  chunks[4],
2817
3105
  );
2818
3106
  }
@@ -2824,18 +3112,30 @@ impl OnboardWizard {
2824
3112
  " [ Enter: Continue to Finish ➔ ] "
2825
3113
  };
2826
3114
 
2827
- let action_hint = Paragraph::new(Line::from(vec![
2828
- Span::styled(" [ Esc: Back ] ", Style::default().fg(MUTED)),
2829
- Span::raw(" "),
2830
- Span::styled(
2831
- next_label,
2832
- Style::default()
2833
- .fg(Color::Black)
2834
- .bg(ACCENT)
2835
- .add_modifier(Modifier::BOLD),
2836
- ),
2837
- ]))
2838
- .alignment(Alignment::Right);
3115
+ let action_hint = if is_f0_focused {
3116
+ Paragraph::new(Line::from(vec![
3117
+ Span::styled(" [ Esc: Back ] ", Style::default().fg(MUTED)),
3118
+ Span::raw(" "),
3119
+ Span::styled(
3120
+ " [ Enter / Tab: Next Field (Select Model) ➔ ] ",
3121
+ Style::default().fg(TEXT),
3122
+ ),
3123
+ ]))
3124
+ .alignment(Alignment::Right)
3125
+ } else {
3126
+ Paragraph::new(Line::from(vec![
3127
+ Span::styled(" [ Tab: Back to Key ] ", Style::default().fg(MUTED)),
3128
+ Span::raw(" "),
3129
+ Span::styled(
3130
+ next_label,
3131
+ Style::default()
3132
+ .fg(SELECTION_TEXT)
3133
+ .bg(SELECTION_BACKGROUND)
3134
+ .add_modifier(Modifier::BOLD),
3135
+ ),
3136
+ ]))
3137
+ .alignment(Alignment::Right)
3138
+ };
2839
3139
  frame.render_widget(action_hint, chunks[5]);
2840
3140
  return;
2841
3141
  } else {
@@ -2862,19 +3162,24 @@ impl OnboardWizard {
2862
3162
  frame.render_widget(subtitle, chunks[0]);
2863
3163
 
2864
3164
  for (slot, (f, idx_rel)) in [(&f0, 0), (&f1, 1)].into_iter().enumerate() {
2865
- let val_disp = if f.value.is_empty() {
2866
- Span::styled(
3165
+ let is_active = focused_rel == idx_rel;
3166
+ let mut val_spans = Vec::new();
3167
+ if f.value.is_empty() {
3168
+ val_spans.push(Span::styled(
2867
3169
  format!("(Type or paste {} here)", f.label),
2868
3170
  Style::default().fg(MUTED),
2869
- )
3171
+ ));
2870
3172
  } else if f.is_secret && !f.show_secret {
2871
- Span::styled(
3173
+ val_spans.push(Span::styled(
2872
3174
  "•".repeat(f.value.chars().count().min(50)),
2873
3175
  Style::default().fg(TEXT),
2874
- )
3176
+ ));
2875
3177
  } else {
2876
- Span::styled(&f.value, Style::default().fg(TEXT))
2877
- };
3178
+ val_spans.push(Span::styled(&f.value, Style::default().fg(TEXT)));
3179
+ }
3180
+ if is_active {
3181
+ val_spans.push(Span::styled("█", Style::default().fg(TEXT)));
3182
+ }
2878
3183
 
2879
3184
  let mask_badge = if f.is_secret {
2880
3185
  if f.show_secret {
@@ -2886,28 +3191,52 @@ impl OnboardWizard {
2886
3191
  ""
2887
3192
  };
2888
3193
 
2889
- let border_color = if focused_rel == idx_rel {
2890
- ACCENT
2891
- } else {
2892
- SUBTLE
2893
- };
2894
- let b = Block::default()
2895
- .borders(Borders::ALL)
2896
- .border_type(BorderType::Rounded)
2897
- .border_style(Style::default().fg(border_color))
2898
- .title(Line::from(vec![
3194
+ let border_color = if is_active { TEXT } else { SUBTLE };
3195
+
3196
+ let title = if is_active {
3197
+ Line::from(vec![
2899
3198
  Span::styled(
2900
3199
  format!(" {} ", f.label),
3200
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3201
+ ),
3202
+ Span::styled(
3203
+ " [ EDITING ] ",
2901
3204
  Style::default()
2902
- .fg(if focused_rel == idx_rel { ACCENT } else { TEXT })
3205
+ .fg(SELECTION_TEXT)
3206
+ .bg(SELECTION_BACKGROUND)
2903
3207
  .add_modifier(Modifier::BOLD),
2904
3208
  ),
3209
+ Span::raw(" "),
2905
3210
  Span::styled(mask_badge, Style::default().fg(BLUE)),
2906
- ]));
3211
+ ])
3212
+ } else if !f.value.is_empty() {
3213
+ Line::from(vec![
3214
+ Span::styled(
3215
+ format!(" ✔ {} (Configured · Tab to edit) ", f.label),
3216
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3217
+ ),
3218
+ Span::styled(mask_badge, Style::default().fg(MUTED)),
3219
+ ])
3220
+ } else {
3221
+ Line::from(vec![
3222
+ Span::styled(
3223
+ format!(" {} (Tab to edit) ", f.label),
3224
+ Style::default().fg(MUTED),
3225
+ ),
3226
+ Span::styled(mask_badge, Style::default().fg(MUTED)),
3227
+ ])
3228
+ };
3229
+
3230
+ let b = Block::default()
3231
+ .borders(Borders::ALL)
3232
+ .border_type(BorderType::Rounded)
3233
+ .border_style(Style::default().fg(border_color))
3234
+ .title(title);
2907
3235
 
2908
3236
  let chunk_input = if slot == 0 { chunks[1] } else { chunks[3] };
2909
3237
  let chunk_hint = if slot == 0 { chunks[2] } else { chunks[4] };
2910
- frame.render_widget(Paragraph::new(val_disp).block(b), chunk_input);
3238
+ frame
3239
+ .render_widget(Paragraph::new(Line::from(val_spans)).block(b), chunk_input);
2911
3240
  frame.render_widget(
2912
3241
  Paragraph::new(format!(" Example / Hint: {}", f.hint))
2913
3242
  .style(Style::default().fg(MUTED)),
@@ -2921,18 +3250,27 @@ impl OnboardWizard {
2921
3250
  " [ Enter: Continue to Finish ➔ ] "
2922
3251
  };
2923
3252
 
2924
- let action_hint = Paragraph::new(Line::from(vec![
2925
- Span::styled(" [ Esc: Back ] ", Style::default().fg(MUTED)),
2926
- Span::raw(" "),
2927
- Span::styled(
2928
- next_label,
2929
- Style::default()
2930
- .fg(Color::Black)
2931
- .bg(ACCENT)
2932
- .add_modifier(Modifier::BOLD),
2933
- ),
2934
- ]))
2935
- .alignment(Alignment::Right);
3253
+ let action_hint = if focused_rel == 0 {
3254
+ Paragraph::new(Line::from(vec![
3255
+ Span::styled(" [ Esc: Back ] ", Style::default().fg(MUTED)),
3256
+ Span::raw(" "),
3257
+ Span::styled(" [ Enter / Tab: Next Field ➔ ] ", Style::default().fg(TEXT)),
3258
+ ]))
3259
+ .alignment(Alignment::Right)
3260
+ } else {
3261
+ Paragraph::new(Line::from(vec![
3262
+ Span::styled(" [ Tab: Back to Field 1 ] ", Style::default().fg(MUTED)),
3263
+ Span::raw(" "),
3264
+ Span::styled(
3265
+ next_label,
3266
+ Style::default()
3267
+ .fg(SELECTION_TEXT)
3268
+ .bg(SELECTION_BACKGROUND)
3269
+ .add_modifier(Modifier::BOLD),
3270
+ ),
3271
+ ]))
3272
+ .alignment(Alignment::Right)
3273
+ };
2936
3274
  frame.render_widget(action_hint, chunks[6]);
2937
3275
  return;
2938
3276
  }
@@ -2966,40 +3304,68 @@ impl OnboardWizard {
2966
3304
  .map(|(i, opt)| {
2967
3305
  let is_sel = i == *selected;
2968
3306
  let radio = if is_sel { " ◉ " } else { " ○ " };
2969
- let cursor = if is_sel { " ❯ " } else { " " };
3307
+ let cursor = if is_sel { " › " } else { " " };
2970
3308
 
2971
- let name_style = if is_sel {
2972
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD)
3309
+ if is_sel {
3310
+ let line = Line::from(vec![
3311
+ Span::styled(
3312
+ cursor,
3313
+ Style::default()
3314
+ .fg(SELECTION_TEXT)
3315
+ .bg(SELECTION_BACKGROUND)
3316
+ .add_modifier(Modifier::BOLD),
3317
+ ),
3318
+ Span::styled(
3319
+ radio,
3320
+ Style::default()
3321
+ .fg(SELECTION_TEXT)
3322
+ .bg(SELECTION_BACKGROUND)
3323
+ .add_modifier(Modifier::BOLD),
3324
+ ),
3325
+ Span::styled(
3326
+ opt,
3327
+ Style::default()
3328
+ .fg(SELECTION_TEXT)
3329
+ .bg(SELECTION_BACKGROUND)
3330
+ .add_modifier(Modifier::BOLD),
3331
+ ),
3332
+ ]);
3333
+ ListItem::new(line).style(Style::default().bg(SELECTION_BACKGROUND))
2973
3334
  } else {
2974
- Style::default().fg(TEXT)
2975
- };
2976
-
2977
- let line = Line::from(vec![
2978
- Span::styled(cursor, Style::default().fg(ACCENT)),
2979
- Span::styled(
2980
- radio,
2981
- Style::default()
2982
- .fg(if is_sel { ACCENT } else { MUTED })
2983
- .add_modifier(Modifier::BOLD),
2984
- ),
2985
- Span::styled(opt, name_style),
2986
- ]);
2987
- ListItem::new(line)
3335
+ let line = Line::from(vec![
3336
+ Span::raw(" "),
3337
+ Span::styled(radio, Style::default().fg(MUTED)),
3338
+ Span::styled(opt, Style::default().fg(TEXT)),
3339
+ ]);
3340
+ ListItem::new(line)
3341
+ }
2988
3342
  })
2989
3343
  .collect();
2990
3344
 
2991
3345
  let list_block = Block::default()
2992
3346
  .borders(Borders::ALL)
2993
3347
  .border_type(BorderType::Rounded)
2994
- .border_style(Style::default().fg(ACCENT))
2995
- .title(Span::styled(
2996
- format!(
2997
- " Default {} (↑/↓ to select) [✓ {} models loaded] ",
2998
- f0.label,
2999
- options.len().saturating_sub(1).max(1)
3348
+ .border_style(Style::default().fg(TEXT))
3349
+ .title(Line::from(vec![
3350
+ Span::styled(
3351
+ format!(" Default {} ", f0.label),
3352
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3000
3353
  ),
3001
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
3002
- ));
3354
+ Span::styled(
3355
+ " [ SELECTING ] ",
3356
+ Style::default()
3357
+ .fg(SELECTION_TEXT)
3358
+ .bg(SELECTION_BACKGROUND)
3359
+ .add_modifier(Modifier::BOLD),
3360
+ ),
3361
+ Span::styled(
3362
+ format!(
3363
+ " (↑/↓ to select) [✓ {} models loaded] ",
3364
+ options.len().saturating_sub(1).max(1)
3365
+ ),
3366
+ Style::default().fg(MUTED),
3367
+ ),
3368
+ ]));
3003
3369
 
3004
3370
  let mut list_state = ListState::default();
3005
3371
  list_state.select(Some(*selected));
@@ -3013,17 +3379,35 @@ impl OnboardWizard {
3013
3379
  let custom_block = Block::default()
3014
3380
  .borders(Borders::ALL)
3015
3381
  .border_type(BorderType::Rounded)
3016
- .border_style(Style::default().fg(ACCENT))
3017
- .title(Span::styled(
3018
- format!(" Custom {} Identifier ", f0.label),
3019
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
3382
+ .border_style(Style::default().fg(TEXT))
3383
+ .title(Line::from(vec![
3384
+ Span::styled(
3385
+ format!(" Custom {} Identifier ", f0.label),
3386
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3387
+ ),
3388
+ Span::styled(
3389
+ " [ EDITING ] ",
3390
+ Style::default()
3391
+ .fg(SELECTION_TEXT)
3392
+ .bg(SELECTION_BACKGROUND)
3393
+ .add_modifier(Modifier::BOLD),
3394
+ ),
3395
+ ]));
3396
+ let mut custom_spans = Vec::new();
3397
+ if f0.custom_value.is_empty() {
3398
+ custom_spans.push(Span::styled(
3399
+ "(Type custom model identifier)",
3400
+ Style::default().fg(MUTED),
3020
3401
  ));
3021
- let custom_val = if f0.custom_value.is_empty() {
3022
- Span::styled("(Type custom model identifier)", Style::default().fg(MUTED))
3023
3402
  } else {
3024
- Span::styled(&f0.custom_value, Style::default().fg(TEXT))
3025
- };
3026
- frame.render_widget(Paragraph::new(custom_val).block(custom_block), chunks[2]);
3403
+ custom_spans
3404
+ .push(Span::styled(&f0.custom_value, Style::default().fg(TEXT)));
3405
+ }
3406
+ custom_spans.push(Span::styled("█", Style::default().fg(TEXT)));
3407
+ frame.render_widget(
3408
+ Paragraph::new(Line::from(custom_spans)).block(custom_block),
3409
+ chunks[2],
3410
+ );
3027
3411
  }
3028
3412
 
3029
3413
  let next_label = if curr_idx < total_services {
@@ -3038,8 +3422,8 @@ impl OnboardWizard {
3038
3422
  Span::styled(
3039
3423
  next_label,
3040
3424
  Style::default()
3041
- .fg(Color::Black)
3042
- .bg(ACCENT)
3425
+ .fg(SELECTION_TEXT)
3426
+ .bg(SELECTION_BACKGROUND)
3043
3427
  .add_modifier(Modifier::BOLD),
3044
3428
  ),
3045
3429
  ]))
@@ -3066,19 +3450,21 @@ impl OnboardWizard {
3066
3450
  .style(Style::default().fg(MUTED));
3067
3451
  frame.render_widget(subtitle, chunks[0]);
3068
3452
 
3069
- let val_disp = if f0.value.is_empty() {
3070
- Span::styled(
3453
+ let mut val_spans = Vec::new();
3454
+ if f0.value.is_empty() {
3455
+ val_spans.push(Span::styled(
3071
3456
  format!("(Type or paste {} here)", f0.label),
3072
3457
  Style::default().fg(MUTED),
3073
- )
3458
+ ));
3074
3459
  } else if f0.is_secret && !f0.show_secret {
3075
- Span::styled(
3460
+ val_spans.push(Span::styled(
3076
3461
  "•".repeat(f0.value.chars().count().min(50)),
3077
3462
  Style::default().fg(TEXT),
3078
- )
3463
+ ));
3079
3464
  } else {
3080
- Span::styled(&f0.value, Style::default().fg(TEXT))
3081
- };
3465
+ val_spans.push(Span::styled(&f0.value, Style::default().fg(TEXT)));
3466
+ }
3467
+ val_spans.push(Span::styled("█", Style::default().fg(TEXT)));
3082
3468
 
3083
3469
  let mask_badge = if f0.is_secret {
3084
3470
  if f0.show_secret {
@@ -3093,16 +3479,27 @@ impl OnboardWizard {
3093
3479
  let f0_block = Block::default()
3094
3480
  .borders(Borders::ALL)
3095
3481
  .border_type(BorderType::Rounded)
3096
- .border_style(Style::default().fg(ACCENT))
3482
+ .border_style(Style::default().fg(TEXT))
3097
3483
  .title(Line::from(vec![
3098
3484
  Span::styled(
3099
3485
  format!(" {} ", f0.label),
3100
- Style::default().fg(ACCENT).add_modifier(Modifier::BOLD),
3486
+ Style::default().fg(TEXT).add_modifier(Modifier::BOLD),
3487
+ ),
3488
+ Span::styled(
3489
+ " [ EDITING ] ",
3490
+ Style::default()
3491
+ .fg(SELECTION_TEXT)
3492
+ .bg(SELECTION_BACKGROUND)
3493
+ .add_modifier(Modifier::BOLD),
3101
3494
  ),
3495
+ Span::raw(" "),
3102
3496
  Span::styled(mask_badge, Style::default().fg(BLUE)),
3103
3497
  ]));
3104
3498
 
3105
- frame.render_widget(Paragraph::new(val_disp).block(f0_block), chunks[1]);
3499
+ frame.render_widget(
3500
+ Paragraph::new(Line::from(val_spans)).block(f0_block),
3501
+ chunks[1],
3502
+ );
3106
3503
  frame.render_widget(
3107
3504
  Paragraph::new(format!("Example / Hint: {}", f0.hint))
3108
3505
  .style(Style::default().fg(MUTED)),
@@ -3121,8 +3518,8 @@ impl OnboardWizard {
3121
3518
  Span::styled(
3122
3519
  next_label,
3123
3520
  Style::default()
3124
- .fg(Color::Black)
3125
- .bg(ACCENT)
3521
+ .fg(SELECTION_TEXT)
3522
+ .bg(SELECTION_BACKGROUND)
3126
3523
  .add_modifier(Modifier::BOLD),
3127
3524
  ),
3128
3525
  ]))
@@ -3275,30 +3672,30 @@ impl OnboardWizard {
3275
3672
  Span::styled(
3276
3673
  " [ Save & Launch Mint ] ",
3277
3674
  Style::default()
3278
- .fg(Color::Black)
3279
- .bg(ACCENT)
3675
+ .fg(SELECTION_TEXT)
3676
+ .bg(SELECTION_BACKGROUND)
3280
3677
  .add_modifier(Modifier::BOLD),
3281
3678
  )
3282
3679
  } else {
3283
- Span::styled(" [ Save & Launch Mint ] ", Style::default().fg(ACCENT))
3680
+ Span::styled(" [ Save & Launch Mint ] ", Style::default().fg(TEXT))
3284
3681
  };
3285
3682
  let btn_exit = if self.summary_action == 1 {
3286
3683
  Span::styled(
3287
3684
  " [ Save & Exit ] ",
3288
3685
  Style::default()
3289
- .fg(Color::Black)
3290
- .bg(BLUE)
3686
+ .fg(SELECTION_TEXT)
3687
+ .bg(SELECTION_BACKGROUND)
3291
3688
  .add_modifier(Modifier::BOLD),
3292
3689
  )
3293
3690
  } else {
3294
- Span::styled(" [ Save & Exit ] ", Style::default().fg(BLUE))
3691
+ Span::styled(" [ Save & Exit ] ", Style::default().fg(MUTED))
3295
3692
  };
3296
3693
  let btn_back = if self.summary_action == 2 {
3297
3694
  Span::styled(
3298
3695
  " [ ⬅ Back to Edit ] ",
3299
3696
  Style::default()
3300
- .fg(Color::Black)
3301
- .bg(SUBTLE)
3697
+ .fg(SELECTION_TEXT)
3698
+ .bg(SELECTION_BACKGROUND)
3302
3699
  .add_modifier(Modifier::BOLD),
3303
3700
  )
3304
3701
  } else {
@@ -3319,15 +3716,36 @@ impl OnboardWizard {
3319
3716
  fn render_footer(&self, frame: &mut ratatui::Frame, area: Rect) {
3320
3717
  let shortcuts = match self.step {
3321
3718
  OnboardStep::CoreAi => {
3322
- "[Tab] Switch Field [↑/↓] Select Model [Ctrl+H] Mask [Enter] Continue [Esc] Cancel"
3719
+ if self.gemini_focus == 0 {
3720
+ "Focus: [API Key] · Type to edit · [Ctrl+V] Paste · [Enter/Tab] Model List · [Ctrl+H] Mask · [Esc] Cancel"
3721
+ } else {
3722
+ "Focus: [Model List] · [↑/↓] Select Model · [Enter] Continue · [Tab] API Key · [Esc] Cancel"
3723
+ }
3323
3724
  }
3324
3725
  OnboardStep::Services => {
3325
- "[↑/↓] Move [Space] Toggle [a] All [i] Invert [Enter] Next [Esc] Back"
3726
+ "Focus: [Services Checklist] · [↑/↓] Move · [Space] Toggle · [a] All · [i] Invert · [Enter] Next · [Esc] Back"
3326
3727
  }
3327
3728
  OnboardStep::Config => {
3328
- "[Tab] Switch Field [↑/↓] Select Model [Ctrl+H] Mask [Enter] Continue [Esc] Back"
3729
+ if self.service_groups.is_empty() {
3730
+ "[Enter] Continue to Finish · [Esc] Back"
3731
+ } else {
3732
+ let group = &self.service_groups[self.service_group_idx];
3733
+ let focused_rel = group.focused_field;
3734
+ let field_idx = group.field_indices.get(focused_rel).copied().unwrap_or(0);
3735
+ let is_choice = matches!(
3736
+ self.fields.get(field_idx).map(|f| &f.field_type),
3737
+ Some(FieldType::Choice { .. })
3738
+ );
3739
+ if is_choice {
3740
+ "Focus: [Model List] · [↑/↓] Select Model · [Enter] Next · [Tab] Key · [Esc] Back"
3741
+ } else {
3742
+ "Focus: [Input Field] · Type to edit · [Tab] Next Field · [Enter] Next · [Ctrl+H] Mask · [Esc] Back"
3743
+ }
3744
+ }
3745
+ }
3746
+ OnboardStep::Summary => {
3747
+ "Focus: [Action Selection] · [←/→ / Tab] Select Action · [Enter] Confirm · [Esc] Back"
3329
3748
  }
3330
- OnboardStep::Summary => "[←/→ / Tab] Select Action [Enter] Confirm [Esc] Back",
3331
3749
  };
3332
3750
 
3333
3751
  let chunks = Layout::default()
@@ -3340,7 +3758,7 @@ impl OnboardWizard {
3340
3758
  chunks[0],
3341
3759
  );
3342
3760
  frame.render_widget(
3343
- Paragraph::new("Mint v1.17.0 ")
3761
+ Paragraph::new("Mint v1.18.0 ")
3344
3762
  .style(Style::default().fg(SUBTLE))
3345
3763
  .alignment(Alignment::Right),
3346
3764
  chunks[1],