eidotter 0.36.1 → 0.37.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 (327) hide show
  1. package/README.md +19 -0
  2. package/dist/components/AIText/components/AIText.d.ts +0 -1
  3. package/dist/components/AIText/components/AIText.js +23 -0
  4. package/dist/components/AIText/components/index.d.ts +2 -2
  5. package/dist/components/AIText/components/index.js +1 -0
  6. package/dist/components/AIText/index.d.ts +2 -2
  7. package/dist/components/AIText/index.js +1 -0
  8. package/dist/components/Accordion/components/AccordionFill.d.ts +0 -1
  9. package/dist/components/Accordion/components/AccordionFill.js +12 -0
  10. package/dist/components/Accordion/components/Section.d.ts +0 -1
  11. package/dist/components/Accordion/components/Section.js +22 -0
  12. package/dist/components/Accordion/components/index.d.ts +4 -4
  13. package/dist/components/Accordion/components/index.js +2 -0
  14. package/dist/components/Accordion/index.d.ts +1 -1
  15. package/dist/components/Accordion/index.js +1 -0
  16. package/dist/components/Alert/components/Alert.d.ts +0 -1
  17. package/dist/components/Alert/components/Alert.js +51 -0
  18. package/dist/components/Alert/components/index.d.ts +2 -2
  19. package/dist/components/Alert/components/index.js +1 -0
  20. package/dist/components/Alert/index.d.ts +1 -1
  21. package/dist/components/Alert/index.js +1 -0
  22. package/dist/components/Badge/components/Badge.d.ts +0 -1
  23. package/dist/components/Badge/components/Badge.js +31 -0
  24. package/dist/components/Badge/components/index.d.ts +2 -2
  25. package/dist/components/Badge/components/index.js +1 -0
  26. package/dist/components/Badge/index.d.ts +2 -2
  27. package/dist/components/Badge/index.js +1 -0
  28. package/dist/components/Brand/components/BrandLockup.js +14 -0
  29. package/dist/components/Brand/components/Logo.d.ts +0 -1
  30. package/dist/components/Brand/components/Logo.js +22 -0
  31. package/dist/components/Brand/components/Wordmark.d.ts +0 -1
  32. package/dist/components/Brand/components/Wordmark.js +13 -0
  33. package/dist/components/Brand/components/index.d.ts +6 -6
  34. package/dist/components/Brand/components/index.js +3 -0
  35. package/dist/components/Brand/index.d.ts +1 -1
  36. package/dist/components/Brand/index.js +1 -0
  37. package/dist/components/Breadcrumb/components/Breadcrumb.d.ts +0 -1
  38. package/dist/components/Breadcrumb/components/Breadcrumb.js +29 -0
  39. package/dist/components/Breadcrumb/components/index.d.ts +2 -2
  40. package/dist/components/Breadcrumb/components/index.js +1 -0
  41. package/dist/components/Breadcrumb/index.d.ts +2 -2
  42. package/dist/components/Breadcrumb/index.js +1 -0
  43. package/dist/components/Button/components/Button.d.ts +0 -2
  44. package/dist/components/Button/components/Button.js +49 -0
  45. package/dist/components/Button/components/index.d.ts +2 -2
  46. package/dist/components/Button/components/index.js +1 -0
  47. package/dist/components/Button/index.d.ts +2 -2
  48. package/dist/components/Button/index.js +1 -0
  49. package/dist/components/Card/components/Card.d.ts +0 -1
  50. package/dist/components/Card/components/Card.js +16 -0
  51. package/dist/components/Card/components/index.d.ts +2 -2
  52. package/dist/components/Card/components/index.js +1 -0
  53. package/dist/components/Card/index.d.ts +2 -2
  54. package/dist/components/Card/index.js +1 -0
  55. package/dist/components/Chat/components/ChatContainer.d.ts +1 -1
  56. package/dist/components/Chat/components/ChatContainer.js +20 -0
  57. package/dist/components/Chat/components/ChatHistory.d.ts +0 -1
  58. package/dist/components/Chat/components/ChatHistory.js +27 -0
  59. package/dist/components/Chat/components/ChatInput.d.ts +0 -2
  60. package/dist/components/Chat/components/ChatInput.js +37 -0
  61. package/dist/components/Chat/components/ChatMessage.d.ts +0 -2
  62. package/dist/components/Chat/components/ChatMessage.js +18 -0
  63. package/dist/components/Chat/components/index.d.ts +8 -8
  64. package/dist/components/Chat/components/index.js +4 -0
  65. package/dist/components/Chat/index.d.ts +2 -2
  66. package/dist/components/Chat/index.js +1 -0
  67. package/dist/components/Checkbox/components/Checkbox.d.ts +0 -2
  68. package/dist/components/Checkbox/components/Checkbox.js +11 -0
  69. package/dist/components/Checkbox/components/index.d.ts +2 -2
  70. package/dist/components/Checkbox/components/index.js +1 -0
  71. package/dist/components/Checkbox/index.d.ts +2 -2
  72. package/dist/components/Checkbox/index.js +1 -0
  73. package/dist/components/ChecklistRow/components/ChecklistRow.js +18 -0
  74. package/dist/components/ChecklistRow/components/index.d.ts +2 -2
  75. package/dist/components/ChecklistRow/components/index.js +1 -0
  76. package/dist/components/ChecklistRow/index.d.ts +2 -2
  77. package/dist/components/ChecklistRow/index.js +1 -0
  78. package/dist/components/CmdPalette/components/CmdPalette.d.ts +0 -2
  79. package/dist/components/CmdPalette/components/CmdPalette.js +169 -0
  80. package/dist/components/CmdPalette/components/index.d.ts +2 -2
  81. package/dist/components/CmdPalette/components/index.js +1 -0
  82. package/dist/components/CmdPalette/index.d.ts +2 -2
  83. package/dist/components/CmdPalette/index.js +1 -0
  84. package/dist/components/CommandPrompt/components/CommandPrompt.d.ts +0 -2
  85. package/dist/components/CommandPrompt/components/CommandPrompt.js +34 -0
  86. package/dist/components/CommandPrompt/components/index.d.ts +2 -2
  87. package/dist/components/CommandPrompt/components/index.js +1 -0
  88. package/dist/components/CommandPrompt/index.d.ts +1 -1
  89. package/dist/components/CommandPrompt/index.js +1 -0
  90. package/dist/components/CopyButton/components/CopyButton.js +50 -0
  91. package/dist/components/CopyButton/components/index.d.ts +2 -2
  92. package/dist/components/CopyButton/components/index.js +1 -0
  93. package/dist/components/CopyButton/index.d.ts +2 -2
  94. package/dist/components/CopyButton/index.js +1 -0
  95. package/dist/components/DosFigure/components/DosFigure.d.ts +0 -1
  96. package/dist/components/DosFigure/components/DosFigure.js +26 -0
  97. package/dist/components/DosFigure/components/index.d.ts +2 -2
  98. package/dist/components/DosFigure/components/index.js +1 -0
  99. package/dist/components/DosFigure/index.d.ts +2 -2
  100. package/dist/components/DosFigure/index.js +1 -0
  101. package/dist/components/EmptyState/components/EmptyState.js +18 -0
  102. package/dist/components/EmptyState/components/index.d.ts +2 -2
  103. package/dist/components/EmptyState/components/index.js +1 -0
  104. package/dist/components/EmptyState/index.d.ts +2 -2
  105. package/dist/components/EmptyState/index.js +1 -0
  106. package/dist/components/FilterBar/components/FilterBar.d.ts +0 -1
  107. package/dist/components/FilterBar/components/FilterBar.js +120 -0
  108. package/dist/components/FilterBar/components/index.d.ts +2 -2
  109. package/dist/components/FilterBar/components/index.js +1 -0
  110. package/dist/components/FilterBar/index.d.ts +2 -2
  111. package/dist/components/FilterBar/index.js +1 -0
  112. package/dist/components/Footer/components/Footer.d.ts +0 -1
  113. package/dist/components/Footer/components/Footer.js +18 -0
  114. package/dist/components/Footer/components/index.d.ts +2 -2
  115. package/dist/components/Footer/components/index.js +1 -0
  116. package/dist/components/Footer/index.d.ts +2 -2
  117. package/dist/components/Footer/index.js +1 -0
  118. package/dist/components/Header/components/Header.d.ts +1 -2
  119. package/dist/components/Header/components/Header.js +13 -0
  120. package/dist/components/Header/components/index.d.ts +2 -2
  121. package/dist/components/Header/components/index.js +1 -0
  122. package/dist/components/Header/index.d.ts +2 -2
  123. package/dist/components/Header/index.js +1 -0
  124. package/dist/components/Icon/components/Icon.d.ts +0 -1
  125. package/dist/components/Icon/components/Icon.js +78 -0
  126. package/dist/components/Icon/components/index.d.ts +2 -2
  127. package/dist/components/Icon/components/index.js +1 -0
  128. package/dist/components/Icon/index.d.ts +1 -1
  129. package/dist/components/Icon/index.js +1 -0
  130. package/dist/components/InlineExpand/components/InlineExpand.d.ts +0 -1
  131. package/dist/components/InlineExpand/components/InlineExpand.js +50 -0
  132. package/dist/components/InlineExpand/components/index.d.ts +2 -2
  133. package/dist/components/InlineExpand/components/index.js +1 -0
  134. package/dist/components/InlineExpand/index.d.ts +2 -2
  135. package/dist/components/InlineExpand/index.js +1 -0
  136. package/dist/components/InlineLink/components/InlineLink.d.ts +0 -1
  137. package/dist/components/InlineLink/components/InlineLink.js +61 -0
  138. package/dist/components/InlineLink/components/index.d.ts +2 -2
  139. package/dist/components/InlineLink/components/index.js +1 -0
  140. package/dist/components/InlineLink/index.d.ts +2 -2
  141. package/dist/components/InlineLink/index.js +1 -0
  142. package/dist/components/Input/components/Input.d.ts +0 -1
  143. package/dist/components/Input/components/Input.js +18 -0
  144. package/dist/components/Input/components/index.d.ts +2 -2
  145. package/dist/components/Input/components/index.js +1 -0
  146. package/dist/components/Input/index.d.ts +1 -1
  147. package/dist/components/Input/index.js +1 -0
  148. package/dist/components/LabeledProgress/components/LabeledProgress.d.ts +0 -1
  149. package/dist/components/LabeledProgress/components/LabeledProgress.js +16 -0
  150. package/dist/components/LabeledProgress/components/index.d.ts +2 -2
  151. package/dist/components/LabeledProgress/components/index.js +1 -0
  152. package/dist/components/LabeledProgress/index.d.ts +2 -2
  153. package/dist/components/LabeledProgress/index.js +1 -0
  154. package/dist/components/LedgerRow/components/LedgerRow.js +15 -0
  155. package/dist/components/LedgerRow/components/index.d.ts +2 -2
  156. package/dist/components/LedgerRow/components/index.js +1 -0
  157. package/dist/components/LedgerRow/index.d.ts +2 -2
  158. package/dist/components/LedgerRow/index.js +1 -0
  159. package/dist/components/LegalPage/components/LegalPage.d.ts +0 -1
  160. package/dist/components/LegalPage/components/LegalPage.js +11 -0
  161. package/dist/components/LegalPage/components/clauses.js +28 -0
  162. package/dist/components/LegalPage/components/index.d.ts +4 -4
  163. package/dist/components/LegalPage/components/index.js +2 -0
  164. package/dist/components/LegalPage/index.d.ts +2 -2
  165. package/dist/components/LegalPage/index.js +1 -0
  166. package/dist/components/Lightbox/components/Lightbox.d.ts +1 -3
  167. package/dist/components/Lightbox/components/Lightbox.js +97 -0
  168. package/dist/components/Lightbox/components/index.d.ts +2 -2
  169. package/dist/components/Lightbox/components/index.js +1 -0
  170. package/dist/components/Lightbox/index.d.ts +2 -2
  171. package/dist/components/Lightbox/index.js +1 -0
  172. package/dist/components/Modal/components/Modal.d.ts +0 -2
  173. package/dist/components/Modal/components/Modal.js +37 -0
  174. package/dist/components/Modal/components/index.d.ts +2 -2
  175. package/dist/components/Modal/components/index.js +1 -0
  176. package/dist/components/Modal/index.d.ts +2 -2
  177. package/dist/components/Modal/index.js +1 -0
  178. package/dist/components/Nav/components/Nav.d.ts +0 -1
  179. package/dist/components/Nav/components/Nav.js +69 -0
  180. package/dist/components/Nav/components/index.d.ts +2 -2
  181. package/dist/components/Nav/components/index.js +1 -0
  182. package/dist/components/Nav/index.d.ts +2 -2
  183. package/dist/components/Nav/index.js +1 -0
  184. package/dist/components/Notification/components/Notification.d.ts +1 -2
  185. package/dist/components/Notification/components/Notification.js +34 -0
  186. package/dist/components/Notification/components/index.d.ts +2 -2
  187. package/dist/components/Notification/components/index.js +1 -0
  188. package/dist/components/Notification/index.d.ts +1 -1
  189. package/dist/components/Notification/index.js +1 -0
  190. package/dist/components/Progress/components/Progress.d.ts +0 -1
  191. package/dist/components/Progress/components/Progress.js +72 -0
  192. package/dist/components/Progress/components/index.d.ts +2 -2
  193. package/dist/components/Progress/components/index.js +1 -0
  194. package/dist/components/Progress/index.d.ts +2 -2
  195. package/dist/components/Progress/index.js +1 -0
  196. package/dist/components/RetroEffects/components/RetroEffects.d.ts +0 -1
  197. package/dist/components/RetroEffects/components/RetroEffects.js +163 -0
  198. package/dist/components/RetroEffects/components/index.d.ts +2 -2
  199. package/dist/components/RetroEffects/components/index.js +1 -0
  200. package/dist/components/RetroEffects/index.d.ts +2 -2
  201. package/dist/components/RetroEffects/index.js +1 -0
  202. package/dist/components/SectionHeading/components/SectionHeading.js +18 -0
  203. package/dist/components/SectionHeading/components/index.d.ts +2 -2
  204. package/dist/components/SectionHeading/components/index.js +1 -0
  205. package/dist/components/SectionHeading/index.d.ts +2 -2
  206. package/dist/components/SectionHeading/index.js +1 -0
  207. package/dist/components/Select/components/Select.js +14 -0
  208. package/dist/components/Select/components/index.d.ts +2 -2
  209. package/dist/components/Select/components/index.js +1 -0
  210. package/dist/components/Select/index.d.ts +2 -2
  211. package/dist/components/Select/index.js +1 -0
  212. package/dist/components/Separator/components/Separator.d.ts +0 -1
  213. package/dist/components/Separator/components/Separator.js +11 -0
  214. package/dist/components/Separator/components/index.d.ts +2 -2
  215. package/dist/components/Separator/components/index.js +1 -0
  216. package/dist/components/Separator/index.d.ts +2 -2
  217. package/dist/components/Separator/index.js +1 -0
  218. package/dist/components/Skeleton/components/Skeleton.d.ts +0 -1
  219. package/dist/components/Skeleton/components/Skeleton.js +52 -0
  220. package/dist/components/Skeleton/components/index.d.ts +2 -2
  221. package/dist/components/Skeleton/components/index.js +1 -0
  222. package/dist/components/Skeleton/index.d.ts +2 -2
  223. package/dist/components/Skeleton/index.js +1 -0
  224. package/dist/components/Stat/components/Stat.d.ts +0 -1
  225. package/dist/components/Stat/components/Stat.js +42 -0
  226. package/dist/components/Stat/components/index.d.ts +2 -2
  227. package/dist/components/Stat/components/index.js +1 -0
  228. package/dist/components/Stat/index.d.ts +2 -2
  229. package/dist/components/Stat/index.js +1 -0
  230. package/dist/components/Switch/components/Switch.d.ts +0 -1
  231. package/dist/components/Switch/components/Switch.js +11 -0
  232. package/dist/components/Switch/components/index.d.ts +2 -2
  233. package/dist/components/Switch/components/index.js +1 -0
  234. package/dist/components/Switch/index.d.ts +2 -2
  235. package/dist/components/Switch/index.js +1 -0
  236. package/dist/components/Tabs/components/Tabs.d.ts +0 -1
  237. package/dist/components/Tabs/components/Tabs.js +54 -0
  238. package/dist/components/Tabs/components/index.d.ts +2 -2
  239. package/dist/components/Tabs/components/index.js +1 -0
  240. package/dist/components/Tabs/index.d.ts +2 -2
  241. package/dist/components/Tabs/index.js +1 -0
  242. package/dist/components/Tag/components/Tag.d.ts +0 -1
  243. package/dist/components/Tag/components/Tag.js +46 -0
  244. package/dist/components/Tag/components/TagGroup.js +15 -0
  245. package/dist/components/Tag/components/index.d.ts +4 -4
  246. package/dist/components/Tag/components/index.js +2 -0
  247. package/dist/components/Tag/index.d.ts +2 -2
  248. package/dist/components/Tag/index.js +1 -0
  249. package/dist/components/Terminal/components/Terminal.d.ts +0 -2
  250. package/dist/components/Terminal/components/Terminal.js +43 -0
  251. package/dist/components/Terminal/components/index.d.ts +2 -2
  252. package/dist/components/Terminal/components/index.js +1 -0
  253. package/dist/components/Terminal/index.d.ts +1 -1
  254. package/dist/components/Terminal/index.js +1 -0
  255. package/dist/components/TextScramble/components/TextScramble.d.ts +0 -1
  256. package/dist/components/TextScramble/components/TextScramble.js +14 -0
  257. package/dist/components/TextScramble/components/index.d.ts +2 -2
  258. package/dist/components/TextScramble/components/index.js +1 -0
  259. package/dist/components/TextScramble/index.d.ts +1 -1
  260. package/dist/components/TextScramble/index.js +1 -0
  261. package/dist/components/TimelineContainer/components/MasterDetailLayout.d.ts +1 -3
  262. package/dist/components/TimelineContainer/components/MasterDetailLayout.js +77 -0
  263. package/dist/components/TimelineContainer/components/TimelineAxis.d.ts +0 -1
  264. package/dist/components/TimelineContainer/components/TimelineAxis.js +8 -0
  265. package/dist/components/TimelineContainer/components/TimelineContainer.d.ts +1 -3
  266. package/dist/components/TimelineContainer/components/TimelineContainer.js +218 -0
  267. package/dist/components/TimelineContainer/components/TimelineContent.d.ts +1 -1
  268. package/dist/components/TimelineContainer/components/TimelineContent.js +23 -0
  269. package/dist/components/TimelineContainer/components/TimelineEntryCard.d.ts +1 -2
  270. package/dist/components/TimelineContainer/components/TimelineEntryCard.js +50 -0
  271. package/dist/components/TimelineContainer/components/ZoomControls.d.ts +2 -3
  272. package/dist/components/TimelineContainer/components/ZoomControls.js +16 -0
  273. package/dist/components/TimelineContainer/components/index.d.ts +5 -5
  274. package/dist/components/TimelineContainer/components/index.js +2 -0
  275. package/dist/components/TimelineContainer/components/timelineUtils.d.ts +1 -1
  276. package/dist/components/TimelineContainer/components/timelineUtils.js +185 -0
  277. package/dist/components/TimelineContainer/components/types.js +7 -0
  278. package/dist/components/TimelineContainer/components/useDrillDown.d.ts +1 -1
  279. package/dist/components/TimelineContainer/components/useDrillDown.js +83 -0
  280. package/dist/components/TimelineContainer/components/useSelection.js +51 -0
  281. package/dist/components/TimelineContainer/components/useZoom.d.ts +1 -1
  282. package/dist/components/TimelineContainer/components/useZoom.js +54 -0
  283. package/dist/components/TimelineContainer/components/variants/TimelineEntryCardGallery.d.ts +1 -2
  284. package/dist/components/TimelineContainer/components/variants/TimelineEntryCardGallery.js +67 -0
  285. package/dist/components/TimelineContainer/components/variants/TimelineEntryCardImage.d.ts +1 -2
  286. package/dist/components/TimelineContainer/components/variants/TimelineEntryCardImage.js +31 -0
  287. package/dist/components/TimelineContainer/components/views/DayView.d.ts +1 -1
  288. package/dist/components/TimelineContainer/components/views/DayView.js +20 -0
  289. package/dist/components/TimelineContainer/components/views/HourView.d.ts +1 -1
  290. package/dist/components/TimelineContainer/components/views/HourView.js +22 -0
  291. package/dist/components/TimelineContainer/components/views/MonthView.d.ts +1 -1
  292. package/dist/components/TimelineContainer/components/views/MonthView.js +18 -0
  293. package/dist/components/TimelineContainer/components/views/YearView.d.ts +1 -1
  294. package/dist/components/TimelineContainer/components/views/YearView.js +13 -0
  295. package/dist/components/TimelineContainer/components/views/index.d.ts +4 -4
  296. package/dist/components/TimelineContainer/components/views/index.js +4 -0
  297. package/dist/components/TimelineContainer/index.d.ts +3 -3
  298. package/dist/components/TimelineContainer/index.js +2 -0
  299. package/dist/components/TimelineNode/components/TimelineNode.d.ts +0 -1
  300. package/dist/components/TimelineNode/components/TimelineNode.js +49 -0
  301. package/dist/components/TimelineNode/components/index.d.ts +2 -2
  302. package/dist/components/TimelineNode/components/index.js +1 -0
  303. package/dist/components/TimelineNode/index.d.ts +2 -2
  304. package/dist/components/TimelineNode/index.js +1 -0
  305. package/dist/components/registry.js +247 -0
  306. package/dist/hooks/index.d.ts +4 -0
  307. package/dist/hooks/index.js +3 -0
  308. package/dist/hooks/useAnimatedDismiss.d.ts +15 -0
  309. package/dist/hooks/useAnimatedDismiss.js +29 -0
  310. package/dist/hooks/useTextScramble.d.ts +21 -0
  311. package/dist/hooks/useTextScramble.js +91 -0
  312. package/dist/hooks/useTextScramble.test.d.ts +1 -0
  313. package/dist/hooks/useThemePortal.d.ts +12 -0
  314. package/dist/hooks/useThemePortal.js +62 -0
  315. package/dist/hooks/useThemePortal.test.d.ts +1 -0
  316. package/dist/utils/cn.d.ts +9 -0
  317. package/dist/utils/cn.js +12 -0
  318. package/dist/utils/index.d.ts +2 -0
  319. package/dist/utils/index.js +2 -0
  320. package/dist/utils/isSafeHref.d.ts +1 -0
  321. package/dist/utils/isSafeHref.js +22 -0
  322. package/dist/utils/isSafeUrl.d.ts +8 -0
  323. package/dist/utils/isSafeUrl.js +18 -0
  324. package/dist/utils/isSafeUrl.test.d.ts +1 -0
  325. package/dist/utils/prefersReducedMotion.d.ts +5 -0
  326. package/dist/utils/prefersReducedMotion.js +8 -0
  327. package/package.json +9 -2
@@ -0,0 +1,46 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Button as AriaButton } from 'react-aria-components';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { useAnimatedDismiss } from '../../../hooks/useAnimatedDismiss.js';
6
+ const sizeClasses = {
7
+ sm: 'text-dos-text-xs px-2 py-0.5 min-h-6 gap-1',
8
+ md: 'text-dos-text-sm px-2.5 py-1 min-h-7 gap-1.5',
9
+ lg: 'text-dos-text-md px-3 py-1.5 min-h-8 gap-1.5',
10
+ small: 'text-dos-text-xs px-2 py-0.5 min-h-6 gap-1',
11
+ medium: 'text-dos-text-sm px-2.5 py-1 min-h-7 gap-1.5',
12
+ };
13
+ /**
14
+ * DOS-styled Tag for interactive labels with click, close, and selection.
15
+ * Extends Badge with interaction behaviors. Uses useAnimatedDismiss for phosphor exit.
16
+ */
17
+ export const Tag = ({ children, variant = 'default', size = 'md', color, selected = false, closeable = false, disabled = false, onClick, onClose, className, 'aria-label': ariaLabel, ...props }) => {
18
+ const isInteractive = !!onClick && !disabled;
19
+ const { isClosing, triggerClose, handleAnimationEnd } = useAnimatedDismiss('tag-exit', onClose);
20
+ const handleKeyDown = (event) => {
21
+ if (disabled)
22
+ return;
23
+ if ((event.key === 'Enter' || event.key === ' ') && onClick) {
24
+ event.preventDefault();
25
+ onClick(event);
26
+ }
27
+ if ((event.key === 'Delete' || event.key === 'Backspace') && closeable && onClose) {
28
+ event.preventDefault();
29
+ triggerClose();
30
+ }
31
+ };
32
+ // AriaButton's usePress isolates the press event — it calls stopPropagation
33
+ // internally, preventing the close from triggering the parent Tag's onClick.
34
+ const handleClosePress = () => {
35
+ if (!disabled)
36
+ triggerClose();
37
+ };
38
+ const style = color ? { '--tag-color': 'var(' + color + ')' } : undefined;
39
+ return (_jsxs("span", { className: cn('inline-flex items-center justify-center', 'font-dos leading-none whitespace-nowrap select-none', 'rounded-dos-sm uppercase tracking-wider', sizeClasses[size] || sizeClasses.md, 'eidotter-tag', 'eidotter-tag--' + variant, selected && 'eidotter-tag--selected', disabled && 'opacity-50 cursor-not-allowed', isClosing && 'eidotter-tag--closing', isInteractive && 'cursor-pointer', className), style: style, onClick: isInteractive ? (e) => !disabled && onClick?.(e) : undefined, onKeyDown: handleKeyDown, onAnimationEnd: handleAnimationEnd, "aria-label": ariaLabel, "aria-pressed": isInteractive ? (selected || false) : undefined, "aria-disabled": disabled || undefined, "data-variant": variant, ...(isInteractive ? { role: 'button', tabIndex: 0 } : {}), ...props, children: [_jsx("span", { className: "eidotter-tag__content", children: children }), closeable && (_jsx(AriaButton, { className: "eidotter-tag__close", "aria-label": 'Remove ' + (typeof children === 'string' ? children : 'tag'),
40
+ // Keep the close button out of the tab order only when the body is
41
+ // itself focusable (interactive) and already offers Delete/Backspace
42
+ // dismissal. For a closeable, non-interactive tag the body is not
43
+ // focusable, so the close button must stay tabbable to give keyboard
44
+ // users any dismiss path at all.
45
+ excludeFromTabOrder: isInteractive, onPress: handleClosePress, isDisabled: disabled, children: "[x]" }))] }));
46
+ };
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../../../utils/cn.js';
3
+ const gapClasses = {
4
+ tight: 'gap-1',
5
+ normal: 'gap-2',
6
+ loose: 'gap-3',
7
+ };
8
+ /**
9
+ * Wrapper for rendering multiple Tag components with consistent spacing
10
+ *
11
+ * Provides flex layout with configurable gap and optional wrapping.
12
+ */
13
+ export const TagGroup = ({ children, gap = 'normal', wrap = true, className, 'aria-label': ariaLabel, ...props }) => {
14
+ return (_jsx("div", { className: cn('flex', wrap ? 'flex-wrap' : 'flex-nowrap', gapClasses[gap] || gapClasses.normal, className), role: "group", "aria-label": ariaLabel, ...props, children: children }));
15
+ };
@@ -1,4 +1,4 @@
1
- export { Tag } from './Tag';
2
- export type { TagProps } from './Tag';
3
- export { TagGroup } from './TagGroup';
4
- export type { TagGroupProps } from './TagGroup';
1
+ export { Tag } from './Tag.js';
2
+ export type { TagProps } from './Tag.js';
3
+ export { TagGroup } from './TagGroup.js';
4
+ export type { TagGroupProps } from './TagGroup.js';
@@ -0,0 +1,2 @@
1
+ export { Tag } from './Tag.js';
2
+ export { TagGroup } from './TagGroup.js';
@@ -1,2 +1,2 @@
1
- export { Tag, TagGroup } from './components';
2
- export type { TagProps, TagGroupProps } from './components';
1
+ export { Tag, TagGroup } from './components/index.js';
2
+ export type { TagProps, TagGroupProps } from './components/index.js';
@@ -0,0 +1 @@
1
+ export { Tag, TagGroup } from './components/index.js';
@@ -1,6 +1,4 @@
1
1
  import React from 'react';
2
- import '../../../styles/keyframes.css';
3
- import './Terminal.css';
4
2
  export interface TerminalProps {
5
3
  /** Size variant. Use sm/md/lg — small/medium/large are @deprecated aliases. */
6
4
  size?: 'sm' | 'md' | 'lg' | 'small' | 'medium' | 'large';
@@ -0,0 +1,43 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useRef, useEffect } from 'react';
4
+ import { Button as AriaButton } from 'react-aria-components';
5
+ import { Icon } from '../../Icon/components/Icon.js';
6
+ export const Terminal = ({ size = 'md', title = 'MS-DOS Terminal', state = 'active', resizable = true, minimizable = false, maximizable = false, closeable = false, children, onMinimize, onMaximize, onClose, onFocus, className = '', autoFocus = false, }) => {
7
+ const sizeMap = { sm: 'small', md: 'medium', lg: 'large' };
8
+ const normalizedSize = sizeMap[size] ?? size;
9
+ const [windowState, setWindowState] = useState(state);
10
+ const [isMaximized, setIsMaximized] = useState(false);
11
+ const terminalRef = useRef(null);
12
+ useEffect(() => {
13
+ if (autoFocus && terminalRef.current) {
14
+ terminalRef.current.focus();
15
+ }
16
+ }, [autoFocus]);
17
+ const handleMinimize = () => {
18
+ setWindowState('minimized');
19
+ onMinimize?.();
20
+ };
21
+ const handleMaximize = () => {
22
+ setIsMaximized(!isMaximized);
23
+ onMaximize?.();
24
+ };
25
+ const handleClose = () => {
26
+ onClose?.();
27
+ };
28
+ const handleFocus = () => {
29
+ setWindowState('active');
30
+ onFocus?.();
31
+ };
32
+ const handleKeyDown = (event) => {
33
+ // Handle Alt+F4 for closing (DOS convention)
34
+ if (event.altKey && event.key === 'F4' && closeable) {
35
+ event.preventDefault();
36
+ handleClose();
37
+ }
38
+ };
39
+ if (windowState === 'minimized') {
40
+ return (_jsx(AriaButton, { className: `terminal terminal--minimized ${className}`.trim(), onPress: handleFocus, "aria-label": `Restore ${title} window`, children: _jsxs("div", { className: "terminal__taskbar-item", children: [_jsx(Icon, { name: "App", size: "S" }), _jsx("span", { className: "terminal__taskbar-title", children: title })] }) }));
41
+ }
42
+ return (_jsxs("div", { ref: terminalRef, className: `terminal terminal--${normalizedSize} terminal--${windowState} ${isMaximized ? 'terminal--maximized' : ''} ${className}`.trim(), tabIndex: 0, role: "dialog", "aria-label": `${title} terminal window`, onFocus: handleFocus, onKeyDown: handleKeyDown, children: [_jsxs("div", { className: "terminal__title-bar", children: [_jsxs("div", { className: "terminal__title", children: [_jsx(Icon, { name: "App", size: "S", "aria-hidden": "true" }), _jsx("span", { className: "terminal__title-text", children: title })] }), (minimizable || maximizable || closeable) && (_jsxs("div", { className: "terminal__controls", children: [minimizable && (_jsx(AriaButton, { className: "terminal__control terminal__control--minimize", onPress: handleMinimize, "aria-label": "Minimize window", children: _jsx(Icon, { name: "Cancel", size: "S" }) })), maximizable && (_jsx(AriaButton, { className: "terminal__control terminal__control--maximize", onPress: handleMaximize, "aria-label": isMaximized ? "Restore window" : "Maximize window", children: _jsx(Icon, { name: isMaximized ? "Fullscreen" : "Add", size: "S" }) })), closeable && (_jsx(AriaButton, { className: "terminal__control terminal__control--close", onPress: handleClose, "aria-label": "Close window", children: _jsx(Icon, { name: "Close", size: "S" }) }))] }))] }), _jsx("div", { className: "terminal__content", role: "main", children: children || (_jsx("div", { className: "terminal__default-content", children: _jsxs("div", { className: "terminal__prompt", children: [_jsx("span", { className: "terminal__path", children: "C:\\\\>" }), _jsx("span", { className: "terminal__cursor", children: "\u2588" })] }) })) }), resizable && (_jsx("div", { className: "terminal__resize-handle", "aria-hidden": "true" }))] }));
43
+ };
@@ -1,2 +1,2 @@
1
- export { Terminal } from './Terminal';
2
- export type { TerminalProps } from './Terminal';
1
+ export { Terminal } from './Terminal.js';
2
+ export type { TerminalProps } from './Terminal.js';
@@ -0,0 +1 @@
1
+ export { Terminal } from './Terminal.js';
@@ -1 +1 @@
1
- export * from './components';
1
+ export * from './components/index.js';
@@ -0,0 +1 @@
1
+ export * from './components/index.js';
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import './TextScramble.css';
3
2
  export interface TextScrambleProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'children'> {
4
3
  /** The text to display with scramble effect */
5
4
  children: string;
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { forwardRef } from 'react';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { useTextScramble } from '../../../hooks/useTextScramble.js';
6
+ export const TextScramble = forwardRef(({ children, speed, characters, delay, className, ...props }, ref) => {
7
+ const { text, isScrambling } = useTextScramble(children, {
8
+ speed,
9
+ characters,
10
+ delay,
11
+ });
12
+ return (_jsx("span", { ref: ref, className: cn('eidotter-text-scramble', isScrambling && 'eidotter-text-scramble--scrambling', className), ...props, children: text }));
13
+ });
14
+ TextScramble.displayName = 'TextScramble';
@@ -1,2 +1,2 @@
1
- export { TextScramble } from './TextScramble';
2
- export type { TextScrambleProps } from './TextScramble';
1
+ export { TextScramble } from './TextScramble.js';
2
+ export type { TextScrambleProps } from './TextScramble.js';
@@ -0,0 +1 @@
1
+ export { TextScramble } from './TextScramble.js';
@@ -1 +1 @@
1
- export * from './components';
1
+ export * from './components/index.js';
@@ -0,0 +1 @@
1
+ export * from './components/index.js';
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
- import type { TimelineEntryData, TimelineRenderEntry } from './types';
3
- import '../../../styles/keyframes.css';
4
- import './MasterDetailLayout.css';
2
+ import type { TimelineEntryData, TimelineRenderEntry } from './types.js';
5
3
  export interface MasterDetailLayoutProps {
6
4
  /** Pre-sorted entries (the orchestrator sorts; the rail renders in order). */
7
5
  entries: TimelineEntryData[];
@@ -0,0 +1,77 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef } from 'react';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { TimelineNode } from '../../TimelineNode/components/TimelineNode.js';
6
+ import { TimelineEntryCard } from './TimelineEntryCard.js';
7
+ /**
8
+ * Level-3 "master-detail" timeline layout (DMNC-878).
9
+ *
10
+ * A split view: a navigable timeline rail (left) plus a detail pane (right)
11
+ * that renders the selected entry in full. Switching entries swaps the detail
12
+ * pane without remounting the rail, so rail scroll position is preserved.
13
+ *
14
+ * Selection is owned by the orchestrator (controlled/uncontrolled via
15
+ * `useSelection`), so the open entry is deep-linkable by consumers. The rail
16
+ * uses navigation semantics (`aria-current`), arrow keys walk entries, and
17
+ * focus moves into the pane on open and back to the rail on close — without a
18
+ * focus trap, so both panes stay reachable.
19
+ */
20
+ export const MasterDetailLayout = ({ entries, selectedEntryId, onSelect, onDeselect, renderEntry, formatDate, }) => {
21
+ const selectedEntry = entries.find((e) => e.id === selectedEntryId) ?? null;
22
+ const railRef = useRef(null);
23
+ const detailRef = useRef(null);
24
+ // Focus management (DMNC-878): move focus into the detail pane when an entry
25
+ // opens, and restore focus to the active rail row when it closes. Mirrors the
26
+ // requestAnimationFrame focus-move pattern used for drill-down transitions.
27
+ const prevSelectedRef = useRef(selectedEntryId);
28
+ useEffect(() => {
29
+ const prev = prevSelectedRef.current;
30
+ prevSelectedRef.current = selectedEntryId;
31
+ if (selectedEntryId && selectedEntryId !== prev) {
32
+ requestAnimationFrame(() => detailRef.current?.focus());
33
+ }
34
+ else if (!selectedEntryId && prev) {
35
+ requestAnimationFrame(() => {
36
+ railRef.current
37
+ ?.querySelector(`[data-entry-id="${prev}"]`)
38
+ ?.focus();
39
+ });
40
+ }
41
+ }, [selectedEntryId]);
42
+ // Arrow-key walk over the flat sorted entries (mirrors CmdPalette). With
43
+ // nothing selected, ArrowDown opens the first entry and ArrowUp the last.
44
+ const moveSelection = useCallback((delta) => {
45
+ if (entries.length === 0)
46
+ return;
47
+ const idx = entries.findIndex((e) => e.id === selectedEntryId);
48
+ const base = idx === -1 ? (delta > 0 ? -1 : entries.length) : idx;
49
+ const next = Math.max(0, Math.min(entries.length - 1, base + delta));
50
+ onSelect(entries[next].id);
51
+ }, [entries, selectedEntryId, onSelect]);
52
+ const handleRailKeyDown = useCallback((e) => {
53
+ if (e.key === 'ArrowDown') {
54
+ e.preventDefault();
55
+ moveSelection(1);
56
+ }
57
+ else if (e.key === 'ArrowUp') {
58
+ e.preventDefault();
59
+ moveSelection(-1);
60
+ }
61
+ else if (e.key === 'Escape' && selectedEntryId) {
62
+ e.preventDefault();
63
+ onDeselect();
64
+ }
65
+ }, [moveSelection, onDeselect, selectedEntryId]);
66
+ const renderDetailBody = (entry) => {
67
+ const defaultRender = () => (_jsx(TimelineEntryCard, { entry: entry, isSelected: true, isExpanded: true }));
68
+ return renderEntry
69
+ ? renderEntry(entry, { isExpanded: true, isSelected: true, defaultRender })
70
+ : defaultRender();
71
+ };
72
+ return (_jsxs("div", { className: cn('eidotter-timeline-md', selectedEntry && 'eidotter-timeline-md--detail-open'), children: [_jsx("nav", { ref: railRef, className: "eidotter-timeline-md__rail", "aria-label": "Timeline entries", onKeyDown: handleRailKeyDown, children: _jsx("ul", { className: "eidotter-timeline-md__rail-list", children: entries.map((entry) => {
73
+ const isActive = entry.id === selectedEntryId;
74
+ return (_jsx("li", { className: "eidotter-timeline-md__rail-item", children: _jsxs("button", { type: "button", "data-entry-id": entry.id, className: cn('eidotter-timeline-md__rail-button', isActive && 'eidotter-timeline-md__rail-button--active'), "aria-current": isActive ? 'true' : undefined, onClick: () => onSelect(entry.id), children: [_jsx("span", { className: "eidotter-timeline-md__rail-node", "aria-hidden": "true", children: _jsx(TimelineNode, { shape: "circle", size: "sm", variant: "default", isActive: isActive }) }), _jsxs("span", { className: "eidotter-timeline-md__rail-text", children: [_jsx("span", { className: "eidotter-timeline-md__rail-date", children: formatDate(entry.date) }), _jsx("span", { className: "eidotter-timeline-md__rail-title", children: entry.title })] })] }) }, entry.id));
75
+ }) }) }), _jsx("div", { ref: detailRef, className: "eidotter-timeline-md__detail", role: "region", "aria-label": "Entry detail", tabIndex: -1, children: selectedEntry ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "eidotter-timeline-md__back", onClick: onDeselect, children: "\u2039 BACK" }), _jsx("div", { className: "eidotter-timeline-md__detail-body", children: renderDetailBody(selectedEntry) }, selectedEntry.id)] })) : (_jsxs("div", { className: "eidotter-timeline-md__detail-empty", role: "status", children: [_jsx("p", { children: "C:\\TIMELINE> Select an entry to read." }), _jsx("p", { children: "_" })] })) })] }));
76
+ };
77
+ MasterDetailLayout.displayName = 'MasterDetailLayout';
@@ -1,5 +1,4 @@
1
1
  import React from 'react';
2
- import './TimelineAxis.css';
3
2
  export interface TimelineAxisProps {
4
3
  children: React.ReactNode;
5
4
  }
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Vertical spine connecting timeline nodes.
4
+ * Wraps content with a continuous vertical line on the left side.
5
+ */
6
+ export const TimelineAxis = ({ children }) => {
7
+ return (_jsxs("div", { className: "timeline-axis", children: [_jsx("div", { className: "timeline-axis__line", "aria-hidden": "true" }), _jsx("div", { className: "timeline-axis__content", children: children })] }));
8
+ };
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
- import type { TimelineEntryData, TimelineRenderEntry, ZoomLevel } from './types';
3
- import './TimelineContainer.css';
4
- import './views/views.css';
2
+ import type { TimelineEntryData, TimelineRenderEntry, ZoomLevel } from './types.js';
5
3
  export interface TimelineContainerProps extends React.HTMLAttributes<HTMLDivElement> {
6
4
  /** Timeline entries to display */
7
5
  entries: TimelineEntryData[];
@@ -0,0 +1,218 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { cn } from '../../../utils/cn.js';
5
+ import { useDrillDown } from './useDrillDown.js';
6
+ import { useSelection } from './useSelection.js';
7
+ import { groupEntriesByZoom, filterBucketsByPeriod } from './timelineUtils.js';
8
+ import { ZoomControls } from './ZoomControls.js';
9
+ import { TimelineAxis } from './TimelineAxis.js';
10
+ import { TimelineContent } from './TimelineContent.js';
11
+ import { TimelineNode } from '../../TimelineNode/components/TimelineNode.js';
12
+ import { TimelineEntryCard } from './TimelineEntryCard.js';
13
+ import { MasterDetailLayout } from './MasterDetailLayout.js';
14
+ /**
15
+ * TimelineContainer - Interactive multi-level zoom timeline
16
+ *
17
+ * A composite timeline component with 4 zoom levels (year, month, day, hour),
18
+ * entry selection, keyboard shortcuts, scroll-to-zoom, and drill-down navigation.
19
+ * Uses DOS/CGA aesthetic with eidotter primitives.
20
+ *
21
+ * Supports both controlled and uncontrolled patterns for zoom and selection.
22
+ * When zoom is uncontrolled, clicking a bucket drills down into that time period.
23
+ */
24
+ export const TimelineContainer = ({ entries, mode = 'interactive', zoomLevel: controlledZoom, defaultZoomLevel = 'month', onZoomChange, selectedEntryId: controlledSelection, defaultSelectedEntryId, onSelectEntry, sortOrder = 'desc', scrollToZoom = true, keyboardShortcuts = true, renderEntry, pageSize = 10, onLoadMore, ...props }) => {
25
+ const isStatic = mode === 'static';
26
+ const isFeed = mode === 'feed';
27
+ const isVerticalFeed = isStatic || isFeed;
28
+ // Master-detail (DMNC-878) is NOT a vertical feed — it has its own split
29
+ // layout, sort path, focus model, and keyboard handling — but it shares the
30
+ // feed modes' suppression of zoom controls / scroll-to-zoom / drill-down.
31
+ // Each `isVerticalFeed` gate is audited individually rather than widened.
32
+ const isMasterDetail = mode === 'master-detail';
33
+ const containerRef = useRef(null);
34
+ const contentRef = useRef(null);
35
+ // Drill-down announcement for screen readers
36
+ const [announcement, setAnnouncement] = useState('');
37
+ const { zoomLevel, canZoomIn, canZoomOut, breadcrumbs, currentPeriod, isDrillDownEnabled, drillDown, drillUp, reset, } = useDrillDown({
38
+ zoomLevel: controlledZoom,
39
+ defaultZoomLevel,
40
+ onZoomChange,
41
+ });
42
+ const { selectedEntryId, select, toggle, deselect, } = useSelection({
43
+ selectedEntryId: controlledSelection,
44
+ defaultSelectedEntryId,
45
+ onSelectEntry,
46
+ });
47
+ // Group entries by zoom level, then filter by drill-down period
48
+ const allBuckets = useMemo(() => groupEntriesByZoom(entries, zoomLevel, sortOrder), [entries, zoomLevel, sortOrder]);
49
+ // Determine parent zoom level for filtering
50
+ const parentZoomLevel = breadcrumbs.length > 0
51
+ ? ['year', 'month', 'day', 'hour'][breadcrumbs.length - 1]
52
+ : null;
53
+ const buckets = useMemo(() => currentPeriod && parentZoomLevel
54
+ ? filterBucketsByPeriod(allBuckets, currentPeriod, parentZoomLevel)
55
+ : allBuckets, [allBuckets, currentPeriod, parentZoomLevel]);
56
+ // Drill into a specific bucket — shared by bucket clicks, the [+] zoom-in
57
+ // control, and the Ctrl/Cmd+= shortcut.
58
+ const drillIntoBucket = useCallback((bucket) => {
59
+ if (!canZoomIn)
60
+ return;
61
+ drillDown(bucket.periodStart, bucket.label);
62
+ setAnnouncement(`Showing ${zoomLevel === 'year' ? 'months' : zoomLevel === 'month' ? 'days' : 'hours'} in ${bucket.label}`);
63
+ }, [canZoomIn, drillDown, zoomLevel]);
64
+ // Generic zoom-in has no inherent target (unlike drill-up, which pops the
65
+ // stack), so the [+] control and Ctrl/Cmd+= drill into the first bucket of the
66
+ // current view — the most recent period under the default sortOrder. Clicking
67
+ // a specific bucket still drills into that exact period.
68
+ const handleZoomIn = useCallback(() => {
69
+ if (buckets.length > 0)
70
+ drillIntoBucket(buckets[0]);
71
+ }, [buckets, drillIntoBucket]);
72
+ // Scroll-to-zoom: Ctrl/Cmd + wheel (interactive mode only)
73
+ useEffect(() => {
74
+ if (isVerticalFeed || isMasterDetail || !scrollToZoom)
75
+ return;
76
+ const el = containerRef.current;
77
+ if (!el)
78
+ return;
79
+ let rafId = null;
80
+ const handleWheel = (e) => {
81
+ if (!e.ctrlKey && !e.metaKey)
82
+ return;
83
+ e.preventDefault();
84
+ if (rafId !== null)
85
+ return;
86
+ rafId = requestAnimationFrame(() => {
87
+ // Scroll-down zooms out (pops drill-down stack).
88
+ // Scroll-up is a no-op — drill-down requires clicking a specific
89
+ // bucket to know which period to enter. Scroll has no period context.
90
+ if (e.deltaY > 0) {
91
+ drillUp();
92
+ }
93
+ rafId = null;
94
+ });
95
+ };
96
+ el.addEventListener('wheel', handleWheel, { passive: false });
97
+ return () => {
98
+ el.removeEventListener('wheel', handleWheel);
99
+ if (rafId !== null)
100
+ cancelAnimationFrame(rafId);
101
+ };
102
+ }, [isVerticalFeed, isMasterDetail, scrollToZoom, isDrillDownEnabled, canZoomIn, drillUp]);
103
+ // Keyboard shortcuts (interactive mode only). Master-detail has its own
104
+ // keyboard model (arrow-walk + Escape) inside MasterDetailLayout, so the
105
+ // zoom-shortcut handler is suppressed for it as well.
106
+ useEffect(() => {
107
+ if (isVerticalFeed || isMasterDetail || !keyboardShortcuts)
108
+ return;
109
+ const handleKeyDown = (e) => {
110
+ if (!containerRef.current?.contains(document.activeElement) &&
111
+ document.activeElement !== containerRef.current) {
112
+ return;
113
+ }
114
+ const isMod = e.ctrlKey || e.metaKey;
115
+ if (isMod && (e.key === '=' || e.key === '+')) {
116
+ e.preventDefault();
117
+ handleZoomIn();
118
+ }
119
+ else if (isMod && e.key === '-') {
120
+ e.preventDefault();
121
+ drillUp();
122
+ }
123
+ else if (isMod && e.key === '0') {
124
+ e.preventDefault();
125
+ reset();
126
+ setAnnouncement('Showing all years');
127
+ }
128
+ else if (e.key === 'Escape') {
129
+ e.preventDefault();
130
+ deselect();
131
+ }
132
+ };
133
+ document.addEventListener('keydown', handleKeyDown);
134
+ return () => document.removeEventListener('keydown', handleKeyDown);
135
+ }, [isVerticalFeed, isMasterDetail, keyboardShortcuts, handleZoomIn, drillUp, reset, deselect]);
136
+ // Bucket click triggers drill-down
137
+ const handleBucketClick = useCallback((bucket) => drillIntoBucket(bucket), [drillIntoBucket]);
138
+ // Focus management: move focus after drill-down transitions
139
+ const prevBreadcrumbLengthRef = useRef(breadcrumbs.length);
140
+ useEffect(() => {
141
+ if (breadcrumbs.length !== prevBreadcrumbLengthRef.current) {
142
+ prevBreadcrumbLengthRef.current = breadcrumbs.length;
143
+ // Focus first interactive element in the content area
144
+ const firstTrigger = contentRef.current?.querySelector('.eidotter-timeline-card__trigger, .timeline-view__bucket-button, .eidotter-timeline-node[role="button"]');
145
+ if (firstTrigger) {
146
+ requestAnimationFrame(() => firstTrigger.focus());
147
+ }
148
+ }
149
+ }, [breadcrumbs.length]);
150
+ // Sort entries for the flat-list modes (static, feed, and master-detail —
151
+ // the rail and arrow-walk need a stable sorted order).
152
+ const sortedEntries = useMemo(() => {
153
+ if (!isVerticalFeed && !isMasterDetail)
154
+ return entries;
155
+ return [...entries].sort((a, b) => {
156
+ const cmp = a.date.localeCompare(b.date);
157
+ return sortOrder === 'desc' ? -cmp : cmp;
158
+ });
159
+ }, [isVerticalFeed, isMasterDetail, entries, sortOrder]);
160
+ // Pagination for feed mode. Clamp pageSize at the boundary — pageSize=0 or
161
+ // negative would otherwise produce a permanent LOAD MORE button that never
162
+ // advances. Step is always ≥1.
163
+ const safePageSize = Math.max(1, pageSize);
164
+ const [visibleCount, setVisibleCount] = useState(safePageSize);
165
+ // Clamp visibleCount when entries shrink. Crucially, do NOT reset to
166
+ // pageSize when entries grow — consumers using `onLoadMore` to fetch and
167
+ // append the next batch (the documented backend-pagination flow) need
168
+ // visibleCount to stay where the user clicked. Reset to pageSize only
169
+ // happens when the new entries length is below the current visibleCount.
170
+ useEffect(() => {
171
+ if (!isFeed)
172
+ return;
173
+ setVisibleCount((prev) => {
174
+ const max = sortedEntries.length;
175
+ if (max === 0)
176
+ return safePageSize;
177
+ if (prev > max)
178
+ return Math.max(safePageSize, max);
179
+ return prev;
180
+ });
181
+ }, [isFeed, safePageSize, sortedEntries.length]);
182
+ const visibleEntries = useMemo(() => (isFeed ? sortedEntries.slice(0, visibleCount) : sortedEntries), [isFeed, sortedEntries, visibleCount]);
183
+ const hasMore = isFeed && visibleCount < sortedEntries.length;
184
+ const handleLoadMore = useCallback(() => {
185
+ const next = Math.min(visibleCount + safePageSize, sortedEntries.length);
186
+ if (next <= visibleCount)
187
+ return; // already at cap — no-op, no callback
188
+ setVisibleCount(next);
189
+ onLoadMore?.(next);
190
+ }, [visibleCount, safePageSize, sortedEntries.length, onLoadMore]);
191
+ const dateFormatter = useMemo(() => new Intl.DateTimeFormat('en-US', { year: 'numeric', month: 'short', day: 'numeric' }), []);
192
+ const formatDate = (iso) => {
193
+ try {
194
+ return dateFormatter.format(new Date(iso));
195
+ }
196
+ catch {
197
+ return iso;
198
+ }
199
+ };
200
+ return (_jsxs("div", { ref: containerRef, ...props, className: cn('font-dos text-dos-text-brand p-4 min-h-[200px]', 'eidotter-timeline-container', isStatic && 'eidotter-timeline-container--static', isFeed && 'eidotter-timeline-container--feed', isMasterDetail && 'eidotter-timeline-container--master-detail', props.className), role: "region", "aria-label": props['aria-label'] ?? 'Timeline', tabIndex: isVerticalFeed || isMasterDetail ? undefined : 0, children: [_jsx("div", { className: "sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement }), !isVerticalFeed && !isMasterDetail && (_jsx(ZoomControls, { zoomLevel: zoomLevel, canZoomIn: canZoomIn, canZoomOut: canZoomOut, onZoomIn: handleZoomIn, onZoomOut: drillUp, onReset: reset, breadcrumbs: isDrillDownEnabled ? breadcrumbs : [], onBreadcrumbClick: (index) => {
201
+ // Navigate to a specific breadcrumb level
202
+ const stepsBack = breadcrumbs.length - 1 - index;
203
+ for (let i = 0; i < stepsBack; i++) {
204
+ drillUp();
205
+ }
206
+ } })), _jsx("div", { ref: contentRef, children: entries.length === 0 ? (_jsxs("div", { className: "eidotter-timeline-container__empty", role: "status", children: [_jsx("p", { children: "C:\\TIMELINE> No entries found." }), _jsx("p", { children: "_" })] })) : isStatic ? (_jsx(TimelineAxis, { children: _jsx("div", { className: "eidotter-timeline-container__static", role: "list", "aria-label": "Timeline", children: sortedEntries.map((entry) => {
207
+ const defaultRender = () => (_jsx(TimelineEntryCard, { entry: entry, isSelected: false, isExpanded: true }));
208
+ return (_jsxs("div", { className: "eidotter-timeline-container__static-entry", role: "listitem", children: [_jsx("div", { className: "timeline-view__node", children: _jsx(TimelineNode, { shape: "circle", size: "medium", variant: "default", label: formatDate(entry.date), labelPosition: "right" }) }), renderEntry
209
+ ? renderEntry(entry, { isExpanded: true, isSelected: false, defaultRender })
210
+ : defaultRender()] }, entry.id));
211
+ }) }) })) : isFeed ? (_jsxs(TimelineAxis, { children: [_jsx("div", { className: "eidotter-timeline-container__feed", role: "list", "aria-label": "Timeline", children: visibleEntries.map((entry) => {
212
+ const isSelected = selectedEntryId === entry.id;
213
+ const defaultRender = () => (_jsx(TimelineEntryCard, { entry: entry, isSelected: isSelected, isExpanded: isSelected, onSelect: toggle }));
214
+ return (_jsxs("div", { className: "eidotter-timeline-container__feed-entry", role: "listitem", children: [_jsx("div", { className: "timeline-view__node", children: _jsx(TimelineNode, { shape: "circle", size: "medium", variant: "default", label: formatDate(entry.date), labelPosition: "right" }) }), renderEntry
215
+ ? renderEntry(entry, { isExpanded: isSelected, isSelected, defaultRender })
216
+ : defaultRender()] }, entry.id));
217
+ }) }), hasMore && (_jsx("button", { type: "button", className: "eidotter-timeline-container__load-more", onClick: handleLoadMore, "aria-label": `Load more entries (showing ${visibleCount} of ${sortedEntries.length})`, children: "LOAD MORE..." }))] })) : isMasterDetail ? (_jsx(MasterDetailLayout, { entries: sortedEntries, selectedEntryId: selectedEntryId, onSelect: select, onDeselect: deselect, renderEntry: renderEntry, formatDate: formatDate })) : buckets.length === 0 && currentPeriod ? (_jsx(TimelineAxis, { children: _jsxs("div", { className: "eidotter-timeline-container__empty", role: "status", children: [_jsxs("p", { children: ["C:\\TIMELINE> No entries in ", breadcrumbs[breadcrumbs.length - 1]?.label ?? 'this period', "."] }), _jsx("p", { children: "_" })] }) })) : (_jsx(TimelineAxis, { children: _jsx(TimelineContent, { zoomLevel: zoomLevel, buckets: buckets, selectedEntryId: selectedEntryId, onEntrySelect: toggle, onBucketClick: handleBucketClick, renderEntry: renderEntry }) })) })] }));
218
+ };
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { ZoomLevel, DateBucket, TimelineRenderEntry } from './types';
2
+ import type { ZoomLevel, DateBucket, TimelineRenderEntry } from './types.js';
3
3
  export interface TimelineContentProps {
4
4
  zoomLevel: ZoomLevel;
5
5
  buckets: readonly DateBucket[];
@@ -0,0 +1,23 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { YearView, MonthView, DayView, HourView } from './views/index.js';
4
+ /**
5
+ * View switcher that renders the appropriate zoom level view.
6
+ */
7
+ export const TimelineContent = ({ zoomLevel, buckets, selectedEntryId, onEntrySelect, onBucketClick, renderEntry, }) => {
8
+ switch (zoomLevel) {
9
+ case 'year':
10
+ // Year view shows bucket counts, not individual entries — renderEntry not applicable.
11
+ return _jsx(YearView, { buckets: buckets, onBucketClick: onBucketClick });
12
+ case 'month':
13
+ return (_jsx(MonthView, { buckets: buckets, selectedEntryId: selectedEntryId, onEntrySelect: onEntrySelect, onBucketClick: onBucketClick, renderEntry: renderEntry }));
14
+ case 'day':
15
+ return (_jsx(DayView, { buckets: buckets, selectedEntryId: selectedEntryId, onEntrySelect: onEntrySelect, renderEntry: renderEntry }));
16
+ case 'hour':
17
+ return (_jsx(HourView, { buckets: buckets, selectedEntryId: selectedEntryId, onEntrySelect: onEntrySelect, renderEntry: renderEntry }));
18
+ default: {
19
+ const _exhaustive = zoomLevel;
20
+ return _exhaustive;
21
+ }
22
+ }
23
+ };
@@ -1,6 +1,5 @@
1
1
  import React from 'react';
2
- import type { TimelineEntryData } from './types';
3
- import './TimelineEntryCard.css';
2
+ import type { TimelineEntryData } from './types.js';
4
3
  export interface TimelineEntryCardProps {
5
4
  entry: TimelineEntryData;
6
5
  isSelected: boolean;