@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.
Files changed (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -0
  3. package/docs/accessibility.md +27 -0
  4. package/docs/getting-started.md +80 -0
  5. package/docs/licensing.md +30 -0
  6. package/docs/public-api.md +39 -0
  7. package/docs/security.md +21 -0
  8. package/docs/theming.md +35 -0
  9. package/docs/versioning.md +23 -0
  10. package/index.ts +3 -0
  11. package/package.json +592 -0
  12. package/src/components/icons/_internal/generatedSputnigUiIconMarkup.ts +971 -0
  13. package/src/components/icons/sputnigUiIcons.ts +530 -0
  14. package/src/components/layout/SAppShell.vue +232 -0
  15. package/src/components/layout/SDockRegion.vue +200 -0
  16. package/src/components/layout/SResizeHandle.vue +251 -0
  17. package/src/components/layout/SSideMenuButton.vue +104 -0
  18. package/src/components/layout/SSideMenuRail.vue +47 -0
  19. package/src/components/layout/SStack.css +25 -0
  20. package/src/components/layout/SStack.vue +57 -0
  21. package/src/components/layout/SStatusBarShell.css +15 -0
  22. package/src/components/layout/SStatusBarShell.vue +26 -0
  23. package/src/components/layout/SWorkbenchLayout.vue +53 -0
  24. package/src/components/layout/SWorkbenchPanelShell.css +161 -0
  25. package/src/components/layout/SWorkbenchPanelShell.vue +286 -0
  26. package/src/components/layout/_internal/SAppBrand.css +45 -0
  27. package/src/components/layout/_internal/SAppBrand.vue +37 -0
  28. package/src/components/layout/_internal/SAppBrandContent.vue +30 -0
  29. package/src/components/layout/types.ts +28 -0
  30. package/src/components/shared/_internal/SDialogSurface.css +127 -0
  31. package/src/components/shared/_internal/SDialogSurface.vue +117 -0
  32. package/src/components/shared/_internal/SDisclosureSummary.css +92 -0
  33. package/src/components/shared/_internal/SDisclosureSummary.vue +121 -0
  34. package/src/components/shared/_internal/SListboxGroup.css +18 -0
  35. package/src/components/shared/_internal/SListboxGroup.vue +24 -0
  36. package/src/components/shared/_internal/SMenuItem.css +112 -0
  37. package/src/components/shared/_internal/SMenuItem.vue +213 -0
  38. package/src/components/shared/_internal/SMenuLayer.css +21 -0
  39. package/src/components/shared/_internal/SMenuLayer.vue +48 -0
  40. package/src/components/shared/_internal/SMenuSeparator.css +5 -0
  41. package/src/components/shared/_internal/SMenuSeparator.vue +13 -0
  42. package/src/components/shared/_internal/SToastItem.css +65 -0
  43. package/src/components/shared/_internal/SToastItem.vue +58 -0
  44. package/src/components/shared/_internal/STreeNode.vue +761 -0
  45. package/src/components/shared/_internal/dialogSurface.ts +105 -0
  46. package/src/components/shared/_internal/focusTrap.ts +40 -0
  47. package/src/components/shared/_internal/listboxContext.ts +16 -0
  48. package/src/components/shared/_internal/markerContract.ts +10 -0
  49. package/src/components/shared/_internal/menuContext.ts +23 -0
  50. package/src/components/shared/_internal/selectionContract.ts +239 -0
  51. package/src/components/shared/_internal/sidebarGroupContext.ts +31 -0
  52. package/src/components/shared/_internal/tabsContext.ts +57 -0
  53. package/src/components/shared/_internal/treeRuntime.ts +203 -0
  54. package/src/components/shared/_internal/useSelectionRoving.ts +162 -0
  55. package/src/components/shared/codeLanguages.ts +168 -0
  56. package/src/components/shared/complex/SKanbanCard.css +8 -0
  57. package/src/components/shared/complex/SKanbanCard.vue +76 -0
  58. package/src/components/shared/complex/SKanbanLane.vue +82 -0
  59. package/src/components/shared/complex/STree.css +24 -0
  60. package/src/components/shared/complex/STree.vue +288 -0
  61. package/src/components/shared/complex/treeAdapter.ts +210 -0
  62. package/src/components/shared/complex/types.ts +102 -0
  63. package/src/components/shared/containers/SCloudChrome.css +103 -0
  64. package/src/components/shared/containers/SCloudChrome.vue +87 -0
  65. package/src/components/shared/containers/SCloudChromeEmpty.css +10 -0
  66. package/src/components/shared/containers/SCloudChromeEmpty.vue +18 -0
  67. package/src/components/shared/containers/SCloudChromeRow.css +78 -0
  68. package/src/components/shared/containers/SCloudChromeRow.vue +108 -0
  69. package/src/components/shared/containers/SCollapsiblePanel.css +57 -0
  70. package/src/components/shared/containers/SCollapsiblePanel.vue +360 -0
  71. package/src/components/shared/containers/SDrawer.css +60 -0
  72. package/src/components/shared/containers/SDrawer.vue +86 -0
  73. package/src/components/shared/containers/SFormGrid.css +26 -0
  74. package/src/components/shared/containers/SFormGrid.vue +46 -0
  75. package/src/components/shared/containers/SFormGridItem.css +18 -0
  76. package/src/components/shared/containers/SFormGridItem.vue +29 -0
  77. package/src/components/shared/containers/SLeftSidebar.vue +74 -0
  78. package/src/components/shared/containers/SModal.css +27 -0
  79. package/src/components/shared/containers/SModal.vue +105 -0
  80. package/src/components/shared/containers/SPageHeader.css +66 -0
  81. package/src/components/shared/containers/SPageHeader.vue +45 -0
  82. package/src/components/shared/containers/SPanel.css +134 -0
  83. package/src/components/shared/containers/SPanel.vue +169 -0
  84. package/src/components/shared/containers/SPopover.css +35 -0
  85. package/src/components/shared/containers/SPopover.vue +551 -0
  86. package/src/components/shared/containers/SSidebarGroup.css +12 -0
  87. package/src/components/shared/containers/SSidebarGroup.vue +443 -0
  88. package/src/components/shared/containers/SSidebarSection.css +41 -0
  89. package/src/components/shared/containers/SSidebarSection.vue +268 -0
  90. package/src/components/shared/containers/disclosureSummary.ts +48 -0
  91. package/src/components/shared/containers/popover.ts +72 -0
  92. package/src/components/shared/containers/sidebar.ts +206 -0
  93. package/src/components/shared/controls/SButton.css +274 -0
  94. package/src/components/shared/controls/SButton.vue +211 -0
  95. package/src/components/shared/controls/SCheckbox.css +65 -0
  96. package/src/components/shared/controls/SCheckbox.vue +130 -0
  97. package/src/components/shared/controls/SCheckboxGroup.vue +134 -0
  98. package/src/components/shared/controls/SChoiceCards.css +29 -0
  99. package/src/components/shared/controls/SChoiceCards.vue +145 -0
  100. package/src/components/shared/controls/SColorSelect.vue +146 -0
  101. package/src/components/shared/controls/SComboboxTrigger.css +35 -0
  102. package/src/components/shared/controls/SComboboxTrigger.vue +122 -0
  103. package/src/components/shared/controls/SContextMenu.css +11 -0
  104. package/src/components/shared/controls/SContextMenu.vue +186 -0
  105. package/src/components/shared/controls/SContextToggleButton.vue +64 -0
  106. package/src/components/shared/controls/SDragHandle.vue +100 -0
  107. package/src/components/shared/controls/SDropdownMenu.css +5 -0
  108. package/src/components/shared/controls/SDropdownMenu.vue +188 -0
  109. package/src/components/shared/controls/SField.css +79 -0
  110. package/src/components/shared/controls/SField.vue +89 -0
  111. package/src/components/shared/controls/SFieldGroup.css +58 -0
  112. package/src/components/shared/controls/SFieldGroup.vue +117 -0
  113. package/src/components/shared/controls/SFieldLabel.css +12 -0
  114. package/src/components/shared/controls/SFieldLabel.vue +51 -0
  115. package/src/components/shared/controls/SInlineTokenEditor.css +61 -0
  116. package/src/components/shared/controls/SInlineTokenEditor.vue +1081 -0
  117. package/src/components/shared/controls/SInputText.css +66 -0
  118. package/src/components/shared/controls/SInputText.vue +321 -0
  119. package/src/components/shared/controls/SInteractiveSurface.css +72 -0
  120. package/src/components/shared/controls/SInteractiveSurface.vue +326 -0
  121. package/src/components/shared/controls/SLink.css +114 -0
  122. package/src/components/shared/controls/SLink.ts +85 -0
  123. package/src/components/shared/controls/SLink.vue +114 -0
  124. package/src/components/shared/controls/SListbox.css +31 -0
  125. package/src/components/shared/controls/SListbox.vue +388 -0
  126. package/src/components/shared/controls/SListboxOption.css +63 -0
  127. package/src/components/shared/controls/SListboxOption.vue +85 -0
  128. package/src/components/shared/controls/SMarker.css +19 -0
  129. package/src/components/shared/controls/SMarker.vue +33 -0
  130. package/src/components/shared/controls/SMenuSurface.css +20 -0
  131. package/src/components/shared/controls/SMenuSurface.vue +333 -0
  132. package/src/components/shared/controls/SSegmentedControl.css +38 -0
  133. package/src/components/shared/controls/SSegmentedControl.vue +134 -0
  134. package/src/components/shared/controls/SSelect.css +12 -0
  135. package/src/components/shared/controls/SSelect.vue +338 -0
  136. package/src/components/shared/controls/STextarea.css +31 -0
  137. package/src/components/shared/controls/STextarea.vue +316 -0
  138. package/src/components/shared/controls/_internal/SInlineTokenSurface.vue +224 -0
  139. package/src/components/shared/controls/menu.ts +178 -0
  140. package/src/components/shared/data-display/SActionCard.css +6 -0
  141. package/src/components/shared/data-display/SActionCard.vue +49 -0
  142. package/src/components/shared/data-display/SActionList.css +15 -0
  143. package/src/components/shared/data-display/SActionList.vue +34 -0
  144. package/src/components/shared/data-display/SActionListItem.css +114 -0
  145. package/src/components/shared/data-display/SActionListItem.vue +230 -0
  146. package/src/components/shared/data-display/SBadge.css +73 -0
  147. package/src/components/shared/data-display/SBadge.vue +52 -0
  148. package/src/components/shared/data-display/SCodeBlock.css +56 -0
  149. package/src/components/shared/data-display/SCodeBlock.vue +188 -0
  150. package/src/components/shared/data-display/SCodeEditor.css +178 -0
  151. package/src/components/shared/data-display/SCodeEditor.vue +274 -0
  152. package/src/components/shared/data-display/SCodeSearchPanel.vue +327 -0
  153. package/src/components/shared/data-display/SCopyField.vue +77 -0
  154. package/src/components/shared/data-display/SDocBlock.css +136 -0
  155. package/src/components/shared/data-display/SDocBlock.vue +358 -0
  156. package/src/components/shared/data-display/SEmptyState.css +42 -0
  157. package/src/components/shared/data-display/SEmptyState.vue +79 -0
  158. package/src/components/shared/data-display/SExpandableText.vue +314 -0
  159. package/src/components/shared/data-display/SJsonTree.css +6 -0
  160. package/src/components/shared/data-display/SJsonTree.vue +111 -0
  161. package/src/components/shared/data-display/SKeyValueGrid.css +37 -0
  162. package/src/components/shared/data-display/SKeyValueGrid.vue +277 -0
  163. package/src/components/shared/data-display/SLinkedSystemsList.vue +270 -0
  164. package/src/components/shared/data-display/SMessage.css +88 -0
  165. package/src/components/shared/data-display/SMessage.vue +152 -0
  166. package/src/components/shared/data-display/SMetricCard.css +32 -0
  167. package/src/components/shared/data-display/SMetricCard.vue +117 -0
  168. package/src/components/shared/data-display/SProgressBar.css +84 -0
  169. package/src/components/shared/data-display/SProgressBar.vue +114 -0
  170. package/src/components/shared/data-display/SProgressIndicator.css +59 -0
  171. package/src/components/shared/data-display/SProgressIndicator.vue +104 -0
  172. package/src/components/shared/data-display/SSectionHeader.css +84 -0
  173. package/src/components/shared/data-display/SSectionHeader.vue +58 -0
  174. package/src/components/shared/data-display/SStatus.css +90 -0
  175. package/src/components/shared/data-display/SStatus.vue +79 -0
  176. package/src/components/shared/data-display/STable.css +83 -0
  177. package/src/components/shared/data-display/STable.vue +339 -0
  178. package/src/components/shared/data-display/SToastContainer.css +53 -0
  179. package/src/components/shared/data-display/SToastContainer.vue +31 -0
  180. package/src/components/shared/data-display/STooltip.css +114 -0
  181. package/src/components/shared/data-display/STooltip.vue +550 -0
  182. package/src/components/shared/data-display/STooltipAnchor.css +21 -0
  183. package/src/components/shared/data-display/STooltipAnchor.vue +119 -0
  184. package/src/components/shared/data-display/SVirtualList.css +24 -0
  185. package/src/components/shared/data-display/SVirtualList.ts +19 -0
  186. package/src/components/shared/data-display/SVirtualList.vue +361 -0
  187. package/src/components/shared/data-display/json.ts +136 -0
  188. package/src/components/shared/data-display/table.ts +76 -0
  189. package/src/components/shared/database/SDataGrid.css +3 -0
  190. package/src/components/shared/database/SDataGrid.vue +464 -0
  191. package/src/components/shared/database/SSqlEditor.vue +363 -0
  192. package/src/components/shared/database/dataGrid.ts +61 -0
  193. package/src/components/shared/feedback/SAsyncState.css +26 -0
  194. package/src/components/shared/feedback/SAsyncState.vue +190 -0
  195. package/src/components/shared/graph/SGraphViewport.ts +1 -0
  196. package/src/components/shared/graph/SGraphViewport.vue +858 -0
  197. package/src/components/shared/graph/_internal/graphViewportContract.ts +362 -0
  198. package/src/components/shared/markdown.ts +169 -0
  199. package/src/components/shared/navigation/SBottomNav.vue +692 -0
  200. package/src/components/shared/navigation/SBreadcrumbs.css +26 -0
  201. package/src/components/shared/navigation/SBreadcrumbs.vue +155 -0
  202. package/src/components/shared/navigation/SCatalogNavigator.css +100 -0
  203. package/src/components/shared/navigation/SCatalogNavigator.vue +308 -0
  204. package/src/components/shared/navigation/STab.vue +363 -0
  205. package/src/components/shared/navigation/STabList.vue +442 -0
  206. package/src/components/shared/navigation/STabPanel.vue +165 -0
  207. package/src/components/shared/navigation/STabPanels.vue +33 -0
  208. package/src/components/shared/navigation/STabs.vue +617 -0
  209. package/src/components/shared/navigation/SWizardSteps.css +69 -0
  210. package/src/components/shared/navigation/SWizardSteps.vue +171 -0
  211. package/src/components/shared/navigation/_internal/SCatalogNavigatorGroup.vue +63 -0
  212. package/src/components/shared/persona/SPersonaProjectPicker.css +36 -0
  213. package/src/components/shared/persona/SPersonaProjectPicker.vue +361 -0
  214. package/src/components/shared/persona/SPersonaRunWorkbench.css +109 -0
  215. package/src/components/shared/persona/SPersonaRunWorkbench.vue +549 -0
  216. package/src/components/shared/persona/_internal/SPersonaRunConsole.css +40 -0
  217. package/src/components/shared/persona/_internal/SPersonaRunConsole.vue +298 -0
  218. package/src/components/shared/persona/_internal/inventory.ts +110 -0
  219. package/src/components/shared/persona/_internal/labels.ts +79 -0
  220. package/src/components/shared/persona/presentation.ts +371 -0
  221. package/src/components/shared/persona/types.ts +256 -0
  222. package/src/composables/uiPreferencesReset.ts +52 -0
  223. package/src/composables/useClipboard.ts +37 -0
  224. package/src/composables/useDialogOverlay.ts +85 -0
  225. package/src/composables/useDisclosure.ts +40 -0
  226. package/src/composables/useFloatingPosition.ts +245 -0
  227. package/src/composables/useNotifier.ts +26 -0
  228. package/src/composables/useSidebarPanelState.ts +610 -0
  229. package/src/internal/codeEditorContract.ts +110 -0
  230. package/src/internal/codeEditorExtensions.ts +27 -0
  231. package/src/internal/feedbackPresentation.ts +136 -0
  232. package/src/internal/fieldSurface.ts +59 -0
  233. package/src/internal/focusNavigation.ts +69 -0
  234. package/src/internal/inlineTokenEditorContract.ts +136 -0
  235. package/src/internal/interactiveElement.ts +33 -0
  236. package/src/internal/layerStack.ts +202 -0
  237. package/src/internal/linkTarget.ts +256 -0
  238. package/src/internal/ownedAttrs.ts +464 -0
  239. package/src/internal/passiveContentContract.ts +129 -0
  240. package/src/internal/pointerInteractionLease.ts +81 -0
  241. package/src/internal/runtimeContract.ts +194 -0
  242. package/src/internal/runtimeTheme.ts +159 -0
  243. package/src/internal/semanticSizing.ts +60 -0
  244. package/src/internal/useMenuLayer.ts +129 -0
  245. package/src/stores/useNotifierStore.ts +69 -0
  246. package/src/styles/reference.css +4 -0
  247. package/src/styles/style.css +151 -0
  248. package/src/styles/tokens.css +836 -0
  249. package/src/theme.ts +449 -0
  250. package/src/types/highlight-js-lib.d.ts +76 -0
  251. package/src/variants.ts +15 -0
@@ -0,0 +1,109 @@
1
+ @reference "../../../styles/reference.css";
2
+
3
+ .s-persona-run-workbench-shell {
4
+ @apply min-h-0 min-w-0;
5
+ container-type: inline-size;
6
+ }
7
+
8
+ .s-persona-run-workbench {
9
+ @apply grid min-h-0 min-w-0 items-start gap-4;
10
+ }
11
+
12
+ .s-persona-run-workbench[data-density='comfortable'] {
13
+ @apply gap-4;
14
+ }
15
+
16
+ .s-persona-run-workbench[data-density='compact'] {
17
+ @apply gap-3;
18
+ }
19
+
20
+ .s-persona-run-workbench__project,
21
+ .s-persona-run-workbench__catalog,
22
+ .s-persona-run-workbench__structure,
23
+ .s-persona-run-workbench__runs {
24
+ @apply min-h-0 min-w-0;
25
+ }
26
+
27
+ .s-persona-run-workbench__project {
28
+ @apply border-b border-surface-200 pb-4 dark:border-surface-800;
29
+ }
30
+
31
+ .s-persona-run-workbench__runs {
32
+ @apply border-t border-surface-200 pt-4 dark:border-surface-800;
33
+ }
34
+
35
+ .s-persona-run-workbench__project-line {
36
+ @apply grid min-w-0 gap-4;
37
+ }
38
+
39
+ .s-persona-run-workbench__messages {
40
+ @apply mt-3 grid gap-3;
41
+ }
42
+
43
+ .s-persona-run-workbench__diagnostics {
44
+ @apply mt-3;
45
+ }
46
+
47
+ .s-persona-run-workbench__diagnostic-groups {
48
+ @apply mb-3 grid min-w-0 border-b pb-3;
49
+ border-color: var(--s-section-divider);
50
+ }
51
+
52
+ .s-persona-run-workbench__diagnostic-controls {
53
+ @apply mb-3 grid min-w-0 gap-2;
54
+ }
55
+
56
+ .s-persona-run-workbench__project-selector {
57
+ @apply grid min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-2;
58
+ }
59
+
60
+ .s-persona-run-workbench__list {
61
+ @apply mt-3;
62
+ }
63
+
64
+ .s-persona-run-workbench__subheading {
65
+ @apply mt-4;
66
+ }
67
+
68
+ .s-persona-run-workbench__structure-notice {
69
+ @apply mb-3;
70
+ }
71
+
72
+ .s-persona-run-workbench__links {
73
+ @apply mt-3 flex min-w-0 flex-wrap gap-x-4 gap-y-2;
74
+ }
75
+
76
+ @container (min-width: 60rem) {
77
+ .s-persona-run-workbench {
78
+ grid-template-columns: minmax(20rem, 0.72fr) minmax(28rem, 1.28fr);
79
+ }
80
+
81
+ .s-persona-run-workbench__project {
82
+ grid-column: 1 / -1;
83
+ }
84
+
85
+ .s-persona-run-workbench__catalog {
86
+ grid-column: 1;
87
+ grid-row: 2 / span 2;
88
+ }
89
+
90
+ .s-persona-run-workbench__structure {
91
+ grid-column: 2;
92
+ grid-row: 2;
93
+ }
94
+
95
+ .s-persona-run-workbench__runs {
96
+ grid-column: 2;
97
+ grid-row: 3;
98
+ }
99
+
100
+ .s-persona-run-workbench__project-line {
101
+ grid-template-columns: minmax(18rem, 0.55fr) minmax(0, 1.45fr);
102
+ align-items: center;
103
+ }
104
+
105
+ .s-persona-run-workbench__diagnostic-controls {
106
+ grid-template-columns: minmax(16rem, 1fr) auto;
107
+ align-items: end;
108
+ }
109
+ }
@@ -0,0 +1,549 @@
1
+ <script setup lang="ts">
2
+ import { computed, ref } from 'vue'
3
+
4
+ import SButton from '../controls/SButton.vue'
5
+ import SCollapsiblePanel from '../containers/SCollapsiblePanel.vue'
6
+ import SInputText from '../controls/SInputText.vue'
7
+ import SLink from '../controls/SLink.vue'
8
+ import SSegmentedControl from '../controls/SSegmentedControl.vue'
9
+ import type { SegmentedControlOption } from '../controls/SSegmentedControl.vue'
10
+ import SActionListItem from '../data-display/SActionListItem.vue'
11
+ import SBadge from '../data-display/SBadge.vue'
12
+ import SEmptyState from '../data-display/SEmptyState.vue'
13
+ import SMessage from '../data-display/SMessage.vue'
14
+ import SSectionHeader from '../data-display/SSectionHeader.vue'
15
+ import SVirtualList from '../data-display/SVirtualList.vue'
16
+ import SKeyValueGrid from '../data-display/SKeyValueGrid.vue'
17
+ import SAsyncState from '../feedback/SAsyncState.vue'
18
+ import {
19
+ assertPersonaInventory,
20
+ findExactPersonaInventoryItem,
21
+ resolvePersonaInventoryAsyncState,
22
+ } from './_internal/inventory'
23
+ import { resolvePersonaRunWorkbenchLabels } from './_internal/labels'
24
+ import { personaCountLabel } from './presentation'
25
+ import SPersonaRunConsole from './_internal/SPersonaRunConsole.vue'
26
+ import { getPersonaRunConsoleFactKey } from './types'
27
+ import type {
28
+ PersonaCatalogTest,
29
+ PersonaInventoryState,
30
+ PersonaPaginatedInventoryState,
31
+ PersonaRunConsoleOperation,
32
+ PersonaRunConsoleDensity,
33
+ PersonaRunConsoleEvent,
34
+ PersonaRunConsoleItem,
35
+ PersonaRunConsoleRun,
36
+ PersonaRunWorkbenchBinding,
37
+ PersonaRunWorkbenchCapabilities,
38
+ PersonaRunWorkbenchCatalogOverview,
39
+ PersonaRunWorkbenchDiagnostic,
40
+ PersonaRunWorkbenchLabels,
41
+ PersonaRunWorkbenchNotice,
42
+ PersonaRunWorkbenchOperation,
43
+ PersonaRunWorkbenchWatcher,
44
+ } from './types'
45
+ import { useOwnedAttrs } from '../../../internal/ownedAttrs'
46
+
47
+ defineOptions({ inheritAttrs: false })
48
+
49
+ const props = withDefaults(
50
+ defineProps<{
51
+ binding: PersonaRunWorkbenchBinding | null
52
+ watcher?: PersonaRunWorkbenchWatcher | null
53
+ catalogOverview?: PersonaRunWorkbenchCatalogOverview | null
54
+ diagnostics?: PersonaRunWorkbenchDiagnostic[]
55
+ notice?: PersonaRunWorkbenchNotice | null
56
+ catalogState: PersonaInventoryState<PersonaCatalogTest>
57
+ selectedTestId?: string | null
58
+ runsState: PersonaPaginatedInventoryState<PersonaRunConsoleRun>
59
+ selectedRunId?: string | null
60
+ itemsState: PersonaPaginatedInventoryState<PersonaRunConsoleItem>
61
+ eventsState: PersonaPaginatedInventoryState<PersonaRunConsoleEvent>
62
+ capabilities: PersonaRunWorkbenchCapabilities
63
+ operation: PersonaRunWorkbenchOperation
64
+ reportSummary?: string | null
65
+ labels?: Partial<PersonaRunWorkbenchLabels>
66
+ density?: PersonaRunConsoleDensity
67
+ catalogHeight?: number
68
+ stepsHeight?: number
69
+ }>(),
70
+ {
71
+ watcher: null,
72
+ catalogOverview: null,
73
+ diagnostics: () => [],
74
+ notice: null,
75
+ selectedTestId: null,
76
+ selectedRunId: null,
77
+ reportSummary: null,
78
+ labels: () => ({}),
79
+ density: 'comfortable',
80
+ catalogHeight: 520,
81
+ stepsHeight: 300,
82
+ },
83
+ )
84
+
85
+ const emit = defineEmits<{
86
+ discover: []
87
+ watch: []
88
+ 'select-test': [testId: string]
89
+ 'run-test': [testId: string]
90
+ 'run-all': []
91
+ 'select-run': [runId: string]
92
+ 'load-more-runs': []
93
+ 'refresh-events': []
94
+ 'cancel-run': []
95
+ 'load-more-items': []
96
+ 'append-events': []
97
+ 'load-more-events': []
98
+ }>()
99
+ const ownedAttrs = useOwnedAttrs({ component: 'SPersonaRunWorkbench', owner: 'persona workbench shell' })
100
+
101
+ const search = ref('')
102
+ const diagnosticSearch = ref('')
103
+ const diagnosticSeverity = ref<'all' | 'danger' | 'warn'>('all')
104
+
105
+ const uiLabels = computed(() => resolvePersonaRunWorkbenchLabels(props.labels))
106
+
107
+ const consoleOperation = computed<PersonaRunConsoleOperation>(() => {
108
+ switch (props.operation) {
109
+ case 'refreshing-events':
110
+ case 'cancelling-run':
111
+ case 'appending-events':
112
+ return props.operation
113
+ case 'discovering':
114
+ case 'watching':
115
+ case 'starting-run':
116
+ case 'idle':
117
+ return 'idle'
118
+ default:
119
+ throw new TypeError(`SPersonaRunWorkbench: unsupported operation ${String(props.operation)}`)
120
+ }
121
+ })
122
+
123
+ const catalogTests = computed(() => props.catalogState.status === 'ready'
124
+ ? assertPersonaInventory('SPersonaRunWorkbench.catalogState', props.catalogState.items)
125
+ : [])
126
+ const catalogAsyncState = computed(() => resolvePersonaInventoryAsyncState(
127
+ 'SPersonaRunWorkbench.catalogState',
128
+ props.catalogState,
129
+ {
130
+ loadingMessage: 'Загрузка каталога',
131
+ emptyMessage: uiLabels.value.noCatalogTests,
132
+ },
133
+ ))
134
+ const selectedTest = computed(() => findExactPersonaInventoryItem(
135
+ 'SPersonaRunWorkbench.catalogState',
136
+ catalogTests.value,
137
+ props.selectedTestId,
138
+ ))
139
+ const selectedTestBadges = computed(() => assertPersonaInventory(
140
+ 'SPersonaRunWorkbench.selectedTest.badges',
141
+ selectedTest.value?.badges ?? [],
142
+ ))
143
+ const selectedTestLinks = computed(() => assertPersonaInventory(
144
+ 'SPersonaRunWorkbench.selectedTest.links',
145
+ selectedTest.value?.links ?? [],
146
+ ))
147
+ const validatedDiagnostics = computed(() => assertPersonaInventory(
148
+ 'SPersonaRunWorkbench.diagnostics',
149
+ props.diagnostics,
150
+ ))
151
+
152
+ const visibleTests = computed(() => {
153
+ const query = search.value.trim().toLocaleLowerCase('ru-RU')
154
+ if (!query) return catalogTests.value
155
+ return catalogTests.value.filter((test) => {
156
+ const haystack = [
157
+ test.title,
158
+ test.meta,
159
+ test.workItemId,
160
+ test.scenarioId,
161
+ test.variantId,
162
+ test.sourcePath,
163
+ ...(test.tags ?? []),
164
+ ].filter((value): value is string => Boolean(value)).join(' ').toLocaleLowerCase('ru-RU')
165
+ return haystack.includes(query)
166
+ })
167
+ })
168
+
169
+ const diagnosticSeverityOptions = computed<SegmentedControlOption[]>(() => [
170
+ { value: 'all', label: `Все ${validatedDiagnostics.value.length}`, title: 'Показать все сообщения' },
171
+ {
172
+ value: 'danger',
173
+ label: `Ошибки ${validatedDiagnostics.value.filter((diagnostic) => diagnostic.statusSeverity === 'danger').length}`,
174
+ title: 'Показать ошибки',
175
+ },
176
+ {
177
+ value: 'warn',
178
+ label: `Предупреждения ${validatedDiagnostics.value.filter((diagnostic) => diagnostic.statusSeverity === 'warn').length}`,
179
+ title: 'Показать предупреждения',
180
+ },
181
+ ])
182
+
183
+ const diagnosticGroupTitles: Record<PersonaRunWorkbenchDiagnostic['category'], string> = {
184
+ scenario_import: 'Недоступные сценарии',
185
+ component_analysis: 'Ошибки анализа компонентов',
186
+ support_quality: 'Замечания качества PageObject',
187
+ unclassified: 'Сообщения без классификации',
188
+ }
189
+
190
+ const diagnosticGroups = computed(() => {
191
+ const categories: PersonaRunWorkbenchDiagnostic['category'][] = [
192
+ 'scenario_import',
193
+ 'component_analysis',
194
+ 'support_quality',
195
+ 'unclassified',
196
+ ]
197
+ return categories.flatMap((category) => {
198
+ const diagnostics = validatedDiagnostics.value.filter((diagnostic) => diagnostic.category === category)
199
+ if (diagnostics.length === 0) return []
200
+ const statusSeverity = diagnostics.some((diagnostic) => diagnostic.statusSeverity === 'danger')
201
+ ? 'danger' as const
202
+ : diagnostics.some((diagnostic) => diagnostic.statusSeverity === 'warn')
203
+ ? 'warn' as const
204
+ : 'info' as const
205
+ const countLabel = personaCountLabel(diagnostics.length, ['сообщение', 'сообщения', 'сообщений'])
206
+ return [{
207
+ category,
208
+ title: diagnosticGroupTitles[category],
209
+ description: countLabel,
210
+ countLabel,
211
+ statusSeverity,
212
+ }]
213
+ })
214
+ })
215
+
216
+ const visibleDiagnostics = computed(() => {
217
+ const query = diagnosticSearch.value.trim().toLocaleLowerCase('ru-RU')
218
+ return validatedDiagnostics.value.filter((diagnostic) => {
219
+ if (diagnosticSeverity.value !== 'all' && diagnostic.statusSeverity !== diagnosticSeverity.value) {
220
+ return false
221
+ }
222
+ if (!query) return true
223
+ return [
224
+ diagnostic.title,
225
+ diagnostic.message,
226
+ diagnostic.meta,
227
+ diagnostic.ownerLabel,
228
+ diagnostic.impactLabel,
229
+ diagnostic.remediation,
230
+ diagnostic.statusLabel,
231
+ ]
232
+ .filter((value): value is string => Boolean(value))
233
+ .join(' ')
234
+ .toLocaleLowerCase('ru-RU')
235
+ .includes(query)
236
+ })
237
+ })
238
+
239
+ const virtualRowSize = 96
240
+ const catalogItemSize = virtualRowSize
241
+ const stepItemSize = virtualRowSize
242
+ const catalogViewportHeight = computed(() => Math.min(props.catalogHeight, visibleTests.value.length * catalogItemSize))
243
+ const stepsViewportHeight = computed(() => Math.min(
244
+ props.stepsHeight,
245
+ (selectedTest.value?.steps.length ?? 0) * stepItemSize,
246
+ ))
247
+ const diagnosticsViewportHeight = computed(() => Math.min(288, visibleDiagnostics.value.length * virtualRowSize))
248
+
249
+ function diagnosticDescription(diagnostic: PersonaRunWorkbenchDiagnostic): string {
250
+ return [diagnostic.ownerLabel, diagnostic.impactLabel, diagnostic.meta]
251
+ .filter((value): value is string => Boolean(value))
252
+ .join(' · ')
253
+ }
254
+
255
+ </script>
256
+
257
+ <template>
258
+ <div v-bind="ownedAttrs.bindings()" class="s-persona-run-workbench-shell">
259
+ <div class="s-persona-run-workbench" :data-density="density">
260
+ <section class="s-persona-run-workbench__project">
261
+ <div class="s-persona-run-workbench__project-line">
262
+ <div v-if="$slots.projectSelector" class="s-persona-run-workbench__project-selector">
263
+ <slot name="projectSelector" />
264
+ </div>
265
+ <SSectionHeader
266
+ :eyebrow="uiLabels.projectTitle"
267
+ :title="binding?.title ?? uiLabels.projectTitle"
268
+ :description="binding?.root"
269
+ :meta="binding?.meta"
270
+ >
271
+ <template #badges>
272
+ <SBadge v-if="binding" :severity="binding.statusSeverity">{{ binding.statusLabel }}</SBadge>
273
+ <SBadge v-if="watcher" :severity="watcher.statusSeverity">{{ watcher.statusLabel }}</SBadge>
274
+ </template>
275
+ <template #actions>
276
+ <SButton
277
+ size="sm"
278
+ severity="secondary"
279
+ :disabled="!binding || capabilities.discover === 'disabled'"
280
+ :loading="operation === 'discovering'"
281
+ @click="emit('discover')"
282
+ >
283
+ {{ uiLabels.discover }}
284
+ </SButton>
285
+ <SButton
286
+ size="sm"
287
+ severity="secondary"
288
+ :disabled="!binding || capabilities.watch === 'disabled'"
289
+ :loading="operation === 'watching'"
290
+ @click="emit('watch')"
291
+ >
292
+ {{ uiLabels.watch }}
293
+ </SButton>
294
+ </template>
295
+ </SSectionHeader>
296
+ </div>
297
+ <div v-if="watcher?.message || notice" class="s-persona-run-workbench__messages">
298
+ <SMessage v-if="watcher?.message" :severity="watcher.statusSeverity === 'danger' ? 'danger' : 'warn'">
299
+ {{ watcher.message }}
300
+ </SMessage>
301
+ <SMessage v-if="notice" :severity="notice.severity" :title="notice.title">
302
+ {{ notice.message }}
303
+ </SMessage>
304
+ </div>
305
+ <div v-if="validatedDiagnostics.length > 0" class="s-persona-run-workbench__diagnostics">
306
+ <SCollapsiblePanel
307
+ summary="Диагностика проекта"
308
+ density="compact"
309
+ header-variant="minimal"
310
+ surface="plain"
311
+ >
312
+ <div class="s-persona-run-workbench__diagnostic-groups" aria-label="Сводка причин диагностики">
313
+ <SActionListItem
314
+ v-for="group in diagnosticGroups"
315
+ :key="group.category"
316
+ :title="group.title"
317
+ :description="group.description"
318
+ :status-label="group.countLabel"
319
+ :status-severity="group.statusSeverity"
320
+ :surface="{ mode: 'static' }"
321
+ trailing-layout="inline"
322
+ />
323
+ </div>
324
+ <div class="s-persona-run-workbench__diagnostic-controls">
325
+ <SInputText v-model="diagnosticSearch" placeholder="Поиск по диагностике" size="sm" />
326
+ <SSegmentedControl
327
+ v-model="diagnosticSeverity"
328
+ :options="diagnosticSeverityOptions"
329
+ label="Важность диагностических сообщений"
330
+ size="sm"
331
+ />
332
+ </div>
333
+ <SVirtualList
334
+ v-if="visibleDiagnostics.length > 0"
335
+ :key="`${diagnosticSeverity}:${diagnosticSearch}`"
336
+ :items="visibleDiagnostics"
337
+ :item-size="virtualRowSize"
338
+ :height="diagnosticsViewportHeight"
339
+ :item-key="(diagnostic) => diagnostic.id"
340
+ accessible-label="Диагностические сообщения"
341
+ :item-label="(diagnostic) => diagnostic.title"
342
+ >
343
+ <template #default="{ item }">
344
+ <SActionListItem
345
+ role="presentation"
346
+ :title="item.title"
347
+ :description="diagnosticDescription(item)"
348
+ :detail="item.message"
349
+ :status-label="item.statusLabel"
350
+ :status-severity="item.statusSeverity"
351
+ :surface="{ mode: 'static' }"
352
+ trailing-layout="inline"
353
+ height-mode="fill"
354
+ />
355
+ </template>
356
+ </SVirtualList>
357
+ <SEmptyState v-else title="Диагностические сообщения не найдены." size="sm" />
358
+ </SCollapsiblePanel>
359
+ </div>
360
+ </section>
361
+
362
+ <section class="s-persona-run-workbench__catalog">
363
+ <SSectionHeader
364
+ :title="uiLabels.catalogTitle"
365
+ size="sm"
366
+ spacing-after="sm"
367
+ trailing-layout="stacked"
368
+ >
369
+ <template #badges>
370
+ <SBadge severity="info">{{ visibleTests.length }} доступно</SBadge>
371
+ <SBadge v-if="catalogOverview?.unavailableTests" severity="danger">
372
+ {{ catalogOverview.unavailableTests }} недоступно
373
+ </SBadge>
374
+ <SBadge v-if="catalogOverview" severity="secondary">{{ catalogOverview.components }} компонентов</SBadge>
375
+ <SBadge v-if="catalogOverview" severity="secondary">{{ catalogOverview.pages }} страниц</SBadge>
376
+ <SBadge v-if="catalogOverview?.diagnosticErrors" severity="danger">
377
+ {{ personaCountLabel(catalogOverview.diagnosticErrors, ['ошибка', 'ошибки', 'ошибок']) }}
378
+ </SBadge>
379
+ <SBadge v-if="catalogOverview?.diagnosticWarnings" severity="warn">
380
+ {{ personaCountLabel(catalogOverview.diagnosticWarnings, ['предупреждение', 'предупреждения', 'предупреждений']) }}
381
+ </SBadge>
382
+ </template>
383
+ </SSectionHeader>
384
+ <SInputText
385
+ v-if="catalogState.status === 'ready'"
386
+ v-model="search"
387
+ :placeholder="uiLabels.searchPlaceholder"
388
+ size="sm"
389
+ />
390
+ <SAsyncState :state="catalogAsyncState">
391
+ <div class="s-persona-run-workbench__list">
392
+ <SVirtualList
393
+ :items="visibleTests"
394
+ :item-size="catalogItemSize"
395
+ :height="catalogViewportHeight"
396
+ :item-key="(test) => test.id"
397
+ :accessible-label="uiLabels.catalogTitle"
398
+ :item-label="(test) => test.title"
399
+ >
400
+ <template #default="{ item }">
401
+ <SActionListItem
402
+ role="presentation"
403
+ :title="item.title"
404
+ :description="item.meta"
405
+ :detail="item.detail"
406
+ :selected="selectedTest?.id === item.id"
407
+ :status-label="item.statusLabel"
408
+ :status-severity="item.statusSeverity ?? 'secondary'"
409
+ :badges="item.badges"
410
+ trailing-layout="inline"
411
+ height-mode="fill"
412
+ :surface="{
413
+ mode: 'action',
414
+ accessibleLabel: `Выбрать тест ${item.title}`,
415
+ pressed: selectedTest?.id === item.id,
416
+ }"
417
+ @activate="emit('select-test', item.id)"
418
+ />
419
+ </template>
420
+ </SVirtualList>
421
+ </div>
422
+ </SAsyncState>
423
+ </section>
424
+
425
+ <section class="s-persona-run-workbench__structure">
426
+ <SSectionHeader
427
+ :eyebrow="uiLabels.selectedTestTitle"
428
+ :title="selectedTest?.title ?? uiLabels.selectedTestTitle"
429
+ :description="selectedTest?.description || selectedTest?.meta"
430
+ :meta="selectedTest?.detail"
431
+ >
432
+ <template #badges>
433
+ <SBadge
434
+ v-for="badge in selectedTestBadges"
435
+ :key="badge.id"
436
+ :severity="badge.severity"
437
+ >
438
+ {{ badge.label }}
439
+ </SBadge>
440
+ </template>
441
+ <template #actions>
442
+ <SButton
443
+ size="sm"
444
+ severity="primary"
445
+ :disabled="!selectedTest || capabilities.runSelected === 'disabled'"
446
+ :loading="operation === 'starting-run'"
447
+ @click="selectedTest && emit('run-test', selectedTest.id)"
448
+ >
449
+ {{ uiLabels.runSelected }}
450
+ </SButton>
451
+ <SButton
452
+ size="sm"
453
+ severity="secondary"
454
+ :disabled="catalogTests.length === 0 || capabilities.runAll === 'disabled'"
455
+ :loading="operation === 'starting-run'"
456
+ @click="emit('run-all')"
457
+ >
458
+ {{ uiLabels.runAll }}
459
+ </SButton>
460
+ </template>
461
+ </SSectionHeader>
462
+
463
+ <SKeyValueGrid
464
+ v-if="selectedTest && (selectedTest.facts?.length ?? 0) > 0"
465
+ :items="selectedTest.facts ?? []"
466
+ :get-item-key="getPersonaRunConsoleFactKey"
467
+ variant="inline"
468
+ :columns="1"
469
+ />
470
+ <nav
471
+ v-if="selectedTestLinks.length > 0"
472
+ class="s-persona-run-workbench__links"
473
+ aria-label="Связанные материалы теста"
474
+ >
475
+ <SLink
476
+ v-for="link in selectedTestLinks"
477
+ :key="link.id"
478
+ :href="link.url"
479
+ target="_blank"
480
+ external
481
+ size="sm"
482
+ underline="always"
483
+ wrap="anywhere"
484
+ >
485
+ {{ link.label }}
486
+ </SLink>
487
+ </nav>
488
+
489
+ <div class="s-persona-run-workbench__subheading">
490
+ <SSectionHeader :title="uiLabels.structureTitle" size="sm" spacing-after="md" />
491
+ </div>
492
+ <div v-if="selectedTest?.structureNotice" class="s-persona-run-workbench__structure-notice">
493
+ <SMessage
494
+ :severity="selectedTest.structureNotice.severity"
495
+ :title="selectedTest.structureNotice.title"
496
+ >
497
+ {{ selectedTest.structureNotice.message }}
498
+ </SMessage>
499
+ </div>
500
+ <SVirtualList
501
+ v-if="selectedTest && selectedTest.steps.length > 0"
502
+ :items="selectedTest.steps"
503
+ :item-size="stepItemSize"
504
+ :height="stepsViewportHeight"
505
+ :item-key="(step) => step.id"
506
+ :accessible-label="uiLabels.structureTitle"
507
+ :item-label="(step) => step.title"
508
+ >
509
+ <template #default="{ item }">
510
+ <SActionListItem
511
+ role="presentation"
512
+ :title="item.title"
513
+ :description="item.meta || item.kind || ''"
514
+ :detail="item.detail"
515
+ :badges="item.badges"
516
+ :indent-level="item.depth"
517
+ :surface="{ mode: 'static' }"
518
+ height-mode="fill"
519
+ />
520
+ </template>
521
+ </SVirtualList>
522
+ <SEmptyState v-else :title="uiLabels.emptyStructure" size="sm" />
523
+ </section>
524
+
525
+ <section class="s-persona-run-workbench__runs">
526
+ <SPersonaRunConsole
527
+ :runs-state="runsState"
528
+ :selected-run-id="selectedRunId"
529
+ :items-state="itemsState"
530
+ :events-state="eventsState"
531
+ :capabilities="{ cancelRun: capabilities.cancelRun }"
532
+ :operation="consoleOperation"
533
+ :report-summary="reportSummary"
534
+ :resolved-labels="uiLabels"
535
+ :density="density"
536
+ @select-run="emit('select-run', $event)"
537
+ @load-more-runs="emit('load-more-runs')"
538
+ @refresh-events="emit('refresh-events')"
539
+ @cancel-run="emit('cancel-run')"
540
+ @load-more-items="emit('load-more-items')"
541
+ @append-events="emit('append-events')"
542
+ @load-more-events="emit('load-more-events')"
543
+ />
544
+ </section>
545
+ </div>
546
+ </div>
547
+ </template>
548
+
549
+ <style lang="postcss" src="./SPersonaRunWorkbench.css" scoped></style>
@@ -0,0 +1,40 @@
1
+ @reference "../../../../styles/reference.css";
2
+
3
+ .s-persona-run-console-shell {
4
+ @apply min-h-0 min-w-0;
5
+ container-type: inline-size;
6
+ }
7
+
8
+ .s-persona-run-console {
9
+ @apply grid min-h-0 min-w-0 gap-4;
10
+ }
11
+
12
+ .s-persona-run-console[data-density='comfortable'] {
13
+ @apply gap-4;
14
+ }
15
+
16
+ .s-persona-run-console[data-density='compact'] {
17
+ @apply gap-3;
18
+ }
19
+
20
+ .s-persona-run-console__section {
21
+ @apply min-h-0 min-w-0;
22
+ }
23
+
24
+ .s-persona-run-console__detail {
25
+ @apply grid min-h-0 gap-4;
26
+ }
27
+
28
+ .s-persona-run-console__actions-line {
29
+ @apply mt-3 flex flex-wrap items-center gap-2;
30
+ }
31
+
32
+ @container (min-width: 52rem) {
33
+ .s-persona-run-console[data-density='comfortable'] {
34
+ grid-template-columns: minmax(18rem, 0.72fr) minmax(28rem, 1.28fr);
35
+ }
36
+
37
+ .s-persona-run-console[data-density='compact'] {
38
+ grid-template-columns: minmax(16rem, 0.72fr) minmax(24rem, 1.28fr);
39
+ }
40
+ }