@ohmykreee/dsh-remote-control 0.0.0-stage → 1.0.3
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.
- package/AGENTS.md +556 -0
- package/INSTALLATION.md +310 -0
- package/LICENSE +21 -0
- package/README.md +195 -2
- package/client.js +3 -0
- package/components.json +20 -0
- package/cordis.patch.yml +3 -0
- package/docs/README.en.md +196 -0
- package/docs/screenshots/en/overview.png +0 -0
- package/docs/screenshots/en/question.png +0 -0
- package/docs/screenshots/zh/overview.png +0 -0
- package/docs/screenshots/zh/question.png +0 -0
- package/icon.svg +24 -0
- package/index.js +2 -0
- package/lib/account.js +1 -0
- package/lib/app-mapping.js +1 -0
- package/lib/i18n.js +1 -0
- package/lib/media-enrich.js +1 -0
- package/lib/notifications.js +1 -0
- package/lib/performance.js +1 -0
- package/lib/platform/index.js +1 -0
- package/lib/probe-adapter.js +1 -0
- package/lib/probe-daemon.js +1 -0
- package/lib/probe.js +1 -0
- package/lib/qr.js +1 -0
- package/lib/runtime.js +1 -0
- package/lib/service.js +1 -0
- package/lib/sessions.js +1 -0
- package/lib/settings.js +1 -0
- package/lib/types.js +1 -0
- package/lib/util.js +1 -0
- package/lib/weather.js +1 -0
- package/lib/ws.js +1 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +118 -4
- package/scripts/browser-shot.mjs +217 -0
- package/scripts/build-dsh.mjs +228 -0
- package/scripts/build-rust.mjs +319 -0
- package/scripts/build-web.mjs +17 -0
- package/scripts/check-npm-package.mjs +322 -0
- package/scripts/mock-preview.py +574 -0
- package/scripts/screenshot-compose.py +89 -0
- package/scripts/screenshot-device.sh +75 -0
- package/src/dsh/client/api.ts +68 -0
- package/src/dsh/client/i18n.ts +573 -0
- package/src/dsh/client/icons.ts +56 -0
- package/src/dsh/client/index.ts +95 -0
- package/src/dsh/client/logic.ts +310 -0
- package/src/dsh/client/panel.ts +483 -0
- package/src/dsh/client/sections/apps.ts +40 -0
- package/src/dsh/client/sections/balance.ts +38 -0
- package/src/dsh/client/sections/connection.ts +97 -0
- package/src/dsh/client/sections/notifications.ts +43 -0
- package/src/dsh/client/sections/pomodoro.ts +184 -0
- package/src/dsh/client/sections/preferences.ts +138 -0
- package/src/dsh/client/sections/system.ts +120 -0
- package/src/dsh/client/sections/weather.ts +276 -0
- package/src/dsh/client/styles.ts +1119 -0
- package/src/dsh/client/types.ts +366 -0
- package/src/dsh/client/ui.ts +142 -0
- package/src/dsh/index.ts +108 -0
- package/src/dsh/lib/account.ts +520 -0
- package/src/dsh/lib/app-mapping.ts +366 -0
- package/src/dsh/lib/i18n.ts +82 -0
- package/src/dsh/lib/media-enrich.ts +202 -0
- package/src/dsh/lib/notifications.ts +322 -0
- package/src/dsh/lib/performance.ts +279 -0
- package/src/dsh/lib/platform/index.ts +43 -0
- package/src/dsh/lib/probe-adapter.ts +275 -0
- package/src/dsh/lib/probe-daemon.ts +359 -0
- package/src/dsh/lib/probe.ts +395 -0
- package/src/dsh/lib/qr.ts +42 -0
- package/src/dsh/lib/runtime.ts +126 -0
- package/src/dsh/lib/service.ts +1645 -0
- package/src/dsh/lib/sessions.ts +907 -0
- package/src/dsh/lib/settings.ts +468 -0
- package/src/dsh/lib/types.ts +231 -0
- package/src/dsh/lib/util.ts +115 -0
- package/src/dsh/lib/weather.ts +532 -0
- package/src/dsh/lib/ws.ts +328 -0
- package/src/dsh/types/qrcode-generator.d.ts +16 -0
- package/src/rust/Cargo.lock +1431 -0
- package/src/rust/Cargo.toml +68 -0
- package/src/rust/src/cli.rs +233 -0
- package/src/rust/src/daemon.rs +140 -0
- package/src/rust/src/location/linux.rs +118 -0
- package/src/rust/src/location/macos.rs +26 -0
- package/src/rust/src/location/mod.rs +170 -0
- package/src/rust/src/location/windows.rs +205 -0
- package/src/rust/src/main.rs +42 -0
- package/src/rust/src/media/catalog.rs +454 -0
- package/src/rust/src/media/linux.rs +350 -0
- package/src/rust/src/media/macos/accessibility.rs +59 -0
- package/src/rust/src/media/macos.rs +366 -0
- package/src/rust/src/media/mod.rs +298 -0
- package/src/rust/src/media/vendor/apple_music.rs +90 -0
- package/src/rust/src/media/vendor/common.rs +186 -0
- package/src/rust/src/media/vendor/kugou.rs +52 -0
- package/src/rust/src/media/vendor/kuwo.rs +49 -0
- package/src/rust/src/media/vendor/mod.rs +75 -0
- package/src/rust/src/media/vendor/netease.rs +1715 -0
- package/src/rust/src/media/vendor/qishui.rs +36 -0
- package/src/rust/src/media/vendor/qqmusic.rs +33 -0
- package/src/rust/src/media/vendor/spotify.rs +46 -0
- package/src/rust/src/media/vendor/wesing.rs +39 -0
- package/src/rust/src/media/vendor/youtube_music.rs +49 -0
- package/src/rust/src/media/windows/accessibility.rs +85 -0
- package/src/rust/src/media/windows/audio.rs +116 -0
- package/src/rust/src/media/windows/private.rs +291 -0
- package/src/rust/src/media/windows.rs +775 -0
- package/src/rust/src/notifications/linux.rs +276 -0
- package/src/rust/src/notifications/macos.rs +382 -0
- package/src/rust/src/notifications/mod.rs +244 -0
- package/src/rust/src/notifications/windows.rs +565 -0
- package/src/rust/src/perf/common.rs +109 -0
- package/src/rust/src/perf/linux.rs +282 -0
- package/src/rust/src/perf/macos.rs +28 -0
- package/src/rust/src/perf/mod.rs +157 -0
- package/src/rust/src/perf/windows.rs +981 -0
- package/src/rust/src/util.rs +360 -0
- package/src/rust/src/volume/linux.rs +195 -0
- package/src/rust/src/volume/macos.rs +54 -0
- package/src/rust/src/volume/mod.rs +116 -0
- package/src/rust/src/volume/windows.rs +75 -0
- package/src/rust/tests/cli.rs +79 -0
- package/src/rust/tests/daemon.rs +107 -0
- package/src/svelte/App.svelte +385 -0
- package/src/svelte/index.html +18 -0
- package/src/svelte/lib/billing.svelte.ts +228 -0
- package/src/svelte/lib/components/BalanceCard.svelte +173 -0
- package/src/svelte/lib/components/ClockCard.svelte +183 -0
- package/src/svelte/lib/components/CountdownCard.svelte +261 -0
- package/src/svelte/lib/components/CpuMemoryCard.svelte +238 -0
- package/src/svelte/lib/components/EmptyState.svelte +34 -0
- package/src/svelte/lib/components/GpuCard.svelte +245 -0
- package/src/svelte/lib/components/HomePage.svelte +34 -0
- package/src/svelte/lib/components/MusicControls.svelte +256 -0
- package/src/svelte/lib/components/MusicCover.svelte +88 -0
- package/src/svelte/lib/components/MusicPage.svelte +116 -0
- package/src/svelte/lib/components/NotificationsCard.svelte +109 -0
- package/src/svelte/lib/components/Panel.svelte +22 -0
- package/src/svelte/lib/components/PerformancePage.svelte +55 -0
- package/src/svelte/lib/components/PomodoroCard.svelte +250 -0
- package/src/svelte/lib/components/QuestionSheet.svelte +354 -0
- package/src/svelte/lib/components/RadialGauge.svelte +99 -0
- package/src/svelte/lib/components/Rail.svelte +217 -0
- package/src/svelte/lib/components/SectionHeader.svelte +40 -0
- package/src/svelte/lib/components/SeekBar.svelte +421 -0
- package/src/svelte/lib/components/SessionList.svelte +364 -0
- package/src/svelte/lib/components/SessionStats.svelte +98 -0
- package/src/svelte/lib/components/SessionsPage.svelte +61 -0
- package/src/svelte/lib/components/SpendCard.svelte +105 -0
- package/src/svelte/lib/components/ThermalCard.svelte +249 -0
- package/src/svelte/lib/components/Toast.svelte +32 -0
- package/src/svelte/lib/components/WeatherIcon.svelte +40 -0
- package/src/svelte/lib/components/WeatherPanel.svelte +420 -0
- package/src/svelte/lib/components/ui/alert/alert-action.svelte +20 -0
- package/src/svelte/lib/components/ui/alert/alert-description.svelte +23 -0
- package/src/svelte/lib/components/ui/alert/alert-title.svelte +23 -0
- package/src/svelte/lib/components/ui/alert/alert.svelte +43 -0
- package/src/svelte/lib/components/ui/alert/index.ts +17 -0
- package/src/svelte/lib/components/ui/avatar/avatar-badge.svelte +26 -0
- package/src/svelte/lib/components/ui/avatar/avatar-fallback.svelte +20 -0
- package/src/svelte/lib/components/ui/avatar/avatar-group-count.svelte +23 -0
- package/src/svelte/lib/components/ui/avatar/avatar-group.svelte +23 -0
- package/src/svelte/lib/components/ui/avatar/avatar-image.svelte +17 -0
- package/src/svelte/lib/components/ui/avatar/avatar.svelte +26 -0
- package/src/svelte/lib/components/ui/avatar/index.ts +22 -0
- package/src/svelte/lib/components/ui/badge/badge.svelte +49 -0
- package/src/svelte/lib/components/ui/badge/index.ts +2 -0
- package/src/svelte/lib/components/ui/button/button.svelte +82 -0
- package/src/svelte/lib/components/ui/button/index.ts +17 -0
- package/src/svelte/lib/components/ui/card/card-action.svelte +23 -0
- package/src/svelte/lib/components/ui/card/card-content.svelte +20 -0
- package/src/svelte/lib/components/ui/card/card-description.svelte +20 -0
- package/src/svelte/lib/components/ui/card/card-footer.svelte +20 -0
- package/src/svelte/lib/components/ui/card/card-header.svelte +23 -0
- package/src/svelte/lib/components/ui/card/card-title.svelte +20 -0
- package/src/svelte/lib/components/ui/card/card.svelte +22 -0
- package/src/svelte/lib/components/ui/card/index.ts +25 -0
- package/src/svelte/lib/components/ui/dialog/dialog-close.svelte +11 -0
- package/src/svelte/lib/components/ui/dialog/dialog-content.svelte +48 -0
- package/src/svelte/lib/components/ui/dialog/dialog-description.svelte +17 -0
- package/src/svelte/lib/components/ui/dialog/dialog-footer.svelte +32 -0
- package/src/svelte/lib/components/ui/dialog/dialog-header.svelte +20 -0
- package/src/svelte/lib/components/ui/dialog/dialog-overlay.svelte +17 -0
- package/src/svelte/lib/components/ui/dialog/dialog-portal.svelte +7 -0
- package/src/svelte/lib/components/ui/dialog/dialog-title.svelte +17 -0
- package/src/svelte/lib/components/ui/dialog/dialog-trigger.svelte +11 -0
- package/src/svelte/lib/components/ui/dialog/dialog.svelte +7 -0
- package/src/svelte/lib/components/ui/dialog/index.ts +34 -0
- package/src/svelte/lib/components/ui/empty/empty-content.svelte +23 -0
- package/src/svelte/lib/components/ui/empty/empty-description.svelte +23 -0
- package/src/svelte/lib/components/ui/empty/empty-header.svelte +20 -0
- package/src/svelte/lib/components/ui/empty/empty-media.svelte +41 -0
- package/src/svelte/lib/components/ui/empty/empty-title.svelte +20 -0
- package/src/svelte/lib/components/ui/empty/empty.svelte +23 -0
- package/src/svelte/lib/components/ui/empty/index.ts +22 -0
- package/src/svelte/lib/components/ui/input/index.ts +7 -0
- package/src/svelte/lib/components/ui/input/input.svelte +48 -0
- package/src/svelte/lib/components/ui/progress/index.ts +7 -0
- package/src/svelte/lib/components/ui/progress/progress.svelte +27 -0
- package/src/svelte/lib/components/ui/scroll-area/index.ts +10 -0
- package/src/svelte/lib/components/ui/scroll-area/scroll-area-scrollbar.svelte +30 -0
- package/src/svelte/lib/components/ui/scroll-area/scroll-area.svelte +43 -0
- package/src/svelte/lib/components/ui/separator/index.ts +7 -0
- package/src/svelte/lib/components/ui/separator/separator.svelte +21 -0
- package/src/svelte/lib/components/ui/sheet/index.ts +34 -0
- package/src/svelte/lib/components/ui/sheet/sheet-close.svelte +7 -0
- package/src/svelte/lib/components/ui/sheet/sheet-content.svelte +55 -0
- package/src/svelte/lib/components/ui/sheet/sheet-description.svelte +17 -0
- package/src/svelte/lib/components/ui/sheet/sheet-footer.svelte +20 -0
- package/src/svelte/lib/components/ui/sheet/sheet-header.svelte +20 -0
- package/src/svelte/lib/components/ui/sheet/sheet-overlay.svelte +20 -0
- package/src/svelte/lib/components/ui/sheet/sheet-portal.svelte +7 -0
- package/src/svelte/lib/components/ui/sheet/sheet-title.svelte +17 -0
- package/src/svelte/lib/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/src/svelte/lib/components/ui/sheet/sheet.svelte +7 -0
- package/src/svelte/lib/components/ui/skeleton/index.ts +7 -0
- package/src/svelte/lib/components/ui/skeleton/skeleton.svelte +17 -0
- package/src/svelte/lib/components/ui/slider/index.ts +7 -0
- package/src/svelte/lib/components/ui/slider/slider.svelte +52 -0
- package/src/svelte/lib/components/ui/tooltip/index.ts +19 -0
- package/src/svelte/lib/components/ui/tooltip/tooltip-content.svelte +51 -0
- package/src/svelte/lib/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/src/svelte/lib/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/src/svelte/lib/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/src/svelte/lib/components/ui/tooltip/tooltip.svelte +7 -0
- package/src/svelte/lib/countdown.svelte.ts +252 -0
- package/src/svelte/lib/format.ts +199 -0
- package/src/svelte/lib/i18n.svelte.ts +583 -0
- package/src/svelte/lib/media.ts +32 -0
- package/src/svelte/lib/pages.ts +18 -0
- package/src/svelte/lib/pomodoro.svelte.ts +223 -0
- package/src/svelte/lib/session-tree.ts +80 -0
- package/src/svelte/lib/state.svelte.ts +359 -0
- package/src/svelte/lib/timer-alarm.svelte.ts +319 -0
- package/src/svelte/lib/types.ts +306 -0
- package/src/svelte/lib/utils.ts +8 -0
- package/src/svelte/lib/weather.ts +84 -0
- package/src/svelte/lib/ws.ts +104 -0
- package/src/svelte/main.ts +13 -0
- package/src/svelte/styles/global.css +315 -0
- package/src/svelte/vite-env.d.ts +12 -0
- package/static/fonts/LICENSE.txt +287 -0
- package/static/icon.svg +24 -0
- package/static/manifest.webmanifest +18 -0
- package/svelte.config.js +5 -0
- package/tsconfig.dsh-client.json +18 -0
- package/tsconfig.dsh.json +21 -0
- package/tsconfig.json +20 -0
- package/vite.config.js +31 -0
- package/web/assets/index-DLdSfnJ3.css +1 -0
- package/web/assets/index-YNE8X0Uy.js +6 -0
- package/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/web/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2 +0 -0
- package/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/web/assets/noto-sans-sc-100-wght-normal-DluiXYzi.woff2 +0 -0
- package/web/assets/noto-sans-sc-101-wght-normal-CPkZHnw0.woff2 +0 -0
- package/web/assets/noto-sans-sc-102-wght-normal-gzIulUH6.woff2 +0 -0
- package/web/assets/noto-sans-sc-103-wght-normal-CJfSBxFo.woff2 +0 -0
- package/web/assets/noto-sans-sc-104-wght-normal-B3QMglgE.woff2 +0 -0
- package/web/assets/noto-sans-sc-105-wght-normal-xXh_OMfk.woff2 +0 -0
- package/web/assets/noto-sans-sc-106-wght-normal-B9n3jMjP.woff2 +0 -0
- package/web/assets/noto-sans-sc-107-wght-normal-D7fvVprN.woff2 +0 -0
- package/web/assets/noto-sans-sc-108-wght-normal-J0_LhxVo.woff2 +0 -0
- package/web/assets/noto-sans-sc-109-wght-normal-Dsp-De_0.woff2 +0 -0
- package/web/assets/noto-sans-sc-110-wght-normal-DE_6NUrb.woff2 +0 -0
- package/web/assets/noto-sans-sc-111-wght-normal-C4S-TlTn.woff2 +0 -0
- package/web/assets/noto-sans-sc-112-wght-normal-BcFuP3w6.woff2 +0 -0
- package/web/assets/noto-sans-sc-113-wght-normal-Dk932kmj.woff2 +0 -0
- package/web/assets/noto-sans-sc-114-wght-normal-CO_5Xo-i.woff2 +0 -0
- package/web/assets/noto-sans-sc-115-wght-normal-BWy07H14.woff2 +0 -0
- package/web/assets/noto-sans-sc-116-wght-normal-B4HVAfHo.woff2 +0 -0
- package/web/assets/noto-sans-sc-117-wght-normal-siZzFqgK.woff2 +0 -0
- package/web/assets/noto-sans-sc-118-wght-normal-xhBB7f6V.woff2 +0 -0
- package/web/assets/noto-sans-sc-119-wght-normal-Ck_g2FGz.woff2 +0 -0
- package/web/assets/noto-sans-sc-21-wght-normal-D0x9-yBt.woff2 +0 -0
- package/web/assets/noto-sans-sc-22-wght-normal-BBUVTShg.woff2 +0 -0
- package/web/assets/noto-sans-sc-23-wght-normal-Du1oYWkE.woff2 +0 -0
- package/web/assets/noto-sans-sc-24-wght-normal-CmWjJ0gt.woff2 +0 -0
- package/web/assets/noto-sans-sc-25-wght-normal-BFHiqilg.woff2 +0 -0
- package/web/assets/noto-sans-sc-26-wght-normal-C0sF2Z2f.woff2 +0 -0
- package/web/assets/noto-sans-sc-27-wght-normal-COFdrquJ.woff2 +0 -0
- package/web/assets/noto-sans-sc-28-wght-normal-DMgdNu1o.woff2 +0 -0
- package/web/assets/noto-sans-sc-29-wght-normal-Cmq8em30.woff2 +0 -0
- package/web/assets/noto-sans-sc-30-wght-normal-CZWqZ3yU.woff2 +0 -0
- package/web/assets/noto-sans-sc-31-wght-normal-Wfi_lutW.woff2 +0 -0
- package/web/assets/noto-sans-sc-32-wght-normal-BRJQ4PY5.woff2 +0 -0
- package/web/assets/noto-sans-sc-33-wght-normal-CUVQXNAJ.woff2 +0 -0
- package/web/assets/noto-sans-sc-34-wght-normal-Bspba0wC.woff2 +0 -0
- package/web/assets/noto-sans-sc-35-wght-normal-K8fX_YPG.woff2 +0 -0
- package/web/assets/noto-sans-sc-36-wght-normal-CkITyjoT.woff2 +0 -0
- package/web/assets/noto-sans-sc-37-wght-normal-WCZ2zfEe.woff2 +0 -0
- package/web/assets/noto-sans-sc-38-wght-normal-DeM7CUce.woff2 +0 -0
- package/web/assets/noto-sans-sc-39-wght-normal-BMZNLWQU.woff2 +0 -0
- package/web/assets/noto-sans-sc-4-wght-normal-AR5bdDjz.woff2 +0 -0
- package/web/assets/noto-sans-sc-40-wght-normal-BQ5xpS5j.woff2 +0 -0
- package/web/assets/noto-sans-sc-41-wght-normal-Hthm7FFi.woff2 +0 -0
- package/web/assets/noto-sans-sc-42-wght-normal-eaS2gKds.woff2 +0 -0
- package/web/assets/noto-sans-sc-43-wght-normal-o-T53XXV.woff2 +0 -0
- package/web/assets/noto-sans-sc-44-wght-normal-BJH5J4Ez.woff2 +0 -0
- package/web/assets/noto-sans-sc-45-wght-normal-BKuxSCOJ.woff2 +0 -0
- package/web/assets/noto-sans-sc-46-wght-normal-BR_5Mu6e.woff2 +0 -0
- package/web/assets/noto-sans-sc-47-wght-normal-CyY7gKNX.woff2 +0 -0
- package/web/assets/noto-sans-sc-48-wght-normal-BNMfTR6k.woff2 +0 -0
- package/web/assets/noto-sans-sc-49-wght-normal-DX-0y7wg.woff2 +0 -0
- package/web/assets/noto-sans-sc-5-wght-normal-CfWugllB.woff2 +0 -0
- package/web/assets/noto-sans-sc-50-wght-normal-DAOGW-Dr.woff2 +0 -0
- package/web/assets/noto-sans-sc-51-wght-normal-BlFLOjcP.woff2 +0 -0
- package/web/assets/noto-sans-sc-52-wght-normal-OZjrybnw.woff2 +0 -0
- package/web/assets/noto-sans-sc-53-wght-normal-CwyWNT6x.woff2 +0 -0
- package/web/assets/noto-sans-sc-54-wght-normal-DM0nRe18.woff2 +0 -0
- package/web/assets/noto-sans-sc-55-wght-normal-DBK2wUOR.woff2 +0 -0
- package/web/assets/noto-sans-sc-56-wght-normal-C-VKK1LS.woff2 +0 -0
- package/web/assets/noto-sans-sc-57-wght-normal-Cdbfh5kq.woff2 +0 -0
- package/web/assets/noto-sans-sc-58-wght-normal-CflMKARZ.woff2 +0 -0
- package/web/assets/noto-sans-sc-59-wght-normal-BS4Kwzxx.woff2 +0 -0
- package/web/assets/noto-sans-sc-6-wght-normal-ChnZIQfQ.woff2 +0 -0
- package/web/assets/noto-sans-sc-60-wght-normal-B7QOYsXJ.woff2 +0 -0
- package/web/assets/noto-sans-sc-61-wght-normal-C8JjQS7w.woff2 +0 -0
- package/web/assets/noto-sans-sc-62-wght-normal-bS4wq1Gg.woff2 +0 -0
- package/web/assets/noto-sans-sc-63-wght-normal-BJr8P-OB.woff2 +0 -0
- package/web/assets/noto-sans-sc-64-wght-normal-DVB11t7j.woff2 +0 -0
- package/web/assets/noto-sans-sc-65-wght-normal-DMPCXBvK.woff2 +0 -0
- package/web/assets/noto-sans-sc-66-wght-normal-BobaG10V.woff2 +0 -0
- package/web/assets/noto-sans-sc-67-wght-normal-Dzk7WhCR.woff2 +0 -0
- package/web/assets/noto-sans-sc-68-wght-normal-DGbCD9xu.woff2 +0 -0
- package/web/assets/noto-sans-sc-69-wght-normal-Ctrv1EqA.woff2 +0 -0
- package/web/assets/noto-sans-sc-70-wght-normal-RgTO941D.woff2 +0 -0
- package/web/assets/noto-sans-sc-71-wght-normal-D9sG-CWv.woff2 +0 -0
- package/web/assets/noto-sans-sc-72-wght-normal-C4oWy3ul.woff2 +0 -0
- package/web/assets/noto-sans-sc-73-wght-normal-B1ZoOTgM.woff2 +0 -0
- package/web/assets/noto-sans-sc-74-wght-normal-F7dH_rZJ.woff2 +0 -0
- package/web/assets/noto-sans-sc-75-wght-normal-Cp2wp4si.woff2 +0 -0
- package/web/assets/noto-sans-sc-76-wght-normal-B494D7xS.woff2 +0 -0
- package/web/assets/noto-sans-sc-77-wght-normal-B3MVY9Ax.woff2 +0 -0
- package/web/assets/noto-sans-sc-78-wght-normal-5jX7iNMC.woff2 +0 -0
- package/web/assets/noto-sans-sc-79-wght-normal-BIoZy8fr.woff2 +0 -0
- package/web/assets/noto-sans-sc-80-wght-normal-Bje1Fxy1.woff2 +0 -0
- package/web/assets/noto-sans-sc-81-wght-normal-B2NN7Qi3.woff2 +0 -0
- package/web/assets/noto-sans-sc-82-wght-normal-CUggNZi4.woff2 +0 -0
- package/web/assets/noto-sans-sc-83-wght-normal-99rD8YO-.woff2 +0 -0
- package/web/assets/noto-sans-sc-84-wght-normal-zMBmUocm.woff2 +0 -0
- package/web/assets/noto-sans-sc-85-wght-normal-CjI3qdxG.woff2 +0 -0
- package/web/assets/noto-sans-sc-86-wght-normal-BHY8TVEH.woff2 +0 -0
- package/web/assets/noto-sans-sc-87-wght-normal-DioZSHnU.woff2 +0 -0
- package/web/assets/noto-sans-sc-88-wght-normal-BXYxwf2t.woff2 +0 -0
- package/web/assets/noto-sans-sc-89-wght-normal-ClMFrFaL.woff2 +0 -0
- package/web/assets/noto-sans-sc-90-wght-normal-BnOlPreP.woff2 +0 -0
- package/web/assets/noto-sans-sc-91-wght-normal-DTr9RQcS.woff2 +0 -0
- package/web/assets/noto-sans-sc-97-wght-normal-9u6Aiav-.woff2 +0 -0
- package/web/assets/noto-sans-sc-98-wght-normal-BR-H2C0V.woff2 +0 -0
- package/web/assets/noto-sans-sc-99-wght-normal-BI6XPoxp.woff2 +0 -0
- package/web/assets/noto-sans-sc-cyrillic-wght-normal-BGmsQlw-.woff2 +0 -0
- package/web/assets/noto-sans-sc-latin-ext-wght-normal-DuJTpTg_.woff2 +0 -0
- package/web/assets/noto-sans-sc-latin-wght-normal-DDW8mKAR.woff2 +0 -0
- package/web/assets/noto-sans-sc-vietnamese-wght-normal-Bi3ATJ76.woff2 +0 -0
- package/web/fonts/LICENSE.txt +287 -0
- package/web/icon.svg +24 -0
- package/web/index.html +19 -0
- package/web/manifest.webmanifest +18 -0
package/client.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Generated by scripts/build-dsh.mjs from src/dsh — do not edit. */
|
|
2
|
+
/* 17 client modules bundled from src/dsh/client. */
|
|
3
|
+
window.__ModuleLoader__.load({id:"@ohmykreee/dsh-remote-control",factory(e){var n={"client/api.js":function(e,n,t){"use strict";function r(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function o(e){const n=r(e)?e.error:void 0;return n?String(n):void 0}function a(e){return r(e)&&"string"==typeof e.id&&"string"==typeof e.ip&&"number"==typeof e.connectedAt}async function i(e,n={}){const t=await fetch(e,{credentials:"same-origin",cache:"no-store",...n,headers:{...void 0===n.body?{}:{"content-type":"application/json"},...n.headers??{}}}),r=await t.json().catch(()=>null);if(!t.ok)throw new Error(o(r)||`HTTP ${t.status}`);return r}Object.defineProperty(n,"__esModule",{value:!0}),n.DISCONNECT_URL=n.TOKEN_URL=n.LOCALE_URL=n.THEME_URL=n.GEOCODE_URL=n.LOCATION_URL=n.APP_NAMES_URL=n.SETTINGS_URL=void 0,n.isRecord=r,n.isSettingsPayload=function(e){return r(e)&&r(e.settings)},n.errorText=o,n.describeError=function(e){const n=r(e)?e.message:void 0;return String(n||e)},n.readClients=function(e){return(r(e)&&Array.isArray(e.clients)?e.clients:[]).filter(a)},n.requestJson=i,n.postJson=function(e,n){return i(e,{method:"POST",...void 0===n?{}:{body:JSON.stringify(n)}})},n.SETTINGS_URL="/dsh-remote-control/api/settings",n.APP_NAMES_URL="/dsh-remote-control/api/apps",n.LOCATION_URL="/dsh-remote-control/api/location",n.GEOCODE_URL="/dsh-remote-control/api/geocode",n.THEME_URL="/dsh-remote-control/api/theme",n.LOCALE_URL="/dsh-remote-control/api/locale",n.TOKEN_URL="/dsh-remote-control/api/token/rotate",n.DISCONNECT_URL="/dsh-remote-control/api/disconnect"},"client/i18n.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.DICTS=n.en=n.zh=n.SUPPORTED_LOCALES=n.NS=void 0,n.configureLocale=function(e){o=e},n.dshLocaleId=s,n.resolveLocale=c,n.currentLocale=d,n.setPluginLanguage=function(e){const n="zh"===e||"en"===e?e:"dsh";n!==a&&(a=n,function(){for(const e of[...i])try{e()}catch{}}())},n.getPluginLanguage=function(){return a},n.subscribePluginLanguage=l,n.fallbackTranslate=p,n.translate=u,n.useTranslator=function(){const[,e]=(0,r.useState)(0);return(0,r.useEffect)(()=>{const n=()=>e(e=>e+1),t=l(n),r=o?.subscribe?.(n);return()=>{t();try{r?.()}catch{}}},[]),u};const r=t("react");n.NS="dsh-remote-control",n.SUPPORTED_LOCALES=["zh","en"],n.zh={title:"远程控制台",kicker:"REMOTE CONSOLE",heading:"远程控制台",subtitle:"手机控制台的连接、显示与偏好设置。",running:"运行中",stopped:"未运行",refresh:"刷新",save:"保存",saved:"已保存",error:"操作失败",loading:"正在读取设置…",pairing:"扫码连接",pairingHint:"手机与电脑连接同一个局域网,扫描二维码即可打开。",open:"在电脑上打开",copy:"复制链接",copied:"已复制",connection:"连接",menuPlugin:"偏好设置",menuNetwork:"开放网卡",menuAccess:"访问验证",menuTheme:"主题与页面",menuTls:"HTTPS 证书",menuWeather:"天气设置",menuBalance:"余额显示",menuNotifications:"通知设置",menuSystem:"系统信息",menuStatus:"服务状态",menuClients:"已连接设备",menuDiagnostics:"诊断信息",network:"开放网卡",networkHint:"只有勾选网卡所在网段的设备可以访问控制台。",pluginHint:"手机端的访问方式、温度单位、主题、页面顺序与 HTTPS 证书;修改后点击保存设置生效。",access:"访问验证",accessHint:"开启后,手机首次打开需要扫描带验证令牌的链接。",token:"链接验证",rotate:"刷新验证",rotateHint:"刷新后已连接设备会断开,需要重新扫码。",theme:"主题与页面",themeHint:"选择手机控制台跟随 DSH 主题,或固定亮暗模式。",temperatureUnit:"温度单位",temperatureUnitHint:"手机端天气和性能页使用的温度显示单位。",celsius:"摄氏度 (°C)",fahrenheit:"华氏度 (°F)",followDsh:"跟随 DSH",language:"插件语言",languageHint:"选择界面语言;没有对应翻译时使用英语。",languageFollowDsh:"跟随 DSH",light:"浅色",dark:"深色",pageOrder:"页面顺序",pageOrderHint:"拖拽调整手机端的页面顺序;关闭的页面不会显示。",tls:"HTTPS 证书",tlsHint:"可选。填写证书和私钥文件路径后启用 HTTPS;保存后连接会短暂中断。",certPath:"证书路径",keyPath:"私钥路径",saveTls:"保存证书配置",tlsOn:"HTTPS 已启用",tlsOff:"HTTPS 未启用",tlsError:"证书无效,当前仍使用 HTTP",weather:"天气设置",weatherHint:"自动定位优先使用本机位置;手动位置可搜索城市,选中后自动填入经纬度。",forecastDisplay:"天气卡片显示",hourly:"小时天气",daily:"7 天天气",saveSettings:"保存设置",pagePerformance:"性能",auto:"本机位置",manual:"手动位置",locationName:"位置名称",latitude:"纬度",longitude:"经度",saveLocation:"保存位置",citySearchPlaceholder:"输入城市名搜索",citySearchHint:"输入城市名后从下拉结果中选择,纬度和经度会自动填入;也可以直接编辑坐标。",locationAutoHint:"本机位置模式下坐标由系统定位提供,切换到「手动位置」后可搜索或填写城市与坐标。",citySearching:"正在搜索…",cityNoResults:"没有找到匹配的城市",citySearchFailed:"城市搜索失败,请稍后重试",citySearchUnavailable:"城市搜索暂不可用,请重启 DSH 后重试",balance:"余额显示",balanceHint:"选择本周或最近 7 天作为消耗统计窗口。",calendar:"本周",rolling:"最近 7 天",lowThreshold:"低余额提醒线",lowThresholdHint:"低于这个金额时,手机端余额会显示为红色。留空关闭提醒。",thresholdPlaceholder:"例如:5",saveThreshold:"保存提醒线",notifications:"通知设置",notificationsHint:"所有应用默认接收通知;取消勾选的应用不会显示在手机端。",allApps:"全部显示",limit:"显示数量",noApps:"还没有记录到通知应用。",status:"服务状态",statusHint:"当前系统服务能力。",clients:"已连接设备",clientsHint:"正在访问控制台的手机或浏览器。",noClients:"当前没有已连接设备。",disconnect:"断开",diagnostics:"诊断信息",pluginVersion:"插件版本",protocol:"连接方式",profileName:"Profile",notificationsStatus:"通知",mediaStatus:"媒体",volumeStatus:"音量",weatherStatus:"天气",healthy:"正常",unavailable:"不可用",checking:"待检测",pageHome:"首页",pageSessions:"会话",pageMusic:"音乐",moveUp:"上移",moveDown:"下移",noWebServer:"远程控制台页面暂不可用,请重启 DSH 后重试。",serviceRunning:"服务运行中",serviceStopped:"服务未运行",menuPomodoro:"番茄钟/倒计时",connectionHint:"扫码配对、可访问的网卡、访问验证与 HTTPS 证书。",preferencesHint:"手机端的温度单位、主题、页面顺序与导航栏位置;修改后点击保存设置生效。",navPosition:"导航栏位置",navPositionHint:"选择手机控制台导航栏停靠在屏幕的哪一侧。",navLeft:"左侧",navRight:"右侧",navTop:"上方",navBottom:"下方",pomodoro:"番茄钟/倒计时",pomodoroHint:"手机端番茄钟与倒计时的启用、时长、预设与提示音;两者只能开启一个。",pomodoroEnable:"启用番茄钟",pomodoroEnableHint:"启用后,时钟下方会出现番茄钟卡片,并可设置专注与休息时长;启用倒计时会关闭番茄钟。",pomodoroFocus:"专注时长(分钟)",pomodoroBreak:"休息时长(分钟)",pomodoroDurations:"专注与休息",pomodoroDurationHint:"分钟数支持 2 位小数,超出允许范围时会自动调整。",pomodoroSound:"提示音文件",pomodoroSoundSection:"提示音",pomodoroSoundHint:"番茄钟和倒计时结束时使用;留空则使用内置的轻柔提示音,也可以填写本机音频文件路径或 http(s) 链接。",pomodoroSoundPlaceholder:"例如:D:\\sounds\\chime.mp3",pomodoroLoop:"循环响铃直到停止",pomodoroLoopHint:"开启后,时间到的提示音会循环播放,直到在手机端点击「停止响铃」才停止;关闭时只播放一次。",countdown:"倒计时",countdownHint:"最多可添加 5 个快速预设,按「分」「秒」填写,秒数会自动进位到分钟。",countdownPresets:"倒计时预设",countdownEnable:"启用倒计时",countdownEnableHint:"启用后,时钟下方会出现倒计时卡片;与番茄钟互斥,开启此项会关闭番茄钟。",countdownPreset1:"预设 1",countdownPreset2:"预设 2",countdownPreset3:"预设 3",countdownPreset4:"预设 4",countdownPreset5:"预设 5",countdownPresetLimitHint:"最多 5 个",addPreset:"添加预设",removePreset:"删除",peakValley:"显示梁文峰 / 梁文谷",peakValleyHint:"在余额卡片右上角显示当前计费时段:工作日 9:00–12:00、14:00–18:00 为高峰;其余时间、周末与节假日为低谷,价格为高峰的一半。",menuAppMapping:"软件映射",appMapping:"软件映射",appMappingHint:"通知和音乐中显示的应用名称;修改后立即保存。",sourceNotification:"通知",sourceMusic:"音乐",resetName:"恢复默认",locationPermission:"定位权限",locationPermissionHint:"Windows 定位需要系统隐私授权。点击下面的按钮申请定位权限;如果被拒绝,会自动打开系统「位置」设置页。开启「位置服务」和「允许桌面应用访问你的位置」后重试即可。",locationRequest:"请求定位权限",locationRequesting:"正在请求…",locationAllowed:"定位权限已授予,天气会自动使用本机位置。",locationPrivacyDenied:"系统拒绝了定位权限,已打开「位置」设置页;请开启「位置服务」和「允许桌面应用访问你的位置」后重试。",locationLowIntegrity:"定位权限被系统拒绝,请按安装文档修复后重试。",locationUnspecified:"系统还没有授予定位权限,已打开设置页,请手动开启后重试。",locationFailed:"定位权限请求失败,请稍后重试。",locationStatus:"当前状态",accessAllowed:"已授权",accessDenied:"已拒绝",accessUnspecified:"未授权",accessUnknown:"未知",pushQuestions:"推送问询",pushQuestionsHint:"开启后,等待回答的会话会显示完整问题与选项,可直接在手机上回复。",tlsEnable:"启用 HTTPS",weatherPageHint:"手机端天气的位置来源、显示方式与定位权限。",weatherSource:"天气位置",forecastDisplayHint:"选择天气卡片展示小时预报还是 7 天预报。",balancePageHint:"手机端余额卡片的统计窗口、低余额提醒与峰谷标记。",balanceSpend:"消耗统计",peakValleyBlock:"梁文峰/谷标记",peakValleyToggle:"显示峰谷标记",notificationsPageHint:"手机端通知卡片的推送、可见应用与显示数量。",notificationApps:"通知应用",pushQuestionsToggle:"允许远程回答",limitDisplay:"显示数量",limitField:"数量",notificationLimitHint:"手机端通知卡片最多显示的通知数量(1–20)。",appsPageHint:"手机端通知和音乐里显示的应用名称;修改后立即保存。",appNamesBlock:"应用名称",systemPageHint:"服务运行状态、系统能力、已连接设备与诊断信息。",diagnosticsHint:"插件/探针版本、协议、Profile 与运行时信息。",performanceStatus:"性能",locationCapability:"定位",runtime:"系统能力",runtimeHint:"原生探针与 Node.js 运行时的版本、平台和平台后端。",probeVersion:"探针版本",probeBackends:"平台后端",probeBinary:"探针路径",probeMissing:"探针不可用",nodeVersion:"Node.js 版本",nodeV8:"V8 版本",nodeMemory:"内存占用",nodePlatform:"平台",backendMedia:"媒体",backendVolume:"音量",backendPerformance:"性能",backendNotifications:"通知",backendLocation:"定位",device:"设备",durationSeconds:"秒",durationMinutes:"分钟",durationHours:"小时",durationDays:"天",keepOneApp:"至少保留一个通知应用",keepOnePage:"至少保留一个页面",tlsPathsRequired:"请先填写证书和私钥路径"},n.en={title:"Remote control",kicker:"REMOTE CONSOLE",heading:"Remote control",subtitle:"Connection, display, and preferences for the phone console.",running:"Running",stopped:"Stopped",refresh:"Refresh",save:"Save",saved:"Saved",error:"Action failed",loading:"Reading settings…",pairing:"Scan to connect",pairingHint:"Connect the phone to the same LAN and scan the QR code.",open:"Open on this computer",copy:"Copy link",copied:"Copied",connection:"Connection",menuPlugin:"Preferences",menuNetwork:"Open interfaces",menuAccess:"Access verification",menuTheme:"Theme & pages",menuTls:"HTTPS certificate",menuWeather:"Weather",menuBalance:"Balance display",menuNotifications:"Notifications",menuSystem:"System information",menuStatus:"Service status",menuClients:"Connected devices",menuDiagnostics:"Diagnostics",network:"Open network interfaces",networkHint:"Only devices on the checked interface subnets can access the console.",pluginHint:"Access, temperature unit, theme, page order, and HTTPS for the phone console; changes apply after Save.",access:"Access verification",accessHint:"When enabled, the first open requires the pairing link.",token:"Link verification",rotate:"Refresh verification",rotateHint:"Rotating disconnects existing devices; scan again to reconnect.",theme:"Theme & pages",themeHint:"Follow DSH, or force light or dark mode on the phone console.",temperatureUnit:"Temperature unit",temperatureUnitHint:"Temperature unit used by the phone weather and performance pages.",celsius:"Celsius (°C)",fahrenheit:"Fahrenheit (°F)",followDsh:"Follow DSH",language:"Plugin language",languageHint:"Interface language. English is used when a translation is missing.",languageFollowDsh:"Follow DSH",light:"Light",dark:"Dark",pageOrder:"Page order",pageOrderHint:"Drag to reorder the phone pages; disabled pages are hidden.",tls:"HTTPS certificate",tlsHint:"Optional. Provide certificate and private-key file paths, then enable HTTPS. Saving briefly drops connections.",certPath:"Certificate path",keyPath:"Private key path",saveTls:"Save certificate",tlsOn:"HTTPS is enabled",tlsOff:"HTTPS is not enabled",tlsError:"The certificate is invalid; still using HTTP",weather:"Weather",weatherHint:"Automatic location uses this computer first; Manual location lets you search for a city and fills the coordinates when you pick one.",forecastDisplay:"Weather card",hourly:"Hourly",daily:"7-day",saveSettings:"Save settings",pagePerformance:"Performance",auto:"This computer",manual:"Manual location",locationName:"Location name",latitude:"Latitude",longitude:"Longitude",saveLocation:"Save location",citySearchPlaceholder:"Search for a city",citySearchHint:"Type a city and pick a suggestion to fill latitude and longitude automatically; you can also edit the coordinates directly.",locationAutoHint:"This-computer mode uses the system location; switch to Manual location to search or enter a city and coordinates.",citySearching:"Searching…",cityNoResults:"No matching city found",citySearchFailed:"City search failed; try again later",citySearchUnavailable:"City search is unavailable; restart DSH and retry",balance:"Balance display",balanceHint:"Choose this week or the last 7 days as the spend window.",calendar:"This week",rolling:"Last 7 days",lowThreshold:"Low-balance threshold",lowThresholdHint:"Below this amount the phone balance turns red. Leave empty to disable the alert.",thresholdPlaceholder:"e.g. 5",saveThreshold:"Save threshold",notifications:"Notifications",notificationsHint:"Every app is included by default; unchecked apps are hidden on the phone.",allApps:"Select all",limit:"Display limit",noApps:"No notification apps recorded yet.",status:"Service status",statusHint:"Capabilities of the current system service.",clients:"Connected devices",clientsHint:"Phones or browsers currently viewing the console.",noClients:"No devices are connected.",disconnect:"Disconnect",diagnostics:"Diagnostics",pluginVersion:"Plugin version",protocol:"Transport",profileName:"Profile",notificationsStatus:"Notifications",mediaStatus:"Media",volumeStatus:"Volume",weatherStatus:"Weather",healthy:"Healthy",unavailable:"Unavailable",checking:"Checking",pageHome:"Home",pageSessions:"Sessions",pageMusic:"Music",moveUp:"Move up",moveDown:"Move down",noWebServer:"The console page is unavailable; restart DSH and retry.",serviceRunning:"Service running",serviceStopped:"Service stopped",menuPomodoro:"Pomodoro / countdown",connectionHint:"Pairing, reachable interfaces, access verification, and the HTTPS certificate.",preferencesHint:"Temperature unit, theme, page order, and navigation placement for the phone console; save to apply.",navPosition:"Navigation bar",navPositionHint:"Choose where the phone navigation bar sits.",navLeft:"Left",navRight:"Right",navTop:"Top",navBottom:"Bottom",pomodoro:"Pomodoro / countdown",pomodoroHint:"Enable and configure the phone pomodoro and countdown; only one can be on.",pomodoroEnable:"Enable pomodoro",pomodoroEnableHint:"Show a pomodoro card below the clock and set focus and break lengths. Enabling the countdown turns the pomodoro off.",pomodoroFocus:"Focus minutes",pomodoroBreak:"Break minutes",pomodoroDurations:"Focus and break",pomodoroDurationHint:"Minutes accept two decimals and adjust to the allowed range automatically.",pomodoroSound:"Alert sound file",pomodoroSoundSection:"Alert sound",pomodoroSoundHint:"Used when the pomodoro or countdown ends. Leave empty for the built-in gentle chime, or use a local audio file path or http(s) URL.",pomodoroSoundPlaceholder:"e.g. D:\\sounds\\chime.mp3",pomodoroLoop:"Loop until stopped",pomodoroLoopHint:"When enabled, the end-of-timer sound repeats until you click Stop ringing on the phone. When disabled, it plays once.",countdown:"Countdown",countdownHint:"Add up to five quick presets in minutes and seconds; seconds carry into minutes automatically.",countdownPresets:"Countdown presets",countdownEnable:"Enable countdown",countdownEnableHint:"Show a countdown card below the clock. The pomodoro and countdown are mutually exclusive; enabling this turns the pomodoro off.",countdownPreset1:"Preset 1",countdownPreset2:"Preset 2",countdownPreset3:"Preset 3",countdownPreset4:"Preset 4",countdownPreset5:"Preset 5",countdownPresetLimitHint:"Up to 5",addPreset:"Add preset",removePreset:"Remove",peakValley:"Show peak / valley",peakValleyHint:"Show the current billing period in the balance card header: weekdays 09:00–12:00 and 14:00–18:00 are peak; other times, weekends and holidays are off-peak at half the peak price.",menuAppMapping:"App names",appMapping:"App names",appMappingHint:"App names shown in notifications and music; changes save immediately.",sourceNotification:"Notification",sourceMusic:"Music",resetName:"Reset",locationPermission:"Location permission",locationPermissionHint:'Windows location access needs a privacy grant. The button asks for permission; when it is denied, the system Location settings page opens. Enable "Location services" and "Let desktop apps access your location", then retry.',locationRequest:"Request location access",locationRequesting:"Requesting…",locationAllowed:"Location access granted; weather will use this computer position.",locationPrivacyDenied:'Windows denied location access and the Settings page was opened; enable "Location services" and "Let desktop apps access your location", then retry.',locationLowIntegrity:"Location access was denied by the system. Follow the installation guide to fix it, then retry.",locationUnspecified:"Location access has not been granted yet; the Settings page was opened.",locationFailed:"The location permission request failed; try again later.",locationStatus:"Current status",accessAllowed:"Allowed",accessDenied:"Denied",accessUnspecified:"Not granted",accessUnknown:"Unknown",pushQuestions:"Push inquiries",pushQuestionsHint:"When enabled, sessions waiting for an answer show the full questions and options, and you can reply from the phone.",tlsEnable:"Enable HTTPS",weatherPageHint:"Location source, display mode, and location permission for the phone weather page.",weatherSource:"Weather location",forecastDisplayHint:"Choose hourly or 7-day forecast for the weather card.",balancePageHint:"Spend window, low-balance alert, and peak/off-peak marker for the phone balance card.",balanceSpend:"Spend window",peakValleyBlock:"Peak / off-peak marker",peakValleyToggle:"Show peak/off-peak marker",notificationsPageHint:"Push questions, visible apps, and display limit for phone notifications.",notificationApps:"Notification apps",pushQuestionsToggle:"Allow remote answers",limitDisplay:"Display limit",limitField:"Count",notificationLimitHint:"Maximum number of notifications shown on the phone card (1–20).",appsPageHint:"App names shown in phone notifications and media; changes save immediately.",appNamesBlock:"App names",systemPageHint:"Service status, system capabilities, connected devices, and diagnostics.",diagnosticsHint:"Plugin/probe versions, transport, profile, and runtime details.",performanceStatus:"Performance",locationCapability:"Location",runtime:"System capabilities",runtimeHint:"Versions, platforms, and platform backends of the native probe and the Node.js runtime.",probeVersion:"Probe version",probeBackends:"Platform backends",probeBinary:"Probe path",probeMissing:"Probe unavailable",nodeVersion:"Node.js version",nodeV8:"V8 version",nodeMemory:"Memory",nodePlatform:"Platform",backendMedia:"Media",backendVolume:"Volume",backendPerformance:"Performance",backendNotifications:"Notifications",backendLocation:"Location",device:"Device",durationSeconds:"sec",durationMinutes:"min",durationHours:"h",durationDays:"d",keepOneApp:"Keep at least one notification app",keepOnePage:"Keep at least one page",tlsPathsRequired:"Fill in the certificate and private key paths first"},n.DICTS={zh:n.zh,en:n.en};let o=null,a="dsh";const i=new Set;function s(){try{const e=o?.getLocale?.()??o?.getSnapshot?.();return String(e?.active||"")}catch{return""}}function c(e,t){if("zh"===e||"en"===e)return e;const r=String(t||"").trim().toLowerCase();return n.SUPPORTED_LOCALES.find(e=>e===r)??"en"}function d(){return c(a,s())}function l(e){return i.add(e),()=>{i.delete(e)}}function p(e){return n.DICTS[d()][e]??n.en[e]??e}function u(e){return p(e)}},"client/icons.js":function(e,n,t){"use strict";var r,o=this&&this.__createBinding||(Object.create?function(e,n,t,r){void 0===r&&(r=t);var o=Object.getOwnPropertyDescriptor(n,t);o&&!("get"in o?!n.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return n[t]}}),Object.defineProperty(e,r,o)}:function(e,n,t,r){void 0===r&&(r=t),e[r]=n[t]}),a=this&&this.__setModuleDefault||(Object.create?function(e,n){Object.defineProperty(e,"default",{enumerable:!0,value:n})}:function(e,n){e.default=n}),i=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var n=[];for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[n.length]=t);return n},r(e)},function(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t=r(e),i=0;i<t.length;i++)"default"!==t[i]&&o(n,e,t[i]);return a(n,e),n});Object.defineProperty(n,"__esModule",{value:!0}),n.MENU_ICONS=void 0,n.PanelIcon=function({size:e=20,active:n=!1}){return s("svg",{width:e,height:e,viewBox:"0 0 24 24",fill:"none","aria-hidden":!0,style:{display:"block",color:n?"var(--dsw-alias-brand-primary)":"currentColor"}},s("rect",{x:4.2,y:2.8,width:15.6,height:18.4,rx:4,stroke:"currentColor",strokeWidth:1.7}),s("path",{d:"M9.3 18.1h5.4",stroke:"currentColor",strokeWidth:1.7,strokeLinecap:"round"}),s("circle",{cx:12,cy:10.5,r:3.5,stroke:"currentColor",strokeWidth:1.7}),s("path",{d:"M2.6 8.2c-1 1.1-1.5 2.3-1.5 3.6 0 1.4.5 2.6 1.5 3.7M21.4 8.2c1 1.1 1.5 2.3 1.5 3.6 0 1.4-.5 2.6-1.5 3.7",stroke:"currentColor",strokeWidth:1.5,strokeLinecap:"round",opacity:.7}))},n.MenuIcon=function({name:e}){return s("svg",{viewBox:"0 0 24 24",fill:"none","aria-hidden":!0,dangerouslySetInnerHTML:{__html:n.MENU_ICONS[e]||n.MENU_ICONS.info}})};const s=i(t("react")).createElement;n.MENU_ICONS={link:'<path d="M9.5 14.5 14.5 9.5M7.1 16.9a3.5 3.5 0 0 1 0-5l2.1-2.1M16.9 7.1a3.5 3.5 0 0 1 0 5l-2.1 2.1" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>',sliders:'<path d="M4 7h10M18 7h2M4 17h2M10 17h10" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/><circle cx="16" cy="7" r="2.4" stroke="currentColor" stroke-width="1.7"/><circle cx="8" cy="17" r="2.4" stroke="currentColor" stroke-width="1.7"/>',cloud:'<path d="M6.5 18h10.2a4.3 4.3 0 0 0 .7-8.5A5.5 5.5 0 0 0 7 8.6 4.7 4.7 0 0 0 6.5 18Z" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>',wallet:'<rect x="3.5" y="6" width="17" height="13" rx="3" stroke="currentColor" stroke-width="1.7"/><path d="M3.5 10h17M16 14.5h2" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>',bell:'<path d="M6 10a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6ZM10 20h4" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>',apps:'<rect x="3.5" y="3.5" width="7.2" height="7.2" rx="1.8" stroke="currentColor" stroke-width="1.7"/><rect x="13.3" y="3.5" width="7.2" height="7.2" rx="1.8" stroke="currentColor" stroke-width="1.7"/><rect x="3.5" y="13.3" width="7.2" height="7.2" rx="1.8" stroke="currentColor" stroke-width="1.7"/><rect x="13.3" y="13.3" width="7.2" height="7.2" rx="1.8" stroke="currentColor" stroke-width="1.7"/>',timer:'<circle cx="12" cy="13.5" r="7" stroke="currentColor" stroke-width="1.7"/><path d="M12 9.5v4l2.8 1.7M9 2.8h6M12 2.8v2.2M18.8 6.2l1.5-1.5" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>',info:'<circle cx="12" cy="12" r="8.5" stroke="currentColor" stroke-width="1.7"/><path d="M12 11v6M12 7.2v.2" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>'}},"client/index.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.__test=n.inject=void 0,n.apply=function(e){(0,o.configureLocale)(e.locale),e.effect(()=>e.locale.register(o.NS,o.DICTS),"dsh-remote-control: dictionaries"),e.effect(()=>{const n=()=>{try{const n=e.locale.getLocale?.()??e.locale.getSnapshot?.(),t=String(n?.active||"");if(!t)return;fetch(r.LOCALE_URL,{method:"POST",credentials:"same-origin",headers:{"content-type":"application/json"},body:JSON.stringify({locale:t})}).catch(()=>{})}catch{}};let t;n();try{t=e.locale.subscribe?.(n)}catch{t=void 0}return()=>{try{t?.()}catch{}}},"dsh-remote-control: locale sync"),e.effect(()=>{let n=!0;const t=t=>{if(!n)return;const o=t?.active?.colorScheme||e.theme?.getTheme?.()?.active?.colorScheme;o&&fetch(r.THEME_URL,{method:"POST",credentials:"same-origin",headers:{"content-type":"application/json"},body:JSON.stringify({theme:o})}).catch(()=>{})};t(e.theme?.getTheme?.());const o=e.on?.("theme/change",t);return()=>{n=!1;try{o?.()}catch{}}},"dsh-remote-control: theme sync"),e.effect(()=>e.slots.inject("sidebar.panellist",()=>e.slots.register({name:"sidebar.panellist",id:s,order:12,label:()=>(0,o.translate)("title")},a.PanelIcon)),"dsh-remote-control: sidebar launcher"),e.effect(()=>e.slots.inject("main",()=>e.slots.register({name:"main",key:s},i.RemotePanel)),"dsh-remote-control: settings panel")};const r=t("./api.js"),o=t("./i18n.js"),a=t("./icons.js"),i=t("./panel.js"),s="remote-control";n.inject=["slots","locale","theme"],n.__test={zh:o.zh,en:o.en,PanelIcon:a.PanelIcon,RemotePanel:i.RemotePanel}},"client/logic.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.EMPTY_DRAFT=n.POMODORO_MAX_BREAK_MINUTES=n.POMODORO_MAX_FOCUS_MINUTES=n.POMODORO_MIN_MINUTES=n.DEFAULT_COUNTDOWN_PRESETS=n.COUNTDOWN_MAX_SECONDS=n.COUNTDOWN_MIN_SECONDS=n.COUNTDOWN_PRESET_LIMIT=n.PAGE_ORDER=void 0,n.parseCountdownPreset=o,n.presetDraftToSeconds=i,n.sanitizeMinuteInput=function(e){const n=e.replace(/[^\d.]/g,""),t=n.indexOf(".");if(t<0)return n;return`${n.slice(0,t)}.${n.slice(t+1).replace(/\./g,"").slice(0,2)}`},n.capMinuteInput=function(e,n,t){const r=e.trim();if(""===r)return"";const o=Number(r);return Number.isFinite(o)?o>=n&&o<=t?e:String(Math.max(n,Math.min(t,o))):""},n.capPresetDraft=function(e){const t=e.minutes.trim(),r=e.seconds.trim();if(""===t&&""===r)return e;const o=Number(t||0),a=Number(r||0);if(!Number.isFinite(o)||!Number.isFinite(a))return e;const i=Math.round(60*o+a),s=Math.max(n.COUNTDOWN_MIN_SECONDS,Math.min(n.COUNTDOWN_MAX_SECONDS,i));return{minutes:String(Math.floor(s/60)),seconds:String(s%60)}},n.normalizeCountdownPresets=s,n.createCountdownDraft=c,n.normalizePageOrder=function(e){const t=(Array.isArray(e)?e:[]).filter(e=>"string"==typeof e&&n.PAGE_ORDER.includes(e)),r=[...new Set(t)];for(const e of n.PAGE_ORDER)r.includes(e)||r.push(e);return r.slice(0,n.PAGE_ORDER.length)},n.pageLabels=function(e){return{home:e("pageHome"),sessions:e("pageSessions"),music:e("pageMusic"),performance:e("pagePerformance")}},n.createDraft=function(e){return{certPath:e.tls?.certPath??"",keyPath:e.tls?.keyPath??"",locationName:e.weather?.name??"",latitude:d(e.weather?.latitude),longitude:d(e.weather?.longitude),threshold:d(e.balance?.lowThreshold),notificationLimit:String(e.notifications?.limit??5),pomodoroFocus:String(e.pomodoro?.focusMinutes??25),pomodoroBreak:String(e.pomodoro?.breakMinutes??5),pomodoroSound:e.pomodoro?.soundPath??"",countdownPresets:c(e.countdown?.presets)}},n.mergeStaged=function(e,n){const t=(e,n)=>{const o={...(0,r.isRecord)(e)?e:{}};for(const[e,a]of Object.entries((0,r.isRecord)(n)?n:{}))a&&"object"==typeof a&&!Array.isArray(a)?o[e]=t(o[e],a):o[e]=a;return o};return t(e,n)},n.buildSettingsView=function(e,n){return{...e,...n,weather:{...e.weather??{},...n.weather??{}},balance:{...e.balance??{},...n.balance??{}},notifications:{...e.notifications??{},...n.notifications??{}},tls:{...e.tls??{},...n.tls??{}},pomodoro:{...e.pomodoro??{},...n.pomodoro??{}},countdown:{...e.countdown??{},...n.countdown??{}},pageEnabled:{...e.pageEnabled??{},...n.pageEnabled??{}}}},n.buildSavePatch=function(e){const{data:n,settings:t,draft:r,selectedInterfaces:o,selectedApps:a,notificationApps:i,order:c}=e,d=[...new Set((n.interfaces??[]).map(e=>e.name))],p={...t.weather??{},name:r.locationName,latitude:""===r.latitude?null:Number(r.latitude),longitude:""===r.longitude?null:Number(r.longitude)};return{allowedInterfaces:o.length===d.length?null:o,tokenEnabled:t.tokenEnabled,temperatureUnit:"fahrenheit"===t.temperatureUnit?"fahrenheit":"celsius",theme:t.theme,language:"zh"===t.language||"en"===t.language?t.language:"dsh",navPosition:t.navPosition??"left",pomodoro:{enabled:Boolean(t.pomodoro?.enabled),focusMinutes:l(r.pomodoroFocus,25),breakMinutes:l(r.pomodoroBreak,5),soundPath:r.pomodoroSound,loopAlarm:Boolean(t.pomodoro?.loopAlarm)},countdown:{enabled:Boolean(t.countdown?.enabled),presets:s(r.countdownPresets)},pageOrder:[...c],pageEnabled:t.pageEnabled,tls:{enabled:Boolean(t.tls?.enabled),certPath:r.certPath,keyPath:r.keyPath},weather:p,balance:{spendWindow:t.balance?.spendWindow??"calendar",lowThreshold:""===r.threshold?null:Number(r.threshold),showPeakValley:!1!==t.balance?.showPeakValley},notifications:{enabledApps:i.length>0&&a.length===i.length?[]:a,limit:Number(r.notificationLimit),pushQuestions:Boolean(t.notifications?.pushQuestions)}}},n.locationHint=function(e,n){return n.allowed?e("locationAllowed"):"low-integrity"===n.reason?e("locationLowIntegrity"):"privacy-denied"===n.reason?e("locationPrivacyDenied"):"unspecified"===n.reason?e("locationUnspecified"):e("locationFailed")},n.accessLabel=function(e,n){return e("allowed"===n?"accessAllowed":"denied"===n?"accessDenied":"unspecified"===n?"accessUnspecified":"accessUnknown")},n.formatDuration=function(e,n){const t=Math.max(0,Math.floor(e/1e3));if(t<60)return`${t} ${n("durationSeconds")}`;if(t<3600)return`${Math.floor(t/60)} ${n("durationMinutes")}`;if(t<86400){const e=Math.floor(t/3600),r=Math.floor(t%3600/60);return`${e} ${n("durationHours")} ${r} ${n("durationMinutes")}`}const r=Math.floor(t/86400),o=Math.floor(t%86400/3600);return`${r} ${n("durationDays")} ${o} ${n("durationHours")}`},n.formatBytes=function(e){const n=Number(e);if(!Number.isFinite(n)||n<0)return"—";const t=["B","KB","MB","GB","TB"];let r=n,o=0;for(;r>=1024&&o<t.length-1;)r/=1024,o+=1;const a=0===o||r>=10?0:1;return`${r.toFixed(a)} ${t[o]}`};const r=t("./api.js");function o(e,t=null){const r="string"==typeof e?e.trim():e;if(""===r||null==r)return t;const o=Math.round(Number(r));return Number.isFinite(o)?Math.max(n.COUNTDOWN_MIN_SECONDS,Math.min(n.COUNTDOWN_MAX_SECONDS,o)):t}function a(e){return(0,r.isRecord)(e)&&"string"==typeof e.minutes&&"string"==typeof e.seconds}function i(e){const t=e.minutes.trim(),r=e.seconds.trim();if(""===t&&""===r)return null;const o=Number(t||0),a=Number(r||0);if(!Number.isFinite(o)||!Number.isFinite(a))return null;const i=Math.round(60*o+a);return Math.max(n.COUNTDOWN_MIN_SECONDS,Math.min(n.COUNTDOWN_MAX_SECONDS,i))}function s(e){const t=Array.isArray(e)?e:[],r=[];for(const e of t.slice(0,n.COUNTDOWN_PRESET_LIMIT)){const n=a(e)?i(e):o(e);null!==n&&r.push(n)}return r.length>0?r:[...n.DEFAULT_COUNTDOWN_PRESETS]}function c(e){return s(e).map(e=>({minutes:String(Math.floor(e/60)),seconds:String(e%60)}))}function d(e){return null==e?"":String(e)}function l(e,n){const t=e.trim();if(""===t)return n;const r=Number(t);return Number.isFinite(r)?r:n}n.PAGE_ORDER=["home","sessions","music","performance"],n.COUNTDOWN_PRESET_LIMIT=5,n.COUNTDOWN_MIN_SECONDS=1,n.COUNTDOWN_MAX_SECONDS=86400,n.DEFAULT_COUNTDOWN_PRESETS=[60,300,600],n.POMODORO_MIN_MINUTES=.1,n.POMODORO_MAX_FOCUS_MINUTES=180,n.POMODORO_MAX_BREAK_MINUTES=120,n.EMPTY_DRAFT={certPath:"",keyPath:"",locationName:"",latitude:"",longitude:"",threshold:"",notificationLimit:"5",pomodoroFocus:"25",pomodoroBreak:"5",pomodoroSound:"",countdownPresets:c(n.DEFAULT_COUNTDOWN_PRESETS)}},"client/panel.js":function(e,n,t){"use strict";var r,o=this&&this.__createBinding||(Object.create?function(e,n,t,r){void 0===r&&(r=t);var o=Object.getOwnPropertyDescriptor(n,t);o&&!("get"in o?!n.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return n[t]}}),Object.defineProperty(e,r,o)}:function(e,n,t,r){void 0===r&&(r=t),e[r]=n[t]}),a=this&&this.__setModuleDefault||(Object.create?function(e,n){Object.defineProperty(e,"default",{enumerable:!0,value:n})}:function(e,n){e.default=n}),i=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var n=[];for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[n.length]=t);return n},r(e)},function(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t=r(e),i=0;i<t.length;i++)"default"!==t[i]&&o(n,e,t[i]);return a(n,e),n});Object.defineProperty(n,"__esModule",{value:!0}),n.RemotePanel=function(){const e=(0,d.useTranslator)(),n=function(){const[e,n]=N(()=>"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia(M).matches);return k(()=>{if("function"!=typeof window.matchMedia)return;const e=window.matchMedia(M),t=()=>n(e.matches);return t(),e.addEventListener?.("change",t),()=>e.removeEventListener?.("change",t)},[]),e}(),[t,r]=N(null),[o,a]=N(!0),[i,s]=N(!1),[H,T]=N(""),[C,E]=N(""),[_,D]=N(!1),[L,R]=N("connection"),[A,j]=N(l.EMPTY_DRAFT),[U,B]=N([]),[I,z]=N([]),[F,W]=N({}),[$,V]=N(""),[q,K]=N(!1),[G,Q]=N(null),[X,J]=N({}),[Y,Z]=N(null),[ee,ne]=N(null),te=P(null),re=P(null),oe=P(null);k(()=>()=>{re.current?.(),re.current=null},[]);const ae=S(e=>{if(!(0,c.isSettingsPayload)(e))return;r(e),J({}),(0,d.setPluginLanguage)(e.settings.language);const n=function(e){return(e.interfaces??[]).map(e=>e.name)}(e);B(null===e.settings.allowedInterfaces?n:[...e.settings.allowedInterfaces]);const t=(e.apps??[]).map(e=>e.key),o=e.settings.notifications?.enabledApps??[];z(o.length?o.filter(e=>t.includes(e)):t),W(Object.fromEntries((e.apps??[]).map(e=>[e.key,e.name]))),j((0,l.createDraft)(e.settings))},[]),ie=S(async()=>{a(!0),T("");try{ae(await(0,c.requestJson)(c.SETTINGS_URL))}catch(e){T((0,c.describeError)(e))}finally{a(!1)}},[ae]),se=S(async n=>{s(!0),T("");try{ae(await(0,c.postJson)(c.SETTINGS_URL,n)),E(e("saved")),window.setTimeout(()=>E(""),1600)}catch(e){T((0,c.describeError)(e))}finally{s(!1)}},[ae,e]),ce=S(e=>{J(n=>(0,l.mergeStaged)(n,e))},[]);k(()=>{ie()},[ie]);const de=e=>{const n=te.current;if(!n)return 0;const t=[...n.querySelectorAll(".drc-order-row:not(.is-dragging)")];for(let n=0;n<t.length;n+=1){const r=t[n].getBoundingClientRect();if(e<r.top+r.height/2)return n}return t.length};if(o)return(0,p.h)("div",{className:"drc-page"},(0,p.h)("style",null,h.CSS),(0,p.h)("div",{className:"drc-empty",style:{padding:32}},e("loading")));if(!t)return(0,p.h)("div",{className:"drc-page"},(0,p.h)("style",null,h.CSS),(0,p.h)("div",{className:"drc-empty",style:{padding:32,maxWidth:680}},(0,p.h)("h1",{className:"drc-section-title"},e("heading")),(0,p.h)("p",{className:"drc-banner is-error"},H||e("noWebServer")),(0,p.h)(p.Btn,{primary:!0,onClick:()=>{ie()}},e("refresh"))));const le=t.info??{},pe=t.clients??[],ue=(0,l.buildSettingsView)(t.settings,X);oe.current=ue;const he=(0,l.normalizePageOrder)(ue.pageOrder??l.PAGE_ORDER),me=(0,l.pageLabels)(e),ge=t.apps??[],fe=le.routes?.remote||"",be=()=>{se((0,l.buildSavePatch)({data:t,settings:ue,draft:A,selectedInterfaces:U,selectedApps:I,notificationApps:ge,order:he}))},ve={t:e,data:t,settings:ue,draft:A,info:le,apps:ge,clients:pe,busy:i,appBusyKey:$,locationBusy:q,locationResult:G,copied:_,order:he,labels:me,draggingKey:Y,dragIndex:ee,selectedInterfaces:U,selectedApps:I,appNames:F,orderListRef:te,setDraft:j,setError:T,setAppNames:W,selectAllApps:()=>{z((t?.apps??[]).map(e=>e.key))},stage:ce,save:se,saveAll:be,copy:async()=>{const n=t?.info?.routes?.remote||"";if(n)try{await navigator.clipboard.writeText(n),D(!0),window.setTimeout(()=>D(!1),1600)}catch{T(e("error"))}},rotate:async()=>{s(!0),T("");try{ae(await(0,c.postJson)(c.TOKEN_URL)),E(e("saved")),window.setTimeout(()=>E(""),1600)}catch(e){T((0,c.describeError)(e))}finally{s(!1)}},saveAppName:async(n,t)=>{V(n),T("");try{ae(await(0,c.postJson)(c.APP_NAMES_URL,{key:n,name:t})),E(e("saved")),window.setTimeout(()=>E(""),1600)}catch(e){T((0,c.describeError)(e))}finally{V("")}},requestLocation:async()=>{K(!0),T("");try{const n=await(0,c.postJson)(c.LOCATION_URL,{openSettings:!0}),t=(0,c.isRecord)(n)?n:{},r={allowed:!0===t.allowed,access:String(t.access||"unknown"),reason:"string"==typeof t.reason?t.reason:null,lowIntegrity:!0===t.lowIntegrity,openedSettings:!0===t.openedSettings};Q(r),E((0,l.locationHint)(e,r)),window.setTimeout(()=>E(""),2600)}catch(e){T((0,c.describeError)(e))}finally{K(!1)}},disconnect:async e=>{try{const n=await(0,c.postJson)(c.DISCONNECT_URL,{id:e}),t=(0,c.readClients)(n);r(e=>e?{...e,clients:t}:e)}catch(e){T((0,c.describeError)(e))}},toggleApp:n=>{const t=new Set(I);if(t.has(n)?t.delete(n):t.add(n),0===t.size)return E(e("keepOneApp")),void window.setTimeout(()=>E(""),1600);z([...t])},toggleInterface:e=>{B(n=>n.includes(e)?n.filter(n=>n!==e):[...n,e])},togglePage:n=>{const t=oe.current?.pageEnabled??{},r={...t,[n]:!t[n]};if(function(e,n){return e.every(e=>!n[e])}(l.PAGE_ORDER,r))return E(e("keepOnePage")),void window.setTimeout(()=>E(""),1600);ce({pageEnabled:r})},startOrderDrag:(e,n)=>{if("mouse"===n.pointerType&&0!==n.button)return;const t=n.target;if(t instanceof Element&&t.closest("button, input, a, select, textarea"))return;n.preventDefault();const r=n.pointerId,o=n.clientX,a=n.clientY,i=oe.current?(0,l.normalizePageOrder)(oe.current.pageOrder??l.PAGE_ORDER):l.PAGE_ORDER,s=i.indexOf(e);let c=!1;const d=n=>{if(n.pointerId!==r)return;if(window.removeEventListener("pointermove",p),window.removeEventListener("pointerup",d),window.removeEventListener("pointercancel",d),document.body.classList.remove("drc-sorting"),re.current=null,!c||"pointercancel"===n.type)return Z(null),void ne(null);const t=i.filter(n=>n!==e),o=Math.max(0,Math.min(de(n.clientY),t.length)),a=[...t.slice(0,o),e,...t.slice(o)];a.join("|")!==i.join("|")&&ce({pageOrder:a}),Z(null),ne(null)},p=n=>{if(n.pointerId!==r)return;if(!c){if(Math.hypot(n.clientX-o,n.clientY-a)<5)return;c=!0,Z(e),ne(s),document.body.classList.add("drc-sorting")}n.preventDefault();const t=de(n.clientY);ne(e=>e===t?e:t)};window.addEventListener("pointermove",p,{passive:!1}),window.addEventListener("pointerup",d),window.addEventListener("pointercancel",d),re.current=()=>{window.removeEventListener("pointermove",p),window.removeEventListener("pointerup",d),window.removeEventListener("pointercancel",d),document.body.classList.remove("drc-sorting")}}},we="connection"===L?(0,f.renderConnection)(ve):"preferences"===L?(0,w.renderPreferences)(ve):"pomodoro"===L?(0,v.renderPomodoro)(ve):"weather"===L?(0,x.renderWeather)(ve):"balance"===L?(0,g.renderBalance)(ve):"notifications"===L?(0,b.renderNotifications)(ve):"apps"===L?(0,m.renderAppMapping)(ve):(0,y.renderSystem)(ve);return(0,p.h)("div",{className:"drc-page"},(0,p.h)("style",null,h.CSS),(0,p.h)("div",{className:"drc-shell"},(0,p.h)("div",{className:"drc-nav-title"},e("heading")),(0,p.h)("header",{className:"drc-head"},(0,p.h)("div",{className:"drc-head-actions"},(0,p.h)("span",{className:"drc-status"},(0,p.h)("i",{className:"drc-dot "+(le.running?"ok":"bad")}),le.running?e("serviceRunning"):e("serviceStopped")),(0,p.h)(p.Btn,{primary:!0,onClick:be,disabled:i},e("saveSettings")),(0,p.h)(p.Btn,{disabled:!fe,onClick:()=>window.open(fe,"_blank","noopener,noreferrer")},e("open")),(0,p.h)(p.Btn,{onClick:()=>{ie()},disabled:i},e("refresh")))),n?(0,p.h)("nav",{className:"drc-nav drc-nav-compact","aria-label":e("heading")},(0,p.h)("div",{className:"drc-nav-select-wrap"},(0,p.h)("select",{className:"drc-nav-select",value:L,"aria-label":e("heading"),onChange:e=>R(e.target.value)},O.map(n=>(0,p.h)("option",{key:n.key,value:n.key},e(n.labelKey)))))):(0,p.h)("nav",{className:"drc-nav","aria-label":e("heading")},(0,p.h)("div",{className:"drc-nav-list"},O.map(n=>(0,p.h)("button",{key:n.key,type:"button",className:"drc-nav-item"+(L===n.key?" is-active":""),"aria-current":L===n.key?"page":void 0,onClick:()=>R(n.key)},(0,p.h)(u.MenuIcon,{name:n.icon}),(0,p.h)("span",{className:"drc-nav-label"},e(n.labelKey)))))),(0,p.h)("main",{className:"drc-main"},H?(0,p.h)("div",{className:"drc-banner is-error"},`${e("error")}: ${H}`):null,C?(0,p.h)("div",{className:"drc-banner is-ok"},C):null,we)))};const s=i(t("react")),c=t("./api.js"),d=t("./i18n.js"),l=t("./logic.js"),p=t("./ui.js"),u=t("./icons.js"),h=t("./styles.js"),m=t("./sections/apps.js"),g=t("./sections/balance.js"),f=t("./sections/connection.js"),b=t("./sections/notifications.js"),v=t("./sections/pomodoro.js"),w=t("./sections/preferences.js"),y=t("./sections/system.js"),x=t("./sections/weather.js"),{useEffect:k,useState:N,useCallback:S,useRef:P}=s,O=[{key:"connection",labelKey:"connection",icon:"link"},{key:"preferences",labelKey:"menuPlugin",icon:"sliders"},{key:"pomodoro",labelKey:"menuPomodoro",icon:"timer"},{key:"weather",labelKey:"menuWeather",icon:"cloud"},{key:"balance",labelKey:"menuBalance",icon:"wallet"},{key:"notifications",labelKey:"menuNotifications",icon:"bell"},{key:"apps",labelKey:"menuAppMapping",icon:"apps"},{key:"system",labelKey:"menuSystem",icon:"info"}],M="(max-width: 767px)"},"client/sections/apps.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderAppMapping=function(e){const{t:n,data:t,appNames:o,setAppNames:a,appBusyKey:i,saveAppName:s}=e,c=t.apps??[];return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(n("appMapping"),n("appsPageHint")),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("appNamesBlock"),n("appMappingHint")),0===c.length?(0,r.h)("p",{className:"drc-empty"},n("noApps")):(0,r.h)("div",{className:"drc-list"},c.map(e=>(0,r.h)("div",{className:"drc-map-row",key:e.key},(0,r.h)("div",{className:"drc-map-main"},(0,r.h)("input",{className:"drc-map-input",value:o[e.key]??e.name,placeholder:e.defaultName||e.key,onChange:n=>{const t=n.target.value;a(n=>({...n,[e.key]:t}))},onKeyDown:n=>{"Enter"===n.key&&s(e.key,n.currentTarget.value)}}),(0,r.h)("small",{className:"drc-code drc-map-key",title:e.key},e.key)),(0,r.h)("div",{className:"drc-map-meta"},(e.sources??[]).map(e=>(0,r.h)("span",{className:"drc-tag",key:e},n("music"===e?"sourceMusic":"sourceNotification"))),(0,r.h)(r.Btn,{small:!0,onClick:()=>{s(e.key,o[e.key]??e.name)},disabled:i===e.key},n("save")),(0,r.h)(r.Btn,{small:!0,onClick:()=>{s(e.key,"")},disabled:i===e.key||!e.customName},n("resetName"))))))))};const r=t("../ui.js")},"client/sections/balance.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderBalance=function(e){const{t:n,settings:t,draft:o,setDraft:a,stage:i}=e;return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(n("balance"),n("balancePageHint")),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("balanceSpend"),n("balanceHint")),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Seg,{options:[{value:"calendar",label:n("calendar")},{value:"rolling",label:n("rolling")}],value:t.balance?.spendWindow??"calendar",onChange:e=>i({balance:{spendWindow:e}})}))),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("lowThreshold"),n("lowThresholdHint")),(0,r.h)("div",{className:"drc-fields"},(0,r.h)(r.Field,{label:n("lowThreshold"),value:o.threshold,placeholder:n("thresholdPlaceholder"),inputMode:"decimal",onChange:e=>a(n=>({...n,threshold:e}))}))),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("peakValleyBlock"),n("peakValleyHint")),(0,r.h)(r.Row,{title:n("peakValleyToggle")},(0,r.h)(r.Toggle,{checked:!1!==t.balance?.showPeakValley,label:n("peakValley"),onChange:e=>i({balance:{showPeakValley:e}})}))))};const r=t("../ui.js")},"client/sections/connection.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderConnection=function(e){return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(e.t("connection"),e.t("connectionHint")),function(e){const{t:n,info:t,copied:o,copy:a}=e,i=t.routes?.remote||"";return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("pairing"),n("pairingHint")),(0,r.h)("div",{className:"drc-qr"},t.qrPath?(0,r.h)("div",{className:"drc-qr-frame"},(0,r.h)("img",{src:t.qrPath,alt:n("pairing"),width:160,height:160})):(0,r.h)("div",{className:"drc-qr-frame"}),(0,r.h)("div",{className:"drc-qr-copy"},(0,r.h)("div",{className:"drc-actions",style:{marginTop:0,paddingTop:0}},(0,r.h)(r.Btn,{primary:!0,disabled:!i,onClick:()=>window.open(i,"_blank","noopener,noreferrer")},n("open")),(0,r.h)(r.Btn,{disabled:!i,onClick:()=>{a()}},n(o?"copied":"copy"))),(0,r.h)("code",{className:"drc-url"},i||"—"),t.tls?.error?(0,r.h)("p",{className:"drc-row-desc",style:{color:"var(--drc-error)",marginTop:8}},`${n("tlsError")}: ${t.tls.error}`):null)))}(e),function(e){const{t:n,data:t,toggleInterface:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("network"),n("networkHint")),t.interfaces&&t.interfaces.length>0?(0,r.h)("div",{className:"drc-list"},t.interfaces.map(n=>(0,r.h)("div",{className:"drc-list-row",key:`${n.name}-${n.address}`},(0,r.h)("label",null,(0,r.h)("input",{type:"checkbox",checked:e.selectedInterfaces.includes(n.name),onChange:()=>o(n.name)}),(0,r.h)("span",null,n.name)),(0,r.h)("span",{className:"drc-code"},n.cidr||n.address)))):(0,r.h)("p",{className:"drc-empty"},"—"))}(e),function(e){const{t:n,settings:t,stage:o,busy:a,rotate:i}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("access"),n("accessHint")),(0,r.h)(r.Row,{title:n("token")},(0,r.h)(r.Toggle,{checked:t.tokenEnabled,label:n("token"),onChange:e=>o({tokenEnabled:e})})),(0,r.h)("div",{className:"drc-divider"}),(0,r.h)("div",{className:"drc-actions drc-actions-stack"},(0,r.h)(r.Btn,{danger:!0,disabled:a,onClick:()=>{i()}},n("rotate")),(0,r.h)("span",{className:"drc-row-desc"},n("rotateHint"))))}(e),function(e){const{t:n,settings:t,draft:o,info:a,stage:i,setDraft:s,setError:c}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("tls"),n("tlsHint")),(0,r.h)(r.Row,{title:n("tlsEnable")},(0,r.h)(r.Toggle,{checked:t.tls?.enabled,label:n("tlsEnable"),onChange:e=>{!e||o.certPath&&o.keyPath?i({tls:{enabled:e}}):c(n("tlsPathsRequired"))}})),(0,r.h)("div",{className:"drc-divider"}),(0,r.h)("div",{className:"drc-fields"},(0,r.h)(r.Field,{label:n("certPath"),value:o.certPath,placeholder:"C:\\certs\\fullchain.pem",onChange:e=>s(n=>({...n,certPath:e}))}),(0,r.h)(r.Field,{label:n("keyPath"),value:o.keyPath,placeholder:"C:\\certs\\privkey.pem",onChange:e=>s(n=>({...n,keyPath:e}))})),(0,r.h)("div",{className:"drc-actions"},(0,r.h)("span",{className:"drc-row-desc"},t.tls?.enabled?a.tls?.active?n("tlsOn"):n("tlsError"):n("tlsOff"))))}(e))};const r=t("../ui.js")},"client/sections/notifications.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderNotifications=function(e){const{t:n,data:t,settings:o,draft:a,setDraft:i,stage:s,selectedApps:c,toggleApp:d,selectAllApps:l}=e,p=t.apps??[];return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(n("notifications"),n("notificationsPageHint")),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("pushQuestions"),n("pushQuestionsHint")),(0,r.h)(r.Row,{title:n("pushQuestionsToggle")},(0,r.h)(r.Toggle,{checked:Boolean(o.notifications?.pushQuestions),label:n("pushQuestions"),onChange:e=>s({notifications:{pushQuestions:e}})}))),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("notificationApps"),n("notificationsHint")),0===p.length?(0,r.h)("p",{className:"drc-empty"},n("noApps")):(0,r.h)("div",{className:"drc-apps"},p.map(e=>(0,r.h)("button",{key:e.key,type:"button",className:"drc-app"+(c.includes(e.key)?" is-on":""),onClick:()=>d(e.key)},(0,r.h)("i"),e.name))),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Btn,{onClick:l},n("allApps")))),(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("limitDisplay"),n("notificationLimitHint")),(0,r.h)("div",{className:"drc-fields"},(0,r.h)(r.Field,{label:n("limitField"),value:a.notificationLimit,placeholder:"5",inputMode:"numeric",onChange:e=>i(n=>({...n,notificationLimit:e}))}))))};const r=t("../ui.js")},"client/sections/pomodoro.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderPomodoro=function(e){return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(e.t("pomodoro"),e.t("pomodoroHint")),function(e){const{t:n,settings:t,draft:i,setDraft:s,stage:c}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.h)(r.Row,{title:n("pomodoroEnable"),description:n("pomodoroEnableHint")},(0,r.h)(r.Toggle,{checked:Boolean(t.pomodoro?.enabled),label:n("pomodoroEnable"),onChange:e=>{const n={pomodoro:{enabled:e}};e&&(n.countdown={enabled:!1}),c(n)}})),(0,r.h)("div",{className:"drc-divider"}),(0,r.groupHead)(n("pomodoroDurations"),n("pomodoroDurationHint")),(0,r.h)("div",{className:"drc-fields"},a(n("pomodoroFocus"),i.pomodoroFocus,o.POMODORO_MAX_FOCUS_MINUTES,e=>s(n=>({...n,pomodoroFocus:e}))),a(n("pomodoroBreak"),i.pomodoroBreak,o.POMODORO_MAX_BREAK_MINUTES,e=>s(n=>({...n,pomodoroBreak:e})))),(0,r.h)("div",{className:"drc-divider"}),(0,r.h)(r.Row,{title:n("pomodoroLoop"),description:n("pomodoroLoopHint")},(0,r.h)(r.Toggle,{checked:Boolean(t.pomodoro?.loopAlarm),label:n("pomodoroLoop"),onChange:e=>c({pomodoro:{loopAlarm:e}})})))}(e),function(e){const{t:n,settings:t,draft:a,setDraft:i,stage:s}=e,c=a.countdownPresets,d=(e,n,t)=>{i(r=>({...r,countdownPresets:r.countdownPresets.map((r,o)=>o===e?{...r,[n]:t.replace(/[^\d]/g,"")}:r)}))},l=e=>{i(n=>({...n,countdownPresets:n.countdownPresets.map((n,t)=>t===e?(0,o.capPresetDraft)(n):n)}))};return(0,r.h)("div",{className:"drc-group"},(0,r.h)(r.Row,{title:n("countdownEnable"),description:n("countdownEnableHint")},(0,r.h)(r.Toggle,{checked:Boolean(t.countdown?.enabled),label:n("countdownEnable"),onChange:e=>{const n={countdown:{enabled:e}};e&&(n.pomodoro={enabled:!1}),s(n)}})),(0,r.h)("div",{className:"drc-divider"}),(0,r.groupHead)(n("countdownPresets"),n("countdownHint")),(0,r.h)("div",{className:"drc-list"},c.map((e,t)=>(0,r.h)("div",{className:"drc-preset-row",key:`preset-${t}`},(0,r.h)("label",{className:"drc-preset-main"},(0,r.h)("span",{className:"drc-preset-label"},n(`countdownPreset${t+1}`)),(0,r.h)("span",{className:"drc-preset-duration"},(0,r.h)("input",{className:"drc-map-input drc-preset-part",value:e.minutes,placeholder:"0",inputMode:"numeric","aria-label":`${n(`countdownPreset${t+1}`)} ${n("durationMinutes")}`,onChange:e=>d(t,"minutes",e.target.value),onBlur:()=>l(t)}),(0,r.h)("span",{className:"drc-preset-unit"},n("durationMinutes")),(0,r.h)("input",{className:"drc-map-input drc-preset-part",value:e.seconds,placeholder:"0",inputMode:"numeric","aria-label":`${n(`countdownPreset${t+1}`)} ${n("durationSeconds")}`,onChange:e=>d(t,"seconds",e.target.value),onBlur:()=>l(t)}),(0,r.h)("span",{className:"drc-preset-unit"},n("durationSeconds")))),(0,r.h)(r.Btn,{small:!0,danger:!0,disabled:c.length<=1,title:n("removePreset"),onClick:()=>(e=>{i(n=>n.countdownPresets.length<=1?n:{...n,countdownPresets:n.countdownPresets.filter((n,t)=>t!==e)})})(t)},n("removePreset"))))),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Btn,{small:!0,onClick:()=>{i(e=>e.countdownPresets.length>=o.COUNTDOWN_PRESET_LIMIT?e:{...e,countdownPresets:[...e.countdownPresets,{minutes:"",seconds:""}]})},disabled:c.length>=o.COUNTDOWN_PRESET_LIMIT},n("addPreset")),(0,r.h)("span",{className:"drc-preset-count"},`${c.length}/${o.COUNTDOWN_PRESET_LIMIT}`),(0,r.h)("span",{className:"drc-group-desc"},n("countdownPresetLimitHint"))))}(e),function(e){const{t:n,draft:t,setDraft:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("pomodoroSoundSection"),n("pomodoroSoundHint")),(0,r.h)("div",{className:"drc-fields"},(0,r.h)(r.Field,{label:n("pomodoroSound"),value:t.pomodoroSound,placeholder:n("pomodoroSoundPlaceholder"),onChange:e=>o(n=>({...n,pomodoroSound:e}))})))}(e))};const r=t("../ui.js"),o=t("../logic.js");function a(e,n,t,a){return(0,r.h)(r.Field,{label:e,value:n,placeholder:"25",inputMode:"decimal",onChange:e=>a((0,o.sanitizeMinuteInput)(e)),onBlur:()=>a((0,o.capMinuteInput)(n,o.POMODORO_MIN_MINUTES,t))})}},"client/sections/preferences.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderPreferences=function(e){return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(e.t("menuPlugin"),e.t("preferencesHint")),function(e){const{t:n,settings:t,stage:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("language"),n("languageHint")),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Seg,{options:[{value:"dsh",label:n("languageFollowDsh")},{value:"zh",label:"简体中文"},{value:"en",label:"English"}],value:t.language??"dsh",onChange:e=>o({language:e})})))}(e),function(e){const{t:n,settings:t,stage:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("temperatureUnit"),n("temperatureUnitHint")),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Seg,{options:[{value:"celsius",label:n("celsius")},{value:"fahrenheit",label:n("fahrenheit")}],value:t.temperatureUnit??"celsius",onChange:e=>o({temperatureUnit:e})})))}(e),function(e){const{t:n,settings:t,stage:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("theme"),n("themeHint")),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Seg,{options:[{value:"dsh",label:n("followDsh")},{value:"light",label:n("light")},{value:"dark",label:n("dark")}],value:t.theme,onChange:e=>o({theme:e})})))}(e),function(e){const{t:n,orderListRef:t}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("pageOrder"),n("pageOrderHint")),(0,r.h)("div",{className:"drc-list",ref:t},function(e){const{t:n,order:t,labels:o,draggingKey:a,dragIndex:i,settings:s,togglePage:c,startOrderDrag:d}=e,l=[],p=a?t.filter(e=>e!==a):t,u=a?i??t.indexOf(a):-1,h=a?Math.max(0,Math.min(u,p.length)):-1;let m=0;const g=()=>(0,r.h)("div",{className:"drc-order-placeholder",key:"order-placeholder"},(0,r.h)("span",{className:"drc-drag-handle","aria-hidden":"true"},"⋮⋮"),(0,r.h)("span",{className:"drc-order-placeholder-title"},o[a??""]||""));for(const e of t)e!==a?(a&&p.indexOf(e)===h&&l.push(g()),m+=1,l.push((0,r.h)("div",{className:"drc-order-row",key:e,onPointerDown:n=>d(e,n)},(0,r.h)("span",{className:"drc-drag-handle","aria-hidden":"true"},"⋮⋮"),(0,r.h)("b",null,String(m)),(0,r.h)("span",{className:"drc-order-label"},o[e]),(0,r.h)(r.Toggle,{checked:!1!==s.pageEnabled?.[e],label:o[e],onChange:()=>c(e)})))):l.push((0,r.h)("div",{className:"drc-order-row is-dragging",key:e},(0,r.h)("span",{className:"drc-drag-handle","aria-hidden":"true"},"⋮⋮"),(0,r.h)("b",null,""),(0,r.h)("span",{className:"drc-order-label"},o[e]),(0,r.h)(r.Toggle,{checked:!1!==s.pageEnabled?.[e],label:o[e],onChange:()=>c(e)})));return a&&h>=p.length&&l.push(g()),l.push((0,r.h)("div",{className:"drc-order-end",key:"order-end"})),l}(e)))}(e),function(e){const{t:n,settings:t,stage:o}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("navPosition"),n("navPositionHint")),(0,r.h)("div",{className:"drc-actions"},(0,r.h)(r.Seg,{options:[{value:"left",label:n("navLeft")},{value:"right",label:n("navRight")},{value:"top",label:n("navTop")},{value:"bottom",label:n("navBottom")}],value:t.navPosition??"left",onChange:e=>o({navPosition:e})})))}(e))};const r=t("../ui.js")},"client/sections/system.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderSystem=function(e){return(0,r.h)("div",{className:"drc-section"},(0,r.sectionHead)(e.t("menuSystem"),e.t("systemPageHint")),function(e){const{t:n,data:t}=e,o=t.capabilities??{},a=o.probe,i=o.node,s=a?.backends,c={okLabel:n("healthy"),badLabel:n("unavailable"),pendingLabel:n("checking")},d=a?a.ok??a.available:void 0,l=e=>{if(a)return s?Boolean(s[e])&&!0===d:!1!==d&&void 0};return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("status"),n("statusHint")),(0,r.h)(r.StatusRow,{label:n("notificationsStatus"),value:o.notifications?.available,detail:s?.notifications,...c}),(0,r.h)(r.StatusRow,{label:n("mediaStatus"),value:o.media?.available??o.media?.ok,detail:s?.media,...c}),(0,r.h)(r.StatusRow,{label:n("volumeStatus"),value:o.volume?.available??o.volume?.ok,detail:s?.volume,...c}),(0,r.h)(r.StatusRow,{label:n("performanceStatus"),value:l("performance"),detail:s?.performance,...c}),(0,r.h)(r.StatusRow,{label:n("locationCapability"),value:l("location"),detail:s?.location,...c}),(0,r.h)(r.StatusRow,{label:n("weatherStatus"),value:o.weather?.ok??void 0,detail:i?"Node.js":void 0,...c}))}(e),function(e){const{t:n,clients:t,disconnect:a}=e;return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("clients"),n("clientsHint")),0===t.length?(0,r.h)("p",{className:"drc-empty"},n("noClients")):(0,r.h)("div",{className:"drc-list"},t.map(e=>(0,r.h)("div",{className:"drc-client",key:e.id},(0,r.h)("div",{className:"drc-client-main"},(0,r.h)("strong",null,e.label||n("device")),(0,r.h)("small",null,`${e.ip} · ${(0,o.formatDuration)(Date.now()-e.connectedAt,n)}`)),(0,r.h)(r.Btn,{small:!0,danger:!0,onClick:()=>{a(e.id)}},n("disconnect"))))))}(e),function(e){const{t:n,data:t,info:i}=e,s=t.diagnostics??{},c=t.capabilities?.probe,d=t.capabilities?.node,l=[a("protocol",n("protocol"),s.protocol||i.protocol||"http"),a("profile",n("profileName"),s.profile||"—"),a("plugin-version",n("pluginVersion"),s.version||i.version||"—"),a("probe-version",n("probeVersion"),c?.version||(c?.available?"—":n("probeMissing")))],p=[a("node-version",n("nodeVersion"),d?.version||"—"),a("node-v8",n("nodeV8"),d?.v8||"—"),a("node-platform",n("nodePlatform"),d?`${d.platform}/${d.arch}`:"—"),a("node-memory",n("nodeMemory"),d?.memory?`${(0,o.formatBytes)(d.memory.rss)} / ${(0,o.formatBytes)(d.memory.heapUsed)}`:"—")];return(0,r.h)("div",{className:"drc-group"},(0,r.groupHead)(n("diagnostics"),n("diagnosticsHint")),(0,r.h)("div",{className:"drc-fact-columns"},(0,r.h)("div",{className:"drc-fact-column"},l),(0,r.h)("div",{className:"drc-fact-column"},p)),a("probe-binary",n("probeBinary"),c?.binary||"—",!0),c?.error?a("probe-error",n("error"),c.error,!0):null)}(e))};const r=t("../ui.js"),o=t("../logic.js");function a(e,n,t,o=!1){return(0,r.h)("div",{className:"drc-fact-row"+(o?" is-wide":""),key:e},(0,r.h)("span",{className:"drc-fact-key"},n),(0,r.h)("span",{className:"drc-fact-value"},t))}},"client/sections/weather.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.renderWeather=function(e){const{t:n,settings:t,draft:r,setDraft:i,stage:s}=e,d="manual"===(t.weather?.mode??"auto");return(0,o.h)("div",{className:"drc-section"},(0,o.sectionHead)(n("weather"),n("weatherPageHint")),function(e){const{t:n,locationResult:t,locationBusy:r,busy:i,requestLocation:s}=e;return(0,o.h)("div",{className:"drc-group"},(0,o.groupHead)(n("locationPermission"),n("locationPermissionHint")),(0,o.h)("div",{className:"drc-actions"},(0,o.h)(o.Btn,{primary:!0,disabled:r||i,onClick:()=>{s()}},n(r?"locationRequesting":"locationRequest")),t?(0,o.h)("span",{className:"drc-row-desc"},`${n("locationStatus")} · ${(0,a.accessLabel)(n,t.access)}`):null))}(e),(0,o.h)("div",{className:"drc-group"},(0,o.groupHead)(n("weatherSource"),n("weatherHint")),(0,o.h)("div",{className:"drc-actions"},(0,o.h)(o.Seg,{options:[{value:"auto",label:n("auto")},{value:"manual",label:n("manual")}],value:t.weather?.mode??"auto",onChange:e=>s({weather:{mode:e}})})),(0,o.h)("div",{className:"drc-divider"}),(0,o.h)("div",{className:"drc-fields"},(0,o.h)(c,{t:n,value:r.locationName,disabled:!d,onChange:e=>i(n=>({...n,locationName:e})),onSelect:e=>{i(n=>({...n,locationName:e.name,latitude:e.latitude.toFixed(4),longitude:e.longitude.toFixed(4)}))}}),(0,o.h)(o.Field,{label:n("latitude"),value:r.latitude,placeholder:"0.0000",inputMode:"decimal",disabled:!d,onChange:e=>i(n=>({...n,latitude:e}))}),(0,o.h)(o.Field,{label:n("longitude"),value:r.longitude,placeholder:"0.0000",inputMode:"decimal",disabled:!d,onChange:e=>i(n=>({...n,longitude:e}))})),(0,o.h)("div",{className:"drc-actions"},(0,o.h)("span",{className:"drc-row-desc"},n(d?"citySearchHint":"locationAutoHint")))),(0,o.h)("div",{className:"drc-group"},(0,o.groupHead)(n("forecastDisplay"),n("forecastDisplayHint")),(0,o.h)("div",{className:"drc-actions"},(0,o.h)(o.Seg,{options:[{value:"hourly",label:n("hourly")},{value:"daily",label:n("daily")}],value:t.weather?.forecastMode??"hourly",onChange:e=>s({weather:{forecastMode:e}})}))))};const r=t("react"),o=t("../ui.js"),a=t("../logic.js"),i=t("../api.js");function s(e){return[e.admin1,e.country].map(e=>(e||"").trim()).filter(Boolean).join(" · ")}function c({t:e,value:n,disabled:t,onChange:a,onSelect:c}){const[d,l]=(0,r.useState)(!1),[p,u]=(0,r.useState)("idle"),[h,m]=(0,r.useState)([]),[g,f]=(0,r.useState)(-1),[b,v]=(0,r.useState)(""),w=(0,r.useRef)(null),y=(0,r.useRef)(null);(0,r.useEffect)(()=>{t&&l(!1)},[t]),(0,r.useEffect)(()=>{if(!d)return;const e=e=>{const n=e.target;w.current&&n instanceof Node&&w.current.contains(n)||l(!1)};return document.addEventListener("pointerdown",e),()=>document.removeEventListener("pointerdown",e)},[d]),(0,r.useEffect)(()=>{if(!d||t)return u("idle"),m([]),f(-1),void v("");const e=n.trim();if(!e)return u("idle"),m([]),f(-1),void v("");u("loading"),v("");const r=new AbortController,o=window.setTimeout(()=>{(0,i.requestJson)(`${i.GEOCODE_URL}?q=${encodeURIComponent(e)}`,{signal:r.signal}).then(e=>{if(r.signal.aborted)return;const n=function(e){const n=(0,i.isRecord)(e)?e:{},t=Array.isArray(n.results)?n.results:[],r=[];for(const e of t){if(!(0,i.isRecord)(e))continue;const n="string"==typeof e.name?e.name.trim():"",t=Number(e.latitude),o=Number(e.longitude);n&&Number.isFinite(t)&&Number.isFinite(o)&&r.push({id:Number.isFinite(Number(e.id))?Number(e.id):void 0,name:n,latitude:t,longitude:o,country:"string"==typeof e.country?e.country:"",admin1:"string"==typeof e.admin1?e.admin1:"",timezone:"string"==typeof e.timezone?e.timezone:""})}return r}(e);m(n),u("ready"),f(n.length?0:-1)}).catch(e=>{r.signal.aborted||(m([]),u("error"),f(-1),v((0,i.describeError)(e)))})},280);return()=>{window.clearTimeout(o),r.abort()}},[d,t,n]);const x=d&&!t&&n.trim().length>0,k=e=>{c(e),l(!1),y.current?.blur()};return(0,o.h)("div",{className:"drc-field drc-city",ref:w},(0,o.h)("span",null,e("locationName")),(0,o.h)("input",{ref:y,value:n,disabled:t,placeholder:e("citySearchPlaceholder"),autoComplete:"off",spellCheck:!1,role:"combobox","aria-expanded":x,"aria-autocomplete":"list","aria-controls":"drc-city-options",onChange:e=>{l(!0),a(e.target.value)},onFocus:()=>{!t&&n.trim()&&l(!0)},onKeyDown:e=>{if("Tab"!==e.key)return"ArrowDown"===e.key?(e.preventDefault(),void(x?h.length&&f(e=>(e+1)%h.length):l(!0))):"ArrowUp"===e.key?(e.preventDefault(),void(x&&h.length&&f(e=>e<=0?h.length-1:e-1))):void("Enter"!==e.key?"Escape"===e.key&&x&&(e.preventDefault(),l(!1)):x&&g>=0&&(e.preventDefault(),g>=0&&h[g]&&k(h[g])));l(!1)}}),x?(0,o.h)("div",{className:"drc-city-menu",id:"drc-city-options",role:"listbox","aria-label":e("locationName")},"loading"===p?(0,o.h)("div",{className:"drc-city-empty"},e("citySearching")):"error"===p?(0,o.h)("div",{className:"drc-city-empty is-error"},(0,o.h)("span",{className:"drc-city-error-title"},/\b404\b/.test(b)?e("citySearchUnavailable"):e("citySearchFailed")),b?(0,o.h)("span",{className:"drc-city-error-detail"},b):null):"ready"===p&&0===h.length?(0,o.h)("div",{className:"drc-city-empty"},e("cityNoResults")):h.map((e,n)=>(0,o.h)("button",{key:`${e.id??"city"}-${e.latitude}-${e.longitude}-${n}`,type:"button",role:"option","aria-selected":n===g,className:"drc-city-option"+(n===g?" is-active":""),onMouseEnter:()=>f(n),onClick:()=>k(e)},(0,o.h)("span",{className:"drc-city-name"},e.name),s(e)?(0,o.h)("span",{className:"drc-city-meta"},s(e)):null))):null)}},"client/styles.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.CSS=void 0,n.CSS="\n.drc-page {\n --drc-text: var(--dsw-alias-label-primary);\n --drc-muted: var(--dsw-alias-label-secondary);\n --drc-tertiary: var(--dsw-alias-label-tertiary, var(--dsw-alias-label-secondary));\n --drc-border: var(--dsw-alias-border-l2, var(--dsw-alias-border-l1));\n --drc-hairline: var(--dsw-alias-border-l4, var(--dsw-alias-border-l1));\n --drc-card-border: var(--dsw-alias-border-l1, var(--dsw-alias-border-l2));\n --drc-surface: var(--dsw-alias-bg-layer-1);\n --drc-surface-2: var(--dsw-alias-bg-layer-2);\n --drc-surface-3: var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-layer-1));\n --drc-hover: var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-2));\n --drc-accent: var(--dsw-alias-brand-primary);\n --drc-success: var(--dsw-alias-state-success-primary);\n --drc-error: var(--dsw-alias-state-error-primary);\n --drc-radius: var(--dsw-radius-md, 8px);\n --drc-radius-sm: var(--dsw-radius-sm, 6px);\n --drc-card-radius: var(--dsw-radius-lg, 12px);\n height: 100%;\n max-height: none;\n min-height: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n color: var(--drc-text);\n background: var(--dsw-alias-bg-base);\n font-family: inherit;\n font-size: 14px;\n line-height: 22px;\n}\n\n.drc-shell {\n flex: 1 1 auto;\n min-height: 0;\n display: grid;\n grid-template-columns: 188px minmax(0, 1fr);\n grid-template-rows: 54px minmax(0, 1fr);\n grid-template-areas:\n 'title head'\n 'nav main';\n}\n\n.drc-nav-title {\n grid-area: title;\n padding: 22px 12px 8px 24px;\n overflow: hidden;\n color: var(--drc-text);\n font-size: 16px;\n font-weight: 500;\n line-height: 24px;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.drc-head {\n grid-area: head;\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n gap: 8px;\n min-width: 0;\n height: 54px;\n padding: 10px 20px 14px 10px;\n box-sizing: border-box;\n}\n\n.drc-head-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n flex-wrap: wrap;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n.drc-nav {\n grid-area: nav;\n min-width: 0;\n min-height: 0;\n padding: 10px 12px 0;\n overflow-y: auto;\n box-sizing: border-box;\n}\n\n.drc-nav-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-height: 0;\n}\n\n.drc-nav-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 40px;\n padding: 9px 16px 9px 12px;\n box-sizing: border-box;\n border: 0;\n border-radius: var(--drc-radius);\n background: transparent;\n color: var(--drc-text);\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n}\n\n.drc-nav-item svg {\n width: 16px;\n height: 16px;\n flex: 0 0 auto;\n}\n\n.drc-nav-item:hover {\n background: var(--drc-hover);\n}\n\n.drc-nav-item.is-active {\n background: var(--dsw-specific-sidebar-nav-item-active, var(--dsw-alias-bg-layer-2));\n font-weight: 500;\n}\n\n.drc-nav-label {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.drc-nav-compact {\n padding: 0 16px 12px;\n}\n\n.drc-nav-select-wrap {\n position: relative;\n width: 100%;\n}\n\n.drc-nav-select {\n width: 100%;\n height: 40px;\n padding: 0 36px 0 12px;\n box-sizing: border-box;\n border: 0.5px solid var(--drc-hairline);\n border-radius: var(--drc-radius);\n outline: none;\n appearance: none;\n background: var(--dsw-alias-bg-layer-1, var(--drc-surface));\n color: var(--drc-text);\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n.drc-nav-select:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary, var(--drc-accent)));\n outline-offset: 1px;\n}\n\n.drc-nav-select-wrap::after {\n content: '';\n position: absolute;\n top: 50%;\n right: 14px;\n width: 7px;\n height: 7px;\n border-right: 1.5px solid var(--drc-muted);\n border-bottom: 1.5px solid var(--drc-muted);\n transform: translateY(-70%) rotate(45deg);\n pointer-events: none;\n}\n\n.drc-main {\n grid-area: main;\n min-width: 0;\n min-height: 0;\n padding: 0 24px 24px;\n overflow-y: auto;\n box-sizing: border-box;\n scrollbar-width: thin;\n}\n\n.drc-main::-webkit-scrollbar { width: 8px; }\n.drc-main::-webkit-scrollbar-thumb { background: var(--drc-border); border-radius: 999px; }\n\n.drc-section {\n width: 100%;\n max-width: 760px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n color: var(--drc-text);\n}\n\n.drc-section-head {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-bottom: 2px;\n}\n\n.drc-section-title {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n line-height: 26px;\n}\n\n.drc-intro {\n margin: 0;\n color: var(--drc-tertiary);\n font-size: 13px;\n line-height: 20px;\n}\n\n.drc-group {\n display: flex;\n flex-direction: column;\n min-width: 0;\n padding: 0 16px;\n border: 0.5px solid var(--drc-card-border);\n border-radius: var(--drc-card-radius);\n background: var(--dsw-alias-bg-layer-1, var(--drc-surface));\n box-sizing: border-box;\n}\n\n.drc-group + .drc-group { margin-top: 0; }\n\n.drc-group-head {\n display: flex;\n flex-direction: column;\n gap: 4px;\n padding: 16px 0 4px;\n}\n\n.drc-group-title {\n margin: 0;\n font-size: 14px;\n font-weight: 500;\n line-height: 22px;\n}\n\n.drc-group-desc {\n margin: 0;\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n/* Separator between distinct setting groups inside one card. Never use it\n * directly under the card header or between a control and its own action. */\n.drc-divider {\n height: 0;\n margin: 8px 0;\n border-top: 0.5px solid var(--drc-border);\n}\n\n/* List rows keep a separator, but inset from both sides so it reads lighter\n * than the card border instead of touching the card edge. */\n.drc-list-row + .drc-list-row,\n.drc-order-row + .drc-order-row,\n.drc-status-row + .drc-status-row,\n.drc-map-row + .drc-map-row,\n.drc-client + .drc-client {\n position: relative;\n}\n\n.drc-list-row + .drc-list-row::before,\n.drc-order-row + .drc-order-row::before,\n.drc-status-row + .drc-status-row::before,\n.drc-map-row + .drc-map-row::before,\n.drc-client + .drc-client::before {\n content: '';\n position: absolute;\n top: 0;\n right: 12px;\n left: 12px;\n height: 0.5px;\n background: var(--drc-border);\n}\n\n.drc-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 10px 0;\n}\n\n/* A card without a header (for example the pomodoro timer group) starts\n * directly with its first row; keep the same top breathing room as a card\n * header would give it. */\n.drc-group > .drc-row:first-child { padding-top: 16px; }\n\n.drc-row-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.drc-row-title {\n font-size: 14px;\n line-height: 22px;\n font-weight: 400;\n}\n\n.drc-row-desc {\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.drc-row-control {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.drc-btn {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n padding: 0 14px;\n border: 0;\n border-radius: var(--drc-radius);\n background: transparent;\n color: var(--drc-text);\n box-shadow: inset 0 0 0 0.5px var(--dsw-alias-border-l3, var(--drc-border));\n font: inherit;\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n white-space: nowrap;\n cursor: pointer;\n}\n\n.drc-btn:hover:not(:disabled) { background: var(--drc-hover); }\n\n.drc-btn.is-primary {\n background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-label-primary));\n color: var(--dsw-alias-label-primary-foreground, var(--dsw-alias-bg-layer-3));\n box-shadow: none;\n}\n\n.drc-btn.is-primary:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover, var(--dsw-alias-label-primary));\n}\n\n.drc-btn.is-danger {\n color: var(--drc-error);\n box-shadow: inset 0 0 0 0.5px color-mix(in srgb, var(--drc-error) 35%, transparent);\n}\n\n.drc-btn.is-danger:hover:not(:disabled) {\n background: color-mix(in srgb, var(--drc-error) 10%, transparent);\n}\n\n.drc-btn.is-sm {\n height: 28px;\n padding: 0 10px;\n border-radius: var(--drc-radius-sm);\n font-size: 12px;\n line-height: 18px;\n}\n\n.drc-btn:disabled { opacity: 0.4; cursor: not-allowed; }\n\n.drc-btn:focus-visible,\n.drc-nav-item:focus-visible,\n.drc-toggle:focus-visible,\n.drc-seg button:focus-visible,\n.drc-city-option:focus-visible,\n.drc-app:focus-visible {\n outline: var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary, var(--drc-accent)));\n outline-offset: 1px;\n}\n\n.drc-toggle {\n position: relative;\n flex: 0 0 auto;\n width: 36px;\n height: 20px;\n padding: 2px;\n border: 0;\n border-radius: 999px;\n corner-shape: round;\n background: var(--dsw-alias-border-l3, var(--drc-border));\n cursor: pointer;\n}\n\n.drc-toggle i {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n corner-shape: round;\n background: var(--dsw-alias-switch-thumb, var(--drc-muted));\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.drc-toggle.is-on { background: var(--drc-accent); }\n.drc-toggle.is-on i {\n transform: translateX(16px);\n background: var(--dsw-alias-label-primary-foreground, #fff);\n}\n\n.drc-seg {\n display: inline-grid;\n grid-auto-flow: column;\n grid-auto-columns: 1fr;\n gap: 2px;\n max-width: 100%;\n padding: 4px;\n border-radius: var(--drc-radius);\n background: var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-2));\n box-sizing: border-box;\n}\n\n.drc-seg button {\n height: 28px;\n padding: 0 16px;\n border: 0;\n border-radius: var(--drc-radius-sm);\n background: transparent;\n color: var(--drc-muted);\n font: inherit;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n white-space: nowrap;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n\n.drc-seg button.is-active {\n background: var(--dsw-alias-bg-layer-1);\n color: var(--drc-text);\n box-shadow: var(--dsw-elevation-soft, 0 1px 2px rgb(0 0 0 / 8%));\n}\n\n.drc-seg button[aria-pressed='false']:hover { color: var(--drc-text); }\n\n.drc-fields {\n display: flex;\n flex-wrap: wrap;\n gap: 16px;\n padding: 10px 0;\n}\n\n/* Countdown presets are a vertical list: one preset per row, the input grows\n * and the remove action stays on the same line. */\n.drc-preset-row {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 0;\n}\n\n.drc-preset-main {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.drc-preset-label {\n flex: 0 0 auto;\n min-width: 96px;\n color: var(--drc-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n white-space: nowrap;\n}\n\n/* Countdown presets read as \"x 分 y 秒\": two compact fields with unit labels. */\n.drc-preset-duration {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1 1 auto;\n min-width: 0;\n}\n\n.drc-preset-row .drc-preset-part {\n flex: 0 1 92px;\n width: 92px;\n min-width: 0;\n text-align: right;\n font-variant-numeric: tabular-nums;\n}\n\n.drc-preset-unit {\n flex: 0 0 auto;\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n white-space: nowrap;\n}\n\n.drc-preset-count {\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n font-variant-numeric: tabular-nums;\n}\n\n.drc-field {\n flex: 1 1 190px;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.drc-field > span {\n color: var(--drc-text);\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n}\n\n.drc-field input {\n width: 100%;\n height: 34px;\n padding: 0 12px;\n box-sizing: border-box;\n border: 0.5px solid var(--drc-hairline);\n border-radius: var(--drc-radius);\n outline: none;\n background: var(--drc-surface-3);\n color: var(--drc-text);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n}\n\n.drc-field input::placeholder { color: var(--dsw-alias-label-dimmed, var(--drc-tertiary)); }\n.drc-field input:focus { border-color: var(--dsw-alias-state-business-primary, var(--drc-accent)); }\n.drc-field input:disabled { opacity: 0.5; cursor: not-allowed; }\n\n/* City combobox: the field keeps the normal label/input layout and hosts the\n * suggestion list in an absolutely positioned popover so the card never\n * reflows while typing. */\n.drc-city { position: relative; }\n\n.drc-city-menu {\n position: absolute;\n top: calc(100% + 4px);\n right: 0;\n left: 0;\n z-index: 30;\n max-height: 236px;\n padding: 4px;\n overflow-y: auto;\n border: 0.5px solid var(--drc-card-border);\n border-radius: var(--drc-radius);\n background: var(--dsw-alias-bg-layer-1, var(--drc-surface));\n box-shadow: var(--dsw-elevation-soft, 0 12px 32px rgb(0 0 0 / 18%));\n box-sizing: border-box;\n scrollbar-width: thin;\n}\n\n.drc-city-option {\n display: flex;\n flex-direction: column;\n gap: 1px;\n width: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: var(--drc-radius-sm);\n background: transparent;\n color: var(--drc-text);\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.drc-city-option.is-active { background: var(--drc-hover); }\n\n.drc-city-name {\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n}\n\n.drc-city-meta {\n color: var(--drc-tertiary);\n font-size: 11px;\n line-height: 17px;\n}\n\n.drc-city-empty {\n padding: 8px 10px;\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n.drc-city-empty.is-error { color: var(--drc-error); }\n\n.drc-city-error-title,\n.drc-city-error-detail { display: block; }\n\n.drc-city-error-detail {\n margin-top: 2px;\n color: var(--drc-tertiary);\n font-size: 10px;\n line-height: 15px;\n overflow-wrap: anywhere;\n}\n\n.drc-list {\n display: flex;\n flex-direction: column;\n}\n\n.drc-list-row {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 8px 0;\n font-size: 13px;\n line-height: 20px;\n}\n\n.drc-list-row label {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n cursor: pointer;\n}\n\n.drc-list-row input[type='checkbox'] {\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--drc-accent);\n cursor: inherit;\n}\n\n.drc-code {\n color: var(--drc-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 17px;\n overflow-wrap: anywhere;\n}\n\n.drc-actions {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 8px;\n padding: 8px 0;\n}\n\n.drc-actions-stack {\n flex-direction: column;\n align-items: flex-start;\n}\n\n.drc-qr {\n display: flex;\n align-items: center;\n gap: 24px;\n flex-wrap: wrap;\n padding: 4px 0 12px;\n}\n\n.drc-qr-frame {\n flex: 0 0 auto;\n width: 176px;\n height: 176px;\n padding: 8px;\n box-sizing: border-box;\n display: grid;\n place-items: center;\n border-radius: var(--drc-radius);\n background: #fff;\n box-shadow: var(--dsw-elevation-soft, 0 8px 24px rgb(0 0 0 / 14%));\n}\n\n.drc-qr-frame img {\n display: block;\n width: 160px;\n height: 160px;\n image-rendering: pixelated;\n}\n\n.drc-qr-copy { flex: 1 1 280px; min-width: 220px; }\n\n.drc-url {\n display: block;\n max-height: 62px;\n margin-top: 12px;\n padding: 8px 10px;\n overflow: hidden;\n border: 0.5px solid var(--drc-hairline);\n border-radius: var(--drc-radius);\n background: var(--drc-surface-3);\n color: var(--drc-muted);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.45;\n white-space: pre-wrap;\n word-break: break-all;\n overflow-wrap: anywhere;\n}\n\n.drc-apps {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n padding: 8px 0;\n}\n\n.drc-app {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n height: 28px;\n padding: 0 10px;\n border: 0.5px solid var(--drc-hairline);\n border-radius: 999px;\n background: transparent;\n color: var(--drc-muted);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n}\n\n.drc-app i {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n opacity: 0.55;\n}\n\n.drc-app.is-on {\n border-color: transparent;\n background: color-mix(in srgb, var(--drc-accent) 10%, transparent);\n color: var(--drc-text);\n}\n\n.drc-app.is-on i { background: var(--drc-accent); opacity: 1; }\n\n.drc-map-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 0;\n}\n\n.drc-map-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.drc-map-input {\n width: 100%;\n height: 34px;\n padding: 0 12px;\n box-sizing: border-box;\n border: 0.5px solid var(--drc-hairline);\n border-radius: var(--drc-radius);\n outline: none;\n background: var(--drc-surface-3);\n color: var(--drc-text);\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n}\n\n.drc-map-input:focus { border-color: var(--dsw-alias-state-business-primary, var(--drc-accent)); }\n\n.drc-map-key {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n.drc-map-meta {\n flex: 0 0 auto;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.drc-tag {\n display: inline-flex;\n align-items: center;\n padding: 1px 8px;\n border-radius: 999px;\n corner-shape: round;\n background: var(--dsw-alias-bg-module-platform, var(--drc-surface-2));\n color: var(--drc-muted);\n font-size: 11px;\n font-weight: 500;\n line-height: 17px;\n white-space: nowrap;\n}\n\n.drc-tag[data-tone='outline'] {\n border: 0.5px solid var(--drc-hairline);\n background: transparent;\n color: var(--drc-tertiary);\n}\n\n.drc-tag[data-tone='success'] {\n background: color-mix(in srgb, var(--drc-success) 10%, transparent);\n color: var(--drc-success);\n}\n\n.drc-tag[data-tone='warning'] {\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, var(--drc-accent)) 12%, transparent);\n color: var(--dsw-alias-state-warn-primary, var(--drc-accent));\n}\n\n.drc-tag[data-tone='danger'] {\n background: color-mix(in srgb, var(--drc-error) 10%, transparent);\n color: var(--drc-error);\n}\n\n.drc-order-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n font-size: 13px;\n line-height: 20px;\n cursor: grab;\n touch-action: none;\n transition: opacity 150ms ease;\n}\n\n.drc-order-row.is-dragging {\n position: absolute;\n width: 0;\n height: 0;\n min-height: 0;\n margin: 0;\n padding: 0;\n border: 0;\n opacity: 0;\n visibility: hidden;\n overflow: hidden;\n pointer-events: none;\n transition: none;\n}\n\n.drc-order-row b {\n width: 18px;\n color: var(--drc-tertiary);\n font-family: ui-monospace, monospace;\n font-size: 11px;\n font-weight: 500;\n}\n\n.drc-order-row .drc-order-label { flex: 1; min-width: 0; }\n\n.drc-drag-handle {\n color: var(--drc-tertiary);\n font-size: 12px;\n letter-spacing: -2px;\n cursor: grab;\n}\n\n.drc-order-placeholder {\n display: flex;\n align-items: center;\n gap: 8px;\n height: 41px;\n padding: 0 12px;\n border: 1px dashed var(--drc-accent);\n border-radius: var(--drc-radius);\n background: color-mix(in srgb, var(--drc-accent) 9%, transparent);\n pointer-events: none;\n}\n\n.drc-order-placeholder-title {\n color: var(--drc-accent);\n font-size: 13px;\n font-weight: 500;\n}\n\n.drc-order-placeholder .drc-drag-handle {\n color: var(--drc-accent);\n cursor: grabbing;\n}\n\nbody.drc-sorting,\nbody.drc-sorting * {\n user-select: none !important;\n cursor: grabbing !important;\n}\n\n.drc-order-end { height: 8px; }\n\n.drc-status-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 8px 0;\n}\n\n.drc-status-main {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.drc-status-label {\n font-size: 13px;\n line-height: 20px;\n}\n\n.drc-status-detail {\n color: var(--drc-tertiary);\n font-size: 11px;\n line-height: 17px;\n overflow-wrap: anywhere;\n}\n\n.drc-status {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n gap: 8px;\n color: var(--drc-muted);\n font-size: 12px;\n line-height: 18px;\n}\n\n.drc-dot {\n position: relative;\n display: inline-block;\n flex: 0 0 auto;\n width: 10px;\n height: 10px;\n color: var(--dsw-alias-state-idle-primary);\n}\n\n.drc-dot::after {\n content: '';\n position: absolute;\n inset: 20%;\n border-radius: 50%;\n background: currentColor;\n}\n\n.drc-dot.ok { color: var(--drc-success); }\n.drc-dot.bad { color: var(--drc-error); }\n\n.drc-client {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 8px 0;\n}\n\n.drc-client-main {\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.drc-client strong {\n display: block;\n font-size: 13px;\n font-weight: 500;\n line-height: 20px;\n}\n\n.drc-client small {\n display: block;\n color: var(--drc-tertiary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 17px;\n}\n\n.drc-banner {\n margin: 0 0 12px;\n padding: 8px 12px;\n border-radius: var(--drc-radius);\n background: var(--drc-surface-2);\n font-size: 12px;\n line-height: 18px;\n}\n\n.drc-banner.is-error {\n background: color-mix(in srgb, var(--drc-error) 10%, transparent);\n color: var(--drc-error);\n}\n\n.drc-banner.is-ok {\n background: color-mix(in srgb, var(--drc-success) 10%, transparent);\n color: var(--drc-success);\n}\n\n.drc-fact-columns {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 0 32px;\n padding: 2px 0 0;\n}\n\n.drc-fact-column { min-width: 0; }\n\n.drc-fact-row {\n position: relative;\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 16px;\n padding: 8px 0;\n min-width: 0;\n}\n\n.drc-fact-row + .drc-fact-row::before,\n.drc-fact-columns + .drc-fact-row::before {\n content: '';\n position: absolute;\n top: 0;\n right: 12px;\n left: 12px;\n height: 0.5px;\n background: var(--drc-border);\n}\n\n.drc-fact-key {\n flex: 0 0 auto;\n color: var(--drc-tertiary);\n font-size: 11px;\n line-height: 17px;\n}\n\n.drc-fact-value {\n min-width: 0;\n color: var(--drc-muted);\n font-size: 11px;\n line-height: 17px;\n text-align: right;\n overflow-wrap: anywhere;\n}\n\n.drc-backends {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 4px 0 8px;\n}\n\n.drc-empty {\n margin: 0;\n padding: 12px 0;\n color: var(--drc-tertiary);\n font-size: 12px;\n line-height: 18px;\n}\n\n@media (max-width: 767px) {\n .drc-shell {\n grid-template-columns: minmax(0, 1fr) auto;\n grid-template-rows: auto auto minmax(0, 1fr);\n grid-template-areas:\n 'title head'\n 'nav nav'\n 'main main';\n }\n\n .drc-nav-title { padding: 22px 0 0 16px; }\n .drc-head { height: auto; padding: 20px 16px 8px 10px; }\n .drc-nav { padding: 0; }\n .drc-nav-compact { padding: 0 16px 12px; }\n .drc-main { padding: 0 16px 24px; }\n}\n"},"client/types.js":function(e,n,t){"use strict";Object.defineProperty(n,"__esModule",{value:!0})},"client/ui.js":function(e,n,t){"use strict";var r,o=this&&this.__createBinding||(Object.create?function(e,n,t,r){void 0===r&&(r=t);var o=Object.getOwnPropertyDescriptor(n,t);o&&!("get"in o?!n.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return n[t]}}),Object.defineProperty(e,r,o)}:function(e,n,t,r){void 0===r&&(r=t),e[r]=n[t]}),a=this&&this.__setModuleDefault||(Object.create?function(e,n){Object.defineProperty(e,"default",{enumerable:!0,value:n})}:function(e,n){e.default=n}),i=this&&this.__importStar||(r=function(e){return r=Object.getOwnPropertyNames||function(e){var n=[];for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&(n[n.length]=t);return n},r(e)},function(e){if(e&&e.__esModule)return e;var n={};if(null!=e)for(var t=r(e),i=0;i<t.length;i++)"default"!==t[i]&&o(n,e,t[i]);return a(n,e),n});Object.defineProperty(n,"__esModule",{value:!0}),n.h=void 0,n.Toggle=function({checked:e,onChange:t,label:r}){return(0,n.h)("button",{type:"button",role:"switch","aria-checked":e,"aria-label":r,className:"drc-toggle"+(e?" is-on":""),onClick:()=>t(!e)},(0,n.h)("i"))},n.Seg=function({options:e,value:t,onChange:r}){return(0,n.h)("div",{className:"drc-seg",role:"group"},e.map(e=>(0,n.h)("button",{key:e.value,type:"button",className:t===e.value?"is-active":"","aria-pressed":t===e.value,onClick:()=>r(e.value)},e.label)))},n.Btn=function({children:e,onClick:t,primary:r=!1,danger:o=!1,disabled:a=!1,small:i=!1,title:s}){const c=["drc-btn"];return r&&c.push("is-primary"),o&&c.push("is-danger"),i&&c.push("is-sm"),(0,n.h)("button",{type:"button",className:c.join(" "),onClick:t,disabled:a,title:s},e)},n.Field=function({label:e,value:t,onChange:r,onBlur:o,placeholder:a,inputMode:i,disabled:s=!1}){return(0,n.h)("label",{className:"drc-field"},(0,n.h)("span",null,e),(0,n.h)("input",{value:t??"",placeholder:a,inputMode:i,disabled:s,onBlur:o,onChange:e=>r(e.target.value)}))},n.Row=function({title:e,description:t,children:r}){return(0,n.h)("div",{className:"drc-row"},(0,n.h)("div",{className:"drc-row-main"},(0,n.h)("div",{className:"drc-row-title"},e),t?(0,n.h)("div",{className:"drc-row-desc"},t):null),(0,n.h)("div",{className:"drc-row-control"},r))},n.StatusRow=function({label:e,value:t,detail:r,okLabel:o,badLabel:a,pendingLabel:i}){const s=!0===t?"ok":!1===t?"bad":"";return(0,n.h)("div",{className:"drc-status-row"},(0,n.h)("div",{className:"drc-status-main"},(0,n.h)("span",{className:"drc-status-label"},e),r?(0,n.h)("span",{className:"drc-status-detail"},r):null),(0,n.h)("span",{className:"drc-status"},(0,n.h)("i",{className:`drc-dot ${s}`}),"ok"===s?o:"bad"===s?a:i))},n.Tag=function({children:e,tone:t="neutral"}){return(0,n.h)("span",{className:"drc-tag","data-tone":t},e)},n.sectionHead=function(e,t){return(0,n.h)("div",{className:"drc-section-head"},(0,n.h)("h2",{className:"drc-section-title"},e),t?(0,n.h)("p",{className:"drc-intro"},t):null)},n.groupHead=function(e,t){return(0,n.h)("div",{className:"drc-group-head"},(0,n.h)("h3",{className:"drc-group-title"},e),t?(0,n.h)("p",{className:"drc-group-desc"},t):null)};const s=i(t("react"));n.h=s.createElement}},t=Object.create(null);return function r(o,a){if("."!==a.charAt(0))return e(a);var i=function(e,t){var r=function(e,n){var t=e?e.split("/"):[];for(var r of(t.pop(),n.split("/")))r&&"."!==r&&(".."===r?t.pop():t.push(r));return t.join("/")}(e,t);if(n[r])return r;if(n[r+".js"])return r+".js";if(n[r+"/index.js"])return r+"/index.js";throw new Error('Cannot find module "'+t+'" from "'+(e||"<entry>")+'"')}(o,a),s=t[i];if(s)return s.exports;var c={exports:{}};return t[i]=c,n[i](c,c.exports,function(e){return r(i,e)}),c.exports}("","./client/index.js")}});
|
package/components.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-svelte.com/schema.json",
|
|
3
|
+
"tailwind": {
|
|
4
|
+
"css": "src/svelte/styles/global.css",
|
|
5
|
+
"baseColor": "neutral"
|
|
6
|
+
},
|
|
7
|
+
"aliases": {
|
|
8
|
+
"components": "$lib/components",
|
|
9
|
+
"utils": "$lib/utils",
|
|
10
|
+
"ui": "$lib/components/ui",
|
|
11
|
+
"hooks": "$lib/hooks",
|
|
12
|
+
"lib": "$lib"
|
|
13
|
+
},
|
|
14
|
+
"typescript": true,
|
|
15
|
+
"registry": "https://shadcn-svelte.com/registry",
|
|
16
|
+
"style": "vega",
|
|
17
|
+
"iconLibrary": "lucide",
|
|
18
|
+
"menuColor": "default",
|
|
19
|
+
"menuAccent": "subtle"
|
|
20
|
+
}
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="../icon.svg" alt="dsh-remote-control" width="80" height="80">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">dsh-remote-control</h1>
|
|
6
|
+
|
|
7
|
+
<div align="center">
|
|
8
|
+
<p><strong>A touch-first LAN console for DeepSeek Harness</strong></p>
|
|
9
|
+
<p><em>把局域网里的手机变成 DSH 的触屏中控台</em></p>
|
|
10
|
+
|
|
11
|
+
<p>
|
|
12
|
+
<a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/powered_by-dsh-4D6BFE?style=flat&logo=deepseek&logoColor=white" alt="Powered by dsh"></a>
|
|
13
|
+
<a href="https://github.com/ohmykreee/dsh-remote-control/actions/workflows/checks.yml"><img src="https://github.com/ohmykreee/dsh-remote-control/actions/workflows/checks.yml/badge.svg" alt="Checks"></a>
|
|
14
|
+
<a href="https://www.npmjs.com/package/@ohmykreee/dsh-remote-control"><img src="https://img.shields.io/npm/v/@ohmykreee/dsh-remote-control?style=flat&color=4D6BFE" alt="npm"></a>
|
|
15
|
+
<img src="https://img.shields.io/badge/platform-Windows%20%7C%20Linux%20%7C%20macOS-4D6BFE?style=flat" alt="Windows / Linux / macOS">
|
|
16
|
+
<a href="../LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat" alt="MIT License"></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p>
|
|
20
|
+
Install steps → <a href="../INSTALLATION.md">INSTALLATION.md</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
<p>
|
|
24
|
+
<strong>English</strong> · <a href="../README.md">简体中文</a>
|
|
25
|
+
</p>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
When your phone and computer share a LAN, open the console address in the phone browser and DSH becomes a touch panel: clock, weather and system notifications; sessions and balance at a glance; playback and system volume; and answers to a session that is waiting on you.
|
|
31
|
+
|
|
32
|
+
## The pages
|
|
33
|
+
|
|
34
|
+
Four pages, each with one job:
|
|
35
|
+
|
|
36
|
+
| Page | What it shows |
|
|
37
|
+
|---|---|
|
|
38
|
+
| Home | Large clock and date, current weather with hourly / 7-day forecast, Pomodoro or countdown, system notifications |
|
|
39
|
+
| Sessions | Account balance, today / this week spend, a 7-day spend chart behind the spend card, live sessions and subagents, remote answers |
|
|
40
|
+
| Music | Full-bleed blurred artwork, track info, progress and seek, previous / next, play-pause, system volume and mute, NetEase Cloud Music like |
|
|
41
|
+
| Performance | CPU / memory and GPU / VRAM rings, clocks and uptime, hottest temperature and sensor list |
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<img src="screenshots/en/overview.png" alt="Home, sessions, music and performance" width="1000">
|
|
45
|
+
<br>
|
|
46
|
+
<em>Home / Sessions / Music / Performance (top-left / top-right / bottom-left / bottom-right). Each capture is split from top-right to bottom-left: dark above, light below.</em>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<img src="screenshots/en/question.png" alt="Remote question" width="640">
|
|
51
|
+
<br>
|
|
52
|
+
<em>Remote question: options, the recommended marker and a custom reply.</em>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
- The four pages loop horizontally, one page per gesture; page order, enablement and the navigation rail position are configured on the DSH settings page.
|
|
56
|
+
- Disabled pages do not appear in the phone rail.
|
|
57
|
+
- In landscape each page is one screen tall, with notifications, sessions and temperature sensors scrolling inside their cards; portrait allows the whole page to scroll.
|
|
58
|
+
- The phone UI ships in Simplified Chinese and English. **Follow DSH** uses the current DSH language.
|
|
59
|
+
- Small screens use larger type and easy-to-tap controls.
|
|
60
|
+
- A quick refresh button and a fullscreen button sit at the bottom left.
|
|
61
|
+
|
|
62
|
+
## Install
|
|
63
|
+
|
|
64
|
+
**Recommended: let the DSH agent install it.** Start a new DSH session and send it this line:
|
|
65
|
+
|
|
66
|
+
> Install the `@ohmykreee/dsh-remote-control` plugin. Once it is installed, read its README.md and INSTALLATION.md first, then follow them to build the local component that media, volume, notifications, performance and location need.
|
|
67
|
+
|
|
68
|
+
The agent installs the plugin, reads both documents, builds the component and tells you to restart DSH; if build tools are missing it says which ones.
|
|
69
|
+
|
|
70
|
+
**To install manually**, start with [`INSTALLATION.md`](../INSTALLATION.md): npm install, source install, component build, updates and troubleshooting all live there.
|
|
71
|
+
|
|
72
|
+
## Connecting
|
|
73
|
+
|
|
74
|
+
- Open **Remote control → Connection** in the sidebar, then scan the pairing QR code with your phone or copy the console address. Phone and computer must share a LAN.
|
|
75
|
+
- Access verification is on by default: the first visit needs the scanned link, and a missing verification shows an error page. You can turn it off or refresh it; refreshing disconnects every connected device.
|
|
76
|
+
- You can allow only selected network interfaces / subnets.
|
|
77
|
+
- Optional HTTPS: provide certificate and private-key file paths. If the certificate is invalid the console stays on HTTP and shows the error on the connection page.
|
|
78
|
+
- If the port is taken, another available port is chosen automatically. The connection page always shows the actual address.
|
|
79
|
+
|
|
80
|
+
## Settings
|
|
81
|
+
|
|
82
|
+
The **Remote control** sidebar has these groups:
|
|
83
|
+
|
|
84
|
+
| Group | Contents |
|
|
85
|
+
|---|---|
|
|
86
|
+
| Connection | QR code, console address, allowed interfaces, access verification, HTTPS |
|
|
87
|
+
| Preferences | Plugin language, temperature unit, theme, page order and toggles, navigation position |
|
|
88
|
+
| Pomodoro / countdown | Enable switch (mutually exclusive), focus / break lengths, countdown presets, alert sound |
|
|
89
|
+
| Weather | Location mode, city search, hourly / 7-day display, location access |
|
|
90
|
+
| Balance | Spend window, low-balance threshold, peak / off-peak marker |
|
|
91
|
+
| Notifications | Per-app toggles, display limit, remote answers |
|
|
92
|
+
| App names | Rename apps or reset their default names |
|
|
93
|
+
| System information | Service status, diagnostics, and connected devices |
|
|
94
|
+
|
|
95
|
+
Click **Save settings** to apply changes. Renaming an app, theme changes, refreshing access verification and disconnecting a device take effect immediately.
|
|
96
|
+
|
|
97
|
+
## Pomodoro and countdown
|
|
98
|
+
|
|
99
|
+
Both sit between the clock and the weather on the phone, are **mutually exclusive**, and only one can be enabled; disabling one removes it from the phone. Timers run in the phone browser, so refreshing or closing the page loses the current run.
|
|
100
|
+
|
|
101
|
+
| | Pomodoro | Countdown |
|
|
102
|
+
|---|---|---|
|
|
103
|
+
| Setup | Focus / break lengths, optional "ring until stopped" | 1–5 quick presets, 3 by default |
|
|
104
|
+
| At zero | Plays the alert sound, optionally looping until stopped | The card turns red and shows "Time's up"; you must confirm |
|
|
105
|
+
| Controls | Start, pause, reset | Pick a preset, start, pause, reset; reset and presets are locked until confirmed |
|
|
106
|
+
|
|
107
|
+
- The alert sound can be a local audio file or an http(s) URL configured in DSH; leave it empty for the built-in soft arpeggio. Countdown and Pomodoro share it.
|
|
108
|
+
- Focus and break lengths accept two decimals. Countdown presets are entered as minutes and seconds; seconds at or above 60 carry into minutes automatically.
|
|
109
|
+
- Values outside the allowed range adjust automatically: pomodoro 0.1–180 minutes (break 0.1–120), countdown presets 1–86400 seconds.
|
|
110
|
+
- At zero the Home rail tab and the browser tab both show a green dot, and the countdown also vibrates the phone when the browser supports it.
|
|
111
|
+
|
|
112
|
+
## Weather
|
|
113
|
+
|
|
114
|
+
- Automatic mode uses the computer's location. If it is unavailable or denied, network location is used; if that also fails, the time zone is used.
|
|
115
|
+
- Windows may block location through a privacy switch. **Weather → Request location access** asks for permission; when denied, the system Location settings page opens. Refresh after granting access.
|
|
116
|
+
- Manual mode searches for a city and fills in the coordinates, or you can edit latitude and longitude directly.
|
|
117
|
+
- City search and forecasts contact an external weather service; no account or key is required. Temperature units support Celsius and Fahrenheit, and the choice applies to weather, forecasts and performance sensors.
|
|
118
|
+
- In automatic mode the manual location fields are read-only.
|
|
119
|
+
|
|
120
|
+
## Sessions and balance
|
|
121
|
+
|
|
122
|
+
- Balance comes from the DSH account. Usage supports **This week** (Monday 00:00 to now) and **Last 7 days** (today plus the previous six calendar days).
|
|
123
|
+
- The sessions page shows a 7-day daily spend chart behind the spend card, with the highest day at 100% height.
|
|
124
|
+
- Session states are running (green), waiting for an answer (yellow), error (red) and finished. The rail icon shows yellow / red dots while a session is waiting or has errored.
|
|
125
|
+
- Subagent sessions are nested inside the parent card and indented. Running sessions show their accumulated run time.
|
|
126
|
+
- The balance card has a peak / off-peak marker: weekdays 09:00–12:00 and 14:00–18:00 Beijing time are peak; other times, weekends and public holidays are off-peak at half the peak price. The marker can be turned off under **Balance**.
|
|
127
|
+
|
|
128
|
+
### Remote answers
|
|
129
|
+
|
|
130
|
+
Turn on **Push inquiries** and sessions waiting for an answer gain an **Answer** entry. Tapping it opens a centered touch dialog with every question, its options and a custom reply field. Single-choice questions accept one tap; multiple-choice questions can be combined with a custom reply, or you can skip a question.
|
|
131
|
+
|
|
132
|
+
- The phone handles normal DSH user questions only; tool approvals still happen in DSH.
|
|
133
|
+
- After answering remotely, the question card in DSH may still need to be closed manually.
|
|
134
|
+
- With the switch off, the sessions page stays read-only.
|
|
135
|
+
|
|
136
|
+
## Music
|
|
137
|
+
|
|
138
|
+
- Supports NetEase Cloud Music, QQ Music, KuGou, KuWo, Soda Music, WeSing, Spotify, Apple Music, YouTube Music, plus browsers and other common players.
|
|
139
|
+
- The music page shows album art, a full-bleed blurred background, track info, progress, previous / next, play-pause, system volume and mute. NetEase Cloud Music's like button is clickable when available.
|
|
140
|
+
- Whether the progress bar can be dragged depends on the player supporting seek; some players only show progress. When progress is unavailable it is never fabricated.
|
|
141
|
+
- NetEase Cloud Music's like state and exact progress need an optional feature enabled; see [`INSTALLATION.md`](../INSTALLATION.md).
|
|
142
|
+
- Missing artwork or track details show a placeholder instead of invented content.
|
|
143
|
+
|
|
144
|
+
## Performance
|
|
145
|
+
|
|
146
|
+
- The left cards show CPU / memory and GPU / VRAM with radial gauges. Small cards below show current clocks, uptime, used / free memory, VRAM and core / memory clocks.
|
|
147
|
+
- The right column keeps a large hottest temperature and heat-tinted sensor list, combining GPU core, disk and ACPI sensors; long lists scroll inside the card.
|
|
148
|
+
- Readings that are unavailable show "Not provided" instead of guessed values.
|
|
149
|
+
- Some system features may show unavailable after installation; check [`INSTALLATION.md`](../INSTALLATION.md) if that happens.
|
|
150
|
+
|
|
151
|
+
## Notifications and app names
|
|
152
|
+
|
|
153
|
+
- Notifications come from the system notification centre and show the app name, title, body, avatar and time on the phone.
|
|
154
|
+
- Common apps ship with localized names; other apps get a name derived automatically.
|
|
155
|
+
- **App names** lets you rename any app or reset its default; changes apply to phone notifications and music sources immediately.
|
|
156
|
+
- **Notifications** lets you toggle each app, limit how many items are shown (1–20), and control whether questions are pushed.
|
|
157
|
+
|
|
158
|
+
## Security and privacy
|
|
159
|
+
|
|
160
|
+
- Access verification is random and enabled by default. You can turn it off or refresh it; refreshing disconnects every connected device. You can allow only selected network interfaces / subnets.
|
|
161
|
+
- Optional HTTPS; an invalid certificate falls back to HTTP with an error shown.
|
|
162
|
+
- Settings stay on this computer. The repository contains no local paths, verification secrets, certificates or notification content, and there is no telemetry.
|
|
163
|
+
- Weather, network location and public-holiday lookups contact external services; all other data stays between your computer and your phone.
|
|
164
|
+
|
|
165
|
+
## Platform support
|
|
166
|
+
|
|
167
|
+
| Platform | Support |
|
|
168
|
+
|---|---|
|
|
169
|
+
| Windows | Full media, volume, performance, notifications and location support. |
|
|
170
|
+
| Linux | Full media, volume, performance and notifications support; GPU support for NVIDIA / AMD. |
|
|
171
|
+
| macOS | Not yet verified on hardware or CI; GPU and temperature are unavailable. |
|
|
172
|
+
| Phone | Modern browser; layout is optimised for 1280×800 landscape. |
|
|
173
|
+
|
|
174
|
+
## Known limitations
|
|
175
|
+
|
|
176
|
+
- Media, volume, notifications, GPU / temperature and system location need one component built on your own computer. Until it is built those features show unavailable; CPU / memory still work.
|
|
177
|
+
- Windows location needs system privacy permission; if it is denied you may need to enable it in system settings.
|
|
178
|
+
- Only players that support seek allow dragging the progress bar; some players show progress only or offer no transport controls.
|
|
179
|
+
- NetEase Cloud Music's like state and exact progress require an optional feature; they may be briefly unavailable after a page refresh.
|
|
180
|
+
- The sessions page is read-only; media and volume are writable; tool approvals cannot be handled remotely.
|
|
181
|
+
- Pomodoro and countdown run only in the phone browser, so refreshing or closing the page loses them.
|
|
182
|
+
- Layout is optimised for 1280×800 landscape; portrait is a compact fallback.
|
|
183
|
+
|
|
184
|
+
## Documentation
|
|
185
|
+
|
|
186
|
+
| File | Contents |
|
|
187
|
+
|---|---|
|
|
188
|
+
| [`INSTALLATION.md`](../INSTALLATION.md) | Install, update, uninstall and recovery |
|
|
189
|
+
| [`README.md`](../README.md) | Chinese README |
|
|
190
|
+
| [`AGENTS.md`](../AGENTS.md) | Developer and agent notes |
|
|
191
|
+
|
|
192
|
+
Bugs and feature requests go to the repository Issues.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
[MIT](../LICENSE). Inter, Noto Sans SC and JetBrains Mono use the SIL Open Font License 1.1 (`static/fonts/LICENSE.txt`, bundled to `web/fonts/LICENSE.txt`). UI components come from MIT-licensed shadcn-svelte and bits-ui; icons come from ISC-licensed Lucide; some media-adapter ideas are based on [Widdit/now-playing-service](https://github.com/Widdit/now-playing-service) (MIT).
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/icon.svg
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="14" y1="10" x2="112" y2="120" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#15245c"/>
|
|
5
|
+
<stop offset=".52" stop-color="#0a1230"/>
|
|
6
|
+
<stop offset="1" stop-color="#040711"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="accent" x1="38" y1="42" x2="94" y2="90" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop stop-color="#8ea4ff"/>
|
|
10
|
+
<stop offset="1" stop-color="#4d6bff"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
<linearGradient id="ring" x1="28" y1="46" x2="100" y2="80" gradientUnits="userSpaceOnUse">
|
|
13
|
+
<stop stop-color="#57dbe5"/>
|
|
14
|
+
<stop offset="1" stop-color="#4d6bff"/>
|
|
15
|
+
</linearGradient>
|
|
16
|
+
</defs>
|
|
17
|
+
<rect width="128" height="128" rx="30" fill="url(#bg)"/>
|
|
18
|
+
<circle cx="64" cy="64" r="42" fill="none" stroke="url(#ring)" stroke-opacity=".24" stroke-width="2"/>
|
|
19
|
+
<rect x="35" y="22" width="58" height="84" rx="15" fill="none" stroke="#ecf2ff" stroke-width="4.5"/>
|
|
20
|
+
<path d="M51 96h26" stroke="#ecf2ff" stroke-width="4.5" stroke-linecap="round"/>
|
|
21
|
+
<circle cx="64" cy="61" r="20" fill="url(#accent)"/>
|
|
22
|
+
<circle cx="64" cy="61" r="7" fill="#071029"/>
|
|
23
|
+
<path d="M27 45c-6 4-10 11-10 19s4 15 10 19M101 45c6 4 10 11 10 19s-4 15-10 19" fill="none" stroke="#57dbe5" stroke-width="4.5" stroke-linecap="round" opacity=".9"/>
|
|
24
|
+
</svg>
|
package/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Generated by scripts/build-dsh.mjs from src/dsh — do not edit. */
|
|
2
|
+
import{createRemoteControlService as e}from"./lib/service.js";import{shutdownProbeDaemon as t}from"./lib/probe.js";import{APP_NAMES_PATH as o,DISCONNECT_PATH as n,GEOCODE_PATH as r,INFO_PATH as a,LOCALE_PATH as i,LOCATION_PATH as s,PANEL_ID as c,QR_PATH as l,SETTINGS_PATH as p,THEME_PATH as d,TOKEN_ROTATE_PATH as h}from"./lib/util.js";export const inject=["webServer"];export async function apply(f){const g=function(e){try{const t=e.logger,o="function"==typeof t?t(c):t;return e=>{try{o&&"function"==typeof o.info?o.info(`[${c}] ${e}`):console.info(`[${c}] ${e}`)}catch{}}}catch{return()=>{}}}(f),m=e(f,{log:g});try{await m.start()}catch(e){g(`server start threw: ${String(e)}`)}f.effect?.(()=>()=>{m.dispose(),t()},"dsh-remote-control: LAN server and native adapters");const v=f.get?.("webServer");if(v?.register){const e=[[a,(e,t)=>m.handleInfo(e,t),"pairing info"],[l,(e,t)=>m.handleQr(e,t),"pairing QR"],[p,(e,t)=>"POST"===e.method?m.handleSettingsPost(e,t):m.handleSettingsGet(e,t),"settings"],[o,(e,t)=>m.handleAppNames(e,t),"app name mapping"],[s,(e,t)=>m.handleLocation(e,t),"location permission"],[r,(e,t)=>m.handleGeocode(e,t),"city geocoding"],[d,(e,t)=>m.handleTheme(e,t),"theme sync"],[i,(e,t)=>m.handleLocale(e,t),"locale sync"],[h,(e,t)=>m.handleTokenRotate(e,t),"token rotation"],[n,(e,t)=>m.handleDisconnect(e,t),"device disconnect"]];for(const[t,o,n]of e)f.effect?.(()=>v.register({kind:"exact",path:t,handler:o}),`dsh-remote-control: ${n} route`)}else g("webServer is unavailable; the sidebar page cannot read settings");const b=m.getInfo();b.error?g(`remote server unavailable: ${b.error}`):b.tls.error?g(`TLS unavailable; serving HTTP: ${b.tls.error}`):g(`remote server ready on port ${b.port}${b.usesLoopbackOnly?" (loopback only)":""}`)}
|
package/lib/account.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isRecord as e}from"./types.js";import{finiteNumber as n,localTimezoneOffsetSeconds as t,serializeError as r,startOfLocalDay as a,startOfLocalWeek as l,VERSION as o}from"./util.js";function s(e){return Array.isArray(e)?e:[]}function u(e,n,t){return new Promise((r,a)=>{const l=setTimeout(()=>a(new Error(`${t} timed out`)),n);e.then(e=>{clearTimeout(l),r(e)},e=>{clearTimeout(l),a(e)})})}function c(t){const r=s(t).map(t=>{const r=e(t)?t:{};return{currency:String(r.currency||"").toUpperCase(),balance:n(r.balance,null)}}).filter(e=>e.currency&&null!==e.balance);if(0===r.length)return null;const a=r.find(e=>"CNY"===e.currency);if(a)return a.currency;const l=r.find(e=>"USD"===e.currency);return l?l.currency:r[0].currency}function i(t,r){return s(t).filter(n=>String(e(n)&&n.currency||"").toUpperCase()===r).reduce((t,r)=>t+(n(e(r)?r.balance:0,0)??0),0)}function d(t){return t&&"object"==typeof t?n(e(t)?t.value:null,0)??0:n(t,0)??0}function p(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,"0")}-${String(e.getDate()).padStart(2,"0")}`}export function createAccountService(g,f={}){const y=f.locale||"zh",w=f.getSettings||(()=>({balance:{spendWindow:"calendar"}})),b=()=>g.get?.("deepseekAccount");let m=null,h=0,S=null,A=0;async function D(n=!1){const a=b();if(!a)return{signedIn:!1,state:"unavailable",error:"Account service is not mounted"};const l=Date.now();if(!n&&m&&l-h<("ready"===m?.state?6e4:2e4))return{...m,cached:!0};let s;try{s=await a.getState()}catch(e){return m={signedIn:!1,state:"error",error:r(e)},h=l,m}const d=e(s)?s:null;if(!s||"signed-out"===d?.status)return m={signedIn:!1,state:"signed-out",currency:null,balance:null,paid:null,bonus:null,updatedAt:l},h=l,m;try{const n=await u(a.getBalance(function(e="zh"){return{version:o,locale:e,timezoneOffsetSeconds:t()}}(y)),9e3,"Balance"),r=e(n)?n:null;if(!r||"ready"!==r.status)return m={signedIn:!0,state:"failed",currency:null,balance:null,paid:null,bonus:null,error:"Balance request failed",updatedAt:l},h=l,m;const s=c(r.value)||c(r.bonusWallets)||"CNY",d=i(r.value,s),p=i(r.bonusWallets,s);m={signedIn:!0,state:"ready",currency:s,balance:d+p,paid:d,bonus:p,updatedAt:l}}catch(e){m={signedIn:!0,state:"failed",currency:null,balance:null,paid:null,bonus:null,error:r(e),updatedAt:l}}return h=l,m}async function T(c=!1){const i=b(),g="rolling"===w()?.balance?.spendWindow?"rolling":"calendar";if(!i?.getPlatformSession)return{state:"unavailable",currency:null,today:null,spend:null,week:null,spendWindow:g,spendLabel:"rolling"===g?"最近 7 天":"本周",error:"Usage is unavailable"};const f=Date.now(),h="ready"===S?.state?9e4:3e4;if(!c&&S&&S.spendWindow===g&&f-A<h)return{...S,cached:!0};try{const r=await u(i.getPlatformSession(),4e3,"Platform session"),c=e(r)&&"string"==typeof r.origin?r.origin:"",w=e(r)&&"string"==typeof r.token?r.token:"";if(!c||!w)return S={state:"signed-out",currency:null,today:null,spend:null,week:null,spendWindow:g,spendLabel:"rolling"===g?"最近 7 天":"本周",updatedAt:f},A=f,S;const b=new Date,h=a(b),D="rolling"===g?new Date(h.getTime()-5184e5):l(b),T=new Date(b);T.setHours(24,0,0,0);const k=t(b),W=new URL("/api/v0/usage/by_api_key/cost",c);W.searchParams.set("start",String(Math.floor(D.getTime()/1e3))),W.searchParams.set("end",String(Math.floor(T.getTime()/1e3))),W.searchParams.set("tz",String(k));const v=new AbortController,I=setTimeout(()=>v.abort(),9e3);let $;try{$=await fetch(W,{signal:v.signal,headers:{authorization:`Bearer ${w}`,"x-dsh-auth-token":w,accept:"application/json","x-client-platform":"web","x-client-locale":"zh"===y?"zh_CN":"en_US","x-client-version":o}})}finally{clearTimeout(I)}if(401===$.status||403===$.status)return S={state:"expired",currency:null,today:null,spend:null,week:null,spendWindow:g,spendLabel:"rolling"===g?"最近 7 天":"本周",error:`HTTP ${$.status}`,updatedAt:f},A=f,S;if(!$.ok)throw new Error(`HTTP ${$.status}`);const P=await $.json(),z=e(P)?P:{},C=e(z.data)?z.data:{},L=n(z.code,0),U=n(C.biz_code,0);if(0!==L||0!==U){if(40002===L||40003===L||40002===U||40003===U)return S={state:"expired",currency:null,today:null,spend:null,week:null,spendWindow:g,spendLabel:"rolling"===g?"最近 7 天":"本周",error:`code ${L||U}`,updatedAt:f},A=f,S;throw new Error(`Platform code ${L||U}`)}return S=function(t,r,o=new Date,u=null,c="calendar"){const i=function(t,r){const a=s(t).filter(e);if(0===a.length)return null;if(r){const e=a.find(e=>String(e.currency||"").toUpperCase()===r);if(e)return e}return a.find(t=>[...s(t.series),...s(t.function_series)].some(t=>s((e(t)?t:{}).buckets).some(t=>{const r=e(t)?t:{};return(n(d(r.cost),0)??0)>0})))||a[0]}(function(n){const t=e(n)?n:{},r=(e(t.data)?t.data:{}).biz_data;return Array.isArray(r)?r.filter(e):e(r)&&Array.isArray(r.data)?r.data.filter(e):e(r)?[r]:[]}(t),r),g=a(o).getTime(),f=n(u,l(o).getTime())??l(o).getTime(),y=new Date(o);y.setHours(24,0,0,0);const w=y.getTime(),b=function(e){const n=a(e),t=[];for(let e=6;e>=0;e-=1){const r=new Date(n);r.setDate(r.getDate()-e),t.push({date:p(r),label:0===e?"今天":`${r.getMonth()+1}/${r.getDate()}`,cost:0})}return t}(o);if(!i)return{state:"empty",currency:r,today:0,spend:0,spendWindow:c,spendLabel:"rolling"===c?"最近 7 天":"本周",periodStart:new Date(f).toISOString(),daily:b,updatedAt:Date.now()};const m=String(i.currency||r||"CNY").toUpperCase();let h=0,S=0;for(const t of[...s(i.series),...s(i.function_series)]){const r=e(t)?t:{};for(const t of s(r.buckets)){const r=e(t)?t:{},a=n(r.time,null);if(null===a)continue;const l=1e3*a,o=d(r.cost),s=p(new Date(l)),u=b.find(e=>e.date===s);u&&(u.cost+=o),l<f||l>=w||(S+=o,l>=g&&(h+=o))}}return{state:"ready",currency:m,today:h,spend:S,spendWindow:c,spendLabel:"rolling"===c?"最近 7 天":"本周",periodStart:new Date(f).toISOString(),daily:b,updatedAt:Date.now()}}(P,m?.currency||null,b,D.getTime(),g),S.week=S.spend,A=f,S}catch(e){return S={state:"error",currency:m?.currency||null,today:null,spend:null,week:null,spendWindow:g,spendLabel:"rolling"===g?"最近 7 天":"本周",error:r(e),updatedAt:f},A=f,S}}return{getAccount:async function(e=!1){const n=await D(e);if(!n.signedIn){const e="rolling"===w()?.balance?.spendWindow?"rolling":"calendar";return{available:!0,signedIn:!1,balance:n,usage:{state:n.state,currency:null,today:null,spend:null,week:null,spendWindow:e,spendLabel:"rolling"===e?"最近 7 天":"本周"}}}return{available:!0,signedIn:!0,balance:n,usage:await T(e)}},readBalance:D,readUsage:T}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import e from"node:fs";import t from"node:path";import{isRecord as o}from"./types.js";const i={qq:"QQ","com.bilibili.bilibilipc":"哔哩哔哩",bilbilipc:"哔哩哔哩","com.squirrel.discord.discord":"Discord","com.oplus.devicespace":"OPPO 互传","dev.zcode.app":"ZCode","electron.app.zcode":"ZCode","firefoxtoast-308046b0af4a39cb":"Firefox","microsoft.screensketch_8wekyb3d8bbwe!app":"截图工具","microsoft.windowsterminal_8wekyb3d8bbwe!app":"终端","microsoft.visualstudiocode_8wekyb3d8bbwe!vscode":"Visual Studio Code","microsoft.microsoftedge.stable_8wekyb3d8bbwe!app":"Microsoft Edge","microsoft.outlookforwindows_8wekyb3d8bbwe!microsoft.outlookforwindows":"Outlook","microsoft.office.outlook.exe.15":"Outlook","microsoft.yourphone_8wekyb3d8bbwe!app":"手机连接","microsoft.zunemusic_8wekyb3d8bbwe!microsoft.zunemusic":"媒体播放器","microsoft.windowsstore_8wekyb3d8bbwe!app":"Microsoft Store","microsoft.todos_8wekyb3d8bbwe!app":"Microsoft To Do","microsoft.paint_8wekyb3d8bbwe!app":"画图","microsoft.windowsnotepad_8wekyb3d8bbwe!app":"记事本","microsoft.windowscalculator_8wekyb3d8bbwe!app":"计算器","microsoft.windows.photos_8wekyb3d8bbwe!app":"照片","windows.defender.securitycenter":"Windows 安全中心","microsoft.zunemusic_8wekyb3d8bbwe":"媒体播放器","cloudmusic.exe":"网易云音乐","qqmusic.exe":"QQ音乐","kugou.exe":"酷狗音乐","kgmusic.exe":"酷狗音乐","kwmusic.exe":"酷我音乐","kwservice.exe":"酷我音乐","sodamusic.exe":"汽水音乐","wesing.exe":"全民K歌","spotify.exe":"Spotify","applemusic.exe":"Apple Music","youtube music.exe":"YouTube Music","cider.exe":"Cider"},s=240;export function defaultAppName(e){const t=String(e||"").trim();if(!t)return null;const o=i[t.toLowerCase()];if(o)return o;if((t.includes("\\")||t.includes("/"))&&t.toLowerCase().endsWith(".exe"))return(t.split(/[\\/]/).pop()||t).replace(/\.exe$/i,"")||null;if(t.startsWith("NotifyIconGeneratedAumid_"))return"桌面应用";if(t.toLowerCase().startsWith("microsoft.microsoftedge"))return"Microsoft Edge";if(t.toLowerCase().startsWith("microsoft.screensketch"))return"截图工具";if(/bilibili/i.test(t))return"哔哩哔哩";if(/youtube[-_ ]?music|ytmusic/i.test(t))return"YouTube Music";if(/cloudmusic|netease|163music|网易云/i.test(t))return"网易云音乐";if(/qqmusic|qq音乐/i.test(t))return"QQ音乐";if(/kugou|kgmusic|酷狗/i.test(t))return"酷狗音乐";if(/kwmusic|kwservice|kuwo|酷我/i.test(t))return"酷我音乐";if(/sodamusic|汽水音乐/i.test(t))return"汽水音乐";if(/wesing|全民k歌/i.test(t))return"全民K歌";if(/cider/i.test(t))return"Cider";if(/applemusic|apple[ .]music|com\.apple\.music/i.test(t))return"Apple Music";if(/spotify/i.test(t))return"Spotify";if(/msedge/i.test(t))return"Microsoft Edge";if(/brave/i.test(t))return"Brave";if(/vivaldi/i.test(t))return"Vivaldi";if(/firefox/i.test(t))return"Firefox";if(/chrome/i.test(t))return"Chrome";if(t.startsWith("org.mpris.MediaPlayer2.")){const e=t.slice(23).replace(/[._-]+/g," ").trim();return e?e.replace(/\b\w/g,e=>e.toUpperCase()):null}let s=t.split("!")[0];const r=s.lastIndexOf("_");if(r>0){const e=s.slice(r+1);e.length>=8&&/^[a-z0-9]+$/.test(e)&&(s=s.slice(0,r))}return function(e){let t="",o=!1;for(const i of e)"-"===i||"_"===i?t+=" ":(i===i.toUpperCase()&&i!==i.toLowerCase()&&o&&(t+=" "),t+=i),o=i===i.toLowerCase()&&i!==i.toUpperCase();return t.trim()}(s.split(".").filter(Boolean).pop()||"")||null}function r(e){return"string"!=typeof e?null:e.replace(/\s+/g," ").trim().slice(0,60)||null}function n(e){return Array.isArray(e)?[...new Set(e.filter(e=>"notification"===e||"music"===e))]:[]}export function createAppMapping(i){const c=i,u=t.join(c,"app-mapping.json");e.mkdirSync(c,{recursive:!0});let a={},f=!1;try{const t=JSON.parse(e.readFileSync(u,"utf8"));o(t)&&o(t.apps)&&(a=function(e){const t={};if(!o(e))return t;for(const i of Object.values(e)){if(!o(i)||"string"!=typeof i.key||!i.key)continue;const e=i.key.slice(0,s);t[e]={key:e,customName:r(i.customName),firstSeen:Number(i.firstSeen)||Number(i.lastSeen)||0,lastSeen:Number(i.lastSeen)||0,count:Math.max(0,Math.round(Number(i.count)||0)),sources:n(i.sources)}}return t}(t.apps),f=!0)}catch{a={}}f||(a=function(i){const r={};try{const n=JSON.parse(e.readFileSync(t.join(i,"notification-cache.json"),"utf8"));if(!o(n)||!o(n.apps))return r;for(const e of Object.values(n.apps)){if(!o(e)||"string"!=typeof e.key||!e.key)continue;const t=e.key.slice(0,s);r[t]={key:t,customName:null,firstSeen:Number(e.lastSeen)||0,lastSeen:Number(e.lastSeen)||0,count:Math.max(0,Math.round(Number(e.count)||0)),sources:["notification"]}}}catch{}return r}(c));let l=null;function m(e){const t=defaultAppName(e.key);return{key:e.key,name:e.customName||t||e.key,customName:e.customName,defaultName:t,count:e.count,firstSeen:e.firstSeen,lastSeen:e.lastSeen,sources:[...e.sources]}}function p(){l=null;const t=`${u}.tmp-${process.pid}`;try{e.writeFileSync(t,JSON.stringify({version:1,updatedAt:Date.now(),apps:a},null,2),"utf8"),e.renameSync(t,u)}catch{}}return{record:function(e,t){if(!Array.isArray(e)||0===e.length)return;let i=!1;const r=Date.now();for(const n of e){if(!o(n))continue;const e="string"==typeof n.key?n.key.trim():"";if(!e)continue;const c=e.slice(0,s),u=Math.max(0,Math.round(Number(n.count)||0)),f=a[c],l=new Set(f?.sources??[]);l.add(t);const m=l.size!==(f?.sources.length??0),p=!f||r-f.lastSeen>6e4;(!f||f.count!==u||m||p)&&(a[c]={key:c,customName:f?.customName??null,firstSeen:f?.firstSeen||r,lastSeen:r,count:Math.max(u,f?.count??0),sources:[...l]},i=!0)}i&&(function(){const e=Object.values(a);e.length<=300||(e.sort((e,t)=>t.lastSeen-e.lastSeen),a=Object.fromEntries(e.slice(0,300).map(e=>[e.key,e])))}(),l||(l=setTimeout(p,1200),l.unref?.()))},rename:function(e,t){const o=String(e||"").trim();if(!o)return null;const i=o.slice(0,s),n=a[i];if(!n)return null;const c=r(t);return n.customName===c?m(n):(a[i]={...n,customName:c},p(),m(a[i]))},displayName:function(e){const t=String(e||"").trim().slice(0,s);if(!t)return"未知应用";const o=a[t];return o?.customName?o.customName:defaultAppName(t)||t},list:function(){return Object.values(a).map(m).sort((e,t)=>t.lastSeen-e.lastSeen||t.count-e.count)},get:function(e){const t=a[String(e||"").trim().slice(0,s)];return t?m(t):null},flush:p,dispose:function(){l&&(clearTimeout(l),p())}}}
|
package/lib/i18n.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const zh={answerWaiting:"正在等待你的回答",answerCountMismatch:"回答数量不匹配:需要 {expected} 项,收到 {received} 项",answerDuplicate:"回答项重复或缺少问题编号",answerUnknownQuestion:"回答项不属于当前问题",answerSingleChoice:"单选题只能选择一个选项",answerOptionGone:"选择的选项已不在问题中",answerMissingQuestion:"回答缺少问题",answerExpired:"这个问题已经结束或不在等待回答",answerNeedsDsh:"这个问题需要在 DSH 中回答",answerMissingId:"缺少问题编号,无法回答",answerUnsupported:"当前 DSH 不支持远程回答",answerNotWaiting:"这个问题已经不再等待回答",answerPushOff:"推送问询已关闭",answerInvalid:"回答格式无效",approvalPrompt:"需要允许 {tool} 执行",approvalTool:"工具",newSession:"新会话",turnFailed:"本轮运行出错",turnBlocked:"本轮被阻止",pairingTitle:"需要配对",pairingBody:"请从 DSH 侧边栏的远程控制台重新扫码。"};export const en={answerWaiting:"Waiting for your answer",answerCountMismatch:"Answer count mismatch: expected {expected}, received {received}",answerDuplicate:"Duplicate answer or missing question id",answerUnknownQuestion:"An answer does not belong to the current question",answerSingleChoice:"A single-choice question accepts one option",answerOptionGone:"A selected option is no longer part of the question",answerMissingQuestion:"An answer is missing for one question",answerExpired:"This question has finished or is no longer waiting",answerNeedsDsh:"Answer this question in DSH",answerMissingId:"The question id is missing; cannot answer",answerUnsupported:"This DSH build does not support remote answers",answerNotWaiting:"This question is no longer waiting for an answer",answerPushOff:"Pushed inquiries are turned off",answerInvalid:"The answer format is invalid",approvalPrompt:"Allow {tool} to run",approvalTool:"the tool",newSession:"New session",turnFailed:"This turn failed",turnBlocked:"This turn was blocked",pairingTitle:"Pairing required",pairingBody:"Scan the QR code again from the Remote control panel in the DSH sidebar."};export const DICTS={zh,en};export function hostMessage(e,n,s){const i=DICTS[e]?.[n]??en[n];return s?i.replace(/\{(\w+)\}/g,(e,n)=>n in s?String(s[n]):e):i}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{clamp as e,finiteNumber as t}from"./util.js";export function publicMedia(e){const a=e?.media;if(!e||!a)return null;const n="private"===a.provider||"accessibility"===a.provider?a.provider:"system";return{appId:a.appId||"",appName:"",title:a.title||"未命名媒体",artist:a.artist||"",album:a.album||"",albumArtist:a.albumArtist||"",playbackStatus:a.playbackStatus||"Closed",autoRepeatMode:a.autoRepeatMode||"",shuffleActive:Boolean(a.shuffleActive),position:Math.max(0,t(a.position,0)||0),startTime:Math.max(0,t(a.startTime,0)||0),endTime:Math.max(0,t(a.endTime,0)||0),minSeekTime:Math.max(0,t(a.minSeekTime,0)||0),maxSeekTime:Math.max(0,t(a.maxSeekTime,0)||0),lastUpdatedAt:t(a.lastUpdatedAt,null),canPlayPause:!1!==a.canPlayPause,canNext:!1!==a.canNext,canPrevious:!1!==a.canPrevious,canSeek:Boolean(a.canSeek),provider:n,providerDetail:a.providerDetail?String(a.providerDetail):null,thumbnail:a.thumbnail||null,thumbnailUrl:a.thumbnailUrl||null,thumbnailType:a.thumbnailType||null,liked:"boolean"==typeof a.liked?a.liked:null,canLike:Boolean(a.canLike),canSetLiked:Boolean(a.canSetLiked),trackId:a.trackId||null,estimatedDuration:Boolean(a.estimatedDuration),sampledAt:t(e.sampledAt,Date.now())||Date.now()}}export function mediaKey(e){return`${e?.appId||""}|${e?.title||""}|${e?.artist||""}`}export function createMediaEnrichment(){let a=null,n=null,i=null;return{enrich:async function(l){const o=t(l?.sampledAt,Date.now())||Date.now();let s=publicMedia(l);if(!s)return a=null,n=null,i=null,null;const r=Math.max(0,Number(s.startTime)||0),u=Math.max(0,Number(s.endTime)||0),m=Math.max(0,Number(s.position)||0),p=u>r&&u>0,c="system"===s.provider,d="playing"===String(s.playbackStatus).toLowerCase(),y=mediaKey(s);i&&i.key===y?(d&&!i.playing&&m<=.01&&(i.seen=!1),m>.01&&(i.seen=!0),i.playing=d):i={key:y,seen:m>.01,playing:d};const k=i.seen,b=Boolean(s.canSeek)&&c&&p&&k,h=p?c?"smtc":"accessibility"===s.provider?"accessibility":"private":"none";s.canSeek=b,s.positionReported=k,s.timelineSource=h,s.estimatedDuration=!1;const x=Math.max(0,Number(s.endTime)||0),M=Math.max(0,Number(s.startTime)||0),f=x>M?x-M:x,S=t=>f>0?e(t,M,M+f):Math.max(0,t);n&&(n.key!==y||o>=n.until)&&(n=null);const v=function(e,a){const n=t(e,null);if(!n||n<=0)return null;const i=a-n;return i>=-5e3&&i<=216e5?n:null}(s.lastUpdatedAt,o);let T=m;if(d&&p&&v&&(T+=(o-v)/1e3),T=S(T),a&&a.key===y){const e=a.playing?Math.max(0,(o-a.at)/1e3):0;let t=S(a.position+e);const n=m>.01;if(n){const e=T-t;Math.abs(e)>2.5?t=T:Math.abs(e)>.35&&(t=S(t+.45*e))}d!==a.playing&&n&&(t=T),a={key:y,position:t,at:o,playing:d}}else a={key:y,position:T,at:o,playing:d};return n&&(a={key:y,position:S(n.position),at:o,playing:d}),s.position=a.position,s.sampledAt=o,s},markSeek:function(e,t,a=Date.now()+2600){n={key:e,position:t,until:a}},reset:function(){a=null,n=null,i=null}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{spawn as t}from"node:child_process";import*as n from"node:readline";import{probeInfo as a,runProbe as i}from"./probe.js";import{isRecord as o}from"./types.js";import{finiteNumber as e,safeString as l,serializeError as r}from"./util.js";function c(t){const n="string"==typeof t?t.trim():"";return n.startsWith("data:image/")?n:null}export function normalizeNotificationPayload(t,n={}){const a=Math.max(1,Math.min(20,Number(n.limit)||8)),i=o(t)?t:{},r=!1!==i.available&&!1!==i.ok,u=l(i.reason||i.error||"",800)||null,s=l(i.source,120)||"dsh-probe",p=Array.isArray(i.notifications)?i.notifications:[],f=[];for(const t of p){if(!o(t))continue;const n=l(t.appKey||t.appName,240).trim()||"unknown",i=l(t.appName||t.appKey,120).trim()||n,r=l(t.title||i,160).trim(),u=l(t.body,260).trim();if(!r&&!u)continue;const s=e(t.receivedAt,null);if(f.push({id:l(t.id,120)||`${s??Date.now()}-${f.length}`,appKey:n,appName:i,title:r,body:u,avatar:c(t.avatar),hasImage:Boolean(t.hasImage),receivedAt:s&&s>0?Math.round(s):Date.now(),isGroup:Boolean(t.isGroup)}),f.length>=a)break}const m=[],d=new Map,h=Array.isArray(i.apps)?i.apps:[];for(const t of h){if(!o(t))continue;const n=l(t.key,240);if(!n||d.has(n))continue;const a={key:n,name:l(t.name,120)||n,count:Math.max(0,Math.round(e(t.count,0)||0))};d.set(n,a),m.push(a)}if(0===m.length)for(const t of f){const n=d.get(t.appKey);if(n)n.count+=1;else{const n={key:t.appKey,name:t.appName,count:1};d.set(t.appKey,n),m.push(n)}}return m.sort((t,n)=>n.count-t.count),{ok:r,available:r,reason:u,source:s,liveOnly:Boolean(i.liveOnly),notifications:f,apps:m,updatedAt:e(i.updatedAt,null)||Date.now()}}function u(t,n={}){return normalizeNotificationPayload({ok:!1,available:!1,reason:r(t),source:"dsh-probe",notifications:[],apps:[],...n},n)}export function createNotificationService(o={}){const e=Math.max(1,Math.min(20,Number(o.limit)||8)),l="linux"===process.platform;let c=null,s=0,p=null,f=null,m=null,d=null,h=0,y=0,v=!1;function b(){const t=a();return t.available?null:t.error||"native probe unavailable"}return{getNotifications:async function(){if(!l){const t=Date.now();return c&&t-s<(c?.available?2500:6e3)?{...c,cached:!0}:(c=await async function(){const t=b();if(t)return u(t,{limit:e});try{return normalizeNotificationPayload(await i(["notifications","--limit",String(e)],{timeout:9e3}),{limit:e})}catch(t){return u(t,{limit:e})}}(),s=t,c)}return!p&&!v&&Date.now()>=y&&function(){if(p||v)return;const i=a();if(!i.available)return void(d=i.error||"native probe unavailable");let o;try{o=t(i.binary,["notifications","watch","--limit",String(e)],{stdio:["ignore","pipe","pipe"],windowsHide:!0})}catch(t){return d=r(t),void(y=Date.now()+5e3)}p=o,h=Date.now(),d=null;const l=o.stdout;if(l){const t=n.createInterface({input:l});f=t,t.on("line",t=>{const n=String(t).trim();if(n.startsWith("{"))try{const t=normalizeNotificationPayload(JSON.parse(n),{limit:e});t.available?(m=t,d=null):(m=null,d=t.reason||"notification watcher unavailable")}catch{}})}const c=o.stderr;c&&(c.setEncoding("utf8"),c.on("data",t=>{const n=String(t).trim();n&&(d=n.slice(0,500))})),o.on("error",t=>{d=r(t),m=null}),o.on("exit",(t,n)=>{const a=Date.now()-h>1e3;p=null,m=null,f?.close(),f=null,v||(d?a||(d=`${d} (exited immediately)`):d=`notification watcher exited (${n||t||"unknown"})`,y=Date.now()+5e3)})}(),m?{...m,cached:!1}:u(d||b()||(p?"notification watcher starting":"notification watcher unavailable"),{limit:e,liveOnly:!0})},status:function(){return l?{available:!!m||!d&&null,reason:d||null,count:m?.notifications?.length??0,updatedAt:m?.updatedAt??null,liveOnly:!0}:c?{available:c.available,reason:c.reason||null,count:c.notifications?.length??0,updatedAt:c.updatedAt??null}:{available:null,reason:null,count:null,updatedAt:null}},dispose:function(){v=!0;try{f?.close()}catch{}f=null;try{p?.kill()}catch{}p=null}}}
|