@terreno/mcp 57.7.0 → 57.8.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.
- package/dist/docs/resources/ui.md +5 -4
- package/dist/docs/upgrades/57.8.0.md +61 -0
- package/dist/docs/versioned/next/explanation/example-coverage.md +1 -0
- package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +14 -0
- package/dist/docs/versioned/next/how-to/circleci.md +26 -11
- package/dist/docs/versioned/next/how-to/preview-demo.md +19 -0
- package/dist/docs/versioned/next/how-to/product-announcements.md +3 -0
- package/dist/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
- package/dist/docs/versioned/next/reference/ai.md +3 -1
- package/dist/docs/versioned/next/reference/announcement-release-packs.md +146 -0
- package/dist/docs/versioned/next/reference/announcements.md +7 -3
- package/dist/docs/versioned/next/reference/api.md +1 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +1 -0
- package/dist/docs/versioned/next/reference/ui.md +14 -6
- package/dist/docs/versioned/next/tasks/demo-workshop.md +150 -0
- package/package.json +2 -2
- package/src/docs/resources/ui.md +5 -4
- package/src/docs/upgrades/57.8.0.md +61 -0
- package/src/docs/versioned/next/explanation/example-coverage.md +1 -0
- package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +14 -0
- package/src/docs/versioned/next/how-to/circleci.md +26 -11
- package/src/docs/versioned/next/how-to/preview-demo.md +19 -0
- package/src/docs/versioned/next/how-to/product-announcements.md +3 -0
- package/src/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
- package/src/docs/versioned/next/reference/ai.md +3 -1
- package/src/docs/versioned/next/reference/announcement-release-packs.md +146 -0
- package/src/docs/versioned/next/reference/announcements.md +7 -3
- package/src/docs/versioned/next/reference/api.md +1 -0
- package/src/docs/versioned/next/reference/environment-variables.md +1 -0
- package/src/docs/versioned/next/reference/ui.md +14 -6
- package/src/docs/versioned/next/tasks/demo-workshop.md +150 -0
- package/dist/docs/ui-types-documentation.json +0 -33
- package/src/docs/ui-types-documentation.json +0 -33
|
@@ -253,7 +253,7 @@ radiusSm, radiusMd, radiusLg, radiusXl, radius2xl, radius3xl
|
|
|
253
253
|
## Utilities
|
|
254
254
|
|
|
255
255
|
```typescript
|
|
256
|
-
import { useStoredState, MediaQuery,
|
|
256
|
+
import { useStoredState, MediaQuery, isNarrowViewport } from "@terreno/ui";
|
|
257
257
|
|
|
258
258
|
// Persist state to storage
|
|
259
259
|
const [value, setValue] = useStoredState("key", defaultValue);
|
|
@@ -263,9 +263,10 @@ const [value, setValue] = useStoredState("key", defaultValue);
|
|
|
263
263
|
<DesktopLayout />
|
|
264
264
|
</MediaQuery>
|
|
265
265
|
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
// Current window is below the desktop breakpoint.
|
|
267
|
+
// isMobileDevice() is deprecated in favor of isNarrowViewport().
|
|
268
|
+
if (isNarrowViewport()) {
|
|
269
|
+
// Narrow-viewport code
|
|
269
270
|
}
|
|
270
271
|
```
|
|
271
272
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Upgrading to 57.8.0
|
|
2
|
+
|
|
3
|
+
**Action required:** No
|
|
4
|
+
**Affected packages:** @terreno/ui, @terreno/announcements
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
Bump every lockstep `@terreno/*` package together to `57.8.0`. `@terreno/ui` adds opt-in `SplitPage` web layouts and `IconButton` `backgroundOpacity`, and deprecates `isMobileDevice` in favor of `isNarrowViewport` (the old function still returns the same result). `@terreno/announcements` can import a Markdown release pack through `POST /announcements/import-release`. `DropdownPanel` anchors to its trigger, and `/gpt/prompt` emits each generated image once. No Expo upgrade is required if you are already on `57.x`.
|
|
9
|
+
|
|
10
|
+
## Breaking changes
|
|
11
|
+
|
|
12
|
+
None
|
|
13
|
+
|
|
14
|
+
## Deprecations
|
|
15
|
+
|
|
16
|
+
### `isMobileDevice`
|
|
17
|
+
|
|
18
|
+
**What changed**
|
|
19
|
+
|
|
20
|
+
`isMobileDevice` is deprecated. `isNarrowViewport` is the supported name. Both stay exported and return the same boolean: true when the window is below the supported desktop floor (web width under 1024, native width under 1024).
|
|
21
|
+
|
|
22
|
+
**Why**
|
|
23
|
+
|
|
24
|
+
`isMobileDevice` described a window-width check. The new name matches that behavior.
|
|
25
|
+
|
|
26
|
+
**Migration**
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// Before
|
|
30
|
+
import {isMobileDevice} from "@terreno/ui";
|
|
31
|
+
|
|
32
|
+
if (isMobileDevice()) {
|
|
33
|
+
// narrow layout
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// After
|
|
37
|
+
import {isNarrowViewport} from "@terreno/ui";
|
|
38
|
+
|
|
39
|
+
if (isNarrowViewport()) {
|
|
40
|
+
// narrow layout
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## New capabilities
|
|
45
|
+
|
|
46
|
+
`SplitPage` web props are opt-in. Existing callers keep the previous layout.
|
|
47
|
+
|
|
48
|
+
- `desktopChildrenMinWidth` keeps one or two desktop children at least that many pixels wide and scrolls the row when it is tighter. More than two children keeps the segmented control.
|
|
49
|
+
- `narrowViewportChildLabels`, `narrowViewportSelectionActive`, `narrowViewportSelectionKey`, and `narrowViewportListButtonLabel` replace the dotted narrow swiper with a labeled pager.
|
|
50
|
+
- `narrowBelowWidth` uses the narrow layout when the window width is less than or equal to that pixel value. Omit it to follow `isNarrowViewport()`.
|
|
51
|
+
- Native `SplitPage` ignores these props.
|
|
52
|
+
- `IconButton` `backgroundOpacity` tints only that button's background.
|
|
53
|
+
|
|
54
|
+
`@terreno/announcements` accepts a `terreno.announcement-pack/v1` directory (`pack.yaml` plus one Markdown file per announcement) at `POST /announcements/import-release`. Imports are idempotent by product, release version, channel, and slug, default to drafts, and publish immediately when `publish: true`. Admin auth or bearer `ANNOUNCEMENTS_UPLOAD_TOKEN` can call the route. The token is optional.
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
|
|
58
|
+
- `bun add` every lockstep `@terreno/*` dependency to `57.8.0`, then `bun run compile` at the app root
|
|
59
|
+
- Replace `isMobileDevice` imports with `isNarrowViewport` before a future major removes the alias
|
|
60
|
+
- Open a `DropdownPanel` whose trigger is not at the start of its row and confirm the panel opens on the trigger
|
|
61
|
+
- Import a draft announcement pack with `POST /announcements/import-release` and confirm a second import updates the same slug instead of inserting a duplicate
|
|
@@ -20,6 +20,7 @@ that file (and its imports) to a working example. Gaps stay explicit.
|
|
|
20
20
|
| Feature flags + live updates | yes — `FeatureFlagsApp` in `example-backend/src/server.ts`, seed `example-backend/src/scripts/seed-feature-flags.ts` | yes — `useTerrenoFeatureFlags` in `example-frontend/app/_layout.tsx` | Socket-backed live updates share the realtime connection. |
|
|
21
21
|
| Websockets / realtime | yes — `RealtimeApp` in `example-backend/src/server.ts`, `example-backend/src/websockets.ts` | yes — `useSocketConnection` in `example-frontend/app/_layout.tsx` | Sync deltas also use the socket (`SyncApp` + `@terreno/syncdb`). |
|
|
22
22
|
| Consent forms | yes — `example-backend/src/consentDefinitions.ts`, seed scripts | yes — `example-frontend/app/(tabs)/consents.tsx`, admin `example-frontend/app/admin/consent-forms/` | First-login flow is covered by e2e (`example-frontend/e2e/consents.spec.ts`). |
|
|
23
|
+
| Product announcements | yes — `AnnouncementsApp` in `example-backend/src/server.ts`, release upload token via `ANNOUNCEMENTS_UPLOAD_TOKEN`, seed `example-backend/src/scripts/seed-announcements.ts`, sample release pack `example-backend/announcements/releases/1.14.0/` with `bun run announcements:pack` | yes — `AnnouncementNavigator` in `example-frontend/app/_layout.tsx` and admin announcement routes | Release packs support multiple Markdown surfaces and idempotent draft or explicit live API import. |
|
|
23
24
|
| File upload / GCS | yes — `example-backend/src/api/settings.ts`, AI file routes in `example-backend/src/api/ai.ts` | yes — `example-frontend/app/(tabs)/files.tsx`, `example-frontend/app/gcs-settings.tsx` | |
|
|
24
25
|
| Better Auth | yes — `example-backend/src/utils/betterAuthConfig.ts`, `BetterAuthApp` in `example-backend/src/server.ts` | yes — `example-frontend/app/login.tsx`, `example-frontend/lib/betterAuth.ts` | Default `AUTH_PROVIDER` is `better-auth`. |
|
|
25
26
|
| syncdb local-first | yes — `SyncApp` in `example-backend/src/server.ts` | yes — `example-frontend/store/syncdb.ts`, `example-frontend/app/(tabs)/index.tsx` | PR #869 closed without merge; `@terreno/syncdb` shipped on the launch line and is the example todos path. |
|
|
@@ -101,7 +101,7 @@ HTTP method and scope map to a CRUD permission check:
|
|
|
101
101
|
**403** — denied after the instance document is loaded (e.g. `IsOwner` with a document that fails the check).
|
|
102
102
|
**401** — unauthenticated when a permission requires auth (from existing auth middleware).
|
|
103
103
|
|
|
104
|
-
`authenticateMiddleware` honors the router’s `allowAnonymous` option, same as CRUD.
|
|
104
|
+
`authenticateMiddleware` honors the router’s `allowAnonymous` option, same as CRUD. An action may set `allowAnonymous: true` to accept unauthenticated requests without making the router's CRUD routes or other actions anonymous. Keep a non-empty permission such as `Permissions.IsAny` and authenticate any service token in the handler.
|
|
105
105
|
|
|
106
106
|
## Validation
|
|
107
107
|
|
|
@@ -1257,6 +1257,20 @@ IP and tracking issue; this entry is the release checklist, not a plan.
|
|
|
1257
1257
|
|
|
1258
1258
|
---
|
|
1259
1259
|
|
|
1260
|
+
## demo-workshop
|
|
1261
|
+
|
|
1262
|
+
**Title:** `Demo workshop`
|
|
1263
|
+
|
|
1264
|
+
**Labels:** `area:dx`, `type:feature`
|
|
1265
|
+
**Project fields:** Area=`dx`, Target=`Next`, Impact=`Feature`, IP=`demo-workshop`, Status=`Planned`
|
|
1266
|
+
|
|
1267
|
+
Turn the Expo demo into the workshop for `@terreno/ui`: a person can find a component, copy a usage snippet, change theme and viewport from the URL, and see whether every stable story passes interaction, accessibility, and visual checks on web and Android.
|
|
1268
|
+
|
|
1269
|
+
- **Implementation plan:** [demo-workshop.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/implementationPlans/demo-workshop.md)
|
|
1270
|
+
- **Tasks:** [demo-workshop.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/tasks/demo-workshop.md)
|
|
1271
|
+
|
|
1272
|
+
---
|
|
1273
|
+
|
|
1260
1274
|
# Shipped, umbrella, and declined IPs
|
|
1261
1275
|
|
|
1262
1276
|
Tracking issues created for IPs that previously lacked a `**Roadmap issue:**` header.
|
|
@@ -351,20 +351,35 @@ a closed PR.
|
|
|
351
351
|
`manual-publish-package` also accepts `feature-flags`. Versions must be semver.
|
|
352
352
|
Semver git tags (`57.3.0`, `57.3.0-beta.1`) automatically start
|
|
353
353
|
`publish-release`; prereleases publish to their prerelease npm dist-tag.
|
|
354
|
-
`
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
`publish-release` compiles the 16 lockstep packages once with
|
|
355
|
+
`bun run --filter ... compile` (dependency order), then runs
|
|
356
|
+
`scripts/ci/publish-package.sh` for all of them in parallel with
|
|
357
|
+
`TERRENO_PUBLISH_PREBUILT=1`. It does not rerun tests: the tagged commit
|
|
358
|
+
already passed CI on master, and rerunning every suite per package made
|
|
359
|
+
releases take 20+ minutes. The job has no Mongo sidecar. Each publish writes
|
|
360
|
+
its own temporary npmrc (`NPM_CONFIG_USERCONFIG`) so parallel publishes do not
|
|
361
|
+
remove each other's token.
|
|
362
|
+
|
|
363
|
+
`publish-package.sh` pins `workspace:*` to the tag version for the tarball.
|
|
364
|
+
It must not `bun install` after that pin: sibling `@terreno/*` packages are
|
|
365
|
+
not on npm yet, so bun would look up `@terreno/test@X.Y.Z` (and similar) on
|
|
366
|
+
the registry and fail the whole job. Without `TERRENO_PUBLISH_PREBUILT` (the
|
|
367
|
+
manual single-package publish), it compiles the package and its workspace
|
|
368
|
+
dependencies and runs `test:ci` when that script exists, not `test`.
|
|
369
|
+
`@terreno/ui`'s `test` is `bun test --watch` and would hang. If a package's
|
|
370
|
+
tag version is already on npm, `publish-package.sh` skips it so a recut of
|
|
371
|
+
the same tag can finish the rest.
|
|
372
|
+
|
|
373
|
+
Only stable tags (`57.3.0`) run `deploy-demo`. It builds the demo from source,
|
|
374
|
+
so it runs alongside `publish-release` instead of waiting for it. Use
|
|
365
375
|
`{"run-demo-deploy":true}` on `master` if a prerelease must also refresh the
|
|
366
376
|
demo site.
|
|
367
377
|
|
|
378
|
+
`scripts/ci/netlify-deploy.sh` builds each site itself and calls
|
|
379
|
+
`netlify deploy --no-build`. Without `--no-build`, netlify-cli runs the root
|
|
380
|
+
`netlify.toml` docs build (wipe `node_modules`, reinstall, full Docusaurus),
|
|
381
|
+
which added about 3.5 minutes to every demo and frontend deploy.
|
|
382
|
+
|
|
368
383
|
CircleCI does not receive GitHub `pull_request.closed` events, so
|
|
369
384
|
`.github/workflows/preview-cleanup.yml` forwards them: it runs master's
|
|
370
385
|
`scripts/ci/trigger-circleci-pipeline.sh` with `{"run-preview-cleanup":"<PR>"}`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Preview a component demo
|
|
2
|
+
|
|
3
|
+
Open a component demo and change the shareable query. The preview bar is on demo and
|
|
4
|
+
dev component routes. `?embed=1` applies the same query and hides the bar.
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
/demo/Button?theme=dark&viewport=375&background=inverse&locale=en-US&rtl=1&reducedMotion=1
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
| Query | Values |
|
|
11
|
+
| --- | --- |
|
|
12
|
+
| `theme` | `light` (default) or `dark` (canonical dark role remap) |
|
|
13
|
+
| `viewport` | `full` (default), `320`, `375`, `1024`, `1280` |
|
|
14
|
+
| `background` | `default`, `inverse`, `transparent` |
|
|
15
|
+
| `locale` | Passed to components that already read a locale, such as consent |
|
|
16
|
+
| `rtl` | `1` sets document direction to `rtl` on web |
|
|
17
|
+
| `reducedMotion` | `1` sets `data-reduced-motion="1"` on the document element |
|
|
18
|
+
|
|
19
|
+
Reload keeps the query. The bar shows the current query when any value is not the default. Choosing a control's default removes that key from the URL. Leaving the component route restores the app theme and, on web, document direction and reduced motion. Custom palettes stay on `/palette`.
|
|
@@ -11,6 +11,7 @@ import {AnnouncementsApp} from "@terreno/announcements";
|
|
|
11
11
|
|
|
12
12
|
new AnnouncementsApp({
|
|
13
13
|
defaultAcknowledgementPolicy: "dismiss-only",
|
|
14
|
+
uploadToken: process.env.ANNOUNCEMENTS_UPLOAD_TOKEN,
|
|
14
15
|
// audienceType staff/patient/all is applied inside the plugin via matchAudienceByType.
|
|
15
16
|
isStaff: (user) => (user as {admin?: boolean}).admin === true,
|
|
16
17
|
matchAudience: (user, announcement) => {
|
|
@@ -46,6 +47,8 @@ For Flourish-style surfaces on one collection:
|
|
|
46
47
|
|
|
47
48
|
2. Create announcements in admin (draft → publish). Use `AnnouncementOverview`, `AnnouncementEditor`, and (optionally) `AnnouncementList` from `@terreno/admin-frontend` with dedicated Expo routes (see `example-frontend/app/admin/announcements/`). The built-in admin screen widget key `announcements` renders the overview when your host uses `AdminScreenRouter` for custom screens. Published `title`/`body` edits auto-increment `version`, which re-shows the surface to users who only acknowledged the previous version.
|
|
48
49
|
|
|
50
|
+
For release-driven batches, use an [announcement release pack](../reference/announcement-release-packs.md). It keeps one `pack.yaml` manifest plus multiple Markdown announcements in source control, validates release and build versioning, and can import idempotent drafts or explicitly publish the whole pack through a dedicated bearer token. The example Cloud Run service accepts `Authorization: Bearer terreno-example-announcement-upload` (Terraform variable `announcements_upload_token`).
|
|
51
|
+
|
|
49
52
|
### Admin editor fields
|
|
50
53
|
|
|
51
54
|
`AnnouncementEditor` exposes structured targeting controls (no raw `requiresAcknowledgement` boolean):
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Implementation Plan: Demo workshop
|
|
2
|
+
|
|
3
|
+
**Status:** Approved
|
|
4
|
+
**Branch:** `cursor/demo-workshop-cc39`
|
|
5
|
+
**Owner:** —
|
|
6
|
+
**Created:** 2026-09-26
|
|
7
|
+
**Task list:** [demo-workshop.md](../tasks/demo-workshop.md)
|
|
8
|
+
**Depends on:** —
|
|
9
|
+
**RTK deprecation flag:** None
|
|
10
|
+
**Supersedes:** the visual-regression non-goal in [examples-demo-coverage.md](examples-demo-coverage.md). Story coverage from that plan stays as it is.
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
|
|
14
|
+
Turn the Expo demo into the workshop for `@terreno/ui`: a person can find a component, copy a usage snippet, change theme and viewport from the URL, and see whether every stable story passes interaction, accessibility, and visual checks on web and Android.
|
|
15
|
+
|
|
16
|
+
Released demo majors stay online. The site opens on the newest released major (57 today). A dropdown lists older majors, such as 56, and the next major when `preview/<major>` exists, such as 58 preview.
|
|
17
|
+
|
|
18
|
+
## Non-Goals
|
|
19
|
+
|
|
20
|
+
- An addon or plugin system.
|
|
21
|
+
- iOS simulator runs, iOS screenshot baselines, and iOS accessibility checks.
|
|
22
|
+
- A translation catalog for `@terreno/ui`.
|
|
23
|
+
- The commented combinatorial prop matrix in `demo/app/demo/[component].tsx`.
|
|
24
|
+
- A pull-request comment bot.
|
|
25
|
+
- Rebuilding frozen majors on later master commits.
|
|
26
|
+
- Android baselines for historical majors. Android CI proves the branch under test.
|
|
27
|
+
- Feature-branch Netlify aliases (`pr-*`) appearing in the version dropdown.
|
|
28
|
+
|
|
29
|
+
## Decisions
|
|
30
|
+
|
|
31
|
+
| Question | Decision |
|
|
32
|
+
|----------|----------|
|
|
33
|
+
| Platforms | Web and Android in this plan. iOS is a later plan. |
|
|
34
|
+
| Visual coverage | Every stable named story. A story may opt out only with `stability: "exclude"` and a non-empty reason. |
|
|
35
|
+
| Usage snippets | One curated `usageExample` string on each story config, shown with a copy button. |
|
|
36
|
+
| Preview controls | Theme, viewport, background, locale, right-to-left, and reduced motion on demo and dev routes. State lives in the URL. Embeds apply the query and hide the bar. |
|
|
37
|
+
| Theme values | Light, plus the existing canonical dark role remap (`demo/components/palette/darkTheme.ts`). Custom palettes stay on `/palette`. |
|
|
38
|
+
| Viewports | The Box breakpoints already documented for web: 320, 375, 1024, and 1280. |
|
|
39
|
+
| Locale | The toolbar passes the selected locale only to components that already read one (consent content, date and calendar formatting). Right-to-left is a separate control. |
|
|
40
|
+
| Collaboration | CI artifacts and a check summary. No pull-request comment. |
|
|
41
|
+
| Agent access | Public `@terreno/mcp` tools read a generated demo catalog and the version manifest URL. |
|
|
42
|
+
| Visual comparison | Separate cropped web and Android PNG baselines. Reuse the chart pixel matcher. An Android-specific tolerance requires a note in the how-to. |
|
|
43
|
+
| Android CI | The full Android suite blocks pull requests that change `ui/` or `demo/`. |
|
|
44
|
+
| Preview branch | `preview/<major>` (for example `preview/58`) is the only branch labeled as the next-major preview. |
|
|
45
|
+
| Frozen majors | When master moves to a new major, keep the previous major at its last master commit. The current major redeploys on every master merge. Backfill 56 from its last master commit. |
|
|
46
|
+
| Version dropdown | Persistent top bar on the home, demo, and dev pages. Default is the highest released major, which is the production deploy. |
|
|
47
|
+
| Docs embeds | A docs version embeds the demo major it documents. The next docs version embeds `preview/<major>` when that deploy exists. |
|
|
48
|
+
| Accessibility | Web uses axe and fails a story on serious or critical violations. Android asserts the accessibility tree from the same step list. Moderate axe findings are warnings in the health report. |
|
|
49
|
+
| Interaction source | One declarative step list per story. Playwright runs it on web. A generated Maestro flow runs it on Android. |
|
|
50
|
+
|
|
51
|
+
## Architecture
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
demo/demoConfig.tsx
|
|
55
|
+
usageExample, related routes, story stability, interaction steps
|
|
56
|
+
│
|
|
57
|
+
├─ home/dev search + category filters + linked related components
|
|
58
|
+
├─ preview bar (URL state) and version bar (versions.json)
|
|
59
|
+
├─ web Playwright: interactions, axe, cropped PNG
|
|
60
|
+
├─ Android Maestro: same steps, accessibility tree, cropped PNG
|
|
61
|
+
└─ demo-catalog.json ── @terreno/mcp list/get tools
|
|
62
|
+
|
|
63
|
+
master deploy ── production site = current major (57)
|
|
64
|
+
last 56 master commit ── Netlify alias v56 (frozen)
|
|
65
|
+
preview/58 ── Netlify alias v58 (preview: true)
|
|
66
|
+
production /versions.json ── dropdown on every major
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Catalog contract
|
|
70
|
+
|
|
71
|
+
`DemoConfiguration` gains:
|
|
72
|
+
|
|
73
|
+
- `usageExample: string` — copyable usage, including the import when it is a normal `@terreno/ui` import.
|
|
74
|
+
- `related` entries resolve through `findDemoConfig`. Unknown names fail the catalog test.
|
|
75
|
+
- `stories[name].stability`: `"stable"` by default, or `"exclude"` with `excludeReason`.
|
|
76
|
+
- `stories[name].showInDemo` is honored. `false` hides that story from demo mode and keeps it on the dev route.
|
|
77
|
+
- `stories[name].interactions`: ordered steps `{action: "press" | "type" | "expectText" | "expectRole", targetTestID?, value?, role?, name?}`.
|
|
78
|
+
- Control defaults come from the control type: `false` for boolean, `0` for number, `""` for string. The `""` fallback for every type goes away.
|
|
79
|
+
|
|
80
|
+
`demo/collectRegisteredStoryRenders.ts` remains the inventory. Story ids stay `<Component> / <story>`.
|
|
81
|
+
|
|
82
|
+
### Preview and version chrome
|
|
83
|
+
|
|
84
|
+
`DemoChrome` wraps home, demo, and dev. The version dropdown is on every non-embed page. Preview controls are on demo and dev component routes. `?embed=1` hides both and still applies preview query params.
|
|
85
|
+
|
|
86
|
+
Preview query keys: `theme`, `viewport`, `background`, `locale`, `rtl`, `reducedMotion`.
|
|
87
|
+
|
|
88
|
+
The version dropdown does not switch code inside one bundle. It navigates to that major's deploy. Every bundle loads `versions.json` from the production demo origin so a frozen major learns about a new preview without a rebuild. That file is served with CORS for the demo aliases.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
interface DemoVersionManifest {
|
|
92
|
+
currentMajor: number;
|
|
93
|
+
versions: {
|
|
94
|
+
major: number;
|
|
95
|
+
label: string;
|
|
96
|
+
url: string;
|
|
97
|
+
preview: boolean;
|
|
98
|
+
released: boolean;
|
|
99
|
+
}[];
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`currentMajor` is the highest `released && !preview` entry. Production is that major. `preview/58` adds `{major: 58, label: "58 preview", preview: true, released: false}` while the branch exists. Merging 58 to master freezes 57 onto alias `v57`, points production at 58, and removes the preview flag.
|
|
104
|
+
|
|
105
|
+
Feature-branch aliases stay out of the manifest.
|
|
106
|
+
|
|
107
|
+
### Proof
|
|
108
|
+
|
|
109
|
+
| Check | Web | Android |
|
|
110
|
+
| --- | --- | --- |
|
|
111
|
+
| Render | Existing `demo/storiesSmoke.test.tsx` | Maestro opens the story |
|
|
112
|
+
| Interaction | Playwright runs `interactions` | Generated Maestro flow |
|
|
113
|
+
| Accessibility | axe, serious and critical fail | Accessibility-tree assertions from `expectRole` / labels |
|
|
114
|
+
| Visual | Cropped PNG in `demo/rendered-snapshots/web/` | Cropped PNG in `demo/rendered-snapshots/android/` |
|
|
115
|
+
|
|
116
|
+
Chart goldens stay in `demo/rendered-snapshots/` until the chart task moves them. The chart compare script keeps working for chart fixtures.
|
|
117
|
+
|
|
118
|
+
A stable story without a web golden, an Android golden, or an exclusion fails CI. The same rule applies to interaction steps.
|
|
119
|
+
|
|
120
|
+
`demo/story-health.json` is produced by those jobs and copied into the demo build. The component page shows pass, fail, or not-run for render, interaction, accessibility, and visual, per platform. Missing file means not-run.
|
|
121
|
+
|
|
122
|
+
CI uploads the JSON, PNGs, and diffs as job artifacts and writes a check summary. It does not comment on the pull request.
|
|
123
|
+
|
|
124
|
+
Android runs on a CircleCI Android image with one pinned emulator: Pixel 6, API 34. The job is path-filtered like today's Maestro demo job (`ui/**`, `demo/**`) and is required on those pull requests.
|
|
125
|
+
|
|
126
|
+
### MCP
|
|
127
|
+
|
|
128
|
+
`bun run demo:catalog` writes `demo/demo-catalog.json` with component name, category, route, snippet, story ids, stability, and the production `versions.json` URL. `@terreno/mcp` exposes:
|
|
129
|
+
|
|
130
|
+
- `terreno_list_demo_stories`
|
|
131
|
+
- `terreno_get_demo_story`
|
|
132
|
+
|
|
133
|
+
Tools return catalog data. They do not drive a browser and they do not run Android.
|
|
134
|
+
|
|
135
|
+
### Docs embeds
|
|
136
|
+
|
|
137
|
+
`website` `DEMO_URL` becomes per docs version. Version 57.x points at the 57 demo alias (production while 57 is current). The next docs version points at the `preview/<major>` URL when the manifest lists that preview, and otherwise at production. `ComponentDemo` keeps `?embed=1`.
|
|
138
|
+
|
|
139
|
+
## Models
|
|
140
|
+
|
|
141
|
+
None.
|
|
142
|
+
|
|
143
|
+
## APIs
|
|
144
|
+
|
|
145
|
+
`GET /versions.json` on the production demo host, shape above.
|
|
146
|
+
|
|
147
|
+
MCP tools `terreno_list_demo_stories` and `terreno_get_demo_story`. No new HTTP API on `@terreno/api`.
|
|
148
|
+
|
|
149
|
+
## Notifications
|
|
150
|
+
|
|
151
|
+
None.
|
|
152
|
+
|
|
153
|
+
## UI
|
|
154
|
+
|
|
155
|
+
- Home and dev: search box and category filters over `DemoConfig`.
|
|
156
|
+
- Component page: copyable snippet, linked related components, preview controls, per-story health.
|
|
157
|
+
- Persistent version dropdown on home, demo, and dev.
|
|
158
|
+
- Embed mode unchanged except that preview query params apply.
|
|
159
|
+
|
|
160
|
+
Frontend verification uses the running web demo. Android verification uses the emulator flow for Button, then the full stable catalog.
|
|
161
|
+
|
|
162
|
+
## Phases
|
|
163
|
+
|
|
164
|
+
1. Catalog contract, search, snippets, and the preview bar. Button is the tracer.
|
|
165
|
+
2. Web interaction, axe, and visual baselines, then the health report.
|
|
166
|
+
3. Android emulator CI for Button, then every stable story.
|
|
167
|
+
4. Version manifest, frozen majors, preview branch, docs embeds.
|
|
168
|
+
5. MCP catalog tools.
|
|
169
|
+
|
|
170
|
+
## Feature Flags & Migrations
|
|
171
|
+
|
|
172
|
+
None. New demo query params are optional. Existing `/demo/<component>` URLs keep working. Production keeps serving the current major at the existing demo host.
|
|
173
|
+
|
|
174
|
+
## Not Included / Future Work
|
|
175
|
+
|
|
176
|
+
- iOS simulator coverage and iOS goldens.
|
|
177
|
+
- Library-wide translations.
|
|
178
|
+
- Demo addon API.
|
|
179
|
+
- Pixel baselines for every historical major.
|
|
180
|
+
|
|
181
|
+
## Files to Create / Modify
|
|
182
|
+
|
|
183
|
+
Create
|
|
184
|
+
|
|
185
|
+
- `docs/how-to/preview-demo.md`
|
|
186
|
+
- `docs/how-to/demo-versions.md`
|
|
187
|
+
- `docs/how-to/compare-demo-snapshots.md`
|
|
188
|
+
- `demo/components/DemoChrome.tsx`
|
|
189
|
+
- `scripts/demo/` runners and manifest publisher
|
|
190
|
+
- `demo/demo-catalog.json` (generated)
|
|
191
|
+
|
|
192
|
+
Modify
|
|
193
|
+
|
|
194
|
+
- `demo/demoConfig.tsx`, `demo/story-config/*.config.tsx`, `demo/app/demo/[component].tsx`
|
|
195
|
+
- `demo/components/DemoHomePage.tsx`, `demo/components/DevHomePage.tsx`
|
|
196
|
+
- `demo/README.md`
|
|
197
|
+
- `docs/reference/test.md`, `docs/reference/ui.md`, `docs/reference/mcp-server.md`
|
|
198
|
+
- `docs/how-to/run-tests-locally.md`, `docs/how-to/circleci.md`
|
|
199
|
+
- `.circleci/config.yml`, `.circleci/continue-config.yml`
|
|
200
|
+
- `scripts/ci/netlify-deploy.sh`
|
|
201
|
+
- `website/src/components/ComponentDemo/index.tsx`, `website/docusaurus.config.ts`
|
|
202
|
+
- `mcp-server/src/tools.ts`
|
|
203
|
+
|
|
204
|
+
## Task List
|
|
205
|
+
|
|
206
|
+
[docs/tasks/demo-workshop.md](../tasks/demo-workshop.md)
|
|
207
|
+
|
|
208
|
+
## Acceptance Criteria
|
|
209
|
+
|
|
210
|
+
- [ ] Search on the home grid finds Button by name and by category, and Button's related components are links.
|
|
211
|
+
- [ ] Every registered component has a `usageExample`, and the Button page copies it.
|
|
212
|
+
- [ ] A URL on the Button demo sets theme, viewport, background, locale, right-to-left, and reduced motion. Embed hides the bar and keeps the query.
|
|
213
|
+
- [ ] Every stable story has web interaction steps or an exclusion reason, an axe result, and a web PNG baseline.
|
|
214
|
+
- [ ] The same stable stories have Android interaction, accessibility-tree, and PNG results. The Android job fails the pull request when `ui/` or `demo/` changes.
|
|
215
|
+
- [ ] The Button page shows health from `story-health.json`. CI publishes a check summary and artifacts and does not comment.
|
|
216
|
+
- [ ] Production opens on major 57. Alias `v56` serves the last major-56 master commit. `preview/58` appears as "58 preview" and is not the default.
|
|
217
|
+
- [ ] A 57 docs page embeds the 57 demo. The next docs version embeds the 58 preview when that alias exists.
|
|
218
|
+
- [ ] `terreno_get_demo_story` returns Button's snippet, route, and the version manifest URL.
|
|
219
|
+
- [ ] `demo/README.md`, the how-to pages named above, and the reference pages named above match this behavior.
|
|
@@ -211,7 +211,7 @@ Conversation history with multi-modal prompts.
|
|
|
211
211
|
| `userId` | ObjectId | Owner (required) |
|
|
212
212
|
| `title` | string? | Auto-generated on first `/gpt/prompt` response when empty |
|
|
213
213
|
| `projectId` | ObjectId? | Optional project association |
|
|
214
|
-
| `prompts` | array | Messages: `text`, `type` (`user` \| `assistant` \| `system` \| `tool-call` \| `tool-result`), optional `content` parts, `model`, `rating`, tool fields |
|
|
214
|
+
| `prompts` | array | Messages: `text`, `type` (`user` \| `assistant` \| `system` \| `tool-call` \| `tool-result`), optional `content` parts, `model`, `rating`, tool fields. `text` is required unless `content` has parts; an image-only assistant response saves `text: ""` |
|
|
215
215
|
|
|
216
216
|
**Virtual:** `ownerId` aliases `userId` for `Permissions.IsOwner`.
|
|
217
217
|
|
|
@@ -254,6 +254,8 @@ GPT project with persistent context and memories.
|
|
|
254
254
|
| `/gpt/histories/:id/rating` | PATCH | `IsAuthenticated` | Rate a prompt; body: `{promptIndex, rating: "up" \| "down" \| null}` |
|
|
255
255
|
| `/gpt/tools` | GET | `IsAuthenticated` | List builtin + MCP tools |
|
|
256
256
|
|
|
257
|
+
Generated images (image-output models such as `gemini-3-pro-image`) arrive as SSE `image` events: `{image: {mimeType, url}}` with a base64 data URL. Each image is sent once, even when the model reports it both as a stream file part and in the final `result.files`. The saved assistant prompt stores one `image` content part per image. On later turns, `buildMessages` sends an image-only assistant prompt to the model as the text `[Generated image]`, because providers reject empty assistant turns.
|
|
258
|
+
|
|
257
259
|
AI resolution order: `x-ai-api-key` header + `createModelFn` → `createServerModelFn(modelId)` → configured `aiService` → demo SSE response when `demoMode` and none available.
|
|
258
260
|
|
|
259
261
|
### addGptHistoryRoutes(router, options?)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Announcement release packs
|
|
2
|
+
|
|
3
|
+
An announcement release pack is a reviewable directory containing one manifest and one Markdown file per announcement. Import the pack through `POST /announcements/import-release`; imports are idempotent by product, release version, channel, and announcement slug.
|
|
4
|
+
|
|
5
|
+
## Directory format
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
announcements/releases/1.14.0/
|
|
9
|
+
pack.yaml
|
|
10
|
+
staff-required.md
|
|
11
|
+
patient-banner.md
|
|
12
|
+
changelog.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`pack.yaml` carries release metadata, shared defaults, and the ordered file list:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
schema: terreno.announcement-pack/v1
|
|
19
|
+
release:
|
|
20
|
+
product: example
|
|
21
|
+
version: "1.14.0"
|
|
22
|
+
buildNumber: 1842
|
|
23
|
+
channel: production
|
|
24
|
+
defaults:
|
|
25
|
+
platforms: [ios, android, web]
|
|
26
|
+
audienceType: all
|
|
27
|
+
displayMode: feed
|
|
28
|
+
acknowledgementPolicy: dismiss-only
|
|
29
|
+
priority: 0
|
|
30
|
+
announcements:
|
|
31
|
+
- staff-required.md
|
|
32
|
+
- patient-banner.md
|
|
33
|
+
- changelog.md
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each announcement is Markdown with YAML frontmatter:
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
---
|
|
40
|
+
slug: staff-1-14-required
|
|
41
|
+
title: "Staff: version 1.14.0 is live"
|
|
42
|
+
displayMode: modal
|
|
43
|
+
audienceType: staff
|
|
44
|
+
acknowledgementPolicy: required
|
|
45
|
+
priority: 20
|
|
46
|
+
primaryAction:
|
|
47
|
+
label: Read the release notes
|
|
48
|
+
url: https://example.com/releases/1.14.0
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## What changed in 1.14.0
|
|
52
|
+
|
|
53
|
+
The updated workflow is now available.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Frontmatter overrides manifest defaults. Supported fields are:
|
|
57
|
+
|
|
58
|
+
| Field | Required | Description |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `slug` | Yes | Lowercase, hyphenated stable ID within the release. Re-importing the same slug updates instead of duplicating. |
|
|
61
|
+
| `title` | Yes | Surface title. Include the user-facing release version. |
|
|
62
|
+
| `displayMode` | No | `modal`, `banner`, or `feed`. |
|
|
63
|
+
| `audienceType` | No | `all`, `staff`, or `patient`. |
|
|
64
|
+
| `acknowledgementPolicy` | No | `required` or `dismiss-only`. |
|
|
65
|
+
| `platforms` | No | Any non-empty subset of `ios`, `android`, and `web`. |
|
|
66
|
+
| `priority` | No | Higher values appear first. |
|
|
67
|
+
| `minBuildNumber` | No | Per-item override for the release build number. |
|
|
68
|
+
| `publishAt`, `expiresAt` | No | ISO 8601 timestamps. |
|
|
69
|
+
| `audience` | No | Consumer-defined targeting object. |
|
|
70
|
+
| `primaryAction` | No | Object containing `label` and absolute `url`. |
|
|
71
|
+
|
|
72
|
+
## Versioning
|
|
73
|
+
|
|
74
|
+
The format has three separate versions:
|
|
75
|
+
|
|
76
|
+
1. `schema` versions the pack contract. The current value is `terreno.announcement-pack/v1`.
|
|
77
|
+
2. `release.version` is the user-facing product version and is stored with each imported announcement.
|
|
78
|
+
3. `release.buildNumber` is the client build and becomes `minBuildNumber` unless an announcement overrides it.
|
|
79
|
+
|
|
80
|
+
The announcement model's numeric `version` is server-managed content revision metadata. Do not put it in a pack. Editing the title or body of an already-published imported announcement increments it and re-shows required content where applicable.
|
|
81
|
+
|
|
82
|
+
## Example pack
|
|
83
|
+
|
|
84
|
+
`example-backend/announcements/releases/1.14.0/` is a complete pack with a required staff modal, a patient banner, and a changelog feed item. The example backend ships a loader that parses the pack, validates it with `announcementReleaseImportSchema`, and prints or uploads the JSON body:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd example-backend
|
|
88
|
+
bun run announcements:pack announcements/releases/1.14.0 # print JSON
|
|
89
|
+
bun run announcements:pack announcements/releases/1.14.0 --upload http://localhost:4000
|
|
90
|
+
bun run announcements:pack announcements/releases/1.14.0 --publish --upload <api-url>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Uploads use `ANNOUNCEMENTS_UPLOAD_TOKEN`, falling back to the example default `terreno-example-announcement-upload`. Uploads are drafts unless `--publish` is passed.
|
|
94
|
+
|
|
95
|
+
## Import API
|
|
96
|
+
|
|
97
|
+
Enable automation with a dedicated secret:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
new AnnouncementsApp({
|
|
101
|
+
uploadToken: process.env.ANNOUNCEMENTS_UPLOAD_TOKEN,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The example Cloud Run backend seeds `ANNOUNCEMENTS_UPLOAD_TOKEN` from Terraform (`announcements_upload_token`, default `terreno-example-announcement-upload`). Use that bearer token against the example preview or production URL. Preview deploys use the same default until the secret exists.
|
|
106
|
+
|
|
107
|
+
Send the parsed manifest and Markdown files as JSON:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
curl --fail-with-body \
|
|
111
|
+
-H "Authorization: Bearer $ANNOUNCEMENTS_UPLOAD_TOKEN" \
|
|
112
|
+
-H "Content-Type: application/json" \
|
|
113
|
+
--data @announcement-release.json \
|
|
114
|
+
https://api.example.com/announcements/import-release
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The request shape is:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"release": {
|
|
122
|
+
"product": "example",
|
|
123
|
+
"version": "1.14.0",
|
|
124
|
+
"buildNumber": 1842,
|
|
125
|
+
"channel": "production"
|
|
126
|
+
},
|
|
127
|
+
"defaults": {
|
|
128
|
+
"platforms": ["ios", "android", "web"],
|
|
129
|
+
"displayMode": "feed"
|
|
130
|
+
},
|
|
131
|
+
"announcements": [
|
|
132
|
+
{
|
|
133
|
+
"slug": "changelog",
|
|
134
|
+
"title": "Version 1.14.0",
|
|
135
|
+
"body": "## What changed\n\nRelease details."
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"publish": false
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`publish` defaults to `false`, so new rows are drafts. Pass `"publish": true` only for an intentional live import. Draft re-imports do not unpublish an existing live announcement. An authenticated admin may also call the endpoint without the dedicated upload token.
|
|
143
|
+
|
|
144
|
+
The response reports `created`, `updated`, `unchanged`, and `published` counts plus the ID, slug, status, and content version of each row.
|
|
145
|
+
|
|
146
|
+
The body is validated with the exported Zod schema `announcementReleaseImportSchema` before any row is written. Unknown keys, malformed slugs, empty titles or bodies, invalid enum values, missing `release.product` / `release.version`, and duplicate slugs return **400** `Validation failed` with per-field messages in `meta.fields`. `importAnnouncementRelease` runs the same schema when called directly from server code.
|