@livedesk/client 0.1.50 → 0.1.52
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/bin/livedesk-client.js +236 -64
- package/fast/linux-x64/livedesk-client-fast.dll +0 -0
- package/fast/linux-x64/livedesk-client-fast.pdb +0 -0
- package/fast/osx-arm64/livedesk-client-fast.dll +0 -0
- package/fast/osx-arm64/livedesk-client-fast.pdb +0 -0
- package/fast/osx-x64/livedesk-client-fast.dll +0 -0
- package/fast/osx-x64/livedesk-client-fast.pdb +0 -0
- package/fast/win-x64/livedesk-client-fast.dll +0 -0
- package/fast/win-x64/livedesk-client-fast.pdb +0 -0
- package/package.json +1 -1
package/bin/livedesk-client.js
CHANGED
|
@@ -428,10 +428,12 @@ function renderOAuthCallbackPage({ title, message, tone = 'neutral' }) {
|
|
|
428
428
|
</html>`;
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
-
function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
431
|
+
function renderConnectionChoicePage({ error = '', pin = '', slot = '' } = {}) {
|
|
432
432
|
const errorBlock = error
|
|
433
433
|
? `<div class="error">${escapeHtml(error)}</div>`
|
|
434
434
|
: '';
|
|
435
|
+
const normalizedSlot = normalizeSlotNumber(slot);
|
|
436
|
+
const slotLabel = normalizedSlot ? `Slot ${String(normalizedSlot).padStart(3, '0')}` : 'First available';
|
|
435
437
|
return `<!doctype html>
|
|
436
438
|
<html lang="en">
|
|
437
439
|
<head>
|
|
@@ -443,48 +445,99 @@ function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
|
443
445
|
* { box-sizing: border-box; }
|
|
444
446
|
html, body { width: 100%; min-height: 100%; margin: 0; }
|
|
445
447
|
body {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
padding: 24px;
|
|
449
|
-
background:
|
|
450
|
-
radial-gradient(circle at 50% 0%, rgba(148, 163, 184, 0.22), transparent 34%),
|
|
451
|
-
linear-gradient(180deg, #f8fafc, #eef2f7);
|
|
448
|
+
min-height: 100vh;
|
|
449
|
+
background: #f6f7f9;
|
|
452
450
|
color: #111827;
|
|
453
451
|
}
|
|
452
|
+
.shell {
|
|
453
|
+
min-height: 100vh;
|
|
454
|
+
display: grid;
|
|
455
|
+
grid-template-columns: minmax(360px, 462px) minmax(0, 1fr);
|
|
456
|
+
}
|
|
454
457
|
main {
|
|
455
|
-
|
|
458
|
+
display: flex;
|
|
459
|
+
flex-direction: column;
|
|
460
|
+
justify-content: center;
|
|
461
|
+
min-height: 100vh;
|
|
462
|
+
padding: 42px 54px;
|
|
463
|
+
border-right: 1px solid #d8dde5;
|
|
464
|
+
background: rgba(255, 255, 255, 0.96);
|
|
465
|
+
box-shadow: 18px 0 55px rgba(15, 23, 42, 0.08);
|
|
466
|
+
}
|
|
467
|
+
.stack {
|
|
468
|
+
width: min(100%, 386px);
|
|
456
469
|
display: grid;
|
|
457
|
-
gap:
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
470
|
+
gap: 24px;
|
|
471
|
+
}
|
|
472
|
+
.topbar {
|
|
473
|
+
position: absolute;
|
|
474
|
+
top: 28px;
|
|
475
|
+
left: 54px;
|
|
476
|
+
display: flex;
|
|
477
|
+
align-items: center;
|
|
478
|
+
gap: 11px;
|
|
463
479
|
}
|
|
464
480
|
.brand {
|
|
465
481
|
display: flex;
|
|
466
482
|
align-items: center;
|
|
467
|
-
gap:
|
|
483
|
+
gap: 13px;
|
|
468
484
|
}
|
|
469
485
|
.mark {
|
|
470
|
-
width:
|
|
471
|
-
height:
|
|
486
|
+
width: 38px;
|
|
487
|
+
height: 38px;
|
|
472
488
|
display: grid;
|
|
473
489
|
place-items: center;
|
|
474
|
-
border-radius:
|
|
475
|
-
background: #
|
|
490
|
+
border-radius: 9px;
|
|
491
|
+
background: linear-gradient(135deg, #2dd4bf, #2563eb);
|
|
476
492
|
color: #ffffff;
|
|
477
493
|
font-weight: 950;
|
|
494
|
+
box-shadow: 0 12px 28px rgba(37, 99, 235, 0.18);
|
|
478
495
|
}
|
|
479
|
-
|
|
480
|
-
|
|
496
|
+
.brand-copy {
|
|
497
|
+
display: grid;
|
|
498
|
+
gap: 2px;
|
|
499
|
+
line-height: 1;
|
|
500
|
+
}
|
|
501
|
+
.brand-copy strong {
|
|
502
|
+
font-size: 15px;
|
|
503
|
+
letter-spacing: 0;
|
|
504
|
+
}
|
|
505
|
+
.brand-copy span {
|
|
506
|
+
color: #64748b;
|
|
507
|
+
font-size: 12px;
|
|
508
|
+
font-weight: 750;
|
|
509
|
+
}
|
|
510
|
+
.headline {
|
|
511
|
+
display: grid;
|
|
512
|
+
gap: 9px;
|
|
513
|
+
padding-top: 16px;
|
|
514
|
+
}
|
|
515
|
+
h1 { margin: 0; font-size: 32px; line-height: 1.08; letter-spacing: 0; }
|
|
516
|
+
p { margin: 0; color: #64748b; font-size: 14px; line-height: 1.55; font-weight: 650; }
|
|
481
517
|
.panel {
|
|
482
518
|
display: grid;
|
|
483
|
-
gap:
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
519
|
+
gap: 14px;
|
|
520
|
+
}
|
|
521
|
+
.placement {
|
|
522
|
+
display: flex;
|
|
523
|
+
align-items: center;
|
|
524
|
+
justify-content: space-between;
|
|
525
|
+
min-height: 38px;
|
|
526
|
+
padding: 0 12px;
|
|
527
|
+
border: 1px solid #d9dee7;
|
|
528
|
+
border-radius: 8px;
|
|
487
529
|
background: #ffffff;
|
|
530
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04);
|
|
531
|
+
}
|
|
532
|
+
.placement span {
|
|
533
|
+
color: #64748b;
|
|
534
|
+
font-size: 12px;
|
|
535
|
+
font-weight: 800;
|
|
536
|
+
}
|
|
537
|
+
.placement strong {
|
|
538
|
+
color: #111827;
|
|
539
|
+
font-size: 13px;
|
|
540
|
+
font-weight: 900;
|
|
488
541
|
}
|
|
489
542
|
form {
|
|
490
543
|
display: grid;
|
|
@@ -501,12 +554,13 @@ function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
|
501
554
|
letter-spacing: 0.04em;
|
|
502
555
|
}
|
|
503
556
|
input {
|
|
504
|
-
height:
|
|
505
|
-
border: 1px solid
|
|
506
|
-
border-radius:
|
|
507
|
-
padding: 0
|
|
557
|
+
height: 42px;
|
|
558
|
+
border: 1px solid #cfd6e1;
|
|
559
|
+
border-radius: 8px;
|
|
560
|
+
padding: 0 12px;
|
|
561
|
+
background: rgba(17, 24, 39, 0.026);
|
|
508
562
|
color: #111827;
|
|
509
|
-
font-size:
|
|
563
|
+
font-size: 22px;
|
|
510
564
|
font-weight: 950;
|
|
511
565
|
letter-spacing: 0.18em;
|
|
512
566
|
text-align: center;
|
|
@@ -517,25 +571,36 @@ function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
|
517
571
|
box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);
|
|
518
572
|
}
|
|
519
573
|
button, .google {
|
|
520
|
-
height:
|
|
574
|
+
height: 42px;
|
|
521
575
|
display: inline-flex;
|
|
522
576
|
align-items: center;
|
|
523
577
|
justify-content: center;
|
|
524
578
|
gap: 8px;
|
|
525
|
-
border:
|
|
526
|
-
border-radius: 12px;
|
|
579
|
+
border-radius: 8px;
|
|
527
580
|
background: #111827;
|
|
528
581
|
color: #ffffff;
|
|
529
582
|
font-size: 14px;
|
|
530
583
|
font-weight: 900;
|
|
531
584
|
text-decoration: none;
|
|
532
585
|
cursor: pointer;
|
|
586
|
+
transition: border-color 160ms ease, background 160ms ease, transform 160ms ease, box-shadow 160ms ease;
|
|
587
|
+
}
|
|
588
|
+
button {
|
|
589
|
+
border: 1px solid #111827;
|
|
590
|
+
box-shadow: 0 10px 24px rgba(17, 24, 39, 0.12);
|
|
591
|
+
}
|
|
592
|
+
button:hover, .google:hover {
|
|
593
|
+
transform: translateY(-1px);
|
|
533
594
|
}
|
|
534
595
|
.google {
|
|
535
596
|
background: #ffffff;
|
|
536
|
-
border: 1px solid
|
|
597
|
+
border: 1px solid #cfd6e1;
|
|
537
598
|
color: #111827;
|
|
538
599
|
}
|
|
600
|
+
.google:hover {
|
|
601
|
+
border-color: #111827;
|
|
602
|
+
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
|
|
603
|
+
}
|
|
539
604
|
.divider {
|
|
540
605
|
display: grid;
|
|
541
606
|
grid-template-columns: 1fr auto 1fr;
|
|
@@ -550,11 +615,11 @@ function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
|
550
615
|
.divider::before, .divider::after {
|
|
551
616
|
content: "";
|
|
552
617
|
height: 1px;
|
|
553
|
-
background:
|
|
618
|
+
background: #d9dee7;
|
|
554
619
|
}
|
|
555
620
|
.error {
|
|
556
|
-
border: 1px solid rgba(220, 38, 38, 0.
|
|
557
|
-
border-radius:
|
|
621
|
+
border: 1px solid rgba(220, 38, 38, 0.24);
|
|
622
|
+
border-radius: 8px;
|
|
558
623
|
padding: 10px 12px;
|
|
559
624
|
background: rgba(254, 226, 226, 0.75);
|
|
560
625
|
color: #991b1b;
|
|
@@ -567,31 +632,130 @@ function renderConnectionChoicePage({ error = '', pin = '' } = {}) {
|
|
|
567
632
|
font-size: 12px;
|
|
568
633
|
font-weight: 750;
|
|
569
634
|
}
|
|
635
|
+
aside {
|
|
636
|
+
display: grid;
|
|
637
|
+
place-items: center;
|
|
638
|
+
min-height: 100vh;
|
|
639
|
+
padding: 56px;
|
|
640
|
+
background:
|
|
641
|
+
radial-gradient(circle at 58% 28%, rgba(203, 213, 225, 0.5), transparent 28%),
|
|
642
|
+
linear-gradient(180deg, #f8fafc, #eef2f7);
|
|
643
|
+
}
|
|
644
|
+
.preview {
|
|
645
|
+
width: min(560px, 90%);
|
|
646
|
+
display: grid;
|
|
647
|
+
gap: 14px;
|
|
648
|
+
padding: 18px;
|
|
649
|
+
border: 1px solid rgba(203, 213, 225, 0.76);
|
|
650
|
+
border-radius: 14px;
|
|
651
|
+
background: rgba(255, 255, 255, 0.76);
|
|
652
|
+
box-shadow: 0 28px 90px rgba(15, 23, 42, 0.13);
|
|
653
|
+
}
|
|
654
|
+
.preview-title {
|
|
655
|
+
display: flex;
|
|
656
|
+
align-items: center;
|
|
657
|
+
justify-content: space-between;
|
|
658
|
+
color: #475569;
|
|
659
|
+
font-size: 12px;
|
|
660
|
+
font-weight: 900;
|
|
661
|
+
text-transform: uppercase;
|
|
662
|
+
letter-spacing: 0.06em;
|
|
663
|
+
}
|
|
664
|
+
.preview-dot {
|
|
665
|
+
width: 9px;
|
|
666
|
+
height: 9px;
|
|
667
|
+
border-radius: 999px;
|
|
668
|
+
background: #14b8a6;
|
|
669
|
+
box-shadow: 0 0 0 4px rgba(20, 184, 166, 0.12);
|
|
670
|
+
}
|
|
671
|
+
.screen-grid {
|
|
672
|
+
display: grid;
|
|
673
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
674
|
+
gap: 12px;
|
|
675
|
+
}
|
|
676
|
+
.screen {
|
|
677
|
+
aspect-ratio: 16 / 9;
|
|
678
|
+
border: 1px solid #d8dee8;
|
|
679
|
+
border-radius: 8px;
|
|
680
|
+
background:
|
|
681
|
+
linear-gradient(rgba(148, 163, 184, 0.11) 1px, transparent 1px),
|
|
682
|
+
linear-gradient(90deg, rgba(148, 163, 184, 0.11) 1px, transparent 1px),
|
|
683
|
+
#ffffff;
|
|
684
|
+
background-size: 32px 32px;
|
|
685
|
+
box-shadow: inset 0 -16px 26px rgba(15, 23, 42, 0.04);
|
|
686
|
+
}
|
|
687
|
+
.caption {
|
|
688
|
+
color: #64748b;
|
|
689
|
+
font-size: 13px;
|
|
690
|
+
font-weight: 700;
|
|
691
|
+
line-height: 1.55;
|
|
692
|
+
}
|
|
693
|
+
@media (max-width: 860px) {
|
|
694
|
+
.shell { grid-template-columns: 1fr; }
|
|
695
|
+
main {
|
|
696
|
+
min-height: 100vh;
|
|
697
|
+
padding: 84px 28px 36px;
|
|
698
|
+
border-right: 0;
|
|
699
|
+
}
|
|
700
|
+
.topbar {
|
|
701
|
+
left: 28px;
|
|
702
|
+
top: 24px;
|
|
703
|
+
}
|
|
704
|
+
.stack { justify-self: center; }
|
|
705
|
+
aside { display: none; }
|
|
706
|
+
}
|
|
570
707
|
</style>
|
|
571
708
|
</head>
|
|
572
709
|
<body>
|
|
573
|
-
<
|
|
574
|
-
<
|
|
575
|
-
<div class="
|
|
576
|
-
|
|
577
|
-
<
|
|
578
|
-
|
|
710
|
+
<div class="shell">
|
|
711
|
+
<main>
|
|
712
|
+
<div class="topbar">
|
|
713
|
+
<div class="mark">LD</div>
|
|
714
|
+
<div class="brand-copy">
|
|
715
|
+
<strong>LiveDesk</strong>
|
|
716
|
+
<span>Client connect</span>
|
|
717
|
+
</div>
|
|
579
718
|
</div>
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
719
|
+
<div class="stack">
|
|
720
|
+
<div class="headline">
|
|
721
|
+
<h1>Connect this computer</h1>
|
|
722
|
+
<p>Use the same Google account as your Hub, or enter the 6-digit Hub PIN from the screen wall.</p>
|
|
723
|
+
</div>
|
|
724
|
+
${errorBlock}
|
|
725
|
+
<section class="panel">
|
|
726
|
+
<a class="google" href="/google">Continue with Google</a>
|
|
727
|
+
<div class="divider">or</div>
|
|
728
|
+
<form method="post" action="/pin">
|
|
729
|
+
<label>
|
|
730
|
+
Hub PIN
|
|
731
|
+
<input name="pin" value="${escapeHtml(pin)}" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]{6}" maxlength="6" placeholder="000000" autofocus>
|
|
732
|
+
</label>
|
|
733
|
+
<button type="submit">Connect with PIN</button>
|
|
734
|
+
</form>
|
|
735
|
+
<div class="placement">
|
|
736
|
+
<span>Placement</span>
|
|
737
|
+
<strong>${escapeHtml(slotLabel)}</strong>
|
|
738
|
+
</div>
|
|
739
|
+
</section>
|
|
740
|
+
<small>This tab can stay open until the connection is ready.</small>
|
|
741
|
+
</div>
|
|
742
|
+
</main>
|
|
743
|
+
<aside aria-hidden="true">
|
|
744
|
+
<div class="preview">
|
|
745
|
+
<div class="preview-title">
|
|
746
|
+
<span>Screen wall ready</span>
|
|
747
|
+
<span class="preview-dot"></span>
|
|
748
|
+
</div>
|
|
749
|
+
<div class="screen-grid">
|
|
750
|
+
<div class="screen"></div>
|
|
751
|
+
<div class="screen"></div>
|
|
752
|
+
<div class="screen"></div>
|
|
753
|
+
<div class="screen"></div>
|
|
754
|
+
</div>
|
|
755
|
+
<div class="caption">LiveDesk places this computer on the Hub wall after sign-in or PIN approval.</div>
|
|
756
|
+
</div>
|
|
757
|
+
</aside>
|
|
758
|
+
</div>
|
|
595
759
|
</body>
|
|
596
760
|
</html>`;
|
|
597
761
|
}
|
|
@@ -655,6 +819,7 @@ async function resolveManagerFromPin(supabase, pin) {
|
|
|
655
819
|
async function startConnectionChoiceServer(supabase, options = {}) {
|
|
656
820
|
const host = String(process.env.LIVEDESK_CLIENT_AUTH_HOST || DEFAULT_AUTH_CALLBACK_HOST).trim() || DEFAULT_AUTH_CALLBACK_HOST;
|
|
657
821
|
const port = normalizePort(options.authPort) || DEFAULT_AUTH_CALLBACK_PORT;
|
|
822
|
+
const slot = normalizeSlotNumber(options.slot);
|
|
658
823
|
let listeningPort = port;
|
|
659
824
|
let completed = false;
|
|
660
825
|
let completedTitle = 'LiveDesk connection complete';
|
|
@@ -673,10 +838,11 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
673
838
|
settleChoice(choice);
|
|
674
839
|
setImmediate(() => server.close());
|
|
675
840
|
};
|
|
841
|
+
const renderChoice = (props = {}) => renderConnectionChoicePage({ slot, ...props });
|
|
676
842
|
|
|
677
843
|
const handleError = (res, error, pin = '') => {
|
|
678
844
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
679
|
-
res.end(
|
|
845
|
+
res.end(renderChoice({
|
|
680
846
|
error: error instanceof Error ? error.message : String(error),
|
|
681
847
|
pin
|
|
682
848
|
}));
|
|
@@ -701,7 +867,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
701
867
|
|
|
702
868
|
if (requestUrl.pathname === '/' || requestUrl.pathname === '/connect') {
|
|
703
869
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
704
|
-
res.end(
|
|
870
|
+
res.end(renderChoice());
|
|
705
871
|
return;
|
|
706
872
|
}
|
|
707
873
|
|
|
@@ -776,7 +942,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
776
942
|
}
|
|
777
943
|
if (!code) {
|
|
778
944
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
779
|
-
res.end(
|
|
945
|
+
res.end(renderChoice({
|
|
780
946
|
error: 'Google did not return an auth code. Try Google sign-in again.'
|
|
781
947
|
}));
|
|
782
948
|
return;
|
|
@@ -805,7 +971,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
805
971
|
}
|
|
806
972
|
|
|
807
973
|
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
808
|
-
res.end(
|
|
974
|
+
res.end(renderChoice({
|
|
809
975
|
error: 'Unknown LiveDesk connection route.'
|
|
810
976
|
}));
|
|
811
977
|
})().catch(err => {
|
|
@@ -846,7 +1012,10 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
846
1012
|
}
|
|
847
1013
|
|
|
848
1014
|
async function chooseClientConnection(supabase, options = {}) {
|
|
849
|
-
const connectionPage = await startConnectionChoiceServer(supabase, {
|
|
1015
|
+
const connectionPage = await startConnectionChoiceServer(supabase, {
|
|
1016
|
+
authPort: options.authPort,
|
|
1017
|
+
slot: options.slot
|
|
1018
|
+
});
|
|
850
1019
|
console.log('Opening LiveDesk connection page...');
|
|
851
1020
|
openBrowser(connectionPage.url);
|
|
852
1021
|
return connectionPage.waitForChoice;
|
|
@@ -976,7 +1145,10 @@ async function prepareLoginConnection(parsed) {
|
|
|
976
1145
|
|
|
977
1146
|
if (shouldLogin) {
|
|
978
1147
|
const supabase = await createSupabaseClient();
|
|
979
|
-
const choice = await chooseClientConnection(supabase, {
|
|
1148
|
+
const choice = await chooseClientConnection(supabase, {
|
|
1149
|
+
authPort: parsed.authPort,
|
|
1150
|
+
slot: parsed.slot
|
|
1151
|
+
});
|
|
980
1152
|
if (choice.type === 'pin') {
|
|
981
1153
|
manager = choice.manager;
|
|
982
1154
|
pair = choice.pair;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|