@connextar/house 0.1.1 → 0.2.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/README.md +205 -28
- package/dist/changelog/changelog.d.ts +60 -0
- package/dist/changelog/changelog.d.ts.map +1 -0
- package/dist/changelog/changelog.js +61 -0
- package/dist/changelog/changelog.js.map +1 -0
- package/dist/changelog/index.d.ts +2 -0
- package/dist/changelog/index.d.ts.map +1 -0
- package/dist/changelog/index.js +2 -0
- package/dist/changelog/index.js.map +1 -0
- package/dist/editor/block-handle.d.ts +15 -0
- package/dist/editor/block-handle.d.ts.map +1 -0
- package/dist/editor/block-handle.js +106 -0
- package/dist/editor/block-handle.js.map +1 -0
- package/dist/editor/blocks.d.ts +35 -0
- package/dist/editor/blocks.d.ts.map +1 -0
- package/dist/editor/blocks.js +114 -0
- package/dist/editor/blocks.js.map +1 -0
- package/dist/editor/bubble-toolbar.d.ts +11 -0
- package/dist/editor/bubble-toolbar.d.ts.map +1 -0
- package/dist/editor/bubble-toolbar.js +87 -0
- package/dist/editor/bubble-toolbar.js.map +1 -0
- package/dist/editor/content-index.d.ts +2 -0
- package/dist/editor/content-index.d.ts.map +1 -0
- package/dist/editor/content-index.js +2 -0
- package/dist/editor/content-index.js.map +1 -0
- package/dist/editor/content.d.ts +30 -0
- package/dist/editor/content.d.ts.map +1 -0
- package/dist/editor/content.js +256 -0
- package/dist/editor/content.js.map +1 -0
- package/dist/editor/editor.d.ts +52 -0
- package/dist/editor/editor.d.ts.map +1 -0
- package/dist/editor/editor.js +160 -0
- package/dist/editor/editor.js.map +1 -0
- package/dist/editor/emoji-index.d.ts +2 -0
- package/dist/editor/emoji-index.d.ts.map +1 -0
- package/dist/editor/emoji-index.js +2 -0
- package/dist/editor/emoji-index.js.map +1 -0
- package/dist/editor/emoji-picker.d.ts +16 -0
- package/dist/editor/emoji-picker.d.ts.map +1 -0
- package/dist/editor/emoji-picker.js +37 -0
- package/dist/editor/emoji-picker.js.map +1 -0
- package/dist/editor/emoji.d.ts +18 -0
- package/dist/editor/emoji.d.ts.map +1 -0
- package/dist/editor/emoji.js +418 -0
- package/dist/editor/emoji.js.map +1 -0
- package/dist/editor/index.d.ts +5 -0
- package/dist/editor/index.d.ts.map +1 -0
- package/dist/editor/index.js +6 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/suggestion-popup.d.ts +17 -0
- package/dist/editor/suggestion-popup.d.ts.map +1 -0
- package/dist/editor/suggestion-popup.js +57 -0
- package/dist/editor/suggestion-popup.js.map +1 -0
- package/dist/editor/suggestions.d.ts +93 -0
- package/dist/editor/suggestions.d.ts.map +1 -0
- package/dist/editor/suggestions.js +179 -0
- package/dist/editor/suggestions.js.map +1 -0
- package/dist/editor/ui.d.ts +54 -0
- package/dist/editor/ui.d.ts.map +1 -0
- package/dist/editor/ui.js +58 -0
- package/dist/editor/ui.js.map +1 -0
- package/dist/house.css +267 -0
- package/dist/internal/cx.d.ts +3 -0
- package/dist/internal/cx.d.ts.map +1 -0
- package/dist/internal/cx.js +5 -0
- package/dist/internal/cx.js.map +1 -0
- package/dist/internal/next-link.d.ts +23 -0
- package/dist/internal/next-link.d.ts.map +1 -0
- package/dist/internal/next-link.js +3 -0
- package/dist/internal/next-link.js.map +1 -0
- package/dist/markdown/guide.d.ts +98 -0
- package/dist/markdown/guide.d.ts.map +1 -0
- package/dist/markdown/guide.js +328 -0
- package/dist/markdown/guide.js.map +1 -0
- package/dist/markdown/index.d.ts +4 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +4 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markdown/table-view.d.ts +28 -0
- package/dist/markdown/table-view.d.ts.map +1 -0
- package/dist/markdown/table-view.js +25 -0
- package/dist/markdown/table-view.js.map +1 -0
- package/dist/markdown/table.d.ts +35 -0
- package/dist/markdown/table.d.ts.map +1 -0
- package/dist/markdown/table.js +79 -0
- package/dist/markdown/table.js.map +1 -0
- package/dist/release/changelog.d.ts +72 -0
- package/dist/release/changelog.d.ts.map +1 -0
- package/dist/release/changelog.js +92 -0
- package/dist/release/changelog.js.map +1 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +3 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/version.d.ts +33 -0
- package/dist/release/version.d.ts.map +1 -0
- package/dist/release/version.js +57 -0
- package/dist/release/version.js.map +1 -0
- package/dist/trail/index.d.ts +3 -0
- package/dist/trail/index.d.ts.map +1 -0
- package/dist/trail/index.js +3 -0
- package/dist/trail/index.js.map +1 -0
- package/dist/trail/rules.d.ts +44 -0
- package/dist/trail/rules.d.ts.map +1 -0
- package/dist/trail/rules.js +74 -0
- package/dist/trail/rules.js.map +1 -0
- package/dist/trail/trail.d.ts +49 -0
- package/dist/trail/trail.d.ts.map +1 -0
- package/dist/trail/trail.js +188 -0
- package/dist/trail/trail.js.map +1 -0
- package/docs/ReleaseProcess.md +315 -0
- package/package.json +75 -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
|
|
16
|
-
|
|
|
17
|
-
| `error-shell.tsx`
|
|
18
|
-
| `check-motion.mjs` | 3/6
|
|
19
|
-
| `e2e-local.sh`
|
|
20
|
-
| `button.tsx`
|
|
21
|
-
| `dialog.tsx`
|
|
22
|
-
| `card.tsx`
|
|
23
|
-
| `lib/api.ts`
|
|
24
|
-
| `auth.ts`
|
|
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,10 @@ 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
|
+
|
|
38
42
|
## Modules
|
|
39
43
|
|
|
40
44
|
### `@connextar/house/errors`
|
|
@@ -79,7 +83,7 @@ decorative: a stolen mailbox is still enough.
|
|
|
79
83
|
|
|
80
84
|
This module decides which routes to refuse, and why. It is here rather than in
|
|
81
85
|
each app because it has to be the same everywhere and because getting it wrong is
|
|
82
|
-
silent — two-factor keeps
|
|
86
|
+
silent — two-factor keeps _looking_ enabled while doing nothing.
|
|
83
87
|
|
|
84
88
|
The lookup is injected, because only the app knows its own user table:
|
|
85
89
|
|
|
@@ -100,7 +104,7 @@ TOTP. A link in an inbox is.
|
|
|
100
104
|
### `@connextar/house/account`
|
|
101
105
|
|
|
102
106
|
The contract for "your data" and "delete my account" — types, the export notice,
|
|
103
|
-
and the sole-owner refusal message. The
|
|
107
|
+
and the sole-owner refusal message. The _implementation_ stays per-app, because
|
|
104
108
|
an export is a walk over that app's Prisma schema. What must not be per-app is
|
|
105
109
|
the shape of the answer, since that is what a person receives and a regulator
|
|
106
110
|
reads.
|
|
@@ -119,9 +123,166 @@ Two decisions are encoded here rather than left to be rediscovered:
|
|
|
119
123
|
App-specific sections go in `extras`, so one app's furniture does not become
|
|
120
124
|
everyone's obligation.
|
|
121
125
|
|
|
126
|
+
### `@connextar/house/release`
|
|
127
|
+
|
|
128
|
+
Version arithmetic and the release-history guard behind every app's changelog.
|
|
129
|
+
`releaseHistoryProblems(changelog, { pkg, lock })` returns, as sentences, every
|
|
130
|
+
way the changelog, `package.json` and the lockfile disagree — a version that
|
|
131
|
+
skips a step, dates out of order, a release with no title. An app's
|
|
132
|
+
`lib/release.test.ts` asserts it is empty. `nextVersion(current, kind)` applies
|
|
133
|
+
the 0.x rule (a MAJOR-class change bumps the minor before 1.0.0).
|
|
134
|
+
|
|
135
|
+
The process itself — how to classify a change, how to write the notes, the
|
|
136
|
+
runbook — is [`docs/ReleaseProcess.md`](docs/ReleaseProcess.md), shipped in the
|
|
137
|
+
package. Each app's own `docs/operations/ReleaseProcess.md` links to it and adds
|
|
138
|
+
only what is its own: its public API, its trigger documents, its history note.
|
|
139
|
+
|
|
140
|
+
### `@connextar/house/changelog`
|
|
141
|
+
|
|
142
|
+
`<Changelog releases={CHANGELOG} />` — the body of a `/changelog` page. Each
|
|
143
|
+
release shows its version, whether it was a major, minor or patch release, its
|
|
144
|
+
date and a one-line summary; changes are grouped under headings, each with a bold
|
|
145
|
+
headline and a tag. A list of releases sits beside the content on wide screens
|
|
146
|
+
and becomes a scrolling strip on a phone. The app keeps its hero and calls to
|
|
147
|
+
action around it, and maps the four change types onto its palette with
|
|
148
|
+
`tagClassNames`.
|
|
149
|
+
|
|
150
|
+
### `@connextar/house/markdown`
|
|
151
|
+
|
|
152
|
+
The help-centre guide renderer, and the GitHub-flavoured table parser under it.
|
|
153
|
+
Five apps carried a copy that had drifted: some rendered tables as a paragraph of
|
|
154
|
+
pipes, some broke a wrapped list line into a stray paragraph, some passed
|
|
155
|
+
`javascript:` links through, and three let an `####` heading shift the numbering
|
|
156
|
+
of the `##` anchors after it. `GuideMarkdown` is the union of the fixes;
|
|
157
|
+
`extractHeadings` gives exactly the ids it renders.
|
|
158
|
+
|
|
159
|
+
An app's `lib/markdown.tsx` becomes a thin binding that keeps its own export names,
|
|
160
|
+
so no page changes:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { GuideMarkdown } from "@connextar/house/markdown";
|
|
164
|
+
|
|
165
|
+
import { constants } from "@lib/constants";
|
|
166
|
+
|
|
167
|
+
export {
|
|
168
|
+
extractHeadings as extractGuideHeadings,
|
|
169
|
+
extractHowToSteps,
|
|
170
|
+
parseGuideFrontmatter,
|
|
171
|
+
stripInline,
|
|
172
|
+
} from "@connextar/house/markdown";
|
|
173
|
+
|
|
174
|
+
export function Markdown({ content }: { content: string }) {
|
|
175
|
+
return <GuideMarkdown content={content} guidesPath={constants.routes.guides} />;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`classNames` replaces the look of any element for an app without theme tokens.
|
|
180
|
+
|
|
181
|
+
### `@connextar/house/trail`
|
|
182
|
+
|
|
183
|
+
Where you came from, rather than where the URL says you are. A breadcrumb built
|
|
184
|
+
from the path can only show the hierarchy the routes happen to have; a trail
|
|
185
|
+
shows the chain the person actually walked — a learner, then their plan, then a
|
|
186
|
+
lesson — and keeps it when they follow a reference to somewhere else.
|
|
187
|
+
|
|
188
|
+
Pages declare their chain (`useTrail(chain)` or `<TrailHere chain={…} />`); the
|
|
189
|
+
trail lives per tab in session storage under a `scope`; a link marked
|
|
190
|
+
`data-trail-carry` (every internal link in `RichTextContent`) carries it across.
|
|
191
|
+
`<TrailRail />` draws it as pills down the left on wide screens, `<TrailBar />` as
|
|
192
|
+
a pinned bar with Back on phones.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
// components/navigation/dashboard-shell.tsx
|
|
196
|
+
"use client";
|
|
197
|
+
import { ScrollToHash, TrailBar, TrailProvider, TrailRail } from "@connextar/house/trail";
|
|
198
|
+
|
|
199
|
+
// `scope` keeps one signed-in person's trail from surfacing for the next on a shared browser.
|
|
200
|
+
export function DashboardShell({ userId, children }: { userId: string; children: React.ReactNode }) {
|
|
201
|
+
return (
|
|
202
|
+
<TrailProvider scope={userId}>
|
|
203
|
+
<ScrollToHash />
|
|
204
|
+
<div className="flex">
|
|
205
|
+
<TrailRail top="4rem" />
|
|
206
|
+
<main className="min-w-0 flex-1">
|
|
207
|
+
<TrailBar top="4rem" />
|
|
208
|
+
{children}
|
|
209
|
+
</main>
|
|
210
|
+
</div>
|
|
211
|
+
</TrailProvider>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### `@connextar/house/editor`, `/editor/content`, `/editor/emoji`
|
|
217
|
+
|
|
218
|
+
`RichTextEditor` — Notion-style writing for any prose field. Formatting appears
|
|
219
|
+
as a bubble over a selection (with Turn into); new blocks come from typing `/` or
|
|
220
|
+
the `+` beside a block; every block has a grip to drag or open its menu (turn
|
|
221
|
+
into, duplicate, move up, move down, delete); `:` completes an emoji shortcode.
|
|
222
|
+
`@` references and `#` tags work when the app passes `sources`, because only the
|
|
223
|
+
app knows what its people, records and tags are. **Storage is plain Markdown with
|
|
224
|
+
no new node types** — a reference is `[@Ann Lee](/people/1)` — so every existing
|
|
225
|
+
text column keeps working and a server can read mentions with a regex.
|
|
226
|
+
|
|
227
|
+
`RichTextContent` (from `/editor/content`) is the read-only side: Markdown to
|
|
228
|
+
React with no HTML, an allow-list of link schemes, and bounded patterns, safe for
|
|
229
|
+
text a stranger wrote. `EmojiPicker` and the curated set of ~360 emoji are in
|
|
230
|
+
`/editor` and `/editor/emoji`.
|
|
231
|
+
|
|
232
|
+
The editor draws its own menus and popovers on Radix with the house token
|
|
233
|
+
classes. They are internal and not exported: the apps' primitive sets differ too
|
|
234
|
+
much for the editor to ask for theirs, and this is not a second design system.
|
|
235
|
+
|
|
236
|
+
If an app also depends on `@tiptap/*` directly, keep it in the same range as this
|
|
237
|
+
package so npm installs one copy. Two copies of `@tiptap/pm` break the editor.
|
|
238
|
+
|
|
239
|
+
### Styles — `@connextar/house/house.css`
|
|
240
|
+
|
|
241
|
+
What the components need that utility classes cannot say: rich text, the editor's
|
|
242
|
+
block gutter and suggestion highlight, the trail's tinted pills.
|
|
243
|
+
|
|
244
|
+
## Tailwind setup (required for every component)
|
|
245
|
+
|
|
246
|
+
Tailwind does not scan `node_modules`. Every utility class that appears only
|
|
247
|
+
inside this package is silently missing from an app's build unless the app
|
|
248
|
+
points Tailwind at it. This was measured, not assumed: a probe compile of an
|
|
249
|
+
app's stylesheet emitted none of the package's classes until the line below was
|
|
250
|
+
added.
|
|
251
|
+
|
|
252
|
+
**Tailwind v4** — in the global stylesheet:
|
|
253
|
+
|
|
254
|
+
```css
|
|
255
|
+
@import "tailwindcss";
|
|
256
|
+
@import "@connextar/house/house.css";
|
|
257
|
+
@source "../node_modules/@connextar/house/dist";
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
(The `@source` path is relative to the stylesheet.)
|
|
261
|
+
|
|
262
|
+
**Tailwind v3** — add `"./node_modules/@connextar/house/dist/**/*.js"` to
|
|
263
|
+
`content` in `tailwind.config`, and import `@connextar/house/house.css` at the top
|
|
264
|
+
of the global stylesheet, before the `@tailwind` directives.
|
|
265
|
+
|
|
266
|
+
`house.css` reads the app's theme tokens (`--foreground`, `--muted-foreground`,
|
|
267
|
+
`--primary`, `--muted`, `--border`, `--card`, `--accent`) through a `--house-*`
|
|
268
|
+
variable at each use. An app that stores tokens as HSL triplets
|
|
269
|
+
(`--primary: 222 47% 11%`) maps them once:
|
|
270
|
+
|
|
271
|
+
```css
|
|
272
|
+
:root {
|
|
273
|
+
--house-foreground: hsl(var(--foreground));
|
|
274
|
+
--house-muted-foreground: hsl(var(--muted-foreground));
|
|
275
|
+
--house-primary: hsl(var(--primary));
|
|
276
|
+
--house-muted: hsl(var(--muted));
|
|
277
|
+
--house-border: hsl(var(--border));
|
|
278
|
+
--house-card: hsl(var(--card));
|
|
279
|
+
--house-accent: hsl(var(--accent));
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
122
283
|
## Publishing
|
|
123
284
|
|
|
124
|
-
The scope is public and the contents are
|
|
285
|
+
The scope is public and the contents are shared components and policy modules —
|
|
125
286
|
nothing here is a secret. Public publishing is what keeps thirteen repositories
|
|
126
287
|
across two GitHub owners able to install it with no token in any Vercel project,
|
|
127
288
|
which is the failure mode that would otherwise take every deploy down at once.
|
|
@@ -136,24 +297,40 @@ npm publish # publishConfig.access is already "public"
|
|
|
136
297
|
|
|
137
298
|
## Adopting it in an app
|
|
138
299
|
|
|
139
|
-
Once published:
|
|
140
|
-
|
|
141
300
|
```bash
|
|
142
301
|
npm install @connextar/house
|
|
143
302
|
```
|
|
144
303
|
|
|
145
|
-
Then
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
304
|
+
Then, per module the app takes up:
|
|
305
|
+
|
|
306
|
+
1. **Tailwind first.** Add the `@source` line (or the v3 `content` glob) from
|
|
307
|
+
[Tailwind setup](#tailwind-setup-required-for-every-component). Without it the
|
|
308
|
+
components render unstyled and nothing errors.
|
|
309
|
+
2. **Replace the local copy with a binding, not a rewrite of its callers.** The
|
|
310
|
+
app's `lib/markdown.tsx`, `components/editor/rich-text-editor.tsx` or
|
|
311
|
+
`lib/changelog.ts` stays where its importers expect it and shrinks to the
|
|
312
|
+
app's own part: its routes, its type scale, its `@` and `#` lookups, its data.
|
|
313
|
+
A binding that grows past a screen is the package being re-implemented.
|
|
314
|
+
3. **Move the tests with the code.** A local test of behaviour the package now
|
|
315
|
+
owns is deleted; a test of the app's binding stays.
|
|
316
|
+
4. **Changelog and releases** follow [`docs/ReleaseProcess.md`](docs/ReleaseProcess.md)
|
|
317
|
+
§7: the old entries become numbered releases once, `lib/release.test.ts` guards
|
|
318
|
+
them from then on, and the app's own `docs/operations/ReleaseProcess.md` says
|
|
319
|
+
only what is its own.
|
|
320
|
+
5. **Gates, then the pages.** `npm run check`, `npm test`, `npm run build`, and
|
|
321
|
+
grep the built CSS for a class that only the package uses — the one check that
|
|
322
|
+
proves step 1.
|
|
323
|
+
|
|
324
|
+
Traps found adopting it the first time:
|
|
325
|
+
|
|
326
|
+
- A **server component** receives a client module as a reference that throws on
|
|
327
|
+
any property access. Never dot into an import from a client module in shared
|
|
328
|
+
code; the package's own `next/link` helper is a type-only cast for this reason.
|
|
329
|
+
- Next writes route types under `.next/dev/types` during `next dev`. After routes
|
|
330
|
+
are deleted, a stale copy fails `next build`'s type check with "Cannot find
|
|
331
|
+
module"; delete `.next/dev` when no dev server is running.
|
|
332
|
+
- If the app also depends on `@tiptap/*` directly, keep the range aligned or drop
|
|
333
|
+
the direct dependency — two copies of `@tiptap/pm` break the editor.
|
|
157
334
|
|
|
158
335
|
**A note on local development.** Installing this with `npm install ../path` makes
|
|
159
336
|
a symlink, and Next's Turbopack will not resolve a package whose real path is
|
|
@@ -163,7 +340,7 @@ behaves exactly like a registry install:
|
|
|
163
340
|
|
|
164
341
|
```bash
|
|
165
342
|
npm pack --pack-destination /tmp # in this repo
|
|
166
|
-
npm install /tmp/connextar-house-0.
|
|
343
|
+
npm install /tmp/connextar-house-0.2.0.tgz # in the app
|
|
167
344
|
```
|
|
168
345
|
|
|
169
346
|
## 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 @@
|
|
|
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 @@
|
|
|
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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { DragHandle } from "@tiptap/extension-drag-handle-react";
|
|
4
|
+
import { ArrowDown, ArrowUp, Check, Copy, GripVertical, Plus, Trash2 } from "lucide-react";
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
import { BLOCK_TYPES } from "./blocks.js";
|
|
7
|
+
import { ghostButtonClass, Menu, MenuContent, MenuItem, MenuSeparator, MenuSub, MenuSubContent, MenuSubTrigger, MenuTrigger, } from "./ui.js";
|
|
8
|
+
/**
|
|
9
|
+
* Stable on purpose: the DragHandle wrapper re-registers its plugin when this
|
|
10
|
+
* changes identity, and a plugin re-registration recreates every plugin view in
|
|
11
|
+
* the editor — see the note in bubble-toolbar.tsx.
|
|
12
|
+
*/
|
|
13
|
+
const HANDLE_POSITION = { placement: "left-start" };
|
|
14
|
+
const handleButtonClass = `${ghostButtonClass} size-6 p-0 text-muted-foreground hover:text-foreground`;
|
|
15
|
+
/**
|
|
16
|
+
* The gutter beside whichever block the pointer is over: a `+` that starts a new
|
|
17
|
+
* block underneath (with the `/` menu already open, as Notion does) and a grip
|
|
18
|
+
* that drags the block to reorder it or, clicked, opens the block menu — turn
|
|
19
|
+
* into, duplicate, move up, move down, delete.
|
|
20
|
+
*
|
|
21
|
+
* Everything works on the block's document position rather than the caret, so a
|
|
22
|
+
* block can be acted on without ever being clicked into. The drag itself is the
|
|
23
|
+
* extension's: it understands nested lists and shows the drop cursor.
|
|
24
|
+
*/
|
|
25
|
+
export function BlockHandle({ editor }) {
|
|
26
|
+
const [hovered, setHovered] = useState(null);
|
|
27
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
28
|
+
const menuOpenRef = useRef(false);
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
menuOpenRef.current = menuOpen;
|
|
31
|
+
}, [menuOpen]);
|
|
32
|
+
// Leave the last block in place while its menu is open, so the menu still
|
|
33
|
+
// knows what it belongs to after the pointer wanders. Stable identity for the
|
|
34
|
+
// same reason as HANDLE_POSITION.
|
|
35
|
+
const onNodeChange = useCallback(({ node, pos }) => {
|
|
36
|
+
if (menuOpenRef.current)
|
|
37
|
+
return;
|
|
38
|
+
setHovered(node ? { node, pos } : null);
|
|
39
|
+
}, []);
|
|
40
|
+
const range = (block) => ({ from: block.pos, to: block.pos + block.node.nodeSize });
|
|
41
|
+
/** Turn the whole block into another type, without moving the caret by hand. */
|
|
42
|
+
const turnInto = (block, typeId) => {
|
|
43
|
+
const type = BLOCK_TYPES.find((t) => t.id === typeId);
|
|
44
|
+
if (!type)
|
|
45
|
+
return;
|
|
46
|
+
const inside = { from: block.pos + 1, to: Math.max(block.pos + 1, block.pos + block.node.nodeSize - 1) };
|
|
47
|
+
type.apply(editor.chain().focus().setTextSelection(inside)).run();
|
|
48
|
+
};
|
|
49
|
+
const duplicate = (block) => {
|
|
50
|
+
// The node is immutable, so it can be inserted twice.
|
|
51
|
+
editor
|
|
52
|
+
.chain()
|
|
53
|
+
.focus()
|
|
54
|
+
.insertContentAt(block.pos + block.node.nodeSize, block.node)
|
|
55
|
+
.run();
|
|
56
|
+
};
|
|
57
|
+
const remove = (block) => {
|
|
58
|
+
editor.chain().focus().deleteRange(range(block)).run();
|
|
59
|
+
};
|
|
60
|
+
/** Swap the block with its previous (-1) or next (+1) sibling. */
|
|
61
|
+
const move = (block, direction) => {
|
|
62
|
+
const $pos = editor.state.doc.resolve(block.pos);
|
|
63
|
+
const parent = $pos.parent;
|
|
64
|
+
const siblingIndex = $pos.index() + direction;
|
|
65
|
+
if (siblingIndex < 0 || siblingIndex >= parent.childCount)
|
|
66
|
+
return;
|
|
67
|
+
const sibling = parent.child(siblingIndex);
|
|
68
|
+
const { from, to } = range(block);
|
|
69
|
+
editor
|
|
70
|
+
.chain()
|
|
71
|
+
.focus()
|
|
72
|
+
.command(({ tr }) => {
|
|
73
|
+
tr.delete(from, to);
|
|
74
|
+
const insertAt = direction === -1 ? from - sibling.nodeSize : from + sibling.nodeSize;
|
|
75
|
+
tr.insert(insertAt, block.node);
|
|
76
|
+
return true;
|
|
77
|
+
})
|
|
78
|
+
.run();
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* The `+`: a fresh paragraph under this block with the `/` menu open — or, if
|
|
82
|
+
* this block is already an empty paragraph, just the menu, right here.
|
|
83
|
+
*/
|
|
84
|
+
const insertBelow = (block) => {
|
|
85
|
+
const isEmptyParagraph = block.node.type.name === "paragraph" && block.node.content.size === 0;
|
|
86
|
+
if (isEmptyParagraph) {
|
|
87
|
+
editor
|
|
88
|
+
.chain()
|
|
89
|
+
.focus()
|
|
90
|
+
.setTextSelection(block.pos + 1)
|
|
91
|
+
.insertContent("/")
|
|
92
|
+
.run();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const after = block.pos + block.node.nodeSize;
|
|
96
|
+
editor
|
|
97
|
+
.chain()
|
|
98
|
+
.focus()
|
|
99
|
+
.insertContentAt(after, { type: "paragraph" })
|
|
100
|
+
.setTextSelection(after + 1)
|
|
101
|
+
.insertContent("/")
|
|
102
|
+
.run();
|
|
103
|
+
};
|
|
104
|
+
return (_jsxs(DragHandle, { editor: editor, nested: true, className: "house-block-handle", computePositionConfig: HANDLE_POSITION, onNodeChange: onNodeChange, children: [_jsx("button", { type: "button", className: handleButtonClass, "aria-label": "Add a block below", title: "Add a block below", onClick: () => hovered && insertBelow(hovered), children: _jsx(Plus, { className: "size-4", "aria-hidden": true }) }), _jsxs(Menu, { open: menuOpen, onOpenChange: setMenuOpen, children: [_jsx(MenuTrigger, { asChild: true, children: _jsx("button", { type: "button", className: `${handleButtonClass} cursor-grab active:cursor-grabbing`, "aria-label": "Block options \u2014 drag to move", title: "Drag to move, click for options", children: _jsx(GripVertical, { className: "size-4", "aria-hidden": true }) }) }), _jsxs(MenuContent, { className: "w-48", children: [_jsxs(MenuSub, { children: [_jsx(MenuSubTrigger, { children: "Turn into" }), _jsx(MenuSubContent, { className: "w-48", children: BLOCK_TYPES.map((type) => (_jsxs(MenuItem, { onSelect: () => hovered && turnInto(hovered, type.id), children: [_jsx(type.icon, { className: "size-4", "aria-hidden": true }), _jsx("span", { className: "flex-1", children: type.label }), hovered && type.matchesNode(hovered.node) ? (_jsx(Check, { className: "size-4 text-primary", "aria-hidden": true })) : null] }, type.id))) })] }), _jsxs(MenuItem, { onSelect: () => hovered && duplicate(hovered), children: [_jsx(Copy, { className: "size-4", "aria-hidden": true }), "Duplicate"] }), _jsxs(MenuItem, { onSelect: () => hovered && move(hovered, -1), children: [_jsx(ArrowUp, { className: "size-4", "aria-hidden": true }), "Move up"] }), _jsxs(MenuItem, { onSelect: () => hovered && move(hovered, 1), children: [_jsx(ArrowDown, { className: "size-4", "aria-hidden": true }), "Move down"] }), _jsx(MenuSeparator, {}), _jsxs(MenuItem, { className: "text-destructive focus:text-destructive", onSelect: () => hovered && remove(hovered), children: [_jsx(Trash2, { className: "size-4", "aria-hidden": true }), "Delete"] })] })] })] }));
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=block-handle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-handle.js","sourceRoot":"","sources":["../../src/editor/block-handle.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ChainedCommands, Editor } from "@tiptap/core";
|
|
2
|
+
import type { Node as PMNode } from "@tiptap/pm/model";
|
|
3
|
+
import { type LucideIcon } from "lucide-react";
|
|
4
|
+
/**
|
|
5
|
+
* The block vocabulary of the editor — one list, read by three surfaces: the
|
|
6
|
+
* `/` menu, the bubble menu's "Turn into", and each block's grip menu.
|
|
7
|
+
*
|
|
8
|
+
* These are exactly the blocks Markdown can say, which is the whole reason there
|
|
9
|
+
* are no callouts, toggles or columns: the stored format is Markdown text, and a
|
|
10
|
+
* block the read-only renderer (and every printed or emailed copy) could not show
|
|
11
|
+
* would silently degrade on the surfaces people hand to someone else.
|
|
12
|
+
*/
|
|
13
|
+
export interface BlockType {
|
|
14
|
+
id: "paragraph" | "heading1" | "heading2" | "heading3" | "bulletList" | "orderedList" | "taskList" | "blockquote" | "codeBlock";
|
|
15
|
+
label: string;
|
|
16
|
+
description: string;
|
|
17
|
+
icon: LucideIcon;
|
|
18
|
+
/** Extra words the `/` menu matches on. */
|
|
19
|
+
keywords: string[];
|
|
20
|
+
/** Markdown shortcut shown as a hint, e.g. "# ". */
|
|
21
|
+
shortcut: string;
|
|
22
|
+
/**
|
|
23
|
+
* Convert whatever the selection is in. `clearNodes()` first lifts the
|
|
24
|
+
* selection out of lists and quotes, so "turn a quote into a heading" and
|
|
25
|
+
* "turn a list into paragraphs" both work in one step.
|
|
26
|
+
*/
|
|
27
|
+
apply: (chain: ChainedCommands) => ChainedCommands;
|
|
28
|
+
isActive: (editor: Editor) => boolean;
|
|
29
|
+
/** Whether a given block node (from the grip, not the caret) is this type. */
|
|
30
|
+
matchesNode: (node: PMNode) => boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare const BLOCK_TYPES: readonly BlockType[];
|
|
33
|
+
/** The block type the selection currently sits in, for "Turn into" state. */
|
|
34
|
+
export declare function activeBlockType(editor: Editor): BlockType;
|
|
35
|
+
//# sourceMappingURL=blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/editor/blocks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAUL,KAAK,UAAU,EAChB,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EACE,WAAW,GACX,UAAU,GACV,UAAU,GACV,UAAU,GACV,YAAY,GACZ,aAAa,GACb,UAAU,GACV,YAAY,GACZ,WAAW,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,2CAA2C;IAC3C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,eAAe,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,8EAA8E;IAC9E,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACxC;AAsBD,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EA0F3C,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAEzD"}
|