designmd-forge 1.2.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 (907) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/__tests__/designStore.test.js +41 -0
  4. package/dist/__tests__/linter.test.js +23 -0
  5. package/dist/__tests__/sectionParser.test.js +60 -0
  6. package/dist/__tests__/server.test.js +129 -0
  7. package/dist/constants.js +12 -0
  8. package/dist/data/authoring-guide.md +113 -0
  9. package/dist/data/designs/airbnb.md +545 -0
  10. package/dist/data/designs/airtable.md +554 -0
  11. package/dist/data/designs/apple.md +562 -0
  12. package/dist/data/designs/binance.md +634 -0
  13. package/dist/data/designs/bmw-m.md +503 -0
  14. package/dist/data/designs/bmw.md +544 -0
  15. package/dist/data/designs/bugatti.md +454 -0
  16. package/dist/data/designs/cal.md +542 -0
  17. package/dist/data/designs/card-heavy.md +250 -0
  18. package/dist/data/designs/claude.md +589 -0
  19. package/dist/data/designs/clay.md +541 -0
  20. package/dist/data/designs/clickhouse.md +544 -0
  21. package/dist/data/designs/cohere.md +451 -0
  22. package/dist/data/designs/coinbase.md +570 -0
  23. package/dist/data/designs/composio.md +506 -0
  24. package/dist/data/designs/content-first.md +236 -0
  25. package/dist/data/designs/cursor.md +537 -0
  26. package/dist/data/designs/data-dashboard.md +300 -0
  27. package/dist/data/designs/dell-1996.md +632 -0
  28. package/dist/data/designs/elevenlabs.md +504 -0
  29. package/dist/data/designs/enterprise-neutral.md +252 -0
  30. package/dist/data/designs/expo.md +526 -0
  31. package/dist/data/designs/ferrari.md +531 -0
  32. package/dist/data/designs/figma.md +578 -0
  33. package/dist/data/designs/framer.md +544 -0
  34. package/dist/data/designs/hashicorp.md +575 -0
  35. package/dist/data/designs/hp.md +670 -0
  36. package/dist/data/designs/ibm.md +550 -0
  37. package/dist/data/designs/intercom.md +546 -0
  38. package/dist/data/designs/ios-acorns-android.md +430 -0
  39. package/dist/data/designs/ios-acorns-expo.md +381 -0
  40. package/dist/data/designs/ios-acorns-swiftui.md +378 -0
  41. package/dist/data/designs/ios-acorns.md +384 -0
  42. package/dist/data/designs/ios-airbnb-android.md +658 -0
  43. package/dist/data/designs/ios-airbnb-expo.md +457 -0
  44. package/dist/data/designs/ios-airbnb-swiftui.md +464 -0
  45. package/dist/data/designs/ios-airbnb.md +410 -0
  46. package/dist/data/designs/ios-alltrails-android.md +451 -0
  47. package/dist/data/designs/ios-alltrails-expo.md +400 -0
  48. package/dist/data/designs/ios-alltrails-swiftui.md +343 -0
  49. package/dist/data/designs/ios-alltrails.md +344 -0
  50. package/dist/data/designs/ios-amazon-android.md +686 -0
  51. package/dist/data/designs/ios-amazon-expo.md +433 -0
  52. package/dist/data/designs/ios-amazon-music-android.md +416 -0
  53. package/dist/data/designs/ios-amazon-music-expo.md +368 -0
  54. package/dist/data/designs/ios-amazon-music-swiftui.md +436 -0
  55. package/dist/data/designs/ios-amazon-music.md +389 -0
  56. package/dist/data/designs/ios-amazon-swiftui.md +460 -0
  57. package/dist/data/designs/ios-amazon.md +440 -0
  58. package/dist/data/designs/ios-apple-fitness-android.md +462 -0
  59. package/dist/data/designs/ios-apple-fitness-expo.md +373 -0
  60. package/dist/data/designs/ios-apple-fitness-swiftui.md +373 -0
  61. package/dist/data/designs/ios-apple-fitness.md +399 -0
  62. package/dist/data/designs/ios-apple-maps-android.md +646 -0
  63. package/dist/data/designs/ios-apple-maps-expo.md +557 -0
  64. package/dist/data/designs/ios-apple-maps-swiftui.md +588 -0
  65. package/dist/data/designs/ios-apple-maps.md +447 -0
  66. package/dist/data/designs/ios-apple-music-android.md +737 -0
  67. package/dist/data/designs/ios-apple-music-expo.md +596 -0
  68. package/dist/data/designs/ios-apple-music-swiftui.md +578 -0
  69. package/dist/data/designs/ios-apple-music.md +494 -0
  70. package/dist/data/designs/ios-apple-notes-android.md +675 -0
  71. package/dist/data/designs/ios-apple-notes-expo.md +468 -0
  72. package/dist/data/designs/ios-apple-notes-swiftui.md +489 -0
  73. package/dist/data/designs/ios-apple-notes.md +413 -0
  74. package/dist/data/designs/ios-apple-tv-android.md +405 -0
  75. package/dist/data/designs/ios-apple-tv-expo.md +341 -0
  76. package/dist/data/designs/ios-apple-tv-swiftui.md +348 -0
  77. package/dist/data/designs/ios-apple-tv.md +390 -0
  78. package/dist/data/designs/ios-apple-wallet-android.md +617 -0
  79. package/dist/data/designs/ios-apple-wallet-expo.md +487 -0
  80. package/dist/data/designs/ios-apple-wallet-swiftui.md +425 -0
  81. package/dist/data/designs/ios-apple-wallet.md +360 -0
  82. package/dist/data/designs/ios-asana-android.md +435 -0
  83. package/dist/data/designs/ios-asana-expo.md +411 -0
  84. package/dist/data/designs/ios-asana-swiftui.md +416 -0
  85. package/dist/data/designs/ios-asana.md +433 -0
  86. package/dist/data/designs/ios-audible-android.md +450 -0
  87. package/dist/data/designs/ios-audible-expo.md +328 -0
  88. package/dist/data/designs/ios-audible-swiftui.md +418 -0
  89. package/dist/data/designs/ios-audible.md +364 -0
  90. package/dist/data/designs/ios-bandcamp-android.md +471 -0
  91. package/dist/data/designs/ios-bandcamp-expo.md +378 -0
  92. package/dist/data/designs/ios-bandcamp-swiftui.md +374 -0
  93. package/dist/data/designs/ios-bandcamp.md +372 -0
  94. package/dist/data/designs/ios-bear-android.md +496 -0
  95. package/dist/data/designs/ios-bear-expo.md +449 -0
  96. package/dist/data/designs/ios-bear-swiftui.md +479 -0
  97. package/dist/data/designs/ios-bear.md +412 -0
  98. package/dist/data/designs/ios-bereal-android.md +392 -0
  99. package/dist/data/designs/ios-bereal-expo.md +299 -0
  100. package/dist/data/designs/ios-bereal-swiftui.md +319 -0
  101. package/dist/data/designs/ios-bereal.md +336 -0
  102. package/dist/data/designs/ios-binance-android.md +481 -0
  103. package/dist/data/designs/ios-binance-expo.md +404 -0
  104. package/dist/data/designs/ios-binance-swiftui.md +418 -0
  105. package/dist/data/designs/ios-binance.md +410 -0
  106. package/dist/data/designs/ios-bluesky-android.md +442 -0
  107. package/dist/data/designs/ios-bluesky-expo.md +362 -0
  108. package/dist/data/designs/ios-bluesky-swiftui.md +328 -0
  109. package/dist/data/designs/ios-bluesky.md +338 -0
  110. package/dist/data/designs/ios-booking-android.md +471 -0
  111. package/dist/data/designs/ios-booking-expo.md +364 -0
  112. package/dist/data/designs/ios-booking-swiftui.md +377 -0
  113. package/dist/data/designs/ios-booking.md +346 -0
  114. package/dist/data/designs/ios-bumble-android.md +756 -0
  115. package/dist/data/designs/ios-bumble-expo.md +629 -0
  116. package/dist/data/designs/ios-bumble-swiftui.md +546 -0
  117. package/dist/data/designs/ios-bumble.md +424 -0
  118. package/dist/data/designs/ios-cal-ai-android.md +664 -0
  119. package/dist/data/designs/ios-cal-ai-expo.md +415 -0
  120. package/dist/data/designs/ios-cal-ai-swiftui.md +486 -0
  121. package/dist/data/designs/ios-cal-ai.md +387 -0
  122. package/dist/data/designs/ios-calm-android.md +431 -0
  123. package/dist/data/designs/ios-calm-expo.md +384 -0
  124. package/dist/data/designs/ios-calm-swiftui.md +327 -0
  125. package/dist/data/designs/ios-calm.md +356 -0
  126. package/dist/data/designs/ios-canva-android.md +469 -0
  127. package/dist/data/designs/ios-canva-expo.md +374 -0
  128. package/dist/data/designs/ios-canva-swiftui.md +422 -0
  129. package/dist/data/designs/ios-canva.md +395 -0
  130. package/dist/data/designs/ios-cash-app-android.md +601 -0
  131. package/dist/data/designs/ios-cash-app-expo.md +453 -0
  132. package/dist/data/designs/ios-cash-app-swiftui.md +463 -0
  133. package/dist/data/designs/ios-cash-app.md +365 -0
  134. package/dist/data/designs/ios-character-ai-android.md +489 -0
  135. package/dist/data/designs/ios-character-ai-expo.md +420 -0
  136. package/dist/data/designs/ios-character-ai-swiftui.md +417 -0
  137. package/dist/data/designs/ios-character-ai.md +392 -0
  138. package/dist/data/designs/ios-charles-schwab-android.md +454 -0
  139. package/dist/data/designs/ios-charles-schwab-expo.md +388 -0
  140. package/dist/data/designs/ios-charles-schwab-swiftui.md +394 -0
  141. package/dist/data/designs/ios-charles-schwab.md +380 -0
  142. package/dist/data/designs/ios-chatgpt-android.md +753 -0
  143. package/dist/data/designs/ios-chatgpt-expo.md +644 -0
  144. package/dist/data/designs/ios-chatgpt-swiftui.md +653 -0
  145. package/dist/data/designs/ios-chatgpt.md +466 -0
  146. package/dist/data/designs/ios-chime-android.md +438 -0
  147. package/dist/data/designs/ios-chime-expo.md +419 -0
  148. package/dist/data/designs/ios-chime-swiftui.md +383 -0
  149. package/dist/data/designs/ios-chime.md +389 -0
  150. package/dist/data/designs/ios-chipotle-android.md +397 -0
  151. package/dist/data/designs/ios-chipotle-expo.md +301 -0
  152. package/dist/data/designs/ios-chipotle-swiftui.md +345 -0
  153. package/dist/data/designs/ios-chipotle.md +340 -0
  154. package/dist/data/designs/ios-citymapper-android.md +466 -0
  155. package/dist/data/designs/ios-citymapper-expo.md +420 -0
  156. package/dist/data/designs/ios-citymapper-swiftui.md +424 -0
  157. package/dist/data/designs/ios-citymapper.md +394 -0
  158. package/dist/data/designs/ios-claude-android.md +625 -0
  159. package/dist/data/designs/ios-claude-expo.md +671 -0
  160. package/dist/data/designs/ios-claude-swiftui.md +649 -0
  161. package/dist/data/designs/ios-claude.md +447 -0
  162. package/dist/data/designs/ios-clickup-android.md +484 -0
  163. package/dist/data/designs/ios-clickup-expo.md +423 -0
  164. package/dist/data/designs/ios-clickup-swiftui.md +416 -0
  165. package/dist/data/designs/ios-clickup.md +431 -0
  166. package/dist/data/designs/ios-clubhouse-android.md +524 -0
  167. package/dist/data/designs/ios-clubhouse-expo.md +425 -0
  168. package/dist/data/designs/ios-clubhouse-swiftui.md +447 -0
  169. package/dist/data/designs/ios-clubhouse.md +406 -0
  170. package/dist/data/designs/ios-coffee-meets-bagel-android.md +461 -0
  171. package/dist/data/designs/ios-coffee-meets-bagel-expo.md +372 -0
  172. package/dist/data/designs/ios-coffee-meets-bagel-swiftui.md +368 -0
  173. package/dist/data/designs/ios-coffee-meets-bagel.md +376 -0
  174. package/dist/data/designs/ios-coinbase-android.md +611 -0
  175. package/dist/data/designs/ios-coinbase-expo.md +500 -0
  176. package/dist/data/designs/ios-coinbase-swiftui.md +490 -0
  177. package/dist/data/designs/ios-coinbase.md +409 -0
  178. package/dist/data/designs/ios-copilot-android.md +625 -0
  179. package/dist/data/designs/ios-copilot-expo.md +423 -0
  180. package/dist/data/designs/ios-copilot-swiftui.md +471 -0
  181. package/dist/data/designs/ios-copilot.md +367 -0
  182. package/dist/data/designs/ios-craft-android.md +459 -0
  183. package/dist/data/designs/ios-craft-expo.md +417 -0
  184. package/dist/data/designs/ios-craft-swiftui.md +449 -0
  185. package/dist/data/designs/ios-craft.md +449 -0
  186. package/dist/data/designs/ios-crunchyroll-android.md +432 -0
  187. package/dist/data/designs/ios-crunchyroll-expo.md +388 -0
  188. package/dist/data/designs/ios-crunchyroll-swiftui.md +417 -0
  189. package/dist/data/designs/ios-crunchyroll.md +388 -0
  190. package/dist/data/designs/ios-crypto-com-android.md +461 -0
  191. package/dist/data/designs/ios-crypto-com-expo.md +404 -0
  192. package/dist/data/designs/ios-crypto-com-swiftui.md +419 -0
  193. package/dist/data/designs/ios-crypto-com.md +410 -0
  194. package/dist/data/designs/ios-deepseek-android.md +471 -0
  195. package/dist/data/designs/ios-deepseek-expo.md +391 -0
  196. package/dist/data/designs/ios-deepseek-swiftui.md +354 -0
  197. package/dist/data/designs/ios-deepseek.md +393 -0
  198. package/dist/data/designs/ios-deezer-android.md +475 -0
  199. package/dist/data/designs/ios-deezer-expo.md +400 -0
  200. package/dist/data/designs/ios-deezer-swiftui.md +430 -0
  201. package/dist/data/designs/ios-deezer.md +388 -0
  202. package/dist/data/designs/ios-deliveroo-android.md +493 -0
  203. package/dist/data/designs/ios-deliveroo-expo.md +424 -0
  204. package/dist/data/designs/ios-deliveroo-swiftui.md +398 -0
  205. package/dist/data/designs/ios-deliveroo.md +420 -0
  206. package/dist/data/designs/ios-delta-android.md +439 -0
  207. package/dist/data/designs/ios-delta-expo.md +381 -0
  208. package/dist/data/designs/ios-delta-swiftui.md +400 -0
  209. package/dist/data/designs/ios-delta.md +397 -0
  210. package/dist/data/designs/ios-discord-android.md +611 -0
  211. package/dist/data/designs/ios-discord-expo.md +618 -0
  212. package/dist/data/designs/ios-discord-swiftui.md +630 -0
  213. package/dist/data/designs/ios-discord.md +451 -0
  214. package/dist/data/designs/ios-disney-plus-android.md +464 -0
  215. package/dist/data/designs/ios-disney-plus-expo.md +358 -0
  216. package/dist/data/designs/ios-disney-plus-swiftui.md +412 -0
  217. package/dist/data/designs/ios-disney-plus.md +355 -0
  218. package/dist/data/designs/ios-dominos-android.md +417 -0
  219. package/dist/data/designs/ios-dominos-expo.md +316 -0
  220. package/dist/data/designs/ios-dominos-swiftui.md +340 -0
  221. package/dist/data/designs/ios-dominos.md +347 -0
  222. package/dist/data/designs/ios-doordash-android.md +527 -0
  223. package/dist/data/designs/ios-doordash-expo.md +323 -0
  224. package/dist/data/designs/ios-doordash-swiftui.md +301 -0
  225. package/dist/data/designs/ios-doordash.md +393 -0
  226. package/dist/data/designs/ios-dropbox-android.md +522 -0
  227. package/dist/data/designs/ios-dropbox-expo.md +393 -0
  228. package/dist/data/designs/ios-dropbox-swiftui.md +400 -0
  229. package/dist/data/designs/ios-dropbox.md +374 -0
  230. package/dist/data/designs/ios-duolingo-android.md +641 -0
  231. package/dist/data/designs/ios-duolingo-expo.md +597 -0
  232. package/dist/data/designs/ios-duolingo-swiftui.md +539 -0
  233. package/dist/data/designs/ios-duolingo.md +409 -0
  234. package/dist/data/designs/ios-ebay-android.md +436 -0
  235. package/dist/data/designs/ios-ebay-expo.md +344 -0
  236. package/dist/data/designs/ios-ebay-swiftui.md +308 -0
  237. package/dist/data/designs/ios-ebay.md +355 -0
  238. package/dist/data/designs/ios-espn-android.md +468 -0
  239. package/dist/data/designs/ios-espn-expo.md +387 -0
  240. package/dist/data/designs/ios-espn-swiftui.md +410 -0
  241. package/dist/data/designs/ios-espn.md +384 -0
  242. package/dist/data/designs/ios-etsy-android.md +439 -0
  243. package/dist/data/designs/ios-etsy-expo.md +351 -0
  244. package/dist/data/designs/ios-etsy-swiftui.md +296 -0
  245. package/dist/data/designs/ios-etsy.md +355 -0
  246. package/dist/data/designs/ios-evernote-android.md +419 -0
  247. package/dist/data/designs/ios-evernote-expo.md +379 -0
  248. package/dist/data/designs/ios-evernote-swiftui.md +380 -0
  249. package/dist/data/designs/ios-evernote.md +416 -0
  250. package/dist/data/designs/ios-expedia-android.md +451 -0
  251. package/dist/data/designs/ios-expedia-expo.md +433 -0
  252. package/dist/data/designs/ios-expedia-swiftui.md +399 -0
  253. package/dist/data/designs/ios-expedia.md +431 -0
  254. package/dist/data/designs/ios-facebook-android.md +628 -0
  255. package/dist/data/designs/ios-facebook-expo.md +471 -0
  256. package/dist/data/designs/ios-facebook-swiftui.md +459 -0
  257. package/dist/data/designs/ios-facebook.md +452 -0
  258. package/dist/data/designs/ios-feeld-android.md +431 -0
  259. package/dist/data/designs/ios-feeld-expo.md +379 -0
  260. package/dist/data/designs/ios-feeld-swiftui.md +401 -0
  261. package/dist/data/designs/ios-feeld.md +378 -0
  262. package/dist/data/designs/ios-fidelity-android.md +426 -0
  263. package/dist/data/designs/ios-fidelity-expo.md +396 -0
  264. package/dist/data/designs/ios-fidelity-swiftui.md +398 -0
  265. package/dist/data/designs/ios-fidelity.md +387 -0
  266. package/dist/data/designs/ios-figma-android.md +578 -0
  267. package/dist/data/designs/ios-figma-expo.md +504 -0
  268. package/dist/data/designs/ios-figma-swiftui.md +542 -0
  269. package/dist/data/designs/ios-figma.md +404 -0
  270. package/dist/data/designs/ios-fitbit-android.md +439 -0
  271. package/dist/data/designs/ios-fitbit-expo.md +389 -0
  272. package/dist/data/designs/ios-fitbit-swiftui.md +380 -0
  273. package/dist/data/designs/ios-fitbit.md +396 -0
  274. package/dist/data/designs/ios-flickr-android.md +506 -0
  275. package/dist/data/designs/ios-flickr-expo.md +434 -0
  276. package/dist/data/designs/ios-flickr-swiftui.md +421 -0
  277. package/dist/data/designs/ios-flickr.md +394 -0
  278. package/dist/data/designs/ios-flighty-android.md +418 -0
  279. package/dist/data/designs/ios-flighty-expo.md +353 -0
  280. package/dist/data/designs/ios-flighty-swiftui.md +337 -0
  281. package/dist/data/designs/ios-flighty.md +357 -0
  282. package/dist/data/designs/ios-flo-android.md +447 -0
  283. package/dist/data/designs/ios-flo-expo.md +363 -0
  284. package/dist/data/designs/ios-flo-swiftui.md +355 -0
  285. package/dist/data/designs/ios-flo.md +351 -0
  286. package/dist/data/designs/ios-garmin-connect-android.md +531 -0
  287. package/dist/data/designs/ios-garmin-connect-expo.md +429 -0
  288. package/dist/data/designs/ios-garmin-connect-swiftui.md +459 -0
  289. package/dist/data/designs/ios-garmin-connect.md +419 -0
  290. package/dist/data/designs/ios-gas-android.md +438 -0
  291. package/dist/data/designs/ios-gas-expo.md +350 -0
  292. package/dist/data/designs/ios-gas-swiftui.md +329 -0
  293. package/dist/data/designs/ios-gas.md +387 -0
  294. package/dist/data/designs/ios-gemini-android.md +568 -0
  295. package/dist/data/designs/ios-gemini-expo.md +416 -0
  296. package/dist/data/designs/ios-gemini-swiftui.md +424 -0
  297. package/dist/data/designs/ios-gemini.md +351 -0
  298. package/dist/data/designs/ios-github-android.md +507 -0
  299. package/dist/data/designs/ios-github-expo.md +486 -0
  300. package/dist/data/designs/ios-github-swiftui.md +459 -0
  301. package/dist/data/designs/ios-github.md +394 -0
  302. package/dist/data/designs/ios-gmail-android.md +548 -0
  303. package/dist/data/designs/ios-gmail-expo.md +457 -0
  304. package/dist/data/designs/ios-gmail-swiftui.md +470 -0
  305. package/dist/data/designs/ios-gmail.md +441 -0
  306. package/dist/data/designs/ios-goodreads-android.md +436 -0
  307. package/dist/data/designs/ios-goodreads-expo.md +407 -0
  308. package/dist/data/designs/ios-goodreads-swiftui.md +378 -0
  309. package/dist/data/designs/ios-goodreads.md +385 -0
  310. package/dist/data/designs/ios-google-calendar-android.md +565 -0
  311. package/dist/data/designs/ios-google-calendar-expo.md +616 -0
  312. package/dist/data/designs/ios-google-calendar-swiftui.md +585 -0
  313. package/dist/data/designs/ios-google-calendar.md +431 -0
  314. package/dist/data/designs/ios-google-maps-android.md +614 -0
  315. package/dist/data/designs/ios-google-maps-expo.md +515 -0
  316. package/dist/data/designs/ios-google-maps-swiftui.md +484 -0
  317. package/dist/data/designs/ios-google-maps.md +451 -0
  318. package/dist/data/designs/ios-grammarly-android.md +449 -0
  319. package/dist/data/designs/ios-grammarly-expo.md +383 -0
  320. package/dist/data/designs/ios-grammarly-swiftui.md +381 -0
  321. package/dist/data/designs/ios-grammarly.md +392 -0
  322. package/dist/data/designs/ios-grindr-android.md +494 -0
  323. package/dist/data/designs/ios-grindr-expo.md +358 -0
  324. package/dist/data/designs/ios-grindr-swiftui.md +357 -0
  325. package/dist/data/designs/ios-grindr.md +356 -0
  326. package/dist/data/designs/ios-grok-android.md +471 -0
  327. package/dist/data/designs/ios-grok-expo.md +388 -0
  328. package/dist/data/designs/ios-grok-swiftui.md +397 -0
  329. package/dist/data/designs/ios-grok.md +357 -0
  330. package/dist/data/designs/ios-groupme-android.md +551 -0
  331. package/dist/data/designs/ios-groupme-expo.md +442 -0
  332. package/dist/data/designs/ios-groupme-swiftui.md +439 -0
  333. package/dist/data/designs/ios-groupme.md +424 -0
  334. package/dist/data/designs/ios-grubhub-android.md +506 -0
  335. package/dist/data/designs/ios-grubhub-expo.md +428 -0
  336. package/dist/data/designs/ios-grubhub-swiftui.md +408 -0
  337. package/dist/data/designs/ios-grubhub.md +427 -0
  338. package/dist/data/designs/ios-happn-android.md +464 -0
  339. package/dist/data/designs/ios-happn-expo.md +411 -0
  340. package/dist/data/designs/ios-happn-swiftui.md +401 -0
  341. package/dist/data/designs/ios-happn.md +400 -0
  342. package/dist/data/designs/ios-headspace-android.md +688 -0
  343. package/dist/data/designs/ios-headspace-expo.md +574 -0
  344. package/dist/data/designs/ios-headspace-swiftui.md +498 -0
  345. package/dist/data/designs/ios-headspace.md +430 -0
  346. package/dist/data/designs/ios-hevy-android.md +473 -0
  347. package/dist/data/designs/ios-hevy-expo.md +405 -0
  348. package/dist/data/designs/ios-hevy-swiftui.md +367 -0
  349. package/dist/data/designs/ios-hevy.md +410 -0
  350. package/dist/data/designs/ios-hinge-android.md +743 -0
  351. package/dist/data/designs/ios-hinge-expo.md +580 -0
  352. package/dist/data/designs/ios-hinge-swiftui.md +512 -0
  353. package/dist/data/designs/ios-hinge.md +405 -0
  354. package/dist/data/designs/ios-hopper-android.md +440 -0
  355. package/dist/data/designs/ios-hopper-expo.md +353 -0
  356. package/dist/data/designs/ios-hopper-swiftui.md +340 -0
  357. package/dist/data/designs/ios-hopper.md +354 -0
  358. package/dist/data/designs/ios-hulu-android.md +435 -0
  359. package/dist/data/designs/ios-hulu-expo.md +331 -0
  360. package/dist/data/designs/ios-hulu-swiftui.md +374 -0
  361. package/dist/data/designs/ios-hulu.md +353 -0
  362. package/dist/data/designs/ios-iheartradio-android.md +481 -0
  363. package/dist/data/designs/ios-iheartradio-expo.md +417 -0
  364. package/dist/data/designs/ios-iheartradio-swiftui.md +423 -0
  365. package/dist/data/designs/ios-iheartradio.md +381 -0
  366. package/dist/data/designs/ios-imessage-android.md +426 -0
  367. package/dist/data/designs/ios-imessage-expo.md +384 -0
  368. package/dist/data/designs/ios-imessage-swiftui.md +378 -0
  369. package/dist/data/designs/ios-imessage.md +394 -0
  370. package/dist/data/designs/ios-instacart-android.md +471 -0
  371. package/dist/data/designs/ios-instacart-expo.md +349 -0
  372. package/dist/data/designs/ios-instacart-swiftui.md +378 -0
  373. package/dist/data/designs/ios-instacart.md +360 -0
  374. package/dist/data/designs/ios-instagram-android.md +646 -0
  375. package/dist/data/designs/ios-instagram-expo.md +310 -0
  376. package/dist/data/designs/ios-instagram-swiftui.md +333 -0
  377. package/dist/data/designs/ios-instagram.md +376 -0
  378. package/dist/data/designs/ios-jira-android.md +432 -0
  379. package/dist/data/designs/ios-jira-expo.md +375 -0
  380. package/dist/data/designs/ios-jira-swiftui.md +433 -0
  381. package/dist/data/designs/ios-jira.md +424 -0
  382. package/dist/data/designs/ios-kakaotalk-android.md +491 -0
  383. package/dist/data/designs/ios-kakaotalk-expo.md +418 -0
  384. package/dist/data/designs/ios-kakaotalk-swiftui.md +406 -0
  385. package/dist/data/designs/ios-kakaotalk.md +414 -0
  386. package/dist/data/designs/ios-kayak-android.md +466 -0
  387. package/dist/data/designs/ios-kayak-expo.md +407 -0
  388. package/dist/data/designs/ios-kayak-swiftui.md +426 -0
  389. package/dist/data/designs/ios-kayak.md +392 -0
  390. package/dist/data/designs/ios-kick-android.md +487 -0
  391. package/dist/data/designs/ios-kick-expo.md +434 -0
  392. package/dist/data/designs/ios-kick-swiftui.md +429 -0
  393. package/dist/data/designs/ios-kick.md +389 -0
  394. package/dist/data/designs/ios-kik-android.md +433 -0
  395. package/dist/data/designs/ios-kik-expo.md +363 -0
  396. package/dist/data/designs/ios-kik-swiftui.md +363 -0
  397. package/dist/data/designs/ios-kik.md +392 -0
  398. package/dist/data/designs/ios-kindle-android.md +439 -0
  399. package/dist/data/designs/ios-kindle-expo.md +356 -0
  400. package/dist/data/designs/ios-kindle-swiftui.md +372 -0
  401. package/dist/data/designs/ios-kindle.md +369 -0
  402. package/dist/data/designs/ios-klarna-android.md +420 -0
  403. package/dist/data/designs/ios-klarna-expo.md +349 -0
  404. package/dist/data/designs/ios-klarna-swiftui.md +360 -0
  405. package/dist/data/designs/ios-klarna.md +356 -0
  406. package/dist/data/designs/ios-lemon8-android.md +492 -0
  407. package/dist/data/designs/ios-lemon8-expo.md +413 -0
  408. package/dist/data/designs/ios-lemon8-swiftui.md +428 -0
  409. package/dist/data/designs/ios-lemon8.md +378 -0
  410. package/dist/data/designs/ios-life360-android.md +474 -0
  411. package/dist/data/designs/ios-life360-expo.md +375 -0
  412. package/dist/data/designs/ios-life360-swiftui.md +390 -0
  413. package/dist/data/designs/ios-life360.md +402 -0
  414. package/dist/data/designs/ios-line-android.md +517 -0
  415. package/dist/data/designs/ios-line-expo.md +402 -0
  416. package/dist/data/designs/ios-line-swiftui.md +361 -0
  417. package/dist/data/designs/ios-line.md +362 -0
  418. package/dist/data/designs/ios-linear-android.md +457 -0
  419. package/dist/data/designs/ios-linear-expo.md +367 -0
  420. package/dist/data/designs/ios-linear-swiftui.md +401 -0
  421. package/dist/data/designs/ios-linear.md +368 -0
  422. package/dist/data/designs/ios-linkedin-android.md +620 -0
  423. package/dist/data/designs/ios-linkedin-expo.md +469 -0
  424. package/dist/data/designs/ios-linkedin-swiftui.md +472 -0
  425. package/dist/data/designs/ios-linkedin.md +444 -0
  426. package/dist/data/designs/ios-locket-android.md +500 -0
  427. package/dist/data/designs/ios-locket-expo.md +375 -0
  428. package/dist/data/designs/ios-locket-swiftui.md +375 -0
  429. package/dist/data/designs/ios-locket.md +381 -0
  430. package/dist/data/designs/ios-lyft-android.md +406 -0
  431. package/dist/data/designs/ios-lyft-expo.md +333 -0
  432. package/dist/data/designs/ios-lyft-swiftui.md +315 -0
  433. package/dist/data/designs/ios-lyft.md +337 -0
  434. package/dist/data/designs/ios-marriott-bonvoy-android.md +488 -0
  435. package/dist/data/designs/ios-marriott-bonvoy-expo.md +412 -0
  436. package/dist/data/designs/ios-marriott-bonvoy-swiftui.md +461 -0
  437. package/dist/data/designs/ios-marriott-bonvoy.md +379 -0
  438. package/dist/data/designs/ios-mastodon-android.md +476 -0
  439. package/dist/data/designs/ios-mastodon-expo.md +379 -0
  440. package/dist/data/designs/ios-mastodon-swiftui.md +349 -0
  441. package/dist/data/designs/ios-mastodon.md +337 -0
  442. package/dist/data/designs/ios-match-android.md +496 -0
  443. package/dist/data/designs/ios-match-expo.md +393 -0
  444. package/dist/data/designs/ios-match-swiftui.md +450 -0
  445. package/dist/data/designs/ios-match.md +382 -0
  446. package/dist/data/designs/ios-max-android.md +454 -0
  447. package/dist/data/designs/ios-max-expo.md +342 -0
  448. package/dist/data/designs/ios-max-swiftui.md +332 -0
  449. package/dist/data/designs/ios-max.md +352 -0
  450. package/dist/data/designs/ios-mcdonalds-android.md +497 -0
  451. package/dist/data/designs/ios-mcdonalds-expo.md +382 -0
  452. package/dist/data/designs/ios-mcdonalds-swiftui.md +394 -0
  453. package/dist/data/designs/ios-mcdonalds.md +410 -0
  454. package/dist/data/designs/ios-messenger-android.md +523 -0
  455. package/dist/data/designs/ios-messenger-expo.md +379 -0
  456. package/dist/data/designs/ios-messenger-swiftui.md +369 -0
  457. package/dist/data/designs/ios-messenger.md +361 -0
  458. package/dist/data/designs/ios-microsoft-teams-android.md +446 -0
  459. package/dist/data/designs/ios-microsoft-teams-expo.md +362 -0
  460. package/dist/data/designs/ios-microsoft-teams-swiftui.md +384 -0
  461. package/dist/data/designs/ios-microsoft-teams.md +359 -0
  462. package/dist/data/designs/ios-midjourney-android.md +503 -0
  463. package/dist/data/designs/ios-midjourney-expo.md +406 -0
  464. package/dist/data/designs/ios-midjourney-swiftui.md +376 -0
  465. package/dist/data/designs/ios-midjourney.md +407 -0
  466. package/dist/data/designs/ios-miro-android.md +475 -0
  467. package/dist/data/designs/ios-miro-expo.md +421 -0
  468. package/dist/data/designs/ios-miro-swiftui.md +407 -0
  469. package/dist/data/designs/ios-miro.md +424 -0
  470. package/dist/data/designs/ios-monday-android.md +426 -0
  471. package/dist/data/designs/ios-monday-expo.md +382 -0
  472. package/dist/data/designs/ios-monday-swiftui.md +386 -0
  473. package/dist/data/designs/ios-monday.md +415 -0
  474. package/dist/data/designs/ios-monzo-android.md +423 -0
  475. package/dist/data/designs/ios-monzo-expo.md +391 -0
  476. package/dist/data/designs/ios-monzo-swiftui.md +371 -0
  477. package/dist/data/designs/ios-monzo.md +382 -0
  478. package/dist/data/designs/ios-myfitnesspal-android.md +631 -0
  479. package/dist/data/designs/ios-myfitnesspal-expo.md +489 -0
  480. package/dist/data/designs/ios-myfitnesspal-swiftui.md +500 -0
  481. package/dist/data/designs/ios-myfitnesspal.md +408 -0
  482. package/dist/data/designs/ios-netflix-android.md +570 -0
  483. package/dist/data/designs/ios-netflix-expo.md +528 -0
  484. package/dist/data/designs/ios-netflix-swiftui.md +543 -0
  485. package/dist/data/designs/ios-netflix.md +476 -0
  486. package/dist/data/designs/ios-nextdoor-android.md +399 -0
  487. package/dist/data/designs/ios-nextdoor-expo.md +317 -0
  488. package/dist/data/designs/ios-nextdoor-swiftui.md +343 -0
  489. package/dist/data/designs/ios-nextdoor.md +332 -0
  490. package/dist/data/designs/ios-nike-run-club-android.md +598 -0
  491. package/dist/data/designs/ios-nike-run-club-expo.md +459 -0
  492. package/dist/data/designs/ios-nike-run-club-swiftui.md +474 -0
  493. package/dist/data/designs/ios-nike-run-club.md +400 -0
  494. package/dist/data/designs/ios-noom-android.md +455 -0
  495. package/dist/data/designs/ios-noom-expo.md +362 -0
  496. package/dist/data/designs/ios-noom-swiftui.md +369 -0
  497. package/dist/data/designs/ios-noom.md +404 -0
  498. package/dist/data/designs/ios-notion-android.md +575 -0
  499. package/dist/data/designs/ios-notion-expo.md +543 -0
  500. package/dist/data/designs/ios-notion-swiftui.md +562 -0
  501. package/dist/data/designs/ios-notion.md +506 -0
  502. package/dist/data/designs/ios-nubank-android.md +447 -0
  503. package/dist/data/designs/ios-nubank-expo.md +391 -0
  504. package/dist/data/designs/ios-nubank-swiftui.md +391 -0
  505. package/dist/data/designs/ios-nubank.md +388 -0
  506. package/dist/data/designs/ios-obsidian-android.md +456 -0
  507. package/dist/data/designs/ios-obsidian-expo.md +401 -0
  508. package/dist/data/designs/ios-obsidian-swiftui.md +412 -0
  509. package/dist/data/designs/ios-obsidian.md +363 -0
  510. package/dist/data/designs/ios-okcupid-android.md +496 -0
  511. package/dist/data/designs/ios-okcupid-expo.md +378 -0
  512. package/dist/data/designs/ios-okcupid-swiftui.md +343 -0
  513. package/dist/data/designs/ios-okcupid.md +364 -0
  514. package/dist/data/designs/ios-opentable-android.md +426 -0
  515. package/dist/data/designs/ios-opentable-expo.md +350 -0
  516. package/dist/data/designs/ios-opentable-swiftui.md +328 -0
  517. package/dist/data/designs/ios-opentable.md +411 -0
  518. package/dist/data/designs/ios-oura-android.md +426 -0
  519. package/dist/data/designs/ios-oura-expo.md +353 -0
  520. package/dist/data/designs/ios-oura-swiftui.md +327 -0
  521. package/dist/data/designs/ios-oura.md +356 -0
  522. package/dist/data/designs/ios-overcast-android.md +508 -0
  523. package/dist/data/designs/ios-overcast-expo.md +420 -0
  524. package/dist/data/designs/ios-overcast-swiftui.md +427 -0
  525. package/dist/data/designs/ios-overcast.md +414 -0
  526. package/dist/data/designs/ios-pandora-android.md +477 -0
  527. package/dist/data/designs/ios-pandora-expo.md +406 -0
  528. package/dist/data/designs/ios-pandora-swiftui.md +413 -0
  529. package/dist/data/designs/ios-pandora.md +366 -0
  530. package/dist/data/designs/ios-panera-android.md +414 -0
  531. package/dist/data/designs/ios-panera-expo.md +352 -0
  532. package/dist/data/designs/ios-panera-swiftui.md +369 -0
  533. package/dist/data/designs/ios-panera.md +412 -0
  534. package/dist/data/designs/ios-paramount-plus-android.md +415 -0
  535. package/dist/data/designs/ios-paramount-plus-expo.md +363 -0
  536. package/dist/data/designs/ios-paramount-plus-swiftui.md +372 -0
  537. package/dist/data/designs/ios-paramount-plus.md +400 -0
  538. package/dist/data/designs/ios-paypal-android.md +535 -0
  539. package/dist/data/designs/ios-paypal-expo.md +429 -0
  540. package/dist/data/designs/ios-paypal-swiftui.md +466 -0
  541. package/dist/data/designs/ios-paypal.md +393 -0
  542. package/dist/data/designs/ios-peacock-android.md +453 -0
  543. package/dist/data/designs/ios-peacock-expo.md +393 -0
  544. package/dist/data/designs/ios-peacock-swiftui.md +420 -0
  545. package/dist/data/designs/ios-peacock.md +411 -0
  546. package/dist/data/designs/ios-peloton-android.md +470 -0
  547. package/dist/data/designs/ios-peloton-expo.md +401 -0
  548. package/dist/data/designs/ios-peloton-swiftui.md +409 -0
  549. package/dist/data/designs/ios-peloton.md +415 -0
  550. package/dist/data/designs/ios-perplexity-android.md +553 -0
  551. package/dist/data/designs/ios-perplexity-expo.md +703 -0
  552. package/dist/data/designs/ios-perplexity-swiftui.md +602 -0
  553. package/dist/data/designs/ios-perplexity.md +453 -0
  554. package/dist/data/designs/ios-pinterest-android.md +551 -0
  555. package/dist/data/designs/ios-pinterest-expo.md +466 -0
  556. package/dist/data/designs/ios-pinterest-swiftui.md +464 -0
  557. package/dist/data/designs/ios-pinterest.md +427 -0
  558. package/dist/data/designs/ios-plenty-of-fish-android.md +462 -0
  559. package/dist/data/designs/ios-plenty-of-fish-expo.md +380 -0
  560. package/dist/data/designs/ios-plenty-of-fish-swiftui.md +429 -0
  561. package/dist/data/designs/ios-plenty-of-fish.md +387 -0
  562. package/dist/data/designs/ios-plex-android.md +403 -0
  563. package/dist/data/designs/ios-plex-expo.md +333 -0
  564. package/dist/data/designs/ios-plex-swiftui.md +349 -0
  565. package/dist/data/designs/ios-plex.md +379 -0
  566. package/dist/data/designs/ios-pluto-tv-android.md +413 -0
  567. package/dist/data/designs/ios-pluto-tv-expo.md +379 -0
  568. package/dist/data/designs/ios-pluto-tv-swiftui.md +411 -0
  569. package/dist/data/designs/ios-pluto-tv.md +362 -0
  570. package/dist/data/designs/ios-pocket-casts-android.md +492 -0
  571. package/dist/data/designs/ios-pocket-casts-expo.md +430 -0
  572. package/dist/data/designs/ios-pocket-casts-swiftui.md +479 -0
  573. package/dist/data/designs/ios-pocket-casts.md +427 -0
  574. package/dist/data/designs/ios-postman-android.md +472 -0
  575. package/dist/data/designs/ios-postman-expo.md +372 -0
  576. package/dist/data/designs/ios-postman-swiftui.md +377 -0
  577. package/dist/data/designs/ios-postman.md +405 -0
  578. package/dist/data/designs/ios-prime-video-android.md +447 -0
  579. package/dist/data/designs/ios-prime-video-expo.md +365 -0
  580. package/dist/data/designs/ios-prime-video-swiftui.md +333 -0
  581. package/dist/data/designs/ios-prime-video.md +351 -0
  582. package/dist/data/designs/ios-quora-android.md +513 -0
  583. package/dist/data/designs/ios-quora-expo.md +362 -0
  584. package/dist/data/designs/ios-quora-swiftui.md +375 -0
  585. package/dist/data/designs/ios-quora.md +359 -0
  586. package/dist/data/designs/ios-raya-android.md +401 -0
  587. package/dist/data/designs/ios-raya-expo.md +366 -0
  588. package/dist/data/designs/ios-raya-swiftui.md +393 -0
  589. package/dist/data/designs/ios-raya.md +366 -0
  590. package/dist/data/designs/ios-reddit-android.md +605 -0
  591. package/dist/data/designs/ios-reddit-expo.md +553 -0
  592. package/dist/data/designs/ios-reddit-swiftui.md +514 -0
  593. package/dist/data/designs/ios-reddit.md +460 -0
  594. package/dist/data/designs/ios-resy-android.md +430 -0
  595. package/dist/data/designs/ios-resy-expo.md +344 -0
  596. package/dist/data/designs/ios-resy-swiftui.md +330 -0
  597. package/dist/data/designs/ios-resy.md +412 -0
  598. package/dist/data/designs/ios-revolut-android.md +433 -0
  599. package/dist/data/designs/ios-revolut-expo.md +379 -0
  600. package/dist/data/designs/ios-revolut-swiftui.md +372 -0
  601. package/dist/data/designs/ios-revolut.md +353 -0
  602. package/dist/data/designs/ios-robinhood-android.md +587 -0
  603. package/dist/data/designs/ios-robinhood-expo.md +416 -0
  604. package/dist/data/designs/ios-robinhood-swiftui.md +431 -0
  605. package/dist/data/designs/ios-robinhood.md +380 -0
  606. package/dist/data/designs/ios-runna-android.md +485 -0
  607. package/dist/data/designs/ios-runna-expo.md +388 -0
  608. package/dist/data/designs/ios-runna-swiftui.md +413 -0
  609. package/dist/data/designs/ios-runna.md +390 -0
  610. package/dist/data/designs/ios-shazam-android.md +436 -0
  611. package/dist/data/designs/ios-shazam-expo.md +348 -0
  612. package/dist/data/designs/ios-shazam-swiftui.md +394 -0
  613. package/dist/data/designs/ios-shazam.md +338 -0
  614. package/dist/data/designs/ios-signal-android.md +462 -0
  615. package/dist/data/designs/ios-signal-expo.md +361 -0
  616. package/dist/data/designs/ios-signal-swiftui.md +350 -0
  617. package/dist/data/designs/ios-signal.md +357 -0
  618. package/dist/data/designs/ios-skype-android.md +398 -0
  619. package/dist/data/designs/ios-skype-expo.md +368 -0
  620. package/dist/data/designs/ios-skype-swiftui.md +369 -0
  621. package/dist/data/designs/ios-skype.md +406 -0
  622. package/dist/data/designs/ios-skyscanner-android.md +451 -0
  623. package/dist/data/designs/ios-skyscanner-expo.md +423 -0
  624. package/dist/data/designs/ios-skyscanner-swiftui.md +419 -0
  625. package/dist/data/designs/ios-skyscanner.md +421 -0
  626. package/dist/data/designs/ios-slack-android.md +695 -0
  627. package/dist/data/designs/ios-slack-expo.md +601 -0
  628. package/dist/data/designs/ios-slack-swiftui.md +594 -0
  629. package/dist/data/designs/ios-slack.md +474 -0
  630. package/dist/data/designs/ios-sleep-cycle-android.md +436 -0
  631. package/dist/data/designs/ios-sleep-cycle-expo.md +380 -0
  632. package/dist/data/designs/ios-sleep-cycle-swiftui.md +409 -0
  633. package/dist/data/designs/ios-sleep-cycle.md +386 -0
  634. package/dist/data/designs/ios-snapchat-android.md +671 -0
  635. package/dist/data/designs/ios-snapchat-expo.md +528 -0
  636. package/dist/data/designs/ios-snapchat-swiftui.md +557 -0
  637. package/dist/data/designs/ios-snapchat.md +478 -0
  638. package/dist/data/designs/ios-sofi-android.md +446 -0
  639. package/dist/data/designs/ios-sofi-expo.md +387 -0
  640. package/dist/data/designs/ios-sofi-swiftui.md +379 -0
  641. package/dist/data/designs/ios-sofi.md +385 -0
  642. package/dist/data/designs/ios-soundcloud-android.md +488 -0
  643. package/dist/data/designs/ios-soundcloud-expo.md +364 -0
  644. package/dist/data/designs/ios-soundcloud-swiftui.md +399 -0
  645. package/dist/data/designs/ios-soundcloud.md +359 -0
  646. package/dist/data/designs/ios-southwest-android.md +446 -0
  647. package/dist/data/designs/ios-southwest-expo.md +389 -0
  648. package/dist/data/designs/ios-southwest-swiftui.md +405 -0
  649. package/dist/data/designs/ios-southwest.md +405 -0
  650. package/dist/data/designs/ios-spotify-android.md +427 -0
  651. package/dist/data/designs/ios-spotify-expo.md +321 -0
  652. package/dist/data/designs/ios-spotify-swiftui.md +312 -0
  653. package/dist/data/designs/ios-spotify.md +366 -0
  654. package/dist/data/designs/ios-starbucks-android.md +622 -0
  655. package/dist/data/designs/ios-starbucks-expo.md +497 -0
  656. package/dist/data/designs/ios-starbucks-swiftui.md +500 -0
  657. package/dist/data/designs/ios-starbucks.md +440 -0
  658. package/dist/data/designs/ios-strava-android.md +656 -0
  659. package/dist/data/designs/ios-strava-expo.md +501 -0
  660. package/dist/data/designs/ios-strava-swiftui.md +465 -0
  661. package/dist/data/designs/ios-strava.md +433 -0
  662. package/dist/data/designs/ios-strong-android.md +479 -0
  663. package/dist/data/designs/ios-strong-expo.md +396 -0
  664. package/dist/data/designs/ios-strong-swiftui.md +363 -0
  665. package/dist/data/designs/ios-strong.md +415 -0
  666. package/dist/data/designs/ios-substack-android.md +440 -0
  667. package/dist/data/designs/ios-substack-expo.md +395 -0
  668. package/dist/data/designs/ios-substack-swiftui.md +371 -0
  669. package/dist/data/designs/ios-substack.md +372 -0
  670. package/dist/data/designs/ios-swiggy-android.md +549 -0
  671. package/dist/data/designs/ios-swiggy-expo.md +443 -0
  672. package/dist/data/designs/ios-swiggy-swiftui.md +471 -0
  673. package/dist/data/designs/ios-swiggy.md +432 -0
  674. package/dist/data/designs/ios-taco-bell-android.md +529 -0
  675. package/dist/data/designs/ios-taco-bell-expo.md +407 -0
  676. package/dist/data/designs/ios-taco-bell-swiftui.md +419 -0
  677. package/dist/data/designs/ios-taco-bell.md +430 -0
  678. package/dist/data/designs/ios-telegram-android.md +676 -0
  679. package/dist/data/designs/ios-telegram-expo.md +594 -0
  680. package/dist/data/designs/ios-telegram-swiftui.md +518 -0
  681. package/dist/data/designs/ios-telegram.md +458 -0
  682. package/dist/data/designs/ios-the-league-android.md +454 -0
  683. package/dist/data/designs/ios-the-league-expo.md +391 -0
  684. package/dist/data/designs/ios-the-league-swiftui.md +409 -0
  685. package/dist/data/designs/ios-the-league.md +368 -0
  686. package/dist/data/designs/ios-things-3-android.md +421 -0
  687. package/dist/data/designs/ios-things-3-expo.md +351 -0
  688. package/dist/data/designs/ios-things-3-swiftui.md +359 -0
  689. package/dist/data/designs/ios-things-3.md +339 -0
  690. package/dist/data/designs/ios-threads-android.md +588 -0
  691. package/dist/data/designs/ios-threads-expo.md +468 -0
  692. package/dist/data/designs/ios-threads-swiftui.md +452 -0
  693. package/dist/data/designs/ios-threads.md +424 -0
  694. package/dist/data/designs/ios-threema-android.md +496 -0
  695. package/dist/data/designs/ios-threema-expo.md +415 -0
  696. package/dist/data/designs/ios-threema-swiftui.md +446 -0
  697. package/dist/data/designs/ios-threema.md +397 -0
  698. package/dist/data/designs/ios-tidal-android.md +398 -0
  699. package/dist/data/designs/ios-tidal-expo.md +323 -0
  700. package/dist/data/designs/ios-tidal-swiftui.md +340 -0
  701. package/dist/data/designs/ios-tidal.md +351 -0
  702. package/dist/data/designs/ios-tiktok-android.md +654 -0
  703. package/dist/data/designs/ios-tiktok-expo.md +578 -0
  704. package/dist/data/designs/ios-tiktok-swiftui.md +595 -0
  705. package/dist/data/designs/ios-tiktok.md +387 -0
  706. package/dist/data/designs/ios-tinder-android.md +681 -0
  707. package/dist/data/designs/ios-tinder-expo.md +512 -0
  708. package/dist/data/designs/ios-tinder-swiftui.md +497 -0
  709. package/dist/data/designs/ios-tinder.md +415 -0
  710. package/dist/data/designs/ios-todoist-android.md +711 -0
  711. package/dist/data/designs/ios-todoist-expo.md +607 -0
  712. package/dist/data/designs/ios-todoist-swiftui.md +573 -0
  713. package/dist/data/designs/ios-todoist.md +418 -0
  714. package/dist/data/designs/ios-trello-android.md +412 -0
  715. package/dist/data/designs/ios-trello-expo.md +332 -0
  716. package/dist/data/designs/ios-trello-swiftui.md +359 -0
  717. package/dist/data/designs/ios-trello.md +344 -0
  718. package/dist/data/designs/ios-tripadvisor-android.md +450 -0
  719. package/dist/data/designs/ios-tripadvisor-expo.md +322 -0
  720. package/dist/data/designs/ios-tripadvisor-swiftui.md +326 -0
  721. package/dist/data/designs/ios-tripadvisor.md +356 -0
  722. package/dist/data/designs/ios-tubi-android.md +453 -0
  723. package/dist/data/designs/ios-tubi-expo.md +419 -0
  724. package/dist/data/designs/ios-tubi-swiftui.md +463 -0
  725. package/dist/data/designs/ios-tubi.md +383 -0
  726. package/dist/data/designs/ios-tumblr-android.md +462 -0
  727. package/dist/data/designs/ios-tumblr-expo.md +374 -0
  728. package/dist/data/designs/ios-tumblr-swiftui.md +380 -0
  729. package/dist/data/designs/ios-tumblr.md +337 -0
  730. package/dist/data/designs/ios-turo-android.md +500 -0
  731. package/dist/data/designs/ios-turo-expo.md +454 -0
  732. package/dist/data/designs/ios-turo-swiftui.md +447 -0
  733. package/dist/data/designs/ios-turo.md +392 -0
  734. package/dist/data/designs/ios-twitch-android.md +412 -0
  735. package/dist/data/designs/ios-twitch-expo.md +342 -0
  736. package/dist/data/designs/ios-twitch-swiftui.md +328 -0
  737. package/dist/data/designs/ios-twitch.md +354 -0
  738. package/dist/data/designs/ios-uber-android.md +637 -0
  739. package/dist/data/designs/ios-uber-eats-android.md +502 -0
  740. package/dist/data/designs/ios-uber-eats-expo.md +407 -0
  741. package/dist/data/designs/ios-uber-eats-swiftui.md +393 -0
  742. package/dist/data/designs/ios-uber-eats.md +353 -0
  743. package/dist/data/designs/ios-uber-expo.md +512 -0
  744. package/dist/data/designs/ios-uber-swiftui.md +483 -0
  745. package/dist/data/designs/ios-uber.md +395 -0
  746. package/dist/data/designs/ios-united-airlines-android.md +494 -0
  747. package/dist/data/designs/ios-united-airlines-expo.md +408 -0
  748. package/dist/data/designs/ios-united-airlines-swiftui.md +453 -0
  749. package/dist/data/designs/ios-united-airlines.md +388 -0
  750. package/dist/data/designs/ios-venmo-android.md +654 -0
  751. package/dist/data/designs/ios-venmo-expo.md +458 -0
  752. package/dist/data/designs/ios-venmo-swiftui.md +469 -0
  753. package/dist/data/designs/ios-venmo.md +436 -0
  754. package/dist/data/designs/ios-vercel-android.md +454 -0
  755. package/dist/data/designs/ios-vercel-expo.md +387 -0
  756. package/dist/data/designs/ios-vercel-swiftui.md +374 -0
  757. package/dist/data/designs/ios-vercel.md +399 -0
  758. package/dist/data/designs/ios-vero-android.md +498 -0
  759. package/dist/data/designs/ios-vero-expo.md +376 -0
  760. package/dist/data/designs/ios-vero-swiftui.md +374 -0
  761. package/dist/data/designs/ios-vero.md +394 -0
  762. package/dist/data/designs/ios-viber-android.md +377 -0
  763. package/dist/data/designs/ios-viber-expo.md +375 -0
  764. package/dist/data/designs/ios-viber-swiftui.md +378 -0
  765. package/dist/data/designs/ios-viber.md +403 -0
  766. package/dist/data/designs/ios-vimeo-android.md +465 -0
  767. package/dist/data/designs/ios-vimeo-expo.md +347 -0
  768. package/dist/data/designs/ios-vimeo-swiftui.md +383 -0
  769. package/dist/data/designs/ios-vimeo.md +403 -0
  770. package/dist/data/designs/ios-vrbo-android.md +488 -0
  771. package/dist/data/designs/ios-vrbo-expo.md +395 -0
  772. package/dist/data/designs/ios-vrbo-swiftui.md +421 -0
  773. package/dist/data/designs/ios-vrbo.md +383 -0
  774. package/dist/data/designs/ios-vsco-android.md +490 -0
  775. package/dist/data/designs/ios-vsco-expo.md +413 -0
  776. package/dist/data/designs/ios-vsco-swiftui.md +449 -0
  777. package/dist/data/designs/ios-vsco.md +412 -0
  778. package/dist/data/designs/ios-walmart-android.md +479 -0
  779. package/dist/data/designs/ios-walmart-expo.md +370 -0
  780. package/dist/data/designs/ios-walmart-swiftui.md +387 -0
  781. package/dist/data/designs/ios-walmart.md +366 -0
  782. package/dist/data/designs/ios-waze-android.md +695 -0
  783. package/dist/data/designs/ios-waze-expo.md +587 -0
  784. package/dist/data/designs/ios-waze-swiftui.md +601 -0
  785. package/dist/data/designs/ios-waze.md +453 -0
  786. package/dist/data/designs/ios-webull-android.md +429 -0
  787. package/dist/data/designs/ios-webull-expo.md +384 -0
  788. package/dist/data/designs/ios-webull-swiftui.md +406 -0
  789. package/dist/data/designs/ios-webull.md +375 -0
  790. package/dist/data/designs/ios-wechat-android.md +545 -0
  791. package/dist/data/designs/ios-wechat-expo.md +398 -0
  792. package/dist/data/designs/ios-wechat-swiftui.md +394 -0
  793. package/dist/data/designs/ios-wechat.md +363 -0
  794. package/dist/data/designs/ios-whatsapp-android.md +568 -0
  795. package/dist/data/designs/ios-whatsapp-expo.md +502 -0
  796. package/dist/data/designs/ios-whatsapp-swiftui.md +483 -0
  797. package/dist/data/designs/ios-whatsapp.md +425 -0
  798. package/dist/data/designs/ios-whoop-android.md +635 -0
  799. package/dist/data/designs/ios-whoop-expo.md +562 -0
  800. package/dist/data/designs/ios-whoop-swiftui.md +553 -0
  801. package/dist/data/designs/ios-whoop.md +419 -0
  802. package/dist/data/designs/ios-wise-android.md +428 -0
  803. package/dist/data/designs/ios-wise-expo.md +357 -0
  804. package/dist/data/designs/ios-wise-swiftui.md +356 -0
  805. package/dist/data/designs/ios-wise.md +361 -0
  806. package/dist/data/designs/ios-x-twitter-android.md +618 -0
  807. package/dist/data/designs/ios-x-twitter-expo.md +442 -0
  808. package/dist/data/designs/ios-x-twitter-swiftui.md +429 -0
  809. package/dist/data/designs/ios-x-twitter.md +432 -0
  810. package/dist/data/designs/ios-yelp-android.md +455 -0
  811. package/dist/data/designs/ios-yelp-expo.md +397 -0
  812. package/dist/data/designs/ios-yelp-swiftui.md +369 -0
  813. package/dist/data/designs/ios-yelp.md +398 -0
  814. package/dist/data/designs/ios-youtube-android.md +668 -0
  815. package/dist/data/designs/ios-youtube-expo.md +435 -0
  816. package/dist/data/designs/ios-youtube-music-android.md +450 -0
  817. package/dist/data/designs/ios-youtube-music-expo.md +404 -0
  818. package/dist/data/designs/ios-youtube-music-swiftui.md +434 -0
  819. package/dist/data/designs/ios-youtube-music.md +381 -0
  820. package/dist/data/designs/ios-youtube-swiftui.md +496 -0
  821. package/dist/data/designs/ios-youtube.md +466 -0
  822. package/dist/data/designs/ios-zalo-android.md +510 -0
  823. package/dist/data/designs/ios-zalo-expo.md +389 -0
  824. package/dist/data/designs/ios-zalo-swiftui.md +431 -0
  825. package/dist/data/designs/ios-zalo.md +388 -0
  826. package/dist/data/designs/ios-zomato-android.md +527 -0
  827. package/dist/data/designs/ios-zomato-expo.md +437 -0
  828. package/dist/data/designs/ios-zomato-swiftui.md +433 -0
  829. package/dist/data/designs/ios-zomato.md +439 -0
  830. package/dist/data/designs/ios-zoom-android.md +423 -0
  831. package/dist/data/designs/ios-zoom-expo.md +355 -0
  832. package/dist/data/designs/ios-zoom-swiftui.md +379 -0
  833. package/dist/data/designs/ios-zoom.md +357 -0
  834. package/dist/data/designs/ios-zwift-android.md +402 -0
  835. package/dist/data/designs/ios-zwift-expo.md +402 -0
  836. package/dist/data/designs/ios-zwift-swiftui.md +378 -0
  837. package/dist/data/designs/ios-zwift.md +411 -0
  838. package/dist/data/designs/kraken.md +125 -0
  839. package/dist/data/designs/lamborghini.md +288 -0
  840. package/dist/data/designs/linear.app.md +548 -0
  841. package/dist/data/designs/lovable.md +298 -0
  842. package/dist/data/designs/mastercard.md +365 -0
  843. package/dist/data/designs/material-clean.md +289 -0
  844. package/dist/data/designs/meta.md +683 -0
  845. package/dist/data/designs/midnight-pro.md +256 -0
  846. package/dist/data/designs/minimal-brutalist.md +245 -0
  847. package/dist/data/designs/minimax.md +746 -0
  848. package/dist/data/designs/mintlify.md +852 -0
  849. package/dist/data/designs/miro.md +825 -0
  850. package/dist/data/designs/mistral.ai.md +773 -0
  851. package/dist/data/designs/mongodb.md +767 -0
  852. package/dist/data/designs/nike.md +575 -0
  853. package/dist/data/designs/nintendo-2001.md +649 -0
  854. package/dist/data/designs/notion.md +821 -0
  855. package/dist/data/designs/nova-mobile-design-md.md +235 -0
  856. package/dist/data/designs/nvidia.md +640 -0
  857. package/dist/data/designs/ollama.md +539 -0
  858. package/dist/data/designs/opencode.ai.md +521 -0
  859. package/dist/data/designs/pinterest.md +597 -0
  860. package/dist/data/designs/playful-color.md +253 -0
  861. package/dist/data/designs/playstation.md +661 -0
  862. package/dist/data/designs/posthog.md +690 -0
  863. package/dist/data/designs/raycast.md +669 -0
  864. package/dist/data/designs/renault.md +589 -0
  865. package/dist/data/designs/replicate.md +616 -0
  866. package/dist/data/designs/resend.md +585 -0
  867. package/dist/data/designs/revolut.md +636 -0
  868. package/dist/data/designs/runwayml.md +244 -0
  869. package/dist/data/designs/sanity.md +357 -0
  870. package/dist/data/designs/sentry.md +551 -0
  871. package/dist/data/designs/shopify.md +516 -0
  872. package/dist/data/designs/slack.md +482 -0
  873. package/dist/data/designs/soft-glass.md +258 -0
  874. package/dist/data/designs/spacex.md +363 -0
  875. package/dist/data/designs/spotify.md +246 -0
  876. package/dist/data/designs/starbucks.md +580 -0
  877. package/dist/data/designs/stripe.md +487 -0
  878. package/dist/data/designs/supabase.md +462 -0
  879. package/dist/data/designs/superhuman.md +448 -0
  880. package/dist/data/designs/tesla.md +286 -0
  881. package/dist/data/designs/theverge.md +339 -0
  882. package/dist/data/designs/together.ai.md +633 -0
  883. package/dist/data/designs/uber.md +636 -0
  884. package/dist/data/designs/vercel.md +736 -0
  885. package/dist/data/designs/vodafone.md +538 -0
  886. package/dist/data/designs/voltagent.md +521 -0
  887. package/dist/data/designs/warp.md +526 -0
  888. package/dist/data/designs/webflow.md +588 -0
  889. package/dist/data/designs/wired.md +497 -0
  890. package/dist/data/designs/wise.md +544 -0
  891. package/dist/data/designs/x.ai.md +465 -0
  892. package/dist/data/designs/zapier.md +537 -0
  893. package/dist/data/index.json +2600 -0
  894. package/dist/httpServer.js +83 -0
  895. package/dist/index.js +13 -0
  896. package/dist/server.js +32 -0
  897. package/dist/services/designStore.js +59 -0
  898. package/dist/services/linter.js +87 -0
  899. package/dist/services/sectionParser.js +154 -0
  900. package/dist/tools/authoring.js +131 -0
  901. package/dist/tools/browse.js +232 -0
  902. package/dist/tools/diff.js +88 -0
  903. package/dist/tools/getDesign.js +216 -0
  904. package/dist/tools/guardrails.js +87 -0
  905. package/dist/tools/lint.js +104 -0
  906. package/dist/types.js +1 -0
  907. package/package.json +65 -0
@@ -0,0 +1,753 @@
1
+ # ChatGPT (iOS) — Jetpack Compose Implementation Guide
2
+
3
+ Companion to [DESIGN.md](DESIGN.md) — the framework-neutral spec. This file ports ChatGPT's visual language to **Android with Jetpack Compose (Material 3)**: light + dark color token objects, a `Typography` set, paste-ready `@Composable`s, the black/white send button, the markdown atoms, and the full-screen voice-mode sphere.
4
+
5
+ > Why a Compose guide for an iOS-referenced app? The DESIGN.md tokens are platform-neutral. This file keeps the *visual* identity (ChatGPT's monochromatic minimalism, the circular send button, bubble-less assistant text, the pulsing blue voice sphere) while making everything idiomatic Android — a custom asymmetric `Shape` for the user bubble, `ModalNavigationDrawer` for the conversation sidebar, an `Animatable`-driven `Canvas` sphere, `sp`/`dp` instead of `pt`. ChatGPT is radically flat — shadows appear only on ephemeral floating UI.
6
+
7
+ Assumes Compose BOM `2024.09+`, Kotlin `2.0+`, `minSdk 24`, Material 3, [Coil](https://coil-kt.github.io/coil/) `2.6+` for attachment thumbnails, and a markdown renderer such as [compose-markdown](https://github.com/jeziellago/compose-markdown) or `Markwon` for assistant content (headings, code, tables, LaTeX). No color extraction, so no `androidx.palette`.
8
+
9
+ ## 1. Color Tokens
10
+
11
+ ChatGPT ships first-class light **and** dark. Define both palettes, then select per system theme.
12
+
13
+ ```kotlin
14
+ // ui/theme/GptColors.kt
15
+ import androidx.compose.ui.graphics.Color
16
+
17
+ object GptColors {
18
+ // Canvas — pure white light / charcoal (NOT true black) dark
19
+ val Canvas = Color(0xFFFFFFFF)
20
+ val DarkCanvas = Color(0xFF212121)
21
+
22
+ // Sidebar (darker than canvas — signature treatment)
23
+ val SidebarLight = Color(0xFFF9F9F9)
24
+ val SidebarDark = Color(0xFF181818)
25
+ val SidebarActive = Color(0xFFECECEC)
26
+ val SidebarActiveDk = Color(0xFF2F2F2F)
27
+ val SidebarHover = Color(0xFFF0F0F0)
28
+ val SidebarHoverDk = Color(0xFF252525)
29
+ val Divider = Color(0xFFE5E5E5)
30
+ val DividerDark = Color(0xFF424242)
31
+
32
+ // Text
33
+ val TextPrimary = Color(0xFF0D0D0D) // softer than pure #000
34
+ val TextSecondary = Color(0xFF676767) // placeholder, timestamps
35
+ val TextTertiary = Color(0xFF8E8E8E) // disabled
36
+ val DarkTextPrimary = Color(0xFFECECEC)
37
+ val DarkTextSecondary = Color(0xFFB4B4B4)
38
+ val DarkTextTertiary = Color(0xFF707070)
39
+
40
+ // User message bubble (assistant text has NO bubble)
41
+ val UserBubbleLight = Color(0xFFF7F7F8)
42
+ val UserBubbleDark = Color(0xFF2F2F2F)
43
+
44
+ // Code
45
+ val CodeBlockLight = Color(0xFFF7F7F8)
46
+ val CodeBlockDark = Color(0xFF1E1E1E)
47
+ val CodeInlineLight = Color(0xFFF0F0F0)
48
+ val CodeInlineDark = Color(0xFF424242)
49
+ val CodeBorderLight = Color(0xFFE5E5E5)
50
+ val CodeBorderDark = Color(0xFF363636)
51
+
52
+ // Send button (inverse of canvas)
53
+ val SendLight = Color(0xFF0D0D0D)
54
+ val SendDark = Color(0xFFFFFFFF)
55
+ val SendDisabled = Color(0xFFCCCCCC)
56
+ val SendDisabledDk = Color(0xFF4D4D4D)
57
+
58
+ // Semantic — monochromatic; blue only on links
59
+ val LinkBlue = Color(0xFF2A7FFF)
60
+ val LegacyGreen = Color(0xFF10A37F) // largely retired — wordmark / legacy UI only
61
+ val ErrorRed = Color(0xFFE53E3E)
62
+ val WarningOrange = Color(0xFFD97706)
63
+
64
+ // Voice-mode sphere gradient
65
+ val VoiceBlue1 = Color(0xFF3B82F6)
66
+ val VoiceBlue2 = Color(0xFF60A5FA)
67
+ val VoiceBlue3 = Color(0xFF93C5FD)
68
+ }
69
+ ```
70
+
71
+ Wire both into Material 3 schemes and switch on the system setting. ChatGPT supports both modes equally, so unlike most strong-brand apps this one *does* provide a real `lightColorScheme` and `darkColorScheme`.
72
+
73
+ ```kotlin
74
+ // ui/theme/Theme.kt
75
+ import androidx.compose.foundation.isSystemInDarkTheme
76
+ import androidx.compose.material3.darkColorScheme
77
+ import androidx.compose.material3.lightColorScheme
78
+ import androidx.compose.material3.MaterialTheme
79
+
80
+ private val GptLight = lightColorScheme(
81
+ primary = GptColors.SendLight, // monochrome — the "accent" is ink
82
+ onPrimary = GptColors.Canvas,
83
+ background = GptColors.Canvas,
84
+ onBackground = GptColors.TextPrimary,
85
+ surface = GptColors.Canvas,
86
+ onSurface = GptColors.TextPrimary,
87
+ surfaceVariant = GptColors.UserBubbleLight,
88
+ outline = GptColors.Divider,
89
+ error = GptColors.ErrorRed,
90
+ )
91
+
92
+ private val GptDark = darkColorScheme(
93
+ primary = GptColors.SendDark,
94
+ onPrimary = GptColors.TextPrimary,
95
+ background = GptColors.DarkCanvas,
96
+ onBackground = GptColors.DarkTextPrimary,
97
+ surface = GptColors.DarkCanvas,
98
+ onSurface = GptColors.DarkTextPrimary,
99
+ surfaceVariant = GptColors.UserBubbleDark,
100
+ outline = GptColors.DividerDark,
101
+ error = GptColors.ErrorRed,
102
+ )
103
+
104
+ @Composable
105
+ fun GptTheme(content: @Composable () -> Unit) =
106
+ MaterialTheme(
107
+ colorScheme = if (isSystemInDarkTheme()) GptDark else GptLight,
108
+ typography = GptTypography,
109
+ content = content,
110
+ )
111
+ ```
112
+
113
+ ## 2. Typography
114
+
115
+ ChatGPT uses **Söhne** (Klim Type Foundry, commercially licensed), falling back to Inter, then SF Pro. Drop Söhne/Inter TTFs in `res/font/` (lowercase, snake_case); Roboto is the Android system fallback. Weights are concentrated at 400/500/600 — no bold, no thin. Code is always monospace (Menlo / SF Mono on iOS → bundle JetBrains Mono / use the platform monospace family).
116
+
117
+ ```kotlin
118
+ // ui/theme/GptType.kt
119
+ import androidx.compose.material3.Typography
120
+ import androidx.compose.ui.text.TextStyle
121
+ import androidx.compose.ui.text.font.Font
122
+ import androidx.compose.ui.text.font.FontFamily
123
+ import androidx.compose.ui.text.font.FontWeight
124
+ import androidx.compose.ui.unit.sp
125
+
126
+ val Sohne = FontFamily(
127
+ Font(R.font.sohne_buch, FontWeight.Normal), // 400 "Buch"
128
+ Font(R.font.sohne_kraftig, FontWeight.Medium), // 500 "Kräftig"
129
+ Font(R.font.sohne_halbfett, FontWeight.SemiBold), // 600 "Halbfett"
130
+ )
131
+ val GptMono = FontFamily(Font(R.font.jetbrains_mono_regular, FontWeight.Normal)) // code only
132
+
133
+ // Named ramp — mirrors DESIGN.md §3 exactly (pt → sp 1:1, same weights/tracking)
134
+ object GptText {
135
+ val Body = TextStyle(Sohne, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp)
136
+ val BodyCompact = TextStyle(Sohne, fontWeight = FontWeight.Normal, fontSize = 15.sp, lineHeight = 22.sp)
137
+ val H1 = TextStyle(Sohne, fontWeight = FontWeight.SemiBold, fontSize = 24.sp, lineHeight = 30.sp, letterSpacing = (-0.2).sp)
138
+ val H2 = TextStyle(Sohne, fontWeight = FontWeight.SemiBold, fontSize = 20.sp, lineHeight = 26.sp, letterSpacing = (-0.15).sp)
139
+ val H3 = TextStyle(Sohne, fontWeight = FontWeight.SemiBold, fontSize = 17.sp, lineHeight = 23.sp, letterSpacing = (-0.1).sp)
140
+ val H4 = TextStyle(Sohne, fontWeight = FontWeight.SemiBold, fontSize = 16.sp, lineHeight = 22.sp)
141
+ val ModelChip = TextStyle(Sohne, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 14.sp)
142
+ val SidebarTitle = TextStyle(Sohne, fontWeight = FontWeight.Medium, fontSize = 15.sp, lineHeight = 20.sp)
143
+ val SidebarSection = TextStyle(Sohne, fontWeight = FontWeight.Medium, fontSize = 12.sp, lineHeight = 12.sp, letterSpacing = 0.2.sp)
144
+ val Button = TextStyle(Sohne, fontWeight = FontWeight.Medium, fontSize = 14.sp, lineHeight = 14.sp)
145
+ val Meta = TextStyle(Sohne, fontWeight = FontWeight.Normal, fontSize = 13.sp, lineHeight = 16.sp)
146
+ val Placeholder = TextStyle(Sohne, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 22.sp)
147
+ val LinkText = TextStyle(Sohne, fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp)
148
+ val CodeInline = TextStyle(GptMono, fontWeight = FontWeight.Normal, fontSize = 14.sp, lineHeight = 18.sp)
149
+ val CodeBlock = TextStyle(GptMono, fontWeight = FontWeight.Normal, fontSize = 13.sp, lineHeight = 20.sp)
150
+ }
151
+
152
+ // Map onto Material 3 slots so stock components inherit the brand
153
+ val GptTypography = Typography(
154
+ headlineLarge = GptText.H1,
155
+ headlineSmall = GptText.H2,
156
+ titleMedium = GptText.H3,
157
+ bodyMedium = GptText.Body,
158
+ labelSmall = GptText.SidebarSection,
159
+ )
160
+ ```
161
+
162
+ Typography follows **document rhythm, not UI rhythm** — 1.5 line-height on body (`24.sp / 16.sp`), markdown headings stepping down. Code styles must never be substituted away from monospace.
163
+
164
+ ## 3. Signature Components
165
+
166
+ ### Send Button (the black/white circle)
167
+
168
+ The single most recognizable ChatGPT component — a 32dp circle, ink-on-canvas inverse, single up-arrow. Becomes a square "stop" during streaming.
169
+
170
+ ```kotlin
171
+ import androidx.compose.animation.core.animateFloatAsState
172
+ import androidx.compose.animation.core.spring
173
+ import androidx.compose.foundation.background
174
+ import androidx.compose.foundation.clickable
175
+ import androidx.compose.foundation.interaction.MutableInteractionSource
176
+ import androidx.compose.foundation.interaction.collectIsPressedAsState
177
+ import androidx.compose.foundation.isSystemInDarkTheme
178
+ import androidx.compose.foundation.layout.size
179
+ import androidx.compose.foundation.shape.CircleShape
180
+ import androidx.compose.foundation.shape.RoundedCornerShape
181
+ import androidx.compose.material.icons.Icons
182
+ import androidx.compose.material.icons.filled.ArrowUpward
183
+ import androidx.compose.material.icons.filled.Stop
184
+ import androidx.compose.material3.Icon
185
+ import androidx.compose.runtime.*
186
+ import androidx.compose.ui.Modifier
187
+ import androidx.compose.ui.draw.clip
188
+ import androidx.compose.ui.draw.scale
189
+ import androidx.compose.ui.hapticfeedback.HapticFeedbackType
190
+ import androidx.compose.ui.platform.LocalHapticFeedback
191
+ import androidx.compose.ui.unit.dp
192
+
193
+ @Composable
194
+ fun SendButton(
195
+ enabled: Boolean,
196
+ generating: Boolean,
197
+ onClick: () -> Unit,
198
+ modifier: Modifier = Modifier,
199
+ ) {
200
+ val dark = isSystemInDarkTheme()
201
+ val interaction = remember { MutableInteractionSource() }
202
+ val pressed by interaction.collectIsPressedAsState()
203
+ val scale by animateFloatAsState(
204
+ targetValue = if (pressed) 0.94f else 1f,
205
+ animationSpec = spring(dampingRatio = 0.7f, stiffness = 700f),
206
+ label = "sendScale",
207
+ )
208
+ val circle = when {
209
+ !enabled && !generating -> if (dark) GptColors.SendDisabledDk else GptColors.SendDisabled
210
+ dark -> GptColors.SendDark
211
+ else -> GptColors.SendLight
212
+ }
213
+ val iconTint = if (dark) GptColors.SendLight else GptColors.SendDark
214
+ val haptics = LocalHapticFeedback.current
215
+
216
+ Box(
217
+ modifier = modifier
218
+ .size(32.dp)
219
+ .scale(scale)
220
+ .clip(CircleShape)
221
+ .background(circle)
222
+ .clickable(interaction, indication = null, enabled = enabled || generating) {
223
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .impact solid
224
+ onClick()
225
+ },
226
+ contentAlignment = Alignment.Center,
227
+ ) {
228
+ Icon(
229
+ imageVector = if (generating) Icons.Filled.Stop else Icons.Filled.ArrowUpward,
230
+ contentDescription = if (generating) "Stop generating" else "Send",
231
+ tint = iconTint,
232
+ modifier = Modifier.size(16.dp),
233
+ )
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### User Message Bubble (asymmetric corners)
239
+
240
+ Right-aligned, max-width ~80%, soft-gray fill, with the "pointer" corner flattened to 4dp on the bottom-trailing edge.
241
+
242
+ ```kotlin
243
+ import androidx.compose.foundation.layout.*
244
+ import androidx.compose.material3.Text
245
+
246
+ private val UserBubbleShape = RoundedCornerShape(
247
+ topStart = 18.dp, topEnd = 18.dp, bottomStart = 18.dp, bottomEnd = 4.dp,
248
+ )
249
+
250
+ @Composable
251
+ fun UserMessageBubble(text: String, modifier: Modifier = Modifier) {
252
+ val dark = isSystemInDarkTheme()
253
+ Row(modifier.fillMaxWidth().padding(horizontal = 16.dp)) {
254
+ Spacer(Modifier.weight(0.2f)) // ~80% max width
255
+ Box(
256
+ Modifier
257
+ .weight(0.8f, fill = false)
258
+ .clip(UserBubbleShape)
259
+ .background(if (dark) GptColors.UserBubbleDark else GptColors.UserBubbleLight)
260
+ .padding(horizontal = 14.dp, vertical = 10.dp),
261
+ ) {
262
+ Text(
263
+ text,
264
+ style = GptText.Body,
265
+ color = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary,
266
+ )
267
+ }
268
+ }
269
+ }
270
+ ```
271
+
272
+ ### Assistant Message (no bubble + feedback row)
273
+
274
+ Plain markdown-rendered content flowing inline — **no bubble, no box** — with the canonical 4-icon feedback row beneath.
275
+
276
+ ```kotlin
277
+ import androidx.compose.material.icons.filled.ContentCopy
278
+ import androidx.compose.material.icons.filled.Refresh
279
+ import androidx.compose.material.icons.filled.ThumbDown
280
+ import androidx.compose.material.icons.filled.ThumbUp
281
+ import androidx.compose.material.icons.filled.AutoAwesome
282
+ import androidx.compose.ui.graphics.vector.ImageVector
283
+
284
+ @Composable
285
+ fun AssistantMessage(
286
+ markdown: String,
287
+ onRegenerate: () -> Unit,
288
+ onCopy: () -> Unit,
289
+ onThumbUp: () -> Unit,
290
+ onThumbDown: () -> Unit,
291
+ modifier: Modifier = Modifier,
292
+ ) {
293
+ val dark = isSystemInDarkTheme()
294
+ val textColor = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary
295
+ Column(
296
+ modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 8.dp),
297
+ verticalArrangement = Arrangement.spacedBy(12.dp),
298
+ ) {
299
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
300
+ Icon(Icons.Filled.AutoAwesome, null, tint = textColor, modifier = Modifier.size(24.dp))
301
+ // Replace with a real markdown renderer (Markwon / compose-markdown):
302
+ // renders H1/H2/H3, lists, inline code, code blocks, tables, LaTeX.
303
+ Text(markdown, style = GptText.Body, color = textColor)
304
+ }
305
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(start = 32.dp)) {
306
+ FeedbackIcon(Icons.Filled.Refresh, "Regenerate", onRegenerate)
307
+ FeedbackIcon(Icons.Filled.ContentCopy, "Copy", onCopy)
308
+ FeedbackIcon(Icons.Filled.ThumbUp, "Good response", onThumbUp)
309
+ FeedbackIcon(Icons.Filled.ThumbDown, "Bad response", onThumbDown)
310
+ }
311
+ }
312
+ }
313
+
314
+ @Composable
315
+ private fun FeedbackIcon(icon: ImageVector, label: String, onClick: () -> Unit) {
316
+ val dark = isSystemInDarkTheme()
317
+ val interaction = remember { MutableInteractionSource() }
318
+ val pressed by interaction.collectIsPressedAsState()
319
+ val scale by animateFloatAsState(if (pressed) 1.15f else 1f, label = "thumbScale")
320
+ val haptics = LocalHapticFeedback.current
321
+ Box(
322
+ Modifier
323
+ .size(32.dp) // tight per iOS spec — see §8 (bump to 48dp for strict a11y)
324
+ .scale(scale)
325
+ .clip(CircleShape)
326
+ .clickable(interaction, indication = null) {
327
+ haptics.performHapticFeedback(HapticFeedbackType.TextHandleMove) // ≈ .impact soft
328
+ onClick()
329
+ },
330
+ contentAlignment = Alignment.Center,
331
+ ) {
332
+ Icon(icon, contentDescription = label,
333
+ tint = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary,
334
+ modifier = Modifier.size(16.dp))
335
+ }
336
+ }
337
+ ```
338
+
339
+ ### Code Block (with Copy button)
340
+
341
+ ```kotlin
342
+ import androidx.compose.foundation.border
343
+ import androidx.compose.foundation.horizontalScroll
344
+ import androidx.compose.foundation.rememberScrollState
345
+ import androidx.compose.material.icons.filled.Check
346
+ import kotlinx.coroutines.delay
347
+
348
+ @Composable
349
+ fun CodeBlock(language: String, code: String, modifier: Modifier = Modifier) {
350
+ val dark = isSystemInDarkTheme()
351
+ val bg = if (dark) GptColors.CodeBlockDark else GptColors.CodeBlockLight
352
+ val border = if (dark) GptColors.CodeBorderDark else GptColors.CodeBorderLight
353
+ val secondary = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary
354
+ var copied by remember { mutableStateOf(false) }
355
+ val clipboard = androidx.compose.ui.platform.LocalClipboardManager.current
356
+
357
+ LaunchedEffect(copied) { if (copied) { delay(1000); copied = false } }
358
+
359
+ Column(
360
+ modifier
361
+ .fillMaxWidth()
362
+ .padding(horizontal = 16.dp)
363
+ .clip(RoundedCornerShape(8.dp))
364
+ .background(bg)
365
+ .border(1.dp, border, RoundedCornerShape(8.dp)),
366
+ ) {
367
+ Row(
368
+ Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 8.dp),
369
+ verticalAlignment = Alignment.CenterVertically,
370
+ ) {
371
+ Text(language, style = GptText.SidebarSection, color = secondary)
372
+ Spacer(Modifier.weight(1f))
373
+ Row(
374
+ Modifier.clip(RoundedCornerShape(6.dp)).clickable {
375
+ clipboard.setText(androidx.compose.ui.text.AnnotatedString(code)); copied = true
376
+ }.padding(4.dp),
377
+ verticalAlignment = Alignment.CenterVertically,
378
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
379
+ ) {
380
+ Icon(if (copied) Icons.Filled.Check else Icons.Filled.ContentCopy, null,
381
+ tint = secondary, modifier = Modifier.size(12.dp))
382
+ Text(if (copied) "Copied!" else "Copy", style = GptText.Button, color = secondary)
383
+ }
384
+ }
385
+ Box(Modifier.fillMaxWidth().height(1.dp).background(border))
386
+ Box(Modifier.horizontalScroll(rememberScrollState()).padding(12.dp)) {
387
+ Text(code, style = GptText.CodeBlock,
388
+ color = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary)
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ ### Model Selector Chip
395
+
396
+ ```kotlin
397
+ import androidx.compose.material.icons.filled.KeyboardArrowDown
398
+
399
+ @Composable
400
+ fun ModelSelectorChip(modelName: String, onTap: () -> Unit, modifier: Modifier = Modifier) {
401
+ val dark = isSystemInDarkTheme()
402
+ val text = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary
403
+ val border = if (dark) GptColors.DividerDark else GptColors.Divider
404
+ Row(
405
+ modifier
406
+ .clip(CircleShape)
407
+ .border(1.dp, border, CircleShape)
408
+ .clickable { onTap() }
409
+ .padding(horizontal = 10.dp, vertical = 6.dp),
410
+ verticalAlignment = Alignment.CenterVertically,
411
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
412
+ ) {
413
+ Icon(Icons.Filled.AutoAwesome, null, tint = text, modifier = Modifier.size(14.dp))
414
+ Text(modelName, style = GptText.ModelChip, color = text)
415
+ Icon(Icons.Filled.KeyboardArrowDown, "Choose model",
416
+ tint = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary,
417
+ modifier = Modifier.size(10.dp))
418
+ }
419
+ }
420
+ ```
421
+
422
+ ### Message Composer
423
+
424
+ Bottom-docked rounded container. When empty: paperclip + globe + mic. When text entered: the send button replaces the trailing icons.
425
+
426
+ ```kotlin
427
+ import androidx.compose.foundation.text.BasicTextField
428
+ import androidx.compose.material.icons.filled.Add
429
+ import androidx.compose.material.icons.filled.Mic
430
+ import androidx.compose.material.icons.filled.Public
431
+ import androidx.compose.ui.text.input.TextFieldValue
432
+
433
+ @Composable
434
+ fun Composer(
435
+ value: TextFieldValue,
436
+ onValueChange: (TextFieldValue) -> Unit,
437
+ onSend: () -> Unit,
438
+ onVoice: () -> Unit,
439
+ onAttach: () -> Unit,
440
+ onWebSearch: () -> Unit,
441
+ modifier: Modifier = Modifier,
442
+ ) {
443
+ val dark = isSystemInDarkTheme()
444
+ val empty = value.text.isBlank()
445
+ val border = if (dark) GptColors.DividerDark else GptColors.Divider
446
+ val secondary = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary
447
+
448
+ Row(
449
+ modifier
450
+ .fillMaxWidth()
451
+ .padding(horizontal = 16.dp, vertical = 8.dp)
452
+ .clip(RoundedCornerShape(24.dp))
453
+ .background(if (dark) GptColors.DarkCanvas else GptColors.Canvas)
454
+ .border(1.dp, border, RoundedCornerShape(24.dp))
455
+ .padding(horizontal = 8.dp, vertical = 8.dp),
456
+ verticalAlignment = Alignment.Bottom,
457
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
458
+ ) {
459
+ IconHit(Icons.Filled.Add, "Attach", secondary, onAttach)
460
+ Box(Modifier.weight(1f).padding(vertical = 10.dp)) {
461
+ if (value.text.isEmpty()) {
462
+ Text("Message ChatGPT…", style = GptText.Placeholder, color = secondary)
463
+ }
464
+ BasicTextField(
465
+ value = value, onValueChange = onValueChange,
466
+ textStyle = GptText.Body.copy(
467
+ color = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary),
468
+ maxLines = 6,
469
+ )
470
+ }
471
+ if (empty) {
472
+ IconHit(Icons.Filled.Public, "Web search", secondary, onWebSearch)
473
+ Box(
474
+ Modifier.size(32.dp).clip(CircleShape).border(1.dp, border, CircleShape)
475
+ .clickable { onVoice() },
476
+ contentAlignment = Alignment.Center,
477
+ ) { Icon(Icons.Filled.Mic, "Voice", tint = secondary, modifier = Modifier.size(18.dp)) }
478
+ } else {
479
+ SendButton(enabled = true, generating = false, onClick = onSend)
480
+ }
481
+ }
482
+ }
483
+
484
+ @Composable
485
+ private fun IconHit(icon: ImageVector, label: String, tint: Color, onClick: () -> Unit) {
486
+ Box(
487
+ Modifier.size(40.dp).clip(RoundedCornerShape(8.dp)).clickable { onClick() },
488
+ contentAlignment = Alignment.Center,
489
+ ) { Icon(icon, contentDescription = label, tint = tint, modifier = Modifier.size(20.dp)) }
490
+ }
491
+ ```
492
+
493
+ ### Typing Indicator (3-dot pulse)
494
+
495
+ ```kotlin
496
+ import androidx.compose.animation.core.RepeatMode
497
+ import androidx.compose.animation.core.infiniteRepeatable
498
+ import androidx.compose.animation.core.rememberInfiniteTransition
499
+ import androidx.compose.animation.core.tween
500
+ import androidx.compose.ui.draw.alpha
501
+
502
+ @Composable
503
+ fun TypingIndicator(modifier: Modifier = Modifier) {
504
+ val dark = isSystemInDarkTheme()
505
+ val color = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary
506
+ val t = rememberInfiniteTransition(label = "typing")
507
+ Row(modifier, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
508
+ repeat(3) { i ->
509
+ val a by t.animateFloat(
510
+ initialValue = 0.3f, targetValue = 1f,
511
+ animationSpec = infiniteRepeatable(
512
+ tween(600, delayMillis = i * 200), RepeatMode.Reverse),
513
+ label = "dot$i",
514
+ )
515
+ Box(Modifier.size(8.dp).alpha(a).clip(CircleShape).background(color))
516
+ }
517
+ }
518
+ }
519
+ ```
520
+
521
+ ## 4. Voice Mode (the distinctive system)
522
+
523
+ ChatGPT has no color extraction; its flagship dynamic moment is **voice mode** — a full-screen pulsing blue gradient sphere that morphs as you speak. Port the SwiftUI radial-gradient sphere with an `Animatable` scale pulse plus a subtle organic deformation. This is the product's most-recognized animation.
524
+
525
+ ```kotlin
526
+ import androidx.compose.animation.core.Animatable
527
+ import androidx.compose.animation.core.LinearEasing
528
+ import androidx.compose.foundation.Canvas
529
+ import androidx.compose.material.icons.filled.Close
530
+ import androidx.compose.ui.geometry.Offset
531
+ import androidx.compose.ui.graphics.Brush
532
+ import kotlin.math.sin
533
+
534
+ @Composable
535
+ fun VoiceModeOverlay(onEnd: () -> Unit) {
536
+ val haptics = LocalHapticFeedback.current
537
+ val pulse = remember { Animatable(1f) }
538
+ val morph = rememberInfiniteTransition(label = "morph")
539
+ val wobble by morph.animateFloat(
540
+ initialValue = 0f, targetValue = (2 * Math.PI).toFloat(),
541
+ animationSpec = infiniteRepeatable(tween(3000, easing = LinearEasing)),
542
+ label = "wobble",
543
+ )
544
+
545
+ LaunchedEffect(Unit) {
546
+ haptics.performHapticFeedback(HapticFeedbackType.LongPress) // ≈ .impact solid on entry
547
+ // Continuous organic pulse: scale 0.95 ↔ 1.05 over 2s ease-in-out
548
+ while (true) {
549
+ pulse.animateTo(1.05f, tween(2000))
550
+ pulse.animateTo(0.95f, tween(2000))
551
+ }
552
+ }
553
+
554
+ Box(Modifier.fillMaxSize().background(Color.Black), contentAlignment = Alignment.Center) {
555
+ Canvas(Modifier.size(280.dp).scale(pulse.value)) {
556
+ // Perlin-ish deformation: radius modulated by a couple of sine terms
557
+ val baseR = size.minDimension / 2f
558
+ val r = baseR * (1f + 0.03f * sin(wobble) + 0.02f * sin(wobble * 2.3f))
559
+ drawCircle(
560
+ brush = Brush.radialGradient(
561
+ colors = listOf(GptColors.VoiceBlue1, GptColors.VoiceBlue2, GptColors.VoiceBlue3),
562
+ center = center,
563
+ radius = r,
564
+ ),
565
+ radius = r,
566
+ center = center,
567
+ )
568
+ // Glow: a soft larger translucent ring (Android has no blur shadow on Canvas)
569
+ drawCircle(
570
+ color = GptColors.VoiceBlue1.copy(alpha = 0.25f),
571
+ radius = r * 1.25f,
572
+ center = center,
573
+ )
574
+ }
575
+ Column(
576
+ Modifier.align(Alignment.BottomCenter).fillMaxWidth().padding(bottom = 40.dp),
577
+ horizontalAlignment = Alignment.CenterHorizontally,
578
+ verticalArrangement = Arrangement.spacedBy(16.dp),
579
+ ) {
580
+ Text("ChatGPT is listening…", style = GptText.Meta, color = Color.White.copy(alpha = 0.7f))
581
+ Row(
582
+ Modifier.fillMaxWidth().padding(horizontal = 24.dp),
583
+ horizontalArrangement = Arrangement.SpaceBetween,
584
+ verticalAlignment = Alignment.CenterVertically,
585
+ ) {
586
+ Text("Mute", style = GptText.Button, color = Color.White,
587
+ modifier = Modifier.clickable { /* toggle mute */ })
588
+ Box(
589
+ Modifier.size(44.dp).clip(CircleShape).background(Color.White.copy(alpha = 0.2f))
590
+ .clickable { onEnd() },
591
+ contentAlignment = Alignment.Center,
592
+ ) { Icon(Icons.Filled.Close, "End voice mode", tint = Color.White,
593
+ modifier = Modifier.size(18.dp)) }
594
+ }
595
+ }
596
+ }
597
+ }
598
+ ```
599
+
600
+ Present this as a full-screen destination with an `enter = scaleIn(initialScale = 0.9f) + fadeIn(tween(400))` transition. Drive an `AVAudioSession`-equivalent via `AudioManager`/`MediaRecorder`; request the `RECORD_AUDIO` runtime permission before entering. It is portrait-locked and ignores window insets (full-bleed sphere).
601
+
602
+ ## 5. Navigation
603
+
604
+ ChatGPT has **no bottom tab bar**. The conversation history is a left drawer — use Material 3 `ModalNavigationDrawer` (edge-swipe with `spring(dampingRatio = 0.8f)` on release). The chat "top nav" is a transparent 52dp row: sidebar toggle (leading) + model chip (center-leading) + "New chat" (trailing). The sidebar groups chats by time (Today / Yesterday / Previous 7 Days / Previous 30 Days / Month Year) and is darker than the canvas — the signature treatment.
605
+
606
+ ```kotlin
607
+ import androidx.compose.material.icons.filled.Edit
608
+ import androidx.compose.material.icons.filled.Menu
609
+ import androidx.compose.material.icons.filled.Search
610
+ import androidx.compose.material3.*
611
+
612
+ @OptIn(ExperimentalMaterial3Api::class)
613
+ @Composable
614
+ fun GptScaffold(
615
+ drawerState: DrawerState,
616
+ sections: List<Pair<String, List<String>>>, // section title to chat titles
617
+ onNewChat: () -> Unit,
618
+ chat: @Composable () -> Unit,
619
+ ) {
620
+ val dark = isSystemInDarkTheme()
621
+ ModalNavigationDrawer(
622
+ drawerState = drawerState,
623
+ drawerContent = {
624
+ ModalDrawerSheet(
625
+ drawerContainerColor = if (dark) GptColors.SidebarDark else GptColors.SidebarLight,
626
+ modifier = Modifier.width(300.dp),
627
+ ) {
628
+ Column {
629
+ SidebarButton(Icons.Filled.Edit, "New chat", onNewChat)
630
+ SidebarButton(Icons.Filled.Search, "Search chats") {}
631
+ LazyColumn {
632
+ sections.forEach { (title, chats) ->
633
+ item {
634
+ Text(title, style = GptText.SidebarSection,
635
+ color = if (dark) GptColors.DarkTextSecondary else GptColors.TextSecondary,
636
+ modifier = Modifier.padding(start = 12.dp, top = 16.dp, bottom = 4.dp))
637
+ }
638
+ items(chats) { c ->
639
+ Box(
640
+ Modifier.fillMaxWidth().height(40.dp)
641
+ .padding(horizontal = 12.dp),
642
+ contentAlignment = Alignment.CenterStart,
643
+ ) {
644
+ Text(c, style = GptText.SidebarTitle, maxLines = 1,
645
+ color = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary)
646
+ }
647
+ }
648
+ }
649
+ }
650
+ }
651
+ }
652
+ },
653
+ ) {
654
+ Scaffold(
655
+ containerColor = if (dark) GptColors.DarkCanvas else GptColors.Canvas,
656
+ topBar = {
657
+ Row(
658
+ Modifier.fillMaxWidth().padding(horizontal = 12.dp, vertical = 8.dp),
659
+ verticalAlignment = Alignment.CenterVertically,
660
+ ) {
661
+ Icon(Icons.Filled.Menu, "Open conversations",
662
+ tint = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary,
663
+ modifier = Modifier.size(24.dp))
664
+ Spacer(Modifier.width(8.dp))
665
+ ModelSelectorChip("GPT-4o", onTap = {})
666
+ Spacer(Modifier.weight(1f))
667
+ Icon(Icons.Filled.Edit, "New chat",
668
+ tint = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary,
669
+ modifier = Modifier.size(24.dp).clickable { onNewChat() })
670
+ }
671
+ },
672
+ ) { padding -> Box(Modifier.padding(padding)) { chat() } }
673
+ }
674
+ }
675
+
676
+ @Composable
677
+ private fun SidebarButton(icon: ImageVector, label: String, onClick: () -> Unit) {
678
+ val dark = isSystemInDarkTheme()
679
+ Row(
680
+ Modifier.padding(12.dp).fillMaxWidth().clip(RoundedCornerShape(8.dp))
681
+ .background(if (dark) GptColors.SidebarActiveDk else GptColors.SidebarActive)
682
+ .clickable { onClick() }.padding(horizontal = 12.dp, vertical = 10.dp),
683
+ verticalAlignment = Alignment.CenterVertically,
684
+ horizontalArrangement = Arrangement.spacedBy(10.dp),
685
+ ) {
686
+ Icon(icon, null, tint = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary,
687
+ modifier = Modifier.size(18.dp))
688
+ Text(label, style = GptText.Button,
689
+ color = if (dark) GptColors.DarkTextPrimary else GptColors.TextPrimary)
690
+ }
691
+ }
692
+ ```
693
+
694
+ Bottom sheets (model picker, attachment sheet) use Material 3 `ModalBottomSheet`. Android has no live blur; ChatGPT's sheets use a plain dim scrim anyway, so use `scrimColor` directly — nothing to approximate.
695
+
696
+ ## 6. Motion
697
+
698
+ ChatGPT is restrained and editorial — streaming text has **no animation** (append tokens character-by-character).
699
+
700
+ | Moment | Compose recipe |
701
+ |--------|----------------|
702
+ | Message send | user bubble `slideInVertically` + `fadeIn` 250ms ease-out; send button scale 0.9 → 1.0 `spring` |
703
+ | Typing indicator | `rememberInfiniteTransition` 3 dots, alpha 0.3 ↔ 1.0 over 600ms, staggered 200ms |
704
+ | Assistant streaming | none — append to the string as tokens arrive |
705
+ | Regenerate | response `fadeOut` 200ms, new response streams in |
706
+ | Voice mode entry | `scaleIn(0.9f) + fadeIn(tween(400))` spring; `HapticFeedbackType.LongPress` (.impact solid) |
707
+ | Voice sphere | `Animatable` scale 0.95 ↔ 1.05 over 2s + sine deformation on the `Canvas` radius |
708
+ | Drawer slide | `ModalNavigationDrawer` edge-swipe; `spring(dampingRatio = 0.8f)` on release |
709
+ | Copy button | swap icon + label to "Copied!" for ~1s then revert |
710
+ | Thumbs feedback | fill icon + `scale 1.0 → 1.15 → 1.0` `spring`; `HapticFeedbackType.TextHandleMove` (.soft) |
711
+
712
+ ```kotlin
713
+ // Message-send bubble entrance
714
+ AnimatedVisibility(
715
+ visible = true,
716
+ enter = slideInVertically(tween(250)) { it / 2 } + fadeIn(tween(250)),
717
+ ) { UserMessageBubble(text) }
718
+ ```
719
+
720
+ Honor **Reduce Motion**: when `Settings.Global.ANIMATOR_DURATION_SCALE == 0f`, render the voice sphere static (no pulse/morph), the typing indicator as 3 static dots with a "Generating…" label, and skip the send-button scale — keep haptics. For richer haptics use `LocalView.current.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)` (API 30+) or a `Vibrator` `VibrationEffect.createOneShot(8, ...)` to approximate iOS's `.soft` thumbs feedback.
721
+
722
+ ## 7. Icons
723
+
724
+ ChatGPT uses simple glyphs; the closest first-party set is `androidx.compose.material:material-icons-extended`. The **ChatGPT sparkle/blossom logomark** is proprietary — ship it as a vector drawable and load via `ImageVector.vectorResource(R.drawable.…)`; `Icons.Filled.AutoAwesome` is a reasonable stand-in for the assistant avatar in prototypes.
725
+
726
+ | Purpose | SF Symbol (iOS) | Material Icon (Compose) |
727
+ |---------|-----------------|--------------------------|
728
+ | Send | `arrow.up` | `Icons.Filled.ArrowUpward` |
729
+ | Stop generating | `stop.fill` | `Icons.Filled.Stop` |
730
+ | Voice (mic) | `mic.fill` / `waveform` | `Icons.Filled.Mic` |
731
+ | Attach (+) | `plus` | `Icons.Filled.Add` |
732
+ | Web search | `globe` | `Icons.Filled.Public` |
733
+ | New chat | `square.and.pencil` | `Icons.Filled.Edit` |
734
+ | Sidebar toggle | `sidebar.leading` | `Icons.Filled.Menu` |
735
+ | Regenerate | `arrow.triangle.2.circlepath` | `Icons.Filled.Refresh` |
736
+ | Copy | `doc.on.doc` | `Icons.Filled.ContentCopy` |
737
+ | Thumbs up | `hand.thumbsup` / `.fill` | `Icons.Filled.ThumbUp` |
738
+ | Thumbs down | `hand.thumbsdown` / `.fill` | `Icons.Filled.ThumbDown` |
739
+ | Sparkle (assistant avatar) | `sparkles` | `Icons.Filled.AutoAwesome` (or custom logomark) |
740
+ | Model chip chevron | `chevron.down` | `Icons.Filled.KeyboardArrowDown` |
741
+ | Close (X) | `xmark` | `Icons.Filled.Close` |
742
+ | Search | `magnifyingglass` | `Icons.Filled.Search` |
743
+ | Copied confirmation | `checkmark` | `Icons.Filled.Check` |
744
+
745
+ ## 8. Minimum SDK & Accessibility Notes
746
+
747
+ - **minSdk 24** (Compose floor is 21; modern motion + audio comfortable at 24). `compileSdk 34+`, Compose BOM `2024.09+`, Kotlin `2.0+`. Söhne requires a commercial license from Klim Type Foundry — Inter (OFL) or Roboto (system) are acceptable free fallbacks.
748
+ - **Edge-to-edge**: call `enableEdgeToEdge()` in the `Activity`; system-bar icon contrast must follow the theme (dark icons on the light canvas, light icons on `#212121`). The composer rises with the IME via `Modifier.imePadding()`; voice mode deliberately ignores insets (full-bleed sphere).
749
+ - **Font scaling**: `sp` honors the user's font scale automatically — keep it on chat body, markdown headings, user-bubble text, code blocks (code stays monospace at XL). Pin layout-sensitive text (model chip, sidebar section titles, timestamps, code-block language label) by deriving from `dp` or wrapping in `CompositionLocalProvider(LocalDensity provides Density(density, fontScale = 1f))`.
750
+ - **TalkBack**: label user turns "You: {content}" and assistant turns "ChatGPT: {content}"; announce voice mode as "Voice conversation active, ChatGPT is listening". Expose the feedback row via `Modifier.semantics` custom actions ("Regenerate", "Copy", "Good response", "Bad response"). Every icon-only control needs a `contentDescription`.
751
+ - **Touch targets**: Material guidance is 48.dp minimum. The send/voice 32dp circles sit in 40–48dp composer hit areas; the **feedback-row icons are a tight 32dp per the iOS spec** — for strict accessibility compliance bump their hit area to 48dp via padding while keeping the 16dp glyph.
752
+ - **Contrast**: `#0D0D0D` on `#FFFFFF` and `#ECECEC` on `#212121` exceed WCAG AA at all sizes. `#676767` secondary on white meets AA at 14sp+; in High Contrast mode boost it toward `#3E3E3E`. The link blue `#2A7FFF` on white passes AA for text.
753
+ - **Dynamic color**: do **not** enable Material You `dynamicColorScheme()` — ChatGPT's identity is the deliberate monochrome (white/charcoal canvas, ink text, blue only on links and the voice sphere) regardless of wallpaper. (Material You suits Google/Material-first apps; ChatGPT is a strong-brand exception.) Unlike most strong-brand apps, ChatGPT *does* support both light and dark equally — switch on `isSystemInDarkTheme()`, never tint from the wallpaper.