@cat-factory/app 0.308.0 → 0.310.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -2
- package/app/assets/css/main.css +22 -0
- package/app/assets/css/prose.css +4 -4
- package/app/assets/css/type.css +24 -0
- package/app/components/assistant/AssistantModal.vue +8 -6
- package/app/components/auth/BackendMisconfiguredScreen.vue +4 -3
- package/app/components/auth/LoginScreen.vue +40 -26
- package/app/components/auth/ResetPasswordScreen.vue +9 -4
- package/app/components/auth/UserMenu.vue +5 -4
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +48 -37
- package/app/components/binaryOutput/BinaryOutputReport.vue +10 -14
- package/app/components/binaryOutput/StoredAssetView.vue +13 -11
- package/app/components/board/AddTaskModal.vue +30 -27
- package/app/components/board/AgentFailureCard.vue +21 -18
- package/app/components/board/AgentFailureHistory.vue +18 -9
- package/app/components/board/CreateInitiativeModal.vue +7 -6
- package/app/components/board/FailureDetail.vue +22 -12
- package/app/components/board/FailureHistoryList.vue +5 -5
- package/app/components/board/RecurringPipelineModal.vue +12 -11
- package/app/components/board/ReviewFrictionDialog.vue +8 -6
- package/app/components/board/StepExecutionHistory.vue +8 -8
- package/app/components/board/nodes/AgentChip.vue +1 -1
- package/app/components/board/nodes/BlockNode.vue +25 -24
- package/app/components/board/nodes/DecisionBadge.vue +4 -3
- package/app/components/board/nodes/EpicNode.vue +4 -4
- package/app/components/board/nodes/FrameSwimlanes.vue +8 -10
- package/app/components/board/nodes/InitiativeCard.vue +4 -4
- package/app/components/board/nodes/LaneGroup.vue +3 -5
- package/app/components/board/nodes/TaskCard.vue +20 -15
- package/app/components/board/nodes/TaskLane.vue +3 -3
- package/app/components/board/nodes/TaskPipelineMini.vue +21 -18
- package/app/components/bootstrap/AdoptionReviewModal.vue +36 -26
- package/app/components/bootstrap/BootstrapModal.vue +14 -13
- package/app/components/bootstrap/BootstrapRunSteps.vue +1 -1
- package/app/components/brainstorm/BrainstormWindow.vue +13 -10
- package/app/components/bugFishing/BugFishingWindow.vue +81 -58
- package/app/components/clarity/ClarityReviewWindow.vue +13 -10
- package/app/components/common/AsyncViewError.vue +1 -1
- package/app/components/common/ClarificationItem.vue +6 -6
- package/app/components/common/CopyButton.vue +12 -11
- package/app/components/common/DescriptorFields.vue +1 -1
- package/app/components/common/EmptyState.vue +1 -1
- package/app/components/common/IconButton.vue +34 -9
- package/app/components/common/InterviewGateNotice.vue +4 -4
- package/app/components/common/MarkdownProse.vue +8 -2
- package/app/components/common/SectionLabel.vue +38 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +10 -9
- package/app/components/context/ContextAttachmentFields.vue +32 -20
- package/app/components/docs/DocInterviewWindow.vue +10 -9
- package/app/components/documents/ContextDocumentPicker.vue +22 -21
- package/app/components/documents/DocumentImportModal.vue +7 -5
- package/app/components/documents/DocumentOriginLink.vue +12 -9
- package/app/components/documents/DocumentSourceConnectModal.vue +2 -2
- package/app/components/documents/DocumentSyncState.vue +4 -4
- package/app/components/documents/DocumentTemplatesModal.vue +5 -4
- package/app/components/documents/RepoContextDocPicker.vue +7 -9
- package/app/components/documents/StartFromDesignModal.vue +4 -4
- package/app/components/documents/TaskContextDocs.vue +1 -1
- package/app/components/environments/EnvSetupStepper.vue +3 -3
- package/app/components/environments/EnvironmentStatusPanel.vue +15 -13
- package/app/components/environments/steps/EnvPreflightStep.vue +8 -8
- package/app/components/environments/steps/EnvReviewStep.vue +23 -22
- package/app/components/environments/steps/EnvSaveStep.vue +5 -5
- package/app/components/focus/BlockFocusView.vue +11 -10
- package/app/components/followUp/FollowUpWindow.vue +18 -15
- package/app/components/forkDecision/ForkDecisionWindow.vue +85 -97
- package/app/components/foundational/FoundationalContractSummary.vue +2 -2
- package/app/components/foundational/FoundationalServiceCatalogList.vue +6 -5
- package/app/components/foundational/FoundationalServiceRegistry.vue +10 -9
- package/app/components/foundational/FoundationalServiceSources.vue +7 -6
- package/app/components/foundational/FoundationalSuppressions.vue +2 -2
- package/app/components/fragments/FragmentLibraryManager.vue +8 -7
- package/app/components/fragments/FragmentSelector.vue +22 -20
- package/app/components/gates/GateFailingCheckList.vue +7 -6
- package/app/components/gates/GateResultView.vue +55 -49
- package/app/components/github/AddServiceFromRepoModal.vue +16 -11
- package/app/components/github/BranchProtectionPreflight.vue +5 -5
- package/app/components/github/GitHubConnect.vue +5 -3
- package/app/components/github/GitHubOnboarding.vue +7 -2
- package/app/components/github/GitHubPanel.vue +23 -19
- package/app/components/github/RepoTreeBrowser.vue +16 -13
- package/app/components/humanTest/HumanTestWindow.vue +29 -24
- package/app/components/initiative/InitiativePlanNotice.vue +3 -3
- package/app/components/initiative/InitiativePlanReview.vue +46 -38
- package/app/components/initiative/InitiativePlanningWindow.vue +5 -5
- package/app/components/initiative/InitiativeTrackerWindow.vue +213 -165
- package/app/components/judge/JudgeResultView.vue +28 -27
- package/app/components/kaizen/KaizenPanel.vue +70 -56
- package/app/components/kaizen/KaizenStepStatus.vue +7 -6
- package/app/components/layout/AccountAuditLog.vue +1 -1
- package/app/components/layout/AccountDeploymentSettings.vue +10 -10
- 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 +7 -7
- package/app/components/layout/AccountPlatformAlertSettings.vue +8 -8
- package/app/components/layout/AccountRiskPolicySettings.vue +2 -2
- package/app/components/layout/AccountRunCredentialSettings.vue +3 -3
- package/app/components/layout/AccountSkillSettings.vue +1 -1
- package/app/components/layout/AccountTeamSettings.vue +13 -9
- package/app/components/layout/AiProvidersBanner.vue +1 -1
- package/app/components/layout/AppearanceSwitcher.vue +5 -4
- package/app/components/layout/BoardSwitcher.vue +13 -10
- package/app/components/layout/BoardToolbar.vue +10 -5
- package/app/components/layout/CommandBar.vue +8 -5
- package/app/components/layout/DefaultTestEnvBanner.vue +6 -5
- package/app/components/layout/IntegrationsHub.vue +12 -9
- package/app/components/layout/LanguageSwitcher.vue +5 -4
- package/app/components/layout/ModelProvidersHub.vue +10 -7
- package/app/components/layout/NotificationsInbox.vue +47 -36
- package/app/components/layout/PersonalSetupModal.vue +9 -6
- package/app/components/layout/RolePrompt.vue +5 -4
- package/app/components/layout/SideBar.vue +7 -8
- package/app/components/layout/SpendWarningBanner.vue +3 -3
- package/app/components/layout/TranslationWarningBanner.vue +5 -4
- package/app/components/layout/UiModeSwitcher.vue +15 -12
- package/app/components/layout/UiRoleSwitcher.vue +10 -8
- package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
- package/app/components/media/ArtifactLightbox.vue +74 -44
- package/app/components/media/ImageCompare.vue +80 -85
- package/app/components/merge/MergeEffortChips.vue +4 -6
- package/app/components/notifications/NotificationSettingsPanel.vue +6 -5
- package/app/components/observability/OutcomeFilterChips.vue +6 -5
- package/app/components/observability/RunFailureSummary.vue +22 -23
- package/app/components/observability/StepMetricsBar.vue +9 -8
- package/app/components/observability/StepModelActivity.vue +8 -5
- package/app/components/observability/ToolCallList.vue +19 -20
- package/app/components/outcome/OutcomeSummaryWindow.vue +48 -46
- package/app/components/palettes/AgentPalette.vue +21 -15
- package/app/components/palettes/AgentTierSelect.vue +1 -1
- package/app/components/palettes/PipelinePurposeSelect.vue +1 -1
- package/app/components/panels/AgentStepDetail.vue +57 -49
- package/app/components/panels/AttemptEntryHeader.vue +2 -2
- package/app/components/panels/DecisionModal.vue +1 -1
- package/app/components/panels/GenericStructuredResultView.vue +6 -5
- package/app/components/panels/InspectorPanel.vue +15 -13
- package/app/components/panels/MergerResultView.vue +10 -9
- package/app/components/panels/ObservabilityPanel.vue +211 -186
- package/app/components/panels/OperatorDashboardPanel.vue +124 -115
- package/app/components/panels/ReportsPanel.vue +87 -83
- package/app/components/panels/ReportsSpendBreakdown.vue +2 -2
- package/app/components/panels/ResultWindowShell.vue +152 -137
- package/app/components/panels/RunDetailLoadState.vue +1 -1
- package/app/components/panels/StepContainerStatus.vue +17 -16
- package/app/components/panels/StepDelegatedStatus.vue +24 -21
- package/app/components/panels/StepEffortReport.vue +12 -17
- package/app/components/panels/StepFragmentAdherence.vue +9 -10
- package/app/components/panels/StepMetadataCard.vue +41 -43
- package/app/components/panels/StepReproductionReport.vue +18 -20
- package/app/components/panels/StepRestartControl.vue +3 -2
- package/app/components/panels/StepRunMeta.vue +27 -26
- package/app/components/panels/StepTestReport.vue +16 -15
- package/app/components/panels/StepToolServers.vue +7 -8
- package/app/components/panels/StepValidationReport.vue +5 -5
- package/app/components/panels/inspector/ContainerSummary.vue +4 -4
- package/app/components/panels/inspector/DocReferenceRepos.vue +5 -4
- package/app/components/panels/inspector/EnvProbeReport.vue +16 -13
- package/app/components/panels/inspector/EpicChildren.vue +12 -10
- package/app/components/panels/inspector/FrontendBindingsResolved.vue +5 -4
- package/app/components/panels/inspector/FrontendConfig.vue +29 -36
- package/app/components/panels/inspector/InitiativeInspector.vue +3 -3
- package/app/components/panels/inspector/InspectorSection.vue +25 -17
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +9 -8
- package/app/components/panels/inspector/ServiceConnections.vue +5 -4
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +2 -2
- package/app/components/panels/inspector/ServiceSelfTests.vue +9 -13
- package/app/components/panels/inspector/ServiceTestConfig.vue +46 -49
- package/app/components/panels/inspector/ServiceTestSecrets.vue +3 -3
- package/app/components/panels/inspector/ServiceTestingContext.vue +2 -2
- package/app/components/panels/inspector/ServiceValidationConfig.vue +2 -2
- package/app/components/panels/inspector/TaskAgentConfig.vue +2 -2
- package/app/components/panels/inspector/TaskAprioriBranches.vue +6 -5
- package/app/components/panels/inspector/TaskDependencies.vue +1 -1
- package/app/components/panels/inspector/TaskEstimateBadge.vue +3 -7
- package/app/components/panels/inspector/TaskExecution.vue +18 -16
- package/app/components/panels/inspector/TaskRunSettings.vue +45 -44
- package/app/components/panels/inspector/TaskStructure.vue +5 -4
- package/app/components/pipeline/AgentPromptEditor.vue +15 -14
- package/app/components/pipeline/BinaryOutputStepPicker.vue +42 -52
- package/app/components/pipeline/EstimateThresholdFields.vue +14 -11
- package/app/components/pipeline/GateConfigFields.vue +19 -23
- package/app/components/pipeline/IterationCapPrompt.vue +1 -1
- package/app/components/pipeline/PipelineBuilder.vue +113 -99
- package/app/components/pipeline/PipelineHealthModal.vue +7 -7
- package/app/components/pipeline/PipelinePicker.vue +11 -9
- package/app/components/pipeline/PipelinePreview.vue +6 -7
- package/app/components/pipeline/PipelineProgress.vue +49 -45
- package/app/components/prReview/PrReviewWindow.vue +87 -65
- package/app/components/providers/AiPresetMismatchDialog.vue +4 -3
- package/app/components/providers/AiProviderOnboardingModal.vue +2 -2
- package/app/components/providers/ApiKeysSection.vue +11 -9
- package/app/components/providers/PersonalSubscriptionSection.vue +4 -3
- package/app/components/providers/VendorCredentialsModal.vue +5 -4
- package/app/components/provisioning/ProvisioningLogsDrawer.vue +10 -9
- package/app/components/ralph/RalphLoopResultView.vue +29 -29
- package/app/components/requirements/RequirementsReviewWindow.vue +22 -17
- package/app/components/riskPolicy/RiskPolicyPicker.vue +13 -11
- package/app/components/riskPolicy/RiskPolicyPreview.vue +16 -15
- package/app/components/sandbox/SandboxPanel.vue +93 -73
- package/app/components/settings/ApiTokensPanel.vue +1 -1
- package/app/components/settings/BudgetSettings.vue +18 -17
- package/app/components/settings/CapabilityCredentialsPanel.vue +5 -5
- package/app/components/settings/CloudflareHandlerSection.vue +44 -41
- package/app/components/settings/ComposeEnvironmentSetupSection.vue +14 -12
- package/app/components/settings/ConnectionTestVerdict.vue +1 -1
- package/app/components/settings/ConsensusGroupsSection.vue +58 -47
- package/app/components/settings/CustomManifestTypeEditor.vue +8 -7
- package/app/components/settings/DefaultProvisionTypeSection.vue +5 -5
- package/app/components/settings/InfraHandlersConfigurator.vue +26 -24
- package/app/components/settings/InfrastructureBackendPicker.vue +18 -18
- package/app/components/settings/IssueTrackerPanel.vue +30 -21
- package/app/components/settings/KubernetesEngineForm.vue +9 -8
- package/app/components/settings/KubernetesEnvironmentForm.vue +4 -3
- package/app/components/settings/LocalContainerPoolSettings.vue +2 -2
- package/app/components/settings/LocalModelEndpointsPanel.vue +13 -11
- package/app/components/settings/MergeClassRulesEditor.vue +6 -5
- package/app/components/settings/MergeRolePolicyEditor.vue +13 -12
- package/app/components/settings/ModelConfigurationPanel.vue +17 -19
- package/app/components/settings/ModelPresetHealthModal.vue +3 -3
- package/app/components/settings/ObservabilityConnectionPanel.vue +1 -1
- package/app/components/settings/OpenRouterCatalogPanel.vue +10 -8
- package/app/components/settings/PackageRegistriesPanel.vue +4 -4
- package/app/components/settings/ProviderConnectionTab.vue +5 -4
- package/app/components/settings/ProviderManifestEditor.vue +9 -8
- package/app/components/settings/ProviderPreferenceEditor.vue +13 -13
- package/app/components/settings/RiskPolicyCreateForm.vue +40 -26
- package/app/components/settings/RiskPolicyEditorRow.vue +21 -19
- package/app/components/settings/RiskPolicyHealthModal.vue +3 -3
- package/app/components/settings/RiskPolicyPanel.vue +11 -13
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +15 -12
- package/app/components/settings/SharedStacksPanel.vue +5 -5
- package/app/components/settings/TaskSourceCard.vue +2 -2
- package/app/components/settings/TaskTypeSuppressionsPanel.vue +4 -4
- package/app/components/settings/ToolServerChecklist.vue +22 -22
- package/app/components/settings/UsageSettings.vue +13 -13
- package/app/components/settings/UserSecretsSection.vue +4 -3
- package/app/components/settings/WorkspaceMetadataSettings.vue +6 -5
- package/app/components/settings/WorkspaceSettingsPanel.vue +72 -58
- package/app/components/skills/ReviewSkillQueue.vue +12 -10
- package/app/components/skills/SkillLibraryManager.vue +9 -8
- package/app/components/slack/SlackPanel.vue +17 -12
- package/app/components/spec/ServiceSpecWindow.vue +19 -19
- package/app/components/tasks/BugHuntModal.vue +1 -1
- package/app/components/tasks/ContextIssuePicker.vue +16 -15
- package/app/components/tasks/TaskContextIssues.vue +5 -4
- package/app/components/tasks/TaskImportModal.vue +3 -2
- package/app/components/tasks/TaskSourceConnectModal.vue +5 -5
- package/app/components/testing/TestReportWindow.vue +67 -60
- package/app/components/theme/ThemeImportModal.vue +2 -2
- package/app/components/tutorial/TutorialNudge.vue +3 -2
- package/app/components/tutorial/TutorialOverlay.vue +3 -2
- package/app/components/vcs/GitLabConnect.vue +1 -1
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +79 -54
- package/app/utils/theme/builtins.ts +7 -1
- package/i18n/locales/de.json +7 -3
- package/i18n/locales/en.json +7 -3
- package/i18n/locales/es.json +7 -3
- package/i18n/locales/fr.json +7 -3
- package/i18n/locales/he.json +7 -3
- package/i18n/locales/it.json +7 -3
- package/i18n/locales/ja.json +7 -3
- package/i18n/locales/pl.json +7 -3
- package/i18n/locales/tr.json +7 -3
- package/i18n/locales/uk.json +7 -3
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Notification } from '~/types/domain'
|
|
3
3
|
import type { ReviewEffort } from '~/types/merge'
|
|
4
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
4
5
|
|
|
5
6
|
// The board's notification inbox: a bell with an open-count badge that opens a
|
|
6
7
|
// panel of human-actionable items (a PR awaiting a merge decision, a completed
|
|
@@ -406,9 +407,9 @@ function revealDecision(n: Notification) {
|
|
|
406
407
|
|
|
407
408
|
<template #content>
|
|
408
409
|
<div class="max-h-[28rem] w-[min(24rem,92vw)] overflow-y-auto p-2">
|
|
409
|
-
<
|
|
410
|
+
<SectionLabel class="px-2 py-1">
|
|
410
411
|
{{ t('layout.notifications.heading') }}
|
|
411
|
-
</
|
|
412
|
+
</SectionLabel>
|
|
412
413
|
<div
|
|
413
414
|
v-for="n in notifications.open"
|
|
414
415
|
:key="n.id"
|
|
@@ -428,31 +429,34 @@ function revealDecision(n: Notification) {
|
|
|
428
429
|
/>
|
|
429
430
|
<div class="min-w-0 flex-1">
|
|
430
431
|
<div class="flex items-center gap-1.5">
|
|
431
|
-
<
|
|
432
|
-
|
|
432
|
+
<UButton
|
|
433
|
+
color="neutral"
|
|
434
|
+
variant="link"
|
|
435
|
+
class="block min-w-0 flex-1 truncate p-0 text-start text-sm font-medium text-default hover:underline"
|
|
433
436
|
:title="n.title"
|
|
434
437
|
@click="reveal(n)"
|
|
435
438
|
>
|
|
436
439
|
{{ n.title }}
|
|
437
|
-
</
|
|
440
|
+
</UButton>
|
|
438
441
|
<span
|
|
439
442
|
v-if="isUrgent(n)"
|
|
440
|
-
class="shrink-0 rounded bg-app-error-500/20 px-1.5 py-0.5 text-
|
|
443
|
+
class="shrink-0 rounded-sm bg-app-error-500/20 px-1.5 py-0.5 text-3xs font-semibold uppercase tracking-wide text-app-error-400"
|
|
441
444
|
>
|
|
442
445
|
{{ t('layout.notifications.overdue') }}
|
|
443
446
|
</span>
|
|
444
447
|
</div>
|
|
445
|
-
<p class="mt-0.5 text-
|
|
446
|
-
<
|
|
448
|
+
<p class="mt-0.5 text-2xs leading-snug text-muted">{{ n.body }}</p>
|
|
449
|
+
<ULink
|
|
450
|
+
raw
|
|
447
451
|
v-if="n.payload?.prUrl"
|
|
448
|
-
:
|
|
452
|
+
:to="n.payload.prUrl"
|
|
449
453
|
target="_blank"
|
|
450
454
|
rel="noopener"
|
|
451
|
-
class="mt-1 inline-flex items-center gap-1 text-
|
|
455
|
+
class="mt-1 inline-flex items-center gap-1 text-2xs text-app-info-400 hover:underline"
|
|
452
456
|
>
|
|
453
457
|
<UIcon name="i-lucide-external-link" class="h-3 w-3" />
|
|
454
458
|
{{ t('layout.notifications.openPr') }}
|
|
455
|
-
</
|
|
459
|
+
</ULink>
|
|
456
460
|
<!--
|
|
457
461
|
A platform-health card deep-links to the runs it aggregated, so the operator
|
|
458
462
|
lands on the evidence rather than only on the dashboard.
|
|
@@ -462,31 +466,38 @@ function revealDecision(n: Notification) {
|
|
|
462
466
|
class="mt-1.5 flex flex-col gap-0.5"
|
|
463
467
|
data-testid="notification-failing-runs"
|
|
464
468
|
>
|
|
465
|
-
<
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
<span
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
469
|
+
<template v-for="run in failingRuns(n)" :key="run.executionId">
|
|
470
|
+
<UButton
|
|
471
|
+
v-if="canOpenFailingRun(run)"
|
|
472
|
+
color="neutral"
|
|
473
|
+
variant="link"
|
|
474
|
+
size="xs"
|
|
475
|
+
class="gap-1 p-0 text-start text-2xs text-app-info-400 hover:text-app-info-400 hover:underline"
|
|
476
|
+
@click="revealFailingRun(run)"
|
|
477
|
+
>
|
|
478
|
+
<UIcon name="i-lucide-circle-alert" class="h-3 w-3 shrink-0" />
|
|
479
|
+
<span class="truncate">{{
|
|
480
|
+
t('layout.notifications.failingRun', {
|
|
481
|
+
kind: run.failureKind,
|
|
482
|
+
at: d(new Date(run.createdAt), 'short'),
|
|
483
|
+
})
|
|
484
|
+
}}</span>
|
|
485
|
+
</UButton>
|
|
486
|
+
<span
|
|
487
|
+
v-else
|
|
488
|
+
class="flex cursor-default items-center gap-1 text-start text-2xs text-dimmed"
|
|
489
|
+
:title="t('layout.notifications.failingRunGone')"
|
|
490
|
+
>
|
|
491
|
+
<UIcon name="i-lucide-circle-alert" class="h-3 w-3 shrink-0" />
|
|
492
|
+
<span class="truncate">{{
|
|
493
|
+
t('layout.notifications.failingRun', {
|
|
494
|
+
kind: run.failureKind,
|
|
495
|
+
at: d(new Date(run.createdAt), 'short'),
|
|
496
|
+
})
|
|
497
|
+
}}</span>
|
|
498
|
+
</span>
|
|
499
|
+
</template>
|
|
500
|
+
<span v-if="failingRunsOmitted(n) > 0" class="text-2xs text-dimmed">
|
|
490
501
|
{{ t('layout.notifications.failingRunsMore', { count: failingRunsOmitted(n) }) }}
|
|
491
502
|
</span>
|
|
492
503
|
</div>
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// previously sat, confusingly, among the workspace-wide integrations. Each row reuses the
|
|
6
6
|
// existing per-panel handlers via `ui.openFromPersonal(...)`, so opening one closes this
|
|
7
7
|
// hub, reveals that panel, and gives it a "Back to My setup" control (IntegrationBackTitle).
|
|
8
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
9
|
+
|
|
8
10
|
const { t } = useI18n()
|
|
9
11
|
const ui = useUiStore()
|
|
10
12
|
const userSecrets = useUserSecretsStore()
|
|
@@ -117,14 +119,15 @@ const groups = computed<PersonalGroup[]>(() => {
|
|
|
117
119
|
</p>
|
|
118
120
|
|
|
119
121
|
<section v-for="group in groups" :key="group.title">
|
|
120
|
-
<h3 class="mb-2 px-1
|
|
122
|
+
<SectionLabel as="h3" class="mb-2 px-1">
|
|
121
123
|
{{ group.title }}
|
|
122
|
-
</
|
|
124
|
+
</SectionLabel>
|
|
123
125
|
<div class="space-y-1.5">
|
|
124
|
-
<
|
|
126
|
+
<UButton
|
|
127
|
+
color="neutral"
|
|
128
|
+
variant="ghost"
|
|
125
129
|
v-for="item in group.items"
|
|
126
130
|
:key="item.key"
|
|
127
|
-
type="button"
|
|
128
131
|
class="flex w-full items-center gap-3 rounded-lg border border-default bg-default/50 px-3 py-2.5 text-start transition hover:border-muted hover:bg-default"
|
|
129
132
|
@click="item.onClick()"
|
|
130
133
|
>
|
|
@@ -135,7 +138,7 @@ const groups = computed<PersonalGroup[]>(() => {
|
|
|
135
138
|
<UBadge v-if="item.connected" color="success" variant="subtle" size="sm">
|
|
136
139
|
{{ item.status || t('layout.personalSetup.connected') }}
|
|
137
140
|
</UBadge>
|
|
138
|
-
<span v-else class="text-
|
|
141
|
+
<span v-else class="text-2xs text-dimmed">
|
|
139
142
|
{{ t('layout.personalSetup.notConnected') }}
|
|
140
143
|
</span>
|
|
141
144
|
</div>
|
|
@@ -145,7 +148,7 @@ const groups = computed<PersonalGroup[]>(() => {
|
|
|
145
148
|
name="i-lucide-chevron-right"
|
|
146
149
|
class="h-4 w-4 shrink-0 text-dimmed rtl:-scale-x-100"
|
|
147
150
|
/>
|
|
148
|
-
</
|
|
151
|
+
</UButton>
|
|
149
152
|
</div>
|
|
150
153
|
</section>
|
|
151
154
|
</div>
|
|
@@ -31,10 +31,11 @@ function pick(role: UiRole) {
|
|
|
31
31
|
<div class="space-y-4" data-testid="role-prompt">
|
|
32
32
|
<p class="text-sm text-toned">{{ t('uiRole.prompt.intro') }}</p>
|
|
33
33
|
<div class="space-y-2">
|
|
34
|
-
<
|
|
34
|
+
<UButton
|
|
35
|
+
color="neutral"
|
|
36
|
+
variant="ghost"
|
|
35
37
|
v-for="role in UI_ROLES"
|
|
36
38
|
:key="role"
|
|
37
|
-
type="button"
|
|
38
39
|
:data-testid="`role-option-${role}`"
|
|
39
40
|
:aria-pressed="role === uiRole.role && uiRole.chosen"
|
|
40
41
|
class="flex w-full items-center gap-3 rounded-lg border p-3 text-start transition"
|
|
@@ -52,11 +53,11 @@ function pick(role: UiRole) {
|
|
|
52
53
|
</div>
|
|
53
54
|
<p class="text-xs text-muted">{{ t(ROLE_PRESENTATION[role].hintKey) }}</p>
|
|
54
55
|
</div>
|
|
55
|
-
</
|
|
56
|
+
</UButton>
|
|
56
57
|
</div>
|
|
57
58
|
<!-- The choice is not a commitment, and saying so is what makes the narrowed role
|
|
58
59
|
pickable: it is one dropdown at the top of the sidebar to leave again. -->
|
|
59
|
-
<p class="text-
|
|
60
|
+
<p class="text-2xs leading-snug text-dimmed">{{ t('uiRole.prompt.change') }}</p>
|
|
60
61
|
</div>
|
|
61
62
|
</template>
|
|
62
63
|
<template #footer>
|
|
@@ -20,6 +20,7 @@ import UiRoleSwitcher from '~/components/layout/UiRoleSwitcher.vue'
|
|
|
20
20
|
import UserMenu from '~/components/auth/UserMenu.vue'
|
|
21
21
|
import { useViewport } from '~/composables/useViewport'
|
|
22
22
|
import type { NavContribution } from '~/modular/nav-contributions'
|
|
23
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
23
24
|
|
|
24
25
|
const { t } = useI18n()
|
|
25
26
|
|
|
@@ -225,8 +226,9 @@ watch(
|
|
|
225
226
|
<div class="contents" @click="onNavAction">
|
|
226
227
|
<!-- Command bar launcher (⌘K) — the primary way to create blocks / pipelines
|
|
227
228
|
and reach every action below. -->
|
|
228
|
-
<
|
|
229
|
-
|
|
229
|
+
<UButton
|
|
230
|
+
color="neutral"
|
|
231
|
+
variant="ghost"
|
|
230
232
|
class="flex items-center gap-2 rounded-lg border border-muted bg-elevated/60 py-2 text-start text-sm text-muted transition hover:border-app-500 hover:bg-elevated"
|
|
231
233
|
:class="railed ? 'justify-center px-0' : 'px-2.5'"
|
|
232
234
|
:aria-label="t('nav.commandBar')"
|
|
@@ -237,7 +239,7 @@ watch(
|
|
|
237
239
|
<UIcon name="i-lucide-search" class="h-4 w-4 shrink-0" />
|
|
238
240
|
<span v-if="!railed" class="flex-1 truncate">{{ t('nav.commandBar') }}</span>
|
|
239
241
|
<UKbd v-if="!railed" value="⌘K" />
|
|
240
|
-
</
|
|
242
|
+
</UButton>
|
|
241
243
|
|
|
242
244
|
<!-- Sections + items come from the shared nav manifest, already gated by the
|
|
243
245
|
reactive slotFilter (backend/docs/adr/0049-modular-vue-adoption.md, slice 1) — which
|
|
@@ -248,12 +250,9 @@ watch(
|
|
|
248
250
|
<template v-for="section in sidebarGroups" :key="section.group">
|
|
249
251
|
<USeparator />
|
|
250
252
|
<section>
|
|
251
|
-
<h2
|
|
252
|
-
v-if="!railed"
|
|
253
|
-
class="mb-2 px-1 text-[11px] font-semibold uppercase tracking-wide text-muted"
|
|
254
|
-
>
|
|
253
|
+
<SectionLabel v-if="!railed" as="h2" class="mb-2 px-1">
|
|
255
254
|
{{ t(section.labelKey) }}
|
|
256
|
-
</
|
|
255
|
+
</SectionLabel>
|
|
257
256
|
<div class="space-y-1.5">
|
|
258
257
|
<UButton
|
|
259
258
|
v-for="item in section.items"
|
|
@@ -54,7 +54,7 @@ async function resume() {
|
|
|
54
54
|
|
|
55
55
|
<dl class="mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3">
|
|
56
56
|
<div class="rounded-lg bg-app-error-900/50 px-3 py-2">
|
|
57
|
-
<dt class="text-
|
|
57
|
+
<dt class="text-2xs uppercase tracking-wide text-app-error-300/80">
|
|
58
58
|
{{ t('layout.spendWarningBanner.spent') }}
|
|
59
59
|
</dt>
|
|
60
60
|
<dd class="text-base font-semibold tabular-nums text-app-error-50">
|
|
@@ -62,7 +62,7 @@ async function resume() {
|
|
|
62
62
|
</dd>
|
|
63
63
|
</div>
|
|
64
64
|
<div class="rounded-lg bg-app-error-900/50 px-3 py-2">
|
|
65
|
-
<dt class="text-
|
|
65
|
+
<dt class="text-2xs uppercase tracking-wide text-app-error-300/80">
|
|
66
66
|
{{ t('layout.spendWarningBanner.budget') }}
|
|
67
67
|
</dt>
|
|
68
68
|
<dd class="text-base font-semibold tabular-nums text-app-error-50">
|
|
@@ -70,7 +70,7 @@ async function resume() {
|
|
|
70
70
|
</dd>
|
|
71
71
|
</div>
|
|
72
72
|
<div class="rounded-lg bg-app-error-900/50 px-3 py-2">
|
|
73
|
-
<dt class="text-
|
|
73
|
+
<dt class="text-2xs uppercase tracking-wide text-app-error-300/80">
|
|
74
74
|
{{ t('layout.spendWarningBanner.tokens') }}
|
|
75
75
|
</dt>
|
|
76
76
|
<dd class="text-base font-semibold tabular-nums text-app-error-50">{{ tokens }}</dd>
|
|
@@ -31,7 +31,7 @@ function dismiss() {
|
|
|
31
31
|
v-if="show"
|
|
32
32
|
data-testid="translation-warning"
|
|
33
33
|
role="alert"
|
|
34
|
-
class="flex shrink-0 items-center gap-3 border-b border-app-warning-500/40 bg-app-warning-950/95 px-4 py-2 text-
|
|
34
|
+
class="flex shrink-0 items-center gap-3 border-b border-app-warning-500/40 bg-app-warning-950/95 px-4 py-2 text-sm text-app-warning-100 shadow-lg backdrop-blur"
|
|
35
35
|
>
|
|
36
36
|
<UIcon name="i-lucide-languages" class="h-4 w-4 shrink-0 text-app-warning-400" />
|
|
37
37
|
<p class="min-w-0 flex-1">
|
|
@@ -39,15 +39,16 @@ function dismiss() {
|
|
|
39
39
|
<span class="mx-1.5 text-app-warning-400/60">·</span>
|
|
40
40
|
<i18n-t keypath="language.warning.body" tag="span" scope="global">
|
|
41
41
|
<template #repoLink>
|
|
42
|
-
<
|
|
43
|
-
|
|
42
|
+
<ULink
|
|
43
|
+
raw
|
|
44
|
+
:to="REPO_URL"
|
|
44
45
|
target="_blank"
|
|
45
46
|
rel="noopener noreferrer"
|
|
46
47
|
class="inline-flex items-center gap-1 font-medium text-app-info-300 hover:underline"
|
|
47
48
|
>
|
|
48
49
|
{{ t('language.warning.repoLinkLabel') }}
|
|
49
50
|
<UIcon name="i-lucide-external-link" class="h-3 w-3" />
|
|
50
|
-
</
|
|
51
|
+
</ULink>
|
|
51
52
|
</template>
|
|
52
53
|
</i18n-t>
|
|
53
54
|
</p>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { UI_MODES, type UiMode } from '~/utils/uiMode'
|
|
4
|
+
import SectionLabel from '~/components/common/SectionLabel.vue'
|
|
4
5
|
|
|
5
6
|
// Interface-tier picker, shown at the TOP of the sidebar under the board switcher. Basic mode
|
|
6
7
|
// hides the power-user destinations and the less-used run options; advanced shows everything.
|
|
@@ -55,9 +56,9 @@ const pinnedTitle = computed(
|
|
|
55
56
|
>
|
|
56
57
|
<UIcon :name="icon" class="h-4 w-4 shrink-0 text-dimmed" />
|
|
57
58
|
<div v-if="!collapsed" class="min-w-0 flex-1">
|
|
58
|
-
<
|
|
59
|
+
<SectionLabel class="truncate">
|
|
59
60
|
{{ t('uiMode.switcher') }}
|
|
60
|
-
</
|
|
61
|
+
</SectionLabel>
|
|
61
62
|
<div class="truncate text-xs font-medium text-toned">{{ currentLabel }}</div>
|
|
62
63
|
</div>
|
|
63
64
|
<UIcon v-if="!collapsed" name="i-lucide-lock" class="h-3.5 w-3.5 shrink-0 text-app-600" />
|
|
@@ -66,9 +67,10 @@ const pinnedTitle = computed(
|
|
|
66
67
|
<!-- Rail: one button, flips the tier. The label rides under the glyph so the rail still says
|
|
67
68
|
which tier is on — the audience that needs to find the advanced half is exactly the one
|
|
68
69
|
sitting in basic mode's collapsed default. -->
|
|
69
|
-
<
|
|
70
|
+
<UButton
|
|
71
|
+
color="neutral"
|
|
72
|
+
variant="ghost"
|
|
70
73
|
v-else-if="collapsed"
|
|
71
|
-
type="button"
|
|
72
74
|
data-testid="ui-mode-toggle"
|
|
73
75
|
:aria-label="t('uiMode.switchTo', { mode: t(MODE_LABELS[otherMode]) })"
|
|
74
76
|
:title="t('uiMode.switchTo', { mode: t(MODE_LABELS[otherMode]) })"
|
|
@@ -76,24 +78,25 @@ const pinnedTitle = computed(
|
|
|
76
78
|
@click="uiMode.toggleMode()"
|
|
77
79
|
>
|
|
78
80
|
<UIcon :name="icon" class="h-4 w-4 shrink-0 text-primary" />
|
|
79
|
-
<span class="w-full truncate text-center text-
|
|
81
|
+
<span class="w-full truncate text-center text-3xs font-medium uppercase text-toned">
|
|
80
82
|
{{ currentLabel }}
|
|
81
83
|
</span>
|
|
82
|
-
</
|
|
84
|
+
</UButton>
|
|
83
85
|
|
|
84
86
|
<div v-else data-testid="ui-mode-switcher" class="w-full">
|
|
85
|
-
<
|
|
87
|
+
<SectionLabel class="mb-1 px-1">
|
|
86
88
|
{{ t('uiMode.switcher') }}
|
|
87
|
-
</
|
|
89
|
+
</SectionLabel>
|
|
88
90
|
<div
|
|
89
91
|
role="group"
|
|
90
92
|
:aria-label="t('uiMode.switcher')"
|
|
91
93
|
class="flex w-full gap-1 rounded-lg border border-muted bg-default/60 p-1"
|
|
92
94
|
>
|
|
93
|
-
<
|
|
95
|
+
<UButton
|
|
96
|
+
color="neutral"
|
|
97
|
+
variant="ghost"
|
|
94
98
|
v-for="mode in UI_MODES"
|
|
95
99
|
:key="mode"
|
|
96
|
-
type="button"
|
|
97
100
|
:data-testid="`ui-mode-option-${mode}`"
|
|
98
101
|
:aria-pressed="mode === uiMode.mode"
|
|
99
102
|
class="flex-1 truncate rounded-md px-2 py-1 text-xs font-medium transition"
|
|
@@ -105,13 +108,13 @@ const pinnedTitle = computed(
|
|
|
105
108
|
@click="uiMode.setMode(mode)"
|
|
106
109
|
>
|
|
107
110
|
{{ t(MODE_LABELS[mode]) }}
|
|
108
|
-
</
|
|
111
|
+
</UButton>
|
|
109
112
|
</div>
|
|
110
113
|
</div>
|
|
111
114
|
|
|
112
115
|
<!-- The one-line "what this tier gives you", so the choice is self-explanatory. Dropped in
|
|
113
116
|
the collapsed rail, where the tooltip above carries the mode instead. -->
|
|
114
|
-
<p v-if="!collapsed" class="px-1 text-
|
|
117
|
+
<p v-if="!collapsed" class="px-1 text-3xs leading-snug text-dimmed">
|
|
115
118
|
{{ t(MODE_HINTS[uiMode.mode]) }}
|
|
116
119
|
</p>
|
|
117
120
|
</template>
|
|
@@ -38,23 +38,25 @@ const items = computed(() =>
|
|
|
38
38
|
<template>
|
|
39
39
|
<UDropdownMenu :items="items" :ui="{ content: 'min-w-48' }">
|
|
40
40
|
<!-- Rail: glyph over the role name, matching the tier button beside it. -->
|
|
41
|
-
<
|
|
41
|
+
<UButton
|
|
42
|
+
color="neutral"
|
|
43
|
+
variant="ghost"
|
|
42
44
|
v-if="collapsed"
|
|
43
|
-
type="button"
|
|
44
45
|
data-testid="ui-role-toggle"
|
|
45
46
|
:aria-label="`${t('uiRole.switcher')}: ${currentLabel}`"
|
|
46
47
|
:title="`${t('uiRole.switcher')}: ${currentLabel}`"
|
|
47
48
|
class="flex w-full flex-col items-center gap-0.5 rounded-lg border border-muted bg-default/60 px-1 py-1.5 transition hover:border-primary/60 hover:bg-elevated/60"
|
|
48
49
|
>
|
|
49
50
|
<UIcon :name="current.icon" class="h-4 w-4 shrink-0 text-primary" />
|
|
50
|
-
<span class="w-full truncate text-center text-
|
|
51
|
+
<span class="w-full truncate text-center text-3xs font-medium uppercase text-toned">
|
|
51
52
|
{{ currentLabel }}
|
|
52
53
|
</span>
|
|
53
|
-
</
|
|
54
|
+
</UButton>
|
|
54
55
|
|
|
55
|
-
<
|
|
56
|
+
<UButton
|
|
57
|
+
color="neutral"
|
|
58
|
+
variant="ghost"
|
|
56
59
|
v-else
|
|
57
|
-
type="button"
|
|
58
60
|
data-testid="ui-role-switcher"
|
|
59
61
|
:aria-label="t('uiRole.switcher')"
|
|
60
62
|
:title="t(current.hintKey)"
|
|
@@ -62,12 +64,12 @@ const items = computed(() =>
|
|
|
62
64
|
>
|
|
63
65
|
<UIcon :name="current.icon" class="h-4 w-4 shrink-0 text-primary" />
|
|
64
66
|
<div class="min-w-0 flex-1">
|
|
65
|
-
<div class="truncate text-
|
|
67
|
+
<div class="truncate text-2xs uppercase tracking-wide text-muted">
|
|
66
68
|
{{ t('uiRole.switcher') }}
|
|
67
69
|
</div>
|
|
68
70
|
<div class="truncate text-xs font-medium text-default">{{ currentLabel }}</div>
|
|
69
71
|
</div>
|
|
70
72
|
<UIcon name="i-lucide-chevron-down" class="h-3.5 w-3.5 shrink-0 text-dimmed" />
|
|
71
|
-
</
|
|
73
|
+
</UButton>
|
|
72
74
|
</UDropdownMenu>
|
|
73
75
|
</template>
|
|
@@ -221,7 +221,7 @@ function memberLabel(userId: string, name?: string | null, email?: string | null
|
|
|
221
221
|
{{ t('layout.workspaceMembers.add.noAccount') }}
|
|
222
222
|
</p>
|
|
223
223
|
<template v-else>
|
|
224
|
-
<
|
|
224
|
+
<UForm class="flex gap-2" @submit="addMember">
|
|
225
225
|
<USelect
|
|
226
226
|
v-model="addUserId"
|
|
227
227
|
:items="candidates"
|
|
@@ -242,7 +242,7 @@ function memberLabel(userId: string, name?: string | null, email?: string | null
|
|
|
242
242
|
>
|
|
243
243
|
{{ t('layout.workspaceMembers.add.submit') }}
|
|
244
244
|
</UButton>
|
|
245
|
-
</
|
|
245
|
+
</UForm>
|
|
246
246
|
<p v-if="candidates.length === 0" class="mt-2 text-dimmed">
|
|
247
247
|
{{ t('layout.workspaceMembers.add.allAdded') }}
|
|
248
248
|
</p>
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { computed, ref, watch } from 'vue'
|
|
10
10
|
import { useModalBehavior } from '@modular-vue/core'
|
|
11
11
|
import type { ArtifactBlobs } from '~/composables/useArtifactBlobs'
|
|
12
|
+
import IconButton from '~/components/common/IconButton.vue'
|
|
12
13
|
|
|
13
14
|
interface LightboxItem {
|
|
14
15
|
artifactId: string
|
|
@@ -173,46 +174,63 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey, true))
|
|
|
173
174
|
>
|
|
174
175
|
<!-- Toolbar -->
|
|
175
176
|
<div class="flex items-center gap-3 border-b border-default/60 px-4 py-2.5">
|
|
176
|
-
<span class="min-w-0 flex-1 truncate text-
|
|
177
|
+
<span class="min-w-0 flex-1 truncate text-sm font-medium text-default">
|
|
177
178
|
{{ current?.label ?? t('media.lightbox.fallbackTitle') }}
|
|
178
179
|
</span>
|
|
179
|
-
<span v-if="total > 1" class="shrink-0 text-
|
|
180
|
+
<span v-if="total > 1" class="shrink-0 text-xs tabular-nums text-muted">
|
|
180
181
|
{{ t('media.lightbox.counter', { current: index + 1, total }) }}
|
|
181
182
|
</span>
|
|
182
183
|
<div class="flex shrink-0 items-center gap-1">
|
|
183
|
-
<
|
|
184
|
-
|
|
185
|
-
|
|
184
|
+
<IconButton
|
|
185
|
+
color="neutral"
|
|
186
|
+
variant="ghost"
|
|
187
|
+
icon="i-lucide-zoom-out"
|
|
188
|
+
:label="t('media.lightbox.zoomOut')"
|
|
189
|
+
:ui="{
|
|
190
|
+
base: 'rounded-md p-1.5 text-muted hover:bg-elevated hover:text-default disabled:opacity-40',
|
|
191
|
+
leadingIcon: 'h-4 w-4',
|
|
192
|
+
}"
|
|
186
193
|
:disabled="scale <= MIN_SCALE"
|
|
187
194
|
@click="zoomBy(1 / 1.25)"
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
</button>
|
|
191
|
-
<span class="w-10 text-center text-[11px] tabular-nums text-dimmed">{{
|
|
195
|
+
/>
|
|
196
|
+
<span class="w-10 text-center text-2xs tabular-nums text-dimmed">{{
|
|
192
197
|
n(scale, 'percent')
|
|
193
198
|
}}</span>
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
199
|
+
<IconButton
|
|
200
|
+
color="neutral"
|
|
201
|
+
variant="ghost"
|
|
202
|
+
icon="i-lucide-zoom-in"
|
|
203
|
+
:label="t('media.lightbox.zoomIn')"
|
|
204
|
+
:ui="{
|
|
205
|
+
base: 'rounded-md p-1.5 text-muted hover:bg-elevated hover:text-default disabled:opacity-40',
|
|
206
|
+
leadingIcon: 'h-4 w-4',
|
|
207
|
+
}"
|
|
197
208
|
:disabled="scale >= MAX_SCALE"
|
|
198
209
|
@click="zoomBy(1.25)"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
:
|
|
210
|
+
/>
|
|
211
|
+
<IconButton
|
|
212
|
+
color="neutral"
|
|
213
|
+
variant="ghost"
|
|
214
|
+
icon="i-lucide-maximize"
|
|
215
|
+
:label="t('media.lightbox.reset')"
|
|
216
|
+
:ui="{
|
|
217
|
+
base: 'rounded-md p-1.5 text-muted hover:bg-elevated hover:text-default',
|
|
218
|
+
leadingIcon: 'h-4 w-4',
|
|
219
|
+
}"
|
|
205
220
|
@click="resetView"
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
221
|
+
/>
|
|
222
|
+
<IconButton
|
|
223
|
+
color="neutral"
|
|
224
|
+
variant="ghost"
|
|
225
|
+
icon="i-lucide-x"
|
|
226
|
+
class="ms-1"
|
|
227
|
+
:label="t('media.lightbox.close')"
|
|
228
|
+
:ui="{
|
|
229
|
+
base: 'rounded-md p-1.5 text-muted hover:bg-elevated hover:text-default',
|
|
230
|
+
leadingIcon: 'h-4 w-4',
|
|
231
|
+
}"
|
|
212
232
|
@click="close"
|
|
213
|
-
|
|
214
|
-
<UIcon name="i-lucide-x" class="h-4 w-4" />
|
|
215
|
-
</button>
|
|
233
|
+
/>
|
|
216
234
|
</div>
|
|
217
235
|
</div>
|
|
218
236
|
|
|
@@ -222,21 +240,26 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey, true))
|
|
|
222
240
|
@wheel="onWheel"
|
|
223
241
|
@click.self="close"
|
|
224
242
|
>
|
|
225
|
-
<
|
|
243
|
+
<IconButton
|
|
226
244
|
v-if="total > 1"
|
|
227
|
-
|
|
228
|
-
|
|
245
|
+
color="neutral"
|
|
246
|
+
variant="ghost"
|
|
247
|
+
icon="i-lucide-chevron-left"
|
|
248
|
+
class="absolute start-3 top-1/2 z-10 -translate-y-1/2"
|
|
249
|
+
:label="t('media.lightbox.prev')"
|
|
250
|
+
:ui="{
|
|
251
|
+
base: 'rounded-full bg-default/80 p-2 text-toned hover:bg-elevated hover:text-highlighted',
|
|
252
|
+
leadingIcon: 'h-5 w-5 rtl:-scale-x-100',
|
|
253
|
+
}"
|
|
229
254
|
@click="go(-1)"
|
|
230
|
-
|
|
231
|
-
<UIcon name="i-lucide-chevron-left" class="h-5 w-5 rtl:-scale-x-100" />
|
|
232
|
-
</button>
|
|
255
|
+
/>
|
|
233
256
|
|
|
234
257
|
<img
|
|
235
258
|
v-if="url"
|
|
236
259
|
:src="url"
|
|
237
260
|
:alt="current?.alt ?? ''"
|
|
238
261
|
draggable="false"
|
|
239
|
-
class="max-h-full max-w-full select-none rounded shadow-2xl"
|
|
262
|
+
class="max-h-full max-w-full select-none rounded-sm shadow-2xl"
|
|
240
263
|
:class="[
|
|
241
264
|
scale > 1 ? (dragging ? 'cursor-grabbing' : 'cursor-grab') : 'cursor-zoom-in',
|
|
242
265
|
dragging ? '' : 'transition-transform duration-100',
|
|
@@ -254,26 +277,33 @@ onBeforeUnmount(() => window.removeEventListener('keydown', onKey, true))
|
|
|
254
277
|
class="h-8 w-8"
|
|
255
278
|
:class="state === 'error' ? '' : 'animate-spin'"
|
|
256
279
|
/>
|
|
257
|
-
<p class="text-
|
|
280
|
+
<p class="text-xs">
|
|
258
281
|
{{ state === 'error' ? t('media.lightbox.failed') : t('media.lightbox.loading') }}
|
|
259
282
|
</p>
|
|
260
|
-
<
|
|
283
|
+
<UButton
|
|
284
|
+
color="neutral"
|
|
285
|
+
variant="link"
|
|
261
286
|
v-if="state === 'error' && current"
|
|
262
|
-
class="text-
|
|
287
|
+
class="p-0 text-xs text-app-warning-300 hover:underline"
|
|
263
288
|
@click="props.blobs.retry(current.artifactId)"
|
|
264
289
|
>
|
|
265
290
|
{{ t('common.retry') }}
|
|
266
|
-
</
|
|
291
|
+
</UButton>
|
|
267
292
|
</div>
|
|
268
293
|
|
|
269
|
-
<
|
|
294
|
+
<IconButton
|
|
270
295
|
v-if="total > 1"
|
|
271
|
-
|
|
272
|
-
|
|
296
|
+
color="neutral"
|
|
297
|
+
variant="ghost"
|
|
298
|
+
icon="i-lucide-chevron-right"
|
|
299
|
+
class="absolute end-3 top-1/2 z-10 -translate-y-1/2"
|
|
300
|
+
:label="t('media.lightbox.next')"
|
|
301
|
+
:ui="{
|
|
302
|
+
base: 'rounded-full bg-default/80 p-2 text-toned hover:bg-elevated hover:text-highlighted',
|
|
303
|
+
leadingIcon: 'h-5 w-5 rtl:-scale-x-100',
|
|
304
|
+
}"
|
|
273
305
|
@click="go(1)"
|
|
274
|
-
|
|
275
|
-
<UIcon name="i-lucide-chevron-right" class="h-5 w-5 rtl:-scale-x-100" />
|
|
276
|
-
</button>
|
|
306
|
+
/>
|
|
277
307
|
</div>
|
|
278
308
|
</div>
|
|
279
309
|
</Teleport>
|