@tomorrowos/sdk 0.6.2 → 0.6.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.
@@ -109,6 +109,10 @@ export declare class TomorrowOS extends EventEmitter {
109
109
  };
110
110
  /** List paired devices with live connection + timing fields for the CMS panel. */
111
111
  listDevices(): Promise<DeviceListItem[]>;
112
+ setDeviceName(deviceId: string, deviceName: string): Promise<{
113
+ deviceId: string;
114
+ deviceName: string;
115
+ }>;
112
116
  private pushDeviceLog;
113
117
  private getDeviceLogs;
114
118
  private isDeviceConnected;
@@ -1 +1 @@
1
- {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAYxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAKV,eAAe,EAGhB,MAAM,kBAAkB,CAAC;AAc1B,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAkED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,EAAE,KAAK,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoKD,qBAAa,UAAW,SAAQ,YAAY;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;IACxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAClE,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,eAAe,CAAgB;gBAE3B,OAAO,EAAE,iBAAiB;IAOtC,yFAAyF;IACnF,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACxD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;KACtC,CAAC;YAkBY,iBAAiB;IAY/B,6EAA6E;IACvE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAiEhE,6EAA6E;IACvE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IA4BvF,OAAO;uBACU,MAAM;sBA/FgC,MAAM;;2BAgGxC,MAAM;sBA9BgC,MAAM;sBAAY,OAAO;;MA+BlF;IAEF,kFAAkF;IAC5E,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAuD9C,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,gBAAgB;YAkBV,+BAA+B;YAsC/B,iBAAiB;YASjB,iBAAiB;YAoCjB,kBAAkB;IAUhC,uFAAuF;IACvF,OAAO,CAAC,kBAAkB;YAmBZ,gBAAgB;YAMhB,uBAAuB;IAuCrC,MAAM,CAAC,QAAQ,EAAE,MAAM;oBAGD,CAAC,oBACT,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAU5E,OAAO,CAAC,mBAAmB;IA6D3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM;YAqD7B,iBAAiB;YAiDjB,uBAAuB;IAqFrC,OAAO,CAAC,oBAAoB;YASd,yBAAyB;YAQzB,4BAA4B;YAa5B,iCAAiC;YA4BjC,wBAAwB;YAiBxB,cAAc;IAiD5B,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,kBAAkB;IAI1B,OAAO,CAAC,mBAAmB;YAOb,oBAAoB;YAuCpB,yBAAyB;YAgBzB,uBAAuB;YAcvB,UAAU;YAuTV,gBAAgB;IAqG9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;CA4HzB"}
1
+ {"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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"}
@@ -352,6 +352,25 @@ export class TomorrowOS extends EventEmitter {
352
352
  };
353
353
  }));
354
354
  }
355
+ async setDeviceName(deviceId, deviceName) {
356
+ const id = String(deviceId || "").trim();
357
+ if (!id) {
358
+ throw new Error("deviceId is required");
359
+ }
360
+ const existing = await this.store.getPairedDevice(id);
361
+ if (!existing) {
362
+ throw new Error("Device is not paired");
363
+ }
364
+ const normalized = String(deviceName || "").trim();
365
+ if (!normalized) {
366
+ throw new Error("deviceName is required");
367
+ }
368
+ await this.store.setPairedDevice(id, {
369
+ ...existing,
370
+ deviceName: normalized
371
+ });
372
+ return { deviceId: id, deviceName: normalized };
373
+ }
355
374
  pushDeviceLog(deviceId, entry) {
356
375
  const key = String(deviceId || "").trim();
357
376
  if (!key)
@@ -949,6 +968,20 @@ export class TomorrowOS extends EventEmitter {
949
968
  sendJson(res, 200, { status: "success", devices });
950
969
  return;
951
970
  }
971
+ const deviceNamePatch = /^\/device\/([^/]+)\/name$/.exec(pathname);
972
+ if (req.method === "PATCH" && deviceNamePatch) {
973
+ const deviceId = decodeURIComponent(deviceNamePatch[1]);
974
+ const body = (await readJsonBody(req));
975
+ try {
976
+ const result = await this.setDeviceName(deviceId, typeof body.deviceName === "string" ? body.deviceName : "");
977
+ sendJson(res, 200, { status: "success", ...result });
978
+ }
979
+ catch (e) {
980
+ const msg = e instanceof Error ? e.message : "Rename failed";
981
+ sendJson(res, 400, { status: "failed", error: msg });
982
+ }
983
+ return;
984
+ }
952
985
  if (req.method === "GET" && pathname === "/playlists") {
953
986
  const playlists = await this.playlists.listPlaylists();
954
987
  sendJson(res, 200, { status: "success", playlists });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.6.2",
3
+ "version": "0.6.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.6.2",
3
+ "version": "0.6.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.6.2",
13
+ "@tomorrowos/sdk": "^0.6.4",
14
14
  "dotenv": "^17.2.3"
15
15
  },
16
16
  "devDependencies": {
@@ -17,6 +17,10 @@ let editorItems = [];
17
17
 
18
18
  /** @type {string|null} */
19
19
  let publishModalDeviceId = null;
20
+ /** @type {string|null} */
21
+ let editingDeviceNameId = null;
22
+ /** @type {string} */
23
+ let editingDeviceNameValue = "";
20
24
 
21
25
  let devicePollTimer = null;
22
26
  let uploadQueue = [];
@@ -660,13 +664,65 @@ function renderDeviceCards() {
660
664
 
661
665
  const header = document.createElement("div");
662
666
  header.className = "device-card-header";
667
+ const headerMain = document.createElement("div");
668
+ headerMain.className = "device-card-header-main";
663
669
  const led = document.createElement("span");
664
670
  led.className = `status-led ${device.connected ? "status-led--online" : "status-led--offline"}`;
665
671
  const title = document.createElement("h3");
666
672
  title.className = "device-card-title";
667
- title.textContent = device.deviceName || "Screen";
668
- header.appendChild(led);
669
- header.appendChild(title);
673
+ const isEditingName = editingDeviceNameId === device.deviceId;
674
+ if (isEditingName) {
675
+ const input = document.createElement("input");
676
+ input.type = "text";
677
+ input.className = "device-card-title-input";
678
+ input.value = editingDeviceNameValue || device.deviceName || "Screen";
679
+ input.placeholder = "Device name";
680
+ input.addEventListener("input", (ev) => {
681
+ editingDeviceNameValue = String(ev.target.value || "");
682
+ });
683
+ input.addEventListener("keydown", (ev) => {
684
+ if (ev.key === "Enter") {
685
+ ev.preventDefault();
686
+ void submitInlineDeviceRename(device.deviceId);
687
+ } else if (ev.key === "Escape") {
688
+ ev.preventDefault();
689
+ cancelInlineDeviceRename();
690
+ }
691
+ });
692
+ title.appendChild(input);
693
+ } else {
694
+ title.textContent = device.deviceName || "Screen";
695
+ }
696
+ headerMain.appendChild(led);
697
+ headerMain.appendChild(title);
698
+
699
+ const headerActions = document.createElement("div");
700
+ headerActions.className = "device-card-header-actions";
701
+ if (isEditingName) {
702
+ const saveBtn = document.createElement("button");
703
+ saveBtn.type = "button";
704
+ saveBtn.className = "primary";
705
+ saveBtn.textContent = "Save";
706
+ saveBtn.addEventListener("click", () =>
707
+ void submitInlineDeviceRename(device.deviceId)
708
+ );
709
+ const cancelBtn = document.createElement("button");
710
+ cancelBtn.type = "button";
711
+ cancelBtn.textContent = "Cancel";
712
+ cancelBtn.addEventListener("click", cancelInlineDeviceRename);
713
+ headerActions.appendChild(saveBtn);
714
+ headerActions.appendChild(cancelBtn);
715
+ } else {
716
+ const renameBtn = document.createElement("button");
717
+ renameBtn.type = "button";
718
+ renameBtn.textContent = "Rename";
719
+ renameBtn.addEventListener("click", () =>
720
+ startInlineDeviceRename(device.deviceId, device.deviceName)
721
+ );
722
+ headerActions.appendChild(renameBtn);
723
+ }
724
+ header.appendChild(headerMain);
725
+ header.appendChild(headerActions);
670
726
 
671
727
  const published = document.createElement("ul");
672
728
  published.className = "device-published-list";
@@ -1053,6 +1109,48 @@ async function unpairDevice(deviceId) {
1053
1109
  if (res.ok) await fetchDevices();
1054
1110
  }
1055
1111
 
1112
+ async function renameDevice(deviceId, currentName) {
1113
+ if (!deviceId) return false;
1114
+ const trimmed = String(currentName || "").trim();
1115
+ if (!trimmed) {
1116
+ alert("Device name cannot be empty.");
1117
+ return false;
1118
+ }
1119
+
1120
+ const res = await fetch(`/device/${encodeURIComponent(deviceId)}/name`, {
1121
+ method: "PATCH",
1122
+ headers: { "Content-Type": "application/json" },
1123
+ body: JSON.stringify({ deviceName: trimmed })
1124
+ });
1125
+ const data = await res.json();
1126
+ showResult({ deviceId, rename: data });
1127
+ if (!res.ok) {
1128
+ alert(data.error || "Rename failed");
1129
+ return false;
1130
+ }
1131
+ return true;
1132
+ }
1133
+
1134
+ function startInlineDeviceRename(deviceId, currentName) {
1135
+ editingDeviceNameId = deviceId;
1136
+ editingDeviceNameValue = String(currentName || "").trim() || "Screen";
1137
+ renderDeviceCards();
1138
+ }
1139
+
1140
+ function cancelInlineDeviceRename() {
1141
+ editingDeviceNameId = null;
1142
+ editingDeviceNameValue = "";
1143
+ renderDeviceCards();
1144
+ }
1145
+
1146
+ async function submitInlineDeviceRename(deviceId) {
1147
+ const ok = await renameDevice(deviceId, editingDeviceNameValue);
1148
+ if (!ok) return;
1149
+ editingDeviceNameId = null;
1150
+ editingDeviceNameValue = "";
1151
+ await fetchDevices();
1152
+ }
1153
+
1056
1154
  async function deviceAction(deviceId, action) {
1057
1155
  if (!deviceId) return;
1058
1156
  if (action === "reboot" && !confirm("Reboot this device?")) return;
@@ -141,15 +141,45 @@ body {
141
141
  .device-card-header {
142
142
  display: flex;
143
143
  align-items: center;
144
- gap: 0.5rem;
144
+ justify-content: space-between;
145
+ gap: 0.35rem;
145
146
  margin-bottom: 0.65rem;
146
147
  }
147
148
 
149
+ .device-card-header-main {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: 0.5rem;
153
+ min-width: 0;
154
+ flex: 1;
155
+ }
156
+
157
+ .device-card-header-actions {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 0.35rem;
161
+ flex-shrink: 0;
162
+ }
163
+
164
+ .device-card-header-actions button {
165
+ font-size: 0.78rem;
166
+ padding: 0.35rem 0.6rem;
167
+ }
168
+
148
169
  .device-card-title {
149
170
  margin: 0;
150
171
  font-size: 0.95rem;
151
172
  font-weight: 600;
152
173
  line-height: 1.2;
174
+ min-width: 0;
175
+ flex: 1;
176
+ }
177
+
178
+ .device-card-title-input {
179
+ width: 100%;
180
+ min-width: 0;
181
+ font-size: 0.95rem;
182
+ font-weight: 600;
153
183
  }
154
184
 
155
185
  .status-led {