openuispec 0.2.10 → 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 (231) hide show
  1. package/README.md +3 -1
  2. package/cli/index.ts +4 -2
  3. package/cli/init.ts +23 -8
  4. package/docs/cli.md +13 -8
  5. package/drift/index.ts +41 -15
  6. package/mcp-server/index.ts +155 -116
  7. package/mcp-server/screenshot.ts +19 -4
  8. package/package.json +5 -2
  9. package/prepare/index.ts +16 -0
  10. package/scripts/take-all-screenshots.ts +507 -0
  11. package/status/index.ts +2 -2
  12. package/examples/social-app/.mcp.json +0 -10
  13. package/examples/social-app/AGENTS.md +0 -124
  14. package/examples/social-app/CLAUDE.md +0 -124
  15. package/examples/social-app/backend/.gitkeep +0 -1
  16. package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
  17. package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
  18. package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
  19. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
  20. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
  21. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
  22. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
  23. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
  24. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
  25. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
  26. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
  27. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
  28. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
  29. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
  30. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
  31. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
  32. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
  33. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
  34. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
  35. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
  36. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
  37. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
  38. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
  39. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
  40. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
  41. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
  42. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
  43. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
  44. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
  45. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
  46. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
  47. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
  48. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
  49. package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
  50. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
  51. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
  52. package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
  53. package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
  54. package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
  55. package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
  56. package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
  57. package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
  58. package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
  59. package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
  60. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
  61. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
  62. package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
  63. package/examples/social-app/generated/android/social-app/gradlew +0 -248
  64. package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
  65. package/examples/social-app/generated/web/social-app/index.html +0 -12
  66. package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
  67. package/examples/social-app/generated/web/social-app/package.json +0 -27
  68. package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
  69. package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
  70. package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
  71. package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
  72. package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
  73. package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
  74. package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
  75. package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
  76. package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
  77. package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
  78. package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
  79. package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
  80. package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
  81. package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
  82. package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
  83. package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
  84. package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
  85. package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
  86. package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
  87. package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
  88. package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
  89. package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
  90. package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
  91. package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
  92. package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
  93. package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
  94. package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
  95. package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
  96. package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
  97. package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
  98. package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
  99. package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
  100. package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
  101. package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
  102. package/examples/social-app/package.json +0 -13
  103. package/examples/social-app/take-web-screenshots.ts +0 -97
  104. package/examples/taskflow/.codex/config.toml +0 -4
  105. package/examples/taskflow/.mcp.json +0 -10
  106. package/examples/taskflow/AGENTS.md +0 -124
  107. package/examples/taskflow/CLAUDE.md +0 -124
  108. package/examples/taskflow/backend/.gitkeep +0 -1
  109. package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
  110. package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
  111. package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
  112. package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
  113. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
  114. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
  115. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
  116. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
  117. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
  118. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
  119. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
  120. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
  121. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
  122. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
  123. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
  124. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
  125. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
  126. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
  127. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
  128. package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
  129. package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
  130. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
  131. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
  132. package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
  133. package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
  134. package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
  135. package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
  136. package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
  137. package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
  138. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
  139. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
  140. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
  141. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
  142. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
  143. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
  144. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
  145. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
  146. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
  147. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
  148. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
  149. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
  150. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
  151. package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
  152. package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
  153. package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
  154. package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
  155. package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
  156. package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
  157. package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
  158. package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
  159. package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
  160. package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
  161. package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
  162. package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
  163. package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
  164. package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
  165. package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
  166. package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
  167. package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
  168. package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
  169. package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
  170. package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
  171. package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
  172. package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
  173. package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
  174. package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
  175. package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
  176. package/examples/todo-orbit/.codex/config.toml +0 -4
  177. package/examples/todo-orbit/.mcp.json +0 -10
  178. package/examples/todo-orbit/AGENTS.md +0 -124
  179. package/examples/todo-orbit/CLAUDE.md +0 -124
  180. package/examples/todo-orbit/backend/.gitkeep +0 -1
  181. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
  182. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
  183. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
  184. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
  185. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
  186. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
  187. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
  188. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
  189. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
  190. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
  191. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
  192. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
  193. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
  194. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
  195. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
  196. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
  197. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
  198. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
  199. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  200. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
  201. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
  202. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
  203. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
  204. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
  205. package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
  206. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
  207. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
  208. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
  209. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
  210. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
  211. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
  212. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
  213. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
  214. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
  215. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
  216. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
  217. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
  218. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
  219. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
  220. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
  221. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  222. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
  223. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
  224. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
  225. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
  226. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
  227. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
  228. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
  229. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
  230. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
  231. 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
- }