@memberjunction/ng-dashboards 5.37.0 → 5.39.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 (328) hide show
  1. package/README.md +46 -7
  2. package/dist/AI/components/agents/agent-configuration.component.js +199 -198
  3. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  4. package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -1
  5. package/dist/AI/components/analytics/ai-analytics-resource.component.js +20 -17
  6. package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -1
  7. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts +15 -0
  8. package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts.map +1 -1
  9. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js +166 -58
  10. package/dist/AI/components/analytics/cost-budget/cost-budget.component.js.map +1 -1
  11. package/dist/AI/components/analytics/executive-summary/executive-summary.component.d.ts.map +1 -1
  12. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js +2 -1
  13. package/dist/AI/components/analytics/executive-summary/executive-summary.component.js.map +1 -1
  14. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts +1 -0
  15. package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts.map +1 -1
  16. package/dist/AI/components/analytics/model-performance/model-performance.component.js +55 -36
  17. package/dist/AI/components/analytics/model-performance/model-performance.component.js.map +1 -1
  18. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts +9 -1
  19. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts.map +1 -1
  20. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js +158 -117
  21. package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js.map +1 -1
  22. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts +1 -0
  23. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts.map +1 -1
  24. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js +22 -8
  25. package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js.map +1 -1
  26. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +89 -842
  27. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
  28. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +1353 -7683
  29. package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
  30. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts +87 -0
  31. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts.map +1 -0
  32. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js +475 -0
  33. package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js.map +1 -0
  34. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts +29 -0
  35. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts.map +1 -0
  36. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js +208 -0
  37. package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js.map +1 -0
  38. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts +21 -0
  39. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts.map +1 -0
  40. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js +70 -0
  41. package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js.map +1 -0
  42. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts +235 -0
  43. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -0
  44. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +1735 -0
  45. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -0
  46. package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts +61 -0
  47. package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts.map +1 -0
  48. package/dist/AI/components/autotagging/shared/classify.dryrun.js +78 -0
  49. package/dist/AI/components/autotagging/shared/classify.dryrun.js.map +1 -0
  50. package/dist/AI/components/autotagging/shared/classify.format.d.ts +43 -0
  51. package/dist/AI/components/autotagging/shared/classify.format.d.ts.map +1 -0
  52. package/dist/AI/components/autotagging/shared/classify.format.js +209 -0
  53. package/dist/AI/components/autotagging/shared/classify.format.js.map +1 -0
  54. package/dist/AI/components/autotagging/shared/classify.types.d.ts +276 -0
  55. package/dist/AI/components/autotagging/shared/classify.types.d.ts.map +1 -0
  56. package/dist/AI/components/autotagging/shared/classify.types.js +6 -0
  57. package/dist/AI/components/autotagging/shared/classify.types.js.map +1 -0
  58. package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts +103 -0
  59. package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts.map +1 -0
  60. package/dist/AI/components/autotagging/tabs/health-tab.component.js +571 -0
  61. package/dist/AI/components/autotagging/tabs/health-tab.component.js.map +1 -0
  62. package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts +40 -0
  63. package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts.map +1 -0
  64. package/dist/AI/components/autotagging/tabs/history-tab.component.js +402 -0
  65. package/dist/AI/components/autotagging/tabs/history-tab.component.js.map +1 -0
  66. package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts +107 -0
  67. package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts.map +1 -0
  68. package/dist/AI/components/autotagging/tabs/inbox-tab.component.js +719 -0
  69. package/dist/AI/components/autotagging/tabs/inbox-tab.component.js.map +1 -0
  70. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts +122 -0
  71. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts.map +1 -0
  72. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js +752 -0
  73. package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js.map +1 -0
  74. package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +166 -0
  75. package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -0
  76. package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1384 -0
  77. package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -0
  78. package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts +70 -0
  79. package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts.map +1 -0
  80. package/dist/AI/components/autotagging/tabs/tags-tab.component.js +448 -0
  81. package/dist/AI/components/autotagging/tabs/tags-tab.component.js.map +1 -0
  82. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts +397 -0
  83. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts.map +1 -0
  84. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js +3490 -0
  85. package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js.map +1 -0
  86. package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts +47 -0
  87. package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts.map +1 -0
  88. package/dist/AI/components/autotagging/tabs/types-tab.component.js +220 -0
  89. package/dist/AI/components/autotagging/tabs/types-tab.component.js.map +1 -0
  90. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +293 -289
  91. package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
  92. package/dist/AI/components/models/model-management.component.js +209 -208
  93. package/dist/AI/components/models/model-management.component.js.map +1 -1
  94. package/dist/AI/components/prompts/prompt-management.component.js +130 -128
  95. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  96. package/dist/AI/components/requests/agent-requests-resource.component.js +61 -61
  97. package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -1
  98. package/dist/AI/components/system/system-configuration.component.js +17 -17
  99. package/dist/AI/components/system/system-configuration.component.js.map +1 -1
  100. package/dist/AI/components/tags/tags-resource.component.js +550 -532
  101. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  102. package/dist/AI/components/vectors/vector-management-resource.component.js +1 -1
  103. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  104. package/dist/AI/services/ai-instrumentation.service.d.ts +5 -0
  105. package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
  106. package/dist/AI/services/ai-instrumentation.service.js +14 -2
  107. package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
  108. package/dist/AI/services/cache-metrics.d.ts +50 -0
  109. package/dist/AI/services/cache-metrics.d.ts.map +1 -0
  110. package/dist/AI/services/cache-metrics.js +43 -0
  111. package/dist/AI/services/cache-metrics.js.map +1 -0
  112. package/dist/APIKeys/api-key-edit-panel.component.js +2 -2
  113. package/dist/APIKeys/api-keys-resource.component.js +132 -131
  114. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  115. package/dist/Actions/components/actions-overview.component.js +141 -141
  116. package/dist/Actions/components/actions-overview.component.js.map +1 -1
  117. package/dist/Actions/components/execution-monitoring.component.js +15 -15
  118. package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
  119. package/dist/Actions/components/explorer/action-explorer.component.d.ts +0 -5
  120. package/dist/Actions/components/explorer/action-explorer.component.d.ts.map +1 -1
  121. package/dist/Actions/components/explorer/action-explorer.component.js +139 -212
  122. package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -1
  123. package/dist/Admin/admin-data-schema.component.js +2 -2
  124. package/dist/Admin/admin-data-schema.component.js.map +1 -1
  125. package/dist/Admin/admin-dev-tools-resource.component.js +2 -2
  126. package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -1
  127. package/dist/Admin/admin-identity-access.component.js +2 -2
  128. package/dist/Admin/admin-identity-access.component.js.map +1 -1
  129. package/dist/Admin/admin-monitoring.component.js +2 -2
  130. package/dist/Admin/admin-monitoring.component.js.map +1 -1
  131. package/dist/ApplicationRoles/application-roles-resource.component.js +54 -49
  132. package/dist/ApplicationRoles/application-roles-resource.component.js.map +1 -1
  133. package/dist/Communication/communication-logs-resource.component.d.ts +6 -0
  134. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  135. package/dist/Communication/communication-logs-resource.component.js +72 -50
  136. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  137. package/dist/Communication/communication-monitor-resource.component.js +103 -102
  138. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  139. package/dist/Communication/communication-providers-resource.component.js +52 -51
  140. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  141. package/dist/Communication/communication-runs-resource.component.js +39 -38
  142. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  143. package/dist/Communication/communication-templates-resource.component.d.ts +6 -0
  144. package/dist/Communication/communication-templates-resource.component.d.ts.map +1 -1
  145. package/dist/Communication/communication-templates-resource.component.js +92 -89
  146. package/dist/Communication/communication-templates-resource.component.js.map +1 -1
  147. package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +73 -1
  148. package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
  149. package/dist/ComponentStudio/component-studio-dashboard.component.js +512 -127
  150. package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
  151. package/dist/ComponentStudio/component-studio-resource.component.d.ts +22 -0
  152. package/dist/ComponentStudio/component-studio-resource.component.d.ts.map +1 -0
  153. package/dist/ComponentStudio/component-studio-resource.component.js +55 -0
  154. package/dist/ComponentStudio/component-studio-resource.component.js.map +1 -0
  155. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts +104 -45
  156. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts.map +1 -1
  157. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +234 -331
  158. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js.map +1 -1
  159. package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.d.ts +54 -0
  160. package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.d.ts.map +1 -0
  161. package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.js +339 -0
  162. package/dist/ComponentStudio/components/form-builder/form-builder-canvas.component.js.map +1 -0
  163. package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.d.ts +65 -0
  164. package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.d.ts.map +1 -0
  165. package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.js +492 -0
  166. package/dist/ComponentStudio/components/form-builder/form-builder-right-panel.component.js.map +1 -0
  167. package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.d.ts +88 -0
  168. package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.d.ts.map +1 -0
  169. package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.js +457 -0
  170. package/dist/ComponentStudio/components/form-builder/form-builder-tab.component.js.map +1 -0
  171. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts +106 -0
  172. package/dist/ComponentStudio/components/form-override-dialog.component.d.ts.map +1 -0
  173. package/dist/ComponentStudio/components/form-override-dialog.component.js +478 -0
  174. package/dist/ComponentStudio/components/form-override-dialog.component.js.map +1 -0
  175. package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts +54 -0
  176. package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts.map +1 -1
  177. package/dist/ComponentStudio/components/workspace/component-preview.component.js +361 -50
  178. package/dist/ComponentStudio/components/workspace/component-preview.component.js.map +1 -1
  179. package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts +10 -0
  180. package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts.map +1 -1
  181. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +114 -45
  182. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
  183. package/dist/ComponentStudio/services/canvas-to-code.d.ts +32 -0
  184. package/dist/ComponentStudio/services/canvas-to-code.d.ts.map +1 -0
  185. package/dist/ComponentStudio/services/canvas-to-code.js +347 -0
  186. package/dist/ComponentStudio/services/canvas-to-code.js.map +1 -0
  187. package/dist/ComponentStudio/services/code-to-canvas.d.ts +32 -0
  188. package/dist/ComponentStudio/services/code-to-canvas.d.ts.map +1 -0
  189. package/dist/ComponentStudio/services/code-to-canvas.js +92 -0
  190. package/dist/ComponentStudio/services/code-to-canvas.js.map +1 -0
  191. package/dist/ComponentStudio/services/component-studio-state.service.d.ts +29 -0
  192. package/dist/ComponentStudio/services/component-studio-state.service.d.ts.map +1 -1
  193. package/dist/ComponentStudio/services/component-studio-state.service.js +76 -0
  194. package/dist/ComponentStudio/services/component-studio-state.service.js.map +1 -1
  195. package/dist/ComponentStudio/services/entity-form-override.service.d.ts +86 -0
  196. package/dist/ComponentStudio/services/entity-form-override.service.d.ts.map +1 -0
  197. package/dist/ComponentStudio/services/entity-form-override.service.js +246 -0
  198. package/dist/ComponentStudio/services/entity-form-override.service.js.map +1 -0
  199. package/dist/ComponentStudio/services/field-binding-scanner.d.ts +29 -0
  200. package/dist/ComponentStudio/services/field-binding-scanner.d.ts.map +1 -0
  201. package/dist/ComponentStudio/services/field-binding-scanner.js +110 -0
  202. package/dist/ComponentStudio/services/field-binding-scanner.js.map +1 -0
  203. package/dist/ComponentStudio/services/form-canvas-model.d.ts +56 -0
  204. package/dist/ComponentStudio/services/form-canvas-model.d.ts.map +1 -0
  205. package/dist/ComponentStudio/services/form-canvas-model.js +35 -0
  206. package/dist/ComponentStudio/services/form-canvas-model.js.map +1 -0
  207. package/dist/ComponentStudio/services/form-host-props-fixture.d.ts +10 -0
  208. package/dist/ComponentStudio/services/form-host-props-fixture.d.ts.map +1 -0
  209. package/dist/ComponentStudio/services/form-host-props-fixture.js +10 -0
  210. package/dist/ComponentStudio/services/form-host-props-fixture.js.map +1 -0
  211. package/dist/Credentials/components/credentials-audit-resource.component.js +136 -135
  212. package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
  213. package/dist/Credentials/components/credentials-categories-resource.component.js +155 -152
  214. package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
  215. package/dist/Credentials/components/credentials-list-resource.component.js +119 -118
  216. package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
  217. package/dist/Credentials/components/credentials-overview-resource.component.js +129 -128
  218. package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
  219. package/dist/Credentials/components/credentials-types-resource.component.js +107 -106
  220. package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
  221. package/dist/DataExplorer/data-explorer-dashboard.component.js +2 -2
  222. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  223. package/dist/DatabaseDesigner/components/database-designer-dashboard.component.js +1 -1
  224. package/dist/DatabaseDesigner/components/entity-list.component.js +115 -114
  225. package/dist/DatabaseDesigner/components/entity-list.component.js.map +1 -1
  226. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts +5 -6
  227. package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts.map +1 -1
  228. package/dist/DatabaseDesigner/database-designer-dashboards.module.js +4 -5
  229. package/dist/DatabaseDesigner/database-designer-dashboards.module.js.map +1 -1
  230. package/dist/DevTools/app-state-inspector.component.js +18 -17
  231. package/dist/DevTools/app-state-inspector.component.js.map +1 -1
  232. package/dist/DevTools/class-registry.component.js +88 -85
  233. package/dist/DevTools/class-registry.component.js.map +1 -1
  234. package/dist/DevTools/event-monitor.component.js +155 -150
  235. package/dist/DevTools/event-monitor.component.js.map +1 -1
  236. package/dist/DevTools/graphql-console.component.js +245 -243
  237. package/dist/DevTools/graphql-console.component.js.map +1 -1
  238. package/dist/DevTools/layout-inspector.component.js +18 -17
  239. package/dist/DevTools/layout-inspector.component.js.map +1 -1
  240. package/dist/EntityAdmin/entity-admin-dashboard.component.js +20 -19
  241. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
  242. package/dist/FormBuilder/form-builder-resource.component.d.ts +964 -0
  243. package/dist/FormBuilder/form-builder-resource.component.d.ts.map +1 -0
  244. package/dist/FormBuilder/form-builder-resource.component.js +4487 -0
  245. package/dist/FormBuilder/form-builder-resource.component.js.map +1 -0
  246. package/dist/FormBuilder/form-builder-version-rail.helpers.d.ts +55 -0
  247. package/dist/FormBuilder/form-builder-version-rail.helpers.d.ts.map +1 -0
  248. package/dist/FormBuilder/form-builder-version-rail.helpers.js +73 -0
  249. package/dist/FormBuilder/form-builder-version-rail.helpers.js.map +1 -0
  250. package/dist/Home/home-application.d.ts +21 -1
  251. package/dist/Home/home-application.d.ts.map +1 -1
  252. package/dist/Home/home-application.js +60 -8
  253. package/dist/Home/home-application.js.map +1 -1
  254. package/dist/Home/home-dashboard.component.js +2 -2
  255. package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
  256. package/dist/Integration/components/activity/activity.component.js +236 -229
  257. package/dist/Integration/components/activity/activity.component.js.map +1 -1
  258. package/dist/Integration/components/connections/connections.component.js +390 -389
  259. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  260. package/dist/Integration/components/overview/overview.component.js +2 -2
  261. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +2 -2
  262. package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
  263. package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +45 -44
  264. package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -1
  265. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +293 -291
  266. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  267. package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js +62 -61
  268. package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js.map +1 -1
  269. package/dist/Lists/components/lists-browse-resource.component.d.ts +6 -2
  270. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  271. package/dist/Lists/components/lists-browse-resource.component.js +525 -566
  272. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  273. package/dist/Lists/components/lists-categories-resource.component.js +135 -134
  274. package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
  275. package/dist/Lists/components/lists-my-lists-resource.component.js +199 -198
  276. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  277. package/dist/MCP/mcp-dashboard.component.js +443 -438
  278. package/dist/MCP/mcp-dashboard.component.js.map +1 -1
  279. package/dist/QueryBrowser/query-browser-resource.component.d.ts +14 -14
  280. package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
  281. package/dist/QueryBrowser/query-browser-resource.component.js +11 -10
  282. package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
  283. package/dist/Scheduling/components/scheduling-activity.component.d.ts.map +1 -1
  284. package/dist/Scheduling/components/scheduling-activity.component.js +146 -147
  285. package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -1
  286. package/dist/Scheduling/components/scheduling-jobs.component.js +76 -75
  287. package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
  288. package/dist/Scheduling/components/scheduling-overview.component.js +97 -96
  289. package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
  290. package/dist/Scheduling/scheduling-dashboard.component.js +24 -22
  291. package/dist/Scheduling/scheduling-dashboard.component.js.map +1 -1
  292. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts +2 -0
  293. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts.map +1 -1
  294. package/dist/Scheduling/services/scheduling-instrumentation.service.js +1 -0
  295. package/dist/Scheduling/services/scheduling-instrumentation.service.js.map +1 -1
  296. package/dist/Testing/components/testing-dashboard-tab-resource.component.js +1 -1
  297. package/dist/Testing/components/testing-explorer.component.d.ts +14 -4
  298. package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
  299. package/dist/Testing/components/testing-explorer.component.js +436 -427
  300. package/dist/Testing/components/testing-explorer.component.js.map +1 -1
  301. package/dist/Testing/components/testing-runs-resource.component.js +1 -1
  302. package/dist/Testing/components/testing-runs.component.js +116 -115
  303. package/dist/Testing/components/testing-runs.component.js.map +1 -1
  304. package/dist/Testing/testing-dashboard.component.js +6 -7
  305. package/dist/Testing/testing-dashboard.component.js.map +1 -1
  306. package/dist/VersionHistory/components/labels-resource.component.js +173 -172
  307. package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
  308. package/dist/VersionHistory/components/restore-resource.component.d.ts +6 -0
  309. package/dist/VersionHistory/components/restore-resource.component.d.ts.map +1 -1
  310. package/dist/VersionHistory/components/restore-resource.component.js +116 -92
  311. package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
  312. package/dist/ai-dashboards.module.d.ts +47 -35
  313. package/dist/ai-dashboards.module.d.ts.map +1 -1
  314. package/dist/ai-dashboards.module.js +40 -1
  315. package/dist/ai-dashboards.module.js.map +1 -1
  316. package/dist/communication-dashboards.module.d.ts +1 -1
  317. package/dist/communication-dashboards.module.d.ts.map +1 -1
  318. package/dist/communication-dashboards.module.js +7 -1
  319. package/dist/communication-dashboards.module.js.map +1 -1
  320. package/dist/component-studio-dashboards.module.d.ts +34 -22
  321. package/dist/component-studio-dashboards.module.d.ts.map +1 -1
  322. package/dist/component-studio-dashboards.module.js +65 -9
  323. package/dist/component-studio-dashboards.module.js.map +1 -1
  324. package/dist/testing-dashboards.module.d.ts +4 -5
  325. package/dist/testing-dashboards.module.d.ts.map +1 -1
  326. package/dist/testing-dashboards.module.js +7 -5
  327. package/dist/testing-dashboards.module.js.map +1 -1
  328. package/package.json +55 -53
@@ -0,0 +1,448 @@
1
+ /**
2
+ * @fileoverview Classify · Tag Library tab.
3
+ *
4
+ * Self-contained sub-page: owns its header-interior (with the tag search box),
5
+ * the tag rows table, the word cloud, the tags-by-source breakdown, and the
6
+ * per-tag drill-down. Receives the shared raw tag + item rows from the host
7
+ * orchestrator via `[ContentTags]`/`[ContentItems]` and rebuilds its view
8
+ * models from them. Opening a content item from the drill-down is a cross-tab
9
+ * concern owned by the host's slide-in, so it bubbles up via @Output.
10
+ */
11
+ import { Component, ChangeDetectorRef, EventEmitter, Input, Output, inject } from '@angular/core';
12
+ import { NormalizeUUID } from '@memberjunction/global';
13
+ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
14
+ import { formatWeight, tagFontSize, formatShortDate } from '../shared/classify.format';
15
+ import * as i0 from "@angular/core";
16
+ import * as i1 from "@angular/forms";
17
+ import * as i2 from "@memberjunction/ng-ui-components";
18
+ import * as i3 from "@memberjunction/ng-word-cloud";
19
+ const _forTrack0 = ($index, $item) => $item.Tag;
20
+ const _forTrack1 = ($index, $item) => $item.SourceName;
21
+ const _forTrack2 = ($index, $item) => $item.ID;
22
+ function ClassifyTagsTabComponent_For_27_Template(rf, ctx) { if (rf & 1) {
23
+ const _r1 = i0.ɵɵgetCurrentView();
24
+ i0.ɵɵelementStart(0, "tr", 24);
25
+ i0.ɵɵlistener("click", function ClassifyTagsTabComponent_For_27_Template_tr_click_0_listener() { const row_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.DrillDownTag(row_r2.Tag)); });
26
+ i0.ɵɵelementStart(1, "td", 25);
27
+ i0.ɵɵtext(2);
28
+ i0.ɵɵelementEnd();
29
+ i0.ɵɵelementStart(3, "td");
30
+ i0.ɵɵtext(4);
31
+ i0.ɵɵelementEnd();
32
+ i0.ɵɵelementStart(5, "td")(6, "div", 26)(7, "div", 27);
33
+ i0.ɵɵelement(8, "div", 28);
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵelementStart(9, "span", 29);
36
+ i0.ɵɵtext(10);
37
+ i0.ɵɵelementEnd()()();
38
+ i0.ɵɵelementStart(11, "td")(12, "div", 30);
39
+ i0.ɵɵelement(13, "div", 31);
40
+ i0.ɵɵelementEnd()();
41
+ i0.ɵɵelementStart(14, "td");
42
+ i0.ɵɵtext(15);
43
+ i0.ɵɵelementEnd();
44
+ i0.ɵɵelementStart(16, "td");
45
+ i0.ɵɵtext(17);
46
+ i0.ɵɵelementEnd()();
47
+ } if (rf & 2) {
48
+ const row_r2 = ctx.$implicit;
49
+ const ctx_r2 = i0.ɵɵnextContext();
50
+ i0.ɵɵclassProp("at-tag-row-selected", ctx_r2.SelectedDrillDownTag === row_r2.Tag);
51
+ i0.ɵɵadvance(2);
52
+ i0.ɵɵtextInterpolate(row_r2.Tag);
53
+ i0.ɵɵadvance(2);
54
+ i0.ɵɵtextInterpolate(row_r2.UsageCount);
55
+ i0.ɵɵadvance(4);
56
+ i0.ɵɵstyleProp("width", row_r2.AvgWeight * 100, "%");
57
+ i0.ɵɵclassProp("at-weight-high", row_r2.AvgWeight >= 0.7)("at-weight-medium", row_r2.AvgWeight >= 0.4 && row_r2.AvgWeight < 0.7)("at-weight-low", row_r2.AvgWeight < 0.4);
58
+ i0.ɵɵadvance(2);
59
+ i0.ɵɵtextInterpolate(row_r2.AvgWeight.toFixed(2));
60
+ i0.ɵɵadvance(3);
61
+ i0.ɵɵstyleProp("width", row_r2.BarWidthPct, "%");
62
+ i0.ɵɵadvance(2);
63
+ i0.ɵɵtextInterpolate(row_r2.TopSource);
64
+ i0.ɵɵadvance(2);
65
+ i0.ɵɵtextInterpolate(row_r2.FirstSeen);
66
+ } }
67
+ function ClassifyTagsTabComponent_Conditional_28_Conditional_8_Template(rf, ctx) { if (rf & 1) {
68
+ i0.ɵɵelementStart(0, "div", 38)(1, "p");
69
+ i0.ɵɵtext(2, "No content items found for this tag.");
70
+ i0.ɵɵelementEnd()();
71
+ } }
72
+ function ClassifyTagsTabComponent_Conditional_28_Conditional_9_For_13_Template(rf, ctx) { if (rf & 1) {
73
+ const _r5 = i0.ɵɵgetCurrentView();
74
+ i0.ɵɵelementStart(0, "tr", 24);
75
+ i0.ɵɵlistener("click", function ClassifyTagsTabComponent_Conditional_28_Conditional_9_For_13_Template_tr_click_0_listener() { const di_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.onOpenItemDetail(di_r6.ID)); });
76
+ i0.ɵɵelementStart(1, "td");
77
+ i0.ɵɵtext(2);
78
+ i0.ɵɵelementEnd();
79
+ i0.ɵɵelementStart(3, "td");
80
+ i0.ɵɵtext(4);
81
+ i0.ɵɵelementEnd();
82
+ i0.ɵɵelementStart(5, "td");
83
+ i0.ɵɵtext(6);
84
+ i0.ɵɵelementEnd();
85
+ i0.ɵɵelementStart(7, "td");
86
+ i0.ɵɵtext(8);
87
+ i0.ɵɵelementEnd()();
88
+ } if (rf & 2) {
89
+ const di_r6 = ctx.$implicit;
90
+ const ctx_r2 = i0.ɵɵnextContext(3);
91
+ i0.ɵɵadvance(2);
92
+ i0.ɵɵtextInterpolate(di_r6.Name);
93
+ i0.ɵɵadvance(2);
94
+ i0.ɵɵtextInterpolate(di_r6.SourceName);
95
+ i0.ɵɵadvance(2);
96
+ i0.ɵɵtextInterpolate(ctx_r2.FormatWeight(di_r6.Weight));
97
+ i0.ɵɵadvance(2);
98
+ i0.ɵɵtextInterpolate(di_r6.UpdatedAt);
99
+ } }
100
+ function ClassifyTagsTabComponent_Conditional_28_Conditional_9_Template(rf, ctx) { if (rf & 1) {
101
+ i0.ɵɵelementStart(0, "table", 10)(1, "thead")(2, "tr")(3, "th");
102
+ i0.ɵɵtext(4, "Name");
103
+ i0.ɵɵelementEnd();
104
+ i0.ɵɵelementStart(5, "th");
105
+ i0.ɵɵtext(6, "Source");
106
+ i0.ɵɵelementEnd();
107
+ i0.ɵɵelementStart(7, "th");
108
+ i0.ɵɵtext(8, "Weight");
109
+ i0.ɵɵelementEnd();
110
+ i0.ɵɵelementStart(9, "th");
111
+ i0.ɵɵtext(10, "Updated");
112
+ i0.ɵɵelementEnd()()();
113
+ i0.ɵɵelementStart(11, "tbody");
114
+ i0.ɵɵrepeaterCreate(12, ClassifyTagsTabComponent_Conditional_28_Conditional_9_For_13_Template, 9, 4, "tr", 39, _forTrack2);
115
+ i0.ɵɵelementEnd()();
116
+ } if (rf & 2) {
117
+ const ctx_r2 = i0.ɵɵnextContext(2);
118
+ i0.ɵɵadvance(12);
119
+ i0.ɵɵrepeater(ctx_r2.TagDrillDownItems);
120
+ } }
121
+ function ClassifyTagsTabComponent_Conditional_28_Template(rf, ctx) { if (rf & 1) {
122
+ const _r4 = i0.ɵɵgetCurrentView();
123
+ i0.ɵɵelementStart(0, "div", 12)(1, "div", 32)(2, "span", 33);
124
+ i0.ɵɵelement(3, "i", 34);
125
+ i0.ɵɵtext(4);
126
+ i0.ɵɵelementEnd();
127
+ i0.ɵɵelementStart(5, "button", 35);
128
+ i0.ɵɵlistener("click", function ClassifyTagsTabComponent_Conditional_28_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r4); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseDrillDownTag()); });
129
+ i0.ɵɵelement(6, "i", 36);
130
+ i0.ɵɵelementEnd()();
131
+ i0.ɵɵelementStart(7, "div", 37);
132
+ i0.ɵɵconditionalCreate(8, ClassifyTagsTabComponent_Conditional_28_Conditional_8_Template, 3, 0, "div", 38)(9, ClassifyTagsTabComponent_Conditional_28_Conditional_9_Template, 14, 0, "table", 10);
133
+ i0.ɵɵelementEnd()();
134
+ } if (rf & 2) {
135
+ const ctx_r2 = i0.ɵɵnextContext();
136
+ i0.ɵɵadvance(4);
137
+ i0.ɵɵtextInterpolate2(" Content items tagged \"", ctx_r2.SelectedDrillDownTag, "\" (", ctx_r2.TagDrillDownItems.length, ") ");
138
+ i0.ɵɵadvance(4);
139
+ i0.ɵɵconditional(ctx_r2.TagDrillDownItems.length === 0 ? 8 : 9);
140
+ } }
141
+ function ClassifyTagsTabComponent_Conditional_34_Template(rf, ctx) { if (rf & 1) {
142
+ i0.ɵɵelement(0, "mj-word-cloud", 17);
143
+ } if (rf & 2) {
144
+ const ctx_r2 = i0.ɵɵnextContext();
145
+ i0.ɵɵproperty("Items", ctx_r2.TagCloudWordItems)("MaxFontSize", 40)("MinFontSize", 12)("MaxItems", 20)("Interactive", true)("Animate", true);
146
+ } }
147
+ function ClassifyTagsTabComponent_Conditional_35_Template(rf, ctx) { if (rf & 1) {
148
+ i0.ɵɵelementStart(0, "div", 18);
149
+ i0.ɵɵtext(1, "No tags yet");
150
+ i0.ɵɵelementEnd();
151
+ } }
152
+ function ClassifyTagsTabComponent_For_42_Template(rf, ctx) { if (rf & 1) {
153
+ i0.ɵɵelementStart(0, "div", 23)(1, "span");
154
+ i0.ɵɵtext(2);
155
+ i0.ɵɵelementEnd();
156
+ i0.ɵɵelementStart(3, "strong");
157
+ i0.ɵɵtext(4);
158
+ i0.ɵɵelementEnd()();
159
+ } if (rf & 2) {
160
+ const s_r7 = ctx.$implicit;
161
+ i0.ɵɵadvance(2);
162
+ i0.ɵɵtextInterpolate(s_r7.SourceName);
163
+ i0.ɵɵadvance(2);
164
+ i0.ɵɵtextInterpolate(s_r7.Count);
165
+ } }
166
+ export class ClassifyTagsTabComponent extends BaseAngularComponent {
167
+ cdr = inject(ChangeDetectorRef);
168
+ /** Shared raw `MJ: Content Item Tags` rows, supplied by the host orchestrator. */
169
+ _contentTags = [];
170
+ set ContentTags(value) {
171
+ this._contentTags = value ?? [];
172
+ this.rebuild();
173
+ }
174
+ get ContentTags() {
175
+ return this._contentTags;
176
+ }
177
+ /** Shared raw `MJ: Content Items` rows — used for source mapping and drill-down. */
178
+ _contentItems = [];
179
+ set ContentItems(value) {
180
+ this._contentItems = value ?? [];
181
+ this.rebuild();
182
+ }
183
+ get ContentItems() {
184
+ return this._contentItems;
185
+ }
186
+ TagRows = [];
187
+ FilteredTagRows = [];
188
+ TagCloudWordItems = [];
189
+ TagsBySource = [];
190
+ TagSearchQuery = '';
191
+ SelectedDrillDownTag = null;
192
+ TagDrillDownItems = [];
193
+ /** Subtitle reflects the live tag count, mirroring the former host subtitle. */
194
+ get Subtitle() {
195
+ return `${this.TagRows.length} unique tags across all content sources`;
196
+ }
197
+ // Template-facing formatters (shared pure helpers exposed for the view).
198
+ TagFontSize = tagFontSize;
199
+ FormatWeight = formatWeight;
200
+ /** Bubble a request to open a content item's detail slide-in (host-owned). */
201
+ OpenItemDetailRequested = new EventEmitter();
202
+ onOpenItemDetail(contentItemID) {
203
+ this.OpenItemDetailRequested.emit(contentItemID);
204
+ }
205
+ /** Rebuild all tag-tab view models from the current inputs. */
206
+ rebuild() {
207
+ this.buildTagRows();
208
+ this.buildTagCloud();
209
+ this.buildTagsBySource();
210
+ this.FilterTags();
211
+ }
212
+ // ── External control surface (host deep-link + agent tool) ──
213
+ /** Apply a search query programmatically (used by the agent tool + deep link). Returns the match count. */
214
+ ApplySearch(query) {
215
+ this.TagSearchQuery = query;
216
+ this.FilterTags();
217
+ return this.FilteredTagRows.length;
218
+ }
219
+ // ── View-model building ──
220
+ buildTagRows() {
221
+ const tagCounts = this.countAllTags();
222
+ const avgWeights = this.computeAvgWeights();
223
+ const tagSourceMap = this.getTopSourcePerTag();
224
+ const tagFirstSeen = this.getFirstSeenPerTag();
225
+ const sorted = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]);
226
+ const maxCount = sorted.length > 0 ? sorted[0][1] : 1;
227
+ this.TagRows = sorted.map(([tag, count]) => ({
228
+ Tag: tag,
229
+ UsageCount: count,
230
+ AvgWeight: avgWeights.get(tag) ?? 1.0,
231
+ BarWidthPct: Math.round((count / maxCount) * 100),
232
+ TopSource: tagSourceMap.get(tag) ?? 'Unknown',
233
+ FirstSeen: tagFirstSeen.get(tag) ?? ''
234
+ }));
235
+ }
236
+ buildTagCloud() {
237
+ const tagCounts = this.countAllTags();
238
+ const avgWeights = this.computeAvgWeights();
239
+ // Sort by a combined score: usage count * avg weight (so high-weight, high-count tags bubble up)
240
+ const scored = Array.from(tagCounts.entries()).map(([tag, count]) => {
241
+ const weight = avgWeights.get(tag) ?? 1.0;
242
+ return { tag, count, weight, score: count * weight };
243
+ }).sort((a, b) => b.score - a.score).slice(0, 20);
244
+ const maxScore = scored.length > 0 ? scored[0].score : 1;
245
+ // Build WordCloudItem[] for the mj-word-cloud component
246
+ this.TagCloudWordItems = scored.map(s => ({
247
+ Text: s.tag,
248
+ Weight: maxScore > 0 ? s.score / maxScore : 0,
249
+ Metadata: { Count: s.count, AvgWeight: s.weight }
250
+ }));
251
+ }
252
+ buildTagsBySource() {
253
+ const sourceTagCounts = new Map();
254
+ const itemSourceMap = new Map();
255
+ for (const item of this._contentItems) {
256
+ itemSourceMap.set(item['ID'], item['ContentSource'] ?? 'Unknown');
257
+ }
258
+ for (const tag of this._contentTags) {
259
+ const source = itemSourceMap.get(tag['ItemID']) ?? 'Unknown';
260
+ sourceTagCounts.set(source, (sourceTagCounts.get(source) ?? 0) + 1);
261
+ }
262
+ this.TagsBySource = Array.from(sourceTagCounts.entries())
263
+ .sort((a, b) => b[1] - a[1])
264
+ .map(([name, count]) => ({ SourceName: name, Count: count }));
265
+ }
266
+ FilterTags() {
267
+ const q = this.TagSearchQuery.toLowerCase().trim();
268
+ this.FilteredTagRows = q
269
+ ? this.TagRows.filter(r => r.Tag.toLowerCase().includes(q))
270
+ : this.TagRows;
271
+ this.cdr.detectChanges();
272
+ }
273
+ /** Drill down into content items matching a specific tag. */
274
+ DrillDownTag(tagName) {
275
+ if (this.SelectedDrillDownTag === tagName) {
276
+ this.CloseDrillDownTag();
277
+ return;
278
+ }
279
+ this.SelectedDrillDownTag = tagName;
280
+ // Find all content items that have this tag
281
+ const matchingItemIDs = new Map(); // itemID → weight
282
+ for (const tag of this._contentTags) {
283
+ if (tag['Tag'] === tagName) {
284
+ matchingItemIDs.set(NormalizeUUID(tag['ItemID']), Number(tag['Weight'] ?? 1));
285
+ }
286
+ }
287
+ this.TagDrillDownItems = [];
288
+ for (let i = 0; i < this._contentItems.length; i++) {
289
+ const item = this._contentItems[i];
290
+ const normalizedID = NormalizeUUID(item['ID']);
291
+ const weight = matchingItemIDs.get(normalizedID);
292
+ if (weight !== undefined) {
293
+ this.TagDrillDownItems.push({
294
+ ID: item['ID'],
295
+ Name: item['Name'] ?? 'Unnamed',
296
+ SourceName: item['ContentSource'] ?? 'Unknown',
297
+ Weight: weight,
298
+ UpdatedAt: formatShortDate(item['__mj_UpdatedAt'] ?? ''),
299
+ FeedIndex: i,
300
+ });
301
+ }
302
+ }
303
+ // Sort by weight descending
304
+ this.TagDrillDownItems.sort((a, b) => b.Weight - a.Weight);
305
+ this.cdr.detectChanges();
306
+ }
307
+ CloseDrillDownTag() {
308
+ this.SelectedDrillDownTag = null;
309
+ this.TagDrillDownItems = [];
310
+ this.cdr.detectChanges();
311
+ }
312
+ // ── Aggregation utilities (read only the tag + item inputs) ──
313
+ countAllTags() {
314
+ const counts = new Map();
315
+ for (const tag of this._contentTags) {
316
+ const t = tag['Tag'];
317
+ if (t)
318
+ counts.set(t, (counts.get(t) ?? 0) + 1);
319
+ }
320
+ return counts;
321
+ }
322
+ /** Compute average weight per tag across all occurrences. */
323
+ computeAvgWeights() {
324
+ const sums = new Map();
325
+ const counts = new Map();
326
+ for (const tag of this._contentTags) {
327
+ const t = tag['Tag'];
328
+ const w = Number(tag['Weight'] ?? 0.5);
329
+ if (t) {
330
+ sums.set(t, (sums.get(t) ?? 0) + w);
331
+ counts.set(t, (counts.get(t) ?? 0) + 1);
332
+ }
333
+ }
334
+ const avgs = new Map();
335
+ for (const [t, sum] of sums) {
336
+ avgs.set(t, Math.round((sum / (counts.get(t) ?? 1)) * 100) / 100);
337
+ }
338
+ return avgs;
339
+ }
340
+ getTopSourcePerTag() {
341
+ const tagSourceCounts = new Map();
342
+ const itemSourceMap = new Map();
343
+ for (const item of this._contentItems) {
344
+ itemSourceMap.set(item['ID'], item['ContentSource'] ?? 'Unknown');
345
+ }
346
+ for (const tag of this._contentTags) {
347
+ const t = tag['Tag'];
348
+ const source = itemSourceMap.get(tag['ItemID']) ?? 'Unknown';
349
+ if (!tagSourceCounts.has(t))
350
+ tagSourceCounts.set(t, new Map());
351
+ const inner = tagSourceCounts.get(t);
352
+ inner.set(source, (inner.get(source) ?? 0) + 1);
353
+ }
354
+ const result = new Map();
355
+ for (const [tag, sourceCounts] of tagSourceCounts) {
356
+ let maxSource = 'Unknown';
357
+ let maxCount = 0;
358
+ for (const [source, count] of sourceCounts) {
359
+ if (count > maxCount) {
360
+ maxSource = source;
361
+ maxCount = count;
362
+ }
363
+ }
364
+ result.set(tag, maxSource);
365
+ }
366
+ return result;
367
+ }
368
+ getFirstSeenPerTag() {
369
+ const result = new Map();
370
+ for (const tag of this._contentTags) {
371
+ const t = tag['Tag'];
372
+ if (t && !result.has(t)) {
373
+ const date = tag['__mj_CreatedAt'];
374
+ result.set(t, date ? formatShortDate(date) : '');
375
+ }
376
+ }
377
+ return result;
378
+ }
379
+ static ɵfac = /*@__PURE__*/ (() => { let ɵClassifyTagsTabComponent_BaseFactory; return function ClassifyTagsTabComponent_Factory(__ngFactoryType__) { return (ɵClassifyTagsTabComponent_BaseFactory || (ɵClassifyTagsTabComponent_BaseFactory = i0.ɵɵgetInheritedFactory(ClassifyTagsTabComponent)))(__ngFactoryType__ || ClassifyTagsTabComponent); }; })();
380
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ClassifyTagsTabComponent, selectors: [["classify-tags-tab"]], inputs: { ContentTags: "ContentTags", ContentItems: "ContentItems" }, outputs: { OpenItemDetailRequested: "OpenItemDetailRequested" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 43, vars: 6, consts: [["Title", "Tag Library", 3, "Subtitle"], ["toolbar", ""], ["type", "text", "placeholder", "Search tags...", 1, "at-search-input", 3, "ngModelChange", "input", "ngModel"], [3, "Padding", "Flex"], [1, "at-main-area"], [1, "at-page-body"], [1, "at-tag-lib-layout"], [1, "at-tag-lib-main"], [1, "at-card"], [1, "at-card-body", 2, "max-height", "500px", "overflow-y", "auto"], [1, "at-tag-table"], [1, "at-tag-row-clickable", 3, "at-tag-row-selected"], [1, "at-card", 2, "margin-top", "12px"], [1, "at-tag-lib-sidebar"], [1, "at-card", "at-tag-cloud-card"], [1, "at-card-title", 2, "margin-bottom", "12px"], [1, "fa-solid", "fa-cloud"], ["Layout", "spiral", "ColorMode", "weight-gradient", 3, "Items", "MaxFontSize", "MinFontSize", "MaxItems", "Interactive", "Animate"], [1, "at-tag-cloud-empty"], [1, "at-card", 2, "padding", "16px", "margin-top", "12px"], [1, "at-card-title", 2, "margin-bottom", "10px"], [1, "fa-solid", "fa-chart-pie"], [1, "at-tags-by-source"], [1, "at-tag-source-row"], [1, "at-tag-row-clickable", 3, "click"], [1, "at-tag-name-cell"], [1, "at-weight-indicator"], [1, "at-weight-bar"], [1, "at-weight-bar-fill"], [1, "at-weight-value"], [1, "at-tag-bar"], [1, "at-tag-bar-fill"], [1, "at-card-header"], [1, "at-card-title"], [1, "fa-solid", "fa-tag"], ["aria-label", "Close", 1, "at-slide-close", 2, "background", "none", "border", "none", "cursor", "pointer", "color", "var(--mj-text-muted)", 3, "click"], [1, "fa-solid", "fa-times"], [1, "at-card-body", 2, "max-height", "300px", "overflow-y", "auto"], [1, "at-empty-state"], [1, "at-tag-row-clickable"]], template: function ClassifyTagsTabComponent_Template(rf, ctx) { if (rf & 1) {
381
+ i0.ɵɵelementStart(0, "mj-page-header-interior", 0)(1, "div", 1)(2, "input", 2);
382
+ i0.ɵɵtwoWayListener("ngModelChange", function ClassifyTagsTabComponent_Template_input_ngModelChange_2_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.TagSearchQuery, $event) || (ctx.TagSearchQuery = $event); return $event; });
383
+ i0.ɵɵlistener("input", function ClassifyTagsTabComponent_Template_input_input_2_listener() { return ctx.FilterTags(); });
384
+ i0.ɵɵelementEnd()()();
385
+ i0.ɵɵelementStart(3, "mj-page-body-interior", 3)(4, "div", 4)(5, "div", 5)(6, "div", 6)(7, "div", 7)(8, "div", 8)(9, "div", 9)(10, "table", 10)(11, "thead")(12, "tr")(13, "th");
386
+ i0.ɵɵtext(14, "Tag");
387
+ i0.ɵɵelementEnd();
388
+ i0.ɵɵelementStart(15, "th");
389
+ i0.ɵɵtext(16, "Count");
390
+ i0.ɵɵelementEnd();
391
+ i0.ɵɵelementStart(17, "th");
392
+ i0.ɵɵtext(18, "Avg Weight");
393
+ i0.ɵɵelementEnd();
394
+ i0.ɵɵelementStart(19, "th");
395
+ i0.ɵɵtext(20, "Distribution");
396
+ i0.ɵɵelementEnd();
397
+ i0.ɵɵelementStart(21, "th");
398
+ i0.ɵɵtext(22, "Top Source");
399
+ i0.ɵɵelementEnd();
400
+ i0.ɵɵelementStart(23, "th");
401
+ i0.ɵɵtext(24, "First Seen");
402
+ i0.ɵɵelementEnd()()();
403
+ i0.ɵɵelementStart(25, "tbody");
404
+ i0.ɵɵrepeaterCreate(26, ClassifyTagsTabComponent_For_27_Template, 18, 17, "tr", 11, _forTrack0);
405
+ i0.ɵɵelementEnd()()()();
406
+ i0.ɵɵconditionalCreate(28, ClassifyTagsTabComponent_Conditional_28_Template, 10, 3, "div", 12);
407
+ i0.ɵɵelementEnd();
408
+ i0.ɵɵelementStart(29, "div", 13)(30, "div", 14)(31, "div", 15);
409
+ i0.ɵɵelement(32, "i", 16);
410
+ i0.ɵɵtext(33, " Tag Cloud");
411
+ i0.ɵɵelementEnd();
412
+ i0.ɵɵconditionalCreate(34, ClassifyTagsTabComponent_Conditional_34_Template, 1, 6, "mj-word-cloud", 17)(35, ClassifyTagsTabComponent_Conditional_35_Template, 2, 0, "div", 18);
413
+ i0.ɵɵelementEnd();
414
+ i0.ɵɵelementStart(36, "div", 19)(37, "div", 20);
415
+ i0.ɵɵelement(38, "i", 21);
416
+ i0.ɵɵtext(39, " By Source");
417
+ i0.ɵɵelementEnd();
418
+ i0.ɵɵelementStart(40, "div", 22);
419
+ i0.ɵɵrepeaterCreate(41, ClassifyTagsTabComponent_For_42_Template, 5, 2, "div", 23, _forTrack1);
420
+ i0.ɵɵelementEnd()()()()()()();
421
+ } if (rf & 2) {
422
+ i0.ɵɵproperty("Subtitle", ctx.Subtitle);
423
+ i0.ɵɵadvance(2);
424
+ i0.ɵɵtwoWayProperty("ngModel", ctx.TagSearchQuery);
425
+ i0.ɵɵadvance();
426
+ i0.ɵɵproperty("Padding", false)("Flex", true);
427
+ i0.ɵɵadvance(23);
428
+ i0.ɵɵrepeater(ctx.FilteredTagRows);
429
+ i0.ɵɵadvance(2);
430
+ i0.ɵɵconditional(ctx.SelectedDrillDownTag ? 28 : -1);
431
+ i0.ɵɵadvance(6);
432
+ i0.ɵɵconditional(ctx.TagCloudWordItems.length > 0 ? 34 : 35);
433
+ i0.ɵɵadvance(7);
434
+ i0.ɵɵrepeater(ctx.TagsBySource);
435
+ } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.MJPageHeaderInteriorComponent, i2.MJPageBodyInteriorComponent, i3.MJWordCloudComponent], encapsulation: 2 });
436
+ }
437
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ClassifyTagsTabComponent, [{
438
+ type: Component,
439
+ args: [{ standalone: false, selector: 'classify-tags-tab', template: "<mj-page-header-interior\n Title=\"Tag Library\"\n [Subtitle]=\"Subtitle\">\n <div toolbar>\n <input type=\"text\" class=\"at-search-input\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TagSearchQuery\" (input)=\"FilterTags()\">\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior [Padding]=\"false\" [Flex]=\"true\">\n <div class=\"at-main-area\">\n <div class=\"at-page-body\">\n <div class=\"at-tag-lib-layout\">\n <div class=\"at-tag-lib-main\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 500px; overflow-y: auto;\">\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Count</th>\n <th>Avg Weight</th>\n <th>Distribution</th>\n <th>Top Source</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredTagRows; track row.Tag) {\n <tr class=\"at-tag-row-clickable\" (click)=\"DrillDownTag(row.Tag)\"\n [class.at-tag-row-selected]=\"SelectedDrillDownTag === row.Tag\">\n <td class=\"at-tag-name-cell\">{{ row.Tag }}</td>\n <td>{{ row.UsageCount }}</td>\n <td>\n <div class=\"at-weight-indicator\">\n <div class=\"at-weight-bar\">\n <div class=\"at-weight-bar-fill\" [style.width.%]=\"row.AvgWeight * 100\"\n [class.at-weight-high]=\"row.AvgWeight >= 0.7\"\n [class.at-weight-medium]=\"row.AvgWeight >= 0.4 && row.AvgWeight < 0.7\"\n [class.at-weight-low]=\"row.AvgWeight < 0.4\"></div>\n </div>\n <span class=\"at-weight-value\">{{ row.AvgWeight.toFixed(2) }}</span>\n </div>\n </td>\n <td>\n <div class=\"at-tag-bar\">\n <div class=\"at-tag-bar-fill\" [style.width.%]=\"row.BarWidthPct\"></div>\n </div>\n </td>\n <td>{{ row.TopSource }}</td>\n <td>{{ row.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Tag drill-down: content items matching selected tag -->\n @if (SelectedDrillDownTag) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-tag\"></i>\n Content items tagged \"{{ SelectedDrillDownTag }}\"\n ({{ TagDrillDownItems.length }})\n </span>\n <button class=\"at-slide-close\" (click)=\"CloseDrillDownTag()\" style=\"background:none;border:none;cursor:pointer;color:var(--mj-text-muted)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 300px; overflow-y: auto;\">\n @if (TagDrillDownItems.length === 0) {\n <div class=\"at-empty-state\"><p>No content items found for this tag.</p></div>\n } @else {\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Name</th>\n <th>Source</th>\n <th>Weight</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n @for (di of TagDrillDownItems; track di.ID) {\n <tr class=\"at-tag-row-clickable\" (click)=\"onOpenItemDetail(di.ID)\">\n <td>{{ di.Name }}</td>\n <td>{{ di.SourceName }}</td>\n <td>{{ FormatWeight(di.Weight) }}</td>\n <td>{{ di.UpdatedAt }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tag-lib-sidebar\">\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 12px;\"><i class=\"fa-solid fa-cloud\"></i> Tag Cloud</div>\n @if (TagCloudWordItems.length > 0) {\n <mj-word-cloud\n [Items]=\"TagCloudWordItems\"\n [MaxFontSize]=\"40\"\n [MinFontSize]=\"12\"\n [MaxItems]=\"20\"\n Layout=\"spiral\"\n ColorMode=\"weight-gradient\"\n [Interactive]=\"true\"\n [Animate]=\"true\">\n </mj-word-cloud>\n } @else {\n <div class=\"at-tag-cloud-empty\">No tags yet</div>\n }\n </div>\n <div class=\"at-card\" style=\"padding: 16px; margin-top: 12px;\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-pie\"></i> By Source</div>\n <div class=\"at-tags-by-source\">\n @for (s of TagsBySource; track s.SourceName) {\n <div class=\"at-tag-source-row\">\n <span>{{ s.SourceName }}</span>\n <strong>{{ s.Count }}</strong>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</mj-page-body-interior>\n", styles: ["/*\n * Tag Library tab styles.\n *\n * The host (AutotaggingPipelineResourceComponent) uses ViewEncapsulation.None,\n * so its `.at-*` rules are global and already style this tab's markup while it\n * renders inside the host shell. Tab-specific rules live here as the shared CSS\n * is partitioned during the host-slim step.\n */\n"] }]
440
+ }], null, { ContentTags: [{
441
+ type: Input
442
+ }], ContentItems: [{
443
+ type: Input
444
+ }], OpenItemDetailRequested: [{
445
+ type: Output
446
+ }] }); })();
447
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ClassifyTagsTabComponent, { className: "ClassifyTagsTabComponent", filePath: "src/AI/components/autotagging/tabs/tags-tab.component.ts", lineNumber: 34 }); })();
448
+ //# sourceMappingURL=tags-tab.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tags-tab.component.js","sourceRoot":"","sources":["../../../../../src/AI/components/autotagging/tabs/tags-tab.component.ts","../../../../../src/AI/components/autotagging/tabs/tags-tab.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Classify · Tag Library tab.\n *\n * Self-contained sub-page: owns its header-interior (with the tag search box),\n * the tag rows table, the word cloud, the tags-by-source breakdown, and the\n * per-tag drill-down. Receives the shared raw tag + item rows from the host\n * orchestrator via `[ContentTags]`/`[ContentItems]` and rebuilds its view\n * models from them. Opening a content item from the drill-down is a cross-tab\n * concern owned by the host's slide-in, so it bubbles up via @Output.\n */\nimport { Component, ChangeDetectorRef, EventEmitter, Input, Output, inject } from '@angular/core';\nimport { NormalizeUUID } from '@memberjunction/global';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { WordCloudItem } from '@memberjunction/ng-word-cloud';\nimport { TagRow, TagBySource } from '../shared/classify.types';\nimport { formatWeight, tagFontSize, formatShortDate } from '../shared/classify.format';\n\n/** One row in the per-tag drill-down (content items carrying a given tag). */\ninterface TagDrillDownItem {\n ID: string;\n Name: string;\n SourceName: string;\n Weight: number;\n UpdatedAt: string;\n FeedIndex: number;\n}\n\n@Component({\n standalone: false,\n selector: 'classify-tags-tab',\n templateUrl: './tags-tab.component.html',\n styleUrls: ['./tags-tab.component.css']\n})\nexport class ClassifyTagsTabComponent extends BaseAngularComponent {\n private cdr = inject(ChangeDetectorRef);\n\n /** Shared raw `MJ: Content Item Tags` rows, supplied by the host orchestrator. */\n private _contentTags: Record<string, unknown>[] = [];\n @Input()\n set ContentTags(value: Record<string, unknown>[]) {\n this._contentTags = value ?? [];\n this.rebuild();\n }\n get ContentTags(): Record<string, unknown>[] {\n return this._contentTags;\n }\n\n /** Shared raw `MJ: Content Items` rows — used for source mapping and drill-down. */\n private _contentItems: Record<string, unknown>[] = [];\n @Input()\n set ContentItems(value: Record<string, unknown>[]) {\n this._contentItems = value ?? [];\n this.rebuild();\n }\n get ContentItems(): Record<string, unknown>[] {\n return this._contentItems;\n }\n\n public TagRows: TagRow[] = [];\n public FilteredTagRows: TagRow[] = [];\n public TagCloudWordItems: WordCloudItem[] = [];\n public TagsBySource: TagBySource[] = [];\n public TagSearchQuery = '';\n public SelectedDrillDownTag: string | null = null;\n public TagDrillDownItems: TagDrillDownItem[] = [];\n\n /** Subtitle reflects the live tag count, mirroring the former host subtitle. */\n public get Subtitle(): string {\n return `${this.TagRows.length} unique tags across all content sources`;\n }\n\n // Template-facing formatters (shared pure helpers exposed for the view).\n public readonly TagFontSize = tagFontSize;\n public readonly FormatWeight = formatWeight;\n\n /** Bubble a request to open a content item's detail slide-in (host-owned). */\n @Output() OpenItemDetailRequested = new EventEmitter<string>();\n\n public onOpenItemDetail(contentItemID: string): void {\n this.OpenItemDetailRequested.emit(contentItemID);\n }\n\n /** Rebuild all tag-tab view models from the current inputs. */\n private rebuild(): void {\n this.buildTagRows();\n this.buildTagCloud();\n this.buildTagsBySource();\n this.FilterTags();\n }\n\n // ── External control surface (host deep-link + agent tool) ──\n\n /** Apply a search query programmatically (used by the agent tool + deep link). Returns the match count. */\n public ApplySearch(query: string): number {\n this.TagSearchQuery = query;\n this.FilterTags();\n return this.FilteredTagRows.length;\n }\n\n // ── View-model building ──\n\n private buildTagRows(): void {\n const tagCounts = this.countAllTags();\n const avgWeights = this.computeAvgWeights();\n const tagSourceMap = this.getTopSourcePerTag();\n const tagFirstSeen = this.getFirstSeenPerTag();\n const sorted = Array.from(tagCounts.entries()).sort((a, b) => b[1] - a[1]);\n const maxCount = sorted.length > 0 ? sorted[0][1] : 1;\n\n this.TagRows = sorted.map(([tag, count]) => ({\n Tag: tag,\n UsageCount: count,\n AvgWeight: avgWeights.get(tag) ?? 1.0,\n BarWidthPct: Math.round((count / maxCount) * 100),\n TopSource: tagSourceMap.get(tag) ?? 'Unknown',\n FirstSeen: tagFirstSeen.get(tag) ?? ''\n }));\n }\n\n private buildTagCloud(): void {\n const tagCounts = this.countAllTags();\n const avgWeights = this.computeAvgWeights();\n // Sort by a combined score: usage count * avg weight (so high-weight, high-count tags bubble up)\n const scored = Array.from(tagCounts.entries()).map(([tag, count]) => {\n const weight = avgWeights.get(tag) ?? 1.0;\n return { tag, count, weight, score: count * weight };\n }).sort((a, b) => b.score - a.score).slice(0, 20);\n const maxScore = scored.length > 0 ? scored[0].score : 1;\n\n // Build WordCloudItem[] for the mj-word-cloud component\n this.TagCloudWordItems = scored.map(s => ({\n Text: s.tag,\n Weight: maxScore > 0 ? s.score / maxScore : 0,\n Metadata: { Count: s.count, AvgWeight: s.weight }\n }));\n }\n\n private buildTagsBySource(): void {\n const sourceTagCounts = new Map<string, number>();\n const itemSourceMap = new Map<string, string>();\n for (const item of this._contentItems) {\n itemSourceMap.set(item['ID'] as string, (item['ContentSource'] as string) ?? 'Unknown');\n }\n for (const tag of this._contentTags) {\n const source = itemSourceMap.get(tag['ItemID'] as string) ?? 'Unknown';\n sourceTagCounts.set(source, (sourceTagCounts.get(source) ?? 0) + 1);\n }\n this.TagsBySource = Array.from(sourceTagCounts.entries())\n .sort((a, b) => b[1] - a[1])\n .map(([name, count]) => ({ SourceName: name, Count: count }));\n }\n\n public FilterTags(): void {\n const q = this.TagSearchQuery.toLowerCase().trim();\n this.FilteredTagRows = q\n ? this.TagRows.filter(r => r.Tag.toLowerCase().includes(q))\n : this.TagRows;\n this.cdr.detectChanges();\n }\n\n /** Drill down into content items matching a specific tag. */\n public DrillDownTag(tagName: string): void {\n if (this.SelectedDrillDownTag === tagName) {\n this.CloseDrillDownTag();\n return;\n }\n this.SelectedDrillDownTag = tagName;\n\n // Find all content items that have this tag\n const matchingItemIDs = new Map<string, number>(); // itemID → weight\n for (const tag of this._contentTags) {\n if ((tag['Tag'] as string) === tagName) {\n matchingItemIDs.set(\n NormalizeUUID(tag['ItemID'] as string),\n Number(tag['Weight'] ?? 1)\n );\n }\n }\n\n this.TagDrillDownItems = [];\n for (let i = 0; i < this._contentItems.length; i++) {\n const item = this._contentItems[i];\n const normalizedID = NormalizeUUID(item['ID'] as string);\n const weight = matchingItemIDs.get(normalizedID);\n if (weight !== undefined) {\n this.TagDrillDownItems.push({\n ID: item['ID'] as string,\n Name: (item['Name'] as string) ?? 'Unnamed',\n SourceName: (item['ContentSource'] as string) ?? 'Unknown',\n Weight: weight,\n UpdatedAt: formatShortDate((item['__mj_UpdatedAt'] as string) ?? ''),\n FeedIndex: i,\n });\n }\n }\n\n // Sort by weight descending\n this.TagDrillDownItems.sort((a, b) => b.Weight - a.Weight);\n this.cdr.detectChanges();\n }\n\n public CloseDrillDownTag(): void {\n this.SelectedDrillDownTag = null;\n this.TagDrillDownItems = [];\n this.cdr.detectChanges();\n }\n\n // ── Aggregation utilities (read only the tag + item inputs) ──\n\n private countAllTags(): Map<string, number> {\n const counts = new Map<string, number>();\n for (const tag of this._contentTags) {\n const t = tag['Tag'] as string;\n if (t) counts.set(t, (counts.get(t) ?? 0) + 1);\n }\n return counts;\n }\n\n /** Compute average weight per tag across all occurrences. */\n private computeAvgWeights(): Map<string, number> {\n const sums = new Map<string, number>();\n const counts = new Map<string, number>();\n for (const tag of this._contentTags) {\n const t = tag['Tag'] as string;\n const w = Number(tag['Weight'] ?? 0.5);\n if (t) {\n sums.set(t, (sums.get(t) ?? 0) + w);\n counts.set(t, (counts.get(t) ?? 0) + 1);\n }\n }\n const avgs = new Map<string, number>();\n for (const [t, sum] of sums) {\n avgs.set(t, Math.round((sum / (counts.get(t) ?? 1)) * 100) / 100);\n }\n return avgs;\n }\n\n private getTopSourcePerTag(): Map<string, string> {\n const tagSourceCounts = new Map<string, Map<string, number>>();\n const itemSourceMap = new Map<string, string>();\n for (const item of this._contentItems) {\n itemSourceMap.set(item['ID'] as string, (item['ContentSource'] as string) ?? 'Unknown');\n }\n for (const tag of this._contentTags) {\n const t = tag['Tag'] as string;\n const source = itemSourceMap.get(tag['ItemID'] as string) ?? 'Unknown';\n if (!tagSourceCounts.has(t)) tagSourceCounts.set(t, new Map());\n const inner = tagSourceCounts.get(t)!;\n inner.set(source, (inner.get(source) ?? 0) + 1);\n }\n const result = new Map<string, string>();\n for (const [tag, sourceCounts] of tagSourceCounts) {\n let maxSource = 'Unknown';\n let maxCount = 0;\n for (const [source, count] of sourceCounts) {\n if (count > maxCount) { maxSource = source; maxCount = count; }\n }\n result.set(tag, maxSource);\n }\n return result;\n }\n\n private getFirstSeenPerTag(): Map<string, string> {\n const result = new Map<string, string>();\n for (const tag of this._contentTags) {\n const t = tag['Tag'] as string;\n if (t && !result.has(t)) {\n const date = tag['__mj_CreatedAt'] as string;\n result.set(t, date ? formatShortDate(date) : '');\n }\n }\n return result;\n }\n}\n","<mj-page-header-interior\n Title=\"Tag Library\"\n [Subtitle]=\"Subtitle\">\n <div toolbar>\n <input type=\"text\" class=\"at-search-input\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TagSearchQuery\" (input)=\"FilterTags()\">\n </div>\n</mj-page-header-interior>\n\n<mj-page-body-interior [Padding]=\"false\" [Flex]=\"true\">\n <div class=\"at-main-area\">\n <div class=\"at-page-body\">\n <div class=\"at-tag-lib-layout\">\n <div class=\"at-tag-lib-main\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 500px; overflow-y: auto;\">\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Count</th>\n <th>Avg Weight</th>\n <th>Distribution</th>\n <th>Top Source</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredTagRows; track row.Tag) {\n <tr class=\"at-tag-row-clickable\" (click)=\"DrillDownTag(row.Tag)\"\n [class.at-tag-row-selected]=\"SelectedDrillDownTag === row.Tag\">\n <td class=\"at-tag-name-cell\">{{ row.Tag }}</td>\n <td>{{ row.UsageCount }}</td>\n <td>\n <div class=\"at-weight-indicator\">\n <div class=\"at-weight-bar\">\n <div class=\"at-weight-bar-fill\" [style.width.%]=\"row.AvgWeight * 100\"\n [class.at-weight-high]=\"row.AvgWeight >= 0.7\"\n [class.at-weight-medium]=\"row.AvgWeight >= 0.4 && row.AvgWeight < 0.7\"\n [class.at-weight-low]=\"row.AvgWeight < 0.4\"></div>\n </div>\n <span class=\"at-weight-value\">{{ row.AvgWeight.toFixed(2) }}</span>\n </div>\n </td>\n <td>\n <div class=\"at-tag-bar\">\n <div class=\"at-tag-bar-fill\" [style.width.%]=\"row.BarWidthPct\"></div>\n </div>\n </td>\n <td>{{ row.TopSource }}</td>\n <td>{{ row.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Tag drill-down: content items matching selected tag -->\n @if (SelectedDrillDownTag) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-tag\"></i>\n Content items tagged \"{{ SelectedDrillDownTag }}\"\n ({{ TagDrillDownItems.length }})\n </span>\n <button class=\"at-slide-close\" (click)=\"CloseDrillDownTag()\" style=\"background:none;border:none;cursor:pointer;color:var(--mj-text-muted)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 300px; overflow-y: auto;\">\n @if (TagDrillDownItems.length === 0) {\n <div class=\"at-empty-state\"><p>No content items found for this tag.</p></div>\n } @else {\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Name</th>\n <th>Source</th>\n <th>Weight</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n @for (di of TagDrillDownItems; track di.ID) {\n <tr class=\"at-tag-row-clickable\" (click)=\"onOpenItemDetail(di.ID)\">\n <td>{{ di.Name }}</td>\n <td>{{ di.SourceName }}</td>\n <td>{{ FormatWeight(di.Weight) }}</td>\n <td>{{ di.UpdatedAt }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tag-lib-sidebar\">\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 12px;\"><i class=\"fa-solid fa-cloud\"></i> Tag Cloud</div>\n @if (TagCloudWordItems.length > 0) {\n <mj-word-cloud\n [Items]=\"TagCloudWordItems\"\n [MaxFontSize]=\"40\"\n [MinFontSize]=\"12\"\n [MaxItems]=\"20\"\n Layout=\"spiral\"\n ColorMode=\"weight-gradient\"\n [Interactive]=\"true\"\n [Animate]=\"true\">\n </mj-word-cloud>\n } @else {\n <div class=\"at-tag-cloud-empty\">No tags yet</div>\n }\n </div>\n <div class=\"at-card\" style=\"padding: 16px; margin-top: 12px;\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-pie\"></i> By Source</div>\n <div class=\"at-tags-by-source\">\n @for (s of TagsBySource; track s.SourceName) {\n <div class=\"at-tag-source-row\">\n <span>{{ s.SourceName }}</span>\n <strong>{{ s.Count }}</strong>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</mj-page-body-interior>\n"]}