@jtakeit/astro 0.1.0

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 (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +59 -0
  3. package/bin/jtk.mjs +41 -0
  4. package/docs/booking.md +164 -0
  5. package/docs/catalogue.md +459 -0
  6. package/docs/collections.md +249 -0
  7. package/docs/css.md +86 -0
  8. package/docs/gallery.md +127 -0
  9. package/docs/hero-motion.md +189 -0
  10. package/docs/kit.md +454 -0
  11. package/docs/languages.md +182 -0
  12. package/docs/lead-form.md +109 -0
  13. package/docs/pages.md +193 -0
  14. package/docs/photos.md +314 -0
  15. package/docs/scaffold.md +75 -0
  16. package/docs/shapes.md +140 -0
  17. package/docs/surface.md +187 -0
  18. package/lib/catalogue.mjs +1678 -0
  19. package/lib/codes.mjs +171 -0
  20. package/lib/create.mjs +282 -0
  21. package/package.json +16 -0
  22. package/template/astro.config.mjs +84 -0
  23. package/template/figures.mjs +122 -0
  24. package/template/gitignore +16 -0
  25. package/template/jtakeit-meta.mjs +112 -0
  26. package/template/jtk/content/index.json +38 -0
  27. package/template/jtk/design.json +24 -0
  28. package/template/markdown.mjs +36 -0
  29. package/template/package-lock.json +5320 -0
  30. package/template/package.json +26 -0
  31. package/template/specimens.mjs +46 -0
  32. package/template/src/components/Blocks.astro +151 -0
  33. package/template/src/components/BookingForm.astro +506 -0
  34. package/template/src/components/Clip.astro +155 -0
  35. package/template/src/components/Hero.astro +66 -0
  36. package/template/src/components/LeadForm.astro +347 -0
  37. package/template/src/components/OpeningHours.astro +69 -0
  38. package/template/src/components/Pile.astro +185 -0
  39. package/template/src/components/Shot.astro +472 -0
  40. package/template/src/components/gallery/Gallery.astro +381 -0
  41. package/template/src/components/gallery/galleries.ts +139 -0
  42. package/template/src/components/motion/HeroField.astro +520 -0
  43. package/template/src/components/motion/fields.ts +430 -0
  44. package/template/src/components/surface/Pattern.astro +278 -0
  45. package/template/src/components/surface/patterns.ts +187 -0
  46. package/template/src/content/blocks.ts +758 -0
  47. package/template/src/content.config.ts +19 -0
  48. package/template/src/copy/LOCALE.ts +324 -0
  49. package/template/src/data/site.ts +137 -0
  50. package/template/src/layouts/Layout.astro +282 -0
  51. package/template/src/lib/alive.ts +49 -0
  52. package/template/src/lib/entries.ts +106 -0
  53. package/template/src/lib/entryLoader.ts +315 -0
  54. package/template/src/lib/noise.ts +26 -0
  55. package/template/src/lib/page.ts +287 -0
  56. package/template/src/lib/photos.ts +168 -0
  57. package/template/src/lib/under.ts +32 -0
  58. package/template/src/lib/uploads.ts +85 -0
  59. package/template/src/pages/[...entry].astro +207 -0
  60. package/template/src/pages/[...feed].xml.ts +64 -0
  61. package/template/src/pages/index.astro +90 -0
  62. package/template/src/pages/llms.txt.ts +50 -0
  63. package/template/src/pages/privacy.astro +59 -0
  64. package/template/src/pages/robots.txt.ts +21 -0
  65. package/template/src/pages/sitemap.xml.ts +50 -0
  66. package/template/src/styles/global.css +411 -0
  67. package/template/src/styles/surface.css +375 -0
  68. package/template/tsconfig.json +5 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 jtakeit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @jtakeit/astro
2
+
3
+ The scaffold and the catalogue tool for a site on [jtakeit](https://jtakeit.com) —
4
+ the platform's contract as a working Astro project, ready to build on.
5
+
6
+ jtakeit is an agent-driven platform for building and running websites for clients:
7
+ the site is written by an agent in a repository, and the platform runs the panel the
8
+ business owner edits it in, the enquiries and the bookings, the backups and the
9
+ domain. What the platform holds a repository to is small — a `jtk/catalogue.json`
10
+ that validates, a `data-jtk-path` on every editable field, a `dist/` from an Astro
11
+ build — and this package is that contract with a page already on it.
12
+
13
+ Astro is the only generator the platform builds today.
14
+
15
+ ## Scaffold
16
+
17
+ ```
18
+ npx @jtakeit/astro create <slug> --name "The Business" --locale de-CH
19
+ cd <slug> && npm ci && npm run dev
20
+ ```
21
+
22
+ You get a static Astro project: a layout, a hero, a lead form posting to `/api/lead`
23
+ (the platform serves it), a booking form for the platform's diary, opening hours read
24
+ from `jtk/bookings.json`, pages for robots, sitemap and llms.txt, and
25
+ `src/content/blocks.ts` — the declaration of what the owner may edit. Nothing in it
26
+ decides how the site looks; every component is yours to replace.
27
+
28
+ ## The catalogue
29
+
30
+ ```
31
+ npx @jtakeit/astro catalogue # write jtk/catalogue.json, build, check it against dist/
32
+ npx @jtakeit/astro catalogue --emit-only # write it and stop
33
+ ```
34
+
35
+ `jtk/catalogue.json` is derived from `src/content/blocks.ts` and checked against the
36
+ built pages in both directions: every declared field is rendered with its path, and
37
+ every annotated path is declared. The platform's own validator is the authority —
38
+ `validate_catalogue` over MCP, or `--judge` here with `JTK_API` and `JTK_TOKEN` set —
39
+ and this tool sits its exam.
40
+
41
+ ## Then, on the platform
42
+
43
+ 1. Push the repository somewhere the platform's GitHub App can reach.
44
+ 2. `create_site`, then `attach_repo`.
45
+ 3. `trigger_build`; `import_branch` after committing content; `publish`.
46
+ 4. `get_preview_link` to look; the studio hands the owner's link to the client from
47
+ the panel.
48
+
49
+ The whole of it, with the reasons, is in [docs/](docs/): `kit.md` first, then
50
+ `catalogue.md`, `pages.md`, `collections.md`, `lead-form.md`, `booking.md`,
51
+ `photos.md`, and the rest as you need them. The platform's own reference —
52
+ field kinds, modules, every error code — is what the MCP server hands an agent on
53
+ `initialize`, and at `https://docs.jtakeit.com`.
54
+
55
+ ## Not here, on purpose
56
+
57
+ No deploy: the platform builds and serves. No server code: the form and the diary
58
+ are the platform's endpoints beside the site. No design system: what the site looks
59
+ like is the point of writing it.
package/bin/jtk.mjs ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * jtk — the command line of @jtakeit/astro.
4
+ *
5
+ * jtk create <slug> … scaffold a site (lib/create.mjs)
6
+ * jtk catalogue … write jtk/catalogue.json and check it (lib/catalogue.mjs)
7
+ */
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ import { pathToFileURL } from 'node:url';
11
+
12
+ const HERE = dirname(fileURLToPath(import.meta.url));
13
+ const [command, ...rest] = process.argv.slice(2);
14
+
15
+ switch (command) {
16
+ case 'create': {
17
+ const { create } = await import(pathToFileURL(join(HERE, '..', 'lib', 'create.mjs')).href);
18
+ await create(rest);
19
+ break;
20
+ }
21
+ case 'catalogue':
22
+ case 'catalog': {
23
+ process.argv = [process.argv[0], process.argv[1], ...rest];
24
+ await import(pathToFileURL(join(HERE, '..', 'lib', 'catalogue.mjs')).href);
25
+ break;
26
+ }
27
+ default: {
28
+ const { usage } = await import(pathToFileURL(join(HERE, '..', 'lib', 'create.mjs')).href);
29
+ console.error(`@jtakeit/astro — the scaffold and the catalogue tool for a site on jtakeit
30
+
31
+ jtk create <slug> [options] scaffold a site into ./<slug>
32
+ jtk catalogue [--emit-only] [--dist ./dist] [--judge]
33
+ write jtk/catalogue.json from src/content/blocks.ts and
34
+ check it against the built pages
35
+
36
+ ${usage()}
37
+
38
+ The contract the platform holds this repository to is in docs/ of this package.`);
39
+ process.exit(command === undefined || command === '--help' || command === '-h' ? 0 : 1);
40
+ }
41
+ }
@@ -0,0 +1,164 @@
1
+ # The booking form
2
+
3
+ The diary is the platform's; the page draws it. Six addresses on the site's
4
+ own host answer everything a visitor needs, and nothing about availability is
5
+ computed in the repository — which is what lets the owner change their hours
6
+ in the admin and have the site offer them the next minute, with no rebuild.
7
+
8
+ Template: `src/components/BookingForm.astro`. Copy it,
9
+ keep the script, restyle the markup. The contract in full is
10
+ [bookings-on-the-site](https://jtakeit.com/docs/guides/bookings-on-the-site)
11
+ in the platform's docs.
12
+
13
+ ## Turning it on
14
+
15
+ The site's catalogue declares the module and binds two collections:
16
+
17
+ ```jsonc
18
+ "modules": {
19
+ "bookings": {
20
+ "services": "services", // entries with `takes` (minutes) and, to be payable, `costs`
21
+ "resources": "masters" // entries that get occupied; one entry is a solo business
22
+ }
23
+ }
24
+ ```
25
+
26
+ Both are ordinary collections the owner edits in the admin — the team page and
27
+ the diary's masters are the **same entries**. A service entry carries `takes`
28
+ as a `duration` field; a master entry may carry its own weekly `hours`. The
29
+ business's hours, days off and confirmation mode are the module's settings
30
+ document, which the owner edits under the diary; the repository never holds
31
+ them.
32
+
33
+ ## The page
34
+
35
+ ```astro
36
+ ---
37
+ import BookingForm from '../components/BookingForm.astro';
38
+ import { entries } from '../lib/entries';
39
+ const services = entries('services').map((e) => ({
40
+ slug: e.slug, title: e.title,
41
+ takes: e.block.takes, costs: e.block.costs, group: e.block.group, // for the checklist's total and headings
42
+ }));
43
+ const masters = entries('masters').map((e) => ({ slug: e.slug, title: e.title }));
44
+ const combine = bookings.blocks[0].combine === true; // jtk/bookings.json, the owner's setting
45
+ ```
46
+
47
+ And `jtk/bookings.json` says **where the form is** — `"page": "/angebote"` in its
48
+ block — because the form is placed by a page template rather than by a block, so
49
+ nothing else can tell the panel which page to open when the owner presses *Try
50
+ booking*. Empty means the front page.
51
+
52
+ ```ts
53
+ ---
54
+ <BookingForm services={services} resources={masters} locale="de" combine={combine} currency="CHF" />
55
+ ```
56
+
57
+ With `combine` on — the module's setting, the owner's to flip — the services
58
+ are a checklist under their `group` headings with a running total of minutes
59
+ and money, and the visit is booked as one: the same person, the sum of the
60
+ lengths, the sum of the prices. Off, the form is one choice, as it always was.
61
+ Only services with a `takes` belong on the form; a price-list row without one
62
+ is refused by the platform, so filter before passing.
63
+
64
+ Pass `resources` only when the visitor should choose; leave it out for a solo
65
+ business and the platform assigns. The words are the component's, per locale;
66
+ the page's own copy stays in `jtk/content`.
67
+
68
+ ## Its look is the site's to give
69
+
70
+ The component is a reference for the flow, not a design. It ships with
71
+ class names — `field`, `field__input`, `booking__slot`, `booking__submit` —
72
+ and a minimal `<style>` that makes it legible and nothing more. **Restyle
73
+ it before it goes out**, the way you restyle everything else the scaffold
74
+ ships: the site's own field styles, the site's own buttons for the time
75
+ slots and the press, the site's panel around it. A booking form that looks
76
+ like a browser default on a page that does not is the first thing the
77
+ studio sees. Keep the `data-*` hooks and the input `name`s: the script is
78
+ wired to them.
79
+
80
+ ## What the component does, in order
81
+
82
+ 1. `GET /api/turnstile` — the widget's key, or `""`. With a key it renders the
83
+ Turnstile widget and sends the token as `turnstile`; without one it renders
84
+ nothing. **The site carries no key**: which widget covers a host is the
85
+ platform's decision, and a client's own domain is put into one the moment it
86
+ is attached.
87
+ 2. `GET /api/availability?service=&from=&to=[&resource=]` — the free starts
88
+ for one day, as UTC instants, with the business's `zone` beside them for
89
+ the clock. `service` repeats for a visit of several; the platform sums the
90
+ lengths and offers only the resources that do all of them.
91
+ 3. `POST /api/book` — the visitor's choice, with `services: [...]` for a visit
92
+ of several (`service` alone is the one-service form). `409` means the time went while
93
+ the page was open, and the component reloads the day. The answer carries
94
+ `state` (*confirmed* or *pending*), `manage_path` (the visitor's own page:
95
+ cancel, move, add to calendar) and `telegram_url` (reminders).
96
+ 4. `POST /api/pay` — only when `amount_minor` is not zero. The slot is held
97
+ thirty minutes; the platform's Checkout page comes back to `manage_path`.
98
+
99
+ ## The band it sits in
100
+
101
+ The same rule as the lead form: give it something to stand beside — the price
102
+ list, the opening line, the master's photograph — or let it use the width.
103
+ `fl-check` fails a form band that uses less than 45% of a 1920px screen.
104
+
105
+ ## In the preview
106
+
107
+ The form works on the studio's preview exactly as on the live site: the
108
+ platform answers `/p/<slug>/api/…` behind the preview's session, against the
109
+ same demo. A client can book a test appointment in their own diary before they
110
+ have paid, which is the sale.
111
+
112
+ `manage_path` already carries the prefix on a preview — `/p/<slug>/_booking/…`
113
+ — and the visitor's page is served there, so the link is used as it arrives.
114
+ A "received" page of your own writes every address through `under()`, the
115
+ `/_booking/<token>` fallback included: `href="/angebote/"` on it is the one
116
+ link a visitor presses right after booking, and on a preview it led to "no
117
+ site is attached to this address".
118
+
119
+ ## A record, not a post — and every fact once
120
+
121
+ What the module reads is a record the diary occupies or offers, and the panel
122
+ draws it as a form. Shape the two entry types for that, not for the blog:
123
+
124
+ - **A service** is short fields — `title`, `takes`, `costs`, a `group` if the
125
+ price list has headings, one line — with **no `body`** and **`pages: false`**
126
+ on the collection. It is a row of the price list the form reads, not a page.
127
+ - **A resource** — a master, a chair, a table, a room — is a `title` with its
128
+ own `hours`, what it `does`, `seats` or `spots`, a line and a photograph if
129
+ the page wants them. A master may have a page and a paragraph about her; a
130
+ billiard table with an excerpt, a cover and eight thousand characters of
131
+ body is the blog's shape copied without a reason.
132
+
133
+ And a fact lives once. The owner's opening hours are the module's settings,
134
+ which publish writes to `jtk/bookings.json`; the page reads them from there
135
+ (`OpeningHours.astro` below). The prices are the services' `costs`, and the
136
+ price list on the page is the same entries the form reads. **Never type hours
137
+ or prices again as text** on a page block: the copy goes stale the day the
138
+ owner changes the panel's.
139
+
140
+ `validate_catalogue` says both as advice — `JTK_W_MODULE_POST`,
141
+ `JTK_W_MODULE_TWIN` — under `advice`, refusing nothing; `jtk catalogue --judge`
142
+ prints them. Act on them before the first build.
143
+
144
+ ### Opening hours from the module
145
+
146
+ ```astro
147
+ ---
148
+ import OpeningHours from '../components/OpeningHours.astro';
149
+ ---
150
+ <OpeningHours locale="de" />
151
+ ```
152
+
153
+ `src/components/OpeningHours.astro` reads `jtk/bookings.json`
154
+ if it is there — a repository nobody has published yet has none, and the
155
+ component draws nothing rather than failing the build — and lists the week in
156
+ the site's language, two rows for a day with a break. Restyle it like the rest.
157
+
158
+ ## What not to do
159
+
160
+ - Do not compute free times on the page from the hours in `jtk/content` — the
161
+ diary's bookings live in the platform and the page cannot see them.
162
+ - Do not put a Turnstile key in the repository.
163
+ - Do not draw your own "your booking" page unless the design needs it; the
164
+ platform's at `manage_path` is the floor and works from day one.