chatlab-cli 0.34.3 → 0.35.0

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.
Files changed (174) hide show
  1. package/dist/{chat-command-QH7DXCAJ.mjs → chat-command-24RYGJAS.mjs} +8 -8
  2. package/dist/{chunk-2WYXD5ZP.mjs → chunk-22YJOBA3.mjs} +474 -99
  3. package/dist/chunk-22YJOBA3.mjs.map +1 -0
  4. package/dist/{chunk-AWXHYTD5.mjs → chunk-4GWWKHWA.mjs} +42 -14
  5. package/dist/chunk-4GWWKHWA.mjs.map +1 -0
  6. package/dist/{chunk-ZVGDOSGG.mjs → chunk-7IGBPZMI.mjs} +38 -18
  7. package/dist/chunk-7IGBPZMI.mjs.map +1 -0
  8. package/dist/{chunk-WJVE5Q4Y.mjs → chunk-BRZ6YIBR.mjs} +2 -2
  9. package/dist/{chunk-XIWVJAXH.mjs → chunk-DC7IUDNV.mjs} +10 -18
  10. package/dist/chunk-DC7IUDNV.mjs.map +1 -0
  11. package/dist/{chunk-JYBPJWE2.mjs → chunk-DEFEU567.mjs} +10 -18
  12. package/dist/chunk-DEFEU567.mjs.map +1 -0
  13. package/dist/{chunk-C2PD3BS7.mjs → chunk-GQMCDWZQ.mjs} +2 -2
  14. package/dist/{chunk-AWGXYFIC.mjs → chunk-KIMJXKZO.mjs} +2 -2
  15. package/dist/{chunk-SWUTRDGZ.mjs → chunk-KXDJ7N6N.mjs} +2 -2
  16. package/dist/{chunk-3X26JJIP.mjs → chunk-QKEARRFH.mjs} +3 -3
  17. package/dist/{chunk-6U2QZO3N.mjs → chunk-U36TQMAF.mjs} +2 -2
  18. package/dist/{chunk-2Q3HHVTS.mjs → chunk-ZBPPLXM4.mjs} +12 -12
  19. package/dist/cli.mjs +9 -9
  20. package/dist/contacts-worker.mjs +2 -2
  21. package/dist/global-insight-worker.mjs +1 -1
  22. package/dist/{http-D2F4QTQW.mjs → http-3K4YMWDE.mjs} +12 -12
  23. package/dist/{import-H7X2JU2O.mjs → import-D4LV565A.mjs} +7 -7
  24. package/dist/index.mjs +12 -12
  25. package/dist/{mcp-UURXRGDF.mjs → mcp-KL22FKQ2.mjs} +8 -8
  26. package/dist/people-relationships-worker.mjs +2 -2
  27. package/dist/semantic-index-worker.mjs +1 -1
  28. package/dist/{src-MHCO4HYY.mjs → src-COIB6Z4P.mjs} +8 -6
  29. package/dist/{stream-import-BHXACUCA.mjs → stream-import-FSXCFG4M.mjs} +7 -7
  30. package/dist/{worker-runtime-JOUCP2L4.mjs → worker-runtime-DPS2LURI.mjs} +5 -5
  31. package/dist-cli-web/assets/AISettingsTab-rQC2ocFH.js +1 -0
  32. package/dist-cli-web/assets/AboutTab-DMPziKjj.js +1 -0
  33. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-CnRLt_1j.js +110 -0
  34. package/dist-cli-web/assets/Alert--bTQXRcZ.js +1 -0
  35. package/dist-cli-web/assets/ApiSettingsTab-CtXl4I28.js +2 -0
  36. package/dist-cli-web/assets/BatchManageTab-1KaCPweN.js +1 -0
  37. package/dist-cli-web/assets/{BufferResource-Dak23r3B.js → BufferResource-BXSJADxx.js} +1 -1
  38. package/dist-cli-web/assets/CanvasRenderer-Cq--8UQ_.js +1 -0
  39. package/dist-cli-web/assets/ChatRecordDrawer-BC0F7ok1.js +11 -0
  40. package/dist-cli-web/assets/ChatRecordDrawer-Tt6UduRC.css +1 -0
  41. package/dist-cli-web/assets/Checkbox-CwajNPGF.js +1 -0
  42. package/dist-cli-web/assets/DataDirCleanupNotice-D0Oruka2.js +1 -0
  43. package/dist-cli-web/assets/DatePicker.vue_vue_type_script_setup_true_lang-C4CsoIlq.js +1 -0
  44. package/dist-cli-web/assets/DebugToolsPanel-DVDwS-wz.js +1 -0
  45. package/dist-cli-web/assets/EChartWordcloud-KV0MdsDE.js +1 -0
  46. package/dist-cli-web/assets/{Filter-CHKZVYB3.js → Filter-DVaqcFmI.js} +1 -1
  47. package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +1 -0
  48. package/dist-cli-web/assets/GlobalTaskBar-DpPMX0jx.js +1 -0
  49. package/dist-cli-web/assets/GroupRelationshipGalaxyView-CLnO3phW.js +1 -0
  50. package/dist-cli-web/assets/Label-Bt2U4lGy.js +1 -0
  51. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-Cnshyn0f.js +1 -0
  52. package/dist-cli-web/assets/MessageExportModal-BozaR9UZ.js +1 -0
  53. package/dist-cli-web/assets/PageHeader-CEe0xTl3.js +1 -0
  54. package/dist-cli-web/assets/PageTabs.vue_vue_type_script_setup_true_lang-D2DZrrnv.js +1 -0
  55. package/dist-cli-web/assets/{RelationshipGalaxyCanvas-mJhEPzsY.js → RelationshipGalaxyCanvas-6EamagxG.js} +3 -3
  56. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-Bf76yzmQ.js → RelationshipGalaxyThreeCanvas-B6GB4PWS.js} +166 -166
  57. package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CMuibfiA.css → RelationshipGalaxyThreeCanvas-TYGOUDiv.css} +1 -1
  58. package/dist-cli-web/assets/{RenderTargetSystem-1rM7OF-M.js → RenderTargetSystem-mYuj4uBS.js} +1 -1
  59. package/dist-cli-web/assets/RovingFocusItem-B2y6BG1a.js +1 -0
  60. package/dist-cli-web/assets/ScreenCaptureModal-BvAtSNNR.js +1 -0
  61. package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-ueEF4zHE.js +1 -0
  62. package/dist-cli-web/assets/Select-Cm4I8u9n.js +1 -0
  63. package/dist-cli-web/assets/SettingsModal-CjVax3I6.css +1 -0
  64. package/dist-cli-web/assets/SettingsModal-Cwshzj7i.js +2 -0
  65. package/dist-cli-web/assets/StorageTab-D7HzH16Y.js +1 -0
  66. package/dist-cli-web/assets/Switch-BVw4e_19.js +1 -0
  67. package/dist-cli-web/assets/Tabs-Dqwkh3Ea.js +1 -0
  68. package/dist-cli-web/assets/Tabs-OrJkANIA.js +1 -0
  69. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-DPIvbq75.js +1 -0
  70. package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-dEdo6CqM.js +1 -0
  71. package/dist-cli-web/assets/VisuallyHiddenInput-BOdkR_GC.js +1 -0
  72. package/dist-cli-web/assets/{WebGLRenderer-Y0jQtBvK.js → WebGLRenderer-BB8ukBI_.js} +2 -2
  73. package/dist-cli-web/assets/{WebGPURenderer-BcUvCPhd.js → WebGPURenderer-DDOzF1Kc.js} +2 -2
  74. package/dist-cli-web/assets/aiChat-CzFSFuOJ.js +8 -0
  75. package/dist-cli-web/assets/annual-summary-time-range-CmrAqcuj.js +1 -0
  76. package/dist-cli-web/assets/base-CawUl6ET.js +1 -0
  77. package/dist-cli-web/assets/{browserAll-BsR1UaCC.js → browserAll-BRqlnNxq.js} +2 -2
  78. package/dist-cli-web/assets/chatlabSiteLocale-CrCNb8QP.js +1 -0
  79. package/dist-cli-web/assets/clamp-jFYhBC76.js +1 -0
  80. package/dist-cli-web/assets/cli-web-ClAJ6Tao.js +1 -0
  81. package/dist-cli-web/assets/{dateFormat-B6JnNCxp.js → dateFormat-DXuDxIye.js} +1 -1
  82. package/dist-cli-web/assets/fetch-3_xX8Yi8.js +1 -0
  83. package/dist-cli-web/assets/{fetch-DGvqctIb.js → fetch-Bcy2T8Fe.js} +1 -1
  84. package/dist-cli-web/assets/{fetch-BAJ_VjWE.js → fetch-CGu53cxT.js} +1 -1
  85. package/dist-cli-web/assets/{fetch-bdY0RMM3.js → fetch-CP1afnT0.js} +1 -1
  86. package/dist-cli-web/assets/{fetch-Clj_RVUD.js → fetch-DWAIk_6_.js} +1 -1
  87. package/dist-cli-web/assets/{fetch-DtnhbqTN.js → fetch-DijSOvop.js} +1 -1
  88. package/dist-cli-web/assets/{fetch-D7vgaLpy.js → fetch-Dy38lkAa.js} +1 -1
  89. package/dist-cli-web/assets/{fetch-bhHY8rO7.js → fetch-DzKlTaLz.js} +1 -1
  90. package/dist-cli-web/assets/{fetch-CPloHijF.js → fetch-LY-yerw1.js} +1 -1
  91. package/dist-cli-web/assets/{fetch-BNcHe2-O.js → fetch-q480XYZQ.js} +1 -1
  92. package/dist-cli-web/assets/index-0dp58uL4.css +1 -0
  93. package/dist-cli-web/assets/index-1X7zdLgJ.css +1 -0
  94. package/dist-cli-web/assets/index-4enwdBcq.css +1 -0
  95. package/dist-cli-web/assets/index-AudHHbMs.js +1 -0
  96. package/dist-cli-web/assets/{index-DMYFpctK.js → index-BRV_D8dv.js} +3 -3
  97. package/dist-cli-web/assets/index-CF4DTbvD.js +1 -0
  98. package/dist-cli-web/assets/index-CMLoht5R.js +1 -0
  99. package/dist-cli-web/assets/index-CMPHZH6B.css +1 -0
  100. package/dist-cli-web/assets/index-CNzv7aBW.js +1 -0
  101. package/dist-cli-web/assets/index-CXUTiy-V.js +1 -0
  102. package/dist-cli-web/assets/index-D5m0P4ss.js +36 -0
  103. package/dist-cli-web/assets/index-Di6FmCdk.js +64 -0
  104. package/dist-cli-web/assets/index-DkOrt7_X.js +2 -0
  105. package/dist-cli-web/assets/index-Dlb6fC68.js +1 -0
  106. package/dist-cli-web/assets/index-IA7YUjQX.js +2 -0
  107. package/dist-cli-web/assets/index-kzU381y1.js +1 -0
  108. package/dist-cli-web/assets/index-z8Vogc-b.js +137 -0
  109. package/dist-cli-web/assets/lazy-overlay-visibility-Bd9ZFOgR.js +1 -0
  110. package/dist-cli-web/assets/member-select-utils-BXZ3oHct.js +1 -0
  111. package/dist-cli-web/assets/people-page-header-Bh3KXD4g.js +1 -0
  112. package/dist-cli-web/assets/prompt-DcAcgQiB.js +1 -0
  113. package/dist-cli-web/assets/relationship-galaxy-connections-Cg4G5yYE.js +1 -0
  114. package/dist-cli-web/assets/semantic-index-config-builder-CK6v64Y1.js +1 -0
  115. package/dist-cli-web/assets/service-YZnJyEMq.js +1 -0
  116. package/dist-cli-web/assets/useFormControl-B-aXrJZN.js +1 -0
  117. package/dist-cli-web/assets/useTable-DPEMaGby.js +1 -0
  118. package/dist-cli-web/assets/useToast-B36MzFFy.js +1 -0
  119. package/dist-cli-web/assets/vendor-echarts-DfAMQxdr.js +11 -0
  120. package/dist-cli-web/assets/{vendor-echarts-wordcloud-Ca8n6qS6.js → vendor-echarts-wordcloud-BsaEJTPY.js} +1 -1
  121. package/dist-cli-web/assets/vendor-zrender-PFmZe1EY.js +8 -0
  122. package/dist-cli-web/assets/{webworkerAll-CfZWlukP.js → webworkerAll-Dwqmy_YO.js} +1 -1
  123. package/dist-cli-web/index.html +2 -2
  124. package/package.json +1 -1
  125. package/dist/chunk-2WYXD5ZP.mjs.map +0 -1
  126. package/dist/chunk-AWXHYTD5.mjs.map +0 -1
  127. package/dist/chunk-JYBPJWE2.mjs.map +0 -1
  128. package/dist/chunk-XIWVJAXH.mjs.map +0 -1
  129. package/dist/chunk-ZVGDOSGG.mjs.map +0 -1
  130. package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-kN3uxTW8.js +0 -110
  131. package/dist-cli-web/assets/CanvasRenderer-Cmjtvski.js +0 -1
  132. package/dist-cli-web/assets/DataDirCleanupNotice-B5tyDzK-.js +0 -1
  133. package/dist-cli-web/assets/EChartWordcloud-Bd3L3G8I.js +0 -1
  134. package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-CKf_gC1k.js +0 -1
  135. package/dist-cli-web/assets/MessageExportModal-ODYCcSnk.js +0 -1
  136. package/dist-cli-web/assets/PageHeader-Bn7Jn8JW.js +0 -1
  137. package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-CADZpPia.js +0 -1
  138. package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-I3FYjhfs.js +0 -1
  139. package/dist-cli-web/assets/annual-summary-time-range-BIADd8qE.js +0 -1
  140. package/dist-cli-web/assets/base-BpxxC5Z5.js +0 -1
  141. package/dist-cli-web/assets/cli-web-BiWUOncc.js +0 -1
  142. package/dist-cli-web/assets/fetch-DSxgaVBO.js +0 -1
  143. package/dist-cli-web/assets/index-B6Gq11lw.css +0 -1
  144. package/dist-cli-web/assets/index-BJ2lywiG.js +0 -1
  145. package/dist-cli-web/assets/index-BT3_fSP6.js +0 -2
  146. package/dist-cli-web/assets/index-BVZhP1J5.js +0 -1
  147. package/dist-cli-web/assets/index-Br8r4pu8.js +0 -1
  148. package/dist-cli-web/assets/index-Byok4VaA.css +0 -1
  149. package/dist-cli-web/assets/index-CKnnxOci.js +0 -1
  150. package/dist-cli-web/assets/index-CXnYfvTv.js +0 -137
  151. package/dist-cli-web/assets/index-CsFLLk3T.css +0 -1
  152. package/dist-cli-web/assets/index-CzoOQ7zg.js +0 -1
  153. package/dist-cli-web/assets/index-D-dTTUGj.js +0 -1
  154. package/dist-cli-web/assets/index-DEbvEKzi.js +0 -2
  155. package/dist-cli-web/assets/index-KO7uPd8W.js +0 -64
  156. package/dist-cli-web/assets/index-lkmJ6dI7.js +0 -1
  157. package/dist-cli-web/assets/index-y7xPDpsW.js +0 -53
  158. package/dist-cli-web/assets/people-page-header-gjTyODWs.js +0 -1
  159. package/dist-cli-web/assets/vendor-echarts-B-i_56Av.js +0 -11
  160. package/dist-cli-web/assets/vendor-zrender-5DcYjSZk.js +0 -8
  161. /package/dist/{chat-command-QH7DXCAJ.mjs.map → chat-command-24RYGJAS.mjs.map} +0 -0
  162. /package/dist/{chunk-WJVE5Q4Y.mjs.map → chunk-BRZ6YIBR.mjs.map} +0 -0
  163. /package/dist/{chunk-C2PD3BS7.mjs.map → chunk-GQMCDWZQ.mjs.map} +0 -0
  164. /package/dist/{chunk-AWGXYFIC.mjs.map → chunk-KIMJXKZO.mjs.map} +0 -0
  165. /package/dist/{chunk-SWUTRDGZ.mjs.map → chunk-KXDJ7N6N.mjs.map} +0 -0
  166. /package/dist/{chunk-3X26JJIP.mjs.map → chunk-QKEARRFH.mjs.map} +0 -0
  167. /package/dist/{chunk-6U2QZO3N.mjs.map → chunk-U36TQMAF.mjs.map} +0 -0
  168. /package/dist/{chunk-2Q3HHVTS.mjs.map → chunk-ZBPPLXM4.mjs.map} +0 -0
  169. /package/dist/{http-D2F4QTQW.mjs.map → http-3K4YMWDE.mjs.map} +0 -0
  170. /package/dist/{import-H7X2JU2O.mjs.map → import-D4LV565A.mjs.map} +0 -0
  171. /package/dist/{mcp-UURXRGDF.mjs.map → mcp-KL22FKQ2.mjs.map} +0 -0
  172. /package/dist/{src-MHCO4HYY.mjs.map → src-COIB6Z4P.mjs.map} +0 -0
  173. /package/dist/{stream-import-BHXACUCA.mjs.map → stream-import-FSXCFG4M.mjs.map} +0 -0
  174. /package/dist/{worker-runtime-JOUCP2L4.mjs.map → worker-runtime-DPS2LURI.mjs.map} +0 -0
@@ -40,13 +40,17 @@ var cliConfigSchema = z.object({
40
40
  /** Allow the read-only `sql` query command. */
41
41
  allow_sql: z.boolean().default(true)
42
42
  });
43
+ var desktopConfigSchema = z.object({
44
+ close_behavior: z.enum(["background", "quit"]).default("background")
45
+ });
43
46
  var configSchema = z.object({
44
47
  llm: llmConfigSchema.default({}),
45
48
  data: dataConfigSchema.default({}),
46
49
  api: apiConfigSchema.default({}),
47
50
  locale: localeConfigSchema.default({}),
48
51
  ui: uiConfigSchema.default({}),
49
- cli: cliConfigSchema.default({})
52
+ cli: cliConfigSchema.default({}),
53
+ desktop: desktopConfigSchema.default({})
50
54
  });
51
55
 
52
56
  // ../../packages/config/src/loader.ts
@@ -4108,7 +4112,7 @@ function buildMsgConditions(options) {
4108
4112
 
4109
4113
  // ../../packages/core/src/query/advanced/social.ts
4110
4114
  function getMentionAnalysis(db, filter) {
4111
- const emptyResult = {
4115
+ const emptyResult2 = {
4112
4116
  topMentioners: [],
4113
4117
  topMentioned: [],
4114
4118
  totalMentions: 0
@@ -4117,7 +4121,7 @@ function getMentionAnalysis(db, filter) {
4117
4121
  `SELECT id, platform_id as platformId, COALESCE(group_nickname, account_name, platform_id) as name
4118
4122
  FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
4119
4123
  ).all();
4120
- if (members.length === 0) return emptyResult;
4124
+ if (members.length === 0) return emptyResult2;
4121
4125
  const nameToMemberId = /* @__PURE__ */ new Map();
4122
4126
  const memberIdToInfo = /* @__PURE__ */ new Map();
4123
4127
  for (const member of members) {
@@ -4158,7 +4162,7 @@ function getMentionAnalysis(db, filter) {
4158
4162
  }
4159
4163
  }
4160
4164
  }
4161
- if (totalMentions === 0) return emptyResult;
4165
+ if (totalMentions === 0) return emptyResult2;
4162
4166
  const topMentioners = [];
4163
4167
  for (const [memberId, count] of mentionerCount.entries()) {
4164
4168
  const info = memberIdToInfo.get(memberId);
@@ -4185,87 +4189,13 @@ function getMentionAnalysis(db, filter) {
4185
4189
  topMentioned.sort((a, b) => b.count - a.count);
4186
4190
  return { topMentioners, topMentioned, totalMentions };
4187
4191
  }
4188
- function getMentionGraph(db, filter) {
4189
- const emptyResult = { nodes: [], links: [], maxLinkValue: 0 };
4190
- const { clause, params } = buildTimeFilter(filter);
4191
- const msgFilterBase = clause ? clause.replace("WHERE", "AND") : "";
4192
- const msgFilterWithSystem = msgFilterBase + " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'";
4193
- const members = db.prepare(
4194
- `SELECT m.id, m.platform_id as platformId, COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,
4195
- COUNT(msg.id) as messageCount
4196
- FROM member m LEFT JOIN message msg ON m.id = msg.sender_id ${msgFilterWithSystem}
4197
- WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' GROUP BY m.id`
4198
- ).all(...params);
4199
- if (members.length === 0) return emptyResult;
4200
- const nameToMemberId = /* @__PURE__ */ new Map();
4201
- const memberIdToInfo = /* @__PURE__ */ new Map();
4202
- for (const member of members) {
4203
- memberIdToInfo.set(member.id, { name: member.name, messageCount: member.messageCount });
4204
- nameToMemberId.set(member.name, member.id);
4205
- const history = db.prepare("SELECT name FROM member_name_history WHERE member_id = ?").all(member.id);
4206
- for (const h of history) {
4207
- if (!nameToMemberId.has(h.name)) nameToMemberId.set(h.name, member.id);
4208
- }
4209
- }
4210
- let whereClause = clause;
4211
- if (whereClause.includes("WHERE")) {
4212
- whereClause += " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
4213
- } else {
4214
- whereClause = " WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
4215
- }
4216
- const messages = db.prepare(
4217
- `SELECT msg.sender_id as senderId, msg.content FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
4218
- ).all(...params);
4219
- const mentionMatrix = /* @__PURE__ */ new Map();
4220
- const mentionRegex = /@([^\s@]+)/g;
4221
- for (const msg of messages) {
4222
- const matches = msg.content.matchAll(mentionRegex);
4223
- const mentionedInThisMsg = /* @__PURE__ */ new Set();
4224
- for (const match of matches) {
4225
- const mentionedId = nameToMemberId.get(match[1]);
4226
- if (mentionedId && mentionedId !== msg.senderId && !mentionedInThisMsg.has(mentionedId)) {
4227
- mentionedInThisMsg.add(mentionedId);
4228
- if (!mentionMatrix.has(msg.senderId)) mentionMatrix.set(msg.senderId, /* @__PURE__ */ new Map());
4229
- const fromMap = mentionMatrix.get(msg.senderId);
4230
- fromMap.set(mentionedId, (fromMap.get(mentionedId) || 0) + 1);
4231
- }
4232
- }
4233
- }
4234
- const involvedMemberIds = /* @__PURE__ */ new Set();
4235
- for (const [fromId, toMap] of mentionMatrix.entries()) {
4236
- involvedMemberIds.add(fromId);
4237
- for (const toId of toMap.keys()) involvedMemberIds.add(toId);
4238
- }
4239
- const maxMessageCount = Math.max(...members.filter((m) => involvedMemberIds.has(m.id)).map((m) => m.messageCount), 1);
4240
- const nodes = [];
4241
- for (const memberId of involvedMemberIds) {
4242
- const info = memberIdToInfo.get(memberId);
4243
- if (info) {
4244
- const symbolSize = 20 + info.messageCount / maxMessageCount * 40;
4245
- nodes.push({ id: memberId, name: info.name, value: info.messageCount, symbolSize: Math.round(symbolSize) });
4246
- }
4247
- }
4248
- const links = [];
4249
- let maxLinkValue = 0;
4250
- for (const [fromId, toMap] of mentionMatrix.entries()) {
4251
- const fromInfo = memberIdToInfo.get(fromId);
4252
- if (!fromInfo) continue;
4253
- for (const [toId, count] of toMap.entries()) {
4254
- const toInfo = memberIdToInfo.get(toId);
4255
- if (!toInfo) continue;
4256
- links.push({ source: fromInfo.name, target: toInfo.name, value: count });
4257
- maxLinkValue = Math.max(maxLinkValue, count);
4258
- }
4259
- }
4260
- return { nodes, links, maxLinkValue };
4261
- }
4262
4192
  function keywordToPattern(keyword) {
4263
4193
  const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4264
4194
  if (keyword === "\u54C8\u54C8") return "\u54C8\u54C8+";
4265
4195
  return escaped;
4266
4196
  }
4267
4197
  function getLaughAnalysis(db, filter, keywords) {
4268
- const emptyResult = {
4198
+ const emptyResult2 = {
4269
4199
  rankByRate: [],
4270
4200
  rankByCount: [],
4271
4201
  typeDistribution: [],
@@ -4274,7 +4204,7 @@ function getLaughAnalysis(db, filter, keywords) {
4274
4204
  groupLaughRate: 0
4275
4205
  };
4276
4206
  const laughKeywords = keywords?.map((keyword) => keyword.trim()).filter(Boolean) ?? [];
4277
- if (laughKeywords.length === 0) return emptyResult;
4207
+ if (laughKeywords.length === 0) return emptyResult2;
4278
4208
  const patterns = laughKeywords.map(keywordToPattern);
4279
4209
  const laughRegex = new RegExp(`(${patterns.join("|")})`, "gi");
4280
4210
  const { clause, params } = buildTimeFilter(filter);
@@ -4289,7 +4219,7 @@ function getLaughAnalysis(db, filter, keywords) {
4289
4219
  COALESCE(m.group_nickname, m.account_name, m.platform_id) as name
4290
4220
  FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
4291
4221
  ).all(...params);
4292
- if (messages.length === 0) return emptyResult;
4222
+ if (messages.length === 0) return emptyResult2;
4293
4223
  const memberStats = /* @__PURE__ */ new Map();
4294
4224
  const typeCount = /* @__PURE__ */ new Map();
4295
4225
  let totalLaughs = 0;
@@ -4322,7 +4252,7 @@ function getLaughAnalysis(db, filter, keywords) {
4322
4252
  }
4323
4253
  }
4324
4254
  }
4325
- if (totalLaughs === 0) return emptyResult;
4255
+ if (totalLaughs === 0) return emptyResult2;
4326
4256
  const rankItems = [];
4327
4257
  for (const [memberId, stats] of memberStats.entries()) {
4328
4258
  if (stats.laughCount > 0) {
@@ -4412,7 +4342,7 @@ function accumulateCoOccurrencePairs(messages, options) {
4412
4342
  }
4413
4343
  function getClusterGraph(db, filter, options) {
4414
4344
  const opts = { ...DEFAULT_CLUSTER_OPTIONS, ...options };
4415
- const emptyResult = {
4345
+ const emptyResult2 = {
4416
4346
  nodes: [],
4417
4347
  links: [],
4418
4348
  maxLinkValue: 0,
@@ -4424,7 +4354,7 @@ function getClusterGraph(db, filter, options) {
4424
4354
  (SELECT COUNT(*) FROM message WHERE sender_id = member.id) as messageCount
4425
4355
  FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
4426
4356
  ).all();
4427
- if (members.length < 2) return { ...emptyResult, stats: { ...emptyResult.stats, totalMembers: members.length } };
4357
+ if (members.length < 2) return { ...emptyResult2, stats: { ...emptyResult2.stats, totalMembers: members.length } };
4428
4358
  const memberInfo = /* @__PURE__ */ new Map();
4429
4359
  for (const m of members)
4430
4360
  memberInfo.set(m.id, { name: m.name, platformId: m.platformId, messageCount: m.messageCount });
@@ -4441,8 +4371,8 @@ function getClusterGraph(db, filter, options) {
4441
4371
  ).all(...params);
4442
4372
  if (messages.length < 2)
4443
4373
  return {
4444
- ...emptyResult,
4445
- stats: { ...emptyResult.stats, totalMembers: members.length, totalMessages: messages.length }
4374
+ ...emptyResult2,
4375
+ stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
4446
4376
  };
4447
4377
  const memberMsgCount = /* @__PURE__ */ new Map();
4448
4378
  for (const msg of messages) memberMsgCount.set(msg.senderId, (memberMsgCount.get(msg.senderId) || 0) + 1);
@@ -4481,8 +4411,8 @@ function getClusterGraph(db, filter, options) {
4481
4411
  const keptEdges = edges.slice(0, opts.topEdges);
4482
4412
  if (keptEdges.length === 0)
4483
4413
  return {
4484
- ...emptyResult,
4485
- stats: { ...emptyResult.stats, totalMembers: members.length, totalMessages: messages.length }
4414
+ ...emptyResult2,
4415
+ stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
4486
4416
  };
4487
4417
  const involvedIds = /* @__PURE__ */ new Set();
4488
4418
  for (const edge of keptEdges) {
@@ -4755,7 +4685,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
4755
4685
  ORDER BY msg.ts ASC, msg.id ASC`
4756
4686
  ).all(...messageTimeFilter.params);
4757
4687
  const edgeStats = /* @__PURE__ */ new Map();
4758
- const ensureEdge = (aId, bId) => {
4688
+ const ensureEdge2 = (aId, bId) => {
4759
4689
  const sourceId = Math.min(aId, bId);
4760
4690
  const targetId = Math.max(aId, bId);
4761
4691
  const key = `${sourceId}:${targetId}`;
@@ -4775,7 +4705,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
4775
4705
  };
4776
4706
  for (const pair of accumulateCoOccurrencePairs(coOccurrenceRows)) {
4777
4707
  if (!contactById.has(pair.sourceId) || !contactById.has(pair.targetId)) continue;
4778
- const edge = ensureEdge(pair.sourceId, pair.targetId);
4708
+ const edge = ensureEdge2(pair.sourceId, pair.targetId);
4779
4709
  edge.coOccurrenceCount += pair.coOccurrenceCount;
4780
4710
  edge.coOccurrenceRawScore += pair.rawScore;
4781
4711
  edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, pair.lastOccurrenceTs);
@@ -4797,7 +4727,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
4797
4727
  for (const row of replyRows) {
4798
4728
  if (!contactById.has(row.replySenderId) || !contactById.has(row.targetSenderId)) continue;
4799
4729
  if (row.replySenderId === row.targetSenderId) continue;
4800
- const edge = ensureEdge(row.replySenderId, row.targetSenderId);
4730
+ const edge = ensureEdge2(row.replySenderId, row.targetSenderId);
4801
4731
  if (row.replySenderId === edge.sourceId) edge.repliesFromSourceToTarget++;
4802
4732
  else edge.repliesFromTargetToSource++;
4803
4733
  edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, row.replyTs);
@@ -6005,12 +5935,456 @@ function getCatchphraseAnalysis(db, filter) {
6005
5935
  return { members };
6006
5936
  }
6007
5937
 
5938
+ // ../../packages/core/src/query/advanced/group-relationship-galaxy.ts
5939
+ var GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION = "group-relationship-galaxy-v1";
5940
+ var REPLY_WEIGHT = 3;
5941
+ var MENTION_WEIGHT = 2;
5942
+ var CO_OCCURRENCE_COUNT_WEIGHT = 0.05;
5943
+ var MIN_EDGE_WEIGHT = 1e-6;
5944
+ var DEFAULT_LIMITS = {
5945
+ nodeLimit: 400,
5946
+ edgeLimit: 1200,
5947
+ perNodeEdgeLimit: 10
5948
+ };
5949
+ var COMMUNITY_COLORS = [
5950
+ "#7dd3fc",
5951
+ "#c4b5fd",
5952
+ "#f0abfc",
5953
+ "#5eead4",
5954
+ "#fda4af",
5955
+ "#fde68a",
5956
+ "#93c5fd",
5957
+ "#6ee7b7",
5958
+ "#d8b4fe",
5959
+ "#fdba74"
5960
+ ];
5961
+ function getGroupRelationshipGalaxy(db, filter, options = {}) {
5962
+ const limits = normalizeLimits(options);
5963
+ const allMembers = getNonSystemMembersForContacts(db);
5964
+ const memberById = new Map(allMembers.map((member) => [member.id, member]));
5965
+ const messageRows = queryRelationshipMessages(db, filter).filter((message) => memberById.has(message.senderId));
5966
+ const activeMembers = buildActiveMembers(allMembers, messageRows);
5967
+ const activeMemberById = new Map(activeMembers.map((member) => [member.id, member]));
5968
+ if (activeMembers.length < 2) {
5969
+ return emptyResult(allMembers.length, activeMembers.length);
5970
+ }
5971
+ const edgeByPair = /* @__PURE__ */ new Map();
5972
+ for (const pair of accumulateCoOccurrencePairs(messageRows)) {
5973
+ if (!activeMemberById.has(pair.sourceId) || !activeMemberById.has(pair.targetId)) continue;
5974
+ const edge = ensureEdge(edgeByPair, activeMemberById, pair.sourceId, pair.targetId);
5975
+ edge.coOccurrenceCount += pair.coOccurrenceCount;
5976
+ edge.coOccurrenceRawScore += pair.rawScore;
5977
+ edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, pair.lastOccurrenceTs);
5978
+ }
5979
+ accumulateReplies(db, filter, activeMemberById, edgeByPair);
5980
+ accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair);
5981
+ const weightedEdges = [...edgeByPair.values()].map((edge) => ({ ...edge, weight: roundNum2(computeEdgeWeight(edge)) })).filter((edge) => edge.weight >= MIN_EDGE_WEIGHT).sort(compareEdges);
5982
+ if (weightedEdges.length === 0) {
5983
+ return emptyResult(allMembers.length, activeMembers.length);
5984
+ }
5985
+ const candidateNodeKeys = selectCandidateNodeKeys(weightedEdges, limits.nodeLimit);
5986
+ const candidateEdges = weightedEdges.filter(
5987
+ (edge) => candidateNodeKeys.has(edge.sourceKey) && candidateNodeKeys.has(edge.targetKey)
5988
+ );
5989
+ const keptEdges = selectEdges(candidateEdges, limits.perNodeEdgeLimit, limits.edgeLimit);
5990
+ const displayedNodeKeys = /* @__PURE__ */ new Set();
5991
+ for (const edge of keptEdges) {
5992
+ displayedNodeKeys.add(edge.sourceKey);
5993
+ displayedNodeKeys.add(edge.targetKey);
5994
+ }
5995
+ const displayedMembers = activeMembers.filter((member) => displayedNodeKeys.has(member.key));
5996
+ const degreeByKey = computeDegreeByKey(keptEdges);
5997
+ const rankedMembers = [...displayedMembers].sort((a, b) => {
5998
+ const degreeDiff = (degreeByKey.get(b.key) ?? 0) - (degreeByKey.get(a.key) ?? 0);
5999
+ return degreeDiff || b.messageCount - a.messageCount || a.key.localeCompare(b.key);
6000
+ });
6001
+ const communityAssignments = detectWeightedCommunities(rankedMembers, keptEdges, degreeByKey);
6002
+ const communityByNodeKey = /* @__PURE__ */ new Map();
6003
+ for (const community of communityAssignments) {
6004
+ for (const nodeKey of community.nodeKeys) communityByNodeKey.set(nodeKey, community);
6005
+ }
6006
+ const maxDegree = Math.max(...degreeByKey.values(), 1);
6007
+ const renderNodes = [];
6008
+ const memberDetails = [];
6009
+ const incidentTotals = buildIncidentTotals(weightedEdges);
6010
+ for (const [index, member] of rankedMembers.entries()) {
6011
+ const degree = degreeByKey.get(member.key) ?? 0;
6012
+ const score = degree / maxDegree;
6013
+ const community = communityByNodeKey.get(member.key);
6014
+ const position = layoutNode(member.key, index, community, rankedMembers, communityByNodeKey);
6015
+ const totals = incidentTotals.get(member.key) ?? createIncidentTotals();
6016
+ const rank = index + 1;
6017
+ renderNodes.push({
6018
+ key: member.key,
6019
+ displayName: member.displayName,
6020
+ avatar: member.avatar,
6021
+ score: roundNum2(score),
6022
+ rank,
6023
+ communityId: community.id,
6024
+ x: position.x,
6025
+ y: position.y,
6026
+ size: roundNum2(18 + Math.sqrt(score) * 30, 2),
6027
+ color: community.color,
6028
+ labelVisibility: rank <= 8 ? 2 : rank <= 30 ? 1 : 0
6029
+ });
6030
+ memberDetails.push({
6031
+ key: member.key,
6032
+ memberId: member.id,
6033
+ platformId: member.platformId,
6034
+ displayName: member.displayName,
6035
+ avatar: member.avatar,
6036
+ messageCount: member.messageCount,
6037
+ lastMessageTs: member.lastMessageTs,
6038
+ relationshipScore: roundNum2(score),
6039
+ rank,
6040
+ communityId: community.id,
6041
+ replyInteractionCount: totals.replyInteractionCount,
6042
+ mentionInteractionCount: totals.mentionInteractionCount,
6043
+ coOccurrenceCount: totals.coOccurrenceCount,
6044
+ coOccurrenceRawScore: roundNum2(totals.coOccurrenceRawScore),
6045
+ lastInteractionTs: totals.lastInteractionTs
6046
+ });
6047
+ }
6048
+ const maxEdgeWeight = Math.max(...keptEdges.map((edge) => edge.weight), 1);
6049
+ const renderEdges = keptEdges.map((edge) => ({
6050
+ id: edge.id,
6051
+ sourceKey: edge.sourceKey,
6052
+ targetKey: edge.targetKey,
6053
+ weight: edge.weight,
6054
+ visibility: edge.weight / maxEdgeWeight >= 0.35 ? 2 : 1,
6055
+ lastInteractionTs: edge.lastInteractionTs
6056
+ }));
6057
+ const edgeDetails = keptEdges.map((edge) => ({
6058
+ id: edge.id,
6059
+ sourceKey: edge.sourceKey,
6060
+ targetKey: edge.targetKey,
6061
+ weight: edge.weight,
6062
+ coOccurrenceCount: edge.coOccurrenceCount,
6063
+ coOccurrenceRawScore: roundNum2(edge.coOccurrenceRawScore),
6064
+ replyInteractionCount: edge.replyInteractionCount,
6065
+ mentionInteractionCount: edge.mentionInteractionCount,
6066
+ lastInteractionTs: edge.lastInteractionTs
6067
+ }));
6068
+ const renderCommunities = communityAssignments.map((community, index) => ({
6069
+ id: community.id,
6070
+ label: `community-${index + 1}`,
6071
+ size: community.nodeKeys.length,
6072
+ x: community.x,
6073
+ y: community.y,
6074
+ color: community.color
6075
+ }));
6076
+ return {
6077
+ graph: { nodes: renderNodes, edges: renderEdges, communities: renderCommunities },
6078
+ members: memberDetails,
6079
+ edges: edgeDetails,
6080
+ stats: {
6081
+ totalMembers: allMembers.length,
6082
+ activeMembers: activeMembers.length,
6083
+ displayedMembers: renderNodes.length,
6084
+ displayedEdges: renderEdges.length,
6085
+ communityCount: renderCommunities.length
6086
+ },
6087
+ algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
6088
+ };
6089
+ }
6090
+ function queryRelationshipMessages(db, filter) {
6091
+ const { clause, params } = buildTimeFilter(filter, "msg");
6092
+ const where = appendConditions(clause, ["msg.type NOT IN (80, 81)"]);
6093
+ return db.prepare(`SELECT msg.sender_id as senderId, msg.ts FROM message msg ${where} ORDER BY msg.ts ASC, msg.id ASC`).all(...params);
6094
+ }
6095
+ function buildActiveMembers(allMembers, messages) {
6096
+ const messageStats = /* @__PURE__ */ new Map();
6097
+ for (const message of messages) {
6098
+ const stats = messageStats.get(message.senderId) ?? { count: 0, lastTs: 0 };
6099
+ stats.count++;
6100
+ stats.lastTs = Math.max(stats.lastTs, message.ts);
6101
+ messageStats.set(message.senderId, stats);
6102
+ }
6103
+ const active = allMembers.filter((member) => messageStats.has(member.id)).map((member) => ({
6104
+ ...member,
6105
+ key: memberKey(member.id),
6106
+ displayName: member.name,
6107
+ messageCount: messageStats.get(member.id).count,
6108
+ lastMessageTs: messageStats.get(member.id).lastTs || null
6109
+ }));
6110
+ const nameCounts = /* @__PURE__ */ new Map();
6111
+ for (const member of active) nameCounts.set(member.displayName, (nameCounts.get(member.displayName) ?? 0) + 1);
6112
+ for (const member of active) {
6113
+ if ((nameCounts.get(member.displayName) ?? 0) > 1) {
6114
+ member.displayName = `${member.displayName}#${member.platformId.slice(-4)}`;
6115
+ }
6116
+ }
6117
+ return active;
6118
+ }
6119
+ function accumulateReplies(db, filter, activeMemberById, edgeByPair) {
6120
+ if (!hasColumn(db, "message", "reply_to_message_id") || !hasColumn(db, "message", "platform_message_id")) return;
6121
+ const { clause, params } = buildTimeFilter(filter, "msg");
6122
+ const conditions = ["msg.type NOT IN (80, 81)", "target.type NOT IN (80, 81)", "msg.reply_to_message_id IS NOT NULL"];
6123
+ const targetParams = [];
6124
+ if (filter?.startTs !== void 0) {
6125
+ conditions.push("target.ts >= ?");
6126
+ targetParams.push(filter.startTs);
6127
+ }
6128
+ if (filter?.endTs !== void 0) {
6129
+ conditions.push("target.ts <= ?");
6130
+ targetParams.push(filter.endTs);
6131
+ }
6132
+ const rows = db.prepare(
6133
+ `SELECT msg.sender_id as replySenderId, msg.ts as replyTs, target.sender_id as targetSenderId
6134
+ FROM message msg
6135
+ JOIN message target ON msg.reply_to_message_id = target.platform_message_id
6136
+ ${appendConditions(clause, conditions)}`
6137
+ ).all(...params, ...targetParams);
6138
+ for (const row of rows) {
6139
+ if (row.replySenderId === row.targetSenderId) continue;
6140
+ if (!activeMemberById.has(row.replySenderId) || !activeMemberById.has(row.targetSenderId)) continue;
6141
+ const edge = ensureEdge(edgeByPair, activeMemberById, row.replySenderId, row.targetSenderId);
6142
+ edge.replyInteractionCount++;
6143
+ edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.replyTs);
6144
+ }
6145
+ }
6146
+ function accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair) {
6147
+ const nameToMemberId = buildMentionNameIndex(db, activeMembers);
6148
+ const { clause, params } = buildTimeFilter(filter, "msg");
6149
+ const where = appendConditions(clause, ["msg.type = 0", "msg.content IS NOT NULL", "msg.content LIKE '%@%'"]);
6150
+ const rows = db.prepare(`SELECT msg.sender_id as senderId, msg.ts, msg.content FROM message msg ${where}`).all(...params);
6151
+ const mentionRegex = /@([^\s@]+)/g;
6152
+ for (const row of rows) {
6153
+ if (!activeMemberById.has(row.senderId)) continue;
6154
+ const mentionedInMessage = /* @__PURE__ */ new Set();
6155
+ for (const match of row.content.matchAll(mentionRegex)) {
6156
+ const mentionedId = nameToMemberId.get(match[1]);
6157
+ if (mentionedId === null || mentionedId === void 0 || mentionedId === row.senderId) continue;
6158
+ if (!activeMemberById.has(mentionedId) || mentionedInMessage.has(mentionedId)) continue;
6159
+ mentionedInMessage.add(mentionedId);
6160
+ const edge = ensureEdge(edgeByPair, activeMemberById, row.senderId, mentionedId);
6161
+ edge.mentionInteractionCount++;
6162
+ edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.ts);
6163
+ }
6164
+ }
6165
+ }
6166
+ function buildMentionNameIndex(db, members) {
6167
+ const index = /* @__PURE__ */ new Map();
6168
+ const addName = (name, memberId) => {
6169
+ const trimmed = name.trim();
6170
+ if (!trimmed) return;
6171
+ const existing = index.get(trimmed);
6172
+ if (existing === void 0 || existing === memberId) index.set(trimmed, memberId);
6173
+ else index.set(trimmed, null);
6174
+ };
6175
+ for (const member of members) {
6176
+ addName(member.name, member.id);
6177
+ for (const alias of member.aliases) addName(alias, member.id);
6178
+ }
6179
+ if (hasTable(db, "member_name_history")) {
6180
+ const memberIds = new Set(members.map((member) => member.id));
6181
+ const rows = db.prepare("SELECT member_id as memberId, name FROM member_name_history").all();
6182
+ for (const row of rows) {
6183
+ if (memberIds.has(row.memberId)) addName(row.name, row.memberId);
6184
+ }
6185
+ }
6186
+ return index;
6187
+ }
6188
+ function ensureEdge(edgeByPair, memberById, aId, bId) {
6189
+ const sourceId = Math.min(aId, bId);
6190
+ const targetId = Math.max(aId, bId);
6191
+ const id = `${memberKey(sourceId)}--${memberKey(targetId)}`;
6192
+ const existing = edgeByPair.get(id);
6193
+ if (existing) return existing;
6194
+ const created = {
6195
+ id,
6196
+ sourceId,
6197
+ targetId,
6198
+ sourceKey: memberById.get(sourceId).key,
6199
+ targetKey: memberById.get(targetId).key,
6200
+ coOccurrenceCount: 0,
6201
+ coOccurrenceRawScore: 0,
6202
+ replyInteractionCount: 0,
6203
+ mentionInteractionCount: 0,
6204
+ lastInteractionTs: null,
6205
+ weight: 0
6206
+ };
6207
+ edgeByPair.set(id, created);
6208
+ return created;
6209
+ }
6210
+ function computeEdgeWeight(edge) {
6211
+ return edge.coOccurrenceRawScore + edge.replyInteractionCount * REPLY_WEIGHT + edge.mentionInteractionCount * MENTION_WEIGHT + edge.coOccurrenceCount * CO_OCCURRENCE_COUNT_WEIGHT;
6212
+ }
6213
+ function selectCandidateNodeKeys(edges, nodeLimit) {
6214
+ const degree = computeDegreeByKey(edges);
6215
+ return new Set(
6216
+ [...degree.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, nodeLimit).map(([key]) => key)
6217
+ );
6218
+ }
6219
+ function selectEdges(edges, perNodeLimit, edgeLimit) {
6220
+ const incident = /* @__PURE__ */ new Map();
6221
+ for (const edge of edges) {
6222
+ for (const key of [edge.sourceKey, edge.targetKey]) {
6223
+ const list = incident.get(key) ?? [];
6224
+ list.push(edge);
6225
+ incident.set(key, list);
6226
+ }
6227
+ }
6228
+ const selectedIds = /* @__PURE__ */ new Set();
6229
+ for (const list of incident.values()) {
6230
+ list.sort(compareEdges);
6231
+ for (const edge of list.slice(0, perNodeLimit)) selectedIds.add(edge.id);
6232
+ }
6233
+ return edges.filter((edge) => selectedIds.has(edge.id)).sort(compareEdges).slice(0, edgeLimit);
6234
+ }
6235
+ function computeDegreeByKey(edges) {
6236
+ const degree = /* @__PURE__ */ new Map();
6237
+ for (const edge of edges) {
6238
+ degree.set(edge.sourceKey, (degree.get(edge.sourceKey) ?? 0) + edge.weight);
6239
+ degree.set(edge.targetKey, (degree.get(edge.targetKey) ?? 0) + edge.weight);
6240
+ }
6241
+ return degree;
6242
+ }
6243
+ function detectWeightedCommunities(members, edges, degreeByKey) {
6244
+ const parent = new Map(members.map((member) => [member.key, member.key]));
6245
+ const strongestByKey = /* @__PURE__ */ new Map();
6246
+ for (const edge of edges) {
6247
+ strongestByKey.set(edge.sourceKey, Math.max(strongestByKey.get(edge.sourceKey) ?? 0, edge.weight));
6248
+ strongestByKey.set(edge.targetKey, Math.max(strongestByKey.get(edge.targetKey) ?? 0, edge.weight));
6249
+ }
6250
+ const find = (key) => {
6251
+ const current = parent.get(key) ?? key;
6252
+ if (current === key) return current;
6253
+ const root = find(current);
6254
+ parent.set(key, root);
6255
+ return root;
6256
+ };
6257
+ const union = (a, b) => {
6258
+ const aRoot = find(a);
6259
+ const bRoot = find(b);
6260
+ if (aRoot === bRoot) return;
6261
+ const [source, target] = aRoot.localeCompare(bRoot) <= 0 ? [aRoot, bRoot] : [bRoot, aRoot];
6262
+ parent.set(target, source);
6263
+ };
6264
+ for (const edge of edges) {
6265
+ const sourceThreshold = (strongestByKey.get(edge.sourceKey) ?? edge.weight) * 0.6;
6266
+ const targetThreshold = (strongestByKey.get(edge.targetKey) ?? edge.weight) * 0.6;
6267
+ if (edge.weight >= sourceThreshold && edge.weight >= targetThreshold) union(edge.sourceKey, edge.targetKey);
6268
+ }
6269
+ const groups = /* @__PURE__ */ new Map();
6270
+ for (const member of members) {
6271
+ const root = find(member.key);
6272
+ const list = groups.get(root) ?? [];
6273
+ list.push(member.key);
6274
+ groups.set(root, list);
6275
+ }
6276
+ const sortedGroups = [...groups.values()].map((keys) => keys.sort()).sort((a, b) => {
6277
+ const aWeight = a.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
6278
+ const bWeight = b.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
6279
+ return bWeight - aWeight || a[0].localeCompare(b[0]);
6280
+ });
6281
+ return sortedGroups.map((nodeKeys, index) => {
6282
+ const center = layoutCommunity(index, sortedGroups.length);
6283
+ return {
6284
+ id: `community:${nodeKeys[0]}`,
6285
+ nodeKeys,
6286
+ color: COMMUNITY_COLORS[stableHash(nodeKeys[0]) % COMMUNITY_COLORS.length],
6287
+ x: center.x,
6288
+ y: center.y
6289
+ };
6290
+ });
6291
+ }
6292
+ function layoutCommunity(index, count) {
6293
+ if (index === 0 || count === 1) return { x: 0, y: 0 };
6294
+ const ringIndex = index - 1;
6295
+ const ring = Math.floor(ringIndex / 6);
6296
+ const position = ringIndex % 6;
6297
+ const radius = 380 + ring * 280;
6298
+ const angle = -Math.PI / 2 + position / 6 * Math.PI * 2 + ring * 0.31;
6299
+ return { x: roundNum2(Math.cos(angle) * radius, 2), y: roundNum2(Math.sin(angle) * radius, 2) };
6300
+ }
6301
+ function layoutNode(nodeKey, globalIndex, community, members, communityByNodeKey) {
6302
+ const communityMembers = members.filter((member) => communityByNodeKey.get(member.key)?.id === community.id);
6303
+ const localIndex = communityMembers.findIndex((member) => member.key === nodeKey);
6304
+ const angle = localIndex * 2.399963229728653 + stableHash(nodeKey) % 1e3 / 1e3;
6305
+ const radius = localIndex === 0 ? 0 : 54 + Math.sqrt(localIndex) * 42;
6306
+ const globalJitter = globalIndex % 3 * 2;
6307
+ return {
6308
+ x: roundNum2(community.x + Math.cos(angle) * (radius + globalJitter), 2),
6309
+ y: roundNum2(community.y + Math.sin(angle) * (radius + globalJitter), 2)
6310
+ };
6311
+ }
6312
+ function buildIncidentTotals(edges) {
6313
+ const totals = /* @__PURE__ */ new Map();
6314
+ for (const edge of edges) {
6315
+ for (const key of [edge.sourceKey, edge.targetKey]) {
6316
+ const item = totals.get(key) ?? createIncidentTotals();
6317
+ item.replyInteractionCount += edge.replyInteractionCount;
6318
+ item.mentionInteractionCount += edge.mentionInteractionCount;
6319
+ item.coOccurrenceCount += edge.coOccurrenceCount;
6320
+ item.coOccurrenceRawScore += edge.coOccurrenceRawScore;
6321
+ item.lastInteractionTs = maxTimestamp(item.lastInteractionTs, edge.lastInteractionTs);
6322
+ totals.set(key, item);
6323
+ }
6324
+ }
6325
+ return totals;
6326
+ }
6327
+ function createIncidentTotals() {
6328
+ return {
6329
+ replyInteractionCount: 0,
6330
+ mentionInteractionCount: 0,
6331
+ coOccurrenceCount: 0,
6332
+ coOccurrenceRawScore: 0,
6333
+ lastInteractionTs: null
6334
+ };
6335
+ }
6336
+ function normalizeLimits(options) {
6337
+ return {
6338
+ nodeLimit: normalizeLimit(options.nodeLimit, DEFAULT_LIMITS.nodeLimit),
6339
+ edgeLimit: normalizeLimit(options.edgeLimit, DEFAULT_LIMITS.edgeLimit),
6340
+ perNodeEdgeLimit: normalizeLimit(options.perNodeEdgeLimit, DEFAULT_LIMITS.perNodeEdgeLimit)
6341
+ };
6342
+ }
6343
+ function normalizeLimit(value, fallback) {
6344
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
6345
+ }
6346
+ function appendConditions(clause, conditions) {
6347
+ const existing = clause.trim().replace(/^WHERE\s+/i, "");
6348
+ return `WHERE ${[existing, ...conditions].filter(Boolean).join(" AND ")}`;
6349
+ }
6350
+ function emptyResult(totalMembers, activeMembers) {
6351
+ return {
6352
+ graph: { nodes: [], edges: [], communities: [] },
6353
+ members: [],
6354
+ edges: [],
6355
+ stats: { totalMembers, activeMembers, displayedMembers: 0, displayedEdges: 0, communityCount: 0 },
6356
+ algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
6357
+ };
6358
+ }
6359
+ function compareEdges(a, b) {
6360
+ return b.weight - a.weight || a.id.localeCompare(b.id);
6361
+ }
6362
+ function memberKey(memberId) {
6363
+ return `member:${memberId}`;
6364
+ }
6365
+ function maxTimestamp(current, next) {
6366
+ if (next === null) return current;
6367
+ return Math.max(current ?? 0, next) || null;
6368
+ }
6369
+ function roundNum2(value, digits = 4) {
6370
+ const factor = 10 ** digits;
6371
+ return Math.round(value * factor) / factor;
6372
+ }
6373
+ function stableHash(value) {
6374
+ let hash = 2166136261;
6375
+ for (let index = 0; index < value.length; index++) {
6376
+ hash ^= value.charCodeAt(index);
6377
+ hash = Math.imul(hash, 16777619);
6378
+ }
6379
+ return hash >>> 0;
6380
+ }
6381
+
6008
6382
  // ../../packages/core/src/query/advanced/relationship.ts
6009
6383
  var ICE_BREAK_THRESHOLD = 24 * 60 * 60;
6010
6384
  var DEFAULT_PERSEVERANCE_THRESHOLD = 300;
6011
6385
  function getRelationshipStats(db, filter, options) {
6012
6386
  const perseveranceThreshold = options?.perseveranceThreshold ?? DEFAULT_PERSEVERANCE_THRESHOLD;
6013
- const emptyResult = {
6387
+ const emptyResult2 = {
6014
6388
  months: [],
6015
6389
  members: [],
6016
6390
  totalSessions: 0,
@@ -6022,7 +6396,7 @@ function getRelationshipStats(db, filter, options) {
6022
6396
  monthlyPerseverance: []
6023
6397
  };
6024
6398
  const sessionCount = db.prepare("SELECT COUNT(*) as count FROM segment").get();
6025
- if (!sessionCount || sessionCount.count === 0) return emptyResult;
6399
+ if (!sessionCount || sessionCount.count === 0) return emptyResult2;
6026
6400
  const timeConditions = [];
6027
6401
  const params = [];
6028
6402
  if (filter?.startTs !== void 0) {
@@ -6755,7 +7129,7 @@ function getCheckInAnalysis(db, filter) {
6755
7129
  return computeCheckIn(dailyActivity);
6756
7130
  }
6757
7131
  function computeMemeBattle(messages) {
6758
- const emptyResult = {
7132
+ const emptyResult2 = {
6759
7133
  topBattles: [],
6760
7134
  rankByCount: [],
6761
7135
  rankByImageCount: [],
@@ -6789,7 +7163,7 @@ function computeMemeBattle(messages) {
6789
7163
  }
6790
7164
  }
6791
7165
  processChain();
6792
- if (battles.length === 0) return emptyResult;
7166
+ if (battles.length === 0) return emptyResult2;
6793
7167
  const topBattles = battles.map((battle) => ({
6794
7168
  startTime: battle.startTime,
6795
7169
  endTime: battle.endTime,
@@ -6868,7 +7242,7 @@ function getMemeBattleAnalysis(db, filter) {
6868
7242
  return computeMemeBattle(messages);
6869
7243
  }
6870
7244
  function computeNightOwl(messages) {
6871
- const emptyResult = {
7245
+ const emptyResult2 = {
6872
7246
  nightOwlRank: [],
6873
7247
  lastSpeakerRank: [],
6874
7248
  firstSpeakerRank: [],
@@ -6876,7 +7250,7 @@ function computeNightOwl(messages) {
6876
7250
  champions: [],
6877
7251
  totalDays: 0
6878
7252
  };
6879
- if (messages.length === 0) return emptyResult;
7253
+ if (messages.length === 0) return emptyResult2;
6880
7254
  function getNightOwlTitleByCount(count) {
6881
7255
  if (count === 0) return "\u517B\u751F\u8FBE\u4EBA";
6882
7256
  if (count <= 20) return "\u5076\u5C14\u5931\u7720";
@@ -9319,7 +9693,6 @@ export {
9319
9693
  isValidWord,
9320
9694
  buildExcludeKeywordsConditions,
9321
9695
  getMentionAnalysis,
9322
- getMentionGraph,
9323
9696
  getLaughAnalysis,
9324
9697
  getClusterGraph,
9325
9698
  resolveOwnerMember,
@@ -9366,6 +9739,8 @@ export {
9366
9739
  deleteMembers,
9367
9740
  deleteMember,
9368
9741
  getCatchphraseAnalysis,
9742
+ GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION,
9743
+ getGroupRelationshipGalaxy,
9369
9744
  getRelationshipStats,
9370
9745
  getJourneyStats,
9371
9746
  getLanguagePreferenceAnalysis,
@@ -9448,4 +9823,4 @@ export {
9448
9823
  StaticPathProvider,
9449
9824
  createDatabaseManagerAdapter
9450
9825
  };
9451
- //# sourceMappingURL=chunk-2WYXD5ZP.mjs.map
9826
+ //# sourceMappingURL=chunk-22YJOBA3.mjs.map