Multilingual support via Fluent + Frontend improvements + Rewrite of ArcReactor
This commit is contained in:
parent
adec595cfa
commit
412acb7e2d
41 changed files with 2436 additions and 723 deletions
|
|
@ -1,17 +1,23 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { appInfo } from "@/stores"
|
||||
import { appInfo, currentLanguage, translations, translate } from "@/stores"
|
||||
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
|
||||
let authorName = ""
|
||||
let tgLink = ""
|
||||
let repoLink = ""
|
||||
let boostyLink = ""
|
||||
let patreonLink = ""
|
||||
|
||||
const currentYear = new Date().getFullYear()
|
||||
|
||||
appInfo.subscribe(info => {
|
||||
tgLink = info.tgOfficialLink
|
||||
repoLink = info.repositoryLink
|
||||
boostyLink = info.boostySupportLink
|
||||
patreonLink = info.patreonSupportLink
|
||||
})
|
||||
|
||||
onMount(async () => {
|
||||
|
|
@ -24,28 +30,42 @@
|
|||
</script>
|
||||
|
||||
<footer id="footer">
|
||||
<p>© {currentYear}. Автор проекта: {authorName}</p>
|
||||
<p>© {currentYear}. {t('footer-author')}: <b>{authorName}</b></p>
|
||||
<p class="links">
|
||||
<a href={tgLink} target="_blank" class="special-link">
|
||||
<img src="/media/icons/howdy-logo.png" alt="Telegram" width="20px" />
|
||||
Наш телеграм
|
||||
{#if $currentLanguage === "ru" || $currentLanguage === "ua"}
|
||||
<a href={tgLink} target="_blank" class="telegram-link">
|
||||
<img src="/media/icons/telegram.webp" alt="Telegram" width="18px" />
|
||||
<span>{t('footer-telegram')}</span>
|
||||
</a>
|
||||
канал.
|
||||
|
||||
|
||||
{/if}
|
||||
<a href={repoLink} target="_blank">
|
||||
<img src="/media/icons/github-logo.png" alt="GitHub" width="18px" />
|
||||
Github репозиторий
|
||||
<span>{t('footer-github')}</span>
|
||||
</a>
|
||||
проекта.
|
||||
</p>
|
||||
<p class="links last">
|
||||
{#if $currentLanguage === "ru"}
|
||||
{t('footer-support')} <a href={tgLink} target="_blank" class="telegram-link">
|
||||
<img src="/media/icons/boosty.webp" alt="Boosty" width="18px" />
|
||||
<span>Boosty</span>
|
||||
</a>.
|
||||
{/if}
|
||||
{#if $currentLanguage === "ua" || $currentLanguage === "en"}
|
||||
{t('footer-support')} <a href={tgLink} target="_blank" class="telegram-link">
|
||||
<img src="/media/icons/patreon.png" alt="Patreon" width="18px" />
|
||||
<span>Patreon</span>
|
||||
</a>.
|
||||
{/if}
|
||||
</p>
|
||||
</footer>
|
||||
|
||||
<style lang="scss">
|
||||
#footer {
|
||||
text-align: center;
|
||||
color: #565759;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #6c6e71;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
line-height: 1.7em;
|
||||
margin-top: 15px;
|
||||
|
||||
|
|
@ -56,28 +76,54 @@
|
|||
&.links {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&.last {
|
||||
margin-top: -5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #185876;
|
||||
color: #555759!important;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.5s;
|
||||
transition: 0.3s;
|
||||
|
||||
& > span {
|
||||
color: #185876;
|
||||
border-bottom: 1px solid #185876;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
img {
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.5s;
|
||||
margin-top: -4px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #2A9CD0;
|
||||
color: #777a7d!important;
|
||||
|
||||
& > span {
|
||||
color: #2A9CD0;
|
||||
}
|
||||
|
||||
img {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.telegram-link {
|
||||
color: #185876;
|
||||
display: inline-block;
|
||||
|
||||
&:hover {
|
||||
color: #2A9CD0;
|
||||
// background: url(/media/images/bg/bg24.gif);
|
||||
// background-repeat: no-repeat;
|
||||
// background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&.special-link {
|
||||
color: #941d92;
|
||||
display: inline-block;
|
||||
|
|
|
|||
|
|
@ -1,54 +1,185 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
import { goto } from "@roxi/routify"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { isActive } from "@roxi/routify"
|
||||
import { Dashboard, Gear } from "radix-icons-svelte"
|
||||
|
||||
import { onMount } from "svelte"
|
||||
import { currentLanguage, setLanguage, translations, translate } from "@/stores"
|
||||
|
||||
let appVersion = ""
|
||||
let commandsCount = 0
|
||||
|
||||
let selectedLang = "?"
|
||||
let langDropdownOpen = false
|
||||
|
||||
const languages = [
|
||||
{ code: "ru", label: "RU", flag: "🇷🇺", name: "Русский" },
|
||||
{ code: "en", label: "EN", flag: "🇬🇧", name: "English" },
|
||||
{ code: "ua", label: "UA", flag: "🇺🇦", name: "Українська" },
|
||||
]
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
appVersion = await invoke<string>("get_app_version")
|
||||
} catch (err) {
|
||||
console.error("failed to get app version:", err)
|
||||
commandsCount = await invoke<number>("get_commands_count")
|
||||
|
||||
// load saved language
|
||||
const savedLang = await invoke<string>("db_read", { key: "language" })
|
||||
if (savedLang) {
|
||||
selectedLang = savedLang
|
||||
}
|
||||
} catch {
|
||||
commandsCount = 0
|
||||
}
|
||||
})
|
||||
|
||||
async function selectLanguage(code: string) {
|
||||
await setLanguage(code)
|
||||
langDropdownOpen = false
|
||||
}
|
||||
|
||||
function toggleLangDropdown() {
|
||||
langDropdownOpen = !langDropdownOpen
|
||||
}
|
||||
|
||||
function closeLangDropdown(e: MouseEvent) {
|
||||
const target = e.target as HTMLElement
|
||||
if (!target.closest('.lang-selector')) {
|
||||
langDropdownOpen = false
|
||||
}
|
||||
}
|
||||
|
||||
$: currentLang = languages.find(l => l.code === $currentLanguage) || languages[0]
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
</script>
|
||||
|
||||
<header id="header">
|
||||
<div class="logo">
|
||||
<a href="/" title="Проект канала Хауди Хо!">
|
||||
<img src="/media/header-logo.png" alt="Jarvis Logo" />
|
||||
</a>
|
||||
<div>
|
||||
<h1><a href="/">JARVIS</a></h1>
|
||||
<h2>
|
||||
v{appVersion}
|
||||
<small class="beta-badge">BETA</small>
|
||||
</h2>
|
||||
<svelte:window on:click={closeLangDropdown} />
|
||||
|
||||
<header id="header" class="header">
|
||||
<div class="header-left">
|
||||
<div class="logo">
|
||||
<a href="/" title="JARVIS">
|
||||
<img src="/media/128x128.png" alt="Jarvis Logo" />
|
||||
</a>
|
||||
<div class="logo-text">
|
||||
<span class="logo-title"><a href="/" id="jarvis-logo"> </a></span>
|
||||
<span class="logo-version"><small>v</small>{appVersion} <span class="v-badge">BETA</span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
<button class="header-btn" on:click={() => $goto('/commands')}>
|
||||
<span class="btn-text">{t('header-commands')}</span>
|
||||
<span class="btn-badge purple">{commandsCount}+</span>
|
||||
</button>
|
||||
|
||||
<button class="header-btn" on:click={() => $goto('/settings')}>
|
||||
<span class="btn-text">{t('header-settings')}</span>
|
||||
</button>
|
||||
|
||||
<nav class="top-menu">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/commands" class:active={$isActive("/commands")}>
|
||||
<Dashboard /> Команды
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/settings" class:active={$isActive("/settings")}>
|
||||
<Gear /> Настройки
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="lang-selector">
|
||||
<button class="lang-btn" on:click|stopPropagation={toggleLangDropdown}>
|
||||
<span class="lang-flag"><img src="/media/flags/{currentLang.label}.png" width="23px" alt="{currentLang.flag}"></span>
|
||||
</button>
|
||||
|
||||
{#if langDropdownOpen}
|
||||
<div class="lang-dropdown">
|
||||
{#each languages as lang}
|
||||
<button
|
||||
class="lang-option"
|
||||
class:active={lang.code === $currentLanguage}
|
||||
on:click|stopPropagation={() => selectLanguage(lang.code)}
|
||||
>
|
||||
<span class="lang-flag"><img src="/media/flags/{lang.label}.png" width="20px" alt="{lang.flag}"></span>
|
||||
<span class="lang-name">{lang.name}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
.beta-badge {
|
||||
color: #8AC832;
|
||||
opacity: 0.9;
|
||||
font-size: 13px;
|
||||
.lang-selector {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
.lang-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.5rem 0.65rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
color: #ffffff;
|
||||
font-size: 0.7rem;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: rgba(35, 50, 55, 0.7);
|
||||
}
|
||||
}
|
||||
|
||||
.lang-flag {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.lang-code {
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.lang-arrow {
|
||||
font-size: 0.8rem;
|
||||
opacity: 0.6;
|
||||
transition: transform 0.2s ease;
|
||||
|
||||
&.open {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.lang-dropdown {
|
||||
position: absolute;
|
||||
top: calc(100% + 0.35rem);
|
||||
right: 0;
|
||||
background: rgba(20, 30, 35, 0.98);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
z-index: 100;
|
||||
min-width: 130px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.lang-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
padding: 0.6rem 0.85rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
font-size: 0.75rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
text-align: left;
|
||||
|
||||
&:hover {
|
||||
background: rgba(82, 254, 254, 0.1);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: rgba(82, 254, 254, 0.15);
|
||||
color: #52fefe;
|
||||
}
|
||||
}
|
||||
|
||||
.lang-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { jarvisState } from "@/stores"
|
||||
|
||||
// map state to class
|
||||
$: stateClass = getStateClass($jarvisState)
|
||||
|
||||
function getStateClass(state: string): string {
|
||||
|
|
@ -18,92 +17,72 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<!-- Based on: https://github.com/rembertdesigns/Iron-Man-Arc-Reactor-Pure-CSS -->
|
||||
<!-- and https://codepen.io/FlyingEmu/pen/DZNqEj -->
|
||||
|
||||
<div class="arc-reactor-wrapper">
|
||||
<div id="arc-reactor" class="reactor-container {stateClass}">
|
||||
<div class="reactor-container-inner circle abs-center">
|
||||
<ul class="marks">
|
||||
{#each Array(60) as _, i}
|
||||
<li></li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="e7">
|
||||
<div class="semi_arc_3 e5_1">
|
||||
<div class="semi_arc_3 e5_2">
|
||||
<div class="semi_arc_3 e5_3">
|
||||
<div class="semi_arc_3 e5_4"></div>
|
||||
<div id="arc-reactor" class="reactor-container {stateClass} arc-white">
|
||||
<div class="reactor-container-inner circle abs-center">
|
||||
<ul class="marks">
|
||||
{#each Array(60) as _, i}
|
||||
<li></li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="e7">
|
||||
<div class="semi_arc_3 e5_1">
|
||||
<div class="semi_arc_3 e5_1_ghost"></div>
|
||||
<div class="semi_arc_3 e5_2">
|
||||
<div class="semi_arc_3 e5_2_ghost"></div>
|
||||
<div class="semi_arc_3 e5_3">
|
||||
<div class="semi_arc_3 e5_3_ghost"></div>
|
||||
<div class="semi_arc_3 e5_4">
|
||||
<div class="semi_arc_3 e5_4_ghost"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tunnel circle abs-center"></div>
|
||||
<div class="core-wrapper circle abs-center"></div>
|
||||
<div class="core-outer circle abs-center"></div>
|
||||
<div class="core-inner circle abs-center"></div>
|
||||
<div class="coil-container">
|
||||
{#each Array(8) as _, i}
|
||||
<div class="coil coil-{i + 1}"></div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="state-label">
|
||||
<span class="status-dot"></span>
|
||||
<span class="label-text">
|
||||
{#if $jarvisState === "disconnected"}
|
||||
Отключен
|
||||
{:else if $jarvisState === "idle"}
|
||||
Ожидание
|
||||
{:else if $jarvisState === "listening"}
|
||||
Слушаю
|
||||
{:else if $jarvisState === "processing"}
|
||||
Обработка
|
||||
{/if}
|
||||
</span>
|
||||
<div class="tunnel circle abs-center"></div>
|
||||
<div class="core-wrapper circle abs-center"></div>
|
||||
<div class="core-outer circle abs-center"></div>
|
||||
<div class="core-inner circle abs-center"></div>
|
||||
<div class="coil-container">
|
||||
{#each Array(8) as _, i}
|
||||
<div class="coil coil-{i + 1}"></div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss" global>
|
||||
// [ Variables ]--
|
||||
$arc-radius: 130px;
|
||||
$size3: 6px;
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
$marks-color-1: rgba(2, 254, 255, 1);
|
||||
$marks-color-2: rgba(2, 254, 255, 0.3);
|
||||
$colour1: rgba(2, 255, 255, 0.15);
|
||||
$colour3: rgba(2, 255, 255, 0.3);
|
||||
// [ ARC REACTOR VARIABLES ]
|
||||
$arc-radius: 133px;
|
||||
$arc-container-size: 195%;
|
||||
$arc-spacing: 93%;
|
||||
|
||||
// [ Wrapper ]--
|
||||
.arc-reactor-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
// arc thickness per level
|
||||
$arc-thickness-1: 1px;
|
||||
$arc-thickness-2: 2px;
|
||||
$arc-thickness-3: 3px;
|
||||
$arc-thickness-4: 4px;
|
||||
|
||||
.state-label {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #52fefe;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
// marks (lines) settings
|
||||
$mark-width: 2.5px;
|
||||
$mark-height: 11px;
|
||||
|
||||
// [ Base container ]--
|
||||
// [ DEFAULT THEME - CYAN ]
|
||||
.reactor-container {
|
||||
--arc-color: 2, 254, 255; // RGB values for easy rgba()
|
||||
--arc-glow: #52fefe;
|
||||
--arc-glow-rgb: 82, 254, 254;
|
||||
--arc-core-border: #1b4e5f;
|
||||
--arc-core-bg: #073c4b;
|
||||
|
||||
width: 300px;
|
||||
height: 320px;
|
||||
height: 300px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
transition: scale 1s ease, opacity 0.5s ease;
|
||||
scale: 0.9;
|
||||
transition: transform 0.5s ease, opacity 0.5s ease, filter 0.5s ease;
|
||||
transform: scale(0.95);
|
||||
opacity: 0.9;
|
||||
top: 10px;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
|
|
@ -112,18 +91,113 @@
|
|||
}
|
||||
}
|
||||
|
||||
// [ COLOR THEMES ]
|
||||
.reactor-container.arc-cyan {
|
||||
--arc-color: 2, 254, 255;
|
||||
--arc-glow: #52fefe;
|
||||
--arc-glow-rgb: 82, 254, 254;
|
||||
--arc-core-border: #1b4e5f;
|
||||
--arc-core-bg: #073c4b;
|
||||
}
|
||||
|
||||
.reactor-container.arc-red {
|
||||
--arc-color: 255, 50, 50;
|
||||
--arc-glow: #ff5050;
|
||||
--arc-glow-rgb: 255, 80, 80;
|
||||
--arc-core-border: #5f1b1b;
|
||||
--arc-core-bg: #4b0707;
|
||||
}
|
||||
|
||||
.reactor-container.arc-orange {
|
||||
--arc-color: 255, 150, 50;
|
||||
--arc-glow: #ff9632;
|
||||
--arc-glow-rgb: 255, 150, 50;
|
||||
--arc-core-border: #5f3c1b;
|
||||
--arc-core-bg: #4b2a07;
|
||||
}
|
||||
|
||||
.reactor-container.arc-yellow {
|
||||
--arc-color: 255, 230, 50;
|
||||
--arc-glow: #ffe632;
|
||||
--arc-glow-rgb: 255, 230, 50;
|
||||
--arc-core-border: #5f5a1b;
|
||||
--arc-core-bg: #4b4507;
|
||||
}
|
||||
|
||||
.reactor-container.arc-green {
|
||||
--arc-color: 50, 255, 100;
|
||||
--arc-glow: #32ff64;
|
||||
--arc-glow-rgb: 50, 255, 100;
|
||||
--arc-core-border: #1b5f2a;
|
||||
--arc-core-bg: #074b15;
|
||||
}
|
||||
|
||||
.reactor-container.arc-blue {
|
||||
--arc-color: 50, 150, 255;
|
||||
--arc-glow: #3296ff;
|
||||
--arc-glow-rgb: 50, 150, 255;
|
||||
--arc-core-border: #1b3c5f;
|
||||
--arc-core-bg: #072a4b;
|
||||
}
|
||||
|
||||
.reactor-container.arc-purple {
|
||||
--arc-color: 180, 100, 255;
|
||||
--arc-glow: #b464ff;
|
||||
--arc-glow-rgb: 180, 100, 255;
|
||||
--arc-core-border: #3c1b5f;
|
||||
--arc-core-bg: #28074b;
|
||||
}
|
||||
|
||||
.reactor-container.arc-pink {
|
||||
--arc-color: 255, 100, 200;
|
||||
--arc-glow: #ff64c8;
|
||||
--arc-glow-rgb: 255, 100, 200;
|
||||
--arc-core-border: #5f1b4a;
|
||||
--arc-core-bg: #4b0735;
|
||||
}
|
||||
|
||||
.reactor-container.arc-white {
|
||||
--arc-color: 255, 255, 255;
|
||||
--arc-glow: #ffffff;
|
||||
--arc-glow-rgb: 255, 255, 255;
|
||||
--arc-core-border: #4a4a4a;
|
||||
--arc-core-bg: #2a2a2a;
|
||||
}
|
||||
|
||||
// [ BACKGROUND GLOW ]
|
||||
.reactor-container::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 1000px;
|
||||
height: 1000px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
rgba(var(--arc-color), 0.20) 0%,
|
||||
rgba(var(--arc-color), 0.15) 30%,
|
||||
rgba(var(--arc-color), 0.10) 40%,
|
||||
rgba(var(--arc-color), 0.04) 50%,
|
||||
transparent 70%
|
||||
);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.5s ease, transform 0.5s ease;
|
||||
}
|
||||
|
||||
// [ CORE ELEMENTS - using CSS vars ]
|
||||
.reactor-container-inner {
|
||||
height: 238px;
|
||||
width: 238px;
|
||||
background-color: #161a1b;
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
box-shadow: 0px 0px 50px 15px rgba(var(--arc-color), 0.3),
|
||||
inset 0px 0px 50px 15px rgba(var(--arc-color), 0.3);
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
// [ Utility classes ]--
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
.circle { border-radius: 50%; }
|
||||
|
||||
.abs-center {
|
||||
position: absolute;
|
||||
|
|
@ -134,30 +208,32 @@
|
|||
margin: auto;
|
||||
}
|
||||
|
||||
// [ Core elements ]--
|
||||
.core-inner {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border: 5px solid #1b4e5f;
|
||||
border: 5px solid var(--arc-core-border);
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 7px 5px #52fefe, 0px 0px 10px 10px #52fefe inset;
|
||||
box-shadow: 0px 0px 7px 5px var(--arc-glow),
|
||||
0px 0px 10px 10px var(--arc-glow) inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
.core-outer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px solid #52fefe;
|
||||
border: 1px solid var(--arc-glow);
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
box-shadow: 0px 0px 2px 1px var(--arc-glow),
|
||||
0px 0px 10px 5px var(--arc-glow) inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
.core-wrapper {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px 4px #52fefe, 0px 0px 6px 2px #52fefe inset;
|
||||
background-color: var(--arc-core-bg);
|
||||
box-shadow: 0px 0px 5px 4px var(--arc-glow),
|
||||
0px 0px 6px 2px var(--arc-glow) inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
|
|
@ -165,17 +241,16 @@
|
|||
width: 220px;
|
||||
height: 220px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 5px 1px #52fefe, 0px 0px 5px 4px #52fefe inset;
|
||||
box-shadow: 0px 0px 5px 1px var(--arc-glow),
|
||||
0px 0px 5px 4px var(--arc-glow) inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
// [ Coil animation ]--
|
||||
.coil-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: 10s infinite linear reactor-anim;
|
||||
transition: animation-duration 0.5s ease;
|
||||
animation: reactor-anim 10s infinite linear;
|
||||
}
|
||||
|
||||
.coil {
|
||||
|
|
@ -185,8 +260,8 @@
|
|||
top: calc(50% - 110px);
|
||||
left: calc(50% - 15px);
|
||||
transform-origin: 15px 110px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px #52fefe inset;
|
||||
background-color: var(--arc-core-bg);
|
||||
box-shadow: 0px 0px 5px var(--arc-glow) inset;
|
||||
}
|
||||
|
||||
@for $i from 1 through 8 {
|
||||
|
|
@ -200,41 +275,112 @@
|
|||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
// [ Arc element ]--
|
||||
.e7 {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
width: 160%;
|
||||
height: 160%;
|
||||
left: -32.5%;
|
||||
top: -32.5%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
border: $size3 solid transparent;
|
||||
width: $arc-container-size;
|
||||
height: $arc-container-size;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border: 6px solid transparent;
|
||||
background: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotateZ(0deg);
|
||||
transition: box-shadow 3s ease, opacity 0.5s ease;
|
||||
transition: opacity 0.5s ease;
|
||||
text-align: center;
|
||||
opacity: 0.3;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.semi_arc_3 {
|
||||
content: "";
|
||||
$offset: calc((100% - #{$arc-spacing}) / 2);
|
||||
|
||||
position: absolute;
|
||||
width: 94%;
|
||||
height: 94%;
|
||||
left: 3%;
|
||||
top: 3%;
|
||||
border: 5px solid #02feff;
|
||||
width: $arc-spacing;
|
||||
height: $arc-spacing;
|
||||
left: $offset;
|
||||
top: $offset;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
animation: rotate 4s linear infinite;
|
||||
text-align: center;
|
||||
animation: rotate 6s linear infinite;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// [ MAIN ARCS ]
|
||||
.e5_1 {
|
||||
border-width: $arc-thickness-1;
|
||||
border-top-color: rgba(var(--arc-color), 0.25);
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: rgba(var(--arc-color), 0.25);
|
||||
animation: rotate 8s linear infinite;
|
||||
}
|
||||
|
||||
.e5_2 {
|
||||
border-width: $arc-thickness-2;
|
||||
border-top-color: transparent;
|
||||
border-right-color: rgba(var(--arc-color), 0.4);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.4);
|
||||
border-left-color: transparent;
|
||||
animation: rotate_anti 6s linear infinite;
|
||||
}
|
||||
|
||||
.e5_3 {
|
||||
border-width: $arc-thickness-3;
|
||||
border-top-color: rgba(var(--arc-color), 0.6);
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
animation: rotate 4s linear infinite;
|
||||
}
|
||||
|
||||
.e5_4 {
|
||||
border-width: $arc-thickness-4;
|
||||
border-top-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: rgba(var(--arc-color), 0.8);
|
||||
border-left-color: transparent;
|
||||
animation: rotate_anti 5s linear infinite;
|
||||
}
|
||||
|
||||
// [ GHOST ARCS ]
|
||||
.e5_1_ghost {
|
||||
border-width: $arc-thickness-1;
|
||||
border-top-color: rgba(var(--arc-color), 0.08);
|
||||
border-right-color: rgba(var(--arc-color), 0.08);
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: transparent;
|
||||
animation: rotate_anti 12s linear infinite;
|
||||
}
|
||||
|
||||
.e5_2_ghost {
|
||||
border-width: $arc-thickness-2;
|
||||
border-top-color: rgba(var(--arc-color), 0.12);
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: rgba(var(--arc-color), 0.12);
|
||||
animation: rotate 9s linear infinite;
|
||||
}
|
||||
|
||||
.e5_3_ghost {
|
||||
border-width: $arc-thickness-3;
|
||||
border-top-color: transparent;
|
||||
border-right-color: rgba(var(--arc-color), 0.18);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.18);
|
||||
border-left-color: transparent;
|
||||
animation: rotate_anti 6s linear infinite;
|
||||
}
|
||||
|
||||
.e5_4_ghost {
|
||||
border-width: $arc-thickness-4;
|
||||
border-top-color: rgba(var(--arc-color), 0.24);
|
||||
border-right-color: transparent;
|
||||
border-bottom-color: transparent;
|
||||
border-left-color: rgba(var(--arc-color), 0.24);
|
||||
animation: rotate 7.5s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% { transform: rotateZ(0deg); }
|
||||
100% { transform: rotateZ(360deg); }
|
||||
|
|
@ -245,167 +391,246 @@
|
|||
100% { transform: rotateZ(-360deg); }
|
||||
}
|
||||
|
||||
// [ Marks ]--
|
||||
// [ MARKS ]
|
||||
.marks {
|
||||
li {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
background: $cshadow;
|
||||
position: absolute;
|
||||
margin-left: 117.5px;
|
||||
margin-top: 113.5px;
|
||||
animation: colour_ease2 3s infinite ease-in-out;
|
||||
}
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.marks li {
|
||||
width: $mark-width;
|
||||
height: $mark-height;
|
||||
background: rgba(var(--arc-color), 0.8);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: calc(-#{$mark-width} / 2);
|
||||
margin-top: calc(-#{$mark-height} / 2);
|
||||
animation: colour_ease2 3s infinite ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes colour_ease2 {
|
||||
0% { background: $marks-color-1; }
|
||||
50% { background: $marks-color-2; }
|
||||
100% { background: $marks-color-1; }
|
||||
0% { background: rgba(var(--arc-color), 1); }
|
||||
50% { background: rgba(var(--arc-color), 0.3); }
|
||||
100% { background: rgba(var(--arc-color), 1); }
|
||||
}
|
||||
|
||||
@for $i from 1 through 60 {
|
||||
.marks li:nth-child(#{$i}) {
|
||||
transform: rotate(#{$i * 6}deg) translateY($arc-radius);
|
||||
transform: rotate(#{$i * 6}deg) translateY(-$arc-radius);
|
||||
}
|
||||
}
|
||||
|
||||
// [ DISCONNECTED state ]--
|
||||
// [ DISCONNECTED ]
|
||||
.reactor-container.disconnected {
|
||||
transform: scale(0.8);
|
||||
transform: scale(0.85);
|
||||
opacity: 0.4;
|
||||
filter: grayscale(0.8) brightness(0.6);
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 20s;
|
||||
filter: grayscale(0.7) brightness(0.6);
|
||||
|
||||
.coil-container { animation-duration: 20s; }
|
||||
.e7 { opacity: 0.3; }
|
||||
|
||||
.e5_1 {
|
||||
border-top-color: rgba(var(--arc-color), 0.1);
|
||||
border-left-color: rgba(var(--arc-color), 0.1);
|
||||
animation: rotate 35s linear infinite;
|
||||
}
|
||||
.e5_2 {
|
||||
border-right-color: rgba(var(--arc-color), 0.15);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.15);
|
||||
animation: rotate_anti 30s linear infinite;
|
||||
}
|
||||
.e5_3 {
|
||||
border-top-color: rgba(var(--arc-color), 0.2);
|
||||
animation: rotate 25s linear infinite;
|
||||
}
|
||||
.e5_4 {
|
||||
border-bottom-color: rgba(var(--arc-color), 0.25);
|
||||
animation: rotate_anti 28s linear infinite;
|
||||
}
|
||||
|
||||
.state-label {
|
||||
opacity: 0.5;
|
||||
.e5_1_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.03);
|
||||
border-right-color: rgba(var(--arc-color), 0.03);
|
||||
animation: rotate_anti 50s linear infinite;
|
||||
}
|
||||
.e5_2_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.05);
|
||||
border-left-color: rgba(var(--arc-color), 0.05);
|
||||
animation: rotate 45s linear infinite;
|
||||
}
|
||||
.e5_3_ghost {
|
||||
border-right-color: rgba(var(--arc-color), 0.06);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.06);
|
||||
animation: rotate_anti 40s linear infinite;
|
||||
}
|
||||
.e5_4_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.08);
|
||||
border-left-color: rgba(var(--arc-color), 0.08);
|
||||
animation: rotate 42s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
// [ IDLE state ]--
|
||||
.reactor-container.disconnected::before {
|
||||
opacity: 0.3;
|
||||
transform: translate(-50%, -50%) scale(0.7);
|
||||
}
|
||||
|
||||
// [ IDLE ]
|
||||
.reactor-container.idle {
|
||||
transform: scale(0.9);
|
||||
transform: scale(0.95);
|
||||
opacity: 0.9;
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 10s;
|
||||
|
||||
.coil-container { animation-duration: 10s; }
|
||||
.e7 { opacity: 0.6; }
|
||||
|
||||
.e5_1 {
|
||||
border-top-color: rgba(var(--arc-color), 0.2);
|
||||
border-left-color: rgba(var(--arc-color), 0.2);
|
||||
animation: rotate 9s linear infinite;
|
||||
}
|
||||
.e5_2 {
|
||||
border-right-color: rgba(var(--arc-color), 0.35);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.35);
|
||||
animation: rotate_anti 15s linear infinite;
|
||||
}
|
||||
.e5_3 {
|
||||
border-top-color: rgba(var(--arc-color), 0.5);
|
||||
animation: rotate 12s linear infinite;
|
||||
}
|
||||
.e5_4 {
|
||||
border-bottom-color: rgba(var(--arc-color), 0.65);
|
||||
animation: rotate_anti 14s linear infinite;
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
.e5_1_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.06);
|
||||
border-right-color: rgba(var(--arc-color), 0.06);
|
||||
animation: rotate_anti 14s linear infinite;
|
||||
}
|
||||
.e5_2_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.1);
|
||||
border-left-color: rgba(var(--arc-color), 0.1);
|
||||
animation: rotate 22s linear infinite;
|
||||
}
|
||||
.e5_3_ghost {
|
||||
border-right-color: rgba(var(--arc-color), 0.15);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.15);
|
||||
animation: rotate_anti 18s linear infinite;
|
||||
}
|
||||
.e5_4_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.2);
|
||||
border-left-color: rgba(var(--arc-color), 0.2);
|
||||
animation: rotate 21s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
// [ ACTIVE state (listening/processing) ]--
|
||||
.reactor-container.idle::before {
|
||||
opacity: 0.7;
|
||||
transform: translate(-50%, -50%) scale(0.9);
|
||||
}
|
||||
|
||||
// [ ACTIVE ]
|
||||
.reactor-container.active {
|
||||
transform: scale(1);
|
||||
transform: scale(1.05);
|
||||
opacity: 1;
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 3s;
|
||||
.coil-container { animation-duration: 3s; }
|
||||
.e7 { opacity: 1; }
|
||||
|
||||
.e5_1 {
|
||||
border-top-color: rgba(var(--arc-color), 0.3);
|
||||
border-left-color: rgba(var(--arc-color), 0.3);
|
||||
animation: rotate 4s linear infinite;
|
||||
}
|
||||
.e5_2 {
|
||||
border-right-color: rgba(var(--arc-color), 0.5);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.5);
|
||||
animation: rotate_anti 3s linear infinite;
|
||||
}
|
||||
.e5_3 {
|
||||
border-top-color: rgba(var(--arc-color), 0.7);
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
.e5_4 {
|
||||
border-bottom-color: rgba(var(--arc-color), 0.9);
|
||||
animation: rotate_anti 2.5s linear infinite;
|
||||
}
|
||||
|
||||
.e5_1_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.1);
|
||||
border-right-color: rgba(var(--arc-color), 0.1);
|
||||
animation: rotate_anti 6s linear infinite;
|
||||
}
|
||||
.e5_2_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.15);
|
||||
border-left-color: rgba(var(--arc-color), 0.15);
|
||||
animation: rotate 4.5s linear infinite;
|
||||
}
|
||||
.e5_3_ghost {
|
||||
border-right-color: rgba(var(--arc-color), 0.2);
|
||||
border-bottom-color: rgba(var(--arc-color), 0.2);
|
||||
animation: rotate_anti 3s linear infinite;
|
||||
}
|
||||
.e5_4_ghost {
|
||||
border-top-color: rgba(var(--arc-color), 0.28);
|
||||
border-left-color: rgba(var(--arc-color), 0.28);
|
||||
animation: rotate 3.75s linear infinite;
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
box-shadow: 0px 0px 70px 25px $colour3, inset 0px 0px 70px 25px $colour3;
|
||||
box-shadow: 0px 0px 70px 25px rgba(var(--arc-color), 0.3),
|
||||
inset 0px 0px 70px 25px rgba(var(--arc-color), 0.3);
|
||||
}
|
||||
|
||||
.core-inner {
|
||||
box-shadow: 0px 0px 15px 10px #52fefe, 0px 0px 20px 15px #52fefe inset;
|
||||
box-shadow: 0px 0px 15px 10px var(--arc-glow),
|
||||
0px 0px 20px 15px var(--arc-glow) inset;
|
||||
}
|
||||
|
||||
.core-outer {
|
||||
box-shadow: 0px 0px 5px 3px #52fefe, 0px 0px 15px 10px #52fefe inset;
|
||||
box-shadow: 0px 0px 5px 3px var(--arc-glow),
|
||||
0px 0px 15px 10px var(--arc-glow) inset;
|
||||
}
|
||||
|
||||
.core-wrapper {
|
||||
box-shadow: 0px 0px 10px 8px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
box-shadow: 0px 0px 10px 8px var(--arc-glow),
|
||||
0px 0px 10px 5px var(--arc-glow) inset;
|
||||
}
|
||||
|
||||
.tunnel {
|
||||
box-shadow: 0px 0px 10px 3px #52fefe, 0px 0px 10px 8px #52fefe inset;
|
||||
box-shadow: 0px 0px 10px 3px var(--arc-glow),
|
||||
0px 0px 10px 8px var(--arc-glow) inset;
|
||||
}
|
||||
|
||||
.e7 {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.e5_1 { animation: rotate 3s linear infinite; }
|
||||
.e5_2 { animation: rotate_anti 2s linear infinite; }
|
||||
.e5_3 { animation: rotate 2s linear infinite; }
|
||||
.e5_4 { animation: rotate_anti 2s linear infinite; }
|
||||
|
||||
.marks li {
|
||||
animation: colour_ease2_active 1s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colour_ease2_active {
|
||||
0% { background: $marks-color-1; box-shadow: 0 0 5px $marks-color-1; }
|
||||
50% { background: $marks-color-2; box-shadow: none; }
|
||||
100% { background: $marks-color-1; box-shadow: 0 0 5px $marks-color-1; }
|
||||
.reactor-container.active::before {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
animation: bg-pulse 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
// [ Pulse animation for listening ]--
|
||||
@keyframes listening-pulse {
|
||||
@keyframes bg-pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.03);
|
||||
opacity: 0.85;
|
||||
transform: translate(-50%, -50%) scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// [ State Label ]
|
||||
|
||||
.state-label {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #52fefe;
|
||||
box-shadow: 0 0 8px #52fefe;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(82, 254, 254, 0.8);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
font-weight: 400;
|
||||
font-family: "Roboto Condensed", sans-serif; // "Trebuchet MS", Arial, Helvetica, sans-serif
|
||||
}
|
||||
|
||||
.reactor-container.active + .state-label {
|
||||
.status-dot {
|
||||
animation: dot-pulse 0.8s ease-in-out infinite;
|
||||
}
|
||||
.label-text {
|
||||
color: #52fefe;
|
||||
}
|
||||
}
|
||||
|
||||
.reactor-container.disconnected + .state-label {
|
||||
.status-dot {
|
||||
background: #445555;
|
||||
box-shadow: none;
|
||||
}
|
||||
.label-text {
|
||||
color: #445555;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dot-pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.5); opacity: 0.7; }
|
||||
@keyframes colour_ease2_active {
|
||||
0% { background: rgba(var(--arc-color), 1); box-shadow: 0 0 5px rgba(var(--arc-color), 1); }
|
||||
50% { background: rgba(var(--arc-color), 0.3); box-shadow: none; }
|
||||
100% { background: rgba(var(--arc-color), 1); box-shadow: 0 0 5px rgba(var(--arc-color), 1); }
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
<script lang="ts">
|
||||
import { translations, translate } from "@/stores"
|
||||
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
|
||||
let searchQuery = ""
|
||||
</script>
|
||||
|
||||
|
|
@ -8,12 +12,11 @@
|
|||
bind:value={searchQuery}
|
||||
type="text"
|
||||
name="q"
|
||||
placeholder="Введите команду или скажите «Джарвис» ..."
|
||||
placeholder={t('search-placeholder')}
|
||||
autocomplete="off"
|
||||
minlength="3"
|
||||
maxlength="30"
|
||||
/>
|
||||
<button type="submit" aria-label="Search"></button>
|
||||
<small>Enter</small>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,346 +1,130 @@
|
|||
<script lang="ts">
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { invoke } from "@tauri-apps/api/core"
|
||||
import { capitalizeFirstLetter } from "@/functions"
|
||||
import { onMount } from "svelte"
|
||||
import {
|
||||
isJarvisRunning,
|
||||
jarvisRamUsage,
|
||||
jarvisCpuUsage,
|
||||
ipcConnected,
|
||||
translations,
|
||||
translate
|
||||
} from "@/stores"
|
||||
|
||||
import {
|
||||
Text,
|
||||
} from "@svelteuidev/core"
|
||||
$: t = (key: string) => translate($translations, key)
|
||||
|
||||
let jarvisStats = { running: false, ram_mb: 0, cpu_usage: 0 }
|
||||
|
||||
let microphoneLabel = ""
|
||||
let wakeWordEngine = ""
|
||||
let microphoneName = "Загрузка..."
|
||||
let wakeWordEngine = "Rustpotter"
|
||||
let sttEngine = "Vosk"
|
||||
// let ramUsage = "-"
|
||||
|
||||
let statsUpdateInterval: number | null = null
|
||||
|
||||
async function updateStats() {
|
||||
try {
|
||||
jarvisStats = await invoke<{running: boolean, ram_mb: number, cpu_usage: number}>("get_jarvis_app_stats")
|
||||
//const usage = await invoke<number>("get_current_ram_usage")
|
||||
//ramUsage = usage.toFixed(2)
|
||||
} catch (err) {
|
||||
console.error("failed to get ram usage:", err)
|
||||
}
|
||||
}
|
||||
let vadInfo = "Snip + ChatGPT"
|
||||
|
||||
onMount(async () => {
|
||||
// start polling ram usage
|
||||
updateStats()
|
||||
statsUpdateInterval = setInterval(updateStats, 5000) as unknown as number
|
||||
|
||||
microphoneName = t('stats-loading')
|
||||
|
||||
try {
|
||||
// load microphone info
|
||||
const micIndex = Number(await invoke<string>("db_read", { key: "selected_microphone" }))
|
||||
microphoneLabel = await invoke<string>("pv_get_audio_device_name", { idx: micIndex })
|
||||
const micIndex = await invoke<string>("db_read", { key: "selected_microphone" })
|
||||
if (micIndex && micIndex !== "-1") {
|
||||
const devices = await invoke<string[]>("pv_get_audio_devices")
|
||||
const idx = parseInt(micIndex)
|
||||
if (devices[idx]) {
|
||||
microphoneName = devices[idx]
|
||||
}
|
||||
} else {
|
||||
microphoneName = t('stats-system-default')
|
||||
}
|
||||
|
||||
// load wake word engine
|
||||
const engine = await invoke<string>("db_read", { key: "selected_wake_word_engine" })
|
||||
wakeWordEngine = capitalizeFirstLetter(engine)
|
||||
wakeWordEngine = await invoke<string>("db_read", { key: "selected_wake_word_engine" }) || "Rustpotter"
|
||||
sttEngine = await invoke<string>("db_read", { key: "selected_stt_engine" }) || "Vosk"
|
||||
vadInfo = await invoke<string>("db_read", { key: "vad" }) || "Vosk"
|
||||
} catch (err) {
|
||||
console.error("failed to load stats:", err)
|
||||
console.error("Failed to load stats:", err)
|
||||
microphoneName = t('stats-not-selected')
|
||||
}
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
if (statsUpdateInterval) {
|
||||
clearInterval(statsUpdateInterval)
|
||||
}
|
||||
})
|
||||
function truncate(str: string, max: number): string {
|
||||
return str.length > max ? str.slice(0, max) + "..." : str
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="statistics">
|
||||
<div class="online">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Микрофон</span>
|
||||
<small title={microphoneLabel}>{microphoneLabel}</small>
|
||||
<div class="stats-bar">
|
||||
<div class="stat-item">
|
||||
<span class="stat-dot" class:active={$isJarvisRunning} style="--color: #22c55e;"></span>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">{t('stats-microphone')}</span>
|
||||
<span class="stat-value" title="{microphoneName}">{truncate(microphoneName, 18)}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="files">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Нейросети</span>
|
||||
<small>{wakeWordEngine} + {sttEngine}</small>
|
||||
|
||||
<div class="stat-item">
|
||||
<span class="stat-dot" class:active={$ipcConnected} style="--color: #f97316;"></span>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">{t('stats-neural-networks')}</span>
|
||||
<span class="stat-value"><span title="Wake Word Engine">{wakeWordEngine}</span> + <span title="Speech To Text">{sttEngine}</span></span>
|
||||
<span class="stat-value-sub">{#if vadInfo != "None"}VAD: {vadInfo}{/if}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="downloads hint--bottom" aria-label="Общее количество скачиваний по всему проекту">
|
||||
<div class="pulse"><div class="wave"></div></div>
|
||||
<div class="info">
|
||||
<span class="num">Ресурсы</span>
|
||||
{#if jarvisStats.running}
|
||||
<small>RAM: {jarvisStats.ram_mb} MB</small>
|
||||
<!--<Text>CPU: {jarvisStats.cpu_usage.toFixed(1)}%</Text>-->
|
||||
{:else}
|
||||
<Text color="gray">-</Text>
|
||||
{/if}
|
||||
|
||||
<div class="stat-item">
|
||||
<span class="stat-dot" class:active={$ipcConnected} style="--color: #3b82f6;"></span>
|
||||
<div class="stat-content">
|
||||
<span class="stat-label">{t('stats-resources')}</span>
|
||||
<span class="stat-value">{#if jarvisRamUsage }RAM {$jarvisRamUsage}mb{:else}...{/if}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.statistics {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
padding: 0 10px;
|
||||
height: 100px;
|
||||
.stats-bar {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 1.1rem 1.5rem;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
& > div {
|
||||
height: 70px;
|
||||
}
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.info {
|
||||
z-index: 10;
|
||||
}
|
||||
.stat-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-top: 0.5rem;
|
||||
background: rgba(70, 70, 70, 0.6);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
// [ Online/Microphone stat ]--
|
||||
& > .online {
|
||||
position: relative;
|
||||
width: 40%;
|
||||
|
||||
$base-color: rgba(0, 191, 8, 1);
|
||||
$mid-color: rgba(0, 191, 8, 0.4);
|
||||
$end-color: rgba(0, 191, 8, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background-color: $base-color;
|
||||
}
|
||||
|
||||
& > .pulse::after {
|
||||
background-color: $base-color;
|
||||
animation: online-cdot linear 3s infinite forwards;
|
||||
}
|
||||
|
||||
& > .pulse .wave {
|
||||
background-color: $mid-color;
|
||||
animation: online-radarWave cubic-bezier(0, 0.54, 0.53, 1) 3s 0s infinite;
|
||||
}
|
||||
|
||||
& > .pulse .wave::after {
|
||||
background-color: $mid-color;
|
||||
animation: online-radarWave cubic-bezier(0, 0.54, 0.53, 1) 3s 0.1s infinite;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #00bf08;
|
||||
}
|
||||
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
width: 130px;
|
||||
max-height: 40px;
|
||||
overflow: hidden;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes online-cdot {
|
||||
0% { opacity: 0.3; background: $base-color; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; background: $end-color; }
|
||||
}
|
||||
|
||||
@keyframes online-radarWave {
|
||||
0% { opacity: 0.1; transform: scale(0); }
|
||||
5% { background: $mid-color; opacity: 1; }
|
||||
100% { transform: scale(1.2); background: $end-color; }
|
||||
}
|
||||
}
|
||||
|
||||
// [ Files/Neural networks stat ]--
|
||||
& > .files {
|
||||
position: relative;
|
||||
width: 35%;
|
||||
|
||||
$base-color: rgba(255, 129, 48, 1);
|
||||
$mid-color: rgba(255, 129, 48, 0.4);
|
||||
$end-color: rgba(255, 129, 48, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background-color: $base-color;
|
||||
}
|
||||
|
||||
& > .pulse::after {
|
||||
background-color: $base-color;
|
||||
animation: files-cdot linear 5s infinite forwards;
|
||||
}
|
||||
|
||||
& > .pulse .wave {
|
||||
background-color: $mid-color;
|
||||
animation: files-radarWave cubic-bezier(0, 0.54, 0.53, 1) 5s 0s infinite;
|
||||
}
|
||||
|
||||
& > .pulse .wave::after {
|
||||
background-color: $mid-color;
|
||||
animation: files-radarWave cubic-bezier(0, 0.54, 0.53, 1) 5s 0.1s infinite;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #ff8130;
|
||||
}
|
||||
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes files-cdot {
|
||||
0% { opacity: 0.3; background: $base-color; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; background: $end-color; }
|
||||
}
|
||||
|
||||
@keyframes files-radarWave {
|
||||
0% { opacity: 0.1; transform: scale(0); }
|
||||
5% { background: $mid-color; transform: scale(0.2); opacity: 1; }
|
||||
100% { transform: scale(0.8); background: $end-color; }
|
||||
}
|
||||
}
|
||||
|
||||
// [ Downloads/Resources stat ]--
|
||||
& > .downloads {
|
||||
position: relative;
|
||||
|
||||
$base-color: rgba(11, 66, 166, 1);
|
||||
$mid-color: rgba(32, 150, 243, 0.4);
|
||||
$end-color: rgba(32, 150, 243, 0);
|
||||
|
||||
& > .pulse::before {
|
||||
background: rgba(32, 150, 243, 1);
|
||||
}
|
||||
|
||||
& > .pulse::after {
|
||||
background: rgba(32, 150, 243, 1);
|
||||
animation: downloads-cdot linear 7s infinite forwards;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
& > .pulse .wave {
|
||||
background-color: $mid-color;
|
||||
animation: downloads-radarWave cubic-bezier(0, 0.54, 0.53, 1) 7s 0s infinite;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
& > .pulse .wave::after {
|
||||
background-color: $mid-color;
|
||||
animation: downloads-radarWave cubic-bezier(0, 0.54, 0.53, 1) 7s 0.1s infinite;
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
& > .info {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
left: 26px;
|
||||
|
||||
& > span.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #1b78a6;
|
||||
}
|
||||
|
||||
& > small {
|
||||
display: block;
|
||||
color: #535a60;
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes downloads-cdot {
|
||||
0% { opacity: 0.3; background: $base-color; }
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; background: $end-color; }
|
||||
}
|
||||
|
||||
@keyframes downloads-radarWave {
|
||||
0% { opacity: 0.1; transform: scale(0); }
|
||||
5% { background: $mid-color; opacity: 1; }
|
||||
100% { transform: scale(0.7); background: $end-color; }
|
||||
}
|
||||
}
|
||||
|
||||
// [ Shared pulse styles ]--
|
||||
.pulse {
|
||||
position: relative;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
margin: 0;
|
||||
left: -43px;
|
||||
top: 0px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.pulse::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pulse::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.pulse .wave {
|
||||
position: absolute;
|
||||
left: 7%;
|
||||
top: 7%;
|
||||
width: 86%;
|
||||
height: 86%;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pulse .wave::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 7%;
|
||||
top: 7%;
|
||||
width: 86%;
|
||||
height: 86%;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
&.active {
|
||||
background: var(--color);
|
||||
box-shadow: 0 0 10px var(--color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 0.7rem;
|
||||
color: rgba(255, 255, 255, 0.42);
|
||||
line-height: 1.35;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.stat-value-sub {
|
||||
font-size: 0.65rem;
|
||||
color: rgba(255, 255, 255, 0.28);
|
||||
}
|
||||
</style>
|
||||
411
frontend/src/components/elements/__ArcReactor.svelte
Normal file
411
frontend/src/components/elements/__ArcReactor.svelte
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
<script lang="ts">
|
||||
import { jarvisState } from "@/stores"
|
||||
|
||||
// map state to class
|
||||
$: stateClass = getStateClass($jarvisState)
|
||||
|
||||
function getStateClass(state: string): string {
|
||||
switch (state) {
|
||||
case "listening":
|
||||
case "processing":
|
||||
return "active"
|
||||
case "idle":
|
||||
return "idle"
|
||||
case "disconnected":
|
||||
default:
|
||||
return "disconnected"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Based on: https://github.com/rembertdesigns/Iron-Man-Arc-Reactor-Pure-CSS -->
|
||||
<!-- and https://codepen.io/FlyingEmu/pen/DZNqEj -->
|
||||
|
||||
<div class="arc-reactor-wrapper">
|
||||
<div id="arc-reactor" class="reactor-container {stateClass}">
|
||||
<div class="reactor-container-inner circle abs-center">
|
||||
<ul class="marks">
|
||||
{#each Array(60) as _, i}
|
||||
<li></li>
|
||||
{/each}
|
||||
</ul>
|
||||
<div class="e7">
|
||||
<div class="semi_arc_3 e5_1">
|
||||
<div class="semi_arc_3 e5_2">
|
||||
<div class="semi_arc_3 e5_3">
|
||||
<div class="semi_arc_3 e5_4"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tunnel circle abs-center"></div>
|
||||
<div class="core-wrapper circle abs-center"></div>
|
||||
<div class="core-outer circle abs-center"></div>
|
||||
<div class="core-inner circle abs-center"></div>
|
||||
<div class="coil-container">
|
||||
{#each Array(8) as _, i}
|
||||
<div class="coil coil-{i + 1}"></div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="state-label">
|
||||
<span class="status-dot"></span>
|
||||
<span class="label-text">
|
||||
{#if $jarvisState === "disconnected"}
|
||||
Отключен
|
||||
{:else if $jarvisState === "idle"}
|
||||
Ожидание
|
||||
{:else if $jarvisState === "listening"}
|
||||
Слушаю
|
||||
{:else if $jarvisState === "processing"}
|
||||
Обработка
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss" global>
|
||||
// [ Variables ]--
|
||||
$arc-radius: 130px;
|
||||
$size3: 6px;
|
||||
$cshadow: rgba(2, 254, 255, 0.8);
|
||||
$marks-color-1: rgba(2, 254, 255, 1);
|
||||
$marks-color-2: rgba(2, 254, 255, 0.3);
|
||||
$colour1: rgba(2, 255, 255, 0.15);
|
||||
$colour3: rgba(2, 255, 255, 0.3);
|
||||
|
||||
// [ Wrapper ]--
|
||||
.arc-reactor-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.state-label {
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #52fefe;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
// [ Base container ]--
|
||||
.reactor-container {
|
||||
width: 300px;
|
||||
height: 320px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
transition: scale 1s ease, opacity 0.5s ease;
|
||||
scale: 0.9;
|
||||
opacity: 0.9;
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
height: 238px;
|
||||
width: 238px;
|
||||
background-color: #161a1b;
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
// [ Utility classes ]--
|
||||
.circle {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.abs-center {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
// [ Core elements ]--
|
||||
.core-inner {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border: 5px solid #1b4e5f;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 7px 5px #52fefe, 0px 0px 10px 10px #52fefe inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
.core-outer {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1px solid #52fefe;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 2px 1px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
.core-wrapper {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px 4px #52fefe, 0px 0px 6px 2px #52fefe inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
.tunnel {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 0px 5px 1px #52fefe, 0px 0px 5px 4px #52fefe inset;
|
||||
transition: box-shadow 0.5s ease;
|
||||
}
|
||||
|
||||
// [ Coil animation ]--
|
||||
.coil-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: 10s infinite linear reactor-anim;
|
||||
transition: animation-duration 0.5s ease;
|
||||
}
|
||||
|
||||
.coil {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 20px;
|
||||
top: calc(50% - 110px);
|
||||
left: calc(50% - 15px);
|
||||
transform-origin: 15px 110px;
|
||||
background-color: #073c4b;
|
||||
box-shadow: 0px 0px 5px #52fefe inset;
|
||||
}
|
||||
|
||||
@for $i from 1 through 8 {
|
||||
.coil-#{$i} {
|
||||
transform: rotate(#{($i - 1) * 45}deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes reactor-anim {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
// [ Arc element ]--
|
||||
.e7 {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 160%;
|
||||
height: 160%;
|
||||
left: -32.5%;
|
||||
top: -32.5%;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
border: $size3 solid transparent;
|
||||
background: transparent;
|
||||
border-radius: 50%;
|
||||
transform: rotateZ(0deg);
|
||||
transition: box-shadow 3s ease, opacity 0.5s ease;
|
||||
text-align: center;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.semi_arc_3 {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 94%;
|
||||
height: 94%;
|
||||
left: 3%;
|
||||
top: 3%;
|
||||
border: 5px solid #02feff;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
animation: rotate 4s linear infinite;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% { transform: rotateZ(0deg); }
|
||||
100% { transform: rotateZ(360deg); }
|
||||
}
|
||||
|
||||
@keyframes rotate_anti {
|
||||
0% { transform: rotateZ(0deg); }
|
||||
100% { transform: rotateZ(-360deg); }
|
||||
}
|
||||
|
||||
// [ Marks ]--
|
||||
.marks {
|
||||
li {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
background: $cshadow;
|
||||
position: absolute;
|
||||
margin-left: 117.5px;
|
||||
margin-top: 113.5px;
|
||||
animation: colour_ease2 3s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colour_ease2 {
|
||||
0% { background: $marks-color-1; }
|
||||
50% { background: $marks-color-2; }
|
||||
100% { background: $marks-color-1; }
|
||||
}
|
||||
|
||||
@for $i from 1 through 60 {
|
||||
.marks li:nth-child(#{$i}) {
|
||||
transform: rotate(#{$i * 6}deg) translateY($arc-radius);
|
||||
}
|
||||
}
|
||||
|
||||
// [ DISCONNECTED state ]--
|
||||
.reactor-container.disconnected {
|
||||
transform: scale(0.8);
|
||||
opacity: 0.4;
|
||||
filter: grayscale(0.8) brightness(0.6);
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
.state-label {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
// [ IDLE state ]--
|
||||
.reactor-container.idle {
|
||||
transform: scale(0.9);
|
||||
opacity: 0.9;
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 10s;
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
box-shadow: 0px 0px 50px 15px $colour3, inset 0px 0px 50px 15px $colour3;
|
||||
}
|
||||
}
|
||||
|
||||
// [ ACTIVE state (listening/processing) ]--
|
||||
.reactor-container.active {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
|
||||
.coil-container {
|
||||
animation-duration: 3s;
|
||||
}
|
||||
|
||||
.reactor-container-inner {
|
||||
box-shadow: 0px 0px 70px 25px $colour3, inset 0px 0px 70px 25px $colour3;
|
||||
}
|
||||
|
||||
.core-inner {
|
||||
box-shadow: 0px 0px 15px 10px #52fefe, 0px 0px 20px 15px #52fefe inset;
|
||||
}
|
||||
|
||||
.core-outer {
|
||||
box-shadow: 0px 0px 5px 3px #52fefe, 0px 0px 15px 10px #52fefe inset;
|
||||
}
|
||||
|
||||
.core-wrapper {
|
||||
box-shadow: 0px 0px 10px 8px #52fefe, 0px 0px 10px 5px #52fefe inset;
|
||||
}
|
||||
|
||||
.tunnel {
|
||||
box-shadow: 0px 0px 10px 3px #52fefe, 0px 0px 10px 8px #52fefe inset;
|
||||
}
|
||||
|
||||
.e7 {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.e5_1 { animation: rotate 3s linear infinite; }
|
||||
.e5_2 { animation: rotate_anti 2s linear infinite; }
|
||||
.e5_3 { animation: rotate 2s linear infinite; }
|
||||
.e5_4 { animation: rotate_anti 2s linear infinite; }
|
||||
|
||||
.marks li {
|
||||
animation: colour_ease2_active 1s infinite ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes colour_ease2_active {
|
||||
0% { background: $marks-color-1; box-shadow: 0 0 5px $marks-color-1; }
|
||||
50% { background: $marks-color-2; box-shadow: none; }
|
||||
100% { background: $marks-color-1; box-shadow: 0 0 5px $marks-color-1; }
|
||||
}
|
||||
|
||||
// [ Pulse animation for listening ]--
|
||||
@keyframes listening-pulse {
|
||||
0%, 100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// [ State Label ]
|
||||
|
||||
.state-label {
|
||||
margin-top: 1.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #52fefe;
|
||||
box-shadow: 0 0 8px #52fefe;
|
||||
}
|
||||
|
||||
.label-text {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(82, 254, 254, 0.8);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
font-weight: 400;
|
||||
font-family: "Roboto Condensed", sans-serif; // "Trebuchet MS", Arial, Helvetica, sans-serif
|
||||
}
|
||||
|
||||
.reactor-container.active + .state-label {
|
||||
.status-dot {
|
||||
animation: dot-pulse 0.8s ease-in-out infinite;
|
||||
}
|
||||
.label-text {
|
||||
color: #52fefe;
|
||||
}
|
||||
}
|
||||
|
||||
.reactor-container.disconnected + .state-label {
|
||||
.status-dot {
|
||||
background: #445555;
|
||||
box-shadow: none;
|
||||
}
|
||||
.label-text {
|
||||
color: #445555;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dot-pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.5); opacity: 0.7; }
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue