@notis_ai/cli 0.2.5 → 0.2.7
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 +33 -9
- package/dist/scaffolds/notis-affiliate-prospects/CHANGELOG.md +7 -0
- package/dist/scaffolds/notis-affiliate-prospects/README.md +20 -0
- package/dist/scaffolds/notis-affiliate-prospects/app/globals.css +42 -0
- package/dist/scaffolds/notis-affiliate-prospects/app/layout.tsx +7 -0
- package/dist/scaffolds/notis-affiliate-prospects/app/page.tsx +248 -0
- package/dist/scaffolds/notis-affiliate-prospects/app/prospects/page.tsx +134 -0
- package/dist/scaffolds/notis-affiliate-prospects/app/segments/page.tsx +108 -0
- package/dist/scaffolds/notis-affiliate-prospects/components/ui/card.tsx +35 -0
- package/dist/scaffolds/notis-affiliate-prospects/components.json +20 -0
- package/dist/scaffolds/notis-affiliate-prospects/index.html +12 -0
- package/dist/scaffolds/notis-affiliate-prospects/lib/affiliate-data.ts +218 -0
- package/dist/scaffolds/notis-affiliate-prospects/lib/demo-prospects.ts +52 -0
- package/dist/scaffolds/notis-affiliate-prospects/lib/notis-tools.ts +100 -0
- package/dist/scaffolds/notis-affiliate-prospects/lib/utils.ts +25 -0
- package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-fixtures.json +187 -0
- package/dist/scaffolds/notis-affiliate-prospects/notis.config.ts +65 -0
- package/dist/scaffolds/notis-affiliate-prospects/package.json +32 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/package.json +32 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectActionBar.tsx +273 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/config.ts +90 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useMultiSelect.ts +503 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useUpsertDocument.ts +50 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/index.ts +54 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/runtime.ts +161 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/styles.css +38 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-affiliate-prospects/postcss.config.mjs +6 -0
- package/dist/scaffolds/notis-affiliate-prospects/src/dev-main.tsx +72 -0
- package/dist/scaffolds/notis-affiliate-prospects/src/mock-runtime.ts +242 -0
- package/dist/scaffolds/notis-affiliate-prospects/tailwind.config.ts +51 -0
- package/dist/scaffolds/notis-affiliate-prospects/tsconfig.json +23 -0
- package/dist/scaffolds/notis-affiliate-prospects/vite.config.ts +11 -0
- package/dist/scaffolds/notis-database/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-database/notis.config.ts +0 -1
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +1 -0
- package/dist/scaffolds/notis-journal/CHANGELOG.md +25 -0
- package/dist/scaffolds/notis-journal/app/globals.css +37 -0
- package/dist/scaffolds/notis-journal/app/insights/page.tsx +513 -0
- package/dist/scaffolds/notis-journal/app/journal-core.tsx +362 -0
- package/dist/scaffolds/notis-journal/app/journal-ui.tsx +337 -0
- package/dist/scaffolds/notis-journal/app/layout.tsx +6 -0
- package/dist/scaffolds/notis-journal/app/page.tsx +485 -0
- package/dist/scaffolds/notis-journal/components/ui/badge.tsx +28 -0
- package/dist/scaffolds/notis-journal/components/ui/button.tsx +53 -0
- package/dist/scaffolds/notis-journal/components/ui/card.tsx +56 -0
- package/dist/scaffolds/notis-journal/components.json +20 -0
- package/dist/scaffolds/notis-journal/index.html +12 -0
- package/dist/scaffolds/notis-journal/lib/utils.ts +6 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
- package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +132 -0
- package/dist/scaffolds/notis-journal/notis.config.ts +93 -0
- package/dist/scaffolds/notis-journal/package.json +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +145 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-journal/postcss.config.mjs +8 -0
- package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +120 -0
- package/dist/scaffolds/notis-journal/src/dev-main.tsx +58 -0
- package/dist/scaffolds/notis-journal/src/mock-runtime.ts +197 -0
- package/dist/scaffolds/notis-journal/tailwind.config.ts +58 -0
- package/dist/scaffolds/notis-journal/tsconfig.json +23 -0
- package/dist/scaffolds/notis-journal/vite.config.ts +10 -0
- package/dist/scaffolds/notis-notes/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-notes/app/page.tsx +17 -373
- package/dist/scaffolds/notis-notes/notis.config.ts +0 -1
- package/dist/scaffolds/notis-random/CHANGELOG.md +5 -0
- package/dist/scaffolds/notis-random/notis.config.ts +0 -1
- package/dist/scaffolds/notis-random/src/mock-runtime.ts +1 -0
- package/dist/scaffolds.json +22 -0
- package/package.json +3 -3
- package/skills/notis-apps/SKILL.md +43 -5
- package/skills/notis-apps/cli.md +22 -6
- package/skills/notis-cli/SKILL.md +20 -2
- package/skills/notis-query/cli.md +1 -1
- package/src/cli.js +8 -1
- package/src/command-specs/apps.js +307 -36
- package/src/command-specs/diagnostics.js +674 -0
- package/src/command-specs/helpers.js +4 -1
- package/src/command-specs/index.js +7 -1
- package/src/command-specs/meta.js +8 -2
- package/src/command-specs/smoke.js +386 -0
- package/src/command-specs/tools.js +237 -44
- package/src/runtime/agent-browser.js +204 -21
- package/src/runtime/app-changelog.js +79 -0
- package/src/runtime/app-dev-server.js +21 -4
- package/src/runtime/app-dev-sessions.js +99 -1
- package/src/runtime/app-platform.js +197 -18
- package/src/runtime/assets/store-screenshot-dark.png +0 -0
- package/src/runtime/desktop-auth.js +93 -0
- package/src/runtime/output.js +12 -1
- package/src/runtime/profiles.js +72 -16
- package/src/runtime/store-screenshot.js +138 -0
- package/src/runtime/transport.js +24 -82
- package/template/.harness/index.html.tmpl +128 -6
- package/template/CHANGELOG.md +5 -0
- package/template/app/page.tsx +11 -41
- package/template/notis.config.ts +0 -1
- package/template/package-lock.json +4137 -0
- package/template/package.json +1 -0
- package/template/packages/sdk/package.json +4 -0
- package/template/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/template/packages/sdk/src/components/Markdown.tsx +60 -0
- package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +7 -2
- package/template/packages/sdk/src/config.ts +74 -0
- package/template/packages/sdk/src/documents.ts +229 -0
- package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/template/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/template/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/template/packages/sdk/src/hooks/useMultiSelect.ts +38 -2
- package/template/packages/sdk/src/hooks/useUpsertDocument.ts +54 -9
- package/template/packages/sdk/src/index.ts +30 -1
- package/template/packages/sdk/src/runtime.ts +76 -17
- package/template/packages/sdk/src/styles.css +148 -0
- /package/{template → dist/scaffolds/notis-affiliate-prospects}/packages/sdk/src/hooks/useDatabase.ts +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useNotisRuntime } from '../provider';
|
|
5
|
+
import type { ToolDescriptor } from '../runtime';
|
|
6
|
+
|
|
7
|
+
interface UseToolsResult {
|
|
8
|
+
tools: ToolDescriptor[];
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* List all tools available to this app.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const { tools, loading } = useTools();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function useTools(): UseToolsResult {
|
|
21
|
+
const runtime = useNotisRuntime();
|
|
22
|
+
const [tools, setTools] = useState<ToolDescriptor[]>([]);
|
|
23
|
+
const [loading, setLoading] = useState(true);
|
|
24
|
+
const [error, setError] = useState<Error | null>(null);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!runtime) {
|
|
28
|
+
setLoading(false);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let cancelled = false;
|
|
33
|
+
setLoading(true);
|
|
34
|
+
|
|
35
|
+
runtime
|
|
36
|
+
.listTools()
|
|
37
|
+
.then((result) => {
|
|
38
|
+
if (!cancelled) {
|
|
39
|
+
setTools(result);
|
|
40
|
+
setLoading(false);
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
.catch((err) => {
|
|
44
|
+
if (!cancelled) {
|
|
45
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
46
|
+
setLoading(false);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return () => {
|
|
51
|
+
cancelled = true;
|
|
52
|
+
};
|
|
53
|
+
}, [runtime]);
|
|
54
|
+
|
|
55
|
+
return { tools, loading, error };
|
|
56
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect } from 'react';
|
|
4
|
+
import { useNotisRuntime } from '../provider';
|
|
5
|
+
|
|
6
|
+
interface TopBarSearchOptions {
|
|
7
|
+
/** Current value shown in the top bar search input. */
|
|
8
|
+
value: string;
|
|
9
|
+
/** Called when the user types in the top bar. */
|
|
10
|
+
onChange: (value: string) => void;
|
|
11
|
+
/** Placeholder text for the input. Defaults to 'Search…'. */
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
/** Called when the user presses Enter. May return a Promise. */
|
|
14
|
+
onSubmit?: () => void | Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface TopBarSearchActions {
|
|
18
|
+
/** Toggle the loader that replaces the search icon in the top bar. */
|
|
19
|
+
setLoading: (loading: boolean) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Registers this view's search binding with the portal's top bar search input.
|
|
24
|
+
* While the component is mounted, typing in the top bar calls `onChange`,
|
|
25
|
+
* pressing Enter calls `onSubmit`, and the `value` prop drives the input.
|
|
26
|
+
*
|
|
27
|
+
* Call `setLoading(true)` to show the standard spinner in the top bar while a
|
|
28
|
+
* query is running; `setLoading(false)` restores the search icon.
|
|
29
|
+
*
|
|
30
|
+
* Without a portal runtime, this hook is a safe no-op.
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* const [q, setQ] = useState('');
|
|
34
|
+
* const { setLoading } = useTopBarSearch({
|
|
35
|
+
* value: q,
|
|
36
|
+
* onChange: setQ,
|
|
37
|
+
* placeholder: 'Search notes…',
|
|
38
|
+
* onSubmit: async () => {
|
|
39
|
+
* setLoading(true);
|
|
40
|
+
* try { await runSearch(q); } finally { setLoading(false); }
|
|
41
|
+
* },
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Stabilize `onChange` and `onSubmit` with `useCallback` to avoid re-registering
|
|
46
|
+
* on every render.
|
|
47
|
+
*/
|
|
48
|
+
export function useTopBarSearch(opts: TopBarSearchOptions): TopBarSearchActions {
|
|
49
|
+
const runtime = useNotisRuntime();
|
|
50
|
+
const { value, onChange, placeholder, onSubmit } = opts;
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
const register = runtime?.registerTopBarSearch;
|
|
54
|
+
if (!register) return;
|
|
55
|
+
register({ onChange, placeholder, onSubmit });
|
|
56
|
+
return () => {
|
|
57
|
+
register(null);
|
|
58
|
+
};
|
|
59
|
+
}, [runtime, onChange, placeholder, onSubmit]);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
runtime?.setTopBarSearchValue?.(value);
|
|
63
|
+
}, [runtime, value]);
|
|
64
|
+
|
|
65
|
+
const setLoading = useCallback(
|
|
66
|
+
(loading: boolean) => {
|
|
67
|
+
runtime?.setTopBarSearchLoading?.(loading);
|
|
68
|
+
},
|
|
69
|
+
[runtime],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return { setLoading };
|
|
73
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from 'react';
|
|
4
|
+
import { useNotisRuntime } from '../provider';
|
|
5
|
+
import { normalizeDocumentRecord } from '../documents';
|
|
6
|
+
import type { DocumentRecord } from '../runtime';
|
|
7
|
+
|
|
8
|
+
export interface UpsertDocumentArgs {
|
|
9
|
+
documentId?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
/** Property values keyed by property name (already-plain values). */
|
|
12
|
+
properties?: Record<string, unknown>;
|
|
13
|
+
/** Markdown content; converted server-side to BlockNote. Exclusive with contentBlocknote. */
|
|
14
|
+
contentMarkdown?: string | null;
|
|
15
|
+
/** BlockNote block JSON. Exclusive with contentMarkdown. */
|
|
16
|
+
contentBlocknote?: Array<Record<string, unknown>> | null;
|
|
17
|
+
/** 'archive' soft-deletes the document (sets archived_at); 'restore' undoes it. */
|
|
18
|
+
operation?: 'create' | 'update' | 'archive' | 'restore';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface UpsertToolResult {
|
|
22
|
+
status?: string;
|
|
23
|
+
document?: unknown;
|
|
24
|
+
message?: string;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UseUpsertDocumentResult {
|
|
29
|
+
upsert: (args: UpsertDocumentArgs) => Promise<DocumentRecord>;
|
|
30
|
+
loading: boolean;
|
|
31
|
+
error: Error | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function upsertToolName(databaseSlug: string): string {
|
|
35
|
+
return `LOCAL_NOTIS_DATABASE_UPSERT_${databaseSlug.replace(/-/g, '_').toUpperCase()}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function buildToolArgs(args: UpsertDocumentArgs): Record<string, unknown> {
|
|
39
|
+
const payload: Record<string, unknown> = {};
|
|
40
|
+
if (args.documentId) payload.document_id = args.documentId;
|
|
41
|
+
if (typeof args.title === 'string') payload.title = args.title;
|
|
42
|
+
if (args.properties) Object.assign(payload, args.properties);
|
|
43
|
+
if ('contentMarkdown' in args) payload.content_markdown = args.contentMarkdown ?? '';
|
|
44
|
+
if ('contentBlocknote' in args) payload.content_blocknote = args.contentBlocknote ?? null;
|
|
45
|
+
if (args.operation) payload.operation = args.operation;
|
|
46
|
+
return payload;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create/update/archive documents in a Notis database.
|
|
51
|
+
*
|
|
52
|
+
* ```tsx
|
|
53
|
+
* const { upsert } = useUpsertDocument('journal_entries');
|
|
54
|
+
* await upsert({ documentId, properties: { Mood: 'Great' }, contentMarkdown: reflection });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function useUpsertDocument(databaseSlug: string): UseUpsertDocumentResult {
|
|
58
|
+
const runtime = useNotisRuntime();
|
|
59
|
+
const [loading, setLoading] = useState(false);
|
|
60
|
+
const [error, setError] = useState<Error | null>(null);
|
|
61
|
+
|
|
62
|
+
const upsert = useCallback(
|
|
63
|
+
async (args: UpsertDocumentArgs): Promise<DocumentRecord> => {
|
|
64
|
+
if (!runtime) {
|
|
65
|
+
throw new Error('Notis runtime not available. Ensure NotisProvider is mounted.');
|
|
66
|
+
}
|
|
67
|
+
if ('contentMarkdown' in args && 'contentBlocknote' in args) {
|
|
68
|
+
throw new Error('Provide contentMarkdown or contentBlocknote, not both.');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setLoading(true);
|
|
72
|
+
setError(null);
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const result = await runtime.callTool<UpsertToolResult>(
|
|
76
|
+
upsertToolName(databaseSlug),
|
|
77
|
+
buildToolArgs(args),
|
|
78
|
+
);
|
|
79
|
+
if (!result.document) {
|
|
80
|
+
throw new Error(result.error ?? result.message ?? 'Failed to save document');
|
|
81
|
+
}
|
|
82
|
+
return normalizeDocumentRecord(result.document);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
85
|
+
setError(e);
|
|
86
|
+
throw e;
|
|
87
|
+
} finally {
|
|
88
|
+
setLoading(false);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[runtime, databaseSlug],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
return { upsert, loading, error };
|
|
95
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @notis/sdk - The Notis App SDK
|
|
3
|
+
*
|
|
4
|
+
* Public API for building Notis apps. Import hooks and the provider from
|
|
5
|
+
* this entrypoint. For configuration, use `@notis/sdk/config`. For the
|
|
6
|
+
* Vite config builder, use `@notis/sdk/vite`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Provider
|
|
10
|
+
export { NotisProvider, useNotisRuntime } from './provider';
|
|
11
|
+
|
|
12
|
+
// Hooks
|
|
13
|
+
export { useNotis } from './hooks/useNotis';
|
|
14
|
+
export { useDocuments } from './hooks/useDocuments';
|
|
15
|
+
export type { UseDocumentsOptions, UseDocumentsResult } from './hooks/useDocuments';
|
|
16
|
+
export { useDocument } from './hooks/useDocument';
|
|
17
|
+
export type { UseDocumentOptions, UseDocumentResult } from './hooks/useDocument';
|
|
18
|
+
export { useUpsertDocument } from './hooks/useUpsertDocument';
|
|
19
|
+
export type { UpsertDocumentArgs, UseUpsertDocumentResult } from './hooks/useUpsertDocument';
|
|
20
|
+
export { useDatabaseSchema } from './hooks/useDatabaseSchema';
|
|
21
|
+
export type { UseDatabaseSchemaResult } from './hooks/useDatabaseSchema';
|
|
22
|
+
export { useTool } from './hooks/useTool';
|
|
23
|
+
export type { ToolCallState, UseToolResult } from './hooks/useTool';
|
|
24
|
+
export { useTools } from './hooks/useTools';
|
|
25
|
+
export { useNotisNavigation } from './hooks/useNotisNavigation';
|
|
26
|
+
export { useTopBarSearch } from './hooks/useTopBarSearch';
|
|
27
|
+
export { useBackend } from './hooks/useBackend';
|
|
28
|
+
export { useMultiSelect } from './hooks/useMultiSelect';
|
|
29
|
+
|
|
30
|
+
// Documents & markdown
|
|
31
|
+
export { Markdown } from './components/Markdown';
|
|
32
|
+
export type { MarkdownProps } from './components/Markdown';
|
|
33
|
+
export { DocumentEditor } from './components/DocumentEditor';
|
|
34
|
+
export {
|
|
35
|
+
asRecord,
|
|
36
|
+
blockNoteToPlainText,
|
|
37
|
+
extractRichText,
|
|
38
|
+
getDocumentPreview,
|
|
39
|
+
getRelationIds,
|
|
40
|
+
isPresentString,
|
|
41
|
+
markdownToPlainText,
|
|
42
|
+
normalizeDatabaseProperty,
|
|
43
|
+
normalizeDocumentRecord,
|
|
44
|
+
normalizePropertyValue,
|
|
45
|
+
optionalString,
|
|
46
|
+
} from './documents';
|
|
47
|
+
|
|
48
|
+
// Multi-select components
|
|
49
|
+
export { MultiSelectActionBar } from './components/MultiSelectActionBar';
|
|
50
|
+
export { MultiSelectCheckbox } from './components/MultiSelectCheckbox';
|
|
51
|
+
export { MultiSelectDragOverlay } from './components/MultiSelectDragOverlay';
|
|
52
|
+
export type {
|
|
53
|
+
DragRect,
|
|
54
|
+
MultiSelectController,
|
|
55
|
+
UseMultiSelectOptions,
|
|
56
|
+
} from './hooks/useMultiSelect';
|
|
57
|
+
export type {
|
|
58
|
+
MultiSelectAction,
|
|
59
|
+
MultiSelectActionBarProps,
|
|
60
|
+
} from './components/MultiSelectActionBar';
|
|
61
|
+
export type { MultiSelectCheckboxProps } from './components/MultiSelectCheckbox';
|
|
62
|
+
export type { MultiSelectDragOverlayProps } from './components/MultiSelectDragOverlay';
|
|
63
|
+
|
|
64
|
+
// Types (re-exported for convenience)
|
|
65
|
+
export type {
|
|
66
|
+
AppDescriptor,
|
|
67
|
+
CollectionItem,
|
|
68
|
+
CollectionItemDetail,
|
|
69
|
+
DatabaseDescriptor,
|
|
70
|
+
DatabaseProperty,
|
|
71
|
+
DatabasePropertyOption,
|
|
72
|
+
DatabasePropertyType,
|
|
73
|
+
DocumentContentType,
|
|
74
|
+
DocumentRecord,
|
|
75
|
+
NotisDocumentEditorProps,
|
|
76
|
+
NotisRuntime,
|
|
77
|
+
NotisRuntimeContext,
|
|
78
|
+
NotisRuntimeUI,
|
|
79
|
+
QueryFilter,
|
|
80
|
+
RouteDescriptor,
|
|
81
|
+
ToolDescriptor,
|
|
82
|
+
ToolInputSchema,
|
|
83
|
+
} from './runtime';
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { createContext, useContext, type Context, type ReactNode } from 'react';
|
|
4
|
+
import type { NotisRuntime } from './runtime';
|
|
5
|
+
|
|
6
|
+
const NOTIS_CONTEXT_SYMBOL = Symbol.for('notis.sdk.runtime_context');
|
|
7
|
+
|
|
8
|
+
type NotisContextGlobal = typeof globalThis & {
|
|
9
|
+
[NOTIS_CONTEXT_SYMBOL]?: Context<NotisRuntime | null>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function getNotisContext(): Context<NotisRuntime | null> {
|
|
13
|
+
const scope = globalThis as NotisContextGlobal;
|
|
14
|
+
if (!scope[NOTIS_CONTEXT_SYMBOL]) {
|
|
15
|
+
scope[NOTIS_CONTEXT_SYMBOL] = createContext<NotisRuntime | null>(null);
|
|
16
|
+
}
|
|
17
|
+
return scope[NOTIS_CONTEXT_SYMBOL]!;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NotisContext = getNotisContext();
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Provides the Notis runtime context to all child components. The portal owns
|
|
24
|
+
* the runtime and injects it into the rendered app tree.
|
|
25
|
+
*/
|
|
26
|
+
export function NotisProvider({ children, runtime }: { children: ReactNode; runtime: NotisRuntime | null }) {
|
|
27
|
+
if (runtime === undefined) {
|
|
28
|
+
throw new Error('NotisProvider requires an explicit runtime prop.');
|
|
29
|
+
}
|
|
30
|
+
return (
|
|
31
|
+
<NotisContext.Provider value={runtime}>
|
|
32
|
+
{children}
|
|
33
|
+
</NotisContext.Provider>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns the raw NotisRuntime or null if not yet available.
|
|
39
|
+
* Prefer the typed hooks (useTool, useNotis, etc.) over this.
|
|
40
|
+
*/
|
|
41
|
+
export function useNotisRuntime(): NotisRuntime | null {
|
|
42
|
+
return useContext(NotisContext);
|
|
43
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NotisRuntime is the bridge between app code running in the browser and the
|
|
3
|
+
* Notis platform. The portal owns the runtime and injects it through
|
|
4
|
+
* `NotisProvider` when the app is rendered.
|
|
5
|
+
*
|
|
6
|
+
* App code should never reach for globals. Use the hooks from `@notis/sdk`
|
|
7
|
+
* (useTool, useNotis, etc.) which read from the NotisProvider context.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ComponentType } from 'react';
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Database types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export type DatabasePropertyType =
|
|
17
|
+
| 'title'
|
|
18
|
+
| 'rich_text'
|
|
19
|
+
| 'text'
|
|
20
|
+
| 'number'
|
|
21
|
+
| 'checkbox'
|
|
22
|
+
| 'date'
|
|
23
|
+
| 'select'
|
|
24
|
+
| 'multi_select'
|
|
25
|
+
| 'status'
|
|
26
|
+
| 'relation'
|
|
27
|
+
| 'formula'
|
|
28
|
+
| 'files';
|
|
29
|
+
|
|
30
|
+
export interface DatabasePropertyOption {
|
|
31
|
+
id?: string | null;
|
|
32
|
+
name: string;
|
|
33
|
+
color?: string | null;
|
|
34
|
+
order?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface DatabaseProperty {
|
|
38
|
+
id?: string | null;
|
|
39
|
+
name: string;
|
|
40
|
+
type: DatabasePropertyType;
|
|
41
|
+
description?: string | null;
|
|
42
|
+
options?: DatabasePropertyOption[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface DatabaseDescriptor {
|
|
46
|
+
slug: string;
|
|
47
|
+
title: string;
|
|
48
|
+
description?: string | null;
|
|
49
|
+
icon?: string | null;
|
|
50
|
+
properties: DatabaseProperty[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A document's content is typed. `markdown` documents carry rich text as
|
|
55
|
+
* BlockNote JSON (canonical) with a derived markdown projection; `file`
|
|
56
|
+
* documents carry an uploaded file whose format is in `fileType`
|
|
57
|
+
* (pdf, xlsx, pptx, ...). Future content types (canvas, ...) extend this
|
|
58
|
+
* union without changing the component contracts.
|
|
59
|
+
*/
|
|
60
|
+
export type DocumentContentType = 'markdown' | 'file';
|
|
61
|
+
|
|
62
|
+
export interface DocumentRecord {
|
|
63
|
+
id: string;
|
|
64
|
+
title: string;
|
|
65
|
+
properties: Record<string, unknown>;
|
|
66
|
+
url?: string | null;
|
|
67
|
+
icon?: string | null;
|
|
68
|
+
cover?: string | null;
|
|
69
|
+
databaseSlug?: string;
|
|
70
|
+
contentType?: DocumentContentType | null;
|
|
71
|
+
fileType?: string | null;
|
|
72
|
+
contentBlocknote?: Array<Record<string, unknown>> | null;
|
|
73
|
+
contentMarkdown?: string | null;
|
|
74
|
+
plainText?: string | null;
|
|
75
|
+
createdAt?: string | null;
|
|
76
|
+
lastEditedTime?: string | null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Tool types
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
export type ToolInputSchema = Record<string, unknown>;
|
|
84
|
+
|
|
85
|
+
export interface ToolDescriptor {
|
|
86
|
+
name: string;
|
|
87
|
+
description?: string;
|
|
88
|
+
inputSchema?: ToolInputSchema;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Route types
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
export interface RouteDescriptor {
|
|
96
|
+
slug: string;
|
|
97
|
+
path: string;
|
|
98
|
+
name: string;
|
|
99
|
+
icon?: string | null;
|
|
100
|
+
parentSlug?: string | null;
|
|
101
|
+
default?: boolean;
|
|
102
|
+
collection?: {
|
|
103
|
+
database: string;
|
|
104
|
+
titleProperty: string;
|
|
105
|
+
parentProperty?: string | null;
|
|
106
|
+
sidebar?: {
|
|
107
|
+
mode: 'flat-list' | 'tree';
|
|
108
|
+
allowCreate: boolean;
|
|
109
|
+
} | null;
|
|
110
|
+
} | null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface CollectionItem {
|
|
114
|
+
id: string;
|
|
115
|
+
title: string;
|
|
116
|
+
icon?: string | null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface CollectionItemDetail extends CollectionItem {
|
|
120
|
+
properties: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// App descriptor
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
|
|
127
|
+
export interface AppDescriptor {
|
|
128
|
+
id: string;
|
|
129
|
+
name: string;
|
|
130
|
+
icon?: string | null;
|
|
131
|
+
description?: string | null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface QueryFilter {
|
|
135
|
+
filters?: Array<{
|
|
136
|
+
property: string;
|
|
137
|
+
operator: string;
|
|
138
|
+
type?: string;
|
|
139
|
+
value: unknown;
|
|
140
|
+
}>;
|
|
141
|
+
sorts?: Array<{
|
|
142
|
+
property: string;
|
|
143
|
+
direction: 'asc' | 'desc';
|
|
144
|
+
}>;
|
|
145
|
+
page_size?: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface NotisRuntimeContext {
|
|
149
|
+
collectionItem?: CollectionItemDetail | null;
|
|
150
|
+
/**
|
|
151
|
+
* Set when the app is being rendered by the screenshot harness (`notis apps
|
|
152
|
+
* screenshot`) for the named listing scenario. Lets apps and SDK components
|
|
153
|
+
* hide dev-only chrome from listing images.
|
|
154
|
+
*/
|
|
155
|
+
screenshotScenario?: string | null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Host-provided UI
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Props for the host-provided document editor. The host implementation
|
|
164
|
+
* dispatches on the document's content type (markdown -> rich text editor,
|
|
165
|
+
* file -> matching viewer/editor), so this contract stays stable as new
|
|
166
|
+
* content types ship.
|
|
167
|
+
*/
|
|
168
|
+
export interface NotisDocumentEditorProps {
|
|
169
|
+
documentId: string;
|
|
170
|
+
/** 'full' renders icon/title/cover above the content; 'body' renders content only. */
|
|
171
|
+
variant?: 'full' | 'body';
|
|
172
|
+
readOnly?: boolean;
|
|
173
|
+
className?: string;
|
|
174
|
+
onDirtyChange?: (dirty: boolean) => void;
|
|
175
|
+
onSavingChange?: (saving: boolean) => void;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Components the host (portal) injects through the runtime. Apps consume them
|
|
180
|
+
* via the SDK wrappers (e.g. `DocumentEditor`), which fall back gracefully
|
|
181
|
+
* when a host component is unavailable (standalone dev harness, snapshots).
|
|
182
|
+
*/
|
|
183
|
+
export interface NotisRuntimeUI {
|
|
184
|
+
DocumentEditor?: ComponentType<NotisDocumentEditorProps>;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// NotisRuntime
|
|
189
|
+
// ---------------------------------------------------------------------------
|
|
190
|
+
|
|
191
|
+
export interface NotisRuntime {
|
|
192
|
+
app: AppDescriptor;
|
|
193
|
+
route: RouteDescriptor;
|
|
194
|
+
databases: DatabaseDescriptor[];
|
|
195
|
+
context: NotisRuntimeContext;
|
|
196
|
+
ui?: NotisRuntimeUI;
|
|
197
|
+
|
|
198
|
+
navigate?: (payload: { kind: string; [key: string]: unknown }) => void;
|
|
199
|
+
|
|
200
|
+
registerTopBarSearch?: (
|
|
201
|
+
config:
|
|
202
|
+
| {
|
|
203
|
+
onChange: (value: string) => void;
|
|
204
|
+
placeholder?: string;
|
|
205
|
+
onSubmit?: () => void | Promise<void>;
|
|
206
|
+
}
|
|
207
|
+
| null,
|
|
208
|
+
) => void;
|
|
209
|
+
setTopBarSearchValue?: (value: string) => void;
|
|
210
|
+
setTopBarSearchLoading?: (loading: boolean) => void;
|
|
211
|
+
|
|
212
|
+
listTools(): Promise<ToolDescriptor[]>;
|
|
213
|
+
callTool<TResult = unknown>(name: string, args?: Record<string, unknown>): Promise<TResult>;
|
|
214
|
+
|
|
215
|
+
request(path: string, options?: {
|
|
216
|
+
method?: string;
|
|
217
|
+
headers?: Record<string, string>;
|
|
218
|
+
body?: unknown;
|
|
219
|
+
}): Promise<unknown>;
|
|
220
|
+
}
|