feat: change css to use single brand color, only new browsers support
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": ["stylelint-config-recommended", "stylelint-config-html/svelte"],
|
||||||
|
"rules": {
|
||||||
|
"selector-pseudo-class-no-unknown": [
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
"ignorePseudoClasses": ["global"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ignoreFiles": [
|
||||||
|
".github/**/*",
|
||||||
|
".svelte-kit/**/*",
|
||||||
|
"build/**/*",
|
||||||
|
"coverage/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"node_modules/**/*",
|
||||||
|
"reference/**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
Generated
+1658
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"lint:css": "stylelint '**/*.{css,svelte}",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
@@ -32,8 +33,12 @@
|
|||||||
"@types/node": "^25.1.0",
|
"@types/node": "^25.1.0",
|
||||||
"@vitest/coverage-istanbul": "^4.0.18",
|
"@vitest/coverage-istanbul": "^4.0.18",
|
||||||
"@vitest/ui": "^4.0.18",
|
"@vitest/ui": "^4.0.18",
|
||||||
|
"glob": "^13.0.2",
|
||||||
"jsdom": "^28.0.0",
|
"jsdom": "^28.0.0",
|
||||||
"konva": "^10.2.0",
|
"konva": "^10.2.0",
|
||||||
|
"stylelint": "^17.2.0",
|
||||||
|
"stylelint-config-html": "^1.1.0",
|
||||||
|
"stylelint-config-recommended": "^18.0.0",
|
||||||
"svelte": "^5.48.2",
|
"svelte": "^5.48.2",
|
||||||
"svelte-check": "^4.3.5",
|
"svelte-check": "^4.3.5",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { readFileSync } from "fs";
|
||||||
|
import { globSync } from "glob";
|
||||||
|
|
||||||
|
const includePatterns = ["src/**/*.svelte", "src/**/*.css"];
|
||||||
|
|
||||||
|
const files = globSync(includePatterns);
|
||||||
|
|
||||||
|
const defined = new Set();
|
||||||
|
const used = new Set();
|
||||||
|
const usageLocations = [];
|
||||||
|
|
||||||
|
files.forEach((file) => {
|
||||||
|
const content = readFileSync(file, "utf-8");
|
||||||
|
const lines = content.split("\n");
|
||||||
|
|
||||||
|
const defMatches = content.matchAll(/--[\w-]+(?=\s*:)/g);
|
||||||
|
for (const m of defMatches) {
|
||||||
|
defined.add(m[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.forEach((line, idx) => {
|
||||||
|
const varMatches = line.matchAll(/var\(\s*--[\w-]+\s*\)/g);
|
||||||
|
for (const match of varMatches) {
|
||||||
|
const varName = match[0].slice(4, -1).trim();
|
||||||
|
used.add(varName);
|
||||||
|
|
||||||
|
usageLocations.push({
|
||||||
|
file,
|
||||||
|
line: idx + 1,
|
||||||
|
column: match.index + 1,
|
||||||
|
varName,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let hasUndefined = false;
|
||||||
|
const undefinedErrors = usageLocations.filter((loc) => !defined.has(loc.varName));
|
||||||
|
|
||||||
|
undefinedErrors.forEach(({ file, line, column, varName }) => {
|
||||||
|
console.error(`❌ ${file}:${line}:${column} — не определена CSS-переменная "${varName}"`);
|
||||||
|
hasUndefined = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const unused = [...defined].filter((varName) => !used.has(varName));
|
||||||
|
if (unused.length > 0) {
|
||||||
|
console.warn("\n⚠️ Объявлены, но нигде не используются:");
|
||||||
|
unused.forEach((varName) => console.warn(` ${varName}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasUndefined) {
|
||||||
|
console.error("\n❌ Найдены неопределённые переменные. Исправьте их.");
|
||||||
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
if (unused.length === 0) {
|
||||||
|
console.log("\n✅ Все CSS-переменные определены и используются.");
|
||||||
|
} else {
|
||||||
|
console.log("\n✅ Неопределённых переменных нет, но есть неиспользуемые (см. предупреждения).");
|
||||||
|
}
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
+81
@@ -0,0 +1,81 @@
|
|||||||
|
:root {
|
||||||
|
--brand-main: #86efac;
|
||||||
|
--brand-alt: oklch(from var(--brand-main) l c calc(h + 36));
|
||||||
|
|
||||||
|
--action-lightness: 0.6;
|
||||||
|
--action-chroma: 0.18;
|
||||||
|
--hover-step: -0.08;
|
||||||
|
|
||||||
|
--danger-base: #ef4444;
|
||||||
|
|
||||||
|
--surface-main: oklch(from var(--brand-main) 0.99 0.02 h);
|
||||||
|
--surface-subtle: oklch(from var(--brand-main) 0.95 0.1 h);
|
||||||
|
--surface-elevated: oklch(from var(--brand-main) 0.99 0.05 h);
|
||||||
|
--surface-control: oklch(from var(--brand-main) 0.85 0.04 h);
|
||||||
|
--surface-hover: oklch(from var(--brand-main) 0.9 0.1 h);
|
||||||
|
|
||||||
|
--action-primary: oklch(from var(--brand-main) var(--action-lightness) var(--action-chroma) h);
|
||||||
|
--action-primary-hover: oklch(from var(--action-primary) calc(l - var(--hover-step)) c h);
|
||||||
|
--action-secondary: oklch(from var(--brand-alt) var(--action-lightness) var(--action-chroma) h);
|
||||||
|
--action-secondary-hover: oklch(from var(--action-secondary) calc(l - var(--hover-step)) c h);
|
||||||
|
|
||||||
|
--text-main: oklch(from var(--brand-main) 0.25 0.02 h);
|
||||||
|
--text-muted: oklch(from var(--brand-main) 0.45 0.02 h);
|
||||||
|
--text-disabled: oklch(from var(--brand-main) 0.7 0.01 h);
|
||||||
|
--text-action: oklch(from var(--brand-main) 0.99 0.02 h);
|
||||||
|
|
||||||
|
--border-main: oklch(from var(--brand-main) 0.9 0.04 h);
|
||||||
|
--border-strong: oklch(from var(--brand-main) 0.8 0.06 h);
|
||||||
|
|
||||||
|
--shadow: 0 1px 3px oklch(from var(--brand-main) 0.2 0.05 h / 0.1);
|
||||||
|
--shadow-md: 0 4px 12px oklch(from var(--brand-main) 0.2 0.05 h / 0.1);
|
||||||
|
|
||||||
|
--radius: 8px;
|
||||||
|
--transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--action-lightness: 0.5;
|
||||||
|
--action-chroma: 0.18;
|
||||||
|
|
||||||
|
--surface-main: oklch(from var(--brand-main) 0.12 0.02 h);
|
||||||
|
--surface-subtle: oklch(from var(--brand-main) 0.16 0.03 h);
|
||||||
|
--surface-elevated: oklch(from var(--brand-main) 0.2 0.03 h);
|
||||||
|
--surface-control: oklch(from var(--brand-main) 0.4 0.06 h);
|
||||||
|
--surface-hover: oklch(from var(--brand-main) 0.24 0.04 h);
|
||||||
|
|
||||||
|
--action-primary-hover: oklch(from var(--action-primary) calc(l + var(--hover-step)) c h);
|
||||||
|
--action-secondary-hover: oklch(from var(--action-secondary) calc(l + var(--hover-step)) c h);
|
||||||
|
|
||||||
|
--text-main: oklch(from var(--brand-main) 0.94 0.01 h);
|
||||||
|
--text-muted: oklch(from var(--brand-main) 0.75 0.02 h);
|
||||||
|
--text-disabled: oklch(from var(--brand-main) 0.5 0.02 h);
|
||||||
|
--text-action: oklch(from var(--brand-main) 0.12 0.02 h);
|
||||||
|
|
||||||
|
--border-main: oklch(from var(--brand-main) 0.22 0.04 h);
|
||||||
|
--border-strong: oklch(from var(--brand-main) 0.3 0.06 h);
|
||||||
|
|
||||||
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
|
||||||
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
text-indent: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif;
|
||||||
|
background: var(--surface-main);
|
||||||
|
color: var(--text-main);
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 16px;
|
||||||
|
transition: var(--transition);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 16 / 5;
|
aspect-ratio: 16 / 5;
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-subtle);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: crosshair;
|
cursor: crosshair;
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
.crop-box {
|
.crop-box {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
border: 2px solid var(--accent-primary);
|
border: 2px solid var(--action-primary);
|
||||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
|
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
|
||||||
cursor: move;
|
cursor: move;
|
||||||
display: none;
|
display: none;
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
width: 10px;
|
width: 10px;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background: white;
|
background: white;
|
||||||
border: 2px solid var(--accent-primary);
|
border: 2px solid var(--action-primary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,12 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.header {
|
.header {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, var(--action-primary) 0%, var(--action-secondary) 100%);
|
||||||
color: white;
|
color: var(--text-action);
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-content {
|
.header-content {
|
||||||
|
|||||||
@@ -33,19 +33,20 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.card {
|
.card {
|
||||||
background: var(--bg-card);
|
background: var(--surface-elevated);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-primary);
|
color: var(--text-main);
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -7,6 +7,3 @@
|
|||||||
<ImageManager />
|
<ImageManager />
|
||||||
<PreviewManager />
|
<PreviewManager />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.setting-label {
|
.setting-label {
|
||||||
color: var(--text-secondary);
|
color: var(--text-muted);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,3 @@
|
|||||||
<TextManager />
|
<TextManager />
|
||||||
<TextConfig />
|
<TextConfig />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
<style>
|
<style>
|
||||||
.panel-indicator {
|
.panel-indicator {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--text-secondary);
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
min-width: 50px;
|
min-width: 50px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
<div class="panel-actions">
|
<div class="panel-actions">
|
||||||
<Button label="Скачать всё" ariaLabel="Download all" icon={Download} onclick={downloadAll} />
|
<Button label="Скачать всё" ariaLabel="Download all" icon={Download} onclick={downloadAll} />
|
||||||
<Button label="Скачать" ariaLabel="Download current" type="outline" icon={Download} onclick={downloadCurrent} />
|
<Button label="Скачать" ariaLabel="Download current" type="secondary" icon={Download} onclick={downloadCurrent} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel-display {
|
.panel-display {
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-subtle);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -110,7 +110,6 @@
|
|||||||
.empty-state {
|
.empty-state {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 32px 16px;
|
padding: 32px 16px;
|
||||||
color: var(--text-tertiary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty-state :global(svg) {
|
.empty-state :global(svg) {
|
||||||
|
|||||||
@@ -24,30 +24,30 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-subtle);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-item:hover {
|
.text-item:hover {
|
||||||
border-color: var(--border-hover);
|
border-color: var(--border-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-item input {
|
.text-item input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: var(--bg-primary);
|
background: var(--surface-main);
|
||||||
color: var(--text-primary);
|
color: var(--text-main);
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-item input:focus {
|
.text-item input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent-primary);
|
border-color: var(--border-strong);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -27,21 +27,21 @@
|
|||||||
.text-input {
|
.text-input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10px 12px;
|
padding: 10px 12px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: var(--bg-primary);
|
background: var(--surface-main);
|
||||||
color: var(--text-primary);
|
color: var(--text-main);
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-input:focus {
|
.text-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent-primary);
|
border-color: var(--border-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-input::placeholder {
|
.text-input::placeholder {
|
||||||
color: var(--text-tertiary);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import TextAlignCenter from "~icons/lucide/text-align-center";
|
import TextAlignCenter from "~icons/lucide/text-align-center";
|
||||||
import TextAlignEnd from "~icons/lucide/text-align-end";
|
import TextAlignEnd from "~icons/lucide/text-align-end";
|
||||||
import TextAlignStart from "~icons/lucide/text-align-start";
|
import TextAlignStart from "~icons/lucide/text-align-start";
|
||||||
// import { TextAlignCenter, TextAlignEnd, TextAlignStart } from "@lucide/svelte";
|
import Button from "./Button.svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
align: TextAlignType;
|
align: TextAlignType;
|
||||||
@@ -22,46 +22,24 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button aria-label="Set left" class="align-btn" class:active={align === TextAlign.LEFT} onclick={onAlignLeft}>
|
<Button
|
||||||
<TextAlignStart />
|
icon={TextAlignStart}
|
||||||
</button>
|
ariaLabel="Set left"
|
||||||
<button aria-label="Set center" class="align-btn" class:active={align === TextAlign.CENTER} onclick={onAlignCenter}>
|
onclick={onAlignLeft}
|
||||||
<TextAlignCenter />
|
type={align === TextAlign.LEFT ? "primary" : "outline"}
|
||||||
</button>
|
extra="grow"
|
||||||
<button aria-label="Set right" class="align-btn" class:active={align === TextAlign.RIGHT} onclick={onAlignRight}>
|
/>
|
||||||
<TextAlignEnd />
|
<Button
|
||||||
</button>
|
icon={TextAlignCenter}
|
||||||
|
ariaLabel="Set center"
|
||||||
<style>
|
onclick={onAlignCenter}
|
||||||
.align-btn {
|
type={align === TextAlign.CENTER ? "primary" : "outline"}
|
||||||
flex: 1;
|
extra="grow"
|
||||||
padding: 8px;
|
/>
|
||||||
border: 1px solid var(--border-color);
|
<Button
|
||||||
background: var(--bg-primary);
|
icon={TextAlignEnd}
|
||||||
border-radius: var(--radius);
|
ariaLabel="Set right"
|
||||||
cursor: pointer;
|
onclick={onAlignRight}
|
||||||
transition: var(--transition);
|
type={align === TextAlign.RIGHT ? "primary" : "outline"}
|
||||||
display: flex;
|
extra="grow"
|
||||||
align-items: center;
|
/>
|
||||||
justify-content: center;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-btn :global(svg) {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
transition: var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-btn:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
border-color: var(--accent-primary);
|
|
||||||
color: var(--accent-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-btn.active {
|
|
||||||
background: var(--accent-primary);
|
|
||||||
border-color: var(--accent-primary);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
min-width: 20px;
|
min-width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
background: var(--accent-light);
|
background: var(--action-primary);
|
||||||
color: var(--accent-primary);
|
color: var(--text-action);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|||||||
@@ -9,9 +9,18 @@
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
label?: string;
|
label?: string;
|
||||||
type?: "primary" | "secondary" | "danger" | "outline" | "mini";
|
type?: "primary" | "secondary" | "danger" | "outline" | "mini";
|
||||||
|
extra?: "grow";
|
||||||
}
|
}
|
||||||
|
|
||||||
let { icon: Icon, ariaLabel, onclick = () => {}, disabled = false, label = "", type = "primary" }: Props = $props();
|
let {
|
||||||
|
icon: Icon,
|
||||||
|
ariaLabel,
|
||||||
|
onclick = () => {},
|
||||||
|
disabled = false,
|
||||||
|
label = "",
|
||||||
|
type = "primary",
|
||||||
|
extra,
|
||||||
|
}: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -21,6 +30,7 @@
|
|||||||
class:btn-outline={type === "outline"}
|
class:btn-outline={type === "outline"}
|
||||||
class:btn-danger={type === "danger"}
|
class:btn-danger={type === "danger"}
|
||||||
class:btn-mini={type === "mini"}
|
class:btn-mini={type === "mini"}
|
||||||
|
class:grow={extra === "grow"}
|
||||||
{disabled}
|
{disabled}
|
||||||
{onclick}
|
{onclick}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
@@ -31,6 +41,8 @@
|
|||||||
|
|
||||||
<style>
|
<style>
|
||||||
.btn {
|
.btn {
|
||||||
|
--btn-main: var(--action-primary);
|
||||||
|
--btn-hover: var(--action-primary-hover);
|
||||||
padding: 10px 16px;
|
padding: 10px 16px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
@@ -43,6 +55,8 @@
|
|||||||
gap: 6px;
|
gap: 6px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
background-color: var(--btn-main);
|
||||||
|
color: oklch(from var(--btn-main) 99% 0.05 h);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn :global(svg) {
|
.btn :global(svg) {
|
||||||
@@ -50,75 +64,52 @@
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover:not(:disabled) {
|
|
||||||
background: var(--accent-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:disabled {
|
.btn:disabled {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn:hover:not(:disabled) {
|
||||||
background: var(--accent-primary);
|
background-color: var(--btn-hover);
|
||||||
color: white;
|
box-shadow: var(--shadow-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: var(--bg-secondary);
|
--btn-main: var(--action-secondary);
|
||||||
color: var(--text-primary);
|
--btn-hover: var(--action-secondary-hover);
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover {
|
|
||||||
background: var(--bg-hover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline {
|
.btn-outline {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--text-secondary);
|
color: var(--text-main);
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-strong);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline:hover:not(:disabled) {
|
.btn-outline:hover:not(:disabled) {
|
||||||
background: var(--bg-hover);
|
background: var(--surface-hover);
|
||||||
border-color: var(--accent-primary);
|
border-color: var(--border-strong);
|
||||||
color: var(--accent-primary);
|
color: var(--text-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
background: var(--danger);
|
--btn-main: var(--danger-base);
|
||||||
color: white;
|
|
||||||
padding: 6px 10px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: var(--transition);
|
|
||||||
min-width: 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-danger:hover {
|
|
||||||
background: #dc2626;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-mini {
|
.btn-mini {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
background: var(--bg-primary);
|
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
color: var(--text-secondary);
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-mini:hover:not(:disabled) {
|
.btn-mini:hover:not(:disabled) {
|
||||||
background: var(--bg-hover);
|
border-color: var(--border-main);
|
||||||
border-color: var(--accent-primary);
|
}
|
||||||
|
|
||||||
|
.grow {
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,26 +15,26 @@
|
|||||||
.color-input {
|
.color-input {
|
||||||
width: 40px;
|
width: 40px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
background: var(--bg-primary);
|
background: var(--surface-main);
|
||||||
padding: 0px 8px;
|
padding: 0px 8px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-input:hover {
|
.color-input:hover {
|
||||||
border-color: var(--accent-primary);
|
border-color: var(--action-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-value {
|
.color-value {
|
||||||
font-family: "Courier New", monospace;
|
font-family: "Courier New", monospace;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: var(--text-secondary);
|
color: var(--text-muted);
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-main);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
height: 4px;
|
height: 4px;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-control);
|
||||||
outline: none;
|
outline: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--accent-primary);
|
background: var(--action-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--accent-primary);
|
background: var(--action-primary);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: none;
|
border: none;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
@@ -56,13 +56,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.value-display {
|
.value-display {
|
||||||
color: var(--text-primary);
|
color: var(--text-main);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: var(--bg-secondary);
|
background: var(--surface-main);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -20,10 +20,10 @@
|
|||||||
<style>
|
<style>
|
||||||
.select-input {
|
.select-input {
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-main);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
background: var(--bg-primary);
|
background: var(--surface-main);
|
||||||
color: var(--text-primary);
|
color: var(--text-main);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
@@ -33,6 +33,6 @@
|
|||||||
|
|
||||||
.select-input:focus {
|
.select-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--accent-primary);
|
border-color: var(--border-strong);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import { imageConfigState } from "$states/imageConfig.svelte";
|
import { imageConfigState } from "$states/imageConfig.svelte";
|
||||||
import { themeState } from "$states/theme.svelte";
|
import { themeState } from "$states/theme.svelte";
|
||||||
import { onMount, type Snippet } from "svelte";
|
import { onMount, type Snippet } from "svelte";
|
||||||
|
import "../app.css";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: Snippet;
|
children: Snippet;
|
||||||
@@ -27,74 +28,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:root {
|
|
||||||
--bg-primary: #ffffff;
|
|
||||||
--bg-secondary: #dddddd;
|
|
||||||
--bg-card: #ffffff;
|
|
||||||
--bg-hover: #f5f5f5;
|
|
||||||
|
|
||||||
--text-primary: #1a1a1a;
|
|
||||||
--text-secondary: #666666;
|
|
||||||
--text-tertiary: #999999;
|
|
||||||
|
|
||||||
--border-color: #e5e5e5;
|
|
||||||
--border-hover: #d4d4d4;
|
|
||||||
|
|
||||||
--accent-primary: #6366f1;
|
|
||||||
--accent-hover: #4f46e5;
|
|
||||||
--accent-light: #eef2ff;
|
|
||||||
|
|
||||||
--danger: #ef4444;
|
|
||||||
|
|
||||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
||||||
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
|
|
||||||
--radius: 8px;
|
|
||||||
--transition: all 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global([data-theme="dark"]) {
|
|
||||||
--bg-primary: #0a0a0a;
|
|
||||||
--bg-secondary: #333333;
|
|
||||||
--bg-card: #1a1a1a;
|
|
||||||
--bg-hover: #2a2a2a;
|
|
||||||
|
|
||||||
--text-primary: #f5f5f5;
|
|
||||||
--text-secondary: #a3a3a3;
|
|
||||||
--text-tertiary: #737373;
|
|
||||||
|
|
||||||
--border-color: #2a2a2a;
|
|
||||||
--border-hover: #3a3a3a;
|
|
||||||
|
|
||||||
--accent-primary: #818cf8;
|
|
||||||
--accent-hover: #6366f1;
|
|
||||||
--accent-light: #312e81;
|
|
||||||
|
|
||||||
--shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
||||||
--shadow-md: 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(*) {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(ul) {
|
|
||||||
list-style: none;
|
|
||||||
text-indent: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(body) {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", sans-serif;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
color: var(--text-primary);
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 16px;
|
|
||||||
transition: var(--transition);
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
|||||||
Reference in New Issue
Block a user