@goodandready/dsh-cost-meter 0.8.29 → 0.8.30
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 +115 -36
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -42,6 +42,8 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
'badge.deepseek': 'DeepSeek tariff: active',
|
|
43
43
|
'section.general': 'General Parameters',
|
|
44
44
|
'section.general_desc': 'Display currency, conversion rate, budget threshold and time zone configuration',
|
|
45
|
+
'section.visibility': 'Widget Visibility & Privacy',
|
|
46
|
+
'section.visibility_desc': 'Configure widget visibility in the conversation header and messages, or enable privacy mode',
|
|
45
47
|
'section.openrouter': 'OpenRouter Pricing Catalog',
|
|
46
48
|
'section.openrouter_desc': 'Public catalog for dynamic model pricing and discount windows',
|
|
47
49
|
'section.diagnostics': 'Diagnostics & Status',
|
|
@@ -179,6 +181,8 @@ window.__ModuleLoader__.load({
|
|
|
179
181
|
'badge.deepseek': 'DeepSeek 费率:生效中',
|
|
180
182
|
'section.general': '基础参数',
|
|
181
183
|
'section.general_desc': '显示货币、汇率转换、预算阈值与时区设置',
|
|
184
|
+
'section.visibility': '组件显示与隐私设置',
|
|
185
|
+
'section.visibility_desc': '配置对话顶部与消息栏组件的显示状态,或开启隐私保护模式',
|
|
182
186
|
'section.openrouter': 'OpenRouter 定价目录',
|
|
183
187
|
'section.openrouter_desc': '用于自动获取模型价格与折扣窗口的公开价格目录',
|
|
184
188
|
'section.diagnostics': '诊断与状态',
|
|
@@ -741,24 +745,30 @@ window.__ModuleLoader__.load({
|
|
|
741
745
|
const status = (snapshot && snapshot.status) || 'loading'
|
|
742
746
|
const stored = (snapshot && snapshot.value) || {}
|
|
743
747
|
|
|
748
|
+
const initDraft = React.useCallback((s) => {
|
|
749
|
+
const val = s || {}
|
|
750
|
+
return {
|
|
751
|
+
currency: val.currency !== undefined ? String(val.currency) : '$',
|
|
752
|
+
usdRate: val.usdRate !== undefined ? String(val.usdRate) : '1',
|
|
753
|
+
budgetThreshold: val.budgetThreshold !== undefined ? String(val.budgetThreshold) : '0',
|
|
754
|
+
displayTimeZone: val.displayTimeZone !== undefined ? String(val.displayTimeZone) : 'Europe/Moscow',
|
|
755
|
+
displayMode: val.displayMode !== undefined ? String(val.displayMode) : 'auto',
|
|
756
|
+
alertBeforeSwitch: val.alertBeforeSwitch !== undefined ? Boolean(val.alertBeforeSwitch) : true,
|
|
757
|
+
alertLeadMinutes: val.alertLeadMinutes !== undefined ? String(val.alertLeadMinutes) : '5',
|
|
758
|
+
browserNotifications: val.browserNotifications !== undefined ? Boolean(val.browserNotifications) : false,
|
|
759
|
+
privacyMode: val.privacyMode !== undefined ? Boolean(val.privacyMode) : false,
|
|
760
|
+
showHeaderChip: val.showHeaderChip !== undefined ? Boolean(val.showHeaderChip) : true,
|
|
761
|
+
showMessageAction: val.showMessageAction !== undefined ? Boolean(val.showMessageAction) : true,
|
|
762
|
+
useOpenRouter: val.useOpenRouter !== undefined ? Boolean(val.useOpenRouter) : true,
|
|
763
|
+
refreshHours: val.refreshHours !== undefined ? String(val.refreshHours) : '24',
|
|
764
|
+
}
|
|
765
|
+
}, [])
|
|
766
|
+
|
|
744
767
|
React.useEffect(() => {
|
|
745
768
|
if (status === 'ready' && draft === null) {
|
|
746
|
-
setDraft(
|
|
747
|
-
currency: stored.currency !== undefined ? String(stored.currency) : '$',
|
|
748
|
-
usdRate: stored.usdRate !== undefined ? String(stored.usdRate) : '1',
|
|
749
|
-
budgetThreshold: stored.budgetThreshold !== undefined ? String(stored.budgetThreshold) : '0',
|
|
750
|
-
displayTimeZone: stored.displayTimeZone !== undefined ? String(stored.displayTimeZone) : 'Europe/Moscow',
|
|
751
|
-
alertBeforeSwitch: stored.alertBeforeSwitch !== undefined ? Boolean(stored.alertBeforeSwitch) : true,
|
|
752
|
-
alertLeadMinutes: stored.alertLeadMinutes !== undefined ? String(stored.alertLeadMinutes) : '5',
|
|
753
|
-
browserNotifications: stored.browserNotifications !== undefined ? Boolean(stored.browserNotifications) : false,
|
|
754
|
-
privacyMode: stored.privacyMode !== undefined ? Boolean(stored.privacyMode) : false,
|
|
755
|
-
showHeaderChip: stored.showHeaderChip !== undefined ? Boolean(stored.showHeaderChip) : true,
|
|
756
|
-
showMessageAction: stored.showMessageAction !== undefined ? Boolean(stored.showMessageAction) : true,
|
|
757
|
-
useOpenRouter: stored.useOpenRouter !== undefined ? Boolean(stored.useOpenRouter) : true,
|
|
758
|
-
refreshHours: stored.refreshHours !== undefined ? String(stored.refreshHours) : '24',
|
|
759
|
-
})
|
|
769
|
+
setDraft(initDraft(stored))
|
|
760
770
|
}
|
|
761
|
-
}, [status, stored, draft])
|
|
771
|
+
}, [status, stored, draft, initDraft])
|
|
762
772
|
|
|
763
773
|
const tzValid = draft ? isValidTimeZone(draft.displayTimeZone) : true
|
|
764
774
|
const rateValid = draft ? (!isNaN(Number(draft.usdRate)) && Number(draft.usdRate) > 0) : true
|
|
@@ -829,6 +839,30 @@ window.__ModuleLoader__.load({
|
|
|
829
839
|
} catch (e) { broken.push('displayTimeZone: ' + (e && e.message || String(e))) }
|
|
830
840
|
}
|
|
831
841
|
|
|
842
|
+
try {
|
|
843
|
+
await scope.set('alertBeforeSwitch', Boolean(draft.alertBeforeSwitch))
|
|
844
|
+
} catch (e) { broken.push('alertBeforeSwitch: ' + (e && e.message || String(e))) }
|
|
845
|
+
|
|
846
|
+
try {
|
|
847
|
+
await scope.set('alertLeadMinutes', String(draft.alertLeadMinutes || '5'))
|
|
848
|
+
} catch (e) { broken.push('alertLeadMinutes: ' + (e && e.message || String(e))) }
|
|
849
|
+
|
|
850
|
+
try {
|
|
851
|
+
await scope.set('browserNotifications', Boolean(draft.browserNotifications))
|
|
852
|
+
} catch (e) { broken.push('browserNotifications: ' + (e && e.message || String(e))) }
|
|
853
|
+
|
|
854
|
+
try {
|
|
855
|
+
await scope.set('privacyMode', Boolean(draft.privacyMode))
|
|
856
|
+
} catch (e) { broken.push('privacyMode: ' + (e && e.message || String(e))) }
|
|
857
|
+
|
|
858
|
+
try {
|
|
859
|
+
await scope.set('showHeaderChip', Boolean(draft.showHeaderChip))
|
|
860
|
+
} catch (e) { broken.push('showHeaderChip: ' + (e && e.message || String(e))) }
|
|
861
|
+
|
|
862
|
+
try {
|
|
863
|
+
await scope.set('showMessageAction', Boolean(draft.showMessageAction))
|
|
864
|
+
} catch (e) { broken.push('showMessageAction: ' + (e && e.message || String(e))) }
|
|
865
|
+
|
|
832
866
|
try {
|
|
833
867
|
await scope.set('useOpenRouter', Boolean(draft.useOpenRouter))
|
|
834
868
|
} catch (e) { broken.push('useOpenRouter: ' + (e && e.message || String(e))) }
|
|
@@ -889,7 +923,7 @@ window.__ModuleLoader__.load({
|
|
|
889
923
|
React.createElement(Chevron, { open }),
|
|
890
924
|
),
|
|
891
925
|
(page || open) ? React.createElement('div', { className: 'dcm-body' },
|
|
892
|
-
status === 'loading'
|
|
926
|
+
(status === 'loading' || !draft)
|
|
893
927
|
? React.createElement('div', { className: 'dcm-hint', style: { padding: '12px 0' } }, t('settings.loading'))
|
|
894
928
|
: status !== 'ready'
|
|
895
929
|
? React.createElement('div', { className: 'dcm-alert dcm-alert-err' }, t('settings.unavailable'))
|
|
@@ -935,8 +969,8 @@ window.__ModuleLoader__.load({
|
|
|
935
969
|
React.createElement('label', { className: 'dcm-label' }, t('field.currency')),
|
|
936
970
|
React.createElement('input', {
|
|
937
971
|
className: 'dcm-input',
|
|
938
|
-
value:
|
|
939
|
-
onChange: (e) => setDraft({ ...
|
|
972
|
+
value: draft?.currency ?? '',
|
|
973
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), currency: v })) },
|
|
940
974
|
}),
|
|
941
975
|
React.createElement('span', { className: 'dcm-hint' }, t('hint.currency')),
|
|
942
976
|
),
|
|
@@ -946,8 +980,8 @@ window.__ModuleLoader__.load({
|
|
|
946
980
|
className: 'dcm-input' + (rateValid ? '' : ' dcm-input-err'),
|
|
947
981
|
type: 'number',
|
|
948
982
|
step: 'any',
|
|
949
|
-
value:
|
|
950
|
-
onChange: (e) => setDraft({ ...
|
|
983
|
+
value: draft?.usdRate ?? '',
|
|
984
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), usdRate: v })) },
|
|
951
985
|
}),
|
|
952
986
|
React.createElement('span', { className: 'dcm-hint' }, t('hint.usdRate')),
|
|
953
987
|
),
|
|
@@ -960,8 +994,8 @@ window.__ModuleLoader__.load({
|
|
|
960
994
|
type: 'number',
|
|
961
995
|
step: 'any',
|
|
962
996
|
min: 0,
|
|
963
|
-
value:
|
|
964
|
-
onChange: (e) => setDraft({ ...
|
|
997
|
+
value: draft?.budgetThreshold ?? '',
|
|
998
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), budgetThreshold: v })) },
|
|
965
999
|
}),
|
|
966
1000
|
React.createElement('span', { className: 'dcm-hint' }, t('hint.budgetThreshold')),
|
|
967
1001
|
),
|
|
@@ -969,8 +1003,8 @@ window.__ModuleLoader__.load({
|
|
|
969
1003
|
React.createElement('label', { className: 'dcm-label' }, t('field.displayTimeZone')),
|
|
970
1004
|
React.createElement('input', {
|
|
971
1005
|
className: 'dcm-input' + (tzValid ? '' : ' dcm-input-err'),
|
|
972
|
-
value:
|
|
973
|
-
onChange: (e) => setDraft({ ...
|
|
1006
|
+
value: draft?.displayTimeZone ?? '',
|
|
1007
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), displayTimeZone: v })) },
|
|
974
1008
|
}),
|
|
975
1009
|
React.createElement('span', { className: 'dcm-hint' },
|
|
976
1010
|
tzValid ? t('hint.displayTimeZone') : t('tz.invalid')
|
|
@@ -981,8 +1015,8 @@ window.__ModuleLoader__.load({
|
|
|
981
1015
|
React.createElement('label', { className: 'dcm-label' }, t('field.displayMode')),
|
|
982
1016
|
React.createElement('select', {
|
|
983
1017
|
className: 'dcm-input',
|
|
984
|
-
value:
|
|
985
|
-
onChange: (e) => setDraft({ ...
|
|
1018
|
+
value: draft?.displayMode || 'auto',
|
|
1019
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), displayMode: v })) },
|
|
986
1020
|
}, [
|
|
987
1021
|
React.createElement('option', { key: 'auto', value: 'auto' }, t('mode.auto')),
|
|
988
1022
|
React.createElement('option', { key: 'full', value: 'full' }, t('mode.full')),
|
|
@@ -1001,8 +1035,8 @@ window.__ModuleLoader__.load({
|
|
|
1001
1035
|
id: 'dcm-alert-switch',
|
|
1002
1036
|
type: 'checkbox',
|
|
1003
1037
|
className: 'dcm-check',
|
|
1004
|
-
checked:
|
|
1005
|
-
onChange: (e) => setDraft({ ...
|
|
1038
|
+
checked: Boolean(draft?.alertBeforeSwitch ?? true),
|
|
1039
|
+
onChange: (e) => { const c = e.target.checked; setDraft((d) => ({ ...(d || {}), alertBeforeSwitch: c })) },
|
|
1006
1040
|
}),
|
|
1007
1041
|
React.createElement('label', { htmlFor: 'dcm-alert-switch', className: 'dcm-label', style: { cursor: 'pointer' } }, t('field.alertBeforeSwitch')),
|
|
1008
1042
|
),
|
|
@@ -1012,8 +1046,8 @@ window.__ModuleLoader__.load({
|
|
|
1012
1046
|
React.createElement('label', { className: 'dcm-label' }, t('field.alertLeadMinutes')),
|
|
1013
1047
|
React.createElement('select', {
|
|
1014
1048
|
className: 'dcm-input',
|
|
1015
|
-
value:
|
|
1016
|
-
onChange: (e) => setDraft({ ...
|
|
1049
|
+
value: draft?.alertLeadMinutes || '5',
|
|
1050
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), alertLeadMinutes: v })) },
|
|
1017
1051
|
}, [
|
|
1018
1052
|
React.createElement('option', { key: '1', value: '1' }, '1 min'),
|
|
1019
1053
|
React.createElement('option', { key: '2', value: '2' }, '2 min'),
|
|
@@ -1029,10 +1063,10 @@ window.__ModuleLoader__.load({
|
|
|
1029
1063
|
id: 'dcm-browser-notif',
|
|
1030
1064
|
type: 'checkbox',
|
|
1031
1065
|
className: 'dcm-check',
|
|
1032
|
-
checked:
|
|
1066
|
+
checked: Boolean(draft?.browserNotifications ?? false),
|
|
1033
1067
|
onChange: (e) => {
|
|
1034
1068
|
const checked = e.target.checked
|
|
1035
|
-
setDraft({ ...
|
|
1069
|
+
setDraft((d) => ({ ...(d || {}), browserNotifications: checked }))
|
|
1036
1070
|
if (checked && typeof Notification !== 'undefined' && Notification.permission !== 'granted') {
|
|
1037
1071
|
Notification.requestPermission()
|
|
1038
1072
|
}
|
|
@@ -1058,6 +1092,51 @@ window.__ModuleLoader__.load({
|
|
|
1058
1092
|
),
|
|
1059
1093
|
),
|
|
1060
1094
|
|
|
1095
|
+
// Section: Widget Visibility & Privacy Mode (Issues #121, #165, #168)
|
|
1096
|
+
React.createElement('div', { className: 'dcm-section-card' },
|
|
1097
|
+
React.createElement('div', { className: 'dcm-section-title' }, t('section.visibility')),
|
|
1098
|
+
React.createElement('div', { className: 'dcm-section-desc' }, t('section.visibility_desc')),
|
|
1099
|
+
React.createElement('div', { className: 'dcm-row-check' },
|
|
1100
|
+
React.createElement('input', {
|
|
1101
|
+
id: 'dcm-show-header-chip',
|
|
1102
|
+
type: 'checkbox',
|
|
1103
|
+
className: 'dcm-check',
|
|
1104
|
+
checked: Boolean(draft?.showHeaderChip ?? true),
|
|
1105
|
+
onChange: (e) => { const c = e.target.checked; setDraft((d) => ({ ...(d || {}), showHeaderChip: c })) },
|
|
1106
|
+
}),
|
|
1107
|
+
React.createElement('label', { htmlFor: 'dcm-show-header-chip', className: 'dcm-label', style: { cursor: 'pointer' } }, t('field.showHeaderChip')),
|
|
1108
|
+
),
|
|
1109
|
+
React.createElement('span', { className: 'dcm-hint', style: { marginTop: -6 } }, t('hint.showHeaderChip')),
|
|
1110
|
+
|
|
1111
|
+
React.createElement('div', { className: 'dcm-row-check', style: { marginTop: 10 } },
|
|
1112
|
+
React.createElement('input', {
|
|
1113
|
+
id: 'dcm-show-msg-action',
|
|
1114
|
+
type: 'checkbox',
|
|
1115
|
+
className: 'dcm-check',
|
|
1116
|
+
checked: Boolean(draft?.showMessageAction ?? true),
|
|
1117
|
+
onChange: (e) => { const c = e.target.checked; setDraft((d) => ({ ...(d || {}), showMessageAction: c })) },
|
|
1118
|
+
}),
|
|
1119
|
+
React.createElement('label', { htmlFor: 'dcm-show-msg-action', className: 'dcm-label', style: { cursor: 'pointer' } }, t('field.showMessageAction')),
|
|
1120
|
+
),
|
|
1121
|
+
React.createElement('span', { className: 'dcm-hint', style: { marginTop: -6 } }, t('hint.showMessageAction')),
|
|
1122
|
+
|
|
1123
|
+
React.createElement('div', { className: 'dcm-row-check', style: { marginTop: 10 } },
|
|
1124
|
+
React.createElement('input', {
|
|
1125
|
+
id: 'dcm-privacy-mode',
|
|
1126
|
+
type: 'checkbox',
|
|
1127
|
+
className: 'dcm-check',
|
|
1128
|
+
checked: Boolean(draft?.privacyMode ?? false),
|
|
1129
|
+
onChange: (e) => { const c = e.target.checked; setDraft((d) => ({ ...(d || {}), privacyMode: c })) },
|
|
1130
|
+
}),
|
|
1131
|
+
React.createElement('label', { htmlFor: 'dcm-privacy-mode', className: 'dcm-label', style: { cursor: 'pointer' } }, t('field.privacyMode')),
|
|
1132
|
+
),
|
|
1133
|
+
React.createElement('span', { className: 'dcm-hint', style: { marginTop: -6 } }, t('hint.privacyMode')),
|
|
1134
|
+
|
|
1135
|
+
(!draft?.showHeaderChip || !draft?.showMessageAction)
|
|
1136
|
+
? React.createElement('div', { className: 'dcm-alert dcm-alert-warn', style: { marginTop: 10, fontSize: 12 } }, t('notice.hidden_access'))
|
|
1137
|
+
: null,
|
|
1138
|
+
),
|
|
1139
|
+
|
|
1061
1140
|
// Section 2: OpenRouter Pricing Catalog
|
|
1062
1141
|
React.createElement('div', { className: 'dcm-section-card' },
|
|
1063
1142
|
React.createElement('div', { className: 'dcm-section-title' },
|
|
@@ -1078,8 +1157,8 @@ window.__ModuleLoader__.load({
|
|
|
1078
1157
|
id: 'dcm-use-openrouter',
|
|
1079
1158
|
type: 'checkbox',
|
|
1080
1159
|
className: 'dcm-check',
|
|
1081
|
-
checked:
|
|
1082
|
-
onChange: (e) => setDraft({ ...
|
|
1160
|
+
checked: Boolean(draft?.useOpenRouter ?? true),
|
|
1161
|
+
onChange: (e) => { const c = e.target.checked; setDraft((d) => ({ ...(d || {}), useOpenRouter: c })) },
|
|
1083
1162
|
}),
|
|
1084
1163
|
React.createElement('label', { htmlFor: 'dcm-use-openrouter', className: 'dcm-label', style: { cursor: 'pointer' } }, t('field.useOpenRouter')),
|
|
1085
1164
|
),
|
|
@@ -1091,8 +1170,8 @@ window.__ModuleLoader__.load({
|
|
|
1091
1170
|
type: 'number',
|
|
1092
1171
|
min: 1,
|
|
1093
1172
|
max: 168,
|
|
1094
|
-
value:
|
|
1095
|
-
onChange: (e) => setDraft({ ...
|
|
1173
|
+
value: draft?.refreshHours ?? '',
|
|
1174
|
+
onChange: (e) => { const v = e.target.value; setDraft((d) => ({ ...(d || {}), refreshHours: v })) },
|
|
1096
1175
|
}),
|
|
1097
1176
|
React.createElement('span', { className: 'dcm-hint' }, t('hint.refreshHours')),
|
|
1098
1177
|
),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-cost-meter",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.30",
|
|
4
4
|
"description": "DeepSeek Harness plugin: a session cost chip in the conversation header - live token usage by bucket, peak/off-peak tariff, and a countdown to the next tariff switch.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|