@senguoyun/dsh-arkme 0.1.55 → 0.1.57

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/client.js CHANGED
@@ -5934,7 +5934,7 @@ window.__ModuleLoader__.load({
5934
5934
  qrcode.stringToBytes;
5935
5935
  //#endregion
5936
5936
  //#region package.json
5937
- var version$3 = "0.1.55";
5937
+ var version$3 = "0.1.57";
5938
5938
  //#endregion
5939
5939
  //#region src/client/arkme-billing.css?inline
5940
5940
  var arkme_billing_default = ".arkme-billing-backdrop {\n z-index: 120;\n backdrop-filter: blur(3px);\n background: #181b223d;\n place-items: center;\n padding: 28px;\n display: grid;\n position: fixed;\n inset: 0;\n}\n\n.arkme-billing-recharge-dialog, .arkme-billing-payment-dialog {\n color: #20232c;\n background: #fff;\n border: 1px solid #dedfe3;\n border-radius: 20px;\n width: min(620px, 100%);\n max-height: min(720px, 100vh - 56px);\n overflow-y: auto;\n box-shadow: 0 24px 72px #191c232e;\n}\n\n.arkme-billing-payment-dialog {\n width: min(420px, 100%);\n}\n\n.arkme-billing-dialog-header {\n border-bottom: 1px solid #ececef;\n justify-content: space-between;\n align-items: flex-start;\n gap: 18px;\n min-height: 78px;\n padding: 20px 22px 16px;\n display: flex;\n}\n\n.arkme-billing-dialog-header h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n line-height: 25px;\n}\n\n.arkme-billing-dialog-header p {\n overflow-wrap: anywhere;\n color: #8a8e96;\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-dialog-header > button {\n color: #777c85;\n width: 32px;\n height: 32px;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 9px;\n flex: none;\n place-items: center;\n font-size: 22px;\n display: grid;\n}\n\n.arkme-billing-dialog-header > button:hover {\n color: #20232c;\n background: #f3f3f5;\n}\n\n.arkme-billing-dialog-body {\n padding: 18px 22px 22px;\n}\n\n.arkme-billing-dialog-balance {\n border-bottom: 1px solid #ececef;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n column-gap: 12px;\n min-height: 48px;\n padding: 0 2px 16px;\n display: grid;\n}\n\n.arkme-billing-dialog-balance > span {\n color: #8a8e96;\n grid-column: 1;\n font-size: 11px;\n line-height: 16px;\n}\n\n.arkme-billing-dialog-balance > strong {\n grid-column: 1;\n font-size: 22px;\n font-weight: 600;\n line-height: 28px;\n}\n\n.arkme-billing-dialog-balance > button, .arkme-billing-error > button {\n color: #626772;\n height: 30px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 9px;\n grid-area: 1 / 2 / span 2;\n padding: 0 12px;\n font-size: 11px;\n}\n\n.arkme-billing-products-section, .arkme-billing-payment-section {\n padding-top: 17px;\n}\n\n.arkme-billing-products-section h3, .arkme-billing-payment-section h3 {\n color: #747984;\n margin: 0 0 11px;\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n}\n\n.arkme-billing-product-grid {\n width: 100%;\n}\n\n.arkme-billing-product-grid > button {\n color: #20232c;\n min-width: 0;\n min-height: 62px;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 11px;\n align-content: center;\n gap: 4px;\n padding: 9px 11px;\n display: grid;\n}\n\n.arkme-billing-product-grid > button:hover {\n background: #f8f8f9;\n border-color: #cfd2d8;\n}\n\n.arkme-billing-product-grid > button.is-selected {\n border-color: #75839e;\n box-shadow: 0 0 0 1px #75839e;\n}\n\n.arkme-billing-product-grid > button > span {\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 12px;\n font-weight: 500;\n line-height: 17px;\n overflow: hidden;\n}\n\n.arkme-billing-product-grid > button > strong {\n font-size: 14px;\n font-weight: 600;\n line-height: 19px;\n}\n\n.arkme-billing-payment-actions {\n flex-wrap: wrap;\n gap: 10px;\n display: flex;\n}\n\n.arkme-billing-payment-actions > button {\n color: #333842;\n min-width: 154px;\n height: 38px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 10px;\n justify-content: center;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.arkme-billing-payment-actions > button:hover {\n background: #f7f7f8;\n border-color: #cfd3da;\n}\n\n.arkme-billing-payment-actions > button:disabled, .arkme-billing-product-grid > button:disabled {\n opacity: .48;\n cursor: default;\n}\n\n.arkme-billing-platform-icon {\n flex: none;\n width: 18px;\n height: 18px;\n}\n\n.arkme-billing-platform-icon.is-alipay {\n color: #1677ff;\n}\n\n.arkme-billing-platform-icon.is-wechat {\n color: #07c160;\n}\n\n.arkme-billing-state {\n color: #858992;\n margin: 12px 0 2px;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-error {\n color: #b54841;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n margin-top: 10px;\n font-size: 12px;\n line-height: 18px;\n display: flex;\n}\n\n.arkme-billing-error.is-purchase {\n justify-content: flex-start;\n}\n\n.arkme-billing-payment-content {\n text-align: center;\n justify-items: center;\n gap: 10px;\n padding: 28px 24px 18px;\n display: grid;\n}\n\n.arkme-billing-payment-content > .arkme-billing-platform-icon {\n width: 38px;\n height: 38px;\n margin-bottom: 4px;\n}\n\n.arkme-billing-payment-content > img {\n border-radius: 10px;\n width: 210px;\n height: 210px;\n}\n\n.arkme-billing-payment-content > strong {\n font-size: 14px;\n font-weight: 600;\n line-height: 21px;\n}\n\n.arkme-billing-payment-content > p {\n color: #858992;\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-payment-content.is-paid > strong {\n color: #16794f;\n font-size: 18px;\n line-height: 25px;\n}\n\n.arkme-billing-payment-content.is-paid > .arkme-billing-error {\n justify-content: center;\n}\n\n.arkme-billing-payment-content.is-crediting > strong {\n color: #335f9e;\n}\n\n.arkme-billing-dialog-actions {\n flex-wrap: wrap;\n justify-content: center;\n gap: 9px;\n padding: 0 22px 20px;\n display: flex;\n}\n\n.arkme-billing-dialog-actions > button, .arkme-billing-primary-action {\n color: #3c424d;\n height: 34px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 9px;\n padding: 0 14px;\n font-size: 12px;\n}\n\n.arkme-billing-dialog-actions > .arkme-billing-primary-action, .arkme-billing-payment-content > .arkme-billing-primary-action {\n color: #fff;\n background: #20232d;\n border-color: #20232d;\n}\n";
@@ -49976,10 +49976,23 @@ html body {
49976
49976
  if (block.kind === "video") return "视频";
49977
49977
  return "文件";
49978
49978
  }
49979
- function arkmeClipboardImageBlob(blob, fallbackMimeType) {
49980
- const mediaType = (blob.type || fallbackMimeType).trim().toLowerCase();
49981
- const safeMediaType = mediaType.startsWith("image/") ? mediaType : "image/png";
49982
- return blob.type.trim().toLowerCase() === safeMediaType ? blob : new Blob([blob], { type: safeMediaType });
49979
+ async function arkmeClipboardImageBlob(blob) {
49980
+ const canvas = document.createElement("canvas");
49981
+ const bitmap = await createImageBitmap(blob);
49982
+ try {
49983
+ canvas.width = bitmap.width;
49984
+ canvas.height = bitmap.height;
49985
+ const context = canvas.getContext("2d");
49986
+ if (context === null) throw new Error("图片转换失败");
49987
+ context.drawImage(bitmap, 0, 0);
49988
+ return await new Promise((resolve, reject) => {
49989
+ canvas.toBlob((value) => value === null ? reject(/* @__PURE__ */ new Error("图片转换失败")) : resolve(value), "image/png");
49990
+ });
49991
+ } finally {
49992
+ bitmap.close();
49993
+ canvas.width = 0;
49994
+ canvas.height = 0;
49995
+ }
49983
49996
  }
49984
49997
  function FileReceptionProgress({ reception, fileName, noun = "文件" }) {
49985
49998
  const percent = reception.totalBytes > 0 ? Math.max(0, Math.min(100, Math.round(reception.receivedBytes / reception.totalBytes * 100))) : void 0;
@@ -50114,21 +50127,23 @@ html body {
50114
50127
  save
50115
50128
  };
50116
50129
  }
50117
- function useArkmeImageCopy(block, sourceUrl, onNotice) {
50130
+ function useArkmeImageCopy(block, sources, onNotice) {
50131
+ const { localOriginalRef, remoteOriginalRef, previewUrl } = sources;
50132
+ const imageIdentity = block.fileAssetUid ?? block.mediaRef;
50118
50133
  const [copying, setCopying] = (0, react$1.useState)(false);
50119
50134
  const copyController = (0, react$1.useRef)();
50120
- (0, react$1.useEffect)(() => () => {
50121
- copyController.current?.abort();
50122
- }, [
50123
- block.mediaRef,
50124
- block.localFileRef,
50125
- sourceUrl
50126
- ]);
50135
+ (0, react$1.useEffect)(() => {
50136
+ setCopying(false);
50137
+ return () => {
50138
+ copyController.current?.abort();
50139
+ copyController.current = void 0;
50140
+ };
50141
+ }, [imageIdentity]);
50127
50142
  const copy = async () => {
50128
- if (block.kind !== "image" || copying) return;
50143
+ if (block.kind !== "image" || copyController.current !== void 0) return;
50129
50144
  const clipboardWrite = typeof navigator === "undefined" ? void 0 : navigator.clipboard?.write?.bind(navigator.clipboard);
50130
50145
  const ClipboardItemConstructor = typeof ClipboardItem === "undefined" ? void 0 : ClipboardItem;
50131
- if (clipboardWrite === void 0 || ClipboardItemConstructor === void 0 || typeof fetch === "undefined" || sourceUrl === void 0) {
50146
+ if (clipboardWrite === void 0 || ClipboardItemConstructor === void 0 || typeof fetch === "undefined" || localOriginalRef === void 0 && remoteOriginalRef === void 0 && previewUrl === void 0) {
50132
50147
  onNotice?.({
50133
50148
  message: "复制失败",
50134
50149
  kind: "error"
@@ -50143,11 +50158,30 @@ html body {
50143
50158
  kind: "progress"
50144
50159
  });
50145
50160
  try {
50146
- const response = await fetch(sourceUrl, { signal: controller.signal });
50147
- if (!response.ok) throw new Error("图片已不可用,请重新打开");
50148
- const image = arkmeClipboardImageBlob(await response.blob(), block.mimeType);
50149
- controller.signal.throwIfAborted();
50150
- await clipboardWrite([new ClipboardItemConstructor({ [image.type]: image })]);
50161
+ const urls = [...new Set([
50162
+ localOriginalRef === void 0 ? void 0 : arkmeLocalFileUrl(localOriginalRef),
50163
+ remoteOriginalRef === void 0 ? void 0 : `/arkme-self/api/media?ref=${encodeURIComponent(remoteOriginalRef)}`,
50164
+ previewUrl
50165
+ ].filter((url) => url !== void 0))];
50166
+ const prepare = async () => {
50167
+ for (const url of urls) try {
50168
+ controller.signal.throwIfAborted();
50169
+ const response = await fetch(url, { signal: controller.signal });
50170
+ if (!response.ok) throw new Error("图片不可用");
50171
+ const blob = await response.blob();
50172
+ controller.signal.throwIfAborted();
50173
+ const image = await arkmeClipboardImageBlob(blob);
50174
+ controller.signal.throwIfAborted();
50175
+ return image;
50176
+ } catch (error) {
50177
+ controller.signal.throwIfAborted();
50178
+ if (url === urls.at(-1)) throw error;
50179
+ }
50180
+ throw new Error("图片不可用");
50181
+ };
50182
+ const image = prepare();
50183
+ image.catch(() => {});
50184
+ await clipboardWrite([new ClipboardItemConstructor({ "image/png": image })]);
50151
50185
  controller.signal.throwIfAborted();
50152
50186
  onNotice?.({
50153
50187
  message: "已复制",
@@ -50159,7 +50193,11 @@ html body {
50159
50193
  kind: "error"
50160
50194
  });
50161
50195
  } finally {
50162
- if (!controller.signal.aborted) setCopying(false);
50196
+ controller.abort();
50197
+ if (copyController.current === controller) {
50198
+ copyController.current = void 0;
50199
+ setCopying(false);
50200
+ }
50163
50201
  }
50164
50202
  };
50165
50203
  return {
@@ -50321,10 +50359,10 @@ html body {
50321
50359
  })
50322
50360
  });
50323
50361
  }
50324
- function ImageCopyAction({ block, sourceUrl, onNotice }) {
50325
- const { copying, copy } = useArkmeImageCopy(block, sourceUrl, onNotice);
50362
+ function ImageCopyAction({ block, sources, onNotice }) {
50363
+ const { copying, copy } = useArkmeImageCopy(block, sources, onNotice);
50326
50364
  if (block.kind !== "image") return null;
50327
- const unavailable = sourceUrl === void 0;
50365
+ const unavailable = sources.localOriginalRef === void 0 && sources.remoteOriginalRef === void 0 && sources.previewUrl === void 0;
50328
50366
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50329
50367
  type: "button",
50330
50368
  "aria-label": "复制图片",
@@ -50349,7 +50387,11 @@ html body {
50349
50387
  }
50350
50388
  function ArkmeFileActions({ block, original, copySourceUrl, onImageCopyNotice, showDownloadStatus = true, hideDownloadAfterSave = true, style }) {
50351
50389
  const download = useArkmeFileDownload(block, original);
50352
- const resolvedCopySourceUrl = copySourceUrl ?? (original.localRef === void 0 ? void 0 : arkmeLocalFileUrl(original.localRef));
50390
+ const sources = {
50391
+ localOriginalRef: original.localRef ?? block.localFileRef,
50392
+ remoteOriginalRef: block.originalRef,
50393
+ previewUrl: copySourceUrl ?? (block.mediaRef === "" || block.mediaRef === block.localFileRef ? void 0 : `/arkme-self/api/media?ref=${encodeURIComponent(block.mediaRef)}`)
50394
+ };
50353
50395
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
50354
50396
  style: {
50355
50397
  ...fileActionGroupStyle,
@@ -50357,7 +50399,7 @@ html body {
50357
50399
  },
50358
50400
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageCopyAction, {
50359
50401
  block,
50360
- sourceUrl: resolvedCopySourceUrl,
50402
+ sources,
50361
50403
  onNotice: onImageCopyNotice
50362
50404
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FileDownloadAction, {
50363
50405
  block,
@@ -50633,7 +50675,11 @@ html body {
50633
50675
  }),
50634
50676
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageCopyAction, {
50635
50677
  block,
50636
- sourceUrl: url,
50678
+ sources: {
50679
+ localOriginalRef: original.localRef ?? block.localFileRef,
50680
+ remoteOriginalRef: block.originalRef,
50681
+ previewUrl: block.mediaRef === "" || block.mediaRef === block.localFileRef ? void 0 : `/arkme-self/api/media?ref=${encodeURIComponent(block.mediaRef)}`
50682
+ },
50637
50683
  onNotice: showActionNotice
50638
50684
  }),
50639
50685
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -89604,7 +89650,7 @@ yoga yokohama you youtube yt yun za zappos zara zero zip zm zone zuerich zw
89604
89650
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeFileActions, {
89605
89651
  block: selected,
89606
89652
  original,
89607
- copySourceUrl: originalUrl,
89653
+ copySourceUrl: previewUrl ?? (selected.mediaRef === selected.localFileRef ? void 0 : `${mediaRoute$1}?ref=${encodeURIComponent(selected.mediaRef)}`),
89608
89654
  onImageCopyNotice: showActionNotice,
89609
89655
  showDownloadStatus: false,
89610
89656
  hideDownloadAfterSave: false,
@@ -129326,6 +129372,166 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
129326
129372
  });
129327
129373
  }
129328
129374
  //#endregion
129375
+ //#region src/client/source-tree.ts
129376
+ /** Return the complete visible hierarchy for a topic, from its root to itself. */
129377
+ function arkmeTopicPathNames(selectedSource, sources) {
129378
+ if (selectedSource === void 0 || selectedSource.kind !== "topic") return [];
129379
+ const sourceByRef = new Map(sources.map((source) => [source.sourceRef, source]));
129380
+ const sourceByHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
129381
+ const path = [];
129382
+ const visited = /* @__PURE__ */ new Set();
129383
+ let current = sourceByRef.get(selectedSource.sourceRef) ?? selectedSource;
129384
+ while (current !== void 0 && current.kind === "topic" && !visited.has(current.sourceRef)) {
129385
+ visited.add(current.sourceRef);
129386
+ path.unshift(current);
129387
+ const parentReference = current.parentTopicHierarchyKey ?? current.parentSourceRef;
129388
+ current = parentReference === void 0 ? void 0 : current.parentTopicHierarchyKey === void 0 ? sourceByRef.get(parentReference) : sourceByHierarchyKey.get(parentReference);
129389
+ }
129390
+ return path.map((source) => source.displayName);
129391
+ }
129392
+ /** A hierarchy move may not create a cycle or exceed the five-level topic limit. */
129393
+ function canMoveArkmeTopicToParent(source, nextParent, sources) {
129394
+ if (!arkmeSourceAllowsUserWrite(source) || nextParent !== void 0 && !arkmeSourceAllowsUserWrite(nextParent)) return false;
129395
+ if (source.kind !== "topic") return false;
129396
+ if (nextParent === void 0) return true;
129397
+ if (nextParent.kind !== "topic" || nextParent.sourceRef === source.sourceRef) return false;
129398
+ const byRef = new Map(sources.map((item) => [item.sourceRef, item]));
129399
+ const byHierarchyKey = new Map(sources.flatMap((item) => item.topicHierarchyKey === void 0 ? [] : [[item.topicHierarchyKey, item]]));
129400
+ const parentOf = (item) => item.parentTopicHierarchyKey === void 0 ? item.parentSourceRef === void 0 ? void 0 : byRef.get(item.parentSourceRef) : byHierarchyKey.get(item.parentTopicHierarchyKey);
129401
+ const targetAncestors = /* @__PURE__ */ new Set();
129402
+ let current = nextParent;
129403
+ while (current !== void 0 && !targetAncestors.has(current.sourceRef)) {
129404
+ targetAncestors.add(current.sourceRef);
129405
+ current = parentOf(current);
129406
+ }
129407
+ if (targetAncestors.has(source.sourceRef)) return false;
129408
+ const childRefsByParent = /* @__PURE__ */ new Map();
129409
+ for (const item of sources) {
129410
+ const parent = parentOf(item);
129411
+ if (parent === void 0) continue;
129412
+ const children = childRefsByParent.get(parent.sourceRef) ?? [];
129413
+ children.push(item);
129414
+ childRefsByParent.set(parent.sourceRef, children);
129415
+ }
129416
+ const subtreeDepth = (item, visited = /* @__PURE__ */ new Set()) => {
129417
+ if (visited.has(item.sourceRef)) return 0;
129418
+ visited.add(item.sourceRef);
129419
+ return 1 + Math.max(0, ...(childRefsByParent.get(item.sourceRef) ?? []).map((child) => subtreeDepth(child, visited)));
129420
+ };
129421
+ return targetAncestors.size + subtreeDepth(source) <= 5;
129422
+ }
129423
+ /** Build a stable tree while keeping paged children hidden until their known parent arrives. */
129424
+ function buildArkmeSourceTree(sources) {
129425
+ const sourcesByRef = new Map(sources.map((source) => [source.sourceRef, source]));
129426
+ const sourcesByTopicHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
129427
+ const nodesByRef = new Map(sources.map((source) => [source.sourceRef, {
129428
+ source,
129429
+ children: []
129430
+ }]));
129431
+ const roots = [];
129432
+ const validParent = (source) => {
129433
+ if (source.kind !== "topic") return void 0;
129434
+ const parentReference = source.parentTopicHierarchyKey ?? source.parentSourceRef;
129435
+ if (parentReference === void 0) return void 0;
129436
+ const parentByReference = (reference) => source.parentTopicHierarchyKey === void 0 ? sourcesByRef.get(reference) : sourcesByTopicHierarchyKey.get(reference);
129437
+ const seen = /* @__PURE__ */ new Set([source.topicHierarchyKey ?? source.sourceRef]);
129438
+ let parentRef = parentReference;
129439
+ let directParent;
129440
+ while (parentRef !== void 0) {
129441
+ if (seen.has(parentRef)) return void 0;
129442
+ seen.add(parentRef);
129443
+ const parent = parentByReference(parentRef);
129444
+ if (parent === void 0) return source.parentTopicHierarchyKey === void 0 ? void 0 : null;
129445
+ if (parent.kind !== "topic") return void 0;
129446
+ directParent ??= parent;
129447
+ parentRef = parent.parentTopicHierarchyKey ?? parent.parentSourceRef;
129448
+ }
129449
+ return directParent;
129450
+ };
129451
+ for (const source of sources) {
129452
+ const node = nodesByRef.get(source.sourceRef);
129453
+ const parent = validParent(source);
129454
+ if (parent === null) continue;
129455
+ const parentNode = parent === void 0 ? void 0 : nodesByRef.get(parent.sourceRef);
129456
+ if (parentNode === void 0) roots.push(node);
129457
+ else parentNode.children.push(node);
129458
+ }
129459
+ return roots;
129460
+ }
129461
+ const sourceNameCollator = new Intl.Collator("zh-CN", {
129462
+ numeric: true,
129463
+ sensitivity: "base"
129464
+ });
129465
+ function nonNegativeFinite(value) {
129466
+ return value !== void 0 && Number.isFinite(value) && value > 0 ? value : 0;
129467
+ }
129468
+ /**
129469
+ * Sort peers only, preserving every parent-child relationship. `custom` uses
129470
+ * the server's persisted sibling order, with the legacy name order as fallback.
129471
+ */
129472
+ function sortArkmeSourceTree(roots, sort) {
129473
+ const compare = (left, right) => {
129474
+ if (left.source.kind === "default_category") return right.source.kind === "default_category" ? 0 : -1;
129475
+ if (right.source.kind === "default_category") return 1;
129476
+ if (sort === "latest") {
129477
+ const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
129478
+ if (byLatest !== 0) return byLatest;
129479
+ const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
129480
+ if (byCount !== 0) return byCount;
129481
+ } else if (sort === "most") {
129482
+ const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
129483
+ if (byCount !== 0) return byCount;
129484
+ const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
129485
+ if (byLatest !== 0) return byLatest;
129486
+ } else {
129487
+ const leftOrder = nonNegativeFinite(left.source.siblingOrder);
129488
+ const rightOrder = nonNegativeFinite(right.source.siblingOrder);
129489
+ if (leftOrder > 0 || rightOrder > 0) {
129490
+ if (leftOrder <= 0) return 1;
129491
+ if (rightOrder <= 0) return -1;
129492
+ const byOrder = leftOrder - rightOrder;
129493
+ if (byOrder !== 0) return byOrder;
129494
+ }
129495
+ }
129496
+ return sourceNameCollator.compare(left.source.displayName, right.source.displayName);
129497
+ };
129498
+ return roots.map((node) => ({
129499
+ ...node,
129500
+ children: sortArkmeSourceTree(node.children, sort)
129501
+ })).sort(compare);
129502
+ }
129503
+ /** Total every topic from its direct records plus every descendant exactly once. */
129504
+ function aggregateArkmeSourceTreeRecordCounts(roots) {
129505
+ const totals = /* @__PURE__ */ new Map();
129506
+ const visit = (node) => {
129507
+ const total = nonNegativeFinite(node.source.recordCount) + node.children.reduce((sum, child) => sum + visit(child), 0);
129508
+ totals.set(node.source.sourceRef, total);
129509
+ return total;
129510
+ };
129511
+ roots.forEach(visit);
129512
+ return totals;
129513
+ }
129514
+ function flattenVisibleArkmeSourceTree(roots, collapsedSourceRefs) {
129515
+ const rows = [];
129516
+ const visit = (node, depth) => {
129517
+ const hasChildren = node.children.length > 0;
129518
+ const expanded = hasChildren && !collapsedSourceRefs.has(node.source.sourceRef);
129519
+ rows.push({
129520
+ source: node.source,
129521
+ depth,
129522
+ hasChildren,
129523
+ expanded
129524
+ });
129525
+ if (expanded) node.children.forEach((child) => {
129526
+ visit(child, depth + 1);
129527
+ });
129528
+ };
129529
+ roots.forEach((root) => {
129530
+ visit(root, 0);
129531
+ });
129532
+ return rows;
129533
+ }
129534
+ //#endregion
129329
129535
  //#region src/client/read-deadline.ts
129330
129536
  /** Bound interactive reads, including browser connection-queue time. Never retry writes here. */
129331
129537
  async function withArkmeReadDeadline(read, signal) {
@@ -138079,7 +138285,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
138079
138285
  }
138080
138286
  //#endregion
138081
138287
  //#region src/client/arko-pending-turn-store.ts
138082
- const STORAGE_KEY_PREFIX$2 = "arkme:arko:pending-turn:v1:";
138288
+ const STORAGE_KEY_PREFIX$3 = "arkme:arko:pending-turn:v1:";
138083
138289
  const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
138084
138290
  function defaultStorage() {
138085
138291
  try {
@@ -138089,7 +138295,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
138089
138295
  }
138090
138296
  }
138091
138297
  function storageKey$2(userId) {
138092
- return `${STORAGE_KEY_PREFIX$2}${String(userId)}`;
138298
+ return `${STORAGE_KEY_PREFIX$3}${String(userId)}`;
138093
138299
  }
138094
138300
  function optionalTrimmedString(value) {
138095
138301
  if (typeof value !== "string") return void 0;
@@ -148794,166 +149000,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
148794
149000
  //#region assets/recording-icons/icon_send_to_self_sidebar_dark.svg
148795
149001
  var icon_send_to_self_sidebar_dark_default = "PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iMTUiIGN5PSIxNSIgcj0iMTUiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuMSIvPgo8Y2lyY2xlIGN4PSIxNSIgY3k9IjE1IiByPSIxNC43NSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLW9wYWNpdHk9IjAuMTUiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0xNS43OTM0IDExSDExLjUyNjdDMTEuMzUzNCAxMSAxMS4xODY3IDExLjAwNjcgMTEuMDI2NyAxMS4wMjY3QzkuMjMzMzcgMTEuMTggOC4zMzMzNyAxMi4yNCA4LjMzMzM3IDE0LjE5MzNWMTYuODZDOC4zMzMzNyAxOS41MjY3IDkuNDAwMDQgMjAuMDUzMyAxMS41MjY3IDIwLjA1MzNIMTEuNzkzNEMxMS45NCAyMC4wNTMzIDEyLjEzMzQgMjAuMTUzMyAxMi4yMiAyMC4yNjY3TDEzLjAyIDIxLjMzMzNDMTMuMzczNCAyMS44MDY3IDEzLjk0NjcgMjEuODA2NyAxNC4zIDIxLjMzMzNMMTUuMSAyMC4yNjY3QzE1LjIgMjAuMTMzMyAxNS4zNiAyMC4wNTMzIDE1LjUyNjcgMjAuMDUzM0gxNS43OTM0QzE3Ljc0NjcgMjAuMDUzMyAxOC44MDY3IDE5LjE2IDE4Ljk2IDE3LjM2QzE4Ljk4IDE3LjIgMTguOTg2NyAxNy4wMzMzIDE4Ljk4NjcgMTYuODZWMTQuMTkzM0MxOC45ODY3IDEyLjA2NjcgMTcuOTIgMTEgMTUuNzkzNCAxMVpNMTEuMzMzNCAxNi4zMzMzQzEwLjk2IDE2LjMzMzMgMTAuNjY2NyAxNi4wMzMzIDEwLjY2NjcgMTUuNjY2N0MxMC42NjY3IDE1LjMgMTAuOTY2NyAxNSAxMS4zMzM0IDE1QzExLjcgMTUgMTIgMTUuMyAxMiAxNS42NjY3QzEyIDE2LjAzMzMgMTEuNyAxNi4zMzMzIDExLjMzMzQgMTYuMzMzM1pNMTMuNjYgMTYuMzMzM0MxMy4yODY3IDE2LjMzMzMgMTIuOTkzNCAxNi4wMzMzIDEyLjk5MzQgMTUuNjY2N0MxMi45OTM0IDE1LjMgMTMuMjkzNCAxNSAxMy42NiAxNUMxNC4wMjY3IDE1IDE0LjMyNjcgMTUuMyAxNC4zMjY3IDE1LjY2NjdDMTQuMzI2NyAxNi4wMzMzIDE0LjAzMzQgMTYuMzMzMyAxMy42NiAxNi4zMzMzWk0xNS45OTM0IDE2LjMzMzNDMTUuNjIgMTYuMzMzMyAxNS4zMjY3IDE2LjAzMzMgMTUuMzI2NyAxNS42NjY3QzE1LjMyNjcgMTUuMyAxNS42MjY3IDE1IDE1Ljk5MzQgMTVDMTYuMzYgMTUgMTYuNjYgMTUuMyAxNi42NiAxNS42NjY3QzE2LjY2IDE2LjAzMzMgMTYuMzYgMTYuMzMzMyAxNS45OTM0IDE2LjMzMzNaIiBmaWxsPSIjOUVEREE0Ii8+CjxwYXRoIGQ9Ik0yMS42NTMzIDExLjUyNjZWMTQuMTkzM0MyMS42NTMzIDE1LjUyNjYgMjEuMjQgMTYuNDMzMyAyMC40MTMzIDE2LjkzMzNDMjAuMjEzMyAxNy4wNTMzIDE5Ljk4IDE2Ljg5MzMgMTkuOTggMTYuNjU5OUwxOS45ODY2IDE0LjE5MzNDMTkuOTg2NiAxMS41MjY2IDE4LjQ2IDkuOTk5OTIgMTUuNzkzMyA5Ljk5OTkyTDExLjczMzMgMTAuMDA2NkMxMS41IDEwLjAwNjYgMTEuMzQgOS43NzMyNSAxMS40NiA5LjU3MzI1QzExLjk2IDguNzQ2NTkgMTIuODY2NiA4LjMzMzI1IDE0LjE5MzMgOC4zMzMyNUgxOC40NkMyMC41ODY2IDguMzMzMjUgMjEuNjUzMyA5LjM5OTkyIDIxLjY1MzMgMTEuNTI2NloiIGZpbGw9IiM5RUREQTQiLz4KPC9zdmc+Cg==";
148796
149002
  //#endregion
148797
- //#region src/client/source-tree.ts
148798
- /** Return the complete visible hierarchy for a topic, from its root to itself. */
148799
- function arkmeTopicPathNames(selectedSource, sources) {
148800
- if (selectedSource === void 0 || selectedSource.kind !== "topic") return [];
148801
- const sourceByRef = new Map(sources.map((source) => [source.sourceRef, source]));
148802
- const sourceByHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
148803
- const path = [];
148804
- const visited = /* @__PURE__ */ new Set();
148805
- let current = sourceByRef.get(selectedSource.sourceRef) ?? selectedSource;
148806
- while (current !== void 0 && current.kind === "topic" && !visited.has(current.sourceRef)) {
148807
- visited.add(current.sourceRef);
148808
- path.unshift(current);
148809
- const parentReference = current.parentTopicHierarchyKey ?? current.parentSourceRef;
148810
- current = parentReference === void 0 ? void 0 : current.parentTopicHierarchyKey === void 0 ? sourceByRef.get(parentReference) : sourceByHierarchyKey.get(parentReference);
148811
- }
148812
- return path.map((source) => source.displayName);
148813
- }
148814
- /** A hierarchy move may not create a cycle or exceed the five-level topic limit. */
148815
- function canMoveArkmeTopicToParent(source, nextParent, sources) {
148816
- if (!arkmeSourceAllowsUserWrite(source) || nextParent !== void 0 && !arkmeSourceAllowsUserWrite(nextParent)) return false;
148817
- if (source.kind !== "topic") return false;
148818
- if (nextParent === void 0) return true;
148819
- if (nextParent.kind !== "topic" || nextParent.sourceRef === source.sourceRef) return false;
148820
- const byRef = new Map(sources.map((item) => [item.sourceRef, item]));
148821
- const byHierarchyKey = new Map(sources.flatMap((item) => item.topicHierarchyKey === void 0 ? [] : [[item.topicHierarchyKey, item]]));
148822
- const parentOf = (item) => item.parentTopicHierarchyKey === void 0 ? item.parentSourceRef === void 0 ? void 0 : byRef.get(item.parentSourceRef) : byHierarchyKey.get(item.parentTopicHierarchyKey);
148823
- const targetAncestors = /* @__PURE__ */ new Set();
148824
- let current = nextParent;
148825
- while (current !== void 0 && !targetAncestors.has(current.sourceRef)) {
148826
- targetAncestors.add(current.sourceRef);
148827
- current = parentOf(current);
148828
- }
148829
- if (targetAncestors.has(source.sourceRef)) return false;
148830
- const childRefsByParent = /* @__PURE__ */ new Map();
148831
- for (const item of sources) {
148832
- const parent = parentOf(item);
148833
- if (parent === void 0) continue;
148834
- const children = childRefsByParent.get(parent.sourceRef) ?? [];
148835
- children.push(item);
148836
- childRefsByParent.set(parent.sourceRef, children);
148837
- }
148838
- const subtreeDepth = (item, visited = /* @__PURE__ */ new Set()) => {
148839
- if (visited.has(item.sourceRef)) return 0;
148840
- visited.add(item.sourceRef);
148841
- return 1 + Math.max(0, ...(childRefsByParent.get(item.sourceRef) ?? []).map((child) => subtreeDepth(child, visited)));
148842
- };
148843
- return targetAncestors.size + subtreeDepth(source) <= 5;
148844
- }
148845
- /** Build a stable tree while keeping paged children hidden until their known parent arrives. */
148846
- function buildArkmeSourceTree(sources) {
148847
- const sourcesByRef = new Map(sources.map((source) => [source.sourceRef, source]));
148848
- const sourcesByTopicHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
148849
- const nodesByRef = new Map(sources.map((source) => [source.sourceRef, {
148850
- source,
148851
- children: []
148852
- }]));
148853
- const roots = [];
148854
- const validParent = (source) => {
148855
- if (source.kind !== "topic") return void 0;
148856
- const parentReference = source.parentTopicHierarchyKey ?? source.parentSourceRef;
148857
- if (parentReference === void 0) return void 0;
148858
- const parentByReference = (reference) => source.parentTopicHierarchyKey === void 0 ? sourcesByRef.get(reference) : sourcesByTopicHierarchyKey.get(reference);
148859
- const seen = /* @__PURE__ */ new Set([source.topicHierarchyKey ?? source.sourceRef]);
148860
- let parentRef = parentReference;
148861
- let directParent;
148862
- while (parentRef !== void 0) {
148863
- if (seen.has(parentRef)) return void 0;
148864
- seen.add(parentRef);
148865
- const parent = parentByReference(parentRef);
148866
- if (parent === void 0) return source.parentTopicHierarchyKey === void 0 ? void 0 : null;
148867
- if (parent.kind !== "topic") return void 0;
148868
- directParent ??= parent;
148869
- parentRef = parent.parentTopicHierarchyKey ?? parent.parentSourceRef;
148870
- }
148871
- return directParent;
148872
- };
148873
- for (const source of sources) {
148874
- const node = nodesByRef.get(source.sourceRef);
148875
- const parent = validParent(source);
148876
- if (parent === null) continue;
148877
- const parentNode = parent === void 0 ? void 0 : nodesByRef.get(parent.sourceRef);
148878
- if (parentNode === void 0) roots.push(node);
148879
- else parentNode.children.push(node);
148880
- }
148881
- return roots;
148882
- }
148883
- const sourceNameCollator = new Intl.Collator("zh-CN", {
148884
- numeric: true,
148885
- sensitivity: "base"
148886
- });
148887
- function nonNegativeFinite(value) {
148888
- return value !== void 0 && Number.isFinite(value) && value > 0 ? value : 0;
148889
- }
148890
- /**
148891
- * Sort peers only, preserving every parent-child relationship. `custom` uses
148892
- * the server's persisted sibling order, with the legacy name order as fallback.
148893
- */
148894
- function sortArkmeSourceTree(roots, sort) {
148895
- const compare = (left, right) => {
148896
- if (left.source.kind === "default_category") return right.source.kind === "default_category" ? 0 : -1;
148897
- if (right.source.kind === "default_category") return 1;
148898
- if (sort === "latest") {
148899
- const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
148900
- if (byLatest !== 0) return byLatest;
148901
- const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
148902
- if (byCount !== 0) return byCount;
148903
- } else if (sort === "most") {
148904
- const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
148905
- if (byCount !== 0) return byCount;
148906
- const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
148907
- if (byLatest !== 0) return byLatest;
148908
- } else {
148909
- const leftOrder = nonNegativeFinite(left.source.siblingOrder);
148910
- const rightOrder = nonNegativeFinite(right.source.siblingOrder);
148911
- if (leftOrder > 0 || rightOrder > 0) {
148912
- if (leftOrder <= 0) return 1;
148913
- if (rightOrder <= 0) return -1;
148914
- const byOrder = leftOrder - rightOrder;
148915
- if (byOrder !== 0) return byOrder;
148916
- }
148917
- }
148918
- return sourceNameCollator.compare(left.source.displayName, right.source.displayName);
148919
- };
148920
- return roots.map((node) => ({
148921
- ...node,
148922
- children: sortArkmeSourceTree(node.children, sort)
148923
- })).sort(compare);
148924
- }
148925
- /** Total every topic from its direct records plus every descendant exactly once. */
148926
- function aggregateArkmeSourceTreeRecordCounts(roots) {
148927
- const totals = /* @__PURE__ */ new Map();
148928
- const visit = (node) => {
148929
- const total = nonNegativeFinite(node.source.recordCount) + node.children.reduce((sum, child) => sum + visit(child), 0);
148930
- totals.set(node.source.sourceRef, total);
148931
- return total;
148932
- };
148933
- roots.forEach(visit);
148934
- return totals;
148935
- }
148936
- function flattenVisibleArkmeSourceTree(roots, collapsedSourceRefs) {
148937
- const rows = [];
148938
- const visit = (node, depth) => {
148939
- const hasChildren = node.children.length > 0;
148940
- const expanded = hasChildren && !collapsedSourceRefs.has(node.source.sourceRef);
148941
- rows.push({
148942
- source: node.source,
148943
- depth,
148944
- hasChildren,
148945
- expanded
148946
- });
148947
- if (expanded) node.children.forEach((child) => {
148948
- visit(child, depth + 1);
148949
- });
148950
- };
148951
- roots.forEach((root) => {
148952
- visit(root, 0);
148953
- });
148954
- return rows;
148955
- }
148956
- //#endregion
148957
149003
  //#region src/client/recordings/recording-forward-attempt.ts
148958
149004
  function recordingForwardTargetKey(target) {
148959
149005
  if (target.kind === "private_chat" || target.kind === "group_chat") return target.sourceKey ?? "";
@@ -179988,8 +180034,8 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
179988
180034
  //#endregion
179989
180035
  //#region src/client/marketplace-sort-preference.ts
179990
180036
  const DEFAULT_MARKETPLACE_SORT = "rating";
179991
- const STORAGE_KEY_PREFIX$1 = "dsh-arkme:marketplace-sort:v1:";
179992
- function preferenceStorage(storage) {
180037
+ const STORAGE_KEY_PREFIX$2 = "dsh-arkme:marketplace-sort:v1:";
180038
+ function preferenceStorage$1(storage) {
179993
180039
  if (storage !== void 0) return storage;
179994
180040
  try {
179995
180041
  return typeof window === "undefined" ? void 0 : window.localStorage;
@@ -179997,27 +180043,27 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
179997
180043
  return;
179998
180044
  }
179999
180045
  }
180000
- function preferenceKey(userId) {
180001
- return Number.isSafeInteger(userId) && (userId ?? 0) > 0 ? `${STORAGE_KEY_PREFIX$1}user:${String(userId)}` : `${STORAGE_KEY_PREFIX$1}anonymous`;
180046
+ function preferenceKey$1(userId) {
180047
+ return Number.isSafeInteger(userId) && (userId ?? 0) > 0 ? `${STORAGE_KEY_PREFIX$2}user:${String(userId)}` : `${STORAGE_KEY_PREFIX$2}anonymous`;
180002
180048
  }
180003
180049
  function isMarketplaceSort(value) {
180004
180050
  return value === "rating" || value === "comments" || value === "opens" || value === "created_at";
180005
180051
  }
180006
180052
  function readMarketplaceSortPreference(userId, storage) {
180007
- const target = preferenceStorage(storage);
180053
+ const target = preferenceStorage$1(storage);
180008
180054
  if (target === void 0) return DEFAULT_MARKETPLACE_SORT;
180009
180055
  try {
180010
- const value = target.getItem(preferenceKey(userId));
180056
+ const value = target.getItem(preferenceKey$1(userId));
180011
180057
  return isMarketplaceSort(value) ? value : DEFAULT_MARKETPLACE_SORT;
180012
180058
  } catch {
180013
180059
  return DEFAULT_MARKETPLACE_SORT;
180014
180060
  }
180015
180061
  }
180016
180062
  function writeMarketplaceSortPreference(userId, sort, storage) {
180017
- const target = preferenceStorage(storage);
180063
+ const target = preferenceStorage$1(storage);
180018
180064
  if (target === void 0) return;
180019
180065
  try {
180020
- target.setItem(preferenceKey(userId), sort);
180066
+ target.setItem(preferenceKey$1(userId), sort);
180021
180067
  } catch {}
180022
180068
  }
180023
180069
  //#endregion
@@ -185036,6 +185082,43 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
185036
185082
  });
185037
185083
  }
185038
185084
  //#endregion
185085
+ //#region src/client/self-topic-sort-preference.ts
185086
+ const DEFAULT_SELF_TOPIC_SORT = "latest";
185087
+ const STORAGE_KEY_PREFIX$1 = "dsh-arkme:self-topic-sort:v1:user:";
185088
+ function preferenceStorage(storage) {
185089
+ if (storage !== void 0) return storage;
185090
+ try {
185091
+ return typeof window === "undefined" ? void 0 : window.localStorage;
185092
+ } catch {
185093
+ return;
185094
+ }
185095
+ }
185096
+ function preferenceKey(userId) {
185097
+ return Number.isSafeInteger(userId) && (userId ?? 0) > 0 ? `${STORAGE_KEY_PREFIX$1}${String(userId)}` : void 0;
185098
+ }
185099
+ function isSelfTopicSort(value) {
185100
+ return value === "latest" || value === "most" || value === "custom";
185101
+ }
185102
+ function readSelfTopicSortPreference(userId, storage) {
185103
+ const key = preferenceKey(userId);
185104
+ const target = preferenceStorage(storage);
185105
+ if (key === void 0 || target === void 0) return DEFAULT_SELF_TOPIC_SORT;
185106
+ try {
185107
+ const value = target.getItem(key);
185108
+ return isSelfTopicSort(value) ? value : DEFAULT_SELF_TOPIC_SORT;
185109
+ } catch {
185110
+ return DEFAULT_SELF_TOPIC_SORT;
185111
+ }
185112
+ }
185113
+ function writeSelfTopicSortPreference(userId, sort, storage) {
185114
+ const key = preferenceKey(userId);
185115
+ const target = preferenceStorage(storage);
185116
+ if (key === void 0 || target === void 0) return;
185117
+ try {
185118
+ target.setItem(key, sort);
185119
+ } catch {}
185120
+ }
185121
+ //#endregion
185039
185122
  //#region src/client/conversation-timeline-read-port.ts
185040
185123
  const conversationTimelineReadPort = { readPage: async (sourceRef, cursor, signal) => await callArkme("source.timeline", {
185041
185124
  sourceRef,
@@ -190015,6 +190098,23 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190015
190098
  }
190016
190099
  //#endregion
190017
190100
  //#region src/client/ArkmeSourceBreadcrumb.tsx
190101
+ /** Split a topic row into a narrow reorder edge and a generous nesting center. */
190102
+ function arkmeTopicDropPosition(clientY, rect) {
190103
+ if (rect.height <= 0) return "into";
190104
+ const ratio = (clientY - rect.top) / rect.height;
190105
+ if (ratio < .22) return "before";
190106
+ if (ratio > .78) return "after";
190107
+ return "into";
190108
+ }
190109
+ /** Return one frame of proportional list scrolling while a drag hugs an edge. */
190110
+ function arkmeTopicDragAutoScrollDelta(clientX, clientY, rect, edgeSize = 44, minimumSpeed = 4, maximumSpeed = 18) {
190111
+ if (clientX < rect.left || clientX > rect.right || clientY < rect.top || clientY > rect.bottom) return 0;
190112
+ const safeEdgeSize = Math.max(1, Math.min(edgeSize, (rect.bottom - rect.top) / 2));
190113
+ const speed = (distance) => Math.round(minimumSpeed + (maximumSpeed - minimumSpeed) * Math.min(1, Math.max(0, distance / safeEdgeSize)));
190114
+ if (clientY < rect.top + safeEdgeSize) return -speed(rect.top + safeEdgeSize - clientY);
190115
+ if (clientY > rect.bottom - safeEdgeSize) return speed(clientY - (rect.bottom - safeEdgeSize));
190116
+ return 0;
190117
+ }
190018
190118
  const colors$4 = {
190019
190119
  text: "#171923",
190020
190120
  secondary: "#6f747d",
@@ -190160,10 +190260,48 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190160
190260
  borderRadius: 7
190161
190261
  },
190162
190262
  topicRowHover: { background: "#f3f4f7" },
190163
- topicRowDropTarget: { background: "#f3f5ff" },
190164
- topicRowDropBefore: { boxShadow: "inset 0 2px 0 #5870d8" },
190165
- topicRowDropAfter: { boxShadow: "inset 0 -2px 0 #5870d8" },
190166
- topicRowDropInto: { outline: "1px solid #8fa0e9" },
190263
+ topicRowSelected: {
190264
+ background: colors$4.selected,
190265
+ fontWeight: 600
190266
+ },
190267
+ topicHierarchyGuide: {
190268
+ position: "absolute",
190269
+ top: 0,
190270
+ bottom: 0,
190271
+ width: 1,
190272
+ background: "#e7e9ed",
190273
+ pointerEvents: "none"
190274
+ },
190275
+ topicRowDropInto: {
190276
+ background: "#eef2ff",
190277
+ outline: "1px solid #8295e5",
190278
+ outlineOffset: -1
190279
+ },
190280
+ topicDropLine: {
190281
+ position: "absolute",
190282
+ zIndex: 2,
190283
+ right: 4,
190284
+ height: 2,
190285
+ borderRadius: 0,
190286
+ background: "#5870d8",
190287
+ pointerEvents: "none"
190288
+ },
190289
+ topicDropIntoBadge: {
190290
+ position: "absolute",
190291
+ zIndex: 3,
190292
+ right: 6,
190293
+ top: 5,
190294
+ height: 22,
190295
+ display: "inline-flex",
190296
+ alignItems: "center",
190297
+ padding: "0 6px",
190298
+ borderRadius: 5,
190299
+ background: "#dce4ff",
190300
+ color: "#445bbd",
190301
+ fontSize: 10,
190302
+ fontWeight: 600,
190303
+ pointerEvents: "none"
190304
+ },
190167
190305
  topicToggle: {
190168
190306
  width: 24,
190169
190307
  height: 28,
@@ -190202,10 +190340,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190202
190340
  textAlign: "left",
190203
190341
  cursor: "pointer"
190204
190342
  },
190205
- topicSelectSelected: {
190206
- background: colors$4.selected,
190207
- fontWeight: 600
190208
- },
190209
190343
  topicName: {
190210
190344
  minWidth: 0,
190211
190345
  flex: 1,
@@ -190477,11 +190611,11 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190477
190611
  function topicCountLabel(count) {
190478
190612
  return count === void 0 ? "…" : count.toLocaleString("zh-CN");
190479
190613
  }
190480
- function ArkmeSourceBreadcrumb({ selectedSource, sources, loading = false, error, onSelect, onSelectAggregate, onCreateTopic, onCreateChildTopic, onRenameTopic, onDissolveTopic, onRetry, onMoveTopic, activeDissolve, tourOpen }) {
190614
+ function ArkmeSourceBreadcrumb({ userId, selectedSource, sources, loading = false, error, onSelect, onSelectAggregate, onCreateTopic, onCreateChildTopic, onRenameTopic, onDissolveTopic, onRetry, onMoveTopic, activeDissolve, tourOpen }) {
190481
190615
  const [manualOpen, setOpen] = (0, react$1.useState)(false);
190482
190616
  const open = tourOpen ?? manualOpen;
190483
190617
  const [collapsedSourceRefs, setCollapsedSourceRefs] = (0, react$1.useState)(() => /* @__PURE__ */ new Set());
190484
- const [sort, setSort] = (0, react$1.useState)("latest");
190618
+ const [sort, setSort] = (0, react$1.useState)(() => readSelfTopicSortPreference(userId));
190485
190619
  const [draggingSourceRef, setDraggingSourceRef] = (0, react$1.useState)();
190486
190620
  const [hoveredSourceRef, setHoveredSourceRef] = (0, react$1.useState)();
190487
190621
  const [topicMenuSource, setTopicMenuSource] = (0, react$1.useState)();
@@ -190495,10 +190629,15 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190495
190629
  const [dropPlan, setDropPlan] = (0, react$1.useState)();
190496
190630
  const [movingTopic, setMovingTopic] = (0, react$1.useState)(false);
190497
190631
  const [moveError, setMoveError] = (0, react$1.useState)("");
190498
- const rootRef = (0, react$1.useRef)(null);
190632
+ const selectorRef = (0, react$1.useRef)(null);
190633
+ const menuRef = (0, react$1.useRef)(null);
190499
190634
  const menuListRef = (0, react$1.useRef)(null);
190500
190635
  const pendingSelectedFocusRef = (0, react$1.useRef)(false);
190501
190636
  const dragStartXRef = (0, react$1.useRef)(0);
190637
+ const dragPointerRef = (0, react$1.useRef)();
190638
+ const dragAutoScrollFrameRef = (0, react$1.useRef)();
190639
+ const dragAutoExpandTimerRef = (0, react$1.useRef)();
190640
+ const dragAutoExpandSourceRef = (0, react$1.useRef)();
190502
190641
  const observedActiveDissolveRef = (0, react$1.useRef)(false);
190503
190642
  const topicRoots = (0, react$1.useMemo)(() => sortArkmeSourceTree(buildArkmeSourceTree(arkmeSelfDirectorySources(sources)), sort), [sort, sources]);
190504
190643
  const rows = (0, react$1.useMemo)(() => flattenVisibleArkmeSourceTree(topicRoots, collapsedSourceRefs), [collapsedSourceRefs, topicRoots]);
@@ -190528,6 +190667,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190528
190667
  setTopicMutationError("");
190529
190668
  setDissolveDialogOpen(true);
190530
190669
  };
190670
+ (0, react$1.useEffect)(() => {
190671
+ setSort(readSelfTopicSortPreference(userId));
190672
+ }, [userId]);
190531
190673
  (0, react$1.useEffect)(() => {
190532
190674
  if (activeDissolveRunning && activeDissolve !== void 0) {
190533
190675
  observedActiveDissolveRef.current = true;
@@ -190555,24 +190697,75 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190555
190697
  const currentParent = currentParentOf(draggingSource);
190556
190698
  return currentParent?.sourceRef !== nextParent?.sourceRef || nextSiblingOf(draggingSource, currentParent)?.sourceRef !== insertBefore?.sourceRef;
190557
190699
  };
190700
+ const stopTopicDragAutoScroll = () => {
190701
+ dragPointerRef.current = void 0;
190702
+ if (dragAutoScrollFrameRef.current !== void 0) cancelAnimationFrame(dragAutoScrollFrameRef.current);
190703
+ dragAutoScrollFrameRef.current = void 0;
190704
+ };
190705
+ const runTopicDragAutoScroll = () => {
190706
+ dragAutoScrollFrameRef.current = void 0;
190707
+ const list = menuListRef.current;
190708
+ const pointer = dragPointerRef.current;
190709
+ if (list === null || pointer === void 0) return;
190710
+ const delta = arkmeTopicDragAutoScrollDelta(pointer.clientX, pointer.clientY, list.getBoundingClientRect());
190711
+ if (delta === 0) return;
190712
+ const before = list.scrollTop;
190713
+ list.scrollTop += delta;
190714
+ if (list.scrollTop === before) return;
190715
+ dragAutoScrollFrameRef.current = requestAnimationFrame(runTopicDragAutoScroll);
190716
+ };
190717
+ const updateTopicDragAutoScroll = (clientX, clientY) => {
190718
+ dragPointerRef.current = {
190719
+ clientX,
190720
+ clientY
190721
+ };
190722
+ if (dragAutoScrollFrameRef.current === void 0) dragAutoScrollFrameRef.current = requestAnimationFrame(runTopicDragAutoScroll);
190723
+ };
190724
+ const stopTopicAutoExpand = () => {
190725
+ if (dragAutoExpandTimerRef.current !== void 0) clearTimeout(dragAutoExpandTimerRef.current);
190726
+ dragAutoExpandTimerRef.current = void 0;
190727
+ dragAutoExpandSourceRef.current = void 0;
190728
+ };
190729
+ const scheduleTopicAutoExpand = (row, plan) => {
190730
+ const expandable = row.hasChildren || row.source.hasPendingChildren === true;
190731
+ if (plan?.into !== true || !expandable || row.expanded) {
190732
+ stopTopicAutoExpand();
190733
+ return;
190734
+ }
190735
+ if (dragAutoExpandSourceRef.current === row.source.sourceRef) return;
190736
+ stopTopicAutoExpand();
190737
+ dragAutoExpandSourceRef.current = row.source.sourceRef;
190738
+ dragAutoExpandTimerRef.current = setTimeout(() => {
190739
+ setCollapsedSourceRefs((current) => {
190740
+ if (!current.has(row.source.sourceRef)) return current;
190741
+ const next = new Set(current);
190742
+ next.delete(row.source.sourceRef);
190743
+ return next;
190744
+ });
190745
+ dragAutoExpandTimerRef.current = void 0;
190746
+ dragAutoExpandSourceRef.current = void 0;
190747
+ }, 500);
190748
+ };
190558
190749
  const planMoveAtRow = (row, clientX, clientY, rect) => {
190559
190750
  if (row.source.kind === "default_category" || !arkmeSourceAllowsUserWrite(row.source)) return void 0;
190560
190751
  if (draggingSource === void 0) return void 0;
190561
190752
  const horizontalDelta = clientX - dragStartXRef.current;
190562
- if (horizontalDelta >= 24) {
190753
+ const position = arkmeTopicDropPosition(clientY, rect);
190754
+ const targetParent = currentParentOf(row.source);
190755
+ const outdenting = horizontalDelta <= -24 && targetParent !== void 0;
190756
+ if (position === "into" && !outdenting) {
190563
190757
  if (!canMoveTo(row.source, void 0)) return void 0;
190564
190758
  return {
190565
190759
  parent: row.source,
190566
190760
  insertBefore: void 0,
190567
190761
  indicatorSourceRef: row.source.sourceRef,
190762
+ indicatorDepth: row.depth + 1,
190568
190763
  before: false,
190569
190764
  into: true
190570
190765
  };
190571
190766
  }
190572
- const targetParent = currentParentOf(row.source);
190573
- const outdenting = horizontalDelta <= -24 && targetParent !== void 0;
190574
190767
  const parent = outdenting ? currentParentOf(targetParent) : targetParent;
190575
- const before = clientY < rect.top + rect.height / 2;
190768
+ const before = position === "before";
190576
190769
  const anchor = outdenting ? targetParent : row.source;
190577
190770
  const insertBefore = before ? anchor : nextSiblingOf(anchor, parent);
190578
190771
  if (!canMoveTo(parent, insertBefore)) return void 0;
@@ -190580,6 +190773,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190580
190773
  parent,
190581
190774
  insertBefore,
190582
190775
  indicatorSourceRef: row.source.sourceRef,
190776
+ indicatorDepth: outdenting ? Math.max(0, row.depth - 1) : row.depth,
190583
190777
  before,
190584
190778
  into: false
190585
190779
  };
@@ -190587,6 +190781,14 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190587
190781
  const finishTopicMove = (plan) => {
190588
190782
  if (plan === void 0 || !canMoveTo(plan.parent, plan.insertBefore) || draggingSource === void 0 || onMoveTopic === void 0) return;
190589
190783
  const movedSource = draggingSource;
190784
+ stopTopicDragAutoScroll();
190785
+ stopTopicAutoExpand();
190786
+ if (plan.into && plan.parent !== void 0) setCollapsedSourceRefs((current) => {
190787
+ if (!current.has(plan.parent.sourceRef)) return current;
190788
+ const next = new Set(current);
190789
+ next.delete(plan.parent.sourceRef);
190790
+ return next;
190791
+ });
190590
190792
  setMovingTopic(true);
190591
190793
  setMoveError("");
190592
190794
  setDropPlan(void 0);
@@ -190597,6 +190799,10 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190597
190799
  setMovingTopic(false);
190598
190800
  });
190599
190801
  };
190802
+ (0, react$1.useEffect)(() => () => {
190803
+ stopTopicDragAutoScroll();
190804
+ stopTopicAutoExpand();
190805
+ }, []);
190600
190806
  const revealSelectedTopic = () => {
190601
190807
  if (selectedRef === void 0) {
190602
190808
  menuListRef.current?.scrollTo({ top: 0 });
@@ -190636,10 +190842,10 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190636
190842
  if (!open) return;
190637
190843
  const closeOutside = (event) => {
190638
190844
  if (tourOpen !== void 0) return;
190639
- if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {
190640
- setOpen(false);
190641
- setTopicMenuSource(void 0);
190642
- }
190845
+ if (!(event.target instanceof Node)) return;
190846
+ if (selectorRef.current?.contains(event.target) || menuRef.current?.contains(event.target)) return;
190847
+ setOpen(false);
190848
+ setTopicMenuSource(void 0);
190643
190849
  };
190644
190850
  const closeEscape = (event) => {
190645
190851
  if (tourOpen !== void 0) return;
@@ -190732,7 +190938,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190732
190938
  });
190733
190939
  };
190734
190940
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
190735
- ref: rootRef,
190736
190941
  "aria-label": "发给自己主题",
190737
190942
  style: styles$13.breadcrumb,
190738
190943
  children: [
@@ -190742,6 +190947,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190742
190947
  children: "发给自己"
190743
190948
  }),
190744
190949
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190950
+ ref: selectorRef,
190745
190951
  type: "button",
190746
190952
  "aria-label": "选择主题",
190747
190953
  "aria-haspopup": "tree",
@@ -190803,6 +191009,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190803
191009
  }), activeDissolveLabel]
190804
191010
  }),
190805
191011
  open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
191012
+ ref: menuRef,
190806
191013
  role: "tree",
190807
191014
  "aria-label": "主题",
190808
191015
  "data-arkme-self-topic-menu": true,
@@ -190828,6 +191035,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190828
191035
  },
190829
191036
  onClick: () => {
190830
191037
  setSort(value);
191038
+ writeSelfTopicSortPreference(userId, value);
190831
191039
  revealSelectedTopic();
190832
191040
  },
190833
191041
  children: label
@@ -190842,6 +191050,20 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190842
191050
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
190843
191051
  ref: menuListRef,
190844
191052
  style: styles$13.menuList,
191053
+ onDragOver: (event) => {
191054
+ if (!customDragEnabled || draggingSource === void 0) return;
191055
+ updateTopicDragAutoScroll(event.clientX, event.clientY);
191056
+ },
191057
+ onDragLeave: (event) => {
191058
+ if (event.relatedTarget instanceof Node && event.currentTarget.contains(event.relatedTarget)) return;
191059
+ stopTopicDragAutoScroll();
191060
+ stopTopicAutoExpand();
191061
+ setDropPlan(void 0);
191062
+ },
191063
+ onDrop: () => {
191064
+ stopTopicDragAutoScroll();
191065
+ stopTopicAutoExpand();
191066
+ },
190845
191067
  children: [
190846
191068
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190847
191069
  type: "button",
@@ -190874,24 +191096,34 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190874
191096
  const isSelected = selectedRef === row.source.sourceRef;
190875
191097
  const isHovered = hoveredSourceRef === row.source.sourceRef;
190876
191098
  const isDefaultCategory = row.source.kind === "default_category";
191099
+ const canDragTopic = customDragEnabled && !isDefaultCategory && arkmeSourceAllowsUserWrite(row.source);
190877
191100
  const canCreateChild = arkmeSourceAllowsUserWrite(row.source) && !isDefaultCategory && onCreateChildTopic !== void 0 && row.depth + 1 < 5;
190878
191101
  const canManageTopic = arkmeSourceAllowsUserWrite(row.source) && !isDefaultCategory && (canCreateChild || onRenameTopic !== void 0 || onDissolveTopic !== void 0);
190879
- const showActions = isHovered && canManageTopic;
191102
+ const showActions = isHovered && canManageTopic && draggingSource === void 0;
190880
191103
  const manageMenuOpen = topicMenuSource?.sourceRef === row.source.sourceRef;
191104
+ const rowDropPlan = dropPlan?.indicatorSourceRef === row.source.sourceRef ? dropPlan : void 0;
190881
191105
  const displayedCount = (row.hasChildren || row.source.hasPendingChildren === true) && !countsComplete ? void 0 : aggregateTopicCounts.get(row.source.sourceRef) ?? topicDirectRecordCount(row.source);
191106
+ const depthInset = row.depth * 16;
190882
191107
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
190883
191108
  role: "treeitem",
190884
191109
  "aria-level": row.depth + 1,
190885
191110
  "aria-selected": selectedRef === row.source.sourceRef,
190886
191111
  ...row.hasChildren ? { "aria-expanded": row.expanded } : {},
191112
+ draggable: canDragTopic,
190887
191113
  "data-arkme-self-topic-tree-row": "true",
190888
191114
  "data-arkme-self-topic-tree-row-ref": row.source.sourceRef,
191115
+ "data-arkme-self-topic-hit-region": "true",
190889
191116
  style: {
190890
191117
  ...styles$13.topicRow,
190891
- paddingLeft: row.depth * 16,
191118
+ marginLeft: depthInset,
191119
+ width: `calc(100% - ${String(depthInset)}px)`,
190892
191120
  ...isHovered && !isSelected ? styles$13.topicRowHover : {},
190893
- ...dropPlan?.indicatorSourceRef === row.source.sourceRef ? styles$13.topicRowDropTarget : {},
190894
- ...dropPlan?.indicatorSourceRef === row.source.sourceRef && dropPlan.into ? styles$13.topicRowDropInto : dropPlan?.indicatorSourceRef === row.source.sourceRef && dropPlan.before ? styles$13.topicRowDropBefore : dropPlan?.indicatorSourceRef === row.source.sourceRef ? styles$13.topicRowDropAfter : {}
191121
+ ...isSelected ? styles$13.topicRowSelected : {},
191122
+ ...rowDropPlan?.into === true ? styles$13.topicRowDropInto : {},
191123
+ ...canDragTopic ? { cursor: "grab" } : {}
191124
+ },
191125
+ onClick: () => {
191126
+ selectTopic(row.source);
190895
191127
  },
190896
191128
  onMouseEnter: () => {
190897
191129
  setHoveredSourceRef(row.source.sourceRef);
@@ -190903,22 +191135,67 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190903
191135
  },
190904
191136
  onDragOver: (event) => {
190905
191137
  const plan = planMoveAtRow(row, event.clientX, event.clientY, event.currentTarget.getBoundingClientRect());
190906
- if (plan === void 0) return;
191138
+ if (plan === void 0) {
191139
+ setDropPlan(void 0);
191140
+ stopTopicAutoExpand();
191141
+ return;
191142
+ }
190907
191143
  event.preventDefault();
190908
191144
  event.dataTransfer.dropEffect = "move";
190909
191145
  setDropPlan(plan);
191146
+ scheduleTopicAutoExpand(row, plan);
191147
+ },
191148
+ onDragStart: (event) => {
191149
+ event.dataTransfer.effectAllowed = "move";
191150
+ event.dataTransfer.setData("text/plain", row.source.sourceRef);
191151
+ dragStartXRef.current = event.clientX;
191152
+ stopTopicDragAutoScroll();
191153
+ stopTopicAutoExpand();
191154
+ setMoveError("");
191155
+ setDropPlan(void 0);
191156
+ setDraggingSourceRef(row.source.sourceRef);
191157
+ },
191158
+ onDragEnd: () => {
191159
+ stopTopicDragAutoScroll();
191160
+ stopTopicAutoExpand();
191161
+ setDraggingSourceRef(void 0);
191162
+ setDropPlan(void 0);
190910
191163
  },
190911
191164
  onDrop: (event) => {
190912
191165
  event.preventDefault();
190913
191166
  finishTopicMove(planMoveAtRow(row, event.clientX, event.clientY, event.currentTarget.getBoundingClientRect()));
190914
191167
  },
190915
191168
  children: [
191169
+ Array.from({ length: row.depth }, (_, guideDepth) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
191170
+ "aria-hidden": true,
191171
+ "data-arkme-self-topic-hierarchy-guide": guideDepth,
191172
+ style: {
191173
+ ...styles$13.topicHierarchyGuide,
191174
+ left: (guideDepth - row.depth) * 16 + 12
191175
+ }
191176
+ }, `guide-${String(guideDepth)}`)),
191177
+ rowDropPlan !== void 0 && !rowDropPlan.into && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
191178
+ "aria-hidden": true,
191179
+ "data-arkme-self-topic-drop-line": rowDropPlan.before ? "before" : "after",
191180
+ style: {
191181
+ ...styles$13.topicDropLine,
191182
+ left: (rowDropPlan.indicatorDepth - row.depth) * 16 + 4,
191183
+ ...rowDropPlan.before ? { top: -1 } : { bottom: -1 }
191184
+ }
191185
+ }),
191186
+ rowDropPlan?.into === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
191187
+ "aria-hidden": true,
191188
+ "data-arkme-self-topic-drop-into": "true",
191189
+ style: styles$13.topicDropIntoBadge,
191190
+ children: "移入"
191191
+ }),
190916
191192
  row.hasChildren ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
190917
191193
  type: "button",
190918
191194
  "aria-label": `${row.expanded ? "收起" : "展开"}${row.source.displayName}`,
190919
191195
  title: row.expanded ? "收起子主题" : "展开子主题",
190920
191196
  style: styles$13.topicToggle,
190921
- onClick: () => {
191197
+ onClick: (event) => {
191198
+ event.stopPropagation();
190922
191199
  toggleTopicExpansion(row.source.sourceRef);
190923
191200
  },
190924
191201
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -190946,23 +191223,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190946
191223
  }),
190947
191224
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190948
191225
  type: "button",
190949
- draggable: customDragEnabled && !isDefaultCategory && arkmeSourceAllowsUserWrite(row.source),
190950
- style: {
190951
- ...styles$13.topicSelect,
190952
- ...isSelected ? styles$13.topicSelectSelected : {}
190953
- },
190954
- onDragStart: (event) => {
190955
- event.dataTransfer.effectAllowed = "move";
190956
- event.dataTransfer.setData("text/plain", row.source.sourceRef);
190957
- dragStartXRef.current = event.clientX;
190958
- setMoveError("");
190959
- setDraggingSourceRef(row.source.sourceRef);
190960
- },
190961
- onDragEnd: () => {
190962
- setDraggingSourceRef(void 0);
190963
- setDropPlan(void 0);
190964
- },
190965
- onClick: () => {
191226
+ style: styles$13.topicSelect,
191227
+ onClick: (event) => {
191228
+ event.stopPropagation();
190966
191229
  selectTopic(row.source);
190967
191230
  },
190968
191231
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -191110,10 +191373,12 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
191110
191373
  if (!canMoveTo(void 0, void 0)) return;
191111
191374
  event.preventDefault();
191112
191375
  event.dataTransfer.dropEffect = "move";
191376
+ stopTopicAutoExpand();
191113
191377
  setDropPlan({
191114
191378
  parent: void 0,
191115
191379
  insertBefore: void 0,
191116
191380
  indicatorSourceRef: "root",
191381
+ indicatorDepth: 0,
191117
191382
  before: false,
191118
191383
  into: false
191119
191384
  });
@@ -191124,6 +191389,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
191124
191389
  parent: void 0,
191125
191390
  insertBefore: void 0,
191126
191391
  indicatorSourceRef: "root",
191392
+ indicatorDepth: 0,
191127
191393
  before: false,
191128
191394
  into: false
191129
191395
  });
@@ -198959,11 +199225,22 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
198959
199225
  unreadCount: 0
198960
199226
  };
198961
199227
  }
198962
- function ArkmeTimelineSelfTopicBadge({ topic, onSelect }) {
199228
+ function arkmeTimelineSelfTopicPresentation(item, currentSource, sources) {
199229
+ if (currentSource?.kind !== "send_to_self" && currentSource?.kind !== "topic") return void 0;
199230
+ const topic = arkmeTimelineSelfTopicSource(item, sources);
199231
+ if (topic === void 0) return void 0;
199232
+ return {
199233
+ topic,
199234
+ displayLabel: arkmeTopicPathNames(topic, sources).join(" / ") || topic.displayName
199235
+ };
199236
+ }
199237
+ function ArkmeTimelineSelfTopicBadge({ topic, displayLabel, onSelect }) {
199238
+ const label = displayLabel?.trim() || topic.displayName;
198963
199239
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
198964
199240
  type: "button",
198965
- "data-arkme-self-topic-badge": topic.displayName,
198966
- "aria-label": `查看主题「${topic.displayName}」`,
199241
+ "data-arkme-self-topic-badge": label,
199242
+ "aria-label": `查看主题「${label}」`,
199243
+ title: label,
198967
199244
  style: styles$6.selfTopicBadge,
198968
199245
  onClick: (event) => {
198969
199246
  event.stopPropagation();
@@ -198984,7 +199261,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
198984
199261
  }),
198985
199262
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
198986
199263
  style: styles$6.selfTopicBadgeText,
198987
- children: topic.displayName
199264
+ children: label
198988
199265
  }),
198989
199266
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
198990
199267
  "aria-hidden": true,
@@ -205200,6 +205477,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205200
205477
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
205201
205478
  style: styles$6.titleGroup,
205202
205479
  children: [authenticated && conversationBackdropVisible && isArkmeSelfWorkspaceSource(selectedSource) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeSourceBreadcrumb, {
205480
+ userId: auth?.userId,
205203
205481
  selectedSource,
205204
205482
  sources: selfSources,
205205
205483
  tourOpen: selfTour.topicMenuOpen,
@@ -205609,7 +205887,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205609
205887
  onOpen: openMomentDetail
205610
205888
  })] }, row.id);
205611
205889
  const item = row.item;
205612
- const selfTopicSource = source?.kind === "send_to_self" ? arkmeTimelineSelfTopicSource(item, selfSources) : void 0;
205890
+ const selfTopicPresentation = arkmeTimelineSelfTopicPresentation(item, source, selfSources);
205613
205891
  const avatarRef = arkmeTimelineAvatarRef(item, selfProfile);
205614
205892
  const messageMember = item.memberRef === void 0 ? item.isMe ? selfConversationMember : void 0 : conversationMemberByRef.get(item.memberRef);
205615
205893
  const messageHeader = item.isMe || source.kind === "private_chat" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeTimelineMessageHeader, {
@@ -205826,8 +206104,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205826
206104
  wide: isStructuredMessageCard,
205827
206105
  children: messageBubble
205828
206106
  }, `read-receipt:${conversationOverlayKey}`);
205829
- const topicBadge = selfTopicSource === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeTimelineSelfTopicBadge, {
205830
- topic: selfTopicSource,
206107
+ const topicBadge = selfTopicPresentation === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeTimelineSelfTopicBadge, {
206108
+ topic: selfTopicPresentation.topic,
206109
+ ...selfTopicPresentation.displayLabel === void 0 ? {} : { displayLabel: selfTopicPresentation.displayLabel },
205831
206110
  onSelect: activateSelfSource
205832
206111
  });
205833
206112
  if (!isExtensionMessage || item.extensionParent === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [messageContentLine, topicBadge] });