@trypattern/sdk 0.1.37 → 0.1.39
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/dist/bookings.d.ts +1 -7
- package/dist/bookingsStore.d.ts +1 -7
- package/dist/cli/app.js +71 -2
- package/dist/cli/guidance.d.ts +8 -0
- package/dist/cli/guidance.js +121 -0
- package/dist/cli/instructions.d.ts +21 -0
- package/dist/cli/instructions.js +206 -0
- package/dist/components/Calendar.d.ts +3 -1
- package/dist/components/Calendar.js +5 -5
- package/dist/components/Checkbox.js +2 -2
- package/dist/components/Dialog.js +10 -1
- package/dist/components/ScrollArea.js +4 -2
- package/dist/components/Select.d.ts +2 -1
- package/dist/components/Select.js +14 -3
- package/dist/components/calendarDates.d.ts +4 -3
- package/dist/components/calendarDates.js +8 -7
- package/dist/inquiriesStore.d.ts +1 -7
- package/dist/publishWire.d.ts +37 -59
- package/dist/publishWire.js +4 -2
- package/dist/requests.d.ts +14 -32
- package/dist/requests.js +12 -5
- package/dist/rules/lintPlugin.js +4 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +1 -0
- package/dist/server/inquiries.d.ts +7 -2
- package/dist/server/inquiries.js +2 -2
- package/dist/server/requestSources.d.ts +41 -0
- package/dist/server/requestSources.js +157 -0
- package/dist/server/requests.d.ts +7 -3
- package/dist/server/requests.js +130 -62
- package/dist/start.js +14 -0
- package/dist/theme/tokens.stylex.d.ts +1 -0
- package/dist/theme/tokens.stylex.js +2 -1
- package/docs/CHANGELOG.json +331 -0
- package/docs/PATTERN.md +87 -0
- package/docs/app.md +74 -0
- package/docs/data.md +109 -0
- package/docs/history/agents.json +1141 -0
- package/docs/look.md +147 -0
- package/docs/requests.md +115 -0
- package/docs/state.md +142 -0
- package/package.json +4 -3
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
{
|
|
2
|
+
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "0.1.39",
|
|
5
|
+
"added": [
|
|
6
|
+
"Pattern's guide ships inside Pattern: node_modules/@trypattern/sdk/docs/PATTERN.md, with references beside it, so it always matches the installed version.",
|
|
7
|
+
"app update prints what changed between the old and new Pattern and saves it to .pattern/update-report.md.",
|
|
8
|
+
"app migrate-instructions moves an older AGENTS.md onto the guide, keeping everything written for this site.",
|
|
9
|
+
"requestSource(), recordRequest() and requestSources([...]): your own server code (a custom checkout) records requests in the same batch as its rows, and declares its own owner actions.",
|
|
10
|
+
"deliverRequestNotices(c) sends the owner's email after the response, and Pattern keeps retrying on its own."
|
|
11
|
+
],
|
|
12
|
+
"changed": [
|
|
13
|
+
"A project's AGENTS.md keeps only what's about the site and imports Pattern's guide. app check and app start say \"instructions migration incomplete\" until an older one is moved over."
|
|
14
|
+
],
|
|
15
|
+
"visual": [],
|
|
16
|
+
"migrations": [
|
|
17
|
+
"Run app migrate-instructions, then follow .pattern/instructions-migration.md if it writes one."
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"version": "0.1.38",
|
|
22
|
+
"added": [
|
|
23
|
+
"Calendar weekStart=\"sunday\" starts its weeks on Sunday.",
|
|
24
|
+
"Select isLabelHidden={false} shows its label above it, like a TextField's.",
|
|
25
|
+
"corners.checkbox: a square Checkbox's corner, 4px whatever radii says."
|
|
26
|
+
],
|
|
27
|
+
"changed": [
|
|
28
|
+
"Dialog scrolls its own content when it's taller than the screen, so nothing in a sheet is out of reach.",
|
|
29
|
+
"A sideways ScrollArea lets up-and-down scrolling reach the page."
|
|
30
|
+
],
|
|
31
|
+
"visual": [
|
|
32
|
+
{
|
|
33
|
+
"what": "corners (Button, IconButton, TextField, Select, Menu, SegmentedControl)",
|
|
34
|
+
"before": "Since 0.1.28, buttons and fields kept Pattern's default corners even when the theme re-valued radii, so pill buttons became rounded rectangles.",
|
|
35
|
+
"after": "Buttons and fields follow the theme's radii again; a theme's own corners still win.",
|
|
36
|
+
"keep": "Set corners (buttonMd, fieldMd and the other sizes) in src/theme/theme.ts to the corners you had."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"what": "Checkbox shape=\"square\"",
|
|
40
|
+
"before": "Its corner came from radii.sm, so a theme with rounder cards made it round.",
|
|
41
|
+
"after": "It uses corners.checkbox (4px).",
|
|
42
|
+
"keep": "Re-value corners.checkbox in the theme."
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"migrations": []
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"version": "0.1.37",
|
|
49
|
+
"added": [
|
|
50
|
+
"Skeleton text=\"heading\", \"lead\", \"label\" or another Text variant takes exactly one line of that text, so loading draws in the final layout.",
|
|
51
|
+
"app update waits and tries again when npm hasn't finished sharing a brand-new Pattern."
|
|
52
|
+
],
|
|
53
|
+
"changed": [],
|
|
54
|
+
"visual": [],
|
|
55
|
+
"migrations": []
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"version": "0.1.36",
|
|
59
|
+
"added": [
|
|
60
|
+
"colors.attentionSolid and colors.onAttentionSolidForeground: a blue for something waiting, whatever the brand colour.",
|
|
61
|
+
"fontSizes.badge (9px), for counts."
|
|
62
|
+
],
|
|
63
|
+
"changed": [
|
|
64
|
+
"AppFrame links: countTone 'brand' is now countTone 'attention', a solid blue badge."
|
|
65
|
+
],
|
|
66
|
+
"visual": [
|
|
67
|
+
{
|
|
68
|
+
"what": "Calendar",
|
|
69
|
+
"before": "Its days had a fixed width, so it didn't fill a wide sheet or panel.",
|
|
70
|
+
"after": "It fills the width it's given; each day keeps its size.",
|
|
71
|
+
"keep": "Give the Calendar a narrower parent."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"what": "Choice (selected)",
|
|
75
|
+
"before": "A 2px dark ring.",
|
|
76
|
+
"after": "A 1px ring.",
|
|
77
|
+
"keep": ""
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"what": "Sidebar count badge",
|
|
81
|
+
"before": "18px, in the brand colour.",
|
|
82
|
+
"after": "16px, blue (attention), with a 9px number.",
|
|
83
|
+
"keep": ""
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"what": "TableRow",
|
|
87
|
+
"before": "Hover and the open row used the standard grey highlight.",
|
|
88
|
+
"after": "A lighter highlight on white cards.",
|
|
89
|
+
"keep": ""
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"what": "FilePicker",
|
|
93
|
+
"before": "A 1.5px dashed edge.",
|
|
94
|
+
"after": "A 1px dashed edge.",
|
|
95
|
+
"keep": ""
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"migrations": [
|
|
99
|
+
"Replace countTone: 'brand' with countTone: 'attention' in the layout model's links."
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"version": "0.1.35",
|
|
104
|
+
"added": [],
|
|
105
|
+
"changed": [],
|
|
106
|
+
"visual": [
|
|
107
|
+
{
|
|
108
|
+
"what": "Icon",
|
|
109
|
+
"before": "An icon made of several paths was drawn as separate lines, so their colour doubled where they overlapped.",
|
|
110
|
+
"after": "All of an icon's paths draw as one shape, in one even colour.",
|
|
111
|
+
"keep": ""
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"migrations": []
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"version": "0.1.34",
|
|
118
|
+
"added": [
|
|
119
|
+
"Requests: inquiry() and inquiries([...]) declare forms visitors send; Pattern saves them, emails the owner and lists them on onpattern.app with bookings.",
|
|
120
|
+
"this.stores.inquiries.send(source, fields) sends a form.",
|
|
121
|
+
"app requests lists the live site's requests and drafts replies for the owner to send.",
|
|
122
|
+
"AppFrame links take countTone for a solid count badge; SegmentedControl options take a count; TableRow size=\"lg\"."
|
|
123
|
+
],
|
|
124
|
+
"changed": [
|
|
125
|
+
"The bookings block is a request source: booking requests appear in Requests and the owner accepts or declines them there.",
|
|
126
|
+
"bookingTables now include requestTables."
|
|
127
|
+
],
|
|
128
|
+
"visual": [],
|
|
129
|
+
"migrations": [
|
|
130
|
+
"A site with bookings or forms runs app migration (for example app migration pattern_requests) to create Pattern's request tables, then publishes."
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"version": "0.1.33",
|
|
135
|
+
"added": [
|
|
136
|
+
"FilePreview shows a picked photo straight away, before it uploads.",
|
|
137
|
+
"Image origin shows a photo from another site."
|
|
138
|
+
],
|
|
139
|
+
"changed": [
|
|
140
|
+
"A new site's starting rows get the time of its first publish, not a fixed date in 2000."
|
|
141
|
+
],
|
|
142
|
+
"visual": [
|
|
143
|
+
{
|
|
144
|
+
"what": "Menu and SegmentedControl",
|
|
145
|
+
"before": "Their buttons lost their round corners in 0.1.28.",
|
|
146
|
+
"after": "Round corners again.",
|
|
147
|
+
"keep": ""
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"migrations": []
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"version": "0.1.32",
|
|
154
|
+
"added": [
|
|
155
|
+
"FilePicker (a drop zone with a button that picks a file), ProgressBar and TableRow.",
|
|
156
|
+
"Server calls can upload a file and report progress."
|
|
157
|
+
],
|
|
158
|
+
"changed": [],
|
|
159
|
+
"visual": [],
|
|
160
|
+
"migrations": []
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"version": "0.1.31",
|
|
164
|
+
"added": [
|
|
165
|
+
"Gallery: photos one at a time, swipe on phones, arrows on hover, dots and an \"N of M\" count.",
|
|
166
|
+
"Dialog placement=\"sheet\": a bottom sheet on phones with a grab handle to drag it closed.",
|
|
167
|
+
"Header: keeps a site's top bar at the top, frosted with a hairline once scrolled; layers.header."
|
|
168
|
+
],
|
|
169
|
+
"changed": [],
|
|
170
|
+
"visual": [],
|
|
171
|
+
"migrations": []
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"version": "0.1.30",
|
|
175
|
+
"added": [
|
|
176
|
+
"Card hover=\"zoom\" and hover=\"outline\", and isPressable, all still for people who turn motion off."
|
|
177
|
+
],
|
|
178
|
+
"changed": [],
|
|
179
|
+
"visual": [],
|
|
180
|
+
"migrations": []
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"version": "0.1.29",
|
|
184
|
+
"added": [
|
|
185
|
+
"Checkbox shape=\"square\".",
|
|
186
|
+
"IconButton variant=\"primary\" (brand-coloured) and shape=\"round\".",
|
|
187
|
+
"Button align=\"start\"."
|
|
188
|
+
],
|
|
189
|
+
"changed": [],
|
|
190
|
+
"visual": [
|
|
191
|
+
{
|
|
192
|
+
"what": "Button isFullWidth",
|
|
193
|
+
"before": "The label sat at the start.",
|
|
194
|
+
"after": "The label is centred.",
|
|
195
|
+
"keep": "Add align=\"start\"."
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"migrations": []
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"version": "0.1.28",
|
|
202
|
+
"added": [
|
|
203
|
+
"corners: buttons' and fields' own corner sizes in the theme, so pill buttons can sit beside squarer fields.",
|
|
204
|
+
"src/theme/colors.stylex.ts holds the business's own named colours."
|
|
205
|
+
],
|
|
206
|
+
"changed": ["The theme may re-value fontWeights, trackings and corners."],
|
|
207
|
+
"visual": [
|
|
208
|
+
{
|
|
209
|
+
"what": "corners (Button, IconButton, TextField, Select)",
|
|
210
|
+
"before": "Buttons and fields took their corners from radii.",
|
|
211
|
+
"after": "They take them from corners, which kept Pattern's defaults even when the theme re-valued radii (fixed in 0.1.38).",
|
|
212
|
+
"keep": ""
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
"migrations": []
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"version": "0.1.27",
|
|
219
|
+
"added": [
|
|
220
|
+
"fonts.heading: a second font for the hero, display, section, heading and subheading variants.",
|
|
221
|
+
"Text weight (normal, medium, semibold, bold, heavy), and fontWeights.heading.",
|
|
222
|
+
"fontSizes.bodyCompact and lineHeights.bodyCompact: the body and label size on phones."
|
|
223
|
+
],
|
|
224
|
+
"changed": [],
|
|
225
|
+
"visual": [],
|
|
226
|
+
"migrations": []
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"version": "0.1.26",
|
|
230
|
+
"added": [
|
|
231
|
+
"The device store: state.today (kept current past midnight) and state.isCompact (phone width), readable in getters."
|
|
232
|
+
],
|
|
233
|
+
"changed": [
|
|
234
|
+
"A table no longer needs an exported row type.",
|
|
235
|
+
"A store's this.services is typed with the app's own server calls."
|
|
236
|
+
],
|
|
237
|
+
"visual": [],
|
|
238
|
+
"migrations": []
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"version": "0.1.25",
|
|
242
|
+
"added": [
|
|
243
|
+
"A table's defaults: what rows that already exist start with when a required column is added."
|
|
244
|
+
],
|
|
245
|
+
"changed": [
|
|
246
|
+
"app migration refuses a new required column without a default, because the live site has rows."
|
|
247
|
+
],
|
|
248
|
+
"visual": [],
|
|
249
|
+
"migrations": []
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"version": "0.1.24",
|
|
253
|
+
"added": [],
|
|
254
|
+
"changed": [
|
|
255
|
+
"Migrations give new required columns a starting value, so they work on tables that already have rows."
|
|
256
|
+
],
|
|
257
|
+
"visual": [],
|
|
258
|
+
"migrations": []
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
"version": "0.1.23",
|
|
262
|
+
"added": [
|
|
263
|
+
"app start, check, publish and data say when a newer Pattern is out, and app update installs it."
|
|
264
|
+
],
|
|
265
|
+
"changed": [],
|
|
266
|
+
"visual": [],
|
|
267
|
+
"migrations": []
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"version": "0.1.22",
|
|
271
|
+
"added": [
|
|
272
|
+
"app data: Claude collects changes to the live site's rows and the owner approves them on onpattern.app."
|
|
273
|
+
],
|
|
274
|
+
"changed": [],
|
|
275
|
+
"visual": [],
|
|
276
|
+
"migrations": []
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
"version": "0.1.21",
|
|
280
|
+
"added": [],
|
|
281
|
+
"changed": [
|
|
282
|
+
"Owner emails about bookings read like a person wrote them, link to Requests and reply to the customer."
|
|
283
|
+
],
|
|
284
|
+
"visual": [],
|
|
285
|
+
"migrations": []
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"version": "0.1.20",
|
|
289
|
+
"added": ["New booking requests email the site's owner."],
|
|
290
|
+
"changed": [],
|
|
291
|
+
"visual": [],
|
|
292
|
+
"migrations": []
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"version": "0.1.19",
|
|
296
|
+
"added": [],
|
|
297
|
+
"changed": [
|
|
298
|
+
"An old copy of a site can't be published over the live one; app publish says to pull into a new folder."
|
|
299
|
+
],
|
|
300
|
+
"visual": [],
|
|
301
|
+
"migrations": []
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
"version": "0.1.18",
|
|
305
|
+
"added": [],
|
|
306
|
+
"changed": [
|
|
307
|
+
"The site's owner on onpattern.app is an admin on the live site without being listed in admins."
|
|
308
|
+
],
|
|
309
|
+
"visual": [],
|
|
310
|
+
"migrations": []
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"version": "0.1.17",
|
|
314
|
+
"added": [
|
|
315
|
+
"ScrollArea resetOn goes back to the top when it changes, so a link lands at the top of the next page."
|
|
316
|
+
],
|
|
317
|
+
"changed": [],
|
|
318
|
+
"visual": [],
|
|
319
|
+
"migrations": []
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"version": "0.1.16",
|
|
323
|
+
"added": [],
|
|
324
|
+
"changed": [
|
|
325
|
+
"app pull picks a fresh folder (./<name>-2 and on) when ./<name> is taken."
|
|
326
|
+
],
|
|
327
|
+
"visual": [],
|
|
328
|
+
"migrations": []
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
}
|
package/docs/PATTERN.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Pattern
|
|
2
|
+
|
|
3
|
+
Pattern (`@trypattern/sdk`) is the engine this project runs on: the screens, state, data, sign-in,
|
|
4
|
+
look, checks and the `app` command. This guide ships inside Pattern, so it always describes the
|
|
5
|
+
version installed here. The project's own `AGENTS.md` describes this site or app; this guide and the
|
|
6
|
+
references beside it describe Pattern.
|
|
7
|
+
|
|
8
|
+
## Working with the person
|
|
9
|
+
|
|
10
|
+
- The person you work with may not be a developer. Explain what you are doing in plain words, one
|
|
11
|
+
short step at a time, and run every command yourself: never ask them to type one.
|
|
12
|
+
- `app help` lists the commands and `app <command> --help` explains one. If `app` is not on your
|
|
13
|
+
PATH, run `./app setup` once.
|
|
14
|
+
- Done = `app check` passes. Its errors explain every rule; fix the code, never the rule. A failing
|
|
15
|
+
step prints a `rerun:` command and, when one exists, a `fix:` command (`app fix`).
|
|
16
|
+
- Publish only when the person says so (or the first change, which `app pull` says goes live
|
|
17
|
+
straight away), and only after `app check` passes.
|
|
18
|
+
|
|
19
|
+
## When Pattern updates
|
|
20
|
+
|
|
21
|
+
- When `app` says a newer Pattern is out, run `app update`. It installs the new version, prints what
|
|
22
|
+
changed and saves it to `.pattern/update-report.md`: what's new, what looks different and what to
|
|
23
|
+
do now (such as `app migration`). Do those, run `app check`, and tell the person in a few words.
|
|
24
|
+
- After an update, reread this guide and the references you rely on: they changed with Pattern, and
|
|
25
|
+
what you remember of them is from the old version. Before deciding Pattern can't do something,
|
|
26
|
+
check the capability list below.
|
|
27
|
+
- `app check` and `app start` say "instructions migration incomplete" while `AGENTS.md` still
|
|
28
|
+
carries Pattern's old rules. Run `app migrate-instructions` and follow what it prints.
|
|
29
|
+
|
|
30
|
+
## Rules that always apply
|
|
31
|
+
|
|
32
|
+
- State flows network → service → store → model → screen, and each layer reads only the one below
|
|
33
|
+
it. Screens read one model and arrange primitives; they have no styles and no plain elements.
|
|
34
|
+
- Everything on screen is drawn by the primitives from `@trypattern/sdk/react`. A piece in
|
|
35
|
+
`src/components/` may draw a shape no primitive gives, with every style value a token. If no
|
|
36
|
+
primitive fits, the gap is in Pattern: never write `<button>`, `<input>`, `<p>` or `onClick` on
|
|
37
|
+
a `<div>`.
|
|
38
|
+
- Every table says who may read and change it (`access`), and the server enforces it.
|
|
39
|
+
- The live site's data is the owner's: change it only with `app data`, which the owner approves.
|
|
40
|
+
- A new feature gets a command on a model and a flow that proves it.
|
|
41
|
+
- Draw loading in the page's final layout (`Skeleton` lines where the words will be), never a
|
|
42
|
+
separate loader first.
|
|
43
|
+
|
|
44
|
+
## Does Pattern already do this?
|
|
45
|
+
|
|
46
|
+
| You want | Pattern has | Read |
|
|
47
|
+
| ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------- |
|
|
48
|
+
| Change the live site's items, prices or other rows | `app data`, approved by the owner on onpattern.app | [data](data.md) |
|
|
49
|
+
| A photo on the site | `app media add photo.jpg --alt '…' --live`, or the owner uploads it on onpattern.app | [data](data.md) |
|
|
50
|
+
| A form visitors send: order, quote, contact, early access | `inquiry()` and `inquiries([...])`; it lands in Requests | [requests](requests.md) |
|
|
51
|
+
| Bookings with availability | the bookings block | [requests](requests.md) |
|
|
52
|
+
| Email the owner when something arrives | automatic for every request: never write it yourself | [requests](requests.md) |
|
|
53
|
+
| Your own server code (a custom checkout) taking in requests | `requestSource()`, `recordRequest()` and `requestSources([...])`, with your own owner actions | [requests](requests.md) |
|
|
54
|
+
| A reply drafted for the owner to send | `app requests draft` | [requests](requests.md) |
|
|
55
|
+
| Photos one at a time, swipe, dots and arrows | `Gallery` | [look](look.md) |
|
|
56
|
+
| A bottom sheet on phones that drags closed | `Dialog placement="sheet"` | [look](look.md) |
|
|
57
|
+
| A header that stays at the top, frosted once scrolled | `Header` | [look](look.md) |
|
|
58
|
+
| Hover zoom, a darker edge on hover, press-in | `Card` `hover="zoom"`, `hover="outline"`, `isPressable` | [look](look.md) |
|
|
59
|
+
| Big type: the home page line, page titles, section headings | `Text` variants `hero`, `display`, `section`, `intro`, `lead` | [look](look.md) |
|
|
60
|
+
| Bold or semibold text | `Text weight`, and the theme's `fontWeights` | [look](look.md) |
|
|
61
|
+
| A second font for headings | the theme's `fonts.heading` | [look](look.md) |
|
|
62
|
+
| Text that doesn't grow on phones | the theme's `fontSizes.bodyCompact` and `lineHeights.bodyCompact` | [look](look.md) |
|
|
63
|
+
| Tighter or looser letter spacing | the theme's `trackings` | [look](look.md) |
|
|
64
|
+
| Pill buttons beside squarer fields | the theme's `corners` | [look](look.md) |
|
|
65
|
+
| The business's own colour names | `src/theme/colors.stylex.ts` | [look](look.md) |
|
|
66
|
+
| A square checkbox | `Checkbox shape="square"` | [look](look.md) |
|
|
67
|
+
| A brand-coloured round icon button | `IconButton variant="primary" shape="round"` | [look](look.md) |
|
|
68
|
+
| A full-width button with its label centred | `Button isFullWidth` (centred unless `align="start"`) | [look](look.md) |
|
|
69
|
+
| A calendar starting on Sunday, with fully booked days crossed out | `Calendar weekStart="sunday"` and `full` days | [look](look.md) |
|
|
70
|
+
| A dropdown with a visible label | `Select isLabelHidden={false}` | [look](look.md) |
|
|
71
|
+
| Picking and uploading a file | `FilePicker`, `FilePreview`, `ProgressBar` | [look](look.md) |
|
|
72
|
+
| Today's date or a phone-width screen in a getter | `this.stores.device.state.today` and `.isCompact` | [state](state.md) |
|
|
73
|
+
| Menu entries and keyboard shortcuts, ⌘K | actions | [state](state.md) |
|
|
74
|
+
| A new required column on a table that already has rows | the table's `defaults` | [data](data.md) |
|
|
75
|
+
| Search engines and shared links reading real words | `server/pages.ts` | [data](data.md) |
|
|
76
|
+
|
|
77
|
+
Not in Pattern, on purpose: the hand cursor on controls, breakpoints besides `breakpoints.compact`,
|
|
78
|
+
inline `style` attributes, and font sizes outside the theme's scale. Not in Pattern yet: secrets on
|
|
79
|
+
the live site (such as a Stripe key; payments will come through Pattern).
|
|
80
|
+
|
|
81
|
+
## References
|
|
82
|
+
|
|
83
|
+
- [The `app` command, going live and updating](app.md)
|
|
84
|
+
- [State: stores, models, actions, screens and flows](state.md)
|
|
85
|
+
- [Data: tables, access, migrations, photos, live data and sign-in](data.md)
|
|
86
|
+
- [The look: primitives, theme, tokens and icons](look.md)
|
|
87
|
+
- [Requests: forms, bookings and owner emails](requests.md)
|
package/docs/app.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# The `app` command
|
|
2
|
+
|
|
3
|
+
The `app` command, the checks, the lint rules, the flow runner and the Vite setup all come from
|
|
4
|
+
`@trypattern/sdk`, so a project has no scripts or tool configs of its own: `vite.config.ts` is
|
|
5
|
+
`pattern({ tables, admins, seed })` and the tsconfigs extend Pattern's. Updating Pattern brings
|
|
6
|
+
their fixes. `app help` lists every command and `app <command> --help` explains one.
|
|
7
|
+
|
|
8
|
+
## Working on this computer
|
|
9
|
+
|
|
10
|
+
- `app start` runs the dev server in the background and prints its address. `app status` prints
|
|
11
|
+
the address and the log's path; `app stop` stops it.
|
|
12
|
+
- `app check` is the definition of done: typecheck, lint, Pattern's rules, format, flows and build.
|
|
13
|
+
It runs every step even when one fails; `app check <step>` runs one. `app fix` applies the
|
|
14
|
+
automatic fixes (formatting and the lint fixes that are safe).
|
|
15
|
+
- `app test` runs every flow in about a second; `app test --visual` runs them in Chrome and saves a
|
|
16
|
+
screenshot of every step, with a contact sheet. `app screenshot <path>` shows one page at desktop
|
|
17
|
+
and phone sizes. Look at the PNGs to see a change.
|
|
18
|
+
- `app send '<command json>'` sends a command to the running app, the same commands flows send.
|
|
19
|
+
- `app sign-in [email]` signs the browser in as a dev user. Locally the emailed sign-in code prints
|
|
20
|
+
in the dev log.
|
|
21
|
+
- `app reset` empties the local database and fills it again from `seed.ts` and `starter/`.
|
|
22
|
+
- `app migration <name>` writes a migration after a table changes (see [data](data.md)).
|
|
23
|
+
|
|
24
|
+
## Going live
|
|
25
|
+
|
|
26
|
+
- `pattern.json` names the site (`<name>.onpattern.app`), the `title` an empty local database starts
|
|
27
|
+
with, and `blueprint`, the starter it began from (`website` or `web-app`).
|
|
28
|
+
- When the person says publish (or put it live), run `app check`, then
|
|
29
|
+
`app publish --message '…'`: it builds, uploads and waits until the new version answers, printing
|
|
30
|
+
each step. The message is a few plain words for the owner about what changed, such as 'Pink brand
|
|
31
|
+
colour and a welcome line', never technical; onpattern.app shows it beside the version.
|
|
32
|
+
- `app versions` lists what went live and `app restore <number>` puts an earlier version back. The
|
|
33
|
+
database is not rolled back.
|
|
34
|
+
- When this computer isn't connected yet, these print a short code and open the browser: tell the
|
|
35
|
+
person to check the code matches and click Allow, then run the same command again straight away;
|
|
36
|
+
it waits for Allow.
|
|
37
|
+
- An old copy of a site can't be published over the live one. If publish refuses, `app pull` the
|
|
38
|
+
site into a new folder and carry the change over.
|
|
39
|
+
- `app pull <name>` downloads a live site into a new folder, ready for `app start`.
|
|
40
|
+
|
|
41
|
+
## The live site's content
|
|
42
|
+
|
|
43
|
+
- `app data` reads and changes the live site's rows, with the owner's approval (see
|
|
44
|
+
[data](data.md)).
|
|
45
|
+
- `app media add photo.jpg --alt '…' --live` puts a photo on the live site.
|
|
46
|
+
- `app requests` lists the live site's requests and drafts replies for the owner (see
|
|
47
|
+
[requests](requests.md)).
|
|
48
|
+
|
|
49
|
+
## Updating Pattern
|
|
50
|
+
|
|
51
|
+
- `app start`, `app check`, `app publish` and `app data` say when a newer Pattern is out. Run
|
|
52
|
+
`app update`: it installs the newest Pattern (waiting a moment if npm hasn't finished sharing a
|
|
53
|
+
brand-new version), prints what changed and saves the report to `.pattern/update-report.md`.
|
|
54
|
+
- The report has what's new, what looks different (and how to keep the old look), and what to do
|
|
55
|
+
now. Do what it says, usually `app migration` with the name it gives (Pattern's own tables may
|
|
56
|
+
have changed, and it writes nothing when they didn't), then `app check`.
|
|
57
|
+
- Then publish (straight away when nothing else is in progress) so the live site gets the new
|
|
58
|
+
Pattern too, and tell the person in a few words.
|
|
59
|
+
- Reread [the guide](PATTERN.md) after an update: it changed with Pattern.
|
|
60
|
+
- `app migrate-instructions` moves an older project's `AGENTS.md` onto this guide: it removes the
|
|
61
|
+
rules Pattern now ships, keeps everything written for this site, and writes
|
|
62
|
+
`.pattern/instructions-migration.md` for anything it couldn't decide. `app check` says
|
|
63
|
+
"instructions migration incomplete" until that's done.
|
|
64
|
+
|
|
65
|
+
## Design system and analytics
|
|
66
|
+
|
|
67
|
+
- `app build` (and so `app check`) writes `dist/design.json`, the app's design system as Pattern's
|
|
68
|
+
dashboard shows it: every token with the app's overrides marked, each primitive with the variants,
|
|
69
|
+
tones and sizes it is used with, the app's own pieces and the tokens they draw with, and its
|
|
70
|
+
icons. Write those props out where they are used (`variant="secondary"`, or a choice between two),
|
|
71
|
+
so the list is exact.
|
|
72
|
+
- `startApp(App, { onInteraction })` receives a report from every primitive a person uses
|
|
73
|
+
(`{ primitive: 'Button', event: 'click', label: 'Send request', page: 'home' }`), never a
|
|
74
|
+
field's value, which is how analytics see every control.
|
package/docs/data.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Data: tables, access, migrations, photos, live data and sign-in
|
|
2
|
+
|
|
3
|
+
## Tables
|
|
4
|
+
|
|
5
|
+
- Saved data lives in tables declared once in `schema.ts`. A table is `{ columns, access, writes }`:
|
|
6
|
+
`reviews: { columns: { name: z.string().min(1) }, access: { read: 'anyone', create: 'signedIn',
|
|
7
|
+
update: 'admin', delete: 'admin' }, writes: 'confirmed' }`. Every row also gets `id` and
|
|
8
|
+
`createdAt`, which Pattern sets: `reviews.create({ name, … })`. A row type
|
|
9
|
+
(`export type Review = Row<typeof tables.reviews>`) is optional; export one where another file
|
|
10
|
+
uses it.
|
|
11
|
+
- Every table says who may do what, or it does not compile. `access` gives `read`, `create`,
|
|
12
|
+
`update` and `delete` one of:
|
|
13
|
+
- `anyone` (signed out too);
|
|
14
|
+
- `signedIn`;
|
|
15
|
+
- `owner` (your own rows);
|
|
16
|
+
- `admin`: the site's owner on onpattern.app, who is an admin on the live site without being
|
|
17
|
+
listed, and the emails in `export const admins` in `schema.ts`, which is who is an admin locally
|
|
18
|
+
and in flows. Admins pass every rule except `server`;
|
|
19
|
+
- `server` (only Pattern's server code; a table nobody reads from the browser has no store).
|
|
20
|
+
|
|
21
|
+
The server enforces it and the flow runner's fake server uses the same rules, so read `access`
|
|
22
|
+
aloud before writing a screen around it.
|
|
23
|
+
|
|
24
|
+
- `owned: true` gives every row an `ownerId` that the server fills from the session; the app never
|
|
25
|
+
sends it. With `read: 'owner'` the store holds only this person's rows (an admin's holds
|
|
26
|
+
everyone's), so a model never filters "mine" itself.
|
|
27
|
+
- `public: ['chairId', 'date']` lets anyone see those columns of every row, through the read-only
|
|
28
|
+
`publicBookings` store, whose rows have only `id` and those columns (list `createdAt` too if it may
|
|
29
|
+
be seen).
|
|
30
|
+
- `unique: [['chairId', 'date']]` makes the database refuse a second row with the same values.
|
|
31
|
+
- `defaults: { colour: 'white' }` is what rows that already exist start with when a required column
|
|
32
|
+
is added or a column becomes required. `app migration` refuses without one, because the live site
|
|
33
|
+
has rows; pick a value that's true for the business, or make the column `.nullable()`.
|
|
34
|
+
- `writes: 'optimistic'` shows a write at once and takes it back if the server refuses;
|
|
35
|
+
`writes: 'confirmed'` shows the row only once the server saved it, and a table with `unique` must be
|
|
36
|
+
confirmed. Meanwhile `pending` lists each unanswered write as `{ kind, id, row }`, so a screen can
|
|
37
|
+
say "Booking…".
|
|
38
|
+
- `create`, `update` and `remove` return `'saved'`, `'conflict'` (a unique clash), `'forbidden'`,
|
|
39
|
+
`'missing'` (the row is gone or not yours) or `'failed'` (no connection). Show a toast for
|
|
40
|
+
anything but `'saved'`.
|
|
41
|
+
- A table the person may not read yet stays `status: 'idle'` and loads once they sign in.
|
|
42
|
+
- The server serves every table under `/api/tables/<name>`, the database columns come from the same
|
|
43
|
+
declaration, and each table gets a store of the same name.
|
|
44
|
+
|
|
45
|
+
## Migrations
|
|
46
|
+
|
|
47
|
+
- After changing a table, run `app migration <name>`. It writes the next file in
|
|
48
|
+
`server/migrations/`, the database's history, and refuses a change that would break the live
|
|
49
|
+
site's existing rows (see `defaults`).
|
|
50
|
+
- Never put content in a migration to get it live: that's `app data`'s job.
|
|
51
|
+
|
|
52
|
+
## Sample data and starting content
|
|
53
|
+
|
|
54
|
+
- `seed.ts` is sample data for local work and flows and never ships. `app start` fills only empty
|
|
55
|
+
tables, so after changing `seed.ts` run `app reset`.
|
|
56
|
+
- Seed rows of an owned table name their owner: `{ id: 'booking-1', owner: 'ada@acme.test', ... }`.
|
|
57
|
+
- `flows/Access.flow.ts` checks every table's rules against the seed on each `app test` (signed
|
|
58
|
+
out, a stranger, each owner, an admin, public columns, a duplicate), so a broken rule fails by
|
|
59
|
+
name. `app check` requires it. The admin emails stay on the server and in tests; the browser only
|
|
60
|
+
learns whether the signed-in person is an admin, as `session.state.user.isAdmin`.
|
|
61
|
+
- A website's `starter/` is the content a new site opens with, written once on its first publish and
|
|
62
|
+
then owned by the owner. Once the site is live the folder does nothing.
|
|
63
|
+
|
|
64
|
+
## The live site's data
|
|
65
|
+
|
|
66
|
+
The live site's data (its items, prices, bookings) is the owner's. To change it:
|
|
67
|
+
|
|
68
|
+
1. `app data tables` and `app data list <table>` read it.
|
|
69
|
+
2. `app data add`, `update` and `remove` collect the changes.
|
|
70
|
+
3. `app data send --asked '<their request, in their words>'` sends them: the owner approves them on
|
|
71
|
+
onpattern.app, and it waits for their decision (`app data --help`).
|
|
72
|
+
|
|
73
|
+
Never change live data any other way, and never put it in `seed.ts` or a migration to get it live.
|
|
74
|
+
The owner can also change rows themselves on onpattern.app (Database).
|
|
75
|
+
|
|
76
|
+
## Photos
|
|
77
|
+
|
|
78
|
+
- Photos live in the `media` table (`{ ...defineTables({ … }), media }` in `schema.ts`), added with
|
|
79
|
+
`app media add photo.jpg --alt '…'`. `--live` also puts it on the live site, and the owner can
|
|
80
|
+
upload photos on onpattern.app (Database).
|
|
81
|
+
- Show one with `<Image image={…} sizes="…" />`; `priority` on the first image of a page only; never
|
|
82
|
+
`<img>`, never a hard-coded image URL. Every photo has alt text written for someone who cannot see
|
|
83
|
+
it.
|
|
84
|
+
- A row stores the photo's media `id`, and a model hands the media row to the screen.
|
|
85
|
+
|
|
86
|
+
## The server
|
|
87
|
+
|
|
88
|
+
- The server is `createServer({ tables, admins })` in `server/worker.ts` (a website adds `pages`), a
|
|
89
|
+
Cloudflare Worker that `app start` serves on the same origin under `/api`.
|
|
90
|
+
- A website's `server/pages.ts` gives each public page its `<head>`: a title, a description, the
|
|
91
|
+
photo shown when the link is shared, structured data for search engines (`localBusiness`,
|
|
92
|
+
`product`) and the rows the page needs, so the page arrives filled in and search engines read real
|
|
93
|
+
words. A new public page gets an entry there, and its path goes in `run_worker_first` in
|
|
94
|
+
`wrangler.jsonc`.
|
|
95
|
+
- Blocks such as `bookings(...)` and `inquiries([...])` go in `server/worker.ts`'s `blocks` (see
|
|
96
|
+
[requests](requests.md)).
|
|
97
|
+
|
|
98
|
+
## Sign-in
|
|
99
|
+
|
|
100
|
+
- Sign-in comes from Pattern and goes through auth.trypattern.ai: Google, or a 6-digit code emailed
|
|
101
|
+
to the person, and a dev-only sign-in. Locally the emailed code prints in the dev log instead, and
|
|
102
|
+
Google works only once the app is deployed.
|
|
103
|
+
- Everything under `/api` needs a session except `/api/health`, `/api/auth/*`, reading `/api/app`
|
|
104
|
+
(renaming needs an admin) and the tables, which follow their `access`.
|
|
105
|
+
- A session is a row in Pattern's `sessions` table: signing out deletes it, and a copied cookie
|
|
106
|
+
stops working within 15 minutes.
|
|
107
|
+
- To test signed in, run `app sign-in [email]`. Flows and screenshots sign in as ada@acme.test by
|
|
108
|
+
default, and in flows the emailed code is always 123456.
|
|
109
|
+
- The app holds no Google secrets: Pattern's deploy gives it an `AUTH_SECRET`.
|