@cat-factory/app 0.6.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/LICENSE +21 -0
- package/README.md +88 -0
- package/app/app.config.ts +8 -0
- package/app/app.vue +11 -0
- package/app/assets/css/main.css +100 -0
- package/app/components/auth/AuthGate.vue +24 -0
- package/app/components/auth/LoginScreen.vue +143 -0
- package/app/components/auth/UserMenu.vue +39 -0
- package/app/components/board/AddTaskModal.vue +444 -0
- package/app/components/board/AgentFailureCard.vue +97 -0
- package/app/components/board/AgentStopButton.vue +61 -0
- package/app/components/board/BoardCanvas.vue +183 -0
- package/app/components/board/ContextPicker.vue +367 -0
- package/app/components/board/RecurringPipelineModal.vue +219 -0
- package/app/components/board/TaskDependencyEdges.vue +132 -0
- package/app/components/board/nodes/AgentChip.vue +59 -0
- package/app/components/board/nodes/BlockNode.vue +433 -0
- package/app/components/board/nodes/DecisionBadge.vue +27 -0
- package/app/components/board/nodes/DraggableTask.vue +48 -0
- package/app/components/board/nodes/ModuleFrame.vue +97 -0
- package/app/components/board/nodes/TaskCard.vue +359 -0
- package/app/components/board/nodes/TaskPipelineMini.vue +159 -0
- package/app/components/bootstrap/BootstrapModal.vue +665 -0
- package/app/components/clarity/ClarityReviewWindow.vue +611 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +210 -0
- package/app/components/documents/DocumentImportModal.vue +161 -0
- package/app/components/documents/DocumentSourceConnectModal.vue +127 -0
- package/app/components/documents/SpawnPreviewModal.vue +161 -0
- package/app/components/documents/TaskContextDocs.vue +83 -0
- package/app/components/focus/BlockFocusView.vue +171 -0
- package/app/components/fragments/FragmentLibraryPanel.vue +340 -0
- package/app/components/gates/GateResultView.vue +282 -0
- package/app/components/github/AddServiceFromRepoModal.vue +354 -0
- package/app/components/github/GitHubConnect.vue +183 -0
- package/app/components/github/GitHubOnboarding.vue +45 -0
- package/app/components/github/GitHubPanel.vue +584 -0
- package/app/components/github/RepoTreeBrowser.vue +171 -0
- package/app/components/layout/AccountTeamSettings.vue +237 -0
- package/app/components/layout/BoardSwitcher.vue +280 -0
- package/app/components/layout/BoardToolbar.vue +156 -0
- package/app/components/layout/CommandBar.vue +336 -0
- package/app/components/layout/GitHubPatBanner.vue +73 -0
- package/app/components/layout/NotificationsInbox.vue +175 -0
- package/app/components/layout/SideBar.vue +314 -0
- package/app/components/layout/SpendWarningBanner.vue +107 -0
- package/app/components/observability/StepMetricsBar.vue +102 -0
- package/app/components/palettes/AgentPalette.vue +86 -0
- package/app/components/panels/AgentStepDetail.vue +737 -0
- package/app/components/panels/DecisionModal.vue +71 -0
- package/app/components/panels/InspectorPanel.vue +465 -0
- package/app/components/panels/ObservabilityPanel.vue +351 -0
- package/app/components/panels/StepMetadataCard.vue +253 -0
- package/app/components/panels/StepRestartControl.vue +90 -0
- package/app/components/panels/StepResultViewHost.vue +40 -0
- package/app/components/panels/StepTestReport.vue +84 -0
- package/app/components/panels/inspector/ContainerSummary.vue +74 -0
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -0
- package/app/components/panels/inspector/ServiceFragments.vue +82 -0
- package/app/components/panels/inspector/ServiceTestConfig.vue +198 -0
- package/app/components/panels/inspector/TaskAgentConfig.vue +81 -0
- package/app/components/panels/inspector/TaskDependencies.vue +70 -0
- package/app/components/panels/inspector/TaskEstimateBadge.vue +56 -0
- package/app/components/panels/inspector/TaskExecution.vue +364 -0
- package/app/components/panels/inspector/TaskRunSettings.vue +187 -0
- package/app/components/panels/inspector/TaskStructure.vue +96 -0
- package/app/components/pipeline/AgentKindIcon.vue +30 -0
- package/app/components/pipeline/IterationCapPrompt.vue +70 -0
- package/app/components/pipeline/PipelineBuilder.vue +817 -0
- package/app/components/pipeline/PipelineProgress.vue +484 -0
- package/app/components/providers/ApiKeysSection.vue +273 -0
- package/app/components/providers/PersonalCredentialModal.vue +128 -0
- package/app/components/providers/PersonalSubscriptionSection.vue +225 -0
- package/app/components/providers/VendorCredentialsModal.vue +197 -0
- package/app/components/recurring/RecurrenceEditor.vue +124 -0
- package/app/components/requirements/RequirementsReviewWindow.vue +620 -0
- package/app/components/settings/DatadogPanel.vue +213 -0
- package/app/components/settings/LocalModelEndpointsPanel.vue +286 -0
- package/app/components/settings/MergeThresholdsPanel.vue +378 -0
- package/app/components/settings/ModelDefaultsPanel.vue +250 -0
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +124 -0
- package/app/components/settings/WorkspaceSettingsPanel.vue +142 -0
- package/app/components/slack/SlackPanel.vue +299 -0
- package/app/components/tasks/TaskContextIssues.vue +88 -0
- package/app/components/tasks/TaskImportModal.vue +207 -0
- package/app/components/tasks/TaskSourceConnectModal.vue +133 -0
- package/app/components/testing/TestReportWindow.vue +404 -0
- package/app/composables/api/accounts.ts +81 -0
- package/app/composables/api/auth.ts +45 -0
- package/app/composables/api/board.ts +101 -0
- package/app/composables/api/bootstrap.ts +62 -0
- package/app/composables/api/context.ts +25 -0
- package/app/composables/api/documents.ts +74 -0
- package/app/composables/api/execution.ts +127 -0
- package/app/composables/api/fragments.ts +71 -0
- package/app/composables/api/github.ts +131 -0
- package/app/composables/api/models.ts +127 -0
- package/app/composables/api/notifications.ts +23 -0
- package/app/composables/api/presets.ts +29 -0
- package/app/composables/api/recurring.ts +68 -0
- package/app/composables/api/releaseHealth.ts +43 -0
- package/app/composables/api/reviews.ts +146 -0
- package/app/composables/api/slack.ts +54 -0
- package/app/composables/api/tasks.ts +72 -0
- package/app/composables/api/workspaces.ts +36 -0
- package/app/composables/useApi.ts +89 -0
- package/app/composables/useBlockDrag.ts +90 -0
- package/app/composables/useBlockQueries.ts +154 -0
- package/app/composables/useBoardFlow.ts +11 -0
- package/app/composables/useContextLinking.ts +65 -0
- package/app/composables/useDepLabels.ts +26 -0
- package/app/composables/useFrameResize.ts +54 -0
- package/app/composables/useResultView.ts +48 -0
- package/app/composables/useReviewStage.ts +40 -0
- package/app/composables/useSemanticZoom.ts +31 -0
- package/app/composables/useStepApproval.ts +233 -0
- package/app/composables/useStepProse.ts +78 -0
- package/app/composables/useStepTimer.ts +63 -0
- package/app/composables/useTaskExpansion.ts +92 -0
- package/app/composables/useWorkspaceStream.ts +155 -0
- package/app/docs/architecture.md +31 -0
- package/app/pages/index.vue +141 -0
- package/app/stores/accounts.ts +152 -0
- package/app/stores/agentConfig.ts +35 -0
- package/app/stores/agentRuns.ts +122 -0
- package/app/stores/agents.ts +40 -0
- package/app/stores/apiKeys.ts +108 -0
- package/app/stores/auth.ts +166 -0
- package/app/stores/board.spec.ts +205 -0
- package/app/stores/board.ts +286 -0
- package/app/stores/bootstrap.ts +97 -0
- package/app/stores/clarity.ts +196 -0
- package/app/stores/consensus.ts +60 -0
- package/app/stores/documents.ts +176 -0
- package/app/stores/execution.ts +273 -0
- package/app/stores/fragmentLibrary.ts +147 -0
- package/app/stores/fragments.ts +40 -0
- package/app/stores/github.ts +305 -0
- package/app/stores/localModels.ts +51 -0
- package/app/stores/mergePresets.ts +58 -0
- package/app/stores/modelDefaults.ts +76 -0
- package/app/stores/models.ts +134 -0
- package/app/stores/notifications.ts +70 -0
- package/app/stores/observability.ts +144 -0
- package/app/stores/personalSubscriptions.ts +215 -0
- package/app/stores/pipelines.ts +327 -0
- package/app/stores/recurringPipelines.ts +112 -0
- package/app/stores/releaseHealth.ts +75 -0
- package/app/stores/requirements.spec.ts +94 -0
- package/app/stores/requirements.ts +208 -0
- package/app/stores/serviceFragmentDefaults.ts +29 -0
- package/app/stores/services.ts +87 -0
- package/app/stores/slack.ts +142 -0
- package/app/stores/taskExpansion.ts +36 -0
- package/app/stores/tasks.spec.ts +71 -0
- package/app/stores/tasks.ts +176 -0
- package/app/stores/tracker.ts +27 -0
- package/app/stores/ui.ts +434 -0
- package/app/stores/vendorCredentials.ts +54 -0
- package/app/stores/workspace.ts +215 -0
- package/app/stores/workspaceSettings.ts +36 -0
- package/app/types/accounts.ts +77 -0
- package/app/types/bootstrap.ts +83 -0
- package/app/types/clarity.ts +59 -0
- package/app/types/consensus.ts +91 -0
- package/app/types/documents.ts +104 -0
- package/app/types/domain.ts +495 -0
- package/app/types/execution.ts +383 -0
- package/app/types/fragments.ts +72 -0
- package/app/types/github.ts +173 -0
- package/app/types/localModels.ts +73 -0
- package/app/types/merge.ts +71 -0
- package/app/types/models.ts +157 -0
- package/app/types/notifications.ts +74 -0
- package/app/types/recurring.ts +69 -0
- package/app/types/releaseHealth.ts +31 -0
- package/app/types/requirements.ts +61 -0
- package/app/types/services.ts +27 -0
- package/app/types/slack.ts +57 -0
- package/app/types/tasks.ts +82 -0
- package/app/types/tracker.ts +18 -0
- package/app/utils/agentOutput.spec.ts +128 -0
- package/app/utils/agentOutput.ts +173 -0
- package/app/utils/catalog.spec.ts +112 -0
- package/app/utils/catalog.ts +455 -0
- package/app/utils/dnd.ts +29 -0
- package/app/utils/observability.ts +52 -0
- package/app/utils/pipelineRender.ts +151 -0
- package/nuxt.config.ts +55 -0
- package/package.json +45 -0
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { ref, computed, watch, onMounted } from 'vue'
|
|
3
|
+
import { onKeyStroke } from '@vueuse/core'
|
|
4
|
+
import type { IterationCapChoice } from '~/types/execution'
|
|
5
|
+
import { agentKindMeta } from '~/utils/catalog'
|
|
6
|
+
import StepRestartControl from '~/components/panels/StepRestartControl.vue'
|
|
7
|
+
import StepMetadataCard from '~/components/panels/StepMetadataCard.vue'
|
|
8
|
+
import StepTestReport from '~/components/panels/StepTestReport.vue'
|
|
9
|
+
import { useStepTimer } from '~/composables/useStepTimer'
|
|
10
|
+
import { useStepProse } from '~/composables/useStepProse'
|
|
11
|
+
import { useStepApproval } from '~/composables/useStepApproval'
|
|
12
|
+
|
|
13
|
+
// Detail overlay for a single pipeline step. Opened by clicking an agent in the
|
|
14
|
+
// inspector list (TaskExecution) or the focus-view pipeline (PipelineProgress) via
|
|
15
|
+
// `ui.openStepDetail(instanceId, stepIndex)`. It resolves the step from the
|
|
16
|
+
// execution store so it stays live while open, and shows the step's metadata
|
|
17
|
+
// (state, timing, model, subtasks, fragments, decision/approval). When the agent
|
|
18
|
+
// produced prose (architect, researcher, reviewer, …) it also renders that output
|
|
19
|
+
// as markdown, split into collapsible sections with an auto-generated ToC sidebar.
|
|
20
|
+
// This component is orchestration only: the metadata card + the tester report are
|
|
21
|
+
// child components, and the live clock / prose reader / approval-review state machine
|
|
22
|
+
// live in the `useStepTimer` / `useStepProse` / `useStepApproval` composables.
|
|
23
|
+
const ui = useUiStore()
|
|
24
|
+
const execution = useExecutionStore()
|
|
25
|
+
const board = useBoardStore()
|
|
26
|
+
const models = useModelsStore()
|
|
27
|
+
const workspace = useWorkspaceStore()
|
|
28
|
+
|
|
29
|
+
onMounted(() => models.ensureLoaded(workspace.workspaceId ?? undefined))
|
|
30
|
+
|
|
31
|
+
const ctx = computed(() => ui.stepDetail)
|
|
32
|
+
const instance = computed(() => execution.getInstance(ctx.value?.instanceId))
|
|
33
|
+
const step = computed(() =>
|
|
34
|
+
ctx.value ? (instance.value?.steps[ctx.value.stepIndex] ?? null) : null,
|
|
35
|
+
)
|
|
36
|
+
const block = computed(() => (instance.value ? board.getBlock(instance.value.blockId) : undefined))
|
|
37
|
+
const agent = computed(() => (step.value ? agentKindMeta(step.value.agentKind) : null))
|
|
38
|
+
const open = computed(() => !!ctx.value && !!step.value)
|
|
39
|
+
|
|
40
|
+
const stepNumber = computed(() => (ctx.value ? ctx.value.stepIndex + 1 : 0))
|
|
41
|
+
const totalSteps = computed(() => instance.value?.steps.length ?? 0)
|
|
42
|
+
|
|
43
|
+
// Companion verdicts for a companion step: the full sequence of correction cycles.
|
|
44
|
+
const companionVerdicts = computed(() => step.value?.companion?.verdicts ?? [])
|
|
45
|
+
const latestVerdict = computed(() => companionVerdicts.value.at(-1) ?? null)
|
|
46
|
+
const pctOf = (n: number) => `${Math.round(n * 100)}%`
|
|
47
|
+
|
|
48
|
+
// A tester step's latest structured report (what was tested, outcomes, concerns,
|
|
49
|
+
// greenlight) + its loop phase/attempts, surfaced when this is a `tester` step.
|
|
50
|
+
const testReport = computed(() => step.value?.test?.lastReport ?? null)
|
|
51
|
+
const testPhase = computed(() => step.value?.test ?? null)
|
|
52
|
+
|
|
53
|
+
// A failed run is no longer executing: a step left mid-flight (state still
|
|
54
|
+
// `working`, no `finishedAt`) must stop looking live — no ticking clock, no
|
|
55
|
+
// "spinning up" phase, no spinner.
|
|
56
|
+
const runFailed = computed(() => instance.value?.status === 'failed')
|
|
57
|
+
|
|
58
|
+
// Live elapsed-time clock for the open step.
|
|
59
|
+
const { isRunning, durationLabel } = useStepTimer({
|
|
60
|
+
step: () => step.value,
|
|
61
|
+
runFailed: () => runFailed.value,
|
|
62
|
+
failureAt: () => instance.value?.failure?.occurredAt,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// The prose reader: heading outline, collapse state, scroll-spy + scroll refs.
|
|
66
|
+
const prose = useStepProse(() => step.value?.output ?? '')
|
|
67
|
+
const {
|
|
68
|
+
outline,
|
|
69
|
+
tocSections,
|
|
70
|
+
hasOutput,
|
|
71
|
+
collapsed,
|
|
72
|
+
activeId,
|
|
73
|
+
scrollEl,
|
|
74
|
+
sectionEls,
|
|
75
|
+
toggle,
|
|
76
|
+
setAll,
|
|
77
|
+
allCollapsed,
|
|
78
|
+
goTo,
|
|
79
|
+
onScroll,
|
|
80
|
+
} = prose
|
|
81
|
+
|
|
82
|
+
const approvalPending = computed(() => step.value?.approval?.status === 'pending')
|
|
83
|
+
const approvalId = computed(() => step.value?.approval?.id ?? null)
|
|
84
|
+
// A companion step parked at its automatic-rework cap: instead of the generic
|
|
85
|
+
// approve/request-changes/reject rail, it shows the shared iteration-cap prompt
|
|
86
|
+
// (one more round / proceed / stop & reset), resolved through its own endpoint.
|
|
87
|
+
const companionExceeded = computed(() => approvalPending.value && !!step.value?.companion?.exceeded)
|
|
88
|
+
|
|
89
|
+
function close() {
|
|
90
|
+
// Reset the approval-mode sub-states so reopening the same step is clean
|
|
91
|
+
// (the step-change watch only fires when the step key actually changes).
|
|
92
|
+
approval.resetForClose()
|
|
93
|
+
ui.closeStepDetail()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The GitHub-style approval/review state machine for a pending gate step.
|
|
97
|
+
const approval = useStepApproval({
|
|
98
|
+
step: () => step.value,
|
|
99
|
+
scrollEl: () => scrollEl.value,
|
|
100
|
+
instanceId: () => ctx.value?.instanceId,
|
|
101
|
+
approvalId: () => approvalId.value,
|
|
102
|
+
approvalPending: () => approvalPending.value,
|
|
103
|
+
companionExceeded: () => companionExceeded.value,
|
|
104
|
+
close,
|
|
105
|
+
})
|
|
106
|
+
const {
|
|
107
|
+
reviewComments,
|
|
108
|
+
feedback,
|
|
109
|
+
submitting,
|
|
110
|
+
draftTarget,
|
|
111
|
+
draftBody,
|
|
112
|
+
editing,
|
|
113
|
+
draftProposal,
|
|
114
|
+
rejectArmed,
|
|
115
|
+
canRequestChanges,
|
|
116
|
+
onProseClick,
|
|
117
|
+
addDraftComment,
|
|
118
|
+
cancelDraft,
|
|
119
|
+
removeComment,
|
|
120
|
+
approve,
|
|
121
|
+
startEditing,
|
|
122
|
+
cancelEditing,
|
|
123
|
+
approveWithEdits,
|
|
124
|
+
requestChanges,
|
|
125
|
+
armReject,
|
|
126
|
+
disarmReject,
|
|
127
|
+
reject,
|
|
128
|
+
} = approval
|
|
129
|
+
|
|
130
|
+
const resolvingCap = ref(false)
|
|
131
|
+
async function resolveCompanionCap(choice: IterationCapChoice) {
|
|
132
|
+
if (!ctx.value || !approvalId.value || resolvingCap.value) return
|
|
133
|
+
resolvingCap.value = true
|
|
134
|
+
try {
|
|
135
|
+
await execution.resolveCompanionExceeded(ctx.value.instanceId, approvalId.value, choice)
|
|
136
|
+
close()
|
|
137
|
+
} finally {
|
|
138
|
+
resolvingCap.value = false
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Re-seed the reader (all sections expanded, scrolled to top) + reset the review
|
|
143
|
+
// drafts whenever a different step opens.
|
|
144
|
+
watch(
|
|
145
|
+
() => ctx.value && `${ctx.value.instanceId}:${ctx.value.stepIndex}`,
|
|
146
|
+
() => {
|
|
147
|
+
prose.reset()
|
|
148
|
+
approval.resetForStep()
|
|
149
|
+
},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
onKeyStroke('Escape', () => {
|
|
153
|
+
if (open.value) close()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
async function copyOutput() {
|
|
157
|
+
if (step.value?.output) await navigator.clipboard?.writeText(step.value.output)
|
|
158
|
+
}
|
|
159
|
+
</script>
|
|
160
|
+
|
|
161
|
+
<template>
|
|
162
|
+
<Teleport to="body">
|
|
163
|
+
<Transition name="reader-fade">
|
|
164
|
+
<div
|
|
165
|
+
v-if="open && step && agent"
|
|
166
|
+
class="fixed inset-0 z-50 flex bg-slate-950/96 backdrop-blur-sm"
|
|
167
|
+
role="dialog"
|
|
168
|
+
aria-modal="true"
|
|
169
|
+
>
|
|
170
|
+
<!-- ToC sidebar (only meaningful when there are prose headings) -->
|
|
171
|
+
<aside
|
|
172
|
+
v-if="outline.hasToc"
|
|
173
|
+
class="hidden w-72 shrink-0 flex-col border-r border-slate-800 bg-slate-900/60 md:flex"
|
|
174
|
+
>
|
|
175
|
+
<div class="border-b border-slate-800 px-4 py-3">
|
|
176
|
+
<div class="text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
177
|
+
Contents
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
<nav class="flex-1 space-y-0.5 overflow-auto px-2 py-3">
|
|
181
|
+
<button
|
|
182
|
+
class="block w-full truncate rounded-md px-2 py-1 text-left text-[13px] transition"
|
|
183
|
+
:class="
|
|
184
|
+
activeId === 'step-details'
|
|
185
|
+
? 'bg-indigo-500/15 font-medium text-indigo-200'
|
|
186
|
+
: 'text-slate-400 hover:bg-slate-800/60 hover:text-slate-200'
|
|
187
|
+
"
|
|
188
|
+
@click="goTo('step-details')"
|
|
189
|
+
>
|
|
190
|
+
Details
|
|
191
|
+
</button>
|
|
192
|
+
<button
|
|
193
|
+
v-for="s in tocSections"
|
|
194
|
+
:key="s.id"
|
|
195
|
+
class="block w-full truncate rounded-md px-2 py-1 text-left text-[13px] transition"
|
|
196
|
+
:class="
|
|
197
|
+
activeId === s.id
|
|
198
|
+
? 'bg-indigo-500/15 font-medium text-indigo-200'
|
|
199
|
+
: 'text-slate-400 hover:bg-slate-800/60 hover:text-slate-200'
|
|
200
|
+
"
|
|
201
|
+
:style="{ paddingLeft: `${(s.depth - outline.minDepth) * 0.85 + 0.5}rem` }"
|
|
202
|
+
:title="s.title"
|
|
203
|
+
@click="goTo(s.id)"
|
|
204
|
+
>
|
|
205
|
+
{{ s.title }}
|
|
206
|
+
</button>
|
|
207
|
+
</nav>
|
|
208
|
+
</aside>
|
|
209
|
+
|
|
210
|
+
<!-- main column -->
|
|
211
|
+
<div class="flex min-w-0 flex-1 flex-col">
|
|
212
|
+
<header class="flex items-center gap-3 border-b border-slate-800 px-6 py-4">
|
|
213
|
+
<div
|
|
214
|
+
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg"
|
|
215
|
+
:style="{ backgroundColor: agent.color + '22' }"
|
|
216
|
+
>
|
|
217
|
+
<UIcon :name="agent.icon" class="h-5 w-5" :style="{ color: agent.color }" />
|
|
218
|
+
</div>
|
|
219
|
+
<div class="min-w-0">
|
|
220
|
+
<h1 class="truncate text-base font-semibold text-white">{{ agent.label }}</h1>
|
|
221
|
+
<p v-if="block" class="truncate text-xs text-slate-500">{{ block.title }}</p>
|
|
222
|
+
</div>
|
|
223
|
+
<div class="ml-auto flex items-center gap-1.5">
|
|
224
|
+
<UBadge
|
|
225
|
+
v-if="approvalPending && !companionExceeded"
|
|
226
|
+
color="warning"
|
|
227
|
+
variant="subtle"
|
|
228
|
+
size="sm"
|
|
229
|
+
class="mr-1"
|
|
230
|
+
>
|
|
231
|
+
<UIcon name="i-lucide-shield-check" class="mr-1 h-3 w-3" />
|
|
232
|
+
Approval required
|
|
233
|
+
</UBadge>
|
|
234
|
+
<UBadge
|
|
235
|
+
v-else-if="companionExceeded"
|
|
236
|
+
color="warning"
|
|
237
|
+
variant="subtle"
|
|
238
|
+
size="sm"
|
|
239
|
+
class="mr-1"
|
|
240
|
+
>
|
|
241
|
+
<UIcon name="i-lucide-alert-triangle" class="mr-1 h-3 w-3" />
|
|
242
|
+
Decision required
|
|
243
|
+
</UBadge>
|
|
244
|
+
<UButton
|
|
245
|
+
v-if="outline.sections.length"
|
|
246
|
+
:icon="allCollapsed ? 'i-lucide-unfold-vertical' : 'i-lucide-fold-vertical'"
|
|
247
|
+
color="neutral"
|
|
248
|
+
variant="ghost"
|
|
249
|
+
size="sm"
|
|
250
|
+
:title="allCollapsed ? 'Expand all sections' : 'Collapse all sections'"
|
|
251
|
+
@click="setAll(!allCollapsed)"
|
|
252
|
+
/>
|
|
253
|
+
<UButton
|
|
254
|
+
v-if="hasOutput"
|
|
255
|
+
icon="i-lucide-copy"
|
|
256
|
+
color="neutral"
|
|
257
|
+
variant="ghost"
|
|
258
|
+
size="sm"
|
|
259
|
+
title="Copy raw output"
|
|
260
|
+
@click="copyOutput"
|
|
261
|
+
/>
|
|
262
|
+
<!-- Restart the pipeline from this step (shared two-click confirm; resetting
|
|
263
|
+
later steps is destructive). Keyed on the step so its armed state resets
|
|
264
|
+
when a different step opens within this overlay. -->
|
|
265
|
+
<StepRestartControl
|
|
266
|
+
:key="`${ctx?.instanceId}:${ctx?.stepIndex}`"
|
|
267
|
+
:instance-id="ctx?.instanceId ?? null"
|
|
268
|
+
:step-index="ctx?.stepIndex ?? null"
|
|
269
|
+
@restarted="close"
|
|
270
|
+
/>
|
|
271
|
+
<UButton
|
|
272
|
+
icon="i-lucide-x"
|
|
273
|
+
color="neutral"
|
|
274
|
+
variant="ghost"
|
|
275
|
+
size="sm"
|
|
276
|
+
title="Close (Esc)"
|
|
277
|
+
@click="close"
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
</header>
|
|
281
|
+
|
|
282
|
+
<div ref="scrollEl" class="flex-1 overflow-auto px-6 py-6" @scroll="onScroll">
|
|
283
|
+
<div class="mx-auto max-w-3xl space-y-5">
|
|
284
|
+
<!-- metadata card (always shown) -->
|
|
285
|
+
<section
|
|
286
|
+
id="step-details"
|
|
287
|
+
:ref="(el) => (sectionEls['step-details'] = el as HTMLElement | null)"
|
|
288
|
+
class="scroll-mt-4 rounded-xl border border-slate-800 bg-slate-900/50 p-4"
|
|
289
|
+
>
|
|
290
|
+
<StepMetadataCard
|
|
291
|
+
:step="step"
|
|
292
|
+
:run-failed="runFailed"
|
|
293
|
+
:duration-label="durationLabel"
|
|
294
|
+
:is-running="isRunning"
|
|
295
|
+
:step-number="stepNumber"
|
|
296
|
+
:total-steps="totalSteps"
|
|
297
|
+
:instance-id="instance?.id"
|
|
298
|
+
:companion-verdicts="companionVerdicts"
|
|
299
|
+
:latest-verdict="latestVerdict"
|
|
300
|
+
/>
|
|
301
|
+
</section>
|
|
302
|
+
|
|
303
|
+
<!-- companion rework budget spent: the shared iteration-cap decision
|
|
304
|
+
(one more round / proceed with the current output / stop & reset) -->
|
|
305
|
+
<IterationCapPrompt
|
|
306
|
+
v-if="companionExceeded"
|
|
307
|
+
:heading="`${agent.label} hit its ${step.companion?.maxAttempts}-attempt rework limit, still below the ${pctOf(latestVerdict?.threshold ?? 0)} bar.`"
|
|
308
|
+
detail="Do one more automatic rework round, proceed to the next step accepting the current output, or stop and reset the task so you can edit the inputs and resubmit."
|
|
309
|
+
:loading="resolvingCap"
|
|
310
|
+
@resolve="resolveCompanionCap"
|
|
311
|
+
/>
|
|
312
|
+
|
|
313
|
+
<!-- tester report: what was tested, the per-area outcomes, the concerns
|
|
314
|
+
it raised and the greenlight verdict; plus the fixer-loop phase -->
|
|
315
|
+
<StepTestReport v-if="testReport" :report="testReport" :phase="testPhase" />
|
|
316
|
+
|
|
317
|
+
<!-- edit-then-approve: a direct editor over the raw conclusions; the
|
|
318
|
+
edits become the approved proposal that flows to the next step -->
|
|
319
|
+
<section v-if="editing" class="scroll-mt-4">
|
|
320
|
+
<div class="mb-2 flex items-center gap-1.5 text-[11px] text-amber-400">
|
|
321
|
+
<UIcon name="i-lucide-pencil" class="h-3.5 w-3.5" />
|
|
322
|
+
<span class="font-semibold uppercase tracking-wide">Editing the conclusions</span>
|
|
323
|
+
</div>
|
|
324
|
+
<UTextarea
|
|
325
|
+
v-model="draftProposal"
|
|
326
|
+
:rows="22"
|
|
327
|
+
autoresize
|
|
328
|
+
size="sm"
|
|
329
|
+
class="w-full"
|
|
330
|
+
:ui="{ base: 'font-mono text-[12px] leading-relaxed' }"
|
|
331
|
+
placeholder="Edit the agent's conclusions; your edits are saved when you approve…"
|
|
332
|
+
/>
|
|
333
|
+
</section>
|
|
334
|
+
|
|
335
|
+
<!-- the agent's prose output, sectioned + collapsible -->
|
|
336
|
+
<template v-else-if="hasOutput">
|
|
337
|
+
<section
|
|
338
|
+
v-for="s in outline.sections"
|
|
339
|
+
:id="s.id"
|
|
340
|
+
:key="s.id"
|
|
341
|
+
:ref="(el) => (sectionEls[s.id] = el as HTMLElement | null)"
|
|
342
|
+
class="scroll-mt-4"
|
|
343
|
+
>
|
|
344
|
+
<button
|
|
345
|
+
v-if="s.depth > 0"
|
|
346
|
+
class="group flex w-full items-center gap-2 rounded-md py-1 text-left transition hover:text-white"
|
|
347
|
+
@click="toggle(s.id)"
|
|
348
|
+
>
|
|
349
|
+
<UIcon
|
|
350
|
+
name="i-lucide-chevron-right"
|
|
351
|
+
class="h-4 w-4 shrink-0 text-slate-500 transition-transform group-hover:text-slate-300"
|
|
352
|
+
:class="collapsed[s.id] ? '' : 'rotate-90'"
|
|
353
|
+
/>
|
|
354
|
+
<span
|
|
355
|
+
class="font-semibold text-slate-100"
|
|
356
|
+
:class="s.depth <= 1 ? 'text-lg' : s.depth === 2 ? 'text-base' : 'text-sm'"
|
|
357
|
+
v-html="s.titleHtml"
|
|
358
|
+
/>
|
|
359
|
+
</button>
|
|
360
|
+
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
361
|
+
<div
|
|
362
|
+
v-show="!collapsed[s.id]"
|
|
363
|
+
class="reader-prose mt-1 text-[13px] leading-relaxed text-slate-300"
|
|
364
|
+
:class="[
|
|
365
|
+
s.depth > 0 ? 'pl-6' : '',
|
|
366
|
+
approvalPending && !editing && !companionExceeded ? 'review-mode' : '',
|
|
367
|
+
]"
|
|
368
|
+
@click="onProseClick"
|
|
369
|
+
v-html="s.bodyHtml"
|
|
370
|
+
/>
|
|
371
|
+
</section>
|
|
372
|
+
</template>
|
|
373
|
+
|
|
374
|
+
<p
|
|
375
|
+
v-else
|
|
376
|
+
class="rounded-lg border border-dashed border-slate-800 py-6 text-center text-sm text-slate-500"
|
|
377
|
+
>
|
|
378
|
+
This agent produced no prose output.
|
|
379
|
+
</p>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<!-- review rail (approval mode): per-block comments + overall feedback +
|
|
385
|
+
Approve / Request changes / Reject. A right-side rail on wide screens; a
|
|
386
|
+
bottom sheet (still reachable) below lg, so the gate is always actionable. -->
|
|
387
|
+
<aside
|
|
388
|
+
v-if="approvalPending && !companionExceeded"
|
|
389
|
+
class="absolute inset-x-0 bottom-0 z-10 flex max-h-[70vh] flex-col rounded-t-2xl border-t border-slate-700 bg-slate-900/95 shadow-2xl backdrop-blur lg:static lg:inset-auto lg:z-auto lg:max-h-none lg:w-96 lg:shrink-0 lg:rounded-none lg:border-l lg:border-t-0 lg:border-slate-800 lg:bg-slate-900/60 lg:shadow-none lg:backdrop-blur-none"
|
|
390
|
+
>
|
|
391
|
+
<div class="border-b border-slate-800 px-4 py-3">
|
|
392
|
+
<div class="text-[11px] font-semibold uppercase tracking-wide text-amber-400">
|
|
393
|
+
{{ editing ? 'Approve with corrections' : 'Review & approve' }}
|
|
394
|
+
</div>
|
|
395
|
+
<p class="mt-1 text-[12px] text-slate-400">
|
|
396
|
+
{{
|
|
397
|
+
editing
|
|
398
|
+
? 'Edit the conclusions on the left; your edits are saved when you approve.'
|
|
399
|
+
: 'Click any block in the output to comment on it, or leave overall feedback below.'
|
|
400
|
+
}}
|
|
401
|
+
</p>
|
|
402
|
+
</div>
|
|
403
|
+
|
|
404
|
+
<div class="flex-1 space-y-3 overflow-auto px-4 py-3">
|
|
405
|
+
<p
|
|
406
|
+
v-if="editing"
|
|
407
|
+
class="rounded-lg border border-amber-500/30 bg-amber-500/5 p-3 text-[12px] leading-relaxed text-amber-200/90"
|
|
408
|
+
>
|
|
409
|
+
You're editing the conclusions directly. Manual edits can't be combined with per-block
|
|
410
|
+
comments — approve to save them, or cancel to return to review.
|
|
411
|
+
</p>
|
|
412
|
+
<template v-else>
|
|
413
|
+
<!-- composer for the block the human just clicked -->
|
|
414
|
+
<div
|
|
415
|
+
v-if="draftTarget"
|
|
416
|
+
class="rounded-lg border border-indigo-500/40 bg-indigo-500/5 p-3"
|
|
417
|
+
>
|
|
418
|
+
<div class="mb-1 text-[10px] uppercase tracking-wide text-indigo-300">
|
|
419
|
+
Commenting on
|
|
420
|
+
</div>
|
|
421
|
+
<pre
|
|
422
|
+
class="mb-2 max-h-24 overflow-auto whitespace-pre-wrap rounded bg-slate-950/60 p-2 text-[11px] text-slate-300"
|
|
423
|
+
>{{ draftTarget.quotedSource }}</pre
|
|
424
|
+
>
|
|
425
|
+
<UTextarea
|
|
426
|
+
v-model="draftBody"
|
|
427
|
+
:rows="3"
|
|
428
|
+
autoresize
|
|
429
|
+
size="sm"
|
|
430
|
+
class="w-full"
|
|
431
|
+
placeholder="Leave a comment on this block…"
|
|
432
|
+
/>
|
|
433
|
+
<div class="mt-2 flex justify-end gap-2">
|
|
434
|
+
<UButton color="neutral" variant="ghost" size="xs" @click="cancelDraft">
|
|
435
|
+
Cancel
|
|
436
|
+
</UButton>
|
|
437
|
+
<UButton
|
|
438
|
+
color="primary"
|
|
439
|
+
size="xs"
|
|
440
|
+
:disabled="!draftBody.trim()"
|
|
441
|
+
@click="addDraftComment"
|
|
442
|
+
>
|
|
443
|
+
Add comment
|
|
444
|
+
</UButton>
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
|
|
448
|
+
<!-- comments added so far -->
|
|
449
|
+
<div
|
|
450
|
+
v-for="(c, idx) in reviewComments"
|
|
451
|
+
:key="idx"
|
|
452
|
+
class="rounded-lg border border-slate-800 bg-slate-900/50 p-3"
|
|
453
|
+
>
|
|
454
|
+
<div class="mb-1 flex items-start justify-between gap-2">
|
|
455
|
+
<div class="text-[10px] uppercase tracking-wide text-slate-500">
|
|
456
|
+
Comment {{ idx + 1 }}
|
|
457
|
+
</div>
|
|
458
|
+
<button
|
|
459
|
+
class="text-slate-500 transition hover:text-rose-400"
|
|
460
|
+
title="Remove comment"
|
|
461
|
+
@click="removeComment(idx)"
|
|
462
|
+
>
|
|
463
|
+
<UIcon name="i-lucide-x" class="h-3.5 w-3.5" />
|
|
464
|
+
</button>
|
|
465
|
+
</div>
|
|
466
|
+
<pre
|
|
467
|
+
class="mb-1 max-h-20 overflow-auto whitespace-pre-wrap rounded bg-slate-950/50 p-1.5 text-[10px] text-slate-400"
|
|
468
|
+
>{{ c.quotedSource }}</pre
|
|
469
|
+
>
|
|
470
|
+
<p class="text-[12px] text-slate-200">{{ c.body }}</p>
|
|
471
|
+
</div>
|
|
472
|
+
|
|
473
|
+
<div>
|
|
474
|
+
<label
|
|
475
|
+
class="mb-1 block text-[11px] font-semibold uppercase tracking-wide text-slate-400"
|
|
476
|
+
>
|
|
477
|
+
Overall feedback / reject reason
|
|
478
|
+
</label>
|
|
479
|
+
<UTextarea
|
|
480
|
+
v-model="feedback"
|
|
481
|
+
:rows="3"
|
|
482
|
+
autoresize
|
|
483
|
+
size="sm"
|
|
484
|
+
class="w-full"
|
|
485
|
+
placeholder="Describe the changes the agent should make (optional if you left per-block comments)…"
|
|
486
|
+
/>
|
|
487
|
+
</div>
|
|
488
|
+
</template>
|
|
489
|
+
</div>
|
|
490
|
+
|
|
491
|
+
<!-- edit-then-approve actions -->
|
|
492
|
+
<div v-if="editing" class="space-y-2 border-t border-slate-800 px-4 py-3">
|
|
493
|
+
<UButton
|
|
494
|
+
color="primary"
|
|
495
|
+
size="sm"
|
|
496
|
+
icon="i-lucide-check"
|
|
497
|
+
block
|
|
498
|
+
:loading="submitting"
|
|
499
|
+
@click="approveWithEdits"
|
|
500
|
+
>
|
|
501
|
+
Approve with these edits
|
|
502
|
+
</UButton>
|
|
503
|
+
<UButton
|
|
504
|
+
color="neutral"
|
|
505
|
+
variant="ghost"
|
|
506
|
+
size="sm"
|
|
507
|
+
block
|
|
508
|
+
:disabled="submitting"
|
|
509
|
+
@click="cancelEditing"
|
|
510
|
+
>
|
|
511
|
+
Cancel edits
|
|
512
|
+
</UButton>
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
<div v-else class="space-y-2 border-t border-slate-800 px-4 py-3">
|
|
516
|
+
<UButton
|
|
517
|
+
color="primary"
|
|
518
|
+
size="sm"
|
|
519
|
+
icon="i-lucide-check"
|
|
520
|
+
block
|
|
521
|
+
:disabled="rejectArmed"
|
|
522
|
+
:loading="submitting"
|
|
523
|
+
@click="approve"
|
|
524
|
+
>
|
|
525
|
+
Approve & proceed
|
|
526
|
+
</UButton>
|
|
527
|
+
<UButton
|
|
528
|
+
color="primary"
|
|
529
|
+
variant="soft"
|
|
530
|
+
size="sm"
|
|
531
|
+
icon="i-lucide-pencil"
|
|
532
|
+
block
|
|
533
|
+
:disabled="rejectArmed || submitting"
|
|
534
|
+
@click="startEditing"
|
|
535
|
+
>
|
|
536
|
+
Approve with corrections
|
|
537
|
+
</UButton>
|
|
538
|
+
|
|
539
|
+
<!-- destructive: a two-step inline confirm instead of a native dialog -->
|
|
540
|
+
<div
|
|
541
|
+
v-if="rejectArmed"
|
|
542
|
+
class="rounded-lg border border-rose-500/40 bg-rose-500/5 p-2.5"
|
|
543
|
+
>
|
|
544
|
+
<p class="mb-2 text-[11px] text-rose-200">
|
|
545
|
+
Reject this proposal and stop the run entirely?
|
|
546
|
+
</p>
|
|
547
|
+
<div class="flex gap-2">
|
|
548
|
+
<UButton
|
|
549
|
+
color="neutral"
|
|
550
|
+
variant="ghost"
|
|
551
|
+
size="xs"
|
|
552
|
+
class="flex-1"
|
|
553
|
+
:disabled="submitting"
|
|
554
|
+
@click="disarmReject"
|
|
555
|
+
>
|
|
556
|
+
Cancel
|
|
557
|
+
</UButton>
|
|
558
|
+
<UButton
|
|
559
|
+
color="error"
|
|
560
|
+
size="xs"
|
|
561
|
+
icon="i-lucide-ban"
|
|
562
|
+
class="flex-1"
|
|
563
|
+
:loading="submitting"
|
|
564
|
+
@click="reject"
|
|
565
|
+
>
|
|
566
|
+
Confirm reject
|
|
567
|
+
</UButton>
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
<div v-else class="flex gap-2">
|
|
571
|
+
<UButton
|
|
572
|
+
color="warning"
|
|
573
|
+
variant="soft"
|
|
574
|
+
size="sm"
|
|
575
|
+
icon="i-lucide-rotate-ccw"
|
|
576
|
+
class="flex-1"
|
|
577
|
+
:disabled="!canRequestChanges"
|
|
578
|
+
:loading="submitting"
|
|
579
|
+
@click="requestChanges"
|
|
580
|
+
>
|
|
581
|
+
Request changes
|
|
582
|
+
</UButton>
|
|
583
|
+
<UButton
|
|
584
|
+
color="error"
|
|
585
|
+
variant="soft"
|
|
586
|
+
size="sm"
|
|
587
|
+
icon="i-lucide-ban"
|
|
588
|
+
class="flex-1"
|
|
589
|
+
:disabled="submitting"
|
|
590
|
+
@click="armReject"
|
|
591
|
+
>
|
|
592
|
+
Reject
|
|
593
|
+
</UButton>
|
|
594
|
+
</div>
|
|
595
|
+
<p class="text-[10px] text-slate-500">
|
|
596
|
+
Request changes re-runs this step with your feedback & comments. Reject stops the
|
|
597
|
+
run entirely.
|
|
598
|
+
</p>
|
|
599
|
+
</div>
|
|
600
|
+
</aside>
|
|
601
|
+
</div>
|
|
602
|
+
</Transition>
|
|
603
|
+
</Teleport>
|
|
604
|
+
</template>
|
|
605
|
+
|
|
606
|
+
<style scoped>
|
|
607
|
+
.reader-fade-enter-active,
|
|
608
|
+
.reader-fade-leave-active {
|
|
609
|
+
transition: opacity 0.18s ease;
|
|
610
|
+
}
|
|
611
|
+
.reader-fade-enter-from,
|
|
612
|
+
.reader-fade-leave-to {
|
|
613
|
+
opacity: 0;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/* Approval mode: each source-mapped block becomes a comment target — a hover
|
|
617
|
+
highlight + a "+" gutter affordance, GitHub-review style. */
|
|
618
|
+
.reader-prose.review-mode :deep([data-src-start]) {
|
|
619
|
+
position: relative;
|
|
620
|
+
cursor: pointer;
|
|
621
|
+
border-radius: 0.375rem;
|
|
622
|
+
transition: background 0.12s ease;
|
|
623
|
+
}
|
|
624
|
+
.reader-prose.review-mode :deep([data-src-start]:hover) {
|
|
625
|
+
background: rgb(99 102 241 / 0.08);
|
|
626
|
+
box-shadow: inset 2px 0 0 rgb(99 102 241 / 0.5);
|
|
627
|
+
}
|
|
628
|
+
.reader-prose.review-mode :deep([data-src-start])::before {
|
|
629
|
+
content: '+';
|
|
630
|
+
position: absolute;
|
|
631
|
+
left: -1.4rem;
|
|
632
|
+
top: 0.1rem;
|
|
633
|
+
display: none;
|
|
634
|
+
height: 1.1rem;
|
|
635
|
+
width: 1.1rem;
|
|
636
|
+
align-items: center;
|
|
637
|
+
justify-content: center;
|
|
638
|
+
border-radius: 0.25rem;
|
|
639
|
+
background: rgb(99 102 241);
|
|
640
|
+
color: white;
|
|
641
|
+
font-size: 0.8rem;
|
|
642
|
+
line-height: 1;
|
|
643
|
+
}
|
|
644
|
+
.reader-prose.review-mode :deep([data-src-start]:hover)::before {
|
|
645
|
+
display: flex;
|
|
646
|
+
}
|
|
647
|
+
/* Persistent markers: amber for a block that already has a comment, indigo for
|
|
648
|
+
the block whose composer is currently open. */
|
|
649
|
+
.reader-prose :deep(.cf-commented) {
|
|
650
|
+
background: rgb(234 179 8 / 0.1);
|
|
651
|
+
box-shadow: inset 2px 0 0 rgb(234 179 8 / 0.6);
|
|
652
|
+
}
|
|
653
|
+
.reader-prose :deep(.cf-selected) {
|
|
654
|
+
background: rgb(99 102 241 / 0.12);
|
|
655
|
+
box-shadow: inset 2px 0 0 rgb(99 102 241 / 0.8);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* Styling for the markdown HTML injected via v-html (out of scoped reach without
|
|
659
|
+
:deep), kept close to the inspector's existing prose styling. */
|
|
660
|
+
.reader-prose :deep(p) {
|
|
661
|
+
margin: 0.5rem 0;
|
|
662
|
+
}
|
|
663
|
+
.reader-prose :deep(ul),
|
|
664
|
+
.reader-prose :deep(ol) {
|
|
665
|
+
margin: 0.5rem 0;
|
|
666
|
+
padding-left: 1.25rem;
|
|
667
|
+
}
|
|
668
|
+
.reader-prose :deep(ul) {
|
|
669
|
+
list-style: disc;
|
|
670
|
+
}
|
|
671
|
+
.reader-prose :deep(ol) {
|
|
672
|
+
list-style: decimal;
|
|
673
|
+
}
|
|
674
|
+
.reader-prose :deep(li) {
|
|
675
|
+
margin: 0.2rem 0;
|
|
676
|
+
}
|
|
677
|
+
.reader-prose :deep(strong) {
|
|
678
|
+
font-weight: 600;
|
|
679
|
+
color: rgb(226 232 240);
|
|
680
|
+
}
|
|
681
|
+
.reader-prose :deep(em) {
|
|
682
|
+
font-style: italic;
|
|
683
|
+
}
|
|
684
|
+
.reader-prose :deep(code) {
|
|
685
|
+
border-radius: 0.25rem;
|
|
686
|
+
background: rgb(30 41 59 / 0.8);
|
|
687
|
+
padding: 0.1rem 0.3rem;
|
|
688
|
+
font-family: ui-monospace, monospace;
|
|
689
|
+
font-size: 0.85em;
|
|
690
|
+
color: rgb(199 210 254);
|
|
691
|
+
}
|
|
692
|
+
.reader-prose :deep(pre) {
|
|
693
|
+
margin: 0.6rem 0;
|
|
694
|
+
overflow: auto;
|
|
695
|
+
border-radius: 0.5rem;
|
|
696
|
+
background: rgb(2 6 23 / 0.6);
|
|
697
|
+
padding: 0.75rem 0.9rem;
|
|
698
|
+
}
|
|
699
|
+
.reader-prose :deep(pre code) {
|
|
700
|
+
background: transparent;
|
|
701
|
+
padding: 0;
|
|
702
|
+
color: rgb(203 213 225);
|
|
703
|
+
}
|
|
704
|
+
.reader-prose :deep(blockquote) {
|
|
705
|
+
margin: 0.6rem 0;
|
|
706
|
+
border-left: 3px solid rgb(99 102 241 / 0.5);
|
|
707
|
+
padding-left: 0.75rem;
|
|
708
|
+
color: rgb(148 163 184);
|
|
709
|
+
}
|
|
710
|
+
.reader-prose :deep(table) {
|
|
711
|
+
margin: 0.6rem 0;
|
|
712
|
+
border-collapse: collapse;
|
|
713
|
+
font-size: 0.95em;
|
|
714
|
+
}
|
|
715
|
+
.reader-prose :deep(th),
|
|
716
|
+
.reader-prose :deep(td) {
|
|
717
|
+
border: 1px solid rgb(51 65 85);
|
|
718
|
+
padding: 0.3rem 0.6rem;
|
|
719
|
+
}
|
|
720
|
+
.reader-prose :deep(th) {
|
|
721
|
+
background: rgb(30 41 59 / 0.6);
|
|
722
|
+
font-weight: 600;
|
|
723
|
+
}
|
|
724
|
+
.reader-prose :deep(hr) {
|
|
725
|
+
margin: 1rem 0;
|
|
726
|
+
border: none;
|
|
727
|
+
border-top: 1px solid rgb(51 65 85);
|
|
728
|
+
}
|
|
729
|
+
.reader-prose :deep(h1),
|
|
730
|
+
.reader-prose :deep(h2),
|
|
731
|
+
.reader-prose :deep(h3),
|
|
732
|
+
.reader-prose :deep(h4) {
|
|
733
|
+
margin: 0.6rem 0 0.3rem;
|
|
734
|
+
font-weight: 600;
|
|
735
|
+
color: rgb(226 232 240);
|
|
736
|
+
}
|
|
737
|
+
</style>
|