diff options
Diffstat (limited to 'index.html')
| -rw-r--r-- | index.html | 810 |
1 files changed, 810 insertions, 0 deletions
diff --git a/index.html b/index.html new file mode 100644 index 0000000..f1f315e --- /dev/null +++ b/index.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> |