@cat-factory/app 0.309.0 → 0.310.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 +11 -2
- package/app/components/assistant/AssistantModal.vue +5 -4
- 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 +4 -3
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +34 -23
- package/app/components/binaryOutput/StoredAssetView.vue +8 -6
- package/app/components/board/AddTaskModal.vue +16 -15
- package/app/components/board/AgentFailureCard.vue +12 -9
- package/app/components/board/AgentFailureHistory.vue +18 -9
- package/app/components/board/CreateInitiativeModal.vue +5 -4
- package/app/components/board/FailureDetail.vue +22 -12
- package/app/components/board/ReviewFrictionDialog.vue +4 -3
- package/app/components/board/nodes/BlockNode.vue +17 -15
- package/app/components/board/nodes/DecisionBadge.vue +4 -3
- package/app/components/board/nodes/FrameSwimlanes.vue +4 -3
- package/app/components/board/nodes/TaskCard.vue +12 -7
- package/app/components/board/nodes/TaskPipelineMini.vue +9 -7
- package/app/components/bootstrap/AdoptionReviewModal.vue +30 -21
- package/app/components/bootstrap/BootstrapModal.vue +3 -3
- package/app/components/brainstorm/BrainstormWindow.vue +5 -3
- package/app/components/bugFishing/BugFishingWindow.vue +49 -27
- package/app/components/clarity/ClarityReviewWindow.vue +5 -3
- package/app/components/common/CopyButton.vue +12 -11
- package/app/components/common/IconButton.vue +34 -9
- package/app/components/context/ContextAttachmentFields.vue +23 -12
- package/app/components/documents/ContextDocumentPicker.vue +12 -9
- package/app/components/documents/DocumentImportModal.vue +4 -3
- package/app/components/documents/DocumentOriginLink.vue +12 -9
- package/app/components/documents/DocumentSyncState.vue +3 -3
- package/app/components/documents/RepoContextDocPicker.vue +4 -3
- package/app/components/environments/EnvironmentStatusPanel.vue +4 -3
- package/app/components/followUp/FollowUpWindow.vue +9 -6
- package/app/components/forkDecision/ForkDecisionWindow.vue +71 -80
- package/app/components/foundational/FoundationalServiceCatalogList.vue +3 -2
- package/app/components/foundational/FoundationalServiceRegistry.vue +7 -6
- package/app/components/foundational/FoundationalServiceSources.vue +7 -6
- package/app/components/fragments/FragmentLibraryManager.vue +5 -4
- package/app/components/fragments/FragmentSelector.vue +12 -9
- package/app/components/gates/GateFailingCheckList.vue +4 -3
- package/app/components/gates/GateResultView.vue +17 -12
- package/app/components/github/AddServiceFromRepoModal.vue +11 -7
- package/app/components/github/GitHubOnboarding.vue +7 -2
- package/app/components/github/GitHubPanel.vue +9 -6
- package/app/components/github/RepoTreeBrowser.vue +15 -12
- package/app/components/humanTest/HumanTestWindow.vue +13 -9
- package/app/components/initiative/InitiativePlanReview.vue +28 -18
- package/app/components/initiative/InitiativeTrackerWindow.vue +171 -124
- package/app/components/judge/JudgeResultView.vue +5 -3
- package/app/components/kaizen/KaizenPanel.vue +67 -53
- package/app/components/layout/AccountTeamSettings.vue +9 -9
- package/app/components/layout/AppearanceSwitcher.vue +4 -3
- package/app/components/layout/BoardSwitcher.vue +10 -8
- package/app/components/layout/BoardToolbar.vue +5 -4
- package/app/components/layout/CommandBar.vue +4 -3
- package/app/components/layout/DefaultTestEnvBanner.vue +4 -3
- package/app/components/layout/IntegrationsHub.vue +8 -6
- package/app/components/layout/LanguageSwitcher.vue +4 -3
- package/app/components/layout/ModelProvidersHub.vue +4 -3
- package/app/components/layout/NotificationsInbox.vue +40 -30
- package/app/components/layout/PersonalSetupModal.vue +4 -3
- package/app/components/layout/RolePrompt.vue +4 -3
- package/app/components/layout/SideBar.vue +4 -3
- package/app/components/layout/TranslationWarningBanner.vue +4 -3
- package/app/components/layout/UiModeSwitcher.vue +8 -6
- package/app/components/layout/UiRoleSwitcher.vue +8 -6
- package/app/components/layout/WorkspaceMembersSettings.vue +2 -2
- package/app/components/media/ArtifactLightbox.vue +69 -39
- package/app/components/media/ImageCompare.vue +69 -74
- package/app/components/observability/OutcomeFilterChips.vue +5 -4
- package/app/components/observability/RunFailureSummary.vue +8 -6
- package/app/components/observability/StepModelActivity.vue +5 -3
- package/app/components/observability/ToolCallList.vue +4 -2
- package/app/components/outcome/OutcomeSummaryWindow.vue +5 -4
- package/app/components/palettes/AgentPalette.vue +18 -14
- package/app/components/panels/AgentStepDetail.vue +30 -19
- package/app/components/panels/ObservabilityPanel.vue +149 -114
- package/app/components/panels/OperatorDashboardPanel.vue +99 -91
- package/app/components/panels/ReportsPanel.vue +64 -61
- package/app/components/panels/ResultWindowShell.vue +148 -134
- package/app/components/panels/StepContainerStatus.vue +9 -9
- package/app/components/panels/StepDelegatedStatus.vue +11 -9
- package/app/components/panels/StepRestartControl.vue +3 -2
- package/app/components/panels/inspector/EpicChildren.vue +4 -3
- package/app/components/panels/inspector/FrontendConfig.vue +3 -2
- package/app/components/panels/inspector/InspectorSection.vue +22 -17
- package/app/components/panels/inspector/ServiceConnections.vue +3 -2
- package/app/components/panels/inspector/ServiceTestConfig.vue +5 -4
- package/app/components/panels/inspector/TaskExecution.vue +5 -4
- package/app/components/panels/inspector/TaskRunSettings.vue +10 -6
- package/app/components/pipeline/EstimateThresholdFields.vue +13 -10
- package/app/components/pipeline/GateConfigFields.vue +14 -18
- package/app/components/pipeline/PipelineBuilder.vue +90 -77
- package/app/components/pipeline/PipelinePicker.vue +8 -6
- package/app/components/pipeline/PipelineProgress.vue +26 -21
- package/app/components/prReview/PrReviewWindow.vue +46 -25
- package/app/components/providers/ApiKeysSection.vue +4 -3
- package/app/components/ralph/RalphLoopResultView.vue +4 -3
- package/app/components/requirements/RequirementsReviewWindow.vue +10 -6
- package/app/components/riskPolicy/RiskPolicyPicker.vue +8 -6
- package/app/components/sandbox/SandboxPanel.vue +73 -54
- package/app/components/settings/CloudflareHandlerSection.vue +33 -30
- package/app/components/settings/ConsensusGroupsSection.vue +40 -30
- package/app/components/settings/InfraHandlersConfigurator.vue +16 -13
- package/app/components/settings/InfrastructureBackendPicker.vue +17 -17
- package/app/components/settings/IssueTrackerPanel.vue +21 -6
- package/app/components/settings/LocalModelEndpointsPanel.vue +3 -2
- package/app/components/settings/ModelConfigurationPanel.vue +9 -8
- package/app/components/settings/OpenRouterCatalogPanel.vue +4 -3
- package/app/components/settings/ProviderPreferenceEditor.vue +5 -6
- package/app/components/settings/RiskPolicyEditorRow.vue +3 -2
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +10 -8
- package/app/components/skills/ReviewSkillQueue.vue +4 -3
- package/app/components/skills/SkillLibraryManager.vue +7 -6
- package/app/components/slack/SlackPanel.vue +9 -5
- package/app/components/spec/ServiceSpecWindow.vue +4 -3
- package/app/components/tasks/ContextIssuePicker.vue +12 -9
- package/app/components/tasks/TaskContextIssues.vue +4 -3
- package/app/components/testing/TestReportWindow.vue +14 -8
- package/app/components/theme/ThemeImportModal.vue +2 -2
- package/app/components/visualConfirm/VisualConfirmationWindow.vue +62 -35
- 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 +2 -2
|
@@ -8,6 +8,7 @@ import AccountModelPolicySettings from '~/components/layout/AccountModelPolicySe
|
|
|
8
8
|
import AccountPlatformAlertSettings from '~/components/layout/AccountPlatformAlertSettings.vue'
|
|
9
9
|
import AccountRunCredentialSettings from '~/components/layout/AccountRunCredentialSettings.vue'
|
|
10
10
|
import SecretInput from '~/components/common/SecretInput.vue'
|
|
11
|
+
import IconButton from '~/components/common/IconButton.vue'
|
|
11
12
|
|
|
12
13
|
// Team settings for an org account: the member roster (with combinable admin /
|
|
13
14
|
// developer / product roles), pending email invitations, and the per-account
|
|
@@ -207,7 +208,7 @@ async function disconnectEmail() {
|
|
|
207
208
|
<p class="mb-3 text-muted">
|
|
208
209
|
{{ t('layout.accountTeam.org.ctaBody') }}
|
|
209
210
|
</p>
|
|
210
|
-
<
|
|
211
|
+
<UForm class="flex gap-2" @submit="createOrganization">
|
|
211
212
|
<UInput
|
|
212
213
|
v-model="newOrgName"
|
|
213
214
|
:placeholder="t('layout.accountTeam.org.namePlaceholder')"
|
|
@@ -216,7 +217,7 @@ async function disconnectEmail() {
|
|
|
216
217
|
<UButton type="submit" color="primary" :loading="busy" icon="i-lucide-plus">
|
|
217
218
|
{{ t('layout.accountTeam.org.create') }}
|
|
218
219
|
</UButton>
|
|
219
|
-
</
|
|
220
|
+
</UForm>
|
|
220
221
|
</section>
|
|
221
222
|
|
|
222
223
|
<!-- members -->
|
|
@@ -249,14 +250,13 @@ async function disconnectEmail() {
|
|
|
249
250
|
<!-- Offboarding: end every session this member holds, leaving their membership and
|
|
250
251
|
roles alone. Confirmed, because it is not undoable from here (the person simply
|
|
251
252
|
signs in again) and because it is the sort of thing a mis-click should not do. -->
|
|
252
|
-
<
|
|
253
|
+
<IconButton
|
|
253
254
|
v-if="isAdmin"
|
|
254
255
|
size="xs"
|
|
255
256
|
color="neutral"
|
|
256
257
|
variant="ghost"
|
|
257
258
|
icon="i-lucide-log-out"
|
|
258
|
-
:
|
|
259
|
-
:aria-label="t('layout.accountTeam.members.revokeSessions')"
|
|
259
|
+
:label="t('layout.accountTeam.members.revokeSessions')"
|
|
260
260
|
data-testid="revoke-member-sessions"
|
|
261
261
|
@click="revokeSessions(m.userId, m.name || m.email || m.userId)"
|
|
262
262
|
/>
|
|
@@ -272,7 +272,7 @@ async function disconnectEmail() {
|
|
|
272
272
|
<h3 class="mb-2 font-semibold text-highlighted">
|
|
273
273
|
{{ t('layout.accountTeam.invite.title') }}
|
|
274
274
|
</h3>
|
|
275
|
-
<
|
|
275
|
+
<UForm class="flex gap-2" @submit="sendInvite">
|
|
276
276
|
<UInput
|
|
277
277
|
v-model="inviteEmail"
|
|
278
278
|
type="email"
|
|
@@ -283,7 +283,7 @@ async function disconnectEmail() {
|
|
|
283
283
|
<UButton type="submit" color="primary" :loading="busy" icon="i-lucide-send">
|
|
284
284
|
{{ t('layout.accountTeam.invite.submit') }}
|
|
285
285
|
</UButton>
|
|
286
|
-
</
|
|
286
|
+
</UForm>
|
|
287
287
|
|
|
288
288
|
<ul v-if="accounts.invitations.length" class="mt-3 space-y-1">
|
|
289
289
|
<li
|
|
@@ -332,7 +332,7 @@ async function disconnectEmail() {
|
|
|
332
332
|
{{ t('layout.accountTeam.email.disconnect') }}
|
|
333
333
|
</UButton>
|
|
334
334
|
</div>
|
|
335
|
-
<
|
|
335
|
+
<UForm v-else class="space-y-2" @submit="connectEmail">
|
|
336
336
|
<USelect v-model="emailProvider" :items="EMAIL_PROVIDER_ITEMS" class="w-full" />
|
|
337
337
|
<UInput
|
|
338
338
|
v-model="emailFrom"
|
|
@@ -348,7 +348,7 @@ async function disconnectEmail() {
|
|
|
348
348
|
<UButton type="submit" color="primary" :loading="busy">
|
|
349
349
|
{{ t('layout.accountTeam.email.connect') }}
|
|
350
350
|
</UButton>
|
|
351
|
-
</
|
|
351
|
+
</UForm>
|
|
352
352
|
</template>
|
|
353
353
|
</section>
|
|
354
354
|
|
|
@@ -86,8 +86,9 @@ const items = computed<DropdownMenuItem[][]>(() => [
|
|
|
86
86
|
|
|
87
87
|
<template>
|
|
88
88
|
<UDropdownMenu :items="items" :content="{ side: 'top', align: 'start' }">
|
|
89
|
-
<
|
|
90
|
-
|
|
89
|
+
<UButton
|
|
90
|
+
color="neutral"
|
|
91
|
+
variant="ghost"
|
|
91
92
|
data-testid="appearance-switcher"
|
|
92
93
|
:aria-label="t('appearance.switcher')"
|
|
93
94
|
:title="collapsed ? `${t('appearance.switcher')}: ${summary}` : undefined"
|
|
@@ -102,7 +103,7 @@ const items = computed<DropdownMenuItem[][]>(() => [
|
|
|
102
103
|
<div class="truncate text-xs font-medium text-highlighted">{{ summary }}</div>
|
|
103
104
|
</div>
|
|
104
105
|
<UIcon v-if="!collapsed" name="i-lucide-chevron-up" class="h-4 w-4 shrink-0 text-dimmed" />
|
|
105
|
-
</
|
|
106
|
+
</UButton>
|
|
106
107
|
</UDropdownMenu>
|
|
107
108
|
<ThemeImportModal v-model:open="importOpen" />
|
|
108
109
|
</template>
|
|
@@ -257,8 +257,9 @@ async function submitPrompt() {
|
|
|
257
257
|
:content="{ align: 'start' }"
|
|
258
258
|
class="w-full"
|
|
259
259
|
>
|
|
260
|
-
<
|
|
261
|
-
|
|
260
|
+
<UButton
|
|
261
|
+
color="neutral"
|
|
262
|
+
variant="ghost"
|
|
262
263
|
class="flex w-full items-center gap-2 rounded-md px-1.5 py-1 text-start transition hover:bg-elevated/60"
|
|
263
264
|
:disabled="busy"
|
|
264
265
|
>
|
|
@@ -270,13 +271,14 @@ async function submitPrompt() {
|
|
|
270
271
|
{{ accounts.activeAccount?.name ?? t('layout.boardSwitcher.accountFallback') }}
|
|
271
272
|
</SectionLabel>
|
|
272
273
|
<UIcon name="i-lucide-chevrons-up-down" class="ms-auto h-3.5 w-3.5 shrink-0 text-app-600" />
|
|
273
|
-
</
|
|
274
|
+
</UButton>
|
|
274
275
|
</UDropdownMenu>
|
|
275
276
|
|
|
276
277
|
<!-- board selector -->
|
|
277
278
|
<UDropdownMenu :items="boardItems" :content="{ align: 'start' }" class="w-full">
|
|
278
|
-
<
|
|
279
|
-
|
|
279
|
+
<UButton
|
|
280
|
+
color="neutral"
|
|
281
|
+
variant="ghost"
|
|
280
282
|
:title="
|
|
281
283
|
collapsed
|
|
282
284
|
? (workspace.activeWorkspace?.name ?? t('layout.boardSwitcher.boardFallback'))
|
|
@@ -297,13 +299,13 @@ async function submitPrompt() {
|
|
|
297
299
|
name="i-lucide-chevron-down"
|
|
298
300
|
class="ms-auto h-4 w-4 shrink-0 text-dimmed"
|
|
299
301
|
/>
|
|
300
|
-
</
|
|
302
|
+
</UButton>
|
|
301
303
|
</UDropdownMenu>
|
|
302
304
|
|
|
303
305
|
<!-- create / rename prompt -->
|
|
304
306
|
<UModal v-model:open="promptOpen" :title="prompt ? promptMeta[prompt].title : ''">
|
|
305
307
|
<template #body>
|
|
306
|
-
<
|
|
308
|
+
<UForm class="space-y-3" @submit="submitPrompt">
|
|
307
309
|
<UFormField :label="t('layout.boardSwitcher.prompt.nameLabel')">
|
|
308
310
|
<UInput
|
|
309
311
|
v-model="promptValue"
|
|
@@ -341,7 +343,7 @@ async function submitPrompt() {
|
|
|
341
343
|
{{ prompt ? promptMeta[prompt].cta : '' }}
|
|
342
344
|
</UButton>
|
|
343
345
|
</div>
|
|
344
|
-
</
|
|
346
|
+
</UForm>
|
|
345
347
|
</template>
|
|
346
348
|
</UModal>
|
|
347
349
|
</div>
|
|
@@ -151,9 +151,10 @@ const decisionItems = computed(() =>
|
|
|
151
151
|
/>
|
|
152
152
|
<!-- Click the readout to snap back to 100%. Always visible (only the LOD sub-label
|
|
153
153
|
drops on narrow viewports) so the zoom level is never a mystery. -->
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
<UButton
|
|
155
|
+
color="neutral"
|
|
156
|
+
variant="ghost"
|
|
157
|
+
class="flex w-16 flex-col items-center gap-0 rounded-sm p-0 text-center text-xs tabular-nums text-toned hover:bg-elevated sm:w-20"
|
|
157
158
|
:title="t('board.toolbar.resetZoom')"
|
|
158
159
|
:aria-label="t('board.toolbar.resetZoom')"
|
|
159
160
|
data-testid="board-zoom-reset"
|
|
@@ -167,7 +168,7 @@ const decisionItems = computed(() =>
|
|
|
167
168
|
<span class="hidden text-3xs uppercase tracking-wide text-dimmed sm:block">{{
|
|
168
169
|
lodLabel
|
|
169
170
|
}}</span>
|
|
170
|
-
</
|
|
171
|
+
</UButton>
|
|
171
172
|
<IconButton
|
|
172
173
|
:label="t('board.toolbar.zoomIn')"
|
|
173
174
|
icon="i-lucide-zoom-in"
|
|
@@ -281,10 +281,11 @@ function indexOf(cmd: Command) {
|
|
|
281
281
|
<SectionLabel as="p" class="px-2 pb-1 pt-2">
|
|
282
282
|
{{ group.name }}
|
|
283
283
|
</SectionLabel>
|
|
284
|
-
<
|
|
284
|
+
<UButton
|
|
285
|
+
color="neutral"
|
|
286
|
+
variant="ghost"
|
|
285
287
|
v-for="cmd in group.items"
|
|
286
288
|
:key="cmd.id"
|
|
287
|
-
type="button"
|
|
288
289
|
:data-testid="`command-${cmd.id}`"
|
|
289
290
|
class="flex w-full items-center gap-2.5 rounded-md px-2 py-1.5 text-start text-sm transition"
|
|
290
291
|
:class="
|
|
@@ -297,7 +298,7 @@ function indexOf(cmd: Command) {
|
|
|
297
298
|
>
|
|
298
299
|
<UIcon :name="cmd.icon" class="h-4 w-4 shrink-0 text-muted" />
|
|
299
300
|
<span class="truncate">{{ cmd.label }}</span>
|
|
300
|
-
</
|
|
301
|
+
</UButton>
|
|
301
302
|
</div>
|
|
302
303
|
</div>
|
|
303
304
|
</div>
|
|
@@ -90,14 +90,15 @@ function openConfig(event: MouseEvent) {
|
|
|
90
90
|
{{ t('layout.defaultTestEnvBanner.action') }}
|
|
91
91
|
</UButton>
|
|
92
92
|
<!-- The URL itself, shown so it can be read and copied, not just clicked. -->
|
|
93
|
-
<
|
|
94
|
-
|
|
93
|
+
<ULink
|
|
94
|
+
raw
|
|
95
|
+
:to="configUrl"
|
|
95
96
|
class="min-w-0 truncate font-mono text-2xs text-app-info-300/80 underline decoration-dotted underline-offset-2 hover:text-app-info-200"
|
|
96
97
|
data-testid="default-test-env-url"
|
|
97
98
|
@click="openConfig"
|
|
98
99
|
>
|
|
99
100
|
{{ configUrl }}
|
|
100
|
-
</
|
|
101
|
+
</ULink>
|
|
101
102
|
</div>
|
|
102
103
|
</div>
|
|
103
104
|
</div>
|
|
@@ -427,10 +427,11 @@ const filteredGroups = computed<IntegrationGroup[]>(() => {
|
|
|
427
427
|
{{ group.title }}
|
|
428
428
|
</SectionLabel>
|
|
429
429
|
<div class="space-y-1.5">
|
|
430
|
-
<
|
|
430
|
+
<UButton
|
|
431
|
+
color="neutral"
|
|
432
|
+
variant="ghost"
|
|
431
433
|
v-for="item in group.items"
|
|
432
434
|
:key="item.key"
|
|
433
|
-
type="button"
|
|
434
435
|
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"
|
|
435
436
|
@click="item.onClick()"
|
|
436
437
|
>
|
|
@@ -462,13 +463,14 @@ const filteredGroups = computed<IntegrationGroup[]>(() => {
|
|
|
462
463
|
name="i-lucide-chevron-right"
|
|
463
464
|
class="h-4 w-4 shrink-0 text-dimmed rtl:-scale-x-100"
|
|
464
465
|
/>
|
|
465
|
-
</
|
|
466
|
+
</UButton>
|
|
466
467
|
</div>
|
|
467
468
|
|
|
468
469
|
<!-- De-emphasised workspace-config link (e.g. issue tracker settings). -->
|
|
469
|
-
<
|
|
470
|
+
<UButton
|
|
471
|
+
color="neutral"
|
|
472
|
+
variant="ghost"
|
|
470
473
|
v-if="group.footerLink"
|
|
471
|
-
type="button"
|
|
472
474
|
class="mt-1.5 flex w-full items-center gap-2 rounded-md px-3 py-1.5 text-start text-xs text-muted transition hover:bg-default/60 hover:text-default"
|
|
473
475
|
@click="group.footerLink.onClick()"
|
|
474
476
|
>
|
|
@@ -481,7 +483,7 @@ const filteredGroups = computed<IntegrationGroup[]>(() => {
|
|
|
481
483
|
name="i-lucide-chevron-right"
|
|
482
484
|
class="h-3.5 w-3.5 shrink-0 text-app-600 rtl:-scale-x-100"
|
|
483
485
|
/>
|
|
484
|
-
</
|
|
486
|
+
</UButton>
|
|
485
487
|
</section>
|
|
486
488
|
</div>
|
|
487
489
|
</template>
|
|
@@ -40,8 +40,9 @@ const items = computed<DropdownMenuItem[][]>(() => [
|
|
|
40
40
|
|
|
41
41
|
<template>
|
|
42
42
|
<UDropdownMenu :items="items" :content="{ side: 'top', align: 'start' }">
|
|
43
|
-
<
|
|
44
|
-
|
|
43
|
+
<UButton
|
|
44
|
+
color="neutral"
|
|
45
|
+
variant="ghost"
|
|
45
46
|
data-testid="language-switcher"
|
|
46
47
|
:aria-label="t('language.switcher')"
|
|
47
48
|
:title="collapsed ? `${t('language.switcher')}: ${current}` : undefined"
|
|
@@ -56,6 +57,6 @@ const items = computed<DropdownMenuItem[][]>(() => [
|
|
|
56
57
|
<div class="truncate text-xs font-medium text-highlighted">{{ current }}</div>
|
|
57
58
|
</div>
|
|
58
59
|
<UIcon v-if="!collapsed" name="i-lucide-chevron-up" class="h-4 w-4 shrink-0 text-dimmed" />
|
|
59
|
-
</
|
|
60
|
+
</UButton>
|
|
60
61
|
</UDropdownMenu>
|
|
61
62
|
</template>
|
|
@@ -206,10 +206,11 @@ const filteredGroups = computed<ProviderGroup[]>(() => {
|
|
|
206
206
|
{{ group.title }}
|
|
207
207
|
</SectionLabel>
|
|
208
208
|
<div class="space-y-1.5">
|
|
209
|
-
<
|
|
209
|
+
<UButton
|
|
210
|
+
color="neutral"
|
|
211
|
+
variant="ghost"
|
|
210
212
|
v-for="item in group.items"
|
|
211
213
|
:key="item.key"
|
|
212
|
-
type="button"
|
|
213
214
|
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"
|
|
214
215
|
:data-testid="`model-provider-${item.key}`"
|
|
215
216
|
@click="item.onClick()"
|
|
@@ -239,7 +240,7 @@ const filteredGroups = computed<ProviderGroup[]>(() => {
|
|
|
239
240
|
name="i-lucide-chevron-right"
|
|
240
241
|
class="h-4 w-4 shrink-0 text-dimmed rtl:-scale-x-100"
|
|
241
242
|
/>
|
|
242
|
-
</
|
|
243
|
+
</UButton>
|
|
243
244
|
</div>
|
|
244
245
|
<p v-if="group.note" class="mt-1.5 px-1 text-2xs text-dimmed">{{ group.note }}</p>
|
|
245
246
|
</section>
|
|
@@ -429,13 +429,15 @@ function revealDecision(n: Notification) {
|
|
|
429
429
|
/>
|
|
430
430
|
<div class="min-w-0 flex-1">
|
|
431
431
|
<div class="flex items-center gap-1.5">
|
|
432
|
-
<
|
|
433
|
-
|
|
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"
|
|
434
436
|
:title="n.title"
|
|
435
437
|
@click="reveal(n)"
|
|
436
438
|
>
|
|
437
439
|
{{ n.title }}
|
|
438
|
-
</
|
|
440
|
+
</UButton>
|
|
439
441
|
<span
|
|
440
442
|
v-if="isUrgent(n)"
|
|
441
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"
|
|
@@ -444,16 +446,17 @@ function revealDecision(n: Notification) {
|
|
|
444
446
|
</span>
|
|
445
447
|
</div>
|
|
446
448
|
<p class="mt-0.5 text-2xs leading-snug text-muted">{{ n.body }}</p>
|
|
447
|
-
<
|
|
449
|
+
<ULink
|
|
450
|
+
raw
|
|
448
451
|
v-if="n.payload?.prUrl"
|
|
449
|
-
:
|
|
452
|
+
:to="n.payload.prUrl"
|
|
450
453
|
target="_blank"
|
|
451
454
|
rel="noopener"
|
|
452
455
|
class="mt-1 inline-flex items-center gap-1 text-2xs text-app-info-400 hover:underline"
|
|
453
456
|
>
|
|
454
457
|
<UIcon name="i-lucide-external-link" class="h-3 w-3" />
|
|
455
458
|
{{ t('layout.notifications.openPr') }}
|
|
456
|
-
</
|
|
459
|
+
</ULink>
|
|
457
460
|
<!--
|
|
458
461
|
A platform-health card deep-links to the runs it aggregated, so the operator
|
|
459
462
|
lands on the evidence rather than only on the dashboard.
|
|
@@ -463,30 +466,37 @@ function revealDecision(n: Notification) {
|
|
|
463
466
|
class="mt-1.5 flex flex-col gap-0.5"
|
|
464
467
|
data-testid="notification-failing-runs"
|
|
465
468
|
>
|
|
466
|
-
<
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
<span
|
|
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>
|
|
490
500
|
<span v-if="failingRunsOmitted(n) > 0" class="text-2xs text-dimmed">
|
|
491
501
|
{{ t('layout.notifications.failingRunsMore', { count: failingRunsOmitted(n) }) }}
|
|
492
502
|
</span>
|
|
@@ -123,10 +123,11 @@ const groups = computed<PersonalGroup[]>(() => {
|
|
|
123
123
|
{{ group.title }}
|
|
124
124
|
</SectionLabel>
|
|
125
125
|
<div class="space-y-1.5">
|
|
126
|
-
<
|
|
126
|
+
<UButton
|
|
127
|
+
color="neutral"
|
|
128
|
+
variant="ghost"
|
|
127
129
|
v-for="item in group.items"
|
|
128
130
|
:key="item.key"
|
|
129
|
-
type="button"
|
|
130
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"
|
|
131
132
|
@click="item.onClick()"
|
|
132
133
|
>
|
|
@@ -147,7 +148,7 @@ const groups = computed<PersonalGroup[]>(() => {
|
|
|
147
148
|
name="i-lucide-chevron-right"
|
|
148
149
|
class="h-4 w-4 shrink-0 text-dimmed rtl:-scale-x-100"
|
|
149
150
|
/>
|
|
150
|
-
</
|
|
151
|
+
</UButton>
|
|
151
152
|
</div>
|
|
152
153
|
</section>
|
|
153
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,7 +53,7 @@ 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. -->
|
|
@@ -226,8 +226,9 @@ watch(
|
|
|
226
226
|
<div class="contents" @click="onNavAction">
|
|
227
227
|
<!-- Command bar launcher (⌘K) — the primary way to create blocks / pipelines
|
|
228
228
|
and reach every action below. -->
|
|
229
|
-
<
|
|
230
|
-
|
|
229
|
+
<UButton
|
|
230
|
+
color="neutral"
|
|
231
|
+
variant="ghost"
|
|
231
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"
|
|
232
233
|
:class="railed ? 'justify-center px-0' : 'px-2.5'"
|
|
233
234
|
:aria-label="t('nav.commandBar')"
|
|
@@ -238,7 +239,7 @@ watch(
|
|
|
238
239
|
<UIcon name="i-lucide-search" class="h-4 w-4 shrink-0" />
|
|
239
240
|
<span v-if="!railed" class="flex-1 truncate">{{ t('nav.commandBar') }}</span>
|
|
240
241
|
<UKbd v-if="!railed" value="⌘K" />
|
|
241
|
-
</
|
|
242
|
+
</UButton>
|
|
242
243
|
|
|
243
244
|
<!-- Sections + items come from the shared nav manifest, already gated by the
|
|
244
245
|
reactive slotFilter (backend/docs/adr/0049-modular-vue-adoption.md, slice 1) — which
|
|
@@ -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>
|
|
@@ -67,9 +67,10 @@ const pinnedTitle = computed(
|
|
|
67
67
|
<!-- Rail: one button, flips the tier. The label rides under the glyph so the rail still says
|
|
68
68
|
which tier is on — the audience that needs to find the advanced half is exactly the one
|
|
69
69
|
sitting in basic mode's collapsed default. -->
|
|
70
|
-
<
|
|
70
|
+
<UButton
|
|
71
|
+
color="neutral"
|
|
72
|
+
variant="ghost"
|
|
71
73
|
v-else-if="collapsed"
|
|
72
|
-
type="button"
|
|
73
74
|
data-testid="ui-mode-toggle"
|
|
74
75
|
:aria-label="t('uiMode.switchTo', { mode: t(MODE_LABELS[otherMode]) })"
|
|
75
76
|
:title="t('uiMode.switchTo', { mode: t(MODE_LABELS[otherMode]) })"
|
|
@@ -80,7 +81,7 @@ const pinnedTitle = computed(
|
|
|
80
81
|
<span class="w-full truncate text-center text-3xs font-medium uppercase text-toned">
|
|
81
82
|
{{ currentLabel }}
|
|
82
83
|
</span>
|
|
83
|
-
</
|
|
84
|
+
</UButton>
|
|
84
85
|
|
|
85
86
|
<div v-else data-testid="ui-mode-switcher" class="w-full">
|
|
86
87
|
<SectionLabel class="mb-1 px-1">
|
|
@@ -91,10 +92,11 @@ const pinnedTitle = computed(
|
|
|
91
92
|
:aria-label="t('uiMode.switcher')"
|
|
92
93
|
class="flex w-full gap-1 rounded-lg border border-muted bg-default/60 p-1"
|
|
93
94
|
>
|
|
94
|
-
<
|
|
95
|
+
<UButton
|
|
96
|
+
color="neutral"
|
|
97
|
+
variant="ghost"
|
|
95
98
|
v-for="mode in UI_MODES"
|
|
96
99
|
:key="mode"
|
|
97
|
-
type="button"
|
|
98
100
|
:data-testid="`ui-mode-option-${mode}`"
|
|
99
101
|
:aria-pressed="mode === uiMode.mode"
|
|
100
102
|
class="flex-1 truncate rounded-md px-2 py-1 text-xs font-medium transition"
|
|
@@ -106,7 +108,7 @@ const pinnedTitle = computed(
|
|
|
106
108
|
@click="uiMode.setMode(mode)"
|
|
107
109
|
>
|
|
108
110
|
{{ t(MODE_LABELS[mode]) }}
|
|
109
|
-
</
|
|
111
|
+
</UButton>
|
|
110
112
|
</div>
|
|
111
113
|
</div>
|
|
112
114
|
|
|
@@ -38,9 +38,10 @@ 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}`"
|
|
@@ -50,11 +51,12 @@ const items = computed(() =>
|
|
|
50
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)"
|
|
@@ -68,6 +70,6 @@ const items = computed(() =>
|
|
|
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>
|