@jxgame2020/dsh-token-quota 0.1.41 → 0.1.43

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/lib/client.js CHANGED
@@ -132,7 +132,7 @@ window.__ModuleLoader__.load({
132
132
  d.transcribe = settings;
133
133
  },
134
134
  setUpgrade: (d, upgrade) => {
135
- const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.41" ? upgrade : null;
135
+ const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.43" ? upgrade : null;
136
136
  if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) d.upgradeDismissed = false;
137
137
  d.upgrade = normalized;
138
138
  },
@@ -262,142 +262,142 @@ window.__ModuleLoader__.load({
262
262
  document.head.appendChild(tag);
263
263
  }
264
264
  var TokenQuotaPanel_module_css_default = {
265
- "balanceToggleOpen": "NPLLIq_balanceToggleOpen",
266
- "settingsBtn": "NPLLIq_settingsBtn",
267
- "fillOver": "NPLLIq_fillOver",
268
- "checkErrorHint": "NPLLIq_checkErrorHint",
269
- "dialogBtn": "NPLLIq_dialogBtn",
270
- "logScroll": "NPLLIq_logScroll",
271
- "logPager": "NPLLIq_logPager",
272
- "logPagerControls": "NPLLIq_logPagerControls",
273
- "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
274
- "balanceWinHeader": "NPLLIq_balanceWinHeader",
275
- "balanceBarRefresh": "NPLLIq_balanceBarRefresh",
276
- "gearBtn": "NPLLIq_gearBtn",
277
- "logDayCol": "NPLLIq_logDayCol",
278
- "noteChip": "NPLLIq_noteChip",
279
- "dialogClose": "NPLLIq_dialogClose",
280
- "radioRow": "NPLLIq_radioRow",
281
- "noteMenuBackdrop": "NPLLIq_noteMenuBackdrop",
282
- "rowMeta": "NPLLIq_rowMeta",
283
- "balanceRowValue": "NPLLIq_balanceRowValue",
284
- "collapse": "NPLLIq_collapse",
285
- "tab": "NPLLIq_tab",
286
- "balanceRowRefresh": "NPLLIq_balanceRowRefresh",
287
- "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
288
- "tabIcon": "NPLLIq_tabIcon",
289
- "header": "NPLLIq_header",
290
- "balanceRefreshAll": "NPLLIq_balanceRefreshAll",
291
- "dialogOverlay": "NPLLIq_dialogOverlay",
292
- "noteWinBtn": "NPLLIq_noteWinBtn",
293
- "subtitle": "NPLLIq_subtitle",
294
- "rowHeader": "NPLLIq_rowHeader",
295
- "monitorName": "NPLLIq_monitorName",
296
- "logClearDate": "NPLLIq_logClearDate",
297
- "balanceStateError": "NPLLIq_balanceStateError",
265
+ "dialogActions": "NPLLIq_dialogActions",
266
+ "balanceValueOk": "NPLLIq_balanceValueOk",
267
+ "logTable": "NPLLIq_logTable",
268
+ "noteWinBar": "NPLLIq_noteWinBar",
298
269
  "row": "NPLLIq_row",
299
- "balanceStateHint": "NPLLIq_balanceStateHint",
300
- "balanceBarDim": "NPLLIq_balanceBarDim",
301
- "headerText": "NPLLIq_headerText",
270
+ "noteMenu": "NPLLIq_noteMenu",
271
+ "dialogTabActive": "NPLLIq_dialogTabActive",
272
+ "header": "NPLLIq_header",
273
+ "noteMenuBackdrop": "NPLLIq_noteMenuBackdrop",
274
+ "noteWin": "NPLLIq_noteWin",
275
+ "currentBadge": "NPLLIq_currentBadge",
276
+ "body": "NPLLIq_body",
277
+ "balanceWin": "NPLLIq_balanceWin",
278
+ "noteWinResize": "NPLLIq_noteWinResize",
302
279
  "settingsDialog": "NPLLIq_settingsDialog",
280
+ "settingsBtn": "NPLLIq_settingsBtn",
281
+ "logUsedCol": "NPLLIq_logUsedCol",
282
+ "balanceToggle": "NPLLIq_balanceToggle",
283
+ "balanceRow": "NPLLIq_balanceRow",
284
+ "balanceStateError": "NPLLIq_balanceStateError",
285
+ "fillIdle": "NPLLIq_fillIdle",
286
+ "balanceSortBtn": "NPLLIq_balanceSortBtn",
287
+ "monitorList": "NPLLIq_monitorList",
288
+ "dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
289
+ "noticeError": "NPLLIq_noticeError",
303
290
  "panelDimStrong": "NPLLIq_panelDimStrong",
304
- "balanceValueOk": "NPLLIq_balanceValueOk",
305
- "noteChipOpen": "NPLLIq_noteChipOpen",
306
- "upgradeBanner": "NPLLIq_upgradeBanner",
307
- "title": "NPLLIq_title",
308
291
  "dialogLabel": "NPLLIq_dialogLabel",
309
- "logPagerInfo": "NPLLIq_logPagerInfo",
310
- "balanceWinBody": "NPLLIq_balanceWinBody",
311
- "noteWinResize": "NPLLIq_noteWinResize",
312
- "noteNewPlus": "NPLLIq_noteNewPlus",
313
- "selectBtn": "NPLLIq_selectBtn",
314
- "upgradeCommands": "NPLLIq_upgradeCommands",
315
- "balanceBarError": "NPLLIq_balanceBarError",
316
- "upgradeClose": "NPLLIq_upgradeClose",
317
- "logToolbar": "NPLLIq_logToolbar",
318
- "balanceValueError": "NPLLIq_balanceValueError",
319
- "upgradeCommand": "NPLLIq_upgradeCommand",
320
- "input": "NPLLIq_input",
321
- "monitorList": "NPLLIq_monitorList",
322
- "dialogActions": "NPLLIq_dialogActions",
323
- "noteWinTitleText": "NPLLIq_noteWinTitleText",
324
- "rowDragging": "NPLLIq_rowDragging",
325
- "balanceRow": "NPLLIq_balanceRow",
292
+ "logDayCol": "NPLLIq_logDayCol",
326
293
  "noteWinCollapsed": "NPLLIq_noteWinCollapsed",
327
- "fillIdle": "NPLLIq_fillIdle",
328
- "balanceRowName": "NPLLIq_balanceRowName",
329
- "noteMenu": "NPLLIq_noteMenu",
330
- "fillWarn": "NPLLIq_fillWarn",
331
294
  "noteWinGripBar": "NPLLIq_noteWinGripBar",
332
- "rowDrag": "NPLLIq_rowDrag",
295
+ "controls": "NPLLIq_controls",
296
+ "noteWinText": "NPLLIq_noteWinText",
297
+ "dialogTab": "NPLLIq_dialogTab",
298
+ "logPagerInfo": "NPLLIq_logPagerInfo",
299
+ "logModelCol": "NPLLIq_logModelCol",
300
+ "balanceBarBadge": "NPLLIq_balanceBarBadge",
301
+ "rowMeta": "NPLLIq_rowMeta",
302
+ "rowHeader": "NPLLIq_rowHeader",
333
303
  "noteMenuItem": "NPLLIq_noteMenuItem",
334
- "rowName": "NPLLIq_rowName",
335
- "version": "NPLLIq_version",
336
304
  "dialogTabs": "NPLLIq_dialogTabs",
337
- "controls": "NPLLIq_controls",
338
- "balanceSortBtn": "NPLLIq_balanceSortBtn",
339
- "currentBadge": "NPLLIq_currentBadge",
305
+ "logClearDate": "NPLLIq_logClearDate",
306
+ "noteNew": "NPLLIq_noteNew",
307
+ "balanceLastLine": "NPLLIq_balanceLastLine",
308
+ "upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
309
+ "copyBtnDone": "NPLLIq_copyBtnDone",
310
+ "panelDim": "NPLLIq_panelDim",
311
+ "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
312
+ "tab": "NPLLIq_tab",
313
+ "logPagerBtn": "NPLLIq_logPagerBtn",
314
+ "balanceBarDim": "NPLLIq_balanceBarDim",
315
+ "resetSelect": "NPLLIq_resetSelect",
316
+ "dialogBody": "NPLLIq_dialogBody",
317
+ "headerActions": "NPLLIq_headerActions",
318
+ "balanceBarLow": "NPLLIq_balanceBarLow",
319
+ "balanceRowValue": "NPLLIq_balanceRowValue",
320
+ "dialogHint": "NPLLIq_dialogHint",
321
+ "tabIcon": "NPLLIq_tabIcon",
322
+ "selectBtn": "NPLLIq_selectBtn",
323
+ "switch": "NPLLIq_switch",
324
+ "panel": "NPLLIq_panel",
325
+ "fillOver": "NPLLIq_fillOver",
326
+ "subtitle": "NPLLIq_subtitle",
340
327
  "checkUpdatesRow": "NPLLIq_checkUpdatesRow",
328
+ "balanceValueLow": "NPLLIq_balanceValueLow",
329
+ "notice": "NPLLIq_notice",
330
+ "balanceBar": "NPLLIq_balanceBar",
341
331
  "dialogHeader": "NPLLIq_dialogHeader",
332
+ "balanceWinHeader": "NPLLIq_balanceWinHeader",
342
333
  "upgradeCommandRow": "NPLLIq_upgradeCommandRow",
334
+ "upgradeClose": "NPLLIq_upgradeClose",
335
+ "rowName": "NPLLIq_rowName",
336
+ "noteWinTitleText": "NPLLIq_noteWinTitleText",
337
+ "copyBtn": "NPLLIq_copyBtn",
338
+ "rowDragging": "NPLLIq_rowDragging",
339
+ "noteChipOpen": "NPLLIq_noteChipOpen",
340
+ "balanceWinTitle": "NPLLIq_balanceWinTitle",
341
+ "upgradeCommand": "NPLLIq_upgradeCommand",
342
+ "balanceStateHint": "NPLLIq_balanceStateHint",
343
343
  "upgradeHint": "NPLLIq_upgradeHint",
344
- "noteWin": "NPLLIq_noteWin",
345
- "dialogTab": "NPLLIq_dialogTab",
346
- "dialogTitle": "NPLLIq_dialogTitle",
347
- "noteWinBar": "NPLLIq_noteWinBar",
348
- "headerActions": "NPLLIq_headerActions",
349
- "versionNew": "NPLLIq_versionNew",
350
- "dialog": "NPLLIq_dialog",
351
- "dialogTabActive": "NPLLIq_dialogTabActive",
344
+ "checkErrorHint": "NPLLIq_checkErrorHint",
345
+ "logPageSizeSelect": "NPLLIq_logPageSizeSelect",
346
+ "balanceToggleOpen": "NPLLIq_balanceToggleOpen",
347
+ "fullNotice": "NPLLIq_fullNotice",
348
+ "noteChip": "NPLLIq_noteChip",
349
+ "balanceValueError": "NPLLIq_balanceValueError",
350
+ "monitorName": "NPLLIq_monitorName",
351
+ "noteWinBtn": "NPLLIq_noteWinBtn",
352
+ "balanceRowRefresh": "NPLLIq_balanceRowRefresh",
353
+ "resetRow": "NPLLIq_resetRow",
352
354
  "logSearch": "NPLLIq_logSearch",
353
- "noticeError": "NPLLIq_noticeError",
354
- "monitorRow": "NPLLIq_monitorRow",
355
- "resetSelect": "NPLLIq_resetSelect",
356
- "logModelCol": "NPLLIq_logModelCol",
357
- "bar": "NPLLIq_bar",
358
- "logClearBtn": "NPLLIq_logClearBtn",
359
- "settingsBtnActive": "NPLLIq_settingsBtnActive",
360
- "balanceToggle": "NPLLIq_balanceToggle",
361
- "balanceBarLow": "NPLLIq_balanceBarLow",
355
+ "monitorHint": "NPLLIq_monitorHint",
356
+ "logPagerControls": "NPLLIq_logPagerControls",
362
357
  "save": "NPLLIq_save",
363
- "noteWinText": "NPLLIq_noteWinText",
364
358
  "logClearDialog": "NPLLIq_logClearDialog",
365
- "balanceBarBadge": "NPLLIq_balanceBarBadge",
366
- "logPagerBtn": "NPLLIq_logPagerBtn",
367
- "panelDim": "NPLLIq_panelDim",
368
- "balanceWin": "NPLLIq_balanceWin",
359
+ "tabLabel": "NPLLIq_tabLabel",
360
+ "balanceRefreshAll": "NPLLIq_balanceRefreshAll",
361
+ "noteNewPlus": "NPLLIq_noteNewPlus",
362
+ "balanceWinBody": "NPLLIq_balanceWinBody",
363
+ "collapse": "NPLLIq_collapse",
364
+ "dialogSection": "NPLLIq_dialogSection",
365
+ "dialogClose": "NPLLIq_dialogClose",
366
+ "balanceRowName": "NPLLIq_balanceRowName",
367
+ "dialog": "NPLLIq_dialog",
368
+ "upgradeCommands": "NPLLIq_upgradeCommands",
369
+ "logPager": "NPLLIq_logPager",
370
+ "fillWarn": "NPLLIq_fillWarn",
371
+ "rowDropTarget": "NPLLIq_rowDropTarget",
372
+ "dialogOverlay": "NPLLIq_dialogOverlay",
373
+ "checkBtn": "NPLLIq_checkBtn",
374
+ "notesBar": "NPLLIq_notesBar",
375
+ "dialogBtn": "NPLLIq_dialogBtn",
376
+ "radioRow": "NPLLIq_radioRow",
369
377
  "balanceBarText": "NPLLIq_balanceBarText",
378
+ "monitorRow": "NPLLIq_monitorRow",
379
+ "balanceRowHeader": "NPLLIq_balanceRowHeader",
380
+ "noteWinTitle": "NPLLIq_noteWinTitle",
370
381
  "upgradeBannerText": "NPLLIq_upgradeBannerText",
371
- "fullNotice": "NPLLIq_fullNotice",
372
- "checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
373
- "resetRow": "NPLLIq_resetRow",
374
- "rowDropTarget": "NPLLIq_rowDropTarget",
382
+ "balanceBarRefresh": "NPLLIq_balanceBarRefresh",
383
+ "logToolbar": "NPLLIq_logToolbar",
384
+ "balanceBarError": "NPLLIq_balanceBarError",
385
+ "version": "NPLLIq_version",
386
+ "upgradeBanner": "NPLLIq_upgradeBanner",
375
387
  "logHeader": "NPLLIq_logHeader",
376
- "logTable": "NPLLIq_logTable",
377
- "tabLabel": "NPLLIq_tabLabel",
378
- "balanceValueLow": "NPLLIq_balanceValueLow",
379
- "switch": "NPLLIq_switch",
380
- "noteNew": "NPLLIq_noteNew",
381
- "copyBtnDone": "NPLLIq_copyBtnDone",
382
- "panel": "NPLLIq_panel",
383
- "dialogBody": "NPLLIq_dialogBody",
384
- "balanceRowHeader": "NPLLIq_balanceRowHeader",
385
- "logUsedCol": "NPLLIq_logUsedCol",
386
- "body": "NPLLIq_body",
388
+ "logScroll": "NPLLIq_logScroll",
389
+ "bar": "NPLLIq_bar",
390
+ "gearBtn": "NPLLIq_gearBtn",
391
+ "headerText": "NPLLIq_headerText",
392
+ "input": "NPLLIq_input",
387
393
  "logDialog": "NPLLIq_logDialog",
388
- "dialogHint": "NPLLIq_dialogHint",
394
+ "versionNew": "NPLLIq_versionNew",
389
395
  "logPagerPages": "NPLLIq_logPagerPages",
390
- "notice": "NPLLIq_notice",
391
- "dialogSection": "NPLLIq_dialogSection",
392
- "copyBtn": "NPLLIq_copyBtn",
393
- "checkBtn": "NPLLIq_checkBtn",
394
- "noteWinTitle": "NPLLIq_noteWinTitle",
395
- "notesBar": "NPLLIq_notesBar",
396
- "balanceLastLine": "NPLLIq_balanceLastLine",
397
- "balanceWinTitle": "NPLLIq_balanceWinTitle",
398
- "balanceBar": "NPLLIq_balanceBar",
399
- "monitorHint": "NPLLIq_monitorHint",
400
- "upgradeBannerFlash": "NPLLIq_upgradeBannerFlash"
396
+ "logClearBtn": "NPLLIq_logClearBtn",
397
+ "dialogTitle": "NPLLIq_dialogTitle",
398
+ "settingsBtnActive": "NPLLIq_settingsBtnActive",
399
+ "title": "NPLLIq_title",
400
+ "rowDrag": "NPLLIq_rowDrag"
401
401
  };
402
402
  //#endregion
403
403
  //#region lib/types/client/TokenQuotaPanel.js
@@ -1068,13 +1068,13 @@ window.__ModuleLoader__.load({
1068
1068
  title: t("upgradeAvailableHint"),
1069
1069
  children: [
1070
1070
  "v",
1071
- "0.1.41",
1071
+ "0.1.43",
1072
1072
  " → v",
1073
1073
  upgrade.latestVersion
1074
1074
  ]
1075
1075
  }) : (0, react_jsx_runtime.jsxs)("span", {
1076
1076
  className: TokenQuotaPanel_module_css_default.version,
1077
- children: ["v", "0.1.41"]
1077
+ children: ["v", "0.1.43"]
1078
1078
  })]
1079
1079
  })
1080
1080
  }),
@@ -2890,13 +2890,13 @@ window.__ModuleLoader__.load({
2890
2890
  document.head.appendChild(tag);
2891
2891
  }
2892
2892
  var mic_module_css_default = {
2893
- "micRec": "KCoVpa_micRec",
2893
+ "micPulse": "KCoVpa_micPulse",
2894
2894
  "micErr": "KCoVpa_micErr",
2895
2895
  "micErrTip": "KCoVpa_micErrTip",
2896
+ "micRec": "KCoVpa_micRec",
2896
2897
  "micBusy": "KCoVpa_micBusy",
2897
- "micWrap": "KCoVpa_micWrap",
2898
2898
  "micBtn": "KCoVpa_micBtn",
2899
- "micPulse": "KCoVpa_micPulse"
2899
+ "micWrap": "KCoVpa_micWrap"
2900
2900
  };
2901
2901
  //#endregion
2902
2902
  //#region lib/types/client/mic.js
@@ -2907,11 +2907,17 @@ window.__ModuleLoader__.load({
2907
2907
  * Host, which forwards it to the configured OpenAI-compatible transcription
2908
2908
  * endpoint. The returned text is appended to the composer. The button only
2909
2909
  * renders while transcription is enabled in the token-quota settings.
2910
+ *
2911
+ * This entry is mounted in a session-scoped slot, so it cannot share the
2912
+ * panel's root-scoped store handle (which would fork a per-session instance
2913
+ * with stale state). Instead it subscribes to a tiny transcribe-only
2914
+ * observable passed through injection and reads the locale-bound `t` from
2915
+ * injection as well.
2910
2916
  */
2911
2917
  /**
2912
2918
  * Append recognised text to the contentEditable composer. Focuses the editor,
2913
2919
  * moves the caret to the end, and uses `insertText` so the host editor's own
2914
- * input handling (undo, model binding) sees it as a typed insertion.
2920
+ * input handling (undo, lexical binding) sees it as a typed insertion.
2915
2921
  */
2916
2922
  function appendToComposer(text) {
2917
2923
  const editor = document.querySelector("[data-composer-input]");
@@ -2938,8 +2944,8 @@ window.__ModuleLoader__.load({
2938
2944
  * Mic button rendered inside the composer card. Manages its own MediaRecorder
2939
2945
  * lifecycle; only the button is shown — no floating panel UI.
2940
2946
  */
2941
- function TokenQuotaMic({ t, useStore, transcribe }) {
2942
- const enabled = useStore((s) => s.transcribe.enabled);
2947
+ function TokenQuotaMic({ t, transcribe, subscribeTranscribe, getTranscribe }) {
2948
+ const settings = (0, react.useSyncExternalStore)(subscribeTranscribe, getTranscribe, getTranscribe);
2943
2949
  const [recording, setRecording] = (0, react.useState)(false);
2944
2950
  const [transcribing, setTranscribing] = (0, react.useState)(false);
2945
2951
  const [error, setError] = (0, react.useState)(null);
@@ -3016,7 +3022,7 @@ window.__ModuleLoader__.load({
3016
3022
  if (recording) stopAndTranscribe();
3017
3023
  else startRecording();
3018
3024
  };
3019
- if (!enabled) return null;
3025
+ if (!settings.enabled) return null;
3020
3026
  const state = transcribing ? "busy" : recording ? "rec" : "idle";
3021
3027
  return (0, react_jsx_runtime.jsxs)("div", {
3022
3028
  className: mic_module_css_default.micWrap,
@@ -3422,6 +3428,23 @@ window.__ModuleLoader__.load({
3422
3428
  apiKeyEnv: typeof raw?.apiKeyEnv === "string" && raw.apiKeyEnv !== "" ? raw.apiKeyEnv : TOKEN_QUOTA_DEFAULT_TRANSCRIBE.apiKeyEnv,
3423
3429
  model: typeof raw?.model === "string" && raw.model !== "" ? raw.model : TOKEN_QUOTA_DEFAULT_TRANSCRIBE.model
3424
3430
  });
3431
+ let currentTranscribe = TOKEN_QUOTA_DEFAULT_TRANSCRIBE;
3432
+ const transcribeSubscribers = /* @__PURE__ */ new Set();
3433
+ const emitTranscribe = () => {
3434
+ for (const fn of transcribeSubscribers) fn();
3435
+ };
3436
+ const subscribeTranscribe = (cb) => {
3437
+ transcribeSubscribers.add(cb);
3438
+ return () => {
3439
+ transcribeSubscribers.delete(cb);
3440
+ };
3441
+ };
3442
+ const getTranscribe = () => currentTranscribe;
3443
+ const applyTranscribe = (settings) => {
3444
+ if (settings.enabled === currentTranscribe.enabled && settings.baseURL === currentTranscribe.baseURL && settings.apiKeyEnv === currentTranscribe.apiKeyEnv && settings.model === currentTranscribe.model) return;
3445
+ currentTranscribe = settings;
3446
+ emitTranscribe();
3447
+ };
3425
3448
  const keyOf = (selection) => {
3426
3449
  if (selection === null) return void 0;
3427
3450
  return `${selection.provider}/${selection.model}`;
@@ -3544,7 +3567,10 @@ window.__ModuleLoader__.load({
3544
3567
  bound?.setMusicVolume(musicSettings.volume);
3545
3568
  bound?.setMusicStyle(musicSettings.style);
3546
3569
  bound?.setMusicOnlyCurrentSession(musicSettings.onlyCurrentSession);
3547
- bound?.setTranscribeSettings(transcribeDefaults(doc?.transcribe));
3570
+ const transcribeRaw = doc?.transcribe;
3571
+ const resolvedTranscribe = transcribeDefaults(transcribeRaw);
3572
+ applyTranscribe(resolvedTranscribe);
3573
+ bound?.setTranscribeSettings(resolvedTranscribe);
3548
3574
  fetch("/token-quota", { headers: { accept: "application/json" } }).then((response) => {
3549
3575
  if (!response.ok) {
3550
3576
  bound?.setError(`quota route: ${String(response.status)}`);
@@ -3635,6 +3661,7 @@ window.__ModuleLoader__.load({
3635
3661
  };
3636
3662
  /** Persist the voice transcription settings and mirror them into the store. */
3637
3663
  const setTranscribeSettings = (settings) => {
3664
+ applyTranscribe(settings);
3638
3665
  bound?.setTranscribeSettings(settings);
3639
3666
  scope.set("transcribe", settings);
3640
3667
  };
@@ -3809,15 +3836,13 @@ window.__ModuleLoader__.load({
3809
3836
  id: "token-quota-mic",
3810
3837
  order: 100,
3811
3838
  label: () => t("transcribeTitle"),
3812
- store,
3813
- locale: NS,
3814
- inject: (_sessionId, micActions) => {
3815
- bound = micActions;
3816
- return {
3817
- transcribe,
3818
- setTranscribeSettings
3819
- };
3820
- }
3839
+ inject: (_sessionId) => ({
3840
+ transcribe,
3841
+ setTranscribeSettings,
3842
+ subscribeTranscribe,
3843
+ getTranscribe,
3844
+ t
3845
+ })
3821
3846
  }, TokenQuotaMic));
3822
3847
  }
3823
3848
  //#endregion
package/lib/index.js CHANGED
@@ -651,7 +651,7 @@ var TokenQuotaService = class TokenQuotaService extends Service {
651
651
  return this.upgradeCheck;
652
652
  }
653
653
  async doRefreshUpgrade() {
654
- const current = "0.1.41";
654
+ const current = "0.1.43";
655
655
  let latest;
656
656
  try {
657
657
  const response = await fetch(REGISTRY_LATEST_URL, {
@@ -129,6 +129,28 @@ export function apply(ctx) {
129
129
  apiKeyEnv: typeof raw?.apiKeyEnv === 'string' && raw.apiKeyEnv !== '' ? raw.apiKeyEnv : TOKEN_QUOTA_DEFAULT_TRANSCRIBE.apiKeyEnv,
130
130
  model: typeof raw?.model === 'string' && raw.model !== '' ? raw.model : TOKEN_QUOTA_DEFAULT_TRANSCRIBE.model,
131
131
  });
132
+ // --- Transcription settings source (independent of the panel store, so the
133
+ // mic entry — mounted in the session-scoped conversation.input.overlay
134
+ // slot — can subscribe without sharing the panel's root-scoped store
135
+ // instance (which would fork state across scopes). ---
136
+ let currentTranscribe = TOKEN_QUOTA_DEFAULT_TRANSCRIBE;
137
+ const transcribeSubscribers = new Set();
138
+ const emitTranscribe = () => { for (const fn of transcribeSubscribers)
139
+ fn(); };
140
+ const subscribeTranscribe = (cb) => {
141
+ transcribeSubscribers.add(cb);
142
+ return () => { transcribeSubscribers.delete(cb); };
143
+ };
144
+ const getTranscribe = () => currentTranscribe;
145
+ const applyTranscribe = (settings) => {
146
+ if (settings.enabled === currentTranscribe.enabled
147
+ && settings.baseURL === currentTranscribe.baseURL
148
+ && settings.apiKeyEnv === currentTranscribe.apiKeyEnv
149
+ && settings.model === currentTranscribe.model)
150
+ return;
151
+ currentTranscribe = settings;
152
+ emitTranscribe();
153
+ };
132
154
  const keyOf = (selection) => {
133
155
  if (selection === null)
134
156
  return undefined;
@@ -299,7 +321,10 @@ export function apply(ctx) {
299
321
  bound?.setMusicVolume(musicSettings.volume);
300
322
  bound?.setMusicStyle(musicSettings.style);
301
323
  bound?.setMusicOnlyCurrentSession(musicSettings.onlyCurrentSession);
302
- bound?.setTranscribeSettings(transcribeDefaults(doc?.transcribe));
324
+ const transcribeRaw = doc?.transcribe;
325
+ const resolvedTranscribe = transcribeDefaults(transcribeRaw);
326
+ applyTranscribe(resolvedTranscribe);
327
+ bound?.setTranscribeSettings(resolvedTranscribe);
303
328
  void fetch('/token-quota', { headers: { accept: 'application/json' } }).then((response) => {
304
329
  if (!response.ok) {
305
330
  bound?.setError(`quota route: ${String(response.status)}`);
@@ -389,6 +414,7 @@ export function apply(ctx) {
389
414
  };
390
415
  /** Persist the voice transcription settings and mirror them into the store. */
391
416
  const setTranscribeSettings = (settings) => {
417
+ applyTranscribe(settings);
392
418
  bound?.setTranscribeSettings(settings);
393
419
  void scope.set('transcribe', settings);
394
420
  };
@@ -562,17 +588,22 @@ export function apply(ctx) {
562
588
  // transcription is enabled in settings; a press starts recording, a second
563
589
  // press stops and sends the audio to the Host for transcription, then the
564
590
  // recognised text is appended to the composer.
591
+ // NOTE: This entry is scoped to a session (not root), so it cannot share the
592
+ // panel's root-scoped store handle without forking a separate per-session
593
+ // instance. Instead it subscribes to a tiny transcribe-only observable and
594
+ // receives the locale-bound `t` function through injection.
565
595
  ctx.slots.inject('conversation.input.overlay', () => ctx.slots.register({
566
596
  name: 'conversation.input.overlay',
567
597
  id: 'token-quota-mic',
568
598
  order: 100,
569
599
  label: () => t('transcribeTitle'),
570
- store,
571
- locale: NS,
572
- inject: (_sessionId, micActions) => {
573
- bound = micActions;
574
- return { transcribe, setTranscribeSettings };
575
- },
600
+ inject: (_sessionId) => ({
601
+ transcribe,
602
+ setTranscribeSettings,
603
+ subscribeTranscribe,
604
+ getTranscribe,
605
+ t: t,
606
+ }),
576
607
  }, TokenQuotaMic));
577
608
  }
578
609
  //# sourceMappingURL=index.js.map
@@ -1,19 +1,23 @@
1
- import type { PropsLocale, PropsRuntime, PropsStore } from '@deepseek-ai/dsh-client-ui-slots';
2
- import { TOKEN_QUOTA_NAMESPACE } from '@jxgame2020/dsh-token-quota/types';
1
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
2
  import type { TokenQuotaTranscribeSettings } from '@jxgame2020/dsh-token-quota/types';
4
- import type { createTokenQuotaPanelStore } from './store.ts';
5
- /** Injected business face: transcription send + settings persistence. */
3
+ /** Injected business face: transcription send + settings subscription. */
6
4
  export interface TokenQuotaMicInjected {
7
5
  /** Send a recorded audio blob to the Host for transcription. */
8
6
  transcribe: (blob: Blob) => Promise<string>;
9
7
  /** Persist the transcription settings (the panel owns the settings UI). */
10
8
  setTranscribeSettings: (settings: TokenQuotaTranscribeSettings) => void;
9
+ /** Subscribe to transcribe settings changes (returns an unsubscribe). */
10
+ subscribeTranscribe: (cb: () => void) => () => void;
11
+ /** Read the current transcribe settings snapshot. */
12
+ getTranscribe: () => TokenQuotaTranscribeSettings;
13
+ /** Locale-bound translate function. */
14
+ t: (key: string) => string;
11
15
  }
12
- /** Full mic component props: runtime + store + locale + injected face. */
13
- export type TokenQuotaMicComponentProps = PropsRuntime<'conversation.input.overlay'> & PropsStore<ReturnType<typeof createTokenQuotaPanelStore>> & PropsLocale<typeof TOKEN_QUOTA_NAMESPACE> & TokenQuotaMicInjected;
16
+ /** Full mic component props: runtime + injected face (no store/locale shares). */
17
+ export type TokenQuotaMicComponentProps = PropsRuntime<'conversation.input.overlay'> & TokenQuotaMicInjected;
14
18
  /**
15
19
  * Mic button rendered inside the composer card. Manages its own MediaRecorder
16
20
  * lifecycle; only the button is shown — no floating panel UI.
17
21
  */
18
- export declare function TokenQuotaMic({ t, useStore, transcribe, }: TokenQuotaMicComponentProps): JSX.Element | null;
22
+ export declare function TokenQuotaMic({ t, transcribe, subscribeTranscribe, getTranscribe, }: TokenQuotaMicComponentProps): JSX.Element | null;
19
23
  //# sourceMappingURL=mic.d.ts.map
@@ -6,23 +6,27 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * Host, which forwards it to the configured OpenAI-compatible transcription
7
7
  * endpoint. The returned text is appended to the composer. The button only
8
8
  * renders while transcription is enabled in the token-quota settings.
9
+ *
10
+ * This entry is mounted in a session-scoped slot, so it cannot share the
11
+ * panel's root-scoped store handle (which would fork a per-session instance
12
+ * with stale state). Instead it subscribes to a tiny transcribe-only
13
+ * observable passed through injection and reads the locale-bound `t` from
14
+ * injection as well.
9
15
  */
10
- import { useEffect, useRef, useState } from 'react';
16
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
11
17
  import css from './mic.module.css';
12
18
  /**
13
19
  * Append recognised text to the contentEditable composer. Focuses the editor,
14
20
  * moves the caret to the end, and uses `insertText` so the host editor's own
15
- * input handling (undo, model binding) sees it as a typed insertion.
21
+ * input handling (undo, lexical binding) sees it as a typed insertion.
16
22
  */
17
23
  function appendToComposer(text) {
18
24
  const editor = document.querySelector('[data-composer-input]');
19
25
  if (editor === null) {
20
- // No composer on this page — copy to the clipboard as a fallback.
21
26
  void navigator.clipboard?.writeText(text).catch(() => { });
22
27
  return;
23
28
  }
24
29
  editor.focus();
25
- // Move the caret to the end of the editor's text content.
26
30
  const selection = window.getSelection();
27
31
  if (selection !== null) {
28
32
  const range = document.createRange();
@@ -31,9 +35,6 @@ function appendToComposer(text) {
31
35
  selection.removeAllRanges();
32
36
  selection.addRange(range);
33
37
  }
34
- // execCommand is deprecated but remains the only cross-browser way to trigger
35
- // a contentEditable editor's own "typed text" handling (undo stack, lexical
36
- // binding). The host composer listens to it.
37
38
  let inserted = false;
38
39
  try {
39
40
  inserted = document.execCommand('insertText', false, text);
@@ -49,8 +50,8 @@ function appendToComposer(text) {
49
50
  * Mic button rendered inside the composer card. Manages its own MediaRecorder
50
51
  * lifecycle; only the button is shown — no floating panel UI.
51
52
  */
52
- export function TokenQuotaMic({ t, useStore, transcribe, }) {
53
- const enabled = useStore(s => s.transcribe.enabled);
53
+ export function TokenQuotaMic({ t, transcribe, subscribeTranscribe, getTranscribe, }) {
54
+ const settings = useSyncExternalStore(subscribeTranscribe, getTranscribe, getTranscribe);
54
55
  const [recording, setRecording] = useState(false);
55
56
  const [transcribing, setTranscribing] = useState(false);
56
57
  const [error, setError] = useState(null);
@@ -58,8 +59,6 @@ export function TokenQuotaMic({ t, useStore, transcribe, }) {
58
59
  const chunksRef = useRef([]);
59
60
  const streamRef = useRef(null);
60
61
  const errorTimerRef = useRef(undefined);
61
- // Clean up the recorder / stream on unmount so a navigation while recording
62
- // stops the mic tracks rather than leaving them open.
63
62
  useEffect(() => {
64
63
  return () => {
65
64
  if (errorTimerRef.current !== undefined)
@@ -150,7 +149,7 @@ export function TokenQuotaMic({ t, useStore, transcribe, }) {
150
149
  void startRecording();
151
150
  }
152
151
  };
153
- if (!enabled)
152
+ if (!settings.enabled)
154
153
  return null;
155
154
  const state = transcribing ? 'busy' : recording ? 'rec' : 'idle';
156
155
  return (_jsxs("div", { className: css.micWrap, title: error ?? t('transcribeHint'), children: [_jsx("button", { type: "button", className: `${css.micBtn}${state === 'rec' ? ` ${css.micRec}` : ''}${state === 'busy' ? ` ${css.micBusy}` : ''}${error !== null ? ` ${css.micErr}` : ''}`, disabled: transcribing, onClick: (event) => { event.stopPropagation(); onToggle(); }, onPointerDown: (event) => { event.stopPropagation(); }, "aria-pressed": recording, "aria-label": error ?? t('transcribeTitle'), children: transcribing ? '…' : '🎤' }), error !== null && _jsx("span", { className: css.micErrTip, children: error })] }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jxgame2020/dsh-token-quota",
3
3
  "description": "Daily per-model token quota accounting, enforcement, and a floating quota panel for the DeepSeek Harness — a fully self-contained plugin (no core-Harness wiring, no generated RPC contract)",
4
- "version": "0.1.41",
4
+ "version": "0.1.43",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },