@celestia-island/hikari 0.4.4 → 0.30.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 (385) hide show
  1. package/README.md +1 -1
  2. package/package.json +22 -7
  3. package/src/DemoApp.tsx +491 -0
  4. package/src/animation/registerAnimations.test.ts +94 -0
  5. package/src/animation/registerAnimations.ts +103 -0
  6. package/src/components/HkAboutModal.scss +95 -0
  7. package/src/components/HkAboutModal.tsx +114 -0
  8. package/src/components/HkAdaptiveDialog.scss +22 -0
  9. package/src/components/HkAdaptiveDialog.test.tsx +271 -0
  10. package/src/components/HkAdaptiveDialog.tsx +167 -0
  11. package/src/components/HkAdminHeader.test.tsx +185 -0
  12. package/src/components/HkAdminHeader.tsx +308 -0
  13. package/src/components/HkAdminShell.test.tsx +237 -0
  14. package/src/components/HkAdminShell.tsx +129 -0
  15. package/src/components/HkAdminTablePage.tsx +70 -0
  16. package/src/components/HkAdminWidgets.test.ts +89 -0
  17. package/src/components/HkAlert.tsx +1 -1
  18. package/src/components/HkAltSignIn.scss +55 -0
  19. package/src/components/HkAltSignIn.test.tsx +88 -0
  20. package/src/components/HkAltSignIn.tsx +92 -0
  21. package/src/components/HkAttachmentModal.scss +49 -0
  22. package/src/components/HkAttachmentModal.tsx +318 -0
  23. package/src/components/HkAuthCard.tsx +50 -0
  24. package/src/components/HkAuthSubmitButton.scss +12 -0
  25. package/src/components/HkAuthSubmitButton.tsx +94 -0
  26. package/src/components/HkAvatar.scss +93 -0
  27. package/src/components/HkAvatar.tsx +2 -2
  28. package/src/components/HkBlockingToast.scss +123 -0
  29. package/src/components/HkBlockingToast.test.tsx +221 -0
  30. package/src/components/HkBlockingToast.tsx +182 -0
  31. package/src/components/HkBreadcrumb.scss +624 -0
  32. package/src/components/HkBreadcrumb.tsx +2 -2
  33. package/src/components/HkButton.scss +16 -2
  34. package/src/components/HkCaptchaModal.scss +14 -0
  35. package/src/components/HkCaptchaModal.tsx +62 -0
  36. package/src/components/HkCaptchaWidget.scss +30 -0
  37. package/src/components/HkCaptchaWidget.tsx +181 -0
  38. package/src/components/HkCard.scss +1 -1
  39. package/src/components/HkChatTypes.ts +65 -0
  40. package/src/components/HkCheckbox.scss +4 -9
  41. package/src/components/HkCheckbox.tsx +18 -6
  42. package/src/components/HkColorPicker.scss +120 -3
  43. package/src/components/HkColorPicker.tsx +130 -19
  44. package/src/components/HkColorPickerTouch.test.tsx +145 -0
  45. package/src/components/HkColorSchemeDialog.scss +52 -0
  46. package/src/components/HkColorSchemeDialog.test.ts +201 -0
  47. package/src/components/HkColorSchemeDialog.tsx +96 -0
  48. package/src/components/HkColorSchemeEditor.test.ts +270 -0
  49. package/src/components/HkColorSchemeEditor.tsx +410 -0
  50. package/src/components/HkConfirmDialog.scss +43 -3
  51. package/src/components/HkConfirmDialog.tsx +7 -4
  52. package/src/components/HkConnectionInfo.ts +80 -0
  53. package/src/components/HkConnectionStatus.tsx +158 -0
  54. package/src/components/HkCookieConsent.scss +22 -0
  55. package/src/components/HkCookieConsent.test.tsx +70 -0
  56. package/src/components/HkCookieConsent.tsx +56 -0
  57. package/src/components/HkCountdownDigit.tsx +105 -0
  58. package/src/components/HkDatePicker.scss +361 -0
  59. package/src/components/HkDatePicker.test.ts +459 -0
  60. package/src/components/HkDatePicker.tsx +568 -0
  61. package/src/components/HkDateTimePicker.scss +65 -37
  62. package/src/components/HkDateTimePicker.test.ts +344 -0
  63. package/src/components/HkDateTimePicker.tsx +249 -82
  64. package/src/components/HkDraggableGrid.scss +4 -1
  65. package/src/components/HkDraggableList.scss +4 -1
  66. package/src/components/HkDrawer.scss +27 -13
  67. package/src/components/HkDrawer.test.tsx +73 -0
  68. package/src/components/HkDrawer.tsx +157 -10
  69. package/src/components/HkErrorBoundary.scss +5 -2
  70. package/src/components/HkErrorBoundary.tsx +7 -4
  71. package/src/components/HkExpansionPanel.scss +143 -0
  72. package/src/components/HkExpansionPanel.test.tsx +109 -0
  73. package/src/components/HkExpansionPanel.tsx +101 -0
  74. package/src/components/HkFab.scss +162 -0
  75. package/src/components/HkFab.test.tsx +217 -0
  76. package/src/components/HkFab.tsx +214 -0
  77. package/src/components/HkHoverRevealAction.scss +6 -0
  78. package/src/components/HkHoverRevealAction.test.ts +68 -0
  79. package/src/components/HkHoverRevealAction.tsx +84 -6
  80. package/src/components/HkIcon.scss +123 -0
  81. package/src/components/HkIcon.tsx +7 -23
  82. package/src/components/HkIconButton.scss +170 -0
  83. package/src/components/HkIconButton.test.tsx +75 -0
  84. package/src/components/HkIconButton.tsx +4 -3
  85. package/src/components/HkIconButtonVars.scss +180 -0
  86. package/src/components/HkInput.affix.test.ts +253 -0
  87. package/src/components/HkInput.enter.test.ts +88 -0
  88. package/src/components/HkInput.scss +127 -8
  89. package/src/components/HkInput.tsx +245 -13
  90. package/src/components/HkKeywordSearchModal.scss +13 -0
  91. package/src/components/HkKeywordSearchModal.tsx +46 -9
  92. package/src/components/HkLabel.scss +34 -0
  93. package/src/components/HkLabel.test.tsx +61 -0
  94. package/src/components/HkLabel.tsx +45 -0
  95. package/src/components/HkListTransition.scss +26 -0
  96. package/src/components/HkListTransition.tsx +57 -0
  97. package/src/components/HkLocalePicker.scss +37 -0
  98. package/src/components/HkLocalePicker.tsx +133 -0
  99. package/src/components/HkLocalePickerPopup.tsx +35 -19
  100. package/src/components/HkLocalizedInput.scss +277 -0
  101. package/src/components/HkLocalizedInput.test.tsx +693 -0
  102. package/src/components/HkLocalizedInput.tsx +495 -0
  103. package/src/components/HkLogWindow.scss +121 -0
  104. package/src/components/HkLogWindow.tsx +195 -0
  105. package/src/components/HkLogo.scss +1 -1
  106. package/src/components/HkLogo.tsx +1 -1
  107. package/src/components/HkMarkdownRenderer.scss +22 -2
  108. package/src/components/HkMarkdownRenderer.tsx +33 -3
  109. package/src/components/HkMediaControlBar.tsx +20 -13
  110. package/src/components/HkMenu.scss +200 -0
  111. package/src/components/HkMenu.test.ts +779 -0
  112. package/src/components/HkMenu.tsx +770 -0
  113. package/src/components/HkMenuActionItem.scss +58 -0
  114. package/src/components/HkMenuActionItem.tsx +41 -0
  115. package/src/components/HkMenuIdentityItem.scss +70 -0
  116. package/src/components/HkMenuIdentityItem.tsx +42 -0
  117. package/src/components/HkMenuPanel.scss +20 -0
  118. package/src/components/HkMenuPanel.test.tsx +118 -0
  119. package/src/components/HkMenuPanel.tsx +32 -0
  120. package/src/components/HkMinimap.tsx +16 -8
  121. package/src/components/HkModal.autofollow.test.ts +44 -0
  122. package/src/components/HkModal.scss +170 -53
  123. package/src/components/HkModal.sheetgap.test.ts +82 -0
  124. package/src/components/HkModal.test.tsx +334 -0
  125. package/src/components/HkModal.tsx +314 -68
  126. package/src/components/HkModalBreadcrumb.scss +4 -1
  127. package/src/components/HkModalBreadcrumb.test.tsx +111 -0
  128. package/src/components/HkModalBreadcrumb.tsx +34 -11
  129. package/src/components/HkModelCatalog.ts +98 -0
  130. package/src/components/HkModelTag.scss +130 -0
  131. package/src/components/HkModelTag.test.tsx +111 -0
  132. package/src/components/HkModelTag.tsx +190 -0
  133. package/src/components/HkNavItem.scss +13 -2
  134. package/src/components/HkNavItem.tsx +2 -1
  135. package/src/components/HkNavSidebar.tsx +99 -0
  136. package/src/components/HkNumberInput.scss +12 -0
  137. package/src/components/HkNumberInput.tsx +15 -8
  138. package/src/components/HkPageHeader.scss +79 -0
  139. package/src/components/HkPageHeader.test.ts +67 -0
  140. package/src/components/HkPageHeader.tsx +51 -0
  141. package/src/components/HkPasswordInput.scss +29 -17
  142. package/src/components/HkPasswordInput.test.ts +281 -0
  143. package/src/components/HkPasswordInput.tsx +337 -59
  144. package/src/components/HkPickerPane.scss +158 -0
  145. package/src/components/HkPlaceholderMarquee.scss +109 -0
  146. package/src/components/HkPlaceholderMarquee.test.ts +305 -0
  147. package/src/components/HkPlaceholderMarquee.tsx +170 -0
  148. package/src/components/HkPopover.scss +134 -10
  149. package/src/components/HkPopover.sheetdock.test.ts +34 -0
  150. package/src/components/HkPopover.surfacevars.test.ts +20 -0
  151. package/src/components/HkPopover.titleSuppress.test.tsx +265 -0
  152. package/src/components/HkPopover.tsx +225 -50
  153. package/src/components/HkPopoverSheet.test.tsx +130 -0
  154. package/src/components/HkPopupSelect.scss +30 -10
  155. package/src/components/HkPopupSelect.tsx +21 -1
  156. package/src/components/HkProgressDialog.scss +31 -0
  157. package/src/components/HkProgressDialog.tsx +37 -19
  158. package/src/components/HkProtocolModal.scss +26 -0
  159. package/src/components/HkProtocolModal.tsx +112 -0
  160. package/src/components/HkRadio.scss +1 -16
  161. package/src/components/HkRadio.tsx +5 -2
  162. package/src/components/HkRichInput.scss +162 -0
  163. package/src/components/HkRichInput.tsx +335 -0
  164. package/src/components/HkRollingNumber.test.ts +90 -0
  165. package/src/components/HkRollingNumber.tsx +39 -1
  166. package/src/components/HkScrollContainer.scss +71 -54
  167. package/src/components/HkScrollContainer.test.ts +160 -0
  168. package/src/components/HkScrollContainer.tsx +187 -160
  169. package/src/components/HkSearchInput.tsx +3 -3
  170. package/src/components/HkSecretRevealModal.scss +61 -0
  171. package/src/components/HkSecretRevealModal.tsx +91 -0
  172. package/src/components/HkSelect.scss +263 -12
  173. package/src/components/HkSelect.sheetdock.test.ts +30 -0
  174. package/src/components/HkSelect.test.tsx +88 -0
  175. package/src/components/HkSelect.tsx +59 -74
  176. package/src/components/HkSelectPanel.test.tsx +666 -0
  177. package/src/components/HkSelectPanel.tsx +595 -0
  178. package/src/components/HkSelectSheet.test.tsx +132 -0
  179. package/src/components/HkSelectionGrid.scss +1 -1
  180. package/src/components/HkSelectionGrid.tsx +2 -1
  181. package/src/components/HkSelectionWaterfall.scss +1 -1
  182. package/src/components/HkShareBar.scss +40 -0
  183. package/src/components/HkShareBar.tsx +50 -0
  184. package/src/components/HkSidebar.scss +15 -10
  185. package/src/components/HkSidebar.tsx +26 -2
  186. package/src/components/HkSignInCard.test.ts +154 -0
  187. package/src/components/HkSignInCard.tsx +155 -0
  188. package/src/components/HkSlider.scss +99 -0
  189. package/src/components/HkSlider.test.tsx +155 -0
  190. package/src/components/HkSlider.tsx +173 -0
  191. package/src/components/HkSpinner.scss +36 -1
  192. package/src/components/HkSpinner.test.tsx +63 -0
  193. package/src/components/HkSpinner.tsx +16 -1
  194. package/src/components/HkSplash.scss +1 -1
  195. package/src/components/HkSplash.tsx +3 -1
  196. package/src/components/HkStatCard.scss +73 -0
  197. package/src/components/HkStatCard.tsx +46 -0
  198. package/src/components/HkStatusBar.test.tsx +178 -0
  199. package/src/components/HkStatusBar.tsx +379 -0
  200. package/src/components/HkStatusPill.scss +73 -0
  201. package/src/components/HkStatusPill.tsx +41 -0
  202. package/src/components/HkStepFlow.scss +75 -0
  203. package/src/components/HkStepFlow.test.tsx +246 -0
  204. package/src/components/HkStepFlow.tsx +115 -0
  205. package/src/components/HkSwitch.scss +5 -17
  206. package/src/components/HkSwitch.tsx +6 -3
  207. package/src/components/HkTable.scss +20 -1
  208. package/src/components/HkTable.tsx +28 -3
  209. package/src/components/HkTabs.scss +222 -48
  210. package/src/components/HkTabs.test.ts +517 -0
  211. package/src/components/HkTabs.tsx +567 -56
  212. package/src/components/HkTag.tsx +2 -1
  213. package/src/components/HkTextarea.tsx +5 -0
  214. package/src/components/HkThemeToggle.scss +232 -0
  215. package/src/components/HkThemeToggle.test.ts +231 -0
  216. package/src/components/HkThemeToggle.tsx +289 -0
  217. package/src/components/HkTimeline.scss +134 -1
  218. package/src/components/HkTimeline.test.tsx +319 -0
  219. package/src/components/HkTimeline.tsx +303 -54
  220. package/src/components/HkToast.scss +34 -5
  221. package/src/components/HkToast.test.tsx +109 -0
  222. package/src/components/HkToast.tsx +57 -8
  223. package/src/components/HkTokenUsageBadge.scss +53 -0
  224. package/src/components/HkTokenUsageBadge.test.tsx +59 -0
  225. package/src/components/HkTokenUsageBadge.tsx +54 -0
  226. package/src/components/HkTokenUsagePanel.scss +95 -0
  227. package/src/components/HkTokenUsagePanel.tsx +140 -0
  228. package/src/components/HkToolBlock.scss +428 -0
  229. package/src/components/HkToolBlock.tsx +639 -0
  230. package/src/components/HkTooltip.scss +14 -2
  231. package/src/components/HkTooltip.tsx +25 -2
  232. package/src/components/HkTrendChart.tsx +27 -16
  233. package/src/components/HkVoiceInputPopup.scss +77 -0
  234. package/src/components/HkVoiceInputPopup.tsx +97 -0
  235. package/src/components/HkZoomToolbar.scss +4 -1
  236. package/src/components/HkZoomToolbar.tsx +7 -4
  237. package/src/components/_hikari-vars.scss +6 -0
  238. package/src/composables/highlightLanguages.ts +109 -0
  239. package/src/composables/iconRegistry.ts +127 -0
  240. package/src/composables/messaging/browserTransport.ts +96 -0
  241. package/src/composables/messaging/index.ts +204 -0
  242. package/src/composables/messaging/messaging.test.ts +256 -0
  243. package/src/composables/messaging/toastTransport.ts +26 -0
  244. package/src/composables/messaging/types.ts +115 -0
  245. package/src/composables/useActionBar.ts +66 -0
  246. package/src/composables/useAttachments.ts +154 -0
  247. package/src/composables/useConnectionProbe.ts +142 -0
  248. package/src/composables/useDeferredTransition.ts +16 -0
  249. package/src/composables/useEngineHealth.ts +69 -0
  250. package/src/composables/useHighlight.ts +124 -0
  251. package/src/composables/useMeasuredHighlight.ts +150 -0
  252. package/src/composables/useOverlayScrollbar.test.ts +221 -0
  253. package/src/composables/useOverlayScrollbar.ts +296 -0
  254. package/src/composables/usePageTitle.ts +37 -0
  255. package/src/composables/useReducedMotion.ts +34 -0
  256. package/src/composables/useResourceListModal.tsx +147 -0
  257. package/src/composables/useSafeArea.ts +137 -0
  258. package/src/composables/useSendShortcut.ts +35 -0
  259. package/src/composables/useSizeMorph.test.ts +156 -0
  260. package/src/composables/useSizeMorph.ts +149 -0
  261. package/src/composables/useSurfaceTransition.test.ts +96 -0
  262. package/src/composables/useSurfaceTransition.ts +71 -0
  263. package/src/composables/useZoomPan.ts +192 -0
  264. package/src/demo.scss +9 -1
  265. package/src/demo.ts +5 -1
  266. package/src/i18n/locales/ar/chat.json +38 -0
  267. package/src/i18n/locales/ar/components.json +141 -40
  268. package/src/i18n/locales/ar/components.toml +59 -0
  269. package/src/i18n/locales/ar/platform.json +65 -0
  270. package/src/i18n/locales/de/chat.json +38 -0
  271. package/src/i18n/locales/de/components.json +139 -38
  272. package/src/i18n/locales/de/components.toml +59 -0
  273. package/src/i18n/locales/de/platform.json +65 -0
  274. package/src/i18n/locales/en/chat.json +38 -0
  275. package/src/i18n/locales/en/components.json +104 -3
  276. package/src/i18n/locales/en/components.toml +59 -0
  277. package/src/i18n/locales/en/platform.json +65 -0
  278. package/src/i18n/locales/es/chat.json +38 -0
  279. package/src/i18n/locales/es/components.json +141 -40
  280. package/src/i18n/locales/es/components.toml +59 -0
  281. package/src/i18n/locales/es/platform.json +65 -0
  282. package/src/i18n/locales/fr/chat.json +38 -0
  283. package/src/i18n/locales/fr/components.json +137 -36
  284. package/src/i18n/locales/fr/components.toml +59 -0
  285. package/src/i18n/locales/fr/platform.json +65 -0
  286. package/src/i18n/locales/ja/chat.json +38 -0
  287. package/src/i18n/locales/ja/components.json +104 -3
  288. package/src/i18n/locales/ja/components.toml +59 -0
  289. package/src/i18n/locales/ja/platform.json +65 -0
  290. package/src/i18n/locales/ko/chat.json +38 -0
  291. package/src/i18n/locales/ko/components.json +104 -3
  292. package/src/i18n/locales/ko/components.toml +59 -0
  293. package/src/i18n/locales/ko/platform.json +65 -0
  294. package/src/i18n/locales/pt/chat.json +38 -0
  295. package/src/i18n/locales/pt/components.json +141 -40
  296. package/src/i18n/locales/pt/components.toml +59 -0
  297. package/src/i18n/locales/pt/platform.json +65 -0
  298. package/src/i18n/locales/ru/chat.json +38 -0
  299. package/src/i18n/locales/ru/components.json +141 -40
  300. package/src/i18n/locales/ru/components.toml +59 -0
  301. package/src/i18n/locales/ru/platform.json +65 -0
  302. package/src/i18n/locales/zh-Hans/chat.json +38 -0
  303. package/src/i18n/locales/zh-Hans/components.json +109 -4
  304. package/src/i18n/locales/zh-Hans/components.toml +59 -0
  305. package/src/i18n/locales/zh-Hans/platform.json +65 -0
  306. package/src/i18n/locales/zh-Hant/chat.json +38 -0
  307. package/src/i18n/locales/zh-Hant/components.json +109 -4
  308. package/src/i18n/locales/zh-Hant/components.toml +59 -0
  309. package/src/i18n/locales/zh-Hant/platform.json +65 -0
  310. package/src/index.ts +225 -6
  311. package/src/plugins/bootScriptsPlugin.ts +47 -0
  312. package/src/plugins/buildHashPlugin.ts +67 -0
  313. package/src/plugins/index.ts +2 -0
  314. package/src/render.ts +1 -1
  315. package/src/runtime/animationBus.test.ts +112 -0
  316. package/src/runtime/animationBus.ts +33 -2
  317. package/src/runtime/backStack.test.ts +334 -0
  318. package/src/runtime/backStack.ts +377 -0
  319. package/src/runtime/env.ts +4 -0
  320. package/src/runtime/index.ts +15 -3
  321. package/src/runtime/intervalBus.test.ts +118 -0
  322. package/src/runtime/intervalBus.ts +189 -0
  323. package/src/runtime/mobileViewport.test.ts +132 -0
  324. package/src/runtime/mobileViewport.ts +157 -0
  325. package/src/runtime/pageLifecycle.ts +91 -0
  326. package/src/runtime/useBlockingToast.ts +112 -0
  327. package/src/runtime/useClipboard.ts +28 -0
  328. package/src/runtime/useConfirm.ts +15 -7
  329. package/src/runtime/useMediaQuery.test.ts +67 -0
  330. package/src/runtime/useMediaQuery.ts +89 -0
  331. package/src/runtime/useOverlay.test.ts +160 -0
  332. package/src/runtime/useOverlay.ts +59 -12
  333. package/src/runtime/usePopupManager.test.ts +200 -0
  334. package/src/runtime/usePopupManager.ts +106 -10
  335. package/src/runtime/useToast.test.ts +90 -0
  336. package/src/runtime/useToast.ts +20 -2
  337. package/src/styles/admin-tokens.scss +744 -0
  338. package/src/styles/index.scss +5 -3
  339. package/src/styles/theme/_glass.scss +33 -0
  340. package/src/styles/theme/_layout.scss +123 -0
  341. package/src/styles/theme/_scrollbar.scss +73 -0
  342. package/src/styles/theme/_tokens.scss +69 -0
  343. package/src/styles/theme/base.scss +617 -0
  344. package/src/styles/theme/foundation.scss +219 -0
  345. package/src/styles/theme/mixins.scss +793 -0
  346. package/src/styles/theme/themes.scss +25 -0
  347. package/src/styles/theme/variables.scss +125 -0
  348. package/src/theme/_field.scss +116 -0
  349. package/src/theme/fontContext.test.ts +179 -0
  350. package/src/theme/fontContext.ts +169 -0
  351. package/src/theme/index.ts +19 -2
  352. package/src/theme/presets.ts +86 -18
  353. package/src/theme/theme.scss +61 -1
  354. package/src/theme/tokenGroups.test.ts +534 -0
  355. package/src/theme/tokenGroups.ts +552 -0
  356. package/src/theme/useSolarTime.test.ts +113 -0
  357. package/src/theme/useSolarTime.ts +95 -20
  358. package/src/theme/useTheme.test.ts +72 -0
  359. package/src/theme/useTheme.ts +144 -9
  360. package/src/tokens.scss +110 -0
  361. package/src/utils/base64.ts +31 -0
  362. package/src/utils/connectivity.ts +51 -0
  363. package/src/utils/dom.test.ts +131 -0
  364. package/src/utils/dom.ts +133 -0
  365. package/src/utils/download.ts +22 -0
  366. package/src/utils/fileType.ts +127 -0
  367. package/src/utils/format.ts +98 -0
  368. package/src/utils/fuzzy.ts +77 -0
  369. package/src/utils/fuzzySearch.test.ts +87 -0
  370. package/src/utils/healthProbe.ts +64 -0
  371. package/src/utils/objectPath.ts +59 -0
  372. package/src/utils/password.ts +53 -0
  373. package/src/utils/pow.test.ts +45 -0
  374. package/src/utils/pow.ts +226 -0
  375. package/src/utils/powNonce.ts +95 -0
  376. package/src/utils/validation.test.ts +78 -0
  377. package/src/DemoApp.vue +0 -400
  378. package/src/components/HkCollapse.tsx +0 -48
  379. package/src/components/HkMorphingTabs.scss +0 -141
  380. package/src/components/HkMorphingTabs.tsx +0 -113
  381. package/src/composables/useBreakpoint.ts +0 -41
  382. package/src/composables/useClipboard.ts +0 -37
  383. package/src/composables/useConfirm.ts +0 -35
  384. package/src/composables/useToast.ts +0 -2
  385. package/src/runtime/useAsyncData.ts +0 -30
package/README.md CHANGED
@@ -51,4 +51,4 @@ import { HkButton, HkCard, HkInput } from "@celestia-island/hikari";
51
51
 
52
52
  ## License
53
53
 
54
- BUSL-1.1
54
+ Licensed under the [Synthetic Source License (SySL), Version 1.0](../../LICENSE).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@celestia-island/hikari",
3
- "version": "0.4.4",
3
+ "version": "0.30.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Hikari Vue 3 component library — production-grade UI components based on shittim-chest design system",
@@ -24,8 +24,18 @@
24
24
  "import": "./src/index.ts",
25
25
  "types": "./src/index.ts"
26
26
  },
27
+ "./runtime": {
28
+ "import": "./src/runtime/index.ts",
29
+ "types": "./src/runtime/index.ts"
30
+ },
27
31
  "./styles": {
28
32
  "import": "./src/styles/index.scss"
33
+ },
34
+ "./styles/*": "./src/styles/*",
35
+ "./components/*": "./src/components/*",
36
+ "./plugins": {
37
+ "import": "./src/plugins/index.ts",
38
+ "types": "./src/plugins/index.ts"
29
39
  }
30
40
  },
31
41
  "files": [
@@ -46,18 +56,23 @@
46
56
  "dompurify": "^*",
47
57
  "echarts": "^*",
48
58
  "lucide-vue-next": "^*",
49
- "marked": "^*"
59
+ "marked": "^*",
60
+ "highlight.js": "^*"
50
61
  },
51
62
  "devDependencies": {
52
63
  "@vitejs/plugin-vue": "^*",
53
64
  "@vitejs/plugin-vue-jsx": "^*",
54
65
  "@vue/server-renderer": "^*",
66
+ "happy-dom": "^*",
55
67
  "sass": "^*",
56
- "typescript": "~6.0",
68
+ "typescript": "^*",
57
69
  "vite": "^*",
58
- "vue-tsc": "^*"
70
+ "vue-tsc": "^*",
71
+ "vitest": "^*",
72
+ "@types/node": "^*"
59
73
  },
60
- "overrides": {
61
- "typescript": "~6.0"
62
- }
74
+ "sideEffects": [
75
+ "**/*.scss",
76
+ "**/*.css"
77
+ ]
63
78
  }
@@ -0,0 +1,491 @@
1
+ // Demo gallery app (converted from DemoApp.vue → TSX + demo.scss, 2026-08-30
2
+ // house rule: no SFCs in the tree). Render-only showcase of every exported
3
+ // component; state is local and throwaway.
4
+ import { computed, defineComponent, ref } from "vue";
5
+ import { HIKARI_FONT_MONO } from "./theme/fontContext";
6
+ import {
7
+ HButton, HIconButton, HTooltip, HBadge, HTag, HIcon, HSpinner,
8
+ HProgressBar, HProgressRing, HGaugeRing,
9
+ HInput, HSearchInput, HNumberInput, HPasswordInput, HTextarea,
10
+ HCheckbox, HSwitch, HRadio, HSelect, HSelectPanel,
11
+ HLocalizedInput,
12
+ HSkeleton, HSkeletonList, HAvatar, HKbd, HDivider,
13
+ HAlert, HEmptyState, HExpansionPanel,
14
+ HTabs, HCard, HTable, HTimeline,
15
+ HMediaSlider, HMediaPlayer, HImageViewer,
16
+ HZoomToolbar, HMinimap, HTrendChart,
17
+ type TrendPen, type MinimapBox,
18
+ } from "@celestia-island/hikari";
19
+
20
+ const totalComponents = 64;
21
+
22
+ const icons = ["home", "settings", "user", "search", "bell", "heart", "star", "mail", "download", "upload", "trash", "edit", "plus", "check", "x"];
23
+
24
+ const radioOpts = [{ value: "x", label: "X" }, { value: "y", label: "Y" }];
25
+ const gaugeRings = [{ pct: 45, color: "rgb(var(--color-primary, 122 162 247))", trackColor: "rgba(122, 162, 247, 0.15)" }];
26
+ const localeOptions = [
27
+ { code: "en", label: "English" },
28
+ { code: "zh-Hans", label: "简体中文" },
29
+ { code: "ja", label: "日本語" },
30
+ ];
31
+
32
+ const tableCols = [{ key: "name", title: "Name" }, { key: "role", title: "Role" }];
33
+ const tableRows = [{ name: "Alice", role: "Admin" }, { name: "Bob", role: "Editor" }, { name: "Charlie", role: "Viewer" }];
34
+ const timelineSteps = [
35
+ { key: "v1", label: "v1.0 Released" },
36
+ { key: "v11", label: "v1.1" },
37
+ { key: "v2", label: "v2.0" },
38
+ ];
39
+ const wizardSteps = [
40
+ { key: "type", label: "Type" },
41
+ { key: "vendor", label: "Vendor" },
42
+ { key: "apiKey", label: "API key" },
43
+ { key: "models", label: "Models" },
44
+ ];
45
+
46
+ function makeSilentWav(seconds = 2, sampleRate = 8000): string {
47
+ const frames = seconds * sampleRate;
48
+ const bytes = 44 + frames;
49
+ const buf = new Uint8Array(bytes);
50
+ const v = new DataView(buf.buffer);
51
+ v.setUint32(0, 0x52494646, false); // "RIFF"
52
+ v.setUint32(4, bytes - 8, true);
53
+ v.setUint32(8, 0x57415645, false); // "WAVE"
54
+ v.setUint32(12, 0x666d7420, false); // "fmt "
55
+ v.setUint32(16, 16, true);
56
+ v.setUint16(20, 1, true);
57
+ v.setUint16(22, 1, true);
58
+ v.setUint32(24, sampleRate, true);
59
+ v.setUint32(28, sampleRate, true);
60
+ v.setUint16(32, 1, true);
61
+ v.setUint16(34, 8, true);
62
+ v.setUint32(36, 0x64617461, false); // "data"
63
+ v.setUint32(40, frames, true);
64
+ return "data:audio/wav;base64," + btoa(String.fromCharCode(...buf));
65
+ }
66
+
67
+ const zoomBoxes: MinimapBox[] = [
68
+ { id: "a", bounds: { x: 60, y: 40, w: 200, h: 90 }, color: "rgb(var(--color-info, 59 130 246))" },
69
+ { id: "b", bounds: { x: 340, y: 40, w: 200, h: 90 }, color: "rgb(var(--color-success, 34 197 94))" },
70
+ { id: "c", bounds: { x: 200, y: 160, w: 200, h: 90 }, color: "rgb(var(--color-warning, 245 158 11))" },
71
+ ];
72
+ const zoomHub = { x: 300, y: 210 };
73
+ const zoomContent = { x: 0, y: 0, w: 600, h: 300 };
74
+
75
+ const now = Date.now();
76
+ const trendPens: TrendPen[] = [
77
+ {
78
+ label: "CPU",
79
+ thresholds: { h: 70, l: 15 },
80
+ data: Array.from({ length: 40 }, (_, i) => ({
81
+ time: now - (40 - i) * 15_000,
82
+ value: 30 + Math.sin(i / 3) * 18 + (i % 7) * 2,
83
+ })),
84
+ },
85
+ {
86
+ label: "Memory",
87
+ thresholds: { hh: 90 },
88
+ data: Array.from({ length: 40 }, (_, i) => ({
89
+ time: now - (40 - i) * 15_000,
90
+ value: 48 + Math.cos(i / 4) * 10,
91
+ })),
92
+ },
93
+ ];
94
+
95
+ export default defineComponent({
96
+ name: "DemoApp",
97
+ setup() {
98
+ const form = ref({ text: "", search: "", number: 0, password: "", textarea: "" });
99
+ const anyForm = computed(() =>
100
+ Object.values(form.value).filter((v) => v !== "" && v !== 0).join(", ") || null,
101
+ );
102
+ const flags = ref({ a: true, b: false, on: false, radio: "x" });
103
+ const select = ref({ val: "", opts: [{ value: "a", label: "Alpha" }, { value: "b", label: "Beta" }, { value: "c", label: "Gamma" }] });
104
+ const localizedInput = ref({
105
+ value: "工厂总览",
106
+ lang: "zh-Hans",
107
+ translations: { en: "Plant overview", "zh-Hans": "工厂总览" } as Record<string, string>,
108
+ });
109
+ const panelAnchor = ref<HTMLElement | null>(null);
110
+ const panel = ref({
111
+ open: false,
112
+ checked: ["a"] as string[],
113
+ opts: [
114
+ { key: "a", label: "Recent" },
115
+ { key: "b", label: "Starred" },
116
+ { key: "c", label: "Archived" },
117
+ ],
118
+ });
119
+ function togglePanelOpt(key: string, v: boolean) {
120
+ panel.value.checked = v
121
+ ? [...panel.value.checked, key]
122
+ : panel.value.checked.filter((k) => k !== key);
123
+ }
124
+ const tabs = ref({ active: "tab1", items: [{ key: "tab1", label: "Overview" }, { key: "tab2", label: "Details" }, { key: "tab3", label: "Settings" }] });
125
+ const mediaSliderRatio = ref(0.42);
126
+ const demoAudioSrc = makeSilentWav();
127
+ const demoImageSrc =
128
+ "data:image/svg+xml;base64," +
129
+ btoa(`<svg xmlns="http://www.w3.org/2000/svg" width="1600" height="900">
130
+ <rect width="1600" height="900" fill="#1b2333"/>
131
+ <g stroke="#2c3a55" stroke-width="1">
132
+ ${Array.from({ length: 15 }, (_, i) => `<line x1="${i * 110}" y1="0" x2="${i * 110}" y2="900"/>`).join("")}
133
+ ${Array.from({ length: 9 }, (_, i) => `<line x1="0" y1="${i * 110}" x2="1600" y2="${i * 110}"/>`).join("")}
134
+ </g>
135
+ <circle cx="800" cy="450" r="180" fill="rgba(122,162,247,0.35)" stroke="#7aa2f7" stroke-width="3"/>
136
+ <circle cx="800" cy="450" r="90" fill="rgba(122,162,247,0.2)" stroke="#7aa2f7" stroke-width="2" stroke-dasharray="6 4"/>
137
+ <circle cx="800" cy="450" r="12" fill="#f7768e"/>
138
+ <text x="800" y="700" fill="#9aa5ce" style="font-family: var(--font-mono, ${HIKARI_FONT_MONO})" font-size="28" text-anchor="middle">Hikari — zoom / pan demo</text>
139
+ </svg>`);
140
+ const zoomState = ref({ zoom: 1.6, panX: -40, panY: -18, canIn: true, canOut: true, zoomed: true });
141
+ function syncZoom() {
142
+ const z = zoomState.value;
143
+ z.zoomed = z.zoom > 1;
144
+ z.canIn = z.zoom < 8;
145
+ z.canOut = z.zoom > 1;
146
+ }
147
+ function zoomIn() {
148
+ zoomState.value.zoom = Math.min(8, zoomState.value.zoom * 1.2);
149
+ syncZoom();
150
+ }
151
+ function zoomOut() {
152
+ zoomState.value.zoom = Math.max(1, zoomState.value.zoom / 1.2);
153
+ syncZoom();
154
+ }
155
+ function zoomReset() {
156
+ zoomState.value.zoom = 1;
157
+ zoomState.value.panX = 0;
158
+ zoomState.value.panY = 0;
159
+ syncZoom();
160
+ }
161
+ function panBy(dx: number, dy: number) {
162
+ zoomState.value.panX += dx;
163
+ zoomState.value.panY += dy;
164
+ syncZoom();
165
+ }
166
+
167
+ return () => (
168
+ <div class="demo">
169
+ <header class="demo-header">
170
+ <h1>Hikari</h1>
171
+ <p class="subtitle">Vue 3 Component Library — {totalComponents} components</p>
172
+ </header>
173
+
174
+ <section>
175
+ <h2>HButton</h2>
176
+ <div class="row">
177
+ <HButton variant="primary" size="sm">Primary</HButton>
178
+ <HButton variant="secondary" size="sm">Secondary</HButton>
179
+ <HButton variant="outline" size="sm">Outline</HButton>
180
+ <HButton variant="ghost" size="sm">Ghost</HButton>
181
+ <HButton variant="danger" size="sm">Danger</HButton>
182
+ </div>
183
+ <div class="row">
184
+ <HButton variant="primary" size="sm">Sm</HButton>
185
+ <HButton variant="primary" size="md">Md</HButton>
186
+ <HButton variant="primary" size="lg">Lg</HButton>
187
+ </div>
188
+ <div class="row">
189
+ <HButton variant="primary" loading>Loading</HButton>
190
+ <HButton variant="primary" disabled>Disabled</HButton>
191
+ <HButton variant="primary" shortcut="⌘K">Shortcut</HButton>
192
+ </div>
193
+ </section>
194
+
195
+ <section>
196
+ <h2>HIconButton</h2>
197
+ <div class="row">
198
+ <HIconButton icon="search" variant="ghost" size={16} />
199
+ <HIconButton icon="settings" variant="secondary" size={24} />
200
+ <HIconButton icon="heart" variant="primary" size={40} />
201
+ </div>
202
+ </section>
203
+
204
+ <section>
205
+ <h2>HTooltip</h2>
206
+ <div class="row">
207
+ <HTooltip text="Top placement" placement="top"><HButton variant="outline" size="sm">Top</HButton></HTooltip>
208
+ <HTooltip text="Bottom placement" placement="bottom"><HButton variant="outline" size="sm">Bottom</HButton></HTooltip>
209
+ <HTooltip text="Left placement" placement="left"><HButton variant="outline" size="sm">Left</HButton></HTooltip>
210
+ <HTooltip text="Right placement" placement="right"><HButton variant="outline" size="sm">Right</HButton></HTooltip>
211
+ </div>
212
+ </section>
213
+
214
+ <section>
215
+ <h2>HBadge</h2>
216
+ <div class="row">
217
+ <HBadge>Default</HBadge>
218
+ <HBadge variant="success">Success</HBadge>
219
+ <HBadge variant="warning">Warning</HBadge>
220
+ <HBadge variant="error">Error</HBadge>
221
+ </div>
222
+ </section>
223
+
224
+ <section>
225
+ <h2>HTag</h2>
226
+ <div class="row">
227
+ <HTag>Default</HTag>
228
+ <HTag variant="success">Success</HTag>
229
+ <HTag variant="warning">Warning</HTag>
230
+ <HTag variant="danger">Danger</HTag>
231
+ <HTag closable onClose={() => {}}>Closable</HTag>
232
+ </div>
233
+ </section>
234
+
235
+ <section>
236
+ <h2>HIcons</h2>
237
+ <div class="row icons">
238
+ {icons.map((i) => (
239
+ <span class="icon-grid" key={i}><HIcon name={i} size={24} /><small>{i}</small></span>
240
+ ))}
241
+ </div>
242
+ </section>
243
+
244
+ <section>
245
+ <h2>HSpinner</h2>
246
+ <div class="row"><HSpinner size="xs" /><HSpinner size="sm" /><HSpinner size="md" /><HSpinner size="lg" /><HSpinner size="xl" /></div>
247
+ </section>
248
+
249
+ <section>
250
+ <h2>HProgressBar / HProgressRing / HGaugeRing</h2>
251
+ <div class="row"><HProgressBar value={65} style="width:200px" /></div>
252
+ <div class="row"><HProgressRing value={75} size={80} /><HGaugeRing rings={gaugeRings} size={80} centerLabel="CPU" /></div>
253
+ </section>
254
+
255
+ <section>
256
+ <h2>HInput / HSearchInput / HNumberInput / HPasswordInput / HTextarea</h2>
257
+ <div class="form-grid">
258
+ <HInput modelValue={form.value.text} onUpdate:modelValue={(v: string) => (form.value.text = v)} placeholder="Text input" />
259
+ <HInput modelValue={form.value.text} onUpdate:modelValue={(v: string) => (form.value.text = v)} placeholder="Disabled" disabled />
260
+ <HSearchInput modelValue={form.value.search} onUpdate:modelValue={(v: string | number) => (form.value.search = String(v))} placeholder="Search..." />
261
+ <HNumberInput modelValue={form.value.number} onUpdate:modelValue={(v: number) => (form.value.number = v)} placeholder="Number" />
262
+ <HPasswordInput modelValue={form.value.password} onUpdate:modelValue={(v: string) => (form.value.password = v)} placeholder="Password" />
263
+ <HTextarea modelValue={form.value.textarea} onUpdate:modelValue={(v: string) => (form.value.textarea = v)} placeholder="Textarea" rows={3} />
264
+ </div>
265
+ {anyForm.value ? <p class="result">Form: {anyForm.value}</p> : null}
266
+ </section>
267
+
268
+ <section>
269
+ <h2>HCheckbox / HSwitch / HRadio</h2>
270
+ <div class="row">
271
+ <HCheckbox modelValue={flags.value.a} onUpdate:modelValue={(v: boolean) => (flags.value.a = v)}>Option A</HCheckbox>
272
+ <HCheckbox modelValue={flags.value.b} onUpdate:modelValue={(v: boolean) => (flags.value.b = v)}>Option B</HCheckbox>
273
+ <HSwitch modelValue={flags.value.on} onUpdate:modelValue={(v: boolean) => (flags.value.on = v)} label="Toggle" />
274
+ <HRadio modelValue={flags.value.radio} onUpdate:modelValue={(v: string | number) => (flags.value.radio = String(v))} options={radioOpts} />
275
+ </div>
276
+ </section>
277
+
278
+ <section>
279
+ <h2>HSelect</h2>
280
+ <div class="row"><HSelect modelValue={select.value.val} onUpdate:modelValue={(v: string) => (select.value.val = v)} options={select.value.opts} placeholder="Choose..." style="width:200px" /></div>
281
+ </section>
282
+
283
+ <section>
284
+ <h2>HSelectPanel</h2>
285
+ <div class="row">
286
+ <span style="align-self:center;color:var(--color-muted, gray)">custom trigger + checkbox rows:</span>
287
+ <span ref={panelAnchor} style="display:inline-flex">
288
+ <HButton onClick={() => (panel.value.open = !panel.value.open)}>Filter ▲</HButton>
289
+ </span>
290
+ <HSelectPanel
291
+ open={panel.value.open}
292
+ onUpdate:open={(v: boolean) => (panel.value.open = v)}
293
+ anchorRef={panelAnchor.value ?? null}
294
+ title="Filters"
295
+ placement="top-start"
296
+ >
297
+ {panel.value.opts.map((opt) => (
298
+ <HCheckbox
299
+ key={opt.key}
300
+ modelValue={panel.value.checked.includes(opt.key)}
301
+ onUpdate:modelValue={(v: boolean) => togglePanelOpt(opt.key, v)}
302
+ >{opt.label}</HCheckbox>
303
+ ))}
304
+ </HSelectPanel>
305
+ </div>
306
+ </section>
307
+
308
+ <section>
309
+ <h2>HLocalizedInput</h2>
310
+ <div class="row" style="max-width:420px">
311
+ <HLocalizedInput
312
+ modelValue={localizedInput.value.value}
313
+ onUpdate:modelValue={(v: string) => (localizedInput.value.value = v)}
314
+ sourceLang={localizedInput.value.lang}
315
+ translations={localizedInput.value.translations}
316
+ onUpdate:translations={(v: Record<string, string>) => (localizedInput.value.translations = v)}
317
+ localeOptions={localeOptions}
318
+ label="Localized title"
319
+ placeholder="Localized text..."
320
+ />
321
+ </div>
322
+ <p class="result">Translations: {JSON.stringify(localizedInput.value.translations)}</p>
323
+ </section>
324
+
325
+ <section>
326
+ <h2>HSkeleton / HSkeletonList</h2>
327
+ <div class="row">
328
+ <HSkeleton width="100px" height="24px" />
329
+ <HSkeletonList count={3} style="width:300px" />
330
+ </div>
331
+ </section>
332
+
333
+ <section>
334
+ <h2>HAvatar</h2>
335
+ <div class="row">
336
+ <HAvatar name="John Doe" size="sm" />
337
+ <HAvatar name="Jane Smith" size="md" />
338
+ <HAvatar name="Admin" size="lg" />
339
+ </div>
340
+ </section>
341
+
342
+ <section>
343
+ <h2>HKbd</h2>
344
+ <div class="row"><HKbd keys="⌘" /><HKbd keys="⌥" /><HKbd keys="⇧" /><HKbd keys="⌃" /><HKbd keys="K" /><span> = open command palette</span></div>
345
+ </section>
346
+
347
+ <section>
348
+ <h2>HDivider</h2>
349
+ <HDivider />
350
+ <p style="opacity:0.5;text-align:center;padding:8px 0">Section above divider</p>
351
+ <HDivider />
352
+ <p style="opacity:0.5;text-align:center;padding:8px 0">Section below divider</p>
353
+ </section>
354
+
355
+ <section>
356
+ <h2>HAlert</h2>
357
+ <div class="col"><HAlert message="Default info alert" /><HAlert variant="success" message="Success alert" /><HAlert variant="warning" message="Warning alert" /><HAlert variant="error" message="Error alert" /></div>
358
+ </section>
359
+
360
+ <section>
361
+ <h2>HEmptyState</h2>
362
+ <HEmptyState
363
+ title="No items"
364
+ description="Nothing to show here yet."
365
+ v-slots={{ icon: () => <HIcon name="inbox" size={48} /> }}
366
+ />
367
+ </section>
368
+
369
+ <section>
370
+ <h2>HExpansionPanel</h2>
371
+ <HExpansionPanel title="Electrical power" subtitle="9 colors">
372
+ <p style="padding:12px">Material Design 3 expansion panel — animated body, aria-expanded header.</p>
373
+ </HExpansionPanel>
374
+ <div style="height:8px" />
375
+ <HExpansionPanel title="Disabled panel" subtitle="cannot toggle" disabled={true}>
376
+ <p style="padding:12px">Never opens.</p>
377
+ </HExpansionPanel>
378
+ </section>
379
+
380
+ <section>
381
+ <h2>HTabs — one look, variants pick the working mode (pill = tablist, segmented = radiogroup)</h2>
382
+ <HTabs modelValue={tabs.value.active} onUpdate:modelValue={(v: string) => (tabs.value.active = v)} tabs={tabs.value.items} />
383
+ <p class="result" style="margin-top:8px">Active tab: {tabs.value.active}</p>
384
+ <h3>segmented (option picker) + protruding end actions</h3>
385
+ <HTabs
386
+ modelValue={tabs.value.active}
387
+ onUpdate:modelValue={(v: string) => (tabs.value.active = v)}
388
+ tabs={tabs.value.items}
389
+ variant="segmented"
390
+ startAction={{ label: "Back" }}
391
+ endAction={{ label: "Add tab" }}
392
+ onAction={(side: string) => console.log("tabs action", side)}
393
+ />
394
+ <div style="margin-top:12px" />
395
+ </section>
396
+
397
+ <section>
398
+ <h2>HCard</h2>
399
+ <div class="row">
400
+ <HCard style="padding:20px;flex:1">
401
+ <h4>Card title</h4>
402
+ <p style="font-size:0.75rem;opacity:0.7">Card body content goes here.</p>
403
+ </HCard>
404
+ <HCard style="padding:20px;flex:1" hoverable>
405
+ <h4>Hoverable card</h4>
406
+ <p style="font-size:0.75rem;opacity:0.7">Hover me for effect.</p>
407
+ </HCard>
408
+ </div>
409
+ </section>
410
+
411
+ <section>
412
+ <h2>HTable</h2>
413
+ <HTable columns={tableCols} rows={tableRows} style="max-height:200px" />
414
+ </section>
415
+
416
+ <section>
417
+ <h2>HTimeline</h2>
418
+ <HTimeline steps={timelineSteps} currentKey="v2" />
419
+ <h3>Narrow host → auto window (prev / current / next, edges fade)</h3>
420
+ <div style="max-width:280px;padding:8px;border:1px dashed var(--hi-color-border, #888);border-radius:8px">
421
+ <HTimeline steps={wizardSteps} currentKey="vendor" />
422
+ </div>
423
+ <h3>collapse="always" at the first step</h3>
424
+ <div style="max-width:280px;padding:8px;border:1px dashed var(--hi-color-border, #888);border-radius:8px">
425
+ <HTimeline steps={wizardSteps} currentKey="type" collapse="always" />
426
+ </div>
427
+ </section>
428
+
429
+ <section>
430
+ <h2>HMediaSlider</h2>
431
+ <div class="row" style="width:min(420px,100%)">
432
+ <HMediaSlider ratio={mediaSliderRatio.value} onUpdate:ratio={(v: number) => (mediaSliderRatio.value = v)} buffered={0.85} />
433
+ <span style="font-size:0.7rem;opacity:0.6">{Math.round(mediaSliderRatio.value * 100)}%</span>
434
+ </div>
435
+ </section>
436
+
437
+ <section>
438
+ <h2>HMediaPlayer (audio)</h2>
439
+ <HMediaPlayer type="audio" src={demoAudioSrc} style="max-width:560px" />
440
+ </section>
441
+
442
+ <section>
443
+ <h2>HImageViewer</h2>
444
+ <HImageViewer src={demoImageSrc} alt="Hikari demo image" />
445
+ </section>
446
+
447
+ <section>
448
+ <h2>HZoomToolbar / HMinimap</h2>
449
+ <div style="position:relative;height:200px;border-radius:8px;overflow:hidden;background:rgba(0,0,0,0.35)">
450
+ <div style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:0.75rem;opacity:0.5">
451
+ Host surface — zoom {zoomState.value.zoom.toFixed(2)}x, pan ({Math.round(zoomState.value.panX)}, {Math.round(zoomState.value.panY)})
452
+ </div>
453
+ <HZoomToolbar
454
+ zoom={zoomState.value.zoom}
455
+ canZoomIn={zoomState.value.canIn}
456
+ canZoomOut={zoomState.value.canOut}
457
+ isZoomed={zoomState.value.zoomed}
458
+ onZoomIn={zoomIn}
459
+ onZoomOut={zoomOut}
460
+ onReset={zoomReset}
461
+ />
462
+ <HMinimap
463
+ boxes={zoomBoxes}
464
+ hubPos={zoomHub}
465
+ zoom={zoomState.value.zoom}
466
+ panX={zoomState.value.panX}
467
+ panY={zoomState.value.panY}
468
+ viewportWidth={800}
469
+ viewportHeight={200}
470
+ contentBounds={zoomContent}
471
+ zoomPercent={Math.round(zoomState.value.zoom * 100)}
472
+ canZoomIn={zoomState.value.canIn}
473
+ canZoomOut={zoomState.value.canOut}
474
+ showReset
475
+ onPanDelta={panBy}
476
+ />
477
+ </div>
478
+ </section>
479
+
480
+ <section>
481
+ <h2>HTrendChart</h2>
482
+ <HTrendChart pens={trendPens} height="260px" style="max-width:760px" />
483
+ </section>
484
+
485
+ <section class="demo-footer">
486
+ <p>Hikari — Powered by celestia-island</p>
487
+ </section>
488
+ </div>
489
+ );
490
+ },
491
+ });
@@ -0,0 +1,94 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { describe, expect, it } from "vitest";
5
+
6
+ import {
7
+ listCssAnimations,
8
+ registerCssAnimation,
9
+ setCssAnimationsEnabled,
10
+ isCssAnimationsEnabled,
11
+ } from "./registerAnimations";
12
+ import { setReducedMotion } from "../runtime/animationBus";
13
+
14
+ const srcDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
15
+
16
+ /** Recursively collect SCSS/TS/TSX sources under src/, skipping the
17
+ * animation registrar's own directory (it is the thing under test, not
18
+ * an animation-owning component). */
19
+ function collectSourceFiles(dir: string): string[] {
20
+ const out: string[] = [];
21
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
22
+ const p = path.join(dir, entry.name);
23
+ if (entry.isDirectory()) {
24
+ if (p === path.resolve(srcDir, "animation")) continue;
25
+ out.push(...collectSourceFiles(p));
26
+ } else if (/\.(scss|ts|tsx)$/.test(entry.name)) {
27
+ out.push(p);
28
+ }
29
+ }
30
+ return out;
31
+ }
32
+
33
+ /** Names of every `@keyframes` actually declared in the source tree.
34
+ * Full-line `//` comments are stripped first so commented-out keyframes
35
+ * (see HkBreadcrumb.scss) are not counted. */
36
+ function declaredKeyframeNames(): string[] {
37
+ const names = new Set<string>();
38
+ for (const file of collectSourceFiles(srcDir)) {
39
+ const stripped = readFileSync(file, "utf-8").replace(/^[ \t]*\/\/.*$/gm, "");
40
+ for (const m of stripped.matchAll(/@keyframes\s+([A-Za-z0-9_-]+)/g)) {
41
+ names.add(m[1]);
42
+ }
43
+ }
44
+ return [...names].sort();
45
+ }
46
+
47
+ describe("registerAnimations", () => {
48
+ it("registers exactly the keyframes declared in the source tree", () => {
49
+ const declared = declaredKeyframeNames();
50
+ expect(declared.length).toBeGreaterThanOrEqual(12);
51
+ const registered = listCssAnimations().map((a) => a.name);
52
+ expect([...registered].sort()).toEqual(declared);
53
+ });
54
+
55
+ it("marks looping animations as infinite and one-shots as finite", () => {
56
+ const byName = new Map(listCssAnimations().map((a) => [a.name, a]));
57
+ expect(byName.get("hk-spinner-rotate")?.infinite).toBe(true);
58
+ expect(byName.get("hk-toast-spin")?.infinite).toBe(true);
59
+ expect(byName.get("hk-skeleton-shimmer")?.infinite).toBe(true);
60
+ expect(byName.get("hk-progress-indeterminate")?.infinite).toBe(true);
61
+ expect(byName.get("hk-status-pill-pulse")?.infinite).toBe(true);
62
+ expect(byName.get("hk-pwd-breathe")?.infinite).toBe(true);
63
+ expect(byName.get("s-nav-item-badge-pulse")?.infinite).toBe(true);
64
+ expect(byName.get("s-auth-card-in")?.infinite).toBe(false);
65
+ expect(byName.get("hk-rolling-number-up")?.infinite).toBe(false);
66
+ expect(byName.get("hk-modal-breadcrumb-in")?.infinite).toBe(false);
67
+ expect(byName.get("hk-pwd-flash")?.infinite).toBe(false);
68
+ });
69
+
70
+ it("flips the html dataset attribute in both directions", () => {
71
+ setCssAnimationsEnabled(false);
72
+ expect(document.documentElement.dataset.cssAnimations).toBe("0");
73
+ expect(isCssAnimationsEnabled()).toBe(false);
74
+ setCssAnimationsEnabled(true);
75
+ expect(document.documentElement.dataset.cssAnimations).toBe("1");
76
+ expect(isCssAnimationsEnabled()).toBe(true);
77
+ });
78
+
79
+ it("suspends CSS animations together with the reduced-motion bus", () => {
80
+ setReducedMotion(true);
81
+ expect(document.documentElement.dataset.cssAnimations).toBe("0");
82
+ expect(isCssAnimationsEnabled()).toBe(false);
83
+ setReducedMotion(false);
84
+ expect(document.documentElement.dataset.cssAnimations).toBe("1");
85
+ expect(isCssAnimationsEnabled()).toBe(true);
86
+ });
87
+
88
+ it("re-registers idempotently and keeps the list sorted", () => {
89
+ registerCssAnimation("hk-spinner-rotate", { infinite: true });
90
+ const names = listCssAnimations().map((a) => a.name);
91
+ expect(names.indexOf("hk-spinner-rotate")).toBe(names.lastIndexOf("hk-spinner-rotate"));
92
+ expect(names).toEqual([...names].sort());
93
+ });
94
+ });