From 6f04af073de53659cd70ba907b1b2252cefe422d Mon Sep 17 00:00:00 2001 From: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:08:57 +0000 Subject: [PATCH] feat: fe caching (#13642) Co-authored-by: Kirtan Thakkar --- .claude/skills/ui-architect/SKILL.md | 22 +- .../ui-architect/references/calm-signal.md | 74 +-- .../references/component-catalog.md | 132 +++--- .../ui-architect/references/conventions.md | 115 ++--- .../references/core-controls-table.md | 188 ++++++-- .../ui-architect/references/core-display.md | 75 ++- .../references/creating-components.md | 42 +- .../ui-architect/references/data-fetching.md | 347 ++++++++++++++ .../ui-architect/references/design-tokens.md | 106 +++-- .../ui-architect/references/feedback-data.md | 94 ++-- .../ui-architect/references/forms-inputs.md | 68 ++- .../references/forms-specialized.md | 65 ++- .../references/forms-validation.md | 179 +++++-- .../references/keyboard-shortcuts.md | 34 +- .../references/navigation-menus.md | 90 ++-- .../references/overlay-navigation.md | 50 +- .../ui-architect/references/page-recipes.md | 67 ++- web/package-lock.json | 128 +++++ web/package.json | 2 + .../EnterpriseUpgradeDialog.spec.ts | 13 +- .../components/EnterpriseUpgradeDialog.vue | 6 +- web/src/components/O2AIChat.vue | 3 +- .../ai_toolsets/AddAiToolset.spec.ts | 21 +- .../alertLibrary/LibraryInstallDialog.vue | 8 +- web/src/components/alerts/AddAlert.spec.ts | 73 +-- .../components/alerts/AddDestination.spec.ts | 17 +- web/src/components/alerts/AddDestination.vue | 29 +- .../alerts/AddExternalAlertSource.spec.ts | 18 +- web/src/components/alerts/AddTemplate.spec.ts | 15 +- web/src/components/alerts/AddTemplate.vue | 27 +- .../alerts/AlertDestinationsField.spec.ts | 114 +++++ .../alerts/AlertDestinationsField.vue | 21 +- .../alerts/AlertEvaluationHistory.spec.ts | 13 +- .../alerts/AlertEvaluationHistory.vue | 43 +- .../components/alerts/AlertHistory.spec.ts | 17 +- web/src/components/alerts/AlertHistory.vue | 117 +++-- .../alerts/AlertHistoryDrawer.spec.ts | 26 +- .../components/alerts/AlertHistoryDrawer.vue | 17 +- .../components/alerts/AlertInsights.spec.ts | 27 +- web/src/components/alerts/AlertList.spec.ts | 73 ++- web/src/components/alerts/AlertList.vue | 438 ++++++++++++------ .../components/alerts/AlertListSlo.spec.ts | 38 +- .../alerts/AlertsDestinationList.spec.ts | 28 +- .../alerts/AlertsDestinationList.vue | 155 ++++--- .../alerts/DependencyImpactDialog.vue | 27 +- .../alerts/ExternalAlertSourcesList.spec.ts | 34 +- .../alerts/ExternalAlertSourcesList.vue | 76 ++- web/src/components/alerts/ImportAlert.spec.ts | 29 +- web/src/components/alerts/ImportAlert.vue | 18 +- .../alerts/ImportDestination.spec.ts | 13 +- .../components/alerts/ImportDestination.vue | 13 +- .../alerts/ImportSemanticGroups.spec.ts | 15 +- .../alerts/ImportSemanticGroupsDrawer.spec.ts | 13 +- .../components/alerts/ImportTemplate.spec.ts | 27 +- web/src/components/alerts/ImportTemplate.vue | 13 +- .../alerts/IncidentAlertTriggersTable.spec.ts | 13 +- .../alerts/IncidentDetailDrawer.spec.ts | 28 +- .../alerts/IncidentDetailDrawer.vue | 67 +-- .../components/alerts/IncidentList.spec.ts | 22 +- web/src/components/alerts/IncidentList.vue | 110 +++-- .../alerts/IncidentTimeline.spec.ts | 15 +- .../OrganizationDeduplicationSettings.spec.ts | 41 +- .../alerts/PipelinesDestinationList.spec.ts | 61 ++- .../alerts/PipelinesDestinationList.vue | 144 +++--- .../components/alerts/PreviewAlert.spec.ts | 19 +- web/src/components/alerts/PreviewAlert.vue | 4 +- .../alerts/QueryEditorDialog.spec.ts | 22 +- .../components/alerts/QueryEditorDialog.vue | 12 +- .../components/alerts/TemplateList.spec.ts | 20 +- web/src/components/alerts/TemplateList.vue | 187 ++++---- .../alerts/steps/AlertSettings.spec.ts | 13 +- .../template-content/TemplatePreviewPanel.vue | 17 +- .../steps/AnomalyDetectionConfig.spec.ts | 13 +- .../steps/AnomalyDetectionConfig.vue | 10 +- .../cipherkeys/AddCipherKey.spec.ts | 21 +- .../common/sidebar/FolderList.spec.ts | 16 +- .../components/common/sidebar/FolderList.vue | 5 +- .../common/sidebar/SearchFieldList.spec.ts | 13 +- .../common/sidebar/SelectFolderDropDown.vue | 3 +- .../dashboards/AddDashboard.spec.ts | 15 +- .../components/dashboards/AddDashboard.vue | 2 +- .../dashboards/AddDashboardFromGitHub.spec.ts | 19 +- .../dashboards/AddDashboardFromGitHub.vue | 3 +- .../components/dashboards/PanelContainer.vue | 3 +- .../dashboards/PanelSchemaRenderer.vue | 3 + .../SelectDashboardDropdown.spec.ts | 54 ++- .../dashboards/SelectDashboardDropdown.vue | 68 ++- .../dashboards/SelectTabDropdown.vue | 3 +- .../dashboards/VariablesValueSelector.spec.ts | 13 +- .../dashboards/VariablesValueSelector.vue | 14 +- .../dashboards/addPanel/AddAnnotation.spec.ts | 17 +- .../dashboards/addPanel/AddAnnotation.vue | 35 +- .../addPanel/ColorBySeriesPopUp.vue | 5 +- .../dashboards/addPanel/ConfigPanel.vue | 14 +- .../dashboards/addPanel/CustomChartEditor.vue | 3 +- .../addPanel/DashboardGeoMapsQueryBuilder.vue | 3 +- .../addPanel/DashboardQueryEditor.vue | 19 +- .../dashboards/addPanel/Drilldown.vue | 3 +- .../dashboards/addPanel/DrilldownPopUp.vue | 26 +- .../dashboards/addPanel/MarkLineConfig.vue | 3 +- .../addPanel/QueryTypeSelector.spec.ts | 3 +- .../dashboards/addPanel/SortByBtnGrp.vue | 3 +- .../dashboards/addPanel/ValueMapping.vue | 3 +- .../dashboards/addPanel/ValueMappingPopUp.vue | 4 +- .../dashboards/settings/SinglePanelMove.vue | 4 +- .../dashboards/settings/TabsDeletePopUp.vue | 3 +- .../dashboards/settings/TabsSettings.vue | 12 +- .../settings/VariablesDependenciesGraph.vue | 4 +- .../components/dashboards/tabs/TabList.vue | 3 +- .../dashboards/viewPanel/ViewPanel.vue | 8 +- .../flow/forms/DestinationPicker.spec.ts | 9 +- .../flow/forms/DestinationPicker.vue | 22 +- .../flow/forms/FunctionPicker.spec.ts | 18 +- .../components/flow/forms/FunctionPicker.vue | 31 +- .../functions/AddEnrichmentTable.spec.ts | 31 +- .../components/functions/AddFunction.spec.js | 15 +- .../components/functions/AddFunction.spec.ts | 15 +- web/src/components/functions/AddFunction.vue | 19 +- .../AssociatedStreamFunction.spec.ts | 38 +- .../functions/AssociatedStreamFunction.vue | 29 +- .../functions/EnrichmentTableList.spec.js | 32 +- .../functions/EnrichmentTableList.spec.ts | 26 +- .../functions/EnrichmentTableList.vue | 99 ++-- .../components/functions/FunctionList.spec.ts | 39 +- web/src/components/functions/FunctionList.vue | 196 ++++---- .../components/functions/FunctionsToolbar.vue | 3 +- .../components/functions/TestFunction.spec.js | 50 +- .../components/functions/TestFunction.spec.ts | 26 +- .../components/iam/IngestionTokens.spec.ts | 17 +- web/src/components/iam/IngestionTokens.vue | 141 +++--- web/src/components/iam/McpServer.vue | 32 +- web/src/components/iam/SyntheticsTokens.vue | 113 +++-- .../components/iam/groups/AddGroup.spec.ts | 9 +- web/src/components/iam/groups/AddGroup.vue | 13 +- .../components/iam/groups/AppGroups.spec.ts | 48 +- web/src/components/iam/groups/AppGroups.vue | 117 +++-- .../components/iam/groups/EditGroup.spec.ts | 11 +- web/src/components/iam/groups/EditGroup.vue | 18 +- .../iam/groups/GroupRoles.debug.spec.ts | 9 +- .../components/iam/groups/GroupRoles.spec.ts | 25 +- web/src/components/iam/groups/GroupRoles.vue | 43 +- .../iam/groups/GroupServiceAccounts.vue | 3 +- web/src/components/iam/groups/GroupUsers.vue | 3 +- .../AddUpdateOrganization.spec.ts | 27 +- .../iam/organizations/AppOrganizations.vue | 3 +- .../organizations/ListOrganizations.spec.js | 22 +- .../organizations/OrgCleanupTasksDialog.vue | 76 ++- web/src/components/iam/quota/Quota.spec.js | 34 +- web/src/components/iam/quota/Quota.vue | 20 +- web/src/components/iam/roles/AddRole.spec.ts | 11 +- web/src/components/iam/roles/AddRole.vue | 13 +- web/src/components/iam/roles/AppRoles.spec.ts | 20 +- web/src/components/iam/roles/AppRoles.vue | 147 ++++-- web/src/components/iam/roles/EditRole.spec.js | 199 +++++--- web/src/components/iam/roles/EditRole.vue | 47 +- .../iam/roles/readonlyPreset.spec.ts | 12 +- .../components/iam/roles/readonlyPreset.ts | 8 +- .../serviceAccounts/AddServiceAccount.spec.ts | 42 +- .../iam/serviceAccounts/AddServiceAccount.vue | 42 +- .../ServiceAccountsList.spec.js | 22 +- .../serviceAccounts/ServiceAccountsList.vue | 148 +++--- web/src/components/iam/users/AddUser.spec.ts | 19 +- .../iam/users/InvitationList.spec.ts | 38 +- .../iam/users/MemberInvitation.spec.ts | 43 +- .../components/iam/users/UpdateRole.spec.ts | 13 +- web/src/components/iam/users/User.spec.js | 36 +- web/src/components/iam/users/User.spec.ts | 26 +- web/src/components/iam/users/User.vue | 255 +++++----- .../recommended/AWSIntegrationTile.spec.ts | 21 +- .../recommended/AWSIntegrationTile.vue | 15 +- .../setupCard/useStreamDetect.spec.ts | 14 +- web/src/components/login/GetStarted.spec.ts | 13 +- web/src/components/login/Login.spec.ts | 17 +- web/src/components/login/Login.vue | 3 +- .../components/logstream/AddStream.spec.ts | 15 +- web/src/components/logstream/AddStream.vue | 20 +- .../logstream/AssociatedRegexPatterns.spec.ts | 15 +- web/src/components/logstream/Schema.spec.ts | 23 +- web/src/components/logstream/schema.vue | 37 +- .../pipeline/ImportPipeline.spec.ts | 43 +- .../components/pipeline/ImportPipeline.vue | 32 +- .../pipeline/ImportPipelineOrOperator.spec.ts | 43 +- .../pipeline/NodeForm/Condition.spec.js | 5 +- .../NodeForm/ConditionOrOperator.spec.js | 13 +- .../NodeForm/CreateDestinationForm.spec.ts | 15 +- .../NodeForm/CreateDestinationForm.vue | 29 +- .../pipeline/NodeForm/PreviewPromqlQuery.vue | 4 +- .../pipeline/NodeForm/Query.spec.js | 13 +- .../pipeline/NodeForm/Query.spec.ts | 9 +- .../NodeForm/ScheduledPipeline.spec.js | 13 +- .../pipeline/NodeForm/ScheduledPipeline.vue | 4 +- .../pipeline/PipelineEditor.spec.js | 30 +- .../components/pipeline/PipelineEditor.vue | 18 +- web/src/components/pipeline/PipelineView.vue | 3 +- .../components/pipeline/PipelinesList.spec.ts | 31 +- web/src/components/pipeline/PipelinesList.vue | 222 +++++---- .../pipelines/PipelineHistory.spec.ts | 51 +- .../components/pipelines/PipelineHistory.vue | 44 +- .../promql/components/MetricSelector.spec.ts | 13 +- .../components/queries/RunningQueries.spec.ts | 15 +- web/src/components/queries/RunningQueries.vue | 3 +- .../components/reports/CreateReport.spec.ts | 38 +- web/src/components/reports/CreateReport.vue | 27 +- web/src/components/reports/ReportList.spec.js | 28 +- web/src/components/reports/ReportList.spec.ts | 19 +- web/src/components/reports/ReportList.vue | 238 ++++++---- .../components/rum/PlayerTracesTab.spec.ts | 13 +- .../rum/performance/ApiDashboard.spec.ts | 13 +- .../rum/performance/ErrorsDashboard.spec.ts | 9 +- .../rum/performance/WebVitalsDashboard.vue | 3 +- .../components/settings/AddRegexPattern.vue | 3 +- web/src/components/settings/AiToolsets.vue | 143 +++--- .../settings/BuiltInPatternsTab.spec.ts | 32 +- .../settings/BuiltInPatternsTab.vue | 120 ++--- .../components/settings/CipherKeys.spec.ts | 19 +- web/src/components/settings/CipherKeys.vue | 165 ++++--- .../DomainManagement.integration.spec.ts | 39 +- .../settings/DomainManagement.spec.ts | 39 +- .../components/settings/DomainManagement.vue | 13 +- .../settings/GenAiAgentMappingSettings.vue | 13 +- web/src/components/settings/General.spec.ts | 45 +- web/src/components/settings/General.vue | 100 ++-- .../settings/GeneralDangerZone.spec.ts | 36 +- .../settings/ImportRegexPattern.spec.ts | 13 +- .../settings/ImportRegexPattern.vue | 4 +- .../ImportSyntheticsLocations.spec.ts | 22 +- web/src/components/settings/License.spec.ts | 15 +- web/src/components/settings/License.vue | 5 +- .../settings/LlmProvidersSettings.vue | 75 +-- .../settings/ModelPricingEditor.spec.ts | 19 +- .../settings/ModelPricingEditor.vue | 9 +- .../settings/ModelPricingList.spec.ts | 27 +- .../components/settings/ModelPricingList.vue | 95 ++-- web/src/components/settings/Nodes.spec.ts | 13 +- web/src/components/settings/Nodes.vue | 163 +++++-- .../settings/OrganizationManagement.spec.ts | 25 +- .../settings/OrganizationSettings.spec.ts | 13 +- .../settings/OrganizationSettings.vue | 11 +- .../settings/RegexPatternList.spec.ts | 15 +- .../components/settings/RegexPatternList.vue | 149 +++--- .../settings/SyntheticsLocationForm.spec.ts | 20 +- .../settings/SyntheticsLocationsList.spec.ts | 22 +- .../settings/TestModelMatchDialog.spec.ts | 13 +- web/src/components/slos/SloAlertForm.vue | 10 +- .../results/ProtocolRunSummary.spec.ts | 13 +- .../results/RunRowExpansion.spec.ts | 17 +- .../workflows/WorkflowEditor.spec.ts | 26 +- .../WorkflowLinkAlertsDialog.spec.ts | 17 +- .../workflows/WorkflowLinkAlertsDialog.vue | 6 +- .../workflows/WorkflowNodeDrawer.spec.ts | 3 +- .../workflows/WorkflowRunSwitcher.vue | 5 +- .../components/workflows/WorkflowRuns.spec.ts | 12 +- web/src/components/workflows/WorkflowRuns.vue | 7 +- .../workflows/WorkflowRunsPanel.spec.ts | 18 +- .../workflows/WorkflowRunsPanel.vue | 19 +- .../workflows/WorkflowTestDialog.spec.ts | 17 +- .../components/workflows/WorkflowView.spec.ts | 3 +- .../workflows/WorkflowsList.spec.ts | 90 +++- .../components/workflows/WorkflowsList.vue | 102 ++-- .../alerts/useDependencyGraph.spec.ts | 60 ++- .../composables/alerts/useDependencyGraph.ts | 75 ++- .../composables/dashboard/panel.querykeys.ts | 34 ++ .../dashboard/useAnnotations.spec.ts | 17 +- .../composables/dashboard/useAnnotations.ts | 20 +- .../dashboard/useCancelQuery.spec.ts | 13 +- .../useDashboardPanel.filterValues.spec.ts | 5 +- .../useDashboardPanel.promqlLabels.spec.ts | 8 +- .../dashboard/useDashboardPanel.ts | 11 +- .../dashboard/usePanelCache.spec.ts | 149 +++--- .../composables/dashboard/usePanelCache.ts | 346 +++++++++----- .../dashboard/usePanelDataLoader.spec.ts | 49 +- .../dashboard/usePanelDataLoader.ts | 63 ++- .../dashboard/usePanelDrilldown.spec.ts | 13 +- .../dashboard/usePanelDrilldown.ts | 8 +- .../dashboard/usePanelFields.spec.ts | 3 +- .../dashboard/useValuesWebSocket.spec.ts | 13 +- web/src/composables/fieldValueDB.ts | 97 +++- .../fieldValueStore.serverFetch.spec.ts | 9 +- web/src/composables/fieldValueStore.ts | 9 + .../composables/iam/usePermissions.spec.ts | 26 +- .../metrics/useMetricsExplorerGrid.spec.ts | 5 +- .../metrics/useMetricsExplorerGrid.ts | 10 +- web/src/composables/query/cachePolicy.ts | 43 ++ web/src/composables/query/fetchInto.spec.ts | 127 +++++ web/src/composables/query/fetchInto.ts | 79 ++++ web/src/composables/query/idbStorage.ts | 392 ++++++++++++++++ web/src/composables/query/index.ts | 37 ++ web/src/composables/query/keys.ts | 43 ++ web/src/composables/query/panelKey.ts | 90 ++++ web/src/composables/query/persisters.ts | 113 +++++ web/src/composables/query/queryClient.ts | 199 ++++++++ web/src/composables/query/useOrgId.ts | 23 + web/src/composables/query/useServerTable.ts | 152 ++++++ .../rum/useErrorIssuesData.spec.ts | 13 +- .../composables/rum/useRumSpanBuilder.spec.ts | 13 +- .../rum/useTraceCorrelation.spec.ts | 13 +- web/src/composables/shared/router.spec.ts | 1 + web/src/composables/shared/router.ts | 21 +- web/src/composables/useAlertForm.ts | 49 +- web/src/composables/useChatHistory.spec.ts | 9 + web/src/composables/useChatHistory.ts | 28 +- web/src/composables/useDashboardPanel.spec.ts | 10 +- .../composables/useFavoriteDashboards.spec.ts | 18 +- web/src/composables/useFavoriteDashboards.ts | 33 +- web/src/composables/useFunctions.spec.ts | 13 +- web/src/composables/useFunctions.ts | 28 +- web/src/composables/useHomeDashboard.spec.ts | 41 +- web/src/composables/useHomeDashboard.ts | 48 +- .../composables/useHostMetricsDashboard.ts | 4 + .../useLatencyInsightsAnalysis.spec.ts | 9 +- web/src/composables/useLogs.spec.ts | 13 +- .../composables/useLogs/useSearchBar.spec.ts | 36 +- web/src/composables/useLogs/useSearchBar.ts | 28 +- web/src/composables/useMcpCredential.ts | 9 + .../composables/useMetricsExplorer.spec.ts | 9 +- web/src/composables/usePipelines.spec.ts | 10 +- web/src/composables/usePipelines.ts | 18 +- .../usePrebuiltDestinations.spec.ts | 48 +- .../composables/usePrebuiltDestinations.ts | 24 +- .../composables/usePromqlSuggestions.spec.ts | 13 +- ...usePromqlSuggestions.streamSources.spec.ts | 20 +- web/src/composables/usePromqlSuggestions.ts | 31 +- web/src/composables/useStreams.spec.ts | 116 +++-- web/src/composables/useStreams.ts | 222 +++++---- .../useSuggestions.serverCatalog.spec.ts | 9 +- web/src/composables/useSuggestions.spec.ts | 9 +- web/src/composables/useSuggestions.ts | 15 +- .../composables/useSyntheticEvidence.spec.ts | 9 +- web/src/composables/useSyntheticsRecorder.ts | 3 +- .../AIObservability/AddToDatasetDrawer.vue | 22 +- .../AIObservability/ExperimentBrowser.vue | 21 +- .../ExperimentComparePickerDialog.vue | 4 +- .../AIObservability/ExperimentForm.vue | 23 +- .../AIObservability/RemoteTaskFormPage.vue | 40 +- .../RemoteTaskSigningPanel.vue | 31 +- web/src/enterprise/components/OnlineEvals.vue | 114 +++-- .../components/billings/Billing.spec.ts | 13 +- .../components/billings/BillingGroup.spec.ts | 23 +- .../components/billings/TrialPeriod.spec.ts | 13 +- .../components/billings/invoiceTable.spec.ts | 17 +- .../components/billings/invoiceTable.vue | 4 +- .../components/billings/plans.spec.ts | 21 +- .../components/billings/usage.spec.ts | 42 +- .../enterprise/components/billings/usage.vue | 13 +- .../components/onlineEvals/EvalJobList.vue | 19 +- .../ManualEvaluationDialog.spec.ts | 17 +- .../onlineEvals/ManualEvaluationDialog.vue | 4 +- .../onlineEvals/QualityPage.spec.ts | 151 ++++++ .../components/onlineEvals/QualityPage.vue | 12 +- .../onlineEvals/ScoreConfigList.vue | 23 +- .../components/onlineEvals/ScorerLibrary.vue | 12 +- .../components/onlineEvals/ScorerList.vue | 19 +- .../composables/useOnlineEvalsData.spec.ts | 26 +- .../composables/useOnlineEvalsData.ts | 65 ++- .../onlineEvals/forms/EvalJobDetail.vue | 18 +- .../onlineEvals/forms/JobFormPage.spec.ts | 19 +- .../forms/ProviderFormPage.spec.ts | 20 +- .../onlineEvals/forms/ProviderFormPage.vue | 21 +- .../forms/ScoreConfigDialog.spec.ts | 17 +- .../onlineEvals/forms/ScorerDetail.vue | 18 +- .../onlineEvals/forms/ScorerFormPage.spec.ts | 21 +- .../enterprise/composables/useAgentScope.ts | 5 +- .../views/AIObservability/AgentGraphPage.vue | 8 +- .../AIObservability/DatasetDetailPage.vue | 63 +-- .../views/AIObservability/DatasetsPage.vue | 58 ++- .../AIObservability/ExperimentComparePage.vue | 4 +- .../ExperimentDetailPage.spec.ts | 11 + .../AIObservability/ExperimentDetailPage.vue | 35 +- .../views/AIObservability/ExperimentsPage.vue | 19 +- .../AIObservability/QueueWorkbenchPage.vue | 58 ++- .../views/AIObservability/QueuesPage.vue | 54 ++- .../AIObservability/RemoteTaskDetailPage.vue | 30 +- .../views/AIObservability/RemoteTasksPage.vue | 44 +- web/src/layouts/MainLayout.vue | 42 +- .../core/RefreshButton/ORefreshButton.spec.ts | 68 ++- .../RefreshButton/ORefreshButton.types.ts | 6 + .../lib/core/RefreshButton/ORefreshButton.vue | 29 +- web/src/lib/core/Splitter/OSplitter.vue | 3 +- web/src/lib/feedback/Toast/OToastProvider.vue | 3 +- web/src/main.ts | 17 +- .../TelemetryCorrelationDashboard.spec.ts | 28 +- web/src/plugins/logs/Index.spec.ts | 3 +- web/src/plugins/logs/IndexList.spec.ts | 31 +- web/src/plugins/logs/JsonPreview.spec.ts | 13 +- web/src/plugins/logs/SearchBar.spec.ts | 78 ++-- web/src/plugins/logs/SearchBar.vue | 51 +- web/src/plugins/logs/SearchHistory.spec.js | 21 +- web/src/plugins/logs/SearchHistory.vue | 3 +- .../plugins/logs/SearchJobInspector.spec.ts | 13 +- .../plugins/logs/SearchSchedulersList.spec.js | 19 +- .../plugins/logs/SearchSchedulersList.spec.ts | 19 +- web/src/plugins/logs/SearchSchedulersList.vue | 11 +- web/src/plugins/logs/VisualizeLogsQuery.vue | 13 +- web/src/plugins/metrics/AddToDashboard.vue | 3 +- web/src/plugins/metrics/Index.vue | 2 +- .../metrics/explorer/MetricsVisualize.vue | 3 +- web/src/plugins/traces/Index.spec.ts | 39 +- web/src/plugins/traces/IndexList.spec.ts | 15 +- .../traces/LLMInsightsDashboard.spec.ts | 24 +- .../plugins/traces/LLMInsightsDashboard.vue | 39 +- web/src/plugins/traces/ServiceGraph.spec.ts | 39 +- .../traces/ServiceGraphNodeSidePanel.spec.ts | 13 +- .../traces/ServiceGraphNodeSidePanel.vue | 15 +- .../traces/ServiceGraphSidePanel.spec.ts | 13 +- .../plugins/traces/ServicesCatalog.spec.ts | 26 +- web/src/plugins/traces/ServicesCatalog.vue | 21 +- web/src/plugins/traces/SessionsList.vue | 28 +- web/src/plugins/traces/TraceDAG.spec.ts | 5 +- web/src/plugins/traces/TraceDAG.vue | 12 +- web/src/plugins/traces/TraceDetails.vue | 8 +- .../plugins/traces/TraceDetailsSidebar.vue | 17 +- .../traces/composables/useLLMInsights.ts | 3 +- .../composables/useLLMStreamQuery.spec.ts | 9 +- .../plugins/traces/llmInsightsCache.spec.ts | 34 +- web/src/plugins/traces/llmInsightsCache.ts | 20 +- .../plugins/workflows/WorkflowNode.spec.ts | 9 +- .../workflows/nodes/WorkflowCondition.spec.ts | 5 +- .../nodes/WorkflowConfigSummary.spec.ts | 21 + .../workflows/nodes/WorkflowConfigSummary.vue | 17 +- .../nodes/WorkflowDestination.spec.ts | 5 +- .../workflows/nodes/WorkflowFunction.spec.ts | 5 +- .../workflows/nodes/WorkflowTrigger.spec.ts | 5 +- .../workflows/useWorkflowCanvas.spec.ts | 58 ++- .../plugins/workflows/useWorkflowCanvas.ts | 32 +- .../workflows/useWorkflowDestinations.ts | 22 +- web/src/services/ai_toolsets.queries.ts | 27 ++ web/src/services/ai_toolsets.querykeys.ts | 28 ++ web/src/services/alert_destination.queries.ts | 69 +++ .../services/alert_destination.querykeys.ts | 32 ++ web/src/services/alert_sources.queries.ts | 26 ++ web/src/services/alert_sources.querykeys.ts | 28 ++ web/src/services/alert_templates.queries.ts | 52 +++ web/src/services/alert_templates.querykeys.ts | 28 ++ web/src/services/alerts.queries.spec.ts | 63 +++ web/src/services/alerts.queries.ts | 148 ++++++ web/src/services/alerts.querykeys.ts | 48 ++ web/src/services/alerts.ts | 16 + web/src/services/anomaly_detection.queries.ts | 56 +++ .../services/anomaly_detection.querykeys.ts | 29 ++ web/src/services/api_keys.queries.ts | 42 ++ web/src/services/api_keys.querykeys.ts | 28 ++ web/src/services/cipher_keys.queries.ts | 26 ++ web/src/services/cipher_keys.querykeys.ts | 28 ++ web/src/services/common.queries.ts | 47 ++ web/src/services/common.querykeys.ts | 33 ++ web/src/services/common.ts | 7 + web/src/services/config.queries.ts | 55 +++ web/src/services/config.querykeys.ts | 34 ++ .../dashboard_annotations.queries.spec.ts | 70 +++ .../services/dashboard_annotations.queries.ts | 55 +++ .../dashboard_annotations.querykeys.ts | 31 ++ web/src/services/dashboards.queries.ts | 53 +++ web/src/services/dashboards.querykeys.ts | 28 ++ .../services/gen-ai-agent-mapping.queries.ts | 49 ++ .../gen-ai-agent-mapping.querykeys.ts | 24 + web/src/services/iam.queries.spec.ts | 80 ++++ web/src/services/iam.queries.ts | 118 +++++ web/src/services/iam.querykeys.ts | 32 ++ web/src/services/incidents.queries.ts | 44 ++ web/src/services/incidents.querykeys.ts | 29 ++ web/src/services/jstransform.queries.ts | 79 ++++ web/src/services/jstransform.querykeys.ts | 32 ++ web/src/services/license_server.queries.ts | 26 ++ web/src/services/license_server.querykeys.ts | 28 ++ .../services/llm-datasets.service.queries.ts | 64 +++ .../llm-datasets.service.querykeys.ts | 28 ++ web/src/services/llm-experiments.queries.ts | 136 ++++++ web/src/services/llm-experiments.querykeys.ts | 29 ++ .../services/llm-queues.service.queries.ts | 47 ++ .../services/llm-queues.service.querykeys.ts | 28 ++ web/src/services/model_pricing.queries.ts | 38 ++ web/src/services/model_pricing.querykeys.ts | 28 ++ .../services/online-evals.service.queries.ts | 89 ++++ .../online-evals.service.querykeys.ts | 31 ++ web/src/services/online-evals.service.spec.ts | 3 +- web/src/services/organizations.queries.ts | 101 ++++ web/src/services/organizations.querykeys.ts | 33 ++ web/src/services/pipelines.queries.spec.ts | 53 +++ web/src/services/pipelines.queries.ts | 53 +++ web/src/services/pipelines.querykeys.ts | 30 ++ web/src/services/pipelines.ts | 5 + web/src/services/query_functions.queries.ts | 37 ++ web/src/services/query_functions.querykeys.ts | 30 ++ web/src/services/regex_pattern.queries.ts | 34 ++ web/src/services/regex_pattern.querykeys.ts | 30 ++ web/src/services/reports.queries.ts | 52 +++ web/src/services/reports.querykeys.ts | 37 ++ web/src/services/reports.ts | 7 + web/src/services/saved_views.queries.ts | 47 ++ web/src/services/saved_views.querykeys.ts | 28 ++ web/src/services/search.queries.ts | 35 ++ web/src/services/search.querykeys.ts | 29 ++ web/src/services/service_accounts.queries.ts | 39 ++ .../services/service_accounts.querykeys.ts | 28 ++ web/src/services/service_graph.queries.ts | 27 ++ web/src/services/service_graph.querykeys.ts | 37 ++ web/src/services/service_graph.ts | 17 + web/src/services/service_streams.queries.ts | 38 ++ web/src/services/service_streams.querykeys.ts | 27 ++ web/src/services/settings.queries.ts | 36 ++ web/src/services/settings.querykeys.ts | 29 ++ web/src/services/slos.queries.ts | 57 +++ web/src/services/slos.querykeys.ts | 28 ++ web/src/services/stream.queries.ts | 80 ++++ web/src/services/stream.querykeys.ts | 36 ++ web/src/services/stream.ts | 8 + web/src/services/synthetics.queries.spec.ts | 57 +++ web/src/services/synthetics.queries.ts | 73 +++ web/src/services/synthetics.querykeys.ts | 33 ++ web/src/services/users.queries.ts | 42 ++ web/src/services/users.querykeys.ts | 32 ++ web/src/services/workflows.queries.ts | 72 +++ web/src/services/workflows.querykeys.ts | 33 ++ web/src/stores/index.ts | 11 +- web/src/test/unit/HomeView.spec.ts | 23 +- web/src/test/unit/Nodes.spec.ts | 13 +- .../pipelines/PipelineHistory.spec.ts | 31 +- web/src/test/unit/helpers/mockService.ts | 76 +++ web/src/test/unit/helpers/queryClient.ts | 50 ++ web/src/test/unit/helpers/setupTests.ts | 29 ++ web/src/test/unit/helpers/store.ts | 2 +- web/src/utils/auth.spec.ts | 28 +- web/src/utils/auth.ts | 9 +- web/src/utils/buildVersionChecker.ts | 12 +- web/src/utils/common.spec.ts | 5 +- web/src/utils/commons.spec.ts | 79 ++-- web/src/utils/commons.ts | 203 +++++--- web/src/utils/identityConfig.ts | 107 ++--- web/src/utils/modelPricingCache.ts | 142 ------ web/src/utils/regexPatternCache.spec.ts | 360 -------------- web/src/utils/regexPatternCache.ts | 142 ------ web/src/utils/semanticGroupsCache.ts | 105 ++--- web/src/views/About.spec.ts | 41 +- web/src/views/AddAlertView.spec.ts | 33 +- web/src/views/AddAlertView.vue | 43 +- web/src/views/AppAlerts.spec.ts | 63 +-- web/src/views/AppAlerts.vue | 12 +- web/src/views/AwsMarketplaceSetup.spec.ts | 15 +- web/src/views/AzureMarketplaceSetup.spec.ts | 15 +- .../views/Dashboards/DashboardJsonEditor.vue | 3 +- web/src/views/Dashboards/Dashboards.spec.ts | 36 +- web/src/views/Dashboards/Dashboards.vue | 114 +++-- .../views/Dashboards/ImportDashboard.spec.ts | 13 +- web/src/views/Dashboards/ImportDashboard.vue | 13 +- .../Dashboards/RenderDashboardCharts.vue | 4 +- web/src/views/Dashboards/ViewDashboard.vue | 18 +- .../views/Dashboards/addPanel/AddPanel.vue | 3 +- .../views/Dashboards/viewDashboard.spec.ts | 69 +-- web/src/views/Ingestion.spec.ts | 112 +++-- web/src/views/Ingestion.vue | 87 ++-- web/src/views/LogStream.spec.ts | 118 +++-- web/src/views/LogStream.vue | 223 ++++++--- web/src/views/Login.spec.ts | 96 ++-- web/src/views/Login.vue | 27 +- web/src/views/MemberSubscription.spec.ts | 13 +- web/src/views/OverviewTab.spec.ts | 36 +- web/src/views/OverviewTab.vue | 126 +++-- web/src/views/RUM/AppSessions.spec.ts | 53 ++- web/src/views/RUM/ErrorViewer.spec.ts | 13 +- web/src/views/RUM/SessionViewer.spec.ts | 17 +- web/src/views/StreamExplorer.spec.ts | 10 +- web/src/views/SyntheticMonitoring.spec.ts | 31 +- web/src/views/SyntheticMonitoring.vue | 110 +++-- web/src/views/UsageTab.spec.ts | 13 +- web/src/views/UsageTab.vue | 26 +- web/src/views/alerts/AlertDetail.spec.ts | 23 +- web/src/views/slos/AddSlo.vue | 17 +- web/src/views/slos/SloList.vue | 122 +++-- web/src/views/slos/SloListDelete.spec.ts | 32 +- .../synthetics/CreateBrowserTest.spec.ts | 48 +- .../views/synthetics/CreateBrowserTest.vue | 51 +- web/src/views/synthetics/CreateCheck.spec.ts | 36 +- .../synthetics/CreateProtocolCheck.spec.ts | 49 +- .../views/synthetics/CreateProtocolCheck.vue | 48 +- .../views/synthetics/MonitorResults.spec.ts | 15 +- web/src/views/synthetics/MonitorResults.vue | 11 +- web/src/views/synthetics/MonitorRuns.spec.ts | 20 +- .../synthetics/PrivateLocationDetail.spec.ts | 15 +- web/src/views/synthetics/RunDetail.spec.ts | 32 +- 579 files changed, 16517 insertions(+), 6876 deletions(-) create mode 100644 .claude/skills/ui-architect/references/data-fetching.md create mode 100644 web/src/components/alerts/AlertDestinationsField.spec.ts create mode 100644 web/src/composables/dashboard/panel.querykeys.ts create mode 100644 web/src/composables/query/cachePolicy.ts create mode 100644 web/src/composables/query/fetchInto.spec.ts create mode 100644 web/src/composables/query/fetchInto.ts create mode 100644 web/src/composables/query/idbStorage.ts create mode 100644 web/src/composables/query/index.ts create mode 100644 web/src/composables/query/keys.ts create mode 100644 web/src/composables/query/panelKey.ts create mode 100644 web/src/composables/query/persisters.ts create mode 100644 web/src/composables/query/queryClient.ts create mode 100644 web/src/composables/query/useOrgId.ts create mode 100644 web/src/composables/query/useServerTable.ts create mode 100644 web/src/enterprise/components/onlineEvals/QualityPage.spec.ts create mode 100644 web/src/services/ai_toolsets.queries.ts create mode 100644 web/src/services/ai_toolsets.querykeys.ts create mode 100644 web/src/services/alert_destination.queries.ts create mode 100644 web/src/services/alert_destination.querykeys.ts create mode 100644 web/src/services/alert_sources.queries.ts create mode 100644 web/src/services/alert_sources.querykeys.ts create mode 100644 web/src/services/alert_templates.queries.ts create mode 100644 web/src/services/alert_templates.querykeys.ts create mode 100644 web/src/services/alerts.queries.spec.ts create mode 100644 web/src/services/alerts.queries.ts create mode 100644 web/src/services/alerts.querykeys.ts create mode 100644 web/src/services/anomaly_detection.queries.ts create mode 100644 web/src/services/anomaly_detection.querykeys.ts create mode 100644 web/src/services/api_keys.queries.ts create mode 100644 web/src/services/api_keys.querykeys.ts create mode 100644 web/src/services/cipher_keys.queries.ts create mode 100644 web/src/services/cipher_keys.querykeys.ts create mode 100644 web/src/services/common.queries.ts create mode 100644 web/src/services/common.querykeys.ts create mode 100644 web/src/services/config.queries.ts create mode 100644 web/src/services/config.querykeys.ts create mode 100644 web/src/services/dashboard_annotations.queries.spec.ts create mode 100644 web/src/services/dashboard_annotations.queries.ts create mode 100644 web/src/services/dashboard_annotations.querykeys.ts create mode 100644 web/src/services/dashboards.queries.ts create mode 100644 web/src/services/dashboards.querykeys.ts create mode 100644 web/src/services/gen-ai-agent-mapping.queries.ts create mode 100644 web/src/services/gen-ai-agent-mapping.querykeys.ts create mode 100644 web/src/services/iam.queries.spec.ts create mode 100644 web/src/services/iam.queries.ts create mode 100644 web/src/services/iam.querykeys.ts create mode 100644 web/src/services/incidents.queries.ts create mode 100644 web/src/services/incidents.querykeys.ts create mode 100644 web/src/services/jstransform.queries.ts create mode 100644 web/src/services/jstransform.querykeys.ts create mode 100644 web/src/services/license_server.queries.ts create mode 100644 web/src/services/license_server.querykeys.ts create mode 100644 web/src/services/llm-datasets.service.queries.ts create mode 100644 web/src/services/llm-datasets.service.querykeys.ts create mode 100644 web/src/services/llm-experiments.queries.ts create mode 100644 web/src/services/llm-experiments.querykeys.ts create mode 100644 web/src/services/llm-queues.service.queries.ts create mode 100644 web/src/services/llm-queues.service.querykeys.ts create mode 100644 web/src/services/model_pricing.queries.ts create mode 100644 web/src/services/model_pricing.querykeys.ts create mode 100644 web/src/services/online-evals.service.queries.ts create mode 100644 web/src/services/online-evals.service.querykeys.ts create mode 100644 web/src/services/organizations.queries.ts create mode 100644 web/src/services/organizations.querykeys.ts create mode 100644 web/src/services/pipelines.queries.spec.ts create mode 100644 web/src/services/pipelines.queries.ts create mode 100644 web/src/services/pipelines.querykeys.ts create mode 100644 web/src/services/query_functions.queries.ts create mode 100644 web/src/services/query_functions.querykeys.ts create mode 100644 web/src/services/regex_pattern.queries.ts create mode 100644 web/src/services/regex_pattern.querykeys.ts create mode 100644 web/src/services/reports.queries.ts create mode 100644 web/src/services/reports.querykeys.ts create mode 100644 web/src/services/saved_views.queries.ts create mode 100644 web/src/services/saved_views.querykeys.ts create mode 100644 web/src/services/search.queries.ts create mode 100644 web/src/services/search.querykeys.ts create mode 100644 web/src/services/service_accounts.queries.ts create mode 100644 web/src/services/service_accounts.querykeys.ts create mode 100644 web/src/services/service_graph.queries.ts create mode 100644 web/src/services/service_graph.querykeys.ts create mode 100644 web/src/services/service_streams.queries.ts create mode 100644 web/src/services/service_streams.querykeys.ts create mode 100644 web/src/services/settings.queries.ts create mode 100644 web/src/services/settings.querykeys.ts create mode 100644 web/src/services/slos.queries.ts create mode 100644 web/src/services/slos.querykeys.ts create mode 100644 web/src/services/stream.queries.ts create mode 100644 web/src/services/stream.querykeys.ts create mode 100644 web/src/services/synthetics.queries.spec.ts create mode 100644 web/src/services/synthetics.queries.ts create mode 100644 web/src/services/synthetics.querykeys.ts create mode 100644 web/src/services/users.queries.ts create mode 100644 web/src/services/users.querykeys.ts create mode 100644 web/src/services/workflows.queries.ts create mode 100644 web/src/services/workflows.querykeys.ts create mode 100644 web/src/test/unit/helpers/mockService.ts create mode 100644 web/src/test/unit/helpers/queryClient.ts delete mode 100644 web/src/utils/modelPricingCache.ts delete mode 100644 web/src/utils/regexPatternCache.spec.ts delete mode 100644 web/src/utils/regexPatternCache.ts diff --git a/.claude/skills/ui-architect/SKILL.md b/.claude/skills/ui-architect/SKILL.md index 9637d9cdd5..baaed00b05 100644 --- a/.claude/skills/ui-architect/SKILL.md +++ b/.claude/skills/ui-architect/SKILL.md @@ -36,7 +36,9 @@ description: >- side panels become drawers that open from their own row, row actions fold into a kebab, and popups fit the viewport. It also settles the recurring structural decisions: use OTable for any tabular data, follow the - view → service → Vuex/local-ref layering for fetching list data, choose the + TanStack Query layering for server data (a declared queryOptions() per read + on its module's staleTime tier, mutationOptions() writes that invalidate, + refresh buttons that force every read on the view, no Vuex copies), choose the right form container (ConfirmDialog vs ODialog vs ODrawer vs a full in-page view) by the weight of the interaction, and build every validated form with OForm + a colocated Zod schema (single-source-of-truth name-bound fields, no @@ -45,7 +47,7 @@ description: >- validate, or restyle any screen, component, header, table, list, dialog, drawer, form, field, or panel in the web frontend, asks to make any of them work on mobile / phone / tablet / small screens (responsive), or asks where a - form/table/fetch should live, how to validate a form, how to add a keyboard + form/table/fetch should live, how to cache or refresh server data, how to validate a form, how to add a keyboard shortcut, how to build a new reusable/common O2 component when nothing existing fits (create one in web/src/lib instead of assembling divs and classes), whether something belongs in a dialog or a drawer, or where a new page should be @@ -557,13 +559,13 @@ and each domain has its own reference below. | Decision | The rule | Detail | | --- | --- | --- | +| **Server data (fetch & cache)** | Every read is a declared `queryOptions()` in `services/.queries.ts` (reuse the existing one if the list is already declared), keyed with `orgKey`, on its **module's** `staleTime` tier from `cachePolicy.ts`; components `useQuery` it (rows as a `computed`) — never `http`/axios, never a Vuex copy of a server list. Writes are `mutationOptions()` with `meta.invalidates`. A user refresh forces **every** read on the view; mount, paging and search read the cache. | [data-fetching](references/data-fetching.md) | | **Tabular data** | `OTable` + `OTableColumnDef[]`; client-side pagination unless the backend paginates a set too large to fetch whole | [core-controls-table](references/core-controls-table.md) | | **Charts / graphs** | **Every data chart renders through the shared dashboard engine — never mount a charting lib in a feature page.** Time-series, category, scatter, geo/map, gauge, pie → **`PanelSchemaRenderer`** (`web/src/components/dashboards/PanelSchemaRenderer.vue`) with a panel schema: it runs the query, applies the app's unit/theme/annotation formatting, and owns the loading/error ladder. **Banned in feature code:** `echarts.init` / a raw `` / ApexCharts / D3 / Chart.js / a hand-rolled `` or `` plot. The low-level **`panels/ChartRenderer.vue`** (raw ECharts option) is the ONLY sanctioned escape hatch, and ONLY when you need chart-`@click` forwarding `PanelSchemaRenderer` doesn't re-emit — annotate the site with why, and convert once the schema renderer forwards clicks. **Not charts** (do NOT force these through the renderer): in-row trend lines are **`OSparkline`**, single-value share bars are **`OProgressBar`**, in-cell data bars are the table's **`ODataBarCell`**, and a decorative topology/diagram is bespoke SVG. | [core-display](references/core-display.md) | | **Whole-page layout** | **Every routed view is a `OPageLayout`.** It's the ONE page component — it owns the full-height column, the header (from `:title`/`:icon`/`:subtitle`/`:back` props + `#actions`/`#header-tabs`, the latter needing **`tabs-below`** to land in row 2 instead of inline), an optional `#subnav` strip, an optional `#sidebar` rail (fixed or `resizable`), and the body's inset. You plug in data; there's no place to hand-roll a padded `
`. Body is inset to the page-edge grid by default — pass **`bleed`** for a full-bleed body (an `OTable`, a chart, a `router-view` shell), or **`constrained`** for a centered reading column (forms). The `#header` slot is a rare escape hatch only. | [page-recipes](references/page-recipes.md) | | **Content inset** | `OPageLayout` already insets the body. Anywhere else (a panel, a dialog section, one tab's content) wrap it in **`OContent`** (bakes the one `px-page-edge` grid line, the primitive `OPageLayout` uses internally) instead of hand-picking `px-2`/`px-4`/`p-2.5`; pass `bleed` (or `bleed-x`/`bleed-y`) for full-bleed content that owns its own edge — same escape-hatch idea as `ODrawer`/`ODialog` `bleed`. Never hand-roll a content inset. | [conventions](references/conventions.md) | | **Tab strips** | an `OTabs` strip needs **no** horizontal wrapper padding — the first tab's label self-aligns to the `px-page-edge` grid, so it lines up with the `OContent` body below it. Put the strip's bottom divider on the strip (`border-b`) and give it no `px-*`; wrapping a tab strip in `px-page-edge` double-insets the labels. | [conventions](references/conventions.md) | | **Listing toolbar** | every list carries three affordances — search + filters (`#toolbar`), refresh (`#toolbar-trailing`), and the auto-injected column-visibility toggle; empty state is one `OEmptyState` with `:filtered`, in the `#empty` slot only, plus `:forbidden` on the table so a 403 shows "You don't have access" instead of "create your first…" | [page-recipes](references/page-recipes.md) | -| **Data fetching** | view → domain service (`src/services`, via the `http.ts` wrapper) → Vuex (shared/cached) or local `ref` (ephemeral); never call `http`/axios from a component | [conventions](references/conventions.md) | | **Form container** | confirm → `ConfirmDialog`; short form → `ODialog`; tall or contextual form → `ODrawer`; primary multi-section flow → a full in-page view. Use `ODialog` / `ODrawer` for these | [conventions](references/conventions.md) | | **Form validation** | `OForm` + a colocated Zod `
.schema.ts`; fields are `OForm*` bound **only by `name=`** (no `v-model`/`ref` mirror, no `formData`); submit + loading automatic; payload built with explicit keys; field arrays use `:key="index"` | [forms-validation](references/forms-validation.md) | | **New page in nav** | a route **+ exactly one** surface (rail item / flyout child / Settings / IAM sub-page) **+** an env/role gate — the route condition, the nav-entry gate, and the SectionRail `visible` all express the same rule | [navigation-menus](references/navigation-menus.md) | @@ -653,6 +655,15 @@ considering the UI done: (`navGroups.ts` / `linksList` / `settingsItems` / SectionRail). A module showing one glyph in the rail and another in its header reads as two places — see [navigation-menus](references/navigation-menus.md#icon-parity). +- [ ] Any data the page reads is a `queryOptions()` in + `services/.queries.ts`, consumed with `useQuery` — not a service + call with a hand-rolled `loading` ref. Durations come from + `cachePolicy.ts` (the module's tier), never a bare number; no `persister`, + no per-query `gcTime`. Writes are `mutationOptions()` declaring + `meta.invalidates`; a component never calls `invalidateQueries` itself. + Refresh (button, `r`, section icons, Retry) forces **every** read on the + view through a named handler. See + [references/data-fetching.md](references/data-fetching.md). - [ ] Every interactive control is an O2 component if one exists in `web/src/lib` — no bare HTML controls or third-party primitives with an O2 equivalent. - [ ] A self-contained/repeated UI element with no matching component was @@ -697,8 +708,9 @@ considering the UI done: (`config.isEnterprise` / `config.isCloud` / `zoConfig.*`), with the route, nav-entry gate, and SectionRail `visible` all in sync (see [navigation-menus.md](references/navigation-menus.md)). -- [ ] Data fetched through a domain service (`src/services`), not raw `http` in - the component; shared data in Vuex, ephemeral data in local refs. +- [ ] Server data reaches the component through a declared query (never raw + `http`), and no server list is copied into Vuex or a load-once map; + ephemeral view state stays in local refs. - [ ] Form container matches weight: confirm → `ConfirmDialog`, short form → `ODialog`, large/contextual form → `ODrawer`, primary multi-section flow → full page. diff --git a/.claude/skills/ui-architect/references/calm-signal.md b/.claude/skills/ui-architect/references/calm-signal.md index d48b17b180..4157e38486 100644 --- a/.claude/skills/ui-architect/references/calm-signal.md +++ b/.claude/skills/ui-architect/references/calm-signal.md @@ -14,15 +14,15 @@ Apply it in three steps. ## Step 1 — Name the screen's primary signal -Every screen answers one question. Colour *that*; keep the rest quiet. Which +Every screen answers one question. Colour _that_; keep the rest quiet. Which signal earns the colour changes by page type: -| Archetype | Examples | Primary signal → colour | -| --- | --- | --- | -| **Monitoring** | Alerts, Incidents, RUM errors, Synthetics | **state / severity / health** — active/paused/failed, firing, recency | -| **Catalog** | Dashboards, Streams, Functions, Pipelines | **organise & orient** — category/type, owner, freshness, size/usage, favourites | -| **Access** | Users, IAM, Roles, Service accounts | **role / permission** — privilege runs hot→cool, plus active/invited status | -| **Forms & settings** | creation flows, editors, org/stream settings | **progress & validity** — where am I, is it right, what still needs me | +| Archetype | Examples | Primary signal → colour | +| -------------------- | -------------------------------------------- | ------------------------------------------------------------------------------- | +| **Monitoring** | Alerts, Incidents, RUM errors, Synthetics | **state / severity / health** — active/paused/failed, firing, recency | +| **Catalog** | Dashboards, Streams, Functions, Pipelines | **organise & orient** — category/type, owner, freshness, size/usage, favourites | +| **Access** | Users, IAM, Roles, Service accounts | **role / permission** — privilege runs hot→cool, plus active/invited status | +| **Forms & settings** | creation flows, editors, org/stream settings | **progress & validity** — where am I, is it right, what still needs me | Don't bolt "status" onto a screen that has none. A Dashboards list has no firing/paused state — its signal is category + recency + ownership. @@ -61,27 +61,28 @@ All token-backed and dark-mode-safe. Reuse these before inventing anything. exception highlight** via `row-class`. The rail and the wash are two different strengths of the same signal, and the rail is the default: - | | Rail (`getRowStyle`) | Wash (`row-class`) | - | --- | --- | --- | - | Cost | a few px at the row edge | ~the whole row | - | Use for | **every** state, always | only the two cases below | + | | Rail (`getRowStyle`) | Wash (`row-class`) | + | ------- | ------------------------ | ------------------------ | + | Cost | a few px at the row edge | ~the whole row | + | Use for | **every** state, always | only the two cases below | A full-row wash is the loudest thing on a list, so it earns its place only when **all three** hold: the state means **act now** (not merely "not green"), it is **rare in a healthy system**, and **the row is the unit of action**. In practice that leaves exactly two washes: - - `!bg-status-error-bg` — failing/errored/offline. *Alerts* failed, *Pipelines* - errored, *Nodes* offline, *Synthetics* failed. + - `!bg-status-error-bg` — failing/errored/offline. _Alerts_ failed, _Pipelines_ + errored, _Nodes_ offline, _Synthetics_ failed. - `!bg-surface-panel` — paused/disabled. This one is **de-emphasis, not alarm**: the row is deliberately inert, so it recedes rather than shouts. Everything else keeps a clean row and reads from the rail — including states that are "bad but not urgent": **degraded/warning** (worth noticing, not worth acting on this second), **stale**, **unknown**, **never-ran**. And whatever the - state means, if it is **common** the wash is wrong regardless: *Streams* drops it + state means, if it is **common** the wash is wrong regardless: _Streams_ drops it entirely, because "never ingested" and "quiet for a day" describe a large share of rows in a normal org and a table where most rows are tinted signals nothing. Pages with no failure state at all — Users, Roles, Dashboards — never wash. + - **Recency** — `OTimeCell` `mode="relative"` (`"3 min ago"`) with a hot/warm/ cold dot, instead of a raw timestamp column. - **People** — `OUserCell` for owner/author columns. @@ -100,12 +101,12 @@ Once a strip is `selectable`, four rules keep every strip behaving identically - **Order attention-first, left → right.** The most critical / attention-worthy tile is leftmost, descending to the calm/inert ones — e.g. `failed → recent → - active → paused`, `P1 → P2 → P3 → P4`, `degraded → paused → active → draft → - archived`. This mirrors the row rail's "critical colour on the left edge": what +active → paused`, `P1 → P2 → P3 → P4`, `degraded → paused → active → draft → +archived`. This mirrors the row rail's "critical colour on the left edge": what needs attention lives on the left, everywhere. - **The "All" / "Total" tile is LAST, stays CLICKABLE, and is never highlighted.** It only clears the facet; it is never itself the active tile. Wire `:selected-key` - to the *raw filter value* (`null` / `"all"` when unfiltered) so nothing shows the + to the _raw filter value_ (`null` / `"all"` when unfiltered) so nothing shows the ring while viewing everything — never fall back to selecting "All" as the default. **Never put `selectable: false` on it** — that is the one wrong way to express "never highlighted": it turns the tile into a plain `
`, so clicking it does @@ -120,7 +121,7 @@ Once a strip is `selectable`, four rules keep every strip behaving identically see Step 3. - **A strip in `#subheader` is a CLAIM ABOUT THE ROWS — scope decides position.** Inside the table frame, a strip promises "these numbers describe the list below", - so it must be computed from the same filtered set *and* be facet-clickable. Before + so it must be computed from the same filtered set _and_ be facet-clickable. Before writing one, check the fetch: **if the list is server-paginated you only hold one page**, so page-local sums are not totals — never sum the visible page and label it "Total". When the numbers genuinely can't meet the promise (server pagination, @@ -135,8 +136,7 @@ Once a strip is `selectable`, four rules keep every strip behaving identically - **`ODataBarCell` needs the whole set — client-paginated tables only.** Its bar is a share of the `max` the caller computes over the rendered rows, so on a **server-paginated** table it silently means "biggest on this page": the scale - changes as you page, and the same stream draws a different bar on page 1 and page - 4. On such a table drop the bars and let the (sortable) numbers rank the rows — + changes as you page, and the same stream draws a different bar on page 1 and page 4. On such a table drop the bars and let the (sortable) numbers rank the rows — right-aligned + `tabular-nums` already scans fine, and the stray part-width underlines read as artefacts rather than data. Same test as the strip: can this mark be computed from data you actually hold? @@ -153,7 +153,7 @@ strip feeds a drill-down. ### Before you colour a state, check how it CLEARS -A State column is a claim about *now*. Read the write path of whatever field backs +A State column is a claim about _now_. Read the write path of whatever field backs it and confirm something resets it — an expiry/retention job, a success write, a status transition. A sticky error field (written on failure, never cleared) pins a row to "Errored" forever and the column becomes noise within a week. Pipelines' @@ -182,20 +182,20 @@ Colour only earns attention if most of the screen stays quiet: each tile to three questions: does the number **vary**, does someone **act** on it, and is it **not already on screen**? Tiles that fail are noise dressed as signal: - - *structurally constant* — "System accounts" is 1 in almost every org, so the + - _structurally constant_ — "System accounts" is 1 in almost every org, so the tile is a label with a number stuck to it; - - *almost always zero* — "New this week" on a list that gains an item a quarter; - - *derivable from its neighbours* — "In use" beside "Unused" and "Total"; - - *already in the footer* — `footerTitle` renders "N Dashboards" under every + - _almost always zero_ — "New this week" on a list that gains an item a quarter; + - _derivable from its neighbours_ — "In use" beside "Unused" and "Total"; + - _already in the footer_ — `footerTitle` renders "N Dashboards" under every table, so a Total tile alone is not a reason to have a strip. - A page whose only candidates fail these gets **no strip** — keep the per-row - signals (relative recency, a state rail, a count column) and stop. Dashboards, - Service Accounts and Roles all ended up here: pages where a strip added pixels - and no information. Roles is the clearest case — "Unused" is just the member - column sorted ascending, so two tiles restated what the rows already said. **A - count column plus sorting usually beats a strip**; reach for a strip only when - the page has a real distribution to summarise (Users across roles, monitors - across health) *and* the tiles double as the facet. + A page whose only candidates fail these gets **no strip** — keep the per-row + signals (relative recency, a state rail, a count column) and stop. Dashboards, + Service Accounts and Roles all ended up here: pages where a strip added pixels + and no information. Roles is the clearest case — "Unused" is just the member + column sorted ascending, so two tiles restated what the rows already said. **A + count column plus sorting usually beats a strip**; reach for a strip only when + the page has a real distribution to summarise (Users across roles, monitors + across health) _and_ the tiles double as the facet. - **Highlight exceptions, not the norm.** Tint the failed/paused rows; leave the healthy majority clean. A table where every row is coloured signals nothing. And if a page has no true failure state (a catalog list), the calm answer is a @@ -204,19 +204,19 @@ Colour only earns attention if most of the screen stays quiet: data" is a `—`, not a wall of zeros. (`OStatCard` does this.) - **State is border/colour, not fills.** Selected/hover on interactive tiles use an accent **border**, not a grey/blue background wash. No hover shadows. -- **Semantic colour ≠ brand accent.** Green/amber/red carry *meaning* (health); - the brand teal carries *structure/selection*. Don't mix the two roles. +- **Semantic colour ≠ brand accent.** Green/amber/red carry _meaning_ (health); + the brand teal carries _structure/selection_. Don't mix the two roles. - **One primary action.** In forms, exactly one brand-coloured primary (Save); destructive in red; everything else neutral. - **No layout shift.** Reserve space for anything that streams in (a stat card - renders the same box loaded or not; a proportion-bar *track* is always drawn). + renders the same box loaded or not; a proportion-bar _track_ is always drawn). --- ## Per-archetype quick recipe - **Monitoring** → `OStatStrip` (state counts, filter tiles) in `#subheader`; - `OTag` status chips; row rail + exception tint; relative recency. *(Alerts.)* + `OTag` status chips; row rail + exception tint; relative recency. _(Alerts.)_ - **Catalog** → `OStatStrip` counting totals (Total · Folders · Favourites · Updated-this-week, `max` optional); category `OTag` (folder/type/language); `OUserCell` owner; relative "updated"; a colour-filled favourite. diff --git a/.claude/skills/ui-architect/references/component-catalog.md b/.claude/skills/ui-architect/references/component-catalog.md index f5f0a2ba03..c4fd07afe3 100644 --- a/.claude/skills/ui-architect/references/component-catalog.md +++ b/.claude/skills/ui-architect/references/component-catalog.md @@ -10,21 +10,21 @@ markup — don't guess a prop name. ### Reference files -| File | Covers | -| --- | --- | -| [references/core-display.md](core-display.md) | Badge/Tag/DimensionChip, Card, Code, Collapsible, EmptyState, Icon, Separator, Shortcut, **Text (typography)**, VirtualScroll | -| [references/core-controls-table.md](core-controls-table.md) | **Button**/ButtonGroup, Navbar, RefreshButton, Splitter, ToggleGroup, **Table** (+ cell renderers) | -| [references/forms-inputs.md](forms-inputs.md) | Input/Textarea, Select, Combobox, SearchInput, Checkbox, Radio, Switch, OptionGroup | -| [references/forms-specialized.md](forms-specialized.md) | **Form + useOForm**, Color, Date, DateTimeRange, Time, File, Range, Slider | -| [references/forms-validation.md](forms-validation.md) | **Validating a form: OForm + Zod schema, binding rules, submit/loading, conditional rendering, field arrays, testing** | -| [references/keyboard-shortcuts.md](keyboard-shortcuts.md) | **Keyboard shortcuts: registry, `useShortcut`/`useShortcuts`, display via `OShortcut`/`shortcut-id`, cheatsheet** | -| [references/creating-components.md](creating-components.md) | **Building a NEW O2 component: lib vs components, folder contract, families, headless-first (reka-ui), no-UI-prop-leakage, strict TS, tokens, form wrappers, workflow, testing** | -| [references/design-tokens.md](design-tokens.md) | **Design tokens: using `--color-*`, the token files, registering a new token, the `--o2-*` ban + full `--o2-*` → `--color-*` migration map** | -| [references/page-recipes.md](page-recipes.md) | **Whole-page layouts: the listing/table page (full-height flush skeleton, mandatory search · refresh · column-visibility toggle, filtered-vs-first-run empty state) and the detail/editor page** | -| [references/navigation-menus.md](navigation-menus.md) | **Registering a new page in navigation: the route composables, the left-rail item (`linksList`), Settings/IAM sub-pages (`SectionRail`), hover-flyout children (`navGroups.ts`), and cloud/enterprise/RBAC gating** | -| [references/overlay-navigation.md](overlay-navigation.md) | Dialog, Drawer, Dropdown, Popover, Tooltip · Pagination, Stepper, Tabs | -| [references/responsive.md](responsive.md) | **Phones & tablets with an unchanged laptop view: breakpoints + `useBreakpoint`, one-row header/toolbar/stat chrome, rails → anchored drawers, row-action kebab, popup sizing, clipping and touch gotchas, verification** | -| [references/feedback-data.md](feedback-data.md) | Banner, Toast (+ useToast), Spinner, Skeleton, InnerLoading · ProgressBar, Timeline, Tree · FieldList | +| File | Covers | +| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [references/core-display.md](core-display.md) | Badge/Tag/DimensionChip, Card, Code, Collapsible, EmptyState, Icon, Separator, Shortcut, **Text (typography)**, VirtualScroll | +| [references/core-controls-table.md](core-controls-table.md) | **Button**/ButtonGroup, Navbar, RefreshButton, Splitter, ToggleGroup, **Table** (+ cell renderers) | +| [references/forms-inputs.md](forms-inputs.md) | Input/Textarea, Select, Combobox, SearchInput, Checkbox, Radio, Switch, OptionGroup | +| [references/forms-specialized.md](forms-specialized.md) | **Form + useOForm**, Color, Date, DateTimeRange, Time, File, Range, Slider | +| [references/forms-validation.md](forms-validation.md) | **Validating a form: OForm + Zod schema, binding rules, submit/loading, conditional rendering, field arrays, testing** | +| [references/keyboard-shortcuts.md](keyboard-shortcuts.md) | **Keyboard shortcuts: registry, `useShortcut`/`useShortcuts`, display via `OShortcut`/`shortcut-id`, cheatsheet** | +| [references/creating-components.md](creating-components.md) | **Building a NEW O2 component: lib vs components, folder contract, families, headless-first (reka-ui), no-UI-prop-leakage, strict TS, tokens, form wrappers, workflow, testing** | +| [references/design-tokens.md](design-tokens.md) | **Design tokens: using `--color-*`, the token files, registering a new token, the `--o2-*` ban + full `--o2-*` → `--color-*` migration map** | +| [references/page-recipes.md](page-recipes.md) | **Whole-page layouts: the listing/table page (full-height flush skeleton, mandatory search · refresh · column-visibility toggle, filtered-vs-first-run empty state) and the detail/editor page** | +| [references/navigation-menus.md](navigation-menus.md) | **Registering a new page in navigation: the route composables, the left-rail item (`linksList`), Settings/IAM sub-pages (`SectionRail`), hover-flyout children (`navGroups.ts`), and cloud/enterprise/RBAC gating** | +| [references/overlay-navigation.md](overlay-navigation.md) | Dialog, Drawer, Dropdown, Popover, Tooltip · Pagination, Stepper, Tabs | +| [references/responsive.md](responsive.md) | **Phones & tablets with an unchanged laptop view: breakpoints + `useBreakpoint`, one-row header/toolbar/stat chrome, rails → anchored drawers, row-action kebab, popup sizing, clipping and touch gotchas, verification** | +| [references/feedback-data.md](feedback-data.md) | Banner, Toast (+ useToast), Spinner, Skeleton, InnerLoading · ProgressBar, Timeline, Tree · FieldList | > **Headless vs form-bound.** Most form controls come as a pair: `OInput` > (headless, `v-model` only) and `OFormInput` (adds label / error / required / @@ -33,57 +33,57 @@ markup — don't guess a prop name. ### Scenario → component -| I need to… | Use | Reference | -| --- | --- | --- | -| Trigger an action (submit, add, open) | `OButton` (`OButtonGroup` for a set) | core-controls-table | -| Show a page/module header | `OPageHeader` (rule 1) | — | -| Render tabular data | `OTable` + `OTableColumnDef[]` | core-controls-table | -| Show a status label / count / tag | `OBadge`, `OTag`, `ODimensionChip` | core-display | -| Group content in a surface | `OCard` (+ `OCardSection`, `OCardActions`) | core-display | -| Body / heading / muted text | `OText` (pick the `variant`) | core-display | -| Show an icon | `OIcon` (`name` from the icon registry) | core-display | -| A divider / hairline | `OSeparator` | core-display | -| Show code / a code block | `OCode`, `OCodeBlock` | core-display | -| Expand/collapse a section | `OCollapsible` | core-display | -| Empty "no data" state | `OEmptyState` | core-display | -| Render a huge list performantly | `OVirtualScroll` | core-display | -| Single-line / multiline text entry | `OInput` / `OTextarea` (`OForm*` in a form) | forms-inputs | -| Pick one/many from a fixed list | `OSelect` (+ `OSelectItem`) | forms-inputs | -| Typeahead / free-entry combobox | `OCombobox` | forms-inputs | -| A search box | `OSearchInput` | forms-inputs | -| Boolean checkbox / group | `OCheckbox` / `OCheckboxGroup` | forms-inputs | -| Mutually exclusive choice | `ORadioGroup` (few) / `OSelect` (many) | forms-inputs | -| Instant on/off toggle | `OSwitch` | forms-inputs | -| Segmented control | `OToggleGroup` | core-controls-table | -| Filter strip that must fit a phone row | `OToggleGroup mobile-dropdown` | **responsive** | -| Side rail / field list on a phone | `ODrawer` with `anchor` (or `OPageLayout #sidebar` / `FolderList`) | **responsive** | -| Secondary header actions on a phone | `OPageHeader`/`OPageLayout` `#actions-overflow` | **responsive** | -| Build a validated form | `OForm` + Zod schema (binding rules) | **forms-validation** | -| Read form state for conditional rendering | `useOForm` (owner) / `inject`+`useStore` (child) | forms-validation | -| Add repeatable/dynamic field rows | `z.array` + indexed `name`, `:key="index"` | forms-validation | -| Pick a color | `OColor` | forms-specialized | -| Pick a date / date-time range / time | `ODate` / `ODateTimeRange` / `OTime` | forms-specialized | -| Upload a file | `OFile` | forms-specialized | -| Numeric slider / range | `OSlider` / `ORange` | forms-specialized | -| Modal task / short form | `ODialog` (see form-container rule) | overlay-navigation | -| Side panel / large or contextual form | `ODrawer` | overlay-navigation | -| Menu of actions | `ODropdown` (+ `ODropdownItem`) | overlay-navigation | -| Freeform floating content | `OPopover` | overlay-navigation | -| Hover hint | `OTooltip` | overlay-navigation | -| Switch between content sections | `OTabs` (+ `OTab`/`OTabPanel`); `ORouteTab` for route-driven | overlay-navigation | -| Multi-step wizard | `OStepper` (+ `OStep`) | overlay-navigation | -| Paginate a list | `OPagination` | overlay-navigation | -| Persistent inline message | `OBanner` | feedback-data | -| Transient notification (replaces `q-notify`) | `useToast()` (+ `OToastProvider` at root) | feedback-data | -| Loading state | `OSkeleton` (placeholder) / `OSpinner` (bare) / `OInnerLoading` (over a container) | feedback-data | -| Progress indicator | `OProgressBar` | feedback-data | -| Event timeline | `OTimeline` (+ `OTimelineItem`) | feedback-data | -| Hierarchical / tree data | `OTree` (+ `OTreeNode`) | feedback-data | -| Label–value detail rows | `OFieldList` (+ `OFieldRow`, `OFieldLabel`) | feedback-data | -| Confirm / destructive prompt | `ConfirmDialog` + `useConfirmDialog` (app-level, `@/components` + `@/composables`) | — | -| Add a keyboard shortcut | registry entry + `useShortcuts([{ id, handler }])`; display via `OShortcut`/`shortcut-id` | keyboard-shortcuts | -| Build a NEW component (nothing fits) | new `O*` in `web/src/lib` (generic) or named component in `web/src/components` (app-specific) — never inline classes | **creating-components** | -| Add a new page to the nav / menu / settings sub-menu | route composable + `linksList` / `navGroups.ts` / `settingsItems`, gated by env/role | **navigation-menus** | +| I need to… | Use | Reference | +| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| Trigger an action (submit, add, open) | `OButton` (`OButtonGroup` for a set) | core-controls-table | +| Show a page/module header | `OPageHeader` (rule 1) | — | +| Render tabular data | `OTable` + `OTableColumnDef[]` | core-controls-table | +| Show a status label / count / tag | `OBadge`, `OTag`, `ODimensionChip` | core-display | +| Group content in a surface | `OCard` (+ `OCardSection`, `OCardActions`) | core-display | +| Body / heading / muted text | `OText` (pick the `variant`) | core-display | +| Show an icon | `OIcon` (`name` from the icon registry) | core-display | +| A divider / hairline | `OSeparator` | core-display | +| Show code / a code block | `OCode`, `OCodeBlock` | core-display | +| Expand/collapse a section | `OCollapsible` | core-display | +| Empty "no data" state | `OEmptyState` | core-display | +| Render a huge list performantly | `OVirtualScroll` | core-display | +| Single-line / multiline text entry | `OInput` / `OTextarea` (`OForm*` in a form) | forms-inputs | +| Pick one/many from a fixed list | `OSelect` (+ `OSelectItem`) | forms-inputs | +| Typeahead / free-entry combobox | `OCombobox` | forms-inputs | +| A search box | `OSearchInput` | forms-inputs | +| Boolean checkbox / group | `OCheckbox` / `OCheckboxGroup` | forms-inputs | +| Mutually exclusive choice | `ORadioGroup` (few) / `OSelect` (many) | forms-inputs | +| Instant on/off toggle | `OSwitch` | forms-inputs | +| Segmented control | `OToggleGroup` | core-controls-table | +| Filter strip that must fit a phone row | `OToggleGroup mobile-dropdown` | **responsive** | +| Side rail / field list on a phone | `ODrawer` with `anchor` (or `OPageLayout #sidebar` / `FolderList`) | **responsive** | +| Secondary header actions on a phone | `OPageHeader`/`OPageLayout` `#actions-overflow` | **responsive** | +| Build a validated form | `OForm` + Zod schema (binding rules) | **forms-validation** | +| Read form state for conditional rendering | `useOForm` (owner) / `inject`+`useStore` (child) | forms-validation | +| Add repeatable/dynamic field rows | `z.array` + indexed `name`, `:key="index"` | forms-validation | +| Pick a color | `OColor` | forms-specialized | +| Pick a date / date-time range / time | `ODate` / `ODateTimeRange` / `OTime` | forms-specialized | +| Upload a file | `OFile` | forms-specialized | +| Numeric slider / range | `OSlider` / `ORange` | forms-specialized | +| Modal task / short form | `ODialog` (see form-container rule) | overlay-navigation | +| Side panel / large or contextual form | `ODrawer` | overlay-navigation | +| Menu of actions | `ODropdown` (+ `ODropdownItem`) | overlay-navigation | +| Freeform floating content | `OPopover` | overlay-navigation | +| Hover hint | `OTooltip` | overlay-navigation | +| Switch between content sections | `OTabs` (+ `OTab`/`OTabPanel`); `ORouteTab` for route-driven | overlay-navigation | +| Multi-step wizard | `OStepper` (+ `OStep`) | overlay-navigation | +| Paginate a list | `OPagination` | overlay-navigation | +| Persistent inline message | `OBanner` | feedback-data | +| Transient notification (replaces `q-notify`) | `useToast()` (+ `OToastProvider` at root) | feedback-data | +| Loading state | `OSkeleton` (placeholder) / `OSpinner` (bare) / `OInnerLoading` (over a container) | feedback-data | +| Progress indicator | `OProgressBar` | feedback-data | +| Event timeline | `OTimeline` (+ `OTimelineItem`) | feedback-data | +| Hierarchical / tree data | `OTree` (+ `OTreeNode`) | feedback-data | +| Label–value detail rows | `OFieldList` (+ `OFieldRow`, `OFieldLabel`) | feedback-data | +| Confirm / destructive prompt | `ConfirmDialog` + `useConfirmDialog` (app-level, `@/components` + `@/composables`) | — | +| Add a keyboard shortcut | registry entry + `useShortcuts([{ id, handler }])`; display via `OShortcut`/`shortcut-id` | keyboard-shortcuts | +| Build a NEW component (nothing fits) | new `O*` in `web/src/lib` (generic) or named component in `web/src/components` (app-specific) — never inline classes | **creating-components** | +| Add a new page to the nav / menu / settings sub-menu | route composable + `linksList` / `navGroups.ts` / `settingsItems`, gated by env/role | **navigation-menus** | If a scenario isn't covered by anything above, re-check the reference files before assuming a component is missing — the list is complete as of this skill's diff --git a/.claude/skills/ui-architect/references/conventions.md b/.claude/skills/ui-architect/references/conventions.md index ffcc3d8ce3..b8fe6360e9 100644 --- a/.claude/skills/ui-architect/references/conventions.md +++ b/.claude/skills/ui-architect/references/conventions.md @@ -33,7 +33,7 @@ and [references/page-recipes.md](page-recipes.md). background, no border). Every card must carry explicit surface classes, e.g. `bg-card-glass-bg rounded-default border border-border-default` (the AI Observability panel family) or `bg-surface-base border border-border-default - rounded-default` (synthetics). Copy the class string from the sibling family +rounded-default` (synthetics). Copy the class string from the sibling family verbatim. - **A fixed set of stacked panels must not make the page scroll.** When a page is N cards tall (dashboards, signal panels), give it the full-height split: @@ -98,13 +98,14 @@ looking for one to reshape a component, that's the signal you need a new The visual of an O2 component is produced by its own `variant`/`size`, never by patching it from outside. Banned on an O2 component: -| Banned | Why | Instead | -| --- | --- | --- | -| `style="color: #fff; padding: 10px"` | hardcoded colors/px, bypasses tokens | use `variant` / `size` / component design | -| `class="tw:px-2 tw:text-sm"` (`tw:` prefix) | prefix removed — `tw:*` doesn't resolve | use component `variant`/`size` props | -| `