@cat-factory/app 0.311.2 → 0.311.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -4
- package/app/app.config.ts +7 -0
- package/app/components/assistant/AssistantModal.vue +2 -5
- package/app/components/auth/AuthGate.vue +2 -1
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +14 -15
- package/app/components/binaryOutput/StoredAssetView.vue +1 -3
- package/app/components/board/AddTaskModal.vue +3 -10
- package/app/components/board/AgentFailureCard.vue +4 -5
- package/app/components/board/AgentStopButton.vue +5 -7
- package/app/components/board/BoardCanvas.vue +31 -30
- package/app/components/board/CreateInitiativeModal.vue +2 -4
- package/app/components/board/RecurringPipelineModal.vue +2 -7
- package/app/components/board/nodes/AgentChip.vue +3 -2
- package/app/components/board/nodes/BlockNode.vue +14 -11
- package/app/components/board/nodes/InitiativeCard.vue +6 -3
- package/app/components/bootstrap/AdoptionReviewModal.vue +2 -5
- package/app/components/bootstrap/BootstrapModal.vue +6 -15
- package/app/components/bootstrap/BootstrapRunSteps.vue +3 -1
- package/app/components/brainstorm/BrainstormWindow.vue +32 -26
- package/app/components/bugFishing/BugFishingWindow.vue +15 -13
- package/app/components/clarity/ClarityReviewWindow.vue +31 -26
- package/app/components/common/ClarificationItem.vue +3 -1
- package/app/components/common/EmptyState.vue +41 -19
- package/app/components/common/InterviewGateNotice.vue +3 -1
- package/app/components/common/Spinner.vue +28 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +5 -5
- package/app/components/docs/DocInterviewWindow.vue +6 -6
- package/app/components/documents/ContextDocumentPicker.vue +2 -2
- package/app/components/documents/DocumentImportModal.vue +9 -8
- package/app/components/documents/DocumentSourceConnectModal.vue +10 -10
- package/app/components/documents/DocumentTemplatesModal.vue +13 -7
- package/app/components/documents/RepoContextDocPicker.vue +2 -2
- package/app/components/documents/SpawnPreviewModal.vue +4 -6
- package/app/components/documents/StartFromDesignModal.vue +2 -1
- package/app/components/documents/TaskContextDocs.vue +4 -5
- package/app/components/environments/EnvironmentStatusPanel.vue +11 -8
- package/app/components/environments/steps/EnvPickStep.vue +7 -3
- package/app/components/environments/steps/EnvPreflightStep.vue +6 -5
- package/app/components/focus/BlockFocusView.vue +6 -5
- package/app/components/followUp/FollowUpWindow.vue +7 -9
- package/app/components/forkDecision/ForkDecisionWindow.vue +9 -8
- package/app/components/foundational/FoundationalServiceCatalogList.vue +9 -5
- package/app/components/foundational/FoundationalServiceRegistry.vue +11 -7
- package/app/components/foundational/FoundationalServiceSources.vue +20 -22
- package/app/components/foundational/FoundationalSuppressions.vue +2 -2
- package/app/components/foundational/ServiceCatalogConnection.vue +15 -15
- package/app/components/fragments/FragmentLibraryManager.vue +36 -34
- package/app/components/fragments/FragmentSelector.vue +7 -3
- package/app/components/gates/GateResultView.vue +6 -9
- package/app/components/github/AddServiceFromRepoModal.vue +6 -10
- package/app/components/github/GitHubConnect.vue +10 -17
- package/app/components/github/GitHubPanel.vue +44 -47
- package/app/components/github/RepoTreeBrowser.vue +9 -5
- package/app/components/guidedReview/GuidedReviewModal.vue +16 -6
- package/app/components/guidedReview/GuidedReviewOverview.vue +2 -1
- package/app/components/guidedReview/GuidedReviewPicker.vue +10 -7
- package/app/components/guidedReview/GuidedReviewThread.vue +9 -4
- package/app/components/humanTest/HumanTestWindow.vue +2 -1
- package/app/components/initiative/InitiativePlanningWindow.vue +6 -6
- package/app/components/initiative/InitiativeTrackerWindow.vue +11 -11
- package/app/components/judge/JudgeResultView.vue +2 -7
- package/app/components/kaizen/KaizenPanel.vue +20 -5
- package/app/components/kaizen/KaizenStepStatus.vue +2 -1
- package/app/components/layout/AccountAuditLog.vue +11 -3
- package/app/components/layout/AccountDeploymentSettings.vue +17 -62
- package/app/components/layout/AccountFailureKindRules.vue +6 -5
- package/app/components/layout/AccountModelPolicySettings.vue +2 -6
- package/app/components/layout/AccountPlatformAlertSettings.vue +5 -13
- package/app/components/layout/AccountRiskPolicySettings.vue +13 -14
- package/app/components/layout/AccountRunCredentialSettings.vue +2 -2
- package/app/components/layout/AccountTeamSettings.vue +12 -9
- package/app/components/layout/BoardSwitcher.vue +2 -2
- package/app/components/layout/BoardToolbar.vue +3 -11
- package/app/components/layout/CommandBar.vue +7 -3
- package/app/components/layout/ConnectionStatusBanner.vue +2 -1
- package/app/components/layout/IntegrationsHub.vue +7 -3
- package/app/components/layout/ModelProvidersHub.vue +7 -3
- package/app/components/layout/NotificationsInbox.vue +3 -11
- package/app/components/layout/WorkspaceMembersSettings.vue +12 -5
- package/app/components/media/ArtifactLightbox.vue +4 -3
- package/app/components/notifications/NotificationSettingsPanel.vue +4 -11
- package/app/components/observability/ToolCallList.vue +12 -17
- package/app/components/panels/AgentStepDetail.vue +5 -5
- package/app/components/panels/GenericStructuredResultView.vue +6 -9
- package/app/components/panels/InspectorPanel.vue +2 -1
- package/app/components/panels/MergerResultView.vue +5 -6
- package/app/components/panels/ObservabilityPanel.vue +26 -41
- package/app/components/panels/OperatorDashboardPanel.vue +29 -17
- package/app/components/panels/ReportsPanel.vue +17 -11
- package/app/components/panels/ReportsSpendBreakdown.vue +7 -3
- package/app/components/panels/RunDetailLoadState.vue +4 -3
- package/app/components/panels/StepContainerStatus.vue +4 -3
- package/app/components/panels/StepDelegatedStatus.vue +3 -2
- package/app/components/panels/StepMetadataCard.vue +6 -8
- package/app/components/panels/StepRunMeta.vue +2 -5
- package/app/components/panels/inspector/ContainerSummary.vue +7 -1
- package/app/components/panels/inspector/EpicChildren.vue +7 -3
- package/app/components/panels/inspector/FrontendConfig.vue +7 -3
- package/app/components/panels/inspector/InitiativeInspector.vue +5 -2
- package/app/components/panels/inspector/ServiceConnections.vue +7 -3
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -6
- package/app/components/panels/inspector/ServiceTestSecrets.vue +2 -6
- package/app/components/panels/inspector/ServiceTestingContext.vue +2 -6
- package/app/components/panels/inspector/ServiceValidationConfig.vue +38 -39
- package/app/components/panels/inspector/TaskExecution.vue +5 -6
- package/app/components/panels/inspector/TaskRunSettings.vue +7 -3
- package/app/components/panels/inspector/TaskStructure.vue +0 -1
- package/app/components/pipeline/AgentPromptEditor.vue +25 -18
- package/app/components/pipeline/PipelineBuilder.vue +14 -24
- package/app/components/pipeline/PipelineProgress.vue +16 -17
- package/app/components/prReview/PrReviewPhaseBadge.vue +3 -2
- package/app/components/prReview/PrReviewWindow.vue +16 -20
- package/app/components/providers/ApiKeysSection.vue +2 -6
- package/app/components/providers/PersonalCredentialModal.vue +2 -6
- package/app/components/providers/PersonalSubscriptionSection.vue +3 -7
- package/app/components/providers/VendorCredentialsModal.vue +2 -6
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -6
- package/app/components/ralph/RalphLoopResultView.vue +2 -7
- package/app/components/requirements/RequirementsReviewWindow.vue +44 -39
- package/app/components/sandbox/SandboxPanel.vue +37 -25
- package/app/components/settings/AccountSettingsPanel.vue +6 -3
- package/app/components/settings/ApiTokensPanel.vue +2 -6
- package/app/components/settings/BudgetSettings.vue +2 -2
- package/app/components/settings/CapabilityCredentialsPanel.vue +2 -6
- package/app/components/settings/CloudflareHandlerSection.vue +3 -7
- package/app/components/settings/ConsensusGroupsSection.vue +7 -3
- package/app/components/settings/CustomManifestTypeEditor.vue +7 -3
- package/app/components/settings/DefaultProvisionTypeSection.vue +2 -2
- package/app/components/settings/InfraHandlersConfigurator.vue +8 -11
- package/app/components/settings/IssueTrackerPanel.vue +9 -9
- package/app/components/settings/LocalContainerPoolSettings.vue +2 -6
- package/app/components/settings/LocalModelEndpointsPanel.vue +5 -7
- package/app/components/settings/McpAuthorizeScreen.vue +8 -3
- package/app/components/settings/McpOAuthCallbackScreen.vue +2 -1
- package/app/components/settings/ModelConfigurationPanel.vue +45 -17
- package/app/components/settings/ObservabilityConnectionPanel.vue +4 -15
- package/app/components/settings/OpenRouterCatalogPanel.vue +8 -24
- package/app/components/settings/PackageRegistriesPanel.vue +2 -6
- package/app/components/settings/ProviderConnectionTab.vue +3 -7
- package/app/components/settings/RiskPolicyPanel.vue +4 -16
- package/app/components/settings/SharedStacksPanel.vue +8 -18
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +10 -6
- package/app/components/settings/UsageSettings.vue +11 -2
- package/app/components/settings/UserSecretsSection.vue +5 -7
- package/app/components/settings/WorkspaceMetadataSettings.vue +9 -9
- package/app/components/settings/WorkspaceSettingsPanel.vue +3 -11
- package/app/components/skills/ReviewSkillQueue.vue +7 -3
- package/app/components/skills/SkillLibraryManager.vue +20 -18
- package/app/components/slack/SlackPanel.vue +5 -8
- package/app/components/spec/ServiceSpecWindow.vue +32 -29
- package/app/components/tasks/BugHuntModal.vue +23 -19
- package/app/components/tasks/ContextIssuePicker.vue +2 -2
- package/app/components/tasks/TaskContextIssues.vue +4 -5
- package/app/components/tasks/TaskImportModal.vue +10 -8
- package/app/components/tasks/TaskSourceConnectModal.vue +11 -11
- package/app/components/testing/TestReportWindow.vue +6 -12
- package/app/components/tutorial/TutorialCatalogue.vue +7 -3
- package/app/components/tutorial/TutorialOverlay.vue +2 -1
- package/app/components/tutorial/TutorialPrompt.vue +7 -3
- package/app/components/vcs/GitLabConnect.vue +2 -6
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +9 -4
- package/app/composables/toastTitleKeys.spec.ts +143 -0
- package/app/composables/useActionToast.spec.ts +71 -0
- package/app/composables/useActionToast.ts +101 -0
- package/app/composables/useBlockDeletion.ts +3 -6
- package/app/composables/useConfirmAction.ts +5 -3
- package/app/composables/useContextLinking.spec.ts +3 -0
- package/app/composables/useContextLinking.ts +11 -16
- package/app/composables/useCopyToClipboard.ts +4 -3
- package/app/composables/useNavContributions.ts +4 -5
- package/app/composables/usePipelineErrorToast.spec.ts +24 -0
- package/app/composables/usePipelineErrorToast.ts +26 -2
- package/app/composables/usePipelineLibraryActions.ts +8 -9
- package/app/pages/index.vue +3 -2
- package/app/stores/board/context.ts +4 -3
- package/app/stores/board/placement.ts +8 -20
- package/app/stores/board/removal.ts +4 -13
- package/app/stores/board.spec.ts +10 -1
- package/app/stores/board.ts +3 -2
- package/app/utils/bootstrapSteps.ts +11 -2
- package/app/utils/pipelineRender.ts +21 -8
- package/i18n/locales/de.json +12 -30
- package/i18n/locales/en.json +12 -30
- package/i18n/locales/es.json +12 -30
- package/i18n/locales/fr.json +12 -30
- package/i18n/locales/he.json +12 -30
- package/i18n/locales/it.json +12 -30
- package/i18n/locales/ja.json +12 -30
- package/i18n/locales/pl.json +12 -30
- package/i18n/locales/tr.json +12 -30
- package/i18n/locales/uk.json +12 -30
- package/package.json +1 -1
|
@@ -20,6 +20,7 @@ import GitHubRepoSearchSelect from '~/components/github/GitHubRepoSearchSelect.v
|
|
|
20
20
|
import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
|
|
21
21
|
import GitHubDocUrlImport from '~/components/fragments/GitHubDocUrlImport.vue'
|
|
22
22
|
import IconButton from '~/components/common/IconButton.vue'
|
|
23
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
23
24
|
|
|
24
25
|
const props = withDefaults(
|
|
25
26
|
defineProps<{
|
|
@@ -41,7 +42,7 @@ const library =
|
|
|
41
42
|
: useFragmentLibrary(props.kind, props.ownerId)
|
|
42
43
|
const documents = useDocumentsStore()
|
|
43
44
|
const github = useGitHubStore()
|
|
44
|
-
const
|
|
45
|
+
const actionToast = useActionToast()
|
|
45
46
|
const { present } = usePipelineErrorToast()
|
|
46
47
|
const { t, d } = useI18n()
|
|
47
48
|
const { confirm } = useConfirm()
|
|
@@ -207,7 +208,7 @@ async function saveEdit() {
|
|
|
207
208
|
.filter(Boolean),
|
|
208
209
|
})
|
|
209
210
|
editDraft.value = null
|
|
210
|
-
|
|
211
|
+
actionToast.success('fragments.toast.updated')
|
|
211
212
|
} catch (e) {
|
|
212
213
|
present(e, 'fragments.toast.updateFailed')
|
|
213
214
|
}
|
|
@@ -229,7 +230,7 @@ async function createFragment() {
|
|
|
229
230
|
.filter(Boolean),
|
|
230
231
|
})
|
|
231
232
|
draft.value = { title: '', summary: '', body: '', brief: '', tags: '' }
|
|
232
|
-
|
|
233
|
+
actionToast.success('fragments.toast.added')
|
|
233
234
|
} catch (e) {
|
|
234
235
|
present(e, 'fragments.toast.addFailed')
|
|
235
236
|
} finally {
|
|
@@ -250,7 +251,7 @@ async function removeFragment(id: string) {
|
|
|
250
251
|
await withRow(`remove:${id}`, async () => {
|
|
251
252
|
try {
|
|
252
253
|
await library.remove(id)
|
|
253
|
-
|
|
254
|
+
actionToast.success('fragments.toast.removed')
|
|
254
255
|
} catch (e) {
|
|
255
256
|
present(e, 'fragments.toast.removeFailed')
|
|
256
257
|
}
|
|
@@ -415,7 +416,7 @@ async function linkDocumentFragment() {
|
|
|
415
416
|
// Free-text field: exactly one ref.
|
|
416
417
|
await library.createDocumentFragment({ source, ref: docDraft.value.ref.trim(), tags })
|
|
417
418
|
docDraft.value = { source: '', ref: '', tags: '' }
|
|
418
|
-
|
|
419
|
+
actionToast.success('fragments.toast.documentLinked')
|
|
419
420
|
return
|
|
420
421
|
}
|
|
421
422
|
|
|
@@ -440,9 +441,9 @@ async function linkDocumentFragment() {
|
|
|
440
441
|
if (linked > 0) {
|
|
441
442
|
// A vue-i18n plural message (count as the plural choice) so Slavic few/many forms
|
|
442
443
|
// render correctly, and the count==1 case reads naturally too.
|
|
443
|
-
toast.
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
actionToast.success('fragments.toast.documentsLinked', {
|
|
445
|
+
params: { count: linked },
|
|
446
|
+
plural: linked,
|
|
446
447
|
})
|
|
447
448
|
}
|
|
448
449
|
if (firstError) {
|
|
@@ -462,7 +463,7 @@ async function refreshFragment(id: string) {
|
|
|
462
463
|
await withRow(`refresh:${id}`, async () => {
|
|
463
464
|
try {
|
|
464
465
|
await library.refreshDocumentFragment(id)
|
|
465
|
-
|
|
466
|
+
actionToast.success('fragments.toast.refreshed')
|
|
466
467
|
} catch (e) {
|
|
467
468
|
present(e, 'fragments.toast.refreshFailed')
|
|
468
469
|
}
|
|
@@ -519,7 +520,7 @@ async function linkSource() {
|
|
|
519
520
|
// `syncSource` row wrapper): a failure here should surface as a link failure, and
|
|
520
521
|
// the form-level `linkingSource` spinner already covers the whole operation.
|
|
521
522
|
await library.syncSource(source.id)
|
|
522
|
-
|
|
523
|
+
actionToast.success('fragments.toast.sourceLinked')
|
|
523
524
|
} catch (e) {
|
|
524
525
|
present(e, 'fragments.toast.linkSourceFailed')
|
|
525
526
|
} finally {
|
|
@@ -531,13 +532,8 @@ async function syncSource(id: string) {
|
|
|
531
532
|
await withRow(`sync:${id}`, async () => {
|
|
532
533
|
try {
|
|
533
534
|
const result = await library.syncSource(id)
|
|
534
|
-
toast.
|
|
535
|
-
|
|
536
|
-
updated: result.upserted,
|
|
537
|
-
removed: result.tombstoned,
|
|
538
|
-
}),
|
|
539
|
-
icon: 'i-lucide-refresh-cw',
|
|
540
|
-
color: 'info',
|
|
535
|
+
actionToast.success('fragments.toast.synced', {
|
|
536
|
+
params: { updated: result.upserted, removed: result.tombstoned },
|
|
541
537
|
})
|
|
542
538
|
} catch (e) {
|
|
543
539
|
present(e, 'fragments.toast.syncFailed')
|
|
@@ -549,12 +545,9 @@ async function checkSource(id: string) {
|
|
|
549
545
|
await withRow(`check:${id}`, async () => {
|
|
550
546
|
try {
|
|
551
547
|
const status = await library.checkSource(id)
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
: t('fragments.toast.upToDate'),
|
|
556
|
-
icon: status.changed ? 'i-lucide-bell-dot' : 'i-lucide-check',
|
|
557
|
-
})
|
|
548
|
+
actionToast.info(
|
|
549
|
+
status.changed ? 'fragments.toast.changesAvailable' : 'fragments.toast.upToDate',
|
|
550
|
+
)
|
|
558
551
|
} catch (e) {
|
|
559
552
|
present(e, 'fragments.toast.checkSourceFailed')
|
|
560
553
|
}
|
|
@@ -575,7 +568,7 @@ async function unlinkSource(id: string) {
|
|
|
575
568
|
await withRow(`unlink:${id}`, async () => {
|
|
576
569
|
try {
|
|
577
570
|
await library.unlinkSource(id)
|
|
578
|
-
|
|
571
|
+
actionToast.success('fragments.toast.sourceUnlinked')
|
|
579
572
|
} catch (e) {
|
|
580
573
|
present(e, 'fragments.toast.unlinkSourceFailed')
|
|
581
574
|
}
|
|
@@ -771,13 +764,16 @@ async function unlinkSource(id: string) {
|
|
|
771
764
|
</div>
|
|
772
765
|
</div>
|
|
773
766
|
</div>
|
|
774
|
-
<
|
|
775
|
-
|
|
767
|
+
<EmptyState
|
|
768
|
+
v-if="!library.fragments.length"
|
|
769
|
+
compact
|
|
770
|
+
icon="i-lucide-puzzle"
|
|
771
|
+
:title="
|
|
776
772
|
isWorkspace
|
|
777
773
|
? t('fragments.authored.empty.workspace')
|
|
778
774
|
: t('fragments.authored.empty.account')
|
|
779
|
-
|
|
780
|
-
|
|
775
|
+
"
|
|
776
|
+
/>
|
|
781
777
|
|
|
782
778
|
<div class="rounded-md border border-default p-3">
|
|
783
779
|
<p class="mb-2 text-sm font-medium">{{ t('fragments.authored.addTitle') }}</p>
|
|
@@ -887,9 +883,12 @@ async function unlinkSource(id: string) {
|
|
|
887
883
|
/>
|
|
888
884
|
</div>
|
|
889
885
|
</div>
|
|
890
|
-
<
|
|
891
|
-
|
|
892
|
-
|
|
886
|
+
<EmptyState
|
|
887
|
+
v-if="!documentFragments.length"
|
|
888
|
+
compact
|
|
889
|
+
icon="i-lucide-file-text"
|
|
890
|
+
:title="t('fragments.documents.empty')"
|
|
891
|
+
/>
|
|
893
892
|
|
|
894
893
|
<div class="rounded-md border border-default p-3">
|
|
895
894
|
<p class="mb-2 text-sm font-medium">{{ t('fragments.documents.linkTitle') }}</p>
|
|
@@ -1053,9 +1052,12 @@ async function unlinkSource(id: string) {
|
|
|
1053
1052
|
/>
|
|
1054
1053
|
</div>
|
|
1055
1054
|
</div>
|
|
1056
|
-
<
|
|
1057
|
-
|
|
1058
|
-
|
|
1055
|
+
<EmptyState
|
|
1056
|
+
v-if="!library.sources.length"
|
|
1057
|
+
compact
|
|
1058
|
+
icon="i-lucide-git-branch"
|
|
1059
|
+
:title="t('fragments.sources.empty')"
|
|
1060
|
+
/>
|
|
1059
1061
|
|
|
1060
1062
|
<div class="rounded-md border border-default p-3">
|
|
1061
1063
|
<p class="mb-2 text-sm font-medium">{{ t('fragments.sources.linkTitle') }}</p>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import type { PromptFragment } from '~/types/domain'
|
|
12
12
|
import { buildFragmentCategoryGroups } from '~/utils/fragmentPicker'
|
|
13
13
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
14
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
14
15
|
|
|
15
16
|
const props = withDefaults(
|
|
16
17
|
defineProps<{
|
|
@@ -123,9 +124,12 @@ function manageAccount() {
|
|
|
123
124
|
</UButton>
|
|
124
125
|
</div>
|
|
125
126
|
</template>
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
<EmptyState
|
|
128
|
+
v-else
|
|
129
|
+
compact
|
|
130
|
+
icon="i-lucide-puzzle"
|
|
131
|
+
:title="t('inspector.fragments.pickerEmpty')"
|
|
132
|
+
/>
|
|
129
133
|
|
|
130
134
|
<div class="mt-1 border-t border-default pt-1">
|
|
131
135
|
<UButton
|
|
@@ -14,6 +14,7 @@ import AttemptEntryHeader from '~/components/panels/AttemptEntryHeader.vue'
|
|
|
14
14
|
import GateFailingCheckList from '~/components/gates/GateFailingCheckList.vue'
|
|
15
15
|
import CopyButton from '~/components/common/CopyButton.vue'
|
|
16
16
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
17
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
17
18
|
|
|
18
19
|
const board = useBoardStore()
|
|
19
20
|
const execution = useExecutionStore()
|
|
@@ -213,16 +214,12 @@ const conflictVerdict = computed(() => {
|
|
|
213
214
|
<div class="flex min-h-0 flex-1">
|
|
214
215
|
<!-- Main: the conclusion -->
|
|
215
216
|
<div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
|
|
216
|
-
<
|
|
217
|
+
<EmptyState
|
|
217
218
|
v-if="!gate"
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
<p class="max-w-sm text-2xs text-dimmed">
|
|
223
|
-
{{ t('gates.noActivityHint') }}
|
|
224
|
-
</p>
|
|
225
|
-
</div>
|
|
219
|
+
:icon="meta.icon"
|
|
220
|
+
:title="t('gates.noActivity')"
|
|
221
|
+
:description="t('gates.noActivityHint')"
|
|
222
|
+
/>
|
|
226
223
|
|
|
227
224
|
<template v-else>
|
|
228
225
|
<!-- Passed -->
|
|
@@ -41,7 +41,7 @@ const ui = useUiStore()
|
|
|
41
41
|
const github = useGitHubStore()
|
|
42
42
|
const board = useBoardStore()
|
|
43
43
|
const services = useServicesStore()
|
|
44
|
-
const
|
|
44
|
+
const actionToast = useActionToast()
|
|
45
45
|
const { present } = usePipelineErrorToast()
|
|
46
46
|
const { freeFramePosition, focusFrame } = useFramePlacement()
|
|
47
47
|
|
|
@@ -349,11 +349,8 @@ async function add() {
|
|
|
349
349
|
// Centre the camera on the newly imported service.
|
|
350
350
|
await focusFrame(block.id)
|
|
351
351
|
configuredBlockId.value = block.id
|
|
352
|
-
toast.
|
|
353
|
-
title: t('github.addService.toast.addedTitle'),
|
|
352
|
+
actionToast.success('github.addService.toast.addedTitle', {
|
|
354
353
|
description: t('github.addService.toast.addedDescription', { title: block.title }),
|
|
355
|
-
icon: 'i-lucide-check',
|
|
356
|
-
color: 'success',
|
|
357
354
|
})
|
|
358
355
|
} catch (e) {
|
|
359
356
|
present(e, 'github.addService.toast.addFailedTitle')
|
|
@@ -417,17 +414,16 @@ async function addServices() {
|
|
|
417
414
|
const lastBlockId = created.at(-1)?.blockId
|
|
418
415
|
if (lastBlockId) await focusFrame(lastBlockId)
|
|
419
416
|
selectedDirectories.value = []
|
|
420
|
-
|
|
421
|
-
title: t('github.addService.toast.servicesAddedTitle'),
|
|
417
|
+
const added = {
|
|
422
418
|
description: [
|
|
423
419
|
t('github.addService.toast.servicesAddedDescription', { count: dirs.length }, dirs.length),
|
|
424
420
|
frontendNote(designatedDirectory, wiringLanded),
|
|
425
421
|
]
|
|
426
422
|
.filter(Boolean)
|
|
427
423
|
.join(' '),
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
424
|
+
}
|
|
425
|
+
if (wiringLanded) actionToast.success('github.addService.toast.servicesAddedTitle', added)
|
|
426
|
+
else actionToast.warning('github.addService.toast.servicesAddedTitle', added)
|
|
431
427
|
} catch (e) {
|
|
432
428
|
present(e, 'github.addService.toast.addFailedTitle')
|
|
433
429
|
} finally {
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
// react to. Shared by the GitHub panel and the bootstrap modal so the connect
|
|
8
8
|
// flow lives in one place.
|
|
9
9
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
10
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
10
11
|
|
|
11
12
|
const { t } = useI18n()
|
|
12
13
|
const github = useGitHubStore()
|
|
13
|
-
const
|
|
14
|
+
const actionToast = useActionToast()
|
|
14
15
|
const { present } = usePipelineErrorToast()
|
|
15
16
|
|
|
16
17
|
const installing = ref(false)
|
|
@@ -48,11 +49,7 @@ async function connect(id: number, onDone?: () => void) {
|
|
|
48
49
|
try {
|
|
49
50
|
await github.connect(id)
|
|
50
51
|
onDone?.()
|
|
51
|
-
toast.
|
|
52
|
-
title: t('github.connect.toast.connected'),
|
|
53
|
-
icon: 'i-lucide-check',
|
|
54
|
-
color: 'success',
|
|
55
|
-
})
|
|
52
|
+
actionToast.success('github.connect.toast.connected')
|
|
56
53
|
} catch (e) {
|
|
57
54
|
present(e, 'github.connect.errors.connect')
|
|
58
55
|
} finally {
|
|
@@ -90,20 +87,16 @@ async function connectManually() {
|
|
|
90
87
|
</UButton>
|
|
91
88
|
</div>
|
|
92
89
|
|
|
93
|
-
<div
|
|
94
|
-
v-
|
|
95
|
-
class="flex items-center gap-2 py-3 text-sm text-muted"
|
|
96
|
-
>
|
|
97
|
-
<UIcon name="i-lucide-loader" class="h-4 w-4 animate-spin" />
|
|
98
|
-
{{ t('github.connect.lookingForInstallations') }}
|
|
90
|
+
<div v-if="github.loadingInstallations && !github.installations.length" class="space-y-2">
|
|
91
|
+
<USkeleton v-for="i in 2" :key="i" class="h-12 w-full" />
|
|
99
92
|
</div>
|
|
100
93
|
|
|
101
|
-
<
|
|
94
|
+
<EmptyState
|
|
102
95
|
v-else-if="!github.installations.length"
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
96
|
+
compact
|
|
97
|
+
icon="i-lucide-github"
|
|
98
|
+
:title="t('github.connect.noInstallations')"
|
|
99
|
+
/>
|
|
107
100
|
|
|
108
101
|
<div
|
|
109
102
|
v-for="inst in github.installations"
|
|
@@ -12,12 +12,13 @@ import VcsConnectSurfaces from '~/components/vcs/VcsConnectSurfaces.vue'
|
|
|
12
12
|
import IntegrationBackTitle from '~/components/layout/IntegrationBackTitle.vue'
|
|
13
13
|
import { VCS_PROVIDER_ICONS, VCS_PROVIDER_LABELS } from '~/utils/vcs'
|
|
14
14
|
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
15
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
15
16
|
|
|
16
17
|
const { t } = useI18n()
|
|
17
18
|
const ui = useUiStore()
|
|
18
19
|
const access = useWorkspaceAccess()
|
|
19
20
|
const github = useGitHubStore()
|
|
20
|
-
const
|
|
21
|
+
const actionToast = useActionToast()
|
|
21
22
|
const { present } = usePipelineErrorToast()
|
|
22
23
|
const { confirm } = useConfirm()
|
|
23
24
|
|
|
@@ -80,6 +81,12 @@ const PULL_TERMS = {
|
|
|
80
81
|
},
|
|
81
82
|
} as const satisfies Record<VcsProvider, Record<string, string>>
|
|
82
83
|
const pullTerms = computed(() => PULL_TERMS[github.provider])
|
|
84
|
+
// Skeleton rows only while there is nothing to show yet. `github.loading` is also set by every
|
|
85
|
+
// refresh (after a resync, on re-open), and blanking lists the reader is looking at to skeletons
|
|
86
|
+
// and back loses their place for no gain.
|
|
87
|
+
const firstLoad = computed(
|
|
88
|
+
() => github.loading && !github.repos.length && !github.pulls.length && !github.issues.length,
|
|
89
|
+
)
|
|
83
90
|
|
|
84
91
|
// What the repo picker can offer, and why a repo might be missing from it, differ by how the
|
|
85
92
|
// workspace authenticates rather than by provider: an App installation is shared across the
|
|
@@ -110,9 +117,8 @@ async function disconnect() {
|
|
|
110
117
|
if (!ok) return
|
|
111
118
|
try {
|
|
112
119
|
await github.disconnect()
|
|
113
|
-
toast.
|
|
114
|
-
|
|
115
|
-
icon: 'i-lucide-unplug',
|
|
120
|
+
actionToast.success('vcs.panel.toast.disconnected', {
|
|
121
|
+
params: { provider: VCS_PROVIDER_LABELS[github.provider] },
|
|
116
122
|
})
|
|
117
123
|
} catch (e) {
|
|
118
124
|
present(e, 'github.panel.errors.disconnect')
|
|
@@ -122,11 +128,7 @@ async function disconnect() {
|
|
|
122
128
|
async function resync(full = false) {
|
|
123
129
|
try {
|
|
124
130
|
const { status } = await github.resync({ full })
|
|
125
|
-
toast.
|
|
126
|
-
title: t('github.panel.toast.resync', { status }),
|
|
127
|
-
icon: 'i-lucide-refresh-cw',
|
|
128
|
-
color: 'info',
|
|
129
|
-
})
|
|
131
|
+
actionToast.info('github.panel.toast.resync', { params: { status } })
|
|
130
132
|
} catch (e) {
|
|
131
133
|
present(e, 'github.panel.errors.resync')
|
|
132
134
|
}
|
|
@@ -168,11 +170,7 @@ async function saveRepos() {
|
|
|
168
170
|
try {
|
|
169
171
|
await github.setLinkedRepos([...selected.value])
|
|
170
172
|
managing.value = false
|
|
171
|
-
toast.
|
|
172
|
-
title: t('github.panel.toast.reposUpdated'),
|
|
173
|
-
icon: 'i-lucide-check',
|
|
174
|
-
color: 'success',
|
|
175
|
-
})
|
|
173
|
+
actionToast.success('github.panel.toast.reposUpdated')
|
|
176
174
|
} catch (e) {
|
|
177
175
|
present(e, 'github.panel.errors.updateRepos')
|
|
178
176
|
}
|
|
@@ -207,11 +205,7 @@ async function createBranch(repo: GitHubRepo) {
|
|
|
207
205
|
try {
|
|
208
206
|
await github.createBranch(repo.githubId, { name, fromSha })
|
|
209
207
|
branchForm.value = { name: '', fromSha: '' }
|
|
210
|
-
toast.
|
|
211
|
-
title: t('github.panel.toast.branchCreated', { name }),
|
|
212
|
-
icon: 'i-lucide-check',
|
|
213
|
-
color: 'success',
|
|
214
|
-
})
|
|
208
|
+
actionToast.success('github.panel.toast.branchCreated', { params: { name } })
|
|
215
209
|
} catch (e) {
|
|
216
210
|
present(e, 'github.panel.errors.createBranch')
|
|
217
211
|
} finally {
|
|
@@ -259,7 +253,7 @@ async function openPr() {
|
|
|
259
253
|
})
|
|
260
254
|
showPrForm.value = false
|
|
261
255
|
prForm.value = { repoGithubId: null, title: '', head: '', base: '' }
|
|
262
|
-
|
|
256
|
+
actionToast.success(pullTerms.value.opened)
|
|
263
257
|
} catch (e) {
|
|
264
258
|
present(e, pullTerms.value.openFailed)
|
|
265
259
|
} finally {
|
|
@@ -283,11 +277,7 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
283
277
|
merging.value = pr.number
|
|
284
278
|
try {
|
|
285
279
|
await github.mergePullRequest(pr.repoGithubId, pr.number, { method: 'squash' })
|
|
286
|
-
|
|
287
|
-
title: t(pullTerms.value.mergedToast, { number: pr.number }),
|
|
288
|
-
icon: 'i-lucide-git-merge',
|
|
289
|
-
color: 'success',
|
|
290
|
-
})
|
|
280
|
+
actionToast.success(pullTerms.value.mergedToast, { params: { number: pr.number } })
|
|
291
281
|
} catch (e) {
|
|
292
282
|
present(e, 'github.panel.errors.merge')
|
|
293
283
|
} finally {
|
|
@@ -372,9 +362,8 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
372
362
|
</UButton>
|
|
373
363
|
</div>
|
|
374
364
|
|
|
375
|
-
<div v-if="
|
|
376
|
-
<
|
|
377
|
-
{{ t('github.panel.loading') }}
|
|
365
|
+
<div v-if="firstLoad" class="space-y-2">
|
|
366
|
+
<USkeleton v-for="i in 3" :key="i" class="h-12 w-full" />
|
|
378
367
|
</div>
|
|
379
368
|
|
|
380
369
|
<!-- repositories -->
|
|
@@ -405,20 +394,19 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
405
394
|
isAppConnection ? t('vcs.panel.manageHintApp') : t('vcs.panel.manageHintToken')
|
|
406
395
|
}}
|
|
407
396
|
</p>
|
|
408
|
-
<div
|
|
409
|
-
v-
|
|
410
|
-
class="flex items-center gap-2 py-3 text-sm text-muted"
|
|
411
|
-
>
|
|
412
|
-
<UIcon name="i-lucide-loader" class="h-4 w-4 animate-spin" />
|
|
413
|
-
{{ t('github.panel.loadingRepos') }}
|
|
397
|
+
<div v-if="github.loadingAvailable" class="space-y-1">
|
|
398
|
+
<USkeleton v-for="i in 4" :key="i" class="h-8 w-full" />
|
|
414
399
|
</div>
|
|
415
|
-
<
|
|
416
|
-
|
|
400
|
+
<EmptyState
|
|
401
|
+
v-else-if="!github.availableRepos.length"
|
|
402
|
+
compact
|
|
403
|
+
icon="i-lucide-folder-git-2"
|
|
404
|
+
:title="
|
|
417
405
|
isAppConnection
|
|
418
406
|
? t('vcs.panel.noAvailableReposApp')
|
|
419
407
|
: t('vcs.panel.noAvailableReposToken')
|
|
420
|
-
|
|
421
|
-
|
|
408
|
+
"
|
|
409
|
+
/>
|
|
422
410
|
<div v-else class="max-h-64 space-y-1 overflow-y-auto">
|
|
423
411
|
<UButton
|
|
424
412
|
color="neutral"
|
|
@@ -463,9 +451,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
463
451
|
</div>
|
|
464
452
|
</div>
|
|
465
453
|
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
454
|
+
<EmptyState
|
|
455
|
+
v-if="!github.repos.length && !managing"
|
|
456
|
+
compact
|
|
457
|
+
icon="i-lucide-folder-git-2"
|
|
458
|
+
:title="t('github.panel.noLinkedRepos')"
|
|
459
|
+
/>
|
|
469
460
|
|
|
470
461
|
<!-- Whether the host actually protects what an agent run pushes to. Placed above the
|
|
471
462
|
repo list because it is a posture check across ALL of them, not a per-repo
|
|
@@ -634,9 +625,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
634
625
|
</div>
|
|
635
626
|
</div>
|
|
636
627
|
|
|
637
|
-
<
|
|
638
|
-
|
|
639
|
-
|
|
628
|
+
<EmptyState
|
|
629
|
+
v-if="!github.pulls.length"
|
|
630
|
+
compact
|
|
631
|
+
icon="i-lucide-git-pull-request"
|
|
632
|
+
:title="t(pullTerms.none)"
|
|
633
|
+
/>
|
|
640
634
|
<div
|
|
641
635
|
v-for="pr in github.pulls"
|
|
642
636
|
:key="`${pr.repoGithubId}-${pr.number}`"
|
|
@@ -681,9 +675,12 @@ async function merge(pr: GitHubPullRequest) {
|
|
|
681
675
|
|
|
682
676
|
<!-- issues -->
|
|
683
677
|
<section v-else class="space-y-2">
|
|
684
|
-
<
|
|
685
|
-
|
|
686
|
-
|
|
678
|
+
<EmptyState
|
|
679
|
+
v-if="!github.issues.length"
|
|
680
|
+
compact
|
|
681
|
+
icon="i-lucide-circle-dot"
|
|
682
|
+
:title="t('github.panel.noIssues')"
|
|
683
|
+
/>
|
|
687
684
|
<div
|
|
688
685
|
v-for="issue in github.issues"
|
|
689
686
|
:key="`${issue.repoGithubId}-${issue.number}`"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
// service-directory field). Nothing in the tree can be the target then, so the pick is the
|
|
20
20
|
// folder the caller is standing in and the emitted value is that folder plus the new name.
|
|
21
21
|
import type { RepoTreeEntry } from '~/types/domain'
|
|
22
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
22
23
|
|
|
23
24
|
const props = withDefaults(
|
|
24
25
|
defineProps<{
|
|
@@ -194,12 +195,15 @@ watch(
|
|
|
194
195
|
|
|
195
196
|
<!-- listing -->
|
|
196
197
|
<div class="max-h-56 overflow-auto rounded-sm border border-default">
|
|
197
|
-
<div v-if="loading" class="
|
|
198
|
-
|
|
199
|
-
</div>
|
|
200
|
-
<div v-else-if="isEmpty" class="p-3 text-sm text-muted">
|
|
201
|
-
{{ mode === 'dir' ? t('github.repoTree.noSubdirectories') : t('github.repoTree.empty') }}
|
|
198
|
+
<div v-if="loading" class="space-y-1 p-2">
|
|
199
|
+
<USkeleton v-for="i in 4" :key="i" class="h-7 w-full" />
|
|
202
200
|
</div>
|
|
201
|
+
<EmptyState
|
|
202
|
+
v-else-if="isEmpty"
|
|
203
|
+
compact
|
|
204
|
+
icon="i-lucide-folder-open"
|
|
205
|
+
:title="mode === 'dir' ? t('github.repoTree.noSubdirectories') : t('github.repoTree.empty')"
|
|
206
|
+
/>
|
|
203
207
|
<ul v-else class="divide-y divide-default">
|
|
204
208
|
<li
|
|
205
209
|
v-for="entry in dirEntries"
|
|
@@ -7,6 +7,7 @@ import type { OpenGuidedReviewInput } from '~/types/domain'
|
|
|
7
7
|
import GuidedReviewOverview from '~/components/guidedReview/GuidedReviewOverview.vue'
|
|
8
8
|
import GuidedReviewThread from '~/components/guidedReview/GuidedReviewThread.vue'
|
|
9
9
|
import GuidedReviewPicker from '~/components/guidedReview/GuidedReviewPicker.vue'
|
|
10
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
10
11
|
import {
|
|
11
12
|
NEW_THREAD_TAB,
|
|
12
13
|
isStale,
|
|
@@ -175,10 +176,16 @@ const title = computed(() =>
|
|
|
175
176
|
@open="openFor"
|
|
176
177
|
/>
|
|
177
178
|
|
|
178
|
-
<
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
<div v-else-if="!session" class="flex h-full min-h-0 flex-col gap-4 lg:flex-row">
|
|
180
|
+
<div class="space-y-3 lg:w-2/5 lg:pr-2">
|
|
181
|
+
<USkeleton class="h-6 w-2/3" />
|
|
182
|
+
<USkeleton v-for="n in 4" :key="n" class="h-4 w-full" />
|
|
183
|
+
</div>
|
|
184
|
+
<div class="flex flex-1 flex-col gap-3">
|
|
185
|
+
<USkeleton class="h-8 w-1/2" />
|
|
186
|
+
<USkeleton class="h-48 w-full rounded-lg" />
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
182
189
|
|
|
183
190
|
<div v-else class="flex h-full min-h-0 flex-col gap-4 lg:flex-row">
|
|
184
191
|
<div class="min-h-0 space-y-3 overflow-y-auto lg:w-2/5 lg:pr-2">
|
|
@@ -210,11 +217,14 @@ const title = computed(() =>
|
|
|
210
217
|
:aria-selected="tab === activeTab"
|
|
211
218
|
:variant="tab === activeTab ? 'soft' : 'ghost'"
|
|
212
219
|
:color="tab === activeTab ? 'primary' : 'neutral'"
|
|
213
|
-
:icon="threadBusy(tab) ? 'i-lucide-loader-circle' : undefined"
|
|
214
|
-
:ui="{ leadingIcon: threadBusy(tab) ? 'animate-spin' : '' }"
|
|
215
220
|
:data-testid="`guided-review-tab-${tab}`"
|
|
216
221
|
@click="activeTab = tab"
|
|
217
222
|
>
|
|
223
|
+
<!-- Not `:loading`: that disables the tab, and a reader switches back to a thread
|
|
224
|
+
precisely while its reply is pending. -->
|
|
225
|
+
<template v-if="threadBusy(tab)" #leading>
|
|
226
|
+
<Spinner class="size-4 shrink-0" />
|
|
227
|
+
</template>
|
|
218
228
|
<span class="max-w-48 truncate">{{ threadTitle(tab) }}</span>
|
|
219
229
|
</UButton>
|
|
220
230
|
<UButton
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// chips the window turns into new threads.
|
|
4
4
|
import type { GuidedReviewSession } from '~/types/domain'
|
|
5
5
|
import MarkdownProse from '~/components/common/MarkdownProse.vue'
|
|
6
|
+
import Spinner from '~/components/common/Spinner.vue'
|
|
6
7
|
import GuidedReviewFailure from '~/components/guidedReview/GuidedReviewFailure.vue'
|
|
7
8
|
import { citationLabel } from '~/components/guidedReview/GuidedReview.logic'
|
|
8
9
|
|
|
@@ -53,7 +54,7 @@ const SEVERITY_COLOR = { high: 'error', medium: 'warning', low: 'neutral' } as c
|
|
|
53
54
|
class="flex items-center gap-2 text-sm text-muted"
|
|
54
55
|
data-testid="guided-review-overview-working"
|
|
55
56
|
>
|
|
56
|
-
<
|
|
57
|
+
<Spinner class="h-4 w-4" />
|
|
57
58
|
{{ t('guidedReview.overview.working') }}
|
|
58
59
|
</p>
|
|
59
60
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Pick a linked repository and one of its pull requests (an open one from the synced list, or any
|
|
3
3
|
// number typed in) to open a guided review on.
|
|
4
4
|
import type { OpenGuidedReviewInput } from '~/types/domain'
|
|
5
|
+
import EmptyState from '~/components/common/EmptyState.vue'
|
|
5
6
|
|
|
6
7
|
defineProps<{ opening: boolean; loading: boolean }>()
|
|
7
8
|
const emit = defineEmits<{ open: [target: OpenGuidedReviewInput] }>()
|
|
@@ -34,13 +35,15 @@ function submit(number = prNumber.value): void {
|
|
|
34
35
|
<template>
|
|
35
36
|
<div class="mx-auto max-w-xl space-y-4" data-testid="guided-review-picker">
|
|
36
37
|
<p class="text-sm text-muted">{{ t('guidedReview.picker.intro') }}</p>
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
</
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
<div v-if="loading" class="space-y-4">
|
|
39
|
+
<USkeleton class="h-8 w-full" />
|
|
40
|
+
<USkeleton class="h-8 w-full" />
|
|
41
|
+
</div>
|
|
42
|
+
<EmptyState
|
|
43
|
+
v-else-if="!github.repos.length"
|
|
44
|
+
icon="i-lucide-folder-git-2"
|
|
45
|
+
:title="t('guidedReview.picker.noRepos')"
|
|
46
|
+
/>
|
|
44
47
|
<template v-else>
|
|
45
48
|
<UFormField :label="t('guidedReview.picker.repo')">
|
|
46
49
|
<USelectMenu
|