@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
@@ -0,0 +1,92 @@
1
+ import { defineComponent, ref } from "vue";
2
+ import HkPopover from "./HkPopover";
3
+ import HkMenuActionItem from "./HkMenuActionItem";
4
+ import "./HkAltSignIn.scss";
5
+
6
+ /**
7
+ * Shared "alternative sign-in" entry for auth cards (P77).
8
+ *
9
+ * A small muted trigger row (styled in the `.s-auth-footer` grammar) that
10
+ * opens a popover listing third-party/alternative credential entries —
11
+ * platform passkey on shittim-chest, feishu OAuth on easy-hydro-erp — so
12
+ * both apps share one implementation and only differ in content.
13
+ *
14
+ * Entries render as [`HkMenuActionItem`] rows inside an [`HkPopover`]
15
+ * (glass panel, flip/escape/backdrop close, bottom-sheet on mobile), which
16
+ * gives keyboard and pointer handling for free.
17
+ */
18
+ export default defineComponent({
19
+ name: "HkAltSignIn",
20
+ props: {
21
+ /** Trigger row text, e.g. "其他方式登录". Consumer-localized on
22
+ * purpose: hikari ships no dictionary dependency onto hosts here. */
23
+ label: { type: String, required: true },
24
+ /** Alternative sign-in entries offered in the dropdown. */
25
+ entries: {
26
+ type: Array as unknown as () => Array<{
27
+ key: string;
28
+ label: string;
29
+ icon?: Record<string, unknown>;
30
+ danger?: boolean;
31
+ }>,
32
+ required: true,
33
+ },
34
+ disabled: { type: Boolean, default: false },
35
+ /** Popover placement around the trigger row. */
36
+ placement: {
37
+ type: String,
38
+ default: "top",
39
+ } as const,
40
+ },
41
+ emits: {
42
+ /** An entry was chosen; the popover closes automatically. */
43
+ select: (_key: string) => true,
44
+ },
45
+ setup(props, { emit }) {
46
+ const open = ref(false);
47
+ const anchor = ref<HTMLElement | null>(null);
48
+
49
+ function choose(key: string): void {
50
+ open.value = false;
51
+ emit("select", key);
52
+ }
53
+
54
+ return () => (
55
+ <div class="hk-alt-signin">
56
+ <button
57
+ ref={(el) => (anchor.value = el as HTMLElement | null)}
58
+ type="button"
59
+ class="hk-alt-signin__trigger"
60
+ aria-haspopup="menu"
61
+ aria-expanded={open.value}
62
+ disabled={props.disabled}
63
+ onClick={() => {
64
+ if (!props.disabled) open.value = !open.value;
65
+ }}
66
+ >
67
+ <span class="hk-alt-signin__rule" />
68
+ <span class="hk-alt-signin__label">{props.label}</span>
69
+ <span class="hk-alt-signin__rule" />
70
+ </button>
71
+ <HkPopover
72
+ modelValue={open.value}
73
+ onUpdate:modelValue={(v: boolean) => (open.value = v)}
74
+ placement={props.placement as never}
75
+ anchorRef={anchor.value}
76
+ >
77
+ <div class="hk-alt-signin__panel" role="menu">
78
+ {props.entries.map((entry) => (
79
+ <HkMenuActionItem
80
+ key={entry.key}
81
+ icon={entry.icon}
82
+ label={entry.label}
83
+ danger={entry.danger === true}
84
+ onClick={() => choose(entry.key)}
85
+ />
86
+ ))}
87
+ </div>
88
+ </HkPopover>
89
+ </div>
90
+ );
91
+ },
92
+ });
@@ -0,0 +1,49 @@
1
+ .s-attachment-modal {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--space-12, 0.75rem);
5
+ }
6
+
7
+ .s-attachment-modal-text {
8
+ max-height: 48vh;
9
+ padding: var(--space-12, 0.75rem);
10
+ border: 1px solid var(--border-faint, rgb(var(--color-border) / 10%));
11
+ border-radius: var(--radius-md, 8px);
12
+ background: rgb(var(--color-surface) / 0.5);
13
+ }
14
+
15
+ .s-attachment-modal-text-empty {
16
+ margin: 0;
17
+ font-size: var(--text-xs, 0.75rem);
18
+ color: rgb(var(--color-muted));
19
+ font-style: italic;
20
+ }
21
+
22
+ .s-attachment-modal-text-error {
23
+ margin: var(--space-8, 0.5rem) 0 0;
24
+ font-size: var(--text-xs, 0.75rem);
25
+ color: rgb(var(--color-error));
26
+ }
27
+
28
+ .s-attachment-modal-file {
29
+ display: flex;
30
+ flex-direction: column;
31
+ align-items: center;
32
+ gap: var(--space-8, 0.5rem);
33
+ padding: var(--space-24, 1.5rem);
34
+ color: rgb(var(--color-muted));
35
+ }
36
+
37
+ .s-attachment-modal-file-name {
38
+ margin: 0;
39
+ font-size: var(--text-sm, 0.8125rem);
40
+ max-width: 100%;
41
+ overflow: hidden;
42
+ text-overflow: ellipsis;
43
+ white-space: nowrap;
44
+ }
45
+
46
+ .s-attachment-modal-meta {
47
+ display: flex;
48
+ gap: var(--space-8, 0.5rem);
49
+ }
@@ -0,0 +1,318 @@
1
+ import { computed, defineComponent, ref, watch, type PropType } from "vue";
2
+ import { Film, File as FileIcon, Image as ImageIcon, Music } from "lucide-vue-next";
3
+ import {
4
+ HBadge,
5
+ HImageViewer,
6
+ HMarkdownRenderer,
7
+ HMediaPlayer,
8
+ HModal,
9
+ HScrollContainer,
10
+ } from "@celestia-island/hikari";
11
+
12
+ import { useI18n } from "../i18n/context";
13
+
14
+ import "./HkAttachmentModal.scss";
15
+
16
+ const CODE_LANGS: Record<string, string> = {
17
+ ".ts": "typescript", ".tsx": "typescript", ".js": "javascript",
18
+ ".jsx": "javascript", ".json": "json", ".rs": "rust", ".go": "go",
19
+ ".py": "python", ".java": "java", ".c": "c", ".cpp": "cpp", ".h": "c",
20
+ ".cs": "csharp", ".sh": "bash", ".bash": "bash", ".yaml": "yaml",
21
+ ".yml": "yaml", ".toml": "toml", ".html": "html", ".css": "css",
22
+ ".scss": "scss", ".sql": "sql", ".xml": "xml", ".ini": "ini",
23
+ };
24
+
25
+ function codeLanguage(name: string): string | undefined {
26
+ const idx = name.lastIndexOf(".");
27
+ if (idx < 0) return undefined;
28
+ return CODE_LANGS[name.slice(idx).toLowerCase()];
29
+ }
30
+
31
+ const TEXT_EXTS = new Set([".md", ".markdown", ".txt", ".log", ".csv"]);
32
+ const CODE_EXTS = new Set(Object.keys(CODE_LANGS));
33
+
34
+ /** Attachment row in the HRichInput strip. Parent-owned upload state. */
35
+ export interface HAttachmentItem {
36
+ id: string;
37
+ name: string;
38
+ type: string;
39
+ size: number;
40
+ preview?: string;
41
+ /** Upload progress 0-100; omitted/100 = ready. */
42
+ progress?: number;
43
+ status?: "uploading" | "done" | "error";
44
+ }
45
+
46
+ /** Attachment payload for HAttachmentModal preview. */
47
+ export interface HAttachmentDetail {
48
+ name: string;
49
+ type: string;
50
+ size: number;
51
+ preview?: string;
52
+ url?: string;
53
+ /** Original File handle — used to read text/code content for preview. */
54
+ file?: File;
55
+ }
56
+
57
+ /** Kind of rich preview to render; inferred from MIME unless hinted. */
58
+ export type HAttachmentPreviewType = "image" | "video" | "audio" | "other";
59
+
60
+ /** Resolve the preview kind: an explicit hint wins, MIME prefix otherwise. */
61
+ export function previewKindFor(
62
+ att: Pick<HAttachmentDetail, "type"> | null | undefined,
63
+ hint?: HAttachmentPreviewType,
64
+ ): HAttachmentPreviewType {
65
+ if (hint) return hint;
66
+ const type = att?.type ?? "";
67
+ if (type.startsWith("image/")) return "image";
68
+ if (type.startsWith("video/")) return "video";
69
+ if (type.startsWith("audio/")) return "audio";
70
+ return "other";
71
+ }
72
+
73
+ function isTextFile(att: HAttachmentDetail): boolean {
74
+ const ext = att.name.slice(att.name.lastIndexOf(".")).toLowerCase();
75
+ return att.type.startsWith("text/") || TEXT_EXTS.has(ext) || CODE_EXTS.has(ext);
76
+ }
77
+
78
+ /** "512" -> "512B", "1536" -> "1.5KB", "2621440" -> "2.5MB". */
79
+ function formatBytes(bytes: number): string {
80
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0B";
81
+ if (bytes < 1024) return `${Math.floor(bytes)}B`;
82
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
83
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
84
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}GB`;
85
+ }
86
+
87
+ /**
88
+ * HkAttachmentModal — generic file-picker preview modal.
89
+ * (Upstreamed from shittim-chest's plana-legacy layer.)
90
+ *
91
+ * Renders an attachment by preview kind: image (HImageViewer), video/audio
92
+ * (HMediaPlayer), text/code (HMarkdownRenderer, with syntax fences for
93
+ * known code extensions) or a generic file chip.
94
+ *
95
+ * URL handling is transport-agnostic: when the attachment carries no
96
+ * `preview`/`url`, the consumer may pass `resolveUrl` (its own API client /
97
+ * transport knows how to turn a backend file name into an authed URL).
98
+ * The resolved URL is used for media previews, the text fetch and the
99
+ * download action. Text content is read via the attachment's `file` handle
100
+ * when provided.
101
+ */
102
+ export const HkAttachmentModal = defineComponent({
103
+ name: "HkAttachmentModal",
104
+ props: {
105
+ modelValue: { type: Boolean, default: false },
106
+ attachment: { type: Object as PropType<HAttachmentDetail | null>, default: null },
107
+ /** Transport-provided URL resolver; called with the file name when the
108
+ * attachment has neither `preview` nor `url`. */
109
+ resolveUrl: { type: Function as PropType<(name: string) => Promise<string>>, default: undefined },
110
+ /** Preview kind hint; inferred from the MIME type when omitted. */
111
+ previewType: { type: String as PropType<HAttachmentPreviewType>, default: undefined },
112
+ },
113
+ emits: {
114
+ "update:modelValue": (_v: boolean) => true,
115
+ },
116
+ setup(props, { emit }) {
117
+ const { t } = useI18n();
118
+
119
+ const kind = computed(() => previewKindFor(props.attachment, props.previewType));
120
+ const isText = computed(() => (props.attachment ? isTextFile(props.attachment) : false));
121
+
122
+ /* ── URL resolution ──────────────────────────────────────────── */
123
+ const resolvedSrc = ref("");
124
+ const srcLoading = ref(false);
125
+ const srcError = ref<string | null>(null);
126
+
127
+ async function resolveSrc() {
128
+ const att = props.attachment;
129
+ srcLoading.value = false;
130
+ srcError.value = null;
131
+ if (!att) {
132
+ resolvedSrc.value = "";
133
+ return;
134
+ }
135
+ if (att.preview || att.url) {
136
+ resolvedSrc.value = att.preview || att.url || "";
137
+ return;
138
+ }
139
+ if (props.resolveUrl) {
140
+ srcLoading.value = true;
141
+ try {
142
+ const resolved = await props.resolveUrl(att.name);
143
+ // A resolver that misses (map lookup, stale cache) may return
144
+ // undefined — store the empty string so every src binding stays
145
+ // a real string and the srcError path explains the empty preview.
146
+ if (typeof resolved !== "string" || !resolved) {
147
+ resolvedSrc.value = "";
148
+ srcError.value = `no URL resolved for "${att.name}"`;
149
+ return;
150
+ }
151
+ resolvedSrc.value = resolved;
152
+ } catch (e) {
153
+ resolvedSrc.value = "";
154
+ srcError.value = e instanceof Error ? e.message : String(e);
155
+ } finally {
156
+ srcLoading.value = false;
157
+ }
158
+ return;
159
+ }
160
+ resolvedSrc.value = "";
161
+ }
162
+
163
+ watch(
164
+ () => [props.attachment, props.resolveUrl, props.previewType] as const,
165
+ () => { void resolveSrc(); },
166
+ { immediate: true },
167
+ );
168
+
169
+ /* ── Text / code preview ─────────────────────────────────────── */
170
+ const textContent = ref("");
171
+ const textPlain = ref(false);
172
+ const textLoading = ref(false);
173
+ const textError = ref<string | null>(null);
174
+
175
+ async function loadText() {
176
+ const att = props.attachment;
177
+ if (!att) return;
178
+ textLoading.value = true;
179
+ textError.value = null;
180
+ try {
181
+ let raw: string;
182
+ if (att.file) {
183
+ raw = await att.file.text();
184
+ } else if (resolvedSrc.value) {
185
+ // The resolved URL comes from the consumer's transport (via
186
+ // `resolveUrl`) or was already a usable preview/object URL.
187
+ const res = await fetch(resolvedSrc.value);
188
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
189
+ raw = await res.text();
190
+ } else {
191
+ raw = "";
192
+ }
193
+ const lang = codeLanguage(att.name);
194
+ const isMd = /\.(md|markdown)$/i.test(att.name);
195
+ if (isMd) {
196
+ textContent.value = raw;
197
+ textPlain.value = false;
198
+ } else if (lang) {
199
+ textContent.value = "```" + lang + "\n" + raw + "\n```";
200
+ textPlain.value = false;
201
+ } else {
202
+ textContent.value = raw;
203
+ textPlain.value = true;
204
+ }
205
+ } catch (e) {
206
+ textError.value = e instanceof Error ? e.message : String(e);
207
+ } finally {
208
+ textLoading.value = false;
209
+ }
210
+ }
211
+
212
+ function startTextLoad() {
213
+ if (!props.modelValue || !props.attachment) return;
214
+ if (isText.value) void loadText();
215
+ }
216
+
217
+ watch(
218
+ () => [props.modelValue, props.attachment] as const,
219
+ () => startTextLoad(),
220
+ { immediate: true },
221
+ );
222
+
223
+ // Re-fetch text once the transport URL lands (resolveUrl is async).
224
+ watch(resolvedSrc, () => {
225
+ if (!props.attachment?.file) startTextLoad();
226
+ });
227
+
228
+ function download() {
229
+ if (!resolvedSrc.value) return;
230
+ const a = document.createElement("a");
231
+ a.href = resolvedSrc.value;
232
+ a.download = props.attachment?.name || "download";
233
+ a.click();
234
+ }
235
+
236
+ function fileIcon() {
237
+ const att = props.attachment;
238
+ if (!att) return <FileIcon size={40} />;
239
+ if (att.type.startsWith("image/")) return <ImageIcon size={40} />;
240
+ if (att.type.startsWith("video/")) return <Film size={40} />;
241
+ if (att.type.startsWith("audio/")) return <Music size={40} />;
242
+ return <FileIcon size={40} />;
243
+ }
244
+
245
+ return () => (
246
+ <HModal
247
+ modelValue={props.modelValue}
248
+ onUpdate:modelValue={(v: boolean) => emit("update:modelValue", v)}
249
+ title={props.attachment?.name}
250
+ width="44rem"
251
+ footerActions={[
252
+ { label: t("hikari::attachment.download", "Download"), onClick: download, disabled: !resolvedSrc.value },
253
+ { label: t("hikari::attachment.close", "Close"), variant: "secondary", onClick: () => emit("update:modelValue", false) },
254
+ ]}
255
+ >
256
+ <div class="s-attachment-modal">
257
+ {srcLoading.value && (
258
+ <p class="s-attachment-modal-text-empty">{t("hikari::attachment.loading", "Loading…")}</p>
259
+ )}
260
+
261
+ {/* Image — zoomable viewer with minimap navigator */}
262
+ {!srcLoading.value && kind.value === "image" && resolvedSrc.value && (
263
+ <HImageViewer src={resolvedSrc.value} alt={props.attachment?.name ?? ""} />
264
+ )}
265
+
266
+ {/* Video — hikari media player with control bar */}
267
+ {!srcLoading.value && kind.value === "video" && resolvedSrc.value && (
268
+ <HMediaPlayer type="video" src={resolvedSrc.value} />
269
+ )}
270
+
271
+ {/* Audio — hikari media player with visualizer + control bar */}
272
+ {!srcLoading.value && kind.value === "audio" && resolvedSrc.value && (
273
+ <HMediaPlayer type="audio" src={resolvedSrc.value} />
274
+ )}
275
+
276
+ {/* Text / code — markdown + highlight.js via HMarkdownRenderer */}
277
+ {isText.value && (
278
+ <HScrollContainer class="s-attachment-modal-text">
279
+ {props.attachment?.file || resolvedSrc.value ? (
280
+ <HMarkdownRenderer
281
+ content={textContent.value}
282
+ loading={textLoading.value}
283
+ plain={textPlain.value}
284
+ />
285
+ ) : (
286
+ <p class="s-attachment-modal-text-empty">
287
+ {srcError.value
288
+ ? srcError.value
289
+ : t("hikari::attachment.noPreview", "No preview available.")}
290
+ </p>
291
+ )}
292
+ {textError.value && (
293
+ <p class="s-attachment-modal-text-error">{textError.value}</p>
294
+ )}
295
+ </HScrollContainer>
296
+ )}
297
+
298
+ {/* Generic file */}
299
+ {!srcLoading.value && kind.value === "other" && !isText.value && (
300
+ <div class="s-attachment-modal-file">
301
+ {fileIcon()}
302
+ <p class="s-attachment-modal-file-name">{props.attachment?.name}</p>
303
+ </div>
304
+ )}
305
+
306
+ {srcError.value && kind.value !== "other" && (
307
+ <p class="s-attachment-modal-text-error">{srcError.value}</p>
308
+ )}
309
+
310
+ <div class="s-attachment-modal-meta">
311
+ <HBadge variant="muted">{props.attachment?.type || "unknown"}</HBadge>
312
+ <HBadge variant="muted">{formatBytes(props.attachment?.size ?? 0)}</HBadge>
313
+ </div>
314
+ </div>
315
+ </HModal>
316
+ );
317
+ },
318
+ });
@@ -0,0 +1,50 @@
1
+ import { defineComponent, type PropType } from "vue";
2
+
3
+ /**
4
+ * HkAuthCard — the shared shell of every Celestia auth screen (login,
5
+ * register, setup): centered header (logo/title/subtitle) over a column
6
+ * form body.
7
+ *
8
+ * Slot layout contract:
9
+ * - `footer` renders into `.s-auth-footer`, a flex column sized to its
10
+ * widest row and centered in the card: every slot child is its own row
11
+ * (remember-me, protocol consent, a sign-in link…) and the rows share
12
+ * one left edge. Children must not assume a shared inline line or
13
+ * per-row centering; content that must sit on one row belongs inside
14
+ * one child element.
15
+ * - `logo` swaps the header's logo slot; `default` is the form body.
16
+ */
17
+ export const HkAuthCard = defineComponent({
18
+ name: "HkAuthCard",
19
+ props: {
20
+ title: { type: String, required: true },
21
+ subtitle: { type: String, default: "" },
22
+ },
23
+ setup(props, { slots }) {
24
+ return () => (
25
+ <div class="s-auth-card">
26
+ {slots.logo && (
27
+ <div class="s-auth-header">
28
+ {slots.logo()}
29
+ <h1 class="s-auth-title">{props.title}</h1>
30
+ {props.subtitle && <p class="s-auth-subtitle">{props.subtitle}</p>}
31
+ </div>
32
+ )}
33
+ {!slots.logo && (
34
+ <div class="s-auth-header">
35
+ <h1 class="s-auth-title">{props.title}</h1>
36
+ {props.subtitle && <p class="s-auth-subtitle">{props.subtitle}</p>}
37
+ </div>
38
+ )}
39
+ <div class="s-auth-form">
40
+ {slots.default?.()}
41
+ </div>
42
+ {slots.footer && (
43
+ <div class="s-auth-footer">
44
+ {slots.footer()}
45
+ </div>
46
+ )}
47
+ </div>
48
+ );
49
+ },
50
+ });
@@ -0,0 +1,12 @@
1
+ .s-auth-submit {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 6px;
5
+ width: 100%;
6
+ }
7
+
8
+ .s-auth-submit-error {
9
+ margin: 0;
10
+ font-size: 0.8125rem;
11
+ color: rgb(var(--color-danger, 229 62 62));
12
+ }
@@ -0,0 +1,94 @@
1
+ import { computed, defineComponent, ref, type PropType } from "vue";
2
+ import { HButton, useI18n } from "@celestia-island/hikari";
3
+ import { solvePow, type PowChallenge, type PowSolution } from "../utils/pow";
4
+
5
+ export interface AuthSubmitContext {
6
+ /** Solved PoW for the challenge the backend issued (if any). */
7
+ pow?: PowSolution;
8
+ /** Captcha verification token (if the flow requires one). */
9
+ captchaToken?: string | null;
10
+ }
11
+
12
+ /**
13
+ * Submit button with an auth-challenge lifecycle (the plana-ui half of the
14
+ * auth kit; hikari provides the base input variants, the backend provides
15
+ * the challenge/captcha facilities this wraps):
16
+ *
17
+ * 1. `onGetChallenge` (optional) — ask the backend for a `{ seed, bits }`
18
+ * PoW challenge.
19
+ * 2. Solve it with the built-in `solvePow` (or a custom `solver`).
20
+ * 3. `onCaptcha` (optional) — acquire a captcha verification token.
21
+ * 4. Call `onSubmit` with the assembled `AuthSubmitContext`.
22
+ *
23
+ * While solving, the button shows its own spinner; `loading` stays
24
+ * external so callers can reflect server-side submission progress.
25
+ */
26
+ export default defineComponent({
27
+ name: "HkAuthSubmitButton",
28
+ props: {
29
+ label: { type: String, required: true },
30
+ loading: { type: Boolean, default: false },
31
+ disabled: { type: Boolean, default: false },
32
+ error: { type: String, default: undefined },
33
+ block: { type: Boolean, default: false },
34
+ size: { type: String as () => "sm" | "md" | "lg", default: "md" },
35
+ variant: { type: String as () => "primary" | "secondary" | "ghost", default: "primary" },
36
+ /** Fetch the backend PoW challenge; return null to skip PoW. */
37
+ onGetChallenge: { type: Function as PropType<() => Promise<PowChallenge | null>>, default: undefined },
38
+ /** Custom solver; defaults to the built-in subtle-based solvePow. */
39
+ solver: { type: Function as PropType<(c: PowChallenge) => Promise<number>>, default: undefined },
40
+ /** Acquire a captcha token; return null to skip captcha. */
41
+ onCaptcha: { type: Function as PropType<() => Promise<string | null>>, default: undefined },
42
+ /** Invoked with the assembled challenge context when ready. */
43
+ doSubmit: { type: Function as PropType<(ctx: AuthSubmitContext) => Promise<void>>, required: true },
44
+ },
45
+ emits: {
46
+ error: (_message: string) => true,
47
+ },
48
+ setup(props, { emit }) {
49
+ const { t } = useI18n();
50
+ const solving = ref(false);
51
+
52
+ const busy = computed(() => props.loading || solving.value);
53
+
54
+ async function handleClick() {
55
+ if (busy.value) return;
56
+ solving.value = true;
57
+ try {
58
+ const ctx: AuthSubmitContext = { captchaToken: null };
59
+ const challenge = props.onGetChallenge ? await props.onGetChallenge() : null;
60
+ if (challenge && challenge.bits > 0) {
61
+ const solve = props.solver ?? solvePow;
62
+ const counter = await solve(challenge);
63
+ ctx.pow = { seed: challenge.seed, bits: challenge.bits, counter };
64
+ }
65
+ if (props.onCaptcha) {
66
+ ctx.captchaToken = await props.onCaptcha();
67
+ }
68
+ await props.doSubmit(ctx);
69
+ } catch (e) {
70
+ emit("error", e instanceof Error ? e.message : String(e));
71
+ } finally {
72
+ solving.value = false;
73
+ }
74
+ }
75
+
76
+ return () => (
77
+ <div class="s-auth-submit">
78
+ <HButton
79
+ variant={props.variant}
80
+ size={props.size}
81
+ block={props.block}
82
+ loading={busy.value}
83
+ disabled={props.disabled || busy.value}
84
+ onClick={() => void handleClick()}
85
+ >
86
+ {solving.value
87
+ ? t("hikari::auth.solvingChallenge", "Verifying challenge…")
88
+ : props.label}
89
+ </HButton>
90
+ {props.error && <p class="s-auth-submit-error">{props.error}</p>}
91
+ </div>
92
+ );
93
+ },
94
+ });