@tomorrowos/sdk 0.7.2 → 0.7.4

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.7.2",
3
+ "version": "0.7.4",
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.7.2",
3
+ "version": "0.7.4",
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.7.2",
13
+ "@tomorrowos/sdk": "^0.7.4",
14
14
  "dotenv": "^17.2.3"
15
15
  },
16
16
  "devDependencies": {
@@ -103,6 +103,7 @@
103
103
  <h2>Assets</h2>
104
104
  <button type="button" class="primary" id="addAssetBtn" title="Add image or video">+</button>
105
105
  </div>
106
+ <p class="hint playlist-assets-hint">Drag ⋮⋮ to reorder assets. Save playlist to keep the new order.</p>
106
107
  <input
107
108
  type="file"
108
109
  id="fileInput"
@@ -15,6 +15,9 @@ let playlistDraftActive = false;
15
15
  /** @type {{ id: string, assetId?: string, url: string, name: string, type: string, durationMs: number }[]} */
16
16
  let editorItems = [];
17
17
 
18
+ /** Item id currently being dragged in the asset list (reorder). */
19
+ let draggingEditorItemId = null;
20
+
18
21
  /** @type {string|null} */
19
22
  let publishModalDeviceId = null;
20
23
  let publishInProgress = false;
@@ -670,9 +673,75 @@ function loadEditorFromSelection() {
670
673
  updatePlaylistEditorVisibility();
671
674
  }
672
675
 
676
+ function clearPlaylistItemDropTargets() {
677
+ document
678
+ .querySelectorAll(".playlist-item--drop-target, .playlist-item--dragging")
679
+ .forEach((el) => {
680
+ el.classList.remove("playlist-item--drop-target", "playlist-item--dragging");
681
+ });
682
+ }
683
+
684
+ function reorderEditorItems(fromId, toId) {
685
+ const fromIdx = editorItems.findIndex((x) => x.id === fromId);
686
+ const toIdx = editorItems.findIndex((x) => x.id === toId);
687
+ if (fromIdx < 0 || toIdx < 0 || fromIdx === toIdx) return;
688
+ const [moved] = editorItems.splice(fromIdx, 1);
689
+ editorItems.splice(toIdx, 0, moved);
690
+ renderEditorAssets();
691
+ }
692
+
693
+ function attachPlaylistItemDragDrop(li, item) {
694
+ li.dataset.itemId = item.id;
695
+
696
+ const handle = document.createElement("button");
697
+ handle.type = "button";
698
+ handle.className = "playlist-item-drag-handle";
699
+ handle.setAttribute("aria-label", "Drag to reorder");
700
+ handle.title = "Drag to reorder";
701
+ handle.textContent = "⋮⋮";
702
+ handle.draggable = true;
703
+
704
+ handle.addEventListener("dragstart", (e) => {
705
+ e.dataTransfer.setData("text/plain", item.id);
706
+ e.dataTransfer.effectAllowed = "move";
707
+ draggingEditorItemId = item.id;
708
+ li.classList.add("playlist-item--dragging");
709
+ });
710
+
711
+ handle.addEventListener("dragend", () => {
712
+ draggingEditorItemId = null;
713
+ clearPlaylistItemDropTargets();
714
+ });
715
+
716
+ li.addEventListener("dragover", (e) => {
717
+ e.preventDefault();
718
+ e.dataTransfer.dropEffect = "move";
719
+ if (draggingEditorItemId && draggingEditorItemId !== item.id) {
720
+ li.classList.add("playlist-item--drop-target");
721
+ }
722
+ });
723
+
724
+ li.addEventListener("dragleave", (e) => {
725
+ if (!li.contains(e.relatedTarget)) {
726
+ li.classList.remove("playlist-item--drop-target");
727
+ }
728
+ });
729
+
730
+ li.addEventListener("drop", (e) => {
731
+ e.preventDefault();
732
+ li.classList.remove("playlist-item--drop-target");
733
+ const fromId = e.dataTransfer.getData("text/plain") || draggingEditorItemId;
734
+ if (!fromId || fromId === item.id) return;
735
+ reorderEditorItems(fromId, item.id);
736
+ });
737
+
738
+ li.insertBefore(handle, li.firstChild);
739
+ }
740
+
673
741
  function renderEditorAssets() {
674
742
  const list = document.getElementById("playlistList");
675
743
  const empty = document.getElementById("playlistEmpty");
744
+ draggingEditorItemId = null;
676
745
  list.querySelectorAll(".playlist-item").forEach((el) => el.remove());
677
746
 
678
747
  if (!isPlaylistEditorOpen()) {
@@ -696,6 +765,7 @@ function renderEditorAssets() {
696
765
  if (item.type === "image" || item.type === "video") {
697
766
  const thumb = document.createElement(item.type === "video" ? "video" : "img");
698
767
  thumb.className = "playlist-item-thumb";
768
+ thumb.draggable = false;
699
769
  try {
700
770
  thumb.src = absoluteMediaUrl(item.url);
701
771
  } catch {
@@ -752,6 +822,7 @@ function renderEditorAssets() {
752
822
  li.appendChild(name);
753
823
  li.appendChild(meta);
754
824
  li.appendChild(actions);
825
+ attachPlaylistItemDragDrop(li, item);
755
826
  list.appendChild(li);
756
827
  }
757
828
  }
@@ -120,7 +120,7 @@ body {
120
120
 
121
121
  .devices-grid {
122
122
  display: grid;
123
- grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
123
+ grid-template-columns: repeat(auto-fill, minmax(308px, 1fr));
124
124
  gap: 0.85rem;
125
125
  }
126
126
 
@@ -214,14 +214,16 @@ body {
214
214
 
215
215
  .device-meta-top {
216
216
  display: grid;
217
- grid-template-columns: minmax(0, 1fr) 92px;
218
- gap: 0.65rem;
219
- align-items: stretch;
217
+ grid-template-columns: minmax(0, 1fr) 6.5rem;
218
+ gap: 0.5rem;
219
+ align-items: start;
220
220
  margin-bottom: 0.35rem;
221
221
  }
222
222
 
223
223
  .device-meta--primary {
224
224
  min-width: 0;
225
+ font-size: 0.77rem;
226
+ gap: 0.32rem;
225
227
  }
226
228
 
227
229
  .device-meta--secondary {
@@ -229,11 +231,11 @@ body {
229
231
  }
230
232
 
231
233
  .device-screenshot-slot {
232
- width: 92px;
233
- min-height: 4.85rem;
234
+ width: 6.5rem;
235
+ aspect-ratio: 13 / 9;
234
236
  flex-shrink: 0;
235
237
  border: 1px solid transparent;
236
- border-radius: 6px;
238
+ border-radius: 4px;
237
239
  background: #fafaf9;
238
240
  overflow: hidden;
239
241
  }
@@ -242,7 +244,6 @@ body {
242
244
  display: block;
243
245
  width: 100%;
244
246
  height: 100%;
245
- min-height: 4.85rem;
246
247
  padding: 0;
247
248
  border: 0;
248
249
  background: transparent;
@@ -253,7 +254,6 @@ body {
253
254
  display: block;
254
255
  width: 100%;
255
256
  height: 100%;
256
- min-height: 4.85rem;
257
257
  object-fit: cover;
258
258
  object-position: center;
259
259
  }
@@ -429,12 +429,52 @@ button.danger {
429
429
  font-size: 0.875rem;
430
430
  }
431
431
 
432
+ .playlist-assets-hint {
433
+ margin: 0 0 0.75rem;
434
+ font-size: 0.78rem;
435
+ }
436
+
432
437
  .playlist-item {
433
438
  border: 1px solid #e4e1dc;
434
439
  border-radius: 8px;
435
440
  padding: 0.65rem;
436
441
  margin-bottom: 0.5rem;
437
442
  background: #fafaf9;
443
+ position: relative;
444
+ }
445
+
446
+ .playlist-item--dragging {
447
+ opacity: 0.55;
448
+ }
449
+
450
+ .playlist-item--drop-target {
451
+ border-color: #ff8a3d;
452
+ box-shadow: 0 0 0 2px rgba(255, 138, 61, 0.25);
453
+ }
454
+
455
+ .playlist-item-drag-handle {
456
+ display: block;
457
+ width: 100%;
458
+ margin: 0 0 0.45rem;
459
+ padding: 0.2rem 0;
460
+ border: 0;
461
+ border-radius: 4px;
462
+ background: transparent;
463
+ color: #888;
464
+ font-size: 0.95rem;
465
+ line-height: 1;
466
+ letter-spacing: 0.08em;
467
+ cursor: grab;
468
+ touch-action: none;
469
+ }
470
+
471
+ .playlist-item-drag-handle:active {
472
+ cursor: grabbing;
473
+ }
474
+
475
+ .playlist-item-drag-handle:hover {
476
+ color: #444;
477
+ background: #f0eeea;
438
478
  }
439
479
 
440
480
  .playlist-item-thumb {