Improve page style

This commit is contained in:
Ettore Di Giacinto
2025-03-03 23:32:06 +01:00
parent d714c4f80b
commit 5dd4b9cb65
4 changed files with 598 additions and 249 deletions

View File

@@ -30,6 +30,8 @@ func (app *App) registerRoutes(pool *state.AgentPool, webapp *fiber.App) {
webapp.Get("/", func(c *fiber.Ctx) error { webapp.Get("/", func(c *fiber.Ctx) error {
return c.Render("views/index", fiber.Map{ return c.Render("views/index", fiber.Map{
"Agents": pool.List(), "Agents": pool.List(),
"AgentCount": len(pool.List()),
"Actions": len(services.AvailableActions),
}) })
}) })

View File

@@ -6,93 +6,133 @@
<title>Agent List</title> <title>Agent List</title>
{{template "views/partials/header"}} {{template "views/partials/header"}}
</head> </head>
<body class="bg-gray-900 p-4 text-white font-sans"> <body>
{{template "views/partials/menu"}} {{template "views/partials/menu"}}
<!-- Toast for notifications --> <!-- Toast for notifications -->
<div id="toast" class="toast"> <div id="toast" class="toast">
<span id="toast-message"></span> <span id="toast-message"></span>
</div> </div>
<div class="max-w-6xl mx-auto"> <div class="container mx-auto">
<header class="text-center mb-8"> <header class="text-center mb-8">
<h1 class="text-4xl md:text-6xl font-bold text-primary">Smart Agent List</h1> <h1 class="text-4xl md:text-6xl font-bold">Smart Agent List</h1>
<p class="mt-4 text-gray-400">Manage and interact with your AI agents</p>
</header> </header>
<div class="button-container justify-center mb-6"> <div class="button-container justify-center mb-6">
<a href="/create" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out"> <a href="/create" class="action-btn start-btn">
Add New Agent <i class="fas fa-plus-circle"></i> Add New Agent
</a> </a>
<button id="toggle-import" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out ml-4"> <button id="toggle-import" class="action-btn" style="background: linear-gradient(135deg, var(--tertiary), #4a76a8);">
Import Agent <i class="fas fa-file-import"></i> Import Agent
</button> </button>
</div> </div>
<section id="import-section" class="hidden mb-8"> <section id="import-section" class="hidden mb-8">
<div class="section-box"> <div class="section-box">
<h2 class="text-2xl font-bold mb-4">Import Agent</h2> <h2>Import Agent</h2>
<!-- Response Messages Container --> <!-- Response Messages Container -->
<div id="response-container"> <div id="response-container">
<!-- Success Alert --> <!-- Success Alert -->
<div id="success-alert" class="alert alert-success"> <div id="success-alert" class="alert alert-success" style="display: none;">
Agent imported successfully! The page will refresh in a moment. Agent imported successfully! The page will refresh in a moment.
</div> </div>
<!-- Error Alert --> <!-- Error Alert -->
<div id="error-alert" class="alert alert-error"> <div id="error-alert" class="alert alert-error" style="display: none;">
<span id="error-message">Error importing agent.</span> <span id="error-message">Error importing agent.</span>
</div> </div>
</div> </div>
<form id='import-form' hx-encoding='multipart/form-data' hx-post='/settings/import' hx-target="#response-container" hx-swap="none"> <form id='import-form' hx-encoding='multipart/form-data' hx-post='/settings/import' hx-target="#response-container" hx-swap="none">
<div class="mb-4"> <div class="mb-4">
<label for="file" class="block text-gray-300 mb-2">Select Agent File:</label> <label for="file" class="block mb-2">Select Agent File:</label>
<input type='file' name='file' id='file' class="bg-gray-700 text-white rounded p-2 w-full"> <input type='file' name='file' id='file'>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<button id="import-button" type="submit" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded transition duration-300 ease-in-out"> <button id="import-button" type="submit" class="action-btn">
Import Agent <i class="fas fa-cloud-upload-alt"></i> Import Agent
</button> </button>
<progress id='progress' value='0' max='100' class="ml-4"></progress> <progress id='progress' value='0' max='100' class="ml-4"></progress>
</div> </div>
</form> </form>
</div> </div>
</section> </section>
<section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> <section class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{{ $status := .Status }} {{ $status := .Status }}
{{ range .Agents }} {{ range .Agents }}
<div class="card relative flex flex-col items-center text-center p-6 bg-gray-800 rounded-lg shadow-md"> <div hx-ext="sse" data-agent-name="{{.}}" class="card">
<img src="/public/agent_{{.}}.png" alt="{{.}}" class="w-24 h-24 rounded-full mb-4"> <div class="flex flex-col items-center text-center p-4">
<h2 class="text-xl font-bold mb-2">{{.}}</h2> <!--<img src="/public/agent_{{.}}.png" alt="{{.}}" class="w-24 h-24 rounded-full mb-4"
<p class="text-gray-400 mb-4">Online: {{ index $status . }}</p> style="border: 2px solid var(--primary); box-shadow: var(--neon-glow);">-->
<div class="flex justify-center space-x-4"> <h2>{{.}}</h2>
<a href="/status/{{.}}" class="text-indigo-500 hover:text-indigo-400"> <div class="mb-4 flex items-center justify-center">
<i class="fas fa-info-circle"></i> Status <span class="badge {{ if eq (index $status .) true }}badge-primary{{ else }}badge-secondary{{ end }} mr-2">
{{ if eq (index $status .) true }}Active{{ else }}Inactive{{ end }}
</span>
</div>
<div class="grid grid-cols-2 gap-2 w-full mb-4">
<a href="/status/{{.}}" class="action-btn flex items-center justify-center"
style="background: linear-gradient(135deg, #2a2a2a, #3a3a3a);">
<i class="fas fa-info-circle mr-2"></i> Status
</a> </a>
<a href="/talk/{{.}}" class="text-indigo-500 hover:text-indigo-400"> <a href="/talk/{{.}}" class="action-btn flex items-center justify-center"
<i class="fas fa-comments"></i> Talk style="background: linear-gradient(135deg, #2a2a2a, #3a3a3a);">
<i class="fas fa-comments mr-2"></i> Talk
</a> </a>
<button class="action-btn start-btn" hx-put="/start/{{.}}" hx-swap="none" hx-ext="json-enc" data-action="start" data-agent="{{.}}"> </div>
Start
<div class="grid grid-cols-3 gap-2 w-full">
<button class="action-btn start-btn col-span-1"
hx-put="/start/{{.}}" hx-swap="none" hx-ext="json-enc"
data-action="start" data-agent="{{.}}">
<i class="fas fa-play"></i>
</button> </button>
<button class="action-btn pause-btn" hx-put="/pause/{{.}}" hx-swap="none" hx-ext="json-enc" data-action="pause" data-agent="{{.}}"> <button class="action-btn pause-btn col-span-1"
Pause hx-put="/pause/{{.}}" hx-swap="none" hx-ext="json-enc"
data-action="pause" data-agent="{{.}}">
<i class="fas fa-pause"></i>
</button> </button>
<a href="/settings/{{.}}" class="text-indigo-500 hover:text-indigo-400"> <a href="/settings/{{.}}" class="action-btn col-span-1 flex items-center justify-center"
<i class="fas fa-cog"></i> Settings style="background: linear-gradient(135deg, #2a2a2a, #3a3a3a);">
<i class="fas fa-cog"></i>
</a> </a>
</div> </div>
</div> </div>
</div>
{{ end }} {{ end }}
</section> </section>
<footer class="mt-8 text-center">
<p>&copy; 2025 Smart Agent List. All rights reserved.</p> <div class="user-info mt-8 mb-4">
<span></span>
<span class="timestamp"></span>
</div>
<footer class="text-center text-gray-500 text-sm mb-8">
<p>&copy; 2025 LocalAgent.</p>
</footer> </footer>
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const importSection = document.getElementById('import-section'); const importSection = document.getElementById('import-section');
const toggleImport = document.getElementById('toggle-import'); const toggleImport = document.getElementById('toggle-import');
// Toggle import section visibility // Toggle import section visibility
toggleImport.addEventListener('click', function() { toggleImport.addEventListener('click', function() {
importSection.classList.toggle('hidden'); importSection.classList.toggle('hidden');
// Add glitch effect when showing
if (!importSection.classList.contains('hidden')) {
importSection.style.animation = 'glitch 0.3s';
setTimeout(() => {
importSection.style.animation = '';
}, 300);
}
}); });
// Handle import form submission // Handle import form submission
@@ -113,6 +153,7 @@
if (response.status === "ok") { if (response.status === "ok") {
// Show success message // Show success message
successAlert.style.display = 'block'; successAlert.style.display = 'block';
showToast("Agent imported successfully!", "success");
// Refresh the page after a short delay // Refresh the page after a short delay
setTimeout(() => { setTimeout(() => {
@@ -122,26 +163,35 @@
// Show error message // Show error message
errorMessage.textContent = response.error; errorMessage.textContent = response.error;
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
showToast("Import failed: " + response.error, "error");
} }
} catch (e) { } catch (e) {
// Handle parsing error // Handle parsing error
errorMessage.textContent = "Invalid response format"; errorMessage.textContent = "Invalid response format";
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
showToast("Invalid response format", "error");
} }
} else { } else {
// Handle HTTP error // Handle HTTP error
errorMessage.textContent = "Server error: " + xhr.status; errorMessage.textContent = "Server error: " + xhr.status;
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
showToast("Server error: " + xhr.status, "error");
} }
}); });
// Add event listeners for Start and Pause buttons // Add event listeners for Start and Pause buttons
document.querySelectorAll('.action-btn').forEach(button => { document.querySelectorAll('.action-btn[data-action]').forEach(button => {
button.addEventListener('htmx:beforeRequest', function() {
this.style.animation = 'pulse 0.5s';
});
button.addEventListener('htmx:afterRequest', function(event) { button.addEventListener('htmx:afterRequest', function(event) {
const xhr = event.detail.xhr; const xhr = event.detail.xhr;
const action = this.getAttribute('data-action'); const action = this.getAttribute('data-action');
const agent = this.getAttribute('data-agent'); const agent = this.getAttribute('data-agent');
this.style.animation = '';
if (xhr.status === 200) { if (xhr.status === 200) {
try { try {
const response = JSON.parse(xhr.responseText); const response = JSON.parse(xhr.responseText);
@@ -175,9 +225,15 @@
cards.forEach(card => { cards.forEach(card => {
const agentTitle = card.querySelector('h2').textContent; const agentTitle = card.querySelector('h2').textContent;
if (agentTitle === agentName) { if (agentTitle === agentName) {
// Update the "Online:" text // Update the badge
const statusText = card.querySelector('p'); const badge = card.querySelector('.badge');
statusText.textContent = `Online: ${isOnline}`; if (isOnline) {
badge.className = 'badge badge-primary mr-2';
badge.textContent = 'Active';
} else {
badge.className = 'badge badge-secondary mr-2';
badge.textContent = 'Inactive';
}
} }
}); });
} }

View File

@@ -3,207 +3,277 @@
<head> <head>
<title>Create New Agent</title> <title>Create New Agent</title>
{{template "views/partials/header"}} {{template "views/partials/header"}}
<script src="https://unpkg.com/htmx.org"></script>
<script src="https://unpkg.com/htmx.org/dist/ext/sse.js"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<style>
.alert {
padding: 12px 16px;
border-radius: 4px;
margin-bottom: 20px;
display: none;
}
.alert-success {
background-color: rgba(16, 185, 129, 0.2);
border: 1px solid #10b981;
color: #10b981;
}
.alert-error {
background-color: rgba(239, 68, 68, 0.2);
border: 1px solid #ef4444;
color: #ef4444;
}
</style>
</head> </head>
<body class="bg-gray-900 p-4 text-white font-sans"> <body>
{{template "views/partials/menu"}} {{template "views/partials/menu"}}
<div class="max-w-2xl mx-auto my-12 bg-gray-800 p-8 rounded-lg shadow-lg"> <div class="container">
<h1 class="text-3xl font-bold text-center mb-6 text-blue-400">Create New Agent</h1> <div class="section-box">
<h1>Create New Agent</h1>
<form id="create-agent-form" action="/create" method="POST">
<div class="mb-4">
<form id="create-agent-form" action="/create" method="POST" class="space-y-6"> <label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="Name">
<div class="mb-6">
<label for="name" class="block text-lg font-medium text-gray-400">Name</label>
<input type="text" name="name" id="name" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Name">
</div> </div>
<div id="connectorsSection"> <div id="connectorsSection">
<!-- Connectors will be added here dynamically -->
</div> </div>
<button type="button" id="addConnectorButton" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Add Connector <div class="button-container">
<button type="button" id="addConnectorButton" class="action-btn">
<i class="fas fa-plus-circle"></i> Add Connector
</button> </button>
</div>
<div class="mb-4" id="action_box">
<!-- Actions will be added here dynamically -->
</div>
<div class="button-container">
<button id="action_button" type="button" class="action-btn">
<i class="fas fa-plus-circle"></i> Add Action
</button>
</div>
<div class="mb-4" id="dynamic_box">
<!-- Dynamic prompts will be added here dynamically -->
</div>
<div class="button-container">
<button id="dynamic_button" type="button" class="action-btn">
<i class="fas fa-plus-circle"></i> Add Dynamic Prompt
</button>
</div>
<div class="mb-4">
<label for="hud" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="hud" id="hud">
<span class="checkmark"></span>
</span>
HUD
</label>
</div>
<div class="mb-4">
<label for="enable_kb" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="enable_kb" id="enable_kb">
<span class="checkmark"></span>
</span>
Enable Knowledge Base
</label>
</div>
<div class="mb-4">
<label for="enable_reasoning" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="enable_reasoning" id="enable_reasoning">
<span class="checkmark"></span>
</span>
Enable Reasoning
</label>
</div>
<div class="mb-4">
<label for="kb_results">Knowledge Base Results</label>
<input type="text" name="kb_results" id="kb_results" placeholder="3">
</div>
<div class="mb-4">
<label for="standalone_job" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="standalone_job" id="standalone_job">
<span class="checkmark"></span>
</span>
Standalone Job
</label>
</div>
<div class="mb-4">
<label for="initiate_conversations" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="initiate_conversations" id="initiate_conversations">
<span class="checkmark"></span>
</span>
Initiate Conversations
</label>
</div>
<div class="mb-4">
<label for="can_stop_itself" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="can_stop_itself" id="can_stop_itself">
<span class="checkmark"></span>
</span>
Can Stop Itself
</label>
</div>
<div class="mb-4">
<label for="random_identity" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="random_identity" id="random_identity">
<span class="checkmark"></span>
</span>
Random Identity
</label>
</div>
<div class="mb-4">
<label for="long_term_memory" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="long_term_memory" id="long_term_memory">
<span class="checkmark"></span>
</span>
Long Term Memory
</label>
</div>
<div class="mb-4">
<label for="summary_long_term_memory" class="checkbox-label">
<span class="checkbox-custom">
<input type="checkbox" name="summary_long_term_memory" id="summary_long_term_memory">
<span class="checkmark"></span>
</span>
Long Term Memory (Summarize!)
</label>
</div>
<div class="mb-4">
<label for="identity_guidance">Identity Guidance</label>
<textarea name="identity_guidance" id="identity_guidance" placeholder="Identity Guidance"></textarea>
</div>
<div class="mb-4">
<label for="periodic_runs">Periodic Runs</label>
<input type="text" name="periodic_runs" id="periodic_runs" placeholder="Periodic Runs">
</div>
<div class="mb-4">
<label for="permanent_goal">Permanent Goal</label>
<textarea name="permanent_goal" id="permanent_goal" placeholder="Permanent goal"></textarea>
</div>
<div class="mb-4">
<label for="system_prompt">System Prompt</label>
<textarea name="system_prompt" id="system_prompt" placeholder="System prompt"></textarea>
</div>
<button type="submit" id="create-button">
<i class="fas fa-robot"></i> Create Agent
</button>
</form>
</div>
<!-- Response Messages Container -->
<div id="response-container">
<!-- Alert messages will be shown here -->
<div id="success-alert" class="alert alert-success" style="display: none;">
Agent created successfully! Redirecting to agent list...
</div>
<div id="error-alert" class="alert alert-error" style="display: none;">
<span id="error-message">Error creating agent.</span>
</div>
</div>
</div>
<!-- Toast notification container -->
<div id="toast" class="toast">
<span id="toast-message"></span>
</div>
<script> <script>
const actions = `{{ range .Actions }}<option value="{{.}}">{{.}}</option>{{ end }}`; const actions = `{{ range .Actions }}<option value="{{.}}">{{.}}</option>{{ end }}`;
const connectors = `{{ range .Connectors }}<option value="{{.}}">{{.}}</option>{{ end }}`; const connectors = `{{ range .Connectors }}<option value="{{.}}">{{.}}</option>{{ end }}`;
const promptBlocks = `{{ range .PromptBlocks }}<option value="{{.}}">{{.}}</option>{{ end }}`;
// Add connector functionality
document.getElementById('addConnectorButton').addEventListener('click', function() { document.getElementById('addConnectorButton').addEventListener('click', function() {
const connectorsSection = document.getElementById('connectorsSection'); const connectorsSection = document.getElementById('connectorsSection');
const newConnectorIndex = connectorsSection.getElementsByClassName('connector').length; const newConnectorIndex = connectorsSection.getElementsByClassName('connector').length;
const newConnectorHTML = ` const newConnectorHTML = `
<div class="connector mb-4"> <div class="connector mb-4 section-box" style="margin-top: 15px; padding: 15px;">
<h2>Connector ${newConnectorIndex + 1}</h2>
<div class="mb-4"> <div class="mb-4">
<label for="connectorType${newConnectorIndex}" class="block text-lg font-medium text-gray-400">Connector Type</label> <label for="connectorType${newConnectorIndex}">Connector Type</label>
<select name="connectors[${newConnectorIndex}].type" id="connectorType${newConnectorIndex}" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white"> <select name="connectors[${newConnectorIndex}].type" id="connectorType${newConnectorIndex}">
`+connectors+` </select> ${connectors}
</select>
</div> </div>
<div class="mb-4"> <div class="mb-4">
<label for="connectorConfig${newConnectorIndex}" class="block text-lg font-medium text-gray-400">Connector Config (JSON)</label> <label for="connectorConfig${newConnectorIndex}">Connector Config (JSON)</label>
<textarea id="connectorConfig${newConnectorIndex}" name="connectors[${newConnectorIndex}].config" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='{"token":"sk-mg3.."}'>{}</textarea> <textarea id="connectorConfig${newConnectorIndex}" name="connectors[${newConnectorIndex}].config" placeholder='{"token":"sk-mg3.."}'>{}</textarea>
</div> </div>
</div> </div>
`; `;
connectorsSection.insertAdjacentHTML('beforeend', newConnectorHTML); connectorsSection.insertAdjacentHTML('beforeend', newConnectorHTML);
}); });
</script>
<div class="mb-4" id="action_box"> // Add action functionality
</div>
<button id="action_button" type="button" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">Add action</button>
<script>
document.getElementById('action_button').addEventListener('click', function() { document.getElementById('action_button').addEventListener('click', function() {
const actionsSection = document.getElementById('action_box'); const actionsSection = document.getElementById('action_box');
const ii = actionsSection.getElementsByClassName('action').length; const ii = actionsSection.getElementsByClassName('action').length;
const newActionHTML = ` const newActionHTML = `
<div class="action mb-4"> <div class="action mb-4 section-box" style="margin-top: 15px; padding: 15px;">
<label for="actionsName${ii}" class="block text-lg font-medium text-gray-400">Action</label> <h2>Action ${ii + 1}</h2>
<select name="actions[${ii}].name" id="actionsName${ii}" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white">
`+actions+`</select>
<div class="mb-4"> <div class="mb-4">
<label for="actionsConfig${ii}" class="block text-lg font-medium text-gray-400">Action Config (JSON)</label> <label for="actionsName${ii}">Action</label>
<textarea id="actionsConfig${ii}" name="actions[${ii}].config" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='{"results":"5"}'>{}</textarea> <select name="actions[${ii}].name" id="actionsName${ii}">
${actions}
</select>
</div>
<div class="mb-4">
<label for="actionsConfig${ii}">Action Config (JSON)</label>
<textarea id="actionsConfig${ii}" name="actions[${ii}].config" placeholder='{"results":"5"}'>{}</textarea>
</div> </div>
</div> </div>
`; `;
actionsSection.insertAdjacentHTML('beforeend', newActionHTML); actionsSection.insertAdjacentHTML('beforeend', newActionHTML);
}); });
</script>
<div class="mb-4" id="dynamic_box">
</div>
<button id="dynamic_button" type="button" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">Add Dynamic prompt</button>
<script>
const promptBlocks = `{{ range .PromptBlocks }}<option value="{{.}}">{{.}}</option>{{ end }}`;
// Add dynamic prompt functionality
document.getElementById('dynamic_button').addEventListener('click', function() { document.getElementById('dynamic_button').addEventListener('click', function() {
const actionsSection = document.getElementById('dynamic_box'); const promptsSection = document.getElementById('dynamic_box');
const ii = actionsSection.getElementsByClassName('promptBlock').length; const ii = promptsSection.getElementsByClassName('promptBlock').length;
const newActionHTML = ` const newPromptHTML = `
<div class="promptBlock mb-4"> <div class="promptBlock mb-4 section-box" style="margin-top: 15px; padding: 15px;">
<label for="promptName${ii}" class="block text-lg font-medium text-gray-400">Block Prompt</label> <h2>Prompt Block ${ii + 1}</h2>
<select name="promptblocks[${ii}].name" id="promptName${ii}" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white">
`+promptBlocks+`</select>
<div class="mb-4"> <div class="mb-4">
<label for="promptConfig${ii}" class="block text-lg font-medium text-gray-400">Prompt Config (JSON)</label> <label for="promptName${ii}">Block Prompt</label>
<textarea id="promptConfig${ii}" name="promptblocks[${ii}].config" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder='{"results":"5"}'>{}</textarea> <select name="promptblocks[${ii}].name" id="promptName${ii}">
${promptBlocks}
</select>
</div>
<div class="mb-4">
<label for="promptConfig${ii}">Prompt Config (JSON)</label>
<textarea id="promptConfig${ii}" name="promptblocks[${ii}].config" placeholder='{"results":"5"}'>{}</textarea>
</div> </div>
</div> </div>
`; `;
actionsSection.insertAdjacentHTML('beforeend', newActionHTML); promptsSection.insertAdjacentHTML('beforeend', newPromptHTML);
}); });
</script>
<div class="mb-4"> // Form submission handling
<label for="hud" class="block text-lg font-medium text-gray-400">HUD</label>
<input type="checkbox" name="hud" id="hud" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
</div>
<div class="mb-4">
</div>
<div class="mb-4">
<label for="enable_kb" class="block text-lg font-medium text-gray-400">Enable Knowledge Base</label>
<input type="checkbox" name="enable_kb" id="enable_kb" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="enable_reasoning" class="block text-lg font-medium text-gray-400">Enable Reasoning</label>
<input type="checkbox" name="enable_reasoning" id="enable_reasoning" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<div class="mb-6">
<label for="kb_results" class="block text-lg font-medium text-gray-400">Knowledge base results</label>
<input type="text" name="kb_results" id="kb_results" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white" placeholder="3">
</div>
<label for="standalone_job" class="block text-lg font-medium text-gray-400">Standalone Job</label>
<input type="checkbox" name="standalone_job" id="standalone_job" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="initiate_conversations" class="block text-lg font-medium text-gray-400">Initiate conversations</label>
<input type="checkbox" name="random_identity" id="initiate_conversations" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="can_stop_itself" class="block text-lg font-medium text-gray-400">Can stop itself</label>
<input type="checkbox" name="can_stop_itself" id="can_stop_itself" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="random_identity" class="block text-lg font-medium text-gray-400">Random Identity</label>
<input type="checkbox" name="random_identity" id="random_identity" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="long_term_memory" class="block text-lg font-medium text-gray-400">Long term memory</label>
<input type="checkbox" name="long_term_memory" id="long_term_memory" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
<label for="summary_long_term_memory" class="block text-lg font-medium text-gray-400">Long term memory (summarize!)</label>
<input type="checkbox" name="summary_long_term_memory" id="summary_long_term_memory" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white">
</div>
<div class="mb-4">
<label for="identity_guidance" class="block text-lg font-medium text-gray-400">Identity Guidance</label>
<textarea name="identity_guidance" id="identity_guidance" class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-lg border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Identity Guidance"></textarea>
</div>
<div class="mb-4">
<label for="periodic_runs" class="block text-lg font-medium text-gray-400">Periodic Runs</label>
<input type="text" name="periodic_runs" id="periodic_runs" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Periodic Runs">
</div>
<div class="mb-4">
<label for="permanent_goal" class="block text-lg font-medium text-gray-400">Permanent goal</label>
<textarea name="permanent_goal" id="permanent_goal" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder="Permanent goal"></textarea>
</div>
<div class="mb-4">
<label for="system_prompt" class="block text-lg font-medium text-gray-400">System prompt</label>
<textarea name="system_prompt" id="system_prompt" class="mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md bg-gray-700 text-white" placeholder="System prompt"></textarea>
</div>
<div class="flex items-center justify-between">
<button type="submit" id="create-button" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-500 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Create Agent
</button>
</div>
</form>
</div>
<!-- Response Messages Container -->
<div id="response-container">
<!-- Success Alert -->
<div id="success-alert" class="alert alert-success">
Agent created successfully! Redirecting to agent list...
</div>
<!-- Error Alert -->
<div id="error-alert" class="alert alert-error">
<span id="error-message">Error creating agent.</span>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Convert regular form to use HTMX
const form = document.getElementById('create-agent-form'); const form = document.getElementById('create-agent-form');
// Add HTMX attributes to the form
form.setAttribute('hx-post', '/create');
form.setAttribute('hx-swap', 'none');
form.addEventListener('submit', function(e) { form.addEventListener('submit', function(e) {
e.preventDefault(); e.preventDefault();
// Show a loading state
const createButton = document.getElementById('create-button');
const originalButtonText = createButton.innerHTML;
createButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Creating...';
createButton.disabled = true;
// Get form data // Get form data
const formData = new FormData(form); const formData = new FormData(form);
@@ -223,6 +293,9 @@
errorAlert.style.display = 'none'; errorAlert.style.display = 'none';
if (data.status === "ok") { if (data.status === "ok") {
// Show success toast
showToast('Agent created successfully!', 'success');
// Show success message // Show success message
successAlert.style.display = 'block'; successAlert.style.display = 'block';
@@ -231,31 +304,60 @@
window.location.href = '/'; window.location.href = '/';
}, 2000); }, 2000);
} else if (data.error) { } else if (data.error) {
// Show error toast
showToast('Error: ' + data.error, 'error');
// Show error message // Show error message
errorMessage.textContent = data.error; errorMessage.textContent = data.error;
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
// Scroll to the error message // Restore button state
errorAlert.scrollIntoView({ behavior: 'smooth', block: 'center' }); createButton.innerHTML = originalButtonText;
createButton.disabled = false;
} else { } else {
// Handle unexpected response format // Handle unexpected response format
showToast('Unexpected response format', 'error');
errorMessage.textContent = "Unexpected response format"; errorMessage.textContent = "Unexpected response format";
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
// Restore button state
createButton.innerHTML = originalButtonText;
createButton.disabled = false;
} }
}) })
.catch(error => { .catch(error => {
// Handle network or other errors // Handle network or other errors
showToast('Network error: ' + error.message, 'error');
const errorAlert = document.getElementById('error-alert'); const errorAlert = document.getElementById('error-alert');
const errorMessage = document.getElementById('error-message'); const errorMessage = document.getElementById('error-message');
errorMessage.textContent = "Network error: " + error.message; errorMessage.textContent = "Network error: " + error.message;
errorAlert.style.display = 'block'; errorAlert.style.display = 'block';
errorAlert.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Restore button state
createButton.innerHTML = originalButtonText;
createButton.disabled = false;
}); });
}); });
}); });
// Add additional CSS for checkbox labels
document.addEventListener('DOMContentLoaded', function() {
const style = document.createElement('style');
style.textContent = `
.checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
margin-bottom: 10px;
}
.checkbox-label .checkbox-custom {
margin-right: 10px;
}
`;
document.head.appendChild(style);
});
</script> </script>
</div>
</body> </body>
</html> </html>

View File

@@ -4,28 +4,217 @@
<title>Smart Assistant Dashboard</title> <title>Smart Assistant Dashboard</title>
{{template "views/partials/header"}} {{template "views/partials/header"}}
<style> <style>
.image-container {
display: flex;
justify-content: center;
margin: 40px 0;
position: relative;
}
.image-container img {
filter: drop-shadow(0 0 15px rgba(94, 0, 255, 0.6));
transition: all 0.5s ease;
}
.image-container:hover img {
transform: scale(1.05);
filter: drop-shadow(0 0 25px rgba(0, 255, 149, 0.8));
}
.image-container::after {
content: "";
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
width: 40%;
height: 2px;
background: linear-gradient(90deg, transparent, var(--primary), var(--secondary), var(--tertiary), transparent);
}
.dashboard-title {
text-align: center;
margin-bottom: 40px;
font-size: 2.5rem;
letter-spacing: 2px;
position: relative;
display: inline-block;
left: 50%;
transform: translateX(-50%);
color: var(--primary);
text-shadow: var(--neon-glow);
animation: gentlePulse 3s infinite;
}
/* Gentle pulse animation for the title */
@keyframes gentlePulse {
0% { text-shadow: 0 0 7px var(--primary), 0 0 10px var(--primary); }
50% { text-shadow: 0 0 12px var(--primary), 0 0 20px var(--primary); }
100% { text-shadow: 0 0 7px var(--primary), 0 0 10px var(--primary); }
}
/* Subtle glitch effect for hover */
@keyframes subtleGlitch {
0% { transform: translateX(-50%); }
20% { transform: translateX(-50%) translate(-1px, 1px); }
40% { transform: translateX(-50%) translate(-1px, -1px); }
60% { transform: translateX(-50%) translate(1px, 1px); }
80% { transform: translateX(-50%) translate(1px, -1px); }
100% { transform: translateX(-50%); }
}
.cards-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 30px;
margin-top: 30px;
}
.user-info {
position: absolute;
top: 20px;
right: 20px;
}
.dashboard-stats {
display: flex;
justify-content: center;
gap: 20px;
margin: 20px 0 40px;
}
.stat-item {
background: rgba(17, 17, 17, 0.7);
border-radius: 8px;
padding: 15px;
min-width: 150px;
text-align: center;
position: relative;
overflow: hidden;
border: 1px solid rgba(94, 0, 255, 0.2);
}
.stat-item::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg, var(--primary), var(--secondary));
opacity: 0.7;
}
.stat-count {
font-size: 2rem;
font-weight: 700;
color: var(--primary);
text-shadow: var(--neon-glow);
}
.stat-label {
font-size: 0.9rem;
color: #cccccc;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Badge positioning */
.card {
position: relative;
}
.badge {
position: absolute;
top: 15px;
right: 15px;
}
</style> </style>
</head> </head>
<body class="bg-gray-900 p-4 text-white font-sans"> <body>
{{template "views/partials/menu"}} {{template "views/partials/menu"}}
<div class="container"> <div class="container">
<div class="image-container"> <div class="image-container">
<img src="/public/logo_1.png" width="250" alt="Company Logo"> <img src="/public/logo_1.png" width="250" alt="Company Logo">
</div> </div>
<h1 class="dashboard-title">SMART ASSISTANT DASHBOARD</h1>
<!-- Simple stats display -->
<div class="dashboard-stats">
<div class="stat-item">
<div class="stat-count">{{.Actions}}</div>
<div class="stat-label">Available Actions</div>
</div>
<div class="stat-item">
<div class="stat-count">{{ .AgentCount }}</div>
<div class="stat-label">Agents</div>
</div>
</div>
<div class="cards-container">
<!-- Card for Agent List Page --> <!-- Card for Agent List Page -->
<a href="/agents" class="card-link"> <a href="/agents" class="card-link">
<div class="card"> <div class="card">
<h2>Agent List</h2> <h2><i class="fas fa-robot"></i> Agent List</h2>
<p>View and manage your list of agents, including detailed profiles and statistics.</p> <p>View and manage your list of agents, including detailed profiles and statistics.</p>
</div> </div>
</a> </a>
<!-- Card for Create Agent -->
<a href="/create" class="card-link"> <a href="/create" class="card-link">
<div class="card"> <div class="card">
<h2>Create</h2> <h2><i class="fas fa-plus-circle"></i> Create Agent</h2>
<p>Create a new agent.</p> <p>Create a new intelligent agent with custom behaviors, connectors, and actions.</p>
</div>
</a>
<!-- Additional Cards for Future Features -->
<a href="#" class="card-link">
<div class="card">
<h2><i class="fas fa-chart-line"></i> Analytics</h2>
<p>View performance metrics and insights from your agent operations.</p>
<span class="badge badge-secondary">Coming Soon</span>
</div>
</a>
<a href="#" class="card-link">
<div class="card">
<h2><i class="fas fa-cogs"></i> Settings</h2>
<p>Configure system preferences and global settings for all agents.</p>
<span class="badge badge-secondary">Coming Soon</span>
</div> </div>
</a> </a>
</div> </div>
</div>
<!-- Toast notification container -->
<div id="toast" class="toast">
<span id="toast-message"></span>
</div>
<script>
// Add the controlled glitch effect to dashboard title
document.addEventListener('DOMContentLoaded', function() {
const title = document.querySelector('.dashboard-title');
title.addEventListener('mouseover', function() {
// Use the more subtle glitch animation
this.style.animation = 'subtleGlitch 0.5s infinite';
});
title.addEventListener('mouseout', function() {
// Return to gentle pulse animation
this.style.animation = 'gentlePulse 3s infinite';
});
// Welcome toast notification
setTimeout(() => {
showToast('Welcome to Smart Assistant Dashboard', 'success');
}, 1000);
});
</script>
</body> </body>
</html> </html>