@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.
- package/README.md +4 -6
- package/package.json +1 -6
- package/src/command-specs/apps.js +10 -77
- package/src/runtime/app-platform.js +69 -309
- package/src/runtime/app-preview-server.js +96 -136
- package/template/app/globals.css +0 -3
- package/template/app/layout.tsx +0 -7
- package/template/app/page.tsx +0 -55
- package/template/components/ui/badge.tsx +0 -28
- package/template/components/ui/button.tsx +0 -53
- package/template/components/ui/card.tsx +0 -56
- package/template/components.json +0 -20
- package/template/lib/utils.ts +0 -6
- package/template/notis.config.ts +0 -18
- package/template/package.json +0 -32
- package/template/packages/notis-sdk/package.json +0 -26
- package/template/packages/notis-sdk/src/config.ts +0 -48
- package/template/packages/notis-sdk/src/helpers.ts +0 -131
- package/template/packages/notis-sdk/src/hooks/useAppState.ts +0 -50
- package/template/packages/notis-sdk/src/hooks/useBackend.ts +0 -41
- package/template/packages/notis-sdk/src/hooks/useCollectionItem.ts +0 -58
- package/template/packages/notis-sdk/src/hooks/useDatabase.ts +0 -87
- package/template/packages/notis-sdk/src/hooks/useDocument.ts +0 -61
- package/template/packages/notis-sdk/src/hooks/useNotis.ts +0 -31
- package/template/packages/notis-sdk/src/hooks/useNotisNavigation.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useTool.ts +0 -49
- package/template/packages/notis-sdk/src/hooks/useTools.ts +0 -56
- package/template/packages/notis-sdk/src/hooks/useUpsertDocument.ts +0 -57
- package/template/packages/notis-sdk/src/index.ts +0 -47
- package/template/packages/notis-sdk/src/provider.tsx +0 -44
- package/template/packages/notis-sdk/src/runtime.ts +0 -159
- package/template/packages/notis-sdk/src/styles.css +0 -123
- package/template/packages/notis-sdk/src/ui.ts +0 -15
- package/template/packages/notis-sdk/src/vite.ts +0 -54
- package/template/packages/notis-sdk/tsconfig.json +0 -15
- package/template/postcss.config.mjs +0 -8
- package/template/tailwind.config.ts +0 -58
- package/template/tsconfig.json +0 -22
- package/template/vite.config.ts +0 -10
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local preview server for built Notis app
|
|
2
|
+
* Local preview server for built Notis app artifacts.
|
|
3
3
|
*
|
|
4
|
-
* Serves
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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,
|
|
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
|
|
45
|
-
|
|
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: `${
|
|
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
|
|
59
|
-
return String(value)
|
|
60
|
-
.replaceAll('&', '&')
|
|
61
|
-
.replaceAll('<', '<')
|
|
62
|
-
.replaceAll('>', '>')
|
|
63
|
-
.replaceAll('"', '"')
|
|
64
|
-
.replaceAll("'", ''');
|
|
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
|
-
|
|
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 = ${
|
|
99
|
-
|
|
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: ${
|
|
113
|
-
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(${
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
243
|
-
|
|
244
|
-
|
|
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
|
|
254
|
-
const
|
|
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)
|
|
224
|
+
const route = routesByPath.get(pathname);
|
|
286
225
|
if (route) {
|
|
287
|
-
const
|
|
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
|
|
294
|
-
const filePath =
|
|
295
|
-
if (
|
|
296
|
-
response.writeHead(
|
|
297
|
-
response.end(
|
|
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
|
-
|
|
302
|
-
|
|
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) => {
|
package/template/app/globals.css
DELETED
package/template/app/layout.tsx
DELETED
package/template/app/page.tsx
DELETED
|
@@ -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 };
|
package/template/components.json
DELETED
|
@@ -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
|
-
}
|
package/template/lib/utils.ts
DELETED
package/template/notis.config.ts
DELETED
|
@@ -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
|
-
});
|
package/template/package.json
DELETED
|
@@ -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
|
-
}
|