mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
feat(r128): complete OpenClaw naming unification phase 3 with legacy telemetry, canonical UI selectors, and e2e alignment
This commit is contained in:
+24
-4
@@ -514,15 +514,35 @@ def register_dual_route(server, method: str, path: str, handler) -> None:
|
||||
f"[OpenClaw] Warning: Skipping route {method} {path} because handler is missing (None)."
|
||||
)
|
||||
return
|
||||
# Phase 3 Deprecation wrapper for legacy paths
|
||||
actual_handler = handler
|
||||
if path.startswith("/moltbot"):
|
||||
from functools import wraps
|
||||
|
||||
@wraps(handler)
|
||||
async def _deprecated_handler(request: web.Request) -> web.Response:
|
||||
try:
|
||||
# Assuming `metrics` is available in scope (from module level imports)
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except Exception:
|
||||
pass
|
||||
print(
|
||||
f"[OpenClaw] DEPRECATION WARNING: Legacy route accessed: {request.path}. Please migrate to /openclaw/* equivalents."
|
||||
)
|
||||
return await handler(request)
|
||||
|
||||
actual_handler = _deprecated_handler
|
||||
|
||||
# 1. Standard ComfyUI registration
|
||||
if method == "GET":
|
||||
server.routes.get(path)(handler)
|
||||
server.routes.get(path)(actual_handler)
|
||||
elif method == "POST":
|
||||
server.routes.post(path)(handler)
|
||||
server.routes.post(path)(actual_handler)
|
||||
elif method == "PUT":
|
||||
server.routes.put(path)(handler)
|
||||
server.routes.put(path)(actual_handler)
|
||||
elif method == "DELETE":
|
||||
server.routes.delete(path)(handler)
|
||||
server.routes.delete(path)(actual_handler)
|
||||
|
||||
# 2. Hardened direct registration
|
||||
if hasattr(server, "app") and hasattr(server.app, "router"):
|
||||
|
||||
@@ -160,13 +160,35 @@ def resolve_token_info(request) -> Optional[TokenInfo]:
|
||||
2. Check Environment Variables (Static)
|
||||
"""
|
||||
# Extract token from headers
|
||||
client_token = (
|
||||
request.headers.get("X-OpenClaw-Admin-Token")
|
||||
or request.headers.get("X-Moltbot-Admin-Token")
|
||||
or request.headers.get("X-OpenClaw-Obs-Token")
|
||||
or request.headers.get("X-Moltbot-Obs-Token")
|
||||
or ""
|
||||
)
|
||||
client_token = ""
|
||||
if request.headers.get("X-OpenClaw-Admin-Token"):
|
||||
client_token = request.headers.get("X-OpenClaw-Admin-Token")
|
||||
elif request.headers.get("X-Moltbot-Admin-Token"):
|
||||
client_token = request.headers.get("X-Moltbot-Admin-Token")
|
||||
try:
|
||||
from .metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
"DEPRECATION WARNING: Legacy header X-Moltbot-Admin-Token used. Please migrate to X-OpenClaw-Admin-Token."
|
||||
)
|
||||
elif request.headers.get("X-OpenClaw-Obs-Token"):
|
||||
client_token = request.headers.get("X-OpenClaw-Obs-Token")
|
||||
elif request.headers.get("X-Moltbot-Obs-Token"):
|
||||
client_token = request.headers.get("X-Moltbot-Obs-Token")
|
||||
try:
|
||||
from .metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
"DEPRECATION WARNING: Legacy header X-Moltbot-Obs-Token used. Please migrate to X-OpenClaw-Obs-Token."
|
||||
)
|
||||
|
||||
# 1. Registry Check
|
||||
if client_token:
|
||||
|
||||
@@ -27,6 +27,7 @@ class Metrics:
|
||||
"webhook_requests_deduped": 0,
|
||||
"webhook_requests_executed": 0,
|
||||
"webhook_requests_validated": 0,
|
||||
"legacy_api_hits": 0,
|
||||
# R33: Budget denial metrics
|
||||
"budget_denied_total": 0,
|
||||
"budget_denied_global_concurrency": 0,
|
||||
@@ -68,6 +69,7 @@ class Metrics:
|
||||
"errors_captured": counters.get("errors", 0),
|
||||
# Log processing is not currently tracked; keep as 0 for now.
|
||||
"logs_processed": 0,
|
||||
"legacy_api_hits": counters.get("legacy_api_hits", 0),
|
||||
}
|
||||
|
||||
def reset(self) -> None:
|
||||
|
||||
@@ -153,12 +153,33 @@ def validate_device_token(
|
||||
return False, "Bridge not enabled", None
|
||||
|
||||
# Extract headers
|
||||
device_id = request.headers.get(HEADER_DEVICE_ID, "") or request.headers.get(
|
||||
LEGACY_HEADER_DEVICE_ID, ""
|
||||
)
|
||||
device_token = request.headers.get(HEADER_DEVICE_TOKEN, "") or request.headers.get(
|
||||
LEGACY_HEADER_DEVICE_TOKEN, ""
|
||||
)
|
||||
device_id = request.headers.get(HEADER_DEVICE_ID)
|
||||
if not device_id and request.headers.get(LEGACY_HEADER_DEVICE_ID):
|
||||
device_id = request.headers.get(LEGACY_HEADER_DEVICE_ID)
|
||||
try:
|
||||
from ..metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
f"DEPRECATION WARNING: Legacy header {LEGACY_HEADER_DEVICE_ID} used. Please migrate to {HEADER_DEVICE_ID}."
|
||||
)
|
||||
|
||||
device_token = request.headers.get(HEADER_DEVICE_TOKEN)
|
||||
if not device_token and request.headers.get(LEGACY_HEADER_DEVICE_TOKEN):
|
||||
device_token = request.headers.get(LEGACY_HEADER_DEVICE_TOKEN)
|
||||
try:
|
||||
from ..metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
f"DEPRECATION WARNING: Legacy header {LEGACY_HEADER_DEVICE_TOKEN} used. Please migrate to {HEADER_DEVICE_TOKEN}."
|
||||
)
|
||||
|
||||
if not device_id:
|
||||
return False, "Missing device ID", None
|
||||
|
||||
+14
-1
@@ -75,7 +75,20 @@ def get_effective_trace_id(headers: dict, body_data: dict) -> str:
|
||||
3. Body: traceId (camelCase, for JS clients)
|
||||
4. Generated
|
||||
"""
|
||||
header_trace = headers.get(TRACE_HEADER) or headers.get(LEGACY_TRACE_HEADER)
|
||||
try:
|
||||
from .metrics import metrics
|
||||
except ImportError:
|
||||
metrics = None
|
||||
|
||||
header_trace = headers.get(TRACE_HEADER)
|
||||
if not header_trace and headers.get(LEGACY_TRACE_HEADER):
|
||||
header_trace = headers.get(LEGACY_TRACE_HEADER)
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
logger.warning(
|
||||
f"DEPRECATION WARNING: Legacy header used: {LEGACY_TRACE_HEADER}. Please migrate to {TRACE_HEADER}."
|
||||
)
|
||||
|
||||
body_trace = body_data.get("trace_id") or body_data.get("traceId")
|
||||
# Priority: Header > Body > Generated
|
||||
return get_or_create_trace_id(header_trace or body_trace)
|
||||
|
||||
@@ -145,9 +145,19 @@ def verify_hmac(request: RequestLike, raw_body: bytes) -> Tuple[bool, str]:
|
||||
if not secret:
|
||||
return False, "hmac_not_configured"
|
||||
|
||||
sig_header = request.headers.get("X-OpenClaw-Signature", "") or request.headers.get(
|
||||
"X-Moltbot-Signature", ""
|
||||
)
|
||||
sig_header = request.headers.get("X-OpenClaw-Signature", "")
|
||||
if not sig_header and request.headers.get("X-Moltbot-Signature"):
|
||||
sig_header = request.headers.get("X-Moltbot-Signature", "")
|
||||
try:
|
||||
from .metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
"DEPRECATION WARNING: Legacy header X-Moltbot-Signature is used. Please migrate to X-OpenClaw-Signature."
|
||||
)
|
||||
|
||||
if not sig_header:
|
||||
return False, "missing_signature_header"
|
||||
@@ -168,12 +178,33 @@ def verify_hmac(request: RequestLike, raw_body: bytes) -> Tuple[bool, str]:
|
||||
return False, "invalid_signature"
|
||||
|
||||
# Replay Protection (S2.1)
|
||||
timestamp = request.headers.get("X-OpenClaw-Timestamp") or request.headers.get(
|
||||
"X-Moltbot-Timestamp"
|
||||
)
|
||||
nonce = request.headers.get("X-OpenClaw-Nonce") or request.headers.get(
|
||||
"X-Moltbot-Nonce"
|
||||
)
|
||||
timestamp = request.headers.get("X-OpenClaw-Timestamp")
|
||||
if not timestamp and request.headers.get("X-Moltbot-Timestamp"):
|
||||
timestamp = request.headers.get("X-Moltbot-Timestamp")
|
||||
try:
|
||||
from .metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
"DEPRECATION WARNING: Legacy header X-Moltbot-Timestamp is used. Please migrate to X-OpenClaw-Timestamp."
|
||||
)
|
||||
|
||||
nonce = request.headers.get("X-OpenClaw-Nonce")
|
||||
if not nonce and request.headers.get("X-Moltbot-Nonce"):
|
||||
nonce = request.headers.get("X-Moltbot-Nonce")
|
||||
try:
|
||||
from .metrics import metrics
|
||||
|
||||
if metrics:
|
||||
metrics.inc("legacy_api_hits")
|
||||
except ImportError:
|
||||
pass
|
||||
logger.warning(
|
||||
"DEPRECATION WARNING: Legacy header X-Moltbot-Nonce is used. Please migrate to X-OpenClaw-Nonce."
|
||||
)
|
||||
|
||||
# Enforced if headers present OR if strictly required configuration
|
||||
should_enforce = timestamp or nonce or should_require_replay_protection()
|
||||
|
||||
@@ -43,7 +43,7 @@ test.describe('Parameter Lab - Dynamic Dimensions', () => {
|
||||
test('can select node, widget, and add values via dropdown', async ({ page }) => {
|
||||
// Add Dimension
|
||||
await page.click('#lab-add-dim');
|
||||
await expect(page.locator('.moltbot-lab-dim-row.dynamic')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-lab-dim-row.dynamic')).toBeVisible();
|
||||
|
||||
// Select Node (KSampler id=10)
|
||||
await page.selectOption('.dim-node-select', { value: '10' });
|
||||
@@ -59,15 +59,15 @@ test.describe('Parameter Lab - Dynamic Dimensions', () => {
|
||||
await page.selectOption('.dim-candidate-select', { value: 'ddim' });
|
||||
|
||||
// Verify chip added
|
||||
await expect(page.locator('.moltbot-chip >> text=ddim')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-chip >> text=ddim')).toBeVisible();
|
||||
|
||||
// Select another "uni_pc"
|
||||
await page.selectOption('.dim-candidate-select', { value: 'uni_pc' });
|
||||
await expect(page.locator('.moltbot-chip >> text=uni_pc')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-chip >> text=uni_pc')).toBeVisible();
|
||||
|
||||
// Verify remove chip
|
||||
await page.click('.moltbot-chip:has-text("ddim") .chip-rm');
|
||||
await expect(page.locator('.moltbot-chip >> text=ddim')).not.toBeVisible();
|
||||
await page.click('.openclaw-chip:has-text("ddim") .chip-rm');
|
||||
await expect(page.locator('.openclaw-chip >> text=ddim')).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('can add custom manual values', async ({ page }) => {
|
||||
@@ -84,7 +84,7 @@ test.describe('Parameter Lab - Dynamic Dimensions', () => {
|
||||
await page.press('.dim-manual-input', 'Enter');
|
||||
|
||||
// Verify chip
|
||||
await expect(page.locator('.moltbot-chip >> text=9999')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-chip >> text=9999')).toBeVisible();
|
||||
});
|
||||
|
||||
test('generates correct plan payload', async ({ page }) => {
|
||||
|
||||
@@ -89,12 +89,12 @@ test.describe('R107 Live Backend Parity', () => {
|
||||
await page.getByText('Add').click();
|
||||
|
||||
// Assert Job Row Appears
|
||||
const jobRow = page.locator('.moltbot-job-row').first();
|
||||
const jobRow = page.locator('.openclaw-job-row').first();
|
||||
await expect(jobRow).toBeVisible();
|
||||
await expect(jobRow).toContainText(jobId.substring(0, 16));
|
||||
|
||||
// Wait for status to become completed (polling)
|
||||
await expect(page.locator('.moltbot-kv-val.ok')).toHaveText('completed', { timeout: 10000 });
|
||||
await expect(page.locator('.openclaw-kv-val.ok')).toHaveText('completed', { timeout: 10000 });
|
||||
|
||||
// Assert Image Output
|
||||
await expect(page.locator('img[src*="test_img.png"]')).toBeVisible();
|
||||
|
||||
@@ -93,7 +93,7 @@ test.describe('Settings Tab Stability', () => {
|
||||
await savePromise;
|
||||
|
||||
// Expect success message
|
||||
await expect(page.locator('.moltbot-status.ok')).toContainText('Saved!');
|
||||
await expect(page.locator('.moltbot-status.ok')).toContainText('Applied immediately');
|
||||
await expect(page.locator('.openclaw-status.ok')).toContainText('Saved!');
|
||||
await expect(page.locator('.openclaw-status.ok')).toContainText('Applied immediately');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,15 +9,15 @@ test.describe('OpenClaw Sidebar', () => {
|
||||
});
|
||||
|
||||
test('renders header + tabs', async ({ page }) => {
|
||||
await expect(page.locator('.moltbot-title')).toHaveText('OpenClaw');
|
||||
await expect(page.locator('.moltbot-repo-link')).toContainText('View on GitHub');
|
||||
await expect(page.locator('.openclaw-title')).toHaveText('OpenClaw');
|
||||
await expect(page.locator('.openclaw-repo-link')).toContainText('View on GitHub');
|
||||
});
|
||||
|
||||
test('switching tabs does not lose content', async ({ page }) => {
|
||||
// Click a few tabs and verify active pane is non-empty
|
||||
for (const t of ['Settings', 'Jobs', 'Planner', 'Variants', 'Refiner', 'Library', 'Approvals']) {
|
||||
await clickTab(page, t);
|
||||
const active = page.locator('.moltbot-tab-pane.active');
|
||||
const active = page.locator('.openclaw-tab-pane.active');
|
||||
await expect(active).toBeVisible();
|
||||
await expect(active).not.toBeEmpty();
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
window.__moltbotTestReady = true;
|
||||
document.getElementById('test-status').textContent = '✓ Ready for Testing';
|
||||
document.getElementById('test-status').className = 'ready';
|
||||
window.dispatchEvent(new CustomEvent('openclaw-ready'));
|
||||
window.dispatchEvent(new CustomEvent('moltbot-ready'));
|
||||
} catch (e) {
|
||||
console.error('[Test Harness] Failed to load OpenClaw UI:', e);
|
||||
|
||||
@@ -54,11 +54,11 @@ export async function waitForMoltbotReady(page) {
|
||||
}
|
||||
|
||||
// Basic sanity: header + tab bar exists
|
||||
await expect(page.locator('.moltbot-header')).toBeVisible();
|
||||
await expect(page.locator('.moltbot-tabs')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-header')).toBeVisible();
|
||||
await expect(page.locator('.openclaw-tabs')).toBeVisible();
|
||||
}
|
||||
|
||||
export async function clickTab(page, title) {
|
||||
const tab = page.locator('.moltbot-tab', { hasText: title });
|
||||
const tab = page.locator('.openclaw-tab', { hasText: title });
|
||||
await tab.click();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export class ErrorBoundary {
|
||||
}
|
||||
|
||||
const box = document.createElement("div");
|
||||
box.className = "moltbot-error-boundary";
|
||||
box.className = "openclaw-error-boundary moltbot-error-boundary";
|
||||
|
||||
const h3 = document.createElement("h3");
|
||||
h3.textContent = `Error in ${this.componentName}`;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* R6: Error Boundary Styles */
|
||||
|
||||
.openclaw-error-boundary,
|
||||
.moltbot-error-boundary {
|
||||
padding: 1rem;
|
||||
border: 1px solid #ff4444;
|
||||
@@ -10,11 +11,13 @@
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.openclaw-error-boundary h3,
|
||||
.moltbot-error-boundary h3 {
|
||||
margin-top: 0;
|
||||
color: #ff8888;
|
||||
}
|
||||
|
||||
.openclaw-error-boundary code,
|
||||
.moltbot-error-boundary code {
|
||||
display: block;
|
||||
background: #000;
|
||||
|
||||
@@ -9,7 +9,7 @@ export function installGlobalErrorHandlers() {
|
||||
if (installed) return;
|
||||
|
||||
window.addEventListener("error", (event) => {
|
||||
// Filter for moltbot-related errors if possible, or just log generic
|
||||
// Filter for openclaw-related moltbot-related errors if possible, or just log generic
|
||||
// For MVP, we simply log to console with a specific prefix for easier debugging
|
||||
console.error("[OpenClaw Global Error]", event.error);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,935 @@
|
||||
/* Moltbot Design System */
|
||||
|
||||
:root {
|
||||
/* Spacing Scale */
|
||||
--openclaw-space-xs: 4px;
|
||||
--openclaw-space-sm: 8px;
|
||||
--openclaw-space-md: 12px;
|
||||
--openclaw-space-lg: 16px;
|
||||
--openclaw-space-xl: 24px;
|
||||
|
||||
/* Typography */
|
||||
--openclaw-font-xs: 11px;
|
||||
--openclaw-font-sm: 12px;
|
||||
--openclaw-font-md: 13px;
|
||||
--openclaw-font-lg: 14px;
|
||||
--openclaw-font-mono: 'Consolas', 'Monaco', 'Courier New', monospace;
|
||||
|
||||
/* Colors (inheriting from ComfyUI variable names where possible, fallbacks provided) */
|
||||
--openclaw-color-bg: var(--comfy-menu-bg, #222);
|
||||
--openclaw-color-bg-light: var(--comfy-input-bg, #333);
|
||||
--openclaw-color-fg: var(--fg-color, #eee);
|
||||
--openclaw-color-fg-muted: #aaa;
|
||||
--openclaw-color-border: var(--border-color, #444);
|
||||
--openclaw-color-primary: var(--primary-color, #2a2);
|
||||
--openclaw-color-danger: #d44;
|
||||
--openclaw-color-warning: #ea0;
|
||||
--openclaw-color-success: #2a2;
|
||||
}
|
||||
|
||||
/* Layout Primitives */
|
||||
|
||||
.openclaw-sidebar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-width: 560px; /* Keep sidebar readable: header tabs + Parameter Lab controls */
|
||||
min-height: 0; /* allow children to shrink for overflow handling */
|
||||
background: var(--openclaw-color-bg);
|
||||
color: var(--openclaw-color-fg);
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: var(--openclaw-font-md);
|
||||
}
|
||||
|
||||
/* CRITICAL: Keep this rule + !important; SplitterPanel inline sizing can override inner container min-width. */
|
||||
.side-bar-panel:has(.openclaw-sidebar-container) {
|
||||
min-width: 560px !important;
|
||||
}
|
||||
|
||||
.openclaw-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--openclaw-space-md);
|
||||
padding: var(--openclaw-space-md) var(--openclaw-space-lg);
|
||||
border-bottom: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.openclaw-status-dot {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #666;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.openclaw-status-dot.ok { background: #2a2; }
|
||||
.openclaw-status-dot.warn { background: #ea0; }
|
||||
.openclaw-status-dot.err { background: #c44; }
|
||||
|
||||
.openclaw-title {
|
||||
font-size: var(--openclaw-font-lg);
|
||||
font-weight: 700;
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.openclaw-badges {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: var(--openclaw-space-sm);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.openclaw-version {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
}
|
||||
|
||||
.openclaw-repo-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
border-radius: 999px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: var(--openclaw-color-fg);
|
||||
text-decoration: none;
|
||||
font-size: var(--openclaw-font-sm);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.openclaw-repo-link:hover { background: rgba(255,255,255,0.08); }
|
||||
|
||||
.openclaw-banner {
|
||||
padding: var(--openclaw-space-sm) var(--openclaw-space-lg);
|
||||
border-bottom: 1px solid var(--openclaw-color-border);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--openclaw-space-md);
|
||||
justify-content: space-between;
|
||||
}
|
||||
.openclaw-banner > span { flex: 1; }
|
||||
|
||||
.openclaw-banner-warning { background: rgba(255, 170, 0, 0.10); color: #ffd27a; }
|
||||
.openclaw-banner-info { background: rgba(0, 120, 255, 0.10); color: #a8d1ff; }
|
||||
.openclaw-banner-success { background: rgba(42, 170, 42, 0.15); color: #bfffc0; }
|
||||
.openclaw-banner-error { background: rgba(200, 50, 50, 0.15); color: #ffb4b4; }
|
||||
|
||||
.openclaw-banner-action {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border: 1px solid rgba(255,255,255,0.2);
|
||||
color: inherit;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: var(--openclaw-font-xs);
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
}
|
||||
.openclaw-banner-action:hover { background: rgba(255,255,255,0.2); }
|
||||
|
||||
.openclaw-banner-close {
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
padding: 0 4px;
|
||||
}
|
||||
.openclaw-banner-close:hover { opacity: 1; }
|
||||
|
||||
.openclaw-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 4 tabs per row */
|
||||
grid-auto-flow: row; /* left-to-right, then top-to-bottom */
|
||||
gap: 6px;
|
||||
padding: 8px var(--openclaw-space-lg);
|
||||
border-bottom: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(0,0,0,0.10);
|
||||
}
|
||||
|
||||
.openclaw-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
background: rgba(255,255,255,0.02);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
line-height: 1;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.openclaw-tab:hover {
|
||||
color: var(--openclaw-color-fg);
|
||||
border-color: rgba(255,255,255,0.08);
|
||||
background: rgba(255,255,255,0.04);
|
||||
}
|
||||
|
||||
.openclaw-tab.active {
|
||||
color: var(--openclaw-color-fg);
|
||||
border-color: rgba(42, 170, 42, 0.5);
|
||||
background: rgba(42, 170, 42, 0.15);
|
||||
}
|
||||
|
||||
.openclaw-tab-icon {
|
||||
font-size: 13px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.openclaw-tab-label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.openclaw-content {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.openclaw-tab-pane {
|
||||
display: none;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
}
|
||||
.openclaw-tab-pane.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.openclaw-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
color: var(--openclaw-color-fg);
|
||||
font-size: var(--openclaw-font-md);
|
||||
background: var(--openclaw-color-bg);
|
||||
}
|
||||
|
||||
.openclaw-scroll-area {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: var(--openclaw-space-lg); /* More breathing room */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-lg);
|
||||
}
|
||||
|
||||
.openclaw-card {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
border-radius: 4px;
|
||||
padding: var(--openclaw-space-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-section-header {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
text-transform: uppercase;
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: var(--openclaw-space-xs);
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid var(--openclaw-color-border);
|
||||
padding-bottom: var(--openclaw-space-xs);
|
||||
}
|
||||
|
||||
/* Grids & Splits */
|
||||
.openclaw-grid-2 {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--openclaw-space-md);
|
||||
}
|
||||
|
||||
.openclaw-split-v {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-md);
|
||||
}
|
||||
|
||||
.openclaw-split-h {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--openclaw-space-md);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Form Elements */
|
||||
.openclaw-input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-xs);
|
||||
}
|
||||
|
||||
.openclaw-section {
|
||||
background: rgba(0,0,0,0.18);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
border-radius: 6px;
|
||||
padding: var(--openclaw-space-lg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-md);
|
||||
}
|
||||
|
||||
.openclaw-section > h4 {
|
||||
margin: 0;
|
||||
font-size: var(--openclaw-font-lg);
|
||||
}
|
||||
|
||||
.openclaw-form-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-xs);
|
||||
}
|
||||
|
||||
.openclaw-btn-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--openclaw-space-sm);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.openclaw-note {
|
||||
padding: var(--openclaw-space-sm) var(--openclaw-space-md);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.openclaw-status {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
min-height: 16px;
|
||||
}
|
||||
.openclaw-status.ok { color: #bfffc0; }
|
||||
.openclaw-status.error { color: #ffb4b4; }
|
||||
|
||||
.openclaw-kv-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: var(--openclaw-space-md);
|
||||
}
|
||||
.openclaw-kv-key { color: var(--openclaw-color-fg-muted); }
|
||||
.openclaw-kv-val { color: var(--openclaw-color-fg); }
|
||||
.openclaw-kv-val.ok { color: #bfffc0; }
|
||||
.openclaw-kv-val.error { color: #ffb4b4; }
|
||||
|
||||
.openclaw-log-viewer {
|
||||
white-space: pre-wrap;
|
||||
font-family: var(--openclaw-font-mono);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
background: rgba(0,0,0,0.25);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
border-radius: 4px;
|
||||
padding: var(--openclaw-space-md);
|
||||
max-height: 260px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.openclaw-checkbox {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.openclaw-help-btn {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: var(--openclaw-color-fg);
|
||||
cursor: pointer;
|
||||
font-size: var(--openclaw-font-sm);
|
||||
line-height: 1;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.openclaw-help-btn:hover {
|
||||
background: rgba(255,255,255,0.08);
|
||||
}
|
||||
|
||||
.openclaw-label {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
}
|
||||
|
||||
.openclaw-input, .openclaw-select, .openclaw-textarea {
|
||||
background: var(--openclaw-color-bg-light);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
color: var(--openclaw-color-fg);
|
||||
padding: 6px 8px;
|
||||
border-radius: 2px;
|
||||
font-size: var(--openclaw-font-md);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.openclaw-input:focus, .openclaw-select:focus, .openclaw-textarea:focus {
|
||||
border-color: var(--openclaw-color-primary);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.openclaw-textarea {
|
||||
font-family: var(--openclaw-font-mono);
|
||||
resize: vertical;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
.openclaw-textarea-sm { min-height: 80px; }
|
||||
.openclaw-textarea-md { min-height: 150px; }
|
||||
.openclaw-textarea-lg { min-height: 250px; }
|
||||
.openclaw-textarea-xl { min-height: 400px; }
|
||||
|
||||
/* Buttons */
|
||||
.openclaw-btn {
|
||||
background: var(--openclaw-color-bg-light);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
color: var(--openclaw-color-fg);
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
font-size: var(--openclaw-font-md);
|
||||
border-radius: 2px;
|
||||
text-align: center;
|
||||
transition: background 0.1s;
|
||||
}
|
||||
|
||||
.openclaw-btn:hover:not(:disabled) {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.openclaw-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.openclaw-btn-primary {
|
||||
background: #2a2; /* Green-ish */
|
||||
color: #fff;
|
||||
border-color: #181;
|
||||
}
|
||||
.openclaw-btn-primary:hover:not(:disabled) { background: #3b3; }
|
||||
|
||||
.openclaw-btn-danger {
|
||||
background: #a33;
|
||||
color: #fff;
|
||||
border-color: #822;
|
||||
}
|
||||
.openclaw-btn-danger:hover:not(:disabled) { background: #c44; }
|
||||
|
||||
.openclaw-btn-sm {
|
||||
padding: 2px 6px;
|
||||
font-size: var(--openclaw-font-sm);
|
||||
}
|
||||
|
||||
.openclaw-btn-success {
|
||||
background: rgba(42, 170, 42, 0.2);
|
||||
border-color: rgba(42, 170, 42, 0.6);
|
||||
color: #dfffe0;
|
||||
}
|
||||
|
||||
/* Compatibility aliases used by existing tabs */
|
||||
.openclaw-btn.primary {
|
||||
background: rgba(42, 170, 42, 0.2);
|
||||
border-color: rgba(42, 170, 42, 0.6);
|
||||
color: #dfffe0;
|
||||
}
|
||||
.openclaw-btn.primary:hover:not(:disabled) {
|
||||
background: rgba(42, 170, 42, 0.3);
|
||||
}
|
||||
.openclaw-btn.secondary {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border-color: var(--openclaw-color-border);
|
||||
color: var(--openclaw-color-fg);
|
||||
}
|
||||
|
||||
.openclaw-btn.has-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.openclaw-btn-icon {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
color: var(--openclaw-color-fg);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: var(--openclaw-font-sm);
|
||||
line-height: 1;
|
||||
}
|
||||
.openclaw-btn-icon:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.openclaw-separator {
|
||||
width: 1px;
|
||||
height: 24px;
|
||||
background: var(--openclaw-color-border);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.openclaw-form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.openclaw-form-group > label {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
}
|
||||
|
||||
.openclaw-form-group > input,
|
||||
.openclaw-form-group > select,
|
||||
.openclaw-form-group > textarea {
|
||||
background: var(--openclaw-color-bg-light);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
color: var(--openclaw-color-fg);
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: var(--openclaw-font-md);
|
||||
}
|
||||
.openclaw-form-group > input:focus,
|
||||
.openclaw-form-group > select:focus,
|
||||
.openclaw-form-group > textarea:focus {
|
||||
outline: none;
|
||||
border-color: rgba(42, 170, 42, 0.7);
|
||||
}
|
||||
|
||||
.openclaw-hint,
|
||||
.openclaw-loading,
|
||||
.openclaw-error {
|
||||
padding: 10px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: var(--openclaw-font-sm);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
}
|
||||
|
||||
.openclaw-loading {
|
||||
color: #d2e4ff;
|
||||
border-color: rgba(90, 150, 255, 0.45);
|
||||
background: rgba(90, 150, 255, 0.12);
|
||||
}
|
||||
|
||||
.openclaw-error {
|
||||
color: #ffb4b4;
|
||||
border-color: rgba(200, 60, 60, 0.6);
|
||||
background: rgba(200, 60, 60, 0.18);
|
||||
}
|
||||
|
||||
/* Status Indicators */
|
||||
.openclaw-badge {
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: var(--openclaw-font-xs);
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.openclaw-badge.pending { background: #aa0; color: #000; }
|
||||
.openclaw-badge.approved { background: #2a2; color: #fff; }
|
||||
.openclaw-badge.rejected { background: #c00; color: #fff; }
|
||||
.openclaw-badge.expired { background: #666; color: #ccc; }
|
||||
|
||||
/* Error / Empty States */
|
||||
.openclaw-empty-state {
|
||||
padding: var(--openclaw-space-xl);
|
||||
text-align: center;
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
border: 1px dashed var(--openclaw-color-border);
|
||||
border-radius: 4px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.openclaw-error-box {
|
||||
padding: var(--openclaw-space-md);
|
||||
background: rgba(200, 50, 50, 0.1);
|
||||
border: 1px solid #a33;
|
||||
border-radius: 4px;
|
||||
color: #eaa;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-error-title {
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-error-hint {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
color: #ccc;
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* Loading Spinner (Simple) */
|
||||
.openclaw-spinner {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 2px solid rgba(255,255,255,0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #fff;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Modals */
|
||||
.openclaw-modal-overlay {
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.openclaw-modal {
|
||||
background: var(--openclaw-color-bg);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
|
||||
border-radius: 6px;
|
||||
min-width: 400px;
|
||||
max-width: 90vw;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.openclaw-modal-header {
|
||||
padding: var(--openclaw-space-md);
|
||||
border-bottom: 1px solid var(--openclaw-color-border);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: var(--openclaw-font-lg);
|
||||
}
|
||||
|
||||
.openclaw-modal-body {
|
||||
padding: var(--openclaw-space-lg);
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.openclaw-modal-footer {
|
||||
padding: var(--openclaw-space-md);
|
||||
border-top: 1px solid var(--openclaw-color-border);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--openclaw-space-md);
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Parameter Lab */
|
||||
.openclaw-lab-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-md);
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
padding: var(--openclaw-space-md);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.openclaw-lab-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--openclaw-space-md);
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 8px;
|
||||
padding: var(--openclaw-space-md);
|
||||
}
|
||||
|
||||
.openclaw-lab-title-wrap {
|
||||
flex: 1 1 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.openclaw-lab-title-wrap > h3 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
line-height: 1.1;
|
||||
word-break: normal;
|
||||
overflow-wrap: normal;
|
||||
}
|
||||
|
||||
.openclaw-lab-title-wrap > p {
|
||||
margin: 4px 0 0;
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
line-height: 1.35;
|
||||
max-width: 60ch;
|
||||
word-break: normal;
|
||||
overflow-wrap: normal;
|
||||
}
|
||||
|
||||
.openclaw-lab-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: var(--openclaw-space-sm);
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.openclaw-lab-actions .openclaw-btn.has-icon.openclaw-lab-action-btn {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-icon {
|
||||
flex: 0 0 auto;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-label {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-btn.active {
|
||||
border-color: rgba(42, 170, 42, 0.65);
|
||||
background: rgba(42, 170, 42, 0.2);
|
||||
color: #dfffe0;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-btn:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.openclaw-lab-action-btn:focus-visible {
|
||||
border-color: rgba(42, 170, 42, 0.75);
|
||||
box-shadow: 0 0 0 1px rgba(42, 170, 42, 0.35) inset;
|
||||
}
|
||||
|
||||
.openclaw-lab-actions .openclaw-separator {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.openclaw-lab-main {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: var(--openclaw-space-md);
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.openclaw-lab-card {
|
||||
border: 1px solid var(--openclaw-color-border);
|
||||
background: rgba(0, 0, 0, 0.16);
|
||||
border-radius: 8px;
|
||||
padding: var(--openclaw-space-md);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-md);
|
||||
}
|
||||
|
||||
.openclaw-lab-card-head {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: var(--openclaw-space-md);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
padding-bottom: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-lab-card-head > h4 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.openclaw-lab-meta {
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
}
|
||||
|
||||
.openclaw-lab-config,
|
||||
.openclaw-lab-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-lab-dim-row {
|
||||
display: grid;
|
||||
grid-template-columns: 84px 110px 1fr 32px;
|
||||
gap: var(--openclaw-space-sm);
|
||||
align-items: end;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-radius: 6px;
|
||||
padding: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-lab-dim-row .openclaw-form-group.wide {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.openclaw-lab-plan-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--openclaw-space-sm);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
padding: var(--openclaw-space-sm);
|
||||
}
|
||||
|
||||
.openclaw-lab-plan-header > h4 {
|
||||
margin: 0;
|
||||
font-size: var(--openclaw-font-md);
|
||||
}
|
||||
|
||||
.openclaw-lab-run-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.openclaw-lab-run-item {
|
||||
display: grid;
|
||||
grid-template-columns: 72px 1fr auto 32px;
|
||||
align-items: center;
|
||||
gap: var(--openclaw-space-sm);
|
||||
padding: 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 6px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
|
||||
.run-idx {
|
||||
font-family: var(--openclaw-font-mono);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
}
|
||||
|
||||
.run-params {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-family: var(--openclaw-font-mono);
|
||||
font-size: var(--openclaw-font-sm);
|
||||
}
|
||||
|
||||
.run-status {
|
||||
font-size: var(--openclaw-font-sm);
|
||||
border-radius: 999px;
|
||||
padding: 2px 8px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
color: var(--openclaw-color-fg-muted);
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.run-status.pending,
|
||||
.run-status.queued {
|
||||
color: #ffe38f;
|
||||
border-color: rgba(230, 180, 50, 0.55);
|
||||
background: rgba(230, 180, 50, 0.14);
|
||||
}
|
||||
|
||||
.run-status.running {
|
||||
color: #a8d1ff;
|
||||
border-color: rgba(90, 150, 255, 0.55);
|
||||
background: rgba(90, 150, 255, 0.16);
|
||||
}
|
||||
|
||||
.run-status.success,
|
||||
.run-status.completed {
|
||||
color: #bfffc0;
|
||||
border-color: rgba(42, 170, 42, 0.55);
|
||||
background: rgba(42, 170, 42, 0.16);
|
||||
}
|
||||
|
||||
.run-status.error,
|
||||
.run-status.failed {
|
||||
color: #ffb4b4;
|
||||
border-color: rgba(200, 60, 60, 0.6);
|
||||
background: rgba(200, 60, 60, 0.16);
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.openclaw-lab-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.openclaw-lab-actions {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.openclaw-lab-dim-row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.openclaw-lab-run-item {
|
||||
grid-template-columns: 1fr;
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.openclaw-lab-actions {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*====================================
|
||||
* LEGACY COMPATIBILITY
|
||||
*====================================*/
|
||||
|
||||
/* Moltbot Design System */
|
||||
|
||||
:root {
|
||||
/* Spacing Scale */
|
||||
--moltbot-space-xs: 4px;
|
||||
|
||||
+17
-10
@@ -49,12 +49,12 @@ export class TabManager {
|
||||
|
||||
this.tabs.forEach(tab => {
|
||||
const btn = document.createElement("div");
|
||||
btn.className = "moltbot-tab";
|
||||
btn.className = "openclaw-tab moltbot-tab";
|
||||
if (tab.icon) {
|
||||
const icon = document.createElement("i");
|
||||
icon.className = `moltbot-tab-icon ${tab.icon}`;
|
||||
icon.className = `openclaw-tab-icon moltbot-tab-icon ${tab.icon}`;
|
||||
const label = document.createElement("span");
|
||||
label.className = "moltbot-tab-label";
|
||||
label.className = "openclaw-tab-label moltbot-tab-label";
|
||||
label.textContent = tab.title;
|
||||
btn.appendChild(icon);
|
||||
btn.appendChild(label);
|
||||
@@ -67,10 +67,10 @@ export class TabManager {
|
||||
this.tabsEl.appendChild(btn);
|
||||
|
||||
// Create container for tab content if not exists
|
||||
if (!this.contentEl.querySelector(`#moltbot-tab-${tab.id}`)) {
|
||||
if (!this.contentEl.querySelector(`#openclaw-tab-${tab.id}`)) {
|
||||
const pane = document.createElement("div");
|
||||
pane.id = `moltbot-tab-${tab.id}`;
|
||||
pane.className = "moltbot-tab-pane";
|
||||
pane.id = `openclaw-tab-${tab.id}`;
|
||||
pane.className = "openclaw-tab-pane moltbot-tab-pane";
|
||||
this.contentEl.appendChild(pane);
|
||||
}
|
||||
});
|
||||
@@ -78,7 +78,7 @@ export class TabManager {
|
||||
|
||||
activateTab(id) {
|
||||
this.activeTabId = id;
|
||||
localStorage.setItem("moltbot-active-tab", id);
|
||||
localStorage.setItem("openclaw-active-tab", id);
|
||||
|
||||
// Update Tab Buttons
|
||||
Array.from(this.tabsEl.children).forEach((btn, idx) => {
|
||||
@@ -89,13 +89,17 @@ export class TabManager {
|
||||
|
||||
// Update Panes
|
||||
Array.from(this.contentEl.children).forEach(pane => {
|
||||
if (pane.id === `moltbot-tab-${id}`) pane.classList.add("active");
|
||||
if (pane.id === `openclaw-tab-${id}` || pane.id === `moltbot-tab-${id}`) {
|
||||
pane.classList.add("active");
|
||||
}
|
||||
else pane.classList.remove("active");
|
||||
});
|
||||
|
||||
// Lazy Render
|
||||
const tab = this.tabs.find(t => t.id === id);
|
||||
const pane = this.contentEl.querySelector(`#moltbot-tab-${id}`);
|
||||
const pane =
|
||||
this.contentEl.querySelector(`#openclaw-tab-${id}`) ||
|
||||
this.contentEl.querySelector(`#moltbot-tab-${id}`);
|
||||
const shouldRender = tab && pane && (!tab.loaded || !pane.hasChildNodes());
|
||||
if (shouldRender) {
|
||||
const boundary = new ErrorBoundary(`Tab: ${tab.title}`);
|
||||
@@ -110,7 +114,10 @@ export class TabManager {
|
||||
}
|
||||
|
||||
_restoreActiveTab() {
|
||||
const saved = localStorage.getItem("moltbot-active-tab");
|
||||
// CRITICAL: keep legacy key fallback to avoid tab-state loss across migration.
|
||||
const saved =
|
||||
localStorage.getItem("openclaw-active-tab") ||
|
||||
localStorage.getItem("moltbot-active-tab");
|
||||
const defaultTab = this.tabs.length > 0 ? this.tabs[0].id : null;
|
||||
this.activateTab(saved && this.tabs.find(t => t.id === saved) ? saved : defaultTab);
|
||||
}
|
||||
|
||||
+25
-25
@@ -27,7 +27,7 @@ export class OpenClawUI {
|
||||
}
|
||||
|
||||
_enforceSidebarMinWidth(container) {
|
||||
// IMPORTANT: Keep this value aligned with CSS .moltbot-sidebar-container/.side-bar-panel min-width.
|
||||
// IMPORTANT: Keep this value aligned with CSS .openclaw-sidebar-container/.moltbot-sidebar-container min-width.
|
||||
const minWidthPx = 560;
|
||||
|
||||
const applyMinWidth = () => {
|
||||
@@ -62,10 +62,10 @@ export class OpenClawUI {
|
||||
toggleFloatingPanel() {
|
||||
if (!this.floating.panel) {
|
||||
const panel = document.createElement("div");
|
||||
panel.className = "moltbot-floating-panel";
|
||||
panel.className = "openclaw-floating-panel moltbot-floating-panel";
|
||||
|
||||
const close = document.createElement("button");
|
||||
close.className = "moltbot-floating-close";
|
||||
close.className = "openclaw-floating-close moltbot-floating-close";
|
||||
close.textContent = "\u00D7";
|
||||
close.title = "Close";
|
||||
close.addEventListener("click", () => {
|
||||
@@ -73,7 +73,7 @@ export class OpenClawUI {
|
||||
});
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "moltbot-floating-content";
|
||||
content.className = "openclaw-floating-content moltbot-floating-content";
|
||||
|
||||
panel.appendChild(close);
|
||||
panel.appendChild(content);
|
||||
@@ -98,31 +98,31 @@ export class OpenClawUI {
|
||||
|
||||
_render(container) {
|
||||
container.innerHTML = "";
|
||||
container.className = "moltbot-sidebar-container";
|
||||
container.className = "openclaw-sidebar-container moltbot-sidebar-container";
|
||||
|
||||
// 1. Header
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-header";
|
||||
header.className = "openclaw-header moltbot-header";
|
||||
|
||||
const statusDot = document.createElement("div");
|
||||
statusDot.className = "moltbot-status-dot ok";
|
||||
statusDot.className = "openclaw-status-dot moltbot-status-dot ok";
|
||||
statusDot.title = "System Status";
|
||||
this.statusDot = statusDot;
|
||||
|
||||
const title = document.createElement("div");
|
||||
title.className = "moltbot-title";
|
||||
title.className = "openclaw-title moltbot-title";
|
||||
title.textContent = "OpenClaw";
|
||||
|
||||
// F9: About badges (version fetched from /openclaw/health; legacy /moltbot/health)
|
||||
const badges = document.createElement("div");
|
||||
badges.className = "moltbot-badges";
|
||||
badges.className = "openclaw-badges moltbot-badges";
|
||||
const versionSpan = document.createElement("span");
|
||||
versionSpan.className = "moltbot-version";
|
||||
versionSpan.className = "openclaw-version moltbot-version";
|
||||
versionSpan.textContent = "v...";
|
||||
const repoLink = document.createElement("a");
|
||||
repoLink.href = "https://github.com/rookiestar28/ComfyUI-OpenClaw";
|
||||
repoLink.target = "_blank";
|
||||
repoLink.className = "moltbot-repo-link";
|
||||
repoLink.className = "openclaw-repo-link moltbot-repo-link";
|
||||
repoLink.title = "View on GitHub";
|
||||
repoLink.textContent = "View on GitHub";
|
||||
badges.appendChild(versionSpan);
|
||||
@@ -139,7 +139,7 @@ export class OpenClawUI {
|
||||
// F55: Control plane mode indicator badge
|
||||
const cpMode = data?.control_plane?.mode || data?.deployment_profile || "local";
|
||||
const modeBadge = document.createElement("span");
|
||||
modeBadge.className = `moltbot-mode-badge moltbot-mode-${cpMode}`;
|
||||
modeBadge.className = `openclaw-mode-badge moltbot-mode-badge openclaw-mode-${cpMode} moltbot-mode-${cpMode}`;
|
||||
modeBadge.textContent = cpMode.toUpperCase();
|
||||
modeBadge.title = `Control plane: ${cpMode}`;
|
||||
// Style inline for immediate visibility
|
||||
@@ -183,13 +183,13 @@ export class OpenClawUI {
|
||||
|
||||
// 2. Tab Bar
|
||||
const tabBar = document.createElement("div");
|
||||
tabBar.className = "moltbot-tabs";
|
||||
tabBar.className = "openclaw-tabs moltbot-tabs";
|
||||
this.tabBar = tabBar;
|
||||
container.appendChild(tabBar);
|
||||
|
||||
// 3. Content Area
|
||||
const contentArea = document.createElement("div");
|
||||
contentArea.className = "moltbot-content";
|
||||
contentArea.className = "openclaw-content moltbot-content";
|
||||
this.contentArea = contentArea;
|
||||
container.appendChild(contentArea);
|
||||
|
||||
@@ -239,7 +239,7 @@ export class OpenClawUI {
|
||||
|
||||
// 1. Priority Check
|
||||
// If an error is currently shown, don't replace with info/warning unless it's a new error
|
||||
const currentBanner = this.container.querySelector(".moltbot-banner");
|
||||
const currentBanner = this.container.querySelector('.openclaw-banner');
|
||||
if (currentBanner) {
|
||||
const currentSeverity = currentBanner.dataset.severity;
|
||||
const isCurrentError = currentSeverity === "error";
|
||||
@@ -264,11 +264,11 @@ export class OpenClawUI {
|
||||
if (!bannerEl) {
|
||||
bannerEl = document.createElement("div");
|
||||
// Insert after header
|
||||
const header = this.container.querySelector(".moltbot-header");
|
||||
const header = this.container.querySelector('.openclaw-header');
|
||||
header.after(bannerEl);
|
||||
}
|
||||
|
||||
bannerEl.className = `moltbot-banner moltbot-banner-${severity}`;
|
||||
bannerEl.className = `openclaw-banner moltbot-banner openclaw-banner-${severity} moltbot-banner-${severity}`;
|
||||
bannerEl.dataset.id = id;
|
||||
bannerEl.dataset.severity = severity;
|
||||
bannerEl.innerHTML = ""; // Clear content
|
||||
@@ -281,7 +281,7 @@ export class OpenClawUI {
|
||||
// Action Button
|
||||
if (action) {
|
||||
const btn = document.createElement("button");
|
||||
btn.className = "moltbot-banner-action";
|
||||
btn.className = "openclaw-banner-action moltbot-banner-action";
|
||||
btn.textContent = action.label;
|
||||
btn.addEventListener("click", () => this.handleAction(action));
|
||||
bannerEl.appendChild(btn);
|
||||
@@ -290,7 +290,7 @@ export class OpenClawUI {
|
||||
// Dismiss Button
|
||||
if (dismissible) {
|
||||
const close = document.createElement("button");
|
||||
close.className = "moltbot-banner-close";
|
||||
close.className = "openclaw-banner-close moltbot-banner-close";
|
||||
close.textContent = "\u00D7";
|
||||
close.addEventListener("click", () => {
|
||||
bannerEl.remove();
|
||||
@@ -345,10 +345,10 @@ export class OpenClawUI {
|
||||
showConfirm({ title, message, fatal = false, onConfirm }) {
|
||||
// Create modal overlay
|
||||
const overlay = document.createElement("div");
|
||||
overlay.className = "moltbot-modal-overlay";
|
||||
overlay.className = "openclaw-modal-overlay moltbot-modal-overlay";
|
||||
|
||||
const modal = document.createElement("div");
|
||||
modal.className = `moltbot-modal ${fatal ? "fatal" : ""}`;
|
||||
modal.className = `openclaw-modal moltbot-modal ${fatal ? "fatal" : ""}`;
|
||||
|
||||
const h3 = document.createElement("h3");
|
||||
h3.textContent = title || "Confirm Action";
|
||||
@@ -357,15 +357,15 @@ export class OpenClawUI {
|
||||
p.textContent = message || "Are you sure?";
|
||||
|
||||
const buttons = document.createElement("div");
|
||||
buttons.className = "moltbot-modal-buttons";
|
||||
buttons.className = "openclaw-modal-buttons moltbot-modal-buttons";
|
||||
|
||||
const cancelBtn = document.createElement("button");
|
||||
cancelBtn.className = "moltbot-btn secondary";
|
||||
cancelBtn.className = "openclaw-btn moltbot-btn secondary";
|
||||
cancelBtn.textContent = "Cancel";
|
||||
cancelBtn.onclick = () => overlay.remove();
|
||||
|
||||
const confirmBtn = document.createElement("button");
|
||||
confirmBtn.className = `moltbot-btn ${fatal ? "danger" : "primary"}`;
|
||||
confirmBtn.className = `openclaw-btn moltbot-btn ${fatal ? "danger" : "primary"}`;
|
||||
confirmBtn.textContent = "Confirm";
|
||||
confirmBtn.onclick = () => {
|
||||
overlay.remove();
|
||||
@@ -574,7 +574,7 @@ export class OpenClawActions {
|
||||
*/
|
||||
_showBlockedToast(actionName, reason) {
|
||||
const toast = document.createElement("div");
|
||||
toast.className = "moltbot-blocked-toast";
|
||||
toast.className = "openclaw-blocked-toast moltbot-blocked-toast";
|
||||
toast.style.cssText = `
|
||||
position: fixed; bottom: 20px; right: 20px; z-index: 99999;
|
||||
background: #1e1e2e; border: 1px solid #f59e0b;
|
||||
|
||||
@@ -25,7 +25,7 @@ export function makeEl(tag, className = "", text = "") {
|
||||
*/
|
||||
export function showToast(message, variant = "info") {
|
||||
const toast = document.createElement("div");
|
||||
toast.className = `moltbot-toast moltbot-toast-${variant}`;
|
||||
toast.className = `openclaw-toast moltbot-toast openclaw-toast-${variant} moltbot-toast-${variant}`;
|
||||
toast.textContent = message;
|
||||
toast.style.position = "fixed";
|
||||
toast.style.right = "16px";
|
||||
@@ -45,19 +45,19 @@ export function showToast(message, variant = "info") {
|
||||
|
||||
/**
|
||||
* Display an error message within a container.
|
||||
* Looks for an existing .moltbot-error-box, or creates one at the top.
|
||||
* Looks for an existing .openclaw-error-box (legacy: .moltbot-error-box), or creates one at the top.
|
||||
*/
|
||||
export function showError(container, message) {
|
||||
let errorBox = container.querySelector(".moltbot-error-box");
|
||||
let errorBox = container.querySelector('.openclaw-error-box');
|
||||
|
||||
if (!errorBox) {
|
||||
// Try to find one by ID pattern if specific class missing? No, stick to class.
|
||||
// If not found, inject at top of panel
|
||||
const panel = container.querySelector(".moltbot-panel") || container;
|
||||
const panel = container.querySelector('.openclaw-panel') || container;
|
||||
errorBox = document.createElement("div");
|
||||
errorBox.className = "moltbot-error-box";
|
||||
errorBox.className = "openclaw-error-box moltbot-error-box";
|
||||
// Insert after panel header or at top
|
||||
const header = panel.querySelector(".moltbot-section-header");
|
||||
const header = panel.querySelector('.openclaw-section-header');
|
||||
if (header && header.nextSibling) {
|
||||
panel.insertBefore(errorBox, header.nextSibling);
|
||||
} else {
|
||||
@@ -76,7 +76,7 @@ export function showError(container, message) {
|
||||
* Clear error message in container.
|
||||
*/
|
||||
export function clearError(container) {
|
||||
const errorBox = container.querySelector(".moltbot-error-box");
|
||||
const errorBox = container.querySelector('.openclaw-error-box');
|
||||
if (errorBox) {
|
||||
errorBox.style.display = "none";
|
||||
errorBox.textContent = "";
|
||||
@@ -93,11 +93,11 @@ export async function copyToClipboard(text, btnElement) {
|
||||
// Show feedback on button
|
||||
const origText = btnElement.textContent;
|
||||
btnElement.textContent = "Copied!";
|
||||
btnElement.classList.add("moltbot-btn-success");
|
||||
btnElement.classList.add("openclaw-btn-success", "moltbot-btn-success");
|
||||
|
||||
setTimeout(() => {
|
||||
btnElement.textContent = origText;
|
||||
btnElement.classList.remove("moltbot-btn-success");
|
||||
btnElement.classList.remove("openclaw-btn-success", "moltbot-btn-success");
|
||||
}, 1500);
|
||||
|
||||
} catch (err) {
|
||||
|
||||
+28
-28
@@ -20,41 +20,41 @@ export const ApprovalsTab = {
|
||||
render(container) {
|
||||
// --- 1. Static Layout ---
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="moltbot-section-header">Approval Requests</div>
|
||||
<div class="moltbot-error-box" style="display:none"></div>
|
||||
<div class="moltbot-toolbar" style="margin-top:5px; display:flex; gap:5px; align-items:center;" id="apr-toolbar">
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-card openclaw-card moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Approval Requests</div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none"></div>
|
||||
<div class="openclaw-toolbar openclaw-toolbar moltbot-toolbar" style="margin-top:5px; display:flex; gap:5px; align-items:center;" id="apr-toolbar">
|
||||
<div id="apr-filter-btns" style="display: flex; gap: 5px;">
|
||||
<button class="moltbot-btn moltbot-btn-primary" data-status="pending">Pending</button>
|
||||
<button class="moltbot-btn" data-status="approved">Approved</button>
|
||||
<button class="moltbot-btn" data-status="rejected">Rejected</button>
|
||||
<button class="moltbot-btn" data-status="">All</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-status="pending">Pending</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-status="approved">Approved</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-status="rejected">Rejected</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-status="">All</button>
|
||||
</div>
|
||||
<button class="moltbot-btn moltbot-btn-sm" id="apr-refresh-btn" style="margin-left: auto;">
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" id="apr-refresh-btn" style="margin-left: auto;">
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="apr-list" class="moltbot-scroll-area" style="padding:0;">
|
||||
<div class="moltbot-empty-state">Loading...</div>
|
||||
<div id="apr-list" class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area" style="padding:0;">
|
||||
<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Details Modal -->
|
||||
<div id="apr-editor-overlay" class="moltbot-modal-overlay" style="display:none;">
|
||||
<div id="apr-details-modal" class="moltbot-modal" style="width: 600px;">
|
||||
<div class="moltbot-modal-header">
|
||||
<div id="apr-editor-overlay" class="openclaw-modal-overlay openclaw-modal-overlay moltbot-modal-overlay" style="display:none;">
|
||||
<div id="apr-details-modal" class="openclaw-modal openclaw-modal moltbot-modal" style="width: 600px;">
|
||||
<div class="openclaw-modal-header openclaw-modal-header moltbot-modal-header">
|
||||
<span id="apr-modal-title">Request Details</span>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-modal-body">
|
||||
<div class="openclaw-modal-body openclaw-modal-body moltbot-modal-body">
|
||||
<div style="font-family: var(--moltbot-font-mono); font-size: var(--moltbot-font-xs); background: #111; padding: 10px; border: 1px solid #333; height: 300px; overflow: auto; white-space: pre-wrap;" id="apr-modal-content"></div>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-modal-footer">
|
||||
<button class="moltbot-btn" id="apr-modal-close">Close</button>
|
||||
<div class="openclaw-modal-footer openclaw-modal-footer moltbot-modal-footer">
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" id="apr-modal-close">Close</button>
|
||||
<div id="apr-modal-actions" style="display:flex; gap:10px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -104,7 +104,7 @@ export const ApprovalsTab = {
|
||||
const isPending = req.status === "pending";
|
||||
|
||||
return `
|
||||
<div class="moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); margin-bottom: 4px; border-left: 3px solid var(--moltbot-color-border);">
|
||||
<div class="openclaw-list-item openclaw-list-item moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); margin-bottom: 4px; border-left: 3px solid var(--moltbot-color-border);">
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
||||
<div>
|
||||
<div style="font-weight: bold; font-size: var(--moltbot-font-md); color: var(--moltbot-color-fg);">
|
||||
@@ -120,15 +120,15 @@ export const ApprovalsTab = {
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: flex-end; gap: 5px;">
|
||||
<span class="moltbot-badge ${statusClass}">
|
||||
<span class="openclaw-badge openclaw-badge moltbot-badge ${statusClass}">
|
||||
${req.status.toUpperCase()}
|
||||
</span>
|
||||
|
||||
<div style="display: flex; gap: 5px; margin-top: 5px;">
|
||||
<button class="moltbot-btn moltbot-btn-sm" data-action="details" data-id="${req.approval_id}">Details</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" data-action="details" data-id="${req.approval_id}">Details</button>
|
||||
${isPending ? `
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-primary" data-action="approve" data-id="${req.approval_id}">Approve</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-danger" data-action="reject" data-id="${req.approval_id}">Reject</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-action="approve" data-id="${req.approval_id}">Approve</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger" data-action="reject" data-id="${req.approval_id}">Reject</button>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
@@ -139,7 +139,7 @@ export const ApprovalsTab = {
|
||||
|
||||
const renderList = () => {
|
||||
if (currentState.approvals.length === 0) {
|
||||
ui.list.innerHTML = '<div class="moltbot-empty-state">No requests found.</div>';
|
||||
ui.list.innerHTML = '<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">No requests found.</div>';
|
||||
return;
|
||||
}
|
||||
ui.list.innerHTML = currentState.approvals.map(renderListItem).join("");
|
||||
@@ -203,8 +203,8 @@ export const ApprovalsTab = {
|
||||
// Render actions
|
||||
if (req.status === "pending") {
|
||||
ui.modal.actions.innerHTML = `
|
||||
<button class="moltbot-btn moltbot-btn-primary" id="apr-modal-approve">Approve</button>
|
||||
<button class="moltbot-btn moltbot-btn-danger" id="apr-modal-reject">Reject</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" id="apr-modal-approve">Approve</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger" id="apr-modal-reject">Reject</button>
|
||||
`;
|
||||
|
||||
// Bind dynamic buttons
|
||||
@@ -233,8 +233,8 @@ export const ApprovalsTab = {
|
||||
const btn = e.target.closest("button[data-status]");
|
||||
if (btn && btn.parentElement === ui.filters) { // Ensure strict match
|
||||
// Update active state
|
||||
ui.filters.querySelectorAll("button").forEach(b => b.classList.remove("moltbot-btn-primary"));
|
||||
btn.classList.add("moltbot-btn-primary");
|
||||
ui.filters.querySelectorAll("button").forEach(b => b.classList.remove("openclaw-btn-primary", "openclaw-btn-primary", "moltbot-btn-primary"));
|
||||
btn.classList.add("openclaw-btn-primary", "openclaw-btn-primary", "moltbot-btn-primary");
|
||||
|
||||
// Update state
|
||||
currentState.status = btn.dataset.status; // "" for all
|
||||
|
||||
+18
-18
@@ -15,17 +15,17 @@ export const ExplorerTab = {
|
||||
container.innerHTML = "";
|
||||
|
||||
// Layout: Sidebar (Inventory/Snapshots) + Main (Preflight/Details)
|
||||
const layout = makeEl("div", "moltbot-explorer-layout");
|
||||
const layout = makeEl("div", "openclaw-explorer-layout openclaw-explorer-layout moltbot-explorer-layout");
|
||||
layout.style.display = "flex";
|
||||
layout.style.height = "100%";
|
||||
layout.style.gap = "1rem";
|
||||
|
||||
const leftPanel = makeEl("div", "moltbot-explorer-sidebar");
|
||||
const leftPanel = makeEl("div", "openclaw-explorer-sidebar openclaw-explorer-sidebar moltbot-explorer-sidebar");
|
||||
leftPanel.style.flex = "1";
|
||||
leftPanel.style.display = "flex";
|
||||
leftPanel.style.flexDirection = "column";
|
||||
|
||||
const rightPanel = makeEl("div", "moltbot-explorer-main");
|
||||
const rightPanel = makeEl("div", "openclaw-explorer-main openclaw-explorer-main moltbot-explorer-main");
|
||||
rightPanel.style.flex = "2";
|
||||
rightPanel.style.display = "flex";
|
||||
rightPanel.style.flexDirection = "column";
|
||||
@@ -35,24 +35,24 @@ export const ExplorerTab = {
|
||||
container.appendChild(layout);
|
||||
|
||||
// --- Left Panel Tabs ---
|
||||
const leftTabs = makeEl("div", "moltbot-subtabs");
|
||||
const leftTabs = makeEl("div", "openclaw-subtabs openclaw-subtabs moltbot-subtabs");
|
||||
leftTabs.style.display = "flex";
|
||||
leftTabs.style.gap = "10px";
|
||||
leftTabs.style.marginBottom = "10px";
|
||||
|
||||
const tabInv = makeEl("button", "moltbot-btn active", "Inventory");
|
||||
const tabSnaps = makeEl("button", "moltbot-btn", "Snapshots");
|
||||
const tabInv = makeEl("button", "openclaw-btn openclaw-btn moltbot-btn active", "Inventory");
|
||||
const tabSnaps = makeEl("button", "openclaw-btn openclaw-btn moltbot-btn", "Snapshots");
|
||||
leftTabs.appendChild(tabInv);
|
||||
leftTabs.appendChild(tabSnaps);
|
||||
leftPanel.appendChild(leftTabs);
|
||||
|
||||
// Content Areas
|
||||
const invContent = makeEl("div", "moltbot-tab-content active");
|
||||
const invContent = makeEl("div", "openclaw-tab-content openclaw-tab-content moltbot-tab-content active");
|
||||
invContent.style.flex = "1";
|
||||
invContent.style.display = "flex";
|
||||
invContent.style.flexDirection = "column";
|
||||
|
||||
const snapsContent = makeEl("div", "moltbot-tab-content");
|
||||
const snapsContent = makeEl("div", "openclaw-tab-content openclaw-tab-content moltbot-tab-content");
|
||||
snapsContent.style.flex = "1";
|
||||
snapsContent.style.display = "none";
|
||||
snapsContent.style.flexDirection = "column";
|
||||
@@ -76,11 +76,11 @@ export const ExplorerTab = {
|
||||
};
|
||||
|
||||
// --- Inventory Content ---
|
||||
const searchInput = makeEl("input", "moltbot-input");
|
||||
const searchInput = makeEl("input", "openclaw-input openclaw-input moltbot-input");
|
||||
searchInput.placeholder = "Search nodes or models...";
|
||||
searchInput.style.marginBottom = "10px";
|
||||
|
||||
const invList = makeEl("div", "moltbot-inventory-list");
|
||||
const invList = makeEl("div", "openclaw-inventory-list openclaw-inventory-list moltbot-inventory-list");
|
||||
invList.style.flex = "1";
|
||||
invList.style.overflowY = "auto";
|
||||
invList.style.border = "1px solid var(--border-color, #444)";
|
||||
@@ -90,7 +90,7 @@ export const ExplorerTab = {
|
||||
invContent.appendChild(invList);
|
||||
|
||||
// --- Snapshots Content ---
|
||||
const snapList = makeEl("div", "moltbot-snapshot-list");
|
||||
const snapList = makeEl("div", "openclaw-snapshot-list openclaw-snapshot-list moltbot-snapshot-list");
|
||||
snapList.style.flex = "1";
|
||||
snapList.style.overflowY = "auto";
|
||||
snapList.style.border = "1px solid var(--border-color, #444)";
|
||||
@@ -106,7 +106,7 @@ export const ExplorerTab = {
|
||||
diagDesc.style.fontSize = "0.9em";
|
||||
diagDesc.style.opacity = "0.8";
|
||||
|
||||
const jsonInput = makeEl("textarea", "moltbot-input");
|
||||
const jsonInput = makeEl("textarea", "openclaw-input openclaw-input moltbot-input");
|
||||
jsonInput.placeholder = 'Paste workflow JSON here... {"3": {"class_type": ...}}';
|
||||
jsonInput.style.flex = "1";
|
||||
jsonInput.style.fontFamily = "monospace";
|
||||
@@ -117,13 +117,13 @@ export const ExplorerTab = {
|
||||
actionsRow.style.display = "flex";
|
||||
actionsRow.style.gap = "10px";
|
||||
|
||||
const runBtn = makeEl("button", "moltbot-btn primary", "Run Preflight");
|
||||
const clearBtn = makeEl("button", "moltbot-btn", "Clear");
|
||||
const runBtn = makeEl("button", "openclaw-btn openclaw-btn moltbot-btn primary", "Run Preflight");
|
||||
const clearBtn = makeEl("button", "openclaw-btn openclaw-btn moltbot-btn", "Clear");
|
||||
|
||||
actionsRow.appendChild(runBtn);
|
||||
actionsRow.appendChild(clearBtn);
|
||||
|
||||
const resultsArea = makeEl("div", "moltbot-preflight-results");
|
||||
const resultsArea = makeEl("div", "openclaw-preflight-results openclaw-preflight-results moltbot-preflight-results");
|
||||
resultsArea.style.marginTop = "10px";
|
||||
resultsArea.style.padding = "10px";
|
||||
resultsArea.style.border = "1px solid var(--border-color, #444)";
|
||||
@@ -187,7 +187,7 @@ export const ExplorerTab = {
|
||||
invList.appendChild(h);
|
||||
|
||||
filteredNodes.slice(0, MAX_ITEMS_PER_CAT).forEach(n => {
|
||||
const row = makeEl("div", "moltbot-inv-item", n);
|
||||
const row = makeEl("div", "openclaw-inv-item openclaw-inv-item moltbot-inv-item", n);
|
||||
row.style.fontSize = "0.9em";
|
||||
row.style.padding = "2px 0";
|
||||
invList.appendChild(row);
|
||||
@@ -212,7 +212,7 @@ export const ExplorerTab = {
|
||||
invList.appendChild(h);
|
||||
|
||||
filteredVars.slice(0, MAX_ITEMS_PER_CAT).forEach(m => {
|
||||
const row = makeEl("div", "moltbot-inv-item", m);
|
||||
const row = makeEl("div", "openclaw-inv-item openclaw-inv-item moltbot-inv-item", m);
|
||||
row.style.fontSize = "0.9em";
|
||||
row.style.padding = "2px 0";
|
||||
row.title = m; // tooltip
|
||||
@@ -281,7 +281,7 @@ export const ExplorerTab = {
|
||||
summaryEl.style.fontWeight = "bold";
|
||||
summaryEl.textContent = report.ok ? "✅ Workflow Compatible" : "❌ Issues Detected";
|
||||
|
||||
const saveBtn = makeEl("button", "moltbot-btn", "Save Snapshot");
|
||||
const saveBtn = makeEl("button", "openclaw-btn openclaw-btn moltbot-btn", "Save Snapshot");
|
||||
saveBtn.onclick = async () => {
|
||||
const name = prompt("Snapshot Name:", "New Snapshot");
|
||||
if (name) {
|
||||
|
||||
@@ -6,15 +6,20 @@ import { openclawApi } from "../openclaw_api.js";
|
||||
|
||||
const POLL_INTERVAL_MS = 2000;
|
||||
const POLL_MAX_ATTEMPTS = 150;
|
||||
const STORAGE_KEY = "moltbot-job-monitor-jobs";
|
||||
const STORAGE_KEY = "openclaw-job-monitor-jobs";
|
||||
const LEGACY_STORAGE_KEY = "moltbot-job-monitor-jobs";
|
||||
|
||||
let currentJobs = [];
|
||||
let pollIntervals = {};
|
||||
|
||||
function loadJobs() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY);
|
||||
// Keep one-way fallback so existing users keep their tracked jobs after rename.
|
||||
const stored = localStorage.getItem(STORAGE_KEY) || localStorage.getItem(LEGACY_STORAGE_KEY);
|
||||
currentJobs = stored ? JSON.parse(stored) : [];
|
||||
if (stored && !localStorage.getItem(STORAGE_KEY)) {
|
||||
localStorage.setItem(STORAGE_KEY, stored);
|
||||
}
|
||||
} catch {
|
||||
currentJobs = [];
|
||||
}
|
||||
@@ -40,7 +45,7 @@ export const jobMonitorTab = {
|
||||
|
||||
// Header
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-section";
|
||||
header.className = "openclaw-section moltbot-section";
|
||||
header.innerHTML = `<h4>Job Monitor</h4>`;
|
||||
|
||||
// Add Manual Job
|
||||
@@ -73,7 +78,7 @@ export const jobMonitorTab = {
|
||||
|
||||
// Job List
|
||||
const listContainer = document.createElement("div");
|
||||
listContainer.id = "moltbot-job-list";
|
||||
listContainer.id = "openclaw-job-list";
|
||||
container.appendChild(listContainer);
|
||||
|
||||
renderJobList();
|
||||
@@ -88,7 +93,7 @@ export const jobMonitorTab = {
|
||||
|
||||
currentJobs.forEach((job) => {
|
||||
const row = document.createElement("div");
|
||||
row.className = "moltbot-job-row";
|
||||
row.className = "openclaw-job-row moltbot-job-row";
|
||||
row.style.borderBottom = "1px solid var(--border-color)";
|
||||
row.style.padding = "8px 0";
|
||||
|
||||
@@ -104,7 +109,7 @@ export const jobMonitorTab = {
|
||||
idSpan.title = job.promptId;
|
||||
|
||||
const statusBadge = document.createElement("span");
|
||||
statusBadge.className = `moltbot-kv-val ${job.status === "completed" ? "ok" : job.status === "error" ? "error" : ""}`;
|
||||
statusBadge.className = `openclaw-kv-val moltbot-kv-val ${job.status === "completed" ? "ok" : job.status === "error" ? "error" : ""}`;
|
||||
statusBadge.textContent = job.status;
|
||||
|
||||
const removeBtn = document.createElement("button");
|
||||
|
||||
+44
-44
@@ -21,59 +21,59 @@ export const LibraryTab = {
|
||||
render(container) {
|
||||
// --- 1. Static Layout ---
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="moltbot-section-header">Asset Library</div>
|
||||
<div class="moltbot-error-box" style="display:none"></div>
|
||||
<div class="moltbot-input-group">
|
||||
<input type="text" id="lib-search" class="moltbot-input" placeholder="Search...">
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-card openclaw-card moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Asset Library</div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none"></div>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<input type="text" id="lib-search" class="openclaw-input openclaw-input moltbot-input" placeholder="Search...">
|
||||
</div>
|
||||
<div class="moltbot-toolbar" style="margin-top:8px; display:flex; gap:5px;" id="lib-filter-btns">
|
||||
<button class="moltbot-btn moltbot-btn-primary" data-cat="all">All</button>
|
||||
<button class="moltbot-btn" data-cat="prompt">Prompts</button>
|
||||
<button class="moltbot-btn" data-cat="params">Params</button>
|
||||
<button class="moltbot-btn" data-cat="packs">Packs</button>
|
||||
<button class="moltbot-btn" id="lib-new-btn" style="margin-left: auto;">+ New</button>
|
||||
<div class="openclaw-toolbar openclaw-toolbar moltbot-toolbar" style="margin-top:8px; display:flex; gap:5px;" id="lib-filter-btns">
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-cat="all">All</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-cat="prompt">Prompts</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-cat="params">Params</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" data-cat="packs">Packs</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" id="lib-new-btn" style="margin-left: auto;">+ New</button>
|
||||
</div>
|
||||
<input type="file" id="lib-pack-upload" accept=".zip" style="display:none">
|
||||
</div>
|
||||
|
||||
<div id="lib-list" class="moltbot-scroll-area" style="padding:0;">
|
||||
<div id="lib-list" class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area" style="padding:0;">
|
||||
<!-- Items -->
|
||||
<div class="moltbot-empty-state">Loading...</div>
|
||||
<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Editor Modal (Presets) -->
|
||||
<div id="lib-editor-overlay" class="moltbot-modal-overlay" style="display:none;">
|
||||
<div id="lib-editor" class="moltbot-modal">
|
||||
<div class="moltbot-modal-header">
|
||||
<div id="lib-editor-overlay" class="openclaw-modal-overlay openclaw-modal-overlay moltbot-modal-overlay" style="display:none;">
|
||||
<div id="lib-editor" class="openclaw-modal openclaw-modal moltbot-modal">
|
||||
<div class="openclaw-modal-header openclaw-modal-header moltbot-modal-header">
|
||||
<span id="lib-editor-title">Edit Preset</span>
|
||||
<input type="hidden" id="lib-edit-id">
|
||||
</div>
|
||||
<div class="moltbot-modal-body">
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Name</label>
|
||||
<input type="text" id="lib-edit-name" class="moltbot-input">
|
||||
<div class="openclaw-modal-body openclaw-modal-body moltbot-modal-body">
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Name</label>
|
||||
<input type="text" id="lib-edit-name" class="openclaw-input openclaw-input moltbot-input">
|
||||
</div>
|
||||
<br>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Category</label>
|
||||
<select id="lib-edit-cat" class="moltbot-select">
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Category</label>
|
||||
<select id="lib-edit-cat" class="openclaw-select openclaw-select moltbot-select">
|
||||
<option value="general">General</option>
|
||||
<option value="prompt">Prompt</option>
|
||||
<option value="params">Params</option>
|
||||
</select>
|
||||
</div>
|
||||
<br>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Content (JSON)</label>
|
||||
<textarea id="lib-edit-params-json" class="moltbot-textarea moltbot-textarea-md"></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Content (JSON)</label>
|
||||
<textarea id="lib-edit-params-json" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-md openclaw-textarea-md moltbot-textarea-md"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="moltbot-modal-footer">
|
||||
<button class="moltbot-btn" id="lib-editor-cancel">Cancel</button>
|
||||
<button class="moltbot-btn moltbot-btn-primary" id="lib-editor-save">Save</button>
|
||||
<div class="openclaw-modal-footer openclaw-modal-footer moltbot-modal-footer">
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn" id="lib-editor-cancel">Cancel</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" id="lib-editor-save">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -107,33 +107,33 @@ export const LibraryTab = {
|
||||
// --- 3. View Logic (Renderers) ---
|
||||
|
||||
const renderPresetItem = (p) => `
|
||||
<div class="moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); display: flex; justify-content: space-between; align-items: center;">
|
||||
<div class="openclaw-list-item openclaw-list-item moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<div style="font-weight: bold;">${escapeHtml(p.name)}</div>
|
||||
<div style="font-size: var(--moltbot-font-sm); color: var(--moltbot-color-fg-muted); margin-top:4px;">
|
||||
<span class="moltbot-badge" style="background:#555; color:#eee;">${escapeHtml(p.category)}</span>
|
||||
<span class="openclaw-badge openclaw-badge moltbot-badge" style="background:#555; color:#eee;">${escapeHtml(p.category)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
${getApplyButton(p)}
|
||||
<button class="moltbot-btn moltbot-btn-sm" data-action="edit" data-id="${p.id}">Edit</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-danger" data-action="delete" data-id="${p.id}">Del</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" data-action="edit" data-id="${p.id}">Edit</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger" data-action="delete" data-id="${p.id}">Del</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const renderPackItem = (p) => `
|
||||
<div class="moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); display: flex; justify-content: space-between; align-items: center;">
|
||||
<div class="openclaw-list-item openclaw-list-item moltbot-list-item" style="padding: 10px; border-bottom: 1px solid var(--moltbot-color-border); display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<div style="font-weight: bold;">${escapeHtml(p.name)} <span style="font-weight:normal; opacity:0.7">v${escapeHtml(p.version)}</span></div>
|
||||
<div style="font-size: var(--moltbot-font-sm); color: var(--moltbot-color-fg-muted); margin-top:4px;">
|
||||
<span class="moltbot-badge" style="background:#2c4f7c; color:#eee;">${escapeHtml(p.type)}</span>
|
||||
<span class="openclaw-badge openclaw-badge moltbot-badge" style="background:#2c4f7c; color:#eee;">${escapeHtml(p.type)}</span>
|
||||
<span style="margin-left:6px;">by ${escapeHtml(p.author)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
<button class="moltbot-btn moltbot-btn-sm" data-action="export-pack" data-name="${p.name}" data-ver="${p.version}">Export</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-danger" data-action="delete-pack" data-name="${p.name}" data-ver="${p.version}">Uninst</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" data-action="export-pack" data-name="${p.name}" data-ver="${p.version}">Export</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger" data-action="delete-pack" data-name="${p.name}" data-ver="${p.version}">Uninst</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -141,11 +141,11 @@ export const LibraryTab = {
|
||||
function getApplyButton(p) {
|
||||
if (p.category === "prompt") {
|
||||
return `
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-primary" data-action="apply" data-id="${p.id}">Plan</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-primary" data-action="apply-refiner" data-id="${p.id}">Refine</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-action="apply" data-id="${p.id}">Plan</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-action="apply-refiner" data-id="${p.id}">Refine</button>
|
||||
`;
|
||||
} else if (p.category === "params") {
|
||||
return `<button class="moltbot-btn moltbot-btn-sm moltbot-btn-primary" data-action="apply" data-id="${p.id}">Use</button>`;
|
||||
return `<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" data-action="apply" data-id="${p.id}">Use</button>`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -157,7 +157,7 @@ export const LibraryTab = {
|
||||
);
|
||||
|
||||
if (filtered.length === 0) {
|
||||
ui.list.innerHTML = '<div class="moltbot-empty-state">No items found.</div>';
|
||||
ui.list.innerHTML = '<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">No items found.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -289,8 +289,8 @@ export const LibraryTab = {
|
||||
const btn = e.target.closest("button[data-cat]");
|
||||
if (!btn) return;
|
||||
|
||||
ui.filters.querySelectorAll("button").forEach(b => b.classList.remove("moltbot-btn-primary"));
|
||||
btn.classList.add("moltbot-btn-primary");
|
||||
ui.filters.querySelectorAll("button").forEach(b => b.classList.remove("openclaw-btn-primary", "openclaw-btn-primary", "moltbot-btn-primary"));
|
||||
btn.classList.add("openclaw-btn-primary", "openclaw-btn-primary", "moltbot-btn-primary");
|
||||
|
||||
const cat = btn.dataset.cat;
|
||||
currentState.category = cat === "all" ? null : cat;
|
||||
|
||||
+13
-13
@@ -20,21 +20,21 @@ export const PacksTab = {
|
||||
render(container) {
|
||||
// --- 1. Static Layout ---
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="moltbot-section-header">Asset Packs</div>
|
||||
<div class="moltbot-error-box" style="display:none"></div>
|
||||
<div class="moltbot-toolbar" style="margin-top:5px; display:flex; gap:5px; align-items:center;" id="pack-toolbar">
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-card openclaw-card moltbot-card" style="border-radius:0; border:none; border-bottom:1px solid var(--moltbot-color-border);">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Asset Packs</div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none"></div>
|
||||
<div class="openclaw-toolbar openclaw-toolbar moltbot-toolbar" style="margin-top:5px; display:flex; gap:5px; align-items:center;" id="pack-toolbar">
|
||||
<input type="file" id="pack-import-input" accept=".zip" style="display:none">
|
||||
<button class="moltbot-btn moltbot-btn-primary" id="pack-import-btn">Import Pack</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm" id="pack-refresh-btn" style="margin-left: auto;">
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary" id="pack-import-btn">Import Pack</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" id="pack-refresh-btn" style="margin-left: auto;">
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="pack-list" class="moltbot-scroll-area" style="padding:10px;">
|
||||
<div class="moltbot-empty-state">Loading...</div>
|
||||
<div id="pack-list" class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area" style="padding:10px;">
|
||||
<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -51,7 +51,7 @@ export const PacksTab = {
|
||||
|
||||
const renderListItem = (pack) => {
|
||||
return `
|
||||
<div class="moltbot-card" style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: start;">
|
||||
<div class="openclaw-card openclaw-card moltbot-card" style="margin-bottom: 10px; display: flex; justify-content: space-between; align-items: start;">
|
||||
<div>
|
||||
<div style="font-weight: bold; font-size: var(--moltbot-font-md); color: var(--moltbot-color-fg);">
|
||||
${escapeHtml(pack.name)} <span style="font-weight:normal; color:var(--moltbot-color-fg-muted);">v${escapeHtml(pack.version)}</span>
|
||||
@@ -64,8 +64,8 @@ export const PacksTab = {
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 5px; flex-direction: column; align-items: flex-end;">
|
||||
<button class="moltbot-btn moltbot-btn-sm" data-action="export" data-name="${escapeHtml(pack.name)}" data-version="${escapeHtml(pack.version)}">Export</button>
|
||||
<button class="moltbot-btn moltbot-btn-sm moltbot-btn-danger" data-action="delete" data-name="${escapeHtml(pack.name)}" data-version="${escapeHtml(pack.version)}">Uninstall</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm" data-action="export" data-name="${escapeHtml(pack.name)}" data-version="${escapeHtml(pack.version)}">Export</button>
|
||||
<button class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-sm openclaw-btn-sm moltbot-btn-sm openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger" data-action="delete" data-name="${escapeHtml(pack.name)}" data-version="${escapeHtml(pack.version)}">Uninstall</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -73,7 +73,7 @@ export const PacksTab = {
|
||||
|
||||
const renderList = (packs) => {
|
||||
if (!packs || packs.length === 0) {
|
||||
ui.list.innerHTML = '<div class="moltbot-empty-state">No packs installed.</div>';
|
||||
ui.list.innerHTML = '<div class="openclaw-empty-state openclaw-empty-state moltbot-empty-state">No packs installed.</div>';
|
||||
return;
|
||||
}
|
||||
ui.list.innerHTML = packs.map(renderListItem).join("");
|
||||
|
||||
@@ -24,34 +24,34 @@ export const ParameterLabTab = {
|
||||
|
||||
render(container) {
|
||||
container.innerHTML = "";
|
||||
container.className = "moltbot-tab-content moltbot-lab-container";
|
||||
container.className = "openclaw-tab-content openclaw-tab-content moltbot-tab-content openclaw-lab-container openclaw-lab-container moltbot-lab-container";
|
||||
|
||||
// 1. Header / Toolbar
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-lab-header";
|
||||
header.className = "openclaw-lab-header openclaw-lab-header moltbot-lab-header";
|
||||
header.innerHTML = `
|
||||
<div class="moltbot-lab-title-wrap">
|
||||
<div class="openclaw-lab-title-wrap openclaw-lab-title-wrap moltbot-lab-title-wrap">
|
||||
<h3>Parameter Lab</h3>
|
||||
<p>Build bounded sweeps and compare model variants directly from canvas.</p>
|
||||
</div>
|
||||
<div class="moltbot-lab-actions">
|
||||
<button id="lab-history" class="moltbot-btn has-icon moltbot-lab-action-btn" title="View History">
|
||||
<span class="moltbot-lab-action-icon">\uD83D\uDCDC</span>
|
||||
<span class="moltbot-lab-action-label">History</span>
|
||||
<div class="openclaw-lab-actions openclaw-lab-actions moltbot-lab-actions">
|
||||
<button id="lab-history" class="openclaw-btn openclaw-btn moltbot-btn has-icon openclaw-lab-action-btn openclaw-lab-action-btn moltbot-lab-action-btn" title="View History">
|
||||
<span class="openclaw-lab-action-icon openclaw-lab-action-icon moltbot-lab-action-icon">\uD83D\uDCDC</span>
|
||||
<span class="openclaw-lab-action-label openclaw-lab-action-label moltbot-lab-action-label">History</span>
|
||||
</button>
|
||||
<div class="moltbot-separator"></div>
|
||||
<button id="lab-compare-models" class="moltbot-btn has-icon moltbot-lab-action-btn" title="Wizard: Compare Models">
|
||||
<span class="moltbot-lab-action-icon">\u2696\uFE0F</span>
|
||||
<span class="moltbot-lab-action-label">Compare Models</span>
|
||||
<div class="openclaw-separator openclaw-separator moltbot-separator"></div>
|
||||
<button id="lab-compare-models" class="openclaw-btn openclaw-btn moltbot-btn has-icon openclaw-lab-action-btn openclaw-lab-action-btn moltbot-lab-action-btn" title="Wizard: Compare Models">
|
||||
<span class="openclaw-lab-action-icon openclaw-lab-action-icon moltbot-lab-action-icon">\u2696\uFE0F</span>
|
||||
<span class="openclaw-lab-action-label openclaw-lab-action-label moltbot-lab-action-label">Compare Models</span>
|
||||
</button>
|
||||
<div class="moltbot-separator"></div>
|
||||
<button id="lab-add-dim" class="moltbot-btn moltbot-lab-action-btn">
|
||||
<span class="moltbot-lab-action-icon">➕</span>
|
||||
<span class="moltbot-lab-action-label">+ Dimension</span>
|
||||
<div class="openclaw-separator openclaw-separator moltbot-separator"></div>
|
||||
<button id="lab-add-dim" class="openclaw-btn openclaw-btn moltbot-btn openclaw-lab-action-btn openclaw-lab-action-btn moltbot-lab-action-btn">
|
||||
<span class="openclaw-lab-action-icon openclaw-lab-action-icon moltbot-lab-action-icon">➕</span>
|
||||
<span class="openclaw-lab-action-label openclaw-lab-action-label moltbot-lab-action-label">+ Dimension</span>
|
||||
</button>
|
||||
<button id="lab-generate" class="moltbot-btn moltbot-lab-action-btn">
|
||||
<span class="moltbot-lab-action-icon">🧭</span>
|
||||
<span class="moltbot-lab-action-label">Generate Plan</span>
|
||||
<button id="lab-generate" class="openclaw-btn openclaw-btn moltbot-btn openclaw-lab-action-btn openclaw-lab-action-btn moltbot-lab-action-btn">
|
||||
<span class="openclaw-lab-action-icon openclaw-lab-action-icon moltbot-lab-action-icon">🧭</span>
|
||||
<span class="openclaw-lab-action-label openclaw-lab-action-label moltbot-lab-action-label">Generate Plan</span>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
@@ -59,20 +59,20 @@ export const ParameterLabTab = {
|
||||
this.container = container;
|
||||
|
||||
const main = document.createElement("div");
|
||||
main.className = "moltbot-lab-main";
|
||||
main.className = "openclaw-lab-main openclaw-lab-main moltbot-lab-main";
|
||||
container.appendChild(main);
|
||||
|
||||
// 2. Configuration Area (Dimensions)
|
||||
const configCard = document.createElement("section");
|
||||
configCard.className = "moltbot-lab-card";
|
||||
configCard.className = "openclaw-lab-card openclaw-lab-card moltbot-lab-card";
|
||||
configCard.innerHTML = `
|
||||
<div class="moltbot-lab-card-head">
|
||||
<div class="openclaw-lab-card-head openclaw-lab-card-head moltbot-lab-card-head">
|
||||
<h4>Dimensions</h4>
|
||||
<span class="moltbot-lab-meta" id="lab-dimension-count">0 configured</span>
|
||||
<span class="openclaw-lab-meta openclaw-lab-meta moltbot-lab-meta" id="lab-dimension-count">0 configured</span>
|
||||
</div>
|
||||
`;
|
||||
const configArea = document.createElement("div");
|
||||
configArea.className = "moltbot-lab-config";
|
||||
configArea.className = "openclaw-lab-config openclaw-lab-config moltbot-lab-config";
|
||||
configCard.appendChild(configArea);
|
||||
main.appendChild(configCard);
|
||||
this.configContainer = configArea;
|
||||
@@ -80,15 +80,15 @@ export const ParameterLabTab = {
|
||||
|
||||
// 3. Plan / Results Area
|
||||
const resultsCard = document.createElement("section");
|
||||
resultsCard.className = "moltbot-lab-card";
|
||||
resultsCard.className = "openclaw-lab-card openclaw-lab-card moltbot-lab-card";
|
||||
resultsCard.innerHTML = `
|
||||
<div class="moltbot-lab-card-head">
|
||||
<div class="openclaw-lab-card-head openclaw-lab-card-head moltbot-lab-card-head">
|
||||
<h4>Plan & Results</h4>
|
||||
<span class="moltbot-lab-meta">Live status</span>
|
||||
<span class="openclaw-lab-meta openclaw-lab-meta moltbot-lab-meta">Live status</span>
|
||||
</div>
|
||||
`;
|
||||
const resultsArea = document.createElement("div");
|
||||
resultsArea.className = "moltbot-lab-results";
|
||||
resultsArea.className = "openclaw-lab-results openclaw-lab-results moltbot-lab-results";
|
||||
resultsCard.appendChild(resultsArea);
|
||||
main.appendChild(resultsCard);
|
||||
this.resultsContainer = resultsArea;
|
||||
@@ -131,22 +131,22 @@ export const ParameterLabTab = {
|
||||
},
|
||||
|
||||
async showHistory() {
|
||||
this.resultsContainer.innerHTML = "<div class='moltbot-loading'>Loading history...</div>";
|
||||
this.resultsContainer.innerHTML = "<div class='openclaw-loading openclaw-loading moltbot-loading'>Loading history...</div>";
|
||||
try {
|
||||
const res = await openclawApi.fetch(openclawApi._path("/lab/experiments"));
|
||||
if (res.ok && res.data) {
|
||||
this.renderHistoryList(res.data.experiments);
|
||||
} else {
|
||||
this.resultsContainer.innerHTML = "<div class='moltbot-error'>Failed to load history.</div>";
|
||||
this.resultsContainer.innerHTML = "<div class='openclaw-error openclaw-error moltbot-error'>Failed to load history.</div>";
|
||||
}
|
||||
} catch (e) {
|
||||
this.resultsContainer.innerHTML = "<div class='moltbot-error'>Error: " + e.message + "</div>";
|
||||
this.resultsContainer.innerHTML = "<div class='openclaw-error openclaw-error moltbot-error'>Error: " + e.message + "</div>";
|
||||
}
|
||||
},
|
||||
|
||||
setActiveToolbarButton(buttonId) {
|
||||
if (!this.container) return;
|
||||
this.container.querySelectorAll(".moltbot-lab-action-btn").forEach((btn) => {
|
||||
this.container.querySelectorAll(".openclaw-lab-action-btn").forEach((btn) => {
|
||||
btn.classList.toggle("active", buttonId ? btn.id === buttonId : false);
|
||||
});
|
||||
},
|
||||
@@ -154,26 +154,26 @@ export const ParameterLabTab = {
|
||||
renderHistoryList(experiments) {
|
||||
this.resultsContainer.innerHTML = "";
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-lab-plan-header";
|
||||
header.className = "openclaw-lab-plan-header openclaw-lab-plan-header moltbot-lab-plan-header";
|
||||
header.innerHTML = `<h4>Experiment History</h4><span>${experiments.length} Records</span>`;
|
||||
this.resultsContainer.appendChild(header);
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "moltbot-lab-run-list";
|
||||
list.className = "openclaw-lab-run-list openclaw-lab-run-list moltbot-lab-run-list";
|
||||
|
||||
if (experiments.length === 0) {
|
||||
list.innerHTML = "<div class='moltbot-hint'>No history found. Run a sweep or compare to see results here.</div>";
|
||||
list.innerHTML = "<div class='openclaw-hint openclaw-hint moltbot-hint'>No history found. Run a sweep or compare to see results here.</div>";
|
||||
}
|
||||
|
||||
experiments.forEach(exp => {
|
||||
const item = document.createElement("div");
|
||||
item.className = "moltbot-lab-run-item";
|
||||
item.className = "openclaw-lab-run-item openclaw-lab-run-item moltbot-lab-run-item";
|
||||
const dateStr = new Date(exp.created_at * 1000).toLocaleString();
|
||||
item.innerHTML = `
|
||||
<span class="run-idx">${exp.id.slice(0, 8)}</span>
|
||||
<span class="run-params">${dateStr}</span>
|
||||
<span class="run-status">${exp.completed_count}/${exp.run_count} runs</span>
|
||||
<button class="moltbot-btn-icon load-exp" title="Load Details">\u2192</button>
|
||||
<button class="openclaw-btn-icon openclaw-btn-icon moltbot-btn-icon load-exp" title="Load Details">\u2192</button>
|
||||
`;
|
||||
item.querySelector(".load-exp").onclick = () => this.loadExperiment(exp.id);
|
||||
list.appendChild(item);
|
||||
@@ -182,7 +182,7 @@ export const ParameterLabTab = {
|
||||
},
|
||||
|
||||
async loadExperiment(expId) {
|
||||
this.resultsContainer.innerHTML = "<div class='moltbot-loading'>Loading details...</div>";
|
||||
this.resultsContainer.innerHTML = "<div class='openclaw-loading openclaw-loading moltbot-loading'>Loading details...</div>";
|
||||
try {
|
||||
const res = await openclawApi.fetch(openclawApi._path(`/lab/experiments/${expId}`));
|
||||
if (res.ok && res.data) {
|
||||
@@ -191,7 +191,7 @@ export const ParameterLabTab = {
|
||||
this.renderPlan();
|
||||
}
|
||||
} catch (e) {
|
||||
this.resultsContainer.innerHTML = "<div class='moltbot-error'>Failed to load experiment.</div>";
|
||||
this.resultsContainer.innerHTML = "<div class='openclaw-error openclaw-error moltbot-error'>Failed to load experiment.</div>";
|
||||
}
|
||||
},
|
||||
|
||||
@@ -268,9 +268,9 @@ export const ParameterLabTab = {
|
||||
|
||||
// "Refresh" button (lightweight, just re-renders to pick up graph changes)
|
||||
const toolbar = document.createElement("div");
|
||||
toolbar.className = "moltbot-lab-config-toolbar";
|
||||
toolbar.className = "openclaw-lab-config-toolbar openclaw-lab-config-toolbar moltbot-lab-config-toolbar";
|
||||
const refreshBtn = document.createElement("button");
|
||||
refreshBtn.className = "moltbot-btn-text";
|
||||
refreshBtn.className = "openclaw-btn-text openclaw-btn-text moltbot-btn-text";
|
||||
refreshBtn.id = "lab-refresh-graph";
|
||||
refreshBtn.title = "Refresh from Canvas";
|
||||
refreshBtn.textContent = "\u21BB Refresh Options";
|
||||
@@ -280,7 +280,7 @@ export const ParameterLabTab = {
|
||||
|
||||
if (this.dimensions.length === 0) {
|
||||
const hint = document.createElement("div");
|
||||
hint.className = "moltbot-hint";
|
||||
hint.className = "openclaw-hint openclaw-hint moltbot-hint";
|
||||
hint.textContent = "No dimensions configured. Add one or use 'Compare Models'.";
|
||||
this.configContainer.appendChild(hint);
|
||||
return;
|
||||
@@ -296,11 +296,11 @@ export const ParameterLabTab = {
|
||||
}
|
||||
|
||||
const row = document.createElement("div");
|
||||
row.className = "moltbot-lab-dim-row dynamic";
|
||||
row.className = "openclaw-lab-dim-row openclaw-lab-dim-row moltbot-lab-dim-row dynamic";
|
||||
|
||||
// 1. Node Selector
|
||||
const nodeGroup = document.createElement("div");
|
||||
nodeGroup.className = "moltbot-form-group narrow";
|
||||
nodeGroup.className = "openclaw-form-group openclaw-form-group moltbot-form-group narrow";
|
||||
nodeGroup.innerHTML = `<label>Node</label>`;
|
||||
const nodeSelect = document.createElement("select");
|
||||
nodeSelect.className = "dim-node-select";
|
||||
@@ -332,7 +332,7 @@ export const ParameterLabTab = {
|
||||
|
||||
// 2. Widget Selector (Dependent)
|
||||
const widgetGroup = document.createElement("div");
|
||||
widgetGroup.className = "moltbot-form-group narrow";
|
||||
widgetGroup.className = "openclaw-form-group openclaw-form-group moltbot-form-group narrow";
|
||||
widgetGroup.innerHTML = `<label>Widget</label>`;
|
||||
const widgetSelect = document.createElement("select");
|
||||
widgetSelect.className = "dim-widget-select";
|
||||
@@ -371,7 +371,7 @@ export const ParameterLabTab = {
|
||||
|
||||
// 3. Value Management (Candidates + Chips)
|
||||
const valueGroup = document.createElement("div");
|
||||
valueGroup.className = "moltbot-form-group wide dynamic-values";
|
||||
valueGroup.className = "openclaw-form-group openclaw-form-group moltbot-form-group wide dynamic-values";
|
||||
valueGroup.innerHTML = `<label>Values</label>`;
|
||||
|
||||
const valueControls = document.createElement("div");
|
||||
@@ -449,7 +449,7 @@ export const ParameterLabTab = {
|
||||
chips.className = "dim-value-chips";
|
||||
(dim.values || []).forEach((v, vIdx) => {
|
||||
const chip = document.createElement("span");
|
||||
chip.className = "moltbot-chip";
|
||||
chip.className = "openclaw-chip openclaw-chip moltbot-chip";
|
||||
// IMPORTANT: render value via textContent to avoid UI injection/markup breakage from workflow-provided strings.
|
||||
chip.textContent = String(v) + " ";
|
||||
|
||||
@@ -472,7 +472,7 @@ export const ParameterLabTab = {
|
||||
|
||||
// Remove Button
|
||||
const rmBtn = document.createElement("button");
|
||||
rmBtn.className = "moltbot-btn-icon remove-dim";
|
||||
rmBtn.className = "openclaw-btn-icon openclaw-btn-icon moltbot-btn-icon remove-dim";
|
||||
rmBtn.textContent = "x";
|
||||
rmBtn.title = "Remove Dimension";
|
||||
rmBtn.onclick = () => this.removeDimension(idx);
|
||||
@@ -609,25 +609,25 @@ export const ParameterLabTab = {
|
||||
if (!this.plan) return;
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-lab-plan-header";
|
||||
header.className = "openclaw-lab-plan-header openclaw-lab-plan-header moltbot-lab-plan-header";
|
||||
header.innerHTML = `
|
||||
<h4>Experiment: ${this.experimentId.slice(0, 8)}</h4>
|
||||
<span>${this.plan.runs.length} Runs</span>
|
||||
<button id="lab-run-all" class="moltbot-btn primary">Run Experiment</button>
|
||||
<button id="lab-run-all" class="openclaw-btn openclaw-btn moltbot-btn primary">Run Experiment</button>
|
||||
`;
|
||||
this.resultsContainer.appendChild(header);
|
||||
|
||||
const list = document.createElement("div");
|
||||
list.className = "moltbot-lab-run-list";
|
||||
list.className = "openclaw-lab-run-list openclaw-lab-run-list moltbot-lab-run-list";
|
||||
|
||||
this.plan.runs.forEach((run, idx) => {
|
||||
const item = document.createElement("div");
|
||||
item.className = "moltbot-lab-run-item";
|
||||
item.className = "openclaw-lab-run-item openclaw-lab-run-item moltbot-lab-run-item";
|
||||
item.innerHTML = `
|
||||
<span class="run-idx">#${idx + 1}</span>
|
||||
<span class="run-params">${JSON.stringify(run).slice(0, 50)}...</span>
|
||||
<span class="run-status ${run.status || 'pending'}">${run.status || 'Pending'}</span>
|
||||
<button class="moltbot-btn-icon replay-run" title="Replay (Apply Values)">\u21A9\uFE0F</button>
|
||||
<button class="openclaw-btn-icon openclaw-btn-icon moltbot-btn-icon replay-run" title="Replay (Apply Values)">\u21A9\uFE0F</button>
|
||||
`;
|
||||
item.dataset.idx = idx;
|
||||
item.querySelector(".replay-run").onclick = (e) => {
|
||||
@@ -641,9 +641,9 @@ export const ParameterLabTab = {
|
||||
|
||||
// F50: Side-by-Side Comparison Layout
|
||||
if (this.plan.dimensions.some(d => d.strategy === "compare")) {
|
||||
this.resultsContainer.classList.add("moltbot-lab-compare-mode");
|
||||
this.resultsContainer.classList.add("openclaw-lab-compare-mode", "moltbot-lab-compare-mode");
|
||||
} else {
|
||||
this.resultsContainer.classList.remove("moltbot-lab-compare-mode");
|
||||
this.resultsContainer.classList.remove("openclaw-lab-compare-mode", "moltbot-lab-compare-mode");
|
||||
}
|
||||
|
||||
this.resultsContainer.querySelector("#lab-run-all").onclick = () => this.runExperiment();
|
||||
@@ -654,7 +654,7 @@ export const ParameterLabTab = {
|
||||
this.isRunning = true;
|
||||
openclawUI.showBanner("info", "Starting experiment...");
|
||||
|
||||
const items = this.resultsContainer.querySelectorAll(".moltbot-lab-run-item");
|
||||
const items = this.resultsContainer.querySelectorAll(".openclaw-lab-run-item");
|
||||
|
||||
// Subscribe to events for status updates
|
||||
const es = openclawApi.subscribeEvents((data) => {
|
||||
|
||||
+29
-29
@@ -8,30 +8,30 @@ export const PlannerTab = {
|
||||
|
||||
render(container) {
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-scroll-area">
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Generation Goal</div>
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area">
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Generation Goal</div>
|
||||
|
||||
<div class="moltbot-error-box" style="display:none" id="planner-error"></div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none" id="planner-error"></div>
|
||||
|
||||
<div class="moltbot-grid-2">
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Profile</label>
|
||||
<select id="planner-profile" class="moltbot-select">
|
||||
<div class="openclaw-grid-2 openclaw-grid-2 moltbot-grid-2">
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Profile</label>
|
||||
<select id="planner-profile" class="openclaw-select openclaw-select moltbot-select">
|
||||
<option value="SDXL-v1">SDXL v1</option>
|
||||
<option value="Flux-Dev">Flux Dev</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Style / Directives</label>
|
||||
<input type="text" id="planner-style" class="moltbot-input" placeholder="e.g. Cyberpunk, 8k...">
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Style / Directives</label>
|
||||
<input type="text" id="planner-style" class="openclaw-input openclaw-input moltbot-input" placeholder="e.g. Cyberpunk, 8k...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Requirements</label>
|
||||
<textarea id="planner-reqs" class="moltbot-textarea moltbot-textarea-sm" placeholder="Describe the image..."></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Requirements</label>
|
||||
<textarea id="planner-reqs" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-sm openclaw-textarea-sm moltbot-textarea-sm" placeholder="Describe the image..."></textarea>
|
||||
</div>
|
||||
|
||||
<!-- R38-Lite: Loading state container -->
|
||||
@@ -43,26 +43,26 @@ export const PlannerTab = {
|
||||
<div id="planner-elapsed" style="font-size: 0.9em; opacity: 0.7;">Elapsed: 0s</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="planner-cancel-btn" class="moltbot-btn" style="margin-top: 8px; width: 100%; background: var(--input-background); border: 1px solid var(--border-color);">Cancel</button>
|
||||
<button id="planner-cancel-btn" class="openclaw-btn openclaw-btn moltbot-btn" style="margin-top: 8px; width: 100%; background: var(--input-background); border: 1px solid var(--border-color);">Cancel</button>
|
||||
</div>
|
||||
|
||||
<button id="planner-run-btn" class="moltbot-btn moltbot-btn-primary">Plan Generation</button>
|
||||
<button id="planner-run-btn" class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary">Plan Generation</button>
|
||||
</div>
|
||||
|
||||
<div id="planner-results" style="display:none;" class="moltbot-split-v">
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Plan Output</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Positive</label>
|
||||
<textarea id="planner-out-pos" class="moltbot-textarea moltbot-textarea-md" readonly></textarea>
|
||||
<div id="planner-results" style="display:none;" class="openclaw-split-v openclaw-split-v moltbot-split-v">
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Plan Output</div>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Positive</label>
|
||||
<textarea id="planner-out-pos" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-md openclaw-textarea-md moltbot-textarea-md" readonly></textarea>
|
||||
</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Negative</label>
|
||||
<textarea id="planner-out-neg" class="moltbot-textarea" rows="2" readonly></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Negative</label>
|
||||
<textarea id="planner-out-neg" class="openclaw-textarea openclaw-textarea moltbot-textarea" rows="2" readonly></textarea>
|
||||
</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Params (JSON)</label>
|
||||
<textarea id="planner-out-params" class="moltbot-textarea moltbot-textarea-md" readonly></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Params (JSON)</label>
|
||||
<textarea id="planner-out-params" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-md openclaw-textarea-md moltbot-textarea-md" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+32
-32
@@ -8,36 +8,36 @@ export const RefinerTab = {
|
||||
|
||||
render(container) {
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-scroll-area">
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Source Context</div>
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area">
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Source Context</div>
|
||||
|
||||
<div class="moltbot-error-box" style="display:none"></div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none"></div>
|
||||
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Source Image</label>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Source Image</label>
|
||||
<div style="display:flex; gap:10px; align-items:center;">
|
||||
<input type="file" id="refiner-img-upload" class="moltbot-input" accept="image/png, image/jpeg">
|
||||
<input type="file" id="refiner-img-upload" class="openclaw-input openclaw-input moltbot-input" accept="image/png, image/jpeg">
|
||||
<img id="refiner-img-preview" style="height:40px; border-radius:4px; display:none; border:1px solid #444;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Original Positive</label>
|
||||
<textarea id="refiner-orig-pos" class="moltbot-textarea"></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Original Positive</label>
|
||||
<textarea id="refiner-orig-pos" class="openclaw-textarea openclaw-textarea moltbot-textarea"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Original Negative</label>
|
||||
<textarea id="refiner-orig-neg" class="moltbot-textarea" rows="2"></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Original Negative</label>
|
||||
<textarea id="refiner-orig-neg" class="openclaw-textarea openclaw-textarea moltbot-textarea" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Goal / Issue</div>
|
||||
<div class="moltbot-input-group">
|
||||
<textarea id="refiner-issue" class="moltbot-textarea moltbot-textarea-sm" placeholder="What's wrong? or What to change?"></textarea>
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Goal / Issue</div>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<textarea id="refiner-issue" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-sm openclaw-textarea-sm moltbot-textarea-sm" placeholder="What's wrong? or What to change?"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- R38-Lite: Loading state container -->
|
||||
@@ -49,27 +49,27 @@ export const RefinerTab = {
|
||||
<div id="refiner-elapsed" style="font-size: 0.9em; opacity: 0.7;">Elapsed: 0s</div>
|
||||
</div>
|
||||
</div>
|
||||
<button id="refiner-cancel-btn" class="moltbot-btn" style="margin-top: 8px; width: 100%; background: var(--input-background); border: 1px solid var(--border-color);">Cancel</button>
|
||||
<button id="refiner-cancel-btn" class="openclaw-btn openclaw-btn moltbot-btn" style="margin-top: 8px; width: 100%; background: var(--input-background); border: 1px solid var(--border-color);">Cancel</button>
|
||||
</div>
|
||||
|
||||
<button id="refiner-run-btn" class="moltbot-btn moltbot-btn-primary">Refine Prompts</button>
|
||||
<button id="refiner-run-btn" class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary">Refine Prompts</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="refiner-results" style="display:none;" class="moltbot-split-v">
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Refinement</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Rationale</label>
|
||||
<div id="refiner-rationale" class="moltbot-markdown-box"></div>
|
||||
<div id="refiner-results" style="display:none;" class="openclaw-split-v openclaw-split-v moltbot-split-v">
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Refinement</div>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Rationale</label>
|
||||
<div id="refiner-rationale" class="openclaw-markdown-box openclaw-markdown-box moltbot-markdown-box"></div>
|
||||
</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">New Positive</label>
|
||||
<textarea id="refiner-new-pos" class="moltbot-textarea moltbot-textarea-md"></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">New Positive</label>
|
||||
<textarea id="refiner-new-pos" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-md openclaw-textarea-md moltbot-textarea-md"></textarea>
|
||||
</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">New Negative</label>
|
||||
<textarea id="refiner-new-neg" class="moltbot-textarea" rows="2"></textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">New Negative</label>
|
||||
<textarea id="refiner-new-neg" class="openclaw-textarea openclaw-textarea moltbot-textarea" rows="2"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+73
-73
@@ -10,12 +10,12 @@ export const settingsTab = {
|
||||
title: "Settings",
|
||||
icon: "pi pi-cog",
|
||||
render: async (container) => {
|
||||
// IMPORTANT (UI layout): `.moltbot-content` has `overflow: hidden`.
|
||||
// This tab MUST render its own scroll container (`.moltbot-scroll-area`),
|
||||
// IMPORTANT (UI layout): `.openclaw-content` has `overflow: hidden`.
|
||||
// This tab MUST render its own scroll container (`.openclaw-scroll-area`),
|
||||
// otherwise lower sections (e.g. UI Key Store) will be clipped with no way to scroll.
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-scroll-area" id="openclaw-settings-scroll">
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area" id="openclaw-settings-scroll">
|
||||
<div class="openclaw-loading-gate" style="padding:16px;text-align:center;opacity:0.6;">Initializing…</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -47,7 +47,7 @@ export const settingsTab = {
|
||||
if (all404) {
|
||||
const warn = createSection("Backend Not Loaded");
|
||||
const hint = document.createElement("div");
|
||||
hint.className = "moltbot-note";
|
||||
hint.className = "openclaw-note openclaw-note moltbot-note";
|
||||
hint.style.borderLeft = "4px solid #ff4444";
|
||||
hint.innerHTML = `
|
||||
OpenClaw UI loaded, but the server endpoints returned <code>HTTP 404</code>.
|
||||
@@ -71,7 +71,7 @@ export const settingsTab = {
|
||||
if (!all404 && healthRes.ok && Object.keys(capabilities).length === 0) {
|
||||
const degradedWarn = createSection("Limited Mode");
|
||||
const degradedHint = document.createElement("div");
|
||||
degradedHint.className = "moltbot-note";
|
||||
degradedHint.className = "openclaw-note openclaw-note moltbot-note";
|
||||
degradedHint.style.borderLeft = "4px solid #ffaa00";
|
||||
degradedHint.innerHTML = `
|
||||
<b>⚠ Capabilities endpoint unavailable.</b> Some features may be hidden or behave differently.
|
||||
@@ -168,7 +168,7 @@ export const settingsTab = {
|
||||
// Provider dropdown
|
||||
const providerRow = createFormRow("Provider", sources.provider === "env");
|
||||
const providerSelect = document.createElement("select");
|
||||
providerSelect.className = "moltbot-input";
|
||||
providerSelect.className = "openclaw-input openclaw-input moltbot-input";
|
||||
providerSelect.disabled = sources.provider === "env";
|
||||
providers.forEach(p => {
|
||||
const opt = document.createElement("option");
|
||||
@@ -187,7 +187,7 @@ export const settingsTab = {
|
||||
modelSelect.innerHTML = "";
|
||||
modelDatalist.innerHTML = "";
|
||||
modelsStatus.textContent = "";
|
||||
modelsStatus.className = "moltbot-status";
|
||||
modelsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
updateModelUiVisibility();
|
||||
};
|
||||
providerSelect.onchange = () => resetModelList();
|
||||
@@ -204,13 +204,13 @@ export const settingsTab = {
|
||||
// - After "Load Models": show a real <select> for discoverability + still allow "Custom…".
|
||||
const modelInput = document.createElement("input");
|
||||
modelInput.type = "text";
|
||||
modelInput.className = "moltbot-input";
|
||||
modelInput.className = "openclaw-input openclaw-input moltbot-input";
|
||||
modelInput.value = config.model || "";
|
||||
modelInput.disabled = sources.model === "env";
|
||||
modelInput.style.flex = "1";
|
||||
|
||||
const modelSelect = document.createElement("select");
|
||||
modelSelect.className = "moltbot-input";
|
||||
modelSelect.className = "openclaw-input openclaw-input moltbot-input";
|
||||
modelSelect.disabled = sources.model === "env";
|
||||
modelSelect.style.flex = "1";
|
||||
modelSelect.style.display = "none"; // shown after models load
|
||||
@@ -274,13 +274,13 @@ export const settingsTab = {
|
||||
};
|
||||
|
||||
const refreshModelsBtn = document.createElement("button");
|
||||
refreshModelsBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
refreshModelsBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
refreshModelsBtn.textContent = "Load Models";
|
||||
refreshModelsBtn.disabled = false;
|
||||
refreshModelsBtn.title = "Fetch remote model list (admin boundary).";
|
||||
|
||||
const modelsStatus = document.createElement("div");
|
||||
modelsStatus.className = "moltbot-status";
|
||||
modelsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
modelsStatus.style.minWidth = "120px";
|
||||
|
||||
let tokenInput; // Will be set below
|
||||
@@ -289,7 +289,7 @@ export const settingsTab = {
|
||||
const token = (tokenInput?.value || OpenClawSession.getAdminToken() || "").trim();
|
||||
refreshModelsBtn.disabled = true;
|
||||
modelsStatus.textContent = "Loading...";
|
||||
modelsStatus.className = "moltbot-status";
|
||||
modelsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
|
||||
const res = await openclawApi.getModelList(providerSelect.value, token);
|
||||
if (res.ok) {
|
||||
@@ -303,14 +303,14 @@ export const settingsTab = {
|
||||
});
|
||||
populateModelSelect(models);
|
||||
modelsStatus.textContent = `✓ ${models.length} models`;
|
||||
modelsStatus.className = "moltbot-status ok";
|
||||
modelsStatus.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
} else {
|
||||
const detail = [
|
||||
res.status ? `HTTP ${res.status}` : null,
|
||||
res.error || "Failed",
|
||||
].filter(Boolean).join(" — ");
|
||||
modelsStatus.textContent = `✗ ${detail}`;
|
||||
modelsStatus.className = "moltbot-status error";
|
||||
modelsStatus.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
refreshModelsBtn.disabled = false;
|
||||
};
|
||||
@@ -328,7 +328,7 @@ export const settingsTab = {
|
||||
const baseUrlRow = createFormRow("Base URL", sources.base_url === "env");
|
||||
const baseUrlInput = document.createElement("input");
|
||||
baseUrlInput.type = "text";
|
||||
baseUrlInput.className = "moltbot-input";
|
||||
baseUrlInput.className = "openclaw-input openclaw-input moltbot-input";
|
||||
baseUrlInput.value = config.base_url || "";
|
||||
baseUrlInput.placeholder = "Leave empty for provider default";
|
||||
baseUrlInput.disabled = sources.base_url === "env";
|
||||
@@ -342,7 +342,7 @@ export const settingsTab = {
|
||||
const timeoutRow = createFormRow("Timeout (sec)", sources.timeout_sec === "env");
|
||||
const timeoutInput = document.createElement("input");
|
||||
timeoutInput.type = "number";
|
||||
timeoutInput.className = "moltbot-input moltbot-input-sm";
|
||||
timeoutInput.className = "openclaw-input openclaw-input moltbot-input openclaw-input-sm openclaw-input-sm moltbot-input-sm";
|
||||
timeoutInput.value = config.timeout_sec || 120;
|
||||
timeoutInput.min = 5;
|
||||
timeoutInput.max = 300;
|
||||
@@ -354,7 +354,7 @@ export const settingsTab = {
|
||||
const retriesRow = createFormRow("Max Retries", sources.max_retries === "env");
|
||||
const retriesInput = document.createElement("input");
|
||||
retriesInput.type = "number";
|
||||
retriesInput.className = "moltbot-input moltbot-input-sm";
|
||||
retriesInput.className = "openclaw-input openclaw-input moltbot-input openclaw-input-sm openclaw-input-sm moltbot-input-sm";
|
||||
retriesInput.value = config.max_retries || 3;
|
||||
retriesInput.min = 0;
|
||||
retriesInput.max = 10;
|
||||
@@ -382,13 +382,13 @@ export const settingsTab = {
|
||||
);
|
||||
tokenInput = document.createElement("input");
|
||||
tokenInput.type = "password";
|
||||
tokenInput.className = "moltbot-input";
|
||||
tokenInput.className = "openclaw-input openclaw-input moltbot-input";
|
||||
tokenInput.placeholder = "Enter OPENCLAW_ADMIN_TOKEN if required (localhost-only if not configured)";
|
||||
tokenInput.value = "";
|
||||
tokenInput.autocomplete = "off";
|
||||
|
||||
const tokenClearBtn = document.createElement("button");
|
||||
tokenClearBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
tokenClearBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
tokenClearBtn.textContent = "Clear";
|
||||
tokenClearBtn.style.marginLeft = "4px";
|
||||
tokenClearBtn.onclick = () => {
|
||||
@@ -402,16 +402,16 @@ export const settingsTab = {
|
||||
|
||||
// Status message area
|
||||
const statusDiv = document.createElement("div");
|
||||
statusDiv.className = "moltbot-status";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status";
|
||||
llmSec.appendChild(statusDiv);
|
||||
|
||||
// Buttons row
|
||||
const btnRow = document.createElement("div");
|
||||
btnRow.className = "moltbot-btn-row";
|
||||
btnRow.className = "openclaw-btn-row openclaw-btn-row moltbot-btn-row";
|
||||
|
||||
// Save button
|
||||
const saveBtn = document.createElement("button");
|
||||
saveBtn.className = "moltbot-btn";
|
||||
saveBtn.className = "openclaw-btn openclaw-btn moltbot-btn";
|
||||
saveBtn.textContent = "Save";
|
||||
saveBtn.onclick = async () => {
|
||||
const token = (tokenInput.value || OpenClawSession.getAdminToken() || "").trim();
|
||||
@@ -419,7 +419,7 @@ export const settingsTab = {
|
||||
|
||||
saveBtn.disabled = true;
|
||||
statusDiv.textContent = "Saving...";
|
||||
statusDiv.className = "moltbot-status";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status";
|
||||
|
||||
const updates = {
|
||||
provider: providerSelect.value,
|
||||
@@ -448,19 +448,19 @@ export const settingsTab = {
|
||||
|
||||
if (apply.restart_required?.length > 0) {
|
||||
msg += " Restart required for: " + apply.restart_required.join(", ");
|
||||
statusDiv.className = "moltbot-status warning"; // Yellow/Orange
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status warning"; // Yellow/Orange
|
||||
} else if (apply.applied_now?.length > 0) {
|
||||
msg += " Applied immediately (Hot Reload).";
|
||||
statusDiv.className = "moltbot-status ok";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
} else {
|
||||
// No changes or unknown
|
||||
statusDiv.className = "moltbot-status ok";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
}
|
||||
statusDiv.textContent = msg;
|
||||
} else {
|
||||
const errorMsg = getAdminErrorMessage(res.error, res.status);
|
||||
statusDiv.textContent = `✗ ${res.errors?.join(", ") || errorMsg}`;
|
||||
statusDiv.className = "moltbot-status error";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
saveBtn.disabled = false;
|
||||
};
|
||||
@@ -468,7 +468,7 @@ export const settingsTab = {
|
||||
|
||||
// Test button
|
||||
const testBtn = document.createElement("button");
|
||||
testBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
testBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
testBtn.textContent = "Test Connection";
|
||||
|
||||
// R54: Debounced Test Action to prevent spam
|
||||
@@ -485,7 +485,7 @@ export const settingsTab = {
|
||||
|
||||
testBtn.disabled = true;
|
||||
statusDiv.textContent = "Testing...";
|
||||
statusDiv.className = "moltbot-status";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status";
|
||||
|
||||
// IMPORTANT (provider mismatch): "Test Connection" must test the provider/model currently
|
||||
// selected in the UI, even if the user hasn't clicked Save yet. Otherwise, the backend
|
||||
@@ -501,11 +501,11 @@ export const settingsTab = {
|
||||
});
|
||||
if (res.ok) {
|
||||
statusDiv.textContent = "✓ Success! " + (res.response ? `"${res.response}"` : "");
|
||||
statusDiv.className = "moltbot-status ok";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
} else {
|
||||
const errorMsg = getAdminErrorMessage(res.error, res.status);
|
||||
statusDiv.textContent = `✗ ${errorMsg}`;
|
||||
statusDiv.className = "moltbot-status error";
|
||||
statusDiv.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
} finally {
|
||||
testBtn.disabled = false;
|
||||
@@ -517,7 +517,7 @@ export const settingsTab = {
|
||||
|
||||
// API Key instructions
|
||||
const keyNote = document.createElement("div");
|
||||
keyNote.className = "moltbot-note";
|
||||
keyNote.className = "openclaw-note openclaw-note moltbot-note";
|
||||
keyNote.innerHTML = `<b>API Key</b>: Use <code>OPENCLAW_LLM_API_KEY</code> (or provider-specific keys) via environment variable (recommended), or enable the UI Key Store below (server-side storage; never stored in browser).`;
|
||||
llmSec.appendChild(keyNote);
|
||||
|
||||
@@ -544,7 +544,7 @@ export const settingsTab = {
|
||||
|
||||
const secretProviderRow = createFormRow("Store For");
|
||||
const secretProviderSelect = document.createElement("select");
|
||||
secretProviderSelect.className = "moltbot-input";
|
||||
secretProviderSelect.className = "openclaw-input openclaw-input moltbot-input";
|
||||
// Build options from provider catalog + generic fallback
|
||||
const providerOptions = [];
|
||||
providers.forEach(p => providerOptions.push({ id: p.id, label: p.label, requires_key: p.requires_key }));
|
||||
@@ -568,14 +568,14 @@ export const settingsTab = {
|
||||
|
||||
const secretKeyInput = document.createElement("input");
|
||||
secretKeyInput.type = "password";
|
||||
secretKeyInput.className = "moltbot-input";
|
||||
secretKeyInput.className = "openclaw-input openclaw-input moltbot-input";
|
||||
secretKeyInput.placeholder = "Paste provider API key (not stored in browser)";
|
||||
secretKeyInput.value = "";
|
||||
secretKeyInput.autocomplete = "off";
|
||||
secretKeyInput.style.flex = "1";
|
||||
|
||||
const secretKeyClearBtn = document.createElement("button");
|
||||
secretKeyClearBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
secretKeyClearBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
secretKeyClearBtn.textContent = "Clear";
|
||||
secretKeyClearBtn.onclick = () => {
|
||||
secretKeyInput.value = "";
|
||||
@@ -587,7 +587,7 @@ export const settingsTab = {
|
||||
secretsContent.appendChild(secretKeyRow);
|
||||
|
||||
const secretsStatus = document.createElement("div");
|
||||
secretsStatus.className = "moltbot-status";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
secretsContent.appendChild(secretsStatus);
|
||||
|
||||
const getAdminToken = () => {
|
||||
@@ -599,17 +599,17 @@ export const settingsTab = {
|
||||
const token = getAdminToken();
|
||||
|
||||
secretsStatus.textContent = "Loading...";
|
||||
secretsStatus.className = "moltbot-status";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
const res = await openclawApi.getSecretsStatus(token);
|
||||
if (res.ok) {
|
||||
const secrets = res.data?.secrets || {};
|
||||
const keys = Object.keys(secrets);
|
||||
if (keys.length === 0) {
|
||||
secretsStatus.textContent = "✓ No stored keys.";
|
||||
secretsStatus.className = "moltbot-status ok";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
} else {
|
||||
secretsStatus.textContent = `✓ Stored keys: ${keys.join(", ")}`;
|
||||
secretsStatus.className = "moltbot-status ok";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
}
|
||||
} else {
|
||||
const detail = [
|
||||
@@ -617,15 +617,15 @@ export const settingsTab = {
|
||||
res.error || "Failed",
|
||||
].filter(Boolean).join(" — ");
|
||||
secretsStatus.textContent = `✗ ${detail}`;
|
||||
secretsStatus.className = "moltbot-status error";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
};
|
||||
|
||||
const secretsBtnRow = document.createElement("div");
|
||||
secretsBtnRow.className = "moltbot-btn-row";
|
||||
secretsBtnRow.className = "openclaw-btn-row openclaw-btn-row moltbot-btn-row";
|
||||
|
||||
const secretsStatusBtn = document.createElement("button");
|
||||
secretsStatusBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
secretsStatusBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
secretsStatusBtn.textContent = "Check Status";
|
||||
secretsStatusBtn.onclick = async () => {
|
||||
secretsStatusBtn.disabled = true;
|
||||
@@ -635,27 +635,27 @@ export const settingsTab = {
|
||||
secretsBtnRow.appendChild(secretsStatusBtn);
|
||||
|
||||
const secretsSaveBtn = document.createElement("button");
|
||||
secretsSaveBtn.className = "moltbot-btn";
|
||||
secretsSaveBtn.className = "openclaw-btn openclaw-btn moltbot-btn";
|
||||
secretsSaveBtn.textContent = "Save Key";
|
||||
secretsSaveBtn.onclick = async () => {
|
||||
const token = getAdminToken();
|
||||
const apiKey = (secretKeyInput.value || "").trim();
|
||||
if (!apiKey) {
|
||||
secretsStatus.textContent = "Please paste an API key first.";
|
||||
secretsStatus.className = "moltbot-status error";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
return;
|
||||
}
|
||||
if (token) OpenClawSession.setAdminToken(token);
|
||||
|
||||
secretsSaveBtn.disabled = true;
|
||||
secretsStatus.textContent = "Saving...";
|
||||
secretsStatus.className = "moltbot-status";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
|
||||
const res = await openclawApi.saveSecret(secretProviderSelect.value, apiKey, token);
|
||||
if (res.ok) {
|
||||
secretKeyInput.value = "";
|
||||
secretsStatus.textContent = "✓ Saved to server store. Restart ComfyUI if needed.";
|
||||
secretsStatus.className = "moltbot-status ok";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
await refreshSecretsStatus();
|
||||
} else {
|
||||
const detail = [
|
||||
@@ -663,14 +663,14 @@ export const settingsTab = {
|
||||
res.error || "Failed",
|
||||
].filter(Boolean).join(" — ");
|
||||
secretsStatus.textContent = `✗ ${detail}`;
|
||||
secretsStatus.className = "moltbot-status error";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
secretsSaveBtn.disabled = false;
|
||||
};
|
||||
secretsBtnRow.appendChild(secretsSaveBtn);
|
||||
|
||||
const secretsClearBtn = document.createElement("button");
|
||||
secretsClearBtn.className = "moltbot-btn moltbot-btn-danger";
|
||||
secretsClearBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-danger openclaw-btn-danger moltbot-btn-danger";
|
||||
secretsClearBtn.textContent = "Clear Stored Key";
|
||||
secretsClearBtn.onclick = async () => {
|
||||
const token = getAdminToken();
|
||||
@@ -678,12 +678,12 @@ export const settingsTab = {
|
||||
|
||||
secretsClearBtn.disabled = true;
|
||||
secretsStatus.textContent = "Clearing...";
|
||||
secretsStatus.className = "moltbot-status";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status";
|
||||
|
||||
const res = await openclawApi.clearSecret(secretProviderSelect.value, token);
|
||||
if (res.ok) {
|
||||
secretsStatus.textContent = "✓ Cleared.";
|
||||
secretsStatus.className = "moltbot-status ok";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status ok";
|
||||
await refreshSecretsStatus();
|
||||
} else {
|
||||
const detail = [
|
||||
@@ -691,7 +691,7 @@ export const settingsTab = {
|
||||
res.error || "Failed",
|
||||
].filter(Boolean).join(" — ");
|
||||
secretsStatus.textContent = `✗ ${detail}`;
|
||||
secretsStatus.className = "moltbot-status error";
|
||||
secretsStatus.className = "openclaw-status openclaw-status moltbot-status error";
|
||||
}
|
||||
secretsClearBtn.disabled = false;
|
||||
};
|
||||
@@ -705,7 +705,7 @@ export const settingsTab = {
|
||||
// -- Logs Section --
|
||||
const logsSec = createSection("Recent Logs");
|
||||
const logView = document.createElement("div");
|
||||
logView.className = "moltbot-log-viewer";
|
||||
logView.className = "openclaw-log-viewer openclaw-log-viewer moltbot-log-viewer";
|
||||
|
||||
if (logRes.ok) {
|
||||
const content = logRes.data?.content;
|
||||
@@ -737,14 +737,14 @@ export const settingsTab = {
|
||||
// "Recent Logs" -> "logs"
|
||||
// "System Health" -> "health"
|
||||
|
||||
const sections = Array.from(scroll.querySelectorAll(".moltbot-section"));
|
||||
const sections = Array.from(scroll.querySelectorAll(".openclaw-section"));
|
||||
if (sectionKey === "llm") target = sections.find(s => s.textContent.includes("LLM Settings"));
|
||||
else if (sectionKey === "secrets") {
|
||||
target = sections.find(s => s.textContent.includes("UI Key Store"));
|
||||
// Auto-expand if targeted
|
||||
if (target) {
|
||||
const content = target.querySelector(".moltbot-collapsible-content");
|
||||
const toggle = target.querySelector(".moltbot-collapsible-header span:last-child");
|
||||
const content = target.querySelector(".openclaw-collapsible-content");
|
||||
const toggle = target.querySelector(".openclaw-collapsible-header span:last-child");
|
||||
if (content) content.style.display = "block";
|
||||
if (toggle) toggle.textContent = "▼";
|
||||
}
|
||||
@@ -765,7 +765,7 @@ export const settingsTab = {
|
||||
|
||||
function createSection(title) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "moltbot-section";
|
||||
div.className = "openclaw-section openclaw-section moltbot-section";
|
||||
const h4 = document.createElement("h4");
|
||||
h4.textContent = title;
|
||||
div.appendChild(h4);
|
||||
@@ -774,10 +774,10 @@ function createSection(title) {
|
||||
|
||||
function createCollapsibleSection(title, description, defaultExpanded = false) {
|
||||
const container = document.createElement("div");
|
||||
container.className = "moltbot-section moltbot-collapsible-section";
|
||||
container.className = "openclaw-section openclaw-section moltbot-section openclaw-collapsible-section openclaw-collapsible-section moltbot-collapsible-section";
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-collapsible-header";
|
||||
header.className = "openclaw-collapsible-header openclaw-collapsible-header moltbot-collapsible-header";
|
||||
header.style.cursor = "pointer";
|
||||
header.style.display = "flex";
|
||||
header.style.justifyContent = "space-between";
|
||||
@@ -825,13 +825,13 @@ function createCollapsibleSection(title, description, defaultExpanded = false) {
|
||||
container.appendChild(header);
|
||||
|
||||
const descDiv = document.createElement("div");
|
||||
descDiv.className = "moltbot-note";
|
||||
descDiv.className = "openclaw-note openclaw-note moltbot-note";
|
||||
descDiv.style.margin = "8px 0";
|
||||
descDiv.innerHTML = description;
|
||||
container.appendChild(descDiv);
|
||||
|
||||
const content = document.createElement("div");
|
||||
content.className = "moltbot-collapsible-content";
|
||||
content.className = "openclaw-collapsible-content openclaw-collapsible-content moltbot-collapsible-content";
|
||||
content.style.display = defaultExpanded ? "block" : "none";
|
||||
content.style.marginTop = "8px";
|
||||
container.appendChild(content);
|
||||
@@ -847,7 +847,7 @@ function createCollapsibleSection(title, description, defaultExpanded = false) {
|
||||
|
||||
function createFormRow(label, locked = false, helpBtn = null) {
|
||||
const row = document.createElement("div");
|
||||
row.className = "moltbot-form-row";
|
||||
row.className = "openclaw-form-row openclaw-form-row moltbot-form-row";
|
||||
const header = document.createElement("div");
|
||||
header.style.display = "flex";
|
||||
header.style.alignItems = "center";
|
||||
@@ -855,7 +855,7 @@ function createFormRow(label, locked = false, helpBtn = null) {
|
||||
header.style.gap = "8px";
|
||||
|
||||
const lbl = document.createElement("label");
|
||||
lbl.className = "moltbot-label";
|
||||
lbl.className = "openclaw-label openclaw-label moltbot-label";
|
||||
lbl.textContent = label + (locked ? " 🔒" : "");
|
||||
if (locked) lbl.title = "Locked (env override)";
|
||||
|
||||
@@ -868,7 +868,7 @@ function createFormRow(label, locked = false, helpBtn = null) {
|
||||
function createHelpButton(title, html) {
|
||||
const btn = document.createElement("button");
|
||||
btn.type = "button";
|
||||
btn.className = "moltbot-help-btn";
|
||||
btn.className = "openclaw-help-btn openclaw-help-btn moltbot-help-btn";
|
||||
btn.textContent = "?";
|
||||
btn.title = "Help";
|
||||
btn.onclick = (e) => {
|
||||
@@ -880,30 +880,30 @@ function createHelpButton(title, html) {
|
||||
|
||||
function showHelpModal(title, html) {
|
||||
// Remove any existing modal overlay
|
||||
const existing = document.querySelector(".moltbot-modal-overlay");
|
||||
const existing = document.querySelector(".openclaw-modal-overlay");
|
||||
if (existing) existing.remove();
|
||||
|
||||
const overlay = document.createElement("div");
|
||||
overlay.className = "moltbot-modal-overlay";
|
||||
overlay.className = "openclaw-modal-overlay openclaw-modal-overlay moltbot-modal-overlay";
|
||||
overlay.addEventListener("click", (e) => {
|
||||
if (e.target === overlay) overlay.remove();
|
||||
});
|
||||
|
||||
const modal = document.createElement("div");
|
||||
modal.className = "moltbot-modal";
|
||||
modal.className = "openclaw-modal openclaw-modal moltbot-modal";
|
||||
|
||||
const header = document.createElement("div");
|
||||
header.className = "moltbot-modal-header";
|
||||
header.className = "openclaw-modal-header openclaw-modal-header moltbot-modal-header";
|
||||
header.textContent = title;
|
||||
|
||||
const closeBtn = document.createElement("button");
|
||||
closeBtn.className = "moltbot-btn moltbot-btn-secondary";
|
||||
closeBtn.className = "openclaw-btn openclaw-btn moltbot-btn openclaw-btn-secondary openclaw-btn-secondary moltbot-btn-secondary";
|
||||
closeBtn.textContent = "Close";
|
||||
closeBtn.onclick = () => overlay.remove();
|
||||
header.appendChild(closeBtn);
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "moltbot-modal-body";
|
||||
body.className = "openclaw-modal-body openclaw-modal-body moltbot-modal-body";
|
||||
body.innerHTML = html;
|
||||
|
||||
modal.appendChild(header);
|
||||
@@ -914,14 +914,14 @@ function showHelpModal(title, html) {
|
||||
|
||||
function addRow(container, key, val, valClass = "") {
|
||||
const row = document.createElement("div");
|
||||
row.className = "moltbot-kv-row";
|
||||
row.className = "openclaw-kv-row openclaw-kv-row moltbot-kv-row";
|
||||
|
||||
const k = document.createElement("span");
|
||||
k.className = "moltbot-kv-key";
|
||||
k.className = "openclaw-kv-key openclaw-kv-key moltbot-kv-key";
|
||||
k.textContent = key;
|
||||
|
||||
const v = document.createElement("span");
|
||||
v.className = `moltbot-kv-val ${valClass}`;
|
||||
v.className = `openclaw-kv-val openclaw-kv-val moltbot-kv-val ${valClass}`;
|
||||
v.textContent = val;
|
||||
|
||||
row.appendChild(k);
|
||||
|
||||
+21
-21
@@ -7,42 +7,42 @@ export const VariantsTab = {
|
||||
|
||||
render(container) {
|
||||
container.innerHTML = `
|
||||
<div class="moltbot-panel">
|
||||
<div class="moltbot-scroll-area">
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Variants Configuration</div>
|
||||
<div class="openclaw-panel openclaw-panel moltbot-panel">
|
||||
<div class="openclaw-scroll-area openclaw-scroll-area moltbot-scroll-area">
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Variants Configuration</div>
|
||||
|
||||
<div class="moltbot-error-box" style="display:none"></div>
|
||||
<div class="openclaw-error-box openclaw-error-box moltbot-error-box" style="display:none"></div>
|
||||
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Base Parameters (JSON)</label>
|
||||
<textarea id="var-base-params" class="moltbot-textarea moltbot-textarea-md">{"width": 1024, "height": 1024, "seed": 0}</textarea>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Base Parameters (JSON)</label>
|
||||
<textarea id="var-base-params" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-md openclaw-textarea-md moltbot-textarea-md">{"width": 1024, "height": 1024, "seed": 0}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-grid-2">
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Strategy</label>
|
||||
<select id="var-strategy" class="moltbot-select">
|
||||
<div class="openclaw-grid-2 openclaw-grid-2 moltbot-grid-2">
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Strategy</label>
|
||||
<select id="var-strategy" class="openclaw-select openclaw-select moltbot-select">
|
||||
<option value="seeds">Seed Sweep (Count)</option>
|
||||
<option value="cfg">CFG Scale (Range)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Dynamic inputs based on strategy -->
|
||||
<div id="var-opts-seeds" class="var-opts moltbot-input-group">
|
||||
<label class="moltbot-label">Count</label>
|
||||
<input type="number" id="var-seed-count" class="moltbot-input" value="4" min="1" max="100">
|
||||
<div id="var-opts-seeds" class="var-opts openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Count</label>
|
||||
<input type="number" id="var-seed-count" class="openclaw-input openclaw-input moltbot-input" value="4" min="1" max="100">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="var-run-btn" class="moltbot-btn moltbot-btn-primary">Generate Variants JSON</button>
|
||||
<button id="var-run-btn" class="openclaw-btn openclaw-btn moltbot-btn openclaw-btn-primary openclaw-btn-primary moltbot-btn-primary">Generate Variants JSON</button>
|
||||
</div>
|
||||
|
||||
<div class="moltbot-card">
|
||||
<div class="moltbot-section-header">Resulting List</div>
|
||||
<div class="moltbot-input-group">
|
||||
<label class="moltbot-label">Output (List of Params)</label>
|
||||
<textarea id="var-output" class="moltbot-textarea moltbot-textarea-lg" readonly></textarea>
|
||||
<div class="openclaw-card openclaw-card moltbot-card">
|
||||
<div class="openclaw-section-header openclaw-section-header moltbot-section-header">Resulting List</div>
|
||||
<div class="openclaw-input-group openclaw-input-group moltbot-input-group">
|
||||
<label class="openclaw-label openclaw-label moltbot-label">Output (List of Params)</label>
|
||||
<textarea id="var-output" class="openclaw-textarea openclaw-textarea moltbot-textarea openclaw-textarea-lg openclaw-textarea-lg moltbot-textarea-lg" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user