@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,187 @@
1
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
2
+ import { cx } from './chunk-ODEQTPFM.js';
3
+ import './DiffView-RVLIHTKG.css';
4
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
+
6
+ var DIFF_VIEW_LABELS = {
7
+ skipped: (count) => count === 1 ? "1 unchanged line" : `${count.toLocaleString()} unchanged lines`,
8
+ added: "Added",
9
+ removed: "Removed",
10
+ region: "Line changes"
11
+ };
12
+ function DiffView({ sections, labels: overrides, className, "aria-label": ariaLabel, ...rest }) {
13
+ const labels = overrides ? { ...DIFF_VIEW_LABELS, ...overrides } : DIFF_VIEW_LABELS;
14
+ return /* @__PURE__ */ jsx("div", { ...rest, role: "group", "aria-label": ariaLabel ?? labels.region, className: cx("crewlet-diff-view", className), children: sections.map((section, s) => (
15
+ // A section has no identity of its own: a diff is computed whole, so
16
+ // its position is what tells two sections apart.
17
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-diff-view__section", children: [
18
+ section.skipped > 0 && /* @__PURE__ */ jsx("div", { className: "crewlet-diff-view__skip", children: labels.skipped(section.skipped) }),
19
+ section.lines.map((line, i) => (
20
+ // THE CLASS NAMES ARE LITERALS, never assembled from the value: a
21
+ // stylesheet gate that cannot see a class cannot tell a rule this
22
+ // file relies on from one nothing uses.
23
+ /* @__PURE__ */ jsxs(
24
+ "div",
25
+ {
26
+ className: cx(
27
+ "crewlet-diff-view__line",
28
+ line.kind === "add" && "crewlet-diff-view__line--add",
29
+ line.kind === "remove" && "crewlet-diff-view__line--remove"
30
+ ),
31
+ children: [
32
+ /* @__PURE__ */ jsx("span", { className: "crewlet-diff-view__number", "aria-hidden": "true", children: line.before ?? "" }),
33
+ /* @__PURE__ */ jsx("span", { className: "crewlet-diff-view__number", "aria-hidden": "true", children: line.after ?? "" }),
34
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-diff-view__mark", children: [
35
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: line.kind === "add" ? "+" : line.kind === "remove" ? "\u2212" : " " }),
36
+ line.kind === "add" && /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.added }),
37
+ line.kind === "remove" && /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.removed })
38
+ ] }),
39
+ /* @__PURE__ */ jsx("span", { className: "crewlet-diff-view__text", children: line.text || " " })
40
+ ]
41
+ },
42
+ i
43
+ )
44
+ ))
45
+ ] }, s)
46
+ )) });
47
+ }
48
+ var DIFF_SUMMARY_LABELS = {
49
+ spoken: (added, removed) => `${added === 1 ? "1 line" : `${added.toLocaleString()} lines`} added, ${removed.toLocaleString()} removed`,
50
+ against: (what) => `against ${what}`
51
+ };
52
+ function DiffSummary({ added, removed, against, labels: overrides, className, ...rest }) {
53
+ const labels = overrides ? { ...DIFF_SUMMARY_LABELS, ...overrides } : DIFF_SUMMARY_LABELS;
54
+ return /* @__PURE__ */ jsxs("p", { ...rest, className: cx("crewlet-diff-summary", className), children: [
55
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", children: [
56
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-diff-summary__added", children: [
57
+ "+",
58
+ added.toLocaleString()
59
+ ] }),
60
+ " ",
61
+ /* @__PURE__ */ jsx("span", { className: "crewlet-diff-summary__removed", children: `\u2212${removed.toLocaleString()}` })
62
+ ] }),
63
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.spoken(added, removed) }),
64
+ against !== void 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
65
+ " ",
66
+ labels.against(against)
67
+ ] })
68
+ ] });
69
+ }
70
+
71
+ // src/DiffView/diff.ts
72
+ var MAX_DIFF_LINES = 2e3;
73
+ function splitLines(text) {
74
+ const normalised = (text ?? "").replace(/\r\n?/g, "\n");
75
+ if (normalised === "") return [];
76
+ return normalised.replace(/\n$/, "").split("\n");
77
+ }
78
+ function diffLines(before, after) {
79
+ const a = splitLines(before);
80
+ const b = splitLines(after);
81
+ if (a.length > MAX_DIFF_LINES || b.length > MAX_DIFF_LINES) {
82
+ return [
83
+ ...a.map((text, i) => ({ kind: "remove", text, before: i + 1, after: null })),
84
+ ...b.map((text, i) => ({ kind: "add", text, before: null, after: i + 1 }))
85
+ ];
86
+ }
87
+ return walk(a, b, common(a, b));
88
+ }
89
+ function diffStat(diff) {
90
+ let added = 0;
91
+ let removed = 0;
92
+ for (const line of diff) {
93
+ if (line.kind === "add") added++;
94
+ if (line.kind === "remove") removed++;
95
+ }
96
+ return { added, removed, identical: added === 0 && removed === 0 };
97
+ }
98
+ function collapseDiff(diff, context = 3) {
99
+ const keep = new Array(diff.length).fill(false);
100
+ diff.forEach((line, i) => {
101
+ if (line.kind === "same") return;
102
+ for (let k = Math.max(0, i - context); k <= Math.min(diff.length - 1, i + context); k++) {
103
+ keep[k] = true;
104
+ }
105
+ });
106
+ const out = [];
107
+ let skipped = 0;
108
+ let current = null;
109
+ diff.forEach((line, i) => {
110
+ if (keep[i]) {
111
+ if (!current) {
112
+ current = [];
113
+ out.push({ lines: current, skipped });
114
+ skipped = 0;
115
+ }
116
+ current.push(line);
117
+ return;
118
+ }
119
+ skipped++;
120
+ current = null;
121
+ });
122
+ return out;
123
+ }
124
+ function firstChangedLines(sections, max) {
125
+ const out = [];
126
+ let shown = 0;
127
+ let more = 0;
128
+ for (const section of sections) {
129
+ const kept = [];
130
+ for (const line of section.lines) {
131
+ const changed = line.kind !== "same";
132
+ if (shown >= max) {
133
+ if (changed) more++;
134
+ continue;
135
+ }
136
+ if (changed) shown++;
137
+ kept.push(line);
138
+ }
139
+ if (kept.length > 0) out.push({ lines: kept, skipped: section.skipped });
140
+ }
141
+ return { sections: out, more };
142
+ }
143
+ function common(a, b) {
144
+ const table = [];
145
+ for (let i = 0; i <= a.length; i++) table.push(new Uint32Array(b.length + 1));
146
+ for (let i = a.length - 1; i >= 0; i--) {
147
+ const row = table[i];
148
+ const next = table[i + 1];
149
+ for (let j = b.length - 1; j >= 0; j--) {
150
+ row[j] = a[i] === b[j] ? next[j + 1] + 1 : Math.max(next[j], row[j + 1]);
151
+ }
152
+ }
153
+ return table;
154
+ }
155
+ function walk(a, b, table) {
156
+ const out = [];
157
+ let i = 0;
158
+ let j = 0;
159
+ while (i < a.length && j < b.length) {
160
+ if (a[i] === b[j]) {
161
+ out.push({ kind: "same", text: a[i], before: i + 1, after: j + 1 });
162
+ i++;
163
+ j++;
164
+ continue;
165
+ }
166
+ const down = table[i + 1][j];
167
+ const right = table[i][j + 1];
168
+ if (down >= right) {
169
+ out.push({ kind: "remove", text: a[i], before: i + 1, after: null });
170
+ i++;
171
+ } else {
172
+ out.push({ kind: "add", text: b[j], before: null, after: j + 1 });
173
+ j++;
174
+ }
175
+ }
176
+ while (i < a.length) {
177
+ out.push({ kind: "remove", text: a[i], before: i + 1, after: null });
178
+ i++;
179
+ }
180
+ while (j < b.length) {
181
+ out.push({ kind: "add", text: b[j], before: null, after: j + 1 });
182
+ j++;
183
+ }
184
+ return out;
185
+ }
186
+
187
+ export { DIFF_SUMMARY_LABELS, DIFF_VIEW_LABELS, DiffSummary, DiffView, MAX_DIFF_LINES, collapseDiff, diffLines, diffStat, firstChangedLines, splitLines };
@@ -1,12 +1,13 @@
1
1
  import { useMediaMatch } from './chunk-IG75YMUN.js';
2
+ import { Menu } from './chunk-4MIM356A.js';
2
3
  import { useModalLayer, focusables } from './chunk-R25RGPUB.js';
3
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
4
5
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
5
6
  import { cx } from './chunk-ODEQTPFM.js';
6
- import './AppShell-RUNVM4WH.css';
7
+ import './AppShell-APHZKJYH.css';
7
8
  import { createContext, forwardRef, useContext, useId, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
8
9
  import { breakpoint } from '@crewlethq/tokens';
9
- import { ChevronUpGlyph, ChevronRightGlyph, MenuGlyph } from '@crewlethq/icons/glyphs';
10
+ import { ChevronUpGlyph, ChevronsUpDownGlyph, ChevronRightGlyph, MenuGlyph } from '@crewlethq/icons/glyphs';
10
11
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
11
12
 
12
13
  function AccountRow({
@@ -14,6 +15,7 @@ function AccountRow({
14
15
  name,
15
16
  detail,
16
17
  href,
18
+ menu,
17
19
  current = false,
18
20
  title,
19
21
  linkSuffix = ", your account",
@@ -32,7 +34,29 @@ function AccountRow({
32
34
  ] })
33
35
  ] });
34
36
  return /* @__PURE__ */ jsxs("div", { className: cx("crewlet-account-row", className), children: [
35
- href === void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-account-row__who", children: who }) : /* @__PURE__ */ jsxs(
37
+ menu ? /* @__PURE__ */ jsx(
38
+ Menu,
39
+ {
40
+ label: menu.label ?? "Your account",
41
+ items: menu.items,
42
+ header: menu.header,
43
+ renderTrigger: (trigger) => /* @__PURE__ */ jsxs(
44
+ "button",
45
+ {
46
+ type: "button",
47
+ className: "crewlet-account-row__who",
48
+ title,
49
+ "data-current": current ? "" : void 0,
50
+ ...trigger,
51
+ children: [
52
+ who,
53
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: linkSuffix }),
54
+ /* @__PURE__ */ jsx(ChevronsUpDownGlyph, { size: "xs", className: "crewlet-account-row__go", "aria-hidden": "true" })
55
+ ]
56
+ }
57
+ )
58
+ }
59
+ ) : href === void 0 ? /* @__PURE__ */ jsx("span", { className: "crewlet-account-row__who", children: who }) : /* @__PURE__ */ jsxs(
36
60
  "a",
37
61
  {
38
62
  className: "crewlet-account-row__who",
@@ -0,0 +1,176 @@
1
+ import { Document } from './chunk-WI6K5M5X.js';
2
+ import { Composer } from './chunk-VJLYGPPE.js';
3
+ import { PrincipalAvatar } from './chunk-4KP4RC3O.js';
4
+ import { toInstant, RelativeTime, formatExact, toISO } from './chunk-3CZ7XAVY.js';
5
+ import { useHeadingLevel, headingTag } from './chunk-PQADV6P6.js';
6
+ import { Count } from './chunk-BHHIIK3M.js';
7
+ import { Button } from './chunk-OG6CL3EB.js';
8
+ import { IconButton } from './chunk-4FQT6ZAY.js';
9
+ import { cx } from './chunk-ODEQTPFM.js';
10
+ import './CommentThread-7VXOFTZO.css';
11
+ import { useState, useRef, useId, useMemo } from 'react';
12
+ import { MessageSquareGlyph, XGlyph } from '@crewlethq/icons/glyphs';
13
+ import { jsxs, jsx } from 'react/jsx-runtime';
14
+
15
+ var COMMENT_THREAD_LABELS = {
16
+ heading: "Comments",
17
+ empty: "Nobody has commented.",
18
+ system: "System",
19
+ edited: "(edited)",
20
+ inReplyTo: (name) => `in reply to ${name}`,
21
+ replyTo: (name) => `Reply to ${name}`,
22
+ reply: "Reply",
23
+ replying: (name) => `Replying to ${name}`,
24
+ stopReplying: "Stop replying",
25
+ field: (subject, replyingTo) => replyingTo !== void 0 ? `Reply to ${replyingTo}` : subject !== void 0 ? `Comment on \u201C${subject}\u201D` : "Write a comment",
26
+ placeholder: "Write a comment",
27
+ submit: "Comment",
28
+ submitReply: "Reply",
29
+ emptyDraft: "Write the comment first."
30
+ };
31
+ function CommentThread({
32
+ comments,
33
+ subject,
34
+ onSubmit,
35
+ disabledReason,
36
+ busy = false,
37
+ hint,
38
+ notice,
39
+ heading = true,
40
+ now,
41
+ labels: overrides,
42
+ className
43
+ }) {
44
+ const labels = overrides ? { ...COMMENT_THREAD_LABELS, ...overrides } : COMMENT_THREAD_LABELS;
45
+ const [draft, setDraft] = useState("");
46
+ const [replyTo, setReplyTo] = useState(null);
47
+ const [sending, setSending] = useState(false);
48
+ const field = useRef(null);
49
+ const headingID = useId();
50
+ const level = useHeadingLevel();
51
+ const Heading = headingTag(level);
52
+ const byID = useMemo(() => new Map(comments.map((comment) => [comment.id, comment])), [comments]);
53
+ const nameOf = (comment) => comment.author?.name ?? labels.system;
54
+ const body = draft.trim();
55
+ const pending = busy || sending;
56
+ const reason = disabledReason ?? (body === "" ? labels.emptyDraft : void 0);
57
+ const blocked = pending || reason !== void 0;
58
+ const send = async () => {
59
+ if (!onSubmit || blocked) return;
60
+ setSending(true);
61
+ try {
62
+ const result = await onSubmit(body, replyTo);
63
+ if (result !== false) {
64
+ setDraft("");
65
+ setReplyTo(null);
66
+ }
67
+ } finally {
68
+ setSending(false);
69
+ }
70
+ };
71
+ const startReply = (comment) => {
72
+ setReplyTo(comment);
73
+ field.current?.focus();
74
+ };
75
+ return /* @__PURE__ */ jsxs(
76
+ "section",
77
+ {
78
+ className: cx("crewlet-comment-thread", className),
79
+ "aria-labelledby": heading ? headingID : void 0,
80
+ "aria-label": heading ? void 0 : labels.heading,
81
+ children: [
82
+ heading && /* @__PURE__ */ jsxs(Heading, { className: "crewlet-comment-thread__heading", id: headingID, children: [
83
+ /* @__PURE__ */ jsx(MessageSquareGlyph, { size: "sm" }),
84
+ labels.heading,
85
+ /* @__PURE__ */ jsx(Count, { value: comments.length })
86
+ ] }),
87
+ comments.length === 0 ? /* @__PURE__ */ jsx("p", { className: "crewlet-comment-thread__empty", children: labels.empty }) : (
88
+ // The markers are off, and WebKit drops the list role with them, so
89
+ // the role is declared and the redundant-role rule silenced for that.
90
+ // eslint-disable-next-line jsx-a11y/no-redundant-roles
91
+ /* @__PURE__ */ jsx("ol", { role: "list", className: "crewlet-comment-thread__list", children: comments.map((comment) => {
92
+ const parent = comment.replyTo !== void 0 ? byID.get(comment.replyTo) : void 0;
93
+ const created = toInstant(comment.createdAt);
94
+ const edited = toInstant(comment.editedAt);
95
+ const name = nameOf(comment);
96
+ return /* @__PURE__ */ jsxs("li", { className: "crewlet-comment-thread__comment", children: [
97
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-comment-thread__head", children: [
98
+ comment.author ? /* @__PURE__ */ jsxs("span", { className: "crewlet-comment-thread__author", children: [
99
+ /* @__PURE__ */ jsx(PrincipalAvatar, { principal: comment.author, size: "xs" }),
100
+ /* @__PURE__ */ jsx("span", { className: "crewlet-comment-thread__name", children: comment.author.name })
101
+ ] }) : /* @__PURE__ */ jsx("span", { className: "crewlet-comment-thread__name", children: labels.system }),
102
+ /* @__PURE__ */ jsx(RelativeTime, { value: comment.createdAt, now, className: "crewlet-comment-thread__meta" }),
103
+ edited !== null && edited !== created && /* @__PURE__ */ jsx(
104
+ "time",
105
+ {
106
+ className: "crewlet-comment-thread__meta",
107
+ dateTime: toISO(comment.editedAt) ?? void 0,
108
+ title: formatExact(comment.editedAt) ?? void 0,
109
+ children: labels.edited
110
+ }
111
+ ),
112
+ parent && /* @__PURE__ */ jsx("span", { className: "crewlet-comment-thread__meta", children: labels.inReplyTo(nameOf(parent)) }),
113
+ onSubmit && /* @__PURE__ */ jsx(
114
+ Button,
115
+ {
116
+ size: "small",
117
+ variant: "ghost",
118
+ className: "crewlet-comment-thread__reply",
119
+ "aria-label": labels.replyTo(name),
120
+ onClick: () => startReply(comment),
121
+ children: labels.reply
122
+ }
123
+ )
124
+ ] }),
125
+ /* @__PURE__ */ jsx(Document, { size: "sm", measure: "none", children: comment.body })
126
+ ] }, comment.id);
127
+ }) })
128
+ ),
129
+ onSubmit && /* @__PURE__ */ jsxs("div", { className: "crewlet-comment-thread__compose", children: [
130
+ replyTo && /* @__PURE__ */ jsxs("span", { className: "crewlet-comment-thread__replying", children: [
131
+ labels.replying(nameOf(replyTo)),
132
+ /* @__PURE__ */ jsx(
133
+ IconButton,
134
+ {
135
+ size: "sm",
136
+ variant: "ghost",
137
+ label: labels.stopReplying,
138
+ icon: /* @__PURE__ */ jsx(XGlyph, { size: "sm" }),
139
+ onClick: () => {
140
+ setReplyTo(null);
141
+ field.current?.focus();
142
+ }
143
+ }
144
+ )
145
+ ] }),
146
+ /* @__PURE__ */ jsx(
147
+ Composer,
148
+ {
149
+ ref: field,
150
+ value: draft,
151
+ onChange: setDraft,
152
+ onSubmit: () => void send(),
153
+ label: labels.field(subject, replyTo ? nameOf(replyTo) : void 0),
154
+ placeholder: labels.placeholder,
155
+ actions: /* @__PURE__ */ jsx(
156
+ Button,
157
+ {
158
+ size: "small",
159
+ variant: "secondary",
160
+ loading: pending,
161
+ disabledReason: pending ? void 0 : reason,
162
+ onClick: () => void send(),
163
+ children: replyTo ? labels.submitReply : labels.submit
164
+ }
165
+ )
166
+ }
167
+ ),
168
+ (disabledReason ?? hint) !== void 0 && /* @__PURE__ */ jsx("p", { className: "crewlet-comment-thread__hint", children: disabledReason ?? hint }),
169
+ notice
170
+ ] })
171
+ ]
172
+ }
173
+ );
174
+ }
175
+
176
+ export { COMMENT_THREAD_LABELS, CommentThread };
@@ -0,0 +1,96 @@
1
+ import { PRESENCE_LABELS, presenceOrder, collapseSessions } from './chunk-S35G6PNC.js';
2
+ import { Avatar } from './chunk-5OM447RW.js';
3
+ import { headingTag, useHeadingLevel } from './chunk-PQADV6P6.js';
4
+ import { Button } from './chunk-OG6CL3EB.js';
5
+ import { IconButton } from './chunk-4FQT6ZAY.js';
6
+ import { cx } from './chunk-ODEQTPFM.js';
7
+ import './PeopleRail-R64WVOSH.css';
8
+ import { useId } from 'react';
9
+ import { isCrewletCharacter } from '@crewlethq/icons/characters';
10
+ import { XGlyph } from '@crewlethq/icons/glyphs';
11
+ import { jsxs, jsx } from 'react/jsx-runtime';
12
+
13
+ var PEOPLE_RAIL_LABELS = {
14
+ title: "People here",
15
+ close: "Close people",
16
+ you: "You",
17
+ nobody: "Nobody else is here right now.",
18
+ follow: "Follow",
19
+ following: "Following",
20
+ followName: (name) => `Follow ${name}`,
21
+ stopFollowingName: (name) => `Stop following ${name}`,
22
+ activity: PRESENCE_LABELS.activity
23
+ };
24
+ function peopleRailKey(member) {
25
+ return member.seat ?? member.id;
26
+ }
27
+ function PeopleRail({
28
+ members,
29
+ self,
30
+ following,
31
+ onFollow,
32
+ onClose,
33
+ actions,
34
+ labels: overrides,
35
+ className
36
+ }) {
37
+ const labels = overrides ? { ...PEOPLE_RAIL_LABELS, ...overrides } : PEOPLE_RAIL_LABELS;
38
+ const headingID = useId();
39
+ const Heading = headingTag(useHeadingLevel());
40
+ const others = presenceOrder(collapseSessions(members));
41
+ return /* @__PURE__ */ jsxs("aside", { className: cx("crewlet-people-rail", className), "aria-labelledby": headingID, children: [
42
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-people-rail__head", children: [
43
+ /* @__PURE__ */ jsx(Heading, { className: "crewlet-people-rail__title", id: headingID, children: labels.title }),
44
+ onClose && /* @__PURE__ */ jsx(IconButton, { size: "sm", variant: "ghost", label: labels.close, icon: /* @__PURE__ */ jsx(XGlyph, { size: "sm" }), onClick: onClose })
45
+ ] }),
46
+ /* @__PURE__ */ jsxs("ul", { role: "list", className: "crewlet-people-rail__list", children: [
47
+ self && /* @__PURE__ */ jsxs("li", { className: "crewlet-people-rail__person", children: [
48
+ /* @__PURE__ */ jsx(PresenceBadge, { member: self }),
49
+ /* @__PURE__ */ jsx("span", { className: "crewlet-people-rail__text", children: /* @__PURE__ */ jsx("span", { className: "crewlet-people-rail__name", children: labels.you }) })
50
+ ] }),
51
+ others.map((member) => {
52
+ const key = peopleRailKey(member);
53
+ const followed = following !== void 0 && following === key;
54
+ const extra = actions?.(member);
55
+ return /* @__PURE__ */ jsxs("li", { className: "crewlet-people-rail__person", children: [
56
+ /* @__PURE__ */ jsx(PresenceBadge, { member }),
57
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-people-rail__text", children: [
58
+ /* @__PURE__ */ jsx("span", { className: "crewlet-people-rail__name", children: member.name }),
59
+ /* @__PURE__ */ jsx("span", { className: "crewlet-people-rail__where", children: member.location ?? labels.activity(member.activity, member.kind) })
60
+ ] }),
61
+ extra !== void 0 && extra !== null || onFollow ? /* @__PURE__ */ jsxs("span", { className: "crewlet-people-rail__actions", children: [
62
+ extra,
63
+ onFollow && /* @__PURE__ */ jsx(
64
+ Button,
65
+ {
66
+ size: "small",
67
+ variant: followed ? "primary" : "secondary",
68
+ pressed: followed,
69
+ "aria-label": followed ? labels.stopFollowingName(member.name) : labels.followName(member.name),
70
+ onClick: () => onFollow(followed ? null : member),
71
+ children: followed ? labels.following : labels.follow
72
+ }
73
+ )
74
+ ] }) : null
75
+ ] }, key);
76
+ })
77
+ ] }),
78
+ others.length === 0 && /* @__PURE__ */ jsx("p", { className: "crewlet-people-rail__note", children: labels.nobody })
79
+ ] });
80
+ }
81
+ function PresenceBadge({ member }) {
82
+ return /* @__PURE__ */ jsx(
83
+ Avatar,
84
+ {
85
+ name: member.name,
86
+ kind: member.kind,
87
+ src: member.src,
88
+ character: member.character !== void 0 && isCrewletCharacter(member.character) ? member.character : void 0,
89
+ hue: member.kind === "agent" ? member.hue : void 0,
90
+ size: "sm",
91
+ decorative: true
92
+ }
93
+ );
94
+ }
95
+
96
+ export { PEOPLE_RAIL_LABELS, PeopleRail, peopleRailKey };
@@ -1,10 +1,10 @@
1
- import { Menu } from './chunk-JULMZNGT.js';
2
1
  import { useClipboard } from './chunk-STJ7QBC3.js';
3
- import { Modal } from './chunk-7IJARUIF.js';
4
- import { Button } from './chunk-OG6CL3EB.js';
2
+ import { Modal } from './chunk-F4DPAJCN.js';
5
3
  import { Checkbox } from './chunk-I6NZKJGK.js';
6
4
  import { EmptyValue } from './chunk-EUCC37WY.js';
7
5
  import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
6
+ import { Menu } from './chunk-4MIM356A.js';
7
+ import { Button } from './chunk-OG6CL3EB.js';
8
8
  import { IconButton } from './chunk-4FQT6ZAY.js';
9
9
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
10
10
  import { cx } from './chunk-ODEQTPFM.js';
@@ -0,0 +1,88 @@
1
+ import { Tag } from './chunk-GFV4LJHH.js';
2
+ import { Link } from './chunk-FBSEJO2C.js';
3
+ import { RelativeTime } from './chunk-3CZ7XAVY.js';
4
+ import { EmptyValue } from './chunk-EUCC37WY.js';
5
+ import { Avatar } from './chunk-5OM447RW.js';
6
+ import { VisuallyHidden } from './chunk-BZFWDIV6.js';
7
+ import { cx } from './chunk-ODEQTPFM.js';
8
+ import './TableCells-3VN5N2M3.css';
9
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
+
11
+ function TableKeyCell({ value, text, href, emptyLabel = "Not set" }) {
12
+ if (!value) return /* @__PURE__ */ jsx(EmptyValue, { label: emptyLabel });
13
+ if (href !== void 0) {
14
+ return /* @__PURE__ */ jsx(Link, { href, size: "inherit", className: "crewlet-table-cell__key", title: value, children: text ?? value });
15
+ }
16
+ return /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__key", title: value, children: text ?? value });
17
+ }
18
+ var groupFigure = (value) => value.toLocaleString();
19
+ function TableNumberCell({
20
+ value,
21
+ format = groupFigure,
22
+ suffix,
23
+ title,
24
+ emptyLabel = "Nothing recorded"
25
+ }) {
26
+ if (value === null || value === void 0 || !Number.isFinite(value)) return /* @__PURE__ */ jsx(EmptyValue, { label: emptyLabel });
27
+ return /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__number", title, children: [
28
+ format(value),
29
+ suffix
30
+ ] });
31
+ }
32
+ function TableDateCell({ value, now, emptyLabel = "Never" }) {
33
+ if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(EmptyValue, { label: emptyLabel });
34
+ return /* @__PURE__ */ jsx(RelativeTime, { value, now, emptyLabel, className: "crewlet-table-cell__date" });
35
+ }
36
+ function TableEntityCell({
37
+ name,
38
+ kind = "agent",
39
+ badge,
40
+ href,
41
+ title,
42
+ size = "xs",
43
+ emptyLabel = "Nobody"
44
+ }) {
45
+ if (!name) return /* @__PURE__ */ jsx(EmptyValue, { label: emptyLabel });
46
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__badge", "aria-hidden": "true", children: badge ?? /* @__PURE__ */ jsx(Avatar, { name, size, kind, decorative: true }) }),
48
+ /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__truncate", children: name })
49
+ ] });
50
+ if (href !== void 0) {
51
+ return /* @__PURE__ */ jsx(Link, { href, variant: "subtle", size: "inherit", className: "crewlet-table-cell__entity", title: title ?? name, children: inner });
52
+ }
53
+ return /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__entity", title: title ?? name, children: inner });
54
+ }
55
+ function TableStatusCell({ label, glyph, tone = "neutral", title }) {
56
+ return /* @__PURE__ */ jsxs("span", { className: cx("crewlet-table-cell__status", `crewlet-table-cell__status--${tone}`), title: title ?? label, children: [
57
+ /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__glyph", "aria-hidden": "true", children: glyph }),
58
+ /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__truncate", children: label })
59
+ ] });
60
+ }
61
+ var TABLE_TAGS_CELL_LABELS = {
62
+ more: (count) => count === 1 ? "1 more tag" : `${count.toLocaleString()} more tags`
63
+ };
64
+ function TableTagsCell({ tags, max = 3, emptyLabel = "No tags", labels: overrides }) {
65
+ const list = tags ?? [];
66
+ if (list.length === 0) return /* @__PURE__ */ jsx(EmptyValue, { label: emptyLabel });
67
+ const labels = overrides ? { ...TABLE_TAGS_CELL_LABELS, ...overrides } : TABLE_TAGS_CELL_LABELS;
68
+ const shown = list.slice(0, max);
69
+ const rest = list.length - shown.length;
70
+ return /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__tags", children: [
71
+ shown.map((tag) => /* @__PURE__ */ jsx(Tag, { appearance: "outline", size: "xs", children: tag }, tag)),
72
+ rest > 0 && /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__more", title: list.slice(max).join(", "), children: [
73
+ /* @__PURE__ */ jsxs("span", { "aria-hidden": "true", children: [
74
+ "+",
75
+ rest
76
+ ] }),
77
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: labels.more(rest) })
78
+ ] })
79
+ ] });
80
+ }
81
+ function TableTextCell({ children, icon, mark }) {
82
+ return /* @__PURE__ */ jsxs("span", { className: "crewlet-table-cell__text", children: [
83
+ mark ?? (icon !== void 0 && icon !== null ? /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__icon", "aria-hidden": "true", children: icon }) : null),
84
+ /* @__PURE__ */ jsx("span", { className: "crewlet-table-cell__truncate", children })
85
+ ] });
86
+ }
87
+
88
+ export { TABLE_TAGS_CELL_LABELS, TableDateCell, TableEntityCell, TableKeyCell, TableNumberCell, TableStatusCell, TableTagsCell, TableTextCell };
@@ -0,0 +1,35 @@
1
+ import { Button } from './chunk-OG6CL3EB.js';
2
+ import { cx } from './chunk-ODEQTPFM.js';
3
+ import './ConflictNote-DH77PYF7.css';
4
+ import { CircleAlertGlyph } from '@crewlethq/icons/glyphs';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ var CONFLICT_NOTE_LABELS = {
8
+ changedBy: (subject, name) => `${subject} was changed by ${name} since you opened it. Look again, then retry.`,
9
+ changed: (subject) => `${subject} was changed since you opened it. Look again, then retry.`,
10
+ dismiss: "Dismiss"
11
+ };
12
+ function ConflictNote({
13
+ subject,
14
+ changedBy,
15
+ message,
16
+ onDismiss,
17
+ actions,
18
+ labels: overrides,
19
+ className,
20
+ ...rest
21
+ }) {
22
+ const labels = overrides ? { ...CONFLICT_NOTE_LABELS, ...overrides } : CONFLICT_NOTE_LABELS;
23
+ const named = changedBy !== void 0 && changedBy !== "";
24
+ const sentence = named ? labels.changedBy(subject, changedBy) : message ?? labels.changed(subject);
25
+ return /* @__PURE__ */ jsxs("div", { ...rest, role: "alert", className: cx("crewlet-conflict-note", className), children: [
26
+ /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "sm", className: "crewlet-conflict-note__glyph" }),
27
+ /* @__PURE__ */ jsx("span", { className: "crewlet-conflict-note__text", children: sentence }),
28
+ (actions !== void 0 || onDismiss) && /* @__PURE__ */ jsxs("span", { className: "crewlet-conflict-note__actions", children: [
29
+ actions,
30
+ onDismiss && /* @__PURE__ */ jsx(Button, { size: "small", variant: "ghost", onClick: onDismiss, children: labels.dismiss })
31
+ ] })
32
+ ] });
33
+ }
34
+
35
+ export { CONFLICT_NOTE_LABELS, ConflictNote };
@@ -1,11 +1,11 @@
1
1
  import { Select } from './chunk-FC4YTTFJ.js';
2
2
  import { CopyButton } from './chunk-W46LPU4R.js';
3
- import { Modal } from './chunk-7IJARUIF.js';
3
+ import { Modal } from './chunk-F4DPAJCN.js';
4
4
  import { Callout } from './chunk-SVSCLHHI.js';
5
- import { Button } from './chunk-OG6CL3EB.js';
6
5
  import { PrincipalAvatar } from './chunk-4KP4RC3O.js';
7
6
  import { useHeadingLevel, headingTag } from './chunk-PQADV6P6.js';
8
7
  import { useMediaMatch } from './chunk-IG75YMUN.js';
8
+ import { Button } from './chunk-OG6CL3EB.js';
9
9
  import { IconButton } from './chunk-4FQT6ZAY.js';
10
10
  import { cx } from './chunk-ODEQTPFM.js';
11
11
  import './ShareDialog-YZ4F37MW.css';