KingOppai

App.svelte

Feb 12th, 2026
121
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.93 KB | None | 0 0
  1. <script lang="ts">
  2. import { onMount } from "svelte";
  3. import { emit, listen } from "@tauri-apps/api/event";
  4. import { getVersion } from "@tauri-apps/api/app";
  5. import { initSdvxLayout, sdvxKeyMap } from './lib/sdvx-layout';
  6.  
  7. import Link from "./Link.svelte";
  8. import Preview from "./Preview.svelte";
  9.  
  10. let deviceMode = "none";
  11. let outputMode = "none";
  12. let ledMode = "none";
  13.  
  14. let disableAirStrings = false;
  15. let divaSerialPort = "COM1";
  16. let divaBrightness = 63;
  17. let brokenithmPort = 1606;
  18. let keyboardSensitivity = 20;
  19. let keyboardDirectInput = false;
  20. let outputPolling = "100";
  21. let outputWebsocketUrl = "http://localhost:3000";
  22. let ledFaster = false;
  23. let ledColorActive = "#ff00ff";
  24. let ledColorInactive = "#ffff00";
  25. let ledColorAirActive = "#0086ed";
  26. let ledColorAirInactive = "#000000";
  27. let ledSensitivity = 20;
  28. let ledWebsocketUrl = "http://localhost:3001";
  29. let ledUmgrWebsocketPort = 7124;
  30. let ledSerialPort = "COM5";
  31.  
  32. let dirty = false;
  33.  
  34. function markDirty() {
  35. dirty = true;
  36. }
  37.  
  38. // let debugstr = "";
  39. let versionString = "";
  40. let ips: Array<string> = [];
  41. let polling = null;
  42. let tick = 0;
  43. let previewData = Array(131).fill(0);
  44. let timerData = "";
  45.  
  46. function updatePolling(enabled) {
  47. if (!!polling) {
  48. clearInterval(polling);
  49. polling = null;
  50. }
  51. if (enabled) {
  52. polling = setInterval(async () => {
  53. tick += 1;
  54. await emit("queryState", "");
  55. }, 50);
  56. }
  57. // console.log(enabled, polling, tick);
  58. }
  59.  
  60. // Receive events
  61. onMount(async () => {
  62. document.addEventListener("contextmenu", (event) => event.preventDefault());
  63.  
  64. // console.log(emit, listen);
  65. await listen("showConfig", (event) => {
  66. const payload: any = JSON.parse(event.payload as any);
  67. deviceMode = payload.deviceMode || "none";
  68. outputMode = payload.outputMode || "none";
  69. ledMode = payload.ledMode || "none";
  70.  
  71. disableAirStrings = payload.disableAirStrings || false;
  72. divaSerialPort = payload.divaSerialPort || "COM1";
  73. divaBrightness = payload.divaBrightness || 63;
  74. brokenithmPort = payload.brokenithmPort || 1606;
  75. keyboardSensitivity = payload.keyboardSensitivity || 20;
  76. keyboardDirectInput = payload.keyboardDirectInput || false;
  77. outputPolling = payload.outputPolling || "100";
  78. outputWebsocketUrl =
  79. payload.outputWebsocketUrl || "http://localhost:3000/";
  80. ledFaster = payload.ledFaster || false;
  81. ledColorActive = payload.ledColorActive || "#ff00ff";
  82. ledColorInactive = payload.ledColorInactive || "#ffff00";
  83. ledColorAirActive = payload.ledColorAirActive || "#0086ed";
  84. ledColorAirInactive = payload.ledColorAirInactive || "#000000";
  85. ledSensitivity = payload.ledSensitivity || 20;
  86. ledWebsocketUrl = payload.ledWebsocketUrl || "http://localhost:3001";
  87. ledUmgrWebsocketPort = payload.ledUmgrWebsocketPort || 7124;
  88. ledSerialPort = payload.ledSerialPort || "COM5";
  89. });
  90.  
  91. await listen("showState", (event) => {
  92. previewData = event.payload as any;
  93. });
  94. await listen("showTimerState", (event) => {
  95. timerData = event.payload as string;
  96. });
  97.  
  98. await listen("listIps", (event) => {
  99. ips = (event.payload as Array<string>).filter(
  100. (x) => x.split(".").length == 4
  101. );
  102. });
  103.  
  104. await emit("ready", "");
  105.  
  106. updatePolling(true);
  107. await listen("ackShow", (event) => {
  108. console.log("ackShow");
  109. updatePolling(true);
  110. });
  111. await listen("ackHide", (event) => {
  112. console.log("ackHide");
  113. updatePolling(false);
  114. });
  115.  
  116. versionString = ` ${await getVersion()}`;
  117. });
  118.  
  119. // Emit events
  120.  
  121. async function setConfig() {
  122. console.log("Updating config");
  123. console.log(disableAirStrings);
  124. await emit(
  125. "setConfig",
  126. JSON.stringify({
  127. deviceMode,
  128. outputMode,
  129. ledMode,
  130. disableAirStrings,
  131. divaSerialPort,
  132. divaBrightness,
  133. brokenithmPort,
  134. keyboardSensitivity,
  135. keyboardDirectInput,
  136. outputPolling,
  137. outputWebsocketUrl,
  138. ledFaster,
  139. ledColorActive,
  140. ledColorInactive,
  141. ledColorAirActive,
  142. ledColorAirInactive,
  143. ledSensitivity,
  144. ledWebsocketUrl,
  145. ledUmgrWebsocketPort,
  146. ledSerialPort,
  147. })
  148. );
  149. dirty = false;
  150. console.log("Done");
  151. }
  152.  
  153. async function hide() {
  154. await emit("hide", "");
  155. }
  156.  
  157. async function quit() {
  158. await emit("quit", "");
  159. }
  160.  
  161. async function logs() {
  162. await emit("openLogfile", "");
  163. }
  164.  
  165. async function brokenithmQr() {
  166. await emit("openBrokenithmQr");
  167. }
  168.  
  169. async function repo() {
  170. await emit("openRepo");
  171. }
  172. </script>
  173.  
  174. <div class="titlebar">
  175. <div class="header-icon">
  176. <img src="/icon.png" alt="logo" />
  177. </div>
  178. <div class="header">
  179. &nbsp;slidershim{versionString}
  180. </div>
  181. <div class="header-space" />
  182. <div class="header-timer">
  183. {timerData}
  184. </div>
  185. </div>
  186. <div data-tauri-drag-region class="titlebar titlebar-front" />
  187. <main class="main">
  188. <div class="preview-row">
  189. <Preview data={previewData} />
  190. </div>
  191. <div class="options">
  192. <div class="row">
  193. <div class="label">Input Device</div>
  194. <div class="input">
  195. <select bind:value={deviceMode} on:change={markDirty}>
  196. <option value="none">None</option>
  197. <option value="tasoller-one">GAMO2 Tasoller, 1.0 HID Firmware</option>
  198. <option value="tasoller-two">GAMO2 Tasoller, 2.0 HID Firmware</option>
  199. <option value="yuancon">Yuancon Laverita, HID Firmware</option>
  200. <option value="yuancon-three">Yuancon Laverita v3, HID Firmware</option>
  201. <option value="yubideck">大四 / Yubideck, HID Firmware 1.0</option>
  202. <option value="yubideck-three">大四 / Yubideck, HID Firmware 3.0</option>
  203. <option value="diva">Slider over Serial</option>
  204. <option value="brokenithm">Brokenithm</option>
  205. <option value="brokenithm-led">Brokenithm + Led</option>
  206. <option value="brokenithm-nostalgia">Brokestalgia (28k)</option>
  207. </select>
  208. </div>
  209. </div>
  210. {#if deviceMode.slice(0, 8) === "tasoller" || deviceMode.slice(0, 7) === "yuancon" || deviceMode.slice(0, 8) === "yubideck" || (deviceMode.slice(0, 10) === "brokenithm" && deviceMode !== "brokenithm-nostalgia")}
  211. <div class="row">
  212. <div class="label" />
  213. <div class="input">
  214. <span>
  215. <input
  216. type="checkbox"
  217. id="disable-air"
  218. style="width: unset;"
  219. bind:checked={disableAirStrings}
  220. on:change={markDirty}
  221. />
  222. <label for="disable-air">Disable Air Strings</label>
  223. </span>
  224. </div>
  225. </div>
  226. {/if}
  227. {#if deviceMode.slice(0, 10) === "brokenithm"}
  228. <div class="row">
  229. <div class="label">Brokenithm Port</div>
  230. <div class="input">
  231. <input
  232. type="number"
  233. min="1024"
  234. max="65535"
  235. step="1"
  236. bind:value={brokenithmPort}
  237. on:change={markDirty}
  238. />
  239. </div>
  240. </div>
  241. <div class="row">
  242. <div class="label" />
  243. <div class="input">
  244. <div class="serverlist">
  245. Brokenithm will be running at one of:
  246. <div class="iplist">
  247. {ips
  248. .map((x) => `http://${x}:${brokenithmPort || 1606}/`)
  249. .join("\n")
  250. .trim()}
  251. </div>
  252. </div>
  253. </div>
  254. </div>
  255. {/if}
  256. {#if deviceMode === "diva"}
  257. <div class="row">
  258. <div class="label">Slider Serial Port</div>
  259. <div class="input">
  260. <select bind:value={divaSerialPort} on:change={markDirty}>
  261. <option value="COM1">COM1</option>
  262. <option value="COM2">COM2</option>
  263. <option value="COM3">COM3</option>
  264. <option value="COM4">COM4</option>
  265. <option value="COM5">COM5</option>
  266. <option value="COM6">COM6</option>
  267. <option value="COM7">COM7</option>
  268. <option value="COM8">COM8</option>
  269. <option value="COM9">COM9</option>
  270. </select>
  271. </div>
  272. </div>
  273. <div class="row">
  274. <div class="label">Brightness</div>
  275. <div class="input">
  276. <input
  277. type="number"
  278. min="1"
  279. max="255"
  280. step="1"
  281. bind:value={divaBrightness}
  282. on:change={markDirty}
  283. />
  284. </div>
  285. </div>
  286. <div class="row">
  287. <div class="label" />
  288. <div class="input">
  289. <input
  290. type="range"
  291. min="1"
  292. max="255"
  293. step="1"
  294. bind:value={divaBrightness}
  295. on:change={markDirty}
  296. />
  297. </div>
  298. </div>
  299. {/if}
  300.  
  301. <div class="row">
  302. <div class="label">Output Mode</div>
  303. <div class="input">
  304. <select bind:value={outputMode} on:change={markDirty}>
  305. <option value="none">None</option>
  306. <option value="kb-32-tasoller"
  307. >Keyboard 32-zone, Tasoller Layout</option
  308. >
  309. <option value="kb-32-yuancon">Keyboard 32-zone, Yuancon Layout</option
  310. >
  311. <option value="kb-32-umiguri">Keyboard 32-zone, UMIGURI Layout</option
  312. >
  313. <option value="kb-16">Keyboard 16-zone, Linear</option>
  314. <option value="kb-8">Keyboard 8-zone, Linear</option>
  315. <option value="kb-6">Keyboard 6-zone, Linear</option>
  316. <option value="kb-4">Keyboard 4-zone, Linear</option>
  317. <option value="kb-voltex">Keyboard 10-zone, Voltex Layout</option>
  318. <option value="kb-neardayo">Keyboard 10-zone, Neardayo Layout</option>
  319. <option value="gamepad-voltex">XBOX 360 Gamepad, Voltex Layout</option
  320. >
  321. <option value="gamepad-neardayo"
  322. >XBOX 360 Gamepad, Neardayo Layout</option
  323. >
  324. <option value="gamepad-hori">DS4, HORI DIVA FT ASC Layout</option>
  325. <option value="gamepad-hori-wide"
  326. >DS4, HORI DIVA FT ASC Slider Only Layout</option
  327. >
  328. <!-- <option value="websocket">Websocket</option> -->
  329. </select>
  330. </div>
  331. </div>
  332. {#if deviceMode === "brokenithm-nostalgia" && outputMode !== "none" && outputMode.slice(0, 5) !== "kb-32"}
  333. <div class="row">
  334. <div class="label" />
  335. <div class="input comment">
  336. 32 key layout is recommended for Brokestalgia controllers
  337. </div>
  338. </div>
  339. {:else if deviceMode.slice(0, 10) === "brokenithm" && ["kb-voltex", "kb-neardayo", "gamepad-voltex", "gamepad-neardayo", "gamepad-hori"].includes(outputMode)}
  340. <div class="row">
  341. <div class="label" />
  342. <div class="input comment">
  343. Gamepad layouts are not recommended for Brokenithm controllers
  344. </div>
  345. </div>
  346. {/if}
  347. {#if outputMode.slice(0, 7) === "gamepad"}
  348. <div class="row">
  349. <div class="label" />
  350. <div class="input comment">
  351. Gamepad emulation requires <Link
  352. href="https://github.com/ViGEm/ViGEmBus/releases">ViGEMBus</Link
  353. >
  354. </div>
  355. </div>
  356. {/if}
  357. {#if outputMode !== "none"}
  358. <div class="row">
  359. <div class="label">Output Polling</div>
  360. <div class="input">
  361. <select bind:value={outputPolling} on:change={markDirty}>
  362. <option value="60">60 Hz</option>
  363. <option value="100">100 Hz</option>
  364. <option value="250">250 Hz</option>
  365. <option value="500">500 Hz</option>
  366. <option value="1000">1000 Hz</option>
  367. </select>
  368. </div>
  369. </div>
  370. {/if}
  371. {#if (outputMode.slice(0, 2) === "kb" || outputMode.slice(0, 7) === "gamepad") && deviceMode.slice(0, 10) !== "brokenithm"}
  372. <div class="row">
  373. <div class="label" title="Larger means harder to trigger">
  374. Sensitivity
  375. </div>
  376. <div class="input">
  377. <input
  378. type="number"
  379. min="1"
  380. max="255"
  381. step="1"
  382. bind:value={keyboardSensitivity}
  383. on:change={markDirty}
  384. />
  385. </div>
  386. </div>
  387. <div class="row">
  388. <div class="label" />
  389. <div class="input">
  390. <input
  391. type="range"
  392. min="1"
  393. max="255"
  394. step="1"
  395. bind:value={keyboardSensitivity}
  396. on:change={markDirty}
  397. />
  398. </div>
  399. </div>
  400. {/if}
  401. {#if outputMode.slice(0, 2) === "kb"}
  402. <div class="row">
  403. <div class="label" />
  404. <div class="input">
  405. <span>
  406. <input
  407. type="checkbox"
  408. id="direct-input"
  409. style="width: unset;"
  410. bind:checked={keyboardDirectInput}
  411. on:change={markDirty}
  412. />
  413. <label for="direct-input">Use DirectInput</label>
  414. </span>
  415. </div>
  416. </div>
  417. {#if keyboardDirectInput}
  418. <div class="row">
  419. <div class="label" />
  420. <div class="input comment">
  421. DirectInput emulation requires <Link
  422. href="https://github.com/oblitum/Interception/releases/tag/v1.0.1"
  423. >Interception</Link
  424. >
  425. </div>
  426. </div>
  427. {/if}
  428. {/if}
  429. {#if outputMode === "websocket"}
  430. <div class="row">
  431. <div class="label">Output URL</div>
  432. <div class="input">
  433. <input
  434. placeholder="URL"
  435. bind:value={outputWebsocketUrl}
  436. on:change={markDirty}
  437. />
  438. </div>
  439. </div>
  440. {/if}
  441.  
  442. <div class="row">
  443. <div class="label">LED Mode</div>
  444. <div class="input">
  445. <select bind:value={ledMode} on:change={markDirty}>
  446. <option value="none">None</option>
  447. <option value="reactive-16">Reactive, 16-Zone</option>
  448. <option value="reactive-8">Reactive, 8-Zone</option>
  449. <option value="reactive-6">Reactive, 6-Zone</option>
  450. <option value="reactive-4">Reactive, 4-Zone</option>
  451. <option value="reactive-rainbow">Reactive, 16-Zone Rainbow</option>
  452. <option value="reactive-voltex">Reactive, Voltex Layout</option>
  453. <option value="reactive-hori"
  454. >Reactive, DIVA Future Tone Layout</option
  455. >
  456. <option value="attract">Rainbow Attract Mode</option>
  457. <!-- <option value="websocket">Websocket</option> -->
  458. <option value="umgr-websocket">UMIGURI Websocket</option>
  459. <option value="serial">Serial</option>
  460. </select>
  461. </div>
  462. </div>
  463. {#if ledMode !== "none"}
  464. <div class="row">
  465. <div class="label" />
  466. <div class="input">
  467. <span>
  468. <input
  469. type="checkbox"
  470. id="led-faster"
  471. style="width: unset;"
  472. bind:checked={ledFaster}
  473. on:change={markDirty}
  474. />
  475. <label for="led-faster">Update LED data faster</label>
  476. </span>
  477. </div>
  478. </div>
  479. {/if}
  480. {#if ledMode.slice(0, 8) === "reactive" && ["16", "8", "6", "4"].includes(ledMode.slice(9))}
  481. <div class="row">
  482. <div class="label">Slider Color</div>
  483. <div class="input">
  484. <span>
  485. <input
  486. type="color"
  487. id="color-active"
  488. style="width: 3rem;"
  489. bind:value={ledColorActive}
  490. on:change={markDirty}
  491. />
  492. <label for="color-active">Active</label>
  493. </span>
  494. <span>
  495. <input
  496. type="color"
  497. id="color-base"
  498. style="width: 3rem;"
  499. bind:value={ledColorInactive}
  500. on:change={markDirty}
  501. />
  502. <label for="color-base">Inactive</label>
  503. </span>
  504. </div>
  505. </div>
  506. <div class="row">
  507. <div class="label">Air Color</div>
  508. <div class="input">
  509. <span>
  510. <input
  511. type="color"
  512. id="color-active"
  513. style="width: 3rem;"
  514. bind:value={ledColorAirActive}
  515. on:change={markDirty}
  516. />
  517. <label for="color-active">Active</label>
  518. </span>
  519. <span>
  520. <input
  521. type="color"
  522. id="color-base"
  523. style="width: 3rem;"
  524. bind:value={ledColorAirInactive}
  525. on:change={markDirty}
  526. />
  527. <label for="color-base">Inactive</label>
  528. </span>
  529. </div>
  530. </div>
  531. {/if}
  532. {#if ledMode.slice(0, 8) === "reactive" && deviceMode.slice(0, 10) !== "brokenithm"}
  533. <div class="row">
  534. <div class="label" title="Larger means harder to trigger">
  535. Sensitivity
  536. </div>
  537. <div class="input">
  538. <input
  539. type="number"
  540. min="1"
  541. max="255"
  542. step="1"
  543. bind:value={ledSensitivity}
  544. on:change={markDirty}
  545. />
  546. </div>
  547. </div>
  548. <div class="row">
  549. <div class="label" />
  550. <div class="input">
  551. <input
  552. type="range"
  553. min="1"
  554. max="255"
  555. step="1"
  556. bind:value={ledSensitivity}
  557. on:change={markDirty}
  558. />
  559. </div>
  560. </div>
  561. {/if}
  562. {#if ledMode === "websocket"}
  563. <div class="row">
  564. <div class="label">LED URL</div>
  565. <div class="input">
  566. <input
  567. placeholder="URL"
  568. bind:value={ledWebsocketUrl}
  569. on:change={markDirty}
  570. />
  571. </div>
  572. </div>
  573. {/if}
  574. {#if ledMode === "umgr-websocket"}
  575. <div class="row">
  576. <div class="label">UMIGURI Port</div>
  577. <div class="input">
  578. <input
  579. type="number"
  580. min="1024"
  581. max="65535"
  582. step="1"
  583. bind:value={ledUmgrWebsocketPort}
  584. on:change={markDirty}
  585. />
  586. </div>
  587. </div>
  588. {/if}
  589. {#if ledMode === "serial"}
  590. <div class="row">
  591. <div class="label" />
  592. <div class="input comment">
  593. Serial LED may require <Link
  594. href="https://sourceforge.net/projects/com0com/files/com0com/2.2.2.0/com0com-2.2.2.0-x64-fre-signed.zip/download"
  595. >com0com</Link
  596. >
  597. </div>
  598. </div>
  599. <div class="row">
  600. <div class="label">LED Serial Port</div>
  601. <div class="input">
  602. <select bind:value={ledSerialPort} on:change={markDirty}>
  603. <option value="COM1">COM1</option>
  604. <option value="COM2">COM2</option>
  605. <option value="COM3">COM3</option>
  606. <option value="COM4">COM4</option>
  607. <option value="COM5">COM5</option>
  608. <option value="COM6">COM6</option>
  609. <option value="COM7">COM7</option>
  610. <option value="COM8">COM8</option>
  611. <option value="COM9">COM9</option>
  612. </select>
  613. </div>
  614. </div>
  615. {/if}
  616. </div>
  617. <div class="buttons-row">
  618. <button
  619. on:click={async () => await setConfig()}
  620. class={`${dirty && "primary"}`}>Apply</button
  621. >
  622. <button on:click={async () => await hide()}>Hide</button>
  623. <button on:click={async () => await quit()}>Quit</button>
  624. <button on:click={async () => await logs()}>Logs</button>
  625. {#if deviceMode.slice(0, 10) === "brokenithm"}
  626. <button on:click={async () => await brokenithmQr()}>Brokenithm QR</button>
  627. {/if}
  628. <button on:click={async () => await repo()}>About</button>
  629. </div>
  630. </main>
  631.  
  632. <style>
  633. </style>
  634.  
Advertisement
Add Comment
Please, Sign In to add comment