dsh-claude-style 0.3.2 → 0.4.0

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/index.js CHANGED
@@ -71,6 +71,9 @@ let namespaceState = 'pending'
71
71
  /** Accepted values, mirrored by src/constants.js. */
72
72
  const BRANDS = ['off', 'claude', 'anthropic']
73
73
  const COMPOSER_SCOPES = ['off', 'hero', 'conversation', 'all']
74
+ const AUTO_POPOVER_SCOPES = ['off', 'account', 'all']
75
+ /** The official service id, mirrored by src/constants.js. */
76
+ const MODEL_OFFICIAL_GROUP = 'deepseek-official'
74
77
  /** Languages the account-hold easter egg's page can be written in. */
75
78
  const BAN_LOCALES = ['en', 'zh']
76
79
  const USERNAME_MAX = 64
@@ -79,8 +82,10 @@ const USERNAME_MAX = 64
79
82
  const PREFS_DEFAULT = Object.freeze({
80
83
  brand: 'claude',
81
84
  collapseFooter: true,
82
- autoPopover: true,
85
+ autoPopover: 'all',
83
86
  composerScope: 'all',
87
+ modelPicker: true,
88
+ quickProviders: [],
84
89
  username: '',
85
90
  banLocale: 'en',
86
91
  })
@@ -126,21 +131,52 @@ function buildPrefsSchema(Schema) {
126
131
  collapseFooter: Schema.any().default(PREFS_DEFAULT.collapseFooter),
127
132
  autoPopover: Schema.any().default(PREFS_DEFAULT.autoPopover),
128
133
  composerScope: Schema.any().default(PREFS_DEFAULT.composerScope),
134
+ modelPicker: Schema.any().default(PREFS_DEFAULT.modelPicker),
135
+ quickProviders: Schema.any().default(PREFS_DEFAULT.quickProviders),
129
136
  username: Schema.any().default(PREFS_DEFAULT.username),
130
137
  banLocale: Schema.any().default(PREFS_DEFAULT.banLocale),
131
138
  })
132
139
  }
133
140
 
141
+ /**
142
+ * Clamp the hover-open preference. It used to be a boolean, and a value stored
143
+ * in that shape still has to land on a scope: `true` meant every popover,
144
+ * `false` meant click-only.
145
+ */
146
+ function normalizeAutoPopover(value) {
147
+ if (value === true) return 'all'
148
+ if (value === false) return 'off'
149
+ return AUTO_POPOVER_SCOPES.includes(value) ? value : PREFS_DEFAULT.autoPopover
150
+ }
151
+
152
+ /**
153
+ * The provider ids the picker's first level carries: strings, deduped, order
154
+ * kept. Ids rather than names so a catalog rename cannot drop the selection.
155
+ * The official service is the picker's default, not a choice, so a stored id
156
+ * for it is dropped — the first level shows it whenever nothing else is picked.
157
+ */
158
+ function normalizeQuickProviders(value) {
159
+ if (!Array.isArray(value)) return []
160
+ const out = []
161
+ for (const id of value) {
162
+ if (typeof id !== 'string' || id === '' || id === MODEL_OFFICIAL_GROUP || out.includes(id)) continue
163
+ out.push(id)
164
+ }
165
+ return out
166
+ }
167
+
134
168
  /** Clamp one stored section into the preference shape (the schema stores `any`). */
135
169
  function normalizePrefs(value) {
136
170
  const section = value !== null && typeof value === 'object' ? value : {}
137
171
  return {
138
172
  brand: BRANDS.includes(section.brand) ? section.brand : PREFS_DEFAULT.brand,
139
173
  collapseFooter: section.collapseFooter !== false,
140
- autoPopover: section.autoPopover !== false,
174
+ autoPopover: normalizeAutoPopover(section.autoPopover),
141
175
  composerScope: COMPOSER_SCOPES.includes(section.composerScope)
142
176
  ? section.composerScope
143
177
  : PREFS_DEFAULT.composerScope,
178
+ modelPicker: section.modelPicker !== false,
179
+ quickProviders: normalizeQuickProviders(section.quickProviders),
144
180
  username: typeof section.username === 'string'
145
181
  ? section.username.trim().slice(0, USERNAME_MAX)
146
182
  : PREFS_DEFAULT.username,
@@ -350,7 +386,9 @@ export function apply(ctx) {
350
386
  const patch = {}
351
387
  if (BRANDS.includes(body.brand)) patch.brand = body.brand
352
388
  if (typeof body.collapseFooter === 'boolean') patch.collapseFooter = body.collapseFooter
353
- if (typeof body.autoPopover === 'boolean') patch.autoPopover = body.autoPopover
389
+ if (typeof body.autoPopover === 'boolean' || AUTO_POPOVER_SCOPES.includes(body.autoPopover)) patch.autoPopover = body.autoPopover
390
+ if (typeof body.modelPicker === 'boolean') patch.modelPicker = body.modelPicker
391
+ if (Array.isArray(body.quickProviders)) patch.quickProviders = normalizeQuickProviders(body.quickProviders)
354
392
  if (COMPOSER_SCOPES.includes(body.composerScope)) patch.composerScope = body.composerScope
355
393
  if (typeof body.username === 'string') patch.username = body.username.trim().slice(0, USERNAME_MAX)
356
394
  if (BAN_LOCALES.includes(body.banLocale)) patch.banLocale = body.banLocale
@@ -57,8 +57,20 @@
57
57
  "en": "Open popovers on hover"
58
58
  },
59
59
  "autoPopoverDesc": {
60
- "zh": "鼠标移入即展开账户、模型和权限弹层。",
61
- "en": "Hover opens the account, model, and permission popovers. Off switches them to click-to-open."
60
+ "zh": "哪些弹层悬停即展开。「仅账号区」只让侧栏账户弹层这样;「全部」再加上权限、模型、会话统计与首页的目录/预设。",
61
+ "en": "Which popovers hover opens. \"Account only\" keeps it to the sidebar account popover; \"All\" adds the permission, model, session-stats and the home page's workspace and preset pickers."
62
+ },
63
+ "autoPopoverOff": {
64
+ "zh": "关闭",
65
+ "en": "Off"
66
+ },
67
+ "autoPopoverAccount": {
68
+ "zh": "仅账号区",
69
+ "en": "Account only"
70
+ },
71
+ "autoPopoverAll": {
72
+ "zh": "全部",
73
+ "en": "All"
62
74
  },
63
75
  "composerTitle": {
64
76
  "zh": "对话框样式改动范围",
@@ -68,6 +80,38 @@
68
80
  "zh": "重绘输入区,可选新会话页、对话内或两者。",
69
81
  "en": "Which input area the skin restyles: the new-conversation page, the conversation, or both. The hero brand mark and headline are unaffected."
70
82
  },
83
+ "pickerTitle": {
84
+ "zh": "重做模型选择器",
85
+ "en": "Redraw the model picker"
86
+ },
87
+ "pickerDesc": {
88
+ "zh": "把输入框的模型席位换成 Claude 风格的两级弹层。关闭后交回系统自己的模型菜单,输入区其余改动不受影响。",
89
+ "en": "Replace the composer's model seat with the two-level Claude-style menu. Off hands the model menu back to the host; the rest of the composer restyle is unaffected."
90
+ },
91
+ "quickTitle": {
92
+ "zh": "快捷供应商",
93
+ "en": "Quick providers"
94
+ },
95
+ "quickDesc": {
96
+ "zh": "官方服务始终列在一级弹层最前面,不在这份清单里;勾选的供应商跟在它后面,各自的模型之间用一条横线分隔。已被移除的供应商会留在清单里,带「已移除」标记,取消勾选即可清除。",
97
+ "en": "The official service always leads the picker's first level and is not listed here; picked providers follow it, one rule between providers. A provider removed from the catalog stays in the list, marked \"Removed\", and can be unchecked to clear it."
98
+ },
99
+ "quickNone": {
100
+ "zh": "未选择",
101
+ "en": "None"
102
+ },
103
+ "quickCount": {
104
+ "zh": "{count} 个供应商",
105
+ "en": "{count} providers"
106
+ },
107
+ "quickLoading": {
108
+ "zh": "正在加载模型目录…",
109
+ "en": "Loading providers…"
110
+ },
111
+ "quickRemoved": {
112
+ "zh": "已移除",
113
+ "en": "Removed"
114
+ },
71
115
  "usernameTitle": {
72
116
  "zh": "用户名",
73
117
  "en": "Username"
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-claude-style",
3
3
  "description": "Claude Code Desktop theme for DeepSeek Harness (dsh) web GUI — visual & interaction overhaul",
4
- "version": "0.3.2",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "scripts": {