@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,58 @@
|
|
|
1
|
+
import type { Config } from 'tailwindcss';
|
|
2
|
+
import tailwindAnimate from 'tailwindcss-animate';
|
|
3
|
+
|
|
4
|
+
const config: Config = {
|
|
5
|
+
darkMode: ['class'],
|
|
6
|
+
content: [
|
|
7
|
+
'./app/**/*.{ts,tsx}',
|
|
8
|
+
'./components/**/*.{ts,tsx}',
|
|
9
|
+
'./lib/**/*.{ts,tsx}',
|
|
10
|
+
],
|
|
11
|
+
theme: {
|
|
12
|
+
extend: {
|
|
13
|
+
colors: {
|
|
14
|
+
border: 'hsl(var(--border))',
|
|
15
|
+
input: 'hsl(var(--input))',
|
|
16
|
+
ring: 'hsl(var(--ring))',
|
|
17
|
+
background: 'hsl(var(--background))',
|
|
18
|
+
foreground: 'hsl(var(--foreground))',
|
|
19
|
+
primary: {
|
|
20
|
+
DEFAULT: 'hsl(var(--primary))',
|
|
21
|
+
foreground: 'hsl(var(--primary-foreground))',
|
|
22
|
+
},
|
|
23
|
+
secondary: {
|
|
24
|
+
DEFAULT: 'hsl(var(--secondary))',
|
|
25
|
+
foreground: 'hsl(var(--secondary-foreground))',
|
|
26
|
+
},
|
|
27
|
+
destructive: {
|
|
28
|
+
DEFAULT: 'hsl(var(--destructive))',
|
|
29
|
+
foreground: 'hsl(var(--destructive-foreground))',
|
|
30
|
+
},
|
|
31
|
+
muted: {
|
|
32
|
+
DEFAULT: 'hsl(var(--muted))',
|
|
33
|
+
foreground: 'hsl(var(--muted-foreground))',
|
|
34
|
+
},
|
|
35
|
+
accent: {
|
|
36
|
+
DEFAULT: 'hsl(var(--accent))',
|
|
37
|
+
foreground: 'hsl(var(--accent-foreground))',
|
|
38
|
+
},
|
|
39
|
+
popover: {
|
|
40
|
+
DEFAULT: 'hsl(var(--popover))',
|
|
41
|
+
foreground: 'hsl(var(--popover-foreground))',
|
|
42
|
+
},
|
|
43
|
+
card: {
|
|
44
|
+
DEFAULT: 'hsl(var(--card))',
|
|
45
|
+
foreground: 'hsl(var(--card-foreground))',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
borderRadius: {
|
|
49
|
+
lg: 'var(--radius)',
|
|
50
|
+
md: 'calc(var(--radius) - 2px)',
|
|
51
|
+
sm: 'calc(var(--radius) - 4px)',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
plugins: [tailwindAnimate],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default config;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2017",
|
|
4
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"preserveSymlinks": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"isolatedModules": true,
|
|
15
|
+
"jsx": "react-jsx",
|
|
16
|
+
"types": ["vite/client", "node"],
|
|
17
|
+
"paths": {
|
|
18
|
+
"@/*": ["./*"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
22
|
+
"exclude": ["node_modules"]
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { notisViteConfig } from '@notis/sdk/vite';
|
|
2
|
+
import react from '@vitejs/plugin-react';
|
|
3
|
+
import appConfig from './notis.config';
|
|
4
|
+
|
|
5
|
+
const config = notisViteConfig(appConfig);
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
...config,
|
|
9
|
+
plugins: [react(), ...(config.plugins || [])],
|
|
10
|
+
};
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import {
|
|
5
5
|
MultiSelectActionBar,
|
|
6
6
|
MultiSelectCheckbox,
|
|
7
7
|
MultiSelectDragOverlay,
|
|
8
8
|
useMultiSelect,
|
|
9
9
|
useBackend,
|
|
10
|
+
useDatabaseSchema,
|
|
11
|
+
useDocuments,
|
|
10
12
|
useNotis,
|
|
11
13
|
useNotisNavigation,
|
|
12
14
|
useTopBarSearch,
|
|
13
|
-
|
|
15
|
+
useUpsertDocument,
|
|
16
|
+
asRecord,
|
|
17
|
+
getDocumentPreview,
|
|
18
|
+
getRelationIds,
|
|
19
|
+
isPresentString,
|
|
20
|
+
optionalString,
|
|
21
|
+
type DatabaseProperty,
|
|
22
|
+
type DocumentRecord,
|
|
14
23
|
type MultiSelectController,
|
|
24
|
+
type UpsertDocumentArgs,
|
|
15
25
|
} from '@notis/sdk';
|
|
16
26
|
import { ArrowUpRightIcon as ArrowUpRight, CubeIcon as Boxes, BookOpenIcon as BookOpen, BookOpenTextIcon as BookOpenText, CalendarIcon as CalendarDays, CaretDownIcon as ChevronDown, CaretLeftIcon as ChevronLeft, CaretRightIcon as ChevronRight, FileTextIcon as FileText, FolderIcon as Folder, FolderMinusIcon as FolderMinus, FolderOpenIcon as FolderOpen, FolderPlusIcon as FolderPlus, FoldersIcon as Folders, SquaresFourIcon as LayoutGrid, CircleNotchIcon as Loader2, MagnifyingGlassIcon as Search, NotePencilIcon as NotebookPen, PencilIcon as Pencil, PlusIcon as Plus, NoteIcon as StickyNote, TableIcon as Table2, TrashIcon as Trash, XIcon as X, type Icon } from '@phosphor-icons/react';
|
|
17
27
|
import * as PhosphorIcons from '@phosphor-icons/react';
|
|
@@ -34,50 +44,6 @@ type FolderOption = {
|
|
|
34
44
|
pathLabel: string;
|
|
35
45
|
};
|
|
36
46
|
|
|
37
|
-
type DatabasePropertyType =
|
|
38
|
-
| 'title'
|
|
39
|
-
| 'rich_text'
|
|
40
|
-
| 'text'
|
|
41
|
-
| 'number'
|
|
42
|
-
| 'checkbox'
|
|
43
|
-
| 'date'
|
|
44
|
-
| 'select'
|
|
45
|
-
| 'multi_select'
|
|
46
|
-
| 'status'
|
|
47
|
-
| 'relation'
|
|
48
|
-
| 'formula'
|
|
49
|
-
| 'files';
|
|
50
|
-
|
|
51
|
-
type DatabasePropertyOption = {
|
|
52
|
-
id?: string | null;
|
|
53
|
-
name: string;
|
|
54
|
-
color?: string | null;
|
|
55
|
-
order?: number;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
type DatabaseProperty = {
|
|
59
|
-
id?: string | null;
|
|
60
|
-
name: string;
|
|
61
|
-
type: DatabasePropertyType;
|
|
62
|
-
description?: string | null;
|
|
63
|
-
options?: DatabasePropertyOption[];
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
type DocumentRecord = {
|
|
67
|
-
id: string;
|
|
68
|
-
title: string;
|
|
69
|
-
properties: Record<string, unknown>;
|
|
70
|
-
url?: string | null;
|
|
71
|
-
icon?: string | null;
|
|
72
|
-
cover?: string | null;
|
|
73
|
-
databaseSlug?: string;
|
|
74
|
-
contentBlocknote?: Array<Record<string, unknown>> | null;
|
|
75
|
-
contentMarkdown?: string | null;
|
|
76
|
-
plainText?: string | null;
|
|
77
|
-
createdAt?: string | null;
|
|
78
|
-
lastEditedTime?: string | null;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
47
|
type CollectionItemContext = {
|
|
82
48
|
id: string;
|
|
83
49
|
title: string;
|
|
@@ -85,69 +51,6 @@ type CollectionItemContext = {
|
|
|
85
51
|
properties: Record<string, unknown>;
|
|
86
52
|
};
|
|
87
53
|
|
|
88
|
-
type QueryFilter = {
|
|
89
|
-
filters?: Array<{
|
|
90
|
-
property: string;
|
|
91
|
-
operator: string;
|
|
92
|
-
type?: string;
|
|
93
|
-
value: unknown;
|
|
94
|
-
}>;
|
|
95
|
-
sorts?: Array<{
|
|
96
|
-
property: string;
|
|
97
|
-
direction: 'asc' | 'desc';
|
|
98
|
-
}>;
|
|
99
|
-
page_size?: number;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
type QueryDatabaseArgs = {
|
|
103
|
-
database_slug: string;
|
|
104
|
-
query?: QueryFilter;
|
|
105
|
-
offset?: number;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
type QueryDatabaseResult = {
|
|
109
|
-
documents?: unknown[];
|
|
110
|
-
message?: string;
|
|
111
|
-
error?: string;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
type GetDatabaseArgs = {
|
|
115
|
-
database_slug: string;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
type GetDatabaseResult = {
|
|
119
|
-
database?: {
|
|
120
|
-
schema?: {
|
|
121
|
-
properties?: unknown[];
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
message?: string;
|
|
125
|
-
error?: string;
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
type UpsertNoteArgs = {
|
|
129
|
-
document_id?: string;
|
|
130
|
-
title?: string;
|
|
131
|
-
content_blocknote?: Array<Record<string, unknown>> | null;
|
|
132
|
-
[propertyName: string]: unknown;
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
type UpsertNoteResult = {
|
|
136
|
-
status?: string;
|
|
137
|
-
document?: unknown;
|
|
138
|
-
message?: string;
|
|
139
|
-
error?: string;
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
type UpsertDocumentArgs = {
|
|
143
|
-
documentId?: string;
|
|
144
|
-
title?: string;
|
|
145
|
-
properties?: Record<string, unknown>;
|
|
146
|
-
contentBlocknote?: Array<Record<string, unknown>> | null;
|
|
147
|
-
/** 'archive' soft-deletes the document (sets archived_at); 'restore' undoes it. */
|
|
148
|
-
operation?: 'create' | 'update' | 'archive' | 'restore';
|
|
149
|
-
};
|
|
150
|
-
|
|
151
54
|
const NOTE_DATABASE_SLUG = 'notes';
|
|
152
55
|
const FOLDER_DATABASE_SLUG = 'note_folders';
|
|
153
56
|
const DEFAULT_NOTE_TITLE = 'Untitled note';
|
|
@@ -251,112 +154,6 @@ const ALL_PHOSPHOR_ICON_NAMES = Object.keys(PhosphorIcons)
|
|
|
251
154
|
.map(toKebabCase)
|
|
252
155
|
.sort((a, b) => a.localeCompare(b));
|
|
253
156
|
|
|
254
|
-
function isPresentString(value: unknown): value is string {
|
|
255
|
-
return typeof value === 'string' && value.trim().length > 0;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
259
|
-
return value && typeof value === 'object' && !Array.isArray(value)
|
|
260
|
-
? (value as Record<string, unknown>)
|
|
261
|
-
: null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function optionalString(value: unknown): string | null {
|
|
265
|
-
return isPresentString(value) ? value.trim() : null;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function extractRichText(value: unknown): string {
|
|
269
|
-
if (typeof value === 'string') return value;
|
|
270
|
-
if (!Array.isArray(value)) return '';
|
|
271
|
-
return value
|
|
272
|
-
.map((item) => {
|
|
273
|
-
const record = asRecord(item);
|
|
274
|
-
const text = asRecord(record?.text);
|
|
275
|
-
return optionalString(text?.content) ?? optionalString(record?.plain_text) ?? '';
|
|
276
|
-
})
|
|
277
|
-
.join('');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function normalizePropertyValue(value: unknown): unknown {
|
|
281
|
-
const record = asRecord(value);
|
|
282
|
-
if (!record) return value;
|
|
283
|
-
|
|
284
|
-
const type = optionalString(record.type);
|
|
285
|
-
if (!type) return value;
|
|
286
|
-
|
|
287
|
-
if (type === 'title') return extractRichText(record.title);
|
|
288
|
-
if (type === 'rich_text') return extractRichText(record.rich_text);
|
|
289
|
-
if (type === 'select' || type === 'status') return optionalString(asRecord(record[type])?.name) ?? record[type] ?? null;
|
|
290
|
-
if (type === 'multi_select') {
|
|
291
|
-
const items = Array.isArray(record.multi_select) ? record.multi_select : [];
|
|
292
|
-
return items.map((item) => optionalString(asRecord(item)?.name) ?? item).filter(Boolean);
|
|
293
|
-
}
|
|
294
|
-
if (type === 'relation') {
|
|
295
|
-
const items = Array.isArray(record.relation) ? record.relation : [];
|
|
296
|
-
return items.map((item) => optionalString(asRecord(item)?.id) ?? item).filter(Boolean);
|
|
297
|
-
}
|
|
298
|
-
if (type === 'date') return optionalString(asRecord(record.date)?.start) ?? record.date ?? null;
|
|
299
|
-
if (type in record) return record[type];
|
|
300
|
-
return value;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function normalizeDocumentRecord(value: unknown): DocumentRecord {
|
|
304
|
-
const record = asRecord(value) ?? {};
|
|
305
|
-
const rawProperties = asRecord(record.properties) ?? {};
|
|
306
|
-
const properties: Record<string, unknown> = {};
|
|
307
|
-
for (const [key, propertyValue] of Object.entries(rawProperties)) {
|
|
308
|
-
properties[key] = normalizePropertyValue(propertyValue);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
id: optionalString(record.id) ?? '',
|
|
313
|
-
title: optionalString(record.title) ?? DEFAULT_NOTE_TITLE,
|
|
314
|
-
url: optionalString(record.url),
|
|
315
|
-
properties,
|
|
316
|
-
icon: optionalString(record.icon),
|
|
317
|
-
cover: optionalString(record.cover),
|
|
318
|
-
databaseSlug: optionalString(record.databaseSlug) ?? optionalString(record.database_slug) ?? undefined,
|
|
319
|
-
contentBlocknote: Array.isArray(record.contentBlocknote)
|
|
320
|
-
? record.contentBlocknote as Array<Record<string, unknown>>
|
|
321
|
-
: Array.isArray(record.content_blocknote)
|
|
322
|
-
? record.content_blocknote as Array<Record<string, unknown>>
|
|
323
|
-
: null,
|
|
324
|
-
contentMarkdown: optionalString(record.contentMarkdown) ?? optionalString(record.content_markdown),
|
|
325
|
-
plainText: optionalString(record.plainText) ?? optionalString(record.plain_text),
|
|
326
|
-
createdAt: optionalString(record.createdAt) ?? optionalString(record.created_at) ?? optionalString(record.created_time),
|
|
327
|
-
lastEditedTime:
|
|
328
|
-
optionalString(record.lastEditedTime)
|
|
329
|
-
?? optionalString(record.last_edited_time)
|
|
330
|
-
?? optionalString(record.updated_at),
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function normalizeDatabaseProperty(value: unknown): DatabaseProperty | null {
|
|
335
|
-
const record = asRecord(value);
|
|
336
|
-
const name = optionalString(record?.name);
|
|
337
|
-
if (!record || !name) return null;
|
|
338
|
-
const rawOptions = Array.isArray(record.options) ? record.options : [];
|
|
339
|
-
const options = rawOptions.flatMap((option): DatabasePropertyOption[] => {
|
|
340
|
-
const optionRecord = asRecord(option);
|
|
341
|
-
const optionName = optionalString(optionRecord?.name);
|
|
342
|
-
if (!optionRecord || !optionName) return [];
|
|
343
|
-
return [{
|
|
344
|
-
id: optionalString(optionRecord.id),
|
|
345
|
-
name: optionName,
|
|
346
|
-
color: optionalString(optionRecord.color),
|
|
347
|
-
order: typeof optionRecord.order === 'number' ? optionRecord.order : undefined,
|
|
348
|
-
}];
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
return {
|
|
352
|
-
id: optionalString(record.id),
|
|
353
|
-
name,
|
|
354
|
-
type: (optionalString(record.type) ?? 'rich_text') as DatabasePropertyType,
|
|
355
|
-
description: optionalString(record.description),
|
|
356
|
-
options,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
|
|
360
157
|
function normalizeCollectionItem(value: unknown): CollectionItemContext | null {
|
|
361
158
|
const record = asRecord(value);
|
|
362
159
|
const id = optionalString(record?.id);
|
|
@@ -369,105 +166,6 @@ function normalizeCollectionItem(value: unknown): CollectionItemContext | null {
|
|
|
369
166
|
};
|
|
370
167
|
}
|
|
371
168
|
|
|
372
|
-
function useNoteSchema(databaseSlug: string) {
|
|
373
|
-
const getDatabase = useTool<GetDatabaseArgs, GetDatabaseResult>('LOCAL_NOTIS_DATABASE_GET_DATABASE');
|
|
374
|
-
const [properties, setProperties] = useState<DatabaseProperty[]>([]);
|
|
375
|
-
const [loading, setLoading] = useState(true);
|
|
376
|
-
const [error, setError] = useState<Error | null>(null);
|
|
377
|
-
const [fetchKey, setFetchKey] = useState(0);
|
|
378
|
-
|
|
379
|
-
const refetch = useCallback(() => {
|
|
380
|
-
setFetchKey((key) => key + 1);
|
|
381
|
-
}, []);
|
|
382
|
-
|
|
383
|
-
useEffect(() => {
|
|
384
|
-
let cancelled = false;
|
|
385
|
-
setLoading(true);
|
|
386
|
-
setError(null);
|
|
387
|
-
|
|
388
|
-
getDatabase.call({ database_slug: databaseSlug })
|
|
389
|
-
.then((result) => {
|
|
390
|
-
if (cancelled) return;
|
|
391
|
-
const message = result.error ?? result.message;
|
|
392
|
-
if (!result.database && message) {
|
|
393
|
-
throw new Error(message);
|
|
394
|
-
}
|
|
395
|
-
const rawProperties = result.database?.schema?.properties ?? [];
|
|
396
|
-
setProperties(rawProperties.map(normalizeDatabaseProperty).filter((p): p is DatabaseProperty => Boolean(p)));
|
|
397
|
-
setLoading(false);
|
|
398
|
-
})
|
|
399
|
-
.catch((err) => {
|
|
400
|
-
if (cancelled) return;
|
|
401
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
402
|
-
setLoading(false);
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
return () => {
|
|
406
|
-
cancelled = true;
|
|
407
|
-
};
|
|
408
|
-
}, [databaseSlug, fetchKey, getDatabase.call]);
|
|
409
|
-
|
|
410
|
-
return { properties, loading, error, refetch };
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function useQueryDocuments(databaseSlug: string, options: { filter?: QueryFilter; pageSize?: number; offset?: number } = {}) {
|
|
414
|
-
const queryTool = useTool<QueryDatabaseArgs, QueryDatabaseResult>('LOCAL_NOTIS_DATABASE_QUERY');
|
|
415
|
-
const [documents, setDocuments] = useState<DocumentRecord[]>([]);
|
|
416
|
-
const [loading, setLoading] = useState(true);
|
|
417
|
-
const [error, setError] = useState<Error | null>(null);
|
|
418
|
-
const [fetchKey, setFetchKey] = useState(0);
|
|
419
|
-
const filterKey = JSON.stringify(options.filter ?? null);
|
|
420
|
-
|
|
421
|
-
const refetch = useCallback(() => {
|
|
422
|
-
setFetchKey((key) => key + 1);
|
|
423
|
-
}, []);
|
|
424
|
-
|
|
425
|
-
useEffect(() => {
|
|
426
|
-
let cancelled = false;
|
|
427
|
-
setLoading(true);
|
|
428
|
-
setError(null);
|
|
429
|
-
|
|
430
|
-
queryTool.call({
|
|
431
|
-
database_slug: databaseSlug,
|
|
432
|
-
query: {
|
|
433
|
-
...(options.filter ?? {}),
|
|
434
|
-
page_size: options.pageSize,
|
|
435
|
-
},
|
|
436
|
-
offset: options.offset,
|
|
437
|
-
})
|
|
438
|
-
.then((result) => {
|
|
439
|
-
if (cancelled) return;
|
|
440
|
-
const message = result.error ?? result.message;
|
|
441
|
-
if (message && !result.documents) {
|
|
442
|
-
throw new Error(message);
|
|
443
|
-
}
|
|
444
|
-
setDocuments((result.documents ?? []).map(normalizeDocumentRecord).filter((doc) => doc.id));
|
|
445
|
-
setLoading(false);
|
|
446
|
-
})
|
|
447
|
-
.catch((err) => {
|
|
448
|
-
if (cancelled) return;
|
|
449
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
450
|
-
setLoading(false);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
return () => {
|
|
454
|
-
cancelled = true;
|
|
455
|
-
};
|
|
456
|
-
}, [databaseSlug, fetchKey, filterKey, options.offset, options.pageSize, queryTool.call]);
|
|
457
|
-
|
|
458
|
-
return { documents, loading, error, refetch };
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
function buildUpsertNoteArgs(args: UpsertDocumentArgs): UpsertNoteArgs {
|
|
462
|
-
const payload: UpsertNoteArgs = {};
|
|
463
|
-
if (args.documentId) payload.document_id = args.documentId;
|
|
464
|
-
if (typeof args.title === 'string') payload.title = args.title;
|
|
465
|
-
if (args.properties) Object.assign(payload, args.properties);
|
|
466
|
-
if ('contentBlocknote' in args) payload.content_blocknote = args.contentBlocknote ?? null;
|
|
467
|
-
if (args.operation) payload.operation = args.operation;
|
|
468
|
-
return payload;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
169
|
function pluralize(count: number, singular: string, plural = `${singular}s`): string {
|
|
472
170
|
return `${count} ${count === 1 ? singular : plural}`;
|
|
473
171
|
}
|
|
@@ -487,11 +185,6 @@ function getNoteDateValue(note: DocumentRecord, field: string): unknown {
|
|
|
487
185
|
return note.properties[field];
|
|
488
186
|
}
|
|
489
187
|
|
|
490
|
-
function getRelationIds(value: unknown): string[] {
|
|
491
|
-
if (!Array.isArray(value)) return [];
|
|
492
|
-
return value.filter((item): item is string => isPresentString(item));
|
|
493
|
-
}
|
|
494
|
-
|
|
495
188
|
function getFolderTitle(document: DocumentRecord): string {
|
|
496
189
|
const explicit = document.properties.Name;
|
|
497
190
|
if (isPresentString(explicit)) return explicit;
|
|
@@ -502,45 +195,8 @@ function getNoteTitle(document: DocumentRecord): string {
|
|
|
502
195
|
return isPresentString(document.title) ? document.title : DEFAULT_NOTE_TITLE;
|
|
503
196
|
}
|
|
504
197
|
|
|
505
|
-
function getPlainTextFromBlockNote(value: unknown): string {
|
|
506
|
-
const segments: string[] = [];
|
|
507
|
-
|
|
508
|
-
function visit(node: unknown): void {
|
|
509
|
-
if (typeof node === 'string') {
|
|
510
|
-
if (node.trim()) segments.push(node.trim());
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
if (Array.isArray(node)) {
|
|
514
|
-
node.forEach(visit);
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
if (!node || typeof node !== 'object') return;
|
|
518
|
-
|
|
519
|
-
const record = node as Record<string, unknown>;
|
|
520
|
-
if (typeof record.text === 'string' && record.text.trim()) {
|
|
521
|
-
segments.push(record.text.trim());
|
|
522
|
-
}
|
|
523
|
-
visit(record.content);
|
|
524
|
-
visit(record.children);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
visit(value);
|
|
528
|
-
return segments.join(' ').replace(/\s+/g, ' ').trim();
|
|
529
|
-
}
|
|
530
|
-
|
|
531
198
|
function getNotePreviewText(document: DocumentRecord): string {
|
|
532
|
-
|
|
533
|
-
return document.plainText.replace(/\s+/g, ' ').trim();
|
|
534
|
-
}
|
|
535
|
-
if (isPresentString(document.contentMarkdown)) {
|
|
536
|
-
return document.contentMarkdown
|
|
537
|
-
.replace(/[#*_`>~[\]()]/g, ' ')
|
|
538
|
-
.replace(/\s+/g, ' ')
|
|
539
|
-
.trim();
|
|
540
|
-
}
|
|
541
|
-
const fromBlocks = getPlainTextFromBlockNote(document.contentBlocknote);
|
|
542
|
-
if (fromBlocks) return fromBlocks;
|
|
543
|
-
return getNoteTitle(document);
|
|
199
|
+
return getDocumentPreview(document);
|
|
544
200
|
}
|
|
545
201
|
|
|
546
202
|
function buildFolderOptions(documents: DocumentRecord[]): FolderOption[] {
|
|
@@ -1057,7 +713,7 @@ export default function NotesPage() {
|
|
|
1057
713
|
const { app, route, collectionItem: rawCollectionItem } = useNotis();
|
|
1058
714
|
const navigation = useNotisNavigation();
|
|
1059
715
|
const { request } = useBackend();
|
|
1060
|
-
const
|
|
716
|
+
const { upsert: upsertNoteDocument } = useUpsertDocument(NOTE_DATABASE_SLUG);
|
|
1061
717
|
|
|
1062
718
|
const [activeTab, setActiveTab] = useState<TabKey>('gallery');
|
|
1063
719
|
const [activeDateProperty, setActiveDateProperty] = useState(CREATED_DATE_FIELD);
|
|
@@ -1078,7 +734,7 @@ export default function NotesPage() {
|
|
|
1078
734
|
const folderRenameSubmittingRef = useRef(false);
|
|
1079
735
|
|
|
1080
736
|
const collectionItem = normalizeCollectionItem(rawCollectionItem);
|
|
1081
|
-
const noteSchema =
|
|
737
|
+
const noteSchema = useDatabaseSchema(NOTE_DATABASE_SLUG);
|
|
1082
738
|
const noteProperties = noteSchema.properties;
|
|
1083
739
|
const titleProperty = noteProperties.find((p) => p.type === 'title');
|
|
1084
740
|
const titlePropertyName = titleProperty?.name ?? 'Title';
|
|
@@ -1111,8 +767,8 @@ export default function NotesPage() {
|
|
|
1111
767
|
}
|
|
1112
768
|
: undefined;
|
|
1113
769
|
|
|
1114
|
-
const notesQuery =
|
|
1115
|
-
const foldersQuery =
|
|
770
|
+
const notesQuery = useDocuments(NOTE_DATABASE_SLUG, { filter: notesFilter, pageSize: 250 });
|
|
771
|
+
const foldersQuery = useDocuments(FOLDER_DATABASE_SLUG, { pageSize: 500 });
|
|
1116
772
|
|
|
1117
773
|
const { setLoading: setSearchLoading } = useTopBarSearch({
|
|
1118
774
|
value: searchQuery,
|
|
@@ -1186,18 +842,6 @@ export default function NotesPage() {
|
|
|
1186
842
|
navigation.toDocument(document.id, document.title);
|
|
1187
843
|
}
|
|
1188
844
|
|
|
1189
|
-
const upsertNoteDocument = useCallback(
|
|
1190
|
-
async (args: UpsertDocumentArgs): Promise<DocumentRecord> => {
|
|
1191
|
-
const result = await upsertNote.call(buildUpsertNoteArgs(args));
|
|
1192
|
-
const message = result.error ?? result.message;
|
|
1193
|
-
if (!result.document) {
|
|
1194
|
-
throw new Error(message ?? 'Failed to save note');
|
|
1195
|
-
}
|
|
1196
|
-
return normalizeDocumentRecord(result.document);
|
|
1197
|
-
},
|
|
1198
|
-
[upsertNote.call],
|
|
1199
|
-
);
|
|
1200
|
-
|
|
1201
845
|
async function createDocument() {
|
|
1202
846
|
setCreatingDocument(true);
|
|
1203
847
|
setErrorMessage(null);
|
|
@@ -8,7 +8,6 @@ export default defineNotisApp({
|
|
|
8
8
|
author: { name: 'Notis' },
|
|
9
9
|
categories: ['Productivity'],
|
|
10
10
|
tagline: 'Folder-based notes with gallery, table, and calendar views.',
|
|
11
|
-
versionNotes: 'Initial scaffold release.',
|
|
12
11
|
databases: ['note_folders', 'notes'],
|
|
13
12
|
routes: [
|
|
14
13
|
{
|
package/dist/scaffolds.json
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "apps/*/notis.config.ts",
|
|
3
3
|
"scaffolds": [
|
|
4
|
+
{
|
|
5
|
+
"slug": "notis-affiliate-prospects",
|
|
6
|
+
"name": "Affiliate Prospects",
|
|
7
|
+
"description": "A campaign control center for sourcing, qualifying, contacting, and activating high-fit Notis affiliates.",
|
|
8
|
+
"icon": "phosphor:users-three",
|
|
9
|
+
"categories": [
|
|
10
|
+
"Sales & Marketing",
|
|
11
|
+
"Operations"
|
|
12
|
+
],
|
|
13
|
+
"tagline": "Turn affiliate recruiting into a measurable, repeatable pipeline."
|
|
14
|
+
},
|
|
4
15
|
{
|
|
5
16
|
"slug": "notis-database",
|
|
6
17
|
"name": "Databases",
|
|
@@ -12,6 +23,17 @@
|
|
|
12
23
|
],
|
|
13
24
|
"tagline": "Explore every database and schema in your Notis workspace."
|
|
14
25
|
},
|
|
26
|
+
{
|
|
27
|
+
"slug": "notis-journal",
|
|
28
|
+
"name": "5 Minutes Journal",
|
|
29
|
+
"description": "A display-only five-minute journal built from your morning and evening check-ins with Notis. Browse daily mood, energy, motivation, gratitude, intentions, affirmations, highlights, lessons, and free-form reflections, then use Stats to see trends and ritual consistency over time.",
|
|
30
|
+
"icon": "phosphor:notebook",
|
|
31
|
+
"categories": [
|
|
32
|
+
"Personal",
|
|
33
|
+
"Productivity"
|
|
34
|
+
],
|
|
35
|
+
"tagline": "Five intentional minutes with Notis, split between morning and evening."
|
|
36
|
+
},
|
|
15
37
|
{
|
|
16
38
|
"slug": "notis-notes",
|
|
17
39
|
"name": "Notis Notes",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notis_ai/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "Agent-first Notis CLI for apps and generic tool execution",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"node": ">=18.0.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"commander": "^12.0.0"
|
|
32
|
+
"commander": "^12.0.0",
|
|
33
|
+
"sharp": "^0.34.5"
|
|
33
34
|
},
|
|
34
|
-
"devDependencies": {},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"notis",
|
|
37
37
|
"cli",
|