openuispec 0.1.18 → 0.1.20

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 (98) hide show
  1. package/README.md +52 -34
  2. package/cli/index.ts +1 -1
  3. package/cli/init.ts +48 -211
  4. package/docs/stress-test-maturity-report.md +97 -0
  5. package/examples/todo-orbit/AGENTS.md +127 -0
  6. package/examples/todo-orbit/CLAUDE.md +75 -0
  7. package/examples/todo-orbit/README.md +62 -0
  8. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +14 -0
  9. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +58 -0
  10. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +1 -0
  11. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +20 -0
  12. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +14 -0
  13. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +345 -0
  14. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +231 -0
  15. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +169 -0
  16. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +8 -0
  17. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +185 -0
  18. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +193 -0
  19. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +102 -0
  20. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +342 -0
  21. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +344 -0
  22. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +59 -0
  23. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +148 -0
  24. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +154 -0
  25. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +4 -0
  26. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  27. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +7 -0
  28. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +4 -0
  29. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +248 -0
  30. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +93 -0
  31. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +18 -0
  32. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +29 -0
  33. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +118 -0
  34. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +118 -0
  35. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +50 -0
  36. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +204 -0
  37. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +126 -0
  38. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +70 -0
  39. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +123 -0
  40. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +60 -0
  41. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +238 -0
  42. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +94 -0
  43. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +74 -0
  44. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +363 -0
  45. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +324 -0
  46. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +408 -0
  47. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  48. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +79 -0
  49. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +24 -0
  50. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +16 -0
  51. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +1087 -0
  52. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +24 -0
  53. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +2114 -0
  54. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +13 -0
  55. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +886 -0
  56. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +19 -0
  57. package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +6 -0
  58. package/examples/todo-orbit/openuispec/README.md +158 -0
  59. package/examples/todo-orbit/openuispec/contracts/.gitkeep +0 -0
  60. package/examples/todo-orbit/openuispec/contracts/action_trigger.yaml +28 -0
  61. package/examples/todo-orbit/openuispec/contracts/collection.yaml +32 -0
  62. package/examples/todo-orbit/openuispec/contracts/data_display.yaml +38 -0
  63. package/examples/todo-orbit/openuispec/contracts/feedback.yaml +32 -0
  64. package/examples/todo-orbit/openuispec/contracts/input_field.yaml +52 -0
  65. package/examples/todo-orbit/openuispec/contracts/nav_container.yaml +47 -0
  66. package/examples/todo-orbit/openuispec/contracts/surface.yaml +28 -0
  67. package/examples/todo-orbit/openuispec/contracts/x_schedule_preview.yaml +134 -0
  68. package/examples/todo-orbit/openuispec/contracts/x_task_trend_chart.yaml +139 -0
  69. package/examples/todo-orbit/openuispec/flows/.gitkeep +0 -0
  70. package/examples/todo-orbit/openuispec/flows/create_recurring_rule.yaml +253 -0
  71. package/examples/todo-orbit/openuispec/flows/create_task.yaml +118 -0
  72. package/examples/todo-orbit/openuispec/flows/edit_task.yaml +126 -0
  73. package/examples/todo-orbit/openuispec/locales/.gitkeep +0 -0
  74. package/examples/todo-orbit/openuispec/locales/en.json +150 -0
  75. package/examples/todo-orbit/openuispec/locales/ru.json +150 -0
  76. package/examples/todo-orbit/openuispec/openuispec.yaml +122 -0
  77. package/examples/todo-orbit/openuispec/platform/.gitkeep +0 -0
  78. package/examples/todo-orbit/openuispec/platform/android.yaml +19 -0
  79. package/examples/todo-orbit/openuispec/platform/ios.yaml +20 -0
  80. package/examples/todo-orbit/openuispec/platform/web.yaml +22 -0
  81. package/examples/todo-orbit/openuispec/screens/.gitkeep +0 -0
  82. package/examples/todo-orbit/openuispec/screens/analytics.yaml +140 -0
  83. package/examples/todo-orbit/openuispec/screens/home.yaml +173 -0
  84. package/examples/todo-orbit/openuispec/screens/settings.yaml +149 -0
  85. package/examples/todo-orbit/openuispec/screens/task_detail.yaml +223 -0
  86. package/examples/todo-orbit/openuispec/tokens/.gitkeep +0 -0
  87. package/examples/todo-orbit/openuispec/tokens/color.yaml +93 -0
  88. package/examples/todo-orbit/openuispec/tokens/elevation.yaml +25 -0
  89. package/examples/todo-orbit/openuispec/tokens/icons.yaml +92 -0
  90. package/examples/todo-orbit/openuispec/tokens/layout.yaml +107 -0
  91. package/examples/todo-orbit/openuispec/tokens/motion.yaml +39 -0
  92. package/examples/todo-orbit/openuispec/tokens/spacing.yaml +18 -0
  93. package/examples/todo-orbit/openuispec/tokens/themes.yaml +23 -0
  94. package/examples/todo-orbit/openuispec/tokens/typography.yaml +52 -0
  95. package/package.json +1 -1
  96. package/schema/screen.schema.json +9 -0
  97. package/schema/validate.ts +0 -2
  98. package/spec/openuispec-v0.1.md +129 -27
@@ -0,0 +1,139 @@
1
+ x_task_trend_chart:
2
+ semantic: "Visualizes task creation and completion trends over time for productivity analysis"
3
+
4
+ props:
5
+ series: { type: "list<trend_point>", required: true, description: "Ordered trend points for the selected period" }
6
+ metric:
7
+ type: enum
8
+ values: [completed, created, completion_rate]
9
+ required: true
10
+ description: "Primary metric emphasized in the chart"
11
+ period:
12
+ type: enum
13
+ values: [week, month, quarter]
14
+ required: true
15
+ description: "Time range represented by the chart"
16
+ variant:
17
+ type: enum
18
+ values: [compact, detail]
19
+ default: compact
20
+ description: "Chart presentation density"
21
+ highlighted_index: { type: int, required: false, description: "Optional highlighted datapoint" }
22
+ show_legend: { type: bool, default: true, description: "Whether to render a legend for created/completed series" }
23
+ empty_message: { type: string, required: false, description: "Message to show when series is empty" }
24
+
25
+ states:
26
+ idle:
27
+ semantic: "Chart has not loaded data yet"
28
+ transitions_to: [loading]
29
+ visual: "Reserved chart frame with no plotted data"
30
+ loading:
31
+ semantic: "Trend data is loading"
32
+ transitions_to: [ready, empty, error]
33
+ feedback: "Skeleton chart and loading copy shown"
34
+ visual: "Placeholder bars or line skeleton"
35
+ ready:
36
+ semantic: "Trend data is available and rendered"
37
+ transitions_to: [highlighted, loading, error]
38
+ behavior: "Chart axes, series, and legend are visible"
39
+ highlighted:
40
+ semantic: "One datapoint is emphasized"
41
+ transitions_to: [ready]
42
+ behavior: "Highlighted datapoint shows stronger color and tooltip/callout"
43
+ empty:
44
+ semantic: "No trend points available"
45
+ transitions_to: [loading]
46
+ feedback: "Empty analytics message displayed"
47
+ visual: "Empty state in chart container"
48
+ error:
49
+ semantic: "Trend data failed to load"
50
+ transitions_to: [loading]
51
+ feedback: "Inline error with retry affordance"
52
+ visual: "Error indicator in chart container"
53
+
54
+ a11y:
55
+ role: "img"
56
+ label: "Analytics trend chart"
57
+ traits:
58
+ loading: { announces: "Loading analytics chart" }
59
+ empty: { announces: "No analytics data available" }
60
+ error: { announces: "Analytics chart failed to load" }
61
+ focus:
62
+ keyboard:
63
+ next_point: "ArrowRight"
64
+ previous_point: "ArrowLeft"
65
+ details: "Enter"
66
+
67
+ tokens:
68
+ compact:
69
+ min_height: 220
70
+ padding: "spacing.md"
71
+ background: "color.surface.secondary"
72
+ border: { width: 1, color: "color.border.default" }
73
+ radius: "spacing.sm"
74
+ axis_color: "color.text.tertiary"
75
+ completed_color: "color.brand.primary"
76
+ created_color: "color.brand.secondary"
77
+ grid_color: "color.border.default"
78
+ label_style: "typography.caption"
79
+ detail:
80
+ min_height: 320
81
+ padding: "spacing.lg"
82
+ background: "color.surface.primary"
83
+ border: { width: 1, color: "color.border.default" }
84
+ radius: "spacing.sm"
85
+ axis_color: "color.text.tertiary"
86
+ completed_color: "color.brand.primary"
87
+ created_color: "color.brand.secondary"
88
+ grid_color: "color.border.default"
89
+ label_style: "typography.body_sm"
90
+
91
+ platform_mapping:
92
+ ios:
93
+ compact: { component: "Chart", framework: "Swift Charts" }
94
+ detail: { component: "Chart", framework: "Swift Charts" }
95
+ android:
96
+ compact: { component: "Canvas chart composable", library: "custom compose drawing" }
97
+ detail: { component: "Canvas chart composable", library: "custom compose drawing" }
98
+ web:
99
+ compact: { component: "SVG chart component" }
100
+ detail: { component: "SVG chart component" }
101
+
102
+ dependencies:
103
+ ios:
104
+ frameworks: ["Charts"]
105
+ android:
106
+ libraries: []
107
+ web:
108
+ packages: []
109
+
110
+ generation:
111
+ must_handle:
112
+ - "Render completed and created series for the supplied period"
113
+ - "Respect compact and detail variants with different density"
114
+ - "Provide accessible summary text for the focused datapoint"
115
+ - "Handle loading, empty, and error states in the chart container"
116
+ should_handle:
117
+ - "Animate transitions when highlighted_index changes"
118
+ - "Render a legend when show_legend is true"
119
+ - "Support touch and pointer hover highlighting"
120
+ may_handle:
121
+ - "Tooltips for datapoints"
122
+ - "Trend delta badges and annotations"
123
+
124
+ test_cases:
125
+ - id: ready_state_renders_series
126
+ description: "Chart renders series lines or bars when data is present"
127
+ given: "Chart receives 7 trend points for period week"
128
+ when: "The component enters ready state"
129
+ then: "Both created and completed series are visible with labels and axes"
130
+ - id: empty_state_message
131
+ description: "Chart shows an empty message when no data exists"
132
+ given: "Chart receives an empty series"
133
+ when: "The component enters empty state"
134
+ then: "The empty_message or default empty copy is rendered in the chart frame"
135
+ - id: highlighted_point
136
+ description: "Highlighted point receives emphasis"
137
+ given: "Chart is in ready state with highlighted_index set"
138
+ when: "The highlighted datapoint is rendered"
139
+ then: "That datapoint is visually emphasized and exposed to accessibility"
File without changes
@@ -0,0 +1,253 @@
1
+ create_recurring_rule:
2
+ semantic: "Validation-heavy flow for creating recurring task rules"
3
+ status: ready
4
+
5
+ entry: "rule_form"
6
+
7
+ screens:
8
+ rule_form:
9
+ screen_inline:
10
+ semantic: "Recurring rule form with field dependencies, async validation, and cross-field checks"
11
+
12
+ data:
13
+ preferences:
14
+ source: "api.preferences.get"
15
+
16
+ state:
17
+ is_submitting: { type: bool, default: false }
18
+
19
+ layout:
20
+ type: scroll_vertical
21
+ safe_area: true
22
+ padding: "spacing.page_margin"
23
+ sections:
24
+ - id: header
25
+ layout: { type: row, justify: "space-between", align: "center" }
26
+ children:
27
+ - contract: action_trigger
28
+ variant: ghost
29
+ size: sm
30
+ props: { label: "$t:common.cancel" }
31
+ action: { type: dismiss }
32
+
33
+ - contract: data_display
34
+ variant: inline
35
+ props:
36
+ title: "$t:recurring_rule.title"
37
+ subtitle: "$t:recurring_rule.subtitle"
38
+ tokens_override:
39
+ title_style: "typography.heading_sm"
40
+ subtitle_style: "typography.body_sm"
41
+
42
+ - contract: action_trigger
43
+ variant: primary
44
+ size: sm
45
+ props:
46
+ label: "$t:recurring_rule.save"
47
+ loading_label: "$t:recurring_rule.saving"
48
+ state_binding:
49
+ loading: "state.is_submitting"
50
+ action:
51
+ type: submit_form
52
+ form_id: "recurring_rule_form"
53
+ on_validation_error:
54
+ type: feedback
55
+ variant: toast
56
+ message: "$t:validation.fix_errors"
57
+ severity: warning
58
+ duration: 2800
59
+
60
+ - id: form
61
+ form_id: "recurring_rule_form"
62
+ margin_top: "spacing.lg"
63
+ layout: { type: stack, spacing: "spacing.md" }
64
+ children:
65
+ - contract: input_field
66
+ input_type: text
67
+ props:
68
+ label: "$t:recurring_rule.field_name"
69
+ placeholder: "$t:recurring_rule.field_name_placeholder"
70
+ required: true
71
+ max_length: 80
72
+ validate:
73
+ min_length: { value: 4, message: "$t:validation.rule_name_min_length" }
74
+ custom:
75
+ expression: "$value != 'Default'"
76
+ message: "$t:validation.rule_name_reserved"
77
+ async:
78
+ endpoint: "api.recurring.checkName"
79
+ debounce: 450
80
+ message: "$t:validation.rule_name_taken"
81
+ validate_trigger: on_change
82
+ data_binding: "form.name"
83
+
84
+ - contract: input_field
85
+ input_type: text
86
+ props:
87
+ label: "$t:recurring_rule.field_confirm_name"
88
+ placeholder: "$t:recurring_rule.field_confirm_name_placeholder"
89
+ required: true
90
+ validate:
91
+ match_field:
92
+ field: "form.name"
93
+ message: "$t:validation.match_field"
94
+ validate_trigger: on_blur
95
+ data_binding: "form.confirm_name"
96
+
97
+ - contract: input_field
98
+ input_type: select
99
+ props:
100
+ label: "$t:recurring_rule.field_cadence"
101
+ required: true
102
+ options:
103
+ - { value: "daily", label: "$t:recurring_rule.cadence_daily" }
104
+ - { value: "weekly", label: "$t:recurring_rule.cadence_weekly" }
105
+ - { value: "monthly", label: "$t:recurring_rule.cadence_monthly" }
106
+ data_binding: "form.cadence"
107
+
108
+ - contract: input_field
109
+ input_type: number
110
+ props:
111
+ label: "$t:recurring_rule.field_interval"
112
+ helper_text: "$t:recurring_rule.field_interval_helper"
113
+ required: true
114
+ validate:
115
+ min: { value: 1, message: "$t:validation.min_value" }
116
+ max: { value: 30, message: "$t:validation.max_value" }
117
+ data_binding: "form.interval"
118
+
119
+ - contract: input_field
120
+ input_type: select
121
+ props:
122
+ label: "$t:recurring_rule.field_weekday"
123
+ options:
124
+ - { value: "mon", label: "$t:weekday.mon" }
125
+ - { value: "tue", label: "$t:weekday.tue" }
126
+ - { value: "wed", label: "$t:weekday.wed" }
127
+ - { value: "thu", label: "$t:weekday.thu" }
128
+ - { value: "fri", label: "$t:weekday.fri" }
129
+ - { value: "sat", label: "$t:weekday.sat" }
130
+ - { value: "sun", label: "$t:weekday.sun" }
131
+ condition: "form.cadence == 'weekly'"
132
+ required_when: "form.cadence == 'weekly'"
133
+ enabled_when: "form.cadence == 'weekly'"
134
+ data_binding: "form.weekday"
135
+
136
+ - contract: input_field
137
+ input_type: number
138
+ props:
139
+ label: "$t:recurring_rule.field_month_day"
140
+ helper_text: "$t:recurring_rule.field_month_day_helper"
141
+ condition: "form.cadence == 'monthly'"
142
+ required_when: "form.cadence == 'monthly'"
143
+ enabled_when: "form.cadence == 'monthly'"
144
+ validate:
145
+ min: { value: 1, message: "$t:validation.min_value" }
146
+ max: { value: 28, message: "$t:validation.month_day_max" }
147
+ data_binding: "form.month_day"
148
+
149
+ - contract: input_field
150
+ input_type: date
151
+ props:
152
+ label: "$t:recurring_rule.field_start_date"
153
+ required: true
154
+ data_binding: "form.start_date"
155
+
156
+ - contract: input_field
157
+ input_type: toggle
158
+ props:
159
+ label: "$t:recurring_rule.field_has_end_date"
160
+ helper_text: "$t:recurring_rule.field_has_end_date_helper"
161
+ value: false
162
+ data_binding: "form.has_end_date"
163
+
164
+ - contract: input_field
165
+ input_type: date
166
+ props:
167
+ label: "$t:recurring_rule.field_end_date"
168
+ required_when: "form.has_end_date == true"
169
+ enabled_when: "form.has_end_date == true"
170
+ validate:
171
+ custom:
172
+ expression: "$value >= form.start_date"
173
+ message: "$t:validation.end_date_after_start"
174
+ data_binding: "form.end_date"
175
+
176
+ - contract: input_field
177
+ input_type: text
178
+ props:
179
+ label: "$t:recurring_rule.field_remind_at"
180
+ placeholder: "$t:recurring_rule.field_remind_at_placeholder"
181
+ helper_text: "$t:recurring_rule.field_remind_at_helper"
182
+ condition: "preferences.reminders_enabled == true"
183
+ required_when: "preferences.reminders_enabled == true"
184
+ enabled_when: "preferences.reminders_enabled == true"
185
+ validate:
186
+ pattern:
187
+ regex: "^([01]\\d|2[0-3]):[0-5]\\d$"
188
+ message: "$t:validation.time_format"
189
+ validate_trigger: on_submit
190
+ data_binding: "form.remind_at"
191
+
192
+ - contract: input_field
193
+ input_type: toggle
194
+ props:
195
+ label: "$t:recurring_rule.field_enable_summary"
196
+ helper_text: "$t:recurring_rule.field_enable_summary_helper"
197
+ value: false
198
+ data_binding: "form.enable_summary"
199
+
200
+ - contract: input_field
201
+ input_type: select
202
+ props:
203
+ label: "$t:recurring_rule.field_summary_channel"
204
+ options:
205
+ - { value: "push", label: "$t:recurring_rule.summary_push" }
206
+ - { value: "email", label: "$t:recurring_rule.summary_email" }
207
+ condition: "form.enable_summary == true"
208
+ required_when: "form.enable_summary == true"
209
+ enabled_when: "form.enable_summary == true"
210
+ data_binding: "form.summary_channel"
211
+
212
+ - id: preview
213
+ margin_top: "spacing.xl"
214
+ contract: x_schedule_preview
215
+ variant: detail
216
+ condition: "form.cadence != ''"
217
+ props:
218
+ title: "$t:recurring_preview.title"
219
+ cadence: "form.cadence"
220
+ interval: "form.interval"
221
+ weekday: "form.weekday"
222
+ month_day: "form.month_day"
223
+ start_date: "form.start_date"
224
+ end_date: "form.end_date"
225
+ preview_count: 4
226
+ empty_message: "$t:recurring_preview.empty"
227
+ invalid_message: "$t:recurring_preview.invalid"
228
+
229
+ on_submit:
230
+ type: sequence
231
+ actions:
232
+ - { type: set_state, target: "state.is_submitting", value: true }
233
+ - type: api_call
234
+ endpoint: "api.recurring.create"
235
+ body: "form"
236
+ on_success:
237
+ type: sequence
238
+ actions:
239
+ - { type: set_state, target: "state.is_submitting", value: false }
240
+ - { type: feedback, variant: toast, message: "$t:recurring_rule.success", severity: success, duration: 2500 }
241
+ - { type: dismiss }
242
+ on_error:
243
+ type: sequence
244
+ actions:
245
+ - { type: set_state, target: "state.is_submitting", value: false }
246
+ - { type: feedback, variant: banner, title: "$t:recurring_rule.error_title", message: "{error.message}", severity: error }
247
+
248
+ transitions: {}
249
+
250
+ platform_hints:
251
+ ios: { presentation: "sheet", detents: [large] }
252
+ android: { presentation: "bottom_sheet", fullscreen: true }
253
+ web: { presentation: "modal", size: "lg" }
@@ -0,0 +1,118 @@
1
+ create_task:
2
+ semantic: "Single-screen flow for creating a new todo item"
3
+ status: ready
4
+
5
+ entry: "task_form"
6
+
7
+ screens:
8
+ task_form:
9
+ screen_inline:
10
+ semantic: "Task creation form with fields for title, notes, priority, and due date"
11
+
12
+ state:
13
+ is_submitting: { type: bool, default: false }
14
+
15
+ layout:
16
+ type: scroll_vertical
17
+ safe_area: true
18
+ padding: "spacing.page_margin"
19
+ sections:
20
+ - id: header
21
+ layout: { type: row, justify: "space-between", align: "center" }
22
+ children:
23
+ - contract: action_trigger
24
+ variant: ghost
25
+ size: sm
26
+ props: { label: "$t:common.cancel" }
27
+ action: { type: dismiss }
28
+
29
+ - contract: data_display
30
+ variant: inline
31
+ props:
32
+ title: "$t:create_task.title"
33
+ tokens_override:
34
+ title_style: "typography.heading"
35
+
36
+ - contract: action_trigger
37
+ variant: primary
38
+ size: sm
39
+ props:
40
+ label: "$t:create_task.save"
41
+ loading_label: "$t:create_task.saving"
42
+ state_binding:
43
+ loading: "state.is_submitting"
44
+ action:
45
+ type: submit_form
46
+ form_id: "task_form"
47
+
48
+ - id: form
49
+ form_id: "task_form"
50
+ margin_top: "spacing.lg"
51
+ layout: { type: stack, spacing: "spacing.md" }
52
+ children:
53
+ - contract: input_field
54
+ input_type: text
55
+ props:
56
+ label: "$t:create_task.field_title"
57
+ placeholder: "$t:create_task.field_title_placeholder"
58
+ required: true
59
+ max_length: 160
60
+ validate:
61
+ min_length: { value: 2, message: "$t:validation.min_length" }
62
+ data_binding: "form.title"
63
+
64
+ - contract: input_field
65
+ input_type: multiline
66
+ props:
67
+ label: "$t:create_task.field_notes"
68
+ placeholder: "$t:create_task.field_notes_placeholder"
69
+ required: false
70
+ data_binding: "form.notes"
71
+ tokens_override:
72
+ min_height: 96
73
+
74
+ - contract: input_field
75
+ input_type: select
76
+ props:
77
+ label: "$t:create_task.field_priority"
78
+ options:
79
+ - { value: "low", label: "$t:priority.low", icon: "flag" }
80
+ - { value: "medium", label: "$t:priority.medium", icon: "flag_fill" }
81
+ - { value: "high", label: "$t:priority.high", icon: "flag_fill" }
82
+ required: true
83
+ data_binding: "form.priority"
84
+
85
+ - contract: input_field
86
+ input_type: date
87
+ props:
88
+ label: "$t:create_task.field_due_date"
89
+ placeholder: "$t:create_task.field_due_date_placeholder"
90
+ required: false
91
+ data_binding: "form.due_date"
92
+
93
+ on_submit:
94
+ type: sequence
95
+ actions:
96
+ - { type: set_state, target: "state.is_submitting", value: true }
97
+ - type: api_call
98
+ endpoint: "api.tasks.create"
99
+ body: "form"
100
+ on_success:
101
+ type: sequence
102
+ actions:
103
+ - { type: set_state, target: "state.is_submitting", value: false }
104
+ - { type: feedback, variant: toast, message: "$t:create_task.success", severity: success, duration: 2500 }
105
+ - { type: dismiss }
106
+ - { type: refresh, target: "screens/home.tasks" }
107
+ on_error:
108
+ type: sequence
109
+ actions:
110
+ - { type: set_state, target: "state.is_submitting", value: false }
111
+ - { type: feedback, variant: banner, title: "$t:create_task.error_title", message: "{error.message}", severity: error }
112
+
113
+ transitions: {}
114
+
115
+ platform_hints:
116
+ ios: { presentation: "sheet", detents: [medium, large] }
117
+ android: { presentation: "bottom_sheet", fullscreen: false }
118
+ web: { presentation: "modal", size: "md" }
@@ -0,0 +1,126 @@
1
+ edit_task:
2
+ semantic: "Single-screen flow for editing an existing task"
3
+ status: ready
4
+
5
+ params:
6
+ task_id: { type: string, required: true }
7
+
8
+ entry: "task_form"
9
+
10
+ screens:
11
+ task_form:
12
+ screen_inline:
13
+ semantic: "Task edit form pre-populated with existing task data"
14
+
15
+ data:
16
+ task:
17
+ source: "api.tasks.getById"
18
+ params: { id: "params.task_id" }
19
+
20
+ state:
21
+ is_submitting: { type: bool, default: false }
22
+
23
+ layout:
24
+ type: scroll_vertical
25
+ safe_area: true
26
+ padding: "spacing.page_margin"
27
+ sections:
28
+ - id: header
29
+ layout: { type: row, justify: "space-between", align: "center" }
30
+ children:
31
+ - contract: action_trigger
32
+ variant: ghost
33
+ size: sm
34
+ props: { label: "$t:common.cancel" }
35
+ action: { type: dismiss }
36
+
37
+ - contract: data_display
38
+ variant: inline
39
+ props:
40
+ title: "$t:edit_task.title"
41
+ tokens_override:
42
+ title_style: "typography.heading"
43
+
44
+ - contract: action_trigger
45
+ variant: primary
46
+ size: sm
47
+ props:
48
+ label: "$t:edit_task.save"
49
+ loading_label: "$t:edit_task.saving"
50
+ state_binding:
51
+ loading: "state.is_submitting"
52
+ action:
53
+ type: submit_form
54
+ form_id: "edit_task_form"
55
+
56
+ - id: form
57
+ form_id: "edit_task_form"
58
+ margin_top: "spacing.lg"
59
+ layout: { type: stack, spacing: "spacing.md" }
60
+ children:
61
+ - contract: input_field
62
+ input_type: text
63
+ props:
64
+ label: "$t:edit_task.field_title"
65
+ value: "task.title"
66
+ required: true
67
+ max_length: 160
68
+ validate:
69
+ min_length: { value: 2, message: "$t:validation.min_length" }
70
+ data_binding: "form.title"
71
+
72
+ - contract: input_field
73
+ input_type: multiline
74
+ props:
75
+ label: "$t:edit_task.field_notes"
76
+ value: "task.notes"
77
+ data_binding: "form.notes"
78
+ tokens_override:
79
+ min_height: 96
80
+
81
+ - contract: input_field
82
+ input_type: select
83
+ props:
84
+ label: "$t:edit_task.field_priority"
85
+ value: "task.priority"
86
+ options:
87
+ - { value: "low", label: "$t:priority.low", icon: "flag" }
88
+ - { value: "medium", label: "$t:priority.medium", icon: "flag_fill" }
89
+ - { value: "high", label: "$t:priority.high", icon: "flag_fill" }
90
+ required: true
91
+ data_binding: "form.priority"
92
+
93
+ - contract: input_field
94
+ input_type: date
95
+ props:
96
+ label: "$t:edit_task.field_due_date"
97
+ value: "task.due_date"
98
+ data_binding: "form.due_date"
99
+
100
+ on_submit:
101
+ type: sequence
102
+ actions:
103
+ - { type: set_state, target: "state.is_submitting", value: true }
104
+ - type: api_call
105
+ endpoint: "api.tasks.update"
106
+ params: { id: "params.task_id" }
107
+ body: "form"
108
+ on_success:
109
+ type: sequence
110
+ actions:
111
+ - { type: set_state, target: "state.is_submitting", value: false }
112
+ - { type: feedback, variant: toast, message: "$t:edit_task.success", severity: success, duration: 2500 }
113
+ - { type: dismiss }
114
+ - { type: refresh, target: "screens/task_detail.task" }
115
+ on_error:
116
+ type: sequence
117
+ actions:
118
+ - { type: set_state, target: "state.is_submitting", value: false }
119
+ - { type: feedback, variant: banner, title: "$t:edit_task.error_title", message: "{error.message}", severity: error }
120
+
121
+ transitions: {}
122
+
123
+ platform_hints:
124
+ ios: { presentation: "sheet", detents: [large] }
125
+ android: { presentation: "bottom_sheet", fullscreen: false }
126
+ web: { presentation: "modal", size: "md" }