@oneuptime/common 14.0.10 → 14.0.11
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/Models/DatabaseModels/AIConversation.ts +127 -1
- package/Models/DatabaseModels/CephCluster.ts +12 -0
- package/Models/DatabaseModels/CloudResource.ts +12 -0
- package/Models/DatabaseModels/DatabaseServer.ts +12 -0
- package/Models/DatabaseModels/DockerHost.ts +12 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +12 -0
- package/Models/DatabaseModels/GlobalOidc.ts +0 -4
- package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
- package/Models/DatabaseModels/GlobalSso.ts +0 -4
- package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
- package/Models/DatabaseModels/Host.ts +12 -0
- package/Models/DatabaseModels/IncidentForm.ts +594 -0
- package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
- package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
- package/Models/DatabaseModels/Index.ts +14 -0
- package/Models/DatabaseModels/IoTFleet.ts +12 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
- package/Models/DatabaseModels/MessageQueue.ts +1102 -0
- package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
- package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
- package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
- package/Models/DatabaseModels/PodmanHost.ts +12 -0
- package/Models/DatabaseModels/Project.ts +59 -0
- package/Models/DatabaseModels/ProjectOidc.ts +0 -4
- package/Models/DatabaseModels/ProjectSso.ts +0 -4
- package/Models/DatabaseModels/ProxmoxCluster.ts +12 -0
- package/Models/DatabaseModels/RumApplication.ts +12 -0
- package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
- package/Models/DatabaseModels/Service.ts +12 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
- package/Models/DatabaseModels/StatusPageSso.ts +0 -4
- package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +12 -0
- package/Server/API/AIChatAPI.ts +13 -114
- package/Server/API/AIInvestigationAPI.ts +3 -3
- package/Server/API/AIInvestigationConversationAPI.ts +266 -0
- package/Server/API/IncidentFormAPI.ts +376 -0
- package/Server/API/ProjectOIDC.ts +16 -25
- package/Server/API/ProjectSSO.ts +16 -24
- package/Server/API/StatusPageAPI.ts +42 -77
- package/Server/Enterprise/EnterpriseEdition.ts +93 -28
- package/Server/Enterprise/EnterpriseFeature.ts +25 -3
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
- package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
- package/Server/Middleware/UserAuthorization.ts +16 -38
- package/Server/Services/AIService.ts +10 -13
- package/Server/Services/IncidentCustomFieldService.ts +112 -2
- package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
- package/Server/Services/IncidentEpisodeService.ts +10 -1
- package/Server/Services/IncidentFormService.ts +1566 -0
- package/Server/Services/IncidentFormSubmissionService.ts +97 -0
- package/Server/Services/IncidentGroupingEngineService.ts +45 -8
- package/Server/Services/IncidentService.ts +123 -5
- package/Server/Services/IncidentTemplateService.ts +22 -0
- package/Server/Services/Index.ts +14 -0
- package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
- package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
- package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
- package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
- package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
- package/Server/Services/MessageQueueService.ts +1463 -0
- package/Server/Services/OpenTelemetryIngestService.ts +37 -4
- package/Server/Services/ProjectService.ts +0 -59
- package/Server/Services/StatusPagePrivateUserService.ts +1 -4
- package/Server/Services/StatusPageService.ts +1 -51
- package/Server/Services/TeamComplianceSettingService.ts +163 -42
- package/Server/Services/UserNotificationRuleService.ts +8 -1
- package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
- package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
- package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
- package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
- package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
- package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
- package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +7 -1
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +34 -14
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +69 -24
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +9 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +53 -10
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +27 -10
- package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
- package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
- package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
- package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
- package/Server/Utils/EditionEnforcement.ts +19 -202
- package/Server/Utils/LLM/LLMService.ts +259 -63
- package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/SameOriginRequest.ts +203 -0
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
- package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
- package/Server/Utils/VendorAssets.ts +3 -3
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +3 -1
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +10 -1
- package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
- package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
- package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
- package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
- package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
- package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
- package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
- package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
- package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
- package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +174 -12
- package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
- package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
- package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +5 -0
- package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
- package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
- package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
- package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
- package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
- package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
- package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
- package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
- package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
- package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
- package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
- package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
- package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
- package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
- package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
- package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
- package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
- package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
- package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
- package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
- package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
- package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
- package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
- package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
- package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
- package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
- package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
- package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
- package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
- package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
- package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
- package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
- package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
- package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
- package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
- package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
- package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
- package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
- package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
- package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
- package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
- package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
- package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
- package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
- package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
- package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
- package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
- package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
- package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
- package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +298 -9
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +453 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
- package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
- package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
- package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
- package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
- package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
- package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
- package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
- package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
- package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
- package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
- package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
- package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
- package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
- package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
- package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +28 -9
- package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
- package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
- package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
- package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
- package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
- package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
- package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
- package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
- package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
- package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
- package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
- package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
- package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
- package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
- package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
- package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
- package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
- package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
- package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
- package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
- package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
- package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
- package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
- package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
- package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
- package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
- package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
- package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
- package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
- package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
- package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
- package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
- package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
- package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
- package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
- package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
- package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
- package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
- package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
- package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
- package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +266 -25
- package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
- package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
- package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
- package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +297 -15
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +10 -6
- package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
- package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
- package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
- package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
- package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
- package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
- package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
- package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
- package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
- package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
- package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
- package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
- package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
- package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
- package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
- package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
- package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
- package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
- package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
- package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
- package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +5 -3
- package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
- package/Tests/Types/XML.test.ts +51 -0
- package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
- package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
- package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
- package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
- package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
- package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
- package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
- package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
- package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
- package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
- package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
- package/Tests/UI/Components/Icon.test.tsx +32 -0
- package/Tests/UI/Components/Input.test.tsx +31 -0
- package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
- package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
- package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
- package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
- package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
- package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
- package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
- package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
- package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
- package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
- package/Tests/UI/Components/TextArea.test.tsx +19 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
- package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
- package/Tests/UI/EsbuildConfig.test.ts +115 -39
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
- package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
- package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
- package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
- package/Types/AI/AIAgentRunLimits.ts +146 -0
- package/Types/Code/CodeType.ts +2 -0
- package/Types/Code/JsonSyntax.ts +923 -0
- package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
- package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
- package/Types/Icon/IconProp.ts +2 -0
- package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
- package/Types/Incident/IncidentFormPublic.ts +1145 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
- package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
- package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
- package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
- package/Types/MessageQueue/MessagingSystem.ts +518 -0
- package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
- package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
- package/Types/Permission.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +10 -4
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
- package/Types/Rules/RuleRun.ts +4 -0
- package/Types/Team/ComplianceNotificationChannel.ts +4 -3
- package/Types/Team/ComplianceRule.ts +71 -18
- package/Types/Team/ComplianceRuleType.ts +2 -2
- package/Types/Team/TeamComplianceStatus.ts +6 -2
- package/Types/Telemetry/EntityType.ts +16 -0
- package/Types/Telemetry/LockedFilterDetail.ts +22 -0
- package/Types/XML.ts +26 -0
- package/UI/Components/CodeEditor/CodeEditor.css +379 -0
- package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
- package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
- package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
- package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
- package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
- package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
- package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
- package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
- package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
- package/UI/Components/Forms/Fields/FormField.tsx +8 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Icon/Icon.tsx +18 -0
- package/UI/Components/Input/Input.tsx +11 -4
- package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
- package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
- package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
- package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
- package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
- package/UI/Components/Table/EmptyTableMessage.ts +81 -0
- package/UI/Components/Table/Table.tsx +50 -21
- package/UI/Components/Table/TableRow.tsx +9 -1
- package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
- package/UI/Components/TextArea/TextArea.tsx +9 -2
- package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
- package/UI/Styles/Theme.css +8 -0
- package/UI/esbuild-config.js +2 -32
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
- package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
- package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
- package/Utils/Telemetry/EntityKey.ts +48 -0
- package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
- package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +12 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
- package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
- package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +14 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
- package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
- package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js +12 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +61 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
- package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +12 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
- package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +12 -0
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
- package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +12 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -103
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
- package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentFormAPI.js +245 -0
- package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectOIDC.js +16 -24
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +16 -24
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +42 -75
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
- package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +10 -13
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentFormService.js +1202 -0
- package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
- package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +106 -4
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +14 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
- package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/MessageQueueService.js +1140 -0
- package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +0 -54
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +0 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
- package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
- package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
- package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
- package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +7 -2
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +29 -14
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +38 -14
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +2 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +40 -10
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +19 -7
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
- package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
- package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
- package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
- package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
- package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +3 -3
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +3 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +10 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
- package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
- package/build/dist/Types/Code/CodeType.js +2 -0
- package/build/dist/Types/Code/CodeType.js.map +1 -1
- package/build/dist/Types/Code/JsonSyntax.js +604 -0
- package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
- package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
- package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
- package/build/dist/Types/Icon/IconProp.js +2 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
- package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
- package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
- package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
- package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
- package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
- package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
- package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
- package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
- package/build/dist/Types/Permission.js +274 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +10 -4
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRule.js +48 -14
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
- package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
- package/build/dist/Types/Telemetry/EntityType.js +16 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/XML.js +23 -0
- package/build/dist/Types/XML.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
- package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
- package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
- package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +8 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +11 -3
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
- package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +30 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +9 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
- package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
- package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
- package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
- package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -3
- package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
- package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
- package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
- package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
- package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
- package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
- package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
- package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
- package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
- package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
- package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
|
@@ -1,49 +1,134 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import "./CodeEditor.css";
|
|
2
|
+
import {
|
|
3
|
+
EditorState,
|
|
4
|
+
IndentUnit,
|
|
5
|
+
LineColumn,
|
|
6
|
+
TextEdit,
|
|
7
|
+
applyTextEdit,
|
|
8
|
+
backspace,
|
|
9
|
+
countLines,
|
|
10
|
+
detectIndentUnit,
|
|
11
|
+
getLineColumn,
|
|
12
|
+
indent,
|
|
13
|
+
indentLines,
|
|
14
|
+
lineEndOf,
|
|
15
|
+
lineStartOf,
|
|
16
|
+
newLine,
|
|
17
|
+
outdent,
|
|
18
|
+
spacesIndent,
|
|
19
|
+
toggleLineComment,
|
|
20
|
+
typeCharacter,
|
|
21
|
+
} from "./CodeEditorCommands";
|
|
22
|
+
import { escapeHtml, highlightCode } from "./CodeEditorHighlight";
|
|
23
|
+
import { CodeLanguage, getCodeLanguage } from "./CodeEditorLanguages";
|
|
24
|
+
import Icon from "../Icon/Icon";
|
|
3
25
|
import CodeType from "../../../Types/Code/CodeType";
|
|
4
|
-
import
|
|
5
|
-
|
|
26
|
+
import {
|
|
27
|
+
JsonSyntaxCheckResult,
|
|
28
|
+
checkJsonSyntax,
|
|
29
|
+
describeJsonSyntaxError,
|
|
30
|
+
findJsonSyntaxError,
|
|
31
|
+
formatJson,
|
|
32
|
+
} from "../../../Types/Code/JsonSyntax";
|
|
33
|
+
import {
|
|
34
|
+
YamlSyntaxCheckResult,
|
|
35
|
+
checkYamlSyntax,
|
|
36
|
+
describeYamlSyntaxError,
|
|
37
|
+
} from "../../../Types/Code/YamlSyntax";
|
|
38
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
6
39
|
import React, {
|
|
7
40
|
FunctionComponent,
|
|
8
41
|
ReactElement,
|
|
9
42
|
useEffect,
|
|
43
|
+
useId,
|
|
44
|
+
useLayoutEffect,
|
|
10
45
|
useMemo,
|
|
11
46
|
useRef,
|
|
12
47
|
useState,
|
|
13
48
|
} from "react";
|
|
14
49
|
|
|
15
|
-
configureMonacoLoader();
|
|
16
|
-
|
|
17
50
|
export interface ComponentProps {
|
|
51
|
+
type: CodeType;
|
|
52
|
+
/** The controlled value. Wins over `initialValue` whenever it is set. */
|
|
53
|
+
value?: string | undefined;
|
|
54
|
+
/** Seeds an editor that is not given `value`. */
|
|
18
55
|
initialValue?: undefined | string;
|
|
19
|
-
onClick?: undefined | (() => void);
|
|
20
|
-
placeholder?: undefined | string;
|
|
21
|
-
className?: undefined | string;
|
|
22
56
|
onChange?: undefined | ((value: string) => void);
|
|
23
|
-
readOnly?: boolean | undefined;
|
|
24
|
-
type: CodeType;
|
|
25
57
|
onFocus?: (() => void) | undefined;
|
|
26
58
|
onBlur?: (() => void) | undefined;
|
|
59
|
+
onClick?: undefined | (() => void);
|
|
60
|
+
/** Shown dimmed in the empty editor. Never becomes document text. */
|
|
61
|
+
placeholder?: undefined | string;
|
|
62
|
+
/**
|
|
63
|
+
* Sample code for an empty editor: shown dimmed and highlighted in place of
|
|
64
|
+
* the document, and offered by an "Insert example" button.
|
|
65
|
+
*/
|
|
66
|
+
example?: string | undefined;
|
|
67
|
+
/** One short line of guidance in the toolbar. */
|
|
68
|
+
hint?: string | undefined;
|
|
69
|
+
error?: string | undefined;
|
|
70
|
+
readOnly?: boolean | undefined;
|
|
71
|
+
className?: undefined | string;
|
|
27
72
|
dataTestId?: string | undefined;
|
|
28
73
|
tabIndex?: number | undefined;
|
|
29
|
-
|
|
30
|
-
|
|
74
|
+
/** Id of the textarea, for a <label htmlFor>. */
|
|
75
|
+
id?: string | undefined;
|
|
76
|
+
/** The line number gutter. On for everything but wrapped prose. */
|
|
31
77
|
showLineNumbers?: boolean | undefined;
|
|
32
78
|
disableSpellCheck?: boolean | undefined;
|
|
33
79
|
ariaLabelledby?: string | undefined;
|
|
34
80
|
/** Ids of the elements describing this control (hint, status, error). */
|
|
35
81
|
ariaDescribedby?: string | undefined;
|
|
36
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Marks the control invalid to assistive technology. Implied by `error`;
|
|
84
|
+
* for a caller that reports the problem somewhere else.
|
|
85
|
+
*/
|
|
37
86
|
ariaInvalid?: boolean | undefined;
|
|
38
|
-
/**
|
|
87
|
+
/**
|
|
88
|
+
* A fixed CSS height for the text area. Without it the editor grows with
|
|
89
|
+
* its content from `minLines` up to `maxHeight`, then scrolls.
|
|
90
|
+
*/
|
|
39
91
|
height?: string | undefined;
|
|
92
|
+
minLines?: number | undefined;
|
|
93
|
+
maxHeight?: string | undefined;
|
|
94
|
+
/** JSON only: the field is read with JSON5, so judge it by JSON5's rules. */
|
|
95
|
+
allowJSON5?: boolean | undefined;
|
|
40
96
|
}
|
|
41
97
|
|
|
98
|
+
/*
|
|
99
|
+
* These two have to match .ou-code-editor's --ou-code-line-height and
|
|
100
|
+
* --ou-code-padding-y in CodeEditor.css: the gutter's click target and the
|
|
101
|
+
* current-line band are placed with them.
|
|
102
|
+
*/
|
|
103
|
+
export const CODE_EDITOR_LINE_HEIGHT_PX: number = 20;
|
|
104
|
+
export const CODE_EDITOR_PADDING_Y_PX: number = 10;
|
|
105
|
+
export const CODE_EDITOR_PADDING_X_PX: number = 12;
|
|
106
|
+
|
|
107
|
+
// Columns a tab advances to, as `tab-size` in CodeEditor.css draws it.
|
|
108
|
+
const TAB_SIZE: number = 4;
|
|
109
|
+
|
|
110
|
+
// Characters in the hidden run the character width is measured from.
|
|
111
|
+
const MEASURE_LENGTH: number = 32;
|
|
112
|
+
|
|
113
|
+
export const CODE_EDITOR_DEFAULT_MIN_LINES: number = 5;
|
|
114
|
+
export const CODE_EDITOR_DEFAULT_MAX_HEIGHT: string = "min(60vh, 30rem)";
|
|
115
|
+
|
|
116
|
+
/** How long the document sits still before it is checked again. */
|
|
117
|
+
export const CODE_EDITOR_VALIDATION_DEBOUNCE_MS: number = 250;
|
|
118
|
+
|
|
119
|
+
const FALLBACK_INDENT: IndentUnit = spacesIndent(2);
|
|
120
|
+
|
|
42
121
|
/*
|
|
43
122
|
* Callers hand us `any` through Form's currentValues, so a non-string can
|
|
44
|
-
* still reach a prop typed as string.
|
|
123
|
+
* still reach a prop typed as string. Line breaks are normalised because a
|
|
124
|
+
* textarea's value always reads back with "\n": keeping "\r\n" here would
|
|
125
|
+
* put the highlighted layer one character per line out of step with it.
|
|
45
126
|
*/
|
|
46
|
-
|
|
127
|
+
type ToEditorTextFunction = (value: string | undefined) => string;
|
|
128
|
+
|
|
129
|
+
export const toEditorText: ToEditorTextFunction = (
|
|
130
|
+
value: string | undefined,
|
|
131
|
+
): string => {
|
|
47
132
|
if (value === undefined || value === null) {
|
|
48
133
|
return "";
|
|
49
134
|
}
|
|
@@ -52,366 +137,1115 @@ function toEditorText(value: string | undefined): string {
|
|
|
52
137
|
return JSON.stringify(value, null, 4);
|
|
53
138
|
}
|
|
54
139
|
|
|
55
|
-
return value;
|
|
140
|
+
return value.replace(/\r\n?/g, "\n");
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
type SetNativeValueFunction = (
|
|
144
|
+
textarea: HTMLTextAreaElement,
|
|
145
|
+
value: string,
|
|
146
|
+
) => void;
|
|
147
|
+
|
|
148
|
+
/*
|
|
149
|
+
* Through the prototype's setter, not `textarea.value =`: React watches the
|
|
150
|
+
* instance property to know what it last rendered, and a write through it
|
|
151
|
+
* would make the input event below look like no change at all.
|
|
152
|
+
*/
|
|
153
|
+
const setNativeValue: SetNativeValueFunction = (
|
|
154
|
+
textarea: HTMLTextAreaElement,
|
|
155
|
+
value: string,
|
|
156
|
+
): void => {
|
|
157
|
+
const descriptor: PropertyDescriptor | undefined =
|
|
158
|
+
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(textarea), "value");
|
|
159
|
+
|
|
160
|
+
if (descriptor?.set) {
|
|
161
|
+
descriptor.set.call(textarea, value);
|
|
162
|
+
} else {
|
|
163
|
+
textarea.value = value;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export type ApplyEditToTextareaFunction = (
|
|
168
|
+
textarea: HTMLTextAreaElement,
|
|
169
|
+
edit: TextEdit,
|
|
170
|
+
) => void;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Make `edit` in the textarea the way typing would, so it lands on the
|
|
174
|
+
* browser's own undo stack as one step. execCommand is deprecated but is
|
|
175
|
+
* still the only way to do that, in every browser; where it is missing
|
|
176
|
+
* (jsdom) or declines, the value is written directly and an input event
|
|
177
|
+
* fired, which keeps React in step at the cost of that one undo entry.
|
|
178
|
+
*/
|
|
179
|
+
export const applyEditToTextarea: ApplyEditToTextareaFunction = (
|
|
180
|
+
textarea: HTMLTextAreaElement,
|
|
181
|
+
edit: TextEdit,
|
|
182
|
+
): void => {
|
|
183
|
+
const expected: string = applyTextEdit(textarea.value, edit);
|
|
184
|
+
|
|
185
|
+
if (edit.from !== edit.to || edit.insert !== "") {
|
|
186
|
+
textarea.setSelectionRange(edit.from, edit.to);
|
|
187
|
+
|
|
188
|
+
let applied: boolean = false;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
if (typeof document.execCommand === "function") {
|
|
192
|
+
applied =
|
|
193
|
+
edit.insert === ""
|
|
194
|
+
? document.execCommand("delete", false)
|
|
195
|
+
: document.execCommand("insertText", false, edit.insert);
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
applied = false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!applied || textarea.value !== expected) {
|
|
202
|
+
setNativeValue(textarea, expected);
|
|
203
|
+
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
textarea.setSelectionRange(edit.selectionStart, edit.selectionEnd);
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
interface SelectionShape {
|
|
211
|
+
start: number;
|
|
212
|
+
end: number;
|
|
213
|
+
/** The end the caret is on. */
|
|
214
|
+
active: number;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
interface GutterSegment {
|
|
218
|
+
text: string;
|
|
219
|
+
className?: string | undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
type BuildGutterFunction = (
|
|
223
|
+
lineCount: number,
|
|
224
|
+
marks: Map<number, string>,
|
|
225
|
+
) => Array<GutterSegment>;
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* The gutter is one run of text - "1\n2\n3..." - so a long document costs
|
|
229
|
+
* a handful of nodes, not one per line. Only the lines that look different
|
|
230
|
+
* (the current line, a line with a syntax error) are split out.
|
|
231
|
+
*/
|
|
232
|
+
const buildGutter: BuildGutterFunction = (
|
|
233
|
+
lineCount: number,
|
|
234
|
+
marks: Map<number, string>,
|
|
235
|
+
): Array<GutterSegment> => {
|
|
236
|
+
const segments: Array<GutterSegment> = [];
|
|
237
|
+
let buffer: string = "";
|
|
238
|
+
|
|
239
|
+
for (let line: number = 1; line <= lineCount; line++) {
|
|
240
|
+
const className: string | undefined = marks.get(line);
|
|
241
|
+
const separator: string = line < lineCount ? "\n" : "";
|
|
242
|
+
|
|
243
|
+
if (className) {
|
|
244
|
+
if (buffer) {
|
|
245
|
+
segments.push({ text: buffer });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
segments.push({ text: String(line), className });
|
|
249
|
+
buffer = separator;
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
buffer += String(line) + separator;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (buffer) {
|
|
257
|
+
segments.push({ text: buffer });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return segments;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const MODIFIER_KEYS: Array<string> = ["Shift", "Control", "Alt", "Meta"];
|
|
264
|
+
|
|
265
|
+
/*
|
|
266
|
+
* Heard on the way in, through aria-describedby: Tab indents here, so the way
|
|
267
|
+
* out has to be announced rather than discovered.
|
|
268
|
+
*/
|
|
269
|
+
export const CODE_EDITOR_KEYS_HINT: string =
|
|
270
|
+
"Tab inserts indentation. To move focus out of the editor, press Escape, then Tab.";
|
|
271
|
+
|
|
272
|
+
type StatusTone = "error" | "valid" | "invalid" | "neutral";
|
|
273
|
+
|
|
274
|
+
interface StatusShape {
|
|
275
|
+
tone: StatusTone;
|
|
276
|
+
icon: IconProp;
|
|
277
|
+
message: string;
|
|
56
278
|
}
|
|
57
279
|
|
|
280
|
+
/*
|
|
281
|
+
* Light-theme values, one step darker than the obvious choice so the 12px
|
|
282
|
+
* status text clears 4.5:1 on bg-gray-50. Theme.css remaps these classes
|
|
283
|
+
* under html.dark.
|
|
284
|
+
*/
|
|
285
|
+
const STATUS_TONE_CLASS: Record<StatusTone, string> = {
|
|
286
|
+
error: "text-red-700",
|
|
287
|
+
invalid: "text-amber-700",
|
|
288
|
+
valid: "text-green-700",
|
|
289
|
+
neutral: "text-gray-500",
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const TOOLBAR_BUTTON_CLASS: string =
|
|
293
|
+
"inline-flex shrink-0 items-center gap-1 rounded px-2 py-1 text-xs font-medium text-gray-600 transition-colors hover:bg-gray-200 hover:text-gray-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 disabled:cursor-not-allowed disabled:opacity-40";
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* A code editor for form fields: a native textarea over a highlighted copy
|
|
297
|
+
* of its text, with line numbers, auto-indentation, bracket pairing, a
|
|
298
|
+
* status bar that says whether JSON or YAML parses (and where it does not),
|
|
299
|
+
* and Format / Copy actions. It renders on the first paint, has no runtime
|
|
300
|
+
* to download, and edits with the browser's own caret, selection, undo and
|
|
301
|
+
* find - which is also what makes it work with a phone keyboard and a screen
|
|
302
|
+
* reader.
|
|
303
|
+
*/
|
|
58
304
|
const CodeEditor: FunctionComponent<ComponentProps> = (
|
|
59
305
|
props: ComponentProps,
|
|
60
306
|
): ReactElement => {
|
|
61
|
-
|
|
307
|
+
const language: CodeLanguage = getCodeLanguage(props.type);
|
|
308
|
+
const readOnly: boolean = Boolean(props.readOnly);
|
|
309
|
+
|
|
310
|
+
const [text, setText] = useState<string>(() => {
|
|
311
|
+
return toEditorText(props.value ?? props.initialValue);
|
|
312
|
+
});
|
|
62
313
|
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
const editorRef: React.MutableRefObject<any> = useRef<any>(null);
|
|
66
|
-
const theme: Theme = useTheme();
|
|
314
|
+
// The latest text, for effects that must not re-run on every keystroke.
|
|
315
|
+
const textRef: React.MutableRefObject<string> = useRef<string>(text);
|
|
67
316
|
|
|
68
317
|
/*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* effects run in declaration order the `initialValue` one ran last on mount
|
|
72
|
-
* and reset the state to "" - so a caller that passed only `value` mounted
|
|
73
|
-
* an empty editor even though it had text to show. Seed the state on the
|
|
74
|
-
* first render and keep the precedence in a single effect.
|
|
318
|
+
* Checked on a timer, so a half-typed line is not judged the instant it is
|
|
319
|
+
* typed and a long document is not re-parsed on every keystroke.
|
|
75
320
|
*/
|
|
76
|
-
const [
|
|
77
|
-
|
|
321
|
+
const [checkedText, setCheckedText] = useState<string>(text);
|
|
322
|
+
|
|
323
|
+
const [selection, setSelection] = useState<SelectionShape>({
|
|
324
|
+
start: 0,
|
|
325
|
+
end: 0,
|
|
326
|
+
active: 0,
|
|
78
327
|
});
|
|
328
|
+
const [isFocused, setIsFocused] = useState<boolean>(false);
|
|
329
|
+
|
|
330
|
+
/*
|
|
331
|
+
* Tab indents, so it cannot also move focus - a keyboard trap unless there
|
|
332
|
+
* is a way out (WCAG 2.1.2). Escape releases the next Tab to the browser.
|
|
333
|
+
*/
|
|
334
|
+
const [tabMovesFocus, setTabMovesFocus] = useState<boolean>(false);
|
|
335
|
+
const tabMovesFocusRef: React.MutableRefObject<boolean> =
|
|
336
|
+
useRef<boolean>(false);
|
|
337
|
+
|
|
338
|
+
const [copied, setCopied] = useState<boolean>(false);
|
|
339
|
+
const copyResetTimer: React.MutableRefObject<ReturnType<
|
|
340
|
+
typeof setTimeout
|
|
341
|
+
> | null> = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
342
|
+
|
|
343
|
+
const textareaRef: React.MutableRefObject<HTMLTextAreaElement | null> =
|
|
344
|
+
useRef<HTMLTextAreaElement | null>(null);
|
|
345
|
+
const scrollerRef: React.MutableRefObject<HTMLDivElement | null> =
|
|
346
|
+
useRef<HTMLDivElement | null>(null);
|
|
347
|
+
const gutterRef: React.MutableRefObject<HTMLDivElement | null> =
|
|
348
|
+
useRef<HTMLDivElement | null>(null);
|
|
349
|
+
const measureRef: React.MutableRefObject<HTMLSpanElement | null> =
|
|
350
|
+
useRef<HTMLSpanElement | null>(null);
|
|
79
351
|
|
|
352
|
+
const instanceId: string = useId();
|
|
353
|
+
const keysHintId: string = `${instanceId}-code-keys`;
|
|
354
|
+
const statusId: string = `${instanceId}-code-status`;
|
|
355
|
+
const hintId: string = `${instanceId}-code-hint`;
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* `value` is the controlled prop; `initialValue` only seeds an editor that
|
|
359
|
+
* is not given one. Seeded on the first render above and kept in step in
|
|
360
|
+
* one effect: two effects, one per prop, ran in declaration order on mount
|
|
361
|
+
* and the `initialValue` one blanked an editor that was only given `value`.
|
|
362
|
+
*/
|
|
80
363
|
useEffect(() => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
props.value === undefined ? props.initialValue : props.value,
|
|
84
|
-
),
|
|
364
|
+
const next: string = toEditorText(
|
|
365
|
+
props.value === undefined ? props.initialValue : props.value,
|
|
85
366
|
);
|
|
367
|
+
|
|
368
|
+
if (next === textRef.current) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Replaced from outside (a sample inserted, a record loaded): judge it now.
|
|
373
|
+
textRef.current = next;
|
|
374
|
+
setText(next);
|
|
375
|
+
setCheckedText(next);
|
|
86
376
|
}, [props.value, props.initialValue]);
|
|
87
377
|
|
|
88
378
|
useEffect(() => {
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
379
|
+
if (!language.validator) {
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
93
382
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
383
|
+
const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
|
|
384
|
+
setCheckedText(text);
|
|
385
|
+
}, CODE_EDITOR_VALIDATION_DEBOUNCE_MS);
|
|
97
386
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
This is in JavaScript.
|
|
104
|
-
|
|
105
|
-
*/`,
|
|
106
|
-
);
|
|
107
|
-
}
|
|
387
|
+
return () => {
|
|
388
|
+
clearTimeout(timer);
|
|
389
|
+
};
|
|
390
|
+
}, [text, language.validator]);
|
|
108
391
|
|
|
109
|
-
|
|
110
|
-
|
|
392
|
+
useEffect(() => {
|
|
393
|
+
return () => {
|
|
394
|
+
if (copyResetTimer.current) {
|
|
395
|
+
clearTimeout(copyResetTimer.current);
|
|
111
396
|
}
|
|
397
|
+
};
|
|
398
|
+
}, []);
|
|
112
399
|
|
|
113
|
-
|
|
114
|
-
|
|
400
|
+
const indentUnit: IndentUnit = useMemo(() => {
|
|
401
|
+
return detectIndentUnit(text, FALLBACK_INDENT, language.allowTabs);
|
|
402
|
+
}, [text, language.allowTabs]);
|
|
403
|
+
|
|
404
|
+
const syntax: JsonSyntaxCheckResult | YamlSyntaxCheckResult | null =
|
|
405
|
+
useMemo(() => {
|
|
406
|
+
if (language.validator === "json") {
|
|
407
|
+
return checkJsonSyntax(checkedText, { allowJSON5: props.allowJSON5 });
|
|
115
408
|
}
|
|
116
409
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* rendered into `defaultValue` below, i.e. it becomes real document
|
|
120
|
-
* text. The JS and CSS arms above get away with it because they wrap
|
|
121
|
-
* the hint in a comment; an unwrapped YAML hint would be content the
|
|
122
|
-
* user can accidentally save, and the server would reject it.
|
|
123
|
-
*/
|
|
124
|
-
if (props.type === CodeType.YAML) {
|
|
125
|
-
setHelpText(`${props.placeholder}`);
|
|
410
|
+
if (language.validator === "yaml") {
|
|
411
|
+
return checkYamlSyntax(checkedText);
|
|
126
412
|
}
|
|
413
|
+
|
|
414
|
+
return null;
|
|
415
|
+
}, [checkedText, language.validator, props.allowJSON5]);
|
|
416
|
+
|
|
417
|
+
// Format rewrites only strict JSON, placeholders and all, never JSON5.
|
|
418
|
+
const canFormat: boolean = useMemo(() => {
|
|
419
|
+
return (
|
|
420
|
+
language.canFormat &&
|
|
421
|
+
!readOnly &&
|
|
422
|
+
checkedText.trim() !== "" &&
|
|
423
|
+
findJsonSyntaxError(checkedText) === null
|
|
424
|
+
);
|
|
425
|
+
}, [language.canFormat, readOnly, checkedText]);
|
|
426
|
+
|
|
427
|
+
const highlightedHtml: string = useMemo(() => {
|
|
428
|
+
return highlightCode(text, language.grammar, {
|
|
429
|
+
showTabs: language.showTabs,
|
|
430
|
+
});
|
|
431
|
+
}, [text, language.grammar, language.showTabs]);
|
|
432
|
+
|
|
433
|
+
const example: string = (props.example || "")
|
|
434
|
+
.replace(/^\s*\n/, "")
|
|
435
|
+
.replace(/\s+$/, "");
|
|
436
|
+
|
|
437
|
+
const ghostHtml: string | null = useMemo(() => {
|
|
438
|
+
if (text !== "") {
|
|
439
|
+
return null;
|
|
127
440
|
}
|
|
128
|
-
}, [props.placeholder, props.type]);
|
|
129
441
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
442
|
+
if (props.placeholder) {
|
|
443
|
+
return `<span class="ou-code-editor__placeholder">${escapeHtml(
|
|
444
|
+
props.placeholder,
|
|
445
|
+
)}</span>`;
|
|
446
|
+
}
|
|
136
447
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
448
|
+
if (example) {
|
|
449
|
+
return `<span class="ou-code-editor__example">${highlightCode(
|
|
450
|
+
example,
|
|
451
|
+
language.grammar,
|
|
452
|
+
)}</span>`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
return null;
|
|
456
|
+
}, [text, props.placeholder, example, language.grammar]);
|
|
457
|
+
|
|
458
|
+
const lineCount: number = useMemo(() => {
|
|
459
|
+
return countLines(text);
|
|
460
|
+
}, [text]);
|
|
461
|
+
|
|
462
|
+
const cursor: LineColumn = getLineColumn(text, selection.active);
|
|
463
|
+
const selectedCharacters: number = Math.abs(selection.end - selection.start);
|
|
141
464
|
|
|
142
|
-
const
|
|
465
|
+
const errorLine: number | null =
|
|
466
|
+
syntax &&
|
|
467
|
+
!syntax.isValid &&
|
|
468
|
+
syntax.line !== null &&
|
|
469
|
+
syntax.line <= lineCount
|
|
470
|
+
? syntax.line
|
|
471
|
+
: null;
|
|
143
472
|
|
|
144
|
-
|
|
473
|
+
const showGutter: boolean = language.wrap
|
|
474
|
+
? false
|
|
475
|
+
: props.showLineNumbers !== false;
|
|
476
|
+
|
|
477
|
+
const showActiveLine: boolean =
|
|
478
|
+
isFocused && !readOnly && selectedCharacters === 0;
|
|
479
|
+
|
|
480
|
+
const gutterSegments: Array<GutterSegment> = useMemo(() => {
|
|
481
|
+
const marks: Map<number, string> = new Map<number, string>();
|
|
482
|
+
|
|
483
|
+
if (showActiveLine) {
|
|
484
|
+
marks.set(cursor.line, "ou-code-editor__gutter-active");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (errorLine !== null) {
|
|
488
|
+
marks.set(errorLine, "ou-code-editor__gutter-error");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return buildGutter(lineCount, marks);
|
|
492
|
+
}, [lineCount, showActiveLine, cursor.line, errorLine]);
|
|
145
493
|
|
|
146
494
|
/*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
495
|
+
* Keep the caret in view. The textarea never scrolls - it is exactly as
|
|
496
|
+
* big as its text - so the scroller around it has to, and browsers do not
|
|
497
|
+
* agree on doing that for a caret inside a textarea: Firefox leaves the
|
|
498
|
+
* end of the document off-screen after Ctrl+End, and Chromium parks a
|
|
499
|
+
* caret at column 1 underneath the sticky line numbers. Lines have a fixed
|
|
500
|
+
* height and the font is monospaced, so where the caret is is arithmetic.
|
|
501
|
+
* (Wrapped prose is left to the browser: a wrapped line has no fixed row.)
|
|
150
502
|
*/
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const domNode: HTMLElement | null = editor?.getDomNode?.() || null;
|
|
503
|
+
useLayoutEffect(() => {
|
|
504
|
+
const scroller: HTMLDivElement | null = scrollerRef.current;
|
|
505
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
155
506
|
|
|
156
|
-
if (!
|
|
157
|
-
return
|
|
507
|
+
if (!isFocused || language.wrap || !scroller || !textarea) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const value: string = textarea.value;
|
|
512
|
+
const position: number = Math.min(selection.active, value.length);
|
|
513
|
+
const line: number = getLineColumn(value, position).line;
|
|
514
|
+
|
|
515
|
+
const top: number =
|
|
516
|
+
CODE_EDITOR_PADDING_Y_PX + (line - 1) * CODE_EDITOR_LINE_HEIGHT_PX;
|
|
517
|
+
const bottom: number = top + CODE_EDITOR_LINE_HEIGHT_PX;
|
|
518
|
+
|
|
519
|
+
if (top < scroller.scrollTop) {
|
|
520
|
+
scroller.scrollTop = Math.max(0, top - CODE_EDITOR_PADDING_Y_PX);
|
|
521
|
+
} else if (bottom > scroller.scrollTop + scroller.clientHeight) {
|
|
522
|
+
scroller.scrollTop =
|
|
523
|
+
bottom + CODE_EDITOR_PADDING_Y_PX - scroller.clientHeight;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const characterWidth: number =
|
|
527
|
+
(measureRef.current?.getBoundingClientRect().width || 0) / MEASURE_LENGTH;
|
|
528
|
+
|
|
529
|
+
if (characterWidth <= 0) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
let columns: number = 0;
|
|
534
|
+
|
|
535
|
+
for (const character of value.slice(
|
|
536
|
+
lineStartOf(value, position),
|
|
537
|
+
position,
|
|
538
|
+
)) {
|
|
539
|
+
columns =
|
|
540
|
+
character === "\t"
|
|
541
|
+
? columns + TAB_SIZE - (columns % TAB_SIZE)
|
|
542
|
+
: columns + 1;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const gutterWidth: number = gutterRef.current?.offsetWidth || 0;
|
|
546
|
+
const margin: number = 2 * characterWidth;
|
|
547
|
+
const x: number =
|
|
548
|
+
gutterWidth + CODE_EDITOR_PADDING_X_PX + columns * characterWidth;
|
|
549
|
+
|
|
550
|
+
if (x < scroller.scrollLeft + gutterWidth + CODE_EDITOR_PADDING_X_PX) {
|
|
551
|
+
scroller.scrollLeft = Math.max(
|
|
552
|
+
0,
|
|
553
|
+
x - gutterWidth - CODE_EDITOR_PADDING_X_PX - margin,
|
|
554
|
+
);
|
|
555
|
+
} else if (x + margin > scroller.scrollLeft + scroller.clientWidth) {
|
|
556
|
+
scroller.scrollLeft = x + margin - scroller.clientWidth;
|
|
557
|
+
}
|
|
558
|
+
}, [selection, text, isFocused, language.wrap]);
|
|
559
|
+
|
|
560
|
+
type CaptureSelectionFunction = () => void;
|
|
561
|
+
|
|
562
|
+
const captureSelection: CaptureSelectionFunction = (): void => {
|
|
563
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
564
|
+
|
|
565
|
+
if (!textarea) {
|
|
566
|
+
return;
|
|
158
567
|
}
|
|
159
568
|
|
|
160
|
-
|
|
569
|
+
const start: number = textarea.selectionStart;
|
|
570
|
+
const end: number = textarea.selectionEnd;
|
|
571
|
+
const active: number =
|
|
572
|
+
textarea.selectionDirection === "backward" ? start : end;
|
|
573
|
+
|
|
574
|
+
setSelection((previous: SelectionShape) => {
|
|
575
|
+
if (
|
|
576
|
+
previous.start === start &&
|
|
577
|
+
previous.end === end &&
|
|
578
|
+
previous.active === active
|
|
579
|
+
) {
|
|
580
|
+
return previous;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return { start, end, active };
|
|
584
|
+
});
|
|
161
585
|
};
|
|
162
586
|
|
|
163
|
-
type
|
|
587
|
+
type SetTabMovesFocusFunction = (value: boolean) => void;
|
|
164
588
|
|
|
165
|
-
const
|
|
166
|
-
|
|
589
|
+
const setTabRelease: SetTabMovesFocusFunction = (value: boolean): void => {
|
|
590
|
+
tabMovesFocusRef.current = value;
|
|
591
|
+
setTabMovesFocus(value);
|
|
592
|
+
};
|
|
167
593
|
|
|
168
|
-
|
|
594
|
+
type RunEditFunction = (edit: TextEdit | null) => void;
|
|
595
|
+
|
|
596
|
+
const runEdit: RunEditFunction = (edit: TextEdit | null): void => {
|
|
597
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
598
|
+
|
|
599
|
+
if (!edit || !textarea) {
|
|
169
600
|
return;
|
|
170
601
|
}
|
|
171
602
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
603
|
+
applyEditToTextarea(textarea, edit);
|
|
604
|
+
captureSelection();
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
type ReadStateFunction = () => EditorState;
|
|
608
|
+
|
|
609
|
+
const readState: ReadStateFunction = (): EditorState => {
|
|
610
|
+
const textarea: HTMLTextAreaElement = textareaRef.current!;
|
|
611
|
+
|
|
612
|
+
return {
|
|
613
|
+
text: textarea.value,
|
|
614
|
+
selectionStart: textarea.selectionStart,
|
|
615
|
+
selectionEnd: textarea.selectionEnd,
|
|
616
|
+
};
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
type ReplaceDocumentFunction = (replacement: string) => void;
|
|
620
|
+
|
|
621
|
+
// Through the textarea, so Undo brings the previous document back.
|
|
622
|
+
const replaceDocument: ReplaceDocumentFunction = (
|
|
623
|
+
replacement: string,
|
|
624
|
+
): void => {
|
|
625
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
626
|
+
|
|
627
|
+
if (!textarea || replacement === textarea.value) {
|
|
628
|
+
return;
|
|
180
629
|
}
|
|
181
630
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
631
|
+
textarea.focus();
|
|
632
|
+
runEdit({
|
|
633
|
+
from: 0,
|
|
634
|
+
to: textarea.value.length,
|
|
635
|
+
insert: replacement,
|
|
636
|
+
selectionStart: 0,
|
|
637
|
+
selectionEnd: 0,
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
if (scrollerRef.current) {
|
|
641
|
+
scrollerRef.current.scrollTop = 0;
|
|
642
|
+
scrollerRef.current.scrollLeft = 0;
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
type HandleFormatFunction = () => void;
|
|
647
|
+
|
|
648
|
+
const handleFormat: HandleFormatFunction = (): void => {
|
|
649
|
+
const formatted: string | null = formatJson(
|
|
650
|
+
textareaRef.current?.value ?? text,
|
|
651
|
+
indentUnit.text,
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
if (formatted !== null) {
|
|
655
|
+
replaceDocument(formatted);
|
|
186
656
|
}
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
type HandleCopyFunction = () => void;
|
|
660
|
+
|
|
661
|
+
const handleCopy: HandleCopyFunction = (): void => {
|
|
662
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
663
|
+
const content: string = textarea?.value ?? text;
|
|
664
|
+
const clipboard: Clipboard | undefined =
|
|
665
|
+
typeof navigator !== "undefined" ? navigator.clipboard : undefined;
|
|
666
|
+
|
|
667
|
+
if (clipboard && typeof clipboard.writeText === "function") {
|
|
668
|
+
void Promise.resolve(clipboard.writeText(content)).catch(() => {
|
|
669
|
+
/* A denied clipboard permission is not worth an error state. */
|
|
670
|
+
});
|
|
671
|
+
} else if (textarea && typeof document.execCommand === "function") {
|
|
672
|
+
// No async clipboard outside a secure context (plain-HTTP installs).
|
|
673
|
+
const { selectionStart, selectionEnd } = textarea;
|
|
674
|
+
|
|
675
|
+
textarea.focus();
|
|
676
|
+
textarea.select();
|
|
677
|
+
|
|
678
|
+
try {
|
|
679
|
+
document.execCommand("copy");
|
|
680
|
+
} catch {
|
|
681
|
+
/* Nothing more to try. */
|
|
682
|
+
}
|
|
187
683
|
|
|
188
|
-
|
|
189
|
-
inputArea.setAttribute("aria-invalid", "true");
|
|
684
|
+
textarea.setSelectionRange(selectionStart, selectionEnd);
|
|
190
685
|
} else {
|
|
191
|
-
|
|
686
|
+
return;
|
|
192
687
|
}
|
|
688
|
+
|
|
689
|
+
setCopied(true);
|
|
690
|
+
|
|
691
|
+
if (copyResetTimer.current) {
|
|
692
|
+
clearTimeout(copyResetTimer.current);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
copyResetTimer.current = setTimeout(() => {
|
|
696
|
+
setCopied(false);
|
|
697
|
+
}, 2000);
|
|
193
698
|
};
|
|
194
699
|
|
|
195
|
-
type
|
|
700
|
+
type HandleKeyDownFunction = (
|
|
701
|
+
event: React.KeyboardEvent<HTMLTextAreaElement>,
|
|
702
|
+
) => void;
|
|
703
|
+
|
|
704
|
+
const handleKeyDown: HandleKeyDownFunction = (
|
|
705
|
+
event: React.KeyboardEvent<HTMLTextAreaElement>,
|
|
706
|
+
): void => {
|
|
707
|
+
// Leave composition (IME input) entirely to the browser.
|
|
708
|
+
if (event.nativeEvent.isComposing || event.keyCode === 229) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const key: string = event.key;
|
|
713
|
+
const command: boolean = event.ctrlKey || event.metaKey;
|
|
714
|
+
|
|
715
|
+
if (key === "Escape") {
|
|
716
|
+
/*
|
|
717
|
+
* Only the first Escape is ours. A second one is left alone, so a
|
|
718
|
+
* dialog around the editor still closes on Escape, Escape.
|
|
719
|
+
*/
|
|
720
|
+
if (!readOnly && !tabMovesFocusRef.current) {
|
|
721
|
+
event.preventDefault();
|
|
722
|
+
setTabRelease(true);
|
|
723
|
+
}
|
|
196
724
|
|
|
197
|
-
/*
|
|
198
|
-
* Per-model, not per-editor and emphatically not global: a literal tab is
|
|
199
|
-
* illegal as YAML indentation, and Monaco's model defaults are four spaces
|
|
200
|
-
* with indentation detected from the initial content - so a rule pasted in
|
|
201
|
-
* tab-indented would keep emitting tabs the parser rejects.
|
|
202
|
-
*/
|
|
203
|
-
const applyModelOptions: ApplyModelOptionsFunction = (editor: any): void => {
|
|
204
|
-
if (!isYaml) {
|
|
205
725
|
return;
|
|
206
726
|
}
|
|
207
727
|
|
|
208
|
-
|
|
728
|
+
if (key === "Tab") {
|
|
729
|
+
if (readOnly || tabMovesFocusRef.current || command || event.altKey) {
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
209
732
|
|
|
210
|
-
|
|
733
|
+
event.preventDefault();
|
|
734
|
+
runEdit(
|
|
735
|
+
event.shiftKey
|
|
736
|
+
? outdent(readState(), indentUnit)
|
|
737
|
+
: indent(readState(), indentUnit),
|
|
738
|
+
);
|
|
211
739
|
return;
|
|
212
740
|
}
|
|
213
741
|
|
|
214
|
-
|
|
215
|
-
|
|
742
|
+
if (!MODIFIER_KEYS.includes(key) && tabMovesFocusRef.current) {
|
|
743
|
+
setTabRelease(false);
|
|
744
|
+
}
|
|
216
745
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
746
|
+
if (readOnly) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (key === "Enter" && !command && !event.altKey) {
|
|
751
|
+
event.preventDefault();
|
|
752
|
+
runEdit(newLine(readState(), indentUnit, language.rules));
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (key === "Backspace" && !command && !event.altKey) {
|
|
757
|
+
const edit: TextEdit | null = backspace(
|
|
758
|
+
readState(),
|
|
759
|
+
indentUnit,
|
|
760
|
+
language.rules,
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
if (edit) {
|
|
764
|
+
event.preventDefault();
|
|
765
|
+
runEdit(edit);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (command && !event.altKey && (key === "/" || event.code === "Slash")) {
|
|
772
|
+
const edit: TextEdit | null = toggleLineComment(
|
|
773
|
+
readState(),
|
|
774
|
+
language.rules,
|
|
775
|
+
);
|
|
224
776
|
|
|
225
|
-
|
|
777
|
+
if (edit) {
|
|
778
|
+
event.preventDefault();
|
|
779
|
+
runEdit(edit);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
226
784
|
|
|
227
|
-
|
|
228
|
-
|
|
785
|
+
if (
|
|
786
|
+
command &&
|
|
787
|
+
!event.altKey &&
|
|
788
|
+
!event.shiftKey &&
|
|
789
|
+
(key === "]" || key === "[")
|
|
790
|
+
) {
|
|
791
|
+
event.preventDefault();
|
|
792
|
+
runEdit(
|
|
793
|
+
key === "]"
|
|
794
|
+
? indentLines(readState(), indentUnit)
|
|
795
|
+
: outdent(readState(), indentUnit),
|
|
796
|
+
);
|
|
229
797
|
return;
|
|
230
798
|
}
|
|
231
799
|
|
|
232
|
-
|
|
800
|
+
// Shift+Alt+F, as in VS Code. By code: Alt changes the key on a Mac.
|
|
801
|
+
if (event.shiftKey && event.altKey && !command && event.code === "KeyF") {
|
|
802
|
+
if (canFormat) {
|
|
803
|
+
event.preventDefault();
|
|
804
|
+
handleFormat();
|
|
805
|
+
}
|
|
233
806
|
|
|
234
|
-
if (!domNode) {
|
|
235
807
|
return;
|
|
236
808
|
}
|
|
237
809
|
|
|
238
|
-
|
|
239
|
-
|
|
810
|
+
/*
|
|
811
|
+
* A printable character. Ctrl+Alt is AltGr on Windows, which is how a
|
|
812
|
+
* German keyboard types a bracket, so only Ctrl or Cmd alone is a
|
|
813
|
+
* shortcut.
|
|
814
|
+
*/
|
|
815
|
+
const isShortcut: boolean =
|
|
816
|
+
event.metaKey || (event.ctrlKey && !event.altKey);
|
|
240
817
|
|
|
241
|
-
if (
|
|
242
|
-
|
|
818
|
+
if (key.length === 1 && !isShortcut) {
|
|
819
|
+
const edit: TextEdit | null = typeCharacter(
|
|
820
|
+
readState(),
|
|
821
|
+
key,
|
|
822
|
+
language.rules,
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
if (edit) {
|
|
826
|
+
event.preventDefault();
|
|
827
|
+
runEdit(edit);
|
|
828
|
+
}
|
|
243
829
|
}
|
|
244
830
|
};
|
|
245
831
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
applyModelOptions(editorRef.current);
|
|
250
|
-
// eslint-disable-next-line
|
|
251
|
-
}, [props.disableSpellCheck, props.type]);
|
|
832
|
+
type HandleGutterMouseDownFunction = (
|
|
833
|
+
event: React.MouseEvent<HTMLDivElement>,
|
|
834
|
+
) => void;
|
|
252
835
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
836
|
+
// A click on a line number selects that line, as in every other editor.
|
|
837
|
+
const handleGutterMouseDown: HandleGutterMouseDownFunction = (
|
|
838
|
+
event: React.MouseEvent<HTMLDivElement>,
|
|
839
|
+
): void => {
|
|
840
|
+
const textarea: HTMLTextAreaElement | null = textareaRef.current;
|
|
841
|
+
|
|
842
|
+
if (!textarea || event.button !== 0) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
event.preventDefault();
|
|
847
|
+
|
|
848
|
+
const bounds: DOMRect = event.currentTarget.getBoundingClientRect();
|
|
849
|
+
const line: number = Math.min(
|
|
850
|
+
lineCount,
|
|
851
|
+
Math.max(
|
|
852
|
+
1,
|
|
853
|
+
Math.floor(
|
|
854
|
+
(event.clientY - bounds.top - CODE_EDITOR_PADDING_Y_PX) /
|
|
855
|
+
CODE_EDITOR_LINE_HEIGHT_PX,
|
|
856
|
+
) + 1,
|
|
857
|
+
),
|
|
858
|
+
);
|
|
859
|
+
|
|
860
|
+
const value: string = textarea.value;
|
|
861
|
+
let start: number = 0;
|
|
862
|
+
|
|
863
|
+
for (let current: number = 1; current < line; current++) {
|
|
864
|
+
start = value.indexOf("\n", start) + 1;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
const end: number = lineEndOf(value, start);
|
|
868
|
+
|
|
869
|
+
textarea.focus();
|
|
870
|
+
textarea.setSelectionRange(start, end < value.length ? end + 1 : end);
|
|
871
|
+
captureSelection();
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
const status: StatusShape | null = useMemo(() => {
|
|
875
|
+
// Only once the check has caught up: a half-typed line is not an error yet.
|
|
876
|
+
const settled: boolean = !language.validator || checkedText === text;
|
|
877
|
+
|
|
878
|
+
if (props.error && settled) {
|
|
879
|
+
return { tone: "error", icon: IconProp.Error, message: props.error };
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (!syntax) {
|
|
883
|
+
return null;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
if (checkedText.trim() === "") {
|
|
887
|
+
return {
|
|
888
|
+
tone: "neutral",
|
|
889
|
+
icon: IconProp.Info,
|
|
890
|
+
message: "Nothing entered yet.",
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Nobody parsed it, so nothing may be claimed about it.
|
|
895
|
+
if (syntax.wasSkipped) {
|
|
896
|
+
return {
|
|
897
|
+
tone: "neutral",
|
|
898
|
+
icon: IconProp.Info,
|
|
899
|
+
message: "Contains template expressions — syntax not checked.",
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (syntax.isValid) {
|
|
904
|
+
const lines: number = countLines(checkedText);
|
|
905
|
+
|
|
906
|
+
return {
|
|
907
|
+
tone: "valid",
|
|
908
|
+
icon: IconProp.CheckCircle,
|
|
909
|
+
message: `Valid ${language.label} · ${lines} ${
|
|
910
|
+
lines === 1 ? "line" : "lines"
|
|
911
|
+
}`,
|
|
912
|
+
};
|
|
913
|
+
}
|
|
257
914
|
|
|
258
|
-
/*
|
|
259
|
-
* Memoised: @monaco-editor/react calls editor.updateOptions() whenever this
|
|
260
|
-
* object's identity changes, so an inline literal reconfigured the editor on
|
|
261
|
-
* every parent re-render - which for the indentation options below would
|
|
262
|
-
* reset them mid-keystroke.
|
|
263
|
-
*/
|
|
264
|
-
const editorOptions: Record<string, unknown> = useMemo(() => {
|
|
265
915
|
return {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
colorDecorators: true,
|
|
273
|
-
contextmenu: false,
|
|
274
|
-
cursorBlinking: "blink",
|
|
275
|
-
tabIndex: props.tabIndex || 0,
|
|
276
|
-
minimap: { enabled: false },
|
|
277
|
-
cursorStyle: "line",
|
|
278
|
-
disableLayerHinting: false,
|
|
279
|
-
disableMonospaceOptimizations: false,
|
|
280
|
-
dragAndDrop: false,
|
|
281
|
-
fixedOverflowWidgets: false,
|
|
282
|
-
folding: true,
|
|
283
|
-
foldingStrategy: "auto",
|
|
284
|
-
fontLigatures: false,
|
|
285
|
-
formatOnPaste: false,
|
|
286
|
-
formatOnType: false,
|
|
287
|
-
|
|
288
|
-
hideCursorInOverviewRuler: false,
|
|
289
|
-
links: true,
|
|
290
|
-
mouseWheelZoom: false,
|
|
291
|
-
multiCursorMergeOverlapping: true,
|
|
292
|
-
multiCursorModifier: "alt",
|
|
293
|
-
overviewRulerBorder: true,
|
|
294
|
-
overviewRulerLanes: 2,
|
|
295
|
-
quickSuggestions: false,
|
|
296
|
-
quickSuggestionsDelay: 100,
|
|
297
|
-
readOnly: props.readOnly || false,
|
|
298
|
-
renderControlCharacters: false,
|
|
299
|
-
/*
|
|
300
|
-
* Long scalars - a URL, a base64 blob, a Sigma `condition:` line - run
|
|
301
|
-
* off the right edge, and with wordWrap off for code there is no other
|
|
302
|
-
* way to reach them. Wrapping YAML instead is not an option: the
|
|
303
|
-
* indentation IS the syntax, and a wrapped line reads as a deeper one.
|
|
304
|
-
*/
|
|
305
|
-
scrollbar: {
|
|
306
|
-
horizontal: isYaml ? "auto" : "hidden",
|
|
307
|
-
},
|
|
308
|
-
renderLineHighlight: "all",
|
|
309
|
-
suggestOnTriggerCharacters: false,
|
|
310
|
-
/*
|
|
311
|
-
* In YAML whitespace is the syntax. Rendering it at the boundaries makes
|
|
312
|
-
* the indentation depth countable and, more importantly, makes a literal
|
|
313
|
-
* tab visible - tabs are illegal as YAML indentation and are otherwise
|
|
314
|
-
* indistinguishable from spaces.
|
|
315
|
-
*/
|
|
316
|
-
renderWhitespace: isYaml ? "boundary" : "none",
|
|
317
|
-
revealHorizontalRightPadding: 30,
|
|
318
|
-
roundedSelection: true,
|
|
319
|
-
rulers: [],
|
|
320
|
-
scrollBeyondLastColumn: 5,
|
|
321
|
-
// Half a short editor as blank runway is wasted space in a form field.
|
|
322
|
-
scrollBeyondLastLine: !isYaml,
|
|
323
|
-
selectOnLineNumbers: true,
|
|
324
|
-
/*
|
|
325
|
-
* Every YAML parse error - ours and the server's - is reported as a
|
|
326
|
-
* line and column, so the gutter is what makes the message actionable.
|
|
327
|
-
*/
|
|
328
|
-
lineNumbers: props.showLineNumbers || isYaml ? "on" : "off",
|
|
329
|
-
selectionClipboard: true,
|
|
330
|
-
selectionHighlight: true,
|
|
331
|
-
showFoldingControls: "mouseover",
|
|
332
|
-
smoothScrolling: false,
|
|
333
|
-
wordBasedSuggestions: "off",
|
|
334
|
-
wordWrap: props.type === CodeType.Markdown ? "on" : "off",
|
|
335
|
-
tabCompletion: "off",
|
|
336
|
-
// The YAML chrome owns the padding, so the editor supplies its own.
|
|
337
|
-
padding: isYaml ? { top: 10, bottom: 10 } : undefined,
|
|
338
|
-
/*
|
|
339
|
-
* tabSize / insertSpaces / detectIndentation deliberately do NOT belong
|
|
340
|
-
* here. They are IGlobalEditorOptions: monaco's standalone editor pipes
|
|
341
|
-
* whatever it is constructed with through updateConfigurationService,
|
|
342
|
-
* which writes every registered `editor.*` key into a PROCESS-WIDE
|
|
343
|
-
* config service - and those three are exactly the ones ModelService
|
|
344
|
-
* reads back, pushing them onto every model on the page. Setting them
|
|
345
|
-
* here would mean the last editor to mount dictates the indentation of
|
|
346
|
-
* all the others. They are applied to this editor's own model instead,
|
|
347
|
-
* in applyModelOptions below.
|
|
348
|
-
*/
|
|
916
|
+
tone: "invalid",
|
|
917
|
+
icon: IconProp.Alert,
|
|
918
|
+
message:
|
|
919
|
+
language.validator === "json"
|
|
920
|
+
? describeJsonSyntaxError(syntax as JsonSyntaxCheckResult)
|
|
921
|
+
: describeYamlSyntaxError(syntax as YamlSyntaxCheckResult),
|
|
349
922
|
};
|
|
350
|
-
}, [
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
923
|
+
}, [props.error, syntax, checkedText, text, language]);
|
|
924
|
+
|
|
925
|
+
const isEmpty: boolean = text === "";
|
|
926
|
+
const showExampleButton: boolean = Boolean(example) && isEmpty && !readOnly;
|
|
927
|
+
|
|
928
|
+
const spellCheck: boolean = language.spellCheckFollowsCaller
|
|
929
|
+
? !props.disableSpellCheck
|
|
930
|
+
: false;
|
|
931
|
+
|
|
932
|
+
const describedBy: string =
|
|
933
|
+
[
|
|
934
|
+
props.ariaDescribedby,
|
|
935
|
+
props.hint ? hintId : null,
|
|
936
|
+
readOnly ? null : keysHintId,
|
|
937
|
+
status ? statusId : null,
|
|
938
|
+
]
|
|
939
|
+
.filter(Boolean)
|
|
940
|
+
.join(" ") || "";
|
|
941
|
+
|
|
942
|
+
const rootClassName: string = [
|
|
943
|
+
"ou-code-editor overflow-hidden rounded-md border bg-white shadow-sm transition-shadow",
|
|
944
|
+
props.error
|
|
945
|
+
? "border-red-300 focus-within:border-red-500 focus-within:ring-1 focus-within:ring-red-500"
|
|
946
|
+
: "border-gray-300 focus-within:border-indigo-500 focus-within:ring-1 focus-within:ring-indigo-500",
|
|
947
|
+
language.wrap ? "ou-code-editor--wrap" : "",
|
|
948
|
+
readOnly ? "ou-code-editor--readonly" : "",
|
|
949
|
+
props.className || "",
|
|
950
|
+
]
|
|
951
|
+
.filter(Boolean)
|
|
952
|
+
.join(" ");
|
|
953
|
+
|
|
954
|
+
const minHeight: number =
|
|
955
|
+
(props.minLines ?? CODE_EDITOR_DEFAULT_MIN_LINES) *
|
|
956
|
+
CODE_EDITOR_LINE_HEIGHT_PX +
|
|
957
|
+
2 * CODE_EDITOR_PADDING_Y_PX;
|
|
958
|
+
|
|
959
|
+
const scrollerStyle: React.CSSProperties = props.height
|
|
960
|
+
? { height: props.height }
|
|
961
|
+
: { maxHeight: props.maxHeight || CODE_EDITOR_DEFAULT_MAX_HEIGHT };
|
|
357
962
|
|
|
358
963
|
return (
|
|
359
964
|
<div
|
|
965
|
+
className={rootClassName}
|
|
360
966
|
data-testid={props.dataTestId}
|
|
361
|
-
|
|
362
|
-
onClick={
|
|
363
|
-
if (props.onClick) {
|
|
364
|
-
props.onClick();
|
|
365
|
-
}
|
|
366
|
-
if (props.onFocus) {
|
|
367
|
-
props.onFocus();
|
|
368
|
-
}
|
|
369
|
-
}}
|
|
967
|
+
data-code-type={props.type}
|
|
968
|
+
onClick={props.onClick}
|
|
370
969
|
>
|
|
371
|
-
|
|
372
|
-
<
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
970
|
+
<div className="flex items-center justify-between gap-3 border-b border-gray-200 bg-gray-50 py-1 pl-3 pr-1.5">
|
|
971
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
972
|
+
<span
|
|
973
|
+
data-testid="code-editor-language"
|
|
974
|
+
className="inline-flex shrink-0 items-center rounded border border-indigo-100 bg-indigo-50 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-indigo-700"
|
|
975
|
+
>
|
|
976
|
+
{language.label}
|
|
977
|
+
</span>
|
|
978
|
+
{readOnly && (
|
|
979
|
+
<span
|
|
980
|
+
data-testid="code-editor-read-only"
|
|
981
|
+
className="inline-flex shrink-0 items-center gap-1 text-xs text-gray-500"
|
|
982
|
+
>
|
|
983
|
+
<Icon icon={IconProp.Lock} className="h-3 w-3" />
|
|
984
|
+
Read-only
|
|
985
|
+
</span>
|
|
986
|
+
)}
|
|
987
|
+
{props.hint && (
|
|
988
|
+
<span
|
|
989
|
+
id={hintId}
|
|
990
|
+
data-testid="code-editor-hint"
|
|
991
|
+
className="truncate text-xs text-gray-500"
|
|
992
|
+
>
|
|
993
|
+
{props.hint}
|
|
994
|
+
</span>
|
|
995
|
+
)}
|
|
996
|
+
</div>
|
|
997
|
+
|
|
998
|
+
<div className="flex shrink-0 items-center gap-0.5">
|
|
999
|
+
{showExampleButton && (
|
|
1000
|
+
<button
|
|
1001
|
+
type="button"
|
|
1002
|
+
data-testid="code-editor-example-button"
|
|
1003
|
+
className={TOOLBAR_BUTTON_CLASS}
|
|
1004
|
+
onClick={() => {
|
|
1005
|
+
replaceDocument(example);
|
|
1006
|
+
}}
|
|
1007
|
+
>
|
|
1008
|
+
<Icon icon={IconProp.Code} className="h-3.5 w-3.5" />
|
|
1009
|
+
Insert example
|
|
1010
|
+
</button>
|
|
1011
|
+
)}
|
|
1012
|
+
{language.canFormat && !readOnly && (
|
|
1013
|
+
<button
|
|
1014
|
+
type="button"
|
|
1015
|
+
data-testid="code-editor-format-button"
|
|
1016
|
+
title={
|
|
1017
|
+
canFormat
|
|
1018
|
+
? "Format the document (Shift+Alt+F)"
|
|
1019
|
+
: "Format is available once the document is valid JSON"
|
|
1020
|
+
}
|
|
1021
|
+
disabled={!canFormat}
|
|
1022
|
+
className={TOOLBAR_BUTTON_CLASS}
|
|
1023
|
+
onClick={handleFormat}
|
|
1024
|
+
>
|
|
1025
|
+
<Icon icon={IconProp.Indent} className="h-3.5 w-3.5" />
|
|
1026
|
+
Format
|
|
1027
|
+
</button>
|
|
1028
|
+
)}
|
|
1029
|
+
<button
|
|
1030
|
+
type="button"
|
|
1031
|
+
data-testid="code-editor-copy-button"
|
|
1032
|
+
aria-label={
|
|
1033
|
+
copied
|
|
1034
|
+
? `${language.label} copied to clipboard`
|
|
1035
|
+
: `Copy ${language.label} to clipboard`
|
|
1036
|
+
}
|
|
1037
|
+
disabled={isEmpty}
|
|
1038
|
+
className={TOOLBAR_BUTTON_CLASS}
|
|
1039
|
+
onClick={handleCopy}
|
|
1040
|
+
>
|
|
1041
|
+
<Icon
|
|
1042
|
+
icon={copied ? IconProp.Check : IconProp.Copy}
|
|
1043
|
+
className="h-3.5 w-3.5"
|
|
1044
|
+
/>
|
|
1045
|
+
{copied ? "Copied" : "Copy"}
|
|
1046
|
+
</button>
|
|
1047
|
+
</div>
|
|
1048
|
+
</div>
|
|
1049
|
+
|
|
1050
|
+
<div
|
|
1051
|
+
ref={scrollerRef}
|
|
1052
|
+
data-testid="code-editor-scroller"
|
|
1053
|
+
className="ou-code-editor__scroller"
|
|
1054
|
+
style={scrollerStyle}
|
|
1055
|
+
>
|
|
1056
|
+
<div className="ou-code-editor__content">
|
|
1057
|
+
{showGutter && (
|
|
1058
|
+
<div
|
|
1059
|
+
ref={gutterRef}
|
|
1060
|
+
aria-hidden="true"
|
|
1061
|
+
data-testid="code-editor-gutter"
|
|
1062
|
+
className="ou-code-editor__gutter"
|
|
1063
|
+
style={{
|
|
1064
|
+
minWidth: `calc(${String(lineCount).length + 1}ch + 22px)`,
|
|
1065
|
+
}}
|
|
1066
|
+
onMouseDown={handleGutterMouseDown}
|
|
1067
|
+
>
|
|
1068
|
+
{gutterSegments.map((segment: GutterSegment, index: number) => {
|
|
1069
|
+
return segment.className ? (
|
|
1070
|
+
<span key={index} className={segment.className}>
|
|
1071
|
+
{segment.text}
|
|
1072
|
+
</span>
|
|
1073
|
+
) : (
|
|
1074
|
+
<React.Fragment key={index}>{segment.text}</React.Fragment>
|
|
1075
|
+
);
|
|
1076
|
+
})}
|
|
1077
|
+
</div>
|
|
1078
|
+
)}
|
|
1079
|
+
|
|
1080
|
+
<div className="ou-code-editor__code" style={{ minHeight }}>
|
|
1081
|
+
<span
|
|
1082
|
+
ref={measureRef}
|
|
1083
|
+
aria-hidden="true"
|
|
1084
|
+
className="ou-code-editor__measure"
|
|
1085
|
+
>
|
|
1086
|
+
{"0".repeat(MEASURE_LENGTH)}
|
|
1087
|
+
</span>
|
|
1088
|
+
{showActiveLine && !language.wrap && (
|
|
1089
|
+
<div
|
|
1090
|
+
aria-hidden="true"
|
|
1091
|
+
data-testid="code-editor-active-line"
|
|
1092
|
+
className="ou-code-editor__active-line"
|
|
1093
|
+
style={{
|
|
1094
|
+
top:
|
|
1095
|
+
CODE_EDITOR_PADDING_Y_PX +
|
|
1096
|
+
(cursor.line - 1) * CODE_EDITOR_LINE_HEIGHT_PX,
|
|
1097
|
+
}}
|
|
1098
|
+
/>
|
|
1099
|
+
)}
|
|
1100
|
+
|
|
1101
|
+
{/*
|
|
1102
|
+
* A div, not a <pre>: the HTML parser drops a newline straight
|
|
1103
|
+
* after <pre>, and one character missing here shifts every line
|
|
1104
|
+
* under the textarea.
|
|
1105
|
+
*/}
|
|
1106
|
+
<div
|
|
1107
|
+
aria-hidden="true"
|
|
1108
|
+
data-testid="code-editor-highlight"
|
|
1109
|
+
className="ou-code-editor__layer ou-code-editor__highlight"
|
|
1110
|
+
dangerouslySetInnerHTML={{
|
|
1111
|
+
/*
|
|
1112
|
+
* The trailing newline gives a document that ends in one the
|
|
1113
|
+
* empty last line the textarea shows; a final line break on
|
|
1114
|
+
* its own draws nothing.
|
|
1115
|
+
*/
|
|
1116
|
+
__html: (ghostHtml ?? highlightedHtml) + "\n",
|
|
1117
|
+
}}
|
|
1118
|
+
/>
|
|
377
1119
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
1120
|
+
<textarea
|
|
1121
|
+
ref={textareaRef}
|
|
1122
|
+
id={props.id}
|
|
1123
|
+
data-testid="code-editor-input"
|
|
1124
|
+
className="ou-code-editor__layer ou-code-editor__input"
|
|
1125
|
+
value={text}
|
|
1126
|
+
readOnly={readOnly}
|
|
1127
|
+
tabIndex={props.tabIndex}
|
|
1128
|
+
placeholder={props.placeholder}
|
|
1129
|
+
wrap={language.wrap ? "soft" : "off"}
|
|
1130
|
+
spellCheck={spellCheck}
|
|
1131
|
+
autoCapitalize="off"
|
|
1132
|
+
autoComplete="off"
|
|
1133
|
+
autoCorrect="off"
|
|
1134
|
+
data-gramm="false"
|
|
1135
|
+
data-gramm_editor="false"
|
|
1136
|
+
data-enable-grammarly="false"
|
|
1137
|
+
/*
|
|
1138
|
+
* A fallback name only: aria-label outranks a <label for> as
|
|
1139
|
+
* well as aria-labelledby, so it would hide either one.
|
|
1140
|
+
*/
|
|
1141
|
+
aria-label={
|
|
1142
|
+
props.ariaLabelledby || props.id
|
|
1143
|
+
? undefined
|
|
1144
|
+
: `${language.label} editor`
|
|
1145
|
+
}
|
|
1146
|
+
aria-labelledby={props.ariaLabelledby}
|
|
1147
|
+
aria-describedby={describedBy || undefined}
|
|
1148
|
+
aria-invalid={props.ariaInvalid || props.error ? true : undefined}
|
|
1149
|
+
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
1150
|
+
const next: string = event.target.value;
|
|
1151
|
+
|
|
1152
|
+
textRef.current = next;
|
|
1153
|
+
setText(next);
|
|
1154
|
+
captureSelection();
|
|
1155
|
+
|
|
1156
|
+
if (props.onChange) {
|
|
1157
|
+
props.onChange(next);
|
|
1158
|
+
}
|
|
1159
|
+
}}
|
|
1160
|
+
onKeyDown={handleKeyDown}
|
|
1161
|
+
onSelect={captureSelection}
|
|
1162
|
+
onKeyUp={captureSelection}
|
|
1163
|
+
onMouseUp={captureSelection}
|
|
1164
|
+
onFocus={() => {
|
|
1165
|
+
setIsFocused(true);
|
|
1166
|
+
setTabRelease(false);
|
|
1167
|
+
captureSelection();
|
|
1168
|
+
|
|
1169
|
+
if (props.onFocus) {
|
|
1170
|
+
props.onFocus();
|
|
1171
|
+
}
|
|
1172
|
+
}}
|
|
1173
|
+
onBlur={() => {
|
|
1174
|
+
setIsFocused(false);
|
|
1175
|
+
setTabRelease(false);
|
|
1176
|
+
|
|
1177
|
+
if (props.onBlur) {
|
|
1178
|
+
props.onBlur();
|
|
1179
|
+
}
|
|
1180
|
+
}}
|
|
1181
|
+
onScroll={(event: React.UIEvent<HTMLTextAreaElement>) => {
|
|
1182
|
+
/*
|
|
1183
|
+
* The textarea is always exactly as big as its text, and the
|
|
1184
|
+
* scroller around it does the scrolling. Should it ever scroll
|
|
1185
|
+
* itself, its glyphs would slide off the colours under them.
|
|
1186
|
+
*/
|
|
1187
|
+
const target: HTMLTextAreaElement = event.currentTarget;
|
|
1188
|
+
|
|
1189
|
+
if (target.scrollTop !== 0 || target.scrollLeft !== 0) {
|
|
1190
|
+
target.scrollTop = 0;
|
|
1191
|
+
target.scrollLeft = 0;
|
|
1192
|
+
}
|
|
1193
|
+
}}
|
|
1194
|
+
/>
|
|
1195
|
+
</div>
|
|
1196
|
+
</div>
|
|
1197
|
+
</div>
|
|
1198
|
+
|
|
1199
|
+
<div className="flex items-center justify-between gap-3 border-t border-gray-200 bg-gray-50 px-3 py-1 text-xs">
|
|
1200
|
+
<div
|
|
1201
|
+
id={statusId}
|
|
1202
|
+
data-testid="code-editor-status"
|
|
1203
|
+
role="status"
|
|
1204
|
+
aria-live="polite"
|
|
1205
|
+
className={`flex min-w-0 items-center gap-1.5 ${
|
|
1206
|
+
status ? STATUS_TONE_CLASS[status.tone] : "text-gray-500"
|
|
1207
|
+
}`}
|
|
1208
|
+
>
|
|
1209
|
+
{status && (
|
|
1210
|
+
<>
|
|
1211
|
+
<Icon icon={status.icon} className="h-3.5 w-3.5 shrink-0" />
|
|
1212
|
+
<span className="min-w-0 break-words">{status.message}</span>
|
|
1213
|
+
</>
|
|
1214
|
+
)}
|
|
1215
|
+
</div>
|
|
1216
|
+
|
|
1217
|
+
<div
|
|
1218
|
+
data-testid="code-editor-cursor"
|
|
1219
|
+
className="flex shrink-0 items-center gap-2 tabular-nums text-gray-500"
|
|
1220
|
+
>
|
|
1221
|
+
{tabMovesFocus && isFocused && (
|
|
1222
|
+
<span
|
|
1223
|
+
data-testid="code-editor-tab-released"
|
|
1224
|
+
className="rounded border border-gray-200 bg-white px-1.5 text-gray-600"
|
|
1225
|
+
>
|
|
1226
|
+
Tab moves focus
|
|
1227
|
+
</span>
|
|
1228
|
+
)}
|
|
1229
|
+
<span>
|
|
1230
|
+
Ln {cursor.line}, Col {cursor.column}
|
|
1231
|
+
{selectedCharacters > 0 ? ` (${selectedCharacters} selected)` : ""}
|
|
1232
|
+
</span>
|
|
1233
|
+
</div>
|
|
1234
|
+
</div>
|
|
1235
|
+
|
|
1236
|
+
{!readOnly && (
|
|
1237
|
+
<span id={keysHintId} className="sr-only">
|
|
1238
|
+
{CODE_EDITOR_KEYS_HINT}
|
|
1239
|
+
</span>
|
|
414
1240
|
)}
|
|
1241
|
+
|
|
1242
|
+
{/*
|
|
1243
|
+
* The copy button's own name changes, but a button name is not a live
|
|
1244
|
+
* region - nothing would speak the confirmation. This does.
|
|
1245
|
+
*/}
|
|
1246
|
+
<span role="status" aria-live="polite" className="sr-only">
|
|
1247
|
+
{copied ? `${language.label} copied to clipboard` : ""}
|
|
1248
|
+
</span>
|
|
415
1249
|
</div>
|
|
416
1250
|
);
|
|
417
1251
|
};
|