mirror of
https://github.com/mudler/LocalAI.git
synced 2026-02-18 23:21:58 -05:00
feat(ui): left navbar, dark/light theme (#8594)
* feat(ui): left navbar, dark/light theme Signed-off-by: Ettore Di Giacinto <mudler@localai.io> * darker background Signed-off-by: Ettore Di Giacinto <mudler@localai.io> --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
committed by
GitHub
parent
ed832cf0e0
commit
2fabdc08e6
@@ -418,6 +418,337 @@ textarea.input-success {
|
||||
animation: nodeGlow 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Sidebar Navigation
|
||||
============================================ */
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: var(--sidebar-width);
|
||||
background: var(--sidebar-bg);
|
||||
border-right: 1px solid var(--sidebar-border);
|
||||
box-shadow: var(--shadow-sidebar);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 40;
|
||||
transition: transform var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 1rem 1.25rem;
|
||||
border-bottom: 1px solid var(--color-border-divider);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sidebar-logo img {
|
||||
height: 2rem;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.sidebar-logo-text {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-lg);
|
||||
font-weight: var(--weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.sidebar-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 0.75rem 0;
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.sidebar-section-title {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: var(--weight-medium);
|
||||
color: var(--color-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding: 0.75rem 1.25rem 0.25rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
color: var(--color-text-secondary);
|
||||
text-decoration: none;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
font-weight: var(--weight-normal);
|
||||
transition: all var(--duration-fast) var(--ease-default);
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border-left: 3px solid transparent;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-text-primary);
|
||||
border-left-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--color-primary-light);
|
||||
color: var(--color-primary);
|
||||
font-weight: var(--weight-medium);
|
||||
border-left-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.nav-item i,
|
||||
.nav-item .nav-icon {
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
font-size: var(--text-base);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.nav-item .nav-label {
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.nav-item .nav-chevron {
|
||||
font-size: var(--text-xs);
|
||||
transition: transform var(--duration-fast) var(--ease-default);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.nav-item.expanded .nav-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Dropdown submenu */
|
||||
.nav-submenu {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.nav-submenu.open {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.nav-submenu .nav-item {
|
||||
padding-left: 2.75rem;
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
.nav-submenu .nav-item i,
|
||||
.nav-submenu .nav-item .nav-icon {
|
||||
width: 1rem;
|
||||
font-size: var(--text-xs);
|
||||
}
|
||||
|
||||
/* Sidebar footer with theme toggle */
|
||||
.sidebar-footer {
|
||||
padding: 0.75rem;
|
||||
border-top: 1px solid var(--color-border-divider);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.625rem 0.875rem;
|
||||
margin: 0.125rem 0.5rem;
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--color-bg-secondary);
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
}
|
||||
|
||||
.theme-toggle-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.theme-toggle-label i {
|
||||
font-size: var(--text-sm);
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
width: 2.5rem;
|
||||
height: 1.375rem;
|
||||
background: var(--color-bg-primary);
|
||||
border-radius: var(--radius-full);
|
||||
cursor: pointer;
|
||||
transition: background-color var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
.toggle-switch::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: 1.125rem;
|
||||
height: 1.125rem;
|
||||
background: var(--color-text-secondary);
|
||||
border-radius: var(--radius-full);
|
||||
transition: all var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
.toggle-switch.active {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
.toggle-switch.active::after {
|
||||
transform: translateX(1.125rem);
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* Mobile overlay */
|
||||
.sidebar-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 35;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity var(--duration-normal) var(--ease-default),
|
||||
visibility var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.sidebar-overlay.open {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Mobile menu button */
|
||||
.mobile-menu-btn {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
z-index: 50;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background: var(--color-bg-secondary);
|
||||
border: none;
|
||||
border-radius: var(--radius-full);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all var(--duration-fast) var(--ease-default);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.mobile-menu-btn:hover {
|
||||
background: var(--color-bg-primary);
|
||||
color: var(--color-primary);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.mobile-menu-btn:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Hide menu button when sidebar is open */
|
||||
.mobile-menu-btn[style*="opacity: 0"] {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Mobile close button inside sidebar */
|
||||
.sidebar-close-btn {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
transition: all var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
.sidebar-close-btn:hover {
|
||||
background: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Tables
|
||||
============================================ */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: var(--color-bg-secondary);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
thead {
|
||||
background: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
thead tr {
|
||||
border-bottom: 1px solid var(--color-border-subtle);
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: 0.5rem;
|
||||
font-size: var(--text-xs);
|
||||
font-weight: var(--weight-semibold);
|
||||
color: var(--color-text-primary);
|
||||
border-bottom: 1px solid var(--color-border-subtle);
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
border-bottom: 1px solid var(--color-border-subtle);
|
||||
transition: background-color var(--duration-fast) var(--ease-default);
|
||||
}
|
||||
|
||||
tbody tr:hover {
|
||||
background: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 0.5rem;
|
||||
font-size: var(--text-xs);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* Table container */
|
||||
.table-container {
|
||||
background: var(--color-bg-secondary);
|
||||
border-radius: var(--radius-xl);
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Responsive Adjustments
|
||||
============================================ */
|
||||
@@ -439,3 +770,36 @@ textarea.input-success {
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet and mobile - sidebar becomes overlay */
|
||||
@media (max-width: 1023px) {
|
||||
.mobile-menu-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
z-index: 45;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.sidebar-close-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding-right: 3rem;
|
||||
}
|
||||
|
||||
.sidebar-overlay.open + .sidebar,
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,49 @@
|
||||
/* Layout Structure */
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--font-body, 'Space Grotesk', -apple-system, BlinkMacSystemFont, sans-serif);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100%;
|
||||
background-color: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
transition: background-color var(--duration-normal) var(--ease-default),
|
||||
color var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.app-layout {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
background-color: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
margin-left: var(--sidebar-width);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-bg-primary);
|
||||
transition: margin-left var(--duration-normal) var(--ease-default);
|
||||
}
|
||||
|
||||
.main-content-inner {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
/* Tablet and mobile */
|
||||
@media (max-width: 1023px) {
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-container { height: 90vh; display: flex; flex-direction: column; }
|
||||
.chat-messages { overflow-y: auto; flex-grow: 1; }
|
||||
.htmx-indicator{
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
/* 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 */
|
||||
/* Dark Theme (Default) - Charcoal Gray Style */
|
||||
:root,
|
||||
[data-theme="dark"],
|
||||
.dark {
|
||||
/* Base Colors - Charcoal Gray */
|
||||
--color-bg-primary: #121212; /* Main background */
|
||||
--color-bg-secondary: #1A1A1A; /* Elevated surfaces */
|
||||
--color-bg-tertiary: #222222; /* Cards, panels */
|
||||
--color-bg-overlay: rgba(18, 18, 18, 0.95); /* Modals, overlays */
|
||||
|
||||
/* Override tw-elements dark background */
|
||||
background-color: #121212 !important;
|
||||
|
||||
/* Brand Colors */
|
||||
--color-primary: #38BDF8; /* Cyan - primary actions */
|
||||
@@ -32,16 +38,16 @@
|
||||
--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 */
|
||||
--color-text-inverse: #FFFFFF; /* 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) */
|
||||
/* Border Colors - Visible on charcoal */
|
||||
--color-border-subtle: rgba(255, 255, 255, 0.08); /* Minimal borders */
|
||||
--color-border-default: rgba(255, 255, 255, 0.12); /* Default borders */
|
||||
--color-border-strong: rgba(56, 189, 248, 0.3); /* Focus borders */
|
||||
--color-border-divider: rgba(255, 255, 255, 0.05); /* Section dividers */
|
||||
--color-border-primary: rgba(56, 189, 248, 0.2); /* Primary borders */
|
||||
--color-border-secondary: rgba(255, 255, 255, 0.1);
|
||||
--color-border-focus: rgba(56, 189, 248, 0.4); /* Focus borders */
|
||||
|
||||
/* Status Colors */
|
||||
--color-success: #14B8A6; /* Use teal for success (aligned with logo) */
|
||||
@@ -55,17 +61,18 @@
|
||||
|
||||
/* 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-hero: linear-gradient(135deg, #121212 0%, #1A1A1A 50%, #121212 100%);
|
||||
--gradient-card: linear-gradient(135deg, rgba(56, 189, 248, 0.04) 0%, rgba(139, 92, 246, 0.04) 100%);
|
||||
--gradient-text: linear-gradient(135deg, #38BDF8 0%, #8B5CF6 50%, #14B8A6 100%);
|
||||
|
||||
/* Shadows - Minimal System */
|
||||
/* Shadows - Charcoal theme */
|
||||
--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 */
|
||||
--shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.25);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.35);
|
||||
--shadow-glow: 0 0 0 1px rgba(56, 189, 248, 0.15), 0 0 12px rgba(56, 189, 248, 0.2);
|
||||
--shadow-sidebar: 1px 0 3px rgba(0, 0, 0, 0.25);
|
||||
|
||||
/* Animation Timing - Minimal */
|
||||
--duration-instant: 100ms;
|
||||
@@ -109,5 +116,83 @@
|
||||
--width-5xl: 64rem; /* 1024px */
|
||||
--width-6xl: 72rem; /* 1152px */
|
||||
--width-7xl: 80rem; /* 1280px */
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar-width: 220px;
|
||||
--sidebar-bg: var(--color-bg-primary);
|
||||
--sidebar-border: var(--color-border-subtle);
|
||||
}
|
||||
|
||||
/* Light Theme */
|
||||
[data-theme="light"] {
|
||||
/* Base Colors */
|
||||
--color-bg-primary: #F8FAFC; /* Soft gray background */
|
||||
--color-bg-secondary: #FFFFFF; /* Elevated surfaces */
|
||||
--color-bg-tertiary: #FFFFFF; /* Cards, panels */
|
||||
--color-bg-overlay: rgba(248, 250, 252, 0.9); /* Modals, overlays */
|
||||
|
||||
/* Brand Colors - Slightly adjusted for light backgrounds */
|
||||
--color-primary: #0EA5E9; /* Slightly darker cyan for better contrast */
|
||||
--color-primary-hover: #0284C7; /* Darker on hover */
|
||||
--color-primary-active: #0369A1; /* Active state */
|
||||
--color-primary-text: #FFFFFF; /* Text on primary background */
|
||||
--color-primary-light: rgba(14, 165, 233, 0.08); /* Light cyan backgrounds */
|
||||
--color-primary-border: rgba(14, 165, 233, 0.2); /* Cyan borders */
|
||||
|
||||
--color-secondary: #0D9488; /* Teal - secondary actions */
|
||||
--color-secondary-hover: #0F766E; /* Darker teal on hover */
|
||||
--color-secondary-light: rgba(13, 148, 136, 0.1);
|
||||
|
||||
--color-accent: #7C3AED; /* Purple - special states */
|
||||
--color-accent-hover: #6D28D9; /* Darker purple on hover */
|
||||
--color-accent-light: rgba(124, 58, 237, 0.1);
|
||||
|
||||
--color-accent-purple: #A78BFA; /* Light purple for gradients */
|
||||
--color-accent-teal: #2DD4BF; /* Light teal for gradients */
|
||||
|
||||
/* Text Colors */
|
||||
--color-text-primary: #1E293B; /* Primary text - dark slate */
|
||||
--color-text-secondary: #64748B; /* Secondary text */
|
||||
--color-text-muted: #94A3B8; /* Tertiary text */
|
||||
--color-text-disabled: #CBD5E1; /* Disabled text */
|
||||
--color-text-inverse: #FFFFFF; /* Text on dark backgrounds */
|
||||
|
||||
/* Border Colors */
|
||||
--color-border-subtle: rgba(15, 23, 42, 0.06); /* Minimal borders */
|
||||
--color-border-default: rgba(15, 23, 42, 0.1); /* Default borders */
|
||||
--color-border-strong: rgba(14, 165, 233, 0.3); /* Focus borders */
|
||||
--color-border-divider: rgba(15, 23, 42, 0.04); /* Section dividers */
|
||||
--color-border-primary: rgba(14, 165, 233, 0.2); /* Primary borders */
|
||||
--color-border-secondary: rgba(15, 23, 42, 0.08);
|
||||
--color-border-focus: rgba(14, 165, 233, 0.4); /* Focus borders */
|
||||
|
||||
/* Status Colors - Adjusted for light theme */
|
||||
--color-success: #0D9488;
|
||||
--color-success-light: rgba(13, 148, 136, 0.1);
|
||||
--color-warning: #D97706;
|
||||
--color-warning-light: rgba(217, 119, 6, 0.1);
|
||||
--color-error: #DC2626;
|
||||
--color-error-light: rgba(220, 38, 38, 0.1);
|
||||
--color-info: #0EA5E9;
|
||||
--color-info-light: rgba(14, 165, 233, 0.1);
|
||||
|
||||
/* Gradient Definitions */
|
||||
--gradient-primary: linear-gradient(135deg, #0EA5E9 0%, #7C3AED 50%, #0D9488 100%);
|
||||
--gradient-hero: linear-gradient(135deg, #F8FAFC 0%, #FFFFFF 50%, #F8FAFC 100%);
|
||||
--gradient-card: linear-gradient(135deg, rgba(14, 165, 233, 0.03) 0%, rgba(124, 58, 237, 0.03) 100%);
|
||||
--gradient-text: linear-gradient(135deg, #0EA5E9 0%, #7C3AED 50%, #0D9488 100%);
|
||||
|
||||
/* Shadows - More visible in light theme */
|
||||
--shadow-none: none;
|
||||
--shadow-subtle: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
|
||||
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.08);
|
||||
--shadow-glow: 0 0 0 1px rgba(14, 165, 233, 0.15), 0 0 8px rgba(14, 165, 233, 0.2);
|
||||
--shadow-sidebar: 1px 0 3px rgba(0, 0, 0, 0.08);
|
||||
|
||||
/* Sidebar */
|
||||
--sidebar-bg: #FFFFFF;
|
||||
--sidebar-border: rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,30 +2,31 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Error Section -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-10">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-border-subtle)] rounded-xl p-8 mb-10">
|
||||
<div class="max-w-4xl mx-auto text-center">
|
||||
<div class="mb-6 text-6xl text-[#38BDF8]">
|
||||
<div class="mb-6 text-6xl text-[var(--color-primary)]">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
</div>
|
||||
<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>
|
||||
<p class="text-xl text-[var(--color-text-secondary)] 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">
|
||||
<a href="./"
|
||||
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] font-semibold py-3 px-6 rounded-lg transition-colors">
|
||||
class="btn-primary">
|
||||
<i class="fas fa-home mr-2"></i>
|
||||
<span>Return Home</span>
|
||||
</a>
|
||||
<a href="browse/"
|
||||
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white font-semibold py-3 px-6 rounded-lg transition-colors">
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-images mr-2"></i>
|
||||
<span>Browse Gallery</span>
|
||||
</a>
|
||||
@@ -33,19 +34,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional Information -->
|
||||
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl p-8">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-border-subtle)] rounded-xl p-8">
|
||||
<div class="text-center max-w-3xl 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-4">
|
||||
<i class="text-yellow-400 text-2xl fa-solid fa-triangle-exclamation"></i>
|
||||
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[var(--color-warning-light)] border border-[var(--color-warning)]/20 mb-4">
|
||||
<i class="text-[var(--color-warning)] text-2xl fa-solid fa-triangle-exclamation"></i>
|
||||
</div>
|
||||
<h2 class="text-2xl md:text-3xl font-semibold text-[#E5E7EB] mb-4">Looking for resources?</h2>
|
||||
<p class="text-lg text-[#94A3B8] mb-6">Visit our <a class="text-[#38BDF8] hover:text-[#8B5CF6] underline underline-offset-2 transition-colors" href="browse">🖼️ Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-[#38BDF8] hover:text-[#8B5CF6] underline underline-offset-2 transition-colors"> <i class="fa-solid fa-book"></i> Getting started documentation</a></p>
|
||||
<h2 class="text-2xl md:text-3xl font-semibold text-[var(--color-text-primary)] mb-4">Looking for resources?</h2>
|
||||
<p class="text-lg text-[var(--color-text-secondary)] mb-6">Visit our <a class="text-[var(--color-primary)] hover:text-[var(--color-accent)] underline underline-offset-2 transition-colors" href="browse">Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-[var(--color-primary)] hover:text-[var(--color-accent)] underline underline-offset-2 transition-colors">Getting started documentation</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen" x-data="jobDetails()" x-init="init()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="jobDetails()" x-init="init()">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow max-w-6xl">
|
||||
<!-- Header -->
|
||||
@@ -17,7 +19,7 @@
|
||||
</h1>
|
||||
<p class="hero-subtitle">Live job status, reasoning traces, and execution details</p>
|
||||
</div>
|
||||
<a href="/agent-jobs" class="text-[#94A3B8] hover:text-[#E5E7EB]">
|
||||
<a href="/agent-jobs" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]">
|
||||
<i class="fas fa-arrow-left mr-2"></i>Back to Jobs
|
||||
</a>
|
||||
</div>
|
||||
@@ -26,7 +28,7 @@
|
||||
<!-- Job Status Card -->
|
||||
<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>
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)]">Job Status</h2>
|
||||
<div class="flex items-center space-x-4">
|
||||
<span :class="{
|
||||
'bg-yellow-500': job.status === 'pending',
|
||||
@@ -48,64 +50,64 @@
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Job ID</label>
|
||||
<div class="font-mono text-[#E5E7EB] mt-1" x-text="job.id || '-'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Job ID</label>
|
||||
<div class="font-mono text-[var(--color-text-primary)] mt-1" x-text="job.id || '-'"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Task</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="task ? task.name : (job.task_id || '-')"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Task</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="task ? task.name : (job.task_id || '-')"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Created</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="formatDate(job.created_at)"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Created</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="formatDate(job.created_at)"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Started</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="formatDate(job.started_at)"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Started</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="formatDate(job.started_at)"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Completed</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="formatDate(job.completed_at)"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Completed</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="formatDate(job.completed_at)"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Triggered By</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="job.triggered_by || '-'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Triggered By</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="job.triggered_by || '-'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agent Prompt Template -->
|
||||
<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>
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Agent Prompt Template</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">The original prompt template from the task definition.</p>
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] whitespace-pre-wrap font-mono text-sm" x-text="task.prompt"></div>
|
||||
</div>
|
||||
|
||||
<!-- Cron Parameters -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="job.triggered_by === 'cron' && task && task.cron_parameters && Object.keys(task.cron_parameters).length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Cron Parameters</h2>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">Parameters configured for cron-triggered executions of this task.</p>
|
||||
<pre class="bg-[#101827] p-4 rounded text-[#E5E7EB] text-sm overflow-x-auto" x-text="JSON.stringify(task.cron_parameters, null, 2)"></pre>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="job.triggered_by === 'cron' && task && task.cron_parameters && Object.keys(task.cron_parameters).length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Cron Parameters</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">Parameters configured for cron-triggered executions of this task.</p>
|
||||
<pre class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] text-sm overflow-x-auto" x-text="JSON.stringify(task.cron_parameters, null, 2)"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Parameters -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="job.parameters && Object.keys(job.parameters).length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Job Parameters</h2>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">Parameters used for this specific job execution.</p>
|
||||
<pre class="bg-[#101827] p-4 rounded text-[#E5E7EB] text-sm overflow-x-auto" x-text="JSON.stringify(job.parameters, null, 2)"></pre>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="job.parameters && Object.keys(job.parameters).length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Job Parameters</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">Parameters used for this specific job execution.</p>
|
||||
<pre class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] text-sm overflow-x-auto" x-text="JSON.stringify(job.parameters, null, 2)"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Rendered Job 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>
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Rendered Job Prompt</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">The prompt with parameters substituted, as it was sent to the agent.</p>
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] whitespace-pre-wrap" x-text="getRenderedPrompt()"></div>
|
||||
</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="job.result">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Result</h2>
|
||||
<div class="bg-[#101827] p-4 rounded text-[#E5E7EB] whitespace-pre-wrap" x-text="job.result"></div>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="job.result">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Result</h2>
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] whitespace-pre-wrap" x-text="job.result"></div>
|
||||
</div>
|
||||
|
||||
<!-- Error -->
|
||||
@@ -115,18 +117,18 @@
|
||||
</div>
|
||||
|
||||
<!-- Reasoning Traces & Actions -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Execution Traces</h2>
|
||||
<div x-show="!traces || traces.length === 0" class="text-[#94A3B8] text-center py-8">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Execution Traces</h2>
|
||||
<div x-show="!traces || traces.length === 0" class="text-[var(--color-text-secondary)] text-center py-8">
|
||||
<i class="fas fa-info-circle text-2xl mb-2"></i>
|
||||
<p>No execution traces available yet. Traces will appear here as the job executes.</p>
|
||||
</div>
|
||||
<div x-show="traces && traces.length > 0" class="space-y-4">
|
||||
<template x-for="(trace, index) in traces" :key="index">
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4">
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="text-xs text-[#94A3B8] font-mono" x-text="'Step ' + (index + 1)"></span>
|
||||
<span class="text-xs text-[var(--color-text-secondary)] font-mono" x-text="'Step ' + (index + 1)"></span>
|
||||
<span class="text-xs px-2 py-1 rounded"
|
||||
:class="{
|
||||
'bg-blue-500/20 text-blue-400': trace.type === 'reasoning',
|
||||
@@ -136,14 +138,14 @@
|
||||
}"
|
||||
x-text="trace.type"></span>
|
||||
</div>
|
||||
<span class="text-xs text-[#94A3B8]" x-text="formatTime(trace.timestamp)"></span>
|
||||
<span class="text-xs text-[var(--color-text-secondary)]" x-text="formatTime(trace.timestamp)"></span>
|
||||
</div>
|
||||
<div class="text-[#E5E7EB] text-sm" x-text="trace.content"></div>
|
||||
<div x-show="trace.tool_name" class="mt-2 text-xs text-[#94A3B8]">
|
||||
<div class="text-[var(--color-text-primary)] text-sm" x-text="trace.content"></div>
|
||||
<div x-show="trace.tool_name" class="mt-2 text-xs text-[var(--color-text-secondary)]">
|
||||
<span class="font-semibold">Tool:</span> <span x-text="trace.tool_name"></span>
|
||||
</div>
|
||||
<div x-show="trace.arguments" class="mt-2">
|
||||
<pre class="text-xs text-[#94A3B8] bg-[#0A0E1A] p-2 rounded overflow-x-auto" x-text="JSON.stringify(trace.arguments, null, 2)"></pre>
|
||||
<pre class="text-xs text-[var(--color-text-secondary)] bg-[var(--color-bg-tertiary)] p-2 rounded overflow-x-auto" x-text="JSON.stringify(trace.arguments, null, 2)"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -151,16 +153,16 @@
|
||||
</div>
|
||||
|
||||
<!-- Webhook Status -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="job.webhook_sent !== undefined || job.webhook_error">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Webhook Status</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="job.webhook_sent !== undefined || job.webhook_error">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Webhook Status</h2>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span :class="job.webhook_sent && !job.webhook_error ? 'text-green-400' : (job.webhook_error ? 'text-yellow-400' : 'text-gray-400')">
|
||||
<span :class="job.webhook_sent && !job.webhook_error ? 'text-[var(--color-success)]' : (job.webhook_error ? 'text-[var(--color-warning)]' : 'text-[var(--color-text-muted)]')">
|
||||
<i class="fas" :class="job.webhook_sent && !job.webhook_error ? 'fa-check-circle' : (job.webhook_error ? 'fa-exclamation-triangle' : 'fa-clock')"></i>
|
||||
</span>
|
||||
<span class="text-[#E5E7EB]"
|
||||
<span class="text-[var(--color-text-primary)]"
|
||||
x-text="job.webhook_sent && !job.webhook_error ? 'All webhooks sent successfully' : (job.webhook_error ? 'Webhook delivery had errors' : 'Webhook pending')"></span>
|
||||
<span x-show="job.webhook_sent_at" class="text-[#94A3B8] text-sm" x-text="'at ' + formatDate(job.webhook_sent_at)"></span>
|
||||
<span x-show="job.webhook_sent_at" class="text-[var(--color-text-secondary)] text-sm" x-text="'at ' + formatDate(job.webhook_sent_at)"></span>
|
||||
</div>
|
||||
<div x-show="job.webhook_error" class="bg-red-900/20 border border-red-500/20 rounded-lg p-4">
|
||||
<div class="flex items-start space-x-2">
|
||||
@@ -320,7 +322,9 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<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="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="agentJobs()" x-init="init()">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Header -->
|
||||
@@ -140,13 +142,13 @@
|
||||
</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-[var(--color-primary-border)]/10">
|
||||
<div class="flex items-center justify-between p-3 bg-[var(--color-bg-secondary)] rounded-lg border border-[var(--color-border-subtle)]">
|
||||
<div class="flex items-center space-x-3">
|
||||
<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">
|
||||
class="inline-flex items-center bg-[var(--color-warning)] hover:bg-[var(--color-warning)]/80 text-white px-4 py-2 rounded-lg transition-colors text-sm">
|
||||
<i class="fas fa-edit mr-2"></i>
|
||||
Configure MCP
|
||||
</a>
|
||||
@@ -759,7 +761,9 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
</html>
|
||||
@@ -2,20 +2,22 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen" x-data="taskDetails()" x-init="init()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="taskDetails()" x-init="init()">
|
||||
|
||||
<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="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<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>
|
||||
<p class="text-lg text-[var(--color-text-secondary)]" x-text="isNewTask ? 'Create a new agent task' : (task ? task.name : 'Loading...')"></p>
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<template x-if="!isNewTask && !isEditMode">
|
||||
@@ -37,7 +39,7 @@
|
||||
<template x-if="isEditMode || isNewTask">
|
||||
<div class="flex space-x-3">
|
||||
<button @click="cancelEdit()"
|
||||
class="bg-[#1E293B] hover:bg-[#2D3A4F] text-white px-4 py-2 rounded-lg transition-colors">
|
||||
class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-white px-4 py-2 rounded-lg transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button @click="saveTask()"
|
||||
@@ -46,7 +48,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<a href="/agent-jobs" class="text-[#94A3B8] hover:text-[#E5E7EB] px-4 py-2">
|
||||
<a href="/agent-jobs" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-4 py-2">
|
||||
<i class="fas fa-arrow-left mr-2"></i>Back
|
||||
</a>
|
||||
</div>
|
||||
@@ -57,62 +59,62 @@
|
||||
<template x-if="isEditMode || isNewTask">
|
||||
<form @submit.prevent="saveTask()" class="space-y-8">
|
||||
<!-- Basic Information -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Basic Information</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Basic Information</h2>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Name *</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Name *</label>
|
||||
<input type="text" x-model="taskForm.name" required
|
||||
class="w-full bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Description</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Description</label>
|
||||
<textarea x-model="taskForm.description" rows="3"
|
||||
class="w-full bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50"></textarea>
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Model *</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Model *</label>
|
||||
<select x-model="taskForm.model" required
|
||||
class="w-full bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
<option value="">Select a model with MCP configuration...</option>
|
||||
{{ range .ModelsConfig }}
|
||||
{{ $cfg := . }}
|
||||
{{ $hasMCP := or (ne $cfg.MCP.Servers "") (ne $cfg.MCP.Stdio "") }}
|
||||
{{ if $hasMCP }}
|
||||
<option value="{{$cfg.Name}}" class="bg-[#1E293B] text-[#E5E7EB]">{{$cfg.Name}}</option>
|
||||
<option value="{{$cfg.Name}}" class="bg-[var(--color-bg-secondary)] text-[var(--color-text-primary)]">{{$cfg.Name}}</option>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</select>
|
||||
<p class="text-sm text-[#94A3B8] mt-1">Only models with MCP configuration are shown</p>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mt-1">Only models with MCP configuration are shown</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="flex items-center">
|
||||
<input type="checkbox" x-model="taskForm.enabled"
|
||||
class="mr-2">
|
||||
<span class="text-[#E5E7EB]">Enabled</span>
|
||||
<span class="text-[var(--color-text-primary)]">Enabled</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Prompt Template -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Prompt Template</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Prompt Template</h2>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Prompt *</label>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">
|
||||
Use Go template syntax with <code class="bg-[#101827] px-1.5 py-0.5 rounded text-[#38BDF8]">{{"{{"}}.param{{"}}"}}</code> for dynamic parameters.
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Prompt *</label>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
|
||||
Use Go template syntax with <code class="bg-[var(--color-bg-primary)] px-1.5 py-0.5 rounded text-[var(--color-primary)]">{{"{{"}}.param{{"}}"}}</code> for dynamic parameters.
|
||||
Parameters are provided when executing the job and will be substituted into the prompt.
|
||||
</p>
|
||||
|
||||
<!-- Example Prompt -->
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4 mb-4">
|
||||
<p class="text-xs text-[#94A3B8] mb-2 font-semibold">Example Prompt:</p>
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono whitespace-pre-wrap">You are a helpful assistant. The user's name is {{"{{"}}.user_name{{"}}"}} and they work as a {{"{{"}}.job_title{{"}}"}}.
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4 mb-4">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mb-2 font-semibold">Example Prompt:</p>
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono whitespace-pre-wrap">You are a helpful assistant. The user's name is {{"{{"}}.user_name{{"}}"}} and they work as a {{"{{"}}.job_title{{"}}"}}.
|
||||
|
||||
Please help them with the following task: {{"{{"}}.task_description{{"}}"}}
|
||||
|
||||
@@ -121,8 +123,8 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
|
||||
<textarea x-model="taskForm.prompt" required rows="12"
|
||||
placeholder="Enter your prompt template here. Use {{.parameter_name}} to reference parameters that will be provided when the job executes."
|
||||
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-2">
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-2">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
The prompt will be processed as a Go template. All parameters passed during job execution will be available as template variables.
|
||||
</p>
|
||||
@@ -130,25 +132,25 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
|
||||
<!-- Cron Schedule -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Cron Schedule (Optional)</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Cron Schedule (Optional)</h2>
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Cron Expression</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Cron Expression</label>
|
||||
<input type="text"
|
||||
x-model="taskForm.cron"
|
||||
@blur="validateCron(taskForm.cron)"
|
||||
@input="cronError = ''"
|
||||
placeholder="0 0 * * * (daily at midnight)"
|
||||
:class="cronError ? 'w-full bg-[#101827] border border-red-500 rounded px-4 py-2 text-[#E5E7EB] focus:border-red-500 focus:ring-2 focus:ring-red-500/50' : 'w-full bg-[#101827] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50'">
|
||||
<p class="text-sm text-[#94A3B8] mt-1">Standard 5-field cron format (minute hour day month weekday)</p>
|
||||
:class="cronError ? 'w-full bg-[var(--color-bg-primary)] border border-red-500 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-red-500 focus:ring-2 focus:ring-red-500/50' : 'w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50'">
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mt-1">Standard 5-field cron format (minute hour day month weekday)</p>
|
||||
<p x-show="cronError" class="text-sm text-red-400 mt-2" x-text="cronError"></p>
|
||||
</div>
|
||||
|
||||
<!-- Cron Parameters -->
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Cron Parameters (Optional)</label>
|
||||
<p class="text-sm text-[#94A3B8] mb-3">
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Cron Parameters (Optional)</label>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-3">
|
||||
Parameters to use when executing jobs triggered by cron. These will be used to template the prompt.
|
||||
Enter as key-value pairs (one per line, format: key=value).
|
||||
</p>
|
||||
@@ -156,27 +158,27 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
@input="updateCronParameters()"
|
||||
rows="6"
|
||||
placeholder="user_name=Alice job_title=Software Engineer task_description=Daily status report"
|
||||
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">
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-1">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
Example: <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">user_name=Alice</code>
|
||||
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>
|
||||
</div>
|
||||
|
||||
<!-- Multimedia Sources Configuration -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Multimedia Sources (Optional)</h2>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Multimedia Sources (Optional)</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
|
||||
Configure multimedia sources (images, videos, audios, files) to fetch when cron jobs execute.
|
||||
Each source can have custom headers for authentication/authorization. These will be fetched and included in the job execution.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<template x-for="(source, index) in taskForm.multimedia_sources" :key="index">
|
||||
<div class="bg-[#101827] p-4 rounded border border-[#38BDF8]/10">
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded border border-[var(--color-primary)]/10">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB]">Multimedia Source <span x-text="index + 1"></span></h3>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Multimedia Source <span x-text="index + 1"></span></h3>
|
||||
<button type="button" @click="taskForm.multimedia_sources.splice(index, 1)"
|
||||
class="text-red-400 hover:text-red-300">
|
||||
<i class="fas fa-trash"></i>
|
||||
@@ -184,9 +186,9 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Type *</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Type *</label>
|
||||
<select x-model="source.type" required
|
||||
class="w-full bg-[#0A0E1A] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
<option value="">Select type...</option>
|
||||
<option value="image">Image</option>
|
||||
<option value="video">Video</option>
|
||||
@@ -195,40 +197,40 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">URL *</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">URL *</label>
|
||||
<input type="url" x-model="source.url" required
|
||||
placeholder="https://example.com/image.png"
|
||||
class="w-full bg-[#0A0E1A] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
<p class="text-xs text-[#94A3B8] mt-1">URL where multimedia content will be fetched from</p>
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-1">URL where multimedia content will be fetched from</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Headers (JSON)</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Headers (JSON)</label>
|
||||
<textarea x-model="source.headers_json" rows="3"
|
||||
placeholder='{"Authorization": "Bearer token"}'
|
||||
class="w-full bg-[#0A0E1A] 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">Custom headers for the HTTP request (e.g., Authorization)</p>
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Custom headers for the HTTP request (e.g., Authorization)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<button type="button" @click="addMultimediaSource()"
|
||||
class="w-full bg-[#101827] hover:bg-[#0A0E1A] border border-[#38BDF8]/20 border-dashed rounded-lg p-4 text-[#94A3B8] hover:text-[#E5E7EB] transition-colors">
|
||||
class="w-full bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 border-dashed rounded-lg p-4 text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors">
|
||||
<i class="fas fa-plus mr-2"></i>Add Multimedia Source
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Webhook Configuration -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Webhooks (Optional)</h2>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Webhooks (Optional)</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
|
||||
Configure webhook URLs to receive notifications when jobs complete. You can add multiple webhooks, each with custom headers and HTTP methods.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
<template x-for="(webhook, index) in taskForm.webhooks" :key="index">
|
||||
<div class="bg-[#101827] p-4 rounded border border-[#38BDF8]/10">
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded border border-[var(--color-primary)]/10">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB]">Webhook <span x-text="index + 1"></span></h3>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Webhook <span x-text="index + 1"></span></h3>
|
||||
<button type="button" @click="taskForm.webhooks.splice(index, 1)"
|
||||
class="text-red-400 hover:text-red-300">
|
||||
<i class="fas fa-trash"></i>
|
||||
@@ -236,35 +238,35 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">URL *</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">URL *</label>
|
||||
<input type="url" x-model="webhook.url" required
|
||||
placeholder="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
|
||||
class="w-full bg-[#0A0E1A] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
<p class="text-xs text-[#94A3B8] mt-1">URL where webhook notifications will be sent</p>
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-1">URL where webhook notifications will be sent</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">HTTP Method</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">HTTP Method</label>
|
||||
<select x-model="webhook.method"
|
||||
class="w-full bg-[#0A0E1A] border border-[#38BDF8]/20 rounded px-4 py-2 text-[#E5E7EB] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50">
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50">
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="PATCH">PATCH</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Headers (JSON)</label>
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Headers (JSON)</label>
|
||||
<textarea x-model="webhook.headers_json" rows="3"
|
||||
placeholder='{"Authorization": "Bearer token", "Content-Type": "application/json"}'
|
||||
class="w-full bg-[#0A0E1A] 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">Custom headers for the webhook request (e.g., Authorization)</p>
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-1">Custom headers for the webhook request (e.g., Authorization)</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-[#E5E7EB] mb-2">Custom Payload Template (Optional)</label>
|
||||
<p class="text-xs text-[#94A3B8] mb-2">Customize the webhook payload using Go template syntax. Available variables: <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Job</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Task</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Result</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Error</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Status</code></p>
|
||||
<p class="text-xs text-[#94A3B8] mb-2">Note: <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">.Error</code> will be empty string if job succeeded, or contain the error message if it failed. Use this to handle both success and failure cases in a single webhook.</p>
|
||||
<div class="bg-[#0A0E1A] border border-[#38BDF8]/10 rounded-lg p-3 mb-2">
|
||||
<p class="text-xs text-[#94A3B8] mb-1 font-semibold">Example (Slack with error handling):</p>
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono whitespace-pre-wrap">{
|
||||
<label class="block text-[var(--color-text-primary)] mb-2">Custom Payload Template (Optional)</label>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Customize the webhook payload using Go template syntax. Available variables: <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Job</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Task</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Result</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Error</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Status</code></p>
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mb-2">Note: <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">.Error</code> will be empty string if job succeeded, or contain the error message if it failed. Use this to handle both success and failure cases in a single webhook.</p>
|
||||
<div class="bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/10 rounded-lg p-3 mb-2">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mb-1 font-semibold">Example (Slack with error handling):</p>
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono whitespace-pre-wrap">{
|
||||
"text": "Job {{.Job.ID}} {{if .Error}}failed{{else}}completed{{end}}",
|
||||
"blocks": [
|
||||
{
|
||||
@@ -279,13 +281,13 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
<textarea x-model="webhook.payload_template" rows="5"
|
||||
placeholder='{"text": "Job {{.Job.ID}} completed with status {{.Status}}", "error": "{{.Error}}"}'
|
||||
class="w-full bg-[#0A0E1A] 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>
|
||||
class="w-full bg-[var(--color-bg-tertiary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<button type="button" @click="addWebhook()"
|
||||
class="w-full bg-[#101827] border border-[#38BDF8]/20 hover:border-[#38BDF8]/40 rounded px-4 py-3 text-[#38BDF8] transition-colors">
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 hover:border-[var(--color-primary)]/40 rounded px-4 py-3 text-[var(--color-primary)] transition-colors">
|
||||
<i class="fas fa-plus mr-2"></i>Add Webhook
|
||||
</button>
|
||||
</div>
|
||||
@@ -297,15 +299,15 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
<!-- Task Information (always visible when not in edit mode and not creating new task) -->
|
||||
<div x-show="!isEditMode && !isNewTask" x-cloak>
|
||||
<!-- Task Information -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Task Information</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Task Information</h2>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Name</label>
|
||||
<div class="text-[#E5E7EB] mt-1 font-semibold" x-text="task ? task.name : 'Loading...'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Name</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1 font-semibold" x-text="task ? task.name : 'Loading...'"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Status</label>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Status</label>
|
||||
<div class="mt-1">
|
||||
<span :class="task && task.enabled ? 'bg-green-500' : 'bg-gray-500'"
|
||||
class="px-2 py-1 rounded text-xs text-white"
|
||||
@@ -313,10 +315,10 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Model</label>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Model</label>
|
||||
<div class="mt-1 flex items-center space-x-2">
|
||||
<a :href="task ? '/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 ? task.model : '-'"></a>
|
||||
<a :href="task ? '/models/edit/' + task.model : '#'"
|
||||
class="text-yellow-400 hover:text-yellow-300"
|
||||
@@ -326,47 +328,47 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Cron Schedule</label>
|
||||
<div class="text-[#E5E7EB] mt-1 font-mono text-sm" x-text="task && task.cron ? task.cron : '-'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Cron Schedule</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1 font-mono text-sm" x-text="task && task.cron ? task.cron : '-'"></div>
|
||||
</div>
|
||||
<div class="md:col-span-2" x-show="task && task.cron_parameters && Object.keys(task.cron_parameters).length > 0">
|
||||
<label class="text-[#94A3B8] text-sm">Cron Parameters</label>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Cron Parameters</label>
|
||||
<div class="mt-1">
|
||||
<template x-for="(value, key) in task.cron_parameters" :key="key">
|
||||
<div class="text-[#E5E7EB] text-sm mb-1">
|
||||
<span class="font-semibold text-[#38BDF8]" x-text="key + ':'"></span>
|
||||
<div class="text-[var(--color-text-primary)] text-sm mb-1">
|
||||
<span class="font-semibold text-[var(--color-primary)]" x-text="key + ':'"></span>
|
||||
<span x-text="value"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[#94A3B8] text-sm">Description</label>
|
||||
<div class="text-[#E5E7EB] mt-1" x-text="task && task.description ? task.description : 'No description'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Description</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1" x-text="task && task.description ? task.description : 'No description'"></div>
|
||||
</div>
|
||||
<div class="md:col-span-2">
|
||||
<label class="text-[#94A3B8] text-sm">Prompt Template</label>
|
||||
<pre class="bg-[#101827] p-4 rounded text-[#E5E7EB] text-sm mt-1 whitespace-pre-wrap" x-text="task ? task.prompt : '-'"></pre>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Prompt Template</label>
|
||||
<pre class="bg-[var(--color-bg-primary)] p-4 rounded text-[var(--color-text-primary)] text-sm mt-1 whitespace-pre-wrap" x-text="task ? task.prompt : '-'"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- API Usage Examples -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="task && task.id">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">API Usage Examples</h2>
|
||||
<p class="text-sm text-[#94A3B8] mb-4">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="task && task.id">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">API Usage Examples</h2>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mb-4">
|
||||
Use these curl commands to interact with this task programmatically.
|
||||
</p>
|
||||
|
||||
<div class="space-y-6">
|
||||
<!-- Execute Task by ID -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-3 flex items-center">
|
||||
<i class="fas fa-play text-[#38BDF8] mr-2"></i>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-3 flex items-center">
|
||||
<i class="fas fa-play text-[var(--color-primary)] mr-2"></i>
|
||||
Execute Task by ID
|
||||
</h3>
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/jobs/execute \
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/jobs/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-d '{
|
||||
@@ -382,12 +384,12 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
|
||||
<!-- Execute Task by Name -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-3 flex items-center">
|
||||
<i class="fas fa-code text-[#38BDF8] mr-2"></i>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-3 flex items-center">
|
||||
<i class="fas fa-code text-[var(--color-primary)] mr-2"></i>
|
||||
Execute Task by Name
|
||||
</h3>
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/tasks/<span x-text="task ? task.name : 'task-name'"></span>/execute \
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/tasks/<span x-text="task ? task.name : 'task-name'"></span>/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-d '{
|
||||
@@ -396,7 +398,7 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
"task_description": "Analyze sales data"
|
||||
}'</code></pre>
|
||||
</div>
|
||||
<p class="text-xs text-[#94A3B8] mt-2">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-2">
|
||||
<i class="fas fa-info-circle mr-1"></i>
|
||||
The request body should be a JSON object where keys are parameter names and values are strings.
|
||||
If no body is provided, the task will execute with empty parameters.
|
||||
@@ -405,12 +407,12 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
|
||||
<!-- Execute Task with Multimedia -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-3 flex items-center">
|
||||
<i class="fas fa-images text-[#38BDF8] mr-2"></i>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-3 flex items-center">
|
||||
<i class="fas fa-images text-[var(--color-primary)] mr-2"></i>
|
||||
Execute Task with Multimedia (Images)
|
||||
</h3>
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/jobs/execute \
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono overflow-x-auto"><code>curl -X POST {{ .BaseURL }}api/agent/jobs/execute \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_API_KEY" \
|
||||
-d '{
|
||||
@@ -425,53 +427,53 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
]
|
||||
}'</code></pre>
|
||||
</div>
|
||||
<p class="text-xs text-[#94A3B8] mt-2">
|
||||
You can provide multimedia content as URLs or base64-encoded data URIs. Supported types: <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">images</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">videos</code>, <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">audios</code>, and <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">files</code>.
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-2">
|
||||
You can provide multimedia content as URLs or base64-encoded data URIs. Supported types: <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">images</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">videos</code>, <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">audios</code>, and <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">files</code>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Check Job Status -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB] mb-3 flex items-center">
|
||||
<i class="fas fa-info-circle text-[#38BDF8] mr-2"></i>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)] mb-3 flex items-center">
|
||||
<i class="fas fa-info-circle text-[var(--color-primary)] mr-2"></i>
|
||||
Check Job Status
|
||||
</h3>
|
||||
<div class="bg-[#101827] border border-[#38BDF8]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[#E5E7EB] font-mono overflow-x-auto"><code>curl -X GET {{ .BaseURL }}api/agent/jobs/JOB_ID \
|
||||
<div class="bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/10 rounded-lg p-4">
|
||||
<pre class="text-xs text-[var(--color-text-primary)] font-mono overflow-x-auto"><code>curl -X GET {{ .BaseURL }}api/agent/jobs/JOB_ID \
|
||||
-H "Authorization: Bearer YOUR_API_KEY"</code></pre>
|
||||
</div>
|
||||
<p class="text-xs text-[#94A3B8] mt-2">
|
||||
After executing a task, you will receive a <code class="bg-[#101827] px-1 py-0.5 rounded text-[#38BDF8]">job_id</code> in the response. Use it to query the job's status and results.
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mt-2">
|
||||
After executing a task, you will receive a <code class="bg-[var(--color-bg-primary)] px-1 py-0.5 rounded text-[var(--color-primary)]">job_id</code> in the response. Use it to query the job's status and results.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Webhook Configuration (View Mode) -->
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8 mb-8" x-show="task && task.id && task.webhooks && task.webhooks.length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[#E5E7EB] mb-6">Webhook Configuration</h2>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8 mb-8" x-show="task && task.id && task.webhooks && task.webhooks.length > 0">
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] mb-6">Webhook Configuration</h2>
|
||||
<div class="space-y-4">
|
||||
<template x-for="(webhook, index) in task.webhooks" :key="index">
|
||||
<div class="bg-[#101827] p-4 rounded border border-[#38BDF8]/10">
|
||||
<div class="bg-[var(--color-bg-primary)] p-4 rounded border border-[var(--color-primary)]/10">
|
||||
<div class="flex items-center mb-3">
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB]">Webhook <span x-text="index + 1"></span></h3>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Webhook <span x-text="index + 1"></span></h3>
|
||||
</div>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">URL</label>
|
||||
<div class="text-[#E5E7EB] mt-1 font-mono text-sm break-all" x-text="webhook.url"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">URL</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1 font-mono text-sm break-all" x-text="webhook.url"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-[#94A3B8] text-sm">Method</label>
|
||||
<div class="text-[#E5E7EB] mt-1 font-mono text-sm" x-text="webhook.method || 'POST'"></div>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Method</label>
|
||||
<div class="text-[var(--color-text-primary)] mt-1 font-mono text-sm" x-text="webhook.method || 'POST'"></div>
|
||||
</div>
|
||||
<div x-show="webhook.headers && Object.keys(webhook.headers).length > 0">
|
||||
<label class="text-[#94A3B8] text-sm">Headers</label>
|
||||
<pre class="bg-[#0A0E1A] p-3 rounded text-[#E5E7EB] text-xs mt-1 overflow-x-auto" x-text="JSON.stringify(webhook.headers, null, 2)"></pre>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Headers</label>
|
||||
<pre class="bg-[var(--color-bg-tertiary)] p-3 rounded text-[var(--color-text-primary)] text-xs mt-1 overflow-x-auto" x-text="JSON.stringify(webhook.headers, null, 2)"></pre>
|
||||
</div>
|
||||
<div x-show="webhook.payload_template">
|
||||
<label class="text-[#94A3B8] text-sm">Payload Template</label>
|
||||
<pre class="bg-[#0A0E1A] p-3 rounded text-[#E5E7EB] text-xs mt-1 whitespace-pre-wrap overflow-x-auto" x-text="webhook.payload_template"></pre>
|
||||
<label class="text-[var(--color-text-secondary)] text-sm">Payload Template</label>
|
||||
<pre class="bg-[var(--color-bg-tertiary)] p-3 rounded text-[var(--color-text-primary)] text-xs mt-1 whitespace-pre-wrap overflow-x-auto" x-text="webhook.payload_template"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -482,12 +484,12 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
|
||||
<!-- Jobs for this Task (visible when not creating new task and not in edit mode) -->
|
||||
<template x-if="!isNewTask && !isEditMode">
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl p-8">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl p-8">
|
||||
<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)]/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>
|
||||
@@ -505,20 +507,20 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
<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]">Status</th>
|
||||
<th class="text-left py-3 px-4 text-[#94A3B8]">Created</th>
|
||||
<th class="text-left py-3 px-4 text-[#94A3B8]">Triggered By</th>
|
||||
<th class="text-left py-3 px-4 text-[#94A3B8]">Actions</th>
|
||||
<tr class="border-b border-[var(--color-primary)]/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)]">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)]">Triggered By</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)]/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>
|
||||
@@ -533,8 +535,8 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
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-[#94A3B8] text-sm" x-text="job.triggered_by || '-'"></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 text-[var(--color-text-secondary)] text-sm" x-text="job.triggered_by || '-'"></td>
|
||||
<td class="py-3 px-4">
|
||||
<button x-show="job.status === 'pending' || job.status === 'running'"
|
||||
@click="cancelJob(job.id)"
|
||||
@@ -546,7 +548,7 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
</tr>
|
||||
</template>
|
||||
<tr x-show="jobs.length === 0">
|
||||
<td colspan="5" class="py-8 text-center text-[#94A3B8]">No jobs found for this task</td>
|
||||
<td colspan="5" class="py-8 text-center text-[var(--color-text-secondary)]">No jobs found for this task</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -560,11 +562,11 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
x-cloak
|
||||
@click.away="showExecuteTaskModal = false; executionParameters = {}; executionParametersText = ''; executionMultimedia = {images: '', videos: '', audios: '', files: ''}; executeModalTab = 'parameters'"
|
||||
class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
||||
<div class="bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl max-w-2xl w-full mx-4 max-h-[90vh] flex flex-col" @click.stop>
|
||||
<div class="flex justify-between items-center p-8 pb-6 border-b border-[#38BDF8]/20">
|
||||
<h3 class="text-2xl font-semibold text-[#E5E7EB]">Execute Task</h3>
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary)]/20 rounded-xl max-w-2xl w-full mx-4 max-h-[90vh] flex flex-col" @click.stop>
|
||||
<div class="flex justify-between items-center p-8 pb-6 border-b border-[var(--color-primary)]/20">
|
||||
<h3 class="text-2xl font-semibold text-[var(--color-text-primary)]">Execute Task</h3>
|
||||
<button @click="showExecuteTaskModal = false; executionParameters = {}; executionParametersText = ''; executionMultimedia = {images: '', videos: '', audios: '', files: ''}; executeModalTab = 'parameters'"
|
||||
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>
|
||||
@@ -572,20 +574,20 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
<div class="flex flex-col flex-1 min-h-0">
|
||||
<div class="flex-1 overflow-y-auto px-8 py-6 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Task</label>
|
||||
<div class="text-[#94A3B8]" x-text="task.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="task.name"></div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs for Parameters and Multimedia -->
|
||||
<div class="border-b border-[#38BDF8]/20">
|
||||
<div class="border-b border-[var(--color-primary)]/20">
|
||||
<div class="flex space-x-4">
|
||||
<button @click="executeModalTab = 'parameters'"
|
||||
:class="executeModalTab === 'parameters' ? 'border-b-2 border-[#38BDF8] text-[#38BDF8]' : 'text-[#94A3B8] hover:text-[#E5E7EB]'"
|
||||
:class="executeModalTab === 'parameters' ? 'border-b-2 border-[var(--color-primary)] text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'"
|
||||
class="px-4 py-2 font-medium transition-colors">
|
||||
Parameters
|
||||
</button>
|
||||
<button @click="executeModalTab = 'multimedia'"
|
||||
:class="executeModalTab === 'multimedia' ? 'border-b-2 border-[#38BDF8] text-[#38BDF8]' : 'text-[#94A3B8] hover:text-[#E5E7EB]'"
|
||||
:class="executeModalTab === 'multimedia' ? 'border-b-2 border-[var(--color-primary)] text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]'"
|
||||
class="px-4 py-2 font-medium transition-colors">
|
||||
Multimedia
|
||||
</button>
|
||||
@@ -594,75 +596,75 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
|
||||
<!-- Parameters Tab -->
|
||||
<div x-show="executeModalTab === 'parameters'">
|
||||
<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 job_title=Software Engineer 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)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/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>
|
||||
|
||||
<!-- Multimedia Tab -->
|
||||
<div x-show="executeModalTab === 'multimedia'" class="space-y-4">
|
||||
<p class="text-xs text-[#94A3B8] mb-3">
|
||||
<p class="text-xs text-[var(--color-text-secondary)] mb-3">
|
||||
Provide multimedia content as URLs or base64-encoded data URIs. You can also upload files which will be converted to base64.
|
||||
</p>
|
||||
|
||||
<!-- Images -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Images</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Images</label>
|
||||
<textarea x-model="executionMultimedia.images"
|
||||
rows="3"
|
||||
placeholder="https://example.com/image.png data:image/png;base64,iVBORw0KG..."
|
||||
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>
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<input type="file" @change="handleFileUpload($event, 'image')" accept="image/*" multiple
|
||||
class="mt-2 text-sm text-[#94A3B8] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[#38BDF8] file:text-white hover:file:bg-[#38BDF8]/80">
|
||||
class="mt-2 text-sm text-[var(--color-text-secondary)] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[var(--color-primary)] file:text-white hover:file:bg-[var(--color-primary)]/80">
|
||||
</div>
|
||||
|
||||
<!-- Videos -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Videos</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Videos</label>
|
||||
<textarea x-model="executionMultimedia.videos"
|
||||
rows="3"
|
||||
placeholder="https://example.com/video.mp4 data:video/mp4;base64,..."
|
||||
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>
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<input type="file" @change="handleFileUpload($event, 'video')" accept="video/*" multiple
|
||||
class="mt-2 text-sm text-[#94A3B8] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[#38BDF8] file:text-white hover:file:bg-[#38BDF8]/80">
|
||||
class="mt-2 text-sm text-[var(--color-text-secondary)] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[var(--color-primary)] file:text-white hover:file:bg-[var(--color-primary)]/80">
|
||||
</div>
|
||||
|
||||
<!-- Audios -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Audios</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Audios</label>
|
||||
<textarea x-model="executionMultimedia.audios"
|
||||
rows="3"
|
||||
placeholder="https://example.com/audio.mp3 data:audio/mpeg;base64,..."
|
||||
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>
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<input type="file" @change="handleFileUpload($event, 'audio')" accept="audio/*" multiple
|
||||
class="mt-2 text-sm text-[#94A3B8] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[#38BDF8] file:text-white hover:file:bg-[#38BDF8]/80">
|
||||
class="mt-2 text-sm text-[var(--color-text-secondary)] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[var(--color-primary)] file:text-white hover:file:bg-[var(--color-primary)]/80">
|
||||
</div>
|
||||
|
||||
<!-- Files -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#E5E7EB] mb-2">Files</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-primary)] mb-2">Files</label>
|
||||
<textarea x-model="executionMultimedia.files"
|
||||
rows="3"
|
||||
placeholder="https://example.com/file.pdf data:application/pdf;base64,..."
|
||||
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>
|
||||
class="w-full bg-[var(--color-bg-primary)] border border-[var(--color-primary)]/20 rounded px-4 py-2 text-[var(--color-text-primary)] font-mono text-sm focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50"></textarea>
|
||||
<input type="file" @change="handleFileUpload($event, 'file')" multiple
|
||||
class="mt-2 text-sm text-[#94A3B8] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[#38BDF8] file:text-white hover:file:bg-[#38BDF8]/80">
|
||||
class="mt-2 text-sm text-[var(--color-text-secondary)] file:mr-4 file:py-2 file:px-4 file:rounded file:border-0 file:text-sm file:font-semibold file:bg-[var(--color-primary)] file:text-white hover:file:bg-[var(--color-primary)]/80">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-4 p-8 pt-6 border-t border-[#38BDF8]/20 bg-[#1E293B]">
|
||||
<div class="flex justify-end space-x-4 p-8 pt-6 border-t border-[var(--color-primary)]/20 bg-[var(--color-bg-secondary)]">
|
||||
<button @click="showExecuteTaskModal = false; executionParameters = {}; executionParametersText = ''; executionMultimedia = {images: '', videos: '', audios: '', files: ''}; executeModalTab = 'parameters'"
|
||||
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-[var(--color-bg-tertiary)] text-[var(--color-text-primary)] rounded-lg transition-colors">
|
||||
Cancel
|
||||
</button>
|
||||
<button @click="executeTaskWithParameters()"
|
||||
@@ -1135,6 +1137,9 @@ Provide a detailed response that addresses their specific needs.</pre>
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -2,10 +2,12 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen" x-data="backendsGallery()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="backendsGallery()">
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="fixed top-20 right-4 z-50 space-y-2" style="max-width: 400px;">
|
||||
@@ -44,20 +46,20 @@
|
||||
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">
|
||||
<div class="flex items-center bg-[#101827] rounded-lg px-4 py-2">
|
||||
<div class="w-2 h-2 bg-emerald-400 rounded-full mr-2"></div>
|
||||
<span class="font-semibold text-emerald-300" x-text="availableBackends"></span>
|
||||
<span class="text-[#94A3B8] ml-1">backends available</span>
|
||||
<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-success)] rounded-full mr-2"></div>
|
||||
<span class="font-semibold text-[var(--color-success)]" x-text="availableBackends"></span>
|
||||
<span class="text-[var(--color-text-secondary)] ml-1">backends 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-[#8B5CF6]/30 hover:border-[#8B5CF6]/50">
|
||||
<div class="w-2 h-2 bg-cyan-400 rounded-full mr-2"></div>
|
||||
<span class="font-semibold text-cyan-300" x-text="installedBackends"></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-accent)]/30 hover:border-[var(--color-accent)]/50">
|
||||
<div class="w-2 h-2 bg-[var(--color-primary)] rounded-full mr-2"></div>
|
||||
<span class="font-semibold text-[var(--color-primary)]" x-text="installedBackends"></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 border border-[#38BDF8]/30">
|
||||
<i class="fas fa-microchip text-[#38BDF8] mr-2"></i>
|
||||
<span class="text-[#94A3B8] mr-1">Capability:</span>
|
||||
<span class="font-semibold text-[#38BDF8]" x-text="systemCapability"></span>
|
||||
<div class="flex items-center bg-[var(--color-bg-primary)] rounded-lg px-4 py-2 border border-[var(--color-primary-border)]">
|
||||
<i class="fas fa-microchip text-[var(--color-primary)] mr-2"></i>
|
||||
<span class="text-[var(--color-text-secondary)] mr-1">Capability:</span>
|
||||
<span class="font-semibold text-[var(--color-primary)]" x-text="systemCapability"></span>
|
||||
</div>
|
||||
<a href="https://localai.io/backends/" target="_blank" class="btn-primary">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
@@ -77,41 +79,41 @@
|
||||
class="w-full flex items-center justify-between text-left"
|
||||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-plus-circle text-[#38BDF8] text-lg"></i>
|
||||
<h3 class="text-lg font-semibold text-[#E5E7EB]">Install Backend Manually</h3>
|
||||
<i class="fas fa-plus-circle text-[var(--color-primary)] text-lg"></i>
|
||||
<h3 class="text-lg font-semibold text-[var(--color-text-primary)]">Install Backend Manually</h3>
|
||||
</div>
|
||||
<i class="fas text-[#94A3B8] transition-transform duration-200" :class="showManualInstall ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
|
||||
<i class="fas text-[var(--color-text-secondary)] transition-transform duration-200" :class="showManualInstall ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
|
||||
</button>
|
||||
|
||||
<div x-show="showManualInstall" x-collapse>
|
||||
<p class="text-sm text-[#94A3B8] mt-4 mb-6">Install a backend from an OCI image, URL, or local path</p>
|
||||
<p class="text-sm text-[var(--color-text-secondary)] mt-4 mb-6">Install a backend from an OCI image, URL, or local path</p>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#94A3B8] mb-2">OCI Image / URL / Path *</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">OCI Image / URL / Path *</label>
|
||||
<input
|
||||
type="text"
|
||||
x-model="externalBackend.uri"
|
||||
placeholder="e.g., oci://quay.io/example/backend:latest"
|
||||
class="w-full px-4 py-3 text-sm bg-[#101827] border border-[#38BDF8]/30 rounded-lg text-[#E5E7EB] placeholder-[#94A3B8]/50 focus:border-[#38BDF8] focus:outline-none focus:ring-1 focus:ring-[#38BDF8]"
|
||||
class="input w-full px-4 py-3 text-sm"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#94A3B8] mb-2">Name (required for OCI)</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">Name (required for OCI)</label>
|
||||
<input
|
||||
type="text"
|
||||
x-model="externalBackend.name"
|
||||
placeholder="e.g., my-backend"
|
||||
class="w-full px-4 py-3 text-sm bg-[#101827] border border-[#38BDF8]/30 rounded-lg text-[#E5E7EB] placeholder-[#94A3B8]/50 focus:border-[#38BDF8] focus:outline-none focus:ring-1 focus:ring-[#38BDF8]"
|
||||
class="input w-full px-4 py-3 text-sm"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-[#94A3B8] mb-2">Alias (optional)</label>
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">Alias (optional)</label>
|
||||
<input
|
||||
type="text"
|
||||
x-model="externalBackend.alias"
|
||||
placeholder="e.g., backend-alias"
|
||||
class="w-full px-4 py-3 text-sm bg-[#101827] border border-[#38BDF8]/30 rounded-lg text-[#E5E7EB] placeholder-[#94A3B8]/50 focus:border-[#38BDF8] focus:outline-none focus:ring-1 focus:ring-[#38BDF8]"
|
||||
class="input w-full px-4 py-3 text-sm"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,12 +122,12 @@
|
||||
<button
|
||||
@click="installExternalBackend()"
|
||||
:disabled="installingExternal || !externalBackend.uri"
|
||||
class="inline-flex items-center px-5 py-2.5 rounded-lg bg-[#38BDF8] hover:bg-[#38BDF8]/80 text-sm font-medium text-white transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
class="btn-primary"
|
||||
>
|
||||
<i class="mr-2" :class="installingExternal ? 'fas fa-spinner fa-spin' : 'fas fa-download'"></i>
|
||||
<span x-text="installingExternal ? 'Installing...' : 'Install Backend'"></span>
|
||||
</button>
|
||||
<span x-show="externalBackendProgress" class="text-sm text-[#94A3B8]" x-text="externalBackendProgress"></span>
|
||||
<span x-show="externalBackendProgress" class="text-sm text-[var(--color-text-secondary)]" x-text="externalBackendProgress"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,13 +137,13 @@
|
||||
<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-[#8B5CF6]"></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-accent)]"></i>
|
||||
Find Backend Components
|
||||
</h3>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 start-0 flex items-center ps-4 pointer-events-none z-10">
|
||||
<i class="fas fa-search text-[#94A3B8]"></i>
|
||||
<i class="fas fa-search text-[var(--color-text-secondary)]"></i>
|
||||
</div>
|
||||
<input
|
||||
x-model="searchTerm"
|
||||
@@ -151,7 +153,7 @@
|
||||
type="search"
|
||||
placeholder="Search backends by name, description or type...">
|
||||
<span class="absolute right-4 top-4" x-show="loading">
|
||||
<svg class="animate-spin h-6 w-6 text-[#8B5CF6]" 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-accent)]" 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>
|
||||
@@ -161,33 +163,33 @@
|
||||
|
||||
<!-- Filter by Type -->
|
||||
<div>
|
||||
<h3 class="text-lg font-semibold text-white mb-4 flex items-center">
|
||||
<i class="fas fa-filter mr-3 text-teal-400"></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-secondary)]"></i>
|
||||
Filter by Backend Type
|
||||
</h3>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-3">
|
||||
<button @click="filterByTerm('llm')"
|
||||
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-accent-light)] hover:bg-[var(--color-accent)]/30 text-[var(--color-text-primary)] border border-[var(--color-accent)]/30 transition-colors">
|
||||
<i class="fas fa-brain mr-2"></i>
|
||||
<span>LLM</span>
|
||||
</button>
|
||||
<button @click="filterByTerm('diffusion')"
|
||||
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-accent-light)] hover:bg-[var(--color-accent)]/30 text-[var(--color-text-primary)] border border-[var(--color-accent)]/30 transition-colors">
|
||||
<i class="fas fa-image mr-2"></i>
|
||||
<span>Diffusion</span>
|
||||
</button>
|
||||
<button @click="filterByTerm('tts')"
|
||||
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-primary-light)] hover:bg-[var(--color-primary)]/30 text-[var(--color-text-primary)] border border-[var(--color-primary-border)] transition-colors">
|
||||
<i class="fas fa-microphone mr-2"></i>
|
||||
<span>TTS</span>
|
||||
</button>
|
||||
<button @click="filterByTerm('whisper')"
|
||||
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-success-light)] hover:bg-[var(--color-success)]/30 text-[var(--color-success)] border border-[var(--color-success)]/30 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-light)] hover:bg-[var(--color-error)]/30 text-[var(--color-error)] border border-[var(--color-error)]/30 transition-colors">
|
||||
<i class="fas fa-eye mr-2"></i>
|
||||
<span>Vision</span>
|
||||
</button>
|
||||
@@ -199,97 +201,97 @@
|
||||
<!-- Results Section -->
|
||||
<div id="search-results" class="transition-all duration-300">
|
||||
<div x-show="loading && backends.length === 0" class="text-center py-12">
|
||||
<svg class="animate-spin h-12 w-12 text-emerald-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 backends...</p>
|
||||
<p class="text-[var(--color-text-secondary)]">Loading backends...</p>
|
||||
</div>
|
||||
|
||||
<div x-show="!loading && backends.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 backends 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 backends found matching your criteria</p>
|
||||
</div>
|
||||
|
||||
<!-- Table View -->
|
||||
<div x-show="backends.length > 0" class="bg-[#1E293B] rounded-2xl border border-[#38BDF8]/20 overflow-hidden shadow-xl backdrop-blur-sm">
|
||||
<div x-show="backends.length > 0" class="bg-[var(--color-bg-secondary)] rounded-2xl border border-[var(--color-border-subtle)] overflow-hidden shadow-xl backdrop-blur-sm">
|
||||
<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>
|
||||
<tr class="bg-[var(--color-primary-light)] border-b border-[var(--color-border-subtle)]">
|
||||
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Icon</th>
|
||||
<th @click="setSort('name')"
|
||||
:class="sortBy === 'name' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'name' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Backend Name</span>
|
||||
<i :class="sortBy === 'name' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'name' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'name' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</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-[var(--color-text-primary)] uppercase tracking-wider">Description</th>
|
||||
<th @click="setSort('repository')"
|
||||
:class="sortBy === 'repository' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'repository' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Repository</span>
|
||||
<i :class="sortBy === 'repository' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'repository' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'repository' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="setSort('license')"
|
||||
:class="sortBy === 'license' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'license' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>License</span>
|
||||
<i :class="sortBy === 'license' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'license' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'license' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="setSort('status')"
|
||||
:class="sortBy === 'status' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'status' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Status</span>
|
||||
<i :class="sortBy === 'status' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'status' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'status' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-4 text-right text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Actions</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">
|
||||
<tbody class="divide-y divide-[var(--color-border-subtle)]">
|
||||
<template x-for="backend in backends" :key="backend.id">
|
||||
<tr class="hover:bg-[#38BDF8]/10 transition-colors duration-200">
|
||||
<tr class="hover:bg-[var(--color-bg-primary)] 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-border-subtle)] flex items-center justify-center bg-[var(--color-bg-primary)]">
|
||||
<img x-show="backend.icon"
|
||||
:src="backend.icon"
|
||||
class="w-full h-full object-cover rounded-lg"
|
||||
loading="lazy"
|
||||
:alt="backend.name">
|
||||
<i x-show="!backend.icon" class="fas fa-cog text-xl text-[#8B5CF6]"></i>
|
||||
<i x-show="!backend.icon" class="fas fa-cog text-xl text-[var(--color-accent)]"></i>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<!-- Backend Name -->
|
||||
<td class="px-6 py-4">
|
||||
<span class="text-sm font-semibold text-[#E5E7EB]" x-text="backend.name"></span>
|
||||
<span class="text-sm font-semibold text-[var(--color-text-primary)]" x-text="backend.name"></span>
|
||||
</td>
|
||||
|
||||
<!-- Description -->
|
||||
<td class="px-6 py-4">
|
||||
<div class="text-sm text-[#94A3B8] max-w-xs truncate" x-text="backend.description" :title="backend.description"></div>
|
||||
<div class="text-sm text-[var(--color-text-secondary)] max-w-xs truncate" x-text="backend.description" :title="backend.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-light)] text-[var(--color-text-primary)] border border-[var(--color-primary-border)]">
|
||||
<i class="fa-brands fa-git-alt mr-1"></i>
|
||||
<span x-text="backend.gallery"></span>
|
||||
</span>
|
||||
@@ -297,21 +299,21 @@
|
||||
|
||||
<!-- License -->
|
||||
<td class="px-6 py-4">
|
||||
<span x-show="backend.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="backend.license" class="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-accent-light)] text-[var(--color-text-primary)] border border-[var(--color-accent)]/30">
|
||||
<i class="fas fa-book mr-1"></i>
|
||||
<span x-text="backend.license"></span>
|
||||
</span>
|
||||
<span x-show="!backend.license" class="text-xs text-[#94A3B8]">-</span>
|
||||
<span x-show="!backend.license" class="text-xs text-[var(--color-text-secondary)]">-</span>
|
||||
</td>
|
||||
|
||||
<!-- Status -->
|
||||
<td class="px-6 py-4">
|
||||
<!-- Processing State -->
|
||||
<div x-show="backend.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="backend.isDeletion ? 'Deleting...' : 'Installing...'"></span>
|
||||
</div>
|
||||
<div x-show="(jobProgress[backend.jobID] || 0) === 0" class="text-xs text-[#38BDF8]">
|
||||
<div x-show="(jobProgress[backend.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">
|
||||
@@ -321,7 +323,7 @@
|
||||
|
||||
<!-- Installed State -->
|
||||
<div x-show="!backend.processing && backend.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="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-success-light)] text-[var(--color-success)] border border-[var(--color-success)]/30">
|
||||
<i class="fas fa-check-circle mr-1"></i>
|
||||
Installed
|
||||
</span>
|
||||
@@ -329,7 +331,7 @@
|
||||
|
||||
<!-- Not Installed State -->
|
||||
<div x-show="!backend.processing && !backend.installed">
|
||||
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[#1E293B] text-[#94A3B8] border border-[#38BDF8]/30">
|
||||
<span class="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] border border-[var(--color-border-subtle)]">
|
||||
<i class="fas fa-circle mr-1"></i>
|
||||
Not Installed
|
||||
</span>
|
||||
@@ -341,7 +343,7 @@
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<!-- Info Button -->
|
||||
<button @click="openModal(backend)"
|
||||
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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-bg-primary)] hover:bg-[var(--color-primary-light)] text-xs font-medium text-[var(--color-text-primary)] transition duration-200 border border-[var(--color-border-subtle)]"
|
||||
title="View details">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
@@ -350,12 +352,12 @@
|
||||
<template x-if="!backend.processing && backend.installed">
|
||||
<div class="flex gap-2">
|
||||
<button @click="reinstallBackend(backend.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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-xs font-medium text-white transition duration-200"
|
||||
title="Reinstall">
|
||||
<i class="fa-solid fa-arrow-rotate-right"></i>
|
||||
</button>
|
||||
<button @click="deleteBackend(backend.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)]/80 text-xs font-medium text-white transition duration-200"
|
||||
title="Delete">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@@ -365,7 +367,7 @@
|
||||
<!-- Not Installed State Actions -->
|
||||
<template x-if="!backend.processing && !backend.installed">
|
||||
<button @click="installBackend(backend.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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-xs font-medium text-white transition duration-200"
|
||||
title="Install">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
</button>
|
||||
@@ -383,15 +385,15 @@
|
||||
<div x-show="selectedBackend"
|
||||
x-transition
|
||||
@click.away="closeModal()"
|
||||
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-gray-900/50"
|
||||
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-black/50"
|
||||
style="display: none;">
|
||||
<div class="relative p-4 w-full max-w-2xl h-[90vh] mx-auto mt-[5vh]">
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 h-full flex flex-col">
|
||||
<div class="relative bg-[var(--color-bg-secondary)] rounded-lg shadow h-full flex flex-col border border-[var(--color-border-subtle)]">
|
||||
<!-- Modal Header -->
|
||||
<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="selectedBackend?.name"></h3>
|
||||
<div class="flex items-center justify-between p-4 md:p-5 border-b border-[var(--color-border-subtle)] rounded-t">
|
||||
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]" x-text="selectedBackend?.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-[var(--color-bg-primary)] hover:text-[var(--color-text-primary)] rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center transition-colors">
|
||||
<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>
|
||||
@@ -401,21 +403,21 @@
|
||||
<!-- Modal Body -->
|
||||
<div class="p-4 md:p-5 space-y-4 overflow-y-auto flex-1 min-h-0">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="w-48 h-48 rounded-lg border border-gray-300 dark:border-gray-600 flex items-center justify-center bg-gray-100 dark:bg-gray-800 mt-3">
|
||||
<div class="w-48 h-48 rounded-lg border border-[var(--color-border-subtle)] flex items-center justify-center bg-[var(--color-bg-primary)] mt-3">
|
||||
<img x-show="selectedBackend?.icon"
|
||||
:src="selectedBackend?.icon"
|
||||
class="rounded-lg max-h-48 max-w-96 object-cover"
|
||||
loading="lazy">
|
||||
<i x-show="!selectedBackend?.icon" class="fas fa-cog text-6xl text-gray-400 dark:text-gray-500"></i>
|
||||
<i x-show="!selectedBackend?.icon" class="fas fa-cog text-6xl 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(selectedBackend?.description)"></div>
|
||||
<div class="text-base leading-relaxed text-[var(--color-text-secondary)] break-words max-w-full markdown-content" x-html="renderMarkdown(selectedBackend?.description)"></div>
|
||||
<template x-if="selectedBackend?.tags && selectedBackend.tags.length > 0">
|
||||
<div>
|
||||
<p class="text-sm mb-3 font-semibold text-gray-900 dark:text-white">Tags</p>
|
||||
<p class="text-sm mb-3 font-semibold text-[var(--color-text-primary)]">Tags</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<template x-for="tag in selectedBackend.tags" :key="tag">
|
||||
<span class="inline-flex items-center text-xs px-3 py-1 rounded-full bg-gray-700/60 text-gray-300 border border-gray-600/50">
|
||||
<span class="inline-flex items-center text-xs px-3 py-1 rounded-full bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] border border-[var(--color-border-subtle)]">
|
||||
<i class="fas fa-tag pr-2"></i>
|
||||
<span x-text="tag"></span>
|
||||
</span>
|
||||
@@ -425,11 +427,11 @@
|
||||
</template>
|
||||
<template x-if="selectedBackend?.urls && selectedBackend.urls.length > 0">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white mb-2">Links</p>
|
||||
<p class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Links</p>
|
||||
<ul>
|
||||
<template x-for="url in selectedBackend.urls" :key="url">
|
||||
<li>
|
||||
<a :href="url" target="_blank" class="text-blue-500 hover:underline">
|
||||
<a :href="url" target="_blank" class="text-[var(--color-primary)] hover:underline">
|
||||
<i class="fas fa-link pr-2"></i>
|
||||
<span x-text="url"></span>
|
||||
</a>
|
||||
@@ -440,9 +442,9 @@
|
||||
</template>
|
||||
</div>
|
||||
<!-- Modal Footer -->
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-[var(--color-border-subtle)] rounded-b">
|
||||
<button @click="closeModal()"
|
||||
class="text-white bg-emerald-700 hover:bg-emerald-800 focus:ring-4 focus:outline-none focus:ring-emerald-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-emerald-600 dark:hover:bg-emerald-700 dark:focus:ring-emerald-800">
|
||||
class="text-white bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] focus:ring-2 focus:outline-none focus:ring-[var(--color-primary)]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center transition-colors">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
@@ -453,30 +455,29 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
<div x-show="totalPages > 1" class="flex justify-center mt-12">
|
||||
<div class="flex items-center gap-4 bg-gray-800/60 rounded-2xl p-4 backdrop-blur-sm border border-gray-700/50">
|
||||
<div class="flex items-center gap-4 bg-[var(--color-bg-secondary)] rounded-2xl p-4 backdrop-blur-sm border border-[var(--color-border-subtle)]">
|
||||
<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-emerald-600 text-[#94A3B8] hover:text-white rounded-lg transition-colors">
|
||||
class="flex items-center justify-center h-12 w-12 bg-[var(--color-bg-primary)] hover:bg-[var(--color-success)] 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-white font-bold text-lg mx-2" x-text="currentPage"></span>
|
||||
<span class="text-gray-400">of</span>
|
||||
<span class="text-white font-bold text-lg mx-2" x-text="totalPages"></span>
|
||||
<div class="text-[var(--color-text-primary)] text-sm font-medium px-4">
|
||||
<span class="text-[var(--color-text-secondary)]">Page</span>
|
||||
<span class="text-[var(--color-text-primary)] font-bold text-lg mx-2" x-text="currentPage"></span>
|
||||
<span class="text-[var(--color-text-secondary)]">of</span>
|
||||
<span class="text-[var(--color-text-primary)] font-bold text-lg mx-2" x-text="totalPages"></span>
|
||||
</div>
|
||||
<button @click="goToPage(currentPage + 1)"
|
||||
:disabled="currentPage >= totalPages"
|
||||
:class="currentPage >= totalPages ? 'opacity-50 cursor-not-allowed' : ''"
|
||||
class="group flex items-center justify-center h-12 w-12 bg-gray-700/80 hover:bg-emerald-600 text-gray-300 hover:text-white rounded-xl shadow-lg transition-all duration-300 ease-in-out transform hover:scale-110">
|
||||
class="group flex items-center justify-center h-12 w-12 bg-[var(--color-bg-primary)] hover:bg-[var(--color-success)] text-[var(--color-text-secondary)] hover:text-white rounded-xl shadow-lg transition-all duration-300 ease-in-out transform hover:scale-110">
|
||||
<i class="fas fa-chevron-right group-hover:animate-pulse"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -516,16 +517,16 @@
|
||||
|
||||
/* Table progress bar styling */
|
||||
.progress-table {
|
||||
background: linear-gradient(135deg, rgba(56, 189, 248, 0.2) 0%, rgba(139, 92, 246, 0.2) 100%);
|
||||
background: var(--color-primary-light);
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid rgba(56, 189, 248, 0.3);
|
||||
border: 1px solid var(--color-primary-border);
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-bar-table-backend {
|
||||
background: linear-gradient(135deg, #38BDF8 0%, #8B5CF6 100%);
|
||||
background: var(--gradient-primary);
|
||||
height: 100%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
@@ -534,6 +535,7 @@
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
tbody tr:last-child td:first-child {
|
||||
@@ -905,5 +907,10 @@ function backendsGallery() {
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -587,19 +587,23 @@ SOFTWARE.
|
||||
<script defer src="static/chat.js"></script>
|
||||
{{ $allGalleryConfigs:=.GalleryConfig }}
|
||||
{{ $model:=.Model}}
|
||||
<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" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]" x-data="{ settingsPanelOpen: true, showClearAlert: false }">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner h-full flex flex-col">
|
||||
|
||||
<!-- Main container with sidebar toggle -->
|
||||
<div class="flex flex-1 overflow-hidden relative">
|
||||
<!-- Sidebar -->
|
||||
<!-- Main container with settings panel -->
|
||||
<div class="flex flex-1 min-h-0 relative">
|
||||
<!-- Chat Settings Panel (right side) -->
|
||||
<div
|
||||
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'">
|
||||
class="chat-settings-panel bg-[var(--color-bg-secondary)] fixed top-0 right-0 bottom-0 w-56 transform transition-transform duration-300 ease-in-out z-30 border-l border-[var(--color-border-subtle)] overflow-y-auto"
|
||||
:class="settingsPanelOpen ? 'translate-x-0' : 'translate-x-full'">
|
||||
|
||||
<div class="p-3 flex justify-between items-center border-b border-[var(--color-bg-primary)]">
|
||||
<div class="p-3 flex justify-between items-center border-b border-[var(--color-border-subtle)]">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2 class="text-sm font-semibold text-[var(--color-text-primary)]">Settings</h2>
|
||||
<h2 class="text-sm font-semibold text-[var(--color-text-primary)]">Chat Settings</h2>
|
||||
<a
|
||||
href="https://localai.io/features/text-generation/"
|
||||
target="_blank"
|
||||
@@ -609,10 +613,10 @@ SOFTWARE.
|
||||
</a>
|
||||
</div>
|
||||
<button
|
||||
@click="sidebarOpen = false"
|
||||
@click="settingsPanelOpen = false"
|
||||
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>
|
||||
title="Hide settings">
|
||||
<i class="fa-solid fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1099,23 +1103,14 @@ SOFTWARE.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main chat container (shifts with sidebar) -->
|
||||
<!-- Main chat container (shifts with settings panel) -->
|
||||
<div
|
||||
class="flex-1 flex flex-col transition-all duration-300 ease-in-out"
|
||||
:class="sidebarOpen ? 'ml-56' : 'ml-0'">
|
||||
class="flex-1 flex flex-col min-h-0 transition-all duration-300 ease-in-out"
|
||||
:class="settingsPanelOpen ? 'mr-56' : 'mr-0'">
|
||||
|
||||
<!-- Chat header with toggle button -->
|
||||
<div class="border-b border-[var(--color-bg-secondary)] p-4 flex items-center justify-between">
|
||||
<div class="flex-shrink-0 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-[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-[var(--color-primary)]"></i>
|
||||
<!-- Model icon - reactive to active chat -->
|
||||
@@ -1151,6 +1146,13 @@ SOFTWARE.
|
||||
x-show="$store.chat.activeChat() && ($store.chat.activeChat()?.history?.length || 0) > 0">
|
||||
<i class="fa-solid fa-broom"></i>
|
||||
</button>
|
||||
<!-- Settings panel toggle button -->
|
||||
<button
|
||||
@click="settingsPanelOpen = !settingsPanelOpen"
|
||||
class="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"
|
||||
title="Toggle chat settings">
|
||||
<i class="fa-solid" :class="settingsPanelOpen ? 'fa-chevron-right' : 'fa-cog'"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Clear Chat Alert -->
|
||||
@@ -1172,7 +1174,8 @@ SOFTWARE.
|
||||
</div>
|
||||
|
||||
<!-- Chat messages area -->
|
||||
<div class="flex-1 p-4 overflow-auto" id="chat">
|
||||
<div class="flex-1 min-h-0 overflow-y-auto" id="chat">
|
||||
<div class="p-4">
|
||||
<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">
|
||||
@@ -1367,11 +1370,11 @@ SOFTWARE.
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Chat Input -->
|
||||
<div class="p-4 border-t border-[var(--color-bg-secondary)]" x-data="{ inputValue: '', shiftPressed: false, attachedFiles: [] }">
|
||||
<div class="flex-shrink-0 p-4 border-t border-[var(--color-bg-secondary)] bg-[var(--color-bg-primary)]" 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">
|
||||
@@ -1536,11 +1539,11 @@ SOFTWARE.
|
||||
<!-- Modal moved outside of sidebar to appear in center of page - Always available, content updated dynamically -->
|
||||
<div id="model-info-modal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full h-full md:inset-0 max-h-full" style="padding: 1rem;">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full bg-white rounded-lg shadow dark:bg-gray-700">
|
||||
<div class="relative p-4 w-full max-w-2xl max-h-full bg-[var(--color-bg-secondary)] rounded-lg shadow border border-[var(--color-border-subtle)]">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
||||
<h3 id="model-info-modal-title" class="text-xl font-semibold text-gray-900 dark:text-white">{{ if $model }}{{ $model }}{{ end }}</h3>
|
||||
<button 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" data-modal-hide="model-info-modal" @click="if (window.closeModelInfoModal) { window.closeModelInfoModal(); }">
|
||||
<div class="flex items-center justify-between p-4 md:p-5 border-b border-[var(--color-border-subtle)] rounded-t">
|
||||
<h3 id="model-info-modal-title" class="text-xl font-semibold text-[var(--color-text-primary)]">{{ if $model }}{{ $model }}{{ end }}</h3>
|
||||
<button class="text-[var(--color-text-secondary)] bg-transparent hover:bg-[var(--color-bg-primary)] hover:text-[var(--color-text-primary)] rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center transition-colors" data-modal-hide="model-info-modal" @click="if (window.closeModelInfoModal) { window.closeModelInfoModal(); }">
|
||||
<svg class="w-3 h-3" aria-hidden="true" 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>
|
||||
@@ -1553,16 +1556,16 @@ SOFTWARE.
|
||||
<div class="flex justify-center items-center">
|
||||
<img id="model-info-modal-icon" class="lazy rounded-t-lg max-h-48 max-w-96 object-cover mt-3 entered loaded" style="display: none;" loading="lazy"/>
|
||||
</div>
|
||||
<div id="model-info-description" class="text-base leading-relaxed text-gray-500 dark:text-gray-400 break-words max-w-full"></div>
|
||||
<hr>
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white">Links</p>
|
||||
<div id="model-info-description" class="text-base leading-relaxed text-[var(--color-text-secondary)] break-words max-w-full"></div>
|
||||
<hr class="border-[var(--color-border-subtle)]">
|
||||
<p class="text-sm font-semibold text-[var(--color-text-primary)]">Links</p>
|
||||
<ul id="model-info-links">
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||
<button data-modal-hide="model-info-modal" 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" @click="if (window.closeModelInfoModal) { window.closeModelInfoModal(); }">
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-[var(--color-border-subtle)] rounded-b">
|
||||
<button data-modal-hide="model-info-modal" class="py-2.5 px-5 ms-3 text-sm font-medium text-white focus:outline-none bg-[var(--color-primary)] rounded-lg border-none hover:bg-[var(--color-primary-hover)] focus:z-10 focus:ring-2 focus:ring-[var(--color-primary)]/50 transition-colors" @click="if (window.closeModelInfoModal) { window.closeModelInfoModal(); }">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
@@ -1874,7 +1877,7 @@ SOFTWARE.
|
||||
let backdrop = document.querySelector('.modal-backdrop');
|
||||
if (!backdrop) {
|
||||
backdrop = document.createElement('div');
|
||||
backdrop.className = 'modal-backdrop fixed inset-0 bg-gray-900 bg-opacity-50 dark:bg-opacity-80 z-40';
|
||||
backdrop.className = 'modal-backdrop fixed inset-0 bg-black/50 z-40';
|
||||
document.body.appendChild(backdrop);
|
||||
backdrop.addEventListener('click', () => {
|
||||
closeModelInfoModal();
|
||||
@@ -1962,7 +1965,7 @@ SOFTWARE.
|
||||
let backdrop = document.querySelector('.modal-backdrop');
|
||||
if (!backdrop) {
|
||||
backdrop = document.createElement('div');
|
||||
backdrop.className = 'modal-backdrop fixed inset-0 bg-gray-900 bg-opacity-50 dark:bg-opacity-80 z-40';
|
||||
backdrop.className = 'modal-backdrop fixed inset-0 bg-black/50 z-40';
|
||||
document.body.appendChild(backdrop);
|
||||
backdrop.addEventListener('click', () => {
|
||||
window.closeModelInfoModal();
|
||||
@@ -2065,8 +2068,8 @@ SOFTWARE.
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre;
|
||||
background: #101827 !important;
|
||||
border: 1px solid #1E293B;
|
||||
background: var(--color-bg-primary) !important;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
margin: 0;
|
||||
@@ -2079,7 +2082,7 @@ SOFTWARE.
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre;
|
||||
background: transparent !important;
|
||||
color: #E5E7EB;
|
||||
color: var(--color-text-primary);
|
||||
font-family: 'ui-monospace', 'Monaco', 'Consolas', monospace;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
@@ -2182,13 +2185,13 @@ SOFTWARE.
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-track,
|
||||
.chat-settings-panel::-webkit-scrollbar-track,
|
||||
#chat::-webkit-scrollbar-track,
|
||||
#messages::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb,
|
||||
.chat-settings-panel::-webkit-scrollbar-thumb,
|
||||
#chat::-webkit-scrollbar-thumb,
|
||||
#messages::-webkit-scrollbar-thumb {
|
||||
background: rgba(148, 163, 184, 0.2);
|
||||
@@ -2196,14 +2199,14 @@ SOFTWARE.
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar-thumb:hover,
|
||||
.chat-settings-panel::-webkit-scrollbar-thumb:hover,
|
||||
#chat::-webkit-scrollbar-thumb:hover,
|
||||
#messages::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(148, 163, 184, 0.4);
|
||||
}
|
||||
|
||||
/* Firefox - Minimal */
|
||||
.sidebar,
|
||||
.chat-settings-panel,
|
||||
#chat,
|
||||
#messages {
|
||||
scrollbar-width: thin;
|
||||
@@ -2234,5 +2237,8 @@ SOFTWARE.
|
||||
scrollbar-color: rgba(148, 163, 184, 0.15) transparent;
|
||||
}
|
||||
</style>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,30 +2,31 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Error Section -->
|
||||
<div class="bg-[#1E293B] border border-red-500/20 rounded-xl p-8 mb-10">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-error)]/20 rounded-xl p-8 mb-10">
|
||||
<div class="max-w-4xl mx-auto text-center">
|
||||
<div class="mb-6 text-6xl text-red-400">
|
||||
<div class="mb-6 text-6xl text-[var(--color-error)]">
|
||||
<i class="fas fa-exclamation-circle"></i>
|
||||
</div>
|
||||
<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>
|
||||
<p class="text-xl text-[var(--color-text-secondary)] mb-6">{{if .ErrorMessage}}{{.ErrorMessage}}{{else}}An unexpected error occurred{{end}}</p>
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href="./"
|
||||
class="inline-flex items-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] font-semibold py-3 px-6 rounded-lg transition-colors">
|
||||
class="btn-primary">
|
||||
<i class="fas fa-home mr-2"></i>
|
||||
<span>Return Home</span>
|
||||
</a>
|
||||
<a href="browse/"
|
||||
class="inline-flex items-center bg-[#8B5CF6] hover:bg-[#8B5CF6]/90 text-white font-semibold py-3 px-6 rounded-lg transition-colors">
|
||||
class="btn-secondary">
|
||||
<i class="fas fa-images mr-2"></i>
|
||||
<span>Browse Gallery</span>
|
||||
</a>
|
||||
@@ -33,20 +34,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Additional Information -->
|
||||
<div class="bg-[#1E293B] border border-[#1E293B] rounded-xl p-8">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-border-subtle)] rounded-xl p-8">
|
||||
<div class="text-center max-w-3xl 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-4">
|
||||
<i class="text-yellow-400 text-2xl fa-solid fa-triangle-exclamation"></i>
|
||||
<div class="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[var(--color-warning-light)] border border-[var(--color-warning)]/20 mb-4">
|
||||
<i class="text-[var(--color-warning)] text-2xl fa-solid fa-triangle-exclamation"></i>
|
||||
</div>
|
||||
<h2 class="text-2xl md:text-3xl font-semibold text-[#E5E7EB] mb-4">Need help?</h2>
|
||||
<p class="text-lg text-[#94A3B8] mb-6">Visit our <a class="text-[#38BDF8] hover:text-[#8B5CF6] underline underline-offset-2 transition-colors" href="browse">🖼️ Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-[#38BDF8] hover:text-[#8B5CF6] underline underline-offset-2 transition-colors"> <i class="fa-solid fa-book"></i> Getting started documentation</a></p>
|
||||
<h2 class="text-2xl md:text-3xl font-semibold text-[var(--color-text-primary)] mb-4">Need help?</h2>
|
||||
<p class="text-lg text-[var(--color-text-secondary)] mb-6">Visit our <a class="text-[var(--color-primary)] hover:text-[var(--color-accent)] underline underline-offset-2 transition-colors" href="browse">Gallery</a> or check the <a href="https://localai.io/basics/getting_started/" class="text-[var(--color-primary)] hover:text-[var(--color-accent)] underline underline-offset-2 transition-colors">Getting started documentation</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #101827;
|
||||
color: #E5E7EB;
|
||||
background-color: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
.token {
|
||||
@@ -19,51 +19,53 @@
|
||||
position: relative;
|
||||
}
|
||||
.network-card {
|
||||
background-color: #2d3748;
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.network-card:hover {
|
||||
background-color: #374151;
|
||||
background-color: var(--color-bg-tertiary);
|
||||
}
|
||||
.network-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #63b3ed;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.network-token {
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
color: #cbd5e0;
|
||||
color: var(--color-text-secondary);
|
||||
margin-bottom: 10px;
|
||||
word-break: break-word; /* Breaks words to prevent overflow */
|
||||
overflow-wrap: break-word; /* Ensures long strings break */
|
||||
white-space: pre-wrap; /* Preserves whitespace for breaking */
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.cluster {
|
||||
margin-top: 10px;
|
||||
background-color: #4a5568;
|
||||
background-color: var(--color-bg-tertiary);
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
.cluster:hover {
|
||||
background-color: #5a6b78;
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
.cluster-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #e2e8f0;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
.form-container {
|
||||
background-color: #2d3748;
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
}
|
||||
.form-control {
|
||||
margin-bottom: 15px;
|
||||
@@ -72,47 +74,50 @@
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
input[type="text"],
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #4a5568;
|
||||
background-color: #3a4250;
|
||||
color: #e2e8f0;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
background-color: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
transition: border-color 0.3s ease, background-color 0.3s ease;
|
||||
}
|
||||
input[type="text"]:focus,
|
||||
textarea:focus {
|
||||
border-color: #63b3ed;
|
||||
background-color: #4a5568;
|
||||
border-color: var(--color-primary);
|
||||
background-color: var(--color-bg-tertiary);
|
||||
}
|
||||
button {
|
||||
background-color: #3182ce;
|
||||
color: #e2e8f0;
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
button:hover {
|
||||
background-color: var(--color-primary-hover);
|
||||
}
|
||||
.error {
|
||||
color: #e53e3e;
|
||||
color: var(--color-error);
|
||||
margin-top: 5px;
|
||||
}
|
||||
.success {
|
||||
color: #38a169;
|
||||
color: var(--color-success);
|
||||
margin-top: 5px;
|
||||
}
|
||||
/* Spinner Styles */
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid rgba(255, 255, 255, 0.2);
|
||||
border: 5px solid var(--color-border-subtle);
|
||||
border-radius: 50%;
|
||||
border-top-color: #3182ce;
|
||||
border-top-color: var(--color-primary);
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -121,43 +126,46 @@
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Center the loading text and spinner */
|
||||
.loading-container {
|
||||
text-align: center;
|
||||
padding: 50px;
|
||||
}
|
||||
.warning-box {
|
||||
border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.warning-box i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.token-box {
|
||||
background-color: #4a5568;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
.token-box:hover {
|
||||
background-color: #5a6b7e;
|
||||
}
|
||||
.token-text {
|
||||
overflow-wrap: break-word;
|
||||
font-family: monospace;
|
||||
}
|
||||
.copy-icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
background-color: var(--color-bg-tertiary);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
border: 1px solid var(--color-border-subtle);
|
||||
}
|
||||
.token-box:hover {
|
||||
background-color: var(--color-bg-secondary);
|
||||
}
|
||||
.token-text {
|
||||
overflow-wrap: break-word;
|
||||
font-family: monospace;
|
||||
}
|
||||
.copy-icon {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="bg-gray-900 text-gray-200">
|
||||
<div class="flex flex-col min-h-screen" x-data="networkClusters()" x-init="init()">
|
||||
{{template "views/partials/navbar_explorer" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar_explorer" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="networkClusters()" x-init="init()">
|
||||
<div class="animation-container">
|
||||
<canvas id="networkCanvas"></canvas>
|
||||
<div class="text-overlay">
|
||||
@@ -178,8 +186,8 @@
|
||||
|
||||
<div class="container mx-auto px-4 flex-grow">
|
||||
<!-- Warning Box -->
|
||||
<div class="warning-box bg-yellow-100 text-gray-800 mb-20 pt-5 pb-5 pr-5 pl-5 text-lg">
|
||||
<i class="fa-solid fa-triangle-exclamation"></i><i class="fa-solid fa-flask"></i>
|
||||
<div class="warning-box bg-[var(--color-warning-light)] border border-[var(--color-warning)]/30 text-[var(--color-text-primary)] mb-20 pt-5 pb-5 pr-5 pl-5 text-lg rounded-lg">
|
||||
<i class="fa-solid fa-triangle-exclamation text-[var(--color-warning)]"></i><i class="fa-solid fa-flask text-[var(--color-warning)]"></i>
|
||||
The explorer is a global, community-driven tool to share network tokens and view available clusters in the globe.
|
||||
Anyone can use the tokens to offload computation and use the clusters available or share resources.
|
||||
This is provided without any warranty. Use it at your own risk. We are not responsible for any potential harm or misuse. Sharing tokens globally allows anyone from the internet to use your instances.
|
||||
@@ -259,19 +267,19 @@
|
||||
<span class="inline-block bg-blue-500 text-white py-1 px-3 rounded-full text-xs" x-text="'Number of Workers: ' + cluster.Workers.length">
|
||||
</span>
|
||||
<!-- Give commands and instructions to join the network -->
|
||||
<span class="inline-block token-box text-white py-1 px-3 text-xs" x-show="cluster.Type == 'federated'" >
|
||||
<p class="text-lg font-bold mb-4 mt-1">
|
||||
<i class="fa-solid fa-copy copy-icon float-right"></i>
|
||||
Command to connect (click to copy):
|
||||
</p>
|
||||
<code class="block bg-gray-700 text-yellow-300 p-4 rounded-lg break-words" @click="copyToken($el.textContent)" >
|
||||
docker run -d --restart=always -e ADDRESS=":80" -e LOCALAI_P2P_NETWORK_ID=<span class="token" x-text="cluster.NetworkID"></span> -e LOCALAI_P2P_LOGLEVEL=debug --name local-ai -e TOKEN="<span class="token" x-text="network.token"></span>" --net host -ti localai/localai:master federated --debug
|
||||
</code>
|
||||
or via CLI:
|
||||
<code class="block bg-gray-700 text-yellow-300 p-4 rounded-lg break-words" @click="copyToken($el.textContent)" >
|
||||
ADDRESS=":80" LOCALAI_P2P_NETWORK_ID=<span class="token" x-text="cluster.NetworkID"></span> LOCALAI_P2P_LOGLEVEL=debug TOKEN="<span class="token" x-text="network.token"></span>" local-ai federated --debug
|
||||
</code>
|
||||
</span>
|
||||
<span class="inline-block token-box text-white py-1 px-3 text-xs" x-show="cluster.Type == 'federated'" >
|
||||
<p class="text-lg font-bold mb-4 mt-1">
|
||||
<i class="fa-solid fa-copy copy-icon float-right"></i>
|
||||
Command to connect (click to copy):
|
||||
</p>
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words border border-[var(--color-border-subtle)]" @click="copyToken($el.textContent)" >
|
||||
docker run -d --restart=always -e ADDRESS=":80" -e LOCALAI_P2P_NETWORK_ID=<span class="token" x-text="cluster.NetworkID"></span> -e LOCALAI_P2P_LOGLEVEL=debug --name local-ai -e TOKEN="<span class="token" x-text="network.token"></span>" --net host -ti localai/localai:master federated --debug
|
||||
</code>
|
||||
or via CLI:
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words border border-[var(--color-border-subtle)]" @click="copyToken($el.textContent)" >
|
||||
ADDRESS=":80" LOCALAI_P2P_NETWORK_ID=<span class="token" x-text="cluster.NetworkID"></span> LOCALAI_P2P_LOGLEVEL=debug TOKEN="<span class="token" x-text="network.token"></span>" local-ai federated --debug
|
||||
</code>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
@@ -371,6 +379,8 @@
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
{{template "views/partials/head" .}}
|
||||
<script defer src="static/image.js"></script>
|
||||
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] flex flex-col h-screen">
|
||||
<div class="flex flex-col flex-1 overflow-hidden">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner h-screen flex flex-col">
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<!-- Two Column Layout: Settings on Left, Preview on Right -->
|
||||
<div class="flex flex-col lg:flex-row flex-1 gap-4 p-4 overflow-hidden">
|
||||
@@ -237,6 +239,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<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="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
|
||||
<!-- Main Content - ChatGPT-style minimal interface -->
|
||||
<div class="flex-1 flex flex-col items-center justify-center px-4 py-12">
|
||||
@@ -524,6 +526,8 @@
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -2,14 +2,16 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow flex items-center justify-center">
|
||||
<!-- Auth Card -->
|
||||
<div class="max-w-md w-full bg-[#1E293B] border border-[#38BDF8]/20 rounded-xl overflow-hidden">
|
||||
<div class="max-w-md w-full bg-[var(--color-bg-secondary)] border border-[var(--color-border-subtle)] rounded-xl overflow-hidden">
|
||||
<div class="animation-container">
|
||||
<div class="text-overlay">
|
||||
<img src="static/logo.png" alt="LocalAI Logo" class="h-32 drop-shadow-[0_0_15px_rgba(56,189,248,0.3)]">
|
||||
@@ -21,22 +23,22 @@
|
||||
<h2 class="h2">
|
||||
Authorization Required
|
||||
</h2>
|
||||
<p class="text-[#94A3B8] mt-2">Please enter your access token to continue</p>
|
||||
<p class="text-[var(--color-text-secondary)] mt-2">Please enter your access token to continue</p>
|
||||
</div>
|
||||
|
||||
<form id="login-form" class="space-y-6" onsubmit="login(); return false;">
|
||||
<div>
|
||||
<label for="token" class="block text-sm font-medium text-[#94A3B8] mb-2">Access Token</label>
|
||||
<label for="token" class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">Access Token</label>
|
||||
<div class="relative">
|
||||
<div class="absolute inset-y-0 start-0 flex items-center ps-4 pointer-events-none z-10">
|
||||
<i class="fas fa-key text-[#38BDF8]"></i>
|
||||
<i class="fas fa-key text-[var(--color-primary)]"></i>
|
||||
</div>
|
||||
<input
|
||||
type="password"
|
||||
id="token"
|
||||
name="token"
|
||||
placeholder="Enter your token"
|
||||
class="bg-[#101827] border border-[#1E293B] text-[#E5E7EB] placeholder-[#94A3B8] text-sm rounded-lg focus:ring-[#38BDF8] focus:border-[#38BDF8] focus:ring-2 block w-full p-2.5 transition-all"
|
||||
class="input"
|
||||
style="padding-left: 3.5rem !important;"
|
||||
required
|
||||
/>
|
||||
@@ -46,7 +48,7 @@
|
||||
<div>
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full flex items-center justify-center bg-[#38BDF8] hover:bg-[#38BDF8]/90 text-[#101827] font-semibold py-3 px-6 rounded-lg transition-colors"
|
||||
class="btn-primary w-full"
|
||||
>
|
||||
<i class="fas fa-sign-in-alt mr-2"></i>
|
||||
<span>Login</span>
|
||||
@@ -54,9 +56,9 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="mt-8 pt-6 border-t border-[#1E293B] text-center text-sm text-[#94A3B8]">
|
||||
<div class="mt-8 pt-6 border-t border-[var(--color-border-subtle)] text-center text-sm text-[var(--color-text-secondary)]">
|
||||
<div class="flex items-center justify-center mb-2">
|
||||
<i class="fas fa-shield-alt mr-2 text-[#38BDF8]"></i>
|
||||
<i class="fas fa-shield-alt mr-2 text-[var(--color-primary)]"></i>
|
||||
<span>Instance is token protected</span>
|
||||
</div>
|
||||
<p>Current time (UTC): <span id="current-time">{{.CurrentDate}}</span></p>
|
||||
@@ -66,6 +68,8 @@
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<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" .}}
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="indexDashboard()">
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="fixed top-20 right-4 z-50 space-y-2" style="max-width: 400px;">
|
||||
@@ -869,6 +871,10 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,10 +2,12 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen" x-data="importModel()" x-init="init()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="importModel()" x-init="init()">
|
||||
{{template "views/partials/inprogress" .}}
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
@@ -64,9 +66,9 @@
|
||||
x-transition:enter-end="opacity-100"
|
||||
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">
|
||||
<i class="fas fa-link text-green-400"></i>
|
||||
<h2 class="text-2xl font-semibold text-[var(--color-text-primary)] flex items-center gap-3 mb-6">
|
||||
<div class="w-10 h-10 rounded-lg bg-[var(--color-success-light)] flex items-center justify-center">
|
||||
<i class="fas fa-link text-[var(--color-success)]"></i>
|
||||
</div>
|
||||
Import from URI
|
||||
</h2>
|
||||
@@ -74,20 +76,20 @@
|
||||
<!-- URI Input -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<label class="block text-sm font-medium text-[#94A3B8]">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-link mr-2"></i>Model URI
|
||||
</label>
|
||||
<div class="flex gap-2">
|
||||
<a href="https://huggingface.co/models?search=gguf&sort=trending"
|
||||
target="_blank"
|
||||
class="text-xs px-3 py-1.5 rounded-lg bg-purple-600/20 hover:bg-purple-600/30 text-purple-300 border border-purple-500/30 transition-all flex items-center gap-1.5">
|
||||
class="text-xs px-3 py-1.5 rounded-lg bg-[var(--color-accent-light)] hover:bg-[var(--color-accent)]/30 text-[var(--color-accent)] border border-[var(--color-accent)]/30 transition-all flex items-center gap-1.5">
|
||||
<i class="fab fa-huggingface"></i>
|
||||
<span>Search GGUF Models on Hugging Face</span>
|
||||
<i class="fas fa-external-link-alt text-xs"></i>
|
||||
</a>
|
||||
<a href="https://huggingface.co/models?sort=trending"
|
||||
target="_blank"
|
||||
class="text-xs px-3 py-1.5 rounded-lg bg-purple-600/20 hover:bg-purple-600/30 text-purple-300 border border-purple-500/30 transition-all flex items-center gap-1.5">
|
||||
class="text-xs px-3 py-1.5 rounded-lg bg-[var(--color-accent-light)] hover:bg-[var(--color-accent)]/30 text-[var(--color-accent)] border border-[var(--color-accent)]/30 transition-all flex items-center gap-1.5">
|
||||
<i class="fab fa-huggingface"></i>
|
||||
<span>Browse All Models on Hugging Face</span>
|
||||
<i class="fas fa-external-link-alt text-xs"></i>
|
||||
@@ -100,14 +102,14 @@
|
||||
placeholder="huggingface://TheBloke/Llama-2-7B-Chat-GGUF or https://example.com/model.gguf"
|
||||
class="input w-full"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-2 text-xs text-[#94A3B8]">
|
||||
<p class="mt-2 text-xs text-[var(--color-text-secondary)]">
|
||||
Enter the URI or path to the model file you want to import
|
||||
</p>
|
||||
|
||||
<!-- URI Format Guide -->
|
||||
<div class="mt-4" x-data="{ showGuide: false }">
|
||||
<button @click="showGuide = !showGuide"
|
||||
class="flex items-center gap-2 text-sm text-[#94A3B8] hover:text-[#E5E7EB] transition-colors">
|
||||
class="flex items-center gap-2 text-sm text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors">
|
||||
<i class="fas" :class="showGuide ? 'fa-chevron-down' : 'fa-chevron-right'"></i>
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<span>Supported URI Formats</span>
|
||||
@@ -117,34 +119,34 @@
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 transform -translate-y-2"
|
||||
x-transition:enter-end="opacity-100 transform translate-y-0"
|
||||
class="mt-3 p-4 bg-[#101827] border border-[#1E293B] rounded-lg space-y-4">
|
||||
class="mt-3 p-4 bg-[var(--color-bg-primary)] border border-[var(--color-border-subtle)] rounded-lg space-y-4">
|
||||
|
||||
<!-- HuggingFace -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<i class="fab fa-huggingface text-purple-400"></i>
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fab fa-huggingface text-[var(--color-accent)]"></i>
|
||||
HuggingFace
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">huggingface://</code><span class="text-[#94A3B8]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Standard HuggingFace format</p>
|
||||
<code class="text-[var(--color-success)]">huggingface://</code><span class="text-[var(--color-text-secondary)]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Standard HuggingFace format</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">hf://</code><span class="text-[#94A3B8]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Short HuggingFace format</p>
|
||||
<code class="text-[var(--color-success)]">hf://</code><span class="text-[var(--color-text-secondary)]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Short HuggingFace format</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">https://huggingface.co/</code><span class="text-[#94A3B8]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Full HuggingFace URL</p>
|
||||
<code class="text-[var(--color-success)]">https://huggingface.co/</code><span class="text-[var(--color-text-secondary)]">TheBloke/Llama-2-7B-Chat-GGUF</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Full HuggingFace URL</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -152,16 +154,16 @@
|
||||
|
||||
<!-- HTTP/HTTPS -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-globe text-blue-400"></i>
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-globe text-[var(--color-primary)]"></i>
|
||||
HTTP/HTTPS URLs
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">https://</code><span class="text-[#94A3B8]">example.com/model.gguf</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Direct download from any HTTPS URL</p>
|
||||
<code class="text-[var(--color-success)]">https://</code><span class="text-[var(--color-text-secondary)]">example.com/model.gguf</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Direct download from any HTTPS URL</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -169,23 +171,23 @@
|
||||
|
||||
<!-- Local Files -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-file text-yellow-400"></i>
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-file text-[var(--color-warning)]"></i>
|
||||
Local Files
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">file://</code><span class="text-[#94A3B8]">/path/to/model.gguf</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Local file path (absolute)</p>
|
||||
<code class="text-[var(--color-success)]">file://</code><span class="text-[var(--color-text-secondary)]">/path/to/model.gguf</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Local file path (absolute)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#94A3B8]">/path/to/model.yaml</code>
|
||||
<p class="text-[#6B7280] mt-0.5">Direct local YAML config file</p>
|
||||
<span class="text-[var(--color-text-secondary)]">/path/to/model.yaml</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Direct local YAML config file</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -193,23 +195,23 @@
|
||||
|
||||
<!-- OCI -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-box text-cyan-400"></i>
|
||||
OCI Registry
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">oci://</code><span class="text-[#94A3B8]">registry.example.com/model:tag</span>
|
||||
<p class="text-[#6B7280] mt-0.5">OCI container registry</p>
|
||||
<code class="text-[var(--color-success)]">oci://</code><span class="text-[var(--color-text-secondary)]">registry.example.com/model:tag</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">OCI container registry</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">ocifile://</code><span class="text-[#94A3B8]">/path/to/image.tar</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Local OCI tarball file</p>
|
||||
<code class="text-[var(--color-success)]">ocifile://</code><span class="text-[var(--color-text-secondary)]">/path/to/image.tar</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Local OCI tarball file</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -217,16 +219,16 @@
|
||||
|
||||
<!-- Ollama -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-cube text-indigo-400"></i>
|
||||
Ollama
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#10B981]">ollama://</code><span class="text-[#94A3B8]">llama2:7b</span>
|
||||
<p class="text-[#6B7280] mt-0.5">Ollama model format</p>
|
||||
<code class="text-[var(--color-success)]">ollama://</code><span class="text-[var(--color-text-secondary)]">llama2:7b</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Ollama model format</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -234,31 +236,31 @@
|
||||
|
||||
<!-- YAML Config Files -->
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-[#E5E7EB] mb-2 flex items-center gap-2">
|
||||
<h4 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2 flex items-center gap-2">
|
||||
<i class="fas fa-code text-pink-400"></i>
|
||||
YAML Configuration Files
|
||||
</h4>
|
||||
<div class="space-y-1.5 text-xs text-[#94A3B8] font-mono pl-6">
|
||||
<div class="space-y-1.5 text-xs text-[var(--color-text-secondary)] font-mono pl-6">
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#94A3B8]">https://example.com/model.yaml</code>
|
||||
<p class="text-[#6B7280] mt-0.5">Remote YAML config file</p>
|
||||
<span class="text-[var(--color-text-secondary)]">https://example.com/model.yaml</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Remote YAML config file</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-start gap-2">
|
||||
<span class="text-green-400">•</span>
|
||||
<span class="text-[var(--color-success)]">•</span>
|
||||
<div>
|
||||
<code class="text-[#94A3B8]">file:///path/to/config.yaml</code>
|
||||
<p class="text-[#6B7280] mt-0.5">Local YAML config file</p>
|
||||
<span class="text-[var(--color-text-secondary)]">file:///path/to/config.yaml</span>
|
||||
<p class="text-[var(--color-text-muted)] mt-0.5">Local YAML config file</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-2 mt-3 border-t border-[#1E293B]">
|
||||
<p class="text-xs text-[#6B7280] italic">
|
||||
<i class="fas fa-lightbulb mr-1.5 text-yellow-400"></i>
|
||||
<div class="pt-2 mt-3 border-t border-[var(--color-border-subtle)]">
|
||||
<p class="text-xs text-[var(--color-text-muted)] italic">
|
||||
<i class="fas fa-lightbulb mr-1.5 text-[var(--color-warning)]"></i>
|
||||
Tip: For HuggingFace models, you can use any of the three formats. The system will automatically detect and download the appropriate model files.
|
||||
</p>
|
||||
</div>
|
||||
@@ -269,25 +271,25 @@
|
||||
<!-- Preferences Section -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<label class="block text-sm font-medium text-gray-300">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-cog mr-2"></i>Preferences (Optional)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Common Preferences -->
|
||||
<div class="space-y-4 mb-6 p-4 bg-gray-900/50 rounded-xl border border-gray-700/50">
|
||||
<h3 class="text-sm font-semibold text-gray-300 mb-3 flex items-center">
|
||||
<i class="fas fa-star mr-2 text-yellow-400"></i>Common Preferences
|
||||
<div class="space-y-4 mb-6 p-4 bg-[var(--color-bg-primary)]/50 rounded-xl border border-[var(--color-border-subtle)]/50">
|
||||
<h3 class="text-sm font-semibold text-[var(--color-text-secondary)] mb-3 flex items-center">
|
||||
<i class="fas fa-star mr-2 text-[var(--color-warning)]"></i>Common Preferences
|
||||
</h3>
|
||||
|
||||
<!-- Backend Selection -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-server mr-2"></i>Backend
|
||||
</label>
|
||||
<select
|
||||
x-model="commonPreferences.backend"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<option value="">Auto-detect (based on URI)</option>
|
||||
<option value="llama-cpp">llama-cpp</option>
|
||||
@@ -297,30 +299,30 @@
|
||||
<option value="vllm">vllm</option>
|
||||
<option value="diffusers">diffusers</option>
|
||||
</select>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Force a specific backend. Leave empty to auto-detect from URI.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Model Name -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-tag mr-2"></i>Model Name
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.name"
|
||||
type="text"
|
||||
placeholder="Leave empty to use filename"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Custom name for the model. If empty, the filename will be used.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-align-left mr-2"></i>Description
|
||||
</label>
|
||||
<textarea
|
||||
@@ -329,39 +331,39 @@
|
||||
placeholder="Leave empty to use default description"
|
||||
class="input w-full resize-none"
|
||||
:disabled="isSubmitting"></textarea>
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Custom description for the model. If empty, a default description will be generated.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Quantizations -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-layer-group mr-2"></i>Quantizations
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.quantizations"
|
||||
type="text"
|
||||
placeholder="q4_k_m,q4_k_s,q3_k_m (comma-separated)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Preferred quantizations (comma-separated). Examples: q4_k_m, q4_k_s, q3_k_m, q2_k. Leave empty to use default (q4_k_m).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- MMProj Quantizations -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-image mr-2"></i>MMProj Quantizations
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.mmproj_quantizations"
|
||||
type="text"
|
||||
placeholder="fp16,fp32 (comma-separated)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Preferred MMProj quantizations (comma-separated). Examples: fp16, fp32. Leave empty to use default (fp16).
|
||||
</p>
|
||||
</div>
|
||||
@@ -372,77 +374,77 @@
|
||||
<input
|
||||
x-model="commonPreferences.embeddings"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded bg-gray-900/90 border-gray-700/70 text-green-500 focus:ring-2 focus:ring-green-500/50 focus:outline-none transition-all cursor-pointer"
|
||||
class="w-5 h-5 rounded bg-[var(--color-bg-primary)] border-[var(--color-border-subtle)] text-[var(--color-success)] focus:ring-2 focus:ring-[var(--color-success)]/50 focus:outline-none transition-all cursor-pointer"
|
||||
:disabled="isSubmitting">
|
||||
<span class="ml-3 text-sm font-medium text-gray-300">
|
||||
<span class="ml-3 text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-vector-square mr-2"></i>Embeddings
|
||||
</span>
|
||||
</label>
|
||||
<p class="mt-1 ml-8 text-xs text-gray-400">
|
||||
<p class="mt-1 ml-8 text-xs text-[var(--color-text-muted)]">
|
||||
Enable embeddings support for this model.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Model Type -->
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-tag mr-2"></i>Model Type
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.type"
|
||||
type="text"
|
||||
placeholder="AutoModelForCausalLM (for transformers backend)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Model type for transformers backend. Examples: AutoModelForCausalLM, SentenceTransformer, Mamba, MusicgenForConditionalGeneration. Leave empty to use default (AutoModelForCausalLM).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Pipeline Type (Diffusers) -->
|
||||
<div x-show="commonPreferences.backend === 'diffusers'">
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-stream mr-2"></i>Pipeline Type
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.pipeline_type"
|
||||
type="text"
|
||||
placeholder="StableDiffusionPipeline (for diffusers backend)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Pipeline type for diffusers backend. Examples: StableDiffusionPipeline, StableDiffusion3Pipeline, FluxPipeline. Leave empty to use default (StableDiffusionPipeline).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Scheduler Type (Diffusers) -->
|
||||
<div x-show="commonPreferences.backend === 'diffusers'">
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-clock mr-2"></i>Scheduler Type
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.scheduler_type"
|
||||
type="text"
|
||||
placeholder="k_dpmpp_2m (optional)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Scheduler type for diffusers backend. Examples: k_dpmpp_2m, euler_a, ddim. Leave empty to use model default.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Enable Parameters (Diffusers) -->
|
||||
<div x-show="commonPreferences.backend === 'diffusers'">
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)] mb-2">
|
||||
<i class="fas fa-cogs mr-2"></i>Enable Parameters
|
||||
</label>
|
||||
<input
|
||||
x-model="commonPreferences.enable_parameters"
|
||||
type="text"
|
||||
placeholder="negative_prompt,num_inference_steps (comma-separated)"
|
||||
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"
|
||||
class="input w-full px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<p class="mt-1 text-xs text-gray-400">
|
||||
<p class="mt-1 text-xs text-[var(--color-text-muted)]">
|
||||
Enabled parameters for diffusers backend (comma-separated). Leave empty to use default (negative_prompt,num_inference_steps).
|
||||
</p>
|
||||
</div>
|
||||
@@ -453,13 +455,13 @@
|
||||
<input
|
||||
x-model="commonPreferences.cuda"
|
||||
type="checkbox"
|
||||
class="w-5 h-5 rounded bg-gray-900/90 border-gray-700/70 text-green-500 focus:ring-2 focus:ring-green-500/50 focus:outline-none transition-all cursor-pointer"
|
||||
class="w-5 h-5 rounded bg-[var(--color-bg-primary)] border-[var(--color-border-subtle)] text-[var(--color-success)] focus:ring-2 focus:ring-[var(--color-success)]/50 focus:outline-none transition-all cursor-pointer"
|
||||
:disabled="isSubmitting">
|
||||
<span class="ml-3 text-sm font-medium text-gray-300">
|
||||
<span class="ml-3 text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-microchip mr-2"></i>CUDA
|
||||
</span>
|
||||
</label>
|
||||
<p class="mt-1 ml-8 text-xs text-gray-400">
|
||||
<p class="mt-1 ml-8 text-xs text-[var(--color-text-muted)]">
|
||||
Enable CUDA support for GPU acceleration with diffusers backend.
|
||||
</p>
|
||||
</div>
|
||||
@@ -468,12 +470,12 @@
|
||||
<!-- Custom Preferences -->
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<label class="block text-sm font-medium text-gray-300">
|
||||
<label class="block text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-sliders-h mr-2"></i>Custom Preferences
|
||||
</label>
|
||||
<button @click="addPreference()"
|
||||
:disabled="isSubmitting"
|
||||
class="text-sm px-3 py-1.5 rounded-lg bg-green-600/20 hover:bg-green-600/30 text-green-300 border border-green-500/30 transition-all">
|
||||
class="text-sm px-3 py-1.5 rounded-lg bg-[var(--color-success-light)] hover:bg-[var(--color-success)]/30 text-[var(--color-success)] border border-[var(--color-success)]/30 transition-all">
|
||||
<i class="fas fa-plus mr-1"></i>Add Custom
|
||||
</button>
|
||||
</div>
|
||||
@@ -485,24 +487,24 @@
|
||||
x-model="pref.key"
|
||||
type="text"
|
||||
placeholder="Key"
|
||||
class="flex-1 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"
|
||||
class="flex-1 input px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<span class="text-gray-400">:</span>
|
||||
<span class="text-[var(--color-text-secondary)]">:</span>
|
||||
<input
|
||||
x-model="pref.value"
|
||||
type="text"
|
||||
placeholder="Value"
|
||||
class="flex-1 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"
|
||||
class="flex-1 input px-4 py-2"
|
||||
:disabled="isSubmitting">
|
||||
<button @click="removePreference(index)"
|
||||
:disabled="isSubmitting"
|
||||
class="px-3 py-2 rounded-lg bg-red-600/20 hover:bg-red-600/30 text-red-300 border border-red-500/30 transition-all">
|
||||
class="px-3 py-2 rounded-lg bg-[var(--color-error-light)] hover:bg-[var(--color-error)]/30 text-[var(--color-error)] border border-[var(--color-error)]/30 transition-all">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-400">
|
||||
<p class="mt-2 text-xs text-[var(--color-text-muted)]">
|
||||
Add custom key-value pairs for advanced configuration
|
||||
</p>
|
||||
</div>
|
||||
@@ -515,19 +517,19 @@
|
||||
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 overflow-hidden h-[calc(100vh-250px)]">
|
||||
<div class="sticky top-0 bg-[#1E293B] border-b border-[#101827] p-6 flex items-center justify-between z-10">
|
||||
<h2 class="text-xl font-semibold text-[#E5E7EB] flex items-center gap-3">
|
||||
class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-xl overflow-hidden h-[calc(100vh-250px)]">
|
||||
<div class="sticky top-0 bg-[var(--color-bg-secondary)] border-b border-[var(--color-border-subtle)] p-6 flex items-center justify-between z-10">
|
||||
<h2 class="text-xl font-semibold text-[var(--color-text-primary)] flex items-center gap-3">
|
||||
<div class="w-8 h-8 rounded-lg bg-fuchsia-500/10 flex items-center justify-center">
|
||||
<i class="fas fa-code text-fuchsia-400"></i>
|
||||
</div>
|
||||
YAML Configuration Editor
|
||||
</h2>
|
||||
<div class="flex items-center gap-3">
|
||||
<button id="formatYamlBtn" class="text-[#94A3B8] hover:text-[#E5E7EB] text-sm px-3 py-1.5 rounded-lg hover:bg-[#101827] transition-colors">
|
||||
<button id="formatYamlBtn" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] text-sm px-3 py-1.5 rounded-lg hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<i class="fas fa-indent mr-1.5"></i> Format
|
||||
</button>
|
||||
<button id="copyYamlBtn" class="text-[#94A3B8] hover:text-[#E5E7EB] text-sm px-3 py-1.5 rounded-lg hover:bg-[#101827] transition-colors">
|
||||
<button id="copyYamlBtn" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] text-sm px-3 py-1.5 rounded-lg hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<i class="fas fa-copy mr-1.5"></i> Copy
|
||||
</button>
|
||||
</div>
|
||||
@@ -553,8 +555,8 @@
|
||||
<style>
|
||||
/* Enhanced CodeMirror styling */
|
||||
.CodeMirror {
|
||||
background: linear-gradient(135deg, #111827 0%, #1f2937 100%) !important;
|
||||
color: #e5e7eb !important;
|
||||
background: var(--color-bg-primary) !important;
|
||||
color: var(--color-text-primary) !important;
|
||||
border: none !important;
|
||||
height: 100% !important;
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Monaco', 'Consolas', monospace !important;
|
||||
@@ -564,7 +566,7 @@
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
border-left: 2px solid #a78bfa !important;
|
||||
border-left: 2px solid var(--color-accent) !important;
|
||||
animation: blink 1s infinite;
|
||||
}
|
||||
|
||||
@@ -574,20 +576,20 @@
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
background: linear-gradient(135deg, #1f2937 0%, #374151 100%) !important;
|
||||
border-right: 1px solid rgba(75, 85, 99, 0.5) !important;
|
||||
color: #9ca3af !important;
|
||||
background: var(--color-bg-secondary) !important;
|
||||
border-right: 1px solid var(--color-border-subtle) !important;
|
||||
color: var(--color-text-secondary) !important;
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
|
||||
.CodeMirror-linenumber {
|
||||
color: #6b7280 !important;
|
||||
color: var(--color-text-muted) !important;
|
||||
padding: 0 8px 0 4px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.CodeMirror-activeline-background {
|
||||
background: rgba(139, 92, 246, 0.1) !important;
|
||||
background: var(--color-accent-light) !important;
|
||||
}
|
||||
|
||||
.CodeMirror-selected {
|
||||
@@ -614,27 +616,27 @@
|
||||
.cm-keyword { color: #8b5cf6 !important; font-weight: 600 !important; }
|
||||
.cm-string { color: #10b981 !important; }
|
||||
.cm-number { color: #f59e0b !important; }
|
||||
.cm-comment { color: #6b7280 !important; font-style: italic !important; }
|
||||
.cm-comment { color: var(--color-text-muted) !important; font-style: italic !important; }
|
||||
.cm-property { color: #ec4899 !important; }
|
||||
.cm-operator { color: #ef4444 !important; }
|
||||
.cm-variable { color: #06b6d4 !important; }
|
||||
.cm-tag { color: #8b5cf6 !important; font-weight: 600 !important; }
|
||||
.cm-attribute { color: #f59e0b !important; }
|
||||
.cm-def { color: #ec4899 !important; font-weight: 600 !important; }
|
||||
.cm-bracket { color: #d1d5db !important; }
|
||||
.cm-punctuation { color: #d1d5db !important; }
|
||||
.cm-bracket { color: var(--color-text-secondary) !important; }
|
||||
.cm-punctuation { color: var(--color-text-secondary) !important; }
|
||||
.cm-quote { color: #10b981 !important; }
|
||||
.cm-meta { color: #6b7280 !important; }
|
||||
.cm-meta { color: var(--color-text-muted) !important; }
|
||||
.cm-builtin { color: #f472b6 !important; }
|
||||
.cm-atom { color: #f59e0b !important; }
|
||||
|
||||
/* Enhanced scrollbar styling */
|
||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
background: #1f2937 !important;
|
||||
background: var(--color-bg-secondary) !important;
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar {
|
||||
background: #1f2937 !important;
|
||||
background: var(--color-bg-secondary) !important;
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar, .CodeMirror-hscrollbar::-webkit-scrollbar {
|
||||
@@ -643,17 +645,17 @@
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar-track, .CodeMirror-hscrollbar::-webkit-scrollbar-track {
|
||||
background: #1f2937;
|
||||
background: var(--color-bg-secondary);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar-thumb, .CodeMirror-hscrollbar::-webkit-scrollbar-thumb {
|
||||
background: linear-gradient(135deg, #6b7280 0%, #9ca3af 100%);
|
||||
background: var(--color-text-muted);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar-thumb:hover, .CodeMirror-hscrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: linear-gradient(135deg, #9ca3af 0%, #d1d5db 100%);
|
||||
background: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Focus ring styling */
|
||||
@@ -682,27 +684,27 @@
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(5, 150, 105, 0.1) 100%);
|
||||
border-color: rgba(16, 185, 129, 0.3);
|
||||
color: #10b981;
|
||||
background: var(--color-success-light);
|
||||
border-color: var(--color-success);
|
||||
color: var(--color-success);
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(220, 38, 38, 0.1) 100%);
|
||||
border-color: rgba(239, 68, 68, 0.3);
|
||||
color: #ef4444;
|
||||
background: var(--color-error-light);
|
||||
border-color: var(--color-error);
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(217, 119, 6, 0.1) 100%);
|
||||
border-color: rgba(245, 158, 11, 0.3);
|
||||
color: #f59e0b;
|
||||
background: var(--color-warning-light);
|
||||
border-color: var(--color-warning);
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(37, 99, 235, 0.1) 100%);
|
||||
border-color: rgba(59, 130, 246, 0.3);
|
||||
color: #3b82f6;
|
||||
background: var(--color-info-light);
|
||||
border-color: var(--color-info);
|
||||
color: var(--color-info);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1180,5 +1182,10 @@ parameters:
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<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" .}}
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="modelsGallery()">
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="fixed top-20 right-4 z-50 space-y-2" style="max-width: 400px;">
|
||||
@@ -190,75 +192,75 @@
|
||||
</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="bg-[var(--color-bg-secondary)] rounded-2xl border border-[var(--color-border-subtle)] overflow-hidden shadow-xl backdrop-blur-sm">
|
||||
<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>
|
||||
<tr class="bg-[var(--color-primary-light)] border-b border-[var(--color-border-subtle)]">
|
||||
<th class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider">Icon</th>
|
||||
<th @click="setSort('name')"
|
||||
:class="sortBy === 'name' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'name' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Model Name</span>
|
||||
<i :class="sortBy === 'name' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'name' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'name' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</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-[var(--color-text-primary)] uppercase tracking-wider">Description</th>
|
||||
<th @click="setSort('repository')"
|
||||
:class="sortBy === 'repository' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'repository' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Repository</span>
|
||||
<i :class="sortBy === 'repository' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'repository' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'repository' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="setSort('license')"
|
||||
:class="sortBy === 'license' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'license' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>License</span>
|
||||
<i :class="sortBy === 'license' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'license' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'license' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th @click="setSort('status')"
|
||||
:class="sortBy === 'status' ? 'bg-[#38BDF8]/20' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider cursor-pointer hover:bg-[#38BDF8]/10 transition-colors">
|
||||
:class="sortBy === 'status' ? 'bg-[var(--color-primary-light)]' : ''"
|
||||
class="px-6 py-4 text-left text-xs font-semibold text-[var(--color-text-primary)] uppercase tracking-wider cursor-pointer hover:bg-[var(--color-bg-primary)] transition-colors">
|
||||
<div class="flex items-center gap-2">
|
||||
<span>Status</span>
|
||||
<i :class="sortBy === 'status' ? (sortOrder === 'asc' ? 'fas fa-sort-up' : 'fas fa-sort-down') : 'fas fa-sort'"
|
||||
:class="sortBy === 'status' ? 'text-[#38BDF8]' : 'text-[#94A3B8]'"
|
||||
:class="sortBy === 'status' ? 'text-[var(--color-primary)]' : 'text-[var(--color-text-secondary)]'"
|
||||
class="text-xs"></i>
|
||||
</div>
|
||||
</th>
|
||||
<th class="px-6 py-4 text-right text-xs font-semibold text-[#E5E7EB] uppercase tracking-wider">Actions</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">
|
||||
<tbody class="divide-y divide-[var(--color-border-subtle)]">
|
||||
<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-bg-primary)] 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-border-subtle)] 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-[#8B5CF6]"></i>
|
||||
<i x-show="!model.icon" class="fas fa-brain text-xl text-[var(--color-accent)]"></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">
|
||||
<i class="fa-solid fa-circle-exclamation mr-1"></i>
|
||||
@@ -270,12 +272,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-light)] text-[var(--color-text-primary)] border border-[var(--color-primary-border)]">
|
||||
<i class="fa-brands fa-git-alt mr-1"></i>
|
||||
<span x-text="model.gallery"></span>
|
||||
</span>
|
||||
@@ -283,21 +285,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-light)] text-[var(--color-text-primary)] border border-[var(--color-accent)]/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">
|
||||
@@ -307,7 +309,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="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-success-light)] text-[var(--color-success)] border border-[var(--color-success)]/30">
|
||||
<i class="fas fa-check-circle mr-1"></i>
|
||||
Installed
|
||||
</span>
|
||||
@@ -315,7 +317,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="inline-flex items-center text-xs px-2 py-1 rounded bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] border border-[var(--color-border-subtle)]">
|
||||
<i class="fas fa-circle mr-1"></i>
|
||||
Not Installed
|
||||
</span>
|
||||
@@ -327,7 +329,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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-bg-primary)] hover:bg-[var(--color-primary-light)] text-xs font-medium text-[var(--color-text-primary)] transition duration-200 border border-[var(--color-border-subtle)]"
|
||||
title="View details">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
</button>
|
||||
@@ -336,12 +338,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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-xs font-medium text-white transition duration-200"
|
||||
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)]/80 text-xs font-medium text-white transition duration-200"
|
||||
title="Delete">
|
||||
<i class="fa-solid fa-trash"></i>
|
||||
</button>
|
||||
@@ -352,12 +354,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-[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"
|
||||
class="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-accent-light)] hover:bg-[var(--color-accent)]/30 text-xs font-medium text-[var(--color-text-primary)] transition duration-200 border border-[var(--color-accent)]/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="inline-flex items-center px-3 py-1.5 rounded-lg bg-[var(--color-primary)] hover:bg-[var(--color-primary-hover)] text-xs font-medium text-white transition duration-200"
|
||||
title="Install">
|
||||
<i class="fa-solid fa-download"></i>
|
||||
</button>
|
||||
@@ -376,15 +378,15 @@
|
||||
<div x-show="selectedModel"
|
||||
x-transition
|
||||
@click.away="closeModal()"
|
||||
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-gray-900/50"
|
||||
class="fixed top-0 right-0 left-0 z-50 flex justify-center items-center w-full md:inset-0 h-full max-h-full bg-black/50"
|
||||
style="display: none;">
|
||||
<div class="relative p-4 w-full max-w-2xl h-[90vh] mx-auto mt-[5vh]">
|
||||
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 h-full flex flex-col">
|
||||
<div class="relative bg-[var(--color-bg-secondary)] rounded-lg shadow h-full flex flex-col border border-[var(--color-border-subtle)]">
|
||||
<!-- Modal Header -->
|
||||
<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>
|
||||
<div class="flex items-center justify-between p-4 md:p-5 border-b border-[var(--color-border-subtle)] rounded-t">
|
||||
<h3 class="text-xl font-semibold text-[var(--color-text-primary)]" x-text="selectedModel?.name"></h3>
|
||||
<button @click="closeModal()"
|
||||
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">
|
||||
class="text-[var(--color-text-secondary)] bg-transparent hover:bg-[var(--color-bg-primary)] hover:text-[var(--color-text-primary)] rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center transition-colors">
|
||||
<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>
|
||||
@@ -394,23 +396,23 @@
|
||||
<!-- Modal Body -->
|
||||
<div class="p-4 md:p-5 space-y-4 overflow-y-auto flex-1 min-h-0">
|
||||
<div class="flex justify-center items-center">
|
||||
<div class="w-48 h-48 rounded-lg border border-gray-300 dark:border-gray-600 flex items-center justify-center bg-gray-100 dark:bg-gray-800 mt-3">
|
||||
<div class="w-48 h-48 rounded-lg border border-[var(--color-border-subtle)] flex items-center justify-center bg-[var(--color-bg-primary)] mt-3">
|
||||
<img x-show="selectedModel?.icon"
|
||||
: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-[var(--color-text-secondary)] dark:text-[var(--color-text-muted)]"></i>
|
||||
<i x-show="!selectedModel?.icon" class="fas fa-brain text-6xl text-[var(--color-text-muted)]"></i>
|
||||
</div>
|
||||
</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>
|
||||
<div class="text-base leading-relaxed 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>
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white mb-2">Links</p>
|
||||
<p class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Links</p>
|
||||
<ul>
|
||||
<template x-for="url in selectedModel.urls" :key="url">
|
||||
<li>
|
||||
<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)]">
|
||||
<a :href="url" target="_blank" class="text-base leading-relaxed text-[var(--color-text-secondary)] hover:text-[var(--color-primary)]">
|
||||
<i class="fas fa-link pr-2"></i>
|
||||
<span x-text="url"></span>
|
||||
</a>
|
||||
@@ -421,11 +423,11 @@
|
||||
</template>
|
||||
<template x-if="selectedModel?.additionalFiles && selectedModel.additionalFiles.length > 0">
|
||||
<div>
|
||||
<p class="text-sm font-semibold text-gray-900 dark:text-white mb-2">Files</p>
|
||||
<p class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Files</p>
|
||||
<ul>
|
||||
<template x-for="file in selectedModel.additionalFiles" :key="file">
|
||||
<li class="mb-0">
|
||||
<p class="text-base leading-tight text-[var(--color-text-muted)] dark:text-[var(--color-text-secondary)]">
|
||||
<p class="text-base leading-tight text-[var(--color-text-secondary)]">
|
||||
<i class="fas fa-file pr-2"></i>
|
||||
<span x-text="file.filename"></span>
|
||||
</p>
|
||||
@@ -436,11 +438,11 @@
|
||||
</template>
|
||||
<template x-if="selectedModel?.tags && selectedModel.tags.length > 0">
|
||||
<div>
|
||||
<p class="text-sm mb-3 font-semibold text-gray-900 dark:text-white">Tags</p>
|
||||
<p class="text-sm mb-3 font-semibold text-[var(--color-text-primary)]">Tags</p>
|
||||
<div class="flex flex-row flex-wrap content-center">
|
||||
<template x-for="tag in selectedModel.tags" :key="tag">
|
||||
<a :href="'browse?term=' + tag"
|
||||
class="inline-flex items-center text-xs px-3 py-1 rounded-full bg-gray-700/60 text-gray-300 border border-gray-600/50 hover:bg-gray-600 hover:text-gray-100 transition duration-200 ease-in-out mr-2 mb-2">
|
||||
class="inline-flex items-center text-xs px-3 py-1 rounded-full bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] border border-[var(--color-border-subtle)] hover:bg-[var(--color-primary-light)] hover:text-[var(--color-text-primary)] transition duration-200 ease-in-out mr-2 mb-2">
|
||||
<i class="fas fa-tag pr-2"></i>
|
||||
<span x-text="tag"></span>
|
||||
</a>
|
||||
@@ -450,9 +452,9 @@
|
||||
</template>
|
||||
</div>
|
||||
<!-- Modal Footer -->
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-gray-200 rounded-b dark:border-gray-600">
|
||||
<div class="flex items-center p-4 md:p-5 border-t border-[var(--color-border-subtle)] rounded-b">
|
||||
<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-[var(--color-text-secondary)] dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
||||
class="btn-secondary">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
@@ -463,30 +465,29 @@
|
||||
|
||||
<!-- Pagination -->
|
||||
<div x-show="totalPages > 1" class="flex justify-center mt-12">
|
||||
<div class="flex items-center gap-4 bg-gray-800/60 rounded-2xl p-4 backdrop-blur-sm border border-gray-700/50">
|
||||
<div class="flex items-center gap-4 bg-[var(--color-bg-secondary)] rounded-2xl p-4 backdrop-blur-sm border border-[var(--color-border-subtle)]">
|
||||
<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-[var(--color-bg-secondary)] hover:bg-indigo-600 text-[var(--color-text-secondary)] hover:text-white rounded-lg transition-colors">
|
||||
class="flex items-center justify-center h-12 w-12 bg-[var(--color-bg-primary)] hover:bg-[var(--color-primary)] 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">
|
||||
<div class="text-[var(--color-text-primary)] text-sm font-medium px-4">
|
||||
<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-[var(--color-text-primary)] font-bold text-lg mx-2" x-text="currentPage"></span>
|
||||
<span class="text-[var(--color-text-secondary)]">of</span>
|
||||
<span class="text-white font-bold text-lg mx-2" x-text="totalPages"></span>
|
||||
<span class="text-[var(--color-text-primary)] font-bold text-lg mx-2" x-text="totalPages"></span>
|
||||
</div>
|
||||
<button @click="goToPage(currentPage + 1)"
|
||||
:disabled="currentPage >= totalPages"
|
||||
:class="currentPage >= totalPages ? 'opacity-50 cursor-not-allowed' : ''"
|
||||
class="group flex items-center justify-center h-12 w-12 bg-gray-700/80 hover:bg-indigo-600 text-gray-300 hover:text-white rounded-xl shadow-lg transition-all duration-300 ease-in-out transform hover:scale-110">
|
||||
class="group flex items-center justify-center h-12 w-12 bg-[var(--color-bg-primary)] hover:bg-[var(--color-primary)] text-[var(--color-text-secondary)] hover:text-white rounded-xl shadow-lg transition-all duration-300 ease-in-out transform hover:scale-110">
|
||||
<i class="fas fa-chevron-right group-hover:animate-pulse"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
@@ -526,16 +527,16 @@
|
||||
|
||||
/* Table progress bar styling */
|
||||
.progress-table {
|
||||
background: linear-gradient(135deg, rgba(56, 189, 248, 0.2) 0%, rgba(139, 92, 246, 0.2) 100%);
|
||||
background: var(--color-primary-light);
|
||||
border-radius: 0.25rem;
|
||||
border: 1px solid rgba(56, 189, 248, 0.3);
|
||||
border: 1px solid var(--color-primary-border);
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.progress-bar-table {
|
||||
background: linear-gradient(135deg, #38BDF8 0%, #8B5CF6 100%);
|
||||
background: var(--gradient-primary);
|
||||
height: 100%;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
@@ -544,6 +545,7 @@
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
background: var(--color-bg-secondary);
|
||||
}
|
||||
|
||||
tbody tr:last-child td:first-child {
|
||||
@@ -831,5 +833,10 @@ function modelsGallery() {
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen" x-data="p2pNetwork()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="p2pNetwork()">
|
||||
|
||||
{{template "views/partials/inprogress" .}}
|
||||
|
||||
@@ -25,66 +27,66 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-10">
|
||||
<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>
|
||||
<i class="fas fa-network-wired text-[var(--color-primary)] 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>
|
||||
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Instance Federation</h3>
|
||||
<p class="text-xs text-[var(--color-text-secondary)]">Load balance across multiple instances</p>
|
||||
</div>
|
||||
<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>
|
||||
<i class="fas fa-puzzle-piece text-[var(--color-accent)] 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>
|
||||
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Model Sharding</h3>
|
||||
<p class="text-xs text-[var(--color-text-secondary)]">Split large models across workers</p>
|
||||
</div>
|
||||
<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>
|
||||
<i class="fas fa-share-alt text-[var(--color-success)] text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-sm font-semibold text-[#E5E7EB] mb-2">Resource Sharing</h3>
|
||||
<p class="text-xs text-[#94A3B8]">Pool resources from multiple devices</p>
|
||||
<h3 class="text-sm font-semibold text-[var(--color-text-primary)] mb-2">Resource Sharing</h3>
|
||||
<p class="text-xs text-[var(--color-text-secondary)]">Pool resources from multiple devices</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Setup Instructions -->
|
||||
<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>
|
||||
<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 Enable P2P
|
||||
</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-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-[#E5E7EB] font-medium mb-2">Start LocalAI with P2P enabled</p>
|
||||
<code class="block bg-[#1E293B] text-[#38BDF8] p-3 rounded-lg text-sm border border-[#38BDF8]/20">
|
||||
<p class="text-[var(--color-text-primary)] font-medium mb-2">Start LocalAI with P2P enabled</p>
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-primary)] p-3 rounded-lg text-sm border border-[var(--color-primary-border)]/20">
|
||||
local-ai run --p2p
|
||||
</code>
|
||||
<p class="text-[#94A3B8] text-sm mt-2">This will automatically generate a network token for you.</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm mt-2">This will automatically generate a network token for you.</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-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-[#E5E7EB] font-medium mb-2">Or use an existing token</p>
|
||||
<code class="block bg-[#1E293B] text-[#38BDF8] p-3 rounded-lg text-sm border border-[#38BDF8]/20">
|
||||
<p class="text-[var(--color-text-primary)] font-medium mb-2">Or use an existing token</p>
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-primary)] p-3 rounded-lg text-sm border border-[var(--color-primary-border)]/20">
|
||||
export TOKEN="your-token-here"<br>
|
||||
local-ai run --p2p
|
||||
</code>
|
||||
<p class="text-[#94A3B8] text-sm mt-2">If you already have a token from another instance, you can reuse it.</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm mt-2">If you already have a token from another instance, you can reuse it.</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-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-[#E5E7EB] font-medium mb-2">Access the P2P dashboard</p>
|
||||
<p class="text-[#94A3B8] text-sm">Once enabled, refresh this page to see your network token and start connecting nodes.</p>
|
||||
<p class="text-[var(--color-text-primary)] font-medium mb-2">Access the P2P dashboard</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm">Once enabled, refresh this page to see your network token and start connecting nodes.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -92,13 +94,13 @@
|
||||
|
||||
<div class="flex flex-wrap justify-center gap-4">
|
||||
<a href="https://localai.io/features/distribute/" target="_blank"
|
||||
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-book mr-2"></i>
|
||||
Documentation
|
||||
<i class="fas fa-external-link-alt ml-2 text-sm"></i>
|
||||
</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-primary)] hover:bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/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>
|
||||
@@ -118,7 +120,7 @@
|
||||
</h1>
|
||||
<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">
|
||||
<a href="https://localai.io/features/distribute/" target="_blank" class="text-[var(--color-primary)] hover:text-[var(--color-accent)] transition-colors">
|
||||
<i class="fas fa-circle-info ml-2"></i>
|
||||
</a>
|
||||
</p>
|
||||
@@ -132,7 +134,7 @@
|
||||
<h2 class="h2 mb-4">
|
||||
How P2P Distribution Works
|
||||
</h2>
|
||||
<p class="text-lg text-[#94A3B8] max-w-3xl mx-auto">
|
||||
<p class="text-lg text-[var(--color-text-secondary)] max-w-3xl mx-auto">
|
||||
LocalAI leverages cutting-edge peer-to-peer technologies to distribute AI workloads intelligently across your network
|
||||
</p>
|
||||
</div>
|
||||
@@ -140,34 +142,34 @@
|
||||
<!-- Key Features Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<!-- Federation -->
|
||||
<div class="bg-[#101827] rounded-xl p-6 border border-[#38BDF8]/20 transition-colors">
|
||||
<div class="bg-[var(--color-bg-primary)] rounded-xl p-6 border border-[var(--color-primary-border)]/20 transition-colors">
|
||||
<div class="w-12 h-12 bg-blue-500/10 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-network-wired text-blue-400 text-xl"></i>
|
||||
<i class="fas fa-network-wired text-[var(--color-primary)] text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-[#E5E7EB] mb-3">Instance Federation</h3>
|
||||
<p class="text-[#94A3B8] text-sm leading-relaxed">
|
||||
<h3 class="text-xl font-bold text-[var(--color-text-primary)] mb-3">Instance Federation</h3>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm leading-relaxed">
|
||||
Share complete LocalAI instances across your network for load balancing and redundancy. Perfect for scaling across multiple devices.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Model Sharding -->
|
||||
<div class="bg-[#101827] rounded-xl p-6 border border-[#8B5CF6]/20 transition-colors">
|
||||
<div class="bg-[var(--color-bg-primary)] rounded-xl p-6 border border-[var(--color-accent)]/20 transition-colors">
|
||||
<div class="w-12 h-12 bg-purple-500/10 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-puzzle-piece text-purple-400 text-xl"></i>
|
||||
<i class="fas fa-puzzle-piece text-[var(--color-accent)] text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-[#E5E7EB] mb-3">Model Sharding</h3>
|
||||
<p class="text-[#94A3B8] text-sm leading-relaxed">
|
||||
<h3 class="text-xl font-bold text-[var(--color-text-primary)] mb-3">Model Sharding</h3>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm leading-relaxed">
|
||||
Split large model weights across multiple workers. Currently supported with llama.cpp backends for efficient memory usage.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Resource Sharing -->
|
||||
<div class="bg-[#101827] rounded-xl p-6 border border-green-500/20 transition-colors">
|
||||
<div class="bg-[var(--color-bg-primary)] rounded-xl p-6 border border-[var(--color-success)]/20 transition-colors">
|
||||
<div class="w-12 h-12 bg-green-500/10 rounded-lg flex items-center justify-center mb-4">
|
||||
<i class="fas fa-share-alt text-green-400 text-xl"></i>
|
||||
<i class="fas fa-share-alt text-[var(--color-success)] text-xl"></i>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-[#E5E7EB] mb-3">Resource Sharing</h3>
|
||||
<p class="text-[#94A3B8] text-sm leading-relaxed">
|
||||
<h3 class="text-xl font-bold text-[var(--color-text-primary)] mb-3">Resource Sharing</h3>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm leading-relaxed">
|
||||
Pool computational resources from multiple devices, including your friends' machines, to handle larger workloads collaboratively.
|
||||
</p>
|
||||
</div>
|
||||
@@ -176,44 +178,44 @@
|
||||
<!-- Benefits -->
|
||||
<div class="mt-10 grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-blue-400 mb-1">
|
||||
<div class="text-2xl font-bold text-[var(--color-primary)] mb-1">
|
||||
<i class="fas fa-tachometer-alt mr-2"></i>Faster
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">Parallel processing</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm">Parallel processing</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-purple-400 mb-1">
|
||||
<div class="text-2xl font-bold text-[var(--color-accent)] mb-1">
|
||||
<i class="fas fa-expand-arrows-alt mr-2"></i>Scalable
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">Add more nodes</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm">Add more nodes</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-green-400 mb-1">
|
||||
<div class="text-2xl font-bold text-[var(--color-success)] mb-1">
|
||||
<i class="fas fa-shield-alt mr-2"></i>Resilient
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">Fault tolerant</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm">Fault tolerant</p>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-yellow-400 mb-1">
|
||||
<div class="text-2xl font-bold text-[var(--color-warning)] mb-1">
|
||||
<i class="fas fa-coins mr-2"></i>Efficient
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">Resource optimization</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm">Resource optimization</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Network Token Card -->
|
||||
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl mb-10 p-6">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-xl mb-10 p-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<i class="fas fa-key text-yellow-400 text-xl mr-3"></i>
|
||||
<h3 class="text-xl font-bold text-white">Network Token</h3>
|
||||
<button onclick="copyClipboard('{{.P2PToken}}')" class="ml-auto bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<i class="fas fa-key text-[var(--color-warning)] text-xl mr-3"></i>
|
||||
<h3 class="text-xl font-bold text-[var(--color-text-primary)]">Network Token</h3>
|
||||
<button onclick="copyClipboard('{{.P2PToken}}')" class="ml-auto bg-[var(--color-bg-primary)] hover:bg-[var(--color-bg-secondary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-900/80 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50 cursor-pointer hover:bg-gray-900" @click="copyClipboard($el.textContent.trim())">{{.P2PToken}}</code>
|
||||
<p class="text-gray-300">
|
||||
<code class="block bg-[var(--color-bg-primary)]/80 text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50 cursor-pointer hover:bg-[var(--color-bg-primary)]" @click="copyClipboard($el.textContent.trim())">{{.P2PToken}}</code>
|
||||
<p class="text-[var(--color-text-secondary)]">
|
||||
The network token can be used to either share the instance or join a federation or a worker network. Below you will find examples on how to start a new instance or a worker with this token.
|
||||
</p>
|
||||
</div>
|
||||
@@ -221,74 +223,74 @@
|
||||
<!-- Network Status Overview -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-10">
|
||||
<!-- Federation Status -->
|
||||
<div class="bg-[#1E293B] border border-blue-500/20 rounded-xl p-6">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-primary-border)]/20 rounded-xl p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="w-12 h-12 bg-blue-500/20 rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-network-wired text-blue-400 text-xl"></i>
|
||||
<div class="w-12 h-12 bg-[var(--color-primary-light)] rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-network-wired text-[var(--color-primary)] text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white">Federation</h3>
|
||||
<p class="text-blue-300 text-sm">Instance sharing</p>
|
||||
<h3 class="text-lg font-bold text-[var(--color-text-primary)]">Federation</h3>
|
||||
<p class="text-[var(--color-primary)] text-sm">Instance sharing</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-2xl font-bold">
|
||||
<span :class="stats.federated.online > 0 ? 'text-green-400' : 'text-red-400'" x-text="stats.federated.online"></span>
|
||||
<span class="text-gray-300 text-xl">/<span x-text="stats.federated.total"></span></span>
|
||||
<span :class="stats.federated.online > 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" x-text="stats.federated.online"></span>
|
||||
<span class="text-[var(--color-text-secondary)] text-xl">/<span x-text="stats.federated.total"></span></span>
|
||||
</div>
|
||||
<p class="text-blue-300 text-sm">nodes</p>
|
||||
<p class="text-[var(--color-primary)] text-sm">nodes</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center text-sm text-blue-200">
|
||||
<div class="flex items-center text-sm text-[var(--color-primary)]/80">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
<span>Load balanced instances</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Workers Status -->
|
||||
<div class="bg-[#1E293B] border border-purple-500/20 rounded-xl p-6">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-xl p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="w-12 h-12 bg-purple-500/20 rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-puzzle-piece text-purple-400 text-xl"></i>
|
||||
<div class="w-12 h-12 bg-[var(--color-accent-light)] rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-puzzle-piece text-[var(--color-accent)] text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white">Workers</h3>
|
||||
<p class="text-purple-300 text-sm">Model sharding</p>
|
||||
<h3 class="text-lg font-bold text-[var(--color-text-primary)]">Workers</h3>
|
||||
<p class="text-[var(--color-accent)] text-sm">Model sharding</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-2xl font-bold">
|
||||
<span :class="stats.workers.online > 0 ? 'text-green-400' : 'text-red-400'" x-text="stats.workers.online"></span>
|
||||
<span class="text-gray-300 text-xl">/<span x-text="stats.workers.total"></span></span>
|
||||
<span :class="stats.workers.online > 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" x-text="stats.workers.online"></span>
|
||||
<span class="text-[var(--color-text-secondary)] text-xl">/<span x-text="stats.workers.total"></span></span>
|
||||
</div>
|
||||
<p class="text-purple-300 text-sm">workers</p>
|
||||
<p class="text-[var(--color-accent)] text-sm">workers</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center text-sm text-purple-200">
|
||||
<div class="flex items-center text-sm text-[var(--color-accent)]/80">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
<span>Distributed computation</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Network Token -->
|
||||
<div class="bg-[#1E293B] border border-yellow-500/20 rounded-xl p-6">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-warning)]/20 rounded-xl p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<div class="flex items-center">
|
||||
<div class="w-12 h-12 bg-yellow-500/20 rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-key text-yellow-400 text-xl"></i>
|
||||
<div class="w-12 h-12 bg-[var(--color-warning-light)] rounded-xl flex items-center justify-center mr-3">
|
||||
<i class="fas fa-key text-[var(--color-warning)] text-xl"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-white">Network</h3>
|
||||
<p class="text-yellow-300 text-sm">Connection token</p>
|
||||
<h3 class="text-lg font-bold text-[var(--color-text-primary)]">Network</h3>
|
||||
<p class="text-[var(--color-warning)] text-sm">Connection token</p>
|
||||
</div>
|
||||
</div>
|
||||
<button onclick="copyClipboard('{{.P2PToken}}')" class="bg-yellow-600/30 hover:bg-yellow-600/50 text-yellow-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('{{.P2PToken}}')" class="bg-[var(--color-warning-light)] hover:bg-[var(--color-warning)]/30 text-[var(--color-warning)] p-2 rounded-lg transition-colors duration-200">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center text-sm text-yellow-200">
|
||||
<div class="flex items-center text-sm text-[var(--color-warning)]/80">
|
||||
<i class="fas fa-info-circle mr-2"></i>
|
||||
<span>Ready to connect</span>
|
||||
</div>
|
||||
@@ -296,30 +298,30 @@
|
||||
</div>
|
||||
|
||||
<!-- Federation Box -->
|
||||
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl mb-10">
|
||||
<div class="p-8 border-b border-gray-700/50">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-xl mb-10">
|
||||
<div class="p-8 border-b border-[var(--color-border-subtle)]">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center">
|
||||
<div class="w-14 h-14 bg-blue-500/20 rounded-2xl flex items-center justify-center mr-4">
|
||||
<i class="text-blue-400 fa-solid fa-circle-nodes text-2xl fa-spin-pulse"></i>
|
||||
<div class="w-14 h-14 bg-[var(--color-primary-light)] rounded-2xl flex items-center justify-center mr-4">
|
||||
<i class="text-[var(--color-primary)] fa-solid fa-circle-nodes text-2xl fa-spin-pulse"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-white">Federation Network</h2>
|
||||
<p class="text-blue-300 text-sm">Instance load balancing and sharing</p>
|
||||
<h2 class="text-2xl font-bold text-[var(--color-text-primary)]">Federation Network</h2>
|
||||
<p class="text-[var(--color-primary)] text-sm">Instance load balancing and sharing</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm text-gray-400 mb-1">Active Nodes</div>
|
||||
<div class="text-sm text-[var(--color-text-secondary)] mb-1">Active Nodes</div>
|
||||
<div class="text-3xl font-bold">
|
||||
<span :class="stats.federated.online > 0 ? 'text-blue-400' : 'text-red-400'" x-text="stats.federated.online"></span>
|
||||
<span class="text-gray-400 text-xl">/<span x-text="stats.federated.total"></span></span>
|
||||
<span :class="stats.federated.online > 0 ? 'text-[var(--color-primary)]' : 'text-[var(--color-error)]'" x-text="stats.federated.online"></span>
|
||||
<span class="text-[var(--color-text-secondary)] text-xl">/<span x-text="stats.federated.total"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-900/20 rounded-xl p-4 mb-6 border border-blue-700/30">
|
||||
<p class="text-gray-300 text-sm leading-relaxed">
|
||||
<i class="fas fa-lightbulb text-blue-400 mr-2"></i>
|
||||
<div class="bg-[var(--color-primary-light)] rounded-xl p-4 mb-6 border border-[var(--color-primary-border)]/30">
|
||||
<p class="text-[var(--color-text-secondary)] text-sm leading-relaxed">
|
||||
<i class="fas fa-lightbulb text-[var(--color-primary)] mr-2"></i>
|
||||
Start LocalAI in federated mode to share your instance, or launch a federated server to distribute requests intelligently across multiple nodes in your network.
|
||||
</p>
|
||||
</div>
|
||||
@@ -327,36 +329,36 @@
|
||||
<!-- Federation Nodes Grid -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<template x-if="federationNodes.length === 0">
|
||||
<div class="col-span-full flex flex-col items-center justify-center py-12 text-center bg-gray-800/50 border border-gray-700/50 rounded-xl">
|
||||
<i class="fas fa-server text-gray-500 text-4xl mb-4"></i>
|
||||
<p class="text-gray-400 text-lg font-medium">No nodes available</p>
|
||||
<p class="text-gray-500 text-sm mt-2">Start some workers to see them here</p>
|
||||
<div class="col-span-full flex flex-col items-center justify-center py-12 text-center bg-[var(--color-bg-primary)]/50 border border-[var(--color-border-subtle)]/50 rounded-xl">
|
||||
<i class="fas fa-server text-[var(--color-text-muted)] text-4xl mb-4"></i>
|
||||
<p class="text-[var(--color-text-secondary)] text-lg font-medium">No nodes available</p>
|
||||
<p class="text-[var(--color-text-muted)] text-sm mt-2">Start some workers to see them here</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-for="node in federationNodes" :key="node.id">
|
||||
<div :class="node.isOnline ? 'border-green-400/50' : 'border-red-400/50'"
|
||||
class="bg-[#101827] border rounded-lg p-5 transition-colors">
|
||||
<div :class="node.isOnline ? 'border-[var(--color-success)]/50' : 'border-[var(--color-error)]/50'"
|
||||
class="bg-[var(--color-bg-primary)] border rounded-lg p-5 transition-colors">
|
||||
<!-- Header with node icon and status -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<!-- Node info -->
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 bg-blue-500/20 rounded-lg flex items-center justify-center mr-3">
|
||||
<i class="fas fa-server text-blue-400 text-lg"></i>
|
||||
<div class="w-10 h-10 bg-[var(--color-primary-light)] rounded-lg flex items-center justify-center mr-3">
|
||||
<i class="fas fa-server text-[var(--color-primary)] text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-white font-semibold text-sm">Node</h4>
|
||||
<p class="text-gray-400 text-xs font-mono break-all" x-text="node.id"></p>
|
||||
<h4 class="text-[var(--color-text-primary)] font-semibold text-sm">Node</h4>
|
||||
<p class="text-[var(--color-text-secondary)] text-xs font-mono break-all" x-text="node.id"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Status badge -->
|
||||
<div class="flex items-center bg-[#101827] rounded-lg px-3 py-1.5 border border-[#1E293B]">
|
||||
<i :class="node.isOnline ? 'text-green-400' : 'text-red-400'" class="fas fa-circle mr-2 text-xs"></i>
|
||||
<span :class="node.isOnline ? 'text-green-400' : 'text-red-400'" class="text-xs font-medium" x-text="node.isOnline ? 'Online' : 'Offline'"></span>
|
||||
<div class="flex items-center bg-[var(--color-bg-primary)] rounded-lg px-3 py-1.5 border border-[var(--color-border-subtle)]">
|
||||
<i :class="node.isOnline ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" class="fas fa-circle mr-2 text-xs"></i>
|
||||
<span :class="node.isOnline ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" class="text-xs font-medium" x-text="node.isOnline ? 'Online' : 'Offline'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer with timestamp -->
|
||||
<div class="text-xs text-gray-500 pt-3 border-t border-gray-700/30 flex items-center">
|
||||
<div class="text-xs text-[var(--color-text-muted)] pt-3 border-t border-[var(--color-border-subtle)]/30 flex items-center">
|
||||
<i class="fas fa-clock mr-2"></i>
|
||||
<span x-text="'Updated: ' + new Date().toLocaleTimeString()"></span>
|
||||
</div>
|
||||
@@ -366,19 +368,19 @@
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
<h3 class="text-2xl font-bold text-white mb-6">
|
||||
<i class="fa-solid fa-book text-blue-400 mr-2"></i> Start a federated instance
|
||||
<h3 class="text-2xl font-bold text-[var(--color-text-primary)] mb-6">
|
||||
<i class="fa-solid fa-book text-[var(--color-primary)] mr-2"></i> Start a federated instance
|
||||
</h3>
|
||||
|
||||
<!-- Tabs navigation -->
|
||||
<ul class="mb-5 flex list-none flex-row flex-wrap ps-0 border border-gray-700/50 rounded-lg overflow-hidden" role="tablist" data-twe-nav-ref>
|
||||
<ul class="mb-5 flex list-none flex-row flex-wrap ps-0 border border-[var(--color-border-subtle)] rounded-lg overflow-hidden" role="tablist" data-twe-nav-ref>
|
||||
<li role="presentation" class="flex-auto text-center">
|
||||
<a href="#tabs-federated-cli" class="tablink block border-0 bg-gray-800 px-7 py-4 text-sm font-medium uppercase leading-tight text-white hover:bg-gray-700 focus:bg-gray-700 data-[twe-nav-active]:border-blue-500 data-[twe-nav-active]:text-blue-400 data-[twe-nav-active]:bg-gray-700 active transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-federated-cli" data-twe-nav-active role="tab" aria-controls="tabs-federated-cli" aria-selected="true">
|
||||
<a href="#tabs-federated-cli" class="tablink block border-0 bg-[var(--color-bg-primary)] px-7 py-4 text-sm font-medium uppercase leading-tight text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] focus:bg-[var(--color-bg-secondary)] data-[twe-nav-active]:border-[var(--color-primary)] data-[twe-nav-active]:text-[var(--color-primary)] data-[twe-nav-active]:bg-[var(--color-bg-secondary)] active transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-federated-cli" data-twe-nav-active role="tab" aria-controls="tabs-federated-cli" aria-selected="true">
|
||||
<i class="fa-solid fa-terminal mr-2"></i> CLI
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="flex-auto text-center">
|
||||
<a href="#tabs-federated-docker" class="tablink block border-0 bg-gray-800 px-7 py-4 text-sm font-medium uppercase leading-tight text-white hover:bg-gray-700 focus:bg-gray-700 data-[twe-nav-active]:border-blue-500 data-[twe-nav-active]:text-blue-400 data-[twe-nav-active]:bg-gray-700 transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-federated-docker" role="tab" aria-controls="tabs-federated-docker" aria-selected="false">
|
||||
<a href="#tabs-federated-docker" class="tablink block border-0 bg-[var(--color-bg-primary)] px-7 py-4 text-sm font-medium uppercase leading-tight text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] focus:bg-[var(--color-bg-secondary)] data-[twe-nav-active]:border-[var(--color-primary)] data-[twe-nav-active]:text-[var(--color-primary)] data-[twe-nav-active]:bg-[var(--color-bg-secondary)] transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-federated-docker" role="tab" aria-controls="tabs-federated-docker" aria-selected="false">
|
||||
<i class="fa-solid fa-box-open mr-2"></i> Container images
|
||||
</a>
|
||||
</li>
|
||||
@@ -387,64 +389,64 @@
|
||||
<!-- Tabs content -->
|
||||
<div class="mb-6">
|
||||
<div class="tabcontent hidden opacity-100 transition-opacity duration-150 ease-linear data-[twe-tab-active]:block p-4" id="tabs-federated-cli" role="tabpanel" aria-labelledby="tabs-federated-cli" data-twe-tab-active>
|
||||
<div class="bg-gray-900/50 rounded-xl border border-gray-700/50 p-6">
|
||||
<div class="bg-[var(--color-bg-primary)]/50 rounded-xl border border-[var(--color-border-subtle)]/50 p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new instance to share:
|
||||
</h4>
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai run --federated --p2p')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai run --federated --p2p')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
# Start a new instance to share with --federated and a TOKEN<br>
|
||||
export TOKEN="<span class="token">{{.P2PToken}}</span>"<br>
|
||||
local-ai run --federated --p2p</code>
|
||||
|
||||
<p class="text-gray-400 text-sm mt-2">Note: If you don't have a token do not specify it and use the generated one that you can find in this page.</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm mt-2">Note: If you don't have a token do not specify it and use the generated one that you can find in this page.</p>
|
||||
|
||||
<div class="flex items-center justify-between mb-4 mt-8">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new federated load balancer:
|
||||
</h4>
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai federated')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai federated')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
export TOKEN="<span class="token">{{.P2PToken}}</span>"<br>
|
||||
local-ai federated</code>
|
||||
|
||||
<p class="text-gray-400 text-sm mt-2">Note: Token is needed when starting the federated server.</p>
|
||||
<p class="text-[var(--color-text-secondary)] text-sm mt-2">Note: Token is needed when starting the federated server.</p>
|
||||
|
||||
<p class="text-gray-300 mt-4">For all the options available, please refer to the <a href="https://localai.io/features/distribute/#starting-workers" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">documentation</a>.</p>
|
||||
<p class="text-[var(--color-text-secondary)] mt-4">For all the options available, please refer to the <a href="https://localai.io/features/distribute/#starting-workers" target="_blank" class="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 transition-colors">documentation</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabcontent hidden opacity-0 transition-opacity duration-150 ease-linear data-[twe-tab-active]:block p-4" id="tabs-federated-docker" role="tabpanel" aria-labelledby="tabs-federated-docker">
|
||||
<div class="bg-gray-900/50 rounded-xl border border-gray-700/50 p-6">
|
||||
<div class="bg-[var(--color-bg-primary)]/50 rounded-xl border border-[var(--color-border-subtle)]/50 p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new federated instance:
|
||||
</h4>
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 8080:8080 localai/localai:latest-cpu run --federated --p2p')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 8080:8080 localai/localai:latest-cpu run --federated --p2p')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --name local-ai -p 8080:8080 localai/localai:latest-cpu run --federated --p2p</code>
|
||||
|
||||
<div class="flex items-center justify-between mb-4 mt-8">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new federated server with Docker (port to 9090):
|
||||
</h4>
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 9090:8080 localai/localai:latest-cpu federated')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 9090:8080 localai/localai:latest-cpu federated')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --name local-ai -p 9090:8080 localai/localai:latest-cpu federated</code>
|
||||
|
||||
<p class="text-gray-300 mt-4">For all the options available and see what image to use, please refer to the <a href="https://localai.io/basics/container/" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">Container images documentation</a> and <a href="https://localai.io/advanced/#cli-parameters" target="_blank" class="text-blue-400 hover:text-blue-300 transition-colors">CLI parameters documentation</a>.</p>
|
||||
<p class="text-[var(--color-text-secondary)] mt-4">For all the options available and see what image to use, please refer to the <a href="https://localai.io/basics/container/" target="_blank" class="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 transition-colors">Container images documentation</a> and <a href="https://localai.io/advanced/#cli-parameters" target="_blank" class="text-[var(--color-primary)] hover:text-[var(--color-primary)]/80 transition-colors">CLI parameters documentation</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -452,30 +454,30 @@ docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --
|
||||
</div>
|
||||
|
||||
<!-- Workers Box -->
|
||||
<div class="bg-[#1E293B] border border-[#8B5CF6]/20 rounded-xl mb-10">
|
||||
<div class="p-8 border-b border-gray-700/50">
|
||||
<div class="bg-[var(--color-bg-secondary)] border border-[var(--color-accent)]/20 rounded-xl mb-10">
|
||||
<div class="p-8 border-b border-[var(--color-border-subtle)]">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center">
|
||||
<div class="w-14 h-14 bg-purple-500/20 rounded-2xl flex items-center justify-center mr-4">
|
||||
<i class="text-purple-400 fa-solid fa-puzzle-piece text-2xl fa-spin-pulse"></i>
|
||||
<div class="w-14 h-14 bg-[var(--color-accent-light)] rounded-2xl flex items-center justify-center mr-4">
|
||||
<i class="text-[var(--color-accent)] fa-solid fa-puzzle-piece text-2xl fa-spin-pulse"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-white">Worker Network</h2>
|
||||
<p class="text-purple-300 text-sm">Distributed model computation (llama.cpp)</p>
|
||||
<h2 class="text-2xl font-bold text-[var(--color-text-primary)]">Worker Network</h2>
|
||||
<p class="text-[var(--color-accent)] text-sm">Distributed model computation (llama.cpp)</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-sm text-gray-400 mb-1">Active Workers</div>
|
||||
<div class="text-sm text-[var(--color-text-secondary)] mb-1">Active Workers</div>
|
||||
<div class="text-3xl font-bold">
|
||||
<span :class="stats.workers.online > 0 ? 'text-purple-400' : 'text-red-400'" x-text="stats.workers.online"></span>
|
||||
<span class="text-gray-400 text-xl">/<span x-text="stats.workers.total"></span></span>
|
||||
<span :class="stats.workers.online > 0 ? 'text-[var(--color-accent)]' : 'text-[var(--color-error)]'" x-text="stats.workers.online"></span>
|
||||
<span class="text-[var(--color-text-secondary)] text-xl">/<span x-text="stats.workers.total"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-purple-900/20 rounded-xl p-4 mb-6 border border-purple-700/30">
|
||||
<p class="text-gray-300 text-sm leading-relaxed">
|
||||
<i class="fas fa-lightbulb text-purple-400 mr-2"></i>
|
||||
<div class="bg-[var(--color-accent-light)] rounded-xl p-4 mb-6 border border-[var(--color-accent)]/30">
|
||||
<p class="text-[var(--color-text-secondary)] text-sm leading-relaxed">
|
||||
<i class="fas fa-lightbulb text-[var(--color-accent)] mr-2"></i>
|
||||
Deploy llama.cpp workers to split model weights across multiple devices. This enables processing larger models by distributing computational load and memory requirements.
|
||||
</p>
|
||||
</div>
|
||||
@@ -483,36 +485,36 @@ docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --
|
||||
<!-- Workers Grid -->
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<template x-if="workerNodes.length === 0">
|
||||
<div class="col-span-full flex flex-col items-center justify-center py-12 text-center bg-gray-800/50 border border-gray-700/50 rounded-xl">
|
||||
<i class="fas fa-server text-gray-500 text-4xl mb-4"></i>
|
||||
<p class="text-gray-400 text-lg font-medium">No workers available</p>
|
||||
<p class="text-gray-500 text-sm mt-2">Start some workers to see them here</p>
|
||||
<div class="col-span-full flex flex-col items-center justify-center py-12 text-center bg-[var(--color-bg-primary)]/50 border border-[var(--color-border-subtle)]/50 rounded-xl">
|
||||
<i class="fas fa-server text-[var(--color-text-muted)] text-4xl mb-4"></i>
|
||||
<p class="text-[var(--color-text-secondary)] text-lg font-medium">No workers available</p>
|
||||
<p class="text-[var(--color-text-muted)] text-sm mt-2">Start some workers to see them here</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template x-for="node in workerNodes" :key="node.id">
|
||||
<div :class="node.isOnline ? 'border-green-400/50' : 'border-red-400/50'"
|
||||
class="bg-[#101827] border rounded-lg p-5 transition-colors">
|
||||
<div :class="node.isOnline ? 'border-[var(--color-success)]/50' : 'border-[var(--color-error)]/50'"
|
||||
class="bg-[var(--color-bg-primary)] border rounded-lg p-5 transition-colors">
|
||||
<!-- Header with node icon and status -->
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<!-- Node info -->
|
||||
<div class="flex items-center">
|
||||
<div class="w-10 h-10 bg-purple-500/20 rounded-lg flex items-center justify-center mr-3">
|
||||
<i class="fas fa-server text-purple-400 text-lg"></i>
|
||||
<div class="w-10 h-10 bg-[var(--color-accent-light)] rounded-lg flex items-center justify-center mr-3">
|
||||
<i class="fas fa-server text-[var(--color-accent)] text-lg"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-white font-semibold text-sm">Worker</h4>
|
||||
<p class="text-gray-400 text-xs font-mono break-all" x-text="node.id"></p>
|
||||
<h4 class="text-[var(--color-text-primary)] font-semibold text-sm">Worker</h4>
|
||||
<p class="text-[var(--color-text-secondary)] text-xs font-mono break-all" x-text="node.id"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Status badge -->
|
||||
<div class="flex items-center bg-[#101827] rounded-lg px-3 py-1.5 border border-[#1E293B]">
|
||||
<i :class="node.isOnline ? 'text-green-400' : 'text-red-400'" class="fas fa-circle mr-2 text-xs"></i>
|
||||
<span :class="node.isOnline ? 'text-green-400' : 'text-red-400'" class="text-xs font-medium" x-text="node.isOnline ? 'Online' : 'Offline'"></span>
|
||||
<div class="flex items-center bg-[var(--color-bg-primary)] rounded-lg px-3 py-1.5 border border-[var(--color-border-subtle)]">
|
||||
<i :class="node.isOnline ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" class="fas fa-circle mr-2 text-xs"></i>
|
||||
<span :class="node.isOnline ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'" class="text-xs font-medium" x-text="node.isOnline ? 'Online' : 'Offline'"></span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer with timestamp -->
|
||||
<div class="text-xs text-gray-500 pt-3 border-t border-gray-700/30 flex items-center">
|
||||
<div class="text-xs text-[var(--color-text-muted)] pt-3 border-t border-[var(--color-border-subtle)]/30 flex items-center">
|
||||
<i class="fas fa-clock mr-2"></i>
|
||||
<span x-text="'Updated: ' + new Date().toLocaleTimeString()"></span>
|
||||
</div>
|
||||
@@ -522,19 +524,19 @@ docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --
|
||||
</div>
|
||||
|
||||
<div class="p-8">
|
||||
<h3 class="text-2xl font-bold text-white mb-6">
|
||||
<i class="fa-solid fa-book text-purple-400 mr-2"></i> Start a new llama.cpp worker
|
||||
<h3 class="text-2xl font-bold text-[var(--color-text-primary)] mb-6">
|
||||
<i class="fa-solid fa-book text-[var(--color-accent)] mr-2"></i> Start a new llama.cpp worker
|
||||
</h3>
|
||||
|
||||
<!-- Tabs navigation -->
|
||||
<ul class="mb-5 flex list-none flex-row flex-wrap ps-0 border border-gray-700/50 rounded-lg overflow-hidden" role="tablist" data-twe-nav-ref>
|
||||
<ul class="mb-5 flex list-none flex-row flex-wrap ps-0 border border-[var(--color-border-subtle)] rounded-lg overflow-hidden" role="tablist" data-twe-nav-ref>
|
||||
<li role="presentation" class="flex-auto text-center">
|
||||
<a href="#tabs-cli" class="tablink block border-0 bg-gray-800 px-7 py-4 text-sm font-medium uppercase leading-tight text-white hover:bg-gray-700 focus:bg-gray-700 data-[twe-nav-active]:border-purple-500 data-[twe-nav-active]:text-purple-400 data-[twe-nav-active]:bg-gray-700 active transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-cli" data-twe-nav-active role="tab" aria-controls="tabs-cli" aria-selected="true">
|
||||
<a href="#tabs-cli" class="tablink block border-0 bg-[var(--color-bg-primary)] px-7 py-4 text-sm font-medium uppercase leading-tight text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] focus:bg-[var(--color-bg-secondary)] data-[twe-nav-active]:border-[var(--color-accent)] data-[twe-nav-active]:text-[var(--color-accent)] data-[twe-nav-active]:bg-[var(--color-bg-secondary)] active transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-cli" data-twe-nav-active role="tab" aria-controls="tabs-cli" aria-selected="true">
|
||||
<i class="fa-solid fa-terminal mr-2"></i> CLI
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation" class="flex-auto text-center">
|
||||
<a href="#tabs-docker" class="tablink block border-0 bg-gray-800 px-7 py-4 text-sm font-medium uppercase leading-tight text-white hover:bg-gray-700 focus:bg-gray-700 data-[twe-nav-active]:border-purple-500 data-[twe-nav-active]:text-purple-400 data-[twe-nav-active]:bg-gray-700 transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-docker" role="tab" aria-controls="tabs-docker" aria-selected="false">
|
||||
<a href="#tabs-docker" class="tablink block border-0 bg-[var(--color-bg-primary)] px-7 py-4 text-sm font-medium uppercase leading-tight text-[var(--color-text-primary)] hover:bg-[var(--color-bg-secondary)] focus:bg-[var(--color-bg-secondary)] data-[twe-nav-active]:border-[var(--color-accent)] data-[twe-nav-active]:text-[var(--color-accent)] data-[twe-nav-active]:bg-[var(--color-bg-secondary)] transition-all duration-200" data-twe-toggle="pill" data-twe-target="#tabs-docker" role="tab" aria-controls="tabs-docker" aria-selected="false">
|
||||
<i class="fa-solid fa-box-open mr-2"></i> Container images
|
||||
</a>
|
||||
</li>
|
||||
@@ -543,36 +545,36 @@ docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --
|
||||
<!-- Tabs content -->
|
||||
<div class="mb-6">
|
||||
<div class="tabcontent hidden opacity-100 transition-opacity duration-150 ease-linear data-[twe-tab-active]:block p-4" id="tabs-cli" role="tabpanel" aria-labelledby="tabs-cli" data-twe-tab-active>
|
||||
<div class="bg-gray-900/50 rounded-xl border border-gray-700/50 p-6">
|
||||
<div class="bg-[var(--color-bg-primary)]/50 rounded-xl border border-[var(--color-border-subtle)]/50 p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new worker:
|
||||
</h4>
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai worker p2p-llama-cpp-rpc')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('export TOKEN=\'{{.P2PToken}}\'\nlocal-ai worker p2p-llama-cpp-rpc')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
export TOKEN="<span class="token">{{.P2PToken}}</span>"<br>
|
||||
local-ai worker p2p-llama-cpp-rpc</code>
|
||||
|
||||
<p class="text-gray-300 mt-4">For all the options available, please refer to the <a href="https://localai.io/features/distribute/#starting-workers" target="_blank" class="text-purple-400 hover:text-purple-300 transition-colors">documentation</a>.</p>
|
||||
<p class="text-[var(--color-text-secondary)] mt-4">For all the options available, please refer to the <a href="https://localai.io/features/distribute/#starting-workers" target="_blank" class="text-[var(--color-accent)] hover:text-[var(--color-accent)]/80 transition-colors">documentation</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabcontent hidden opacity-0 transition-opacity duration-150 ease-linear data-[twe-tab-active]:block p-4" id="tabs-docker" role="tabpanel" aria-labelledby="tabs-docker">
|
||||
<div class="bg-gray-900/50 rounded-xl border border-gray-700/50 p-6">
|
||||
<div class="bg-[var(--color-bg-primary)]/50 rounded-xl border border-[var(--color-border-subtle)]/50 p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h4 class="text-lg font-bold text-white">
|
||||
<h4 class="text-lg font-bold text-[var(--color-text-primary)]">
|
||||
Start a new worker with Docker:
|
||||
</h4>
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 8080:8080 localai/localai:latest-cpu worker p2p-llama-cpp-rpc')" class="bg-gray-700 hover:bg-gray-600 text-gray-300 p-2 rounded-lg transition-colors duration-200">
|
||||
<button onclick="copyClipboard('docker run -ti --net host -e TOKEN=\'{{.P2PToken}}\' --name local-ai -p 8080:8080 localai/localai:latest-cpu worker p2p-llama-cpp-rpc')" class="bg-[var(--color-bg-secondary)] hover:bg-[var(--color-bg-primary)] text-[var(--color-text-secondary)] p-2 rounded-lg transition-colors duration-200 border border-[var(--color-border-subtle)]">
|
||||
<i class="fa-solid fa-copy"></i>
|
||||
</button>
|
||||
</div>
|
||||
<code class="block bg-gray-800 text-yellow-300 p-4 rounded-lg break-words mb-4 border border-gray-700/50">
|
||||
<code class="block bg-[var(--color-bg-primary)] text-[var(--color-warning)] p-4 rounded-lg break-words mb-4 border border-[var(--color-border-subtle)]/50">
|
||||
docker run -ti --net host -e TOKEN="<span class="token">{{.P2PToken}}</span>" --name local-ai -p 8080:8080 localai/localai:latest-cpu worker p2p-llama-cpp-rpc</code>
|
||||
|
||||
<p class="text-gray-300 mt-4">For all the options available and see what image to use, please refer to the <a href="https://localai.io/basics/container/" target="_blank" class="text-purple-400 hover:text-purple-300 transition-colors">Container images documentation</a> and <a href="https://localai.io/advanced/#cli-parameters" target="_blank" class="text-purple-400 hover:text-purple-300 transition-colors">CLI parameters documentation</a>.</p>
|
||||
<p class="text-[var(--color-text-secondary)] mt-4">For all the options available and see what image to use, please refer to the <a href="https://localai.io/basics/container/" target="_blank" class="text-[var(--color-accent)] hover:text-[var(--color-accent)]/80 transition-colors">Container images documentation</a> and <a href="https://localai.io/advanced/#cli-parameters" target="_blank" class="text-[var(--color-accent)] hover:text-[var(--color-accent)]/80 transition-colors">CLI parameters documentation</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -708,5 +710,10 @@ function p2pNetwork() {
|
||||
</script>
|
||||
{{ end }}
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,43 +1,37 @@
|
||||
<footer class="bg-[#101827] border-t border-[#1E293B] py-8 mt-auto">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col items-center justify-center space-y-4">
|
||||
<!-- Logo & Version -->
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ if .Version }}
|
||||
<span class="text-sm md:text-base font-medium text-[#94A3B8]">LocalAI Version <span class="text-[#38BDF8] font-semibold">{{.Version}}</span></span>
|
||||
{{ end }}
|
||||
</div>
|
||||
|
||||
<!-- Links -->
|
||||
<div class="flex flex-wrap justify-center gap-x-6 gap-y-3">
|
||||
<a href="https://github.com/mudler/LocalAI"
|
||||
class="group flex items-center text-[#94A3B8] hover:text-[#38BDF8] transition duration-300 ease-in-out"
|
||||
target="_blank">
|
||||
<i class="fab fa-github mr-2 text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span>GitHub</span>
|
||||
<i class="fas fa-external-link-alt text-xs ml-1.5 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></i>
|
||||
</a>
|
||||
<a href="https://localai.io"
|
||||
class="group flex items-center text-[#94A3B8] hover:text-[#38BDF8] transition duration-300 ease-in-out"
|
||||
target="_blank">
|
||||
<i class="fas fa-book mr-2 text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span>Documentation</span>
|
||||
<i class="fas fa-external-link-alt text-xs ml-1.5 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></i>
|
||||
</a>
|
||||
<a href="https://mudler.pm"
|
||||
class="group flex items-center text-[#94A3B8] hover:text-[#38BDF8] transition duration-300 ease-in-out"
|
||||
target="_blank">
|
||||
<i class="fas fa-user mr-2 text-lg group-hover:scale-110 transition-transform"></i>
|
||||
<span>Author</span>
|
||||
<i class="fas fa-external-link-alt text-xs ml-1.5 opacity-0 group-hover:opacity-100 transition-opacity duration-300"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Copyright Notice -->
|
||||
<div class="mt-4 text-sm text-[#94A3B8]">
|
||||
<span>© 2023-2025 <a href="https://mudler.pm" class="text-[#38BDF8] hover:text-[#8B5CF6] transition duration-300" target="_blank">Ettore Di Giacinto</a></span>
|
||||
</div>
|
||||
<footer class="bg-[var(--color-bg-secondary)] border-t border-[var(--color-border-subtle)] py-6 mt-auto">
|
||||
<div class="container mx-auto px-6">
|
||||
<div class="flex flex-col items-center justify-center space-y-3">
|
||||
{{ if .Version }}
|
||||
<span class="text-xs text-[var(--color-text-secondary)]">
|
||||
LocalAI <span class="text-[var(--color-primary)] font-medium">{{.Version}}</span>
|
||||
</span>
|
||||
{{ end }}
|
||||
|
||||
<div class="flex flex-wrap justify-center gap-x-5 gap-y-2">
|
||||
<a href="https://github.com/mudler/LocalAI"
|
||||
class="group flex items-center text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors"
|
||||
target="_blank">
|
||||
<i class="fab fa-github mr-1.5 text-sm"></i>
|
||||
<span>GitHub</span>
|
||||
</a>
|
||||
<a href="https://localai.io"
|
||||
class="group flex items-center text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors"
|
||||
target="_blank">
|
||||
<i class="fas fa-book mr-1.5 text-sm"></i>
|
||||
<span>Documentation</span>
|
||||
</a>
|
||||
<a href="https://mudler.pm"
|
||||
class="group flex items-center text-xs text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors"
|
||||
target="_blank">
|
||||
<i class="fas fa-user mr-1.5 text-sm"></i>
|
||||
<span>Author</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-[var(--color-text-muted)]">
|
||||
<span>© 2023-2025 <a href="https://mudler.pm" class="text-[var(--color-text-secondary)] hover:text-[var(--color-primary)] transition-colors" target="_blank">Ettore Di Giacinto</a></span>
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/assets/tw-elements.js"></script>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="static/assets/tw-elements.js"></script>
|
||||
</footer>
|
||||
|
||||
@@ -37,6 +37,58 @@
|
||||
preflight: false,
|
||||
},
|
||||
};
|
||||
|
||||
// Theme Management
|
||||
(function() {
|
||||
const THEME_KEY = 'localai-theme';
|
||||
const DARK = 'dark';
|
||||
const LIGHT = 'light';
|
||||
|
||||
function getStoredTheme() {
|
||||
return localStorage.getItem(THEME_KEY);
|
||||
}
|
||||
|
||||
function getSystemTheme() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? DARK : LIGHT;
|
||||
}
|
||||
|
||||
function applyTheme(theme) {
|
||||
document.documentElement.setAttribute('data-theme', theme);
|
||||
// Also set class for Tailwind compatibility
|
||||
if (theme === DARK) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const current = document.documentElement.getAttribute('data-theme') || DARK;
|
||||
const newTheme = current === DARK ? LIGHT : DARK;
|
||||
localStorage.setItem(THEME_KEY, newTheme);
|
||||
applyTheme(newTheme);
|
||||
return newTheme;
|
||||
}
|
||||
|
||||
// Initialize theme immediately to prevent flash
|
||||
const stored = getStoredTheme();
|
||||
const initialTheme = stored || getSystemTheme();
|
||||
applyTheme(initialTheme);
|
||||
|
||||
// Expose toggle function globally
|
||||
window.toggleTheme = toggleTheme;
|
||||
window.getCurrentTheme = function() {
|
||||
return document.documentElement.getAttribute('data-theme') || DARK;
|
||||
};
|
||||
|
||||
// Listen for system theme changes
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
|
||||
if (!getStoredTheme()) {
|
||||
applyTheme(e.matches ? DARK : LIGHT);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
function copyClipboard(token) {
|
||||
// Try modern Clipboard API first (requires secure context)
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
class="sticky top-0 left-0 right-0 z-40 bg-[#1E293B]/95 backdrop-blur-sm border-b border-[#38BDF8]/50">
|
||||
class="sticky top-0 left-0 right-0 z-40 bg-[var(--color-bg-secondary)]/95 backdrop-blur-sm border-b border-[var(--color-primary)]/50">
|
||||
|
||||
<div class="container mx-auto px-4 py-3">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="relative">
|
||||
<i class="fas fa-spinner fa-spin text-[#38BDF8] text-lg"></i>
|
||||
<i class="fas fa-spinner fa-spin text-[var(--color-primary)] text-lg"></i>
|
||||
</div>
|
||||
<h3 class="text-[#E5E7EB] font-semibold text-sm">
|
||||
<h3 class="text-[var(--color-text-primary)] font-semibold text-sm">
|
||||
Operations in Progress
|
||||
<span class="ml-2 bg-[#38BDF8]/20 px-2 py-1 rounded-full text-xs border border-[#38BDF8]/30" x-text="operations.length"></span>
|
||||
<span class="ml-2 bg-[var(--color-primary-light)] px-2 py-1 rounded-full text-xs border border-[var(--color-primary-border)]" x-text="operations.length"></span>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<button @click="collapsed = !collapsed"
|
||||
class="text-[#94A3B8] hover:text-[#E5E7EB] transition-colors">
|
||||
class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] transition-colors">
|
||||
<i class="fas" :class="collapsed ? 'fa-chevron-down' : 'fa-chevron-up'"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -37,28 +37,28 @@
|
||||
x-transition:leave-end="opacity-0 max-h-0"
|
||||
class="space-y-2 overflow-y-auto max-h-96">
|
||||
<template x-for="operation in operations" :key="operation.id">
|
||||
<div class="bg-[#101827]/80 rounded-lg p-3 border border-[#1E293B] hover:border-[#38BDF8]/50 transition-colors">
|
||||
<div class="bg-[var(--color-bg-primary)]/80 rounded-lg p-3 border border-[var(--color-border-subtle)] hover:border-[var(--color-primary)]/50 transition-colors">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-3 flex-1 min-w-0">
|
||||
<!-- Icon based on type -->
|
||||
<div class="flex-shrink-0">
|
||||
<i class="text-lg"
|
||||
:class="{
|
||||
'fas fa-cube text-[#38BDF8]': !operation.isBackend && !operation.isDeletion,
|
||||
'fas fa-cubes text-[#8B5CF6]': operation.isBackend && !operation.isDeletion,
|
||||
'fas fa-trash text-red-400': operation.isDeletion
|
||||
'fas fa-cube text-[var(--color-primary)]': !operation.isBackend && !operation.isDeletion,
|
||||
'fas fa-cubes text-[var(--color-accent)]': operation.isBackend && !operation.isDeletion,
|
||||
'fas fa-trash text-[var(--color-error)]': operation.isDeletion
|
||||
}"></i>
|
||||
</div>
|
||||
|
||||
<!-- Operation details -->
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-[#E5E7EB] font-medium text-sm truncate" x-text="operation.name"></span>
|
||||
<span class="text-[var(--color-text-primary)] font-medium text-sm truncate" x-text="operation.name"></span>
|
||||
<span class="flex-shrink-0 text-xs px-2 py-0.5 rounded border"
|
||||
:class="{
|
||||
'bg-[#38BDF8]/10 text-[#38BDF8]': !operation.isDeletion && !operation.isBackend,
|
||||
'bg-[#8B5CF6]/10 text-[#8B5CF6]': !operation.isDeletion && operation.isBackend,
|
||||
'bg-red-500/10 text-red-300': operation.isDeletion
|
||||
'bg-[var(--color-primary-light)] text-[var(--color-primary)]': !operation.isDeletion && !operation.isBackend,
|
||||
'bg-[var(--color-accent-light)] text-[var(--color-accent)]': !operation.isDeletion && operation.isBackend,
|
||||
'bg-[var(--color-error-light)] text-[var(--color-error)]': operation.isDeletion
|
||||
}"
|
||||
x-text="operation.isBackend ? 'Backend' : 'Model'"></span>
|
||||
</div>
|
||||
@@ -66,35 +66,35 @@
|
||||
<!-- Status message -->
|
||||
<div class="flex items-center space-x-2 mt-1">
|
||||
<template x-if="operation.isQueued">
|
||||
<span class="text-xs text-[#38BDF8] flex items-center">
|
||||
<span class="text-xs text-[var(--color-primary)] flex items-center">
|
||||
<i class="fas fa-clock mr-1"></i>
|
||||
Queued
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="operation.isCancelled">
|
||||
<span class="text-xs text-red-400 flex items-center">
|
||||
<span class="text-xs text-[var(--color-error)] flex items-center">
|
||||
<i class="fas fa-ban mr-1"></i>
|
||||
Cancelling...
|
||||
</span>
|
||||
</template>
|
||||
<template x-if="!operation.isQueued && !operation.isCancelled && operation.message">
|
||||
<span class="text-xs text-[#94A3B8] truncate" x-text="operation.message"></span>
|
||||
<span class="text-xs text-[var(--color-text-secondary)] truncate" x-text="operation.message"></span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress percentage and cancel button -->
|
||||
<div class="flex-shrink-0 text-right flex items-center space-x-2">
|
||||
<span class="text-[#E5E7EB] font-bold text-lg" x-text="operation.progress + '%'"></span>
|
||||
<span class="text-[var(--color-text-primary)] font-bold text-lg" x-text="operation.progress + '%'"></span>
|
||||
<template x-if="operation.cancellable && !operation.isCancelled">
|
||||
<button @click="cancelOperation(operation.jobID, operation.id)"
|
||||
class="text-red-400 hover:text-red-300 transition-colors p-1 rounded hover:bg-red-500/20"
|
||||
class="text-[var(--color-error)] hover:text-[var(--color-error)] transition-colors p-1 rounded hover:bg-[var(--color-error-light)]"
|
||||
title="Cancel operation">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</template>
|
||||
<template x-if="operation.isCancelled">
|
||||
<span class="text-red-400 text-xs flex items-center">
|
||||
<span class="text-[var(--color-error)] text-xs flex items-center">
|
||||
<i class="fas fa-ban mr-1"></i>
|
||||
Cancelled
|
||||
</span>
|
||||
@@ -104,11 +104,11 @@
|
||||
</div>
|
||||
|
||||
<!-- Progress bar -->
|
||||
<div class="w-full bg-[#101827] rounded-full h-2 overflow-hidden border border-[#1E293B]">
|
||||
<div class="w-full bg-[var(--color-bg-primary)] rounded-full h-2 overflow-hidden border border-[var(--color-border-subtle)]">
|
||||
<div class="h-full rounded-full transition-all duration-300"
|
||||
:class="{
|
||||
'bg-[#38BDF8]': !operation.isDeletion && !operation.isCancelled,
|
||||
'bg-red-500': operation.isDeletion || operation.isCancelled
|
||||
'bg-[var(--color-primary)]': !operation.isDeletion && !operation.isCancelled,
|
||||
'bg-[var(--color-error)]': operation.isDeletion || operation.isCancelled
|
||||
}"
|
||||
:style="'width: ' + operation.progress + '%'">
|
||||
</div>
|
||||
|
||||
@@ -1,175 +1,200 @@
|
||||
<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">
|
||||
<!-- Logo Image -->
|
||||
<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_var(--color-primary-border)]">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Menu button for small screens -->
|
||||
<div class="lg:hidden">
|
||||
<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-[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-[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="image/" 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="video/" 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-video text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Video
|
||||
</a>
|
||||
<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="sound/" 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-volume-high text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Sound
|
||||
</a>
|
||||
<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="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>
|
||||
<a href="traces/" 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-chart-line text-[var(--color-primary)] mr-1.5 text-sm group-hover:scale-110 transition-transform"></i>Traces
|
||||
</a>
|
||||
<a href="swagger/index.html" 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>
|
||||
|
||||
<!-- System Dropdown -->
|
||||
<div class="relative" @click.away="manageOpen = false">
|
||||
<button @click="manageOpen = !manageOpen"
|
||||
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"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
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-[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-[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-[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-[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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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-[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>
|
||||
<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="image/" 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="video/" 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-video text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Video
|
||||
</a>
|
||||
<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="sound/" 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-volume-high text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Sound
|
||||
</a>
|
||||
<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="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>
|
||||
<a href="traces/" 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-chart-line text-[var(--color-primary)] mr-3 w-5 text-center text-sm"></i>Traces
|
||||
</a>
|
||||
<a href="swagger/index.html" 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>
|
||||
|
||||
<!-- System with submenu -->
|
||||
<div>
|
||||
<button @click="manageOpen = !manageOpen"
|
||||
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-[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>
|
||||
<div x-show="manageOpen"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 max-h-0"
|
||||
x-transition:enter-end="opacity-100 max-h-96"
|
||||
x-transition:leave="transition ease-in duration-150"
|
||||
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-[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-[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-[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-[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>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile Menu Button -->
|
||||
<button id="mobile-menu-btn" class="mobile-menu-btn" aria-label="Open menu">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
|
||||
<!-- Mobile Overlay -->
|
||||
<div id="sidebar-overlay" class="sidebar-overlay"></div>
|
||||
|
||||
<!-- Sidebar Navigation -->
|
||||
<aside id="sidebar" class="sidebar">
|
||||
<!-- Logo Header -->
|
||||
<div class="sidebar-header">
|
||||
<a href="./" class="block">
|
||||
<img src="static/logo_horizontal.png" alt="LocalAI" class="w-full h-auto px-2">
|
||||
</a>
|
||||
<button id="sidebar-close-btn" class="sidebar-close-btn" aria-label="Close menu">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Content -->
|
||||
<nav class="sidebar-content">
|
||||
<!-- Main Section -->
|
||||
<div class="sidebar-section">
|
||||
<a href="./" class="nav-item" :class="{ 'active': window.location.pathname === '/' || window.location.pathname.endsWith('/index.html') }">
|
||||
<i class="fas fa-home nav-icon"></i>
|
||||
<span class="nav-label">Home</span>
|
||||
</a>
|
||||
<a href="browse/" class="nav-item">
|
||||
<i class="fas fa-download nav-icon"></i>
|
||||
<span class="nav-label">Install Models</span>
|
||||
</a>
|
||||
<a href="chat/" class="nav-item">
|
||||
<i class="fa-solid fa-comments nav-icon"></i>
|
||||
<span class="nav-label">Chat</span>
|
||||
</a>
|
||||
<a href="image/" class="nav-item">
|
||||
<i class="fas fa-image nav-icon"></i>
|
||||
<span class="nav-label">Images</span>
|
||||
</a>
|
||||
<a href="video/" class="nav-item">
|
||||
<i class="fas fa-video nav-icon"></i>
|
||||
<span class="nav-label">Video</span>
|
||||
</a>
|
||||
<a href="tts/" class="nav-item">
|
||||
<i class="fa-solid fa-music nav-icon"></i>
|
||||
<span class="nav-label">TTS</span>
|
||||
</a>
|
||||
<a href="sound/" class="nav-item">
|
||||
<i class="fas fa-volume-high nav-icon"></i>
|
||||
<span class="nav-label">Sound</span>
|
||||
</a>
|
||||
<a href="talk/" class="nav-item">
|
||||
<i class="fa-solid fa-phone nav-icon"></i>
|
||||
<span class="nav-label">Talk</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Tools Section -->
|
||||
<div class="sidebar-section">
|
||||
<div class="sidebar-section-title">Tools</div>
|
||||
<a href="agent-jobs" class="nav-item">
|
||||
<i class="fas fa-tasks nav-icon"></i>
|
||||
<span class="nav-label">Agent Jobs</span>
|
||||
</a>
|
||||
<a href="traces/" class="nav-item">
|
||||
<i class="fas fa-chart-line nav-icon"></i>
|
||||
<span class="nav-label">Traces</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- System Section -->
|
||||
<div class="sidebar-section">
|
||||
<div class="sidebar-section-title">System</div>
|
||||
<a href="swagger/index.html" class="nav-item">
|
||||
<i class="fas fa-code nav-icon"></i>
|
||||
<span class="nav-label">API</span>
|
||||
</a>
|
||||
<a href="browse/backends" class="nav-item">
|
||||
<i class="fas fa-server nav-icon"></i>
|
||||
<span class="nav-label">Backends</span>
|
||||
</a>
|
||||
<a href="p2p/" class="nav-item">
|
||||
<i class="fa-solid fa-circle-nodes nav-icon"></i>
|
||||
<span class="nav-label">Swarm</span>
|
||||
</a>
|
||||
<a href="/manage" class="nav-item">
|
||||
<i class="fas fa-cog nav-icon"></i>
|
||||
<span class="nav-label">Settings</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Theme Toggle Footer -->
|
||||
<div class="sidebar-footer">
|
||||
<div class="theme-toggle">
|
||||
<span class="theme-toggle-label">
|
||||
<i class="fas fa-sun" id="theme-icon-light"></i>
|
||||
<i class="fas fa-moon" id="theme-icon-dark" style="display: none;"></i>
|
||||
<span id="theme-label">Dark</span>
|
||||
</span>
|
||||
<div
|
||||
class="toggle-switch"
|
||||
id="theme-toggle-switch"
|
||||
onclick="toggleTheme && toggleTheme(); updateThemeUI();"
|
||||
role="switch"
|
||||
aria-label="Toggle theme"
|
||||
tabindex="0"
|
||||
onkeydown="if(event.key === 'Enter' || event.key === ' ') { event.preventDefault(); toggleTheme && toggleTheme(); updateThemeUI(); }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<script>
|
||||
// JavaScript to toggle the mobile menu with animation
|
||||
document.getElementById('menu-toggle').addEventListener('click', function () {
|
||||
var mobileMenu = document.getElementById('mobile-menu');
|
||||
if (mobileMenu.classList.contains('hidden')) {
|
||||
mobileMenu.classList.remove('hidden');
|
||||
// Use setTimeout to create a mild animation effect
|
||||
setTimeout(function() {
|
||||
mobileMenu.classList.add('opacity-100');
|
||||
mobileMenu.classList.remove('opacity-0');
|
||||
}, 10);
|
||||
} else {
|
||||
mobileMenu.classList.add('opacity-0');
|
||||
mobileMenu.classList.remove('opacity-100');
|
||||
// Wait for transition to finish before hiding
|
||||
setTimeout(function() {
|
||||
mobileMenu.classList.add('hidden');
|
||||
}, 300);
|
||||
// Mobile menu functionality
|
||||
(function() {
|
||||
const menuBtn = document.getElementById('mobile-menu-btn');
|
||||
const closeBtn = document.getElementById('sidebar-close-btn');
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const overlay = document.getElementById('sidebar-overlay');
|
||||
|
||||
function openSidebar() {
|
||||
sidebar.classList.add('open');
|
||||
overlay.classList.add('open');
|
||||
if (menuBtn) menuBtn.style.opacity = '0';
|
||||
if (menuBtn) menuBtn.style.pointerEvents = 'none';
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closeSidebar() {
|
||||
sidebar.classList.remove('open');
|
||||
overlay.classList.remove('open');
|
||||
if (menuBtn) menuBtn.style.opacity = '1';
|
||||
if (menuBtn) menuBtn.style.pointerEvents = '';
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
if (menuBtn) {
|
||||
menuBtn.addEventListener('click', openSidebar);
|
||||
}
|
||||
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', closeSidebar);
|
||||
}
|
||||
|
||||
if (overlay) {
|
||||
overlay.addEventListener('click', closeSidebar);
|
||||
}
|
||||
|
||||
// Close sidebar on escape key
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Escape' && sidebar.classList.contains('open')) {
|
||||
closeSidebar();
|
||||
}
|
||||
});
|
||||
|
||||
// Close sidebar when clicking a nav link on mobile
|
||||
const navLinks = sidebar.querySelectorAll('.nav-item');
|
||||
navLinks.forEach(function(link) {
|
||||
if (link.tagName === 'A') {
|
||||
link.addEventListener('click', function() {
|
||||
if (window.innerWidth < 1024) {
|
||||
closeSidebar();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// Theme UI update function
|
||||
function updateThemeUI() {
|
||||
const theme = (window.getCurrentTheme && window.getCurrentTheme()) || 'dark';
|
||||
const toggle = document.getElementById('theme-toggle-switch');
|
||||
const label = document.getElementById('theme-label');
|
||||
const iconLight = document.getElementById('theme-icon-light');
|
||||
const iconDark = document.getElementById('theme-icon-dark');
|
||||
|
||||
if (toggle) {
|
||||
if (theme === 'light') {
|
||||
toggle.classList.add('active');
|
||||
if (label) label.textContent = 'Light';
|
||||
if (iconLight) iconLight.style.display = 'none';
|
||||
if (iconDark) iconDark.style.display = 'inline';
|
||||
} else {
|
||||
toggle.classList.remove('active');
|
||||
if (label) label.textContent = 'Dark';
|
||||
if (iconLight) iconLight.style.display = 'inline';
|
||||
if (iconDark) iconDark.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize theme UI on load
|
||||
document.addEventListener('DOMContentLoaded', updateThemeUI);
|
||||
// Also run immediately in case DOM is already ready
|
||||
if (document.readyState !== 'loading') {
|
||||
updateThemeUI();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<nav class="bg-gradient-to-r from-gray-900 to-gray-950 shadow-lg border-b border-gray-800/50">
|
||||
<nav class="bg-[var(--color-bg-secondary)] shadow-lg border-b border-[var(--color-border-subtle)]">
|
||||
<div class="container mx-auto px-4 py-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
@@ -6,44 +6,44 @@
|
||||
<a href="./" class="flex items-center group">
|
||||
<img src="static/logo_horizontal.png"
|
||||
alt="LocalAI Logo"
|
||||
class="h-10 mr-3 rounded-lg border border-blue-600/30 shadow-md transition-all duration-300 group-hover:shadow-blue-500/20 group-hover:border-blue-500/50">
|
||||
class="h-10 mr-3 rounded-lg border border-[var(--color-primary-border)] shadow-md transition-all duration-300 group-hover:shadow-[var(--color-primary)]/20 group-hover:border-[var(--color-primary)]/50">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Menu button for small screens -->
|
||||
<div class="lg:hidden">
|
||||
<button id="menu-toggle" class="text-gray-300 hover:text-blue-400 focus:outline-none p-2 rounded-lg transition duration-300 ease-in-out hover:bg-gray-800/70">
|
||||
<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-primary)]">
|
||||
<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">
|
||||
<a href="./" class="text-gray-300 hover:text-white px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-blue-900/30 flex items-center">
|
||||
<i class="fas fa-home text-blue-400 mr-2"></i>Home
|
||||
<a href="./" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-primary)] flex items-center">
|
||||
<i class="fas fa-home text-[var(--color-primary)] mr-2"></i>Home
|
||||
</a>
|
||||
<a href="https://localai.io" target="_blank" class="text-gray-300 hover:text-white px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-blue-900/30 flex items-center group">
|
||||
<i class="fas fa-book-reader text-blue-400 mr-2"></i>Documentation
|
||||
<a href="https://localai.io" target="_blank" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-primary)] flex items-center group">
|
||||
<i class="fas fa-book-reader text-[var(--color-primary)] mr-2"></i>Documentation
|
||||
<i class="fas fa-external-link-alt text-xs ml-1 opacity-70 group-hover:opacity-100 transition-opacity"></i>
|
||||
</a>
|
||||
<a href="https://models.localai.io/" class="text-gray-300 hover:text-white px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-blue-900/30 flex items-center">
|
||||
<i class="fas fa-brain text-blue-400 mr-2"></i>Models
|
||||
<a href="https://models.localai.io/" class="text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out hover:bg-[var(--color-bg-primary)] flex items-center">
|
||||
<i class="fas fa-brain text-[var(--color-primary)] mr-2"></i>Models
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Collapsible menu for small screens -->
|
||||
<div class="hidden lg:hidden" id="mobile-menu">
|
||||
<div class="pt-3 pb-2 space-y-1 border-t border-gray-800/50 mt-2">
|
||||
<a href="./" class="block text-gray-300 hover:text-white hover:bg-blue-900/30 px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-home text-blue-400 mr-3 w-5 text-center"></i>Home
|
||||
<div class="pt-3 pb-2 space-y-1 border-t border-[var(--color-border-subtle)] mt-2">
|
||||
<a href="./" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-home text-[var(--color-primary)] mr-3 w-5 text-center"></i>Home
|
||||
</a>
|
||||
<a href="https://localai.io" target="_blank" class="block text-gray-300 hover:text-white hover:bg-blue-900/30 px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-book-reader text-blue-400 mr-3 w-5 text-center"></i>Documentation
|
||||
<a href="https://localai.io" target="_blank" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-book-reader text-[var(--color-primary)] mr-3 w-5 text-center"></i>Documentation
|
||||
<i class="fas fa-external-link-alt text-xs ml-1 opacity-70"></i>
|
||||
</a>
|
||||
<a href="https://models.localai.io/" class="block text-gray-300 hover:text-white hover:bg-blue-900/30 px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-brain text-blue-400 mr-3 w-5 text-center"></i>Models
|
||||
<a href="https://models.localai.io/" class="block text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-primary)] px-3 py-2 rounded-lg transition duration-300 ease-in-out flex items-center">
|
||||
<i class="fas fa-brain text-[var(--color-primary)] mr-3 w-5 text-center"></i>Models
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -71,4 +71,4 @@
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<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" .}}
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="settingsDashboard()">
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="fixed top-20 right-4 z-50 space-y-2" style="max-width: 400px;">
|
||||
@@ -71,7 +73,7 @@
|
||||
<input type="checkbox" x-model="settings.watchdog_enabled"
|
||||
@change="updateWatchdogEnabled()"
|
||||
class="sr-only peer">
|
||||
<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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -85,7 +87,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -110,7 +112,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -146,7 +148,7 @@
|
||||
<input type="checkbox" x-model="settings.force_eviction_when_busy"
|
||||
:disabled="!settings.watchdog_enabled"
|
||||
class="sr-only peer" :class="!settings.watchdog_enabled ? 'opacity-50' : ''">
|
||||
<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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -226,7 +228,7 @@
|
||||
<input type="checkbox" x-model="settings.memory_reclaimer_enabled"
|
||||
:disabled="!settings.watchdog_enabled"
|
||||
class="sr-only peer" :class="!settings.watchdog_enabled ? 'opacity-50' : ''">
|
||||
<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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -279,7 +281,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,7 +327,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-success)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -338,7 +340,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-success)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -351,7 +353,7 @@
|
||||
<label class="relative inline-flex items-center cursor-pointer">
|
||||
<input type="checkbox" x-model="settings.enable_tracing"
|
||||
class="sr-only peer">
|
||||
<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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-success)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -390,7 +392,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-warning)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -412,7 +414,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-warning)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -456,7 +458,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -551,7 +553,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -564,7 +566,7 @@
|
||||
<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-[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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-accent)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -616,7 +618,6 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -922,6 +923,10 @@ function resourceStatus() {
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
<script defer src="static/sound.js"></script>
|
||||
|
||||
<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="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Hero Section -->
|
||||
<div class="hero-section">
|
||||
@@ -169,6 +171,8 @@
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
<html lang="en">
|
||||
{{template "views/partials/head" .}}
|
||||
<script defer src="static/talk.js"></script>
|
||||
<body class="bg-[#101827] text-[#E5E7EB]" x-data="{ key: $store.chat.key }">
|
||||
<div class="flex flex-col min-h-screen">
|
||||
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]" x-data="{ key: $store.chat.key }">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Hero Section -->
|
||||
@@ -24,25 +26,25 @@
|
||||
<!-- Talk Interface Body -->
|
||||
<div class="p-6">
|
||||
<!-- Recording Status -->
|
||||
<div id="recording" class="bg-red-500/10 border border-red-500/30 rounded-lg p-4 mb-4 flex items-center space-x-3" style="display: none;">
|
||||
<i class="fa-solid fa-microphone text-2xl text-red-400"></i>
|
||||
<span class="text-red-300 font-medium">Recording... press "Stop recording" to stop</span>
|
||||
<div id="recording" class="bg-[var(--color-error-light)] border border-[var(--color-error)]/30 rounded-lg p-4 mb-4 flex items-center space-x-3" style="display: none;">
|
||||
<i class="fa-solid fa-microphone text-2xl text-[var(--color-error)]"></i>
|
||||
<span class="text-[var(--color-error)] font-medium">Recording... press "Stop recording" to stop</span>
|
||||
</div>
|
||||
|
||||
<!-- Loader -->
|
||||
<div id="loader" class="my-4 flex justify-center" style="display: none;">
|
||||
<div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-[#38BDF8]"></div>
|
||||
<div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-[var(--color-primary)]"></div>
|
||||
</div>
|
||||
|
||||
<!-- Status Text -->
|
||||
<div id="statustext" class="my-4 p-3 bg-[#101827]/50 border border-[#1E293B] rounded-lg text-[#E5E7EB]" style="min-height: 3rem;">Press the record button to start recording.</div>
|
||||
<div id="statustext" class="my-4 p-3 bg-[var(--color-bg-primary)]/50 border border-[var(--color-border-subtle)] rounded-lg text-[var(--color-text-primary)]" style="min-height: 3rem;">Press the record button to start recording.</div>
|
||||
|
||||
<!-- Note -->
|
||||
<div class="bg-[#38BDF8]/10 border border-[#38BDF8]/20 rounded-lg p-4 mb-6">
|
||||
<div class="bg-[var(--color-primary-light)] border border-[var(--color-primary)]/20 rounded-lg p-4 mb-6">
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-info-circle text-[#38BDF8] mt-1 mr-3 flex-shrink-0"></i>
|
||||
<p class="text-[#94A3B8]">
|
||||
<strong class="text-[#38BDF8]">Note:</strong> You need an LLM, an audio-transcription (whisper), and a TTS model installed for this to work. Select the appropriate models below and click 'Talk' to start recording. The recording will continue until you click 'Stop recording'. Make sure your microphone is set up and enabled.
|
||||
<i class="fas fa-info-circle text-[var(--color-primary)] mt-1 mr-3 flex-shrink-0"></i>
|
||||
<p class="text-[var(--color-text-secondary)]">
|
||||
<strong class="text-[var(--color-primary)]">Note:</strong> You need an LLM, an audio-transcription (whisper), and a TTS model installed for this to work. Select the appropriate models below and click 'Talk' to start recording. The recording will continue until you click 'Stop recording'. Make sure your microphone is set up and enabled.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,42 +53,42 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
|
||||
<!-- LLM Model -->
|
||||
<div class="space-y-2">
|
||||
<label for="modelSelect" class="flex items-center text-[#94A3B8] font-medium">
|
||||
<i class="fas fa-brain text-[#38BDF8] mr-2"></i>LLM Model
|
||||
<label for="modelSelect" class="flex items-center text-[var(--color-text-secondary)] font-medium">
|
||||
<i class="fas fa-brain text-[var(--color-primary)] mr-2"></i>LLM Model
|
||||
</label>
|
||||
<select id="modelSelect"
|
||||
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-[#38BDF8] focus:ring-2 focus:ring-[#38BDF8]/50 rounded-lg shadow-sm p-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
|
||||
class="w-full bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] focus:border-[var(--color-primary)] focus:ring-2 focus:ring-[var(--color-primary)]/50 rounded-lg shadow-sm p-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
|
||||
{{ range .ModelsConfig }}
|
||||
<option value="{{.}}" class="bg-[#101827] text-[#E5E7EB]">{{.}}</option>
|
||||
<option value="{{.}}" class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Whisper Model -->
|
||||
<div class="space-y-2">
|
||||
<label for="whisperModelSelect" class="flex items-center text-[#94A3B8] font-medium">
|
||||
<i class="fas fa-ear-listen text-[#8B5CF6] mr-2"></i>Whisper Model
|
||||
<label for="whisperModelSelect" class="flex items-center text-[var(--color-text-secondary)] font-medium">
|
||||
<i class="fas fa-ear-listen text-[var(--color-accent)] mr-2"></i>Whisper Model
|
||||
</label>
|
||||
<select id="whisperModelSelect"
|
||||
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-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
|
||||
class="w-full bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] focus:border-[var(--color-accent)] focus:ring-2 focus:ring-[var(--color-accent)]/50 rounded-lg shadow-sm p-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
|
||||
{{ range .ModelsConfig }}
|
||||
<option value="{{.}}" class="bg-[#101827] text-[#E5E7EB]">{{.}}</option>
|
||||
<option value="{{.}}" class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- TTS Model -->
|
||||
<div class="space-y-2">
|
||||
<label for="ttsModelSelect" class="flex items-center text-[#94A3B8] font-medium">
|
||||
<i class="fas fa-volume-high text-green-400 mr-2"></i>TTS Model
|
||||
<label for="ttsModelSelect" class="flex items-center text-[var(--color-text-secondary)] font-medium">
|
||||
<i class="fas fa-volume-high text-[var(--color-success)] mr-2"></i>TTS Model
|
||||
</label>
|
||||
<select id="ttsModelSelect"
|
||||
class="w-full bg-[#101827] text-[#E5E7EB] border border-[#1E293B] focus:border-green-500 focus:ring-2 focus:ring-green-500/50 rounded-lg shadow-sm p-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[#94A3B8]">Select a model</option>
|
||||
class="w-full bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] border border-[var(--color-border-subtle)] focus:border-[var(--color-success)] focus:ring-2 focus:ring-[var(--color-success)]/50 rounded-lg shadow-sm p-2.5 appearance-none">
|
||||
<option value="" disabled class="text-[var(--color-text-secondary)]">Select a model</option>
|
||||
{{ range .ModelsConfig }}
|
||||
<option value="{{.}}" class="bg-[#101827] text-[#E5E7EB]">{{.}}</option>
|
||||
<option value="{{.}}" class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
|
||||
{{ end }}
|
||||
</select>
|
||||
</div>
|
||||
@@ -95,15 +97,15 @@
|
||||
<!-- Buttons -->
|
||||
<div class="flex items-center justify-between mt-8">
|
||||
<button id="recordButton"
|
||||
class="inline-flex items-center bg-red-500 hover:bg-red-600 text-white font-semibold py-2 px-6 rounded-lg transition-colors">
|
||||
class="inline-flex items-center bg-[var(--color-error)] hover:bg-[var(--color-error)]/90 text-white font-semibold py-2 px-6 rounded-lg transition-colors">
|
||||
<i class="fas fa-microphone mr-2"></i>
|
||||
<span>Talk</span>
|
||||
</button>
|
||||
|
||||
<a id="resetButton"
|
||||
class="flex items-center text-[#38BDF8] hover:text-[#8B5CF6] transition-colors"
|
||||
class="flex items-center text-[var(--color-primary)] hover:text-[var(--color-accent)] transition-colors"
|
||||
href="#">
|
||||
<i class="fas fa-rotate-right mr-2"></i>
|
||||
<i class="fas fa-rotate-left mr-2"></i>
|
||||
<span>Reset conversation</span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -116,6 +118,8 @@
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
{{template "views/partials/head" .}}
|
||||
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="flex flex-col min-h-screen" x-data="tracesApp()" x-init="init()">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner" x-data="tracesApp()" x-init="init()">
|
||||
|
||||
<!-- Notifications -->
|
||||
<div class="fixed top-20 right-4 z-50 space-y-2" style="max-width: 400px;">
|
||||
@@ -73,7 +75,7 @@
|
||||
<input type="checkbox" x-model="settings.enable_tracing"
|
||||
@change="updateTracingEnabled()"
|
||||
class="sr-only peer">
|
||||
<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>
|
||||
<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-[var(--color-border-subtle)] after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--color-primary)]"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -155,7 +157,6 @@
|
||||
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -330,5 +331,10 @@ function tracesApp() {
|
||||
}
|
||||
</script>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
{{template "views/partials/head" .}}
|
||||
<script defer src="static/tts.js"></script>
|
||||
|
||||
<body class="bg-[#101827] text-[#E5E7EB]">
|
||||
<div class="flex flex-col min-h-screen">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner">
|
||||
<div class="container mx-auto px-4 py-8 flex-grow">
|
||||
<!-- Hero Section -->
|
||||
<div class="hero-section">
|
||||
@@ -22,12 +24,12 @@
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="card overflow-hidden">
|
||||
<!-- Header with Model Selection -->
|
||||
<div class="border-b border-[#1E293B] p-5">
|
||||
<div class="border-b border-[var(--color-border-subtle)] p-5">
|
||||
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<!-- Model Selection -->
|
||||
<div class="flex items-center" x-data="{ link : '{{ if .Model }}tts/{{.Model}}{{ end }}' }">
|
||||
<label for="model-select" class="mr-3 text-[#94A3B8] font-medium">
|
||||
<i class="fas fa-microphone-lines text-[#8B5CF6] mr-2"></i>Model:
|
||||
<label for="model-select" class="mr-3 text-[var(--color-text-secondary)] font-medium">
|
||||
<i class="fas fa-microphone-lines text-[var(--color-accent)] mr-2"></i>Model:
|
||||
</label>
|
||||
<select
|
||||
id="model-select"
|
||||
@@ -35,18 +37,18 @@
|
||||
@change="window.location = link"
|
||||
class="input p-2.5"
|
||||
>
|
||||
<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_TTS" }}
|
||||
<option value="tts/{{$cfg.Name}}" {{ if eq $cfg.Name $model }} selected {{end}} class="bg-[#101827] text-[#E5E7EB]">{{$cfg.Name}}</option>
|
||||
<option value="tts/{{$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="tts/{{.}}" {{ if eq . $model }} selected {{ end }} class="bg-[#101827] text-[#E5E7EB]">{{.}}</option>
|
||||
<option value="tts/{{.}}" {{ if eq . $model }} selected {{ end }} class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">{{.}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
@@ -55,10 +57,10 @@
|
||||
|
||||
<!-- Input Area -->
|
||||
<div class="p-6">
|
||||
<div class="bg-[#8B5CF6]/10 border border-[#8B5CF6]/20 rounded-lg p-4 mb-6">
|
||||
<div class="bg-[var(--color-accent-light)] border border-[var(--color-accent)]/20 rounded-lg p-4 mb-6">
|
||||
<div class="flex items-start">
|
||||
<i class="fas fa-info-circle text-[#8B5CF6] mt-1 mr-3 flex-shrink-0"></i>
|
||||
<p class="text-[#94A3B8]">
|
||||
<i class="fas fa-info-circle text-[var(--color-accent)] mt-1 mr-3 flex-shrink-0"></i>
|
||||
<p class="text-[var(--color-text-secondary)]">
|
||||
Enter your text below and submit to generate speech with the selected TTS model.
|
||||
The generated audio will appear below the input field.
|
||||
</p>
|
||||
@@ -77,7 +79,7 @@
|
||||
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 icon-hover">
|
||||
<button type="submit" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-[var(--color-accent)] hover:text-[var(--color-primary)] transition icon-hover">
|
||||
<i class="fas fa-paper-plane"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -85,11 +87,11 @@
|
||||
|
||||
<!-- Loading indicator -->
|
||||
<div class="flex justify-center my-6">
|
||||
<div id="loader" class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-[#8B5CF6]" style="display: none;"></div>
|
||||
<div id="loader" class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-[var(--color-accent)]" style="display: none;"></div>
|
||||
</div>
|
||||
|
||||
<!-- Results Area -->
|
||||
<div class="bg-[#101827]/50 border border-[#1E293B] rounded-lg p-4 min-h-[100px] flex items-center justify-center">
|
||||
<div class="bg-[var(--color-bg-primary)]/50 border border-[var(--color-border-subtle)] rounded-lg p-4 min-h-[100px] flex items-center justify-center">
|
||||
<div id="result" class="w-full"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,6 +100,8 @@
|
||||
</div>
|
||||
|
||||
{{template "views/partials/footer" .}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
{{template "views/partials/head" .}}
|
||||
<script defer src="static/video.js"></script>
|
||||
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)] flex flex-col h-screen">
|
||||
<div class="flex flex-col flex-1 overflow-hidden">
|
||||
|
||||
{{template "views/partials/navbar" .}}
|
||||
<body class="bg-[var(--color-bg-primary)] text-[var(--color-text-primary)]">
|
||||
<div class="app-layout">
|
||||
{{template "views/partials/navbar" .}}
|
||||
|
||||
<main class="main-content">
|
||||
<div class="main-content-inner h-screen flex flex-col">
|
||||
<div class="flex flex-1 overflow-hidden">
|
||||
<!-- Two Column Layout: Settings on Left, Preview on Right -->
|
||||
<div class="flex flex-col lg:flex-row flex-1 gap-4 p-4 overflow-hidden">
|
||||
@@ -274,6 +276,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user