botmux 2.98.4-canary.2 → 2.99.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 (205) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/dist/adapters/cli/genius.d.ts +4 -0
  4. package/dist/adapters/cli/genius.d.ts.map +1 -0
  5. package/dist/adapters/cli/genius.js +216 -0
  6. package/dist/adapters/cli/genius.js.map +1 -0
  7. package/dist/adapters/cli/registry.d.ts +2 -1
  8. package/dist/adapters/cli/registry.d.ts.map +1 -1
  9. package/dist/adapters/cli/registry.js +3 -1
  10. package/dist/adapters/cli/registry.js.map +1 -1
  11. package/dist/adapters/cli/types.d.ts +1 -1
  12. package/dist/adapters/cli/types.d.ts.map +1 -1
  13. package/dist/core/dashboard-rows.d.ts +1 -1
  14. package/dist/core/dashboard-rows.d.ts.map +1 -1
  15. package/dist/core/dashboard-rows.js +2 -1
  16. package/dist/core/dashboard-rows.js.map +1 -1
  17. package/dist/dashboard/session-card-model.d.ts +2 -2
  18. package/dist/dashboard/session-card-model.d.ts.map +1 -1
  19. package/dist/dashboard/session-card-model.js +5 -2
  20. package/dist/dashboard/session-card-model.js.map +1 -1
  21. package/dist/dashboard/web/app.d.ts.map +1 -1
  22. package/dist/dashboard/web/app.js +28 -63
  23. package/dist/dashboard/web/app.js.map +1 -1
  24. package/dist/dashboard/web/bot-defaults-page.d.ts +3 -0
  25. package/dist/dashboard/web/bot-defaults-page.d.ts.map +1 -0
  26. package/dist/dashboard/web/bot-defaults-page.js +23 -0
  27. package/dist/dashboard/web/bot-defaults-page.js.map +1 -0
  28. package/dist/dashboard/web/bot-defaults.d.ts +2 -1
  29. package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
  30. package/dist/dashboard/web/bot-defaults.js +30 -30
  31. package/dist/dashboard/web/bot-defaults.js.map +1 -1
  32. package/dist/dashboard/web/connectors-page.d.ts +14 -0
  33. package/dist/dashboard/web/connectors-page.d.ts.map +1 -0
  34. package/dist/dashboard/web/connectors-page.js +303 -0
  35. package/dist/dashboard/web/connectors-page.js.map +1 -0
  36. package/dist/dashboard/web/connectors.d.ts +1 -12
  37. package/dist/dashboard/web/connectors.d.ts.map +1 -1
  38. package/dist/dashboard/web/connectors.js +1 -325
  39. package/dist/dashboard/web/connectors.js.map +1 -1
  40. package/dist/dashboard/web/dashboard-routes.d.ts +14 -0
  41. package/dist/dashboard/web/dashboard-routes.d.ts.map +1 -0
  42. package/dist/dashboard/web/dashboard-routes.js +176 -0
  43. package/dist/dashboard/web/dashboard-routes.js.map +1 -0
  44. package/dist/dashboard/web/groups-page.d.ts +3 -0
  45. package/dist/dashboard/web/groups-page.d.ts.map +1 -0
  46. package/dist/dashboard/web/groups-page.js +26 -0
  47. package/dist/dashboard/web/groups-page.js.map +1 -0
  48. package/dist/dashboard/web/groups.d.ts +1 -1
  49. package/dist/dashboard/web/groups.d.ts.map +1 -1
  50. package/dist/dashboard/web/groups.js +113 -52
  51. package/dist/dashboard/web/groups.js.map +1 -1
  52. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  53. package/dist/dashboard/web/i18n.js +18 -0
  54. package/dist/dashboard/web/i18n.js.map +1 -1
  55. package/dist/dashboard/web/insights-page.d.ts +3 -0
  56. package/dist/dashboard/web/insights-page.d.ts.map +1 -0
  57. package/dist/dashboard/web/insights-page.js +26 -0
  58. package/dist/dashboard/web/insights-page.js.map +1 -0
  59. package/dist/dashboard/web/insights.d.ts +18 -1
  60. package/dist/dashboard/web/insights.d.ts.map +1 -1
  61. package/dist/dashboard/web/insights.js +25 -61
  62. package/dist/dashboard/web/insights.js.map +1 -1
  63. package/dist/dashboard/web/office-page.d.ts +3 -0
  64. package/dist/dashboard/web/office-page.d.ts.map +1 -0
  65. package/dist/dashboard/web/office-page.js +149 -0
  66. package/dist/dashboard/web/office-page.js.map +1 -0
  67. package/dist/dashboard/web/office.d.ts +1 -1
  68. package/dist/dashboard/web/office.d.ts.map +1 -1
  69. package/dist/dashboard/web/office.js +1 -139
  70. package/dist/dashboard/web/office.js.map +1 -1
  71. package/dist/dashboard/web/overview-page.d.ts +3 -0
  72. package/dist/dashboard/web/overview-page.d.ts.map +1 -0
  73. package/dist/dashboard/web/overview-page.js +149 -0
  74. package/dist/dashboard/web/overview-page.js.map +1 -0
  75. package/dist/dashboard/web/overview.d.ts +2 -2
  76. package/dist/dashboard/web/overview.d.ts.map +1 -1
  77. package/dist/dashboard/web/overview.js +14 -3
  78. package/dist/dashboard/web/overview.js.map +1 -1
  79. package/dist/dashboard/web/react-hooks.d.ts +8 -0
  80. package/dist/dashboard/web/react-hooks.d.ts.map +1 -0
  81. package/dist/dashboard/web/react-hooks.js +43 -0
  82. package/dist/dashboard/web/react-hooks.js.map +1 -0
  83. package/dist/dashboard/web/react-mount.d.ts +4 -0
  84. package/dist/dashboard/web/react-mount.d.ts.map +1 -0
  85. package/dist/dashboard/web/react-mount.js +7 -0
  86. package/dist/dashboard/web/react-mount.js.map +1 -0
  87. package/dist/dashboard/web/roles-page.d.ts +4 -0
  88. package/dist/dashboard/web/roles-page.d.ts.map +1 -0
  89. package/dist/dashboard/web/roles-page.js +26 -0
  90. package/dist/dashboard/web/roles-page.js.map +1 -0
  91. package/dist/dashboard/web/roles.d.ts +1 -2
  92. package/dist/dashboard/web/roles.d.ts.map +1 -1
  93. package/dist/dashboard/web/roles.js +189 -135
  94. package/dist/dashboard/web/roles.js.map +1 -1
  95. package/dist/dashboard/web/route-lifecycle.d.ts +14 -0
  96. package/dist/dashboard/web/route-lifecycle.d.ts.map +1 -0
  97. package/dist/dashboard/web/route-lifecycle.js +29 -0
  98. package/dist/dashboard/web/route-lifecycle.js.map +1 -0
  99. package/dist/dashboard/web/schedules-page.d.ts +14 -0
  100. package/dist/dashboard/web/schedules-page.d.ts.map +1 -0
  101. package/dist/dashboard/web/schedules-page.js +84 -0
  102. package/dist/dashboard/web/schedules-page.js.map +1 -0
  103. package/dist/dashboard/web/schedules.d.ts +1 -1
  104. package/dist/dashboard/web/schedules.d.ts.map +1 -1
  105. package/dist/dashboard/web/schedules.js +1 -115
  106. package/dist/dashboard/web/schedules.js.map +1 -1
  107. package/dist/dashboard/web/sessions-page.d.ts +3 -0
  108. package/dist/dashboard/web/sessions-page.d.ts.map +1 -0
  109. package/dist/dashboard/web/sessions-page.js +28 -0
  110. package/dist/dashboard/web/sessions-page.js.map +1 -0
  111. package/dist/dashboard/web/sessions.d.ts +4 -1
  112. package/dist/dashboard/web/sessions.d.ts.map +1 -1
  113. package/dist/dashboard/web/sessions.js +43 -10
  114. package/dist/dashboard/web/sessions.js.map +1 -1
  115. package/dist/dashboard/web/settings-page.d.ts +3 -0
  116. package/dist/dashboard/web/settings-page.d.ts.map +1 -0
  117. package/dist/dashboard/web/settings-page.js +387 -0
  118. package/dist/dashboard/web/settings-page.js.map +1 -0
  119. package/dist/dashboard/web/settings.d.ts +1 -1
  120. package/dist/dashboard/web/settings.d.ts.map +1 -1
  121. package/dist/dashboard/web/settings.js +1 -534
  122. package/dist/dashboard/web/settings.js.map +1 -1
  123. package/dist/dashboard/web/skills-page.d.ts +33 -0
  124. package/dist/dashboard/web/skills-page.d.ts.map +1 -0
  125. package/dist/dashboard/web/skills-page.js +515 -0
  126. package/dist/dashboard/web/skills-page.js.map +1 -0
  127. package/dist/dashboard/web/skills.d.ts +1 -1
  128. package/dist/dashboard/web/skills.d.ts.map +1 -1
  129. package/dist/dashboard/web/skills.js +1 -711
  130. package/dist/dashboard/web/skills.js.map +1 -1
  131. package/dist/dashboard/web/store.d.ts +9 -0
  132. package/dist/dashboard/web/store.d.ts.map +1 -1
  133. package/dist/dashboard/web/store.js +19 -2
  134. package/dist/dashboard/web/store.js.map +1 -1
  135. package/dist/dashboard/web/team-federation-page.d.ts +4 -0
  136. package/dist/dashboard/web/team-federation-page.d.ts.map +1 -0
  137. package/dist/dashboard/web/team-federation-page.js +47 -0
  138. package/dist/dashboard/web/team-federation-page.js.map +1 -0
  139. package/dist/dashboard/web/team-federation.d.ts +2 -2
  140. package/dist/dashboard/web/team-federation.d.ts.map +1 -1
  141. package/dist/dashboard/web/team-federation.js +104 -97
  142. package/dist/dashboard/web/team-federation.js.map +1 -1
  143. package/dist/dashboard/web/v3-page.d.ts +3 -0
  144. package/dist/dashboard/web/v3-page.d.ts.map +1 -0
  145. package/dist/dashboard/web/v3-page.js +27 -0
  146. package/dist/dashboard/web/v3-page.js.map +1 -0
  147. package/dist/dashboard/web/v3.d.ts +2 -1
  148. package/dist/dashboard/web/v3.d.ts.map +1 -1
  149. package/dist/dashboard/web/v3.js +18 -43
  150. package/dist/dashboard/web/v3.js.map +1 -1
  151. package/dist/dashboard/web/whiteboards-page.d.ts +3 -0
  152. package/dist/dashboard/web/whiteboards-page.d.ts.map +1 -0
  153. package/dist/dashboard/web/whiteboards-page.js +203 -0
  154. package/dist/dashboard/web/whiteboards-page.js.map +1 -0
  155. package/dist/dashboard/web/whiteboards.d.ts +1 -1
  156. package/dist/dashboard/web/whiteboards.d.ts.map +1 -1
  157. package/dist/dashboard/web/whiteboards.js +1 -275
  158. package/dist/dashboard/web/whiteboards.js.map +1 -1
  159. package/dist/dashboard-web/app.js +36 -2550
  160. package/dist/dashboard-web/chunks/bot-defaults-page-3EV7K6LJ.js +358 -0
  161. package/dist/dashboard-web/chunks/chunk-5E3MJJL4.js +1 -0
  162. package/dist/dashboard-web/chunks/chunk-76J7PNZI.js +1 -0
  163. package/dist/dashboard-web/chunks/chunk-7SZRL6FY.js +1 -0
  164. package/dist/dashboard-web/chunks/chunk-JTSE5VP6.js +54 -0
  165. package/dist/dashboard-web/chunks/chunk-JXLHRRJD.js +31 -0
  166. package/dist/dashboard-web/chunks/chunk-NASYW5FD.js +1 -0
  167. package/dist/dashboard-web/chunks/connectors-XPZKEHF6.js +1 -0
  168. package/dist/dashboard-web/chunks/groups-page-C54PVKYP.js +207 -0
  169. package/dist/dashboard-web/chunks/insights-page-BBQYR7WY.js +309 -0
  170. package/dist/dashboard-web/chunks/office-page-6DXYOG55.js +1 -0
  171. package/dist/dashboard-web/chunks/overview-page-5PJCAMID.js +1 -0
  172. package/dist/dashboard-web/chunks/roles-page-APUDLPVM.js +98 -0
  173. package/dist/dashboard-web/chunks/schedules-WG2UKHSO.js +1 -0
  174. package/dist/dashboard-web/chunks/sessions-page-F3U5NFPD.js +238 -0
  175. package/dist/dashboard-web/chunks/settings-page-OSBZNCXL.js +2 -0
  176. package/dist/dashboard-web/chunks/skills-page-Y3VUAOYG.js +2 -0
  177. package/dist/dashboard-web/chunks/team-federation-page-CRH3WZPH.js +10 -0
  178. package/dist/dashboard-web/chunks/v3-page-FVPUL665.js +149 -0
  179. package/dist/dashboard-web/chunks/whiteboards-page-HLCTYSSV.js +1 -0
  180. package/dist/dashboard-web/chunks/workflows-UB7OPYGI.js +184 -0
  181. package/dist/dashboard-web/index.html +1 -1
  182. package/dist/dashboard-web/style.css +82 -1
  183. package/dist/dashboard.js +5 -6
  184. package/dist/dashboard.js.map +1 -1
  185. package/dist/i18n/en.d.ts.map +1 -1
  186. package/dist/i18n/en.js +1 -0
  187. package/dist/i18n/en.js.map +1 -1
  188. package/dist/i18n/zh.d.ts.map +1 -1
  189. package/dist/i18n/zh.js +1 -0
  190. package/dist/i18n/zh.js.map +1 -1
  191. package/dist/im/lark/card-builder.d.ts.map +1 -1
  192. package/dist/im/lark/card-builder.js +1 -0
  193. package/dist/im/lark/card-builder.js.map +1 -1
  194. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  195. package/dist/im/lark/event-dispatcher.js +7 -0
  196. package/dist/im/lark/event-dispatcher.js.map +1 -1
  197. package/dist/im/lark/sessions-card.d.ts.map +1 -1
  198. package/dist/im/lark/sessions-card.js +4 -1
  199. package/dist/im/lark/sessions-card.js.map +1 -1
  200. package/dist/setup/bot-config-editor.d.ts.map +1 -1
  201. package/dist/setup/bot-config-editor.js +16 -14
  202. package/dist/setup/bot-config-editor.js.map +1 -1
  203. package/dist/worker.js +1 -1
  204. package/dist/worker.js.map +1 -1
  205. package/package.json +8 -2
@@ -0,0 +1,176 @@
1
+ export const dashboardRoutes = [
2
+ {
3
+ id: 'legacy-workflow',
4
+ routePrefix: '#/legacy-workflow',
5
+ load: async () => {
6
+ const mod = await import('./workflows.js');
7
+ return root => mod.renderWorkflowsPage(root);
8
+ },
9
+ },
10
+ {
11
+ id: 'workflows',
12
+ routePrefix: '#/workflows',
13
+ rerenderOnUiChange: false,
14
+ load: async () => {
15
+ const mod = await import('./v3-page.js');
16
+ return root => mod.renderV3RunsPage(root);
17
+ },
18
+ },
19
+ {
20
+ id: 'groups',
21
+ routePrefix: '#/groups',
22
+ // Groups is currently a render-once React scaffold around the existing
23
+ // imperative matrix/dialog controller, so locale/theme changes full-remount
24
+ // it until those controller-owned subtrees become React components.
25
+ rerenderOnUiChange: true,
26
+ load: async () => {
27
+ const mod = await import('./groups-page.js');
28
+ return root => mod.renderGroupsPage(root);
29
+ },
30
+ },
31
+ {
32
+ id: 'settings',
33
+ routePrefix: '#/settings',
34
+ rerenderOnUiChange: false,
35
+ load: async () => {
36
+ const mod = await import('./settings-page.js');
37
+ return root => mod.renderSettingsPage(root);
38
+ },
39
+ },
40
+ {
41
+ id: 'bot-defaults',
42
+ routePrefix: '#/bot-defaults',
43
+ // Bot defaults is currently a render-once React scaffold around the
44
+ // imperative settings controller, so ui changes still full-remount it to
45
+ // refresh translations without partial React renders fighting innerHTML.
46
+ rerenderOnUiChange: true,
47
+ load: async () => {
48
+ const mod = await import('./bot-defaults-page.js');
49
+ return root => mod.renderBotDefaultsPage(root);
50
+ },
51
+ },
52
+ {
53
+ id: 'skills',
54
+ routePrefix: '#/skills',
55
+ rerenderOnUiChange: false,
56
+ load: async () => {
57
+ const mod = await import('./skills-page.js');
58
+ return root => mod.renderSkillsPage(root);
59
+ },
60
+ },
61
+ {
62
+ id: 'connectors',
63
+ routePrefix: '#/connectors',
64
+ rerenderOnUiChange: false,
65
+ load: async () => {
66
+ const mod = await import('./connectors.js');
67
+ return root => mod.renderConnectorsPage(root);
68
+ },
69
+ },
70
+ {
71
+ id: 'team-manage',
72
+ routePrefix: '#/team/manage',
73
+ // Team pages are render-once React scaffolds around the existing
74
+ // federation controller, so ui changes full-remount translated subtrees.
75
+ rerenderOnUiChange: true,
76
+ load: async () => {
77
+ const mod = await import('./team-federation-page.js');
78
+ return root => mod.renderTeamManagePage(root);
79
+ },
80
+ },
81
+ {
82
+ id: 'team',
83
+ routePrefix: '#/team',
84
+ // Team pages are render-once React scaffolds around the existing
85
+ // federation controller, so ui changes full-remount translated subtrees.
86
+ rerenderOnUiChange: true,
87
+ load: async () => {
88
+ const mod = await import('./team-federation-page.js');
89
+ return root => mod.renderTeamFederationPage(root);
90
+ },
91
+ },
92
+ {
93
+ id: 'roles-profile',
94
+ routePrefix: '#/roles/profile',
95
+ // Roles still uses a render-once React scaffold around the imperative
96
+ // editor/profile controller, so ui changes full-remount it until the
97
+ // controller-owned subtrees become reactive components.
98
+ rerenderOnUiChange: true,
99
+ load: async () => {
100
+ const mod = await import('./roles-page.js');
101
+ return root => mod.renderRoleProfilesPage(root);
102
+ },
103
+ },
104
+ {
105
+ id: 'roles',
106
+ routePrefix: '#/roles',
107
+ // Roles still uses a render-once React scaffold around the imperative
108
+ // editor/profile controller, so ui changes full-remount it until the
109
+ // controller-owned subtrees become reactive components.
110
+ rerenderOnUiChange: true,
111
+ load: async () => {
112
+ const mod = await import('./roles-page.js');
113
+ return root => mod.renderRolesPage(root);
114
+ },
115
+ },
116
+ {
117
+ id: 'schedules',
118
+ routePrefix: '#/schedules',
119
+ rerenderOnUiChange: false,
120
+ load: async () => {
121
+ const mod = await import('./schedules.js');
122
+ return root => mod.renderSchedulesPage(root);
123
+ },
124
+ },
125
+ {
126
+ id: 'whiteboards',
127
+ routePrefix: '#/whiteboards',
128
+ rerenderOnUiChange: false,
129
+ load: async () => {
130
+ const mod = await import('./whiteboards-page.js');
131
+ return root => mod.renderWhiteboardsPage(root);
132
+ },
133
+ },
134
+ {
135
+ id: 'sessions',
136
+ routePrefix: '#/sessions',
137
+ // Sessions is a render-once React scaffold around the legacy imperative
138
+ // controller. Keep full remounts on locale/theme changes until the
139
+ // controller is split into reactive components, so translated scaffold text
140
+ // and command-owned DOM stay in sync without partial React re-renders.
141
+ rerenderOnUiChange: true,
142
+ load: async () => {
143
+ const mod = await import('./sessions-page.js');
144
+ return root => mod.renderSessionsPage(root);
145
+ },
146
+ },
147
+ {
148
+ id: 'office',
149
+ routePrefix: '#/office',
150
+ rerenderOnUiChange: false,
151
+ load: async () => {
152
+ const mod = await import('./office-page.js');
153
+ return root => mod.renderOfficePage(root);
154
+ },
155
+ },
156
+ {
157
+ id: 'insights',
158
+ routePrefix: '#/insights',
159
+ // Insights is a render-once React scaffold around a large imperative
160
+ // workbench controller; remount on ui changes until that controller is
161
+ // split into reactive components.
162
+ rerenderOnUiChange: true,
163
+ load: async () => {
164
+ const mod = await import('./insights-page.js');
165
+ return root => mod.renderInsightsPage(root);
166
+ },
167
+ },
168
+ ];
169
+ export function findDashboardRoute(hash) {
170
+ return dashboardRoutes.find(route => route.exact ? hash === route.routePrefix : hash.startsWith(route.routePrefix));
171
+ }
172
+ export async function loadOverviewPage() {
173
+ const mod = await import('./overview-page.js');
174
+ return root => mod.renderOverviewPage(root);
175
+ }
176
+ //# sourceMappingURL=dashboard-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-routes.js","sourceRoot":"","sources":["../../../src/dashboard/web/dashboard-routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type PageDisposer } from './react-mount.js';
2
+ export declare function renderGroupsPage(root: HTMLElement): PageDisposer;
3
+ //# sourceMappingURL=groups-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups-page.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups-page.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA8CrE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAEhE"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { loadingHtml } from './ui.js';
4
+ import { useT } from './react-hooks.js';
5
+ import { mountReactPage } from './react-mount.js';
6
+ import { wireGroupsPage } from './groups.js';
7
+ function Html(props) {
8
+ return _jsx("span", { style: { display: 'contents' }, dangerouslySetInnerHTML: { __html: props.html } });
9
+ }
10
+ // Render-once scaffold: React owns the translated route shell, while
11
+ // wireGroupsPage owns the matrix and dialog flows until those controllers are
12
+ // split into reactive components. Keep ui-change rerenders as full remounts.
13
+ function GroupsPage() {
14
+ const tr = useT();
15
+ const rootRef = useRef(null);
16
+ useEffect(() => {
17
+ if (!rootRef.current)
18
+ return undefined;
19
+ return wireGroupsPage(rootRef.current);
20
+ }, []);
21
+ return (_jsxs("section", { className: "page", ref: rootRef, children: [_jsx("div", { className: "page-heading", children: _jsxs("div", { children: [_jsx("p", { className: "eyebrow", children: tr('nav.groups') }), _jsx("h1", { children: tr('groups.title') }), _jsx("p", { children: tr('groups.subtitle') })] }) }), _jsxs("form", { id: "g-filters", className: "filters", children: [_jsx("input", { type: "search", name: "q", placeholder: tr('groups.search') }), _jsxs("label", { children: [_jsx("input", { type: "checkbox", name: "missing" }), " ", tr('groups.missingOnly')] }), _jsx("button", { type: "button", id: "g-refresh", children: tr('groups.refresh') }), _jsx("button", { type: "button", id: "g-create", className: "primary", children: tr('groups.create') })] }), _jsx("div", { id: "g-loading", children: _jsx(Html, { html: loadingHtml() }) }), _jsx("div", { className: "table-scroll matrix-scroll", id: "g-table-wrap", hidden: true, children: _jsxs("table", { children: [_jsx("thead", { id: "g-head" }), _jsx("tbody", { id: "g-body" })] }) }), _jsx("dialog", { id: "g-drawer" })] }));
22
+ }
23
+ export function renderGroupsPage(root) {
24
+ return mountReactPage(root, _jsx(GroupsPage, {}));
25
+ }
26
+ //# sourceMappingURL=groups-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups-page.js","sourceRoot":"","sources":["../../../src/dashboard/web/groups-page.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,cAAc,EAAqB,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,SAAS,IAAI,CAAC,KAAuB;IACnC,OAAO,eAAM,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE,EAAE,uBAAuB,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,GAAI,CAAC;AACnG,CAAC;AAED,qEAAqE;AACrE,8EAA8E;AAC9E,6EAA6E;AAC7E,SAAS,UAAU;IACjB,MAAM,EAAE,GAAG,IAAI,EAAE,CAAC;IAClB,MAAM,OAAO,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IAEjD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO,SAAS,CAAC;QACvC,OAAO,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,mBAAS,SAAS,EAAC,MAAM,EAAC,GAAG,EAAE,OAAO,aACpC,cAAK,SAAS,EAAC,cAAc,YAC3B,0BACE,YAAG,SAAS,EAAC,SAAS,YAAE,EAAE,CAAC,YAAY,CAAC,GAAK,EAC7C,uBAAK,EAAE,CAAC,cAAc,CAAC,GAAM,EAC7B,sBAAI,EAAE,CAAC,iBAAiB,CAAC,GAAK,IAC1B,GACF,EACN,gBAAM,EAAE,EAAC,WAAW,EAAC,SAAS,EAAC,SAAS,aACtC,gBAAO,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,GAAG,EAAC,WAAW,EAAE,EAAE,CAAC,eAAe,CAAC,GAAI,EAClE,4BAAO,gBAAO,IAAI,EAAC,UAAU,EAAC,IAAI,EAAC,SAAS,GAAG,OAAE,EAAE,CAAC,oBAAoB,CAAC,IAAS,EAClF,iBAAQ,IAAI,EAAC,QAAQ,EAAC,EAAE,EAAC,WAAW,YAAE,EAAE,CAAC,gBAAgB,CAAC,GAAU,EACpE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,EAAE,EAAC,UAAU,EAAC,SAAS,EAAC,SAAS,YAAE,EAAE,CAAC,eAAe,CAAC,GAAU,IACjF,EACP,cAAK,EAAE,EAAC,WAAW,YAAC,KAAC,IAAI,IAAC,IAAI,EAAE,WAAW,EAAE,GAAI,GAAM,EACvD,cAAK,SAAS,EAAC,4BAA4B,EAAC,EAAE,EAAC,cAAc,EAAC,MAAM,kBAClE,4BACE,gBAAO,EAAE,EAAC,QAAQ,GAAG,EACrB,gBAAO,EAAE,EAAC,QAAQ,GAAG,IACf,GACJ,EACN,iBAAQ,EAAE,EAAC,UAAU,GAAG,IAChB,CACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAiB;IAChD,OAAO,cAAc,CAAC,IAAI,EAAE,KAAC,UAAU,KAAG,CAAC,CAAC;AAC9C,CAAC"}
@@ -20,6 +20,6 @@ export declare function renderBotCheckboxes(bots: Array<{
20
20
  }>, excludeIds?: Set<string>): string;
21
21
  export declare function renderRoleProfileBootstrapSummary(profileId: string, messageId?: unknown, error?: unknown): string;
22
22
  export declare function renderAddBotsResultSummary(result: GroupAddBotResult[]): string;
23
- export declare function renderGroupsPage(root: HTMLElement): Promise<void>;
23
+ export declare function wireGroupsPage(root: HTMLElement): () => void;
24
24
  export {};
25
25
  //# sourceMappingURL=groups.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"AA4BA,KAAK,iBAAiB,GAAG;IACvB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAMF,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQlE;AA0FD;;;kEAGkE;AAClE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAOhF;AAED;;;;iDAIiD;AACjD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACpD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,MAAM,CASR;AAED,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,OAAO,EACnB,KAAK,CAAC,EAAE,OAAO,GACd,MAAM,CAoBR;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAkB9E;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,WAAW,iBA+4BvD"}
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/groups.ts"],"names":[],"mappings":"AA4BA,KAAK,iBAAiB,GAAG;IACvB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAMF,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQlE;AAgED;;;kEAGkE;AAClE,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAOhF;AAED;;;;iDAIiD;AACjD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EACpD,UAAU,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACvB,MAAM,CASR;AAED,wBAAgB,iCAAiC,CAC/C,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,OAAO,EACnB,KAAK,CAAC,EAAE,OAAO,GACd,MAAM,CAoBR;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,CAkB9E;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,IAAI,CAq9B5D"}
@@ -2,7 +2,7 @@
2
2
  // The aggregator at /api/groups fans out to all online daemons and merges chats
3
3
  // by chatId; the dashboard displays this as a matrix where each cell shows
4
4
  // whether a bot is a member of a given chat.
5
- import { chatAvatarHtml, escapeHtml, loadingHtml, t } from './ui.js';
5
+ import { chatAvatarHtml, escapeHtml, t } from './ui.js';
6
6
  import { hasExplicitChatRole, summarizeGroupProfileMatches, } from './role-profile-match.js';
7
7
  let cache = { chats: [], bots: [] };
8
8
  const PROFILE_ID_RE = /^[A-Za-z0-9._-]{1,64}$/;
@@ -23,31 +23,6 @@ export function suggestRoleProfileIdFromChat(value) {
23
23
  .slice(0, 64);
24
24
  return isValidProfileId(cleaned) ? cleaned : 'profile';
25
25
  }
26
- function pageHtml() {
27
- return `<section class="page">
28
- <div class="page-heading">
29
- <div>
30
- <p class="eyebrow">${t('nav.groups')}</p>
31
- <h1>${t('groups.title')}</h1>
32
- <p>${t('groups.subtitle')}</p>
33
- </div>
34
- </div>
35
- <form id="g-filters" class="filters">
36
- <input type="search" name="q" placeholder="${t('groups.search')}" />
37
- <label><input type="checkbox" name="missing"> ${t('groups.missingOnly')}</label>
38
- <button type="button" id="g-refresh">${t('groups.refresh')}</button>
39
- <button type="button" id="g-create" class="primary">${t('groups.create')}</button>
40
- </form>
41
- <div id="g-loading">${loadingHtml()}</div>
42
- <div class="table-scroll matrix-scroll" id="g-table-wrap" hidden>
43
- <table>
44
- <thead id="g-head"></thead>
45
- <tbody id="g-body"></tbody>
46
- </table>
47
- </div>
48
- <dialog id="g-drawer"></dialog>
49
- </section>`;
50
- }
51
26
  async function loadGroups() {
52
27
  const r = await fetch('/api/groups');
53
28
  cache = await r.json();
@@ -171,8 +146,28 @@ export function renderAddBotsResultSummary(result) {
171
146
  <ul>${items}</ul>
172
147
  </div>`;
173
148
  }
174
- export async function renderGroupsPage(root) {
175
- root.innerHTML = pageHtml();
149
+ export function wireGroupsPage(root) {
150
+ let disposed = false;
151
+ const timers = new Set();
152
+ const delayResolvers = new Map();
153
+ const setTimer = (fn, ms) => {
154
+ const id = window.setTimeout(() => {
155
+ timers.delete(id);
156
+ if (!disposed)
157
+ fn();
158
+ }, ms);
159
+ timers.add(id);
160
+ return id;
161
+ };
162
+ const delay = (ms) => new Promise(resolve => {
163
+ const id = window.setTimeout(() => {
164
+ timers.delete(id);
165
+ delayResolvers.delete(id);
166
+ resolve();
167
+ }, ms);
168
+ timers.add(id);
169
+ delayResolvers.set(id, resolve);
170
+ });
176
171
  const head = root.querySelector('#g-head');
177
172
  const body = root.querySelector('#g-body');
178
173
  const form = root.querySelector('#g-filters');
@@ -182,11 +177,14 @@ export async function renderGroupsPage(root) {
182
177
  refreshBtn.disabled = true;
183
178
  try {
184
179
  await loadGroups();
180
+ if (disposed)
181
+ return;
185
182
  rerender();
186
183
  void refreshRoleProfileContext();
187
184
  }
188
185
  finally {
189
- refreshBtn.disabled = false;
186
+ if (!disposed)
187
+ refreshBtn.disabled = false;
190
188
  }
191
189
  };
192
190
  const createBtn = root.querySelector('#g-create');
@@ -194,13 +192,19 @@ export async function renderGroupsPage(root) {
194
192
  // /api/groups 要扇出到所有 daemon、逐群查成员,慢——先亮 loading,回来再换表格。
195
193
  const loadingEl = root.querySelector('#g-loading');
196
194
  const tableWrap = root.querySelector('#g-table-wrap');
197
- try {
198
- await loadGroups();
199
- }
200
- finally {
201
- loadingEl.remove();
202
- tableWrap.hidden = false;
203
- }
195
+ void (async () => {
196
+ try {
197
+ await loadGroups();
198
+ }
199
+ finally {
200
+ if (disposed)
201
+ return;
202
+ loadingEl.remove();
203
+ tableWrap.hidden = false;
204
+ rerender();
205
+ void refreshRoleProfileContext();
206
+ }
207
+ })();
204
208
  async function refreshRoleProfileContext() {
205
209
  try {
206
210
  await loadGroupRoleProfileContext();
@@ -211,6 +215,8 @@ export async function renderGroupsPage(root) {
211
215
  groupRoleContentByBot = new Map();
212
216
  }
213
217
  finally {
218
+ if (disposed)
219
+ return;
214
220
  roleProfileContextLoaded = true;
215
221
  rerender();
216
222
  }
@@ -240,6 +246,8 @@ export async function renderGroupsPage(root) {
240
246
  roleProfiles = data.profiles ?? [];
241
247
  }
242
248
  catch { /* profile selector is optional */ }
249
+ if (disposed)
250
+ return;
243
251
  drawer.innerHTML = `
244
252
  <article>
245
253
  <header><h3>${t('groups.createTitle')}</h3></header>
@@ -305,6 +313,8 @@ export async function renderGroupsPage(root) {
305
313
  }),
306
314
  });
307
315
  const respBody = await r.json();
316
+ if (disposed)
317
+ return;
308
318
  if (respBody.ok && respBody.chatId) {
309
319
  renderCreateSuccess(respBody);
310
320
  // Lark's chat.list has eventual consistency: the chat we just
@@ -370,7 +380,9 @@ export async function renderGroupsPage(root) {
370
380
  // Refresh will reconcile.
371
381
  const delays = [600, 1200, 1200, 1200, 1200, 1200];
372
382
  for (const d of delays) {
373
- await new Promise(r => setTimeout(r, d));
383
+ await delay(d);
384
+ if (disposed)
385
+ return;
374
386
  let next;
375
387
  try {
376
388
  next = await fetchGroups();
@@ -378,6 +390,8 @@ export async function renderGroupsPage(root) {
378
390
  catch {
379
391
  continue;
380
392
  }
393
+ if (disposed)
394
+ return;
381
395
  const row = (next.chats ?? []).find((c) => c.chatId === chatId);
382
396
  if (row && allExpectedInChat(row, expectedBotIds)) {
383
397
  cache = next;
@@ -455,7 +469,7 @@ export async function renderGroupsPage(root) {
455
469
  b.onclick = () => {
456
470
  navigator.clipboard.writeText(b.dataset.copy ?? '');
457
471
  b.textContent = t('sessions.copied');
458
- setTimeout(() => { b.textContent = t('sessions.copy'); }, 800);
472
+ setTimer(() => { b.textContent = t('sessions.copy'); }, 800);
459
473
  };
460
474
  });
461
475
  drawer.querySelector('#g-create-close').onclick = () => drawer.close();
@@ -532,9 +546,7 @@ export async function renderGroupsPage(root) {
532
546
  </td>
533
547
  </tr>`).join('');
534
548
  }
535
- rerender();
536
- void refreshRoleProfileContext();
537
- body.addEventListener('click', async (e) => {
549
+ const onAddBotsClick = async (e) => {
538
550
  const btn = e.target.closest('button.add-bots');
539
551
  if (!btn)
540
552
  return;
@@ -589,6 +601,8 @@ export async function renderGroupsPage(root) {
589
601
  body: JSON.stringify({ larkAppIds: ids }),
590
602
  });
591
603
  const respBody = await r.json();
604
+ if (disposed)
605
+ return;
592
606
  if (respBody.error === 'no_proxy_bot') {
593
607
  setDialogStatus(statusEl, renderDialogError('无法添加 bot', '当前群里没有可代理操作的 bot。请先在飞书里手动拉入一个 bot,然后重试。'));
594
608
  }
@@ -597,11 +611,15 @@ export async function renderGroupsPage(root) {
597
611
  setDialogStatus(statusEl, resultHtml);
598
612
  try {
599
613
  await loadGroups();
614
+ if (disposed)
615
+ return;
600
616
  rerender();
601
617
  void refreshRoleProfileContext();
602
618
  }
603
619
  catch (e) {
604
- setDialogStatus(statusEl, `${resultHtml}${renderDialogError('刷新失败', `添加结果已返回,但刷新群组列表失败:${e}`)}`);
620
+ if (!disposed) {
621
+ setDialogStatus(statusEl, `${resultHtml}${renderDialogError('刷新失败', `添加结果已返回,但刷新群组列表失败:${e}`)}`);
622
+ }
605
623
  }
606
624
  }
607
625
  else {
@@ -612,11 +630,13 @@ export async function renderGroupsPage(root) {
612
630
  setDialogStatus(statusEl, renderDialogError('网络错误', e));
613
631
  }
614
632
  finally {
615
- restoreSubmit(submitBtn, t('groups.addBots'));
633
+ if (!disposed)
634
+ restoreSubmit(submitBtn, t('groups.addBots'));
616
635
  }
617
636
  };
618
- });
619
- body.addEventListener('click', async (e) => {
637
+ };
638
+ body.addEventListener('click', onAddBotsClick);
639
+ const onSaveProfileClick = async (e) => {
620
640
  const btn = e.target.closest('button.save-profile');
621
641
  if (!btn)
622
642
  return;
@@ -626,7 +646,8 @@ export async function renderGroupsPage(root) {
626
646
  if (!chat)
627
647
  return;
628
648
  await saveGroupRolesAsProfile(chat, btn);
629
- });
649
+ };
650
+ body.addEventListener('click', onSaveProfileClick);
630
651
  async function saveGroupRolesAsProfile(chat, btn) {
631
652
  const suggestedByName = suggestRoleProfileIdFromChat(chat.name ?? '');
632
653
  const suggested = suggestedByName === 'profile'
@@ -640,11 +661,15 @@ export async function renderGroupsPage(root) {
640
661
  collectGroupProfileEntries(chat),
641
662
  fetchRoleProfileSummaries(),
642
663
  ]);
664
+ if (disposed)
665
+ return;
643
666
  openSaveProfileDialog(chat, suggested, entries, profiles);
644
667
  }
645
668
  finally {
646
- btn.disabled = false;
647
- btn.textContent = originalText;
669
+ if (!disposed) {
670
+ btn.disabled = false;
671
+ btn.textContent = originalText;
672
+ }
648
673
  }
649
674
  }
650
675
  async function collectGroupProfileEntries(chat) {
@@ -851,6 +876,8 @@ export async function renderGroupsPage(root) {
851
876
  });
852
877
  return r.ok;
853
878
  }));
879
+ if (disposed)
880
+ return;
854
881
  const saved = results.filter(Boolean).length;
855
882
  if (saved !== entries.length) {
856
883
  statusEl.textContent = t('groups.saveProfileFailed', { saved, total: entries.length });
@@ -864,7 +891,9 @@ export async function renderGroupsPage(root) {
864
891
  statusEl.textContent = t('groups.saveProfileDone', { name: profileId, count: saved });
865
892
  statusEl.className = 'g-save-profile-status ok';
866
893
  await refreshRoleProfileContext();
867
- setTimeout(() => drawer.close(), 700);
894
+ if (disposed)
895
+ return;
896
+ setTimer(() => drawer.close(), 700);
868
897
  }
869
898
  catch (err) {
870
899
  statusEl.textContent = String(err);
@@ -876,7 +905,7 @@ export async function renderGroupsPage(root) {
876
905
  }
877
906
  };
878
907
  }
879
- body.addEventListener('click', async (e) => {
908
+ const onManageChatClick = async (e) => {
880
909
  const btn = e.target.closest('button.manage-chat');
881
910
  if (!btn)
882
911
  return;
@@ -886,7 +915,8 @@ export async function renderGroupsPage(root) {
886
915
  if (!chat)
887
916
  return;
888
917
  openManageDrawer(chat);
889
- });
918
+ };
919
+ body.addEventListener('click', onManageChatClick);
890
920
  function openManageDrawer(chat) {
891
921
  const inChat = chat.memberBots.filter((m) => m.inChat);
892
922
  // Lark `owner_id` returns app_id format for bot owners (string match works).
@@ -978,6 +1008,8 @@ export async function renderGroupsPage(root) {
978
1008
  })
979
1009
  : await fetch(url, { method: 'DELETE' });
980
1010
  const body = await r.json().catch(() => ({}));
1011
+ if (disposed)
1012
+ return;
981
1013
  if (r.ok && body.ok) {
982
1014
  statusEl.textContent = want
983
1015
  ? `✓ 已绑定 → ${body.resolvedPath ?? wd}`
@@ -987,6 +1019,8 @@ export async function renderGroupsPage(root) {
987
1019
  // (current row reflects the just-saved values).
988
1020
  try {
989
1021
  await loadGroups();
1022
+ if (disposed)
1023
+ return;
990
1024
  rerender();
991
1025
  void refreshRoleProfileContext();
992
1026
  }
@@ -1022,6 +1056,8 @@ export async function renderGroupsPage(root) {
1022
1056
  body: JSON.stringify({ larkAppIds: checked }),
1023
1057
  });
1024
1058
  const respBody = await r.json();
1059
+ if (disposed)
1060
+ return;
1025
1061
  const lines = (respBody.result ?? []).map((x) => {
1026
1062
  if (!x.ok)
1027
1063
  return `${x.larkAppId}: 失败 (${x.error ?? 'unknown'})`;
@@ -1035,6 +1071,8 @@ export async function renderGroupsPage(root) {
1035
1071
  }).join('\n');
1036
1072
  alert(lines || `Unexpected: ${JSON.stringify(respBody)}`);
1037
1073
  await loadGroups();
1074
+ if (disposed)
1075
+ return;
1038
1076
  rerender();
1039
1077
  void refreshRoleProfileContext();
1040
1078
  }
@@ -1042,7 +1080,8 @@ export async function renderGroupsPage(root) {
1042
1080
  alert('Network error: ' + e);
1043
1081
  }
1044
1082
  finally {
1045
- drawer.close();
1083
+ if (!disposed)
1084
+ drawer.close();
1046
1085
  }
1047
1086
  };
1048
1087
  drawer.querySelector('#g-disband-btn').onclick = async () => {
@@ -1063,6 +1102,8 @@ export async function renderGroupsPage(root) {
1063
1102
  body: JSON.stringify({ larkAppId: m.larkAppId }),
1064
1103
  });
1065
1104
  const respBody = await r.json();
1105
+ if (disposed)
1106
+ return;
1066
1107
  if (respBody.ok) {
1067
1108
  const closed = (respBody.closedSessions ?? []);
1068
1109
  const failed = closed.filter(c => !c.ok).length;
@@ -1072,6 +1113,8 @@ export async function renderGroupsPage(root) {
1072
1113
  : failed === 0 ? `\n关闭了 ${ok} 个会话。` : `\n关闭了 ${ok} 个会话,${failed} 个会话关闭失败。`;
1073
1114
  alert(`已解散(由 ${m.botName ?? m.larkAppId} 执行)${closedNote}`);
1074
1115
  await loadGroups();
1116
+ if (disposed)
1117
+ return;
1075
1118
  rerender();
1076
1119
  void refreshRoleProfileContext();
1077
1120
  drawer.close();
@@ -1087,5 +1130,23 @@ export async function renderGroupsPage(root) {
1087
1130
  };
1088
1131
  }
1089
1132
  form.addEventListener('input', rerender);
1133
+ return () => {
1134
+ disposed = true;
1135
+ for (const id of timers)
1136
+ window.clearTimeout(id);
1137
+ timers.clear();
1138
+ for (const resolve of delayResolvers.values())
1139
+ resolve();
1140
+ delayResolvers.clear();
1141
+ refreshBtn.onclick = null;
1142
+ createBtn.onclick = null;
1143
+ form.removeEventListener('input', rerender);
1144
+ body.removeEventListener('click', onAddBotsClick);
1145
+ body.removeEventListener('click', onSaveProfileClick);
1146
+ body.removeEventListener('click', onManageChatClick);
1147
+ if (drawer.open)
1148
+ drawer.close();
1149
+ drawer.replaceChildren();
1150
+ };
1090
1151
  }
1091
1152
  //# sourceMappingURL=groups.js.map