@tomorrowos/sdk 0.3.0 → 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/dist/cli.js
CHANGED
|
@@ -11,6 +11,27 @@ function getSdkVersion() {
|
|
|
11
11
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
12
12
|
return pkg.version ?? "0.0.0";
|
|
13
13
|
}
|
|
14
|
+
const STARTER_SKIP_DIRS = new Set(["node_modules"]);
|
|
15
|
+
/** Never ship local install artifacts into a new CMS project. */
|
|
16
|
+
function shouldCopyStarterEntry(starterRoot, source) {
|
|
17
|
+
const rel = path.relative(starterRoot, source);
|
|
18
|
+
if (!rel)
|
|
19
|
+
return true;
|
|
20
|
+
const parts = rel.split(path.sep).filter(Boolean);
|
|
21
|
+
if (parts.some((p) => STARTER_SKIP_DIRS.has(p)))
|
|
22
|
+
return false;
|
|
23
|
+
if (path.basename(source) === "package-lock.json")
|
|
24
|
+
return false;
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function removeStarterArtifacts(destDir) {
|
|
28
|
+
const lockPath = path.join(destDir, "package-lock.json");
|
|
29
|
+
const modulesPath = path.join(destDir, "node_modules");
|
|
30
|
+
if (fs.existsSync(lockPath))
|
|
31
|
+
fs.rmSync(lockPath, { force: true });
|
|
32
|
+
if (fs.existsSync(modulesPath))
|
|
33
|
+
fs.rmSync(modulesPath, { recursive: true, force: true });
|
|
34
|
+
}
|
|
14
35
|
/** Align generated cms-starter package.json with the installed SDK version. */
|
|
15
36
|
function patchStarterPackageJson(destDir) {
|
|
16
37
|
const pkgPath = path.join(path.resolve(destDir), "package.json");
|
|
@@ -41,12 +62,16 @@ function copyStarter(destDir, force) {
|
|
|
41
62
|
else {
|
|
42
63
|
fs.mkdirSync(resolved, { recursive: true });
|
|
43
64
|
}
|
|
44
|
-
fs.cpSync(src, resolved, {
|
|
65
|
+
fs.cpSync(src, resolved, {
|
|
66
|
+
recursive: true,
|
|
67
|
+
filter: (source) => shouldCopyStarterEntry(src, source),
|
|
68
|
+
});
|
|
45
69
|
patchStarterPackageJson(resolved);
|
|
70
|
+
removeStarterArtifacts(resolved);
|
|
46
71
|
const sdkVer = getSdkVersion();
|
|
47
72
|
console.log(`[tomorrowos] Created CMS project at ${resolved}`);
|
|
48
73
|
console.log(`[tomorrowos] @tomorrowos/sdk dependency: ^${sdkVer}`);
|
|
49
|
-
console.log("Next: cd there, run npm install, then npm
|
|
74
|
+
console.log("Next: cd there, run npm install, then npm start");
|
|
50
75
|
}
|
|
51
76
|
function cmdBuild(argv) {
|
|
52
77
|
const platformIdx = argv.indexOf("--platform");
|
package/dist/tomorrowos.js
CHANGED
|
@@ -794,6 +794,7 @@ export class TomorrowOS extends EventEmitter {
|
|
|
794
794
|
createdAt: Date.now()
|
|
795
795
|
});
|
|
796
796
|
ws.deviceId = deviceId;
|
|
797
|
+
this.sendBrandSnapshot(ws);
|
|
797
798
|
ws.send(JSON.stringify({
|
|
798
799
|
type: "pairing.code",
|
|
799
800
|
method: "tomorrowos.pairing.createCode",
|
|
@@ -801,7 +802,6 @@ export class TomorrowOS extends EventEmitter {
|
|
|
801
802
|
deviceId,
|
|
802
803
|
serialNumber
|
|
803
804
|
}));
|
|
804
|
-
this.sendBrandSnapshot(ws);
|
|
805
805
|
this.emit("device.online", { deviceId });
|
|
806
806
|
}
|
|
807
807
|
catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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.2
|
|
3
|
+
"version": "0.3.2",
|
|
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",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"build-player": "tomorrowos build --platform tizen"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@tomorrowos/sdk": "^0.2
|
|
13
|
+
"@tomorrowos/sdk": "^0.3.2"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^20.0.0",
|
|
@@ -9,6 +9,9 @@ let devicesCache = [];
|
|
|
9
9
|
/** @type {string|null} */
|
|
10
10
|
let selectedPlaylistId = null;
|
|
11
11
|
|
|
12
|
+
/** True while creating a new playlist locally (assets allowed before Save). */
|
|
13
|
+
let playlistDraftActive = false;
|
|
14
|
+
|
|
12
15
|
/** @type {{ id: string, url: string, name: string, type: string, durationMs: number }[]} */
|
|
13
16
|
let editorItems = [];
|
|
14
17
|
|
|
@@ -171,15 +174,36 @@ function getSelectedPlaylist() {
|
|
|
171
174
|
}
|
|
172
175
|
|
|
173
176
|
async function fetchPlaylists() {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch("/playlists");
|
|
179
|
+
let data = {};
|
|
180
|
+
try {
|
|
181
|
+
data = await res.json();
|
|
182
|
+
} catch {
|
|
183
|
+
data = {};
|
|
184
|
+
}
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
console.warn("[CMS] GET /playlists failed", res.status, data);
|
|
187
|
+
if (res.status === 404) {
|
|
188
|
+
showResult({
|
|
189
|
+
status: "failed",
|
|
190
|
+
error:
|
|
191
|
+
"CMS server is missing /playlists. Restart CMS with @tomorrowos/sdk 0.3.2 or newer."
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return;
|
|
182
195
|
}
|
|
196
|
+
if (Array.isArray(data.playlists)) {
|
|
197
|
+
playlistsCatalog = data.playlists;
|
|
198
|
+
renderPlaylistCatalog();
|
|
199
|
+
if (selectedPlaylistId && !getSelectedPlaylist()) {
|
|
200
|
+
selectedPlaylistId = playlistsCatalog[0]?.id || null;
|
|
201
|
+
loadEditorFromSelection();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.error("[CMS] fetchPlaylists:", err);
|
|
206
|
+
showResult({ status: "failed", error: err.message });
|
|
183
207
|
}
|
|
184
208
|
}
|
|
185
209
|
|
|
@@ -202,6 +226,7 @@ function renderPlaylistCatalog() {
|
|
|
202
226
|
if (pl.id === selectedPlaylistId) li.classList.add("playlist-catalog-item--active");
|
|
203
227
|
li.innerHTML = `<strong>${escapeHtml(pl.name)}</strong><small>v${pl.version} · ${(pl.items || []).length} items</small>`;
|
|
204
228
|
li.addEventListener("click", () => {
|
|
229
|
+
playlistDraftActive = false;
|
|
205
230
|
selectedPlaylistId = pl.id;
|
|
206
231
|
loadEditorFromSelection();
|
|
207
232
|
renderPlaylistCatalog();
|
|
@@ -210,12 +235,22 @@ function renderPlaylistCatalog() {
|
|
|
210
235
|
}
|
|
211
236
|
}
|
|
212
237
|
|
|
238
|
+
function isPlaylistEditorOpen() {
|
|
239
|
+
return playlistDraftActive || !!selectedPlaylistId;
|
|
240
|
+
}
|
|
241
|
+
|
|
213
242
|
function loadEditorFromSelection() {
|
|
214
243
|
const pl = getSelectedPlaylist();
|
|
215
244
|
const nameInput = document.getElementById("playlistName");
|
|
216
245
|
const editorTitle = document.getElementById("editorTitle");
|
|
217
246
|
|
|
218
247
|
if (!pl) {
|
|
248
|
+
if (playlistDraftActive) {
|
|
249
|
+
if (editorTitle) editorTitle.textContent = "New playlist";
|
|
250
|
+
renderEditorAssets();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
playlistDraftActive = false;
|
|
219
254
|
if (editorTitle) editorTitle.textContent = "Playlist editor";
|
|
220
255
|
if (nameInput) nameInput.value = "";
|
|
221
256
|
editorItems = [];
|
|
@@ -224,6 +259,7 @@ function loadEditorFromSelection() {
|
|
|
224
259
|
return;
|
|
225
260
|
}
|
|
226
261
|
|
|
262
|
+
playlistDraftActive = false;
|
|
227
263
|
if (editorTitle) editorTitle.textContent = `Edit: ${pl.name}`;
|
|
228
264
|
if (nameInput) nameInput.value = pl.name || "";
|
|
229
265
|
loadScheduleIntoForm(pl.schedule);
|
|
@@ -242,11 +278,15 @@ function renderEditorAssets() {
|
|
|
242
278
|
const empty = document.getElementById("playlistEmpty");
|
|
243
279
|
list.querySelectorAll(".playlist-item").forEach((el) => el.remove());
|
|
244
280
|
|
|
245
|
-
if (!
|
|
281
|
+
if (!isPlaylistEditorOpen()) {
|
|
282
|
+
empty.classList.remove("hidden");
|
|
283
|
+
empty.textContent = "Select or create a playlist (Playlists +).";
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (editorItems.length === 0) {
|
|
246
288
|
empty.classList.remove("hidden");
|
|
247
|
-
empty.textContent =
|
|
248
|
-
? "No assets yet. Tap +."
|
|
249
|
-
: "Select or create a playlist.";
|
|
289
|
+
empty.textContent = "No assets yet. Tap + to upload.";
|
|
250
290
|
return;
|
|
251
291
|
}
|
|
252
292
|
|
|
@@ -352,6 +392,7 @@ async function saveCurrentPlaylist() {
|
|
|
352
392
|
return;
|
|
353
393
|
}
|
|
354
394
|
|
|
395
|
+
playlistDraftActive = false;
|
|
355
396
|
selectedPlaylistId = data.playlist?.id || selectedPlaylistId;
|
|
356
397
|
await fetchPlaylists();
|
|
357
398
|
loadEditorFromSelection();
|
|
@@ -382,6 +423,7 @@ async function deleteCurrentPlaylist() {
|
|
|
382
423
|
}
|
|
383
424
|
|
|
384
425
|
selectedPlaylistId = null;
|
|
426
|
+
playlistDraftActive = false;
|
|
385
427
|
editorItems = [];
|
|
386
428
|
await fetchPlaylists();
|
|
387
429
|
loadEditorFromSelection();
|
|
@@ -389,12 +431,23 @@ async function deleteCurrentPlaylist() {
|
|
|
389
431
|
|
|
390
432
|
function newPlaylistDraft() {
|
|
391
433
|
selectedPlaylistId = null;
|
|
392
|
-
|
|
434
|
+
playlistDraftActive = true;
|
|
435
|
+
const nameInput = document.getElementById("playlistName");
|
|
436
|
+
if (nameInput) {
|
|
437
|
+
nameInput.value = "";
|
|
438
|
+
nameInput.focus();
|
|
439
|
+
}
|
|
393
440
|
loadScheduleIntoForm(null);
|
|
394
441
|
editorItems = [];
|
|
395
442
|
renderPlaylistCatalog();
|
|
396
443
|
renderEditorAssets();
|
|
397
|
-
document.getElementById("editorTitle")
|
|
444
|
+
const editorTitle = document.getElementById("editorTitle");
|
|
445
|
+
if (editorTitle) editorTitle.textContent = "New playlist";
|
|
446
|
+
document.getElementById("playlistEditorSection")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
447
|
+
showResult({
|
|
448
|
+
status: "draft",
|
|
449
|
+
message: "New playlist — enter a name, add assets with + (right), then Save playlist."
|
|
450
|
+
});
|
|
398
451
|
}
|
|
399
452
|
|
|
400
453
|
async function fetchDevices() {
|
|
@@ -621,9 +674,8 @@ async function uploadFile(file) {
|
|
|
621
674
|
}
|
|
622
675
|
|
|
623
676
|
async function addAssetFromFile(file) {
|
|
624
|
-
if (!
|
|
625
|
-
|
|
626
|
-
return;
|
|
677
|
+
if (!isPlaylistEditorOpen()) {
|
|
678
|
+
newPlaylistDraft();
|
|
627
679
|
}
|
|
628
680
|
const data = await uploadFile(file);
|
|
629
681
|
const type = inferMediaType(file.name, file.type);
|
|
@@ -711,7 +763,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
711
763
|
});
|
|
712
764
|
|
|
713
765
|
document.getElementById("addAssetBtn")?.addEventListener("click", () => {
|
|
714
|
-
|
|
766
|
+
if (!isPlaylistEditorOpen()) {
|
|
767
|
+
newPlaylistDraft();
|
|
768
|
+
}
|
|
769
|
+
document.getElementById("fileInput")?.click();
|
|
715
770
|
});
|
|
716
771
|
|
|
717
772
|
document.getElementById("fileInput")?.addEventListener("change", async (ev) => {
|