@crewlethq/ui 0.29.0 → 0.30.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 (45) hide show
  1. package/README.md +24 -3
  2. package/dist/ArtifactCover/index.js +2 -2
  3. package/dist/CommentThread/index.js +2 -2
  4. package/dist/Document/Document.d.ts +8 -1
  5. package/dist/Document/index.js +1 -1
  6. package/dist/{Document-PQGXJHB7.css → Document-G3YTZIUL.css} +71 -0
  7. package/dist/EditingStatus/EditingStatus.d.ts +25 -0
  8. package/dist/EditingStatus/index.d.ts +3 -0
  9. package/dist/EditingStatus/index.js +2 -0
  10. package/dist/EditingStatus-NFADEPSX.css +53 -0
  11. package/dist/Editor-YZ7KG2RD.js +2099 -0
  12. package/dist/Markdown/grammar.d.ts +170 -0
  13. package/dist/Markdown/index.d.ts +2 -0
  14. package/dist/Markdown/index.js +1 -0
  15. package/dist/RichEditor/Editor.d.ts +17 -0
  16. package/dist/RichEditor/RichEditor.d.ts +26 -0
  17. package/dist/RichEditor/codec.d.ts +78 -0
  18. package/dist/RichEditor/commands.d.ts +89 -0
  19. package/dist/RichEditor/index.d.ts +4 -0
  20. package/dist/RichEditor/index.js +8 -0
  21. package/dist/RichEditor/keys.d.ts +16 -0
  22. package/dist/RichEditor/nodeviews.d.ts +60 -0
  23. package/dist/RichEditor/paste.d.ts +47 -0
  24. package/dist/RichEditor/plugins.d.ts +49 -0
  25. package/dist/RichEditor/roundtrip.d.ts +14 -0
  26. package/dist/RichEditor/schema.d.ts +23 -0
  27. package/dist/RichEditor/types.d.ts +140 -0
  28. package/dist/RichEditor-NPD2KAPS.css +419 -0
  29. package/dist/RichEditorMarkdown/index.d.ts +1 -0
  30. package/dist/RichEditorMarkdown/index.js +3 -0
  31. package/dist/chunk-3DPIU2Q7.js +28 -0
  32. package/dist/{chunk-XALUCIG5.js → chunk-6AH2XJ3Q.js} +1 -1
  33. package/dist/chunk-A4E5YLXO.js +85 -0
  34. package/dist/{chunk-FKULOLKG.js → chunk-GR55WT4R.js} +1 -1
  35. package/dist/chunk-IDBHX5SR.js +12 -0
  36. package/dist/{chunk-WI6K5M5X.js → chunk-M3LUNN7F.js} +1 -1
  37. package/dist/chunk-SKOYC2CF.js +288 -0
  38. package/dist/chunk-VOX3YRFO.js +76 -0
  39. package/dist/chunk-YFW4MTK4.js +749 -0
  40. package/dist/generated/index.d.ts +4 -0
  41. package/dist/generated/index.js +44 -38
  42. package/dist/index.js +44 -38
  43. package/dist/styles.css +550 -1
  44. package/package.json +13 -3
  45. /package/dist/{chunk-IFNQKWUO.js → chunk-OC46246T.js} +0 -0
package/README.md CHANGED
@@ -41,11 +41,11 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
41
41
  | Area | Components |
42
42
  | ---- | ---------- |
43
43
  | Actions | `Button` and `ButtonLink` (variants `primary`, `secondary`, `outline`, `ghost`, `accent`, `danger`; sizes `small`, `medium`, `large`; shapes `square`, `pill`), `IconButton`, `Menu`, `Link`, `Copyable`, `CopyButton`, `DownloadButton`, `Kbd` |
44
- | Content and text | `Text` (the eight registers and `inherit`), `Prose`, `Document` and `DocumentTitle`, `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime`, `Note`, `Divider` |
45
- | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor` |
44
+ | Content and text | `Text` (the eight registers and `inherit`), `Prose`, `Document` and `DocumentTitle`, the Markdown grammar (`parseMarkdown`, `parseMarkdownSpans`, `markdownInline`, `safeHref` and the line readers, with no React in it), `Highlight`, `InlineCode`, `List` and `ListItem`, `DescriptionList`, `Timeline`, `RelativeTime`, `Note`, `Divider` |
45
+ | Forms | `Input`, `Textarea`, `Label`, `FormField`, `FormRow`, `Checkbox`, `CheckboxGroup`, `ConfirmField`, `Select`, `Tag`, `TagsInput`, `DateTimePicker`, `TimeWindowPicker`, `ImageUpload`, `CharacterPicker`, `HuePicker`, `AvatarPicker`, `FactList`, `ChoiceList`, `Composer`, `MarkdownEditor`, `RichEditor` (with `preloadRichEditor`, and the round trip `roundTripMarkdown` and `canonicalMarkdown` from `@crewlethq/ui/RichEditorMarkdown`) |
46
46
  | Data display | `DataTable`, `CopyableCell`, `Table`, the read-only table cells (`TableKeyCell`, `TableNumberCell`, `TableDateCell`, `TableEntityCell`, `TableStatusCell`, `TableTagsCell`, `TableTextCell`), `StatCard`, `Meter`, `SegmentedMeter`, `Stepper`, `PricingCard`, `Avatar`, `AvatarStack`, `EntityChip`, `CodeBlock`, `QrCode`, `ObjectHeader`, `FactLine`, `SetByLine`, `Skeleton`, `Eyebrow`, `StatGroup`, `PinTile`, `IconTile` |
47
47
  | List screens | `DataView`, and its parts on their own: `DataViewToolbar`, `FilterAxisBar`, `FilterAxisChip`, plus the filter model (`filterDefsFromColumns`, `applyColumnFilters`, `filterPredicate`, `blankFilterValue`) |
48
- | Feedback and overlays | `Callout`, `ConflictNote`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
48
+ | Feedback and overlays | `Callout`, `ConflictNote`, `EditingStatus`, `Toaster`, `Modal` (with `variant="sheet"`), `ConfirmModal`, `CommandPalette`, `Tooltip`, `Popover`, `Announcer` |
49
49
  | Documents and review | `DiffView` and `DiffSummary` with the line diff they draw (`diffLines`, `diffStat`, `collapseDiff`, `firstChangedLines`, `splitLines`, `MAX_DIFF_LINES`, with no React in it), `DiffList`, `VersionCompare`, `CommentThread` |
50
50
  | Charts | `Legend`, `BarList`, `StackedBar`, `StackedColumns`, `ActivityStrip`, `TimeSeries`, `Sparkline`, the reading layer they share (`ChartTooltip`, `useChartHover`), and the ramp (`DATA_COLORS`, `DATA_COLOR_OTHER`, `dataColor`) |
51
51
  | Marks | `Count`, `StatusDot`, `EmptyValue`, `VisuallyHidden` |
@@ -204,6 +204,27 @@ places:
204
204
  - A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
205
205
  when the content beneath it moves, and every panel anchored inside follows.
206
206
 
207
+ ## Changes in 0.30.0
208
+
209
+ **`RichEditor` edits Markdown in place, as the document it reads as.** The editable element is set by `Document`, so a page looks the same while it is edited as when it is read, and there is no separate preview. It is built on ProseMirror and loaded lazily: the component is a shell that fetches the editor's code with `import()` the first time one is drawn, so a screen that only reads pays for none of it, and `preloadRichEditor()` starts that fetch from a press that is about to open an editor.
210
+
211
+ - **Blocks**: headings, bulleted, numbered and task lists, quotes, callouts, code blocks with their language, tables, links, pictures and dividers.
212
+ - **A slash menu**: type `/` at the start of a line or after a space, filter by name, move with the arrows and choose with Enter. An application adds entries of its own (`commands`), each opening a panel it draws ("Link to a page").
213
+ - **A selection toolbar**: heading levels, bold, italic, strikethrough, inline code and a link, reached from the keyboard with Alt+F10.
214
+ - **Markdown typed as Markdown** becomes the block or the format it spells (`# `, `- `, `1. `, `[ ] `, `> `, three backticks and a language, `---`, `**bold**`, `*em*`, `~~struck~~`, `` `code` ``, `[text](address)`), and Backspace right after gives back what was typed. The shortcuts are the ones every editor shares, and Command or Control with Enter is the caller's `onSubmit`.
215
+ - **A paste of Markdown or HTML converts cleanly, under Trusted Types.** ProseMirror reads pasted HTML by assigning it to `innerHTML` behind a pass-through policy of its own, which a page enforcing `trusted-types 'none'` refuses and a page allowing it should not want. The editor takes paste and drop itself and reads HTML with a tokenizer that builds elements one by one in an inert document, keeping only the elements and attributes it has a meaning for, and copy writes Markdown as the plain text.
216
+ - **A load and a save with no edit change no byte.** Every top-level block remembers the lines it was read from and is written back as them until it is edited; a changed block is written in the spelling it was read with (its bullet, its fence, `*` or `_`), checked by reading it back. `@crewlethq/ui/RichEditorMarkdown` exports the round trip (`roundTripMarkdown`, `canonicalMarkdown`) for an application to hold its own bodies to.
217
+
218
+ **The Markdown grammar is part of the package** (`parseMarkdown`, `parseMarkdownSpans`, `markdownInline`, `safeHref`, `markdownHeadingOf`, `markdownFenceOf`, `markdownClosesFence`), so an application's renderer and `RichEditor` read a body by one grammar and cannot disagree about it. It reads what the Crewlet engine's renderer read, plus three things that renderer did not: backslash escapes (`\*` is an asterisk), an escaped pipe inside a table cell, and a link label holding an escaped bracket. It also reads **callouts** in GitHub's alert syntax, `> [!NOTE]`, `> [!TIP]`, `> [!IMPORTANT]`, `> [!WARNING]` and `> [!CAUTION]`.
219
+
220
+ **`Document` sets a callout and a cell aligned by `data-align`.** A renderer emits an element with `data-callout="<tone>"`, the tone's name in an element with `data-callout-label` and the blocks in one with `data-callout-body`; each tone is drawn from the same three steps `Callout` paints with.
221
+
222
+ **`EditingStatus` says a page is being edited**: "Editing", and an accent dot with "Unsaved changes" once anything differs from what was opened (or "Saving" while a save is in flight), announced politely. It sits in a page bar beside the caller's own Cancel and Save, and on a phone the words fold away behind the dot.
223
+
224
+ **`@crewlethq/icons` carries the editor's glyphs**: `heading-1`, `type`, `strikethrough`, `text-quote`, `square-code`, `unlink`, and `text-align-start`, `text-align-center` and `text-align-end`.
225
+
226
+ What to change: nothing. An application that renders Markdown itself may read it through `parseMarkdown` and `markdownInline` instead, and draws a callout by the shape above.
227
+
207
228
  ## Changes in 0.29.0
208
229
 
209
230
  0.29.0 adds the chrome an open artifact sits in: one slim bar over the artifact, and one panel at its side, so a host can give the artifact the screen and open everything else on demand.
@@ -1,5 +1,5 @@
1
- export { DashboardCover, DocCover, SHEET_COVER_COLUMNS, SHEET_COVER_ROWS, SheetCover } from '../chunk-XALUCIG5.js';
2
- import '../chunk-WI6K5M5X.js';
1
+ export { DashboardCover, DocCover, SHEET_COVER_COLUMNS, SHEET_COVER_ROWS, SheetCover } from '../chunk-6AH2XJ3Q.js';
2
+ import '../chunk-M3LUNN7F.js';
3
3
  import '../chunk-GOSQXEZU.js';
4
4
  import '../chunk-4KP4RC3O.js';
5
5
  import '../chunk-3CZ7XAVY.js';
@@ -1,8 +1,8 @@
1
- export { COMMENT_THREAD_LABELS, CommentThread } from '../chunk-FKULOLKG.js';
1
+ export { COMMENT_THREAD_LABELS, CommentThread } from '../chunk-GR55WT4R.js';
2
2
  import '../chunk-7DZDSSJS.js';
3
3
  import '../chunk-6QFAK2WV.js';
4
4
  import '../chunk-SMBKXWML.js';
5
- import '../chunk-WI6K5M5X.js';
5
+ import '../chunk-M3LUNN7F.js';
6
6
  import '../chunk-4KP4RC3O.js';
7
7
  import '../chunk-3CZ7XAVY.js';
8
8
  import '../chunk-EUCC37WY.js';
@@ -21,7 +21,7 @@ export interface DocumentProps extends HTMLAttributes<HTMLElement> {
21
21
  }
22
22
  /**
23
23
  * Rendered markdown, set as a document: headings, paragraphs, lists and task
24
- * lists, quotes, code, tables, rules, links and images.
24
+ * lists, quotes, callouts, code, tables, rules, links and images.
25
25
  *
26
26
  * A STYLESHEET OVER ELEMENTS, NOT A RENDERER. What a body may contain (which
27
27
  * link schemes are safe, whether raw HTML is text) is a decision about the
@@ -37,6 +37,13 @@ export interface DocumentProps extends HTMLAttributes<HTMLElement> {
37
37
  * gives that element `tabIndex={0}`, a `region` role and a name, because a
38
38
  * scroller no keyboard can reach is a table no keyboard can read to its end.
39
39
  *
40
+ * A CALLOUT is recognised the same way: an element with `data-callout` set
41
+ * to its tone (`note`, `tip`, `important`, `warning`, `caution`), the tone's
42
+ * name in an element with `data-callout-label` and its blocks in one with
43
+ * `data-callout-body`. A table cell aligned by `data-align` is set as one
44
+ * aligned by a style, so an editor that may write no style attribute sets a
45
+ * column exactly as the reader's renderer does.
46
+ *
40
47
  * THE TITLE IS NOT PART OF IT. A page's title is its address and its `h1`, set
41
48
  * above the body by `DocumentTitle`, so a renderer draws the body's own
42
49
  * headings from `h2` down and the outline of the page starts there.
@@ -1,2 +1,2 @@
1
- export { Document, DocumentTitle } from '../chunk-WI6K5M5X.js';
1
+ export { Document, DocumentTitle } from '../chunk-M3LUNN7F.js';
2
2
  import '../chunk-ODEQTPFM.js';
@@ -286,3 +286,74 @@
286
286
  color: var(--color-text-primary);
287
287
  overflow-wrap: anywhere;
288
288
  }
289
+
290
+ /*
291
+ * A TABLE CELL'S ALIGNMENT, read off `data-align` as well as off a style: an
292
+ * editor that may not write a style attribute under a strict policy marks a
293
+ * column this way, and the cell is set as the reader's is.
294
+ */
295
+ .crewlet-document :where(th, td)[data-align='center'] {
296
+ text-align: center;
297
+ }
298
+
299
+ .crewlet-document :where(th, td)[data-align='right'] {
300
+ text-align: end;
301
+ }
302
+
303
+ /*
304
+ * A CALLOUT (`> [!NOTE]` and its four siblings): a tinted block with its tone
305
+ * named on a line of its own, read by its shape as everything here is. A
306
+ * renderer emits an element with `data-callout="<tone>"` and `role="note"`,
307
+ * the tone's name in an element with `data-callout-label`, and the blocks in
308
+ * one with `data-callout-body`. The fill, the ink and the line are each tone's
309
+ * declared steps, the same three `Callout` paints with.
310
+ */
311
+ .crewlet-document [data-callout] {
312
+ --crewlet-document-callout-fill: var(--color-feedback-info-soft);
313
+ --crewlet-document-callout-ink: var(--color-feedback-info-ink);
314
+ --crewlet-document-callout-line: var(--color-feedback-info-line);
315
+ padding: var(--spacing-3) var(--spacing-4);
316
+ border-inline-start: 3px solid var(--crewlet-document-callout-line);
317
+ border-radius: var(--radius-sm);
318
+ background: var(--crewlet-document-callout-fill);
319
+ }
320
+
321
+ .crewlet-document [data-callout='tip'] {
322
+ --crewlet-document-callout-fill: var(--color-feedback-success-soft);
323
+ --crewlet-document-callout-ink: var(--color-feedback-success-ink);
324
+ --crewlet-document-callout-line: var(--color-feedback-success-line);
325
+ }
326
+
327
+ .crewlet-document [data-callout='important'] {
328
+ --crewlet-document-callout-fill: var(--color-brand-accent-soft);
329
+ --crewlet-document-callout-ink: var(--color-brand-accent-ink);
330
+ --crewlet-document-callout-line: var(--color-brand-accent);
331
+ }
332
+
333
+ .crewlet-document [data-callout='warning'] {
334
+ --crewlet-document-callout-fill: var(--color-feedback-warning-soft);
335
+ --crewlet-document-callout-ink: var(--color-feedback-warning-ink);
336
+ --crewlet-document-callout-line: var(--color-feedback-warning-line);
337
+ }
338
+
339
+ .crewlet-document [data-callout='caution'] {
340
+ --crewlet-document-callout-fill: var(--color-feedback-danger-soft);
341
+ --crewlet-document-callout-ink: var(--color-feedback-danger-ink);
342
+ --crewlet-document-callout-line: var(--color-feedback-danger-line);
343
+ }
344
+
345
+ .crewlet-document [data-callout-label] {
346
+ margin: 0 0 var(--spacing-1);
347
+ color: var(--crewlet-document-callout-ink);
348
+ font-size: var(--font-size-xs);
349
+ font-weight: var(--font-weight-semibold);
350
+ line-height: var(--font-line-height-snug);
351
+ }
352
+
353
+ .crewlet-document [data-callout-body] {
354
+ color: var(--color-text-primary);
355
+ }
356
+
357
+ .crewlet-document [data-callout-body] > * + * {
358
+ margin-block-start: var(--crewlet-document-gap);
359
+ }
@@ -0,0 +1,25 @@
1
+ /** Every string the status draws. */
2
+ export interface EditingStatusLabels {
3
+ editing: string;
4
+ unsaved: string;
5
+ saving: string;
6
+ }
7
+ export interface EditingStatusProps {
8
+ /** Whether anything differs from what was opened. */
9
+ dirty: boolean;
10
+ /** Whether a save is in flight. */
11
+ saving?: boolean | undefined;
12
+ labels?: Partial<EditingStatusLabels> | undefined;
13
+ className?: string | undefined;
14
+ }
15
+ /**
16
+ * The quiet state a page is in while somebody edits it: "Editing", and a dot
17
+ * with "Unsaved changes" once anything differs from what was opened.
18
+ *
19
+ * It sits in the bar beside Save and Cancel, which are the caller's: what a
20
+ * save sends and what Cancel discards are decisions about the caller's own
21
+ * record. A STATUS, announced politely, so a screen reader hears the change
22
+ * from saved to unsaved without being interrupted. On a narrow bar the words
23
+ * fold away and the dot stays, named by the same words.
24
+ */
25
+ export declare function EditingStatus({ dirty, saving, labels, className }: EditingStatusProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './EditingStatus.css';
2
+ export { EditingStatus } from './EditingStatus.js';
3
+ export type { EditingStatusLabels, EditingStatusProps } from './EditingStatus.js';
@@ -0,0 +1,2 @@
1
+ export { EditingStatus } from '../chunk-3DPIU2Q7.js';
2
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,53 @@
1
+ /*
2
+ * The editing status (EditingStatus.tsx): a word and a dot, in the bar's own
3
+ * small step, quiet until something is unsaved.
4
+ */
5
+
6
+ .crewlet-editing-status {
7
+ display: inline-flex;
8
+ align-items: center;
9
+ gap: var(--spacing-2);
10
+ min-width: 0;
11
+ color: var(--color-text-secondary);
12
+ font-family: var(--font-family-sans);
13
+ font-size: var(--font-size-compact);
14
+ white-space: nowrap;
15
+ }
16
+
17
+ .crewlet-editing-status__dot {
18
+ flex: none;
19
+ width: var(--spacing-2);
20
+ height: var(--spacing-2);
21
+ border-radius: var(--radius-circle);
22
+ background: var(--color-text-tertiary);
23
+ }
24
+
25
+ .crewlet-editing-status--dirty .crewlet-editing-status__dot {
26
+ background: var(--color-brand-accent);
27
+ }
28
+
29
+ .crewlet-editing-status__label {
30
+ color: var(--color-text-primary);
31
+ font-weight: var(--font-weight-medium);
32
+ }
33
+
34
+ .crewlet-editing-status__state {
35
+ overflow: hidden;
36
+ color: var(--color-text-tertiary);
37
+ text-overflow: ellipsis;
38
+ }
39
+
40
+ .crewlet-editing-status__state::before {
41
+ content: '\00b7';
42
+ margin-right: var(--spacing-2);
43
+ }
44
+
45
+ @media (width < 480px) {
46
+ .crewlet-editing-status__state {
47
+ position: absolute;
48
+ width: 1px;
49
+ height: 1px;
50
+ overflow: hidden;
51
+ clip-path: inset(50%);
52
+ }
53
+ }