@efolusi/meridian 1.5.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 (231) hide show
  1. package/README.md +12 -0
  2. package/ai/AgentRun.d.ts +20 -0
  3. package/ai/AgentRun.js +100 -0
  4. package/ai/ChatMessage.d.ts +19 -0
  5. package/ai/ChatMessage.js +81 -0
  6. package/ai/Citation.d.ts +23 -0
  7. package/ai/Citation.js +146 -0
  8. package/ai/Confirmation.d.ts +26 -0
  9. package/ai/Confirmation.js +84 -0
  10. package/ai/Conversation.d.ts +15 -0
  11. package/ai/Conversation.js +81 -0
  12. package/ai/DocumentCard.d.ts +17 -0
  13. package/ai/DocumentCard.js +101 -0
  14. package/ai/FeedbackBar.d.ts +14 -0
  15. package/ai/FeedbackBar.js +84 -0
  16. package/ai/GeneratedImage.d.ts +22 -0
  17. package/ai/GeneratedImage.js +100 -0
  18. package/ai/ModelSelector.d.ts +21 -0
  19. package/ai/ModelSelector.js +112 -0
  20. package/ai/Player.d.ts +18 -0
  21. package/ai/Player.js +201 -0
  22. package/ai/PromptComposer.d.ts +28 -0
  23. package/ai/PromptComposer.js +111 -0
  24. package/ai/PromptSteps.d.ts +18 -0
  25. package/ai/PromptSteps.js +167 -0
  26. package/ai/Reasoning.d.ts +17 -0
  27. package/ai/Reasoning.js +57 -0
  28. package/ai/RichComposer.d.ts +25 -0
  29. package/ai/RichComposer.js +247 -0
  30. package/ai/Sandbox.d.ts +20 -0
  31. package/ai/Sandbox.js +96 -0
  32. package/ai/SelectionQuote.d.ts +15 -0
  33. package/ai/SelectionQuote.js +123 -0
  34. package/ai/SourceCard.d.ts +17 -0
  35. package/ai/SourceCard.js +57 -0
  36. package/ai/Suggestions.d.ts +10 -0
  37. package/ai/Suggestions.js +38 -0
  38. package/ai/Task.d.ts +15 -0
  39. package/ai/Task.js +49 -0
  40. package/ai/Todo.d.ts +13 -0
  41. package/ai/Todo.js +87 -0
  42. package/ai/ToolCall.d.ts +30 -0
  43. package/ai/ToolCall.js +147 -0
  44. package/ai/Transcript.d.ts +27 -0
  45. package/ai/Transcript.js +96 -0
  46. package/ai/UsageMeter.d.ts +21 -0
  47. package/ai/UsageMeter.js +81 -0
  48. package/ai/WebPreview.d.ts +19 -0
  49. package/ai/WebPreview.js +179 -0
  50. package/assets/fonts/BricolageGrotesque-Variable.ttf +0 -0
  51. package/assets/fonts/Figtree-Italic-Variable.ttf +0 -0
  52. package/assets/fonts/Figtree-Variable.ttf +0 -0
  53. package/assets/fonts/JetBrainsMono-Italic-Variable.ttf +0 -0
  54. package/assets/fonts/JetBrainsMono-Variable.ttf +0 -0
  55. package/assets/fonts/OFL-BricolageGrotesque.txt +93 -0
  56. package/assets/fonts/OFL-Figtree.txt +93 -0
  57. package/assets/fonts/OFL-JetBrainsMono.txt +93 -0
  58. package/code/CodeBlock.d.ts +17 -0
  59. package/code/CodeBlock.js +99 -0
  60. package/code/Console.d.ts +19 -0
  61. package/code/Console.js +124 -0
  62. package/code/CopyField.d.ts +10 -0
  63. package/code/CopyField.js +54 -0
  64. package/code/Diff.d.ts +26 -0
  65. package/code/Diff.js +358 -0
  66. package/code/EnvList.d.ts +14 -0
  67. package/code/EnvList.js +89 -0
  68. package/code/Exception.d.ts +25 -0
  69. package/code/Exception.js +90 -0
  70. package/code/Terminal.d.ts +20 -0
  71. package/code/Terminal.js +58 -0
  72. package/data/BarChart.d.ts +14 -0
  73. package/data/BarChart.js +44 -0
  74. package/data/DonutChart.d.ts +17 -0
  75. package/data/DonutChart.js +114 -0
  76. package/data/EmptyState.d.ts +13 -0
  77. package/data/EmptyState.js +39 -0
  78. package/data/KeyValueList.d.ts +14 -0
  79. package/data/KeyValueList.js +34 -0
  80. package/data/LineChart.d.ts +15 -0
  81. package/data/LineChart.js +85 -0
  82. package/data/Skeleton.d.ts +11 -0
  83. package/data/Skeleton.js +49 -0
  84. package/data/Sparkline.d.ts +14 -0
  85. package/data/Sparkline.js +42 -0
  86. package/data/Stat.d.ts +14 -0
  87. package/data/Stat.js +40 -0
  88. package/data/StatusDot.d.ts +10 -0
  89. package/data/StatusDot.js +34 -0
  90. package/data/Table.d.ts +53 -0
  91. package/data/Table.js +174 -0
  92. package/dates/Calendar.d.ts +8 -0
  93. package/dates/Calendar.js +143 -0
  94. package/dates/DatePicker.d.ts +12 -0
  95. package/dates/DatePicker.js +118 -0
  96. package/display/Accordion.d.ts +15 -0
  97. package/display/Accordion.js +52 -0
  98. package/display/AspectRatio.d.ts +8 -0
  99. package/display/AspectRatio.js +24 -0
  100. package/display/Avatar.d.ts +17 -0
  101. package/display/Avatar.js +52 -0
  102. package/display/Badge.d.ts +10 -0
  103. package/display/Badge.js +34 -0
  104. package/display/Card.d.ts +17 -0
  105. package/display/Card.js +51 -0
  106. package/display/Carousel.d.ts +16 -0
  107. package/display/Carousel.js +97 -0
  108. package/display/Collapsible.d.ts +12 -0
  109. package/display/Collapsible.js +46 -0
  110. package/display/Divider.d.ts +9 -0
  111. package/display/Divider.js +33 -0
  112. package/display/Kbd.d.ts +7 -0
  113. package/display/Kbd.js +18 -0
  114. package/display/Link.d.ts +12 -0
  115. package/display/Link.js +41 -0
  116. package/display/ListItem.d.ts +19 -0
  117. package/display/ListItem.js +61 -0
  118. package/display/Resizable.d.ts +16 -0
  119. package/display/Resizable.js +90 -0
  120. package/display/ScrollArea.d.ts +8 -0
  121. package/display/ScrollArea.js +28 -0
  122. package/display/Tag.d.ts +9 -0
  123. package/display/Tag.js +41 -0
  124. package/display/Toolbar.d.ts +22 -0
  125. package/display/Toolbar.js +55 -0
  126. package/display/TreeList.d.ts +20 -0
  127. package/display/TreeList.js +101 -0
  128. package/feedback/Alert.d.ts +14 -0
  129. package/feedback/Alert.js +56 -0
  130. package/feedback/Banner.d.ts +15 -0
  131. package/feedback/Banner.js +49 -0
  132. package/feedback/ConfirmDialog.d.ts +18 -0
  133. package/feedback/ConfirmDialog.js +49 -0
  134. package/feedback/Dialog.d.ts +13 -0
  135. package/feedback/Dialog.js +107 -0
  136. package/feedback/Loader.d.ts +12 -0
  137. package/feedback/Loader.js +41 -0
  138. package/feedback/Progress.d.ts +15 -0
  139. package/feedback/Progress.js +48 -0
  140. package/feedback/Spinner.d.ts +9 -0
  141. package/feedback/Spinner.js +38 -0
  142. package/feedback/Toast.d.ts +70 -0
  143. package/feedback/Toast.js +216 -0
  144. package/feedback/Tooltip.d.ts +12 -0
  145. package/feedback/Tooltip.js +86 -0
  146. package/files/FileDrop.d.ts +15 -0
  147. package/files/FileDrop.js +75 -0
  148. package/files/FileTile.d.ts +19 -0
  149. package/files/FileTile.js +96 -0
  150. package/files/FileTypeIcon.d.ts +11 -0
  151. package/files/FileTypeIcon.js +121 -0
  152. package/finance/PaymentCard.d.ts +17 -0
  153. package/finance/PaymentCard.js +109 -0
  154. package/forms/Button.d.ts +15 -0
  155. package/forms/Button.js +82 -0
  156. package/forms/ButtonGroup.d.ts +5 -0
  157. package/forms/ButtonGroup.js +22 -0
  158. package/forms/ButtonTile.d.ts +21 -0
  159. package/forms/ButtonTile.js +69 -0
  160. package/forms/Checkbox.d.ts +6 -0
  161. package/forms/Checkbox.js +52 -0
  162. package/forms/Combobox.d.ts +17 -0
  163. package/forms/Combobox.js +162 -0
  164. package/forms/DigitEntry.d.ts +15 -0
  165. package/forms/DigitEntry.js +88 -0
  166. package/forms/FormField.d.ts +39 -0
  167. package/forms/FormField.js +127 -0
  168. package/forms/IconButton.d.ts +11 -0
  169. package/forms/IconButton.js +41 -0
  170. package/forms/Input.d.ts +14 -0
  171. package/forms/Input.js +75 -0
  172. package/forms/InputGroup.d.ts +15 -0
  173. package/forms/InputGroup.js +59 -0
  174. package/forms/Label.d.ts +11 -0
  175. package/forms/Label.js +29 -0
  176. package/forms/Radio.d.ts +6 -0
  177. package/forms/Radio.js +46 -0
  178. package/forms/Select.d.ts +10 -0
  179. package/forms/Select.js +68 -0
  180. package/forms/Slider.d.ts +18 -0
  181. package/forms/Slider.js +75 -0
  182. package/forms/Switch.d.ts +6 -0
  183. package/forms/Switch.js +46 -0
  184. package/forms/Textarea.d.ts +8 -0
  185. package/forms/Textarea.js +58 -0
  186. package/forms/Toggle.d.ts +26 -0
  187. package/forms/Toggle.js +85 -0
  188. package/icons/Icon.d.ts +13 -0
  189. package/icons/Icon.js +54 -0
  190. package/index.d.ts +106 -0
  191. package/index.js +106 -0
  192. package/navigation/Breadcrumbs.d.ts +12 -0
  193. package/navigation/Breadcrumbs.js +41 -0
  194. package/navigation/Menubar.d.ts +10 -0
  195. package/navigation/Menubar.js +173 -0
  196. package/navigation/PageControl.d.ts +11 -0
  197. package/navigation/PageControl.js +36 -0
  198. package/navigation/Pagination.d.ts +9 -0
  199. package/navigation/Pagination.js +68 -0
  200. package/navigation/SegmentedControl.d.ts +17 -0
  201. package/navigation/SegmentedControl.js +42 -0
  202. package/navigation/SideNav.d.ts +18 -0
  203. package/navigation/SideNav.js +87 -0
  204. package/navigation/Steps.d.ts +15 -0
  205. package/navigation/Steps.js +61 -0
  206. package/navigation/Tabs.d.ts +17 -0
  207. package/navigation/Tabs.js +76 -0
  208. package/navigation/TopNav.d.ts +10 -0
  209. package/navigation/TopNav.js +26 -0
  210. package/overlay/CommandPalette.d.ts +21 -0
  211. package/overlay/CommandPalette.js +164 -0
  212. package/overlay/ContextMenu.d.ts +11 -0
  213. package/overlay/ContextMenu.js +140 -0
  214. package/overlay/Drawer.d.ts +14 -0
  215. package/overlay/Drawer.js +99 -0
  216. package/overlay/HoverCard.d.ts +17 -0
  217. package/overlay/HoverCard.js +74 -0
  218. package/overlay/Menu.d.ts +24 -0
  219. package/overlay/Menu.js +153 -0
  220. package/overlay/Popover.d.ts +16 -0
  221. package/overlay/Popover.js +96 -0
  222. package/overlay/Portal.d.ts +10 -0
  223. package/overlay/Portal.js +127 -0
  224. package/package.json +41 -0
  225. package/styles.css +6 -0
  226. package/tokens/base.css +10 -0
  227. package/tokens/colors.css +38 -0
  228. package/tokens/effects.css +22 -0
  229. package/tokens/fonts.css +5 -0
  230. package/tokens/spacing.css +12 -0
  231. package/tokens/typography.css +9 -0
@@ -0,0 +1,13 @@
1
+ export interface DialogProps {
2
+ open: boolean;
3
+ /** Called on ✕, ESC, or overlay click; omit to hide ✕ */
4
+ onClose?: () => void;
5
+ title: React.ReactNode;
6
+ description?: React.ReactNode;
7
+ /** Right-aligned action row on sunken strip */
8
+ footer?: React.ReactNode;
9
+ /** Max width px @default 440 */
10
+ width?: number;
11
+ children?: React.ReactNode;
12
+ }
13
+ export declare const Dialog: React.ForwardRefExoticComponent<DialogProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,107 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { Icon } from "../icons/Icon.js";
4
+ import { IconButton } from "../forms/IconButton.js";
5
+ import { injectEfCss } from "../forms/Button.js";
6
+ const CSS = `
7
+ .ef-dialog__overlay{position:fixed;inset:0;background:var(--overlay-scrim);display:flex;align-items:center;justify-content:center;padding:24px;z-index:var(--z-overlay);animation:ef-fade var(--dur-med) var(--ease-out)}
8
+ .ef-dialog{width:100%;max-width:440px;background:var(--surface-card);border-radius:var(--radius-lg);box-shadow:var(--shadow-pop);animation:ef-pop var(--dur-slow) var(--ease-spring);overflow:hidden}
9
+ .ef-dialog:focus{outline:none}
10
+ .ef-dialog__head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:20px 24px 0}
11
+ .ef-dialog__title{font-family:var(--font-display);font-size:var(--text-xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tight);color:var(--text-primary)}
12
+ .ef-dialog__desc{font-size:var(--text-md);color:var(--text-secondary);margin-top:6px}
13
+ .ef-dialog__body{padding:16px 24px 24px}
14
+ .ef-dialog__foot{display:flex;justify-content:flex-end;gap:8px;padding:14px 24px;background:var(--surface-subtle);border-top:1px solid var(--border-default)}
15
+ @keyframes ef-fade{from{opacity:0}}
16
+ @keyframes ef-pop{from{opacity:0;transform:scale(.94) translateY(8px)}}
17
+ `;
18
+ const FOCUSABLE = 'button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])';
19
+ export const Dialog = /*#__PURE__*/React.forwardRef(function Dialog({
20
+ open,
21
+ onClose,
22
+ title,
23
+ description,
24
+ footer,
25
+ width = 440,
26
+ children,
27
+ ...rest
28
+ }, ref) {
29
+ injectEfCss('ef-css-dialog', CSS);
30
+ const panelRef = React.useRef(null);
31
+ const prevFocus = React.useRef(null);
32
+ const titleId = React.useId();
33
+ const descId = React.useId();
34
+ React.useEffect(() => {
35
+ if (!open) return;
36
+ prevFocus.current = document.activeElement;
37
+ const panel = panelRef.current;
38
+ const first = panel && panel.querySelector(FOCUSABLE);
39
+ (first || panel).focus();
40
+ return () => {
41
+ if (prevFocus.current && prevFocus.current.focus) prevFocus.current.focus();
42
+ };
43
+ }, [open]);
44
+ React.useEffect(() => {
45
+ if (!open) return;
46
+ const onKey = e => {
47
+ if (e.key === 'Escape') {
48
+ if (onClose) onClose();
49
+ return;
50
+ }
51
+ if (e.key !== 'Tab') return;
52
+ const panel = panelRef.current;
53
+ const f = panel ? Array.from(panel.querySelectorAll(FOCUSABLE)).filter(el => !el.disabled) : [];
54
+ if (!f.length) {
55
+ e.preventDefault();
56
+ return;
57
+ }
58
+ const i = f.indexOf(document.activeElement);
59
+ if (e.shiftKey && i <= 0) {
60
+ e.preventDefault();
61
+ f[f.length - 1].focus();
62
+ } else if (!e.shiftKey && (i === -1 || i === f.length - 1)) {
63
+ e.preventDefault();
64
+ f[0].focus();
65
+ }
66
+ };
67
+ document.addEventListener('keydown', onKey);
68
+ return () => document.removeEventListener('keydown', onKey);
69
+ }, [open, onClose]);
70
+ if (!open) return null;
71
+ return /*#__PURE__*/React.createElement("div", _extends({
72
+ ref: ref
73
+ }, rest, {
74
+ className: "ef-dialog__overlay",
75
+ onMouseDown: e => {
76
+ if (e.target === e.currentTarget && onClose) onClose();
77
+ }
78
+ }), /*#__PURE__*/React.createElement("div", {
79
+ className: "ef-dialog",
80
+ role: "dialog",
81
+ "aria-modal": "true",
82
+ ref: panelRef,
83
+ tabIndex: -1,
84
+ "aria-labelledby": title ? titleId : undefined,
85
+ "aria-describedby": description ? descId : undefined,
86
+ style: {
87
+ maxWidth: width
88
+ }
89
+ }, /*#__PURE__*/React.createElement("div", {
90
+ className: "ef-dialog__head"
91
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
92
+ className: "ef-dialog__title",
93
+ id: titleId
94
+ }, title), description ? /*#__PURE__*/React.createElement("div", {
95
+ className: "ef-dialog__desc",
96
+ id: descId
97
+ }, description) : null), onClose ? /*#__PURE__*/React.createElement(IconButton, {
98
+ icon: "x",
99
+ label: "Close",
100
+ size: "sm",
101
+ onClick: onClose
102
+ }) : null), /*#__PURE__*/React.createElement("div", {
103
+ className: "ef-dialog__body"
104
+ }, children), footer ? /*#__PURE__*/React.createElement("div", {
105
+ className: "ef-dialog__foot"
106
+ }, footer) : null));
107
+ });
@@ -0,0 +1,12 @@
1
+ export interface LoaderProps {
2
+ /** 'pulse' | 'shimmer' */
3
+ variant?: 'pulse' | 'shimmer';
4
+ /** Trailing animated ellipsis */
5
+ dots?: boolean;
6
+ /** Cycle duration in seconds */
7
+ duration?: number;
8
+ children?: React.ReactNode;
9
+ style?: React.CSSProperties;
10
+ className?: string;
11
+ }
12
+ export declare function Loader(props: LoaderProps): React.JSX.Element;
@@ -0,0 +1,41 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { injectEfCss } from "../forms/Button.js";
4
+ const CSS = `
5
+ .ef-loader{display:inline-flex;align-items:baseline;font-family:var(--font-sans);font-size:13.5px;color:var(--text-muted)}
6
+ .ef-loader--pulse{animation:ef-loader-pulse var(--ef-loader-dur,1.4s) ease-in-out infinite both}
7
+ @keyframes ef-loader-pulse{0%,100%{opacity:.4}50%{opacity:1}}
8
+ .ef-loader--shimmer{background:linear-gradient(90deg,color-mix(in oklab,currentColor 40%,transparent) 0%,color-mix(in oklab,currentColor 40%,transparent) 35%,currentColor 50%,color-mix(in oklab,currentColor 40%,transparent) 65%,color-mix(in oklab,currentColor 40%,transparent) 100%);background-size:200% 100%;-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;animation:ef-loader-shimmer var(--ef-loader-dur,2.2s) linear infinite}
9
+ @keyframes ef-loader-shimmer{0%{background-position:100% 0}100%{background-position:-100% 0}}
10
+ .ef-loader__dots{margin-left:2px;display:inline-flex;-webkit-text-fill-color:currentColor}
11
+ .ef-loader__dots span{animation:ef-loader-dot var(--ef-loader-dur,1.4s) ease-in-out infinite both}
12
+ .ef-loader__dots span:nth-child(2){animation-delay:calc(var(--ef-loader-dur,1.4s)*.14)}
13
+ .ef-loader__dots span:nth-child(3){animation-delay:calc(var(--ef-loader-dur,1.4s)*.28)}
14
+ @keyframes ef-loader-dot{0%,80%,100%{opacity:0}40%{opacity:1}}
15
+ `;
16
+ export function Loader({
17
+ variant = 'pulse',
18
+ dots,
19
+ duration,
20
+ children = 'Thinking',
21
+ style,
22
+ className,
23
+ ...rest
24
+ }) {
25
+ injectEfCss('ef-css-loader', CSS);
26
+ const vars = duration ? {
27
+ '--ef-loader-dur': duration + 's'
28
+ } : null;
29
+ return /*#__PURE__*/React.createElement("span", _extends({}, rest, {
30
+ role: "status",
31
+ "aria-label": typeof children === 'string' ? children : 'Loading',
32
+ className: `ef-loader ef-loader--${variant}${className ? ' ' + className : ''}`,
33
+ style: {
34
+ ...vars,
35
+ ...style
36
+ }
37
+ }), children, dots ? /*#__PURE__*/React.createElement("span", {
38
+ className: "ef-loader__dots",
39
+ "aria-hidden": "true"
40
+ }, /*#__PURE__*/React.createElement("span", null, "."), /*#__PURE__*/React.createElement("span", null, "."), /*#__PURE__*/React.createElement("span", null, ".")) : null);
41
+ }
@@ -0,0 +1,15 @@
1
+ export interface ProgressProps {
2
+ /** @default 0 */
3
+ value?: number;
4
+ /** @default 100 */
5
+ max?: number;
6
+ label?: string;
7
+ /** Show value right of the label (default = percent) */
8
+ showValue?: boolean;
9
+ format?: (value: number, max: number) => string;
10
+ /** @default 'default' (ink) */
11
+ tone?: 'default' | 'warning' | 'danger';
12
+ style?: React.CSSProperties;
13
+ className?: string;
14
+ }
15
+ export declare function Progress(props: ProgressProps): React.JSX.Element;
@@ -0,0 +1,48 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { injectEfCss } from "../forms/Button.js";
4
+ const CSS = `
5
+ .ef-progress{display:flex;flex-direction:column;gap:6px}
6
+ .ef-progress__head{display:flex;justify-content:space-between;align-items:baseline}
7
+ .ef-progress__label{font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--text-primary)}
8
+ .ef-progress__val{font-family:var(--font-mono);font-size:var(--text-xs);color:var(--text-muted)}
9
+ .ef-progress__track{height:4px;border-radius:var(--radius-full);background:var(--sand-200);overflow:hidden}
10
+ .ef-progress__fill{height:100%;border-radius:var(--radius-full);background:var(--accent);transition:width var(--dur-slow) var(--ease-out)}
11
+ .ef-progress--warning .ef-progress__fill{background:var(--warning-600)}
12
+ .ef-progress--danger .ef-progress__fill{background:var(--danger-600)}
13
+ `;
14
+ export function Progress({
15
+ value = 0,
16
+ max = 100,
17
+ label,
18
+ showValue,
19
+ format,
20
+ tone = 'default',
21
+ style,
22
+ className,
23
+ ...rest
24
+ }) {
25
+ injectEfCss('ef-css-progress', CSS);
26
+ const pct = Math.min(100, Math.max(0, value / max * 100));
27
+ return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
28
+ className: `ef-progress ef-progress--${tone}${className ? ' ' + className : ''}`,
29
+ style: style,
30
+ role: "progressbar",
31
+ "aria-valuenow": value,
32
+ "aria-valuemin": 0,
33
+ "aria-valuemax": max
34
+ }), (label || showValue) && /*#__PURE__*/React.createElement("div", {
35
+ className: "ef-progress__head"
36
+ }, /*#__PURE__*/React.createElement("span", {
37
+ className: "ef-progress__label"
38
+ }, label), showValue ? /*#__PURE__*/React.createElement("span", {
39
+ className: "ef-progress__val"
40
+ }, format ? format(value, max) : Math.round(pct) + '%') : null), /*#__PURE__*/React.createElement("div", {
41
+ className: "ef-progress__track"
42
+ }, /*#__PURE__*/React.createElement("div", {
43
+ className: "ef-progress__fill",
44
+ style: {
45
+ width: pct + '%'
46
+ }
47
+ })));
48
+ }
@@ -0,0 +1,9 @@
1
+ export interface SpinnerProps {
2
+ /** Px @default 16 */
3
+ size?: number;
4
+ /** Optional text right of the spinner (also the aria-label) */
5
+ label?: string;
6
+ style?: React.CSSProperties;
7
+ className?: string;
8
+ }
9
+ export declare function Spinner(props: SpinnerProps): React.JSX.Element;
@@ -0,0 +1,38 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { Icon } from "../icons/Icon.js";
4
+ import { injectEfCss } from "../forms/Button.js";
5
+ const CSS = `
6
+ @keyframes ef-spinner{to{transform:rotate(360deg)}}
7
+ .ef-spinner{display:inline-flex;color:var(--text-muted);animation:ef-spinner .7s linear infinite}
8
+ `;
9
+ export function Spinner({
10
+ size = 16,
11
+ label,
12
+ style,
13
+ className,
14
+ ...rest
15
+ }) {
16
+ injectEfCss('ef-css-spinner', CSS);
17
+ return /*#__PURE__*/React.createElement("span", _extends({}, rest, {
18
+ role: "status",
19
+ "aria-label": label || 'Loading',
20
+ style: {
21
+ display: 'inline-flex',
22
+ alignItems: 'center',
23
+ gap: 8,
24
+ ...style
25
+ },
26
+ className: className
27
+ }), /*#__PURE__*/React.createElement("span", {
28
+ className: "ef-spinner"
29
+ }, /*#__PURE__*/React.createElement(Icon, {
30
+ name: "loader-circle",
31
+ size: size
32
+ })), label ? /*#__PURE__*/React.createElement("span", {
33
+ style: {
34
+ fontSize: 'var(--text-sm)',
35
+ color: 'var(--text-muted)'
36
+ }
37
+ }, label) : null);
38
+ }
@@ -0,0 +1,70 @@
1
+ export interface ToastProps {
2
+ /** @default 'info' */
3
+ tone?: 'info' | 'success' | 'warning' | 'danger';
4
+ title: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ actionLabel?: string;
7
+ onAction?: () => void;
8
+ /** Shows a dismiss ✕ */
9
+ onClose?: () => void;
10
+ /**
11
+ * Live-region role. Inside a `Toaster` the stack owns the live region and each
12
+ * toast renders with `null`, so a queue announces once rather than as N
13
+ * competing regions. @default 'status'
14
+ */
15
+ role?: 'status' | 'alert' | null;
16
+ style?: React.CSSProperties;
17
+ className?: string;
18
+ }
19
+ export declare function Toast(props: ToastProps): React.JSX.Element;
20
+
21
+ export interface ToastStackProps {
22
+ /** Toasts, stacked fixed bottom-right */
23
+ children?: React.ReactNode;
24
+ style?: React.CSSProperties;
25
+ className?: string;
26
+ }
27
+ export declare function ToastStack(props: ToastStackProps): React.JSX.Element;
28
+
29
+ export interface ToastOptions {
30
+ title: React.ReactNode;
31
+ description?: React.ReactNode;
32
+ /** @default 'info' */
33
+ tone?: 'info' | 'success' | 'warning' | 'danger';
34
+ actionLabel?: string;
35
+ onAction?: () => void;
36
+ /**
37
+ * ms before auto-dismiss; 0 never dismisses.
38
+ * @default the Toaster's `duration`, or 0 when `actionLabel` is set — a
39
+ * control must not vanish on a timer the user cannot adjust (WCAG 2.2.1).
40
+ */
41
+ duration?: number;
42
+ }
43
+ export interface ToastApi {
44
+ /** Queue a toast; returns its id. */
45
+ notify: (options: ToastOptions) => string;
46
+ dismiss: (id: string) => void;
47
+ dismissAll: () => void;
48
+ }
49
+ export interface ToasterProps {
50
+ /** ms before a toast auto-dismisses; 0 never. @default 5000 */
51
+ duration?: number;
52
+ /** aria-label of the live region. @default 'Notifications' */
53
+ label?: string;
54
+ /** Your app. Toaster renders a Fragment, never a wrapper element. */
55
+ children?: React.ReactNode;
56
+ style?: React.CSSProperties;
57
+ className?: string;
58
+ }
59
+ /**
60
+ * Owns the toast queue: ids, timers, the live region and the portal. Timers
61
+ * pause while the stack is hovered or focused.
62
+ *
63
+ * The hook is published as a static — `Toaster.useToast()` — because only
64
+ * capitalised exports reach the global namespace.
65
+ */
66
+ export declare function Toaster(props: ToasterProps): React.JSX.Element;
67
+ export declare namespace Toaster {
68
+ /** Must be called inside a `<Toaster>`; throws otherwise. */
69
+ function useToast(): ToastApi;
70
+ }
@@ -0,0 +1,216 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { Icon } from "../icons/Icon.js";
4
+ import { IconButton } from "../forms/IconButton.js";
5
+ import { injectEfCss } from "../forms/Button.js";
6
+ import { Portal } from "../overlay/Portal.js";
7
+ const CSS = `
8
+ .ef-toast{display:flex;align-items:flex-start;gap:10px;width:360px;max-width:100%;padding:12px 14px;background:var(--surface-inverse);color:var(--text-inverse);border-radius:var(--radius-md);box-shadow:var(--shadow-lg);animation:ef-toast-in var(--dur-slow) var(--ease-spring)}
9
+ .ef-toast__icon{display:inline-flex;margin-top:1px}
10
+ .ef-toast--success .ef-toast__icon{color:#7FD08D}
11
+ .ef-toast--danger .ef-toast__icon{color:#F49B93}
12
+ .ef-toast--warning .ef-toast__icon{color:#F2C078}
13
+ .ef-toast--info .ef-toast__icon{color:#EFCFAC}
14
+ .ef-toast__title{font-size:var(--text-md);font-weight:var(--weight-semibold);line-height:1.35}
15
+ .ef-toast__desc{font-size:var(--text-sm);color:var(--text-inverse);opacity:.75;margin-top:2px;line-height:1.4}
16
+ .ef-toast__action{margin-top:8px;background:none;border:none;padding:0;color:var(--peach-200);font-family:var(--font-sans);font-size:var(--text-sm);font-weight:var(--weight-semibold);cursor:pointer}
17
+ .ef-toast__action:hover{color:var(--cream-50);text-decoration:underline}
18
+ .ef-toast .ef-iconbtn{color:rgba(250,249,246,.6)}
19
+ .ef-toast .ef-iconbtn:hover:not(:disabled){background:color-mix(in srgb,var(--text-inverse) 12%,transparent);color:var(--text-inverse)}
20
+ .ef-toast-stack{position:fixed;bottom:24px;right:24px;display:flex;flex-direction:column;gap:10px;z-index:var(--z-toast)}
21
+ [data-theme="dark"] .ef-toast--success .ef-toast__icon{color:var(--success-600)}
22
+ [data-theme="dark"] .ef-toast--danger .ef-toast__icon{color:var(--danger-600)}
23
+ [data-theme="dark"] .ef-toast--warning .ef-toast__icon{color:var(--warning-600)}
24
+ [data-theme="dark"] .ef-toast--info .ef-toast__icon{color:var(--brand-600)}
25
+ [data-theme="dark"] .ef-toast__action{color:var(--brand-700)}
26
+ [data-theme="dark"] .ef-toast .ef-iconbtn{color:rgba(30,26,20,.55)}
27
+ @keyframes ef-toast-in{from{opacity:0;transform:translateY(12px) scale(.96)}}
28
+ `;
29
+ const ICONS = {
30
+ success: 'circle-check',
31
+ danger: 'circle-alert',
32
+ warning: 'triangle-alert',
33
+ info: 'info'
34
+ };
35
+ export function Toast({
36
+ tone = 'info',
37
+ title,
38
+ description,
39
+ actionLabel,
40
+ onAction,
41
+ onClose,
42
+ role = 'status',
43
+ style,
44
+ className,
45
+ ...rest
46
+ }) {
47
+ injectEfCss('ef-css-toast', CSS);
48
+ return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
49
+ className: `ef-toast ef-toast--${tone}${className ? ' ' + className : ''}`,
50
+ role: role || undefined,
51
+ style: style
52
+ }), /*#__PURE__*/React.createElement("span", {
53
+ className: "ef-toast__icon"
54
+ }, /*#__PURE__*/React.createElement(Icon, {
55
+ name: ICONS[tone] || 'info',
56
+ size: 18
57
+ })), /*#__PURE__*/React.createElement("div", {
58
+ style: {
59
+ flex: 1
60
+ }
61
+ }, /*#__PURE__*/React.createElement("div", {
62
+ className: "ef-toast__title"
63
+ }, title), description ? /*#__PURE__*/React.createElement("div", {
64
+ className: "ef-toast__desc"
65
+ }, description) : null, actionLabel ? /*#__PURE__*/React.createElement("button", {
66
+ className: "ef-toast__action",
67
+ onClick: onAction
68
+ }, actionLabel) : null), onClose ? /*#__PURE__*/React.createElement(IconButton, {
69
+ icon: "x",
70
+ label: "Dismiss",
71
+ size: "sm",
72
+ onClick: onClose
73
+ }) : null);
74
+ }
75
+ export function ToastStack({
76
+ children,
77
+ style,
78
+ className,
79
+ ...rest
80
+ }) {
81
+ injectEfCss('ef-css-toast', CSS);
82
+ return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
83
+ className: `ef-toast-stack${className ? ' ' + className : ''}`,
84
+ style: style
85
+ }), children);
86
+ }
87
+ const ToastCtx = /*#__PURE__*/React.createContext(null);
88
+
89
+ /**
90
+ * The toast queue API: notify, dismiss, dismissAll.
91
+ *
92
+ * Lowercase, so the compiler files it under unexposedExports and it never
93
+ * reaches the global namespace directly. Consumers get it as `Toaster.useToast`
94
+ * (assigned at the bottom of this file), which is the one sanctioned way to
95
+ * publish a hook when only capitalised exports are exposed.
96
+ *
97
+ * Throws outside a <Toaster> rather than degrading to a no-op: a notification
98
+ * that silently never appears is the harder bug to find.
99
+ */
100
+ export function useToast() {
101
+ const ctx = React.useContext(ToastCtx);
102
+ if (!ctx) throw new Error('useToast must be called inside a <Toaster>');
103
+ return ctx;
104
+ }
105
+
106
+ /**
107
+ * Owns the toast queue: ids, timers, the live region, and the portal.
108
+ *
109
+ * Renders a Fragment around your app plus one portaled stack, so it can sit at
110
+ * the root without introducing a wrapper element.
111
+ */
112
+ export function Toaster({
113
+ duration = 5000,
114
+ label = 'Notifications',
115
+ children,
116
+ ...rest
117
+ }) {
118
+ injectEfCss('ef-css-toast', CSS);
119
+ const uid = React.useId();
120
+ const seq = React.useRef(0);
121
+ const [toasts, setToasts] = React.useState([]);
122
+ const timers = React.useRef(new Map());
123
+ const dismiss = React.useCallback(id => {
124
+ const rec = timers.current.get(id);
125
+ if (rec && rec.handle) clearTimeout(rec.handle);
126
+ timers.current.delete(id);
127
+ setToasts(list => list.filter(x => x.id !== id));
128
+ }, []);
129
+ const arm = React.useCallback((id, ms) => {
130
+ const rec = {
131
+ remaining: ms,
132
+ startedAt: Date.now(),
133
+ handle: null
134
+ };
135
+ rec.handle = setTimeout(() => dismiss(id), ms);
136
+ timers.current.set(id, rec);
137
+ }, [dismiss]);
138
+ const notify = React.useCallback(options => {
139
+ const id = uid + seq.current++;
140
+ const {
141
+ duration: own,
142
+ ...props
143
+ } = options || {};
144
+ // A toast carrying an action must not time out: WCAG 2.2.1 does not allow a
145
+ // control to disappear on a timer the user cannot adjust.
146
+ const ms = own != null ? own : props.actionLabel ? 0 : duration;
147
+ setToasts(list => [...list, {
148
+ id,
149
+ props
150
+ }]);
151
+ if (ms > 0) arm(id, ms);
152
+ return id;
153
+ }, [uid, duration, arm]);
154
+ const dismissAll = React.useCallback(() => {
155
+ timers.current.forEach(rec => {
156
+ if (rec.handle) clearTimeout(rec.handle);
157
+ });
158
+ timers.current.clear();
159
+ setToasts([]);
160
+ }, []);
161
+ React.useEffect(() => () => {
162
+ timers.current.forEach(rec => {
163
+ if (rec.handle) clearTimeout(rec.handle);
164
+ });
165
+ timers.current.clear();
166
+ }, []);
167
+
168
+ // Hovering or focusing the stack holds every pending timer, so a slow reader
169
+ // or a keyboard user can actually reach the action inside a toast.
170
+ const pause = () => {
171
+ timers.current.forEach(rec => {
172
+ if (!rec.handle) return;
173
+ clearTimeout(rec.handle);
174
+ rec.remaining = Math.max(0, rec.remaining - (Date.now() - rec.startedAt));
175
+ rec.handle = null;
176
+ });
177
+ };
178
+ const resume = () => {
179
+ // A toast whose time ran out while paused must go on unpause, not become
180
+ // permanent. Collect them first: dismiss() mutates the map being iterated.
181
+ const expired = [];
182
+ timers.current.forEach((rec, id) => {
183
+ if (rec.handle) return;
184
+ if (rec.remaining <= 0) {
185
+ expired.push(id);
186
+ return;
187
+ }
188
+ rec.startedAt = Date.now();
189
+ rec.handle = setTimeout(() => dismiss(id), rec.remaining);
190
+ });
191
+ expired.forEach(dismiss);
192
+ };
193
+ const api = React.useMemo(() => ({
194
+ notify,
195
+ dismiss,
196
+ dismissAll
197
+ }), [notify, dismiss, dismissAll]);
198
+ return /*#__PURE__*/React.createElement(ToastCtx.Provider, {
199
+ value: api
200
+ }, children, /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement(ToastStack, _extends({}, rest, {
201
+ "aria-label": label,
202
+ role: "log",
203
+ "aria-live": "polite",
204
+ "aria-relevant": "additions",
205
+ onMouseEnter: pause,
206
+ onMouseLeave: resume,
207
+ onFocusCapture: pause,
208
+ onBlurCapture: resume
209
+ }), toasts.map(t => /*#__PURE__*/React.createElement(Toast, _extends({
210
+ key: t.id
211
+ }, t.props, {
212
+ role: null,
213
+ onClose: () => dismiss(t.id)
214
+ }))))));
215
+ }
216
+ Toaster.useToast = useToast;
@@ -0,0 +1,12 @@
1
+ export interface TooltipProps {
2
+ label: React.ReactNode;
3
+ /** Preferred side; flips automatically when there is no room. @default 'top' */
4
+ position?: 'top' | 'bottom';
5
+ /** Hover delay in ms before showing. Focus always shows immediately. @default 200 */
6
+ delay?: number;
7
+ /** The trigger element */
8
+ children?: React.ReactNode;
9
+ style?: React.CSSProperties;
10
+ className?: string;
11
+ }
12
+ export declare function Tooltip(props: TooltipProps): React.JSX.Element;
@@ -0,0 +1,86 @@
1
+ function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
+ import React from 'react';
3
+ import { injectEfCss } from "../forms/Button.js";
4
+ import { Portal, useAnchoredStyle } from "../overlay/Portal.js";
5
+ const CSS = `
6
+ .ef-tooltip{position:relative;display:inline-flex}
7
+ .ef-tooltip__bubble{position:fixed;background:var(--surface-inverse);color:var(--text-inverse);font-size:var(--text-xs);font-weight:var(--weight-medium);line-height:1.35;padding:5px 9px;border-radius:6px;white-space:nowrap;pointer-events:none;z-index:var(--z-tooltip);animation:ef-tooltip-in var(--dur-fast) var(--ease-out)}
8
+ .ef-tooltip__bubble::after{content:'';position:absolute;top:100%;left:var(--ef-tt-arrow,50%);transform:translateX(-50%);border:4px solid transparent;border-top-color:var(--surface-inverse)}
9
+ .ef-tooltip__bubble--bottom::after{top:auto;bottom:100%;border-top-color:transparent;border-bottom-color:var(--surface-inverse)}
10
+ @keyframes ef-tooltip-in{from{opacity:0}to{opacity:1}}
11
+ `;
12
+ export function Tooltip({
13
+ label,
14
+ position = 'top',
15
+ delay = 200,
16
+ children,
17
+ style,
18
+ className,
19
+ ...rest
20
+ }) {
21
+ injectEfCss('ef-css-tooltip', CSS);
22
+ const id = React.useId();
23
+ const [open, setOpen] = React.useState(false);
24
+ const ref = React.useRef(null);
25
+ const bubbleRef = React.useRef(null);
26
+ const timer = React.useRef(null);
27
+ const {
28
+ style: anchored,
29
+ side
30
+ } = useAnchoredStyle(ref, bubbleRef, {
31
+ open,
32
+ placement: position === 'bottom' ? 'bottom' : 'top',
33
+ align: 'center',
34
+ offset: 8
35
+ });
36
+
37
+ // Keep the arrow over the trigger's centre even when the bubble has been
38
+ // shifted to stay on screen, so it never points at empty space.
39
+ const [arrowX, setArrowX] = React.useState(null);
40
+ React.useLayoutEffect(() => {
41
+ if (!open || !ref.current || !bubbleRef.current) {
42
+ setArrowX(null);
43
+ return;
44
+ }
45
+ const a = ref.current.getBoundingClientRect();
46
+ const b = bubbleRef.current.getBoundingClientRect();
47
+ if (!b.width) return;
48
+ const x = a.left + a.width / 2 - b.left;
49
+ setArrowX(Math.max(8, Math.min(x, b.width - 8)));
50
+ }, [open, anchored.left, anchored.top]);
51
+ const show = immediate => {
52
+ clearTimeout(timer.current);
53
+ if (immediate) setOpen(true);else timer.current = setTimeout(() => setOpen(true), delay);
54
+ };
55
+ const hide = () => {
56
+ clearTimeout(timer.current);
57
+ setOpen(false);
58
+ };
59
+ React.useEffect(() => () => clearTimeout(timer.current), []);
60
+ const child = React.Children.count(children) === 1 && /*#__PURE__*/React.isValidElement(children) ? /*#__PURE__*/React.cloneElement(children, {
61
+ 'aria-describedby': open ? id : undefined
62
+ }) : children;
63
+ return /*#__PURE__*/React.createElement("span", _extends({}, rest, {
64
+ ref: ref,
65
+ className: `ef-tooltip${className ? ' ' + className : ''}`,
66
+ style: style,
67
+ onMouseEnter: () => show(false),
68
+ onMouseLeave: hide,
69
+ onFocus: () => show(true),
70
+ onBlur: hide,
71
+ onKeyDown: e => {
72
+ if (e.key === 'Escape') hide();
73
+ }
74
+ }), child, open ? /*#__PURE__*/React.createElement(Portal, null, /*#__PURE__*/React.createElement("span", {
75
+ ref: bubbleRef,
76
+ role: "tooltip",
77
+ id: id,
78
+ className: `ef-tooltip__bubble${side === 'bottom' ? ' ef-tooltip__bubble--bottom' : ''}`,
79
+ style: {
80
+ ...anchored,
81
+ ...(arrowX == null ? null : {
82
+ '--ef-tt-arrow': arrowX + 'px'
83
+ })
84
+ }
85
+ }, label)) : null);
86
+ }