robodev 0.12.0 → 0.14.0
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/package.json +1 -1
- package/src/emit-starter.test.ts +14 -0
- package/src/index.ts +54 -45
- package/src/parse-create-args.test.ts +26 -0
- package/src/parse-create-args.ts +63 -0
- package/templates/auth-chat/.config/local.spa.template.yml +27 -0
- package/templates/auth-chat/.oxlint-base.json +29 -0
- package/templates/auth-chat/.oxlintrc.json +78 -0
- package/templates/auth-chat/.rulesync/rules/frontend-api-boundary.md +36 -0
- package/templates/auth-chat/.rulesync/rules/frontend-component-structure.md +38 -0
- package/templates/auth-chat/.rulesync/rules/frontend-forms.md +32 -0
- package/templates/auth-chat/.rulesync/rules/frontend-notifications.md +24 -0
- package/templates/auth-chat/.rulesync/rules/frontend-povio-components.md +42 -0
- package/templates/auth-chat/.rulesync/rules/frontend-povio-ui.md +45 -0
- package/templates/auth-chat/.rulesync/rules/frontend-query-autocomplete.md +29 -0
- package/templates/auth-chat/.rulesync/rules/frontend-tables-and-lists.md +34 -0
- package/templates/auth-chat/.rulesync/rules/frontend-translations.md +26 -0
- package/templates/auth-chat/.rulesync/rules/project-overview.md +32 -0
- package/templates/auth-chat/.rulesync/rules/robodev-api.md +30 -0
- package/templates/auth-chat/.rulesync/rules/robodev-auth.md +40 -0
- package/templates/auth-chat/.rulesync/rules/robodev-database.md +22 -0
- package/templates/auth-chat/.rulesync/rules/role-based-app-structure.md +36 -0
- package/templates/auth-chat/.rulesync/skills/media-feature/SKILL.md +21 -0
- package/templates/auth-chat/.rulesync/skills/povio-ui-styling/SKILL.md +208 -0
- package/templates/auth-chat/.rulesync/skills/povio-ui-styling/agents/openai.yaml +4 -0
- package/templates/auth-chat/.rulesync/skills/robodev-api-route/SKILL.md +13 -0
- package/templates/auth-chat/.rulesync/skills/robodev-table/SKILL.md +12 -0
- package/templates/auth-chat/README.md +26 -7
- package/templates/auth-chat/api/invite.ts +43 -0
- package/templates/auth-chat/apps/fe/index.html +24 -0
- package/templates/auth-chat/apps/fe/openapi-codegen.config.ts +37 -0
- package/templates/auth-chat/apps/fe/package.json +82 -0
- package/templates/auth-chat/apps/fe/public/apple-touch-icon.png +0 -0
- package/templates/auth-chat/apps/fe/public/favicon-96x96.png +0 -0
- package/templates/auth-chat/apps/fe/public/favicon.ico +0 -0
- package/templates/auth-chat/apps/fe/public/favicon.svg +3 -0
- package/templates/auth-chat/apps/fe/public/site.webmanifest +21 -0
- package/templates/auth-chat/apps/fe/public/web-app-manifest-192x192.png +0 -0
- package/templates/auth-chat/apps/fe/public/web-app-manifest-512x512.png +0 -0
- package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Bold.otf +0 -0
- package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Medium.otf +0 -0
- package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Regular.otf +0 -0
- package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Semibold.otf +0 -0
- package/templates/auth-chat/apps/fe/src/assets/locales/en/translation.json +202 -0
- package/templates/auth-chat/apps/fe/src/assets/locales/sl/translation.json +202 -0
- package/templates/auth-chat/apps/fe/src/clients/app-rest-client.ts +15 -0
- package/templates/auth-chat/apps/fe/src/clients/auth-token-store.ts +101 -0
- package/templates/auth-chat/apps/fe/src/clients/rest/app-error-handler.ts +31 -0
- package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/authorization-header.interceptor.ts +15 -0
- package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/refresh-token.interceptor.ts +37 -0
- package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/response.interceptor.ts +17 -0
- package/templates/auth-chat/apps/fe/src/components/404.tsx +18 -0
- package/templates/auth-chat/apps/fe/src/components/features/auth/AuthBrandPanel.tsx +60 -0
- package/templates/auth-chat/apps/fe/src/components/features/auth/AuthLayout.tsx +23 -0
- package/templates/auth-chat/apps/fe/src/components/features/auth/LoginPage.tsx +111 -0
- package/templates/auth-chat/apps/fe/src/components/features/auth/RegisterPage.tsx +143 -0
- package/templates/auth-chat/apps/fe/src/components/features/chat/ChatPage.tsx +105 -0
- package/templates/auth-chat/apps/fe/src/components/features/chat/InviteForm.tsx +84 -0
- package/templates/auth-chat/apps/fe/src/components/features/profile/ProfilePage.tsx +96 -0
- package/templates/auth-chat/apps/fe/src/components/googleAnalytics/GoogleAnalytics.tsx +35 -0
- package/templates/auth-chat/apps/fe/src/components/layout/AppLayout.tsx +19 -0
- package/templates/auth-chat/apps/fe/src/components/layout/app-header/AppHeader.tsx +91 -0
- package/templates/auth-chat/apps/fe/src/components/layout/app-header/MobileNavigation.tsx +51 -0
- package/templates/auth-chat/apps/fe/src/components/layout/app-header/NavLink.tsx +29 -0
- package/templates/auth-chat/apps/fe/src/components/shared/branding/BrandLogo.tsx +13 -0
- package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorFallback.tsx +43 -0
- package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorText.tsx +20 -0
- package/templates/auth-chat/apps/fe/src/components/shared/error/NotFound.tsx +36 -0
- package/templates/auth-chat/apps/fe/src/components/shared/forms/RequiredLabel.tsx +21 -0
- package/templates/auth-chat/apps/fe/src/components/shared/forms/RowInputWrapper.tsx +50 -0
- package/templates/auth-chat/apps/fe/src/components/shared/head/AppHead.tsx +32 -0
- package/templates/auth-chat/apps/fe/src/components/shared/head/DefaultAppHead.tsx +34 -0
- package/templates/auth-chat/apps/fe/src/components/shared/layout/LoadingState.tsx +9 -0
- package/templates/auth-chat/apps/fe/src/components/shared/layout/ThinPageWrapper.tsx +5 -0
- package/templates/auth-chat/apps/fe/src/components/shared/page/PageHeader.tsx +69 -0
- package/templates/auth-chat/apps/fe/src/components/shared/ui/Card.tsx +60 -0
- package/templates/auth-chat/apps/fe/src/components/shared/ui/GoogleLoginButton.tsx +19 -0
- package/templates/auth-chat/apps/fe/src/components/shared/ui/RequiredLabel.tsx +22 -0
- package/templates/auth-chat/apps/fe/src/components/shared/ui/TableActions.tsx +22 -0
- package/templates/auth-chat/apps/fe/src/config/app.config.ts +35 -0
- package/templates/auth-chat/apps/fe/src/config/i18n.ts +43 -0
- package/templates/auth-chat/apps/fe/src/config/inits/a11y.ts +14 -0
- package/templates/auth-chat/apps/fe/src/config/inits/logger.ts +7 -0
- package/templates/auth-chat/apps/fe/src/config/inits/sentry.ts +21 -0
- package/templates/auth-chat/apps/fe/src/config/jwt.config.ts +2 -0
- package/templates/auth-chat/apps/fe/src/config/query.config.ts +20 -0
- package/templates/auth-chat/apps/fe/src/hooks/useAuth.ts +5 -0
- package/templates/auth-chat/apps/fe/src/main.tsx +52 -0
- package/templates/auth-chat/apps/fe/src/pages/(guest)/login.tsx +23 -0
- package/templates/auth-chat/apps/fe/src/pages/(guest)/register.tsx +23 -0
- package/templates/auth-chat/apps/fe/src/pages/(guest)/route.tsx +18 -0
- package/templates/auth-chat/apps/fe/src/pages/(private)/index.tsx +23 -0
- package/templates/auth-chat/apps/fe/src/pages/(private)/profile.tsx +23 -0
- package/templates/auth-chat/apps/fe/src/pages/(private)/route.tsx +18 -0
- package/templates/auth-chat/apps/fe/src/pages/(public)/auth.tsx +37 -0
- package/templates/auth-chat/apps/fe/src/pages/(public)/route.tsx +9 -0
- package/templates/auth-chat/apps/fe/src/pages/__root.tsx +164 -0
- package/templates/auth-chat/apps/fe/src/providers/AppErrorBoundary.tsx +10 -0
- package/templates/auth-chat/apps/fe/src/providers/OpenApiRuntimeProvider.tsx +31 -0
- package/templates/auth-chat/apps/fe/src/providers/index.tsx +44 -0
- package/templates/auth-chat/apps/fe/src/providers/jwt.provider.tsx +95 -0
- package/templates/auth-chat/apps/fe/src/routeTree.gen.ts +225 -0
- package/templates/auth-chat/apps/fe/src/styles/base.css +103 -0
- package/templates/auth-chat/apps/fe/src/styles/fonts/fonts.tsx +10 -0
- package/templates/auth-chat/apps/fe/src/styles/fonts/general-sans.css +31 -0
- package/templates/auth-chat/apps/fe/src/styles/globals.css +28 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/button.override.ts +536 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/checkbox.override.ts +71 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/input.override.ts +252 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/label.override.ts +91 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/modal.override.ts +57 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/radio.override.ts +46 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/table.override.ts +104 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/tag.override.ts +66 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/typography.override.ts +115 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/outline.clsx.ts +10 -0
- package/templates/auth-chat/apps/fe/src/styles/overrides/uiOverrides.override.ts +60 -0
- package/templates/auth-chat/apps/fe/src/styles/theme.css +2177 -0
- package/templates/auth-chat/apps/fe/src/types/i18next.d.ts +12 -0
- package/templates/auth-chat/apps/fe/src/types/table.d.ts +17 -0
- package/templates/auth-chat/apps/fe/src/types/ui.d.ts +26 -0
- package/templates/auth-chat/apps/fe/src/types/vite-env.d.ts +20 -0
- package/templates/auth-chat/apps/fe/src/utils/date.utils.ts +17 -0
- package/templates/auth-chat/apps/fe/src/utils/number.utils.ts +12 -0
- package/templates/auth-chat/apps/fe/src/utils/string.utils.ts +5 -0
- package/templates/auth-chat/apps/fe/src/vite-env.d.ts +1 -0
- package/templates/auth-chat/apps/fe/tsconfig.app.json +32 -0
- package/templates/auth-chat/apps/fe/tsconfig.json +9 -0
- package/templates/auth-chat/apps/fe/tsconfig.node.json +31 -0
- package/templates/auth-chat/apps/fe/vite.config.ts +94 -0
- package/templates/auth-chat/openapi.json +1016 -0
- package/templates/auth-chat/oxfmt.config.js +23 -0
- package/templates/auth-chat/package.json +6 -13
- package/templates/auth-chat/rulesync.jsonc +18 -0
- package/templates/auth-chat/tsconfig.json +2 -4
- package/templates/backend/README.md +2 -2
- package/templates/backend/api/_lib.ts +359 -0
- package/templates/backend/api/aliens/labels.ts +19 -0
- package/templates/backend/api/files/presigned-url.ts +48 -0
- package/templates/backend/api/files/upload.ts +29 -0
- package/templates/backend/api/planets/[id]/like.ts +36 -0
- package/templates/backend/api/planets/[id].ts +56 -13
- package/templates/backend/api/planets/paginate.ts +36 -0
- package/templates/backend/api/planets.ts +52 -44
- package/templates/backend/database.ts +33 -13
- package/templates/backend/package.json +1 -1
- package/templates/catalog.json +2 -2
- package/templates/empty/package.json +1 -1
- package/templates/space/package.json +1 -1
- package/templates/auth-chat/index.html +0 -201
- package/templates/auth-chat/src/main.tsx +0 -282
- package/templates/auth-chat/vite.config.ts +0 -7
- package/templates/backend/api/health.ts +0 -7
- package/templates/backend/api/launch.ts +0 -29
- package/templates/backend/api/me.ts +0 -14
- package/templates/backend/api/motto.ts +0 -9
- package/templates/backend/api/rockets.ts +0 -43
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Frontend QueryAutocomplete usage for database-entity dropdowns with generated label queries and queryParams."
|
|
5
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when a frontend form or filter needs a dropdown/search picker of database entities."
|
|
9
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Frontend QueryAutocomplete
|
|
13
|
+
|
|
14
|
+
Use this pattern whenever the UI needs a dropdown/search picker of entities from the database, such as selecting a alien, company, project, user, category, or any other related row in a form or filter.
|
|
15
|
+
|
|
16
|
+
Use `QueryAutocomplete` for these database-entity dropdowns. It should call a generated labels endpoint such as `AliensQueries.useListLabels`, not load full related records into the form.
|
|
17
|
+
|
|
18
|
+
Reference examples:
|
|
19
|
+
|
|
20
|
+
- Create form: `apps/fe/src/components/features/planets/list/PlanetCreateModal.tsx`
|
|
21
|
+
- Edit form: `apps/fe/src/components/features/planets/details/PlanetEditPage.tsx`
|
|
22
|
+
- Filter control: `apps/fe/src/components/features/planets/list/PlanetsFilters.tsx`
|
|
23
|
+
The matching labels endpoint input schema must define `search`, usually as `search: z.string().optional()`, because `QueryAutocomplete` passes search text through that parameter.
|
|
24
|
+
|
|
25
|
+
If a labels endpoint needs extra context, pass it through `QueryAutocomplete` with `queryParams`. The matching labels endpoint input schema can include optional parameters beyond `search`, such as extra filters or flags that control how rows map into `{ id, label }`.
|
|
26
|
+
|
|
27
|
+
When the autocomplete belongs to a form, import it from `@povio/ui/tanstack` and prefer `field={{ form, name }}`. When it belongs to filters, read and write through the `useFilters` store.
|
|
28
|
+
|
|
29
|
+
Real app features should use generated labels queries from `@/openapi`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Frontend list and table patterns for grids, Table, InfiniteTable, filters, and sorting."
|
|
5
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when building or changing list pages, grids, tables, filters, sorting, pagination, or row actions."
|
|
9
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Frontend Tables And Lists
|
|
13
|
+
|
|
14
|
+
The planets feature intentionally shows multiple list patterns while the template is still a prototype:
|
|
15
|
+
|
|
16
|
+
- Demo switcher: `apps/fe/src/components/features/planets/list/PlanetsPage.tsx`
|
|
17
|
+
- Grid list: `apps/fe/src/components/features/planets/list/PlanetsGridPage.tsx`
|
|
18
|
+
- Table list: `apps/fe/src/components/features/planets/list/PlanetsTablePage.tsx`
|
|
19
|
+
- Infinite table list: `apps/fe/src/components/features/planets/list/PlanetsInfiniteTablePage.tsx`
|
|
20
|
+
- Table wrappers and actions: `apps/fe/src/components/features/planets/list/table/*`
|
|
21
|
+
|
|
22
|
+
The `Segment` in `PlanetsPage` is demo-only. Real feature pages should usually choose one list pattern and use it consistently.
|
|
23
|
+
|
|
24
|
+
Prefer paginated API queries for database-backed collections. Use regular generated list queries only for data that is guaranteed to stay short, such as enums, compact option lists, or label-style endpoints.
|
|
25
|
+
|
|
26
|
+
Use `Table` from `@povio/ui` for small bounded table lists and `InfiniteTable` when incremental loading is useful for the feature. Infinite tables require a paginated backend endpoint and generated `use<Endpoint>Infinite` query. Even non-infinite collection views should normally be backed by a paginated endpoint unless the collection is intentionally bounded.
|
|
27
|
+
|
|
28
|
+
When a feature needs filters, define a filter schema on the API model, expose it through OpenAPI when useful, and use `useFilters` on the frontend. Put filter controls in a focused component such as `PlanetsFilters`. Use `as="filter"` on Povio UI filter controls.
|
|
29
|
+
|
|
30
|
+
When a feature needs sorting, expose an enum of sortable keys from the API layer and pass the generated enum schema into `dynamicColumns({ options: { sortable } })`. Use `useSorting` on the frontend and pass its `order` string to the generated query.
|
|
31
|
+
|
|
32
|
+
Use wrapper components around table primitives. A table wrapper should accept `TableWrapperProps<T>` or `InfiniteTableWrapperProps<T>` from `@povio/ui` and spread props into `Table` or `InfiniteTable`, while defining columns locally with a `getColumns` helper above the component.
|
|
33
|
+
|
|
34
|
+
Keep per-row actions in a small action component, such as `PlanetsTableActions`, and use a shared row-action wrapper when available so clicks do not accidentally trigger row navigation.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Frontend translation key structure and no-hardcoded-text conventions."
|
|
5
|
+
globs: ["apps/fe/src/**/*.{ts,tsx,json}"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when adding or changing frontend user-facing text, locale files, or translation keys."
|
|
9
|
+
globs: ["apps/fe/src/**/*.{ts,tsx,json}"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Frontend Translations
|
|
13
|
+
|
|
14
|
+
Do not hardcode user-facing frontend text. Add copy to locale files and access it through `useTranslation`.
|
|
15
|
+
|
|
16
|
+
Translation key structure should generally follow the feature/component tree. The planets feature is the current reference example:
|
|
17
|
+
|
|
18
|
+
- `planets.page.*`
|
|
19
|
+
- `planets.views.*`
|
|
20
|
+
- `planets.filters.*`
|
|
21
|
+
- `planets.createModal.*`
|
|
22
|
+
- `planets.editModal.*`
|
|
23
|
+
- `planets.table.*`
|
|
24
|
+
- `planets.detail.*`
|
|
25
|
+
|
|
26
|
+
When Slovenian translations are required, preserve real Slovenian characters with carons, especially Unicode `U+0161`, `U+010D`, and `U+017E`. Do not leave mojibake or replacement-character artifacts in locale files.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: true
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Project overview and Rulesync source-of-truth guidance."
|
|
5
|
+
globs: ["**/*"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: true
|
|
8
|
+
description: "Project overview and Rulesync source-of-truth guidance."
|
|
9
|
+
globs: ["**/*"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Space starter AI rules
|
|
13
|
+
|
|
14
|
+
This project is a Vite React frontend in `apps/fe` plus a Robodev backend at the **repository root** (`database.ts` + `api/**/*.ts` using `@robodev-ai/sdk`). There is no Expo / mobile app in this starter. End-user auth is reserved on the project host at `/api/user/*`. There is no in-browser fake backend.
|
|
15
|
+
|
|
16
|
+
`robodev deploy` from this root uploads schema and APIs. It does not host the Povio SPA. Run the UI with bun in `apps/fe`.
|
|
17
|
+
|
|
18
|
+
Shared AI rules and skills are authored in `.rulesync/`. Generated files for Codex, Claude Code, and Cursor are local outputs and should be regenerated with `bun rules:gen`.
|
|
19
|
+
|
|
20
|
+
The demo is a shared chat room plus an authenticated invite-email form. Signed-in users read and post on `GET/POST /api/messages`. Invites go through `POST /api/invite` (`auth: "required"`); posting a chat message does not send email. Do not add `api/user/**`.
|
|
21
|
+
|
|
22
|
+
When an app has multiple user roles with distinct UI or business workflows, use the conditional role-based app structure rule. Do not apply role splitting to simple single-role apps or small permission differences.
|
|
23
|
+
|
|
24
|
+
Use the more specific generated rules for:
|
|
25
|
+
|
|
26
|
+
- Robodev `defineDatabase` tables in `database.ts`.
|
|
27
|
+
- Robodev `defineApi` routes in `api/`.
|
|
28
|
+
- Reserved `/api/user/*` auth contracts (do not implement `api/user/**` in the project).
|
|
29
|
+
- Frontend API boundaries around generated `@/openapi` queries and models.
|
|
30
|
+
- Povio UI and semantic Tailwind token usage.
|
|
31
|
+
- Frontend forms, tables/lists, `QueryAutocomplete`, translations, and component structure.
|
|
32
|
+
- Role-based frontend structure when the app has multiple distinct user roles.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Robodev defineApi file routes and handler conventions."
|
|
5
|
+
globs: ["api/**/*.ts"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when editing Starbase API routes."
|
|
9
|
+
globs: ["api/**/*.ts"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Robodev API
|
|
13
|
+
|
|
14
|
+
Project APIs live in `api/`. `api/planets.ts` is served at `/planets` and `/api/planets`. Prefer the `/api/...` path in OpenAPI and frontend clients.
|
|
15
|
+
|
|
16
|
+
## File layout
|
|
17
|
+
|
|
18
|
+
- `api/foo.ts` → `/api/foo`
|
|
19
|
+
- `api/foo/[id].ts` → `/api/foo/:id`
|
|
20
|
+
- Export `get` / `post` / `put` / `patch` / `delete` as `defineApi(...)`.
|
|
21
|
+
- Shared helpers belong in `api/_lib.ts`. Do not export `defineApi` from `_lib.ts`.
|
|
22
|
+
- Do not add `api/user/**`. Those paths are reserved for Robodev Auth.
|
|
23
|
+
|
|
24
|
+
## Handlers
|
|
25
|
+
|
|
26
|
+
- Use `auth: "required"` on routes that Tiny marked authenticated.
|
|
27
|
+
- Handler context includes `db`, `user`, `email`, `storage`, `params`, `query`, `body`.
|
|
28
|
+
- Return JSON for 200, or `{ status, body }` for errors (404/403/400).
|
|
29
|
+
- Keep request/response shapes compatible with `openapi.json` so OpenAPI codegen stays stable.
|
|
30
|
+
- After changing public contracts, update `openapi.json` if needed and run `bun openapi:gen` from `apps/fe`.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Reserved Robodev /api/user auth contracts."
|
|
5
|
+
globs: ["**/*.ts", "apps/fe/src/**/*.{ts,tsx}"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when touching auth, login, register, or /api/user clients."
|
|
9
|
+
globs:
|
|
10
|
+
[
|
|
11
|
+
"database.ts",
|
|
12
|
+
"api/**/*.ts",
|
|
13
|
+
"apps/fe/src/components/features/auth/**/*.{ts,tsx}",
|
|
14
|
+
"apps/fe/src/clients/**/*.ts",
|
|
15
|
+
]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Robodev Auth
|
|
19
|
+
|
|
20
|
+
End-user auth is reserved on the project host. Do not implement `api/user/**` in this project.
|
|
21
|
+
|
|
22
|
+
## Contracts
|
|
23
|
+
|
|
24
|
+
| Method | Path | Notes |
|
|
25
|
+
| ------- | ----------------------------------------- | ------------------------------------------------------- |
|
|
26
|
+
| POST | `/api/user/auth/login` | `{ email, password }` → `{ accessToken, refreshToken }` |
|
|
27
|
+
| POST | `/api/user/auth/register` | password min 12, 201, token pair |
|
|
28
|
+
| POST | `/api/user/auth/refresh` | `{ refreshToken }` → new pair |
|
|
29
|
+
| GET | `/api/user/auth/magic-link` | query `email`, generic status |
|
|
30
|
+
| GET | `/api/user/auth/magic-link/callback` | query `code` → tokens |
|
|
31
|
+
| POST | `/api/user/auth/forgot-password` | `{ email }` |
|
|
32
|
+
| POST | `/api/user/auth/forgot-password/callback` | `{ code, password }` |
|
|
33
|
+
| GET | `/api/user/auth/google` | start OAuth; `redirect_uri` required |
|
|
34
|
+
| GET/PUT | `/api/user/me` | flat `{ id, name, email }` |
|
|
35
|
+
|
|
36
|
+
Access tokens last 15 minutes. Refresh tokens last 30 days. Frontend stores both and refreshes on 401 via `UserAuthApi.accessToken`.
|
|
37
|
+
|
|
38
|
+
Error `code` is top-level: `invalid-credentials`, `identity-already-exists`, `identity-not-found`, `nonce-invalid`.
|
|
39
|
+
|
|
40
|
+
Codegen operationIds stay Tiny-shaped (`UserAuthLocalPasswordController_login`, `UserMeController_get`, …) in `openapi.json`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Robodev database.ts Drizzle schema and deploy sync."
|
|
5
|
+
globs: ["database.ts"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply when editing the Starbase database schema."
|
|
9
|
+
globs: ["database.ts"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Robodev Database
|
|
13
|
+
|
|
14
|
+
The backend schema lives in `database.ts` at the project root. Default-export `defineDatabase` with Drizzle `pgTable` definitions. `robodev deploy` from this root creates or syncs the named Postgres database. There are no migration files.
|
|
15
|
+
|
|
16
|
+
## Rules
|
|
17
|
+
|
|
18
|
+
- Import table helpers from `@robodev-ai/sdk` (`pgTable`, `uuid`, `text`, `timestamp`, `integer`, and query helpers).
|
|
19
|
+
- Do not recreate User or auth tables. End users live in platform `robodev_auth`. Store `userId` as `text` matching `ctx.user.id`.
|
|
20
|
+
- Prefer scalar columns and foreign keys. Destructive drops need deploy confirmation or `--force`.
|
|
21
|
+
- Export named tables so API files can import them from `../database` or `api/_lib.ts`.
|
|
22
|
+
- Seed demo rows in API handlers on first read (see `api/_lib.ts` `ensureSeed`), not with a separate seed runner.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
root: false
|
|
3
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
4
|
+
description: "Conditional role-based frontend and API structure for apps with distinct user roles."
|
|
5
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}", "api/**/*.ts"]
|
|
6
|
+
cursor:
|
|
7
|
+
alwaysApply: false
|
|
8
|
+
description: "Apply only when an app has multiple user roles with distinct UI surfaces or business workflows."
|
|
9
|
+
globs: ["apps/fe/src/**/*.{ts,tsx}", "api/**/*.ts"]
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Role-Based App Structure
|
|
13
|
+
|
|
14
|
+
Apply this rule only when the app has multiple user roles with meaningfully different UI surfaces or business workflows. For single-role apps or small permission differences, keep normal feature-based structure and express authorization in handlers, ACL metadata, or UI affordances.
|
|
15
|
+
|
|
16
|
+
## Frontend Structure
|
|
17
|
+
|
|
18
|
+
Split role-specific frontend code by role first:
|
|
19
|
+
|
|
20
|
+
- `apps/fe/src/components/features/<role>/<feature>/...` for role-owned feature UI, such as `admin/companies`, `manager/projects`, or `worker/timesheets`.
|
|
21
|
+
- `apps/fe/src/pages/(private)/(<role>)/...` for role-owned private routes and layout guards.
|
|
22
|
+
- Shared auth, profile, layout, and role-neutral UI should stay outside role folders.
|
|
23
|
+
|
|
24
|
+
When two roles touch the same entity with different screens or permissions, prefer separate role-owned feature components over a single component with many role branches.
|
|
25
|
+
|
|
26
|
+
## API Structure
|
|
27
|
+
|
|
28
|
+
Create role-prefixed `defineApi` files for role-owned behavior even when the backing table is shared. For example, use `api/manager/projects.ts` and `api/worker/projects.ts` when managers can create projects while workers can only list assigned ones.
|
|
29
|
+
|
|
30
|
+
Role-prefixed routes should keep the role visible across the API surface:
|
|
31
|
+
|
|
32
|
+
- Files: `api/<role>/<resource>.ts`, such as `api/manager/projects.ts`.
|
|
33
|
+
- HTTP paths: `/api/<role>/<resource>`, such as `/api/manager/projects/paginate`.
|
|
34
|
+
- Generated client modules should follow the same names.
|
|
35
|
+
|
|
36
|
+
Keep shared auth, user, and media contracts role-neutral. Auth stays reserved at `/api/user/*`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: media-feature
|
|
3
|
+
description: "Use when adding or changing feature-level media/file upload support or frontend FileUpload handling."
|
|
4
|
+
targets: ["claudecode", "codexcli", "cursor"]
|
|
5
|
+
codexcli:
|
|
6
|
+
short-description: Add or update media upload support for features.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Media Feature Skill
|
|
10
|
+
|
|
11
|
+
Preserve the upload contract:
|
|
12
|
+
|
|
13
|
+
1. Frontend selects a `File`.
|
|
14
|
+
2. Frontend calls `MediaQueries.useUploadRequest` with metadata only (`POST /api/files/presigned-url`).
|
|
15
|
+
3. Frontend uploads bytes to the returned URL (`POST /api/files/upload` via Starbase `ctx.storage`).
|
|
16
|
+
4. Feature create/update submits only `image: { id }`.
|
|
17
|
+
5. Feature reads resolve that ID to `image: { id, url }`.
|
|
18
|
+
|
|
19
|
+
Never send `File`, `Blob`, base64, or raw bytes in a feature create/update request.
|
|
20
|
+
|
|
21
|
+
Media metadata lives in the `media` table in `database.ts`. Bytes live in Robodev storage. Planet images use resource name `planet-image`.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: povio-ui-styling
|
|
3
|
+
description: Mandatory first skill for any Tiny Template web frontend UI work in apps/fe. Load and apply it before other frontend, design, UX, accessibility, responsive, animation, or performance skills when creating, changing, reviewing, debugging, polishing, or refactoring pages and React components. It requires checking and using @povio/ui primitives before raw HTML or custom controls, and governs UIConfig defaults, UIOverrides, Tailwind utilities, semantic tokens, shared UI wrappers, and dedicated CSS files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Povio UI Styling
|
|
7
|
+
|
|
8
|
+
Apply the narrowest styling layer that expresses the intended scope. Preserve one source of truth for each visual rule.
|
|
9
|
+
|
|
10
|
+
## Priority and Coordination
|
|
11
|
+
|
|
12
|
+
This is the foundation skill for every UI task under `apps/fe`. Apply it first, before broader design, UX, accessibility, animation, responsive, or React performance skills. Those skills may improve the result, but they must preserve the component and styling decisions defined here.
|
|
13
|
+
|
|
14
|
+
Use this skill for all work that creates, changes, reviews, or diagnoses visible or interactive frontend UI, including:
|
|
15
|
+
|
|
16
|
+
- Pages, layouts, navigation, dashboards, cards, lists, and empty states
|
|
17
|
+
- Forms, filters, search, inputs, validation, uploads, and settings
|
|
18
|
+
- Buttons, links, menus, dialogs, drawers, popovers, tooltips, and toasts
|
|
19
|
+
- Tables, pagination, tags, badges, typography, icons, and loading states
|
|
20
|
+
- Responsive behavior, theming, accessibility, motion, and visual polish
|
|
21
|
+
- Component extraction, shared wrappers, design-system alignment, and Figma implementation
|
|
22
|
+
|
|
23
|
+
This skill does not replace feature-specific skills. Apply the relevant feature skill as well, after establishing the Povio UI component and styling layer.
|
|
24
|
+
|
|
25
|
+
## Inspect Before Styling
|
|
26
|
+
|
|
27
|
+
1. Read the target component and nearby feature components.
|
|
28
|
+
2. Inspect existing `@povio/ui` usage in `apps/fe` for the relevant primitive.
|
|
29
|
+
3. Inspect `apps/fe/src/styles/overrides/uiOverrides.override.ts` and the matching file under `styles/overrides/defaults` before changing a Povio primitive.
|
|
30
|
+
4. Inspect `apps/fe/src/styles/theme.css` for available generated tokens, but never edit that file manually.
|
|
31
|
+
5. Inspect `apps/fe/src/styles/base.css` and `globals.css` before adding global rules or tokens.
|
|
32
|
+
|
|
33
|
+
## Use This Decision Order
|
|
34
|
+
|
|
35
|
+
### 1. Use an Existing Povio UI Component
|
|
36
|
+
|
|
37
|
+
Use `@povio/ui` whenever it provides the semantic control or presentation primitive. This includes buttons, links, typography, icons, inputs, textareas, selects, checkboxes, radios, switches, forms, uploads, tags, badges, tables, pagination, menus, tooltips, modals, confirmations, drawers, popovers, toasts, loading indicators, and empty or feedback states.
|
|
38
|
+
|
|
39
|
+
Prefer the component's supported props and variants over restyling its internals. Do not recreate a primitive with a raw HTML element merely to get a different appearance.
|
|
40
|
+
|
|
41
|
+
Before introducing a native interactive element, a custom control, or a third-party UI component, search `@povio/ui` exports and existing feature usage for an existing equivalent. Native structural elements such as `main`, `section`, `article`, `nav`, and layout containers remain appropriate when no Povio semantic component exists.
|
|
42
|
+
|
|
43
|
+
### 2. Use `UIConfig` for Application-Wide Defaults
|
|
44
|
+
|
|
45
|
+
Use the root `UIConfig.Provider` when an existing Povio variant or option is correct and only its default should change across the application.
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
|
|
49
|
+
- Make all inputs default to `small`.
|
|
50
|
+
- Set the default table-cell typography size.
|
|
51
|
+
- Set the default tag text size.
|
|
52
|
+
|
|
53
|
+
Do not use `UIConfig` for a one-off screen or to invent a new variant.
|
|
54
|
+
|
|
55
|
+
### 3. Use Tailwind for Composition and Local Presentation
|
|
56
|
+
|
|
57
|
+
Use Tailwind classes in JSX for:
|
|
58
|
+
|
|
59
|
+
- Page and component layout
|
|
60
|
+
- Grid and flex behavior
|
|
61
|
+
- Width, height, spacing, alignment, and overflow
|
|
62
|
+
- Responsive composition
|
|
63
|
+
- A one-instance surface, border, or text treatment
|
|
64
|
+
- State selectors that remain short and readable
|
|
65
|
+
|
|
66
|
+
Use semantic project utilities such as `bg-elevation-fill-default-1`, `text-text-default-1`, and `border-elevation-outline-default-1`. Do not use removed default palette utilities such as `bg-blue-500`, and do not repeat raw hex values in components.
|
|
67
|
+
|
|
68
|
+
Keep Povio component `className` additions focused on outer layout or a genuinely local treatment. Do not pile conflicting utilities onto a component to fight its configured CVA variants. If the same correction is required on most instances, move it to `UIConfig` or `UIOverrides`.
|
|
69
|
+
|
|
70
|
+
### 4. Use `UIOverrides` for Design-System-Level Povio Changes
|
|
71
|
+
|
|
72
|
+
Use `UIOverrides.defineOverride(...)` when a Povio primitive must change consistently throughout the application:
|
|
73
|
+
|
|
74
|
+
- Change the base classes of every instance.
|
|
75
|
+
- Add or change supported variants.
|
|
76
|
+
- Change compound-variant behavior.
|
|
77
|
+
- Change internal slots not exposed by ordinary component props.
|
|
78
|
+
- Align a primitive with the application's Figma design system.
|
|
79
|
+
|
|
80
|
+
Place overrides under `apps/fe/src/styles/overrides/defaults/<component>.override.ts` and register them in `uiOverrides.override.ts`. Use the existing `mode`, `base`, `config`, variant, and compound-variant patterns.
|
|
81
|
+
|
|
82
|
+
Update `apps/fe/src/types/ui.d.ts` when a new override variant must augment public Povio component types.
|
|
83
|
+
|
|
84
|
+
Do not use `UIOverrides` for:
|
|
85
|
+
|
|
86
|
+
- Page layout
|
|
87
|
+
- One feature's special card
|
|
88
|
+
- One exceptional component instance
|
|
89
|
+
- A workaround that can be expressed through an existing prop
|
|
90
|
+
|
|
91
|
+
Changing an override affects every consumer. Search all uses of the primitive and verify representative screens before finishing.
|
|
92
|
+
|
|
93
|
+
### 5. Use a Shared Wrapper for Repeated Product Semantics
|
|
94
|
+
|
|
95
|
+
Create a component under `apps/fe/src/components/shared` when several features need the same product-specific composition but the base Povio primitive should remain unchanged.
|
|
96
|
+
|
|
97
|
+
Examples:
|
|
98
|
+
|
|
99
|
+
- `StatusTag` mapping domain statuses to Povio `Tag` variants
|
|
100
|
+
- `WalkSchedule` composing typography, icons, and layout
|
|
101
|
+
- `PageCard` applying a repeated application surface
|
|
102
|
+
|
|
103
|
+
Expose semantic props rather than forwarding a growing collection of styling flags.
|
|
104
|
+
|
|
105
|
+
### 6. Use Dedicated CSS for Selector-Heavy or Browser-Level Behavior
|
|
106
|
+
|
|
107
|
+
Create or extend a `.css` file when CSS communicates the rule more clearly than Tailwind:
|
|
108
|
+
|
|
109
|
+
- Pseudo-elements used for connected timelines or route lines
|
|
110
|
+
- Multi-step keyframes
|
|
111
|
+
- Complex descendant, sibling, or state selectors
|
|
112
|
+
- Calendar grids and overlapping appointments
|
|
113
|
+
- Map-library markers, popups, or third-party DOM overrides
|
|
114
|
+
- Print styles
|
|
115
|
+
- Browser-global behavior such as scrollbars
|
|
116
|
+
- Rules driven by CSS custom properties that many descendants consume
|
|
117
|
+
|
|
118
|
+
Co-locate feature CSS with its owning component and import it there. Use a narrow, feature-prefixed class namespace, for example `walk-route__marker`. Keep truly global rules in `globals.css` or `base.css`.
|
|
119
|
+
|
|
120
|
+
Do not create a CSS file for ordinary spacing, flex/grid layout, a simple hover, or a semantic color already expressible with a short Tailwind class list.
|
|
121
|
+
|
|
122
|
+
## Scope Guide
|
|
123
|
+
|
|
124
|
+
| Desired scope | Styling layer |
|
|
125
|
+
| -------------------------------------------------------------- | --------------------------- |
|
|
126
|
+
| Existing primitive behavior | Povio UI props/variants |
|
|
127
|
+
| Default for every instance | `UIConfig` |
|
|
128
|
+
| One page or component composition | Tailwind |
|
|
129
|
+
| Every instance or internal Povio slot | `UIOverrides` |
|
|
130
|
+
| Repeated product-specific composition | Shared wrapper |
|
|
131
|
+
| Pseudo-elements, keyframes, complex selectors, third-party DOM | Scoped `.css` |
|
|
132
|
+
| Generated design tokens | Figma-generated `theme.css` |
|
|
133
|
+
| Handwritten global token or browser rule | `base.css` or `globals.css` |
|
|
134
|
+
|
|
135
|
+
## Guardrails
|
|
136
|
+
|
|
137
|
+
- Use `@povio/ui` before creating a custom primitive.
|
|
138
|
+
- Do not let guidance from a general frontend or design skill replace an available `@povio/ui` primitive with raw HTML, a local reimplementation, or another component library.
|
|
139
|
+
- Use semantic Tailwind tokens from the project theme.
|
|
140
|
+
- Never manually edit `apps/fe/src/styles/theme.css`.
|
|
141
|
+
- Avoid `!important`; resolve ownership or specificity instead.
|
|
142
|
+
- Avoid inline `style` except for truly runtime numeric values such as map coordinates or calculated dimensions. Prefer CSS custom properties when descendants need the value.
|
|
143
|
+
- Avoid Sass, CSS-in-JS, and a second styling system.
|
|
144
|
+
- Preserve visible focus, disabled, hover, pressed, error, and loading states.
|
|
145
|
+
- Respect `prefers-reduced-motion` for nonessential animation.
|
|
146
|
+
- Keep touch targets at least 44 by 44 CSS pixels where practical.
|
|
147
|
+
- Verify changes at mobile and desktop breakpoints.
|
|
148
|
+
|
|
149
|
+
## Examples
|
|
150
|
+
|
|
151
|
+
Use Tailwind for local layout:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<section className="grid gap-4 rounded-modal-rounding-default border border-elevation-outline-default-1 bg-elevation-fill-default-1 p-4">
|
|
155
|
+
<Typography size="title-4">{title}</Typography>
|
|
156
|
+
{children}
|
|
157
|
+
</section>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Use `UIConfig` for an existing global default:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<UIConfig.Provider
|
|
164
|
+
config={{
|
|
165
|
+
input: { size: "small" },
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
{children}
|
|
169
|
+
</UIConfig.Provider>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Use a scoped CSS file for a connected route line:
|
|
173
|
+
|
|
174
|
+
```css
|
|
175
|
+
.walk-route__marker::after {
|
|
176
|
+
content: "";
|
|
177
|
+
position: absolute;
|
|
178
|
+
inset-block: 1.25rem -0.75rem;
|
|
179
|
+
inset-inline-start: 50%;
|
|
180
|
+
width: 0.125rem;
|
|
181
|
+
background: rgb(var(--elevation-outline-default-1-base));
|
|
182
|
+
transform: translateX(-50%);
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Use `UIOverrides` only when the same primitive-level change is intentionally global:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
export const tagOverride = UIOverrides.defineOverride("tag.cva", {
|
|
190
|
+
mode: "overrideCva",
|
|
191
|
+
base: ["inline-flex items-center border"],
|
|
192
|
+
config: {
|
|
193
|
+
variants: {
|
|
194
|
+
color: {
|
|
195
|
+
success: "border-interactive-subtle-success-idle bg-interactive-subtle-success-idle",
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Verification
|
|
203
|
+
|
|
204
|
+
1. Run formatting, linting, and TypeScript checks relevant to `apps/fe`.
|
|
205
|
+
2. Inspect at least one representative use for every globally changed primitive.
|
|
206
|
+
3. Check keyboard focus and disabled/error states.
|
|
207
|
+
4. Check mobile and desktop layouts.
|
|
208
|
+
5. Confirm no raw default-palette classes or duplicated design values were introduced.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: robodev-api-route
|
|
3
|
+
description: Add a defineApi route under api/.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add a Robodev API route
|
|
7
|
+
|
|
8
|
+
1. Create `api/<path>.ts` or `api/<path>/[id].ts`.
|
|
9
|
+
2. Export HTTP methods with `defineApi` from `@robodev-ai/sdk`.
|
|
10
|
+
3. Use `auth: "required"` unless the route is public.
|
|
11
|
+
4. Keep JSON shapes aligned with `openapi.json`.
|
|
12
|
+
5. Do not create `api/user/**` files.
|
|
13
|
+
6. Deploy from the project root (`robodev deploy`), then `bun openapi:gen` in `apps/fe` if the contract changed.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: robodev-table
|
|
3
|
+
description: Add a Drizzle table to database.ts and wire it into defineApi handlers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Add a Robodev table
|
|
7
|
+
|
|
8
|
+
1. Add a `pgTable` to `database.ts` and include it in `defineDatabase({ tables })`.
|
|
9
|
+
2. Do not add a User table. Auth users are platform-managed; store `userId` as text.
|
|
10
|
+
3. Import the table in `api/_lib.ts` or the route file that needs it.
|
|
11
|
+
4. Deploy from the project root with `robodev deploy`. Destructive drops need confirmation or `--force`.
|
|
12
|
+
5. If the frontend needs the new fields, update `openapi.json` and run `bun openapi:gen` from `apps/fe`.
|
|
@@ -1,17 +1,36 @@
|
|
|
1
1
|
# {{NAME}}
|
|
2
2
|
|
|
3
|
-
Starter
|
|
3
|
+
Starter from `robodev create --starter auth-chat`: Tiny chat APIs (`chat` database, `messages` table), authenticated invite email, and a **local** Povio UI in `apps/fe`.
|
|
4
4
|
|
|
5
|
-
The project host is
|
|
5
|
+
The project host is **API-only**. After create or deploy, the printed App URL serves APIs and Swagger — not the SPA. Run the UI with bun:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
cd apps/fe
|
|
9
|
+
bun install
|
|
10
|
+
bun openapi:gen
|
|
11
|
+
bun dev
|
|
9
12
|
```
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
Vite defaults to port **3000** (`VITE_DEV_PORT`). `bun` is required for `apps/fe`.
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
Auth, link, and deploy from this **root**:
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
```bash
|
|
19
|
+
npx robodev auth
|
|
20
|
+
npx robodev link
|
|
21
|
+
npx robodev deploy
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`robodev create` / `robodev link` write `VITE_API_URL`, `VITE_PUBLIC_API_URL`, and `APP_PUBLIC_API_URL` in `.env`, and rewrite `.config/local.spa.yml` so the FE talks to the project host.
|
|
25
|
+
|
|
26
|
+
Dashboard create deploys schema + APIs only. The Tiny FE tree is copied by `robodev create`.
|
|
27
|
+
|
|
28
|
+
`GET /api/me`, `GET/POST /api/messages`, and `POST /api/invite` require a Robodev Auth Bearer token. `GET /api/health` stays public. Invites send one email; posting a chat message does not. Auth stays reserved at `/api/user/*`. Do not add `api/user/**`.
|
|
29
|
+
|
|
30
|
+
Regenerate AI rules after editing `.rulesync/`:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun rules:gen
|
|
34
|
+
```
|
|
16
35
|
|
|
17
|
-
|
|
36
|
+
Docs: https://robodev.povio.dev/docs
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** POST /api/invite — signed-in users send a chat invite email. */
|
|
2
|
+
import { defineApi, z } from "@robodev-ai/sdk";
|
|
3
|
+
|
|
4
|
+
function headerValue(
|
|
5
|
+
headers: Record<string, string | string[] | undefined>,
|
|
6
|
+
name: string,
|
|
7
|
+
): string | undefined {
|
|
8
|
+
const raw = headers[name] ?? headers[name.toLowerCase()];
|
|
9
|
+
if (Array.isArray(raw)) return raw[0];
|
|
10
|
+
return typeof raw === "string" ? raw : undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolveProjectUrl(
|
|
14
|
+
env: Record<string, string>,
|
|
15
|
+
headers: Record<string, string | string[] | undefined>,
|
|
16
|
+
): string {
|
|
17
|
+
const fromEnv =
|
|
18
|
+
env.APP_PUBLIC_API_URL?.trim() || env.VITE_PUBLIC_API_URL?.trim() || env.VITE_API_URL?.trim();
|
|
19
|
+
if (fromEnv) return fromEnv.replace(/\/+$/, "");
|
|
20
|
+
const host = headerValue(headers, "x-forwarded-host") || headerValue(headers, "host");
|
|
21
|
+
if (!host) return "";
|
|
22
|
+
const proto =
|
|
23
|
+
headerValue(headers, "x-forwarded-proto") || (host.includes("localhost") ? "http" : "https");
|
|
24
|
+
return `${proto}://${host}`.replace(/\/+$/, "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const post = defineApi({
|
|
28
|
+
auth: "required",
|
|
29
|
+
body: z.object({ to: z.string().email() }),
|
|
30
|
+
response: z.object({ id: z.string(), status: z.literal("sent") }),
|
|
31
|
+
handler: async ({ email, body, user, env, headers }) => {
|
|
32
|
+
const url = resolveProjectUrl(env, headers);
|
|
33
|
+
const who = user?.name?.trim() || user?.email || "Someone";
|
|
34
|
+
const project = url ? ` Project URL: ${url}` : "";
|
|
35
|
+
return email.send({
|
|
36
|
+
to: body.to,
|
|
37
|
+
subject: "You're invited to the chat",
|
|
38
|
+
text:
|
|
39
|
+
`${who} invited you to a shared chat on Robodev. ` +
|
|
40
|
+
`Open the App URL and sign in to join.${project}`,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link
|
|
6
|
+
rel="icon"
|
|
7
|
+
type="image/svg+xml"
|
|
8
|
+
href="/favicon.svg"
|
|
9
|
+
/>
|
|
10
|
+
<meta
|
|
11
|
+
name="viewport"
|
|
12
|
+
content="width=device-width, initial-scale=1.0"
|
|
13
|
+
/>
|
|
14
|
+
<title>Space</title>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body class="bg-elevation-fill-default-2 text-text-default-1">
|
|
18
|
+
<div id="app"></div>
|
|
19
|
+
<script
|
|
20
|
+
type="module"
|
|
21
|
+
src="/src/main.tsx"
|
|
22
|
+
></script>
|
|
23
|
+
</body>
|
|
24
|
+
</html>
|