@memberjunction/ng-dashboards 6.2.0-edge.5 → 6.2.0-edge.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.js +2 -2
- package/dist/AI/components/analytics/agent-runs/agent-run-analysis.component.js.map +1 -1
- package/dist/AI/components/analytics/realtime/realtime-management.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/realtime/realtime-management.component.js +3 -1
- package/dist/AI/components/analytics/realtime/realtime-management.component.js.map +1 -1
- package/dist/AI/components/analytics/realtime/realtime-overview.component.d.ts +1 -1
- package/dist/AI/components/analytics/realtime/realtime-overview.component.js +3 -3
- package/dist/AI/components/analytics/realtime/realtime-overview.component.js.map +1 -1
- package/dist/AI/components/analytics/realtime/realtime-session-data.d.ts +6 -4
- package/dist/AI/components/analytics/realtime/realtime-session-data.d.ts.map +1 -1
- package/dist/AI/components/analytics/realtime/realtime-session-data.js +8 -2
- package/dist/AI/components/analytics/realtime/realtime-session-data.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +21 -13
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js +1 -1
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js.map +1 -1
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js +1 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js.map +1 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +1 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +7 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +210 -57
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts +12 -11
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.js +15 -15
- package/dist/AI/components/duplicates/duplicate-detection-run-scope.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +28 -16
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts +1 -0
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.d.ts.map +1 -1
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +32 -8
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +18 -9
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/sensitive-fields.d.ts +9 -4
- package/dist/AI/components/vectors/sensitive-fields.d.ts.map +1 -1
- package/dist/AI/components/vectors/sensitive-fields.js +39 -14
- package/dist/AI/components/vectors/sensitive-fields.js.map +1 -1
- package/dist/AI/components/vectors/vector-document-rules.d.ts +13 -0
- package/dist/AI/components/vectors/vector-document-rules.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-document-rules.js +14 -0
- package/dist/AI/components/vectors/vector-document-rules.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +34 -14
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +134 -51
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-store-selection.d.ts +32 -0
- package/dist/AI/components/vectors/vector-store-selection.d.ts.map +1 -0
- package/dist/AI/components/vectors/vector-store-selection.js +57 -0
- package/dist/AI/components/vectors/vector-store-selection.js.map +1 -0
- package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.js +4 -2
- package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
- package/dist/Actions/action-result-code.d.ts +80 -0
- package/dist/Actions/action-result-code.d.ts.map +1 -0
- package/dist/Actions/action-result-code.js +163 -0
- package/dist/Actions/action-result-code.js.map +1 -0
- package/dist/Actions/components/actions-overview.component.d.ts +8 -0
- package/dist/Actions/components/actions-overview.component.d.ts.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +12 -9
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.d.ts +29 -9
- package/dist/Actions/components/execution-monitoring.component.d.ts.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +65 -42
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-card.component.d.ts.map +1 -1
- package/dist/Actions/components/explorer/action-card.component.js +5 -4
- package/dist/Actions/components/explorer/action-card.component.js.map +1 -1
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts +198 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.js +289 -0
- package/dist/ChiefOfStaff/chief-of-staff-agent-context.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-approvals.component.d.ts +29 -0
- package/dist/ChiefOfStaff/chief-of-staff-approvals.component.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-approvals.component.js +666 -0
- package/dist/ChiefOfStaff/chief-of-staff-approvals.component.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-governance.component.d.ts +56 -0
- package/dist/ChiefOfStaff/chief-of-staff-governance.component.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-governance.component.js +515 -0
- package/dist/ChiefOfStaff/chief-of-staff-governance.component.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-live-refresh.d.ts +14 -0
- package/dist/ChiefOfStaff/chief-of-staff-live-refresh.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-live-refresh.js +39 -0
- package/dist/ChiefOfStaff/chief-of-staff-live-refresh.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-overview.component.d.ts +17 -0
- package/dist/ChiefOfStaff/chief-of-staff-overview.component.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-overview.component.js +485 -0
- package/dist/ChiefOfStaff/chief-of-staff-overview.component.js.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-page-base.d.ts +139 -0
- package/dist/ChiefOfStaff/chief-of-staff-page-base.d.ts.map +1 -0
- package/dist/ChiefOfStaff/chief-of-staff-page-base.js +602 -0
- package/dist/ChiefOfStaff/chief-of-staff-page-base.js.map +1 -0
- package/dist/ChiefOfStaff/index.d.ts +6 -0
- package/dist/ChiefOfStaff/index.d.ts.map +1 -0
- package/dist/ChiefOfStaff/index.js +6 -0
- package/dist/ChiefOfStaff/index.js.map +1 -0
- package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-logs-resource.component.js +2 -1
- package/dist/Communication/communication-logs-resource.component.js.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.js +26 -21
- package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
- package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-runs-resource.component.js +14 -13
- package/dist/Communication/communication-runs-resource.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +1 -0
- package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.d.ts.map +1 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +2 -1
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/services/component-version.service.d.ts.map +1 -1
- package/dist/ComponentStudio/services/component-version.service.js +3 -0
- package/dist/ComponentStudio/services/component-version.service.js.map +1 -1
- package/dist/DashboardEditor/dashboard-editor.component.d.ts +17 -2
- package/dist/DashboardEditor/dashboard-editor.component.d.ts.map +1 -1
- package/dist/DashboardEditor/dashboard-editor.component.js +94 -43
- package/dist/DashboardEditor/dashboard-editor.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +45 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +178 -55
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.d.ts +2 -0
- package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
- package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
- package/dist/DevTools/graphql-console.component.js +2 -2
- package/dist/DevTools/lazy-module-status.component.js +2 -2
- package/dist/FormBuilder/form-builder-resource.component.d.ts.map +1 -1
- package/dist/FormBuilder/form-builder-resource.component.js +5 -0
- package/dist/FormBuilder/form-builder-resource.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +2 -2
- package/dist/Integration/services/integration-data.service.d.ts +1 -1
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +4 -3
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/LoginStudio/login-studio-resource.component.d.ts +10 -2
- package/dist/LoginStudio/login-studio-resource.component.d.ts.map +1 -1
- package/dist/LoginStudio/login-studio-resource.component.js +111 -77
- package/dist/LoginStudio/login-studio-resource.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.d.ts +24 -4
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +118 -65
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.d.ts +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.d.ts.map +1 -1
- package/dist/PredictiveStudio/business-predictions.view-models.js +3 -3
- package/dist/PredictiveStudio/business-predictions.view-models.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts +6 -63
- package/dist/PredictiveStudio/components/ps-experiments.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-experiments.component.js +257 -521
- package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts +0 -18
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js +124 -251
- package/dist/PredictiveStudio/components/ps-pipeline-wizard.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts +5 -9
- package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-pipelines.component.js +98 -130
- package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts +53 -4
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js +338 -326
- package/dist/PredictiveStudio/components/ps-predictions-grid.component.js.map +1 -1
- package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts +19 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.d.ts.map +1 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.js +85 -0
- package/dist/PredictiveStudio/components/ps-row-drivers.component.js.map +1 -0
- package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts +19 -1
- package/dist/PredictiveStudio/engine/predictive-studio.engine.d.ts.map +1 -1
- package/dist/PredictiveStudio/engine/predictive-studio.engine.js +52 -2
- package/dist/PredictiveStudio/engine/predictive-studio.engine.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +3 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-agent-context.js +3 -0
- package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts +27 -4
- package/dist/PredictiveStudio/predictive-studio-score-history.service.d.ts.map +1 -1
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js +94 -61
- package/dist/PredictiveStudio/predictive-studio-score-history.service.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +29 -5
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +186 -103
- package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +1 -1
- package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +26 -0
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +375 -252
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/UserRoutines/user-routines-resource.component.d.ts +8 -1
- package/dist/UserRoutines/user-routines-resource.component.d.ts.map +1 -1
- package/dist/UserRoutines/user-routines-resource.component.js +22 -4
- package/dist/UserRoutines/user-routines-resource.component.js.map +1 -1
- package/dist/VersionHistory/components/diff-resource.component.d.ts.map +1 -1
- package/dist/VersionHistory/components/diff-resource.component.js +1 -0
- package/dist/VersionHistory/components/diff-resource.component.js.map +1 -1
- package/dist/VideoStudio/components/vs-archetype-editor.component.d.ts +162 -0
- package/dist/VideoStudio/components/vs-archetype-editor.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-archetype-editor.component.js +1105 -0
- package/dist/VideoStudio/components/vs-archetype-editor.component.js.map +1 -0
- package/dist/VideoStudio/components/vs-audio-options.component.d.ts +27 -0
- package/dist/VideoStudio/components/vs-audio-options.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-audio-options.component.js +119 -0
- package/dist/VideoStudio/components/vs-audio-options.component.js.map +1 -0
- package/dist/VideoStudio/components/vs-copilot-panel.component.d.ts +105 -0
- package/dist/VideoStudio/components/vs-copilot-panel.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-copilot-panel.component.js +560 -0
- package/dist/VideoStudio/components/vs-copilot-panel.component.js.map +1 -0
- package/dist/VideoStudio/components/vs-production-brief.component.d.ts +163 -0
- package/dist/VideoStudio/components/vs-production-brief.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-production-brief.component.js +1054 -0
- package/dist/VideoStudio/components/vs-production-brief.component.js.map +1 -0
- package/dist/VideoStudio/components/vs-setup-health-card.component.d.ts +43 -0
- package/dist/VideoStudio/components/vs-setup-health-card.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-setup-health-card.component.js +412 -0
- package/dist/VideoStudio/components/vs-setup-health-card.component.js.map +1 -0
- package/dist/VideoStudio/components/vs-studio-slide-in.component.d.ts +73 -0
- package/dist/VideoStudio/components/vs-studio-slide-in.component.d.ts.map +1 -0
- package/dist/VideoStudio/components/vs-studio-slide-in.component.js +271 -0
- package/dist/VideoStudio/components/vs-studio-slide-in.component.js.map +1 -0
- package/dist/VideoStudio/index.d.ts +12 -0
- package/dist/VideoStudio/index.d.ts.map +1 -0
- package/dist/VideoStudio/index.js +12 -0
- package/dist/VideoStudio/index.js.map +1 -0
- package/dist/VideoStudio/resources/video-studio-archetypes-resource.component.d.ts +118 -0
- package/dist/VideoStudio/resources/video-studio-archetypes-resource.component.d.ts.map +1 -0
- package/dist/VideoStudio/resources/video-studio-archetypes-resource.component.js +964 -0
- package/dist/VideoStudio/resources/video-studio-archetypes-resource.component.js.map +1 -0
- package/dist/VideoStudio/resources/video-studio-brand-kits-resource.component.d.ts +70 -0
- package/dist/VideoStudio/resources/video-studio-brand-kits-resource.component.d.ts.map +1 -0
- package/dist/VideoStudio/resources/video-studio-brand-kits-resource.component.js +600 -0
- package/dist/VideoStudio/resources/video-studio-brand-kits-resource.component.js.map +1 -0
- package/dist/VideoStudio/resources/video-studio-productions-resource.component.d.ts +182 -0
- package/dist/VideoStudio/resources/video-studio-productions-resource.component.d.ts.map +1 -0
- package/dist/VideoStudio/resources/video-studio-productions-resource.component.js +1481 -0
- package/dist/VideoStudio/resources/video-studio-productions-resource.component.js.map +1 -0
- package/dist/VideoStudio/services/vs-studio-slide-in-state.service.d.ts +102 -0
- package/dist/VideoStudio/services/vs-studio-slide-in-state.service.d.ts.map +1 -0
- package/dist/VideoStudio/services/vs-studio-slide-in-state.service.js +240 -0
- package/dist/VideoStudio/services/vs-studio-slide-in-state.service.js.map +1 -0
- package/dist/VideoStudio/video-studio-agent-context.d.ts +495 -0
- package/dist/VideoStudio/video-studio-agent-context.d.ts.map +1 -0
- package/dist/VideoStudio/video-studio-agent-context.js +992 -0
- package/dist/VideoStudio/video-studio-agent-context.js.map +1 -0
- package/dist/ai-dashboards.module.d.ts +25 -21
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +25 -3
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/module.d.ts +13 -12
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +8 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +10 -0
- package/dist/public-api.js.map +1 -1
- package/dist/shared/action-scheduled-job.d.ts.map +1 -1
- package/dist/shared/action-scheduled-job.js +2 -0
- package/dist/shared/action-scheduled-job.js.map +1 -1
- package/dist/video-studio-dashboards.module.d.ts +40 -0
- package/dist/video-studio-dashboards.module.d.ts.map +1 -0
- package/dist/video-studio-dashboards.module.js +159 -0
- package/dist/video-studio-dashboards.module.js.map +1 -0
- package/package.json +77 -71
- package/dist/PredictiveStudio/at-risk.view-models.d.ts +0 -120
- package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +0 -1
- package/dist/PredictiveStudio/at-risk.view-models.js +0 -261
- package/dist/PredictiveStudio/at-risk.view-models.js.map +0 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts +0 -109
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.d.ts.map +0 -1
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +0 -323
- package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +0 -1
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts +0 -137
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.d.ts.map +0 -1
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js +0 -826
- package/dist/PredictiveStudio/components/ps-run-experiment-modal.component.js.map +0 -1
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Pure helpers for the Video Studio app's AI-agent integration
|
|
3
|
+
* across all three resource surfaces:
|
|
4
|
+
* 1. Productions (`VideoStudioProductionsResource`) + right-side slide-in Production Brief (`VSProductionBriefComponent`)
|
|
5
|
+
* 2. Brand Kits (`VideoStudioBrandKitsResource`)
|
|
6
|
+
* 3. Archetypes (`VideoStudioArchetypesResource`)
|
|
7
|
+
*
|
|
8
|
+
* These functions are intentionally free of Angular / DOM dependencies so they
|
|
9
|
+
* can be unit-tested in Node via Vitest. Each Video Studio resource component
|
|
10
|
+
* supplies a plain snapshot of its current state and these helpers shape it
|
|
11
|
+
* into the key-value `AgentContext` object published via
|
|
12
|
+
* `this.navigationService.SetAgentContext(this, ...)`.
|
|
13
|
+
*
|
|
14
|
+
* 🚨 SAFETY BOUNDARY (READ / NAVIGATE / FILTER / SEARCH / REFRESH + STUDIO BRIEF FORM-FILL):
|
|
15
|
+
* Productions, Brand Kits, and Archetypes expose ONLY read, filter, search,
|
|
16
|
+
* select/open-for-viewing, and refresh operations to the AI agent — never create,
|
|
17
|
+
* edit, copy, share, upload, policy, or delete tools. Productions additionally exposes
|
|
18
|
+
* four reversible in-memory brief form-fill tools (`SelectStudioArchetype`,
|
|
19
|
+
* `SelectStudioBrandKit`, `SetStudioFormats`, `SetStudioTopic`) while deliberately
|
|
20
|
+
* excluding `StartProduction`:
|
|
21
|
+
* - Productions: no create/delete production, no share/permission grant (`ArtifactShareModal`
|
|
22
|
+
* is user-confirm-only), no storyboard/render mutation. Brief form-fill tools only
|
|
23
|
+
* (`SelectStudioArchetype`, `SelectStudioBrandKit`, `SetStudioFormats`, `SetStudioTopic`);
|
|
24
|
+
* `StartProduction` spends model/render tokens and creates a conversation, so it stays
|
|
25
|
+
* human-triggered in the UI.
|
|
26
|
+
* - Brand Kits: `OpenBrandKit` opens the `MJ: Brand Kits` entity form for viewing via
|
|
27
|
+
* `NavigationService.OpenEntityRecord`; no create, save, delete, or asset/policy write
|
|
28
|
+
* tool is exposed.
|
|
29
|
+
* - Archetypes: `SelectArchetype` / `OpenArchetype` selects or opens a company archetype
|
|
30
|
+
* record for viewing; making a copy or editing an archetype (`vs-archetype-editor`) creates
|
|
31
|
+
* or updates a database record and is gated behind the user-driven editor dialog only.
|
|
32
|
+
*/
|
|
33
|
+
import { EscapeSQLString } from '@memberjunction/global';
|
|
34
|
+
import { AGENT_CONTEXT_NAME_LIST_CAP, BoundNameList } from '../shared/agent-tool-validation';
|
|
35
|
+
/**
|
|
36
|
+
* Upper bound on how many names any Video Studio surface publishes in a
|
|
37
|
+
* name-list context field. Companion `*Count` fields are emitted when a list
|
|
38
|
+
* exceeds this cap.
|
|
39
|
+
*/
|
|
40
|
+
export const VIDEO_STUDIO_NAME_LIST_CAP = AGENT_CONTEXT_NAME_LIST_CAP;
|
|
41
|
+
/**
|
|
42
|
+
* Cap an array of names to {@link VIDEO_STUDIO_NAME_LIST_CAP} entries without
|
|
43
|
+
* mutating the input array.
|
|
44
|
+
*/
|
|
45
|
+
export function CapVideoStudioNames(names) {
|
|
46
|
+
return BoundNameList(names, VIDEO_STUDIO_NAME_LIST_CAP);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve an agent-supplied reference (an ID or a display name) against a list
|
|
50
|
+
* of loaded items in precedence order:
|
|
51
|
+
* 1. Exact `ID` match (case-insensitive)
|
|
52
|
+
* 2. Exact `Name` match (case-insensitive, trimmed)
|
|
53
|
+
* 3. First `Name` substring match (case-insensitive, trimmed)
|
|
54
|
+
*
|
|
55
|
+
* Returns a descriptive error listing up to {@link VIDEO_STUDIO_NAME_LIST_CAP}
|
|
56
|
+
* available names when no item matches. Never throws.
|
|
57
|
+
*/
|
|
58
|
+
export function ResolveVideoStudioItem(reference, items, itemNoun) {
|
|
59
|
+
const raw = typeof reference === 'string' ? reference.trim() : '';
|
|
60
|
+
if (!raw) {
|
|
61
|
+
return { Ok: false, Error: `A ${itemNoun} ID or name is required.` };
|
|
62
|
+
}
|
|
63
|
+
if (items.length === 0) {
|
|
64
|
+
return { Ok: false, Error: `No ${itemNoun}s are currently loaded.` };
|
|
65
|
+
}
|
|
66
|
+
const needle = raw.toLowerCase();
|
|
67
|
+
const byId = items.find((item) => (item.ID ?? '').trim().toLowerCase() === needle);
|
|
68
|
+
if (byId) {
|
|
69
|
+
return { Ok: true, Item: byId };
|
|
70
|
+
}
|
|
71
|
+
const byExactName = items.find((item) => (item.Name ?? '').trim().toLowerCase() === needle);
|
|
72
|
+
if (byExactName) {
|
|
73
|
+
return { Ok: true, Item: byExactName };
|
|
74
|
+
}
|
|
75
|
+
const byPartialName = items.find((item) => (item.Name ?? '').trim().toLowerCase().includes(needle));
|
|
76
|
+
if (byPartialName) {
|
|
77
|
+
return { Ok: true, Item: byPartialName };
|
|
78
|
+
}
|
|
79
|
+
const available = CapVideoStudioNames(items.map((item) => (item.Name ?? '').trim()).filter((n) => n.length > 0));
|
|
80
|
+
const suffix = available.length > 0 ? ` Available ${itemNoun}s: ${available.join(', ')}.` : '';
|
|
81
|
+
return {
|
|
82
|
+
Ok: false,
|
|
83
|
+
Error: `No ${itemNoun} matches "${raw}".${suffix}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Allowed status filter values on the Productions surface (`'all'` means unfiltered).
|
|
88
|
+
*/
|
|
89
|
+
export const PRODUCTION_STATUS_FILTERS = [
|
|
90
|
+
'all',
|
|
91
|
+
'Briefing',
|
|
92
|
+
'Styled',
|
|
93
|
+
'Storyboarded',
|
|
94
|
+
'Approved',
|
|
95
|
+
'PreProduced',
|
|
96
|
+
'Composed',
|
|
97
|
+
'RoughCut',
|
|
98
|
+
'Reviewed',
|
|
99
|
+
'Delivered',
|
|
100
|
+
'Blocked',
|
|
101
|
+
'Unknown',
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Filter a list of production cards according to {@link ProductionFilterState}.
|
|
105
|
+
* Pure + deterministic; returns a new array without mutating the input.
|
|
106
|
+
*/
|
|
107
|
+
export function FilterProductionCards(cards, filters) {
|
|
108
|
+
const query = (filters.SearchQuery ?? '').trim().toLowerCase();
|
|
109
|
+
const archetype = (filters.Archetype ?? '').trim().toLowerCase();
|
|
110
|
+
const brandKitId = (filters.BrandKitID ?? '').trim().toLowerCase();
|
|
111
|
+
const companyId = (filters.CompanyID ?? '').trim().toLowerCase();
|
|
112
|
+
const status = (filters.Status ?? '').trim().toLowerCase();
|
|
113
|
+
const fromTime = ParseFilterDateStart(filters.FromDate);
|
|
114
|
+
const toTime = ParseFilterDateEnd(filters.ToDate);
|
|
115
|
+
return cards.filter((card) => {
|
|
116
|
+
if (query) {
|
|
117
|
+
const haystack = [
|
|
118
|
+
card.Name,
|
|
119
|
+
card.Description,
|
|
120
|
+
card.Archetype,
|
|
121
|
+
card.BrandKitName ?? '',
|
|
122
|
+
card.CompanyName ?? '',
|
|
123
|
+
card.Status,
|
|
124
|
+
]
|
|
125
|
+
.join(' ')
|
|
126
|
+
.toLowerCase();
|
|
127
|
+
if (!haystack.includes(query)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (archetype && archetype !== 'all') {
|
|
132
|
+
if ((card.Archetype ?? '').trim().toLowerCase() !== archetype) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (brandKitId && brandKitId !== 'all') {
|
|
137
|
+
if ((card.BrandKitID ?? '').trim().toLowerCase() !== brandKitId) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (companyId && companyId !== 'all') {
|
|
142
|
+
if ((card.CompanyID ?? '').trim().toLowerCase() !== companyId) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (status && status !== 'all') {
|
|
147
|
+
if ((card.Status ?? '').trim().toLowerCase() !== status) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (fromTime !== null || toTime !== null) {
|
|
152
|
+
const cardTime = card.UpdatedAt instanceof Date ? card.UpdatedAt.getTime() : NaN;
|
|
153
|
+
if (!Number.isFinite(cardTime)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (fromTime !== null && cardTime < fromTime) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
if (toTime !== null && cardTime > toTime) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return true;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const LOCAL_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
167
|
+
function ParseFilterDateStart(dateStr) {
|
|
168
|
+
const trimmed = (dateStr ?? '').trim();
|
|
169
|
+
if (!trimmed) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
const match = LOCAL_DATE_PATTERN.exec(trimmed);
|
|
173
|
+
if (match) {
|
|
174
|
+
const y = Number(match[1]);
|
|
175
|
+
const m = Number(match[2]);
|
|
176
|
+
const d = Number(match[3]);
|
|
177
|
+
const localStart = new Date(y, m - 1, d, 0, 0, 0, 0).getTime();
|
|
178
|
+
return Number.isFinite(localStart) ? localStart : null;
|
|
179
|
+
}
|
|
180
|
+
const parsed = Date.parse(trimmed);
|
|
181
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
182
|
+
}
|
|
183
|
+
function ParseFilterDateEnd(dateStr) {
|
|
184
|
+
const trimmed = (dateStr ?? '').trim();
|
|
185
|
+
if (!trimmed) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
const match = LOCAL_DATE_PATTERN.exec(trimmed);
|
|
189
|
+
if (match) {
|
|
190
|
+
const y = Number(match[1]);
|
|
191
|
+
const m = Number(match[2]);
|
|
192
|
+
const d = Number(match[3]);
|
|
193
|
+
const localEnd = new Date(y, m - 1, d, 23, 59, 59, 999).getTime();
|
|
194
|
+
return Number.isFinite(localEnd) ? localEnd : null;
|
|
195
|
+
}
|
|
196
|
+
const parsed = Date.parse(trimmed);
|
|
197
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Count how many popover filters are active on the Productions surface
|
|
201
|
+
* (excluding the free-text search box).
|
|
202
|
+
*/
|
|
203
|
+
export function CountActiveProductionPopoverFilters(filters) {
|
|
204
|
+
let count = 0;
|
|
205
|
+
if (filters.Archetype && filters.Archetype !== 'all')
|
|
206
|
+
count++;
|
|
207
|
+
if (filters.BrandKitID && filters.BrandKitID !== 'all')
|
|
208
|
+
count++;
|
|
209
|
+
if (filters.CompanyID && filters.CompanyID !== 'all')
|
|
210
|
+
count++;
|
|
211
|
+
if (filters.Status && filters.Status !== 'all')
|
|
212
|
+
count++;
|
|
213
|
+
if ((filters.FromDate ?? '').trim().length > 0)
|
|
214
|
+
count++;
|
|
215
|
+
if ((filters.ToDate ?? '').trim().length > 0)
|
|
216
|
+
count++;
|
|
217
|
+
return count;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Build the bounded, read-only agent context object for the Productions surface.
|
|
221
|
+
*/
|
|
222
|
+
export function BuildProductionsAgentContext(input) {
|
|
223
|
+
const visibleNames = input.FilteredCards.map((c) => c.Name);
|
|
224
|
+
const cappedVisible = CapVideoStudioNames(visibleNames);
|
|
225
|
+
const cappedArchetypes = CapVideoStudioNames(input.AvailableArchetypes);
|
|
226
|
+
const cappedBrandKits = CapVideoStudioNames(input.AvailableBrandKitNames);
|
|
227
|
+
const cappedCompanies = CapVideoStudioNames(input.AvailableCompanyNames);
|
|
228
|
+
const hasSearch = (input.Filters.SearchQuery ?? '').trim().length > 0;
|
|
229
|
+
const popoverFilterCount = CountActiveProductionPopoverFilters(input.Filters);
|
|
230
|
+
const context = {
|
|
231
|
+
Surface: 'VideoStudioProductions',
|
|
232
|
+
SuppressChatOverlay: true,
|
|
233
|
+
TotalProductionCount: input.TotalCount,
|
|
234
|
+
FilteredProductionCount: input.FilteredCards.length,
|
|
235
|
+
SearchQuery: input.Filters.SearchQuery,
|
|
236
|
+
HasSearch: hasSearch,
|
|
237
|
+
ArchetypeFilter: input.Filters.Archetype || 'all',
|
|
238
|
+
BrandKitFilterId: input.Filters.BrandKitID || 'all',
|
|
239
|
+
BrandKitFilterName: input.BrandKitFilterName,
|
|
240
|
+
CompanyFilterId: input.Filters.CompanyID || 'all',
|
|
241
|
+
CompanyFilterName: input.CompanyFilterName,
|
|
242
|
+
StatusFilter: input.Filters.Status || 'all',
|
|
243
|
+
FromDateFilter: input.Filters.FromDate || null,
|
|
244
|
+
ToDateFilter: input.Filters.ToDate || null,
|
|
245
|
+
HasActiveFilters: hasSearch || popoverFilterCount > 0,
|
|
246
|
+
SelectedProductionId: input.SelectedProduction?.ID ?? null,
|
|
247
|
+
SelectedProductionName: input.SelectedProduction?.Name ?? null,
|
|
248
|
+
SelectedProductionStatus: input.SelectedProduction?.Status ?? null,
|
|
249
|
+
VisibleProductionNames: cappedVisible,
|
|
250
|
+
AvailableArchetypes: cappedArchetypes,
|
|
251
|
+
AvailableBrandKitNames: cappedBrandKits,
|
|
252
|
+
AvailableCompanyNames: cappedCompanies,
|
|
253
|
+
IsLoading: input.IsLoading,
|
|
254
|
+
};
|
|
255
|
+
if (visibleNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
256
|
+
context['VisibleProductionCount'] = visibleNames.length;
|
|
257
|
+
}
|
|
258
|
+
if (input.AvailableArchetypes.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
259
|
+
context['AvailableArchetypeCount'] = input.AvailableArchetypes.length;
|
|
260
|
+
}
|
|
261
|
+
if (input.AvailableBrandKitNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
262
|
+
context['AvailableBrandKitCount'] = input.AvailableBrandKitNames.length;
|
|
263
|
+
}
|
|
264
|
+
if (input.AvailableCompanyNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
265
|
+
context['AvailableCompanyCount'] = input.AvailableCompanyNames.length;
|
|
266
|
+
}
|
|
267
|
+
return context;
|
|
268
|
+
}
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// 2. BRAND KITS SURFACE HELPERS
|
|
271
|
+
// ============================================================================
|
|
272
|
+
/**
|
|
273
|
+
* Allowed status filter values on the Brand Kits surface.
|
|
274
|
+
*/
|
|
275
|
+
export const BRAND_KIT_STATUS_FILTERS = ['all', 'Active', 'Inactive'];
|
|
276
|
+
/**
|
|
277
|
+
* Shared mapper converting an `MJ: Brand Kits` entity row and its optional assets
|
|
278
|
+
* into a {@link BrandKitCardSnapshot}.
|
|
279
|
+
*/
|
|
280
|
+
export function MapBrandKitToCard(kit, assets) {
|
|
281
|
+
const rawPalette = Array.isArray(kit.StyleObject?.Palette) ? kit.StyleObject.Palette : [];
|
|
282
|
+
const swatches = rawPalette.length > 0
|
|
283
|
+
? rawPalette
|
|
284
|
+
.filter((entry) => Boolean(entry && typeof entry.Value === 'string' && entry.Value.trim()))
|
|
285
|
+
.map((entry) => ({
|
|
286
|
+
Name: (entry.Name ?? '').trim() || 'Color',
|
|
287
|
+
Value: entry.Value.trim(),
|
|
288
|
+
}))
|
|
289
|
+
: Array.isArray(kit.Swatches)
|
|
290
|
+
? [...kit.Swatches]
|
|
291
|
+
: [];
|
|
292
|
+
const assetList = Array.isArray(assets) ? assets : [];
|
|
293
|
+
const logoAssets = assetList.filter((a) => a.AssetType === 'Logo' || a.AssetType === 'LogoMark');
|
|
294
|
+
const primaryLogoAsset = logoAssets.find((a) => Boolean(a.IsPrimary)) ??
|
|
295
|
+
[...logoAssets].sort((a, b) => (a.Sequence ?? Number.MAX_SAFE_INTEGER) - (b.Sequence ?? Number.MAX_SAFE_INTEGER))[0] ??
|
|
296
|
+
null;
|
|
297
|
+
const companyId = typeof kit.CompanyID === 'string' && kit.CompanyID.trim().length > 0
|
|
298
|
+
? kit.CompanyID.trim()
|
|
299
|
+
: null;
|
|
300
|
+
const companyRaw = kit.Company ?? kit.CompanyName ?? null;
|
|
301
|
+
const companyName = typeof companyRaw === 'string' && companyRaw.trim().length > 0
|
|
302
|
+
? companyRaw.trim()
|
|
303
|
+
: null;
|
|
304
|
+
return {
|
|
305
|
+
ID: kit.ID,
|
|
306
|
+
Name: (kit.Name ?? '').trim() || 'Untitled Brand Kit',
|
|
307
|
+
Description: kit.Description ?? '',
|
|
308
|
+
CompanyID: companyId,
|
|
309
|
+
CompanyName: companyName,
|
|
310
|
+
IsDefault: Boolean(kit.IsDefault),
|
|
311
|
+
Status: kit.Status === 'Inactive' ? 'Inactive' : 'Active',
|
|
312
|
+
Swatches: swatches,
|
|
313
|
+
PrimaryLogoFileID: primaryLogoAsset?.FileID ?? kit.PrimaryLogoFileID ?? null,
|
|
314
|
+
PrimaryLogoUrl: kit.PrimaryLogoUrl ?? null,
|
|
315
|
+
AssetCount: assetList.length > 0 ? assetList.length : (kit.AssetCount ?? 0),
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
export const MapBrandKitEntityToCard = MapBrandKitToCard;
|
|
319
|
+
/**
|
|
320
|
+
* Filter a list of Brand Kit cards according to {@link BrandKitFilterState}.
|
|
321
|
+
* Pure + deterministic; returns a new array without mutating the input.
|
|
322
|
+
*/
|
|
323
|
+
export function FilterBrandKitCards(cards, filters) {
|
|
324
|
+
const query = (filters.SearchQuery ?? '').trim().toLowerCase();
|
|
325
|
+
const companyFilter = (filters.CompanyID ?? 'all').trim().toLowerCase();
|
|
326
|
+
const statusFilter = (filters.Status ?? 'all').trim().toLowerCase();
|
|
327
|
+
return cards.filter((card) => {
|
|
328
|
+
if (query) {
|
|
329
|
+
const haystack = [
|
|
330
|
+
card.Name,
|
|
331
|
+
card.Description,
|
|
332
|
+
card.CompanyName ?? 'Org-wide',
|
|
333
|
+
card.Status,
|
|
334
|
+
]
|
|
335
|
+
.join(' ')
|
|
336
|
+
.toLowerCase();
|
|
337
|
+
if (!haystack.includes(query)) {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
if (companyFilter && companyFilter !== 'all') {
|
|
342
|
+
if (companyFilter === 'org-wide') {
|
|
343
|
+
if (card.CompanyID !== null) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
else if ((card.CompanyID ?? '').trim().toLowerCase() !== companyFilter) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
if (statusFilter && statusFilter !== 'all') {
|
|
352
|
+
if ((card.Status ?? '').trim().toLowerCase() !== statusFilter) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return true;
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Build the bounded, read-only agent context object for the Brand Kits surface.
|
|
361
|
+
*/
|
|
362
|
+
export function BuildBrandKitsAgentContext(input) {
|
|
363
|
+
let activeCount = 0;
|
|
364
|
+
let inactiveCount = 0;
|
|
365
|
+
let defaultCount = 0;
|
|
366
|
+
for (const card of input.AllCards) {
|
|
367
|
+
if (card.Status === 'Active') {
|
|
368
|
+
activeCount++;
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
inactiveCount++;
|
|
372
|
+
}
|
|
373
|
+
if (card.IsDefault) {
|
|
374
|
+
defaultCount++;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const visibleNames = input.FilteredCards.map((c) => c.Name);
|
|
378
|
+
const cappedVisible = CapVideoStudioNames(visibleNames);
|
|
379
|
+
const cappedCompanies = CapVideoStudioNames(input.AvailableCompanyNames);
|
|
380
|
+
const hasSearch = (input.Filters.SearchQuery ?? '').trim().length > 0;
|
|
381
|
+
const hasPopoverFilter = (input.Filters.CompanyID !== '' && input.Filters.CompanyID !== 'all') ||
|
|
382
|
+
(input.Filters.Status !== 'all');
|
|
383
|
+
const context = {
|
|
384
|
+
Surface: 'VideoStudioBrandKits',
|
|
385
|
+
SuppressChatOverlay: true,
|
|
386
|
+
TotalBrandKitCount: input.AllCards.length,
|
|
387
|
+
FilteredBrandKitCount: input.FilteredCards.length,
|
|
388
|
+
ActiveBrandKitCount: activeCount,
|
|
389
|
+
InactiveBrandKitCount: inactiveCount,
|
|
390
|
+
DefaultBrandKitCount: defaultCount,
|
|
391
|
+
SearchQuery: input.Filters.SearchQuery,
|
|
392
|
+
HasSearch: hasSearch,
|
|
393
|
+
CompanyFilter: input.Filters.CompanyID || 'all',
|
|
394
|
+
CompanyFilterName: input.CompanyFilterName,
|
|
395
|
+
StatusFilter: input.Filters.Status || 'all',
|
|
396
|
+
HasActiveFilters: hasSearch || hasPopoverFilter,
|
|
397
|
+
VisibleBrandKitNames: cappedVisible,
|
|
398
|
+
AvailableCompanyNames: cappedCompanies,
|
|
399
|
+
IsLoading: input.IsLoading,
|
|
400
|
+
};
|
|
401
|
+
if (visibleNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
402
|
+
context['VisibleBrandKitCount'] = visibleNames.length;
|
|
403
|
+
}
|
|
404
|
+
if (input.AvailableCompanyNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
405
|
+
context['AvailableCompanyCount'] = input.AvailableCompanyNames.length;
|
|
406
|
+
}
|
|
407
|
+
return context;
|
|
408
|
+
}
|
|
409
|
+
// ============================================================================
|
|
410
|
+
// 3. ARCHETYPES SURFACE HELPERS
|
|
411
|
+
// ============================================================================
|
|
412
|
+
/**
|
|
413
|
+
* Canonical `MJ: Video Archetypes.Category` values.
|
|
414
|
+
*/
|
|
415
|
+
export const ARCHETYPE_CATEGORIES = [
|
|
416
|
+
'Announcement',
|
|
417
|
+
'Briefing',
|
|
418
|
+
'DataStory',
|
|
419
|
+
'Explainer',
|
|
420
|
+
'Product',
|
|
421
|
+
'Social',
|
|
422
|
+
'Tutorial',
|
|
423
|
+
];
|
|
424
|
+
/**
|
|
425
|
+
* Allowed category filter values on the Archetypes surface (`'all'` + the 7 categories).
|
|
426
|
+
*/
|
|
427
|
+
export const ARCHETYPE_CATEGORY_FILTERS = ['all', ...ARCHETYPE_CATEGORIES];
|
|
428
|
+
/**
|
|
429
|
+
* Allowed scope filter values on the Archetypes surface.
|
|
430
|
+
*/
|
|
431
|
+
export const ARCHETYPE_SCOPE_FILTERS = ['all', 'shipped', 'company'];
|
|
432
|
+
/**
|
|
433
|
+
* Convert an archetype's beat template into `VideoBeatStripItem[]` with integer
|
|
434
|
+
* `Seconds` scaled (via largest-remainder rounding) so their sum equals
|
|
435
|
+
* `targetDurationSeconds`.
|
|
436
|
+
*/
|
|
437
|
+
export function BuildArchetypeBeatStripItems(rawBeats, targetDurationSeconds) {
|
|
438
|
+
if (!Array.isArray(rawBeats) || rawBeats.length === 0) {
|
|
439
|
+
return [];
|
|
440
|
+
}
|
|
441
|
+
const beatCount = rawBeats.length;
|
|
442
|
+
const targetTotal = Number.isFinite(targetDurationSeconds) && targetDurationSeconds > 0
|
|
443
|
+
? Math.max(beatCount, Math.round(targetDurationSeconds))
|
|
444
|
+
: beatCount * 5;
|
|
445
|
+
const weights = rawBeats.map((b) => {
|
|
446
|
+
const def = b.DefaultDurationSeconds;
|
|
447
|
+
if (typeof def === 'number' && Number.isFinite(def) && def > 0) {
|
|
448
|
+
return def;
|
|
449
|
+
}
|
|
450
|
+
const min = typeof b.MinDurationSeconds === 'number' &&
|
|
451
|
+
Number.isFinite(b.MinDurationSeconds) &&
|
|
452
|
+
b.MinDurationSeconds > 0
|
|
453
|
+
? b.MinDurationSeconds
|
|
454
|
+
: 1;
|
|
455
|
+
const max = typeof b.MaxDurationSeconds === 'number' &&
|
|
456
|
+
Number.isFinite(b.MaxDurationSeconds) &&
|
|
457
|
+
b.MaxDurationSeconds >= min
|
|
458
|
+
? b.MaxDurationSeconds
|
|
459
|
+
: min;
|
|
460
|
+
return Math.max(0.1, (min + max) / 2);
|
|
461
|
+
});
|
|
462
|
+
const totalWeight = weights.reduce((sum, w) => sum + w, 0) || beatCount;
|
|
463
|
+
const exactQuotas = weights.map((w) => (w / totalWeight) * targetTotal);
|
|
464
|
+
const integerSeconds = exactQuotas.map((q) => Math.max(1, Math.floor(q)));
|
|
465
|
+
let currentSum = integerSeconds.reduce((sum, s) => sum + s, 0);
|
|
466
|
+
if (currentSum < targetTotal) {
|
|
467
|
+
const order = exactQuotas
|
|
468
|
+
.map((q, idx) => ({ idx, remainder: q - Math.floor(q), quota: q }))
|
|
469
|
+
.sort((a, b) => {
|
|
470
|
+
if (Math.abs(b.remainder - a.remainder) > 1e-9) {
|
|
471
|
+
return b.remainder - a.remainder;
|
|
472
|
+
}
|
|
473
|
+
if (Math.abs(b.quota - a.quota) > 1e-9) {
|
|
474
|
+
return b.quota - a.quota;
|
|
475
|
+
}
|
|
476
|
+
return a.idx - b.idx;
|
|
477
|
+
});
|
|
478
|
+
let cursor = 0;
|
|
479
|
+
while (currentSum < targetTotal) {
|
|
480
|
+
const targetIdx = order[cursor % order.length].idx;
|
|
481
|
+
integerSeconds[targetIdx] += 1;
|
|
482
|
+
currentSum += 1;
|
|
483
|
+
cursor += 1;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
else if (currentSum > targetTotal) {
|
|
487
|
+
const order = exactQuotas
|
|
488
|
+
.map((q, idx) => ({ idx, remainder: q - Math.floor(q), quota: q }))
|
|
489
|
+
.sort((a, b) => a.remainder - b.remainder || a.quota - b.quota || b.idx - a.idx);
|
|
490
|
+
let cursor = 0;
|
|
491
|
+
let safety = beatCount * targetTotal;
|
|
492
|
+
while (currentSum > targetTotal && safety > 0) {
|
|
493
|
+
const targetIdx = order[cursor % order.length].idx;
|
|
494
|
+
if (integerSeconds[targetIdx] > 1) {
|
|
495
|
+
integerSeconds[targetIdx] -= 1;
|
|
496
|
+
currentSum -= 1;
|
|
497
|
+
}
|
|
498
|
+
cursor += 1;
|
|
499
|
+
safety -= 1;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return rawBeats.map((b, idx) => {
|
|
503
|
+
const key = (b.Key ?? '').trim() || `beat-${idx + 1}`;
|
|
504
|
+
const label = (b.Label ?? b.Key ?? '').trim() || `Beat ${idx + 1}`;
|
|
505
|
+
const detail = (b.Purpose ?? b.Guidance ?? '').trim();
|
|
506
|
+
return {
|
|
507
|
+
Key: key,
|
|
508
|
+
Label: label,
|
|
509
|
+
Seconds: integerSeconds[idx],
|
|
510
|
+
...(detail ? { Detail: detail } : {}),
|
|
511
|
+
};
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Shared mapper converting an `MJ: Video Archetypes` entity row into an
|
|
516
|
+
* {@link ArchetypeCardSnapshot}, scaling its beat template with
|
|
517
|
+
* {@link BuildArchetypeBeatStripItems} so `BeatItems` always sum to
|
|
518
|
+
* `DefaultDurationSeconds`.
|
|
519
|
+
*/
|
|
520
|
+
export function MapArchetypeToCard(entity) {
|
|
521
|
+
const rawCategory = (entity.Category ?? 'Explainer').trim();
|
|
522
|
+
const category = ARCHETYPE_CATEGORIES.includes(rawCategory)
|
|
523
|
+
? rawCategory
|
|
524
|
+
: 'Explainer';
|
|
525
|
+
const defaultDur = typeof entity.DefaultDurationSeconds === 'number' &&
|
|
526
|
+
Number.isFinite(entity.DefaultDurationSeconds) &&
|
|
527
|
+
entity.DefaultDurationSeconds > 0
|
|
528
|
+
? entity.DefaultDurationSeconds
|
|
529
|
+
: 60;
|
|
530
|
+
const minDur = typeof entity.MinDurationSeconds === 'number' &&
|
|
531
|
+
Number.isFinite(entity.MinDurationSeconds) &&
|
|
532
|
+
entity.MinDurationSeconds > 0
|
|
533
|
+
? entity.MinDurationSeconds
|
|
534
|
+
: 30;
|
|
535
|
+
const maxDur = typeof entity.MinDurationSeconds === 'number' &&
|
|
536
|
+
typeof entity.MaxDurationSeconds === 'number' &&
|
|
537
|
+
Number.isFinite(entity.MaxDurationSeconds) &&
|
|
538
|
+
entity.MaxDurationSeconds >= minDur
|
|
539
|
+
? entity.MaxDurationSeconds
|
|
540
|
+
: typeof entity.MaxDurationSeconds === 'number' &&
|
|
541
|
+
Number.isFinite(entity.MaxDurationSeconds) &&
|
|
542
|
+
entity.MaxDurationSeconds > 0
|
|
543
|
+
? entity.MaxDurationSeconds
|
|
544
|
+
: Math.max(defaultDur, 120);
|
|
545
|
+
let rawBeats = [];
|
|
546
|
+
if (Array.isArray(entity.BeatTemplateObject)) {
|
|
547
|
+
rawBeats = entity.BeatTemplateObject;
|
|
548
|
+
}
|
|
549
|
+
else if (typeof entity.BeatTemplate === 'string' && entity.BeatTemplate.trim().startsWith('[')) {
|
|
550
|
+
try {
|
|
551
|
+
const parsed = JSON.parse(entity.BeatTemplate);
|
|
552
|
+
if (Array.isArray(parsed)) {
|
|
553
|
+
rawBeats = parsed;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
catch {
|
|
557
|
+
rawBeats = [];
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const beatItems = rawBeats.length > 0
|
|
561
|
+
? BuildArchetypeBeatStripItems(rawBeats, defaultDur)
|
|
562
|
+
: Array.isArray(entity.BeatItems)
|
|
563
|
+
? entity.BeatItems.map((item) => ({ ...item }))
|
|
564
|
+
: [];
|
|
565
|
+
const beatLabels = beatItems.length > 0
|
|
566
|
+
? beatItems.map((b) => b.Label)
|
|
567
|
+
: Array.isArray(entity.BeatLabels)
|
|
568
|
+
? [...entity.BeatLabels]
|
|
569
|
+
: [];
|
|
570
|
+
const companyId = typeof entity.CompanyID === 'string' && entity.CompanyID.trim().length > 0
|
|
571
|
+
? entity.CompanyID.trim()
|
|
572
|
+
: null;
|
|
573
|
+
const companyRaw = entity.Company ?? entity.CompanyName ?? null;
|
|
574
|
+
const companyName = typeof companyRaw === 'string' && companyRaw.trim().length > 0
|
|
575
|
+
? companyRaw.trim()
|
|
576
|
+
: null;
|
|
577
|
+
return {
|
|
578
|
+
ID: entity.ID,
|
|
579
|
+
Name: (entity.Name ?? '').trim() || 'Untitled Archetype',
|
|
580
|
+
Description: entity.Description ?? '',
|
|
581
|
+
Category: category,
|
|
582
|
+
Status: entity.Status === 'Inactive' ? 'Inactive' : 'Active',
|
|
583
|
+
CompanyID: companyId,
|
|
584
|
+
CompanyName: companyName,
|
|
585
|
+
IsShipped: companyId === null,
|
|
586
|
+
DefaultDurationSeconds: defaultDur,
|
|
587
|
+
MinDurationSeconds: minDur,
|
|
588
|
+
MaxDurationSeconds: maxDur,
|
|
589
|
+
DefaultFormats: (entity.DefaultFormats ?? '').trim() || '16:9',
|
|
590
|
+
BeatCount: beatItems.length || beatLabels.length,
|
|
591
|
+
BeatLabels: beatLabels,
|
|
592
|
+
BeatItems: beatItems,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
export const MapArchetypeEntityToCard = MapArchetypeToCard;
|
|
596
|
+
/**
|
|
597
|
+
* Derive a stable snake_case beat `Key` from its human-readable `Label`,
|
|
598
|
+
* deduplicating against `usedKeys` when provided.
|
|
599
|
+
*/
|
|
600
|
+
export function DeriveBeatKeyFromLabel(label, fallbackIndex = 1, usedKeys) {
|
|
601
|
+
const cleaned = (label ?? '')
|
|
602
|
+
.trim()
|
|
603
|
+
.toLowerCase()
|
|
604
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
605
|
+
.replace(/^_/, '')
|
|
606
|
+
.replace(/_$/, '');
|
|
607
|
+
const baseKey = cleaned || `beat_${Math.max(1, fallbackIndex)}`;
|
|
608
|
+
if (!usedKeys) {
|
|
609
|
+
return baseKey;
|
|
610
|
+
}
|
|
611
|
+
let candidate = baseKey;
|
|
612
|
+
let suffix = 2;
|
|
613
|
+
while (usedKeys.has(candidate)) {
|
|
614
|
+
candidate = `${baseKey}_${suffix}`;
|
|
615
|
+
suffix += 1;
|
|
616
|
+
}
|
|
617
|
+
usedKeys.add(candidate);
|
|
618
|
+
return candidate;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Filter a list of Archetype cards according to {@link ArchetypeFilterState}.
|
|
622
|
+
* Pure + deterministic; returns a new array without mutating the input.
|
|
623
|
+
*/
|
|
624
|
+
export function FilterArchetypeCards(cards, filters) {
|
|
625
|
+
const query = (filters.SearchQuery ?? '').trim().toLowerCase();
|
|
626
|
+
const category = (filters.Category ?? 'all').trim().toLowerCase();
|
|
627
|
+
const scope = (filters.Scope ?? 'all').trim().toLowerCase();
|
|
628
|
+
return cards.filter((card) => {
|
|
629
|
+
if (query) {
|
|
630
|
+
const haystack = [
|
|
631
|
+
card.Name,
|
|
632
|
+
card.Description,
|
|
633
|
+
card.Category,
|
|
634
|
+
card.CompanyName ?? 'Shipped',
|
|
635
|
+
card.DefaultFormats,
|
|
636
|
+
...card.BeatLabels,
|
|
637
|
+
]
|
|
638
|
+
.join(' ')
|
|
639
|
+
.toLowerCase();
|
|
640
|
+
if (!haystack.includes(query)) {
|
|
641
|
+
return false;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
if (category && category !== 'all') {
|
|
645
|
+
if ((card.Category ?? '').trim().toLowerCase() !== category) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
if (scope === 'shipped' && !card.IsShipped) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
if (scope === 'company' && card.IsShipped) {
|
|
653
|
+
return false;
|
|
654
|
+
}
|
|
655
|
+
return true;
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Group filtered archetype cards by `Category` in canonical category order,
|
|
660
|
+
* omitting empty categories. Cards within each category are sorted alphabetically
|
|
661
|
+
* by `Name`.
|
|
662
|
+
*/
|
|
663
|
+
export function GroupArchetypesByCategory(cards) {
|
|
664
|
+
const byCategory = new Map();
|
|
665
|
+
for (const card of cards) {
|
|
666
|
+
const list = byCategory.get(card.Category) ?? [];
|
|
667
|
+
list.push(card);
|
|
668
|
+
byCategory.set(card.Category, list);
|
|
669
|
+
}
|
|
670
|
+
const groups = [];
|
|
671
|
+
for (const category of ARCHETYPE_CATEGORIES) {
|
|
672
|
+
const list = byCategory.get(category);
|
|
673
|
+
if (list && list.length > 0) {
|
|
674
|
+
const sorted = [...list].sort((a, b) => (a.Name ?? '').localeCompare(b.Name ?? ''));
|
|
675
|
+
groups.push({ Category: category, Archetypes: sorted });
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return groups;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Build the bounded, read-only agent context object for the Archetypes surface.
|
|
682
|
+
*/
|
|
683
|
+
export function BuildArchetypesAgentContext(input) {
|
|
684
|
+
let shippedCount = 0;
|
|
685
|
+
let companyCount = 0;
|
|
686
|
+
for (const card of input.AllCards) {
|
|
687
|
+
if (card.IsShipped) {
|
|
688
|
+
shippedCount++;
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
companyCount++;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const visibleNames = input.FilteredCards.map((c) => c.Name);
|
|
695
|
+
const cappedVisible = CapVideoStudioNames(visibleNames);
|
|
696
|
+
const hasSearch = (input.Filters.SearchQuery ?? '').trim().length > 0;
|
|
697
|
+
const hasPopoverFilter = input.Filters.Category !== 'all' || input.Filters.Scope !== 'all';
|
|
698
|
+
const context = {
|
|
699
|
+
Surface: 'VideoStudioArchetypes',
|
|
700
|
+
SuppressChatOverlay: true,
|
|
701
|
+
TotalArchetypeCount: input.AllCards.length,
|
|
702
|
+
FilteredArchetypeCount: input.FilteredCards.length,
|
|
703
|
+
ShippedArchetypeCount: shippedCount,
|
|
704
|
+
CompanyArchetypeCount: companyCount,
|
|
705
|
+
SearchQuery: input.Filters.SearchQuery,
|
|
706
|
+
HasSearch: hasSearch,
|
|
707
|
+
CategoryFilter: input.Filters.Category,
|
|
708
|
+
ScopeFilter: input.Filters.Scope,
|
|
709
|
+
HasActiveFilters: hasSearch || hasPopoverFilter,
|
|
710
|
+
SelectedArchetypeId: input.SelectedArchetype?.ID ?? null,
|
|
711
|
+
SelectedArchetypeName: input.SelectedArchetype?.Name ?? null,
|
|
712
|
+
VisibleArchetypeNames: cappedVisible,
|
|
713
|
+
AvailableCategories: [...ARCHETYPE_CATEGORIES],
|
|
714
|
+
IsLoading: input.IsLoading,
|
|
715
|
+
};
|
|
716
|
+
if (visibleNames.length > VIDEO_STUDIO_NAME_LIST_CAP) {
|
|
717
|
+
context['VisibleArchetypeCount'] = visibleNames.length;
|
|
718
|
+
}
|
|
719
|
+
return context;
|
|
720
|
+
}
|
|
721
|
+
// ============================================================================
|
|
722
|
+
// 4. STUDIO SURFACE HELPERS
|
|
723
|
+
// ============================================================================
|
|
724
|
+
/**
|
|
725
|
+
* Canonical video aspect-ratio format codes selectable in the Studio Start form.
|
|
726
|
+
*/
|
|
727
|
+
export const STUDIO_VIDEO_FORMATS = ['16:9', '9:16', '1:1', '4:5'];
|
|
728
|
+
/**
|
|
729
|
+
* Parse an archetype's `DefaultFormats` string (JSON array or comma-separated list)
|
|
730
|
+
* into a deduplicated array of canonical {@link StudioVideoFormat} values.
|
|
731
|
+
* Falls back to `['16:9']` when no valid format codes are present.
|
|
732
|
+
*/
|
|
733
|
+
export function ParseArchetypeDefaultFormats(raw) {
|
|
734
|
+
const text = (raw ?? '').trim();
|
|
735
|
+
if (!text) {
|
|
736
|
+
return ['16:9'];
|
|
737
|
+
}
|
|
738
|
+
let tokens = [];
|
|
739
|
+
if (text.startsWith('[')) {
|
|
740
|
+
try {
|
|
741
|
+
const parsed = JSON.parse(text);
|
|
742
|
+
if (Array.isArray(parsed)) {
|
|
743
|
+
tokens = parsed.filter((v) => typeof v === 'string');
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
catch {
|
|
747
|
+
tokens = text.split(',');
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
else {
|
|
751
|
+
tokens = text.split(',');
|
|
752
|
+
}
|
|
753
|
+
const allowed = new Set(STUDIO_VIDEO_FORMATS);
|
|
754
|
+
const seen = new Set();
|
|
755
|
+
const result = [];
|
|
756
|
+
for (const token of tokens) {
|
|
757
|
+
const cleaned = token.replace(/['"\[\]\s]/g, '').trim();
|
|
758
|
+
if (allowed.has(cleaned)) {
|
|
759
|
+
const fmt = cleaned;
|
|
760
|
+
if (!seen.has(fmt)) {
|
|
761
|
+
seen.add(fmt);
|
|
762
|
+
result.push(fmt);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
return result.length > 0 ? result : ['16:9'];
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Clamp a requested duration in seconds to an archetype's `[min, max]` bounds.
|
|
770
|
+
*/
|
|
771
|
+
export function ClampStudioDuration(value, minSeconds, maxSeconds) {
|
|
772
|
+
const safeMin = Number.isFinite(minSeconds) && minSeconds > 0 ? Math.round(minSeconds) : 5;
|
|
773
|
+
const safeMax = Number.isFinite(maxSeconds) && maxSeconds >= safeMin ? Math.round(maxSeconds) : Math.max(safeMin, 180);
|
|
774
|
+
if (!Number.isFinite(value)) {
|
|
775
|
+
return safeMin;
|
|
776
|
+
}
|
|
777
|
+
const rounded = Math.round(value);
|
|
778
|
+
return Math.min(safeMax, Math.max(safeMin, rounded));
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Build the `MJ: Queries` RunView `ExtraFilter` for the Studio saved-query picker.
|
|
782
|
+
* Escapes SQL `LIKE` wildcards (`%`, `_`, `[`, `\`) with `\` + `ESCAPE '\'` and
|
|
783
|
+
* escapes single quotes via {@link EscapeSQLString} from `@memberjunction/global`.
|
|
784
|
+
*/
|
|
785
|
+
export function BuildSavedQuerySearchFilter(searchText) {
|
|
786
|
+
const trimmed = (searchText ?? '').trim();
|
|
787
|
+
if (!trimmed) {
|
|
788
|
+
return '';
|
|
789
|
+
}
|
|
790
|
+
const likeEscaped = trimmed.replace(/[\\%_[]/g, '\\$&');
|
|
791
|
+
const sqlEscaped = EscapeSQLString(likeEscaped);
|
|
792
|
+
return `Name LIKE '%${sqlEscaped}%' ESCAPE '\\'`;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Deterministic plain-text first message composer for starting a new Studio production:
|
|
796
|
+
* `"Make a <archetype> video, about <topic>, <n> seconds, formats <list>, brand kit <name>, data from <query names>."`
|
|
797
|
+
*/
|
|
798
|
+
export function ComposeStudioFirstMessage(input) {
|
|
799
|
+
const archetype = (input.ArchetypeName ?? '').trim() || 'Explainer';
|
|
800
|
+
const topic = (input.Topic ?? '').trim();
|
|
801
|
+
const duration = Number.isFinite(input.DurationSeconds) && input.DurationSeconds > 0
|
|
802
|
+
? Math.round(input.DurationSeconds)
|
|
803
|
+
: 30;
|
|
804
|
+
const formats = input.Formats && input.Formats.length > 0
|
|
805
|
+
? input.Formats.map((f) => f.trim()).filter((f) => f.length > 0).join(', ')
|
|
806
|
+
: '16:9';
|
|
807
|
+
const rawBrandKit = (input.BrandKitName ?? '').trim();
|
|
808
|
+
const brandKitClause = rawBrandKit.length > 0 && rawBrandKit.toLowerCase() !== 'none'
|
|
809
|
+
? `, brand kit ${rawBrandKit}`
|
|
810
|
+
: '';
|
|
811
|
+
const queriesList = (input.QueryNames ?? [])
|
|
812
|
+
.map((q) => q.trim())
|
|
813
|
+
.filter((q) => q.length > 0 && q.toLowerCase() !== 'none');
|
|
814
|
+
const dataClause = queriesList.length > 0 ? `, data from ${queriesList.join(', ')}` : '';
|
|
815
|
+
const narrationClause = input.NarrationEnabled === false ? ', without voiceover narration' : '';
|
|
816
|
+
return `Make a ${archetype} video, about ${topic}, ${duration} seconds, formats ${formats}${brandKitClause}${dataClause}${narrationClause}.`;
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Build the `{ VideoStudio: { ArchetypeID, BrandKitID, Formats, DurationSeconds, QueryIDs, ... } }`
|
|
820
|
+
* app context object passed into `<vs-copilot-panel>` / `<mj-conversation-chat-area>`.
|
|
821
|
+
*/
|
|
822
|
+
export function BuildStudioAppContext(input) {
|
|
823
|
+
return {
|
|
824
|
+
VideoStudio: {
|
|
825
|
+
ArchetypeID: input.ArchetypeID ?? null,
|
|
826
|
+
BrandKitID: input.BrandKitID ?? null,
|
|
827
|
+
Formats: [...(input.Formats ?? [])],
|
|
828
|
+
DurationSeconds: input.DurationSeconds,
|
|
829
|
+
QueryIDs: [...(input.QueryIDs ?? [])],
|
|
830
|
+
...(input.NarrationEnabled !== undefined
|
|
831
|
+
? { NarrationEnabled: input.NarrationEnabled }
|
|
832
|
+
: {}),
|
|
833
|
+
...(input.CaptionsMode !== undefined
|
|
834
|
+
? { CaptionsMode: input.CaptionsMode }
|
|
835
|
+
: {}),
|
|
836
|
+
},
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Build the four reversible Studio brief form-fill client tools
|
|
841
|
+
* (`SelectStudioArchetype`, `SelectStudioBrandKit`, `SetStudioFormats`, `SetStudioTopic`)
|
|
842
|
+
* shared across Productions, Brand Kits, and Archetypes while the brief is showing.
|
|
843
|
+
*/
|
|
844
|
+
export function BuildStudioBriefClientTools(adapter) {
|
|
845
|
+
return [
|
|
846
|
+
{
|
|
847
|
+
Name: 'SelectStudioArchetype',
|
|
848
|
+
Description: 'Select a video archetype in the Video Studio brief by ID or name. Prefills default formats and duration bounds.',
|
|
849
|
+
ParameterSchema: {
|
|
850
|
+
type: 'object',
|
|
851
|
+
properties: {
|
|
852
|
+
archetype: {
|
|
853
|
+
type: 'string',
|
|
854
|
+
description: 'Archetype ID or name to select',
|
|
855
|
+
},
|
|
856
|
+
},
|
|
857
|
+
required: ['archetype'],
|
|
858
|
+
},
|
|
859
|
+
Handler: async (params) => {
|
|
860
|
+
// SAFETY BOUNDARY: Mutates local brief form state only.
|
|
861
|
+
const raw = typeof params?.['archetype'] === 'string' ? params['archetype'] : '';
|
|
862
|
+
const brief = adapter.GetBriefComponent?.();
|
|
863
|
+
if (brief) {
|
|
864
|
+
return brief.ApplyClientToolArchetype(raw);
|
|
865
|
+
}
|
|
866
|
+
const archetypes = adapter.GetArchetypes?.() ?? [];
|
|
867
|
+
const resolution = ResolveVideoStudioItem(raw, archetypes, 'archetype');
|
|
868
|
+
if (!resolution.Ok) {
|
|
869
|
+
return { Success: false, ErrorMessage: resolution.Error };
|
|
870
|
+
}
|
|
871
|
+
const formats = ParseArchetypeDefaultFormats(resolution.Item.DefaultFormats);
|
|
872
|
+
const durationSeconds = ClampStudioDuration(resolution.Item.DefaultDurationSeconds, resolution.Item.MinDurationSeconds, resolution.Item.MaxDurationSeconds);
|
|
873
|
+
adapter.OnArchetypeSelected?.(resolution.Item, formats, durationSeconds);
|
|
874
|
+
return {
|
|
875
|
+
Success: true,
|
|
876
|
+
Data: {
|
|
877
|
+
SelectedArchetypeId: resolution.Item.ID,
|
|
878
|
+
SelectedArchetypeName: resolution.Item.Name,
|
|
879
|
+
},
|
|
880
|
+
};
|
|
881
|
+
},
|
|
882
|
+
},
|
|
883
|
+
{
|
|
884
|
+
Name: 'SelectStudioBrandKit',
|
|
885
|
+
Description: 'Select a Brand Kit in the Video Studio brief by ID or name (or pass "none" to clear the selection).',
|
|
886
|
+
ParameterSchema: {
|
|
887
|
+
type: 'object',
|
|
888
|
+
properties: {
|
|
889
|
+
brandKit: {
|
|
890
|
+
type: 'string',
|
|
891
|
+
description: 'Brand Kit ID or name to select, or "none" to clear',
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
required: ['brandKit'],
|
|
895
|
+
},
|
|
896
|
+
Handler: async (params) => {
|
|
897
|
+
// SAFETY BOUNDARY: Mutates local brief form state only.
|
|
898
|
+
const raw = typeof params?.['brandKit'] === 'string' ? params['brandKit'].trim() : '';
|
|
899
|
+
const brief = adapter.GetBriefComponent?.();
|
|
900
|
+
if (brief) {
|
|
901
|
+
return brief.ApplyClientToolBrandKit(raw);
|
|
902
|
+
}
|
|
903
|
+
if (!raw || raw.toLowerCase() === 'none') {
|
|
904
|
+
adapter.OnBrandKitSelected?.(null);
|
|
905
|
+
return { Success: true, Data: { SelectedBrandKitId: null } };
|
|
906
|
+
}
|
|
907
|
+
const kits = adapter.GetBrandKits?.() ?? [];
|
|
908
|
+
const resolution = ResolveVideoStudioItem(raw, kits, 'brand kit');
|
|
909
|
+
if (!resolution.Ok) {
|
|
910
|
+
return { Success: false, ErrorMessage: resolution.Error };
|
|
911
|
+
}
|
|
912
|
+
adapter.OnBrandKitSelected?.(resolution.Item);
|
|
913
|
+
return {
|
|
914
|
+
Success: true,
|
|
915
|
+
Data: {
|
|
916
|
+
SelectedBrandKitId: resolution.Item.ID,
|
|
917
|
+
SelectedBrandKitName: resolution.Item.Name,
|
|
918
|
+
},
|
|
919
|
+
};
|
|
920
|
+
},
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
Name: 'SetStudioFormats',
|
|
924
|
+
Description: 'Set the output video aspect-ratio formats in the Video Studio brief (e.g. ["16:9", "9:16"]).',
|
|
925
|
+
ParameterSchema: {
|
|
926
|
+
type: 'object',
|
|
927
|
+
properties: {
|
|
928
|
+
formats: {
|
|
929
|
+
type: 'array',
|
|
930
|
+
items: { type: 'string' },
|
|
931
|
+
description: 'Array of format codes from 16:9, 9:16, 1:1, 4:5',
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
required: ['formats'],
|
|
935
|
+
},
|
|
936
|
+
Handler: async (params) => {
|
|
937
|
+
// SAFETY BOUNDARY: Mutates local brief form state only.
|
|
938
|
+
const rawFormats = params?.['formats'];
|
|
939
|
+
const brief = adapter.GetBriefComponent?.();
|
|
940
|
+
if (brief) {
|
|
941
|
+
return brief.ApplyClientToolFormats(rawFormats);
|
|
942
|
+
}
|
|
943
|
+
const list = Array.isArray(rawFormats)
|
|
944
|
+
? rawFormats.filter((v) => typeof v === 'string')
|
|
945
|
+
: typeof rawFormats === 'string'
|
|
946
|
+
? rawFormats.split(',')
|
|
947
|
+
: [];
|
|
948
|
+
const allowed = new Set(STUDIO_VIDEO_FORMATS);
|
|
949
|
+
const valid = [];
|
|
950
|
+
for (const item of list) {
|
|
951
|
+
const cleaned = item.trim();
|
|
952
|
+
if (allowed.has(cleaned) && !valid.includes(cleaned)) {
|
|
953
|
+
valid.push(cleaned);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (valid.length === 0) {
|
|
957
|
+
return {
|
|
958
|
+
Success: false,
|
|
959
|
+
ErrorMessage: `Specify at least one valid format (${STUDIO_VIDEO_FORMATS.join(', ')}).`,
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
adapter.OnFormatsSelected?.(valid);
|
|
963
|
+
return { Success: true, Data: { SelectedFormats: [...valid] } };
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
Name: 'SetStudioTopic',
|
|
968
|
+
Description: 'Set the topic description in the Video Studio brief without starting production.',
|
|
969
|
+
ParameterSchema: {
|
|
970
|
+
type: 'object',
|
|
971
|
+
properties: {
|
|
972
|
+
topic: {
|
|
973
|
+
type: 'string',
|
|
974
|
+
description: 'Topic or prompt description for the video',
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
required: ['topic'],
|
|
978
|
+
},
|
|
979
|
+
Handler: async (params) => {
|
|
980
|
+
// SAFETY BOUNDARY: Mutates local brief form state only.
|
|
981
|
+
const topic = typeof params?.['topic'] === 'string' ? params['topic'] : '';
|
|
982
|
+
const brief = adapter.GetBriefComponent?.();
|
|
983
|
+
if (brief) {
|
|
984
|
+
return brief.ApplyClientToolTopic(topic);
|
|
985
|
+
}
|
|
986
|
+
adapter.OnTopicChanged?.(topic);
|
|
987
|
+
return { Success: true, Data: { Topic: topic } };
|
|
988
|
+
},
|
|
989
|
+
},
|
|
990
|
+
];
|
|
991
|
+
}
|
|
992
|
+
//# sourceMappingURL=video-studio-agent-context.js.map
|