249 lines
5.5 KiB
Svelte
249 lines
5.5 KiB
Svelte
<script lang="ts" context="module">
|
|
import { writable, type Writable } from "svelte/store";
|
|
const transparent = writable(false);
|
|
const threshold: Writable<number> = writable(0.5);
|
|
const level = writable(0);
|
|
|
|
type WebRay = {
|
|
frames: Array<string>;
|
|
meta: {
|
|
threshold: string | undefined;
|
|
closeThreshold: string | undefined;
|
|
};
|
|
};
|
|
</script>
|
|
|
|
<script lang="ts">
|
|
import { appWindow, PhysicalSize } from "@tauri-apps/api/window";
|
|
import { onMount, onDestroy } from "svelte";
|
|
import { fly } from "svelte/transition";
|
|
import FramePreview from "../components/FramePreview.svelte";
|
|
import Tuber from "../components/tube.svelte";
|
|
import { invoke } from "@tauri-apps/api/tauri";
|
|
import Bar from "../components/bar.svelte";
|
|
import { tick } from "svelte";
|
|
import { frames } from "../store";
|
|
import { quintInOut } from "svelte/easing";
|
|
|
|
let monitorTimer: NodeJS.Timer;
|
|
|
|
let active = false;
|
|
let activation = 0;
|
|
let closeThreshold = 75;
|
|
|
|
$: {
|
|
invoke("set_mic_threshold", { threshold: $threshold as number })
|
|
.then()
|
|
.catch(async (e) => {
|
|
await invoke("log", { msg: `Error setting mic threshold: ${e}` });
|
|
});
|
|
}
|
|
|
|
const openRay = (ray: WebRay) => {
|
|
for (let i = 0; i < 4; i++) {
|
|
$frames[i] = ray.frames[i];
|
|
}
|
|
if (ray.meta.threshold) {
|
|
$threshold = parseFloat(ray.meta.threshold);
|
|
}
|
|
if (ray.meta.closeThreshold) {
|
|
closeThreshold = parseFloat(ray.meta.closeThreshold);
|
|
}
|
|
};
|
|
onMount(async () => {
|
|
await appWindow.setMinSize(new PhysicalSize(720, 600));
|
|
|
|
await appWindow.onFocusChanged(({ payload: focused }) => {
|
|
$transparent = !focused;
|
|
});
|
|
|
|
await appWindow.listen("load-ray", (e) => {
|
|
openRay(e.payload as WebRay);
|
|
});
|
|
|
|
monitorTimer = setInterval(async () => {
|
|
if (!$transparent) {
|
|
$level = await invoke("get_audio_level");
|
|
await tick();
|
|
}
|
|
}, 40);
|
|
|
|
$threshold = await invoke("get_mic_threshold");
|
|
});
|
|
|
|
onDestroy(() => {
|
|
if (monitorTimer) {
|
|
clearInterval(monitorTimer);
|
|
monitorTimer = undefined;
|
|
}
|
|
});
|
|
|
|
const saveRay = async () => {
|
|
let fr = new Array<string>(4);
|
|
for (let i = 0; i < 4; i++) {
|
|
$frames[i] ? (fr[i] = $frames[i]) : (fr[i] = "");
|
|
}
|
|
fr.map((e) => (e ? e : ""));
|
|
const ray = {
|
|
frames: [fr[0], fr[1], fr[2], fr[3]],
|
|
meta: {
|
|
threshold: $threshold.toString(),
|
|
closeThreshold: closeThreshold.toString(),
|
|
},
|
|
};
|
|
|
|
// await invoke("log", { msg: `Saving ray: ${JSON.stringify(ray)}` });
|
|
invoke("save_ray", { ray })
|
|
.then()
|
|
.catch((e) => invoke("log", { msg: e }).then());
|
|
};
|
|
|
|
const loadRay = async () => {
|
|
const ray: WebRay = await invoke("open_ray");
|
|
openRay(ray);
|
|
};
|
|
</script>
|
|
|
|
<div class="container" class:transparent={$transparent}>
|
|
<Tuber
|
|
bind:open={active}
|
|
bind:buf={activation}
|
|
bind:threshold={closeThreshold}
|
|
/>
|
|
{#if !$transparent}
|
|
<div
|
|
transition:fly={{
|
|
duration: 200,
|
|
x: -200,
|
|
opacity: 100,
|
|
easing: quintInOut,
|
|
}}
|
|
class="frames"
|
|
>
|
|
{#each [0, 1, 2, 3] as i}
|
|
<FramePreview index={i} />
|
|
{/each}
|
|
</div>
|
|
|
|
<div
|
|
transition:fly={{
|
|
duration: 200,
|
|
x: 200,
|
|
opacity: 100,
|
|
easing: quintInOut,
|
|
}}
|
|
class="audio"
|
|
>
|
|
{#key $threshold}
|
|
<Bar
|
|
progress={$level * 100}
|
|
setpoint={$threshold * 100}
|
|
withSetpoint
|
|
onSetpointChange={async (e) => {
|
|
$threshold = e;
|
|
}}
|
|
--bar-color={active ? "lightgreen" : "blue"}
|
|
><img src="mic.svg" alt="microphone" /></Bar
|
|
>
|
|
{/key}
|
|
{#key closeThreshold}
|
|
<Bar
|
|
withSetpoint
|
|
progress={activation}
|
|
setpoint={closeThreshold}
|
|
onSetpointChange={(y) => (closeThreshold = y * 100)}
|
|
><img src="mouth.svg" alt="mouth" /></Bar
|
|
>
|
|
{/key}
|
|
</div>
|
|
<div
|
|
transition:fly={{
|
|
duration: 200,
|
|
y: -100,
|
|
opacity: 100,
|
|
easing: quintInOut,
|
|
}}
|
|
class="buttons"
|
|
>
|
|
<div on:click={saveRay}>Save</div>
|
|
<div on:click={loadRay}>Load</div>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<svelte:body on:contextmenu|preventDefault />
|
|
|
|
<style lang="scss">
|
|
.buttons {
|
|
position: absolute;
|
|
top: 2vh;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-items: center;
|
|
gap: 5vh;
|
|
div {
|
|
color: white;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
background-color: rgba(0.5, 0.5, 0.5, 0.5);
|
|
border: solid black 2px;
|
|
user-select: none;
|
|
cursor: pointer;
|
|
&:hover {
|
|
background-color: rgba(0.9, 0.9, 0.9, 0.9);
|
|
}
|
|
}
|
|
}
|
|
|
|
.frames {
|
|
align-items: left;
|
|
position: absolute;
|
|
top: 5vh;
|
|
left: 20px;
|
|
bottom: 5vh;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.audio {
|
|
position: absolute;
|
|
display: flex;
|
|
gap: 2.5vh;
|
|
bottom: 5vh;
|
|
top: 5vh;
|
|
right: 20px;
|
|
}
|
|
|
|
@keyframes fade-out {
|
|
0% {
|
|
background-color: inherit;
|
|
}
|
|
100% {
|
|
background-color: transparent;
|
|
}
|
|
}
|
|
|
|
@keyframes fade-in {
|
|
0% {
|
|
background-color: transparent;
|
|
}
|
|
100% {
|
|
background-color: inherit;
|
|
}
|
|
}
|
|
.container {
|
|
background-color: inherit;
|
|
animation-name: fade-in;
|
|
animation-duration: 0.2s;
|
|
}
|
|
|
|
.container.transparent {
|
|
animation-name: fade-out;
|
|
animation-duration: 0.2s;
|
|
background-color: transparent;
|
|
}
|
|
</style>
|