@crewlethq/ui 0.15.1 → 0.17.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 (111) hide show
  1. package/README.md +36 -6
  2. package/dist/AppShell/AccountRow.d.ts +40 -0
  3. package/dist/AppShell/AppShell.d.ts +1 -0
  4. package/dist/AppShell/index.d.ts +2 -0
  5. package/dist/AppShell/index.js +1 -1
  6. package/dist/{AppShell-6QUD7NWM.css → AppShell-RUNVM4WH.css} +93 -0
  7. package/dist/AuthLayout/AuthLayout.d.ts +35 -0
  8. package/dist/AuthLayout/index.d.ts +3 -0
  9. package/dist/AuthLayout/index.js +8 -0
  10. package/dist/AuthLayout-ODHBUPHQ.css +54 -0
  11. package/dist/Avatar/Avatar.d.ts +37 -2
  12. package/dist/Avatar/index.d.ts +1 -1
  13. package/dist/Avatar/index.js +2 -2
  14. package/dist/{Avatar-I4NFP3WQ.css → Avatar-3C2H2WA4.css} +34 -0
  15. package/dist/AvatarPicker/index.js +4 -4
  16. package/dist/Callout/Callout.d.ts +7 -1
  17. package/dist/Callout/index.js +1 -1
  18. package/dist/{Callout-S7QDWHDL.css → Callout-SXXK4B5R.css} +3 -1
  19. package/dist/CharacterPicker/index.js +2 -2
  20. package/dist/Checkbox/CheckboxGroup.d.ts +30 -0
  21. package/dist/Checkbox/index.d.ts +2 -0
  22. package/dist/Checkbox/index.js +5 -1
  23. package/dist/{Checkbox-UE7TEPAM.css → Checkbox-KT7RS6QQ.css} +16 -0
  24. package/dist/CodeBlock/index.js +4 -3
  25. package/dist/Combobox/index.js +3 -3
  26. package/dist/CommandPalette/index.js +5 -5
  27. package/dist/Composer/index.js +3 -3
  28. package/dist/ConfirmField/ConfirmField.d.ts +43 -0
  29. package/dist/ConfirmField/index.d.ts +2 -0
  30. package/dist/ConfirmField/index.js +7 -0
  31. package/dist/Copyable/CopyButton.d.ts +13 -6
  32. package/dist/Copyable/index.js +3 -2
  33. package/dist/DataTable/index.js +7 -5
  34. package/dist/DataView/index.js +12 -11
  35. package/dist/DateTimePicker/index.js +2 -2
  36. package/dist/DownloadButton/DownloadButton.d.ts +52 -0
  37. package/dist/DownloadButton/index.d.ts +2 -0
  38. package/dist/DownloadButton/index.js +6 -0
  39. package/dist/EntityChip/index.js +3 -3
  40. package/dist/ImageUpload/index.js +3 -3
  41. package/dist/InlineCode/InlineCode.d.ts +8 -1
  42. package/dist/InlineCode/index.js +1 -1
  43. package/dist/{InlineCode-6WSR5COL.css → InlineCode-ASYHLN3E.css} +9 -0
  44. package/dist/Input/index.js +2 -2
  45. package/dist/{Input-RAKNQJCV.css → Input-3KVSNUO5.css} +11 -0
  46. package/dist/ListInput/index.js +3 -3
  47. package/dist/MarkdownEditor/index.js +1 -1
  48. package/dist/Modal/index.js +2 -2
  49. package/dist/ObjectHeader/ObjectHeader.d.ts +138 -0
  50. package/dist/ObjectHeader/index.d.ts +3 -0
  51. package/dist/ObjectHeader/index.js +4 -0
  52. package/dist/ObjectHeader-UNVYM2KD.css +174 -0
  53. package/dist/OrgLabel/index.js +3 -3
  54. package/dist/OrgNode/index.js +4 -4
  55. package/dist/OrgTable/index.js +4 -4
  56. package/dist/QrCode/QrCode.d.ts +58 -0
  57. package/dist/QrCode/index.d.ts +3 -0
  58. package/dist/QrCode/index.js +2 -0
  59. package/dist/QrCode-JWJTWMU5.css +19 -0
  60. package/dist/Switcher/index.js +4 -4
  61. package/dist/Text/Text.d.ts +7 -1
  62. package/dist/Text/index.js +1 -1
  63. package/dist/{Text-XIPYYZX2.css → Text-S3Q4O3IG.css} +11 -0
  64. package/dist/TimeWindowPicker/index.js +4 -4
  65. package/dist/{chunk-FTEJPHBI.js → chunk-4GB57WIT.js} +2 -2
  66. package/dist/{chunk-DDTDN5L2.js → chunk-4KWOKX2B.js} +1 -1
  67. package/dist/{chunk-NN62JQLR.js → chunk-5CLW2CXT.js} +3 -1
  68. package/dist/{chunk-HI6Q2UXQ.js → chunk-5OM447RW.js} +19 -7
  69. package/dist/{chunk-YXMCUKFI.js → chunk-5PSXBL73.js} +1 -1
  70. package/dist/{chunk-QSLIN64M.js → chunk-6NIMZ5XC.js} +1 -1
  71. package/dist/{chunk-YCTMKKRA.js → chunk-75CLJNDQ.js} +12 -2
  72. package/dist/chunk-7QFAORCY.js +37 -0
  73. package/dist/{chunk-22M7OXKW.js → chunk-AKEZ3G5V.js} +1 -1
  74. package/dist/chunk-BJCFNRGH.js +53 -0
  75. package/dist/chunk-CXBAIVYZ.js +39 -0
  76. package/dist/chunk-DTIW4CHZ.js +37 -0
  77. package/dist/chunk-F6NZOVWL.js +68 -0
  78. package/dist/{chunk-LCF4WPYS.js → chunk-FA2V2VT6.js} +2 -2
  79. package/dist/{chunk-KGTQ5CVD.js → chunk-GDDE4UM7.js} +1 -1
  80. package/dist/{chunk-EFNHOVC2.js → chunk-HENOTGUV.js} +2 -2
  81. package/dist/chunk-I7WXZOWA.js +90 -0
  82. package/dist/{chunk-23DHIMJA.js → chunk-IKBK4A5O.js} +1 -1
  83. package/dist/{chunk-DY77H372.js → chunk-JF7X673Z.js} +2 -2
  84. package/dist/{chunk-YYBD73OI.js → chunk-L4ZLXYPO.js} +1 -1
  85. package/dist/{chunk-H3XOMT7W.js → chunk-OYOQU7O5.js} +44 -3
  86. package/dist/{chunk-IYZZL3MZ.js → chunk-PQ63NLFC.js} +3 -3
  87. package/dist/{chunk-TK6YC5QT.js → chunk-RF5O4FYL.js} +4 -4
  88. package/dist/{chunk-5VNUYO76.js → chunk-STJ7QBC3.js} +8 -25
  89. package/dist/{chunk-R6FBDTP3.js → chunk-SVSCLHHI.js} +1 -1
  90. package/dist/{chunk-GARACZAH.js → chunk-T3ZWBZ65.js} +1 -1
  91. package/dist/{chunk-FY2R4U4Q.js → chunk-TBMDMXEX.js} +1 -1
  92. package/dist/{chunk-ZPWUVVIB.js → chunk-UAN5NE7M.js} +2 -2
  93. package/dist/{chunk-YACKC3K2.js → chunk-W46LPU4R.js} +8 -8
  94. package/dist/{chunk-4EOJR52M.js → chunk-XU4H6JQB.js} +4 -4
  95. package/dist/chunk-Y56FM3K4.js +57 -0
  96. package/dist/{chunk-DJMLHCLD.js → chunk-YDBC4DNW.js} +1 -1
  97. package/dist/{chunk-4Y7WKXAW.js → chunk-ZFH7H7GS.js} +1 -1
  98. package/dist/generated/index.d.ts +5 -0
  99. package/dist/generated/index.js +67 -60
  100. package/dist/index.js +67 -60
  101. package/dist/styles.css +440 -8
  102. package/dist/utils/index.d.ts +4 -0
  103. package/dist/utils/index.js +4 -2
  104. package/dist/utils/useClipboard.d.ts +6 -3
  105. package/dist/utils/useDownload.d.ts +63 -0
  106. package/dist/utils/useOutcome.d.ts +44 -0
  107. package/package.json +4 -3
  108. package/dist/{chunk-GUHP5JHC.js → chunk-2UU5IGZI.js} +1 -1
  109. package/dist/{chunk-6AR2T5Y4.js → chunk-CRVETI5L.js} +0 -0
  110. package/dist/{chunk-K4VORB64.js → chunk-DEEVA6TV.js} +1 -1
  111. /package/dist/{chunk-AER7UDD4.js → chunk-U5LQ2AS5.js} +0 -0
@@ -0,0 +1,39 @@
1
+ import { InlineCode } from './chunk-5CLW2CXT.js';
2
+ import { FormField, Input } from './chunk-4GB57WIT.js';
3
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+
5
+ function confirmMatches(typed, phrase) {
6
+ return phrase !== "" && typed.trim() === phrase;
7
+ }
8
+ var defaultLabel = (phrase) => /* @__PURE__ */ jsxs(Fragment, { children: [
9
+ "Type ",
10
+ phrase,
11
+ " to confirm"
12
+ ] });
13
+ function ConfirmField({
14
+ phrase,
15
+ value,
16
+ onValueChange,
17
+ label = defaultLabel,
18
+ helper,
19
+ autoFocus,
20
+ className
21
+ }) {
22
+ return /* @__PURE__ */ jsx(FormField, { label: label(/* @__PURE__ */ jsx(InlineCode, { children: phrase })), helper, className, children: (field) => /* @__PURE__ */ jsx(
23
+ Input,
24
+ {
25
+ id: field.id,
26
+ "aria-describedby": field.describedBy,
27
+ autoFocus,
28
+ width: "full",
29
+ spellCheck: false,
30
+ autoComplete: "off",
31
+ autoCapitalize: "none",
32
+ autoCorrect: "off",
33
+ value,
34
+ onChange: (event) => onValueChange(event.target.value)
35
+ }
36
+ ) });
37
+ }
38
+
39
+ export { ConfirmField, confirmMatches };
@@ -0,0 +1,37 @@
1
+ import { useState, useRef, useEffect, useCallback } from 'react';
2
+
3
+ // src/utils/useOutcome.ts
4
+ function useOutcome({ resetMs = 2e3, failedResetMs } = {}) {
5
+ const [{ state, attempt }, setOutcome] = useState({
6
+ state: "idle",
7
+ attempt: 0
8
+ });
9
+ const timer = useRef(void 0);
10
+ const live = useRef(true);
11
+ useEffect(() => {
12
+ live.current = true;
13
+ return () => {
14
+ live.current = false;
15
+ clearTimeout(timer.current);
16
+ timer.current = void 0;
17
+ };
18
+ }, []);
19
+ const run = useCallback(
20
+ async (task) => {
21
+ const ok = await task();
22
+ if (!live.current) return ok;
23
+ setOutcome((prior) => ({ state: ok ? "done" : "failed", attempt: prior.attempt + 1 }));
24
+ clearTimeout(timer.current);
25
+ timer.current = void 0;
26
+ const after = ok ? resetMs : failedResetMs === void 0 ? resetMs : failedResetMs;
27
+ if (after !== null) {
28
+ timer.current = setTimeout(() => setOutcome((prior) => ({ ...prior, state: "idle" })), after);
29
+ }
30
+ return ok;
31
+ },
32
+ [resetMs, failedResetMs]
33
+ );
34
+ return { state, attempt, run };
35
+ }
36
+
37
+ export { useOutcome };
@@ -0,0 +1,68 @@
1
+ import { useOutcome } from './chunk-DTIW4CHZ.js';
2
+ import { useCallback } from 'react';
3
+
4
+ var DEFAULT_MIME = "text/plain;charset=utf-8";
5
+ function useDownload(options = {}) {
6
+ const outcome = useOutcome(options);
7
+ const { run } = outcome;
8
+ const download = useCallback(
9
+ (text, filename, mime = DEFAULT_MIME) => run(() => downloadText(typeof text === "function" ? text() : text, filename, mime).started),
10
+ [run]
11
+ );
12
+ const state = outcome.state === "done" ? "started" : outcome.state;
13
+ return { state, attempt: outcome.attempt, download };
14
+ }
15
+ function downloadText(text, filename, mime = DEFAULT_MIME) {
16
+ const name = safeFilename(filename);
17
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function" || typeof HTMLAnchorElement === "undefined" || !("download" in HTMLAnchorElement.prototype)) {
18
+ return { started: false, name };
19
+ }
20
+ let url = "";
21
+ try {
22
+ url = URL.createObjectURL(new Blob([text], { type: mime }));
23
+ const link = document.createElement("a");
24
+ link.href = url;
25
+ link.download = name;
26
+ document.body.appendChild(link);
27
+ try {
28
+ link.click();
29
+ } finally {
30
+ link.remove();
31
+ }
32
+ return { started: true, name };
33
+ } catch {
34
+ return { started: false, name };
35
+ } finally {
36
+ if (url) setTimeout(() => URL.revokeObjectURL(url), 0);
37
+ }
38
+ }
39
+ var MAX_EXTENSION = 12;
40
+ var MAX_FILENAME = 120;
41
+ var encoder = new TextEncoder();
42
+ function bytes(value) {
43
+ return encoder.encode(value).length;
44
+ }
45
+ function cutToBytes(value, max) {
46
+ if (max <= 0) return "";
47
+ if (bytes(value) <= max) return value;
48
+ let out = "";
49
+ let used = 0;
50
+ for (const character of value) {
51
+ const size = bytes(character);
52
+ if (used + size > max) break;
53
+ out += character;
54
+ used += size;
55
+ }
56
+ return out;
57
+ }
58
+ function safeFilename(name) {
59
+ const cleaned = name.replace(/[^\p{L}\p{M}\p{N}_.-]+/gu, "-").replace(/-{2,}/g, "-");
60
+ const dot = cleaned.lastIndexOf(".");
61
+ const tail = cleaned.length - dot;
62
+ const extension = dot >= 0 && tail >= 2 && tail <= MAX_EXTENSION ? cleaned.slice(dot) : "";
63
+ const stem = (extension ? cleaned.slice(0, dot) : cleaned).replace(/^[-.]+/, "");
64
+ if (!stem) return `download${extension}`;
65
+ return cutToBytes(stem, MAX_FILENAME - bytes(extension)) + extension;
66
+ }
67
+
68
+ export { downloadText, safeFilename, useDownload };
@@ -1,7 +1,7 @@
1
1
  import { Skeleton } from './chunk-W5J7R5EB.js';
2
- import { Modal } from './chunk-K4VORB64.js';
2
+ import { Modal } from './chunk-DEEVA6TV.js';
3
3
  import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
4
- import { Input } from './chunk-FTEJPHBI.js';
4
+ import { Input } from './chunk-4GB57WIT.js';
5
5
  import { KeyChip, highlightMatch } from './chunk-FYTIJM2T.js';
6
6
  import { announce } from './chunk-RSAQOKH3.js';
7
7
  import { useMediaMatch } from './chunk-IG75YMUN.js';
@@ -1,4 +1,4 @@
1
- import { CopyButton } from './chunk-YACKC3K2.js';
1
+ import { CopyButton } from './chunk-W46LPU4R.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
3
  import './CodeBlock-GYNJ2HI6.css';
4
4
  import { forwardRef, useRef, useState, useLayoutEffect } from 'react';
@@ -1,8 +1,8 @@
1
1
  import { HuePicker } from './chunk-NXDEPUVG.js';
2
2
  import { NODE_HUE_NAMES } from './chunk-AYKXMIOR.js';
3
3
  import { Popover } from './chunk-BWRGFKJP.js';
4
- import { CharacterPicker } from './chunk-YXMCUKFI.js';
5
- import { Avatar } from './chunk-HI6Q2UXQ.js';
4
+ import { CharacterPicker } from './chunk-5PSXBL73.js';
5
+ import { Avatar } from './chunk-5OM447RW.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
7
7
  import './AvatarPicker-PREY36MG.css';
8
8
  import { CREWLET_CHARACTER_GEOMETRY, CREWLET_CHARACTERS } from '@crewlethq/icons/characters';
@@ -0,0 +1,90 @@
1
+ import { Link } from './chunk-FBSEJO2C.js';
2
+ import { cx } from './chunk-ODEQTPFM.js';
3
+ import './ObjectHeader-UNVYM2KD.css';
4
+ import { ArrowUpRightGlyph } from '@crewlethq/icons/glyphs';
5
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
6
+
7
+ function listOf(names) {
8
+ if (names.length <= 1) return names[0] ?? "";
9
+ return `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
10
+ }
11
+ function SetByLine({ setBy, fields, turnLabel = "turn", className }) {
12
+ return /* @__PURE__ */ jsxs("span", { className: cx("crewlet-set-by", className), children: [
13
+ fields && fields.length > 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
14
+ listOf(fields),
15
+ " \xB7 "
16
+ ] }) : null,
17
+ setBy.cleared ? "cleared by " : "set by ",
18
+ setBy.actor,
19
+ setBy.ago ? ` \xB7 ${setBy.ago}` : "",
20
+ setBy.turnHref ? /* @__PURE__ */ jsxs(Fragment, { children: [
21
+ " \xB7 ",
22
+ /* @__PURE__ */ jsxs(Link, { className: "crewlet-set-by__turn", href: setBy.turnHref, size: "inherit", children: [
23
+ turnLabel,
24
+ " ",
25
+ /* @__PURE__ */ jsx(ArrowUpRightGlyph, { size: "xs", "aria-hidden": "true" })
26
+ ] })
27
+ ] }) : null
28
+ ] });
29
+ }
30
+ function FactLine({ facts, className }) {
31
+ const shown = facts.filter((fact) => fact.value !== null && fact.value !== void 0 && fact.value !== "");
32
+ if (shown.length === 0) return null;
33
+ return /* @__PURE__ */ jsx("div", { className: cx("crewlet-fact-line", className), children: shown.map((fact) => /* @__PURE__ */ jsxs(
34
+ "span",
35
+ {
36
+ className: cx(
37
+ "crewlet-fact-line__fact",
38
+ fact.token && "crewlet-fact-line__fact--token",
39
+ fact.set && "crewlet-fact-line__fact--set"
40
+ ),
41
+ children: [
42
+ /* @__PURE__ */ jsx("span", { className: "crewlet-fact-line__label", children: fact.label }),
43
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-fact-line__body", children: [
44
+ /* @__PURE__ */ jsx(
45
+ "span",
46
+ {
47
+ className: cx(
48
+ "crewlet-fact-line__value",
49
+ fact.token ? "crewlet-fact-line__value--token" : !fact.whole && !fact.set && "crewlet-fact-line__value--clamp"
50
+ ),
51
+ children: fact.href ? /* @__PURE__ */ jsx(Link, { href: fact.href, size: "inherit", children: fact.value }) : fact.value
52
+ }
53
+ ),
54
+ fact.note ? /* @__PURE__ */ jsx("span", { className: "crewlet-fact-line__note", children: fact.note }) : null,
55
+ fact.setBy ? /* @__PURE__ */ jsx(SetByLine, { setBy: fact.setBy, className: "crewlet-fact-line__set-by" }) : null
56
+ ] })
57
+ ]
58
+ },
59
+ fact.label
60
+ )) });
61
+ }
62
+ function ObjectHeader({
63
+ kind,
64
+ icon,
65
+ identifier,
66
+ within,
67
+ title,
68
+ status,
69
+ facts,
70
+ actions,
71
+ size = "page",
72
+ className
73
+ }) {
74
+ return /* @__PURE__ */ jsxs("header", { className: cx("crewlet-object-header", `crewlet-object-header--${size}`, className), children: [
75
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-object-header__eyebrow", children: [
76
+ icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-object-header__icon", "aria-hidden": "true", children: icon }) : null,
77
+ /* @__PURE__ */ jsx("span", { children: kind }),
78
+ identifier ? /* @__PURE__ */ jsx("span", { className: "crewlet-object-header__id crewlet-object-header__id--mono", children: identifier }) : null,
79
+ within ? /* @__PURE__ */ jsx("span", { className: "crewlet-object-header__id", children: within }) : null
80
+ ] }),
81
+ /* @__PURE__ */ jsxs("div", { className: "crewlet-object-header__head", children: [
82
+ /* @__PURE__ */ jsx("h2", { className: "crewlet-object-header__title", children: title }),
83
+ status ? /* @__PURE__ */ jsx("div", { className: "crewlet-object-header__status", children: status }) : null,
84
+ actions ? /* @__PURE__ */ jsx("div", { className: "crewlet-object-header__actions", children: actions }) : null
85
+ ] }),
86
+ facts ? /* @__PURE__ */ jsx(FactLine, { facts }) : null
87
+ ] });
88
+ }
89
+
90
+ export { FactLine, ObjectHeader, SetByLine };
@@ -1,5 +1,5 @@
1
1
  import { cx } from './chunk-ODEQTPFM.js';
2
- import './Text-XIPYYZX2.css';
2
+ import './Text-S3Q4O3IG.css';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
5
5
  function Text({
@@ -1,7 +1,7 @@
1
1
  import { Select } from './chunk-FC4YTTFJ.js';
2
- import { Input } from './chunk-FTEJPHBI.js';
3
- import { Button } from './chunk-OG6CL3EB.js';
2
+ import { Input } from './chunk-4GB57WIT.js';
4
3
  import { today, shiftMonth, CalendarGrid, compareDays } from './chunk-GXQ3B46I.js';
4
+ import { Button } from './chunk-OG6CL3EB.js';
5
5
  import { IconButton } from './chunk-4FQT6ZAY.js';
6
6
  import { Popover } from './chunk-BWRGFKJP.js';
7
7
  import { isComposing } from './chunk-R25RGPUB.js';
@@ -1,6 +1,6 @@
1
1
  import { TreeGrid } from './chunk-B2Z2CDM5.js';
2
2
  import { createTreeModel, treeParent } from './chunk-MY374UUD.js';
3
- import { OrgLabel } from './chunk-22M7OXKW.js';
3
+ import { OrgLabel } from './chunk-AKEZ3G5V.js';
4
4
  import { AddPill } from './chunk-TROLVEZY.js';
5
5
  import { IconButton } from './chunk-4FQT6ZAY.js';
6
6
  import { cx } from './chunk-ODEQTPFM.js';
@@ -3,12 +3,52 @@ import { IconButton } from './chunk-4FQT6ZAY.js';
3
3
  import { useModalLayer, focusables } from './chunk-R25RGPUB.js';
4
4
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
5
5
  import { cx } from './chunk-ODEQTPFM.js';
6
- import './AppShell-6QUD7NWM.css';
6
+ import './AppShell-RUNVM4WH.css';
7
7
  import { createContext, forwardRef, useContext, useId, useState, useRef, useCallback, useEffect, useLayoutEffect } from 'react';
8
8
  import { breakpoint } from '@crewlethq/tokens';
9
- import { ChevronUpGlyph, MenuGlyph } from '@crewlethq/icons/glyphs';
9
+ import { ChevronUpGlyph, ChevronRightGlyph, MenuGlyph } from '@crewlethq/icons/glyphs';
10
10
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
11
11
 
12
+ function AccountRow({
13
+ avatar,
14
+ name,
15
+ detail,
16
+ href,
17
+ current = false,
18
+ title,
19
+ linkSuffix = ", your account",
20
+ actions,
21
+ className
22
+ }) {
23
+ const who = /* @__PURE__ */ jsxs(Fragment, { children: [
24
+ /* @__PURE__ */ jsx("span", { className: "crewlet-account-row__avatar", children: avatar }),
25
+ /* @__PURE__ */ jsxs("span", { className: "crewlet-account-row__text", children: [
26
+ /* @__PURE__ */ jsx("span", { className: "crewlet-account-row__name", children: name }),
27
+ detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
28
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: "," }),
29
+ " ",
30
+ /* @__PURE__ */ jsx("span", { className: "crewlet-account-row__detail", children: detail })
31
+ ] }) : null
32
+ ] })
33
+ ] });
34
+ 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(
36
+ "a",
37
+ {
38
+ className: "crewlet-account-row__who",
39
+ href,
40
+ title,
41
+ "aria-current": current ? "page" : void 0,
42
+ children: [
43
+ who,
44
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: linkSuffix }),
45
+ /* @__PURE__ */ jsx(ChevronRightGlyph, { size: "xs", className: "crewlet-account-row__go", "aria-hidden": "true" })
46
+ ]
47
+ }
48
+ ),
49
+ actions ? /* @__PURE__ */ jsx("div", { className: "crewlet-account-row__actions", children: actions }) : null
50
+ ] });
51
+ }
12
52
  var NARROW = `(width < ${breakpoint.shell})`;
13
53
  var AppShellContext = createContext(null);
14
54
  function useAppShell() {
@@ -259,5 +299,6 @@ AppShell.Rail = AppShellRail;
259
299
  AppShell.RailRow = AppShellRailRow;
260
300
  AppShell.Topbar = AppShellTopbar;
261
301
  AppShell.Toggle = AppShellToggle;
302
+ AppShell.AccountRow = AccountRow;
262
303
 
263
- export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell };
304
+ export { AccountRow, AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell };
@@ -1,9 +1,9 @@
1
- import { Modal } from './chunk-K4VORB64.js';
1
+ import { Modal } from './chunk-DEEVA6TV.js';
2
2
  import { useListbox, useHighlightRest } from './chunk-F73MC7EZ.js';
3
3
  import { Kbd } from './chunk-SMBKXWML.js';
4
- import { Input } from './chunk-FTEJPHBI.js';
5
- import { Count } from './chunk-BHHIIK3M.js';
4
+ import { Input } from './chunk-4GB57WIT.js';
6
5
  import { isComposing } from './chunk-R25RGPUB.js';
6
+ import { Count } from './chunk-BHHIIK3M.js';
7
7
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
8
8
  import { cx } from './chunk-ODEQTPFM.js';
9
9
  import './CommandPalette-HBTNKHZQ.css';
@@ -1,11 +1,11 @@
1
1
  import { Menu } from './chunk-NS7BVLL3.js';
2
2
  import { EmptyValue } from './chunk-EUCC37WY.js';
3
- import { Checkbox } from './chunk-YCTMKKRA.js';
4
- import { useClipboard } from './chunk-5VNUYO76.js';
5
- import { Modal } from './chunk-K4VORB64.js';
3
+ import { Checkbox } from './chunk-75CLJNDQ.js';
4
+ import { useClipboard } from './chunk-STJ7QBC3.js';
5
+ import { Modal } from './chunk-DEEVA6TV.js';
6
6
  import { Button } from './chunk-OG6CL3EB.js';
7
- import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
8
7
  import { IconButton } from './chunk-4FQT6ZAY.js';
8
+ import { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
9
9
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
10
10
  import { cx } from './chunk-ODEQTPFM.js';
11
11
  import './DataTable-WZK3V62B.css';
@@ -1,32 +1,15 @@
1
- import { useState, useRef, useEffect, useCallback } from 'react';
1
+ import { useOutcome } from './chunk-DTIW4CHZ.js';
2
+ import { useCallback } from 'react';
2
3
 
3
- // src/utils/useClipboard.ts
4
4
  function useClipboard({ resetMs = 2e3, failedResetMs } = {}) {
5
- const [state, setState] = useState("idle");
6
- const timer = useRef(void 0);
7
- const live = useRef(true);
8
- useEffect(() => {
9
- live.current = true;
10
- return () => {
11
- live.current = false;
12
- clearTimeout(timer.current);
13
- timer.current = void 0;
14
- };
15
- }, []);
5
+ const outcome = useOutcome({ resetMs, failedResetMs });
6
+ const { run } = outcome;
16
7
  const copy = useCallback(
17
- async (text) => {
18
- const ok = await writeClipboard(typeof text === "function" ? text() : text);
19
- if (!live.current) return ok;
20
- setState(ok ? "copied" : "failed");
21
- clearTimeout(timer.current);
22
- timer.current = void 0;
23
- const after = ok ? resetMs : failedResetMs === void 0 ? resetMs : failedResetMs;
24
- if (after !== null) timer.current = setTimeout(() => setState("idle"), after);
25
- return ok;
26
- },
27
- [resetMs, failedResetMs]
8
+ (text) => run(() => writeClipboard(typeof text === "function" ? text() : text)),
9
+ [run]
28
10
  );
29
- return { state, copy };
11
+ const state = outcome.state === "done" ? "copied" : outcome.state;
12
+ return { state, attempt: outcome.attempt, copy };
30
13
  }
31
14
  async function writeClipboard(text) {
32
15
  try {
@@ -1,5 +1,5 @@
1
1
  import { cx } from './chunk-ODEQTPFM.js';
2
- import './Callout-S7QDWHDL.css';
2
+ import './Callout-SXXK4B5R.css';
3
3
  import { CircleAlertGlyph, TriangleAlertGlyph, CircleCheckGlyph, InfoGlyph } from '@crewlethq/icons/glyphs';
4
4
  import { jsxs, jsx } from 'react/jsx-runtime';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { Kbd } from './chunk-SMBKXWML.js';
2
- import { FormField, Textarea, Input } from './chunk-FTEJPHBI.js';
2
+ import { FormField, Textarea, Input } from './chunk-4GB57WIT.js';
3
3
  import { announce } from './chunk-RSAQOKH3.js';
4
4
  import { IconButton } from './chunk-4FQT6ZAY.js';
5
5
  import { isComposing } from './chunk-R25RGPUB.js';
@@ -1,4 +1,4 @@
1
- import { OrgLabel } from './chunk-22M7OXKW.js';
1
+ import { OrgLabel } from './chunk-AKEZ3G5V.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
3
  import './OrgNode-TCHJH5FO.css';
4
4
  import { jsx, jsxs } from 'react/jsx-runtime';
@@ -1,6 +1,6 @@
1
- import { AVATAR_SIZES, Avatar } from './chunk-HI6Q2UXQ.js';
1
+ import { AVATAR_SIZES, Avatar } from './chunk-5OM447RW.js';
2
2
  import { cx } from './chunk-ODEQTPFM.js';
3
- import './Avatar-I4NFP3WQ.css';
3
+ import './Avatar-3C2H2WA4.css';
4
4
  import './AvatarStack-GEJBNV7E.css';
5
5
  import { jsxs, jsx } from 'react/jsx-runtime';
6
6
 
@@ -1,4 +1,4 @@
1
- import { useClipboard } from './chunk-5VNUYO76.js';
1
+ import { useClipboard } from './chunk-STJ7QBC3.js';
2
2
  import { Button } from './chunk-OG6CL3EB.js';
3
3
  import { IconButton } from './chunk-4FQT6ZAY.js';
4
4
  import { VisuallyHidden } from './chunk-BZFWDIV6.js';
@@ -13,8 +13,8 @@ function copyGlyph(state) {
13
13
  if (state === "failed") return /* @__PURE__ */ jsx(CircleAlertGlyph, { size: "sm" });
14
14
  return /* @__PURE__ */ jsx(CopyGlyph, { size: "sm" });
15
15
  }
16
- function CopyStatus({ state, copiedMessage, failedMessage }) {
17
- return /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("span", { role: "status", children: state === "copied" ? copiedMessage : state === "failed" ? failedMessage : "" }) });
16
+ function CopyStatus({ state, copiedMessage, failedMessage, attempt = 0 }) {
17
+ return /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx("span", { role: "status", children: /* @__PURE__ */ jsx("span", { children: state === "copied" ? copiedMessage : state === "failed" ? failedMessage : "" }, attempt) }) });
18
18
  }
19
19
  function CopyButton({
20
20
  text,
@@ -27,10 +27,10 @@ function CopyButton({
27
27
  variant = "ghost",
28
28
  title,
29
29
  resetMs,
30
- failedResetMs,
30
+ failedResetMs = null,
31
31
  className
32
32
  }) {
33
- const { state, copy } = useClipboard({ resetMs, failedResetMs });
33
+ const { state, attempt, copy } = useClipboard({ resetMs, failedResetMs });
34
34
  const onClick = useCallback(() => {
35
35
  void copy(text);
36
36
  }, [copy, text]);
@@ -46,7 +46,7 @@ function CopyButton({
46
46
  children: state === "copied" ? copiedLabel : state === "failed" ? failedLabel : label
47
47
  }
48
48
  ),
49
- /* @__PURE__ */ jsx(CopyStatus, { state, copiedMessage, failedMessage })
49
+ /* @__PURE__ */ jsx(CopyStatus, { state, attempt, copiedMessage, failedMessage })
50
50
  ] });
51
51
  }
52
52
  var truncateValue = (text, length, variant) => {
@@ -74,7 +74,7 @@ var Copyable = ({
74
74
  className
75
75
  }) => {
76
76
  const text = value === void 0 || value === null ? "" : String(value);
77
- const { state, copy } = useClipboard({ resetMs, failedResetMs });
77
+ const { state, attempt, copy } = useClipboard({ resetMs, failedResetMs });
78
78
  const onCopy = useCallback(() => {
79
79
  void copy(text);
80
80
  }, [copy, text]);
@@ -107,7 +107,7 @@ var Copyable = ({
107
107
  onClick: onCopy
108
108
  }
109
109
  ),
110
- /* @__PURE__ */ jsx(CopyStatus, { state, copiedMessage, failedMessage })
110
+ /* @__PURE__ */ jsx(CopyStatus, { state, attempt, copiedMessage, failedMessage })
111
111
  ]
112
112
  }
113
113
  );
@@ -1,12 +1,12 @@
1
- import { PageHeader } from './chunk-HOXGBLEY.js';
2
1
  import { RelativeTime } from './chunk-3CZ7XAVY.js';
2
+ import { PageHeader } from './chunk-HOXGBLEY.js';
3
3
  import { Toolbar } from './chunk-PGRPNDWM.js';
4
- import { DataTable } from './chunk-TK6YC5QT.js';
4
+ import { DataTable } from './chunk-RF5O4FYL.js';
5
5
  import { Menu } from './chunk-NS7BVLL3.js';
6
- import { DateTimePicker } from './chunk-GUHP5JHC.js';
6
+ import { DateTimePicker } from './chunk-2UU5IGZI.js';
7
7
  import { useListbox, useOptionKeys } from './chunk-F73MC7EZ.js';
8
8
  import { Kbd } from './chunk-SMBKXWML.js';
9
- import { Input } from './chunk-FTEJPHBI.js';
9
+ import { Input } from './chunk-4GB57WIT.js';
10
10
  import { Button } from './chunk-OG6CL3EB.js';
11
11
  import { IconButton } from './chunk-4FQT6ZAY.js';
12
12
  import { Popover } from './chunk-BWRGFKJP.js';
@@ -0,0 +1,57 @@
1
+ import { cx } from './chunk-ODEQTPFM.js';
2
+ import './QrCode-JWJTWMU5.css';
3
+ import { useMemo } from 'react';
4
+ import { color } from '@crewlethq/tokens';
5
+ import { encode } from 'uqr';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var QR_QUIET_ZONE = 4;
9
+ var QR_DARK = color.qr.dark;
10
+ var QR_LIGHT = color.qr.light;
11
+ function qrModules(value) {
12
+ try {
13
+ return encode(value, { ecc: "M", border: 0 }).data;
14
+ } catch (error) {
15
+ if (error instanceof RangeError) return null;
16
+ throw error;
17
+ }
18
+ }
19
+ function qrModulePath(modules) {
20
+ let d = "";
21
+ modules.forEach((row, y) => {
22
+ let x = 0;
23
+ while (x < row.length) {
24
+ if (!row[x]) {
25
+ x += 1;
26
+ continue;
27
+ }
28
+ let end = x;
29
+ while (end < row.length && row[end]) end += 1;
30
+ d += `M${x} ${y}h${end - x}v1h-${end - x}z`;
31
+ x = end;
32
+ }
33
+ });
34
+ return d;
35
+ }
36
+ function QrCode({ value, label, className }) {
37
+ const modules = useMemo(() => qrModules(value), [value]);
38
+ if (!modules) return null;
39
+ const span = modules.length + 2 * QR_QUIET_ZONE;
40
+ return /* @__PURE__ */ jsxs(
41
+ "svg",
42
+ {
43
+ className: cx("crewlet-qr-code", className),
44
+ viewBox: `${-QR_QUIET_ZONE} ${-QR_QUIET_ZONE} ${span} ${span}`,
45
+ role: "img",
46
+ "aria-label": label,
47
+ shapeRendering: "crispEdges",
48
+ focusable: "false",
49
+ children: [
50
+ /* @__PURE__ */ jsx("rect", { x: -QR_QUIET_ZONE, y: -QR_QUIET_ZONE, width: span, height: span, fill: QR_LIGHT }),
51
+ /* @__PURE__ */ jsx("path", { d: qrModulePath(modules), fill: QR_DARK })
52
+ ]
53
+ }
54
+ );
55
+ }
56
+
57
+ export { QR_DARK, QR_LIGHT, QR_QUIET_ZONE, QrCode, qrModulePath, qrModules };
@@ -1,4 +1,4 @@
1
- import { avatarCorner, Avatar } from './chunk-HI6Q2UXQ.js';
1
+ import { avatarCorner, Avatar } from './chunk-5OM447RW.js';
2
2
  import './ImageUpload-NVGMN7DP.css';
3
3
  import { useRef } from 'react';
4
4
  import { CameraGlyph, XGlyph } from '@crewlethq/icons/glyphs';
@@ -1,5 +1,5 @@
1
1
  import { useListbox } from './chunk-F73MC7EZ.js';
2
- import { Input } from './chunk-FTEJPHBI.js';
2
+ import { Input } from './chunk-4GB57WIT.js';
3
3
  import { cx } from './chunk-ODEQTPFM.js';
4
4
  import './Combobox-4FUVYIE7.css';
5
5
  import { forwardRef, useId, useMemo, useRef, useCallback } from 'react';
@@ -2,6 +2,7 @@ export * from '../Accordion/index.js';
2
2
  export * from '../AddPill/index.js';
3
3
  export * from '../Announcer/index.js';
4
4
  export * from '../AppShell/index.js';
5
+ export * from '../AuthLayout/index.js';
5
6
  export * from '../Avatar/index.js';
6
7
  export * from '../AvatarPicker/index.js';
7
8
  export * from '../BrandLockup/index.js';
@@ -20,6 +21,7 @@ export * from '../CodeBlock/index.js';
20
21
  export * from '../Combobox/index.js';
21
22
  export * from '../CommandPalette/index.js';
22
23
  export * from '../Composer/index.js';
24
+ export * from '../ConfirmField/index.js';
23
25
  export * from '../Container/index.js';
24
26
  export * from '../Copyable/index.js';
25
27
  export * from '../Count/index.js';
@@ -30,6 +32,7 @@ export * from '../DescriptionList/index.js';
30
32
  export * from '../DiffList/index.js';
31
33
  export * from '../Disclosure/index.js';
32
34
  export * from '../Document/index.js';
35
+ export * from '../DownloadButton/index.js';
33
36
  export * from '../EmptyState/index.js';
34
37
  export * from '../EmptyValue/index.js';
35
38
  export * from '../EntityChip/index.js';
@@ -58,6 +61,7 @@ export * from '../Menu/index.js';
58
61
  export * from '../Meter/index.js';
59
62
  export * from '../Modal/index.js';
60
63
  export * from '../NewItemsNotice/index.js';
64
+ export * from '../ObjectHeader/index.js';
61
65
  export * from '../OrgLabel/index.js';
62
66
  export * from '../OrgNode/index.js';
63
67
  export * from '../OrgTable/index.js';
@@ -66,6 +70,7 @@ export * from '../PageHeader/index.js';
66
70
  export * from '../Popover/index.js';
67
71
  export * from '../PricingCard/index.js';
68
72
  export * from '../Prose/index.js';
73
+ export * from '../QrCode/index.js';
69
74
  export * from '../ReadingPane/index.js';
70
75
  export * from '../RelativeTime/index.js';
71
76
  export * from '../SearchTrigger/index.js';