@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
|
@@ -27,6 +27,7 @@ new TerrenoApp({ userModel: User })
|
|
|
27
27
|
| `help.enabled` | `boolean` | `false` | Registers help search/detail routes for MCP and in-app help |
|
|
28
28
|
| `matchAudience` | `(user, announcement) => boolean` | always `true` | Opaque audience JSON filter composed with `audienceType` via `matchAudienceByType` |
|
|
29
29
|
| `isStaff` | `(user) => boolean` | `user.admin === true` | Staff check used by `matchAudienceByType` for `audienceType` |
|
|
30
|
+
| `uploadToken` | `string` | unset | Dedicated bearer token for idempotent `POST /announcements/import-release` automation |
|
|
30
31
|
| `permissions` | partial CRUD overrides | admin-only | Overrides default `IsAdmin` permissions on announcement CRUD |
|
|
31
32
|
|
|
32
33
|
## Acknowledgement policy
|
|
@@ -52,7 +53,7 @@ Legacy MongoDB documents that still store `requiresAcknowledgement: true` map to
|
|
|
52
53
|
|
|
53
54
|
## Models
|
|
54
55
|
|
|
55
|
-
- **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`
|
|
56
|
+
- **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`, and optional imported `release` metadata plus `releaseSlug`
|
|
56
57
|
- **AnnouncementAcknowledgement** — per-user acknowledgement at a specific `version`
|
|
57
58
|
- **AnnouncementImpression** — per-view analytics row
|
|
58
59
|
- **AnnouncementClickEvent** — per-click analytics row (`action: "primaryAction"`, `version` at click time, optional `platform`)
|
|
@@ -83,7 +84,7 @@ The plugin applies `matchAudienceByType` before `matchAudience`. When `isStaff`
|
|
|
83
84
|
| GET | `/announcements/pending` | Current interrupt (`modal` or `banner` only) + `remainingCount` (`platform` query: `ios` \| `android` \| `web`; optional `version` build number) |
|
|
84
85
|
| GET | `/announcements/feed` | Paginated published changelog — all display modes (`platform` and optional `version` as above) |
|
|
85
86
|
| POST | `/announcements/:id/acknowledge` | Record acknowledgement (idempotent per version). Same visibility as pending/feed; **404** when not visible. |
|
|
86
|
-
| POST | `/announcements/:id/impression` | Record a view. Same visibility as pending/feed; **404** when not visible. |
|
|
87
|
+
| POST | `/announcements/:id/impression` | Record a view (`{ platform? }`). Same visibility as pending/feed; **404** when not visible. **400** for unknown keys or an invalid `platform`. |
|
|
87
88
|
| POST | `/announcements/:id/click` | Record a primary-action click (`{ action: "primaryAction", platform? }`; optional `?version=` for min-build visibility). Returns **404** when not visible (checked before action/CTA validation). **400** when `action` is invalid, `primaryAction` is absent, or an explicit `platform` value is invalid. |
|
|
88
89
|
|
|
89
90
|
`current` and feed items include resolved `requiresAcknowledgement` (boolean) and `displayMode` derived from policy resolution and stored fields above.
|
|
@@ -96,7 +97,7 @@ Optional integer build number on `GET /pending`, `GET /feed`, and help routes. W
|
|
|
96
97
|
- `?version=10` (or higher) shows it
|
|
97
98
|
- omitting `version` does **not** hide gated items
|
|
98
99
|
|
|
99
|
-
`POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Click checks visibility before action
|
|
100
|
+
`POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Impression and click bodies are validated with strict Zod schemas (`{ platform? }` and `{ action: "primaryAction", platform? }`); unknown keys or invalid values return **400** `Validation failed` with per-field messages in `meta.fields`. Click validates `platform` first, then checks visibility before validating `action` and the CTA. Returns **400** when `action` is not `"primaryAction"`, the announcement has no `primaryAction`, or an explicit body `platform` is not `ios` / `android` / `web` (omit `platform` to use query/user-agent resolution). Each click inserts a new row (not idempotent). Acknowledge is idempotent per `(userId, announcementId, version)`.
|
|
100
101
|
|
|
101
102
|
## Admin routes
|
|
102
103
|
|
|
@@ -104,9 +105,12 @@ Admin CRUD is on `/announcements` via `modelRouter`. Custom actions:
|
|
|
104
105
|
|
|
105
106
|
- `GET /announcements/config` — returns `{ data: { defaultAcknowledgementPolicy } }` from the plugin constructor (admin only; defaults to `"dismiss-only"` when omitted). Used to pre-fill the admin editor acknowledgement policy field.
|
|
106
107
|
- `GET /announcements/overview` — paginated admin dashboard with per-announcement metrics and aggregate totals. Access uses `adminOverviewPermissions` (defaults to `IsAdmin`). Query: `page` (default `1`), `limit` (default `20`, max `100`).
|
|
108
|
+
- `POST /announcements/import-release` — idempotently imports up to 100 announcements keyed by release product/version/channel plus slug. Admin auth or the configured `uploadToken` bearer token is required. New items default to draft; request `publish: true` to publish them. The example deployment's token is `terreno-example-announcement-upload`, seeded by Terraform.
|
|
107
109
|
- `POST /announcements/:id/publish` — draft → published
|
|
108
110
|
- `POST /announcements/:id/archive` — published → archived
|
|
109
111
|
|
|
112
|
+
See [Announcement release packs](announcement-release-packs.md) for the Markdown/frontmatter format, JSON request shape, version rules, and draft/live behavior.
|
|
113
|
+
|
|
110
114
|
### `GET /announcements/overview`
|
|
111
115
|
|
|
112
116
|
Admin-only list for launch dashboards. Includes **all** lifecycle statuses (`draft`, `published`, `archived`). Soft-deleted announcements and event rows are excluded.
|
|
@@ -222,6 +222,7 @@ export const todoRouter = modelRouter("/todos", Todo, {
|
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
Do not add `endpoints: (router) => { router.get(...) }` when an action fits.
|
|
225
|
+
Action config supports `allowAnonymous: true` as a per-action override; it does not change authentication for CRUD or sibling actions.
|
|
225
226
|
|
|
226
227
|
## modelRouter array operations
|
|
227
228
|
|
|
@@ -239,6 +239,7 @@ secrets and `PUBLIC_API_URL` / `COMMS_WEBHOOK_PUBLIC_URL` as above. See
|
|
|
239
239
|
| Variable | Read by | Required | Default | Secret | Scope |
|
|
240
240
|
|----------|---------|----------|---------|--------|-------|
|
|
241
241
|
| `ADMIN_SPA_ENABLED` | example-backend | ❌ | — | No | server |
|
|
242
|
+
| `ANNOUNCEMENTS_UPLOAD_TOKEN` | example-backend `AnnouncementsApp` | ❌ | example deploy: `terreno-example-announcement-upload` | Yes outside the example app | server |
|
|
242
243
|
| `ADMIN_SPA_DIST_DIR` | example-backend Dockerfile | ❌ | — | No | server |
|
|
243
244
|
| `ADMIN_SPA_DEV_PROXY` | admin-spa plugin | ❌ | — | No | server |
|
|
244
245
|
| `ADMIN_SPA_BACKEND_URL` | admin-spa tests | ❌ | — | No | tooling |
|
|
@@ -320,9 +320,17 @@ images once per mount.
|
|
|
320
320
|
### SplitPage
|
|
321
321
|
|
|
322
322
|
Master-detail layout. Pass `listViewData` plus `renderListViewItem` for the list, and
|
|
323
|
-
`renderContent` for the detail pane. On large screens both panes stay visible. On small
|
|
323
|
+
`renderContent` or children for the detail pane. On large screens both panes stay visible. On small
|
|
324
324
|
screens the detail replaces the list until the user goes back.
|
|
325
325
|
|
|
326
|
+
`desktopChildrenMinWidth` opts into a minimum pixel width for each desktop child when there
|
|
327
|
+
are one or two children. `narrowViewportChildLabels` opts into a labeled full-width pager on
|
|
328
|
+
the narrow viewport. `narrowBelowWidth` uses that viewport when the window is at or below the
|
|
329
|
+
given pixel width; when omitted, the narrow viewport follows `isNarrowViewport()`. These props
|
|
330
|
+
are web only; the native `SplitPage` ignores them. See `SplitPageProps` for when each prop
|
|
331
|
+
applies and what is ignored.
|
|
332
|
+
`IconButton`'s `backgroundOpacity` tints only that button's background.
|
|
333
|
+
|
|
326
334
|
```typescript
|
|
327
335
|
import {SplitPage, Text} from "@terreno/ui";
|
|
328
336
|
|
|
@@ -627,7 +635,7 @@ import {
|
|
|
627
635
|
mediaQuery,
|
|
628
636
|
mediaQueryLargerThan,
|
|
629
637
|
mediaQuerySmallerThan,
|
|
630
|
-
|
|
638
|
+
isNarrowViewport,
|
|
631
639
|
} from "@terreno/ui";
|
|
632
640
|
|
|
633
641
|
// Read the current breakpoint
|
|
@@ -645,9 +653,9 @@ if (mediaQuerySmallerThan("lg")) {
|
|
|
645
653
|
console.log("Smaller than large");
|
|
646
654
|
}
|
|
647
655
|
|
|
648
|
-
//
|
|
649
|
-
if (
|
|
650
|
-
console.info("
|
|
656
|
+
// Current window is below the desktop breakpoint
|
|
657
|
+
if (isNarrowViewport()) {
|
|
658
|
+
console.info("Narrow viewport");
|
|
651
659
|
}
|
|
652
660
|
``````
|
|
653
661
|
|
|
@@ -673,7 +681,7 @@ Web (desktop staff):
|
|
|
673
681
|
|
|
674
682
|
On web, `sm` (320) and `md` (375) still classify widths below 1024 so layouts can remain accessible.
|
|
675
683
|
|
|
676
|
-
`
|
|
684
|
+
`isNarrowViewport()` is true below the supported desktop floor: native width < 1024 (`xl`), web width < 1024 (`lg`). `isMobileDevice()` is the same check and is deprecated in favor of `isNarrowViewport`.
|
|
677
685
|
|
|
678
686
|
Responsive `Box` direction props update automatically when the window resizes or a device rotates:
|
|
679
687
|
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Task List: Demo workshop
|
|
2
|
+
|
|
3
|
+
See: [`docs/implementationPlans/demo-workshop.md`](../implementationPlans/demo-workshop.md)
|
|
4
|
+
|
|
5
|
+
**RTK deprecation flag:** None
|
|
6
|
+
|
|
7
|
+
## Instructions for the implementing agent
|
|
8
|
+
|
|
9
|
+
- Work the frontier: any task whose blockers are complete. The tracer is Task 1.1 (Button).
|
|
10
|
+
- Do not start iOS work, a translation catalog, or an addon API.
|
|
11
|
+
- Docs named on a task ship in that task. Use the `update-docs` skill.
|
|
12
|
+
- UI tasks also use `verify-ui-changes` and `terreno-ui`.
|
|
13
|
+
- Visual tasks use `review-chart-visuals` for the PNG diff workflow, then the new demo how-to.
|
|
14
|
+
- Run the task's verification command before handoff. `bun run lint` and `bun run demo:compile` on every demo task.
|
|
15
|
+
|
|
16
|
+
## Phase 1: Catalog, snippets, and preview bar
|
|
17
|
+
|
|
18
|
+
- [x] **Task 1.1**: Button catalog contract
|
|
19
|
+
- Delivers: Button has a copyable usage snippet, resolvable related links, stable stories, one declarative interaction list, and typed control defaults.
|
|
20
|
+
- Files: `demo/demoConfig.tsx`, `demo/story-config/Button.config.tsx`, `demo/stories/Button.stories.tsx`, `demo/collectRegisteredStoryRenders.ts`, `demo/app/demo/[component].tsx`, `demo/README.md`
|
|
21
|
+
- Blocked by: none
|
|
22
|
+
- Acceptance: a unit test fails when a related name does not resolve, when an excluded story has an empty reason, or when a boolean control defaults to `""`. Button's snippet renders and copies. `showInDemo: false` hides a Button story in demo mode and leaves it on the dev route.
|
|
23
|
+
- Verification: `bun test demo/collectRegisteredStoryRenders.ts demo/demoConfig` (or the new colocated test), `bun run demo:compile`, and a web demo pass that opens Button, copies the snippet, and follows one related link.
|
|
24
|
+
- Docs: `demo/README.md` catalog-contract section
|
|
25
|
+
- Skills: `update-docs`, `terreno-ui`, `verify-ui-changes`
|
|
26
|
+
|
|
27
|
+
- [x] **Task 1.2**: Search, categories, and related links
|
|
28
|
+
- Delivers: the home grid and dev index filter by text and category, and every component's related row is links.
|
|
29
|
+
- Files: `demo/components/DemoHomePage.tsx`, `demo/components/DevHomePage.tsx`, `demo/app/demo/[component].tsx`, story configs whose `related` strings do not resolve, `demo/README.md`
|
|
30
|
+
- Blocked by: Task 1.1
|
|
31
|
+
- Acceptance: searching "button" shows Button; choosing a category hides other categories; a broken related name fails the catalog test; the home grid still does not nest pressables (`demo/components/DemoCard.test.tsx`).
|
|
32
|
+
- Verification: catalog unit test, `demo/components/DemoCard.test.tsx`, and a web demo pass of search, category filter, and a related link.
|
|
33
|
+
- Docs: `demo/README.md`
|
|
34
|
+
- Skills: `update-docs`, `terreno-ui`, `verify-ui-changes`
|
|
35
|
+
|
|
36
|
+
- [x] **Task 1.3**: Usage snippet on every component
|
|
37
|
+
- Delivers: every registered story config has `usageExample`, and the component page can copy it.
|
|
38
|
+
- Files: `demo/story-config/*.config.tsx`, `demo/app/demo/[component].tsx`, `scripts/check-demo-coverage.ts` or a sibling check, `demo/README.md`
|
|
39
|
+
- Blocked by: Task 1.1
|
|
40
|
+
- Acceptance: `bun run check:demo-coverage` (or the new snippet check it calls) fails when any registered config lacks a non-empty `usageExample`. The Button page copy control still works.
|
|
41
|
+
- Verification: the check command, plus one web copy action on a second component (TextField).
|
|
42
|
+
- Docs: `demo/README.md`
|
|
43
|
+
- Skills: `update-docs`, `verify-ui-changes`
|
|
44
|
+
|
|
45
|
+
- [x] **Task 1.4**: Preview toolbar and URL state
|
|
46
|
+
- Delivers: demo and dev component routes change theme, viewport, background, locale, right-to-left, and reduced motion from the URL. Embeds apply that state and hide the bar.
|
|
47
|
+
- Files: `demo/components/DemoChrome.tsx`, `demo/app/_layout.tsx`, `demo/app/demo/_layout.tsx`, `demo/app/dev/_layout.tsx`, `demo/contexts/EmbedModeContext.tsx`, `docs/how-to/preview-demo.md`, `demo/README.md`
|
|
48
|
+
- Blocked by: Task 1.1
|
|
49
|
+
- Acceptance: Button at a documented query uses the dark remap and the 375 viewport. Consent or date stories that already read locale receive the toolbar locale. Embed hides the bar. Reloading keeps the query.
|
|
50
|
+
- Verification: a focused demo unit test for query parse/serialize, `bun run demo:compile`, and a web demo pass of the Button query plus one embed URL.
|
|
51
|
+
- Docs: `docs/how-to/preview-demo.md`, `demo/README.md`
|
|
52
|
+
- Skills: `update-docs`, `terreno-ui`, `verify-ui-changes`
|
|
53
|
+
|
|
54
|
+
## Phase 2: Web proof and health
|
|
55
|
+
|
|
56
|
+
- [ ] **Task 2.1**: Web interaction and axe for Button
|
|
57
|
+
- Delivers: Playwright runs Button's step list and axe against the Button stories.
|
|
58
|
+
- Files: `scripts/demo/runWebStoryChecks.ts`, `demo/story-config/Button.config.tsx`, root `package.json` (axe dependency if needed), `.circleci/config.yml`, `.circleci/continue-config.yml`, `docs/how-to/run-tests-locally.md`, `docs/reference/test.md`
|
|
59
|
+
- Blocked by: Task 1.1
|
|
60
|
+
- Acceptance: the Button interaction steps pass in Chromium. A serious axe violation fails the command. An excluded story with a reason is skipped. The job is required for `ui/**` and `demo/**` pull requests.
|
|
61
|
+
- Verification: `bun run demo:web-checks --only=Button` (the script this task adds) locally against demo web, then the CircleCI path-filter test if one exists beside `.circleci/`.
|
|
62
|
+
- Docs: `docs/how-to/run-tests-locally.md`, `docs/reference/test.md`
|
|
63
|
+
- Skills: `update-docs`, `verify-ui-changes`
|
|
64
|
+
|
|
65
|
+
- [ ] **Task 2.2**: Web visual harness for Button
|
|
66
|
+
- Delivers: one cropped web PNG baseline for each stable Button story, compared with the chart pixel matcher.
|
|
67
|
+
- Files: `scripts/demo/compareDemoSnapshots.ts`, `demo/rendered-snapshots/web/`, `scripts/charts/compareChartImages.ts` (reuse only), `docs/how-to/compare-demo-snapshots.md`, `docs/reference/ui.md`
|
|
68
|
+
- Blocked by: Task 1.1
|
|
69
|
+
- Acceptance: `bun run demo:snapshots:compare --only=Button` passes against the new goldens and fails when a golden is missing. Chart compare still passes for the existing chart fixtures.
|
|
70
|
+
- Verification: the new compare command and `bun run ui:charts:compare`.
|
|
71
|
+
- Docs: `docs/how-to/compare-demo-snapshots.md`, `docs/reference/ui.md`, `docs/how-to/compare-chart-rendered-snapshots.md` (pointer only)
|
|
72
|
+
- Skills: `update-docs`, `review-chart-visuals`
|
|
73
|
+
|
|
74
|
+
- [ ] **Task 2.3**: Web proof for every stable story
|
|
75
|
+
- Delivers: every stable story has interaction steps or an exclusion reason, an axe run, and a web PNG baseline.
|
|
76
|
+
- Files: `demo/story-config/*.config.tsx`, `demo/stories/*.stories.tsx`, `demo/rendered-snapshots/web/`, the web check and snapshot scripts
|
|
77
|
+
- Blocked by: Task 2.1, Task 2.2
|
|
78
|
+
- Acceptance: the web check and snapshot commands, with no `--only`, exit 0. Removing one stable story's golden or steps fails the matching command. Excluded stories name a reason.
|
|
79
|
+
- Verification: `bun run demo:web-checks` and `bun run demo:snapshots:compare`.
|
|
80
|
+
- Docs: `docs/how-to/compare-demo-snapshots.md`, `docs/reference/test.md`
|
|
81
|
+
- Skills: `update-docs`, `review-chart-visuals`
|
|
82
|
+
|
|
83
|
+
- [ ] **Task 2.4**: Story health and CI summary
|
|
84
|
+
- Delivers: the component page shows render, interaction, accessibility, and visual status from `demo/story-health.json`. CI stores that file and writes a check summary.
|
|
85
|
+
- Files: `demo/app/demo/[component].tsx`, `scripts/demo/writeStoryHealth.ts`, `.circleci/continue-config.yml`, `docs/reference/test.md`
|
|
86
|
+
- Blocked by: Task 2.3
|
|
87
|
+
- Acceptance: Button shows a pass row after the web commands. A forced failing story shows fail. A missing health file shows not-run. The CircleCI job summary lists failed story ids. The job does not call `gh pr comment`.
|
|
88
|
+
- Verification: unit test for health rendering, the web commands, and a CI config test or review that the job uploads artifacts and writes the summary.
|
|
89
|
+
- Docs: `docs/reference/test.md`, `demo/README.md`
|
|
90
|
+
- Skills: `update-docs`, `verify-ui-changes`
|
|
91
|
+
|
|
92
|
+
## Phase 3: Android
|
|
93
|
+
|
|
94
|
+
- [ ] **Task 3.1**: Android proof for Button
|
|
95
|
+
- Delivers: a CircleCI Android emulator job runs Button's steps, accessibility-tree assertions, and a cropped Android PNG.
|
|
96
|
+
- Files: `.circleci/config.yml`, `.circleci/continue-config.yml`, `scripts/demo/generateMaestroFlows.ts`, `.maestro/flows/demo/generated/`, `demo/rendered-snapshots/android/`, `docs/how-to/circleci.md`, `docs/how-to/run-tests-locally.md`
|
|
97
|
+
- Blocked by: Task 2.1
|
|
98
|
+
- Acceptance: Pixel 6 API 34 runs the generated Button flow. The job is required when `ui/**` or `demo/**` changes. A missing Android golden fails the job. Web axe is unchanged.
|
|
99
|
+
- Verification: the local Maestro command documented in the how-to, against an emulator, for the Button flow. Record the CircleCI job name.
|
|
100
|
+
- Docs: `docs/how-to/circleci.md`, `docs/how-to/run-tests-locally.md`, `docs/reference/test.md`
|
|
101
|
+
- Skills: `update-docs`, `verify-ui-changes`
|
|
102
|
+
|
|
103
|
+
- [ ] **Task 3.2**: Android proof for every stable story
|
|
104
|
+
- Delivers: every stable story has an Android flow, accessibility-tree check, and PNG, or an exclusion reason.
|
|
105
|
+
- Files: generated Maestro flows, `demo/rendered-snapshots/android/`, `demo/story-health.json` writer
|
|
106
|
+
- Blocked by: Task 3.1, Task 2.3
|
|
107
|
+
- Acceptance: the Android job without `--only` exits 0. Health on Button includes the Android columns. An excluded story is absent from the Android failure list and its reason is in the summary.
|
|
108
|
+
- Verification: the Android compare and Maestro commands across the stable catalog.
|
|
109
|
+
- Docs: `docs/how-to/compare-demo-snapshots.md`, `docs/reference/test.md`
|
|
110
|
+
- Skills: `update-docs`, `review-chart-visuals`
|
|
111
|
+
|
|
112
|
+
## Phase 4: Major versions
|
|
113
|
+
|
|
114
|
+
- [ ] **Task 4.1**: Version manifest and dropdown
|
|
115
|
+
- Delivers: production stays on the current major, `versions.json` lists released majors and a `preview/<major>` branch, and the top bar dropdown navigates there. The default is the highest released major.
|
|
116
|
+
- Files: `scripts/demo/publishDemoVersions.ts`, `scripts/ci/netlify-deploy.sh`, `demo/components/DemoChrome.tsx`, `docs/how-to/demo-versions.md`, `.circleci/continue-config.yml`
|
|
117
|
+
- Blocked by: Task 1.4
|
|
118
|
+
- Acceptance: a unit test of the manifest builder marks `preview/58` as preview and not current, marks 57 as current, and omits `pr-*` aliases. The dropdown on the home page shows those labels. `versions.json` on the production origin sends CORS headers the aliases can read.
|
|
119
|
+
- Verification: the manifest unit test, a deploy dry-run, and a web demo pass of the dropdown.
|
|
120
|
+
- Docs: `docs/how-to/demo-versions.md`, `demo/README.md`
|
|
121
|
+
- Skills: `update-docs`, `verify-ui-changes`
|
|
122
|
+
|
|
123
|
+
- [ ] **Task 4.2**: Freeze the previous major and backfill 56
|
|
124
|
+
- Delivers: moving master from 57 to a later major freezes 57 on alias `v57`. Alias `v56` serves the last master commit whose package major is 56.
|
|
125
|
+
- Files: `scripts/demo/publishDemoVersions.ts`, `.circleci/continue-config.yml`, `docs/how-to/demo-versions.md`
|
|
126
|
+
- Blocked by: Task 4.1
|
|
127
|
+
- Acceptance: a test with a fake deploy history freezes the previous production deploy when the major increases, and leaves 57 updating on ordinary master commits. The backfill command documents the git revision it deployed for 56.
|
|
128
|
+
- Verification: unit test of the freeze decision, plus the one-time 56 alias deploy recorded in the how-to.
|
|
129
|
+
- Docs: `docs/how-to/demo-versions.md`
|
|
130
|
+
- Skills: `update-docs`
|
|
131
|
+
|
|
132
|
+
- [ ] **Task 4.3**: Docs embeds follow the demo major
|
|
133
|
+
- Delivers: a 57 docs page embeds the 57 demo. The next docs version embeds the 58 preview when the manifest lists it.
|
|
134
|
+
- Files: `website/docusaurus.config.ts`, `website/src/components/ComponentDemo/index.tsx`, `docs/how-to/demo-versions.md`, the docs reference page that describes component embeds
|
|
135
|
+
- Blocked by: Task 4.1
|
|
136
|
+
- Acceptance: `ComponentDemo` for the 57 docs build requests the 57 demo URL with `?embed=1`. When the manifest has an unreleased preview, the next docs build uses that preview URL. A missing preview falls back to production.
|
|
137
|
+
- Verification: a website unit test or build-arg test for the URL choice, and `bun run website:build`.
|
|
138
|
+
- Docs: `docs/how-to/demo-versions.md` and the existing component-embed reference section
|
|
139
|
+
- Skills: `update-docs`
|
|
140
|
+
|
|
141
|
+
## Phase 5: MCP catalog
|
|
142
|
+
|
|
143
|
+
- [ ] **Task 5.1**: Public demo catalog tools
|
|
144
|
+
- Delivers: `terreno_list_demo_stories` and `terreno_get_demo_story` return Button's snippet, route, story ids, and the version manifest URL.
|
|
145
|
+
- Files: `demo/demo-catalog.json`, `demo/package.json`, `mcp-server/src/tools.ts`, MCP tests, `docs/reference/mcp-server.md`
|
|
146
|
+
- Blocked by: Task 1.3, Task 4.1
|
|
147
|
+
- Acceptance: `terreno_get_demo_story` for Button includes `usageExample` and a `/demo/Button` route. The list tool filters by category. The response points at production `versions.json`. A catalog drift test fails when a registered component is missing from the JSON.
|
|
148
|
+
- Verification: `cd mcp-server && bun test` for the new tool tests, and `bun run demo:catalog` followed by the drift check.
|
|
149
|
+
- Docs: `docs/reference/mcp-server.md`, `demo/README.md`
|
|
150
|
+
- Skills: `update-docs`
|
package/package.json
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"@modelcontextprotocol/node": "^2.0.0",
|
|
12
12
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
13
13
|
"@sentry/bun": "^10.39.0",
|
|
14
|
-
"@terreno/api": "57.
|
|
14
|
+
"@terreno/api": "57.8.0",
|
|
15
15
|
"express": "^5.2.1",
|
|
16
16
|
"luxon": "^3.7.2",
|
|
17
17
|
"minisearch": "^7.2.0",
|
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"test:coverage": "bun run ../scripts/check-coverage.ts"
|
|
105
105
|
},
|
|
106
106
|
"type": "module",
|
|
107
|
-
"version": "57.
|
|
107
|
+
"version": "57.8.0"
|
|
108
108
|
}
|
package/src/docs/resources/ui.md
CHANGED
|
@@ -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):
|