@tomorrowos/sdk 0.9.41 → 0.9.43

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.9.41",
3
+ "version": "0.9.43",
4
4
  "description": "TomorrowOS CMS server SDK - WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.41",
3
+ "version": "0.9.43",
4
4
  "description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
5
5
  "private": true,
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "build-player": "tomorrowos build --platform tizen"
14
14
  },
15
15
  "dependencies": {
16
- "@tomorrowos/sdk": "^0.9.41",
16
+ "@tomorrowos/sdk": "^0.9.43",
17
17
  "dotenv": "^17.2.3",
18
18
  "tsx": "^4.19.0"
19
19
  },
@@ -44,6 +44,41 @@ const UPLOAD_MAX_RETRIES = 3;
44
44
  const DEVICE_RECONNECT_GRACE_MS = 60000;
45
45
  const UPLOAD_TIMEOUT_MS = 120000;
46
46
 
47
+ /**
48
+ * Apply brand.json to the Control Panel (name + colours). No logo on the panel.
49
+ * Fetches same-origin GET /brand.json served by TomorrowOS.listen().
50
+ */
51
+ async function applyBrandFromServer() {
52
+ try {
53
+ const res = await fetch("/brand.json", { cache: "no-store" });
54
+ if (!res.ok) return;
55
+ const brand = await res.json();
56
+ if (!brand || typeof brand !== "object") return;
57
+
58
+ const name = String(brand.name || "").trim() || "TomorrowOS";
59
+ const panelTitle = `${name} Control Panel`;
60
+ document.title = panelTitle;
61
+ const heading = document.querySelector(".app-header h1");
62
+ if (heading) heading.textContent = panelTitle;
63
+
64
+ const tagline = String(brand.tagline || "").trim();
65
+ const subtitle = document.querySelector(".app-header p");
66
+ if (subtitle && tagline) subtitle.textContent = tagline;
67
+
68
+ const root = document.documentElement;
69
+ const primary = String(brand.primaryColor || "").trim();
70
+ const background = String(brand.backgroundColor || "").trim();
71
+ const text = String(brand.textColor || "").trim();
72
+ const secondary = String(brand.secondaryColor || "").trim();
73
+ if (primary) root.style.setProperty("--brand-primary", primary);
74
+ if (background) root.style.setProperty("--brand-background", background);
75
+ if (text) root.style.setProperty("--brand-text", text);
76
+ if (secondary) root.style.setProperty("--brand-secondary", secondary);
77
+ } catch (_) {
78
+ /* keep starter defaults */
79
+ }
80
+ }
81
+
47
82
  function escapeHtml(value) {
48
83
  return String(value ?? "")
49
84
  .replace(/&/g, "&")
@@ -2285,6 +2320,8 @@ function startServerStatusPolling() {
2285
2320
  }
2286
2321
 
2287
2322
  document.addEventListener("DOMContentLoaded", () => {
2323
+ void applyBrandFromServer();
2324
+
2288
2325
  const cmsUrlSection = document.getElementById("cmsUrlSection");
2289
2326
  if (cmsUrlSection && !isLocalPanelHost(window.location.hostname)) {
2290
2327
  cmsUrlSection.classList.add("hidden");
@@ -1,3 +1,12 @@
1
+ :root {
2
+ --brand-primary: #ff8a3d;
3
+ --brand-background: #f4f3f0;
4
+ --brand-text: #0a0908;
5
+ --brand-secondary: #fafaf9;
6
+ --brand-header-bg: #0a0908;
7
+ --brand-header-fg: #fafaf9;
8
+ }
9
+
1
10
  * {
2
11
  box-sizing: border-box;
3
12
  }
@@ -5,14 +14,14 @@
5
14
  body {
6
15
  margin: 0;
7
16
  font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
8
- background: #f4f3f0;
9
- color: #0a0908;
17
+ background: var(--brand-background);
18
+ color: var(--brand-text);
10
19
  }
11
20
 
12
21
  .app-header {
13
22
  padding: 1rem 1.5rem;
14
- background: #0a0908;
15
- color: #fafaf9;
23
+ background: var(--brand-header-bg);
24
+ color: var(--brand-header-fg);
16
25
  }
17
26
 
18
27
  .app-header h1 {
@@ -79,7 +88,7 @@ body {
79
88
  }
80
89
 
81
90
  .playlist-catalog-item--active {
82
- border-color: #ff8a3d;
91
+ border-color: var(--brand-primary);
83
92
  background: #fff5ee;
84
93
  }
85
94
 
@@ -579,14 +588,14 @@ button:hover {
579
588
  }
580
589
 
581
590
  button.primary {
582
- background: #ff8a3d;
583
- border-color: #e67320;
584
- color: #0a0908;
591
+ background: var(--brand-primary);
592
+ border-color: color-mix(in srgb, var(--brand-primary) 82%, #000);
593
+ color: var(--brand-text);
585
594
  font-weight: 600;
586
595
  }
587
596
 
588
597
  button.primary:hover {
589
- background: #ff9a57;
598
+ background: color-mix(in srgb, var(--brand-primary) 88%, #fff);
590
599
  }
591
600
 
592
601
  button.danger {
@@ -670,7 +679,7 @@ button.danger {
670
679
  .upload-progress-bar {
671
680
  width: 0%;
672
681
  height: 100%;
673
- background: #ff8a3d;
682
+ background: var(--brand-primary);
674
683
  transition: width 120ms linear;
675
684
  }
676
685
 
@@ -710,7 +719,7 @@ button.danger {
710
719
  }
711
720
 
712
721
  .playlist-item--drop-target {
713
- border-color: #ff8a3d;
722
+ border-color: var(--brand-primary);
714
723
  box-shadow: 0 0 0 2px rgba(255, 138, 61, 0.25);
715
724
  }
716
725
 
@@ -1078,7 +1087,7 @@ button.danger {
1078
1087
  }
1079
1088
 
1080
1089
  .player-platform-link:focus-visible {
1081
- outline: 2px solid #ff8a3d;
1090
+ outline: 2px solid var(--brand-primary);
1082
1091
  outline-offset: 3px;
1083
1092
  }
1084
1093
 
@@ -44,6 +44,41 @@ const UPLOAD_MAX_RETRIES = 3;
44
44
  const DEVICE_RECONNECT_GRACE_MS = 60000;
45
45
  const UPLOAD_TIMEOUT_MS = 120000;
46
46
 
47
+ /**
48
+ * Apply brand.json to the Control Panel (name + colours). No logo on the panel.
49
+ * Fetches same-origin GET /brand.json served by TomorrowOS.listen().
50
+ */
51
+ async function applyBrandFromServer() {
52
+ try {
53
+ const res = await fetch("/brand.json", { cache: "no-store" });
54
+ if (!res.ok) return;
55
+ const brand = await res.json();
56
+ if (!brand || typeof brand !== "object") return;
57
+
58
+ const name = String(brand.name || "").trim() || "TomorrowOS";
59
+ const panelTitle = `${name} Control Panel`;
60
+ document.title = panelTitle;
61
+ const heading = document.querySelector(".app-header h1");
62
+ if (heading) heading.textContent = panelTitle;
63
+
64
+ const tagline = String(brand.tagline || "").trim();
65
+ const subtitle = document.querySelector(".app-header p");
66
+ if (subtitle && tagline) subtitle.textContent = tagline;
67
+
68
+ const root = document.documentElement;
69
+ const primary = String(brand.primaryColor || "").trim();
70
+ const background = String(brand.backgroundColor || "").trim();
71
+ const text = String(brand.textColor || "").trim();
72
+ const secondary = String(brand.secondaryColor || "").trim();
73
+ if (primary) root.style.setProperty("--brand-primary", primary);
74
+ if (background) root.style.setProperty("--brand-background", background);
75
+ if (text) root.style.setProperty("--brand-text", text);
76
+ if (secondary) root.style.setProperty("--brand-secondary", secondary);
77
+ } catch (_) {
78
+ /* keep starter defaults */
79
+ }
80
+ }
81
+
47
82
  function escapeHtml(value) {
48
83
  return String(value ?? "")
49
84
  .replace(/&/g, "&")
@@ -2285,6 +2320,8 @@ function startServerStatusPolling() {
2285
2320
  }
2286
2321
 
2287
2322
  document.addEventListener("DOMContentLoaded", () => {
2323
+ void applyBrandFromServer();
2324
+
2288
2325
  const cmsUrlSection = document.getElementById("cmsUrlSection");
2289
2326
  if (cmsUrlSection && !isLocalPanelHost(window.location.hostname)) {
2290
2327
  cmsUrlSection.classList.add("hidden");
@@ -1,3 +1,12 @@
1
+ :root {
2
+ --brand-primary: #ff8a3d;
3
+ --brand-background: #f4f3f0;
4
+ --brand-text: #0a0908;
5
+ --brand-secondary: #fafaf9;
6
+ --brand-header-bg: #0a0908;
7
+ --brand-header-fg: #fafaf9;
8
+ }
9
+
1
10
  * {
2
11
  box-sizing: border-box;
3
12
  }
@@ -5,14 +14,14 @@
5
14
  body {
6
15
  margin: 0;
7
16
  font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
8
- background: #f4f3f0;
9
- color: #0a0908;
17
+ background: var(--brand-background);
18
+ color: var(--brand-text);
10
19
  }
11
20
 
12
21
  .app-header {
13
22
  padding: 1rem 1.5rem;
14
- background: #0a0908;
15
- color: #fafaf9;
23
+ background: var(--brand-header-bg);
24
+ color: var(--brand-header-fg);
16
25
  }
17
26
 
18
27
  .app-header h1 {
@@ -79,7 +88,7 @@ body {
79
88
  }
80
89
 
81
90
  .playlist-catalog-item--active {
82
- border-color: #ff8a3d;
91
+ border-color: var(--brand-primary);
83
92
  background: #fff5ee;
84
93
  }
85
94
 
@@ -579,14 +588,14 @@ button:hover {
579
588
  }
580
589
 
581
590
  button.primary {
582
- background: #ff8a3d;
583
- border-color: #e67320;
584
- color: #0a0908;
591
+ background: var(--brand-primary);
592
+ border-color: color-mix(in srgb, var(--brand-primary) 82%, #000);
593
+ color: var(--brand-text);
585
594
  font-weight: 600;
586
595
  }
587
596
 
588
597
  button.primary:hover {
589
- background: #ff9a57;
598
+ background: color-mix(in srgb, var(--brand-primary) 88%, #fff);
590
599
  }
591
600
 
592
601
  button.danger {
@@ -670,7 +679,7 @@ button.danger {
670
679
  .upload-progress-bar {
671
680
  width: 0%;
672
681
  height: 100%;
673
- background: #ff8a3d;
682
+ background: var(--brand-primary);
674
683
  transition: width 120ms linear;
675
684
  }
676
685
 
@@ -710,7 +719,7 @@ button.danger {
710
719
  }
711
720
 
712
721
  .playlist-item--drop-target {
713
- border-color: #ff8a3d;
722
+ border-color: var(--brand-primary);
714
723
  box-shadow: 0 0 0 2px rgba(255, 138, 61, 0.25);
715
724
  }
716
725
 
@@ -1078,7 +1087,7 @@ button.danger {
1078
1087
  }
1079
1088
 
1080
1089
  .player-platform-link:focus-visible {
1081
- outline: 2px solid #ff8a3d;
1090
+ outline: 2px solid var(--brand-primary);
1082
1091
  outline-offset: 3px;
1083
1092
  }
1084
1093
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.41",
3
+ "version": "0.9.43",
4
4
  "description": "TomorrowOS CMS for Vercel / v0 Publish (Fluid Function + WebSockets + optional Next Preview).",
5
5
  "private": true,
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "build-player": "tomorrowos build --platform tizen"
18
18
  },
19
19
  "dependencies": {
20
- "@tomorrowos/sdk": "^0.9.41",
20
+ "@tomorrowos/sdk": "^0.9.43",
21
21
  "dotenv": "^17.2.3",
22
22
  "tsx": "^4.19.0"
23
23
  },