@sailfish-ai/recorder 1.7.17 → 1.7.19

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>
@@ -229,6 +234,7 @@ function injectModalHTML(initialMode = "lookback") {
229
234
  bindListeners();
230
235
  }
231
236
  function setActiveTab(mode) {
237
+ currentState.mode = mode;
232
238
  const tabLookback = modalEl?.querySelector("#sf-tab-lookback");
233
239
  const tabStartnow = modalEl?.querySelector("#sf-tab-startnow");
234
240
  if (mode === "lookback") {
@@ -245,121 +251,96 @@ function setActiveTab(mode) {
245
251
  }
246
252
  }
247
253
  function updateModeSpecificUI(mode) {
248
- const infoText = document.getElementById("sf-issue-mode-info");
249
- const infoMessage = infoText?.querySelector("div");
254
+ const infoMessage = document.querySelector("#sf-issue-mode-info div");
250
255
  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)
256
+ const recordContainer = document.getElementById("sf-record-button-container");
257
+ const recordLabel = document.getElementById("sf-recording-timer-label");
258
+ const recordTime = document.getElementById("sf-recording-timer-display");
259
+ const footer = document.getElementById("sf-modal-footer");
260
+ const lookbackContainer = document.getElementById("sf-lookback-container");
261
+ if (!infoMessage ||
262
+ !submitBtn ||
263
+ !recordContainer ||
264
+ !recordLabel ||
265
+ !recordTime ||
266
+ !footer ||
267
+ !lookbackContainer)
255
268
  return;
256
269
  if (mode === "startnow") {
257
- if (startRecording)
258
- startRecording.style.display = "block";
259
- if (timestampContainer)
260
- timestampContainer.style.display = "none";
270
+ // Show record button, hide lookback
271
+ recordContainer.style.display = "block";
272
+ lookbackContainer.style.display = "none";
273
+ footer.style.justifyContent = "space-between";
261
274
  infoMessage.textContent = "I want to reproduce the issue right now.";
262
- // Disable submit if recording not done yet
263
275
  const canSubmit = recordingStartTime !== null && recordingEndTime !== null;
264
276
  submitBtn.disabled = !canSubmit;
265
277
  submitBtn.style.opacity = canSubmit ? "1" : "0.4";
266
278
  submitBtn.style.cursor = canSubmit ? "pointer" : "not-allowed";
267
- // Show current timer value if available
268
- if (recordingStartTime && recordingEndTime && timerText) {
279
+ if (recordingStartTime && recordingEndTime) {
269
280
  const duration = Math.floor((recordingEndTime - recordingStartTime) / 1000);
270
281
  const mins = String(Math.floor(duration / 60)).padStart(2, "0");
271
282
  const secs = String(duration % 60).padStart(2, "0");
272
- timerText.textContent = `${mins}:${secs}`;
273
- timerText.style.color = "#171717";
283
+ recordLabel.style.display = "block";
284
+ recordTime.textContent = `${mins}:${secs}`;
274
285
  }
275
- else if (timerText) {
276
- timerText.textContent = "00:00";
277
- timerText.style.color = "#d4d4d8";
286
+ else {
287
+ recordLabel.style.display = "none";
278
288
  }
279
289
  }
280
- else if (mode === "lookback") {
281
- if (startRecording)
282
- startRecording.style.display = "none";
283
- if (timestampContainer)
284
- timestampContainer.style.display = "block";
290
+ else {
291
+ // Show lookback, hide recording
292
+ recordContainer.style.display = "none";
293
+ recordLabel.style.display = "none";
294
+ lookbackContainer.style.display = "block";
295
+ footer.style.justifyContent = "flex-end";
285
296
  infoMessage.textContent =
286
297
  "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";
298
+ submitBtn.disabled = false;
299
+ submitBtn.style.opacity = "1";
300
+ submitBtn.style.cursor = "pointer";
296
301
  }
297
302
  }
298
303
  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");
304
+ const tabButtons = modalEl?.querySelectorAll(".sf-issue-tab");
305
+ const recordBtn = document.getElementById("sf-start-recording-btn");
302
306
  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) => {
307
+ const submitBtn = document.getElementById("sf-issue-submit-btn");
308
+ const minutesSelect = document.getElementById("sf-lookback-minutes");
309
+ // Tab switch
310
+ tabButtons?.forEach((tabBtn) => {
354
311
  tabBtn.addEventListener("click", (e) => {
355
312
  const mode = e.currentTarget.dataset
356
313
  .mode;
357
- currentState.mode = mode;
358
314
  setActiveTab(mode);
359
- // Optionally toggle relevant UI
360
315
  updateModeSpecificUI(mode);
361
316
  });
362
317
  });
318
+ // Close modal
319
+ if (closeBtn) {
320
+ closeBtn.onclick = closeModal;
321
+ }
322
+ // Time select change
323
+ if (minutesSelect) {
324
+ minutesSelect.addEventListener("change", () => {
325
+ if (currentState.mode === "lookback") {
326
+ // Always valid now, so no need to validate input
327
+ submitBtn.disabled = false;
328
+ submitBtn.style.opacity = "1";
329
+ submitBtn.style.cursor = "pointer";
330
+ }
331
+ });
332
+ }
333
+ // Start recording
334
+ if (recordBtn) {
335
+ recordBtn.onclick = () => {
336
+ const descInput = document.getElementById("sf-issue-description");
337
+ if (descInput) {
338
+ currentState.description = descInput.value;
339
+ }
340
+ startCountdownThenRecord();
341
+ };
342
+ }
343
+ // Submit
363
344
  modalEl?.addEventListener("click", (e) => {
364
345
  const target = e.target;
365
346
  if (target.closest("#sf-issue-submit-btn")) {
@@ -374,12 +355,8 @@ function bindListeners() {
374
355
  endTimestamp = recordingEndTime ?? Date.now();
375
356
  }
376
357
  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;
358
+ const minutes = Number(minutesSelect?.value || "2");
359
+ const delta = minutes * 60 * 1000;
383
360
  endTimestamp = Date.now();
384
361
  startTimestamp = endTimestamp - delta;
385
362
  }
@@ -431,28 +408,40 @@ function showFloatingTimer() {
431
408
  timer.id = "sf-recording-indicator";
432
409
  timer.style.cssText = `
433
410
  position: fixed;
434
- bottom: 16px;
435
- right: 16px;
411
+ bottom: 20px;
412
+ right: 20px;
436
413
  background: white;
437
- border: 1px solid #cbd5e1;
438
414
  border-radius: 8px;
439
- box-shadow: 0 2px 10px rgba(0,0,0,0.15);
440
- padding: 8px 16px;
415
+ box-shadow: 0 4px 20px rgba(0,0,0,0.15);
416
+ padding: 8px 12px;
441
417
  font-family: sans-serif;
442
418
  font-size: 14px;
443
419
  display: flex;
444
- align-items: center;
445
- gap: 8px;
420
+ align-items: start;
421
+ gap: 24px;
446
422
  z-index: 10000;
447
- width: 168px;
423
+ cursor: pointer;
448
424
  `;
449
425
  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>
426
+ <div style="display:flex; align-items:center; gap:8px;">
427
+ <div style="width:32px; height:32px; background:#FC5555; border-radius:6px; border:1px solid #991b1b; display:flex; align-items:center; justify-content:center;">
428
+ <div style="width:10px; height:10px; background:white; border-radius:2px;"></div>
429
+ </div>
430
+ <div style="display:flex; flex-direction:column; font-size:14px; line-height:20px;">
431
+ <span id="sf-recording-timer" style="font-weight:600; color: #171717;">00:00</span>
432
+ <span style="font-size:12px; line-height:12px; color: #71717A;">Max time 15 mins</span>
433
+ </div>
434
+ </div>
435
+ <div style="display:flex; align-items:center; gap:4px; margin-left:auto; font-size:14px; color:#0F172A;">
436
+ <span style="color: #71717A;">stop</span>
437
+ <span style="display: inline-flex; align-items: center; gap: 4px; color: #94A3B8; font-size: 14px; line-height:18px;">
438
+ <span style="background: #F1F5F9; border-radius: 4px; border:1px solid #cbd5e1; padding: 0 6px; font-weight: 500;">cmd</span>
439
+ +
440
+ <span style="background: #F1F5F9; border-radius: 4px; border:1px solid #cbd5e1; padding: 0 6px; font-weight: 500;">esc</span>
441
+ </span>
452
442
  </div>
453
- <span id="sf-recording-timer">00:00</span>
454
- <div style="font-size:10px; color:#94a3b8;">[cmd] + [esc]</div>
455
443
  `;
444
+ timer.addEventListener("click", () => stopRecording());
456
445
  document.body.appendChild(timer);
457
446
  const timerEl = timer.querySelector("#sf-recording-timer");
458
447
  if (!timerEl)
@@ -467,8 +456,6 @@ function showFloatingTimer() {
467
456
  .padStart(2, "0");
468
457
  timerEl.textContent = `${mins}:${secs}`;
469
458
  }, 1000);
470
- const stopBtn = timer.querySelector("#sf-stop-icon");
471
- stopBtn?.addEventListener("click", () => stopRecording());
472
459
  }
473
460
  function stopRecording() {
474
461
  recordingEndTime = Date.now();
@@ -480,6 +467,23 @@ function stopRecording() {
480
467
  }
481
468
  function reopenModalAfterStop() {
482
469
  injectModalHTML("startnow");
470
+ const recordBtn = document.getElementById("sf-start-recording-btn");
471
+ if (recordBtn) {
472
+ const span = recordBtn.querySelector("span");
473
+ if (span)
474
+ span.textContent = "Re-record";
475
+ }
476
+ const timerLabel = document.getElementById("sf-recording-timer-label");
477
+ const timerDisplay = document.getElementById("sf-recording-timer-display");
478
+ if (timerLabel && timerDisplay && recordingStartTime && recordingEndTime) {
479
+ const durationSec = Math.floor((recordingEndTime - recordingStartTime) / 1000);
480
+ const mins = Math.floor(durationSec / 60)
481
+ .toString()
482
+ .padStart(2, "0");
483
+ const secs = (durationSec % 60).toString().padStart(2, "0");
484
+ timerDisplay.textContent = `${mins}:${secs}`;
485
+ timerLabel.style.display = "block";
486
+ }
483
487
  const descBox = document.getElementById("sf-issue-description");
484
488
  if (descBox)
485
489
  descBox.value = currentState.description;
@@ -536,7 +540,7 @@ function showTriageStatusModal(isLoading, triageId) {
536
540
  : "";
537
541
  const spinner = isLoading
538
542
  ? `<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>
543
+ <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
544
  </div>`
541
545
  : "";
542
546
  const copiedStatus = !isLoading