feat(web): Add admin and index UI for PromptManager
This commit is contained in:
+1458
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>PromptManager - Redirecting to Admin</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
||||
min-height: 100vh;
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.container {
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #4facfe;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
h1 {
|
||||
background: linear-gradient(45deg, #4facfe, #00f2fe);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="spinner"></div>
|
||||
<h1>🔍 PromptManager</h1>
|
||||
<p>Redirecting to Admin Dashboard...</p>
|
||||
<p><small>Loading the full-featured prompt management interface</small></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Redirect to admin interface immediately
|
||||
window.location.href = '/prompt_manager/admin';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,673 @@
|
||||
// PromptManager/web/prompt_manager.js
|
||||
|
||||
import { app } from "../../scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "PromptManager.UI",
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
if (nodeData.name === "PromptManager") {
|
||||
console.log("[PromptManager] Patching node type for custom UI");
|
||||
|
||||
// Store original methods
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
|
||||
// Override onNodeCreated to add custom UI
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
if (onNodeCreated) {
|
||||
onNodeCreated.apply(this, arguments);
|
||||
}
|
||||
|
||||
// Initialize properties for search state
|
||||
this.properties = this.properties || {};
|
||||
this.properties.searchResults = [];
|
||||
this.properties.selectedPromptIndex = -1;
|
||||
this.properties.resultTimeout = 3; // Default 3 seconds
|
||||
this.properties.showTestButton = false; // Default hide test button
|
||||
this.properties.webuiDisplayMode = 'popup'; // Default popup mode
|
||||
this.resultHideTimer = null;
|
||||
|
||||
// Load settings from API
|
||||
this.loadSettings();
|
||||
|
||||
// Create DOM widget container
|
||||
const container = document.createElement("div");
|
||||
container.style.padding = "8px";
|
||||
container.style.backgroundColor = "#2a2a2a";
|
||||
container.style.borderRadius = "4px";
|
||||
container.style.marginTop = "5px";
|
||||
|
||||
this.createSearchUI(container);
|
||||
|
||||
// Add as DOM widget
|
||||
this.searchWidget = this.addDOMWidget(
|
||||
"prompt_manager_search_ui",
|
||||
"div",
|
||||
container,
|
||||
);
|
||||
|
||||
// Recompute size
|
||||
this.size = this.computeSize();
|
||||
this.setDirtyCanvas(true, true);
|
||||
};
|
||||
|
||||
// Method to create search UI elements
|
||||
nodeType.prototype.createSearchUI = function (container) {
|
||||
container.innerHTML = "";
|
||||
|
||||
// Search controls section
|
||||
const searchSection = document.createElement("div");
|
||||
searchSection.style.marginBottom = "10px";
|
||||
|
||||
// Search buttons row
|
||||
const buttonRow = document.createElement("div");
|
||||
buttonRow.style.display = "flex";
|
||||
buttonRow.style.gap = "8px";
|
||||
buttonRow.style.marginBottom = "8px";
|
||||
|
||||
// Search button
|
||||
const searchButton = document.createElement("button");
|
||||
searchButton.textContent = "🔍 Search";
|
||||
searchButton.style.flex = "1";
|
||||
searchButton.style.padding = "6px 12px";
|
||||
searchButton.style.backgroundColor = "#4a90e2";
|
||||
searchButton.style.color = "white";
|
||||
searchButton.style.border = "none";
|
||||
searchButton.style.borderRadius = "4px";
|
||||
searchButton.style.cursor = "pointer";
|
||||
searchButton.style.fontSize = "12px";
|
||||
searchButton.addEventListener("click", () => this.performSearch());
|
||||
buttonRow.appendChild(searchButton);
|
||||
|
||||
// Recent prompts button
|
||||
const recentButton = document.createElement("button");
|
||||
recentButton.textContent = "📋 Recent";
|
||||
recentButton.style.flex = "1";
|
||||
recentButton.style.padding = "6px 12px";
|
||||
recentButton.style.backgroundColor = "#7b68ee";
|
||||
recentButton.style.color = "white";
|
||||
recentButton.style.border = "none";
|
||||
recentButton.style.borderRadius = "4px";
|
||||
recentButton.style.cursor = "pointer";
|
||||
recentButton.style.fontSize = "12px";
|
||||
recentButton.addEventListener("click", () => this.loadRecentPrompts());
|
||||
buttonRow.appendChild(recentButton);
|
||||
|
||||
// Web UI button
|
||||
const webUIButton = document.createElement("button");
|
||||
webUIButton.textContent = "🌐 Web UI";
|
||||
webUIButton.style.flex = "1";
|
||||
webUIButton.style.padding = "6px 12px";
|
||||
webUIButton.style.backgroundColor = "#50c878";
|
||||
webUIButton.style.color = "white";
|
||||
webUIButton.style.border = "none";
|
||||
webUIButton.style.borderRadius = "4px";
|
||||
webUIButton.style.cursor = "pointer";
|
||||
webUIButton.style.fontSize = "12px";
|
||||
webUIButton.addEventListener("click", () => this.openWebInterface());
|
||||
buttonRow.appendChild(webUIButton);
|
||||
|
||||
searchSection.appendChild(buttonRow);
|
||||
|
||||
// Add test API button for debugging (only if enabled in settings)
|
||||
if (this.properties.showTestButton) {
|
||||
const testRow = document.createElement("div");
|
||||
testRow.style.marginBottom = "8px";
|
||||
|
||||
const testButton = document.createElement("button");
|
||||
testButton.textContent = "🔧 Test API";
|
||||
testButton.style.width = "100%";
|
||||
testButton.style.padding = "4px 8px";
|
||||
testButton.style.backgroundColor = "#666";
|
||||
testButton.style.color = "white";
|
||||
testButton.style.border = "none";
|
||||
testButton.style.borderRadius = "3px";
|
||||
testButton.style.cursor = "pointer";
|
||||
testButton.style.fontSize = "10px";
|
||||
testButton.addEventListener("click", () => this.testApiConnection());
|
||||
testRow.appendChild(testButton);
|
||||
|
||||
searchSection.appendChild(testRow);
|
||||
}
|
||||
|
||||
// Results section
|
||||
const resultsSection = document.createElement("div");
|
||||
resultsSection.style.maxHeight = "200px";
|
||||
resultsSection.style.overflowY = "auto";
|
||||
resultsSection.style.border = "1px solid #444";
|
||||
resultsSection.style.borderRadius = "4px";
|
||||
resultsSection.style.backgroundColor = "#333";
|
||||
resultsSection.style.padding = "8px";
|
||||
resultsSection.style.fontSize = "11px";
|
||||
resultsSection.style.fontFamily = "monospace";
|
||||
resultsSection.innerHTML =
|
||||
'<div style="color: #999; text-align: center; padding: 20px;">💡 Click Search or Recent to find prompts</div>';
|
||||
|
||||
this.resultsSection = resultsSection;
|
||||
searchSection.appendChild(resultsSection);
|
||||
|
||||
container.appendChild(searchSection);
|
||||
};
|
||||
|
||||
// Method to perform search
|
||||
nodeType.prototype.performSearch = async function () {
|
||||
try {
|
||||
this.resultsSection.innerHTML =
|
||||
'<div style="color: #999; text-align: center;">🔍 Searching database...</div>';
|
||||
|
||||
// Get search criteria from node widgets (only search_text available now)
|
||||
const searchText =
|
||||
this.widgets?.find((w) => w.name === "search_text")?.value || "";
|
||||
|
||||
// Validate search criteria
|
||||
if (!searchText.trim()) {
|
||||
this.resultsSection.innerHTML =
|
||||
'<div style="color: #f9a825; text-align: center;">⚠️ Please enter search text</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the backend search function
|
||||
const response = await this.callNodeMethod("search_prompts", {
|
||||
search_text: searchText,
|
||||
});
|
||||
|
||||
this.displayResults(response.results || []);
|
||||
|
||||
// Show success notification
|
||||
if (response.results && response.results.length > 0) {
|
||||
this.showNotification(
|
||||
`Found ${response.results.length} prompts`,
|
||||
"success",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] Search error:", error);
|
||||
this.resultsSection.innerHTML = `<div style="color: #f88; text-align: center;">❌ Search error: ${error.message}</div>`;
|
||||
this.showNotification(`Search failed: ${error.message}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
// Method to load recent prompts
|
||||
nodeType.prototype.loadRecentPrompts = async function () {
|
||||
try {
|
||||
this.resultsSection.innerHTML =
|
||||
'<div style="color: #999; text-align: center;">📋 Loading recent prompts...</div>';
|
||||
|
||||
const response = await this.callNodeMethod("get_recent_prompts", {
|
||||
limit: 20,
|
||||
});
|
||||
this.displayResults(response.results || []);
|
||||
|
||||
// Show success notification
|
||||
if (response.results && response.results.length > 0) {
|
||||
this.showNotification(
|
||||
`Loaded ${response.results.length} recent prompts`,
|
||||
"success",
|
||||
);
|
||||
} else {
|
||||
this.showNotification("No recent prompts found", "info");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] Recent prompts error:", error);
|
||||
this.resultsSection.innerHTML = `<div style="color: #f88; text-align: center;">❌ Error loading recent prompts: ${error.message}</div>`;
|
||||
this.showNotification(
|
||||
`Failed to load recent prompts: ${error.message}`,
|
||||
"error",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to load settings from API
|
||||
nodeType.prototype.loadSettings = async function () {
|
||||
try {
|
||||
const response = await fetch("/prompt_manager/settings");
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
if (data.success && data.settings) {
|
||||
this.properties.resultTimeout = data.settings.result_timeout || 3;
|
||||
this.properties.showTestButton =
|
||||
data.settings.show_test_button || false;
|
||||
this.properties.webuiDisplayMode =
|
||||
data.settings.webui_display_mode || 'popup';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(
|
||||
"[PromptManager] Could not load settings, using defaults",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to start auto-hide timer
|
||||
nodeType.prototype.startAutoHideTimer = function () {
|
||||
// Clear existing timer
|
||||
if (this.resultHideTimer) {
|
||||
clearTimeout(this.resultHideTimer);
|
||||
}
|
||||
|
||||
// Only set timer if timeout > 0
|
||||
if (this.properties.resultTimeout > 0) {
|
||||
this.resultHideTimer = setTimeout(() => {
|
||||
this.hideResults();
|
||||
}, this.properties.resultTimeout * 1000);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to hide results
|
||||
nodeType.prototype.hideResults = function () {
|
||||
if (this.resultsSection) {
|
||||
// Completely hide the results section
|
||||
this.resultsSection.style.display = "none";
|
||||
|
||||
// Create a restore button in place of the results
|
||||
const restoreButton = document.createElement("div");
|
||||
restoreButton.style.padding = "20px";
|
||||
restoreButton.style.backgroundColor = "#333";
|
||||
restoreButton.style.border = "1px solid #444";
|
||||
restoreButton.style.borderRadius = "4px";
|
||||
restoreButton.style.textAlign = "center";
|
||||
restoreButton.style.cursor = "pointer";
|
||||
restoreButton.style.color = "#999";
|
||||
restoreButton.style.fontSize = "12px";
|
||||
restoreButton.innerHTML =
|
||||
"🔄 Results auto-hidden<br><small>Click to restore</small>";
|
||||
|
||||
// Add hover effect
|
||||
restoreButton.addEventListener("mouseenter", () => {
|
||||
restoreButton.style.backgroundColor = "#444";
|
||||
restoreButton.style.color = "#ccc";
|
||||
});
|
||||
restoreButton.addEventListener("mouseleave", () => {
|
||||
restoreButton.style.backgroundColor = "#333";
|
||||
restoreButton.style.color = "#999";
|
||||
});
|
||||
|
||||
// Click to restore
|
||||
restoreButton.addEventListener("click", () => {
|
||||
this.showResults();
|
||||
});
|
||||
|
||||
// Store reference and insert after results section
|
||||
this.restoreButton = restoreButton;
|
||||
this.resultsSection.parentNode.insertBefore(
|
||||
restoreButton,
|
||||
this.resultsSection.nextSibling,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to show results
|
||||
nodeType.prototype.showResults = function () {
|
||||
if (this.resultsSection) {
|
||||
// Show the results section
|
||||
this.resultsSection.style.display = "block";
|
||||
|
||||
// Remove restore button if it exists
|
||||
if (this.restoreButton && this.restoreButton.parentNode) {
|
||||
this.restoreButton.parentNode.removeChild(this.restoreButton);
|
||||
this.restoreButton = null;
|
||||
}
|
||||
|
||||
// Restart timer
|
||||
this.startAutoHideTimer();
|
||||
}
|
||||
};
|
||||
|
||||
// Method to display search results
|
||||
nodeType.prototype.displayResults = function (results) {
|
||||
if (!results || results.length === 0) {
|
||||
this.resultsSection.innerHTML =
|
||||
'<div style="color: #f9a825; text-align: center; padding: 20px;">📭 No prompts found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
this.properties.searchResults = results;
|
||||
this.resultsSection.innerHTML = "";
|
||||
|
||||
// Ensure results are visible
|
||||
this.resultsSection.style.opacity = "1";
|
||||
this.resultsSection.style.pointerEvents = "auto";
|
||||
|
||||
// Header
|
||||
const header = document.createElement("div");
|
||||
header.style.marginBottom = "10px";
|
||||
header.style.fontWeight = "bold";
|
||||
header.style.color = "#4facfe";
|
||||
header.style.textAlign = "center";
|
||||
header.style.padding = "8px";
|
||||
header.style.backgroundColor = "rgba(79, 172, 254, 0.1)";
|
||||
header.style.borderRadius = "4px";
|
||||
header.innerHTML = `📚 Found ${results.length} prompt${results.length === 1 ? "" : "s"}`;
|
||||
this.resultsSection.appendChild(header);
|
||||
|
||||
// Start auto-hide timer
|
||||
this.startAutoHideTimer();
|
||||
|
||||
// Results list
|
||||
results.forEach((result, index) => {
|
||||
const resultItem = document.createElement("div");
|
||||
resultItem.style.padding = "6px";
|
||||
resultItem.style.marginBottom = "4px";
|
||||
resultItem.style.backgroundColor = "#444";
|
||||
resultItem.style.borderRadius = "3px";
|
||||
resultItem.style.cursor = "pointer";
|
||||
resultItem.style.border = "1px solid transparent";
|
||||
|
||||
// Hover effect
|
||||
resultItem.addEventListener("mouseenter", () => {
|
||||
resultItem.style.backgroundColor = "#555";
|
||||
resultItem.style.border = "1px solid #666";
|
||||
});
|
||||
resultItem.addEventListener("mouseleave", () => {
|
||||
resultItem.style.backgroundColor = "#444";
|
||||
resultItem.style.border = "1px solid transparent";
|
||||
});
|
||||
|
||||
// Click to use prompt
|
||||
resultItem.addEventListener("click", () => {
|
||||
this.usePrompt(result, index);
|
||||
});
|
||||
|
||||
// Format display
|
||||
const tags = Array.isArray(result.tags)
|
||||
? result.tags.join(", ")
|
||||
: "No tags";
|
||||
const rating = result.rating ? `⭐${result.rating}` : "No rating";
|
||||
const category = result.category || "No category";
|
||||
const created = result.created_at
|
||||
? new Date(result.created_at).toLocaleDateString()
|
||||
: "Unknown";
|
||||
|
||||
const promptPreview =
|
||||
result.text.length > 80
|
||||
? result.text.substring(0, 80) + "..."
|
||||
: result.text;
|
||||
|
||||
resultItem.innerHTML = `
|
||||
<div style="color: #e8e8e8; font-weight: bold; margin-bottom: 4px; line-height: 1.3;">
|
||||
${promptPreview}
|
||||
</div>
|
||||
<div style="color: #aaa; font-size: 10px; line-height: 1.4;">
|
||||
<div style="margin-bottom: 2px;">
|
||||
<span style="color: #4facfe;">📁 ${category}</span> •
|
||||
<span style="color: #ffd700;">${rating}</span> •
|
||||
<span style="color: #8cc8ff;">📅 ${created}</span>
|
||||
</div>
|
||||
<div style="color: #9c9c9c;">
|
||||
🏷️ ${tags}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
this.resultsSection.appendChild(resultItem);
|
||||
});
|
||||
};
|
||||
|
||||
// Method to use a selected prompt
|
||||
nodeType.prototype.usePrompt = function (prompt, index) {
|
||||
// Find the text widget and set its value
|
||||
const textWidget = this.widgets?.find((w) => w.name === "text");
|
||||
if (textWidget) {
|
||||
textWidget.value = prompt.text;
|
||||
|
||||
// Also set metadata if available
|
||||
const categoryWidget = this.widgets?.find(
|
||||
(w) => w.name === "category",
|
||||
);
|
||||
if (categoryWidget && prompt.category) {
|
||||
categoryWidget.value = prompt.category;
|
||||
}
|
||||
|
||||
const tagsWidget = this.widgets?.find((w) => w.name === "tags");
|
||||
if (tagsWidget && prompt.tags) {
|
||||
tagsWidget.value = Array.isArray(prompt.tags)
|
||||
? prompt.tags.join(", ")
|
||||
: prompt.tags;
|
||||
}
|
||||
|
||||
// Visual feedback
|
||||
this.highlightSelectedResult(index);
|
||||
|
||||
// Trigger widget change events
|
||||
this.setDirtyCanvas(true, true);
|
||||
|
||||
console.log(
|
||||
`[PromptManager] Loaded prompt: ${prompt.text.substring(0, 50)}...`,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to highlight selected result
|
||||
nodeType.prototype.highlightSelectedResult = function (index) {
|
||||
const resultItems = this.resultsSection.querySelectorAll(
|
||||
"div[style*='cursor: pointer']",
|
||||
);
|
||||
resultItems.forEach((item, i) => {
|
||||
if (i === index) {
|
||||
item.style.backgroundColor = "#2a5d31";
|
||||
item.style.border = "1px solid #4a8c57";
|
||||
} else {
|
||||
item.style.backgroundColor = "#444";
|
||||
item.style.border = "1px solid transparent";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Method to call backend API endpoints
|
||||
nodeType.prototype.callNodeMethod = async function (method, params) {
|
||||
try {
|
||||
let url,
|
||||
options = { method: "GET" };
|
||||
|
||||
if (method === "search_prompts") {
|
||||
// Build query string for search (simplified - only text search)
|
||||
const queryParams = new URLSearchParams();
|
||||
if (params.search_text)
|
||||
queryParams.append("text", params.search_text);
|
||||
queryParams.append("limit", "50");
|
||||
|
||||
url = `/prompt_manager/search?${queryParams.toString()}`;
|
||||
} else if (method === "get_recent_prompts") {
|
||||
const limit = params.limit || 20;
|
||||
url = `/prompt_manager/recent?limit=${limit}`;
|
||||
} else {
|
||||
throw new Error(`Unknown method: ${method}`);
|
||||
}
|
||||
|
||||
const response = await fetch(url, options);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || "API call failed");
|
||||
}
|
||||
|
||||
return { results: data.results || [] };
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[PromptManager] API call failed for ${method}:`,
|
||||
error,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Method to save a prompt via API
|
||||
nodeType.prototype.savePromptToDatabase = async function (promptData) {
|
||||
try {
|
||||
const response = await fetch("/prompt_manager/save", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(promptData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || "Save failed");
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] Save prompt failed:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Method to delete a prompt via API
|
||||
nodeType.prototype.deletePrompt = async function (promptId) {
|
||||
try {
|
||||
const response = await fetch(`/prompt_manager/delete/${promptId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || "Delete failed");
|
||||
}
|
||||
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] Delete prompt failed:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// Method to test API connection
|
||||
nodeType.prototype.testApiConnection = async function () {
|
||||
try {
|
||||
this.resultsSection.innerHTML =
|
||||
'<div style="color: #999; text-align: center;">🔧 Testing API connection...</div>';
|
||||
|
||||
const response = await fetch("/prompt_manager/test");
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.resultsSection.innerHTML = `
|
||||
<div style="color: #4caf50; text-align: center; padding: 10px;">
|
||||
✅ API Connection Successful!<br>
|
||||
<small>${data.message}</small><br>
|
||||
<small>Time: ${data.timestamp}</small>
|
||||
</div>
|
||||
`;
|
||||
this.showNotification("API connection test passed!", "success");
|
||||
} else {
|
||||
throw new Error(data.message || "Test failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] API test failed:", error);
|
||||
this.resultsSection.innerHTML = `
|
||||
<div style="color: #f44336; text-align: center; padding: 10px;">
|
||||
❌ API Connection Failed<br>
|
||||
<small>${error.message}</small>
|
||||
</div>
|
||||
`;
|
||||
this.showNotification(`API test failed: ${error.message}`, "error");
|
||||
}
|
||||
};
|
||||
|
||||
// Method to open web interface
|
||||
nodeType.prototype.openWebInterface = function () {
|
||||
try {
|
||||
// Open the web interface served via our API endpoint
|
||||
const currentOrigin = window.location.origin;
|
||||
const webUrl = `${currentOrigin}/prompt_manager/web`;
|
||||
|
||||
if (this.properties.webuiDisplayMode === 'newtab') {
|
||||
// Open in new tab without window options
|
||||
const newWindow = window.open(webUrl, "_blank");
|
||||
|
||||
if (newWindow) {
|
||||
console.log("[PromptManager] Web interface opened in new tab");
|
||||
this.showNotification("Web interface opened in new tab", "success");
|
||||
} else {
|
||||
// Fallback if popup was blocked
|
||||
console.log("[PromptManager] Popup blocked, trying alternative method");
|
||||
window.location.href = webUrl;
|
||||
}
|
||||
} else {
|
||||
// Default popup mode with specific dimensions
|
||||
const newWindow = window.open(
|
||||
webUrl,
|
||||
"_blank",
|
||||
"width=1200,height=800,scrollbars=yes,resizable=yes",
|
||||
);
|
||||
|
||||
if (newWindow) {
|
||||
console.log("[PromptManager] Web interface opened in popup");
|
||||
this.showNotification("Web interface opened in popup", "success");
|
||||
} else {
|
||||
// Fallback if popup was blocked
|
||||
console.log("[PromptManager] Popup blocked, trying alternative method");
|
||||
window.location.href = webUrl;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[PromptManager] Error opening web interface:", error);
|
||||
this.showNotification(
|
||||
`Web interface error: ${error.message}`,
|
||||
"error",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Method to show notifications
|
||||
nodeType.prototype.showNotification = function (message, type = "info") {
|
||||
// Create a temporary notification element
|
||||
const notification = document.createElement("div");
|
||||
notification.style.position = "fixed";
|
||||
notification.style.top = "20px";
|
||||
notification.style.right = "20px";
|
||||
notification.style.padding = "10px 15px";
|
||||
notification.style.borderRadius = "4px";
|
||||
notification.style.color = "white";
|
||||
notification.style.fontSize = "12px";
|
||||
notification.style.zIndex = "10000";
|
||||
notification.style.maxWidth = "300px";
|
||||
notification.textContent = message;
|
||||
|
||||
// Set color based on type
|
||||
switch (type) {
|
||||
case "success":
|
||||
notification.style.backgroundColor = "#4caf50";
|
||||
break;
|
||||
case "error":
|
||||
notification.style.backgroundColor = "#f44336";
|
||||
break;
|
||||
case "warning":
|
||||
notification.style.backgroundColor = "#ff9800";
|
||||
break;
|
||||
default:
|
||||
notification.style.backgroundColor = "#2196f3";
|
||||
}
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(notification);
|
||||
}, 3000);
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user