@trypattern/sdk 0.0.0-stage → 0.1.1

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 (273) hide show
  1. package/README.md +127 -2
  2. package/dist/access.d.ts +31 -0
  3. package/dist/access.js +72 -0
  4. package/dist/actions.d.ts +57 -0
  5. package/dist/actions.js +198 -0
  6. package/dist/api.d.ts +30 -0
  7. package/dist/api.js +60 -0
  8. package/dist/app.d.ts +115 -0
  9. package/dist/app.js +266 -0
  10. package/dist/appInfo.d.ts +35 -0
  11. package/dist/appInfo.js +67 -0
  12. package/dist/appTitle.d.ts +6 -0
  13. package/dist/appTitle.js +3 -0
  14. package/dist/appearance.d.ts +7 -0
  15. package/dist/appearance.js +2 -0
  16. package/dist/auth/sessionApi.d.ts +41 -0
  17. package/dist/auth/sessionApi.js +158 -0
  18. package/dist/auth/sessionStore.d.ts +20 -0
  19. package/dist/auth/sessionStore.js +40 -0
  20. package/dist/auth/user.d.ts +11 -0
  21. package/dist/auth/user.js +9 -0
  22. package/dist/builtins/Account.d.ts +25 -0
  23. package/dist/builtins/Account.js +15 -0
  24. package/dist/builtins/AppSettings.d.ts +12 -0
  25. package/dist/builtins/AppSettings.js +31 -0
  26. package/dist/builtins/Palette.d.ts +29 -0
  27. package/dist/builtins/Palette.js +63 -0
  28. package/dist/builtins/SignIn.d.ts +28 -0
  29. package/dist/builtins/SignIn.js +60 -0
  30. package/dist/builtins/Toasts.d.ts +32 -0
  31. package/dist/builtins/Toasts.js +25 -0
  32. package/dist/builtins/actions.d.ts +2 -0
  33. package/dist/builtins/actions.js +45 -0
  34. package/dist/cache.d.ts +24 -0
  35. package/dist/cache.js +112 -0
  36. package/dist/cli/app.d.ts +2 -0
  37. package/dist/cli/app.js +1406 -0
  38. package/dist/cli/publishing.d.ts +21 -0
  39. package/dist/cli/publishing.js +493 -0
  40. package/dist/cli/screenshot.d.ts +1 -0
  41. package/dist/cli/screenshot.js +96 -0
  42. package/dist/cli/tools.d.ts +12 -0
  43. package/dist/cli/tools.js +35 -0
  44. package/dist/clock.d.ts +6 -0
  45. package/dist/clock.js +31 -0
  46. package/dist/components/AppFrame.d.ts +14 -0
  47. package/dist/components/AppFrame.js +64 -0
  48. package/dist/components/AppearancePicker.d.ts +1 -0
  49. package/dist/components/AppearancePicker.js +12 -0
  50. package/dist/components/Avatar.d.ts +9 -0
  51. package/dist/components/Avatar.js +63 -0
  52. package/dist/components/Badge.d.ts +11 -0
  53. package/dist/components/Badge.js +87 -0
  54. package/dist/components/Button.d.ts +24 -0
  55. package/dist/components/Button.js +252 -0
  56. package/dist/components/Card.d.ts +12 -0
  57. package/dist/components/Card.js +38 -0
  58. package/dist/components/CommandMenu.d.ts +23 -0
  59. package/dist/components/CommandMenu.js +214 -0
  60. package/dist/components/CommandPalette.d.ts +1 -0
  61. package/dist/components/CommandPalette.js +7 -0
  62. package/dist/components/Content.d.ts +7 -0
  63. package/dist/components/Content.js +24 -0
  64. package/dist/components/ContextMenu.d.ts +8 -0
  65. package/dist/components/ContextMenu.js +23 -0
  66. package/dist/components/CopyButton.d.ts +11 -0
  67. package/dist/components/CopyButton.js +16 -0
  68. package/dist/components/Dialog.d.ts +11 -0
  69. package/dist/components/Dialog.js +81 -0
  70. package/dist/components/Divider.d.ts +7 -0
  71. package/dist/components/Divider.js +30 -0
  72. package/dist/components/EmptyState.d.ts +8 -0
  73. package/dist/components/EmptyState.js +24 -0
  74. package/dist/components/Form.d.ts +9 -0
  75. package/dist/components/Form.js +23 -0
  76. package/dist/components/Icon.d.ts +10 -0
  77. package/dist/components/Icon.js +22 -0
  78. package/dist/components/IconButton.d.ts +14 -0
  79. package/dist/components/IconButton.js +53 -0
  80. package/dist/components/Kbd.d.ts +6 -0
  81. package/dist/components/Kbd.js +67 -0
  82. package/dist/components/Link.d.ts +14 -0
  83. package/dist/components/Link.js +44 -0
  84. package/dist/components/ListRow.d.ts +12 -0
  85. package/dist/components/ListRow.js +62 -0
  86. package/dist/components/LoadingRows.d.ts +5 -0
  87. package/dist/components/LoadingRows.js +6 -0
  88. package/dist/components/Menu.d.ts +12 -0
  89. package/dist/components/Menu.js +33 -0
  90. package/dist/components/MenuItems.d.ts +21 -0
  91. package/dist/components/MenuItems.js +32 -0
  92. package/dist/components/Placement.d.ts +21 -0
  93. package/dist/components/Placement.js +1 -0
  94. package/dist/components/Popup.d.ts +8 -0
  95. package/dist/components/Popup.js +43 -0
  96. package/dist/components/PopupItem.d.ts +8 -0
  97. package/dist/components/PopupItem.js +51 -0
  98. package/dist/components/PropertyRow.d.ts +7 -0
  99. package/dist/components/PropertyRow.js +23 -0
  100. package/dist/components/RadioGroup.d.ts +18 -0
  101. package/dist/components/RadioGroup.js +85 -0
  102. package/dist/components/Row.d.ts +16 -0
  103. package/dist/components/Row.js +27 -0
  104. package/dist/components/Screen.d.ts +13 -0
  105. package/dist/components/Screen.js +56 -0
  106. package/dist/components/Scrim.d.ts +8 -0
  107. package/dist/components/Scrim.js +40 -0
  108. package/dist/components/ScrollArea.d.ts +15 -0
  109. package/dist/components/ScrollArea.js +13 -0
  110. package/dist/components/SegmentedControl.d.ts +17 -0
  111. package/dist/components/SegmentedControl.js +68 -0
  112. package/dist/components/Select.d.ts +17 -0
  113. package/dist/components/Select.js +107 -0
  114. package/dist/components/SettingsRow.d.ts +8 -0
  115. package/dist/components/SettingsRow.js +32 -0
  116. package/dist/components/Sidebar.d.ts +20 -0
  117. package/dist/components/Sidebar.js +137 -0
  118. package/dist/components/SignInScreen.d.ts +1 -0
  119. package/dist/components/SignInScreen.js +40 -0
  120. package/dist/components/Size.d.ts +1 -0
  121. package/dist/components/Size.js +1 -0
  122. package/dist/components/Skeleton.d.ts +8 -0
  123. package/dist/components/Skeleton.js +48 -0
  124. package/dist/components/Spinner.d.ts +6 -0
  125. package/dist/components/Spinner.js +54 -0
  126. package/dist/components/Stack.d.ts +14 -0
  127. package/dist/components/Stack.js +19 -0
  128. package/dist/components/Switch.d.ts +10 -0
  129. package/dist/components/Switch.js +72 -0
  130. package/dist/components/Text.d.ts +21 -0
  131. package/dist/components/Text.js +77 -0
  132. package/dist/components/TextField.d.ts +25 -0
  133. package/dist/components/TextField.js +68 -0
  134. package/dist/components/Toaster.d.ts +1 -0
  135. package/dist/components/Toaster.js +83 -0
  136. package/dist/components/Tooltip.d.ts +8 -0
  137. package/dist/components/Tooltip.js +43 -0
  138. package/dist/components/controlLook.d.ts +70 -0
  139. package/dist/components/controlLook.js +80 -0
  140. package/dist/components/frame.d.ts +4 -0
  141. package/dist/components/frame.js +2 -0
  142. package/dist/components/icons.d.ts +59 -0
  143. package/dist/components/icons.js +111 -0
  144. package/dist/components/interaction.d.ts +12 -0
  145. package/dist/components/interaction.js +22 -0
  146. package/dist/components/spacing.d.ts +118 -0
  147. package/dist/components/spacing.js +46 -0
  148. package/dist/components/tones.d.ts +28 -0
  149. package/dist/components/tones.js +12 -0
  150. package/dist/entityStore.d.ts +46 -0
  151. package/dist/entityStore.js +160 -0
  152. package/dist/flows/accessFlows.d.ts +9 -0
  153. package/dist/flows/accessFlows.js +167 -0
  154. package/dist/flows/browser.d.ts +34 -0
  155. package/dist/flows/browser.js +167 -0
  156. package/dist/flows/contactSheet.d.ts +1 -0
  157. package/dist/flows/contactSheet.js +63 -0
  158. package/dist/flows/flowBrowser.d.ts +25 -0
  159. package/dist/flows/flowBrowser.js +23 -0
  160. package/dist/flows/flowSetup.d.ts +3 -0
  161. package/dist/flows/flowSetup.js +35 -0
  162. package/dist/flows/flows.d.ts +47 -0
  163. package/dist/flows/flows.js +175 -0
  164. package/dist/flows/screenshotName.d.ts +1 -0
  165. package/dist/flows/screenshotName.js +6 -0
  166. package/dist/gate.d.ts +8 -0
  167. package/dist/gate.js +52 -0
  168. package/dist/hooks/appContext.d.ts +15 -0
  169. package/dist/hooks/appContext.js +5 -0
  170. package/dist/hooks/appHooks.d.ts +21 -0
  171. package/dist/hooks/appHooks.js +24 -0
  172. package/dist/hooks/useAnimatesOnChange.d.ts +1 -0
  173. package/dist/hooks/useAnimatesOnChange.js +14 -0
  174. package/dist/hooks/useStoreState.d.ts +6 -0
  175. package/dist/hooks/useStoreState.js +5 -0
  176. package/dist/index.d.ts +44 -0
  177. package/dist/index.js +21 -0
  178. package/dist/memoryApi.d.ts +19 -0
  179. package/dist/memoryApi.js +42 -0
  180. package/dist/model.d.ts +94 -0
  181. package/dist/model.js +243 -0
  182. package/dist/page.d.ts +11 -0
  183. package/dist/page.js +9 -0
  184. package/dist/platform.d.ts +1 -0
  185. package/dist/platform.js +5 -0
  186. package/dist/publishWire.d.ts +271 -0
  187. package/dist/publishWire.js +168 -0
  188. package/dist/react.d.ts +49 -0
  189. package/dist/react.js +40 -0
  190. package/dist/registered.d.ts +60 -0
  191. package/dist/registered.js +19 -0
  192. package/dist/routes.d.ts +38 -0
  193. package/dist/routes.js +21 -0
  194. package/dist/rules/checkAllFiles.d.ts +1 -0
  195. package/dist/rules/checkAllFiles.js +749 -0
  196. package/dist/rules/checkApiRouting.d.ts +1 -0
  197. package/dist/rules/checkApiRouting.js +112 -0
  198. package/dist/rules/lintPlugin.d.ts +252 -0
  199. package/dist/rules/lintPlugin.js +1159 -0
  200. package/dist/rules/messages.d.ts +48 -0
  201. package/dist/rules/messages.js +48 -0
  202. package/dist/rules/themeTokens.d.ts +6 -0
  203. package/dist/rules/themeTokens.js +64 -0
  204. package/dist/schema.d.ts +4 -0
  205. package/dist/schema.js +2 -0
  206. package/dist/server/auth.d.ts +115 -0
  207. package/dist/server/auth.js +134 -0
  208. package/dist/server/authService.d.ts +9 -0
  209. package/dist/server/authService.js +50 -0
  210. package/dist/server/createServer.d.ts +402 -0
  211. package/dist/server/createServer.js +82 -0
  212. package/dist/server/env.d.ts +15 -0
  213. package/dist/server/env.js +1 -0
  214. package/dist/server/index.d.ts +13 -0
  215. package/dist/server/index.js +9 -0
  216. package/dist/server/services.d.ts +9 -0
  217. package/dist/server/services.js +47 -0
  218. package/dist/server/session.d.ts +29 -0
  219. package/dist/server/session.js +65 -0
  220. package/dist/server/sessions.d.ts +92 -0
  221. package/dist/server/sessions.js +47 -0
  222. package/dist/server/settings.d.ts +49 -0
  223. package/dist/server/settings.js +25 -0
  224. package/dist/server/signInWire.d.ts +53 -0
  225. package/dist/server/signInWire.js +23 -0
  226. package/dist/server/tables.d.ts +242 -0
  227. package/dist/server/tables.js +246 -0
  228. package/dist/server/users.d.ts +189 -0
  229. package/dist/server/users.js +49 -0
  230. package/dist/serviceClients.d.ts +5 -0
  231. package/dist/serviceClients.js +70 -0
  232. package/dist/services.d.ts +53 -0
  233. package/dist/services.js +30 -0
  234. package/dist/shortcuts.d.ts +3 -0
  235. package/dist/shortcuts.js +40 -0
  236. package/dist/start.d.ts +20 -0
  237. package/dist/start.js +145 -0
  238. package/dist/state.d.ts +14 -0
  239. package/dist/state.js +17 -0
  240. package/dist/store.d.ts +45 -0
  241. package/dist/store.js +208 -0
  242. package/dist/subscribers.d.ts +5 -0
  243. package/dist/subscribers.js +13 -0
  244. package/dist/table.d.ts +143 -0
  245. package/dist/table.js +91 -0
  246. package/dist/tables.d.ts +77 -0
  247. package/dist/tables.js +216 -0
  248. package/dist/theme/reset.css +84 -0
  249. package/dist/theme/scales.d.ts +76 -0
  250. package/dist/theme/scales.js +82 -0
  251. package/dist/theme/tokens.stylex.d.ts +218 -0
  252. package/dist/theme/tokens.stylex.js +233 -0
  253. package/dist/vite/commandBridge.d.ts +2 -0
  254. package/dist/vite/commandBridge.js +46 -0
  255. package/dist/vite/designManifest.d.ts +25 -0
  256. package/dist/vite/designManifest.js +191 -0
  257. package/dist/vite/localWorker.d.ts +4 -0
  258. package/dist/vite/localWorker.js +60 -0
  259. package/dist/vite/projectFile.d.ts +9 -0
  260. package/dist/vite/projectFile.js +17 -0
  261. package/dist/vite/rootAlias.d.ts +2 -0
  262. package/dist/vite/rootAlias.js +15 -0
  263. package/dist/vite/vite.d.ts +8 -0
  264. package/dist/vite/vite.js +95 -0
  265. package/dist/wire.d.ts +7 -0
  266. package/dist/wire.js +1 -0
  267. package/package.json +103 -3
  268. package/presets/lintPlugin.js +7 -0
  269. package/presets/oxlint.json +167 -0
  270. package/presets/register.d.ts +21 -0
  271. package/presets/server.d.ts +2 -0
  272. package/presets/tsconfig.app.json +29 -0
  273. package/presets/tsconfig.server.json +27 -0
@@ -0,0 +1,158 @@
1
+ import { adminEmails } from "../access.js";
2
+ import { MemoryServer } from "../memoryApi.js";
3
+ import { ownerIdOf } from "../table.js";
4
+ import { USER_COOKIE, userSchema } from "./user.js";
5
+ export const sessionCalls = [
6
+ 'me',
7
+ 'devSignIn',
8
+ 'sendEmailCode',
9
+ 'verifyEmailCode',
10
+ 'signOut'
11
+ ];
12
+ const MEMORY_EMAIL_CODE = '123456';
13
+ const JSON_HEADERS = { 'content-type': 'application/json' };
14
+ export class HttpSessionApi {
15
+ #gate;
16
+ #signedOutListeners = new Set();
17
+ constructor(gate) {
18
+ this.#gate = gate;
19
+ }
20
+ rememberedUser() {
21
+ const prefix = `${USER_COOKIE}=`;
22
+ const cookie = document.cookie
23
+ .split('; ')
24
+ .find((part) => part.startsWith(prefix));
25
+ if (cookie === undefined)
26
+ return undefined;
27
+ try {
28
+ const json = JSON.parse(decodeURIComponent(cookie.slice(prefix.length)));
29
+ const parsed = userSchema.safeParse(json);
30
+ return parsed.success ? parsed.data : undefined;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ }
36
+ me() {
37
+ return this.#gate.pass('session.me', async () => {
38
+ const response = await fetch('/api/me');
39
+ if (response.status === 401)
40
+ return undefined;
41
+ if (!response.ok)
42
+ throw new Error('session.me failed');
43
+ return userSchema.parse(await response.json());
44
+ });
45
+ }
46
+ devSignIn(email) {
47
+ return this.#gate.pass('session.devSignIn', async () => {
48
+ const response = await fetch('/api/auth/dev-sign-in', {
49
+ method: 'POST',
50
+ headers: JSON_HEADERS,
51
+ body: JSON.stringify({ email })
52
+ });
53
+ if (!response.ok)
54
+ throw new Error('session.devSignIn failed');
55
+ return userSchema.parse(await response.json());
56
+ });
57
+ }
58
+ sendEmailCode(email) {
59
+ return this.#gate.pass('session.sendEmailCode', async () => {
60
+ const response = await fetch('/api/auth/email', {
61
+ method: 'POST',
62
+ headers: JSON_HEADERS,
63
+ body: JSON.stringify({ email })
64
+ });
65
+ return response.ok ? 'sent' : 'refused';
66
+ });
67
+ }
68
+ verifyEmailCode(code) {
69
+ return this.#gate.pass('session.verifyEmailCode', async () => {
70
+ const response = await fetch('/api/auth/email/verify', {
71
+ method: 'POST',
72
+ headers: JSON_HEADERS,
73
+ body: JSON.stringify({ code })
74
+ });
75
+ if (response.status === 401)
76
+ return undefined;
77
+ if (!response.ok)
78
+ throw new Error('session.verifyEmailCode failed');
79
+ return userSchema.parse(await response.json());
80
+ });
81
+ }
82
+ signOut() {
83
+ return this.#gate.pass('session.signOut', async () => {
84
+ await fetch('/api/auth/sign-out', { method: 'POST' });
85
+ });
86
+ }
87
+ startGoogleSignIn() {
88
+ location.assign('/api/auth/google');
89
+ }
90
+ onSignedOut(listener) {
91
+ this.#signedOutListeners.add(listener);
92
+ }
93
+ reportSignedOut() {
94
+ for (const listener of this.#signedOutListeners)
95
+ listener();
96
+ }
97
+ }
98
+ function devUser(email, admins) {
99
+ const name = email.split('@')[0] ?? email;
100
+ return {
101
+ id: ownerIdOf(email),
102
+ email,
103
+ name,
104
+ avatarUrl: null,
105
+ isAdmin: admins.has(email.toLowerCase())
106
+ };
107
+ }
108
+ export class MemorySessionApi {
109
+ #server;
110
+ #admins;
111
+ #user;
112
+ #emailCodeFor;
113
+ constructor(signedInAs, admins, options) {
114
+ this.#server = new MemoryServer('session', options);
115
+ this.#admins = adminEmails(admins);
116
+ this.#user =
117
+ signedInAs === null ? undefined : devUser(signedInAs, this.#admins);
118
+ }
119
+ rememberedUser() {
120
+ return this.#user;
121
+ }
122
+ me() {
123
+ return this.#server.reply('me', () => this.#user);
124
+ }
125
+ devSignIn(email) {
126
+ return this.#server.reply('devSignIn', () => {
127
+ this.#user = devUser(email, this.#admins);
128
+ return this.#user;
129
+ });
130
+ }
131
+ sendEmailCode(email) {
132
+ return this.#server.reply('sendEmailCode', () => {
133
+ this.#emailCodeFor = email;
134
+ return 'sent';
135
+ });
136
+ }
137
+ verifyEmailCode(code) {
138
+ return this.#server.reply('verifyEmailCode', () => {
139
+ if (this.#emailCodeFor === undefined || code !== MEMORY_EMAIL_CODE) {
140
+ return undefined;
141
+ }
142
+ this.#user = devUser(this.#emailCodeFor, this.#admins);
143
+ this.#emailCodeFor = undefined;
144
+ return this.#user;
145
+ });
146
+ }
147
+ signOut() {
148
+ return this.#server.reply('signOut', () => {
149
+ this.#user = undefined;
150
+ });
151
+ }
152
+ startGoogleSignIn() {
153
+ return undefined;
154
+ }
155
+ onSignedOut() {
156
+ return undefined;
157
+ }
158
+ }
@@ -0,0 +1,20 @@
1
+ import { StoreBase } from '../store.ts';
2
+ import type { EmailCodeOutcome, SessionApi } from './sessionApi.ts';
3
+ import type { User } from './user.ts';
4
+ export type SessionState = {
5
+ user: User | null;
6
+ };
7
+ export declare class SessionStore extends StoreBase<SessionState, {
8
+ api: SessionApi;
9
+ }> {
10
+ constructor(deps: {
11
+ api: SessionApi;
12
+ });
13
+ protected initialState(): SessionState;
14
+ confirm(): Promise<void>;
15
+ devSignIn(email: string): Promise<void>;
16
+ sendEmailCode(email: string): Promise<EmailCodeOutcome>;
17
+ verifyEmailCode(code: string): Promise<boolean>;
18
+ continueWithGoogle(): void;
19
+ signOut(): Promise<void>;
20
+ }
@@ -0,0 +1,40 @@
1
+ import { StoreBase } from "../store.js";
2
+ export class SessionStore extends StoreBase {
3
+ constructor(deps) {
4
+ super(deps);
5
+ deps.api.onSignedOut(() => void this.confirm());
6
+ }
7
+ initialState() {
8
+ return { user: this.deps.api.rememberedUser() ?? null };
9
+ }
10
+ async confirm() {
11
+ if (this.state.user === null)
12
+ return;
13
+ try {
14
+ this.set({ user: (await this.deps.api.me()) ?? null });
15
+ }
16
+ catch {
17
+ return;
18
+ }
19
+ }
20
+ async devSignIn(email) {
21
+ this.set({ user: await this.deps.api.devSignIn(email) });
22
+ }
23
+ sendEmailCode(email) {
24
+ return this.deps.api.sendEmailCode(email);
25
+ }
26
+ async verifyEmailCode(code) {
27
+ const user = await this.deps.api.verifyEmailCode(code);
28
+ if (user === undefined)
29
+ return false;
30
+ this.set({ user });
31
+ return true;
32
+ }
33
+ continueWithGoogle() {
34
+ this.deps.api.startGoogleSignIn();
35
+ }
36
+ async signOut() {
37
+ this.set({ user: null });
38
+ await this.deps.api.signOut();
39
+ }
40
+ }
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ export declare const userSchema: z.ZodObject<{
3
+ id: z.ZodString;
4
+ email: z.ZodString;
5
+ name: z.ZodString;
6
+ avatarUrl: z.ZodNullable<z.ZodString>;
7
+ isAdmin: z.ZodDefault<z.ZodBoolean>;
8
+ }, z.core.$strip>;
9
+ export type User = z.output<typeof userSchema>;
10
+ export type StoredUser = Omit<User, 'isAdmin'>;
11
+ export declare const USER_COOKIE = "pattern_user";
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ export const userSchema = z.object({
3
+ id: z.string(),
4
+ email: z.string(),
5
+ name: z.string(),
6
+ avatarUrl: z.string().nullable(),
7
+ isAdmin: z.boolean().default(false)
8
+ });
9
+ export const USER_COOKIE = 'pattern_user';
@@ -0,0 +1,25 @@
1
+ declare const Account_base: import("../model.ts").ModelClass<import("../registered.ts").AppStores, import("../registered.ts").ModelServices, {
2
+ appearance: import("zod").ZodDefault<import("zod").ZodEnum<{
3
+ system: "system";
4
+ light: "light";
5
+ dark: "dark";
6
+ }>>;
7
+ }, readonly ["appearance"]>;
8
+ export declare class Account extends Account_base {
9
+ get user(): {
10
+ id: string;
11
+ email: string;
12
+ name: string;
13
+ avatarUrl: string | null;
14
+ isAdmin: boolean;
15
+ } | null;
16
+ setAppearance: import("../model.ts").Command<{
17
+ appearance: import("zod").ZodEnum<{
18
+ system: "system";
19
+ light: "light";
20
+ dark: "dark";
21
+ }>;
22
+ }, void>;
23
+ signOut: import("../model.ts").Command<{}, Promise<void>>;
24
+ }
25
+ export {};
@@ -0,0 +1,15 @@
1
+ import { appearance } from "../appearance.js";
2
+ import { command } from "../model.js";
3
+ import { Model } from "../registered.js";
4
+ export class Account extends Model({
5
+ state: { appearance: appearance.default('system') },
6
+ persist: ['appearance']
7
+ }) {
8
+ get user() {
9
+ return this.stores.session.state.user;
10
+ }
11
+ setAppearance = command({ input: { appearance } }, ({ appearance }) => {
12
+ this.set({ appearance });
13
+ });
14
+ signOut = command(() => this.stores.session.signOut());
15
+ }
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ declare const AppSettings_base: import("../model.ts").ModelClass<import("../registered.ts").AppStores, import("../registered.ts").ModelServices, {
3
+ isRenaming: z.ZodDefault<z.ZodBoolean>;
4
+ }, []>;
5
+ export declare class AppSettings extends AppSettings_base {
6
+ get title(): string;
7
+ get canRename(): boolean;
8
+ rename: import("../model.ts").Command<{
9
+ title: z.ZodString;
10
+ }, Promise<void>>;
11
+ }
12
+ export {};
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+ import { appTitle } from "../appTitle.js";
3
+ import { command } from "../model.js";
4
+ import { Model } from "../registered.js";
5
+ export class AppSettings extends Model({
6
+ state: { isRenaming: z.boolean().default(false) }
7
+ }) {
8
+ get title() {
9
+ return this.stores.app.state.title;
10
+ }
11
+ get canRename() {
12
+ return this.stores.session.state.user?.isAdmin === true;
13
+ }
14
+ rename = command({ input: { title: appTitle } }, async ({ title }) => {
15
+ this.set({ isRenaming: true });
16
+ try {
17
+ await this.stores.app.rename(title);
18
+ this.stores.toasts.show({ tone: 'success', title: 'Name saved' });
19
+ }
20
+ catch {
21
+ this.stores.toasts.show({
22
+ tone: 'error',
23
+ title: 'Couldn’t save the name',
24
+ description: 'Check your connection and try again.'
25
+ });
26
+ }
27
+ finally {
28
+ this.set({ isRenaming: false });
29
+ }
30
+ });
31
+ }
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import type { AppAction } from '../registered.ts';
3
+ declare const Palette_base: import("../model.ts").ModelClass<import("../registered.ts").AppStores, import("../registered.ts").ModelServices, {
4
+ isOpen: z.ZodDefault<z.ZodBoolean>;
5
+ query: z.ZodDefault<z.ZodString>;
6
+ highlightedId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
7
+ }, []>;
8
+ export declare class Palette extends Palette_base {
9
+ get results(): AppAction[];
10
+ get highlighted(): string | null;
11
+ open: import("../model.ts").Command<{}, void>;
12
+ close: import("../model.ts").Command<{}, void>;
13
+ toggle: import("../model.ts").Command<{}, void>;
14
+ setQuery: import("../model.ts").Command<{
15
+ query: z.ZodString;
16
+ }, void>;
17
+ highlight: import("../model.ts").Command<{
18
+ id: z.ZodString;
19
+ }, void>;
20
+ moveHighlight: import("../model.ts").Command<{
21
+ by: z.ZodNumber;
22
+ }, void>;
23
+ submit: import("../model.ts").Command<{}, Promise<void> | undefined>;
24
+ run: import("../model.ts").Command<{
25
+ id: z.ZodString;
26
+ }, Promise<void> | undefined>;
27
+ private runAction;
28
+ }
29
+ export {};
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod';
2
+ import { rankActions } from "../actions.js";
3
+ import { command } from "../model.js";
4
+ import { Model } from "../registered.js";
5
+ export class Palette extends Model({
6
+ state: {
7
+ isOpen: z.boolean().default(false),
8
+ query: z.string().default(''),
9
+ highlightedId: z.string().nullable().default(null)
10
+ }
11
+ }) {
12
+ get results() {
13
+ return rankActions(this.stores.actions.state.items, this.state.query);
14
+ }
15
+ get highlighted() {
16
+ const { highlightedId } = this.state;
17
+ const { results } = this;
18
+ if (results.some((result) => result.id === highlightedId)) {
19
+ return highlightedId;
20
+ }
21
+ return results[0]?.id ?? null;
22
+ }
23
+ open = command(() => {
24
+ this.set({ isOpen: true, query: '', highlightedId: null });
25
+ });
26
+ close = command(() => {
27
+ this.set({ isOpen: false });
28
+ });
29
+ toggle = command(() => {
30
+ if (this.state.isOpen)
31
+ this.close();
32
+ else
33
+ this.open();
34
+ });
35
+ setQuery = command({ input: { query: z.string() } }, ({ query }) => {
36
+ this.set({ query, highlightedId: null });
37
+ });
38
+ highlight = command({ input: { id: z.string() } }, ({ id }) => {
39
+ if (this.results.some((result) => result.id === id)) {
40
+ this.set({ highlightedId: id });
41
+ }
42
+ });
43
+ moveHighlight = command({ input: { by: z.number().int() } }, ({ by }) => {
44
+ const { results } = this;
45
+ if (results.length === 0)
46
+ return;
47
+ const index = results.findIndex((result) => result.id === this.highlighted);
48
+ const next = (((index + by) % results.length) + results.length) % results.length;
49
+ this.set({ highlightedId: results[next]?.id ?? null });
50
+ });
51
+ submit = command(() => {
52
+ const chosen = this.results.find((result) => result.id === this.highlighted);
53
+ return chosen === undefined ? undefined : this.runAction(chosen);
54
+ });
55
+ run = command({ input: { id: z.string() } }, ({ id }) => {
56
+ const chosen = this.results.find((result) => result.id === id);
57
+ return chosen === undefined ? undefined : this.runAction(chosen);
58
+ });
59
+ async runAction(chosen) {
60
+ this.close();
61
+ await this.stores.actions.run(chosen);
62
+ }
63
+ }
@@ -0,0 +1,28 @@
1
+ import { z } from 'zod';
2
+ declare const SignIn_base: import("../model.ts").ModelClass<import("../registered.ts").AppStores, import("../registered.ts").ModelServices, {
3
+ step: z.ZodDefault<z.ZodEnum<{
4
+ code: "code";
5
+ choose: "choose";
6
+ }>>;
7
+ email: z.ZodDefault<z.ZodString>;
8
+ signingIn: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
9
+ email: "email";
10
+ google: "google";
11
+ dev: "dev";
12
+ }>>>;
13
+ notice: z.ZodDefault<z.ZodNullable<z.ZodString>>;
14
+ }, []>;
15
+ export declare class SignIn extends SignIn_base {
16
+ continueWithGoogle: import("../model.ts").Command<{}, void>;
17
+ sendEmailCode: import("../model.ts").Command<{
18
+ email: z.ZodEmail;
19
+ }, Promise<void>>;
20
+ verifyEmailCode: import("../model.ts").Command<{
21
+ code: z.ZodString;
22
+ }, Promise<void>>;
23
+ startOver: import("../model.ts").Command<{}, void>;
24
+ signInAsDevUser: import("../model.ts").Command<{
25
+ email: z.ZodEmail;
26
+ }, Promise<void>>;
27
+ }
28
+ export {};
@@ -0,0 +1,60 @@
1
+ import { z } from 'zod';
2
+ import { command } from "../model.js";
3
+ import { Model } from "../registered.js";
4
+ export class SignIn extends Model({
5
+ state: {
6
+ step: z.enum(['choose', 'code']).default('choose'),
7
+ email: z.string().default(''),
8
+ signingIn: z.enum(['google', 'email', 'dev']).nullable().default(null),
9
+ notice: z.string().nullable().default(null)
10
+ }
11
+ }) {
12
+ continueWithGoogle = command(() => {
13
+ this.set({ signingIn: 'google', notice: null });
14
+ this.stores.session.continueWithGoogle();
15
+ });
16
+ sendEmailCode = command({ input: { email: z.email() } }, async ({ email }) => {
17
+ this.set({ signingIn: 'email', notice: null });
18
+ try {
19
+ const outcome = await this.stores.session.sendEmailCode(email);
20
+ this.set(outcome === 'sent'
21
+ ? { signingIn: null, step: 'code', email }
22
+ : {
23
+ signingIn: null,
24
+ notice: `Couldn’t send a code to ${email}. Try again in a few minutes.`
25
+ });
26
+ }
27
+ catch {
28
+ this.set({ signingIn: null, notice: UNREACHABLE });
29
+ }
30
+ });
31
+ verifyEmailCode = command({ input: { code: z.string() } }, async ({ code }) => {
32
+ this.set({ signingIn: 'email', notice: null });
33
+ try {
34
+ const isSignedIn = await this.stores.session.verifyEmailCode(code);
35
+ this.set(isSignedIn
36
+ ? { signingIn: null, step: 'choose', email: '' }
37
+ : { signingIn: null, notice: 'That code is wrong or has expired.' });
38
+ }
39
+ catch {
40
+ this.set({ signingIn: null, notice: UNREACHABLE });
41
+ }
42
+ });
43
+ startOver = command(() => {
44
+ this.set({ step: 'choose', email: '', signingIn: null, notice: null });
45
+ });
46
+ signInAsDevUser = command({ input: { email: z.email() } }, async ({ email }) => {
47
+ this.set({ signingIn: 'dev', notice: null });
48
+ try {
49
+ await this.stores.session.devSignIn(email);
50
+ this.set({ signingIn: null });
51
+ }
52
+ catch {
53
+ this.set({
54
+ signingIn: null,
55
+ notice: `Couldn’t sign in as ${email}. Is the dev server running?`
56
+ });
57
+ }
58
+ });
59
+ }
60
+ const UNREACHABLE = 'Couldn’t reach the server. Check your connection and try again.';
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ declare const toast: z.ZodObject<{
3
+ id: z.ZodString;
4
+ tone: z.ZodEnum<{
5
+ success: "success";
6
+ error: "error";
7
+ neutral: "neutral";
8
+ }>;
9
+ title: z.ZodString;
10
+ description: z.ZodNullable<z.ZodString>;
11
+ }, z.core.$strip>;
12
+ export type Toast = z.output<typeof toast>;
13
+ type ToastInput = Pick<Toast, 'tone' | 'title'> & {
14
+ description?: string;
15
+ };
16
+ declare const Toasts_base: import("../store.ts").StoreClass<{
17
+ items: z.ZodDefault<z.ZodArray<z.ZodObject<{
18
+ id: z.ZodString;
19
+ tone: z.ZodEnum<{
20
+ success: "success";
21
+ error: "error";
22
+ neutral: "neutral";
23
+ }>;
24
+ title: z.ZodString;
25
+ description: z.ZodNullable<z.ZodString>;
26
+ }, z.core.$strip>>>;
27
+ }, [], Record<never, never>>;
28
+ export declare class Toasts extends Toasts_base {
29
+ show({ description, ...shown }: ToastInput): void;
30
+ dismiss(id: string): void;
31
+ }
32
+ export {};
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ import { defineStore } from "../store.js";
3
+ const Store = defineStore();
4
+ const toast = z.object({
5
+ id: z.string(),
6
+ tone: z.enum(['neutral', 'success', 'error']),
7
+ title: z.string(),
8
+ description: z.string().nullable()
9
+ });
10
+ const TOAST_MS = 4000;
11
+ export class Toasts extends Store({
12
+ state: { items: z.array(toast).default([]) }
13
+ }) {
14
+ show({ description, ...shown }) {
15
+ const id = crypto.randomUUID();
16
+ const added = { id, description: description ?? null, ...shown };
17
+ this.set({ items: [...this.state.items, added] });
18
+ setTimeout(() => this.dismiss(id), TOAST_MS);
19
+ }
20
+ dismiss(id) {
21
+ const items = this.state.items.filter((item) => item.id !== id);
22
+ if (items.length !== this.state.items.length)
23
+ this.set({ items });
24
+ }
25
+ }
@@ -0,0 +1,2 @@
1
+ export declare const BUILT_IN_GROUPS: readonly ["Preferences", "Account"];
2
+ export declare const builtInActions: import("../actions.ts").ActionsBuilder<import("../registered.ts").ActionContext, import("../registered.ts").AppAction>[];
@@ -0,0 +1,45 @@
1
+ import { appearance } from "../appearance.js";
2
+ import { action, defineActions } from "../registered.js";
3
+ export const BUILT_IN_GROUPS = ['Preferences', 'Account'];
4
+ const appearanceEntries = {
5
+ system: { label: 'Match the system appearance', icon: 'monitor' },
6
+ light: { label: 'Switch to light appearance', icon: 'sun' },
7
+ dark: { label: 'Switch to dark appearance', icon: 'moon' }
8
+ };
9
+ const paletteActions = defineActions(() => [
10
+ action({
11
+ id: 'palette.toggle',
12
+ label: 'Open command menu',
13
+ group: 'Preferences',
14
+ icon: 'search',
15
+ keys: ['mod+k'],
16
+ isHidden: true,
17
+ send: [{ type: 'palette.toggle' }]
18
+ })
19
+ ]);
20
+ const appearanceActions = defineActions(({ models }) => appearance.options
21
+ .filter((option) => option !== models.account.appearance)
22
+ .map((option) => action({
23
+ id: `appearance.${option}`,
24
+ label: appearanceEntries[option].label,
25
+ group: 'Preferences',
26
+ icon: appearanceEntries[option].icon,
27
+ keywords: 'theme mode color scheme',
28
+ send: [{ type: 'account.setAppearance', appearance: option }]
29
+ })));
30
+ const accountActions = defineActions(({ models }) => [
31
+ models.account.user !== null &&
32
+ action({
33
+ id: 'account.signOut',
34
+ label: 'Log out',
35
+ group: 'Account',
36
+ icon: 'logOut',
37
+ keywords: 'sign out',
38
+ send: [{ type: 'account.signOut' }]
39
+ })
40
+ ]);
41
+ export const builtInActions = [
42
+ paletteActions,
43
+ appearanceActions,
44
+ accountActions
45
+ ];
@@ -0,0 +1,24 @@
1
+ import type { z } from 'zod';
2
+ import type { Persisted } from './state.ts';
3
+ export type CacheStorage = {
4
+ get: (key: string) => string | undefined;
5
+ set: (key: string, value: string) => void;
6
+ keys: () => readonly string[];
7
+ remove: (key: string) => void;
8
+ };
9
+ export type CacheKey = string | (() => string);
10
+ export declare class Cached<Value> {
11
+ #private;
12
+ constructor(storage: CacheStorage, key: CacheKey, schema: z.ZodType<Value>);
13
+ read(): Value | undefined;
14
+ write(value: Value): void;
15
+ }
16
+ export declare class Cache {
17
+ #private;
18
+ constructor(storage: CacheStorage);
19
+ static local(): Cache;
20
+ static memory(): Cache;
21
+ scope<Value>(key: CacheKey, schema: z.ZodType<Value>): Cached<Value>;
22
+ persisted(model: string): Persisted;
23
+ clear(): void;
24
+ }