@orbytes/astrolab 0.3.0 → 0.4.0-next.2
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 +261 -116
- package/bin/pin-gallery.mjs +53 -19
- package/defaults.mjs +72 -20
- package/dist/core/virtual-module/virtual-routes.js +12 -1
- package/docs/PIN-CONTRACT.md +84 -10
- package/docs/PIN.md +117 -37
- package/index.d.ts +41 -23
- package/index.mjs +46 -91
- package/package.json +7 -3
- package/src/Home.astro +166 -264
- package/src/LabHead.astro +37 -1047
- package/src/chrome/ActionsMenu.astro +97 -0
- package/src/chrome/ComponentCard.astro +76 -0
- package/src/chrome/Icon.astro +21 -0
- package/src/chrome/LICENSE-icons +43 -0
- package/src/chrome/Nav.astro +131 -0
- package/src/chrome/Panel.astro +117 -0
- package/src/chrome/Properties.astro +104 -0
- package/src/chrome/SectionsTree.astro +128 -0
- package/src/chrome/Shell.astro +120 -0
- package/src/chrome/Sprite.astro +23 -0
- package/src/chrome/StoryView.astro +192 -0
- package/src/chrome/Tree.astro +86 -0
- package/src/chrome/ViewportControls.astro +173 -0
- package/src/chrome/ViewportStage.astro +55 -0
- package/src/chrome/fonts/OFL.txt +93 -0
- package/src/chrome/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/src/chrome/icons.ts +68 -0
- package/src/chrome/marks-client.ts +75 -0
- package/src/chrome/model.ts +156 -0
- package/src/chrome/navbar-client.ts +324 -0
- package/src/chrome/params-client.ts +434 -0
- package/src/chrome/pins-data.ts +63 -0
- package/src/chrome/shell-client.ts +468 -0
- package/src/chrome/site-data.ts +230 -0
- package/src/chrome/trees.ts +257 -0
- package/src/chrome/viewport-client.ts +405 -0
- package/src/chrome/views/Assets.astro +125 -0
- package/src/chrome/views/Pages.astro +214 -0
- package/src/chrome/views/Placeholder.astro +37 -0
- package/src/chrome/views/Tasks.astro +79 -0
- package/src/core/LICENSE-astrobook +21 -0
- package/src/core/lib/components/home.astro +4 -2
- package/src/core/lib/pages/story.astro +12 -10
- package/src/core/utils/kebab-case.ts +2 -2
- package/src/core/virtual-module/virtual-routes.ts +20 -4
- package/src/pin/board.mjs +414 -190
- package/src/pin/index.mjs +67 -22
- package/src/pin/tickets.mjs +6 -5
- package/src/pin/toolbar.js +82 -4
- package/src/shell/Browse.astro +131 -353
- package/src/shell/Viewport.astro +22 -1315
- package/src/shell/lab-index.ts +28 -18
- package/src/shell/lab-params.ts +113 -6
- package/src/shell/live-files.mjs +212 -10
- package/src/shell/marks.mjs +17 -41
- package/src/ui/components/app.astro +5 -7
- package/src/ui/components/preview-layout.astro +17 -0
- package/src/ui/components/theme-script.astro +17 -5
- package/src/ui/lab.css +3754 -371
- package/virtual.d.ts +13 -4
- package/bin/lab-cull.mjs +0 -401
- package/src/shell/CardGrid.astro +0 -297
- package/src/ui/components/build-path.ts +0 -13
- package/src/ui/components/build-tree.ts +0 -108
- package/src/ui/components/collapse-duration.ts +0 -28
- package/src/ui/components/compress-terms.ts +0 -10
- package/src/ui/components/dashboard-layout.astro +0 -39
- package/src/ui/components/home.astro +0 -65
- package/src/ui/components/layout.astro +0 -110
- package/src/ui/components/sidebar-button-fullscreen.astro +0 -38
- package/src/ui/components/sidebar-button-search.astro +0 -23
- package/src/ui/components/sidebar-button-theme.astro +0 -9
- package/src/ui/components/sidebar-button.astro +0 -24
- package/src/ui/components/sidebar-resize-handle.astro +0 -74
- package/src/ui/components/sidebar-search-panel.astro +0 -41
- package/src/ui/components/sidebar-search-script.ts +0 -103
- package/src/ui/components/sidebar-title.astro +0 -17
- package/src/ui/components/sidebar-tree-node.astro +0 -143
- package/src/ui/components/sidebar-tree.astro +0 -84
- package/src/ui/components/sidebar.astro +0 -29
- package/src/ui/components/theme-toggle.astro +0 -63
package/docs/PIN.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# The pin board
|
|
2
2
|
|
|
3
3
|
Click a rendered element on `astro dev`, leave a comment, and get a markdown ticket plus a PNG on
|
|
4
|
-
local disk for a Claude Code agent to pick up
|
|
5
|
-
|
|
4
|
+
local disk for a Claude Code agent to pick up — from the lab's navbar (**Comment**, on the page it is
|
|
5
|
+
previewing) or from Astro's dev toolbar on the site's own pages. Then work the tickets on a kanban
|
|
6
|
+
at `/lab/tasks`, in the lab, on the same dev server. No cloud, no API key, no MCP, no second
|
|
7
|
+
process. It needs the site to be inside a git repository (› *Wiring*).
|
|
6
8
|
|
|
7
9
|
It is for a **solo pass** — your own review of a site in dev, before anyone else sees it. It does
|
|
8
10
|
not sync with whatever tool you use for review rounds with other people, and is not meant to
|
|
@@ -24,7 +26,7 @@ own port (`backlog browser`, 6420). That is the whole reason it is gone — the
|
|
|
24
26
|
|
|
25
27
|
| Stop | Do instead |
|
|
26
28
|
|---|---|
|
|
27
|
-
| `backlog browser` | `npm run dev`, then `/
|
|
29
|
+
| `backlog browser` | `npm run dev`, then `/lab/tasks` |
|
|
28
30
|
| `backlog task edit <n> …` on a pin ticket | drag the card, or use the status menu in its detail panel |
|
|
29
31
|
|
|
30
32
|
`backlog task edit` is the original defect, not an alternative. Any write through backlog.md
|
|
@@ -33,7 +35,7 @@ pin ticket that erases `dispatch`, `source`, `selector`, `rect`, `shot` and `out
|
|
|
33
35
|
why the pin fields live in a fenced block in the body, and why `/pin` was read-only until
|
|
34
36
|
2026-09-22:
|
|
35
37
|
a second writer of these files would have rebuilt the bug. With backlog.md out of the loop there
|
|
36
|
-
is no second writer, so
|
|
38
|
+
is no second writer, so the board writes them correctly by construction (› *The write path*).
|
|
37
39
|
|
|
38
40
|
If backlog.md is still installed globally, this does not uninstall it.
|
|
39
41
|
A `backlog/config.yml` left behind in a repo that used backlog.md is a record and nothing reads it
|
|
@@ -78,20 +80,34 @@ isCancelled(t.status); // the archive test
|
|
|
78
80
|
|
|
79
81
|
`isOpen()` is *not `Resolved` and not `Cancelled`* — one definition, shared by this board's chips
|
|
80
82
|
and the in-page panel's Open tab, so the two cannot disagree by one ticket. `Cancelled` appears in
|
|
81
|
-
neither tab: it is reachable on
|
|
82
|
-
|
|
83
|
-
## The board at `/
|
|
83
|
+
neither tab: it is reachable on the board only.
|
|
84
|
+
|
|
85
|
+
## The board at `/lab/tasks`
|
|
86
|
+
|
|
87
|
+
**In the lab since 2026-09-24** (decided that day: one URL, everything under `/lab`). The board is
|
|
88
|
+
two views in the lab's own chrome — **Kanban Board** at `/lab/tasks` and **All Tasks**, a sortable
|
|
89
|
+
table of every ticket, newest first, at `/lab/tasks/all` — with no second-level panel beside them
|
|
90
|
+
(decided 2026-09-24: the board's own status chips already filter, so a status list beside it only
|
|
91
|
+
took width from the columns); `/lab/tasks?status=Ready%20for%20review` opens the board filtered to
|
|
92
|
+
one column.
|
|
93
|
+
`/pin`, the board's address until then, redirects there. Both views are Astro pages the pin half
|
|
94
|
+
injects in `astro dev` only, rendered from the same markup, stylesheet and script as the standalone
|
|
95
|
+
file (`boardParts` in [`src/pin/board.mjs`](../src/pin/board.mjs)); the stylesheet is scoped with
|
|
96
|
+
`@scope (.pin-board)` so it cannot restyle the chrome around it, and it takes the lab's palette and
|
|
97
|
+
dark mode inside the lab.
|
|
84
98
|
|
|
85
99
|
Six columns in the order above, read fresh from `backlog/tasks/` on every request. No build step,
|
|
86
|
-
no regeneration; a refresh is the reload.
|
|
100
|
+
no regeneration; a refresh is the reload. In the lab the columns keep a fixed width and the row
|
|
101
|
+
scrolls sideways, rather than wrapping into a stack beside two sidebars.
|
|
87
102
|
|
|
88
103
|
- **Ready for review is drawn to be seen**, because it is the human review queue and the whole
|
|
89
104
|
reason the model changed — violet rule, violet dot, *waiting on you* under the name, a tinted panel
|
|
90
105
|
with a filled count **only when it actually holds something**. An empty queue stays quiet; one
|
|
91
106
|
that shouts at nothing teaches you to stop looking at it. Its count also leads the top bar.
|
|
92
107
|
- **Cancelled is drawn to be ignored**: a narrow dashed rail rather than a sixth of the wall, its
|
|
93
|
-
cards desaturated until hovered. Still a real drop target
|
|
94
|
-
ticket
|
|
108
|
+
cards desaturated until hovered. Still a real drop target. This rail, the All Tasks table (which
|
|
109
|
+
lists every ticket) and the gallery file are the only places a cancelled ticket can be seen at
|
|
110
|
+
all — an archive nobody can open is a delete.
|
|
95
111
|
- Every column carries a one-line gloss under its name (*agents start here*, *waiting on you*),
|
|
96
112
|
because six nouns with no explanation is a vocabulary you have to be told once and remember.
|
|
97
113
|
- **A card** carries its screenshot, id, title, dispatch mode, priority and source file, plus the
|
|
@@ -110,27 +126,51 @@ printed `0 ticket(s)` until 2026-09-22). It renders from the same module and is
|
|
|
110
126
|
board is writable exactly when `renderBoard` is given an endpoint, and a `file://` page has nothing
|
|
111
127
|
listening behind it.
|
|
112
128
|
|
|
113
|
-
**The board's corner links** are an option (`links`), not the hardcoded `/` and `/lab`
|
|
114
|
-
until 2026-09-22.
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
**The standalone board's corner links** are an option (`links`), not the hardcoded `/` and `/lab`
|
|
130
|
+
they were until 2026-09-22. Inside the lab there are none — the lab's own menu is right there.
|
|
131
|
+
|
|
132
|
+
## Pinning from inside the lab
|
|
133
|
+
|
|
134
|
+
Every preview in the lab is an `<iframe>`, and Astro's dev toolbar is hidden inside the lab's
|
|
135
|
+
frames, so a lab page has one toolbar — its own — and one pin app. The lab drives that app rather
|
|
136
|
+
than carrying a second picker:
|
|
137
|
+
|
|
138
|
+
- **Comment**, in the navbar of a component page and of a page view, starts a pick confined to the
|
|
139
|
+
canvas: only the framed page can be picked, never the lab's chrome around it. Escape stops the
|
|
140
|
+
pick and turns Comment off; a comment already typed into the composer is kept, never discarded.
|
|
141
|
+
The composer that follows is the pin app's own window on the dev toolbar. The button is disabled
|
|
142
|
+
until the pin app has started, which it does when the dev toolbar is idle.
|
|
143
|
+
- **The pins** already left on the framed page are drawn over the frame at their elements: blue
|
|
144
|
+
while open, grey once resolved, numbered in the order they were made, cancelled ones never. A
|
|
145
|
+
ticket whose selector finds nothing on that page, or more than one thing, draws no pin rather
|
|
146
|
+
than a guessed one. A pin, and the pin row in the component's Properties panel, open All Tasks.
|
|
147
|
+
- **⋯ › Pins** holds two switches, Show pins (on) and Show resolved (off), remembered per browser
|
|
148
|
+
in `localStorage` (`lab-pins-show`, `lab-pins-resolved`).
|
|
149
|
+
|
|
150
|
+
The list of pins is the pin app's own, re-read after every write, so a new pin appears as soon as
|
|
151
|
+
its ticket lands. How the lab and the app talk is in [the contract](./PIN-CONTRACT.md) › *The lab's
|
|
152
|
+
channel*. On the site's own pages, outside `/lab`, the dev toolbar's pin app works exactly as it
|
|
153
|
+
always did.
|
|
117
154
|
|
|
118
155
|
## The write path
|
|
119
156
|
|
|
120
157
|
One endpoint, `POST <route>/api/ticket`, registered in `astro:server:setup` — which does not exist
|
|
121
158
|
in a build, so it cannot reach `dist/`.
|
|
122
159
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
the
|
|
126
|
-
|
|
127
|
-
that
|
|
160
|
+
**The endpoint, the screenshots and the `/pin` redirect are middleware**, installed ahead of
|
|
161
|
+
Astro's own request handler, so they shadow a host page at the same path — in dev only. Through
|
|
162
|
+
`orbytesLab()` the route is `<subpath>/tasks`, which no site page is likely to be; the one exposed
|
|
163
|
+
address is `/pin`, which redirects. Setting `route` does not free it: inside the lab `/pin`
|
|
164
|
+
redirects to `route` whatever that is, and a `route` of `/pin` puts the Tasks views there instead,
|
|
165
|
+
so no `route` leaves a site's own `/pin` page alone in dev — `pin: false` does. On its own,
|
|
166
|
+
without the lab, the pin half still serves its standalone board at `route`, default `/pin`, and
|
|
167
|
+
there `pin: { route: "/__pin" }` moves it off a site page.
|
|
128
168
|
|
|
129
169
|
```json
|
|
130
170
|
{ "id": "PIN-004", "status": "Ready for review", "expect": { "status": "In Progress" } }
|
|
131
171
|
```
|
|
132
172
|
|
|
133
|
-
`updateTicket` (`src/board.mjs`) rewrites **only** the frontmatter lines it is changing, plus
|
|
173
|
+
`updateTicket` (`src/pin/board.mjs`) rewrites **only** the frontmatter lines it is changing, plus
|
|
134
174
|
`updated_date`. It never serialises: the file is split by index, and the slice from the closing
|
|
135
175
|
`---` onward — the comment, the image and the fenced ```yaml pin block — is the same substring on
|
|
136
176
|
the way out as on the way in. That identity is asserted on every write, and the write is a temp
|
|
@@ -172,7 +212,7 @@ Nothing is wired separately: the board comes with the lab, through the one integ
|
|
|
172
212
|
already configures.
|
|
173
213
|
|
|
174
214
|
```js
|
|
175
|
-
import orbytesLab from "
|
|
215
|
+
import orbytesLab from "@orbytes/astrolab";
|
|
176
216
|
|
|
177
217
|
const includeLab =
|
|
178
218
|
process.argv.includes("dev") || process.env.PUBLIC_DEPLOY_ENV === "staging";
|
|
@@ -188,6 +228,15 @@ export default defineConfig({
|
|
|
188
228
|
gates itself on `command === "dev"`, so including the lab in a staging build registers it and it
|
|
189
229
|
does nothing there.
|
|
190
230
|
|
|
231
|
+
**The site must be inside a git repository.** Every path in a ticket is repo-relative, and the root
|
|
232
|
+
is the Astro project's folder or the nearest one above it that holds `.git`. With none, the board
|
|
233
|
+
stands down for that run instead of taking the dev server with it (measured 2026-09-22: a freshly
|
|
234
|
+
scaffolded Astro project, which has no git yet, could not run `astro dev` at all before the board
|
|
235
|
+
learned to stand down). The only sign is a warning in the dev server's log. `<route>`,
|
|
236
|
+
`<route>/all` and `/pin` answer 404, the lab shows no Tasks group and no Comment button, and there
|
|
237
|
+
is no pin app on the dev toolbar and no source stamp. `git init` and a restart turn it on;
|
|
238
|
+
`pin: false` silences the warning.
|
|
239
|
+
|
|
191
240
|
`playwright` is an **optional** peer and is loaded by dynamic import inside the screenshotter, not
|
|
192
241
|
at module scope — a site without it loads its config, runs the lab and writes tickets without
|
|
193
242
|
pictures. Install it (`npm i -D playwright && npx playwright install chromium`) for the screenshots,
|
|
@@ -199,14 +248,14 @@ Pass these as `orbytesLab({ pin: { … } })`.
|
|
|
199
248
|
|
|
200
249
|
| Option | Default | What it does |
|
|
201
250
|
|---|---|---|
|
|
202
|
-
| `stamp` | `["src/lab/sections", "src/components"]` | **Site-relative** directories whose `.astro` files get `data-orbytes-src`. |
|
|
251
|
+
| `stamp` | `["src/lab/sections", "src/components"]` | **Site-relative** directories whose `.astro` files get `data-orbytes-src`. Replaces the default whole, and does not follow the lab's `directory`. `src/lab/components` and `src/lab/explorations` are not in it — see *The source attribute*. |
|
|
203
252
|
| `stampSkip` | `[]` | **Site-relative** `.astro` paths exempt from the stamp's hard failure. See below. A repo-relative path is accepted too — until 2026-09-22 that was the only spelling that worked, while `stamp` beside it was site-relative, so the exemption silently lapsed wherever the Astro project was not the repo root. |
|
|
204
253
|
| `backlogDir` | `"backlog"` | Repo-relative board directory: tickets in `tasks/`, PNGs in `assets/`. |
|
|
205
254
|
| `archiveDir` | `~/.orbytes/feedback-archive` | Canonical screenshot home; the repo copy is a hardlink into it. |
|
|
206
255
|
| `project` | the checkout's folder name | The folder under `archiveDir` this project's screenshots live in. Two clones of **different** repos under one folder name share an archive silently, one overwriting the other's `pin-007.png` — set it where that is possible. |
|
|
207
256
|
| `shots` | `true` | `false` writes tickets and takes no screenshots. |
|
|
208
|
-
| `route` | `"/pin"` | Where the board is served. `<route>/assets/*` serves its screenshots, `<route>/api/ticket` is the write endpoint
|
|
209
|
-
| `links` | the lab's resolved subpath | The board's corner links, `[{ href, label }]`. `[]` draws none. |
|
|
257
|
+
| `route` | `"<subpath>/tasks"` (`"/pin"` without the lab) | Where the board is served. `<route>/assets/*` serves its screenshots, `<route>/api/ticket` is the write endpoint; in the lab, `<route>` and `<route>/all` are the two views. Trailing slashes are stripped. The toolbar app reads the resolved value too (it fetches the ticket list from it and its **Board ↗** button opens it), through the virtual module below — until 2026-09-22 it carried a literal `/pin`, so setting this left the panel fetching a path nothing serves. |
|
|
258
|
+
| `links` | the lab's resolved subpath | The standalone board's corner links, `[{ href, label }]`. `[]` draws none. |
|
|
210
259
|
| `appId` / `appName` / `icon` | `"orbytes-pin"` / `"Pin"` / `"bug"` | The dev-toolbar app's identity. |
|
|
211
260
|
|
|
212
261
|
## What a ticket looks like
|
|
@@ -306,26 +355,33 @@ Two rules now keep a selector off that ground, both in
|
|
|
306
355
|
- **A class naming a runtime condition never enters a selector** (`isTransientClass`,
|
|
307
356
|
`extendsSiblingClass`) — `swiper-slide-active` is on slide 1 now and slide 2 after a scroll.
|
|
308
357
|
|
|
309
|
-
Neither is complete, and the field is what makes the remainder visible. `src/shot.mjs` already
|
|
358
|
+
Neither is complete, and the field is what makes the remainder visible. `src/pin/shot.mjs` already
|
|
310
359
|
loads the page **fresh in a real browser** to take the screenshot; before the shutter it asks that
|
|
311
360
|
page how many nodes the ticket's selector finds, and writes the answer back into the pin block:
|
|
312
361
|
|
|
313
362
|
| Value | Means |
|
|
314
363
|
|---|---|
|
|
315
364
|
| `1` | healthy — the selector names exactly this element on a fresh load |
|
|
316
|
-
| `0` | dead —
|
|
365
|
+
| `0` | dead — flagged *needs a human* on the board; a person works it from `source`, the screenshot and `outer_html` |
|
|
317
366
|
| `2`+ | ambiguous — the selector names several elements |
|
|
318
367
|
| `pending` | never tested — shots are off, or the screenshot failed |
|
|
319
368
|
|
|
320
369
|
Anything but `1` is said in the dev log and in the toolbar panel. A ticket whose selector died is
|
|
321
370
|
**still a good ticket** — it is labelled, never discarded. The defect was the silence.
|
|
322
371
|
|
|
372
|
+
On the board, `0` and `2`+ put a banner on the card, count towards *needing a human* in the
|
|
373
|
+
summary, and match the *Needs a human* filter. The banner's note for `0` says an agent cannot
|
|
374
|
+
locate the element: its selector cannot, and the rest of the ticket is intact for a person to work
|
|
375
|
+
from. **The flag is a label, not a gate.** It changes no status, and neither `isAgentReady()` nor
|
|
376
|
+
`listTickets()` reads `selector_matches` — a flagged ticket left in `Ready for agent` is still
|
|
377
|
+
handed to an agent. Only its status keeps an agent off it.
|
|
378
|
+
|
|
323
379
|
## Screenshots
|
|
324
380
|
|
|
325
381
|
One chromium for the whole dev session, launched lazily on the first pin and kept warm, closed
|
|
326
|
-
when the dev server closes.
|
|
327
|
-
|
|
328
|
-
|
|
382
|
+
when the dev server closes. What a shot costs has been measured three times, each timing a
|
|
383
|
+
different span; the figures, their dates and what each one timed are in
|
|
384
|
+
[the contract](./PIN-CONTRACT.md) › *Screenshots*.
|
|
329
385
|
|
|
330
386
|
You never wait for any of it. The ticket is written and `orbytes-pin:created` replied on the
|
|
331
387
|
same synchronous tick; the screenshot follows and sends `orbytes-pin:shot` when it lands. A ticket
|
|
@@ -349,6 +405,22 @@ file under `stamp`, and the picker walks up from the clicked node to the nearest
|
|
|
349
405
|
it. No ancestor found → `source` is `unresolved`, and the CSS selector plus `outer_html` still
|
|
350
406
|
make the ticket actionable.
|
|
351
407
|
|
|
408
|
+
**The default does not cover every tier.** It stamps `src/lab/sections` and `src/components`, so a
|
|
409
|
+
component kept in the lab's own `src/lab/components/` (or `src/lab/explorations/`) and pinned on its
|
|
410
|
+
own variant has no stamped ancestor, and its tickets say `unresolved`. Name the folder to include it
|
|
411
|
+
— `stamp` replaces the default, so restate what you keep:
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
orbytesLab({
|
|
415
|
+
pin: {
|
|
416
|
+
stamp: ["src/lab/sections", "src/lab/components", "src/components"],
|
|
417
|
+
},
|
|
418
|
+
})
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Widening `stamp` widens the hard failure below with it, so check the folder for templates with no
|
|
422
|
+
element to stamp first.
|
|
423
|
+
|
|
352
424
|
The attribute is inserted immediately after the tag name (`<section` → `<section
|
|
353
425
|
data-orbytes-src="…"`), so no attribute value, expression, spread or self-closing slash can be
|
|
354
426
|
mangled and no line number moves.
|
|
@@ -374,14 +446,17 @@ orbytesLab({
|
|
|
374
446
|
|
|
375
447
|
## The browser ↔ server channel
|
|
376
448
|
|
|
377
|
-
Astro's dev toolbar,
|
|
449
|
+
Creating a ticket goes over Astro's dev toolbar, on the existing Vite HMR socket — no HTTP endpoint,
|
|
450
|
+
no port, no CORS. Changing a ticket's status is the one HTTP write, from the board and from the
|
|
451
|
+
panel's own menus alike: `POST <route>/api/ticket`, same-origin, on the dev server already
|
|
452
|
+
running.
|
|
378
453
|
|
|
379
454
|
| Direction | Event | Payload |
|
|
380
455
|
|---|---|---|
|
|
381
456
|
| browser → server | `orbytes-pin:create` | `{ comment, dispatch, source, selector, url, viewport, scroll, rect, outerHTML }` |
|
|
382
457
|
| server → browser | `orbytes-pin:created` | `{ id, file, shot, error }` — all four keys, always |
|
|
383
458
|
| server → browser | `orbytes-pin:shot` | `{ id, shot, selectorMatches, error }` |
|
|
384
|
-
| board → server | `POST <route>/api/ticket` | `{ id, status?, priority?, expect? }` → `{ ok, id, status, priority, file, changed, card }` |
|
|
459
|
+
| board or panel → server | `POST <route>/api/ticket` | `{ id, status?, priority?, expect? }` → `{ ok, id, status, priority, file, changed, card }` |
|
|
385
460
|
|
|
386
461
|
On failure the reply is `{ id: null, file: null, shot: null, error: "why, in one plain sentence" }`.
|
|
387
462
|
|
|
@@ -410,10 +485,12 @@ window, so clicking it again focuses that tab instead of opening another.
|
|
|
410
485
|
|
|
411
486
|
1. `astro:config:setup` returns early unless the Astro command is `dev` — no plugin, no toolbar
|
|
412
487
|
app, no stamp.
|
|
413
|
-
2.
|
|
488
|
+
2. All three Vite plugins — the source stamp, the toolbar's config module and the one that closes
|
|
489
|
+
the browser — carry `apply: "serve"`, so they cannot load in a build even if they were
|
|
414
490
|
registered.
|
|
415
|
-
3. `astro:server:setup`, where every write lives — the ticket writer, the
|
|
416
|
-
|
|
491
|
+
3. `astro:server:setup`, where every write lives — the ticket writer, the screenshots and the
|
|
492
|
+
`<route>/api/ticket` endpoint — does not run in a build at all. The lab's two Tasks views are
|
|
493
|
+
injected past assertion 1, so they exist in `astro dev` and in no build.
|
|
417
494
|
4. That hook returns early anyway when `astro:config:setup` stood down, so a host that runs the
|
|
418
495
|
hooks in an order this package did not choose still gets nothing.
|
|
419
496
|
|
|
@@ -422,8 +499,11 @@ The first one matters more since the merge than it did before: the lab is delibe
|
|
|
422
499
|
in a build for the first time, and `command === "dev"` is what makes that a no-op.
|
|
423
500
|
|
|
424
501
|
Verified by running a real `npm run build` and grepping `dist/` for `orbytes-pin`, `data-orbytes-src`
|
|
425
|
-
and `/pin/api`: no hit.
|
|
502
|
+
and `/pin/api`: no hit. Re-checked 2026-09-24 on a staging build of the testbed, the case where the
|
|
503
|
+
pin half is registered: no hit for those three or for `/tasks/api`, and no `lab/tasks/` page.
|
|
426
504
|
|
|
427
505
|
No path this package emits into served HTML or into a ticket is absolute. The repo root is found
|
|
428
|
-
by walking up from the Astro config root to the first directory containing `.git`, and
|
|
429
|
-
rather than guess.
|
|
506
|
+
by walking up from the Astro config root to the first directory containing `.git`, and
|
|
507
|
+
`findRepoRoot` throws rather than guess. The integration catches that throw, warns, and stands down
|
|
508
|
+
for the run (› *Wiring*) — a library that guesses writes files where nobody asked, and an
|
|
509
|
+
integration that rethrows takes the dev server down.
|
package/index.d.ts
CHANGED
|
@@ -11,11 +11,6 @@ export interface LabTierOption {
|
|
|
11
11
|
* thumbnails and in the viewport configurator. Defaults to true for a tier named "sections".
|
|
12
12
|
*/
|
|
13
13
|
responsive?: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* This tier's stories may be marked for deletion in the sidebar and removed by
|
|
16
|
-
* `orbytes-lab-cull`. Defaults to true for a tier named "explorations".
|
|
17
|
-
*/
|
|
18
|
-
cullable?: boolean;
|
|
19
14
|
}
|
|
20
15
|
|
|
21
16
|
export interface OrbytesLabOptions {
|
|
@@ -23,14 +18,17 @@ export interface OrbytesLabOptions {
|
|
|
23
18
|
directory?: string;
|
|
24
19
|
/** Where the lab is served. Default `"/lab"`. */
|
|
25
20
|
subpath?: string;
|
|
26
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* The consumer's token/stylesheet files. They reach the PREVIEW only — the bare story each
|
|
23
|
+
* component page shows in its canvas — never the lab's own chrome. Default `[]`.
|
|
24
|
+
*/
|
|
27
25
|
css?: string[];
|
|
28
26
|
/**
|
|
29
|
-
* A path to the consumer's own head component, rendered inside
|
|
30
|
-
* anything else site-specific reach
|
|
27
|
+
* A path to the consumer's own head component, rendered inside every story preview's head — how
|
|
28
|
+
* fonts and anything else site-specific reach the previews. Relative to the project root.
|
|
31
29
|
*/
|
|
32
30
|
head?: string;
|
|
33
|
-
/** The
|
|
31
|
+
/** The site's name as the lab shows it (the tab title, the home page). Default `"Component lab"`. */
|
|
34
32
|
title?: string;
|
|
35
33
|
/**
|
|
36
34
|
* The deployed lab's base URL — the real minified build, which a dev server is not. Every card
|
|
@@ -49,17 +47,35 @@ export interface OrbytesLabOptions {
|
|
|
49
47
|
feedbucketKey?: string;
|
|
50
48
|
/**
|
|
51
49
|
* The tiers, in reading order. Default `["sections", "components", "explorations"]`; a tier
|
|
52
|
-
* named "sections" takes the responsive role
|
|
53
|
-
* the entry says otherwise.
|
|
50
|
+
* named "sections" takes the responsive role unless the entry says otherwise.
|
|
54
51
|
*/
|
|
55
52
|
tiers?: (string | LabTierOption)[];
|
|
53
|
+
/**
|
|
54
|
+
* The screen sizes the viewport offers. Every key is optional; an omitted one keeps the default
|
|
55
|
+
* (the orbytes scale).
|
|
56
|
+
*/
|
|
57
|
+
viewports?: {
|
|
58
|
+
/**
|
|
59
|
+
* The widths the design is drawn at — the navbar's device switch, in order. `icon` is one of
|
|
60
|
+
* `monitor`, `tablet`, `phone-landscape`, `phone`.
|
|
61
|
+
* @default 1440 desktop · 834 tablet · 550 phone landscape · 390 phone
|
|
62
|
+
*/
|
|
63
|
+
design?: { label: string; width: number; icon?: string }[];
|
|
64
|
+
/**
|
|
65
|
+
* The lower edge of each CSS band; the last is 0. The readout names the band the frame is in,
|
|
66
|
+
* and the presets offer both sides of every edge.
|
|
67
|
+
* @default desktop 992 · tablet 768 · phone landscape 480 · phone 0
|
|
68
|
+
*/
|
|
69
|
+
breakpoints?: { name: string; min: number }[];
|
|
70
|
+
/** Named sizes that set width AND height. @default iPhone 15, iPad, MacBook, … */
|
|
71
|
+
devices?: { label: string; width: number; height: number }[];
|
|
72
|
+
};
|
|
56
73
|
/**
|
|
57
74
|
* The pin board — the dev-only click-to-ticket feedback half of this package (`docs/PIN.md`).
|
|
58
75
|
*
|
|
59
|
-
* `true` or omitted ships it at
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* than guessed — pass your own `links` to override that.
|
|
76
|
+
* `true` or omitted ships it at `<subpath>/tasks` (the lab's Kanban Board and All Tasks views),
|
|
77
|
+
* with `/pin` redirecting there; `false` omits it entirely, for a site that wants the lab alone.
|
|
78
|
+
* An object is the pin integration's own options.
|
|
63
79
|
*
|
|
64
80
|
* @default true
|
|
65
81
|
*/
|
|
@@ -95,11 +111,12 @@ export interface OrbytesPinOptions {
|
|
|
95
111
|
shots?: boolean;
|
|
96
112
|
/**
|
|
97
113
|
* Where the board is served, dev only. `<route>/assets/*` serves its screenshots and
|
|
98
|
-
* `<route>/api/ticket` is the one write endpoint
|
|
114
|
+
* `<route>/api/ticket` is the one write endpoint; through `orbytesLab()`, `<route>` and
|
|
115
|
+
* `<route>/all` are the lab's Kanban Board and All Tasks views.
|
|
99
116
|
*
|
|
100
|
-
*
|
|
101
|
-
* the same path — in dev, silently.
|
|
102
|
-
* @default "/pin"
|
|
117
|
+
* Its asset and API paths are MIDDLEWARE, installed ahead of Astro's own request handler, so they
|
|
118
|
+
* shadow a host page at the same path — in dev, silently.
|
|
119
|
+
* @default "<subpath>/tasks" through orbytesLab(), "/pin" on its own
|
|
103
120
|
*/
|
|
104
121
|
route?: string;
|
|
105
122
|
/**
|
|
@@ -117,9 +134,10 @@ export interface OrbytesPinOptions {
|
|
|
117
134
|
}
|
|
118
135
|
|
|
119
136
|
/**
|
|
120
|
-
* The orbytes
|
|
121
|
-
* (`src/core/`, 0.13.3), the lab
|
|
122
|
-
* flattens the returned array, so one entry in `integrations` configures
|
|
137
|
+
* The orbytes lab AND the pin board: the Astrobook core vendored into this package
|
|
138
|
+
* (`src/core/`, 0.13.3), the lab's chrome around it, and the dev-only feedback board at
|
|
139
|
+
* `<subpath>/tasks`. Astro flattens the returned array, so one entry in `integrations` configures
|
|
140
|
+
* all three.
|
|
123
141
|
*
|
|
124
142
|
* `pin: false` returns the lab alone.
|
|
125
143
|
*/
|
|
@@ -206,7 +224,7 @@ export const OPEN_STATUSES: readonly PinStatus[];
|
|
|
206
224
|
export function normaliseStatus(status: unknown): string;
|
|
207
225
|
/** Open — neither accepted nor cancelled. `isOpen("Done")` is `false`. */
|
|
208
226
|
export function isOpen(status: unknown): boolean;
|
|
209
|
-
/** Archived. Every agent-facing read drops these;
|
|
227
|
+
/** Archived. Every agent-facing read drops these; the board is the one place they are visible. */
|
|
210
228
|
export function isCancelled(status: unknown): boolean;
|
|
211
229
|
/** May an agent START work on this ticket? Exactly one status says yes. */
|
|
212
230
|
export function isAgentReady(status: unknown): boolean;
|