@cat-factory/app 0.9.0 → 0.10.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.
Files changed (29) hide show
  1. package/app/components/board/AddTaskModal.vue +209 -21
  2. package/app/components/board/nodes/BlockNode.vue +2 -2
  3. package/app/components/focus/BlockFocusView.vue +2 -2
  4. package/app/components/layout/CommandBar.vue +7 -33
  5. package/app/components/layout/IntegrationsHub.vue +230 -0
  6. package/app/components/layout/SideBar.vue +8 -170
  7. package/app/components/panels/GenericStructuredResultView.vue +131 -0
  8. package/app/components/panels/InspectorPanel.vue +6 -2
  9. package/app/components/panels/StepResultViewHost.vue +4 -0
  10. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +148 -0
  11. package/app/components/settings/IssueTrackerWritebackPanel.vue +45 -57
  12. package/app/components/settings/MergeThresholdsPanel.vue +189 -226
  13. package/app/components/settings/ObservabilityConnectionPanel.vue +151 -0
  14. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +46 -61
  15. package/app/components/settings/WorkspaceSettingsPanel.vue +136 -63
  16. package/app/composables/api/releaseHealth.ts +11 -10
  17. package/app/pages/index.vue +4 -8
  18. package/app/stores/agents.ts +27 -2
  19. package/app/stores/releaseHealth.ts +48 -12
  20. package/app/stores/ui.ts +34 -42
  21. package/app/stores/workspace.ts +4 -0
  22. package/app/types/domain.ts +33 -1
  23. package/app/types/execution.ts +6 -0
  24. package/app/types/releaseHealth.ts +19 -11
  25. package/app/utils/catalog.spec.ts +10 -0
  26. package/app/utils/catalog.ts +20 -6
  27. package/package.json +2 -2
  28. package/app/components/board/ContextPicker.vue +0 -367
  29. package/app/components/settings/DatadogPanel.vue +0 -213
@@ -68,17 +68,6 @@ watch(
68
68
  >
69
69
  Build a pipeline
70
70
  </UButton>
71
- <UButton
72
- block
73
- color="primary"
74
- variant="soft"
75
- size="sm"
76
- icon="i-lucide-plus"
77
- class="justify-start"
78
- @click="ui.openCommandBar()"
79
- >
80
- Add a block
81
- </UButton>
82
71
  </div>
83
72
  </section>
84
73
 
@@ -122,95 +111,20 @@ watch(
122
111
  Integrations
123
112
  </h2>
124
113
  <div class="space-y-1.5">
114
+ <!-- Every external system the workspace can enable/link now lives behind
115
+ this single button — the hub modal lists them grouped (source control,
116
+ communication, documents, trackers, observability, model providers). -->
125
117
  <UButton
126
- v-if="github.available"
127
118
  block
128
119
  color="primary"
129
120
  variant="soft"
130
121
  size="sm"
131
- icon="i-lucide-github"
122
+ icon="i-lucide-blocks"
132
123
  class="justify-start"
133
- @click="ui.openGitHub()"
124
+ @click="ui.openIntegrations()"
134
125
  >
135
- <span class="truncate">
136
- {{ github.connected ? github.connection?.accountLogin : 'Connect GitHub' }}
137
- </span>
126
+ Integrations
138
127
  </UButton>
139
-
140
- <UButton
141
- v-if="slack.available"
142
- block
143
- color="primary"
144
- variant="soft"
145
- size="sm"
146
- icon="i-lucide-slack"
147
- class="justify-start"
148
- @click="ui.openSlack()"
149
- >
150
- <span class="truncate">
151
- {{ slack.connected ? slack.connection?.teamName : 'Connect Slack' }}
152
- </span>
153
- </UButton>
154
-
155
- <template v-if="documents.available">
156
- <UButton
157
- v-for="src in documents.sources"
158
- :key="src.source"
159
- block
160
- color="neutral"
161
- variant="soft"
162
- size="sm"
163
- :icon="src.icon"
164
- class="justify-start"
165
- @click="ui.openDocumentConnect(src.source)"
166
- >
167
- <span class="truncate">
168
- {{ documents.isConnected(src.source) ? src.label : `Connect ${src.label}` }}
169
- </span>
170
- </UButton>
171
- <UButton
172
- v-if="documents.anyConnected"
173
- block
174
- color="neutral"
175
- variant="soft"
176
- size="sm"
177
- icon="i-lucide-file-down"
178
- class="justify-start"
179
- @click="ui.openDocumentImport(null)"
180
- >
181
- Import &amp; spawn
182
- </UButton>
183
- </template>
184
-
185
- <template v-if="tasks.available">
186
- <UButton
187
- v-for="src in tasks.sources"
188
- :key="src.source"
189
- block
190
- color="neutral"
191
- variant="soft"
192
- size="sm"
193
- :icon="src.icon"
194
- class="justify-start"
195
- @click="ui.openTaskConnect(src.source)"
196
- >
197
- <span class="truncate">
198
- {{ tasks.isConnected(src.source) ? src.label : `Connect ${src.label}` }}
199
- </span>
200
- </UButton>
201
- <UButton
202
- v-if="tasks.anyConnected"
203
- block
204
- color="neutral"
205
- variant="soft"
206
- size="sm"
207
- icon="i-lucide-file-down"
208
- class="justify-start"
209
- @click="ui.openTaskImport(null)"
210
- >
211
- Import issues
212
- </UButton>
213
- </template>
214
128
  </div>
215
129
  </section>
216
130
 
@@ -240,17 +154,8 @@ watch(
240
154
  Configuration
241
155
  </h2>
242
156
  <div class="space-y-1.5">
243
- <UButton
244
- block
245
- color="primary"
246
- variant="soft"
247
- size="sm"
248
- icon="i-lucide-git-merge"
249
- class="justify-start"
250
- @click="ui.openMergeThresholds()"
251
- >
252
- Merge thresholds
253
- </UButton>
157
+ <!-- Merge thresholds, issue writeback and default service best practices are
158
+ now tabs inside Workspace settings. -->
254
159
  <UButton
255
160
  block
256
161
  color="primary"
@@ -262,28 +167,6 @@ watch(
262
167
  >
263
168
  Workspace settings
264
169
  </UButton>
265
- <UButton
266
- block
267
- color="primary"
268
- variant="soft"
269
- size="sm"
270
- icon="i-lucide-message-square-reply"
271
- class="justify-start"
272
- @click="ui.openIssueWriteback()"
273
- >
274
- Issue tracker writeback
275
- </UButton>
276
- <UButton
277
- block
278
- color="primary"
279
- variant="soft"
280
- size="sm"
281
- icon="i-lucide-activity"
282
- class="justify-start"
283
- @click="ui.openDatadog()"
284
- >
285
- Post-release health
286
- </UButton>
287
170
  <UButton
288
171
  block
289
172
  color="primary"
@@ -295,51 +178,6 @@ watch(
295
178
  >
296
179
  Model Configuration
297
180
  </UButton>
298
- <UButton
299
- block
300
- color="primary"
301
- variant="soft"
302
- size="sm"
303
- icon="i-lucide-book-open-check"
304
- class="justify-start"
305
- @click="ui.openServiceFragmentDefaults()"
306
- >
307
- Default service best practices
308
- </UButton>
309
- <UButton
310
- block
311
- color="primary"
312
- variant="soft"
313
- size="sm"
314
- icon="i-lucide-key-round"
315
- class="justify-start"
316
- title="Connect LLM vendor subscriptions + provider API keys"
317
- @click="ui.openVendorCredentials()"
318
- >
319
- Vendors &amp; keys
320
- </UButton>
321
- <UButton
322
- block
323
- color="primary"
324
- variant="soft"
325
- size="sm"
326
- icon="i-lucide-server"
327
- class="justify-start"
328
- @click="ui.openLocalModels()"
329
- >
330
- My local runners
331
- </UButton>
332
- <UButton
333
- block
334
- color="primary"
335
- variant="soft"
336
- size="sm"
337
- icon="i-lucide-waypoints"
338
- class="justify-start"
339
- @click="ui.openOpenRouter()"
340
- >
341
- OpenRouter models
342
- </UButton>
343
181
  </div>
344
182
  </section>
345
183
 
@@ -0,0 +1,131 @@
1
+ <script setup lang="ts">
2
+ // Generic structured-result window — the default dedicated surface for a registered
3
+ // CUSTOM agent kind whose archetype declares `resultView: 'generic-structured'`. A custom
4
+ // `container-explore` agent returns structured JSON (the engine's `custom` channel), which
5
+ // is recorded on the step; this renders it read-only (pretty-printed JSON), alongside the
6
+ // agent's prose summary and the shared run metadata — so a proprietary kind ships a usable
7
+ // result view with ZERO bespoke frontend code. Opened via the universal result-view host,
8
+ // the same seam the requirements / tester windows use.
9
+ import { computed } from 'vue'
10
+ import StepRunMeta from '~/components/panels/StepRunMeta.vue'
11
+ import StepRestartControl from '~/components/panels/StepRestartControl.vue'
12
+
13
+ const board = useBoardStore()
14
+ const execution = useExecutionStore()
15
+ const agents = useAgentsStore()
16
+
17
+ // Shared seam contract (open/blockId/close + Escape). No `onOpen` loader: this window reads
18
+ // its data straight off the execution step, so there's nothing to fetch on open.
19
+ const { open, blockId, instanceId, stepIndex, close } = useResultView('generic-structured')
20
+ const block = computed(() => (blockId.value ? board.getBlock(blockId.value) : undefined))
21
+
22
+ const instance = computed(() =>
23
+ instanceId.value === null ? null : (execution.getInstance(instanceId.value) ?? null),
24
+ )
25
+ const step = computed(() => {
26
+ if (instance.value === null || stepIndex.value === null) return null
27
+ return instance.value.steps[stepIndex.value] ?? null
28
+ })
29
+ const meta = computed(() => (step.value ? agents.get(step.value.agentKind) : undefined))
30
+
31
+ /** The agent's structured JSON, pretty-printed; null when the step produced none. */
32
+ const customJson = computed<string | null>(() => {
33
+ const custom = step.value?.custom
34
+ if (custom === undefined || custom === null) return null
35
+ try {
36
+ return JSON.stringify(custom, null, 2)
37
+ } catch {
38
+ return String(custom)
39
+ }
40
+ })
41
+ </script>
42
+
43
+ <template>
44
+ <Teleport to="body">
45
+ <div
46
+ v-if="open"
47
+ class="fixed inset-0 z-50 flex items-stretch justify-center bg-slate-950/70 backdrop-blur-sm"
48
+ @click.self="close"
49
+ >
50
+ <div
51
+ class="m-4 flex w-full max-w-4xl flex-col overflow-hidden rounded-2xl border border-slate-800 bg-slate-900 shadow-2xl"
52
+ >
53
+ <!-- Header -->
54
+ <header class="flex items-center gap-3 border-b border-slate-800 px-5 py-3">
55
+ <span
56
+ class="flex h-8 w-8 items-center justify-center rounded-lg bg-cyan-500/15 text-cyan-300"
57
+ >
58
+ <UIcon :name="meta?.icon ?? 'i-lucide-braces'" class="h-4 w-4" />
59
+ </span>
60
+ <div class="min-w-0 flex-1">
61
+ <h2 class="truncate text-sm font-semibold text-slate-100">
62
+ {{ meta?.label ?? 'Agent result' }}{{ block ? ` — ${block.title}` : '' }}
63
+ </h2>
64
+ <p class="truncate text-[11px] text-slate-400">
65
+ {{ meta?.description ?? 'Structured agent output' }}
66
+ </p>
67
+ </div>
68
+ <StepRestartControl
69
+ :instance-id="instanceId"
70
+ :step-index="stepIndex"
71
+ @restarted="close"
72
+ />
73
+ <button
74
+ class="rounded-md p-1.5 text-slate-400 hover:bg-slate-800 hover:text-slate-200"
75
+ @click="close"
76
+ >
77
+ <UIcon name="i-lucide-x" class="h-4 w-4" />
78
+ </button>
79
+ </header>
80
+
81
+ <div class="flex min-h-0 flex-1">
82
+ <!-- Main: prose summary + structured JSON -->
83
+ <div class="min-w-0 flex-1 overflow-y-auto px-5 py-4">
84
+ <p
85
+ v-if="step?.output"
86
+ class="mb-4 whitespace-pre-wrap text-[13px] leading-relaxed text-slate-300"
87
+ >
88
+ {{ step.output }}
89
+ </p>
90
+
91
+ <template v-if="customJson">
92
+ <h3 class="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
93
+ Structured output
94
+ </h3>
95
+ <pre
96
+ class="overflow-x-auto rounded-lg border border-slate-800 bg-slate-950/60 p-3 text-[12px] leading-relaxed text-slate-200"
97
+ ><code>{{ customJson }}</code></pre>
98
+ </template>
99
+
100
+ <div
101
+ v-else-if="!step?.output"
102
+ class="flex h-full flex-col items-center justify-center gap-2 text-center text-slate-400"
103
+ >
104
+ <UIcon name="i-lucide-braces" class="h-8 w-8 opacity-40" />
105
+ <p class="text-sm">No result yet.</p>
106
+ <p class="max-w-sm text-[11px] text-slate-500">
107
+ The structured output appears once this agent finishes. While it runs, the step
108
+ shows live progress on the board.
109
+ </p>
110
+ </div>
111
+ </div>
112
+
113
+ <!-- Sidebar: shared run metadata + observability rollup -->
114
+ <aside
115
+ class="hidden w-60 shrink-0 flex-col gap-4 border-l border-slate-800 bg-slate-900/50 px-4 py-4 lg:flex"
116
+ >
117
+ <StepRunMeta
118
+ v-if="step"
119
+ :step="step"
120
+ :instance-id="instanceId ?? undefined"
121
+ :step-number="stepIndex === null ? undefined : stepIndex + 1"
122
+ :total-steps="instance?.steps.length"
123
+ :run-failed="instance?.status === 'failed'"
124
+ :failure-at="instance?.failure?.occurredAt"
125
+ />
126
+ </aside>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ </Teleport>
131
+ </template>
@@ -1,11 +1,12 @@
1
1
  <script setup lang="ts">
2
2
  import type { Block, BlockStatus } from '~/types/domain'
3
- import { BLOCK_TYPE_META, STATUS_META } from '~/utils/catalog'
3
+ import { blockTypeMeta, STATUS_META } from '~/utils/catalog'
4
4
  import TaskContextDocs from '~/components/documents/TaskContextDocs.vue'
5
5
  import TaskContextIssues from '~/components/tasks/TaskContextIssues.vue'
6
6
  import TaskAgentConfig from '~/components/panels/inspector/TaskAgentConfig.vue'
7
7
  import ServiceTestConfig from '~/components/panels/inspector/ServiceTestConfig.vue'
8
8
  import ServiceFragments from '~/components/panels/inspector/ServiceFragments.vue'
9
+ import ServiceReleaseHealthConfig from '~/components/panels/inspector/ServiceReleaseHealthConfig.vue'
9
10
  import ContainerSummary from '~/components/panels/inspector/ContainerSummary.vue'
10
11
  import TaskDependencies from '~/components/panels/inspector/TaskDependencies.vue'
11
12
  import TaskStructure from '~/components/panels/inspector/TaskStructure.vue'
@@ -52,7 +53,7 @@ const isContainer = computed(() => level.value === 'frame' || level.value === 'm
52
53
  const isTask = computed(() => level.value === 'task')
53
54
 
54
55
  const instance = computed(() => execution.getInstance(block.value?.executionId))
55
- const typeMeta = computed(() => (block.value ? BLOCK_TYPE_META[block.value.type] : null))
56
+ const typeMeta = computed(() => (block.value ? blockTypeMeta(block.value.type) : null))
56
57
 
57
58
  // Containers show a derived activity status (never "done"); tasks use their own.
58
59
  const FRAME_LABEL: Record<BlockStatus, string> = {
@@ -413,6 +414,9 @@ const showOriginalDescription = ref(false)
413
414
  <!-- service (frame): best-practice fragments for code-aware agents -->
414
415
  <ServiceFragments v-if="isFrame" :block="block" />
415
416
 
417
+ <!-- service (frame): post-release-health monitor/SLO mapping -->
418
+ <ServiceReleaseHealthConfig v-if="isFrame" :block="block" />
419
+
416
420
  <!-- task: dependencies, structure, agent config, run settings, execution -->
417
421
  <template v-else-if="isTask">
418
422
  <RecurringScheduleSettings :block="block" />
@@ -16,6 +16,7 @@ import ClarityReviewWindow from '~/components/clarity/ClarityReviewWindow.vue'
16
16
  import TestReportWindow from '~/components/testing/TestReportWindow.vue'
17
17
  import GateResultView from '~/components/gates/GateResultView.vue'
18
18
  import ConsensusSessionWindow from '~/components/consensus/ConsensusSessionWindow.vue'
19
+ import GenericStructuredResultView from '~/components/panels/GenericStructuredResultView.vue'
19
20
 
20
21
  const ui = useUiStore()
21
22
 
@@ -27,6 +28,9 @@ const STEP_RESULT_VIEWS: Record<string, Component> = {
27
28
  gate: GateResultView,
28
29
  // Opened for any step that ran the consensus mechanism (routed in `ui.dispatchStepView`).
29
30
  'consensus-session': ConsensusSessionWindow,
31
+ // Default dedicated view for a registered CUSTOM kind's structured (`custom`) output —
32
+ // a read-only JSON viewer, so a proprietary agent ships a result view with no bespoke code.
33
+ 'generic-structured': GenericStructuredResultView,
30
34
  }
31
35
 
32
36
  const active = computed<Component | null>(() => {
@@ -0,0 +1,148 @@
1
+ <script setup lang="ts">
2
+ import { computed, reactive, ref, watch } from 'vue'
3
+ import type { Block } from '~/types/domain'
4
+
5
+ // Per-service (frame) post-release-health mapping: which observability monitors/SLOs the
6
+ // `post-release-health` gate watches after this service's PRs ship. Keyed by THIS block's
7
+ // id (no manual entry) — the global window only owns the connection now. The Attach/save
8
+ // control is disabled with a hint until an observability integration is connected.
9
+ const props = defineProps<{ block: Block }>()
10
+
11
+ const store = useReleaseHealthStore()
12
+ const ui = useUiStore()
13
+ const toast = useToast()
14
+
15
+ const busy = ref(false)
16
+ const draft = reactive({ monitorIds: '', sloIds: '', envTag: '' })
17
+
18
+ const connected = computed(() => store.connection.connected)
19
+ const saved = computed(() => store.configForBlock(props.block.id))
20
+
21
+ function parseIds(csv: string): string[] {
22
+ return csv
23
+ .split(',')
24
+ .map((s) => s.trim())
25
+ .filter((s) => s.length > 0)
26
+ }
27
+
28
+ // Load the connection + configs once, then hydrate the form from this block's saved config.
29
+ onMounted(() => {
30
+ store.ensureLoaded().catch(() => {})
31
+ })
32
+ watch(
33
+ saved,
34
+ (config) => {
35
+ draft.monitorIds = config?.monitorIds.join(', ') ?? ''
36
+ draft.sloIds = config?.sloIds.join(', ') ?? ''
37
+ draft.envTag = config?.envTag ?? ''
38
+ },
39
+ { immediate: true },
40
+ )
41
+
42
+ function notifyError(title: string, e: unknown) {
43
+ toast.add({
44
+ title,
45
+ description: e instanceof Error ? e.message : String(e),
46
+ icon: 'i-lucide-triangle-alert',
47
+ color: 'error',
48
+ })
49
+ }
50
+
51
+ async function save() {
52
+ busy.value = true
53
+ try {
54
+ await store.saveConfig(props.block.id, {
55
+ monitorIds: parseIds(draft.monitorIds),
56
+ sloIds: parseIds(draft.sloIds),
57
+ envTag: draft.envTag.trim() || null,
58
+ })
59
+ toast.add({ title: 'Monitoring saved', icon: 'i-lucide-check', color: 'success' })
60
+ } catch (e) {
61
+ notifyError('Could not save the mapping', e)
62
+ } finally {
63
+ busy.value = false
64
+ }
65
+ }
66
+
67
+ async function clear() {
68
+ busy.value = true
69
+ try {
70
+ await store.removeConfig(props.block.id)
71
+ draft.monitorIds = ''
72
+ draft.sloIds = ''
73
+ draft.envTag = ''
74
+ } catch (e) {
75
+ notifyError('Could not clear the mapping', e)
76
+ } finally {
77
+ busy.value = false
78
+ }
79
+ }
80
+ </script>
81
+
82
+ <template>
83
+ <div class="space-y-2">
84
+ <div class="flex items-center justify-between">
85
+ <span class="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
86
+ Post-release health
87
+ </span>
88
+ <UButton
89
+ v-if="saved"
90
+ color="error"
91
+ variant="ghost"
92
+ size="xs"
93
+ icon="i-lucide-trash-2"
94
+ :loading="busy"
95
+ @click="clear"
96
+ >
97
+ Clear
98
+ </UButton>
99
+ </div>
100
+
101
+ <!-- Disabled affordance until an observability integration is connected. -->
102
+ <div
103
+ v-if="!connected"
104
+ class="flex items-center justify-between gap-2 rounded-md border border-slate-800 bg-slate-900/60 px-2 py-1.5 text-[11px] text-slate-400"
105
+ >
106
+ <span>Connect an observability integration to watch this service after releases.</span>
107
+ <UButton
108
+ color="neutral"
109
+ variant="soft"
110
+ size="xs"
111
+ icon="i-lucide-plug"
112
+ title="Connect an observability integration first"
113
+ @click="ui.openObservabilityConnection()"
114
+ >
115
+ Connect
116
+ </UButton>
117
+ </div>
118
+
119
+ <div v-else class="space-y-2">
120
+ <p class="text-[11px] text-slate-500">
121
+ The monitors and SLOs the gate watches after this service ships. Comma-separate ids.
122
+ </p>
123
+ <div class="grid grid-cols-2 gap-2">
124
+ <UFormField label="Monitor ids">
125
+ <UInput v-model="draft.monitorIds" placeholder="123, 456" size="sm" class="w-full" />
126
+ </UFormField>
127
+ <UFormField label="SLO ids">
128
+ <UInput v-model="draft.sloIds" placeholder="abc, def" size="sm" class="w-full" />
129
+ </UFormField>
130
+ </div>
131
+ <UFormField label="Env tag (optional)">
132
+ <UInput v-model="draft.envTag" placeholder="prod" size="sm" class="w-full" />
133
+ </UFormField>
134
+ <div class="flex justify-end">
135
+ <UButton
136
+ color="primary"
137
+ variant="soft"
138
+ size="xs"
139
+ icon="i-lucide-save"
140
+ :loading="busy"
141
+ @click="save"
142
+ >
143
+ Save monitoring
144
+ </UButton>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </template>
@@ -4,27 +4,23 @@
4
4
  // progresses — comment when the PR opens, and comment + close as resolved when it
5
5
  // merges. Each is overridable per task in the inspector. Persisted on the workspace
6
6
  // tracker settings (the selection + Jira project key are preserved on save).
7
- import { ref, watch } from 'vue'
7
+ import { onMounted, ref, watch } from 'vue'
8
8
 
9
- const ui = useUiStore()
10
9
  const tracker = useTrackerStore()
11
10
  const toast = useToast()
12
11
 
13
- const open = computed({
14
- get: () => ui.issueWritebackOpen,
15
- set: (v: boolean) => (v ? ui.openIssueWriteback() : ui.closeIssueWriteback()),
16
- })
17
-
18
12
  const commentOnPrOpen = ref(false)
19
13
  const resolveOnMerge = ref(false)
20
14
  const saving = ref(false)
21
15
 
22
- // Re-sync the local toggles from the store whenever the panel opens.
23
- watch(open, (isOpen) => {
24
- if (!isOpen) return
16
+ // Sync the local toggles from the store on mount (the tab renders when Workspace
17
+ // settings opens) and whenever the stored settings change underneath.
18
+ function hydrate() {
25
19
  commentOnPrOpen.value = tracker.settings.writebackCommentOnPrOpen
26
20
  resolveOnMerge.value = tracker.settings.writebackResolveOnMerge
27
- })
21
+ }
22
+ onMounted(hydrate)
23
+ watch(() => tracker.settings, hydrate, { deep: true })
28
24
 
29
25
  async function save() {
30
26
  saving.value = true
@@ -51,53 +47,45 @@ async function save() {
51
47
  </script>
52
48
 
53
49
  <template>
54
- <UModal v-model:open="open" title="Issue tracker writeback" :ui="{ content: 'max-w-lg' }">
55
- <template #body>
56
- <div class="space-y-4">
57
- <p class="text-xs text-slate-400">
58
- When a task is linked to a tracker issue (GitHub Issues or Jira), write back to it as the
59
- task's pull request progresses. Each toggle is the workspace default and can be overridden
60
- per task in the inspector. GitHub issues close natively; Jira issues transition to the
61
- first status in their <span class="text-slate-300">Done</span> category.
62
- </p>
50
+ <div class="space-y-4">
51
+ <p class="text-xs text-slate-400">
52
+ When a task is linked to a tracker issue (GitHub Issues or Jira), write back to it as the
53
+ task's pull request progresses. Each toggle is the workspace default and can be overridden per
54
+ task in the inspector. GitHub issues close natively; Jira issues transition to the first
55
+ status in their <span class="text-slate-300">Done</span> category.
56
+ </p>
63
57
 
64
- <label
65
- class="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-800/40 p-3"
66
- >
67
- <USwitch v-model="commentOnPrOpen" />
68
- <span class="text-sm">
69
- <span class="block text-slate-200">Comment when a PR opens</span>
70
- <span class="block text-xs text-slate-500">
71
- Post a comment on the linked issue with the new pull request's link.
72
- </span>
73
- </span>
74
- </label>
58
+ <label class="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-800/40 p-3">
59
+ <USwitch v-model="commentOnPrOpen" />
60
+ <span class="text-sm">
61
+ <span class="block text-slate-200">Comment when a PR opens</span>
62
+ <span class="block text-xs text-slate-500">
63
+ Post a comment on the linked issue with the new pull request's link.
64
+ </span>
65
+ </span>
66
+ </label>
75
67
 
76
- <label
77
- class="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-800/40 p-3"
78
- >
79
- <USwitch v-model="resolveOnMerge" />
80
- <span class="text-sm">
81
- <span class="block text-slate-200">Close as resolved when a PR merges</span>
82
- <span class="block text-xs text-slate-500">
83
- Comment that the PR merged, then close / resolve the linked issue.
84
- </span>
85
- </span>
86
- </label>
68
+ <label class="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-800/40 p-3">
69
+ <USwitch v-model="resolveOnMerge" />
70
+ <span class="text-sm">
71
+ <span class="block text-slate-200">Close as resolved when a PR merges</span>
72
+ <span class="block text-xs text-slate-500">
73
+ Comment that the PR merged, then close / resolve the linked issue.
74
+ </span>
75
+ </span>
76
+ </label>
87
77
 
88
- <div class="flex justify-end">
89
- <UButton
90
- color="primary"
91
- variant="soft"
92
- size="sm"
93
- icon="i-lucide-save"
94
- :loading="saving"
95
- @click="save"
96
- >
97
- Save
98
- </UButton>
99
- </div>
100
- </div>
101
- </template>
102
- </UModal>
78
+ <div class="flex justify-end">
79
+ <UButton
80
+ color="primary"
81
+ variant="soft"
82
+ size="sm"
83
+ icon="i-lucide-save"
84
+ :loading="saving"
85
+ @click="save"
86
+ >
87
+ Save
88
+ </UButton>
89
+ </div>
90
+ </div>
103
91
  </template>