aboutsummaryrefslogtreecommitdiff
path: root/main.html
diff options
context:
space:
mode:
Diffstat (limited to 'main.html')
-rw-r--r--main.html810
1 files changed, 810 insertions, 0 deletions
diff --git a/main.html b/main.html
new file mode 100644
index 0000000..f1f315e
--- /dev/null
+++ b/main.html
@@ -0,0 +1,810 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+ <meta name="theme-color" content="#000000">
+ <title>WebX11 Module System v4.2</title>
+ <style>
+ :root {
+ --bg-color: #000000;
+ --bg-secondary: #0a0a0a;
+ --bg-tertiary: #111111;
+ --text-color: #00ff00;
+ --text-muted: #888888;
+ --accent-color: #00aa00;
+ --border-color: #005500;
+ --error-color: #ff5555;
+ --warning-color: #ffaa00;
+ --info-color: #00aaff;
+ --success-color: #00ff00;
+ --ai-text-color: #aaddff;
+ --ai-bg-color: #001a26;
+ --font-primary: 'Courier New', Courier, monospace;
+ }
+ * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
+ body {
+ background-color: var(--bg-color);
+ color: var(--text-color);
+ font-family: var(--font-primary);
+ font-size: 16px;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ }
+ @media (max-width: 600px) { body { font-size: 12px; } }
+ header {
+ border-bottom: 1px solid var(--border-color);
+ padding: 10px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: var(--bg-secondary);
+ flex-shrink: 0;
+ }
+ .header-title { font-weight: bold; font-size: 1.1em; }
+ .header-version { font-size: 0.7em; color: var(--text-muted); }
+ .status-pill {
+ font-size: 0.8em;
+ padding: 2px 6px;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ background: var(--bg-tertiary);
+ }
+ #terminal-container {
+ flex-grow: 1;
+ overflow-y: auto;
+ padding: 10px;
+ scroll-behavior: smooth;
+ -webkit-overflow-scrolling: touch;
+ }
+ #ai-confirm-area {
+ display: none;
+ flex-shrink: 0;
+ padding: 10px;
+ margin: 0 8px 4px;
+ border: 1px solid var(--warning-color);
+ border-radius: 6px;
+ background: var(--bg-secondary);
+ z-index: 200;
+ font-size: 0.95em;
+ min-height: 48px;
+ align-items: center;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: center;
+ pointer-events: auto;
+ }
+ #ai-confirm-area.visible { display: flex; }
+ #ai-confirm-area.hidden { display: none !important; }
+ #ai-confirm-area .confirm-desc {
+ flex: 1;
+ min-width: 120px;
+ color: var(--warning-color);
+ word-break: break-word;
+ }
+ #ai-confirm-area .confirm-btn {
+ padding: 12px 24px;
+ border: 1px solid var(--success-color);
+ color: var(--success-color);
+ background: transparent;
+ border-radius: 6px;
+ cursor: pointer;
+ font-family: inherit;
+ font-size: 1.1em;
+ font-weight: bold;
+ touch-action: manipulation;
+ min-height: 48px;
+ min-width: 90px;
+ user-select: none;
+ -webkit-user-select: none;
+ transition: background 0.15s;
+ }
+ #ai-confirm-area .confirm-btn:active {
+ background: rgba(0, 255, 0, 0.15);
+ }
+ #ai-confirm-area .confirm-btn.deny {
+ border-color: var(--error-color);
+ color: var(--error-color);
+ }
+ #ai-confirm-area .confirm-timer {
+ font-size: 0.8em;
+ color: var(--warning-color);
+ flex-shrink: 0;
+ }
+ #ai-confirm-area .confirm-timer.urgent {
+ color: var(--error-color);
+ font-weight: bold;
+ }
+ #ai-confirm-area .confirm-timer.critical {
+ color: #ff0000;
+ font-weight: bold;
+ animation: pulse 1s infinite;
+ }
+ /* ── Startup Modules Modal ── */
+ .output-line { white-space: pre-wrap; word-break: break-word; line-height: 1.4; }
+ .output-line.command-prompt { color: var(--text-color); font-weight: bold; }
+ .output-line.error { color: var(--error-color); }
+ .output-line.warning { color: var(--warning-color); }
+ .output-line.info { color: var(--info-color); }
+ .output-line.success { color: var(--success-color); }
+ .output-line.text-muted { color: var(--text-muted); }
+ .output-line.ollama {
+ color: var(--ai-text-color);
+ background: var(--ai-bg-color);
+ padding: 8px;
+ border-radius: 4px;
+ border: 1px solid #003344;
+ margin-bottom: 10px;
+ position: relative;
+ }
+ #input-area {
+ border-top: 1px solid var(--border-color);
+ padding: 8px 10px;
+ background: var(--bg-secondary);
+ display: flex;
+ align-items: flex-start;
+ flex-shrink: 0;
+ position: relative;
+ }
+ .prompt-label { margin-right: 8px; color: var(--accent-color); white-space: nowrap; margin-top: 6px; }
+ #command-input {
+ flex-grow: 1;
+ background: transparent;
+ border: none;
+ color: var(--text-color);
+ font-family: var(--font-primary);
+ font-size: inherit;
+ outline: none;
+ resize: none;
+ overflow-y: auto;
+ max-height: 120px;
+ line-height: 1.4;
+ padding: 4px 0;
+ }
+ #send-btn {
+ flex-shrink: 0;
+ min-width: 44px;
+ touch-action: manipulation;
+ margin-top: 2px;
+ }
+ #quick-actions { scrollbar-width: none; -ms-overflow-style: none; }
+ #quick-actions::-webkit-scrollbar { display: none; }
+ @media (max-width: 480px) {
+ #input-area { padding: 6px; }
+ #command-input { font-size: 0.9em; }
+ .prompt-label { font-size: 0.8em; max-width: 80px; overflow: hidden; text-overflow: ellipsis; }
+ }
+ #autocomplete-dropdown {
+ position: absolute;
+ bottom: 100%;
+ left: 0;
+ right: 0;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ border-bottom: none;
+ max-height: 200px;
+ overflow-y: auto;
+ display: none;
+ z-index: 100;
+ }
+ .autocomplete-item {
+ padding: 8px 12px;
+ cursor: pointer;
+ border-bottom: 1px solid #111;
+ }
+ .autocomplete-item:hover, .autocomplete-item.selected {
+ background: var(--bg-tertiary);
+ color: var(--success-color);
+ }
+ .autocomplete-item .cmd-name { color: var(--text-color); font-weight: bold; }
+ .autocomplete-item .cmd-desc { color: var(--text-muted); font-size: 0.85em; }
+ #quick-actions {
+ display: flex;
+ gap: 8px;
+ padding: 8px 10px;
+ overflow-x: auto;
+ border-top: 1px solid #111;
+ background: var(--bg-tertiary);
+ flex-shrink: 0;
+ }
+ .qa-btn {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ color: var(--text-color);
+ padding: 8px 12px;
+ border-radius: 4px;
+ font-family: inherit;
+ font-size: 0.9em;
+ cursor: pointer;
+ white-space: nowrap;
+ touch-action: manipulation;
+ }
+ .qa-btn:active { background: var(--border-color); }
+ #status-bar {
+ border-top: 1px solid var(--border-color);
+ padding: 4px 10px;
+ font-size: 0.8em;
+ display: flex;
+ justify-content: space-between;
+ color: var(--text-muted);
+ flex-shrink: 0;
+ }
+ .modal-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(0,0,0,0.85);
+ display: none;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ }
+ .modal-content {
+ background: var(--bg-color);
+ border: 1px solid var(--border-color);
+ width: 90%;
+ max-width: 500px;
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ border-radius: 4px;
+ }
+ .modal-header {
+ padding: 10px;
+ border-bottom: 1px solid var(--border-color);
+ display: flex;
+ justify-content: space-between;
+ font-weight: bold;
+ }
+ .modal-body { padding: 10px; overflow-y: auto; flex-grow: 1; }
+ .modal-footer {
+ padding: 10px;
+ border-top: 1px solid var(--border-color);
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+ }
+ .btn {
+ padding: 6px 12px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-color);
+ color: var(--text-color);
+ cursor: pointer;
+ border-radius: 3px;
+ }
+ .btn:hover { background: var(--bg-tertiary); }
+ .btn-danger { border-color: var(--error-color); color: var(--error-color); }
+ .file-item, .module-item, .model-item {
+ padding: 8px;
+ border-bottom: 1px solid #111;
+ cursor: pointer;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+ .file-item:hover, .module-item:hover, .model-item:hover { background: var(--bg-tertiary); }
+ .file-item.dir { color: var(--info-color); }
+ .module-item.active { border-left: 3px solid var(--success-color); }
+ .module-item.disabled { opacity: 0.5; }
+ .model-item.connected { color: var(--success-color); }
+ .hidden { display: none !important; }
+ .text-center { text-align: center; }
+ .clickable-file, .clickable-dir {
+ cursor: pointer;
+ display: inline-block;
+ padding: 1px 3px;
+ border: 1px solid transparent;
+ border-radius: 2px;
+ margin: 1px 0;
+ }
+ .clickable-file:hover { background: rgba(0,255,0,0.1); border-color: var(--border-color); }
+ .module-url-input {
+ width: 100%;
+ padding: 8px;
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border-color);
+ color: var(--text-color);
+ font-family: var(--font-primary);
+ margin-bottom: 10px;
+ }
+ .ollama-status {
+ padding: 10px;
+ margin-bottom: 10px;
+ border-radius: 4px;
+ }
+ .ollama-status.connected { background: rgba(0, 255, 0, 0.1); border: 1px solid var(--success-color); }
+ .ollama-status.disconnected { background: rgba(255, 85, 85, 0.1); border: 1px solid var(--error-color); }
+ .copy-btn {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ background: rgba(0, 0, 0, 0.5);
+ border: 1px solid var(--border-color);
+ color: var(--text-muted);
+ font-size: 0.7em;
+ padding: 2px 6px;
+ cursor: pointer;
+ border-radius: 3px;
+ font-family: sans-serif;
+ }
+ .copy-btn:hover {
+ color: var(--text-color);
+ background: rgba(0, 255, 0, 0.1);
+ }
+ .module-badge {
+ display: inline-block;
+ padding: 2px 6px;
+ border-radius: 3px;
+ font-size: 0.75em;
+ margin-left: 5px;
+ }
+ .module-badge.builtin { background: var(--bg-tertiary); border: 1px solid var(--border-color); }
+ .fetch-content {
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border-color);
+ padding: 10px;
+ margin: 5px 0;
+ border-radius: 4px;
+ max-height: 300px;
+ overflow-y: auto;
+ font-size: 0.85em;
+ }
+ .search-result {
+ margin-bottom: 10px;
+ padding: 8px;
+ background: var(--bg-tertiary);
+ border-radius: 4px;
+ border-left: 3px solid var(--info-color);
+ }
+ .search-result a {
+ color: var(--info-color);
+ text-decoration: none;
+ }
+ .search-result a:hover {
+ text-decoration: underline;
+ }
+
+ /* ── Splash Screen ── */
+ #splash-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: var(--bg-color);
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ z-index: 2000;
+ transition: opacity 0.3s ease;
+ }
+ #splash-overlay.hidden {
+ opacity: 0;
+ pointer-events: none;
+ }
+ #splash-title {
+ font-size: 1.8em;
+ font-weight: bold;
+ margin-bottom: 4px;
+ }
+ #splash-version {
+ font-size: 0.8em;
+ color: var(--text-muted);
+ margin-bottom: 24px;
+ }
+ #splash-progress-track {
+ width: 260px;
+ max-width: 70vw;
+ height: 8px;
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ overflow: hidden;
+ margin-bottom: 12px;
+ }
+ #splash-progress-fill {
+ height: 100%;
+ width: 0%;
+ background: var(--accent-color);
+ transition: width 0.15s ease;
+ border-radius: 3px;
+ }
+ #splash-phase {
+ font-size: 0.75em;
+ color: var(--text-muted);
+ margin-bottom: 4px;
+ }
+ #splash-filename {
+ font-size: 0.85em;
+ color: var(--info-color);
+ margin-bottom: 16px;
+ }
+ #splash-cursor {
+ display: inline-block;
+ width: 8px;
+ height: 14px;
+ background: var(--text-color);
+ animation: blink 1s step-end infinite;
+ }
+ @keyframes blink {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0; }
+ }
+ #splash-gear {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ font-size: 1.4em;
+ cursor: pointer;
+ opacity: 0.5;
+ transition: opacity 0.2s;
+ touch-action: manipulation;
+ padding: 8px;
+ }
+ #splash-gear:hover { opacity: 1; }
+ #splash-error {
+ color: var(--error-color);
+ font-size: 0.85em;
+ margin-top: 12px;
+ text-align: center;
+ display: none;
+ }
+ #splash-error.show { display: block; }
+
+ /* ── Startup Modules Modal ── */
+ .mod-card {
+ display: flex;
+ align-items: center;
+ padding: 10px 8px;
+ border-bottom: 1px solid #111;
+ min-height: 44px;
+ touch-action: manipulation;
+ }
+ .mod-card:last-child { border-bottom: none; }
+ .mod-card.disabled { opacity: 0.4; pointer-events: none; }
+ .mod-card label {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-grow: 1;
+ cursor: pointer;
+ }
+ .mod-card input[type="checkbox"] {
+ width: 18px;
+ height: 18px;
+ accent-color: var(--accent-color);
+ flex-shrink: 0;
+ }
+ .mod-card .mod-name {
+ font-size: 0.9em;
+ flex-shrink: 0;
+ }
+ .mod-card .mod-badge {
+ font-size: 0.7em;
+ padding: 2px 6px;
+ border-radius: 3px;
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border-color);
+ color: var(--text-muted);
+ margin-left: auto;
+ }
+ .mod-card .mod-badge.foundation {
+ background: #001a00;
+ border-color: var(--accent-color);
+ color: var(--accent-color);
+ }
+ .mod-card .mod-badge.ext {
+ background: #1a001a;
+ border-color: #550055;
+ color: #aa55ff;
+ }
+ .mod-section-header {
+ padding: 8px 8px 4px;
+ font-size: 0.75em;
+ color: var(--text-muted);
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ border-bottom: 1px solid var(--border-color);
+ background: var(--bg-secondary);
+ }
+ @media (max-width: 400px) {
+ .mod-card { padding: 10px 6px; }
+ .mod-card .mod-name { font-size: 0.82em; }
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <div>
+ <span class="header-title">WebX11</span>
+ <span class="header-version">v4.2</span>
+ </div>
+ <div id="header-status" class="status-pill">Mem: --</div>
+ </header>
+
+ <div id="terminal-container">
+<div class="output-line info">WebX11 Module System v4.2 - Unix-like Terminal</div>
+ <div class="output-line info">Commands: ls, cd, pwd, cat, echo, rm, mkdir, mv, cp, touch, tree, find, grep</div>
+ <div class="output-line info">Type 'help' for all commands</div>
+ <div class="output-line"></div>
+ <div class="output-line info loading" id="loading-indicator">Loading modules...</div>
+ </div>
+
+ <!-- AI Confirm Area — fixed outside scrollable output, always visible -->
+ <div id="ai-confirm-area" class="hidden"></div>
+
+ <div id="quick-actions">
+ <button class="qa-btn" onclick="webx11.executeCommand('ls')">📁 Files</button>
+ <button class="qa-btn" onclick="webx11.executeCommand('help')">📖 Help</button>
+ <button class="qa-btn" onclick="webx11.executeCommand('modules')">🧩 Modules</button>
+ <button class="qa-btn" onclick="webx11.openOllamaManager()">🤖 Ollama</button>
+ <button class="qa-btn" onclick="webx11.openSkillsManager()">📚 Skills</button>
+ <button class="qa-btn" onclick="webx11.openConfigManager()">⚙ Config</button>
+ <button class="qa-btn" onclick="webx11.executeCommand('clear')">🧹 Clear</button>
+ <button class="qa-btn" id="voice-toggle-btn" onclick="webx11.toggleMicVisibility()">🎤 Mic</button>
+ <button class="qa-btn" id="sound-toggle-btn">🔊 Sound</button>
+ </div>
+
+ <div id="input-area">
+ <span class="prompt-label">$</span>
+ <textarea id="command-input" rows="1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" placeholder="Type a command..." style="resize:none; overflow-y:auto; max-height:120px;"></textarea>
+ <button id="send-btn" onclick="var i=document.getElementById('command-input');webx11.executeCommand(i.value);i.value='';i.style.height='auto'" style="background:var(--bg-tertiary); border:1px solid var(--border-color); color:var(--success-color); padding:6px 12px; border-radius:4px; cursor:pointer; font-family:inherit; font-size:0.9em; margin-left:6px; white-space:nowrap; flex-shrink:0;">⏎ Send</button>
+ <div id="autocomplete-dropdown"></div>
+ </div>
+
+ <div id="status-bar">
+ <span id="status-text">Ready</span>
+ <span id="network-status">📶 Online</span>
+ <button id="ai-cancel-btn" style="display:none; background:var(--error-color); color:#fff; border:none; border-radius:4px; padding:2px 10px; cursor:pointer; font-family:inherit; font-size:0.8em; touch-action:manipulation; min-height:28px;" onclick="webx11.executeCommand('ai-stop')">⏹ Stop</button>
+ </div>
+
+ <!-- File Browser Modal -->
+ <div id="file-browser" class="modal-overlay">
+ <div class="modal-content">
+ <div class="modal-header">
+ <span>File Browser</span>
+ <span style="cursor:pointer" onclick="webx11.closeFileBrowser()">✕</span>
+ </div>
+ <div class="modal-body" id="fb-content"></div>
+ <div class="modal-footer">
+ <button class="btn" onclick="document.getElementById('file-upload-input').click()">⬆️ Upload</button>
+ <button class="btn" onclick="webx11.closeFileBrowser()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Module Manager Modal -->
+ <div id="module-manager" class="modal-overlay">
+ <div class="modal-content" style="max-width: 600px;">
+ <div class="modal-header">
+ <span>Module Manager</span>
+ <span style="cursor:pointer" onclick="webx11.closeModuleManager()">✕</span>
+ </div>
+ <div class="modal-body">
+ <div style="margin-bottom: 15px;">
+ <input type="text" id="module-url-input" class="module-url-input" placeholder="Enter module URL (https://example.com/module.js)">
+ <button class="btn" onclick="webx11.loadModuleFromURL()" style="width: 100%;">Load Module from URL</button>
+ </div>
+ <h4 style="color: var(--info-color); margin-bottom: 10px;">Loaded Modules</h4>
+ <div id="module-list"></div>
+ </div>
+ <div class="modal-footer">
+ <button class="btn btn-danger" onclick="webx11.clearAllModules()">Clear All</button>
+ <button class="btn" onclick="webx11.closeModuleManager()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Ollama Manager Modal -->
+ <div id="ollama-manager" class="modal-overlay">
+ <div class="modal-content" style="max-width: 600px;">
+ <div class="modal-header">
+ <span>Ollama AI Manager</span>
+ <span style="cursor:pointer" onclick="webx11.closeOllamaManager()">✕</span>
+ </div>
+ <div class="modal-body">
+ <div id="ollama-status" class="ollama-status disconnected">
+ <strong>Status:</strong> <span id="ollama-status-text">Disconnected</span>
+ </div>
+ <div style="margin-bottom: 15px;">
+ <label style="display: block; margin-bottom: 5px; color: var(--text-muted);">Ollama Endpoint:</label>
+ <input type="text" id="ollama-endpoint" class="module-url-input" value="http://localhost:11434" placeholder="http://localhost:11434">
+ <button class="btn" onclick="webx11.connectOllama()" style="width: 100%;">Connect</button>
+ </div>
+ <h4 style="color: var(--info-color); margin-bottom: 10px;">Available Models</h4>
+ <div id="ollama-models"></div>
+ <div style="margin-top: 15px;">
+ <label style="display: block; margin-bottom: 5px; color: var(--text-muted);">Current Model:</label>
+ <select id="ollama-current-model" class="module-url-input">
+ <option value="">Select a model</option>
+ </select>
+ <button class="btn" onclick="webx11.setOllamaModel()" style="width: 100%;">Set Model</button>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" onclick="webx11.closeOllamaManager()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Splash Screen -->
+ <div id="splash-overlay">
+ <span id="splash-gear" onclick="openStartupModulesSplash(event)" title="Select startup modules">⚙</span>
+ <div id="splash-title">⚡ WebX11</div>
+ <div id="splash-version">v4.2</div>
+ <div id="splash-progress-track"><div id="splash-progress-fill"></div></div>
+ <div id="splash-phase">Initializing...</div>
+ <div id="splash-filename"></div>
+ <div><span id="splash-cursor"></span></div>
+ <div id="splash-error"></div>
+ </div>
+
+ <!-- Skills Manager Modal -->
+ <div id="skills-manager" class="modal-overlay">
+ <div class="modal-content" style="max-width: 700px; max-height: 85vh;">
+ <div class="modal-header">
+ <span>📚 Skills Manager</span>
+ <span style="cursor:pointer" onclick="webx11.closeSkillsManager()">✕</span>
+ </div>
+ <div class="modal-body" style="display:flex; flex-direction:column; gap:10px;">
+ <div style="flex-shrink:0;">
+ <h4 style="color: var(--info-color); margin-bottom: 8px;">Installed Skills</h4>
+ <div id="skills-list"></div>
+ </div>
+ <div id="skill-preview" style="display:none; flex-shrink:0; background:var(--bg-tertiary); border:1px solid var(--border-color); border-radius:4px; padding:10px; max-height:250px; overflow-y:auto; white-space:pre-wrap; font-family:var(--font-primary); font-size:0.85em; color:var(--text-color);">
+ <div style="display:flex; justify-content:space-between; margin-bottom:6px;">
+ <span id="skill-preview-title" style="color:var(--success-color); font-weight:bold;"></span>
+ <span style="cursor:pointer; color:var(--text-muted); font-size:1.2em;" onclick="webx11.closeSkillPreview()">✕</span>
+ </div>
+ <div id="skill-preview-content"></div>
+ </div>
+ <div style="flex-shrink:0;">
+ <h4 style="color: var(--info-color); margin-bottom: 8px;">Create New Skill</h4>
+ <input type="text" id="skill-name-input" class="module-url-input" placeholder="Skill name (e.g. web-scrape-and-summarize)">
+ <input type="text" id="skill-desc-input" class="module-url-input" style="margin-top:6px" placeholder="One-line description">
+ <input type="text" id="skill-tags-input" class="module-url-input" style="margin-top:6px" placeholder="Tags (comma-separated, e.g. web,research)">
+ <textarea id="skill-content-input" class="module-url-input" style="margin-top:6px; height:100px; resize:vertical; font-family:var(--font-primary)" placeholder="Markdown skill content (optional — will use template)"></textarea>
+ <button class="btn" onclick="webx11.createSkillFromUI()" style="width:100%; margin-top:6px; border-color:var(--success-color);color:var(--success-color)">Create Skill</button>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" onclick="webx11.refreshSkillsList()">🔄 Refresh</button>
+ <button class="btn" onclick="webx11.closeSkillsManager()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Preview Modal -->
+ <div id="preview-modal" class="modal-overlay">
+ <div class="modal-content" style="max-width: 95%; max-height: 90vh; width: 95%;">
+ <div class="modal-header">
+ <span id="preview-title">📄 Preview</span>
+ <span style="cursor:pointer" onclick="webx11.closePreview()">✕</span>
+ </div>
+ <div class="modal-body" style="padding:0; overflow:hidden; height:75vh;">
+ <iframe id="preview-iframe" style="width:100%; height:100%; border:none; background:var(--bg-color);" sandbox="allow-same-origin allow-scripts"></iframe>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" onclick="webx11.closePreview()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Config Manager Modal -->
+ <div id="config-manager" class="modal-overlay">
+ <div class="modal-content" style="max-width: 550px; max-height: 80vh;">
+ <div class="modal-header">
+ <span>⚙ Configuration</span>
+ <span style="cursor:pointer" onclick="webx11.closeConfigManager()">✕</span>
+ </div>
+ <div class="modal-body" id="config-manager-body">
+ <div id="config-list" style="margin-bottom:16px;"></div>
+ <div style="border-top:1px solid var(--border-color); padding-top:12px;">
+ <h4 style="color:var(--info-color); margin-bottom:8px;">Add / Update Config</h4>
+ <div style="display:flex; gap:6px; flex-wrap:wrap;">
+ <input type="text" id="config-key-input" class="module-url-input" placeholder="Key" style="flex:1; min-width:100px;">
+ <input type="text" id="config-value-input" class="module-url-input" placeholder="Value" style="flex:3; min-width:150px;">
+ <button class="btn" onclick="webx11.addConfigFromUI()" style="border-color:var(--success-color);color:var(--success-color); padding:6px 14px;">Add</button>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <button class="btn" onclick="webx11.exportConfigFromUI()">📤 Export</button>
+ <button class="btn btn-danger" onclick="webx11.resetConfigFromUI()">🗑 Reset All</button>
+ <button class="btn" onclick="webx11.closeConfigManager()">Close</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Startup Modules Modal -->
+ <div id="startup-modules" class="modal-overlay">
+ <div class="modal-content" style="max-width: 500px;">
+ <div class="modal-header">
+ <span>⚙ Startup Modules</span>
+ <span style="cursor:pointer" onclick="var w=window.webx11;if(w&&typeof w.closeStartupModules==='function')w.closeStartupModules();else document.getElementById('startup-modules').style.display='none'">✕</span>
+ </div>
+ <div class="modal-body" id="startup-modules-body"></div>
+ <div class="modal-footer">
+ <button class="btn btn-danger" onclick="webx11.closeStartupModules()">Cancel</button>
+ <button class="btn" onclick="webx11.saveStartupModules()" style="border-color:var(--info-color);color:var(--info-color)">Save & Reload</button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Code Editor Modal -->
+ <div id="code-editor" class="modal-overlay">
+ <div class="modal-content" style="max-width: 90vw; max-height: 90vh; width: 900px; height: 80vh;">
+ <div class="modal-header">
+ <span id="code-editor-title">📝 Edit Module</span>
+ <span style="cursor:pointer" onclick="if(webx11) webx11.closeCodeEditor()">✕</span>
+ </div>
+ <div class="modal-body" style="padding:0; display:flex; flex-direction:column; height:calc(100% - 100px);">
+ <div id="code-editor-info" style="padding:6px 10px; font-size:0.8em; color:var(--text-muted); border-bottom:1px solid var(--border-color); flex-shrink:0;"></div>
+ <textarea id="code-editor-textarea" style="flex:1; width:100%; border:none; background:var(--bg-tertiary); color:var(--text-color); font-family:monospace; font-size:13px; padding:10px; resize:none; outline:none; tab-size:2; white-space:pre; overflow:auto;"></textarea>
+ </div>
+ <div class="modal-footer">
+ <button class="btn btn-danger" onclick="if(webx11) webx11.closeCodeEditor()">Cancel</button>
+ <button class="btn" onclick="(async()=>{if(webx11)await webx11.saveCodeEditor()})()" style="border-color:var(--info-color);color:var(--info-color)">💾 Save & Test</button>
+ </div>
+ </div>
+ </div>
+
+ <input type="file" id="file-upload-input" style="display:none">
+
+
+ <!-- Gear splash function — works before webx11 initializes -->
+ <script>
+ function openStartupModulesSplash(event) {
+ event.stopPropagation();
+ var w = window.webx11;
+ if (w && typeof w.openStartupModules === 'function') {
+ w.openStartupModules();
+ } else {
+ var overlay = document.getElementById('startup-modules');
+ if (!overlay) return;
+ // Bare-bones render for when webx11 isn't ready yet
+ var body = document.getElementById('startup-modules-body');
+ if (body && !body.children.length) {
+ var mods = [
+ ['Foundation (always loaded)', ['virtual-fs.js','module-manager.js','ollama-manager.js','terminal.js'], true],
+ ['Built-in modules', ['module-system.js','module-fs.js','module-modmgr.js','module-ollama.js','module-my.js','module-searxng.js','module-servermon.js','module-ollamamodels.js','module-config.js','module-simplegui.js','module-io.js','module-dynamicprompts.js','module-aiassist.js','module-aiasm.js','module-smartctx.js','module-duckduckgo.js','module-fetcher.js','module-skills.js','module-chatstudio.js'], false],
+ ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js'], false]
+ ];
+ var isSel = function(name) { return true; }; // default: all selected
+ mods.forEach(function(section) {
+ var h = document.createElement('div');
+ h.className = 'mod-section-header';
+ h.textContent = section[0];
+ body.appendChild(h);
+ section[1].forEach(function(name) {
+ var card = document.createElement('div');
+ card.className = 'mod-card' + (section[2] ? ' disabled' : '');
+ var label = document.createElement('label');
+ var cb = document.createElement('input');
+ cb.type = 'checkbox';
+ cb.checked = section[2] || isSel(name);
+ cb.disabled = section[2];
+ cb.dataset.name = name;
+ var nameSpan = document.createElement('span');
+ nameSpan.className = 'mod-name';
+ nameSpan.textContent = name;
+ var badgeSpan = document.createElement('span');
+ badgeSpan.className = 'mod-badge' + (section[2] ? ' foundation' : '');
+ badgeSpan.textContent = section[2] ? 'foundation' : 'built-in';
+ label.appendChild(cb);
+ label.appendChild(nameSpan);
+ card.appendChild(label);
+ card.appendChild(badgeSpan);
+ body.appendChild(card);
+ });
+ });
+ }
+ overlay.style.display = 'flex';
+ }
+ }
+ </script>
+
+ <!-- Async dependency-ordered module loader (replaces 26 synchronous script tags) -->
+ <script src="modules/core/module-loader.js?v=19"></script>
+
+</body>
+</html>