@dualityinstitute/blink 0.0.3 → 0.0.5
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/README.md +37 -34
- package/dist/cli.js +20 -16
- package/dist/electron/main.js +754 -5992
- package/dist/electron/preload.cjs +31 -4
- package/dist/electron/renderer/popup.css +125 -15
- package/dist/electron/renderer/popup.html +17 -4
- package/dist/electron/renderer/popup.js +126 -59
- package/dist/electron/renderer/settings.css +439 -0
- package/dist/electron/renderer/settings.html +93 -0
- package/dist/electron/renderer/settings.js +4067 -0
- package/package.json +2 -2
- package/vendor/Blink.app/Contents/Helpers/BlinkAudio +0 -0
- package/vendor/Blink.app/Contents/MacOS/Blink +0 -0
- package/vendor/Blink.app/Contents/_CodeSignature/CodeResources +2 -2
- package/dist/electron/fsevents-1bczt1x4.node +0 -0
|
@@ -20,13 +20,19 @@ var CHANNEL_ANSWER_START = "blink:answer-start";
|
|
|
20
20
|
var CHANNEL_ANSWER_DELTA = "blink:answer-delta";
|
|
21
21
|
var CHANNEL_ANSWER_DONE = "blink:answer-done";
|
|
22
22
|
var CHANNEL_ANSWER_ERROR = "blink:answer-error";
|
|
23
|
-
var CHANNEL_QUEUE_CHANGE = "blink:queue-change";
|
|
24
23
|
var CHANNEL_PRESENTER_LOAD = "blink:presenter-load";
|
|
25
24
|
var CHANNEL_PRESENTER_SAVE = "blink:presenter-save";
|
|
26
25
|
var CHANNEL_PRESENTER_DISMISS = "blink:presenter-dismiss";
|
|
27
26
|
var CHANNEL_PRESENTER_SET_PRIVACY = "blink:presenter-set-privacy";
|
|
28
27
|
var CHANNEL_PRESENTER_PRIVACY_CHANGED = "blink:presenter-privacy-changed";
|
|
29
28
|
var CHANNEL_PRESENTER_SCROLL = "blink:presenter-scroll";
|
|
29
|
+
var CHANNEL_SETTINGS_LOAD_PROMPTS = "blink:settings-load-prompts";
|
|
30
|
+
var CHANNEL_SETTINGS_SAVE_PROMPTS = "blink:settings-save-prompts";
|
|
31
|
+
var CHANNEL_SETTINGS_LOAD_ANSWERS = "blink:settings-load-answers";
|
|
32
|
+
var CHANNEL_SETTINGS_DISMISS = "blink:settings-dismiss";
|
|
33
|
+
var CHANNEL_SETTINGS_LOAD_TRANSCRIPTS = "blink:settings-load-transcripts";
|
|
34
|
+
var CHANNEL_SETTINGS_READ_TRANSCRIPT = "blink:settings-read-transcript";
|
|
35
|
+
var CHANNEL_SETTINGS_SHOW_TAB = "blink:settings-show-tab";
|
|
30
36
|
|
|
31
37
|
// src/electron/preload.ts
|
|
32
38
|
function subscribe(channel, callback) {
|
|
@@ -51,9 +57,6 @@ var blinkAPI = {
|
|
|
51
57
|
onAnswerError(callback) {
|
|
52
58
|
subscribe(CHANNEL_ANSWER_ERROR, callback);
|
|
53
59
|
},
|
|
54
|
-
onQueueChange(callback) {
|
|
55
|
-
subscribe(CHANNEL_QUEUE_CHANGE, callback);
|
|
56
|
-
},
|
|
57
60
|
abort() {
|
|
58
61
|
import_electron.ipcRenderer.send(CHANNEL_ABORT);
|
|
59
62
|
},
|
|
@@ -111,5 +114,29 @@ var presenterAPI = {
|
|
|
111
114
|
subscribe(CHANNEL_PRESENTER_SCROLL, callback);
|
|
112
115
|
}
|
|
113
116
|
};
|
|
117
|
+
var settingsAPI = {
|
|
118
|
+
loadPrompts() {
|
|
119
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_PROMPTS);
|
|
120
|
+
},
|
|
121
|
+
savePrompts(prompts) {
|
|
122
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_SAVE_PROMPTS, prompts);
|
|
123
|
+
},
|
|
124
|
+
loadAnswers() {
|
|
125
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_ANSWERS);
|
|
126
|
+
},
|
|
127
|
+
loadTranscripts(query) {
|
|
128
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_TRANSCRIPTS, query);
|
|
129
|
+
},
|
|
130
|
+
readTranscript(file) {
|
|
131
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_READ_TRANSCRIPT, file);
|
|
132
|
+
},
|
|
133
|
+
dismiss() {
|
|
134
|
+
import_electron.ipcRenderer.send(CHANNEL_SETTINGS_DISMISS);
|
|
135
|
+
},
|
|
136
|
+
onShowTab(callback) {
|
|
137
|
+
subscribe(CHANNEL_SETTINGS_SHOW_TAB, callback);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
114
140
|
import_electron.contextBridge.exposeInMainWorld("blink", blinkAPI);
|
|
115
141
|
import_electron.contextBridge.exposeInMainWorld("presenter", presenterAPI);
|
|
142
|
+
import_electron.contextBridge.exposeInMainWorld("settings", settingsAPI);
|
|
@@ -215,6 +215,23 @@ body {
|
|
|
215
215
|
border-color: color-mix(in srgb, var(--fg) 24%, transparent);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
+
.meeting-control.is-live {
|
|
219
|
+
color: #fff;
|
|
220
|
+
background: var(--leave-bg, #c0392b);
|
|
221
|
+
border-color: color-mix(in srgb, #c0392b 70%, #000);
|
|
222
|
+
cursor: pointer;
|
|
223
|
+
font-variant-numeric: tabular-nums;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.meeting-control.is-live:hover {
|
|
227
|
+
background: color-mix(in srgb, #c0392b 85%, #fff);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.meeting-control.is-live .meeting-indicator {
|
|
231
|
+
background: #fff;
|
|
232
|
+
border-color: #fff;
|
|
233
|
+
}
|
|
234
|
+
|
|
218
235
|
.meeting-control.is-meeting .meeting-indicator {
|
|
219
236
|
background: currentColor;
|
|
220
237
|
}
|
|
@@ -242,6 +259,46 @@ body {
|
|
|
242
259
|
outline-offset: 2px;
|
|
243
260
|
}
|
|
244
261
|
|
|
262
|
+
.runtime-error {
|
|
263
|
+
display: flex;
|
|
264
|
+
align-items: flex-start;
|
|
265
|
+
gap: 8px;
|
|
266
|
+
margin: 6px 12px 0 12px;
|
|
267
|
+
padding: 8px 10px;
|
|
268
|
+
border: 1px solid rgba(200, 60, 60, 0.35);
|
|
269
|
+
border-radius: 8px;
|
|
270
|
+
background: rgba(200, 60, 60, 0.12);
|
|
271
|
+
font-size: 12px;
|
|
272
|
+
line-height: 1.4;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.runtime-error[hidden] {
|
|
276
|
+
display: none;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.runtime-error-text {
|
|
280
|
+
flex: 1;
|
|
281
|
+
min-width: 0;
|
|
282
|
+
overflow-wrap: anywhere;
|
|
283
|
+
color: #ffd9d9;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.runtime-error-close {
|
|
287
|
+
flex: none;
|
|
288
|
+
padding: 0 4px;
|
|
289
|
+
border: 0;
|
|
290
|
+
background: transparent;
|
|
291
|
+
color: #ffd9d9;
|
|
292
|
+
font-size: 14px;
|
|
293
|
+
line-height: 1;
|
|
294
|
+
cursor: pointer;
|
|
295
|
+
opacity: 0.75;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.runtime-error-close:hover {
|
|
299
|
+
opacity: 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
245
302
|
.response-context {
|
|
246
303
|
display: flex;
|
|
247
304
|
align-items: center;
|
|
@@ -395,35 +452,90 @@ body {
|
|
|
395
452
|
opacity: 0.45;
|
|
396
453
|
}
|
|
397
454
|
|
|
455
|
+
.workspace {
|
|
456
|
+
display: flex;
|
|
457
|
+
flex: 1 1 auto;
|
|
458
|
+
min-height: 0;
|
|
459
|
+
}
|
|
460
|
+
|
|
398
461
|
.transcript-panel {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
462
|
+
display: flex;
|
|
463
|
+
flex-direction: column;
|
|
464
|
+
/* Shrinks below its 400px basis only when the window is too narrow to hold
|
|
465
|
+
both panes, so dragging the popup small never hides the answer entirely. */
|
|
466
|
+
flex: 0 1 400px;
|
|
467
|
+
width: 400px;
|
|
468
|
+
min-width: 200px;
|
|
469
|
+
border-right: 1px solid var(--divider);
|
|
404
470
|
background: color-mix(in srgb, var(--bg-strong) 12%, transparent);
|
|
405
471
|
font-size: 11.5px;
|
|
406
472
|
line-height: 1.35;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.transcript-panel[hidden] {
|
|
476
|
+
display: none;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.transcript-heading {
|
|
480
|
+
display: flex;
|
|
481
|
+
align-items: baseline;
|
|
482
|
+
justify-content: space-between;
|
|
483
|
+
gap: 8px;
|
|
484
|
+
padding: 7px 12px 6px 16px;
|
|
485
|
+
border-bottom: 1px solid var(--divider);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.transcript-heading-label {
|
|
489
|
+
font-size: 9.5px;
|
|
490
|
+
font-weight: 700;
|
|
491
|
+
letter-spacing: 0.06em;
|
|
492
|
+
text-transform: uppercase;
|
|
493
|
+
color: var(--fg-subtle);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.transcript-count {
|
|
497
|
+
font-size: 9.5px;
|
|
498
|
+
font-variant-numeric: tabular-nums;
|
|
499
|
+
color: var(--fg-subtle);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.transcript-scroll {
|
|
503
|
+
flex: 1 1 auto;
|
|
504
|
+
min-height: 0;
|
|
505
|
+
overflow-y: auto;
|
|
506
|
+
padding: 8px 12px 10px 16px;
|
|
407
507
|
user-select: text;
|
|
408
508
|
-webkit-user-select: text;
|
|
409
509
|
scrollbar-width: thin;
|
|
410
510
|
scrollbar-color: var(--fg-subtle) transparent;
|
|
411
511
|
}
|
|
412
512
|
|
|
413
|
-
.transcript-
|
|
513
|
+
.transcript-scroll::-webkit-scrollbar {
|
|
414
514
|
width: 5px;
|
|
415
515
|
}
|
|
416
516
|
|
|
417
|
-
.transcript-
|
|
517
|
+
.transcript-scroll::-webkit-scrollbar-thumb {
|
|
418
518
|
background: var(--fg-subtle);
|
|
419
519
|
border-radius: 3px;
|
|
420
520
|
opacity: 0.4;
|
|
421
521
|
}
|
|
422
522
|
|
|
523
|
+
/* Placeholder lives in CSS so an empty transcript needs no throwaway DOM node. */
|
|
524
|
+
.transcript-lines.is-empty::before {
|
|
525
|
+
content: "Waiting for speech…";
|
|
526
|
+
color: var(--fg-subtle);
|
|
527
|
+
font-style: italic;
|
|
528
|
+
}
|
|
529
|
+
|
|
423
530
|
.transcript-line {
|
|
424
531
|
display: flex;
|
|
425
|
-
|
|
426
|
-
|
|
532
|
+
flex-direction: column;
|
|
533
|
+
gap: 1px;
|
|
534
|
+
margin: 0 0 7px;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.transcript-line.is-partial .transcript-text {
|
|
538
|
+
color: var(--fg-subtle);
|
|
427
539
|
}
|
|
428
540
|
|
|
429
541
|
.transcript-speaker {
|
|
@@ -451,11 +563,6 @@ body {
|
|
|
451
563
|
overflow-wrap: break-word;
|
|
452
564
|
}
|
|
453
565
|
|
|
454
|
-
.transcript-empty {
|
|
455
|
-
color: var(--fg-subtle);
|
|
456
|
-
font-style: italic;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
566
|
.pill.reconciled-pill {
|
|
460
567
|
background: var(--pill-reconciled-bg);
|
|
461
568
|
color: var(--pill-reconciled-fg);
|
|
@@ -543,8 +650,11 @@ body {
|
|
|
543
650
|
|
|
544
651
|
.card-body {
|
|
545
652
|
position: relative;
|
|
546
|
-
|
|
653
|
+
/* The floor is what forces a narrowed window to shrink the transcript column
|
|
654
|
+
first; without it the answer silently collapses to an unusable sliver. */
|
|
655
|
+
flex: 1 1 0;
|
|
547
656
|
min-height: 0;
|
|
657
|
+
min-width: 280px;
|
|
548
658
|
overflow-y: auto;
|
|
549
659
|
padding: 12px 14px 14px 14px;
|
|
550
660
|
scrollbar-width: thin;
|
|
@@ -68,13 +68,17 @@
|
|
|
68
68
|
</div>
|
|
69
69
|
</header>
|
|
70
70
|
|
|
71
|
+
<div class="runtime-error" id="runtime-error" hidden role="alert">
|
|
72
|
+
<span class="runtime-error-text" id="runtime-error-text"></span>
|
|
73
|
+
<button type="button" class="runtime-error-close" id="runtime-error-close" aria-label="Dismiss">×</button>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
71
76
|
<div class="response-context">
|
|
72
77
|
<div class="source" id="source">
|
|
73
78
|
<span class="source-label">Ready for a screenshot</span>
|
|
74
79
|
</div>
|
|
75
80
|
<div class="status-group">
|
|
76
81
|
<span class="pill usage-pill" id="usage-pill" hidden role="status"></span>
|
|
77
|
-
<span class="pill queue-pill" id="queue-pill" hidden aria-hidden="true"></span>
|
|
78
82
|
<span class="pill reconciled-pill" id="reconciled-pill" hidden aria-hidden="true">recovered</span>
|
|
79
83
|
<span class="pill stopped-pill" id="stopped-pill" hidden aria-hidden="true">stopped</span>
|
|
80
84
|
</div>
|
|
@@ -108,14 +112,22 @@
|
|
|
108
112
|
>Ask</button>
|
|
109
113
|
</div>
|
|
110
114
|
|
|
111
|
-
<div
|
|
115
|
+
<div class="workspace" id="workspace">
|
|
116
|
+
<aside
|
|
112
117
|
class="transcript-panel"
|
|
113
118
|
id="transcript-panel"
|
|
114
119
|
aria-label="Live meeting transcript"
|
|
115
120
|
hidden
|
|
116
121
|
>
|
|
117
|
-
<div class="transcript-
|
|
118
|
-
|
|
122
|
+
<div class="transcript-heading">
|
|
123
|
+
<span class="transcript-heading-label">Live transcript</span>
|
|
124
|
+
<span class="transcript-count" id="transcript-count"></span>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="transcript-scroll" id="transcript-scroll">
|
|
127
|
+
<div class="transcript-lines" id="transcript-lines"></div>
|
|
128
|
+
<div class="transcript-volatile" id="transcript-volatile"></div>
|
|
129
|
+
</div>
|
|
130
|
+
</aside>
|
|
119
131
|
|
|
120
132
|
<section class="card-body" id="body">
|
|
121
133
|
<div class="answering" id="answering" hidden>
|
|
@@ -135,6 +147,7 @@
|
|
|
135
147
|
<div class="error-message" id="error-message"></div>
|
|
136
148
|
</div>
|
|
137
149
|
</section>
|
|
150
|
+
</div>
|
|
138
151
|
</main>
|
|
139
152
|
<script src="./popup.js"></script>
|
|
140
153
|
</body>
|
|
@@ -3736,10 +3736,10 @@ var state = {
|
|
|
3736
3736
|
meeting: false,
|
|
3737
3737
|
meetingStarting: false,
|
|
3738
3738
|
meetingAsking: false,
|
|
3739
|
-
|
|
3739
|
+
monitoring: false,
|
|
3740
3740
|
screenshotCount: 0,
|
|
3741
3741
|
showTranscript: true,
|
|
3742
|
-
|
|
3742
|
+
meetingStartedAt: null
|
|
3743
3743
|
};
|
|
3744
3744
|
function el(id) {
|
|
3745
3745
|
const node = document.getElementById(id);
|
|
@@ -3762,11 +3762,13 @@ var dom = {
|
|
|
3762
3762
|
askMeeting: el("ask-meeting"),
|
|
3763
3763
|
transcriptToggle: el("transcript-toggle"),
|
|
3764
3764
|
transcriptPanel: el("transcript-panel"),
|
|
3765
|
+
transcriptScroll: el("transcript-scroll"),
|
|
3765
3766
|
transcriptLines: el("transcript-lines"),
|
|
3767
|
+
transcriptVolatile: el("transcript-volatile"),
|
|
3768
|
+
transcriptCount: el("transcript-count"),
|
|
3766
3769
|
privacy: el("privacy"),
|
|
3767
3770
|
privacyLabel: el("privacy-label"),
|
|
3768
3771
|
usagePill: el("usage-pill"),
|
|
3769
|
-
queuePill: el("queue-pill"),
|
|
3770
3772
|
reconciledPill: el("reconciled-pill"),
|
|
3771
3773
|
stoppedPill: el("stopped-pill"),
|
|
3772
3774
|
abort: el("abort"),
|
|
@@ -3775,6 +3777,9 @@ var dom = {
|
|
|
3775
3777
|
answer: el("answer"),
|
|
3776
3778
|
error: el("error"),
|
|
3777
3779
|
errorMessage: el("error-message"),
|
|
3780
|
+
runtimeError: el("runtime-error"),
|
|
3781
|
+
runtimeErrorText: el("runtime-error-text"),
|
|
3782
|
+
runtimeErrorClose: el("runtime-error-close"),
|
|
3778
3783
|
body: el("body")
|
|
3779
3784
|
};
|
|
3780
3785
|
function setState(next) {
|
|
@@ -3816,80 +3821,144 @@ function updatePrivacy(hidden) {
|
|
|
3816
3821
|
}
|
|
3817
3822
|
function updateMonitoring(payload) {
|
|
3818
3823
|
const active = payload.monitoring || payload.monitoringStarting;
|
|
3824
|
+
state.monitoring = payload.monitoring;
|
|
3819
3825
|
dom.monitoring.checked = active;
|
|
3820
3826
|
dom.monitoring.disabled = payload.monitoringStarting;
|
|
3821
3827
|
dom.monitoringLabel.textContent = payload.monitoringStarting ? "Starting…" : "Monitor";
|
|
3822
3828
|
dom.monitoringControl.classList.toggle("is-monitoring", active);
|
|
3823
3829
|
dom.monitoringControl.classList.toggle("is-starting", payload.monitoringStarting);
|
|
3824
3830
|
dom.monitoringControl.setAttribute("aria-busy", payload.monitoringStarting ? "true" : "false");
|
|
3831
|
+
renderMeetingContext();
|
|
3825
3832
|
}
|
|
3833
|
+
var transcriptRows = new Map;
|
|
3826
3834
|
function renderTranscript() {
|
|
3827
3835
|
const expanded = state.showTranscript;
|
|
3828
3836
|
dom.transcriptToggle.setAttribute("aria-expanded", expanded ? "true" : "false");
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
}
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
|
|
3837
|
+
const visible = state.meeting && expanded;
|
|
3838
|
+
dom.transcriptPanel.hidden = !visible;
|
|
3839
|
+
dom.root.classList.toggle("has-transcript", visible);
|
|
3840
|
+
updateTranscriptPlaceholder();
|
|
3841
|
+
}
|
|
3842
|
+
function buildTranscriptRow(line) {
|
|
3843
|
+
const row = document.createElement("div");
|
|
3844
|
+
row.className = "transcript-line";
|
|
3845
|
+
const speaker = document.createElement("span");
|
|
3846
|
+
speaker.className = `transcript-speaker is-${line.speaker}`;
|
|
3847
|
+
speaker.textContent = line.speaker === "them" ? "Them" : "You";
|
|
3848
|
+
const text2 = document.createElement("span");
|
|
3849
|
+
text2.className = "transcript-text";
|
|
3850
|
+
text2.textContent = line.text;
|
|
3851
|
+
row.append(speaker, text2);
|
|
3852
|
+
return row;
|
|
3853
|
+
}
|
|
3854
|
+
function setRowText(row, text2) {
|
|
3855
|
+
const node = row.querySelector(".transcript-text");
|
|
3856
|
+
if (node !== null)
|
|
3857
|
+
node.textContent = text2;
|
|
3858
|
+
}
|
|
3859
|
+
function updateTranscriptPlaceholder() {
|
|
3860
|
+
const empty = transcriptRows.size === 0 && dom.transcriptVolatile.childElementCount === 0;
|
|
3861
|
+
dom.transcriptLines.classList.toggle("is-empty", empty);
|
|
3862
|
+
dom.transcriptCount.textContent = transcriptRows.size === 0 ? "" : String(transcriptRows.size);
|
|
3863
|
+
}
|
|
3864
|
+
function isTranscriptAtBottom() {
|
|
3865
|
+
const node = dom.transcriptScroll;
|
|
3866
|
+
return node.scrollHeight - node.scrollTop - node.clientHeight < 40;
|
|
3853
3867
|
}
|
|
3854
3868
|
function pinTranscriptToBottom() {
|
|
3855
|
-
dom.
|
|
3869
|
+
dom.transcriptScroll.scrollTop = dom.transcriptScroll.scrollHeight;
|
|
3856
3870
|
}
|
|
3857
3871
|
function onTranscriptUpdate(payload) {
|
|
3858
|
-
|
|
3859
|
-
|
|
3872
|
+
if (payload.reset === true) {
|
|
3873
|
+
transcriptRows.clear();
|
|
3874
|
+
dom.transcriptLines.replaceChildren();
|
|
3875
|
+
dom.transcriptVolatile.replaceChildren();
|
|
3876
|
+
}
|
|
3877
|
+
const follow = isTranscriptAtBottom();
|
|
3878
|
+
for (const line of payload.changed ?? []) {
|
|
3879
|
+
const existing = transcriptRows.get(line.id);
|
|
3880
|
+
if (existing !== undefined) {
|
|
3881
|
+
setRowText(existing, line.text);
|
|
3882
|
+
continue;
|
|
3883
|
+
}
|
|
3884
|
+
const row = buildTranscriptRow(line);
|
|
3885
|
+
transcriptRows.set(line.id, row);
|
|
3886
|
+
dom.transcriptLines.append(row);
|
|
3887
|
+
}
|
|
3888
|
+
dom.transcriptVolatile.replaceChildren(...(payload.volatile ?? []).map((line) => {
|
|
3889
|
+
const row = buildTranscriptRow(line);
|
|
3890
|
+
row.classList.add("is-partial");
|
|
3891
|
+
return row;
|
|
3892
|
+
}));
|
|
3893
|
+
updateTranscriptPlaceholder();
|
|
3894
|
+
if (follow)
|
|
3895
|
+
pinTranscriptToBottom();
|
|
3896
|
+
}
|
|
3897
|
+
function clearMeetingAnswer() {
|
|
3898
|
+
const label = document.createElement("span");
|
|
3899
|
+
label.className = "source-label";
|
|
3900
|
+
label.textContent = "Ready for a screenshot";
|
|
3901
|
+
dom.source.replaceChildren(label);
|
|
3902
|
+
delete dom.source.dataset["source"];
|
|
3903
|
+
dom.answer.replaceChildren();
|
|
3904
|
+
dom.errorMessage.textContent = "";
|
|
3905
|
+
state.streamBuffer = "";
|
|
3906
|
+
setReconciled(false);
|
|
3907
|
+
setStopped(false);
|
|
3908
|
+
setState("idle");
|
|
3860
3909
|
}
|
|
3861
3910
|
function updateMeeting(payload) {
|
|
3911
|
+
const wasMeeting = state.meeting;
|
|
3862
3912
|
state.meeting = payload.meeting;
|
|
3863
3913
|
state.meetingStarting = payload.meetingStarting;
|
|
3864
3914
|
state.meetingAsking = payload.asking;
|
|
3865
|
-
state.transcriptAvailable = payload.transcriptAvailable;
|
|
3866
3915
|
state.screenshotCount = Math.max(0, Math.floor(payload.screenshotCount));
|
|
3867
3916
|
state.showTranscript = payload.showTranscript;
|
|
3868
3917
|
renderTranscript();
|
|
3918
|
+
state.meetingStartedAt = payload.startedAt ?? null;
|
|
3869
3919
|
const active = payload.meeting || payload.meetingStarting;
|
|
3870
3920
|
dom.meeting.checked = active;
|
|
3871
3921
|
dom.meeting.disabled = payload.meetingStarting;
|
|
3872
|
-
dom.meetingLabel.textContent = payload.meetingStarting ? "Starting…" : "Meeting";
|
|
3873
3922
|
dom.meetingControl.classList.toggle("is-meeting", active);
|
|
3923
|
+
dom.meetingControl.classList.toggle("is-live", payload.meeting);
|
|
3924
|
+
dom.meetingControl.title = payload.meeting ? "Leave the meeting and stop recording" : "Capture private Meeting context";
|
|
3925
|
+
renderMeetingClock();
|
|
3874
3926
|
dom.meetingControl.classList.toggle("is-starting", payload.meetingStarting);
|
|
3875
3927
|
dom.meetingControl.setAttribute("aria-busy", payload.meetingStarting ? "true" : "false");
|
|
3876
3928
|
renderMeetingContext();
|
|
3929
|
+
if (wasMeeting && !payload.meeting)
|
|
3930
|
+
clearMeetingAnswer();
|
|
3931
|
+
}
|
|
3932
|
+
function elapsedLabel(startedAt) {
|
|
3933
|
+
const total = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
|
3934
|
+
const hours = Math.floor(total / 3600);
|
|
3935
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
3936
|
+
const seconds = total % 60;
|
|
3937
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
3938
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`;
|
|
3939
|
+
}
|
|
3940
|
+
function renderMeetingClock() {
|
|
3941
|
+
if (state.meetingStarting) {
|
|
3942
|
+
dom.meetingLabel.textContent = "Starting…";
|
|
3943
|
+
return;
|
|
3944
|
+
}
|
|
3945
|
+
if (!state.meeting || state.meetingStartedAt === null) {
|
|
3946
|
+
dom.meetingLabel.textContent = "Meeting";
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
dom.meetingLabel.textContent = `Leave · ${elapsedLabel(state.meetingStartedAt)}`;
|
|
3877
3950
|
}
|
|
3951
|
+
window.setInterval(renderMeetingClock, 1000);
|
|
3878
3952
|
function renderMeetingContext() {
|
|
3879
|
-
const visible = state.meeting || state.meetingStarting;
|
|
3880
|
-
const hasContext = state.transcriptAvailable || state.screenshotCount > 0;
|
|
3953
|
+
const visible = state.meeting || state.meetingStarting || state.monitoring;
|
|
3881
3954
|
const answerRunning = state.currentState === "answering" || state.currentState === "streaming";
|
|
3882
3955
|
dom.meetingContext.hidden = !visible;
|
|
3883
3956
|
dom.root.classList.toggle("has-meeting", visible);
|
|
3884
|
-
const
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
}
|
|
3890
|
-
dom.contextCopy.textContent = details.length > 0 ? details.join(" · ") : state.meetingStarting ? "Preparing private context…" : "No context yet";
|
|
3891
|
-
dom.contextCopy.classList.toggle("has-context", hasContext);
|
|
3892
|
-
dom.askMeeting.disabled = !state.meeting || state.meetingStarting || state.meetingAsking || answerRunning || !hasContext;
|
|
3957
|
+
const staged = state.screenshotCount;
|
|
3958
|
+
dom.contextCopy.textContent = state.meetingStarting ? "Preparing private context…" : staged > 0 ? `${staged} screenshot${staged === 1 ? "" : "s"}` : state.meeting ? "Transcript only" : "No screenshots yet";
|
|
3959
|
+
dom.contextCopy.classList.toggle("has-context", staged > 0);
|
|
3960
|
+
const askable = state.meeting || state.monitoring && staged > 0;
|
|
3961
|
+
dom.askMeeting.disabled = !askable || state.meetingStarting || state.meetingAsking || answerRunning;
|
|
3893
3962
|
dom.askMeeting.textContent = state.meetingAsking ? "Asking…" : "Ask";
|
|
3894
3963
|
dom.askMeeting.setAttribute("aria-busy", state.meetingAsking ? "true" : "false");
|
|
3895
3964
|
}
|
|
@@ -3910,11 +3979,22 @@ function updateUsage(payload) {
|
|
|
3910
3979
|
dom.usagePill.classList.toggle("is-near-limit", usage.status === "active" && !exhausted && usage.answersUsed >= usage.answersLimit * 0.9);
|
|
3911
3980
|
dom.usagePill.classList.toggle("is-exhausted", usage.status !== "active" || exhausted);
|
|
3912
3981
|
}
|
|
3982
|
+
function updateRuntimeError(header) {
|
|
3983
|
+
const message = header.lastError;
|
|
3984
|
+
if (message === undefined || message === "") {
|
|
3985
|
+
dom.runtimeError.hidden = true;
|
|
3986
|
+
dom.runtimeErrorText.textContent = "";
|
|
3987
|
+
return;
|
|
3988
|
+
}
|
|
3989
|
+
dom.runtimeErrorText.textContent = message;
|
|
3990
|
+
dom.runtimeError.hidden = false;
|
|
3991
|
+
}
|
|
3913
3992
|
function applyHeader(header) {
|
|
3914
3993
|
updatePrivacy(header.hideFromCapture);
|
|
3915
3994
|
updateMonitoring(header);
|
|
3916
3995
|
updateMeeting(header);
|
|
3917
3996
|
updateUsage(header);
|
|
3997
|
+
updateRuntimeError(header);
|
|
3918
3998
|
}
|
|
3919
3999
|
async function loadHeader() {
|
|
3920
4000
|
if (window.blink === undefined)
|
|
@@ -3944,16 +4024,6 @@ function maybeScrollToBottom() {
|
|
|
3944
4024
|
return;
|
|
3945
4025
|
dom.body.scrollTop = dom.body.scrollHeight;
|
|
3946
4026
|
}
|
|
3947
|
-
function renderQueue(count) {
|
|
3948
|
-
if (count > 0) {
|
|
3949
|
-
dom.queuePill.textContent = `${count} queued`;
|
|
3950
|
-
dom.queuePill.hidden = false;
|
|
3951
|
-
dom.queuePill.setAttribute("aria-hidden", "false");
|
|
3952
|
-
} else {
|
|
3953
|
-
dom.queuePill.hidden = true;
|
|
3954
|
-
dom.queuePill.setAttribute("aria-hidden", "true");
|
|
3955
|
-
}
|
|
3956
|
-
}
|
|
3957
4027
|
function setReconciled(reconciled) {
|
|
3958
4028
|
dom.reconciledPill.hidden = !reconciled;
|
|
3959
4029
|
dom.reconciledPill.setAttribute("aria-hidden", reconciled ? "false" : "true");
|
|
@@ -4003,10 +4073,6 @@ function onError(payload) {
|
|
|
4003
4073
|
setStopped(false);
|
|
4004
4074
|
setState("error");
|
|
4005
4075
|
}
|
|
4006
|
-
function onQueueChange(payload) {
|
|
4007
|
-
const count = Number.isFinite(payload.pendingCount) ? Math.max(0, Math.floor(payload.pendingCount)) : 0;
|
|
4008
|
-
renderQueue(count);
|
|
4009
|
-
}
|
|
4010
4076
|
function dismiss() {
|
|
4011
4077
|
if (window.blink !== undefined) {
|
|
4012
4078
|
window.blink.dismiss();
|
|
@@ -4031,7 +4097,6 @@ dom.meeting.addEventListener("change", () => {
|
|
|
4031
4097
|
meetingStarting: false,
|
|
4032
4098
|
asking: state.meetingAsking,
|
|
4033
4099
|
showTranscript: state.showTranscript,
|
|
4034
|
-
transcriptAvailable: state.transcriptAvailable,
|
|
4035
4100
|
screenshotCount: state.screenshotCount
|
|
4036
4101
|
});
|
|
4037
4102
|
window.blink?.setMeeting(meeting);
|
|
@@ -4056,6 +4121,9 @@ dom.transcriptToggle.addEventListener("click", () => {
|
|
|
4056
4121
|
renderTranscript();
|
|
4057
4122
|
window.blink?.setShowTranscript(state.showTranscript);
|
|
4058
4123
|
});
|
|
4124
|
+
dom.runtimeErrorClose.addEventListener("click", () => {
|
|
4125
|
+
dom.runtimeError.hidden = true;
|
|
4126
|
+
});
|
|
4059
4127
|
dom.abort.addEventListener("click", () => {
|
|
4060
4128
|
if (window.blink === undefined || dom.abort.disabled)
|
|
4061
4129
|
return;
|
|
@@ -4076,7 +4144,6 @@ function bootstrap() {
|
|
|
4076
4144
|
window.blink.onAnswerDelta(onDelta);
|
|
4077
4145
|
window.blink.onAnswerDone(onDone);
|
|
4078
4146
|
window.blink.onAnswerError(onError);
|
|
4079
|
-
window.blink.onQueueChange(onQueueChange);
|
|
4080
4147
|
window.blink.onPrivacyChanged(updatePrivacy);
|
|
4081
4148
|
window.blink.onMonitoringChanged(updateMonitoring);
|
|
4082
4149
|
window.blink.onMeetingChanged(updateMeeting);
|