deepseek-harness-background 0.5.4 → 0.5.6

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
@@ -1,6 +1,6 @@
1
1
  import z from "@deepseek-ai/schemastery";
2
2
  import { randomBytes } from "node:crypto";
3
- import { existsSync, mkdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { readFile } from "node:fs/promises";
5
5
  import { extname, join, resolve } from "node:path";
6
6
  import { homedir } from "node:os";
@@ -52,12 +52,11 @@ const FIT_MODES = ["cover", "contain"];
52
52
  //#region src/schema.ts
53
53
  /** Host-side schema for the `ui-background` settings namespace. */
54
54
  /**
55
- * Durable background section; also the wire envelope the browser scope
56
- * validates against. `uploadId` and `url` name the two exclusive sources; the
57
- * schema stays structural (no trim/transform — a function callback would break
58
- * the schema's toJSON wire serialization).
55
+ * The section's fields, in schema order — ONE definition feeding both
56
+ * generations. Field schemas are immutable builders (`volatile()` clones),
57
+ * so deriving the volatile view below leaves this one unmarked.
59
58
  */
60
- const BackgroundSettingsSchema = z.object({
59
+ const BackgroundSettingsFields = {
61
60
  enabled: z.boolean().default(false),
62
61
  /** Content-addressed local upload id or empty. */
63
62
  uploadId: z.string().default(""),
@@ -70,7 +69,32 @@ const BackgroundSettingsSchema = z.object({
70
69
  wallpaperBlur: z.number().min(0).max(60).default(0),
71
70
  fit: z.union([...FIT_MODES]).default(DEFAULT_FIT),
72
71
  timeline: z.boolean().default(true)
73
- });
72
+ };
73
+ /**
74
+ * Durable background section; also the wire envelope the browser scope
75
+ * validates against and the namespace schema registered on dsh 0.1.5/0.1.6.
76
+ * `uploadId` and `url` name the two exclusive sources; the schema stays
77
+ * structural (no trim/transform — a function callback would break the
78
+ * schema's toJSON wire serialization).
79
+ */
80
+ const BackgroundSettingsSchema = z.object(BackgroundSettingsFields);
81
+ /** The same fields, each marked volatile (the marker is cloned, so the plain
82
+ * view above stays unmarked). */
83
+ function volatileFields(fields) {
84
+ return Object.fromEntries(Object.entries(fields).map(([key, field]) => [key, field.volatile()]));
85
+ }
86
+ /**
87
+ * The plugin's live configuration as dsh 0.1.7+ reads it: the settings
88
+ * service projects each active profile entry's exported `Config` into a form
89
+ * and keys the section on the entry id. Every field is volatile — "editable
90
+ * without remounting" — which is exactly what a wholesale
91
+ * `settings.replace(entryId, section)` demands of each written path
92
+ * (`isVolatilePath` walks to a volatile ancestor or refuses the write).
93
+ *
94
+ * The wire schema above stays unmarked: it is also the 0.1.5/0.1.6
95
+ * registration schema, where `volatile` has no meaning.
96
+ */
97
+ const Config = z.object(volatileFields(BackgroundSettingsFields));
74
98
  //#endregion
75
99
  //#region src/harness-home.ts
76
100
  /**
@@ -108,6 +132,167 @@ function resolveHarnessHome(env = process.env) {
108
132
  /** Subdirectory under the harness home that this plugin owns. */
109
133
  const PLUGIN_HOME_REL = join("deepseek-harness-background");
110
134
  //#endregion
135
+ //#region src/migrate.ts
136
+ /**
137
+ * Legacy-settings migration.
138
+ *
139
+ * dsh <= 0.1.6 stored this plugin's section in `~/.dsh/settings.yaml`. dsh
140
+ * 0.1.7 imports that document once, BY PROFILE ENTRY ID, then renames it to
141
+ * `settings.yaml.imported`; `ui-background` is not an entry id, so the section
142
+ * is logged and skipped — the renamed file is the one surviving copy of a
143
+ * user's background settings.
144
+ *
145
+ * This module reads that copy back and hands it to the 0.1.7 settings service
146
+ * under the entry-id namespace, so an upgrade does not silently reset every
147
+ * background. The document is user-editable, so each value is validated
148
+ * against the section schema and a value the schema refuses is dropped (its
149
+ * default then applies) rather than poisoning the whole migration.
150
+ */
151
+ /**
152
+ * Settings documents an older dsh left in the harness home, in precedence
153
+ * order: the live document first, then the copy 0.1.7 renamed away.
154
+ */
155
+ const LEGACY_SETTINGS_FILES = ["settings.yaml", "settings.yaml.imported"];
156
+ /** Marker written into the plugin home once a migration has been attempted. */
157
+ const MIGRATION_MARKER = ".migrated-from-legacy-settings";
158
+ /**
159
+ * One scalar as the settings provider's YAML writer emitted it. The section is
160
+ * flat and holds only scalars, so this covers the whole surface; a quoted
161
+ * scalar stays a string (a quoted `"2"` must not become the number 2).
162
+ */
163
+ function parseScalar(raw) {
164
+ const text = raw.trim();
165
+ if (text === "") return "";
166
+ if (text === "true") return true;
167
+ if (text === "false") return false;
168
+ if (text === "null" || text === "~") return null;
169
+ if (text.startsWith("\"") && text.endsWith("\"") || text.startsWith("'") && text.endsWith("'")) return text.slice(1, -1);
170
+ const uncommented = text.replace(/\s+#.*$/, "");
171
+ const numeric = Number(uncommented);
172
+ if (uncommented !== "" && Number.isFinite(numeric)) return numeric;
173
+ return uncommented;
174
+ }
175
+ /**
176
+ * The lines of one TOP-LEVEL `namespace:` block, or undefined when the document
177
+ * has no such key. Only the block's own indent level is returned: a deeper
178
+ * mapping that happens to reuse the field names belongs to some other section.
179
+ */
180
+ function sectionFields(text, namespace) {
181
+ const lines = text.split(/\r?\n/);
182
+ const header = new RegExp(`^${namespace}\\s*:(?:\\s*#.*)?$`);
183
+ const start = lines.findIndex((line) => header.test(line));
184
+ if (start < 0) return void 0;
185
+ const fields = {};
186
+ let indent;
187
+ for (let index = start + 1; index < lines.length; index += 1) {
188
+ const line = lines[index] ?? "";
189
+ if (line.trim() === "") continue;
190
+ const match = /^(\s+)([A-Za-z0-9_-]+)\s*:\s*(.*)$/.exec(line);
191
+ if (match === null) {
192
+ if (!/^\s/.test(line)) break;
193
+ continue;
194
+ }
195
+ indent ??= match[1]?.length;
196
+ if (match[1]?.length !== indent) continue;
197
+ const key = match[2] ?? "";
198
+ if (!BACKGROUND_SETTINGS_FIELDS.includes(key)) continue;
199
+ fields[key] = parseScalar(match[3] ?? "");
200
+ }
201
+ return Object.keys(fields).length === 0 ? void 0 : fields;
202
+ }
203
+ /** Keep one field only when the section schema accepts it. */
204
+ function acceptedFields(candidate) {
205
+ const accepted = {};
206
+ for (const [key, value] of Object.entries(candidate)) try {
207
+ BackgroundSettingsSchema({ [key]: value });
208
+ accepted[key] = value;
209
+ } catch {}
210
+ return accepted;
211
+ }
212
+ /**
213
+ * Read the plugin's section out of the newest settings document that carries
214
+ * one.
215
+ * @param home - harness home (defaults to the resolved one).
216
+ * @returns the schema-resolved section and its source, or undefined when no
217
+ * legacy document holds a usable section.
218
+ */
219
+ function readLegacyBackgroundSection(home = resolveHarnessHome()) {
220
+ for (const file of LEGACY_SETTINGS_FILES) {
221
+ const path = join(home, file);
222
+ if (!existsSync(path)) continue;
223
+ let text;
224
+ try {
225
+ text = readFileSync(path, "utf8");
226
+ } catch {
227
+ continue;
228
+ }
229
+ const candidate = sectionFields(text, "ui-background");
230
+ if (candidate === void 0) continue;
231
+ const accepted = acceptedFields(candidate);
232
+ if (Object.keys(accepted).length === 0) continue;
233
+ return {
234
+ from: file,
235
+ section: BackgroundSettingsSchema(accepted)
236
+ };
237
+ }
238
+ }
239
+ /**
240
+ * Whether a descriptor actually carries a user layer. dsh 0.1.7's `describe()`
241
+ * projects the raw user section through the form, so an entry with no config
242
+ * row reports `user: {}` rather than leaving the field off — an empty object
243
+ * IS the fresh-upgrade state this migration exists for.
244
+ */
245
+ function hasUserSection(user) {
246
+ if (user === void 0 || user === null) return false;
247
+ if (typeof user === "object") return Object.keys(user).length > 0;
248
+ return true;
249
+ }
250
+ /** The plugin's own directory under the harness home. */
251
+ function pluginHome$1(home) {
252
+ return join(home, PLUGIN_HOME_REL);
253
+ }
254
+ /**
255
+ * Bring a pre-0.1.7 section forward into the entry-keyed document, ONCE.
256
+ *
257
+ * Runs lazily from the first settings read rather than from `apply`: the
258
+ * settings service refuses a write until this plugin's fiber is ACTIVE, and a
259
+ * route handler only ever runs once it is. The marker file is what makes it
260
+ * one-shot — a user who resets every field to its default deletes the entry's
261
+ * config row, and without the marker the next boot would resurrect the old
262
+ * background.
263
+ *
264
+ * @param settings - the mounted settings service.
265
+ * @param namespace - the entry-keyed namespace to write.
266
+ * @param home - harness home (defaults to the resolved one).
267
+ * @returns what the attempt did.
268
+ */
269
+ async function ensureLegacyMigration(settings, namespace, home = resolveHarnessHome()) {
270
+ const marker = join(pluginHome$1(home), MIGRATION_MARKER);
271
+ if (existsSync(marker)) return {
272
+ kind: "skipped",
273
+ reason: "already-migrated"
274
+ };
275
+ const descriptor = settings.describe().find((candidate) => candidate.ns === namespace);
276
+ if (descriptor !== void 0 && hasUserSection(descriptor.user)) return {
277
+ kind: "skipped",
278
+ reason: "target-has-user-section"
279
+ };
280
+ const legacy = readLegacyBackgroundSection(home);
281
+ if (legacy === void 0) return {
282
+ kind: "skipped",
283
+ reason: "no-legacy-section"
284
+ };
285
+ await settings.replace(namespace, legacy.section);
286
+ try {
287
+ mkdirSync(pluginHome$1(home), { recursive: true });
288
+ writeFileSync(marker, `${(/* @__PURE__ */ new Date()).toISOString()}\n${legacy.from}\n`, "utf8");
289
+ } catch {}
290
+ return {
291
+ kind: "migrated",
292
+ from: legacy.from
293
+ };
294
+ }
295
+ //#endregion
111
296
  //#region src/routes.ts
112
297
  /**
113
298
  * Host HTTP routes for the background plugin — the browser half talks to the
@@ -146,6 +331,22 @@ function pickKnown(section) {
146
331
  for (const key of BACKGROUND_SETTINGS_FIELDS) if (key in src) out[key] = src[key];
147
332
  return out;
148
333
  }
334
+ /**
335
+ * Resolve the plugin's section from whatever settings generation is mounted.
336
+ * Both generations return the schema-resolved value from `describe()`, so one
337
+ * read path serves 0.1.5, 0.1.6 and 0.1.7; an absent namespace (or a settings
338
+ * service that has not finished registering) resolves to the schema defaults.
339
+ * @param settings - the mounted settings service.
340
+ * @param namespace - the namespace/entry id owning the section.
341
+ * @returns the resolved section, defaults filled in.
342
+ */
343
+ function readBackgroundSection(settings, namespace = BACKGROUND_SETTINGS_NAMESPACE) {
344
+ const described = settings.describe().find((descriptor) => descriptor.ns === namespace)?.value;
345
+ return {
346
+ ...DEFAULTS,
347
+ ...described
348
+ };
349
+ }
149
350
  /** Accepted upload MIME type → file extension. */
150
351
  const ACCEPTED_TYPES = {
151
352
  "image/jpeg": "jpg",
@@ -407,15 +608,38 @@ function mimeForPath(path) {
407
608
  if (ext === "webp") return "image/webp";
408
609
  if (ext === "gif") return "image/gif";
409
610
  }
611
+ /**
612
+ * Lazily attempt the pre-0.1.7 settings migration exactly once per mounted
613
+ * route family.
614
+ *
615
+ * It runs from the first settings READ rather than from `apply` for two
616
+ * reasons: the settings service refuses a write until this plugin's fiber is
617
+ * ACTIVE, and a route handler only ever runs once it is; and the read that
618
+ * triggers it is the browser half's own first `load()`, so the very response
619
+ * that carries the adopted section also carries the `migrated` notice.
620
+ * @param settings - the mounted settings service.
621
+ * @param namespace - the entry-keyed namespace to write.
622
+ * @param home - harness home override (tests); production resolves the env.
623
+ */
624
+ function createMigrationGate(settings, namespace, home) {
625
+ let attempted = false;
626
+ return async () => {
627
+ if (attempted) return void 0;
628
+ attempted = true;
629
+ try {
630
+ const outcome = await ensureLegacyMigration(settings, namespace, home ?? resolveHarnessHome());
631
+ return outcome.kind === "migrated" ? { from: outcome.from } : void 0;
632
+ } catch (error) {
633
+ console.error("[deepseek-harness-background] legacy settings migration failed:", error);
634
+ return;
635
+ }
636
+ };
637
+ }
410
638
  /** One background API route family over the settings provider + home. */
411
639
  function makeBackgroundRoutes(settings, opts = {}) {
412
- const readSection = () => {
413
- const section = settings.get(BACKGROUND_SETTINGS_NAMESPACE);
414
- return {
415
- ...DEFAULTS,
416
- ...section
417
- };
418
- };
640
+ const namespace = opts.namespace ?? "ui-background";
641
+ const readSection = () => readBackgroundSection(settings, namespace);
642
+ const migrate = createMigrationGate(settings, namespace, opts.home);
419
643
  return [
420
644
  {
421
645
  kind: "exact",
@@ -429,9 +653,11 @@ function makeBackgroundRoutes(settings, opts = {}) {
429
653
  return;
430
654
  }
431
655
  if (req.method === "GET") {
656
+ const migrated = await migrate();
432
657
  json(res, 200, {
433
658
  ok: true,
434
- value: readSection()
659
+ value: readSection(),
660
+ ...migrated === void 0 ? {} : { migrated }
435
661
  });
436
662
  return;
437
663
  }
@@ -458,7 +684,7 @@ function makeBackgroundRoutes(settings, opts = {}) {
458
684
  });
459
685
  return;
460
686
  }
461
- await settings.replace(BACKGROUND_SETTINGS_NAMESPACE, merged);
687
+ await settings.replace(namespace, merged);
462
688
  const after = readSection();
463
689
  if (before.uploadId !== "" && before.uploadId !== after.uploadId) deleteUploadFile(before.uploadId, opts.home);
464
690
  json(res, 200, {
@@ -585,6 +811,32 @@ function makeBackgroundRoutes(settings, opts = {}) {
585
811
  //#endregion
586
812
  //#region src/index.ts
587
813
  /**
814
+ * Entry id this plugin's bundle patch inserts into the profile. From dsh
815
+ * 0.1.7 on it doubles as the settings namespace, and it is the documented
816
+ * fallback when the running kernel exposes no Loader entry (a bare
817
+ * `ctx.plugin()` mount in tests, or a future loader that moves the handle).
818
+ */
819
+ const BACKGROUND_ENTRY_ID = "deepseek-harness-background";
820
+ /**
821
+ * Whether the mounted settings service is the pre-0.1.7 provider. The one
822
+ * generation test: `register` was removed in the 0.1.7 rewrite, so its
823
+ * presence IS the older generation (never a version compare).
824
+ * @param settings - the mounted settings service.
825
+ */
826
+ function isLegacySettingsService(settings) {
827
+ return typeof settings.register === "function";
828
+ }
829
+ /**
830
+ * The namespace the section lives under on the running generation: the
831
+ * plugin-owned `ui-background` on 0.1.5/0.1.6, the profile entry id from
832
+ * 0.1.7 on.
833
+ * @param settings - the mounted settings service.
834
+ * @param entryId - this plugin's Loader entry id, when the kernel exposes one.
835
+ */
836
+ function resolveBackgroundNamespace(settings, entryId) {
837
+ return isLegacySettingsService(settings) ? BACKGROUND_SETTINGS_NAMESPACE : entryId ?? "deepseek-harness-background";
838
+ }
839
+ /**
588
840
  * Register the durable background section and its API routes when the Host
589
841
  * settings/webServer services are composed. Effect timing is `live`: the
590
842
  * browser half repaints through the route without a restart.
@@ -592,13 +844,23 @@ function makeBackgroundRoutes(settings, opts = {}) {
592
844
  */
593
845
  function apply(ctx) {
594
846
  ctx.inject(["settings", "webServer"], (hostCtx) => {
595
- if (!hostCtx.settings.describe().some((descriptor) => descriptor.ns === "ui-background")) hostCtx.settings.register(BACKGROUND_SETTINGS_NAMESPACE, BackgroundSettingsSchema, { applies: "live" });
847
+ const settings = hostCtx.settings;
848
+ const entryId = ctx.fiber.entry?.options?.id;
849
+ const namespace = resolveBackgroundNamespace(settings, entryId);
850
+ if (isLegacySettingsService(settings)) {
851
+ if (!settings.describe().some((descriptor) => descriptor.ns === namespace)) settings.register?.(namespace, BackgroundSettingsSchema, { applies: "live" });
852
+ } else if (typeof settings.configure === "function") hostCtx.effect(() => {
853
+ const dispose = settings.configure?.({ auto: false }, ctx.fiber);
854
+ return () => {
855
+ dispose?.();
856
+ };
857
+ }, "deepseek-harness-background: settings presentation");
596
858
  try {
597
- for (const route of makeBackgroundRoutes(hostCtx.settings)) hostCtx.effect(() => hostCtx.webServer.register(route), "deepseek-harness-background: settings route");
859
+ for (const route of makeBackgroundRoutes(settings, { namespace })) hostCtx.effect(() => hostCtx.webServer.register(route), "deepseek-harness-background: settings route");
598
860
  } catch (error) {
599
861
  console.error("[deepseek-harness-background] route registration failed:", error);
600
862
  }
601
863
  });
602
864
  }
603
865
  //#endregion
604
- export { BACKGROUND_API_PREFIX, BACKGROUND_SETTINGS_NAMESPACE, BackgroundSettingsSchema, DEFAULT_FIT, DEFAULT_OPACITY, DEFAULT_PANEL_OPACITY, DEFAULT_SCRIM, FIT_MODES, OPACITY_MAX, OPACITY_MIN, PLUGIN_HOME_REL, SCRIM_MAX, SCRIM_MIN, apply, makeBackgroundRoutes, resolveHarnessHome };
866
+ export { BACKGROUND_API_PREFIX, BACKGROUND_ENTRY_ID, BACKGROUND_SETTINGS_NAMESPACE, BackgroundSettingsSchema, Config, DEFAULT_FIT, DEFAULT_OPACITY, DEFAULT_PANEL_OPACITY, DEFAULT_SCRIM, FIT_MODES, OPACITY_MAX, OPACITY_MIN, PLUGIN_HOME_REL, SCRIM_MAX, SCRIM_MIN, apply, isLegacySettingsService, makeBackgroundRoutes, readBackgroundSection, resolveBackgroundNamespace, resolveHarnessHome };
package/package.json CHANGED
@@ -1,85 +1,85 @@
1
- {
2
- "name": "deepseek-harness-background",
3
- "description": "Custom background image plugin for the DeepSeek Harness web GUI: upload a local picture or paste an image URL, and render it behind the whole app surface with adjustable opacity, scrim, panel transparency and frosted-glass blur.",
4
- "version": "0.5.4",
5
- "keywords": [
6
- "deepseek-harness",
7
- "dsh",
8
- "plugin",
9
- "dsh-plugin",
10
- "theme",
11
- "wallpaper",
12
- "background",
13
- "web-ui"
14
- ],
15
- "author": "HaoyueQin",
16
- "license": "MIT",
17
- "repository": {
18
- "type": "git",
19
- "url": "git+https://github.com/HaoyueQin/deepseek-harness-background.git"
20
- },
21
- "homepage": "https://github.com/HaoyueQin/deepseek-harness-background",
22
- "packageManager": "pnpm@11.9.0",
23
- "type": "module",
24
- "main": "lib/index.js",
25
- "exports": {
26
- ".": "./lib/index.js",
27
- "./client": "./lib/client.js",
28
- "./package.json": "./package.json"
29
- },
30
- "dsh": {
31
- "bundle": {
32
- "patch": "./cordis.patch.yml"
33
- },
34
- "client": {
35
- "inject": [
36
- "@deepseek-ai/dsh-client-locale"
37
- ],
38
- "platform": "web"
39
- }
40
- },
41
- "scripts": {
42
- "build": "tsdown",
43
- "prepare": "tsdown",
44
- "watch": "tsdown --watch",
45
- "test": "vitest run",
46
- "typecheck": "tsc --noEmit -p tsconfig.json"
47
- },
48
- "files": [
49
- "lib",
50
- "cordis.patch.yml",
51
- "README.md",
52
- "README.zh.md"
53
- ],
54
- "peerDependencies": {
55
- "@deepseek-ai/cordis": "^4.0.1",
56
- "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
57
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
58
- "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
59
- "react": "^18.2.0"
60
- },
61
- "dependencies": {
62
- "@deepseek-ai/schemastery": "^3.18.1"
63
- },
64
- "devDependencies": {
65
- "@deepseek-ai/cordis": "^4.0.1",
66
- "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
67
- "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
68
- "@deepseek-ai/dsh-host-webserver": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
69
- "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1",
70
- "@deepseek-ai/schemastery": "^3.18.1",
71
- "@testing-library/dom": "^10.4.1",
72
- "@testing-library/react": "^16.3.0",
73
- "@types/node": "^22.0.0",
74
- "@types/react": "~18.3.1",
75
- "@types/react-dom": "~18.3.0",
76
- "clsx": "^2.1.1",
77
- "jsdom": "^29.1.1",
78
- "lightningcss": "^1.33.0",
79
- "react": "^18.2.0",
80
- "react-dom": "^18.2.0",
81
- "tsdown": "^0.22.14",
82
- "typescript": "^5.8.0",
83
- "vitest": "^4.1.8"
84
- }
85
- }
1
+ {
2
+ "name": "deepseek-harness-background",
3
+ "description": "Custom background image plugin for the DeepSeek Harness web GUI: upload a local picture or paste an image URL, and render it behind the whole app surface with adjustable opacity, scrim, panel transparency and frosted-glass blur.",
4
+ "version": "0.5.6",
5
+ "keywords": [
6
+ "deepseek-harness",
7
+ "dsh",
8
+ "plugin",
9
+ "dsh-plugin",
10
+ "theme",
11
+ "wallpaper",
12
+ "background",
13
+ "web-ui"
14
+ ],
15
+ "author": "HaoyueQin",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/HaoyueQin/deepseek-harness-background.git"
20
+ },
21
+ "homepage": "https://github.com/HaoyueQin/deepseek-harness-background",
22
+ "packageManager": "pnpm@11.9.0",
23
+ "type": "module",
24
+ "main": "lib/index.js",
25
+ "exports": {
26
+ ".": "./lib/index.js",
27
+ "./client": "./lib/client.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "dsh": {
31
+ "bundle": {
32
+ "patch": "./cordis.patch.yml"
33
+ },
34
+ "client": {
35
+ "inject": [
36
+ "@deepseek-ai/dsh-client-locale"
37
+ ],
38
+ "platform": "web"
39
+ }
40
+ },
41
+ "scripts": {
42
+ "build": "tsdown",
43
+ "prepare": "tsdown",
44
+ "watch": "tsdown --watch",
45
+ "test": "vitest run",
46
+ "typecheck": "tsc --noEmit -p tsconfig.json"
47
+ },
48
+ "files": [
49
+ "lib",
50
+ "cordis.patch.yml",
51
+ "README.md",
52
+ "README.zh.md"
53
+ ],
54
+ "peerDependencies": {
55
+ "@deepseek-ai/cordis": "^4.0.1",
56
+ "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
57
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
58
+ "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
59
+ "react": "^18.2.0"
60
+ },
61
+ "dependencies": {
62
+ "@deepseek-ai/schemastery": "^3.18.3"
63
+ },
64
+ "devDependencies": {
65
+ "@deepseek-ai/cordis": "^4.0.1",
66
+ "@deepseek-ai/dsh-client-locale": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
67
+ "@deepseek-ai/dsh-client-ui-slots": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
68
+ "@deepseek-ai/dsh-host-webserver": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
69
+ "@deepseek-ai/dsh-settings": ">=0.1.2-rc.1 || >=0.1.3-alpha.1 || >=0.1.5-alpha.1 || >=0.1.5-rc.1 || >=0.1.5-rc.2 || >=0.1.6-alpha.1 || >=0.1.6-alpha.2 || >=0.1.7-alpha.1",
70
+ "@deepseek-ai/schemastery": "^3.18.3",
71
+ "@testing-library/dom": "^10.4.1",
72
+ "@testing-library/react": "^16.3.0",
73
+ "@types/node": "^22.0.0",
74
+ "@types/react": "~18.3.1",
75
+ "@types/react-dom": "~18.3.0",
76
+ "clsx": "^2.1.1",
77
+ "jsdom": "^29.1.1",
78
+ "lightningcss": "^1.33.0",
79
+ "react": "^18.2.0",
80
+ "react-dom": "^18.2.0",
81
+ "tsdown": "^0.22.14",
82
+ "typescript": "^5.8.0",
83
+ "vitest": "^4.1.8"
84
+ }
85
+ }