livedesk 0.1.407 → 0.1.409

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livedesk/client",
3
- "version": "0.1.173",
3
+ "version": "0.1.174",
4
4
  "description": "LiveDesk local remote client",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,10 +39,10 @@
39
39
  "ws": "^8.18.3"
40
40
  },
41
41
  "optionalDependencies": {
42
- "@livedesk/fast-linux-x64": "0.1.379",
43
- "@livedesk/fast-osx-arm64": "0.1.379",
44
- "@livedesk/fast-osx-x64": "0.1.379",
45
- "@livedesk/fast-win-x64": "0.1.379"
42
+ "@livedesk/fast-linux-x64": "0.1.380",
43
+ "@livedesk/fast-osx-arm64": "0.1.380",
44
+ "@livedesk/fast-osx-x64": "0.1.380",
45
+ "@livedesk/fast-win-x64": "0.1.380"
46
46
  },
47
47
  "publishConfig": {
48
48
  "access": "public"
@@ -16,10 +16,13 @@ asarUnpack:
16
16
  - bin/**
17
17
  - bootstrap/**
18
18
  - client/**
19
+ - electron/icon.*
20
+ - electron/tray.*
19
21
  - hub/**
20
22
  - runtime-core/**
21
23
  - web/**
22
24
  mac:
25
+ icon: packages/livedesk/electron/icon.icns
23
26
  category: public.app-category.utilities
24
27
  target:
25
28
  - target: dmg
@@ -16,10 +16,13 @@ asarUnpack:
16
16
  - bin/**
17
17
  - bootstrap/**
18
18
  - client/**
19
+ - electron/icon.*
20
+ - electron/tray.*
19
21
  - hub/**
20
22
  - runtime-core/**
21
23
  - web/**
22
24
  mac:
25
+ icon: packages/livedesk/electron/icon.icns
23
26
  category: public.app-category.utilities
24
27
  target:
25
28
  - target: dmg
@@ -16,10 +16,13 @@ asarUnpack:
16
16
  - bin/**
17
17
  - bootstrap/**
18
18
  - client/**
19
+ - electron/icon.*
20
+ - electron/tray.*
19
21
  - hub/**
20
22
  - runtime-core/**
21
23
  - web/**
22
24
  win:
25
+ icon: packages/livedesk/electron/icon.ico
23
26
  target:
24
27
  - target: nsis
25
28
  arch:
Binary file
Binary file
Binary file
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024" fill="none">
2
+ <rect x="24" y="24" width="976" height="976" rx="220" fill="#111827"/>
3
+ <g transform="translate(192 192) scale(22.8571428571)">
4
+ <rect x="2.5" y="4" width="14" height="9" rx="1.5" stroke="#F8FAFC" stroke-width="1.7" opacity="0.42"/>
5
+ <path d="M7 16v1.5h5" stroke="#F8FAFC" stroke-width="1.7" stroke-linecap="round" opacity="0.42"/>
6
+ <rect x="10.5" y="2.5" width="14" height="9" rx="1.5" stroke="#F8FAFC" stroke-width="1.7" opacity="0.62"/>
7
+ <path d="M15 14.5V16h5" stroke="#F8FAFC" stroke-width="1.7" stroke-linecap="round" opacity="0.62"/>
8
+ <rect x="6.5" y="8.5" width="15" height="10" rx="1.7" fill="#F8FAFC" fill-opacity="0.12" stroke="#F8FAFC" stroke-width="1.9"/>
9
+ <path d="M12 21v1.5H9.5M16 21v1.5h2.5M9.5 22.5h9" stroke="#F8FAFC" stroke-width="1.9" stroke-linecap="round"/>
10
+ </g>
11
+ </svg>
package/electron/main.mjs CHANGED
@@ -69,7 +69,7 @@ let productUpdates = null;
69
69
  let pendingOAuth = null;
70
70
  let pendingProtocolUrl = '';
71
71
  let runtimeLogoutPromise = null;
72
- const TRAY_ICON_DATA_URL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="#111827"/><path d="M9 23V9h4l3 5 3-5h4v14h-4v-7l-3 5-3-5v7H9Z" fill="#5eead4"/></svg>')}`;
72
+ const FALLBACK_ICON_DATA_URL = `data:image/svg+xml;charset=utf-8,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none"><rect x="1" y="1" width="30" height="30" rx="7" fill="#111827"/><g transform="translate(5 5) scale(.785714)"><rect x="2.5" y="4" width="14" height="9" rx="1.5" stroke="#F8FAFC" stroke-width="1.7" opacity=".42"/><path d="M7 16v1.5h5" stroke="#F8FAFC" stroke-width="1.7" stroke-linecap="round" opacity=".42"/><rect x="10.5" y="2.5" width="14" height="9" rx="1.5" stroke="#F8FAFC" stroke-width="1.7" opacity=".62"/><path d="M15 14.5V16h5" stroke="#F8FAFC" stroke-width="1.7" stroke-linecap="round" opacity=".62"/><rect x="6.5" y="8.5" width="15" height="10" rx="1.7" fill="#F8FAFC" fill-opacity=".12" stroke="#F8FAFC" stroke-width="1.9"/><path d="M12 21v1.5H9.5M16 21v1.5h2.5M9.5 22.5h9" stroke="#F8FAFC" stroke-width="1.9" stroke-linecap="round"/></g></svg>')}`;
73
73
 
74
74
  function mask(value) {
75
75
  return String(value ?? '')
@@ -595,14 +595,16 @@ function showWindow() {
595
595
  mainWindow.focus();
596
596
  }
597
597
 
598
- function createMainWindow() {
599
- mainWindow = new BrowserWindow({
598
+ function createMainWindow() {
599
+ const iconPath = appResource('electron', 'icon.png');
600
+ mainWindow = new BrowserWindow({
600
601
  width: 1440,
601
602
  height: 960,
602
603
  minWidth: 1080,
603
- minHeight: 720,
604
- show: false,
605
- title: APP_NAME,
604
+ minHeight: 720,
605
+ show: false,
606
+ title: APP_NAME,
607
+ icon: existsSync(iconPath) ? iconPath : nativeImage.createFromDataURL(FALLBACK_ICON_DATA_URL),
606
608
  webPreferences: {
607
609
  preload: appResource('electron', 'preload.cjs'),
608
610
  contextIsolation: true,
@@ -707,9 +709,9 @@ function registerIpc() {
707
709
  handle('updates:install', () => productUpdates?.install() || Promise.resolve({ state: 'unavailable' }));
708
710
  }
709
711
 
710
- function createTray() {
711
- const iconPath = appResource('electron', 'tray.ico');
712
- tray = new Tray(existsSync(iconPath) ? iconPath : nativeImage.createFromDataURL(TRAY_ICON_DATA_URL));
712
+ function createTray() {
713
+ const iconPath = appResource('electron', process.platform === 'win32' ? 'tray.ico' : 'tray.png');
714
+ tray = new Tray(existsSync(iconPath) ? iconPath : nativeImage.createFromDataURL(FALLBACK_ICON_DATA_URL));
713
715
  const menu = Menu.buildFromTemplate([
714
716
  { label: 'Show LiveDesk', click: showWindow },
715
717
  { label: 'Restart runtime', click: restartRuntimeProcess },
Binary file
Binary file
@@ -0,0 +1,32 @@
1
+ const TILE_PRESETS = Object.freeze([
2
+ Object.freeze({ width: 320, height: 180 }),
3
+ Object.freeze({ width: 480, height: 270 }),
4
+ Object.freeze({ width: 640, height: 360 })
5
+ ]);
6
+
7
+ export function resolveMode4AtlasTileSize(options = {}) {
8
+ const deviceCount = Math.max(1, Math.floor(Number(options.deviceCount) || 1));
9
+ const atlasWidth = Math.max(640, Math.floor(Number(options.atlasWidth) || 1920));
10
+ const atlasHeight = Math.max(360, Math.floor(Number(options.atlasHeight) || 1080));
11
+ const requestedWidth = Number(options.requestedWidth) || 320;
12
+ const requestedHeight = Number(options.requestedHeight) || 180;
13
+ const columns = Math.max(1, Math.ceil(Math.sqrt(deviceCount)));
14
+ const rows = Math.max(1, Math.ceil(deviceCount / columns));
15
+ const requestedPresetIndex = requestedWidth >= 640 || requestedHeight >= 360
16
+ ? 2
17
+ : requestedWidth >= 480 || requestedHeight >= 270
18
+ ? 1
19
+ : 0;
20
+ const supportedCandidates = TILE_PRESETS.slice(0, requestedPresetIndex + 1).reverse();
21
+ const selected = supportedCandidates.find(candidate => (
22
+ candidate.width * columns <= atlasWidth
23
+ && candidate.height * rows <= atlasHeight
24
+ )) || TILE_PRESETS[0];
25
+
26
+ return {
27
+ width: selected.width,
28
+ height: selected.height,
29
+ columns,
30
+ rows
31
+ };
32
+ }
@@ -4,6 +4,7 @@ import { spawn } from 'node:child_process';
4
4
  import { createRequire } from 'node:module';
5
5
  import { existsSync } from 'node:fs';
6
6
  import { decodeLzo1xBlock } from './lzo1x.js';
7
+ import { resolveMode4AtlasTileSize } from './mode4-atlas-sizing.js';
7
8
 
8
9
  const require = createRequire(import.meta.url);
9
10
  const DEFAULT_WIDTH = 1920;
@@ -11,8 +12,8 @@ const DEFAULT_HEIGHT = 1080;
11
12
  const DEFAULT_FPS = 20;
12
13
  const MAX_DEVICES = 100;
13
14
  const FRAME_POOL_SIZE = 3;
14
- const MAX_TILE_WIDTH = 480;
15
- const MAX_TILE_HEIGHT = 270;
15
+ const MAX_TILE_WIDTH = 640;
16
+ const MAX_TILE_HEIGHT = 360;
16
17
 
17
18
  const rgb565Red = new Uint8Array(65_536);
18
19
  const rgb565Green = new Uint8Array(65_536);
@@ -59,12 +60,18 @@ function normalizeConfig(value) {
59
60
  .map(item => String(item || '').trim()).filter(Boolean))].slice(0, MAX_DEVICES);
60
61
  const maxWidth = clamp(value?.width, 640, 3840, DEFAULT_WIDTH);
61
62
  const maxHeight = clamp(value?.height, 360, 2160, DEFAULT_HEIGHT);
62
- const sharpTiles = Number(value?.tileWidth) >= 480 || Number(value?.tileHeight) >= 270;
63
- const sourceTileWidth = sharpTiles ? 480 : 320;
64
- const sourceTileHeight = sharpTiles ? 270 : 180;
65
63
  const count = Math.max(1, deviceIds.length);
66
- const columns = Math.max(1, Math.ceil(Math.sqrt(count)));
67
- const rows = Math.max(1, Math.ceil(count / columns));
64
+ const tileSize = resolveMode4AtlasTileSize({
65
+ requestedWidth: value?.tileWidth,
66
+ requestedHeight: value?.tileHeight,
67
+ deviceCount: count,
68
+ atlasWidth: maxWidth,
69
+ atlasHeight: maxHeight
70
+ });
71
+ const columns = tileSize.columns;
72
+ const rows = tileSize.rows;
73
+ const sourceTileWidth = tileSize.width;
74
+ const sourceTileHeight = tileSize.height;
68
75
  const tileWidth = Math.max(2, Math.min(sourceTileWidth, Math.floor(maxWidth / columns))) & ~1;
69
76
  const tileHeight = Math.max(2, Math.min(sourceTileHeight, Math.floor(maxHeight / rows))) & ~1;
70
77
  return {
package/hub/src/server.js CHANGED
@@ -8,8 +8,9 @@ import { dirname, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import os from 'node:os';
10
10
  import { WebSocketServer } from 'ws';
11
- import { createRemoteHub } from './remote-hub.js';
11
+ import { createRemoteHub } from './remote-hub.js';
12
12
  import { buildMode4AtlasSessionKey, Mode4AtlasPool, planMode4AtlasInputTransitions } from './mode4-atlas-pool.js';
13
+ import { resolveMode4AtlasTileSize } from './mode4-atlas-sizing.js';
13
14
  import { createHubFilesystem } from './filesystem/hub-filesystem.js';
14
15
  import { createHubTransferJobs } from './filesystem/transfer-jobs.js';
15
16
  import { createHubSharedFolders } from './filesystem/shared-folders.js';
@@ -2287,13 +2288,21 @@ function sendMode4AtlasFrame(ws, output) {
2287
2288
  }
2288
2289
 
2289
2290
  function configureMode4Atlas(ws, payload = {}) {
2290
- const deviceIds = normalizeDeviceIds(payload.deviceIds ?? payload.devices ?? payload.deviceId).slice(0, 100);
2291
- const captureDeviceIds = normalizeDeviceIds(payload.captureDeviceIds ?? payload.inputDeviceIds ?? deviceIds)
2292
- .filter(deviceId => deviceIds.includes(deviceId));
2293
- const monitorSelections = normalizeMonitorSelections(payload.monitorSelections);
2294
- const sharpTiles = Number(payload.tileWidth) >= 480 || Number(payload.tileHeight) >= 270;
2295
- const tileWidth = sharpTiles ? 480 : 320;
2296
- const tileHeight = sharpTiles ? 270 : 180;
2291
+ const deviceIds = normalizeDeviceIds(payload.deviceIds ?? payload.devices ?? payload.deviceId).slice(0, 100);
2292
+ const captureDeviceIds = normalizeDeviceIds(payload.captureDeviceIds ?? payload.inputDeviceIds ?? deviceIds)
2293
+ .filter(deviceId => deviceIds.includes(deviceId));
2294
+ const monitorSelections = normalizeMonitorSelections(payload.monitorSelections);
2295
+ const width = clampNumber(payload.width, 640, 3840, 1920);
2296
+ const height = clampNumber(payload.height, 360, 2160, 1080);
2297
+ const tileSize = resolveMode4AtlasTileSize({
2298
+ requestedWidth: payload.tileWidth,
2299
+ requestedHeight: payload.tileHeight,
2300
+ deviceCount: deviceIds.length,
2301
+ atlasWidth: width,
2302
+ atlasHeight: height
2303
+ });
2304
+ const tileWidth = tileSize.width;
2305
+ const tileHeight = tileSize.height;
2297
2306
  const previousInputDeviceIds = ws.liveDeskAtlasInputDeviceIds instanceof Set
2298
2307
  ? new Set(ws.liveDeskAtlasInputDeviceIds)
2299
2308
  : new Set();
@@ -2304,8 +2313,8 @@ function configureMode4Atlas(ws, payload = {}) {
2304
2313
  // every layout device ingestible lets a paused/reused input retain its
2305
2314
  // latest tile while only the changed device restarts capture.
2306
2315
  inputDeviceIds: deviceIds,
2307
- width: clampNumber(payload.width, 640, 3840, 1920),
2308
- height: clampNumber(payload.height, 360, 2160, 1080),
2316
+ width,
2317
+ height,
2309
2318
  tileWidth,
2310
2319
  tileHeight,
2311
2320
  fps: clampNumber(payload.fps, 1, 30, 20),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.407",
4
- "livedeskClientVersion": "0.1.173",
3
+ "version": "0.1.409",
4
+ "livedeskClientVersion": "0.1.174",
5
5
  "buildFlavor": "production",
6
6
  "description": "LiveDesk Hub and client launcher",
7
7
  "type": "module",
@@ -49,10 +49,10 @@
49
49
  "ws": "^8.18.3"
50
50
  },
51
51
  "optionalDependencies": {
52
- "@livedesk/fast-linux-x64": "0.1.379",
53
- "@livedesk/fast-osx-arm64": "0.1.379",
54
- "@livedesk/fast-osx-x64": "0.1.379",
55
- "@livedesk/fast-win-x64": "0.1.379"
52
+ "@livedesk/fast-linux-x64": "0.1.380",
53
+ "@livedesk/fast-osx-arm64": "0.1.380",
54
+ "@livedesk/fast-osx-x64": "0.1.380",
55
+ "@livedesk/fast-win-x64": "0.1.380"
56
56
  },
57
57
  "publishConfig": {
58
58
  "access": "public"
Binary file