nastech-app 1.0.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 (1111) hide show
  1. package/.claude/agents/i18n-translator.md +119 -0
  2. package/.claude/settings.json +8 -0
  3. package/.eas/workflows/ota.yaml +9 -0
  4. package/.eas/workflows/preview.yaml +12 -0
  5. package/.easignore +12 -0
  6. package/.github/workflows/eas-build.yml +24 -0
  7. package/CHANGELOG.md +117 -0
  8. package/CLAUDE.md +413 -0
  9. package/GoogleService-Info.plist +30 -0
  10. package/LICENSE +21 -0
  11. package/NasTechapp.md +383 -0
  12. package/README.md +75 -0
  13. package/Stores.md +85 -0
  14. package/TERMS.md +83 -0
  15. package/app.config.js +153 -0
  16. package/babel.config.js +28 -0
  17. package/deploy/nastech-app.yaml +51 -0
  18. package/docs/marketing/README-creators.md +73 -0
  19. package/eas-build-post-install.sh +11 -0
  20. package/eas-build-pre-install.sh +27 -0
  21. package/eas.json +78 -0
  22. package/google-services.json +67 -0
  23. package/index.ts +3 -0
  24. package/logo.png +0 -0
  25. package/metro.config.js +54 -0
  26. package/nativewind-env.d.ts +1 -0
  27. package/package.json +233 -0
  28. package/patches/.keep +0 -0
  29. package/plugins/withEinkCompatibility.js +156 -0
  30. package/public/.well-known/apple-app-site-association +22 -0
  31. package/public/.well-known/assetlinks.json +12 -0
  32. package/public/canvaskit.wasm +0 -0
  33. package/public/favicon-active.ico +0 -0
  34. package/release-dev.sh +7 -0
  35. package/release-production.sh +3 -0
  36. package/release.cjs +160 -0
  37. package/sources/-session/SessionView.tsx +944 -0
  38. package/sources/-session/sessionOverlayNav.ts +34 -0
  39. package/sources/app/(app)/_layout.tsx +321 -0
  40. package/sources/app/(app)/artifacts/[id].tsx +279 -0
  41. package/sources/app/(app)/artifacts/edit/[id].tsx +318 -0
  42. package/sources/app/(app)/artifacts/index.tsx +264 -0
  43. package/sources/app/(app)/artifacts/new.tsx +219 -0
  44. package/sources/app/(app)/changelog.tsx +113 -0
  45. package/sources/app/(app)/dev/colors.tsx +197 -0
  46. package/sources/app/(app)/dev/device-info.tsx +183 -0
  47. package/sources/app/(app)/dev/expo-constants.tsx +394 -0
  48. package/sources/app/(app)/dev/index.tsx +400 -0
  49. package/sources/app/(app)/dev/input-styles.tsx +1951 -0
  50. package/sources/app/(app)/dev/inverted-list.tsx +295 -0
  51. package/sources/app/(app)/dev/list-demo.tsx +125 -0
  52. package/sources/app/(app)/dev/logs.tsx +160 -0
  53. package/sources/app/(app)/dev/messages-demo-data.ts +479 -0
  54. package/sources/app/(app)/dev/messages-demo.tsx +45 -0
  55. package/sources/app/(app)/dev/modal-demo.tsx +211 -0
  56. package/sources/app/(app)/dev/multi-text-input.tsx +224 -0
  57. package/sources/app/(app)/dev/purchases.tsx +228 -0
  58. package/sources/app/(app)/dev/qr-test.tsx +168 -0
  59. package/sources/app/(app)/dev/session-composer.tsx +812 -0
  60. package/sources/app/(app)/dev/shimmer-demo.tsx +275 -0
  61. package/sources/app/(app)/dev/tests.tsx +203 -0
  62. package/sources/app/(app)/dev/tools2.tsx +556 -0
  63. package/sources/app/(app)/dev/typography.tsx +177 -0
  64. package/sources/app/(app)/dev/unistyles-demo.tsx +376 -0
  65. package/sources/app/(app)/friends/index.tsx +167 -0
  66. package/sources/app/(app)/friends/search.tsx +232 -0
  67. package/sources/app/(app)/inbox/index.tsx +124 -0
  68. package/sources/app/(app)/index.tsx +264 -0
  69. package/sources/app/(app)/machine/[id].tsx +646 -0
  70. package/sources/app/(app)/new/index.tsx +1611 -0
  71. package/sources/app/(app)/restore/index.tsx +167 -0
  72. package/sources/app/(app)/restore/manual.tsx +138 -0
  73. package/sources/app/(app)/server.tsx +234 -0
  74. package/sources/app/(app)/session/[id]/file.tsx +527 -0
  75. package/sources/app/(app)/session/[id]/files.tsx +442 -0
  76. package/sources/app/(app)/session/[id]/info.tsx +655 -0
  77. package/sources/app/(app)/session/[id]/message/[messageId].tsx +125 -0
  78. package/sources/app/(app)/session/[id].tsx +10 -0
  79. package/sources/app/(app)/session/recent.tsx +270 -0
  80. package/sources/app/(app)/settings/account.tsx +600 -0
  81. package/sources/app/(app)/settings/agents.tsx +180 -0
  82. package/sources/app/(app)/settings/appearance.tsx +259 -0
  83. package/sources/app/(app)/settings/connect/claude.tsx +178 -0
  84. package/sources/app/(app)/settings/features.tsx +177 -0
  85. package/sources/app/(app)/settings/index.tsx +3 -0
  86. package/sources/app/(app)/settings/language.tsx +106 -0
  87. package/sources/app/(app)/settings/usage.tsx +11 -0
  88. package/sources/app/(app)/settings/voice/language.tsx +114 -0
  89. package/sources/app/(app)/settings/voice.tsx +274 -0
  90. package/sources/app/(app)/terminal/connect.tsx +241 -0
  91. package/sources/app/(app)/terminal/index.tsx +184 -0
  92. package/sources/app/(app)/text-selection.tsx +149 -0
  93. package/sources/app/(app)/user/[id].tsx +314 -0
  94. package/sources/app/+html.tsx +39 -0
  95. package/sources/app/_layout.tsx +402 -0
  96. package/sources/assets/animations/game.json +1 -0
  97. package/sources/assets/animations/owl.json +1 -0
  98. package/sources/assets/animations/popcorn.json +1 -0
  99. package/sources/assets/animations/robot.json +1 -0
  100. package/sources/assets/animations/sparkles.json +1 -0
  101. package/sources/assets/animations/stone.json +1 -0
  102. package/sources/assets/fonts/BricolageGrotesque-Bold.ttf +0 -0
  103. package/sources/assets/fonts/IBMPlexMono-Italic.ttf +0 -0
  104. package/sources/assets/fonts/IBMPlexMono-Regular.ttf +0 -0
  105. package/sources/assets/fonts/IBMPlexMono-SemiBold.ttf +0 -0
  106. package/sources/assets/fonts/IBMPlexSans-Italic.ttf +0 -0
  107. package/sources/assets/fonts/IBMPlexSans-Regular.ttf +0 -0
  108. package/sources/assets/fonts/IBMPlexSans-SemiBold.ttf +0 -0
  109. package/sources/assets/fonts/SpaceMono-Regular.ttf +0 -0
  110. package/sources/assets/images/brutalist/Abstract-1.png +0 -0
  111. package/sources/assets/images/brutalist/Abstract-10.png +0 -0
  112. package/sources/assets/images/brutalist/Abstract-100.png +0 -0
  113. package/sources/assets/images/brutalist/Abstract-101.png +0 -0
  114. package/sources/assets/images/brutalist/Abstract-102.png +0 -0
  115. package/sources/assets/images/brutalist/Abstract-103.png +0 -0
  116. package/sources/assets/images/brutalist/Abstract-104.png +0 -0
  117. package/sources/assets/images/brutalist/Abstract-105.png +0 -0
  118. package/sources/assets/images/brutalist/Abstract-106.png +0 -0
  119. package/sources/assets/images/brutalist/Abstract-107.png +0 -0
  120. package/sources/assets/images/brutalist/Abstract-108.png +0 -0
  121. package/sources/assets/images/brutalist/Abstract-109.png +0 -0
  122. package/sources/assets/images/brutalist/Abstract-11.png +0 -0
  123. package/sources/assets/images/brutalist/Abstract-110.png +0 -0
  124. package/sources/assets/images/brutalist/Abstract-111.png +0 -0
  125. package/sources/assets/images/brutalist/Abstract-112.png +0 -0
  126. package/sources/assets/images/brutalist/Abstract-113.png +0 -0
  127. package/sources/assets/images/brutalist/Abstract-114.png +0 -0
  128. package/sources/assets/images/brutalist/Abstract-115.png +0 -0
  129. package/sources/assets/images/brutalist/Abstract-116.png +0 -0
  130. package/sources/assets/images/brutalist/Abstract-117.png +0 -0
  131. package/sources/assets/images/brutalist/Abstract-118.png +0 -0
  132. package/sources/assets/images/brutalist/Abstract-119.png +0 -0
  133. package/sources/assets/images/brutalist/Abstract-12.png +0 -0
  134. package/sources/assets/images/brutalist/Abstract-120.png +0 -0
  135. package/sources/assets/images/brutalist/Abstract-121.png +0 -0
  136. package/sources/assets/images/brutalist/Abstract-122.png +0 -0
  137. package/sources/assets/images/brutalist/Abstract-123.png +0 -0
  138. package/sources/assets/images/brutalist/Abstract-124.png +0 -0
  139. package/sources/assets/images/brutalist/Abstract-125.png +0 -0
  140. package/sources/assets/images/brutalist/Abstract-126.png +0 -0
  141. package/sources/assets/images/brutalist/Abstract-127.png +0 -0
  142. package/sources/assets/images/brutalist/Abstract-128.png +0 -0
  143. package/sources/assets/images/brutalist/Abstract-129.png +0 -0
  144. package/sources/assets/images/brutalist/Abstract-13.png +0 -0
  145. package/sources/assets/images/brutalist/Abstract-130.png +0 -0
  146. package/sources/assets/images/brutalist/Abstract-131.png +0 -0
  147. package/sources/assets/images/brutalist/Abstract-132.png +0 -0
  148. package/sources/assets/images/brutalist/Abstract-133.png +0 -0
  149. package/sources/assets/images/brutalist/Abstract-134.png +0 -0
  150. package/sources/assets/images/brutalist/Abstract-135.png +0 -0
  151. package/sources/assets/images/brutalist/Abstract-136.png +0 -0
  152. package/sources/assets/images/brutalist/Abstract-137.png +0 -0
  153. package/sources/assets/images/brutalist/Abstract-138.png +0 -0
  154. package/sources/assets/images/brutalist/Abstract-139.png +0 -0
  155. package/sources/assets/images/brutalist/Abstract-14.png +0 -0
  156. package/sources/assets/images/brutalist/Abstract-140.png +0 -0
  157. package/sources/assets/images/brutalist/Abstract-141.png +0 -0
  158. package/sources/assets/images/brutalist/Abstract-142.png +0 -0
  159. package/sources/assets/images/brutalist/Abstract-143.png +0 -0
  160. package/sources/assets/images/brutalist/Abstract-144.png +0 -0
  161. package/sources/assets/images/brutalist/Abstract-145.png +0 -0
  162. package/sources/assets/images/brutalist/Abstract-146.png +0 -0
  163. package/sources/assets/images/brutalist/Abstract-147.png +0 -0
  164. package/sources/assets/images/brutalist/Abstract-148.png +0 -0
  165. package/sources/assets/images/brutalist/Abstract-149.png +0 -0
  166. package/sources/assets/images/brutalist/Abstract-15.png +0 -0
  167. package/sources/assets/images/brutalist/Abstract-150.png +0 -0
  168. package/sources/assets/images/brutalist/Abstract-151.png +0 -0
  169. package/sources/assets/images/brutalist/Abstract-152.png +0 -0
  170. package/sources/assets/images/brutalist/Abstract-153.png +0 -0
  171. package/sources/assets/images/brutalist/Abstract-154.png +0 -0
  172. package/sources/assets/images/brutalist/Abstract-155.png +0 -0
  173. package/sources/assets/images/brutalist/Abstract-156.png +0 -0
  174. package/sources/assets/images/brutalist/Abstract-157.png +0 -0
  175. package/sources/assets/images/brutalist/Abstract-158.png +0 -0
  176. package/sources/assets/images/brutalist/Abstract-159.png +0 -0
  177. package/sources/assets/images/brutalist/Abstract-16.png +0 -0
  178. package/sources/assets/images/brutalist/Abstract-160.png +0 -0
  179. package/sources/assets/images/brutalist/Abstract-161.png +0 -0
  180. package/sources/assets/images/brutalist/Abstract-162.png +0 -0
  181. package/sources/assets/images/brutalist/Abstract-163.png +0 -0
  182. package/sources/assets/images/brutalist/Abstract-164.png +0 -0
  183. package/sources/assets/images/brutalist/Abstract-165.png +0 -0
  184. package/sources/assets/images/brutalist/Abstract-166.png +0 -0
  185. package/sources/assets/images/brutalist/Abstract-167.png +0 -0
  186. package/sources/assets/images/brutalist/Abstract-168.png +0 -0
  187. package/sources/assets/images/brutalist/Abstract-169.png +0 -0
  188. package/sources/assets/images/brutalist/Abstract-17.png +0 -0
  189. package/sources/assets/images/brutalist/Abstract-170.png +0 -0
  190. package/sources/assets/images/brutalist/Abstract-171.png +0 -0
  191. package/sources/assets/images/brutalist/Abstract-172.png +0 -0
  192. package/sources/assets/images/brutalist/Abstract-173.png +0 -0
  193. package/sources/assets/images/brutalist/Abstract-174.png +0 -0
  194. package/sources/assets/images/brutalist/Abstract-175.png +0 -0
  195. package/sources/assets/images/brutalist/Abstract-176.png +0 -0
  196. package/sources/assets/images/brutalist/Abstract-177.png +0 -0
  197. package/sources/assets/images/brutalist/Abstract-178.png +0 -0
  198. package/sources/assets/images/brutalist/Abstract-179.png +0 -0
  199. package/sources/assets/images/brutalist/Abstract-18.png +0 -0
  200. package/sources/assets/images/brutalist/Abstract-180.png +0 -0
  201. package/sources/assets/images/brutalist/Abstract-181.png +0 -0
  202. package/sources/assets/images/brutalist/Abstract-182.png +0 -0
  203. package/sources/assets/images/brutalist/Abstract-183.png +0 -0
  204. package/sources/assets/images/brutalist/Abstract-184.png +0 -0
  205. package/sources/assets/images/brutalist/Abstract-185.png +0 -0
  206. package/sources/assets/images/brutalist/Abstract-186.png +0 -0
  207. package/sources/assets/images/brutalist/Abstract-187.png +0 -0
  208. package/sources/assets/images/brutalist/Abstract-188.png +0 -0
  209. package/sources/assets/images/brutalist/Abstract-189.png +0 -0
  210. package/sources/assets/images/brutalist/Abstract-19.png +0 -0
  211. package/sources/assets/images/brutalist/Abstract-190.png +0 -0
  212. package/sources/assets/images/brutalist/Abstract-191.png +0 -0
  213. package/sources/assets/images/brutalist/Abstract-192.png +0 -0
  214. package/sources/assets/images/brutalist/Abstract-193.png +0 -0
  215. package/sources/assets/images/brutalist/Abstract-194.png +0 -0
  216. package/sources/assets/images/brutalist/Abstract-195.png +0 -0
  217. package/sources/assets/images/brutalist/Abstract-196.png +0 -0
  218. package/sources/assets/images/brutalist/Abstract-197.png +0 -0
  219. package/sources/assets/images/brutalist/Abstract-198.png +0 -0
  220. package/sources/assets/images/brutalist/Abstract-199.png +0 -0
  221. package/sources/assets/images/brutalist/Abstract-2.png +0 -0
  222. package/sources/assets/images/brutalist/Abstract-20.png +0 -0
  223. package/sources/assets/images/brutalist/Abstract-200.png +0 -0
  224. package/sources/assets/images/brutalist/Abstract-201.png +0 -0
  225. package/sources/assets/images/brutalist/Abstract-202.png +0 -0
  226. package/sources/assets/images/brutalist/Abstract-203.png +0 -0
  227. package/sources/assets/images/brutalist/Abstract-204.png +0 -0
  228. package/sources/assets/images/brutalist/Abstract-205.png +0 -0
  229. package/sources/assets/images/brutalist/Abstract-206.png +0 -0
  230. package/sources/assets/images/brutalist/Abstract-207.png +0 -0
  231. package/sources/assets/images/brutalist/Abstract-208.png +0 -0
  232. package/sources/assets/images/brutalist/Abstract-209.png +0 -0
  233. package/sources/assets/images/brutalist/Abstract-21.png +0 -0
  234. package/sources/assets/images/brutalist/Abstract-210.png +0 -0
  235. package/sources/assets/images/brutalist/Abstract-211.png +0 -0
  236. package/sources/assets/images/brutalist/Abstract-212.png +0 -0
  237. package/sources/assets/images/brutalist/Abstract-213.png +0 -0
  238. package/sources/assets/images/brutalist/Abstract-214.png +0 -0
  239. package/sources/assets/images/brutalist/Abstract-215.png +0 -0
  240. package/sources/assets/images/brutalist/Abstract-216.png +0 -0
  241. package/sources/assets/images/brutalist/Abstract-217.png +0 -0
  242. package/sources/assets/images/brutalist/Abstract-218.png +0 -0
  243. package/sources/assets/images/brutalist/Abstract-219.png +0 -0
  244. package/sources/assets/images/brutalist/Abstract-22.png +0 -0
  245. package/sources/assets/images/brutalist/Abstract-220.png +0 -0
  246. package/sources/assets/images/brutalist/Abstract-221.png +0 -0
  247. package/sources/assets/images/brutalist/Abstract-222.png +0 -0
  248. package/sources/assets/images/brutalist/Abstract-223.png +0 -0
  249. package/sources/assets/images/brutalist/Abstract-224.png +0 -0
  250. package/sources/assets/images/brutalist/Abstract-225.png +0 -0
  251. package/sources/assets/images/brutalist/Abstract-226.png +0 -0
  252. package/sources/assets/images/brutalist/Abstract-227.png +0 -0
  253. package/sources/assets/images/brutalist/Abstract-228.png +0 -0
  254. package/sources/assets/images/brutalist/Abstract-229.png +0 -0
  255. package/sources/assets/images/brutalist/Abstract-23.png +0 -0
  256. package/sources/assets/images/brutalist/Abstract-230.png +0 -0
  257. package/sources/assets/images/brutalist/Abstract-231.png +0 -0
  258. package/sources/assets/images/brutalist/Abstract-232.png +0 -0
  259. package/sources/assets/images/brutalist/Abstract-233.png +0 -0
  260. package/sources/assets/images/brutalist/Abstract-234.png +0 -0
  261. package/sources/assets/images/brutalist/Abstract-235.png +0 -0
  262. package/sources/assets/images/brutalist/Abstract-236.png +0 -0
  263. package/sources/assets/images/brutalist/Abstract-237.png +0 -0
  264. package/sources/assets/images/brutalist/Abstract-238.png +0 -0
  265. package/sources/assets/images/brutalist/Abstract-239.png +0 -0
  266. package/sources/assets/images/brutalist/Abstract-24.png +0 -0
  267. package/sources/assets/images/brutalist/Abstract-240.png +0 -0
  268. package/sources/assets/images/brutalist/Abstract-241.png +0 -0
  269. package/sources/assets/images/brutalist/Abstract-242.png +0 -0
  270. package/sources/assets/images/brutalist/Abstract-243.png +0 -0
  271. package/sources/assets/images/brutalist/Abstract-244.png +0 -0
  272. package/sources/assets/images/brutalist/Abstract-245.png +0 -0
  273. package/sources/assets/images/brutalist/Abstract-246.png +0 -0
  274. package/sources/assets/images/brutalist/Abstract-247.png +0 -0
  275. package/sources/assets/images/brutalist/Abstract-248.png +0 -0
  276. package/sources/assets/images/brutalist/Abstract-249.png +0 -0
  277. package/sources/assets/images/brutalist/Abstract-25.png +0 -0
  278. package/sources/assets/images/brutalist/Abstract-250.png +0 -0
  279. package/sources/assets/images/brutalist/Abstract-251.png +0 -0
  280. package/sources/assets/images/brutalist/Abstract-252.png +0 -0
  281. package/sources/assets/images/brutalist/Abstract-253.png +0 -0
  282. package/sources/assets/images/brutalist/Abstract-254.png +0 -0
  283. package/sources/assets/images/brutalist/Abstract-255.png +0 -0
  284. package/sources/assets/images/brutalist/Abstract-256.png +0 -0
  285. package/sources/assets/images/brutalist/Abstract-257.png +0 -0
  286. package/sources/assets/images/brutalist/Abstract-258.png +0 -0
  287. package/sources/assets/images/brutalist/Abstract-259.png +0 -0
  288. package/sources/assets/images/brutalist/Abstract-26.png +0 -0
  289. package/sources/assets/images/brutalist/Abstract-260.png +0 -0
  290. package/sources/assets/images/brutalist/Abstract-261.png +0 -0
  291. package/sources/assets/images/brutalist/Abstract-262.png +0 -0
  292. package/sources/assets/images/brutalist/Abstract-27.png +0 -0
  293. package/sources/assets/images/brutalist/Abstract-28.png +0 -0
  294. package/sources/assets/images/brutalist/Abstract-29.png +0 -0
  295. package/sources/assets/images/brutalist/Abstract-3.png +0 -0
  296. package/sources/assets/images/brutalist/Abstract-30.png +0 -0
  297. package/sources/assets/images/brutalist/Abstract-31.png +0 -0
  298. package/sources/assets/images/brutalist/Abstract-32.png +0 -0
  299. package/sources/assets/images/brutalist/Abstract-33.png +0 -0
  300. package/sources/assets/images/brutalist/Abstract-34.png +0 -0
  301. package/sources/assets/images/brutalist/Abstract-35.png +0 -0
  302. package/sources/assets/images/brutalist/Abstract-36.png +0 -0
  303. package/sources/assets/images/brutalist/Abstract-37.png +0 -0
  304. package/sources/assets/images/brutalist/Abstract-38.png +0 -0
  305. package/sources/assets/images/brutalist/Abstract-39.png +0 -0
  306. package/sources/assets/images/brutalist/Abstract-4.png +0 -0
  307. package/sources/assets/images/brutalist/Abstract-40.png +0 -0
  308. package/sources/assets/images/brutalist/Abstract-41.png +0 -0
  309. package/sources/assets/images/brutalist/Abstract-42.png +0 -0
  310. package/sources/assets/images/brutalist/Abstract-43.png +0 -0
  311. package/sources/assets/images/brutalist/Abstract-44.png +0 -0
  312. package/sources/assets/images/brutalist/Abstract-45.png +0 -0
  313. package/sources/assets/images/brutalist/Abstract-46.png +0 -0
  314. package/sources/assets/images/brutalist/Abstract-47.png +0 -0
  315. package/sources/assets/images/brutalist/Abstract-48.png +0 -0
  316. package/sources/assets/images/brutalist/Abstract-49.png +0 -0
  317. package/sources/assets/images/brutalist/Abstract-5.png +0 -0
  318. package/sources/assets/images/brutalist/Abstract-50.png +0 -0
  319. package/sources/assets/images/brutalist/Abstract-51.png +0 -0
  320. package/sources/assets/images/brutalist/Abstract-52.png +0 -0
  321. package/sources/assets/images/brutalist/Abstract-53.png +0 -0
  322. package/sources/assets/images/brutalist/Abstract-54.png +0 -0
  323. package/sources/assets/images/brutalist/Abstract-55.png +0 -0
  324. package/sources/assets/images/brutalist/Abstract-56.png +0 -0
  325. package/sources/assets/images/brutalist/Abstract-57.png +0 -0
  326. package/sources/assets/images/brutalist/Abstract-58.png +0 -0
  327. package/sources/assets/images/brutalist/Abstract-59.png +0 -0
  328. package/sources/assets/images/brutalist/Abstract-6.png +0 -0
  329. package/sources/assets/images/brutalist/Abstract-60.png +0 -0
  330. package/sources/assets/images/brutalist/Abstract-61.png +0 -0
  331. package/sources/assets/images/brutalist/Abstract-62.png +0 -0
  332. package/sources/assets/images/brutalist/Abstract-63.png +0 -0
  333. package/sources/assets/images/brutalist/Abstract-64.png +0 -0
  334. package/sources/assets/images/brutalist/Abstract-65.png +0 -0
  335. package/sources/assets/images/brutalist/Abstract-66.png +0 -0
  336. package/sources/assets/images/brutalist/Abstract-67.png +0 -0
  337. package/sources/assets/images/brutalist/Abstract-68.png +0 -0
  338. package/sources/assets/images/brutalist/Abstract-69.png +0 -0
  339. package/sources/assets/images/brutalist/Abstract-7.png +0 -0
  340. package/sources/assets/images/brutalist/Abstract-70.png +0 -0
  341. package/sources/assets/images/brutalist/Abstract-71.png +0 -0
  342. package/sources/assets/images/brutalist/Abstract-72.png +0 -0
  343. package/sources/assets/images/brutalist/Abstract-73.png +0 -0
  344. package/sources/assets/images/brutalist/Abstract-74.png +0 -0
  345. package/sources/assets/images/brutalist/Abstract-75.png +0 -0
  346. package/sources/assets/images/brutalist/Abstract-76.png +0 -0
  347. package/sources/assets/images/brutalist/Abstract-77.png +0 -0
  348. package/sources/assets/images/brutalist/Abstract-78.png +0 -0
  349. package/sources/assets/images/brutalist/Abstract-79.png +0 -0
  350. package/sources/assets/images/brutalist/Abstract-8.png +0 -0
  351. package/sources/assets/images/brutalist/Abstract-80.png +0 -0
  352. package/sources/assets/images/brutalist/Abstract-81.png +0 -0
  353. package/sources/assets/images/brutalist/Abstract-82.png +0 -0
  354. package/sources/assets/images/brutalist/Abstract-83.png +0 -0
  355. package/sources/assets/images/brutalist/Abstract-84.png +0 -0
  356. package/sources/assets/images/brutalist/Abstract-85.png +0 -0
  357. package/sources/assets/images/brutalist/Abstract-86.png +0 -0
  358. package/sources/assets/images/brutalist/Abstract-87.png +0 -0
  359. package/sources/assets/images/brutalist/Abstract-88.png +0 -0
  360. package/sources/assets/images/brutalist/Abstract-89.png +0 -0
  361. package/sources/assets/images/brutalist/Abstract-9.png +0 -0
  362. package/sources/assets/images/brutalist/Abstract-90.png +0 -0
  363. package/sources/assets/images/brutalist/Abstract-91.png +0 -0
  364. package/sources/assets/images/brutalist/Abstract-92.png +0 -0
  365. package/sources/assets/images/brutalist/Abstract-93.png +0 -0
  366. package/sources/assets/images/brutalist/Abstract-94.png +0 -0
  367. package/sources/assets/images/brutalist/Abstract-95.png +0 -0
  368. package/sources/assets/images/brutalist/Abstract-96.png +0 -0
  369. package/sources/assets/images/brutalist/Abstract-97.png +0 -0
  370. package/sources/assets/images/brutalist/Abstract-98.png +0 -0
  371. package/sources/assets/images/brutalist/Abstract-99.png +0 -0
  372. package/sources/assets/images/brutalist/Bauhaus-1.png +0 -0
  373. package/sources/assets/images/brutalist/Bauhaus-10.png +0 -0
  374. package/sources/assets/images/brutalist/Bauhaus-11.png +0 -0
  375. package/sources/assets/images/brutalist/Bauhaus-12.png +0 -0
  376. package/sources/assets/images/brutalist/Bauhaus-13.png +0 -0
  377. package/sources/assets/images/brutalist/Bauhaus-14.png +0 -0
  378. package/sources/assets/images/brutalist/Bauhaus-15.png +0 -0
  379. package/sources/assets/images/brutalist/Bauhaus-16.png +0 -0
  380. package/sources/assets/images/brutalist/Bauhaus-17.png +0 -0
  381. package/sources/assets/images/brutalist/Bauhaus-18.png +0 -0
  382. package/sources/assets/images/brutalist/Bauhaus-19.png +0 -0
  383. package/sources/assets/images/brutalist/Bauhaus-2.png +0 -0
  384. package/sources/assets/images/brutalist/Bauhaus-20.png +0 -0
  385. package/sources/assets/images/brutalist/Bauhaus-21.png +0 -0
  386. package/sources/assets/images/brutalist/Bauhaus-22.png +0 -0
  387. package/sources/assets/images/brutalist/Bauhaus-23.png +0 -0
  388. package/sources/assets/images/brutalist/Bauhaus-24.png +0 -0
  389. package/sources/assets/images/brutalist/Bauhaus-25.png +0 -0
  390. package/sources/assets/images/brutalist/Bauhaus-26.png +0 -0
  391. package/sources/assets/images/brutalist/Bauhaus-27.png +0 -0
  392. package/sources/assets/images/brutalist/Bauhaus-28.png +0 -0
  393. package/sources/assets/images/brutalist/Bauhaus-29.png +0 -0
  394. package/sources/assets/images/brutalist/Bauhaus-3.png +0 -0
  395. package/sources/assets/images/brutalist/Bauhaus-30.png +0 -0
  396. package/sources/assets/images/brutalist/Bauhaus-31.png +0 -0
  397. package/sources/assets/images/brutalist/Bauhaus-32.png +0 -0
  398. package/sources/assets/images/brutalist/Bauhaus-33.png +0 -0
  399. package/sources/assets/images/brutalist/Bauhaus-34.png +0 -0
  400. package/sources/assets/images/brutalist/Bauhaus-35.png +0 -0
  401. package/sources/assets/images/brutalist/Bauhaus-36.png +0 -0
  402. package/sources/assets/images/brutalist/Bauhaus-37.png +0 -0
  403. package/sources/assets/images/brutalist/Bauhaus-38.png +0 -0
  404. package/sources/assets/images/brutalist/Bauhaus-39.png +0 -0
  405. package/sources/assets/images/brutalist/Bauhaus-4.png +0 -0
  406. package/sources/assets/images/brutalist/Bauhaus-40.png +0 -0
  407. package/sources/assets/images/brutalist/Bauhaus-5.png +0 -0
  408. package/sources/assets/images/brutalist/Bauhaus-6.png +0 -0
  409. package/sources/assets/images/brutalist/Bauhaus-7.png +0 -0
  410. package/sources/assets/images/brutalist/Bauhaus-8.png +0 -0
  411. package/sources/assets/images/brutalist/Bauhaus-9.png +0 -0
  412. package/sources/assets/images/brutalist/Brutalism-1.png +0 -0
  413. package/sources/assets/images/brutalist/Brutalism-10.png +0 -0
  414. package/sources/assets/images/brutalist/Brutalism-100.png +0 -0
  415. package/sources/assets/images/brutalist/Brutalism-101.png +0 -0
  416. package/sources/assets/images/brutalist/Brutalism-102.png +0 -0
  417. package/sources/assets/images/brutalist/Brutalism-103.png +0 -0
  418. package/sources/assets/images/brutalist/Brutalism-104.png +0 -0
  419. package/sources/assets/images/brutalist/Brutalism-105.png +0 -0
  420. package/sources/assets/images/brutalist/Brutalism-106.png +0 -0
  421. package/sources/assets/images/brutalist/Brutalism-107.png +0 -0
  422. package/sources/assets/images/brutalist/Brutalism-108.png +0 -0
  423. package/sources/assets/images/brutalist/Brutalism-109.png +0 -0
  424. package/sources/assets/images/brutalist/Brutalism-11.png +0 -0
  425. package/sources/assets/images/brutalist/Brutalism-110.png +0 -0
  426. package/sources/assets/images/brutalist/Brutalism-111.png +0 -0
  427. package/sources/assets/images/brutalist/Brutalism-112.png +0 -0
  428. package/sources/assets/images/brutalist/Brutalism-113.png +0 -0
  429. package/sources/assets/images/brutalist/Brutalism-114.png +0 -0
  430. package/sources/assets/images/brutalist/Brutalism-115.png +0 -0
  431. package/sources/assets/images/brutalist/Brutalism-116.png +0 -0
  432. package/sources/assets/images/brutalist/Brutalism-117.png +0 -0
  433. package/sources/assets/images/brutalist/Brutalism-118.png +0 -0
  434. package/sources/assets/images/brutalist/Brutalism-12.png +0 -0
  435. package/sources/assets/images/brutalist/Brutalism-13.png +0 -0
  436. package/sources/assets/images/brutalist/Brutalism-14.png +0 -0
  437. package/sources/assets/images/brutalist/Brutalism-15.png +0 -0
  438. package/sources/assets/images/brutalist/Brutalism-16.png +0 -0
  439. package/sources/assets/images/brutalist/Brutalism-17.png +0 -0
  440. package/sources/assets/images/brutalist/Brutalism-18.png +0 -0
  441. package/sources/assets/images/brutalist/Brutalism-19.png +0 -0
  442. package/sources/assets/images/brutalist/Brutalism-2.png +0 -0
  443. package/sources/assets/images/brutalist/Brutalism-20.png +0 -0
  444. package/sources/assets/images/brutalist/Brutalism-21.png +0 -0
  445. package/sources/assets/images/brutalist/Brutalism-22.png +0 -0
  446. package/sources/assets/images/brutalist/Brutalism-23.png +0 -0
  447. package/sources/assets/images/brutalist/Brutalism-24.png +0 -0
  448. package/sources/assets/images/brutalist/Brutalism-25.png +0 -0
  449. package/sources/assets/images/brutalist/Brutalism-26.png +0 -0
  450. package/sources/assets/images/brutalist/Brutalism-27.png +0 -0
  451. package/sources/assets/images/brutalist/Brutalism-28.png +0 -0
  452. package/sources/assets/images/brutalist/Brutalism-29.png +0 -0
  453. package/sources/assets/images/brutalist/Brutalism-3.png +0 -0
  454. package/sources/assets/images/brutalist/Brutalism-30.png +0 -0
  455. package/sources/assets/images/brutalist/Brutalism-31.png +0 -0
  456. package/sources/assets/images/brutalist/Brutalism-32.png +0 -0
  457. package/sources/assets/images/brutalist/Brutalism-33.png +0 -0
  458. package/sources/assets/images/brutalist/Brutalism-34.png +0 -0
  459. package/sources/assets/images/brutalist/Brutalism-35.png +0 -0
  460. package/sources/assets/images/brutalist/Brutalism-36.png +0 -0
  461. package/sources/assets/images/brutalist/Brutalism-37.png +0 -0
  462. package/sources/assets/images/brutalist/Brutalism-38.png +0 -0
  463. package/sources/assets/images/brutalist/Brutalism-39.png +0 -0
  464. package/sources/assets/images/brutalist/Brutalism-4.png +0 -0
  465. package/sources/assets/images/brutalist/Brutalism-40.png +0 -0
  466. package/sources/assets/images/brutalist/Brutalism-41.png +0 -0
  467. package/sources/assets/images/brutalist/Brutalism-42.png +0 -0
  468. package/sources/assets/images/brutalist/Brutalism-43.png +0 -0
  469. package/sources/assets/images/brutalist/Brutalism-44.png +0 -0
  470. package/sources/assets/images/brutalist/Brutalism-45.png +0 -0
  471. package/sources/assets/images/brutalist/Brutalism-46.png +0 -0
  472. package/sources/assets/images/brutalist/Brutalism-47.png +0 -0
  473. package/sources/assets/images/brutalist/Brutalism-48.png +0 -0
  474. package/sources/assets/images/brutalist/Brutalism-49.png +0 -0
  475. package/sources/assets/images/brutalist/Brutalism-5.png +0 -0
  476. package/sources/assets/images/brutalist/Brutalism-50.png +0 -0
  477. package/sources/assets/images/brutalist/Brutalism-51.png +0 -0
  478. package/sources/assets/images/brutalist/Brutalism-52.png +0 -0
  479. package/sources/assets/images/brutalist/Brutalism-53.png +0 -0
  480. package/sources/assets/images/brutalist/Brutalism-54.png +0 -0
  481. package/sources/assets/images/brutalist/Brutalism-55.png +0 -0
  482. package/sources/assets/images/brutalist/Brutalism-56.png +0 -0
  483. package/sources/assets/images/brutalist/Brutalism-57.png +0 -0
  484. package/sources/assets/images/brutalist/Brutalism-58.png +0 -0
  485. package/sources/assets/images/brutalist/Brutalism-59.png +0 -0
  486. package/sources/assets/images/brutalist/Brutalism-6.png +0 -0
  487. package/sources/assets/images/brutalist/Brutalism-60.png +0 -0
  488. package/sources/assets/images/brutalist/Brutalism-61.png +0 -0
  489. package/sources/assets/images/brutalist/Brutalism-62.png +0 -0
  490. package/sources/assets/images/brutalist/Brutalism-63.png +0 -0
  491. package/sources/assets/images/brutalist/Brutalism-64.png +0 -0
  492. package/sources/assets/images/brutalist/Brutalism-65.png +0 -0
  493. package/sources/assets/images/brutalist/Brutalism-66.png +0 -0
  494. package/sources/assets/images/brutalist/Brutalism-67.png +0 -0
  495. package/sources/assets/images/brutalist/Brutalism-68.png +0 -0
  496. package/sources/assets/images/brutalist/Brutalism-69.png +0 -0
  497. package/sources/assets/images/brutalist/Brutalism-7.png +0 -0
  498. package/sources/assets/images/brutalist/Brutalism-70.png +0 -0
  499. package/sources/assets/images/brutalist/Brutalism-71.png +0 -0
  500. package/sources/assets/images/brutalist/Brutalism-72.png +0 -0
  501. package/sources/assets/images/brutalist/Brutalism-73.png +0 -0
  502. package/sources/assets/images/brutalist/Brutalism-74.png +0 -0
  503. package/sources/assets/images/brutalist/Brutalism-75.png +0 -0
  504. package/sources/assets/images/brutalist/Brutalism-76.png +0 -0
  505. package/sources/assets/images/brutalist/Brutalism-77.png +0 -0
  506. package/sources/assets/images/brutalist/Brutalism-78.png +0 -0
  507. package/sources/assets/images/brutalist/Brutalism-79.png +0 -0
  508. package/sources/assets/images/brutalist/Brutalism-8.png +0 -0
  509. package/sources/assets/images/brutalist/Brutalism-80.png +0 -0
  510. package/sources/assets/images/brutalist/Brutalism-81.png +0 -0
  511. package/sources/assets/images/brutalist/Brutalism-82.png +0 -0
  512. package/sources/assets/images/brutalist/Brutalism-83.png +0 -0
  513. package/sources/assets/images/brutalist/Brutalism-84.png +0 -0
  514. package/sources/assets/images/brutalist/Brutalism-85.png +0 -0
  515. package/sources/assets/images/brutalist/Brutalism-86.png +0 -0
  516. package/sources/assets/images/brutalist/Brutalism-87.png +0 -0
  517. package/sources/assets/images/brutalist/Brutalism-88.png +0 -0
  518. package/sources/assets/images/brutalist/Brutalism-89.png +0 -0
  519. package/sources/assets/images/brutalist/Brutalism-9.png +0 -0
  520. package/sources/assets/images/brutalist/Brutalism-90.png +0 -0
  521. package/sources/assets/images/brutalist/Brutalism-91.png +0 -0
  522. package/sources/assets/images/brutalist/Brutalism-92.png +0 -0
  523. package/sources/assets/images/brutalist/Brutalism-93.png +0 -0
  524. package/sources/assets/images/brutalist/Brutalism-94.png +0 -0
  525. package/sources/assets/images/brutalist/Brutalism-95.png +0 -0
  526. package/sources/assets/images/brutalist/Brutalism-96.png +0 -0
  527. package/sources/assets/images/brutalist/Brutalism-97.png +0 -0
  528. package/sources/assets/images/brutalist/Brutalism-98.png +0 -0
  529. package/sources/assets/images/brutalist/Brutalism-99.png +0 -0
  530. package/sources/assets/images/favicon-active.png +0 -0
  531. package/sources/assets/images/favicon.png +0 -0
  532. package/sources/assets/images/gradients/01.png +0 -0
  533. package/sources/assets/images/gradients/02.png +0 -0
  534. package/sources/assets/images/gradients/03.png +0 -0
  535. package/sources/assets/images/gradients/04.png +0 -0
  536. package/sources/assets/images/gradients/05.png +0 -0
  537. package/sources/assets/images/gradients/06.png +0 -0
  538. package/sources/assets/images/gradients/07.png +0 -0
  539. package/sources/assets/images/gradients/08.png +0 -0
  540. package/sources/assets/images/gradients/09.png +0 -0
  541. package/sources/assets/images/gradients/10.png +0 -0
  542. package/sources/assets/images/gradients/100.png +0 -0
  543. package/sources/assets/images/gradients/11.png +0 -0
  544. package/sources/assets/images/gradients/12.png +0 -0
  545. package/sources/assets/images/gradients/13.png +0 -0
  546. package/sources/assets/images/gradients/14.png +0 -0
  547. package/sources/assets/images/gradients/15.png +0 -0
  548. package/sources/assets/images/gradients/16.png +0 -0
  549. package/sources/assets/images/gradients/17.png +0 -0
  550. package/sources/assets/images/gradients/18.png +0 -0
  551. package/sources/assets/images/gradients/19.png +0 -0
  552. package/sources/assets/images/gradients/20.png +0 -0
  553. package/sources/assets/images/gradients/21.png +0 -0
  554. package/sources/assets/images/gradients/22.png +0 -0
  555. package/sources/assets/images/gradients/23.png +0 -0
  556. package/sources/assets/images/gradients/24.png +0 -0
  557. package/sources/assets/images/gradients/25.png +0 -0
  558. package/sources/assets/images/gradients/26.png +0 -0
  559. package/sources/assets/images/gradients/27.png +0 -0
  560. package/sources/assets/images/gradients/28.png +0 -0
  561. package/sources/assets/images/gradients/29.png +0 -0
  562. package/sources/assets/images/gradients/30.png +0 -0
  563. package/sources/assets/images/gradients/31.png +0 -0
  564. package/sources/assets/images/gradients/32.png +0 -0
  565. package/sources/assets/images/gradients/33.png +0 -0
  566. package/sources/assets/images/gradients/34.png +0 -0
  567. package/sources/assets/images/gradients/35.png +0 -0
  568. package/sources/assets/images/gradients/36.png +0 -0
  569. package/sources/assets/images/gradients/37.png +0 -0
  570. package/sources/assets/images/gradients/38.png +0 -0
  571. package/sources/assets/images/gradients/39.png +0 -0
  572. package/sources/assets/images/gradients/40.png +0 -0
  573. package/sources/assets/images/gradients/41.png +0 -0
  574. package/sources/assets/images/gradients/42.png +0 -0
  575. package/sources/assets/images/gradients/43.png +0 -0
  576. package/sources/assets/images/gradients/44.png +0 -0
  577. package/sources/assets/images/gradients/45.png +0 -0
  578. package/sources/assets/images/gradients/46.png +0 -0
  579. package/sources/assets/images/gradients/47.png +0 -0
  580. package/sources/assets/images/gradients/48.png +0 -0
  581. package/sources/assets/images/gradients/49.png +0 -0
  582. package/sources/assets/images/gradients/50.png +0 -0
  583. package/sources/assets/images/gradients/51.png +0 -0
  584. package/sources/assets/images/gradients/52.png +0 -0
  585. package/sources/assets/images/gradients/53.png +0 -0
  586. package/sources/assets/images/gradients/54.png +0 -0
  587. package/sources/assets/images/gradients/55.png +0 -0
  588. package/sources/assets/images/gradients/56.png +0 -0
  589. package/sources/assets/images/gradients/57.png +0 -0
  590. package/sources/assets/images/gradients/58.png +0 -0
  591. package/sources/assets/images/gradients/59.png +0 -0
  592. package/sources/assets/images/gradients/60.png +0 -0
  593. package/sources/assets/images/gradients/61.png +0 -0
  594. package/sources/assets/images/gradients/62.png +0 -0
  595. package/sources/assets/images/gradients/63.png +0 -0
  596. package/sources/assets/images/gradients/64.png +0 -0
  597. package/sources/assets/images/gradients/65.png +0 -0
  598. package/sources/assets/images/gradients/66.png +0 -0
  599. package/sources/assets/images/gradients/67.png +0 -0
  600. package/sources/assets/images/gradients/68.png +0 -0
  601. package/sources/assets/images/gradients/69.png +0 -0
  602. package/sources/assets/images/gradients/70.png +0 -0
  603. package/sources/assets/images/gradients/71.png +0 -0
  604. package/sources/assets/images/gradients/72.png +0 -0
  605. package/sources/assets/images/gradients/73.png +0 -0
  606. package/sources/assets/images/gradients/74.png +0 -0
  607. package/sources/assets/images/gradients/75.png +0 -0
  608. package/sources/assets/images/gradients/76.png +0 -0
  609. package/sources/assets/images/gradients/77.png +0 -0
  610. package/sources/assets/images/gradients/78.png +0 -0
  611. package/sources/assets/images/gradients/79.png +0 -0
  612. package/sources/assets/images/gradients/80.png +0 -0
  613. package/sources/assets/images/gradients/81.png +0 -0
  614. package/sources/assets/images/gradients/82.png +0 -0
  615. package/sources/assets/images/gradients/83.png +0 -0
  616. package/sources/assets/images/gradients/84.png +0 -0
  617. package/sources/assets/images/gradients/85.png +0 -0
  618. package/sources/assets/images/gradients/86.png +0 -0
  619. package/sources/assets/images/gradients/87.png +0 -0
  620. package/sources/assets/images/gradients/88.png +0 -0
  621. package/sources/assets/images/gradients/89.png +0 -0
  622. package/sources/assets/images/gradients/90.png +0 -0
  623. package/sources/assets/images/gradients/91.png +0 -0
  624. package/sources/assets/images/gradients/92.png +0 -0
  625. package/sources/assets/images/gradients/93.png +0 -0
  626. package/sources/assets/images/gradients/94.png +0 -0
  627. package/sources/assets/images/gradients/95.png +0 -0
  628. package/sources/assets/images/gradients/96.png +0 -0
  629. package/sources/assets/images/gradients/97.png +0 -0
  630. package/sources/assets/images/gradients/98.png +0 -0
  631. package/sources/assets/images/gradients/99.png +0 -0
  632. package/sources/assets/images/icon-adaptive.png +0 -0
  633. package/sources/assets/images/icon-claude.png +0 -0
  634. package/sources/assets/images/icon-claude@2x.png +0 -0
  635. package/sources/assets/images/icon-claude@3x.png +0 -0
  636. package/sources/assets/images/icon-gemini.png +0 -0
  637. package/sources/assets/images/icon-gemini@2x.png +0 -0
  638. package/sources/assets/images/icon-gemini@3x.png +0 -0
  639. package/sources/assets/images/icon-gpt.png +0 -0
  640. package/sources/assets/images/icon-gpt@2x.png +0 -0
  641. package/sources/assets/images/icon-gpt@3x.png +0 -0
  642. package/sources/assets/images/icon-monochrome.png +0 -0
  643. package/sources/assets/images/icon-notification.png +0 -0
  644. package/sources/assets/images/icon-openclaw.png +0 -0
  645. package/sources/assets/images/icon-openclaw@2x.png +0 -0
  646. package/sources/assets/images/icon-openclaw@3x.png +0 -0
  647. package/sources/assets/images/icon-tauri.png +0 -0
  648. package/sources/assets/images/icon-voice-white.png +0 -0
  649. package/sources/assets/images/icon-voice.png +0 -0
  650. package/sources/assets/images/icon-voice@2x.png +0 -0
  651. package/sources/assets/images/icon-voice@3x.png +0 -0
  652. package/sources/assets/images/icon.png +0 -0
  653. package/sources/assets/images/logo-black.png +0 -0
  654. package/sources/assets/images/logo-white.png +0 -0
  655. package/sources/assets/images/logotype-dark.png +0 -0
  656. package/sources/assets/images/logotype-dark@2x.png +0 -0
  657. package/sources/assets/images/logotype-dark@3x.png +0 -0
  658. package/sources/assets/images/logotype-light.png +0 -0
  659. package/sources/assets/images/logotype-light@2x.png +0 -0
  660. package/sources/assets/images/logotype-light@3x.png +0 -0
  661. package/sources/assets/images/logotype.png +0 -0
  662. package/sources/assets/images/logotype@2x.png +0 -0
  663. package/sources/assets/images/logotype@3x.png +0 -0
  664. package/sources/assets/images/splash-android-dark.png +0 -0
  665. package/sources/assets/images/splash-android-light.png +0 -0
  666. package/sources/assets/images/transparent.png +0 -0
  667. package/sources/assets/images/zen-icon.png +0 -0
  668. package/sources/auth/AuthContext.tsx +100 -0
  669. package/sources/auth/authAccountApprove.ts +2 -0
  670. package/sources/auth/authApprove.ts +2 -0
  671. package/sources/auth/authChallenge.ts +8 -0
  672. package/sources/auth/authGetToken.ts +4 -0
  673. package/sources/auth/authQRStart.ts +17 -0
  674. package/sources/auth/authQRWait.ts +13 -0
  675. package/sources/auth/secretKeyBackup.spec.ts +465 -0
  676. package/sources/auth/secretKeyBackup.ts +179 -0
  677. package/sources/auth/tokenStorage.ts +27 -0
  678. package/sources/changelog/changelog.json +60 -0
  679. package/sources/changelog/index.ts +3 -0
  680. package/sources/changelog/parser.ts +23 -0
  681. package/sources/changelog/storage.ts +17 -0
  682. package/sources/changelog/types.ts +10 -0
  683. package/sources/components/ActiveSessionsGroupCompact.tsx +567 -0
  684. package/sources/components/AgentContentView.ios.tsx +70 -0
  685. package/sources/components/AgentContentView.tsx +48 -0
  686. package/sources/components/AgentInput.tsx +1468 -0
  687. package/sources/components/AgentInputAttachmentStrip.tsx +122 -0
  688. package/sources/components/AgentInputAutocomplete.tsx +96 -0
  689. package/sources/components/AgentInputSuggestionView.tsx +106 -0
  690. package/sources/components/AllFilesDiffView.tsx +515 -0
  691. package/sources/components/Avatar.tsx +149 -0
  692. package/sources/components/AvatarBrutalist.tsx +501 -0
  693. package/sources/components/AvatarGradient.tsx +147 -0
  694. package/sources/components/AvatarSkia.tsx +111 -0
  695. package/sources/components/AvatarSkia.web.tsx +113 -0
  696. package/sources/components/ChatFooter.tsx +50 -0
  697. package/sources/components/ChatHeaderView.tsx +180 -0
  698. package/sources/components/ChatList.tsx +283 -0
  699. package/sources/components/CodeEditor.tsx +22 -0
  700. package/sources/components/CodeEditor.web.tsx +180 -0
  701. package/sources/components/CodeView.tsx +33 -0
  702. package/sources/components/CommandPalette/CommandPalette.tsx +72 -0
  703. package/sources/components/CommandPalette/CommandPaletteInput.tsx +65 -0
  704. package/sources/components/CommandPalette/CommandPaletteItem.tsx +141 -0
  705. package/sources/components/CommandPalette/CommandPaletteModal.tsx +148 -0
  706. package/sources/components/CommandPalette/CommandPaletteProvider.tsx +141 -0
  707. package/sources/components/CommandPalette/CommandPaletteResults.tsx +129 -0
  708. package/sources/components/CommandPalette/index.ts +3 -0
  709. package/sources/components/CommandPalette/types.ts +15 -0
  710. package/sources/components/CommandPalette/useCommandPalette.ts +107 -0
  711. package/sources/components/CommandView.tsx +135 -0
  712. package/sources/components/CompactGitStatus.tsx +88 -0
  713. package/sources/components/ConnectButton.tsx +117 -0
  714. package/sources/components/Deferred.tsx +18 -0
  715. package/sources/components/DuplicateSheet.tsx +295 -0
  716. package/sources/components/EmptyMainScreen.tsx +171 -0
  717. package/sources/components/EmptyMessages.tsx +123 -0
  718. package/sources/components/EmptySessionsTablet.tsx +111 -0
  719. package/sources/components/ExternalLink.tsx +22 -0
  720. package/sources/components/FAB.tsx +53 -0
  721. package/sources/components/FABWide.tsx +59 -0
  722. package/sources/components/FeedItemCard.tsx +98 -0
  723. package/sources/components/FileIcon.tsx +63 -0
  724. package/sources/components/FileViewPanel.tsx +673 -0
  725. package/sources/components/FilesSidebar.tsx +739 -0
  726. package/sources/components/FloatingOverlay.tsx +48 -0
  727. package/sources/components/GitStatusBadge.tsx +82 -0
  728. package/sources/components/HeaderLogo.tsx +28 -0
  729. package/sources/components/HomeHeader.tsx +243 -0
  730. package/sources/components/HorizontalScrollView.tsx +88 -0
  731. package/sources/components/InboxView.tsx +260 -0
  732. package/sources/components/InlineFileDiff.tsx +277 -0
  733. package/sources/components/Item.tsx +315 -0
  734. package/sources/components/ItemGroup.tsx +147 -0
  735. package/sources/components/ItemList.tsx +102 -0
  736. package/sources/components/MainView.tsx +324 -0
  737. package/sources/components/MessageView.tsx +299 -0
  738. package/sources/components/MultiTextInput.tsx +285 -0
  739. package/sources/components/MultiTextInput.web.tsx +220 -0
  740. package/sources/components/OAuthView.tsx +374 -0
  741. package/sources/components/PermissionModeSelector.tsx +68 -0
  742. package/sources/components/PlaceholderContainerView.tsx +47 -0
  743. package/sources/components/PlusPlus.tsx +34 -0
  744. package/sources/components/PlusPlus.web.tsx +34 -0
  745. package/sources/components/ProjectGitStatus.tsx +102 -0
  746. package/sources/components/RoundButton.tsx +130 -0
  747. package/sources/components/SearchableListSelector.tsx +675 -0
  748. package/sources/components/SessionActionsNativeMenu.android.tsx +58 -0
  749. package/sources/components/SessionActionsNativeMenu.ios.tsx +55 -0
  750. package/sources/components/SessionActionsNativeMenu.tsx +20 -0
  751. package/sources/components/SessionActionsNativeMenu.web.tsx +13 -0
  752. package/sources/components/SessionActionsPopover.tsx +240 -0
  753. package/sources/components/SessionsList.tsx +471 -0
  754. package/sources/components/SessionsListWrapper.tsx +72 -0
  755. package/sources/components/SettingsView.tsx +470 -0
  756. package/sources/components/SettingsViewWrapper.tsx +21 -0
  757. package/sources/components/Shaker.tsx +42 -0
  758. package/sources/components/Shaker.web.tsx +46 -0
  759. package/sources/components/ShimmerView.tsx +106 -0
  760. package/sources/components/SidebarNavigator.tsx +218 -0
  761. package/sources/components/SidebarView.tsx +104 -0
  762. package/sources/components/SimpleSyntaxHighlighter.tsx +322 -0
  763. package/sources/components/StatusBarProvider.tsx +12 -0
  764. package/sources/components/StatusDot.tsx +49 -0
  765. package/sources/components/StyledText.tsx +35 -0
  766. package/sources/components/Switch.tsx +20 -0
  767. package/sources/components/TabBar.tsx +140 -0
  768. package/sources/components/ToolGroupView.tsx +101 -0
  769. package/sources/components/TransitionStack.tsx +14 -0
  770. package/sources/components/UpdateBanner.tsx +74 -0
  771. package/sources/components/UserCard.tsx +41 -0
  772. package/sources/components/UserSearchResult.tsx +129 -0
  773. package/sources/components/VoiceAssistantStatusBar.tsx +260 -0
  774. package/sources/components/VoiceBars.tsx +95 -0
  775. package/sources/components/autocomplete/applySuggestion.test.ts +194 -0
  776. package/sources/components/autocomplete/applySuggestion.ts +61 -0
  777. package/sources/components/autocomplete/findActiveWord.test.ts +365 -0
  778. package/sources/components/autocomplete/findActiveWord.ts +207 -0
  779. package/sources/components/autocomplete/suggestions.ts +79 -0
  780. package/sources/components/autocomplete/useActiveSuggestions.ts +130 -0
  781. package/sources/components/autocomplete/useActiveWord.ts +19 -0
  782. package/sources/components/diff/DiffView.tsx +188 -0
  783. package/sources/components/diff/PierreDiffView.tsx +253 -0
  784. package/sources/components/diff/calculateDiff.ts +317 -0
  785. package/sources/components/entityColor.ts +51 -0
  786. package/sources/components/haptics.ts +9 -0
  787. package/sources/components/haptics.web.ts +7 -0
  788. package/sources/components/layout.ts +44 -0
  789. package/sources/components/markdown/MarkdownView.tsx +670 -0
  790. package/sources/components/markdown/MermaidRenderer.tsx +233 -0
  791. package/sources/components/markdown/linkUtils.test.ts +17 -0
  792. package/sources/components/markdown/linkUtils.ts +5 -0
  793. package/sources/components/markdown/parseMarkdown.test.ts +64 -0
  794. package/sources/components/markdown/parseMarkdown.ts +46 -0
  795. package/sources/components/markdown/parseMarkdownBlock.test.ts +108 -0
  796. package/sources/components/markdown/parseMarkdownBlock.ts +200 -0
  797. package/sources/components/markdown/parseMarkdownSpans.ts +88 -0
  798. package/sources/components/modelModeOptions.test.ts +114 -0
  799. package/sources/components/modelModeOptions.ts +257 -0
  800. package/sources/components/navigation/Header.tsx +252 -0
  801. package/sources/components/parseLocalCommandMessage.spec.ts +96 -0
  802. package/sources/components/parseLocalCommandMessage.ts +97 -0
  803. package/sources/components/qr/QRCode.tsx +178 -0
  804. package/sources/components/qr/QRCode.web.tsx +229 -0
  805. package/sources/components/qr/index.ts +2 -0
  806. package/sources/components/qr/qrMatrix.ts +48 -0
  807. package/sources/components/tools/PermissionFooter.tsx +527 -0
  808. package/sources/components/tools/ToolDiffView.tsx +60 -0
  809. package/sources/components/tools/ToolError.tsx +49 -0
  810. package/sources/components/tools/ToolFullView.tsx +193 -0
  811. package/sources/components/tools/ToolHeader.tsx +93 -0
  812. package/sources/components/tools/ToolSectionView.tsx +42 -0
  813. package/sources/components/tools/ToolStatusIndicator.tsx +36 -0
  814. package/sources/components/tools/ToolView.tsx +340 -0
  815. package/sources/components/tools/knownTools.tsx +957 -0
  816. package/sources/components/tools/views/AskUserQuestionView.tsx +357 -0
  817. package/sources/components/tools/views/BashView.tsx +47 -0
  818. package/sources/components/tools/views/BashViewFull.tsx +81 -0
  819. package/sources/components/tools/views/CodexBashView.tsx +126 -0
  820. package/sources/components/tools/views/CodexDiffView.tsx +79 -0
  821. package/sources/components/tools/views/CodexPatchView.tsx +186 -0
  822. package/sources/components/tools/views/EditView.tsx +33 -0
  823. package/sources/components/tools/views/EditViewFull.tsx +38 -0
  824. package/sources/components/tools/views/ExitPlanToolView.tsx +21 -0
  825. package/sources/components/tools/views/FileView.tsx +118 -0
  826. package/sources/components/tools/views/GeminiEditView.tsx +75 -0
  827. package/sources/components/tools/views/GeminiExecuteView.tsx +92 -0
  828. package/sources/components/tools/views/MCPToolView.tsx +31 -0
  829. package/sources/components/tools/views/MultiEditView.tsx +41 -0
  830. package/sources/components/tools/views/MultiEditViewFull.tsx +84 -0
  831. package/sources/components/tools/views/TaskView.tsx +129 -0
  832. package/sources/components/tools/views/TodoView.tsx +90 -0
  833. package/sources/components/tools/views/WriteView.tsx +29 -0
  834. package/sources/components/tools/views/_all.tsx +88 -0
  835. package/sources/components/usage/UsageBar.tsx +80 -0
  836. package/sources/components/usage/UsageChart.tsx +164 -0
  837. package/sources/components/usage/UsagePanel.tsx +283 -0
  838. package/sources/components/web/FaviconPermissionIndicator.tsx +44 -0
  839. package/sources/config.ts +3 -0
  840. package/sources/constants/Languages.ts +116 -0
  841. package/sources/constants/Typography.ts +116 -0
  842. package/sources/dev/testRunner.ts +277 -0
  843. package/sources/docs/autocomplete-text-manipulation.md +224 -0
  844. package/sources/encryption/aes.appspec.ts +25 -0
  845. package/sources/encryption/aes.ts +21 -0
  846. package/sources/encryption/aes.web.test.ts +73 -0
  847. package/sources/encryption/aes.web.ts +79 -0
  848. package/sources/encryption/base64.appspec.ts +240 -0
  849. package/sources/encryption/base64.native.ts +12 -0
  850. package/sources/encryption/base64.ts +46 -0
  851. package/sources/encryption/blob.test.ts +120 -0
  852. package/sources/encryption/blob.ts +58 -0
  853. package/sources/encryption/deriveKey.appspec.ts +72 -0
  854. package/sources/encryption/deriveKey.ts +46 -0
  855. package/sources/encryption/hex.ts +17 -0
  856. package/sources/encryption/hmac_sha512.appspec.ts +40 -0
  857. package/sources/encryption/hmac_sha512.ts +42 -0
  858. package/sources/encryption/libsodium.lib.ts +2 -0
  859. package/sources/encryption/libsodium.lib.web.ts +2 -0
  860. package/sources/encryption/libsodium.ts +58 -0
  861. package/sources/encryption/text.test.ts +61 -0
  862. package/sources/encryption/text.ts +11 -0
  863. package/sources/hooks/useAsyncCommand.ts +25 -0
  864. package/sources/hooks/useAttachmentImage.ts +134 -0
  865. package/sources/hooks/useAutocomplete.ts +69 -0
  866. package/sources/hooks/useAutocompleteSession.ts +53 -0
  867. package/sources/hooks/useChangelog.ts +45 -0
  868. package/sources/hooks/useCheckCameraPermissions.ts +25 -0
  869. package/sources/hooks/useConnectAccount.ts +107 -0
  870. package/sources/hooks/useConnectTerminal.ts +112 -0
  871. package/sources/hooks/useDemoMessages.ts +48 -0
  872. package/sources/hooks/useDraft.ts +120 -0
  873. package/sources/hooks/useElapsedTime.ts +36 -0
  874. package/sources/hooks/useGetPath.ts +13 -0
  875. package/sources/hooks/useGitStatusFiles.ts +45 -0
  876. package/sources/hooks/useGlobalKeyboard.ts +29 -0
  877. package/sources/hooks/useGroupedMessages.ts +149 -0
  878. package/sources/hooks/useImagePicker.ts +135 -0
  879. package/sources/hooks/useInboxHasContent.ts +19 -0
  880. package/sources/hooks/useMultiClick.ts +56 -0
  881. package/sources/hooks/useNasTechAction.ts +45 -0
  882. package/sources/hooks/useNativeUpdate.ts +10 -0
  883. package/sources/hooks/useNavigateToSession.ts +20 -0
  884. package/sources/hooks/useNewSessionDraft.ts +70 -0
  885. package/sources/hooks/usePrefetchFileContents.ts +162 -0
  886. package/sources/hooks/useSearch.ts +120 -0
  887. package/sources/hooks/useSessionQuickActions.ts +325 -0
  888. package/sources/hooks/useTauriDrag.ts +76 -0
  889. package/sources/hooks/useTauriZoom.ts +67 -0
  890. package/sources/hooks/useUpdates.ts +84 -0
  891. package/sources/hooks/useVisibleSessionListViewData.ts +65 -0
  892. package/sources/hooks/useWorktreeCleanup.ts +69 -0
  893. package/sources/log.ts +108 -0
  894. package/sources/modal/ModalManager.ts +203 -0
  895. package/sources/modal/ModalProvider.tsx +103 -0
  896. package/sources/modal/components/BaseModal.tsx +122 -0
  897. package/sources/modal/components/CustomModal.tsx +42 -0
  898. package/sources/modal/components/WebAlertModal.tsx +139 -0
  899. package/sources/modal/components/WebPromptModal.tsx +185 -0
  900. package/sources/modal/index.ts +3 -0
  901. package/sources/modal/types.ts +79 -0
  902. package/sources/nastech-wire/index.ts +10 -0
  903. package/sources/nastech-wire/legacyProtocol.ts +27 -0
  904. package/sources/nastech-wire/messageMeta.ts +14 -0
  905. package/sources/nastech-wire/messages.ts +113 -0
  906. package/sources/nastech-wire/sessionProtocol.ts +134 -0
  907. package/sources/nastech-wire/voice.ts +34 -0
  908. package/sources/polyfills/screenOrientation.ts +33 -0
  909. package/sources/realtime/RealtimeProvider.tsx +20 -0
  910. package/sources/realtime/RealtimeProvider.web.tsx +15 -0
  911. package/sources/realtime/RealtimeSession.ts +200 -0
  912. package/sources/realtime/RealtimeVoiceSession.tsx +211 -0
  913. package/sources/realtime/RealtimeVoiceSession.web.tsx +209 -0
  914. package/sources/realtime/hooks/contextFormatters.ts +127 -0
  915. package/sources/realtime/hooks/voiceHooks.ts +232 -0
  916. package/sources/realtime/realtimeClientTools.ts +94 -0
  917. package/sources/realtime/types.ts +19 -0
  918. package/sources/realtime/voiceConfig.ts +31 -0
  919. package/sources/realtime/voiceExperiment.ts +91 -0
  920. package/sources/realtime/voiceSystemPrompt.ts +75 -0
  921. package/sources/scripts/compareTranslations.ts +217 -0
  922. package/sources/scripts/parseChangelog.ts +87 -0
  923. package/sources/sync/__testdata__/trace_0.json +3986 -0
  924. package/sources/sync/__testdata__/trace_1.json +1391 -0
  925. package/sources/sync/__testdata__/trace_2.json +182 -0
  926. package/sources/sync/agentDefaults.ts +108 -0
  927. package/sources/sync/apiArtifacts.ts +143 -0
  928. package/sources/sync/apiAttachments.ts +217 -0
  929. package/sources/sync/apiFeed.ts +60 -0
  930. package/sources/sync/apiFriends.ts +217 -0
  931. package/sources/sync/apiGithub.spec.ts +97 -0
  932. package/sources/sync/apiGithub.ts +103 -0
  933. package/sources/sync/apiKv.ts +270 -0
  934. package/sources/sync/apiPush.ts +83 -0
  935. package/sources/sync/apiServices.ts +64 -0
  936. package/sources/sync/apiSocket.ts +290 -0
  937. package/sources/sync/apiTypes.spec.ts +23 -0
  938. package/sources/sync/apiTypes.ts +213 -0
  939. package/sources/sync/apiUsage.ts +130 -0
  940. package/sources/sync/apiVoice.ts +57 -0
  941. package/sources/sync/appConfig.ts +95 -0
  942. package/sources/sync/artifactTypes.ts +85 -0
  943. package/sources/sync/attachmentTypes.ts +28 -0
  944. package/sources/sync/encryption/artifactEncryption.ts +83 -0
  945. package/sources/sync/encryption/encryption.ts +220 -0
  946. package/sources/sync/encryption/encryptionCache.ts +248 -0
  947. package/sources/sync/encryption/encryptor.appspec.ts +409 -0
  948. package/sources/sync/encryption/encryptor.ts +126 -0
  949. package/sources/sync/encryption/machineEncryption.ts +122 -0
  950. package/sources/sync/encryption/sessionEncryption.ts +207 -0
  951. package/sources/sync/feedTypes.ts +43 -0
  952. package/sources/sync/friendTypes.ts +92 -0
  953. package/sources/sync/git-parsers/LineParser.ts +62 -0
  954. package/sources/sync/git-parsers/parseBranch.ts +97 -0
  955. package/sources/sync/git-parsers/parseDiff.ts +180 -0
  956. package/sources/sync/git-parsers/parseStatus.ts +162 -0
  957. package/sources/sync/git-parsers/parseStatusV2.ts +307 -0
  958. package/sources/sync/gitStatusFiles.ts +185 -0
  959. package/sources/sync/gitStatusSync.ts +282 -0
  960. package/sources/sync/localSettings.ts +67 -0
  961. package/sources/sync/messageMeta.test.ts +87 -0
  962. package/sources/sync/messageMeta.ts +36 -0
  963. package/sources/sync/modeHacks.test.ts +29 -0
  964. package/sources/sync/modeHacks.ts +22 -0
  965. package/sources/sync/nastechApi.ts +124 -0
  966. package/sources/sync/ops.ts +776 -0
  967. package/sources/sync/persistence.ts +322 -0
  968. package/sources/sync/profile.ts +95 -0
  969. package/sources/sync/projectFiles.ts +54 -0
  970. package/sources/sync/prompt/systemPrompt.ts +20 -0
  971. package/sources/sync/purchases.ts +67 -0
  972. package/sources/sync/pushRegistration.ts +229 -0
  973. package/sources/sync/reducer/activityUpdateAccumulator.test.ts +492 -0
  974. package/sources/sync/reducer/activityUpdateAccumulator.ts +96 -0
  975. package/sources/sync/reducer/messageToEvent.ts +85 -0
  976. package/sources/sync/reducer/phase0-skipping.spec.ts +206 -0
  977. package/sources/sync/reducer/reducer.spec.ts +3169 -0
  978. package/sources/sync/reducer/reducer.ts +1214 -0
  979. package/sources/sync/reducer/reducerTracer.spec.ts +502 -0
  980. package/sources/sync/reducer/reducerTracer.ts +310 -0
  981. package/sources/sync/revenueCat/index.ts +21 -0
  982. package/sources/sync/revenueCat/revenueCat.ts +215 -0
  983. package/sources/sync/revenueCat/revenueCat.web.ts +238 -0
  984. package/sources/sync/revenueCat/types.ts +82 -0
  985. package/sources/sync/serverConfig.ts +72 -0
  986. package/sources/sync/settings.spec.ts +456 -0
  987. package/sources/sync/settings.ts +186 -0
  988. package/sources/sync/storage.ts +1653 -0
  989. package/sources/sync/storageTypes.spec.ts +24 -0
  990. package/sources/sync/storageTypes.ts +215 -0
  991. package/sources/sync/suggestionCommands.ts +149 -0
  992. package/sources/sync/suggestionFile.ts +206 -0
  993. package/sources/sync/sync.ts +2555 -0
  994. package/sources/sync/typesMessage.ts +69 -0
  995. package/sources/sync/typesMessageMeta.test.ts +14 -0
  996. package/sources/sync/typesMessageMeta.ts +17 -0
  997. package/sources/sync/typesRaw.spec.ts +2010 -0
  998. package/sources/sync/typesRaw.ts +1183 -0
  999. package/sources/sync/uploadFormFile.ts +29 -0
  1000. package/sources/sync/uploadFormFile.web.ts +14 -0
  1001. package/sources/sync/webTabTitle.ts +58 -0
  1002. package/sources/text/README.md +223 -0
  1003. package/sources/text/_all.ts +104 -0
  1004. package/sources/text/_default.ts +1015 -0
  1005. package/sources/text/index.ts +215 -0
  1006. package/sources/text/translations/ca.ts +993 -0
  1007. package/sources/text/translations/en.ts +1009 -0
  1008. package/sources/text/translations/es.ts +995 -0
  1009. package/sources/text/translations/it.ts +992 -0
  1010. package/sources/text/translations/ja.ts +993 -0
  1011. package/sources/text/translations/pl.ts +1024 -0
  1012. package/sources/text/translations/pt.ts +992 -0
  1013. package/sources/text/translations/ru.ts +1023 -0
  1014. package/sources/text/translations/zh-Hans.ts +992 -0
  1015. package/sources/text/translations/zh-Hant.ts +991 -0
  1016. package/sources/theme.css +72 -0
  1017. package/sources/theme.dark.json +31 -0
  1018. package/sources/theme.figma.json +457 -0
  1019. package/sources/theme.gen.ts +10 -0
  1020. package/sources/theme.light.json +31 -0
  1021. package/sources/theme.ts +452 -0
  1022. package/sources/track/index.ts +171 -0
  1023. package/sources/track/tracking.ts +12 -0
  1024. package/sources/track/useTrackScreens.ts +10 -0
  1025. package/sources/types/react-native-webrtc-web-shim.d.ts +6 -0
  1026. package/sources/unistyles.ts +97 -0
  1027. package/sources/utils/codexUnifiedDiff.spec.ts +43 -0
  1028. package/sources/utils/codexUnifiedDiff.ts +70 -0
  1029. package/sources/utils/consoleLogging.ts +145 -0
  1030. package/sources/utils/copySessionMetadataToClipboard.ts +53 -0
  1031. package/sources/utils/debounce.test.ts +646 -0
  1032. package/sources/utils/debounce.ts +122 -0
  1033. package/sources/utils/deviceCalculations.test.ts +318 -0
  1034. package/sources/utils/deviceCalculations.ts +87 -0
  1035. package/sources/utils/errors.ts +10 -0
  1036. package/sources/utils/formatPermissionParams.ts +23 -0
  1037. package/sources/utils/isTauri.ts +7 -0
  1038. package/sources/utils/loadSkia.ts +3 -0
  1039. package/sources/utils/loadSkia.web.ts +5 -0
  1040. package/sources/utils/lock.ts +40 -0
  1041. package/sources/utils/machineUtils.ts +6 -0
  1042. package/sources/utils/messageUtils.ts +254 -0
  1043. package/sources/utils/microphonePermissions.ts +109 -0
  1044. package/sources/utils/notificationRouting.test.ts +51 -0
  1045. package/sources/utils/notificationRouting.ts +81 -0
  1046. package/sources/utils/oauth.ts +143 -0
  1047. package/sources/utils/openExternalUrl.ts +19 -0
  1048. package/sources/utils/parseToken.ts +23 -0
  1049. package/sources/utils/pasteImages.web.ts +81 -0
  1050. package/sources/utils/pathUtils.spec.ts +226 -0
  1051. package/sources/utils/pathUtils.ts +75 -0
  1052. package/sources/utils/platform.ts +19 -0
  1053. package/sources/utils/readFileBytes.ts +11 -0
  1054. package/sources/utils/readFileBytes.web.ts +12 -0
  1055. package/sources/utils/requestReview.ts +135 -0
  1056. package/sources/utils/responsive.ts +87 -0
  1057. package/sources/utils/resumeCommand.test.ts +78 -0
  1058. package/sources/utils/resumeCommand.ts +70 -0
  1059. package/sources/utils/sessionFileLinks.test.ts +112 -0
  1060. package/sources/utils/sessionFileLinks.ts +388 -0
  1061. package/sources/utils/sessionUtils.ts +226 -0
  1062. package/sources/utils/stringUtils.ts +41 -0
  1063. package/sources/utils/sync.ts +164 -0
  1064. package/sources/utils/thumbhash.ts +17 -0
  1065. package/sources/utils/thumbhash.web.ts +85 -0
  1066. package/sources/utils/time.ts +41 -0
  1067. package/sources/utils/toSnakeCase.test.ts +182 -0
  1068. package/sources/utils/toSnakeCase.ts +40 -0
  1069. package/sources/utils/toolCommand.test.ts +23 -0
  1070. package/sources/utils/toolCommand.ts +35 -0
  1071. package/sources/utils/toolComparison.test.ts +101 -0
  1072. package/sources/utils/toolComparison.ts +73 -0
  1073. package/sources/utils/toolErrorParser.test.ts +126 -0
  1074. package/sources/utils/toolErrorParser.ts +102 -0
  1075. package/sources/utils/trimIdent.ts +27 -0
  1076. package/sources/utils/truncateForLogs.ts +37 -0
  1077. package/sources/utils/versionUtils.test.ts +58 -0
  1078. package/sources/utils/versionUtils.ts +69 -0
  1079. package/sources/utils/web/faviconGenerator.ts +39 -0
  1080. package/sources/utils/worktree.ts +192 -0
  1081. package/sources/wire/index.ts +97 -0
  1082. package/src-tauri/Cargo.lock +5978 -0
  1083. package/src-tauri/Cargo.toml +27 -0
  1084. package/src-tauri/build.rs +3 -0
  1085. package/src-tauri/capabilities/default.json +27 -0
  1086. package/src-tauri/deny.toml +53 -0
  1087. package/src-tauri/entitlements.plist +24 -0
  1088. package/src-tauri/icons/128x128.png +0 -0
  1089. package/src-tauri/icons/128x128@2x.png +0 -0
  1090. package/src-tauri/icons/32x32.png +0 -0
  1091. package/src-tauri/icons/64x64.png +0 -0
  1092. package/src-tauri/icons/Square107x107Logo.png +0 -0
  1093. package/src-tauri/icons/Square142x142Logo.png +0 -0
  1094. package/src-tauri/icons/Square150x150Logo.png +0 -0
  1095. package/src-tauri/icons/Square284x284Logo.png +0 -0
  1096. package/src-tauri/icons/Square30x30Logo.png +0 -0
  1097. package/src-tauri/icons/Square310x310Logo.png +0 -0
  1098. package/src-tauri/icons/Square44x44Logo.png +0 -0
  1099. package/src-tauri/icons/Square71x71Logo.png +0 -0
  1100. package/src-tauri/icons/Square89x89Logo.png +0 -0
  1101. package/src-tauri/icons/StoreLogo.png +0 -0
  1102. package/src-tauri/icons/icon.icns +0 -0
  1103. package/src-tauri/icons/icon.ico +0 -0
  1104. package/src-tauri/icons/icon.png +0 -0
  1105. package/src-tauri/src/lib.rs +18 -0
  1106. package/src-tauri/src/main.rs +6 -0
  1107. package/src-tauri/tauri.conf.json +51 -0
  1108. package/src-tauri/tauri.dev.conf.json +20 -0
  1109. package/src-tauri/tauri.preview.conf.json +20 -0
  1110. package/tsconfig.json +45 -0
  1111. package/vitest.config.ts +26 -0
@@ -0,0 +1,1611 @@
1
+ import React from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ Platform,
6
+ Pressable,
7
+ Modal as RNModal,
8
+ TouchableWithoutFeedback,
9
+ Animated,
10
+ TextInput,
11
+ ScrollView,
12
+ LayoutAnimation,
13
+ ActivityIndicator,
14
+ TextInputSelectionChangeEventData,
15
+ NativeSyntheticEvent,
16
+ Image as RNImage,
17
+ } from 'react-native';
18
+ import { GlassView } from 'expo-glass-effect';
19
+ import { Ionicons, Octicons, MaterialCommunityIcons } from '@expo/vector-icons';
20
+ import { useRouter } from 'expo-router';
21
+ import { Typography } from '@/constants/Typography';
22
+ import { layout } from '@/components/layout';
23
+ import {
24
+ MultiTextInput,
25
+ MULTI_TEXT_INPUT_LINE_HEIGHT,
26
+ type KeyPressEvent,
27
+ } from '@/components/MultiTextInput';
28
+ import { StyleSheet, useUnistyles } from 'react-native-unistyles';
29
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
30
+ import { KeyboardAvoidingView } from 'react-native-keyboard-controller';
31
+ import Constants from 'expo-constants';
32
+ import { useHeaderHeight } from '@/utils/responsive';
33
+ import { t } from '@/text';
34
+ import { useAllMachines, useSessions, useSetting, storage } from '@/sync/storage';
35
+ import type { NewSessionAgentType } from '@/sync/persistence';
36
+ import { sync } from '@/sync/sync';
37
+ import { isMachineOnline } from '@/utils/machineUtils';
38
+ import { machineSpawnNewSession } from '@/sync/ops';
39
+ import { createWorktree, listWorktrees } from '@/utils/worktree';
40
+ import { resolveAbsolutePath } from '@/utils/pathUtils';
41
+ import { formatPathRelativeToHome, formatLastSeen } from '@/utils/sessionUtils';
42
+ import { useNavigateToSession } from '@/hooks/useNavigateToSession';
43
+ import { useNewSessionDraft } from '@/hooks/useNewSessionDraft';
44
+ import { useShallow } from 'zustand/react/shallow';
45
+ import type { MultiTextInputHandle } from '@/components/MultiTextInput';
46
+ import { Modal } from '@/modal';
47
+ import type { Machine, Session } from '@/sync/storageTypes';
48
+ import {
49
+ getHardcodedPermissionModes,
50
+ getHardcodedModelModes,
51
+ getEffortLevelsForModel,
52
+ getSupportsWorktree,
53
+ type PermissionMode,
54
+ type ModelMode,
55
+ type EffortLevel,
56
+ } from '@/components/modelModeOptions';
57
+ import { isRunningOnMac } from '@/utils/platform';
58
+ import { resolveAgentDefaultConfig } from '@/sync/agentDefaults';
59
+
60
+ // Agent icon assets
61
+ const agentIcons = {
62
+ claude: require('@/assets/images/icon-claude.png'),
63
+ codex: require('@/assets/images/icon-gpt.png'),
64
+ openclaw: require('@/assets/images/icon-openclaw.png'),
65
+ gemini: require('@/assets/images/icon-gemini.png'),
66
+ };
67
+
68
+ type AgentKey = NewSessionAgentType;
69
+ const ALL_AGENTS: { key: AgentKey; label: string }[] = [
70
+ { key: 'claude', label: 'claude code' },
71
+ { key: 'codex', label: 'codex' },
72
+ { key: 'openclaw', label: 'openclaw' },
73
+ { key: 'gemini', label: 'gemini' },
74
+ ];
75
+
76
+ type PickerItem = { key: string; label: string; subtitle?: string; dimmed?: boolean };
77
+
78
+ type PickerType = 'machine' | 'path' | 'worktree';
79
+
80
+ type PermissionStyle = { color: string; icon: 'play-forward' | 'pause' };
81
+
82
+ const COMPOSER_INPUT_VERTICAL_PADDING = Platform.OS === 'web' ? 10 : 8;
83
+ // Taller composer on web/desktop where vertical space is plentiful; keep the
84
+ // compact cap on native mobile so the input doesn't dominate the screen.
85
+ const COMPOSER_INPUT_MAX_HEIGHT = Platform.OS === 'web' ? 480 : 240;
86
+ const COMPOSER_SEND_BUTTON_SIZE = 32;
87
+ const COMPOSER_SEND_BUTTON_MARGIN_BOTTOM = Math.max(
88
+ 0,
89
+ Math.round((MULTI_TEXT_INPUT_LINE_HEIGHT + COMPOSER_INPUT_VERTICAL_PADDING * 2 - COMPOSER_SEND_BUTTON_SIZE) / 2),
90
+ );
91
+ const WORKTREE_PATH_DEBOUNCE_MS = 300;
92
+
93
+ function trimPathInput(path: string | null | undefined): string {
94
+ return path?.trim() ?? '';
95
+ }
96
+
97
+ function trimTrailingPathSeparator(path: string): string {
98
+ if (path === '/' || /^[A-Za-z]:[\\/]?$/.test(path)) {
99
+ return path;
100
+ }
101
+ return path.replace(/[\\/]+$/, '');
102
+ }
103
+
104
+ function normalizePathForComparison(path: string | null | undefined, homeDir?: string): string | null {
105
+ const trimmed = trimPathInput(path);
106
+ if (!trimmed) {
107
+ return null;
108
+ }
109
+ return trimTrailingPathSeparator(resolveAbsolutePath(trimmed, homeDir));
110
+ }
111
+
112
+ function getPermissionStyle(key: string): PermissionStyle | null {
113
+ switch (key) {
114
+ case 'acceptEdits':
115
+ case 'auto_edit':
116
+ return { color: '#A78BFA', icon: 'play-forward' };
117
+ case 'plan':
118
+ return { color: '#5EABA4', icon: 'pause' };
119
+ case 'dontAsk':
120
+ case 'safe-yolo':
121
+ return { color: '#FBBF24', icon: 'play-forward' };
122
+ case 'bypassPermissions':
123
+ case 'yolo':
124
+ return { color: '#F87171', icon: 'play-forward' };
125
+ case 'read-only':
126
+ return { color: '#60A5FA', icon: 'pause' };
127
+ default:
128
+ return null;
129
+ }
130
+ }
131
+
132
+ // Bottom sheet modal — native formSheet on iOS, slide-up sheet on Android
133
+ function BottomSheet({
134
+ visible,
135
+ onClose,
136
+ children,
137
+ }: {
138
+ visible: boolean;
139
+ onClose: () => void;
140
+ children: React.ReactNode;
141
+ }) {
142
+ const { theme } = useUnistyles();
143
+ const safeArea = useSafeAreaInsets();
144
+
145
+ if (Platform.OS === 'ios') {
146
+ return (
147
+ <RNModal
148
+ visible={visible}
149
+ animationType="slide"
150
+ presentationStyle="formSheet"
151
+ onRequestClose={onClose}
152
+ >
153
+ <View style={[sheetStyles.iosContainer, { backgroundColor: theme.colors.header.background }]}>
154
+ <View style={sheetStyles.handleRow}>
155
+ <View style={[sheetStyles.handle, { backgroundColor: theme.colors.textSecondary }]} />
156
+ </View>
157
+ {children}
158
+ <View style={{ height: safeArea.bottom }} />
159
+ </View>
160
+ </RNModal>
161
+ );
162
+ }
163
+
164
+ // Android: slide-up sheet with backdrop
165
+ const fadeAnim = React.useRef(new Animated.Value(0)).current;
166
+ const slideAnim = React.useRef(new Animated.Value(300)).current;
167
+
168
+ React.useEffect(() => {
169
+ if (visible) {
170
+ Animated.parallel([
171
+ Animated.timing(fadeAnim, { toValue: 1, duration: 250, useNativeDriver: true }),
172
+ Animated.spring(slideAnim, { toValue: 0, damping: 25, stiffness: 300, useNativeDriver: true }),
173
+ ]).start();
174
+ } else {
175
+ Animated.parallel([
176
+ Animated.timing(fadeAnim, { toValue: 0, duration: 200, useNativeDriver: true }),
177
+ Animated.timing(slideAnim, { toValue: 300, duration: 200, useNativeDriver: true }),
178
+ ]).start();
179
+ }
180
+ }, [visible, fadeAnim, slideAnim]);
181
+
182
+ return (
183
+ <RNModal
184
+ visible={visible}
185
+ transparent
186
+ animationType="none"
187
+ onRequestClose={onClose}
188
+ >
189
+ <View style={sheetStyles.overlay}>
190
+ <TouchableWithoutFeedback onPress={onClose}>
191
+ <Animated.View style={[sheetStyles.backdrop, { opacity: fadeAnim }]} />
192
+ </TouchableWithoutFeedback>
193
+ <Animated.View
194
+ style={[
195
+ sheetStyles.sheet,
196
+ {
197
+ backgroundColor: theme.colors.header.background,
198
+ paddingBottom: Math.max(16, safeArea.bottom),
199
+ transform: [{ translateY: slideAnim }],
200
+ },
201
+ ]}
202
+ >
203
+ <View style={sheetStyles.handleRow}>
204
+ <View style={[sheetStyles.handle, { backgroundColor: theme.colors.textSecondary }]} />
205
+ </View>
206
+ {children}
207
+ </Animated.View>
208
+ </View>
209
+ </RNModal>
210
+ );
211
+ }
212
+
213
+ // Generic picker content — reused for machine, path, and worktree selection
214
+ function PickerContent({
215
+ title,
216
+ fixedItems,
217
+ items,
218
+ selectedKey,
219
+ onSelect,
220
+ searchPlaceholder,
221
+ }: {
222
+ title: string;
223
+ fixedItems?: PickerItem[];
224
+ items: PickerItem[];
225
+ selectedKey: string | null;
226
+ onSelect: (key: string) => void;
227
+ searchPlaceholder?: string;
228
+ }) {
229
+ const { theme } = useUnistyles();
230
+ const [search, setSearch] = React.useState('');
231
+
232
+ const filtered = React.useMemo(() => {
233
+ if (!search) return items;
234
+ const q = search.toLowerCase();
235
+ return items.filter(item => item.label.toLowerCase().includes(q));
236
+ }, [search, items]);
237
+
238
+ const renderOption = (item: PickerItem) => {
239
+ const isSelected = item.key === selectedKey;
240
+ return (
241
+ <Pressable
242
+ key={item.key}
243
+ style={(p) => [pickerStyles.option, p.pressed && pickerStyles.optionPressed, item.dimmed && { opacity: 0.45 }]}
244
+ onPress={() => onSelect(item.key)}
245
+ >
246
+ <Octicons
247
+ name={isSelected ? 'check-circle-fill' : 'circle'}
248
+ size={16}
249
+ color={isSelected ? theme.colors.button.primary.background : theme.colors.textSecondary}
250
+ />
251
+ <View style={{ flex: 1 }}>
252
+ <Text style={[pickerStyles.optionText, { color: theme.colors.text }]}>{item.label}</Text>
253
+ {item.subtitle && (
254
+ <Text style={[pickerStyles.optionText, { color: theme.colors.textSecondary, fontSize: 13 }]}>{item.subtitle}</Text>
255
+ )}
256
+ </View>
257
+ </Pressable>
258
+ );
259
+ };
260
+
261
+ return (
262
+ <View style={pickerStyles.container}>
263
+ <Text style={[pickerStyles.title, { color: theme.colors.text }]}>{title}</Text>
264
+
265
+ <View style={[pickerStyles.searchRow, { backgroundColor: theme.colors.input.background }]}>
266
+ <Ionicons name="search" size={16} color={theme.colors.textSecondary} />
267
+ <TextInput
268
+ value={search}
269
+ onChangeText={setSearch}
270
+ placeholder={searchPlaceholder ?? 'search...'}
271
+ placeholderTextColor={theme.colors.textSecondary}
272
+ style={[pickerStyles.searchInput, { color: theme.colors.text }]}
273
+ autoCapitalize="none"
274
+ autoCorrect={false}
275
+ />
276
+ </View>
277
+
278
+ <ScrollView style={pickerStyles.optionList} keyboardShouldPersistTaps="handled">
279
+ {fixedItems?.map(renderOption)}
280
+ {fixedItems && fixedItems.length > 0 && filtered.length > 0 && (
281
+ <View style={[pickerStyles.divider, { backgroundColor: theme.colors.divider }]} />
282
+ )}
283
+ {filtered.map(renderOption)}
284
+ {filtered.length === 0 && search.length > 0 && (
285
+ <Text style={[pickerStyles.emptyText, { color: theme.colors.textSecondary }]}>
286
+ no results
287
+ </Text>
288
+ )}
289
+ </ScrollView>
290
+ </View>
291
+ );
292
+ }
293
+
294
+ function PathPickerContent({
295
+ title,
296
+ items,
297
+ value,
298
+ homeDir,
299
+ onChangeValue,
300
+ onDone,
301
+ }: {
302
+ title: string;
303
+ items: PickerItem[];
304
+ value: string | null;
305
+ homeDir?: string;
306
+ onChangeValue: (value: string) => void;
307
+ onDone?: () => void;
308
+ }) {
309
+ const { theme } = useUnistyles();
310
+ const inputRef = React.useRef<TextInput>(null);
311
+ const currentValue = value ?? '';
312
+ const [selection, setSelection] = React.useState<{ start: number; end: number } | undefined>(undefined);
313
+
314
+ React.useEffect(() => {
315
+ const timeout = setTimeout(() => {
316
+ inputRef.current?.focus();
317
+ }, 50);
318
+ return () => clearTimeout(timeout);
319
+ }, []);
320
+
321
+ const matchedItemKey = React.useMemo(() => {
322
+ const normalizedValue = normalizePathForComparison(currentValue, homeDir);
323
+ if (!normalizedValue) {
324
+ return null;
325
+ }
326
+
327
+ const match = items.find((item) =>
328
+ normalizePathForComparison(item.key, homeDir) === normalizedValue,
329
+ );
330
+
331
+ return match?.key ?? null;
332
+ }, [currentValue, homeDir, items]);
333
+
334
+ const handleSuggestionPress = React.useCallback((item: PickerItem) => {
335
+ const nextValue = item.label;
336
+ const nextSelection = { start: nextValue.length, end: nextValue.length };
337
+
338
+ onChangeValue(nextValue);
339
+ setSelection(nextSelection);
340
+
341
+ setTimeout(() => {
342
+ inputRef.current?.focus();
343
+ }, 0);
344
+ }, [onChangeValue]);
345
+
346
+ const isCustomPath = currentValue.trim().length > 0 && matchedItemKey === null;
347
+ const handleSelectionChange = React.useCallback((event: NativeSyntheticEvent<TextInputSelectionChangeEventData>) => {
348
+ setSelection(event.nativeEvent.selection);
349
+ }, []);
350
+ const doneIconColor = theme.colors.header.tint;
351
+
352
+ return (
353
+ <View style={pickerStyles.container}>
354
+ <View style={pickerStyles.titleRow}>
355
+ <Text style={[pickerStyles.title, { color: theme.colors.text }]}>{title}</Text>
356
+ {Platform.OS !== 'web' && onDone && (
357
+ <Pressable
358
+ onPress={onDone}
359
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
360
+ style={({ pressed }) => [
361
+ pickerStyles.doneButtonPressable,
362
+ { opacity: pressed ? 0.82 : 1 },
363
+ ]}
364
+ accessibilityRole="button"
365
+ accessibilityLabel="Done"
366
+ >
367
+ <GlassView
368
+ glassEffectStyle="regular"
369
+ tintColor="rgba(255,255,255,0.10)"
370
+ isInteractive={true}
371
+ style={[
372
+ pickerStyles.doneButtonGlass,
373
+ { borderColor: 'rgba(255,255,255,0.16)' },
374
+ ]}
375
+ >
376
+ <Ionicons
377
+ name="checkmark"
378
+ size={20}
379
+ color={doneIconColor}
380
+ />
381
+ </GlassView>
382
+ </Pressable>
383
+ )}
384
+ </View>
385
+
386
+ <View
387
+ style={[
388
+ pickerStyles.pathInputRow,
389
+ {
390
+ backgroundColor: theme.colors.input.background,
391
+ borderColor: theme.colors.divider,
392
+ },
393
+ ]}
394
+ >
395
+ <Ionicons name="folder-outline" size={16} color={theme.colors.textSecondary} />
396
+ <View style={pickerStyles.pathInputField}>
397
+ <TextInput
398
+ ref={inputRef}
399
+ value={currentValue}
400
+ onChangeText={onChangeValue}
401
+ onSelectionChange={handleSelectionChange}
402
+ selection={selection}
403
+ placeholder="Enter project path"
404
+ placeholderTextColor={theme.colors.textSecondary}
405
+ style={[pickerStyles.pathTextInput, { color: theme.colors.text }]}
406
+ autoCapitalize="none"
407
+ autoCorrect={false}
408
+ multiline={false}
409
+ numberOfLines={1}
410
+ returnKeyType="done"
411
+ onSubmitEditing={onDone}
412
+ />
413
+ </View>
414
+ </View>
415
+
416
+ {isCustomPath && (
417
+ <Text style={[pickerStyles.pathMetaText, { color: theme.colors.textSecondary }]}>
418
+ using custom path above
419
+ </Text>
420
+ )}
421
+
422
+ <Text style={[pickerStyles.sectionLabel, { color: theme.colors.textSecondary }]}>
423
+ Recent
424
+ </Text>
425
+
426
+ <ScrollView style={pickerStyles.optionList} keyboardShouldPersistTaps="handled">
427
+ {items.map((item) => {
428
+ const isSelected = item.key === matchedItemKey;
429
+
430
+ return (
431
+ <Pressable
432
+ key={item.key}
433
+ style={(p) => [pickerStyles.option, p.pressed && pickerStyles.optionPressed]}
434
+ onPress={() => handleSuggestionPress(item)}
435
+ >
436
+ <Ionicons
437
+ name="folder-outline"
438
+ size={16}
439
+ color={theme.colors.textSecondary}
440
+ />
441
+ <View style={{ flex: 1 }}>
442
+ <Text style={[pickerStyles.optionText, { color: theme.colors.text }]}>
443
+ {item.label}
444
+ </Text>
445
+ </View>
446
+ {isSelected && (
447
+ <Ionicons
448
+ name="checkmark-circle"
449
+ size={18}
450
+ color={theme.colors.button.primary.background}
451
+ />
452
+ )}
453
+ </Pressable>
454
+ );
455
+ })}
456
+
457
+ {items.length === 0 && (
458
+ <Text style={[pickerStyles.emptyText, { color: theme.colors.textSecondary }]}>
459
+ no recent projects yet
460
+ </Text>
461
+ )}
462
+ </ScrollView>
463
+ </View>
464
+ );
465
+ }
466
+
467
+ // Helper: get machine display name
468
+ function getMachineName(machine: Machine): string {
469
+ return machine.metadata?.displayName || machine.metadata?.host || 'unknown';
470
+ }
471
+
472
+ // Owns the `input` subscription so the parent screen can stay decoupled from
473
+ // keystroke-rate state changes. Memoized: parent re-renders (e.g. when
474
+ // `canSend` flips or a picker opens) won't force the input to re-render
475
+ // because all of its props are stable.
476
+ type PromptInputProps = {
477
+ placeholder: string;
478
+ onKeyPress?: (e: KeyPressEvent) => boolean;
479
+ };
480
+ const PromptInput = React.memo(React.forwardRef<MultiTextInputHandle, PromptInputProps>(
481
+ function PromptInput(props, ref) {
482
+ const value = useNewSessionDraft((s) => s.input);
483
+ const onChangeText = useNewSessionDraft((s) => s.setInput);
484
+ return (
485
+ <MultiTextInput
486
+ ref={ref}
487
+ value={value}
488
+ onChangeText={onChangeText}
489
+ placeholder={props.placeholder}
490
+ lineHeight={MULTI_TEXT_INPUT_LINE_HEIGHT}
491
+ paddingTop={COMPOSER_INPUT_VERTICAL_PADDING}
492
+ paddingBottom={COMPOSER_INPUT_VERTICAL_PADDING}
493
+ maxHeight={COMPOSER_INPUT_MAX_HEIGHT}
494
+ onKeyPress={props.onKeyPress}
495
+ />
496
+ );
497
+ },
498
+ ));
499
+
500
+ function NewSessionScreen() {
501
+ const { theme } = useUnistyles();
502
+ const safeArea = useSafeAreaInsets();
503
+ const headerHeight = useHeaderHeight();
504
+ const router = useRouter();
505
+ const navigateToSession = useNavigateToSession();
506
+
507
+ // Real data sources
508
+ const allMachines = useAllMachines({ includeOffline: true });
509
+ const sessions = useSessions();
510
+ const agentInputEnterToSend = useSetting('agentInputEnterToSend');
511
+ const agentDefaultOverrides = useSetting('agentDefaultOverrides');
512
+
513
+ // Persisted draft state (survives navigation).
514
+ //
515
+ // We deliberately do NOT subscribe to `input` at the parent level here:
516
+ // typing flips `input` on every keystroke, and a parent re-render would
517
+ // cascade through the whole config box, machine/path pickers, and all
518
+ // the heavy `useMemo`s below. Instead, the input subtree (PromptInput)
519
+ // owns the subscription, the parent only listens to a derived
520
+ // `hasText` boolean for the auto-collapse effect, and `handleSend`
521
+ // reads the live value via `useNewSessionDraft.getState()` on demand.
522
+ const draft = useNewSessionDraft(useShallow((s) => ({
523
+ selectedMachineId: s.selectedMachineId,
524
+ setMachineId: s.setMachineId,
525
+ selectedPath: s.selectedPath,
526
+ setPath: s.setPath,
527
+ agentType: s.agentType,
528
+ setAgentType: s.setAgentType,
529
+ permissionMode: s.permissionMode,
530
+ setPermissionMode: s.setPermissionMode,
531
+ modelMode: s.modelMode,
532
+ setModelMode: s.setModelMode,
533
+ sessionType: s.sessionType,
534
+ setSessionType: s.setSessionType,
535
+ worktreeKey: s.worktreeKey,
536
+ setWorktreeKey: s.setWorktreeKey,
537
+ })));
538
+ const hasText = useNewSessionDraft((s) => s.input.trim().length > 0);
539
+ const selectedAgent = draft.agentType;
540
+ const setSelectedAgent = draft.setAgentType;
541
+ const selectedMachineId = draft.selectedMachineId;
542
+ const setSelectedMachineId = draft.setMachineId;
543
+ const selectedPath = draft.selectedPath;
544
+ const setSelectedPath = draft.setPath;
545
+ const [worktreeKey, setWorktreeKey] = React.useState<string>(
546
+ draft.worktreeKey ?? (draft.sessionType === 'worktree' ? '__new__' : '__none__')
547
+ );
548
+ React.useEffect(() => {
549
+ draft.setSessionType(worktreeKey !== '__none__' ? 'worktree' : 'simple');
550
+ draft.setWorktreeKey(worktreeKey === '__none__' || worktreeKey === '__new__' ? null : worktreeKey);
551
+ }, [worktreeKey]);
552
+
553
+ // Local-only UI state (not persisted)
554
+ const [permissionIndex, setPermissionIndex] = React.useState(0);
555
+ const [modelIndex, setModelIndex] = React.useState(0);
556
+ const [effortIndex, setEffortIndex] = React.useState(0);
557
+ const [isSpawning, setIsSpawning] = React.useState(false);
558
+ const [activePicker, setActivePicker] = React.useState<PickerType | null>(null);
559
+
560
+ // Config collapse — auto-collapses when typing, expands when empty
561
+ const [isConfigExpanded, setIsConfigExpanded] = React.useState(true);
562
+
563
+ // Auto-select first machine when none selected (first-ever use, no draft)
564
+ React.useEffect(() => {
565
+ if (selectedMachineId) return;
566
+ if (allMachines.length > 0) {
567
+ setSelectedMachineId(allMachines[0].id);
568
+ }
569
+ }, [allMachines, selectedMachineId]);
570
+
571
+ const selectedMachine = React.useMemo(
572
+ () => allMachines.find(m => m.id === selectedMachineId) ?? null,
573
+ [allMachines, selectedMachineId],
574
+ );
575
+ const selectedHomeDir = selectedMachine?.metadata?.homeDir;
576
+
577
+ // Build machine picker items: online first, then offline
578
+ const machineItems = React.useMemo<PickerItem[]>(() => {
579
+ const sorted = [...allMachines].sort((a, b) => {
580
+ const aOnline = isMachineOnline(a) ? 0 : 1;
581
+ const bOnline = isMachineOnline(b) ? 0 : 1;
582
+ return aOnline - bOnline;
583
+ });
584
+ return sorted.map(m => ({
585
+ key: m.id,
586
+ label: getMachineName(m),
587
+ subtitle: isMachineOnline(m) ? t('status.online') : t('status.lastSeen', { time: formatLastSeen(m.activeAt, false) }),
588
+ dimmed: !isMachineOnline(m),
589
+ }));
590
+ }, [allMachines]);
591
+
592
+ // Build path items from session history for selected machine
593
+ const pathItems = React.useMemo<PickerItem[]>(() => {
594
+ if (!selectedMachineId || !sessions) return [];
595
+ const paths = new Set<string>();
596
+ for (const s of sessions) {
597
+ if (typeof s === 'string') continue;
598
+ const session = s as Session;
599
+ if (session.metadata?.machineId === selectedMachineId && session.metadata?.path) {
600
+ paths.add(session.metadata.path);
601
+ }
602
+ }
603
+ const homeDir = selectedMachine?.metadata?.homeDir;
604
+ return Array.from(paths).sort().map(p => ({
605
+ key: p,
606
+ label: formatPathRelativeToHome(p, homeDir),
607
+ }));
608
+ }, [selectedMachineId, sessions, selectedMachine]);
609
+
610
+ // Auto-select first path when machine changes
611
+ React.useEffect(() => {
612
+ if (!selectedMachineId || selectedPath !== null) {
613
+ return;
614
+ }
615
+
616
+ setSelectedPath(pathItems[0]?.label ?? '~');
617
+ }, [selectedMachineId, pathItems, selectedPath, setSelectedPath]);
618
+
619
+ const resolvedSelectedPath = React.useMemo(() => {
620
+ return normalizePathForComparison(selectedPath, selectedHomeDir);
621
+ }, [selectedHomeDir, selectedPath]);
622
+
623
+ const [debouncedResolvedSelectedPath, setDebouncedResolvedSelectedPath] = React.useState<string | null>(resolvedSelectedPath);
624
+
625
+ React.useEffect(() => {
626
+ if (!resolvedSelectedPath) {
627
+ setDebouncedResolvedSelectedPath(null);
628
+ return;
629
+ }
630
+
631
+ const timeout = setTimeout(() => {
632
+ setDebouncedResolvedSelectedPath(resolvedSelectedPath);
633
+ }, WORKTREE_PATH_DEBOUNCE_MS);
634
+
635
+ return () => clearTimeout(timeout);
636
+ }, [resolvedSelectedPath]);
637
+
638
+ // Fetch existing worktrees from the selected machine/path
639
+ const [worktreeItems, setWorktreeItems] = React.useState<PickerItem[]>([]);
640
+ React.useEffect(() => {
641
+ if (!selectedMachineId || !debouncedResolvedSelectedPath) {
642
+ setWorktreeItems([]);
643
+ return;
644
+ }
645
+ if (!selectedMachine || !isMachineOnline(selectedMachine)) {
646
+ setWorktreeItems([]);
647
+ return;
648
+ }
649
+ let cancelled = false;
650
+ listWorktrees(selectedMachineId, debouncedResolvedSelectedPath).then(worktrees => {
651
+ if (cancelled) return;
652
+ setWorktreeItems(worktrees.map(wt => ({
653
+ key: wt.path,
654
+ label: wt.branch,
655
+ subtitle: wt.path,
656
+ })));
657
+ });
658
+ return () => { cancelled = true; };
659
+ }, [debouncedResolvedSelectedPath, selectedMachineId, selectedMachine]);
660
+
661
+ React.useEffect(() => {
662
+ if (worktreeKey === '__none__' || worktreeKey === '__new__') {
663
+ return;
664
+ }
665
+
666
+ if (!worktreeItems.some((item) => item.key === worktreeKey)) {
667
+ setWorktreeKey('__none__');
668
+ }
669
+ }, [worktreeItems, worktreeKey]);
670
+
671
+ // Filter available agents based on CLI availability from machine metadata
672
+ const availableAgents = React.useMemo(() => {
673
+ const availability = selectedMachine?.metadata?.cliAvailability;
674
+ if (!availability) return ALL_AGENTS;
675
+ return ALL_AGENTS.filter(a => availability[a.key]);
676
+ }, [selectedMachine]);
677
+
678
+ // If current agent not available on this machine, switch to first available
679
+ React.useEffect(() => {
680
+ if (availableAgents.length > 0 && !availableAgents.find(a => a.key === selectedAgent)) {
681
+ setSelectedAgent(availableAgents[0].key);
682
+ }
683
+ }, [availableAgents, selectedAgent, setSelectedAgent]);
684
+
685
+ // Derive options from agent type
686
+ const permissionModes = React.useMemo<PermissionMode[]>(
687
+ () => getHardcodedPermissionModes(selectedAgent, t),
688
+ [selectedAgent],
689
+ );
690
+ const modelModes = React.useMemo<ModelMode[]>(
691
+ () => getHardcodedModelModes(selectedAgent, t),
692
+ [selectedAgent],
693
+ );
694
+
695
+ const currentModel = modelModes[modelIndex] ?? modelModes[0];
696
+ const currentModelKey = currentModel?.key ?? 'default';
697
+
698
+ const effortLevels = React.useMemo<EffortLevel[]>(
699
+ () => getEffortLevelsForModel(selectedAgent, currentModelKey),
700
+ [selectedAgent, currentModelKey],
701
+ );
702
+ const effectiveAgentDefaults = React.useMemo(() => (
703
+ resolveAgentDefaultConfig(agentDefaultOverrides, selectedAgent)
704
+ ), [agentDefaultOverrides, selectedAgent]);
705
+
706
+ const supportsWorktree = getSupportsWorktree(selectedAgent);
707
+ const showModel = modelModes.length > 1;
708
+ const showEffort = effortLevels.length > 0;
709
+ const showPermission = permissionModes.length > 1;
710
+
711
+ // Reset indices when agent/default settings change.
712
+ React.useEffect(() => {
713
+ const defaultPermIdx = permissionModes.findIndex(m => m.key === effectiveAgentDefaults.permissionMode);
714
+ setPermissionIndex(defaultPermIdx >= 0 ? defaultPermIdx : 0);
715
+
716
+ const defaultModelIdx = modelModes.findIndex(m => m.key === effectiveAgentDefaults.modelMode);
717
+ setModelIndex(defaultModelIdx >= 0 ? defaultModelIdx : 0);
718
+
719
+ if (!supportsWorktree) setWorktreeKey('__none__');
720
+ }, [permissionModes, modelModes, supportsWorktree, effectiveAgentDefaults.permissionMode, effectiveAgentDefaults.modelMode]);
721
+
722
+ // Reset effort when model changes
723
+ React.useEffect(() => {
724
+ const defaultEffort = effectiveAgentDefaults.effortLevel;
725
+ if (defaultEffort && effortLevels.length > 0) {
726
+ const idx = effortLevels.findIndex(e => e.key === defaultEffort);
727
+ setEffortIndex(idx >= 0 ? idx : effortLevels.length - 1);
728
+ } else {
729
+ setEffortIndex(0);
730
+ }
731
+ }, [effectiveAgentDefaults.effortLevel, currentModelKey, effortLevels]);
732
+
733
+ // Auto collapse config once when user starts typing (mobile only)
734
+ // On desktop (web / Mac Catalyst) the panel stays expanded
735
+ // Also skip collapsing on the initial render when draft text is restored
736
+ const hasCollapsedOnceRef = React.useRef(false);
737
+ const isInitialRef = React.useRef(true);
738
+ const isDesktop = Platform.OS === 'web' || isRunningOnMac();
739
+ React.useEffect(() => {
740
+ if (isInitialRef.current) {
741
+ isInitialRef.current = false;
742
+ return;
743
+ }
744
+ if (isDesktop) return;
745
+ if (hasText && !hasCollapsedOnceRef.current) {
746
+ hasCollapsedOnceRef.current = true;
747
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
748
+ setIsConfigExpanded(false);
749
+ }
750
+ }, [hasText]);
751
+
752
+
753
+ const toggleConfig = React.useCallback(() => {
754
+ LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
755
+ setIsConfigExpanded(v => !v);
756
+ }, []);
757
+
758
+ const togglePicker = React.useCallback((type: PickerType) => {
759
+ setActivePicker(v => v === type ? null : type);
760
+ }, []);
761
+
762
+ const cyclePermission = React.useCallback(() => {
763
+ setPermissionIndex(i => {
764
+ const next = (i + 1) % permissionModes.length;
765
+ draft.setPermissionMode(permissionModes[next]?.key ?? 'default');
766
+ return next;
767
+ });
768
+ }, [permissionModes, draft.setPermissionMode]);
769
+
770
+ const cycleModel = React.useCallback(() => {
771
+ setModelIndex(i => {
772
+ const next = (i + 1) % modelModes.length;
773
+ draft.setModelMode(modelModes[next]?.key ?? 'default');
774
+ return next;
775
+ });
776
+ }, [modelModes, draft.setModelMode]);
777
+
778
+ const cycleEffort = React.useCallback(() => {
779
+ setEffortIndex(i => (i + 1) % effortLevels.length);
780
+ }, [effortLevels.length]);
781
+
782
+ const cycleAgent = React.useCallback(() => {
783
+ const idx = availableAgents.findIndex(a => a.key === selectedAgent);
784
+ const next = availableAgents[(idx + 1) % availableAgents.length].key;
785
+ setSelectedAgent(next);
786
+ }, [availableAgents, selectedAgent, setSelectedAgent]);
787
+
788
+ const isOffline = selectedMachine ? !isMachineOnline(selectedMachine) : false;
789
+ const agent = availableAgents.find(a => a.key === selectedAgent) ?? ALL_AGENTS[0];
790
+ const currentPermission = permissionModes[permissionIndex] ?? permissionModes[0];
791
+ const currentEffort = effortLevels[effortIndex] ?? effortLevels[0];
792
+ const permissionStyle = currentPermission?.key !== 'default' ? getPermissionStyle(currentPermission.key) : null;
793
+
794
+ // Display values
795
+ const machineName = selectedMachine ? getMachineName(selectedMachine) : 'Select machine';
796
+ const pathName = trimPathInput(selectedPath)
797
+ ? formatPathRelativeToHome(trimPathInput(selectedPath), selectedHomeDir)
798
+ : '~';
799
+ const worktreeLabel = worktreeKey === '__none__'
800
+ ? 'no worktree'
801
+ : worktreeKey === '__new__'
802
+ ? 'new worktree'
803
+ : worktreeItems.find(wt => wt.key === worktreeKey)?.label || worktreeKey;
804
+
805
+ // Flash label for collapsed icon taps — shows label briefly above the icon
806
+ const flashOpacity = React.useRef(new Animated.Value(0)).current;
807
+ const [flashText, setFlashText] = React.useState('');
808
+ const flashTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
809
+
810
+ const showFlash = React.useCallback((text: string) => {
811
+ if (flashTimerRef.current) clearTimeout(flashTimerRef.current);
812
+ setFlashText(text);
813
+ flashOpacity.setValue(0);
814
+ Animated.timing(flashOpacity, { toValue: 1, duration: 120, useNativeDriver: true }).start();
815
+ flashTimerRef.current = setTimeout(() => {
816
+ Animated.timing(flashOpacity, { toValue: 0, duration: 300, useNativeDriver: true }).start();
817
+ }, 800);
818
+ }, [flashOpacity]);
819
+
820
+ // Picker data derived from active picker type
821
+ const pickerData = React.useMemo(() => {
822
+ switch (activePicker) {
823
+ case 'machine':
824
+ return { title: 'Machine', items: machineItems, selectedKey: selectedMachineId, searchPlaceholder: 'search machines...' };
825
+ case 'worktree':
826
+ return { title: 'Worktree', fixedItems: WORKTREE_FIXED_ITEMS, items: worktreeItems, selectedKey: worktreeKey, searchPlaceholder: 'search worktrees...' };
827
+ default:
828
+ return null;
829
+ }
830
+ }, [activePicker, machineItems, selectedMachineId, worktreeKey, worktreeItems]);
831
+
832
+ const handlePickerSelect = React.useCallback((key: string) => {
833
+ switch (activePicker) {
834
+ case 'machine':
835
+ setSelectedMachineId(key);
836
+ break;
837
+ case 'worktree':
838
+ setWorktreeKey(key);
839
+ break;
840
+ }
841
+ setActivePicker(null);
842
+ }, [activePicker, setSelectedMachineId, setWorktreeKey]);
843
+
844
+ // Spawn session handler
845
+ const handleSend = React.useCallback(async (approvedNewDirectoryCreation: boolean = false) => {
846
+ if (!selectedMachineId || !selectedMachine) {
847
+ Modal.alert(t('common.error'), 'Please select a machine');
848
+ return;
849
+ }
850
+ if (!isMachineOnline(selectedMachine)) {
851
+ Modal.alert(t('common.error'), 'Machine is offline');
852
+ return;
853
+ }
854
+
855
+ setIsSpawning(true);
856
+ try {
857
+ const pathToUse = trimPathInput(selectedPath) || '~';
858
+ const absolutePath = resolveAbsolutePath(pathToUse, selectedMachine.metadata?.homeDir);
859
+
860
+ // Handle worktree selection
861
+ let spawnDirectory = absolutePath;
862
+ if (worktreeKey === '__new__') {
863
+ const worktreeResult = await createWorktree(selectedMachineId, absolutePath);
864
+ if (!worktreeResult.success) {
865
+ Modal.alert(t('common.error'), worktreeResult.error || 'Failed to create worktree');
866
+ return;
867
+ }
868
+ spawnDirectory = worktreeResult.worktreePath;
869
+ } else if (worktreeKey !== '__none__') {
870
+ // Existing worktree — use its path directly
871
+ spawnDirectory = worktreeKey;
872
+ }
873
+
874
+ const result = await machineSpawnNewSession({
875
+ machineId: selectedMachineId,
876
+ directory: spawnDirectory,
877
+ approvedNewDirectoryCreation,
878
+ agent: selectedAgent,
879
+ });
880
+
881
+ switch (result.type) {
882
+ case 'success':
883
+ await sync.refreshSessions();
884
+
885
+ // Store only per-session overrides. Matching the effective
886
+ // default stays null so future code default changes apply.
887
+ const permissionOverride = currentPermission.key === effectiveAgentDefaults.permissionMode
888
+ ? null
889
+ : currentPermission.key;
890
+ const modelOverride = currentModelKey === effectiveAgentDefaults.modelMode
891
+ ? null
892
+ : currentModelKey;
893
+ const currentEffortKey = currentEffort?.key ?? null;
894
+ const effortOverride = currentEffortKey === effectiveAgentDefaults.effortLevel
895
+ ? null
896
+ : currentEffortKey;
897
+ storage.getState().updateSessionPermissionMode(result.sessionId, permissionOverride);
898
+ storage.getState().updateSessionModelMode(result.sessionId, modelOverride);
899
+ storage.getState().updateSessionEffortLevel(result.sessionId, effortOverride);
900
+
901
+ // Pull live prompt and clear it. We read via getState() so this
902
+ // callback doesn't have to subscribe to `input` (which would
903
+ // re-render the screen on every keystroke).
904
+ const draftState = useNewSessionDraft.getState();
905
+ const trimmedPrompt = draftState.input.trim();
906
+ draftState.setInput('');
907
+
908
+ // Send initial message if provided
909
+ if (trimmedPrompt) {
910
+ await sync.sendMessage(result.sessionId, trimmedPrompt, { source: 'new_session' });
911
+ }
912
+
913
+ router.back();
914
+ navigateToSession(result.sessionId);
915
+ break;
916
+ case 'requestToApproveDirectoryCreation': {
917
+ const approved = await Modal.confirm(
918
+ 'Create Directory?',
919
+ `The directory '${result.directory}' does not exist. Would you like to create it?`,
920
+ { cancelText: t('common.cancel'), confirmText: t('common.create') },
921
+ );
922
+ if (approved) {
923
+ await handleSend(true);
924
+ }
925
+ break;
926
+ }
927
+ case 'error':
928
+ Modal.alert(t('common.error'), result.errorMessage);
929
+ break;
930
+ }
931
+ } catch (error) {
932
+ const errorMessage = error instanceof Error
933
+ ? error.message
934
+ : 'Failed to start session';
935
+ Modal.alert(t('common.error'), errorMessage);
936
+ } finally {
937
+ setIsSpawning(false);
938
+ }
939
+ }, [selectedMachineId, selectedMachine, selectedPath, selectedAgent, router, navigateToSession, currentPermission.key, currentModelKey, currentEffort?.key, effectiveAgentDefaults.permissionMode, effectiveAgentDefaults.modelMode, effectiveAgentDefaults.effortLevel, worktreeKey]);
940
+
941
+ const canSend = selectedMachineId && selectedMachine && isMachineOnline(selectedMachine) && !isSpawning;
942
+
943
+ // Handle Enter/Cmd+Enter to send on web
944
+ const handleKeyPress = React.useCallback((event: KeyPressEvent): boolean => {
945
+ if (Platform.OS === 'web' && event.key === 'Enter' && !event.shiftKey && agentInputEnterToSend) {
946
+ if (canSend) {
947
+ handleSend();
948
+ return true;
949
+ }
950
+ }
951
+ return false;
952
+ }, [agentInputEnterToSend, canSend, handleSend]);
953
+
954
+ // Auto-focus the text input when the composer mounts
955
+ const composerInputRef = React.useRef<import('@/components/MultiTextInput').MultiTextInputHandle>(null);
956
+ React.useEffect(() => {
957
+ const timeout = setTimeout(() => {
958
+ composerInputRef.current?.focus();
959
+ }, 100);
960
+ return () => clearTimeout(timeout);
961
+ }, []);
962
+
963
+ return (
964
+ <KeyboardAvoidingView
965
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
966
+ keyboardVerticalOffset={Platform.OS === 'ios' ? Constants.statusBarHeight + headerHeight : 0}
967
+ style={styles.container}
968
+ >
969
+ <View style={styles.inner}>
970
+ <View style={{ maxWidth: layout.maxWidth, width: '100%', alignSelf: 'center', paddingHorizontal: 12, gap: 8, paddingTop: 12 }}>
971
+
972
+ {/* Config box */}
973
+ <View style={styles.configBox}>
974
+ {isConfigExpanded ? (
975
+ <>
976
+ {/* Machine row */}
977
+ <View style={styles.configRowWithToggle}>
978
+ <Pressable
979
+ style={(p) => [styles.configRow, { flex: 1 }, p.pressed && styles.configRowPressed]}
980
+ onPress={() => togglePicker('machine')}
981
+ >
982
+ <Ionicons name="desktop-outline" size={15} color={theme.colors.textSecondary} />
983
+ <Text style={styles.configLabel} numberOfLines={1}>
984
+ {machineName}
985
+ </Text>
986
+ </Pressable>
987
+ <Pressable
988
+ onPress={toggleConfig}
989
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
990
+ style={(p) => [styles.collapseToggle, p.pressed && styles.configRowPressed]}
991
+ >
992
+ <Ionicons name="chevron-up" size={16} color={theme.colors.textSecondary} />
993
+ </Pressable>
994
+ </View>
995
+
996
+ {/* Offline help section — right under machine */}
997
+ {isOffline && (
998
+ <View style={styles.offlineHelp}>
999
+ <Ionicons name="cloud-offline-outline" size={14} color={theme.colors.status.disconnected} />
1000
+ <View style={{ flex: 1 }}>
1001
+ <Text style={[styles.offlineHelpTitle, { color: theme.colors.status.disconnected }]}>
1002
+ {t('newSession.machineOffline')}
1003
+ </Text>
1004
+ <Text style={[styles.offlineHelpText, { color: theme.colors.textSecondary }]}>
1005
+ {t('machine.offlineHelp')}
1006
+ {'\n'}{t('newSession.switchMachinesHint')}
1007
+ </Text>
1008
+ </View>
1009
+ </View>
1010
+ )}
1011
+
1012
+ {/* Config rows below machine — grayed out when offline */}
1013
+ <View style={{ opacity: isOffline ? 0.4 : 1 }} pointerEvents={isOffline ? 'none' : 'auto'}>
1014
+ {/* Path row */}
1015
+ <Pressable
1016
+ style={(p) => [styles.configRow, p.pressed && styles.configRowPressed]}
1017
+ onPress={() => togglePicker('path')}
1018
+ >
1019
+ <Ionicons name="folder-outline" size={15} color={theme.colors.textSecondary} />
1020
+ <Text style={styles.configLabel} numberOfLines={1}>
1021
+ {pathName}
1022
+ </Text>
1023
+ </Pressable>
1024
+
1025
+ {/* Agent + model + effort row */}
1026
+ <View style={styles.configRow}>
1027
+ <Pressable
1028
+ onPress={cycleAgent}
1029
+ style={(p) => [{ flexDirection: 'row', alignItems: 'center', gap: 8 }, p.pressed && styles.configRowPressed]}
1030
+ >
1031
+ <RNImage
1032
+ source={agentIcons[agent.key]}
1033
+ style={[styles.agentIcon, { tintColor: theme.colors.textSecondary }]}
1034
+ resizeMode="contain"
1035
+ />
1036
+ <Text style={styles.configLabel} numberOfLines={1}>
1037
+ {agent.label}
1038
+ </Text>
1039
+ </Pressable>
1040
+
1041
+ {showModel && (
1042
+ <>
1043
+ <Text style={[styles.configLabel, { color: theme.colors.textSecondary }]}>·</Text>
1044
+ <Pressable onPress={cycleModel} style={(p) => [p.pressed && styles.configRowPressed]}>
1045
+ <Text style={[styles.configLabel, { color: theme.colors.textSecondary }]} numberOfLines={1}>
1046
+ {currentModel.name}
1047
+ </Text>
1048
+ </Pressable>
1049
+ </>
1050
+ )}
1051
+
1052
+ {showEffort && (
1053
+ <>
1054
+ <Text style={[styles.configLabel, { color: theme.colors.textSecondary }]}>·</Text>
1055
+ <Pressable onPress={cycleEffort} style={(p) => [p.pressed && styles.configRowPressed]}>
1056
+ <Text style={[styles.configLabel, { color: theme.colors.textSecondary }]} numberOfLines={1}>
1057
+ {currentEffort?.name}
1058
+ </Text>
1059
+ </Pressable>
1060
+ </>
1061
+ )}
1062
+ </View>
1063
+
1064
+ {/* Permission row */}
1065
+ {showPermission && (
1066
+ <Pressable
1067
+ style={(p) => [styles.configRow, p.pressed && styles.configRowPressed]}
1068
+ onPress={cyclePermission}
1069
+ >
1070
+ <Ionicons
1071
+ name={permissionStyle?.icon ?? 'shield-outline'}
1072
+ size={15}
1073
+ color={theme.colors.textSecondary}
1074
+ />
1075
+ <Text style={styles.configLabel} numberOfLines={1}>
1076
+ {currentPermission?.name}
1077
+ </Text>
1078
+ </Pressable>
1079
+ )}
1080
+
1081
+ {/* Worktree row */}
1082
+ {supportsWorktree && (
1083
+ <Pressable
1084
+ style={(p) => [styles.configRow, p.pressed && styles.configRowPressed]}
1085
+ onPress={() => togglePicker('worktree')}
1086
+ >
1087
+ <MaterialCommunityIcons name="tree" size={15} color={theme.colors.textSecondary} />
1088
+ <Text style={styles.configLabel} numberOfLines={1}>
1089
+ {worktreeLabel}
1090
+ </Text>
1091
+ </Pressable>
1092
+ )}
1093
+ </View>
1094
+
1095
+ </>
1096
+ ) : (
1097
+ /* Collapsed: path row + icons row + optional offline warning */
1098
+ <>
1099
+ {/* Path row with expand chevron */}
1100
+ <View style={styles.configRowWithToggle}>
1101
+ <Pressable
1102
+ style={(p) => [styles.collapsedRow, { flex: 1 }, p.pressed && styles.configRowPressed]}
1103
+ onPress={() => togglePicker('path')}
1104
+ >
1105
+ <Ionicons name="folder-outline" size={15} color={theme.colors.textSecondary} />
1106
+ <Text style={[styles.configLabel, { flex: 1 }]} numberOfLines={1}>
1107
+ {pathName}
1108
+ </Text>
1109
+ </Pressable>
1110
+ <Pressable
1111
+ onPress={toggleConfig}
1112
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
1113
+ style={(p) => [styles.collapseToggle, p.pressed && styles.configRowPressed]}
1114
+ >
1115
+ <Ionicons name="chevron-down" size={16} color={theme.colors.textSecondary} />
1116
+ </Pressable>
1117
+ </View>
1118
+
1119
+ {/* Tappable icons row: machine, agent, permission, worktree */}
1120
+ <View style={styles.collapsedIconsRow}>
1121
+ {/* Machine */}
1122
+ <Pressable
1123
+ onPress={() => togglePicker('machine')}
1124
+ hitSlop={{ top: 4, bottom: 4, left: 4, right: 4 }}
1125
+ style={(p) => [styles.collapsedIconButton, p.pressed && styles.configRowPressed]}
1126
+ >
1127
+ <Ionicons name="desktop-outline" size={14} color={isOffline ? theme.colors.status.disconnected : theme.colors.textSecondary} />
1128
+ </Pressable>
1129
+
1130
+ {/* Agent */}
1131
+ <Pressable
1132
+ onPress={() => { cycleAgent(); showFlash(availableAgents[(availableAgents.findIndex(a => a.key === selectedAgent) + 1) % availableAgents.length].label); }}
1133
+ hitSlop={{ top: 4, bottom: 4, left: 4, right: 4 }}
1134
+ style={(p) => [styles.collapsedIconButton, p.pressed && styles.configRowPressed]}
1135
+ >
1136
+ <RNImage
1137
+ source={agentIcons[agent.key]}
1138
+ style={[styles.collapsedAgentIcon, { tintColor: theme.colors.textSecondary }]}
1139
+ resizeMode="contain"
1140
+ />
1141
+ </Pressable>
1142
+
1143
+ {/* Permission */}
1144
+ {showPermission && (
1145
+ <Pressable
1146
+ onPress={() => { cyclePermission(); showFlash(permissionModes[(permissionIndex + 1) % permissionModes.length]?.name ?? 'default'); }}
1147
+ hitSlop={{ top: 4, bottom: 4, left: 4, right: 4 }}
1148
+ style={(p) => [styles.collapsedIconButton, p.pressed && styles.configRowPressed]}
1149
+ >
1150
+ <Ionicons
1151
+ name={permissionStyle?.icon ?? 'shield-outline'}
1152
+ size={14}
1153
+ color={permissionStyle?.color ?? theme.colors.textSecondary}
1154
+ />
1155
+ </Pressable>
1156
+ )}
1157
+
1158
+ {/* Worktree */}
1159
+ {supportsWorktree && (
1160
+ <Pressable
1161
+ onPress={() => togglePicker('worktree')}
1162
+ hitSlop={{ top: 4, bottom: 4, left: 4, right: 4 }}
1163
+ style={(p) => [styles.collapsedIconButton, p.pressed && styles.configRowPressed]}
1164
+ >
1165
+ <MaterialCommunityIcons name="tree" size={14} color={theme.colors.textSecondary} />
1166
+ </Pressable>
1167
+ )}
1168
+ </View>
1169
+
1170
+ {/* Offline warning in collapsed state */}
1171
+ {isOffline && (
1172
+ <View style={styles.offlineHelp}>
1173
+ <Ionicons name="cloud-offline-outline" size={14} color={theme.colors.status.disconnected} />
1174
+ <View style={{ flex: 1 }}>
1175
+ <Text style={[styles.offlineHelpTitle, { color: theme.colors.status.disconnected }]}>
1176
+ {t('newSession.machineOffline')}
1177
+ </Text>
1178
+ <Text style={[styles.offlineHelpText, { color: theme.colors.textSecondary }]}>
1179
+ {t('machine.offlineHelp')}
1180
+ {'\n'}{t('newSession.switchMachinesHint')}
1181
+ </Text>
1182
+ </View>
1183
+ </View>
1184
+ )}
1185
+ </>
1186
+ )}
1187
+ </View>
1188
+
1189
+ {/* Flash label — centered below config box, hidden when picker is open */}
1190
+ {flashText !== '' && !activePicker && (
1191
+ <Animated.View style={[styles.flashLabel, { opacity: flashOpacity }]} pointerEvents="none">
1192
+ <Text style={[styles.flashLabelText, { color: theme.colors.textSecondary }]}>{flashText}</Text>
1193
+ </Animated.View>
1194
+ )}
1195
+
1196
+ {/* Web: inline popover */}
1197
+ {Platform.OS === 'web' && activePicker && (
1198
+ <View style={[styles.popover, { backgroundColor: theme.colors.header.background }]}>
1199
+ {activePicker === 'path' ? (
1200
+ <PathPickerContent
1201
+ title="Project"
1202
+ items={pathItems}
1203
+ value={selectedPath}
1204
+ homeDir={selectedHomeDir}
1205
+ onChangeValue={setSelectedPath}
1206
+ onDone={() => setActivePicker(null)}
1207
+ />
1208
+ ) : pickerData ? (
1209
+ <PickerContent {...pickerData} onSelect={handlePickerSelect} />
1210
+ ) : null}
1211
+ </View>
1212
+ )}
1213
+ </View>
1214
+
1215
+ {/* Web: click-away backdrop */}
1216
+ {Platform.OS === 'web' && activePicker && (
1217
+ <Pressable
1218
+ style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, zIndex: -1 }}
1219
+ onPress={() => setActivePicker(null)}
1220
+ />
1221
+ )}
1222
+
1223
+ {/* Spacer */}
1224
+ <View style={{ flex: 1 }} />
1225
+
1226
+ <View style={{ maxWidth: layout.maxWidth, width: '100%', alignSelf: 'center', paddingHorizontal: 12, gap: 8 }}>
1227
+ {/* Input box */}
1228
+ <View style={styles.inputBox}>
1229
+ <View style={styles.inputField}>
1230
+ <View style={{ flex: 1 }}>
1231
+ <PromptInput
1232
+ ref={composerInputRef}
1233
+ placeholder="What would you like to work on?"
1234
+ onKeyPress={handleKeyPress}
1235
+ />
1236
+ </View>
1237
+ <View style={[
1238
+ styles.sendButton,
1239
+ canSend ? styles.sendButtonActive : styles.sendButtonInactive,
1240
+ ]}>
1241
+ <Pressable
1242
+ style={(p) => ({
1243
+ width: '100%',
1244
+ height: '100%',
1245
+ alignItems: 'center',
1246
+ justifyContent: 'center',
1247
+ opacity: p.pressed ? 0.7 : 1,
1248
+ })}
1249
+ disabled={!canSend}
1250
+ onPress={() => handleSend()}
1251
+ >
1252
+ {isSpawning ? (
1253
+ <ActivityIndicator
1254
+ size="small"
1255
+ color={theme.colors.button.primary.tint}
1256
+ />
1257
+ ) : (
1258
+ <Octicons
1259
+ name="arrow-up"
1260
+ size={16}
1261
+ color={theme.colors.button.primary.tint}
1262
+ style={{ marginTop: Platform.OS === 'web' ? 2 : 0 }}
1263
+ />
1264
+ )}
1265
+ </Pressable>
1266
+ </View>
1267
+ </View>
1268
+ </View>
1269
+ </View>
1270
+
1271
+ <View style={{ height: Math.max(16, safeArea.bottom) }} />
1272
+ </View>
1273
+
1274
+ {/* Native: picker bottom sheet */}
1275
+ {Platform.OS !== 'web' && (
1276
+ <BottomSheet
1277
+ visible={!!activePicker}
1278
+ onClose={() => setActivePicker(null)}
1279
+ >
1280
+ {activePicker === 'path' ? (
1281
+ <PathPickerContent
1282
+ title="Project"
1283
+ items={pathItems}
1284
+ value={selectedPath}
1285
+ homeDir={selectedHomeDir}
1286
+ onChangeValue={setSelectedPath}
1287
+ onDone={() => setActivePicker(null)}
1288
+ />
1289
+ ) : pickerData ? (
1290
+ <PickerContent {...pickerData} onSelect={handlePickerSelect} />
1291
+ ) : null}
1292
+ </BottomSheet>
1293
+ )}
1294
+ </KeyboardAvoidingView>
1295
+ );
1296
+ }
1297
+
1298
+ const WORKTREE_FIXED_ITEMS: PickerItem[] = [
1299
+ { key: '__none__', label: 'no worktree' },
1300
+ { key: '__new__', label: 'new worktree' },
1301
+ ];
1302
+
1303
+ const styles = StyleSheet.create((theme) => ({
1304
+ container: {
1305
+ flex: 1,
1306
+ backgroundColor: theme.colors.header.background,
1307
+ },
1308
+ inner: {
1309
+ flex: 1,
1310
+ },
1311
+ configBox: {
1312
+ backgroundColor: theme.colors.input.background,
1313
+ borderRadius: Platform.select({ default: 16, android: 20 }),
1314
+ paddingVertical: 4,
1315
+ paddingHorizontal: 4,
1316
+ overflow: 'hidden',
1317
+ },
1318
+ popover: {
1319
+ borderRadius: 12,
1320
+ paddingVertical: 4,
1321
+ marginTop: 4,
1322
+ borderWidth: 1,
1323
+ borderColor: theme.colors.divider,
1324
+ ...Platform.select({
1325
+ web: {
1326
+ boxShadow: '0 4px 20px rgba(0, 0, 0, 0.12)',
1327
+ },
1328
+ default: {
1329
+ shadowColor: '#000',
1330
+ shadowOffset: { width: 0, height: 4 },
1331
+ shadowOpacity: 0.12,
1332
+ shadowRadius: 10,
1333
+ elevation: 8,
1334
+ },
1335
+ }),
1336
+ },
1337
+ configRow: {
1338
+ flexDirection: 'row',
1339
+ alignItems: 'center',
1340
+ gap: 8,
1341
+ paddingHorizontal: 12,
1342
+ paddingVertical: 10,
1343
+ borderRadius: 12,
1344
+ },
1345
+ configRowWithToggle: {
1346
+ flexDirection: 'row',
1347
+ alignItems: 'center',
1348
+ },
1349
+ collapseToggle: {
1350
+ paddingHorizontal: 12,
1351
+ paddingVertical: 10,
1352
+ justifyContent: 'center',
1353
+ alignItems: 'center',
1354
+ },
1355
+ collapsedRow: {
1356
+ flexDirection: 'row',
1357
+ alignItems: 'center',
1358
+ gap: 8,
1359
+ paddingHorizontal: 12,
1360
+ paddingVertical: 10,
1361
+ borderRadius: 12,
1362
+ },
1363
+ collapsedIconsRow: {
1364
+ flexDirection: 'row',
1365
+ alignItems: 'center',
1366
+ gap: 2,
1367
+ paddingHorizontal: 4,
1368
+ paddingBottom: 8,
1369
+ },
1370
+ collapsedIconButton: {
1371
+ width: 34,
1372
+ height: 28,
1373
+ borderRadius: 8,
1374
+ justifyContent: 'center',
1375
+ alignItems: 'center',
1376
+ },
1377
+ flashLabel: {
1378
+ alignSelf: 'center',
1379
+ paddingVertical: 4,
1380
+ },
1381
+ flashLabelText: {
1382
+ fontSize: 12,
1383
+ ...Typography.default(),
1384
+ },
1385
+ configRowPressed: {
1386
+ opacity: 0.6,
1387
+ },
1388
+ agentIcon: {
1389
+ width: 15,
1390
+ height: 15,
1391
+ },
1392
+ collapsedAgentIcon: {
1393
+ width: 14,
1394
+ height: 14,
1395
+ },
1396
+ configLabel: {
1397
+ fontSize: 14,
1398
+ color: theme.colors.text,
1399
+ ...Typography.default('semiBold'),
1400
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1401
+ },
1402
+ inputBox: {
1403
+ backgroundColor: theme.colors.input.background,
1404
+ borderRadius: Platform.select({ default: 16, android: 20 }),
1405
+ overflow: 'hidden',
1406
+ paddingVertical: 2,
1407
+ paddingHorizontal: 8,
1408
+ },
1409
+ inputField: {
1410
+ flexDirection: 'row',
1411
+ alignItems: 'flex-end',
1412
+ paddingLeft: 8,
1413
+ paddingRight: 4,
1414
+ paddingVertical: 4,
1415
+ minHeight: 40,
1416
+ gap: 8,
1417
+ },
1418
+ sendButton: {
1419
+ width: COMPOSER_SEND_BUTTON_SIZE,
1420
+ height: COMPOSER_SEND_BUTTON_SIZE,
1421
+ borderRadius: COMPOSER_SEND_BUTTON_SIZE / 2,
1422
+ justifyContent: 'center',
1423
+ alignItems: 'center',
1424
+ flexShrink: 0,
1425
+ marginBottom: COMPOSER_SEND_BUTTON_MARGIN_BOTTOM,
1426
+ },
1427
+ sendButtonActive: {
1428
+ backgroundColor: theme.colors.button.primary.background,
1429
+ },
1430
+ sendButtonInactive: {
1431
+ backgroundColor: theme.colors.button.primary.disabled,
1432
+ },
1433
+ offlineHelp: {
1434
+ flexDirection: 'row',
1435
+ alignItems: 'flex-start',
1436
+ gap: 8,
1437
+ paddingHorizontal: 12,
1438
+ paddingVertical: 10,
1439
+ borderRadius: 12,
1440
+ },
1441
+ offlineHelpTitle: {
1442
+ fontSize: 13,
1443
+ ...Typography.default('semiBold'),
1444
+ marginBottom: 4,
1445
+ },
1446
+ offlineHelpText: {
1447
+ fontSize: 12,
1448
+ lineHeight: 18,
1449
+ ...Typography.default(),
1450
+ },
1451
+ }));
1452
+
1453
+ // Bottom sheet styles
1454
+ const sheetStyles = {
1455
+ iosContainer: {
1456
+ flex: 1,
1457
+ } as const,
1458
+ handleRow: {
1459
+ alignItems: 'center' as const,
1460
+ paddingTop: 10,
1461
+ paddingBottom: 6,
1462
+ },
1463
+ handle: {
1464
+ width: 36,
1465
+ height: 4,
1466
+ borderRadius: 2,
1467
+ opacity: 0.3,
1468
+ },
1469
+ overlay: {
1470
+ flex: 1,
1471
+ justifyContent: 'flex-end' as const,
1472
+ },
1473
+ backdrop: {
1474
+ position: 'absolute' as const,
1475
+ top: 0,
1476
+ left: 0,
1477
+ right: 0,
1478
+ bottom: 0,
1479
+ backgroundColor: 'black',
1480
+ opacity: 0.4,
1481
+ },
1482
+ sheet: {
1483
+ borderTopLeftRadius: 16,
1484
+ borderTopRightRadius: 16,
1485
+ maxHeight: '70%' as const,
1486
+ },
1487
+ };
1488
+
1489
+ // Picker styles
1490
+ const pickerStyles = {
1491
+ container: {
1492
+ paddingHorizontal: 16,
1493
+ paddingBottom: 8,
1494
+ } as const,
1495
+ title: {
1496
+ fontSize: 18,
1497
+ paddingVertical: 12,
1498
+ paddingHorizontal: 4,
1499
+ ...Typography.default('semiBold'),
1500
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1501
+ } as const,
1502
+ titleRow: {
1503
+ flexDirection: 'row' as const,
1504
+ alignItems: 'center' as const,
1505
+ justifyContent: 'space-between' as const,
1506
+ },
1507
+ doneButtonPressable: {
1508
+ width: 44,
1509
+ height: 44,
1510
+ alignItems: 'center' as const,
1511
+ justifyContent: 'center' as const,
1512
+ },
1513
+ doneButtonGlass: {
1514
+ width: 40,
1515
+ height: 36,
1516
+ borderRadius: 18,
1517
+ alignItems: 'center' as const,
1518
+ justifyContent: 'center' as const,
1519
+ overflow: 'hidden' as const,
1520
+ borderWidth: 1,
1521
+ backgroundColor: 'rgba(255,255,255,0.08)',
1522
+ },
1523
+ searchRow: {
1524
+ flexDirection: 'row' as const,
1525
+ alignItems: 'center' as const,
1526
+ gap: 10,
1527
+ paddingHorizontal: 12,
1528
+ paddingVertical: 10,
1529
+ borderRadius: 12,
1530
+ marginBottom: 8,
1531
+ },
1532
+ searchInput: {
1533
+ flex: 1,
1534
+ fontSize: 15,
1535
+ padding: 0,
1536
+ ...Typography.default(),
1537
+ ...Platform.select({ web: { outlineStyle: 'none' } as any, default: {} }),
1538
+ } as const,
1539
+ pathInputRow: {
1540
+ flexDirection: 'row' as const,
1541
+ alignItems: 'center' as const,
1542
+ gap: 10,
1543
+ paddingHorizontal: 12,
1544
+ minHeight: 46,
1545
+ borderRadius: 12,
1546
+ marginBottom: 8,
1547
+ borderWidth: 1,
1548
+ },
1549
+ pathInputField: {
1550
+ flex: 1,
1551
+ } as const,
1552
+ pathTextInput: {
1553
+ fontSize: 16,
1554
+ minHeight: 44,
1555
+ paddingVertical: 0,
1556
+ ...Typography.default(),
1557
+ ...Platform.select({
1558
+ android: { textAlignVertical: 'center' as const },
1559
+ web: { outlineStyle: 'none' } as any,
1560
+ default: {},
1561
+ }),
1562
+ } as const,
1563
+ pathMetaText: {
1564
+ fontSize: 13,
1565
+ paddingHorizontal: 4,
1566
+ paddingBottom: 8,
1567
+ ...Typography.default(),
1568
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1569
+ } as const,
1570
+ sectionLabel: {
1571
+ fontSize: 13,
1572
+ paddingHorizontal: 4,
1573
+ paddingBottom: 8,
1574
+ ...Typography.default('semiBold'),
1575
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1576
+ } as const,
1577
+ option: {
1578
+ flexDirection: 'row' as const,
1579
+ alignItems: 'center' as const,
1580
+ gap: 12,
1581
+ paddingHorizontal: 12,
1582
+ paddingVertical: 12,
1583
+ borderRadius: 12,
1584
+ },
1585
+ optionPressed: {
1586
+ opacity: 0.6,
1587
+ } as const,
1588
+ optionText: {
1589
+ fontSize: 15,
1590
+ ...Typography.default(),
1591
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1592
+ } as const,
1593
+ divider: {
1594
+ height: 1,
1595
+ marginHorizontal: 12,
1596
+ marginVertical: 4,
1597
+ } as const,
1598
+ optionList: {
1599
+ flexGrow: 0,
1600
+ flexShrink: 1,
1601
+ } as const,
1602
+ emptyText: {
1603
+ fontSize: 14,
1604
+ textAlign: 'center' as const,
1605
+ paddingVertical: 20,
1606
+ ...Typography.default(),
1607
+ ...Platform.select({ web: { userSelect: 'none' } as any, default: {} }),
1608
+ } as const,
1609
+ };
1610
+
1611
+ export default React.memo(NewSessionScreen);