@tomorrowos/sdk 0.7.3 → 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.3",
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.3",
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.3",
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
  }
@@ -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 {