@connextar/house 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.md +286 -28
  2. package/dist/changelog/changelog.d.ts +60 -0
  3. package/dist/changelog/changelog.d.ts.map +1 -0
  4. package/dist/changelog/changelog.js +61 -0
  5. package/dist/changelog/changelog.js.map +1 -0
  6. package/dist/changelog/index.d.ts +2 -0
  7. package/dist/changelog/index.d.ts.map +1 -0
  8. package/dist/changelog/index.js +2 -0
  9. package/dist/changelog/index.js.map +1 -0
  10. package/dist/editor/block-handle.d.ts +15 -0
  11. package/dist/editor/block-handle.d.ts.map +1 -0
  12. package/dist/editor/block-handle.js +106 -0
  13. package/dist/editor/block-handle.js.map +1 -0
  14. package/dist/editor/blocks.d.ts +35 -0
  15. package/dist/editor/blocks.d.ts.map +1 -0
  16. package/dist/editor/blocks.js +114 -0
  17. package/dist/editor/blocks.js.map +1 -0
  18. package/dist/editor/bubble-toolbar.d.ts +11 -0
  19. package/dist/editor/bubble-toolbar.d.ts.map +1 -0
  20. package/dist/editor/bubble-toolbar.js +87 -0
  21. package/dist/editor/bubble-toolbar.js.map +1 -0
  22. package/dist/editor/content-index.d.ts +2 -0
  23. package/dist/editor/content-index.d.ts.map +1 -0
  24. package/dist/editor/content-index.js +2 -0
  25. package/dist/editor/content-index.js.map +1 -0
  26. package/dist/editor/content.d.ts +30 -0
  27. package/dist/editor/content.d.ts.map +1 -0
  28. package/dist/editor/content.js +256 -0
  29. package/dist/editor/content.js.map +1 -0
  30. package/dist/editor/editor.d.ts +52 -0
  31. package/dist/editor/editor.d.ts.map +1 -0
  32. package/dist/editor/editor.js +160 -0
  33. package/dist/editor/editor.js.map +1 -0
  34. package/dist/editor/emoji-index.d.ts +2 -0
  35. package/dist/editor/emoji-index.d.ts.map +1 -0
  36. package/dist/editor/emoji-index.js +2 -0
  37. package/dist/editor/emoji-index.js.map +1 -0
  38. package/dist/editor/emoji-picker.d.ts +16 -0
  39. package/dist/editor/emoji-picker.d.ts.map +1 -0
  40. package/dist/editor/emoji-picker.js +37 -0
  41. package/dist/editor/emoji-picker.js.map +1 -0
  42. package/dist/editor/emoji.d.ts +18 -0
  43. package/dist/editor/emoji.d.ts.map +1 -0
  44. package/dist/editor/emoji.js +418 -0
  45. package/dist/editor/emoji.js.map +1 -0
  46. package/dist/editor/index.d.ts +5 -0
  47. package/dist/editor/index.d.ts.map +1 -0
  48. package/dist/editor/index.js +6 -0
  49. package/dist/editor/index.js.map +1 -0
  50. package/dist/editor/suggestion-popup.d.ts +17 -0
  51. package/dist/editor/suggestion-popup.d.ts.map +1 -0
  52. package/dist/editor/suggestion-popup.js +57 -0
  53. package/dist/editor/suggestion-popup.js.map +1 -0
  54. package/dist/editor/suggestions.d.ts +93 -0
  55. package/dist/editor/suggestions.d.ts.map +1 -0
  56. package/dist/editor/suggestions.js +179 -0
  57. package/dist/editor/suggestions.js.map +1 -0
  58. package/dist/editor/ui.d.ts +54 -0
  59. package/dist/editor/ui.d.ts.map +1 -0
  60. package/dist/editor/ui.js +58 -0
  61. package/dist/editor/ui.js.map +1 -0
  62. package/dist/house.css +333 -0
  63. package/dist/internal/cx.d.ts +3 -0
  64. package/dist/internal/cx.d.ts.map +1 -0
  65. package/dist/internal/cx.js +5 -0
  66. package/dist/internal/cx.js.map +1 -0
  67. package/dist/internal/next-link.d.ts +23 -0
  68. package/dist/internal/next-link.d.ts.map +1 -0
  69. package/dist/internal/next-link.js +3 -0
  70. package/dist/internal/next-link.js.map +1 -0
  71. package/dist/markdown/guide.d.ts +98 -0
  72. package/dist/markdown/guide.d.ts.map +1 -0
  73. package/dist/markdown/guide.js +328 -0
  74. package/dist/markdown/guide.js.map +1 -0
  75. package/dist/markdown/index.d.ts +4 -0
  76. package/dist/markdown/index.d.ts.map +1 -0
  77. package/dist/markdown/index.js +4 -0
  78. package/dist/markdown/index.js.map +1 -0
  79. package/dist/markdown/table-view.d.ts +28 -0
  80. package/dist/markdown/table-view.d.ts.map +1 -0
  81. package/dist/markdown/table-view.js +25 -0
  82. package/dist/markdown/table-view.js.map +1 -0
  83. package/dist/markdown/table.d.ts +35 -0
  84. package/dist/markdown/table.d.ts.map +1 -0
  85. package/dist/markdown/table.js +79 -0
  86. package/dist/markdown/table.js.map +1 -0
  87. package/dist/release/changelog.d.ts +72 -0
  88. package/dist/release/changelog.d.ts.map +1 -0
  89. package/dist/release/changelog.js +92 -0
  90. package/dist/release/changelog.js.map +1 -0
  91. package/dist/release/index.d.ts +3 -0
  92. package/dist/release/index.d.ts.map +1 -0
  93. package/dist/release/index.js +3 -0
  94. package/dist/release/index.js.map +1 -0
  95. package/dist/release/version.d.ts +33 -0
  96. package/dist/release/version.d.ts.map +1 -0
  97. package/dist/release/version.js +57 -0
  98. package/dist/release/version.js.map +1 -0
  99. package/dist/trail/index.d.ts +3 -0
  100. package/dist/trail/index.d.ts.map +1 -0
  101. package/dist/trail/index.js +3 -0
  102. package/dist/trail/index.js.map +1 -0
  103. package/dist/trail/rules.d.ts +44 -0
  104. package/dist/trail/rules.d.ts.map +1 -0
  105. package/dist/trail/rules.js +74 -0
  106. package/dist/trail/rules.js.map +1 -0
  107. package/dist/trail/trail.d.ts +49 -0
  108. package/dist/trail/trail.d.ts.map +1 -0
  109. package/dist/trail/trail.js +188 -0
  110. package/dist/trail/trail.js.map +1 -0
  111. package/dist/ux/errors.d.ts +26 -0
  112. package/dist/ux/errors.d.ts.map +1 -0
  113. package/dist/ux/errors.js +33 -0
  114. package/dist/ux/errors.js.map +1 -0
  115. package/dist/ux/feedback.d.ts +23 -0
  116. package/dist/ux/feedback.d.ts.map +1 -0
  117. package/dist/ux/feedback.js +22 -0
  118. package/dist/ux/feedback.js.map +1 -0
  119. package/dist/ux/index.d.ts +20 -0
  120. package/dist/ux/index.d.ts.map +1 -0
  121. package/dist/ux/index.js +20 -0
  122. package/dist/ux/index.js.map +1 -0
  123. package/dist/ux/navigation-index.d.ts +10 -0
  124. package/dist/ux/navigation-index.d.ts.map +1 -0
  125. package/dist/ux/navigation-index.js +10 -0
  126. package/dist/ux/navigation-index.js.map +1 -0
  127. package/dist/ux/navigation-progress.d.ts +53 -0
  128. package/dist/ux/navigation-progress.d.ts.map +1 -0
  129. package/dist/ux/navigation-progress.js +245 -0
  130. package/dist/ux/navigation-progress.js.map +1 -0
  131. package/dist/ux/optimistic.d.ts +139 -0
  132. package/dist/ux/optimistic.d.ts.map +1 -0
  133. package/dist/ux/optimistic.js +242 -0
  134. package/dist/ux/optimistic.js.map +1 -0
  135. package/dist/ux/use-action.d.ts +60 -0
  136. package/dist/ux/use-action.d.ts.map +1 -0
  137. package/dist/ux/use-action.js +100 -0
  138. package/dist/ux/use-action.js.map +1 -0
  139. package/dist/ux/use-optimistic-list.d.ts +69 -0
  140. package/dist/ux/use-optimistic-list.d.ts.map +1 -0
  141. package/dist/ux/use-optimistic-list.js +115 -0
  142. package/dist/ux/use-optimistic-list.js.map +1 -0
  143. package/dist/ux/use-optimistic-value.d.ts +40 -0
  144. package/dist/ux/use-optimistic-value.d.ts.map +1 -0
  145. package/dist/ux/use-optimistic-value.js +68 -0
  146. package/dist/ux/use-optimistic-value.js.map +1 -0
  147. package/docs/ReleaseProcess.md +315 -0
  148. package/package.json +83 -11
package/README.md CHANGED
@@ -12,16 +12,16 @@ small, and the reason it is small is worth reading before adding to it.
12
12
  The portfolio was measured before anything was extracted. Divergence, against
13
13
  Quickie, of files that exist in six or more apps:
14
14
 
15
- | File | copies | divergence |
16
- | --- | --- | --- |
17
- | `error-shell.tsx` | 6/6 | **0%** |
18
- | `check-motion.mjs` | 3/6 | 11% |
19
- | `e2e-local.sh` | 6/6 | 30% |
20
- | `button.tsx` | 6/6 | 42% |
21
- | `dialog.tsx` | 5/6 | 62% |
22
- | `card.tsx` | 5/6 | 106% |
23
- | `lib/api.ts` | 6/6 | 152% |
24
- | `auth.ts` | 4/6 | 42–103% |
15
+ | File | copies | divergence |
16
+ | ------------------ | ------ | ---------- |
17
+ | `error-shell.tsx` | 6/6 | **0%** |
18
+ | `check-motion.mjs` | 3/6 | 11% |
19
+ | `e2e-local.sh` | 6/6 | 30% |
20
+ | `button.tsx` | 6/6 | 42% |
21
+ | `dialog.tsx` | 5/6 | 62% |
22
+ | `card.tsx` | 5/6 | 106% |
23
+ | `lib/api.ts` | 6/6 | 152% |
24
+ | `auth.ts` | 4/6 | 42–103% |
25
25
 
26
26
  Everything below about 15% is an extraction. Everything above it is a rewrite
27
27
  wearing an extraction's clothes, and pulling it in here would mean thirteen apps
@@ -35,6 +35,18 @@ That is why there is no `Button` here, and why the error shell takes its actions
35
35
  as props instead of rendering its own. Bringing a second design system into an
36
36
  app that already has one is a cost with no matching benefit.
37
37
 
38
+ The 0.2.0 modules are the second half of that rule. AltEd built a versioned
39
+ changelog, a navigation trail, a block editor and guide tables; every other app
40
+ was about to write each of them for the second time.
41
+
42
+ `ux`, in 0.3.0, is the clearest case of that clause yet. Every app in the
43
+ portfolio writes over `fetch` and refreshes afterwards, so every one of them has
44
+ the same dead-click problem — a tick that goes nowhere for half a second, a
45
+ button that looks broken while working perfectly — and none of them had solved
46
+ it. It is also the kind of code nobody should write twice: the rules about when
47
+ to let go of an optimistic change are small, subtle and easy to get quietly
48
+ wrong.
49
+
38
50
  ## Modules
39
51
 
40
52
  ### `@connextar/house/errors`
@@ -79,7 +91,7 @@ decorative: a stolen mailbox is still enough.
79
91
 
80
92
  This module decides which routes to refuse, and why. It is here rather than in
81
93
  each app because it has to be the same everywhere and because getting it wrong is
82
- silent — two-factor keeps *looking* enabled while doing nothing.
94
+ silent — two-factor keeps _looking_ enabled while doing nothing.
83
95
 
84
96
  The lookup is injected, because only the app knows its own user table:
85
97
 
@@ -100,7 +112,7 @@ TOTP. A link in an inbox is.
100
112
  ### `@connextar/house/account`
101
113
 
102
114
  The contract for "your data" and "delete my account" — types, the export notice,
103
- and the sole-owner refusal message. The *implementation* stays per-app, because
115
+ and the sole-owner refusal message. The _implementation_ stays per-app, because
104
116
  an export is a walk over that app's Prisma schema. What must not be per-app is
105
117
  the shape of the answer, since that is what a person receives and a regulator
106
118
  reads.
@@ -119,9 +131,234 @@ Two decisions are encoded here rather than left to be rediscovered:
119
131
  App-specific sections go in `extras`, so one app's furniture does not become
120
132
  everyone's obligation.
121
133
 
134
+ ### `@connextar/house/release`
135
+
136
+ Version arithmetic and the release-history guard behind every app's changelog.
137
+ `releaseHistoryProblems(changelog, { pkg, lock })` returns, as sentences, every
138
+ way the changelog, `package.json` and the lockfile disagree — a version that
139
+ skips a step, dates out of order, a release with no title. An app's
140
+ `lib/release.test.ts` asserts it is empty. `nextVersion(current, kind)` applies
141
+ the 0.x rule (a MAJOR-class change bumps the minor before 1.0.0).
142
+
143
+ The process itself — how to classify a change, how to write the notes, the
144
+ runbook — is [`docs/ReleaseProcess.md`](docs/ReleaseProcess.md), shipped in the
145
+ package. Each app's own `docs/operations/ReleaseProcess.md` links to it and adds
146
+ only what is its own: its public API, its trigger documents, its history note.
147
+
148
+ ### `@connextar/house/changelog`
149
+
150
+ `<Changelog releases={CHANGELOG} />` — the body of a `/changelog` page. Each
151
+ release shows its version, whether it was a major, minor or patch release, its
152
+ date and a one-line summary; changes are grouped under headings, each with a bold
153
+ headline and a tag. A list of releases sits beside the content on wide screens
154
+ and becomes a scrolling strip on a phone. The app keeps its hero and calls to
155
+ action around it, and maps the four change types onto its palette with
156
+ `tagClassNames`.
157
+
158
+ ### `@connextar/house/markdown`
159
+
160
+ The help-centre guide renderer, and the GitHub-flavoured table parser under it.
161
+ Five apps carried a copy that had drifted: some rendered tables as a paragraph of
162
+ pipes, some broke a wrapped list line into a stray paragraph, some passed
163
+ `javascript:` links through, and three let an `####` heading shift the numbering
164
+ of the `##` anchors after it. `GuideMarkdown` is the union of the fixes;
165
+ `extractHeadings` gives exactly the ids it renders.
166
+
167
+ An app's `lib/markdown.tsx` becomes a thin binding that keeps its own export names,
168
+ so no page changes:
169
+
170
+ ```tsx
171
+ import { GuideMarkdown } from "@connextar/house/markdown";
172
+
173
+ import { constants } from "@lib/constants";
174
+
175
+ export {
176
+ extractHeadings as extractGuideHeadings,
177
+ extractHowToSteps,
178
+ parseGuideFrontmatter,
179
+ stripInline,
180
+ } from "@connextar/house/markdown";
181
+
182
+ export function Markdown({ content }: { content: string }) {
183
+ return <GuideMarkdown content={content} guidesPath={constants.routes.guides} />;
184
+ }
185
+ ```
186
+
187
+ `classNames` replaces the look of any element for an app without theme tokens.
188
+
189
+ ### `@connextar/house/trail`
190
+
191
+ Where you came from, rather than where the URL says you are. A breadcrumb built
192
+ from the path can only show the hierarchy the routes happen to have; a trail
193
+ shows the chain the person actually walked — a learner, then their plan, then a
194
+ lesson — and keeps it when they follow a reference to somewhere else.
195
+
196
+ Pages declare their chain (`useTrail(chain)` or `<TrailHere chain={…} />`); the
197
+ trail lives per tab in session storage under a `scope`; a link marked
198
+ `data-trail-carry` (every internal link in `RichTextContent`) carries it across.
199
+ `<TrailRail />` draws it as pills down the left on wide screens, `<TrailBar />` as
200
+ a pinned bar with Back on phones.
201
+
202
+ ```tsx
203
+ // components/navigation/dashboard-shell.tsx
204
+ "use client";
205
+ import { ScrollToHash, TrailBar, TrailProvider, TrailRail } from "@connextar/house/trail";
206
+
207
+ // `scope` keeps one signed-in person's trail from surfacing for the next on a shared browser.
208
+ export function DashboardShell({ userId, children }: { userId: string; children: React.ReactNode }) {
209
+ return (
210
+ <TrailProvider scope={userId}>
211
+ <ScrollToHash />
212
+ <div className="flex">
213
+ <TrailRail top="4rem" />
214
+ <main className="min-w-0 flex-1">
215
+ <TrailBar top="4rem" />
216
+ {children}
217
+ </main>
218
+ </div>
219
+ </TrailProvider>
220
+ );
221
+ }
222
+ ```
223
+
224
+ ### `@connextar/house/editor`, `/editor/content`, `/editor/emoji`
225
+
226
+ `RichTextEditor` — Notion-style writing for any prose field. Formatting appears
227
+ as a bubble over a selection (with Turn into); new blocks come from typing `/` or
228
+ the `+` beside a block; every block has a grip to drag or open its menu (turn
229
+ into, duplicate, move up, move down, delete); `:` completes an emoji shortcode.
230
+ `@` references and `#` tags work when the app passes `sources`, because only the
231
+ app knows what its people, records and tags are. **Storage is plain Markdown with
232
+ no new node types** — a reference is `[@Ann Lee](/people/1)` — so every existing
233
+ text column keeps working and a server can read mentions with a regex.
234
+
235
+ `RichTextContent` (from `/editor/content`) is the read-only side: Markdown to
236
+ React with no HTML, an allow-list of link schemes, and bounded patterns, safe for
237
+ text a stranger wrote. `EmojiPicker` and the curated set of ~360 emoji are in
238
+ `/editor` and `/editor/emoji`.
239
+
240
+ The editor draws its own menus and popovers on Radix with the house token
241
+ classes. They are internal and not exported: the apps' primitive sets differ too
242
+ much for the editor to ask for theirs, and this is not a second design system.
243
+
244
+ If an app also depends on `@tiptap/*` directly, keep it in the same range as this
245
+ package so npm installs one copy. Two copies of `@tiptap/pm` break the editor.
246
+
247
+ ### `@connextar/house/ux`
248
+
249
+ The interface answers the click: optimistic updates, pending state, and
250
+ navigation feedback. Headless — no components, no design system, no toast
251
+ library, no HTTP client.
252
+
253
+ `useOptimisticList(rows, { key })` holds the server's rows as a _base_ and every
254
+ change as a _patch_ beside them, folded in on render. `insert`, `update`,
255
+ `remove`, `move` and `bulk` each take a commit function; `isPending(key)` marks
256
+ one row without disabling the table. `useOptimisticValue` is the same for a
257
+ switch, a status or a settings record; `useAction` and `useKeyedAction` are for
258
+ work that should not be guessed at — a payment, a model call, a sign-in — and
259
+ give a control the `pending` flag it needs and nothing more.
260
+
261
+ ```tsx
262
+ const tasks = useOptimisticList(rows, { key: (t) => t.id, onSettled: router.refresh });
263
+
264
+ tasks.update(task.id, { done: true }, () => save(task.id, { done: true }));
265
+ ```
266
+
267
+ **Why not `useOptimistic`.** React 19's hook is right when the write _is_ the
268
+ transition — a server action. It is wrong for an app that writes over `fetch` and
269
+ then calls `router.refresh()`, because `router.refresh()` is not awaitable: the
270
+ transition ends before the new rows arrive and the optimistic value snaps back to
271
+ the stale one, flashing the old answer on every single write.
272
+
273
+ **When a change is let go of.** A patch is `pending` while its write is in
274
+ flight, then dropped (it failed — the base alone is the truth again) or `settled`
275
+ (it landed, and keeps being applied, because the base is still the _old_ rows
276
+ until a refresh arrives). A settled patch goes when fresh rows account for it:
277
+ either they agree, or they disagree with what they said when the write landed, in
278
+ which case they win and the screen corrects itself. Evidence, never a timer — a
279
+ screen that does not refetch has no truth to fall back to, and snapping to stale
280
+ rows after N seconds is a bug that only appears on a slow connection.
281
+
282
+ **The three seams that keep it portable.** A commit is
283
+ `() => Promise<Row | void>` that **throws** on failure, which is the one failure
284
+ contract every client already has; an app whose client returns a result object
285
+ wraps it once. Failures are reported to an `ActionFeedbackProvider` as
286
+ `{ tone, title, description }`, so the app decides what a message looks like.
287
+ And `NavigationProgress` takes a `className` for its bar, so it never needs a
288
+ token.
289
+
290
+ `NavigationProgress` (from `@connextar/house/ux/navigation` — its own entry
291
+ point, so `next` stays an optional peer) also marks the link that was clicked
292
+ with `data-busy`, which `house.css` turns into a spinner. Two notes on it, both
293
+ learned the hard way:
294
+
295
+ - Its click listener is in the **capture** phase, and deliberately does not check
296
+ `defaultPrevented`. Next's `Link` cancels the click on its way to a
297
+ client-side navigation, so a bubble-phase listener guarding on
298
+ `defaultPrevented` fires for every link _except_ the app's own — which is
299
+ precisely backwards, and is what AltEd shipped unnoticed until it was measured
300
+ in a browser.
301
+ - `useAppRouter()` is `useRouter()` with the same feedback on `push` and
302
+ `replace`. Swapping the import is the whole change; no call site moves.
303
+
304
+ **Not everything should be optimistic.** Money, model calls, sign-in, imports and
305
+ exports: anything whose result the person cannot predict, or would be alarmed to
306
+ see undone. Guessing is only honest when the guess is nearly always right.
307
+
308
+ ### Styles — `@connextar/house/house.css`
309
+
310
+ What the components need that utility classes cannot say: rich text, the editor's
311
+ block gutter and suggestion highlight, the trail's tinted pills, and the busy
312
+ spinner `ux` puts on whatever was clicked.
313
+
314
+ The spinner is `[data-busy="true"]::after`, drawn in `currentColor` with no theme
315
+ token. A control that renders its own spinner marks it `.house-spinner` so it
316
+ does not end up with two. For `prefers-reduced-motion` the ring stays and stops
317
+ turning, rather than disappearing — it is the only thing on screen saying the
318
+ click was heard.
319
+
320
+ ## Tailwind setup (required for every component)
321
+
322
+ Tailwind does not scan `node_modules`. Every utility class that appears only
323
+ inside this package is silently missing from an app's build unless the app
324
+ points Tailwind at it. This was measured, not assumed: a probe compile of an
325
+ app's stylesheet emitted none of the package's classes until the line below was
326
+ added.
327
+
328
+ **Tailwind v4** — in the global stylesheet:
329
+
330
+ ```css
331
+ @import "tailwindcss";
332
+ @import "@connextar/house/house.css";
333
+ @source "../node_modules/@connextar/house/dist";
334
+ ```
335
+
336
+ (The `@source` path is relative to the stylesheet.)
337
+
338
+ **Tailwind v3** — add `"./node_modules/@connextar/house/dist/**/*.js"` to
339
+ `content` in `tailwind.config`, and import `@connextar/house/house.css` at the top
340
+ of the global stylesheet, before the `@tailwind` directives.
341
+
342
+ `house.css` reads the app's theme tokens (`--foreground`, `--muted-foreground`,
343
+ `--primary`, `--muted`, `--border`, `--card`, `--accent`) through a `--house-*`
344
+ variable at each use. An app that stores tokens as HSL triplets
345
+ (`--primary: 222 47% 11%`) maps them once:
346
+
347
+ ```css
348
+ :root {
349
+ --house-foreground: hsl(var(--foreground));
350
+ --house-muted-foreground: hsl(var(--muted-foreground));
351
+ --house-primary: hsl(var(--primary));
352
+ --house-muted: hsl(var(--muted));
353
+ --house-border: hsl(var(--border));
354
+ --house-card: hsl(var(--card));
355
+ --house-accent: hsl(var(--accent));
356
+ }
357
+ ```
358
+
122
359
  ## Publishing
123
360
 
124
- The scope is public and the contents are a design system and two policy modules —
361
+ The scope is public and the contents are shared components and policy modules —
125
362
  nothing here is a secret. Public publishing is what keeps thirteen repositories
126
363
  across two GitHub owners able to install it with no token in any Vercel project,
127
364
  which is the failure mode that would otherwise take every deploy down at once.
@@ -136,24 +373,45 @@ npm publish # publishConfig.access is already "public"
136
373
 
137
374
  ## Adopting it in an app
138
375
 
139
- Once published:
140
-
141
376
  ```bash
142
377
  npm install @connextar/house
143
378
  ```
144
379
 
145
- Then replace the local copies. For Quickie the exact change is saved as a patch,
146
- generated from the working migration and verified against a real packed tarball:
147
-
148
- ```bash
149
- git apply ../../Shared/cx-house/migrations/quickie-adopt-house.patch
150
- npm run check && npm test && npm run build
151
- ```
152
-
153
- For the other apps the shape is the same: `global-error.tsx` becomes a call to
154
- `GlobalErrorShell`, `components/errors/error-shell.tsx` re-exports `ErrorShell`
155
- and keeps only its own `ErrorAction`, and any two-factor policy defers to
156
- `@connextar/house/auth`.
380
+ Then, per module the app takes up:
381
+
382
+ 1. **Tailwind first.** Add the `@source` line (or the v3 `content` glob) from
383
+ [Tailwind setup](#tailwind-setup-required-for-every-component). Without it the
384
+ components render unstyled and nothing errors.
385
+ 2. **Replace the local copy with a binding, not a rewrite of its callers.** The
386
+ app's `lib/markdown.tsx`, `components/editor/rich-text-editor.tsx` or
387
+ `lib/changelog.ts` stays where its importers expect it and shrinks to the
388
+ app's own part: its routes, its type scale, its `@` and `#` lookups, its data.
389
+ A binding that grows past a screen is the package being re-implemented.
390
+ 3. **Move the tests with the code.** A local test of behaviour the package now
391
+ owns is deleted; a test of the app's binding stays.
392
+ 4. **Changelog and releases** follow [`docs/ReleaseProcess.md`](docs/ReleaseProcess.md)
393
+ §7: the old entries become numbered releases once, `lib/release.test.ts` guards
394
+ them from then on, and the app's own `docs/operations/ReleaseProcess.md` says
395
+ only what is its own.
396
+ 5. **Gates, then the pages.** `npm run check`, `npm test`, `npm run build`, and
397
+ grep the built CSS for a class that only the package uses — the one check that
398
+ proves step 1.
399
+
400
+ Traps found adopting it the first time:
401
+
402
+ - A **server component** receives a client module as a reference that throws on
403
+ any property access. Never dot into an import from a client module in shared
404
+ code; the package's own `next/link` helper is a type-only cast for this reason.
405
+ - Next writes route types under `.next/dev/types` during `next dev`. After routes
406
+ are deleted, a stale copy fails `next build`'s type check with "Cannot find
407
+ module"; delete `.next/dev` when no dev server is running.
408
+ - If the app also depends on `@tiptap/*` directly, keep the range aligned or drop
409
+ the direct dependency — two copies of `@tiptap/pm` break the editor.
410
+ - **Jest** (with `next/jest`) does not transform `node_modules`, and this package is
411
+ ESM, so the first import fails with `Unexpected token 'export'`. `next/jest` only
412
+ appends to `transformIgnorePatterns`, so export an async config that rewrites each
413
+ `/node_modules/(?!` rule to `/node_modules/(?!@connextar/house/)(?!`. Vitest needs
414
+ nothing.
157
415
 
158
416
  **A note on local development.** Installing this with `npm install ../path` makes
159
417
  a symlink, and Next's Turbopack will not resolve a package whose real path is
@@ -163,7 +421,7 @@ behaves exactly like a registry install:
163
421
 
164
422
  ```bash
165
423
  npm pack --pack-destination /tmp # in this repo
166
- npm install /tmp/connextar-house-0.1.0.tgz # in the app
424
+ npm install /tmp/connextar-house-0.2.0.tgz # in the app
167
425
  ```
168
426
 
169
427
  ## Adding to it
@@ -0,0 +1,60 @@
1
+ import type { ReactNode } from "react";
2
+ import { type ChangeType, type ChangelogRelease } from "../release/changelog.js";
3
+ import { type ReleaseKind } from "../release/version.js";
4
+ /**
5
+ * The body of a /changelog page: an index of releases beside the releases
6
+ * themselves.
7
+ *
8
+ * Each release shows its version, whether it is a major, minor or patch
9
+ * release, and its date, then a headline and a one-sentence summary. Changes
10
+ * sit under the product-area headings they were grouped by, each with a tag
11
+ * and a bold headline. The index is a sticky rail on wide screens and a
12
+ * scrolling strip of versions on a phone.
13
+ *
14
+ * Only the body: the page's header, hero, footer and metadata are the app's,
15
+ * because that is where every product already differs. Server-safe, no hooks.
16
+ *
17
+ * Tag colours default to tokens every house app has (`primary`, `accent`,
18
+ * `muted`, `border`). An app with `success` and `warning` tokens can pass
19
+ * `tagClassNames` for the fuller palette.
20
+ */
21
+ export declare const DEFAULT_TAG_CLASS_NAMES: Record<ChangeType, string>;
22
+ export declare const TAG_LABELS: Record<ChangeType, string>;
23
+ export declare const KIND_LABELS: Record<ReleaseKind, string>;
24
+ export interface ChangelogProps {
25
+ releases: readonly ChangelogRelease[];
26
+ /** Tag classes per change type; merged over the defaults. */
27
+ tagClassNames?: Partial<Record<ChangeType, string>>;
28
+ /** The release headline. Defaults to a plain 2xl semibold heading. */
29
+ releaseTitleClassName?: string;
30
+ /** The small uppercase label above the index. */
31
+ eyebrowClassName?: string;
32
+ /** The one-sentence summary under a release headline. */
33
+ summaryClassName?: string;
34
+ /** The "Latest" pill's colours. */
35
+ latestClassName?: string;
36
+ /** A section heading ("Notes", "Fixes"). */
37
+ sectionHeadingClassName?: string;
38
+ /** The "3 changes" count beside a section heading. */
39
+ sectionCountClassName?: string;
40
+ /**
41
+ * How a change's text is drawn. Defaults to rendering `**bold**` as strong —
42
+ * release notes name UI labels that way — and everything else as text.
43
+ */
44
+ renderText?: (text: string) => ReactNode;
45
+ /** Locale for release dates. */
46
+ locale?: string;
47
+ /** How far below the top a sticky index sits, and where an anchor lands. */
48
+ stickyTop?: string;
49
+ className?: string;
50
+ }
51
+ /** `**label**` → <strong>label</strong>; nothing else is interpreted. */
52
+ export declare function renderReleaseText(text: string): ReactNode;
53
+ export declare function Changelog({ releases, tagClassNames, releaseTitleClassName, eyebrowClassName, summaryClassName, latestClassName, sectionHeadingClassName, sectionCountClassName, renderText, locale, stickyTop, className, }: ChangelogProps): import("react").JSX.Element;
54
+ /** Figures for a page hero: the latest version, how many releases, how many changes. */
55
+ export declare function changelogStats(releases: readonly ChangelogRelease[]): {
56
+ latest: string | null;
57
+ releases: number;
58
+ changes: number;
59
+ };
60
+ //# sourceMappingURL=changelog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.d.ts","sourceRoot":"","sources":["../../src/changelog/changelog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEtE;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAK9D,CAAC;AAEF,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAKjD,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAInD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,6DAA6D;IAC7D,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,sEAAsE;IACtE,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iDAAiD;IACjD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mCAAmC;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,sDAAsD;IACtD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,SAAS,CAAC;IACzC,gCAAgC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAWzD;AAED,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,aAAa,EACb,qBAAwF,EACxF,gBAA4F,EAC5F,gBAAgF,EAChF,eAAqE,EACrE,uBAAyF,EACzF,qBAAmG,EACnG,UAA8B,EAC9B,MAAgB,EAChB,SAAkB,EAClB,SAAS,GACV,EAAE,cAAc,+BA0GhB;AAED,wFAAwF;AACxF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG;IACrE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAMA"}
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cx } from "../internal/cx.js";
3
+ import { countChanges, formatReleaseDate, releaseAnchor, } from "../release/changelog.js";
4
+ import { releaseKind } from "../release/version.js";
5
+ /**
6
+ * The body of a /changelog page: an index of releases beside the releases
7
+ * themselves.
8
+ *
9
+ * Each release shows its version, whether it is a major, minor or patch
10
+ * release, and its date, then a headline and a one-sentence summary. Changes
11
+ * sit under the product-area headings they were grouped by, each with a tag
12
+ * and a bold headline. The index is a sticky rail on wide screens and a
13
+ * scrolling strip of versions on a phone.
14
+ *
15
+ * Only the body: the page's header, hero, footer and metadata are the app's,
16
+ * because that is where every product already differs. Server-safe, no hooks.
17
+ *
18
+ * Tag colours default to tokens every house app has (`primary`, `accent`,
19
+ * `muted`, `border`). An app with `success` and `warning` tokens can pass
20
+ * `tagClassNames` for the fuller palette.
21
+ */
22
+ export const DEFAULT_TAG_CLASS_NAMES = {
23
+ new: "border-primary/40 bg-primary/10 text-primary",
24
+ improved: "border-border bg-accent text-accent-foreground",
25
+ changed: "border-foreground/30 bg-background text-foreground",
26
+ fixed: "border-border bg-muted text-muted-foreground",
27
+ };
28
+ export const TAG_LABELS = {
29
+ new: "New",
30
+ improved: "Improved",
31
+ changed: "Changed",
32
+ fixed: "Fixed",
33
+ };
34
+ export const KIND_LABELS = {
35
+ major: "Major release",
36
+ minor: "Minor release",
37
+ patch: "Patch release",
38
+ };
39
+ /** `**label**` → <strong>label</strong>; nothing else is interpreted. */
40
+ export function renderReleaseText(text) {
41
+ const parts = text.split(/(\*\*[^*\n]+\*\*)/g);
42
+ return parts.map((part, i) => part.startsWith("**") && part.endsWith("**") && part.length > 4 ? (_jsx("strong", { className: "font-semibold text-foreground", children: part.slice(2, -2) }, i)) : (part));
43
+ }
44
+ export function Changelog({ releases, tagClassNames, releaseTitleClassName = "text-2xl font-semibold tracking-tight text-balance sm:text-3xl", eyebrowClassName = "text-xs font-semibold tracking-[0.14em] text-muted-foreground uppercase", summaryClassName = "max-w-prose text-base leading-relaxed text-muted-foreground", latestClassName = "border-primary bg-primary text-primary-foreground", sectionHeadingClassName = "text-xs font-bold tracking-[0.14em] text-foreground uppercase", sectionCountClassName = "text-[11px] font-medium tracking-normal text-muted-foreground normal-case", renderText = renderReleaseText, locale = "en-GB", stickyTop = "7rem", className, }) {
45
+ const tags = { ...DEFAULT_TAG_CLASS_NAMES, ...tagClassNames };
46
+ return (_jsxs("div", { className: cx("mx-auto grid max-w-6xl gap-10 px-4 py-12 lg:grid-cols-[14rem_minmax(0,1fr)] lg:gap-16", className), children: [_jsx("nav", { "aria-label": "Releases", className: "min-w-0", children: _jsxs("div", { className: "lg:sticky", style: { top: stickyTop }, children: [_jsx("p", { className: eyebrowClassName, children: "Releases" }), _jsx("ol", { className: "-mx-4 mt-4 flex gap-2 overflow-x-auto px-4 pb-2 lg:mx-0 lg:block lg:max-h-[calc(100vh-10rem)] lg:space-y-0.5 lg:overflow-y-auto lg:border-l lg:border-border lg:px-0 lg:pb-0", children: releases.map((release) => (_jsx("li", { className: "shrink-0", children: _jsxs("a", { href: `#${releaseAnchor(release.version)}`, className: "block rounded-full border border-border px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:border-primary hover:text-foreground lg:-ml-px lg:rounded-none lg:border-0 lg:border-l-2 lg:border-transparent lg:py-1.5 lg:pr-2 lg:pl-4", children: [_jsxs("span", { className: "font-mono text-xs font-semibold text-foreground", children: ["v", release.version] }), _jsx("span", { className: "hidden truncate lg:block", children: release.title })] }) }, release.version))) })] }) }), _jsx("div", { className: "min-w-0 space-y-20", children: releases.map((release, i) => {
47
+ const anchor = releaseAnchor(release.version);
48
+ const previous = releases[i + 1];
49
+ const kind = previous ? releaseKind(previous.version, release.version) : null;
50
+ return (_jsxs("article", { id: anchor, "aria-labelledby": `${anchor}-title`, style: { scrollMarginTop: stickyTop }, children: [_jsxs("header", { className: "border-b border-border pb-7", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2", children: [_jsxs("span", { className: "rounded-full border border-primary/40 bg-primary/10 px-2.5 py-0.5 font-mono text-xs font-semibold text-primary", children: ["v", release.version] }), i === 0 ? (_jsx("span", { className: cx("rounded-full border px-2.5 py-0.5 text-[11px] font-semibold", latestClassName), children: "Latest" })) : null, kind ? _jsx("span", { className: "text-xs text-muted-foreground", children: KIND_LABELS[kind] }) : null, _jsx("span", { className: "text-xs text-muted-foreground", "aria-hidden": "true", children: "\u00B7" }), _jsx("time", { dateTime: release.date, className: "text-xs text-muted-foreground", children: formatReleaseDate(release.date, locale) })] }), _jsx("h2", { id: `${anchor}-title`, className: cx("mt-4", releaseTitleClassName), children: release.title }), _jsx("p", { className: cx("mt-3", summaryClassName), children: release.summary })] }), _jsx("div", { className: "mt-9 space-y-10", children: release.sections.map((section) => (_jsxs("section", { "aria-label": `${section.heading} — v${release.version}`, children: [_jsxs("h3", { className: cx("flex items-baseline gap-2", sectionHeadingClassName), children: [section.heading, _jsxs("span", { className: sectionCountClassName, children: [section.changes.length, " ", section.changes.length === 1 ? "change" : "changes"] })] }), _jsx("ul", { className: "mt-4 divide-y divide-border overflow-hidden rounded-2xl border border-border bg-card", children: section.changes.map((change) => (_jsxs("li", { className: "grid gap-2.5 p-5 sm:grid-cols-[5.5rem_minmax(0,1fr)] sm:gap-5", children: [_jsx("span", { className: cx("inline-flex h-fit w-fit items-center rounded-full border px-2 py-0.5 text-[11px] font-semibold", tags[change.type]), children: TAG_LABELS[change.type] }), _jsxs("div", { children: [_jsx("p", { className: "leading-snug font-semibold text-card-foreground", children: change.title }), _jsx("p", { className: "mt-1.5 text-sm leading-relaxed text-muted-foreground", children: renderText(change.text) })] })] }, change.title))) })] }, section.heading))) })] }, release.version));
51
+ }) })] }));
52
+ }
53
+ /** Figures for a page hero: the latest version, how many releases, how many changes. */
54
+ export function changelogStats(releases) {
55
+ return {
56
+ latest: releases[0]?.version ?? null,
57
+ releases: releases.length,
58
+ changes: releases.reduce((n, release) => n + countChanges(release), 0),
59
+ };
60
+ }
61
+ //# sourceMappingURL=changelog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changelog.js","sourceRoot":"","sources":["../../src/changelog/changelog.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { Changelog, changelogStats, DEFAULT_TAG_CLASS_NAMES, KIND_LABELS, renderReleaseText, TAG_LABELS, type ChangelogProps, } from "./changelog.js";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/changelog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,cAAc,EACd,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Changelog, changelogStats, DEFAULT_TAG_CLASS_NAMES, KIND_LABELS, renderReleaseText, TAG_LABELS, } from "./changelog.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/changelog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,cAAc,EACd,uBAAuB,EACvB,WAAW,EACX,iBAAiB,EACjB,UAAU,GAEX,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { Editor } from "@tiptap/react";
2
+ /**
3
+ * The gutter beside whichever block the pointer is over: a `+` that starts a new
4
+ * block underneath (with the `/` menu already open, as Notion does) and a grip
5
+ * that drags the block to reorder it or, clicked, opens the block menu — turn
6
+ * into, duplicate, move up, move down, delete.
7
+ *
8
+ * Everything works on the block's document position rather than the caret, so a
9
+ * block can be acted on without ever being clicked into. The drag itself is the
10
+ * extension's: it understands nested lists and shows the drop cursor.
11
+ */
12
+ export declare function BlockHandle({ editor }: {
13
+ editor: Editor;
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=block-handle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-handle.d.ts","sourceRoot":"","sources":["../../src/editor/block-handle.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AA+B5C;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,+BAuJzD"}