create-chaaskit 0.1.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 (122) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.d.ts.map +1 -0
  3. package/dist/cli.js +25 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/commands/add-infra.d.ts +6 -0
  6. package/dist/commands/add-infra.d.ts.map +1 -0
  7. package/dist/commands/add-infra.js +160 -0
  8. package/dist/commands/add-infra.js.map +1 -0
  9. package/dist/commands/build.d.ts +2 -0
  10. package/dist/commands/build.d.ts.map +1 -0
  11. package/dist/commands/build.js +63 -0
  12. package/dist/commands/build.js.map +1 -0
  13. package/dist/commands/db-sync.d.ts +13 -0
  14. package/dist/commands/db-sync.d.ts.map +1 -0
  15. package/dist/commands/db-sync.js +108 -0
  16. package/dist/commands/db-sync.js.map +1 -0
  17. package/dist/commands/dev.d.ts +7 -0
  18. package/dist/commands/dev.d.ts.map +1 -0
  19. package/dist/commands/dev.js +61 -0
  20. package/dist/commands/dev.js.map +1 -0
  21. package/dist/commands/init.d.ts +9 -0
  22. package/dist/commands/init.d.ts.map +1 -0
  23. package/dist/commands/init.js +214 -0
  24. package/dist/commands/init.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +57 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/templates/.env.example +24 -0
  30. package/dist/templates/README.md +81 -0
  31. package/dist/templates/app/components/AcceptInviteClient.tsx +10 -0
  32. package/dist/templates/app/components/AdminDashboardClient.tsx +10 -0
  33. package/dist/templates/app/components/AdminTeamClient.tsx +10 -0
  34. package/dist/templates/app/components/AdminTeamsClient.tsx +10 -0
  35. package/dist/templates/app/components/AdminUsersClient.tsx +10 -0
  36. package/dist/templates/app/components/ApiKeysClient.tsx +10 -0
  37. package/dist/templates/app/components/AutomationsClient.tsx +10 -0
  38. package/dist/templates/app/components/ChatClient.tsx +13 -0
  39. package/dist/templates/app/components/ClientOnly.tsx +6 -0
  40. package/dist/templates/app/components/DocumentsClient.tsx +10 -0
  41. package/dist/templates/app/components/OAuthConsentClient.tsx +10 -0
  42. package/dist/templates/app/components/PricingClient.tsx +10 -0
  43. package/dist/templates/app/components/TeamSettingsClient.tsx +10 -0
  44. package/dist/templates/app/components/VerifyEmailClient.tsx +10 -0
  45. package/dist/templates/app/entry.client.tsx +12 -0
  46. package/dist/templates/app/entry.server.tsx +67 -0
  47. package/dist/templates/app/root.tsx +91 -0
  48. package/dist/templates/app/routes/_index.tsx +82 -0
  49. package/dist/templates/app/routes/admin._index.tsx +57 -0
  50. package/dist/templates/app/routes/admin.teams.$teamId.tsx +57 -0
  51. package/dist/templates/app/routes/admin.teams._index.tsx +57 -0
  52. package/dist/templates/app/routes/admin.users.tsx +57 -0
  53. package/dist/templates/app/routes/api-keys.tsx +57 -0
  54. package/dist/templates/app/routes/automations.tsx +57 -0
  55. package/dist/templates/app/routes/chat._index.tsx +11 -0
  56. package/dist/templates/app/routes/chat.admin._index.tsx +10 -0
  57. package/dist/templates/app/routes/chat.admin.teams.$teamId.tsx +10 -0
  58. package/dist/templates/app/routes/chat.admin.teams._index.tsx +10 -0
  59. package/dist/templates/app/routes/chat.admin.users.tsx +10 -0
  60. package/dist/templates/app/routes/chat.api-keys.tsx +10 -0
  61. package/dist/templates/app/routes/chat.automations.tsx +10 -0
  62. package/dist/templates/app/routes/chat.documents.tsx +10 -0
  63. package/dist/templates/app/routes/chat.team.$teamId.settings.tsx +10 -0
  64. package/dist/templates/app/routes/chat.thread.$threadId.tsx +11 -0
  65. package/dist/templates/app/routes/chat.tsx +39 -0
  66. package/dist/templates/app/routes/documents.tsx +57 -0
  67. package/dist/templates/app/routes/invite.$token.tsx +10 -0
  68. package/dist/templates/app/routes/login.tsx +334 -0
  69. package/dist/templates/app/routes/oauth.consent.tsx +10 -0
  70. package/dist/templates/app/routes/pricing.tsx +10 -0
  71. package/dist/templates/app/routes/privacy.tsx +197 -0
  72. package/dist/templates/app/routes/register.tsx +398 -0
  73. package/dist/templates/app/routes/shared.$shareId.tsx +226 -0
  74. package/dist/templates/app/routes/team.$teamId.settings.tsx +57 -0
  75. package/dist/templates/app/routes/terms.tsx +173 -0
  76. package/dist/templates/app/routes/thread.$threadId.tsx +102 -0
  77. package/dist/templates/app/routes/verify-email.tsx +10 -0
  78. package/dist/templates/app/routes.ts +47 -0
  79. package/dist/templates/config/app.config.ts +216 -0
  80. package/dist/templates/docs/admin.md +257 -0
  81. package/dist/templates/docs/api-keys.md +403 -0
  82. package/dist/templates/docs/authentication.md +247 -0
  83. package/dist/templates/docs/configuration.md +1212 -0
  84. package/dist/templates/docs/custom-pages.md +466 -0
  85. package/dist/templates/docs/deployment.md +362 -0
  86. package/dist/templates/docs/development.md +411 -0
  87. package/dist/templates/docs/documents.md +293 -0
  88. package/dist/templates/docs/extensions.md +639 -0
  89. package/dist/templates/docs/index.md +139 -0
  90. package/dist/templates/docs/installation.md +286 -0
  91. package/dist/templates/docs/mcp.md +952 -0
  92. package/dist/templates/docs/native-tools.md +688 -0
  93. package/dist/templates/docs/queue.md +514 -0
  94. package/dist/templates/docs/scheduled-prompts.md +279 -0
  95. package/dist/templates/docs/settings.md +415 -0
  96. package/dist/templates/docs/slack.md +318 -0
  97. package/dist/templates/docs/styling.md +288 -0
  98. package/dist/templates/extensions/agents/.gitkeep +0 -0
  99. package/dist/templates/extensions/pages/.gitkeep +0 -0
  100. package/dist/templates/extensions/payment-plans/.gitkeep +0 -0
  101. package/dist/templates/index.html +16 -0
  102. package/dist/templates/infra-aws/.github/workflows/deploy.yml +95 -0
  103. package/dist/templates/infra-aws/README.md +207 -0
  104. package/dist/templates/infra-aws/bin/cdk.ts +18 -0
  105. package/dist/templates/infra-aws/cdk.json +43 -0
  106. package/dist/templates/infra-aws/config/deployment.ts +156 -0
  107. package/dist/templates/infra-aws/lib/chaaskit-stack.ts +419 -0
  108. package/dist/templates/infra-aws/package.json +27 -0
  109. package/dist/templates/infra-aws/scripts/build-app.sh +63 -0
  110. package/dist/templates/infra-aws/tsconfig.json +25 -0
  111. package/dist/templates/package.json +46 -0
  112. package/dist/templates/prisma/schema/base.prisma +584 -0
  113. package/dist/templates/prisma/schema/custom.prisma +24 -0
  114. package/dist/templates/prisma/schema.prisma +271 -0
  115. package/dist/templates/public/favicon.svg +4 -0
  116. package/dist/templates/public/logo.svg +4 -0
  117. package/dist/templates/react-router.config.ts +11 -0
  118. package/dist/templates/server.js +52 -0
  119. package/dist/templates/src/main.tsx +8 -0
  120. package/dist/templates/tsconfig.json +26 -0
  121. package/dist/templates/vite.config.ts +26 -0
  122. package/package.json +46 -0
@@ -0,0 +1,57 @@
1
+ import type { Route } from './+types/documents';
2
+ import { lazy, Suspense } from 'react';
3
+ import { ClientOnly } from '../components/ClientOnly';
4
+
5
+ const DocumentsClient = lazy(() => import('../components/DocumentsClient'));
6
+
7
+ export function meta({}: Route.MetaArgs) {
8
+ return [{ title: 'Documents' }];
9
+ }
10
+
11
+ export function links() {
12
+ return [{ rel: 'stylesheet', href: '/node_modules/@chaaskit/client/dist/lib/styles.css' }];
13
+ }
14
+
15
+ export default function Documents() {
16
+ return (
17
+ <ClientOnly fallback={<LoadingSkeleton />}>
18
+ {() => (
19
+ <Suspense fallback={<LoadingSkeleton />}>
20
+ <DocumentsClient />
21
+ </Suspense>
22
+ )}
23
+ </ClientOnly>
24
+ );
25
+ }
26
+
27
+ function LoadingSkeleton() {
28
+ return (
29
+ <div
30
+ style={{
31
+ display: 'flex',
32
+ height: '100vh',
33
+ alignItems: 'center',
34
+ justifyContent: 'center',
35
+ backgroundColor: 'rgb(var(--color-background))',
36
+ }}
37
+ >
38
+ <div
39
+ style={{
40
+ width: '2rem',
41
+ height: '2rem',
42
+ border: '2px solid rgb(var(--color-primary))',
43
+ borderTopColor: 'transparent',
44
+ borderRadius: '50%',
45
+ animation: 'spin 1s linear infinite',
46
+ }}
47
+ />
48
+ <style>
49
+ {`
50
+ @keyframes spin {
51
+ to { transform: rotate(360deg); }
52
+ }
53
+ `}
54
+ </style>
55
+ </div>
56
+ );
57
+ }
@@ -0,0 +1,10 @@
1
+ import { createRoute } from '@chaaskit/client/ssr-utils';
2
+
3
+ const route = createRoute({
4
+ title: 'Accept Invitation',
5
+ load: () => import('@chaaskit/client/routes/AcceptInviteRoute'),
6
+ });
7
+
8
+ export const meta = route.meta;
9
+ export const links = route.links;
10
+ export default route.default;
@@ -0,0 +1,334 @@
1
+ import { Form, redirect, useNavigation } from 'react-router';
2
+ import type { Route } from './+types/login';
3
+ import { config } from '../../config/app.config';
4
+
5
+ export async function loader({}: Route.LoaderArgs) {
6
+ return { config };
7
+ }
8
+
9
+ export async function action({ request }: Route.ActionArgs) {
10
+ const formData = await request.formData();
11
+ const email = formData.get('email') as string;
12
+ const password = formData.get('password') as string;
13
+
14
+ const apiUrl = process.env.API_URL || 'http://localhost:3000';
15
+ const res = await fetch(`${apiUrl}/api/auth/login`, {
16
+ method: 'POST',
17
+ headers: { 'Content-Type': 'application/json' },
18
+ body: JSON.stringify({ email, password }),
19
+ });
20
+
21
+ if (!res.ok) {
22
+ // Try to parse JSON, fallback to text for rate limit errors etc.
23
+ let errorMessage = 'Login failed';
24
+ const contentType = res.headers.get('content-type');
25
+ if (contentType?.includes('application/json')) {
26
+ try {
27
+ const data = await res.json();
28
+ // Handle both {error: "string"} and {error: {message: "string"}} formats
29
+ errorMessage = typeof data.error === 'string'
30
+ ? data.error
31
+ : data.error?.message || data.message || 'Login failed';
32
+ } catch {
33
+ errorMessage = await res.text() || 'Login failed';
34
+ }
35
+ } else {
36
+ errorMessage = await res.text() || 'Login failed';
37
+ }
38
+ return { error: errorMessage };
39
+ }
40
+
41
+ // Get redirect URL from query params or default to /chat
42
+ const url = new URL(request.url);
43
+ const redirectTo = url.searchParams.get('redirect') || '/chat';
44
+
45
+ // Forward the session cookie from the API response
46
+ const setCookie = res.headers.get('set-cookie');
47
+
48
+ return redirect(redirectTo, {
49
+ headers: setCookie ? { 'Set-Cookie': setCookie } : {},
50
+ });
51
+ }
52
+
53
+ export function meta({}: Route.MetaArgs) {
54
+ return [{ title: `Sign In - ${config.app.name}` }];
55
+ }
56
+
57
+ export default function Login({
58
+ loaderData,
59
+ actionData,
60
+ }: Route.ComponentProps) {
61
+ const { config } = loaderData;
62
+ const navigation = useNavigation();
63
+ const isSubmitting = navigation.state === 'submitting';
64
+
65
+ const hasGoogle = config.auth?.methods?.includes('google') ?? false;
66
+ const hasGitHub = config.auth?.methods?.includes('github') ?? false;
67
+ const hasEmailPassword = config.auth?.methods?.includes('email-password') ?? true;
68
+
69
+ return (
70
+ <div
71
+ style={{
72
+ display: 'flex',
73
+ minHeight: '100vh',
74
+ alignItems: 'center',
75
+ justifyContent: 'center',
76
+ backgroundColor: 'rgb(var(--color-background))',
77
+ padding: '1rem',
78
+ }}
79
+ >
80
+ <div style={{ width: '100%', maxWidth: '28rem' }}>
81
+ <div style={{ marginBottom: '2rem', textAlign: 'center' }}>
82
+ {config.ui?.logo && (
83
+ <img
84
+ src={
85
+ typeof config.ui.logo === 'string'
86
+ ? config.ui.logo
87
+ : config.ui.logo.light
88
+ }
89
+ alt={config.app.name}
90
+ style={{
91
+ height: '4rem',
92
+ width: '4rem',
93
+ marginLeft: 'auto',
94
+ marginRight: 'auto',
95
+ marginBottom: '1rem',
96
+ borderRadius: '0.5rem',
97
+ objectFit: 'contain',
98
+ }}
99
+ />
100
+ )}
101
+ <h1
102
+ style={{
103
+ fontSize: '1.875rem',
104
+ fontWeight: 'bold',
105
+ color: 'rgb(var(--color-text-primary))',
106
+ margin: 0,
107
+ }}
108
+ >
109
+ Welcome back
110
+ </h1>
111
+ <p
112
+ style={{
113
+ marginTop: '0.5rem',
114
+ color: 'rgb(var(--color-text-secondary))',
115
+ }}
116
+ >
117
+ Sign in to {config.app.name}
118
+ </p>
119
+ </div>
120
+
121
+ {/* OAuth buttons */}
122
+ {(hasGoogle || hasGitHub) && (
123
+ <div
124
+ style={{
125
+ marginBottom: '1.5rem',
126
+ display: 'flex',
127
+ flexDirection: 'column',
128
+ gap: '0.75rem',
129
+ }}
130
+ >
131
+ {hasGoogle && (
132
+ <a
133
+ href="/api/auth/oauth/google"
134
+ style={{
135
+ display: 'flex',
136
+ width: '100%',
137
+ alignItems: 'center',
138
+ justifyContent: 'center',
139
+ gap: '0.5rem',
140
+ borderRadius: '0.5rem',
141
+ border: '1px solid rgb(var(--color-border))',
142
+ backgroundColor: 'rgb(var(--color-background))',
143
+ padding: '0.5rem 1rem',
144
+ color: 'rgb(var(--color-text-primary))',
145
+ textDecoration: 'none',
146
+ }}
147
+ >
148
+ Continue with Google
149
+ </a>
150
+ )}
151
+ {hasGitHub && (
152
+ <a
153
+ href="/api/auth/oauth/github"
154
+ style={{
155
+ display: 'flex',
156
+ width: '100%',
157
+ alignItems: 'center',
158
+ justifyContent: 'center',
159
+ gap: '0.5rem',
160
+ borderRadius: '0.5rem',
161
+ border: '1px solid rgb(var(--color-border))',
162
+ backgroundColor: 'rgb(var(--color-background))',
163
+ padding: '0.5rem 1rem',
164
+ color: 'rgb(var(--color-text-primary))',
165
+ textDecoration: 'none',
166
+ }}
167
+ >
168
+ Continue with GitHub
169
+ </a>
170
+ )}
171
+ </div>
172
+ )}
173
+
174
+ {(hasGoogle || hasGitHub) && hasEmailPassword && (
175
+ <div style={{ position: 'relative', marginBottom: '1.5rem' }}>
176
+ <div
177
+ style={{
178
+ position: 'absolute',
179
+ inset: 0,
180
+ display: 'flex',
181
+ alignItems: 'center',
182
+ }}
183
+ >
184
+ <div
185
+ style={{
186
+ width: '100%',
187
+ borderTop: '1px solid rgb(var(--color-border))',
188
+ }}
189
+ />
190
+ </div>
191
+ <div
192
+ style={{
193
+ position: 'relative',
194
+ display: 'flex',
195
+ justifyContent: 'center',
196
+ fontSize: '0.875rem',
197
+ }}
198
+ >
199
+ <span
200
+ style={{
201
+ backgroundColor: 'rgb(var(--color-background))',
202
+ padding: '0 0.5rem',
203
+ color: 'rgb(var(--color-text-muted))',
204
+ }}
205
+ >
206
+ Or continue with email
207
+ </span>
208
+ </div>
209
+ </div>
210
+ )}
211
+
212
+ {hasEmailPassword && (
213
+ <Form
214
+ method="post"
215
+ style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}
216
+ >
217
+ <div>
218
+ <label
219
+ htmlFor="email"
220
+ style={{
221
+ display: 'block',
222
+ fontSize: '0.875rem',
223
+ fontWeight: 500,
224
+ color: 'rgb(var(--color-text-primary))',
225
+ }}
226
+ >
227
+ Email
228
+ </label>
229
+ <input
230
+ type="email"
231
+ id="email"
232
+ name="email"
233
+ required
234
+ style={{
235
+ marginTop: '0.25rem',
236
+ width: '100%',
237
+ borderRadius: '0.5rem',
238
+ border: '1px solid rgb(var(--color-input-border))',
239
+ backgroundColor: 'rgb(var(--color-input-background))',
240
+ padding: '0.5rem 1rem',
241
+ color: 'rgb(var(--color-text-primary))',
242
+ boxSizing: 'border-box',
243
+ }}
244
+ />
245
+ </div>
246
+
247
+ <div>
248
+ <label
249
+ htmlFor="password"
250
+ style={{
251
+ display: 'block',
252
+ fontSize: '0.875rem',
253
+ fontWeight: 500,
254
+ color: 'rgb(var(--color-text-primary))',
255
+ }}
256
+ >
257
+ Password
258
+ </label>
259
+ <input
260
+ type="password"
261
+ id="password"
262
+ name="password"
263
+ required
264
+ style={{
265
+ marginTop: '0.25rem',
266
+ width: '100%',
267
+ borderRadius: '0.5rem',
268
+ border: '1px solid rgb(var(--color-input-border))',
269
+ backgroundColor: 'rgb(var(--color-input-background))',
270
+ padding: '0.5rem 1rem',
271
+ color: 'rgb(var(--color-text-primary))',
272
+ boxSizing: 'border-box',
273
+ }}
274
+ />
275
+ </div>
276
+
277
+ {actionData?.error && (
278
+ <div
279
+ style={{
280
+ padding: '0.75rem',
281
+ backgroundColor: 'rgba(239, 68, 68, 0.1)',
282
+ borderRadius: '0.5rem',
283
+ color: 'rgb(239, 68, 68)',
284
+ fontSize: '0.875rem',
285
+ textAlign: 'center',
286
+ }}
287
+ >
288
+ {actionData.error}
289
+ </div>
290
+ )}
291
+
292
+ <button
293
+ type="submit"
294
+ disabled={isSubmitting}
295
+ style={{
296
+ width: '100%',
297
+ borderRadius: '0.5rem',
298
+ backgroundColor: 'rgb(var(--color-primary))',
299
+ padding: '0.5rem 1rem',
300
+ fontWeight: 500,
301
+ color: 'white',
302
+ border: 'none',
303
+ cursor: isSubmitting ? 'not-allowed' : 'pointer',
304
+ opacity: isSubmitting ? 0.7 : 1,
305
+ }}
306
+ >
307
+ {isSubmitting ? 'Signing in...' : 'Sign in'}
308
+ </button>
309
+ </Form>
310
+ )}
311
+
312
+ <p
313
+ style={{
314
+ marginTop: '1.5rem',
315
+ textAlign: 'center',
316
+ fontSize: '0.875rem',
317
+ color: 'rgb(var(--color-text-secondary))',
318
+ }}
319
+ >
320
+ Don't have an account?{' '}
321
+ <a
322
+ href="/register"
323
+ style={{
324
+ color: 'rgb(var(--color-primary))',
325
+ textDecoration: 'none',
326
+ }}
327
+ >
328
+ Sign up
329
+ </a>
330
+ </p>
331
+ </div>
332
+ </div>
333
+ );
334
+ }
@@ -0,0 +1,10 @@
1
+ import { createRoute } from '@chaaskit/client/ssr-utils';
2
+
3
+ const route = createRoute({
4
+ title: 'OAuth Consent',
5
+ load: () => import('@chaaskit/client/routes/OAuthConsentRoute'),
6
+ });
7
+
8
+ export const meta = route.meta;
9
+ export const links = route.links;
10
+ export default route.default;
@@ -0,0 +1,10 @@
1
+ import { createRoute } from '@chaaskit/client/ssr-utils';
2
+
3
+ const route = createRoute({
4
+ title: 'Pricing',
5
+ load: () => import('@chaaskit/client/routes/PricingRoute'),
6
+ });
7
+
8
+ export const meta = route.meta;
9
+ export const links = route.links;
10
+ export default route.default;
@@ -0,0 +1,197 @@
1
+ import type { Route } from './+types/privacy';
2
+ import { config } from '../../config/app.config';
3
+
4
+ export function meta({}: Route.MetaArgs) {
5
+ return [
6
+ { title: `Privacy Policy - ${config.app.name}` },
7
+ { name: 'description', content: `Privacy Policy for ${config.app.name}` },
8
+ ];
9
+ }
10
+
11
+ export default function Privacy() {
12
+ return (
13
+ <div
14
+ style={{
15
+ minHeight: '100vh',
16
+ backgroundColor: 'rgb(var(--color-background))',
17
+ padding: '2rem 1rem',
18
+ }}
19
+ >
20
+ <div style={{ maxWidth: '48rem', marginLeft: 'auto', marginRight: 'auto' }}>
21
+ <div style={{ marginBottom: '2rem' }}>
22
+ <a
23
+ href="/"
24
+ style={{
25
+ fontSize: '0.875rem',
26
+ color: 'rgb(var(--color-primary))',
27
+ textDecoration: 'none',
28
+ }}
29
+ >
30
+ &larr; Back to {config.app.name}
31
+ </a>
32
+ </div>
33
+
34
+ <article>
35
+ <h1
36
+ style={{
37
+ fontSize: '2rem',
38
+ fontWeight: 'bold',
39
+ color: 'rgb(var(--color-text-primary))',
40
+ marginBottom: '1.5rem',
41
+ }}
42
+ >
43
+ Privacy Policy
44
+ </h1>
45
+
46
+ <p
47
+ style={{
48
+ color: 'rgb(var(--color-text-muted))',
49
+ marginBottom: '2rem',
50
+ fontSize: '0.875rem',
51
+ }}
52
+ >
53
+ Last updated:{' '}
54
+ {new Date().toLocaleDateString('en-US', {
55
+ year: 'numeric',
56
+ month: 'long',
57
+ day: 'numeric',
58
+ })}
59
+ </p>
60
+
61
+ <div
62
+ style={{
63
+ color: 'rgb(var(--color-text-secondary))',
64
+ lineHeight: 1.7,
65
+ }}
66
+ >
67
+ <Section title="Information We Collect">
68
+ <p>
69
+ We collect information you provide directly to us, such as when
70
+ you create an account, use our services, or contact us for
71
+ support.
72
+ </p>
73
+ <ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
74
+ <li style={{ marginBottom: '0.5rem' }}>
75
+ Account information (name, email address)
76
+ </li>
77
+ <li style={{ marginBottom: '0.5rem' }}>
78
+ Chat conversations and messages
79
+ </li>
80
+ <li style={{ marginBottom: '0.5rem' }}>
81
+ Usage data and preferences
82
+ </li>
83
+ </ul>
84
+ </Section>
85
+
86
+ <Section title="How We Use Your Information">
87
+ <p>We use the information we collect to:</p>
88
+ <ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
89
+ <li style={{ marginBottom: '0.5rem' }}>
90
+ Provide, maintain, and improve our services
91
+ </li>
92
+ <li style={{ marginBottom: '0.5rem' }}>
93
+ Process your requests and respond to your inquiries
94
+ </li>
95
+ <li style={{ marginBottom: '0.5rem' }}>
96
+ Send you technical notices and support messages
97
+ </li>
98
+ <li style={{ marginBottom: '0.5rem' }}>
99
+ Detect, prevent, and address technical issues
100
+ </li>
101
+ </ul>
102
+ </Section>
103
+
104
+ <Section title="Data Security">
105
+ <p>
106
+ We implement appropriate security measures to protect your
107
+ personal information. However, no method of transmission over the
108
+ Internet or electronic storage is 100% secure.
109
+ </p>
110
+ </Section>
111
+
112
+ <Section title="AI Processing">
113
+ <p>
114
+ Your conversations may be processed by AI models to provide
115
+ responses. We do not use your conversations to train AI models
116
+ without your explicit consent.
117
+ </p>
118
+ </Section>
119
+
120
+ <Section title="Data Retention">
121
+ <p>
122
+ We retain your information for as long as your account is active
123
+ or as needed to provide you services. You can request deletion of
124
+ your data at any time.
125
+ </p>
126
+ </Section>
127
+
128
+ <Section title="Third-Party Services">
129
+ <p>
130
+ We may use third-party services that collect, monitor, and
131
+ analyze data. These services have their own privacy policies
132
+ addressing how they use such information.
133
+ </p>
134
+ </Section>
135
+
136
+ <Section title="Your Rights">
137
+ <p>You have the right to:</p>
138
+ <ul style={{ marginTop: '1rem', paddingLeft: '1.5rem' }}>
139
+ <li style={{ marginBottom: '0.5rem' }}>
140
+ Access your personal data
141
+ </li>
142
+ <li style={{ marginBottom: '0.5rem' }}>
143
+ Correct inaccurate data
144
+ </li>
145
+ <li style={{ marginBottom: '0.5rem' }}>
146
+ Request deletion of your data
147
+ </li>
148
+ <li style={{ marginBottom: '0.5rem' }}>
149
+ Export your data
150
+ </li>
151
+ </ul>
152
+ </Section>
153
+
154
+ <Section title="Changes to This Policy">
155
+ <p>
156
+ We may update this privacy policy from time to time. We will
157
+ notify you of any changes by posting the new policy on this page.
158
+ </p>
159
+ </Section>
160
+
161
+ <Section title="Contact Us">
162
+ <p>
163
+ If you have any questions about this Privacy Policy, please
164
+ contact us.
165
+ </p>
166
+ </Section>
167
+ </div>
168
+ </article>
169
+ </div>
170
+ </div>
171
+ );
172
+ }
173
+
174
+ function Section({
175
+ title,
176
+ children,
177
+ }: {
178
+ title: string;
179
+ children: React.ReactNode;
180
+ }) {
181
+ return (
182
+ <>
183
+ <h2
184
+ style={{
185
+ fontSize: '1.25rem',
186
+ fontWeight: 600,
187
+ marginTop: '2rem',
188
+ marginBottom: '1rem',
189
+ color: 'rgb(var(--color-text-primary))',
190
+ }}
191
+ >
192
+ {title}
193
+ </h2>
194
+ {children}
195
+ </>
196
+ );
197
+ }