kokokoasd commited on
Commit
70bd544
·
verified ·
1 Parent(s): daaa6ed

Upload 15 files

Browse files
Files changed (3) hide show
  1. static/app.js +33 -13
  2. static/index.html +7 -4
  3. static/style.css +64 -2
static/app.js CHANGED
@@ -75,18 +75,36 @@ function toast(message, type = "info") {
75
  // ── Zone Management ──────────────────────────
76
  async function loadZones() {
77
  const zones = await api("/api/zones");
 
 
78
  const list = document.getElementById("zone-list");
79
  if (zones.length === 0) {
80
  list.innerHTML = `<li class="empty-hint" style="color:var(--text-3);font-size:12px;padding:8px 10px;cursor:default;opacity:0.6">No zones yet</li>`;
81
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
- list.innerHTML = zones.map(z => `
84
- <li data-zone="${escapeAttr(z.name)}" class="${currentZone === z.name ? 'active' : ''}" onclick="openZone('${escapeAttr(z.name)}')">
85
- <span class="zone-icon"><i data-lucide="box"></i></span>
86
- <span class="zone-name">${escapeHtml(z.name)}</span>
87
- </li>
88
- `).join("");
89
- lucide.createIcons({ nodes: [list] });
90
  }
91
 
92
  async function openZone(name) {
@@ -100,7 +118,7 @@ async function openZone(name) {
100
 
101
  // Reset editor
102
  const editorContainer = document.getElementById("editor-container");
103
- editorContainer.innerHTML = `<div class="editor-empty"><i data-lucide="mouse-pointer-click"></i><p>${isMobile ? 'Tap a file to open' : 'Double-click a file to open'}</p></div>`;
104
  document.getElementById("editor-tabs").innerHTML = `<span class="tab-placeholder"><i data-lucide="code-2"></i> No file open</span>`;
105
  lucide.createIcons({ nodes: [editorContainer, document.getElementById("editor-tabs")] });
106
  cmEditor = null;
@@ -191,7 +209,7 @@ function renderFiles(files) {
191
  }
192
  let html = "";
193
  if (currentPath) {
194
- html += `<div class="file-item" ondblclick="navigateUp()" onclick="if(isMobile)navigateUp()">
195
  <span class="fi-icon fi-icon-back"><i data-lucide="corner-left-up"></i></span>
196
  <span class="fi-name">..</span>
197
  </div>`;
@@ -205,10 +223,9 @@ function renderFiles(files) {
205
  const iconClass = f.is_dir ? "fi-icon-folder" : fileIconClass(f.name);
206
  const iconName = f.is_dir ? "folder" : "file-text";
207
  const size = f.is_dir ? "" : formatSize(f.size);
208
- const dblAction = f.is_dir ? `navigateTo('${escapeAttr(relPath)}')` : `editFile('${escapeAttr(relPath)}')`;
209
- const tapAction = f.is_dir ? `navigateTo('${escapeAttr(relPath)}')` : `editFile('${escapeAttr(relPath)}')`;
210
 
211
- html += `<div class="file-item" ondblclick="${dblAction}" onclick="if(isMobile){${tapAction}}">
212
  <span class="fi-icon ${iconClass}"><i data-lucide="${iconName}"></i></span>
213
  <span class="fi-name">${escapeHtml(f.name)}</span>
214
  <span class="fi-size">${size}</span>
@@ -724,12 +741,15 @@ function fileIconClass(name) {
724
  function toggleSidebar(open) {
725
  const sidebar = document.getElementById("sidebar");
726
  const backdrop = document.getElementById("sidebar-backdrop");
 
727
  if (open) {
728
  sidebar.classList.add("open");
729
  backdrop.classList.add("open");
 
730
  } else {
731
  sidebar.classList.remove("open");
732
  backdrop.classList.remove("open");
 
733
  }
734
  }
735
 
 
75
  // ── Zone Management ──────────────────────────
76
  async function loadZones() {
77
  const zones = await api("/api/zones");
78
+
79
+ // Sidebar zone list
80
  const list = document.getElementById("zone-list");
81
  if (zones.length === 0) {
82
  list.innerHTML = `<li class="empty-hint" style="color:var(--text-3);font-size:12px;padding:8px 10px;cursor:default;opacity:0.6">No zones yet</li>`;
83
+ } else {
84
+ list.innerHTML = zones.map(z => `
85
+ <li data-zone="${escapeAttr(z.name)}" class="${currentZone === z.name ? 'active' : ''}" onclick="openZone('${escapeAttr(z.name)}')">
86
+ <span class="zone-icon"><i data-lucide="box"></i></span>
87
+ <span class="zone-name">${escapeHtml(z.name)}</span>
88
+ </li>
89
+ `).join("");
90
+ lucide.createIcons({ nodes: [list] });
91
+ }
92
+
93
+ // Welcome page zone cards (so users can pick zone without sidebar)
94
+ const welcomeZones = document.getElementById("welcome-zones");
95
+ if (welcomeZones) {
96
+ if (zones.length === 0) {
97
+ welcomeZones.innerHTML = `<p class="welcome-hint">No zones yet — create one to get started</p>`;
98
+ } else {
99
+ welcomeZones.innerHTML = `<div class="zone-grid">${zones.map(z => `
100
+ <button class="zone-card" onclick="openZone('${escapeAttr(z.name)}')">
101
+ <i data-lucide="box"></i>
102
+ <span>${escapeHtml(z.name)}</span>
103
+ </button>
104
+ `).join("")}</div>`;
105
+ lucide.createIcons({ nodes: [welcomeZones] });
106
+ }
107
  }
 
 
 
 
 
 
 
108
  }
109
 
110
  async function openZone(name) {
 
118
 
119
  // Reset editor
120
  const editorContainer = document.getElementById("editor-container");
121
+ editorContainer.innerHTML = `<div class="editor-empty"><i data-lucide="mouse-pointer-click"></i><p>Tap a file to open</p></div>`;
122
  document.getElementById("editor-tabs").innerHTML = `<span class="tab-placeholder"><i data-lucide="code-2"></i> No file open</span>`;
123
  lucide.createIcons({ nodes: [editorContainer, document.getElementById("editor-tabs")] });
124
  cmEditor = null;
 
209
  }
210
  let html = "";
211
  if (currentPath) {
212
+ html += `<div class="file-item" onclick="navigateUp()">
213
  <span class="fi-icon fi-icon-back"><i data-lucide="corner-left-up"></i></span>
214
  <span class="fi-name">..</span>
215
  </div>`;
 
223
  const iconClass = f.is_dir ? "fi-icon-folder" : fileIconClass(f.name);
224
  const iconName = f.is_dir ? "folder" : "file-text";
225
  const size = f.is_dir ? "" : formatSize(f.size);
226
+ const action = f.is_dir ? `navigateTo('${escapeAttr(relPath)}')` : `editFile('${escapeAttr(relPath)}')`;
 
227
 
228
+ html += `<div class="file-item" onclick="${action}">
229
  <span class="fi-icon ${iconClass}"><i data-lucide="${iconName}"></i></span>
230
  <span class="fi-name">${escapeHtml(f.name)}</span>
231
  <span class="fi-size">${size}</span>
 
741
  function toggleSidebar(open) {
742
  const sidebar = document.getElementById("sidebar");
743
  const backdrop = document.getElementById("sidebar-backdrop");
744
+ const hamburger = document.getElementById("btn-hamburger");
745
  if (open) {
746
  sidebar.classList.add("open");
747
  backdrop.classList.add("open");
748
+ if (hamburger) hamburger.style.display = "none";
749
  } else {
750
  sidebar.classList.remove("open");
751
  backdrop.classList.remove("open");
752
+ if (hamburger && isMobile) hamburger.style.display = "";
753
  }
754
  }
755
 
static/index.html CHANGED
@@ -72,6 +72,11 @@
72
 
73
  <!-- Main -->
74
  <main id="main">
 
 
 
 
 
75
  <!-- Welcome -->
76
  <div id="welcome" class="view active">
77
  <div class="welcome-hero">
@@ -79,6 +84,7 @@
79
  <div class="welcome-icon"><i data-lucide="server"></i></div>
80
  <h1>HugPanel</h1>
81
  <p>Multi-zone workspace with file manager, code editor &amp; terminal.</p>
 
82
  <button class="btn btn-accent btn-lg" onclick="document.getElementById('btn-add-zone').click()">
83
  <i data-lucide="plus"></i> Create Zone
84
  </button>
@@ -89,9 +95,6 @@
89
  <div id="workspace" class="view">
90
  <div class="topbar">
91
  <div class="topbar-left">
92
- <button id="btn-hamburger" class="icon-btn-sm hamburger-btn" onclick="toggleSidebar(true)" title="Menu">
93
- <i data-lucide="menu"></i>
94
- </button>
95
  <span class="zone-indicator" id="zone-badge">
96
  <i data-lucide="box"></i>
97
  <span id="zone-title"></span>
@@ -145,7 +148,7 @@
145
  <div id="editor-container" class="editor-container">
146
  <div class="editor-empty">
147
  <i data-lucide="mouse-pointer-click"></i>
148
- <p>Double-click a file to open</p>
149
  </div>
150
  </div>
151
  </div>
 
72
 
73
  <!-- Main -->
74
  <main id="main">
75
+ <!-- Global hamburger (visible on mobile for both views) -->
76
+ <button id="btn-hamburger" class="icon-btn-sm hamburger-global" onclick="toggleSidebar(true)" title="Menu">
77
+ <i data-lucide="menu"></i>
78
+ </button>
79
+
80
  <!-- Welcome -->
81
  <div id="welcome" class="view active">
82
  <div class="welcome-hero">
 
84
  <div class="welcome-icon"><i data-lucide="server"></i></div>
85
  <h1>HugPanel</h1>
86
  <p>Multi-zone workspace with file manager, code editor &amp; terminal.</p>
87
+ <div id="welcome-zones" class="welcome-zones"></div>
88
  <button class="btn btn-accent btn-lg" onclick="document.getElementById('btn-add-zone').click()">
89
  <i data-lucide="plus"></i> Create Zone
90
  </button>
 
95
  <div id="workspace" class="view">
96
  <div class="topbar">
97
  <div class="topbar-left">
 
 
 
98
  <span class="zone-indicator" id="zone-badge">
99
  <i data-lucide="box"></i>
100
  <span id="zone-title"></span>
 
148
  <div id="editor-container" class="editor-container">
149
  <div class="editor-empty">
150
  <i data-lucide="mouse-pointer-click"></i>
151
+ <p>Tap a file to open</p>
152
  </div>
153
  </div>
154
  </div>
static/style.css CHANGED
@@ -284,6 +284,64 @@ body {
284
  text-align: center;
285
  }
286
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  /* ── Topbar ────────────────── */
288
  .topbar {
289
  height: var(--topbar-h);
@@ -399,6 +457,7 @@ body {
399
  }
400
 
401
  .file-item:hover { background: var(--bg-hover); }
 
402
 
403
  .file-item .fi-icon {
404
  width: 16px; height: 16px;
@@ -845,7 +904,6 @@ body {
845
  }
846
 
847
  .sidebar-close-btn { display: none; }
848
- .hamburger-btn { display: none; }
849
  .mobile-only { display: none !important; }
850
  .mobile-tabs { display: none; }
851
 
@@ -886,7 +944,7 @@ body {
886
  margin-left: auto;
887
  }
888
 
889
- .hamburger-btn { display: inline-flex; }
890
 
891
  /* Sidebar brand spacing */
892
  .sidebar-brand { padding: 12px 14px 10px; }
@@ -901,6 +959,10 @@ body {
901
 
902
  .zone-list li .zone-icon { width: 20px; height: 20px; }
903
 
 
 
 
 
904
  /* Main takes full width */
905
  #main { width: 100%; }
906
 
 
284
  text-align: center;
285
  }
286
 
287
+ /* ── Welcome Zone Grid ─────── */
288
+ .welcome-zones {
289
+ max-width: 480px;
290
+ width: 100%;
291
+ padding: 0 16px;
292
+ }
293
+
294
+ .welcome-hint {
295
+ color: var(--text-3);
296
+ font-size: 13px;
297
+ text-align: center;
298
+ }
299
+
300
+ .zone-grid {
301
+ display: flex;
302
+ flex-wrap: wrap;
303
+ gap: 8px;
304
+ justify-content: center;
305
+ }
306
+
307
+ .zone-card {
308
+ display: flex;
309
+ align-items: center;
310
+ gap: 8px;
311
+ padding: 10px 18px;
312
+ background: var(--bg-2);
313
+ border: 1px solid var(--border);
314
+ border-radius: var(--radius);
315
+ color: var(--text);
316
+ font-size: 14px;
317
+ font-weight: 500;
318
+ font-family: var(--font);
319
+ cursor: pointer;
320
+ transition: all var(--transition);
321
+ -webkit-tap-highlight-color: transparent;
322
+ min-height: 44px;
323
+ }
324
+
325
+ .zone-card svg { width: 16px; height: 16px; color: var(--accent); flex-shrink: 0; }
326
+ .zone-card:hover { background: var(--bg-hover); border-color: var(--accent); }
327
+ .zone-card:active { background: var(--bg-active); transform: scale(0.97); }
328
+
329
+ /* ── Global Hamburger ──────── */
330
+ .hamburger-global {
331
+ display: none;
332
+ position: fixed;
333
+ top: 10px;
334
+ left: 10px;
335
+ z-index: 40;
336
+ width: 40px;
337
+ height: 40px;
338
+ background: var(--bg-2);
339
+ border: 1px solid var(--border);
340
+ border-radius: var(--radius);
341
+ }
342
+
343
+ .hamburger-global svg { width: 20px; height: 20px; }
344
+
345
  /* ── Topbar ────────────────── */
346
  .topbar {
347
  height: var(--topbar-h);
 
457
  }
458
 
459
  .file-item:hover { background: var(--bg-hover); }
460
+ .file-item:active { background: var(--bg-active); }
461
 
462
  .file-item .fi-icon {
463
  width: 16px; height: 16px;
 
904
  }
905
 
906
  .sidebar-close-btn { display: none; }
 
907
  .mobile-only { display: none !important; }
908
  .mobile-tabs { display: none; }
909
 
 
944
  margin-left: auto;
945
  }
946
 
947
+ .hamburger-global { display: inline-flex; }
948
 
949
  /* Sidebar brand spacing */
950
  .sidebar-brand { padding: 12px 14px 10px; }
 
959
 
960
  .zone-list li .zone-icon { width: 20px; height: 20px; }
961
 
962
+ /* Welcome zones on mobile */
963
+ .welcome-zones { max-width: 100%; padding: 0 12px; }
964
+ .zone-card { flex: 1; min-width: 120px; justify-content: center; }
965
+
966
  /* Main takes full width */
967
  #main { width: 100%; }
968