prompt-skill-armory 0.10.0 → 0.10.1

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.en.md CHANGED
@@ -12,13 +12,13 @@
12
12
  |--|--|--|--|
13
13
  | <img src="docs/preview/preview-6.png" width="220"/> | <img src="docs/preview/preview-7.png" width="220"/> | | |
14
14
 
15
- ![version](https://img.shields.io/badge/version-0.10.0-00ff9c)
15
+ ![version](https://img.shields.io/badge/version-0.10.1-00ff9c)
16
16
  ![license](https://img.shields.io/badge/license-MIT-blue)
17
17
  ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
18
  ![CI](https://github.com/Qian-Ning/prompt-skill-armory/actions/workflows/ci.yml/badge.svg)
19
19
 
20
20
  ```
21
- ✦ Armory v0.10.0
21
+ ✦ Armory v0.10.1
22
22
  ┌─────────┬─────────┬─────┬────────────┬──────┬──────┐
23
23
  │ Prompts │ Skills │ MCP │ Wallpaper │ Chat │ Stats│
24
24
  │ global │ merged │ hub │ wallpaper │ sync │ usage│
@@ -125,7 +125,7 @@ Achieved via profile composition (the installer mounts the panel into each profi
125
125
 
126
126
  Every time the panel opens it queries npm for the latest `prompt-skill-armory` version; when a newer version exists a banner appears:
127
127
 
128
- > New version vX.Y.Z (current v0.10.0) [Update now]
128
+ > New version vX.Y.Z (current v0.10.1) [Update now]
129
129
 
130
130
  Clicking "Update now" makes the Host re-run the official installer in place → "Update complete, please restart the client".
131
131
 
package/README.md CHANGED
@@ -12,13 +12,13 @@
12
12
  |--|--|--|--|
13
13
  | <img src="docs/preview/preview-6.png" width="220"/> | <img src="docs/preview/preview-7.png" width="220"/> | | |
14
14
 
15
- ![version](https://img.shields.io/badge/version-0.10.0-00ff9c)
15
+ ![version](https://img.shields.io/badge/version-0.10.1-00ff9c)
16
16
  ![license](https://img.shields.io/badge/license-MIT-blue)
17
17
  ![platform](https://img.shields.io/badge/platform-web%20%2B%20desktop-00ff9c)
18
18
  ![CI](https://github.com/Qian-Ning/prompt-skill-armory/actions/workflows/ci.yml/badge.svg)
19
19
 
20
20
  ```
21
- ✦ Armory v0.10.0
21
+ ✦ Armory v0.10.1
22
22
  ┌─────────┬─────────┬─────┬────────────┬──────┬──────┐
23
23
  │ 提示词 │ 技能 │ MCP │ Wallpaper │ 对话 │ 统计 │
24
24
  │ 全局注入 │ 合并管理 │ 连接 │ 壁纸·特效 │ 导入 │ 用量 │
@@ -128,7 +128,7 @@ pnpm dsh web
128
128
 
129
129
  插件面板每次打开会自动查询 npm 上 `prompt-skill-armory` 的最新版本;发现新版本时顶部显示横幅:
130
130
 
131
- > 有新版本 vX.Y.Z(当前 v0.10.0) [一键更新]
131
+ > 有新版本 vX.Y.Z(当前 v0.10.1) [一键更新]
132
132
 
133
133
  点击「一键更新」→ Host 端运行官方安装器原地重装 → 提示「更新完成,请重启客户端生效」。
134
134
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prompt-skill-armory",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "One-command installer for Prompt-SkillArmory (prompts + skills manager) into the DeepSeek Harness (web + desktop)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -155,14 +155,26 @@ window.__ModuleLoader__.load({
155
155
  const GLASS_ATTR = "data-ar-glass";
156
156
  const CSS_TAG = "prompt-skill-armory/background";
157
157
  const BACKGROUND_CSS = `
158
- .ar-bg-layer { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; }
158
+ /* Backdrop pinned to the viewport at the very bottom of the stacking order.
159
+ z-index 0 + forcing DSH surfaces transparent lets it show through every
160
+ chrome (sidebar / conversation / rightbar), not just the chat area. */
161
+ .ar-bg-layer { position: fixed; inset: 0; z-index: 0; overflow: hidden; pointer-events: none; }
159
162
  .ar-bg-layer .ar-bg-image { width: 100%; height: 100%; display: block; border: 0;
160
163
  object-fit: var(--ar-bg-fit, cover); opacity: var(--ar-bg-opacity, 1);
161
164
  filter: blur(var(--ar-bg-blur, 0px)); transform: scale(var(--ar-bg-scale, 1)); transform-origin: center; }
162
- .ar-bg-scrim { position: fixed; inset: 0; z-index: -1; pointer-events: none;
165
+ .ar-bg-scrim { position: fixed; inset: 0; z-index: 0; pointer-events: none;
163
166
  background: rgba(255,255,255, var(--ar-bg-scrim, 0.25)); }
164
167
  body[data-ds-dark-theme] .ar-bg-scrim { background: rgba(0,0,0, var(--ar-bg-scrim, 0.25)); }
165
- body[${ACTIVE_ATTR}] { --dsw-alias-bg-base: transparent; --dsw-specific-sidebar-fill: transparent; }
168
+
169
+ /* Lift the app chrome above the backdrop so it stays interactive, while
170
+ every DSH surface background becomes transparent to reveal the wallpaper. */
171
+ body[${ACTIVE_ATTR}] #root { position: relative; z-index: 1; background: transparent; }
172
+ body[${ACTIVE_ATTR}] .dshDesktopFrame,
173
+ body[${ACTIVE_ATTR}] .dshDesktopConversationSurface,
174
+ body[${ACTIVE_ATTR}] .dshDesktopSidebarSurface,
175
+ body[${ACTIVE_ATTR}] .dshDesktopRightbarSurface { background: transparent !important; }
176
+ body[${ACTIVE_ATTR}] { --dsw-alias-bg-base: transparent; --dsw-alias-bg-layer-1: transparent; --dsw-specific-sidebar-fill: transparent; }
177
+
166
178
  body[${GLASS_ATTR}] [data-composer-card],
167
179
  body[${GLASS_ATTR}] [class*="_bubble"]:not([role="tooltip"]),
168
180
  body[${GLASS_ATTR}] .md-code-block,
@@ -230,7 +242,11 @@ window.__ModuleLoader__.load({
230
242
  try {
231
243
  injectBackgroundCss();
232
244
  const hasSource = settings.url !== "" || settings.uploadId !== "";
233
- for (const prop of ["--dsw-alias-bg-base", "--dsw-specific-sidebar-fill"]) {
245
+ for (const prop of [
246
+ "--dsw-alias-bg-base",
247
+ "--dsw-alias-bg-layer-1",
248
+ "--dsw-specific-sidebar-fill"
249
+ ]) {
234
250
  this.rememberOnce(prop);
235
251
  document.body.style.setProperty(prop, "transparent");
236
252
  }
@@ -357,17 +373,17 @@ window.__ModuleLoader__.load({
357
373
  const notifyBg = () => {
358
374
  for (const l of bgListeners) l();
359
375
  };
360
- let bgApi;
361
- /** Bind the transport to a live connection API (set once at plugin apply). */
362
- function initBackgroundClient(api) {
363
- bgApi = api;
376
+ let bgScope;
377
+ /** Bind the transport to the switchblade settings scope (set once at plugin apply). */
378
+ function initBackgroundClient(scope) {
379
+ bgScope = scope;
364
380
  }
365
381
  /** Surface key: separate wallpaper per web vs desktop (they share the settings doc). */
366
382
  const isDesktopSurface = typeof navigator !== "undefined" && navigator.userAgent.toLowerCase().includes("electron");
367
383
  const surfaceKey = () => isDesktopSurface ? "backgroundDesktop" : "backgroundWeb";
368
384
  /** Read the background section for the current surface from the switchblade settings. */
369
385
  function readSection(value) {
370
- const raw = (value.namespaces?.find((n) => n.ns === "switchblade")?.value)?.[surfaceKey()];
386
+ const raw = value?.[surfaceKey()];
371
387
  return {
372
388
  ...DEFAULT_BACKGROUND,
373
389
  ...typeof raw === "object" && raw !== null ? raw : {}
@@ -385,15 +401,15 @@ window.__ModuleLoader__.load({
385
401
  },
386
402
  /** Fetch the durable section. @returns true on success (status ready). */
387
403
  async load() {
388
- if (bgApi === void 0) {
404
+ if (bgScope === void 0) {
389
405
  bgState.status = "error";
390
406
  return false;
391
407
  }
392
408
  try {
393
- const res = await bgApi.settings.describe({});
394
- if (!res.result.ok) throw new Error(res.result.error.message);
409
+ const snap = bgScope.getSnapshot();
410
+ if (snap.status !== "ready" || snap.value === void 0) return false;
395
411
  bgState.status = "ready";
396
- bgState.value = readSection(res.result.value);
412
+ bgState.value = readSection(snap.value);
397
413
  notifyBg();
398
414
  return true;
399
415
  } catch {
@@ -402,17 +418,13 @@ window.__ModuleLoader__.load({
402
418
  }
403
419
  },
404
420
  async save(section) {
405
- if (bgApi === void 0) return;
421
+ if (bgScope === void 0) return;
406
422
  try {
407
- const res = await bgApi.settings.mutate({
408
- ns: "switchblade",
409
- ops: [{
410
- op: "set",
411
- path: [surfaceKey()],
412
- value: section
413
- }]
414
- });
415
- if (!res.result.ok) throw new Error(res.result.error.message);
423
+ await bgScope.mutate([{
424
+ op: "set",
425
+ path: [surfaceKey()],
426
+ value: section
427
+ }]);
416
428
  bgState.status = "ready";
417
429
  bgState.value = { ...section };
418
430
  } catch {
@@ -957,7 +969,7 @@ window.__ModuleLoader__.load({
957
969
  });
958
970
  }
959
971
  /** Bump with every release; keep in sync with package.json version + CHANGELOG. */
960
- const ARMORY_VERSION = "0.10.0";
972
+ const ARMORY_VERSION = "0.10.1";
961
973
  /** Compact duration: 45.2s / 2m42s / 1h05m. */
962
974
  function fmtDuration(ms) {
963
975
  const s = ms / 1e3;
@@ -3240,14 +3252,27 @@ window.__ModuleLoader__.load({
3240
3252
  return error instanceof Error ? error.message : String(error);
3241
3253
  }
3242
3254
  var SwitchbladeSectionController = class {
3243
- api;
3255
+ scope;
3256
+ remote;
3244
3257
  sessionId;
3245
3258
  /** Snapshot store backing the section's view state. */
3246
3259
  store = createSnapshotStore(IDLE);
3247
- constructor(api, sessionId) {
3248
- this.api = api;
3260
+ constructor(scope, remote, sessionId) {
3261
+ this.scope = scope;
3262
+ this.remote = remote;
3249
3263
  this.sessionId = sessionId;
3250
3264
  }
3265
+ /** 2.0.9 settings read: scope mirror snapshot (never the old api.settings). */
3266
+ describeSettings() {
3267
+ const snap = this.scope.getSnapshot();
3268
+ if (snap.status !== "ready" || snap.value === void 0) return void 0;
3269
+ return this.sectionFromSettings(snap.value, "switchblade");
3270
+ }
3271
+ /** 2.0.9 settings write: scope.mutate(ops) — Host fold + re-inject. */
3272
+ async mutateSettings(ops) {
3273
+ if (this.scope.getSnapshot().status !== "ready") throw new Error("settings scope not ready");
3274
+ await this.scope.mutate(ops);
3275
+ }
3251
3276
  /**
3252
3277
  * Load skills, prompts, and installed skills. Prompts and installed
3253
3278
  * skills come from the `switchblade` settings namespace (the Host watches
@@ -3260,16 +3285,14 @@ window.__ModuleLoader__.load({
3260
3285
  });
3261
3286
  try {
3262
3287
  const sessionId = this.sessionId?.();
3263
- const calls = [this.api.settings.describe({})];
3264
- if (sessionId !== void 0) calls.push(this.api.skills.list({ sessionId }));
3265
- const [settingsRes, skillRes] = await Promise.all(calls);
3266
- if (!settingsRes.result.ok) throw new Error(`settings.describe: ${settingsRes.result.error.message}`);
3267
- const skills = skillRes !== void 0 && skillRes.result.ok ? skillRes.result.value.skills.map((skill) => ({
3288
+ const calls = [Promise.resolve(this.describeSettings())];
3289
+ if (sessionId !== void 0) calls.push(this.remote.skills.list({ request: { sessionId } }));
3290
+ const [switchbladeSection, skillRes] = await Promise.all(calls);
3291
+ const skills = skillRes !== void 0 && skillRes.ok && skillRes.value !== void 0 ? skillRes.value.skills.map((skill) => ({
3268
3292
  name: skill.name,
3269
3293
  description: skill.description,
3270
3294
  modelInvocable: skill.modelInvocable
3271
3295
  })) : [];
3272
- const switchbladeSection = this.sectionFromSettings(settingsRes.result.value, "switchblade");
3273
3296
  const prompts = Array.isArray(switchbladeSection?.prompts) ? switchbladeSection.prompts : [];
3274
3297
  const installedSkills = Array.isArray(switchbladeSection?.installedSkills) ? switchbladeSection.installedSkills.map((s) => ({
3275
3298
  name: s.name ?? "",
@@ -3311,9 +3334,13 @@ window.__ModuleLoader__.load({
3311
3334
  });
3312
3335
  }
3313
3336
  }
3314
- /** Read one namespace's user section from a settings.describe value. */
3337
+ /** Read one namespace's section from a settings value. The 2.0.9 scope
3338
+ * snapshot carries the bound namespace's own value; older describe payloads
3339
+ * carry a `namespaces[]` table — accept both. */
3315
3340
  sectionFromSettings(value, ns) {
3316
3341
  if (typeof value !== "object" || value === null) return void 0;
3342
+ const asSection = value;
3343
+ if (Array.isArray(asSection.prompts) || Array.isArray(asSection.installedSkills) || asSection.mcpServers !== void 0) return asSection;
3317
3344
  const entries = value.namespaces;
3318
3345
  if (!Array.isArray(entries)) return void 0;
3319
3346
  for (const entry of entries) {
@@ -3326,24 +3353,20 @@ window.__ModuleLoader__.load({
3326
3353
  }
3327
3354
  /** Add a prompt. */
3328
3355
  async addPrompt(input) {
3329
- const res = await this.api.settings.mutate({
3330
- ns: "switchblade",
3331
- ops: [{
3332
- op: "set",
3333
- path: ["prompts"],
3334
- value: [...this.currentPrompts(), {
3335
- id: this.slugify(input.name),
3336
- name: input.name,
3337
- description: input.description,
3338
- content: input.content,
3339
- order: this.currentPrompts().length,
3340
- enabled: true,
3341
- isDefault: this.currentPrompts().length === 0,
3342
- ...input.scope === void 0 || input.scope.type === "global" ? {} : { scope: input.scope }
3343
- }]
3356
+ await this.mutateSettings([{
3357
+ op: "set",
3358
+ path: ["prompts"],
3359
+ value: [...this.currentPrompts(), {
3360
+ id: this.slugify(input.name),
3361
+ name: input.name,
3362
+ description: input.description,
3363
+ content: input.content,
3364
+ order: this.currentPrompts().length,
3365
+ enabled: true,
3366
+ isDefault: this.currentPrompts().length === 0,
3367
+ ...input.scope === void 0 || input.scope.type === "global" ? {} : { scope: input.scope }
3344
3368
  }]
3345
- });
3346
- if (!res.result.ok) throw new Error(res.result.error.message);
3369
+ }]);
3347
3370
  await this.load();
3348
3371
  }
3349
3372
  /** Toggle one prompt's enabled state. */
@@ -3380,15 +3403,11 @@ window.__ModuleLoader__.load({
3380
3403
  }
3381
3404
  /** Persist the prompt list through the settings RPC. */
3382
3405
  async writePrompts(prompts) {
3383
- const res = await this.api.settings.mutate({
3384
- ns: "switchblade",
3385
- ops: [{
3386
- op: "set",
3387
- path: ["prompts"],
3388
- value: prompts
3389
- }]
3390
- });
3391
- if (!res.result.ok) throw new Error(res.result.error.message);
3406
+ await this.mutateSettings([{
3407
+ op: "set",
3408
+ path: ["prompts"],
3409
+ value: prompts
3410
+ }]);
3392
3411
  await this.load();
3393
3412
  }
3394
3413
  /** Current prompt list from the loaded snapshot. */
@@ -3408,15 +3427,11 @@ window.__ModuleLoader__.load({
3408
3427
  content: input.content,
3409
3428
  enabled: true
3410
3429
  }];
3411
- const res = await this.api.settings.mutate({
3412
- ns: "switchblade",
3413
- ops: [{
3414
- op: "set",
3415
- path: ["installedSkills"],
3416
- value: next
3417
- }]
3418
- });
3419
- if (!res.result.ok) throw new Error(res.result.error.message);
3430
+ await this.mutateSettings([{
3431
+ op: "set",
3432
+ path: ["installedSkills"],
3433
+ value: next
3434
+ }]);
3420
3435
  await this.load();
3421
3436
  }
3422
3437
  /** Toggle one installed skill's enabled state. */
@@ -3425,29 +3440,21 @@ window.__ModuleLoader__.load({
3425
3440
  ...s,
3426
3441
  enabled
3427
3442
  } : s);
3428
- const res = await this.api.settings.mutate({
3429
- ns: "switchblade",
3430
- ops: [{
3431
- op: "set",
3432
- path: ["installedSkills"],
3433
- value: next
3434
- }]
3435
- });
3436
- if (!res.result.ok) throw new Error(res.result.error.message);
3443
+ await this.mutateSettings([{
3444
+ op: "set",
3445
+ path: ["installedSkills"],
3446
+ value: next
3447
+ }]);
3437
3448
  await this.load();
3438
3449
  }
3439
3450
  /** Uninstall one installed skill. */
3440
3451
  async uninstallSkill(name) {
3441
3452
  const next = this.currentInstalledSkills().filter((s) => s.name !== name);
3442
- const res = await this.api.settings.mutate({
3443
- ns: "switchblade",
3444
- ops: [{
3445
- op: "set",
3446
- path: ["installedSkills"],
3447
- value: next
3448
- }]
3449
- });
3450
- if (!res.result.ok) throw new Error(res.result.error.message);
3453
+ await this.mutateSettings([{
3454
+ op: "set",
3455
+ path: ["installedSkills"],
3456
+ value: next
3457
+ }]);
3451
3458
  await this.load();
3452
3459
  }
3453
3460
  /** Update an installed skill's name/description/content. */
@@ -3458,15 +3465,11 @@ window.__ModuleLoader__.load({
3458
3465
  description: patch.description ?? s.description,
3459
3466
  content: patch.content ?? s.content
3460
3467
  } : s);
3461
- const res = await this.api.settings.mutate({
3462
- ns: "switchblade",
3463
- ops: [{
3464
- op: "set",
3465
- path: ["installedSkills"],
3466
- value: next
3467
- }]
3468
- });
3469
- if (!res.result.ok) throw new Error(res.result.error.message);
3468
+ await this.mutateSettings([{
3469
+ op: "set",
3470
+ path: ["installedSkills"],
3471
+ value: next
3472
+ }]);
3470
3473
  await this.load();
3471
3474
  }
3472
3475
  /** Current installed skills from the loaded snapshot. */
@@ -3478,18 +3481,14 @@ window.__ModuleLoader__.load({
3478
3481
  * watch sees pendingZip and installs it (skil-filesystem then discovers it).
3479
3482
  */
3480
3483
  async installSkillFromZip(name, dataBase64) {
3481
- const res = await this.api.settings.mutate({
3482
- ns: "switchblade",
3483
- ops: [{
3484
- op: "set",
3485
- path: ["pendingZip"],
3486
- value: {
3487
- name,
3488
- dataBase64
3489
- }
3490
- }]
3491
- });
3492
- if (!res.result.ok) throw new Error(res.result.error.message);
3484
+ await this.mutateSettings([{
3485
+ op: "set",
3486
+ path: ["pendingZip"],
3487
+ value: {
3488
+ name,
3489
+ dataBase64
3490
+ }
3491
+ }]);
3493
3492
  await new Promise((r) => setTimeout(r, 500));
3494
3493
  await this.load();
3495
3494
  }
@@ -3526,32 +3525,24 @@ window.__ModuleLoader__.load({
3526
3525
  * after a short delay so the panel shows the fresh tool list / error.
3527
3526
  */
3528
3527
  async testMcpServer(name) {
3529
- const res = await this.api.settings.mutate({
3530
- ns: "switchblade",
3531
- ops: [{
3532
- op: "set",
3533
- path: ["mcpTestRequest"],
3534
- value: {
3535
- serverName: name,
3536
- ts: Date.now()
3537
- }
3538
- }]
3539
- });
3540
- if (!res.result.ok) throw new Error(res.result.error.message);
3528
+ await this.mutateSettings([{
3529
+ op: "set",
3530
+ path: ["mcpTestRequest"],
3531
+ value: {
3532
+ serverName: name,
3533
+ ts: Date.now()
3534
+ }
3535
+ }]);
3541
3536
  await new Promise((r) => setTimeout(r, 2e3));
3542
3537
  await this.load();
3543
3538
  }
3544
3539
  /** Persist the MCP server config list. */
3545
3540
  async writeMcpServers(servers) {
3546
- const res = await this.api.settings.mutate({
3547
- ns: "switchblade",
3548
- ops: [{
3549
- op: "set",
3550
- path: ["mcpServers"],
3551
- value: servers
3552
- }]
3553
- });
3554
- if (!res.result.ok) throw new Error(res.result.error.message);
3541
+ await this.mutateSettings([{
3542
+ op: "set",
3543
+ path: ["mcpServers"],
3544
+ value: servers
3545
+ }]);
3555
3546
  await this.load();
3556
3547
  }
3557
3548
  /** Current MCP server list from the loaded snapshot. */
@@ -3561,11 +3552,18 @@ window.__ModuleLoader__.load({
3561
3552
  };
3562
3553
  //#endregion
3563
3554
  //#region src/client/index.ts
3555
+ /**
3556
+ * Switchblade management page, browser half: registers the `settings.section`
3557
+ * navigation entry and renders the edgelord panel from the connection RPC
3558
+ * state. Global scope (root) — one management seat for every session.
3559
+ * @module @deepseek-ai/dsh-client-ui-switchblade
3560
+ */
3564
3561
  /** Required services (cordis fiber inject). */
3565
3562
  const inject = [
3566
3563
  "slots",
3567
3564
  "locale",
3568
- "connection",
3565
+ "settingsScope",
3566
+ "remote",
3569
3567
  "sessions"
3570
3568
  ];
3571
3569
  /**
@@ -3577,11 +3575,12 @@ window.__ModuleLoader__.load({
3577
3575
  zh,
3578
3576
  en
3579
3577
  }), "ui-switchblade: dictionaries");
3580
- const api = ctx.get("connection").api;
3578
+ const scope = ctx.get("settingsScope").bind({ namespace: "switchblade" });
3579
+ const remote = ctx.get("remote");
3581
3580
  const sessions = ctx.get("sessions");
3582
3581
  applyHintStyle();
3583
3582
  try {
3584
- initBackgroundClient(api);
3583
+ initBackgroundClient(scope);
3585
3584
  const paintBackground = () => {
3586
3585
  const s = backgroundClient.getSnapshot();
3587
3586
  if (s.status === "ready") applyBackground(s.value);
@@ -3596,7 +3595,7 @@ window.__ModuleLoader__.load({
3596
3595
  } catch (error) {
3597
3596
  console.warn("[switchblade] background init skipped:", error);
3598
3597
  }
3599
- const controller = new SwitchbladeSectionController(api, () => {
3598
+ const controller = new SwitchbladeSectionController(scope, remote, () => {
3600
3599
  const state = sessions.list.getSnapshot();
3601
3600
  return state.current === void 0 ? void 0 : state.current;
3602
3601
  });