@notis_ai/cli 0.2.0-beta.16.1 → 0.2.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 (39) hide show
  1. package/README.md +4 -6
  2. package/package.json +1 -6
  3. package/src/command-specs/apps.js +10 -77
  4. package/src/runtime/app-platform.js +69 -309
  5. package/src/runtime/app-preview-server.js +96 -136
  6. package/template/app/globals.css +0 -3
  7. package/template/app/layout.tsx +0 -7
  8. package/template/app/page.tsx +0 -55
  9. package/template/components/ui/badge.tsx +0 -28
  10. package/template/components/ui/button.tsx +0 -53
  11. package/template/components/ui/card.tsx +0 -56
  12. package/template/components.json +0 -20
  13. package/template/lib/utils.ts +0 -6
  14. package/template/notis.config.ts +0 -18
  15. package/template/package.json +0 -32
  16. package/template/packages/notis-sdk/package.json +0 -26
  17. package/template/packages/notis-sdk/src/config.ts +0 -48
  18. package/template/packages/notis-sdk/src/helpers.ts +0 -131
  19. package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
  20. package/template/packages/notis-sdk/src/hooks/useBackend.ts +0 -41
  21. package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
  22. package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -87
  23. package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
  24. package/template/packages/notis-sdk/src/hooks/useNotis.ts +0 -31
  25. package/template/packages/notis-sdk/src/hooks/useNotisNavigation.ts +0 -49
  26. package/template/packages/notis-sdk/src/hooks/useTool.ts +0 -49
  27. package/template/packages/notis-sdk/src/hooks/useTools.ts +0 -56
  28. package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -57
  29. package/template/packages/notis-sdk/src/index.ts +0 -47
  30. package/template/packages/notis-sdk/src/provider.tsx +0 -44
  31. package/template/packages/notis-sdk/src/runtime.ts +0 -159
  32. package/template/packages/notis-sdk/src/styles.css +0 -123
  33. package/template/packages/notis-sdk/src/ui.ts +0 -15
  34. package/template/packages/notis-sdk/src/vite.ts +0 -54
  35. package/template/packages/notis-sdk/tsconfig.json +0 -15
  36. package/template/postcss.config.mjs +0 -8
  37. package/template/tailwind.config.ts +0 -58
  38. package/template/tsconfig.json +0 -22
  39. package/template/vite.config.ts +0 -10
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Local preview server for built Notis app bundles.
2
+ * Local preview server for built Notis app artifacts.
3
3
  *
4
- * Serves a single index.html that loads the app bundle via <script type="module">.
5
- * Injects mock window.__NOTIS_RUNTIME__ before the bundle loads. Route switching
6
- * uses pathname navigation between route pages.
4
+ * Serves the static site from .notis/output/site/ with the Notis runtime
5
+ * injected into HTML pages. The runtime provides mock implementations for
6
+ * databases (seed data from manifest) and tool stubs.
7
7
  *
8
8
  * Usage: `notis apps preview [dir]` starts this server on localhost:8787.
9
9
  */
10
10
 
11
11
  import { createServer } from 'node:http';
12
12
  import { existsSync, readFileSync } from 'node:fs';
13
- import { extname, resolve, sep } from 'node:path';
13
+ import { extname, join, resolve } from 'node:path';
14
14
 
15
15
  import { readManifest } from './app-platform.js';
16
16
 
@@ -35,71 +35,66 @@ function contentTypeFor(path) {
35
35
  return types[extname(path)] || 'application/octet-stream';
36
36
  }
37
37
 
38
+ function replacePlaceholders(content) {
39
+ return content
40
+ .replaceAll('/__NOTIS_APP_BASE__', '')
41
+ .replaceAll('__NOTIS_APP_BASE__', '')
42
+ .replaceAll('/__NOTIS_ASSET_BASE__', '')
43
+ .replaceAll('__NOTIS_ASSET_BASE__', '');
44
+ }
45
+
38
46
  // ---------------------------------------------------------------------------
39
47
  // Mock runtime generation
40
48
  // ---------------------------------------------------------------------------
41
49
 
50
+ function buildSeedValue(property, index) {
51
+ switch (property?.type) {
52
+ case 'number':
53
+ return (index + 1) * 180;
54
+ case 'checkbox':
55
+ return index % 2 === 0;
56
+ case 'date':
57
+ return `2026-04-0${Math.min(index + 2, 9)}`;
58
+ case 'select':
59
+ case 'status':
60
+ return property.options?.[index % (property.options?.length || 1)]?.name || 'Active';
61
+ case 'multi_select':
62
+ return (property.options || []).slice(0, 2).map((o) => o.name);
63
+ default:
64
+ return `${property?.name || 'Value'} ${index + 1}`;
65
+ }
66
+ }
67
+
42
68
  function buildSeedDocuments(databases) {
43
69
  const state = {};
44
- for (const slug of databases) {
45
- state[slug] = Array.from({ length: 3 }, (_, i) => {
70
+ for (const db of databases) {
71
+ const titleProp = db.properties?.find((p) => p.type === 'title')?.name || 'title';
72
+ state[db.slug] = Array.from({ length: 3 }, (_, i) => {
73
+ const properties = {};
74
+ for (const prop of db.properties || []) {
75
+ properties[prop.name] = prop.name === titleProp
76
+ ? `${db.title} ${i + 1}`
77
+ : buildSeedValue(prop, i);
78
+ }
46
79
  return {
47
- id: `${slug}-seed-${i + 1}`,
48
- databaseSlug: slug,
49
- title: `${slug} ${i + 1}`,
50
- properties: {},
51
- icon: null,
80
+ id: `${db.slug}-seed-${i + 1}`,
81
+ databaseSlug: db.slug,
82
+ title: properties[titleProp] || `${db.title} ${i + 1}`,
83
+ properties,
84
+ icon: db.icon || null,
52
85
  };
53
86
  });
54
87
  }
55
88
  return state;
56
89
  }
57
90
 
58
- function escapeHtml(value) {
59
- return String(value)
60
- .replaceAll('&', '&amp;')
61
- .replaceAll('<', '&lt;')
62
- .replaceAll('>', '&gt;')
63
- .replaceAll('"', '&quot;')
64
- .replaceAll("'", '&#39;');
65
- }
66
-
67
- function sanitizeBundlePath(value, fallback) {
68
- if (typeof value !== 'string') return fallback;
69
- const normalized = value.trim().replaceAll('\\', '/').replace(/^\/+/, '');
70
- if (!normalized || normalized.includes('..')) return fallback;
71
- return /^[A-Za-z0-9._/-]+$/.test(normalized) ? normalized : fallback;
72
- }
73
-
74
- function serializeForInlineScript(value) {
75
- return JSON.stringify(value).replaceAll('</', '<\\/').replaceAll('<!--', '<\\!--');
76
- }
77
-
78
- function buildPreviewHtml({ manifest, route, databases }) {
79
- const app = manifest.app;
91
+ function runtimeScript({ app, route, tools, databases }) {
80
92
  const seedState = buildSeedDocuments(databases);
81
- const bundleJs = sanitizeBundlePath(manifest.bundle?.js, 'bundle/app.js');
82
- const bundleCss = sanitizeBundlePath(manifest.bundle?.css, 'bundle/app.css');
83
- const exportName = typeof route.export_name === 'string' && route.export_name ? route.export_name : 'index';
84
- const pageTitle = escapeHtml(`${app.name} - ${route.name}`);
85
- const bundleCssHref = escapeHtml(`/${bundleCss}`);
86
- const bundleJsPathLiteral = JSON.stringify(`/${bundleJs}`);
87
- const exportNameLiteral = JSON.stringify(exportName);
88
-
89
- return `<!DOCTYPE html>
90
- <html lang="en">
91
- <head>
92
- <meta charset="UTF-8" />
93
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
94
- <title>${pageTitle}</title>
95
- <link rel="stylesheet" href="${bundleCssHref}" />
96
- <script>
93
+ return `<script>
97
94
  (function() {
98
- var DB = ${serializeForInlineScript(databases.map(function(slug) {
99
- return { slug: slug, title: slug, properties: [] };
100
- }))};
101
- var STATE = ${serializeForInlineScript(seedState)};
102
- var COLLECTION = ${serializeForInlineScript(route.collection || null)};
95
+ var DB = ${JSON.stringify(databases)};
96
+ var STATE = ${JSON.stringify(seedState)};
97
+ var COLLECTION = ${JSON.stringify(route.collection || null)};
103
98
 
104
99
  function titleProp(slug) {
105
100
  var db = DB.find(function(d) { return d.slug === slug; });
@@ -109,19 +104,12 @@ function buildPreviewHtml({ manifest, route, databases }) {
109
104
  function dbSlug(v) { return v || (COLLECTION && COLLECTION.database) || null; }
110
105
 
111
106
  window.__NOTIS_RUNTIME__ = {
112
- app: ${serializeForInlineScript(app)},
113
- route: ${serializeForInlineScript(route)},
107
+ app: ${JSON.stringify(app)},
108
+ route: ${JSON.stringify(route)},
114
109
  databases: DB,
115
110
 
116
- navigate: function(payload) {
117
- console.log('[notis-preview] Navigate:', payload);
118
- if (payload.kind === 'route' && payload.path) {
119
- window.location.assign(payload.path);
120
- }
121
- },
122
-
123
111
  listTools: function() {
124
- return Promise.resolve(${serializeForInlineScript((manifest.tools || []).map((name) => ({ name })))});
112
+ return Promise.resolve(${JSON.stringify(tools)});
125
113
  },
126
114
 
127
115
  callTool: function(name, args) {
@@ -186,48 +174,14 @@ function buildPreviewHtml({ manifest, route, databases }) {
186
174
  }
187
175
  };
188
176
  })();
189
- </script>
190
- </head>
191
- <body class="min-h-screen bg-background text-foreground antialiased">
192
- <div id="notis-app-root"></div>
193
- <script type="importmap">
194
- {
195
- "imports": {
196
- "react": "https://esm.sh/react@19",
197
- "react-dom": "https://esm.sh/react-dom@19?external=react",
198
- "react-dom/client": "https://esm.sh/react-dom@19/client?external=react",
199
- "react/jsx-runtime": "https://esm.sh/react@19/jsx-runtime"
200
- }
201
- }
202
- </script>
203
- <script type="module">
204
- import React from 'react';
205
- import { createRoot } from 'react-dom/client';
206
- import * as AppBundle from ${bundleJsPathLiteral};
207
-
208
- // Try to render: AppShell wrapping the route component, or just the route component
209
- const exportName = ${exportNameLiteral};
210
- const RouteComponent = AppBundle[exportName] || AppBundle['default'];
211
- const AppShell = AppBundle['__AppShell'];
212
-
213
- if (RouteComponent) {
214
- const root = document.getElementById('notis-app-root');
215
- if (root) {
216
- const reactRoot = createRoot(root);
217
- if (AppShell) {
218
- reactRoot.render(React.createElement(AppShell, null, React.createElement(RouteComponent)));
219
- } else {
220
- reactRoot.render(React.createElement(RouteComponent));
221
- }
222
- }
223
- } else {
224
- document.getElementById('notis-app-root').innerHTML =
225
- '<p style="padding:2rem;color:red;">No component found for export "' + exportName + '". Available exports: ' +
226
- Object.keys(AppBundle).join(', ') + '</p>';
227
- }
228
- </script>
229
- </body>
230
- </html>`;
177
+ </script>`;
178
+ }
179
+
180
+ function injectRuntime(html, injection) {
181
+ if (html.includes('</head>')) {
182
+ return html.replace('</head>', injection + '\n</head>');
183
+ }
184
+ return injection + '\n' + html;
231
185
  }
232
186
 
233
187
  // ---------------------------------------------------------------------------
@@ -239,20 +193,15 @@ function normalizePathname(pathname) {
239
193
  return pathname.endsWith('/') ? pathname.slice(0, -1) || '/' : pathname;
240
194
  }
241
195
 
242
- function resolveSafePath(baseDir, pathname) {
243
- const normalizedBaseDir = resolve(baseDir);
244
- const resolvedPath = resolve(normalizedBaseDir, pathname.replace(/^\/+/, ''));
245
- if (resolvedPath === normalizedBaseDir || resolvedPath.startsWith(`${normalizedBaseDir}${sep}`)) {
246
- return resolvedPath;
247
- }
248
- return null;
196
+ function entryHtmlPath(routePath) {
197
+ if (routePath === '/') return 'index.html';
198
+ return `${routePath.replace(/^\/+/, '')}/index.html`;
249
199
  }
250
200
 
251
201
  export async function startPreviewServer({ projectDir, port }) {
252
202
  const manifest = readManifest(projectDir);
253
- const outputDir = resolve(projectDir, '.notis/output');
254
- const bundleDir = resolve(projectDir, '.notis/output/bundle');
255
- const databases = (manifest.databases || []).filter((slug) => typeof slug === 'string' && slug);
203
+ const siteDir = resolve(projectDir, '.notis/output/site');
204
+ const databases = (manifest.databases || []).filter((d) => d && typeof d.slug === 'string');
256
205
  const routesByPath = new Map((manifest.routes || []).map((r) => [normalizePathname(r.path), r]));
257
206
  const defaultRoute = (manifest.routes || []).find((r) => r.default) || manifest.routes?.[0];
258
207
 
@@ -264,16 +213,6 @@ export async function startPreviewServer({ projectDir, port }) {
264
213
  const url = new URL(request.url || '/', `http://${request.headers.host || 'localhost'}`);
265
214
  const pathname = normalizePathname(url.pathname);
266
215
 
267
- // Serve bundle files directly
268
- if (pathname.startsWith('/bundle/')) {
269
- const filePath = resolveSafePath(outputDir, pathname);
270
- if (filePath && existsSync(filePath)) {
271
- response.writeHead(200, { 'Content-Type': contentTypeFor(filePath), 'Cache-Control': 'no-store' });
272
- response.end(readFileSync(filePath));
273
- return;
274
- }
275
- }
276
-
277
216
  // Redirect / to default route if it's not /
278
217
  if (pathname === '/' && defaultRoute.path !== '/') {
279
218
  response.writeHead(302, { Location: defaultRoute.path });
@@ -282,24 +221,45 @@ export async function startPreviewServer({ projectDir, port }) {
282
221
  }
283
222
 
284
223
  // Serve route HTML with runtime injection
285
- const route = routesByPath.get(pathname) || (pathname === '/' ? defaultRoute : null);
224
+ const route = routesByPath.get(pathname);
286
225
  if (route) {
287
- const html = buildPreviewHtml({ manifest, route, databases });
226
+ const htmlPath = join(siteDir, entryHtmlPath(route.path));
227
+ if (!existsSync(htmlPath)) {
228
+ response.writeHead(404, { 'Content-Type': 'text/plain' });
229
+ response.end(`Route HTML not found: ${htmlPath}`);
230
+ return;
231
+ }
232
+
233
+ const raw = readFileSync(htmlPath, 'utf-8');
234
+ const html = injectRuntime(
235
+ replacePlaceholders(raw),
236
+ runtimeScript({
237
+ app: manifest.app,
238
+ route,
239
+ tools: (manifest.tools || []).map((name) => ({ name })),
240
+ databases,
241
+ }),
242
+ );
288
243
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store' });
289
244
  response.end(html);
290
245
  return;
291
246
  }
292
247
 
293
- // Serve static files from bundle dir
294
- const filePath = resolveSafePath(bundleDir, pathname);
295
- if (filePath && existsSync(filePath)) {
296
- response.writeHead(200, { 'Content-Type': contentTypeFor(filePath), 'Cache-Control': 'no-store' });
297
- response.end(readFileSync(filePath));
248
+ // Serve static files
249
+ const filePath = join(siteDir, pathname.replace(/^\/+/, ''));
250
+ if (!existsSync(filePath)) {
251
+ response.writeHead(404, { 'Content-Type': 'text/plain' });
252
+ response.end('Not found');
298
253
  return;
299
254
  }
300
255
 
301
- response.writeHead(404, { 'Content-Type': 'text/plain' });
302
- response.end('Not found');
256
+ const isHtml = extname(filePath) === '.html';
257
+ const body = isHtml
258
+ ? replacePlaceholders(readFileSync(filePath, 'utf-8'))
259
+ : readFileSync(filePath);
260
+
261
+ response.writeHead(200, { 'Content-Type': contentTypeFor(filePath), 'Cache-Control': 'no-store' });
262
+ response.end(body);
303
263
  });
304
264
 
305
265
  await new Promise((resolveP, rejectP) => {
@@ -1,3 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
@@ -1,7 +0,0 @@
1
- import { NotisProvider } from '@notis/sdk';
2
- import '@notis/sdk/styles.css';
3
- import './globals.css';
4
-
5
- export default function AppShell({ children }: { children: React.ReactNode }) {
6
- return <NotisProvider>{children}</NotisProvider>;
7
- }
@@ -1,55 +0,0 @@
1
- 'use client';
2
-
3
- import { useNotis, useDatabase } from '@notis/sdk';
4
- import { Badge } from '@/components/ui/badge';
5
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
6
-
7
- export default function HomePage() {
8
- const { app, ready } = useNotis();
9
- const { documents, loading } = useDatabase('items');
10
-
11
- return (
12
- <main className="notis-app-shell space-y-6">
13
- <Card>
14
- <CardHeader className="space-y-3">
15
- <Badge variant="secondary" className="w-fit">Installed app</Badge>
16
- <div className="space-y-2">
17
- <CardTitle>{ready ? app?.name : 'Loading...'}</CardTitle>
18
- <CardDescription>
19
- {ready ? app?.description : 'Loading app metadata...'}
20
- </CardDescription>
21
- </div>
22
- </CardHeader>
23
- </Card>
24
-
25
- <Card>
26
- <CardHeader>
27
- <CardTitle>Items</CardTitle>
28
- <CardDescription>Use shadcn surfaces and portal tokens so the app feels native inside Notis.</CardDescription>
29
- </CardHeader>
30
- <CardContent>
31
- {loading ? (
32
- <p className="text-sm text-muted-foreground">Loading...</p>
33
- ) : documents.length === 0 ? (
34
- <div className="rounded-xl border border-dashed border-border px-4 py-10 text-center text-sm text-muted-foreground">
35
- No items yet. Deploy the app and create some.
36
- </div>
37
- ) : (
38
- <div className="space-y-3">
39
- {documents.map((doc) => (
40
- <div key={doc.id} className="rounded-xl border border-border bg-background px-4 py-3">
41
- <div className="flex items-center justify-between gap-3">
42
- <p className="font-medium">{doc.title}</p>
43
- {doc.properties.status ? (
44
- <Badge variant="outline">{String(doc.properties.status)}</Badge>
45
- ) : null}
46
- </div>
47
- </div>
48
- ))}
49
- </div>
50
- )}
51
- </CardContent>
52
- </Card>
53
- </main>
54
- );
55
- }
@@ -1,28 +0,0 @@
1
- import * as React from 'react';
2
- import { cva, type VariantProps } from 'class-variance-authority';
3
-
4
- import { cn } from '@/lib/utils';
5
-
6
- const badgeVariants = cva(
7
- 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8
- {
9
- variants: {
10
- variant: {
11
- default: 'border-transparent bg-primary/10 text-primary',
12
- secondary: 'border-transparent bg-secondary text-secondary-foreground',
13
- outline: 'text-foreground',
14
- },
15
- },
16
- defaultVariants: {
17
- variant: 'default',
18
- },
19
- },
20
- );
21
-
22
- export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
23
-
24
- function Badge({ className, variant, ...props }: BadgeProps) {
25
- return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
26
- }
27
-
28
- export { Badge, badgeVariants };
@@ -1,53 +0,0 @@
1
- import * as React from 'react';
2
- import { Slot } from '@radix-ui/react-slot';
3
- import { cva, type VariantProps } from 'class-variance-authority';
4
-
5
- import { cn } from '@/lib/utils';
6
-
7
- const buttonVariants = cva(
8
- 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
9
- {
10
- variants: {
11
- variant: {
12
- default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13
- destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
14
- outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
15
- secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
16
- ghost: 'hover:bg-accent hover:text-accent-foreground',
17
- link: 'text-primary underline-offset-4 hover:underline',
18
- },
19
- size: {
20
- default: 'h-10 px-4 py-2',
21
- sm: 'h-9 rounded-md px-3',
22
- lg: 'h-11 rounded-md px-8',
23
- icon: 'h-10 w-10',
24
- },
25
- },
26
- defaultVariants: {
27
- variant: 'default',
28
- size: 'default',
29
- },
30
- },
31
- );
32
-
33
- export interface ButtonProps
34
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35
- VariantProps<typeof buttonVariants> {
36
- asChild?: boolean;
37
- }
38
-
39
- const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
40
- ({ className, variant, size, asChild = false, ...props }, ref) => {
41
- const Comp = asChild ? Slot : 'button';
42
- return (
43
- <Comp
44
- className={cn(buttonVariants({ variant, size, className }))}
45
- ref={ref}
46
- {...props}
47
- />
48
- );
49
- },
50
- );
51
- Button.displayName = 'Button';
52
-
53
- export { Button, buttonVariants };
@@ -1,56 +0,0 @@
1
- import * as React from 'react';
2
-
3
- import { cn } from '@/lib/utils';
4
-
5
- const Card = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
6
- ({ className, ...props }, ref) => (
7
- <div
8
- ref={ref}
9
- className={cn('rounded-2xl border bg-card text-card-foreground shadow-sm', className)}
10
- {...props}
11
- />
12
- ),
13
- );
14
- Card.displayName = 'Card';
15
-
16
- const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
17
- ({ className, ...props }, ref) => (
18
- <div ref={ref} className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />
19
- ),
20
- );
21
- CardHeader.displayName = 'CardHeader';
22
-
23
- const CardTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
24
- ({ className, ...props }, ref) => (
25
- <h3
26
- ref={ref}
27
- className={cn('text-xl font-semibold leading-none tracking-tight', className)}
28
- {...props}
29
- />
30
- ),
31
- );
32
- CardTitle.displayName = 'CardTitle';
33
-
34
- const CardDescription = React.forwardRef<
35
- HTMLParagraphElement,
36
- React.HTMLAttributes<HTMLParagraphElement>
37
- >(({ className, ...props }, ref) => (
38
- <p ref={ref} className={cn('text-sm text-muted-foreground', className)} {...props} />
39
- ));
40
- CardDescription.displayName = 'CardDescription';
41
-
42
- const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
43
- ({ className, ...props }, ref) => (
44
- <div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
45
- ),
46
- );
47
- CardContent.displayName = 'CardContent';
48
-
49
- const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
50
- ({ className, ...props }, ref) => (
51
- <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} />
52
- ),
53
- );
54
- CardFooter.displayName = 'CardFooter';
55
-
56
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
@@ -1,20 +0,0 @@
1
- {
2
- "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
4
- "rsc": false,
5
- "tsx": true,
6
- "tailwind": {
7
- "config": "tailwind.config.ts",
8
- "css": "app/globals.css",
9
- "baseColor": "zinc",
10
- "cssVariables": true,
11
- "prefix": ""
12
- },
13
- "aliases": {
14
- "components": "@/components",
15
- "utils": "@/lib/utils",
16
- "ui": "@/components/ui",
17
- "lib": "@/lib",
18
- "hooks": "@/hooks"
19
- }
20
- }
@@ -1,6 +0,0 @@
1
- import { type ClassValue, clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }
@@ -1,18 +0,0 @@
1
- import { defineNotisApp } from '@notis/sdk/config';
2
-
3
- export default defineNotisApp({
4
- name: 'My Notis App',
5
- description: 'A new Notis app',
6
- icon: 'lucide:layout-dashboard',
7
-
8
- databases: ['items'],
9
-
10
- routes: [
11
- { path: '/', name: 'Home', icon: 'lucide:home', default: true },
12
- ],
13
-
14
- tools: [
15
- 'notis_query_database',
16
- 'notis_upsert_document',
17
- ],
18
- });
@@ -1,32 +0,0 @@
1
- {
2
- "name": "my-notis-app",
3
- "version": "0.1.0",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "vite build",
9
- "preview": "vite preview"
10
- },
11
- "dependencies": {
12
- "@notis/sdk": "file:./packages/notis-sdk",
13
- "@radix-ui/react-slot": "^1.1.0",
14
- "class-variance-authority": "^0.7.0",
15
- "clsx": "^2.1.1",
16
- "lucide-react": "^0.474.0",
17
- "react": "^19.0.0",
18
- "react-dom": "^19.0.0",
19
- "tailwind-merge": "^2.6.0",
20
- "tailwindcss-animate": "^1.0.7"
21
- },
22
- "devDependencies": {
23
- "@types/node": "^22.0.0",
24
- "@types/react": "^19.0.0",
25
- "@types/react-dom": "^19.0.0",
26
- "@vitejs/plugin-react": "^4.0.0",
27
- "postcss": "^8.0.0",
28
- "tailwindcss": "^3.4.0",
29
- "typescript": "^5.0.0",
30
- "vite": "^6.0.0"
31
- }
32
- }
@@ -1,26 +0,0 @@
1
- {
2
- "name": "@notis/sdk",
3
- "version": "0.1.0",
4
- "type": "module",
5
- "main": "./src/index.ts",
6
- "exports": {
7
- ".": "./src/index.ts",
8
- "./config": "./src/config.ts",
9
- "./vite": "./src/vite.ts",
10
- "./ui": "./src/ui.ts",
11
- "./styles.css": "./src/styles.css"
12
- },
13
- "files": [
14
- "src",
15
- "template"
16
- ],
17
- "peerDependencies": {
18
- "react": ">=18.0.0",
19
- "react-dom": ">=18.0.0",
20
- "vite": ">=5.0.0",
21
- "@vitejs/plugin-react": ">=4.0.0"
22
- },
23
- "devDependencies": {
24
- "typescript": "^5.0.0"
25
- }
26
- }