feat: externalize buttplug as separate nginx container
- Add Dockerfile.buttplug: builds Rust/WASM + TS, serves via nginx - Add nginx.buttplug.conf: serves /dist and /wasm with correct MIME types - Add .gitea/workflows/docker-build-buttplug.yml: path-filtered CI workflow - Strip Rust toolchain and buttplug build from frontend Dockerfile - Move buttplug to devDependencies (types only at build time) - Remove vite-plugin-wasm from frontend (WASM now served by nginx) - Add /buttplug proxy in vite.config (dev: localhost:8080) - Add buttplug service to compose.yml - Load buttplug dynamically in play page via runtime import - Fix faq page: suppress no-unnecessary-state-wrap for reassigned SvelteSet Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
import Meta from "$lib/components/meta/meta.svelte";
|
||||
|
||||
let searchQuery = $state("");
|
||||
let expandedItems = new SvelteSet<number>();
|
||||
// eslint-disable-next-line svelte/no-unnecessary-state-wrap -- variable is reassigned, $state is required
|
||||
let expandedItems = $state(new SvelteSet<number>());
|
||||
|
||||
const faqCategories = [
|
||||
{
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { _ } from "svelte-i18n";
|
||||
import Meta from "$lib/components/meta/meta.svelte";
|
||||
import {
|
||||
ButtplugClient,
|
||||
ButtplugWasmClientConnector,
|
||||
type ButtplugClientDevice,
|
||||
type OutputType,
|
||||
InputType,
|
||||
DeviceOutputValueConstructor,
|
||||
} from "@sexy.pivoine.art/buttplug";
|
||||
import type * as ButtplugTypes from "@sexy.pivoine.art/buttplug";
|
||||
import Button from "$lib/components/ui/button/button.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
@@ -19,8 +12,13 @@
|
||||
import { toast } from "svelte-sonner";
|
||||
import SexyBackground from "$lib/components/background/background.svelte";
|
||||
|
||||
const client = new ButtplugClient("Sexy.Art");
|
||||
let connected = $state(client.connected);
|
||||
// Runtime buttplug values — loaded dynamically from the buttplug nginx container
|
||||
let client: ButtplugTypes.ButtplugClient;
|
||||
let InputType: typeof ButtplugTypes.InputType;
|
||||
let DeviceOutputValueConstructor: typeof ButtplugTypes.DeviceOutputValueConstructor;
|
||||
let ButtplugWasmClientConnector: typeof ButtplugTypes.ButtplugWasmClientConnector;
|
||||
|
||||
let connected = $state(false);
|
||||
let scanning = $state(false);
|
||||
let devices = $state<BluetoothDevice[]>([]);
|
||||
|
||||
@@ -45,7 +43,7 @@
|
||||
// await ButtplugWasmClientConnector.activateLogging("info");
|
||||
await client.connect(connector);
|
||||
client.on("deviceadded", onDeviceAdded);
|
||||
client.on("deviceremoved", (dev: ButtplugClientDevice) => {
|
||||
client.on("deviceremoved", (dev: ButtplugTypes.ButtplugClientDevice) => {
|
||||
const idx = devices.findIndex((d) => d.info.index === dev.index);
|
||||
if (idx !== -1) devices.splice(idx, 1);
|
||||
});
|
||||
@@ -59,7 +57,7 @@
|
||||
scanning = true;
|
||||
}
|
||||
|
||||
async function onDeviceAdded(dev: ButtplugClientDevice) {
|
||||
async function onDeviceAdded(dev: ButtplugTypes.ButtplugClientDevice) {
|
||||
const device = convertDevice(dev);
|
||||
devices.push(device);
|
||||
|
||||
@@ -93,7 +91,7 @@
|
||||
if (!feature) return;
|
||||
|
||||
actuator.value = value;
|
||||
const outputType = actuator.outputType as typeof OutputType;
|
||||
const outputType = actuator.outputType as typeof ButtplugTypes.OutputType;
|
||||
await feature.runOutput(new DeviceOutputValueConstructor(outputType).steps(value));
|
||||
|
||||
// Capture event if recording
|
||||
@@ -141,7 +139,7 @@
|
||||
device.actuators.forEach((a) => (a.value = 0));
|
||||
}
|
||||
|
||||
function convertDevice(device: ButtplugClientDevice): BluetoothDevice {
|
||||
function convertDevice(device: ButtplugTypes.ButtplugClientDevice): BluetoothDevice {
|
||||
const actuators: import("$lib/types").DeviceActuator[] = []; // eslint-disable-line @typescript-eslint/consistent-type-imports
|
||||
for (const [, feature] of device.features) {
|
||||
for (const outputType of feature.outputTypes) {
|
||||
@@ -333,7 +331,7 @@
|
||||
// Send command to device via feature
|
||||
const feature = device.info.features.get(actuator.featureIndex);
|
||||
if (feature) {
|
||||
const outputType = actuator.outputType as typeof OutputType;
|
||||
const outputType = actuator.outputType as typeof ButtplugTypes.OutputType;
|
||||
feature.runOutput(new DeviceOutputValueConstructor(outputType).steps(deviceValue));
|
||||
}
|
||||
|
||||
@@ -365,12 +363,20 @@
|
||||
|
||||
const { data } = $props();
|
||||
|
||||
onMount(() => {
|
||||
if (data.authStatus.authenticated) {
|
||||
init();
|
||||
onMount(async () => {
|
||||
if (!data.authStatus.authenticated) {
|
||||
goto("/login");
|
||||
return;
|
||||
}
|
||||
goto("/login");
|
||||
// Concatenation prevents Rollup from statically resolving this URL at build time
|
||||
const buttplugUrl = "/buttplug/" + "dist/index.js";
|
||||
const bp = await import(/* @vite-ignore */ buttplugUrl);
|
||||
InputType = bp.InputType;
|
||||
DeviceOutputValueConstructor = bp.DeviceOutputValueConstructor;
|
||||
ButtplugWasmClientConnector = bp.ButtplugWasmClientConnector;
|
||||
client = new bp.ButtplugClient("Sexy.Art");
|
||||
connected = client.connected;
|
||||
await init();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user