@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,631 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
statSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs"
|
|
8
|
+
import { basename, dirname, join } from "node:path"
|
|
9
|
+
import type { Plugin, ViteDevServer } from "vite"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Asset base dir for browser-side `addon://` fetching, per configured
|
|
13
|
+
* addon. Mirrors buildExternalAddonDirs' dist-preferred logic on the CLI
|
|
14
|
+
* side: assets ship in <pkg>/dist/assets (post-build), source-only addons
|
|
15
|
+
* keep <pkg>/assets.
|
|
16
|
+
*/
|
|
17
|
+
const addonAssetBaseDir = (frontendMain: string): string | null => {
|
|
18
|
+
const dir = dirname(frontendMain)
|
|
19
|
+
const base =
|
|
20
|
+
dirname(dir) === "" || dir === "."
|
|
21
|
+
? null
|
|
22
|
+
: basename(dir) === "src" || basename(dir) === "dist"
|
|
23
|
+
? dirname(dir)
|
|
24
|
+
: dir
|
|
25
|
+
if (base === null) return null
|
|
26
|
+
if (existsSync(join(base, "dist", "assets"))) return join(base, "dist")
|
|
27
|
+
if (existsSync(join(base, "assets"))) return base
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ASSET_MIME: Record<string, string> = {
|
|
32
|
+
svg: "image/svg+xml",
|
|
33
|
+
png: "image/png",
|
|
34
|
+
jpg: "image/jpeg",
|
|
35
|
+
jpeg: "image/jpeg",
|
|
36
|
+
webp: "image/webp",
|
|
37
|
+
gif: "image/gif",
|
|
38
|
+
ogg: "audio/ogg",
|
|
39
|
+
mp3: "audio/mpeg",
|
|
40
|
+
wav: "audio/wav",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface SirenoVitePluginTheme {
|
|
44
|
+
name: string
|
|
45
|
+
manifestPath: string
|
|
46
|
+
uiOverridesPath: string | null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface SirenoVitePluginAddon {
|
|
50
|
+
name: string
|
|
51
|
+
frontend?: { main: string; styles?: string[] }
|
|
52
|
+
buttons?: ReadonlyArray<{ type: string }>
|
|
53
|
+
buttonTypes?: Readonly<Record<string, string>>
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface SirenoVitePluginOptions {
|
|
57
|
+
token?: string
|
|
58
|
+
addons?: ReadonlyArray<SirenoVitePluginAddon>
|
|
59
|
+
theme?: SirenoVitePluginTheme
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const TOKEN_VIRTUAL_ID = "virtual:sireno/token"
|
|
63
|
+
const TOKEN_RESOLVED_ID = "\0virtual:sireno/token"
|
|
64
|
+
|
|
65
|
+
const ADDONS_VIRTUAL_ID = "virtual:sireno/addons"
|
|
66
|
+
const ADDONS_RESOLVED_ID = "\0virtual:sireno/addons"
|
|
67
|
+
|
|
68
|
+
const ADDONS_REGISTRY_VIRTUAL_ID = "virtual:sireno/addons/registry"
|
|
69
|
+
const ADDONS_REGISTRY_RESOLVED_ID = "\0virtual:sireno/addons/registry"
|
|
70
|
+
|
|
71
|
+
const THEME_VIRTUAL_ID = "virtual:sireno/theme.css"
|
|
72
|
+
const THEME_RESOLVED_ID = "\0virtual:sireno/theme.css"
|
|
73
|
+
|
|
74
|
+
const THEMES_MANIFEST_VIRTUAL_ID = "virtual:sireno/themes/manifest"
|
|
75
|
+
const THEMES_MANIFEST_RESOLVED_ID = "\0virtual:sireno/themes/manifest"
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Invalidate hooks registered by addon frontends. When a TSX file changes,
|
|
79
|
+
* the addon can register a cleanup that runs before HMR swaps the module,
|
|
80
|
+
* clearing intervals, animation frames, and event listeners that would
|
|
81
|
+
* otherwise fire on stale state.
|
|
82
|
+
*
|
|
83
|
+
* Addon code calls:
|
|
84
|
+
* `__SIRENO_ADDON_CLEANUP__(addonName, () => clearInterval(id))`
|
|
85
|
+
*
|
|
86
|
+
* The plugin collects these per addon and invokes them whenever the
|
|
87
|
+
* addon's main module is updated.
|
|
88
|
+
*/
|
|
89
|
+
const cleanupRegistry = new Map<string, Set<() => void>>()
|
|
90
|
+
|
|
91
|
+
export const __SIRENO_ADDON_CLEANUP__ = (
|
|
92
|
+
addonName: string,
|
|
93
|
+
cleanup: () => void,
|
|
94
|
+
): void => {
|
|
95
|
+
const set = cleanupRegistry.get(addonName) ?? new Set<() => void>()
|
|
96
|
+
set.add(cleanup)
|
|
97
|
+
cleanupRegistry.set(addonName, set)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export const __SIRENO_RUN_ADDON_CLEANUPS__ = (addonName: string): number => {
|
|
101
|
+
const set = cleanupRegistry.get(addonName)
|
|
102
|
+
if (set === undefined || set.size === 0) return 0
|
|
103
|
+
for (const cleanup of set) {
|
|
104
|
+
try {
|
|
105
|
+
cleanup()
|
|
106
|
+
} catch {
|
|
107
|
+
// cleanup may throw if the addon is in an intermediate state — ignore
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
set.clear()
|
|
111
|
+
return set.size
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const TOKEN_MODULE = (token: string): string =>
|
|
115
|
+
`export const token = ${JSON.stringify(token)};\n`
|
|
116
|
+
|
|
117
|
+
const sanitizeIdentifier = (name: string): string => {
|
|
118
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, "_")
|
|
119
|
+
return /^[a-zA-Z_$]/.test(sanitized) ? sanitized : `_${sanitized}`
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const buildAddonsImports = (
|
|
123
|
+
addons: ReadonlyArray<SirenoVitePluginAddon>,
|
|
124
|
+
): string => {
|
|
125
|
+
const lines: string[] = []
|
|
126
|
+
for (const addon of addons) {
|
|
127
|
+
if (addon.frontend === undefined) continue
|
|
128
|
+
lines.push(
|
|
129
|
+
`import * as ${sanitizeIdentifier(addon.name)}_frontend from ${JSON.stringify(addon.frontend.main)};`,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
lines.push(
|
|
133
|
+
`export const addons = ${JSON.stringify(
|
|
134
|
+
addons
|
|
135
|
+
.filter((a) => a.frontend !== undefined)
|
|
136
|
+
.map((a) => ({
|
|
137
|
+
name: a.name,
|
|
138
|
+
main: a.frontend!.main,
|
|
139
|
+
styles: a.frontend!.styles ?? [],
|
|
140
|
+
})),
|
|
141
|
+
)};`,
|
|
142
|
+
)
|
|
143
|
+
return lines.join("\n")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const buildAddonsRegistryModule = (
|
|
147
|
+
addons: ReadonlyArray<SirenoVitePluginAddon>,
|
|
148
|
+
): string => {
|
|
149
|
+
const lines: string[] = []
|
|
150
|
+
for (const addon of addons) {
|
|
151
|
+
if (addon.frontend === undefined) continue
|
|
152
|
+
if (addon.buttonTypes === undefined) continue
|
|
153
|
+
const modName = sanitizeIdentifier(addon.name) + "_manifest"
|
|
154
|
+
lines.push(
|
|
155
|
+
`import * as ${modName} from ${JSON.stringify(addon.frontend.main)};`,
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
lines.push("/* eslint-disable */")
|
|
159
|
+
for (const addon of addons) {
|
|
160
|
+
if (addon.frontend === undefined) continue
|
|
161
|
+
if (addon.buttonTypes === undefined) continue
|
|
162
|
+
const modName = sanitizeIdentifier(addon.name) + "_manifest"
|
|
163
|
+
lines.push(
|
|
164
|
+
`const ${modName}Obj = ${modName}.manifest ?? ${modName}.default;`,
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
lines.push("export const addonRegistry = {")
|
|
168
|
+
for (const addon of addons) {
|
|
169
|
+
if (addon.frontend === undefined) continue
|
|
170
|
+
if (addon.buttonTypes === undefined) continue
|
|
171
|
+
const modName = sanitizeIdentifier(addon.name) + "_manifest"
|
|
172
|
+
for (const [type, exportName] of Object.entries(addon.buttonTypes)) {
|
|
173
|
+
lines.push(
|
|
174
|
+
` ${JSON.stringify(type)}: { addonName: ${JSON.stringify(addon.name)}, Component: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.frontend, gestures: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.backend?.gestureHandlers },`,
|
|
175
|
+
)
|
|
176
|
+
if (type === `${addon.name}:${addon.name}`) {
|
|
177
|
+
lines.push(
|
|
178
|
+
` ${JSON.stringify(addon.name)}: { addonName: ${JSON.stringify(addon.name)}, Component: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.frontend, gestures: (${modName}Obj.buttonTypes[${JSON.stringify(type)}] ?? ${modName}[${JSON.stringify(exportName)}] ?? ${modName}.default?.buttonTypes?.[${JSON.stringify(type)}])?.backend?.gestureHandlers },`,
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
lines.push("};")
|
|
184
|
+
return lines.join("\n")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const readThemeCss = (themeDir: string | undefined): string => {
|
|
188
|
+
if (!themeDir) return ""
|
|
189
|
+
const cssPath = join(themeDir, ".sirenodeck", "theme.css")
|
|
190
|
+
try {
|
|
191
|
+
return readFileSync(cssPath, "utf8")
|
|
192
|
+
} catch {
|
|
193
|
+
return ""
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export const buildThemesManifestModule = (
|
|
198
|
+
theme: SirenoVitePluginTheme | undefined,
|
|
199
|
+
): string => {
|
|
200
|
+
if (!theme) {
|
|
201
|
+
return `export const activeTheme = null;\nexport const colorTokens = null;\nexport const typography = null;\n`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let manifestData = "{}"
|
|
205
|
+
try {
|
|
206
|
+
manifestData = readFileSync(theme.manifestPath, "utf8")
|
|
207
|
+
} catch {
|
|
208
|
+
// ignore — manifest may not exist in all setups
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const uiOverridesImport = theme.uiOverridesPath
|
|
212
|
+
? `import * as _uiOverrides from ${JSON.stringify(theme.uiOverridesPath)};`
|
|
213
|
+
: null
|
|
214
|
+
|
|
215
|
+
const uiOverridesBody =
|
|
216
|
+
theme.uiOverridesPath !== null
|
|
217
|
+
? [
|
|
218
|
+
`const _components = _uiOverrides?.components ?? {};`,
|
|
219
|
+
`const _surfaces = _uiOverrides?.surfaces ?? {};`,
|
|
220
|
+
`const _primitives = _uiOverrides?.primitives ?? {};`,
|
|
221
|
+
`export const components = _components;`,
|
|
222
|
+
`export const surfaces = _surfaces;`,
|
|
223
|
+
`export const primitives = _primitives;`,
|
|
224
|
+
`export { _uiOverrides as uiOverrides };`,
|
|
225
|
+
].join("\n")
|
|
226
|
+
: [
|
|
227
|
+
`export const components = null;`,
|
|
228
|
+
`export const surfaces = null;`,
|
|
229
|
+
`export const primitives = null;`,
|
|
230
|
+
`export const uiOverrides = null;`,
|
|
231
|
+
].join("\n")
|
|
232
|
+
|
|
233
|
+
return [
|
|
234
|
+
uiOverridesImport,
|
|
235
|
+
`const _manifest = JSON.parse(${JSON.stringify(manifestData)})`,
|
|
236
|
+
`export const activeTheme = { name: ${JSON.stringify(theme.name)}, manifestPath: ${JSON.stringify(theme.manifestPath)}, uiOverridesPath: ${JSON.stringify(theme.uiOverridesPath)} };`,
|
|
237
|
+
`export const colorTokens = _manifest.colorTokens ?? null;`,
|
|
238
|
+
`export const typography = _manifest.typography ?? null;`,
|
|
239
|
+
uiOverridesBody,
|
|
240
|
+
]
|
|
241
|
+
.filter(Boolean)
|
|
242
|
+
.join("\n")
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Node-only modules that can appear in third-party addon SOURCE graphs
|
|
247
|
+
* served to the browser (the run pipeline hands vite each addon's
|
|
248
|
+
* `src/index.ts` when it exists — see browserFrontendMain in run.ts).
|
|
249
|
+
* Nothing on the render path invokes these; an inert stub keeps vite's
|
|
250
|
+
* transform and ESM link checks happy.
|
|
251
|
+
*
|
|
252
|
+
* Named exports must cover every named import across all addon sources
|
|
253
|
+
* (ESM link-time check fails on missing names). If a browser console
|
|
254
|
+
* reports "does not provide an export named 'X'", add X here.
|
|
255
|
+
*/
|
|
256
|
+
const BROWSER_NODE_STUBS: ReadonlyArray<{
|
|
257
|
+
specifier: string
|
|
258
|
+
hasDefault: boolean
|
|
259
|
+
names: ReadonlyArray<string>
|
|
260
|
+
}> = [
|
|
261
|
+
{ specifier: "chokidar", hasDefault: true, names: [] },
|
|
262
|
+
{ specifier: "execa", hasDefault: false, names: ["execa"] },
|
|
263
|
+
{
|
|
264
|
+
specifier: "@opencode-ai/sdk",
|
|
265
|
+
hasDefault: false,
|
|
266
|
+
names: ["createOpencodeClient"],
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
specifier: "node:fs/promises",
|
|
270
|
+
hasDefault: false,
|
|
271
|
+
names: ["readFile", "readdir", "stat"],
|
|
272
|
+
},
|
|
273
|
+
{ specifier: "node:path", hasDefault: false, names: ["join"] },
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
const nodeStubSource = ({
|
|
277
|
+
hasDefault,
|
|
278
|
+
names,
|
|
279
|
+
}: {
|
|
280
|
+
hasDefault: boolean
|
|
281
|
+
names: ReadonlyArray<string>
|
|
282
|
+
}): string => {
|
|
283
|
+
const lines = [
|
|
284
|
+
"// Generated by the sirenoDeck2 vite plugin — inert browser stub for a",
|
|
285
|
+
"// Node-only dependency imported by third-party addon source graphs.",
|
|
286
|
+
"// Never call these: they exist only so module evaluation succeeds.",
|
|
287
|
+
"const noop = () => {}",
|
|
288
|
+
"const proxy = new Proxy(noop, { get: () => noop })",
|
|
289
|
+
]
|
|
290
|
+
if (hasDefault) lines.push("export default noop")
|
|
291
|
+
for (const name of names) lines.push(`export const ${name} = noop`)
|
|
292
|
+
return `${lines.join("\n")}\n`
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const writeNodeDepStubs = (dir: string): void => {
|
|
296
|
+
const stubDir = join(dir, "node-dep-stubs")
|
|
297
|
+
if (!existsSync(stubDir)) mkdirSync(stubDir, { recursive: true })
|
|
298
|
+
for (const stub of BROWSER_NODE_STUBS) {
|
|
299
|
+
const fileName = `${stub.specifier.replace(/[^a-zA-Z0-9]/g, "-")}.js`
|
|
300
|
+
writeFileSync(join(stubDir, fileName), nodeStubSource(stub), "utf8")
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export const sirenoDeck2 = (options: SirenoVitePluginOptions = {}): Plugin => {
|
|
305
|
+
const token = options.token ?? ""
|
|
306
|
+
const addons = options.addons ?? []
|
|
307
|
+
const theme = options.theme
|
|
308
|
+
const themeDir = process.env["SIRENO_THEME_DIR"]
|
|
309
|
+
let themeCss = readThemeCss(themeDir)
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
name: "sirenodeck",
|
|
313
|
+
resolveId: (id) => {
|
|
314
|
+
if (id === TOKEN_VIRTUAL_ID) return TOKEN_RESOLVED_ID
|
|
315
|
+
if (id === ADDONS_VIRTUAL_ID) return ADDONS_RESOLVED_ID
|
|
316
|
+
if (id === ADDONS_REGISTRY_VIRTUAL_ID) return ADDONS_REGISTRY_RESOLVED_ID
|
|
317
|
+
if (id === THEME_VIRTUAL_ID) return THEME_RESOLVED_ID
|
|
318
|
+
if (id === THEMES_MANIFEST_VIRTUAL_ID) return THEMES_MANIFEST_RESOLVED_ID
|
|
319
|
+
return null
|
|
320
|
+
},
|
|
321
|
+
load: (id) => {
|
|
322
|
+
if (id === TOKEN_RESOLVED_ID) return TOKEN_MODULE(token)
|
|
323
|
+
if (id === ADDONS_RESOLVED_ID) return buildAddonsImports(addons)
|
|
324
|
+
if (id === ADDONS_REGISTRY_RESOLVED_ID)
|
|
325
|
+
return buildAddonsRegistryModule(addons)
|
|
326
|
+
if (id === THEME_RESOLVED_ID) return readThemeCss(themeDir)
|
|
327
|
+
if (id === THEMES_MANIFEST_RESOLVED_ID)
|
|
328
|
+
return buildThemesManifestModule(theme)
|
|
329
|
+
return null
|
|
330
|
+
},
|
|
331
|
+
configResolved: (config) => {
|
|
332
|
+
const root = config.root ?? process.cwd()
|
|
333
|
+
const dir = join(root, ".sirenodeck")
|
|
334
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
335
|
+
writeNodeDepStubs(dir)
|
|
336
|
+
if (themeCss.length === 0) return
|
|
337
|
+
const filePath = join(dir, "theme.css")
|
|
338
|
+
// Sireno-deck serves a local Stream Deck UI over loopback — every
|
|
339
|
+
// byte ships through localhost, so Tailwind's "purge unused
|
|
340
|
+
// utilities" optimization costs nothing to undo. Scan the entire
|
|
341
|
+
// CLI package (frontend, emulator, shared ui, builtin addons)
|
|
342
|
+
// plus each third-party addon's frontend directory so addon
|
|
343
|
+
// authors can use any utility class without coordinating with
|
|
344
|
+
// the Vite plugin.
|
|
345
|
+
const cliPackage = join(root, "..")
|
|
346
|
+
const sourceLines = [`@source "${cliPackage}/**/*.{ts,tsx}";\n`]
|
|
347
|
+
const seenFrontendDirs = new Set<string>()
|
|
348
|
+
for (const addon of addons) {
|
|
349
|
+
const mainPath = addon.frontend?.main
|
|
350
|
+
if (typeof mainPath !== "string" || mainPath.length === 0) continue
|
|
351
|
+
const frontendDir = dirname(mainPath)
|
|
352
|
+
if (seenFrontendDirs.has(frontendDir)) continue
|
|
353
|
+
if (frontendDir.startsWith(cliPackage)) continue
|
|
354
|
+
seenFrontendDirs.add(frontendDir)
|
|
355
|
+
sourceLines.push(`@source "${frontendDir}/**/*.{ts,tsx}";\n`)
|
|
356
|
+
}
|
|
357
|
+
const sourceDirective = sourceLines.join("")
|
|
358
|
+
writeFileSync(filePath, sourceDirective + themeCss, "utf8")
|
|
359
|
+
},
|
|
360
|
+
config: (config) => {
|
|
361
|
+
// ponytail: never spread the incoming config — it carries `esbuild`
|
|
362
|
+
// and `optimizeDeps.esbuildOptions` from earlier plugins
|
|
363
|
+
// (`@vitejs/plugin-react`'s vite:react-babel hook). Re-emitting them
|
|
364
|
+
// via our `config` return makes Vite attribute the deprecation
|
|
365
|
+
// warnings to this plugin.
|
|
366
|
+
const root = config.root ?? process.cwd()
|
|
367
|
+
const dir = join(root, ".sirenodeck")
|
|
368
|
+
const alias = (config.resolve?.alias ?? []) as Array<{
|
|
369
|
+
find: string | RegExp
|
|
370
|
+
replacement: string
|
|
371
|
+
}>
|
|
372
|
+
const extra: Array<{ find: string | RegExp; replacement: string }> = [
|
|
373
|
+
...BROWSER_NODE_STUBS.map(({ specifier }) => ({
|
|
374
|
+
find: new RegExp(
|
|
375
|
+
`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`,
|
|
376
|
+
),
|
|
377
|
+
replacement: join(
|
|
378
|
+
dir,
|
|
379
|
+
"node-dep-stubs",
|
|
380
|
+
`${specifier.replace(/[^a-zA-Z0-9]/g, "-")}.js`,
|
|
381
|
+
),
|
|
382
|
+
})),
|
|
383
|
+
]
|
|
384
|
+
if (themeCss.length > 0) {
|
|
385
|
+
extra.push({
|
|
386
|
+
find: /^sirenodeck-theme$/,
|
|
387
|
+
replacement: join(dir, "theme.css"),
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
return {
|
|
391
|
+
resolve: {
|
|
392
|
+
...config.resolve,
|
|
393
|
+
alias: [...alias, ...extra],
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
configureServer: (server: ViteDevServer) => {
|
|
398
|
+
const signalReady = (): void => {
|
|
399
|
+
const addr = server.httpServer?.address()
|
|
400
|
+
const port = typeof addr === "object" && addr !== null ? addr.port : 0
|
|
401
|
+
process.stdout.write(`READY ${port}\n`)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// ponytail: --remote gates HTTP access with the daemon's token. The
|
|
405
|
+
// emulator/frontend vite is bound to 0.0.0.0 so any host on the LAN
|
|
406
|
+
// can reach it; without this gate, anyone with the URL could load
|
|
407
|
+
// the bundle and (via the bundle's baked token) replay commands on
|
|
408
|
+
// the WS bridge. Accept the token via `?token=` query OR a
|
|
409
|
+
// `sireno-token` cookie (the HTML page parses the URL token and sets
|
|
410
|
+
// the cookie on first load so subsequent module/HMR requests stay
|
|
411
|
+
// authenticated). Vite's HMR webSocket is exempt — it's a debug-only
|
|
412
|
+
// surface and tying it to token would break dev refresh.
|
|
413
|
+
const requiredToken = process.env["SIRENO_REQUIRE_TOKEN"]
|
|
414
|
+
if (requiredToken !== undefined && requiredToken.length > 0) {
|
|
415
|
+
const checkToken = (req: {
|
|
416
|
+
url?: string
|
|
417
|
+
headers?: Record<string, string | string[] | undefined>
|
|
418
|
+
}): boolean => {
|
|
419
|
+
const url = req.url ?? ""
|
|
420
|
+
if (url.startsWith("/@vite/") || url.startsWith("/__vite_ping")) {
|
|
421
|
+
return true
|
|
422
|
+
}
|
|
423
|
+
const queryIdx = url.indexOf("?")
|
|
424
|
+
if (queryIdx !== -1) {
|
|
425
|
+
const params = new URLSearchParams(url.slice(queryIdx + 1))
|
|
426
|
+
if (params.get("token") === requiredToken) return true
|
|
427
|
+
}
|
|
428
|
+
const cookieHeader = req.headers?.["cookie"]
|
|
429
|
+
if (typeof cookieHeader === "string") {
|
|
430
|
+
for (const part of cookieHeader.split(";")) {
|
|
431
|
+
const [rawName, ...rest] = part.trim().split("=")
|
|
432
|
+
if (
|
|
433
|
+
rawName === "sireno-token" &&
|
|
434
|
+
rest.join("=") === requiredToken
|
|
435
|
+
) {
|
|
436
|
+
return true
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return false
|
|
441
|
+
}
|
|
442
|
+
server.middlewares.use((req, res, next) => {
|
|
443
|
+
if (checkToken(req)) {
|
|
444
|
+
next()
|
|
445
|
+
return
|
|
446
|
+
}
|
|
447
|
+
res.statusCode = 403
|
|
448
|
+
res.end("token required")
|
|
449
|
+
})
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const configPath = process.env["SIRENO_CONFIG_PATH"]
|
|
453
|
+
if (configPath !== undefined && configPath.length > 0) {
|
|
454
|
+
server.middlewares.use((req, res, next) => {
|
|
455
|
+
const url = req.url?.split("?", 1)[0]
|
|
456
|
+
if (url === "/api/config-path") {
|
|
457
|
+
const body = JSON.stringify({ path: configPath })
|
|
458
|
+
res.writeHead(200, {
|
|
459
|
+
"content-type": "application/json; charset=utf-8",
|
|
460
|
+
"content-length": Buffer.byteLength(body),
|
|
461
|
+
})
|
|
462
|
+
res.end(body)
|
|
463
|
+
return
|
|
464
|
+
}
|
|
465
|
+
if (url !== "/api/config" && url !== "/api/config/") {
|
|
466
|
+
next()
|
|
467
|
+
return
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
const text = readFileSync(configPath, "utf8")
|
|
471
|
+
res.writeHead(200, {
|
|
472
|
+
"content-type": "text/plain; charset=utf-8",
|
|
473
|
+
"content-length": Buffer.byteLength(text),
|
|
474
|
+
})
|
|
475
|
+
res.end(text)
|
|
476
|
+
} catch {
|
|
477
|
+
res.writeHead(404, { "content-type": "text/plain; charset=utf-8" })
|
|
478
|
+
res.end("Config not available")
|
|
479
|
+
}
|
|
480
|
+
})
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
server.httpServer?.once("listening", () => {
|
|
484
|
+
// Wait for vite's dep optimizer to finish so the first browser
|
|
485
|
+
// request doesn't hit a 504 "Outdated Optimize Dep" while deps
|
|
486
|
+
// are still being pre-bundled.
|
|
487
|
+
const warmup = async (): Promise<void> => {
|
|
488
|
+
try {
|
|
489
|
+
await server.warmupRequest("/index.html")
|
|
490
|
+
} catch {
|
|
491
|
+
// ignore warmup failures
|
|
492
|
+
}
|
|
493
|
+
try {
|
|
494
|
+
await server.transformRequest("/src/main.tsx")
|
|
495
|
+
} catch {
|
|
496
|
+
// ignore transform failures
|
|
497
|
+
}
|
|
498
|
+
signalReady()
|
|
499
|
+
}
|
|
500
|
+
void warmup()
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
// ponytail: SPA <img src> cannot fetch `addon://` schemes. Serve
|
|
504
|
+
// addon assets over HTTP at /@sireno/addon-asset/<name>/<subPath>
|
|
505
|
+
// (see resolveAddonAssetSrc in src/api/asset-url.ts). Scoped to
|
|
506
|
+
// configured addons, path-traversal safe.
|
|
507
|
+
const assetDirs = new Map<string, string>()
|
|
508
|
+
for (const addon of addons) {
|
|
509
|
+
if (addon.frontend?.main === undefined) continue
|
|
510
|
+
const base = addonAssetBaseDir(addon.frontend.main)
|
|
511
|
+
if (base !== null) assetDirs.set(addon.name, base)
|
|
512
|
+
}
|
|
513
|
+
if (assetDirs.size > 0) {
|
|
514
|
+
server.middlewares.use((req, res, next) => {
|
|
515
|
+
const url = req.url ?? ""
|
|
516
|
+
const m = /^\/@sireno\/addon-asset\/([^/]+)\/(.+)$/.exec(
|
|
517
|
+
url.split("?")[0] ?? "",
|
|
518
|
+
)
|
|
519
|
+
if (m === null) {
|
|
520
|
+
next()
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
const [, name, subPathRaw] = m as unknown as [string, string, string]
|
|
524
|
+
const base = assetDirs.get(name)
|
|
525
|
+
if (base === undefined) {
|
|
526
|
+
res.statusCode = 404
|
|
527
|
+
res.end("unknown addon")
|
|
528
|
+
return
|
|
529
|
+
}
|
|
530
|
+
const safe = subPathRaw.replace(/^\/+/, "")
|
|
531
|
+
if (safe.includes("..") || safe.length === 0) {
|
|
532
|
+
res.statusCode = 400
|
|
533
|
+
res.end("bad path")
|
|
534
|
+
return
|
|
535
|
+
}
|
|
536
|
+
const filePath = join(base, safe)
|
|
537
|
+
if (!filePath.startsWith(base)) {
|
|
538
|
+
res.statusCode = 400
|
|
539
|
+
res.end("bad path")
|
|
540
|
+
return
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const body = readFileSync(filePath)
|
|
544
|
+
const ext = filePath.split(".").pop()?.toLowerCase() ?? ""
|
|
545
|
+
res.setHeader(
|
|
546
|
+
"Content-Type",
|
|
547
|
+
ASSET_MIME[ext] ?? "application/octet-stream",
|
|
548
|
+
)
|
|
549
|
+
res.setHeader("Cache-Control", "no-cache")
|
|
550
|
+
res.end(body)
|
|
551
|
+
} catch {
|
|
552
|
+
res.statusCode = 404
|
|
553
|
+
res.end("asset not found")
|
|
554
|
+
}
|
|
555
|
+
})
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Theme.css lives on disk and the daemon re-stages it when the
|
|
559
|
+
// active theme changes. Watch the staged file and reload the
|
|
560
|
+
// THEME_VIRTUAL_ID module so the browser gets the new CSS via
|
|
561
|
+
// vite's HMR without a full reload.
|
|
562
|
+
if (themeDir) {
|
|
563
|
+
const stagedCss = join(themeDir, ".sirenodeck", "theme.css")
|
|
564
|
+
if (existsSync(stagedCss)) {
|
|
565
|
+
server.watcher.add(stagedCss)
|
|
566
|
+
server.watcher.on("change", (file) => {
|
|
567
|
+
if (file !== stagedCss) return
|
|
568
|
+
themeCss = readThemeCss(themeDir)
|
|
569
|
+
const mod = server.moduleGraph.getModuleById(THEME_RESOLVED_ID)
|
|
570
|
+
if (mod) server.reloadModule(mod)
|
|
571
|
+
})
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
// ponytail: --remote requires a way to authenticate subsequent module
|
|
576
|
+
// requests after the HTML loads. Inject a small script that parses
|
|
577
|
+
// `?token=X` from the URL and stores it in `document.cookie` so vite's
|
|
578
|
+
// middleware accepts the cascade of `/src/...` and asset requests.
|
|
579
|
+
transformIndexHtml: {
|
|
580
|
+
order: "pre",
|
|
581
|
+
handler: (html, ctx) => {
|
|
582
|
+
// ponytail: third-party addon SOURCE served to the browser may
|
|
583
|
+
// reference Node globals (e.g. `process.env["HOME"]` in the
|
|
584
|
+
// coding-agents manifest probe). Vite doesn't polyfill `process`
|
|
585
|
+
// in dev; inject a minimal shim before any module executes so
|
|
586
|
+
// evaluation succeeds — values are irrelevant on the render path.
|
|
587
|
+
const processShim = `<script>window.process=window.process||{env:{}};</script>`
|
|
588
|
+
let injected = html.replace("<head>", `<head>${processShim}`)
|
|
589
|
+
if (process.env["SIRENO_REQUIRE_TOKEN"] === undefined) return injected
|
|
590
|
+
const script =
|
|
591
|
+
`<script>(function(){try{var p=new URLSearchParams(location.search);` +
|
|
592
|
+
`var t=p.get("token");if(t){document.cookie="sireno-token="+` +
|
|
593
|
+
`encodeURIComponent(t)+"; path=/; SameSite=Lax";` +
|
|
594
|
+
`p.delete("token");var q=p.toString();` +
|
|
595
|
+
`var u=location.pathname+(q?"?"+q:"")+location.hash;` +
|
|
596
|
+
`history.replaceState(null,"",u);}}catch(e){}})();</script>`
|
|
597
|
+
return injected.replace("<head>", `<head>${script}`)
|
|
598
|
+
},
|
|
599
|
+
},
|
|
600
|
+
handleHotUpdate: ({ file, server }) => {
|
|
601
|
+
// When an addon frontend file changes, run any registered cleanup
|
|
602
|
+
// hooks BEFORE Vite swaps in the new module. This lets setInterval,
|
|
603
|
+
// requestAnimationFrame, and event listeners be cleared in time to
|
|
604
|
+
// prevent the old instance from calling state setters on the new
|
|
605
|
+
// (or unmounted) React tree.
|
|
606
|
+
let totalCleanups = 0
|
|
607
|
+
for (const addon of addons) {
|
|
608
|
+
if (addon.frontend === undefined) continue
|
|
609
|
+
const frontendPath = addon.frontend.main
|
|
610
|
+
const normalizedFile = file.replace(/\\/g, "/")
|
|
611
|
+
const normalizedFrontend = frontendPath.replace(/\\/g, "/")
|
|
612
|
+
if (
|
|
613
|
+
!normalizedFile.endsWith(normalizedFrontend) &&
|
|
614
|
+
!normalizedFile.includes(normalizedFrontend)
|
|
615
|
+
)
|
|
616
|
+
continue
|
|
617
|
+
const cleared = __SIRENO_RUN_ADDON_CLEANUPS__(addon.name)
|
|
618
|
+
if (cleared > 0) {
|
|
619
|
+
server.config.logger.info(
|
|
620
|
+
`[sirenodeck] cleared ${cleared} cleanup(s) for addon "${addon.name}" before HMR of ${normalizedFile}`,
|
|
621
|
+
)
|
|
622
|
+
}
|
|
623
|
+
totalCleanups += cleared
|
|
624
|
+
}
|
|
625
|
+
// Always return undefined so Vite handles the HMR normally; the
|
|
626
|
+
// cleanups are side effects, not module replacements.
|
|
627
|
+
void totalCleanups
|
|
628
|
+
return undefined
|
|
629
|
+
},
|
|
630
|
+
}
|
|
631
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|