@sirenodeck/sirenodeck 0.1.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +69 -0
- package/bin/_wrapper-shared.js +42 -0
- package/bin/dev.js +95 -0
- package/bin/sirenodeck.js +42 -0
- package/config/default-config.yml +117 -0
- package/config/starter-config.yml +54 -0
- package/dist/main.mjs +64702 -0
- package/package.json +140 -0
- package/src/action/executor.ts +156 -0
- package/src/action/index.ts +7 -0
- package/src/addon/api-types.ts +15 -0
- package/src/addon/api.ts +461 -0
- package/src/addon/check-runner.ts +84 -0
- package/src/addon/index.ts +5 -0
- package/src/addon/loader.ts +441 -0
- package/src/addon/registry.ts +230 -0
- package/src/addon/spec.ts +69 -0
- package/src/api/addon.ts +33 -0
- package/src/api/asset-url.ts +24 -0
- package/src/api/index.ts +50 -0
- package/src/api/protocol-internal.ts +656 -0
- package/src/api/protocol.ts +2 -0
- package/src/api/react/index.ts +11 -0
- package/src/api/react/registry.ts +91 -0
- package/src/api/react/use-addon-channel.ts +26 -0
- package/src/api/react/use-button-state.tsx +23 -0
- package/src/api/react/use-deck.ts +24 -0
- package/src/assets/logo.png +0 -0
- package/src/assets/logo72x72.png +0 -0
- package/src/assets/logoFull.png +0 -0
- package/src/builtin-addons/brightness/README.md +51 -0
- package/src/builtin-addons/brightness/buttons/brightness/backend.ts +13 -0
- package/src/builtin-addons/brightness/buttons/brightness/config.ts +10 -0
- package/src/builtin-addons/brightness/buttons/brightness/frontend.tsx +50 -0
- package/src/builtin-addons/brightness/domain/macos.ts +46 -0
- package/src/builtin-addons/brightness/index.d.ts +5 -0
- package/src/builtin-addons/brightness/index.ts +20 -0
- package/src/builtin-addons/brightness/poller.ts +27 -0
- package/src/builtin-addons/brightness/providers/darwin.ts +107 -0
- package/src/builtin-addons/brightness/providers/index.ts +81 -0
- package/src/builtin-addons/brightness/providers/linux.ts +108 -0
- package/src/builtin-addons/brightness/providers/windows.ts +77 -0
- package/src/builtin-addons/brightness/sirenodeck.json +6 -0
- package/src/builtin-addons/coding-agents/assets/claude-code.svg +1 -0
- package/src/builtin-addons/coding-agents/assets/opencode-dark-square.svg +1 -0
- package/src/builtin-addons/coding-agents/buttons/agent/backend.ts +82 -0
- package/src/builtin-addons/coding-agents/buttons/agent/config.ts +13 -0
- package/src/builtin-addons/coding-agents/buttons/agent/frontend.tsx +130 -0
- package/src/builtin-addons/coding-agents/buttons/agent/pages.tsx +150 -0
- package/src/builtin-addons/coding-agents/buttons/summary/backend.ts +48 -0
- package/src/builtin-addons/coding-agents/buttons/summary/config.ts +11 -0
- package/src/builtin-addons/coding-agents/buttons/summary/frontend.tsx +254 -0
- package/src/builtin-addons/coding-agents/decks/agents.ts +69 -0
- package/src/builtin-addons/coding-agents/global/backend.ts +402 -0
- package/src/builtin-addons/coding-agents/global-entry.ts +7 -0
- package/src/builtin-addons/coding-agents/hook/script.ts +123 -0
- package/src/builtin-addons/coding-agents/index.ts +15 -0
- package/src/builtin-addons/coding-agents/manifest.ts +115 -0
- package/src/builtin-addons/coding-agents/providers/claude-code.ts +470 -0
- package/src/builtin-addons/coding-agents/providers/claude-instances.ts +184 -0
- package/src/builtin-addons/coding-agents/providers/opencode-instances.ts +108 -0
- package/src/builtin-addons/coding-agents/providers/opencode.ts +636 -0
- package/src/builtin-addons/coding-agents/providers/registry.ts +120 -0
- package/src/builtin-addons/coding-agents/providers/spawn.ts +112 -0
- package/src/builtin-addons/coding-agents/shared/claude-hook-events.ts +55 -0
- package/src/builtin-addons/coding-agents/shared/claude-status.ts +236 -0
- package/src/builtin-addons/coding-agents/shared/live-count.ts +39 -0
- package/src/builtin-addons/coding-agents/shared/notifier.ts +58 -0
- package/src/builtin-addons/coding-agents/shared/opencode-status.ts +82 -0
- package/src/builtin-addons/coding-agents/shared/snapshot.ts +66 -0
- package/src/builtin-addons/coding-agents/shared/state.ts +72 -0
- package/src/builtin-addons/coding-agents/sirenodeck.json +6 -0
- package/src/builtin-addons/coding-agents/types/types.ts +33 -0
- package/src/builtin-addons/core/README.md +89 -0
- package/src/builtin-addons/core/buttons/action/backend.ts +7 -0
- package/src/builtin-addons/core/buttons/action/config.ts +13 -0
- package/src/builtin-addons/core/buttons/action/frontend.tsx +16 -0
- package/src/builtin-addons/core/buttons/change-deck/backend.ts +18 -0
- package/src/builtin-addons/core/buttons/change-deck/config.ts +15 -0
- package/src/builtin-addons/core/buttons/change-deck/frontend.tsx +18 -0
- package/src/builtin-addons/core/buttons/media-sample/backend.ts +8 -0
- package/src/builtin-addons/core/buttons/media-sample/config.ts +7 -0
- package/src/builtin-addons/core/buttons/media-sample/frontend.tsx +2 -0
- package/src/builtin-addons/core/buttons/page-nav/backend.ts +28 -0
- package/src/builtin-addons/core/buttons/page-nav/config.ts +9 -0
- package/src/builtin-addons/core/buttons/page-nav/frontend.tsx +68 -0
- package/src/builtin-addons/core/buttons/toggle/backend.ts +119 -0
- package/src/builtin-addons/core/buttons/toggle/config.ts +57 -0
- package/src/builtin-addons/core/buttons/toggle/frontend.tsx +57 -0
- package/src/builtin-addons/core/buttons/toggle/global-service.ts +174 -0
- package/src/builtin-addons/core/index.ts +120 -0
- package/src/builtin-addons/core/sirenodeck.json +6 -0
- package/src/builtin-addons/date-time/README.md +73 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/config.ts +4 -0
- package/src/builtin-addons/date-time/buttons/analog-clock/frontend.tsx +88 -0
- package/src/builtin-addons/date-time/buttons/custom/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/custom/config.ts +9 -0
- package/src/builtin-addons/date-time/buttons/custom/frontend.tsx +26 -0
- package/src/builtin-addons/date-time/buttons/date/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/date/config.ts +10 -0
- package/src/builtin-addons/date-time/buttons/date/frontend.tsx +42 -0
- package/src/builtin-addons/date-time/buttons/time/backend.ts +10 -0
- package/src/builtin-addons/date-time/buttons/time/config.ts +9 -0
- package/src/builtin-addons/date-time/buttons/time/frontend.tsx +29 -0
- package/src/builtin-addons/date-time/index.d.ts +5 -0
- package/src/builtin-addons/date-time/index.ts +30 -0
- package/src/builtin-addons/date-time/poller.ts +11 -0
- package/src/builtin-addons/date-time/shared/format-date.ts +36 -0
- package/src/builtin-addons/date-time/shared/format.ts +105 -0
- package/src/builtin-addons/date-time/shared/use-now.ts +10 -0
- package/src/builtin-addons/date-time/sirenodeck.json +6 -0
- package/src/builtin-addons/emoji-selector/README.md +45 -0
- package/src/builtin-addons/emoji-selector/assets/launcher.svg +10 -0
- package/src/builtin-addons/emoji-selector/assets/smileys.svg +8 -0
- package/src/builtin-addons/emoji-selector/buttons/category/backend.ts +14 -0
- package/src/builtin-addons/emoji-selector/buttons/category/config.ts +8 -0
- package/src/builtin-addons/emoji-selector/buttons/category/frontend.tsx +19 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/backend.ts +7 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/config.ts +9 -0
- package/src/builtin-addons/emoji-selector/buttons/emoji/frontend.tsx +11 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/backend.ts +13 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/config.ts +6 -0
- package/src/builtin-addons/emoji-selector/buttons/launcher/frontend.tsx +31 -0
- package/src/builtin-addons/emoji-selector/data/categories.ts +1084 -0
- package/src/builtin-addons/emoji-selector/decks/index.ts +137 -0
- package/src/builtin-addons/emoji-selector/index.d.ts +5 -0
- package/src/builtin-addons/emoji-selector/index.ts +29 -0
- package/src/builtin-addons/emoji-selector/sirenodeck.json +6 -0
- package/src/builtin-addons/emoji-selector/support.ts +49 -0
- package/src/builtin-addons/index.ts +13 -0
- package/src/builtin-addons/internal-settings/README.md +26 -0
- package/src/builtin-addons/internal-settings/backend.ts +58 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/backend.ts +7 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/config.ts +13 -0
- package/src/builtin-addons/internal-settings/buttons/app-info/frontend.tsx +18 -0
- package/src/builtin-addons/internal-settings/buttons/brightness/common/frontend.tsx +50 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/backend.ts +16 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/config.ts +6 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-down/frontend.tsx +8 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/backend.ts +16 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/config.ts +6 -0
- package/src/builtin-addons/internal-settings/buttons/brightness-up/frontend.tsx +8 -0
- package/src/builtin-addons/internal-settings/decks/settings.ts +25 -0
- package/src/builtin-addons/internal-settings/index.d.ts +4 -0
- package/src/builtin-addons/internal-settings/index.ts +53 -0
- package/src/builtin-addons/internal-settings/logo72x72.png +0 -0
- package/src/builtin-addons/internal-settings/sirenodeck.json +6 -0
- package/src/builtin-addons/media/README.md +45 -0
- package/src/builtin-addons/media/backend.ts +143 -0
- package/src/builtin-addons/media/buttons/media-player/backend.ts +19 -0
- package/src/builtin-addons/media/buttons/media-player/components/MediaStatus.tsx +25 -0
- package/src/builtin-addons/media/buttons/media-player/components/MediaSurface.tsx +52 -0
- package/src/builtin-addons/media/buttons/media-player/components/ProgressBar.tsx +21 -0
- package/src/builtin-addons/media/buttons/media-player/components/status-meta.ts +36 -0
- package/src/builtin-addons/media/buttons/media-player/config.ts +4 -0
- package/src/builtin-addons/media/buttons/media-player/frontend.tsx +29 -0
- package/src/builtin-addons/media/buttons/volume/common/frontend.tsx +52 -0
- package/src/builtin-addons/media/buttons/volume/mute/backend.ts +13 -0
- package/src/builtin-addons/media/buttons/volume/mute/config.ts +4 -0
- package/src/builtin-addons/media/buttons/volume/mute/frontend.tsx +26 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/backend.ts +14 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/config.ts +6 -0
- package/src/builtin-addons/media/buttons/volume/volume-down/frontend.tsx +10 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/backend.ts +14 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/config.ts +6 -0
- package/src/builtin-addons/media/buttons/volume/volume-up/frontend.tsx +8 -0
- package/src/builtin-addons/media/checks.ts +104 -0
- package/src/builtin-addons/media/executor.ts +36 -0
- package/src/builtin-addons/media/index.d.ts +5 -0
- package/src/builtin-addons/media/index.ts +53 -0
- package/src/builtin-addons/media/progress.ts +16 -0
- package/src/builtin-addons/media/providers/darwin.ts +391 -0
- package/src/builtin-addons/media/providers/index.ts +27 -0
- package/src/builtin-addons/media/providers/linux.ts +196 -0
- package/src/builtin-addons/media/providers/types.ts +48 -0
- package/src/builtin-addons/media/providers/windows.ts +121 -0
- package/src/builtin-addons/media/sirenodeck.json +6 -0
- package/src/builtin-addons/media/state.ts +19 -0
- package/src/builtin-addons/register-builtins.ts +65 -0
- package/src/builtin-addons/session/README.md +14 -0
- package/src/builtin-addons/session/buttons/session-info/backend.ts +8 -0
- package/src/builtin-addons/session/buttons/session-info/config.ts +4 -0
- package/src/builtin-addons/session/buttons/session-info/frontend.tsx +2 -0
- package/src/builtin-addons/session/buttons/time/backend.ts +7 -0
- package/src/builtin-addons/session/buttons/time/config.ts +6 -0
- package/src/builtin-addons/session/buttons/time/frontend.tsx +2 -0
- package/src/builtin-addons/session/index.d.ts +5 -0
- package/src/builtin-addons/session/index.ts +18 -0
- package/src/builtin-addons/session/sirenodeck.json +6 -0
- package/src/builtin-addons/system-status/README.md +169 -0
- package/src/builtin-addons/system-status/buttons/_shared.ts +131 -0
- package/src/builtin-addons/system-status/buttons/system-status/frontend.tsx +224 -0
- package/src/builtin-addons/system-status/buttons/system-status/schemas.ts +67 -0
- package/src/builtin-addons/system-status/domain/catalog.ts +53 -0
- package/src/builtin-addons/system-status/domain/chart-sampler.ts +58 -0
- package/src/builtin-addons/system-status/domain/display-metrics.ts +128 -0
- package/src/builtin-addons/system-status/domain/index.ts +10 -0
- package/src/builtin-addons/system-status/domain/live-metrics.ts +1021 -0
- package/src/builtin-addons/system-status/domain/metric-ids.ts +0 -0
- package/src/builtin-addons/system-status/index.ts +16 -0
- package/src/builtin-addons/system-status/manifest.ts +105 -0
- package/src/builtin-addons/system-status/shared/metrics-catalog.ts +316 -0
- package/src/builtin-addons/system-status/sirenodeck.json +6 -0
- package/src/builtin-addons/test-buildin/decks/action-buttons-test.ts +56 -0
- package/src/builtin-addons/test-buildin/index.d.ts +5 -0
- package/src/builtin-addons/test-buildin/index.ts +16 -0
- package/src/builtin-addons/value-display/README.md +59 -0
- package/src/builtin-addons/value-display/backend.ts +86 -0
- package/src/builtin-addons/value-display/buttons/value-display/backend.ts +17 -0
- package/src/builtin-addons/value-display/buttons/value-display/config.ts +36 -0
- package/src/builtin-addons/value-display/buttons/value-display/frontend.tsx +54 -0
- package/src/builtin-addons/value-display/index.d.ts +5 -0
- package/src/builtin-addons/value-display/index.ts +23 -0
- package/src/builtin-addons/value-display/sirenodeck.json +6 -0
- package/src/builtin-addons/weather/README.md +46 -0
- package/src/builtin-addons/weather/backend.ts +121 -0
- package/src/builtin-addons/weather/buttons/weather/backend.ts +27 -0
- package/src/builtin-addons/weather/buttons/weather/config.ts +29 -0
- package/src/builtin-addons/weather/buttons/weather/frontend.tsx +63 -0
- package/src/builtin-addons/weather/buttons/weather/icons.tsx +88 -0
- package/src/builtin-addons/weather/buttons/weather/pages.tsx +109 -0
- package/src/builtin-addons/weather/domain/codes.ts +35 -0
- package/src/builtin-addons/weather/domain/fetch.ts +47 -0
- package/src/builtin-addons/weather/index.d.ts +5 -0
- package/src/builtin-addons/weather/index.ts +23 -0
- package/src/builtin-addons/weather/poller.ts +70 -0
- package/src/builtin-addons/weather/provider/city-key.ts +9 -0
- package/src/builtin-addons/weather/provider/codes.ts +35 -0
- package/src/builtin-addons/weather/provider/fetch.ts +97 -0
- package/src/builtin-addons/weather/provider/geocode.ts +67 -0
- package/src/builtin-addons/weather/provider/types.ts +42 -0
- package/src/builtin-addons/weather/sirenodeck.json +6 -0
- package/src/cli/coding-agents-onboarding.ts +434 -0
- package/src/cli/commands/addon-decks.ts +441 -0
- package/src/cli/commands/addon-registry.ts +639 -0
- package/src/cli/commands/emulator-mode.ts +289 -0
- package/src/cli/commands/install.ts +128 -0
- package/src/cli/commands/logs.ts +74 -0
- package/src/cli/commands/network-bind.ts +111 -0
- package/src/cli/commands/pipeline/helpers.ts +75 -0
- package/src/cli/commands/pipeline/preflight.ts +20 -0
- package/src/cli/commands/port-identity.ts +152 -0
- package/src/cli/commands/reload.ts +37 -0
- package/src/cli/commands/restart.ts +139 -0
- package/src/cli/commands/run.ts +2270 -0
- package/src/cli/commands/service/install.ts +121 -0
- package/src/cli/commands/service/render-template.ts +158 -0
- package/src/cli/commands/service-manager.ts +184 -0
- package/src/cli/commands/spawn-daemon.ts +251 -0
- package/src/cli/commands/start.ts +1061 -0
- package/src/cli/commands/status.ts +110 -0
- package/src/cli/commands/stop.ts +121 -0
- package/src/cli/commands/subprocess-supervisor.ts +203 -0
- package/src/cli/commands/system-requirements.ts +699 -0
- package/src/cli/cwd.ts +28 -0
- package/src/cli/first-run.ts +149 -0
- package/src/cli/http-server.ts +244 -0
- package/src/cli/index.ts +355 -0
- package/src/cli/install-root.ts +26 -0
- package/src/cli/main.ts +194 -0
- package/src/cli/package-manager.ts +175 -0
- package/src/cli/probe-deps.ts +45 -0
- package/src/cli/prompt.ts +22 -0
- package/src/cli/startup-display.ts +452 -0
- package/src/config/builtin-icons.ts +57 -0
- package/src/config/config-diff.ts +54 -0
- package/src/config/discovery.ts +64 -0
- package/src/config/icon-resolver.ts +90 -0
- package/src/config/include-resolver.ts +202 -0
- package/src/config/index.ts +9 -0
- package/src/config/loader.ts +187 -0
- package/src/config/mutation.ts +514 -0
- package/src/config/reference-expander.ts +84 -0
- package/src/config/schemas.ts +223 -0
- package/src/config/validation.ts +254 -0
- package/src/core/asset-id.ts +21 -0
- package/src/core/gesture-state.ts +344 -0
- package/src/core/icon-asset-registry.ts +127 -0
- package/src/core/icon-source.ts +86 -0
- package/src/core/index.ts +30 -0
- package/src/core/mime.ts +16 -0
- package/src/core/pagination.ts +90 -0
- package/src/core/pub-sub.ts +126 -0
- package/src/core/settings.ts +153 -0
- package/src/core/store.ts +88 -0
- package/src/core/watcher.ts +73 -0
- package/src/deck/addon-handler-bridge.ts +516 -0
- package/src/deck/deck-config.ts +271 -0
- package/src/deck/editor-surfaces.ts +87 -0
- package/src/deck/host-context.ts +18 -0
- package/src/deck/index.ts +102 -0
- package/src/deck/macro-parse.ts +182 -0
- package/src/deck/methods.ts +348 -0
- package/src/deck/paginate-deck.ts +98 -0
- package/src/deck/position-buttons.ts +57 -0
- package/src/deck/runtime/index.ts +5 -0
- package/src/deck/runtime/runtime.ts +1060 -0
- package/src/deck/runtime-subscriptions.ts +45 -0
- package/src/deck/system-back-injection.ts +62 -0
- package/src/deck/system-buttons/registry.tsx +97 -0
- package/src/deck/system-buttons/types.ts +14 -0
- package/src/device/black-frame.ts +73 -0
- package/src/device/index.ts +13 -0
- package/src/device/linux-udev.ts +36 -0
- package/src/device/models.ts +89 -0
- package/src/device/reconnecting.ts +193 -0
- package/src/device/registry.ts +38 -0
- package/src/device/stream-deck.ts +155 -0
- package/src/index.ts +74 -0
- package/src/outputClient/emulator.ts +357 -0
- package/src/outputClient/index.ts +26 -0
- package/src/outputClient/open-browser.ts +26 -0
- package/src/outputClient/real.ts +393 -0
- package/src/outputClient/types.ts +83 -0
- package/src/render/browser-renderer.ts +240 -0
- package/src/render/buffer-hash.ts +20 -0
- package/src/render/editor-handler.ts +197 -0
- package/src/render/emulator-server.ts +128 -0
- package/src/render/icon-source-resolver.ts +69 -0
- package/src/render/index.ts +16 -0
- package/src/render/protocol.ts +61 -0
- package/src/render/push-raw-image.ts +100 -0
- package/src/render/screenshot-cadence.ts +87 -0
- package/src/render/state-publisher.ts +110 -0
- package/src/render/vite-server.ts +138 -0
- package/src/render/ws-bridge.ts +315 -0
- package/src/system/device-selection.ts +80 -0
- package/src/system/glob-match.ts +74 -0
- package/src/system/providers/active-app/darwin.ts +117 -0
- package/src/system/providers/active-app/linux.ts +245 -0
- package/src/system/providers/active-app/wayland-gnome.ts +303 -0
- package/src/system/providers/active-app/windows.ts +94 -0
- package/src/system/providers/active-app.ts +97 -0
- package/src/system/providers/clipboard/darwin.ts +65 -0
- package/src/system/providers/clipboard/linux.ts +163 -0
- package/src/system/providers/clipboard/windows.ts +94 -0
- package/src/system/providers/clipboard.ts +84 -0
- package/src/system/providers/error.ts +16 -0
- package/src/system/providers/key-macro/darwin.ts +221 -0
- package/src/system/providers/key-macro/linux.ts +651 -0
- package/src/system/providers/key-macro/parser.ts +215 -0
- package/src/system/providers/key-macro/windows.ts +476 -0
- package/src/system/providers/key-macro.ts +79 -0
- package/src/system/providers/notification/darwin.ts +36 -0
- package/src/system/providers/notification/linux.ts +115 -0
- package/src/system/providers/notification/windows.ts +72 -0
- package/src/system/providers/notification.ts +75 -0
- package/src/system/providers/session/darwin.ts +82 -0
- package/src/system/providers/session/linux.ts +175 -0
- package/src/system/providers/session/windows.ts +73 -0
- package/src/system/providers/session.ts +89 -0
- package/src/system/providers/shared.ts +65 -0
- package/src/system/requirements.ts +235 -0
- package/src/system/setup-wizard/config-seed.ts +30 -0
- package/src/system/setup-wizard/format.ts +275 -0
- package/src/system/setup-wizard/index.ts +45 -0
- package/src/system/setup-wizard/open-settings.ts +26 -0
- package/src/system/setup-wizard/plan.ts +230 -0
- package/src/system/setup-wizard/probe-cache.ts +34 -0
- package/src/system/setup-wizard/probe.ts +486 -0
- package/src/system/setup-wizard/runtime-features.ts +85 -0
- package/src/system/setup-wizard/sudo.ts +178 -0
- package/src/system/setup-wizard/types.ts +133 -0
- package/src/system/virtual-stream-deck.ts +67 -0
- package/src/themes/css.ts +210 -0
- package/src/themes/default/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/default/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Italic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Light.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-LightItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBold.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-Thin.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/IBMPlexMono-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/IBM_Plex_Mono/OFL.txt +93 -0
- package/src/themes/default/assets/OFL.txt +93 -0
- package/src/themes/default/assets/README.txt +106 -0
- package/src/themes/default/assets/static/IBMPlexSans-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_Condensed-ThinItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Bold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-BoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLight.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ExtraLightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Italic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Light.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-LightItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Medium.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-MediumItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Regular.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBold.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-SemiBoldItalic.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-Thin.ttf +0 -0
- package/src/themes/default/assets/static/IBMPlexSans_SemiCondensed-ThinItalic.ttf +0 -0
- package/src/themes/default/components.css +81 -0
- package/src/themes/default/sirenodeck.json +170 -0
- package/src/themes/index.ts +14 -0
- package/src/themes/light/assets/IBMPlexSans-Italic-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/light/assets/IBMPlexSans-VariableFont_wdth,wght.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Bold.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Medium.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/IBMPlexMono-Regular.ttf +0 -0
- package/src/themes/light/assets/IBM_Plex_Mono/OFL.txt +93 -0
- package/src/themes/light/assets/OFL.txt +93 -0
- package/src/themes/light/assets/README.txt +106 -0
- package/src/themes/light/assets/static/IBMPlexSans-Light.ttf +0 -0
- package/src/themes/light/assets/static/IBMPlexSans-LightItalic.ttf +0 -0
- package/src/themes/light/assets/static/IBMPlexSans-Thin.ttf +0 -0
- package/src/themes/light/components.css +70 -0
- package/src/themes/light/sirenodeck.json +170 -0
- package/src/themes/loader.ts +343 -0
- package/src/themes/manifest.ts +164 -0
- package/src/themes/use-resolved-theme.tsx +32 -0
- package/src/ui/ButtonFrame.tsx +230 -0
- package/src/ui/contexts/AssetCacheContext.tsx +43 -0
- package/src/ui/index.ts +20 -0
- package/src/ui/primitives/Chip.tsx +53 -0
- package/src/ui/primitives/Icon.tsx +209 -0
- package/src/ui/primitives/Label.tsx +82 -0
- package/src/ui/primitives/ProgressBar.tsx +38 -0
- package/src/ui/primitives/TapIndicator.tsx +67 -0
- package/src/ui/primitives/Text.tsx +810 -0
- package/src/ui/primitives/index.ts +6 -0
- package/src/ui/surfaces/BarsSurface.tsx +211 -0
- package/src/ui/surfaces/IconLabelProgressSurface.tsx +133 -0
- package/src/ui/surfaces/IconLabelSurface.tsx +59 -0
- package/src/ui/surfaces/LabelValueListSurface.tsx +202 -0
- package/src/ui/surfaces/PaginatedSurface.tsx +164 -0
- package/src/ui/surfaces/SplitActionSurface.tsx +98 -0
- package/src/ui/surfaces/TemporaryErrorSurface.tsx +68 -0
- package/src/ui/surfaces/ValueChart.tsx +218 -0
- package/src/ui/surfaces/index.ts +32 -0
- package/src/ui/surfaces/paginated-schema.ts +22 -0
- package/src/ui/theme-presentation-builder.ts +184 -0
- package/src/ui/theme-presentation.tsx +146 -0
- package/src/ui/utils/cn.ts +12 -0
- package/src/util/cache-paths.ts +38 -0
- package/src/util/config-diff.ts +16 -0
- package/src/util/daemon-control.ts +85 -0
- package/src/util/daemon.ts +575 -0
- package/src/util/device-config.ts +56 -0
- package/src/util/errors.ts +13 -0
- package/src/util/log-reader.ts +114 -0
- package/src/util/log-tail.ts +103 -0
- package/src/util/logger.ts +417 -0
- package/src/util/process-title.ts +79 -0
- package/src/util/single-instance.ts +202 -0
- package/src/version.ts +10 -0
- package/src/vite/index.ts +6 -0
- package/src/vite/virtual-modules.ts +631 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { execFile, spawn } from "node:child_process"
|
|
2
|
+
import { promisify } from "node:util"
|
|
3
|
+
|
|
4
|
+
import type pino from "pino"
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile)
|
|
7
|
+
|
|
8
|
+
export interface SudoRunOptions {
|
|
9
|
+
readonly command: string
|
|
10
|
+
readonly args: ReadonlyArray<string>
|
|
11
|
+
readonly logger?: pino.Logger
|
|
12
|
+
readonly stdinInput?: string
|
|
13
|
+
readonly timeoutMs?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SudoRunResult {
|
|
17
|
+
readonly exitCode: number
|
|
18
|
+
readonly stdout: string
|
|
19
|
+
readonly stderr: string
|
|
20
|
+
readonly neededPassword: boolean
|
|
21
|
+
readonly succeeded: boolean
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PROBE_TIMEOUT_MS = 3_000
|
|
25
|
+
|
|
26
|
+
export const isSudoNopasswd = async (): Promise<boolean> => {
|
|
27
|
+
try {
|
|
28
|
+
await execFileAsync("sudo", ["-n", "true"], {
|
|
29
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
30
|
+
})
|
|
31
|
+
return true
|
|
32
|
+
} catch {
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ponytail: match the real-world sudo failure patterns. The last pattern
|
|
38
|
+
// catches the PAM-style prompt sudo emits when it has eaten the password
|
|
39
|
+
// from stdin and decides auth failed — `[sudo: authenticate] Password:` is
|
|
40
|
+
// what appears when -S doesn't read from a TTY and the user didn't type
|
|
41
|
+
// anything. Without this match the wizard reports a bare "exit 1" with no
|
|
42
|
+
// hint that the password was the cause.
|
|
43
|
+
const NEEDS_PASSWORD = [
|
|
44
|
+
/password is required/i,
|
|
45
|
+
/sorry,? try again/i,
|
|
46
|
+
/incorrect password attempt/i,
|
|
47
|
+
/authentication required but not attempted/i,
|
|
48
|
+
/^\[sudo:?\s*\w*\]?\s*Password:?$/m,
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
const stderrNeedsPassword = (stderr: string): boolean =>
|
|
52
|
+
NEEDS_PASSWORD.some((re) => re.test(stderr))
|
|
53
|
+
|
|
54
|
+
// ponytail: spawn (not execFile) so we control the stdin lifecycle. The
|
|
55
|
+
// previous execFile + `input` race lost the password to the pipe close —
|
|
56
|
+
// `child.stdin.write(input)` is queued, but sudo reads EOF before Node.js
|
|
57
|
+
// flushes it. spawn gives us an explicit write → drain → close sequence.
|
|
58
|
+
export const runWithSudo = async (
|
|
59
|
+
options: SudoRunOptions,
|
|
60
|
+
): Promise<SudoRunResult> => {
|
|
61
|
+
const args = ["-S", "--", options.command, ...options.args]
|
|
62
|
+
const timeoutMs = options.timeoutMs ?? 60_000
|
|
63
|
+
const logger = options.logger
|
|
64
|
+
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
const child = spawn("sudo", args, { stdio: ["pipe", "pipe", "pipe"] })
|
|
67
|
+
let stdout = ""
|
|
68
|
+
let stderr = ""
|
|
69
|
+
let settled = false
|
|
70
|
+
|
|
71
|
+
const settle = (result: SudoRunResult): void => {
|
|
72
|
+
if (settled) return
|
|
73
|
+
settled = true
|
|
74
|
+
resolve(result)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
child.stdout.on("data", (c: Buffer) => {
|
|
78
|
+
stdout += c.toString()
|
|
79
|
+
})
|
|
80
|
+
child.stderr.on("data", (c: Buffer) => {
|
|
81
|
+
stderr += c.toString()
|
|
82
|
+
})
|
|
83
|
+
child.stdin.on("error", () => {
|
|
84
|
+
// ignore EPIPE if sudo exits before we finish writing
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const timeout = setTimeout(() => {
|
|
88
|
+
child.kill("SIGKILL")
|
|
89
|
+
settle({
|
|
90
|
+
exitCode: 124,
|
|
91
|
+
stdout,
|
|
92
|
+
stderr: stderr + "\n[sirenodeck] sudo timed out",
|
|
93
|
+
neededPassword: stderrNeedsPassword(stderr),
|
|
94
|
+
succeeded: false,
|
|
95
|
+
})
|
|
96
|
+
}, timeoutMs)
|
|
97
|
+
|
|
98
|
+
child.on("error", (err) => {
|
|
99
|
+
clearTimeout(timeout)
|
|
100
|
+
settle({
|
|
101
|
+
exitCode: 1,
|
|
102
|
+
stdout,
|
|
103
|
+
stderr: stderr + (stderr.length > 0 ? "\n" : "") + err.message,
|
|
104
|
+
neededPassword: false,
|
|
105
|
+
succeeded: false,
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
child.on("close", (code) => {
|
|
110
|
+
clearTimeout(timeout)
|
|
111
|
+
const exitCode = code ?? 1
|
|
112
|
+
const neededPassword = stderrNeedsPassword(stderr)
|
|
113
|
+
if (exitCode !== 0) {
|
|
114
|
+
logger?.warn(
|
|
115
|
+
{ exitCode, stderr: stderr.slice(0, 500) },
|
|
116
|
+
"sudo run failed",
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
settle({
|
|
120
|
+
exitCode,
|
|
121
|
+
stdout,
|
|
122
|
+
stderr,
|
|
123
|
+
neededPassword,
|
|
124
|
+
succeeded: code === 0,
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
if (options.stdinInput !== undefined && options.stdinInput.length > 0) {
|
|
129
|
+
child.stdin.write(options.stdinInput, (err) => {
|
|
130
|
+
if (err) return
|
|
131
|
+
child.stdin.end()
|
|
132
|
+
})
|
|
133
|
+
} else {
|
|
134
|
+
child.stdin.end()
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const capturePassword = (prompt: string): Promise<string> =>
|
|
140
|
+
new Promise((resolve, reject) => {
|
|
141
|
+
if (!process.stdin.isTTY) {
|
|
142
|
+
reject(new Error("cannot read sudo password: no TTY"))
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
const stdin = process.stdin
|
|
146
|
+
const oldRaw = (stdin as { isRaw?: boolean }).isRaw
|
|
147
|
+
stdin.setRawMode?.(true)
|
|
148
|
+
stdin.resume()
|
|
149
|
+
stdin.write("")
|
|
150
|
+
let password = ""
|
|
151
|
+
const onData = (chunk: Buffer | string): void => {
|
|
152
|
+
const str = chunk.toString()
|
|
153
|
+
for (const ch of str) {
|
|
154
|
+
if (ch === "\n" || ch === "\r" || ch === "\u0004") {
|
|
155
|
+
stdin.removeListener("data", onData)
|
|
156
|
+
stdin.setRawMode?.(oldRaw ?? false)
|
|
157
|
+
stdin.pause()
|
|
158
|
+
process.stdout.write("\n")
|
|
159
|
+
resolve(password)
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
if (ch === "\u0003") {
|
|
163
|
+
stdin.removeListener("data", onData)
|
|
164
|
+
stdin.setRawMode?.(oldRaw ?? false)
|
|
165
|
+
stdin.pause()
|
|
166
|
+
reject(new Error("user cancelled password prompt"))
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
if (ch === "\u007f" || ch === "\b") {
|
|
170
|
+
password = password.slice(0, -1)
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
password += ch
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
stdin.on("data", onData)
|
|
177
|
+
process.stdout.write(prompt)
|
|
178
|
+
})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { CommandExecutor } from "../providers/shared"
|
|
2
|
+
|
|
3
|
+
export type CapabilityName =
|
|
4
|
+
| "keyMacro"
|
|
5
|
+
| "clipboard"
|
|
6
|
+
| "notification"
|
|
7
|
+
| "activeApp"
|
|
8
|
+
|
|
9
|
+
export type PackageManager =
|
|
10
|
+
| "apt"
|
|
11
|
+
| "dnf"
|
|
12
|
+
| "pacman"
|
|
13
|
+
| "zypper"
|
|
14
|
+
| "brew"
|
|
15
|
+
| "none"
|
|
16
|
+
|
|
17
|
+
export type DesktopSession = "wayland" | "x11" | "unknown"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* An OS permission a capability needs on top of its binary. `null` on
|
|
21
|
+
* platforms where the capability needs no grant.
|
|
22
|
+
*
|
|
23
|
+
* ponytail: without this, a denied grant was squeezed into `missing` as a fake
|
|
24
|
+
* command name, so the panel rendered "osascript: Not installed" for a binary
|
|
25
|
+
* that ships with macOS and is very much installed. Installed-ness and
|
|
26
|
+
* permission are two independent facts and the report now carries both.
|
|
27
|
+
*/
|
|
28
|
+
export interface CapabilityPermission {
|
|
29
|
+
/** Human name of the grant, e.g. "Accessibility". */
|
|
30
|
+
readonly label: string
|
|
31
|
+
readonly granted: boolean
|
|
32
|
+
/** One sentence telling the user how to grant it. */
|
|
33
|
+
readonly hint: string
|
|
34
|
+
/** Deep link we can hand to `open`, when the OS offers one. */
|
|
35
|
+
readonly settingsUrl: string | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CapabilityProbe {
|
|
39
|
+
readonly name: CapabilityName
|
|
40
|
+
/** Binary present AND any required permission granted. */
|
|
41
|
+
readonly available: boolean
|
|
42
|
+
/** Binary present, regardless of permissions. */
|
|
43
|
+
readonly toolInstalled: boolean
|
|
44
|
+
readonly permission: CapabilityPermission | null
|
|
45
|
+
readonly missing: ReadonlyArray<string>
|
|
46
|
+
readonly preferred: string
|
|
47
|
+
readonly reason: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface UdevProbe {
|
|
51
|
+
readonly rulesInstalled: boolean
|
|
52
|
+
readonly rulesPath: string
|
|
53
|
+
readonly streamDeckConnected: boolean
|
|
54
|
+
readonly matchedProductIds: ReadonlyArray<string>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ConfigProbe {
|
|
58
|
+
readonly exists: boolean
|
|
59
|
+
readonly path: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface SystemReport {
|
|
63
|
+
readonly platform: string
|
|
64
|
+
readonly homeDir: string
|
|
65
|
+
readonly xdgConfigHome: string
|
|
66
|
+
readonly session: DesktopSession
|
|
67
|
+
readonly packageManager: PackageManager
|
|
68
|
+
readonly capabilities: Readonly<Record<CapabilityName, CapabilityProbe>>
|
|
69
|
+
readonly udev: UdevProbe
|
|
70
|
+
readonly config: ConfigProbe
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface InstallStep {
|
|
74
|
+
readonly id: string
|
|
75
|
+
readonly capability: CapabilityName | "udev" | "config"
|
|
76
|
+
readonly title: string
|
|
77
|
+
readonly description: string
|
|
78
|
+
readonly packageManager: PackageManager
|
|
79
|
+
readonly packages: ReadonlyArray<string>
|
|
80
|
+
readonly sudo: boolean
|
|
81
|
+
readonly manualOnly: boolean
|
|
82
|
+
readonly manualInstructions: string
|
|
83
|
+
readonly verifyCommand?: string
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type InstallStepResult = "installed" | "skipped" | "failed" | "manual"
|
|
87
|
+
|
|
88
|
+
export interface WizardOutcome {
|
|
89
|
+
readonly report: SystemReport
|
|
90
|
+
readonly steps: ReadonlyArray<InstallStep>
|
|
91
|
+
readonly results: Readonly<Record<string, InstallStepResult>>
|
|
92
|
+
readonly configSeeded: boolean
|
|
93
|
+
readonly configPath: string
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ProbeDeps {
|
|
97
|
+
readonly platform: string
|
|
98
|
+
readonly homeDir: string
|
|
99
|
+
readonly xdgConfigHome: string
|
|
100
|
+
readonly env: NodeJS.ProcessEnv
|
|
101
|
+
readonly executor: CommandExecutor
|
|
102
|
+
readonly extraFsProbe?: (command: string) => boolean
|
|
103
|
+
readonly fileExists: (path: string) => boolean
|
|
104
|
+
readonly readFile: (path: string) => string | null
|
|
105
|
+
// ponytail: the config the caller actually intends to run with. Without it
|
|
106
|
+
// the probe only ever stats $XDG_CONFIG_HOME/sirenodeck/config.yml, so a
|
|
107
|
+
// valid `--config ./my.yml` still reported "Config: missing" and the
|
|
108
|
+
// first-run gate refused to start the daemon.
|
|
109
|
+
readonly configPath?: string
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface SystemReportSummary {
|
|
113
|
+
readonly ok: boolean
|
|
114
|
+
readonly session: DesktopSession
|
|
115
|
+
readonly packageManager: PackageManager
|
|
116
|
+
readonly missingCapabilities: ReadonlyArray<CapabilityName>
|
|
117
|
+
readonly udevMissing: boolean
|
|
118
|
+
readonly configMissing: boolean
|
|
119
|
+
readonly configPath: string
|
|
120
|
+
readonly streamDeckConnected: boolean
|
|
121
|
+
readonly lines: ReadonlyArray<string>
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export const UDEV_RULES_PATH = "/etc/udev/rules.d/70-sirenodeck.rules"
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Deep link that opens System Settings straight at the Accessibility pane, so
|
|
128
|
+
* the user does not have to hunt through Privacy & Security by hand. This URL
|
|
129
|
+
* scheme is the documented way to target a settings pane and has survived the
|
|
130
|
+
* System Preferences → System Settings rename.
|
|
131
|
+
*/
|
|
132
|
+
export const ACCESSIBILITY_SETTINGS_URL =
|
|
133
|
+
"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events"
|
|
2
|
+
|
|
3
|
+
export type StreamDeckKeyEventType = "down" | "up"
|
|
4
|
+
|
|
5
|
+
export interface StreamDeckKeyEvent {
|
|
6
|
+
readonly type: StreamDeckKeyEventType
|
|
7
|
+
readonly keyIndex: number
|
|
8
|
+
readonly timestamp: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface VirtualStreamDeckOptions {
|
|
12
|
+
readonly keyCount: number
|
|
13
|
+
readonly autoTimestamp?: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface VirtualStreamDeckLifecycle {
|
|
17
|
+
getKeyCount(): number
|
|
18
|
+
injectKeyEvent(event: StreamDeckKeyEvent): void
|
|
19
|
+
injectKey(type: StreamDeckKeyEventType, keyIndex: number): void
|
|
20
|
+
onKeyEvent(handler: (event: StreamDeckKeyEvent) => void): () => void
|
|
21
|
+
clear(): void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const createVirtualStreamDeckLifecycle = (
|
|
25
|
+
options: VirtualStreamDeckOptions,
|
|
26
|
+
): VirtualStreamDeckLifecycle => {
|
|
27
|
+
const emitter = new EventEmitter()
|
|
28
|
+
const { autoTimestamp = true } = options
|
|
29
|
+
|
|
30
|
+
const getKeyCount = (): number => options.keyCount
|
|
31
|
+
|
|
32
|
+
const injectKeyEvent = (event: StreamDeckKeyEvent): void => {
|
|
33
|
+
emitter.emit("key", event)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const injectKey = (type: StreamDeckKeyEventType, keyIndex: number): void => {
|
|
37
|
+
if (keyIndex < 0 || keyIndex >= options.keyCount) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`injectKey: keyIndex ${keyIndex} out of range (0..${options.keyCount - 1})`,
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
injectKeyEvent({
|
|
43
|
+
type,
|
|
44
|
+
keyIndex,
|
|
45
|
+
timestamp: autoTimestamp ? Date.now() : 0,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const onKeyEvent = (
|
|
50
|
+
handler: (event: StreamDeckKeyEvent) => void,
|
|
51
|
+
): (() => void) => {
|
|
52
|
+
emitter.on("key", handler)
|
|
53
|
+
return () => emitter.off("key", handler)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const clear = (): void => {
|
|
57
|
+
emitter.removeAllListeners("key")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
getKeyCount,
|
|
62
|
+
injectKeyEvent,
|
|
63
|
+
injectKey,
|
|
64
|
+
onKeyEvent,
|
|
65
|
+
clear,
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { ThemeJsonManifest } from "./manifest"
|
|
2
|
+
|
|
3
|
+
function toFontFamilyValue(fontFamily: string): string {
|
|
4
|
+
return fontFamily.includes(",")
|
|
5
|
+
? fontFamily
|
|
6
|
+
: `'${fontFamily.replace(/'/g, "\\'")}'`
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function formatRoleName(name: string): string {
|
|
10
|
+
if (name === "main_text") return "main"
|
|
11
|
+
if (name === "auxiliary_text") return "aux"
|
|
12
|
+
if (name === "monospace") return "mono"
|
|
13
|
+
return name
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatTypographyRoleVariables(
|
|
17
|
+
roleName: string,
|
|
18
|
+
role: {
|
|
19
|
+
fontFamily: string
|
|
20
|
+
fontSize: number
|
|
21
|
+
fontWeight: number
|
|
22
|
+
letterSpacing?: number
|
|
23
|
+
},
|
|
24
|
+
): string[] {
|
|
25
|
+
const cssRoleName = formatRoleName(roleName)
|
|
26
|
+
return [
|
|
27
|
+
` --sireno-font-${cssRoleName}-family: ${toFontFamilyValue(role.fontFamily)};`,
|
|
28
|
+
` --sireno-font-${cssRoleName}-size: ${role.fontSize}px;`,
|
|
29
|
+
` --sireno-font-${cssRoleName}-weight: ${role.fontWeight};`,
|
|
30
|
+
` --sireno-font-${cssRoleName}-tracking: ${role.letterSpacing ?? 0}px;`,
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const FORMAT_BY_EXT: Record<string, string> = {
|
|
35
|
+
".woff2": "woff2",
|
|
36
|
+
".woff": "woff",
|
|
37
|
+
".ttf": "truetype",
|
|
38
|
+
".otf": "opentype",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Derive the CSS `format()` token from the font src extension. Falls back to
|
|
43
|
+
* truetype for unknown / extensionless paths so legacy manifests keep working.
|
|
44
|
+
*/
|
|
45
|
+
function fontFormatForSrc(src: string): string {
|
|
46
|
+
const match = src.match(/\.[a-z0-9]+(?:\?.*)?$/i)
|
|
47
|
+
if (match === null) return "truetype"
|
|
48
|
+
return FORMAT_BY_EXT[match[0].toLowerCase()] ?? "truetype"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const MANIFEST_TO_CSS_TOKEN: Record<string, string> = {
|
|
52
|
+
background: "bg",
|
|
53
|
+
foreground: "fg",
|
|
54
|
+
"foreground-contrast": "foreground-contrast",
|
|
55
|
+
frame: "frame",
|
|
56
|
+
primary: "primary",
|
|
57
|
+
accent: "accent",
|
|
58
|
+
success: "success",
|
|
59
|
+
danger: "danger",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function buildThemeCss(
|
|
63
|
+
manifest: ThemeJsonManifest,
|
|
64
|
+
stylesheetContents: ReadonlyArray<string>,
|
|
65
|
+
): string {
|
|
66
|
+
const { colorTokens, typography, fonts } = manifest
|
|
67
|
+
|
|
68
|
+
const parts: string[] = []
|
|
69
|
+
|
|
70
|
+
// Tailwind @theme block — emitted last so the @theme block is the
|
|
71
|
+
// final block the Tailwind v4 PostCSS plugin sees. Empirically the
|
|
72
|
+
// vite plugin truncates a leading @theme block to only the first
|
|
73
|
+
// few entries; emitting it after @font-face / :root makes the full
|
|
74
|
+
// set of variables land in @layer theme and produce utilities like
|
|
75
|
+
// bg-frame, text-accent, font-mono, etc.
|
|
76
|
+
const themeLines = ["@theme {"]
|
|
77
|
+
for (const [token, value] of Object.entries(colorTokens)) {
|
|
78
|
+
const cssToken =
|
|
79
|
+
MANIFEST_TO_CSS_TOKEN[token] ??
|
|
80
|
+
token.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
|
81
|
+
themeLines.push(` --color-${cssToken}: ${value};`)
|
|
82
|
+
}
|
|
83
|
+
for (const [roleName, role] of Object.entries(typography)) {
|
|
84
|
+
const cssRoleName = formatRoleName(roleName)
|
|
85
|
+
themeLines.push(
|
|
86
|
+
` --font-${cssRoleName}: ${toFontFamilyValue(role.fontFamily)}, ui-sans-serif, system-ui, sans-serif;`,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
themeLines.push("}")
|
|
90
|
+
|
|
91
|
+
// @font-face declarations
|
|
92
|
+
if (fonts.length > 0) {
|
|
93
|
+
const fontFaces = fonts.map((f) => {
|
|
94
|
+
// Variable fonts — emit a single @font-face with from..to range when
|
|
95
|
+
// the manifest declares axes. Otherwise preserve the single weight.
|
|
96
|
+
const axes = f.axes
|
|
97
|
+
const weightRange = axes?.weight
|
|
98
|
+
const weight =
|
|
99
|
+
weightRange !== undefined
|
|
100
|
+
? `${weightRange[0]} ${weightRange[1]}`
|
|
101
|
+
: f.fontStyle === "italic"
|
|
102
|
+
? `${f.fontWeight} italic`
|
|
103
|
+
: String(f.fontWeight)
|
|
104
|
+
const format = fontFormatForSrc(f.src)
|
|
105
|
+
return `@font-face {\n font-family: '${f.fontFamily}';\n font-style: ${f.fontStyle};\n font-weight: ${weight};\n src: url('${f.src}') format('${format}');\n}`
|
|
106
|
+
})
|
|
107
|
+
parts.push(fontFaces.join("\n\n"))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// :root CSS variables (runtime) — emit only the --sireno-color-* prefixed
|
|
111
|
+
// runtime values. We deliberately avoid also setting the plain --color-*
|
|
112
|
+
// names here, because Tailwind v4 will only keep a @theme variable in
|
|
113
|
+
// @layer theme when it is the unique declaration of that name. If we
|
|
114
|
+
// duplicate --color-frame in :root, Tailwind considers the @theme entry
|
|
115
|
+
// redundant and silently drops it, leaving bg-frame / text-frame / etc.
|
|
116
|
+
// utilities un-generated.
|
|
117
|
+
const rootLines = [":root {"]
|
|
118
|
+
for (const [token, value] of Object.entries(colorTokens)) {
|
|
119
|
+
const cssToken =
|
|
120
|
+
MANIFEST_TO_CSS_TOKEN[token] ??
|
|
121
|
+
token.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
|
122
|
+
rootLines.push(` --sireno-color-${cssToken}: ${value};`)
|
|
123
|
+
}
|
|
124
|
+
for (const [roleName, role] of Object.entries(typography)) {
|
|
125
|
+
rootLines.push(...formatTypographyRoleVariables(roleName, role))
|
|
126
|
+
}
|
|
127
|
+
// Variant tokens — every theme must declare the 5 required keys; themes
|
|
128
|
+
// may add extras. ButtonFrame reads --sireno-variant-<name>-{bg,border,fg,glow}
|
|
129
|
+
// directly, so addons and themes both go through one surface.
|
|
130
|
+
//
|
|
131
|
+
// buttonColor variants may also declare a `tokens` block to override
|
|
132
|
+
// primary/accent/foreground/foreground-contrast/success/danger per-variant.
|
|
133
|
+
// For 4-slot variants (no tokens block), fall back to theme colorTokens so
|
|
134
|
+
// primitives reading --sireno-color-* via cascade get the right value.
|
|
135
|
+
const VARIANT_TOKEN_KEYS = [
|
|
136
|
+
"primary",
|
|
137
|
+
"accent",
|
|
138
|
+
"foreground",
|
|
139
|
+
"foreground-contrast",
|
|
140
|
+
"success",
|
|
141
|
+
"danger",
|
|
142
|
+
"muted",
|
|
143
|
+
] as const
|
|
144
|
+
for (const [variantName, styles] of Object.entries(manifest.variants)) {
|
|
145
|
+
rootLines.push(
|
|
146
|
+
` --sireno-variant-${variantName}-bg: ${styles.background};`,
|
|
147
|
+
)
|
|
148
|
+
rootLines.push(
|
|
149
|
+
` --sireno-variant-${variantName}-border: ${styles.border};`,
|
|
150
|
+
)
|
|
151
|
+
rootLines.push(
|
|
152
|
+
` --sireno-variant-${variantName}-fg: ${styles.foreground};`,
|
|
153
|
+
)
|
|
154
|
+
rootLines.push(
|
|
155
|
+
` --sireno-variant-${variantName}-glow: ${styles.glow ?? "0 0 0 transparent"};`,
|
|
156
|
+
)
|
|
157
|
+
const tokens = styles.tokens
|
|
158
|
+
for (const tokenKey of VARIANT_TOKEN_KEYS) {
|
|
159
|
+
const cssToken =
|
|
160
|
+
MANIFEST_TO_CSS_TOKEN[tokenKey] ??
|
|
161
|
+
tokenKey.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase()
|
|
162
|
+
const themeValue =
|
|
163
|
+
colorTokens[tokenKey as keyof typeof colorTokens] ?? "#000000"
|
|
164
|
+
const variantValue =
|
|
165
|
+
tokens?.[tokenKey as keyof typeof tokens] ?? themeValue
|
|
166
|
+
rootLines.push(
|
|
167
|
+
` --sireno-variant-${variantName}-${cssToken}: ${variantValue};`,
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Effect tokens — glow / shadow / blur, optional. Themes without effects
|
|
172
|
+
// still get sane defaults so component CSS never reads `undefined` for a
|
|
173
|
+
// token.
|
|
174
|
+
const effects = manifest.effects
|
|
175
|
+
rootLines.push(
|
|
176
|
+
` --sireno-glow-sm: ${effects?.glow?.sm ?? "0 0 4px transparent"};`,
|
|
177
|
+
)
|
|
178
|
+
rootLines.push(
|
|
179
|
+
` --sireno-glow-md: ${effects?.glow?.md ?? "0 0 12px transparent"};`,
|
|
180
|
+
)
|
|
181
|
+
rootLines.push(
|
|
182
|
+
` --sireno-glow-lg: ${effects?.glow?.lg ?? "0 0 24px transparent"};`,
|
|
183
|
+
)
|
|
184
|
+
rootLines.push(
|
|
185
|
+
` --sireno-shadow-soft: ${effects?.shadow?.soft ?? "0 1px 2px transparent"};`,
|
|
186
|
+
)
|
|
187
|
+
rootLines.push(
|
|
188
|
+
` --sireno-shadow-hard: ${effects?.shadow?.hard ?? "0 4px 12px transparent"};`,
|
|
189
|
+
)
|
|
190
|
+
rootLines.push(` --sireno-blur-sm: ${effects?.blur?.sm ?? "4px"};`)
|
|
191
|
+
rootLines.push(` --sireno-blur-md: ${effects?.blur?.md ?? "10px"};`)
|
|
192
|
+
// Default pressed / holding glow hooks — every theme gets these for free
|
|
193
|
+
// (Gaps 10). Themes can override in their own stylesheet under @layer.
|
|
194
|
+
rootLines.push(` --sireno-pressed-glow: var(--sireno-glow-md);`)
|
|
195
|
+
rootLines.push(` --sireno-held-glow: var(--sireno-glow-lg);`)
|
|
196
|
+
rootLines.push("}")
|
|
197
|
+
parts.push(rootLines.join("\n"))
|
|
198
|
+
|
|
199
|
+
// Concatenated stylesheets — wrap in @layer so theme styles override
|
|
200
|
+
// built-in styles predictably (Tailwind v4 layer order is fixed:
|
|
201
|
+
// theme < base < components < utilities < user-overrides).
|
|
202
|
+
if (stylesheetContents.length > 0) {
|
|
203
|
+
parts.push(`@layer theme-override {\n${stylesheetContents.join("\n\n")}\n}`)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// @theme block emitted last — see comment above the @theme block.
|
|
207
|
+
parts.push(themeLines.join("\n"))
|
|
208
|
+
|
|
209
|
+
return parts.join("\n\n")
|
|
210
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|