openuispec 0.2.9 → 0.2.11

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 (232) hide show
  1. package/README.md +3 -1
  2. package/cli/index.ts +80 -2
  3. package/cli/init.ts +23 -8
  4. package/docs/cli.md +42 -7
  5. package/drift/index.ts +41 -15
  6. package/mcp-server/index.ts +247 -117
  7. package/mcp-server/screenshot-android.ts +185 -44
  8. package/mcp-server/screenshot-ios.ts +242 -30
  9. package/mcp-server/screenshot.ts +96 -1
  10. package/package.json +5 -2
  11. package/prepare/index.ts +16 -0
  12. package/scripts/take-all-screenshots.ts +507 -0
  13. package/status/index.ts +2 -2
  14. package/examples/social-app/.mcp.json +0 -10
  15. package/examples/social-app/AGENTS.md +0 -124
  16. package/examples/social-app/CLAUDE.md +0 -124
  17. package/examples/social-app/backend/.gitkeep +0 -1
  18. package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
  19. package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
  20. package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
  21. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
  22. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
  23. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
  24. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
  25. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
  26. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
  27. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
  28. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
  29. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
  30. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
  31. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
  32. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
  33. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
  34. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
  35. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
  36. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
  37. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
  38. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
  39. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
  40. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
  41. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
  42. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
  43. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
  44. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
  45. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
  46. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
  47. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
  48. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
  49. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
  50. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
  51. package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
  52. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
  53. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
  54. package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
  55. package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
  56. package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
  57. package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
  58. package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
  59. package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
  60. package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
  61. package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
  62. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
  63. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
  64. package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
  65. package/examples/social-app/generated/android/social-app/gradlew +0 -248
  66. package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
  67. package/examples/social-app/generated/web/social-app/index.html +0 -12
  68. package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
  69. package/examples/social-app/generated/web/social-app/package.json +0 -27
  70. package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
  71. package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
  72. package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
  73. package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
  74. package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
  75. package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
  76. package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
  77. package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
  78. package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
  79. package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
  80. package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
  81. package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
  82. package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
  83. package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
  84. package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
  85. package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
  86. package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
  87. package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
  88. package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
  89. package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
  90. package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
  91. package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
  92. package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
  93. package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
  94. package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
  95. package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
  96. package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
  97. package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
  98. package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
  99. package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
  100. package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
  101. package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
  102. package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
  103. package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
  104. package/examples/social-app/package.json +0 -13
  105. package/examples/social-app/take-web-screenshots.ts +0 -97
  106. package/examples/taskflow/.codex/config.toml +0 -4
  107. package/examples/taskflow/.mcp.json +0 -10
  108. package/examples/taskflow/AGENTS.md +0 -124
  109. package/examples/taskflow/CLAUDE.md +0 -124
  110. package/examples/taskflow/backend/.gitkeep +0 -1
  111. package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
  112. package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
  113. package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
  114. package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
  115. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
  116. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
  117. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
  118. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
  119. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
  120. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
  121. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
  122. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
  123. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
  124. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
  125. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
  126. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
  127. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
  128. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
  129. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
  130. package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
  131. package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
  132. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
  133. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
  134. package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
  135. package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
  136. package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
  137. package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
  138. package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
  139. package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
  140. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
  141. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
  142. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
  143. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
  144. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
  145. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
  146. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
  147. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
  148. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
  149. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
  150. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
  151. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
  152. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
  153. package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -26
  154. package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
  155. package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
  156. package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
  157. package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
  158. package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
  159. package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
  160. package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
  161. package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
  162. package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
  163. package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
  164. package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
  165. package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
  166. package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
  167. package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
  168. package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
  169. package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
  170. package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
  171. package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
  172. package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
  173. package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
  174. package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
  175. package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
  176. package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
  177. package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
  178. package/examples/todo-orbit/.codex/config.toml +0 -4
  179. package/examples/todo-orbit/.mcp.json +0 -10
  180. package/examples/todo-orbit/AGENTS.md +0 -124
  181. package/examples/todo-orbit/CLAUDE.md +0 -124
  182. package/examples/todo-orbit/backend/.gitkeep +0 -1
  183. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
  184. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
  185. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
  186. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
  187. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
  188. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
  189. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
  190. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
  191. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
  192. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
  193. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
  194. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
  195. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
  196. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
  197. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
  198. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
  199. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
  200. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
  201. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  202. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
  203. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
  204. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
  205. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
  206. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
  207. package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
  208. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
  209. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
  210. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
  211. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
  212. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
  213. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
  214. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
  215. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
  216. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
  217. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
  218. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
  219. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
  220. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
  221. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
  222. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -400
  223. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  224. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -25
  225. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
  226. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
  227. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
  228. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
  229. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
  230. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
  231. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
  232. package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
@@ -1,169 +0,0 @@
1
- package uz.rsteam.todoorbit
2
-
3
- import androidx.compose.material3.MaterialTheme
4
- import androidx.compose.runtime.Composable
5
- import androidx.compose.runtime.saveable.Saver
6
- import androidx.compose.runtime.saveable.listSaver
7
- import androidx.compose.ui.res.stringResource
8
- import java.time.LocalDate
9
- import java.time.LocalDateTime
10
-
11
- enum class ScreenTab { Tasks, Analytics, Settings }
12
-
13
- enum class UiLocale { En, Ru }
14
-
15
- enum class ThemeMode { Light, Dark }
16
-
17
- enum class TaskStatus { Open, Done }
18
-
19
- enum class Priority { Low, Medium, High }
20
-
21
- enum class TaskFilter { All, Open, Done }
22
-
23
- enum class AnalyticsPeriod { Week, Month, Quarter }
24
-
25
- enum class Cadence { Daily, Weekly, Monthly }
26
-
27
- enum class Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
28
-
29
- enum class SummaryChannel { Push, Email }
30
-
31
- enum class PreviewMode { Invalid, Empty, Ready }
32
-
33
- sealed interface OverlayState {
34
- data object CreateTask : OverlayState
35
- data class EditTask(val taskId: String) : OverlayState
36
- data object RecurringRule : OverlayState
37
- data class TaskMeta(val taskId: String) : OverlayState
38
- }
39
-
40
- data class PreferencesState(
41
- val locale: UiLocale = UiLocale.En,
42
- val themeMode: ThemeMode = ThemeMode.Light,
43
- val remindersEnabled: Boolean = true,
44
- val dailySummaryEnabled: Boolean = false
45
- )
46
-
47
- val PreferencesStateSaver: Saver<PreferencesState, Any> = listSaver(
48
- save = {
49
- listOf(
50
- it.locale.name,
51
- it.themeMode.name,
52
- it.remindersEnabled,
53
- it.dailySummaryEnabled
54
- )
55
- },
56
- restore = {
57
- PreferencesState(
58
- locale = UiLocale.valueOf(it[0] as String),
59
- themeMode = ThemeMode.valueOf(it[1] as String),
60
- remindersEnabled = it[2] as Boolean,
61
- dailySummaryEnabled = it[3] as Boolean
62
- )
63
- }
64
- )
65
-
66
- data class TaskModel(
67
- val id: String,
68
- val title: String,
69
- val notes: String = "",
70
- val status: TaskStatus,
71
- val priority: Priority,
72
- val dueDate: LocalDate? = null,
73
- val createdAt: LocalDateTime,
74
- val updatedAt: LocalDateTime
75
- )
76
-
77
- data class RecurringRuleModel(
78
- val id: String,
79
- val name: String,
80
- val cadence: Cadence,
81
- val interval: Int,
82
- val weekday: Weekday? = null,
83
- val monthDay: Int? = null,
84
- val startDate: LocalDate,
85
- val endDate: LocalDate? = null,
86
- val remindAt: String? = null,
87
- val summaryChannel: SummaryChannel? = null
88
- )
89
-
90
- data class TrendPoint(
91
- val label: String,
92
- val completed: Int,
93
- val created: Int
94
- )
95
-
96
- data class TaskDraft(
97
- val title: String = "",
98
- val notes: String = "",
99
- val priority: Priority = Priority.Medium,
100
- val dueDate: LocalDate? = null
101
- )
102
-
103
- data class RuleDraft(
104
- val name: String = "",
105
- val confirmName: String = "",
106
- val cadence: Cadence? = null,
107
- val interval: String = "1",
108
- val weekday: Weekday? = null,
109
- val monthDay: String = "",
110
- val startDate: LocalDate = LocalDate.now(),
111
- val hasEndDate: Boolean = false,
112
- val endDate: LocalDate? = null,
113
- val remindAt: String = "",
114
- val enableSummary: Boolean = false,
115
- val summaryChannel: SummaryChannel? = null
116
- )
117
-
118
- data class SchedulePreviewState(
119
- val mode: PreviewMode,
120
- val occurrences: List<LocalDate> = emptyList()
121
- )
122
-
123
- sealed interface UiText {
124
- data class Resource(
125
- val id: Int,
126
- val args: List<Any> = emptyList()
127
- ) : UiText
128
-
129
- data class Literal(val value: String) : UiText
130
- }
131
-
132
- val TaskStatus.labelRes: Int
133
- get() = when (this) {
134
- TaskStatus.Open -> R.string.status_open
135
- TaskStatus.Done -> R.string.status_done
136
- }
137
-
138
- val Priority.labelRes: Int
139
- get() = when (this) {
140
- Priority.Low -> R.string.priority_low
141
- Priority.Medium -> R.string.priority_medium
142
- Priority.High -> R.string.priority_high
143
- }
144
-
145
- val Weekday.labelRes: Int
146
- get() = when (this) {
147
- Weekday.Mon -> R.string.weekday_mon
148
- Weekday.Tue -> R.string.weekday_tue
149
- Weekday.Wed -> R.string.weekday_wed
150
- Weekday.Thu -> R.string.weekday_thu
151
- Weekday.Fri -> R.string.weekday_fri
152
- Weekday.Sat -> R.string.weekday_sat
153
- Weekday.Sun -> R.string.weekday_sun
154
- }
155
-
156
- val Priority.color
157
- @Composable get() = when (this) {
158
- Priority.Low -> MaterialTheme.colorScheme.outline
159
- Priority.Medium -> MaterialTheme.colorScheme.tertiary
160
- Priority.High -> MaterialTheme.colorScheme.secondary
161
- }
162
-
163
- @Composable
164
- fun UiText.resolve(): String {
165
- return when (this) {
166
- is UiText.Resource -> stringResource(id, *args.toTypedArray())
167
- is UiText.Literal -> value
168
- }
169
- }
@@ -1,8 +0,0 @@
1
- package uz.rsteam.todoorbit
2
-
3
- fun UiLocale.toLanguageTag(): String {
4
- return when (this) {
5
- UiLocale.En -> "en"
6
- UiLocale.Ru -> "ru"
7
- }
8
- }
@@ -1,236 +0,0 @@
1
- package uz.rsteam.todoorbit
2
-
3
- import androidx.compose.foundation.background
4
- import androidx.compose.foundation.layout.Arrangement
5
- import androidx.compose.foundation.layout.Box
6
- import androidx.compose.foundation.layout.Column
7
- import androidx.compose.foundation.layout.Row
8
- import androidx.compose.foundation.layout.Spacer
9
- import androidx.compose.foundation.layout.fillMaxWidth
10
- import androidx.compose.foundation.layout.padding
11
- import androidx.compose.foundation.layout.size
12
- import androidx.compose.foundation.layout.width
13
- import androidx.compose.foundation.shape.CircleShape
14
- import androidx.compose.material3.Card
15
- import androidx.compose.material3.CardDefaults
16
- import androidx.compose.material3.DropdownMenu
17
- import androidx.compose.material3.DropdownMenuItem
18
- import androidx.compose.material3.ElevatedCard
19
- import androidx.compose.material3.ExperimentalMaterial3Api
20
- import androidx.compose.material3.ExposedDropdownMenuBox
21
- import androidx.compose.material3.ExposedDropdownMenuDefaults
22
- import androidx.compose.material3.MaterialTheme
23
- import androidx.compose.material3.OutlinedCard
24
- import androidx.compose.material3.OutlinedTextField
25
- import androidx.compose.material3.SegmentedButton
26
- import androidx.compose.material3.SegmentedButtonDefaults
27
- import androidx.compose.material3.SingleChoiceSegmentedButtonRow
28
- import androidx.compose.material3.Switch
29
- import androidx.compose.material3.Text
30
- import androidx.compose.runtime.Composable
31
- import androidx.compose.runtime.getValue
32
- import androidx.compose.runtime.mutableStateOf
33
- import androidx.compose.runtime.remember
34
- import androidx.compose.runtime.setValue
35
- import androidx.compose.ui.Alignment
36
- import androidx.compose.ui.Modifier
37
- import androidx.compose.ui.graphics.Color
38
- import androidx.compose.ui.res.stringResource
39
- import androidx.compose.ui.text.font.FontWeight
40
- import androidx.compose.ui.unit.dp
41
-
42
- @Composable
43
- fun HeroCard(title: String, subtitle: String) {
44
- ElevatedCard(
45
- shape = MaterialTheme.shapes.large,
46
- colors = CardDefaults.elevatedCardColors(
47
- containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.65f)
48
- )
49
- ) {
50
- Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
51
- Text(title, style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold)
52
- Text(subtitle, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)
53
- }
54
- }
55
- }
56
-
57
- @Composable
58
- fun StatCard(title: String, value: String) {
59
- Card(shape = MaterialTheme.shapes.medium) {
60
- Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
61
- Text(title, style = MaterialTheme.typography.labelLarge, color = MaterialTheme.colorScheme.onSurfaceVariant)
62
- Text(value, style = MaterialTheme.typography.headlineSmall, fontWeight = FontWeight.Bold)
63
- }
64
- }
65
- }
66
-
67
- @Composable
68
- fun LegendDot(color: Color, label: String) {
69
- Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
70
- Box(modifier = Modifier.size(10.dp).background(color, CircleShape))
71
- Text(label, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
72
- }
73
- }
74
-
75
- @Composable
76
- fun LanguageSelector(current: UiLocale, onSelected: (UiLocale) -> Unit) {
77
- EnumSelector(
78
- title = stringResource(R.string.settings_language),
79
- current = current.name,
80
- options = listOf(
81
- UiLocale.En.name to stringResource(R.string.settings_language_en),
82
- UiLocale.Ru.name to stringResource(R.string.settings_language_ru)
83
- ),
84
- style = EnumSelectorStyle.Segmented,
85
- onSelected = { selected -> onSelected(UiLocale.entries.first { it.name == selected }) }
86
- )
87
- }
88
-
89
- @Composable
90
- fun ThemeSelector(current: ThemeMode, onSelected: (ThemeMode) -> Unit) {
91
- EnumSelector(
92
- title = stringResource(R.string.settings_theme),
93
- current = current.name,
94
- options = listOf(
95
- ThemeMode.Light.name to stringResource(R.string.settings_theme_light),
96
- ThemeMode.Dark.name to stringResource(R.string.settings_theme_dark)
97
- ),
98
- style = EnumSelectorStyle.Segmented,
99
- onSelected = { selected -> onSelected(ThemeMode.entries.first { it.name == selected }) }
100
- )
101
- }
102
-
103
- enum class EnumSelectorStyle {
104
- Segmented,
105
- Dropdown
106
- }
107
-
108
- @OptIn(ExperimentalMaterial3Api::class)
109
- @Composable
110
- fun EnumSelector(
111
- title: String,
112
- current: String,
113
- options: List<Pair<String, String>>,
114
- style: EnumSelectorStyle = EnumSelectorStyle.Segmented,
115
- leadingIcon: @Composable (() -> Unit)? = null,
116
- onSelected: (String) -> Unit
117
- ) {
118
- Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
119
- Text(title, style = MaterialTheme.typography.titleSmall)
120
- when (style) {
121
- EnumSelectorStyle.Segmented -> {
122
- SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
123
- options.forEachIndexed { index, (value, label) ->
124
- SegmentedButton(
125
- selected = value == current,
126
- onClick = { onSelected(value) },
127
- shape = SegmentedButtonDefaults.itemShape(index = index, count = options.size),
128
- label = { Text(label) }
129
- )
130
- }
131
- }
132
- }
133
-
134
- EnumSelectorStyle.Dropdown -> {
135
- var expanded by remember { mutableStateOf(false) }
136
- val selectedLabel = options.firstOrNull { it.first == current }?.second.orEmpty()
137
- ExposedDropdownMenuBox(
138
- expanded = expanded,
139
- onExpandedChange = { expanded = !expanded }
140
- ) {
141
- OutlinedTextField(
142
- value = selectedLabel,
143
- onValueChange = {},
144
- modifier = Modifier
145
- .menuAnchor()
146
- .fillMaxWidth(),
147
- readOnly = true,
148
- label = { Text(title) },
149
- leadingIcon = leadingIcon,
150
- trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
151
- shape = MaterialTheme.shapes.medium
152
- )
153
- DropdownMenu(
154
- expanded = expanded,
155
- onDismissRequest = { expanded = false }
156
- ) {
157
- options.forEach { (value, label) ->
158
- DropdownMenuItem(
159
- text = { Text(label) },
160
- onClick = {
161
- expanded = false
162
- onSelected(value)
163
- }
164
- )
165
- }
166
- }
167
- }
168
- }
169
- }
170
- }
171
- }
172
-
173
- @Composable
174
- fun SettingsToggle(
175
- title: String,
176
- subtitle: String,
177
- checked: Boolean,
178
- onCheckedChange: (Boolean) -> Unit
179
- ) {
180
- Row(
181
- modifier = Modifier.fillMaxWidth(),
182
- horizontalArrangement = Arrangement.SpaceBetween,
183
- verticalAlignment = Alignment.CenterVertically
184
- ) {
185
- Column(modifier = Modifier.fillMaxWidth(0.8f), verticalArrangement = Arrangement.spacedBy(4.dp)) {
186
- Text(title, fontWeight = FontWeight.SemiBold)
187
- Text(subtitle, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
188
- }
189
- Spacer(Modifier.width(12.dp))
190
- Switch(checked = checked, onCheckedChange = onCheckedChange)
191
- }
192
- }
193
-
194
- @Composable
195
- fun RuleTextField(
196
- label: String,
197
- value: String,
198
- placeholder: String = "",
199
- helper: String? = null,
200
- error: String? = null,
201
- onValueChange: (String) -> Unit
202
- ) {
203
- Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
204
- OutlinedTextField(
205
- value = value,
206
- onValueChange = onValueChange,
207
- modifier = Modifier.fillMaxWidth(),
208
- label = { Text(label) },
209
- placeholder = if (placeholder.isNotBlank()) ({ Text(placeholder) }) else null,
210
- shape = MaterialTheme.shapes.medium,
211
- isError = error != null
212
- )
213
- when {
214
- error != null -> Text(error, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodySmall)
215
- helper != null -> Text(helper, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodySmall)
216
- }
217
- }
218
- }
219
-
220
- @Composable
221
- fun EmptyCard(title: String, body: String) {
222
- OutlinedCard(shape = MaterialTheme.shapes.large) {
223
- Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
224
- Text(title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold)
225
- Text(body, color = MaterialTheme.colorScheme.onSurfaceVariant)
226
- }
227
- }
228
- }
229
-
230
- @Composable
231
- fun LabelValue(label: String, value: String) {
232
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
233
- Text(label, color = MaterialTheme.colorScheme.onSurfaceVariant)
234
- Text(value, fontWeight = FontWeight.SemiBold)
235
- }
236
- }
@@ -1,193 +0,0 @@
1
- package uz.rsteam.todoorbit
2
-
3
- import androidx.compose.foundation.Canvas
4
- import androidx.compose.foundation.layout.Arrangement
5
- import androidx.compose.foundation.layout.Column
6
- import androidx.compose.foundation.layout.ExperimentalLayoutApi
7
- import androidx.compose.foundation.layout.FlowRow
8
- import androidx.compose.foundation.layout.PaddingValues
9
- import androidx.compose.foundation.layout.Row
10
- import androidx.compose.foundation.layout.fillMaxSize
11
- import androidx.compose.foundation.layout.fillMaxWidth
12
- import androidx.compose.foundation.layout.height
13
- import androidx.compose.foundation.layout.padding
14
- import androidx.compose.foundation.lazy.LazyColumn
15
- import androidx.compose.foundation.lazy.grid.GridCells
16
- import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
17
- import androidx.compose.material3.ElevatedCard
18
- import androidx.compose.material3.FilterChip
19
- import androidx.compose.material3.MaterialTheme
20
- import androidx.compose.material3.Text
21
- import androidx.compose.runtime.Composable
22
- import androidx.compose.ui.Modifier
23
- import androidx.compose.ui.geometry.Offset
24
- import androidx.compose.ui.graphics.Path
25
- import androidx.compose.ui.graphics.StrokeCap
26
- import androidx.compose.ui.graphics.drawscope.Stroke
27
- import androidx.compose.ui.res.stringResource
28
- import androidx.compose.ui.text.font.FontWeight
29
- import androidx.compose.ui.unit.dp
30
-
31
- @OptIn(ExperimentalLayoutApi::class)
32
- @Composable
33
- fun AnalyticsScreen(
34
- period: AnalyticsPeriod,
35
- overview: AnalyticsOverview,
36
- trendSeries: List<TrendPoint>,
37
- overdueTasks: List<TaskModel>,
38
- onPeriodChange: (AnalyticsPeriod) -> Unit,
39
- locale: UiLocale
40
- ) {
41
- LazyColumn(
42
- modifier = Modifier.fillMaxSize(),
43
- verticalArrangement = Arrangement.spacedBy(16.dp),
44
- contentPadding = PaddingValues(20.dp)
45
- ) {
46
- item {
47
- HeroCard(
48
- stringResource(R.string.analytics_title),
49
- stringResource(R.string.analytics_subtitle)
50
- )
51
- }
52
- item {
53
- FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) {
54
- AnalyticsPeriod.entries.forEach { entry ->
55
- FilterChip(
56
- selected = period == entry,
57
- onClick = { onPeriodChange(entry) },
58
- label = {
59
- Text(
60
- when (entry) {
61
- AnalyticsPeriod.Week -> stringResource(R.string.analytics_period_week)
62
- AnalyticsPeriod.Month -> stringResource(R.string.analytics_period_month)
63
- AnalyticsPeriod.Quarter -> stringResource(R.string.analytics_period_quarter)
64
- }
65
- )
66
- }
67
- )
68
- }
69
- }
70
- }
71
- item {
72
- LazyVerticalGrid(
73
- columns = GridCells.Fixed(2),
74
- modifier = Modifier.height(220.dp),
75
- verticalArrangement = Arrangement.spacedBy(12.dp),
76
- horizontalArrangement = Arrangement.spacedBy(12.dp),
77
- userScrollEnabled = false
78
- ) {
79
- item { StatCard(stringResource(R.string.analytics_completed_today), overview.completedToday.toString()) }
80
- item { StatCard(stringResource(R.string.analytics_open_tasks), overview.openTasks.toString()) }
81
- item { StatCard(stringResource(R.string.analytics_overdue_tasks), overview.overdueTasks.toString()) }
82
- item { StatCard(stringResource(R.string.analytics_completion_rate), "${overview.completionRate}%") }
83
- }
84
- }
85
- item {
86
- TrendChartCard(series = trendSeries, period = period)
87
- }
88
- item {
89
- ElevatedCard(shape = MaterialTheme.shapes.large) {
90
- Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) {
91
- Text(stringResource(R.string.analytics_overdue_section), style = MaterialTheme.typography.headlineSmall)
92
- Text(stringResource(R.string.analytics_overdue_subtitle), color = MaterialTheme.colorScheme.onSurfaceVariant)
93
- if (overdueTasks.isEmpty()) {
94
- Text(stringResource(R.string.analytics_empty_overdue_body), color = MaterialTheme.colorScheme.onSurfaceVariant)
95
- } else {
96
- overdueTasks.forEach { task ->
97
- Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
98
- Column {
99
- Text(task.title, fontWeight = FontWeight.SemiBold)
100
- Text(stringResource(task.priority.labelRes), color = MaterialTheme.colorScheme.onSurfaceVariant)
101
- }
102
- Text(task.dueDate?.let { formatAbsolute(it, locale) }.orEmpty())
103
- }
104
- }
105
- }
106
- }
107
- }
108
- }
109
- }
110
- }
111
-
112
- @Composable
113
- fun TrendChartCard(
114
- series: List<TrendPoint>,
115
- period: AnalyticsPeriod
116
- ) {
117
- val outlineVariant = MaterialTheme.colorScheme.outlineVariant
118
- val createdColor = MaterialTheme.colorScheme.secondary
119
- val completedColor = MaterialTheme.colorScheme.primary
120
-
121
- ElevatedCard(shape = MaterialTheme.shapes.large) {
122
- Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
123
- Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
124
- Column {
125
- Text(
126
- stringResource(R.string.analytics_contract_label),
127
- style = MaterialTheme.typography.labelSmall,
128
- color = MaterialTheme.colorScheme.onSurfaceVariant
129
- )
130
- Text(
131
- when (period) {
132
- AnalyticsPeriod.Week -> stringResource(R.string.analytics_period_week)
133
- AnalyticsPeriod.Month -> stringResource(R.string.analytics_period_month)
134
- AnalyticsPeriod.Quarter -> stringResource(R.string.analytics_period_quarter)
135
- },
136
- style = MaterialTheme.typography.titleLarge
137
- )
138
- }
139
- Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
140
- LegendDot(color = createdColor, label = stringResource(R.string.analytics_legend_created))
141
- LegendDot(color = completedColor, label = stringResource(R.string.analytics_legend_completed))
142
- }
143
- }
144
- if (series.isEmpty()) {
145
- Text(stringResource(R.string.analytics_empty_trend), color = MaterialTheme.colorScheme.onSurfaceVariant)
146
- } else {
147
- Canvas(modifier = Modifier.fillMaxWidth().height(220.dp)) {
148
- val maxValue = series.maxOf { maxOf(it.completed, it.created) }.coerceAtLeast(1)
149
- val leftPad = 32.dp.toPx()
150
- val bottomPad = 28.dp.toPx()
151
- val usableWidth = size.width - leftPad * 2
152
- val usableHeight = size.height - bottomPad - 20.dp.toPx()
153
- repeat(4) { index ->
154
- val y = 20.dp.toPx() + (usableHeight / 3f) * index
155
- drawLine(
156
- color = outlineVariant,
157
- start = Offset(leftPad, y),
158
- end = Offset(size.width - leftPad, y)
159
- )
160
- }
161
- fun buildPath(selector: (TrendPoint) -> Int): Path {
162
- return Path().apply {
163
- series.forEachIndexed { index, point ->
164
- val x = leftPad + (usableWidth / series.lastIndex.coerceAtLeast(1)) * index
165
- val y = 20.dp.toPx() + usableHeight - (usableHeight * selector(point) / maxValue.toFloat())
166
- if (index == 0) moveTo(x, y) else lineTo(x, y)
167
- }
168
- }
169
- }
170
- drawPath(
171
- path = buildPath { it.created },
172
- color = createdColor,
173
- style = Stroke(width = 6f, cap = StrokeCap.Round)
174
- )
175
- drawPath(
176
- path = buildPath { it.completed },
177
- color = completedColor,
178
- style = Stroke(width = 6f, cap = StrokeCap.Round)
179
- )
180
- }
181
- Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
182
- series.forEach { point ->
183
- Text(
184
- text = point.label,
185
- style = MaterialTheme.typography.labelSmall,
186
- color = MaterialTheme.colorScheme.onSurfaceVariant
187
- )
188
- }
189
- }
190
- }
191
- }
192
- }
193
- }