@notis_ai/cli 0.2.6 → 0.2.8
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 +17 -0
- package/dist/scaffolds/notis-database/app/page.tsx +38 -41
- package/dist/scaffolds/notis-database/lib/types.ts +5 -7
- package/dist/scaffolds/notis-database/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-database/metadata/screenshot-fixtures.json +1839 -0
- package/dist/scaffolds/notis-database/notis.config.ts +48 -1
- package/dist/scaffolds/notis-database/package-lock.json +3935 -0
- package/dist/scaffolds/notis-database/package.json +2 -1
- package/dist/scaffolds/notis-database/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/config.ts +196 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-database/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-database/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-database/src/mock-runtime.ts +4 -1
- package/dist/scaffolds/notis-journal/CHANGELOG.md +4 -0
- package/dist/scaffolds/notis-journal/app/page.tsx +1 -0
- package/dist/scaffolds/notis-journal/package-lock.json +4615 -0
- package/dist/scaffolds/notis-journal/package.json +2 -2
- package/dist/scaffolds/notis-journal/packages/sdk/src/config.ts +52 -1
- package/dist/scaffolds/notis-notes/app/page.tsx +39 -46
- package/dist/scaffolds/notis-notes/app/phosphor-icons.ts +596 -0
- package/dist/scaffolds/notis-notes/lib/visible-properties.ts +144 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-notes/metadata/screenshot-fixtures.json +752 -0
- package/dist/scaffolds/notis-notes/notis.config.ts +48 -3
- package/dist/scaffolds/notis-notes/package-lock.json +4636 -0
- package/dist/scaffolds/notis-notes/package.json +7 -3
- package/dist/scaffolds/notis-notes/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/config.ts +196 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-notes/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-notes/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-random/CHANGELOG.md +12 -2
- package/dist/scaffolds/notis-random/app/history/page.tsx +5 -4
- package/dist/scaffolds/notis-random/app/page.tsx +141 -74
- package/dist/scaffolds/notis-random/components/ui/button.tsx +50 -0
- package/dist/scaffolds/notis-random/components/ui/card.tsx +16 -0
- package/dist/scaffolds/notis-random/components/ui/input.tsx +23 -0
- package/dist/scaffolds/notis-random/components.json +20 -0
- package/dist/scaffolds/notis-random/lib/notis-tools.ts +26 -7
- package/dist/scaffolds/notis-random/lib/rng.ts +179 -19
- package/dist/scaffolds/notis-random/lib/roll-record.ts +103 -16
- package/dist/scaffolds/notis-random/metadata/screenshot-1.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-2.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-3.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-4.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-5.png +0 -0
- package/dist/scaffolds/notis-random/metadata/screenshot-fixtures.json +753 -0
- package/dist/scaffolds/notis-random/notis.config.ts +47 -3
- package/dist/scaffolds/notis-random/package-lock.json +4513 -0
- package/dist/scaffolds/notis-random/package.json +6 -2
- package/dist/scaffolds/notis-random/packages/sdk/package.json +36 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/components/DocumentEditor.tsx +93 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/components/Markdown.tsx +60 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectActionBar.tsx +278 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectCheckbox.tsx +91 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/components/MultiSelectDragOverlay.tsx +39 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/config.ts +196 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/documents.ts +229 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useBackend.ts +41 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDatabaseSchema.ts +85 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocument.ts +78 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useDocuments.ts +121 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useMultiSelect.ts +539 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotis.ts +34 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useNotisNavigation.ts +49 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTool.ts +64 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTools.ts +56 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useTopBarSearch.ts +73 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/hooks/useUpsertDocument.ts +95 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/index.ts +83 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/provider.tsx +43 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/runtime.ts +220 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/styles.css +186 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/ui.ts +15 -0
- package/dist/scaffolds/notis-random/packages/sdk/src/vite.ts +56 -0
- package/dist/scaffolds/notis-random/packages/sdk/tsconfig.json +15 -0
- package/dist/scaffolds/notis-random/tailwind.config.ts +8 -1
- package/dist/scaffolds.json +8 -6
- package/package.json +2 -1
- package/skills/notis-apps/SKILL.md +449 -144
- package/skills/notis-apps/cli.md +15 -0
- package/skills/notis-cli/SKILL.md +8 -4
- package/skills/notis-onboarding/BRIEF.md +93 -0
- package/src/cli.js +52 -7
- package/src/command-specs/apps.js +154 -21
- package/src/command-specs/diagnostics.js +674 -0
- package/src/command-specs/helpers.js +4 -1
- package/src/command-specs/index.js +8 -0
- package/src/command-specs/onboarding.js +216 -0
- package/src/command-specs/smoke.js +386 -0
- package/src/command-specs/tools.js +189 -5
- package/src/runtime/app-platform.js +65 -4
- package/src/runtime/desktop-auth.js +66 -17
- package/src/runtime/errors.js +1 -0
- package/src/runtime/output.js +21 -3
- package/src/runtime/profiles.js +214 -18
- package/src/runtime/telemetry.js +92 -0
- package/src/runtime/transport.js +29 -1
- package/template/.harness/index.html.tmpl +30 -2
- package/template/packages/sdk/src/config.ts +38 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Notis theme CSS variables.
|
|
3
|
+
*
|
|
4
|
+
* Import this in your app layout:
|
|
5
|
+
*
|
|
6
|
+
* import '@notis/sdk/styles.css';
|
|
7
|
+
*
|
|
8
|
+
* The portal injects the live Notis theme variables at render time.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
@tailwind base;
|
|
12
|
+
@tailwind components;
|
|
13
|
+
@tailwind utilities;
|
|
14
|
+
|
|
15
|
+
@layer base {
|
|
16
|
+
* {
|
|
17
|
+
@apply border-border;
|
|
18
|
+
}
|
|
19
|
+
[data-notis-app-root] {
|
|
20
|
+
color: hsl(var(--foreground));
|
|
21
|
+
display: block;
|
|
22
|
+
min-height: 100%;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@layer components {
|
|
27
|
+
.notis-app-shell {
|
|
28
|
+
@apply mx-auto w-full max-w-[var(--portal-page-max-width)] px-4 py-6 md:px-4 md:py-8;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.notis-app-surface {
|
|
32
|
+
@apply rounded-2xl border border-border bg-card text-card-foreground shadow-sm;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.notis-app-section {
|
|
36
|
+
@apply p-5 md:p-6;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* Markdown value-type rendering (see the `Markdown` component). Plain CSS on
|
|
42
|
+
* theme variables so it works inside the portal shadow root and the dev
|
|
43
|
+
* harness without depending on the app's Tailwind content scan.
|
|
44
|
+
*/
|
|
45
|
+
.notis-markdown {
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
line-height: 1.65;
|
|
48
|
+
color: hsl(var(--foreground));
|
|
49
|
+
overflow-wrap: break-word;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.notis-markdown--sm {
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
line-height: 1.55;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.notis-markdown > :first-child {
|
|
58
|
+
margin-top: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.notis-markdown > :last-child {
|
|
62
|
+
margin-bottom: 0;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.notis-markdown p {
|
|
66
|
+
margin: 0.625em 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.notis-markdown h1,
|
|
70
|
+
.notis-markdown h2,
|
|
71
|
+
.notis-markdown h3,
|
|
72
|
+
.notis-markdown h4,
|
|
73
|
+
.notis-markdown h5,
|
|
74
|
+
.notis-markdown h6 {
|
|
75
|
+
margin: 1.25em 0 0.5em;
|
|
76
|
+
font-weight: 650;
|
|
77
|
+
letter-spacing: -0.01em;
|
|
78
|
+
line-height: 1.3;
|
|
79
|
+
color: hsl(var(--foreground));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.notis-markdown h1 { font-size: 1.5em; }
|
|
83
|
+
.notis-markdown h2 { font-size: 1.25em; }
|
|
84
|
+
.notis-markdown h3 { font-size: 1.1em; }
|
|
85
|
+
.notis-markdown h4,
|
|
86
|
+
.notis-markdown h5,
|
|
87
|
+
.notis-markdown h6 { font-size: 1em; }
|
|
88
|
+
|
|
89
|
+
.notis-markdown ul,
|
|
90
|
+
.notis-markdown ol {
|
|
91
|
+
margin: 0.625em 0;
|
|
92
|
+
padding-left: 1.5em;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.notis-markdown ul { list-style-type: disc; }
|
|
96
|
+
.notis-markdown ol { list-style-type: decimal; }
|
|
97
|
+
|
|
98
|
+
.notis-markdown li {
|
|
99
|
+
margin: 0.25em 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.notis-markdown li > ul,
|
|
103
|
+
.notis-markdown li > ol {
|
|
104
|
+
margin: 0.25em 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.notis-markdown ul.contains-task-list {
|
|
108
|
+
list-style: none;
|
|
109
|
+
padding-left: 0.375em;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.notis-markdown li.task-list-item input[type='checkbox'] {
|
|
113
|
+
margin-right: 0.5em;
|
|
114
|
+
accent-color: hsl(var(--primary));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.notis-markdown blockquote {
|
|
118
|
+
margin: 0.75em 0;
|
|
119
|
+
padding: 0.125em 0 0.125em 0.875em;
|
|
120
|
+
border-left: 3px solid hsl(var(--border));
|
|
121
|
+
color: hsl(var(--muted-foreground));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.notis-markdown a {
|
|
125
|
+
color: hsl(var(--primary));
|
|
126
|
+
text-decoration: underline;
|
|
127
|
+
text-decoration-color: hsl(var(--primary) / 0.35);
|
|
128
|
+
text-underline-offset: 2px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.notis-markdown a:hover {
|
|
132
|
+
text-decoration-color: hsl(var(--primary));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.notis-markdown code {
|
|
136
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
137
|
+
font-size: 0.875em;
|
|
138
|
+
padding: 0.125em 0.375em;
|
|
139
|
+
border-radius: 0.25rem;
|
|
140
|
+
background: hsl(var(--muted));
|
|
141
|
+
color: hsl(var(--foreground));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.notis-markdown pre {
|
|
145
|
+
margin: 0.75em 0;
|
|
146
|
+
padding: 0.75em 1em;
|
|
147
|
+
border-radius: 0.5rem;
|
|
148
|
+
background: hsl(var(--muted));
|
|
149
|
+
overflow-x: auto;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.notis-markdown pre code {
|
|
153
|
+
padding: 0;
|
|
154
|
+
background: transparent;
|
|
155
|
+
font-size: 0.875em;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.notis-markdown hr {
|
|
159
|
+
margin: 1.25em 0;
|
|
160
|
+
border: 0;
|
|
161
|
+
border-top: 1px solid hsl(var(--border));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.notis-markdown img {
|
|
165
|
+
max-width: 100%;
|
|
166
|
+
border-radius: 0.5rem;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.notis-markdown table {
|
|
170
|
+
margin: 0.75em 0;
|
|
171
|
+
width: 100%;
|
|
172
|
+
border-collapse: collapse;
|
|
173
|
+
font-size: 0.95em;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.notis-markdown th,
|
|
177
|
+
.notis-markdown td {
|
|
178
|
+
padding: 0.375em 0.75em;
|
|
179
|
+
border: 1px solid hsl(var(--border));
|
|
180
|
+
text-align: left;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.notis-markdown th {
|
|
184
|
+
background: hsl(var(--muted));
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @notis/sdk/ui - Re-exported shadcn components for Notis apps.
|
|
3
|
+
*
|
|
4
|
+
* Apps include shadcn via their own `components.json` and `@/components/ui/*`
|
|
5
|
+
* imports. This entrypoint is provided as a convenience for apps that want to
|
|
6
|
+
* import directly from the SDK without setting up shadcn locally.
|
|
7
|
+
*
|
|
8
|
+
* For now, this is a placeholder. The scaffold template sets up shadcn
|
|
9
|
+
* directly in the app project, so components come from `@/components/ui/*`.
|
|
10
|
+
* This file can be expanded later to re-export a curated set of components
|
|
11
|
+
* pre-themed for Notis.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Placeholder -- apps use shadcn directly via their project's components/ui/
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite config builder for Notis apps.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* ```ts
|
|
6
|
+
* // vite.config.ts
|
|
7
|
+
* import { notisViteConfig } from '@notis/sdk/vite';
|
|
8
|
+
* import appConfig from './notis.config';
|
|
9
|
+
* export default notisViteConfig(appConfig);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* Produces a library-mode ES module bundle with React externalized.
|
|
13
|
+
* Output: .notis/output/bundle/app.js + app.css
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { NotisAppConfig } from './config';
|
|
17
|
+
|
|
18
|
+
export function notisViteConfig(appConfig: NotisAppConfig) {
|
|
19
|
+
return {
|
|
20
|
+
plugins: [
|
|
21
|
+
// @vitejs/plugin-react is added by the consumer's vite.config.ts
|
|
22
|
+
// or auto-detected. We provide the config shape only.
|
|
23
|
+
],
|
|
24
|
+
build: {
|
|
25
|
+
lib: {
|
|
26
|
+
entry: '.notis/_entry.tsx',
|
|
27
|
+
formats: ['es'] as const,
|
|
28
|
+
fileName: () => 'app.js',
|
|
29
|
+
},
|
|
30
|
+
rollupOptions: {
|
|
31
|
+
external: ['react', 'react-dom', 'react-dom/client', 'react/jsx-runtime', 'react/jsx-dev-runtime'],
|
|
32
|
+
output: {
|
|
33
|
+
globals: {
|
|
34
|
+
react: 'window.React',
|
|
35
|
+
'react-dom': 'window.ReactDOM',
|
|
36
|
+
'react-dom/client': 'window.ReactDOMClient',
|
|
37
|
+
'react/jsx-runtime': 'window.React',
|
|
38
|
+
},
|
|
39
|
+
assetFileNames: 'app[extname]',
|
|
40
|
+
inlineDynamicImports: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
outDir: '.notis/output/bundle',
|
|
44
|
+
emptyOutDir: true,
|
|
45
|
+
cssCodeSplit: false,
|
|
46
|
+
},
|
|
47
|
+
resolve: {
|
|
48
|
+
alias: {
|
|
49
|
+
'@': process.cwd(),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
define: {
|
|
53
|
+
'process.env.NODE_ENV': JSON.stringify('production'),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"rootDir": "src"
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Random Number Generator Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [Reliable Range Editing] - 2026-07-29
|
|
4
4
|
|
|
5
|
-
-
|
|
5
|
+
- Keeps empty and negative range drafts intact until they form a valid number.
|
|
6
|
+
|
|
7
|
+
## [Generator modes and history] - 2026-07-29
|
|
8
|
+
|
|
9
|
+
- Added integer, decimal, and d4–d20 dice modes with live bounds validation.
|
|
10
|
+
- Added a complete roll history with reliable saving and in-app navigation.
|
|
11
|
+
- Refreshed the generator with native Notis controls and light and dark Store media.
|
|
12
|
+
|
|
13
|
+
## [Initial Release] - 2026-04-23
|
|
14
|
+
|
|
15
|
+
- Generated random numbers with configurable bounds and saved recent rolls.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import { DiceFiveIcon as Dices, ClockCounterClockwiseIcon as History, HashIcon as Hash, PercentIcon as Percent } from '@phosphor-icons/react';
|
|
5
|
+
import { Card } from '@/components/ui/card';
|
|
5
6
|
import { formatNumber, relativeTime } from '@/lib/utils';
|
|
6
7
|
import type { Mode } from '@/lib/rng';
|
|
7
8
|
import { normalizeRollRecord, sortRollRecordsDesc } from '@/lib/roll-record';
|
|
@@ -14,7 +15,7 @@ const MODE_ICONS: Record<Mode, typeof Hash> = {
|
|
|
14
15
|
};
|
|
15
16
|
|
|
16
17
|
export default function HistoryPage() {
|
|
17
|
-
const { documents, loading } = useRollDocuments();
|
|
18
|
+
const { documents, loading } = useRollDocuments(100, true);
|
|
18
19
|
|
|
19
20
|
const rows = useMemo(
|
|
20
21
|
() => documents
|
|
@@ -24,7 +25,7 @@ export default function HistoryPage() {
|
|
|
24
25
|
);
|
|
25
26
|
|
|
26
27
|
return (
|
|
27
|
-
<main className="notis-random-shell space-y-6">
|
|
28
|
+
<main data-store-screenshot="history" className="notis-random-shell space-y-6">
|
|
28
29
|
<header className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
29
30
|
<History className="h-4 w-4" strokeWidth={1.5} />
|
|
30
31
|
<span>Roll history</span>
|
|
@@ -40,7 +41,7 @@ export default function HistoryPage() {
|
|
|
40
41
|
Nothing rolled yet. Head back to the generator and press Generate.
|
|
41
42
|
</div>
|
|
42
43
|
) : (
|
|
43
|
-
<
|
|
44
|
+
<Card className="divide-y divide-border">
|
|
44
45
|
{rows.map((row) => {
|
|
45
46
|
const Icon = MODE_ICONS[row.mode] ?? Hash;
|
|
46
47
|
return (
|
|
@@ -59,7 +60,7 @@ export default function HistoryPage() {
|
|
|
59
60
|
</div>
|
|
60
61
|
);
|
|
61
62
|
})}
|
|
62
|
-
</
|
|
63
|
+
</Card>
|
|
63
64
|
)}
|
|
64
65
|
</main>
|
|
65
66
|
);
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useCallback,
|
|
4
|
-
import { useNotis } from '@notis/sdk';
|
|
5
|
-
import { DiceFiveIcon as Dices, SparkleIcon as Sparkles, ClockCounterClockwiseIcon as History, ArrowRightIcon as ArrowRight, HashIcon as Hash, PercentIcon as Percent, InfinityIcon, ArrowClockwiseIcon as RefreshCw } from '@phosphor-icons/react';
|
|
3
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
4
|
+
import { useNotis, useNotisNavigation } from '@notis/sdk';
|
|
5
|
+
import { DiceFiveIcon as Dices, SparkleIcon as Sparkles, ClockCounterClockwiseIcon as History, ArrowRightIcon as ArrowRight, HashIcon as Hash, PercentIcon as Percent, InfinityIcon, ArrowClockwiseIcon as RefreshCw, WarningCircleIcon as WarningCircle } from '@phosphor-icons/react';
|
|
6
|
+
import { Button } from '@/components/ui/button';
|
|
7
|
+
import { Card } from '@/components/ui/card';
|
|
8
|
+
import { Input } from '@/components/ui/input';
|
|
6
9
|
import { cn, formatNumber, relativeTime } from '@/lib/utils';
|
|
7
|
-
import { type Mode, type Roll, roll } from '@/lib/rng';
|
|
10
|
+
import { DICE_SIDES, DEFAULT_DICE_SIDES, type DiceSides, type Mode, type Roll, checkBounds, parseBoundDraft, roll } from '@/lib/rng';
|
|
8
11
|
import { normalizeRollRecord, sortRollRecordsDesc } from '@/lib/roll-record';
|
|
9
12
|
import { usePersistRoll, useRollDocuments } from '@/lib/notis-tools';
|
|
10
13
|
|
|
11
14
|
const MODES: Array<{ id: Mode; label: string; icon: typeof Hash; hint: string }> = [
|
|
12
15
|
{ id: 'integer', label: 'Integer', icon: Hash, hint: 'Whole numbers only' },
|
|
13
16
|
{ id: 'decimal', label: 'Decimal', icon: Percent, hint: 'Continuous range' },
|
|
14
|
-
{ id: 'dice', label: 'Dice', icon: Dices, hint: '
|
|
17
|
+
{ id: 'dice', label: 'Dice', icon: Dices, hint: 'Standard polyhedral dice' },
|
|
15
18
|
];
|
|
16
19
|
|
|
17
20
|
export default function HomePage() {
|
|
18
21
|
const { app, ready } = useNotis();
|
|
19
|
-
const
|
|
22
|
+
const nav = useNotisNavigation();
|
|
23
|
+
const { documents, loading, refetch } = useRollDocuments(5);
|
|
20
24
|
const { persist: persistRoll } = usePersistRoll();
|
|
21
25
|
|
|
22
26
|
const [mode, setMode] = useState<Mode>('integer');
|
|
23
|
-
const [
|
|
24
|
-
const [
|
|
27
|
+
const [minDraft, setMinDraft] = useState('1');
|
|
28
|
+
const [maxDraft, setMaxDraft] = useState('100');
|
|
29
|
+
const [sides, setSides] = useState<DiceSides>(DEFAULT_DICE_SIDES);
|
|
25
30
|
const [current, setCurrent] = useState<Roll | null>(null);
|
|
26
31
|
const [rolling, setRolling] = useState(false);
|
|
32
|
+
const [saveError, setSaveError] = useState<string | null>(null);
|
|
27
33
|
|
|
28
34
|
const recent = useMemo(
|
|
29
35
|
() => documents
|
|
@@ -33,32 +39,43 @@ export default function HomePage() {
|
|
|
33
39
|
[documents],
|
|
34
40
|
);
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
// Dice mode draws from 1..sides; the manual range inputs drive the other modes.
|
|
43
|
+
const bounds = useMemo(
|
|
44
|
+
() => (
|
|
45
|
+
mode === 'dice'
|
|
46
|
+
? { min: 1, max: sides }
|
|
47
|
+
: {
|
|
48
|
+
min: parseBoundDraft(minDraft),
|
|
49
|
+
max: parseBoundDraft(maxDraft),
|
|
50
|
+
}
|
|
51
|
+
),
|
|
52
|
+
[mode, sides, minDraft, maxDraft],
|
|
53
|
+
);
|
|
54
|
+
const check = useMemo(
|
|
55
|
+
() => checkBounds(bounds.min, bounds.max, mode),
|
|
56
|
+
[bounds.min, bounds.max, mode],
|
|
57
|
+
);
|
|
40
58
|
|
|
41
59
|
const generate = useCallback(async () => {
|
|
42
|
-
if (rolling) return;
|
|
60
|
+
if (rolling || !check.ok) return;
|
|
43
61
|
setRolling(true);
|
|
44
|
-
|
|
45
|
-
setCurrent(next);
|
|
62
|
+
setSaveError(null);
|
|
46
63
|
try {
|
|
47
|
-
|
|
64
|
+
// roll() throws on unusable bounds, and persist() can fail on the network.
|
|
65
|
+
// Both have to leave `rolling` false or the button would stay stuck.
|
|
66
|
+
const next = roll(bounds.min, bounds.max, mode);
|
|
67
|
+
setCurrent(next);
|
|
68
|
+
await persistRoll(next);
|
|
69
|
+
refetch();
|
|
70
|
+
} catch (err) {
|
|
71
|
+
setSaveError(err instanceof Error ? err.message : 'Could not save this roll.');
|
|
48
72
|
} finally {
|
|
49
73
|
setRolling(false);
|
|
50
74
|
}
|
|
51
|
-
}, [rolling, min, max, mode,
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (mode === 'dice') {
|
|
55
|
-
setMin(1);
|
|
56
|
-
setMax(6);
|
|
57
|
-
}
|
|
58
|
-
}, [mode]);
|
|
75
|
+
}, [rolling, check.ok, bounds.min, bounds.max, mode, persistRoll, refetch]);
|
|
59
76
|
|
|
60
77
|
return (
|
|
61
|
-
<main className="notis-random-shell space-y-
|
|
78
|
+
<main data-store-screenshot="generator" className="notis-random-shell space-y-6">
|
|
62
79
|
<header className="flex items-start justify-between gap-6">
|
|
63
80
|
<div className="space-y-1.5">
|
|
64
81
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
@@ -70,22 +87,26 @@ export default function HomePage() {
|
|
|
70
87
|
Pick a mode, set the range, and press generate. Every roll is saved to the History view.
|
|
71
88
|
</p>
|
|
72
89
|
</div>
|
|
73
|
-
<
|
|
90
|
+
<Button
|
|
74
91
|
type="button"
|
|
92
|
+
variant="outline"
|
|
93
|
+
size="sm"
|
|
75
94
|
onClick={() => refetch()}
|
|
76
|
-
className="
|
|
95
|
+
className="gap-2 border-border text-muted-foreground hover:text-foreground"
|
|
77
96
|
>
|
|
78
97
|
<RefreshCw className={cn('h-3.5 w-3.5', loading && 'animate-spin')} />
|
|
79
98
|
Refresh
|
|
80
|
-
</
|
|
99
|
+
</Button>
|
|
81
100
|
</header>
|
|
82
101
|
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
{/* The dial and the controls sit side by side so the whole generator —
|
|
103
|
+
result, mode, range, and recent rolls — stays above the fold. */}
|
|
104
|
+
<section className="grid gap-6 lg:grid-cols-[1.05fr_1fr]">
|
|
105
|
+
<Card className="flex flex-col items-center justify-center gap-5 rounded-2xl p-6">
|
|
85
106
|
<div
|
|
86
107
|
className={cn(
|
|
87
108
|
'flex items-center justify-center rounded-full border border-border bg-background',
|
|
88
|
-
'h-
|
|
109
|
+
'h-40 w-40 transition-transform',
|
|
89
110
|
rolling && 'animate-pulse',
|
|
90
111
|
)}
|
|
91
112
|
>
|
|
@@ -97,38 +118,49 @@ export default function HomePage() {
|
|
|
97
118
|
<InfinityIcon className="h-10 w-10 text-muted-foreground" strokeWidth={1.2} />
|
|
98
119
|
)}
|
|
99
120
|
</div>
|
|
100
|
-
<
|
|
121
|
+
<Button
|
|
101
122
|
type="button"
|
|
123
|
+
size="lg"
|
|
124
|
+
data-action="generate"
|
|
102
125
|
onClick={generate}
|
|
103
|
-
disabled={rolling}
|
|
104
|
-
className=
|
|
105
|
-
'inline-flex h-11 items-center gap-2 rounded-full px-6 text-sm font-medium',
|
|
106
|
-
'bg-primary text-primary-foreground hover:opacity-90 disabled:opacity-50 transition',
|
|
107
|
-
)}
|
|
126
|
+
disabled={rolling || !check.ok}
|
|
127
|
+
className="gap-2 rounded-full px-6"
|
|
108
128
|
>
|
|
109
129
|
<Dices className="h-4 w-4" />
|
|
110
130
|
{rolling ? 'Rolling…' : current ? 'Roll again' : 'Generate'}
|
|
111
|
-
</
|
|
112
|
-
{
|
|
131
|
+
</Button>
|
|
132
|
+
{check.issue ? (
|
|
133
|
+
<p className="flex items-center gap-1.5 text-xs text-destructive">
|
|
134
|
+
<WarningCircle className="h-3.5 w-3.5 shrink-0" />
|
|
135
|
+
{check.issue.message}
|
|
136
|
+
</p>
|
|
137
|
+
) : current ? (
|
|
113
138
|
<p className="text-xs text-muted-foreground">
|
|
114
139
|
<span className="font-mono">[{formatNumber(current.min)}, {formatNumber(current.max)}]</span>
|
|
115
140
|
{' · '}{current.mode}
|
|
116
141
|
</p>
|
|
142
|
+
) : null}
|
|
143
|
+
{saveError && (
|
|
144
|
+
<p className="flex items-center gap-1.5 text-xs text-destructive">
|
|
145
|
+
<WarningCircle className="h-3.5 w-3.5 shrink-0" />
|
|
146
|
+
{saveError}
|
|
147
|
+
</p>
|
|
117
148
|
)}
|
|
118
|
-
</
|
|
119
|
-
</section>
|
|
149
|
+
</Card>
|
|
120
150
|
|
|
121
|
-
|
|
122
|
-
<
|
|
151
|
+
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-1">
|
|
152
|
+
<Card className="p-5">
|
|
123
153
|
<h2 className="mb-3 text-sm font-medium text-foreground">Mode</h2>
|
|
124
154
|
<div className="grid gap-2">
|
|
125
155
|
{MODES.map(({ id, label, icon: Icon, hint }) => (
|
|
126
|
-
<
|
|
156
|
+
<Button
|
|
127
157
|
key={id}
|
|
128
158
|
type="button"
|
|
159
|
+
variant="outline"
|
|
160
|
+
data-mode={id}
|
|
129
161
|
onClick={() => setMode(id)}
|
|
130
162
|
className={cn(
|
|
131
|
-
'
|
|
163
|
+
'h-auto w-full justify-start gap-3 px-3 py-2.5 text-left whitespace-normal',
|
|
132
164
|
mode === id
|
|
133
165
|
? 'border-foreground bg-accent text-accent-foreground'
|
|
134
166
|
: 'border-border hover:bg-accent/50',
|
|
@@ -139,35 +171,72 @@ export default function HomePage() {
|
|
|
139
171
|
<div className="text-sm font-medium">{label}</div>
|
|
140
172
|
<div className="text-xs text-muted-foreground">{hint}</div>
|
|
141
173
|
</div>
|
|
142
|
-
</
|
|
174
|
+
</Button>
|
|
143
175
|
))}
|
|
144
176
|
</div>
|
|
145
|
-
</
|
|
177
|
+
</Card>
|
|
146
178
|
|
|
147
|
-
<
|
|
148
|
-
<h2 className="text-sm font-medium text-foreground">
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
179
|
+
<Card className="space-y-4 p-5">
|
|
180
|
+
<h2 className="text-sm font-medium text-foreground">
|
|
181
|
+
{mode === 'dice' ? 'Die' : 'Range'}
|
|
182
|
+
</h2>
|
|
183
|
+
{mode === 'dice' ? (
|
|
184
|
+
<>
|
|
185
|
+
<div className="flex flex-wrap gap-2">
|
|
186
|
+
{DICE_SIDES.map((n) => (
|
|
187
|
+
<Button
|
|
188
|
+
key={n}
|
|
189
|
+
type="button"
|
|
190
|
+
variant="outline"
|
|
191
|
+
data-die={n}
|
|
192
|
+
onClick={() => setSides(n)}
|
|
193
|
+
className={cn(
|
|
194
|
+
'h-10 min-w-[3.25rem] px-3 font-mono',
|
|
195
|
+
sides === n
|
|
196
|
+
? 'border-foreground bg-accent text-accent-foreground'
|
|
197
|
+
: 'border-border hover:bg-accent/50',
|
|
198
|
+
)}
|
|
199
|
+
>
|
|
200
|
+
d{n}
|
|
201
|
+
</Button>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
204
|
+
<p className="text-xs text-muted-foreground">
|
|
205
|
+
Rolls a whole number from 1 to {sides}, each equally likely.
|
|
206
|
+
</p>
|
|
207
|
+
</>
|
|
208
|
+
) : (
|
|
209
|
+
<>
|
|
210
|
+
<div className="grid grid-cols-2 gap-3">
|
|
211
|
+
<RangeInput label="Minimum" name="min" value={minDraft} onChange={setMinDraft} />
|
|
212
|
+
<RangeInput label="Maximum" name="max" value={maxDraft} onChange={setMaxDraft} />
|
|
213
|
+
</div>
|
|
214
|
+
<p className="text-xs text-muted-foreground">
|
|
215
|
+
{mode === 'decimal'
|
|
216
|
+
? 'Decimal draws include the minimum but never reach the maximum.'
|
|
217
|
+
: 'Integer draws include both ends of the range.'}
|
|
218
|
+
</p>
|
|
219
|
+
</>
|
|
155
220
|
)}
|
|
221
|
+
</Card>
|
|
156
222
|
</div>
|
|
157
223
|
</section>
|
|
158
224
|
|
|
159
|
-
<
|
|
225
|
+
<Card className="p-5">
|
|
160
226
|
<header className="mb-4 flex items-center justify-between gap-3">
|
|
161
227
|
<h2 className="flex items-center gap-2 text-sm font-medium text-foreground">
|
|
162
228
|
<History className="h-4 w-4 text-muted-foreground" strokeWidth={1.5} />
|
|
163
229
|
Recent rolls
|
|
164
230
|
</h2>
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
231
|
+
<Button
|
|
232
|
+
type="button"
|
|
233
|
+
variant="ghost"
|
|
234
|
+
size="sm"
|
|
235
|
+
onClick={() => nav.toRoute('/history')}
|
|
236
|
+
className="h-auto gap-1 px-2 py-1 text-xs text-muted-foreground hover:text-foreground"
|
|
168
237
|
>
|
|
169
238
|
View all <ArrowRight className="h-3 w-3" />
|
|
170
|
-
</
|
|
239
|
+
</Button>
|
|
171
240
|
</header>
|
|
172
241
|
{loading && recent.length === 0 ? (
|
|
173
242
|
<p className="text-sm text-muted-foreground">Loading…</p>
|
|
@@ -188,34 +257,32 @@ export default function HomePage() {
|
|
|
188
257
|
))}
|
|
189
258
|
</ul>
|
|
190
259
|
)}
|
|
191
|
-
</
|
|
260
|
+
</Card>
|
|
192
261
|
</main>
|
|
193
262
|
);
|
|
194
263
|
}
|
|
195
264
|
|
|
196
265
|
function RangeInput({
|
|
197
266
|
label,
|
|
267
|
+
name,
|
|
198
268
|
value,
|
|
199
269
|
onChange,
|
|
200
|
-
disabled,
|
|
201
270
|
}: {
|
|
202
271
|
label: string;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
272
|
+
name: string;
|
|
273
|
+
value: string;
|
|
274
|
+
onChange: (v: string) => void;
|
|
206
275
|
}) {
|
|
207
276
|
return (
|
|
208
|
-
<label className=
|
|
277
|
+
<label className="block space-y-1.5">
|
|
209
278
|
<span className="text-xs font-medium text-muted-foreground">{label}</span>
|
|
210
|
-
<
|
|
211
|
-
type="
|
|
279
|
+
<Input
|
|
280
|
+
type="text"
|
|
281
|
+
inputMode="decimal"
|
|
282
|
+
data-bound={name}
|
|
212
283
|
value={value}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const parsed = Number(e.target.value);
|
|
216
|
-
if (Number.isFinite(parsed)) onChange(parsed);
|
|
217
|
-
}}
|
|
218
|
-
className="h-10 w-full rounded-md border border-border bg-background px-3 font-mono text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary/40"
|
|
284
|
+
onChange={(e) => onChange(e.target.value)}
|
|
285
|
+
className="border-border font-mono"
|
|
219
286
|
/>
|
|
220
287
|
</label>
|
|
221
288
|
);
|