@sailfish-ai/recorder 1.7.16 → 1.7.18

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/dist/modal.js CHANGED
@@ -25,12 +25,12 @@ export function setupIssueReporting(options) {
25
25
  const isCmdOrCtrl = e.metaKey || e.ctrlKey;
26
26
  // Shortcuts for modal open
27
27
  if (!typingInInput && options.enableShortcuts && !isCmdOrCtrl) {
28
- if (key === "o") {
28
+ if (key === "e") {
29
29
  e.preventDefault();
30
30
  injectModalHTML("lookback");
31
31
  return;
32
32
  }
33
- if (key === "c") {
33
+ if (key === "n") {
34
34
  e.preventDefault();
35
35
  injectModalHTML("startnow");
36
36
  return;
@@ -59,10 +59,10 @@ export function setupIssueReporting(options) {
59
59
  key === "r" &&
60
60
  modalOpen &&
61
61
  currentState.mode === "startnow") {
62
- const recordIcon = document.getElementById("sf-record-icon");
63
- if (recordIcon) {
62
+ const recordBtn = document.getElementById("sf-start-recording-btn");
63
+ if (recordBtn) {
64
64
  e.preventDefault();
65
- recordIcon.click();
65
+ recordBtn.click();
66
66
  }
67
67
  }
68
68
  });
@@ -107,7 +107,7 @@ function injectModalHTML(initialMode = "lookback") {
107
107
  <div style="position:fixed; inset:0; background:rgba(0,0,0,0.4); z-index:9998;"></div>
108
108
  <div style="position:fixed; top:50%; left:50%; transform:translate(-50%, -50%);
109
109
  background:#fff; padding:24px; border-radius:12px;
110
- width:424px; max-width:90%; z-index:9999;
110
+ width:476px; max-width:90%; z-index:9999;
111
111
  box-shadow:0 4px 20px rgba(0,0,0,0.15); font-family:sans-serif;">
112
112
 
113
113
  <button id="sf-modal-close-btn"
@@ -121,13 +121,13 @@ function injectModalHTML(initialMode = "lookback") {
121
121
  <h2 style="font-size:18px; font-weight:600; margin-bottom:16px;">Report Issue</h2>
122
122
 
123
123
  <div id="sf-issue-tabs" style="display:flex; gap:4px; margin-bottom:16px; background:#f1f5f9; padding:6px; border-radius:6px; width: fit-content;">
124
- <button id="sf-tab-lookback" data-mode="lookback" class="issue-tab ${!isStartNow ? "active" : ""}"
125
- style="padding:6px 12px; border:none; background:${!isStartNow ? "white" : "transparent"}; color: ${!isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px;">
126
- Already occurred <span style="color:#94a3b8;">[o]</span>
124
+ <button id="sf-tab-lookback" data-mode="lookback" class="sf-issue-tab ${!isStartNow ? "active" : ""}"
125
+ style="padding:6px 12px; border:none; background:${!isStartNow ? "white" : "transparent"}; color: ${!isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px; display: flex; align-items: center; gap: 8px;">
126
+ Existing <span style="background: #F1F5F9; border:1px solid #cbd5e1; border-radius: 4px; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; color: #94A3B8; font-weight: 500;">e</span>
127
127
  </button>
128
- <button id="sf-tab-startnow" data-mode="startnow" class="issue-tab ${isStartNow ? "active" : ""}"
129
- style="padding:6px 12px; border:none; background:${isStartNow ? "white" : "transparent"}; color: ${isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px;">
130
- Capture new <span style="color:#94a3b8;">[c]</span>
128
+ <button id="sf-tab-startnow" data-mode="startnow" class="sf-issue-tab ${isStartNow ? "active" : ""}"
129
+ style="padding:6px 12px; border:none; background:${isStartNow ? "white" : "transparent"}; color: ${isStartNow ? "#0F172A" : "#64748B"}; border-radius:4px; font-size:14px; cursor:pointer; font-weight:500; height:32px; display: flex; align-items: center; gap: 8px;">
130
+ Capture new <span style="background: #F1F5F9; border:1px solid #cbd5e1; border-radius: 4px; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; color: #94A3B8; font-weight: 500;">n</span>
131
131
  </button>
132
132
  </div>
133
133
 
@@ -138,11 +138,7 @@ function injectModalHTML(initialMode = "lookback") {
138
138
  <path d="M7 9.33333V7" stroke="#A1A1AA" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
139
139
  <path d="M7 4.66663H7.00583" stroke="#A1A1AA" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
140
140
  </g>
141
- <defs>
142
- <clipPath id="clip0_2477_11797">
143
- <rect width="14" height="14" fill="white"/>
144
- </clipPath>
145
- </defs>
141
+ <defs><clipPath id="clip0_2477_11797"><rect width="14" height="14" fill="white"/></clipPath></defs>
146
142
  </svg>
147
143
  <div style="font-size:14px;">
148
144
  ${isStartNow
@@ -159,67 +155,76 @@ function injectModalHTML(initialMode = "lookback") {
159
155
  border:1px solid #cbd5e1; border-radius:6px; margin-bottom:20px;
160
156
  resize:none; outline:none;"></textarea>
161
157
 
162
- <div id="sf-lookback-timestamp-container" style="display: ${isStartNow ? "none" : "block"};">
163
- <label for="sf-lookback-time-input" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
164
- Time stamp
158
+ <div id="sf-lookback-container" style="display:${isStartNow ? "none" : "block"}; margin-bottom:20px;">
159
+ <label for="sf-lookback-minutes" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
160
+ When does this happened?
165
161
  </label>
166
-
167
- <div id="sf-lookback-time-input" style="display:flex; align-items:center; gap:6px; padding:8px 12px;
168
- border:1px solid #cbd5e1; border-radius:6px; width:fit-content; font-size:14px; color:#0f172a; cursor:pointer;">
169
- <span id="sf-lookback-time-display">00:00</span>
170
- <svg width="16" height="16" fill="none" stroke="#0f172a" stroke-width="1.5" viewBox="0 0 24 24">
171
- <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6l4 2" />
172
- <circle cx="12" cy="12" r="9" stroke-linecap="round" stroke-linejoin="round" />
173
- </svg>
174
- </div>
175
-
176
- <div id="sf-lookback-dropdown" style="display:none; position:absolute; background:white; border:1px solid #e5e7eb;
177
- border-radius:6px; padding:12px; margin-top:4px; box-shadow:0 4px 12px rgba(0,0,0,0.1); z-index:10000;">
178
- <div style="display:flex; gap:12px;">
179
- <div>
180
- <label style="font-size:12px; color:#64748b;">Minutes</label>
181
- <select id="sf-lookback-minutes" style="margin-top:4px; padding:6px; border:1px solid #cbd5e1; border-radius:4px;">
182
- ${Array.from({ length: 60 }, (_, i) => `<option value="${i}">${i
183
- .toString()
184
- .padStart(2, "0")}</option>`).join("")}
185
- </select>
186
- </div>
187
- <div>
188
- <label style="font-size:12px; color:#64748b;">Seconds</label>
189
- <select id="sf-lookback-seconds" style="margin-top:4px; padding:6px; border:1px solid #cbd5e1; border-radius:4px;">
190
- ${Array.from({ length: 60 }, (_, i) => `<option value="${i}">${i
191
- .toString()
192
- .padStart(2, "0")}</option>`).join("")}
193
- </select>
162
+ <div style="display:flex; align-items:center; gap:6px;">
163
+ <div style="position:relative;">
164
+ <select id="sf-lookback-minutes"
165
+ style="
166
+ background: white;
167
+ padding: 4px 8px;
168
+ font-size: 14px;
169
+ border: 1px solid #cbd5e1;
170
+ border-radius: 6px;
171
+ color: #64748B;
172
+ font-family: sans-serif;
173
+ outline: none;
174
+ box-shadow: none;
175
+ appearance: none;
176
+ -webkit-appearance: none;
177
+ -moz-appearance: none;
178
+ padding-right: 28px;
179
+ cursor: pointer;
180
+ font-size: 14px;
181
+ line-height: 20px;
182
+ ">
183
+ ${Array.from({ length: 15 }, (_, i) => {
184
+ const value = i + 1;
185
+ const selected = value === 2 ? "selected" : "";
186
+ return `<option value="${value}" ${selected}>${value}</option>`;
187
+ }).join("")}
188
+ </select>
189
+ <div style="position:absolute; left:32px; top:50%; transform:translateY(-50%); pointer-events:none;">
190
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none"
191
+ xmlns="http://www.w3.org/2000/svg">
192
+ <path d="M4 6L8 10L12 6" stroke="#52525B" stroke-width="1.33333"
193
+ stroke-linecap="round" stroke-linejoin="round"/>
194
+ </svg>
194
195
  </div>
195
196
  </div>
197
+ <span style="font-size:14px; color: #64748B;">mins ago</span>
196
198
  </div>
197
-
198
- <div style="font-size:12px; color:#a1a1aa; margin-top:4px;">Enter time stamp here. Max 1 hour.</div>
199
199
  </div>
200
200
 
201
- <div id="sf-start-recording" style="display: ${isStartNow ? "block" : "none"};">
202
- <label for="sf-inline-record-chip" style="display:block; font-size:14px; font-weight:500; margin-bottom:6px;">
203
- Start recording <span style="color:#94a3b8;">[r]</span>
204
- </label>
201
+ <div id="sf-modal-footer" style="display:flex; justify-content:${isStartNow ? "space-between" : "flex-end"}; align-items:flex-end;">
205
202
 
206
- <div id="sf-inline-record-chip" style="display:flex; margin-top:8px; align-items:center; gap:8px;">
207
- <div id="sf-record-icon" style="padding: 8px; border-radius: 6px; border: 1px solid #9dd3ef; cursor: pointer;">
208
- <div style="width: 14px; height: 14px; background: #fc5555; border-radius: 50%; border: 1px solid #991b1b;"></div>
209
- </div>
210
- <div>
211
- <div id="sf-inline-record-timer" style="font-family: monospace; font-weight: 500; color: #d4d4d8;">00:00</div>
212
- <div style="font-size: 12px; color: #a1a1aa; margin-top: 2px;">Max time stamp 1 hour.</div>
203
+ <div id="sf-record-button-container" style="display:${isStartNow ? "block" : "none"};">
204
+ <div id="sf-recording-timer-label" style="display:none; font-size:14px; margin-bottom:20px;">
205
+ Recording: <span id="sf-recording-timer-display">00:00</span>
213
206
  </div>
207
+ <button id="sf-start-recording-btn"
208
+ style="display:flex; align-items:center; gap:8px; border:1px solid #fc5555;
209
+ background:transparent; padding:6px 12px; font-size:14px;
210
+ color: #fc5555; border-radius:6px; cursor:pointer; font-weight:500;">
211
+ <div id="sf-record-icon" style="padding: 6px; border-radius: 6px; border: 1px solid #fc5555; cursor: pointer;">
212
+ <div style="width: 14px; height: 14px; background: #fc5555; border-radius: 50%; border: 1px solid #991b1b;"></div>
213
+ </div>
214
+ <span>Start Recording</span>
215
+ <span style="background: #F1F5F9; border:1px solid #cbd5e1; border-radius: 4px; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; color: #94A3B8; font-weight: 500;">r</span>
216
+ </button>
214
217
  </div>
215
- </div>
216
218
 
217
- <div style="display:flex; justify-content:flex-end; margin-top:16px;">
218
219
  <button id="sf-issue-submit-btn"
219
220
  style="background: #295DBF; color:white; border:none; padding:8px 16px;
220
- border-radius:6px; font-size:14px; line-height: 24px; font-weight:500; cursor:not-allowed; opacity:0.4;"
221
- disabled>
222
- Create Triage <span style="color: #E2E8F0; margin-left:6px; font-size:10px; line-height: 20px;">[cmd+enter]</span>
221
+ border-radius:6px; font-size:14px; line-height: 24px; font-weight:500;
222
+ cursor:${isStartNow ? "not-allowed" : "pointer"}; opacity:${isStartNow ? "0.4" : "1"}; margin-bottom:1px" ${isStartNow ? "disabled" : ""}>
223
+ Create Triage <span style="margin-left: 8px; display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 14px; line-height:18px;">
224
+ <span style="background: #F1F5F9; border-radius: 4px; padding: 0 6px; font-weight: 500;">cmd</span>
225
+ +
226
+ <span style="background: #F1F5F9; border-radius: 4px; padding: 0 6px; font-weight: 500;">enter</span>
227
+ </span>
223
228
  </button>
224
229
  </div>
225
230
  </div>
@@ -245,121 +250,96 @@ function setActiveTab(mode) {
245
250
  }
246
251
  }
247
252
  function updateModeSpecificUI(mode) {
248
- const infoText = document.getElementById("sf-issue-mode-info");
249
- const infoMessage = infoText?.querySelector("div");
253
+ const infoMessage = document.querySelector("#sf-issue-mode-info div");
250
254
  const submitBtn = document.getElementById("sf-issue-submit-btn");
251
- const timerText = document.getElementById("sf-inline-record-timer");
252
- const timestampContainer = document.getElementById("sf-lookback-timestamp-container");
253
- const startRecording = document.getElementById("sf-start-recording");
254
- if (!infoText || !infoMessage || !submitBtn)
255
+ const recordContainer = document.getElementById("sf-record-button-container");
256
+ const recordLabel = document.getElementById("sf-recording-timer-label");
257
+ const recordTime = document.getElementById("sf-recording-timer-display");
258
+ const footer = document.getElementById("sf-modal-footer");
259
+ const lookbackContainer = document.getElementById("sf-lookback-container");
260
+ if (!infoMessage ||
261
+ !submitBtn ||
262
+ !recordContainer ||
263
+ !recordLabel ||
264
+ !recordTime ||
265
+ !footer ||
266
+ !lookbackContainer)
255
267
  return;
256
268
  if (mode === "startnow") {
257
- if (startRecording)
258
- startRecording.style.display = "block";
259
- if (timestampContainer)
260
- timestampContainer.style.display = "none";
269
+ // Show record button, hide lookback
270
+ recordContainer.style.display = "block";
271
+ lookbackContainer.style.display = "none";
272
+ footer.style.justifyContent = "space-between";
261
273
  infoMessage.textContent = "I want to reproduce the issue right now.";
262
- // Disable submit if recording not done yet
263
274
  const canSubmit = recordingStartTime !== null && recordingEndTime !== null;
264
275
  submitBtn.disabled = !canSubmit;
265
276
  submitBtn.style.opacity = canSubmit ? "1" : "0.4";
266
277
  submitBtn.style.cursor = canSubmit ? "pointer" : "not-allowed";
267
- // Show current timer value if available
268
- if (recordingStartTime && recordingEndTime && timerText) {
278
+ if (recordingStartTime && recordingEndTime) {
269
279
  const duration = Math.floor((recordingEndTime - recordingStartTime) / 1000);
270
280
  const mins = String(Math.floor(duration / 60)).padStart(2, "0");
271
281
  const secs = String(duration % 60).padStart(2, "0");
272
- timerText.textContent = `${mins}:${secs}`;
273
- timerText.style.color = "#171717";
282
+ recordLabel.style.display = "block";
283
+ recordTime.textContent = `${mins}:${secs}`;
274
284
  }
275
- else if (timerText) {
276
- timerText.textContent = "00:00";
277
- timerText.style.color = "#d4d4d8";
285
+ else {
286
+ recordLabel.style.display = "none";
278
287
  }
279
288
  }
280
- else if (mode === "lookback") {
281
- if (startRecording)
282
- startRecording.style.display = "none";
283
- if (timestampContainer)
284
- timestampContainer.style.display = "block";
289
+ else {
290
+ // Show lookback, hide recording
291
+ recordContainer.style.display = "none";
292
+ recordLabel.style.display = "none";
293
+ lookbackContainer.style.display = "block";
294
+ footer.style.justifyContent = "flex-end";
285
295
  infoMessage.textContent =
286
296
  "Something already happened. Capture the past few minutes.";
287
- // Enable submit only if a valid timestamp is selected
288
- const mins = Number(document.getElementById("sf-lookback-minutes")
289
- ?.value || "0");
290
- const secs = Number(document.getElementById("sf-lookback-seconds")
291
- ?.value || "0");
292
- const canSubmit = mins > 0 || secs > 0;
293
- submitBtn.disabled = !canSubmit;
294
- submitBtn.style.opacity = canSubmit ? "1" : "0.4";
295
- submitBtn.style.cursor = canSubmit ? "pointer" : "not-allowed";
297
+ submitBtn.disabled = false;
298
+ submitBtn.style.opacity = "1";
299
+ submitBtn.style.cursor = "pointer";
296
300
  }
297
301
  }
298
302
  function bindListeners() {
299
- const timeInput = document.getElementById("sf-lookback-time-input");
300
- const dropdown = document.getElementById("sf-lookback-dropdown");
301
- const display = document.getElementById("sf-lookback-time-display");
303
+ const tabButtons = modalEl?.querySelectorAll(".sf-issue-tab");
304
+ const recordBtn = document.getElementById("sf-start-recording-btn");
302
305
  const closeBtn = document.getElementById("sf-modal-close-btn");
303
- const recordIcon = document.getElementById("sf-record-icon");
304
- if (closeBtn) {
305
- closeBtn.onclick = () => {
306
- closeModal();
307
- };
308
- }
309
- if (recordIcon) {
310
- recordIcon.onclick = () => {
311
- const descInput = document.getElementById("sf-issue-description");
312
- currentState.description = descInput?.value ?? "";
313
- startCountdownThenRecord();
314
- };
315
- }
316
- timeInput?.addEventListener("click", () => {
317
- if (!dropdown)
318
- return;
319
- if (dropdown.style.display === "none") {
320
- dropdown.style.display = "block";
321
- }
322
- else {
323
- dropdown.style.display = "none";
324
- }
325
- });
326
- window.addEventListener("click", (e) => {
327
- if (!dropdown)
328
- return;
329
- if (!dropdown.contains(e.target) &&
330
- !timeInput?.contains(e.target)) {
331
- dropdown.style.display = "none";
332
- }
333
- });
334
- const updateTimeDisplay = () => {
335
- const mins = Number(document.getElementById("sf-lookback-minutes")
336
- ?.value || "0");
337
- const secs = Number(document.getElementById("sf-lookback-seconds")
338
- ?.value || "0");
339
- if (display) {
340
- display.textContent = `${mins.toString().padStart(2, "0")}:${secs
341
- .toString()
342
- .padStart(2, "0")}`;
343
- }
344
- updateModeSpecificUI("lookback"); // re-check submit button status
345
- };
346
- // Handle dropdown value changes
347
- document
348
- .getElementById("sf-lookback-minutes")
349
- ?.addEventListener("change", updateTimeDisplay);
350
- document
351
- .getElementById("sf-lookback-seconds")
352
- ?.addEventListener("change", updateTimeDisplay);
353
- modalEl?.querySelectorAll(".issue-tab").forEach((tabBtn) => {
306
+ const submitBtn = document.getElementById("sf-issue-submit-btn");
307
+ const minutesSelect = document.getElementById("sf-lookback-minutes");
308
+ // Tab switch
309
+ tabButtons?.forEach((tabBtn) => {
354
310
  tabBtn.addEventListener("click", (e) => {
355
311
  const mode = e.currentTarget.dataset
356
312
  .mode;
357
- currentState.mode = mode;
358
313
  setActiveTab(mode);
359
- // Optionally toggle relevant UI
360
314
  updateModeSpecificUI(mode);
361
315
  });
362
316
  });
317
+ // Close modal
318
+ if (closeBtn) {
319
+ closeBtn.onclick = closeModal;
320
+ }
321
+ // Time select change
322
+ if (minutesSelect) {
323
+ minutesSelect.addEventListener("change", () => {
324
+ if (currentState.mode === "lookback") {
325
+ // Always valid now, so no need to validate input
326
+ submitBtn.disabled = false;
327
+ submitBtn.style.opacity = "1";
328
+ submitBtn.style.cursor = "pointer";
329
+ }
330
+ });
331
+ }
332
+ // Start recording
333
+ if (recordBtn) {
334
+ recordBtn.onclick = () => {
335
+ const descInput = document.getElementById("sf-issue-description");
336
+ if (descInput) {
337
+ currentState.description = descInput.value;
338
+ }
339
+ startCountdownThenRecord();
340
+ };
341
+ }
342
+ // Submit
363
343
  modalEl?.addEventListener("click", (e) => {
364
344
  const target = e.target;
365
345
  if (target.closest("#sf-issue-submit-btn")) {
@@ -374,12 +354,8 @@ function bindListeners() {
374
354
  endTimestamp = recordingEndTime ?? Date.now();
375
355
  }
376
356
  else {
377
- // Parse time from time selector
378
- const minutes = Number(document.getElementById("sf-lookback-minutes")
379
- ?.value || "0");
380
- const seconds = Number(document.getElementById("sf-lookback-seconds")
381
- ?.value || "0");
382
- const delta = (minutes * 60 + seconds) * 1000;
357
+ const minutes = Number(minutesSelect?.value || "2");
358
+ const delta = minutes * 60 * 1000;
383
359
  endTimestamp = Date.now();
384
360
  startTimestamp = endTimestamp - delta;
385
361
  }
@@ -431,28 +407,40 @@ function showFloatingTimer() {
431
407
  timer.id = "sf-recording-indicator";
432
408
  timer.style.cssText = `
433
409
  position: fixed;
434
- bottom: 16px;
435
- right: 16px;
410
+ bottom: 20px;
411
+ right: 20px;
436
412
  background: white;
437
- border: 1px solid #cbd5e1;
438
413
  border-radius: 8px;
439
- box-shadow: 0 2px 10px rgba(0,0,0,0.15);
440
- padding: 8px 16px;
414
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
415
+ padding: 8px 12px;
441
416
  font-family: sans-serif;
442
417
  font-size: 14px;
443
418
  display: flex;
444
- align-items: center;
445
- gap: 8px;
419
+ align-items: start;
420
+ gap: 24px;
446
421
  z-index: 10000;
447
- width: 168px;
422
+ cursor: pointer;
448
423
  `;
449
424
  timer.innerHTML = `
450
- <div id="sf-stop-icon" style="width: 24px; height: 24px; background: #fc5555; border-radius: 6px; border: 1px solid #991b1b; display: flex; align-items: center; justify-content: center; cursor: pointer;">
451
- <div style="width: 8px; height: 8px; background: white; border-radius: 2px;"></div>
425
+ <div style="display:flex; align-items:center; gap:8px;">
426
+ <div style="width:32px; height:32px; background:#FC5555; border-radius:6px; border:1px solid #991b1b; display:flex; align-items:center; justify-content:center;">
427
+ <div style="width:10px; height:10px; background:white; border-radius:2px;"></div>
428
+ </div>
429
+ <div style="display:flex; flex-direction:column; font-size:14px; line-height:20px;">
430
+ <span id="sf-recording-timer" style="font-weight:600; color: #171717;">00:00</span>
431
+ <span style="font-size:12px; line-height:12px; color: #71717A;">Max time 15 mins</span>
432
+ </div>
433
+ </div>
434
+ <div style="display:flex; align-items:center; gap:4px; margin-left:auto; font-size:14px; color:#0F172A;">
435
+ <span style="color: #71717A;">stop</span>
436
+ <span style="display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 14px; line-height:18px;">
437
+ <span style="background: #F1F5F9; border-radius: 4px; border:1px solid #cbd5e1; padding: 0 6px; font-weight: 500;">cmd</span>
438
+ +
439
+ <span style="background: #F1F5F9; border-radius: 4px; border:1px solid #cbd5e1; padding: 0 6px; font-weight: 500;">esc</span>
440
+ </span>
452
441
  </div>
453
- <span id="sf-recording-timer">00:00</span>
454
- <div style="font-size:10px; color:#94a3b8;">[cmd] + [esc]</div>
455
442
  `;
443
+ timer.addEventListener("click", () => stopRecording());
456
444
  document.body.appendChild(timer);
457
445
  const timerEl = timer.querySelector("#sf-recording-timer");
458
446
  if (!timerEl)
@@ -467,8 +455,6 @@ function showFloatingTimer() {
467
455
  .padStart(2, "0");
468
456
  timerEl.textContent = `${mins}:${secs}`;
469
457
  }, 1000);
470
- const stopBtn = timer.querySelector("#sf-stop-icon");
471
- stopBtn?.addEventListener("click", () => stopRecording());
472
458
  }
473
459
  function stopRecording() {
474
460
  recordingEndTime = Date.now();
@@ -480,6 +466,23 @@ function stopRecording() {
480
466
  }
481
467
  function reopenModalAfterStop() {
482
468
  injectModalHTML("startnow");
469
+ const recordBtn = document.getElementById("sf-start-recording-btn");
470
+ if (recordBtn) {
471
+ const span = recordBtn.querySelector("span");
472
+ if (span)
473
+ span.textContent = "Re-record";
474
+ }
475
+ const timerLabel = document.getElementById("sf-recording-timer-label");
476
+ const timerDisplay = document.getElementById("sf-recording-timer-display");
477
+ if (timerLabel && timerDisplay && recordingStartTime && recordingEndTime) {
478
+ const durationSec = Math.floor((recordingEndTime - recordingStartTime) / 1000);
479
+ const mins = Math.floor(durationSec / 60)
480
+ .toString()
481
+ .padStart(2, "0");
482
+ const secs = (durationSec % 60).toString().padStart(2, "0");
483
+ timerDisplay.textContent = `${mins}:${secs}`;
484
+ timerLabel.style.display = "block";
485
+ }
483
486
  const descBox = document.getElementById("sf-issue-description");
484
487
  if (descBox)
485
488
  descBox.value = currentState.description;
@@ -536,7 +539,7 @@ function showTriageStatusModal(isLoading, triageId) {
536
539
  : "";
537
540
  const spinner = isLoading
538
541
  ? `<div style="display:flex; justify-content:center; align-items:center; padding: 40px 0;">
539
- <div class="spinner" style="width:24px; height:24px; border:2px solid #295dbf; border-top:2px solid white; border-radius:50%; animation:spin 1s linear infinite;"></div>
542
+ <div style="width:24px; height:24px; border:2px solid #295dbf; border-top:2px solid white; border-radius:50%; animation:spin 1s linear infinite;"></div>
540
543
  </div>`
541
544
  : "";
542
545
  const copiedStatus = !isLoading