dsh-any-background 0.2.1 → 0.2.3

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
@@ -24,6 +24,8 @@ const WALLPAPER_FILE = "wallpaper.jpg";
24
24
  const VIDEO_ROUTE = "/dsh-any-background/video";
25
25
  const UPLOAD_ROUTE = "/dsh-any-background/video/upload";
26
26
  const UPLOAD_TMP = "wallpaper.upload.tmp";
27
+ const WALLPAPER_FETCH_MAX = 26214400;
28
+ const WALLPAPER_FETCH_TIMEOUT = 2e4;
27
29
  function videoFileName(mime) {
28
30
  switch (mime) {
29
31
  case "video/mp4": return "wallpaper.mp4";
@@ -278,6 +280,94 @@ async function writeWallpaper(dataUrl) {
278
280
  return false;
279
281
  }
280
282
  }
283
+ /** Sniff an image's MIME from its leading magic bytes (defaults to JPEG). */
284
+ function sniffImageMime(buf) {
285
+ if (buf.length >= 4 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "image/png";
286
+ if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return "image/jpeg";
287
+ if (buf.length >= 6 && buf[0] === 71 && buf[1] === 73 && buf[2] === 70) return "image/gif";
288
+ if (buf.length >= 12 && buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) return "image/webp";
289
+ return "image/jpeg";
290
+ }
291
+ /** Download a wallpaper from a network URL and persist it into the local
292
+ * wallpaper.jpg slot (replacing whatever was stored), so type switches,
293
+ * export and import keep working through the existing data-URL path.
294
+ * null removes the wallpaper. Returns { ok, dataUrl?, error? }. */
295
+ async function writeWallpaperFromUrl(url) {
296
+ if (url === null) {
297
+ const ok = await writeWallpaper(null);
298
+ return {
299
+ ok,
300
+ dataUrl: null,
301
+ error: ok ? void 0 : "remove failed"
302
+ };
303
+ }
304
+ let u;
305
+ try {
306
+ u = new URL(url);
307
+ } catch {
308
+ return {
309
+ ok: false,
310
+ error: "invalid url"
311
+ };
312
+ }
313
+ if (u.protocol !== "http:" && u.protocol !== "https:") return {
314
+ ok: false,
315
+ error: "unsupported scheme"
316
+ };
317
+ let res;
318
+ try {
319
+ const ctl = new AbortController();
320
+ const timer = setTimeout(() => ctl.abort(), WALLPAPER_FETCH_TIMEOUT);
321
+ try {
322
+ res = await fetch(url, {
323
+ redirect: "follow",
324
+ signal: ctl.signal
325
+ });
326
+ } finally {
327
+ clearTimeout(timer);
328
+ }
329
+ } catch (e) {
330
+ return {
331
+ ok: false,
332
+ error: e instanceof Error && e.name === "AbortError" ? "timeout" : "network error"
333
+ };
334
+ }
335
+ if (!res.ok) return {
336
+ ok: false,
337
+ error: `http ${res.status}`
338
+ };
339
+ const ct = res.headers.get("content-type") ?? "";
340
+ if (ct && !/^image\//.test(ct)) return {
341
+ ok: false,
342
+ error: "not an image"
343
+ };
344
+ let buf;
345
+ try {
346
+ const arr = await res.arrayBuffer();
347
+ if (arr.byteLength === 0) return {
348
+ ok: false,
349
+ error: "empty response"
350
+ };
351
+ if (arr.byteLength > WALLPAPER_FETCH_MAX) return {
352
+ ok: false,
353
+ error: "too large"
354
+ };
355
+ buf = Buffer.from(arr);
356
+ } catch {
357
+ return {
358
+ ok: false,
359
+ error: "read failed"
360
+ };
361
+ }
362
+ const dataUrl = `data:${sniffImageMime(buf)};base64,${buf.toString("base64")}`;
363
+ return await writeWallpaper(dataUrl) ? {
364
+ ok: true,
365
+ dataUrl
366
+ } : {
367
+ ok: false,
368
+ error: "write failed"
369
+ };
370
+ }
281
371
  async function videoUrl() {
282
372
  return await findVideoFile() ? VIDEO_ROUTE : null;
283
373
  }
@@ -472,10 +562,14 @@ function apply(ctx) {
472
562
  ok: true,
473
563
  value: await writeVideo(payload?.dataUrl ?? null)
474
564
  };
565
+ case "setWallpaperUrl": return {
566
+ ok: true,
567
+ value: await writeWallpaperFromUrl(payload?.url ?? null)
568
+ };
475
569
  default: return {
476
570
  ok: false,
477
571
  error: {
478
- code: "bad-request",
572
+ code: "dsh-any-background/bad-request",
479
573
  message: `unknown endpoint ${ep}`,
480
574
  details: { issues: [] }
481
575
  }
@@ -485,7 +579,7 @@ function apply(ctx) {
485
579
  return {
486
580
  ok: false,
487
581
  error: {
488
- code: "internal",
582
+ code: "dsh-any-background/internal",
489
583
  message: e instanceof Error ? e.message : String(e),
490
584
  details: {}
491
585
  }
package/package.json CHANGED
@@ -1,108 +1,123 @@
1
- {
2
- "name": "dsh-any-background",
3
- "version": "0.2.1",
4
- "description": "Appearance plugin for DeepSeek Harness: custom theme color (PS-style color wheel), background wallpaper with opacity/blur controls, and separate main/settings interface opacity.",
5
- "keywords": [
6
- "deepseek-harness",
7
- "dsh",
8
- "plugin",
9
- "dsh-plugin",
10
- "theme",
11
- "wallpaper",
12
- "web-ui"
13
- ],
14
- "homepage": "https://github.com/Tkingxiao/dsh-any-background#readme",
15
- "bugs": {
16
- "url": "https://github.com/Tkingxiao/dsh-any-background/issues"
17
- },
18
- "repository": {
19
- "type": "git",
20
- "url": "git+https://github.com/Tkingxiao/dsh-any-background.git"
21
- },
22
- "license": "MIT",
23
- "author": "Tkingxiao",
24
- "type": "module",
25
- "exports": {
26
- ".": {
27
- "default": "./lib/index.js"
28
- },
29
- "./invariant": {
30
- "default": "./lib/invariant.js"
31
- },
32
- "./client": {
33
- "default": "./lib/client.js"
34
- },
35
- "./package.json": "./package.json"
36
- },
37
- "main": "lib/index.js",
38
- "directories": {
39
- "lib": "lib"
40
- },
41
- "files": [
42
- "lib/index.js",
43
- "lib/invariant.js",
44
- "lib/client.js",
45
- "lib/client.js.map",
46
- "cordis.patch.yml"
47
- ],
48
- "scripts": {
49
- "bundle": "tsdown",
50
- "watch": "tsdown --watch",
51
- "typecheck": "tsc -p tsconfig.json"
52
- },
53
- "dependencies": {
54
- "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6"
55
- },
56
- "devDependencies": {
57
- "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
58
- "@types/node": "^26.2.0",
59
- "@types/react": "^19.2.18",
60
- "@types/react-dom": "^19.2.4",
61
- "tsdown": "^0.22.2",
62
- "typescript": "^7.0.2",
63
- "unrun": "^0.3.1"
64
- },
65
- "peerDependencies": {
66
- "@deepseek-ai/cordis": "^4.0.1",
67
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
68
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
69
- "@deepseek-ai/dsh-client-ui-theme": "^0.1.0-rc.6",
70
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
71
- "react": "^18.2.0"
72
- },
73
- "peerDependenciesMeta": {
74
- "react": {
75
- "optional": true
76
- },
77
- "@deepseek-ai/cordis": {
78
- "optional": true
79
- },
80
- "@deepseek-ai/dsh-client-runtime": {
81
- "optional": true
82
- },
83
- "@deepseek-ai/dsh-client-ui-theme": {
84
- "optional": true
85
- },
86
- "@deepseek-ai/dsh-client-locale": {
87
- "optional": true
88
- },
89
- "@deepseek-ai/dsh-invariants": {
90
- "optional": true
91
- }
92
- },
93
- "dsh": {
94
- "bundle": {
95
- "patch": "./cordis.patch.yml"
96
- },
97
- "client": {
98
- "platform": "web",
99
- "inject": [
100
- "@deepseek-ai/dsh-client-runtime",
101
- "@deepseek-ai/dsh-client-locale",
102
- "@deepseek-ai/dsh-client-ui-theme"
103
- ],
104
- "immediately": true
105
- }
106
- },
107
- "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be"
108
- }
1
+ {
2
+ "name": "dsh-any-background",
3
+ "version": "0.2.3",
4
+ "description": "Appearance plugin for DeepSeek Harness: custom theme color (PS-style color wheel), background wallpaper with opacity/blur controls, and separate main/settings interface opacity.",
5
+ "keywords": [
6
+ "deepseek-harness",
7
+ "dsh",
8
+ "plugin",
9
+ "dsh-plugin",
10
+ "theme",
11
+ "wallpaper",
12
+ "web-ui"
13
+ ],
14
+ "homepage": "https://github.com/Tkingxiao/dsh-any-background#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/Tkingxiao/dsh-any-background/issues"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/Tkingxiao/dsh-any-background.git"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Tkingxiao",
24
+ "type": "module",
25
+ "exports": {
26
+ ".": {
27
+ "default": "./lib/index.js"
28
+ },
29
+ "./invariant": {
30
+ "default": "./lib/invariant.js"
31
+ },
32
+ "./client": {
33
+ "default": "./lib/client.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "main": "lib/index.js",
38
+ "directories": {
39
+ "lib": "lib"
40
+ },
41
+ "files": [
42
+ "lib/index.js",
43
+ "lib/invariant.js",
44
+ "lib/client.js",
45
+ "lib/client.js.map",
46
+ "cordis.patch.yml"
47
+ ],
48
+ "scripts": {
49
+ "bundle": "tsdown",
50
+ "watch": "tsdown --watch",
51
+ "typecheck": "tsc -p tsconfig.json"
52
+ },
53
+ "devDependencies": {
54
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6",
55
+ "@types/node": "^26.2.0",
56
+ "@types/react": "^19.2.18",
57
+ "@types/react-dom": "^19.2.4",
58
+ "tsdown": "^0.22.2",
59
+ "typescript": "^7.0.2",
60
+ "unrun": "^0.3.1"
61
+ },
62
+ "dependencies": {
63
+ "@deepseek-ai/dsh-home-paths": "^0.1.0-rc.6"
64
+ },
65
+ "peerDependencies": {
66
+ "@deepseek-ai/cordis": "^4.0.1",
67
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
68
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
69
+ "@deepseek-ai/dsh-client-store": "^0.1.2-alpha.2",
70
+ "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.6",
71
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
72
+ "@deepseek-ai/dsh-client-ui-theme": "^0.1.0-rc.6",
73
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
74
+ "react": "^18.2.0"
75
+ },
76
+ "peerDependenciesMeta": {
77
+ "react": {
78
+ "optional": true
79
+ },
80
+ "@deepseek-ai/cordis": {
81
+ "optional": true
82
+ },
83
+ "@deepseek-ai/dsh-client-store": {
84
+ "optional": true
85
+ },
86
+ "@deepseek-ai/dsh-client-connection": {
87
+ "optional": true
88
+ },
89
+ "@deepseek-ai/dsh-client-ui-settings": {
90
+ "optional": true
91
+ },
92
+ "@deepseek-ai/dsh-client-runtime": {
93
+ "optional": true
94
+ },
95
+ "@deepseek-ai/dsh-client-ui-theme": {
96
+ "optional": true
97
+ },
98
+ "@deepseek-ai/dsh-client-locale": {
99
+ "optional": true
100
+ },
101
+ "@deepseek-ai/dsh-invariants": {
102
+ "optional": true
103
+ }
104
+ },
105
+ "dsh": {
106
+ "bundle": {
107
+ "patch": "./cordis.patch.yml"
108
+ },
109
+ "client": {
110
+ "platform": "web",
111
+ "inject": [
112
+ "@deepseek-ai/dsh-client-runtime",
113
+ "@deepseek-ai/dsh-client-store",
114
+ "@deepseek-ai/dsh-client-locale",
115
+ "@deepseek-ai/dsh-client-connection",
116
+ "@deepseek-ai/dsh-client-ui-theme",
117
+ "@deepseek-ai/dsh-client-ui-settings"
118
+ ],
119
+ "immediately": true
120
+ }
121
+ },
122
+ "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be"
123
+ }