@jxgame2020/dsh-token-quota 0.1.23 → 0.1.26
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 +341 -195
- package/lib/index.js +13 -4
- package/lib/types/client/TokenQuotaPanel.d.ts +3 -1
- package/lib/types/client/TokenQuotaPanel.js +3 -2
- package/lib/types/client/index.js +26 -8
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/locales.js +4 -0
- package/lib/types/client/music.d.ts +43 -18
- package/lib/types/client/music.js +222 -66
- package/lib/types/client/store.d.ts +2 -0
- package/lib/types/client/store.js +2 -0
- package/lib/types/index.js +5 -0
- package/lib/types/types.d.ts +10 -0
- package/lib/types/types.js +1 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -40,6 +40,7 @@ window.__ModuleLoader__.load({
|
|
|
40
40
|
musicEnabled: false,
|
|
41
41
|
musicVolume: .5,
|
|
42
42
|
musicStyle: "major",
|
|
43
|
+
musicOnlyCurrentSession: true,
|
|
43
44
|
upgrade: null,
|
|
44
45
|
upgradeDismissed: false,
|
|
45
46
|
checkingUpdates: false,
|
|
@@ -90,8 +91,11 @@ window.__ModuleLoader__.load({
|
|
|
90
91
|
setMusicStyle: (d, style) => {
|
|
91
92
|
d.musicStyle = style;
|
|
92
93
|
},
|
|
94
|
+
setMusicOnlyCurrentSession: (d, only) => {
|
|
95
|
+
d.musicOnlyCurrentSession = only;
|
|
96
|
+
},
|
|
93
97
|
setUpgrade: (d, upgrade) => {
|
|
94
|
-
const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.
|
|
98
|
+
const normalized = upgrade !== null && upgrade.latestVersion !== "" && upgrade.latestVersion !== "0.1.26" ? upgrade : null;
|
|
95
99
|
if (d.upgrade === null || d.upgrade.latestVersion !== normalized?.latestVersion) d.upgradeDismissed = false;
|
|
96
100
|
d.upgrade = normalized;
|
|
97
101
|
},
|
|
@@ -197,92 +201,92 @@ window.__ModuleLoader__.load({
|
|
|
197
201
|
document.head.appendChild(tag);
|
|
198
202
|
}
|
|
199
203
|
var TokenQuotaPanel_module_css_default = {
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
204
|
+
"checkBtn": "NPLLIq_checkBtn",
|
|
205
|
+
"body": "NPLLIq_body",
|
|
206
|
+
"dialogLabel": "NPLLIq_dialogLabel",
|
|
207
|
+
"upgradeBanner": "NPLLIq_upgradeBanner",
|
|
208
|
+
"dialogActions": "NPLLIq_dialogActions",
|
|
209
|
+
"currentBadge": "NPLLIq_currentBadge",
|
|
210
|
+
"dialogBody": "NPLLIq_dialogBody",
|
|
211
|
+
"notice": "NPLLIq_notice",
|
|
212
|
+
"rowMeta": "NPLLIq_rowMeta",
|
|
203
213
|
"title": "NPLLIq_title",
|
|
214
|
+
"logPagerInfo": "NPLLIq_logPagerInfo",
|
|
215
|
+
"headerText": "NPLLIq_headerText",
|
|
216
|
+
"bar": "NPLLIq_bar",
|
|
204
217
|
"noticeError": "NPLLIq_noticeError",
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"checkUpdatesRow": "NPLLIq_checkUpdatesRow",
|
|
208
|
-
"logHeader": "NPLLIq_logHeader",
|
|
209
|
-
"logTable": "NPLLIq_logTable",
|
|
218
|
+
"tab": "NPLLIq_tab",
|
|
219
|
+
"logScroll": "NPLLIq_logScroll",
|
|
210
220
|
"dialogTitle": "NPLLIq_dialogTitle",
|
|
211
|
-
"
|
|
212
|
-
"
|
|
221
|
+
"selectBtn": "NPLLIq_selectBtn",
|
|
222
|
+
"monitorList": "NPLLIq_monitorList",
|
|
223
|
+
"switch": "NPLLIq_switch",
|
|
224
|
+
"monitorRow": "NPLLIq_monitorRow",
|
|
225
|
+
"resetSelect": "NPLLIq_resetSelect",
|
|
226
|
+
"dialogBtn": "NPLLIq_dialogBtn",
|
|
227
|
+
"logHeader": "NPLLIq_logHeader",
|
|
228
|
+
"panelDim": "NPLLIq_panelDim",
|
|
229
|
+
"logToolbar": "NPLLIq_logToolbar",
|
|
230
|
+
"upgradeCommands": "NPLLIq_upgradeCommands",
|
|
231
|
+
"upgradeHint": "NPLLIq_upgradeHint",
|
|
232
|
+
"logDayCol": "NPLLIq_logDayCol",
|
|
233
|
+
"settingsBtn": "NPLLIq_settingsBtn",
|
|
213
234
|
"tabLabel": "NPLLIq_tabLabel",
|
|
214
|
-
"logModelCol": "NPLLIq_logModelCol",
|
|
215
|
-
"logPagerPages": "NPLLIq_logPagerPages",
|
|
216
|
-
"logSearch": "NPLLIq_logSearch",
|
|
217
|
-
"monitorName": "NPLLIq_monitorName",
|
|
218
|
-
"controls": "NPLLIq_controls",
|
|
219
235
|
"dialogClose": "NPLLIq_dialogClose",
|
|
236
|
+
"fillOver": "NPLLIq_fillOver",
|
|
237
|
+
"rowHeader": "NPLLIq_rowHeader",
|
|
238
|
+
"upgradeBannerFlash": "NPLLIq_upgradeBannerFlash",
|
|
239
|
+
"copyBtn": "NPLLIq_copyBtn",
|
|
240
|
+
"upgradeClose": "NPLLIq_upgradeClose",
|
|
241
|
+
"logModelCol": "NPLLIq_logModelCol",
|
|
242
|
+
"upgradeCommandRow": "NPLLIq_upgradeCommandRow",
|
|
243
|
+
"logPagerBtn": "NPLLIq_logPagerBtn",
|
|
244
|
+
"upgradeBannerText": "NPLLIq_upgradeBannerText",
|
|
245
|
+
"checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
|
|
246
|
+
"fillWarn": "NPLLIq_fillWarn",
|
|
247
|
+
"headerActions": "NPLLIq_headerActions",
|
|
248
|
+
"save": "NPLLIq_save",
|
|
249
|
+
"checkUpdatesRow": "NPLLIq_checkUpdatesRow",
|
|
250
|
+
"settingsBtnActive": "NPLLIq_settingsBtnActive",
|
|
251
|
+
"radioRow": "NPLLIq_radioRow",
|
|
252
|
+
"logClearBtn": "NPLLIq_logClearBtn",
|
|
220
253
|
"logPageSizeSelect": "NPLLIq_logPageSizeSelect",
|
|
221
254
|
"dialogHint": "NPLLIq_dialogHint",
|
|
222
|
-
"checkBtn": "NPLLIq_checkBtn",
|
|
223
|
-
"dialogBody": "NPLLIq_dialogBody",
|
|
224
|
-
"rowName": "NPLLIq_rowName",
|
|
225
|
-
"copyBtnDone": "NPLLIq_copyBtnDone",
|
|
226
|
-
"logClearDate": "NPLLIq_logClearDate",
|
|
227
|
-
"notice": "NPLLIq_notice",
|
|
228
|
-
"resetRow": "NPLLIq_resetRow",
|
|
229
|
-
"rowMeta": "NPLLIq_rowMeta",
|
|
230
255
|
"logClearDialog": "NPLLIq_logClearDialog",
|
|
231
|
-
"
|
|
232
|
-
"
|
|
233
|
-
"monitorHint": "NPLLIq_monitorHint",
|
|
256
|
+
"monitorName": "NPLLIq_monitorName",
|
|
257
|
+
"dialogBtnPrimary": "NPLLIq_dialogBtnPrimary",
|
|
234
258
|
"logPager": "NPLLIq_logPager",
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"selectBtn": "NPLLIq_selectBtn",
|
|
238
|
-
"upgradeCommand": "NPLLIq_upgradeCommand",
|
|
239
|
-
"resetSelect": "NPLLIq_resetSelect",
|
|
240
|
-
"logScroll": "NPLLIq_logScroll",
|
|
241
|
-
"monitorRow": "NPLLIq_monitorRow",
|
|
242
|
-
"upgradeCommandRow": "NPLLIq_upgradeCommandRow",
|
|
243
|
-
"logPagerControls": "NPLLIq_logPagerControls",
|
|
244
|
-
"logDayCol": "NPLLIq_logDayCol",
|
|
245
|
-
"header": "NPLLIq_header",
|
|
259
|
+
"logClearDate": "NPLLIq_logClearDate",
|
|
260
|
+
"rowName": "NPLLIq_rowName",
|
|
246
261
|
"logDialog": "NPLLIq_logDialog",
|
|
247
|
-
"radioRow": "NPLLIq_radioRow",
|
|
248
262
|
"dialogOverlay": "NPLLIq_dialogOverlay",
|
|
249
|
-
"
|
|
250
|
-
"
|
|
251
|
-
"tabIcon": "NPLLIq_tabIcon",
|
|
252
|
-
"checkUpdatesLabel": "NPLLIq_checkUpdatesLabel",
|
|
253
|
-
"switch": "NPLLIq_switch",
|
|
263
|
+
"logUsedCol": "NPLLIq_logUsedCol",
|
|
264
|
+
"input": "NPLLIq_input",
|
|
254
265
|
"gearBtn": "NPLLIq_gearBtn",
|
|
255
|
-
"
|
|
256
|
-
"
|
|
266
|
+
"row": "NPLLIq_row",
|
|
267
|
+
"monitorHint": "NPLLIq_monitorHint",
|
|
268
|
+
"header": "NPLLIq_header",
|
|
269
|
+
"logPagerPages": "NPLLIq_logPagerPages",
|
|
257
270
|
"versionNew": "NPLLIq_versionNew",
|
|
258
|
-
"
|
|
259
|
-
"
|
|
260
|
-
"settingsBtnActive": "NPLLIq_settingsBtnActive",
|
|
261
|
-
"currentBadge": "NPLLIq_currentBadge",
|
|
271
|
+
"subtitle": "NPLLIq_subtitle",
|
|
272
|
+
"version": "NPLLIq_version",
|
|
262
273
|
"fullNotice": "NPLLIq_fullNotice",
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
"panelDimStrong": "NPLLIq_panelDimStrong",
|
|
266
|
-
"logToolbar": "NPLLIq_logToolbar",
|
|
267
|
-
"logPagerBtn": "NPLLIq_logPagerBtn",
|
|
268
|
-
"panelDim": "NPLLIq_panelDim",
|
|
269
|
-
"upgradeBanner": "NPLLIq_upgradeBanner",
|
|
270
|
-
"copyBtn": "NPLLIq_copyBtn",
|
|
271
|
-
"body": "NPLLIq_body",
|
|
272
|
-
"upgradeHint": "NPLLIq_upgradeHint",
|
|
273
|
-
"dialog": "NPLLIq_dialog",
|
|
274
|
+
"resetRow": "NPLLIq_resetRow",
|
|
275
|
+
"logTable": "NPLLIq_logTable",
|
|
274
276
|
"fillIdle": "NPLLIq_fillIdle",
|
|
275
|
-
"headerText": "NPLLIq_headerText",
|
|
276
|
-
"logPagerInfo": "NPLLIq_logPagerInfo",
|
|
277
|
-
"headerActions": "NPLLIq_headerActions",
|
|
278
277
|
"checkErrorHint": "NPLLIq_checkErrorHint",
|
|
279
|
-
"
|
|
280
|
-
"
|
|
281
|
-
"
|
|
282
|
-
"
|
|
278
|
+
"tabIcon": "NPLLIq_tabIcon",
|
|
279
|
+
"logPagerControls": "NPLLIq_logPagerControls",
|
|
280
|
+
"collapse": "NPLLIq_collapse",
|
|
281
|
+
"upgradeCommand": "NPLLIq_upgradeCommand",
|
|
282
|
+
"copyBtnDone": "NPLLIq_copyBtnDone",
|
|
283
|
+
"dialogSection": "NPLLIq_dialogSection",
|
|
284
|
+
"controls": "NPLLIq_controls",
|
|
285
|
+
"panel": "NPLLIq_panel",
|
|
286
|
+
"panelDimStrong": "NPLLIq_panelDimStrong",
|
|
287
|
+
"logSearch": "NPLLIq_logSearch",
|
|
283
288
|
"dialogHeader": "NPLLIq_dialogHeader",
|
|
284
|
-
"
|
|
285
|
-
"fillOver": "NPLLIq_fillOver"
|
|
289
|
+
"dialog": "NPLLIq_dialog"
|
|
286
290
|
};
|
|
287
291
|
//#endregion
|
|
288
292
|
//#region lib/types/client/TokenQuotaPanel.js
|
|
@@ -369,7 +373,7 @@ window.__ModuleLoader__.load({
|
|
|
369
373
|
* @param props - composed slot props.
|
|
370
374
|
* @returns the panel element tree.
|
|
371
375
|
*/
|
|
372
|
-
function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, setMusicEnabled, setMusicVolume, setMusicStyle, useStore, actions, useSessions }) {
|
|
376
|
+
function TokenQuotaPanel({ t, load, setLimit, selectModel, setMonitored, setOnFull, setReset, setCheckUpdates, checkUpdatesNow, clearLogBefore, setDimWhenIdle, setMusicEnabled, setMusicVolume, setMusicStyle, setMusicOnlyCurrentSession, useStore, actions, useSessions }) {
|
|
373
377
|
const [collapsed, setCollapsed] = (0, react.useState)(false);
|
|
374
378
|
const [drafts, setDrafts] = (0, react.useState)({});
|
|
375
379
|
const [editingKey, setEditingKey] = (0, react.useState)(null);
|
|
@@ -403,6 +407,7 @@ window.__ModuleLoader__.load({
|
|
|
403
407
|
const musicEnabled = useStore((s) => s.musicEnabled);
|
|
404
408
|
const musicVolume = useStore((s) => s.musicVolume);
|
|
405
409
|
const musicStyle = useStore((s) => s.musicStyle);
|
|
410
|
+
const musicOnlyCurrentSession = useStore((s) => s.musicOnlyCurrentSession);
|
|
406
411
|
const upgrade = useStore((s) => s.upgrade);
|
|
407
412
|
const upgradeDismissed = useStore((s) => s.upgradeDismissed);
|
|
408
413
|
const checkingUpdates = useStore((s) => s.checkingUpdates);
|
|
@@ -587,13 +592,13 @@ window.__ModuleLoader__.load({
|
|
|
587
592
|
title: t("upgradeAvailableHint"),
|
|
588
593
|
children: [
|
|
589
594
|
"v",
|
|
590
|
-
"0.1.
|
|
595
|
+
"0.1.26",
|
|
591
596
|
" → v",
|
|
592
597
|
upgrade.latestVersion
|
|
593
598
|
]
|
|
594
599
|
}) : (0, react_jsx_runtime.jsxs)("span", {
|
|
595
600
|
className: TokenQuotaPanel_module_css_default.version,
|
|
596
|
-
children: ["v", "0.1.
|
|
601
|
+
children: ["v", "0.1.26"]
|
|
597
602
|
})]
|
|
598
603
|
}), (0, react_jsx_runtime.jsx)("div", {
|
|
599
604
|
className: TokenQuotaPanel_module_css_default.subtitle,
|
|
@@ -956,41 +961,58 @@ window.__ModuleLoader__.load({
|
|
|
956
961
|
className: TokenQuotaPanel_module_css_default.dialogHint,
|
|
957
962
|
children: t("musicHint")
|
|
958
963
|
}),
|
|
959
|
-
musicEnabled && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
964
|
+
musicEnabled && (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
965
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
966
|
+
className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
|
|
967
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("musicVolume") }), (0, react_jsx_runtime.jsx)("input", {
|
|
968
|
+
type: "range",
|
|
969
|
+
min: 0,
|
|
970
|
+
max: 1,
|
|
971
|
+
step: .05,
|
|
972
|
+
value: musicVolume,
|
|
973
|
+
onChange: (event) => {
|
|
974
|
+
setMusicVolume(Number(event.target.value));
|
|
975
|
+
}
|
|
976
|
+
})]
|
|
977
|
+
}),
|
|
978
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
979
|
+
className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
|
|
980
|
+
children: [(0, react_jsx_runtime.jsx)("span", { children: t("musicStyle") }), (0, react_jsx_runtime.jsxs)("select", {
|
|
981
|
+
value: musicStyle,
|
|
982
|
+
onChange: (event) => {
|
|
983
|
+
setMusicStyle(event.target.value);
|
|
984
|
+
},
|
|
985
|
+
children: [
|
|
986
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
987
|
+
value: "major",
|
|
988
|
+
children: t("musicStyleMajor")
|
|
989
|
+
}),
|
|
990
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
991
|
+
value: "minor",
|
|
992
|
+
children: t("musicStyleMinor")
|
|
993
|
+
}),
|
|
994
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
995
|
+
value: "pentatonic",
|
|
996
|
+
children: t("musicStylePentatonic")
|
|
997
|
+
})
|
|
998
|
+
]
|
|
999
|
+
})]
|
|
1000
|
+
}),
|
|
1001
|
+
(0, react_jsx_runtime.jsxs)("label", {
|
|
1002
|
+
className: TokenQuotaPanel_module_css_default.checkUpdatesLabel,
|
|
1003
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
1004
|
+
type: "checkbox",
|
|
1005
|
+
checked: musicOnlyCurrentSession,
|
|
1006
|
+
onChange: (event) => {
|
|
1007
|
+
setMusicOnlyCurrentSession(event.target.checked);
|
|
1008
|
+
}
|
|
1009
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("musicOnlyCurrentSessionLabel") })]
|
|
1010
|
+
}),
|
|
1011
|
+
(0, react_jsx_runtime.jsx)("div", {
|
|
1012
|
+
className: TokenQuotaPanel_module_css_default.dialogHint,
|
|
1013
|
+
children: t("musicOnlyCurrentSessionHint")
|
|
1014
|
+
})
|
|
1015
|
+
] })
|
|
994
1016
|
]
|
|
995
1017
|
}),
|
|
996
1018
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1210,16 +1232,19 @@ window.__ModuleLoader__.load({
|
|
|
1210
1232
|
//#endregion
|
|
1211
1233
|
//#region lib/types/client/music.js
|
|
1212
1234
|
/**
|
|
1213
|
-
* Live-composed soundtrack driven by host actions (turns, steps,
|
|
1214
|
-
* etc.).
|
|
1215
|
-
* sends MIDI NoteOn/NoteOff to the first available MIDI output when one is
|
|
1216
|
-
* detected (both play simultaneously when MIDI is present — use the panel
|
|
1217
|
-
* volume for overall level, the MIDI output has its own gain on the receiver).
|
|
1235
|
+
* Live-composed ambient soundtrack driven by host actions (turns, steps,
|
|
1236
|
+
* tool calls, etc.). Three layers:
|
|
1218
1237
|
*
|
|
1219
|
-
*
|
|
1220
|
-
*
|
|
1221
|
-
*
|
|
1222
|
-
*
|
|
1238
|
+
* - **Pad**: a sustained three-note chord (triangle waves) that fades in when
|
|
1239
|
+
* a request starts and crossfades to the next chord on every step.
|
|
1240
|
+
* - **Bass**: one long root note per beat (lower octave), keeps the pulse.
|
|
1241
|
+
* - **Melody**: an eighth-note scale line that dances inside the current
|
|
1242
|
+
* chord — always in key, never a wrong note.
|
|
1243
|
+
*
|
|
1244
|
+
* Outputs through Web Audio (built-in synthesis) by default, and also sends
|
|
1245
|
+
* MIDI NoteOn/NoteOff to the first available MIDI output when one is detected
|
|
1246
|
+
* (both play simultaneously when MIDI is present — use the panel volume for
|
|
1247
|
+
* overall level, the MIDI output has its own gain on the receiver).
|
|
1223
1248
|
*
|
|
1224
1249
|
* @module @jxgame2020/dsh-token-quota/music
|
|
1225
1250
|
*/
|
|
@@ -1295,14 +1320,26 @@ window.__ModuleLoader__.load({
|
|
|
1295
1320
|
"major"
|
|
1296
1321
|
]
|
|
1297
1322
|
};
|
|
1298
|
-
/**
|
|
1299
|
-
* Turn a MIDI note number into a frequency (Hz) using equal temperament at
|
|
1300
|
-
* A4 = 440 Hz.
|
|
1301
|
-
*/
|
|
1323
|
+
/** Turn a MIDI note number into a frequency (equal temperament, A4 = 440). */
|
|
1302
1324
|
function midiToFreq(midi) {
|
|
1303
1325
|
return 440 * Math.pow(2, (midi - 69) / 12);
|
|
1304
1326
|
}
|
|
1305
|
-
/**
|
|
1327
|
+
/** Interval in semitones from the root for each chord-tone. */
|
|
1328
|
+
function chordIntervals(quality) {
|
|
1329
|
+
return [
|
|
1330
|
+
0,
|
|
1331
|
+
quality === "major" ? 4 : quality === "minor" ? 3 : 3,
|
|
1332
|
+
quality === "dim" ? 6 : 7
|
|
1333
|
+
];
|
|
1334
|
+
}
|
|
1335
|
+
/**
|
|
1336
|
+
* Live-composed music engine driven by host actions.
|
|
1337
|
+
*
|
|
1338
|
+
* The engine is idle until `ensureStarted()` is called from a user gesture
|
|
1339
|
+
* (the panel switch), and only produces sound while `enabled` is true AND a
|
|
1340
|
+
* request is in flight (pad + bass + melody are gated by `startPad` /
|
|
1341
|
+
* `stopPad`).
|
|
1342
|
+
*/
|
|
1306
1343
|
var TokenQuotaMusic = class TokenQuotaMusic {
|
|
1307
1344
|
audio = null;
|
|
1308
1345
|
midiOutput = null;
|
|
@@ -1310,14 +1347,23 @@ window.__ModuleLoader__.load({
|
|
|
1310
1347
|
enabled = false;
|
|
1311
1348
|
volume = .5;
|
|
1312
1349
|
style = "major";
|
|
1313
|
-
/** Current position in the chord progression (advances per step). */
|
|
1314
1350
|
chordIndex = 0;
|
|
1315
|
-
/** Step counter — used for melody degree selection. */
|
|
1316
1351
|
stepCount = 0;
|
|
1317
|
-
/** Tool counter — used for tool-note pitch. */
|
|
1318
1352
|
toolCount = 0;
|
|
1319
|
-
|
|
1353
|
+
padOscs = [];
|
|
1354
|
+
padGain = null;
|
|
1355
|
+
padTarget = 0;
|
|
1356
|
+
padActive = false;
|
|
1357
|
+
beatTimer;
|
|
1358
|
+
beatIndex = 0;
|
|
1359
|
+
melodyDirection = 1;
|
|
1360
|
+
melodyDegree = 0;
|
|
1361
|
+
currentRoot = 60;
|
|
1362
|
+
currentQuality = "major";
|
|
1363
|
+
/** Base MIDI note of the key. C4 = middle C. */
|
|
1320
1364
|
static BASE_MIDI = 60;
|
|
1365
|
+
/** Beat length in ms — eighth note = beat / 2. */
|
|
1366
|
+
static BEAT_MS = 640;
|
|
1321
1367
|
/** True once the audio context has been started (needs a user gesture). */
|
|
1322
1368
|
get started() {
|
|
1323
1369
|
return this.audio !== null;
|
|
@@ -1353,12 +1399,16 @@ window.__ModuleLoader__.load({
|
|
|
1353
1399
|
}
|
|
1354
1400
|
setEnabled(value) {
|
|
1355
1401
|
this.enabled = value;
|
|
1402
|
+
if (!value) this.stopAll();
|
|
1356
1403
|
}
|
|
1357
1404
|
setVolume(value) {
|
|
1358
1405
|
this.volume = Math.max(0, Math.min(1, value));
|
|
1406
|
+
if (this.padGain !== null && this.audio !== null) this.padGain.gain.setValueAtTime(this.volume * this.padTarget, this.audio.currentTime);
|
|
1359
1407
|
}
|
|
1360
1408
|
setStyle(value) {
|
|
1361
1409
|
this.style = value;
|
|
1410
|
+
this.melodyDegree = 0;
|
|
1411
|
+
this.melodyDirection = 1;
|
|
1362
1412
|
}
|
|
1363
1413
|
/** Handle one incoming host action and turn it into sound. */
|
|
1364
1414
|
onAction(action) {
|
|
@@ -1366,82 +1416,177 @@ window.__ModuleLoader__.load({
|
|
|
1366
1416
|
const base = TokenQuotaMusic.BASE_MIDI;
|
|
1367
1417
|
switch (action.type) {
|
|
1368
1418
|
case "turn/start":
|
|
1419
|
+
this.stopAll();
|
|
1369
1420
|
this.chordIndex = 0;
|
|
1370
1421
|
this.stepCount = 0;
|
|
1371
1422
|
this.toolCount = 0;
|
|
1372
|
-
this.
|
|
1423
|
+
this.currentRoot = base;
|
|
1424
|
+
this.currentQuality = CHORD_QUALITIES[this.style][0] ?? "major";
|
|
1425
|
+
this.playChord(base, this.currentQuality, 1.6, .6);
|
|
1373
1426
|
break;
|
|
1374
1427
|
case "step/start": {
|
|
1375
1428
|
this.chordIndex = (this.chordIndex + 1) % CHORD_PROGRESSIONS[this.style].length;
|
|
1376
1429
|
this.stepCount += 1;
|
|
1377
|
-
this.
|
|
1378
|
-
const
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
setTimeout(() => this.playNote(root + d3, .35, .4), 160);
|
|
1430
|
+
const root = base + (CHORD_PROGRESSIONS[this.style][this.chordIndex] ?? 0);
|
|
1431
|
+
const quality = CHORD_QUALITIES[this.style][this.chordIndex] ?? "major";
|
|
1432
|
+
this.currentRoot = root;
|
|
1433
|
+
this.currentQuality = quality;
|
|
1434
|
+
if (this.padActive) this.crossfadePad(root, quality);
|
|
1383
1435
|
break;
|
|
1384
1436
|
}
|
|
1385
1437
|
case "request/header": {
|
|
1438
|
+
this.startPad(this.currentRoot, this.currentQuality);
|
|
1439
|
+
this.startBeatClock();
|
|
1386
1440
|
const degree = (this.stepCount * 2 + this.toolCount) % SCALES[this.style].length;
|
|
1387
|
-
const note = base + 12 + SCALES[this.style][degree];
|
|
1388
|
-
this.playNote(note, .
|
|
1441
|
+
const note = base + 12 + (SCALES[this.style][degree] ?? 0);
|
|
1442
|
+
this.playNote(note, .25, .4);
|
|
1389
1443
|
break;
|
|
1390
1444
|
}
|
|
1391
1445
|
case "tool/call":
|
|
1392
1446
|
this.toolCount += 1;
|
|
1393
1447
|
this.playNote(base + 24 + this.toolCount % 3 * 2, .12, .45);
|
|
1394
|
-
this.playNote(base - 12, .08, .
|
|
1448
|
+
this.playNote(base - 12, .08, .3);
|
|
1395
1449
|
break;
|
|
1396
1450
|
case "tool/result":
|
|
1397
1451
|
if (action.error) {
|
|
1398
1452
|
this.playNote(base + 13, .18, .4);
|
|
1399
1453
|
setTimeout(() => this.playNote(base + 12, .25, .4), 90);
|
|
1400
1454
|
} else {
|
|
1401
|
-
this.playNote(base + 9, .14, .
|
|
1402
|
-
setTimeout(() => this.playNote(base + 11, .22, .
|
|
1455
|
+
this.playNote(base + 9, .14, .35);
|
|
1456
|
+
setTimeout(() => this.playNote(base + 11, .22, .35), 70);
|
|
1403
1457
|
}
|
|
1404
1458
|
break;
|
|
1459
|
+
case "assistant/attempt":
|
|
1460
|
+
this.playNote(base + 8, .18, .4);
|
|
1461
|
+
setTimeout(() => this.playNote(base + 7, .2, .4), 100);
|
|
1462
|
+
setTimeout(() => this.playNote(base + 5, .3, .4), 200);
|
|
1463
|
+
break;
|
|
1405
1464
|
case "assistant/message":
|
|
1406
|
-
|
|
1407
|
-
|
|
1465
|
+
this.stopBeatClock();
|
|
1466
|
+
this.stopPad();
|
|
1467
|
+
if (action.interrupted) this.playChord(base + 7, "dim", .9, .55);
|
|
1468
|
+
else this.playChord(this.currentRoot, this.currentQuality, 1.1, .6);
|
|
1408
1469
|
break;
|
|
1409
1470
|
case "turn/end":
|
|
1410
|
-
this.
|
|
1471
|
+
this.stopAll();
|
|
1472
|
+
this.playChord(base, this.style === "minor" ? "minor" : "major", 2.4, .7);
|
|
1411
1473
|
this.chordIndex = 0;
|
|
1412
1474
|
break;
|
|
1413
1475
|
}
|
|
1414
1476
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1477
|
+
/** Start the pad from silence, fading in to the target level. */
|
|
1478
|
+
startPad(rootMidi, quality) {
|
|
1479
|
+
if (!this.audio) return;
|
|
1480
|
+
if (this.padActive) return;
|
|
1481
|
+
const now = this.audio.currentTime;
|
|
1482
|
+
const gain = this.audio.createGain();
|
|
1483
|
+
gain.gain.setValueAtTime(0, now);
|
|
1484
|
+
gain.connect(this.audio.destination);
|
|
1485
|
+
const intervals = chordIntervals(quality);
|
|
1486
|
+
const oscs = [];
|
|
1487
|
+
for (const interval of intervals) {
|
|
1488
|
+
const osc = this.audio.createOscillator();
|
|
1489
|
+
osc.type = "triangle";
|
|
1490
|
+
osc.frequency.value = midiToFreq(rootMidi + interval);
|
|
1491
|
+
osc.connect(gain);
|
|
1492
|
+
osc.start(now);
|
|
1493
|
+
oscs.push(osc);
|
|
1494
|
+
}
|
|
1495
|
+
this.padOscs = oscs;
|
|
1496
|
+
this.padGain = gain;
|
|
1497
|
+
this.padTarget = .18;
|
|
1498
|
+
gain.gain.linearRampToValueAtTime(this.volume * this.padTarget, now + .6);
|
|
1499
|
+
this.padActive = true;
|
|
1500
|
+
}
|
|
1501
|
+
/** Smoothly morph the pad to a new root + quality. */
|
|
1502
|
+
crossfadePad(rootMidi, quality) {
|
|
1503
|
+
if (!this.audio || !this.padActive || this.padGain === null) return;
|
|
1504
|
+
const now = this.audio.currentTime;
|
|
1505
|
+
const intervals = chordIntervals(quality);
|
|
1506
|
+
this.padOscs.forEach((osc, i) => {
|
|
1507
|
+
const interval = intervals[i] ?? 0;
|
|
1508
|
+
osc.frequency.setValueAtTime(osc.frequency.value, now);
|
|
1509
|
+
osc.frequency.exponentialRampToValueAtTime(midiToFreq(rootMidi + interval), now + .5);
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
/** Fade the pad to silence, then stop the oscillators. */
|
|
1513
|
+
stopPad() {
|
|
1514
|
+
if (!this.audio || !this.padActive || this.padGain === null) return;
|
|
1515
|
+
const now = this.audio.currentTime;
|
|
1516
|
+
const gain = this.padGain;
|
|
1517
|
+
gain.gain.cancelScheduledValues(now);
|
|
1518
|
+
gain.gain.setValueAtTime(gain.gain.value, now);
|
|
1519
|
+
gain.gain.linearRampToValueAtTime(0, now + .8);
|
|
1520
|
+
const oscs = this.padOscs;
|
|
1521
|
+
setTimeout(() => {
|
|
1522
|
+
for (const osc of oscs) {
|
|
1523
|
+
try {
|
|
1524
|
+
osc.stop();
|
|
1525
|
+
} catch {}
|
|
1526
|
+
try {
|
|
1527
|
+
osc.disconnect();
|
|
1528
|
+
} catch {}
|
|
1529
|
+
}
|
|
1530
|
+
try {
|
|
1531
|
+
gain.disconnect();
|
|
1532
|
+
} catch {}
|
|
1533
|
+
}, 850);
|
|
1534
|
+
this.padActive = false;
|
|
1535
|
+
this.padOscs = [];
|
|
1536
|
+
this.padGain = null;
|
|
1418
1537
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1538
|
+
/** Start the per-beat clock that drives bass and melody. */
|
|
1539
|
+
startBeatClock() {
|
|
1540
|
+
if (this.beatTimer !== void 0) return;
|
|
1541
|
+
this.beatIndex = 0;
|
|
1542
|
+
this.melodyDegree = 0;
|
|
1543
|
+
this.melodyDirection = 1;
|
|
1544
|
+
const tick = () => {
|
|
1545
|
+
if (!this.audio || !this.enabled) return;
|
|
1546
|
+
if (this.beatIndex % 2 === 0) this.playNote(this.currentRoot - 12, .45, .3);
|
|
1547
|
+
const scale = SCALES[this.style];
|
|
1548
|
+
const chordTones = new Set(chordIntervals(this.currentQuality));
|
|
1549
|
+
let degree = this.melodyDegree;
|
|
1550
|
+
let next = degree + this.melodyDirection;
|
|
1551
|
+
if (next >= scale.length) {
|
|
1552
|
+
this.melodyDirection = -1;
|
|
1553
|
+
next = scale.length - 2;
|
|
1554
|
+
} else if (next < 0) {
|
|
1555
|
+
this.melodyDirection = 1;
|
|
1556
|
+
next = 0;
|
|
1557
|
+
}
|
|
1558
|
+
this.melodyDegree = next;
|
|
1559
|
+
degree = next;
|
|
1560
|
+
if (this.beatIndex % 2 === 0) {
|
|
1561
|
+
const rootRel = (scale[degree] ?? 0) % 12;
|
|
1562
|
+
if (!chordTones.has(rootRel)) {
|
|
1563
|
+
this.melodyDegree += this.melodyDirection;
|
|
1564
|
+
degree = this.melodyDegree;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
const note = this.currentRoot + 12 + (scale[degree] ?? 0);
|
|
1568
|
+
this.playNote(note, .22, .28);
|
|
1569
|
+
this.beatIndex = (this.beatIndex + 1) % 4;
|
|
1570
|
+
};
|
|
1571
|
+
tick();
|
|
1572
|
+
this.beatTimer = setInterval(tick, TokenQuotaMusic.BEAT_MS);
|
|
1573
|
+
}
|
|
1574
|
+
stopBeatClock() {
|
|
1575
|
+
if (this.beatTimer !== void 0) {
|
|
1576
|
+
clearInterval(this.beatTimer);
|
|
1577
|
+
this.beatTimer = void 0;
|
|
1578
|
+
}
|
|
1422
1579
|
}
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
0,
|
|
1427
|
-
7,
|
|
1428
|
-
12
|
|
1429
|
-
] : this.currentChordQuality() === "major" ? [
|
|
1430
|
-
0,
|
|
1431
|
-
4,
|
|
1432
|
-
7
|
|
1433
|
-
] : [
|
|
1434
|
-
0,
|
|
1435
|
-
3,
|
|
1436
|
-
7
|
|
1437
|
-
];
|
|
1580
|
+
stopAll() {
|
|
1581
|
+
this.stopBeatClock();
|
|
1582
|
+
this.stopPad();
|
|
1438
1583
|
}
|
|
1439
1584
|
/** Play one note: Web Audio + MIDI simultaneously when available. */
|
|
1440
1585
|
playNote(midi, durationSec, velocity) {
|
|
1441
1586
|
if (!this.audio) return;
|
|
1442
|
-
const now = this.audio.currentTime;
|
|
1443
1587
|
const peak = this.volume * velocity * .5;
|
|
1444
1588
|
if (peak <= 0) return;
|
|
1589
|
+
const now = this.audio.currentTime;
|
|
1445
1590
|
const osc = this.audio.createOscillator();
|
|
1446
1591
|
osc.type = "sine";
|
|
1447
1592
|
osc.frequency.value = midiToFreq(midi);
|
|
@@ -1471,12 +1616,9 @@ window.__ModuleLoader__.load({
|
|
|
1471
1616
|
}
|
|
1472
1617
|
/** Play a three-note chord plus a bass octave below. */
|
|
1473
1618
|
playChord(rootMidi, quality, durationSec, velocity) {
|
|
1474
|
-
const
|
|
1475
|
-
const
|
|
1476
|
-
this.playNote(rootMidi, durationSec, velocity * .
|
|
1477
|
-
this.playNote(rootMidi + third, durationSec, velocity * .35);
|
|
1478
|
-
this.playNote(rootMidi + fifth, durationSec, velocity * .35);
|
|
1479
|
-
this.playNote(rootMidi - 12, durationSec * 1.15, velocity * .45);
|
|
1619
|
+
const intervals = chordIntervals(quality);
|
|
1620
|
+
for (const interval of intervals) this.playNote(rootMidi + interval, durationSec, velocity * .45);
|
|
1621
|
+
this.playNote(rootMidi - 12, durationSec * 1.1, velocity * .5);
|
|
1480
1622
|
}
|
|
1481
1623
|
};
|
|
1482
1624
|
//#endregion
|
|
@@ -1556,6 +1698,8 @@ window.__ModuleLoader__.load({
|
|
|
1556
1698
|
musicStyleMajor: "大调",
|
|
1557
1699
|
musicStyleMinor: "小调",
|
|
1558
1700
|
musicStylePentatonic: "五声",
|
|
1701
|
+
musicOnlyCurrentSessionLabel: "只播当前会话",
|
|
1702
|
+
musicOnlyCurrentSessionHint: "开启后仅当前窗口的会话有音乐,后台子任务静音;关闭则所有会话都参与作曲",
|
|
1559
1703
|
checkUpdatesNow: "立即检查",
|
|
1560
1704
|
checkingUpdates: "检查中…",
|
|
1561
1705
|
checkUpToDate: "已是最新",
|
|
@@ -1630,6 +1774,8 @@ window.__ModuleLoader__.load({
|
|
|
1630
1774
|
musicStyleMajor: "Major",
|
|
1631
1775
|
musicStyleMinor: "Minor",
|
|
1632
1776
|
musicStylePentatonic: "Pentatonic",
|
|
1777
|
+
musicOnlyCurrentSessionLabel: "Current session only",
|
|
1778
|
+
musicOnlyCurrentSessionHint: "When on, only the visible session drives the soundtrack (background tasks stay silent); when off, every session contributes.",
|
|
1633
1779
|
checkUpdatesNow: "Check now",
|
|
1634
1780
|
checkingUpdates: "Checking…",
|
|
1635
1781
|
checkUpToDate: "Up to date",
|
|
@@ -1674,6 +1820,7 @@ window.__ModuleLoader__.load({
|
|
|
1674
1820
|
let lastMonitored = null;
|
|
1675
1821
|
let lastOnFull = "stop";
|
|
1676
1822
|
let lastReset = null;
|
|
1823
|
+
let lastOnlyCurrentSession = true;
|
|
1677
1824
|
const music = new TokenQuotaMusic();
|
|
1678
1825
|
const sse = new EventSource("/token-quota/events");
|
|
1679
1826
|
sse.addEventListener("action", (event) => {
|
|
@@ -1683,12 +1830,19 @@ window.__ModuleLoader__.load({
|
|
|
1683
1830
|
} catch {
|
|
1684
1831
|
return;
|
|
1685
1832
|
}
|
|
1686
|
-
if (lastSessionId !== void 0 && action.sessionId !== String(lastSessionId)) return;
|
|
1833
|
+
if (lastOnlyCurrentSession && lastSessionId !== void 0 && action.sessionId !== String(lastSessionId)) return;
|
|
1687
1834
|
music.onAction(action);
|
|
1688
1835
|
});
|
|
1689
1836
|
ctx.effect(() => () => {
|
|
1690
1837
|
sse.close();
|
|
1691
1838
|
}, "token-quota-ui: music SSE");
|
|
1839
|
+
/** Build a writable music-settings clone with defaults applied. */
|
|
1840
|
+
const musicDefaults = (raw) => ({
|
|
1841
|
+
enabled: raw?.enabled ?? false,
|
|
1842
|
+
volume: raw?.volume ?? .5,
|
|
1843
|
+
style: raw?.style ?? "major",
|
|
1844
|
+
onlyCurrentSession: raw?.onlyCurrentSession ?? true
|
|
1845
|
+
});
|
|
1692
1846
|
const keyOf = (selection) => {
|
|
1693
1847
|
if (selection === null) return void 0;
|
|
1694
1848
|
return `${selection.provider}/${selection.model}`;
|
|
@@ -1798,18 +1952,15 @@ window.__ModuleLoader__.load({
|
|
|
1798
1952
|
bound?.setCheckUpdates(doc?.checkUpdates ?? true);
|
|
1799
1953
|
bound?.setDimWhenIdle(doc?.dimWhenIdle ?? false);
|
|
1800
1954
|
bound?.setReset(lastReset);
|
|
1801
|
-
const musicSettings =
|
|
1802
|
-
|
|
1803
|
-
volume: .5,
|
|
1804
|
-
style: "major",
|
|
1805
|
-
...doc?.music ?? {}
|
|
1806
|
-
};
|
|
1955
|
+
const musicSettings = musicDefaults(doc?.music);
|
|
1956
|
+
lastOnlyCurrentSession = musicSettings.onlyCurrentSession;
|
|
1807
1957
|
music.setEnabled(musicSettings.enabled);
|
|
1808
1958
|
music.setVolume(musicSettings.volume);
|
|
1809
1959
|
music.setStyle(musicSettings.style);
|
|
1810
1960
|
bound?.setMusicEnabled(musicSettings.enabled);
|
|
1811
1961
|
bound?.setMusicVolume(musicSettings.volume);
|
|
1812
1962
|
bound?.setMusicStyle(musicSettings.style);
|
|
1963
|
+
bound?.setMusicOnlyCurrentSession(musicSettings.onlyCurrentSession);
|
|
1813
1964
|
fetch("/token-quota", { headers: { accept: "application/json" } }).then((response) => {
|
|
1814
1965
|
if (!response.ok) {
|
|
1815
1966
|
bound?.setError(`quota route: ${String(response.status)}`);
|
|
@@ -1880,36 +2031,30 @@ window.__ModuleLoader__.load({
|
|
|
1880
2031
|
scope.set("dimWhenIdle", enabled);
|
|
1881
2032
|
};
|
|
1882
2033
|
const setMusicEnabled = (enabled) => {
|
|
1883
|
-
const
|
|
1884
|
-
|
|
1885
|
-
volume: .5,
|
|
1886
|
-
style: "major",
|
|
1887
|
-
...scope.getSnapshot().value?.music ?? {}
|
|
1888
|
-
};
|
|
2034
|
+
const doc = scope.getSnapshot().value;
|
|
2035
|
+
const current = musicDefaults(doc?.music);
|
|
1889
2036
|
current.enabled = enabled;
|
|
1890
2037
|
scope.set("music", current);
|
|
1891
2038
|
if (enabled) music.ensureStarted();
|
|
1892
2039
|
};
|
|
1893
2040
|
const setMusicVolume = (volume) => {
|
|
1894
|
-
const
|
|
1895
|
-
|
|
1896
|
-
volume: .5,
|
|
1897
|
-
style: "major",
|
|
1898
|
-
...scope.getSnapshot().value?.music ?? {}
|
|
1899
|
-
};
|
|
2041
|
+
const doc = scope.getSnapshot().value;
|
|
2042
|
+
const current = musicDefaults(doc?.music);
|
|
1900
2043
|
current.volume = Math.max(0, Math.min(1, volume));
|
|
1901
2044
|
scope.set("music", current);
|
|
1902
2045
|
};
|
|
1903
2046
|
const setMusicStyle = (style) => {
|
|
1904
|
-
const
|
|
1905
|
-
|
|
1906
|
-
volume: .5,
|
|
1907
|
-
style: "major",
|
|
1908
|
-
...scope.getSnapshot().value?.music ?? {}
|
|
1909
|
-
};
|
|
2047
|
+
const doc = scope.getSnapshot().value;
|
|
2048
|
+
const current = musicDefaults(doc?.music);
|
|
1910
2049
|
current.style = style;
|
|
1911
2050
|
scope.set("music", current);
|
|
1912
2051
|
};
|
|
2052
|
+
const setMusicOnlyCurrentSession = (only) => {
|
|
2053
|
+
const doc = scope.getSnapshot().value;
|
|
2054
|
+
const current = musicDefaults(doc?.music);
|
|
2055
|
+
current.onlyCurrentSession = only;
|
|
2056
|
+
scope.set("music", current);
|
|
2057
|
+
};
|
|
1913
2058
|
/** Ask the Host to check the registry right now, then refresh the snapshot. */
|
|
1914
2059
|
const checkUpdatesNow = () => {
|
|
1915
2060
|
bound?.setCheckingUpdates(true);
|
|
@@ -1996,7 +2141,8 @@ window.__ModuleLoader__.load({
|
|
|
1996
2141
|
setDimWhenIdle,
|
|
1997
2142
|
setMusicEnabled,
|
|
1998
2143
|
setMusicVolume,
|
|
1999
|
-
setMusicStyle
|
|
2144
|
+
setMusicStyle,
|
|
2145
|
+
setMusicOnlyCurrentSession
|
|
2000
2146
|
};
|
|
2001
2147
|
};
|
|
2002
2148
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|