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.
- package/README.md +3 -1
- package/check/index.ts +17 -0
- package/cli/index.ts +21 -3
- package/cli/init.ts +224 -10
- package/docs/cli.md +13 -8
- package/docs/file-formats.md +36 -0
- package/docs/implementation-notes.md +7 -0
- package/drift/index.ts +281 -40
- package/mcp-server/index.ts +179 -119
- package/mcp-server/screenshot.ts +19 -4
- package/package.json +5 -2
- package/prepare/index.ts +155 -18
- package/schema/openuispec.schema.json +59 -0
- package/schema/semantic-lint.ts +25 -1
- package/scripts/take-all-screenshots.ts +507 -0
- package/spec/openuispec-v0.1.md +13 -0
- package/status/index.ts +72 -2
- package/examples/social-app/.mcp.json +0 -10
- package/examples/social-app/AGENTS.md +0 -124
- package/examples/social-app/CLAUDE.md +0 -124
- package/examples/social-app/backend/.gitkeep +0 -1
- package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
- package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
- package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
- package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
- package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
- package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
- package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
- package/examples/social-app/generated/android/social-app/gradlew +0 -248
- package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
- package/examples/social-app/generated/web/social-app/index.html +0 -12
- package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
- package/examples/social-app/generated/web/social-app/package.json +0 -27
- package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
- package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
- package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
- package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
- package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
- package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
- package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
- package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
- package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
- package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
- package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
- package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
- package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
- package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
- package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
- package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
- package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
- package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
- package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
- package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
- package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
- package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
- package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
- package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
- package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
- package/examples/social-app/package.json +0 -13
- package/examples/social-app/take-web-screenshots.ts +0 -97
- package/examples/taskflow/.codex/config.toml +0 -4
- package/examples/taskflow/.mcp.json +0 -10
- package/examples/taskflow/AGENTS.md +0 -124
- package/examples/taskflow/CLAUDE.md +0 -124
- package/examples/taskflow/backend/.gitkeep +0 -1
- package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
- package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
- package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
- package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
- package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
- package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
- package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
- package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
- package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
- package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
- package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
- package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
- package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
- package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
- package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
- package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
- package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
- package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
- package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
- package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
- package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
- package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
- package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
- package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
- package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
- package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
- package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
- package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
- package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
- package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
- package/examples/todo-orbit/.codex/config.toml +0 -4
- package/examples/todo-orbit/.mcp.json +0 -10
- package/examples/todo-orbit/AGENTS.md +0 -124
- package/examples/todo-orbit/CLAUDE.md +0 -124
- package/examples/todo-orbit/backend/.gitkeep +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
- package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
- package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
- package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
- package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
- package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
- package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
- package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
- package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
- package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
- package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
- package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
- package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
- package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
- package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
|
@@ -1,340 +0,0 @@
|
|
|
1
|
-
import { type ButtonHTMLAttributes, type PropsWithChildren, type ReactNode } from "react";
|
|
2
|
-
import { Link } from "react-router";
|
|
3
|
-
import { Icon } from "../lib/icons";
|
|
4
|
-
import { cn, getInitials } from "../lib/utils";
|
|
5
|
-
|
|
6
|
-
export function ScreenScaffold({
|
|
7
|
-
title,
|
|
8
|
-
subtitle,
|
|
9
|
-
children,
|
|
10
|
-
}: PropsWithChildren<{ title: string; subtitle?: string }>) {
|
|
11
|
-
return (
|
|
12
|
-
<section className="mx-auto flex w-full max-w-[860px] flex-col gap-6 px-4 pb-28 pt-4 md:px-6 xl:px-8">
|
|
13
|
-
<header className="space-y-2">
|
|
14
|
-
<p className="text-xs uppercase tracking-[0.28em] text-[var(--color-text-tertiary)]">social-app</p>
|
|
15
|
-
<h1 className="text-[clamp(1.5rem,3vw,2rem)] font-semibold leading-[1.2] text-[var(--color-text-primary)]">
|
|
16
|
-
{title}
|
|
17
|
-
</h1>
|
|
18
|
-
{subtitle ? <p className="max-w-2xl text-sm text-[var(--color-text-secondary)]">{subtitle}</p> : null}
|
|
19
|
-
</header>
|
|
20
|
-
{children}
|
|
21
|
-
</section>
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function SectionTitle({ children, action }: { children: ReactNode; action?: ReactNode }) {
|
|
26
|
-
return (
|
|
27
|
-
<div className="flex items-center justify-between gap-4">
|
|
28
|
-
<h2 className="text-lg font-semibold text-[var(--color-text-primary)]">{children}</h2>
|
|
29
|
-
{action}
|
|
30
|
-
</div>
|
|
31
|
-
);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function Surface({
|
|
35
|
-
children,
|
|
36
|
-
className,
|
|
37
|
-
}: PropsWithChildren<{
|
|
38
|
-
className?: string;
|
|
39
|
-
}>) {
|
|
40
|
-
return <div className={cn("rounded-surface border border-[var(--color-border-default)] bg-[var(--color-surface-secondary)] shadow-sm", className)}>{children}</div>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function ActionGroup({
|
|
44
|
-
children,
|
|
45
|
-
className,
|
|
46
|
-
}: PropsWithChildren<{
|
|
47
|
-
className?: string;
|
|
48
|
-
}>) {
|
|
49
|
-
return <div className={cn("flex flex-col items-start gap-3", className)}>{children}</div>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
type ActionButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
53
|
-
variant?: "primary" | "secondary" | "chip" | "destructive" | "fab";
|
|
54
|
-
icon?: Parameters<typeof Icon>[0]["name"];
|
|
55
|
-
fullWidth?: boolean;
|
|
56
|
-
selected?: boolean;
|
|
57
|
-
trailing?: ReactNode;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export function ActionButton({
|
|
61
|
-
children,
|
|
62
|
-
className,
|
|
63
|
-
variant = "primary",
|
|
64
|
-
icon,
|
|
65
|
-
fullWidth,
|
|
66
|
-
selected,
|
|
67
|
-
trailing,
|
|
68
|
-
...props
|
|
69
|
-
}: ActionButtonProps) {
|
|
70
|
-
const variantClass =
|
|
71
|
-
variant === "primary"
|
|
72
|
-
? "rounded-cap-primary border-transparent bg-[var(--color-brand-primary)] text-[var(--color-brand-primary-on)]"
|
|
73
|
-
: variant === "secondary"
|
|
74
|
-
? "rounded-cap-alternate border-[var(--color-border-strong)] bg-transparent text-[var(--color-text-primary)]"
|
|
75
|
-
: variant === "destructive"
|
|
76
|
-
? "rounded-cap-primary border-transparent bg-[var(--color-semantic-danger)] text-[var(--color-semantic-danger-on)]"
|
|
77
|
-
: variant === "fab"
|
|
78
|
-
? "rounded-cap-primary border-transparent bg-[var(--color-brand-primary)] text-[var(--color-brand-primary-on)] shadow-md"
|
|
79
|
-
: selected
|
|
80
|
-
? "rounded-cap-primary border-transparent bg-[var(--color-brand-accent)] text-[var(--color-brand-accent-on)]"
|
|
81
|
-
: "rounded-cap-primary border-[var(--color-border-default)] bg-transparent text-[var(--color-text-primary)]";
|
|
82
|
-
|
|
83
|
-
return (
|
|
84
|
-
<button
|
|
85
|
-
className={cn(
|
|
86
|
-
"interactive-press inline-flex min-h-11 shrink-0 items-center justify-center gap-2 whitespace-nowrap align-middle border px-4 py-3 text-sm font-semibold transition",
|
|
87
|
-
variant === "fab" && "h-14 w-14 gap-0 px-0 py-0",
|
|
88
|
-
fullWidth && "w-full",
|
|
89
|
-
variantClass,
|
|
90
|
-
className,
|
|
91
|
-
)}
|
|
92
|
-
{...props}
|
|
93
|
-
>
|
|
94
|
-
{icon ? <Icon name={icon} className="h-5 w-5" /> : null}
|
|
95
|
-
{variant === "fab" ? (children ? <span className="sr-only">{children}</span> : null) : <span>{children}</span>}
|
|
96
|
-
{trailing}
|
|
97
|
-
</button>
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
type TextFieldProps = {
|
|
102
|
-
label: string;
|
|
103
|
-
value: string;
|
|
104
|
-
multiline?: boolean;
|
|
105
|
-
onValueChange?: (value: string) => void;
|
|
106
|
-
trailingAction?: ReactNode;
|
|
107
|
-
placeholder?: string;
|
|
108
|
-
maxLength?: number;
|
|
109
|
-
type?: string;
|
|
110
|
-
className?: string;
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
export function TextField({
|
|
114
|
-
label,
|
|
115
|
-
value,
|
|
116
|
-
multiline,
|
|
117
|
-
className,
|
|
118
|
-
onValueChange,
|
|
119
|
-
trailingAction,
|
|
120
|
-
placeholder,
|
|
121
|
-
maxLength,
|
|
122
|
-
type,
|
|
123
|
-
}: TextFieldProps) {
|
|
124
|
-
const sharedClassName =
|
|
125
|
-
"min-h-12 w-full rounded-cap-primary border border-[var(--color-border-default)] bg-[var(--color-surface-primary)] px-4 py-3 text-sm text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] outline-none transition focus:border-[var(--color-brand-primary)] focus:border-2";
|
|
126
|
-
|
|
127
|
-
return (
|
|
128
|
-
<label className="flex w-full flex-col gap-2">
|
|
129
|
-
<span className="text-sm font-medium text-[var(--color-text-secondary)]">{label}</span>
|
|
130
|
-
<div className="flex items-end gap-2">
|
|
131
|
-
{multiline ? (
|
|
132
|
-
<textarea
|
|
133
|
-
className={cn(sharedClassName, "min-h-28 resize-y", className)}
|
|
134
|
-
value={String(value ?? "")}
|
|
135
|
-
onChange={(event) => onValueChange?.(event.target.value)}
|
|
136
|
-
placeholder={placeholder}
|
|
137
|
-
maxLength={maxLength}
|
|
138
|
-
/>
|
|
139
|
-
) : (
|
|
140
|
-
<input
|
|
141
|
-
className={cn(sharedClassName, className)}
|
|
142
|
-
value={String(value ?? "")}
|
|
143
|
-
onChange={(event) => onValueChange?.(event.target.value)}
|
|
144
|
-
placeholder={placeholder}
|
|
145
|
-
maxLength={maxLength}
|
|
146
|
-
type={type}
|
|
147
|
-
/>
|
|
148
|
-
)}
|
|
149
|
-
{trailingAction}
|
|
150
|
-
</div>
|
|
151
|
-
</label>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
export function SelectField({
|
|
156
|
-
label,
|
|
157
|
-
value,
|
|
158
|
-
options,
|
|
159
|
-
onValueChange,
|
|
160
|
-
}: {
|
|
161
|
-
label: string;
|
|
162
|
-
value: string;
|
|
163
|
-
options: Array<{ value: string; label: string }>;
|
|
164
|
-
onValueChange: (value: string) => void;
|
|
165
|
-
}) {
|
|
166
|
-
return (
|
|
167
|
-
<label className="flex w-full flex-col gap-2">
|
|
168
|
-
<span className="text-sm font-medium text-[var(--color-text-secondary)]">{label}</span>
|
|
169
|
-
<div className="relative">
|
|
170
|
-
<select
|
|
171
|
-
value={value}
|
|
172
|
-
onChange={(event) => onValueChange(event.target.value)}
|
|
173
|
-
className="min-h-12 w-full appearance-none rounded-cap-primary border border-[var(--color-border-default)] bg-[var(--color-surface-primary)] px-4 py-3 text-sm text-[var(--color-text-primary)] outline-none transition focus:border-2 focus:border-[var(--color-brand-primary)]"
|
|
174
|
-
>
|
|
175
|
-
{options.map((option) => (
|
|
176
|
-
<option key={option.value} value={option.value}>
|
|
177
|
-
{option.label}
|
|
178
|
-
</option>
|
|
179
|
-
))}
|
|
180
|
-
</select>
|
|
181
|
-
<Icon name="more" className="pointer-events-none absolute right-3 top-3.5 h-5 w-5 rotate-90 text-[var(--color-text-tertiary)]" />
|
|
182
|
-
</div>
|
|
183
|
-
</label>
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export function ToggleField({
|
|
188
|
-
label,
|
|
189
|
-
checked,
|
|
190
|
-
onChange,
|
|
191
|
-
}: {
|
|
192
|
-
label: string;
|
|
193
|
-
checked: boolean;
|
|
194
|
-
onChange: (value: boolean) => void;
|
|
195
|
-
}) {
|
|
196
|
-
return (
|
|
197
|
-
<div className="flex items-center justify-between gap-4 rounded-card border border-[var(--color-border-default)] bg-[var(--color-surface-primary)] px-4 py-3">
|
|
198
|
-
<span className="text-sm font-medium text-[var(--color-text-primary)]">{label}</span>
|
|
199
|
-
<input
|
|
200
|
-
type="checkbox"
|
|
201
|
-
role="switch"
|
|
202
|
-
checked={checked}
|
|
203
|
-
onChange={(event) => onChange(event.target.checked)}
|
|
204
|
-
className="h-5 w-10 accent-[var(--color-brand-primary)]"
|
|
205
|
-
/>
|
|
206
|
-
</div>
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function Avatar({
|
|
211
|
-
src,
|
|
212
|
-
name,
|
|
213
|
-
size = "md",
|
|
214
|
-
}: {
|
|
215
|
-
src?: string;
|
|
216
|
-
name: string;
|
|
217
|
-
size?: "sm" | "md" | "lg";
|
|
218
|
-
}) {
|
|
219
|
-
const sizeClass =
|
|
220
|
-
size === "sm"
|
|
221
|
-
? "h-10 w-10 text-xs"
|
|
222
|
-
: size === "lg"
|
|
223
|
-
? "h-[4.5rem] w-[4.5rem] text-lg"
|
|
224
|
-
: "h-12 w-12 text-sm";
|
|
225
|
-
|
|
226
|
-
return src ? (
|
|
227
|
-
<img
|
|
228
|
-
alt={name}
|
|
229
|
-
src={src}
|
|
230
|
-
className={cn("rounded-cap-primary object-cover", sizeClass)}
|
|
231
|
-
/>
|
|
232
|
-
) : (
|
|
233
|
-
<div className={cn("flex items-center justify-center rounded-cap-primary bg-[var(--color-surface-tertiary)] font-semibold text-[var(--color-text-secondary)]", sizeClass)}>
|
|
234
|
-
{getInitials(name)}
|
|
235
|
-
</div>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
export function EmptyState({
|
|
240
|
-
title,
|
|
241
|
-
description,
|
|
242
|
-
action,
|
|
243
|
-
}: {
|
|
244
|
-
title: string;
|
|
245
|
-
description: string;
|
|
246
|
-
action?: ReactNode;
|
|
247
|
-
}) {
|
|
248
|
-
return (
|
|
249
|
-
<Surface className="p-8 text-center">
|
|
250
|
-
<div className="mx-auto flex max-w-md flex-col items-center gap-3">
|
|
251
|
-
<div className="rounded-cap-primary bg-[var(--color-surface-tertiary)] px-4 py-2 text-xs font-semibold uppercase tracking-[0.28em] text-[var(--color-text-secondary)]">
|
|
252
|
-
Empty
|
|
253
|
-
</div>
|
|
254
|
-
<h3 className="text-lg font-semibold text-[var(--color-text-primary)]">{title}</h3>
|
|
255
|
-
<p className="text-sm text-[var(--color-text-secondary)]">{description}</p>
|
|
256
|
-
{action}
|
|
257
|
-
</div>
|
|
258
|
-
</Surface>
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
export function ErrorState({
|
|
263
|
-
title,
|
|
264
|
-
description,
|
|
265
|
-
}: {
|
|
266
|
-
title: string;
|
|
267
|
-
description: string;
|
|
268
|
-
}) {
|
|
269
|
-
return (
|
|
270
|
-
<div className="rounded-surface border border-[color:rgba(212,59,59,0.24)] bg-[color:rgba(212,59,59,0.08)] p-5 text-sm text-[var(--color-text-primary)]">
|
|
271
|
-
<h3 className="font-semibold">{title}</h3>
|
|
272
|
-
<p className="mt-1 text-[var(--color-text-secondary)]">{description}</p>
|
|
273
|
-
</div>
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
export function FeedbackBanner({
|
|
278
|
-
title,
|
|
279
|
-
description,
|
|
280
|
-
onDismiss,
|
|
281
|
-
}: {
|
|
282
|
-
title: string;
|
|
283
|
-
description: string;
|
|
284
|
-
onDismiss: () => void;
|
|
285
|
-
}) {
|
|
286
|
-
return (
|
|
287
|
-
<div className="flex items-start justify-between gap-4 border border-[var(--color-border-default)] bg-[var(--color-surface-tertiary)] px-4 py-4">
|
|
288
|
-
<div>
|
|
289
|
-
<p className="font-semibold text-[var(--color-text-primary)]">{title}</p>
|
|
290
|
-
<p className="mt-1 text-sm text-[var(--color-text-secondary)]">{description}</p>
|
|
291
|
-
</div>
|
|
292
|
-
<button
|
|
293
|
-
type="button"
|
|
294
|
-
onClick={onDismiss}
|
|
295
|
-
className="interactive-press rounded-full p-1.5 text-[var(--color-text-secondary)]"
|
|
296
|
-
aria-label="Dismiss banner"
|
|
297
|
-
>
|
|
298
|
-
<Icon name="close" className="h-4 w-4" />
|
|
299
|
-
</button>
|
|
300
|
-
</div>
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
export function SkeletonList({ count = 3, tall }: { count?: number; tall?: boolean }) {
|
|
305
|
-
return (
|
|
306
|
-
<div className="space-y-3">
|
|
307
|
-
{Array.from({ length: count }).map((_, index) => (
|
|
308
|
-
<div
|
|
309
|
-
key={index}
|
|
310
|
-
className={cn(
|
|
311
|
-
"skeleton rounded-card border border-[var(--color-border-default)] bg-[var(--color-surface-secondary)]",
|
|
312
|
-
tall ? "h-44" : "h-28",
|
|
313
|
-
)}
|
|
314
|
-
/>
|
|
315
|
-
))}
|
|
316
|
-
</div>
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function PillLink({
|
|
321
|
-
to,
|
|
322
|
-
children,
|
|
323
|
-
icon,
|
|
324
|
-
}: {
|
|
325
|
-
to: string;
|
|
326
|
-
children: ReactNode;
|
|
327
|
-
icon?: Parameters<typeof Icon>[0]["name"];
|
|
328
|
-
}) {
|
|
329
|
-
return (
|
|
330
|
-
<Link
|
|
331
|
-
to={to}
|
|
332
|
-
className="interactive-press rounded-cap-alternate border border-[var(--color-border-strong)] px-4 py-2 text-sm font-semibold text-[var(--color-text-primary)]"
|
|
333
|
-
>
|
|
334
|
-
<span className="inline-flex items-center gap-2">
|
|
335
|
-
{icon ? <Icon name={icon} className="h-4 w-4" /> : null}
|
|
336
|
-
{children}
|
|
337
|
-
</span>
|
|
338
|
-
</Link>
|
|
339
|
-
);
|
|
340
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { useNavigate } from "react-router";
|
|
3
|
-
import { useI18n } from "../i18n";
|
|
4
|
-
import { ActionButton, SelectField, TextField } from "../components/ui";
|
|
5
|
-
import { Icon } from "../lib/icons";
|
|
6
|
-
import { useSizeClass } from "../lib/utils";
|
|
7
|
-
import { useAppStore } from "../state/store";
|
|
8
|
-
|
|
9
|
-
export function CreatePostFlow() {
|
|
10
|
-
const { t } = useI18n();
|
|
11
|
-
const navigate = useNavigate();
|
|
12
|
-
const sizeClass = useSizeClass();
|
|
13
|
-
const state = useAppStore();
|
|
14
|
-
const [body, setBody] = useState("");
|
|
15
|
-
const [media, setMedia] = useState("");
|
|
16
|
-
const [audience, setAudience] = useState("public");
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<div className="fixed inset-0 z-40 bg-[rgba(28,27,26,0.32)] px-3 pb-3 pt-20 md:px-6">
|
|
20
|
-
<div
|
|
21
|
-
className={`mx-auto max-w-2xl border border-[var(--color-border-default)] bg-[var(--color-surface-primary)] shadow-lg ${
|
|
22
|
-
sizeClass === "compact" ? "fixed inset-x-0 bottom-0 rounded-t-[24px] p-5" : "rounded-surface p-6"
|
|
23
|
-
}`}
|
|
24
|
-
>
|
|
25
|
-
{sizeClass === "compact" ? <div className="mx-auto mb-4 h-1.5 w-16 rounded-full bg-[var(--color-border-strong)]" /> : null}
|
|
26
|
-
<div className="mb-6 flex items-center justify-between gap-4">
|
|
27
|
-
<div>
|
|
28
|
-
<p className="text-xs uppercase tracking-[0.28em] text-[var(--color-text-tertiary)]">{t("nav.create")}</p>
|
|
29
|
-
<h2 className="mt-2 text-2xl font-semibold text-[var(--color-text-primary)]">Compose</h2>
|
|
30
|
-
</div>
|
|
31
|
-
<button
|
|
32
|
-
type="button"
|
|
33
|
-
onClick={() => navigate(-1)}
|
|
34
|
-
className="interactive-press rounded-cap-primary border border-[var(--color-border-default)] bg-[var(--color-surface-secondary)] p-2"
|
|
35
|
-
aria-label="Close"
|
|
36
|
-
>
|
|
37
|
-
<Icon name="close" className="h-5 w-5" />
|
|
38
|
-
</button>
|
|
39
|
-
</div>
|
|
40
|
-
|
|
41
|
-
<div className="space-y-4">
|
|
42
|
-
<TextField
|
|
43
|
-
label={t("create_post.body_placeholder")}
|
|
44
|
-
value={body}
|
|
45
|
-
multiline
|
|
46
|
-
onValueChange={setBody}
|
|
47
|
-
placeholder={t("create_post.body_placeholder")}
|
|
48
|
-
maxLength={4000}
|
|
49
|
-
/>
|
|
50
|
-
<SelectField
|
|
51
|
-
label={t("create_post.audience")}
|
|
52
|
-
value={audience}
|
|
53
|
-
options={[
|
|
54
|
-
{ value: "public", label: t("create_post.audience_public") },
|
|
55
|
-
{ value: "followers", label: t("create_post.audience_followers") },
|
|
56
|
-
]}
|
|
57
|
-
onValueChange={setAudience}
|
|
58
|
-
/>
|
|
59
|
-
<TextField
|
|
60
|
-
label={t("create_post.add_image")}
|
|
61
|
-
value={media}
|
|
62
|
-
onValueChange={setMedia}
|
|
63
|
-
placeholder="Paste an image URL"
|
|
64
|
-
/>
|
|
65
|
-
<ActionButton variant="secondary" icon="image" onClick={() => setMedia("https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=1200&q=80")}>
|
|
66
|
-
{t("create_post.add_image")}
|
|
67
|
-
</ActionButton>
|
|
68
|
-
<ActionButton
|
|
69
|
-
variant="primary"
|
|
70
|
-
fullWidth
|
|
71
|
-
onClick={() => {
|
|
72
|
-
if (!body.trim()) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
state.createPost({ body: body.trim(), media: media.trim(), audience });
|
|
76
|
-
state.showToast(t("create_post.success"));
|
|
77
|
-
navigate("/home");
|
|
78
|
-
}}
|
|
79
|
-
>
|
|
80
|
-
{t("create_post.publish")}
|
|
81
|
-
</ActionButton>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createContext,
|
|
3
|
-
type PropsWithChildren,
|
|
4
|
-
useContext,
|
|
5
|
-
useEffect,
|
|
6
|
-
useMemo,
|
|
7
|
-
} from "react";
|
|
8
|
-
import en from "./locales/en.json";
|
|
9
|
-
import ru from "./locales/ru.json";
|
|
10
|
-
import uz from "./locales/uz.json";
|
|
11
|
-
import { useAppStore } from "./state/store";
|
|
12
|
-
import type { LocaleCode } from "./lib/tokens";
|
|
13
|
-
|
|
14
|
-
type Messages = Record<string, string>;
|
|
15
|
-
|
|
16
|
-
const bundles: Record<LocaleCode, Messages> = {
|
|
17
|
-
en,
|
|
18
|
-
ru,
|
|
19
|
-
uz,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type I18nValue = {
|
|
23
|
-
locale: LocaleCode;
|
|
24
|
-
direction: "ltr" | "rtl";
|
|
25
|
-
t: (key: string) => string;
|
|
26
|
-
setLocale: (locale: LocaleCode) => void;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const I18nContext = createContext<I18nValue | null>(null);
|
|
30
|
-
|
|
31
|
-
export function I18nProvider({ children }: PropsWithChildren) {
|
|
32
|
-
const locale = useAppStore((state) => state.locale);
|
|
33
|
-
const setLocale = useAppStore((state) => state.setLocale);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
document.documentElement.lang = locale;
|
|
37
|
-
document.documentElement.dir = (bundles[locale].$direction as "ltr" | "rtl") ?? "ltr";
|
|
38
|
-
}, [locale]);
|
|
39
|
-
|
|
40
|
-
const value = useMemo<I18nValue>(() => {
|
|
41
|
-
const messages = bundles[locale] ?? bundles.en;
|
|
42
|
-
return {
|
|
43
|
-
locale,
|
|
44
|
-
direction: (messages.$direction as "ltr" | "rtl") ?? "ltr",
|
|
45
|
-
t: (key: string) => messages[key] ?? bundles.en[key] ?? key,
|
|
46
|
-
setLocale,
|
|
47
|
-
};
|
|
48
|
-
}, [locale, setLocale]);
|
|
49
|
-
|
|
50
|
-
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function useI18n() {
|
|
54
|
-
const context = useContext(I18nContext);
|
|
55
|
-
if (!context) {
|
|
56
|
-
throw new Error("useI18n must be used within I18nProvider");
|
|
57
|
-
}
|
|
58
|
-
return context;
|
|
59
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { SVGProps } from "react";
|
|
2
|
-
|
|
3
|
-
type IconProps = SVGProps<SVGSVGElement> & {
|
|
4
|
-
name:
|
|
5
|
-
| "home"
|
|
6
|
-
| "discover"
|
|
7
|
-
| "notifications"
|
|
8
|
-
| "profile"
|
|
9
|
-
| "search"
|
|
10
|
-
| "create_post"
|
|
11
|
-
| "like"
|
|
12
|
-
| "like_fill"
|
|
13
|
-
| "comment"
|
|
14
|
-
| "share"
|
|
15
|
-
| "bookmark"
|
|
16
|
-
| "bookmark_fill"
|
|
17
|
-
| "more"
|
|
18
|
-
| "send"
|
|
19
|
-
| "camera"
|
|
20
|
-
| "image"
|
|
21
|
-
| "edit"
|
|
22
|
-
| "check"
|
|
23
|
-
| "back"
|
|
24
|
-
| "close"
|
|
25
|
-
| "settings";
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export function Icon({ name, className, ...props }: IconProps) {
|
|
29
|
-
const shared = {
|
|
30
|
-
viewBox: "0 0 24 24",
|
|
31
|
-
fill: "none",
|
|
32
|
-
stroke: "currentColor",
|
|
33
|
-
strokeWidth: 1.8,
|
|
34
|
-
strokeLinecap: "round" as const,
|
|
35
|
-
strokeLinejoin: "round" as const,
|
|
36
|
-
className,
|
|
37
|
-
"aria-hidden": true,
|
|
38
|
-
...props,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
switch (name) {
|
|
42
|
-
case "home":
|
|
43
|
-
return <svg {...shared}><path d="M3 10.5 12 3l9 7.5" /><path d="M5 10v10h14V10" /></svg>;
|
|
44
|
-
case "discover":
|
|
45
|
-
return <svg {...shared}><circle cx="12" cy="12" r="8" /><path d="m15.5 8.5-2.4 5.4-5.6 2.1 2.5-5.5Z" /></svg>;
|
|
46
|
-
case "notifications":
|
|
47
|
-
return <svg {...shared}><path d="M6 17h12l-1.4-1.7A4.5 4.5 0 0 1 15.5 12V10a3.5 3.5 0 1 0-7 0v2c0 1.2-.4 2.3-1.1 3.3Z" /><path d="M10 18.5a2 2 0 0 0 4 0" /></svg>;
|
|
48
|
-
case "profile":
|
|
49
|
-
return <svg {...shared}><circle cx="12" cy="8" r="3.5" /><path d="M5 20a7 7 0 0 1 14 0" /></svg>;
|
|
50
|
-
case "search":
|
|
51
|
-
return <svg {...shared}><circle cx="11" cy="11" r="6" /><path d="m20 20-3.5-3.5" /></svg>;
|
|
52
|
-
case "create_post":
|
|
53
|
-
return <svg {...shared}><circle cx="12" cy="12" r="8.5" /><path d="M12 8v8" /><path d="M8 12h8" /></svg>;
|
|
54
|
-
case "like":
|
|
55
|
-
return <svg {...shared}><path d="M12 20s-7-4.3-7-9.5A4 4 0 0 1 12 8a4 4 0 0 1 7 2.5C19 15.7 12 20 12 20Z" /></svg>;
|
|
56
|
-
case "like_fill":
|
|
57
|
-
return <svg viewBox="0 0 24 24" className={className} aria-hidden="true" {...props}><path fill="currentColor" d="M12 20s-7-4.3-7-9.5A4 4 0 0 1 12 8a4 4 0 0 1 7 2.5C19 15.7 12 20 12 20Z" /></svg>;
|
|
58
|
-
case "comment":
|
|
59
|
-
return <svg {...shared}><path d="M5 6h14v9H9l-4 3V6Z" /></svg>;
|
|
60
|
-
case "share":
|
|
61
|
-
return <svg {...shared}><path d="M14 6h5v5" /><path d="M10 14 19 5" /><path d="M19 13v5H5V5h5" /></svg>;
|
|
62
|
-
case "bookmark":
|
|
63
|
-
return <svg {...shared}><path d="M7 4h10v16l-5-3-5 3V4Z" /></svg>;
|
|
64
|
-
case "bookmark_fill":
|
|
65
|
-
return <svg viewBox="0 0 24 24" className={className} aria-hidden="true" {...props}><path fill="currentColor" d="M7 4h10v16l-5-3-5 3V4Z" /></svg>;
|
|
66
|
-
case "more":
|
|
67
|
-
return <svg {...shared}><circle cx="12" cy="6" r="1.5" /><circle cx="12" cy="12" r="1.5" /><circle cx="12" cy="18" r="1.5" /></svg>;
|
|
68
|
-
case "send":
|
|
69
|
-
return <svg {...shared}><path d="M4 20 20 12 4 4l2.5 8Z" /></svg>;
|
|
70
|
-
case "camera":
|
|
71
|
-
return <svg {...shared}><path d="M5 8h3l2-2h4l2 2h3v10H5Z" /><circle cx="12" cy="13" r="3.5" /></svg>;
|
|
72
|
-
case "image":
|
|
73
|
-
return <svg {...shared}><rect x="4" y="5" width="16" height="14" rx="2" /><circle cx="9" cy="10" r="1.2" /><path d="m7 17 4-4 3 3 3-4 2 5" /></svg>;
|
|
74
|
-
case "edit":
|
|
75
|
-
return <svg {...shared}><path d="m5 19 3.5-.7L18 8.8 15.2 6 5.7 15.5Z" /><path d="m13.8 7.2 3 3" /></svg>;
|
|
76
|
-
case "check":
|
|
77
|
-
return <svg {...shared}><path d="m5 13 4 4L19 7" /></svg>;
|
|
78
|
-
case "back":
|
|
79
|
-
return <svg {...shared}><path d="m15 18-6-6 6-6" /></svg>;
|
|
80
|
-
case "close":
|
|
81
|
-
return <svg {...shared}><path d="m6 6 12 12" /><path d="M18 6 6 18" /></svg>;
|
|
82
|
-
case "settings":
|
|
83
|
-
return <svg {...shared}><circle cx="12" cy="12" r="2.5" /><path d="M19 12a7.2 7.2 0 0 0-.1-1l2-1.5-2-3.5-2.4 1a7.3 7.3 0 0 0-1.8-1L14.5 3h-5L9 6a7.3 7.3 0 0 0-1.8 1l-2.4-1-2 3.5 2 1.5a7.2 7.2 0 0 0 0 2l-2 1.5 2 3.5 2.4-1c.6.4 1.2.7 1.8 1l.5 3h5l.5-3c.6-.3 1.2-.6 1.8-1l2.4 1 2-3.5-2-1.5c.1-.3.1-.7.1-1Z" /></svg>;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
export const tokens = {
|
|
2
|
-
colors: {
|
|
3
|
-
brandPrimary: "#1C1B1A",
|
|
4
|
-
brandPrimaryOn: "#FFFFFF",
|
|
5
|
-
brandAccent: "#5B52A3",
|
|
6
|
-
brandAccentOn: "#FFFFFF",
|
|
7
|
-
surfacePrimary: "#FAF8F5",
|
|
8
|
-
surfaceSecondary: "#F3F0EB",
|
|
9
|
-
surfaceTertiary: "#EBE7E0",
|
|
10
|
-
textPrimary: "#1C1B1A",
|
|
11
|
-
textSecondary: "#6B6966",
|
|
12
|
-
textTertiary: "#9E9A95",
|
|
13
|
-
success: "#2D9D5E",
|
|
14
|
-
warning: "#D4920E",
|
|
15
|
-
danger: "#D43B3B",
|
|
16
|
-
info: "#3B82D4",
|
|
17
|
-
borderDefault: "#E0DCD6",
|
|
18
|
-
borderStrong: "#C5C0B8",
|
|
19
|
-
},
|
|
20
|
-
shadows: {
|
|
21
|
-
sm: "0 1px 3px rgba(28, 27, 26, 0.08)",
|
|
22
|
-
md: "0 4px 12px rgba(28, 27, 26, 0.12)",
|
|
23
|
-
lg: "0 8px 24px rgba(28, 27, 26, 0.16)",
|
|
24
|
-
},
|
|
25
|
-
radius: {
|
|
26
|
-
capPrimary: "2px 24px 2px 24px",
|
|
27
|
-
capAlternate: "24px 2px 24px 2px",
|
|
28
|
-
card: "3px 20px 3px 20px",
|
|
29
|
-
surface: "3px 24px 3px 24px",
|
|
30
|
-
sheet: "24px 24px 0 0",
|
|
31
|
-
},
|
|
32
|
-
spacing: {
|
|
33
|
-
xxs: 2,
|
|
34
|
-
xs: 4,
|
|
35
|
-
sm: 8,
|
|
36
|
-
md: 16,
|
|
37
|
-
lg: 24,
|
|
38
|
-
xl: 32,
|
|
39
|
-
xxl: 48,
|
|
40
|
-
},
|
|
41
|
-
typography: {
|
|
42
|
-
display: { size: 32, weight: 700, lineHeight: 1.2 },
|
|
43
|
-
headingLg: { size: 24, weight: 600, lineHeight: 1.3 },
|
|
44
|
-
headingMd: { size: 20, weight: 600, lineHeight: 1.3 },
|
|
45
|
-
headingSm: { size: 16, weight: 600, lineHeight: 1.4 },
|
|
46
|
-
body: { size: 16, weight: 400, lineHeight: 1.5 },
|
|
47
|
-
bodySm: { size: 14, weight: 400, lineHeight: 1.5 },
|
|
48
|
-
caption: { size: 12, weight: 400, lineHeight: 1.4 },
|
|
49
|
-
button: { size: 16, weight: 600, lineHeight: 1.0 },
|
|
50
|
-
},
|
|
51
|
-
breakpoints: {
|
|
52
|
-
compactMax: 600,
|
|
53
|
-
regularMax: 1024,
|
|
54
|
-
},
|
|
55
|
-
motion: {
|
|
56
|
-
quick: 200,
|
|
57
|
-
normal: 300,
|
|
58
|
-
slow: 500,
|
|
59
|
-
easing: {
|
|
60
|
-
default: "ease-out",
|
|
61
|
-
enter: "ease-out",
|
|
62
|
-
exit: "ease-in",
|
|
63
|
-
emphasis: "cubic-bezier(0.2, 0, 0, 1)",
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
} as const;
|
|
67
|
-
|
|
68
|
-
export type LocaleCode = "en" | "ru" | "uz";
|
|
69
|
-
export type ThemePreference = "system" | "light" | "dark";
|
|
70
|
-
export type SizeClass = "compact" | "regular" | "expanded";
|