@cat-factory/app 0.302.2 → 0.303.1
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 +99 -14
- package/app/app.config.ts +19 -10
- package/app/assets/css/main.css +13 -13
- package/app/assets/css/prose.css +22 -22
- package/app/assets/css/tokens.css +298 -0
- package/app/components/assistant/AssistantModal.vue +22 -22
- package/app/components/auth/AuthGate.vue +1 -1
- package/app/components/auth/BackendMisconfiguredScreen.vue +10 -10
- package/app/components/auth/LoginScreen.vue +34 -36
- package/app/components/auth/ResetPasswordScreen.vue +12 -12
- package/app/components/auth/UserMenu.vue +4 -4
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +27 -25
- package/app/components/binaryOutput/BinaryOutputReport.vue +15 -15
- package/app/components/binaryOutput/StoredAssetView.vue +5 -5
- package/app/components/board/AddTaskModal.vue +17 -20
- package/app/components/board/AgentFailureCard.vue +11 -11
- package/app/components/board/AgentFailureHistory.vue +2 -2
- package/app/components/board/BoardCanvas.vue +6 -5
- package/app/components/board/CreateInitiativeModal.vue +8 -10
- package/app/components/board/DependencyConnectOverlay.vue +1 -1
- package/app/components/board/FailureHistoryList.vue +7 -7
- package/app/components/board/RecurringPipelineModal.vue +17 -17
- package/app/components/board/ReviewFrictionDialog.vue +6 -6
- package/app/components/board/StepExecutionHistory.vue +13 -13
- package/app/components/board/TaskDependencyEdges.vue +8 -8
- package/app/components/board/nodes/AgentChip.vue +10 -10
- package/app/components/board/nodes/BlockNode.vue +29 -29
- package/app/components/board/nodes/DecisionBadge.vue +2 -2
- package/app/components/board/nodes/EpicNode.vue +14 -10
- package/app/components/board/nodes/FrameSwimlanes.vue +7 -9
- package/app/components/board/nodes/InitiativeCard.vue +8 -8
- package/app/components/board/nodes/LaneGroup.vue +1 -1
- package/app/components/board/nodes/LaneTask.vue +2 -2
- package/app/components/board/nodes/ResizeGrips.vue +4 -4
- package/app/components/board/nodes/TaskCard.vue +18 -18
- package/app/components/board/nodes/TaskLane.vue +5 -5
- package/app/components/board/nodes/TaskPipelineMini.vue +18 -18
- package/app/components/bootstrap/AdoptionReviewModal.vue +29 -26
- package/app/components/bootstrap/BootstrapModal.vue +21 -23
- package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
- package/app/components/brainstorm/BrainstormWindow.vue +30 -33
- package/app/components/bugFishing/BugFishingWindow.vue +49 -49
- package/app/components/clarity/ClarityReviewWindow.vue +30 -30
- package/app/components/common/AsyncViewError.vue +5 -5
- package/app/components/common/ClarificationItem.vue +8 -13
- package/app/components/common/ConfirmDialog.vue +4 -2
- package/app/components/common/DescriptorFields.vue +1 -1
- package/app/components/common/EmptyState.vue +3 -3
- package/app/components/common/InterviewGateNotice.vue +7 -7
- package/app/components/common/KeyboardShortcutsHelp.vue +1 -1
- package/app/components/common/MarkdownProse.vue +14 -13
- package/app/components/consensus/ConsensusSessionWindow.vue +26 -26
- package/app/components/context/ContextAttachmentFields.vue +17 -17
- package/app/components/docs/DocInterviewWindow.vue +14 -14
- package/app/components/documents/ContextDocumentPicker.vue +17 -17
- package/app/components/documents/DocumentImportModal.vue +7 -7
- package/app/components/documents/DocumentSourceConnectModal.vue +3 -3
- package/app/components/documents/DocumentSyncState.vue +5 -5
- package/app/components/documents/DocumentTemplatesModal.vue +15 -15
- package/app/components/documents/RepoContextDocPicker.vue +5 -5
- package/app/components/documents/SpawnPreviewModal.vue +12 -12
- package/app/components/documents/StartFromDesignModal.vue +11 -11
- package/app/components/documents/TaskContextDocs.vue +5 -5
- package/app/components/environments/EnvSetupStepper.vue +6 -6
- package/app/components/environments/EnvironmentStatusPanel.vue +17 -17
- package/app/components/environments/steps/EnvPickStep.vue +3 -3
- package/app/components/environments/steps/EnvPreflightStep.vue +9 -9
- package/app/components/environments/steps/EnvReviewStep.vue +25 -22
- package/app/components/environments/steps/EnvSaveStep.vue +7 -5
- package/app/components/environments/steps/JourneyStepNav.vue +1 -1
- package/app/components/focus/BlockFocusView.vue +17 -19
- package/app/components/followUp/FollowUpWindow.vue +23 -23
- package/app/components/forkDecision/ForkDecisionWindow.vue +37 -39
- package/app/components/foundational/FoundationalContractSummary.vue +7 -7
- package/app/components/foundational/FoundationalServiceCatalogList.vue +11 -11
- package/app/components/foundational/FoundationalServiceManager.vue +1 -1
- package/app/components/foundational/FoundationalServiceRegistry.vue +13 -13
- package/app/components/foundational/FoundationalServiceSources.vue +13 -13
- package/app/components/foundational/FoundationalSuppressions.vue +7 -7
- package/app/components/foundational/ServiceCatalogConnection.vue +10 -10
- package/app/components/fragments/FragmentLibraryManager.vue +41 -44
- package/app/components/fragments/FragmentSelector.vue +13 -13
- package/app/components/fragments/GitHubDocUrlImport.vue +1 -1
- package/app/components/gates/GateFailingCheckList.vue +5 -5
- package/app/components/gates/GateResultView.vue +50 -47
- package/app/components/github/AddServiceFromRepoModal.vue +14 -14
- package/app/components/github/BranchProtectionPreflight.vue +13 -13
- package/app/components/github/GitHubConnect.vue +7 -7
- package/app/components/github/GitHubOnboarding.vue +7 -7
- package/app/components/github/GitHubPanel.vue +37 -40
- package/app/components/github/RepoTreeBrowser.vue +18 -18
- package/app/components/humanTest/HumanTestWindow.vue +30 -33
- package/app/components/initiative/InitiativePlanNotice.vue +5 -5
- package/app/components/initiative/InitiativePlanReview.vue +27 -29
- package/app/components/initiative/InitiativePlanningWindow.vue +10 -10
- package/app/components/initiative/InitiativeTrackerWindow.vue +71 -65
- package/app/components/inputGate/InputGateNotice.vue +2 -4
- package/app/components/judge/JudgeResultView.vue +30 -32
- package/app/components/kaizen/KaizenPanel.vue +40 -36
- package/app/components/kaizen/KaizenStepStatus.vue +21 -21
- package/app/components/layout/AccountAuditLog.vue +10 -10
- package/app/components/layout/AccountDeploymentSettings.vue +20 -20
- package/app/components/layout/AccountFailureKindRules.vue +13 -13
- package/app/components/layout/AccountFoundationalSettings.vue +1 -1
- package/app/components/layout/AccountFragmentSettings.vue +1 -1
- package/app/components/layout/AccountModelPolicySettings.vue +9 -9
- package/app/components/layout/AccountPlatformAlertSettings.vue +11 -11
- package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
- package/app/components/layout/AccountRunCredentialSettings.vue +5 -5
- package/app/components/layout/AccountSkillSettings.vue +1 -1
- package/app/components/layout/AccountTeamSettings.vue +23 -15
- package/app/components/layout/AiProvidersBanner.vue +11 -8
- package/app/components/layout/AppearanceSwitcher.vue +108 -0
- package/app/components/layout/BoardSwitcher.vue +8 -11
- package/app/components/layout/BoardToolbar.vue +3 -3
- package/app/components/layout/CommandBar.vue +7 -9
- package/app/components/layout/ConnectionStatusBanner.vue +2 -2
- package/app/components/layout/DefaultTestEnvBanner.vue +5 -5
- package/app/components/layout/GitHubPatBanner.vue +5 -5
- package/app/components/layout/GitHubPatPermissionsBanner.vue +16 -10
- package/app/components/layout/InfraSetupBanner.vue +6 -6
- package/app/components/layout/IntegrationsHub.vue +15 -15
- package/app/components/layout/LanguageSwitcher.vue +5 -5
- package/app/components/layout/ModelProvidersHub.vue +13 -16
- package/app/components/layout/NotificationsInbox.vue +10 -10
- package/app/components/layout/PersonalSetupModal.vue +8 -8
- package/app/components/layout/ProviderConfigBanner.vue +4 -4
- package/app/components/layout/RolePrompt.vue +7 -7
- package/app/components/layout/SideBar.vue +6 -4
- package/app/components/layout/SpendWarningBanner.vue +17 -14
- package/app/components/layout/TranslationWarningBanner.vue +4 -4
- package/app/components/layout/UiModeSwitcher.vue +13 -13
- package/app/components/layout/UiRoleSwitcher.vue +8 -8
- package/app/components/layout/WorkspaceMembersSettings.vue +15 -11
- package/app/components/media/ArtifactLightbox.vue +13 -13
- package/app/components/media/ImageCompare.vue +31 -24
- package/app/components/merge/MergeEffortChips.vue +2 -2
- package/app/components/notifications/NotificationSettingsPanel.vue +14 -12
- package/app/components/observability/OutcomeFilterChips.vue +6 -6
- package/app/components/observability/RunFailureSummary.vue +28 -25
- package/app/components/observability/StepMetricsBar.vue +26 -28
- package/app/components/observability/StepModelActivity.vue +2 -2
- package/app/components/observability/ToolCallList.vue +24 -24
- package/app/components/outcome/OutcomeSummaryWindow.vue +47 -47
- package/app/components/palettes/AgentPalette.vue +8 -8
- package/app/components/palettes/AgentTierSelect.vue +1 -1
- package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
- package/app/components/panels/AgentStepDetail.vue +52 -51
- package/app/components/panels/AttemptEntryHeader.vue +4 -6
- package/app/components/panels/DecisionModal.vue +7 -7
- package/app/components/panels/GenericStructuredResultView.vue +7 -7
- package/app/components/panels/InspectorPanel.vue +17 -17
- package/app/components/panels/MergerResultView.vue +19 -19
- package/app/components/panels/ObservabilityPanel.vue +115 -123
- package/app/components/panels/OperatorDashboardPanel.vue +89 -97
- package/app/components/panels/ReportsPanel.vue +69 -66
- package/app/components/panels/ReportsSpendBreakdown.vue +9 -9
- package/app/components/panels/ResultWindowShell.vue +38 -38
- package/app/components/panels/RunDetailLoadState.vue +2 -2
- package/app/components/panels/StepContainerStatus.vue +9 -9
- package/app/components/panels/StepDelegatedStatus.logic.spec.ts +135 -0
- package/app/components/panels/StepDelegatedStatus.logic.ts +160 -0
- package/app/components/panels/StepDelegatedStatus.vue +181 -0
- package/app/components/panels/StepEffortReport.vue +16 -15
- package/app/components/panels/StepFragmentAdherence.vue +14 -10
- package/app/components/panels/StepMetadataCard.vue +74 -45
- package/app/components/panels/StepReproductionReport.vue +25 -21
- package/app/components/panels/StepRunMeta.vue +20 -20
- package/app/components/panels/StepTestReport.logic.spec.ts +8 -3
- package/app/components/panels/StepTestReport.logic.ts +4 -4
- package/app/components/panels/StepTestReport.vue +28 -28
- package/app/components/panels/StepToolServers.vue +14 -11
- package/app/components/panels/StepValidationReport.vue +7 -7
- package/app/components/panels/inspector/ContainerSummary.vue +9 -9
- package/app/components/panels/inspector/DocReferenceRepos.vue +3 -3
- package/app/components/panels/inspector/EnvProbeReport.vue +24 -27
- package/app/components/panels/inspector/EpicChildren.vue +8 -12
- package/app/components/panels/inspector/FrontendBindingsResolved.vue +11 -11
- package/app/components/panels/inspector/FrontendConfig.vue +37 -41
- package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
- package/app/components/panels/inspector/InspectorSection.vue +6 -6
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +16 -19
- package/app/components/panels/inspector/ServiceConnections.vue +2 -2
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
- package/app/components/panels/inspector/ServiceSelfTests.vue +21 -17
- package/app/components/panels/inspector/ServiceTestConfig.vue +51 -59
- package/app/components/panels/inspector/ServiceTestSecrets.vue +5 -5
- package/app/components/panels/inspector/ServiceTestingContext.vue +3 -3
- package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
- package/app/components/panels/inspector/TaskAgentConfig.vue +3 -3
- package/app/components/panels/inspector/TaskAprioriBranches.vue +8 -5
- package/app/components/panels/inspector/TaskDependencies.vue +1 -1
- package/app/components/panels/inspector/TaskEstimateBadge.vue +11 -11
- package/app/components/panels/inspector/TaskExecution.vue +17 -17
- package/app/components/panels/inspector/TaskReviewTarget.vue +2 -2
- package/app/components/panels/inspector/TaskRunSettings.vue +35 -37
- package/app/components/panels/inspector/TaskStructure.vue +3 -3
- package/app/components/pipeline/AgentKindIcon.vue +1 -1
- package/app/components/pipeline/AgentPromptEditor.vue +17 -17
- package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -56
- package/app/components/pipeline/EstimateThresholdFields.vue +4 -4
- package/app/components/pipeline/GateConfigFields.vue +13 -10
- package/app/components/pipeline/IterationCapPrompt.vue +4 -2
- package/app/components/pipeline/PipelineBuilder.vue +47 -55
- package/app/components/pipeline/PipelineHealthModal.vue +27 -29
- package/app/components/pipeline/PipelinePicker.vue +10 -10
- package/app/components/pipeline/PipelinePreview.vue +10 -10
- package/app/components/pipeline/PipelineProgress.vue +64 -67
- package/app/components/prReview/PrReviewPhaseBadge.vue +7 -7
- package/app/components/prReview/PrReviewWindow.vue +75 -77
- package/app/components/providers/AiPresetMismatchDialog.vue +5 -5
- package/app/components/providers/AiProviderOnboardingModal.vue +9 -9
- package/app/components/providers/ApiKeysSection.vue +13 -13
- package/app/components/providers/PersonalCredentialModal.vue +2 -2
- package/app/components/providers/PersonalSubscriptionSection.vue +10 -10
- package/app/components/providers/SignInRequiredNotice.vue +1 -1
- package/app/components/providers/VendorCredentialsModal.vue +10 -10
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +13 -13
- package/app/components/ralph/RalphLoopResultView.vue +28 -30
- package/app/components/recurring/RecurrenceEditor.vue +2 -2
- package/app/components/requirements/RequirementsReviewWindow.vue +51 -58
- package/app/components/riskPolicy/RiskPolicyPicker.vue +12 -15
- package/app/components/riskPolicy/RiskPolicyPreview.vue +14 -14
- package/app/components/sandbox/SandboxPanel.vue +58 -59
- package/app/components/settings/AccountSettingsPanel.vue +1 -1
- package/app/components/settings/ApiTokensPanel.vue +10 -10
- package/app/components/settings/BudgetSettings.vue +18 -22
- package/app/components/settings/CapabilityCredentialsPanel.vue +15 -15
- package/app/components/settings/CloudflareHandlerSection.vue +20 -20
- package/app/components/settings/ComposeEnvironmentSetupSection.vue +13 -13
- package/app/components/settings/ConnectionTestVerdict.vue +3 -3
- package/app/components/settings/ConnectionWarnings.vue +1 -1
- package/app/components/settings/ConsensusGroupsSection.vue +27 -35
- package/app/components/settings/CustomManifestTypeEditor.vue +10 -10
- package/app/components/settings/DefaultProvisionTypeSection.vue +6 -6
- package/app/components/settings/InfraHandlersConfigurator.vue +24 -22
- package/app/components/settings/InfrastructureBackendPicker.vue +6 -6
- package/app/components/settings/InfrastructureWindow.vue +5 -5
- package/app/components/settings/IssueTrackerPanel.vue +29 -23
- package/app/components/settings/KubernetesEngineForm.vue +11 -11
- package/app/components/settings/KubernetesEnvironmentForm.vue +4 -4
- package/app/components/settings/LocalContainerPoolSettings.vue +6 -11
- package/app/components/settings/LocalModelEndpointsPanel.vue +20 -18
- package/app/components/settings/McpAuthorizeScreen.vue +13 -15
- package/app/components/settings/McpOAuthCallbackScreen.vue +16 -10
- package/app/components/settings/MergeClassRulesEditor.vue +6 -9
- package/app/components/settings/MergeRolePolicyEditor.vue +16 -16
- package/app/components/settings/ModelConfigurationPanel.vue +23 -30
- package/app/components/settings/ModelPresetHealthModal.vue +15 -13
- package/app/components/settings/ObservabilityConnectionPanel.vue +4 -4
- package/app/components/settings/OpenRouterCatalogPanel.vue +26 -19
- package/app/components/settings/PackageRegistriesPanel.vue +8 -8
- package/app/components/settings/ProviderConnectionTab.vue +8 -11
- package/app/components/settings/ProviderManifestEditor.vue +9 -9
- package/app/components/settings/ProviderPreferenceEditor.vue +8 -8
- package/app/components/settings/RiskPolicyCreateForm.vue +10 -10
- package/app/components/settings/RiskPolicyEditorRow.vue +14 -14
- package/app/components/settings/RiskPolicyHealthModal.vue +15 -13
- package/app/components/settings/RiskPolicyInheritedRow.vue +1 -1
- package/app/components/settings/RiskPolicyPanel.vue +10 -15
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +7 -7
- package/app/components/settings/SharedStacksPanel.vue +9 -12
- package/app/components/settings/TaskSourceCard.vue +4 -4
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +6 -6
- package/app/components/settings/ToolServerChecklist.vue +36 -29
- package/app/components/settings/UsageSettings.vue +15 -15
- package/app/components/settings/UserSecretsSection.vue +9 -12
- package/app/components/settings/WorkspaceMetadataSettings.vue +5 -5
- package/app/components/settings/WorkspaceSettingsPanel.vue +47 -47
- package/app/components/skills/ReviewSkillQueue.vue +10 -10
- package/app/components/skills/SkillLibraryManager.vue +23 -20
- package/app/components/slack/SlackPanel.vue +13 -17
- package/app/components/spec/ServiceSpecWindow.vue +45 -42
- package/app/components/tasks/BugHuntModal.vue +24 -27
- package/app/components/tasks/ContextIssuePicker.vue +11 -11
- package/app/components/tasks/TaskContextIssues.vue +3 -3
- package/app/components/tasks/TaskImportModal.vue +7 -7
- package/app/components/tasks/TaskSourceConnectModal.vue +10 -8
- package/app/components/testing/TestReportWindow.vue +99 -82
- package/app/components/theme/ThemeImportModal.vue +115 -0
- package/app/components/tutorial/TutorialCatalogue.vue +9 -9
- package/app/components/tutorial/TutorialNudge.vue +5 -5
- package/app/components/tutorial/TutorialOverlay.vue +12 -12
- package/app/components/tutorial/TutorialPrompt.vue +6 -9
- package/app/components/vcs/GitLabConnect.vue +4 -4
- package/app/components/vcs/VcsConnectSurfaces.vue +2 -2
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +36 -36
- package/app/composables/api/client.ts +6 -1
- package/app/composables/usePipelineErrorToast.ts +17 -0
- package/app/modular/agent-kinds.ts +24 -0
- package/app/pages/index.vue +5 -5
- package/app/plugins/theme.client.ts +125 -0
- package/app/spa-loading-template.html +52 -14
- package/app/stores/agents.ts +1 -1
- package/app/stores/theme.spec.ts +56 -0
- package/app/stores/theme.ts +87 -0
- package/app/types/domain.ts +19 -0
- package/app/types/execution.ts +4 -0
- package/app/utils/agentOutput.ts +1 -1
- package/app/utils/binaryOutput.ts +6 -6
- package/app/utils/bootstrapSteps.ts +14 -14
- package/app/utils/catalog.ts +87 -83
- package/app/utils/colorTint.ts +10 -0
- package/app/utils/failureKinds.ts +1 -0
- package/app/utils/observability.ts +3 -3
- package/app/utils/pipelineRender.ts +13 -17
- package/app/utils/swimlanes.ts +4 -4
- package/app/utils/theme/bootPalette.spec.ts +54 -0
- package/app/utils/theme/bootPalette.ts +108 -0
- package/app/utils/theme/builtins.spec.ts +38 -0
- package/app/utils/theme/builtins.ts +55 -0
- package/app/utils/theme/css.ts +162 -0
- package/app/utils/theme/doc.ts +353 -0
- package/app/utils/theme/link.ts +93 -0
- package/app/utils/theme/presets.ts +509 -0
- package/app/utils/theme/theme.spec.ts +224 -0
- package/i18n/locales/de.json +61 -4
- package/i18n/locales/en.json +70 -4
- package/i18n/locales/es.json +61 -4
- package/i18n/locales/fr.json +61 -4
- package/i18n/locales/he.json +61 -4
- package/i18n/locales/it.json +61 -4
- package/i18n/locales/ja.json +61 -4
- package/i18n/locales/pl.json +61 -4
- package/i18n/locales/tr.json +61 -4
- package/i18n/locales/uk.json +61 -4
- package/nuxt.config.ts +37 -11
- package/package.json +10 -9
|
@@ -58,7 +58,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
58
58
|
>
|
|
59
59
|
<template #body>
|
|
60
60
|
<div class="space-y-4" data-testid="tutorial-catalogue">
|
|
61
|
-
<p v-if="rows.length > 0" class="text-xs text-
|
|
61
|
+
<p v-if="rows.length > 0" class="text-xs text-muted" data-testid="tutorial-progress">
|
|
62
62
|
{{
|
|
63
63
|
t('tutorial.catalogue.progress', {
|
|
64
64
|
completed: progress.completed,
|
|
@@ -70,17 +70,17 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
70
70
|
<li
|
|
71
71
|
v-for="row in rows"
|
|
72
72
|
:key="row.tour.id"
|
|
73
|
-
class="flex items-start gap-3 rounded-lg border border-
|
|
73
|
+
class="flex items-start gap-3 rounded-lg border border-default bg-default/60 p-3"
|
|
74
74
|
:class="row.startable ? '' : 'opacity-75'"
|
|
75
75
|
:data-testid="`tutorial-catalogue-entry-${row.tour.id}`"
|
|
76
76
|
>
|
|
77
77
|
<UIcon
|
|
78
78
|
:name="row.tour.icon ?? 'i-lucide-compass'"
|
|
79
|
-
class="mt-0.5 h-5 w-5 shrink-0 text-primary
|
|
79
|
+
class="mt-0.5 h-5 w-5 shrink-0 text-primary"
|
|
80
80
|
/>
|
|
81
81
|
<div class="min-w-0 flex-1 space-y-1">
|
|
82
82
|
<div class="flex flex-wrap items-center gap-2">
|
|
83
|
-
<span class="text-sm font-medium text-
|
|
83
|
+
<span class="text-sm font-medium text-app-100">{{ t(row.tour.titleKey) }}</span>
|
|
84
84
|
<UBadge
|
|
85
85
|
v-if="showsStatus(row)"
|
|
86
86
|
:color="statusColor(row)"
|
|
@@ -91,15 +91,15 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
91
91
|
{{ t(TUTORIAL_STATUS_KEYS[row.state]) }}
|
|
92
92
|
</UBadge>
|
|
93
93
|
</div>
|
|
94
|
-
<p class="text-xs text-
|
|
95
|
-
<p v-if="row.stepCount !== null" class="text-xs text-
|
|
94
|
+
<p class="text-xs text-muted">{{ t(row.tour.descriptionKey) }}</p>
|
|
95
|
+
<p v-if="row.stepCount !== null" class="text-xs text-dimmed">
|
|
96
96
|
{{ t('tutorial.catalogue.steps', { count: row.stepCount }, row.stepCount) }}
|
|
97
97
|
</p>
|
|
98
98
|
<!-- A held-back tour says what would unlock it, rather than vanishing from the
|
|
99
99
|
list: these are things the reader can go and do. -->
|
|
100
100
|
<div
|
|
101
101
|
v-else-if="row.availability === 'blocked'"
|
|
102
|
-
class="text-xs text-
|
|
102
|
+
class="text-xs text-dimmed"
|
|
103
103
|
:data-testid="`tutorial-catalogue-requirements-${row.tour.id}`"
|
|
104
104
|
>
|
|
105
105
|
<span>{{ t('tutorial.catalogue.blocked') }}</span>
|
|
@@ -112,7 +112,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
112
112
|
</div>
|
|
113
113
|
<!-- Requirements met, but every step is about a branch this board isn't on:
|
|
114
114
|
nothing to go and fix, so it must not read like the case above. -->
|
|
115
|
-
<p v-else class="text-xs text-
|
|
115
|
+
<p v-else class="text-xs text-dimmed">
|
|
116
116
|
{{ t('tutorial.catalogue.notApplicable') }}
|
|
117
117
|
</p>
|
|
118
118
|
</div>
|
|
@@ -130,7 +130,7 @@ const statusColor = (row: TutorialCatalogueRow) =>
|
|
|
130
130
|
</ul>
|
|
131
131
|
<!-- No tours at all is a real state (a deployment may register none of its own and
|
|
132
132
|
strip the built-ins), and it is not the same as one whose tours are all blocked. -->
|
|
133
|
-
<p v-if="rows.length === 0" class="text-sm text-
|
|
133
|
+
<p v-if="rows.length === 0" class="text-sm text-muted">
|
|
134
134
|
{{ t('tutorial.catalogue.empty') }}
|
|
135
135
|
</p>
|
|
136
136
|
</div>
|
|
@@ -64,7 +64,7 @@ function take(tourId: string) {
|
|
|
64
64
|
this far, which is acceptable in a way the standing overlap was not: it clears itself. -->
|
|
65
65
|
<div
|
|
66
66
|
v-if="offered && !suppressed"
|
|
67
|
-
class="fixed end-4 bottom-20 z-50 w-80 max-w-[calc(100vw-32px)] rounded-xl border border-
|
|
67
|
+
class="fixed end-4 bottom-20 z-50 w-80 max-w-[calc(100vw-32px)] rounded-xl border border-muted bg-default/95 p-3 shadow-2xl backdrop-blur"
|
|
68
68
|
role="status"
|
|
69
69
|
aria-live="polite"
|
|
70
70
|
data-testid="tutorial-nudge"
|
|
@@ -72,14 +72,14 @@ function take(tourId: string) {
|
|
|
72
72
|
<div class="flex items-start gap-2">
|
|
73
73
|
<UIcon
|
|
74
74
|
:name="offered.icon ?? 'i-lucide-graduation-cap'"
|
|
75
|
-
class="text-primary
|
|
75
|
+
class="text-primary mt-0.5 h-4 w-4 shrink-0"
|
|
76
76
|
/>
|
|
77
77
|
<div class="min-w-0 flex-1">
|
|
78
|
-
<p class="text-[11px] tracking-wide text-
|
|
78
|
+
<p class="text-[11px] tracking-wide text-muted uppercase">
|
|
79
79
|
{{ t('tutorial.nudge.label') }}
|
|
80
80
|
</p>
|
|
81
|
-
<p class="mt-0.5 text-sm font-medium text-
|
|
82
|
-
<p class="mt-0.5 text-xs text-
|
|
81
|
+
<p class="mt-0.5 text-sm font-medium text-app-100">{{ t(offered.titleKey) }}</p>
|
|
82
|
+
<p class="mt-0.5 text-xs text-muted">{{ t(offered.descriptionKey) }}</p>
|
|
83
83
|
</div>
|
|
84
84
|
<UButton
|
|
85
85
|
size="xs"
|
|
@@ -541,7 +541,7 @@ onUnmounted(() => {
|
|
|
541
541
|
which is exactly the involuntary movement `prefers-reduced-motion` is about. -->
|
|
542
542
|
<div
|
|
543
543
|
v-if="targetRect"
|
|
544
|
-
class="ring-primary
|
|
544
|
+
class="ring-primary outline-primary/25 pointer-events-none fixed z-[70] rounded-lg outline-4 ring-2 motion-safe:transition-all motion-safe:duration-150"
|
|
545
545
|
:style="{
|
|
546
546
|
top: `${targetRect.top - 4}px`,
|
|
547
547
|
left: `${targetRect.left - 4}px`,
|
|
@@ -569,27 +569,27 @@ onUnmounted(() => {
|
|
|
569
569
|
role="dialog"
|
|
570
570
|
:tabindex="cardFocusable ? -1 : undefined"
|
|
571
571
|
:aria-label="t('tutorial.overlay.ariaLabel')"
|
|
572
|
-
class="pointer-events-auto fixed z-[70] w-80 max-w-[calc(100vw-16px)] rounded-xl border border-
|
|
572
|
+
class="pointer-events-auto fixed z-[70] w-80 max-w-[calc(100vw-16px)] rounded-xl border border-muted bg-default p-4 shadow-2xl outline-none focus-visible:ring-2 focus-visible:ring-primary"
|
|
573
573
|
:style="{ top: `${layout.top}px`, left: `${layout.left}px` }"
|
|
574
574
|
data-testid="tutorial-tooltip"
|
|
575
575
|
@pointerdown.stop
|
|
576
576
|
@focusout="onCardFocusOut"
|
|
577
577
|
>
|
|
578
578
|
<div class="mb-1 flex items-start justify-between gap-3">
|
|
579
|
-
<h3 class="text-sm font-semibold text-
|
|
580
|
-
<span class="shrink-0 text-xs text-
|
|
579
|
+
<h3 class="text-sm font-semibold text-app-100">{{ t(step.titleKey) }}</h3>
|
|
580
|
+
<span class="shrink-0 text-xs text-dimmed">
|
|
581
581
|
{{ t('tutorial.overlay.progress', { current: tutorial.stepIndex + 1, total }) }}
|
|
582
582
|
</span>
|
|
583
583
|
</div>
|
|
584
584
|
<!-- `bodyParams` carries the fixed proper nouns a step names (a repository slug),
|
|
585
585
|
which live in the catalog's `{named}` placeholders rather than in nine
|
|
586
586
|
translations of the same literal. Absent for most steps. -->
|
|
587
|
-
<p class="text-sm text-
|
|
587
|
+
<p class="text-sm text-toned">
|
|
588
588
|
{{ t(step.bodyKey, step.bodyParams ?? {}) }}
|
|
589
589
|
</p>
|
|
590
590
|
<p
|
|
591
591
|
v-if="searching"
|
|
592
|
-
class="mt-2 flex items-center gap-1.5 text-xs text-
|
|
592
|
+
class="mt-2 flex items-center gap-1.5 text-xs text-muted"
|
|
593
593
|
data-testid="tutorial-searching"
|
|
594
594
|
>
|
|
595
595
|
<UIcon name="i-lucide-loader" class="h-3.5 w-3.5 motion-safe:animate-spin" />
|
|
@@ -597,7 +597,7 @@ onUnmounted(() => {
|
|
|
597
597
|
</p>
|
|
598
598
|
<p
|
|
599
599
|
v-else-if="step.advanceOn === 'target-click'"
|
|
600
|
-
class="text-primary
|
|
600
|
+
class="text-primary mt-2 text-xs"
|
|
601
601
|
data-testid="tutorial-click-hint"
|
|
602
602
|
>
|
|
603
603
|
{{ t('tutorial.overlay.clickHint') }}
|
|
@@ -606,7 +606,7 @@ onUnmounted(() => {
|
|
|
606
606
|
whole tour: say so rather than congratulating the user either way. -->
|
|
607
607
|
<p
|
|
608
608
|
v-if="isLast && abridged"
|
|
609
|
-
class="mt-2 text-xs text-
|
|
609
|
+
class="mt-2 text-xs text-app-warning-300/90"
|
|
610
610
|
data-testid="tutorial-abridged"
|
|
611
611
|
>
|
|
612
612
|
{{
|
|
@@ -625,14 +625,14 @@ onUnmounted(() => {
|
|
|
625
625
|
nothing ready, where the plain Done below is the honest ending. -->
|
|
626
626
|
<div
|
|
627
627
|
v-if="isLast && nextTour"
|
|
628
|
-
class="mt-3 rounded-lg border border-
|
|
628
|
+
class="mt-3 rounded-lg border border-muted/70 bg-elevated/40 p-2.5"
|
|
629
629
|
data-testid="tutorial-next-tour"
|
|
630
630
|
>
|
|
631
|
-
<p class="text-[11px] tracking-wide text-
|
|
631
|
+
<p class="text-[11px] tracking-wide text-muted uppercase">
|
|
632
632
|
{{ t('tutorial.overlay.nextUp') }}
|
|
633
633
|
</p>
|
|
634
|
-
<p class="mt-0.5 text-sm font-medium text-
|
|
635
|
-
<p class="mt-0.5 text-xs text-
|
|
634
|
+
<p class="mt-0.5 text-sm font-medium text-app-100">{{ t(nextTour.titleKey) }}</p>
|
|
635
|
+
<p class="mt-0.5 text-xs text-muted">{{ t(nextTour.descriptionKey) }}</p>
|
|
636
636
|
<UButton
|
|
637
637
|
size="xs"
|
|
638
638
|
color="primary"
|
|
@@ -40,20 +40,17 @@ const undecided = computed(() => tutorial.decision === null)
|
|
|
40
40
|
decline/close buttons are in a SIBLING slot — address them from the page by
|
|
41
41
|
their own test ids rather than scoping a locator under this one. -->
|
|
42
42
|
<div class="space-y-4" data-testid="tutorial-prompt">
|
|
43
|
-
<p class="text-sm text-
|
|
43
|
+
<p class="text-sm text-toned">{{ t('tutorial.prompt.intro') }}</p>
|
|
44
44
|
<ul class="space-y-2">
|
|
45
45
|
<li
|
|
46
46
|
v-for="tour in offered"
|
|
47
47
|
:key="tour.id"
|
|
48
|
-
class="flex items-center gap-3 rounded-lg border border-
|
|
48
|
+
class="flex items-center gap-3 rounded-lg border border-default bg-default/60 p-3"
|
|
49
49
|
>
|
|
50
|
-
<UIcon
|
|
51
|
-
:name="tour.icon ?? 'i-lucide-compass'"
|
|
52
|
-
class="h-5 w-5 shrink-0 text-primary-400"
|
|
53
|
-
/>
|
|
50
|
+
<UIcon :name="tour.icon ?? 'i-lucide-compass'" class="h-5 w-5 shrink-0 text-primary" />
|
|
54
51
|
<div class="min-w-0 flex-1">
|
|
55
52
|
<div class="flex items-center gap-2">
|
|
56
|
-
<span class="text-sm font-medium text-
|
|
53
|
+
<span class="text-sm font-medium text-app-100">{{ t(tour.titleKey) }}</span>
|
|
57
54
|
<UBadge
|
|
58
55
|
v-if="tutorial.isCompleted(tour.id)"
|
|
59
56
|
color="success"
|
|
@@ -64,7 +61,7 @@ const undecided = computed(() => tutorial.decision === null)
|
|
|
64
61
|
{{ t('tutorial.status.completed') }}
|
|
65
62
|
</UBadge>
|
|
66
63
|
</div>
|
|
67
|
-
<p class="text-xs text-
|
|
64
|
+
<p class="text-xs text-muted">{{ t(tour.descriptionKey) }}</p>
|
|
68
65
|
</div>
|
|
69
66
|
<UButton
|
|
70
67
|
size="sm"
|
|
@@ -85,7 +82,7 @@ const undecided = computed(() => tutorial.decision === null)
|
|
|
85
82
|
alone, where `board-basics` requires nothing at all — but a consumer's own slot
|
|
86
83
|
filter can produce it, and "no tours exist" would be the wrong thing to say then.)
|
|
87
84
|
The footer's browse button is the way on, so it stays. -->
|
|
88
|
-
<p v-if="offered.length === 0" class="text-sm text-
|
|
85
|
+
<p v-if="offered.length === 0" class="text-sm text-muted">
|
|
89
86
|
{{ t('tutorial.prompt.empty') }}
|
|
90
87
|
</p>
|
|
91
88
|
</div>
|
|
@@ -54,7 +54,7 @@ async function connect() {
|
|
|
54
54
|
|
|
55
55
|
<template>
|
|
56
56
|
<section class="space-y-3">
|
|
57
|
-
<p class="text-sm text-
|
|
57
|
+
<p class="text-sm text-muted">{{ t('vcs.connect.gitlab.intro') }}</p>
|
|
58
58
|
|
|
59
59
|
<UFormField :label="t('vcs.connect.gitlab.tokenLabel')" :hint="t('vcs.connect.gitlab.scope')">
|
|
60
60
|
<SecretInput
|
|
@@ -67,13 +67,13 @@ async function connect() {
|
|
|
67
67
|
/>
|
|
68
68
|
</UFormField>
|
|
69
69
|
|
|
70
|
-
<p class="text-[11px] text-
|
|
71
|
-
<ULink :to="tokenUrl" target="_blank" class="text-
|
|
70
|
+
<p class="text-[11px] text-dimmed">
|
|
71
|
+
<ULink :to="tokenUrl" target="_blank" class="text-primary hover:underline">
|
|
72
72
|
{{ t('vcs.connect.gitlab.createToken') }}
|
|
73
73
|
</ULink>
|
|
74
74
|
</p>
|
|
75
75
|
|
|
76
|
-
<p v-if="error" class="text-sm text-
|
|
76
|
+
<p v-if="error" class="text-sm text-app-error-400" data-testid="gitlab-connect-error">
|
|
77
77
|
{{ error }}
|
|
78
78
|
</p>
|
|
79
79
|
|
|
@@ -37,7 +37,7 @@ const nothingConfigured = computed(() => !github.canConnectGitHubApp && !github.
|
|
|
37
37
|
<template>
|
|
38
38
|
<div class="space-y-3">
|
|
39
39
|
<template v-if="github.canConnectGitHubApp">
|
|
40
|
-
<p v-if="props.appIntro" class="text-sm text-
|
|
40
|
+
<p v-if="props.appIntro" class="text-sm text-muted">{{ props.appIntro }}</p>
|
|
41
41
|
<GitHubConnect />
|
|
42
42
|
</template>
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@ const nothingConfigured = computed(() => !github.canConnectGitHubApp && !github.
|
|
|
50
50
|
|
|
51
51
|
<p
|
|
52
52
|
v-if="nothingConfigured"
|
|
53
|
-
class="rounded-md border border-dashed border-
|
|
53
|
+
class="rounded-md border border-dashed border-default px-3 py-3 text-sm text-muted"
|
|
54
54
|
>
|
|
55
55
|
{{ t('vcs.connect.noneConfigured') }}
|
|
56
56
|
</p>
|
|
@@ -224,7 +224,7 @@ async function onFilePicked(e: Event) {
|
|
|
224
224
|
<ResultWindowShell
|
|
225
225
|
:open="open"
|
|
226
226
|
icon="i-lucide-image-play"
|
|
227
|
-
icon-class="bg-
|
|
227
|
+
icon-class="bg-app-warning-500/15 text-app-warning-300"
|
|
228
228
|
:title="headerTitle"
|
|
229
229
|
:subtitle="phase ? PHASE_LABEL[phase] : t('visualConfirm.subtitle')"
|
|
230
230
|
width="5xl"
|
|
@@ -233,7 +233,7 @@ async function onFilePicked(e: Event) {
|
|
|
233
233
|
<div class="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-5 py-4">
|
|
234
234
|
<div
|
|
235
235
|
v-if="!vc"
|
|
236
|
-
class="flex flex-col items-center justify-center gap-2 py-10 text-center text-
|
|
236
|
+
class="flex flex-col items-center justify-center gap-2 py-10 text-center text-muted"
|
|
237
237
|
>
|
|
238
238
|
<UIcon name="i-lucide-image-play" class="h-8 w-8 opacity-40" />
|
|
239
239
|
<p class="text-sm">{{ t('visualConfirm.notStarted') }}</p>
|
|
@@ -242,7 +242,7 @@ async function onFilePicked(e: Event) {
|
|
|
242
242
|
<template v-else>
|
|
243
243
|
<p
|
|
244
244
|
v-if="vc.degradedReason"
|
|
245
|
-
class="rounded-lg border border-
|
|
245
|
+
class="rounded-lg border border-app-warning-700/40 bg-app-warning-500/5 px-3 py-2 text-[12px] text-app-warning-300/90"
|
|
246
246
|
>
|
|
247
247
|
{{ vc.degradedReason }}
|
|
248
248
|
</p>
|
|
@@ -251,21 +251,24 @@ async function onFilePicked(e: Event) {
|
|
|
251
251
|
reference the reviewer is judging against is never anonymous. -->
|
|
252
252
|
<section
|
|
253
253
|
v-if="design"
|
|
254
|
-
class="rounded-lg border border-
|
|
254
|
+
class="rounded-lg border border-default bg-default/60 px-3 py-2 text-[12px] text-toned"
|
|
255
255
|
>
|
|
256
256
|
<p class="flex items-center gap-1.5">
|
|
257
|
-
<UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-
|
|
257
|
+
<UIcon name="i-lucide-figma" class="h-3.5 w-3.5 shrink-0 text-app-warning-300" />
|
|
258
258
|
<span>{{
|
|
259
259
|
t('visualConfirm.design.summary', { count: design.images }, design.images)
|
|
260
260
|
}}</span>
|
|
261
|
-
<span v-if="design.dropped" class="text-
|
|
261
|
+
<span v-if="design.dropped" class="text-dimmed">
|
|
262
262
|
{{ t('visualConfirm.design.dropped', { count: design.dropped }, design.dropped) }}
|
|
263
263
|
</span>
|
|
264
264
|
</p>
|
|
265
265
|
<!-- One line per short design, carrying both ways it can fall short: what its source
|
|
266
266
|
kept, and what this gallery's shared ceiling cut from it. A design the ceiling shut
|
|
267
267
|
out entirely reads as one with no frames unless it is named here. -->
|
|
268
|
-
<ul
|
|
268
|
+
<ul
|
|
269
|
+
v-if="design.gaps?.length"
|
|
270
|
+
class="mt-1.5 space-y-1 text-[11px] text-app-warning-300/90"
|
|
271
|
+
>
|
|
269
272
|
<li v-for="gap in design.gaps" :key="`${gap.title}-${gap.reason ?? 'capped'}`">
|
|
270
273
|
{{ t('visualConfirm.design.gapLine', { title: gap.title }) }}
|
|
271
274
|
<template v-if="gap.reason">{{ DESIGN_GAP_LABELS[gap.reason] }}</template>
|
|
@@ -278,9 +281,9 @@ async function onFilePicked(e: Event) {
|
|
|
278
281
|
|
|
279
282
|
<p
|
|
280
283
|
v-if="working"
|
|
281
|
-
class="flex items-center gap-2 rounded-lg border border-
|
|
284
|
+
class="flex items-center gap-2 rounded-lg border border-default bg-app-950/40 px-3 py-2 text-[12px] text-toned"
|
|
282
285
|
>
|
|
283
|
-
<UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-
|
|
286
|
+
<UIcon name="i-lucide-loader" class="h-3.5 w-3.5 animate-spin text-app-warning-300" />
|
|
284
287
|
{{ phase ? PHASE_LABEL[phase] : '' }}
|
|
285
288
|
</p>
|
|
286
289
|
|
|
@@ -301,7 +304,7 @@ async function onFilePicked(e: Event) {
|
|
|
301
304
|
<!-- Per-view note (folded into the fixer findings) -->
|
|
302
305
|
<div v-if="awaitingHuman" class="px-1">
|
|
303
306
|
<button
|
|
304
|
-
class="flex items-center gap-1.5 text-[11px] text-
|
|
307
|
+
class="flex items-center gap-1.5 text-[11px] text-muted hover:text-default"
|
|
305
308
|
@click="noteOpen[p.view] = !noteOpen[p.view]"
|
|
306
309
|
>
|
|
307
310
|
<UIcon
|
|
@@ -311,7 +314,7 @@ async function onFilePicked(e: Event) {
|
|
|
311
314
|
{{ t('visualConfirm.noteIssue', { view: p.view }) }}
|
|
312
315
|
<span
|
|
313
316
|
v-if="perViewNotes[p.view]?.trim()"
|
|
314
|
-
class="rounded-full bg-
|
|
317
|
+
class="rounded-full bg-app-warning-500/15 px-1.5 text-[9px] text-app-warning-300"
|
|
315
318
|
>{{ t('visualConfirm.noted') }}</span
|
|
316
319
|
>
|
|
317
320
|
</button>
|
|
@@ -320,18 +323,18 @@ async function onFilePicked(e: Event) {
|
|
|
320
323
|
v-model="perViewNotes[p.view]"
|
|
321
324
|
rows="2"
|
|
322
325
|
:placeholder="t('visualConfirm.notePlaceholder', { view: p.view })"
|
|
323
|
-
class="mt-1 w-full rounded-md border border-
|
|
326
|
+
class="mt-1 w-full rounded-md border border-muted bg-app-950 px-2 py-1.5 text-[12px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
|
|
324
327
|
/>
|
|
325
328
|
</div>
|
|
326
329
|
</div>
|
|
327
330
|
</section>
|
|
328
|
-
<p v-else class="text-[12px] italic text-
|
|
331
|
+
<p v-else class="text-[12px] italic text-dimmed">
|
|
329
332
|
{{ t('visualConfirm.noScreenshots') }}
|
|
330
333
|
</p>
|
|
331
334
|
|
|
332
335
|
<!-- Upload a reference for any view -->
|
|
333
|
-
<section class="rounded-lg border border-
|
|
334
|
-
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-
|
|
336
|
+
<section class="rounded-lg border border-default bg-default/60 p-3">
|
|
337
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
|
|
335
338
|
{{ t('visualConfirm.upload.heading') }}
|
|
336
339
|
</h3>
|
|
337
340
|
<div class="flex flex-wrap items-center gap-2">
|
|
@@ -339,7 +342,7 @@ async function onFilePicked(e: Event) {
|
|
|
339
342
|
v-model="uploadView"
|
|
340
343
|
list="vc-views"
|
|
341
344
|
:placeholder="t('visualConfirm.upload.viewPlaceholder')"
|
|
342
|
-
class="rounded-md border border-
|
|
345
|
+
class="rounded-md border border-muted bg-app-950 px-2 py-1 text-[12px] text-default placeholder:text-app-600"
|
|
343
346
|
/>
|
|
344
347
|
<datalist id="vc-views">
|
|
345
348
|
<option v-for="p in pairs" :key="p.view" :value="p.view" />
|
|
@@ -349,11 +352,11 @@ async function onFilePicked(e: Event) {
|
|
|
349
352
|
type="file"
|
|
350
353
|
accept="image/png,image/jpeg"
|
|
351
354
|
:disabled="busy || !uploadView.trim()"
|
|
352
|
-
class="text-[12px] text-
|
|
355
|
+
class="text-[12px] text-toned file:me-2 file:rounded file:border-0 file:bg-elevated file:px-2 file:py-1 file:text-default disabled:opacity-40"
|
|
353
356
|
@change="onFilePicked"
|
|
354
357
|
/>
|
|
355
358
|
</div>
|
|
356
|
-
<p class="mt-1.5 text-[10px] text-
|
|
359
|
+
<p class="mt-1.5 text-[10px] text-app-600">
|
|
357
360
|
{{
|
|
358
361
|
uploadView.trim()
|
|
359
362
|
? t('visualConfirm.upload.tipReady')
|
|
@@ -363,21 +366,18 @@ async function onFilePicked(e: Event) {
|
|
|
363
366
|
</section>
|
|
364
367
|
|
|
365
368
|
<!-- Request fix -->
|
|
366
|
-
<section
|
|
367
|
-
|
|
368
|
-
class="rounded-lg border border-slate-800 bg-slate-900/60 p-3"
|
|
369
|
-
>
|
|
370
|
-
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
369
|
+
<section v-if="awaitingHuman" class="rounded-lg border border-default bg-default/60 p-3">
|
|
370
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
|
|
371
371
|
{{ t('visualConfirm.requestFix.heading') }}
|
|
372
372
|
</h3>
|
|
373
373
|
<textarea
|
|
374
374
|
v-model="globalFindings"
|
|
375
375
|
rows="3"
|
|
376
376
|
:placeholder="t('visualConfirm.requestFix.placeholder')"
|
|
377
|
-
class="w-full rounded-md border border-
|
|
377
|
+
class="w-full rounded-md border border-muted bg-app-950 px-3 py-2 text-[13px] text-default placeholder:text-app-600 focus:border-app-warning-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-app-warning-500/60"
|
|
378
378
|
/>
|
|
379
379
|
<div class="mt-2 flex items-center justify-between">
|
|
380
|
-
<span class="text-[11px] text-
|
|
380
|
+
<span class="text-[11px] text-dimmed">
|
|
381
381
|
{{ t('visualConfirm.requestFix.foldedHint') }}
|
|
382
382
|
</span>
|
|
383
383
|
<UButton
|
|
@@ -397,31 +397,31 @@ async function onFilePicked(e: Event) {
|
|
|
397
397
|
<!-- Rounds history -->
|
|
398
398
|
<section
|
|
399
399
|
v-if="vc.rounds && vc.rounds.length"
|
|
400
|
-
class="rounded-lg border border-
|
|
400
|
+
class="rounded-lg border border-default bg-default/60 p-3"
|
|
401
401
|
>
|
|
402
|
-
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-
|
|
402
|
+
<h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-dimmed">
|
|
403
403
|
{{ t('visualConfirm.history.heading', { count: vc.attempts }, vc.attempts) }}
|
|
404
404
|
</h3>
|
|
405
405
|
<ol class="space-y-2">
|
|
406
406
|
<li v-for="(r, i) in vc.rounds" :key="i" class="flex items-start gap-2 text-[12px]">
|
|
407
|
-
<UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-
|
|
407
|
+
<UIcon name="i-lucide-wrench" class="mt-0.5 h-3.5 w-3.5 shrink-0 text-muted" />
|
|
408
408
|
<div class="min-w-0 flex-1">
|
|
409
|
-
<span class="text-
|
|
409
|
+
<span class="text-default">{{ t('visualConfirm.history.fixRequested') }}</span>
|
|
410
410
|
<span
|
|
411
411
|
class="ms-1.5 rounded px-1 text-[10px] uppercase"
|
|
412
412
|
:class="
|
|
413
413
|
r.outcome === 'completed'
|
|
414
|
-
? 'bg-
|
|
414
|
+
? 'bg-app-success-500/15 text-app-success-300'
|
|
415
415
|
: r.outcome === 'failed'
|
|
416
|
-
? 'bg-
|
|
417
|
-
: 'bg-
|
|
416
|
+
? 'bg-app-error-500/15 text-app-error-300'
|
|
417
|
+
: 'bg-app-500/15 text-toned'
|
|
418
418
|
"
|
|
419
419
|
>
|
|
420
420
|
{{
|
|
421
421
|
r.outcome ? OUTCOME_LABELS[r.outcome] : t('visualConfirm.outcome.inProgress')
|
|
422
422
|
}}
|
|
423
423
|
</span>
|
|
424
|
-
<p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-
|
|
424
|
+
<p v-if="r.findings" class="whitespace-pre-wrap leading-snug text-muted">
|
|
425
425
|
{{ r.findings }}
|
|
426
426
|
</p>
|
|
427
427
|
</div>
|
|
@@ -433,7 +433,7 @@ async function onFilePicked(e: Event) {
|
|
|
433
433
|
|
|
434
434
|
<footer
|
|
435
435
|
v-if="vc"
|
|
436
|
-
class="flex items-center justify-between gap-3 border-t border-
|
|
436
|
+
class="flex items-center justify-between gap-3 border-t border-default px-5 py-3"
|
|
437
437
|
>
|
|
438
438
|
<StepRunMeta
|
|
439
439
|
v-if="step"
|
|
@@ -447,9 +447,9 @@ async function onFilePicked(e: Event) {
|
|
|
447
447
|
<div class="flex items-center gap-2">
|
|
448
448
|
<label
|
|
449
449
|
v-if="awaitingHuman && needsAck"
|
|
450
|
-
class="flex items-center gap-1.5 text-[11px] text-
|
|
450
|
+
class="flex items-center gap-1.5 text-[11px] text-app-warning-300/90"
|
|
451
451
|
>
|
|
452
|
-
<input v-model="ackDegraded" type="checkbox" class="accent-
|
|
452
|
+
<input v-model="ackDegraded" type="checkbox" class="accent-app-warning-500" />
|
|
453
453
|
{{ t('visualConfirm.reviewedManually') }}
|
|
454
454
|
</label>
|
|
455
455
|
<UButton
|
|
@@ -118,7 +118,12 @@ export async function sendContract<T extends ApiContract>(
|
|
|
118
118
|
const { statusCode, body } = error as { statusCode: number; body: unknown }
|
|
119
119
|
throw new ApiError(statusCode, body)
|
|
120
120
|
}
|
|
121
|
-
|
|
121
|
+
// The success arm is read through the same widening as the error arm above. Since
|
|
122
|
+
// `@toad-contracts/core@1`, a success entry expands to one union member per declared media
|
|
123
|
+
// type, so on an unresolved `T` the member type stays a deferred conditional and even reading
|
|
124
|
+
// `.body` off it is unprovable, not just assigning it to `SuccessBodyOf<T>`.
|
|
125
|
+
const { body } = outcome.result! as { body: unknown }
|
|
126
|
+
return body as SuccessBodyOf<T>
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
/** Curry {@link sendContract} over a client into the throw-on-error {@link ApiSend}. */
|
|
@@ -157,6 +157,20 @@ const CONFLICT_INFO: Record<Exclude<ConflictReason, BespokeConflictReason>, Conf
|
|
|
157
157
|
titleKey: 'errors.conflict.title.no_host_frame',
|
|
158
158
|
descriptionKey: 'errors.conflict.description.no_host_frame',
|
|
159
159
|
},
|
|
160
|
+
delegated_executor_unwired: {
|
|
161
|
+
titleKey: 'errors.conflict.title.delegated_executor_unwired',
|
|
162
|
+
descriptionKey: 'errors.conflict.description.delegated_executor_unwired',
|
|
163
|
+
},
|
|
164
|
+
// The two conditions that are NOT about a missing registration. They had borrowed the reason
|
|
165
|
+
// above, whose copy sends an operator to register an executor that is registered and fine.
|
|
166
|
+
delegated_step_async_only: {
|
|
167
|
+
titleKey: 'errors.conflict.title.delegated_step_async_only',
|
|
168
|
+
descriptionKey: 'errors.conflict.description.delegated_step_async_only',
|
|
169
|
+
},
|
|
170
|
+
delegated_claim_missing: {
|
|
171
|
+
titleKey: 'errors.conflict.title.delegated_claim_missing',
|
|
172
|
+
descriptionKey: 'errors.conflict.description.delegated_claim_missing',
|
|
173
|
+
},
|
|
160
174
|
task_limit_reached: {
|
|
161
175
|
titleKey: 'errors.conflict.title.task_limit_reached',
|
|
162
176
|
descriptionKey: 'errors.conflict.description.task_limit_reached',
|
|
@@ -448,6 +462,9 @@ const REASON_DESCRIPTION_KEYS: Record<UnavailableReason | BootstrapReferenceReas
|
|
|
448
462
|
'errors.unavailable.description.service_catalog_response_too_large',
|
|
449
463
|
assistant_generation_failed: 'errors.unavailable.description.assistant_generation_failed',
|
|
450
464
|
assistant_reply_unreadable: 'errors.unavailable.description.assistant_reply_unreadable',
|
|
465
|
+
delegated_executor_failed: 'errors.unavailable.description.delegated_executor_failed',
|
|
466
|
+
delegated_work_branch_unprepared:
|
|
467
|
+
'errors.unavailable.description.delegated_work_branch_unprepared',
|
|
451
468
|
}
|
|
452
469
|
|
|
453
470
|
/**
|
|
@@ -46,5 +46,29 @@ export function customKindToArchetype(kind: CustomAgentKind): AgentArchetype {
|
|
|
46
46
|
// the snapshot, and a required step option cannot be gated on something the read model
|
|
47
47
|
// does not carry.
|
|
48
48
|
...(kind.binaryOutput ? { binaryOutput: true } : {}),
|
|
49
|
+
// WHERE the kind's work runs, when it leaves the platform. Lifted from the entry rather than
|
|
50
|
+
// from `presentation` for the reason `binaryOutput` is: it is a fact about how the kind RUNS.
|
|
51
|
+
//
|
|
52
|
+
// A `delegated` kind with no executor resolved still carries its ID, so the card can name what
|
|
53
|
+
// this build cannot: an executor a deployment stopped registering is a step nobody can run,
|
|
54
|
+
// and rendering it as an ordinary one is how that goes unnoticed until a run refuses.
|
|
55
|
+
//
|
|
56
|
+
// Carried only when the entry actually has one. A synthesised `{ id: '' }` was worse than no
|
|
57
|
+
// entry at all: the empty string is not nullish, so it satisfied every `??` fallback downstream
|
|
58
|
+
// and the card rendered a blank name in precisely the case this branch exists to handle.
|
|
59
|
+
...(kind.executor === 'delegated' && kind.delegatedExecutor
|
|
60
|
+
? {
|
|
61
|
+
delegatedExecutor: {
|
|
62
|
+
id: kind.delegatedExecutor.id,
|
|
63
|
+
...(kind.delegatedExecutor.label ? { label: kind.delegatedExecutor.label } : {}),
|
|
64
|
+
...(kind.delegatedExecutor.description
|
|
65
|
+
? { description: kind.delegatedExecutor.description }
|
|
66
|
+
: {}),
|
|
67
|
+
...(kind.delegatedExecutor.telemetry
|
|
68
|
+
? { telemetry: kind.delegatedExecutor.telemetry }
|
|
69
|
+
: {}),
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
: {}),
|
|
49
73
|
}
|
|
50
74
|
}
|
package/app/pages/index.vue
CHANGED
|
@@ -425,7 +425,7 @@ watch(
|
|
|
425
425
|
<!-- A COLUMN, so the translation strip below takes its own height instead of covering the
|
|
426
426
|
row: page chrome that overlays the app is how the board's top controls came to be
|
|
427
427
|
buried. Everything else lives in the row beneath it. -->
|
|
428
|
-
<div class="flex h-screen w-screen flex-col overflow-hidden bg-
|
|
428
|
+
<div class="flex h-screen w-screen flex-col overflow-hidden bg-app-950 text-app-100">
|
|
429
429
|
<!-- Non-English locale warning (unofficial translation); slim full-width strip above
|
|
430
430
|
everything, in flow. -->
|
|
431
431
|
<TranslationWarningBanner />
|
|
@@ -433,7 +433,7 @@ watch(
|
|
|
433
433
|
<!-- Resolving whether the GitHub App is installed, before we decide what to show. -->
|
|
434
434
|
<div
|
|
435
435
|
v-if="workspace.ready && githubProbePending"
|
|
436
|
-
class="m-auto flex flex-col items-center gap-3 text-
|
|
436
|
+
class="m-auto flex flex-col items-center gap-3 text-muted"
|
|
437
437
|
>
|
|
438
438
|
<UIcon name="i-lucide-loader" class="h-8 w-8 animate-spin" />
|
|
439
439
|
<span class="text-sm">{{ $t('app.loading') }}</span>
|
|
@@ -553,16 +553,16 @@ watch(
|
|
|
553
553
|
|
|
554
554
|
<!-- Backend unreachable / bootstrap failed -->
|
|
555
555
|
<div v-else-if="workspace.error" class="m-auto max-w-md p-8 text-center">
|
|
556
|
-
<UIcon name="i-lucide-plug-zap" class="mx-auto mb-3 h-10 w-10 text-
|
|
556
|
+
<UIcon name="i-lucide-plug-zap" class="mx-auto mb-3 h-10 w-10 text-app-warning-400" />
|
|
557
557
|
<h1 class="mb-1 text-lg font-semibold">{{ $t('app.backendUnreachable') }}</h1>
|
|
558
|
-
<p class="mb-4 text-sm text-
|
|
558
|
+
<p class="mb-4 text-sm text-muted">{{ workspace.error }}</p>
|
|
559
559
|
<UButton color="primary" icon="i-lucide-rotate-ccw" @click="workspace.init()">
|
|
560
560
|
{{ $t('common.retry') }}
|
|
561
561
|
</UButton>
|
|
562
562
|
</div>
|
|
563
563
|
|
|
564
564
|
<!-- Initial load -->
|
|
565
|
-
<div v-else class="m-auto flex flex-col items-center gap-3 text-
|
|
565
|
+
<div v-else class="m-auto flex flex-col items-center gap-3 text-muted">
|
|
566
566
|
<UIcon name="i-lucide-loader" class="h-8 w-8 animate-spin" />
|
|
567
567
|
<span class="text-sm">{{ $t('app.loadingBoard') }}</span>
|
|
568
568
|
</div>
|