openuispec 0.1.18 → 0.1.19

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 (96) hide show
  1. package/README.md +52 -34
  2. package/cli/index.ts +1 -1
  3. package/docs/stress-test-maturity-report.md +97 -0
  4. package/examples/todo-orbit/AGENTS.md +127 -0
  5. package/examples/todo-orbit/CLAUDE.md +127 -0
  6. package/examples/todo-orbit/README.md +62 -0
  7. package/examples/todo-orbit/generated/android/Todo Orbit/README.md +14 -0
  8. package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +58 -0
  9. package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +1 -0
  10. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +20 -0
  11. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +14 -0
  12. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +345 -0
  13. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +231 -0
  14. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +169 -0
  15. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +8 -0
  16. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +185 -0
  17. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +193 -0
  18. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +102 -0
  19. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +342 -0
  20. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +344 -0
  21. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +59 -0
  22. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +148 -0
  23. package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +154 -0
  24. package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +4 -0
  25. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
  26. package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +7 -0
  27. package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +4 -0
  28. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +248 -0
  29. package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +93 -0
  30. package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +18 -0
  31. package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +29 -0
  32. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +118 -0
  33. package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +118 -0
  34. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +50 -0
  35. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +204 -0
  36. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +126 -0
  37. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +70 -0
  38. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +123 -0
  39. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +60 -0
  40. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +238 -0
  41. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +94 -0
  42. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +74 -0
  43. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +363 -0
  44. package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +324 -0
  45. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +408 -0
  46. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  47. package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +79 -0
  48. package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +24 -0
  49. package/examples/todo-orbit/generated/web/Todo Orbit/index.html +16 -0
  50. package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +1087 -0
  51. package/examples/todo-orbit/generated/web/Todo Orbit/package.json +24 -0
  52. package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +2114 -0
  53. package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +13 -0
  54. package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +886 -0
  55. package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +19 -0
  56. package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +6 -0
  57. package/examples/todo-orbit/openuispec/README.md +158 -0
  58. package/examples/todo-orbit/openuispec/contracts/.gitkeep +0 -0
  59. package/examples/todo-orbit/openuispec/contracts/action_trigger.yaml +28 -0
  60. package/examples/todo-orbit/openuispec/contracts/collection.yaml +32 -0
  61. package/examples/todo-orbit/openuispec/contracts/data_display.yaml +38 -0
  62. package/examples/todo-orbit/openuispec/contracts/feedback.yaml +32 -0
  63. package/examples/todo-orbit/openuispec/contracts/input_field.yaml +52 -0
  64. package/examples/todo-orbit/openuispec/contracts/nav_container.yaml +47 -0
  65. package/examples/todo-orbit/openuispec/contracts/surface.yaml +28 -0
  66. package/examples/todo-orbit/openuispec/contracts/x_schedule_preview.yaml +134 -0
  67. package/examples/todo-orbit/openuispec/contracts/x_task_trend_chart.yaml +139 -0
  68. package/examples/todo-orbit/openuispec/flows/.gitkeep +0 -0
  69. package/examples/todo-orbit/openuispec/flows/create_recurring_rule.yaml +253 -0
  70. package/examples/todo-orbit/openuispec/flows/create_task.yaml +118 -0
  71. package/examples/todo-orbit/openuispec/flows/edit_task.yaml +126 -0
  72. package/examples/todo-orbit/openuispec/locales/.gitkeep +0 -0
  73. package/examples/todo-orbit/openuispec/locales/en.json +150 -0
  74. package/examples/todo-orbit/openuispec/locales/ru.json +150 -0
  75. package/examples/todo-orbit/openuispec/openuispec.yaml +122 -0
  76. package/examples/todo-orbit/openuispec/platform/.gitkeep +0 -0
  77. package/examples/todo-orbit/openuispec/platform/android.yaml +19 -0
  78. package/examples/todo-orbit/openuispec/platform/ios.yaml +20 -0
  79. package/examples/todo-orbit/openuispec/platform/web.yaml +22 -0
  80. package/examples/todo-orbit/openuispec/screens/.gitkeep +0 -0
  81. package/examples/todo-orbit/openuispec/screens/analytics.yaml +139 -0
  82. package/examples/todo-orbit/openuispec/screens/home.yaml +172 -0
  83. package/examples/todo-orbit/openuispec/screens/settings.yaml +148 -0
  84. package/examples/todo-orbit/openuispec/screens/task_detail.yaml +223 -0
  85. package/examples/todo-orbit/openuispec/tokens/.gitkeep +0 -0
  86. package/examples/todo-orbit/openuispec/tokens/color.yaml +93 -0
  87. package/examples/todo-orbit/openuispec/tokens/elevation.yaml +25 -0
  88. package/examples/todo-orbit/openuispec/tokens/icons.yaml +92 -0
  89. package/examples/todo-orbit/openuispec/tokens/layout.yaml +107 -0
  90. package/examples/todo-orbit/openuispec/tokens/motion.yaml +39 -0
  91. package/examples/todo-orbit/openuispec/tokens/spacing.yaml +18 -0
  92. package/examples/todo-orbit/openuispec/tokens/themes.yaml +23 -0
  93. package/examples/todo-orbit/openuispec/tokens/typography.yaml +52 -0
  94. package/package.json +1 -1
  95. package/schema/validate.ts +0 -2
  96. package/spec/openuispec-v0.1.md +76 -12
@@ -0,0 +1,93 @@
1
+ @rem
2
+ @rem Copyright 2015 the original author or authors.
3
+ @rem
4
+ @rem Licensed under the Apache License, Version 2.0 (the "License");
5
+ @rem you may not use this file except in compliance with the License.
6
+ @rem You may obtain a copy of the License at
7
+ @rem
8
+ @rem https://www.apache.org/licenses/LICENSE-2.0
9
+ @rem
10
+ @rem Unless required by applicable law or agreed to in writing, software
11
+ @rem distributed under the License is distributed on an "AS IS" BASIS,
12
+ @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ @rem See the License for the specific language governing permissions and
14
+ @rem limitations under the License.
15
+ @rem
16
+ @rem SPDX-License-Identifier: Apache-2.0
17
+ @rem
18
+
19
+ @if "%DEBUG%"=="" @echo off
20
+ @rem ##########################################################################
21
+ @rem
22
+ @rem Gradle startup script for Windows
23
+ @rem
24
+ @rem ##########################################################################
25
+
26
+ @rem Set local scope for the variables with windows NT shell
27
+ if "%OS%"=="Windows_NT" setlocal
28
+
29
+ set DIRNAME=%~dp0
30
+ if "%DIRNAME%"=="" set DIRNAME=.
31
+ @rem This is normally unused
32
+ set APP_BASE_NAME=%~n0
33
+ set APP_HOME=%DIRNAME%
34
+
35
+ @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36
+ for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37
+
38
+ @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39
+ set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40
+
41
+ @rem Find java.exe
42
+ if defined JAVA_HOME goto findJavaFromJavaHome
43
+
44
+ set JAVA_EXE=java.exe
45
+ %JAVA_EXE% -version >NUL 2>&1
46
+ if %ERRORLEVEL% equ 0 goto execute
47
+
48
+ echo. 1>&2
49
+ echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50
+ echo. 1>&2
51
+ echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52
+ echo location of your Java installation. 1>&2
53
+
54
+ goto fail
55
+
56
+ :findJavaFromJavaHome
57
+ set JAVA_HOME=%JAVA_HOME:"=%
58
+ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59
+
60
+ if exist "%JAVA_EXE%" goto execute
61
+
62
+ echo. 1>&2
63
+ echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64
+ echo. 1>&2
65
+ echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66
+ echo location of your Java installation. 1>&2
67
+
68
+ goto fail
69
+
70
+ :execute
71
+ @rem Setup the command line
72
+
73
+
74
+
75
+ @rem Execute Gradle
76
+ "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
77
+
78
+ :end
79
+ @rem End local scope for the variables with windows NT shell
80
+ if %ERRORLEVEL% equ 0 goto mainEnd
81
+
82
+ :fail
83
+ rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
84
+ rem the _cmd.exe /c_ return code!
85
+ set EXIT_CODE=%ERRORLEVEL%
86
+ if %EXIT_CODE% equ 0 set EXIT_CODE=1
87
+ if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
88
+ exit /b %EXIT_CODE%
89
+
90
+ :mainEnd
91
+ if "%OS%"=="Windows_NT" endlocal
92
+
93
+ :omega
@@ -0,0 +1,18 @@
1
+ pluginManagement {
2
+ repositories {
3
+ google()
4
+ mavenCentral()
5
+ gradlePluginPortal()
6
+ }
7
+ }
8
+
9
+ dependencyResolutionManagement {
10
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11
+ repositories {
12
+ google()
13
+ mavenCentral()
14
+ }
15
+ }
16
+
17
+ rootProject.name = "Todo Orbit"
18
+ include(":app")
@@ -0,0 +1,29 @@
1
+ # Todo Orbit iOS Target
2
+
3
+ This directory contains a generated SwiftUI target for the current OpenUISpec project.
4
+
5
+ ## Structure
6
+
7
+ - `TodoOrbit.xcodeproj`: ready-to-open Xcode project
8
+ - `project.yml`: lightweight XcodeGen project definition kept as a secondary source
9
+ - `Sources/TodoOrbit/`: SwiftUI source tree
10
+ - `Resources/`: localized strings for English and Russian
11
+
12
+ ## Coverage
13
+
14
+ The scaffold mirrors the current spec surface:
15
+
16
+ - Tasks home with search, filters, split-view detail, create and edit flows
17
+ - Analytics dashboard with native Swift Charts rendering
18
+ - Settings with language and theme switching
19
+ - Recurring-rule flow with validation and schedule preview
20
+ - Two custom components:
21
+ - `TrendChartView` for `x_task_trend_chart`
22
+ - `SchedulePreviewView` for `x_schedule_preview`
23
+
24
+ ## Notes
25
+
26
+ - This target is intentionally compact and source-focused.
27
+ - `TodoOrbit.xcodeproj` is checked in and builds with `xcodebuild`.
28
+ - `project.yml` remains available if you want to regenerate the project structure with XcodeGen later.
29
+ - The existing `.openuispec-state.json` file was preserved.
@@ -0,0 +1,118 @@
1
+ "nav.tasks" = "Tasks";
2
+ "nav.analytics" = "Analytics";
3
+ "nav.settings" = "Settings";
4
+ "home.title" = "Today, organized";
5
+ "home.summary.done" = "Everything is done";
6
+ "home.summary.remaining" = "%d tasks left out of %d";
7
+ "home.search_placeholder" = "Search by title or notes";
8
+ "home.filter_all" = "All";
9
+ "home.filter_open" = "Open";
10
+ "home.filter_done" = "Done";
11
+ "home.new_task" = "New task";
12
+ "home.empty_title" = "Nothing to do";
13
+ "analytics.title" = "Task analytics";
14
+ "analytics.subtitle" = "Monitor throughput, overdue work, and completion trends.";
15
+ "analytics.period_week" = "Week";
16
+ "analytics.period_month" = "Month";
17
+ "analytics.period_quarter" = "Quarter";
18
+ "analytics.completed_today" = "Completed today";
19
+ "analytics.open_tasks" = "Open tasks";
20
+ "analytics.overdue_tasks" = "Overdue";
21
+ "analytics.completion_rate" = "Completion rate";
22
+ "analytics.trend_subtitle" = "Completion trend";
23
+ "analytics.legend_completed" = "Completed";
24
+ "analytics.legend_created" = "Created";
25
+ "analytics.empty_trend" = "No trend data yet.";
26
+ "analytics.overdue_section" = "Overdue review";
27
+ "analytics.overdue_subtitle" = "Tasks that need attention first.";
28
+ "analytics.empty_overdue_body" = "Everything important is on track.";
29
+ "task_detail.status" = "Status";
30
+ "task_detail.priority" = "Priority";
31
+ "task_detail.notes" = "Notes";
32
+ "task_detail.due_date" = "Due date";
33
+ "task_detail.no_due_date" = "No deadline";
34
+ "task_detail.created" = "Created";
35
+ "task_detail.updated" = "Updated";
36
+ "task_detail.edit" = "Edit task";
37
+ "task_detail.toggle_status" = "Toggle status";
38
+ "task_detail.more_info" = "More info";
39
+ "task_detail.delete" = "Delete task";
40
+ "task_detail.delete_title" = "Delete this task?";
41
+ "task_detail.delete_message" = "This action cannot be undone.";
42
+ "task_detail.updated_feedback" = "Task updated";
43
+ "task_detail.deleted_feedback" = "Task deleted";
44
+ "settings.title" = "Preferences";
45
+ "settings.subtitle" = "Adjust language and theme for every platform target.";
46
+ "settings.language" = "Language";
47
+ "settings.language_en" = "English";
48
+ "settings.language_ru" = "Russian";
49
+ "settings.theme" = "Theme";
50
+ "settings.theme_light" = "Light";
51
+ "settings.theme_dark" = "Dark";
52
+ "settings.reminders" = "Due date reminders";
53
+ "settings.daily_summary" = "Daily summary";
54
+ "settings.automation_title" = "Automation";
55
+ "settings.automation_subtitle" = "Create recurring task rules to stress conditional forms and validation.";
56
+ "settings.automation_create_rule" = "Create recurring rule";
57
+ "settings.save" = "Save changes";
58
+ "settings.saved" = "Preferences updated";
59
+ "create_task.title" = "New task";
60
+ "create_task.field_title" = "Title";
61
+ "create_task.field_notes" = "Notes";
62
+ "create_task.field_priority" = "Priority";
63
+ "create_task.field_due_date" = "Due date";
64
+ "create_task.success" = "Task created";
65
+ "edit_task.title" = "Edit task";
66
+ "edit_task.field_title" = "Title";
67
+ "edit_task.field_notes" = "Notes";
68
+ "edit_task.field_priority" = "Priority";
69
+ "edit_task.field_due_date" = "Due date";
70
+ "edit_task.success" = "Task saved";
71
+ "recurring_rule.title" = "Recurring rule";
72
+ "recurring_rule.field_name" = "Rule name";
73
+ "recurring_rule.field_confirm_name" = "Confirm rule name";
74
+ "recurring_rule.field_cadence" = "Cadence";
75
+ "recurring_rule.cadence_daily" = "Daily";
76
+ "recurring_rule.cadence_weekly" = "Weekly";
77
+ "recurring_rule.cadence_monthly" = "Monthly";
78
+ "recurring_rule.field_interval" = "Repeat every";
79
+ "recurring_rule.field_weekday" = "Weekday";
80
+ "recurring_rule.field_month_day" = "Day of month";
81
+ "recurring_rule.field_start_date" = "Start date";
82
+ "recurring_rule.field_has_end_date" = "Set an end date";
83
+ "recurring_rule.field_end_date" = "End date";
84
+ "recurring_rule.field_remind_at" = "Reminder time";
85
+ "recurring_rule.field_enable_summary" = "Attach daily summary delivery";
86
+ "recurring_rule.field_summary_channel" = "Summary channel";
87
+ "recurring_rule.summary_push" = "Push notification";
88
+ "recurring_rule.summary_email" = "Email";
89
+ "recurring_rule.success" = "Recurring rule created";
90
+ "recurring_preview.title" = "Upcoming schedule preview";
91
+ "recurring_preview.invalid" = "Complete the cadence and date fields to preview the schedule.";
92
+ "recurring_preview.empty" = "No upcoming dates can be generated from this rule.";
93
+ "priority.low" = "Low";
94
+ "priority.medium" = "Medium";
95
+ "priority.high" = "High";
96
+ "status.open" = "Open";
97
+ "status.done" = "Done";
98
+ "weekday.mon" = "Monday";
99
+ "weekday.tue" = "Tuesday";
100
+ "weekday.wed" = "Wednesday";
101
+ "weekday.thu" = "Thursday";
102
+ "weekday.fri" = "Friday";
103
+ "weekday.sat" = "Saturday";
104
+ "weekday.sun" = "Sunday";
105
+ "validation.min_length" = "Must be at least %d characters";
106
+ "validation.min_value" = "Must be at least %d";
107
+ "validation.max_value" = "Must be no more than %d";
108
+ "validation.rule_name_min_length" = "Rule name must be at least %d characters";
109
+ "validation.rule_name_reserved" = "The default name is reserved. Choose a more specific label.";
110
+ "validation.rule_name_taken" = "A recurring rule with this name already exists.";
111
+ "validation.match_field" = "Fields do not match";
112
+ "validation.end_date_after_start" = "End date must be the same as or later than the start date.";
113
+ "validation.time_format" = "Use a 24-hour time like 09:00";
114
+ "validation.month_day_max" = "Choose a day between 1 and 28";
115
+ "validation.fix_errors" = "Fix the highlighted fields before saving.";
116
+ "validation.required" = "This field is required.";
117
+ "common.cancel" = "Cancel";
118
+ "common.delete" = "Delete";
@@ -0,0 +1,118 @@
1
+ "nav.tasks" = "Задачи";
2
+ "nav.analytics" = "Аналитика";
3
+ "nav.settings" = "Настройки";
4
+ "home.title" = "Сегодня все под контролем";
5
+ "home.summary.done" = "Все задачи закрыты";
6
+ "home.summary.remaining" = "Осталось %d из %d";
7
+ "home.search_placeholder" = "Искать по названию или заметкам";
8
+ "home.filter_all" = "Все";
9
+ "home.filter_open" = "Открытые";
10
+ "home.filter_done" = "Выполненные";
11
+ "home.new_task" = "Новая задача";
12
+ "home.empty_title" = "Список пуст";
13
+ "analytics.title" = "Аналитика задач";
14
+ "analytics.subtitle" = "Следите за выполнением, просроченными задачами и динамикой.";
15
+ "analytics.period_week" = "Неделя";
16
+ "analytics.period_month" = "Месяц";
17
+ "analytics.period_quarter" = "Квартал";
18
+ "analytics.completed_today" = "Выполнено сегодня";
19
+ "analytics.open_tasks" = "Открытые задачи";
20
+ "analytics.overdue_tasks" = "Просрочено";
21
+ "analytics.completion_rate" = "Процент выполнения";
22
+ "analytics.trend_subtitle" = "Динамика выполнения";
23
+ "analytics.legend_completed" = "Выполнено";
24
+ "analytics.legend_created" = "Создано";
25
+ "analytics.empty_trend" = "Данные тренда пока отсутствуют.";
26
+ "analytics.overdue_section" = "Просроченные задачи";
27
+ "analytics.overdue_subtitle" = "Задачи, которым нужно уделить внимание в первую очередь.";
28
+ "analytics.empty_overdue_body" = "Все важные задачи идут по плану.";
29
+ "task_detail.status" = "Статус";
30
+ "task_detail.priority" = "Приоритет";
31
+ "task_detail.notes" = "Заметки";
32
+ "task_detail.due_date" = "Срок";
33
+ "task_detail.no_due_date" = "Без срока";
34
+ "task_detail.created" = "Создано";
35
+ "task_detail.updated" = "Обновлено";
36
+ "task_detail.edit" = "Редактировать задачу";
37
+ "task_detail.toggle_status" = "Сменить статус";
38
+ "task_detail.more_info" = "Подробнее";
39
+ "task_detail.delete" = "Удалить задачу";
40
+ "task_detail.delete_title" = "Удалить эту задачу?";
41
+ "task_detail.delete_message" = "Это действие нельзя отменить.";
42
+ "task_detail.updated_feedback" = "Задача обновлена";
43
+ "task_detail.deleted_feedback" = "Задача удалена";
44
+ "settings.title" = "Параметры";
45
+ "settings.subtitle" = "Измените язык и тему для всех целевых платформ.";
46
+ "settings.language" = "Язык";
47
+ "settings.language_en" = "Английский";
48
+ "settings.language_ru" = "Русский";
49
+ "settings.theme" = "Тема";
50
+ "settings.theme_light" = "Светлая";
51
+ "settings.theme_dark" = "Тёмная";
52
+ "settings.reminders" = "Напоминания о сроках";
53
+ "settings.daily_summary" = "Ежедневная сводка";
54
+ "settings.automation_title" = "Автоматизация";
55
+ "settings.automation_subtitle" = "Создавайте повторяющиеся правила задач, чтобы проверить условные формы и валидацию.";
56
+ "settings.automation_create_rule" = "Создать правило";
57
+ "settings.save" = "Сохранить";
58
+ "settings.saved" = "Параметры обновлены";
59
+ "create_task.title" = "Новая задача";
60
+ "create_task.field_title" = "Название";
61
+ "create_task.field_notes" = "Заметки";
62
+ "create_task.field_priority" = "Приоритет";
63
+ "create_task.field_due_date" = "Срок";
64
+ "create_task.success" = "Задача создана";
65
+ "edit_task.title" = "Редактировать задачу";
66
+ "edit_task.field_title" = "Название";
67
+ "edit_task.field_notes" = "Заметки";
68
+ "edit_task.field_priority" = "Приоритет";
69
+ "edit_task.field_due_date" = "Срок";
70
+ "edit_task.success" = "Задача сохранена";
71
+ "recurring_rule.title" = "Повторяющееся правило";
72
+ "recurring_rule.field_name" = "Название правила";
73
+ "recurring_rule.field_confirm_name" = "Подтвердите название";
74
+ "recurring_rule.field_cadence" = "Периодичность";
75
+ "recurring_rule.cadence_daily" = "Ежедневно";
76
+ "recurring_rule.cadence_weekly" = "Еженедельно";
77
+ "recurring_rule.cadence_monthly" = "Ежемесячно";
78
+ "recurring_rule.field_interval" = "Повторять каждые";
79
+ "recurring_rule.field_weekday" = "День недели";
80
+ "recurring_rule.field_month_day" = "День месяца";
81
+ "recurring_rule.field_start_date" = "Дата начала";
82
+ "recurring_rule.field_has_end_date" = "Указать дату окончания";
83
+ "recurring_rule.field_end_date" = "Дата окончания";
84
+ "recurring_rule.field_remind_at" = "Время напоминания";
85
+ "recurring_rule.field_enable_summary" = "Добавить ежедневную сводку";
86
+ "recurring_rule.field_summary_channel" = "Канал сводки";
87
+ "recurring_rule.summary_push" = "Push-уведомление";
88
+ "recurring_rule.summary_email" = "Электронная почта";
89
+ "recurring_rule.success" = "Повторяющееся правило создано";
90
+ "recurring_preview.title" = "Предпросмотр расписания";
91
+ "recurring_preview.invalid" = "Заполните периодичность и даты, чтобы увидеть предпросмотр расписания.";
92
+ "recurring_preview.empty" = "Для этого правила не удаётся сформировать будущие даты.";
93
+ "priority.low" = "Низкий";
94
+ "priority.medium" = "Средний";
95
+ "priority.high" = "Высокий";
96
+ "status.open" = "Открыта";
97
+ "status.done" = "Выполнена";
98
+ "weekday.mon" = "Понедельник";
99
+ "weekday.tue" = "Вторник";
100
+ "weekday.wed" = "Среда";
101
+ "weekday.thu" = "Четверг";
102
+ "weekday.fri" = "Пятница";
103
+ "weekday.sat" = "Суббота";
104
+ "weekday.sun" = "Воскресенье";
105
+ "validation.min_length" = "Минимум %d символа(ов)";
106
+ "validation.min_value" = "Значение должно быть не меньше %d";
107
+ "validation.max_value" = "Значение должно быть не больше %d";
108
+ "validation.rule_name_min_length" = "Название правила должно содержать минимум %d символа(ов)";
109
+ "validation.rule_name_reserved" = "Название по умолчанию зарезервировано. Укажите более точную метку.";
110
+ "validation.rule_name_taken" = "Правило с таким названием уже существует.";
111
+ "validation.match_field" = "Поля не совпадают";
112
+ "validation.end_date_after_start" = "Дата окончания должна быть не раньше даты начала.";
113
+ "validation.time_format" = "Используйте 24-часовой формат, например 09:00";
114
+ "validation.month_day_max" = "Выберите день от 1 до 28";
115
+ "validation.fix_errors" = "Исправьте выделенные поля перед сохранением.";
116
+ "validation.required" = "Это поле обязательно.";
117
+ "common.cancel" = "Отмена";
118
+ "common.delete" = "Удалить";
@@ -0,0 +1,50 @@
1
+ import SwiftUI
2
+
3
+ @main
4
+ struct TodoOrbitApp: App {
5
+ @StateObject private var model = AppModel()
6
+
7
+ var body: some Scene {
8
+ WindowGroup {
9
+ RootView(model: model)
10
+ .preferredColorScheme(model.preferences.theme.colorScheme)
11
+ .environment(\.locale, model.locale)
12
+ .overlay(alignment: .bottom) {
13
+ if let toast = model.toast {
14
+ ToastOverlay(toast: toast)
15
+ .transition(.move(edge: .bottom).combined(with: .opacity))
16
+ }
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ private struct RootView: View {
23
+ @ObservedObject var model: AppModel
24
+
25
+ var body: some View {
26
+ TabView {
27
+ NavigationStack {
28
+ TasksHomeView(model: model)
29
+ }
30
+ .tabItem {
31
+ Label(model.string("nav.tasks"), systemImage: "checklist")
32
+ }
33
+
34
+ NavigationStack {
35
+ AnalyticsView(model: model)
36
+ }
37
+ .tabItem {
38
+ Label(model.string("nav.analytics"), systemImage: "chart.line.uptrend.xyaxis")
39
+ }
40
+
41
+ NavigationStack {
42
+ SettingsView(model: model)
43
+ }
44
+ .tabItem {
45
+ Label(model.string("nav.settings"), systemImage: "gear")
46
+ }
47
+ }
48
+ .tint(.teal)
49
+ }
50
+ }
@@ -0,0 +1,204 @@
1
+ import SwiftUI
2
+
3
+ struct CutCornerShape: Shape {
4
+ var cut: CGFloat = 18
5
+
6
+ func path(in rect: CGRect) -> Path {
7
+ var path = Path()
8
+ path.move(to: CGPoint(x: cut, y: rect.minY))
9
+ path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
10
+ path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cut))
11
+ path.addLine(to: CGPoint(x: rect.maxX - cut, y: rect.maxY))
12
+ path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
13
+ path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cut))
14
+ path.closeSubpath()
15
+ return path
16
+ }
17
+ }
18
+
19
+ struct OrbitSurfaceModifier: ViewModifier {
20
+ let cut: CGFloat
21
+ let fill: Color
22
+ let stroke: Color
23
+ let lineWidth: CGFloat
24
+ let contentPadding: CGFloat
25
+
26
+ func body(content: Content) -> some View {
27
+ content
28
+ .padding(contentPadding)
29
+ .background(
30
+ CutCornerShape(cut: cut)
31
+ .fill(fill)
32
+ )
33
+ .overlay(
34
+ CutCornerShape(cut: cut)
35
+ .stroke(stroke, lineWidth: lineWidth)
36
+ )
37
+ }
38
+ }
39
+
40
+ extension View {
41
+ func orbitSurface(
42
+ cut: CGFloat = 16,
43
+ fill: Color = Color(uiColor: .secondarySystemBackground),
44
+ stroke: Color = Color.teal.opacity(0.24),
45
+ lineWidth: CGFloat = 1,
46
+ contentPadding: CGFloat = 20
47
+ ) -> some View {
48
+ modifier(
49
+ OrbitSurfaceModifier(
50
+ cut: cut,
51
+ fill: fill,
52
+ stroke: stroke,
53
+ lineWidth: lineWidth,
54
+ contentPadding: contentPadding
55
+ )
56
+ )
57
+ }
58
+
59
+ func orbitCard(
60
+ fill: Color = Color(uiColor: .secondarySystemBackground),
61
+ stroke: Color = Color.teal.opacity(0.24),
62
+ lineWidth: CGFloat = 1
63
+ ) -> some View {
64
+ orbitSurface(fill: fill, stroke: stroke, lineWidth: lineWidth)
65
+ }
66
+
67
+ func orbitInputShell(
68
+ cut: CGFloat = 14,
69
+ fill: Color = Color(uiColor: .secondarySystemBackground),
70
+ stroke: Color = Color.teal.opacity(0.22),
71
+ lineWidth: CGFloat = 1.5,
72
+ contentPadding: CGFloat = 14
73
+ ) -> some View {
74
+ orbitSurface(
75
+ cut: cut,
76
+ fill: fill,
77
+ stroke: stroke,
78
+ lineWidth: lineWidth,
79
+ contentPadding: contentPadding
80
+ )
81
+ }
82
+ }
83
+
84
+ struct OrbitPrimaryButtonStyle: ButtonStyle {
85
+ func makeBody(configuration: Configuration) -> some View {
86
+ configuration.label
87
+ .font(.headline.weight(.semibold))
88
+ .foregroundStyle(.white)
89
+ .padding(.horizontal, 18)
90
+ .padding(.vertical, 14)
91
+ .background(
92
+ CutCornerShape(cut: 14)
93
+ .fill(
94
+ LinearGradient(
95
+ colors: [Color.teal, Color(red: 0.07, green: 0.52, blue: 0.48)],
96
+ startPoint: .topLeading,
97
+ endPoint: .bottomTrailing
98
+ )
99
+ )
100
+ )
101
+ .scaleEffect(configuration.isPressed ? 0.98 : 1)
102
+ }
103
+ }
104
+
105
+ struct OrbitGhostButtonStyle: ButtonStyle {
106
+ func makeBody(configuration: Configuration) -> some View {
107
+ configuration.label
108
+ .font(.headline.weight(.semibold))
109
+ .foregroundStyle(Color.primary)
110
+ .padding(.horizontal, 16)
111
+ .padding(.vertical, 12)
112
+ .background(
113
+ CutCornerShape(cut: 14)
114
+ .fill(Color(uiColor: .secondarySystemBackground))
115
+ )
116
+ .overlay(
117
+ CutCornerShape(cut: 14)
118
+ .stroke(Color.teal.opacity(configuration.isPressed ? 0.34 : 0.18), lineWidth: 1.5)
119
+ )
120
+ .scaleEffect(configuration.isPressed ? 0.99 : 1)
121
+ }
122
+ }
123
+
124
+ struct OrbitChipButtonStyle: ButtonStyle {
125
+ let selected: Bool
126
+
127
+ func makeBody(configuration: Configuration) -> some View {
128
+ configuration.label
129
+ .font(.subheadline.weight(.semibold))
130
+ .foregroundStyle(selected ? Color.teal : Color.primary)
131
+ .padding(.horizontal, 16)
132
+ .padding(.vertical, 10)
133
+ .background(
134
+ CutCornerShape(cut: 12)
135
+ .fill(selected ? Color.teal.opacity(0.12) : Color(uiColor: .secondarySystemBackground))
136
+ )
137
+ .overlay(
138
+ CutCornerShape(cut: 12)
139
+ .stroke(
140
+ selected ? Color.teal.opacity(0.34) : Color(uiColor: .separator).opacity(0.45),
141
+ lineWidth: selected ? 1.5 : 1
142
+ )
143
+ )
144
+ .scaleEffect(configuration.isPressed ? 0.98 : 1)
145
+ }
146
+ }
147
+
148
+ struct OrbitFloatingActionButtonStyle: ButtonStyle {
149
+ func makeBody(configuration: Configuration) -> some View {
150
+ configuration.label
151
+ .font(.headline.weight(.semibold))
152
+ .foregroundStyle(.white)
153
+ .padding(.horizontal, 18)
154
+ .padding(.vertical, 14)
155
+ .background(
156
+ CutCornerShape(cut: 16)
157
+ .fill(
158
+ LinearGradient(
159
+ colors: [Color.teal, Color(red: 0.07, green: 0.52, blue: 0.48)],
160
+ startPoint: .topLeading,
161
+ endPoint: .bottomTrailing
162
+ )
163
+ )
164
+ )
165
+ .shadow(color: .black.opacity(configuration.isPressed ? 0.12 : 0.18), radius: 18, y: 8)
166
+ .scaleEffect(configuration.isPressed ? 0.98 : 1)
167
+ }
168
+ }
169
+
170
+ struct PriorityDot: View {
171
+ let priority: TaskPriority
172
+
173
+ var body: some View {
174
+ Circle()
175
+ .fill(priority.tint)
176
+ .frame(width: 12, height: 12)
177
+ }
178
+ }
179
+
180
+ struct ToastOverlay: View {
181
+ let toast: ToastMessage
182
+
183
+ var tint: Color {
184
+ switch toast.level {
185
+ case .success: .green
186
+ case .warning: .orange
187
+ case .error: .red
188
+ }
189
+ }
190
+
191
+ var body: some View {
192
+ Text(toast.text)
193
+ .font(.subheadline.weight(.semibold))
194
+ .foregroundStyle(.white)
195
+ .padding(.horizontal, 18)
196
+ .padding(.vertical, 14)
197
+ .background(
198
+ CutCornerShape(cut: 14)
199
+ .fill(tint)
200
+ )
201
+ .shadow(color: .black.opacity(0.18), radius: 22, y: 10)
202
+ .padding(.bottom, 18)
203
+ }
204
+ }