lobsterboard 0.3.1 → 0.3.2

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 CHANGED
@@ -12,6 +12,12 @@
12
12
  - **Phosphor icon system** — themed widgets use Phosphor icons; Default theme keeps emoji
13
13
  - **Theme selector dropdown** in edit mode header
14
14
  - Theme persists to localStorage and dashboard config
15
+ - **Themes showcase** on website and README with lightbox gallery
16
+
17
+ ## [0.2.6] - 2026-02-23
18
+
19
+ ### Fixed
20
+ - **Version suffix comparison** — versions like `2026.2.22-2` (npm post-release patches) now correctly match GitHub tags like `v2026.2.22`, fixing false "Update available" indicators — thanks @JamesTsetsekas!
15
21
 
16
22
  ## [0.2.5] - 2026-02-19
17
23
 
package/README.md CHANGED
@@ -37,9 +37,27 @@ Open **http://localhost:8080** → press **Ctrl+E** to enter edit mode → drag
37
37
  - **Custom pages** — extend your dashboard with full custom pages (notes, kanban boards, anything)
38
38
  - **Canvas sizes** — preset resolutions (1920×1080, 2560×1440, etc.) or custom sizes
39
39
  - **Live data** — system stats stream via Server-Sent Events, widgets auto-refresh
40
- - **Dark theme** — the only correct choice
40
+ - **5 themes** — Default (dark), Terminal (CRT green), Feminine (pastel pink), Feminine Dark, Paper (sepia)
41
41
  - **No cloud** — everything runs locally, your data stays yours
42
42
 
43
+ ## Themes
44
+
45
+ LobsterBoard ships with 5 built-in themes. Switch themes from the dropdown in edit mode — your choice persists across sessions.
46
+
47
+ | Default | Terminal | Paper |
48
+ |---------|----------|-------|
49
+ | ![Default](site-assets/themes/theme-default.png) | ![Terminal](site-assets/themes/theme-terminal.png) | ![Paper](site-assets/themes/theme-paper.png) |
50
+
51
+ | Feminine | Feminine Dark |
52
+ |----------|---------------|
53
+ | ![Feminine](site-assets/themes/theme-feminine.png) | ![Feminine Dark](site-assets/themes/theme-feminine-dark.png) |
54
+
55
+ - **Default** — dark theme with emoji icons (the classic look)
56
+ - **Terminal** — green CRT aesthetic with scanlines and Phosphor icons
57
+ - **Paper** — warm cream/sepia tones, serif fonts, vintage feel
58
+ - **Feminine** — soft pink and lavender pastels with subtle glows
59
+ - **Feminine Dark** — pink/purple accents on a dark background
60
+
43
61
  ## Configuration
44
62
 
45
63
  ```bash
@@ -1,4 +1,4 @@
1
- /* LobsterBoard v0.3.1 - Dashboard Styles */
1
+ /* LobsterBoard v0.3.2 - Dashboard Styles */
2
2
  /* LobsterBoard Dashboard - Generated Styles */
3
3
 
4
4
  :root {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * LobsterBoard v0.3.1
2
+ * LobsterBoard v0.3.2
3
3
  * Dashboard builder with customizable widgets
4
4
  * https://github.com/curbob/LobsterBoard
5
5
  * @license MIT
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * LobsterBoard v0.3.1
2
+ * LobsterBoard v0.3.2
3
3
  * Dashboard builder with customizable widgets
4
4
  * https://github.com/curbob/LobsterBoard
5
5
  * @license MIT
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * LobsterBoard v0.3.1
2
+ * LobsterBoard v0.3.2
3
3
  * Dashboard builder with customizable widgets
4
4
  * https://github.com/curbob/LobsterBoard
5
5
  * @license MIT
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * LobsterBoard v0.3.1
2
+ * LobsterBoard v0.3.2
3
3
  * Dashboard builder with customizable widgets
4
4
  * https://github.com/curbob/LobsterBoard
5
5
  * @license MIT
package/js/templates.js CHANGED
@@ -155,7 +155,7 @@
155
155
  tplGrid.style.display = '';
156
156
  loadTemplates();
157
157
  } else {
158
- alert('❌ ' + (data.error || 'Delete failed'));
158
+ alert('❌ ' + (data.error || data.message || 'Delete failed'));
159
159
  }
160
160
  } catch (e) {
161
161
  alert('❌ Delete failed: ' + e.message);
@@ -187,7 +187,7 @@
187
187
  alert(`✅ ${data.message}\n\nReloading dashboard...`);
188
188
  location.reload();
189
189
  } else {
190
- alert('❌ ' + (data.error || 'Import failed'));
190
+ alert('❌ ' + (data.error || data.message || 'Import failed'));
191
191
  }
192
192
  } catch (e) {
193
193
  alert('❌ Import failed: ' + e.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lobsterboard",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Self-hosted drag-and-drop dashboard builder with 50 widgets, template gallery, and custom pages. Works standalone or with OpenClaw.",
5
5
  "keywords": [
6
6
  "dashboard",
package/server.cjs CHANGED
@@ -1458,12 +1458,25 @@ const server = http.createServer(async (req, res) => {
1458
1458
  req.on('end', () => {
1459
1459
  try {
1460
1460
  const { id, mode } = JSON.parse(body);
1461
+ if (!id) { sendJson(res, 400, { error: 'Missing template id' }); return; }
1462
+ if (!mode) { sendJson(res, 400, { error: 'Missing import mode' }); return; }
1463
+
1461
1464
  const tplConfigPath = path.join(TEMPLATES_DIR, id, 'config.json');
1462
- if (!fs.existsSync(tplConfigPath)) { sendJson(res, 404, { error: 'Template not found' }); return; }
1463
- const tplConfig = JSON.parse(fs.readFileSync(tplConfigPath, 'utf8'));
1465
+ if (!fs.existsSync(tplConfigPath)) { sendJson(res, 404, { error: `Template "${id}" not found` }); return; }
1466
+
1467
+ let tplConfig;
1468
+ try {
1469
+ tplConfig = JSON.parse(fs.readFileSync(tplConfigPath, 'utf8'));
1470
+ } catch (parseErr) {
1471
+ sendJson(res, 500, { error: `Template config is invalid JSON: ${parseErr.message}` }); return;
1472
+ }
1464
1473
 
1465
1474
  if (mode === 'replace') {
1466
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(tplConfig, null, 2));
1475
+ try {
1476
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(tplConfig, null, 2));
1477
+ } catch (writeErr) {
1478
+ sendJson(res, 500, { error: `Failed to write config: ${writeErr.message}` }); return;
1479
+ }
1467
1480
  sendJson(res, 200, { status: 'success', message: 'Template imported (replace)' });
1468
1481
  } else if (mode === 'merge') {
1469
1482
  let currentConfig = { canvas: { width: 1920, height: 1080 }, widgets: [] };
@@ -1481,12 +1494,16 @@ const server = http.createServer(async (req, res) => {
1481
1494
  y: (w.y || 0) + offset
1482
1495
  }));
1483
1496
  currentConfig.widgets = [...(currentConfig.widgets || []), ...newWidgets];
1484
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(currentConfig, null, 2));
1497
+ try {
1498
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(currentConfig, null, 2));
1499
+ } catch (writeErr) {
1500
+ sendJson(res, 500, { error: `Failed to write config: ${writeErr.message}` }); return;
1501
+ }
1485
1502
  sendJson(res, 200, { status: 'success', message: `Merged ${newWidgets.length} widgets` });
1486
1503
  } else {
1487
1504
  sendJson(res, 400, { error: 'Invalid mode. Use "replace" or "merge"' });
1488
1505
  }
1489
- } catch (e) { sendError(res, e.message); }
1506
+ } catch (e) { sendJson(res, 500, { error: `Import error: ${e.message}` }); }
1490
1507
  });
1491
1508
  return;
1492
1509
  }