feat(design): animated design system foundation with animate-ui and motion

Install motion + animate-ui, overhaul design tokens with brand cyan accent,
and replace CSS keyframe animations in Dialog, Tabs, Switch, and Tooltip
with spring-physics and blur-fade transitions via animate-ui Radix primitives.
This commit is contained in:
SaelixCode
2026-03-20 22:25:29 -04:00
parent 4d1aef744b
commit 0cb5fae947
39 changed files with 5634 additions and 441 deletions
+1 -1
View File
@@ -251,7 +251,7 @@ export class DatabaseService {
stmt.run(key, value);
}
// --- System State (operational/runtime values not user-defined config) ---
// --- System State (operational/runtime values - not user-defined config) ---
public getSystemState(key: string): string | null {
const row = this.db.prepare('SELECT value FROM system_state WHERE key = ?').get(key) as { value: string } | undefined;
+4 -4
View File
@@ -109,7 +109,7 @@ async function getAuthToken(registry: string, repo: string): Promise<string | nu
// ─── Remote digest lookup ─────────────────────────────────────────────────────
// Include manifest list types so we get the fat-manifest digest for multi-arch
// images this matches what Docker stores in local RepoDigests.
// images - this matches what Docker stores in local RepoDigests.
const MANIFEST_ACCEPT = [
'application/vnd.docker.distribution.manifest.list.v2+json',
'application/vnd.docker.distribution.manifest.v2+json',
@@ -145,7 +145,7 @@ export class ImageUpdateService {
private static readonly MANUAL_COOLDOWN_MS = 10 * 60 * 1000; // 10 min between manual triggers
private static readonly INTER_IMAGE_DELAY_MS = 300; // be polite to registries
private constructor() {}
private constructor() { }
public static getInstance(): ImageUpdateService {
if (!ImageUpdateService.instance) {
@@ -195,7 +195,7 @@ export class ImageUpdateService {
try {
const db = DatabaseService.getInstance();
// Only check local nodes remote nodes run their own instance
// Only check local nodes - remote nodes run their own instance
for (const node of db.getNodes()) {
if (node.type !== 'local' || !node.id) continue;
try {
@@ -282,7 +282,7 @@ export class ImageUpdateService {
if (!localDigest) return false; // Locally built or never pulled with a digest
const remoteDigest = await getRemoteDigest(parsed.registry, parsed.repo, parsed.tag);
if (!remoteDigest) return false; // Registry unreachable no false positives
if (!remoteDigest) return false; // Registry unreachable - no false positives
const hasUpdate = localDigest !== remoteDigest;
console.log(
+2 -2
View File
@@ -157,14 +157,14 @@ export class NodeRegistry {
const headers = { Authorization: `Bearer ${node.api_token}` };
try {
// Step 1: Verify auth. A 401 here means wrong token surface that clearly.
// Step 1: Verify auth. A 401 here means wrong token - surface that clearly.
const authRes = await axios.get(`${baseUrl}/api/auth/check`, { headers, timeout: 8000 });
if (authRes.status !== 200) throw new Error(`Unexpected status ${authRes.status}`);
db.updateNodeStatus(node.id, 'online');
// Step 2: Fetch Docker stats in parallel. Use allSettled so a slow or missing
// endpoint doesn't fail the whole test each field falls back to '-' gracefully.
// endpoint doesn't fail the whole test - each field falls back to '-' gracefully.
const [statsResult, sysResult, imagesResult] = await Promise.allSettled([
axios.get(`${baseUrl}/api/stats`, { headers, timeout: 8000 }),
axios.get(`${baseUrl}/api/system/stats`, { headers, timeout: 8000 }),
+1 -1
View File
@@ -34,7 +34,7 @@ export class NotificationService {
this.broadcaster(notification);
}
// 2. Fetch enabled agents
// 3. Fetch enabled agents
const agents = this.dbService.getEnabledAgents();
if (agents.length === 0) {
console.log('No active notification agents found. Skipping external dispatch.');
+1 -1
View File
@@ -248,7 +248,7 @@ export class TemplateService {
});
} else {
// Legacy Portainer v2 Format (Fallback for custom registries)
// The Portainer v2 spec includes a native `categories` field pass it through.
// The Portainer v2 spec includes a native `categories` field - pass it through.
this.cachedTemplates = (response.data.templates || [])
.filter((t: Template) => !!t.image && t.type === 1)
.map((t: Template) => ({ ...t, source: 'custom' }));