@dualityinstitute/blink 0.0.2 → 0.0.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/README.md +16 -11
- package/dist/cli.js +18 -81
- package/dist/electron/main.js +865 -529
- package/dist/electron/preload.cjs +31 -0
- package/dist/electron/renderer/popup.css +125 -15
- package/dist/electron/renderer/popup.html +17 -3
- package/dist/electron/renderer/popup.js +121 -42
- 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
|
@@ -27,6 +27,13 @@ var CHANNEL_PRESENTER_DISMISS = "blink:presenter-dismiss";
|
|
|
27
27
|
var CHANNEL_PRESENTER_SET_PRIVACY = "blink:presenter-set-privacy";
|
|
28
28
|
var CHANNEL_PRESENTER_PRIVACY_CHANGED = "blink:presenter-privacy-changed";
|
|
29
29
|
var CHANNEL_PRESENTER_SCROLL = "blink:presenter-scroll";
|
|
30
|
+
var CHANNEL_SETTINGS_LOAD_PROMPTS = "blink:settings-load-prompts";
|
|
31
|
+
var CHANNEL_SETTINGS_SAVE_PROMPTS = "blink:settings-save-prompts";
|
|
32
|
+
var CHANNEL_SETTINGS_LOAD_ANSWERS = "blink:settings-load-answers";
|
|
33
|
+
var CHANNEL_SETTINGS_DISMISS = "blink:settings-dismiss";
|
|
34
|
+
var CHANNEL_SETTINGS_LOAD_TRANSCRIPTS = "blink:settings-load-transcripts";
|
|
35
|
+
var CHANNEL_SETTINGS_READ_TRANSCRIPT = "blink:settings-read-transcript";
|
|
36
|
+
var CHANNEL_SETTINGS_SHOW_TAB = "blink:settings-show-tab";
|
|
30
37
|
|
|
31
38
|
// src/electron/preload.ts
|
|
32
39
|
function subscribe(channel, callback) {
|
|
@@ -111,5 +118,29 @@ var presenterAPI = {
|
|
|
111
118
|
subscribe(CHANNEL_PRESENTER_SCROLL, callback);
|
|
112
119
|
}
|
|
113
120
|
};
|
|
121
|
+
var settingsAPI = {
|
|
122
|
+
loadPrompts() {
|
|
123
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_PROMPTS);
|
|
124
|
+
},
|
|
125
|
+
savePrompts(prompts) {
|
|
126
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_SAVE_PROMPTS, prompts);
|
|
127
|
+
},
|
|
128
|
+
loadAnswers() {
|
|
129
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_ANSWERS);
|
|
130
|
+
},
|
|
131
|
+
loadTranscripts(query) {
|
|
132
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_LOAD_TRANSCRIPTS, query);
|
|
133
|
+
},
|
|
134
|
+
readTranscript(file) {
|
|
135
|
+
return import_electron.ipcRenderer.invoke(CHANNEL_SETTINGS_READ_TRANSCRIPT, file);
|
|
136
|
+
},
|
|
137
|
+
dismiss() {
|
|
138
|
+
import_electron.ipcRenderer.send(CHANNEL_SETTINGS_DISMISS);
|
|
139
|
+
},
|
|
140
|
+
onShowTab(callback) {
|
|
141
|
+
subscribe(CHANNEL_SETTINGS_SHOW_TAB, callback);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
114
144
|
import_electron.contextBridge.exposeInMainWorld("blink", blinkAPI);
|
|
115
145
|
import_electron.contextBridge.exposeInMainWorld("presenter", presenterAPI);
|
|
146
|
+
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,6 +68,11 @@
|
|
|
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>
|
|
@@ -108,14 +113,22 @@
|
|
|
108
113
|
>Ask</button>
|
|
109
114
|
</div>
|
|
110
115
|
|
|
111
|
-
<div
|
|
116
|
+
<div class="workspace" id="workspace">
|
|
117
|
+
<aside
|
|
112
118
|
class="transcript-panel"
|
|
113
119
|
id="transcript-panel"
|
|
114
120
|
aria-label="Live meeting transcript"
|
|
115
121
|
hidden
|
|
116
122
|
>
|
|
117
|
-
<div class="transcript-
|
|
118
|
-
|
|
123
|
+
<div class="transcript-heading">
|
|
124
|
+
<span class="transcript-heading-label">Live transcript</span>
|
|
125
|
+
<span class="transcript-count" id="transcript-count"></span>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="transcript-scroll" id="transcript-scroll">
|
|
128
|
+
<div class="transcript-lines" id="transcript-lines"></div>
|
|
129
|
+
<div class="transcript-volatile" id="transcript-volatile"></div>
|
|
130
|
+
</div>
|
|
131
|
+
</aside>
|
|
119
132
|
|
|
120
133
|
<section class="card-body" id="body">
|
|
121
134
|
<div class="answering" id="answering" hidden>
|
|
@@ -135,6 +148,7 @@
|
|
|
135
148
|
<div class="error-message" id="error-message"></div>
|
|
136
149
|
</div>
|
|
137
150
|
</section>
|
|
151
|
+
</div>
|
|
138
152
|
</main>
|
|
139
153
|
<script src="./popup.js"></script>
|
|
140
154
|
</body>
|
|
@@ -3736,10 +3736,9 @@ var state = {
|
|
|
3736
3736
|
meeting: false,
|
|
3737
3737
|
meetingStarting: false,
|
|
3738
3738
|
meetingAsking: false,
|
|
3739
|
-
transcriptAvailable: false,
|
|
3740
3739
|
screenshotCount: 0,
|
|
3741
3740
|
showTranscript: true,
|
|
3742
|
-
|
|
3741
|
+
meetingStartedAt: null
|
|
3743
3742
|
};
|
|
3744
3743
|
function el(id) {
|
|
3745
3744
|
const node = document.getElementById(id);
|
|
@@ -3762,7 +3761,10 @@ var dom = {
|
|
|
3762
3761
|
askMeeting: el("ask-meeting"),
|
|
3763
3762
|
transcriptToggle: el("transcript-toggle"),
|
|
3764
3763
|
transcriptPanel: el("transcript-panel"),
|
|
3764
|
+
transcriptScroll: el("transcript-scroll"),
|
|
3765
3765
|
transcriptLines: el("transcript-lines"),
|
|
3766
|
+
transcriptVolatile: el("transcript-volatile"),
|
|
3767
|
+
transcriptCount: el("transcript-count"),
|
|
3766
3768
|
privacy: el("privacy"),
|
|
3767
3769
|
privacyLabel: el("privacy-label"),
|
|
3768
3770
|
usagePill: el("usage-pill"),
|
|
@@ -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) {
|
|
@@ -3823,73 +3828,134 @@ function updateMonitoring(payload) {
|
|
|
3823
3828
|
dom.monitoringControl.classList.toggle("is-starting", payload.monitoringStarting);
|
|
3824
3829
|
dom.monitoringControl.setAttribute("aria-busy", payload.monitoringStarting ? "true" : "false");
|
|
3825
3830
|
}
|
|
3831
|
+
var transcriptRows = new Map;
|
|
3826
3832
|
function renderTranscript() {
|
|
3827
3833
|
const expanded = state.showTranscript;
|
|
3828
3834
|
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
|
-
|
|
3835
|
+
const visible = state.meeting && expanded;
|
|
3836
|
+
dom.transcriptPanel.hidden = !visible;
|
|
3837
|
+
dom.root.classList.toggle("has-transcript", visible);
|
|
3838
|
+
updateTranscriptPlaceholder();
|
|
3839
|
+
}
|
|
3840
|
+
function buildTranscriptRow(line) {
|
|
3841
|
+
const row = document.createElement("div");
|
|
3842
|
+
row.className = "transcript-line";
|
|
3843
|
+
const speaker = document.createElement("span");
|
|
3844
|
+
speaker.className = `transcript-speaker is-${line.speaker}`;
|
|
3845
|
+
speaker.textContent = line.speaker === "them" ? "Them" : "You";
|
|
3846
|
+
const text2 = document.createElement("span");
|
|
3847
|
+
text2.className = "transcript-text";
|
|
3848
|
+
text2.textContent = line.text;
|
|
3849
|
+
row.append(speaker, text2);
|
|
3850
|
+
return row;
|
|
3851
|
+
}
|
|
3852
|
+
function setRowText(row, text2) {
|
|
3853
|
+
const node = row.querySelector(".transcript-text");
|
|
3854
|
+
if (node !== null)
|
|
3855
|
+
node.textContent = text2;
|
|
3856
|
+
}
|
|
3857
|
+
function updateTranscriptPlaceholder() {
|
|
3858
|
+
const empty = transcriptRows.size === 0 && dom.transcriptVolatile.childElementCount === 0;
|
|
3859
|
+
dom.transcriptLines.classList.toggle("is-empty", empty);
|
|
3860
|
+
dom.transcriptCount.textContent = transcriptRows.size === 0 ? "" : String(transcriptRows.size);
|
|
3861
|
+
}
|
|
3862
|
+
function isTranscriptAtBottom() {
|
|
3863
|
+
const node = dom.transcriptScroll;
|
|
3864
|
+
return node.scrollHeight - node.scrollTop - node.clientHeight < 40;
|
|
3853
3865
|
}
|
|
3854
3866
|
function pinTranscriptToBottom() {
|
|
3855
|
-
dom.
|
|
3867
|
+
dom.transcriptScroll.scrollTop = dom.transcriptScroll.scrollHeight;
|
|
3856
3868
|
}
|
|
3857
3869
|
function onTranscriptUpdate(payload) {
|
|
3858
|
-
|
|
3859
|
-
|
|
3870
|
+
if (payload.reset === true) {
|
|
3871
|
+
transcriptRows.clear();
|
|
3872
|
+
dom.transcriptLines.replaceChildren();
|
|
3873
|
+
dom.transcriptVolatile.replaceChildren();
|
|
3874
|
+
}
|
|
3875
|
+
const follow = isTranscriptAtBottom();
|
|
3876
|
+
for (const line of payload.changed ?? []) {
|
|
3877
|
+
const existing = transcriptRows.get(line.id);
|
|
3878
|
+
if (existing !== undefined) {
|
|
3879
|
+
setRowText(existing, line.text);
|
|
3880
|
+
continue;
|
|
3881
|
+
}
|
|
3882
|
+
const row = buildTranscriptRow(line);
|
|
3883
|
+
transcriptRows.set(line.id, row);
|
|
3884
|
+
dom.transcriptLines.append(row);
|
|
3885
|
+
}
|
|
3886
|
+
dom.transcriptVolatile.replaceChildren(...(payload.volatile ?? []).map((line) => {
|
|
3887
|
+
const row = buildTranscriptRow(line);
|
|
3888
|
+
row.classList.add("is-partial");
|
|
3889
|
+
return row;
|
|
3890
|
+
}));
|
|
3891
|
+
updateTranscriptPlaceholder();
|
|
3892
|
+
if (follow)
|
|
3893
|
+
pinTranscriptToBottom();
|
|
3894
|
+
}
|
|
3895
|
+
function clearMeetingAnswer() {
|
|
3896
|
+
const label = document.createElement("span");
|
|
3897
|
+
label.className = "source-label";
|
|
3898
|
+
label.textContent = "Ready for a screenshot";
|
|
3899
|
+
dom.source.replaceChildren(label);
|
|
3900
|
+
delete dom.source.dataset["source"];
|
|
3901
|
+
dom.answer.replaceChildren();
|
|
3902
|
+
dom.errorMessage.textContent = "";
|
|
3903
|
+
state.streamBuffer = "";
|
|
3904
|
+
setReconciled(false);
|
|
3905
|
+
setStopped(false);
|
|
3906
|
+
setState("idle");
|
|
3860
3907
|
}
|
|
3861
3908
|
function updateMeeting(payload) {
|
|
3909
|
+
const wasMeeting = state.meeting;
|
|
3862
3910
|
state.meeting = payload.meeting;
|
|
3863
3911
|
state.meetingStarting = payload.meetingStarting;
|
|
3864
3912
|
state.meetingAsking = payload.asking;
|
|
3865
|
-
state.transcriptAvailable = payload.transcriptAvailable;
|
|
3866
3913
|
state.screenshotCount = Math.max(0, Math.floor(payload.screenshotCount));
|
|
3867
3914
|
state.showTranscript = payload.showTranscript;
|
|
3868
3915
|
renderTranscript();
|
|
3916
|
+
state.meetingStartedAt = payload.startedAt ?? null;
|
|
3869
3917
|
const active = payload.meeting || payload.meetingStarting;
|
|
3870
3918
|
dom.meeting.checked = active;
|
|
3871
3919
|
dom.meeting.disabled = payload.meetingStarting;
|
|
3872
|
-
dom.meetingLabel.textContent = payload.meetingStarting ? "Starting…" : "Meeting";
|
|
3873
3920
|
dom.meetingControl.classList.toggle("is-meeting", active);
|
|
3921
|
+
dom.meetingControl.classList.toggle("is-live", payload.meeting);
|
|
3922
|
+
dom.meetingControl.title = payload.meeting ? "Leave the meeting and stop recording" : "Capture private Meeting context";
|
|
3923
|
+
renderMeetingClock();
|
|
3874
3924
|
dom.meetingControl.classList.toggle("is-starting", payload.meetingStarting);
|
|
3875
3925
|
dom.meetingControl.setAttribute("aria-busy", payload.meetingStarting ? "true" : "false");
|
|
3876
3926
|
renderMeetingContext();
|
|
3927
|
+
if (wasMeeting && !payload.meeting)
|
|
3928
|
+
clearMeetingAnswer();
|
|
3929
|
+
}
|
|
3930
|
+
function elapsedLabel(startedAt) {
|
|
3931
|
+
const total = Math.max(0, Math.floor((Date.now() - startedAt) / 1000));
|
|
3932
|
+
const hours = Math.floor(total / 3600);
|
|
3933
|
+
const minutes = Math.floor(total % 3600 / 60);
|
|
3934
|
+
const seconds = total % 60;
|
|
3935
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
3936
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`;
|
|
3937
|
+
}
|
|
3938
|
+
function renderMeetingClock() {
|
|
3939
|
+
if (state.meetingStarting) {
|
|
3940
|
+
dom.meetingLabel.textContent = "Starting…";
|
|
3941
|
+
return;
|
|
3942
|
+
}
|
|
3943
|
+
if (!state.meeting || state.meetingStartedAt === null) {
|
|
3944
|
+
dom.meetingLabel.textContent = "Meeting";
|
|
3945
|
+
return;
|
|
3946
|
+
}
|
|
3947
|
+
dom.meetingLabel.textContent = `Leave · ${elapsedLabel(state.meetingStartedAt)}`;
|
|
3877
3948
|
}
|
|
3949
|
+
window.setInterval(renderMeetingClock, 1000);
|
|
3878
3950
|
function renderMeetingContext() {
|
|
3879
3951
|
const visible = state.meeting || state.meetingStarting;
|
|
3880
|
-
const hasContext = state.transcriptAvailable || state.screenshotCount > 0;
|
|
3881
3952
|
const answerRunning = state.currentState === "answering" || state.currentState === "streaming";
|
|
3882
3953
|
dom.meetingContext.hidden = !visible;
|
|
3883
3954
|
dom.root.classList.toggle("has-meeting", visible);
|
|
3884
|
-
const
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
details.push(`${state.screenshotCount} screenshot${state.screenshotCount === 1 ? "" : "s"}`);
|
|
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;
|
|
3955
|
+
const staged = state.screenshotCount;
|
|
3956
|
+
dom.contextCopy.textContent = state.meetingStarting ? "Preparing private context…" : staged > 0 ? `${staged} screenshot${staged === 1 ? "" : "s"}` : "Transcript only";
|
|
3957
|
+
dom.contextCopy.classList.toggle("has-context", staged > 0);
|
|
3958
|
+
dom.askMeeting.disabled = !state.meeting || state.meetingStarting || state.meetingAsking || answerRunning;
|
|
3893
3959
|
dom.askMeeting.textContent = state.meetingAsking ? "Asking…" : "Ask";
|
|
3894
3960
|
dom.askMeeting.setAttribute("aria-busy", state.meetingAsking ? "true" : "false");
|
|
3895
3961
|
}
|
|
@@ -3910,11 +3976,22 @@ function updateUsage(payload) {
|
|
|
3910
3976
|
dom.usagePill.classList.toggle("is-near-limit", usage.status === "active" && !exhausted && usage.answersUsed >= usage.answersLimit * 0.9);
|
|
3911
3977
|
dom.usagePill.classList.toggle("is-exhausted", usage.status !== "active" || exhausted);
|
|
3912
3978
|
}
|
|
3979
|
+
function updateRuntimeError(header) {
|
|
3980
|
+
const message = header.lastError;
|
|
3981
|
+
if (message === undefined || message === "") {
|
|
3982
|
+
dom.runtimeError.hidden = true;
|
|
3983
|
+
dom.runtimeErrorText.textContent = "";
|
|
3984
|
+
return;
|
|
3985
|
+
}
|
|
3986
|
+
dom.runtimeErrorText.textContent = message;
|
|
3987
|
+
dom.runtimeError.hidden = false;
|
|
3988
|
+
}
|
|
3913
3989
|
function applyHeader(header) {
|
|
3914
3990
|
updatePrivacy(header.hideFromCapture);
|
|
3915
3991
|
updateMonitoring(header);
|
|
3916
3992
|
updateMeeting(header);
|
|
3917
3993
|
updateUsage(header);
|
|
3994
|
+
updateRuntimeError(header);
|
|
3918
3995
|
}
|
|
3919
3996
|
async function loadHeader() {
|
|
3920
3997
|
if (window.blink === undefined)
|
|
@@ -4031,7 +4108,6 @@ dom.meeting.addEventListener("change", () => {
|
|
|
4031
4108
|
meetingStarting: false,
|
|
4032
4109
|
asking: state.meetingAsking,
|
|
4033
4110
|
showTranscript: state.showTranscript,
|
|
4034
|
-
transcriptAvailable: state.transcriptAvailable,
|
|
4035
4111
|
screenshotCount: state.screenshotCount
|
|
4036
4112
|
});
|
|
4037
4113
|
window.blink?.setMeeting(meeting);
|
|
@@ -4056,6 +4132,9 @@ dom.transcriptToggle.addEventListener("click", () => {
|
|
|
4056
4132
|
renderTranscript();
|
|
4057
4133
|
window.blink?.setShowTranscript(state.showTranscript);
|
|
4058
4134
|
});
|
|
4135
|
+
dom.runtimeErrorClose.addEventListener("click", () => {
|
|
4136
|
+
dom.runtimeError.hidden = true;
|
|
4137
|
+
});
|
|
4059
4138
|
dom.abort.addEventListener("click", () => {
|
|
4060
4139
|
if (window.blink === undefined || dom.abort.disabled)
|
|
4061
4140
|
return;
|