@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.
Files changed (2) hide show
  1. package/lib/client.js +115 -36
  2. 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: (draft && draft.currency) !== undefined ? draft.currency : '',
939
- onChange: (e) => setDraft({ ...draft, currency: e.target.value }),
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: (draft && draft.usdRate) !== undefined ? draft.usdRate : '',
950
- onChange: (e) => setDraft({ ...draft, usdRate: e.target.value }),
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: (draft && draft.budgetThreshold) !== undefined ? draft.budgetThreshold : '',
964
- onChange: (e) => setDraft({ ...draft, budgetThreshold: e.target.value }),
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: (draft && draft.displayTimeZone) !== undefined ? draft.displayTimeZone : '',
973
- onChange: (e) => setDraft({ ...draft, displayTimeZone: e.target.value }),
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: (draft && draft.displayMode) || 'auto',
985
- onChange: (e) => setDraft({ ...draft, displayMode: e.target.value }),
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: draft ? Boolean(draft.alertBeforeSwitch) : true,
1005
- onChange: (e) => setDraft({ ...draft, alertBeforeSwitch: e.target.checked }),
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: (draft && draft.alertLeadMinutes) || '5',
1016
- onChange: (e) => setDraft({ ...draft, alertLeadMinutes: e.target.value }),
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: draft ? Boolean(draft.browserNotifications) : false,
1066
+ checked: Boolean(draft?.browserNotifications ?? false),
1033
1067
  onChange: (e) => {
1034
1068
  const checked = e.target.checked
1035
- setDraft({ ...draft, browserNotifications: checked })
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: draft ? Boolean(draft.useOpenRouter) : true,
1082
- onChange: (e) => setDraft({ ...draft, useOpenRouter: e.target.checked }),
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: (draft && draft.refreshHours) !== undefined ? draft.refreshHours : '',
1095
- onChange: (e) => setDraft({ ...draft, refreshHours: e.target.value }),
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.29",
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",