@ohmykreee/dsh-remote-control 0.0.0-stage → 1.0.2

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 (373) hide show
  1. package/AGENTS.md +556 -0
  2. package/INSTALLATION.md +310 -0
  3. package/LICENSE +21 -0
  4. package/README.md +195 -2
  5. package/client.js +3 -0
  6. package/components.json +20 -0
  7. package/cordis.patch.yml +3 -0
  8. package/docs/README.en.md +196 -0
  9. package/docs/screenshots/en/overview.png +0 -0
  10. package/docs/screenshots/en/question.png +0 -0
  11. package/docs/screenshots/zh/overview.png +0 -0
  12. package/docs/screenshots/zh/question.png +0 -0
  13. package/icon.svg +24 -0
  14. package/index.js +2 -0
  15. package/lib/account.js +1 -0
  16. package/lib/app-mapping.js +1 -0
  17. package/lib/i18n.js +1 -0
  18. package/lib/media-enrich.js +1 -0
  19. package/lib/notifications.js +1 -0
  20. package/lib/performance.js +1 -0
  21. package/lib/platform/index.js +1 -0
  22. package/lib/probe-adapter.js +1 -0
  23. package/lib/probe-daemon.js +1 -0
  24. package/lib/probe.js +1 -0
  25. package/lib/qr.js +1 -0
  26. package/lib/runtime.js +1 -0
  27. package/lib/service.js +1 -0
  28. package/lib/sessions.js +1 -0
  29. package/lib/settings.js +1 -0
  30. package/lib/types.js +1 -0
  31. package/lib/util.js +1 -0
  32. package/lib/weather.js +1 -0
  33. package/lib/ws.js +1 -0
  34. package/locale/en.json +6 -0
  35. package/locale/zh.json +6 -0
  36. package/package.json +118 -4
  37. package/scripts/browser-shot.mjs +217 -0
  38. package/scripts/build-dsh.mjs +228 -0
  39. package/scripts/build-rust.mjs +319 -0
  40. package/scripts/build-web.mjs +17 -0
  41. package/scripts/check-npm-package.mjs +322 -0
  42. package/scripts/mock-preview.py +574 -0
  43. package/scripts/screenshot-compose.py +89 -0
  44. package/scripts/screenshot-device.sh +75 -0
  45. package/src/dsh/client/api.ts +68 -0
  46. package/src/dsh/client/i18n.ts +573 -0
  47. package/src/dsh/client/icons.ts +56 -0
  48. package/src/dsh/client/index.ts +95 -0
  49. package/src/dsh/client/logic.ts +310 -0
  50. package/src/dsh/client/panel.ts +483 -0
  51. package/src/dsh/client/sections/apps.ts +40 -0
  52. package/src/dsh/client/sections/balance.ts +38 -0
  53. package/src/dsh/client/sections/connection.ts +97 -0
  54. package/src/dsh/client/sections/notifications.ts +43 -0
  55. package/src/dsh/client/sections/pomodoro.ts +184 -0
  56. package/src/dsh/client/sections/preferences.ts +138 -0
  57. package/src/dsh/client/sections/system.ts +120 -0
  58. package/src/dsh/client/sections/weather.ts +276 -0
  59. package/src/dsh/client/styles.ts +1119 -0
  60. package/src/dsh/client/types.ts +366 -0
  61. package/src/dsh/client/ui.ts +142 -0
  62. package/src/dsh/index.ts +108 -0
  63. package/src/dsh/lib/account.ts +520 -0
  64. package/src/dsh/lib/app-mapping.ts +366 -0
  65. package/src/dsh/lib/i18n.ts +82 -0
  66. package/src/dsh/lib/media-enrich.ts +202 -0
  67. package/src/dsh/lib/notifications.ts +322 -0
  68. package/src/dsh/lib/performance.ts +279 -0
  69. package/src/dsh/lib/platform/index.ts +43 -0
  70. package/src/dsh/lib/probe-adapter.ts +275 -0
  71. package/src/dsh/lib/probe-daemon.ts +359 -0
  72. package/src/dsh/lib/probe.ts +395 -0
  73. package/src/dsh/lib/qr.ts +42 -0
  74. package/src/dsh/lib/runtime.ts +126 -0
  75. package/src/dsh/lib/service.ts +1645 -0
  76. package/src/dsh/lib/sessions.ts +907 -0
  77. package/src/dsh/lib/settings.ts +468 -0
  78. package/src/dsh/lib/types.ts +231 -0
  79. package/src/dsh/lib/util.ts +115 -0
  80. package/src/dsh/lib/weather.ts +532 -0
  81. package/src/dsh/lib/ws.ts +328 -0
  82. package/src/dsh/types/qrcode-generator.d.ts +16 -0
  83. package/src/rust/Cargo.lock +1431 -0
  84. package/src/rust/Cargo.toml +68 -0
  85. package/src/rust/src/cli.rs +233 -0
  86. package/src/rust/src/daemon.rs +140 -0
  87. package/src/rust/src/location/linux.rs +118 -0
  88. package/src/rust/src/location/macos.rs +26 -0
  89. package/src/rust/src/location/mod.rs +170 -0
  90. package/src/rust/src/location/windows.rs +205 -0
  91. package/src/rust/src/main.rs +42 -0
  92. package/src/rust/src/media/catalog.rs +454 -0
  93. package/src/rust/src/media/linux.rs +350 -0
  94. package/src/rust/src/media/macos/accessibility.rs +59 -0
  95. package/src/rust/src/media/macos.rs +366 -0
  96. package/src/rust/src/media/mod.rs +298 -0
  97. package/src/rust/src/media/vendor/apple_music.rs +90 -0
  98. package/src/rust/src/media/vendor/common.rs +186 -0
  99. package/src/rust/src/media/vendor/kugou.rs +52 -0
  100. package/src/rust/src/media/vendor/kuwo.rs +49 -0
  101. package/src/rust/src/media/vendor/mod.rs +75 -0
  102. package/src/rust/src/media/vendor/netease.rs +1715 -0
  103. package/src/rust/src/media/vendor/qishui.rs +36 -0
  104. package/src/rust/src/media/vendor/qqmusic.rs +33 -0
  105. package/src/rust/src/media/vendor/spotify.rs +46 -0
  106. package/src/rust/src/media/vendor/wesing.rs +39 -0
  107. package/src/rust/src/media/vendor/youtube_music.rs +49 -0
  108. package/src/rust/src/media/windows/accessibility.rs +85 -0
  109. package/src/rust/src/media/windows/audio.rs +116 -0
  110. package/src/rust/src/media/windows/private.rs +291 -0
  111. package/src/rust/src/media/windows.rs +775 -0
  112. package/src/rust/src/notifications/linux.rs +276 -0
  113. package/src/rust/src/notifications/macos.rs +382 -0
  114. package/src/rust/src/notifications/mod.rs +244 -0
  115. package/src/rust/src/notifications/windows.rs +565 -0
  116. package/src/rust/src/perf/common.rs +109 -0
  117. package/src/rust/src/perf/linux.rs +282 -0
  118. package/src/rust/src/perf/macos.rs +28 -0
  119. package/src/rust/src/perf/mod.rs +157 -0
  120. package/src/rust/src/perf/windows.rs +981 -0
  121. package/src/rust/src/util.rs +360 -0
  122. package/src/rust/src/volume/linux.rs +195 -0
  123. package/src/rust/src/volume/macos.rs +54 -0
  124. package/src/rust/src/volume/mod.rs +116 -0
  125. package/src/rust/src/volume/windows.rs +75 -0
  126. package/src/rust/tests/cli.rs +79 -0
  127. package/src/rust/tests/daemon.rs +107 -0
  128. package/src/svelte/App.svelte +385 -0
  129. package/src/svelte/index.html +18 -0
  130. package/src/svelte/lib/billing.svelte.ts +228 -0
  131. package/src/svelte/lib/components/BalanceCard.svelte +173 -0
  132. package/src/svelte/lib/components/ClockCard.svelte +183 -0
  133. package/src/svelte/lib/components/CountdownCard.svelte +261 -0
  134. package/src/svelte/lib/components/CpuMemoryCard.svelte +238 -0
  135. package/src/svelte/lib/components/EmptyState.svelte +34 -0
  136. package/src/svelte/lib/components/GpuCard.svelte +245 -0
  137. package/src/svelte/lib/components/HomePage.svelte +34 -0
  138. package/src/svelte/lib/components/MusicControls.svelte +256 -0
  139. package/src/svelte/lib/components/MusicCover.svelte +88 -0
  140. package/src/svelte/lib/components/MusicPage.svelte +116 -0
  141. package/src/svelte/lib/components/NotificationsCard.svelte +109 -0
  142. package/src/svelte/lib/components/Panel.svelte +22 -0
  143. package/src/svelte/lib/components/PerformancePage.svelte +55 -0
  144. package/src/svelte/lib/components/PomodoroCard.svelte +250 -0
  145. package/src/svelte/lib/components/QuestionSheet.svelte +354 -0
  146. package/src/svelte/lib/components/RadialGauge.svelte +99 -0
  147. package/src/svelte/lib/components/Rail.svelte +217 -0
  148. package/src/svelte/lib/components/SectionHeader.svelte +40 -0
  149. package/src/svelte/lib/components/SeekBar.svelte +421 -0
  150. package/src/svelte/lib/components/SessionList.svelte +364 -0
  151. package/src/svelte/lib/components/SessionStats.svelte +98 -0
  152. package/src/svelte/lib/components/SessionsPage.svelte +61 -0
  153. package/src/svelte/lib/components/SpendCard.svelte +105 -0
  154. package/src/svelte/lib/components/ThermalCard.svelte +249 -0
  155. package/src/svelte/lib/components/Toast.svelte +32 -0
  156. package/src/svelte/lib/components/WeatherIcon.svelte +40 -0
  157. package/src/svelte/lib/components/WeatherPanel.svelte +420 -0
  158. package/src/svelte/lib/components/ui/alert/alert-action.svelte +20 -0
  159. package/src/svelte/lib/components/ui/alert/alert-description.svelte +23 -0
  160. package/src/svelte/lib/components/ui/alert/alert-title.svelte +23 -0
  161. package/src/svelte/lib/components/ui/alert/alert.svelte +43 -0
  162. package/src/svelte/lib/components/ui/alert/index.ts +17 -0
  163. package/src/svelte/lib/components/ui/avatar/avatar-badge.svelte +26 -0
  164. package/src/svelte/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
  165. package/src/svelte/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
  166. package/src/svelte/lib/components/ui/avatar/avatar-group.svelte +23 -0
  167. package/src/svelte/lib/components/ui/avatar/avatar-image.svelte +17 -0
  168. package/src/svelte/lib/components/ui/avatar/avatar.svelte +26 -0
  169. package/src/svelte/lib/components/ui/avatar/index.ts +22 -0
  170. package/src/svelte/lib/components/ui/badge/badge.svelte +49 -0
  171. package/src/svelte/lib/components/ui/badge/index.ts +2 -0
  172. package/src/svelte/lib/components/ui/button/button.svelte +82 -0
  173. package/src/svelte/lib/components/ui/button/index.ts +17 -0
  174. package/src/svelte/lib/components/ui/card/card-action.svelte +23 -0
  175. package/src/svelte/lib/components/ui/card/card-content.svelte +20 -0
  176. package/src/svelte/lib/components/ui/card/card-description.svelte +20 -0
  177. package/src/svelte/lib/components/ui/card/card-footer.svelte +20 -0
  178. package/src/svelte/lib/components/ui/card/card-header.svelte +23 -0
  179. package/src/svelte/lib/components/ui/card/card-title.svelte +20 -0
  180. package/src/svelte/lib/components/ui/card/card.svelte +22 -0
  181. package/src/svelte/lib/components/ui/card/index.ts +25 -0
  182. package/src/svelte/lib/components/ui/dialog/dialog-close.svelte +11 -0
  183. package/src/svelte/lib/components/ui/dialog/dialog-content.svelte +48 -0
  184. package/src/svelte/lib/components/ui/dialog/dialog-description.svelte +17 -0
  185. package/src/svelte/lib/components/ui/dialog/dialog-footer.svelte +32 -0
  186. package/src/svelte/lib/components/ui/dialog/dialog-header.svelte +20 -0
  187. package/src/svelte/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
  188. package/src/svelte/lib/components/ui/dialog/dialog-portal.svelte +7 -0
  189. package/src/svelte/lib/components/ui/dialog/dialog-title.svelte +17 -0
  190. package/src/svelte/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
  191. package/src/svelte/lib/components/ui/dialog/dialog.svelte +7 -0
  192. package/src/svelte/lib/components/ui/dialog/index.ts +34 -0
  193. package/src/svelte/lib/components/ui/empty/empty-content.svelte +23 -0
  194. package/src/svelte/lib/components/ui/empty/empty-description.svelte +23 -0
  195. package/src/svelte/lib/components/ui/empty/empty-header.svelte +20 -0
  196. package/src/svelte/lib/components/ui/empty/empty-media.svelte +41 -0
  197. package/src/svelte/lib/components/ui/empty/empty-title.svelte +20 -0
  198. package/src/svelte/lib/components/ui/empty/empty.svelte +23 -0
  199. package/src/svelte/lib/components/ui/empty/index.ts +22 -0
  200. package/src/svelte/lib/components/ui/input/index.ts +7 -0
  201. package/src/svelte/lib/components/ui/input/input.svelte +48 -0
  202. package/src/svelte/lib/components/ui/progress/index.ts +7 -0
  203. package/src/svelte/lib/components/ui/progress/progress.svelte +27 -0
  204. package/src/svelte/lib/components/ui/scroll-area/index.ts +10 -0
  205. package/src/svelte/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte +30 -0
  206. package/src/svelte/lib/components/ui/scroll-area/scroll-area.svelte +43 -0
  207. package/src/svelte/lib/components/ui/separator/index.ts +7 -0
  208. package/src/svelte/lib/components/ui/separator/separator.svelte +21 -0
  209. package/src/svelte/lib/components/ui/sheet/index.ts +34 -0
  210. package/src/svelte/lib/components/ui/sheet/sheet-close.svelte +7 -0
  211. package/src/svelte/lib/components/ui/sheet/sheet-content.svelte +55 -0
  212. package/src/svelte/lib/components/ui/sheet/sheet-description.svelte +17 -0
  213. package/src/svelte/lib/components/ui/sheet/sheet-footer.svelte +20 -0
  214. package/src/svelte/lib/components/ui/sheet/sheet-header.svelte +20 -0
  215. package/src/svelte/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
  216. package/src/svelte/lib/components/ui/sheet/sheet-portal.svelte +7 -0
  217. package/src/svelte/lib/components/ui/sheet/sheet-title.svelte +17 -0
  218. package/src/svelte/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
  219. package/src/svelte/lib/components/ui/sheet/sheet.svelte +7 -0
  220. package/src/svelte/lib/components/ui/skeleton/index.ts +7 -0
  221. package/src/svelte/lib/components/ui/skeleton/skeleton.svelte +17 -0
  222. package/src/svelte/lib/components/ui/slider/index.ts +7 -0
  223. package/src/svelte/lib/components/ui/slider/slider.svelte +52 -0
  224. package/src/svelte/lib/components/ui/tooltip/index.ts +19 -0
  225. package/src/svelte/lib/components/ui/tooltip/tooltip-content.svelte +51 -0
  226. package/src/svelte/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
  227. package/src/svelte/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
  228. package/src/svelte/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  229. package/src/svelte/lib/components/ui/tooltip/tooltip.svelte +7 -0
  230. package/src/svelte/lib/countdown.svelte.ts +252 -0
  231. package/src/svelte/lib/format.ts +199 -0
  232. package/src/svelte/lib/i18n.svelte.ts +583 -0
  233. package/src/svelte/lib/media.ts +32 -0
  234. package/src/svelte/lib/pages.ts +18 -0
  235. package/src/svelte/lib/pomodoro.svelte.ts +223 -0
  236. package/src/svelte/lib/session-tree.ts +80 -0
  237. package/src/svelte/lib/state.svelte.ts +359 -0
  238. package/src/svelte/lib/timer-alarm.svelte.ts +319 -0
  239. package/src/svelte/lib/types.ts +306 -0
  240. package/src/svelte/lib/utils.ts +8 -0
  241. package/src/svelte/lib/weather.ts +84 -0
  242. package/src/svelte/lib/ws.ts +104 -0
  243. package/src/svelte/main.ts +13 -0
  244. package/src/svelte/styles/global.css +315 -0
  245. package/src/svelte/vite-env.d.ts +12 -0
  246. package/static/fonts/LICENSE.txt +287 -0
  247. package/static/icon.svg +24 -0
  248. package/static/manifest.webmanifest +18 -0
  249. package/svelte.config.js +5 -0
  250. package/tsconfig.dsh-client.json +18 -0
  251. package/tsconfig.dsh.json +21 -0
  252. package/tsconfig.json +20 -0
  253. package/vite.config.js +31 -0
  254. package/web/assets/index-DLdSfnJ3.css +1 -0
  255. package/web/assets/index-YNE8X0Uy.js +6 -0
  256. package/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  257. package/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  258. package/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  259. package/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  260. package/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  261. package/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  262. package/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  263. package/web/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2 +0 -0
  264. package/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  265. package/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  266. package/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  267. package/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  268. package/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  269. package/web/assets/noto-sans-sc-100-wght-normal-DluiXYzi.woff2 +0 -0
  270. package/web/assets/noto-sans-sc-101-wght-normal-CPkZHnw0.woff2 +0 -0
  271. package/web/assets/noto-sans-sc-102-wght-normal-gzIulUH6.woff2 +0 -0
  272. package/web/assets/noto-sans-sc-103-wght-normal-CJfSBxFo.woff2 +0 -0
  273. package/web/assets/noto-sans-sc-104-wght-normal-B3QMglgE.woff2 +0 -0
  274. package/web/assets/noto-sans-sc-105-wght-normal-xXh_OMfk.woff2 +0 -0
  275. package/web/assets/noto-sans-sc-106-wght-normal-B9n3jMjP.woff2 +0 -0
  276. package/web/assets/noto-sans-sc-107-wght-normal-D7fvVprN.woff2 +0 -0
  277. package/web/assets/noto-sans-sc-108-wght-normal-J0_LhxVo.woff2 +0 -0
  278. package/web/assets/noto-sans-sc-109-wght-normal-Dsp-De_0.woff2 +0 -0
  279. package/web/assets/noto-sans-sc-110-wght-normal-DE_6NUrb.woff2 +0 -0
  280. package/web/assets/noto-sans-sc-111-wght-normal-C4S-TlTn.woff2 +0 -0
  281. package/web/assets/noto-sans-sc-112-wght-normal-BcFuP3w6.woff2 +0 -0
  282. package/web/assets/noto-sans-sc-113-wght-normal-Dk932kmj.woff2 +0 -0
  283. package/web/assets/noto-sans-sc-114-wght-normal-CO_5Xo-i.woff2 +0 -0
  284. package/web/assets/noto-sans-sc-115-wght-normal-BWy07H14.woff2 +0 -0
  285. package/web/assets/noto-sans-sc-116-wght-normal-B4HVAfHo.woff2 +0 -0
  286. package/web/assets/noto-sans-sc-117-wght-normal-siZzFqgK.woff2 +0 -0
  287. package/web/assets/noto-sans-sc-118-wght-normal-xhBB7f6V.woff2 +0 -0
  288. package/web/assets/noto-sans-sc-119-wght-normal-Ck_g2FGz.woff2 +0 -0
  289. package/web/assets/noto-sans-sc-21-wght-normal-D0x9-yBt.woff2 +0 -0
  290. package/web/assets/noto-sans-sc-22-wght-normal-BBUVTShg.woff2 +0 -0
  291. package/web/assets/noto-sans-sc-23-wght-normal-Du1oYWkE.woff2 +0 -0
  292. package/web/assets/noto-sans-sc-24-wght-normal-CmWjJ0gt.woff2 +0 -0
  293. package/web/assets/noto-sans-sc-25-wght-normal-BFHiqilg.woff2 +0 -0
  294. package/web/assets/noto-sans-sc-26-wght-normal-C0sF2Z2f.woff2 +0 -0
  295. package/web/assets/noto-sans-sc-27-wght-normal-COFdrquJ.woff2 +0 -0
  296. package/web/assets/noto-sans-sc-28-wght-normal-DMgdNu1o.woff2 +0 -0
  297. package/web/assets/noto-sans-sc-29-wght-normal-Cmq8em30.woff2 +0 -0
  298. package/web/assets/noto-sans-sc-30-wght-normal-CZWqZ3yU.woff2 +0 -0
  299. package/web/assets/noto-sans-sc-31-wght-normal-Wfi_lutW.woff2 +0 -0
  300. package/web/assets/noto-sans-sc-32-wght-normal-BRJQ4PY5.woff2 +0 -0
  301. package/web/assets/noto-sans-sc-33-wght-normal-CUVQXNAJ.woff2 +0 -0
  302. package/web/assets/noto-sans-sc-34-wght-normal-Bspba0wC.woff2 +0 -0
  303. package/web/assets/noto-sans-sc-35-wght-normal-K8fX_YPG.woff2 +0 -0
  304. package/web/assets/noto-sans-sc-36-wght-normal-CkITyjoT.woff2 +0 -0
  305. package/web/assets/noto-sans-sc-37-wght-normal-WCZ2zfEe.woff2 +0 -0
  306. package/web/assets/noto-sans-sc-38-wght-normal-DeM7CUce.woff2 +0 -0
  307. package/web/assets/noto-sans-sc-39-wght-normal-BMZNLWQU.woff2 +0 -0
  308. package/web/assets/noto-sans-sc-4-wght-normal-AR5bdDjz.woff2 +0 -0
  309. package/web/assets/noto-sans-sc-40-wght-normal-BQ5xpS5j.woff2 +0 -0
  310. package/web/assets/noto-sans-sc-41-wght-normal-Hthm7FFi.woff2 +0 -0
  311. package/web/assets/noto-sans-sc-42-wght-normal-eaS2gKds.woff2 +0 -0
  312. package/web/assets/noto-sans-sc-43-wght-normal-o-T53XXV.woff2 +0 -0
  313. package/web/assets/noto-sans-sc-44-wght-normal-BJH5J4Ez.woff2 +0 -0
  314. package/web/assets/noto-sans-sc-45-wght-normal-BKuxSCOJ.woff2 +0 -0
  315. package/web/assets/noto-sans-sc-46-wght-normal-BR_5Mu6e.woff2 +0 -0
  316. package/web/assets/noto-sans-sc-47-wght-normal-CyY7gKNX.woff2 +0 -0
  317. package/web/assets/noto-sans-sc-48-wght-normal-BNMfTR6k.woff2 +0 -0
  318. package/web/assets/noto-sans-sc-49-wght-normal-DX-0y7wg.woff2 +0 -0
  319. package/web/assets/noto-sans-sc-5-wght-normal-CfWugllB.woff2 +0 -0
  320. package/web/assets/noto-sans-sc-50-wght-normal-DAOGW-Dr.woff2 +0 -0
  321. package/web/assets/noto-sans-sc-51-wght-normal-BlFLOjcP.woff2 +0 -0
  322. package/web/assets/noto-sans-sc-52-wght-normal-OZjrybnw.woff2 +0 -0
  323. package/web/assets/noto-sans-sc-53-wght-normal-CwyWNT6x.woff2 +0 -0
  324. package/web/assets/noto-sans-sc-54-wght-normal-DM0nRe18.woff2 +0 -0
  325. package/web/assets/noto-sans-sc-55-wght-normal-DBK2wUOR.woff2 +0 -0
  326. package/web/assets/noto-sans-sc-56-wght-normal-C-VKK1LS.woff2 +0 -0
  327. package/web/assets/noto-sans-sc-57-wght-normal-Cdbfh5kq.woff2 +0 -0
  328. package/web/assets/noto-sans-sc-58-wght-normal-CflMKARZ.woff2 +0 -0
  329. package/web/assets/noto-sans-sc-59-wght-normal-BS4Kwzxx.woff2 +0 -0
  330. package/web/assets/noto-sans-sc-6-wght-normal-ChnZIQfQ.woff2 +0 -0
  331. package/web/assets/noto-sans-sc-60-wght-normal-B7QOYsXJ.woff2 +0 -0
  332. package/web/assets/noto-sans-sc-61-wght-normal-C8JjQS7w.woff2 +0 -0
  333. package/web/assets/noto-sans-sc-62-wght-normal-bS4wq1Gg.woff2 +0 -0
  334. package/web/assets/noto-sans-sc-63-wght-normal-BJr8P-OB.woff2 +0 -0
  335. package/web/assets/noto-sans-sc-64-wght-normal-DVB11t7j.woff2 +0 -0
  336. package/web/assets/noto-sans-sc-65-wght-normal-DMPCXBvK.woff2 +0 -0
  337. package/web/assets/noto-sans-sc-66-wght-normal-BobaG10V.woff2 +0 -0
  338. package/web/assets/noto-sans-sc-67-wght-normal-Dzk7WhCR.woff2 +0 -0
  339. package/web/assets/noto-sans-sc-68-wght-normal-DGbCD9xu.woff2 +0 -0
  340. package/web/assets/noto-sans-sc-69-wght-normal-Ctrv1EqA.woff2 +0 -0
  341. package/web/assets/noto-sans-sc-70-wght-normal-RgTO941D.woff2 +0 -0
  342. package/web/assets/noto-sans-sc-71-wght-normal-D9sG-CWv.woff2 +0 -0
  343. package/web/assets/noto-sans-sc-72-wght-normal-C4oWy3ul.woff2 +0 -0
  344. package/web/assets/noto-sans-sc-73-wght-normal-B1ZoOTgM.woff2 +0 -0
  345. package/web/assets/noto-sans-sc-74-wght-normal-F7dH_rZJ.woff2 +0 -0
  346. package/web/assets/noto-sans-sc-75-wght-normal-Cp2wp4si.woff2 +0 -0
  347. package/web/assets/noto-sans-sc-76-wght-normal-B494D7xS.woff2 +0 -0
  348. package/web/assets/noto-sans-sc-77-wght-normal-B3MVY9Ax.woff2 +0 -0
  349. package/web/assets/noto-sans-sc-78-wght-normal-5jX7iNMC.woff2 +0 -0
  350. package/web/assets/noto-sans-sc-79-wght-normal-BIoZy8fr.woff2 +0 -0
  351. package/web/assets/noto-sans-sc-80-wght-normal-Bje1Fxy1.woff2 +0 -0
  352. package/web/assets/noto-sans-sc-81-wght-normal-B2NN7Qi3.woff2 +0 -0
  353. package/web/assets/noto-sans-sc-82-wght-normal-CUggNZi4.woff2 +0 -0
  354. package/web/assets/noto-sans-sc-83-wght-normal-99rD8YO-.woff2 +0 -0
  355. package/web/assets/noto-sans-sc-84-wght-normal-zMBmUocm.woff2 +0 -0
  356. package/web/assets/noto-sans-sc-85-wght-normal-CjI3qdxG.woff2 +0 -0
  357. package/web/assets/noto-sans-sc-86-wght-normal-BHY8TVEH.woff2 +0 -0
  358. package/web/assets/noto-sans-sc-87-wght-normal-DioZSHnU.woff2 +0 -0
  359. package/web/assets/noto-sans-sc-88-wght-normal-BXYxwf2t.woff2 +0 -0
  360. package/web/assets/noto-sans-sc-89-wght-normal-ClMFrFaL.woff2 +0 -0
  361. package/web/assets/noto-sans-sc-90-wght-normal-BnOlPreP.woff2 +0 -0
  362. package/web/assets/noto-sans-sc-91-wght-normal-DTr9RQcS.woff2 +0 -0
  363. package/web/assets/noto-sans-sc-97-wght-normal-9u6Aiav-.woff2 +0 -0
  364. package/web/assets/noto-sans-sc-98-wght-normal-BR-H2C0V.woff2 +0 -0
  365. package/web/assets/noto-sans-sc-99-wght-normal-BI6XPoxp.woff2 +0 -0
  366. package/web/assets/noto-sans-sc-cyrillic-wght-normal-BGmsQlw-.woff2 +0 -0
  367. package/web/assets/noto-sans-sc-latin-ext-wght-normal-DuJTpTg_.woff2 +0 -0
  368. package/web/assets/noto-sans-sc-latin-wght-normal-DDW8mKAR.woff2 +0 -0
  369. package/web/assets/noto-sans-sc-vietnamese-wght-normal-Bi3ATJ76.woff2 +0 -0
  370. package/web/fonts/LICENSE.txt +287 -0
  371. package/web/icon.svg +24 -0
  372. package/web/index.html +19 -0
  373. package/web/manifest.webmanifest +18 -0
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Shared alert sound and page-tab indicator for the console timers.
3
+ *
4
+ * The pomodoro and the countdown live in the same browser tab and only one can
5
+ * be enabled at a time, so a single alarm engine is enough. Timer modules own
6
+ * the remaining time and the enabled state; this module only knows how to
7
+ * schedule, loop, and stop the configured alert.
8
+ */
9
+
10
+ let audioContext: AudioContext | null = null;
11
+ let scheduled: { endsAt: number; nodes: OscillatorNode[] } | null = null;
12
+ let loopTimer: ReturnType<typeof setTimeout> | null = null;
13
+ let loopActive = false;
14
+ let alarmAudio: HTMLAudioElement | null = null;
15
+ let activeChimeNodes: OscillatorNode[] = [];
16
+ let alerting = false;
17
+ let originalTitle = '';
18
+ let originalIcon = '';
19
+
20
+ /** Reactive mirror of the page-tab alert, consumed by the console rail. */
21
+ export const tabAlert = $state({ active: false });
22
+
23
+ /* ------------------------------------------------------------------ sound */
24
+
25
+ function ensureAudio(): AudioContext | null {
26
+ try {
27
+ if (!audioContext) {
28
+ const Ctor = window.AudioContext
29
+ || (window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
30
+ if (!Ctor) return null;
31
+ audioContext = new Ctor();
32
+ }
33
+ if (audioContext.state === 'suspended') void audioContext.resume();
34
+ return audioContext;
35
+ } catch {
36
+ return null;
37
+ }
38
+ }
39
+
40
+ /** Resume the audio context inside the start-button gesture. */
41
+ export function primeAudio(): void {
42
+ ensureAudio();
43
+ }
44
+
45
+ /**
46
+ * Schedule a soft major-arpeggio chime. Sine fundamentals plus quiet octave
47
+ * triangles and a low-pass keep it gentle rather than a harsh alarm beep.
48
+ */
49
+ function buildChime(delaySeconds: number): OscillatorNode[] {
50
+ const context = ensureAudio();
51
+ if (!context) return [];
52
+ const now = context.currentTime + Math.max(0.02, delaySeconds);
53
+ const master = context.createGain();
54
+ master.gain.value = 0.16;
55
+ const filter = context.createBiquadFilter();
56
+ filter.type = 'lowpass';
57
+ filter.frequency.value = 4200;
58
+ filter.Q.value = 0.4;
59
+ filter.connect(master);
60
+ master.connect(context.destination);
61
+ const notes = [523.25, 659.25, 783.99, 1046.5];
62
+ const nodes: OscillatorNode[] = [];
63
+ const phrase = (offset: number): void => {
64
+ notes.forEach((frequency, index) => {
65
+ const at = now + offset + index * 0.22;
66
+ const gain = context.createGain();
67
+ gain.gain.setValueAtTime(0.0001, at);
68
+ gain.gain.exponentialRampToValueAtTime(0.9, at + 0.02);
69
+ gain.gain.exponentialRampToValueAtTime(0.0001, at + 1.05);
70
+ const fundamental = context.createOscillator();
71
+ fundamental.type = 'sine';
72
+ fundamental.frequency.value = frequency;
73
+ fundamental.connect(gain);
74
+ const shimmer = context.createOscillator();
75
+ shimmer.type = 'triangle';
76
+ shimmer.frequency.value = frequency * 2;
77
+ const shimmerGain = context.createGain();
78
+ shimmerGain.gain.value = 0.1;
79
+ shimmer.connect(shimmerGain);
80
+ shimmerGain.connect(gain);
81
+ gain.connect(filter);
82
+ fundamental.start(at);
83
+ shimmer.start(at);
84
+ fundamental.stop(at + 1.1);
85
+ shimmer.stop(at + 1.1);
86
+ nodes.push(fundamental, shimmer);
87
+ });
88
+ };
89
+ phrase(0);
90
+ phrase(1.08);
91
+ return nodes;
92
+ }
93
+
94
+ function stopNodes(nodes: OscillatorNode[]): void {
95
+ for (const node of nodes) {
96
+ try {
97
+ node.stop(0);
98
+ } catch {
99
+ /* already stopped */
100
+ }
101
+ }
102
+ }
103
+
104
+ function stopActiveChime(): void {
105
+ stopNodes(activeChimeNodes);
106
+ activeChimeNodes = [];
107
+ }
108
+
109
+ /** Start one built-in chime that can be cut off immediately on dismiss. */
110
+ function playChimeNow(): void {
111
+ stopActiveChime();
112
+ activeChimeNodes = buildChime(0.02);
113
+ }
114
+
115
+ function soundSource(path: string): string {
116
+ const direct = /^(https?:)?\/\//i.test(path) || /^(data|blob):/i.test(path);
117
+ return direct ? path : `/api/pomodoro/sound?v=${Date.now()}`;
118
+ }
119
+
120
+ /** Schedule the built-in chime for a timer that is about to end. */
121
+ export function scheduleAlarm(delaySeconds: number, endsAt: number): void {
122
+ cancelScheduledAlarm();
123
+ const nodes = buildChime(delaySeconds);
124
+ if (nodes.length > 0) scheduled = { endsAt, nodes };
125
+ }
126
+
127
+ /** Drop a future chime because the timer was paused or reset. */
128
+ export function cancelScheduledAlarm(): void {
129
+ if (!scheduled) return;
130
+ stopNodes(scheduled.nodes);
131
+ scheduled = null;
132
+ }
133
+
134
+ /**
135
+ * Take ownership of the chime scheduled for `endsAt` when the timer finishes.
136
+ * The nodes keep playing (they are already due) but are tracked as active so a
137
+ * quick confirmation can still cut them off.
138
+ */
139
+ export function claimScheduledAlarm(endsAt: number): boolean {
140
+ if (!scheduled || scheduled.endsAt !== endsAt) return false;
141
+ activeChimeNodes = scheduled.nodes;
142
+ scheduled = null;
143
+ return true;
144
+ }
145
+
146
+ /** Play the configured file (or the built-in chime) exactly once. */
147
+ export function playAlarmOnce(soundPath: string): void {
148
+ const path = soundPath.trim();
149
+ if (path) {
150
+ try {
151
+ const audio = new Audio(soundSource(path));
152
+ audio.volume = 0.72;
153
+ alarmAudio = audio;
154
+ let fellBack = false;
155
+ const fallback = (): void => {
156
+ if (fellBack) return;
157
+ fellBack = true;
158
+ if (alarmAudio === audio) alarmAudio = null;
159
+ playChimeNow();
160
+ };
161
+ audio.addEventListener('error', fallback, { once: true });
162
+ const playing = audio.play();
163
+ if (playing && typeof playing.catch === 'function') playing.catch(fallback);
164
+ setTimeout(() => {
165
+ try {
166
+ audio.pause();
167
+ } catch {
168
+ /* ignore */
169
+ }
170
+ if (alarmAudio === audio) alarmAudio = null;
171
+ }, 30_000);
172
+ } catch {
173
+ playChimeNow();
174
+ }
175
+ return;
176
+ }
177
+ playChimeNow();
178
+ }
179
+
180
+ function stopLoopTimer(): void {
181
+ if (!loopTimer) return;
182
+ clearTimeout(loopTimer);
183
+ loopTimer = null;
184
+ }
185
+
186
+ function startBuiltinLoop(delayMs: number): void {
187
+ stopLoopTimer();
188
+ loopTimer = setTimeout(() => {
189
+ loopTimer = null;
190
+ if (!loopActive) return;
191
+ playChimeNow();
192
+ startBuiltinLoop(4400);
193
+ }, Math.max(0, delayMs));
194
+ }
195
+
196
+ /** Loop the configured audio file until dismissed; false means fall back. */
197
+ function startCustomLoop(path: string): boolean {
198
+ try {
199
+ const audio = new Audio(soundSource(path));
200
+ audio.loop = true;
201
+ audio.volume = 0.72;
202
+ alarmAudio = audio;
203
+ let failed = false;
204
+ const fallback = (): void => {
205
+ if (failed || !loopActive) return;
206
+ failed = true;
207
+ if (alarmAudio === audio) alarmAudio = null;
208
+ startBuiltinLoop(0);
209
+ };
210
+ audio.addEventListener('error', fallback, { once: true });
211
+ const playing = audio.play();
212
+ if (playing && typeof playing.catch === 'function') playing.catch(fallback);
213
+ return true;
214
+ } catch {
215
+ return false;
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Ring until stopped. `skipFirst` is for the scheduled chime that is already
221
+ * playing at the end of a timer, so the alarm does not double up.
222
+ */
223
+ export function startLoopingAlarm(soundPath: string, skipFirst: boolean): void {
224
+ loopActive = true;
225
+ stopLoopTimer();
226
+ const path = soundPath.trim();
227
+ if (path && startCustomLoop(path)) return;
228
+ if (skipFirst) {
229
+ startBuiltinLoop(4400);
230
+ return;
231
+ }
232
+ playChimeNow();
233
+ startBuiltinLoop(4400);
234
+ }
235
+
236
+ /** Stop every alarm source and clear the page-tab indicator. */
237
+ export function stopAlarmSound(): void {
238
+ loopActive = false;
239
+ stopLoopTimer();
240
+ if (alarmAudio) {
241
+ try {
242
+ alarmAudio.pause();
243
+ alarmAudio.currentTime = 0;
244
+ } catch {
245
+ /* ignore */
246
+ }
247
+ alarmAudio = null;
248
+ }
249
+ cancelScheduledAlarm();
250
+ stopActiveChime();
251
+ setTabAlert(false);
252
+ }
253
+
254
+ /* ------------------------------------------------------- page-tab indicator */
255
+
256
+ function cssVariable(name: string, fallback: string): string {
257
+ try {
258
+ const value = getComputedStyle(document.body).getPropertyValue(name).trim();
259
+ return value || fallback;
260
+ } catch {
261
+ return fallback;
262
+ }
263
+ }
264
+
265
+ function iconLink(): HTMLLinkElement | null {
266
+ return document.querySelector<HTMLLinkElement>('link[rel="icon"]');
267
+ }
268
+
269
+ /** A green dot with the same card-colored ring as a session status dot. */
270
+ function dotIcon(base: HTMLImageElement | null): string {
271
+ const size = 64;
272
+ const canvas = document.createElement('canvas');
273
+ canvas.width = size;
274
+ canvas.height = size;
275
+ const context = canvas.getContext('2d');
276
+ if (!context) return '';
277
+ if (base) context.drawImage(base, 0, 0, size, size);
278
+ const green = cssVariable('--green', '#45dfb1');
279
+ const ring = cssVariable('--card', '#0d121d');
280
+ context.beginPath();
281
+ context.arc(46, 46, 13, 0, Math.PI * 2);
282
+ context.fillStyle = ring;
283
+ context.fill();
284
+ context.beginPath();
285
+ context.arc(46, 46, 10, 0, Math.PI * 2);
286
+ context.fillStyle = green;
287
+ context.fill();
288
+ return canvas.toDataURL('image/png');
289
+ }
290
+
291
+ function paintFavicon(): void {
292
+ const link = iconLink();
293
+ if (!link) return;
294
+ const immediate = dotIcon(null);
295
+ if (immediate) link.setAttribute('href', immediate);
296
+ const image = new Image();
297
+ image.onload = () => {
298
+ if (!alerting) return;
299
+ const composed = dotIcon(image);
300
+ if (composed) link.setAttribute('href', composed);
301
+ };
302
+ image.src = '/icon.svg';
303
+ }
304
+
305
+ export function setTabAlert(active: boolean): void {
306
+ if (active === alerting) return;
307
+ alerting = active;
308
+ tabAlert.active = active;
309
+ const link = iconLink();
310
+ if (active) {
311
+ originalTitle = document.title.replace(/^●\s*/, '');
312
+ originalIcon = link?.getAttribute('href') || '';
313
+ document.title = `● ${originalTitle}`;
314
+ paintFavicon();
315
+ return;
316
+ }
317
+ document.title = originalTitle || document.title.replace(/^●\s*/, '');
318
+ if (link && originalIcon) link.setAttribute('href', originalIcon);
319
+ }
@@ -0,0 +1,306 @@
1
+ export type PageKey = 'home' | 'sessions' | 'music' | 'performance';
2
+ export type TemperatureUnit = 'celsius' | 'fahrenheit';
3
+ export type Theme = 'dark' | 'light';
4
+ export type NavPosition = 'left' | 'right' | 'top' | 'bottom';
5
+
6
+ export interface PomodoroSettings {
7
+ enabled: boolean;
8
+ focusMinutes: number;
9
+ breakMinutes: number;
10
+ soundPath: string;
11
+ loopAlarm: boolean;
12
+ }
13
+
14
+ export interface CountdownSettings {
15
+ enabled: boolean;
16
+ /** One to five quick-select durations, in seconds. */
17
+ presets: number[];
18
+ }
19
+
20
+ export interface ConsoleSettings {
21
+ theme?: string;
22
+ /** Console language resolved by the Host (`zh` or `en`). */
23
+ locale?: string;
24
+ temperatureUnit?: string;
25
+ pushQuestions?: boolean;
26
+ navPosition?: string;
27
+ balance?: { spendWindow?: string; lowThreshold?: number | null; showPeakValley?: boolean };
28
+ pomodoro?: Partial<PomodoroSettings>;
29
+ countdown?: Partial<CountdownSettings>;
30
+ pageOrder?: string[];
31
+ pageEnabled?: Partial<Record<PageKey, boolean>>;
32
+ }
33
+
34
+ export interface WeatherCurrent {
35
+ code?: number;
36
+ temperature?: number;
37
+ feelsLike?: number;
38
+ humidity?: number;
39
+ windSpeed?: number;
40
+ isDay?: boolean;
41
+ }
42
+
43
+ export interface WeatherDaily {
44
+ high?: number;
45
+ low?: number;
46
+ sunrise?: string;
47
+ sunset?: string;
48
+ tomorrowHigh?: number;
49
+ tomorrowLow?: number;
50
+ }
51
+
52
+ export interface WeatherHour {
53
+ time?: string;
54
+ code?: number;
55
+ temperature?: number;
56
+ }
57
+
58
+ export interface WeatherDay {
59
+ date?: string;
60
+ code?: number;
61
+ high?: number;
62
+ low?: number;
63
+ }
64
+
65
+ export interface WeatherData {
66
+ ok?: boolean;
67
+ location?: { name?: string };
68
+ current?: WeatherCurrent;
69
+ daily?: WeatherDaily;
70
+ hourly?: WeatherHour[];
71
+ dailyForecast?: WeatherDay[];
72
+ forecastMode?: string;
73
+ }
74
+
75
+ export interface NotificationItem {
76
+ id?: string;
77
+ appKey?: string;
78
+ appName?: string;
79
+ title?: string;
80
+ body?: string;
81
+ avatar?: string;
82
+ receivedAt?: number;
83
+ }
84
+
85
+ export interface NotificationsData {
86
+ available?: boolean;
87
+ reason?: string | null;
88
+ notifications?: NotificationItem[];
89
+ }
90
+
91
+ export interface SessionQuestionOption {
92
+ label: string;
93
+ description?: string;
94
+ }
95
+
96
+ export interface SessionQuestion {
97
+ id: string;
98
+ question: string;
99
+ header?: string;
100
+ detail?: string;
101
+ options?: SessionQuestionOption[];
102
+ multiSelect?: boolean;
103
+ }
104
+
105
+ export interface SessionPending {
106
+ kind?: string;
107
+ question?: string;
108
+ header?: string;
109
+ detail?: string;
110
+ count?: number;
111
+ since?: number;
112
+ requestId?: string;
113
+ questions?: SessionQuestion[];
114
+ answerable?: boolean;
115
+ }
116
+
117
+ export interface SessionItem {
118
+ id?: string;
119
+ title?: string;
120
+ status?: string;
121
+ error?: string;
122
+ pending?: SessionPending;
123
+ preview?: string;
124
+ cwd?: string;
125
+ turn?: number;
126
+ isSubagent?: boolean;
127
+ /** Parent session id for a subagent dispatched by another agent. */
128
+ parentId?: string;
129
+ /** Open turn start time from DSH, used for the live running duration. */
130
+ runningSince?: number | null;
131
+ lastActiveAt?: number;
132
+ }
133
+
134
+ export interface BalanceInfo {
135
+ currency?: string;
136
+ balance?: number;
137
+ paid?: number;
138
+ bonus?: number;
139
+ state?: string;
140
+ error?: string;
141
+ }
142
+
143
+ export interface UsageDay {
144
+ label?: string;
145
+ cost?: number;
146
+ }
147
+
148
+ export interface UsageInfo {
149
+ currency?: string;
150
+ state?: string;
151
+ today?: number;
152
+ spend?: number;
153
+ spendLabel?: string;
154
+ daily?: UsageDay[];
155
+ error?: string;
156
+ }
157
+
158
+ export interface AccountData {
159
+ available?: boolean;
160
+ signedIn?: boolean;
161
+ lowBalance?: boolean;
162
+ lowThreshold?: number;
163
+ balance?: BalanceInfo;
164
+ usage?: UsageInfo;
165
+ }
166
+
167
+ export interface PerformanceData {
168
+ cpu?: {
169
+ usage?: number;
170
+ model?: string;
171
+ cores?: number;
172
+ physicalCores?: number;
173
+ speed?: number;
174
+ baseSpeed?: number;
175
+ uptime?: number;
176
+ };
177
+ memory?: {
178
+ total?: number;
179
+ used?: number;
180
+ free?: number;
181
+ percent?: number;
182
+ };
183
+ gpu?: {
184
+ ok?: boolean;
185
+ name?: string;
186
+ utilization?: number;
187
+ memoryUsed?: number;
188
+ memoryTotal?: number;
189
+ memoryPercent?: number;
190
+ coreClockMhz?: number;
191
+ coreClockMaxMhz?: number;
192
+ memoryClockMhz?: number;
193
+ powerPercent?: number;
194
+ fanRpm?: number;
195
+ driver?: string;
196
+ error?: string;
197
+ };
198
+ temperatures?: Array<{ name?: string; celsius?: number }>;
199
+ }
200
+
201
+ export interface MediaInfo {
202
+ appId?: string;
203
+ appName?: string;
204
+ title?: string;
205
+ artist?: string;
206
+ albumArtist?: string;
207
+ album?: string;
208
+ playbackStatus?: string;
209
+ position?: number;
210
+ startTime?: number;
211
+ endTime?: number;
212
+ canSeek?: boolean;
213
+ positionReported?: boolean;
214
+ canPlayPause?: boolean;
215
+ canNext?: boolean;
216
+ canPrevious?: boolean;
217
+ liked?: boolean;
218
+ canLike?: boolean;
219
+ canSetLiked?: boolean;
220
+ thumbnail?: string | null;
221
+ thumbnailUrl?: string | null;
222
+ }
223
+
224
+ export interface MediaPayload {
225
+ ok?: boolean;
226
+ media?: {
227
+ media?: MediaInfo | null;
228
+ error?: string | null;
229
+ };
230
+ volume?: {
231
+ volume?: number;
232
+ muted?: boolean;
233
+ };
234
+ }
235
+
236
+ export interface OverviewData {
237
+ settings?: ConsoleSettings;
238
+ weather?: WeatherData | null;
239
+ notifications?: NotificationsData | null;
240
+ account?: AccountData | null;
241
+ sessions?: SessionItem[];
242
+ sessionsError?: string | null;
243
+ performance?: PerformanceData | null;
244
+ }
245
+
246
+ export interface QuestionAnswerItem {
247
+ id: string;
248
+ selected: string[];
249
+ custom?: string;
250
+ }
251
+
252
+ export interface ConsoleState {
253
+ online: boolean | null;
254
+ theme: Theme;
255
+ temperatureUnit: TemperatureUnit;
256
+ pushQuestions: boolean;
257
+ navPosition: NavPosition;
258
+ pomodoro: PomodoroSettings;
259
+ countdown: CountdownSettings;
260
+ balanceDisplay: { showPeakValley: boolean; spendWindow: 'calendar' | 'rolling' };
261
+ pageOrder: PageKey[];
262
+ pageEnabled: Record<PageKey, boolean>;
263
+ pageRevision: number;
264
+
265
+ overview: OverviewData | null;
266
+ weather: WeatherData | null;
267
+ notifications: NotificationsData | null;
268
+ account: AccountData | null;
269
+ sessions: SessionItem[];
270
+ sessionsError: string | null;
271
+ performance: PerformanceData | null;
272
+
273
+ media: MediaPayload | null;
274
+ clockNow: number;
275
+
276
+ volumeDragging: boolean;
277
+ volumeInput: number;
278
+ volumeTarget: number;
279
+ volumeHoldUntil: number;
280
+
281
+ toastVisible: boolean;
282
+ toastMessage: string;
283
+
284
+ activeKey: PageKey;
285
+ initialKey: PageKey | null;
286
+ hasNavigated: boolean;
287
+ fullscreen: boolean;
288
+ /** Session id whose pending question sheet is open, or null. */
289
+ answerSessionId: string | null;
290
+ }
291
+
292
+ export type ClientMessage =
293
+ | { type: 'settings' }
294
+ | { type: 'refresh' }
295
+ | { type: 'action'; action: string; value?: number | boolean }
296
+ | { type: 'answer-question'; sessionId: string; requestId: string; answers: QuestionAnswerItem[] };
297
+
298
+ export interface ServerMessage {
299
+ type?: string;
300
+ data?: unknown;
301
+ settings?: ConsoleSettings;
302
+ ok?: boolean;
303
+ error?: string;
304
+ sessionId?: string;
305
+ requestId?: string;
306
+ }
@@ -0,0 +1,8 @@
1
+ export { cn } from "cn";
2
+
3
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
+ export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T;
5
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
+ export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T;
7
+ export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
8
+ export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
@@ -0,0 +1,84 @@
1
+ import { t, type MessageKey } from './i18n.svelte';
2
+ import type { WeatherData } from './types';
3
+
4
+ /** WMO weather-code mapping ported from the original console. */
5
+ export const WMO: Record<number, readonly [MessageKey, string, string]> = {
6
+ 0: ['wmoClear', 'sun', 'day'],
7
+ 1: ['wmoMostlyClear', 'sun', 'day'],
8
+ 2: ['wmoPartlyCloudy', 'cloud-sun', 'day'],
9
+ 3: ['wmoOvercast', 'cloud', 'cloud'],
10
+ 45: ['wmoFog', 'fog', 'cloud'],
11
+ 48: ['wmoRimeFog', 'fog', 'cloud'],
12
+ 51: ['wmoDrizzle', 'rain', 'rain'],
13
+ 53: ['wmoDrizzle', 'rain', 'rain'],
14
+ 55: ['wmoDenseDrizzle', 'rain', 'rain'],
15
+ 56: ['wmoFreezingDrizzle', 'rain', 'rain'],
16
+ 57: ['wmoHeavyFreezingDrizzle', 'rain', 'rain'],
17
+ 61: ['wmoLightRain', 'rain', 'rain'],
18
+ 63: ['wmoRain', 'rain', 'rain'],
19
+ 65: ['wmoHeavyRain', 'rain', 'rain'],
20
+ 66: ['wmoFreezingRain', 'rain', 'rain'],
21
+ 67: ['wmoHeavyFreezingRain', 'rain', 'rain'],
22
+ 71: ['wmoLightSnow', 'snow', 'rain'],
23
+ 73: ['wmoSnow', 'snow', 'rain'],
24
+ 75: ['wmoHeavySnow', 'snow', 'rain'],
25
+ 77: ['wmoSnowGrains', 'snow', 'rain'],
26
+ 80: ['wmoRainShowers', 'rain', 'rain'],
27
+ 81: ['wmoHeavyRainShowers', 'rain', 'rain'],
28
+ 82: ['wmoViolentRainShowers', 'storm', 'storm'],
29
+ 85: ['wmoSnowShowers', 'snow', 'rain'],
30
+ 86: ['wmoHeavySnowShowers', 'snow', 'rain'],
31
+ 95: ['wmoThunderstorm', 'storm', 'storm'],
32
+ 96: ['wmoThunderstormHail', 'storm', 'storm'],
33
+ 99: ['wmoSevereThunderstorm', 'storm', 'storm'],
34
+ };
35
+
36
+ export interface WeatherInfo {
37
+ label: string;
38
+ icon: string;
39
+ skycon: string;
40
+ tone: string;
41
+ }
42
+
43
+ export function skyconFor(code: number | undefined, isDay: boolean | undefined): string {
44
+ switch (Number(code)) {
45
+ case 0: return isDay ? 'CLEAR_DAY' : 'CLEAR_NIGHT';
46
+ case 1:
47
+ case 2: return isDay ? 'PARTLY_CLOUDY_DAY' : 'PARTLY_CLOUDY_NIGHT';
48
+ case 3: return 'CLOUDY';
49
+ case 45:
50
+ case 48: return 'FOG';
51
+ case 51:
52
+ case 53:
53
+ case 55:
54
+ case 56:
55
+ case 57:
56
+ case 61:
57
+ case 66:
58
+ case 67:
59
+ case 80: return 'LIGHT_RAIN';
60
+ case 63:
61
+ case 81: return 'MODERATE_RAIN';
62
+ case 65: return 'HEAVY_RAIN';
63
+ case 71:
64
+ case 77:
65
+ case 85: return 'LIGHT_SNOW';
66
+ case 73:
67
+ case 86: return 'MODERATE_SNOW';
68
+ case 75: return 'HEAVY_SNOW';
69
+ case 82:
70
+ case 95:
71
+ case 96:
72
+ case 99: return 'STORM_RAIN';
73
+ default: return 'CLOUDY';
74
+ }
75
+ }
76
+
77
+ export function weatherInfo(code: number | undefined, isDay: boolean | undefined): WeatherInfo {
78
+ const item = WMO[Number(code)] || (isDay
79
+ ? ['weatherFallback', 'sun', 'day'] as const
80
+ : ['weatherNight', 'moon', 'night'] as const);
81
+ return { label: t(item[0]), icon: item[1], skycon: skyconFor(code, isDay), tone: item[2] };
82
+ }
83
+
84
+ export type { WeatherData };