chore: refactor css, restyle to be slightly minimalistic (#7397)

restyle

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2025-11-29 22:11:44 +01:00
committed by GitHub
parent 468ac608f3
commit 54b5dfa8e1
36 changed files with 1876 additions and 997 deletions

View File

@@ -0,0 +1,247 @@
/* LocalAI Animation System */
/* Purposeful animations with performance optimization */
/* Animation Keyframes */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes cardReveal {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideInRight {
from {
opacity: 0;
transform: translateX(-20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes slideInLeft {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
@keyframes glow {
0%, 100% {
box-shadow: 0 0 8px rgba(56, 189, 248, 0.15);
}
50% {
box-shadow: 0 0 12px rgba(56, 189, 248, 0.25);
}
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0.95);
}
to {
opacity: 1;
transform: scale(1);
}
}
/* P2P/Network Specific Animations */
@keyframes rotateCircleNodes {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes shakeFlask {
0%, 10% { transform: rotate(0deg); }
20% { transform: rotate(-10deg); }
30% { transform: rotate(10deg); }
40% { transform: rotate(-8deg); }
50% { transform: rotate(8deg); }
60% { transform: rotate(-5deg); }
70% { transform: rotate(5deg); }
80% { transform: rotate(-2deg); }
90% { transform: rotate(2deg); }
100% { transform: rotate(0deg); }
}
@keyframes nodeGlow {
0% { left: -100%; }
50% { left: 100%; }
100% { left: 100%; }
}
/* Animation Utility Classes */
.fade-in {
animation: fadeIn var(--duration-fast) var(--ease-out);
}
/* Transition Utility Classes */
.transition-default {
transition: all var(--duration-fast) var(--ease-default);
}
.transition-color {
transition: color var(--duration-fast) var(--ease-default);
}
.transition-background {
transition: background-color var(--duration-fast) var(--ease-default);
}
.fade-in-up {
animation: fadeInUp var(--duration-normal) var(--ease-out) backwards;
}
.fade-in-down {
animation: fadeInDown var(--duration-normal) var(--ease-out) backwards;
}
.slide-in-right {
animation: slideInRight var(--duration-normal) var(--ease-out) backwards;
}
.slide-in-left {
animation: slideInLeft var(--duration-normal) var(--ease-out) backwards;
}
.scale-in {
animation: scaleIn var(--duration-normal) var(--ease-out) backwards;
}
/* Staggered Card Animations */
.card-animate {
animation: cardReveal var(--duration-normal) var(--ease-out) backwards;
}
.card-animate:nth-child(1) { animation-delay: 0ms; }
.card-animate:nth-child(2) { animation-delay: 50ms; }
.card-animate:nth-child(3) { animation-delay: 100ms; }
.card-animate:nth-child(4) { animation-delay: 150ms; }
.card-animate:nth-child(5) { animation-delay: 200ms; }
.card-animate:nth-child(6) { animation-delay: 250ms; }
.card-animate:nth-child(7) { animation-delay: 300ms; }
.card-animate:nth-child(8) { animation-delay: 350ms; }
.card-animate:nth-child(9) { animation-delay: 400ms; }
.card-animate:nth-child(10) { animation-delay: 450ms; }
.card-animate:nth-child(11) { animation-delay: 500ms; }
.card-animate:nth-child(12) { animation-delay: 550ms; }
/* Hero Text Animation */
.hero-title {
animation: fadeInUp var(--duration-normal) var(--ease-out) backwards;
animation-delay: 50ms;
}
.hero-subtitle {
animation: fadeInUp var(--duration-normal) var(--ease-out) backwards;
animation-delay: 100ms;
}
/* Navigation Animation */
.nav-fade-in {
animation: fadeIn var(--duration-normal) var(--ease-out) backwards;
animation-delay: 0ms;
}
/* Loading States - Minimal */
.pulse-animation {
animation: pulse 1.5s var(--ease-in-out) infinite;
}
.glow-animation {
animation: glow 1.5s var(--ease-in-out) infinite;
}
/* Reduced Motion Support */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
.card-animate,
.fade-in-up,
.fade-in-down,
.slide-in-right,
.slide-in-left,
.scale-in,
.hero-title,
.hero-subtitle {
animation: none !important;
}
}
/* Performance Optimization */
.card-animate,
.fade-in-up,
.fade-in-down,
.slide-in-right,
.slide-in-left,
.scale-in {
will-change: transform, opacity;
}
/* After animation completes, remove will-change */
.card-animate.animation-complete,
.fade-in-up.animation-complete,
.fade-in-down.animation-complete,
.slide-in-right.animation-complete,
.slide-in-left.animation-complete,
.scale-in.animation-complete {
will-change: auto;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,441 @@
/* LocalAI Component Styles */
/* Buttons, Cards, Inputs, Grid Pattern, Hero Sections */
/* ============================================
Grid Pattern
============================================ */
.grid-pattern {
background-image:
linear-gradient(rgba(56, 189, 248, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(56, 189, 248, 0.03) 1px, transparent 1px);
background-size: 20px 20px;
background-position: 0 0, 0 0;
}
@media (max-width: 640px) {
.grid-pattern {
background-image:
linear-gradient(rgba(56, 189, 248, 0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(56, 189, 248, 0.02) 1px, transparent 1px);
}
}
/* ============================================
Buttons
============================================ */
.btn-primary,
button.btn-primary,
a.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
background: var(--color-primary);
color: var(--color-primary-text);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: var(--weight-medium);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: background-color var(--duration-fast) var(--ease-default);
text-decoration: none;
box-shadow: var(--shadow-none);
}
.btn-primary:hover {
background: var(--color-primary-hover);
box-shadow: var(--shadow-subtle);
}
.btn-primary:focus {
outline: none;
box-shadow: 0 0 0 2px var(--color-border-focus);
}
.btn-primary:active {
background: var(--color-primary-active);
}
.btn-primary:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-secondary,
button.btn-secondary,
a.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
background: transparent;
color: var(--color-primary);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: var(--weight-medium);
border: 1px solid var(--color-border-default);
border-radius: var(--radius-md);
cursor: pointer;
transition: all var(--duration-fast) var(--ease-default);
text-decoration: none;
}
.btn-secondary:hover {
background: var(--color-primary-light);
border-color: var(--color-primary-border);
}
.btn-secondary:focus {
outline: none;
box-shadow: 0 0 0 2px var(--color-border-focus);
}
.btn-secondary:active {
background: var(--color-primary-light);
}
.btn-secondary:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.btn-tertiary,
button.btn-tertiary,
a.btn-tertiary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 16px;
background: transparent;
color: var(--color-text-secondary);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: var(--weight-normal);
border: none;
border-radius: var(--radius-md);
cursor: pointer;
transition: color var(--duration-fast) var(--ease-default);
text-decoration: none;
}
.btn-tertiary:hover {
color: var(--color-text-primary);
background: var(--color-bg-secondary);
}
.btn-tertiary:focus {
outline: none;
box-shadow: 0 0 0 2px var(--color-border-focus);
}
.btn-tertiary:active {
color: var(--color-text-primary);
}
.btn-tertiary:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
/* ============================================
Cards
============================================ */
.card {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border-subtle);
border-radius: var(--radius-lg);
padding: 24px;
box-shadow: var(--shadow-none);
transition: border-color var(--duration-normal) var(--ease-default);
}
.card:hover {
border-color: var(--color-border-default);
}
.card:focus-within {
outline: none;
box-shadow: 0 0 0 2px var(--color-border-focus);
}
.card-active {
border-color: var(--color-primary-border);
}
.card-elevated {
box-shadow: var(--shadow-subtle);
}
.card-elevated:hover {
box-shadow: var(--shadow-sm);
}
/* ============================================
Input Fields
============================================ */
.input,
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="search"],
input[type="url"],
textarea,
select {
width: 100%;
padding: 0.75rem 1rem;
background: var(--color-bg-secondary);
color: var(--color-text-primary);
font-family: var(--font-body);
font-size: var(--text-base);
border: 1px solid var(--color-border-secondary);
border-radius: var(--radius-md);
transition: all var(--duration-fast) var(--ease-default);
outline: none;
}
.input::placeholder,
input::placeholder,
textarea::placeholder {
color: var(--color-text-muted);
}
.input:focus,
input:focus,
textarea:focus,
select:focus {
border-color: var(--color-primary-border);
box-shadow: 0 0 0 3px var(--color-primary-light);
outline: none;
}
.input:disabled,
input:disabled,
textarea:disabled,
select:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Input States */
.input-error,
input.input-error,
textarea.input-error {
border-color: var(--color-error);
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.2);
}
.input-error:focus {
box-shadow: 0 0 0 3px var(--color-error-light);
}
.input-success,
input.input-success,
textarea.input-success {
border-color: var(--color-success);
box-shadow: 0 0 0 2px var(--color-success-light);
}
.input-success:focus {
box-shadow: 0 0 0 3px var(--color-success-light);
}
/* ============================================
Hero Sections
============================================ */
/* ============================================
Hero Section - Minimal
============================================ */
.hero-section {
padding: 0.75rem 0;
margin-bottom: 1.5rem;
}
.hero-content {
text-align: center;
}
.hero-title {
font-family: var(--font-body);
font-size: var(--text-2xl);
font-weight: var(--weight-semibold);
line-height: var(--leading-tight);
color: var(--color-text-primary);
letter-spacing: -0.01em;
margin-bottom: 0.5rem;
}
@media (max-width: 640px) {
.hero-title {
font-size: var(--text-xl);
}
}
.hero-subtitle {
font-family: var(--font-body);
font-size: var(--text-sm);
font-weight: var(--weight-normal);
color: var(--color-text-secondary);
line-height: var(--leading-normal);
margin: 0;
}
/* ============================================
Icon Hover Effects
============================================ */
.icon-hover {
transition: all var(--duration-fast) var(--ease-default);
cursor: pointer;
}
.icon-hover:hover {
transform: scale(1.1);
color: var(--color-primary);
}
/* ============================================
Status Badges
============================================ */
.badge {
display: inline-flex;
align-items: center;
padding: 4px 12px;
font-family: var(--font-body);
font-size: var(--text-xs);
font-weight: var(--weight-medium);
border-radius: var(--radius-full);
background: var(--color-bg-secondary);
color: var(--color-text-primary);
border: none;
}
.badge-success {
background: var(--color-success-light);
color: var(--color-success);
border: none;
}
.badge-warning {
background: var(--color-warning-light);
color: var(--color-warning);
border: none;
}
.badge-error {
background: var(--color-error-light);
color: var(--color-error);
border: none;
}
.badge-info {
background: var(--color-info-light);
color: var(--color-info);
border: none;
}
.badge-gradient {
background: var(--gradient-primary);
color: #FFFFFF;
border: none;
}
/* ============================================
Navigation
============================================ */
.nav-link {
color: var(--color-text-secondary);
text-decoration: none;
transition: all var(--duration-fast) var(--ease-default);
padding: 0.5rem 1rem;
border-radius: var(--radius-md);
}
.nav-link:hover {
color: var(--color-text-primary);
background: var(--color-bg-secondary);
}
.nav-link-active {
color: var(--color-primary);
background: var(--color-primary-light);
}
.nav-link-active:hover {
color: var(--color-primary-hover);
}
/* ============================================
P2P/Network Specific Components
============================================ */
.animation-container {
position: relative;
width: 100%;
height: 25vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background: linear-gradient(135deg, var(--color-bg-primary) 0%, var(--color-bg-secondary) 100%);
}
.text-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
z-index: 1;
}
.fa-circle-nodes {
animation: rotateCircleNodes 8s linear infinite;
display: inline-block;
filter: drop-shadow(0 0 8px var(--color-primary));
}
.fa-flask {
animation: shakeFlask 3s ease-in-out infinite;
transform-origin: bottom center;
}
.active-node {
position: relative;
overflow: hidden;
}
.active-node::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, var(--color-primary), transparent);
animation: nodeGlow 3s ease-in-out infinite;
}
/* ============================================
Responsive Adjustments
============================================ */
@media (max-width: 640px) {
.btn-primary,
.btn-secondary,
.btn-tertiary {
padding: 0.625rem 1.25rem;
font-size: var(--text-sm);
}
.card {
padding: 1rem;
}
.hero-section {
padding: 0.5rem 0;
margin-bottom: 1rem;
}
}

View File

@@ -1,5 +1,5 @@
body {
font-family: 'Inter', sans-serif;
font-family: var(--font-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif);
}
.chat-container { height: 90vh; display: flex; flex-direction: column; }
.chat-messages { overflow-y: auto; flex-grow: 1; }
@@ -79,3 +79,28 @@ li {
li:last-child {
margin-bottom: 0; /* Removes bottom margin from the last item */
}
/* Scrollbar Styling - Minimal */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: var(--color-bg-primary);
}
::-webkit-scrollbar-thumb {
background: var(--color-bg-secondary);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--color-primary);
}
/* Firefox */
* {
scrollbar-width: thin;
scrollbar-color: var(--color-bg-secondary) var(--color-bg-primary);
}

113
core/http/static/theme.css Normal file
View File

@@ -0,0 +1,113 @@
/* LocalAI Theme - CSS Variables System */
/* Based on logo color palette: cyan, teal, navy, purple */
:root {
/* Base Colors */
--color-bg-primary: #0F172A; /* Deep navy background */
--color-bg-secondary: #1E293B; /* Elevated surfaces */
--color-bg-tertiary: #1E293B; /* Cards, panels */
--color-bg-overlay: rgba(15, 23, 42, 0.8); /* Modals, overlays */
/* Brand Colors */
--color-primary: #38BDF8; /* Cyan - primary actions */
--color-primary-hover: #0EA5E9; /* Darker cyan on hover */
--color-primary-active: #0284C7; /* Active state */
--color-primary-text: #FFFFFF; /* Text on primary background */
--color-primary-light: rgba(56, 189, 248, 0.08); /* Light cyan backgrounds (reduced) */
--color-primary-border: rgba(56, 189, 248, 0.15); /* Cyan borders (reduced) */
--color-secondary: #14B8A6; /* Teal - secondary actions */
--color-secondary-hover: #0D9488; /* Darker teal on hover */
--color-secondary-light: rgba(20, 184, 166, 0.1);
--color-accent: #8B5CF6; /* Purple - special states */
--color-accent-hover: #7C3AED; /* Darker purple on hover */
--color-accent-light: rgba(139, 92, 246, 0.1);
--color-accent-purple: #A78BFA; /* Light purple for gradients */
--color-accent-teal: #2DD4BF; /* Light teal for gradients */
/* Text Colors */
--color-text-primary: #E5E7EB; /* Primary text */
--color-text-secondary: #94A3B8; /* Secondary text */
--color-text-muted: #64748B; /* Tertiary text */
--color-text-disabled: #475569; /* Disabled text */
--color-text-inverse: #0F172A; /* Text on light backgrounds */
/* Border Colors - Minimal System */
--color-border-subtle: rgba(148, 163, 184, 0.08); /* Minimal borders */
--color-border-default: rgba(148, 163, 184, 0.12); /* Default borders */
--color-border-strong: rgba(56, 189, 248, 0.2); /* Focus borders */
--color-border-divider: rgba(148, 163, 184, 0.06); /* Section dividers */
--color-border-primary: rgba(56, 189, 248, 0.15); /* Primary borders (reduced opacity) */
--color-border-secondary: rgba(148, 163, 184, 0.1);
--color-border-focus: rgba(56, 189, 248, 0.3); /* Focus borders (reduced) */
/* Status Colors */
--color-success: #14B8A6; /* Use teal for success (aligned with logo) */
--color-success-light: rgba(20, 184, 166, 0.1);
--color-warning: #F59E0B;
--color-warning-light: rgba(245, 158, 11, 0.1);
--color-error: #EF4444;
--color-error-light: rgba(239, 68, 68, 0.1);
--color-info: #38BDF8; /* Use cyan for info */
--color-info-light: rgba(56, 189, 248, 0.1);
/* Gradient Definitions */
--gradient-primary: linear-gradient(135deg, #38BDF8 0%, #8B5CF6 50%, #14B8A6 100%);
--gradient-hero: linear-gradient(135deg, #0F172A 0%, #1E293B 50%, #0F172A 100%);
--gradient-card: linear-gradient(135deg, rgba(56, 189, 248, 0.05) 0%, rgba(139, 92, 246, 0.05) 100%);
--gradient-text: linear-gradient(135deg, #38BDF8 0%, #8B5CF6 50%, #14B8A6 100%);
/* Shadows - Minimal System */
--shadow-none: none;
--shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.1);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.12);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-glow: 0 0 0 1px rgba(56, 189, 248, 0.1), 0 0 8px rgba(56, 189, 248, 0.15); /* Minimal glow */
/* Animation Timing - Minimal */
--duration-instant: 100ms;
--duration-fast: 150ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
/* Animation Easing */
--ease-default: cubic-bezier(0.4, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.68, -0.55, 0.265, 1.55);
/* Spacing Scale (for reference, used via Tailwind) */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
--spacing-2xl: 3rem; /* 48px */
--spacing-3xl: 4rem; /* 64px */
/* Border Radius */
--radius-none: 0;
--radius-sm: 4px;
--radius-md: 6px; /* Updated per PRD */
--radius-lg: 8px;
--radius-xl: 12px;
--radius-full: 9999px;
/* Width System - Container Sizes */
--width-xs: 20rem; /* 320px */
--width-sm: 24rem; /* 384px */
--width-md: 28rem; /* 448px */
--width-lg: 32rem; /* 512px */
--width-xl: 36rem; /* 576px */
--width-2xl: 42rem; /* 672px */
--width-3xl: 48rem; /* 768px */
--width-4xl: 56rem; /* 896px */
--width-5xl: 64rem; /* 1024px */
--width-6xl: 72rem; /* 1152px */
--width-7xl: 80rem; /* 1280px */
}

View File

@@ -0,0 +1,195 @@
/* LocalAI Typography System */
/* Font-face declarations and typography variables */
/* Playfair Display - Display/Headline Font */
@font-face {
font-family: 'Playfair Display';
src: url('/static/assets/playfair-display-regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Playfair Display';
src: url('/static/assets/playfair-display-semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Playfair Display';
src: url('/static/assets/playfair-display-bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* Space Grotesk - Body Font */
@font-face {
font-family: 'Space Grotesk';
src: url('/static/assets/space-grotesk-regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url('/static/assets/space-grotesk-medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url('/static/assets/space-grotesk-semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Space Grotesk';
src: url('/static/assets/space-grotesk-bold.ttf') format('truetype');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* JetBrains Mono - Monospace Font */
@font-face {
font-family: 'JetBrains Mono';
src: url('/static/assets/jetbrains-mono-regular.ttf') format('truetype');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('/static/assets/jetbrains-mono-medium.ttf') format('truetype');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'JetBrains Mono';
src: url('/static/assets/jetbrains-mono-semibold.ttf') format('truetype');
font-weight: 600;
font-style: normal;
font-display: swap;
}
:root {
/* Font Families */
--font-display: 'Playfair Display', serif;
--font-body: 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', monospace;
/* Font Sizes */
--text-xs: 0.75rem; /* 12px */
--text-sm: 0.875rem; /* 14px */
--text-base: 1rem; /* 16px */
--text-lg: 1.125rem; /* 18px */
--text-xl: 1.25rem; /* 20px */
--text-2xl: 1.5rem; /* 24px */
--text-3xl: 1.875rem; /* 30px */
--text-4xl: 2.25rem; /* 36px */
--text-5xl: 3rem; /* 48px */
--text-6xl: 3.75rem; /* 60px */
--text-7xl: 4.5rem; /* 72px */
/* Line Heights */
--leading-tight: 1.25;
--leading-snug: 1.375;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--leading-loose: 2;
/* Font Weights */
--weight-light: 300;
--weight-normal: 400;
--weight-medium: 500;
--weight-semibold: 600;
--weight-bold: 700;
--weight-extrabold: 800;
}
/* Base typography */
body {
font-family: var(--font-body);
font-size: var(--text-base);
line-height: var(--leading-normal);
color: var(--color-text-primary);
}
/* Headings */
h1, .h1 {
font-family: var(--font-body);
font-size: var(--text-4xl);
font-weight: var(--weight-bold);
line-height: var(--leading-tight);
letter-spacing: -0.02em;
}
h2, .h2 {
font-family: var(--font-body);
font-size: var(--text-3xl);
font-weight: var(--weight-semibold);
line-height: var(--leading-snug);
letter-spacing: -0.01em;
}
h3, .h3 {
font-family: var(--font-body);
font-size: var(--text-2xl);
font-weight: var(--weight-semibold);
line-height: var(--leading-snug);
}
h4, .h4 {
font-family: var(--font-body);
font-size: var(--text-xl);
font-weight: var(--weight-semibold);
line-height: var(--leading-normal);
}
h5, .h5 {
font-family: var(--font-body);
font-size: var(--text-lg);
font-weight: var(--weight-medium);
line-height: var(--leading-normal);
}
h6, .h6 {
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: var(--weight-medium);
line-height: var(--leading-normal);
}
/* Code and monospace */
code, pre, kbd, samp {
font-family: var(--font-mono);
font-size: 0.9em;
}
/* Responsive typography adjustments */
@media (max-width: 640px) {
h1, .h1 {
font-size: var(--text-3xl);
}
h2, .h2 {
font-size: var(--text-2xl);
}
h3, .h3 {
font-size: var(--text-xl);
}
}

View File

@@ -14,10 +14,8 @@
<div class="mb-6 text-6xl text-[#38BDF8]">
<i class="fas fa-exclamation-circle"></i>
</div>
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
404 - Page Not Found
</span>
<h1 class="hero-title mb-4">
404 - Page Not Found
</h1>
<p class="text-xl text-[#94A3B8] mb-6">The page you're looking for doesn't exist or has been moved</p>
<div class="flex flex-wrap justify-center gap-4">

View File

@@ -9,15 +9,13 @@
<div class="container mx-auto px-4 py-8 flex-grow max-w-6xl">
<!-- Header -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
<div class="flex justify-between items-center">
<div class="hero-section">
<div class="hero-content flex justify-between items-center">
<div>
<h1 class="text-4xl font-bold text-[#E5E7EB] mb-2">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] via-[#8B5CF6] to-[#38BDF8]">
Job Details
</span>
<h1 class="hero-title">
Job Details
</h1>
<p class="text-lg text-[#94A3B8]">Live job status, reasoning traces, and execution details</p>
<p class="hero-subtitle">Live job status, reasoning traces, and execution details</p>
</div>
<a href="/agent-jobs" class="text-[#94A3B8] hover:text-[#E5E7EB]">
<i class="fas fa-arrow-left mr-2"></i>Back to Jobs
@@ -26,7 +24,7 @@
</div>
<!-- Job Status Card -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
<div class="card p-8 mb-8">
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-semibold text-[#E5E7EB]">Job Status</h2>
<div class="flex items-center space-x-4">
@@ -41,7 +39,8 @@
x-text="job.status ? job.status.toUpperCase() : 'LOADING...'"></span>
<button x-show="job.status === 'pending' || job.status === 'running'"
@click="cancelJob()"
class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg transition-colors">
class="btn-primary"
style="background: var(--color-error);">
<i class="fas fa-stop mr-2"></i>Cancel
</button>
</div>
@@ -76,7 +75,7 @@
</div>
<!-- Agent Prompt Template -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="task && task.prompt">
<div class="card p-8 mb-8" x-show="task && task.prompt">
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Agent Prompt Template</h2>
<p class="text-sm text-[#94A3B8] mb-4">The original prompt template from the task definition.</p>
<div class="bg-[#101827] p-4 rounded text-[#E5E7EB] whitespace-pre-wrap font-mono text-sm" x-text="task.prompt"></div>
@@ -97,7 +96,7 @@
</div>
<!-- Rendered Job Prompt -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="task && task.prompt">
<div class="card p-8 mb-8" x-show="task && task.prompt">
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Rendered Job Prompt</h2>
<p class="text-sm text-[#94A3B8] mb-4">The prompt with parameters substituted, as it was sent to the agent.</p>
<div class="bg-[#101827] p-4 rounded text-[#E5E7EB] whitespace-pre-wrap" x-text="getRenderedPrompt()"></div>
@@ -110,7 +109,7 @@
</div>
<!-- Error -->
<div class="bg-[#1E293B] border border-red-500/20 rounded-xl p-8 mb-8" x-show="job.error">
<div class="card p-8 mb-8" x-show="job.error" style="border-color: var(--color-error);">
<h2 class="text-2xl font-semibold text-red-400 mb-6">Error</h2>
<div class="bg-red-900/20 p-4 rounded text-red-400 whitespace-pre-wrap" x-text="job.error"></div>
</div>

View File

@@ -2,101 +2,97 @@
<html lang="en">
{{template "views/partials/head" .}}
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen" x-data="agentJobs()" x-init="init()">
{{template "views/partials/navbar" .}}
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Header -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
<div class="flex justify-between items-center">
<div class="hero-section">
<div class="hero-content flex justify-between items-center">
<div>
<h1 class="text-4xl font-bold text-[#E5E7EB] mb-2">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] via-[#8B5CF6] to-[#38BDF8]">
Agent Jobs
</span>
<h1 class="hero-title">
Agent Jobs
</h1>
<p class="text-lg text-[#94A3B8]">Manage agent tasks and monitor job execution</p>
<p class="hero-subtitle">Manage agent tasks and monitor job execution</p>
</div>
<a href="/agent-jobs/tasks/new" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg transition-colors" x-show="hasMCPModels">
<a href="/agent-jobs/tasks/new" class="bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white px-6 py-3 rounded-lg transition-colors" x-show="hasMCPModels">
<i class="fas fa-plus mr-2"></i>Create Task
</a>
</div>
</div>
<!-- Wizard: No Models -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-12 mb-8" x-show="!hasModels">
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent-border)]/20 rounded-xl p-12 mb-8" x-show="!hasModels">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#8B5CF6]/10 border border-[#8B5CF6]/20 mb-6">
<i class="text-[#8B5CF6] text-2xl fas fa-robot"></i>
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[var(--color-accent)]/10 border border-[var(--color-accent-border)]/20 mb-6">
<i class="text-[var(--color-accent)] text-2xl fas fa-robot"></i>
</div>
<h2 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
No Models Installed
</span>
<h2 class="h2 mb-4">
No Models Installed
</h2>
<p class="text-xl text-[#94A3B8] mb-8">
<p class="text-xl text-[var(--color-text-secondary)] mb-8">
To use Agent Jobs, you need to install a model first. Agent Jobs require models with MCP (Model Context Protocol) configuration.
</p>
<!-- Features Preview -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-10">
<div class="bg-[#101827] border border-[#38BDF8]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-images text-[#38BDF8] text-xl"></i>
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-[var(--color-primary-light)] rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-images text-[var(--color-primary)] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Model Gallery</h3>
<p class="text-xs text-[#94A3B8]">Browse and install pre-configured models</p>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Model Gallery</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Browse and install pre-configured models</p>
</div>
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-purple-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-upload text-[#8B5CF6] text-xl"></i>
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-accent-border)]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-[var(--color-accent-light)] rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-upload text-[var(--color-accent)] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Import Models</h3>
<p class="text-xs text-[#94A3B8]">Upload your own model files</p>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Import Models</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Upload your own model files</p>
</div>
<div class="bg-[#101827] border border-green-500/20 rounded-lg p-4">
<div class="bg-[var(--color-bg-primary)] border border-green-500/20 rounded-lg p-4">
<div class="w-10 h-10 bg-green-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-code text-green-400 text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">API Download</h3>
<p class="text-xs text-[#94A3B8]">Use the API to download models programmatically</p>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">API Download</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Use the API to download models programmatically</p>
</div>
</div>
<!-- Setup Instructions -->
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-rocket text-[#8B5CF6] mr-2"></i>
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-accent-border)]/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-rocket text-[var(--color-accent)] mr-2"></i>
How to Get Started
</h3>
<div class="space-y-4">
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">1</span>
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent)]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">1</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Browse the Model Gallery</p>
<p class="text-[#94A3B8] text-sm">Explore our curated collection of pre-configured models. Find models for chat, image generation, audio processing, and more.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Browse the Model Gallery</p>
<p class="text-[var(--color-text-secondary)] text-sm">Explore our curated collection of pre-configured models. Find models for chat, image generation, audio processing, and more.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">2</span>
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent)]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">2</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Install a Model</p>
<p class="text-[#94A3B8] text-sm">Click on a model from the gallery to install it, or use the import feature to upload your own model files.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Install a Model</p>
<p class="text-[var(--color-text-secondary)] text-sm">Click on a model from the gallery to install it, or use the import feature to upload your own model files.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">3</span>
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent)]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">3</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Configure MCP</p>
<p class="text-[#94A3B8] text-sm">After installing a model, configure MCP (Model Context Protocol) to enable Agent Jobs functionality.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Configure MCP</p>
<p class="text-[var(--color-text-secondary)] text-sm">After installing a model, configure MCP (Model Context Protocol) to enable Agent Jobs functionality.</p>
</div>
</div>
</div>
@@ -104,17 +100,17 @@
<div class="flex flex-wrap justify-center gap-4">
<a href="/browse/"
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
class="inline-flex items-center bg-[var(--color-accent)] hover:bg-[var(--color-accent)]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-images mr-2"></i>
Browse Model Gallery
</a>
<a href="/import-model"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
class="inline-flex items-center bg-[var(--color-primary)] hover:bg-[var(--color-primary)]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-upload mr-2"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank"
class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#8B5CF6]/20 text-[#E5E7EB] py-3 px-6 rounded-lg font-semibold transition-colors">
class="inline-flex items-center bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-secondary)]/80 border border-[var(--color-accent-border)]/20 text-[var(--color-text-primary)] py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-graduation-cap mr-2"></i>
Getting Started
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
@@ -124,32 +120,30 @@
</div>
<!-- Wizard: Models but No MCP -->
<div class="bg-[#1E293B] border border-yellow-500/20 rounded-xl p-12 mb-8" x-show="hasModels && !hasMCPModels">
<div class="bg-[var(--color-bg-secondary)] border border-yellow-500/20 rounded-xl p-12 mb-8" x-show="hasModels && !hasMCPModels">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-yellow-500/10 border border-yellow-500/20 mb-6">
<i class="text-yellow-500 text-2xl fas fa-exclamation-triangle"></i>
</div>
<h2 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-yellow-500">
MCP Configuration Required
</span>
<h2 class="h2 mb-4">
MCP Configuration Required
</h2>
<p class="text-xl text-[#94A3B8] mb-8">
<p class="text-xl text-[var(--color-text-secondary)] mb-8">
You have models installed, but none have MCP (Model Context Protocol) enabled. Agent Jobs require MCP to function.
</p>
<!-- Available Models List -->
<div class="bg-[#101827] border border-yellow-500/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[#E5E7EB] mb-4 flex items-center">
<div class="bg-[var(--color-bg-primary)] border border-yellow-500/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-list text-yellow-500 mr-2"></i>
Available Models
</h3>
<div class="space-y-3">
<template x-for="model in availableModels" :key="model.name">
<div class="flex items-center justify-between p-3 bg-[#0A0E1A] rounded-lg border border-[#38BDF8]/10">
<div class="flex items-center justify-between p-3 bg-[#0A0E1A] rounded-lg border border-[var(--color-primary-border)]/10">
<div class="flex items-center space-x-3">
<i class="fas fa-cube text-[#38BDF8]"></i>
<span class="text-[#E5E7EB] font-medium" x-text="model.name"></span>
<i class="fas fa-cube text-[var(--color-primary)]"></i>
<span class="text-[var(--color-text-primary)] font-medium" x-text="model.name"></span>
</div>
<a :href="'/models/edit/' + model.name"
class="inline-flex items-center bg-yellow-600 hover:bg-yellow-700 text-white px-4 py-2 rounded-lg transition-colors text-sm">
@@ -162,8 +156,8 @@
</div>
<!-- Setup Instructions -->
<div class="bg-[#101827] border border-yellow-500/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[#E5E7EB] mb-4 flex items-center">
<div class="bg-[var(--color-bg-primary)] border border-yellow-500/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-cog text-yellow-500 mr-2"></i>
How to Enable MCP
</h3>
@@ -173,8 +167,8 @@
<span class="text-yellow-500 font-bold text-sm">1</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Edit a Model Configuration</p>
<p class="text-[#94A3B8] text-sm">Click "Configure MCP" on any model above, or navigate to the model editor to add MCP configuration.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Edit a Model Configuration</p>
<p class="text-[var(--color-text-secondary)] text-sm">Click "Configure MCP" on any model above, or navigate to the model editor to add MCP configuration.</p>
</div>
</div>
<div class="flex items-start">
@@ -182,8 +176,8 @@
<span class="text-yellow-500 font-bold text-sm">2</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Add MCP Configuration</p>
<p class="text-[#94A3B8] text-sm">In the model YAML, add MCP server or stdio configuration. See the documentation for detailed examples.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Add MCP Configuration</p>
<p class="text-[var(--color-text-secondary)] text-sm">In the model YAML, add MCP server or stdio configuration. See the documentation for detailed examples.</p>
</div>
</div>
<div class="flex items-start">
@@ -191,8 +185,8 @@
<span class="text-yellow-500 font-bold text-sm">3</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Save and Return</p>
<p class="text-[#94A3B8] text-sm">After saving the MCP configuration, return to this page to create your first Agent Job task.</p>
<p class="text-[var(--color-text-primary)] font-medium mb-2">Save and Return</p>
<p class="text-[var(--color-text-secondary)] text-sm">After saving the MCP configuration, return to this page to create your first Agent Job task.</p>
</div>
</div>
</div>
@@ -206,7 +200,7 @@
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
</a>
<a href="/manage"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
class="inline-flex items-center bg-[var(--color-primary)] hover:bg-[var(--color-primary)]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-cog mr-2"></i>
Manage Models
</a>
@@ -215,32 +209,32 @@
</div>
<!-- Tasks Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="hasMCPModels">
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Tasks</h2>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-xl p-8 mb-8" x-show="hasMCPModels">
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Tasks</h2>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-[#38BDF8]/20">
<th class="text-left py-3 px-4 text-[#94A3B8]">Name</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Model</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Cron</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Status</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Actions</th>
<tr class="border-b border-[var(--color-primary-border)]/20">
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Name</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Model</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Cron</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Status</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Actions</th>
</tr>
</thead>
<tbody>
<template x-for="task in tasks" :key="task.id">
<tr class="border-b border-[#38BDF8]/10 hover:bg-[#101827]">
<tr class="border-b border-[var(--color-primary-border)]/10 hover:bg-[var(--color-bg-primary)]">
<td class="py-3 px-4">
<a :href="'/agent-jobs/tasks/' + task.id"
class="font-semibold text-[#38BDF8] hover:text-[#38BDF8]/80 hover:underline"
class="font-semibold text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 hover:underline"
x-text="task.name"></a>
<div class="text-sm text-[#94A3B8]" x-text="task.description || 'No description'"></div>
<div class="text-sm text-[var(--color-text-secondary)]" x-text="task.description || 'No description'"></div>
</td>
<td class="py-3 px-4">
<div class="flex items-center space-x-2">
<a :href="'/chat/' + task.model + '?mcp=true'"
class="text-[#38BDF8] hover:text-[#38BDF8]/80 hover:underline"
class="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 hover:underline"
x-text="task.model"></a>
<a :href="'/models/edit/' + task.model"
class="text-yellow-400 hover:text-yellow-300"
@@ -250,8 +244,8 @@
</div>
</td>
<td class="py-3 px-4">
<span x-show="task.cron" class="text-[#38BDF8]" x-text="task.cron"></span>
<span x-show="!task.cron" class="text-[#94A3B8]">-</span>
<span x-show="task.cron" class="text-[var(--color-primary)]" x-text="task.cron"></span>
<span x-show="!task.cron" class="text-[var(--color-text-secondary)]">-</span>
</td>
<td class="py-3 px-4">
<span :class="task.enabled ? 'bg-green-500' : 'bg-gray-500'"
@@ -280,7 +274,7 @@
</tr>
</template>
<tr x-show="tasks.length === 0">
<td colspan="5" class="py-8 text-center text-[#94A3B8]">
<td colspan="5" class="py-8 text-center text-[var(--color-text-secondary)]">
No tasks found. <a href="/agent-jobs/tasks/new" class="text-blue-400 hover:text-blue-300">Create one</a>
</td>
</tr>
@@ -290,12 +284,12 @@
</div>
<!-- Jobs Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8" x-show="hasMCPModels">
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-xl p-8" x-show="hasMCPModels">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-semibold text-[#E5E7EB]">Job History</h2>
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)]">Job History</h2>
<div class="flex space-x-4">
<select x-model="jobFilter" @change="fetchJobs()"
class="bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB]">
class="bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded px-4 py-2 text-[var(--color-text-primary)]">
<option value="">All Status</option>
<option value="pending">Pending</option>
<option value="running">Running</option>
@@ -313,26 +307,26 @@
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-[#38BDF8]/20">
<th class="text-left py-3 px-4 text-[#94A3B8]">Job ID</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Task</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Status</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Created</th>
<th class="text-left py-3 px-4 text-[#94A3B8]">Actions</th>
<tr class="border-b border-[var(--color-primary-border)]/20">
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Job ID</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Task</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Status</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Created</th>
<th class="text-left py-3 px-4 text-[var(--color-text-secondary)]">Actions</th>
</tr>
</thead>
<tbody>
<template x-for="job in jobs" :key="job.id">
<tr class="border-b border-[#38BDF8]/10 hover:bg-[#101827]">
<tr class="border-b border-[var(--color-primary-border)]/10 hover:bg-[var(--color-bg-primary)]">
<td class="py-3 px-4">
<a :href="'/agent-jobs/jobs/' + job.id"
class="font-mono text-sm text-[#38BDF8] hover:text-[#38BDF8]/80 hover:underline"
class="font-mono text-sm text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 hover:underline"
x-text="job.id.substring(0, 8) + '...'"
:title="job.id"></a>
</td>
<td class="py-3 px-4">
<a :href="'/agent-jobs/tasks/' + job.task_id"
class="text-[#38BDF8] hover:text-[#38BDF8]/80 hover:underline"
class="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 hover:underline"
x-text="getTaskName(job.task_id)"
:title="'Task ID: ' + job.task_id"></a>
</td>
@@ -347,7 +341,7 @@
class="px-2 py-1 rounded text-xs text-white"
x-text="job.status"></span>
</td>
<td class="py-3 px-4 text-[#94A3B8] text-sm" x-text="formatDate(job.created_at)"></td>
<td class="py-3 px-4 text-[var(--color-text-secondary)] text-sm" x-text="formatDate(job.created_at)"></td>
<td class="py-3 px-4">
<button x-show="job.status === 'pending' || job.status === 'running'"
@click="cancelJob(job.id)"
@@ -358,7 +352,7 @@
</tr>
</template>
<tr x-show="jobs.length === 0">
<td colspan="5" class="py-8 text-center text-[#94A3B8]">No jobs found</td>
<td colspan="5" class="py-8 text-center text-[var(--color-text-secondary)]">No jobs found</td>
</tr>
</tbody>
</table>
@@ -371,41 +365,41 @@
x-cloak
@click.away="showExecuteTaskModal = false; selectedTaskForExecution = null; executionParameters = {}; executionParametersText = ''"
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 max-w-2xl w-full mx-4">
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-xl p-8 max-w-2xl w-full mx-4">
<div class="flex justify-between items-center mb-6">
<h3 class="text-2xl font-semibold text-[#E5E7EB]">Execute Task</h3>
<h3 class="text-2xl font-semibold text-[var(--color-text-primary)]">Execute Task</h3>
<button @click="showExecuteTaskModal = false; selectedTaskForExecution = null; executionParameters = {}; executionParametersText = ''"
class="text-[#94A3B8] hover:text-[#E5E7EB]">
class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<template x-if="selectedTaskForExecution">
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Task</label>
<div class="text-[#94A3B8]" x-text="selectedTaskForExecution.name"></div>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Task</label>
<div class="text-[var(--color-text-secondary)]" x-text="selectedTaskForExecution.name"></div>
</div>
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Parameters</label>
<p class="text-xs text-[#94A3B8] mb-3">
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Parameters</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-3">
Enter parameters as key-value pairs (one per line, format: key=value).
These will be used to template the prompt.
</p>
<textarea x-model="executionParametersText"
rows="6"
placeholder="user_name=Alice&#10;job_title=Software Engineer&#10;task_description=Review code changes"
class="w-full bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] font-mono text-sm focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50"></textarea>
<p class="text-xs text-[#94A3B8] mt-1">
Example: <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">user_name=Alice</code>
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary-border)] focus:ring-2 focus:ring-[#38BDF8]/50"></textarea>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">
Example: <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">user_name=Alice</code>
</p>
</div>
<div class="flex justify-end space-x-4">
<button @click="showExecuteTaskModal = false; selectedTaskForExecution = null; executionParameters = {}; executionParametersText = ''"
class="px-4 py-2 bg-[#101827] hover:bg-[#0A0E1A] text-[#E5E7EB] rounded-lg transition-colors">
class="px-4 py-2 bg-[var(--color-bg-primary)] hover:bg-[#0A0E1A] text-[var(--color-text-primary)] rounded-lg transition-colors">
Cancel
</button>
<button @click="executeTaskWithParameters()"
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors">
class="px-4 py-2 bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-white rounded-lg transition-colors">
<i class="fas fa-play mr-2"></i>Execute
</button>
</div>

View File

@@ -12,10 +12,8 @@
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
<div class="flex justify-between items-center">
<div>
<h1 class="text-4xl font-bold text-[#E5E7EB] mb-2">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] via-[#8B5CF6] to-[#38BDF8]">
<span x-text="isNewTask ? 'Create Task' : (isEditMode ? 'Edit Task' : 'Task Details')"></span>
</span>
<h1 class="hero-title">
<span x-text="isNewTask ? 'Create Task' : (isEditMode ? 'Edit Task' : 'Task Details')"></span>
</h1>
<p class="text-lg text-[#94A3B8]" x-text="isNewTask ? 'Create a new agent task' : (task ? task.name : 'Loading...')"></p>
</div>

View File

@@ -35,14 +35,12 @@
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Header -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8 mb-12">
<div class="max-w-5xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#8B5CF6] via-[#38BDF8] to-[#8B5CF6]">
Backend Management
</span>
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
Backend Management
</h1>
<p class="text-lg md:text-xl text-[#94A3B8] mb-6 font-light">
<p class="hero-subtitle">
Discover and install AI backends to power your models
</p>
<div class="flex flex-wrap justify-center items-center gap-6 text-sm md:text-base">
@@ -56,8 +54,7 @@
<span class="font-semibold text-cyan-300" x-text="installedBackends"></span>
<span class="text-[#94A3B8] ml-1">installed</span>
</a>
<a href="https://localai.io/backends/" target="_blank"
class="inline-flex items-center bg-cyan-600 hover:bg-cyan-700 text-white px-4 py-2 rounded-lg transition-colors">
<a href="https://localai.io/backends/" target="_blank" class="btn-primary">
<i class="fas fa-info-circle mr-2"></i>
<span>Documentation</span>
<i class="fas fa-external-link-alt ml-2 text-xs"></i>
@@ -69,7 +66,7 @@
{{template "views/partials/inprogress" .}}
<!-- Search and Filter Section -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8 mb-8">
<div class="card p-8 mb-8">
<div>
<!-- Search Input -->
<div class="mb-8">
@@ -84,7 +81,7 @@
<input
x-model="searchTerm"
@input.debounce.500ms="fetchBackends()"
class="w-full pl-12 pr-16 py-4 text-base font-normal text-[#E5E7EB] bg-[#101827] border border-[#1E293B] rounded-lg transition-colors focus:text-[#E5E7EB] focus:bg-[#101827] focus:border-[#8B5CF6] focus:ring-2 focus:ring-[#8B5CF6]/50 focus:outline-none"
class="input w-full pl-12 pr-16 py-4"
type="search"
placeholder="Search backends by name, description or type...">
<span class="absolute right-4 top-4" x-show="loading">

View File

@@ -526,30 +526,30 @@ SOFTWARE.
<script defer src="static/chat.js"></script>
{{ $allGalleryConfigs:=.GalleryConfig }}
{{ $model:=.Model}}
<body class="bg-[#101827] text-[#E5E7EB] flex flex-col h-screen" x-data="{ sidebarOpen: true, showClearAlert: false }">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] flex flex-col h-screen" x-data="{ sidebarOpen: true, showClearAlert: false }">
{{template "views/partials/navbar" .}}
<!-- Main container with sidebar toggle -->
<div class="flex flex-1 overflow-hidden relative">
<!-- Sidebar -->
<div
class="sidebar bg-[#1E293B] fixed top-14 bottom-0 left-0 w-56 transform transition-transform duration-300 ease-in-out z-30 border-r border-[#101827] overflow-y-auto"
class="sidebar bg-[var(--color-bg-secondary)] fixed top-14 bottom-0 left-0 w-56 transform transition-transform duration-300 ease-in-out z-30 border-r border-[var(--color-bg-primary)] overflow-y-auto"
:class="sidebarOpen ? 'translate-x-0' : '-translate-x-full'">
<div class="p-3 flex justify-between items-center border-b border-[#101827]">
<div class="p-3 flex justify-between items-center border-b border-[var(--color-bg-primary)]">
<div class="flex items-center gap-2">
<h2 class="text-sm font-semibold text-[#E5E7EB]">Settings</h2>
<h2 class="text-sm font-semibold text-[var(--color-text-primary)]">Settings</h2>
<a
href="https://localai.io/features/text-generation/"
target="_blank"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-xs"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-xs"
title="Documentation">
<i class="fas fa-book"></i>
</a>
</div>
<button
@click="sidebarOpen = false"
class="text-[#94A3B8] hover:text-[#E5E7EB] focus:outline-none text-xs"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] focus:outline-none text-xs"
title="Hide sidebar">
<i class="fa-solid fa-chevron-left"></i>
</button>
@@ -560,14 +560,14 @@ SOFTWARE.
<!-- Model selection - Compact -->
<div class="space-y-1.5">
<div class="flex items-center justify-between">
<label class="text-xs font-medium text-[#94A3B8] uppercase tracking-wide">Model</label>
<label class="text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide">Model</label>
{{ if $model }}
{{ $galleryConfig:= index $allGalleryConfigs $model}}
{{ if $galleryConfig }}
<button
data-twe-ripple-init
data-twe-ripple-color="light"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-xs p-1"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-xs p-1"
data-modal-target="model-info-modal"
data-modal-toggle="model-info-modal"
title="Model Information">
@@ -577,7 +577,7 @@ SOFTWARE.
{{ end }}
{{ if $model }}
<a href="/models/edit/{{$model}}"
class="text-[#94A3B8] hover:text-yellow-400 transition-colors text-xs p-1"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-warning)] transition-colors text-xs p-1"
title="Edit Model Configuration">
<i class="fas fa-edit"></i>
</a>
@@ -585,10 +585,10 @@ SOFTWARE.
</div>
<select
id="modelSelector"
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring-1 focus:ring-[#38BDF8]/50 rounded p-1.5 text-xs appearance-none"
class="input w-full p-1.5 text-xs"
onchange="updateModelAndContextSize(this);"
>
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
{{ range .ModelsConfig }}
{{ $cfg := . }}
@@ -600,7 +600,7 @@ SOFTWARE.
{{ if eq $cfg.Name $model }} selected {{end}}
{{ if $cfg.LLMConfig.ContextSize }}data-context-size="{{$cfg.LLMConfig.ContextSize}}"{{ end }}
data-has-mcp="{{if $hasMCP}}true{{else}}false{{end}}"
class="bg-[#101827] text-[#E5E7EB]"
class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]"
>
{{$cfg.Name}}
</option>
@@ -611,7 +611,7 @@ SOFTWARE.
<option
value="chat/{{.}}"
{{ if eq . $model }} selected {{ end }}
class="bg-[#101827] text-[#E5E7EB]"
class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]"
>
{{.}}
</option>
@@ -693,17 +693,17 @@ SOFTWARE.
}
}">
<div class="flex items-center justify-between">
<h2 class="text-xs font-semibold text-[#94A3B8] uppercase tracking-wide">Chats</h2>
<h2 class="text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide">Chats</h2>
<div class="flex items-center gap-1">
<button
@click="createNewChat()"
class="text-[#38BDF8] hover:text-[#8B5CF6] transition-colors text-xs p-1"
class="text-[var(--color-primary)] hover:text-[var(--color-accent)] transition-colors text-xs p-1"
title="New Chat">
<i class="fa-solid fa-plus"></i>
</button>
<button
@click="if (confirm('Delete all chats? This cannot be undone.')) { bulkDeleteChats({deleteAll: true}); }"
class="text-[#94A3B8] hover:text-red-400 transition-colors text-xs p-1"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-error)] transition-colors text-xs p-1"
title="Delete all chats"
x-show="$store.chat.chats.length > 0">
<i class="fa-solid fa-trash"></i>
@@ -717,24 +717,24 @@ SOFTWARE.
type="text"
x-model="searchQuery"
placeholder="Search conversations..."
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring-1 focus:ring-[#38BDF8]/50 rounded px-2 py-1.5 pl-7 text-xs placeholder-[#94A3B8]"
class="w-full bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-bg-secondary)] focus:border-[var(--color-primary-border)] focus:ring-1 focus:ring-[var(--color-primary)]/50 rounded px-2 py-1.5 pl-7 text-xs placeholder-[var(--color-text-secondary)]"
/>
<i class="fa-solid fa-search absolute left-2 top-1/2 transform -translate-y-1/2 text-[#94A3B8] text-xs"></i>
<i class="fa-solid fa-search absolute left-2 top-1/2 transform -translate-y-1/2 text-[var(--color-text-secondary)] text-xs"></i>
<button
x-show="searchQuery.length > 0"
@click="searchQuery = ''"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-[#94A3B8] hover:text-[#E5E7EB] text-xs"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] text-xs"
title="Clear search">
<i class="fa-solid fa-times"></i>
</button>
</div>
<!-- Chat List -->
<div class="max-h-80 overflow-y-auto space-y-1 border border-[#1E293B] rounded p-1.5">
<div class="max-h-80 overflow-y-auto space-y-1 border border-[var(--color-bg-secondary)] rounded p-1.5">
<template x-for="chat in filteredChats()" :key="chat.id">
<div
class="flex items-center justify-between p-1.5 rounded hover:bg-[#1E293B] transition-colors cursor-pointer group"
:class="{ 'bg-[#38BDF8]/20 border border-[#38BDF8]/40': $store.chat.activeChatId === chat.id }"
class="flex items-center justify-between p-1.5 rounded hover:bg-[var(--color-bg-secondary)] transition-colors cursor-pointer group"
:class="{ 'bg-[var(--color-primary)]/20 border border-[var(--color-primary-border)]/40': $store.chat.activeChatId === chat.id }"
@click="if (editingChatId !== chat.id) switchChat(chat.id)"
>
<div class="flex-1 min-w-0">
@@ -745,7 +745,7 @@ SOFTWARE.
@blur="updateChatName(chat.id, editingName); editingChatId = null"
@keydown.enter="updateChatName(chat.id, editingName); editingChatId = null"
@keydown.escape="editingChatId = null"
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#38BDF8] rounded px-1.5 py-0.5 text-xs"
class="w-full bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-primary-border)] rounded px-1.5 py-0.5 text-xs"
x-ref="editInput"
x-effect="if (editingChatId === chat.id) { $refs.editInput?.focus(); editingName = chat.name; }"
/>
@@ -755,17 +755,17 @@ SOFTWARE.
<!-- Loading indicator for active requests -->
<div x-show="$store.chat.hasActiveRequest(chat.id)"
class="flex-shrink-0">
<i class="fa-solid fa-spinner fa-spin text-[#38BDF8] text-[10px]"></i>
<i class="fa-solid fa-spinner fa-spin text-[var(--color-primary)] text-[10px]"></i>
</div>
<div class="flex-1 min-w-0">
<div
class="text-xs font-medium text-[#E5E7EB] truncate"
class="text-xs font-medium text-[var(--color-text-primary)] truncate"
@dblclick="editingChatId = chat.id; editingName = chat.name"
x-text="chat.name || 'New Chat'"
></div>
<div class="flex items-center gap-1.5">
<div class="text-[10px] text-[#94A3B8] truncate" x-text="getLastMessagePreview(chat)"></div>
<span class="text-[9px] text-[#94A3B8]/60" x-text="formatChatDate(chat.updatedAt || chat.createdAt)"></span>
<div class="text-[10px] text-[var(--color-text-secondary)] truncate" x-text="getLastMessagePreview(chat)"></div>
<span class="text-[9px] text-[var(--color-text-secondary)]/60" x-text="formatChatDate(chat.updatedAt || chat.createdAt)"></span>
</div>
</div>
</div>
@@ -774,13 +774,13 @@ SOFTWARE.
<div class="flex items-center space-x-0.5 opacity-0 group-hover:opacity-100 transition-opacity">
<button
@click.stop="editingChatId = chat.id; editingName = chat.name"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-[10px] p-0.5"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-[10px] p-0.5"
title="Rename chat">
<i class="fa-solid fa-edit"></i>
</button>
<button
@click.stop="if (confirm('Delete this chat?')) deleteChat(chat.id)"
class="text-[#94A3B8] hover:text-red-400 transition-colors text-[10px] p-0.5"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-error)] transition-colors text-[10px] p-0.5"
title="Delete chat"
x-show="$store.chat.chats.length > 1">
<i class="fa-solid fa-trash"></i>
@@ -788,10 +788,10 @@ SOFTWARE.
</div>
</div>
</template>
<div x-show="filteredChats().length === 0 && $store.chat.chats.length > 0" class="text-xs text-[#94A3B8] text-center py-2">
<div x-show="filteredChats().length === 0 && $store.chat.chats.length > 0" class="text-xs text-[var(--color-text-secondary)] text-center py-2">
No conversations match your search
</div>
<div x-show="$store.chat.chats.length === 0" class="text-xs text-[#94A3B8] text-center py-2">
<div x-show="$store.chat.chats.length === 0" class="text-xs text-[var(--color-text-secondary)] text-center py-2">
No chats yet
</div>
</div>
@@ -837,21 +837,21 @@ SOFTWARE.
});
}
}" x-show="mcpAvailable">
<div class="flex items-center justify-between px-2 py-1.5 text-xs rounded text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] transition-colors">
<span><i class="fa-solid fa-plug mr-1.5 text-[#38BDF8]"></i> MCP Mode</span>
<div class="flex items-center justify-between px-2 py-1.5 text-xs rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors">
<span><i class="fa-solid fa-plug mr-1.5 text-[var(--color-primary)]"></i> MCP Mode</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="mcp-toggle" class="sr-only peer" :checked="$store.chat.activeChat()?.mcpMode || false" @change="if ($store.chat.activeChat()) { $store.chat.activeChat().mcpMode = $event.target.checked; autoSaveChats(); }">
<div class="w-9 h-5 bg-[#101827] peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-[#38BDF8]/30 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-[#1E293B] after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-[#38BDF8]"></div>
<div class="w-9 h-5 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-[var(--color-primary)]/30 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-[var(--color-bg-secondary)] after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
</label>
</div>
<!-- MCP Mode Notification - Compact -->
<div x-show="$store.chat.activeChat()?.mcpMode" class="p-2 bg-[#38BDF8]/10 border border-[#38BDF8]/30 rounded text-[#94A3B8] text-[10px]">
<div x-show="$store.chat.activeChat()?.mcpMode" class="p-2 bg-[var(--color-primary)]/10 border border-[var(--color-primary-border)]/30 rounded text-[var(--color-text-secondary)] text-[10px]">
<div class="flex items-start space-x-1.5">
<i class="fa-solid fa-info-circle text-[#38BDF8] mt-0.5"></i>
<i class="fa-solid fa-info-circle text-[var(--color-primary)] mt-0.5"></i>
<div>
<p class="font-medium text-[#E5E7EB] mb-0.5">Non-streaming Mode</p>
<p class="text-[#94A3B8]">Full processing before display (may take up to 5 minutes on CPU).</p>
<p class="font-medium text-[var(--color-text-primary)] mb-0.5">Non-streaming Mode</p>
<p class="text-[var(--color-text-secondary)]">Full processing before display (may take up to 5 minutes on CPU).</p>
</div>
</div>
</div>
@@ -859,9 +859,9 @@ SOFTWARE.
<button
@click="showPromptForm = !showPromptForm"
class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] transition-colors"
class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors"
>
<span><i class="fa-solid fa-message mr-1.5 text-[#38BDF8]"></i> System Prompt</span>
<span><i class="fa-solid fa-message mr-1.5 text-[var(--color-primary)]"></i> System Prompt</span>
<i :class="showPromptForm ? 'fa-chevron-up' : 'fa-chevron-down'" class="fa-solid text-[10px]"></i>
</button>
@@ -881,13 +881,14 @@ SOFTWARE.
setTimeout(() => {this.showToast = false;}, 2000);
}
}
}" class="p-2 bg-[#1E293B] border border-[#38BDF8]/20 rounded pl-4 border-l-2 border-[#1E293B]">
}" class="p-2 bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded pl-4 border-l-2 border-[var(--color-bg-secondary)]">
<form id="system_prompt" @submit.prevent="isUpdated" class="flex flex-col space-y-1.5">
<textarea
type="text"
id="systemPrompt"
class="input"
name="systemPrompt"
class="bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring-1 focus:ring-[#38BDF8] focus:ring-opacity-50 rounded p-1.5 text-xs appearance-none min-h-20 placeholder-[#94A3B8]"
class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-bg-secondary)] focus:border-[var(--color-primary-border)] focus:ring-1 focus:ring-[var(--color-primary)] focus:ring-opacity-50 rounded p-1.5 text-xs appearance-none min-h-20 placeholder-[var(--color-text-secondary)]"
placeholder="System prompt"
:value="$store.chat.activeChat()?.systemPrompt || ''"
@input="if ($store.chat.activeChat()) { $store.chat.activeChat().systemPrompt = $event.target.value; $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
@@ -896,13 +897,13 @@ SOFTWARE.
<div
x-show="showToast"
x-transition
class="text-green-400 px-2 py-1 text-xs text-center bg-green-500/10 border border-green-500/30 rounded"
class="text-[var(--color-success)] px-2 py-1 text-xs text-center bg-[var(--color-success-light)] border border-[var(--color-success-light)] rounded"
>
Updated!
</div>
<button
type="submit"
class="px-2 py-1 text-xs rounded text-[#101827] bg-[#38BDF8] hover:bg-[#38BDF8]/90 transition-colors font-medium"
class="px-2 py-1 text-xs rounded text-[var(--color-bg-primary)] bg-[var(--color-primary)] hover:bg-[var(--color-primary)]/90 transition-colors font-medium"
>
Save
</button>
@@ -912,19 +913,19 @@ SOFTWARE.
<!-- Generation Parameters -->
<button
@click="showParamsForm = !showParamsForm"
class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] transition-colors"
class="w-full flex items-center justify-between px-2 py-1.5 text-xs rounded text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] transition-colors"
>
<span><i class="fa-solid fa-sliders mr-1.5 text-[#38BDF8]"></i> Generation Parameters</span>
<span><i class="fa-solid fa-sliders mr-1.5 text-[var(--color-primary)]"></i> Generation Parameters</span>
<i :class="showParamsForm ? 'fa-chevron-up' : 'fa-chevron-down'" class="fa-solid text-[10px]"></i>
</button>
<div x-show="showParamsForm" class="p-2 bg-[#1E293B] border border-[#38BDF8]/20 rounded pl-4 border-l-2 border-[#1E293B] overflow-hidden">
<div x-show="showParamsForm" class="p-2 bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded pl-4 border-l-2 border-[var(--color-bg-secondary)] overflow-hidden">
<div class="flex flex-col space-y-3">
<!-- Temperature -->
<div class="space-y-1 min-w-0">
<div class="flex items-center justify-between gap-2">
<label class="text-xs text-[#94A3B8] flex-shrink-0">Temperature</label>
<span class="text-xs text-[#E5E7EB] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.temperature !== null && $store.chat.activeChat()?.temperature !== undefined) ? $store.chat.activeChat().temperature.toFixed(2) : 'Default'"></span>
<label class="text-xs text-[var(--color-text-secondary)] flex-shrink-0">Temperature</label>
<span class="text-xs text-[var(--color-text-primary)] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.temperature !== null && $store.chat.activeChat()?.temperature !== undefined) ? $store.chat.activeChat().temperature.toFixed(2) : 'Default'"></span>
</div>
<div class="flex items-center gap-2 min-w-0">
<input
@@ -932,27 +933,27 @@ SOFTWARE.
min="0"
max="2"
step="0.01"
class="flex-1 min-w-0 h-1.5 bg-[#101827] rounded-lg appearance-none cursor-pointer accent-[#38BDF8]"
class="flex-1 min-w-0 h-1.5 bg-[var(--color-bg-primary)] rounded-lg appearance-none cursor-pointer accent-[var(--color-primary)]"
:value="$store.chat.activeChat()?.temperature ?? 1.0"
@input="if ($store.chat.activeChat()) { $store.chat.activeChat().temperature = parseFloat($event.target.value); $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
/>
<button
@click="if ($store.chat.activeChat()) { $store.chat.activeChat().temperature = null; $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-xs px-2 py-1 flex-shrink-0"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-xs px-2 py-1 flex-shrink-0"
title="Reset to default"
x-show="$store.chat.activeChat()?.temperature !== null && $store.chat.activeChat()?.temperature !== undefined"
>
<i class="fa-solid fa-rotate-left"></i>
</button>
</div>
<p class="text-[10px] text-[#94A3B8]">Controls randomness (0 = deterministic, 2 = very creative)</p>
<p class="text-[10px] text-[var(--color-text-secondary)]">Controls randomness (0 = deterministic, 2 = very creative)</p>
</div>
<!-- Top P -->
<div class="space-y-1 min-w-0">
<div class="flex items-center justify-between gap-2">
<label class="text-xs text-[#94A3B8] flex-shrink-0">Top P</label>
<span class="text-xs text-[#E5E7EB] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.topP !== null && $store.chat.activeChat()?.topP !== undefined) ? $store.chat.activeChat().topP.toFixed(2) : 'Default'"></span>
<label class="text-xs text-[var(--color-text-secondary)] flex-shrink-0">Top P</label>
<span class="text-xs text-[var(--color-text-primary)] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.topP !== null && $store.chat.activeChat()?.topP !== undefined) ? $store.chat.activeChat().topP.toFixed(2) : 'Default'"></span>
</div>
<div class="flex items-center gap-2 min-w-0">
<input
@@ -960,27 +961,27 @@ SOFTWARE.
min="0"
max="1"
step="0.01"
class="flex-1 min-w-0 h-1.5 bg-[#101827] rounded-lg appearance-none cursor-pointer accent-[#38BDF8]"
class="flex-1 min-w-0 h-1.5 bg-[var(--color-bg-primary)] rounded-lg appearance-none cursor-pointer accent-[var(--color-primary)]"
:value="$store.chat.activeChat()?.topP ?? 0.9"
@input="if ($store.chat.activeChat()) { $store.chat.activeChat().topP = parseFloat($event.target.value); $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
/>
<button
@click="if ($store.chat.activeChat()) { $store.chat.activeChat().topP = null; $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-xs px-2 py-1 flex-shrink-0"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-xs px-2 py-1 flex-shrink-0"
title="Reset to default"
x-show="$store.chat.activeChat()?.topP !== null && $store.chat.activeChat()?.topP !== undefined"
>
<i class="fa-solid fa-rotate-left"></i>
</button>
</div>
<p class="text-[10px] text-[#94A3B8]">Nucleus sampling threshold (0-1)</p>
<p class="text-[10px] text-[var(--color-text-secondary)]">Nucleus sampling threshold (0-1)</p>
</div>
<!-- Top K -->
<div class="space-y-1 min-w-0">
<div class="flex items-center justify-between gap-2">
<label class="text-xs text-[#94A3B8] flex-shrink-0">Top K</label>
<span class="text-xs text-[#E5E7EB] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.topK !== null && $store.chat.activeChat()?.topK !== undefined) ? $store.chat.activeChat().topK : 'Default'"></span>
<label class="text-xs text-[var(--color-text-secondary)] flex-shrink-0">Top K</label>
<span class="text-xs text-[var(--color-text-primary)] font-medium flex-shrink-0" x-text="($store.chat.activeChat()?.topK !== null && $store.chat.activeChat()?.topK !== undefined) ? $store.chat.activeChat().topK : 'Default'"></span>
</div>
<div class="flex items-center gap-2 min-w-0">
<input
@@ -988,20 +989,20 @@ SOFTWARE.
min="0"
max="100"
step="1"
class="flex-1 min-w-0 h-1.5 bg-[#101827] rounded-lg appearance-none cursor-pointer accent-[#38BDF8]"
class="flex-1 min-w-0 h-1.5 bg-[var(--color-bg-primary)] rounded-lg appearance-none cursor-pointer accent-[var(--color-primary)]"
:value="$store.chat.activeChat()?.topK ?? 40"
@input="if ($store.chat.activeChat()) { $store.chat.activeChat().topK = parseInt($event.target.value); $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
/>
<button
@click="if ($store.chat.activeChat()) { $store.chat.activeChat().topK = null; $store.chat.activeChat().updatedAt = Date.now(); autoSaveChats(); }"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors text-xs px-2 py-1 flex-shrink-0"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors text-xs px-2 py-1 flex-shrink-0"
title="Reset to default"
x-show="$store.chat.activeChat()?.topK !== null && $store.chat.activeChat()?.topK !== undefined"
>
<i class="fa-solid fa-rotate-left"></i>
</button>
</div>
<p class="text-[10px] text-[#94A3B8]">Limit sampling to top K tokens (0 = disabled)</p>
<p class="text-[10px] text-[var(--color-text-secondary)]">Limit sampling to top K tokens (0 = disabled)</p>
</div>
</div>
</div>
@@ -1015,30 +1016,30 @@ SOFTWARE.
:class="sidebarOpen ? 'ml-56' : 'ml-0'">
<!-- Chat header with toggle button -->
<div class="border-b border-[#1E293B] p-4 flex items-center justify-between">
<div class="border-b border-[var(--color-bg-secondary)] p-4 flex items-center justify-between">
<div class="flex items-center">
<!-- Sidebar toggle button moved to be the first element in the header and with clear styling -->
<button
@click="sidebarOpen = !sidebarOpen"
class="mr-4 text-[#94A3B8] hover:text-[#E5E7EB] focus:outline-none bg-[#1E293B] hover:bg-[#1E293B]/80 p-2 rounded transition-colors"
class="mr-4 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] focus:outline-none bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-secondary)]/80 p-2 rounded transition-colors"
style="min-width: 36px;"
title="Toggle settings">
<i class="fa-solid" :class="sidebarOpen ? 'fa-chevron-left' : 'fa-bars'"></i>
</button>
<div class="flex items-center">
<i class="fa-solid fa-comments mr-2 text-[#38BDF8]"></i>
<i class="fa-solid fa-comments mr-2 text-[var(--color-primary)]"></i>
{{ if $model }}
{{ $galleryConfig:= index $allGalleryConfigs $model}}
{{ if $galleryConfig }}
{{ if $galleryConfig.Icon }}<img src="{{$galleryConfig.Icon}}" class="rounded-lg w-8 h-8 mr-2">{{end}}
{{ end }}
{{ end }}
<h1 class="text-lg font-semibold text-[#E5E7EB]">
<h1 class="text-lg font-semibold text-[var(--color-text-primary)]">
Chat {{ if .Model }} with {{.Model}} {{ end }}
</h1>
<!-- Loading indicator next to model name -->
<div id="header-loading-indicator" class="ml-3 text-[#38BDF8]" style="display: none;">
<div id="header-loading-indicator" class="ml-3 text-[var(--color-primary)]" style="display: none;">
<i class="fas fa-spinner fa-spin text-sm"></i>
</div>
</div>
@@ -1049,7 +1050,7 @@ SOFTWARE.
@click="if (confirm('Clear all messages from this conversation? This action cannot be undone.')) { $store.chat.clear(); showClearAlert = true; setTimeout(() => showClearAlert = false, 3000); }"
id="clear"
title="Clear current chat history"
class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors p-2 rounded hover:bg-[#1E293B]"
class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors p-2 rounded hover:bg-[var(--color-bg-secondary)]"
x-show="$store.chat.activeChat() && ($store.chat.activeChat()?.history?.length || 0) > 0">
<i class="fa-solid fa-broom"></i>
</button>
@@ -1064,10 +1065,10 @@ SOFTWARE.
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
class="fixed top-20 right-4 z-50 max-w-sm pointer-events-none">
<div class="bg-[#38BDF8]/20 border border-[#38BDF8]/40 rounded-lg p-3 shadow-lg backdrop-blur-sm">
<div class="bg-[var(--color-primary)]/20 border border-[var(--color-primary-border)]/40 rounded-lg p-3 shadow-lg backdrop-blur-sm">
<div class="flex items-center gap-2">
<i class="fa-solid fa-check-circle text-[#38BDF8]"></i>
<span class="text-sm text-[#E5E7EB] font-medium">Chat history cleared successfully</span>
<i class="fa-solid fa-check-circle text-[var(--color-primary)]"></i>
<span class="text-sm text-[var(--color-text-primary)] font-medium">Chat history cleared successfully</span>
</div>
</div>
</div>
@@ -1075,12 +1076,12 @@ SOFTWARE.
<!-- Chat messages area -->
<div class="flex-1 p-4 overflow-auto" id="chat">
<p id="usage" x-show="!$store.chat.activeChat() || ($store.chat.activeChat()?.history?.length || 0) === 0" class="text-[#94A3B8]">
<p id="usage" x-show="!$store.chat.activeChat() || ($store.chat.activeChat()?.history?.length || 0) === 0" class="text-[var(--color-text-secondary)]">
Start chatting with the AI by typing a prompt in the input field below and pressing Enter.<br>
<ul class="list-disc list-inside mt-2 space-y-1">
<li>For models that support images, you can upload an image by clicking the <i class="fa-solid fa-image text-[#38BDF8]"></i> icon.</li>
<li>For models that support audio, you can upload an audio file by clicking the <i class="fa-solid fa-microphone text-[#38BDF8]"></i> icon.</li>
<li>To send a text, markdown or PDF file, click the <i class="fa-solid fa-file text-[#38BDF8]"></i> icon.</li>
<li>For models that support images, you can upload an image by clicking the <i class="fa-solid fa-image text-[var(--color-primary)]"></i> icon.</li>
<li>For models that support audio, you can upload an audio file by clicking the <i class="fa-solid fa-microphone text-[var(--color-primary)]"></i> icon.</li>
<li>To send a text, markdown or PDF file, click the <i class="fa-solid fa-file text-[var(--color-primary)]"></i> icon.</li>
</ul>
</p>
<div id="messages" class="max-w-3xl mx-auto space-y-2" :key="$store.chat.activeChatId">
@@ -1090,28 +1091,28 @@ SOFTWARE.
<template x-if="message.role === 'reasoning' || message.role === 'thinking'">
<div class="flex items-start space-x-2 mb-1">
<div class="flex flex-col flex-1">
<div class="p-2 flex-1 rounded-lg bg-[#38BDF8]/10 text-[#94A3B8] border border-[#38BDF8]/30">
<div class="p-2 flex-1 rounded-lg bg-[var(--color-primary)]/10 text-[var(--color-text-secondary)] border border-[var(--color-primary-border)]/30">
<button
@click="message.expanded = !message.expanded"
class="w-full flex items-center justify-between text-left hover:bg-[#38BDF8]/20 rounded p-2 transition-colors"
class="w-full flex items-center justify-between text-left hover:bg-[var(--color-primary)]/20 rounded p-2 transition-colors"
>
<div class="flex items-center space-x-2">
<i :class="message.role === 'thinking' ? 'fa-solid fa-brain' : 'fa-solid fa-lightbulb'" class="text-[#38BDF8]"></i>
<span class="text-xs font-semibold text-[#38BDF8]" x-text="message.role === 'thinking' ? 'Thinking' : 'Reasoning'"></span>
<span class="text-xs text-[#94A3B8]" x-show="message.content && message.content.length > 0" x-text="'(' + Math.ceil(message.content.length / 100) + ' lines)'"></span>
<i :class="message.role === 'thinking' ? 'fa-solid fa-brain' : 'fa-solid fa-lightbulb'" class="text-[var(--color-primary)]"></i>
<span class="text-xs font-semibold text-[var(--color-primary)]" x-text="message.role === 'thinking' ? 'Thinking' : 'Reasoning'"></span>
<span class="text-xs text-[var(--color-text-secondary)]" x-show="message.content && message.content.length > 0" x-text="'(' + Math.ceil(message.content.length / 100) + ' lines)'"></span>
</div>
<i
class="fa-solid text-[#38BDF8] transition-transform text-xs"
class="fa-solid text-[var(--color-primary)] transition-transform text-xs"
:class="message.expanded ? 'fa-chevron-up' : 'fa-chevron-down'"
></i>
</button>
<div
x-show="message.expanded"
x-transition
class="mt-2 pt-2 border-t border-[#38BDF8]/20"
class="mt-2 pt-2 border-t border-[var(--color-primary-border)]/20"
>
<div
class="text-[#E5E7EB] text-sm max-h-96 overflow-auto"
class="text-[var(--color-text-primary)] text-sm max-h-96 overflow-auto"
x-html="message.html"
data-thinking-box
x-effect="if (message.expanded && message.html) { setTimeout(() => { if ($el.scrollHeight > $el.clientHeight) { $el.scrollTo({ top: $el.scrollHeight, behavior: 'smooth' }); } }, 50); }"
@@ -1126,27 +1127,27 @@ SOFTWARE.
<template x-if="message.role === 'tool_call'">
<div class="flex items-start space-x-2 mb-1">
<div class="flex flex-col flex-1">
<div class="p-2 flex-1 rounded-lg bg-[#8B5CF6]/10 text-[#94A3B8] border border-[#8B5CF6]/30">
<div class="p-2 flex-1 rounded-lg bg-[var(--color-accent)]/10 text-[var(--color-text-secondary)] border border-[var(--color-accent-border)]/30">
<button
@click="message.expanded = !message.expanded"
class="w-full flex items-center justify-between text-left hover:bg-[#8B5CF6]/20 rounded p-2 transition-colors"
class="w-full flex items-center justify-between text-left hover:bg-[var(--color-accent)]/20 rounded p-2 transition-colors"
>
<div class="flex items-center space-x-2">
<i class="fa-solid fa-wrench text-[#8B5CF6]"></i>
<span class="text-xs font-semibold text-[#8B5CF6]">Tool Call</span>
<span class="text-xs text-[#94A3B8]" x-text="getToolName(message.content)"></span>
<i class="fa-solid fa-wrench text-[var(--color-accent)]"></i>
<span class="text-xs font-semibold text-[var(--color-accent)]">Tool Call</span>
<span class="text-xs text-[var(--color-text-secondary)]" x-text="getToolName(message.content)"></span>
</div>
<i
class="fa-solid text-[#8B5CF6] transition-transform text-xs"
class="fa-solid text-[var(--color-accent)] transition-transform text-xs"
:class="message.expanded ? 'fa-chevron-up' : 'fa-chevron-down'"
></i>
</button>
<div
x-show="message.expanded"
x-transition
class="mt-2 pt-2 border-t border-[#8B5CF6]/20"
class="mt-2 pt-2 border-t border-[var(--color-accent-border)]/20"
>
<div class="text-[#E5E7EB] text-xs max-h-96 overflow-auto tool-call-content"
<div class="text-[var(--color-text-primary)] text-xs max-h-96 overflow-auto tool-call-content"
x-html="message.html"
x-effect="if (message.expanded && window.hljs) { setTimeout(() => { $el.querySelectorAll('pre code.language-json').forEach(block => { if (!block.classList.contains('hljs')) window.hljs.highlightElement(block); }); }, 50); }"></div>
</div>
@@ -1159,27 +1160,27 @@ SOFTWARE.
<template x-if="message.role === 'tool_result'">
<div class="flex items-start space-x-2 mb-1">
<div class="flex flex-col flex-1">
<div class="p-2 flex-1 rounded-lg bg-[#10B981]/10 text-[#94A3B8] border border-[#10B981]/30">
<div class="p-2 flex-1 rounded-lg bg-[var(--color-success)]/10 text-[var(--color-text-secondary)] border border-[var(--color-success)]/30">
<button
@click="message.expanded = !message.expanded"
class="w-full flex items-center justify-between text-left hover:bg-[#10B981]/20 rounded p-2 transition-colors"
class="w-full flex items-center justify-between text-left hover:bg-[var(--color-success)]/20 rounded p-2 transition-colors"
>
<div class="flex items-center space-x-2">
<i class="fa-solid fa-check-circle text-[#10B981]"></i>
<span class="text-xs font-semibold text-[#10B981]">Tool Result</span>
<span class="text-xs text-[#94A3B8]" x-text="getToolName(message.content) || 'Success'"></span>
<i class="fa-solid fa-check-circle text-[var(--color-success)]"></i>
<span class="text-xs font-semibold text-[var(--color-success)]">Tool Result</span>
<span class="text-xs text-[var(--color-text-secondary)]" x-text="getToolName(message.content) || 'Success'"></span>
</div>
<i
class="fa-solid text-[#10B981] transition-transform text-xs"
class="fa-solid text-[var(--color-success)] transition-transform text-xs"
:class="message.expanded ? 'fa-chevron-up' : 'fa-chevron-down'"
></i>
</button>
<div
x-show="message.expanded"
x-transition
class="mt-2 pt-2 border-t border-[#10B981]/20"
class="mt-2 pt-2 border-t border-[var(--color-success)]/20"
>
<div class="text-[#E5E7EB] text-xs max-h-96 overflow-auto tool-result-content"
<div class="text-[var(--color-text-primary)] text-xs max-h-96 overflow-auto tool-result-content"
x-html="formatToolResult(message.content)"
x-effect="if (message.expanded && window.hljs) { setTimeout(() => { $el.querySelectorAll('pre code.language-json').forEach(block => { if (!block.classList.contains('hljs')) window.hljs.highlightElement(block); }); }, 50); }"></div>
</div>
@@ -1195,8 +1196,8 @@ SOFTWARE.
<template x-if="message.role === 'user'">
<div class="flex items-center space-x-2">
<div class="flex flex-col flex-1 items-end">
<span class="text-xs font-semibold text-[#94A3B8] mb-1">You</span>
<div class="p-3 flex-1 rounded-lg bg-gradient-to-br from-[#1E293B] to-[#101827] text-[#E5E7EB] border border-[#38BDF8]/20 shadow-lg" x-html="message.html"></div>
<span class="text-xs font-semibold text-[var(--color-text-secondary)] mb-1">You</span>
<div class="p-3 flex-1 rounded-lg bg-[var(--color-bg-secondary)] text-[var(--color-text-primary)] border border-[var(--color-primary-border)]/20 shadow-lg" x-html="message.html"></div>
<template x-if="message.image && message.image.length > 0">
<div class="mt-2 space-y-2">
<template x-for="(img, index) in message.image" :key="index">
@@ -1220,13 +1221,13 @@ SOFTWARE.
<template x-if="message.role != 'user' && message.role != 'thinking' && message.role != 'reasoning' && message.role != 'tool_call' && message.role != 'tool_result'">
<div class="flex items-center space-x-2">
{{ if $galleryConfig }}
{{ if $galleryConfig.Icon }}<img src="{{$galleryConfig.Icon}}" class="rounded-lg mt-2 max-w-8 max-h-8 border border-[#38BDF8]/20">{{end}}
{{ if $galleryConfig.Icon }}<img src="{{$galleryConfig.Icon}}" class="rounded-lg mt-2 max-w-8 max-h-8 border border-[var(--color-primary-border)]/20">{{end}}
{{ end }}
<div class="flex flex-col flex-1">
<span class="text-xs font-semibold text-[#94A3B8] mb-1">{{if .Model}}{{.Model}}{{else}}Assistant{{end}}</span>
<div class="flex-1 text-[#E5E7EB] flex items-center space-x-2 min-w-0">
<div class="p-3 rounded-lg bg-gradient-to-br from-[#1E293B] to-[#101827] border border-[#8B5CF6]/20 shadow-lg max-w-full overflow-x-auto overflow-wrap-anywhere" x-html="message.html"></div>
<button @click="copyToClipboard(message.html)" title="Copy to clipboard" class="text-[#94A3B8] hover:text-[#38BDF8] transition-colors p-1 flex-shrink-0">
<span class="text-xs font-semibold text-[var(--color-text-secondary)] mb-1">{{if .Model}}{{.Model}}{{else}}Assistant{{end}}</span>
<div class="flex-1 text-[var(--color-text-primary)] flex items-center space-x-2 min-w-0">
<div class="p-3 rounded-lg bg-[var(--color-bg-secondary)] border border-[var(--color-accent-border)]/20 shadow-lg max-w-full overflow-x-auto overflow-wrap-anywhere" x-html="message.html"></div>
<button @click="copyToClipboard(message.html)" title="Copy to clipboard" class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors p-1 flex-shrink-0">
<i class="fa-solid fa-copy"></i>
</button>
</div>
@@ -1253,7 +1254,7 @@ SOFTWARE.
{{ else }}
<i
class="fa-solid h-8 w-8"
:class="message.role === 'user' ? 'fa-user text-[#38BDF8]' : 'fa-robot text-[#8B5CF6]'"
:class="message.role === 'user' ? 'fa-user text-[var(--color-primary)]' : 'fa-robot text-[var(--color-accent)]'"
></i>
{{ end }}
</div>
@@ -1264,18 +1265,18 @@ SOFTWARE.
<!-- Chat Input -->
<div class="p-4 border-t border-[#1E293B]" x-data="{ inputValue: '', shiftPressed: false, attachedFiles: [] }">
<div class="p-4 border-t border-[var(--color-bg-secondary)]" x-data="{ inputValue: '', shiftPressed: false, attachedFiles: [] }">
<form id="prompt" action="chat/{{.Model}}" method="get" @submit.prevent="submitPrompt" class="max-w-3xl mx-auto">
<!-- Attachment Tags - Show above input when files are attached -->
<div x-show="attachedFiles.length > 0" class="mb-3 flex flex-wrap gap-2 items-center">
<template x-for="(file, index) in attachedFiles" :key="index">
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm bg-[#38BDF8]/20 border border-[#38BDF8]/40 text-[#E5E7EB]">
<i :class="file.type === 'image' ? 'fa-solid fa-image' : file.type === 'audio' ? 'fa-solid fa-microphone' : 'fa-solid fa-file'" class="text-[#38BDF8]"></i>
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm bg-[var(--color-primary)]/20 border border-[var(--color-primary-border)]/40 text-[var(--color-text-primary)]">
<i :class="file.type === 'image' ? 'fa-solid fa-image' : file.type === 'audio' ? 'fa-solid fa-microphone' : 'fa-solid fa-file'" class="text-[var(--color-primary)]"></i>
<span x-text="file.name" class="max-w-[200px] truncate"></span>
<button
type="button"
@click="attachedFiles.splice(index, 1); removeFileFromInput(file.type, file.name)"
class="ml-1 text-[#94A3B8] hover:text-[#E5E7EB] transition-colors"
class="ml-1 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors"
title="Remove attachment"
>
<i class="fa-solid fa-times text-xs"></i>
@@ -1287,62 +1288,63 @@ SOFTWARE.
<!-- Token Usage and Context Window - Compact above input -->
<div class="mb-3 flex items-center justify-between gap-4 text-xs">
<!-- Token Usage -->
<div class="flex items-center gap-3 text-[#94A3B8]">
<div class="flex items-center gap-3 text-[var(--color-text-secondary)]">
<div class="flex items-center gap-1">
<i class="fas fa-chart-line text-[#38BDF8]"></i>
<i class="fas fa-chart-line text-[var(--color-primary)]"></i>
<span>Prompt:</span>
<span class="text-[#E5E7EB] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.promptTokens || 0)"></span>
<span class="text-[var(--color-text-primary)] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.promptTokens || 0)"></span>
</div>
<div class="flex items-center gap-1">
<span>Completion:</span>
<span class="text-[#E5E7EB] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.completionTokens || 0)"></span>
<span class="text-[var(--color-text-primary)] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.completionTokens || 0)"></span>
</div>
<div class="flex items-center gap-1 border-l border-[#1E293B] pl-3">
<span class="text-[#38BDF8] font-semibold">Total:</span>
<span class="text-[#E5E7EB] font-bold" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.totalTokens || 0)"></span>
<div class="flex items-center gap-1 border-l border-[var(--color-bg-secondary)] pl-3">
<span class="text-[var(--color-primary)] font-semibold">Total:</span>
<span class="text-[var(--color-text-primary)] font-bold" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.totalTokens || 0)"></span>
</div>
<!-- Tokens per second display -->
<div id="tokens-per-second-container" class="flex items-center gap-1 border-l border-[#1E293B] pl-3">
<i class="fas fa-tachometer-alt text-[#38BDF8]"></i>
<span id="tokens-per-second" class="text-[#E5E7EB] font-medium">-</span>
<span id="max-tokens-per-second-badge" class="ml-2 px-1.5 py-0.5 text-[10px] bg-[#38BDF8]/20 text-[#38BDF8] rounded border border-[#38BDF8]/30 hidden"></span>
<div id="tokens-per-second-container" class="flex items-center gap-1 border-l border-[var(--color-bg-secondary)] pl-3">
<i class="fas fa-tachometer-alt text-[var(--color-primary)]"></i>
<span id="tokens-per-second" class="text-[var(--color-text-primary)] font-medium">-</span>
<span id="max-tokens-per-second-badge" class="ml-2 px-1.5 py-0.5 text-[10px] bg-[var(--color-primary)]/20 text-[var(--color-primary)] rounded border border-[var(--color-primary-border)]/30 hidden"></span>
</div>
</div>
<!-- Context Window -->
<template x-if="$store.chat.activeChat()?.contextSize && $store.chat.activeChat().contextSize > 0">
<div class="flex items-center gap-2 text-[#94A3B8]">
<i class="fas fa-database text-[#38BDF8]"></i>
<div class="flex items-center gap-2 text-[var(--color-text-secondary)]">
<i class="fas fa-database text-[var(--color-primary)]"></i>
<span>
<span class="text-[#E5E7EB] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.totalTokens || 0)"></span>
<span class="text-[var(--color-text-primary)] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.tokenUsage?.totalTokens || 0)"></span>
/
<span class="text-[#E5E7EB] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.contextSize || 0)"></span>
<span class="text-[var(--color-text-primary)] font-medium" x-text="new Intl.NumberFormat().format($store.chat.activeChat()?.contextSize || 0)"></span>
</span>
<div class="w-16 bg-[#101827] rounded-full h-1.5 overflow-hidden border border-[#1E293B]">
<div class="w-16 bg-[var(--color-bg-primary)] rounded-full h-1.5 overflow-hidden border border-[var(--color-bg-secondary)]">
<div class="h-full rounded-full transition-all duration-300 ease-out"
:class="{
'bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]': $store.chat.getContextUsagePercent() < 80,
'bg-gradient-to-r from-[var(--color-primary)] to-[var(--color-accent)]': $store.chat.getContextUsagePercent() < 80,
'bg-gradient-to-r from-yellow-500 to-orange-500': $store.chat.getContextUsagePercent() >= 80 && $store.chat.getContextUsagePercent() < 95,
'bg-gradient-to-r from-red-500 to-red-600': $store.chat.getContextUsagePercent() >= 95
}"
:style="'width: ' + Math.min(100, $store.chat.getContextUsagePercent()) + '%'">
</div>
</div>
<span class="text-[#94A3B8]" x-text="Math.round($store.chat.getContextUsagePercent()) + '%'"></span>
<span x-show="$store.chat.getContextUsagePercent() >= 80" class="text-yellow-400">
<span class="text-[var(--color-text-secondary)]" x-text="Math.round($store.chat.getContextUsagePercent()) + '%'"></span>
<span x-show="$store.chat.getContextUsagePercent() >= 80" class="text-[var(--color-warning)]">
<i class="fas fa-exclamation-triangle"></i>
</span>
</div>
</template>
</div>
<div class="relative w-full bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl shadow-lg focus-within:ring-2 focus-within:ring-[#38BDF8]/50 focus-within:border-[#38BDF8] transition-all duration-200">
<div class="relative w-full">
<textarea
id="input"
name="input"
x-model="inputValue"
class="input w-full p-3 pr-16 resize-none border-0"
placeholder="Send a message..."
class="p-3 pr-16 w-full bg-[#1E293B] text-[#E5E7EB] placeholder-[#94A3B8] focus:outline-none resize-none border-0 rounded-xl transition-colors duration-200"
class="p-3 pr-16 w-full bg-[var(--color-bg-secondary)] text-[var(--color-text-primary)] placeholder-[var(--color-text-secondary)] focus:outline-none resize-none border-0 rounded-xl transition-colors duration-200"
required
@keydown.shift="shiftPressed = true"
@keyup.shift="shiftPressed = false"
@@ -1352,19 +1354,19 @@ SOFTWARE.
<button
type="button"
onclick="document.getElementById('input_image').click()"
class="fa-solid fa-image text-[#94A3B8] absolute right-12 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-image text-[var(--color-text-secondary)] absolute right-12 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Attach images"
></button>
<button
type="button"
onclick="document.getElementById('input_audio').click()"
class="fa-solid fa-microphone text-[#94A3B8] absolute right-20 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-microphone text-[var(--color-text-secondary)] absolute right-20 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Attach an audio file"
></button>
<button
type="button"
onclick="document.getElementById('input_file').click()"
class="fa-solid fa-file text-[#94A3B8] absolute right-28 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-file text-[var(--color-text-secondary)] absolute right-28 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Upload text, markdown or PDF file"
></button>
@@ -1375,7 +1377,7 @@ SOFTWARE.
id="stop-button"
type="button"
onclick="stopRequest()"
class="text-lg p-2 text-red-400 hover:text-red-500 transition-colors duration-200"
class="text-lg p-2 text-[var(--color-error)] hover:text-[var(--color-error)] transition-colors duration-200"
style="display: none;"
title="Stop request"
>
@@ -1386,7 +1388,7 @@ SOFTWARE.
<button
id="send-button"
type="submit"
class="text-lg p-2 text-[#94A3B8] hover:text-[#38BDF8] transition-colors duration-200"
class="text-lg p-2 text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors duration-200"
title="Send message (Enter)"
>
<i class="fa-solid fa-paper-plane"></i>
@@ -1504,7 +1506,7 @@ SOFTWARE.
// Format the JSON nicely
const formatted = JSON.stringify(parsed, null, 2);
return DOMPurify.sanitize('<pre class="bg-[#101827] p-3 rounded border border-[#10B981]/20 overflow-x-auto"><code class="language-json">' + formatted + '</code></pre>');
return DOMPurify.sanitize('<pre class="bg-[var(--color-bg-primary)] p-3 rounded border border-[var(--color-success)]/20 overflow-x-auto"><code class="language-json">' + formatted + '</code></pre>');
} catch (e) {
// If not JSON, try to format as markdown or plain text
try {
@@ -1534,7 +1536,7 @@ SOFTWARE.
try {
const parsed = JSON.parse(jsonStr);
const formatted = JSON.stringify(parsed, null, 2);
return DOMPurify.sanitize('<pre class="bg-[#101827] p-3 rounded border border-[#10B981]/20 overflow-x-auto"><code class="language-json">' + formatted + '</code></pre>');
return DOMPurify.sanitize('<pre class="bg-[var(--color-bg-primary)] p-3 rounded border border-[var(--color-success)]/20 overflow-x-auto"><code class="language-json">' + formatted + '</code></pre>');
} catch (e2) {
// Fall through to markdown
}
@@ -1544,7 +1546,7 @@ SOFTWARE.
return DOMPurify.sanitize(marked.parse(content));
} catch (e2) {
// Last resort: plain text
return DOMPurify.sanitize('<pre class="bg-[#101827] p-3 rounded border border-[#10B981]/20 overflow-x-auto text-xs">' + content.replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</pre>');
return DOMPurify.sanitize('<pre class="bg-[var(--color-bg-primary)] p-3 rounded border border-[var(--color-success)]/20 overflow-x-auto text-xs">' + content.replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</pre>');
}
}
};
@@ -1813,18 +1815,18 @@ SOFTWARE.
.tool-call-content .hljs-keyword,
.tool-result-content .hljs-keyword {
color: #8B5CF6 !important;
color: var(--color-accent) !important;
font-weight: 600;
}
.tool-call-content .hljs-string,
.tool-result-content .hljs-string {
color: #10B981 !important;
color: var(--color-success) !important;
}
.tool-call-content .hljs-number,
.tool-result-content .hljs-number {
color: #38BDF8 !important;
color: var(--color-primary) !important;
}
.tool-call-content .hljs-literal,
@@ -1839,12 +1841,12 @@ SOFTWARE.
.tool-call-content .hljs-property,
.tool-result-content .hljs-property {
color: #38BDF8 !important;
color: var(--color-primary) !important;
}
.tool-call-content .hljs-attr,
.tool-result-content .hljs-attr {
color: #8B5CF6 !important;
color: var(--color-accent) !important;
}
</style>

View File

@@ -14,10 +14,8 @@
<div class="mb-6 text-6xl text-red-400">
<i class="fas fa-exclamation-circle"></i>
</div>
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-red-400 to-red-600">
{{if .ErrorCode}}{{.ErrorCode}}{{else}}Error{{end}}
</span>
<h1 class="hero-title mb-4" style="color: var(--color-error);">
{{if .ErrorCode}}{{.ErrorCode}}{{else}}Error{{end}}
</h1>
<p class="text-xl text-[#94A3B8] mb-6">{{if .ErrorMessage}}{{.ErrorMessage}}{{else}}An unexpected error occurred{{end}}</p>
<div class="flex flex-wrap justify-center gap-4">

View File

@@ -162,9 +162,8 @@
<canvas id="networkCanvas"></canvas>
<div class="text-overlay">
<header class="text-center py-12">
<h1 class="text-5xl font-bold text-gray-100">
<h1 class="hero-title">
<i class="fa-solid fa-circle-nodes mr-2"></i> Network Clusters Explorer
</h1>
<p class="mt-4 text-lg">
View the clusters and workers available in each network.
@@ -188,7 +187,7 @@
</div>
<div class="flow-root">
<!-- Toggle button for showing/hiding the form -->
<button class="bg-red-600 hover:bg-blue-600 float-right mb-2 flex items-center px-4 py-2 rounded" @click="toggleForm()">
<button class="btn-primary float-right mb-2" @click="toggleForm()">
<!-- Conditional icon display -->
<i :class="showForm ? 'fa-solid fa-times' : 'fa-solid fa-plus'" class="mr-2"></i>
<span x-text="showForm ? 'Close' : 'Add New Network'"></span>
@@ -196,20 +195,20 @@
</div>
<!-- Form for adding a new network -->
<div class="form-container" x-show="showForm" @click.outside="showForm = false">
<h2 class="text-3xl font-bold mb-4"><i class="fa-solid fa-plus"></i> Add New Network</h2>
<h2 class="h2"><i class="fa-solid fa-plus"></i> Add New Network</h2>
<div class="form-control">
<label for="name">Network Name</label>
<input type="text" id="name" x-model="newNetwork.name" placeholder="Enter network name" />
<input type="text" id="name" x-model="newNetwork.name" placeholder="Enter network name" class="input" />
</div>
<div class="form-control">
<label for="description">Description</label>
<textarea id="description" x-model="newNetwork.description" placeholder="Enter description"></textarea>
<textarea id="description" x-model="newNetwork.description" placeholder="Enter description" class="input"></textarea>
</div>
<div class="form-control">
<label for="token">Token</label>
<textarea id="token" x-model="newNetwork.token" placeholder="Enter token"></textarea>
<textarea id="token" x-model="newNetwork.token" placeholder="Enter token" class="input"></textarea>
</div>
<button @click="addNetwork"><i class="fa-solid fa-plus"></i> Add Network</button>
<button @click="addNetwork" class="btn-primary"><i class="fa-solid fa-plus"></i> Add Network</button>
<template x-if="errorMessage">
<p class="error" x-text="errorMessage"></p>
</template>
@@ -248,7 +247,7 @@
<p class="text-lg font-bold mb-4 mt-1"><i class="fa-solid fa-book mr-2"></i> Description</p>
<p x-text="network.description"></p>
</div>
<h2 class="text-3xl font-bold mb-4 mt-4">Available Clusters in this network</h2>
<h2 class="h2">Available Clusters in this network</h2>
<template x-for="cluster in network.Clusters" :key="cluster.NetworkID + cluster.Type">
<div class="cluster">
<div class="cluster-title"></div>

View File

@@ -2,7 +2,7 @@
<html lang="en">
{{template "views/partials/head" .}}
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen">
{{template "views/partials/navbar" .}}
@@ -12,110 +12,104 @@
<div class="w-full max-w-3xl mx-auto">
{{ if eq (len .ModelsConfig) 0 }}
<!-- No Models - Wizard Guide -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-12">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#8B5CF6]/10 border border-[#8B5CF6]/20 mb-6">
<i class="text-[#8B5CF6] text-2xl fas fa-robot"></i>
</div>
<h2 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
No Models Installed
</span>
<div class="hero-section">
<div class="hero-content">
<h2 class="hero-title">
No Models Installed
</h2>
<p class="text-xl text-[#94A3B8] mb-8">
<p class="hero-subtitle">
Get started with LocalAI by installing your first model. Choose from our gallery, import your own, or use the API to download models.
</p>
<!-- Features Preview -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-10">
<div class="bg-[#101827] border border-[#38BDF8]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-images text-[#38BDF8] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Model Gallery</h3>
<p class="text-xs text-[#94A3B8]">Browse and install pre-configured models</p>
</div>
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-lg p-4">
<div class="w-10 h-10 bg-purple-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-upload text-[#8B5CF6] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Import Models</h3>
<p class="text-xs text-[#94A3B8]">Upload your own model files</p>
</div>
<div class="bg-[#101827] border border-green-500/20 rounded-lg p-4">
<div class="w-10 h-10 bg-green-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-code text-green-400 text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">API Download</h3>
<p class="text-xs text-[#94A3B8]">Use the API to download models programmatically</p>
</div>
</div>
</div>
<!-- Features Preview -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="card card-animate">
<div class="w-10 h-10 bg-[var(--color-primary-light)] rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-images text-[var(--color-primary)] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Model Gallery</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Browse and install pre-configured models</p>
</div>
<div class="card card-animate">
<div class="w-10 h-10 bg-[var(--color-accent-light)] rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-upload text-[var(--color-accent)] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Import Models</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Upload your own model files</p>
</div>
<div class="card card-animate">
<div class="w-10 h-10 bg-[var(--color-success-light)] rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-code text-[var(--color-success)] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">API Download</h3>
<p class="text-xs text-[var(--color-text-secondary)]">Use the API to download models programmatically</p>
</div>
</div>
<!-- Setup Instructions -->
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-xl p-6 mb-8 text-left">
<h3 class="text-lg font-bold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-rocket text-[#8B5CF6] mr-2"></i>
How to Get Started
</h3>
<div class="space-y-4">
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">1</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Browse the Model Gallery</p>
<p class="text-[#94A3B8] text-sm">Explore our curated collection of pre-configured models. Find models for chat, image generation, audio processing, and more.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">2</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Install a Model</p>
<p class="text-[#94A3B8] text-sm">Click on a model from the gallery to install it, or use the import feature to upload your own model files.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[#8B5CF6]/20 flex items-center justify-center mr-3 mt-0.5">
<span class="text-[#8B5CF6] font-bold text-sm">3</span>
</div>
<div class="flex-1">
<p class="text-[#E5E7EB] font-medium mb-2">Start Chatting</p>
<p class="text-[#94A3B8] text-sm">Once installed, return to this page to start chatting with your model or use the API to interact programmatically.</p>
</div>
</div>
<!-- Setup Instructions -->
<div class="card mb-6 text-left">
<h3 class="text-lg font-bold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-rocket text-[var(--color-accent)] mr-2"></i>
How to Get Started
</h3>
<div class="space-y-4">
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent-light)] flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">1</span>
</div>
<div class="flex-1">
<p class="text-[var(--color-text-primary)] font-medium mb-2">Browse the Model Gallery</p>
<p class="text-[var(--color-text-secondary)] text-sm">Explore our curated collection of pre-configured models. Find models for chat, image generation, audio processing, and more.</p>
</div>
</div>
<div class="flex flex-wrap justify-center gap-4">
<a href="/browse/"
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-images mr-2"></i>
Browse Model Gallery
</a>
<a href="/import-model"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-upload mr-2"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank"
class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#8B5CF6]/20 text-[#E5E7EB] py-3 px-6 rounded-lg font-semibold transition-colors">
<i class="fas fa-graduation-cap mr-2"></i>
Getting Started
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
</a>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent-light)] flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">2</span>
</div>
<div class="flex-1">
<p class="text-[var(--color-text-primary)] font-medium mb-2">Install a Model</p>
<p class="text-[var(--color-text-secondary)] text-sm">Click on a model from the gallery to install it, or use the import feature to upload your own model files.</p>
</div>
</div>
<div class="flex items-start">
<div class="flex-shrink-0 w-8 h-8 rounded-full bg-[var(--color-accent-light)] flex items-center justify-center mr-3 mt-0.5">
<span class="text-[var(--color-accent)] font-bold text-sm">3</span>
</div>
<div class="flex-1">
<p class="text-[var(--color-text-primary)] font-medium mb-2">Start Chatting</p>
<p class="text-[var(--color-text-secondary)] text-sm">Once installed, return to this page to start chatting with your model or use the API to interact programmatically.</p>
</div>
</div>
</div>
</div>
<div class="flex flex-wrap justify-center gap-4 mb-8">
<a href="/browse/" class="btn-primary">
<i class="fas fa-images mr-2"></i>
Browse Model Gallery
</a>
<a href="/import-model" class="btn-primary">
<i class="fas fa-upload mr-2"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="btn-secondary">
<i class="fas fa-graduation-cap mr-2"></i>
Getting Started
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
</a>
</div>
{{ else }}
<!-- Welcome Message -->
<div class="text-center mb-12">
<div class="mb-6 flex justify-center">
<img src="static/logo.png" alt="LocalAI Logo" class="h-24 md:h-32 drop-shadow-[0_0_15px_rgba(56,189,248,0.3)]">
<!-- Welcome Message / Hero Section -->
<div class="hero-section">
<div class="hero-content">
<div class="mb-4 flex justify-center">
<img src="static/logo.png" alt="LocalAI Logo" class="h-16 md:h-20">
</div>
<h1 class="hero-title">How can I help you today?</h1>
<p class="hero-subtitle">Ask me anything, and I'll do my best to assist you.</p>
</div>
<p class="text-lg text-[#94A3B8]">How can I help you today?</p>
</div>
<!-- Chat Input Form -->
@@ -309,21 +303,21 @@
}">
<!-- Model Selector with MCP Toggle -->
<div class="mb-4">
<label class="block text-sm font-medium text-[#94A3B8] mb-2">Select Model</label>
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">Select Model</label>
<div class="flex items-center gap-3">
<select
x-model="selectedModel"
@change="$nextTick(() => checkMCPAvailability())"
class="flex-1 bg-[#1E293B] text-[#E5E7EB] border border-[#38BDF8]/20 focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50 rounded-lg p-3 appearance-none"
class="input flex-1"
required
>
<option value="" disabled class="text-[#94A3B8]">Select a model to chat with...</option>
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model to chat with...</option>
{{ range .ModelsConfig }}
{{ $cfg := . }}
{{ $hasMCP := or (ne $cfg.MCP.Servers "") (ne $cfg.MCP.Stdio "") }}
{{ range .KnownUsecaseStrings }}
{{ if eq . "FLAG_CHAT" }}
<option value="{{$cfg.Name}}" data-has-mcp="{{if $hasMCP}}true{{else}}false{{end}}" class="bg-[#1E293B] text-[#E5E7EB]">{{$cfg.Name}}</option>
<option value="{{$cfg.Name}}" data-has-mcp="{{if $hasMCP}}true{{else}}false{{end}}" class="bg-[var(--color-bg-secondary)] text-[var(--color-text-primary)]">{{$cfg.Name}}</option>
{{ end }}
{{ end }}
{{ end }}
@@ -332,12 +326,12 @@
<!-- Compact MCP Toggle - Show only if MCP is available for selected model -->
<div
x-show="mcpAvailable"
class="flex items-center gap-2 px-3 py-2 text-xs rounded text-[#E5E7EB] bg-[#1E293B] border border-[#38BDF8]/20 whitespace-nowrap">
<i class="fa-solid fa-plug text-[#38BDF8] text-sm"></i>
<span class="text-[#94A3B8]">MCP</span>
class="flex items-center gap-2 px-3 py-2 text-xs rounded text-[var(--color-text-primary)] bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)] whitespace-nowrap">
<i class="fa-solid fa-plug text-[var(--color-primary)] text-sm"></i>
<span class="text-[var(--color-text-secondary)]">MCP</span>
<label class="relative inline-flex items-center cursor-pointer ml-1">
<input type="checkbox" id="index_mcp_toggle" class="sr-only peer" x-model="mcpMode">
<div class="w-9 h-5 bg-[#101827] peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-[#38BDF8]/30 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-[#1E293B] after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-[#38BDF8]"></div>
<div class="w-9 h-5 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-[var(--color-primary-border)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-[var(--color-bg-secondary)] after:border after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
</label>
</div>
</div>
@@ -345,10 +339,10 @@
<!-- MCP Mode Notification - Compact tooltip style -->
<div
x-show="mcpMode && mcpAvailable"
class="mt-2 p-2 bg-[#38BDF8]/10 border border-[#38BDF8]/30 rounded text-[#94A3B8] text-xs">
class="mt-2 p-2 bg-[var(--color-primary-light)] border border-[var(--color-primary-border)] rounded text-[var(--color-text-secondary)] text-xs">
<div class="flex items-start space-x-2">
<i class="fa-solid fa-info-circle text-[#38BDF8] mt-0.5 text-xs"></i>
<p class="text-[#94A3B8]">Non-streaming mode active. Responses may take longer to process.</p>
<i class="fa-solid fa-info-circle text-[var(--color-primary)] mt-0.5 text-xs"></i>
<p class="text-[var(--color-text-secondary)]">Non-streaming mode active. Responses may take longer to process.</p>
</div>
</div>
</div>
@@ -358,13 +352,13 @@
<!-- Attachment Tags - Show above input when files are attached -->
<div x-show="attachedFiles.length > 0" class="mb-3 flex flex-wrap gap-2 items-center">
<template x-for="(file, index) in attachedFiles" :key="index">
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm bg-[#38BDF8]/20 border border-[#38BDF8]/40 text-[#E5E7EB]">
<i :class="file.type === 'image' ? 'fa-solid fa-image' : file.type === 'audio' ? 'fa-solid fa-microphone' : 'fa-solid fa-file'" class="text-[#38BDF8]"></i>
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm bg-[var(--color-primary-light)] border border-[var(--color-primary-border)] text-[var(--color-text-primary)]">
<i :class="file.type === 'image' ? 'fa-solid fa-image' : file.type === 'audio' ? 'fa-solid fa-microphone' : 'fa-solid fa-file'" class="text-[var(--color-primary)]"></i>
<span x-text="file.name" class="max-w-[200px] truncate"></span>
<button
type="button"
@click="attachedFiles.splice(index, 1); removeAttachedFile(file.type, file.name)"
class="ml-1 text-[#94A3B8] hover:text-[#E5E7EB] transition-colors"
class="ml-1 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors"
title="Remove attachment"
>
<i class="fa-solid fa-times text-xs"></i>
@@ -373,11 +367,11 @@
</template>
</div>
<div class="relative w-full bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl focus-within:ring-2 focus-within:ring-[#38BDF8]/50 focus-within:border-[#38BDF8] transition-all duration-200">
<div class="relative w-full">
<textarea
x-model="inputValue"
:placeholder="currentPlaceholder"
class="p-3 pr-16 w-full bg-[#1E293B] text-[#E5E7EB] placeholder-[#94A3B8] focus:outline-none resize-none border-0 rounded-xl transition-colors duration-200"
class="input p-3 pr-16 w-full resize-none border-0"
required
@keydown.shift="shiftPressed = true"
@keyup.shift="shiftPressed = false"
@@ -392,19 +386,19 @@
<button
type="button"
@click="document.getElementById('index_input_image').click()"
class="fa-solid fa-image text-[#94A3B8] absolute right-12 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-image text-[var(--color-text-secondary)] absolute right-12 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Attach images"
></button>
<button
type="button"
@click="document.getElementById('index_input_audio').click()"
class="fa-solid fa-microphone text-[#94A3B8] absolute right-20 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-microphone text-[var(--color-text-secondary)] absolute right-20 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Attach an audio file"
></button>
<button
type="button"
@click="document.getElementById('index_input_file').click()"
class="fa-solid fa-file text-[#94A3B8] absolute right-28 top-3 text-base p-1.5 hover:text-[#38BDF8] transition-colors duration-200"
class="fa-solid fa-file text-[var(--color-text-secondary)] absolute right-28 top-3 text-base p-1.5 hover:text-[var(--color-primary)] transition-colors duration-200"
title="Upload text, markdown or PDF file"
></button>
@@ -413,7 +407,7 @@
type="submit"
:disabled="!selectedModel || (!inputValue.trim() && !currentPlaceholder.trim())"
:class="!selectedModel || (!inputValue.trim() && !currentPlaceholder.trim()) ? 'opacity-50 cursor-not-allowed' : ''"
class="text-lg p-2 text-[#94A3B8] hover:text-[#38BDF8] transition-colors duration-200 absolute right-3 top-3"
class="text-lg p-2 text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors duration-200 absolute right-3 top-3"
title="Send message (Enter)"
>
<i class="fa-solid fa-paper-plane"></i>
@@ -450,22 +444,19 @@
<!-- Quick Links -->
<div class="flex flex-wrap justify-center gap-3 mb-8">
<a href="/manage"
class="inline-flex items-center text-sm text-[#94A3B8] hover:text-[#E5E7EB] px-4 py-2 rounded-lg hover:bg-[#1E293B] transition-colors">
<a href="/manage" class="btn-tertiary">
<i class="fas fa-cog mr-2"></i>
Installed Models and Backends
</a>
<a href="/import-model" class="inline-flex items-center text-sm text-[#94A3B8] hover:text-[#E5E7EB] px-4 py-2 rounded-lg hover:bg-[#1E293B] transition-colors">
<a href="/import-model" class="btn-tertiary">
<i class="fas fa-upload mr-2"></i>
Import Model
</a>
<a href="/browse/"
class="inline-flex items-center text-sm text-[#94A3B8] hover:text-[#E5E7EB] px-4 py-2 rounded-lg hover:bg-[#1E293B] transition-colors">
<a href="/browse/" class="btn-tertiary">
<i class="fas fa-images mr-2"></i>
Browse Gallery
</a>
<a href="https://localai.io" target="_blank"
class="inline-flex items-center text-sm text-[#94A3B8] hover:text-[#E5E7EB] px-4 py-2 rounded-lg hover:bg-[#1E293B] transition-colors">
<a href="https://localai.io" target="_blank" class="btn-tertiary">
<i class="fas fa-book mr-2"></i>
Documentation
</a>
@@ -473,7 +464,7 @@
<!-- Model Status Summary - Subtle -->
{{ $loadedModels := .LoadedModels }}
<div class="mb-8 flex items-center justify-center gap-2 text-xs text-[#94A3B8]"
<div class="mb-8 flex items-center justify-center gap-2 text-xs text-[var(--color-text-secondary)]"
x-data="{ stoppingAll: false, stopAllModels() { window.stopAllModels(this); }, stopModel(name) { window.stopModel(name); }, getLoadedCount() { return document.querySelectorAll('[data-loaded-model]').length; } }"
x-show="getLoadedCount() > 0"
style="display: none;">
@@ -481,10 +472,10 @@
<i class="fas fa-circle text-green-500 text-[10px]"></i>
<span x-text="`${getLoadedCount()} model(s) loaded`"></span>
</span>
<span class="text-[#38BDF8]/40"></span>
<span class="text-[var(--color-primary)] opacity-40"></span>
{{ range .ModelsConfig }}
{{ if index $loadedModels .Name }}
<span class="inline-flex items-center gap-1 text-[#94A3B8] hover:text-[#E5E7EB] transition-colors" data-loaded-model>
<span class="inline-flex items-center gap-1 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors" data-loaded-model>
<span class="truncate max-w-[100px]">{{.Name}}</span>
<button
@click="stopModel('{{.Name}}')"
@@ -496,7 +487,7 @@
</span>
{{ end }}
{{ end }}
<span class="text-[#38BDF8]/40"></span>
<span class="text-[var(--color-primary)] opacity-40"></span>
<button
@click="stopAllModels()"
:disabled="stoppingAll"

View File

@@ -18,10 +18,8 @@
<div class="p-8">
<div class="text-center mb-6">
<h2 class="text-2xl font-bold text-[#E5E7EB]">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
Authorization Required
</span>
<h2 class="h2">
Authorization Required
</h2>
<p class="text-[#94A3B8] mt-2">Please enter your access token to continue</p>
</div>

View File

@@ -2,7 +2,7 @@
<html lang="en">
{{template "views/partials/head" .}}
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen" x-data="indexDashboard()">
{{template "views/partials/navbar" .}}
@@ -17,7 +17,7 @@
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
:class="notification.type === 'error' ? 'bg-red-500' : 'bg-green-500'"
:class="notification.type === 'error' ? 'bg-red-500' : 'bg-[var(--color-success)]'"
class="rounded-lg p-4 text-white flex items-start space-x-3">
<div class="flex-shrink-0">
<i :class="notification.type === 'error' ? 'fas fa-exclamation-circle' : 'fas fa-check-circle'" class="text-xl"></i>
@@ -32,48 +32,45 @@
</template>
</div>
<div class="container mx-auto px-4 py-6 flex-grow">
<!-- Header -->
<div class="mb-6">
<h1 class="text-2xl font-semibold text-[#E5E7EB] mb-1">
Model & Backend Management
</h1>
<p class="text-sm text-[#94A3B8]">Manage your installed models and backends</p>
</div>
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Header -->
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
Model & Backend Management
</h1>
<p class="hero-subtitle">Manage your installed models and backends</p>
<!-- Quick Actions -->
<div class="flex flex-wrap justify-center gap-3">
<a href="browse" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-images mr-1.5 text-[10px]"></i>
<span>Model Gallery</span>
</a>
<a href="/import-model" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-plus mr-1.5 text-[10px]"></i>
<span>Import Model</span>
</a>
<!-- Quick Actions -->
<div class="flex flex-wrap gap-2 mb-6">
<a href="browse"
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white py-1.5 px-3 rounded text-xs font-medium transition-colors">
<i class="fas fa-images mr-1.5 text-[10px]"></i>
<span>Model Gallery</span>
</a>
<a href="/import-model"
class="inline-flex items-center bg-green-600 hover:bg-green-700 text-white py-1.5 px-3 rounded text-xs font-medium transition-colors">
<i class="fas fa-plus mr-1.5 text-[10px]"></i>
<span>Import Model</span>
</a>
<button id="reload-models-btn" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-sync-alt mr-1.5 text-[10px]"></i>
<span>Update Models</span>
</button>
<button id="reload-models-btn"
class="inline-flex items-center bg-orange-600 hover:bg-orange-700 text-white py-1.5 px-3 rounded text-xs font-medium transition-colors">
<i class="fas fa-sync-alt mr-1.5 text-[10px]"></i>
<span>Update Models</span>
</button>
<a href="/browse/backends" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-cogs mr-1.5 text-[10px]"></i>
<span>Backend Gallery</span>
</a>
<a href="/browse/backends"
class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#8B5CF6]/20 text-[#E5E7EB] py-1.5 px-3 rounded text-xs font-medium transition-colors">
<i class="fas fa-cogs mr-1.5 text-[10px]"></i>
<span>Backend Gallery</span>
</a>
{{ if not .DisableRuntimeSettings }}
<a href="/settings"
class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-1.5 px-3 rounded text-xs font-medium transition-colors">
<i class="fas fa-cog mr-1.5 text-[10px]"></i>
<span>Settings</span>
</a>
{{ end }}
{{ if not .DisableRuntimeSettings }}
<a href="/settings" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-cog mr-1.5 text-[10px]"></i>
<span>Settings</span>
</a>
{{ end }}
</div>
</div>
</div>
<!-- Models Section -->
@@ -82,41 +79,41 @@
{{ if eq (len .ModelsConfig) 0 }}
<!-- No Models State -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-lg p-8">
<div class="card p-8">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-yellow-500/10 border border-yellow-500/20 mb-4">
<i class="text-yellow-400 text-xl fas fa-robot"></i>
</div>
<h2 class="text-2xl font-bold text-[#E5E7EB] mb-2">No models installed yet</h2>
<p class="text-sm text-[#94A3B8] mb-6">Get started by installing a model from the gallery or importing it</p>
<h2 class="h2 mb-2">No models installed yet</h2>
<p class="text-sm text-[var(--color-text-secondary)] mb-6">Get started by installing a model from the gallery or importing it</p>
<div class="flex flex-wrap justify-center gap-2 mb-6">
<a href="browse" class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] py-1.5 px-3 rounded text-xs font-medium transition-colors">
<a href="browse" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-images mr-1.5 text-[10px]"></i>
Browse Model Gallery
</a>
<a href="/import-model" class="inline-flex items-center bg-green-600 hover:bg-green-700 text-white py-1.5 px-3 rounded text-xs font-medium transition-colors">
<a href="/import-model" class="btn-primary text-sm py-1.5 px-3">
<i class="fas fa-upload mr-1.5 text-[10px]"></i>
Import Model
</a>
<a href="https://localai.io/basics/getting_started/" target="_blank" class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#38BDF8]/20 text-[#E5E7EB] py-1.5 px-3 rounded text-xs font-medium transition-colors">
<a href="https://localai.io/basics/getting_started/" target="_blank" class="btn-secondary text-sm py-1.5 px-3">
<i class="fas fa-book mr-1.5 text-[10px]"></i>
Documentation
</a>
</div>
{{ if ne (len .Models) 0 }}
<div class="mt-8 pt-6 border-t border-[#38BDF8]/20">
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-2 flex items-center">
<i class="fas fa-file-alt mr-2 text-[#38BDF8] text-sm"></i>
<div class="mt-8 pt-6 border-t border-[var(--color-primary-border)]/20">
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-2 flex items-center">
<i class="fas fa-file-alt mr-2 text-[var(--color-primary)] text-sm"></i>
Detected Model Files
</h3>
<p class="text-xs text-[#94A3B8] mb-4">These models were found but don't have configuration files yet</p>
<p class="text-xs text-[var(--color-text-secondary)] mb-4">These models were found but don't have configuration files yet</p>
<div class="flex flex-wrap gap-2 justify-center">
{{ range .Models }}
<div class="bg-[#101827] border border-[#38BDF8]/20 rounded px-2 py-1 flex items-center gap-2">
<i class="fas fa-brain text-xs text-[#38BDF8]"></i>
<span class="text-xs text-[#E5E7EB] font-medium">{{.}}</span>
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded px-2 py-1 flex items-center gap-2">
<i class="fas fa-brain text-xs text-[var(--color-primary)]"></i>
<span class="text-xs text-[var(--color-text-primary)] font-medium">{{.}}</span>
</div>
{{end}}
</div>
@@ -129,24 +126,24 @@
{{ $modelsN := len .ModelsConfig}}
{{ $modelsN = add $modelsN (len .Models)}}
<div class="mb-6">
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-1 flex items-center">
<i class="fas fa-brain mr-2 text-[#38BDF8] text-sm"></i>
<h2 class="h3 mb-1 flex items-center">
<i class="fas fa-brain mr-2 text-[var(--color-primary)] text-sm"></i>
Installed Models
</h2>
<p class="text-sm text-[#94A3B8] mb-4">
<span class="text-[#38BDF8] font-medium">{{$modelsN}}</span> model{{if gt $modelsN 1}}s{{end}} ready to use
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
<span class="text-[var(--color-primary)] font-medium">{{$modelsN}}</span> model{{if gt $modelsN 1}}s{{end}} ready to use
</p>
</div>
<div class="overflow-x-auto mb-8">
<table class="w-full border-collapse">
<thead>
<tr class="border-b border-[#1E293B]">
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Name</th>
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Status</th>
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Backend</th>
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Use Cases</th>
<th class="text-right p-2 text-xs font-semibold text-[#94A3B8]">Actions</th>
<tr class="border-b border-[var(--color-bg-secondary)]">
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Name</th>
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Status</th>
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Backend</th>
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Use Cases</th>
<th class="text-right p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Actions</th>
</tr>
</thead>
<tbody>
@@ -156,7 +153,7 @@
{{ range .ModelsConfig }}
{{ $backendCfg := . }}
{{ $cfg:= index $galleryConfig .Name}}
<tr class="hover:bg-[#1E293B]/50 border-b border-[#1E293B] transition-colors">
<tr class="hover:bg-[var(--color-bg-secondary)]/50 border-b border-[var(--color-bg-secondary)] transition-colors">
<!-- Name Column -->
<td class="p-2">
<div class="flex items-center gap-2">
@@ -164,15 +161,15 @@
{{ if and $cfg $cfg.Icon }}
<img src="{{$cfg.Icon}}" class="w-4 h-4 object-contain" alt="{{.Name}} icon">
{{ else }}
<i class="fas fa-brain text-xs text-[#38BDF8]"></i>
<i class="fas fa-brain text-xs text-[var(--color-primary)]"></i>
{{ end }}
{{ if index $loadedModels .Name }}
<div class="absolute -top-0.5 -right-0.5 w-2 h-2 bg-green-500 rounded-full border border-[#1E293B]"></div>
<div class="absolute -top-0.5 -right-0.5 w-2 h-2 bg-[var(--color-success)] rounded-full border border-[var(--color-bg-secondary)]"></div>
{{ end }}
</div>
<span class="text-xs text-[#E5E7EB] font-medium truncate">{{.Name}}</span>
<span class="text-xs text-[var(--color-text-primary)] font-medium truncate">{{.Name}}</span>
<a href="/models/edit/{{.Name}}"
class="text-[#38BDF8]/60 hover:text-[#38BDF8] hover:bg-[#38BDF8]/10 rounded p-0.5 transition-colors ml-1 flex-shrink-0"
class="text-[var(--color-primary)]/60 hover:text-[var(--color-primary)] hover:bg-[var(--color-primary)]/10 rounded p-0.5 transition-colors ml-1 flex-shrink-0"
title="Edit {{.Name}}">
<i class="fas fa-edit text-[10px]"></i>
</a>
@@ -183,12 +180,12 @@
<td class="p-2">
<div class="flex flex-wrap gap-1">
{{ if index $loadedModels .Name }}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-500/10 text-green-300">
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-success)]/10 text-green-300">
<i class="fas fa-circle text-[8px] mr-1"></i>Running
</span>
{{ end }}
{{ if and $backendCfg (or (ne $backendCfg.MCP.Servers "") (ne $backendCfg.MCP.Stdio "")) }}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#8B5CF6]/10 text-[#8B5CF6]">
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-light)] text-[var(--color-accent)]">
<i class="fas fa-plug text-[8px] mr-1"></i>MCP
</span>
{{ end }}
@@ -198,7 +195,7 @@
<!-- Backend Column -->
<td class="p-2">
{{ if .Backend }}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#38BDF8]/10 text-[#38BDF8]">
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-primary)]/10 text-[var(--color-primary)]">
<i class="fas fa-cog text-[8px] mr-1"></i>{{.Backend}}
</span>
{{ else }}
@@ -213,17 +210,17 @@
<div class="flex flex-wrap gap-1">
{{ range .KnownUsecaseStrings }}
{{ if eq . "FLAG_CHAT" }}
<a href="chat/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#38BDF8]/10 text-[#38BDF8] hover:bg-[#38BDF8]/20 transition-colors" title="Chat">
<a href="chat/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-primary)]/10 text-[var(--color-primary)] hover:bg-[var(--color-primary)]/20 transition-colors" title="Chat">
<i class="fas fa-comment-alt text-[8px] mr-1"></i>Chat
</a>
{{ end }}
{{ if eq . "FLAG_IMAGE" }}
<a href="text2image/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-500/10 text-green-300 hover:bg-green-500/20 transition-colors" title="Image">
<a href="text2image/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-success)]/10 text-green-300 hover:bg-[var(--color-success)]/20 transition-colors" title="Image">
<i class="fas fa-image text-[8px] mr-1"></i>Image
</a>
{{ end }}
{{ if eq . "FLAG_TTS" }}
<a href="tts/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#8B5CF6]/10 text-[#8B5CF6] hover:bg-[#8B5CF6]/20 transition-colors" title="TTS">
<a href="tts/{{$backendCfg.Name}}" class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-light)] text-[var(--color-accent)] hover:bg-[var(--color-accent-light)] transition-colors" title="TTS">
<i class="fas fa-microphone text-[8px] mr-1"></i>TTS
</a>
{{ end }}
@@ -253,11 +250,11 @@
<!-- Models without config -->
{{ range .Models }}
<tr class="hover:bg-[#1E293B]/50 border-b border-[#1E293B] transition-colors">
<tr class="hover:bg-[var(--color-bg-secondary)]/50 border-b border-[var(--color-bg-secondary)] transition-colors">
<td class="p-2">
<div class="flex items-center gap-2">
<i class="fas fa-brain text-xs text-[#94A3B8]"></i>
<span class="text-xs text-[#E5E7EB] font-medium truncate">{{.}}</span>
<i class="fas fa-brain text-xs text-[var(--color-text-secondary)]"></i>
<span class="text-xs text-[var(--color-text-primary)] font-medium truncate">{{.}}</span>
</div>
</td>
<td class="p-2">
@@ -271,10 +268,10 @@
</span>
</td>
<td class="p-2">
<span class="text-xs text-[#94A3B8]"></span>
<span class="text-xs text-[var(--color-text-secondary)]"></span>
</td>
<td class="p-2">
<span class="text-xs text-[#94A3B8]"></span>
<span class="text-xs text-[var(--color-text-secondary)]"></span>
</td>
</tr>
{{end}}
@@ -288,42 +285,42 @@
<div class="mt-8">
<div class="mb-6">
<div class="flex items-center justify-between mb-1">
<h2 class="text-2xl font-semibold text-[#E5E7EB] flex items-center">
<i class="fas fa-cogs mr-2 text-[#8B5CF6] text-sm"></i>
<h2 class="h3 flex items-center">
<i class="fas fa-cogs mr-2 text-[var(--color-accent)] text-sm"></i>
Installed Backends
</h2>
{{ if gt (len .InstalledBackends) 0 }}
<button
@click="reinstallAllBackends()"
:disabled="reinstallingAll"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/80 disabled:opacity-50 disabled:cursor-not-allowed text-white py-1.5 px-3 rounded text-xs font-medium transition-colors"
class="btn-primary text-sm py-1.5 px-3"
title="Reinstall all backends">
<i class="fas fa-arrow-rotate-right mr-1.5 text-[10px]" :class="reinstallingAll ? 'fa-spin' : ''"></i>
<span x-text="reinstallingAll ? 'Reinstalling...' : 'Reinstall All'"></span>
</button>
{{ end }}
</div>
<p class="text-sm text-[#94A3B8] mb-4">
<span class="text-[#8B5CF6] font-medium">{{len .InstalledBackends}}</span> backend{{if gt (len .InstalledBackends) 1}}s{{end}} ready to use
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
<span class="text-[var(--color-accent)] font-medium">{{len .InstalledBackends}}</span> backend{{if gt (len .InstalledBackends) 1}}s{{end}} ready to use
</p>
</div>
{{ if eq (len .InstalledBackends) 0 }}
<!-- No backends state -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-lg p-8">
<div class="card p-8">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-[#8B5CF6]/10 border border-[#8B5CF6]/20 mb-4">
<i class="text-[#8B5CF6] text-xl fas fa-cogs"></i>
<div class="inline-flex items-center justify-center w-12 h-12 rounded-full bg-[var(--color-accent-light)] border border-[var(--color-accent-border)] mb-4">
<i class="text-[var(--color-accent)] text-xl fas fa-cogs"></i>
</div>
<h2 class="text-2xl font-bold text-[#E5E7EB] mb-2">No backends installed yet</h2>
<p class="text-sm text-[#94A3B8] mb-6">Backends power your AI models. Install them from the backend gallery to get started</p>
<h2 class="h2 mb-2">No backends installed yet</h2>
<p class="text-sm text-[var(--color-text-secondary)] mb-6">Backends power your AI models. Install them from the backend gallery to get started</p>
<div class="flex flex-wrap justify-center gap-3">
<a href="/browse/backends" class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white py-2 px-4 rounded-lg text-sm font-medium transition-colors">
<a href="/browse/backends" class="btn-primary">
<i class="fas fa-cogs mr-2 text-xs"></i>
Browse Backend Gallery
</a>
<a href="https://localai.io/backends/" target="_blank" class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#8B5CF6]/20 text-[#E5E7EB] py-2 px-4 rounded-lg text-sm font-medium transition-colors">
<a href="https://localai.io/backends/" target="_blank" class="btn-secondary">
<i class="fas fa-book mr-2 text-xs"></i>
Documentation
</a>
@@ -335,21 +332,21 @@
<div class="overflow-x-auto mb-8">
<table class="w-full border-collapse">
<thead>
<tr class="border-b border-[#1E293B]">
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Name</th>
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Type</th>
<th class="text-left p-2 text-xs font-semibold text-[#94A3B8]">Metadata</th>
<th class="text-right p-2 text-xs font-semibold text-[#94A3B8]">Actions</th>
<tr class="border-b border-[var(--color-bg-secondary)]">
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Name</th>
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Type</th>
<th class="text-left p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Metadata</th>
<th class="text-right p-2 text-xs font-semibold text-[var(--color-text-secondary)]">Actions</th>
</tr>
</thead>
<tbody>
{{ range .InstalledBackends }}
<tr class="hover:bg-[#1E293B]/50 border-b border-[#1E293B] transition-colors" data-backend-name="{{.Name}}" data-is-system="{{.IsSystem}}">
<tr class="hover:bg-[var(--color-bg-secondary)]/50 border-b border-[var(--color-bg-secondary)] transition-colors" data-backend-name="{{.Name}}" data-is-system="{{.IsSystem}}">
<!-- Name Column -->
<td class="p-2">
<div class="flex items-center gap-2">
<i class="fas fa-cog text-xs text-[#8B5CF6]"></i>
<span class="text-xs text-[#E5E7EB] font-medium truncate">{{.Name}}</span>
<i class="fas fa-cog text-xs text-[var(--color-accent)]"></i>
<span class="text-xs text-[var(--color-text-primary)] font-medium truncate">{{.Name}}</span>
</div>
</td>
@@ -361,12 +358,12 @@
<i class="fas fa-shield-alt text-[8px] mr-1"></i>System
</span>
{{ else }}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-500/10 text-green-300">
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-success)]/10 text-green-300">
<i class="fas fa-download text-[8px] mr-1"></i>User
</span>
{{ end }}
{{ if .IsMeta }}
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#8B5CF6]/10 text-[#8B5CF6]">
<span class="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-[var(--color-accent-light)] text-[var(--color-accent)]">
<i class="fas fa-layer-group text-[8px] mr-1"></i>Meta
</span>
{{ end }}
@@ -377,17 +374,17 @@
<td class="p-2">
<div class="flex flex-col gap-1">
{{ if and .Metadata .Metadata.Alias }}
<span class="text-xs text-[#94A3B8]">
<i class="fas fa-tag text-[8px] mr-1"></i>Alias: <span class="text-[#E5E7EB]">{{.Metadata.Alias}}</span>
<span class="text-xs text-[var(--color-text-secondary)]">
<i class="fas fa-tag text-[8px] mr-1"></i>Alias: <span class="text-[var(--color-text-primary)]">{{.Metadata.Alias}}</span>
</span>
{{ end }}
{{ if and .Metadata .Metadata.MetaBackendFor }}
<span class="text-xs text-[#94A3B8]">
<i class="fas fa-link text-[8px] mr-1"></i>For: <span class="text-[#8B5CF6]">{{.Metadata.MetaBackendFor}}</span>
<span class="text-xs text-[var(--color-text-secondary)]">
<i class="fas fa-link text-[8px] mr-1"></i>For: <span class="text-[var(--color-accent)]">{{.Metadata.MetaBackendFor}}</span>
</span>
{{ end }}
{{ if and .Metadata .Metadata.InstalledAt }}
<span class="text-xs text-[#94A3B8]">
<span class="text-xs text-[var(--color-text-secondary)]">
<i class="fas fa-calendar text-[8px] mr-1"></i>{{.Metadata.InstalledAt}}
</span>
{{ end }}
@@ -401,7 +398,7 @@
<button
@click="reinstallBackend('{{.Name}}')"
:disabled="reinstallingBackends['{{.Name}}']"
class="text-[#38BDF8]/60 hover:text-[#38BDF8] hover:bg-[#38BDF8]/10 disabled:opacity-50 disabled:cursor-not-allowed rounded p-1 transition-colors"
class="text-[var(--color-primary)]/60 hover:text-[var(--color-primary)] hover:bg-[var(--color-primary)]/10 disabled:opacity-50 disabled:cursor-not-allowed rounded p-1 transition-colors"
title="Reinstall {{.Name}}">
<i class="fas fa-arrow-rotate-right text-xs" :class="reinstallingBackends['{{.Name}}'] ? 'fa-spin' : ''"></i>
</button>
@@ -412,7 +409,7 @@
<i class="fas fa-trash-alt text-xs"></i>
</button>
{{ else }}
<span class="text-xs text-[#94A3B8]"></span>
<span class="text-xs text-[var(--color-text-secondary)]"></span>
{{ end }}
</div>
</td>

View File

@@ -10,22 +10,19 @@
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Header -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8 mb-8">
<div class="max-w-5xl mx-auto">
<div class="hero-section">
<div class="hero-content">
<div class="flex flex-col md:flex-row md:items-center md:justify-between">
<div class="mb-4 md:mb-0">
<h1 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-2">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-violet-400 via-purple-400 to-fuchsia-400">
{{if .ModelName}}Edit Model: {{.ModelName}}{{else}}Import New Model{{end}}
</span>
<div>
<h1 class="hero-title">
{{if .ModelName}}Edit Model: {{.ModelName}}{{else}}Import New Model{{end}}
</h1>
<p class="text-lg text-[#94A3B8] font-light" x-text="isAdvancedMode ? 'Configure your model settings using YAML' : 'Import a model from URI with preferences'"></p>
<p class="hero-subtitle" x-text="isAdvancedMode ? 'Configure your model settings using YAML' : 'Import a model from URI with preferences'"></p>
</div>
<div class="flex gap-3">
<!-- Mode Toggle (only show when not in edit mode) -->
<template x-if="!isEditMode">
<button @click="toggleMode()"
class="inline-flex items-center bg-[#1E293B] hover:bg-[#1E293B]/80 border border-[#8B5CF6]/20 text-[#E5E7EB] py-3 px-6 rounded-lg font-semibold transition-colors">
<button @click="toggleMode()" class="btn-secondary">
<i class="fas" :class="isAdvancedMode ? 'fa-magic mr-2' : 'fa-code mr-2'"></i>
<span x-text="isAdvancedMode ? 'Simple Mode' : 'Advanced Mode'"></span>
</button>
@@ -33,11 +30,11 @@
<!-- Advanced Mode Buttons -->
<template x-if="isAdvancedMode">
<div class="flex gap-3">
<button id="validateBtn" class="inline-flex items-center bg-blue-600 hover:bg-blue-700 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<button id="validateBtn" class="btn-primary">
<i class="fas fa-check mr-2"></i>
<span>Validate</span>
</button>
<button id="saveBtn" class="inline-flex items-center bg-green-600 hover:bg-green-700 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
<button id="saveBtn" class="btn-primary">
<i class="fas fa-save mr-2"></i>
<span>{{if .ModelName}}Update{{else}}Create{{end}}</span>
</button>
@@ -47,8 +44,7 @@
<template x-if="!isAdvancedMode && !isEditMode">
<button @click="submitImport()"
:disabled="isSubmitting || !importUri.trim()"
:class="(isSubmitting || !importUri.trim()) ? 'opacity-50 cursor-not-allowed' : ''"
class="inline-flex items-center bg-green-600 hover:bg-green-700 text-white py-3 px-6 rounded-lg font-semibold transition-colors">
class="btn-primary">
<i class="fas" :class="isSubmitting ? 'fa-spinner fa-spin mr-2' : 'fa-upload mr-2'"></i>
<span x-text="isSubmitting ? 'Importing...' : 'Import Model'"></span>
</button>
@@ -66,7 +62,7 @@
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8">
class="card p-8">
<div class="space-y-6">
<h2 class="text-2xl font-semibold text-[#E5E7EB] flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-lg bg-green-500/10 flex items-center justify-center">
@@ -102,7 +98,7 @@
x-model="importUri"
type="text"
placeholder="huggingface://TheBloke/Llama-2-7B-Chat-GGUF or https://example.com/model.gguf"
class="w-full px-4 py-3 bg-[#101827] border border-[#1E293B] rounded-lg text-[#E5E7EB] focus:border-green-500 focus:ring-2 focus:ring-green-500/50 focus:outline-none transition-colors"
class="input w-full"
:disabled="isSubmitting">
<p class="mt-2 text-xs text-[#94A3B8]">
Enter the URI or path to the model file you want to import
@@ -331,7 +327,7 @@
x-model="commonPreferences.description"
rows="3"
placeholder="Leave empty to use default description"
class="w-full px-4 py-2 bg-gray-900/90 border border-gray-700/70 rounded-lg text-gray-200 focus:border-green-500 focus:ring-2 focus:ring-green-500/50 focus:outline-none transition-all resize-none"
class="input w-full resize-none"
:disabled="isSubmitting"></textarea>
<p class="mt-1 text-xs text-gray-400">
Custom description for the model. If empty, a default description will be generated.

View File

@@ -2,7 +2,7 @@
<html lang="en">
{{template "views/partials/head" .}}
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen" x-data="modelsGallery()">
{{template "views/partials/navbar" .}}
@@ -17,7 +17,7 @@
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
:class="notification.type === 'error' ? 'bg-red-500' : 'bg-green-500'"
:class="notification.type === 'error' ? 'bg-[var(--color-error)]' : 'bg-[var(--color-success)]'"
class="rounded-lg p-4 text-white flex items-start space-x-3">
<div class="flex-shrink-0">
<i :class="notification.type === 'error' ? 'fas fa-exclamation-circle' : 'fas fa-check-circle'" class="text-xl"></i>
@@ -35,34 +35,31 @@
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Header -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-12">
<div class="max-w-5xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] via-[#8B5CF6] to-[#38BDF8]">
Model Gallery
</span>
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
Model Gallery
</h1>
<p class="text-lg md:text-xl text-[#94A3B8] mb-6 font-light">
<p class="hero-subtitle">
Discover and install AI models from our curated collection
</p>
<div class="flex flex-wrap justify-center items-center gap-6 text-sm md:text-base">
<div class="flex items-center bg-[#101827] rounded-lg px-4 py-2">
<div class="w-2 h-2 bg-indigo-400 rounded-full mr-2"></div>
<div class="flex items-center bg-[var(--color-bg-primary)] rounded-lg px-4 py-2">
<div class="w-2 h-2 bg-[var(--color-primary)] rounded-full mr-2"></div>
<span class="font-semibold text-indigo-300" x-text="availableModels"></span>
<span class="text-[#94A3B8] ml-1">models available</span>
<span class="text-[var(--color-text-secondary)] ml-1">models available</span>
</div>
<a href="/manage" class="flex items-center bg-[#101827] hover:bg-[#1E293B] rounded-lg px-4 py-2 transition-colors border border-[#38BDF8]/30 hover:border-[#38BDF8]/50">
<div class="w-2 h-2 bg-emerald-400 rounded-full mr-2"></div>
<span class="font-semibold text-emerald-300" x-text="installedModels"></span>
<span class="text-[#94A3B8] ml-1">installed</span>
<a href="/manage" class="flex items-center bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)] rounded-lg px-4 py-2 transition-colors border border-[var(--color-primary-border)]/30 hover:border-[var(--color-primary-border)]/50">
<div class="w-2 h-2 bg-[var(--color-success)] rounded-full mr-2"></div>
<span class="font-semibold text-[var(--color-success)]" x-text="installedModels"></span>
<span class="text-[var(--color-text-secondary)] ml-1">installed</span>
</a>
<div class="flex items-center bg-[#101827] rounded-lg px-4 py-2">
<div class="w-2 h-2 bg-purple-400 rounded-full mr-2"></div>
<div class="flex items-center bg-[var(--color-bg-primary)] rounded-lg px-4 py-2">
<div class="w-2 h-2 bg-[var(--color-accent)] rounded-full mr-2"></div>
<span class="font-semibold text-purple-300" x-text="repositories.length"></span>
<span class="text-[#94A3B8] ml-1">repositories</span>
<span class="text-[var(--color-text-secondary)] ml-1">repositories</span>
</div>
<a href="https://localai.io/models/" target="_blank"
class="inline-flex items-center bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors">
<a href="https://localai.io/models/" target="_blank" class="btn-primary">
<i class="fas fa-info-circle mr-2"></i>
<span>Documentation</span>
<i class="fas fa-external-link-alt ml-2 text-xs"></i>
@@ -74,26 +71,26 @@
{{template "views/partials/inprogress" .}}
<!-- Search and Filter Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
<div class="card p-8 mb-8">
<div>
<!-- Search Input -->
<div class="mb-8">
<h3 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-search mr-3 text-[#38BDF8]"></i>
<h3 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-search mr-3 text-[var(--color-primary)]"></i>
Find Your Perfect Model
</h3>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-4 pointer-events-none">
<i class="fas fa-search text-[#94A3B8]"></i>
<i class="fas fa-search text-[var(--color-text-secondary)]"></i>
</div>
<input
x-model="searchTerm"
@input.debounce.500ms="fetchModels()"
class="w-full pl-12 pr-16 py-4 text-base font-normal text-[#E5E7EB] bg-[#101827] border border-[#1E293B] rounded-lg transition-colors focus:text-[#E5E7EB] focus:bg-[#101827] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50 focus:outline-none"
class="input w-full pl-12 pr-16 py-4"
type="search"
placeholder="Search models by name, tag, or description...">
<span class="absolute right-4 top-4" x-show="loading">
<svg class="animate-spin h-6 w-6 text-[#38BDF8]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<svg class="animate-spin h-6 w-6 text-[var(--color-primary)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
@@ -103,48 +100,48 @@
<!-- Filter by Type -->
<div class="mb-8">
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-filter mr-3 text-[#8B5CF6]"></i>
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-filter mr-3 text-[var(--color-accent)]"></i>
Filter by Model Type
</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-8 gap-3">
<button @click="filterByTerm('tts')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-indigo-600/20 hover:bg-indigo-600/30 text-indigo-300 border border-indigo-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-primary-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-primary-border)] transition-colors">
<i class="fas fa-microphone mr-2"></i>
<span>TTS</span>
</button>
<button @click="filterByTerm('stablediffusion')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-purple-600/20 hover:bg-purple-600/30 text-purple-300 border border-purple-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-accent-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-accent-border)] transition-colors">
<i class="fas fa-image mr-2"></i>
<span>Image</span>
</button>
<button @click="filterByTerm('llm')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-blue-600/20 hover:bg-blue-600/30 text-blue-300 border border-blue-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-primary-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-primary-border)] transition-colors">
<i class="fas fa-comment-alt mr-2"></i>
<span>LLM</span>
</button>
<button @click="filterByTerm('multimodal')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-green-600/20 hover:bg-green-600/30 text-green-300 border border-green-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-secondary-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-secondary-light)] transition-colors">
<i class="fas fa-object-group mr-2"></i>
<span>Multimodal</span>
</button>
<button @click="filterByTerm('embedding')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-cyan-600/20 hover:bg-cyan-600/30 text-cyan-300 border border-cyan-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-primary-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-primary-border)] transition-colors">
<i class="fas fa-vector-square mr-2"></i>
<span>Embedding</span>
</button>
<button @click="filterByTerm('rerank')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-amber-600/20 hover:bg-amber-600/30 text-amber-300 border border-amber-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-warning-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-warning-light)] transition-colors">
<i class="fas fa-sort-amount-up mr-2"></i>
<span>Rerank</span>
</button>
<button @click="filterByTerm('whisper')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-teal-600/20 hover:bg-teal-600/30 text-teal-300 border border-teal-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-bg-secondary)] hover:bg-[var(--color-secondary-light)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] hover:border-[var(--color-secondary-light)] transition-colors">
<i class="fas fa-headphones mr-2"></i>
<span>Whisper</span>
</button>
<button @click="filterByTerm('object-detection')"
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-red-600/20 hover:bg-red-600/30 text-red-300 border border-red-500/30 transition-colors">
class="flex items-center justify-center rounded-lg px-4 py-3 text-sm font-semibold bg-[var(--color-error)]/20 hover:bg-[var(--color-error)]/30 text-[var(--color-error)] border border-[var(--color-error-light)] transition-colors">
<i class="fas fa-eye mr-2"></i>
<span>Vision</span>
</button>
@@ -153,15 +150,15 @@
<!-- Filter by Tags -->
<div x-show="allTags.length > 0">
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-tags mr-3 text-[#8B5CF6]"></i>
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-tags mr-3 text-[var(--color-accent)]"></i>
Browse by Tags
</h3>
<div class="max-h-32 overflow-y-auto pr-2">
<div class="flex flex-wrap gap-2">
<template x-for="tag in allTags" :key="tag">
<button @click="filterByTerm(tag)"
class="inline-flex items-center text-xs px-3 py-2 rounded bg-[#101827] hover:bg-[#101827]/80 text-[#94A3B8] hover:text-[#E5E7EB] border border-[#1E293B] transition-colors">
class="inline-flex items-center text-xs px-3 py-2 rounded bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-primary)]/80 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] border border-[var(--color-bg-secondary)] transition-colors">
<i class="fas fa-tag text-xs mr-2"></i>
<span x-text="tag"></span>
</button>
@@ -175,54 +172,54 @@
<!-- Results Section -->
<div id="search-results" class="transition-all duration-300">
<div x-show="loading && models.length === 0" class="text-center py-12">
<svg class="animate-spin h-12 w-12 text-blue-500 mx-auto mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<svg class="animate-spin h-12 w-12 text-[var(--color-primary)] mx-auto mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<p class="text-gray-400">Loading models...</p>
<p class="text-[var(--color-text-secondary)]">Loading models...</p>
</div>
<div x-show="!loading && models.length === 0" class="text-center py-12">
<i class="fas fa-search text-gray-500 text-4xl mb-4"></i>
<p class="text-gray-400">No models found matching your criteria</p>
<i class="fas fa-search text-[var(--color-text-muted)] text-4xl mb-4"></i>
<p class="text-[var(--color-text-secondary)]">No models found matching your criteria</p>
</div>
<!-- Table View -->
<div x-show="models.length > 0" class="bg-[#1E293B] rounded-2xl border border-[#38BDF8]/20 overflow-hidden shadow-xl backdrop-blur-sm">
<div x-show="models.length > 0" class="card overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="bg-gradient-to-r from-[#38BDF8]/20 to-[#8B5CF6]/20 border-b border-[#38BDF8]/30">
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Icon</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Model Name</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Description</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Repository</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">License</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Status</th>
<th class="px-6 py-4 text-right text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Actions</th>
<tr class="bg-gradient-to-r from-[#38BDF8]/20 to-[#8B5CF6]/20 border-b border-[var(--color-primary-border)]/30">
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Icon</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Model Name</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Description</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Repository</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">License</th>
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Status</th>
<th class="px-6 py-4 text-right text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-[#38BDF8]/20">
<template x-for="model in models" :key="model.id">
<tr class="hover:bg-[#38BDF8]/10 transition-colors duration-200">
<tr class="hover:bg-[var(--color-primary)]/10 transition-colors duration-200">
<!-- Icon -->
<td class="px-6 py-4">
<div class="w-12 h-12 rounded-lg border border-[#38BDF8]/30 flex items-center justify-center bg-[#101827]">
<div class="w-12 h-12 rounded-lg border border-[var(--color-primary-border)]/30 flex items-center justify-center bg-[var(--color-bg-primary)]">
<img x-show="model.icon"
:src="model.icon"
class="w-full h-full object-cover rounded-lg"
loading="lazy"
:alt="model.name">
<i x-show="!model.icon" class="fas fa-brain text-xl text-[#38BDF8]"></i>
<i x-show="!model.icon" class="fas fa-brain text-xl text-[var(--color-primary)]"></i>
</div>
</td>
<!-- Model Name -->
<td class="px-6 py-4">
<div class="flex flex-col">
<span class="text-sm font-semibold text-[#E5E7EB]" x-text="model.name"></span>
<span class="text-sm font-semibold text-[var(--color-text-primary)]" x-text="model.name"></span>
<div x-show="model.trustRemoteCode" class="mt-1">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-red-500/20 text-red-300 border border-red-500/30">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-error)]/20 text-[var(--color-error)] border border-[var(--color-error-light)]">
<i class="fa-solid fa-circle-exclamation mr-1"></i>
Trust Remote Code
</span>
@@ -232,12 +229,12 @@
<!-- Description -->
<td class="px-6 py-4">
<div class="text-sm text-[#94A3B8] max-w-xs truncate" x-text="model.description" :title="model.description"></div>
<div class="text-sm text-[var(--color-text-secondary)] max-w-xs truncate" x-text="model.description" :title="model.description"></div>
</td>
<!-- Repository -->
<td class="px-6 py-4">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[#38BDF8]/10 text-[#E5E7EB] border border-[#38BDF8]/30">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-primary)]/10 text-[var(--color-text-primary)] border border-[var(--color-primary-border)]/30">
<i class="fa-brands fa-git-alt mr-1"></i>
<span x-text="model.gallery"></span>
</span>
@@ -245,21 +242,21 @@
<!-- License -->
<td class="px-6 py-4">
<span x-show="model.license" class="inline-flex items-center text-xs px-2 py-1 rounded bg-[#8B5CF6]/10 text-[#E5E7EB] border border-[#8B5CF6]/30">
<span x-show="model.license" class="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-accent)]/10 text-[var(--color-text-primary)] border border-[var(--color-accent-border)]/30">
<i class="fas fa-book mr-1"></i>
<span x-text="model.license"></span>
</span>
<span x-show="!model.license" class="text-xs text-[#94A3B8]">-</span>
<span x-show="!model.license" class="text-xs text-[var(--color-text-secondary)]">-</span>
</td>
<!-- Status -->
<td class="px-6 py-4">
<!-- Processing State -->
<div x-show="model.processing" class="min-w-[200px]">
<div class="text-xs font-medium text-[#E5E7EB] mb-1">
<div class="text-xs font-medium text-[var(--color-text-primary)] mb-1">
<span x-text="model.isDeletion ? 'Deleting...' : 'Installing...'"></span>
</div>
<div x-show="(jobProgress[model.jobID] || 0) === 0" class="text-xs text-[#38BDF8]">
<div x-show="(jobProgress[model.jobID] || 0) === 0" class="text-xs text-[var(--color-primary)]">
<i class="fas fa-clock mr-1"></i>Queued
</div>
<div class="progress-table mt-1">
@@ -269,7 +266,7 @@
<!-- Installed State -->
<div x-show="!model.processing && model.installed">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-green-500/20 text-green-300 border border-green-500/30">
<span class="badge badge-success">
<i class="fas fa-check-circle mr-1"></i>
Installed
</span>
@@ -277,7 +274,7 @@
<!-- Not Installed State -->
<div x-show="!model.processing && !model.installed">
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[#1E293B] text-[#94A3B8] border border-[#38BDF8]/30">
<span class="badge">
<i class="fas fa-circle mr-1"></i>
Not Installed
</span>
@@ -289,7 +286,7 @@
<div class="flex items-center justify-end gap-2">
<!-- Info Button -->
<button @click="openModal(model)"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[#1E293B] hover:bg-[#38BDF8]/20 text-xs font-medium text-[#E5E7EB] transition duration-200 border border-[#38BDF8]/30"
class="btn-secondary text-xs px-3 py-1.5"
title="View details">
<i class="fas fa-info-circle"></i>
</button>
@@ -298,12 +295,12 @@
<template x-if="!model.processing && model.installed">
<div class="flex gap-2">
<button @click="reinstallModel(model.id)"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[#38BDF8] hover:bg-[#38BDF8]/80 text-xs font-medium text-white transition duration-200"
class="btn-primary text-xs px-3 py-1.5"
title="Reinstall">
<i class="fa-solid fa-arrow-rotate-right"></i>
</button>
<button @click="deleteModel(model.id)"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-red-600 hover:bg-red-700 text-xs font-medium text-white transition duration-200"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-error)] hover:bg-[var(--color-error)] text-xs font-medium text-white transition duration-200"
title="Delete">
<i class="fa-solid fa-trash"></i>
</button>
@@ -314,12 +311,12 @@
<template x-if="!model.processing && !model.installed">
<div class="flex gap-2">
<button @click="getConfig(model.id)"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[#8B5CF6]/20 hover:bg-[#8B5CF6]/40 text-xs font-medium text-[#E5E7EB] transition duration-200 border border-[#8B5CF6]/30"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-accent)]/20 hover:bg-[var(--color-accent)]/40 text-xs font-medium text-[var(--color-text-primary)] transition duration-200 border border-[var(--color-accent-border)]/30"
title="Get config">
<i class="fa-solid fa-file-code"></i>
</button>
<button @click="installModel(model.id)"
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[#38BDF8] hover:bg-[#38BDF8]/80 text-xs font-medium text-white transition duration-200"
class="btn-primary text-xs px-3 py-1.5"
title="Install">
<i class="fa-solid fa-download"></i>
</button>
@@ -346,7 +343,7 @@
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
<h3 class="text-xl font-semibold text-gray-900 dark:text-white" x-text="selectedModel?.name"></h3>
<button @click="closeModal()"
class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
class="text-[var(--color-text-secondary)] bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white">
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
@@ -361,10 +358,10 @@
:src="selectedModel?.icon"
class="rounded-lg max-h-48 max-w-96 object-cover"
loading="lazy">
<i x-show="!selectedModel?.icon" class="fas fa-brain text-6xl text-gray-400 dark:text-gray-500"></i>
<i x-show="!selectedModel?.icon" class="fas fa-brain text-6xl text-[var(--color-text-secondary)] dark:text-[var(--color-text-muted)]"></i>
</div>
</div>
<div class="text-base leading-relaxed text-gray-500 dark:text-gray-400 break-words max-w-full markdown-content" x-html="renderMarkdown(selectedModel?.description)"></div>
<div class="text-base leading-relaxed text-[var(--color-text-muted)] dark:text-[var(--color-text-secondary)] break-words max-w-full markdown-content" x-html="renderMarkdown(selectedModel?.description)"></div>
<hr>
<template x-if="selectedModel?.urls && selectedModel.urls.length > 0">
<div>
@@ -372,7 +369,7 @@
<ul>
<template x-for="url in selectedModel.urls" :key="url">
<li>
<a :href="url" target="_blank" class="text-base leading-relaxed text-gray-500 dark:text-gray-400 hover:text-blue-500">
<a :href="url" target="_blank" class="text-base leading-relaxed text-[var(--color-text-muted)] dark:text-[var(--color-text-secondary)] hover:text-[var(--color-primary)]">
<i class="fas fa-link pr-2"></i>
<span x-text="url"></span>
</a>
@@ -387,7 +384,7 @@
<ul>
<template x-for="file in selectedModel.additionalFiles" :key="file">
<li class="mb-0">
<p class="text-base leading-tight text-gray-500 dark:text-gray-400">
<p class="text-base leading-tight text-[var(--color-text-muted)] dark:text-[var(--color-text-secondary)]">
<i class="fas fa-file pr-2"></i>
<span x-text="file.filename"></span>
</p>
@@ -414,7 +411,7 @@
<!-- Modal Footer -->
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
<button @click="closeModal()"
class="py-2.5 px-5 ms-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
class="py-2.5 px-5 ms-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-[var(--color-text-secondary)] dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
Close
</button>
</div>
@@ -429,13 +426,13 @@
<button @click="goToPage(currentPage - 1)"
:disabled="currentPage <= 1"
:class="currentPage <= 1 ? 'opacity-50 cursor-not-allowed' : ''"
class="flex items-center justify-center h-12 w-12 bg-[#1E293B] hover:bg-indigo-600 text-[#94A3B8] hover:text-white rounded-lg transition-colors">
class="flex items-center justify-center h-12 w-12 bg-[var(--color-bg-secondary)] hover:bg-indigo-600 text-[var(--color-text-secondary)] hover:text-white rounded-lg transition-colors">
<i class="fas fa-chevron-left"></i>
</button>
<div class="text-gray-300 text-sm font-medium px-4">
<span class="text-gray-400">Page</span>
<span class="text-[var(--color-text-secondary)]">Page</span>
<span class="text-white font-bold text-lg mx-2" x-text="currentPage"></span>
<span class="text-gray-400">of</span>
<span class="text-[var(--color-text-secondary)]">of</span>
<span class="text-white font-bold text-lg mx-2" x-text="totalPages"></span>
</div>
<button @click="goToPage(currentPage + 1)"

View File

@@ -12,37 +12,32 @@
<div class="container mx-auto px-4 py-8 flex-grow">
{{ if eq .P2PToken "" }}
<!-- P2P Disabled - Wizard Guide -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-12">
<div class="text-center max-w-4xl mx-auto">
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#8B5CF6]/10 border border-[#8B5CF6]/20 mb-6">
<i class="text-[#8B5CF6] text-2xl fas fa-circle-nodes"></i>
</div>
<h2 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
P2P Distribution Not Enabled
</span>
<div class="hero-section">
<div class="hero-content">
<h2 class="hero-title">
P2P Distribution Not Enabled
</h2>
<p class="text-xl text-[#94A3B8] mb-8">
<p class="hero-subtitle">
Enable peer-to-peer distribution to scale your AI workloads across multiple devices. Share instances, shard models, and pool computational resources across your network.
</p>
<!-- Features Preview -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-10">
<div class="bg-[#101827] border border-[#38BDF8]/20 rounded-lg p-4">
<div class="card card-animate">
<div class="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-network-wired text-[#38BDF8] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Instance Federation</h3>
<p class="text-xs text-[#94A3B8]">Load balance across multiple instances</p>
</div>
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-lg p-4">
<div class="card card-animate">
<div class="w-10 h-10 bg-purple-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-puzzle-piece text-[#8B5CF6] text-xl"></i>
</div>
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Model Sharding</h3>
<p class="text-xs text-[#94A3B8]">Split large models across workers</p>
</div>
<div class="bg-[#101827] border border-green-500/20 rounded-lg p-4">
<div class="card card-animate">
<div class="w-10 h-10 bg-green-500/10 rounded-lg flex items-center justify-center mx-auto mb-3">
<i class="fas fa-share-alt text-green-400 text-xl"></i>
</div>
@@ -52,7 +47,7 @@
</div>
<!-- Setup Instructions -->
<div class="bg-[#101827] border border-[#8B5CF6]/20 rounded-xl p-6 mb-8 text-left">
<div class="card mb-8 text-left">
<h3 class="text-lg font-bold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-rocket text-[#8B5CF6] mr-2"></i>
How to Enable P2P
@@ -118,12 +113,10 @@
<div class="animation-container mb-8">
<canvas id="networkCanvas"></canvas>
<div class="text-overlay">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
<i class="fa-solid fa-circle-nodes mr-2"></i> Distributed AI Computing
</span>
<h1 class="hero-title">
<i class="fa-solid fa-circle-nodes mr-2"></i> Distributed AI Computing
</h1>
<p class="text-xl text-[#94A3B8]">
<p class="hero-subtitle">
Scale your AI workloads across multiple devices with peer-to-peer distribution
<a href="https://localai.io/features/distribute/" target="_blank" class="text-[#38BDF8] hover:text-[#8B5CF6] transition-colors">
<i class="fas fa-circle-info ml-2"></i>
@@ -133,14 +126,12 @@
</div>
<!-- How P2P Distribution Works -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8 mb-12">
<div class="card p-8 mb-12">
<div>
<div class="text-center mb-10">
<h2 class="text-3xl md:text-4xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-400">
<h2 class="h2 mb-4">
How P2P Distribution Works
</span>
</h2>
</h2>
<p class="text-lg text-[#94A3B8] max-w-3xl mx-auto">
LocalAI leverages cutting-edge peer-to-peer technologies to distribute AI workloads intelligently across your network
</p>

View File

@@ -9,20 +9,28 @@
<script defer src="static/assets/alpine.js"></script>
<script defer src="static/assets/marked.js"></script>
<script defer src="static/assets/purify.js"></script>
<!-- LocalAI Design System CSS -->
<link href="static/theme.css" rel="stylesheet" />
<link href="static/typography.css" rel="stylesheet" />
<link href="static/animations.css" rel="stylesheet" />
<link href="static/components.css" rel="stylesheet" />
<link href="static/general.css" rel="stylesheet" />
<link href="static/assets/font1.css" rel="stylesheet">
<link href="static/assets/font2.css" rel="stylesheet" />
<link rel="stylesheet" href="static/assets/tw-elements.css" />
<script src="static/assets/tailwindcss.js"></script>
<!-- Preload critical fonts -->
<link rel="preload" href="static/assets/playfair-display-bold.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="static/assets/space-grotesk-regular.ttf" as="font" type="font/ttf" crossorigin>
<script>
tailwind.config = {
darkMode: "class",
theme: {
fontFamily: {
sans: ["Roboto", "sans-serif"],
body: ["Roboto", "sans-serif"],
mono: ["ui-monospace", "monospace"],
sans: ["Space Grotesk", "-apple-system", "BlinkMacSystemFont", "sans-serif"],
body: ["Space Grotesk", "-apple-system", "BlinkMacSystemFont", "sans-serif"],
display: ["Playfair Display", "serif"],
mono: ["JetBrains Mono", "Fira Code", "monospace"],
},
},
corePlugins: {
@@ -77,114 +85,4 @@
<link href="static/assets/fontawesome/css/brands.css" rel="stylesheet" />
<link href="static/assets/fontawesome/css/solid.css" rel="stylesheet" />
<script src="static/assets/flowbite.min.js"></script>
<!-- Tech Noir UI Styling -->
<style>
/* Core Tech Noir Color Palette */
:root {
--tn-bg: #101827;
--tn-ui: #1E293B;
--tn-primary: #38BDF8;
--tn-secondary: #8B5CF6;
--tn-text: #E5E7EB;
--tn-muted: #94A3B8;
}
.animation-container {
position: relative;
width: 100%;
height: 25vh;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
background: linear-gradient(135deg, var(--tn-bg) 0%, var(--tn-ui) 100%);
}
canvas {
position: absolute;
top: 0;
left: 0;
}
.text-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
z-index: 1;
}
.fa-circle-nodes {
animation: rotateCircleNodes 8s linear infinite;
display: inline-block;
filter: drop-shadow(0 0 8px var(--tn-primary));
}
@keyframes rotateCircleNodes {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.fa-flask {
animation: shakeFlask 3s ease-in-out infinite;
transform-origin: bottom center;
}
@keyframes shakeFlask {
0%, 10% { transform: rotate(0deg); }
20% { transform: rotate(-10deg); }
30% { transform: rotate(10deg); }
40% { transform: rotate(-8deg); }
50% { transform: rotate(8deg); }
60% { transform: rotate(-5deg); }
70% { transform: rotate(5deg); }
80% { transform: rotate(-2deg); }
90% { transform: rotate(2deg); }
100% { transform: rotate(0deg); }
}
/* Active node with cyan glow */
.active-node {
position: relative;
overflow: hidden;
}
.active-node::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, var(--tn-primary), transparent);
animation: nodeGlow 3s ease-in-out infinite;
}
@keyframes nodeGlow {
0% { left: -100%; }
50% { left: 100%; }
100% { left: 100%; }
}
/* Enhanced scrollbar styling */
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: var(--tn-bg);
}
::-webkit-scrollbar-thumb {
background: var(--tn-ui);
border-radius: 5px;
border: 2px solid var(--tn-bg);
}
::-webkit-scrollbar-thumb:hover {
background: var(--tn-primary);
}
/* Glow effects for interactive elements */
.glow-on-hover:hover {
box-shadow: 0 0 20px rgba(56, 189, 248, 0.3);
}
</style>
</head>

View File

@@ -1,4 +1,4 @@
<nav class="bg-[#101827] shadow-2xl border-b border-[#1E293B]">
<nav class="bg-[var(--color-bg-primary)] shadow-2xl border-b border-[var(--color-bg-secondary)]">
<div class="container mx-auto px-4 py-2">
<div class="flex items-center justify-between">
<div class="flex items-center">
@@ -6,46 +6,46 @@
<a href="./" class="flex items-center group">
<img src="static/logo_horizontal.png"
alt="LocalAI Logo"
class="h-10 mr-3 brightness-110 transition-all duration-300 group-hover:brightness-125 group-hover:drop-shadow-[0_0_8px_rgba(56,189,248,0.5)]">
class="h-10 mr-3 brightness-110 transition-all duration-300 group-hover:brightness-125 group-hover:drop-shadow-[0_0_8px_var(--color-primary-border)]">
</a>
</div>
<!-- Menu button for small screens -->
<div class="lg:hidden">
<button id="menu-toggle" class="text-[#94A3B8] hover:text-[#38BDF8] focus:outline-none p-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B]">
<button id="menu-toggle" class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] focus:outline-none p-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)]">
<i class="fas fa-bars fa-lg"></i>
</button>
</div>
<!-- Navigation links -->
<div class="hidden lg:flex lg:items-center lg:justify-end lg:space-x-1" x-data="{ manageOpen: false }">
<a href="./" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fas fa-home text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Home
<a href="./" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fas fa-home text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Home
</a>
<a href="chat/" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fa-solid fa-comments text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Chat
<a href="chat/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fa-solid fa-comments text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Chat
</a>
<a href="text2image/" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fas fa-image text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Images
<a href="text2image/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fas fa-image text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Images
</a>
<a href="tts/" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fa-solid fa-music text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>TTS
<a href="tts/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fa-solid fa-music text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>TTS
</a>
<a href="talk/" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fa-solid fa-phone text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Talk
<a href="talk/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fa-solid fa-phone text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Talk
</a>
<a href="swagger/" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fas fa-code text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>API
<a href="swagger/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fas fa-code text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>API
</a>
<a href="agent-jobs" class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fas fa-tasks text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Agent Jobs
<a href="agent-jobs" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fas fa-tasks text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Agent Jobs
</a>
<!-- System Dropdown -->
<div class="relative" @click.away="manageOpen = false">
<button @click="manageOpen = !manageOpen"
class="text-[#94A3B8] hover:text-[#E5E7EB] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[#1E293B] flex items-center group text-sm">
<i class="fas fa-cog text-[#38BDF8] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Settings
class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-2 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-secondary)] flex items-center group text-sm">
<i class="fas fa-cog text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Settings
<i class="fas fa-chevron-down ml-1 text-xs transition-transform" :class="manageOpen ? 'rotate-180' : ''"></i>
</button>
<div x-show="manageOpen"
@@ -55,18 +55,18 @@
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute top-full right-0 mt-1 w-48 bg-[#1E293B] border border-[#38BDF8]/20 rounded-lg shadow-lg z-50 py-1">
<a href="browse/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#101827] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-brain text-[#38BDF8] mr-2 text-xs"></i>Models
class="absolute top-full right-0 mt-1 w-48 bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-lg shadow-lg z-50 py-1">
<a href="browse/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-brain text-[var(--color-primary)] mr-2 text-xs"></i>Models
</a>
<a href="browse/backends" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#101827] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-server text-[#38BDF8] mr-2 text-xs"></i>Backends
<a href="browse/backends" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-server text-[var(--color-primary)] mr-2 text-xs"></i>Backends
</a>
<a href="p2p/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#101827] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fa-solid fa-circle-nodes text-[#38BDF8] mr-2 text-xs"></i>Swarm
<a href="p2p/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fa-solid fa-circle-nodes text-[var(--color-primary)] mr-2 text-xs"></i>Swarm
</a>
<a href="/manage" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#101827] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-cog text-[#38BDF8] mr-2 text-xs"></i>System
<a href="/manage" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 text-sm transition-colors flex items-center">
<i class="fas fa-cog text-[var(--color-primary)] mr-2 text-xs"></i>System
</a>
</div>
</div>
@@ -75,17 +75,17 @@
<!-- Collapsible menu for small screens -->
<div class="hidden lg:hidden" id="mobile-menu" x-data="{ manageOpen: false }">
<div class="pt-3 pb-2 space-y-1 border-t border-[#1E293B] mt-2">
<a href="./" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-home text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Home
<div class="pt-3 pb-2 space-y-1 border-t border-[var(--color-bg-secondary)] mt-2">
<a href="./" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-home text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Home
</a>
<!-- System with submenu -->
<div>
<button @click="manageOpen = !manageOpen"
class="w-full text-left text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center justify-between text-sm">
class="w-full text-left text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center justify-between text-sm">
<div class="flex items-center">
<i class="fas fa-cog text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Settings
<i class="fas fa-cog text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Settings
</div>
<i class="fas fa-chevron-down text-xs transition-transform" :class="manageOpen ? 'rotate-180' : ''"></i>
</button>
@@ -97,38 +97,38 @@
x-transition:leave-start="opacity-100 max-h-96"
x-transition:leave-end="opacity-0 max-h-0"
class="overflow-hidden">
<a href="browse/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-brain text-[#38BDF8] mr-3 w-5 text-center text-xs"></i>Models
<a href="browse/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-brain text-[var(--color-primary)] mr-3 w-5 text-center text-xs"></i>Models
</a>
<a href="browse/backends" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-server text-[#38BDF8] mr-3 w-5 text-center text-xs"></i>Backends
<a href="browse/backends" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-server text-[var(--color-primary)] mr-3 w-5 text-center text-xs"></i>Backends
</a>
<a href="p2p/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-circle-nodes text-[#38BDF8] mr-3 w-5 text-center text-xs"></i>Swarm
<a href="p2p/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-circle-nodes text-[var(--color-primary)] mr-3 w-5 text-center text-xs"></i>Swarm
</a>
<a href="/manage" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-cog text-[#38BDF8] mr-3 w-5 text-center text-xs"></i>System
<a href="/manage" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] pl-8 pr-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-cog text-[var(--color-primary)] mr-3 w-5 text-center text-xs"></i>System
</a>
</div>
</div>
<a href="chat/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-comments text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Chat
<a href="chat/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-comments text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Chat
</a>
<a href="text2image/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-image text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Images
<a href="text2image/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-image text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Images
</a>
<a href="tts/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-music text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>TTS
<a href="tts/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-music text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>TTS
</a>
<a href="talk/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-phone text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Talk
<a href="talk/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fa-solid fa-phone text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Talk
</a>
<a href="swagger/" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-code text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>API
<a href="swagger/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-code text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>API
</a>
<a href="agent-jobs" class="block text-[#94A3B8] hover:text-[#E5E7EB] hover:bg-[#1E293B] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-tasks text-[#38BDF8] mr-3 w-5 text-center text-sm"></i>Agent Jobs
<a href="agent-jobs" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center text-sm">
<i class="fas fa-tasks text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Agent Jobs
</a>
</div>
</div>

View File

@@ -2,7 +2,7 @@
<html lang="en">
{{template "views/partials/head" .}}
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen" x-data="settingsDashboard()">
{{template "views/partials/navbar" .}}
@@ -36,27 +36,27 @@
<!-- Header -->
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<h1 class="text-2xl font-semibold text-[#E5E7EB]">
<h1 class="h2">
Application Settings
</h1>
<a href="/manage"
class="inline-flex items-center text-[#94A3B8] hover:text-[#E5E7EB] transition-colors">
class="inline-flex items-center text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors">
<i class="fas fa-arrow-left mr-2 text-sm"></i>
<span class="text-sm">Back to Manage</span>
</a>
</div>
<p class="text-sm text-[#94A3B8]">Configure watchdog and backend request settings</p>
<p class="text-sm text-[var(--color-text-secondary)]">Configure watchdog and backend request settings</p>
</div>
<!-- Settings Form -->
<form @submit.prevent="saveSettings()" class="space-y-6">
<!-- Watchdog Settings Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-shield-alt mr-2 text-[#38BDF8] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-shield-alt mr-2 text-[var(--color-primary)] text-sm"></i>
Watchdog Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure automatic monitoring and management of backend processes
</p>
@@ -64,76 +64,76 @@
<!-- Enable Watchdog -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Enable Watchdog</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable automatic monitoring of backend processes</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Enable Watchdog</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable automatic monitoring of backend processes</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.watchdog_enabled"
@change="updateWatchdogEnabled()"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#38BDF8]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#38BDF8]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-primary-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
</label>
</div>
<!-- Enable Idle Check -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Enable Idle Check</label>
<p class="text-xs text-[#94A3B8] mt-1">Automatically stop backends that are idle for too long</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Enable Idle Check</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Automatically stop backends that are idle for too long</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.watchdog_idle_enabled"
:disabled="!settings.watchdog_enabled"
class="sr-only peer" :class="!settings.watchdog_enabled ? 'opacity-50' : ''">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#38BDF8]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#38BDF8]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-primary-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
</label>
</div>
<!-- Idle Timeout -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Idle Timeout</label>
<p class="text-xs text-[#94A3B8] mb-2">Time before an idle backend is stopped (e.g., 15m, 1h)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Idle Timeout</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Time before an idle backend is stopped (e.g., 15m, 1h)</p>
<input type="text" x-model="settings.watchdog_idle_timeout"
:disabled="!settings.watchdog_idle_enabled"
placeholder="15m"
class="w-full px-3 py-2 bg-[#101827] border border-[#38BDF8]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#38BDF8]/50"
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary-border)]"
:class="!settings.watchdog_idle_enabled ? 'opacity-50 cursor-not-allowed' : ''">
</div>
<!-- Enable Busy Check -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Enable Busy Check</label>
<p class="text-xs text-[#94A3B8] mt-1">Automatically stop backends that are busy for too long (stuck processes)</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Enable Busy Check</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Automatically stop backends that are busy for too long (stuck processes)</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.watchdog_busy_enabled"
:disabled="!settings.watchdog_enabled"
class="sr-only peer" :class="!settings.watchdog_enabled ? 'opacity-50' : ''">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#38BDF8]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#38BDF8]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-primary-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
</label>
</div>
<!-- Busy Timeout -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Busy Timeout</label>
<p class="text-xs text-[#94A3B8] mb-2">Time before a busy backend is stopped (e.g., 5m, 30m)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Busy Timeout</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Time before a busy backend is stopped (e.g., 5m, 30m)</p>
<input type="text" x-model="settings.watchdog_busy_timeout"
:disabled="!settings.watchdog_busy_enabled"
placeholder="5m"
class="w-full px-3 py-2 bg-[#101827] border border-[#38BDF8]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#38BDF8]/50"
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-primary-border)]/20 rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary-border)]"
:class="!settings.watchdog_busy_enabled ? 'opacity-50 cursor-not-allowed' : ''">
</div>
</div>
</div>
<!-- Backend Request Settings Section -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-cogs mr-2 text-[#8B5CF6] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent-light)] rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-cogs mr-2 text-[var(--color-accent)] text-sm"></i>
Backend Request Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure how backends handle multiple requests
</p>
@@ -141,97 +141,97 @@
<!-- Single Backend Mode -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Single Backend Mode</label>
<p class="text-xs text-[#94A3B8] mt-1">Allow only one backend to be active at a time</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Single Backend Mode</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Allow only one backend to be active at a time</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.single_backend"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#8B5CF6]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#8B5CF6]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-accent-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
</label>
</div>
<!-- Parallel Backend Requests -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Parallel Backend Requests</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable backends to handle multiple requests in parallel (if supported)</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Parallel Backend Requests</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable backends to handle multiple requests in parallel (if supported)</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.parallel_backend_requests"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#8B5CF6]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#8B5CF6]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-accent-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
</label>
</div>
</div>
</div>
<!-- Performance Settings Section -->
<div class="bg-[#1E293B] border border-[#10B981]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-tachometer-alt mr-2 text-[#10B981] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-success-light)] rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-tachometer-alt mr-2 text-[var(--color-success)] text-sm"></i>
Performance Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure default performance parameters for models
</p>
<div class="space-y-4">
<!-- Threads -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Default Threads</label>
<p class="text-xs text-[#94A3B8] mb-2">Number of threads to use for model inference (0 = auto)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Default Threads</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Number of threads to use for model inference (0 = auto)</p>
<input type="number" x-model="settings.threads"
min="0"
placeholder="0"
class="w-full px-3 py-2 bg-[#101827] border border-[#10B981]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#10B981]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-success-light)] rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-success-light)]">
</div>
<!-- Context Size -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Default Context Size</label>
<p class="text-xs text-[#94A3B8] mb-2">Default context window size for models</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Default Context Size</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Default context window size for models</p>
<input type="number" x-model="settings.context_size"
min="0"
placeholder="512"
class="w-full px-3 py-2 bg-[#101827] border border-[#10B981]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#10B981]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-success-light)] rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-success-light)]">
</div>
<!-- F16 -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">F16 Precision</label>
<p class="text-xs text-[#94A3B8] mt-1">Use 16-bit floating point precision</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">F16 Precision</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Use 16-bit floating point precision</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.f16"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#10B981]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#10B981]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-success-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-success)]"></div>
</label>
</div>
<!-- Debug -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Debug Mode</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable debug logging</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Debug Mode</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable debug logging</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.debug"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#10B981]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#10B981]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-success-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-success)]"></div>
</label>
</div>
</div>
</div>
<!-- API Settings Section -->
<div class="bg-[#1E293B] border border-[#F59E0B]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-globe mr-2 text-[#F59E0B] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-warning-light)] rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-globe mr-2 text-[var(--color-warning)] text-sm"></i>
API Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure CORS and CSRF protection
</p>
@@ -239,138 +239,138 @@
<!-- CORS -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Enable CORS</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable Cross-Origin Resource Sharing</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Enable CORS</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable Cross-Origin Resource Sharing</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.cors"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#F59E0B]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#F59E0B]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-warning-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-warning)]"></div>
</label>
</div>
<!-- CORS Allow Origins -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">CORS Allow Origins</label>
<p class="text-xs text-[#94A3B8] mb-2">Comma-separated list of allowed origins</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">CORS Allow Origins</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Comma-separated list of allowed origins</p>
<input type="text" x-model="settings.cors_allow_origins"
placeholder="*"
class="w-full px-3 py-2 bg-[#101827] border border-[#F59E0B]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#F59E0B]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-warning-light)] rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-warning-light)]">
</div>
<!-- CSRF -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Enable CSRF Protection</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable Cross-Site Request Forgery protection</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Enable CSRF Protection</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable Cross-Site Request Forgery protection</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.csrf"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#F59E0B]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#F59E0B]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-warning-light)] rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-warning)]"></div>
</label>
</div>
</div>
</div>
<!-- P2P Settings Section -->
<div class="bg-[#1E293B] border border-[#EC4899]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-network-wired mr-2 text-[#EC4899] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-network-wired mr-2 text-[var(--color-accent)] text-sm"></i>
P2P Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure peer-to-peer networking
</p>
<div class="space-y-4">
<!-- P2P Token -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">P2P Token</label>
<p class="text-xs text-[#94A3B8] mb-2">Authentication token for P2P network (set to 0 to generate a new token)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">P2P Token</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Authentication token for P2P network (set to 0 to generate a new token)</p>
<input type="text" x-model="settings.p2p_token"
placeholder=""
class="w-full px-3 py-2 bg-[#101827] border border-[#EC4899]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#EC4899]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-accent)]/20 rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)]/50">
</div>
<!-- P2P Network ID -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">P2P Network ID</label>
<p class="text-xs text-[#94A3B8] mb-2">Network identifier for P2P connections</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">P2P Network ID</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Network identifier for P2P connections</p>
<input type="text" x-model="settings.p2p_network_id"
placeholder=""
class="w-full px-3 py-2 bg-[#101827] border border-[#EC4899]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#EC4899]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-accent)]/20 rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)]/50">
</div>
<!-- Federated -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Federated Mode</label>
<p class="text-xs text-[#94A3B8] mt-1">Enable federated instance mode</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Federated Mode</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Enable federated instance mode</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.federated"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#EC4899]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#EC4899]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-accent)]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
</label>
</div>
</div>
</div>
<!-- Agent Jobs Settings Section -->
<div class="bg-[#1E293B] border border-[#06B6D4]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-tasks mr-2 text-[#06B6D4] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-tasks mr-2 text-[var(--color-primary)] text-sm"></i>
Agent Jobs Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure agent job retention and cleanup
</p>
<div class="space-y-4">
<!-- Agent Job Retention Days -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Job Retention Days</label>
<p class="text-xs text-[#94A3B8] mb-2">Number of days to keep job history (default: 30)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Job Retention Days</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Number of days to keep job history (default: 30)</p>
<input type="number" x-model="settings.agent_job_retention_days"
min="0"
placeholder="30"
class="w-full px-3 py-2 bg-[#101827] border border-[#06B6D4]/20 rounded text-sm text-[#E5E7EB] focus:outline-none focus:ring-2 focus:ring-[#06B6D4]/50">
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded text-sm text-[var(--color-text-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)]/50">
</div>
</div>
</div>
<!-- API Keys Settings Section -->
<div class="bg-[#1E293B] border border-[#EF4444]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-key mr-2 text-[#EF4444] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-error-light)] rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-key mr-2 text-[var(--color-error)] text-sm"></i>
API Keys
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Manage API keys for authentication. Keys from environment variables are always included.
</p>
<div class="space-y-4">
<!-- API Keys List -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">API Keys</label>
<p class="text-xs text-[#94A3B8] mb-2">List of API keys (one per line or comma-separated)</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">API Keys</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">List of API keys (one per line or comma-separated)</p>
<textarea x-model="settings.api_keys_text"
rows="4"
placeholder="sk-1234567890abcdef&#10;sk-0987654321fedcba"
class="w-full px-3 py-2 bg-[#101827] border border-[#EF4444]/20 rounded text-sm text-[#E5E7EB] font-mono focus:outline-none focus:ring-2 focus:ring-[#EF4444]/50"></textarea>
<p class="text-xs text-[#94A3B8] mt-1">Note: API keys are sensitive. Handle with care.</p>
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-error-light)] rounded text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:ring-2 focus:ring-[var(--color-error-light)]"></textarea>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Note: API keys are sensitive. Handle with care.</p>
</div>
</div>
</div>
<!-- Gallery Settings Section -->
<div class="bg-[#1E293B] border border-[#6366F1]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[#E5E7EB] mb-4 flex items-center">
<i class="fas fa-images mr-2 text-[#6366F1] text-sm"></i>
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-lg p-6">
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4 flex items-center">
<i class="fas fa-images mr-2 text-[var(--color-accent)] text-sm"></i>
Gallery Settings
</h2>
<p class="text-xs text-[#94A3B8] mb-4">
<p class="text-xs text-[var(--color-text-secondary)] mb-4">
Configure model and backend galleries
</p>
@@ -378,47 +378,47 @@
<!-- Autoload Galleries -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Autoload Galleries</label>
<p class="text-xs text-[#94A3B8] mt-1">Automatically load model galleries on startup</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Autoload Galleries</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Automatically load model galleries on startup</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.autoload_galleries"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#6366F1]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#6366F1]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-accent)]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
</label>
</div>
<!-- Autoload Backend Galleries -->
<div class="flex items-center justify-between">
<div>
<label class="text-sm font-medium text-[#E5E7EB]">Autoload Backend Galleries</label>
<p class="text-xs text-[#94A3B8] mt-1">Automatically load backend galleries on startup</p>
<label class="text-sm font-medium text-[var(--color-text-primary)]">Autoload Backend Galleries</label>
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Automatically load backend galleries on startup</p>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" x-model="settings.autoload_backend_galleries"
class="sr-only peer">
<div class="w-11 h-6 bg-[#101827] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#6366F1]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#6366F1]"></div>
<div class="w-11 h-6 bg-[var(--color-bg-primary)] peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--color-accent)]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
</label>
</div>
<!-- Galleries (JSON) -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Model Galleries (JSON)</label>
<p class="text-xs text-[#94A3B8] mb-2">Array of gallery objects with 'url' and 'name' fields</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Model Galleries (JSON)</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Array of gallery objects with 'url' and 'name' fields</p>
<textarea x-model="settings.galleries_json"
rows="4"
placeholder='[{"url": "https://example.com", "name": "Example Gallery"}]'
class="w-full px-3 py-2 bg-[#101827] border border-[#6366F1]/20 rounded text-sm text-[#E5E7EB] font-mono focus:outline-none focus:ring-2 focus:ring-[#6366F1]/50"></textarea>
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-accent)]/20 rounded text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)]/50"></textarea>
</div>
<!-- Backend Galleries (JSON) -->
<div>
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Backend Galleries (JSON)</label>
<p class="text-xs text-[#94A3B8] mb-2">Array of backend gallery objects with 'url' and 'name' fields</p>
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Backend Galleries (JSON)</label>
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Array of backend gallery objects with 'url' and 'name' fields</p>
<textarea x-model="settings.backend_galleries_json"
rows="4"
placeholder='[{"url": "https://example.com", "name": "Example Backend Gallery"}]'
class="w-full px-3 py-2 bg-[#101827] border border-[#6366F1]/20 rounded text-sm text-[#E5E7EB] font-mono focus:outline-none focus:ring-2 focus:ring-[#6366F1]/50"></textarea>
class="w-full px-3 py-2 bg-[var(--color-bg-primary)] border border-[var(--color-accent)]/20 rounded text-sm text-[var(--color-text-primary)] font-mono focus:outline-none focus:ring-2 focus:ring-[var(--color-accent)]/50"></textarea>
</div>
</div>
</div>
@@ -441,7 +441,7 @@
<div class="flex justify-end">
<button type="submit"
:disabled="saving"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 disabled:opacity-50 disabled:cursor-not-allowed text-white py-2 px-6 rounded-lg font-medium transition-colors">
class="btn-primary">
<i class="fas fa-save mr-2" :class="saving ? 'fa-spin fa-spinner' : ''"></i>
<span x-text="saving ? 'Saving...' : 'Save Settings'"></span>
</button>

View File

@@ -9,20 +9,18 @@
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-10">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
<i class="fas fa-comments mr-2"></i>Talk Interface
</span>
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
<i class="fas fa-comments mr-2"></i>Talk Interface
</h1>
<p class="text-xl text-[#94A3B8] mb-6">Speak with your AI models using voice interaction</p>
<p class="hero-subtitle">Speak with your AI models using voice interaction</p>
</div>
</div>
<!-- Talk Interface -->
<div class="max-w-3xl mx-auto">
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl overflow-hidden">
<div class="card overflow-hidden">
<!-- Talk Interface Body -->
<div class="p-6">
<!-- Recording Status -->

View File

@@ -3,59 +3,45 @@
{{template "views/partials/head" .}}
<script defer src="static/image.js"></script>
<body class="bg-[#101827] text-[#E5E7EB]">
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
<div class="flex flex-col min-h-screen">
{{template "views/partials/navbar" .}}
<div class="container mx-auto px-4 py-8 flex-grow" x-data="{ component: 'menu' }">
<!-- Hero Section -->
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-6">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#38BDF8] to-[#8B5CF6]">
Image Generation {{ if .Model }} with {{.Model}} {{ end }}
</span>
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
Image Generation {{ if .Model }} with {{.Model}} {{ end }}
</h1>
<p class="text-xl text-[#94A3B8] mb-6">Create stunning images from text descriptions</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="https://localai.io/features/image-generation/" target="_blank"
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] font-semibold py-2 px-6 rounded-lg transition-colors">
<i class="fas fa-book-reader mr-2"></i>
<span>Documentation</span>
</a>
<a href="browse"
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white font-semibold py-2 px-6 rounded-lg transition-colors">
<i class="fas fa-images mr-2"></i>
<span>Gallery</span>
</a>
</div>
<p class="hero-subtitle">Create stunning images from text descriptions</p>
</div>
</div>
<!-- Model Selection - Positioned between hero and generation form -->
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl p-5 mb-6">
<div class="card p-5 mb-6">
<div class="flex items-center">
<div class="text-lg font-medium text-[#38BDF8] mr-4">
<div class="text-lg font-medium text-[var(--color-primary)] mr-4">
<i class="fas fa-palette mr-2"></i>Select Model:
</div>
<div class="flex-grow">
<select x-data="{ link : '' }" x-model="link" x-init="$watch('link', value => window.location = link)"
id="model-select"
class="bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring focus:ring-[#38BDF8] focus:ring-opacity-50 rounded-lg shadow-sm p-2.5 pr-10 appearance-none w-full max-w-md transition-colors duration-200"
class="input w-full max-w-md p-2.5 pr-10"
>
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
{{ $model:=.Model}}
{{ range .ModelsConfig }}
{{ $cfg := . }}
{{ range .KnownUsecaseStrings }}
{{ if eq . "FLAG_IMAGE" }}
<option value="text2image/{{$cfg.Name}}" {{ if eq $cfg.Name $model }} selected {{end}} class="bg-[#101827] text-[#E5E7EB]">{{$cfg.Name}}</option>
<option value="text2image/{{$cfg.Name}}" {{ if eq $cfg.Name $model }} selected {{end}} class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{$cfg.Name}}</option>
{{ end }}
{{ end }}
{{ end }}
{{ range .ModelsWithoutConfig }}
<option value="text2image/{{.}}" {{ if eq . $model }} selected {{ end }} class="bg-[#101827] text-[#E5E7EB]">{{.}}</option>
<option value="text2image/{{.}}" {{ if eq . $model }} selected {{ end }} class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
{{end}}
</select>
</div>
@@ -63,15 +49,15 @@
</div>
<!-- Image Generation Form -->
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl p-6">
<h2 class="text-2xl font-bold text-[#E5E7EB] mb-6">Generate an Image</h2>
<div class="card p-6">
<h2 class="h3 mb-6">Generate an Image</h2>
<div class="relative">
<input id="image-model" type="hidden" value="{{.Model}}">
<form id="genimage" action="text2image/{{.Model}}" method="get" class="mb-8">
<div class="relative">
<div class="absolute inset-y-0 left-0 flex items-center pl-4">
<i class="fas fa-magic text-[#38BDF8]"></i>
<i class="fas fa-magic text-[var(--color-primary)]"></i>
</div>
<input
type="text"
@@ -79,11 +65,11 @@
name="input"
placeholder="Describe the image you want to generate..."
autocomplete="off"
class="form-control block w-full pl-12 pr-12 py-4 text-lg font-normal text-[#E5E7EB] bg-[#101827] bg-clip-padding border border-[#1E293B] rounded-lg transition ease-in-out focus:text-[#E5E7EB] focus:bg-[#101827] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50 focus:outline-none"
class="input w-full pl-12 pr-12 py-4 text-lg"
required
/>
<span id="loader" class="my-2 loader absolute right-4 top-4 hidden">
<svg class="animate-spin h-6 w-6 text-[#38BDF8]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<svg class="animate-spin h-6 w-6 text-[var(--color-primary)]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
@@ -92,15 +78,15 @@
<!-- Size Selection -->
<div class="mt-4">
<label for="image-size" class="block text-sm font-medium text-[#94A3B8] mb-2">
<i class="fas fa-expand-arrows-alt mr-2 text-[#38BDF8]"></i>Image Size:
<label for="image-size" class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
<i class="fas fa-expand-arrows-alt mr-2 text-[var(--color-primary)]"></i>Image Size:
</label>
<input
type="text"
id="image-size"
value="256x256"
placeholder="e.g., 256x256, 512x512, 1024x1024"
class="bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50 rounded-lg shadow-sm p-2.5 w-full max-w-xs transition-colors duration-200"
class="input p-2.5 w-full max-w-xs"
/>
</div>
@@ -108,7 +94,7 @@
<div class="mt-6">
<button
type="submit"
class="w-full bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] font-semibold py-3 px-6 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-[#38BDF8] focus:ring-opacity-50"
class="btn-primary w-full"
>
<i class="fas fa-magic mr-2"></i>Generate Image
</button>
@@ -117,10 +103,10 @@
<!-- Image Results Container -->
<div class="mt-6 border-t border-[#1E293B] pt-6">
<h3 class="text-xl font-semibold text-[#E5E7EB] mb-4">Generated Image</h3>
<h3 class="text-xl font-semibold text-[var(--color-text-primary)] mb-4">Generated Image</h3>
<div class="container mx-auto flex justify-center">
<div id="result" class="mx-auto bg-[#101827]/50 border border-[#1E293B] rounded-xl p-4 min-h-[300px] w-full flex items-center justify-center">
<p class="text-[#94A3B8] italic">Your generated image will appear here</p>
<div id="result" class="mx-auto bg-[var(--color-bg-primary)]/50 border border-[#1E293B] rounded-xl p-4 min-h-[300px] w-full flex items-center justify-center">
<p class="text-[var(--color-text-secondary)] italic">Your generated image will appear here</p>
</div>
</div>
</div>

View File

@@ -9,27 +9,18 @@
{{template "views/partials/navbar" .}}
<div class="container mx-auto px-4 py-8 flex-grow">
<!-- Hero Section -->
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl p-8 mb-10">
<div class="max-w-4xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl font-bold text-[#E5E7EB] mb-4">
<span class="bg-clip-text text-transparent bg-gradient-to-r from-[#8B5CF6] to-[#38BDF8]">
<i class="fas fa-volume-high mr-2"></i>Text to Speech {{ if .Model }} with {{.Model}} {{ end }}
</span>
<div class="hero-section">
<div class="hero-content">
<h1 class="hero-title">
<i class="fas fa-volume-high mr-2"></i>Text to Speech {{ if .Model }} with {{.Model}} {{ end }}
</h1>
<p class="text-xl text-[#94A3B8] mb-6">Convert your text into natural-sounding speech</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="https://localai.io/features/text-to-audio/" target="_blank"
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white font-semibold py-2 px-6 rounded-lg transition-colors">
<i class="fas fa-book-reader mr-2"></i>
<span>Documentation</span>
</a>
</div>
<p class="hero-subtitle">Convert your text into natural-sounding speech</p>
</div>
</div>
<!-- TTS Interface -->
<div class="max-w-3xl mx-auto">
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl overflow-hidden">
<div class="card overflow-hidden">
<!-- Header with Model Selection -->
<div class="border-b border-[#1E293B] p-5">
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
@@ -42,7 +33,7 @@
id="model-select"
x-model="link"
@change="window.location = link"
class="bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#8B5CF6] focus:ring-2 focus:ring-[#8B5CF6]/50 rounded-lg shadow-sm p-2.5 appearance-none"
class="input p-2.5"
>
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
{{ $model:=.Model}}
@@ -83,10 +74,10 @@
name="input"
placeholder="Enter text to convert to speech..."
autocomplete="off"
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#8B5CF6] focus:ring-2 focus:ring-[#8B5CF6]/50 rounded-lg shadow-sm p-4 pl-4 pr-12"
class="input w-full p-4 pl-4 pr-12"
required
/>
<button type="submit" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-[#8B5CF6] hover:text-[#38BDF8] transition">
<button type="submit" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-[#8B5CF6] hover:text-[#38BDF8] transition icon-hover">
<i class="fas fa-paper-plane"></i>
</button>
</div>

View File

@@ -76,4 +76,34 @@
sha: "7f87153bbeb4a7be02520f08d734c004bd09961e2e04cf5ed627173b3ba5f66c"
- filename: "autorefresh.min.js"
url: "https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/addon/display/autorefresh.min.js"
sha: "bdcc82d01c1cb574d7fb8bbf4938213a81131163375d5c869272de3859245216"
sha: "bdcc82d01c1cb574d7fb8bbf4938213a81131163375d5c869272de3859245216"
- filename: "playfair-display-regular.ttf"
url: "https://fonts.gstatic.com/s/playfairdisplay/v40/nuFvD-vYSZviVYUb_rj3ij__anPXJzDwcbmjWBN2PKdFvUDQ.ttf"
sha: "1609a54fa3ad1abc8d0037132f73700333241ea0b65672079998e352f151dea7"
- filename: "playfair-display-semibold.ttf"
url: "https://fonts.gstatic.com/s/playfairdisplay/v40/nuFvD-vYSZviVYUb_rj3ij__anPXJzDwcbmjWBN2PKebukDQ.ttf"
sha: "8ab6a1db2d7caeb46c742371da500d0f3de1dc6ec6bea2aef0106f678d7a1f8d"
- filename: "playfair-display-bold.ttf"
url: "https://fonts.gstatic.com/s/playfairdisplay/v40/nuFvD-vYSZviVYUb_rj3ij__anPXJzDwcbmjWBN2PKeiukDQ.ttf"
sha: "adefc53e7b3d483f1fa5e85edd82b7689ca79db25a1f6786bd7949cdcfeec601"
- filename: "space-grotesk-regular.ttf"
url: "https://fonts.gstatic.com/s/spacegrotesk/v22/V8mQoQDjQSkFtoMM3T6r8E7mF71Q-gOoraIAEj7oUUsj.ttf"
sha: "ec926d5065eaca49a48f96e312bbae0bbc5733c24215cf5dfabbdccee926fef7"
- filename: "space-grotesk-medium.ttf"
url: "https://fonts.gstatic.com/s/spacegrotesk/v22/V8mQoQDjQSkFtoMM3T6r8E7mF71Q-gOoraIAEj7aUUsj.ttf"
sha: "3e699ead1876244fa392243054ddefe7cf631b488438828a8a100731a22ab995"
- filename: "space-grotesk-semibold.ttf"
url: "https://fonts.gstatic.com/s/spacegrotesk/v22/V8mQoQDjQSkFtoMM3T6r8E7mF71Q-gOoraIAEj42Vksj.ttf"
sha: "6c0346b8d297ebdc225832833e03e884a26ad99d265ecd3924d46e1ba285ea87"
- filename: "space-grotesk-bold.ttf"
url: "https://fonts.gstatic.com/s/spacegrotesk/v22/V8mQoQDjQSkFtoMM3T6r8E7mF71Q-gOoraIAEj4PVksj.ttf"
sha: "3e756954468ff1cb302dae0414262e72f76a67d87bef3fa1f3226cd0fb9b2d85"
- filename: "jetbrains-mono-regular.ttf"
url: "https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKxjPQ.ttf"
sha: "44ce4a84f20d60f24539bd0cef11f79c29e38609e0f8adf18551c9794a5d9dc3"
- filename: "jetbrains-mono-medium.ttf"
url: "https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8-qxjPQ.ttf"
sha: "3386a05f6ece969e4537de6be894170d20558e82f7d56c8c5d332972ef172160"
- filename: "jetbrains-mono-semibold.ttf"
url: "https://fonts.gstatic.com/s/jetbrainsmono/v24/tDbY2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8FqtjPQ.ttf"
sha: "df54dbfafba61d4911eb3dab9bba2d20531fb009f01d64dd42fa96ab862584d8"