@cowwo/dsh-icon-custom 0.2.1 → 0.5.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/README.md +10 -0
- package/README.zh.md +10 -0
- package/client/client.js +97 -3
- package/lib/index.js +22 -5
- package/lib/typert.host.js +44 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,16 @@ When the site is installed as an app, the browser's install dialog picks the cus
|
|
|
29
29
|
|
|
30
30
|

|
|
31
31
|
|
|
32
|
+
## Page logo mark
|
|
33
|
+
|
|
34
|
+
Tick **同时替换页面 Logo 图标** to also replace the whale mark at the top-left of the DSH page — the mark beside the `deepseek HARNESS` brand name. The brand **text is untouched**; the mark falls back to the official whale while the option is off. The change applies instantly and persists across reloads/restarts.
|
|
35
|
+
|
|
36
|
+
Notes:
|
|
37
|
+
|
|
38
|
+
- The option is **off by default**.
|
|
39
|
+
- It can be toggled at any time after upload — no re-upload needed.
|
|
40
|
+
- This only affects the sidebar brand mark (the one next to `deepseek HARNESS`); PWA/installed-app icons are a separate option above.
|
|
41
|
+
|
|
32
42
|
Notes:
|
|
33
43
|
|
|
34
44
|
- The option is **off by default**: without it, behavior is identical to before and the platform manifest is served verbatim.
|
package/README.zh.md
CHANGED
|
@@ -29,6 +29,16 @@ dsh plugin --profile web add dsh-icon-custom
|
|
|
29
29
|
|
|
30
30
|

|
|
31
31
|
|
|
32
|
+
## 页面 Logo 图标
|
|
33
|
+
|
|
34
|
+
勾选**同时替换页面 Logo 图标**,网页左上角品牌 logo 里的**鲸鱼图标**就会换成你上传的这张——`deepseek HARNESS` 文字不受影响。选项关闭时显示官方鲸鱼;改动立即生效,刷新/重启都保留。
|
|
35
|
+
|
|
36
|
+
说明:
|
|
37
|
+
|
|
38
|
+
- 该选项**默认不勾选**。
|
|
39
|
+
- 上传后随时可以勾选/取消,不用重新上传文件。
|
|
40
|
+
- 只影响侧边栏的品牌图标(deepseek HARNESS 旁边那只);PWA 安装图标是上面的另一个选项。
|
|
41
|
+
|
|
32
42
|
说明:
|
|
33
43
|
|
|
34
44
|
- 该选项**默认不勾选**:不勾选时行为与之前完全一致,平台 manifest 原样透传。
|
package/client/client.js
CHANGED
|
@@ -28,6 +28,9 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
pwa: "同时替换 PWA 安装图标",
|
|
29
29
|
pwaHint: "安装为应用(添加到主屏幕)后,桌面上的图标也换成这张;已安装的应用可能需要重新安装才会更新。",
|
|
30
30
|
successPwa: "PWA 图标设置已更新",
|
|
31
|
+
logo: "同时替换页面 Logo 图标",
|
|
32
|
+
logoHint: "把网页左上角品牌 logo 里的鲸鱼图标换成这张(不影响 deepseek HARNESS 文字)。",
|
|
33
|
+
successLogo: "页面 Logo 已更新",
|
|
31
34
|
fail: "操作失败",
|
|
32
35
|
noFile: "请先选择图标文件"
|
|
33
36
|
};
|
|
@@ -50,6 +53,9 @@ window.__ModuleLoader__.load({
|
|
|
50
53
|
pwa: "Also replace the PWA app icon",
|
|
51
54
|
pwaHint: "The icon used after installing the site as an app (add to home screen). Already-installed apps may need a reinstall to pick it up.",
|
|
52
55
|
successPwa: "PWA icon setting updated",
|
|
56
|
+
logo: "Also replace the page logo mark",
|
|
57
|
+
logoHint: "Replaces the whale mark beside the brand name in the top-left (the deepseek HARNESS text stays).",
|
|
58
|
+
successLogo: "Page logo updated",
|
|
53
59
|
fail: "Operation failed",
|
|
54
60
|
noFile: "Choose a file first"
|
|
55
61
|
};
|
|
@@ -72,14 +78,60 @@ window.__ModuleLoader__.load({
|
|
|
72
78
|
return new Date(ms).toLocaleString();
|
|
73
79
|
} catch { return "—"; }
|
|
74
80
|
}
|
|
81
|
+
// In-package brand-mark bridge: the settings section pushes the current
|
|
82
|
+
// logo state here; the sidebar brand-mark component subscribes to it.
|
|
83
|
+
// Both live in this same module — no RPC loop, no cross-package events.
|
|
84
|
+
const logoListeners = new Set();
|
|
85
|
+
function emitLogo(value) {
|
|
86
|
+
logoListeners.forEach((fn) => { try { fn(value); } catch {} });
|
|
87
|
+
}
|
|
88
|
+
function subscribeLogo(fn) {
|
|
89
|
+
logoListeners.add(fn);
|
|
90
|
+
return () => { logoListeners.delete(fn); };
|
|
91
|
+
}
|
|
92
|
+
function syncLogo(status) {
|
|
93
|
+
emitLogo({
|
|
94
|
+
enabled: status !== null && typeof status === "object" && status.active === true && status.logo === true,
|
|
95
|
+
rev: status && status.rev ? status.rev : null
|
|
96
|
+
});
|
|
97
|
+
}
|
|
75
98
|
//#endregion
|
|
76
99
|
|
|
77
|
-
//#region
|
|
100
|
+
//#region components
|
|
101
|
+
/**
|
|
102
|
+
* Sidebar brand-mark occupant (registered on the official
|
|
103
|
+
* `sidebar.brand.mark` single seat). Shows the custom icon when the
|
|
104
|
+
* logo option is on; otherwise falls back to the platform favicon mark
|
|
105
|
+
* (/favicon.svg is the same official whale). The brand NAME text is not
|
|
106
|
+
* touched — this seat is only the mark.
|
|
107
|
+
*/
|
|
108
|
+
function BrandMark(props) {
|
|
109
|
+
const [state, setState] = React.useState(null);
|
|
110
|
+
React.useEffect(() => {
|
|
111
|
+
const unsubscribe = subscribeLogo(setState);
|
|
112
|
+
// Pull the current state on mount too, so the mark is correct
|
|
113
|
+
// even when the settings panel was never opened this session.
|
|
114
|
+
props.rpc.call("/api", "iconCustom/getStatus", { args: {} })
|
|
115
|
+
.then((resp) => { if (resp && resp.ok === true) syncLogo(resp.value); })
|
|
116
|
+
.catch(() => {});
|
|
117
|
+
return unsubscribe;
|
|
118
|
+
}, [props.rpc]);
|
|
119
|
+
const size = (props && props.size) || 24;
|
|
120
|
+
const custom = state && state.enabled === true && state.rev;
|
|
121
|
+
const style = { width: size, height: size, objectFit: "contain", display: "block" };
|
|
122
|
+
return React.createElement("img", {
|
|
123
|
+
src: custom ? `/icon-custom.svg?v=${state.rev}` : "/favicon.svg",
|
|
124
|
+
alt: "",
|
|
125
|
+
style
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
78
129
|
function FaviconSection(props) {
|
|
79
130
|
const t = props.t;
|
|
80
131
|
const rpc = props.rpc;
|
|
81
132
|
const [status, setStatus] = React.useState(null);
|
|
82
133
|
const [pwaEnabled, setPwaEnabled] = React.useState(false);
|
|
134
|
+
const [logoEnabled, setLogoEnabled] = React.useState(false);
|
|
83
135
|
const [busy, setBusy] = React.useState(false);
|
|
84
136
|
const [error, setError] = React.useState("");
|
|
85
137
|
const [notice, setNotice] = React.useState("");
|
|
@@ -91,6 +143,8 @@ window.__ModuleLoader__.load({
|
|
|
91
143
|
if (resp && resp.ok === true) {
|
|
92
144
|
setStatus(resp.value);
|
|
93
145
|
setPwaEnabled(resp.value.pwa === true);
|
|
146
|
+
setLogoEnabled(resp.value.logo === true);
|
|
147
|
+
syncLogo(resp.value);
|
|
94
148
|
} else setError(reason(resp, t("fail")));
|
|
95
149
|
} catch (e) {
|
|
96
150
|
setError(String((e && e.message) || e || t("fail")));
|
|
@@ -147,13 +201,15 @@ window.__ModuleLoader__.load({
|
|
|
147
201
|
setBusy(true);
|
|
148
202
|
try {
|
|
149
203
|
const resp = await rpc.call("/api", "iconCustom/setIcon", {
|
|
150
|
-
args: { request: { mime: file.type || "image/svg+xml", name: file.name, pwa: pwaEnabled, data } }
|
|
204
|
+
args: { request: { mime: file.type || "image/svg+xml", name: file.name, pwa: pwaEnabled, logo: logoEnabled, data } }
|
|
151
205
|
});
|
|
152
206
|
if (resp && resp.ok === true) {
|
|
153
207
|
setStatus(resp.value);
|
|
154
208
|
setPwaEnabled(resp.value.pwa === true);
|
|
209
|
+
setLogoEnabled(resp.value.logo === true);
|
|
155
210
|
setNotice(t("successUpload"));
|
|
156
211
|
applyLive(resp.value);
|
|
212
|
+
syncLogo(resp.value);
|
|
157
213
|
} else {
|
|
158
214
|
setError(reason(resp, t("fail")));
|
|
159
215
|
}
|
|
@@ -165,7 +221,7 @@ window.__ModuleLoader__.load({
|
|
|
165
221
|
};
|
|
166
222
|
reader.onerror = () => setError(t("fail"));
|
|
167
223
|
reader.readAsDataURL(file);
|
|
168
|
-
}, [rpc, t, pwaEnabled, applyLive]);
|
|
224
|
+
}, [rpc, t, pwaEnabled, logoEnabled, applyLive]);
|
|
169
225
|
|
|
170
226
|
const onReset = React.useCallback(async () => {
|
|
171
227
|
setError("");
|
|
@@ -176,8 +232,10 @@ window.__ModuleLoader__.load({
|
|
|
176
232
|
if (resp && resp.ok === true) {
|
|
177
233
|
setStatus(resp.value);
|
|
178
234
|
setPwaEnabled(false);
|
|
235
|
+
setLogoEnabled(false);
|
|
179
236
|
setNotice(t("successReset"));
|
|
180
237
|
applyDefault();
|
|
238
|
+
syncLogo(resp.value);
|
|
181
239
|
} else {
|
|
182
240
|
setError(reason(resp, t("fail")));
|
|
183
241
|
}
|
|
@@ -214,6 +272,32 @@ window.__ModuleLoader__.load({
|
|
|
214
272
|
}
|
|
215
273
|
}, [rpc, t, applyLive]);
|
|
216
274
|
|
|
275
|
+
const onToggleLogo = React.useCallback(async (event) => {
|
|
276
|
+
const enabled = event.target.checked === true;
|
|
277
|
+
setError("");
|
|
278
|
+
setNotice("");
|
|
279
|
+
setBusy(true);
|
|
280
|
+
try {
|
|
281
|
+
const resp = await rpc.call("/api", "iconCustom/setLogo", {
|
|
282
|
+
args: { request: { enabled } }
|
|
283
|
+
});
|
|
284
|
+
if (resp && resp.ok === true) {
|
|
285
|
+
setStatus(resp.value);
|
|
286
|
+
setLogoEnabled(resp.value.logo === true);
|
|
287
|
+
setNotice(t("successLogo"));
|
|
288
|
+
syncLogo(resp.value);
|
|
289
|
+
} else {
|
|
290
|
+
setLogoEnabled(!enabled);
|
|
291
|
+
setError(reason(resp, t("fail")));
|
|
292
|
+
}
|
|
293
|
+
} catch (e) {
|
|
294
|
+
setLogoEnabled(!enabled);
|
|
295
|
+
setError(String((e && e.message) || e || t("fail")));
|
|
296
|
+
} finally {
|
|
297
|
+
setBusy(false);
|
|
298
|
+
}
|
|
299
|
+
}, [rpc, t]);
|
|
300
|
+
|
|
217
301
|
const active = status && status.active === true;
|
|
218
302
|
const previewSrc = active ? `/icon-custom.svg?v=${status.rev}` : "/favicon.svg";
|
|
219
303
|
|
|
@@ -258,6 +342,11 @@ window.__ModuleLoader__.load({
|
|
|
258
342
|
React.createElement("span", null, t("pwa"))
|
|
259
343
|
),
|
|
260
344
|
React.createElement("div", { style: style.hint }, t("pwaHint")),
|
|
345
|
+
React.createElement("label", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "10px 0 0", fontSize: 12, color: "var(--dsw-alias-label-primary)", cursor: busy ? "default" : "pointer" } },
|
|
346
|
+
React.createElement("input", { type: "checkbox", checked: logoEnabled, disabled: busy || !active, onChange: onToggleLogo, style: { cursor: busy ? "default" : "pointer" } }),
|
|
347
|
+
React.createElement("span", null, t("logo"))
|
|
348
|
+
),
|
|
349
|
+
React.createElement("div", { style: style.hint }, t("logoHint")),
|
|
261
350
|
React.createElement("div", { style: style.hint }, t("uploadHint")),
|
|
262
351
|
React.createElement("div", { style: style.hint }, t("resetHint")),
|
|
263
352
|
notice ? React.createElement("div", { style: style.notice }, notice) : null,
|
|
@@ -271,6 +360,11 @@ window.__ModuleLoader__.load({
|
|
|
271
360
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-icon-custom: dictionaries");
|
|
272
361
|
const t = ctx.locale.bind(NS);
|
|
273
362
|
const rpc = ctx.connection.rpc;
|
|
363
|
+
// Brand-mark seat: replaces only the whale mark (verified: a
|
|
364
|
+
// third-party registration wins over the official occupant and the
|
|
365
|
+
// official package's mark steps aside). The brand NAME text is a
|
|
366
|
+
// separate seat and is left untouched.
|
|
367
|
+
ctx.slots.inject("sidebar.brand.mark", () => ctx.slots.register({ name: "sidebar.brand.mark" }, (props) => React.createElement(BrandMark, { ...props, rpc })));
|
|
274
368
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
275
369
|
name: "settings.section",
|
|
276
370
|
id: "favicon",
|
package/lib/index.js
CHANGED
|
@@ -218,9 +218,9 @@ export class FaviconCustomService extends TypertRemoteService {
|
|
|
218
218
|
this.storageDir = join(resolveHome(), 'custom-favicon')
|
|
219
219
|
this.libraryDir = join(this.storageDir, 'library')
|
|
220
220
|
this.indexPath = join(this.storageDir, 'library.json')
|
|
221
|
-
// { activeId, icons: { [id]: { id, mime, ext, name, size, savedAt, rev, pwa } } }
|
|
221
|
+
// { activeId, icons: { [id]: { id, mime, ext, name, size, savedAt, rev, pwa, logo } } }
|
|
222
222
|
this.library = { activeId: null, icons: {} }
|
|
223
|
-
// The active icon's loaded state: { rev, mime, ext, name, size, savedAt, pwa, bytes } | null
|
|
223
|
+
// The active icon's loaded state: { rev, mime, ext, name, size, savedAt, pwa, logo, bytes } | null
|
|
224
224
|
this.current = null
|
|
225
225
|
// Platform PWA manifest (raw + parsed), read once at startup.
|
|
226
226
|
this.baseManifest = loadBaseManifest()
|
|
@@ -268,8 +268,8 @@ export class FaviconCustomService extends TypertRemoteService {
|
|
|
268
268
|
status() {
|
|
269
269
|
const c = this.current
|
|
270
270
|
return c === null
|
|
271
|
-
? { active: false, pwa: false, rev: null, mime: null, ext: null, name: null, size: null, savedAt: null }
|
|
272
|
-
: { active: true, pwa: c.pwa === true, rev: c.rev, mime: c.mime, ext: c.ext, name: c.name, size: c.size, savedAt: c.savedAt }
|
|
271
|
+
? { active: false, pwa: false, logo: false, rev: null, mime: null, ext: null, name: null, size: null, savedAt: null }
|
|
272
|
+
: { active: true, pwa: c.pwa === true, logo: c.logo === true, rev: c.rev, mime: c.mime, ext: c.ext, name: c.name, size: c.size, savedAt: c.savedAt }
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
/** typert endpoint: read current status. */
|
|
@@ -282,6 +282,8 @@ export class FaviconCustomService extends TypertRemoteService {
|
|
|
282
282
|
if (request === null || typeof request !== 'object') throw new Error('缺少图标参数')
|
|
283
283
|
// `pwa`: whether this icon also replaces the PWA install icon (opt-in).
|
|
284
284
|
const pwa = request.pwa === true
|
|
285
|
+
// `logo`: whether this icon also replaces the in-app sidebar brand mark (opt-in).
|
|
286
|
+
const logo = request.logo === true
|
|
285
287
|
const { name, data } = request
|
|
286
288
|
if (typeof data !== 'string' || data.length === 0) throw new Error('缺少图标数据')
|
|
287
289
|
const { bytes } = base64Body(data)
|
|
@@ -299,7 +301,7 @@ export class FaviconCustomService extends TypertRemoteService {
|
|
|
299
301
|
const rec = {
|
|
300
302
|
id,
|
|
301
303
|
mime, ext,
|
|
302
|
-
pwa,
|
|
304
|
+
pwa, logo,
|
|
303
305
|
name: typeof name === 'string' && name.trim().length > 0 ? name.trim().slice(0, 128) : null,
|
|
304
306
|
size: bytes.length, savedAt: now, rev
|
|
305
307
|
}
|
|
@@ -325,6 +327,21 @@ export class FaviconCustomService extends TypertRemoteService {
|
|
|
325
327
|
return this.status()
|
|
326
328
|
}
|
|
327
329
|
|
|
330
|
+
/** typert endpoint: toggle the brand-mark flag on the active icon (no re-upload). */
|
|
331
|
+
async setLogo(request) {
|
|
332
|
+
if (request === null || typeof request !== 'object' || typeof request.enabled !== 'boolean') {
|
|
333
|
+
throw new Error('缺少 enabled 参数')
|
|
334
|
+
}
|
|
335
|
+
const id = this.library.activeId
|
|
336
|
+
if (id === null || this.current === null) throw new Error('当前没有自定义图标')
|
|
337
|
+
const rec = this.library.icons[id]
|
|
338
|
+
if (rec === null || typeof rec !== 'object') throw new Error('图标记录不存在')
|
|
339
|
+
rec.logo = request.enabled === true
|
|
340
|
+
this.current.logo = rec.logo
|
|
341
|
+
await this._saveLibrary().catch(() => {}) // index is advisory
|
|
342
|
+
return this.status()
|
|
343
|
+
}
|
|
344
|
+
|
|
328
345
|
/** typert endpoint: clear the active marker so the platform icon is used.
|
|
329
346
|
* Icon files stay on disk for the future icon-library UI. */
|
|
330
347
|
async resetIcon() {
|
package/lib/typert.host.js
CHANGED
|
@@ -4,6 +4,7 @@ import { z } from 'zod'
|
|
|
4
4
|
const statusSchema = z.object({
|
|
5
5
|
active: z.boolean(),
|
|
6
6
|
pwa: z.boolean(),
|
|
7
|
+
logo: z.boolean(),
|
|
7
8
|
rev: z.string().nullable(),
|
|
8
9
|
mime: z.string().nullable(),
|
|
9
10
|
ext: z.string().nullable(),
|
|
@@ -16,6 +17,7 @@ const setIconRequestSchema = z.object({
|
|
|
16
17
|
mime: z.string().min(1),
|
|
17
18
|
name: z.string().max(128).optional(),
|
|
18
19
|
pwa: z.boolean().optional(),
|
|
20
|
+
logo: z.boolean().optional(),
|
|
19
21
|
data: z.string().min(1)
|
|
20
22
|
})
|
|
21
23
|
|
|
@@ -23,6 +25,10 @@ const setPwaRequestSchema = z.object({
|
|
|
23
25
|
enabled: z.boolean()
|
|
24
26
|
})
|
|
25
27
|
|
|
28
|
+
const setLogoRequestSchema = z.object({
|
|
29
|
+
enabled: z.boolean()
|
|
30
|
+
})
|
|
31
|
+
|
|
26
32
|
const setIconResultSchema = statusSchema
|
|
27
33
|
|
|
28
34
|
export const TYPERT = {
|
|
@@ -94,6 +100,31 @@ export const TYPERT = {
|
|
|
94
100
|
},
|
|
95
101
|
sourceLocation: { file: 'lib/index.js', line: 1, column: 1 }
|
|
96
102
|
},
|
|
103
|
+
{
|
|
104
|
+
id: 'dsh-icon-custom#iconCustom/setLogo',
|
|
105
|
+
service: 'iconCustom',
|
|
106
|
+
namespace: 'iconCustom',
|
|
107
|
+
method: 'setLogo',
|
|
108
|
+
invocation: { kind: 'direct' },
|
|
109
|
+
parameters: [
|
|
110
|
+
{
|
|
111
|
+
name: 'request',
|
|
112
|
+
wire: 'request',
|
|
113
|
+
source: 'json',
|
|
114
|
+
codec: {
|
|
115
|
+
mode: 'strict',
|
|
116
|
+
typeSymbol: 'dsh-icon-custom#iconCustom/setLogo:request',
|
|
117
|
+
schema: setLogoRequestSchema
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
result: {
|
|
122
|
+
mode: 'strict',
|
|
123
|
+
typeSymbol: 'dsh-icon-custom#iconCustom/setLogo:result',
|
|
124
|
+
schema: statusSchema
|
|
125
|
+
},
|
|
126
|
+
sourceLocation: { file: 'lib/index.js', line: 1, column: 1 }
|
|
127
|
+
},
|
|
97
128
|
{
|
|
98
129
|
id: 'dsh-icon-custom#iconCustom/resetIcon',
|
|
99
130
|
service: 'iconCustom',
|
|
@@ -122,7 +153,7 @@ export const TYPERT = {
|
|
|
122
153
|
{
|
|
123
154
|
kind: 'method',
|
|
124
155
|
name: 'getStatus',
|
|
125
|
-
signature: 'async getStatus(): Promise<{ active: boolean; pwa: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
156
|
+
signature: 'async getStatus(): Promise<{ active: boolean; pwa: boolean; logo: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
126
157
|
description: "Read the current favicon status.",
|
|
127
158
|
summary: "Read the current favicon status.",
|
|
128
159
|
jsDoc: "/** Read the current favicon status. */",
|
|
@@ -131,7 +162,7 @@ export const TYPERT = {
|
|
|
131
162
|
{
|
|
132
163
|
kind: 'method',
|
|
133
164
|
name: 'setIcon',
|
|
134
|
-
signature: 'async setIcon(request: { mime: string; name?: string; pwa?: boolean; data: string }): Promise<{ active: boolean; pwa: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
165
|
+
signature: 'async setIcon(request: { mime: string; name?: string; pwa?: boolean; logo?: boolean; data: string }): Promise<{ active: boolean; pwa: boolean; logo: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
135
166
|
description: "Store a new favicon uploaded as a data URL.",
|
|
136
167
|
summary: "Store a new favicon.",
|
|
137
168
|
jsDoc: "/** Store a new favicon. */",
|
|
@@ -140,16 +171,25 @@ export const TYPERT = {
|
|
|
140
171
|
{
|
|
141
172
|
kind: 'method',
|
|
142
173
|
name: 'setPwa',
|
|
143
|
-
signature: 'async setPwa(request: { enabled: boolean }): Promise<{ active: boolean; pwa: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
174
|
+
signature: 'async setPwa(request: { enabled: boolean }): Promise<{ active: boolean; pwa: boolean; logo: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
144
175
|
description: "Toggle whether the active custom icon also replaces the PWA install icon.",
|
|
145
176
|
summary: "Toggle the PWA-icon option.",
|
|
146
177
|
jsDoc: "/** Toggle the PWA-icon option. */",
|
|
147
178
|
tags: []
|
|
148
179
|
},
|
|
180
|
+
{
|
|
181
|
+
kind: 'method',
|
|
182
|
+
name: 'setLogo',
|
|
183
|
+
signature: 'async setLogo(request: { enabled: boolean }): Promise<{ active: boolean; pwa: boolean; logo: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
184
|
+
description: "Toggle whether the active custom icon also replaces the in-app sidebar brand mark.",
|
|
185
|
+
summary: "Toggle the brand-mark option.",
|
|
186
|
+
jsDoc: "/** Toggle the brand-mark option. */",
|
|
187
|
+
tags: []
|
|
188
|
+
},
|
|
149
189
|
{
|
|
150
190
|
kind: 'method',
|
|
151
191
|
name: 'resetIcon',
|
|
152
|
-
signature: 'async resetIcon(): Promise<{ active: boolean; pwa: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
192
|
+
signature: 'async resetIcon(): Promise<{ active: boolean; pwa: boolean; logo: boolean; rev: string | null; mime: string | null; ext: string | null; name: string | null; size: number | null; savedAt: number | null }>',
|
|
153
193
|
description: "Clear the active custom favicon marker so the platform icon is used; stored icon files are kept.",
|
|
154
194
|
summary: "Clear the custom favicon.",
|
|
155
195
|
jsDoc: "/** Clear the custom favicon. */",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cowwo/dsh-icon-custom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly and persists.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|