@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.
Files changed (157) hide show
  1. package/README.md +33 -9
  2. package/dist/scaffolds/notis-affiliate-prospects/CHANGELOG.md +7 -0
  3. package/dist/scaffolds/notis-affiliate-prospects/README.md +20 -0
  4. package/dist/scaffolds/notis-affiliate-prospects/app/globals.css +42 -0
  5. package/dist/scaffolds/notis-affiliate-prospects/app/layout.tsx +7 -0
  6. package/dist/scaffolds/notis-affiliate-prospects/app/page.tsx +248 -0
  7. package/dist/scaffolds/notis-affiliate-prospects/app/prospects/page.tsx +134 -0
  8. package/dist/scaffolds/notis-affiliate-prospects/app/segments/page.tsx +108 -0
  9. package/dist/scaffolds/notis-affiliate-prospects/components/ui/card.tsx +35 -0
  10. package/dist/scaffolds/notis-affiliate-prospects/components.json +20 -0
  11. package/dist/scaffolds/notis-affiliate-prospects/index.html +12 -0
  12. package/dist/scaffolds/notis-affiliate-prospects/lib/affiliate-data.ts +218 -0
  13. package/dist/scaffolds/notis-affiliate-prospects/lib/demo-prospects.ts +52 -0
  14. package/dist/scaffolds/notis-affiliate-prospects/lib/notis-tools.ts +100 -0
  15. package/dist/scaffolds/notis-affiliate-prospects/lib/utils.ts +25 -0
  16. package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-1.png +0 -0
  17. package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-2.png +0 -0
  18. package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-3.png +0 -0
  19. package/dist/scaffolds/notis-affiliate-prospects/metadata/screenshot-fixtures.json +187 -0
  20. package/dist/scaffolds/notis-affiliate-prospects/notis.config.ts +65 -0
  21. package/dist/scaffolds/notis-affiliate-prospects/package.json +32 -0
  22. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/package.json +32 -0
  23. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectActionBar.tsx +273 -0
  24. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  25. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  26. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/config.ts +90 -0
  27. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useBackend.ts +41 -0
  28. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useMultiSelect.ts +503 -0
  29. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useNotis.ts +34 -0
  30. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  31. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTool.ts +64 -0
  32. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTools.ts +56 -0
  33. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  34. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/hooks/useUpsertDocument.ts +50 -0
  35. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/index.ts +54 -0
  36. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/provider.tsx +43 -0
  37. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/runtime.ts +161 -0
  38. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/styles.css +38 -0
  39. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/ui.ts +15 -0
  40. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/src/vite.ts +56 -0
  41. package/dist/scaffolds/notis-affiliate-prospects/packages/sdk/tsconfig.json +15 -0
  42. package/dist/scaffolds/notis-affiliate-prospects/postcss.config.mjs +6 -0
  43. package/dist/scaffolds/notis-affiliate-prospects/src/dev-main.tsx +72 -0
  44. package/dist/scaffolds/notis-affiliate-prospects/src/mock-runtime.ts +242 -0
  45. package/dist/scaffolds/notis-affiliate-prospects/tailwind.config.ts +51 -0
  46. package/dist/scaffolds/notis-affiliate-prospects/tsconfig.json +23 -0
  47. package/dist/scaffolds/notis-affiliate-prospects/vite.config.ts +11 -0
  48. package/dist/scaffolds/notis-database/CHANGELOG.md +5 -0
  49. package/dist/scaffolds/notis-database/notis.config.ts +0 -1
  50. package/dist/scaffolds/notis-database/src/mock-runtime.ts +1 -0
  51. package/dist/scaffolds/notis-journal/CHANGELOG.md +25 -0
  52. package/dist/scaffolds/notis-journal/app/globals.css +37 -0
  53. package/dist/scaffolds/notis-journal/app/insights/page.tsx +513 -0
  54. package/dist/scaffolds/notis-journal/app/journal-core.tsx +362 -0
  55. package/dist/scaffolds/notis-journal/app/journal-ui.tsx +337 -0
  56. package/dist/scaffolds/notis-journal/app/layout.tsx +6 -0
  57. package/dist/scaffolds/notis-journal/app/page.tsx +485 -0
  58. package/dist/scaffolds/notis-journal/components/ui/badge.tsx +28 -0
  59. package/dist/scaffolds/notis-journal/components/ui/button.tsx +53 -0
  60. package/dist/scaffolds/notis-journal/components/ui/card.tsx +56 -0
  61. package/dist/scaffolds/notis-journal/components.json +20 -0
  62. package/dist/scaffolds/notis-journal/index.html +12 -0
  63. package/dist/scaffolds/notis-journal/lib/utils.ts +6 -0
  64. package/dist/scaffolds/notis-journal/metadata/screenshot-1.png +0 -0
  65. package/dist/scaffolds/notis-journal/metadata/screenshot-2.png +0 -0
  66. package/dist/scaffolds/notis-journal/metadata/screenshot-3.png +0 -0
  67. package/dist/scaffolds/notis-journal/metadata/screenshot-4.png +0 -0
  68. package/dist/scaffolds/notis-journal/metadata/screenshot-5.png +0 -0
  69. package/dist/scaffolds/notis-journal/metadata/screenshot-6.png +0 -0
  70. package/dist/scaffolds/notis-journal/metadata/screenshot-fixtures.json +132 -0
  71. package/dist/scaffolds/notis-journal/notis.config.ts +93 -0
  72. package/dist/scaffolds/notis-journal/package.json +34 -0
  73. package/dist/scaffolds/notis-journal/packages/sdk/package.json +36 -0
  74. package/dist/scaffolds/notis-journal/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  75. package/dist/scaffolds/notis-journal/packages/sdk/src/components/Markdown.tsx +60 -0
  76. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
  77. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
  78. package/dist/scaffolds/notis-journal/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
  79. package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +145 -0
  80. package/dist/scaffolds/notis-journal/packages/sdk/src/documents.ts +229 -0
  81. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useBackend.ts +41 -0
  82. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  83. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocument.ts +78 -0
  84. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useDocuments.ts +121 -0
  85. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
  86. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotis.ts +34 -0
  87. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
  88. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTool.ts +64 -0
  89. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTools.ts +56 -0
  90. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
  91. package/dist/scaffolds/notis-journal/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
  92. package/dist/scaffolds/notis-journal/packages/sdk/src/index.ts +83 -0
  93. package/dist/scaffolds/notis-journal/packages/sdk/src/provider.tsx +43 -0
  94. package/dist/scaffolds/notis-journal/packages/sdk/src/runtime.ts +220 -0
  95. package/dist/scaffolds/notis-journal/packages/sdk/src/styles.css +186 -0
  96. package/dist/scaffolds/notis-journal/packages/sdk/src/ui.ts +15 -0
  97. package/dist/scaffolds/notis-journal/packages/sdk/src/vite.ts +56 -0
  98. package/dist/scaffolds/notis-journal/packages/sdk/tsconfig.json +15 -0
  99. package/dist/scaffolds/notis-journal/postcss.config.mjs +8 -0
  100. package/dist/scaffolds/notis-journal/skills/journal-onboarding/SKILL.md +120 -0
  101. package/dist/scaffolds/notis-journal/src/dev-main.tsx +58 -0
  102. package/dist/scaffolds/notis-journal/src/mock-runtime.ts +197 -0
  103. package/dist/scaffolds/notis-journal/tailwind.config.ts +58 -0
  104. package/dist/scaffolds/notis-journal/tsconfig.json +23 -0
  105. package/dist/scaffolds/notis-journal/vite.config.ts +10 -0
  106. package/dist/scaffolds/notis-notes/CHANGELOG.md +5 -0
  107. package/dist/scaffolds/notis-notes/app/page.tsx +17 -373
  108. package/dist/scaffolds/notis-notes/notis.config.ts +0 -1
  109. package/dist/scaffolds/notis-random/CHANGELOG.md +5 -0
  110. package/dist/scaffolds/notis-random/notis.config.ts +0 -1
  111. package/dist/scaffolds/notis-random/src/mock-runtime.ts +1 -0
  112. package/dist/scaffolds.json +22 -0
  113. package/package.json +3 -3
  114. package/skills/notis-apps/SKILL.md +43 -5
  115. package/skills/notis-apps/cli.md +22 -6
  116. package/skills/notis-cli/SKILL.md +20 -2
  117. package/skills/notis-query/cli.md +1 -1
  118. package/src/cli.js +8 -1
  119. package/src/command-specs/apps.js +307 -36
  120. package/src/command-specs/diagnostics.js +674 -0
  121. package/src/command-specs/helpers.js +4 -1
  122. package/src/command-specs/index.js +7 -1
  123. package/src/command-specs/meta.js +8 -2
  124. package/src/command-specs/smoke.js +386 -0
  125. package/src/command-specs/tools.js +237 -44
  126. package/src/runtime/agent-browser.js +204 -21
  127. package/src/runtime/app-changelog.js +79 -0
  128. package/src/runtime/app-dev-server.js +21 -4
  129. package/src/runtime/app-dev-sessions.js +99 -1
  130. package/src/runtime/app-platform.js +197 -18
  131. package/src/runtime/assets/store-screenshot-dark.png +0 -0
  132. package/src/runtime/desktop-auth.js +93 -0
  133. package/src/runtime/output.js +12 -1
  134. package/src/runtime/profiles.js +72 -16
  135. package/src/runtime/store-screenshot.js +138 -0
  136. package/src/runtime/transport.js +24 -82
  137. package/template/.harness/index.html.tmpl +128 -6
  138. package/template/CHANGELOG.md +5 -0
  139. package/template/app/page.tsx +11 -41
  140. package/template/notis.config.ts +0 -1
  141. package/template/package-lock.json +4137 -0
  142. package/template/package.json +1 -0
  143. package/template/packages/sdk/package.json +4 -0
  144. package/template/packages/sdk/src/components/DocumentEditor.tsx +93 -0
  145. package/template/packages/sdk/src/components/Markdown.tsx +60 -0
  146. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +7 -2
  147. package/template/packages/sdk/src/config.ts +74 -0
  148. package/template/packages/sdk/src/documents.ts +229 -0
  149. package/template/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
  150. package/template/packages/sdk/src/hooks/useDocument.ts +78 -0
  151. package/template/packages/sdk/src/hooks/useDocuments.ts +121 -0
  152. package/template/packages/sdk/src/hooks/useMultiSelect.ts +38 -2
  153. package/template/packages/sdk/src/hooks/useUpsertDocument.ts +54 -9
  154. package/template/packages/sdk/src/index.ts +30 -1
  155. package/template/packages/sdk/src/runtime.ts +76 -17
  156. package/template/packages/sdk/src/styles.css +148 -0
  157. /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
+ };
@@ -0,0 +1,5 @@
1
+ # Notes Changelog
2
+
3
+ ## [Initial Release] - {PR_MERGE_DATE}
4
+
5
+ - First Store release.
@@ -1,17 +1,27 @@
1
1
  'use client';
2
2
 
3
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
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
- useTool,
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
- if (isPresentString(document.plainText)) {
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 upsertNote = useTool<UpsertNoteArgs, UpsertNoteResult>('LOCAL_NOTIS_DATABASE_UPSERT_NOTES');
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 = useNoteSchema(NOTE_DATABASE_SLUG);
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 = useQueryDocuments(NOTE_DATABASE_SLUG, { filter: notesFilter, pageSize: 250 });
1115
- const foldersQuery = useQueryDocuments(FOLDER_DATABASE_SLUG, { pageSize: 500 });
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
  {
@@ -0,0 +1,5 @@
1
+ # Random Number Generator Changelog
2
+
3
+ ## [Initial Release] - {PR_MERGE_DATE}
4
+
5
+ - First Store release.
@@ -8,7 +8,6 @@ export default defineNotisApp({
8
8
  author: { name: 'Notis' },
9
9
  categories: ['Personal'],
10
10
  tagline: 'Roll dice, keep history.',
11
- versionNotes: 'Initial scaffold release.',
12
11
 
13
12
  databases: ['rolls'],
14
13
 
@@ -30,6 +30,7 @@ export function installMockRuntime(initialRoute: 'home' | 'history' = 'home'): N
30
30
  description: 'Dev preview — data is in-memory only.',
31
31
  },
32
32
  route: currentRoute(initialRoute),
33
+ databases: [],
33
34
  context: {},
34
35
 
35
36
  listTools: async () => [
@@ -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.5",
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",