chore: ui revamp

This commit is contained in:
2026-07-09 12:08:49 +01:00
parent 0f67d030cf
commit ecc81f0409
16 changed files with 3496 additions and 154 deletions
+11 -2
View File
@@ -1,14 +1,23 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<link rel="icon" href="/tt_logo.ico" />
<link rel="icon" type="image/png" href="/tt_logo.png" />
<link rel="icon" type="image/svg+xml" href="/tt_logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TTRPG Utility App</title>
<meta
name="description"
content="Professional utilities for tabletop RPG players and game masters"
/>
<meta name="theme-color" content="#f7f8fa" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700;800&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
+2944
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

+264 -12
View File
@@ -1,22 +1,274 @@
<script setup>
// App entry with router-view
import { ref, onMounted, onBeforeUnmount, computed } from "vue";
const theme = ref(null); // 'light' | 'dark' | null (system)
function applyThemeClass(t) {
const el = document.documentElement;
el.classList.remove("theme-light", "theme-dark");
if (t === "light") el.classList.add("theme-light");
if (t === "dark") el.classList.add("theme-dark");
}
function setThemePreference(t) {
theme.value = t;
if (t === null) {
localStorage.removeItem("theme");
// remove explicit classes to fallback to system
applyThemeClass(null);
} else {
localStorage.setItem("theme", t);
applyThemeClass(t);
}
}
function toggleTheme() {
const current = effectiveTheme.value;
const next = current === "dark" ? "light" : "dark";
setThemePreference(next);
}
// Track system preference reactively so UI updates immediately when OS theme changes
const systemPrefDark = ref(false);
const effectiveTheme = computed(() => {
if (theme.value === "dark" || theme.value === "light") return theme.value;
return systemPrefDark.value ? "dark" : "light";
});
let mq = null;
function handleMqChange(e) {
systemPrefDark.value = e.matches;
}
onMounted(() => {
const stored = localStorage.getItem("theme");
if (stored === "dark" || stored === "light") {
theme.value = stored;
applyThemeClass(stored);
} else {
theme.value = null; // follow system
}
// set up system preference watcher
mq = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");
if (mq) {
systemPrefDark.value = mq.matches;
mq.addEventListener("change", handleMqChange);
}
});
onBeforeUnmount(() => {
if (mq) mq.removeEventListener("change", handleMqChange);
});
</script>
<template>
<nav class="nav">
<router-link to="/">Home</router-link>
<router-link to="/name-generator">Name Generator</router-link>
<router-link to="/attribute-calculator">Attribute Calculator</router-link>
</nav>
<main>
<router-view />
</main>
<div class="app-shell">
<aside class="sidebar">
<div class="brand">
<img src="/tt_logo.png" alt="TTRPG Utilities logo" class="app-logo" />
<div class="brand-text">
<h1>TTRPG Utilities</h1>
</div>
</div>
<nav class="nav-vertical">
<router-link to="/" exact>Home</router-link>
<router-link to="/name-generator">Name Generator</router-link>
<router-link to="/attribute-calculator"
>Attribute Calculator</router-link
>
<a href="https://foundry.fernandovideira.com">Foundry</a>
</nav>
<div class="sidebar-footer">
<div
style="
display: flex;
align-items: center;
gap: 8px;
justify-content: space-between;
"
>
<small class="muted">v0.1 · production</small>
<small class="muted">by KrioX</small>
<button
class="theme-toggle"
@click="toggleTheme"
:aria-pressed="effectiveTheme === 'dark'"
>
<span class="visually-hidden">Toggle theme</span>
<svg
class="icon icon--sm"
v-if="effectiveTheme === 'dark'"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z" />
</svg>
<svg
class="icon icon--sm"
v-else
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
stroke-linejoin="round"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2" />
<path d="M12 20v2" />
<path d="M2 12h2" />
<path d="M20 12h2" />
<path d="M4.93 4.93l1.41 1.41" />
<path d="M17.66 17.66l1.41 1.41" />
<path d="M4.93 19.07l1.41-1.41" />
<path d="M17.66 6.34l1.41-1.41" />
</svg>
</button>
</div>
</div>
</aside>
<div class="content">
<div class="content-inner">
<router-view />
</div>
</div>
</div>
</template>
<style scoped>
.nav {
.app-shell {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
min-height: 100vh;
}
.sidebar {
width: var(--sidebar-width);
background: transparent;
padding: calc(var(--spacing-lg)) var(--spacing-md);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
gap: var(--spacing-md);
align-items: stretch;
}
.brand h1 {
margin: 0 0 4px 0;
font-size: 1rem;
font-weight: 700;
color: var(--text-primary);
}
.brand {
display: flex;
align-items: center;
gap: 10px;
}
.app-logo {
width: 36px;
height: 36px;
display: block;
border-radius: 8px;
}
.brand-text h1 {
margin: 0;
font-size: 0.95rem;
}
.brand .tagline {
margin: 0;
font-size: 0.85rem;
color: var(--text-secondary);
}
.nav-vertical {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: var(--spacing-md);
}
.nav-vertical a {
display: inline-block;
padding: 8px 12px;
border-radius: 8px;
color: var(--text-primary);
text-decoration: none;
font-weight: 600;
}
.nav-vertical a.router-link-exact-active {
background: var(--color-accent);
color: white;
}
.sidebar-footer {
margin-top: auto;
color: var(--text-secondary);
font-size: 0.85rem;
}
.theme-toggle {
background: transparent;
border: 1px solid var(--border);
padding: 6px;
width: 36px;
height: 36px;
border-radius: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: var(--text-primary);
}
.theme-toggle:hover {
background: var(--surface-hover);
}
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap;
border: 0;
padding: 0;
margin: -1px;
}
.content {
flex: 1;
padding: var(--spacing-xl) var(--spacing-lg);
background: transparent;
}
.content-inner {
max-width: var(--content-max-width);
margin: 0 auto;
}
@media (max-width: 900px) {
.sidebar {
display: none;
}
.content {
padding: var(--spacing-lg);
}
}
</style>
+152 -31
View File
@@ -1,33 +1,144 @@
/* Professional minimalist dark theme */
:root {
--color-background: #0f1419;
--color-background-soft: #1a1f26;
--color-background-mute: #1e242c;
--color-card: #1a1f26;
--color-card-border: #252b35;
--color-border: #2e3440;
--color-border-hover: #3d4450;
--color-heading: #ffffff;
--color-text: #d8dee9;
--color-text-muted: #a8b2c1;
--color-accent: #5e81ac;
--color-accent-hover: #4c6b91;
--color-accent-bg: rgba(94, 129, 172, 0.1);
--color-success: #a3be8c;
--color-warning: #ebcb8b;
--color-error: #bf616a;
--radius-sm: 0.5rem;
--radius-md: 0.75rem;
--radius-lg: 1rem;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.15);
--shadow-lg: 0 8px 25px rgba(0, 0, 0, 0.2);
--color-background: #f7f8fa; /* page background */
--color-background-soft: #ffffff; /* card backgrounds */
--color-background-mute: #f3f4f6; /* subtle surfaces */
--color-card: #ffffff;
--color-card-border: #eaecf0;
--color-border: #e6e9ee;
--color-border-hover: #d6d9df;
--color-heading: #0f1724;
--color-text: #111827;
--color-text-muted: #6b7280;
--color-accent: #007aff; /* Apple-like blue */
--color-accent-hover: #0064d6;
--color-accent-bg: rgba(0, 122, 255, 0.08);
--color-success: #16a34a;
--color-warning: #d97706;
--color-error: #dc2626;
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--shadow-sm: 0 4px 10px rgba(15, 23, 42, 0.06);
--shadow-md: 0 10px 30px rgba(15, 23, 42, 0.06);
--shadow-lg: 0 20px 60px rgba(15, 23, 42, 0.08);
--spacing-xs: 0.5rem;
--spacing-sm: 0.75rem;
--spacing-md: 1rem;
--spacing-lg: 1.5rem;
--spacing-lg: 1.25rem;
--spacing-xl: 2rem;
--spacing-2xl: 3rem;
/* Compatibility aliases used by some components */
--border: var(--color-border);
--border-hover: var(--color-border-hover);
--text-primary: var(--color-heading);
--text-secondary: var(--color-text-muted);
--surface: var(--color-card);
--surface-hover: var(--color-background-mute);
--surface-secondary: var(--color-background-soft);
--color-primary: var(--color-accent);
--color-primary-alpha: var(--color-accent-bg);
--border-radius: var(--radius-md);
}
/* Icon utilities: consistent sizing, stroke/fill behavior, and spacing */
svg.icon {
width: var(--icon-size, 18px);
height: var(--icon-size, 18px);
display: inline-block;
vertical-align: middle;
color: inherit;
}
svg.icon [stroke] {
stroke: currentColor !important;
stroke-width: 1.5 !important;
vector-effect: non-scaling-stroke;
}
svg.icon [fill]:not([fill="none"]) {
fill: currentColor !important;
}
/* smaller variant */
.icon--sm {
--icon-size: 14px;
}
.icon--lg {
--icon-size: 20px;
}
/* spacing when icon appears inside buttons */
.button-secondary svg.icon {
margin-right: var(--spacing-xs);
}
.reset-btn svg.icon,
.copy-icon.icon {
margin: 0;
}
/* Dark theme: follow system preference */
@media (prefers-color-scheme: dark) {
:root {
--color-background: #0f1419;
--color-background-soft: #1a1f26;
--color-background-mute: #1e242c;
--color-card: #111214;
--color-card-border: #121417;
--color-border: #1f2730;
--color-border-hover: #2e3440;
--color-heading: #ffffff;
--color-text: #d8dee9;
--color-text-muted: #9ca3af;
--color-accent: #5e81ac;
--color-accent-hover: #4c6b91;
--color-accent-bg: rgba(94, 129, 172, 0.08);
--color-success: #a3be8c;
--color-warning: #ebcb8b;
--color-error: #bf616a;
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 6px 20px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.6);
}
}
/* Manual overrides when user explicitly chooses theme */
.theme-dark {
--color-background: #0f1419;
--color-background-soft: #1a1f26;
--color-background-mute: #1e242c;
--color-card: #111214;
--color-card-border: #121417;
--color-border: #1f2730;
--color-border-hover: #2e3440;
--color-heading: #ffffff;
--color-text: #d8dee9;
--color-text-muted: #9ca3af;
--color-accent: #5e81ac;
--color-accent-hover: #4c6b91;
--color-accent-bg: rgba(94, 129, 172, 0.08);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 6px 20px rgba(0, 0, 0, 0.5);
}
.theme-light {
--color-background: #f7f8fa;
--color-background-soft: #ffffff;
--color-background-mute: #f3f4f6;
--color-card: #ffffff;
--color-card-border: #eaecf0;
--color-border: #e6e9ee;
--color-border-hover: #d6d9df;
--color-heading: #0f1724;
--color-text: #111827;
--color-text-muted: #6b7280;
--color-accent: #007aff;
--color-accent-hover: #0064d6;
--color-accent-bg: rgba(0, 122, 255, 0.08);
--shadow-sm: 0 4px 10px rgba(15, 23, 42, 0.06);
--shadow-md: 0 10px 30px rgba(15, 23, 42, 0.06);
}
*,
@@ -42,11 +153,14 @@ body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.3s ease, background-color 0.3s ease;
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
sans-serif;
transition:
color 0.2s ease,
background-color 0.2s ease;
font-family:
-apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI",
Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: 15px;
line-height: 1.6;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
@@ -54,12 +168,17 @@ body {
#app {
width: 100%;
min-height: 100vh;
max-width: 800px;
margin: 0 auto;
padding: var(--spacing-xl) var(--spacing-lg);
max-width: 100%;
margin: 0;
padding: 0;
background: var(--color-background);
}
:root {
--sidebar-width: 260px;
--content-max-width: 1100px;
}
/* Typography */
h1 {
color: var(--color-heading);
@@ -133,7 +252,9 @@ select {
color: var(--color-text);
padding: var(--spacing-sm) var(--spacing-md);
font-size: 0.95rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
transition:
border-color 0.2s ease,
box-shadow 0.2s ease;
width: 100%;
}
+4 -3
View File
@@ -2,10 +2,11 @@
/* Main app container override for professional layout */
#app {
background: var(--color-background);
border-radius: 0;
box-shadow: none;
background: var(--surface);
border-radius: 12px;
box-shadow: var(--shadow-md);
min-height: 100vh;
margin-top: 24px;
}
/* Professional link styling */
+17 -16
View File
@@ -1,28 +1,29 @@
.attribute-calculator {
background: var(--color-card);
border: 1px solid var(--color-card-border);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
max-width: 600px;
max-width: 680px;
margin: 0 auto;
padding: var(--spacing-2xl);
padding: var(--spacing-xl);
}
.attribute-calculator h2 {
margin-bottom: var(--spacing-xl);
margin-bottom: var(--spacing-lg);
text-align: center;
}
.inputs {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
margin-bottom: var(--spacing-2xl);
margin-bottom: var(--spacing-xl);
}
.inputs label {
color: var(--color-text);
color: var(--text-primary);
font-size: 0.95rem;
font-weight: 500;
font-weight: 600;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
@@ -95,25 +96,25 @@
}
.results {
background: var(--color-background-soft);
border: 1px solid var(--color-border);
background: var(--surface-secondary);
border: 1px solid var(--border);
border-radius: var(--radius-md);
padding: var(--spacing-xl);
padding: var(--spacing-lg);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: var(--spacing-lg);
}
.results p {
font-weight: 600;
font-size: 1.1rem;
font-weight: 700;
font-size: 1.05rem;
margin: 0;
text-align: center;
padding: var(--spacing-md);
background: var(--color-card);
border: 1px solid var(--color-card-border);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--color-accent);
color: var(--color-primary);
box-shadow: var(--shadow-sm);
}
+53 -32
View File
@@ -1,13 +1,28 @@
<template>
<section class="home">
<h1>Utility App for TTRPGs</h1>
<p>Explore useful tools to enhance your tabletop RPG experience:</p>
<p class="lead">Small, focused tools to speed up tabletop prep: generate names, balance attributes, and connect to Foundry VTT resources.</p>
<p class="intro">This app collects lightweight utilities for GMs and players. Use the sidebar to navigate between tools each page offers quick controls and sensible defaults so you can get results without fumbling through settings.</p>
<ul class="tools-list">
<li><router-link to="/name-generator">Name Generator</router-link></li>
<li>
<router-link to="/attribute-calculator"
>Attribute Calculator</router-link
>
<div class="tool-card">
<h3>Name Generator</h3>
<p class="muted">Generate character names by race, language and gender. Create reusable lists and quickly copy results into your notes.</p>
</div>
</li>
<li>
<div class="tool-card">
<h3>Attribute Calculator</h3>
<p class="muted">Experiment with point-buy and rolled stats, see derived modifiers, and compare different allocation strategies.</p>
</div>
</li>
<li>
<div class="tool-card">
<h3>Foundry</h3>
<p class="muted">Quick link and notes for integrating with Foundry VTT (external). Use this as a launch/reference point rather than a full integration.</p>
</div>
</li>
</ul>
</section>
@@ -21,18 +36,20 @@
.home {
text-align: center;
padding: var(--spacing-2xl) 0;
max-width: 600px;
max-width: 720px;
margin: 0 auto;
}
.home h1 {
margin-bottom: var(--spacing-md);
margin-bottom: var(--spacing-sm);
font-size: 2rem;
letter-spacing: -0.02em;
}
.home p {
color: var(--color-text-muted);
font-size: 1.1rem;
margin-bottom: var(--spacing-2xl);
color: var(--text-secondary);
font-size: 1rem;
margin-bottom: var(--spacing-lg);
line-height: 1.6;
}
@@ -40,56 +57,60 @@
list-style: none;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: var(--spacing-lg);
margin-top: var(--spacing-xl);
}
.tools-list li {
transform: translateY(0);
transition: transform 0.2s ease;
transition: transform 0.16s ease;
}
.tools-list li:hover {
transform: translateY(-2px);
transform: translateY(-4px);
}
.tools-list a {
.tools-list .tool-card {
display: block;
font-size: 1.1rem;
color: var(--color-heading);
background: var(--color-card);
border: 1px solid var(--color-card-border);
font-size: 1.05rem;
color: var(--text-primary);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
padding: var(--spacing-lg);
text-decoration: none;
font-weight: 600;
font-weight: 700;
box-shadow: var(--shadow-md);
transition: all 0.2s ease;
transition: all 0.16s ease;
position: relative;
overflow: hidden;
}
.tools-list a::before {
.tools-list .tool-card::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 2px;
background: var(--color-accent);
transition: left 0.3s ease;
height: 3px;
background: linear-gradient(
90deg,
var(--color-primary),
var(--color-accent-hover)
);
transition: left 0.28s ease;
}
.tools-list a:hover::before {
.tools-list li:hover .tool-card::before {
left: 0;
}
.tools-list a:hover {
background: var(--color-background-mute);
border-color: var(--color-accent);
.tools-list li:hover .tool-card {
background: var(--surface-hover);
border-color: var(--color-primary);
box-shadow: var(--shadow-lg);
color: var(--color-accent);
color: var(--color-primary);
}
@media (max-width: 768px) {
@@ -98,8 +119,8 @@
gap: var(--spacing-md);
}
.tools-list a {
padding: var(--spacing-lg);
.tools-list .tool-card {
padding: var(--spacing-md);
}
}
</style>
+14 -11
View File
@@ -9,6 +9,7 @@
:disabled="generators.length >= 6"
>
<svg
class="icon"
width="16"
height="16"
viewBox="0 0 24 24"
@@ -40,6 +41,7 @@
:disabled="generators.length <= 1"
>
<svg
class="icon"
width="16"
height="16"
viewBox="0 0 24 24"
@@ -81,7 +83,6 @@ import { ref } from "vue";
import NameGeneratorCard from "./NameGeneratorCard.vue";
const generators = ref([1, 2, 3, 4]);
let nextId = 5;
// Toast state
const showToast = ref(false);
@@ -96,13 +97,15 @@ function handleToast(event) {
() => {
showToast.value = false;
},
event.type === "error" ? 4000 : 2000
event.type === "error" ? 4000 : 2000,
);
}
function addGenerator() {
if (generators.value.length < 6) {
generators.value.push(nextId++);
// compute next id based on current count so removed ids are reused
const id = generators.value.length + 1;
generators.value.push(id);
}
}
@@ -115,7 +118,7 @@ function removeGenerator() {
<style scoped>
.name-generator-layout {
padding: var(--spacing-lg);
padding: calc(var(--spacing-lg) + 0.25rem);
max-width: 1400px;
margin: 0 auto;
}
@@ -145,13 +148,13 @@ function removeGenerator() {
align-items: center;
gap: var(--spacing-xs);
padding: var(--spacing-sm) var(--spacing-md);
background: var(--surface);
background: transparent;
color: var(--text-primary);
border: 1px solid var(--border);
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 500;
transition: all 0.2s ease;
font-weight: 600;
transition: all 0.12s ease;
}
.button-secondary:hover:not(:disabled) {
@@ -259,15 +262,15 @@ function removeGenerator() {
}
.toast-success {
background: #10b981;
background: var(--color-success);
color: white;
border: 1px solid #059669;
border: 1px solid rgba(0, 0, 0, 0.06);
}
.toast-error {
background: #ef4444;
background: var(--color-error);
color: white;
border: 1px solid #dc2626;
border: 1px solid rgba(0, 0, 0, 0.06);
}
@keyframes slideInRight {
+17 -16
View File
@@ -4,6 +4,7 @@
<h3>Generator {{ cardId }}</h3>
<button class="reset-btn" @click="resetGenerator" title="Reset Generator">
<svg
class="icon icon--sm"
width="16"
height="16"
viewBox="0 0 24 24"
@@ -102,7 +103,7 @@
>
{{ name }}
<svg
class="copy-icon"
class="copy-icon icon icon--sm"
width="14"
height="14"
viewBox="0 0 24 24"
@@ -241,7 +242,7 @@ async function generate() {
} else {
console.error(
"Failed to fetch modern names:",
data.message || "Unknown error"
data.message || "Unknown error",
);
emit("show-toast", {
message: `Error: ${
@@ -272,7 +273,7 @@ async function generate() {
} else {
console.error(
"Failed to fetch fantasy names:",
data.message || "Unknown error"
data.message || "Unknown error",
);
emit("show-toast", {
message: `Error: ${
@@ -319,15 +320,15 @@ async function copyToClipboard(name) {
<style scoped>
.name-generator-card {
background: var(--color-card);
border: 1px solid var(--color-card-border);
background: var(--surface);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
padding: var(--spacing-md);
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
min-height: 400px;
min-height: 320px;
position: relative;
}
@@ -407,10 +408,10 @@ async function copyToClipboard(name) {
}
.switch-button.active {
background: #3b82f6;
background: var(--color-primary);
color: white;
font-weight: 600;
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.3);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12);
}
.switch-button + .switch-button {
@@ -443,21 +444,21 @@ select:focus {
.button-generate {
padding: var(--spacing-sm) var(--spacing-md);
background: #3b82f6;
background: var(--color-primary);
color: white;
border: none;
border-radius: var(--radius-md);
border-radius: var(--border-radius);
cursor: pointer;
font-weight: 600;
transition: all 0.2s ease;
transition: all 0.16s ease;
margin-top: var(--spacing-sm);
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.2);
box-shadow: var(--shadow-sm);
}
.button-generate:hover:not(:disabled) {
background: #2563eb;
background: var(--color-accent-hover);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(59, 130, 246, 0.3);
box-shadow: var(--shadow-md);
}
.button-generate:disabled {
@@ -484,11 +485,11 @@ select:focus {
border: 1px solid var(--border);
border-radius: var(--border-radius);
cursor: pointer;
transition: all 0.2s ease;
transition: all 0.14s ease;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 0.9rem;
font-size: 0.95rem;
}
.name-item:hover {
+4 -4
View File
@@ -1,7 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<circle cx="9" cy="8" r="2" />
<circle cx="15" cy="8" r="2" />
<path d="M7 17c1.5-2 4.5-2 7 0" />
</svg>
</template>
+5 -4
View File
@@ -1,7 +1,8 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
<path d="M14 2v6h6" />
<path d="M8 12h8" />
<path d="M8 16h8" />
</svg>
</template>
+3 -4
View File
@@ -1,7 +1,6 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<circle cx="12" cy="12" r="3" />
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V20a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06A2 2 0 1 1 6.4 16.9l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H4a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82L5.2 6.4A2 2 0 1 1 8 3.57l.06.06a1.65 1.65 0 0 0 1.82.33H10a1.65 1.65 0 0 0 1-1.51V2a2 2 0 1 1 4 0v.09c0 .6.4 1.12 1 1.51h.12a1.65 1.65 0 0 0 1.82-.33L19.4 4A2 2 0 1 1 22.23 6.58l-.06.06a1.65 1.65 0 0 0-.33 1.82V10c.6 0 1.12.4 1.51 1h.09a2 2 0 1 1 0 4h-.09c-.39.6-.91 1-1.51 1z" />
</svg>
</template>
+4 -4
View File
@@ -1,7 +1,7 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M12 2a10 10 0 0 0-7.07 17.07A10 10 0 1 0 12 2z" />
<path d="M12 8v4" />
<path d="M12 16h.01" />
</svg>
</template>
+4 -15
View File
@@ -1,19 +1,8 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M14.7 10.3l-1-1 2-2 1 1a3 3 0 0 1 0 4.24l-1.24 1.24" />
<path d="M11 13l-6 6" />
<path d="M7 15l6-6" />
</svg>
</template>