@chance722/dsh-inbox 0.2.4 → 0.2.5

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 CHANGED
@@ -173,6 +173,14 @@ pnpm test # vitest
173
173
 
174
174
  Client-side changes need `pnpm build` and a page reload (dsh's client-hmr reloads it for you); host-side changes need a restart. Details in [docs/help/dev-setup.md](docs/help/dev-setup.md); milestones and acceptance records in the [development bus](docs/feature/dev-bus.md).
175
175
 
176
+ To switch the profile you actually run between the **published package** and **this checkout** (default profile `web`; set `DSH_PROFILE` for another):
177
+
178
+ ```powershell
179
+ pnpm dev:status # which one is in use right now
180
+ pnpm dev:npm # point it at the published version
181
+ pnpm dev:local # point it back here (builds first, then links)
182
+ ```
183
+
176
184
  ## License
177
185
 
178
186
  MIT
package/README.zh.md CHANGED
@@ -172,6 +172,14 @@ pnpm test # vitest
172
172
 
173
173
  改客户端代码:`pnpm build` 后刷新页面即可(dsh 的 client-hmr 会自己重载);改宿主代码要重启服务。细节见 [docs/help/dev-setup.md](docs/help/dev-setup.md),阶段与验收记录在[开发总线](docs/feature/dev-bus.md)。
174
174
 
175
+ 日常想在**线上发布版**和**本地改动**之间切换(默认 profile 是 `web`,换 profile 加 `DSH_PROFILE`):
176
+
177
+ ```powershell
178
+ pnpm dev:status # 现在用的是哪一个:本仓库 / 线上包
179
+ pnpm dev:npm # 切到 npm 上发布的版本(体验发布版)
180
+ pnpm dev:local # 切回当前仓库(会先 pnpm build 再链接)
181
+ ```
182
+
175
183
  ## 许可
176
184
 
177
185
  MIT
package/lib/client.js CHANGED
@@ -868,11 +868,14 @@ window.__ModuleLoader__.load({
868
868
  "sync.pullFailed": "\u62C9\u53D6\u5931\u8D25\uFF1A{reason}",
869
869
  // The refresh button's tooltip: counts first, reasons only when something was
870
870
  // skipped. The toast carries the two numbers alone.
871
- "sync.detailPushedIdle": "\u63A8\u9001 0 \u6761\uFF08{count} \u6761\u5DF2\u662F\u6700\u65B0\uFF09",
871
+ // No number here on purpose: the push considers every row it has, tombstones
872
+ // included, so "26 条已是最新" next to a header saying "共 13 条" is a
873
+ // mismatch the reader has to resolve for themselves (2026-09-21).
874
+ "sync.detailPushedIdle": "\u63A8\u9001 0 \u6761\uFF08\u6CA1\u6709\u65B0\u6539\u52A8\uFF09",
872
875
  "sync.detailPushed": "\u63A8\u9001 {records} \u6761 / {attachments} \u4E2A\u9644\u4EF6",
873
876
  "sync.detailPulled": "\u62C9\u53D6 {count} \u6761 \xB7 \u4E91\u7AEF {records} \u6761\u8BB0\u5F55 / {files} \u4E2A\u9644\u4EF6",
874
877
  "sync.detailAdded": "\uFF08\u5176\u4E2D {count} \u6761\u662F\u65B0\u8BB0\u5F55\uFF09",
875
- "sync.detailSkip": "\uFF08\u8DF3\u8FC7 {skipped}\uFF1A\u81EA\u5DF1\u7684 {sync}\u3001\u66F4\u65E7 {older}\uFF09",
878
+ "sync.detailDeleted": "\uFF08\u5176\u4E2D {count} \u6761\u662F\u5220\u9664\uFF0C\u4F1A\u8FDB\u56DE\u6536\u7AD9\uFF09",
876
879
  "sync.pullForeignSync": " \xB7 \u26A0\uFE0F \u53E6\u6709 {records} \u6761\u8BB0\u5F55\u5728\u522B\u7684\u540C\u6B65\u76EE\u5F55\uFF1A{roots}",
877
880
  "sync.detailForeign": " \xB7 \u26A0\uFE0F \u522B\u7684\u540C\u6B65\u76EE\u5F55\uFF1A{roots}\uFF08{records} \u6761\u8BB0\u5F55\uFF1B\u672C\u673A {ours}\uFF09",
878
881
  // The toast: two numbers, nothing else. Everything else about a sync is
@@ -1137,11 +1140,11 @@ window.__ModuleLoader__.load({
1137
1140
  "sync.pushPartial": "{head} \xB7 partly failed: {reason}",
1138
1141
  "sync.pullNoAddress": "No address configured yet",
1139
1142
  "sync.pullFailed": "Pull failed: {reason}",
1140
- "sync.detailPushedIdle": "pushed 0 ({count} already up to date)",
1143
+ "sync.detailPushedIdle": "pushed 0 (nothing new)",
1141
1144
  "sync.detailPushed": "pushed {records} / {attachments} attachments",
1142
1145
  "sync.detailPulled": "pulled {count} \xB7 the cloud holds {records} records / {files} attachments",
1143
1146
  "sync.detailAdded": " ({count} of them new)",
1144
- "sync.detailSkip": " ({skipped} skipped: {sync} ours, {older} older)",
1147
+ "sync.detailDeleted": " ({count} of them deletions \u2014 they land in the recycle bin)",
1145
1148
  "sync.pullForeignSync": " \xB7 \u26A0\uFE0F {records} records live in another sync directory: {roots}",
1146
1149
  "sync.detailForeign": " \xB7 \u26A0\uFE0F another sync directory: {roots} ({records} records; ours is {ours})",
1147
1150
  "sync.shortPushed": "pushed {count}",
@@ -3316,12 +3319,8 @@ window.__ModuleLoader__.load({
3316
3319
  files: result.remoteAttachments ?? 0
3317
3320
  });
3318
3321
  const gained = (result.added ?? 0) === 0 ? "" : t("sync.detailAdded", { count: result.added ?? 0 });
3319
- const skipped = result.skipped === 0 ? "" : t("sync.detailSkip", {
3320
- skipped: result.skipped,
3321
- sync: result.skippedSync ?? 0,
3322
- older: result.skippedOlder ?? 0
3323
- });
3324
- return `${head}${gained}${skipped}${warningsOf(result, true)}`;
3322
+ const deleted = (result.deletions ?? 0) === 0 ? "" : t("sync.detailDeleted", { count: result.deletions ?? 0 });
3323
+ return `${head}${gained}${deleted}${warningsOf(result, true)}`;
3325
3324
  }
3326
3325
  function EncryptionSettings({ call }) {
3327
3326
  const [status, setStatus] = import_react8.default.useState();
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ import { defineTool as defineTool2 } from "@deepseek-ai/dsh-tools";
3
3
 
4
4
  // src/shared/constants.ts
5
5
  var PACKAGE_NAME = "@chance722/dsh-inbox";
6
- var VERSION = true ? "0.2.4" : "0.0.0-dev";
6
+ var VERSION = true ? "0.2.5" : "0.0.0-dev";
7
7
  var DEFAULT_USER_AGENT = "dsh-inbox";
8
8
 
9
9
  // src/shared/vocabulary.ts
@@ -1404,13 +1404,14 @@ async function mergeOnce(vault, tree, prefix, admit) {
1404
1404
  const failures = [];
1405
1405
  let merged = 0;
1406
1406
  let added = 0;
1407
+ let deletions = 0;
1407
1408
  let kept = 0;
1408
1409
  let attachments = 0;
1409
1410
  let objects;
1410
1411
  try {
1411
1412
  objects = await tree.list(`${prefix}/items/`);
1412
1413
  } catch (error) {
1413
- return { merged: 0, added: 0, kept: 0, attachments: 0, failures: [`\u5217\u8FDC\u7AEF\u540C\u6B65\u76EE\u5F55\u5931\u8D25\uFF1A${reasonOf(error)}`] };
1414
+ return { merged: 0, added: 0, deletions: 0, kept: 0, attachments: 0, failures: [`\u5217\u8FDC\u7AEF\u540C\u6B65\u76EE\u5F55\u5931\u8D25\uFF1A${reasonOf(error)}`] };
1414
1415
  }
1415
1416
  const wanted = /* @__PURE__ */ new Map();
1416
1417
  const bytesAt = /* @__PURE__ */ new Map();
@@ -1441,6 +1442,7 @@ async function mergeOnce(vault, tree, prefix, admit) {
1441
1442
  await vault.import(remote);
1442
1443
  merged += 1;
1443
1444
  if (local === void 0) added += 1;
1445
+ if (remote.deletedAt !== void 0) deletions += 1;
1444
1446
  for (const attachmentId of remote.attachmentIds) {
1445
1447
  if (vault.getAttachment(attachmentId) === void 0) {
1446
1448
  wanted.set(attachmentId, remote.id);
@@ -1475,7 +1477,7 @@ async function mergeOnce(vault, tree, prefix, admit) {
1475
1477
  failures.push(`\u9644\u4EF6 ${attachmentId}\uFF1A${reasonOf(error)}`);
1476
1478
  }
1477
1479
  }
1478
- return { merged, added, kept, attachments, failures };
1480
+ return { merged, added, deletions, kept, attachments, failures };
1479
1481
  }
1480
1482
  function extensionOfName(path) {
1481
1483
  return /\.([A-Za-z0-9]{1,8})$/.exec(nameOf2(path))?.[1]?.toLowerCase() ?? "bin";
@@ -1488,7 +1490,7 @@ async function mergeRemote(ctx, vault, attachments, extraRoots = []) {
1488
1490
  const prefix = syncRoot(settings);
1489
1491
  try {
1490
1492
  const tree = settings.protocol === "s3" ? await s3Tree(ctx, settings) : await webdavTree(ctx, settings);
1491
- if (tree === void 0) return { merged: 0, added: 0, kept: 0, attachments: 0, failures: [] };
1493
+ if (tree === void 0) return { merged: 0, added: 0, deletions: 0, kept: 0, attachments: 0, failures: [] };
1492
1494
  const admit = admitWith(attachments);
1493
1495
  const outcome = await mergeOnce(vault, tree, prefix, admit);
1494
1496
  for (const root of extraRoots) {
@@ -1496,13 +1498,14 @@ async function mergeRemote(ctx, vault, attachments, extraRoots = []) {
1496
1498
  const extra = await mergeOnce(vault, tree, root, admit);
1497
1499
  outcome.merged += extra.merged;
1498
1500
  outcome.added += extra.added;
1501
+ outcome.deletions += extra.deletions;
1499
1502
  outcome.kept += extra.kept;
1500
1503
  outcome.attachments += extra.attachments;
1501
1504
  outcome.failures.push(...extra.failures);
1502
1505
  }
1503
1506
  return outcome;
1504
1507
  } catch (error) {
1505
- return { merged: 0, added: 0, kept: 0, attachments: 0, failures: [reasonOf(error)] };
1508
+ return { merged: 0, added: 0, deletions: 0, kept: 0, attachments: 0, failures: [reasonOf(error)] };
1506
1509
  }
1507
1510
  }
1508
1511
  function admitWith(store) {
@@ -1609,6 +1612,7 @@ async function withMerge(ctx, vault, attachments, pulled) {
1609
1612
  ...pulled,
1610
1613
  merged: outcome.merged,
1611
1614
  added: outcome.added,
1615
+ deletions: outcome.deletions,
1612
1616
  kept: outcome.kept,
1613
1617
  attachments: outcome.attachments,
1614
1618
  failed: pulled.failed + outcome.failures.length,
@@ -186,7 +186,7 @@ export declare const zh: {
186
186
  'sync.detailPushed': string;
187
187
  'sync.detailPulled': string;
188
188
  'sync.detailAdded': string;
189
- 'sync.detailSkip': string;
189
+ 'sync.detailDeleted': string;
190
190
  'sync.pullForeignSync': string;
191
191
  'sync.detailForeign': string;
192
192
  'sync.shortPushed': string;
@@ -455,7 +455,7 @@ export declare const MESSAGES: {
455
455
  'sync.detailPushed': string;
456
456
  'sync.detailPulled': string;
457
457
  'sync.detailAdded': string;
458
- 'sync.detailSkip': string;
458
+ 'sync.detailDeleted': string;
459
459
  'sync.pullForeignSync': string;
460
460
  'sync.detailForeign': string;
461
461
  'sync.shortPushed': string;
@@ -42,6 +42,15 @@ export interface SyncTree {
42
42
  export interface MergeOutcome {
43
43
  /** Records that were new here, or overwritten because the remote was newer. */
44
44
  merged: number;
45
+ /**
46
+ * How many of {@link merged} arrived as deletions.
47
+ *
48
+ * A deleted record travels as an ordinary record with deletedAt set, so a
49
+ * merge that brings deletions looks exactly like one that brings updates until
50
+ * somebody opens the recycle bin (measured 2026-09-21: 19 merged — 6 new
51
+ * records and 13 tombstones, and the bin went from empty to thirteen).
52
+ */
53
+ deletions: number;
45
54
  /**
46
55
  * How many of {@link merged} were ids this vault did not have at all.
47
56
  *
@@ -175,6 +175,8 @@ export interface PullResult {
175
175
  merged?: number;
176
176
  /** How many of {@link merged} were ids this vault did not have at all. */
177
177
  added?: number;
178
+ /** How many of {@link merged} were deletions (they land in the recycle bin). */
179
+ deletions?: number;
178
180
  /** Attachment objects the merge had to fetch and admit locally. */
179
181
  attachments?: number;
180
182
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chance722/dsh-inbox",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "description": "Personal paste inbox for DeepSeek Harness (dsh): capture, classify, browse and retrieve.",
6
6
  "keywords": [
@@ -64,7 +64,10 @@
64
64
  "build": "node scripts/build.mjs && tsc -p tsconfig.build.json",
65
65
  "typecheck": "tsc --noEmit",
66
66
  "test": "vitest run",
67
- "prepublishOnly": "npm run build"
67
+ "prepublishOnly": "npm run build",
68
+ "dev:status": "node scripts/dev.mjs status",
69
+ "dev:local": "node scripts/dev.mjs local",
70
+ "dev:npm": "node scripts/dev.mjs npm"
68
71
  },
69
72
  "dependencies": {
70
73
  "lucide-react": "^1.47.0",