@lokutor/sdk 1.2.1 → 1.2.2

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/index.d.mts CHANGED
@@ -827,7 +827,6 @@ declare class ConversationalPanel {
827
827
  private startBtn;
828
828
  private errorEl;
829
829
  private errorText;
830
- private timerEl;
831
830
  private canvas;
832
831
  private muteBtn;
833
832
  private muteSvg;
@@ -863,7 +862,6 @@ declare class ConversationalPanel {
863
862
  showError(text: string): void;
864
863
  /** Destroy the component, removing all DOM and stopping any active session */
865
864
  destroy(): void;
866
- private fmt;
867
865
  private startTimer;
868
866
  private toggleMute;
869
867
  private playConnectingSound;
package/dist/index.d.ts CHANGED
@@ -827,7 +827,6 @@ declare class ConversationalPanel {
827
827
  private startBtn;
828
828
  private errorEl;
829
829
  private errorText;
830
- private timerEl;
831
830
  private canvas;
832
831
  private muteBtn;
833
832
  private muteSvg;
@@ -863,7 +862,6 @@ declare class ConversationalPanel {
863
862
  showError(text: string): void;
864
863
  /** Destroy the component, removing all DOM and stopping any active session */
865
864
  destroy(): void;
866
- private fmt;
867
865
  private startTimer;
868
866
  private toggleMute;
869
867
  private playConnectingSound;
package/dist/index.js CHANGED
@@ -1765,12 +1765,6 @@ var PANEL_CSS = (
1765
1765
  gap: 1rem;
1766
1766
  letter-spacing: -0.02em;
1767
1767
  }
1768
- .cv-title .cv-timer {
1769
- font-variant-numeric: tabular-nums;
1770
- color: var(--cv-accent);
1771
- font-weight: 400;
1772
- opacity: 0.8;
1773
- }
1774
1768
  .cv-visualizer-wrap {
1775
1769
  position: absolute;
1776
1770
  top: 50%;
@@ -1919,7 +1913,6 @@ var PANEL_CSS = (
1919
1913
  .cv-visualizer-wrap { width: 60px; height: 60px; }
1920
1914
  .cv-header { padding-top: 0.75rem; }
1921
1915
  .cv-title { font-size: 0.7rem; gap: 0.3rem; }
1922
- .cv-title .cv-timer { display: none; }
1923
1916
  .cv-controls { gap: 0.6rem; padding-bottom: 0.6rem; }
1924
1917
  .cv-pill { padding: 0.15rem 0.4rem; gap: 0.3rem; }
1925
1918
  .cv-btn { padding: 0.2rem 0.4rem; font-size: 0.6rem; gap: 0; }
@@ -1937,7 +1930,6 @@ var PANEL_CSS = (
1937
1930
  .cv-visualizer-wrap { width: clamp(80px, 35cqw, 140px); height: clamp(80px, 35cqw, 140px); }
1938
1931
  .cv-header { padding-top: 0.8rem; }
1939
1932
  .cv-title { font-size: clamp(0.7rem, 2.5cqw, 1rem); gap: 0.4rem; }
1940
- .cv-title .cv-timer { font-size: 0.65em; }
1941
1933
  .cv-controls { gap: clamp(0.6rem, 1.5cqw, 1rem); padding-bottom: clamp(0.6rem, 1.5cqw, 1rem); }
1942
1934
  .cv-pill { padding: clamp(0.15rem, 0.5cqw, 0.25rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: 0.65rem; gap: 0.3rem; }
1943
1935
  .cv-btn { padding: clamp(0.2rem, 0.5cqw, 0.3rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: clamp(0.6rem, 1.2cqw, 0.7rem); }
@@ -1955,7 +1947,6 @@ var PANEL_CSS = (
1955
1947
  .cv-visualizer-wrap { width: clamp(100px, 40cqw, 200px); height: clamp(100px, 40cqw, 200px); }
1956
1948
  .cv-header { padding-top: clamp(1rem, 1.5cqw, 1.5rem); }
1957
1949
  .cv-title { font-size: clamp(0.9rem, 3cqw, 1.3rem); gap: clamp(0.4rem, 1cqw, 0.75rem); }
1958
- .cv-title .cv-timer { font-size: 0.85em; }
1959
1950
  .cv-controls { gap: clamp(0.8rem, 1.5cqw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5cqw, 1.2rem); }
1960
1951
  .cv-pill { padding: clamp(0.2rem, 0.75cqw, 0.3rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: 0.7rem; }
1961
1952
  .cv-btn { padding: clamp(0.25rem, 0.75cqw, 0.35rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: clamp(0.65rem, 1.2cqw, 0.8rem); }
@@ -1971,7 +1962,6 @@ var PANEL_CSS = (
1971
1962
  .cv-visualizer-wrap { width: clamp(140px, 45cqw, 300px); height: clamp(140px, 45cqw, 300px); }
1972
1963
  .cv-header { padding-top: clamp(1.5rem, 2cqw, 2rem); }
1973
1964
  .cv-title { font-size: clamp(1rem, 3cqw, 1.8rem); gap: clamp(0.75rem, 1.5cqw, 1.2rem); }
1974
- .cv-title .cv-timer { font-size: 0.9em; }
1975
1965
  .cv-controls { gap: clamp(1.2rem, 2cqw, 1.8rem); padding-bottom: clamp(1.2rem, 2cqw, 1.8rem); }
1976
1966
  .cv-pill { padding: clamp(0.3rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: 0.85rem; gap: 0.75rem; }
1977
1967
  .cv-btn { padding: clamp(0.35rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: clamp(0.75rem, 1.2cqw, 0.9rem); }
@@ -2008,7 +1998,6 @@ var ConversationalPanel = class {
2008
1998
  startBtn;
2009
1999
  errorEl;
2010
2000
  errorText;
2011
- timerEl;
2012
2001
  canvas;
2013
2002
  muteBtn;
2014
2003
  muteSvg;
@@ -2060,7 +2049,7 @@ var ConversationalPanel = class {
2060
2049
  </div>
2061
2050
  </div>
2062
2051
  <div class="cv-header">
2063
- <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")} <span class="cv-timer">00:00</span></h2>
2052
+ <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")}</h2>
2064
2053
  </div>
2065
2054
  <div class="cv-visualizer-wrap">
2066
2055
  <canvas class="cv-canvas"></canvas>
@@ -2091,7 +2080,6 @@ var ConversationalPanel = class {
2091
2080
  this.startBtn = this.el.querySelector(".cv-curtain-btn");
2092
2081
  this.errorEl = this.el.querySelector(".cv-error");
2093
2082
  this.errorText = this.el.querySelector(".cv-error-text");
2094
- this.timerEl = this.el.querySelector(".cv-timer");
2095
2083
  this.canvas = this.el.querySelector(".cv-canvas");
2096
2084
  this.muteBtn = this.el.querySelector(".cv-btn--mute");
2097
2085
  this.muteSvg = this.muteBtn.querySelector("svg");
@@ -2269,7 +2257,7 @@ var ConversationalPanel = class {
2269
2257
  this.cfg.title = title;
2270
2258
  this.curtainTitle.textContent = title;
2271
2259
  const h2 = this.el.querySelector(".cv-title");
2272
- if (h2) h2.innerHTML = `${this.esc(title)} <span class="cv-timer">${this.timerEl?.textContent || "00:00"}</span>`;
2260
+ if (h2) h2.textContent = title;
2273
2261
  }
2274
2262
  /** Select a voice from the picker */
2275
2263
  /** Update description text */
@@ -2299,21 +2287,13 @@ var ConversationalPanel = class {
2299
2287
  this.el.remove();
2300
2288
  }
2301
2289
  // ─── internal helpers ────────────────────────────────────
2302
- fmt(s) {
2303
- const m = Math.floor(s / 60).toString().padStart(2, "0");
2304
- const sec = (s % 60).toString().padStart(2, "0");
2305
- return `${m}:${sec}`;
2306
- }
2307
2290
  startTimer() {
2308
2291
  let elapsed = 0;
2309
2292
  const maxDur = this.cfg.maxDuration || 300;
2310
2293
  const silentMax = this.cfg.silenceTimeout || 60;
2311
2294
  this._lastSpeechTime = Date.now();
2312
- this.timerEl.textContent = this.fmt(maxDur);
2313
2295
  this.timerTicker = window.setInterval(() => {
2314
2296
  elapsed++;
2315
- const remaining = Math.max(0, maxDur - elapsed);
2316
- this.timerEl.textContent = this.fmt(remaining);
2317
2297
  if (elapsed >= maxDur) {
2318
2298
  this._locked = true;
2319
2299
  this.stop();
package/dist/index.mjs CHANGED
@@ -1714,12 +1714,6 @@ var PANEL_CSS = (
1714
1714
  gap: 1rem;
1715
1715
  letter-spacing: -0.02em;
1716
1716
  }
1717
- .cv-title .cv-timer {
1718
- font-variant-numeric: tabular-nums;
1719
- color: var(--cv-accent);
1720
- font-weight: 400;
1721
- opacity: 0.8;
1722
- }
1723
1717
  .cv-visualizer-wrap {
1724
1718
  position: absolute;
1725
1719
  top: 50%;
@@ -1868,7 +1862,6 @@ var PANEL_CSS = (
1868
1862
  .cv-visualizer-wrap { width: 60px; height: 60px; }
1869
1863
  .cv-header { padding-top: 0.75rem; }
1870
1864
  .cv-title { font-size: 0.7rem; gap: 0.3rem; }
1871
- .cv-title .cv-timer { display: none; }
1872
1865
  .cv-controls { gap: 0.6rem; padding-bottom: 0.6rem; }
1873
1866
  .cv-pill { padding: 0.15rem 0.4rem; gap: 0.3rem; }
1874
1867
  .cv-btn { padding: 0.2rem 0.4rem; font-size: 0.6rem; gap: 0; }
@@ -1886,7 +1879,6 @@ var PANEL_CSS = (
1886
1879
  .cv-visualizer-wrap { width: clamp(80px, 35cqw, 140px); height: clamp(80px, 35cqw, 140px); }
1887
1880
  .cv-header { padding-top: 0.8rem; }
1888
1881
  .cv-title { font-size: clamp(0.7rem, 2.5cqw, 1rem); gap: 0.4rem; }
1889
- .cv-title .cv-timer { font-size: 0.65em; }
1890
1882
  .cv-controls { gap: clamp(0.6rem, 1.5cqw, 1rem); padding-bottom: clamp(0.6rem, 1.5cqw, 1rem); }
1891
1883
  .cv-pill { padding: clamp(0.15rem, 0.5cqw, 0.25rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: 0.65rem; gap: 0.3rem; }
1892
1884
  .cv-btn { padding: clamp(0.2rem, 0.5cqw, 0.3rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: clamp(0.6rem, 1.2cqw, 0.7rem); }
@@ -1904,7 +1896,6 @@ var PANEL_CSS = (
1904
1896
  .cv-visualizer-wrap { width: clamp(100px, 40cqw, 200px); height: clamp(100px, 40cqw, 200px); }
1905
1897
  .cv-header { padding-top: clamp(1rem, 1.5cqw, 1.5rem); }
1906
1898
  .cv-title { font-size: clamp(0.9rem, 3cqw, 1.3rem); gap: clamp(0.4rem, 1cqw, 0.75rem); }
1907
- .cv-title .cv-timer { font-size: 0.85em; }
1908
1899
  .cv-controls { gap: clamp(0.8rem, 1.5cqw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5cqw, 1.2rem); }
1909
1900
  .cv-pill { padding: clamp(0.2rem, 0.75cqw, 0.3rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: 0.7rem; }
1910
1901
  .cv-btn { padding: clamp(0.25rem, 0.75cqw, 0.35rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: clamp(0.65rem, 1.2cqw, 0.8rem); }
@@ -1920,7 +1911,6 @@ var PANEL_CSS = (
1920
1911
  .cv-visualizer-wrap { width: clamp(140px, 45cqw, 300px); height: clamp(140px, 45cqw, 300px); }
1921
1912
  .cv-header { padding-top: clamp(1.5rem, 2cqw, 2rem); }
1922
1913
  .cv-title { font-size: clamp(1rem, 3cqw, 1.8rem); gap: clamp(0.75rem, 1.5cqw, 1.2rem); }
1923
- .cv-title .cv-timer { font-size: 0.9em; }
1924
1914
  .cv-controls { gap: clamp(1.2rem, 2cqw, 1.8rem); padding-bottom: clamp(1.2rem, 2cqw, 1.8rem); }
1925
1915
  .cv-pill { padding: clamp(0.3rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: 0.85rem; gap: 0.75rem; }
1926
1916
  .cv-btn { padding: clamp(0.35rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: clamp(0.75rem, 1.2cqw, 0.9rem); }
@@ -1957,7 +1947,6 @@ var ConversationalPanel = class {
1957
1947
  startBtn;
1958
1948
  errorEl;
1959
1949
  errorText;
1960
- timerEl;
1961
1950
  canvas;
1962
1951
  muteBtn;
1963
1952
  muteSvg;
@@ -2009,7 +1998,7 @@ var ConversationalPanel = class {
2009
1998
  </div>
2010
1999
  </div>
2011
2000
  <div class="cv-header">
2012
- <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")} <span class="cv-timer">00:00</span></h2>
2001
+ <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")}</h2>
2013
2002
  </div>
2014
2003
  <div class="cv-visualizer-wrap">
2015
2004
  <canvas class="cv-canvas"></canvas>
@@ -2040,7 +2029,6 @@ var ConversationalPanel = class {
2040
2029
  this.startBtn = this.el.querySelector(".cv-curtain-btn");
2041
2030
  this.errorEl = this.el.querySelector(".cv-error");
2042
2031
  this.errorText = this.el.querySelector(".cv-error-text");
2043
- this.timerEl = this.el.querySelector(".cv-timer");
2044
2032
  this.canvas = this.el.querySelector(".cv-canvas");
2045
2033
  this.muteBtn = this.el.querySelector(".cv-btn--mute");
2046
2034
  this.muteSvg = this.muteBtn.querySelector("svg");
@@ -2218,7 +2206,7 @@ var ConversationalPanel = class {
2218
2206
  this.cfg.title = title;
2219
2207
  this.curtainTitle.textContent = title;
2220
2208
  const h2 = this.el.querySelector(".cv-title");
2221
- if (h2) h2.innerHTML = `${this.esc(title)} <span class="cv-timer">${this.timerEl?.textContent || "00:00"}</span>`;
2209
+ if (h2) h2.textContent = title;
2222
2210
  }
2223
2211
  /** Select a voice from the picker */
2224
2212
  /** Update description text */
@@ -2248,21 +2236,13 @@ var ConversationalPanel = class {
2248
2236
  this.el.remove();
2249
2237
  }
2250
2238
  // ─── internal helpers ────────────────────────────────────
2251
- fmt(s) {
2252
- const m = Math.floor(s / 60).toString().padStart(2, "0");
2253
- const sec = (s % 60).toString().padStart(2, "0");
2254
- return `${m}:${sec}`;
2255
- }
2256
2239
  startTimer() {
2257
2240
  let elapsed = 0;
2258
2241
  const maxDur = this.cfg.maxDuration || 300;
2259
2242
  const silentMax = this.cfg.silenceTimeout || 60;
2260
2243
  this._lastSpeechTime = Date.now();
2261
- this.timerEl.textContent = this.fmt(maxDur);
2262
2244
  this.timerTicker = window.setInterval(() => {
2263
2245
  elapsed++;
2264
- const remaining = Math.max(0, maxDur - elapsed);
2265
- this.timerEl.textContent = this.fmt(remaining);
2266
2246
  if (elapsed >= maxDur) {
2267
2247
  this._locked = true;
2268
2248
  this.stop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokutor/sdk",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "JavaScript/TypeScript SDK for Lokutor Real-time Voice AI",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -107,12 +107,6 @@ const PANEL_CSS = /*css*/ `
107
107
  gap: 1rem;
108
108
  letter-spacing: -0.02em;
109
109
  }
110
- .cv-title .cv-timer {
111
- font-variant-numeric: tabular-nums;
112
- color: var(--cv-accent);
113
- font-weight: 400;
114
- opacity: 0.8;
115
- }
116
110
  .cv-visualizer-wrap {
117
111
  position: absolute;
118
112
  top: 50%;
@@ -261,7 +255,6 @@ const PANEL_CSS = /*css*/ `
261
255
  .cv-visualizer-wrap { width: 60px; height: 60px; }
262
256
  .cv-header { padding-top: 0.75rem; }
263
257
  .cv-title { font-size: 0.7rem; gap: 0.3rem; }
264
- .cv-title .cv-timer { display: none; }
265
258
  .cv-controls { gap: 0.6rem; padding-bottom: 0.6rem; }
266
259
  .cv-pill { padding: 0.15rem 0.4rem; gap: 0.3rem; }
267
260
  .cv-btn { padding: 0.2rem 0.4rem; font-size: 0.6rem; gap: 0; }
@@ -279,7 +272,6 @@ const PANEL_CSS = /*css*/ `
279
272
  .cv-visualizer-wrap { width: clamp(80px, 35cqw, 140px); height: clamp(80px, 35cqw, 140px); }
280
273
  .cv-header { padding-top: 0.8rem; }
281
274
  .cv-title { font-size: clamp(0.7rem, 2.5cqw, 1rem); gap: 0.4rem; }
282
- .cv-title .cv-timer { font-size: 0.65em; }
283
275
  .cv-controls { gap: clamp(0.6rem, 1.5cqw, 1rem); padding-bottom: clamp(0.6rem, 1.5cqw, 1rem); }
284
276
  .cv-pill { padding: clamp(0.15rem, 0.5cqw, 0.25rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: 0.65rem; gap: 0.3rem; }
285
277
  .cv-btn { padding: clamp(0.2rem, 0.5cqw, 0.3rem) clamp(0.35rem, 1cqw, 0.6rem); font-size: clamp(0.6rem, 1.2cqw, 0.7rem); }
@@ -297,7 +289,6 @@ const PANEL_CSS = /*css*/ `
297
289
  .cv-visualizer-wrap { width: clamp(100px, 40cqw, 200px); height: clamp(100px, 40cqw, 200px); }
298
290
  .cv-header { padding-top: clamp(1rem, 1.5cqw, 1.5rem); }
299
291
  .cv-title { font-size: clamp(0.9rem, 3cqw, 1.3rem); gap: clamp(0.4rem, 1cqw, 0.75rem); }
300
- .cv-title .cv-timer { font-size: 0.85em; }
301
292
  .cv-controls { gap: clamp(0.8rem, 1.5cqw, 1.2rem); padding-bottom: clamp(0.8rem, 1.5cqw, 1.2rem); }
302
293
  .cv-pill { padding: clamp(0.2rem, 0.75cqw, 0.3rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: 0.7rem; }
303
294
  .cv-btn { padding: clamp(0.25rem, 0.75cqw, 0.35rem) clamp(0.5rem, 1.2cqw, 0.75rem); font-size: clamp(0.65rem, 1.2cqw, 0.8rem); }
@@ -313,7 +304,6 @@ const PANEL_CSS = /*css*/ `
313
304
  .cv-visualizer-wrap { width: clamp(140px, 45cqw, 300px); height: clamp(140px, 45cqw, 300px); }
314
305
  .cv-header { padding-top: clamp(1.5rem, 2cqw, 2rem); }
315
306
  .cv-title { font-size: clamp(1rem, 3cqw, 1.8rem); gap: clamp(0.75rem, 1.5cqw, 1.2rem); }
316
- .cv-title .cv-timer { font-size: 0.9em; }
317
307
  .cv-controls { gap: clamp(1.2rem, 2cqw, 1.8rem); padding-bottom: clamp(1.2rem, 2cqw, 1.8rem); }
318
308
  .cv-pill { padding: clamp(0.3rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: 0.85rem; gap: 0.75rem; }
319
309
  .cv-btn { padding: clamp(0.35rem, 0.8cqw, 0.4rem) clamp(0.75rem, 1.2cqw, 1rem); font-size: clamp(0.75rem, 1.2cqw, 0.9rem); }
@@ -387,7 +377,6 @@ export class ConversationalPanel {
387
377
  private startBtn!: HTMLButtonElement;
388
378
  private errorEl!: HTMLElement;
389
379
  private errorText!: HTMLElement;
390
- private timerEl!: HTMLElement;
391
380
  private canvas!: HTMLCanvasElement;
392
381
  private muteBtn!: HTMLElement;
393
382
  private muteSvg!: SVGElement;
@@ -444,7 +433,7 @@ export class ConversationalPanel {
444
433
  </div>
445
434
  </div>
446
435
  <div class="cv-header">
447
- <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")} <span class="cv-timer">00:00</span></h2>
436
+ <h2 class="cv-title">${this.esc(this.cfg.title || "Voice Chat")}</h2>
448
437
  </div>
449
438
  <div class="cv-visualizer-wrap">
450
439
  <canvas class="cv-canvas"></canvas>
@@ -478,7 +467,6 @@ export class ConversationalPanel {
478
467
  this.startBtn = this.el.querySelector('.cv-curtain-btn')!;
479
468
  this.errorEl = this.el.querySelector('.cv-error')!;
480
469
  this.errorText = this.el.querySelector('.cv-error-text')!;
481
- this.timerEl = this.el.querySelector('.cv-timer')!;
482
470
  this.canvas = this.el.querySelector('.cv-canvas')!;
483
471
  this.muteBtn = this.el.querySelector('.cv-btn--mute')!;
484
472
  this.muteSvg = this.muteBtn.querySelector('svg')!;
@@ -673,7 +661,7 @@ export class ConversationalPanel {
673
661
  this.cfg.title = title;
674
662
  this.curtainTitle.textContent = title;
675
663
  const h2 = this.el.querySelector('.cv-title');
676
- if (h2) h2.innerHTML = `${this.esc(title)} <span class="cv-timer">${this.timerEl?.textContent || '00:00'}</span>`;
664
+ if (h2) h2.textContent = title;
677
665
  }
678
666
 
679
667
  /** Select a voice from the picker */
@@ -710,22 +698,15 @@ export class ConversationalPanel {
710
698
 
711
699
  // ─── internal helpers ────────────────────────────────────
712
700
 
713
- private fmt(s: number): string {
714
- const m = Math.floor(s / 60).toString().padStart(2, '0');
715
- const sec = (s % 60).toString().padStart(2, '0');
716
- return `${m}:${sec}`;
717
- }
718
-
719
701
  private startTimer() {
702
+ // No visible countdown — this interval only enforces maxDuration and
703
+ // the silence timeout, both real safety/cost-control cutoffs, not UI.
720
704
  let elapsed = 0;
721
705
  const maxDur = this.cfg.maxDuration || 300;
722
706
  const silentMax = this.cfg.silenceTimeout || 60;
723
707
  this._lastSpeechTime = Date.now();
724
- this.timerEl.textContent = this.fmt(maxDur);
725
708
  this.timerTicker = window.setInterval(() => {
726
709
  elapsed++;
727
- const remaining = Math.max(0, maxDur - elapsed);
728
- this.timerEl.textContent = this.fmt(remaining);
729
710
 
730
711
  if (elapsed >= maxDur) {
731
712
  this._locked = true;