@pgcorp/ui-kit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/docs/accessibility.md +27 -0
- package/docs/getting-started.md +80 -0
- package/docs/licensing.md +30 -0
- package/docs/public-api.md +39 -0
- package/docs/security.md +21 -0
- package/docs/theming.md +35 -0
- package/docs/versioning.md +23 -0
- package/index.ts +3 -0
- package/package.json +592 -0
- package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
- package/src/components/icons/sputnigUiIcons.ts +530 -0
- package/src/components/layout/SAppShell.vue +232 -0
- package/src/components/layout/SDockRegion.vue +200 -0
- package/src/components/layout/SResizeHandle.vue +251 -0
- package/src/components/layout/SSideMenuButton.vue +104 -0
- package/src/components/layout/SSideMenuRail.vue +47 -0
- package/src/components/layout/SStack.css +25 -0
- package/src/components/layout/SStack.vue +57 -0
- package/src/components/layout/SStatusBarShell.css +15 -0
- package/src/components/layout/SStatusBarShell.vue +26 -0
- package/src/components/layout/SWorkbenchLayout.vue +53 -0
- package/src/components/layout/SWorkbenchPanelShell.css +161 -0
- package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
- package/src/components/layout/_internal/SAppBrand.css +45 -0
- package/src/components/layout/_internal/SAppBrand.vue +37 -0
- package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
- package/src/components/layout/types.ts +28 -0
- package/src/components/shared/_internal/SDialogSurface.css +127 -0
- package/src/components/shared/_internal/SDialogSurface.vue +117 -0
- package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
- package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
- package/src/components/shared/_internal/SListboxGroup.css +18 -0
- package/src/components/shared/_internal/SListboxGroup.vue +24 -0
- package/src/components/shared/_internal/SMenuItem.css +112 -0
- package/src/components/shared/_internal/SMenuItem.vue +213 -0
- package/src/components/shared/_internal/SMenuLayer.css +21 -0
- package/src/components/shared/_internal/SMenuLayer.vue +48 -0
- package/src/components/shared/_internal/SMenuSeparator.css +5 -0
- package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
- package/src/components/shared/_internal/SToastItem.css +65 -0
- package/src/components/shared/_internal/SToastItem.vue +58 -0
- package/src/components/shared/_internal/STreeNode.vue +761 -0
- package/src/components/shared/_internal/dialogSurface.ts +105 -0
- package/src/components/shared/_internal/focusTrap.ts +40 -0
- package/src/components/shared/_internal/listboxContext.ts +16 -0
- package/src/components/shared/_internal/markerContract.ts +10 -0
- package/src/components/shared/_internal/menuContext.ts +23 -0
- package/src/components/shared/_internal/selectionContract.ts +239 -0
- package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
- package/src/components/shared/_internal/tabsContext.ts +57 -0
- package/src/components/shared/_internal/treeRuntime.ts +203 -0
- package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
- package/src/components/shared/codeLanguages.ts +168 -0
- package/src/components/shared/complex/SKanbanCard.css +8 -0
- package/src/components/shared/complex/SKanbanCard.vue +76 -0
- package/src/components/shared/complex/SKanbanLane.vue +82 -0
- package/src/components/shared/complex/STree.css +24 -0
- package/src/components/shared/complex/STree.vue +288 -0
- package/src/components/shared/complex/treeAdapter.ts +210 -0
- package/src/components/shared/complex/types.ts +102 -0
- package/src/components/shared/containers/SCloudChrome.css +103 -0
- package/src/components/shared/containers/SCloudChrome.vue +87 -0
- package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
- package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
- package/src/components/shared/containers/SCloudChromeRow.css +78 -0
- package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
- package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
- package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
- package/src/components/shared/containers/SDrawer.css +60 -0
- package/src/components/shared/containers/SDrawer.vue +86 -0
- package/src/components/shared/containers/SFormGrid.css +26 -0
- package/src/components/shared/containers/SFormGrid.vue +46 -0
- package/src/components/shared/containers/SFormGridItem.css +18 -0
- package/src/components/shared/containers/SFormGridItem.vue +29 -0
- package/src/components/shared/containers/SLeftSidebar.vue +74 -0
- package/src/components/shared/containers/SModal.css +27 -0
- package/src/components/shared/containers/SModal.vue +105 -0
- package/src/components/shared/containers/SPageHeader.css +66 -0
- package/src/components/shared/containers/SPageHeader.vue +45 -0
- package/src/components/shared/containers/SPanel.css +134 -0
- package/src/components/shared/containers/SPanel.vue +169 -0
- package/src/components/shared/containers/SPopover.css +35 -0
- package/src/components/shared/containers/SPopover.vue +551 -0
- package/src/components/shared/containers/SSidebarGroup.css +12 -0
- package/src/components/shared/containers/SSidebarGroup.vue +443 -0
- package/src/components/shared/containers/SSidebarSection.css +41 -0
- package/src/components/shared/containers/SSidebarSection.vue +268 -0
- package/src/components/shared/containers/disclosureSummary.ts +48 -0
- package/src/components/shared/containers/popover.ts +72 -0
- package/src/components/shared/containers/sidebar.ts +206 -0
- package/src/components/shared/controls/SButton.css +274 -0
- package/src/components/shared/controls/SButton.vue +211 -0
- package/src/components/shared/controls/SCheckbox.css +65 -0
- package/src/components/shared/controls/SCheckbox.vue +130 -0
- package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
- package/src/components/shared/controls/SChoiceCards.css +29 -0
- package/src/components/shared/controls/SChoiceCards.vue +145 -0
- package/src/components/shared/controls/SColorSelect.vue +146 -0
- package/src/components/shared/controls/SComboboxTrigger.css +35 -0
- package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
- package/src/components/shared/controls/SContextMenu.css +11 -0
- package/src/components/shared/controls/SContextMenu.vue +186 -0
- package/src/components/shared/controls/SContextToggleButton.vue +64 -0
- package/src/components/shared/controls/SDragHandle.vue +100 -0
- package/src/components/shared/controls/SDropdownMenu.css +5 -0
- package/src/components/shared/controls/SDropdownMenu.vue +188 -0
- package/src/components/shared/controls/SField.css +79 -0
- package/src/components/shared/controls/SField.vue +89 -0
- package/src/components/shared/controls/SFieldGroup.css +58 -0
- package/src/components/shared/controls/SFieldGroup.vue +117 -0
- package/src/components/shared/controls/SFieldLabel.css +12 -0
- package/src/components/shared/controls/SFieldLabel.vue +51 -0
- package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
- package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
- package/src/components/shared/controls/SInputText.css +66 -0
- package/src/components/shared/controls/SInputText.vue +321 -0
- package/src/components/shared/controls/SInteractiveSurface.css +72 -0
- package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
- package/src/components/shared/controls/SLink.css +114 -0
- package/src/components/shared/controls/SLink.ts +85 -0
- package/src/components/shared/controls/SLink.vue +114 -0
- package/src/components/shared/controls/SListbox.css +31 -0
- package/src/components/shared/controls/SListbox.vue +388 -0
- package/src/components/shared/controls/SListboxOption.css +63 -0
- package/src/components/shared/controls/SListboxOption.vue +85 -0
- package/src/components/shared/controls/SMarker.css +19 -0
- package/src/components/shared/controls/SMarker.vue +33 -0
- package/src/components/shared/controls/SMenuSurface.css +20 -0
- package/src/components/shared/controls/SMenuSurface.vue +333 -0
- package/src/components/shared/controls/SSegmentedControl.css +38 -0
- package/src/components/shared/controls/SSegmentedControl.vue +134 -0
- package/src/components/shared/controls/SSelect.css +12 -0
- package/src/components/shared/controls/SSelect.vue +338 -0
- package/src/components/shared/controls/STextarea.css +31 -0
- package/src/components/shared/controls/STextarea.vue +316 -0
- package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
- package/src/components/shared/controls/menu.ts +178 -0
- package/src/components/shared/data-display/SActionCard.css +6 -0
- package/src/components/shared/data-display/SActionCard.vue +49 -0
- package/src/components/shared/data-display/SActionList.css +15 -0
- package/src/components/shared/data-display/SActionList.vue +34 -0
- package/src/components/shared/data-display/SActionListItem.css +114 -0
- package/src/components/shared/data-display/SActionListItem.vue +230 -0
- package/src/components/shared/data-display/SBadge.css +73 -0
- package/src/components/shared/data-display/SBadge.vue +52 -0
- package/src/components/shared/data-display/SCodeBlock.css +56 -0
- package/src/components/shared/data-display/SCodeBlock.vue +188 -0
- package/src/components/shared/data-display/SCodeEditor.css +178 -0
- package/src/components/shared/data-display/SCodeEditor.vue +274 -0
- package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
- package/src/components/shared/data-display/SCopyField.vue +77 -0
- package/src/components/shared/data-display/SDocBlock.css +136 -0
- package/src/components/shared/data-display/SDocBlock.vue +358 -0
- package/src/components/shared/data-display/SEmptyState.css +42 -0
- package/src/components/shared/data-display/SEmptyState.vue +79 -0
- package/src/components/shared/data-display/SExpandableText.vue +314 -0
- package/src/components/shared/data-display/SJsonTree.css +6 -0
- package/src/components/shared/data-display/SJsonTree.vue +111 -0
- package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
- package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
- package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
- package/src/components/shared/data-display/SMessage.css +88 -0
- package/src/components/shared/data-display/SMessage.vue +152 -0
- package/src/components/shared/data-display/SMetricCard.css +32 -0
- package/src/components/shared/data-display/SMetricCard.vue +117 -0
- package/src/components/shared/data-display/SProgressBar.css +84 -0
- package/src/components/shared/data-display/SProgressBar.vue +114 -0
- package/src/components/shared/data-display/SProgressIndicator.css +59 -0
- package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
- package/src/components/shared/data-display/SSectionHeader.css +84 -0
- package/src/components/shared/data-display/SSectionHeader.vue +58 -0
- package/src/components/shared/data-display/SStatus.css +90 -0
- package/src/components/shared/data-display/SStatus.vue +79 -0
- package/src/components/shared/data-display/STable.css +83 -0
- package/src/components/shared/data-display/STable.vue +339 -0
- package/src/components/shared/data-display/SToastContainer.css +53 -0
- package/src/components/shared/data-display/SToastContainer.vue +31 -0
- package/src/components/shared/data-display/STooltip.css +114 -0
- package/src/components/shared/data-display/STooltip.vue +550 -0
- package/src/components/shared/data-display/STooltipAnchor.css +21 -0
- package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
- package/src/components/shared/data-display/SVirtualList.css +24 -0
- package/src/components/shared/data-display/SVirtualList.ts +19 -0
- package/src/components/shared/data-display/SVirtualList.vue +361 -0
- package/src/components/shared/data-display/json.ts +136 -0
- package/src/components/shared/data-display/table.ts +76 -0
- package/src/components/shared/database/SDataGrid.css +3 -0
- package/src/components/shared/database/SDataGrid.vue +464 -0
- package/src/components/shared/database/SSqlEditor.vue +363 -0
- package/src/components/shared/database/dataGrid.ts +61 -0
- package/src/components/shared/feedback/SAsyncState.css +26 -0
- package/src/components/shared/feedback/SAsyncState.vue +190 -0
- package/src/components/shared/graph/SGraphViewport.ts +1 -0
- package/src/components/shared/graph/SGraphViewport.vue +858 -0
- package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
- package/src/components/shared/markdown.ts +169 -0
- package/src/components/shared/navigation/SBottomNav.vue +692 -0
- package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
- package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
- package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
- package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
- package/src/components/shared/navigation/STab.vue +363 -0
- package/src/components/shared/navigation/STabList.vue +442 -0
- package/src/components/shared/navigation/STabPanel.vue +165 -0
- package/src/components/shared/navigation/STabPanels.vue +33 -0
- package/src/components/shared/navigation/STabs.vue +617 -0
- package/src/components/shared/navigation/SWizardSteps.css +69 -0
- package/src/components/shared/navigation/SWizardSteps.vue +171 -0
- package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
- package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
- package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
- package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
- package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
- package/src/components/shared/persona/_internal/inventory.ts +110 -0
- package/src/components/shared/persona/_internal/labels.ts +79 -0
- package/src/components/shared/persona/presentation.ts +371 -0
- package/src/components/shared/persona/types.ts +256 -0
- package/src/composables/uiPreferencesReset.ts +52 -0
- package/src/composables/useClipboard.ts +37 -0
- package/src/composables/useDialogOverlay.ts +85 -0
- package/src/composables/useDisclosure.ts +40 -0
- package/src/composables/useFloatingPosition.ts +245 -0
- package/src/composables/useNotifier.ts +26 -0
- package/src/composables/useSidebarPanelState.ts +610 -0
- package/src/internal/codeEditorContract.ts +110 -0
- package/src/internal/codeEditorExtensions.ts +27 -0
- package/src/internal/feedbackPresentation.ts +136 -0
- package/src/internal/fieldSurface.ts +59 -0
- package/src/internal/focusNavigation.ts +69 -0
- package/src/internal/inlineTokenEditorContract.ts +136 -0
- package/src/internal/interactiveElement.ts +33 -0
- package/src/internal/layerStack.ts +202 -0
- package/src/internal/linkTarget.ts +256 -0
- package/src/internal/ownedAttrs.ts +464 -0
- package/src/internal/passiveContentContract.ts +129 -0
- package/src/internal/pointerInteractionLease.ts +81 -0
- package/src/internal/runtimeContract.ts +194 -0
- package/src/internal/runtimeTheme.ts +159 -0
- package/src/internal/semanticSizing.ts +60 -0
- package/src/internal/useMenuLayer.ts +129 -0
- package/src/stores/useNotifierStore.ts +69 -0
- package/src/styles/reference.css +4 -0
- package/src/styles/style.css +151 -0
- package/src/styles/tokens.css +836 -0
- package/src/theme.ts +449 -0
- package/src/types/highlight-js-lib.d.ts +76 -0
- package/src/variants.ts +15 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
import { validateExactString } from '../../../internal/runtimeContract'
|
|
2
|
+
|
|
3
|
+
export type PersonaStatusSeverity = 'secondary' | 'success' | 'info' | 'warn' | 'danger'
|
|
4
|
+
|
|
5
|
+
export type PersonaParameterPresentation = {
|
|
6
|
+
name: string
|
|
7
|
+
type?: string | null
|
|
8
|
+
required?: boolean
|
|
9
|
+
default?: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type PersonaDiagnosticPresentationInput = {
|
|
13
|
+
context?: string | null
|
|
14
|
+
reason?: string | null
|
|
15
|
+
severity: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type PersonaDiagnosticPresentation = {
|
|
19
|
+
category: 'scenario_import' | 'component_analysis' | 'support_quality' | 'unclassified'
|
|
20
|
+
title: string
|
|
21
|
+
ownerLabel: string
|
|
22
|
+
impactLabel: string
|
|
23
|
+
remediation: string
|
|
24
|
+
statusLabel: string
|
|
25
|
+
statusSeverity: PersonaStatusSeverity
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type PersonaDiagnosticOverviewItem = {
|
|
29
|
+
category: PersonaDiagnosticPresentation['category']
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function personaDiagnosticPresentation(
|
|
33
|
+
input: PersonaDiagnosticPresentationInput,
|
|
34
|
+
): PersonaDiagnosticPresentation {
|
|
35
|
+
const isError = input.severity === 'error' || input.severity === 'blocking'
|
|
36
|
+
const statusLabel = isError ? 'ошибка' : input.severity === 'warning' ? 'предупреждение' : 'информация'
|
|
37
|
+
const statusSeverity: PersonaStatusSeverity = isError ? 'danger' : input.severity === 'warning' ? 'warn' : 'info'
|
|
38
|
+
|
|
39
|
+
if (input.context === 'native_scenarios') {
|
|
40
|
+
return {
|
|
41
|
+
category: 'scenario_import',
|
|
42
|
+
title: 'Сценарий не импортирован',
|
|
43
|
+
ownerLabel: 'Исправить в тестовом проекте',
|
|
44
|
+
impactLabel: 'Сценарий недоступен для запуска',
|
|
45
|
+
remediation: 'Восстановить отсутствующий модуль или исправить импорт в сценарии.',
|
|
46
|
+
statusLabel,
|
|
47
|
+
statusSeverity,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (input.context === 'components') {
|
|
51
|
+
return {
|
|
52
|
+
category: 'component_analysis',
|
|
53
|
+
title: 'Компонент не проанализирован',
|
|
54
|
+
ownerLabel: 'Исправить в тестовом проекте',
|
|
55
|
+
impactLabel: 'Структура связанных сценариев может быть неполной',
|
|
56
|
+
remediation: 'Исправить импорт компонента и его зависимостей.',
|
|
57
|
+
statusLabel,
|
|
58
|
+
statusSeverity,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (input.context === 'support_quality') {
|
|
62
|
+
const remediation = input.reason === 'page_support_import_non_maintained'
|
|
63
|
+
? 'Перенести используемый PageObject из пробной области в поддерживаемую область проекта.'
|
|
64
|
+
: 'Дать элементу или компоненту устойчивое предметное имя в коде проекта.'
|
|
65
|
+
return {
|
|
66
|
+
category: 'support_quality',
|
|
67
|
+
title: 'Качество PageObject',
|
|
68
|
+
ownerLabel: 'Исправить в тестовом проекте',
|
|
69
|
+
impactLabel: 'Не блокирует доступные сценарии',
|
|
70
|
+
remediation,
|
|
71
|
+
statusLabel,
|
|
72
|
+
statusSeverity,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
category: 'unclassified',
|
|
77
|
+
title: 'Неклассифицированное сообщение Persona',
|
|
78
|
+
ownerLabel: 'Требуется классификация Persona',
|
|
79
|
+
impactLabel: 'Влияние не указано',
|
|
80
|
+
remediation: 'Persona должна передать машинно-читаемые владельца, влияние и способ исправления.',
|
|
81
|
+
statusLabel,
|
|
82
|
+
statusSeverity,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function personaDiagnosticOverviewMessage(
|
|
87
|
+
availableTests: number,
|
|
88
|
+
unavailableTests: number,
|
|
89
|
+
diagnostics: readonly PersonaDiagnosticOverviewItem[],
|
|
90
|
+
): string {
|
|
91
|
+
const count = (category: PersonaDiagnosticOverviewItem['category']) => (
|
|
92
|
+
diagnostics.filter((diagnostic) => diagnostic.category === category).length
|
|
93
|
+
)
|
|
94
|
+
const totalTests = availableTests + unavailableTests
|
|
95
|
+
const scenarioImports = count('scenario_import')
|
|
96
|
+
const componentAnalysis = count('component_analysis')
|
|
97
|
+
const supportQuality = count('support_quality')
|
|
98
|
+
const parts = [
|
|
99
|
+
`Доступно ${availableTests} из ${totalTests} сценариев.`,
|
|
100
|
+
scenarioImports > 0 ? `${scenarioImports} сценариев не импортируются из-за ошибок в зависимостях тестового проекта.` : null,
|
|
101
|
+
componentAnalysis > 0
|
|
102
|
+
? `${componentAnalysis} сообщений относятся к анализу компонентов, остановленному отсутствующими модулями тестового проекта.`
|
|
103
|
+
: null,
|
|
104
|
+
supportQuality > 0 ? `${supportQuality} замечаний качества PageObject не блокируют доступные сценарии.` : null,
|
|
105
|
+
]
|
|
106
|
+
return parts.filter((part): part is string => Boolean(part)).join(' ')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const PERSONA_RUN_STATUSES = [
|
|
110
|
+
'cancelled',
|
|
111
|
+
'cancelling',
|
|
112
|
+
'completed',
|
|
113
|
+
'degraded',
|
|
114
|
+
'error',
|
|
115
|
+
'failed',
|
|
116
|
+
'passed',
|
|
117
|
+
'paused',
|
|
118
|
+
'queued',
|
|
119
|
+
'running',
|
|
120
|
+
'skipped',
|
|
121
|
+
'timed_out',
|
|
122
|
+
'timeout',
|
|
123
|
+
'info',
|
|
124
|
+
'warn',
|
|
125
|
+
'warning',
|
|
126
|
+
] as const
|
|
127
|
+
type PersonaRunStatus = typeof PERSONA_RUN_STATUSES[number]
|
|
128
|
+
const PERSONA_RUN_STATUS_PRESENTATIONS = {
|
|
129
|
+
cancelled: { label: 'отменён', severity: 'secondary' },
|
|
130
|
+
cancelling: { label: 'отменяется', severity: 'warn' },
|
|
131
|
+
completed: { label: 'завершён', severity: 'success' },
|
|
132
|
+
degraded: { label: 'требует внимания', severity: 'warn' },
|
|
133
|
+
error: { label: 'ошибка', severity: 'danger' },
|
|
134
|
+
failed: { label: 'ошибка', severity: 'danger' },
|
|
135
|
+
passed: { label: 'пройден', severity: 'success' },
|
|
136
|
+
paused: { label: 'приостановлен', severity: 'info' },
|
|
137
|
+
queued: { label: 'в очереди', severity: 'warn' },
|
|
138
|
+
running: { label: 'выполняется', severity: 'info' },
|
|
139
|
+
skipped: { label: 'пропущен', severity: 'warn' },
|
|
140
|
+
timed_out: { label: 'превышено время', severity: 'danger' },
|
|
141
|
+
timeout: { label: 'превышено время', severity: 'danger' },
|
|
142
|
+
info: { label: 'информация', severity: 'info' },
|
|
143
|
+
warn: { label: 'предупреждение', severity: 'warn' },
|
|
144
|
+
warning: { label: 'предупреждение', severity: 'warn' },
|
|
145
|
+
} as const satisfies Record<PersonaRunStatus, {
|
|
146
|
+
label: string
|
|
147
|
+
severity: PersonaStatusSeverity
|
|
148
|
+
}>
|
|
149
|
+
|
|
150
|
+
function resolvePersonaRunStatus(status: unknown): PersonaRunStatus {
|
|
151
|
+
return validateExactString('Persona presentation', 'run status', status, PERSONA_RUN_STATUSES)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function personaRunStatusLabel(status: unknown): string {
|
|
155
|
+
return PERSONA_RUN_STATUS_PRESENTATIONS[resolvePersonaRunStatus(status)].label
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function personaStatusSeverity(status: unknown): PersonaStatusSeverity {
|
|
159
|
+
return PERSONA_RUN_STATUS_PRESENTATIONS[resolvePersonaRunStatus(status)].severity
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const PERSONA_SEMANTIC_KINDS = [
|
|
163
|
+
'Action',
|
|
164
|
+
'CombinedStep',
|
|
165
|
+
'Expectation',
|
|
166
|
+
'Fact',
|
|
167
|
+
'Goal',
|
|
168
|
+
'Ops',
|
|
169
|
+
'Step',
|
|
170
|
+
'action',
|
|
171
|
+
'check',
|
|
172
|
+
'combined_step',
|
|
173
|
+
'expectation',
|
|
174
|
+
'fact',
|
|
175
|
+
'get',
|
|
176
|
+
'goal',
|
|
177
|
+
'make',
|
|
178
|
+
'persona_call',
|
|
179
|
+
'step',
|
|
180
|
+
] as const
|
|
181
|
+
type PersonaSemanticKind = typeof PERSONA_SEMANTIC_KINDS[number]
|
|
182
|
+
const PERSONA_SEMANTIC_KIND_LABELS = {
|
|
183
|
+
Action: 'Действие',
|
|
184
|
+
CombinedStep: 'Составной шаг',
|
|
185
|
+
Expectation: 'Проверка',
|
|
186
|
+
Fact: 'Данные',
|
|
187
|
+
Goal: 'Цель',
|
|
188
|
+
Ops: 'Служебная операция',
|
|
189
|
+
Step: 'Шаг',
|
|
190
|
+
action: 'Действие',
|
|
191
|
+
check: 'Проверка',
|
|
192
|
+
combined_step: 'Составной шаг',
|
|
193
|
+
expectation: 'Проверка',
|
|
194
|
+
fact: 'Данные',
|
|
195
|
+
get: 'Получение данных',
|
|
196
|
+
goal: 'Цель',
|
|
197
|
+
make: 'Действие',
|
|
198
|
+
persona_call: 'Вызов Persona',
|
|
199
|
+
step: 'Шаг',
|
|
200
|
+
} as const satisfies Record<PersonaSemanticKind, string>
|
|
201
|
+
|
|
202
|
+
export function personaSemanticKindLabel(kind: unknown): string {
|
|
203
|
+
if (kind === null || kind === undefined) return ''
|
|
204
|
+
const resolvedKind = validateExactString(
|
|
205
|
+
'Persona presentation',
|
|
206
|
+
'semantic kind',
|
|
207
|
+
kind,
|
|
208
|
+
PERSONA_SEMANTIC_KINDS,
|
|
209
|
+
)
|
|
210
|
+
return PERSONA_SEMANTIC_KIND_LABELS[resolvedKind]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function personaParameterSummary(parameters: readonly PersonaParameterPresentation[]): string {
|
|
214
|
+
return parameters.map((parameter) => {
|
|
215
|
+
const type = parameter.type ? `: ${parameter.type}` : ''
|
|
216
|
+
const hasPrimitiveDefault = typeof parameter.default === 'string'
|
|
217
|
+
|| typeof parameter.default === 'number'
|
|
218
|
+
|| typeof parameter.default === 'boolean'
|
|
219
|
+
const defaultValue = hasPrimitiveDefault ? ` = ${String(parameter.default)}` : ''
|
|
220
|
+
return `${parameter.name}${type}${defaultValue}${parameter.required ? ' (обязательный)' : ''}`
|
|
221
|
+
}).join(', ')
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const PERSONA_SEVERITIES = ['blocker', 'critical', 'minor', 'normal', 'trivial'] as const
|
|
225
|
+
type PersonaSeverity = typeof PERSONA_SEVERITIES[number]
|
|
226
|
+
const PERSONA_SEVERITY_PRESENTATIONS = {
|
|
227
|
+
blocker: { label: 'блокирующий', severity: 'danger' },
|
|
228
|
+
critical: { label: 'критический', severity: 'danger' },
|
|
229
|
+
minor: { label: 'низкая критичность', severity: 'secondary' },
|
|
230
|
+
normal: { label: 'обычная критичность', severity: 'info' },
|
|
231
|
+
trivial: { label: 'минимальная критичность', severity: 'secondary' },
|
|
232
|
+
} as const satisfies Record<PersonaSeverity, {
|
|
233
|
+
label: string
|
|
234
|
+
severity: PersonaStatusSeverity
|
|
235
|
+
}>
|
|
236
|
+
|
|
237
|
+
function resolvePersonaSeverity(value: unknown): PersonaSeverity {
|
|
238
|
+
return validateExactString('Persona presentation', 'severity', value, PERSONA_SEVERITIES)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function personaSeverityLabel(value: unknown): string {
|
|
242
|
+
return PERSONA_SEVERITY_PRESENTATIONS[resolvePersonaSeverity(value)].label
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function personaSeverityBadge(value: unknown): PersonaStatusSeverity {
|
|
246
|
+
return PERSONA_SEVERITY_PRESENTATIONS[resolvePersonaSeverity(value)].severity
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const PERSONA_REPORT_STATUSES = ['broken', 'failed', 'passed', 'skipped'] as const
|
|
250
|
+
type PersonaReportStatus = typeof PERSONA_REPORT_STATUSES[number]
|
|
251
|
+
const PERSONA_REPORT_STATUS_LABELS = {
|
|
252
|
+
broken: 'сломан',
|
|
253
|
+
failed: 'ошибка',
|
|
254
|
+
passed: 'пройден',
|
|
255
|
+
skipped: 'пропущен',
|
|
256
|
+
} as const satisfies Record<PersonaReportStatus, string>
|
|
257
|
+
|
|
258
|
+
export function personaReportStatusLabel(status: unknown): string {
|
|
259
|
+
const resolvedStatus = validateExactString(
|
|
260
|
+
'Persona presentation',
|
|
261
|
+
'report status',
|
|
262
|
+
status,
|
|
263
|
+
PERSONA_REPORT_STATUSES,
|
|
264
|
+
)
|
|
265
|
+
return PERSONA_REPORT_STATUS_LABELS[resolvedStatus]
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const PERSONA_LIFECYCLE_STAGES = [
|
|
269
|
+
'run_after_scenario',
|
|
270
|
+
'run_after_test',
|
|
271
|
+
'run_before_scenario',
|
|
272
|
+
'run_before_test',
|
|
273
|
+
] as const
|
|
274
|
+
type PersonaLifecycleStage = typeof PERSONA_LIFECYCLE_STAGES[number]
|
|
275
|
+
const PERSONA_LIFECYCLE_STAGE_LABELS = {
|
|
276
|
+
run_after_scenario: 'после сценария',
|
|
277
|
+
run_after_test: 'после теста',
|
|
278
|
+
run_before_scenario: 'перед сценарием',
|
|
279
|
+
run_before_test: 'перед тестом',
|
|
280
|
+
} as const satisfies Record<PersonaLifecycleStage, string>
|
|
281
|
+
|
|
282
|
+
export function personaLifecycleStageLabel(stage: unknown): string {
|
|
283
|
+
const resolvedStage = validateExactString(
|
|
284
|
+
'Persona presentation',
|
|
285
|
+
'lifecycle stage',
|
|
286
|
+
stage,
|
|
287
|
+
PERSONA_LIFECYCLE_STAGES,
|
|
288
|
+
)
|
|
289
|
+
return PERSONA_LIFECYCLE_STAGE_LABELS[resolvedStage]
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const PERSONA_EVENT_TYPES = [
|
|
293
|
+
'cancelled',
|
|
294
|
+
'completed',
|
|
295
|
+
'failed',
|
|
296
|
+
'log',
|
|
297
|
+
'queued',
|
|
298
|
+
'running',
|
|
299
|
+
'started',
|
|
300
|
+
'timeout',
|
|
301
|
+
'report.runtime_log',
|
|
302
|
+
'scenario.accepted',
|
|
303
|
+
'scenario.cancelled',
|
|
304
|
+
'scenario.finished',
|
|
305
|
+
'scenario.started',
|
|
306
|
+
'worker.started',
|
|
307
|
+
'worker.stopped',
|
|
308
|
+
] as const
|
|
309
|
+
type PersonaEventType = typeof PERSONA_EVENT_TYPES[number]
|
|
310
|
+
const PERSONA_EVENT_TYPE_LABELS = {
|
|
311
|
+
cancelled: 'Запуск отменён',
|
|
312
|
+
completed: 'Запуск завершён',
|
|
313
|
+
failed: 'Запуск завершён с ошибкой',
|
|
314
|
+
log: 'Сообщение выполнения',
|
|
315
|
+
queued: 'Запуск поставлен в очередь',
|
|
316
|
+
running: 'Запуск начат',
|
|
317
|
+
started: 'Запуск начат',
|
|
318
|
+
timeout: 'Время запуска превышено',
|
|
319
|
+
'report.runtime_log': 'Сообщение выполнения',
|
|
320
|
+
'scenario.accepted': 'Тест поставлен в очередь',
|
|
321
|
+
'scenario.cancelled': 'Тест отменён',
|
|
322
|
+
'scenario.finished': 'Тест завершён',
|
|
323
|
+
'scenario.started': 'Тест запущен',
|
|
324
|
+
'worker.started': 'Исполнитель подключён',
|
|
325
|
+
'worker.stopped': 'Исполнитель остановлен',
|
|
326
|
+
} as const satisfies Record<PersonaEventType, string>
|
|
327
|
+
|
|
328
|
+
export function personaEventTypeLabel(eventType: unknown): string {
|
|
329
|
+
const resolvedEventType = validateExactString(
|
|
330
|
+
'Persona presentation',
|
|
331
|
+
'event type',
|
|
332
|
+
eventType,
|
|
333
|
+
PERSONA_EVENT_TYPES,
|
|
334
|
+
)
|
|
335
|
+
return PERSONA_EVENT_TYPE_LABELS[resolvedEventType]
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function personaPayloadMessage(payload: Record<string, unknown> | null): string {
|
|
339
|
+
if (!payload) return ''
|
|
340
|
+
for (const key of ['message', 'error_message', 'error', 'reason', 'detail', 'stderr', 'stdout']) {
|
|
341
|
+
const value = payload[key]
|
|
342
|
+
if (typeof value === 'string' && value.trim()) return value.trim()
|
|
343
|
+
}
|
|
344
|
+
return ''
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function personaDistinctMessage(title: string, message: string | null | undefined): string | null {
|
|
348
|
+
const normalizedMessage = message?.trim()
|
|
349
|
+
if (!normalizedMessage) return null
|
|
350
|
+
const comparable = (value: string) => value.toLocaleLowerCase('ru-RU').replace(/[.!?]+$/u, '').trim()
|
|
351
|
+
return comparable(title) === comparable(normalizedMessage) ? null : normalizedMessage
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function personaDurationLabel(durationMs: number | null | undefined): string {
|
|
355
|
+
if (durationMs === null || durationMs === undefined) return 'длительность не определена'
|
|
356
|
+
if (durationMs < 1000) return `${durationMs} мс`
|
|
357
|
+
return `${(durationMs / 1000).toLocaleString('ru-RU', { maximumFractionDigits: 2 })} с`
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function personaCountLabel(count: number, forms: [string, string, string]): string {
|
|
361
|
+
const remainder100 = count % 100
|
|
362
|
+
const remainder10 = count % 10
|
|
363
|
+
const form = remainder100 >= 11 && remainder100 <= 14
|
|
364
|
+
? forms[2]
|
|
365
|
+
: remainder10 === 1
|
|
366
|
+
? forms[0]
|
|
367
|
+
: remainder10 >= 2 && remainder10 <= 4
|
|
368
|
+
? forms[1]
|
|
369
|
+
: forms[2]
|
|
370
|
+
return `${count} ${form}`
|
|
371
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { BadgeSeverity } from '../data-display/SBadge.vue'
|
|
2
|
+
import type { ActionListItemBadge } from '../data-display/SActionListItem.vue'
|
|
3
|
+
import type { Severity as MessageSeverity } from '../data-display/SMessage.vue'
|
|
4
|
+
import type { SAsyncStateContract } from '../feedback/SAsyncState.vue'
|
|
5
|
+
|
|
6
|
+
export type PersonaRunConsoleDensity = 'compact' | 'comfortable'
|
|
7
|
+
|
|
8
|
+
/** Состояние точного инвентаря без одновременных loading/error/ready веток. / Exact inventory state without concurrent loading/error/ready branches. */
|
|
9
|
+
export type PersonaInventoryPendingState = Extract<
|
|
10
|
+
SAsyncStateContract,
|
|
11
|
+
{ status: 'loading' | 'error' }
|
|
12
|
+
>
|
|
13
|
+
|
|
14
|
+
export type PersonaInventoryState<Item> =
|
|
15
|
+
| PersonaInventoryPendingState
|
|
16
|
+
| { status: 'ready'; items: Item[] }
|
|
17
|
+
|
|
18
|
+
/** Состояние точного paginated inventory с явной фазой страницы. / Exact paginated inventory state with an explicit page phase. */
|
|
19
|
+
export type PersonaPaginatedInventoryState<Item> =
|
|
20
|
+
| PersonaInventoryPendingState
|
|
21
|
+
| { status: 'ready'; items: Item[]; pagination: 'complete' | 'has-more' }
|
|
22
|
+
| { status: 'ready'; items: [Item, ...Item[]]; pagination: 'loading-more' }
|
|
23
|
+
|
|
24
|
+
/** Явная доступность действия без boolean capability fallback. / Explicit action availability without a boolean capability fallback. */
|
|
25
|
+
export type PersonaActionAvailability = 'enabled' | 'disabled'
|
|
26
|
+
|
|
27
|
+
/** Единственная активная операция private run console. / The single active private run-console operation. */
|
|
28
|
+
export type PersonaRunConsoleOperation =
|
|
29
|
+
| 'idle'
|
|
30
|
+
| 'refreshing-events'
|
|
31
|
+
| 'cancelling-run'
|
|
32
|
+
| 'appending-events'
|
|
33
|
+
|
|
34
|
+
/** Capability inventory private run console. / Private run-console capability inventory. */
|
|
35
|
+
export type PersonaRunConsoleCapabilities = {
|
|
36
|
+
cancelRun: PersonaActionAvailability
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Стабильный badge-контракт canonical action-list owner. / Stable badge contract shared with the canonical action-list owner. */
|
|
40
|
+
export type PersonaRunConsoleBadge = ActionListItemBadge
|
|
41
|
+
|
|
42
|
+
export type PersonaRunConsoleFact = {
|
|
43
|
+
/** Стабильный доменный id, независимый от presentation label и позиции. / Stable domain id independent of presentation label and position. */
|
|
44
|
+
id: string
|
|
45
|
+
label: string
|
|
46
|
+
value: string | number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Возвращает стабильный доменный id факта. / Returns the stable domain id of a fact. */
|
|
50
|
+
export function getPersonaRunConsoleFactKey(fact: PersonaRunConsoleFact): string {
|
|
51
|
+
return fact.id
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type PersonaRunConsoleRun = {
|
|
55
|
+
id: string
|
|
56
|
+
title: string
|
|
57
|
+
meta: string
|
|
58
|
+
detail?: string | null
|
|
59
|
+
statusLabel: string
|
|
60
|
+
statusSeverity: BadgeSeverity
|
|
61
|
+
artifactLabel?: string | null
|
|
62
|
+
facts?: PersonaRunConsoleFact[]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export type PersonaRunConsoleItem = {
|
|
66
|
+
id: string
|
|
67
|
+
title: string
|
|
68
|
+
meta: string
|
|
69
|
+
detail?: string | null
|
|
70
|
+
statusLabel: string
|
|
71
|
+
statusSeverity: BadgeSeverity
|
|
72
|
+
badges?: PersonaRunConsoleBadge[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type PersonaRunConsoleEvent = {
|
|
76
|
+
id: string
|
|
77
|
+
title: string
|
|
78
|
+
meta: string
|
|
79
|
+
message?: string | null
|
|
80
|
+
badges?: PersonaRunConsoleBadge[]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type PersonaRunConsoleLabels = {
|
|
84
|
+
runListTitle: string
|
|
85
|
+
selectedRunTitle: string
|
|
86
|
+
itemsTitle: string
|
|
87
|
+
eventsTitle: string
|
|
88
|
+
loadMoreRuns: string
|
|
89
|
+
loadMoreItems: string
|
|
90
|
+
appendEvents: string
|
|
91
|
+
loadMoreEvents: string
|
|
92
|
+
refreshEvents: string
|
|
93
|
+
cancelRun: string
|
|
94
|
+
reportSummary: string
|
|
95
|
+
emptyRun: string
|
|
96
|
+
noRuns: string
|
|
97
|
+
noItems: string
|
|
98
|
+
noEvents: string
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export type PersonaCatalogStep = {
|
|
102
|
+
id: string
|
|
103
|
+
title: string
|
|
104
|
+
meta?: string | null
|
|
105
|
+
detail?: string | null
|
|
106
|
+
kind?: string | null
|
|
107
|
+
depth: number
|
|
108
|
+
badges?: PersonaRunConsoleBadge[]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type PersonaCatalogLink = {
|
|
112
|
+
id: string
|
|
113
|
+
label: string
|
|
114
|
+
url: string
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export type PersonaCatalogTest = {
|
|
118
|
+
id: string
|
|
119
|
+
title: string
|
|
120
|
+
meta: string
|
|
121
|
+
description?: string | null
|
|
122
|
+
detail?: string | null
|
|
123
|
+
workItemId: string
|
|
124
|
+
scenarioId: string
|
|
125
|
+
variantId?: string | null
|
|
126
|
+
sourcePath: string
|
|
127
|
+
tags?: string[]
|
|
128
|
+
badges?: PersonaRunConsoleBadge[]
|
|
129
|
+
facts?: PersonaRunConsoleFact[]
|
|
130
|
+
links?: PersonaCatalogLink[]
|
|
131
|
+
statusLabel?: string | null
|
|
132
|
+
statusSeverity?: BadgeSeverity
|
|
133
|
+
structureNotice?: PersonaRunWorkbenchNotice | null
|
|
134
|
+
steps: PersonaCatalogStep[]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type PersonaRunWorkbenchBinding = {
|
|
138
|
+
id: string
|
|
139
|
+
title: string
|
|
140
|
+
root: string
|
|
141
|
+
statusLabel: string
|
|
142
|
+
statusSeverity: BadgeSeverity
|
|
143
|
+
meta?: string | null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type PersonaRunWorkbenchWatcher = {
|
|
147
|
+
statusLabel: string
|
|
148
|
+
statusSeverity: BadgeSeverity
|
|
149
|
+
message?: string | null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export type PersonaRunWorkbenchCatalogOverview = {
|
|
153
|
+
unavailableTests?: number
|
|
154
|
+
components: number
|
|
155
|
+
pages: number
|
|
156
|
+
diagnosticErrors?: number
|
|
157
|
+
diagnosticWarnings?: number
|
|
158
|
+
environments?: string[]
|
|
159
|
+
executionModel?: string | null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type PersonaRunWorkbenchDiagnostic = {
|
|
163
|
+
id: string
|
|
164
|
+
category: 'scenario_import' | 'component_analysis' | 'support_quality' | 'unclassified'
|
|
165
|
+
title: string
|
|
166
|
+
message: string
|
|
167
|
+
meta?: string | null
|
|
168
|
+
ownerLabel: string
|
|
169
|
+
impactLabel: string
|
|
170
|
+
remediation: string
|
|
171
|
+
statusLabel: string
|
|
172
|
+
statusSeverity: BadgeSeverity
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export type PersonaRunWorkbenchNotice = {
|
|
176
|
+
title: string
|
|
177
|
+
message: string
|
|
178
|
+
severity: MessageSeverity
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type PersonaRunWorkbenchLabels = PersonaRunConsoleLabels & {
|
|
182
|
+
projectTitle: string
|
|
183
|
+
discoveryTitle: string
|
|
184
|
+
catalogTitle: string
|
|
185
|
+
structureTitle: string
|
|
186
|
+
runSelected: string
|
|
187
|
+
runAll: string
|
|
188
|
+
discover: string
|
|
189
|
+
watch: string
|
|
190
|
+
searchPlaceholder: string
|
|
191
|
+
noCatalogTests: string
|
|
192
|
+
emptyStructure: string
|
|
193
|
+
selectedTestTitle: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Единственная активная операция workbench и его private console family. / The single active workbench/private-console-family operation. */
|
|
197
|
+
export type PersonaRunWorkbenchOperation =
|
|
198
|
+
| PersonaRunConsoleOperation
|
|
199
|
+
| 'discovering'
|
|
200
|
+
| 'watching'
|
|
201
|
+
| 'starting-run'
|
|
202
|
+
|
|
203
|
+
/** Полный capability inventory workbench. / Complete workbench capability inventory. */
|
|
204
|
+
export type PersonaRunWorkbenchCapabilities = PersonaRunConsoleCapabilities & {
|
|
205
|
+
discover: PersonaActionAvailability
|
|
206
|
+
watch: PersonaActionAvailability
|
|
207
|
+
runSelected: PersonaActionAvailability
|
|
208
|
+
runAll: PersonaActionAvailability
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type PersonaProjectPickerRoot = {
|
|
212
|
+
id: string
|
|
213
|
+
label: string
|
|
214
|
+
path: string
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export type PersonaProjectPickerEntry = {
|
|
218
|
+
/** Стабильный id проекта/директории в полном listing inventory. / Stable project/directory id in the complete listing inventory. */
|
|
219
|
+
id: string
|
|
220
|
+
rootId: string
|
|
221
|
+
name: string
|
|
222
|
+
projectName?: string | null
|
|
223
|
+
relativePath: string
|
|
224
|
+
requestedRoot: string
|
|
225
|
+
displayPath: string
|
|
226
|
+
readiness: 'ready' | 'not_persona' | 'invalid' | 'unreadable'
|
|
227
|
+
isSelectable: boolean
|
|
228
|
+
isHidden: boolean
|
|
229
|
+
diagnosticMessage?: string | null
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export type PersonaProjectPickerListing = {
|
|
233
|
+
root: PersonaProjectPickerRoot
|
|
234
|
+
current: PersonaProjectPickerEntry
|
|
235
|
+
parentRelativePath: string | null
|
|
236
|
+
directories: PersonaProjectPickerEntry[]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export type PersonaProjectPickerConnected = {
|
|
240
|
+
id: string
|
|
241
|
+
/** Id соответствующей PersonaProjectPickerEntry. / Id of the matching PersonaProjectPickerEntry. */
|
|
242
|
+
projectId: string
|
|
243
|
+
title: string
|
|
244
|
+
path: string
|
|
245
|
+
active: boolean
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Состояние каталога picker; pagination loading допустим только при существующем listing. / Picker directory state; pagination loading is valid only with an existing listing. */
|
|
249
|
+
export type PersonaProjectPickerState =
|
|
250
|
+
| PersonaInventoryPendingState
|
|
251
|
+
| { status: 'ready'; listing: null; pagination: 'complete' }
|
|
252
|
+
| {
|
|
253
|
+
status: 'ready'
|
|
254
|
+
listing: PersonaProjectPickerListing
|
|
255
|
+
pagination: 'complete' | 'has-more' | 'loading-more'
|
|
256
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Типизированный scoped-controller для сброса UI-предпочтений.
|
|
3
|
+
* Typed scoped controller for resetting UI preferences.
|
|
4
|
+
*/
|
|
5
|
+
export interface UiPreferencesResetController {
|
|
6
|
+
/**
|
|
7
|
+
* Подписывает owner на сброс и возвращает функцию отписки.
|
|
8
|
+
* Subscribes an owner to reset requests and returns an unsubscribe function.
|
|
9
|
+
*/
|
|
10
|
+
subscribe(listener: () => void): () => void;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Синхронно уведомляет всех active owners о сбросе.
|
|
14
|
+
* Synchronously notifies every active owner about a reset.
|
|
15
|
+
*/
|
|
16
|
+
requestReset(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Создаёт isolated reset scope без document events и глобальных строковых каналов.
|
|
21
|
+
* Creates an isolated reset scope without document events or global string channels.
|
|
22
|
+
*/
|
|
23
|
+
export function createUiPreferencesResetController(): UiPreferencesResetController {
|
|
24
|
+
const listeners = new Set<() => void>();
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
subscribe(listener) {
|
|
28
|
+
listeners.add(listener);
|
|
29
|
+
return () => {
|
|
30
|
+
listeners.delete(listener);
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
requestReset() {
|
|
35
|
+
const errors: unknown[] = [];
|
|
36
|
+
for (const listener of [...listeners]) {
|
|
37
|
+
try {
|
|
38
|
+
listener();
|
|
39
|
+
} catch (error) {
|
|
40
|
+
errors.push(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (errors.length === 1) {
|
|
45
|
+
throw errors[0];
|
|
46
|
+
}
|
|
47
|
+
if (errors.length > 1) {
|
|
48
|
+
throw new AggregateError(errors, 'Не все owners UI-предпочтений смогли обработать сброс.');
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|