- Add desktop tools module with drive cleaner, Windows optimizer, and Brave browser installer - Add antivirus module with ClamAV integration and Windows Defender management - Add tools.html template for settings page integration - Add standalone editor.html for file editing - Re-export new types from desktop and security
519 lines
17 KiB
HTML
519 lines
17 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Editor - General Bots</title>
|
||
<link rel="stylesheet" href="css/app.css" />
|
||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
:root {
|
||
--bg-primary: #0f172a;
|
||
--bg-secondary: #1e293b;
|
||
--bg-tertiary: #334155;
|
||
--text-primary: #f1f5f9;
|
||
--text-secondary: #94a3b8;
|
||
--accent-color: #3b82f6;
|
||
--accent-hover: #2563eb;
|
||
--border-color: #475569;
|
||
--success: #22c55e;
|
||
--warning: #f59e0b;
|
||
--error: #ef4444;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||
Roboto, sans-serif;
|
||
background: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.editor-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
}
|
||
|
||
.editor-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 20px;
|
||
background: var(--bg-secondary);
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.editor-title {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.editor-title-icon {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.editor-title-text {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.editor-path {
|
||
font-size: 12px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.editor-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 20px;
|
||
background: var(--bg-secondary);
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.toolbar-group {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding-right: 12px;
|
||
border-right: 1px solid var(--border-color);
|
||
}
|
||
|
||
.toolbar-group:last-child {
|
||
border-right: none;
|
||
}
|
||
|
||
.btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 8px 14px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
background: var(--bg-tertiary);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.btn:hover {
|
||
background: var(--border-color);
|
||
}
|
||
|
||
.btn-primary {
|
||
background: var(--accent-color);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: var(--accent-hover);
|
||
}
|
||
|
||
.btn-small {
|
||
padding: 6px 10px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.editor-content {
|
||
flex: 1;
|
||
display: flex;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.editor-wrapper {
|
||
display: flex;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.line-numbers {
|
||
width: 50px;
|
||
background: var(--bg-secondary);
|
||
border-right: 1px solid var(--border-color);
|
||
padding: 16px 8px;
|
||
overflow: hidden;
|
||
text-align: right;
|
||
user-select: none;
|
||
font-family: "Consolas", monospace;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.text-editor {
|
||
flex: 1;
|
||
background: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
border: none;
|
||
padding: 16px;
|
||
font-family: "Consolas", monospace;
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
resize: none;
|
||
outline: none;
|
||
white-space: pre;
|
||
overflow: auto;
|
||
tab-size: 4;
|
||
}
|
||
|
||
.csv-editor {
|
||
flex: 1;
|
||
overflow: auto;
|
||
padding: 16px;
|
||
}
|
||
|
||
.csv-table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 13px;
|
||
}
|
||
|
||
.csv-table th,
|
||
.csv-table td {
|
||
border: 1px solid var(--border-color);
|
||
padding: 0;
|
||
min-width: 120px;
|
||
}
|
||
|
||
.csv-table th {
|
||
background: var(--bg-tertiary);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.csv-table .row-num {
|
||
width: 40px;
|
||
min-width: 40px;
|
||
background: var(--bg-secondary);
|
||
color: var(--text-secondary);
|
||
text-align: center;
|
||
padding: 8px 4px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.csv-input {
|
||
width: 100%;
|
||
background: transparent;
|
||
border: none;
|
||
color: var(--text-primary);
|
||
padding: 8px 12px;
|
||
font-size: 13px;
|
||
outline: none;
|
||
}
|
||
|
||
.csv-input:focus {
|
||
background: var(--bg-secondary);
|
||
}
|
||
|
||
.status-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 6px 16px;
|
||
background: var(--bg-secondary);
|
||
border-top: 1px solid var(--border-color);
|
||
font-size: 12px;
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.status-left,
|
||
.status-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.dirty-indicator {
|
||
width: 8px;
|
||
height: 8px;
|
||
background: var(--warning);
|
||
border-radius: 50%;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.htmx-indicator {
|
||
display: none;
|
||
}
|
||
|
||
.htmx-request .htmx-indicator {
|
||
display: inline-block;
|
||
}
|
||
|
||
.spinner {
|
||
width: 14px;
|
||
height: 14px;
|
||
border: 2px solid transparent;
|
||
border-top-color: currentColor;
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
to { transform: rotate(360deg); }
|
||
}
|
||
|
||
.notification {
|
||
position: fixed;
|
||
bottom: 60px;
|
||
right: 20px;
|
||
padding: 12px 20px;
|
||
background: var(--bg-tertiary);
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||
border-left: 4px solid var(--accent-color);
|
||
opacity: 0;
|
||
transform: translateX(100%);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.notification.show {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
}
|
||
|
||
.notification.success {
|
||
border-left-color: var(--success);
|
||
}
|
||
|
||
.notification.error {
|
||
border-left-color: var(--error);
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="editor-container">
|
||
<!-- Header -->
|
||
<div class="editor-header">
|
||
<div class="editor-title">
|
||
<span class="editor-title-icon">📝</span>
|
||
<div>
|
||
<span
|
||
class="editor-title-text"
|
||
id="editor-filename"
|
||
hx-get="/api/v1/editor/filename"
|
||
hx-trigger="load"
|
||
hx-swap="innerHTML"></div>
|
||
Untitled
|
||
</span>
|
||
<div
|
||
class="editor-path"
|
||
id="editor-filepath"
|
||
hx-get="/api/v1/editor/filepath"
|
||
hx-trigger="load"
|
||
hx-swap="innerHTML">
|
||
</div>
|
||
</div>
|
||
<span
|
||
class="dirty-indicator"
|
||
id="dirty-indicator"
|
||
style="display: none;"
|
||
title="Unsaved changes">
|
||
</span>
|
||
</div>
|
||
<div>
|
||
<a href="#drive"
|
||
class="btn btn-small"
|
||
hx-get="/api/drive/list"
|
||
hx-target="#main-content"
|
||
hx-push-url="true">
|
||
✕ Close
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toolbar -->
|
||
<div class="editor-toolbar">
|
||
<div class="toolbar-group">
|
||
<button
|
||
class="btn btn-primary btn-small"
|
||
hx-post="/api/v1/editor/save"
|
||
hx-include="#text-editor"
|
||
hx-indicator="#save-spinner"
|
||
hx-swap="none"
|
||
hx-on::after-request="showSaveNotification(event)">
|
||
<span class="htmx-indicator spinner" id="save-spinner"></span>
|
||
💾 Save
|
||
</button>
|
||
<button
|
||
class="btn btn-small"
|
||
hx-get="/api/v1/editor/save-as"
|
||
hx-target="#save-dialog"
|
||
hx-swap="innerHTML">
|
||
Save As
|
||
</button>
|
||
</div>
|
||
|
||
<div class="toolbar-group">
|
||
<button
|
||
class="btn btn-small"
|
||
hx-post="/api/v1/editor/undo"
|
||
hx-target="#editor-content"
|
||
hx-swap="innerHTML">
|
||
↩️ Undo
|
||
</button>
|
||
<button
|
||
class="btn btn-small"
|
||
hx-post="/api/v1/editor/redo"
|
||
hx-target="#editor-content"
|
||
hx-swap="innerHTML">
|
||
↪️ Redo
|
||
</button>
|
||
</div>
|
||
|
||
<div class="toolbar-group" id="text-tools">
|
||
<button
|
||
class="btn btn-small"
|
||
hx-post="/api/v1/editor/format"
|
||
hx-include="#text-editor"
|
||
hx-target="#text-editor"
|
||
hx-swap="innerHTML">
|
||
{ } Format
|
||
</button>
|
||
</div>
|
||
|
||
<div class="toolbar-group" id="csv-tools" style="display: none;">
|
||
<button
|
||
class="btn btn-small"
|
||
hx-post="/api/v1/editor/csv/add-row"
|
||
hx-target="#csv-table-body"
|
||
hx-swap="beforeend">
|
||
➕ Row
|
||
</button>
|
||
<button
|
||
class="btn btn-small"
|
||
hx-post="/api/v1/editor/csv/add-column"
|
||
hx-target="#csv-editor"
|
||
hx-swap="innerHTML">
|
||
➕ Column
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Editor Content - loaded via HTMX based on file type -->
|
||
<div class="editor-content" id="editor-content">
|
||
<!-- Text Editor (default) -->
|
||
<div class="editor-wrapper" id="text-editor-wrapper">
|
||
<div
|
||
class="line-numbers"
|
||
id="line-numbers"
|
||
hx-get="/api/v1/editor/line-numbers"
|
||
hx-trigger="keyup from:#text-editor delay:100ms"
|
||
hx-swap="innerHTML">
|
||
1
|
||
</div>
|
||
<textarea
|
||
class="text-editor"
|
||
id="text-editor"
|
||
name="content"
|
||
spellcheck="false"
|
||
hx-post="/api/v1/editor/autosave"
|
||
hx-trigger="keyup changed delay:5s"
|
||
hx-swap="none"
|
||
hx-indicator="#autosave-indicator"
|
||
placeholder="Start typing or open a file..."></textarea>
|
||
</div>
|
||
|
||
<!-- CSV Editor (shown for .csv files) -->
|
||
<div class="csv-editor" id="csv-editor" style="display: none;">
|
||
<table class="csv-table">
|
||
<thead id="csv-table-head">
|
||
<tr>
|
||
<th class="row-num">#</th>
|
||
<th>
|
||
<input
|
||
type="text"
|
||
class="csv-input"
|
||
name="header_0"
|
||
value="Column 1"
|
||
hx-post="/api/v1/editor/csv/update-header"
|
||
hx-trigger="change"
|
||
hx-swap="none">
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody
|
||
id="csv-table-body"
|
||
hx-get="/api/v1/editor/csv/rows"
|
||
hx-trigger="load"
|
||
hx-swap="innerHTML">
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Status Bar -->
|
||
<div class="status-bar">
|
||
<div class="status-left">
|
||
<span
|
||
id="file-type"
|
||
hx-get="/api/v1/editor/filetype"
|
||
hx-trigger="load"
|
||
hx-swap="innerHTML">
|
||
📄 Plain Text
|
||
</span>
|
||
<span>UTF-8</span>
|
||
<span
|
||
id="autosave-indicator"
|
||
class="htmx-indicator"
|
||
style="font-size: 11px;">
|
||
Saving...
|
||
</span>
|
||
</div>
|
||
<div class="status-right">
|
||
<span
|
||
id="cursor-position"
|
||
hx-get="/api/v1/editor/position"
|
||
hx-trigger="click from:#text-editor, keyup from:#text-editor"
|
||
hx-swap="innerHTML">
|
||
Ln 1, Col 1
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Save Dialog (loaded via HTMX) -->
|
||
<div id="save-dialog"></div>
|
||
|
||
<!-- Notification -->
|
||
<div class="notification" id="notification"></div>
|
||
|
||
<script>
|
||
// Minimal JS for notification display (could be replaced with htmx extension)
|
||
function showSaveNotification(event) {
|
||
const notification = document.getElementById('notification');
|
||
if (event.detail.successful) {
|
||
notification.textContent = '✓ File saved';
|
||
notification.className = 'notification success show';
|
||
document.getElementById('dirty-indicator').style.display = 'none';
|
||
} else {
|
||
notification.textContent = '✗ Save failed';
|
||
notification.className = 'notification error show';
|
||
}
|
||
setTimeout(() => notification.classList.remove('show'), 3000);
|
||
}
|
||
|
||
// Mark as dirty on edit
|
||
document.getElementById('text-editor')?.addEventListener('input', function() {
|
||
document.getElementById('dirty-indicator').style.display = 'inline-block';
|
||
});
|
||
|
||
// Keyboard shortcuts using htmx triggers
|
||
document.addEventListener('keydown', function(e) {
|
||
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
||
e.preventDefault();
|
||
htmx.trigger(document.querySelector('[hx-post="/api/v1/editor/save"]'), 'click');
|
||
}
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|