robodev 0.12.0 → 0.13.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.
Files changed (155) hide show
  1. package/package.json +1 -1
  2. package/src/emit-starter.test.ts +14 -0
  3. package/src/index.ts +12 -4
  4. package/templates/auth-chat/.config/local.spa.template.yml +27 -0
  5. package/templates/auth-chat/.oxlint-base.json +29 -0
  6. package/templates/auth-chat/.oxlintrc.json +78 -0
  7. package/templates/auth-chat/.rulesync/rules/frontend-api-boundary.md +36 -0
  8. package/templates/auth-chat/.rulesync/rules/frontend-component-structure.md +38 -0
  9. package/templates/auth-chat/.rulesync/rules/frontend-forms.md +32 -0
  10. package/templates/auth-chat/.rulesync/rules/frontend-notifications.md +24 -0
  11. package/templates/auth-chat/.rulesync/rules/frontend-povio-components.md +42 -0
  12. package/templates/auth-chat/.rulesync/rules/frontend-povio-ui.md +45 -0
  13. package/templates/auth-chat/.rulesync/rules/frontend-query-autocomplete.md +29 -0
  14. package/templates/auth-chat/.rulesync/rules/frontend-tables-and-lists.md +34 -0
  15. package/templates/auth-chat/.rulesync/rules/frontend-translations.md +26 -0
  16. package/templates/auth-chat/.rulesync/rules/project-overview.md +32 -0
  17. package/templates/auth-chat/.rulesync/rules/robodev-api.md +30 -0
  18. package/templates/auth-chat/.rulesync/rules/robodev-auth.md +40 -0
  19. package/templates/auth-chat/.rulesync/rules/robodev-database.md +22 -0
  20. package/templates/auth-chat/.rulesync/rules/role-based-app-structure.md +36 -0
  21. package/templates/auth-chat/.rulesync/skills/media-feature/SKILL.md +21 -0
  22. package/templates/auth-chat/.rulesync/skills/povio-ui-styling/SKILL.md +208 -0
  23. package/templates/auth-chat/.rulesync/skills/povio-ui-styling/agents/openai.yaml +4 -0
  24. package/templates/auth-chat/.rulesync/skills/robodev-api-route/SKILL.md +13 -0
  25. package/templates/auth-chat/.rulesync/skills/robodev-table/SKILL.md +12 -0
  26. package/templates/auth-chat/README.md +26 -7
  27. package/templates/auth-chat/api/invite.ts +43 -0
  28. package/templates/auth-chat/apps/fe/index.html +24 -0
  29. package/templates/auth-chat/apps/fe/openapi-codegen.config.ts +37 -0
  30. package/templates/auth-chat/apps/fe/package.json +82 -0
  31. package/templates/auth-chat/apps/fe/public/apple-touch-icon.png +0 -0
  32. package/templates/auth-chat/apps/fe/public/favicon-96x96.png +0 -0
  33. package/templates/auth-chat/apps/fe/public/favicon.ico +0 -0
  34. package/templates/auth-chat/apps/fe/public/favicon.svg +3 -0
  35. package/templates/auth-chat/apps/fe/public/site.webmanifest +21 -0
  36. package/templates/auth-chat/apps/fe/public/web-app-manifest-192x192.png +0 -0
  37. package/templates/auth-chat/apps/fe/public/web-app-manifest-512x512.png +0 -0
  38. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Bold.otf +0 -0
  39. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Medium.otf +0 -0
  40. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Regular.otf +0 -0
  41. package/templates/auth-chat/apps/fe/src/assets/fonts/GeneralSans-Semibold.otf +0 -0
  42. package/templates/auth-chat/apps/fe/src/assets/locales/en/translation.json +202 -0
  43. package/templates/auth-chat/apps/fe/src/assets/locales/sl/translation.json +202 -0
  44. package/templates/auth-chat/apps/fe/src/clients/app-rest-client.ts +15 -0
  45. package/templates/auth-chat/apps/fe/src/clients/auth-token-store.ts +101 -0
  46. package/templates/auth-chat/apps/fe/src/clients/rest/app-error-handler.ts +31 -0
  47. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/authorization-header.interceptor.ts +15 -0
  48. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/refresh-token.interceptor.ts +37 -0
  49. package/templates/auth-chat/apps/fe/src/clients/rest/interceptors/response.interceptor.ts +17 -0
  50. package/templates/auth-chat/apps/fe/src/components/404.tsx +18 -0
  51. package/templates/auth-chat/apps/fe/src/components/features/auth/AuthBrandPanel.tsx +60 -0
  52. package/templates/auth-chat/apps/fe/src/components/features/auth/AuthLayout.tsx +23 -0
  53. package/templates/auth-chat/apps/fe/src/components/features/auth/LoginPage.tsx +111 -0
  54. package/templates/auth-chat/apps/fe/src/components/features/auth/RegisterPage.tsx +143 -0
  55. package/templates/auth-chat/apps/fe/src/components/features/chat/ChatPage.tsx +105 -0
  56. package/templates/auth-chat/apps/fe/src/components/features/chat/InviteForm.tsx +84 -0
  57. package/templates/auth-chat/apps/fe/src/components/features/profile/ProfilePage.tsx +96 -0
  58. package/templates/auth-chat/apps/fe/src/components/googleAnalytics/GoogleAnalytics.tsx +35 -0
  59. package/templates/auth-chat/apps/fe/src/components/layout/AppLayout.tsx +19 -0
  60. package/templates/auth-chat/apps/fe/src/components/layout/app-header/AppHeader.tsx +91 -0
  61. package/templates/auth-chat/apps/fe/src/components/layout/app-header/MobileNavigation.tsx +51 -0
  62. package/templates/auth-chat/apps/fe/src/components/layout/app-header/NavLink.tsx +29 -0
  63. package/templates/auth-chat/apps/fe/src/components/shared/branding/BrandLogo.tsx +13 -0
  64. package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorFallback.tsx +43 -0
  65. package/templates/auth-chat/apps/fe/src/components/shared/error/ErrorText.tsx +20 -0
  66. package/templates/auth-chat/apps/fe/src/components/shared/error/NotFound.tsx +36 -0
  67. package/templates/auth-chat/apps/fe/src/components/shared/forms/RequiredLabel.tsx +21 -0
  68. package/templates/auth-chat/apps/fe/src/components/shared/forms/RowInputWrapper.tsx +50 -0
  69. package/templates/auth-chat/apps/fe/src/components/shared/head/AppHead.tsx +32 -0
  70. package/templates/auth-chat/apps/fe/src/components/shared/head/DefaultAppHead.tsx +34 -0
  71. package/templates/auth-chat/apps/fe/src/components/shared/layout/LoadingState.tsx +9 -0
  72. package/templates/auth-chat/apps/fe/src/components/shared/layout/ThinPageWrapper.tsx +5 -0
  73. package/templates/auth-chat/apps/fe/src/components/shared/page/PageHeader.tsx +69 -0
  74. package/templates/auth-chat/apps/fe/src/components/shared/ui/Card.tsx +60 -0
  75. package/templates/auth-chat/apps/fe/src/components/shared/ui/GoogleLoginButton.tsx +19 -0
  76. package/templates/auth-chat/apps/fe/src/components/shared/ui/RequiredLabel.tsx +22 -0
  77. package/templates/auth-chat/apps/fe/src/components/shared/ui/TableActions.tsx +22 -0
  78. package/templates/auth-chat/apps/fe/src/config/app.config.ts +35 -0
  79. package/templates/auth-chat/apps/fe/src/config/i18n.ts +43 -0
  80. package/templates/auth-chat/apps/fe/src/config/inits/a11y.ts +14 -0
  81. package/templates/auth-chat/apps/fe/src/config/inits/logger.ts +7 -0
  82. package/templates/auth-chat/apps/fe/src/config/inits/sentry.ts +21 -0
  83. package/templates/auth-chat/apps/fe/src/config/jwt.config.ts +2 -0
  84. package/templates/auth-chat/apps/fe/src/config/query.config.ts +20 -0
  85. package/templates/auth-chat/apps/fe/src/hooks/useAuth.ts +5 -0
  86. package/templates/auth-chat/apps/fe/src/main.tsx +52 -0
  87. package/templates/auth-chat/apps/fe/src/pages/(guest)/login.tsx +23 -0
  88. package/templates/auth-chat/apps/fe/src/pages/(guest)/register.tsx +23 -0
  89. package/templates/auth-chat/apps/fe/src/pages/(guest)/route.tsx +18 -0
  90. package/templates/auth-chat/apps/fe/src/pages/(private)/index.tsx +23 -0
  91. package/templates/auth-chat/apps/fe/src/pages/(private)/profile.tsx +23 -0
  92. package/templates/auth-chat/apps/fe/src/pages/(private)/route.tsx +18 -0
  93. package/templates/auth-chat/apps/fe/src/pages/(public)/auth.tsx +37 -0
  94. package/templates/auth-chat/apps/fe/src/pages/(public)/route.tsx +9 -0
  95. package/templates/auth-chat/apps/fe/src/pages/__root.tsx +164 -0
  96. package/templates/auth-chat/apps/fe/src/providers/AppErrorBoundary.tsx +10 -0
  97. package/templates/auth-chat/apps/fe/src/providers/OpenApiRuntimeProvider.tsx +31 -0
  98. package/templates/auth-chat/apps/fe/src/providers/index.tsx +44 -0
  99. package/templates/auth-chat/apps/fe/src/providers/jwt.provider.tsx +95 -0
  100. package/templates/auth-chat/apps/fe/src/routeTree.gen.ts +225 -0
  101. package/templates/auth-chat/apps/fe/src/styles/base.css +103 -0
  102. package/templates/auth-chat/apps/fe/src/styles/fonts/fonts.tsx +10 -0
  103. package/templates/auth-chat/apps/fe/src/styles/fonts/general-sans.css +31 -0
  104. package/templates/auth-chat/apps/fe/src/styles/globals.css +28 -0
  105. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/button.override.ts +536 -0
  106. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/checkbox.override.ts +71 -0
  107. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/input.override.ts +252 -0
  108. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/label.override.ts +91 -0
  109. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/modal.override.ts +57 -0
  110. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/radio.override.ts +46 -0
  111. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/table.override.ts +104 -0
  112. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/tag.override.ts +66 -0
  113. package/templates/auth-chat/apps/fe/src/styles/overrides/defaults/typography.override.ts +115 -0
  114. package/templates/auth-chat/apps/fe/src/styles/overrides/outline.clsx.ts +10 -0
  115. package/templates/auth-chat/apps/fe/src/styles/overrides/uiOverrides.override.ts +60 -0
  116. package/templates/auth-chat/apps/fe/src/styles/theme.css +2177 -0
  117. package/templates/auth-chat/apps/fe/src/types/i18next.d.ts +12 -0
  118. package/templates/auth-chat/apps/fe/src/types/table.d.ts +17 -0
  119. package/templates/auth-chat/apps/fe/src/types/ui.d.ts +26 -0
  120. package/templates/auth-chat/apps/fe/src/types/vite-env.d.ts +20 -0
  121. package/templates/auth-chat/apps/fe/src/utils/date.utils.ts +17 -0
  122. package/templates/auth-chat/apps/fe/src/utils/number.utils.ts +12 -0
  123. package/templates/auth-chat/apps/fe/src/utils/string.utils.ts +5 -0
  124. package/templates/auth-chat/apps/fe/src/vite-env.d.ts +1 -0
  125. package/templates/auth-chat/apps/fe/tsconfig.app.json +32 -0
  126. package/templates/auth-chat/apps/fe/tsconfig.json +9 -0
  127. package/templates/auth-chat/apps/fe/tsconfig.node.json +31 -0
  128. package/templates/auth-chat/apps/fe/vite.config.ts +94 -0
  129. package/templates/auth-chat/openapi.json +1016 -0
  130. package/templates/auth-chat/oxfmt.config.js +23 -0
  131. package/templates/auth-chat/package.json +6 -13
  132. package/templates/auth-chat/rulesync.jsonc +18 -0
  133. package/templates/auth-chat/tsconfig.json +2 -4
  134. package/templates/backend/README.md +2 -2
  135. package/templates/backend/api/_lib.ts +359 -0
  136. package/templates/backend/api/aliens/labels.ts +19 -0
  137. package/templates/backend/api/files/presigned-url.ts +48 -0
  138. package/templates/backend/api/files/upload.ts +29 -0
  139. package/templates/backend/api/planets/[id]/like.ts +36 -0
  140. package/templates/backend/api/planets/[id].ts +56 -13
  141. package/templates/backend/api/planets/paginate.ts +36 -0
  142. package/templates/backend/api/planets.ts +52 -44
  143. package/templates/backend/database.ts +33 -13
  144. package/templates/backend/package.json +1 -1
  145. package/templates/catalog.json +2 -2
  146. package/templates/empty/package.json +1 -1
  147. package/templates/space/package.json +1 -1
  148. package/templates/auth-chat/index.html +0 -201
  149. package/templates/auth-chat/src/main.tsx +0 -282
  150. package/templates/auth-chat/vite.config.ts +0 -7
  151. package/templates/backend/api/health.ts +0 -7
  152. package/templates/backend/api/launch.ts +0 -29
  153. package/templates/backend/api/me.ts +0 -14
  154. package/templates/backend/api/motto.ts +0 -9
  155. package/templates/backend/api/rockets.ts +0 -43
@@ -0,0 +1,36 @@
1
+ import { defineApi } from "@robodev-ai/sdk";
2
+ import {
3
+ ensureSeed,
4
+ enrichPlanet,
5
+ filterPlanetRows,
6
+ loadLookups,
7
+ paginate,
8
+ parseFilter,
9
+ planets,
10
+ sortPlanets,
11
+ } from "../_lib";
12
+
13
+ export const get = defineApi({
14
+ auth: "required",
15
+ handler: async (ctx) => {
16
+ await ensureSeed(ctx.db, ctx.user!.id);
17
+ const query = ctx.query as Record<string, unknown>;
18
+ const rows = await ctx.db.select().from(planets);
19
+ const lookups = await loadLookups(ctx.db);
20
+ const filter = parseFilter(query);
21
+ const filtered = filterPlanetRows(rows, lookups.alienById, filter);
22
+ const page = paginate(
23
+ sortPlanets(filtered, typeof query.order === "string" ? query.order : undefined),
24
+ Number(query.page) || undefined,
25
+ Number(query.limit) || undefined,
26
+ );
27
+ return {
28
+ ...page,
29
+ items: await Promise.all(
30
+ page.items.map((row) =>
31
+ enrichPlanet(ctx, row, ctx.user!.id, lookups.alienById, lookups.likes),
32
+ ),
33
+ ),
34
+ };
35
+ },
36
+ });
@@ -1,60 +1,68 @@
1
- /** GET/POST /api/planets — list (seeds sample rows on first GET) and create planets. */
2
- import { planets, rockets } from "../database";
3
1
  import { defineApi, z } from "@robodev-ai/sdk";
2
+ import {
3
+ ensureSeed,
4
+ enrichOne,
5
+ enrichPlanet,
6
+ fail,
7
+ filterPlanetRows,
8
+ loadLookups,
9
+ parseFilter,
10
+ planets,
11
+ sortPlanets,
12
+ validateAlien,
13
+ validateImage,
14
+ } from "./_lib";
4
15
 
5
- const EARTH = "11111111-1111-1111-1111-111111111111";
6
- const MARS = "22222222-2222-2222-2222-222222222222";
7
- const KEPLER = "33333333-3333-3333-3333-333333333333";
8
- const VOYAGER = "44444444-4444-4444-4444-444444444444";
9
- const ODYSSEY = "55555555-5555-5555-5555-555555555555";
16
+ const imageRequest = z.object({ id: z.string() }).nullish();
10
17
 
11
- const Planet = z.object({
12
- id: z.string(),
13
- name: z.string(),
14
- climate: z.string(),
15
- moons: z.coerce.number(),
18
+ const planetBody = z.object({
19
+ name: z.string().min(1),
20
+ alienId: z.string().nullish(),
21
+ discoveryDate: z.string().nullish(),
22
+ description: z.string().nullish(),
23
+ image: imageRequest,
16
24
  });
17
25
 
18
26
  export const get = defineApi({
19
- response: z.array(Planet),
20
- handler: async ({ db }) => {
21
- const existing = await db.select().from(planets).limit(1);
22
- if (existing.length === 0) {
23
- try {
24
- await db.insert(planets).values([
25
- { id: EARTH, name: "Earth", climate: "temperate", moons: 1 },
26
- { id: MARS, name: "Mars", climate: "arid", moons: 2 },
27
- { id: KEPLER, name: "Kepler-452b", climate: "unknown", moons: 0 },
28
- ]);
29
- await db.insert(rockets).values([
30
- { id: VOYAGER, name: "Voyager", destinationId: MARS, launched: false },
31
- { id: ODYSSEY, name: "Odyssey", destinationId: KEPLER, launched: true },
32
- ]);
33
- } catch {
34
- // First request won the seed race.
35
- }
36
- }
37
- const rows = await db.select().from(planets);
38
- return Planet.array().parse(rows);
27
+ auth: "required",
28
+ handler: async (ctx) => {
29
+ await ensureSeed(ctx.db, ctx.user!.id);
30
+ const rows = await ctx.db.select().from(planets);
31
+ const lookups = await loadLookups(ctx.db);
32
+ const query = ctx.query as Record<string, unknown>;
33
+ const filtered = filterPlanetRows(rows, lookups.alienById, parseFilter(query));
34
+ const order = typeof query.order === "string" ? query.order : undefined;
35
+ return Promise.all(
36
+ sortPlanets(filtered, order).map((row) =>
37
+ enrichPlanet(ctx, row, ctx.user!.id, lookups.alienById, lookups.likes),
38
+ ),
39
+ );
39
40
  },
40
41
  });
41
42
 
42
43
  export const post = defineApi({
43
- body: z.object({
44
- name: z.string().min(1),
45
- climate: z.string().min(1),
46
- moons: z.coerce.number().int().min(0).default(0),
47
- }),
48
- response: Planet,
49
- handler: async ({ db, body }) => {
50
- const [row] = await db
44
+ auth: "required",
45
+ body: planetBody,
46
+ handler: async (ctx) => {
47
+ const alienId = ctx.body.alienId || null;
48
+ if (alienId && !(await validateAlien(ctx.db, alienId))) {
49
+ return fail(404, "Alien not found");
50
+ }
51
+ const image = await validateImage(ctx.db, ctx.body.image, ctx.user!.id);
52
+ if (image && "error" in image) return image.error;
53
+
54
+ const [row] = await ctx.db
51
55
  .insert(planets)
52
56
  .values({
53
- name: body.name,
54
- climate: body.climate,
55
- moons: body.moons,
57
+ userId: ctx.user!.id,
58
+ creatorName: ctx.user!.name ?? ctx.user!.email,
59
+ alienId,
60
+ discoveryDate: ctx.body.discoveryDate ? new Date(ctx.body.discoveryDate) : null,
61
+ name: ctx.body.name,
62
+ description: ctx.body.description ?? null,
63
+ imageId: image?.id ?? null,
56
64
  })
57
65
  .returning();
58
- return Planet.parse(row);
66
+ return enrichOne(ctx, row!, ctx.user!.id);
59
67
  },
60
68
  });
@@ -1,27 +1,47 @@
1
- /**
2
- * Starter schema. `robodev deploy` creates the `space` database and these tables
3
- * (no migration files). Edit here and deploy again to sync.
4
- */
5
- import { boolean, defineDatabase, integer, pgTable, text, timestamp, uuid } from "@robodev-ai/sdk";
1
+ import { defineDatabase, integer, pgTable, text, timestamp, uuid } from "@robodev-ai/sdk";
6
2
 
7
- export const planets = pgTable("planets", {
3
+ export const aliens = pgTable("aliens", {
8
4
  id: uuid("id").primaryKey().defaultRandom(),
9
5
  name: text("name").notNull(),
10
- climate: text("climate").notNull(),
11
- moons: integer("moons").default(0),
6
+ createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
7
+ updatedAt: timestamp("updatedAt", { withTimezone: true }).defaultNow(),
12
8
  });
13
9
 
14
- export const rockets = pgTable("rockets", {
10
+ export const media = pgTable("media", {
15
11
  id: uuid("id").primaryKey().defaultRandom(),
12
+ key: text("key").notNull(),
13
+ userId: text("userId").notNull(),
14
+ resourceName: text("resourceName").notNull(),
15
+ fileName: text("fileName").notNull(),
16
+ fileSize: integer("fileSize").notNull(),
17
+ mimeType: text("mimeType").notNull(),
18
+ uploaded: timestamp("uploaded", { withTimezone: true }),
19
+ createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
20
+ });
21
+
22
+ export const planets = pgTable("planets", {
23
+ id: uuid("id").primaryKey().defaultRandom(),
24
+ userId: text("userId").notNull(),
25
+ creatorName: text("creatorName"),
26
+ alienId: uuid("alienId").references(() => aliens.id, { onDelete: "set null" }),
27
+ discoveryDate: timestamp("discoveryDate", { withTimezone: true }),
16
28
  name: text("name").notNull(),
17
- destinationId: uuid("destinationId")
29
+ description: text("description"),
30
+ imageId: uuid("imageId").references(() => media.id, { onDelete: "set null" }),
31
+ createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
32
+ updatedAt: timestamp("updatedAt", { withTimezone: true }).defaultNow(),
33
+ });
34
+
35
+ export const planetLikes = pgTable("planet_likes", {
36
+ id: uuid("id").primaryKey().defaultRandom(),
37
+ planetId: uuid("planetId")
18
38
  .notNull()
19
39
  .references(() => planets.id, { onDelete: "cascade" }),
20
- launched: boolean("launched").default(false),
40
+ userId: text("userId").notNull(),
21
41
  createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
22
42
  });
23
43
 
24
44
  export default defineDatabase({
25
- name: "space",
26
- tables: { planets, rockets },
45
+ name: "tiny",
46
+ tables: { aliens, media, planets, planetLikes },
27
47
  });
@@ -10,7 +10,7 @@
10
10
  "@robodev-ai/sdk": "^0.6.0"
11
11
  },
12
12
  "devDependencies": {
13
- "robodev": "^0.12.0",
13
+ "robodev": "^0.13.0",
14
14
  "typescript": "^5.9.2"
15
15
  }
16
16
  }
@@ -8,12 +8,12 @@
8
8
  {
9
9
  "id": "auth-chat",
10
10
  "title": "Auth chat",
11
- "description": "Email + Google sign-in and a shared chat room"
11
+ "description": "Tiny chat APIs plus a local Povio UI in apps/fe, with invite email"
12
12
  },
13
13
  {
14
14
  "id": "backend",
15
15
  "title": "Backend",
16
- "description": "Space APIs only — no Vite UI. For Lovable, Bolt, or v0",
16
+ "description": "Tiny Template planets and aliens APIs — no UI. For Lovable, Bolt, or v0",
17
17
  "kind": "backend"
18
18
  },
19
19
  {
@@ -10,7 +10,7 @@
10
10
  "@robodev-ai/sdk": "^0.6.0"
11
11
  },
12
12
  "devDependencies": {
13
- "robodev": "^0.12.0",
13
+ "robodev": "^0.13.0",
14
14
  "typescript": "^5.9.2"
15
15
  }
16
16
  }
@@ -11,7 +11,7 @@
11
11
  "@robodev-ai/sdk": "^0.6.0"
12
12
  },
13
13
  "devDependencies": {
14
- "robodev": "^0.12.0",
14
+ "robodev": "^0.13.0",
15
15
  "rulesync": "^8.18.0",
16
16
  "typescript": "^5.9.2"
17
17
  }
@@ -1,201 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>{{NAME}}</title>
7
- <style>
8
- :root {
9
- color-scheme: dark;
10
- --bg: #10131a;
11
- --panel: #181c26;
12
- --line: #2a3140;
13
- --text: #f2eee6;
14
- --muted: #9aa3b2;
15
- --accent: #7ee0c6;
16
- --ink: #10221c;
17
- --mine: #243f38;
18
- --theirs: #222733;
19
- --danger: #f0a0a0;
20
- }
21
- * {
22
- box-sizing: border-box;
23
- }
24
- body {
25
- margin: 0;
26
- min-height: 100vh;
27
- background:
28
- radial-gradient(900px 420px at 10% -10%, #1d3a34 0%, transparent 55%), var(--bg);
29
- color: var(--text);
30
- font:
31
- 15px/1.5 ui-sans-serif,
32
- system-ui,
33
- sans-serif;
34
- }
35
- main {
36
- max-width: 720px;
37
- margin: 0 auto;
38
- padding: 36px 20px 48px;
39
- }
40
- main.narrow {
41
- max-width: 420px;
42
- }
43
- main.chat {
44
- display: flex;
45
- flex-direction: column;
46
- min-height: 100vh;
47
- padding-bottom: 24px;
48
- }
49
- h1 {
50
- font-size: 28px;
51
- font-weight: 650;
52
- letter-spacing: -0.04em;
53
- margin: 0 0 4px;
54
- }
55
- .eyebrow {
56
- margin: 0 0 6px;
57
- color: var(--accent);
58
- font-size: 12px;
59
- letter-spacing: 0.08em;
60
- text-transform: uppercase;
61
- }
62
- .lede {
63
- color: var(--muted);
64
- margin: 0 0 22px;
65
- overflow-wrap: anywhere;
66
- }
67
- .error {
68
- color: var(--danger);
69
- margin: 0 0 16px;
70
- }
71
- .tabs {
72
- display: flex;
73
- gap: 8px;
74
- margin-bottom: 14px;
75
- }
76
- .tab,
77
- .ghost,
78
- button {
79
- font: inherit;
80
- cursor: pointer;
81
- }
82
- .tab {
83
- background: transparent;
84
- color: var(--muted);
85
- border: 1px solid var(--line);
86
- border-radius: 999px;
87
- padding: 6px 12px;
88
- }
89
- .tab.on {
90
- color: var(--ink);
91
- background: var(--accent);
92
- border-color: var(--accent);
93
- }
94
- form {
95
- display: grid;
96
- gap: 10px;
97
- }
98
- label {
99
- display: grid;
100
- gap: 4px;
101
- font-size: 12px;
102
- color: var(--muted);
103
- }
104
- input,
105
- button {
106
- color: var(--text);
107
- }
108
- input {
109
- background: var(--panel);
110
- border: 1px solid var(--line);
111
- border-radius: 8px;
112
- padding: 10px 12px;
113
- }
114
- button[type="submit"],
115
- .google {
116
- background: var(--accent);
117
- color: var(--ink);
118
- border: 0;
119
- border-radius: 8px;
120
- padding: 10px 12px;
121
- font-weight: 650;
122
- }
123
- .google {
124
- width: 100%;
125
- background: #fff;
126
- color: #1f1f1f;
127
- }
128
- .ghost {
129
- background: transparent;
130
- color: var(--accent);
131
- border: 1px solid var(--accent);
132
- border-radius: 8px;
133
- padding: 8px 12px;
134
- height: fit-content;
135
- }
136
- button:disabled {
137
- opacity: 0.5;
138
- cursor: default;
139
- }
140
- .or {
141
- text-align: center;
142
- color: var(--muted);
143
- margin: 16px 0 12px;
144
- font-size: 12px;
145
- }
146
- header {
147
- display: flex;
148
- justify-content: space-between;
149
- align-items: flex-start;
150
- gap: 16px;
151
- margin-bottom: 16px;
152
- }
153
- .transcript {
154
- flex: 1;
155
- min-height: 320px;
156
- max-height: calc(100vh - 240px);
157
- overflow: auto;
158
- display: flex;
159
- flex-direction: column;
160
- gap: 10px;
161
- padding: 12px;
162
- background: var(--panel);
163
- border: 1px solid var(--line);
164
- border-radius: 12px;
165
- margin-bottom: 12px;
166
- }
167
- .empty {
168
- color: var(--muted);
169
- margin: auto;
170
- }
171
- .bubble {
172
- max-width: 80%;
173
- padding: 8px 12px;
174
- border-radius: 12px;
175
- background: var(--theirs);
176
- }
177
- .bubble.mine {
178
- align-self: flex-end;
179
- background: var(--mine);
180
- }
181
- .bubble p {
182
- margin: 0;
183
- white-space: pre-wrap;
184
- }
185
- .meta {
186
- color: var(--muted);
187
- font-size: 12px;
188
- margin-bottom: 2px;
189
- }
190
- .composer {
191
- display: grid;
192
- grid-template-columns: 1fr auto;
193
- gap: 8px;
194
- }
195
- </style>
196
- </head>
197
- <body>
198
- <div id="root"></div>
199
- <script type="module" src="/src/main.tsx"></script>
200
- </body>
201
- </html>