@senguoyun/dsh-arkme 0.1.56 → 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.56";
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";
@@ -129372,6 +129372,166 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
129372
129372
  });
129373
129373
  }
129374
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
129375
129535
  //#region src/client/read-deadline.ts
129376
129536
  /** Bound interactive reads, including browser connection-queue time. Never retry writes here. */
129377
129537
  async function withArkmeReadDeadline(read, signal) {
@@ -138125,7 +138285,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
138125
138285
  }
138126
138286
  //#endregion
138127
138287
  //#region src/client/arko-pending-turn-store.ts
138128
- const STORAGE_KEY_PREFIX$2 = "arkme:arko:pending-turn:v1:";
138288
+ const STORAGE_KEY_PREFIX$3 = "arkme:arko:pending-turn:v1:";
138129
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;
138130
138290
  function defaultStorage() {
138131
138291
  try {
@@ -138135,7 +138295,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
138135
138295
  }
138136
138296
  }
138137
138297
  function storageKey$2(userId) {
138138
- return `${STORAGE_KEY_PREFIX$2}${String(userId)}`;
138298
+ return `${STORAGE_KEY_PREFIX$3}${String(userId)}`;
138139
138299
  }
138140
138300
  function optionalTrimmedString(value) {
138141
138301
  if (typeof value !== "string") return void 0;
@@ -148840,166 +149000,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
148840
149000
  //#region assets/recording-icons/icon_send_to_self_sidebar_dark.svg
148841
149001
  var icon_send_to_self_sidebar_dark_default = "PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGNpcmNsZSBjeD0iMTUiIGN5PSIxNSIgcj0iMTUiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuMSIvPgo8Y2lyY2xlIGN4PSIxNSIgY3k9IjE1IiByPSIxNC43NSIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLW9wYWNpdHk9IjAuMTUiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0xNS43OTM0IDExSDExLjUyNjdDMTEuMzUzNCAxMSAxMS4xODY3IDExLjAwNjcgMTEuMDI2NyAxMS4wMjY3QzkuMjMzMzcgMTEuMTggOC4zMzMzNyAxMi4yNCA4LjMzMzM3IDE0LjE5MzNWMTYuODZDOC4zMzMzNyAxOS41MjY3IDkuNDAwMDQgMjAuMDUzMyAxMS41MjY3IDIwLjA1MzNIMTEuNzkzNEMxMS45NCAyMC4wNTMzIDEyLjEzMzQgMjAuMTUzMyAxMi4yMiAyMC4yNjY3TDEzLjAyIDIxLjMzMzNDMTMuMzczNCAyMS44MDY3IDEzLjk0NjcgMjEuODA2NyAxNC4zIDIxLjMzMzNMMTUuMSAyMC4yNjY3QzE1LjIgMjAuMTMzMyAxNS4zNiAyMC4wNTMzIDE1LjUyNjcgMjAuMDUzM0gxNS43OTM0QzE3Ljc0NjcgMjAuMDUzMyAxOC44MDY3IDE5LjE2IDE4Ljk2IDE3LjM2QzE4Ljk4IDE3LjIgMTguOTg2NyAxNy4wMzMzIDE4Ljk4NjcgMTYuODZWMTQuMTkzM0MxOC45ODY3IDEyLjA2NjcgMTcuOTIgMTEgMTUuNzkzNCAxMVpNMTEuMzMzNCAxNi4zMzMzQzEwLjk2IDE2LjMzMzMgMTAuNjY2NyAxNi4wMzMzIDEwLjY2NjcgMTUuNjY2N0MxMC42NjY3IDE1LjMgMTAuOTY2NyAxNSAxMS4zMzM0IDE1QzExLjcgMTUgMTIgMTUuMyAxMiAxNS42NjY3QzEyIDE2LjAzMzMgMTEuNyAxNi4zMzMzIDExLjMzMzQgMTYuMzMzM1pNMTMuNjYgMTYuMzMzM0MxMy4yODY3IDE2LjMzMzMgMTIuOTkzNCAxNi4wMzMzIDEyLjk5MzQgMTUuNjY2N0MxMi45OTM0IDE1LjMgMTMuMjkzNCAxNSAxMy42NiAxNUMxNC4wMjY3IDE1IDE0LjMyNjcgMTUuMyAxNC4zMjY3IDE1LjY2NjdDMTQuMzI2NyAxNi4wMzMzIDE0LjAzMzQgMTYuMzMzMyAxMy42NiAxNi4zMzMzWk0xNS45OTM0IDE2LjMzMzNDMTUuNjIgMTYuMzMzMyAxNS4zMjY3IDE2LjAzMzMgMTUuMzI2NyAxNS42NjY3QzE1LjMyNjcgMTUuMyAxNS42MjY3IDE1IDE1Ljk5MzQgMTVDMTYuMzYgMTUgMTYuNjYgMTUuMyAxNi42NiAxNS42NjY3QzE2LjY2IDE2LjAzMzMgMTYuMzYgMTYuMzMzMyAxNS45OTM0IDE2LjMzMzNaIiBmaWxsPSIjOUVEREE0Ii8+CjxwYXRoIGQ9Ik0yMS42NTMzIDExLjUyNjZWMTQuMTkzM0MyMS42NTMzIDE1LjUyNjYgMjEuMjQgMTYuNDMzMyAyMC40MTMzIDE2LjkzMzNDMjAuMjEzMyAxNy4wNTMzIDE5Ljk4IDE2Ljg5MzMgMTkuOTggMTYuNjU5OUwxOS45ODY2IDE0LjE5MzNDMTkuOTg2NiAxMS41MjY2IDE4LjQ2IDkuOTk5OTIgMTUuNzkzMyA5Ljk5OTkyTDExLjczMzMgMTAuMDA2NkMxMS41IDEwLjAwNjYgMTEuMzQgOS43NzMyNSAxMS40NiA5LjU3MzI1QzExLjk2IDguNzQ2NTkgMTIuODY2NiA4LjMzMzI1IDE0LjE5MzMgOC4zMzMyNUgxOC40NkMyMC41ODY2IDguMzMzMjUgMjEuNjUzMyA5LjM5OTkyIDIxLjY1MzMgMTEuNTI2NloiIGZpbGw9IiM5RUREQTQiLz4KPC9zdmc+Cg==";
148842
149002
  //#endregion
148843
- //#region src/client/source-tree.ts
148844
- /** Return the complete visible hierarchy for a topic, from its root to itself. */
148845
- function arkmeTopicPathNames(selectedSource, sources) {
148846
- if (selectedSource === void 0 || selectedSource.kind !== "topic") return [];
148847
- const sourceByRef = new Map(sources.map((source) => [source.sourceRef, source]));
148848
- const sourceByHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
148849
- const path = [];
148850
- const visited = /* @__PURE__ */ new Set();
148851
- let current = sourceByRef.get(selectedSource.sourceRef) ?? selectedSource;
148852
- while (current !== void 0 && current.kind === "topic" && !visited.has(current.sourceRef)) {
148853
- visited.add(current.sourceRef);
148854
- path.unshift(current);
148855
- const parentReference = current.parentTopicHierarchyKey ?? current.parentSourceRef;
148856
- current = parentReference === void 0 ? void 0 : current.parentTopicHierarchyKey === void 0 ? sourceByRef.get(parentReference) : sourceByHierarchyKey.get(parentReference);
148857
- }
148858
- return path.map((source) => source.displayName);
148859
- }
148860
- /** A hierarchy move may not create a cycle or exceed the five-level topic limit. */
148861
- function canMoveArkmeTopicToParent(source, nextParent, sources) {
148862
- if (!arkmeSourceAllowsUserWrite(source) || nextParent !== void 0 && !arkmeSourceAllowsUserWrite(nextParent)) return false;
148863
- if (source.kind !== "topic") return false;
148864
- if (nextParent === void 0) return true;
148865
- if (nextParent.kind !== "topic" || nextParent.sourceRef === source.sourceRef) return false;
148866
- const byRef = new Map(sources.map((item) => [item.sourceRef, item]));
148867
- const byHierarchyKey = new Map(sources.flatMap((item) => item.topicHierarchyKey === void 0 ? [] : [[item.topicHierarchyKey, item]]));
148868
- const parentOf = (item) => item.parentTopicHierarchyKey === void 0 ? item.parentSourceRef === void 0 ? void 0 : byRef.get(item.parentSourceRef) : byHierarchyKey.get(item.parentTopicHierarchyKey);
148869
- const targetAncestors = /* @__PURE__ */ new Set();
148870
- let current = nextParent;
148871
- while (current !== void 0 && !targetAncestors.has(current.sourceRef)) {
148872
- targetAncestors.add(current.sourceRef);
148873
- current = parentOf(current);
148874
- }
148875
- if (targetAncestors.has(source.sourceRef)) return false;
148876
- const childRefsByParent = /* @__PURE__ */ new Map();
148877
- for (const item of sources) {
148878
- const parent = parentOf(item);
148879
- if (parent === void 0) continue;
148880
- const children = childRefsByParent.get(parent.sourceRef) ?? [];
148881
- children.push(item);
148882
- childRefsByParent.set(parent.sourceRef, children);
148883
- }
148884
- const subtreeDepth = (item, visited = /* @__PURE__ */ new Set()) => {
148885
- if (visited.has(item.sourceRef)) return 0;
148886
- visited.add(item.sourceRef);
148887
- return 1 + Math.max(0, ...(childRefsByParent.get(item.sourceRef) ?? []).map((child) => subtreeDepth(child, visited)));
148888
- };
148889
- return targetAncestors.size + subtreeDepth(source) <= 5;
148890
- }
148891
- /** Build a stable tree while keeping paged children hidden until their known parent arrives. */
148892
- function buildArkmeSourceTree(sources) {
148893
- const sourcesByRef = new Map(sources.map((source) => [source.sourceRef, source]));
148894
- const sourcesByTopicHierarchyKey = new Map(sources.flatMap((source) => source.topicHierarchyKey === void 0 ? [] : [[source.topicHierarchyKey, source]]));
148895
- const nodesByRef = new Map(sources.map((source) => [source.sourceRef, {
148896
- source,
148897
- children: []
148898
- }]));
148899
- const roots = [];
148900
- const validParent = (source) => {
148901
- if (source.kind !== "topic") return void 0;
148902
- const parentReference = source.parentTopicHierarchyKey ?? source.parentSourceRef;
148903
- if (parentReference === void 0) return void 0;
148904
- const parentByReference = (reference) => source.parentTopicHierarchyKey === void 0 ? sourcesByRef.get(reference) : sourcesByTopicHierarchyKey.get(reference);
148905
- const seen = /* @__PURE__ */ new Set([source.topicHierarchyKey ?? source.sourceRef]);
148906
- let parentRef = parentReference;
148907
- let directParent;
148908
- while (parentRef !== void 0) {
148909
- if (seen.has(parentRef)) return void 0;
148910
- seen.add(parentRef);
148911
- const parent = parentByReference(parentRef);
148912
- if (parent === void 0) return source.parentTopicHierarchyKey === void 0 ? void 0 : null;
148913
- if (parent.kind !== "topic") return void 0;
148914
- directParent ??= parent;
148915
- parentRef = parent.parentTopicHierarchyKey ?? parent.parentSourceRef;
148916
- }
148917
- return directParent;
148918
- };
148919
- for (const source of sources) {
148920
- const node = nodesByRef.get(source.sourceRef);
148921
- const parent = validParent(source);
148922
- if (parent === null) continue;
148923
- const parentNode = parent === void 0 ? void 0 : nodesByRef.get(parent.sourceRef);
148924
- if (parentNode === void 0) roots.push(node);
148925
- else parentNode.children.push(node);
148926
- }
148927
- return roots;
148928
- }
148929
- const sourceNameCollator = new Intl.Collator("zh-CN", {
148930
- numeric: true,
148931
- sensitivity: "base"
148932
- });
148933
- function nonNegativeFinite(value) {
148934
- return value !== void 0 && Number.isFinite(value) && value > 0 ? value : 0;
148935
- }
148936
- /**
148937
- * Sort peers only, preserving every parent-child relationship. `custom` uses
148938
- * the server's persisted sibling order, with the legacy name order as fallback.
148939
- */
148940
- function sortArkmeSourceTree(roots, sort) {
148941
- const compare = (left, right) => {
148942
- if (left.source.kind === "default_category") return right.source.kind === "default_category" ? 0 : -1;
148943
- if (right.source.kind === "default_category") return 1;
148944
- if (sort === "latest") {
148945
- const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
148946
- if (byLatest !== 0) return byLatest;
148947
- const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
148948
- if (byCount !== 0) return byCount;
148949
- } else if (sort === "most") {
148950
- const byCount = nonNegativeFinite(right.source.recordCount) - nonNegativeFinite(left.source.recordCount);
148951
- if (byCount !== 0) return byCount;
148952
- const byLatest = nonNegativeFinite(right.source.activeAtMillis) - nonNegativeFinite(left.source.activeAtMillis);
148953
- if (byLatest !== 0) return byLatest;
148954
- } else {
148955
- const leftOrder = nonNegativeFinite(left.source.siblingOrder);
148956
- const rightOrder = nonNegativeFinite(right.source.siblingOrder);
148957
- if (leftOrder > 0 || rightOrder > 0) {
148958
- if (leftOrder <= 0) return 1;
148959
- if (rightOrder <= 0) return -1;
148960
- const byOrder = leftOrder - rightOrder;
148961
- if (byOrder !== 0) return byOrder;
148962
- }
148963
- }
148964
- return sourceNameCollator.compare(left.source.displayName, right.source.displayName);
148965
- };
148966
- return roots.map((node) => ({
148967
- ...node,
148968
- children: sortArkmeSourceTree(node.children, sort)
148969
- })).sort(compare);
148970
- }
148971
- /** Total every topic from its direct records plus every descendant exactly once. */
148972
- function aggregateArkmeSourceTreeRecordCounts(roots) {
148973
- const totals = /* @__PURE__ */ new Map();
148974
- const visit = (node) => {
148975
- const total = nonNegativeFinite(node.source.recordCount) + node.children.reduce((sum, child) => sum + visit(child), 0);
148976
- totals.set(node.source.sourceRef, total);
148977
- return total;
148978
- };
148979
- roots.forEach(visit);
148980
- return totals;
148981
- }
148982
- function flattenVisibleArkmeSourceTree(roots, collapsedSourceRefs) {
148983
- const rows = [];
148984
- const visit = (node, depth) => {
148985
- const hasChildren = node.children.length > 0;
148986
- const expanded = hasChildren && !collapsedSourceRefs.has(node.source.sourceRef);
148987
- rows.push({
148988
- source: node.source,
148989
- depth,
148990
- hasChildren,
148991
- expanded
148992
- });
148993
- if (expanded) node.children.forEach((child) => {
148994
- visit(child, depth + 1);
148995
- });
148996
- };
148997
- roots.forEach((root) => {
148998
- visit(root, 0);
148999
- });
149000
- return rows;
149001
- }
149002
- //#endregion
149003
149003
  //#region src/client/recordings/recording-forward-attempt.ts
149004
149004
  function recordingForwardTargetKey(target) {
149005
149005
  if (target.kind === "private_chat" || target.kind === "group_chat") return target.sourceKey ?? "";
@@ -180034,8 +180034,8 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
180034
180034
  //#endregion
180035
180035
  //#region src/client/marketplace-sort-preference.ts
180036
180036
  const DEFAULT_MARKETPLACE_SORT = "rating";
180037
- const STORAGE_KEY_PREFIX$1 = "dsh-arkme:marketplace-sort:v1:";
180038
- function preferenceStorage(storage) {
180037
+ const STORAGE_KEY_PREFIX$2 = "dsh-arkme:marketplace-sort:v1:";
180038
+ function preferenceStorage$1(storage) {
180039
180039
  if (storage !== void 0) return storage;
180040
180040
  try {
180041
180041
  return typeof window === "undefined" ? void 0 : window.localStorage;
@@ -180043,27 +180043,27 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
180043
180043
  return;
180044
180044
  }
180045
180045
  }
180046
- function preferenceKey(userId) {
180047
- 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`;
180048
180048
  }
180049
180049
  function isMarketplaceSort(value) {
180050
180050
  return value === "rating" || value === "comments" || value === "opens" || value === "created_at";
180051
180051
  }
180052
180052
  function readMarketplaceSortPreference(userId, storage) {
180053
- const target = preferenceStorage(storage);
180053
+ const target = preferenceStorage$1(storage);
180054
180054
  if (target === void 0) return DEFAULT_MARKETPLACE_SORT;
180055
180055
  try {
180056
- const value = target.getItem(preferenceKey(userId));
180056
+ const value = target.getItem(preferenceKey$1(userId));
180057
180057
  return isMarketplaceSort(value) ? value : DEFAULT_MARKETPLACE_SORT;
180058
180058
  } catch {
180059
180059
  return DEFAULT_MARKETPLACE_SORT;
180060
180060
  }
180061
180061
  }
180062
180062
  function writeMarketplaceSortPreference(userId, sort, storage) {
180063
- const target = preferenceStorage(storage);
180063
+ const target = preferenceStorage$1(storage);
180064
180064
  if (target === void 0) return;
180065
180065
  try {
180066
- target.setItem(preferenceKey(userId), sort);
180066
+ target.setItem(preferenceKey$1(userId), sort);
180067
180067
  } catch {}
180068
180068
  }
180069
180069
  //#endregion
@@ -185082,6 +185082,43 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
185082
185082
  });
185083
185083
  }
185084
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
185085
185122
  //#region src/client/conversation-timeline-read-port.ts
185086
185123
  const conversationTimelineReadPort = { readPage: async (sourceRef, cursor, signal) => await callArkme("source.timeline", {
185087
185124
  sourceRef,
@@ -190061,6 +190098,23 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190061
190098
  }
190062
190099
  //#endregion
190063
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
+ }
190064
190118
  const colors$4 = {
190065
190119
  text: "#171923",
190066
190120
  secondary: "#6f747d",
@@ -190206,10 +190260,48 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190206
190260
  borderRadius: 7
190207
190261
  },
190208
190262
  topicRowHover: { background: "#f3f4f7" },
190209
- topicRowDropTarget: { background: "#f3f5ff" },
190210
- topicRowDropBefore: { boxShadow: "inset 0 2px 0 #5870d8" },
190211
- topicRowDropAfter: { boxShadow: "inset 0 -2px 0 #5870d8" },
190212
- 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
+ },
190213
190305
  topicToggle: {
190214
190306
  width: 24,
190215
190307
  height: 28,
@@ -190248,10 +190340,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190248
190340
  textAlign: "left",
190249
190341
  cursor: "pointer"
190250
190342
  },
190251
- topicSelectSelected: {
190252
- background: colors$4.selected,
190253
- fontWeight: 600
190254
- },
190255
190343
  topicName: {
190256
190344
  minWidth: 0,
190257
190345
  flex: 1,
@@ -190523,11 +190611,11 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190523
190611
  function topicCountLabel(count) {
190524
190612
  return count === void 0 ? "…" : count.toLocaleString("zh-CN");
190525
190613
  }
190526
- 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 }) {
190527
190615
  const [manualOpen, setOpen] = (0, react$1.useState)(false);
190528
190616
  const open = tourOpen ?? manualOpen;
190529
190617
  const [collapsedSourceRefs, setCollapsedSourceRefs] = (0, react$1.useState)(() => /* @__PURE__ */ new Set());
190530
- const [sort, setSort] = (0, react$1.useState)("latest");
190618
+ const [sort, setSort] = (0, react$1.useState)(() => readSelfTopicSortPreference(userId));
190531
190619
  const [draggingSourceRef, setDraggingSourceRef] = (0, react$1.useState)();
190532
190620
  const [hoveredSourceRef, setHoveredSourceRef] = (0, react$1.useState)();
190533
190621
  const [topicMenuSource, setTopicMenuSource] = (0, react$1.useState)();
@@ -190541,10 +190629,15 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190541
190629
  const [dropPlan, setDropPlan] = (0, react$1.useState)();
190542
190630
  const [movingTopic, setMovingTopic] = (0, react$1.useState)(false);
190543
190631
  const [moveError, setMoveError] = (0, react$1.useState)("");
190544
- const rootRef = (0, react$1.useRef)(null);
190632
+ const selectorRef = (0, react$1.useRef)(null);
190633
+ const menuRef = (0, react$1.useRef)(null);
190545
190634
  const menuListRef = (0, react$1.useRef)(null);
190546
190635
  const pendingSelectedFocusRef = (0, react$1.useRef)(false);
190547
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)();
190548
190641
  const observedActiveDissolveRef = (0, react$1.useRef)(false);
190549
190642
  const topicRoots = (0, react$1.useMemo)(() => sortArkmeSourceTree(buildArkmeSourceTree(arkmeSelfDirectorySources(sources)), sort), [sort, sources]);
190550
190643
  const rows = (0, react$1.useMemo)(() => flattenVisibleArkmeSourceTree(topicRoots, collapsedSourceRefs), [collapsedSourceRefs, topicRoots]);
@@ -190574,6 +190667,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190574
190667
  setTopicMutationError("");
190575
190668
  setDissolveDialogOpen(true);
190576
190669
  };
190670
+ (0, react$1.useEffect)(() => {
190671
+ setSort(readSelfTopicSortPreference(userId));
190672
+ }, [userId]);
190577
190673
  (0, react$1.useEffect)(() => {
190578
190674
  if (activeDissolveRunning && activeDissolve !== void 0) {
190579
190675
  observedActiveDissolveRef.current = true;
@@ -190601,24 +190697,75 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190601
190697
  const currentParent = currentParentOf(draggingSource);
190602
190698
  return currentParent?.sourceRef !== nextParent?.sourceRef || nextSiblingOf(draggingSource, currentParent)?.sourceRef !== insertBefore?.sourceRef;
190603
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
+ };
190604
190749
  const planMoveAtRow = (row, clientX, clientY, rect) => {
190605
190750
  if (row.source.kind === "default_category" || !arkmeSourceAllowsUserWrite(row.source)) return void 0;
190606
190751
  if (draggingSource === void 0) return void 0;
190607
190752
  const horizontalDelta = clientX - dragStartXRef.current;
190608
- 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) {
190609
190757
  if (!canMoveTo(row.source, void 0)) return void 0;
190610
190758
  return {
190611
190759
  parent: row.source,
190612
190760
  insertBefore: void 0,
190613
190761
  indicatorSourceRef: row.source.sourceRef,
190762
+ indicatorDepth: row.depth + 1,
190614
190763
  before: false,
190615
190764
  into: true
190616
190765
  };
190617
190766
  }
190618
- const targetParent = currentParentOf(row.source);
190619
- const outdenting = horizontalDelta <= -24 && targetParent !== void 0;
190620
190767
  const parent = outdenting ? currentParentOf(targetParent) : targetParent;
190621
- const before = clientY < rect.top + rect.height / 2;
190768
+ const before = position === "before";
190622
190769
  const anchor = outdenting ? targetParent : row.source;
190623
190770
  const insertBefore = before ? anchor : nextSiblingOf(anchor, parent);
190624
190771
  if (!canMoveTo(parent, insertBefore)) return void 0;
@@ -190626,6 +190773,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190626
190773
  parent,
190627
190774
  insertBefore,
190628
190775
  indicatorSourceRef: row.source.sourceRef,
190776
+ indicatorDepth: outdenting ? Math.max(0, row.depth - 1) : row.depth,
190629
190777
  before,
190630
190778
  into: false
190631
190779
  };
@@ -190633,6 +190781,14 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190633
190781
  const finishTopicMove = (plan) => {
190634
190782
  if (plan === void 0 || !canMoveTo(plan.parent, plan.insertBefore) || draggingSource === void 0 || onMoveTopic === void 0) return;
190635
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
+ });
190636
190792
  setMovingTopic(true);
190637
190793
  setMoveError("");
190638
190794
  setDropPlan(void 0);
@@ -190643,6 +190799,10 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190643
190799
  setMovingTopic(false);
190644
190800
  });
190645
190801
  };
190802
+ (0, react$1.useEffect)(() => () => {
190803
+ stopTopicDragAutoScroll();
190804
+ stopTopicAutoExpand();
190805
+ }, []);
190646
190806
  const revealSelectedTopic = () => {
190647
190807
  if (selectedRef === void 0) {
190648
190808
  menuListRef.current?.scrollTo({ top: 0 });
@@ -190682,10 +190842,10 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190682
190842
  if (!open) return;
190683
190843
  const closeOutside = (event) => {
190684
190844
  if (tourOpen !== void 0) return;
190685
- if (event.target instanceof Node && !rootRef.current?.contains(event.target)) {
190686
- setOpen(false);
190687
- setTopicMenuSource(void 0);
190688
- }
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);
190689
190849
  };
190690
190850
  const closeEscape = (event) => {
190691
190851
  if (tourOpen !== void 0) return;
@@ -190778,7 +190938,6 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190778
190938
  });
190779
190939
  };
190780
190940
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("nav", {
190781
- ref: rootRef,
190782
190941
  "aria-label": "发给自己主题",
190783
190942
  style: styles$13.breadcrumb,
190784
190943
  children: [
@@ -190788,6 +190947,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190788
190947
  children: "发给自己"
190789
190948
  }),
190790
190949
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190950
+ ref: selectorRef,
190791
190951
  type: "button",
190792
190952
  "aria-label": "选择主题",
190793
190953
  "aria-haspopup": "tree",
@@ -190849,6 +191009,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190849
191009
  }), activeDissolveLabel]
190850
191010
  }),
190851
191011
  open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
191012
+ ref: menuRef,
190852
191013
  role: "tree",
190853
191014
  "aria-label": "主题",
190854
191015
  "data-arkme-self-topic-menu": true,
@@ -190874,6 +191035,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190874
191035
  },
190875
191036
  onClick: () => {
190876
191037
  setSort(value);
191038
+ writeSelfTopicSortPreference(userId, value);
190877
191039
  revealSelectedTopic();
190878
191040
  },
190879
191041
  children: label
@@ -190888,6 +191050,20 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190888
191050
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
190889
191051
  ref: menuListRef,
190890
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
+ },
190891
191067
  children: [
190892
191068
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190893
191069
  type: "button",
@@ -190920,24 +191096,34 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190920
191096
  const isSelected = selectedRef === row.source.sourceRef;
190921
191097
  const isHovered = hoveredSourceRef === row.source.sourceRef;
190922
191098
  const isDefaultCategory = row.source.kind === "default_category";
191099
+ const canDragTopic = customDragEnabled && !isDefaultCategory && arkmeSourceAllowsUserWrite(row.source);
190923
191100
  const canCreateChild = arkmeSourceAllowsUserWrite(row.source) && !isDefaultCategory && onCreateChildTopic !== void 0 && row.depth + 1 < 5;
190924
191101
  const canManageTopic = arkmeSourceAllowsUserWrite(row.source) && !isDefaultCategory && (canCreateChild || onRenameTopic !== void 0 || onDissolveTopic !== void 0);
190925
- const showActions = isHovered && canManageTopic;
191102
+ const showActions = isHovered && canManageTopic && draggingSource === void 0;
190926
191103
  const manageMenuOpen = topicMenuSource?.sourceRef === row.source.sourceRef;
191104
+ const rowDropPlan = dropPlan?.indicatorSourceRef === row.source.sourceRef ? dropPlan : void 0;
190927
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;
190928
191107
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
190929
191108
  role: "treeitem",
190930
191109
  "aria-level": row.depth + 1,
190931
191110
  "aria-selected": selectedRef === row.source.sourceRef,
190932
191111
  ...row.hasChildren ? { "aria-expanded": row.expanded } : {},
191112
+ draggable: canDragTopic,
190933
191113
  "data-arkme-self-topic-tree-row": "true",
190934
191114
  "data-arkme-self-topic-tree-row-ref": row.source.sourceRef,
191115
+ "data-arkme-self-topic-hit-region": "true",
190935
191116
  style: {
190936
191117
  ...styles$13.topicRow,
190937
- paddingLeft: row.depth * 16,
191118
+ marginLeft: depthInset,
191119
+ width: `calc(100% - ${String(depthInset)}px)`,
190938
191120
  ...isHovered && !isSelected ? styles$13.topicRowHover : {},
190939
- ...dropPlan?.indicatorSourceRef === row.source.sourceRef ? styles$13.topicRowDropTarget : {},
190940
- ...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);
190941
191127
  },
190942
191128
  onMouseEnter: () => {
190943
191129
  setHoveredSourceRef(row.source.sourceRef);
@@ -190949,22 +191135,67 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190949
191135
  },
190950
191136
  onDragOver: (event) => {
190951
191137
  const plan = planMoveAtRow(row, event.clientX, event.clientY, event.currentTarget.getBoundingClientRect());
190952
- if (plan === void 0) return;
191138
+ if (plan === void 0) {
191139
+ setDropPlan(void 0);
191140
+ stopTopicAutoExpand();
191141
+ return;
191142
+ }
190953
191143
  event.preventDefault();
190954
191144
  event.dataTransfer.dropEffect = "move";
190955
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);
190956
191163
  },
190957
191164
  onDrop: (event) => {
190958
191165
  event.preventDefault();
190959
191166
  finishTopicMove(planMoveAtRow(row, event.clientX, event.clientY, event.currentTarget.getBoundingClientRect()));
190960
191167
  },
190961
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
+ }),
190962
191192
  row.hasChildren ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
190963
191193
  type: "button",
190964
191194
  "aria-label": `${row.expanded ? "收起" : "展开"}${row.source.displayName}`,
190965
191195
  title: row.expanded ? "收起子主题" : "展开子主题",
190966
191196
  style: styles$13.topicToggle,
190967
- onClick: () => {
191197
+ onClick: (event) => {
191198
+ event.stopPropagation();
190968
191199
  toggleTopicExpansion(row.source.sourceRef);
190969
191200
  },
190970
191201
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -190992,23 +191223,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
190992
191223
  }),
190993
191224
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
190994
191225
  type: "button",
190995
- draggable: customDragEnabled && !isDefaultCategory && arkmeSourceAllowsUserWrite(row.source),
190996
- style: {
190997
- ...styles$13.topicSelect,
190998
- ...isSelected ? styles$13.topicSelectSelected : {}
190999
- },
191000
- onDragStart: (event) => {
191001
- event.dataTransfer.effectAllowed = "move";
191002
- event.dataTransfer.setData("text/plain", row.source.sourceRef);
191003
- dragStartXRef.current = event.clientX;
191004
- setMoveError("");
191005
- setDraggingSourceRef(row.source.sourceRef);
191006
- },
191007
- onDragEnd: () => {
191008
- setDraggingSourceRef(void 0);
191009
- setDropPlan(void 0);
191010
- },
191011
- onClick: () => {
191226
+ style: styles$13.topicSelect,
191227
+ onClick: (event) => {
191228
+ event.stopPropagation();
191012
191229
  selectTopic(row.source);
191013
191230
  },
191014
191231
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
@@ -191156,10 +191373,12 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
191156
191373
  if (!canMoveTo(void 0, void 0)) return;
191157
191374
  event.preventDefault();
191158
191375
  event.dataTransfer.dropEffect = "move";
191376
+ stopTopicAutoExpand();
191159
191377
  setDropPlan({
191160
191378
  parent: void 0,
191161
191379
  insertBefore: void 0,
191162
191380
  indicatorSourceRef: "root",
191381
+ indicatorDepth: 0,
191163
191382
  before: false,
191164
191383
  into: false
191165
191384
  });
@@ -191170,6 +191389,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
191170
191389
  parent: void 0,
191171
191390
  insertBefore: void 0,
191172
191391
  indicatorSourceRef: "root",
191392
+ indicatorDepth: 0,
191173
191393
  before: false,
191174
191394
  into: false
191175
191395
  });
@@ -199005,11 +199225,22 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
199005
199225
  unreadCount: 0
199006
199226
  };
199007
199227
  }
199008
- 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;
199009
199239
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
199010
199240
  type: "button",
199011
- "data-arkme-self-topic-badge": topic.displayName,
199012
- "aria-label": `查看主题「${topic.displayName}」`,
199241
+ "data-arkme-self-topic-badge": label,
199242
+ "aria-label": `查看主题「${label}」`,
199243
+ title: label,
199013
199244
  style: styles$6.selfTopicBadge,
199014
199245
  onClick: (event) => {
199015
199246
  event.stopPropagation();
@@ -199030,7 +199261,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
199030
199261
  }),
199031
199262
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
199032
199263
  style: styles$6.selfTopicBadgeText,
199033
- children: topic.displayName
199264
+ children: label
199034
199265
  }),
199035
199266
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
199036
199267
  "aria-hidden": true,
@@ -205246,6 +205477,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205246
205477
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
205247
205478
  style: styles$6.titleGroup,
205248
205479
  children: [authenticated && conversationBackdropVisible && isArkmeSelfWorkspaceSource(selectedSource) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeSourceBreadcrumb, {
205480
+ userId: auth?.userId,
205249
205481
  selectedSource,
205250
205482
  sources: selfSources,
205251
205483
  tourOpen: selfTour.topicMenuOpen,
@@ -205655,7 +205887,7 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205655
205887
  onOpen: openMomentDetail
205656
205888
  })] }, row.id);
205657
205889
  const item = row.item;
205658
- const selfTopicSource = source?.kind === "send_to_self" ? arkmeTimelineSelfTopicSource(item, selfSources) : void 0;
205890
+ const selfTopicPresentation = arkmeTimelineSelfTopicPresentation(item, source, selfSources);
205659
205891
  const avatarRef = arkmeTimelineAvatarRef(item, selfProfile);
205660
205892
  const messageMember = item.memberRef === void 0 ? item.isMe ? selfConversationMember : void 0 : conversationMemberByRef.get(item.memberRef);
205661
205893
  const messageHeader = item.isMe || source.kind === "private_chat" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeTimelineMessageHeader, {
@@ -205872,8 +206104,9 @@ body[data-ds-dark-theme] .arkme-mention-all-avatar [data-arkme-theme-image="dark
205872
206104
  wide: isStructuredMessageCard,
205873
206105
  children: messageBubble
205874
206106
  }, `read-receipt:${conversationOverlayKey}`);
205875
- const topicBadge = selfTopicSource === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeTimelineSelfTopicBadge, {
205876
- 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 },
205877
206110
  onSelect: activateSelfSource
205878
206111
  });
205879
206112
  if (!isExtensionMessage || item.extensionParent === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [messageContentLine, topicBadge] });