openuispec 0.2.10 → 0.2.12

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 (237) hide show
  1. package/README.md +3 -1
  2. package/check/index.ts +17 -0
  3. package/cli/index.ts +21 -3
  4. package/cli/init.ts +224 -10
  5. package/docs/cli.md +13 -8
  6. package/docs/file-formats.md +36 -0
  7. package/docs/implementation-notes.md +7 -0
  8. package/drift/index.ts +281 -40
  9. package/mcp-server/index.ts +179 -119
  10. package/mcp-server/screenshot.ts +19 -4
  11. package/package.json +5 -2
  12. package/prepare/index.ts +155 -18
  13. package/schema/openuispec.schema.json +59 -0
  14. package/schema/semantic-lint.ts +25 -1
  15. package/scripts/take-all-screenshots.ts +507 -0
  16. package/spec/openuispec-v0.1.md +13 -0
  17. package/status/index.ts +72 -2
  18. package/examples/social-app/.mcp.json +0 -10
  19. package/examples/social-app/AGENTS.md +0 -124
  20. package/examples/social-app/CLAUDE.md +0 -124
  21. package/examples/social-app/backend/.gitkeep +0 -1
  22. package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
  23. package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
  24. package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
  25. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
  26. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
  27. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
  28. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
  29. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
  30. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
  31. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
  32. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
  33. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
  34. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
  35. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
  36. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
  37. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
  38. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
  39. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
  40. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
  41. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
  42. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
  43. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
  44. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
  45. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
  46. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
  47. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
  48. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
  49. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
  50. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
  51. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
  52. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
  53. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
  54. package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
  55. package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
  56. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
  57. package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
  58. package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
  59. package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
  60. package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
  61. package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
  62. package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
  63. package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
  64. package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
  65. package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
  66. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
  67. package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
  68. package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
  69. package/examples/social-app/generated/android/social-app/gradlew +0 -248
  70. package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
  71. package/examples/social-app/generated/web/social-app/index.html +0 -12
  72. package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
  73. package/examples/social-app/generated/web/social-app/package.json +0 -27
  74. package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
  75. package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
  76. package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
  77. package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
  78. package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
  79. package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
  80. package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
  81. package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
  82. package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
  83. package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
  84. package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
  85. package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
  86. package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
  87. package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
  88. package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
  89. package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
  90. package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
  91. package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
  92. package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
  93. package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
  94. package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
  95. package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
  96. package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
  97. package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
  98. package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
  99. package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
  100. package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
  101. package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
  102. package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
  103. package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
  104. package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
  105. package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
  106. package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
  107. package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
  108. package/examples/social-app/package.json +0 -13
  109. package/examples/social-app/take-web-screenshots.ts +0 -97
  110. package/examples/taskflow/.codex/config.toml +0 -4
  111. package/examples/taskflow/.mcp.json +0 -10
  112. package/examples/taskflow/AGENTS.md +0 -124
  113. package/examples/taskflow/CLAUDE.md +0 -124
  114. package/examples/taskflow/backend/.gitkeep +0 -1
  115. package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
  116. package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
  117. package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
  118. package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
  119. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
  120. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
  121. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
  122. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
  123. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
  124. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
  125. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
  126. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
  127. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
  128. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
  129. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
  130. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
  131. package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
  132. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
  133. package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
  134. package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
  135. package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
  136. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
  137. package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
  138. package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
  139. package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
  140. package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
  141. package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
  142. package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
  143. package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
  144. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
  145. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
  146. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
  147. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
  148. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
  149. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
  150. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
  151. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
  152. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
  153. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
  154. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
  155. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
  156. package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
  157. package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
  158. package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
  159. package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
  160. package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
  161. package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
  162. package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
  163. package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
  164. package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
  165. package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
  166. package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
  167. package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
  168. package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
  169. package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
  170. package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
  171. package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
  172. package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
  173. package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
  174. package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
  175. package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
  176. package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
  177. package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
  178. package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
  179. package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
  180. package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
  181. package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
  182. package/examples/todo-orbit/.codex/config.toml +0 -4
  183. package/examples/todo-orbit/.mcp.json +0 -10
  184. package/examples/todo-orbit/AGENTS.md +0 -124
  185. package/examples/todo-orbit/CLAUDE.md +0 -124
  186. package/examples/todo-orbit/backend/.gitkeep +0 -1
  187. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
  188. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
  189. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
  190. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
  191. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
  192. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
  193. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
  194. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
  195. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
  196. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
  197. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
  198. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
  199. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
  200. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
  201. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
  202. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
  203. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
  204. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
  205. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  206. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
  207. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
  208. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
  209. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
  210. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
  211. package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
  212. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
  213. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
  214. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
  215. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
  216. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
  217. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
  218. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
  219. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
  220. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
  221. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
  222. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
  223. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
  224. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
  225. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
  226. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
  227. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  228. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
  229. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
  230. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
  231. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
  232. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
  233. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
  234. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
  235. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
  236. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
  237. package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
package/drift/index.ts CHANGED
@@ -73,6 +73,33 @@ export interface ExplainResult {
73
73
  files: FileExplanation[];
74
74
  }
75
75
 
76
+ // ── shared layer types ───────────────────────────────────────────────
77
+
78
+ export interface SharedLayerConfig {
79
+ name: string;
80
+ platforms: string[];
81
+ language: string;
82
+ root: string; // resolved absolute path
83
+ paths: Record<string, string>;
84
+ tracks: string[]; // spec categories to track for drift
85
+ scope: string; // what code belongs in this layer
86
+ }
87
+
88
+ export interface SharedLayerState {
89
+ spec_version: string;
90
+ snapshot_at: string;
91
+ layer_name: string;
92
+ generated_by_target: string;
93
+ baseline?: BaselineRef;
94
+ files: Record<string, FileEntry>;
95
+ }
96
+
97
+ export interface SharedLayerDriftResult {
98
+ layer: SharedLayerConfig;
99
+ drift: DriftResult;
100
+ state: SharedLayerState | null;
101
+ }
102
+
76
103
  // ── helpers ───────────────────────────────────────────────────────────
77
104
 
78
105
  export function listFiles(dir: string, ext: string): string[] {
@@ -166,16 +193,43 @@ export function discoverSpecFiles(projectDir: string): string[] {
166
193
  return files;
167
194
  }
168
195
 
169
- function categorize(relPath: string): string {
170
- if (relPath === "openuispec.yaml") return "Manifest";
196
+ /** Classify a relative spec path into a lowercase category. */
197
+ export function specCategory(relPath: string): string {
198
+ if (relPath === "openuispec.yaml") return "manifest";
171
199
  const dir = dirname(relPath);
172
- if (dir === "tokens") return "Tokens";
173
- if (dir === "screens") return "Screens";
174
- if (dir === "flows") return "Flows";
175
- if (dir === "platform") return "Platform";
176
- if (dir === "locales") return "Locales";
177
- if (dir === "contracts") return "Contracts";
178
- return "Other";
200
+ if (dir === "tokens") return "tokens";
201
+ if (dir === "screens") return "screens";
202
+ if (dir === "flows") return "flows";
203
+ if (dir === "platform") return "platform";
204
+ if (dir === "locales") return "locales";
205
+ if (dir === "contracts") return "contracts";
206
+ return "other";
207
+ }
208
+
209
+ function categorize(relPath: string): string {
210
+ const cat = specCategory(relPath);
211
+ return cat.charAt(0).toUpperCase() + cat.slice(1);
212
+ }
213
+
214
+ /** Returns true when a DriftResult contains any changes. */
215
+ export function hasDriftChanges(d: DriftResult): boolean {
216
+ return d.changed.length > 0 || d.added.length > 0 || d.removed.length > 0;
217
+ }
218
+
219
+ /** Hash spec files into FileEntry records keyed by relative path. */
220
+ export function buildFileEntries(
221
+ projectDir: string,
222
+ files: string[]
223
+ ): { entries: Record<string, FileEntry>; stubs: number } {
224
+ const entries: Record<string, FileEntry> = {};
225
+ let stubs = 0;
226
+ for (const f of files) {
227
+ const rel = relative(projectDir, f);
228
+ const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
229
+ entries[rel] = { hash: hashFile(f), status };
230
+ if (status === "stub") stubs++;
231
+ }
232
+ return { entries, stubs };
179
233
  }
180
234
 
181
235
  function runGit(args: string[], cwd: string): string | null {
@@ -536,35 +590,187 @@ function normalizeEntry(value: string | FileEntry): FileEntry {
536
590
  return value;
537
591
  }
538
592
 
593
+ // ── shared layers ────────────────────────────────────────────────────
594
+
595
+ /** Parse `generation.shared` from the manifest and resolve roots to absolute paths. */
596
+ export function readSharedLayers(projectDir: string): SharedLayerConfig[] {
597
+ const manifest = readManifest(projectDir);
598
+ const shared = manifest.generation?.shared;
599
+ if (!shared || typeof shared !== "object") return [];
600
+
601
+ return Object.entries(shared).map(([name, config]) => {
602
+ const cfg = config as Record<string, any>;
603
+ return {
604
+ name,
605
+ platforms: Array.isArray(cfg.platforms) ? cfg.platforms : [],
606
+ language: typeof cfg.language === "string" ? cfg.language : "",
607
+ root: resolve(projectDir, cfg.root ?? "."),
608
+ paths: typeof cfg.paths === "object" && cfg.paths !== null ? cfg.paths : {},
609
+ tracks: Array.isArray(cfg.tracks) ? cfg.tracks : [],
610
+ scope: typeof cfg.scope === "string" ? cfg.scope : "",
611
+ };
612
+ });
613
+ }
614
+
615
+ /** Return shared layers whose `platforms` array includes the given target. */
616
+ export function sharedLayersForTarget(projectDir: string, target: string): SharedLayerConfig[] {
617
+ return readSharedLayers(projectDir).filter((layer) => layer.platforms.includes(target));
618
+ }
619
+
620
+ /** Returns the state file path for a shared layer. */
621
+ export function sharedLayerStatePath(layer: SharedLayerConfig): string {
622
+ return join(layer.root, `.openuispec-shared-${layer.name}.json`);
623
+ }
624
+
625
+ /** Read an existing shared layer state file, or null if it doesn't exist. */
626
+ export function readSharedLayerState(layer: SharedLayerConfig): SharedLayerState | null {
627
+ try {
628
+ return JSON.parse(readFileSync(sharedLayerStatePath(layer), "utf-8"));
629
+ } catch {
630
+ return null;
631
+ }
632
+ }
633
+
634
+ /** Filter spec files to only those in the given categories. */
635
+ function filterSpecFilesByCategories(
636
+ projectDir: string,
637
+ files: string[],
638
+ categories: string[]
639
+ ): string[] {
640
+ const catSet = new Set(categories);
641
+ return files.filter((f) => catSet.has(specCategory(relative(projectDir, f))));
642
+ }
643
+
644
+ /** Create a snapshot of spec state for a shared layer. */
645
+ export function createSharedSnapshot(
646
+ cwd: string,
647
+ layerName: string,
648
+ generatedByTarget: string
649
+ ): SnapshotResult {
650
+ const projectDir = findProjectDir(cwd);
651
+ const layers = readSharedLayers(projectDir);
652
+ const layer = layers.find((l) => l.name === layerName);
653
+ if (!layer) {
654
+ throw new Error(`Shared layer "${layerName}" not found in generation.shared`);
655
+ }
656
+
657
+ const manifest = readManifest(projectDir);
658
+ const allFiles = discoverSpecFiles(projectDir);
659
+ const files = filterSpecFilesByCategories(projectDir, allFiles, layer.tracks);
660
+ const baseline = captureBaseline(projectDir, allFiles);
661
+
662
+ const { entries, stubs: stubCount } = buildFileEntries(projectDir, files);
663
+
664
+ const state: SharedLayerState = {
665
+ spec_version: manifest.spec_version ?? "0.1",
666
+ snapshot_at: new Date().toISOString(),
667
+ layer_name: layerName,
668
+ generated_by_target: generatedByTarget,
669
+ baseline,
670
+ files: entries,
671
+ };
672
+
673
+ const outPath = sharedLayerStatePath(layer);
674
+ writeFileSync(outPath, JSON.stringify(state, null, 2) + "\n");
675
+
676
+ return {
677
+ target: `shared:${layerName}`,
678
+ snapshot_at: state.snapshot_at,
679
+ files_hashed: Object.keys(entries).length,
680
+ stubs: stubCount,
681
+ state_path: relative(cwd, outPath),
682
+ baseline: formatBaseline(baseline),
683
+ };
684
+ }
685
+
686
+ /** Compute drift for a shared layer against its saved state. */
687
+ export function computeSharedDrift(
688
+ projectDir: string,
689
+ layer: SharedLayerConfig
690
+ ): SharedLayerDriftResult {
691
+ const state = readSharedLayerState(layer);
692
+ if (!state) {
693
+ return {
694
+ layer,
695
+ drift: { changed: [], added: [], removed: [], unchanged: [] },
696
+ state: null,
697
+ };
698
+ }
699
+
700
+ const allFiles = discoverSpecFiles(projectDir);
701
+ const files = filterSpecFilesByCategories(projectDir, allFiles, layer.tracks);
702
+ const { entries: current } = buildFileEntries(projectDir, files);
703
+
704
+ const drift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
705
+
706
+ for (const [rel, entry] of Object.entries(current)) {
707
+ const snapshotEntry = state.files[rel]
708
+ ? normalizeEntry(state.files[rel] as string | FileEntry)
709
+ : null;
710
+ if (!snapshotEntry) {
711
+ drift.added.push(rel);
712
+ } else if (snapshotEntry.hash !== entry.hash) {
713
+ drift.changed.push(rel);
714
+ } else {
715
+ drift.unchanged.push(rel);
716
+ }
717
+ }
718
+
719
+ for (const rel of Object.keys(state.files)) {
720
+ if (!(rel in current)) {
721
+ drift.removed.push(rel);
722
+ }
723
+ }
724
+
725
+ return { layer, drift, state };
726
+ }
727
+
728
+ /** Read `generation.structure[target]` from the manifest. */
729
+ export function readTargetStructure(
730
+ projectDir: string,
731
+ target: string
732
+ ): { root: string; paths: Record<string, string>; scope: string } | null {
733
+ const manifest = readManifest(projectDir);
734
+ const structure = manifest.generation?.structure?.[target];
735
+ if (!structure || typeof structure !== "object" || typeof structure.root !== "string") {
736
+ return null;
737
+ }
738
+ return {
739
+ root: resolve(projectDir, structure.root),
740
+ paths: typeof structure.paths === "object" && structure.paths !== null ? structure.paths : {},
741
+ scope: typeof structure.scope === "string" ? structure.scope : "",
742
+ };
743
+ }
744
+
539
745
  // ── snapshot ──────────────────────────────────────────────────────────
540
746
 
541
- function snapshot(cwd: string, projectDir: string, target: string): void {
747
+ export interface SnapshotResult {
748
+ target: string;
749
+ snapshot_at: string;
750
+ files_hashed: number;
751
+ stubs: number;
752
+ state_path: string;
753
+ baseline: string | null;
754
+ }
755
+
756
+ export function createSnapshot(cwd: string, target: string): SnapshotResult {
757
+ const projectDir = findProjectDir(cwd);
542
758
  const projectName = readProjectName(projectDir);
543
759
  const outDir = resolveOutputDir(projectDir, projectName, target);
544
760
  if (!existsSync(outDir)) {
545
- console.error(
546
- `Error: Output directory not found: ${relative(cwd, outDir)}\n` +
761
+ throw new Error(
762
+ `Output directory not found: ${relative(cwd, outDir)}\n` +
547
763
  `Run code generation for "${target}" first.`
548
764
  );
549
- process.exit(1);
550
765
  }
551
766
 
767
+ const manifest = readManifest(projectDir);
552
768
  const files = discoverSpecFiles(projectDir);
553
- const doc = YAML.parse(readFileSync(join(projectDir, "openuispec.yaml"), "utf-8"));
554
769
  const baseline = captureBaseline(projectDir, files);
555
-
556
- const entries: Record<string, FileEntry> = {};
557
- let stubCount = 0;
558
-
559
- for (const f of files) {
560
- const rel = relative(projectDir, f);
561
- const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
562
- entries[rel] = { hash: hashFile(f), status };
563
- if (status === "stub") stubCount++;
564
- }
770
+ const { entries, stubs: stubCount } = buildFileEntries(projectDir, files);
565
771
 
566
772
  const state: StateFile = {
567
- spec_version: doc.spec_version ?? "0.1",
773
+ spec_version: manifest.spec_version ?? "0.1",
568
774
  snapshot_at: new Date().toISOString(),
569
775
  target,
570
776
  baseline,
@@ -573,15 +779,46 @@ function snapshot(cwd: string, projectDir: string, target: string): void {
573
779
 
574
780
  const outPath = stateFilePath(projectDir, projectName, target);
575
781
  writeFileSync(outPath, JSON.stringify(state, null, 2) + "\n");
576
- console.log(`Snapshot saved: ${relative(cwd, outPath)}`);
577
- console.log(` ${Object.keys(entries).length} files hashed`);
578
- if (stubCount > 0) {
579
- console.log(` ${stubCount} stubs (not tracked for drift)`);
782
+
783
+ // Auto-snapshot shared layers with tracks for this target if their state file doesn't exist yet
784
+ const sharedLayers = sharedLayersForTarget(projectDir, target);
785
+ for (const layer of sharedLayers) {
786
+ if (layer.tracks.length === 0) continue;
787
+ const layerStatePath = sharedLayerStatePath(layer);
788
+ if (!existsSync(layerStatePath) && existsSync(layer.root)) {
789
+ try {
790
+ createSharedSnapshot(cwd, layer.name, target);
791
+ } catch {
792
+ // Non-fatal: shared layer snapshot is best-effort during target snapshot
793
+ }
794
+ }
580
795
  }
581
- console.log(` target: ${target}`);
582
- const baselineLabel = formatBaseline(baseline);
583
- if (baselineLabel) {
584
- console.log(` baseline: ${baselineLabel}`);
796
+
797
+ return {
798
+ target,
799
+ snapshot_at: state.snapshot_at,
800
+ files_hashed: Object.keys(entries).length,
801
+ stubs: stubCount,
802
+ state_path: relative(cwd, outPath),
803
+ baseline: formatBaseline(baseline),
804
+ };
805
+ }
806
+
807
+ function snapshot(cwd: string, projectDir: string, target: string): void {
808
+ try {
809
+ const result = createSnapshot(cwd, target);
810
+ console.log(`Snapshot saved: ${result.state_path}`);
811
+ console.log(` ${result.files_hashed} files hashed`);
812
+ if (result.stubs > 0) {
813
+ console.log(` ${result.stubs} stubs (not tracked for drift)`);
814
+ }
815
+ console.log(` target: ${result.target}`);
816
+ if (result.baseline) {
817
+ console.log(` baseline: ${result.baseline}`);
818
+ }
819
+ } catch (err) {
820
+ console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
821
+ process.exit(1);
585
822
  }
586
823
  }
587
824
 
@@ -593,6 +830,7 @@ export interface CheckResult {
593
830
  stubDrift: DriftResult;
594
831
  statuses: Record<string, string>;
595
832
  explanation?: ExplainResult;
833
+ shared_layer_drift?: SharedLayerDriftResult[];
596
834
  }
597
835
 
598
836
  export function computeDrift(
@@ -601,13 +839,7 @@ export function computeDrift(
601
839
  includeAll: boolean
602
840
  ): CheckResult {
603
841
  const files = discoverSpecFiles(projectDir);
604
-
605
- const current: Record<string, FileEntry> = {};
606
- for (const f of files) {
607
- const rel = relative(projectDir, f);
608
- const status = hasStatusSemantics(rel) ? readStatus(f) : "ready";
609
- current[rel] = { hash: hashFile(f), status };
610
- }
842
+ const { entries: current } = buildFileEntries(projectDir, files);
611
843
 
612
844
  const drift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
613
845
  const stubDrift: DriftResult = { changed: [], added: [], removed: [], unchanged: [] };
@@ -662,6 +894,15 @@ export function loadTargetDrift(
662
894
  result.explanation = explainDrift(projectDir, result);
663
895
  }
664
896
 
897
+ // Include shared layer drift for this target (only layers with tracks configured)
898
+ const sharedLayers = sharedLayersForTarget(projectDir, target);
899
+ const trackingLayers = sharedLayers.filter((l) => l.tracks.length > 0);
900
+ if (trackingLayers.length > 0) {
901
+ result.shared_layer_drift = trackingLayers.map((layer) =>
902
+ computeSharedDrift(projectDir, layer)
903
+ );
904
+ }
905
+
665
906
  return { projectDir, projectName, statePath, result };
666
907
  }
667
908