lobsterboard 0.7.0 → 0.8.1
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/CHANGELOG.md +14 -0
- package/app.html +32 -2
- package/css/builder.css +42 -0
- package/dist/lobsterboard.css +1 -1
- package/dist/lobsterboard.esm.js +143 -1
- package/dist/lobsterboard.esm.js.map +1 -1
- package/dist/lobsterboard.esm.min.js +2 -2
- package/dist/lobsterboard.esm.min.js.map +1 -1
- package/dist/lobsterboard.umd.js +143 -1
- package/dist/lobsterboard.umd.js.map +1 -1
- package/dist/lobsterboard.umd.min.js +2 -2
- package/dist/lobsterboard.umd.min.js.map +1 -1
- package/js/builder.js +35 -3506
- package/js/editor/auth.js +328 -0
- package/js/editor/canvas.js +118 -0
- package/js/editor/config.js +101 -0
- package/js/editor/controls.js +120 -0
- package/js/editor/drag-drop.js +110 -0
- package/js/editor/export.js +153 -0
- package/js/editor/index.js +20 -0
- package/js/editor/modals.js +55 -0
- package/js/editor/properties.js +745 -0
- package/js/editor/servers.js +154 -0
- package/js/editor/state.js +73 -0
- package/js/widgets/ai-tools.js +818 -0
- package/js/widgets/layout.js +164 -0
- package/js/widgets/media.js +505 -0
- package/js/widgets/misc.js +501 -0
- package/js/widgets/productivity.js +460 -0
- package/js/widgets/releases.js +265 -0
- package/js/widgets/shared/helpers.js +32 -0
- package/js/widgets/shared/icons.js +123 -0
- package/js/widgets/shared/stats.js +172 -0
- package/js/widgets/system.js +413 -0
- package/js/widgets/time.js +318 -0
- package/js/widgets/weather.js +154 -0
- package/js/widgets/widgets.js +58 -0
- package/js/widgets.js +267 -0
- package/package.json +8 -2
- package/server.cjs +76 -3385
- package/src/widgets.js +142 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.8.0] - 2026-03-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Claude Usage widget** — Real-time Claude Code subscription monitoring with 5-hour session, 7-day weekly, Opus/Sonnet limits, and extra usage tracking — thanks @JamesTsetsekas!
|
|
7
|
+
- **Claude Usage standalone page** — Full-page dashboard for detailed usage analysis at `/pages/claude-usage/`
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **OpenClaw version detection** — Widget now correctly parses `openclaw --version` output to fix false "Update available" notifications — thanks @JamesTsetsekas!
|
|
11
|
+
|
|
12
|
+
### Technical
|
|
13
|
+
- Auto-refreshes expired Claude OAuth tokens via CLI
|
|
14
|
+
- Reads from standard `~/.claude/.credentials.json` location
|
|
15
|
+
- Comprehensive error handling for rate limits and authentication issues
|
|
16
|
+
|
|
3
17
|
## [0.7.0] - 2026-03-17
|
|
4
18
|
|
|
5
19
|
### Added
|
package/app.html
CHANGED
|
@@ -164,6 +164,11 @@
|
|
|
164
164
|
<span class="widget-name">Uptime Monitor</span>
|
|
165
165
|
<span class="widget-verified" title="Tested & Verified">✓</span>
|
|
166
166
|
</div>
|
|
167
|
+
<div class="widget-item" draggable="true" data-widget="system-graphical">
|
|
168
|
+
<span class="widget-icon">💻</span>
|
|
169
|
+
<span class="widget-name">System (Graphical)</span>
|
|
170
|
+
<span class="widget-verified" title="Tested & Verified">✓</span>
|
|
171
|
+
</div>
|
|
167
172
|
</div>
|
|
168
173
|
</div>
|
|
169
174
|
|
|
@@ -742,8 +747,33 @@
|
|
|
742
747
|
</div>
|
|
743
748
|
</div>
|
|
744
749
|
|
|
745
|
-
|
|
746
|
-
<script src="js/
|
|
750
|
+
<!-- Widget modules (shared first, then index, then categories) -->
|
|
751
|
+
<script src="js/widgets/shared/helpers.js"></script>
|
|
752
|
+
<script src="js/widgets/shared/icons.js"></script>
|
|
753
|
+
<script src="js/widgets/shared/stats.js"></script>
|
|
754
|
+
<script src="js/widgets/index.js"></script>
|
|
755
|
+
<script src="js/widgets/weather.js"></script>
|
|
756
|
+
<script src="js/widgets/system.js"></script>
|
|
757
|
+
<script src="js/widgets/ai-tools.js"></script>
|
|
758
|
+
<script src="js/widgets/media.js"></script>
|
|
759
|
+
<script src="js/widgets/productivity.js"></script>
|
|
760
|
+
<script src="js/widgets/time.js"></script>
|
|
761
|
+
<script src="js/widgets/layout.js"></script>
|
|
762
|
+
<script src="js/widgets/releases.js"></script>
|
|
763
|
+
<script src="js/widgets/misc.js"></script>
|
|
764
|
+
<!-- Editor modules (order matters: state first, index last) -->
|
|
765
|
+
<script src="js/editor/state.js"></script>
|
|
766
|
+
<script src="js/editor/canvas.js"></script>
|
|
767
|
+
<script src="js/editor/auth.js"></script>
|
|
768
|
+
<script src="js/editor/config.js"></script>
|
|
769
|
+
<script src="js/editor/servers.js"></script>
|
|
770
|
+
<script src="js/editor/drag-drop.js"></script>
|
|
771
|
+
<script src="js/editor/widgets.js"></script>
|
|
772
|
+
<script src="js/editor/properties.js"></script>
|
|
773
|
+
<script src="js/editor/controls.js"></script>
|
|
774
|
+
<script src="js/editor/export.js"></script>
|
|
775
|
+
<script src="js/editor/modals.js"></script>
|
|
776
|
+
<script src="js/editor/index.js"></script>
|
|
747
777
|
<script>
|
|
748
778
|
// Load page links into nav
|
|
749
779
|
fetch('/api/pages').then(r => r.json()).then(pages => {
|
package/css/builder.css
CHANGED
|
@@ -535,6 +535,48 @@ body {
|
|
|
535
535
|
color: var(--text-primary);
|
|
536
536
|
}
|
|
537
537
|
|
|
538
|
+
/* System Graphical Widget */
|
|
539
|
+
.placed-widget .system-metric {
|
|
540
|
+
display: flex;
|
|
541
|
+
flex-direction: column;
|
|
542
|
+
align-items: center;
|
|
543
|
+
gap: 6px;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
.placed-widget .metric-label {
|
|
547
|
+
font-size: calc(11px * var(--font-scale));
|
|
548
|
+
font-weight: 600;
|
|
549
|
+
color: var(--text-muted);
|
|
550
|
+
text-transform: uppercase;
|
|
551
|
+
letter-spacing: 0.5px;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.placed-widget .progress-ring-container {
|
|
555
|
+
position: relative;
|
|
556
|
+
width: 64px;
|
|
557
|
+
height: 64px;
|
|
558
|
+
display: flex;
|
|
559
|
+
align-items: center;
|
|
560
|
+
justify-content: center;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.placed-widget .progress-ring {
|
|
564
|
+
width: 100%;
|
|
565
|
+
height: 100%;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.placed-widget .progress-ring-text {
|
|
569
|
+
position: absolute;
|
|
570
|
+
top: 50%;
|
|
571
|
+
left: 50%;
|
|
572
|
+
transform: translate(-50%, -50%);
|
|
573
|
+
font-size: calc(12px * var(--font-scale));
|
|
574
|
+
font-weight: 700;
|
|
575
|
+
color: var(--text-primary);
|
|
576
|
+
text-align: center;
|
|
577
|
+
line-height: 1;
|
|
578
|
+
}
|
|
579
|
+
|
|
538
580
|
/* Dash Cards */
|
|
539
581
|
.placed-widget .dash-card {
|
|
540
582
|
background: var(--bg-secondary);
|
package/dist/lobsterboard.css
CHANGED
package/dist/lobsterboard.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* LobsterBoard v0.
|
|
2
|
+
* LobsterBoard v0.8.1
|
|
3
3
|
* Dashboard builder with customizable widgets
|
|
4
4
|
* https://github.com/curbob/LobsterBoard
|
|
5
5
|
* @license MIT
|
|
@@ -477,6 +477,148 @@ const WIDGETS = {
|
|
|
477
477
|
document.getElementById('${props.id}-refresh').textContent =
|
|
478
478
|
new Date().toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
|
479
479
|
`
|
|
480
|
+
},
|
|
481
|
+
|
|
482
|
+
'system-graphical': {
|
|
483
|
+
name: 'System (Graphical)',
|
|
484
|
+
icon: '💻',
|
|
485
|
+
category: 'system',
|
|
486
|
+
description: 'Graphical CPU and Memory usage with circular progress rings. Supports remote servers via lobsterboard-agent.',
|
|
487
|
+
defaultWidth: 240,
|
|
488
|
+
defaultHeight: 140,
|
|
489
|
+
hasApiKey: false,
|
|
490
|
+
properties: {
|
|
491
|
+
title: 'System',
|
|
492
|
+
server: 'local',
|
|
493
|
+
refreshInterval: 5,
|
|
494
|
+
showPercentages: true,
|
|
495
|
+
showLabels: true
|
|
496
|
+
},
|
|
497
|
+
preview: `<div style="display:flex;align-items:center;justify-content:space-around;padding:8px;">
|
|
498
|
+
<div style="position:relative;width:50px;height:50px;">
|
|
499
|
+
<svg viewBox="0 0 48 48" style="width:100%;height:100%;transform:rotate(-90deg);">
|
|
500
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="#30363d" stroke-width="4"/>
|
|
501
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="#58a6ff" stroke-width="4"
|
|
502
|
+
stroke-dasharray="113" stroke-dashoffset="85" stroke-linecap="round"/>
|
|
503
|
+
</svg>
|
|
504
|
+
<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:10px;font-weight:600;color:#58a6ff;">25%</div>
|
|
505
|
+
</div>
|
|
506
|
+
<div style="position:relative;width:50px;height:50px;">
|
|
507
|
+
<svg viewBox="0 0 48 48" style="width:100%;height:100%;transform:rotate(-90deg);">
|
|
508
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="#30363d" stroke-width="4"/>
|
|
509
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="#3fb950" stroke-width="4"
|
|
510
|
+
stroke-dasharray="113" stroke-dashoffset="68" stroke-linecap="round"/>
|
|
511
|
+
</svg>
|
|
512
|
+
<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:10px;font-weight:600;color:#3fb950;">40%</div>
|
|
513
|
+
</div>
|
|
514
|
+
</div>`,
|
|
515
|
+
generateHtml: (props) => `
|
|
516
|
+
<div class="dash-card" id="widget-${props.id}" style="height:100%;">
|
|
517
|
+
<div class="dash-card-head">
|
|
518
|
+
<span class="dash-card-title">${renderIcon('cpu')} ${props.title || 'System'}</span>
|
|
519
|
+
${props.server && props.server !== 'local' ? `<span class="dash-card-badge" style="font-size:10px;">🌐</span>` : ''}
|
|
520
|
+
</div>
|
|
521
|
+
<div class="dash-card-body" style="display:flex;align-items:center;justify-content:space-around;padding:12px;">
|
|
522
|
+
<div class="system-metric">
|
|
523
|
+
${props.showLabels ? '<div class="metric-label">CPU</div>' : ''}
|
|
524
|
+
<div class="progress-ring-container">
|
|
525
|
+
<svg class="progress-ring" viewBox="0 0 48 48">
|
|
526
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="var(--bg-tertiary)" stroke-width="4"/>
|
|
527
|
+
<circle id="${props.id}-cpu-ring" cx="24" cy="24" r="18" fill="none" stroke="#58a6ff" stroke-width="4"
|
|
528
|
+
stroke-dasharray="113.1" stroke-dashoffset="113.1" stroke-linecap="round"
|
|
529
|
+
style="transition: stroke-dashoffset 0.6s ease, stroke 0.3s ease; transform: rotate(-90deg); transform-origin: 50% 50%;"/>
|
|
530
|
+
</svg>
|
|
531
|
+
<div class="progress-ring-text">
|
|
532
|
+
<span id="${props.id}-cpu-pct" ${props.showPercentages ? '' : 'style="display:none;"'}>—</span>
|
|
533
|
+
<span id="${props.id}-cpu-icon" ${props.showPercentages ? 'style="display:none;"' : ''}>💻</span>
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="system-metric">
|
|
538
|
+
${props.showLabels ? '<div class="metric-label">MEM</div>' : ''}
|
|
539
|
+
<div class="progress-ring-container">
|
|
540
|
+
<svg class="progress-ring" viewBox="0 0 48 48">
|
|
541
|
+
<circle cx="24" cy="24" r="18" fill="none" stroke="var(--bg-tertiary)" stroke-width="4"/>
|
|
542
|
+
<circle id="${props.id}-mem-ring" cx="24" cy="24" r="18" fill="none" stroke="#3fb950" stroke-width="4"
|
|
543
|
+
stroke-dasharray="113.1" stroke-dashoffset="113.1" stroke-linecap="round"
|
|
544
|
+
style="transition: stroke-dashoffset 0.6s ease, stroke 0.3s ease; transform: rotate(-90deg); transform-origin: 50% 50%;"/>
|
|
545
|
+
</svg>
|
|
546
|
+
<div class="progress-ring-text">
|
|
547
|
+
<span id="${props.id}-mem-pct" ${props.showPercentages ? '' : 'style="display:none;"'}>—</span>
|
|
548
|
+
<span id="${props.id}-mem-icon" ${props.showPercentages ? 'style="display:none;"' : ''}>🧠</span>
|
|
549
|
+
</div>
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
</div>
|
|
553
|
+
</div>`,
|
|
554
|
+
generateJs: (props) => `
|
|
555
|
+
// System (Graphical) Widget: ${props.id} — ${props.server === 'local' ? 'local SSE' : 'remote: ' + props.server}
|
|
556
|
+
|
|
557
|
+
function getUsageColor(percentage) {
|
|
558
|
+
if (percentage >= 90) return '#f85149'; // Red for critical
|
|
559
|
+
if (percentage >= 75) return '#d29922'; // Yellow for warning
|
|
560
|
+
if (percentage >= 50) return '#58a6ff'; // Blue for moderate
|
|
561
|
+
return '#3fb950'; // Green for good
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function updateProgressRing(ringId, percentage, textId) {
|
|
565
|
+
const ring = document.getElementById(ringId);
|
|
566
|
+
const text = document.getElementById(textId);
|
|
567
|
+
if (!ring || !text) return;
|
|
568
|
+
|
|
569
|
+
const normalizedPct = Math.max(0, Math.min(100, percentage || 0));
|
|
570
|
+
const circumference = 113.1; // 2 * π * 18
|
|
571
|
+
const offset = circumference - (normalizedPct / 100 * circumference);
|
|
572
|
+
const color = getUsageColor(normalizedPct);
|
|
573
|
+
|
|
574
|
+
ring.style.strokeDashoffset = offset;
|
|
575
|
+
ring.style.stroke = color;
|
|
576
|
+
text.textContent = Math.round(normalizedPct) + '%';
|
|
577
|
+
text.style.color = color;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
onStats('${props.server || 'local'}', function(data) {
|
|
581
|
+
// Handle offline state
|
|
582
|
+
if (data._offline) {
|
|
583
|
+
document.getElementById('${props.id}-cpu-pct').textContent = '⚠️';
|
|
584
|
+
document.getElementById('${props.id}-mem-pct').textContent = '⚠️';
|
|
585
|
+
document.getElementById('${props.id}-cpu-ring').style.strokeDashoffset = '113.1';
|
|
586
|
+
document.getElementById('${props.id}-mem-ring').style.strokeDashoffset = '113.1';
|
|
587
|
+
return;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Update CPU ring
|
|
591
|
+
if (data.cpu && data.cpu.currentLoad != null) {
|
|
592
|
+
updateProgressRing('${props.id}-cpu-ring', data.cpu.currentLoad, '${props.id}-cpu-pct');
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Update Memory ring
|
|
596
|
+
if (data.memory && data.memory.total && data.memory.active != null) {
|
|
597
|
+
const memoryPct = (data.memory.active / data.memory.total) * 100;
|
|
598
|
+
updateProgressRing('${props.id}-mem-ring', memoryPct, '${props.id}-mem-pct');
|
|
599
|
+
}
|
|
600
|
+
}, ${(props.refreshInterval || 5) * 1000});
|
|
601
|
+
`
|
|
602
|
+
},
|
|
603
|
+
|
|
604
|
+
'claude-usage': {
|
|
605
|
+
name: 'Claude Usage',
|
|
606
|
+
icon: '🤖',
|
|
607
|
+
category: 'large',
|
|
608
|
+
description: 'Real-time Claude Code subscription usage (5h session, 7d weekly, Opus, Sonnet limits). Reads credentials from ~/.claude.',
|
|
609
|
+
defaultWidth: 380,
|
|
610
|
+
defaultHeight: 260,
|
|
611
|
+
hasApiKey: false,
|
|
612
|
+
properties: { title: 'Claude Usage', refreshInterval: 120 },
|
|
613
|
+
preview: `<div style="padding:8px;font-size:11px;"><div><b>5h Session</b> 28%</div><div><b>7d Weekly</b> 31%</div></div>`,
|
|
614
|
+
generateHtml: (props) => `<div class="dash-card" id="widget-${props.id}" style="height:100%;"><div class="dash-card-head"><span class="dash-card-title">🤖 ${props.title || 'Claude Usage'}</span><span id="${props.id}-sub" style="font-size:10px;color:#8b949e;margin-left:auto;"></span></div><div class="dash-card-body" id="${props.id}-body" style="padding:8px 12px;overflow-y:auto;"><div style="color:#8b949e;text-align:center;">Loading...</div></div></div>`,
|
|
615
|
+
generateJs: (props) => `
|
|
616
|
+
function barColor(pct) { return pct >= 80 ? '#f85149' : pct >= 50 ? '#d29922' : '#3fb950'; }
|
|
617
|
+
function timeLeft(iso) { if (!iso) return ''; const ms = new Date(iso) - Date.now(); if (ms <= 0) return 'now'; const h = Math.floor(ms/3600000), m = Math.floor((ms%3600000)/60000); return h > 0 ? h+'h '+m+'m' : m+'m'; }
|
|
618
|
+
function usageBar(label, pct, resetIso) { const p = Math.min(100,Math.max(0,pct||0)), c = barColor(p), reset = resetIso ? '<span style="color:#8b949e;font-size:10px;">resets '+timeLeft(resetIso)+'</span>' : ''; return '<div style="margin-bottom:10px;"><div style="display:flex;justify-content:space-between;margin-bottom:3px;"><span style="font-weight:600;font-size:12px;">'+label+'</span><span style="font-size:13px;font-weight:700;color:'+c+';">'+p.toFixed(0)+'%</span></div><div style="background:#21262d;border-radius:4px;height:8px;overflow:hidden;"><div style="width:'+p+'%;height:100%;background:'+c+';border-radius:4px;transition:width .5s;"></div></div>'+(reset?'<div style="text-align:right;margin-top:2px;">'+reset+'</div>':'')+'</div>'; }
|
|
619
|
+
async function update_${props.id.replace(/-/g,'_')}() { const body = document.getElementById('${props.id}-body'); const subEl = document.getElementById('${props.id}-sub'); try { const res = await fetch('/api/pages/claude-usage/usage'); const d = await res.json(); if (d.error) { body.innerHTML='<div style="color:#f85149;">'+d.error+'</div>'; return; } if (subEl) { subEl.textContent = {max:'Max (5×)',pro:'Pro',free:'Free'}[d.subscription]||d.subscription||''; } let html=''; if(d.five_hour) html+=usageBar('5h Session',d.five_hour.utilization,d.five_hour.resets_at); if(d.seven_day) html+=usageBar('7d Weekly',d.seven_day.utilization,d.seven_day.resets_at); if(d.seven_day_opus) html+=usageBar('Opus (7d)',d.seven_day_opus.utilization,d.seven_day_opus.resets_at); if(d.seven_day_sonnet&&d.seven_day_sonnet.utilization>0) html+=usageBar('Sonnet (7d)',d.seven_day_sonnet.utilization,d.seven_day_sonnet.resets_at); if(d.extra_usage&&d.extra_usage.is_enabled){const used=(d.extra_usage.used_credits/100).toFixed(2),limit=d.extra_usage.monthly_limit>0?(d.extra_usage.monthly_limit/100).toFixed(2):'∞';html+='<div style="margin-top:4px;padding-top:6px;border-top:1px solid #30363d;"><div style="display:flex;justify-content:space-between;font-size:11px;"><span style="color:#8b949e;">Extra Usage</span><span style="font-weight:600;">$'+used+' / $'+limit+'</span></div></div>';} if(!html) html='<div style="color:#8b949e;">No usage data</div>'; body.innerHTML=html; } catch(e) { console.error('Claude usage error:',e); body.innerHTML='<div style="color:#f85149;">Failed to load</div>'; } }
|
|
620
|
+
update_${props.id.replace(/-/g,'_')}(); setInterval(update_${props.id.replace(/-/g,'_')}, ${(props.refreshInterval||120)*1000});
|
|
621
|
+
`
|
|
480
622
|
}
|
|
481
623
|
};
|
|
482
624
|
|