fix background color
This commit is contained in:
parent
598b0fe277
commit
4b8c7536ea
2
justfile
2
justfile
|
@ -3,7 +3,7 @@ set export
|
|||
alias d := debug
|
||||
|
||||
dev:
|
||||
-cargo tauri dev
|
||||
-yarn tauri dev
|
||||
|
||||
debug:
|
||||
RUST_LOG=debug yarn tauri dev
|
||||
|
|
|
@ -25,20 +25,32 @@ pub fn monitor(
|
|||
.build_input_stream(
|
||||
&config.config(),
|
||||
move |data: &[f32], _: &InputCallbackInfo| {
|
||||
if data
|
||||
let Some(new_level) = data
|
||||
.iter()
|
||||
.any(|e| (e.abs() * *sens.lock().unwrap()) >= *threshold.lock().unwrap())
|
||||
.map(|v| {
|
||||
let val = v.abs();
|
||||
if val < 0.0001 {
|
||||
0.0
|
||||
} else {
|
||||
val * *sens.lock().expect("Unable to lock mic sens mutex")
|
||||
}
|
||||
})
|
||||
.reduce(|prev, curr| prev + curr)
|
||||
.map(|v| v / data.len() as f32)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if new_level
|
||||
> *threshold
|
||||
.lock()
|
||||
.expect("Unable to lock mic threshold mutex")
|
||||
{
|
||||
window.emit("mouth-open", "").unwrap();
|
||||
} else {
|
||||
window.emit("mouth-close", "").unwrap();
|
||||
}
|
||||
|
||||
*level.lock().unwrap() = data
|
||||
.iter()
|
||||
.map(|e| e.abs() * *sens.lock().unwrap())
|
||||
.max_by(|a, b| a.total_cmp(&b))
|
||||
.unwrap();
|
||||
*level.lock().unwrap() = new_level;
|
||||
},
|
||||
move |err| {
|
||||
println!("Audio error: {:?}", err);
|
||||
|
@ -55,7 +67,6 @@ pub fn monitor(
|
|||
}
|
||||
|
||||
fn initialize() -> Result<Device> {
|
||||
debug!("Hey there");
|
||||
let host = cpal::default_host();
|
||||
host.default_input_device()
|
||||
.ok_or_else(|| anyhow!("No default output device"))
|
||||
|
|
|
@ -5,7 +5,7 @@ use figment::{
|
|||
providers::{Env, Format, Toml},
|
||||
Figment,
|
||||
};
|
||||
use log::{debug, error, trace, warn};
|
||||
use log::{debug, error, trace};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri::api::path::config_dir;
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ use std::path::Path;
|
|||
use anyhow::Result;
|
||||
use base64::prelude::*;
|
||||
use image::codecs::gif::{GifDecoder, GifEncoder, Repeat};
|
||||
use image::imageops::resize;
|
||||
use image::{guess_format, AnimationDecoder, Frame, ImageDecoder, ImageFormat};
|
||||
use image::{guess_format, AnimationDecoder, ImageFormat};
|
||||
use log::{debug, error};
|
||||
use ray_format::Ray;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"build": {
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"devPath": "http://localhost:1420",
|
||||
"devPath": "http://localhost:5173",
|
||||
"distDir": "../build"
|
||||
},
|
||||
"package": {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover" data-theme="hamlindigo">
|
||||
<body data-sveltekit-preload-data="hover" data-theme="hamlindigo" class="overflow-hidden">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,3 +2,7 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind variants;
|
||||
|
||||
.dark body {
|
||||
@apply bg-transparent;
|
||||
}
|
||||
|
|
|
@ -4,13 +4,10 @@ import { mode } from './store';
|
|||
export const micLevel = writable(0);
|
||||
export const micThreshold = writable(0);
|
||||
|
||||
export const initAudio = async () => {
|
||||
export const initAudio = async (): Promise<NodeJS.Timeout | undefined> => {
|
||||
if (mode == 'tauri') {
|
||||
const { invoke } = await import('@tauri-apps/api');
|
||||
setInterval(async () => {
|
||||
const newLevel = (await invoke('get_audio_level')) as number;
|
||||
micLevel.set(newLevel);
|
||||
}, 40);
|
||||
|
||||
|
||||
micThreshold.subscribe(async (threshold) => {
|
||||
try {
|
||||
|
@ -19,45 +16,50 @@ export const initAudio = async () => {
|
|||
await invoke('log', { msg: `Error setting mic threshold: ${e}` });
|
||||
}
|
||||
});
|
||||
|
||||
return setInterval(async () => {
|
||||
const newLevel = (await invoke('get_audio_level')) as number;
|
||||
micLevel.set(newLevel * 100);
|
||||
}, 40);
|
||||
} else {
|
||||
await webAudio();
|
||||
return await webAudio();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let audioCtx: AudioContext | undefined = undefined;
|
||||
const webAudio = async () => {
|
||||
const { createEventDispatcher } = await import('svelte');
|
||||
|
||||
const audioCtx = new AudioContext();
|
||||
if(audioCtx) audioCtx.close();
|
||||
audioCtx = new AudioContext();
|
||||
if (navigator.mediaDevices) {
|
||||
try {
|
||||
const device = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
const mic = audioCtx.createMediaStreamSource(device);
|
||||
|
||||
const gain = audioCtx.createGain();
|
||||
mic.connect(gain);
|
||||
const analyzer = audioCtx.createAnalyser();
|
||||
analyzer.fftSize = 2048;
|
||||
mic.connect(analyzer);
|
||||
gain.connect(analyzer);
|
||||
|
||||
gain.gain.setValueAtTime(1, audioCtx.currentTime);
|
||||
|
||||
|
||||
const buffer = new Uint8Array(analyzer.frequencyBinCount);
|
||||
const monitor = () => {
|
||||
analyzer.getByteFrequencyData(buffer);
|
||||
|
||||
const max = buffer.reduce((prev, cur) => prev > cur ? prev : cur);
|
||||
console.log(max);
|
||||
micLevel.update((level) => level + (max * 0.8));
|
||||
const max = (buffer.reduce((prev, cur) => prev + cur) / buffer.length);
|
||||
micLevel.set(max);
|
||||
micThreshold.subscribe((threshold) => {
|
||||
if(max > threshold) {
|
||||
console.log(max, threshold * 100 - 1);
|
||||
if(max > (threshold * 100) - 1) {
|
||||
dispatchEvent(new Event('mouth-open'))
|
||||
} else {
|
||||
dispatchEvent(new Event('mouth-close'));
|
||||
}
|
||||
})();
|
||||
requestAnimationFrame(monitor);
|
||||
}
|
||||
|
||||
monitor();
|
||||
|
||||
|
||||
return setInterval(monitor);
|
||||
|
||||
} catch (e) {
|
||||
console.error("Failed to get microphone: ", e);
|
||||
|
|
|
@ -1,170 +1,151 @@
|
|||
<script lang="ts" context="module">
|
||||
const hints = [
|
||||
"Eyes open | Mouth closed",
|
||||
"Eyes open | Mouth open",
|
||||
"Eyes closed | Mouth closed",
|
||||
"Eyes closed | Mouth open",
|
||||
];
|
||||
const hints = [
|
||||
'Eyes open | Mouth closed',
|
||||
'Eyes open | Mouth open',
|
||||
'Eyes closed | Mouth closed',
|
||||
'Eyes closed | Mouth open'
|
||||
];
|
||||
|
||||
let loading = [false, false, false, false];
|
||||
let loading = [false, false, false, false];
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { frames } from "../store";
|
||||
import { fs, invoke } from "@tauri-apps/api";
|
||||
import { fade } from "svelte/transition";
|
||||
import Context from "./context.svelte";
|
||||
import LoadingSVG from "$lib/svg/loading.svg";
|
||||
import Image from "image-js";
|
||||
import { Gif } from "../gifler";
|
||||
export let index: number;
|
||||
import { frames } from '../store';
|
||||
import { fs, invoke } from '@tauri-apps/api';
|
||||
import { fade } from 'svelte/transition';
|
||||
import Context from './context.svelte';
|
||||
import Image from 'image-js';
|
||||
import { Gif } from '../gifler';
|
||||
export let index: number;
|
||||
|
||||
let menuTimeout: NodeJS.Timeout | null = null;
|
||||
let showMenu = false;
|
||||
let src: string | null;
|
||||
let menuTimeout: NodeJS.Timeout | null = null;
|
||||
let showMenu = false;
|
||||
let src: string | null;
|
||||
|
||||
$: {
|
||||
const frame = $frames[index];
|
||||
if (!frame) src = null;
|
||||
else {
|
||||
if (frame?.kind === "still") src = frame.value.toDataURL();
|
||||
else {
|
||||
const b = new Blob([new Uint8Array(frame.data)]);
|
||||
src = URL.createObjectURL(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
$: {
|
||||
const frame = $frames[index];
|
||||
if (!frame) src = null;
|
||||
else {
|
||||
if (frame?.kind === 'still') src = frame.value.toDataURL();
|
||||
else {
|
||||
const b = new Blob([new Uint8Array(frame.data)]);
|
||||
src = URL.createObjectURL(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const openImage = async () => {
|
||||
loading[index] = true;
|
||||
const path = (await invoke("open_image")) as {
|
||||
kind: "png" | "gif";
|
||||
data: string;
|
||||
};
|
||||
const openImage = async () => {
|
||||
loading[index] = true;
|
||||
const path = (await invoke('open_image')) as {
|
||||
kind: 'png' | 'gif';
|
||||
data: string;
|
||||
};
|
||||
|
||||
if (path) {
|
||||
const data = await fs.readBinaryFile(path.data);
|
||||
if (path.kind == "png")
|
||||
$frames[index] = {
|
||||
kind: "still",
|
||||
value: await Image.load(data),
|
||||
};
|
||||
else {
|
||||
$frames[index] = {
|
||||
kind: "GIF",
|
||||
value: new Gif(new Promise((res) => res(data))),
|
||||
data: data,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
console.error("Error loading image");
|
||||
}
|
||||
loading[index] = false;
|
||||
};
|
||||
const clearImage = () => {
|
||||
$frames[index] = null;
|
||||
};
|
||||
if (path) {
|
||||
const data = await fs.readBinaryFile(path.data);
|
||||
if (path.kind == 'png')
|
||||
$frames[index] = {
|
||||
kind: 'still',
|
||||
value: await Image.load(data)
|
||||
};
|
||||
else {
|
||||
$frames[index] = {
|
||||
kind: 'GIF',
|
||||
value: new Gif(new Promise((res) => res(data))),
|
||||
data: data
|
||||
};
|
||||
}
|
||||
} else {
|
||||
console.error('Error loading image');
|
||||
}
|
||||
loading[index] = false;
|
||||
};
|
||||
const clearImage = () => {
|
||||
$frames[index] = null;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="box">
|
||||
<button
|
||||
class="preview"
|
||||
on:click={(e) => {
|
||||
if (e.shiftKey) {
|
||||
clearImage();
|
||||
} else {
|
||||
openImage();
|
||||
}
|
||||
}}
|
||||
on:mouseenter={() =>
|
||||
(menuTimeout = setTimeout(() => (showMenu = true), 200))}
|
||||
on:mouseleave={() => {
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
}
|
||||
showMenu = false;
|
||||
}}
|
||||
>
|
||||
{#if loading[index]}
|
||||
<img class="loading" src="/loading.svg" alt="Loading" />
|
||||
{:else if src}
|
||||
<img {src} alt="Frame {{ index }}" />
|
||||
{/if}
|
||||
</button>
|
||||
{#if showMenu}
|
||||
<div transition:fade={{ duration: 50 }} class="context">
|
||||
<Context>
|
||||
<p>{hints[index]}</p>
|
||||
</Context>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-xl w-32 bg-surface-100 aspect-square"
|
||||
on:click={(e) => {
|
||||
if (e.shiftKey) {
|
||||
clearImage();
|
||||
} else {
|
||||
openImage();
|
||||
}
|
||||
}}
|
||||
on:mouseenter={() => (menuTimeout = setTimeout(() => (showMenu = true), 200))}
|
||||
on:mouseleave={() => {
|
||||
if (menuTimeout) {
|
||||
clearTimeout(menuTimeout);
|
||||
}
|
||||
showMenu = false;
|
||||
}}
|
||||
>
|
||||
{#if loading[index]}
|
||||
<img class="loading" src="/loading.svg" alt="Loading" />
|
||||
{:else if src}
|
||||
<img {src} alt="Frame {{ index }}" />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<style lang="scss">
|
||||
@-webkit-keyframes rotating {
|
||||
from {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes rotating {
|
||||
from {
|
||||
-webkit-transform: rotate(0deg);
|
||||
-o-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(360deg);
|
||||
-o-transform: rotate(360deg);
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotating {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
-webkit-animation: rotating 2s linear infinite;
|
||||
-moz-animation: rotating 2s linear infinite;
|
||||
-ms-animation: rotating 2s linear infinite;
|
||||
-o-animation: rotating 2s linear infinite;
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
$bg: rgba(150, 150, 150, 0.5);
|
||||
.preview {
|
||||
&:hover {
|
||||
background-color: darken($color: $bg, $amount: 5%);
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
border: 2px solid black;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $bg;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
$bg: rgba(150, 150, 150, 0.5);
|
||||
.preview {
|
||||
&:hover {
|
||||
background-color: darken($color: $bg, $amount: 5%);
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10px;
|
||||
border: 2px solid black;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: $bg;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.box {
|
||||
width: 15vh;
|
||||
height: 15vh;
|
||||
user-select: none;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
.context {
|
||||
z-index: 900;
|
||||
position: absolute;
|
||||
left: 105%;
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
width: 15vh;
|
||||
height: 15vh;
|
||||
user-select: none;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
.context {
|
||||
z-index: 900;
|
||||
position: absolute;
|
||||
left: 105%;
|
||||
width: max-content;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
width: 75%;
|
||||
user-select: none;
|
||||
}
|
||||
img {
|
||||
width: 75%;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
export let onSetpointChange = (_: number) => {};
|
||||
export let barColor: string;
|
||||
|
||||
const handleXPos = 0;
|
||||
|
||||
let bar: HTMLDivElement;
|
||||
let point: HTMLDivElement;
|
||||
let pos = { x: 0, y: 0 };
|
||||
let pos = { x: handleXPos, y: 0 };
|
||||
const tweenedProgress = tweened(0, { duration: 100, easing: sineOut });
|
||||
|
||||
onMount(async () => {
|
||||
|
@ -34,10 +36,6 @@
|
|||
$tweenedProgress = pxProgress;
|
||||
}
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
value = (value + 1) % 100;
|
||||
}, 100)
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
|
@ -46,7 +44,7 @@
|
|||
// msg: `Resized`,
|
||||
// });
|
||||
|
||||
pos = { x: 0, y: 0 };
|
||||
pos = { x: handleXPos, y: 0 };
|
||||
let rect = bar.getBoundingClientRect();
|
||||
let prect = point.getBoundingClientRect();
|
||||
pos.y = -(setpoint / 100) * rect.height + prect.height;
|
||||
|
@ -54,13 +52,16 @@
|
|||
/>
|
||||
|
||||
<div class="h-full flex flex-col gap-4">
|
||||
<div bind:this={bar} class="bar-container rounded-container-token bg-surface-500 border-token border-surface-500-400-token">
|
||||
<div
|
||||
id="bar-container"
|
||||
bind:this={bar}
|
||||
class="w-10 flex flex-col justify-end rounded-container-token bg-surface-500 border-token border-surface-500-400-token h-5/6"
|
||||
>
|
||||
{#if withSetpoint}
|
||||
<div
|
||||
bind:this={point}
|
||||
class="setpoint"
|
||||
class="setpoint bg-slate-900 w-9 rounded-token"
|
||||
use:draggable={{
|
||||
// onDragStart: () => (pos = undefined),
|
||||
handle: '.handle',
|
||||
position: pos,
|
||||
axis: 'y',
|
||||
|
@ -73,53 +74,37 @@
|
|||
onSetpointChange(1.0 - y);
|
||||
}}
|
||||
>
|
||||
<div class="handle" />
|
||||
<div class="handle rounded" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="bar rounded-token {barColor ? barColor : "bg-primary-500"}" style="height: {$tweenedProgress}px;" />
|
||||
<div
|
||||
id="bar"
|
||||
class="bar rounded-token {barColor ? barColor : 'bg-primary-500'}"
|
||||
style="height: {$tweenedProgress}px;"
|
||||
/>
|
||||
</div>
|
||||
<div class="aspect-square w-full">
|
||||
<slot />
|
||||
</div>
|
||||
<slot class="icon" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1vh;
|
||||
:global(img) {
|
||||
width: var(--bar-width, 5vh);
|
||||
}
|
||||
}
|
||||
|
||||
.setpoint {
|
||||
position: absolute;
|
||||
background-color: black;
|
||||
width: var(--bar-width, 5vh);
|
||||
height: 0.5vh;
|
||||
.handle {
|
||||
position: absolute;
|
||||
border-top: 2vh solid transparent;
|
||||
border-bottom: 2vh solid transparent;
|
||||
border-left: 2vh solid var(--handle-color, forestgreen);
|
||||
left: -2.2vh;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
border-top: 1rem solid transparent;
|
||||
border-bottom: 1rem solid transparent;
|
||||
border-left: 1.5rem solid var(--handle-color, forestgreen);
|
||||
|
||||
.bar-container {
|
||||
// border-radius: var(--bar-radius, 0px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
height: 80vh;
|
||||
width: var(--bar-width, 5vh);
|
||||
top: 50%;
|
||||
transform: translateY(-50%) translateX(-1.7rem);
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
transition: background-color 0.1s;
|
||||
|
|
|
@ -8,10 +8,17 @@ export const active = derived(
|
|||
([$level, $threshold]) => $level > $threshold
|
||||
);
|
||||
export const blink = writable(false);
|
||||
export const transparent = writable(false);
|
||||
|
||||
export const initState = async () => {
|
||||
if (mode == 'tauri') {
|
||||
const { appWindow } = await import('@tauri-apps/api/window');
|
||||
const { appWindow, PhysicalSize } = await import('@tauri-apps/api/window');
|
||||
|
||||
await appWindow.center();
|
||||
await appWindow.setMinSize(new PhysicalSize(720, 600));
|
||||
await appWindow.onFocusChanged(({ payload: focused }) => {
|
||||
transparent.set(!focused);
|
||||
});
|
||||
|
||||
await appWindow.listen('mouth-open', () => {
|
||||
activationLevel.set(100);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import FramePreview from '$lib/components/FramePreview.svelte';
|
||||
import { quintInOut } from 'svelte/easing';
|
||||
import { initAudio, micLevel, micThreshold } from "$lib/audio";
|
||||
import { initAudio, micLevel, micThreshold } from '$lib/audio';
|
||||
import '../app.postcss';
|
||||
|
||||
// Floating UI for Popups
|
||||
|
@ -9,97 +9,129 @@
|
|||
import { storePopup } from '@skeletonlabs/skeleton';
|
||||
import { fly } from 'svelte/transition';
|
||||
import Bar from '$lib/components/bar.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { active, initState, closeThreshold, activationLevel } from '$lib/state';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { active, initState, closeThreshold, activationLevel, transparent } from '$lib/state';
|
||||
storePopup.set({ computePosition, autoUpdate, flip, shift, offset, arrow });
|
||||
|
||||
|
||||
let deinitAudio: NodeJS.Timeout | undefined = undefined;
|
||||
onMount(async () => {
|
||||
await initAudio();
|
||||
deinitAudio = await initAudio();
|
||||
await initState();
|
||||
})
|
||||
});
|
||||
|
||||
$: transparent = false;
|
||||
onDestroy(() => {
|
||||
if (deinitAudio) clearInterval(deinitAudio);
|
||||
});
|
||||
|
||||
$: bgColor = $transparent ? 'bg-pink-' : 'bg-surface-900';
|
||||
</script>
|
||||
|
||||
<div class="container" class: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 class="{bgColor} w-screen h-screen" id="container">
|
||||
<slot />
|
||||
<div
|
||||
transition:fly={{
|
||||
duration: 200,
|
||||
x: 200,
|
||||
opacity: 100,
|
||||
easing: quintInOut,
|
||||
}}
|
||||
class="audio flex absolute right-6 gap-10 top-1/2 translate-y-[-50%]"
|
||||
>
|
||||
{#key $micThreshold}
|
||||
<Bar
|
||||
bind:value={$micLevel}
|
||||
setpoint={$micThreshold * 100}
|
||||
withSetpoint
|
||||
onSetpointChange={async (e) => {
|
||||
$micThreshold = e;
|
||||
console.log(e);
|
||||
}}
|
||||
barColor={$active ? "bg-success-500" : "bg-tertiary-500"}
|
||||
><img src="mic.svg" alt="microphone" /></Bar
|
||||
>
|
||||
{/key}
|
||||
{#key $closeThreshold}
|
||||
<Bar
|
||||
withSetpoint
|
||||
value={$activationLevel}
|
||||
setpoint={$closeThreshold}
|
||||
onSetpointChange={(y) => ($closeThreshold = y * 100)}
|
||||
><img src="mouth.svg" alt="mouth" /></Bar
|
||||
>
|
||||
{/key}
|
||||
</div>
|
||||
{#if !$transparent}
|
||||
<div
|
||||
transition:fly={{
|
||||
duration: 200,
|
||||
x: -200,
|
||||
opacity: 100,
|
||||
easing: quintInOut
|
||||
}}
|
||||
class="flex flex-col justify-around h-full mx-10"
|
||||
>
|
||||
{#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 flex absolute right-6 gap-10 top-1/2 translate-y-[-50%] h-5/6"
|
||||
>
|
||||
{#key $micThreshold}
|
||||
<Bar
|
||||
value={$micLevel}
|
||||
setpoint={$micThreshold * 100}
|
||||
withSetpoint
|
||||
onSetpointChange={async (e) => {
|
||||
$micThreshold = e;
|
||||
console.log(e);
|
||||
}}
|
||||
barColor={$active ? 'bg-success-500' : 'bg-blue-500'}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class:stroke-success-500={$active}
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z"
|
||||
/>
|
||||
</svg>
|
||||
</Bar>
|
||||
{/key}
|
||||
{#key $closeThreshold}
|
||||
<Bar
|
||||
withSetpoint
|
||||
value={$activationLevel}
|
||||
setpoint={$closeThreshold}
|
||||
onSetpointChange={(y) => ($closeThreshold = y * 100)}
|
||||
barColor={'bg-blue-500'}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z"
|
||||
/>
|
||||
</svg>
|
||||
</Bar>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<style lang="scss">
|
||||
@keyframes fade-out {
|
||||
0% {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
100% {
|
||||
background-color: var(--inactive-color);
|
||||
}
|
||||
}
|
||||
// @keyframes fade-out {
|
||||
// 0% {
|
||||
// background-color: var(--active-color);
|
||||
// }
|
||||
// 100% {
|
||||
// background-color: var(--inactive-color);
|
||||
// }
|
||||
// }
|
||||
|
||||
@keyframes fade-in {
|
||||
0% {
|
||||
background-color: var(--inactive-color);
|
||||
}
|
||||
100% {
|
||||
background-color: var(--active-color);
|
||||
}
|
||||
}
|
||||
.container {
|
||||
background-color: var(--active-color);
|
||||
animation-name: fade-in;
|
||||
animation-duration: 0.2s;
|
||||
}
|
||||
// @keyframes fade-in {
|
||||
// 0% {
|
||||
// background-color: var(--inactive-color);
|
||||
// }
|
||||
// 100% {
|
||||
// background-color: var(--active-color);
|
||||
// }
|
||||
// }
|
||||
// .container {
|
||||
// animation-name: fade-in;
|
||||
// animation-duration: 0.2s;
|
||||
// }
|
||||
|
||||
.container.transparent {
|
||||
animation-name: fade-out;
|
||||
animation-duration: 0.2s;
|
||||
background-color: var(--inactive-color);
|
||||
}
|
||||
// .container.transparent {
|
||||
// animation-name: fade-out;
|
||||
// animation-duration: 0.2s;
|
||||
// }
|
||||
|
||||
.frames {
|
||||
align-items: left;
|
||||
|
@ -111,4 +143,4 @@
|
|||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
</style> -->
|
||||
|
|
|
@ -1,50 +1,3 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 330 330" style="enable-background:new 0 0 330 330;" xml:space="preserve">
|
||||
<g id="XMLID_894_">
|
||||
<g id="XMLID_895_">
|
||||
<path id="XMLID_896_" d="M164.998,210c35.888,0,65.085-29.195,65.085-65.12l-0.204-80c0-35.775-29.105-64.88-64.881-64.88
|
||||
c-35.773,0-64.877,29.105-64.877,64.842l-0.203,80.076C99.918,180.805,129.112,210,164.998,210z M130.121,64.88
|
||||
c0-19.233,15.646-34.88,34.877-34.88c19.233,0,34.881,15.647,34.881,34.919l0.204,80c0,19.344-15.739,35.081-35.085,35.081
|
||||
c-19.343,0-35.08-15.737-35.08-35.044L130.121,64.88z"/>
|
||||
</g>
|
||||
<g id="XMLID_899_">
|
||||
<path id="XMLID_900_" d="M280.084,154.96c0-8.284-6.716-15-15-15c-8.284,0-15,6.716-15,15c0,46.732-37.878,84.774-84.546,85.068
|
||||
c-0.181-0.006-0.357-0.027-0.54-0.027c-0.183,0-0.359,0.021-0.541,0.027c-46.665-0.293-84.541-38.335-84.541-85.068
|
||||
c0-8.284-6.716-15-15-15s-15,6.716-15,15c0,58.373,43.688,106.731,100.082,114.104V300H117c-8.284,0-15,6.716-15,15
|
||||
s6.716,15,15,15h96.001c8.284,0,15-6.716,15-15s-6.716-15-15-15h-33.003v-30.936C236.395,261.69,280.084,213.332,280.084,154.96z"
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 334 B |
|
@ -1 +1,3 @@
|
|||
<svg width="64px" height="64px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--emojione-monotone" preserveAspectRatio="xMidYMid meet"><path d="M32 2C15.432 2 2 15.432 2 32s13.432 30 30 30s30-13.432 30-30S48.568 2 32 2zm0 57.5C16.836 59.5 4.5 47.164 4.5 32S16.836 4.5 32 4.5S59.5 16.836 59.5 32S47.164 59.5 32 59.5z" fill="currentColor"></path><circle cx="32" cy="45.139" r="7" fill="currentColor"></circle><circle cx="20.248" cy="25.045" r="4.5" fill="currentColor"></circle><circle cx="42.75" cy="25.045" r="4.5" fill="currentColor"></circle></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.114 5.636a9 9 0 010 12.728M16.463 8.288a5.25 5.25 0 010 7.424M6.75 8.25l4.72-4.72a.75.75 0 011.28.53v15.88a.75.75 0 01-1.28.53l-4.72-4.72H4.51c-.88 0-1.704-.507-1.938-1.354A9.01 9.01 0 012.25 12c0-.83.112-1.633.322-2.396C2.806 8.756 3.63 8.25 4.51 8.25H6.75z" />
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 654 B After Width: | Height: | Size: 462 B |
|
@ -18,7 +18,7 @@ export default {
|
|||
preset: [
|
||||
{
|
||||
name: 'hamlindigo',
|
||||
enhancements: true,
|
||||
enhancements: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue