bosun 0.28.4 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/ui/app.js +111 -141
- package/ui/components/command-palette.js +17 -139
- package/ui/components/diff-viewer.js +32 -32
- package/ui/components/forms.js +216 -98
- package/ui/components/session-list.js +40 -41
- package/ui/components/shared.js +86 -86
- package/ui/components/workspace-switcher.js +69 -66
- package/ui/index.html +91 -0
- package/ui/styles/components.css +323 -3414
- package/ui/styles/layout.css +5 -488
- package/ui/styles.css +0 -225
- package/ui/tabs/agents.js +533 -504
- package/ui/tabs/chat.js +16 -16
- package/ui/tabs/control.js +76 -74
- package/ui/tabs/dashboard.js +113 -107
- package/ui/tabs/infra.js +76 -67
- package/ui/tabs/logs.js +71 -62
- package/ui/tabs/settings.js +26 -69
- package/ui/tabs/tasks.js +56 -53
- package/ui/tabs/telemetry.js +30 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosun",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "AI-powered orchestrator supervisor — manages AI agent executors with failover, auto-restarts on failure, analyzes crashes with Codex SDK, creates PRs via Vibe-Kanban API, and sends Telegram notifications. Supports N executors with weighted distribution, multi-repo projects, and auto-setup.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache 2.0",
|
package/ui/app.js
CHANGED
|
@@ -180,15 +180,15 @@ function useBackendHealth() {
|
|
|
180
180
|
function OfflineBanner() {
|
|
181
181
|
const { retry: manualRetry } = useBackendHealth();
|
|
182
182
|
return html`
|
|
183
|
-
<div class="offline-banner">
|
|
184
|
-
<
|
|
185
|
-
<div class="
|
|
186
|
-
<div class="
|
|
187
|
-
<div class="
|
|
183
|
+
<div class="offline-banner alert alert-error shadow-sm mx-4 my-2">
|
|
184
|
+
<span class="text-lg">⚠️</span>
|
|
185
|
+
<div class="flex-1">
|
|
186
|
+
<div class="font-semibold text-sm">Backend Unreachable</div>
|
|
187
|
+
<div class="text-xs opacity-70">${backendError.value || "Connection lost"}</div>
|
|
188
188
|
${backendLastSeen.value
|
|
189
|
-
? html`<div class="
|
|
189
|
+
? html`<div class="text-xs opacity-70">Last connected: ${formatTimeAgo(backendLastSeen.value)}</div>`
|
|
190
190
|
: null}
|
|
191
|
-
<div class="
|
|
191
|
+
<div class="text-xs opacity-70">Retry attempt #${backendRetryCount.value}</div>
|
|
192
192
|
</div>
|
|
193
193
|
<button class="btn btn-ghost btn-sm" onClick=${manualRetry}>Retry</button>
|
|
194
194
|
</div>
|
|
@@ -253,27 +253,19 @@ function Header() {
|
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
return html`
|
|
256
|
-
<header class="app-header">
|
|
257
|
-
<div class="
|
|
258
|
-
|
|
259
|
-
<${WorkspaceSwitcher} />
|
|
260
|
-
</div>
|
|
256
|
+
<header class="app-header navbar bg-base-200/80 backdrop-blur-sm sticky top-0 z-30 min-h-0 px-4 py-2 border-b border-base-content/5">
|
|
257
|
+
<div class="navbar-start">
|
|
258
|
+
<${WorkspaceSwitcher} />
|
|
261
259
|
</div>
|
|
262
|
-
<div class="
|
|
263
|
-
<div class="
|
|
264
|
-
<div class="
|
|
265
|
-
<
|
|
266
|
-
|
|
267
|
-
${connLabel}
|
|
268
|
-
</div>
|
|
269
|
-
${freshnessLabel
|
|
270
|
-
? html`<div class="header-freshness">${freshnessLabel}</div>`
|
|
271
|
-
: null}
|
|
260
|
+
<div class="navbar-end gap-2">
|
|
261
|
+
<div class="flex items-center gap-2">
|
|
262
|
+
<div class="badge ${connClass === 'connected' ? 'badge-success' : connClass === 'reconnecting' ? 'badge-warning' : 'badge-error'} badge-sm gap-1">
|
|
263
|
+
<span class="w-1.5 h-1.5 rounded-full ${connClass === 'connected' ? 'bg-success-content' : connClass === 'reconnecting' ? 'bg-warning-content' : 'bg-error-content'}"></span>
|
|
264
|
+
${connLabel}
|
|
272
265
|
</div>
|
|
273
|
-
${
|
|
274
|
-
? html`<div class="app-header-user">@${user.username || user.first_name}</div>`
|
|
275
|
-
: null}
|
|
266
|
+
${freshnessLabel ? html`<span class="text-xs opacity-50">${freshnessLabel}</span>` : null}
|
|
276
267
|
</div>
|
|
268
|
+
${user ? html`<div class="text-xs opacity-60">@${user.username || user.first_name}</div>` : null}
|
|
277
269
|
</div>
|
|
278
270
|
</header>
|
|
279
271
|
`;
|
|
@@ -286,51 +278,49 @@ function SidebarNav() {
|
|
|
286
278
|
const user = getTelegramUser();
|
|
287
279
|
const isConn = connected.value;
|
|
288
280
|
return html`
|
|
289
|
-
<aside class="sidebar">
|
|
290
|
-
<div class="
|
|
291
|
-
<
|
|
292
|
-
|
|
293
|
-
</div>
|
|
294
|
-
<div class="sidebar-title">Bosun</div>
|
|
281
|
+
<aside class="sidebar flex flex-col bg-base-200 border-r border-base-content/5 h-full w-[var(--sidebar-width)]">
|
|
282
|
+
<div class="p-3 flex items-center gap-2">
|
|
283
|
+
<img src="logo.png" alt="Bosun" class="w-8 h-8 rounded" />
|
|
284
|
+
<span class="font-semibold text-sm">Bosun</span>
|
|
295
285
|
</div>
|
|
296
|
-
<div class="
|
|
297
|
-
<button class="btn btn-primary btn-
|
|
298
|
-
|
|
286
|
+
<div class="px-3 flex flex-col gap-1.5 mb-3">
|
|
287
|
+
<button class="btn btn-primary btn-sm w-full gap-1" onClick=${() => createSession({ type: "primary" })}>
|
|
288
|
+
✨ New Session
|
|
299
289
|
</button>
|
|
300
|
-
<button class="btn btn-ghost btn-
|
|
301
|
-
|
|
290
|
+
<button class="btn btn-ghost btn-sm w-full gap-1" onClick=${() => navigateTo("tasks")}>
|
|
291
|
+
📋 View Tasks
|
|
302
292
|
</button>
|
|
303
293
|
</div>
|
|
304
|
-
<
|
|
294
|
+
<ul class="menu menu-sm flex-1 px-2 gap-0.5">
|
|
305
295
|
${TAB_CONFIG.map((tab) => {
|
|
306
296
|
const isActive = activeTab.value === tab.id;
|
|
307
297
|
const isHome = tab.id === "dashboard";
|
|
308
298
|
return html`
|
|
309
|
-
<
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
navigateTo(tab.id, {
|
|
299
|
+
<li key=${tab.id}>
|
|
300
|
+
<a
|
|
301
|
+
class=${isActive ? "active font-medium" : ""}
|
|
302
|
+
aria-label=${tab.label}
|
|
303
|
+
aria-current=${isActive ? "page" : null}
|
|
304
|
+
onClick=${() => navigateTo(tab.id, {
|
|
316
305
|
resetHistory: isHome,
|
|
317
306
|
forceRefresh: isHome && isActive,
|
|
318
307
|
})}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
308
|
+
>
|
|
309
|
+
${ICONS[tab.icon]}
|
|
310
|
+
<span>${tab.label}</span>
|
|
311
|
+
</a>
|
|
312
|
+
</li>
|
|
323
313
|
`;
|
|
324
314
|
})}
|
|
325
|
-
</
|
|
326
|
-
<div class="
|
|
327
|
-
<div class="
|
|
328
|
-
<span class="
|
|
329
|
-
|
|
315
|
+
</ul>
|
|
316
|
+
<div class="p-3 border-t border-base-content/5">
|
|
317
|
+
<div class="flex items-center gap-2 text-xs">
|
|
318
|
+
<span class="w-2 h-2 rounded-full ${isConn ? "bg-success" : "bg-error"}"></span>
|
|
319
|
+
<span class="opacity-70">${isConn ? "Connected" : "Offline"}</span>
|
|
330
320
|
</div>
|
|
331
321
|
${user
|
|
332
|
-
? html`<div class="
|
|
333
|
-
: html`<div class="
|
|
322
|
+
? html`<div class="text-xs opacity-50 mt-1 truncate">@${user.username || user.first_name || "operator"}</div>`
|
|
323
|
+
: html`<div class="text-xs opacity-50 mt-1">Operator Console</div>`}
|
|
334
324
|
</div>
|
|
335
325
|
</aside>
|
|
336
326
|
`;
|
|
@@ -364,29 +354,21 @@ function SessionRail({ onResizeStart, onResizeReset, showResizer }) {
|
|
|
364
354
|
}, [sessionsData.value, selectedSessionId.value]);
|
|
365
355
|
|
|
366
356
|
return html`
|
|
367
|
-
<aside class="session-rail">
|
|
368
|
-
<div class="
|
|
369
|
-
<div class="
|
|
370
|
-
<div class="
|
|
371
|
-
${activeCount} active · ${sessions.length} total
|
|
372
|
-
</div>
|
|
357
|
+
<aside class="session-rail flex flex-col bg-base-200 border-r border-base-content/5 overflow-hidden" style="width: var(--rail-width, 300px)">
|
|
358
|
+
<div class="p-3 border-b border-base-content/5">
|
|
359
|
+
<div class="font-medium text-sm">Sessions</div>
|
|
360
|
+
<div class="text-xs opacity-50 mt-0.5">${activeCount} active · ${sessions.length} total</div>
|
|
373
361
|
</div>
|
|
374
|
-
|
|
375
|
-
showArchived=${showArchived}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
aria-label="Resize sessions panel"
|
|
385
|
-
onPointerDown=${(e) => onResizeStart("rail", e)}
|
|
386
|
-
onDoubleClick=${() => onResizeReset("rail")}
|
|
387
|
-
></div>
|
|
388
|
-
`
|
|
389
|
-
: null}
|
|
362
|
+
<div class="flex-1 overflow-y-auto">
|
|
363
|
+
<${SessionList} showArchived=${showArchived} onToggleArchived=${setShowArchived} defaultType="primary" />
|
|
364
|
+
</div>
|
|
365
|
+
${showResizer ? html`
|
|
366
|
+
<div class="w-1 cursor-col-resize hover:bg-primary/30 active:bg-primary/50 transition-colors absolute right-0 top-0 bottom-0"
|
|
367
|
+
role="separator" aria-label="Resize sessions panel"
|
|
368
|
+
onPointerDown=${(e) => onResizeStart("rail", e)}
|
|
369
|
+
onDoubleClick=${() => onResizeReset("rail")}
|
|
370
|
+
></div>
|
|
371
|
+
` : null}
|
|
390
372
|
</aside>
|
|
391
373
|
`;
|
|
392
374
|
}
|
|
@@ -466,32 +448,34 @@ function InspectorPanel({ onResizeStart, onResizeReset, showResizer }) {
|
|
|
466
448
|
}, [isSessionTab, sessionId, session?.taskId, session?.branch, status]);
|
|
467
449
|
|
|
468
450
|
return html`
|
|
469
|
-
<aside class="inspector">
|
|
470
|
-
<div class="inspector-section">
|
|
471
|
-
<div class="inspector-title">Focus</div>
|
|
451
|
+
<aside class="inspector flex flex-col bg-base-200 border-l border-base-content/5 overflow-y-auto" style="width: var(--inspector-width, 300px)">
|
|
452
|
+
<div class="inspector-section p-3 border-b border-base-content/5">
|
|
453
|
+
<div class="inspector-title font-medium text-sm mb-2">Focus</div>
|
|
472
454
|
${session
|
|
473
455
|
? html`
|
|
474
|
-
<div class="
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
456
|
+
<div class="flex flex-col gap-1">
|
|
457
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Session</span><strong class="truncate ml-2">${session.title || session.taskId || session.id}</strong></div>
|
|
458
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Status</span><strong>${status}</strong></div>
|
|
459
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Type</span><strong>${type}</strong></div>
|
|
460
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Last Active</span><strong>${lastActive ? formatRelative(lastActive) : "—"}</strong></div>
|
|
461
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Preview</span><strong>${preview}</strong></div>
|
|
462
|
+
</div>
|
|
479
463
|
`
|
|
480
|
-
: html`<div class="inspector-empty">Select a session to see context.</div>`}
|
|
464
|
+
: html`<div class="inspector-empty text-xs opacity-40">Select a session to see context.</div>`}
|
|
481
465
|
</div>
|
|
482
466
|
|
|
483
467
|
${isSessionTab
|
|
484
468
|
? html`
|
|
485
|
-
<div class="inspector-section inspector-scroll">
|
|
486
|
-
<div class="inspector-title">Latest Diff</div>
|
|
469
|
+
<div class="inspector-section inspector-scroll p-3 border-b border-base-content/5">
|
|
470
|
+
<div class="inspector-title font-medium text-sm mb-2">Latest Diff</div>
|
|
487
471
|
<${DiffViewer} sessionId=${sessionId} />
|
|
488
472
|
</div>
|
|
489
|
-
<div class="inspector-section">
|
|
490
|
-
<div class="inspector-title">Smart Logs</div>
|
|
473
|
+
<div class="inspector-section p-3">
|
|
474
|
+
<div class="inspector-title font-medium text-sm mb-2">Smart Logs</div>
|
|
491
475
|
${logState === "error"
|
|
492
|
-
? html`<div class="inspector-empty">Log stream unavailable.</div>`
|
|
476
|
+
? html`<div class="inspector-empty text-xs opacity-40">Log stream unavailable.</div>`
|
|
493
477
|
: smartLogs.length === 0
|
|
494
|
-
? html`<div class="inspector-empty">No noteworthy logs right now.</div>`
|
|
478
|
+
? html`<div class="inspector-empty text-xs opacity-40">No noteworthy logs right now.</div>`
|
|
495
479
|
: html`
|
|
496
480
|
<div class="inspector-scroll">
|
|
497
481
|
${smartLogs.map(
|
|
@@ -514,17 +498,17 @@ function InspectorPanel({ onResizeStart, onResizeReset, showResizer }) {
|
|
|
514
498
|
</div>
|
|
515
499
|
`
|
|
516
500
|
: html`
|
|
517
|
-
<div class="inspector-section">
|
|
518
|
-
<div class="inspector-title">System Pulse</div>
|
|
519
|
-
<div class="inspector-kv"><span>API</span><strong>${connected.value ? "Connected" : "Offline"}</strong></div>
|
|
520
|
-
<div class="inspector-kv"><span>WebSocket</span><strong>${wsConnected.value ? "Live" : "Closed"}</strong></div>
|
|
521
|
-
<div class="inspector-kv"><span>Last Seen</span><strong>${backendLastSeen.value ? formatRelative(backendLastSeen.value) : "—"}</strong></div>
|
|
501
|
+
<div class="inspector-section p-3">
|
|
502
|
+
<div class="inspector-title font-medium text-sm mb-2">System Pulse</div>
|
|
503
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">API</span><strong>${connected.value ? "Connected" : "Offline"}</strong></div>
|
|
504
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">WebSocket</span><strong>${wsConnected.value ? "Live" : "Closed"}</strong></div>
|
|
505
|
+
<div class="inspector-kv flex justify-between text-xs"><span class="opacity-50">Last Seen</span><strong>${backendLastSeen.value ? formatRelative(backendLastSeen.value) : "—"}</strong></div>
|
|
522
506
|
</div>
|
|
523
507
|
`}
|
|
524
508
|
${showResizer
|
|
525
509
|
? html`
|
|
526
510
|
<div
|
|
527
|
-
class="inspector-resizer"
|
|
511
|
+
class="inspector-resizer w-1 cursor-col-resize hover:bg-primary/30 active:bg-primary/50 transition-colors absolute left-0 top-0 bottom-0"
|
|
528
512
|
role="separator"
|
|
529
513
|
aria-label="Resize inspector panel"
|
|
530
514
|
onPointerDown=${(e) => onResizeStart("inspector", e)}
|
|
@@ -551,37 +535,23 @@ function getTabsById(ids) {
|
|
|
551
535
|
function BottomNav({ compact, moreOpen, onToggleMore, onNavigate }) {
|
|
552
536
|
const primaryTabs = getTabsById(PRIMARY_NAV_TABS);
|
|
553
537
|
return html`
|
|
554
|
-
<nav class
|
|
538
|
+
<nav class="btm-nav btm-nav-sm bg-base-200 border-t border-base-content/5 z-40">
|
|
555
539
|
${primaryTabs.map((tab) => {
|
|
556
540
|
const isHome = tab.id === "dashboard";
|
|
557
541
|
const isActive = activeTab.value === tab.id;
|
|
558
542
|
return html`
|
|
559
|
-
<button
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
aria-label=${`Go to ${tab.label}`}
|
|
563
|
-
type="button"
|
|
564
|
-
onClick=${() =>
|
|
565
|
-
onNavigate(tab.id, {
|
|
566
|
-
resetHistory: isHome,
|
|
567
|
-
forceRefresh: isHome && isActive,
|
|
568
|
-
})}
|
|
569
|
-
>
|
|
543
|
+
<button key=${tab.id}
|
|
544
|
+
class=${isActive ? "active text-primary" : ""}
|
|
545
|
+
onClick=${() => onNavigate(tab.id, { resetHistory: isHome, forceRefresh: isHome && isActive })}>
|
|
570
546
|
${ICONS[tab.icon]}
|
|
571
|
-
<span class="nav-label">${tab.label}</span>
|
|
547
|
+
<span class="btm-nav-label text-xs">${tab.label}</span>
|
|
572
548
|
</button>
|
|
573
549
|
`;
|
|
574
550
|
})}
|
|
575
|
-
<button
|
|
576
|
-
|
|
577
|
-
aria-haspopup="dialog"
|
|
578
|
-
aria-expanded=${moreOpen ? "true" : "false"}
|
|
579
|
-
aria-label=${moreOpen ? "Close more menu" : "Open more menu"}
|
|
580
|
-
type="button"
|
|
581
|
-
onClick=${onToggleMore}
|
|
582
|
-
>
|
|
551
|
+
<button class=${moreOpen ? "active text-primary" : ""}
|
|
552
|
+
onClick=${onToggleMore}>
|
|
583
553
|
${ICONS.ellipsis}
|
|
584
|
-
<span class="nav-label">More</span>
|
|
554
|
+
<span class="btm-nav-label text-xs">More</span>
|
|
585
555
|
</button>
|
|
586
556
|
</nav>
|
|
587
557
|
`;
|
|
@@ -593,47 +563,47 @@ function MoreSheet({ open, onClose, onNavigate }) {
|
|
|
593
563
|
return html`
|
|
594
564
|
<${Modal} title="More" open=${open} onClose=${onClose}>
|
|
595
565
|
<div class="more-menu" role="navigation" aria-label="More menu">
|
|
596
|
-
<div class="more-menu-section">
|
|
597
|
-
<div class="more-menu-section-title">Quick Access</div>
|
|
598
|
-
<div class="
|
|
566
|
+
<div class="more-menu-section mb-4">
|
|
567
|
+
<div class="more-menu-section-title text-xs font-semibold opacity-50 mb-2">Quick Access</div>
|
|
568
|
+
<div class="grid grid-cols-4 gap-2 p-2">
|
|
599
569
|
${primaryTabs.map((tab) => {
|
|
600
570
|
const isHome = tab.id === "dashboard";
|
|
601
571
|
const isActive = activeTab.value === tab.id;
|
|
602
572
|
return html`
|
|
603
573
|
<button
|
|
604
574
|
key=${tab.id}
|
|
605
|
-
class="
|
|
575
|
+
class="btn btn-ghost btn-sm flex flex-col items-center gap-1 h-auto py-2 ${isActive ? "btn-active" : ""}"
|
|
606
576
|
aria-label=${`Open ${tab.label}`}
|
|
607
577
|
onClick=${() =>
|
|
608
578
|
onNavigate(tab.id, {
|
|
609
579
|
resetHistory: isHome,
|
|
610
580
|
})}
|
|
611
581
|
>
|
|
612
|
-
<span class="
|
|
613
|
-
<span class="
|
|
582
|
+
<span class="text-lg">${ICONS[tab.icon]}</span>
|
|
583
|
+
<span class="text-xs">${tab.label}</span>
|
|
614
584
|
</button>
|
|
615
585
|
`;
|
|
616
586
|
})}
|
|
617
587
|
</div>
|
|
618
588
|
</div>
|
|
619
589
|
<div class="more-menu-section">
|
|
620
|
-
<div class="more-menu-section-title">Explore</div>
|
|
621
|
-
<div class="
|
|
590
|
+
<div class="more-menu-section-title text-xs font-semibold opacity-50 mb-2">Explore</div>
|
|
591
|
+
<div class="grid grid-cols-4 gap-2 p-2">
|
|
622
592
|
${moreTabs.map((tab) => {
|
|
623
593
|
const isHome = tab.id === "dashboard";
|
|
624
594
|
const isActive = activeTab.value === tab.id;
|
|
625
595
|
return html`
|
|
626
596
|
<button
|
|
627
597
|
key=${tab.id}
|
|
628
|
-
class="
|
|
598
|
+
class="btn btn-ghost btn-sm flex flex-col items-center gap-1 h-auto py-2 ${isActive ? "btn-active" : ""}"
|
|
629
599
|
aria-label=${`Open ${tab.label}`}
|
|
630
600
|
onClick=${() =>
|
|
631
601
|
onNavigate(tab.id, {
|
|
632
602
|
resetHistory: isHome,
|
|
633
603
|
})}
|
|
634
604
|
>
|
|
635
|
-
<span class="
|
|
636
|
-
<span class="
|
|
605
|
+
<span class="text-lg">${ICONS[tab.icon]}</span>
|
|
606
|
+
<span class="text-xs">${tab.label}</span>
|
|
637
607
|
</button>
|
|
638
608
|
`;
|
|
639
609
|
})}
|
|
@@ -997,7 +967,7 @@ function App() {
|
|
|
997
967
|
|
|
998
968
|
return html`
|
|
999
969
|
<div
|
|
1000
|
-
class="app-shell"
|
|
970
|
+
class="app-shell flex h-screen bg-base-100 overflow-hidden"
|
|
1001
971
|
style=${shellStyle}
|
|
1002
972
|
data-tab=${activeTab.value}
|
|
1003
973
|
data-has-rail=${showSessionRail ? "true" : "false"}
|
|
@@ -1008,8 +978,8 @@ function App() {
|
|
|
1008
978
|
${/* Sidebar drawer overlay for tablet */ ""}
|
|
1009
979
|
${sidebarDrawerOpen && !isDesktop
|
|
1010
980
|
? html`
|
|
1011
|
-
<div class="drawer-overlay" onClick=${closeDrawers}></div>
|
|
1012
|
-
<div class="drawer drawer-left">
|
|
981
|
+
<div class="drawer-overlay fixed inset-0 bg-black/50 z-40" onClick=${closeDrawers}></div>
|
|
982
|
+
<div class="drawer drawer-left fixed top-0 bottom-0 left-0 z-50 w-72">
|
|
1013
983
|
<${SidebarNav} />
|
|
1014
984
|
</div>
|
|
1015
985
|
`
|
|
@@ -1018,8 +988,8 @@ function App() {
|
|
|
1018
988
|
${/* Inspector drawer overlay for tablet */ ""}
|
|
1019
989
|
${inspectorDrawerOpen && !isDesktop
|
|
1020
990
|
? html`
|
|
1021
|
-
<div class="drawer-overlay" onClick=${closeDrawers}></div>
|
|
1022
|
-
<div class="drawer drawer-right">
|
|
991
|
+
<div class="drawer-overlay fixed inset-0 bg-black/50 z-40" onClick=${closeDrawers}></div>
|
|
992
|
+
<div class="drawer drawer-right fixed top-0 bottom-0 right-0 z-50 w-72">
|
|
1023
993
|
<${InspectorPanel}
|
|
1024
994
|
onResizeStart=${handleResizeStart}
|
|
1025
995
|
onResizeReset=${handleResizeReset}
|
|
@@ -1036,14 +1006,14 @@ function App() {
|
|
|
1036
1006
|
showResizer=${isDesktop}
|
|
1037
1007
|
/>`
|
|
1038
1008
|
: null}
|
|
1039
|
-
<div class="app-main">
|
|
1040
|
-
<div class="main-panel">
|
|
1009
|
+
<div class="app-main flex flex-col flex-1 min-w-0">
|
|
1010
|
+
<div class="main-panel flex flex-col flex-1 min-w-0">
|
|
1041
1011
|
<${Header} />
|
|
1042
1012
|
|
|
1043
1013
|
${/* Tablet action bar with drawer toggles */ ""}
|
|
1044
1014
|
${showDrawerToggles
|
|
1045
1015
|
? html`
|
|
1046
|
-
<div class="tablet-action-bar">
|
|
1016
|
+
<div class="tablet-action-bar flex items-center gap-2 px-4 py-2 bg-base-200 border-b border-base-content/5">
|
|
1047
1017
|
<button
|
|
1048
1018
|
class="btn btn-ghost btn-sm tablet-toggle"
|
|
1049
1019
|
onClick=${toggleSidebar}
|
|
@@ -1070,14 +1040,14 @@ function App() {
|
|
|
1070
1040
|
<${ToastContainer} />
|
|
1071
1041
|
<${CommandPalette} open=${paletteOpen} onClose=${paletteClose} />
|
|
1072
1042
|
<${PullToRefresh} onRefresh=${() => refreshTab(activeTab.value)}>
|
|
1073
|
-
<main class="main-content" ref=${mainRef}>
|
|
1043
|
+
<main class="main-content flex-1 overflow-y-auto p-4" ref=${mainRef}>
|
|
1074
1044
|
<${CurrentTab} />
|
|
1075
1045
|
</main>
|
|
1076
1046
|
<//>
|
|
1077
1047
|
${showScrollTop &&
|
|
1078
1048
|
html`
|
|
1079
1049
|
<button
|
|
1080
|
-
class="scroll-top"
|
|
1050
|
+
class="scroll-top btn btn-circle btn-sm btn-primary fixed bottom-20 right-4 z-30 shadow-lg"
|
|
1081
1051
|
title="Back to top"
|
|
1082
1052
|
onClick=${() => {
|
|
1083
1053
|
mainRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|