@crewlethq/ui 0.19.0 → 0.21.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 (70) hide show
  1. package/README.md +32 -3
  2. package/dist/AppShell/AccountRow.d.ts +24 -2
  3. package/dist/AppShell/index.d.ts +1 -1
  4. package/dist/AppShell/index.js +3 -1
  5. package/dist/{AppShell-RUNVM4WH.css → AppShell-APHZKJYH.css} +25 -3
  6. package/dist/CommandPalette/index.js +3 -3
  7. package/dist/CommentThread/CommentThread.d.ts +85 -0
  8. package/dist/CommentThread/index.d.ts +3 -0
  9. package/dist/CommentThread/index.js +16 -0
  10. package/dist/CommentThread-7VXOFTZO.css +90 -0
  11. package/dist/ConflictNote/ConflictNote.d.ts +42 -0
  12. package/dist/ConflictNote/index.d.ts +3 -0
  13. package/dist/ConflictNote/index.js +4 -0
  14. package/dist/ConflictNote-DH77PYF7.css +27 -0
  15. package/dist/DataTable/index.js +4 -4
  16. package/dist/DataView/index.js +6 -6
  17. package/dist/DateTimePicker/index.js +2 -2
  18. package/dist/DiffView/DiffView.d.ts +59 -0
  19. package/dist/DiffView/diff.d.ts +97 -0
  20. package/dist/DiffView/index.d.ts +5 -0
  21. package/dist/DiffView/index.js +3 -0
  22. package/dist/DiffView-RVLIHTKG.css +91 -0
  23. package/dist/LinkInterstitial/index.js +3 -3
  24. package/dist/Menu/Menu.d.ts +28 -2
  25. package/dist/Menu/index.d.ts +1 -1
  26. package/dist/Menu/index.js +1 -1
  27. package/dist/{Menu-BT5PMFJI.css → Menu-XIJ4LQUT.css} +15 -0
  28. package/dist/Modal/index.js +2 -2
  29. package/dist/PeopleRail/PeopleRail.d.ts +52 -0
  30. package/dist/PeopleRail/index.d.ts +3 -0
  31. package/dist/PeopleRail/index.js +11 -0
  32. package/dist/PeopleRail-R64WVOSH.css +75 -0
  33. package/dist/ShareDialog/index.js +3 -3
  34. package/dist/SheetGrid/index.js +3 -3
  35. package/dist/Switcher/index.js +3 -3
  36. package/dist/TableCells/TableCells.d.ts +153 -0
  37. package/dist/TableCells/index.d.ts +3 -0
  38. package/dist/TableCells/index.js +10 -0
  39. package/dist/TableCells-3VN5N2M3.css +104 -0
  40. package/dist/TimeWindowPicker/index.js +2 -2
  41. package/dist/VersionCompare/VersionCompare.d.ts +70 -0
  42. package/dist/VersionCompare/index.d.ts +3 -0
  43. package/dist/VersionCompare/index.js +11 -0
  44. package/dist/VersionCompare-UUZOPTQO.css +68 -0
  45. package/dist/VersionTimeline/index.js +2 -2
  46. package/dist/{chunk-UXELEORU.js → chunk-4HRB62EA.js} +1 -1
  47. package/dist/{chunk-JULMZNGT.js → chunk-4MIM356A.js} +18 -7
  48. package/dist/chunk-73QBUUYX.js +102 -0
  49. package/dist/{chunk-7FQEUNHX.js → chunk-7X5WOECO.js} +1 -1
  50. package/dist/{chunk-OIMAK5AE.js → chunk-AV6YX4GZ.js} +4 -4
  51. package/dist/chunk-BVWSLXTY.js +187 -0
  52. package/dist/{chunk-EDGCPVVD.js → chunk-G5TAKMEA.js} +27 -3
  53. package/dist/chunk-IC64IWOO.js +176 -0
  54. package/dist/chunk-J4OKGJQ6.js +96 -0
  55. package/dist/{chunk-AJESMWJS.js → chunk-LG244LY7.js} +3 -3
  56. package/dist/chunk-MUGM7SEG.js +88 -0
  57. package/dist/chunk-UO7JZVCO.js +35 -0
  58. package/dist/{chunk-HTCJSUIZ.js → chunk-XMGB3ZAT.js} +2 -2
  59. package/dist/{chunk-RT323I4I.js → chunk-ZM7HLFOA.js} +1 -1
  60. package/dist/generated/index.d.ts +6 -0
  61. package/dist/generated/index.js +52 -46
  62. package/dist/index.js +52 -46
  63. package/dist/styles.css +515 -5
  64. package/package.json +3 -3
  65. package/dist/{chunk-PUQY4WXR.js → chunk-36CWC2NH.js} +1 -1
  66. package/dist/{chunk-7IJARUIF.js → chunk-F4DPAJCN.js} +1 -1
  67. package/dist/{chunk-4Z7PW3L6.js → chunk-MSSZHDZI.js} +0 -0
  68. package/dist/{chunk-EIGEIT4O.js → chunk-NUZC6PJX.js} +1 -1
  69. package/dist/{chunk-L4H7SSWF.js → chunk-RE6ED5TO.js} +1 -1
  70. package/dist/{chunk-WXAULPBH.js → chunk-UNZ7TVV5.js} +1 -1
@@ -0,0 +1,153 @@
1
+ import type { ReactNode } from 'react';
2
+ import { type AvatarKind, type AvatarSizeStep } from '../Avatar/index.js';
3
+ import { type TimeValue } from '../RelativeTime/index.js';
4
+ import type { Tone } from '../utils/tone.js';
5
+ export interface TableKeyCellProps {
6
+ /** The identifier: a key, a handle, an id. Its whole value is the cell's title. */
7
+ value: string | null | undefined;
8
+ /** What the cell prints where that is shorter than the value: an id cut to its head. */
9
+ text?: string | undefined;
10
+ /** Where the identifier goes. Without it the cell is plain text. */
11
+ href?: string | undefined;
12
+ /** What an absent identifier means, read in place of the dash. */
13
+ emptyLabel?: string | undefined;
14
+ }
15
+ /**
16
+ * An identifier, monospaced, on ONE line, cut with its whole value on the
17
+ * title. An identifier is one token with no space to break at, so wrapping it
18
+ * at whatever character the column ran out on stood the row two lines tall and
19
+ * printed a key nobody could search for.
20
+ */
21
+ export declare function TableKeyCell({ value, text, href, emptyLabel }: TableKeyCellProps): import("react").JSX.Element;
22
+ export interface TableNumberCellProps {
23
+ value: number | null | undefined;
24
+ /**
25
+ * How the figure is spelled. The host's own formatter, so a cell never
26
+ * spells a figure differently from the rest of the screen. Defaults to the
27
+ * locale's grouping.
28
+ */
29
+ format?: ((value: number) => string) | undefined;
30
+ /** Printed after the figure: " ms", "%". */
31
+ suffix?: string | undefined;
32
+ title?: string | undefined;
33
+ /** What an absent figure means. Absent is not zero. */
34
+ emptyLabel?: string | undefined;
35
+ }
36
+ /**
37
+ * A figure, tabular, and a marked absence when there is none.
38
+ *
39
+ * ABSENT IS NOT ZERO, which is the whole reason the cell exists: "nothing is
40
+ * recorded" and "nothing was spent" are different facts, and a column that
41
+ * draws both as `0` makes the first invisible. A duration or a token count is
42
+ * this cell with the host's formatter.
43
+ */
44
+ export declare function TableNumberCell({ value, format, suffix, title, emptyLabel, }: TableNumberCellProps): import("react").JSX.Element;
45
+ export interface TableDateCellProps {
46
+ value: TimeValue | null | undefined;
47
+ /** Overrides the shared clock, for a story or a suite. */
48
+ now?: number | undefined;
49
+ /** What an absent instant means: "Never", "Not scheduled". */
50
+ emptyLabel?: string | undefined;
51
+ }
52
+ /**
53
+ * An instant, relative in the cell and exact on hover: a table scanned for
54
+ * "what moved today" is read in relative time, and the exact instant is what
55
+ * somebody needs once they have found the row. It is [RelativeTime], kept on
56
+ * one line in the quiet ink a date column takes.
57
+ */
58
+ export declare function TableDateCell({ value, now, emptyLabel }: TableDateCellProps): import("react").JSX.Element;
59
+ export interface TableEntityCellProps {
60
+ /** Who or what the row names. Absent, the cell is a marked absence. */
61
+ name: string | null | undefined;
62
+ /** Drawn as `Avatar` draws it: a squircle for an agent, a circle for a person. */
63
+ kind?: AvatarKind | undefined;
64
+ /**
65
+ * A badge of the host's own in place of the initials badge, such as a
66
+ * `PrincipalAvatar` carrying an agent's character. It is drawn as
67
+ * decoration, because the name is printed beside it.
68
+ */
69
+ badge?: ReactNode;
70
+ /**
71
+ * Where the name goes. Leave it out inside a row that is itself a link: an
72
+ * anchor inside an anchor is markup no browser agrees about.
73
+ */
74
+ href?: string | undefined;
75
+ /** The hover text, where the name is not the whole story. */
76
+ title?: string | undefined;
77
+ size?: AvatarSizeStep | undefined;
78
+ emptyLabel?: string | undefined;
79
+ }
80
+ /**
81
+ * A seat, a person or a team: the identity badge and the name, optionally a
82
+ * link to its page.
83
+ *
84
+ * ONE BADGE FOR ONE IDENTITY, the kit's `Avatar`, so the same person reads the
85
+ * same in every table. The badge is decorative, because the name is printed
86
+ * beside it: without that the row reads "Ada Lovelace avatar, Ada Lovelace".
87
+ */
88
+ export declare function TableEntityCell({ name, kind, badge, href, title, size, emptyLabel, }: TableEntityCellProps): import("react").JSX.Element;
89
+ export interface TableStatusCellProps {
90
+ /** The state's word, always printed: a glyph is never the only carrier. */
91
+ label: string;
92
+ /**
93
+ * The state's mark: a character such as a filled or a hollow circle, or a
94
+ * glyph. Hidden from assistive technology, since the word says it.
95
+ */
96
+ glyph: ReactNode;
97
+ tone?: Tone | undefined;
98
+ title?: string | undefined;
99
+ }
100
+ /**
101
+ * A state's mark with its word, never colour alone.
102
+ *
103
+ * THE MARK IS THE CALLER'S, which is what sets it apart from `StatusDot`: half
104
+ * of what a status column says is a HOLLOW counterpart (filled against hollow
105
+ * for "running the active revision" against "nothing reported"), and a filled
106
+ * dot has no unfilled state. The tone colours the mark and never the word.
107
+ */
108
+ export declare function TableStatusCell({ label, glyph, tone, title }: TableStatusCellProps): import("react").JSX.Element;
109
+ export interface TableTagsCellLabels {
110
+ /** The count of tags past `max`, read in full: "2 more tags". */
111
+ more: (count: number) => string;
112
+ }
113
+ export declare const TABLE_TAGS_CELL_LABELS: TableTagsCellLabels;
114
+ export interface TableTagsCellProps {
115
+ tags: readonly string[] | null | undefined;
116
+ /** How many tags are drawn before the rest are counted. Three by default. */
117
+ max?: number | undefined;
118
+ emptyLabel?: string | undefined;
119
+ labels?: Partial<TableTagsCellLabels> | undefined;
120
+ }
121
+ /**
122
+ * Tags, capped with a count rather than wrapping a row to three lines. Neutral
123
+ * and outlined: a tag names a thing, and identity takes no colour. The tags
124
+ * past the cap are named on the count's title and counted in words for a
125
+ * screen reader.
126
+ */
127
+ export declare function TableTagsCell({ tags, max, emptyLabel, labels: overrides }: TableTagsCellProps): import("react").JSX.Element;
128
+ export type TableTextCellProps = {
129
+ children: ReactNode;
130
+ } & ({
131
+ /**
132
+ * A mark every row of the column wears, such as the column's own glyph.
133
+ * The header carries the meaning, so it is drawn as decoration.
134
+ */
135
+ icon?: ReactNode;
136
+ mark?: never;
137
+ } | {
138
+ icon?: never;
139
+ /**
140
+ * A mark that varies per row and states a fact of its own (a bug, an
141
+ * epic), so it brings its own accessible name and is read.
142
+ */
143
+ mark: ReactNode;
144
+ });
145
+ /**
146
+ * Plain text with an optional leading mark, cut on one line.
147
+ *
148
+ * TWO SPELLINGS OF ONE SLOT, because a column's mark and a row's mark are
149
+ * different facts: `icon` is the column's and is hidden, `mark` is the row's
150
+ * and is read. Exactly one of the two, enforced by the type: a cell given both
151
+ * would draw the column's answer over the row's.
152
+ */
153
+ export declare function TableTextCell({ children, icon, mark }: TableTextCellProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './TableCells.css';
2
+ export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell, } from './TableCells.js';
3
+ export type { TableDateCellProps, TableEntityCellProps, TableKeyCellProps, TableNumberCellProps, TableStatusCellProps, TableTagsCellLabels, TableTagsCellProps, TableTextCellProps, } from './TableCells.js';
@@ -0,0 +1,10 @@
1
+ export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell } from '../chunk-MUGM7SEG.js';
2
+ import '../chunk-GFV4LJHH.js';
3
+ import '../chunk-WIATRXEJ.js';
4
+ import '../chunk-FBSEJO2C.js';
5
+ import '../chunk-3CZ7XAVY.js';
6
+ import '../chunk-EUCC37WY.js';
7
+ import '../chunk-UAN5NE7M.js';
8
+ import '../chunk-5OM447RW.js';
9
+ import '../chunk-BZFWDIV6.js';
10
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,104 @@
1
+ /* The read-only table cells. Each one holds a single line and shrinks inside
2
+ its column (`min-width: 0`), so a long value is cut with an ellipsis and
3
+ its whole text on the title rather than standing the row two lines tall. */
4
+
5
+ .crewlet-table-cell__truncate {
6
+ min-width: 0;
7
+ overflow: hidden;
8
+ text-overflow: ellipsis;
9
+ white-space: nowrap;
10
+ }
11
+
12
+ /* AN IDENTIFIER IS ONE LINE, cut with its value on the title rather than
13
+ broken at whichever character the column ran out on. Named with the link's
14
+ class as well, so a linked identifier keeps the block box whatever order
15
+ the two stylesheets load in. */
16
+ .crewlet-table-cell__key,
17
+ .crewlet-link.crewlet-table-cell__key {
18
+ display: block;
19
+ min-width: 0;
20
+ overflow: hidden;
21
+ text-overflow: ellipsis;
22
+ white-space: nowrap;
23
+ font-family: var(--font-family-mono);
24
+ }
25
+
26
+ .crewlet-table-cell__number {
27
+ font-variant-numeric: tabular-nums;
28
+ white-space: nowrap;
29
+ }
30
+
31
+ .crewlet-table-cell__date {
32
+ color: var(--color-text-tertiary);
33
+ white-space: nowrap;
34
+ }
35
+
36
+ .crewlet-table-cell__entity,
37
+ .crewlet-link.crewlet-table-cell__entity,
38
+ .crewlet-table-cell__status,
39
+ .crewlet-table-cell__text {
40
+ display: inline-flex;
41
+ align-items: center;
42
+ gap: var(--spacing-2);
43
+ min-width: 0;
44
+ max-width: 100%;
45
+ }
46
+
47
+ .crewlet-table-cell__status,
48
+ .crewlet-table-cell__text {
49
+ gap: calc(var(--spacing-1) * 1.5);
50
+ }
51
+
52
+ .crewlet-table-cell__badge,
53
+ .crewlet-table-cell__icon {
54
+ display: inline-flex;
55
+ flex: none;
56
+ }
57
+
58
+ .crewlet-table-cell__icon {
59
+ color: var(--color-text-tertiary);
60
+ }
61
+
62
+ .crewlet-table-cell__glyph {
63
+ display: inline-flex;
64
+ flex: none;
65
+ font-size: var(--font-size-xs);
66
+ line-height: 1;
67
+ /* The quiet state is still a mark, so it takes the tertiary step: the
68
+ decoration step does not clear 3:1 on every ground. */
69
+ color: var(--color-text-tertiary);
70
+ }
71
+
72
+ .crewlet-table-cell__status--info .crewlet-table-cell__glyph {
73
+ color: var(--color-feedback-info);
74
+ }
75
+
76
+ .crewlet-table-cell__status--success .crewlet-table-cell__glyph {
77
+ color: var(--color-feedback-success);
78
+ }
79
+
80
+ .crewlet-table-cell__status--warning .crewlet-table-cell__glyph {
81
+ color: var(--color-feedback-warning);
82
+ }
83
+
84
+ .crewlet-table-cell__status--danger .crewlet-table-cell__glyph {
85
+ color: var(--color-feedback-danger);
86
+ }
87
+
88
+ .crewlet-table-cell__status--brand .crewlet-table-cell__glyph {
89
+ color: var(--color-brand-accent);
90
+ }
91
+
92
+ .crewlet-table-cell__tags {
93
+ display: inline-flex;
94
+ flex-wrap: wrap;
95
+ align-items: center;
96
+ gap: var(--spacing-1);
97
+ min-width: 0;
98
+ }
99
+
100
+ .crewlet-table-cell__more {
101
+ color: var(--color-text-secondary);
102
+ font-size: var(--font-size-xs);
103
+ font-variant-numeric: tabular-nums;
104
+ }
@@ -1,11 +1,11 @@
1
- export { DEFAULT_TIME_WINDOW_LABELS, TIME_WINDOW_SPANS, TimeWindowPicker, describeTimeWindow, isTimeWindowSet, parseTimeWindowDuration, resolveTimeWindow, timeWindowMatchesPreset } from '../chunk-EIGEIT4O.js';
1
+ export { DEFAULT_TIME_WINDOW_LABELS, TIME_WINDOW_SPANS, TimeWindowPicker, describeTimeWindow, isTimeWindowSet, parseTimeWindowDuration, resolveTimeWindow, timeWindowMatchesPreset } from '../chunk-NUZC6PJX.js';
2
2
  import '../chunk-FC4YTTFJ.js';
3
3
  import '../chunk-F73MC7EZ.js';
4
4
  import '../chunk-GXQ3B46I.js';
5
- import '../chunk-OG6CL3EB.js';
6
5
  import '../chunk-Q55TXPA3.js';
7
6
  import '../chunk-BWRGFKJP.js';
8
7
  import '../chunk-PQADV6P6.js';
8
+ import '../chunk-OG6CL3EB.js';
9
9
  import '../chunk-HMPFGIWM.js';
10
10
  import '../chunk-R25RGPUB.js';
11
11
  import '../chunk-4FQT6ZAY.js';
@@ -0,0 +1,70 @@
1
+ import { type ReactNode } from 'react';
2
+ /** How two versions are compared: what changed between them, or the versions themselves. */
3
+ export type VersionCompareLens = 'changes' | 'versions';
4
+ export declare const VERSION_COMPARE_LENSES: readonly ["changes", "versions"];
5
+ export interface VersionCompareLabels {
6
+ /** The heading when the host gives no title: "Version 3 and version 5". */
7
+ title: (from: string, to: string) => string;
8
+ /** Names the lens switch. */
9
+ lens: string;
10
+ changes: string;
11
+ versions: string;
12
+ /** The changes region's accessible name. */
13
+ region: (from: string, to: string) => string;
14
+ identicalTitle: string;
15
+ identicalDescription: string;
16
+ }
17
+ export declare const VERSION_COMPARE_LABELS: VersionCompareLabels;
18
+ export interface VersionCompareProps {
19
+ /** The older version, named the way the reader knows it: "Version 3", "rev 12". */
20
+ fromLabel: string;
21
+ /** The newer version. */
22
+ toLabel: string;
23
+ /** The heading. Defaults to naming both versions. */
24
+ title?: ReactNode;
25
+ /** A sentence beside the heading: "What changed between them". */
26
+ description?: ReactNode;
27
+ /** The lens on show. A lens with nothing to draw falls back to the other. */
28
+ lens: VersionCompareLens;
29
+ /** Switches the lens. Without it, or with only one lens to draw, no switch is drawn. */
30
+ onLensChange?: ((lens: VersionCompareLens) => void) | undefined;
31
+ /**
32
+ * The older text, compared line by line with `after` for the changes lens.
33
+ * Leave both out and pass `changes` for a comparison the host draws itself,
34
+ * such as a list of changed files.
35
+ */
36
+ before?: string | undefined;
37
+ after?: string | undefined;
38
+ /** The changes lens, drawn by the host in place of the line diff of `before` and `after`. */
39
+ changes?: ReactNode;
40
+ /** Unchanged lines kept either side of a change in the line diff. Three by default. */
41
+ context?: number | undefined;
42
+ /** The older version, drawn for the versions lens. */
43
+ from?: ReactNode;
44
+ /** The newer version, drawn for the versions lens. With `from` left out it is drawn alone. */
45
+ to?: ReactNode;
46
+ /** A sentence over the versions lens: "Both versions show today's data." */
47
+ note?: ReactNode;
48
+ /** Controls at the end of the head, after the lens switch: "Back to the page". */
49
+ actions?: ReactNode;
50
+ labels?: Partial<VersionCompareLabels> | undefined;
51
+ className?: string | undefined;
52
+ }
53
+ /**
54
+ * Two versions of a document, compared: what changed between them, or the two
55
+ * drawn side by side.
56
+ *
57
+ * THE OLDER VERSION IS ALWAYS THE "FROM" SIDE and the left pane, whichever the
58
+ * reader picked first, so a comparison reads forward in time. The host orders
59
+ * the two before it hands them over.
60
+ *
61
+ * THE CHANGES LENS IS A [DiffView] when the host hands over two texts, with
62
+ * the [DiffSummary] above it; two identical texts are said in words ("Nothing
63
+ * changed") rather than drawn as a pane of unmarked lines that reads as one
64
+ * that failed to load. A comparison that is not two texts, such as the files
65
+ * of two releases, is drawn by the host in `changes`.
66
+ *
67
+ * A LENS THAT CANNOT BE DRAWN IS ABSENT, not offered and then empty: a first
68
+ * version has nothing to compare with, and a sheet has no side by side.
69
+ */
70
+ export declare function VersionCompare({ fromLabel, toLabel, title, description, lens, onLensChange, before, after, changes, context, from, to, note, actions, labels: overrides, className, }: VersionCompareProps): import("react").JSX.Element;
@@ -0,0 +1,3 @@
1
+ import './VersionCompare.css';
2
+ export { VersionCompare, VERSION_COMPARE_LABELS, VERSION_COMPARE_LENSES } from './VersionCompare.js';
3
+ export type { VersionCompareLabels, VersionCompareLens, VersionCompareProps } from './VersionCompare.js';
@@ -0,0 +1,11 @@
1
+ export { VERSION_COMPARE_LABELS, VERSION_COMPARE_LENSES, VersionCompare } from '../chunk-73QBUUYX.js';
2
+ import '../chunk-VYY56AMX.js';
3
+ import '../chunk-BVWSLXTY.js';
4
+ import '../chunk-26PEPONN.js';
5
+ import '../chunk-4LDTQWS7.js';
6
+ import '../chunk-CBOICT64.js';
7
+ import '../chunk-RA2ULG6H.js';
8
+ import '../chunk-PQADV6P6.js';
9
+ import '../chunk-BHHIIK3M.js';
10
+ import '../chunk-BZFWDIV6.js';
11
+ import '../chunk-ODEQTPFM.js';
@@ -0,0 +1,68 @@
1
+ /* VersionCompare: a head naming the two versions with the lens switch at its
2
+ end, then the changes or the two versions. */
3
+ .crewlet-version-compare {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var(--spacing-3);
7
+ min-width: 0;
8
+ }
9
+
10
+ .crewlet-version-compare__head {
11
+ display: flex;
12
+ flex-wrap: wrap;
13
+ align-items: center;
14
+ gap: var(--spacing-2) var(--spacing-3);
15
+ }
16
+
17
+ .crewlet-version-compare__titles {
18
+ display: flex;
19
+ flex: 1 1 16rem;
20
+ flex-direction: column;
21
+ gap: var(--spacing-1);
22
+ min-width: 0;
23
+ }
24
+
25
+ .crewlet-version-compare__title {
26
+ margin: 0;
27
+ color: var(--color-text-primary);
28
+ font-size: var(--font-size-sm);
29
+ font-weight: var(--font-weight-semibold);
30
+ }
31
+
32
+ .crewlet-version-compare__description,
33
+ .crewlet-version-compare__note {
34
+ margin: 0;
35
+ color: var(--color-text-tertiary);
36
+ font-size: var(--font-size-compact);
37
+ }
38
+
39
+ .crewlet-version-compare__changes,
40
+ .crewlet-version-compare__versions {
41
+ display: flex;
42
+ flex-direction: column;
43
+ gap: var(--spacing-3);
44
+ min-width: 0;
45
+ }
46
+
47
+ /* SIDE BY SIDE where there is room for two readable columns, and one above
48
+ the other where there is not, decided by the width the panes are given
49
+ rather than by the viewport, so a comparison in a side pane stacks too. */
50
+ .crewlet-version-compare__panes {
51
+ display: grid;
52
+ grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
53
+ gap: var(--spacing-4);
54
+ }
55
+
56
+ .crewlet-version-compare__pane {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: var(--spacing-2);
60
+ min-width: 0;
61
+ }
62
+
63
+ .crewlet-version-compare__pane-title {
64
+ margin: 0;
65
+ color: var(--color-text-primary);
66
+ font-size: var(--font-size-sm);
67
+ font-weight: var(--font-weight-semibold);
68
+ }
@@ -1,10 +1,10 @@
1
- export { VersionTimeline } from '../chunk-WXAULPBH.js';
1
+ export { VersionTimeline } from '../chunk-UNZ7TVV5.js';
2
2
  import '../chunk-AUQPBL4O.js';
3
- import '../chunk-OG6CL3EB.js';
4
3
  import '../chunk-4KP4RC3O.js';
5
4
  import '../chunk-3CZ7XAVY.js';
6
5
  import '../chunk-EUCC37WY.js';
7
6
  import '../chunk-UAN5NE7M.js';
8
7
  import '../chunk-5OM447RW.js';
8
+ import '../chunk-OG6CL3EB.js';
9
9
  import '../chunk-BZFWDIV6.js';
10
10
  import '../chunk-ODEQTPFM.js';
@@ -1,4 +1,4 @@
1
- import { Modal } from './chunk-7IJARUIF.js';
1
+ import { Modal } from './chunk-F4DPAJCN.js';
2
2
  import { useListbox, useHighlightRest } from './chunk-F73MC7EZ.js';
3
3
  import { Kbd } from './chunk-SMBKXWML.js';
4
4
  import { Input } from './chunk-Q55TXPA3.js';
@@ -3,11 +3,11 @@ import { useLayerContainer, viewportBounds, LAYER_GAP, outsideBounds, placePopup
3
3
  import { usePopupLayer } from './chunk-R25RGPUB.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { cx } from './chunk-ODEQTPFM.js';
6
- import './Menu-BT5PMFJI.css';
6
+ import './Menu-XIJ4LQUT.css';
7
7
  import { useId, useState, useRef, useCallback, useLayoutEffect, useEffect } from 'react';
8
8
  import { createPortal } from 'react-dom';
9
9
  import { EllipsisVerticalGlyph, CheckGlyph } from '@crewlethq/icons/glyphs';
10
- import { jsx, jsxs } from 'react/jsx-runtime';
10
+ import { jsxs, jsx } from 'react/jsx-runtime';
11
11
 
12
12
  var ITEM_SELECTOR = "[role='menuitem'],[role='menuitemradio']";
13
13
  function runs(entries) {
@@ -34,10 +34,13 @@ function Menu({
34
34
  open: controlled,
35
35
  onOpenChange,
36
36
  align = "start",
37
+ renderTrigger,
38
+ header,
37
39
  className
38
40
  }) {
39
41
  const id = useId();
40
42
  const menuId = `${id}-menu`;
43
+ const headerId = `${id}-header`;
41
44
  const [uncontrolled, setUncontrolled] = useState(false);
42
45
  const open = controlled ?? uncontrolled;
43
46
  const triggerRef = useRef(null);
@@ -217,13 +220,14 @@ function Menu({
217
220
  },
218
221
  entry.key
219
222
  );
220
- const list = open ? /* @__PURE__ */ jsx(
223
+ const list = open ? /* @__PURE__ */ jsxs(
221
224
  "div",
222
225
  {
223
226
  className: cx("crewlet-menu", container !== null && container !== document.body ? "crewlet-menu--in-host" : null, className),
224
227
  id: menuId,
225
228
  role: "menu",
226
229
  "aria-label": label,
230
+ "aria-describedby": header ? headerId : void 0,
227
231
  tabIndex: -1,
228
232
  ref: (el) => {
229
233
  menu.current = el;
@@ -233,9 +237,16 @@ function Menu({
233
237
  onKeyDown: onMenuKeyDown,
234
238
  onPointerDown: (event) => event.stopPropagation(),
235
239
  onClick: (event) => event.stopPropagation(),
236
- children: runs(items).map(
237
- (run) => run.kind === "separator" ? /* @__PURE__ */ jsx("div", { role: "separator", className: "crewlet-menu__separator" }, run.key) : run.kind === "answers" ? /* @__PURE__ */ jsx("div", { role: "group", className: "crewlet-menu__group", children: run.items.map(item) }, run.key) : item(run.item)
238
- )
240
+ children: [
241
+ header ? (
242
+ // PRESENTATIONAL, so a menu holds only items to a screen reader, and
243
+ // the menu's description, so what it says is heard when it opens.
244
+ /* @__PURE__ */ jsx("div", { role: "presentation", id: headerId, className: "crewlet-menu__header", children: header })
245
+ ) : null,
246
+ runs(items).map(
247
+ (run) => run.kind === "separator" ? /* @__PURE__ */ jsx("div", { role: "separator", className: "crewlet-menu__separator" }, run.key) : run.kind === "answers" ? /* @__PURE__ */ jsx("div", { role: "group", className: "crewlet-menu__group", children: run.items.map(item) }, run.key) : item(run.item)
248
+ )
249
+ ]
239
250
  }
240
251
  ) : null;
241
252
  const shared = {
@@ -258,7 +269,7 @@ function Menu({
258
269
  onKeyDown: onTriggerKeyDown
259
270
  };
260
271
  return /* @__PURE__ */ jsxs("span", { className: "crewlet-menu-anchor", children: [
261
- trigger === void 0 ? /* @__PURE__ */ jsx(IconButton, { ...shared, label, icon: icon ?? /* @__PURE__ */ jsx(EllipsisVerticalGlyph, {}), size: "sm" }) : /* @__PURE__ */ jsx(Button, { ...shared, variant: triggerVariant, size: "small", leadingIcon: icon, children: trigger }),
272
+ renderTrigger ? renderTrigger(shared) : trigger === void 0 ? /* @__PURE__ */ jsx(IconButton, { ...shared, label, icon: icon ?? /* @__PURE__ */ jsx(EllipsisVerticalGlyph, {}), size: "sm" }) : /* @__PURE__ */ jsx(Button, { ...shared, variant: triggerVariant, size: "small", leadingIcon: icon, children: trigger }),
262
273
  list && container ? createPortal(list, container) : null
263
274
  ] });
264
275
  }
@@ -0,0 +1,102 @@
1
+ import { diffLines, diffStat, collapseDiff, DiffSummary, DiffView } from './chunk-BVWSLXTY.js';
2
+ import { SegmentedControl } from './chunk-26PEPONN.js';
3
+ import { EmptyState } from './chunk-RA2ULG6H.js';
4
+ import { useHeadingLevel, headingTag, nextHeadingLevel, HeadingLevelProvider } from './chunk-PQADV6P6.js';
5
+ import { cx } from './chunk-ODEQTPFM.js';
6
+ import './VersionCompare-UUZOPTQO.css';
7
+ import { useId, useMemo } from 'react';
8
+ import { CheckGlyph } from '@crewlethq/icons/glyphs';
9
+ import { jsxs, jsx } from 'react/jsx-runtime';
10
+
11
+ var VERSION_COMPARE_LENSES = ["changes", "versions"];
12
+ var VERSION_COMPARE_LABELS = {
13
+ title: (from, to) => `${from} and ${to}`,
14
+ lens: "Compare by",
15
+ changes: "Changes",
16
+ versions: "Side by side",
17
+ region: (from, to) => `Changes from ${from} to ${to}`,
18
+ identicalTitle: "Nothing changed",
19
+ identicalDescription: "The two versions hold the same text."
20
+ };
21
+ function VersionCompare({
22
+ fromLabel,
23
+ toLabel,
24
+ title,
25
+ description,
26
+ lens,
27
+ onLensChange,
28
+ before,
29
+ after,
30
+ changes,
31
+ context = 3,
32
+ from,
33
+ to,
34
+ note,
35
+ actions,
36
+ labels: overrides,
37
+ className
38
+ }) {
39
+ const labels = overrides ? { ...VERSION_COMPARE_LABELS, ...overrides } : VERSION_COMPARE_LABELS;
40
+ const headingID = useId();
41
+ const level = useHeadingLevel();
42
+ const Heading = headingTag(level);
43
+ const inner = nextHeadingLevel(level);
44
+ const PaneHeading = headingTag(inner);
45
+ const hostChanges = changes !== void 0 && changes !== null;
46
+ const canChanges = hostChanges || before !== void 0 && after !== void 0;
47
+ const canVersions = from !== void 0 && from !== null || to !== void 0 && to !== null;
48
+ const shown = lens === "versions" ? canVersions ? "versions" : "changes" : canChanges ? "changes" : "versions";
49
+ const diff = useMemo(() => {
50
+ if (hostChanges || shown !== "changes" || before === void 0 || after === void 0) return null;
51
+ const lines = diffLines(before, after);
52
+ return { sections: collapseDiff(lines, context), stat: diffStat(lines) };
53
+ }, [hostChanges, shown, before, after, context]);
54
+ const panes = [
55
+ { key: "from", label: fromLabel, node: from },
56
+ { key: "to", label: toLabel, node: to }
57
+ ].filter((pane) => pane.node !== void 0 && pane.node !== null);
58
+ return /* @__PURE__ */ jsxs("section", { className: cx("crewlet-version-compare", className), "aria-labelledby": headingID, children: [
59
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-version-compare__head", children: [
60
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-version-compare__titles", children: [
61
+ /* @__PURE__ */ jsx(Heading, { className: "crewlet-version-compare__title", id: headingID, children: title ?? labels.title(fromLabel, toLabel) }),
62
+ description !== void 0 && description !== null && /* @__PURE__ */ jsx("p", { className: "crewlet-version-compare__description", children: description })
63
+ ] }),
64
+ onLensChange && canChanges && canVersions && /* @__PURE__ */ jsx(
65
+ SegmentedControl,
66
+ {
67
+ semantics: "radio",
68
+ activate: "manual",
69
+ size: "sm",
70
+ label: labels.lens,
71
+ value: shown,
72
+ onValueChange: onLensChange,
73
+ options: [
74
+ { value: "changes", label: labels.changes },
75
+ { value: "versions", label: labels.versions }
76
+ ]
77
+ }
78
+ ),
79
+ actions
80
+ ] }),
81
+ /* @__PURE__ */ jsx(HeadingLevelProvider, { level: inner, children: shown === "changes" ? diff ? diff.stat.identical ? /* @__PURE__ */ jsx(
82
+ EmptyState,
83
+ {
84
+ size: "compact",
85
+ icon: /* @__PURE__ */ jsx(CheckGlyph, { size: "xl" }),
86
+ title: labels.identicalTitle,
87
+ description: labels.identicalDescription
88
+ }
89
+ ) : /* @__PURE__ */ jsxs("div", { className: "crewlet-version-compare__changes", children: [
90
+ /* @__PURE__ */ jsx(DiffSummary, { added: diff.stat.added, removed: diff.stat.removed, against: fromLabel }),
91
+ /* @__PURE__ */ jsx(DiffView, { sections: diff.sections, "aria-label": labels.region(fromLabel, toLabel) })
92
+ ] }) : /* @__PURE__ */ jsx("div", { className: "crewlet-version-compare__changes", children: changes }) : /* @__PURE__ */ jsxs("div", { className: "crewlet-version-compare__versions", children: [
93
+ note !== void 0 && note !== null && /* @__PURE__ */ jsx("p", { className: "crewlet-version-compare__note", children: note }),
94
+ /* @__PURE__ */ jsx("div", { className: "crewlet-version-compare__panes", children: panes.map((pane) => /* @__PURE__ */ jsxs("div", { className: "crewlet-version-compare__pane", children: [
95
+ /* @__PURE__ */ jsx(PaneHeading, { className: "crewlet-version-compare__pane-title", children: pane.label }),
96
+ /* @__PURE__ */ jsx(HeadingLevelProvider, { level: nextHeadingLevel(inner), children: pane.node })
97
+ ] }, pane.key)) })
98
+ ] }) })
99
+ ] });
100
+ }
101
+
102
+ export { VERSION_COMPARE_LABELS, VERSION_COMPARE_LENSES, VersionCompare };
@@ -1,5 +1,5 @@
1
1
  import { Skeleton } from './chunk-W5J7R5EB.js';
2
- import { Modal } from './chunk-7IJARUIF.js';
2
+ import { Modal } from './chunk-F4DPAJCN.js';
3
3
  import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
4
4
  import { Input } from './chunk-Q55TXPA3.js';
5
5
  import { Popover } from './chunk-BWRGFKJP.js';
@@ -1,15 +1,15 @@
1
1
  import { PageHeader } from './chunk-HOXGBLEY.js';
2
2
  import { Toolbar } from './chunk-PGRPNDWM.js';
3
- import { DataTable } from './chunk-AJESMWJS.js';
4
- import { Menu } from './chunk-JULMZNGT.js';
5
- import { DateTimePicker } from './chunk-L4H7SSWF.js';
3
+ import { DateTimePicker } from './chunk-RE6ED5TO.js';
4
+ import { DataTable } from './chunk-LG244LY7.js';
6
5
  import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
7
6
  import { Kbd } from './chunk-SMBKXWML.js';
8
- import { Button } from './chunk-OG6CL3EB.js';
9
7
  import { Input } from './chunk-Q55TXPA3.js';
10
8
  import { RelativeTime } from './chunk-3CZ7XAVY.js';
11
9
  import { Popover } from './chunk-BWRGFKJP.js';
12
10
  import { Count } from './chunk-BHHIIK3M.js';
11
+ import { Menu } from './chunk-4MIM356A.js';
12
+ import { Button } from './chunk-OG6CL3EB.js';
13
13
  import { IconButton } from './chunk-4FQT6ZAY.js';
14
14
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
15
15
  import { cx } from './chunk-ODEQTPFM.js';