@cat-factory/app 0.79.2 → 0.80.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/app/components/documents/TaskContextDocs.vue +10 -7
- package/app/components/fragments/FragmentLibraryManager.vue +2 -10
- package/app/components/github/AddServiceFromRepoModal.vue +2 -1
- package/app/components/panels/InspectorPanel.vue +35 -18
- package/app/components/panels/inspector/ContainerSummary.vue +8 -2
- package/app/components/panels/inspector/EpicChildren.vue +9 -7
- package/app/components/panels/inspector/FrontendConfig.vue +175 -248
- package/app/components/panels/inspector/InspectorSection.vue +60 -0
- package/app/components/panels/inspector/ServiceConnections.vue +10 -10
- package/app/components/panels/inspector/ServiceFragments.vue +14 -9
- package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +8 -7
- package/app/components/panels/inspector/ServiceTestConfig.vue +45 -57
- package/app/components/panels/inspector/TaskAgentConfig.vue +8 -5
- package/app/components/panels/inspector/TaskDependencies.vue +12 -8
- package/app/components/panels/inspector/TaskEstimateBadge.vue +9 -8
- package/app/components/panels/inspector/TaskExecution.vue +9 -2
- package/app/components/panels/inspector/TaskRunSettings.vue +13 -2
- package/app/components/panels/inspector/TaskStructure.vue +9 -2
- package/app/components/tasks/TaskContextIssues.vue +10 -7
- package/app/stores/fragmentLibrary.ts +4 -4
- package/i18n/locales/en.json +25 -3
- package/i18n/locales/es.json +25 -3
- package/i18n/locales/fr.json +25 -3
- package/i18n/locales/he.json +25 -3
- package/i18n/locales/ja.json +25 -3
- package/i18n/locales/pl.json +25 -3
- package/i18n/locales/tr.json +25 -3
- package/i18n/locales/uk.json +25 -3
- package/package.json +2 -2
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Block } from '~/types/domain'
|
|
3
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
3
4
|
|
|
4
5
|
// Service-level best-practice fragments (frame blocks). These are the programming
|
|
5
6
|
// standards/guidelines for the whole service; at run time their bodies are folded
|
|
6
7
|
// into the prompt of every `code-aware` agent on tasks under this service. Drawn from
|
|
7
8
|
// the board's merged fragment catalog (built-in ∪ registered ∪ account ∪ workspace,
|
|
8
9
|
// via the fragments store; static pool when the library is off), grouped by category.
|
|
9
|
-
|
|
10
|
+
// `defaultOpen` expands the section on surfaces that embed this as the primary content
|
|
11
|
+
// (the add-service modal); the inspector leaves it collapsed.
|
|
12
|
+
const props = defineProps<{ block: Block; defaultOpen?: boolean }>()
|
|
10
13
|
|
|
11
14
|
const board = useBoardStore()
|
|
12
15
|
const fragments = useFragmentsStore()
|
|
@@ -76,11 +79,13 @@ function removeFragment(id: string) {
|
|
|
76
79
|
</script>
|
|
77
80
|
|
|
78
81
|
<template>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
<InspectorSection
|
|
83
|
+
:title="t('inspector.fragments.serviceTitle')"
|
|
84
|
+
:hint="t('inspector.fragments.serviceHint')"
|
|
85
|
+
:count="selectedFragments.length"
|
|
86
|
+
:default-open="props.defaultOpen"
|
|
87
|
+
>
|
|
88
|
+
<template #actions>
|
|
84
89
|
<UDropdownMenu :items="fragmentMenu">
|
|
85
90
|
<UButton
|
|
86
91
|
size="xs"
|
|
@@ -90,8 +95,8 @@ function removeFragment(id: string) {
|
|
|
90
95
|
trailing-icon="i-lucide-chevron-down"
|
|
91
96
|
/>
|
|
92
97
|
</UDropdownMenu>
|
|
93
|
-
</
|
|
94
|
-
<div v-if="selectedFragments.length" class="
|
|
98
|
+
</template>
|
|
99
|
+
<div v-if="selectedFragments.length" class="flex flex-wrap gap-1">
|
|
95
100
|
<UBadge
|
|
96
101
|
v-for="f in selectedFragments"
|
|
97
102
|
:key="f.id"
|
|
@@ -108,5 +113,5 @@ function removeFragment(id: string) {
|
|
|
108
113
|
<div v-else class="text-[11px] text-slate-500">
|
|
109
114
|
{{ t('inspector.fragments.serviceEmpty') }}
|
|
110
115
|
</div>
|
|
111
|
-
</
|
|
116
|
+
</InspectorSection>
|
|
112
117
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, reactive, ref, watch } from 'vue'
|
|
3
3
|
import type { Block } from '~/types/domain'
|
|
4
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
4
5
|
|
|
5
6
|
// Per-service (frame) post-release-health mapping: which observability monitors/SLOs the
|
|
6
7
|
// `post-release-health` gate watches after this service's PRs ship. Keyed by THIS block's
|
|
@@ -89,11 +90,11 @@ async function clear() {
|
|
|
89
90
|
</script>
|
|
90
91
|
|
|
91
92
|
<template>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
<InspectorSection
|
|
94
|
+
:title="t('inspector.releaseHealth.title')"
|
|
95
|
+
:hint="t('inspector.releaseHealth.sectionHint')"
|
|
96
|
+
>
|
|
97
|
+
<template #actions>
|
|
97
98
|
<UButton
|
|
98
99
|
v-if="saved"
|
|
99
100
|
color="error"
|
|
@@ -105,7 +106,7 @@ async function clear() {
|
|
|
105
106
|
>
|
|
106
107
|
{{ t('inspector.releaseHealth.clear') }}
|
|
107
108
|
</UButton>
|
|
108
|
-
</
|
|
109
|
+
</template>
|
|
109
110
|
|
|
110
111
|
<!-- Disabled affordance until an observability integration is connected. -->
|
|
111
112
|
<div
|
|
@@ -153,5 +154,5 @@ async function clear() {
|
|
|
153
154
|
</UButton>
|
|
154
155
|
</div>
|
|
155
156
|
</div>
|
|
156
|
-
</
|
|
157
|
+
</InspectorSection>
|
|
157
158
|
</template>
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
ProvisioningServiceDirCandidate,
|
|
18
18
|
} from '@cat-factory/contracts'
|
|
19
19
|
import RepoTreeBrowser from '~/components/github/RepoTreeBrowser.vue'
|
|
20
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
20
21
|
|
|
21
22
|
// Service-level (frame) configuration: the service-owned PROVISIONING — the provision
|
|
22
23
|
// TYPE this service produces (`infraless` / `docker-compose` / `kubernetes` / `custom`)
|
|
@@ -30,6 +31,9 @@ const props = defineProps<{
|
|
|
30
31
|
// Repo backing this service, supplied by the add-service modal when the block is
|
|
31
32
|
// too fresh to be resolvable from the stores yet. Otherwise resolved below.
|
|
32
33
|
repo?: { githubId: number; directory?: string | null }
|
|
34
|
+
// Expands the section on surfaces that embed this as the primary content (the
|
|
35
|
+
// add-service modal); the inspector leaves it collapsed.
|
|
36
|
+
defaultOpen?: boolean
|
|
33
37
|
}>()
|
|
34
38
|
|
|
35
39
|
const board = useBoardStore()
|
|
@@ -403,11 +407,11 @@ function setSize(value: InstanceSize) {
|
|
|
403
407
|
</script>
|
|
404
408
|
|
|
405
409
|
<template>
|
|
406
|
-
<
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
410
|
+
<InspectorSection
|
|
411
|
+
:title="t('inspector.testConfig.title')"
|
|
412
|
+
:hint="t('inspector.testConfig.hint')"
|
|
413
|
+
:default-open="props.defaultOpen"
|
|
414
|
+
>
|
|
411
415
|
<div class="space-y-1">
|
|
412
416
|
<span class="text-[11px] text-slate-400">{{ t('inspector.testConfig.provisionType') }}</span>
|
|
413
417
|
<div class="flex flex-wrap gap-1">
|
|
@@ -839,60 +843,44 @@ function setSize(value: InstanceSize) {
|
|
|
839
843
|
|
|
840
844
|
<!-- Provisioning hints: advisory inputs to the ephemeral-environment provisioner.
|
|
841
845
|
Collapsed by default — most services never tune them. -->
|
|
842
|
-
<
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
>
|
|
848
|
-
<
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
t('inspector.testConfig.cloudProvider')
|
|
863
|
-
}}</span>
|
|
864
|
-
<div class="flex flex-wrap gap-1">
|
|
865
|
-
<UButton
|
|
866
|
-
v-for="p in PROVIDERS"
|
|
867
|
-
:key="p.value"
|
|
868
|
-
:color="effectiveProvider === p.value ? 'primary' : 'neutral'"
|
|
869
|
-
:variant="effectiveProvider === p.value ? 'soft' : 'ghost'"
|
|
870
|
-
size="xs"
|
|
871
|
-
@click="setProvider(p.value)"
|
|
872
|
-
>
|
|
873
|
-
{{ p.label }}
|
|
874
|
-
</UButton>
|
|
875
|
-
</div>
|
|
846
|
+
<InspectorSection
|
|
847
|
+
v-model:open="showProvisioning"
|
|
848
|
+
:title="t('inspector.testConfig.provisioningTitle')"
|
|
849
|
+
:hint="t('inspector.testConfig.provisioningHint')"
|
|
850
|
+
>
|
|
851
|
+
<div class="space-y-1">
|
|
852
|
+
<span class="text-[11px] text-slate-400">{{
|
|
853
|
+
t('inspector.testConfig.cloudProvider')
|
|
854
|
+
}}</span>
|
|
855
|
+
<div class="flex flex-wrap gap-1">
|
|
856
|
+
<UButton
|
|
857
|
+
v-for="p in PROVIDERS"
|
|
858
|
+
:key="p.value"
|
|
859
|
+
:color="effectiveProvider === p.value ? 'primary' : 'neutral'"
|
|
860
|
+
:variant="effectiveProvider === p.value ? 'soft' : 'ghost'"
|
|
861
|
+
size="xs"
|
|
862
|
+
@click="setProvider(p.value)"
|
|
863
|
+
>
|
|
864
|
+
{{ p.label }}
|
|
865
|
+
</UButton>
|
|
876
866
|
</div>
|
|
867
|
+
</div>
|
|
877
868
|
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
{{ s.label }}
|
|
892
|
-
</UButton>
|
|
893
|
-
</div>
|
|
869
|
+
<div class="space-y-1">
|
|
870
|
+
<span class="text-[11px] text-slate-400">{{ t('inspector.testConfig.instanceSize') }}</span>
|
|
871
|
+
<div class="flex flex-wrap gap-1">
|
|
872
|
+
<UButton
|
|
873
|
+
v-for="s in SIZES"
|
|
874
|
+
:key="s.value"
|
|
875
|
+
:color="(block.instanceSize ?? 'medium') === s.value ? 'primary' : 'neutral'"
|
|
876
|
+
:variant="(block.instanceSize ?? 'medium') === s.value ? 'soft' : 'ghost'"
|
|
877
|
+
size="xs"
|
|
878
|
+
@click="setSize(s.value)"
|
|
879
|
+
>
|
|
880
|
+
{{ s.label }}
|
|
881
|
+
</UButton>
|
|
894
882
|
</div>
|
|
895
883
|
</div>
|
|
896
|
-
</
|
|
897
|
-
</
|
|
884
|
+
</InspectorSection>
|
|
885
|
+
</InspectorSection>
|
|
898
886
|
</template>
|
|
@@ -3,6 +3,7 @@ import { computed } from 'vue'
|
|
|
3
3
|
import type { Block } from '~/types/domain'
|
|
4
4
|
import { useAgentConfigStore } from '~/stores/agentConfig'
|
|
5
5
|
import { useExecutionStore } from '~/stores/execution'
|
|
6
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
6
7
|
|
|
7
8
|
// Task-level configuration contributed by the agents in this task's selected
|
|
8
9
|
// pipeline (e.g. the Playwright agent's e2e target: CI vs ephemeral). Each value is
|
|
@@ -49,10 +50,12 @@ function setValue(id: string, value: string) {
|
|
|
49
50
|
</script>
|
|
50
51
|
|
|
51
52
|
<template>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
<InspectorSection
|
|
54
|
+
v-if="descriptors.length"
|
|
55
|
+
:title="t('inspector.agentConfig.title')"
|
|
56
|
+
:hint="t('inspector.agentConfig.hint')"
|
|
57
|
+
:count="descriptors.length"
|
|
58
|
+
>
|
|
56
59
|
<div v-for="d in descriptors" :key="d.id" class="space-y-1">
|
|
57
60
|
<div class="flex items-center justify-between">
|
|
58
61
|
<span class="text-[11px] text-slate-400">{{ d.label }}</span>
|
|
@@ -80,5 +83,5 @@ function setValue(id: string, value: string) {
|
|
|
80
83
|
</div>
|
|
81
84
|
<p class="text-[11px] leading-snug text-slate-500">{{ d.description }}</p>
|
|
82
85
|
</div>
|
|
83
|
-
</
|
|
86
|
+
</InspectorSection>
|
|
84
87
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Block } from '~/types/domain'
|
|
3
3
|
import EmptyState from '~/components/common/EmptyState.vue'
|
|
4
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
4
5
|
|
|
5
6
|
const props = defineProps<{ block: Block }>()
|
|
6
7
|
|
|
@@ -42,11 +43,14 @@ async function removeDep(dep: Block) {
|
|
|
42
43
|
</script>
|
|
43
44
|
|
|
44
45
|
<template>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
<!-- The warning flag keeps "this task is blocked" visible even while collapsed. -->
|
|
47
|
+
<InspectorSection
|
|
48
|
+
:title="t('inspector.dependencies.title')"
|
|
49
|
+
:hint="t('inspector.dependencies.hint')"
|
|
50
|
+
:count="deps.length"
|
|
51
|
+
:warning="!runnable"
|
|
52
|
+
>
|
|
53
|
+
<template #actions>
|
|
50
54
|
<UDropdownMenu v-if="depMenu.length" :items="depMenu">
|
|
51
55
|
<UButton
|
|
52
56
|
size="xs"
|
|
@@ -56,7 +60,7 @@ async function removeDep(dep: Block) {
|
|
|
56
60
|
trailing-icon="i-lucide-chevron-down"
|
|
57
61
|
/>
|
|
58
62
|
</UDropdownMenu>
|
|
59
|
-
</
|
|
63
|
+
</template>
|
|
60
64
|
<div v-if="deps.length" class="flex flex-wrap gap-1">
|
|
61
65
|
<UBadge
|
|
62
66
|
v-for="d in deps"
|
|
@@ -82,8 +86,8 @@ async function removeDep(dep: Block) {
|
|
|
82
86
|
icon="i-lucide-git-branch"
|
|
83
87
|
:title="t('inspector.dependencies.empty')"
|
|
84
88
|
/>
|
|
85
|
-
<div v-if="!runnable" class="
|
|
89
|
+
<div v-if="!runnable" class="text-[10px] text-amber-400">
|
|
86
90
|
{{ t('inspector.dependencies.blocked') }}
|
|
87
91
|
</div>
|
|
88
|
-
</
|
|
92
|
+
</InspectorSection>
|
|
89
93
|
</template>
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// used to gate consensus steps. Hidden when no estimate exists.
|
|
5
5
|
import { computed } from 'vue'
|
|
6
6
|
import type { Block } from '~/types/domain'
|
|
7
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
7
8
|
|
|
8
9
|
const props = defineProps<{ block: Block }>()
|
|
9
10
|
const { t, n } = useI18n()
|
|
@@ -28,13 +29,13 @@ function barClass(n: number): string {
|
|
|
28
29
|
</script>
|
|
29
30
|
|
|
30
31
|
<template>
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
<InspectorSection
|
|
33
|
+
v-if="estimate"
|
|
34
|
+
:title="t('inspector.estimate.title')"
|
|
35
|
+
:hint="t('inspector.estimate.hint')"
|
|
36
|
+
icon="i-lucide-gauge"
|
|
37
|
+
default-open
|
|
38
|
+
>
|
|
38
39
|
<div class="space-y-1.5 rounded-lg border border-slate-800 bg-slate-900/40 p-2.5">
|
|
39
40
|
<div v-for="axis in AXES" :key="axis.key" class="flex items-center gap-2">
|
|
40
41
|
<span class="w-20 shrink-0 text-xs text-slate-400">{{ axis.label }}</span>
|
|
@@ -53,5 +54,5 @@ function barClass(n: number): string {
|
|
|
53
54
|
{{ estimate.rationale }}
|
|
54
55
|
</p>
|
|
55
56
|
</div>
|
|
56
|
-
</
|
|
57
|
+
</InspectorSection>
|
|
57
58
|
</template>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import AgentFailureCard from '~/components/board/AgentFailureCard.vue'
|
|
11
11
|
import AgentFailureHistory from '~/components/board/AgentFailureHistory.vue'
|
|
12
12
|
import EmptyState from '~/components/common/EmptyState.vue'
|
|
13
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
13
14
|
|
|
14
15
|
const props = defineProps<{ block: Block }>()
|
|
15
16
|
|
|
@@ -185,7 +186,13 @@ async function mergePr() {
|
|
|
185
186
|
</script>
|
|
186
187
|
|
|
187
188
|
<template>
|
|
188
|
-
|
|
189
|
+
<!-- The live run surface stays open by default: it is what a user selecting a
|
|
190
|
+
running task is looking for (and what the e2e specs assert on). -->
|
|
191
|
+
<InspectorSection
|
|
192
|
+
:title="t('inspector.execution.title')"
|
|
193
|
+
:hint="t('inspector.execution.hint')"
|
|
194
|
+
default-open
|
|
195
|
+
>
|
|
189
196
|
<!-- running pipeline -->
|
|
190
197
|
<div v-if="instance">
|
|
191
198
|
<div class="mb-1 flex items-center justify-between">
|
|
@@ -472,5 +479,5 @@ async function mergePr() {
|
|
|
472
479
|
>
|
|
473
480
|
{{ t('inspector.execution.mergePr') }}
|
|
474
481
|
</UButton>
|
|
475
|
-
</
|
|
482
|
+
</InspectorSection>
|
|
476
483
|
</template>
|
|
@@ -5,6 +5,7 @@ import type { Block } from '~/types/domain'
|
|
|
5
5
|
import type { WritebackOverride } from '~/types/tracker'
|
|
6
6
|
import { mergePresetOptionLabel, mergePresetThresholds } from '~/utils/mergePreset'
|
|
7
7
|
import { pipelineAllowedForFrame } from '~/utils/pipeline'
|
|
8
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
8
9
|
|
|
9
10
|
const props = defineProps<{ block: Block }>()
|
|
10
11
|
|
|
@@ -256,7 +257,10 @@ const technicalLabel = computed(() => {
|
|
|
256
257
|
</script>
|
|
257
258
|
|
|
258
259
|
<template>
|
|
259
|
-
<
|
|
260
|
+
<InspectorSection
|
|
261
|
+
:title="t('inspector.runSettings.title')"
|
|
262
|
+
:hint="t('inspector.runSettings.hint')"
|
|
263
|
+
>
|
|
260
264
|
<!-- pipeline -->
|
|
261
265
|
<div>
|
|
262
266
|
<div class="mb-1 flex items-center justify-between">
|
|
@@ -287,6 +291,9 @@ const technicalLabel = computed(() => {
|
|
|
287
291
|
<div v-else class="text-[11px] text-slate-500">
|
|
288
292
|
{{ t('inspector.runSettings.pipelineEmpty') }}
|
|
289
293
|
</div>
|
|
294
|
+
<p class="mt-1 text-[11px] leading-snug text-slate-500">
|
|
295
|
+
{{ t('inspector.runSettings.pipelineHint') }}
|
|
296
|
+
</p>
|
|
290
297
|
</div>
|
|
291
298
|
|
|
292
299
|
<!-- merge policy preset -->
|
|
@@ -322,6 +329,9 @@ const technicalLabel = computed(() => {
|
|
|
322
329
|
<div v-else class="text-[11px] text-slate-500">
|
|
323
330
|
{{ t('inspector.runSettings.mergePresetEmpty') }}
|
|
324
331
|
</div>
|
|
332
|
+
<p class="mt-1 text-[11px] leading-snug text-slate-500">
|
|
333
|
+
{{ t('inspector.runSettings.mergePolicyHint') }}
|
|
334
|
+
</p>
|
|
325
335
|
</div>
|
|
326
336
|
|
|
327
337
|
<!-- model preset -->
|
|
@@ -393,6 +403,7 @@ const technicalLabel = computed(() => {
|
|
|
393
403
|
</div>
|
|
394
404
|
</div>
|
|
395
405
|
<p class="mt-1 text-[11px] text-slate-500">
|
|
406
|
+
{{ t('inspector.runSettings.modelPresetHint') }}
|
|
396
407
|
{{ t('inspector.runSettings.modelPresetChangeHint') }}
|
|
397
408
|
</p>
|
|
398
409
|
</div>
|
|
@@ -558,5 +569,5 @@ const technicalLabel = computed(() => {
|
|
|
558
569
|
{{ t('inspector.runSettings.autoStartHint') }}
|
|
559
570
|
</div>
|
|
560
571
|
</div>
|
|
561
|
-
</
|
|
572
|
+
</InspectorSection>
|
|
562
573
|
</template>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { Block } from '~/types/domain'
|
|
3
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
3
4
|
|
|
4
5
|
const props = defineProps<{ block: Block }>()
|
|
5
6
|
|
|
@@ -76,7 +77,7 @@ function removeFragment(id: string) {
|
|
|
76
77
|
</script>
|
|
77
78
|
|
|
78
79
|
<template>
|
|
79
|
-
<
|
|
80
|
+
<InspectorSection :title="t('inspector.structure.title')" :hint="t('inspector.structure.hint')">
|
|
80
81
|
<!-- module assignment -->
|
|
81
82
|
<div>
|
|
82
83
|
<div class="mb-1 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
|
|
@@ -89,6 +90,9 @@ function removeFragment(id: string) {
|
|
|
89
90
|
:placeholder="t('inspector.structure.modulePlaceholder')"
|
|
90
91
|
icon="i-lucide-package"
|
|
91
92
|
/>
|
|
93
|
+
<p class="mt-1 text-[11px] leading-snug text-slate-500">
|
|
94
|
+
{{ t('inspector.structure.moduleHint') }}
|
|
95
|
+
</p>
|
|
92
96
|
</div>
|
|
93
97
|
|
|
94
98
|
<!-- best practices (prompt fragments) -->
|
|
@@ -124,6 +128,9 @@ function removeFragment(id: string) {
|
|
|
124
128
|
<div v-else class="text-[11px] text-slate-500">
|
|
125
129
|
{{ t('inspector.structure.bestPracticesEmpty') }}
|
|
126
130
|
</div>
|
|
131
|
+
<p class="mt-1 text-[11px] leading-snug text-slate-500">
|
|
132
|
+
{{ t('inspector.structure.bestPracticesHint') }}
|
|
133
|
+
</p>
|
|
127
134
|
</div>
|
|
128
|
-
</
|
|
135
|
+
</InspectorSection>
|
|
129
136
|
</template>
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// the structured nature of an issue is visible at a glance.
|
|
7
7
|
import type { DropdownMenuItem } from '@nuxt/ui'
|
|
8
8
|
import type { Block, TaskSourceKind } from '~/types/domain'
|
|
9
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
9
10
|
|
|
10
11
|
const props = defineProps<{ block: Block }>()
|
|
11
12
|
|
|
@@ -53,17 +54,19 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
53
54
|
</script>
|
|
54
55
|
|
|
55
56
|
<template>
|
|
56
|
-
<
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
<InspectorSection
|
|
58
|
+
v-if="tasks.available"
|
|
59
|
+
:title="t('tasks.contextIssues.title')"
|
|
60
|
+
:hint="t('tasks.contextIssues.hint')"
|
|
61
|
+
:count="linked.length"
|
|
62
|
+
>
|
|
63
|
+
<template #actions>
|
|
61
64
|
<UDropdownMenu :items="attachMenu" :content="{ side: 'bottom', align: 'end' }">
|
|
62
65
|
<UButton color="neutral" variant="soft" size="xs" icon="i-lucide-plus">{{
|
|
63
66
|
t('tasks.contextIssues.attach')
|
|
64
67
|
}}</UButton>
|
|
65
68
|
</UDropdownMenu>
|
|
66
|
-
</
|
|
69
|
+
</template>
|
|
67
70
|
|
|
68
71
|
<div v-if="linked.length" class="space-y-1">
|
|
69
72
|
<a
|
|
@@ -87,5 +90,5 @@ const attachMenu = computed<DropdownMenuItem[][]>(() => {
|
|
|
87
90
|
<p v-else class="text-[11px] text-slate-500">
|
|
88
91
|
{{ t('tasks.contextIssues.emptyHint') }}
|
|
89
92
|
</p>
|
|
90
|
-
</
|
|
93
|
+
</InspectorSection>
|
|
91
94
|
</template>
|
|
@@ -42,8 +42,8 @@ function fragmentLibrarySetup(kind: FragmentOwnerKind, resolveOwnerId: () => str
|
|
|
42
42
|
const resolved = ref<ResolvedFragment[]>([])
|
|
43
43
|
/** Linked guideline repos for this owner. */
|
|
44
44
|
const sources = ref<FragmentSource[]>([])
|
|
45
|
-
/** Per-source "changes available"
|
|
46
|
-
const sourceChanges = ref<Record<string,
|
|
45
|
+
/** Per-source "changes available" flag from the last status check. */
|
|
46
|
+
const sourceChanges = ref<Record<string, boolean>>({})
|
|
47
47
|
const loading = ref(false)
|
|
48
48
|
/**
|
|
49
49
|
* Account-tier document fragments only: the workspace whose stored
|
|
@@ -166,12 +166,12 @@ function fragmentLibrarySetup(kind: FragmentOwnerKind, resolveOwnerId: () => str
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
/**
|
|
169
|
+
/** Lightweight commit-version "check for changes" for a source; caches the flag. */
|
|
170
170
|
async function checkSource(sourceId: string) {
|
|
171
171
|
const status = await api.fragmentSourceStatus(kind, requireOwnerId(), sourceId)
|
|
172
172
|
sourceChanges.value = {
|
|
173
173
|
...sourceChanges.value,
|
|
174
|
-
[sourceId]: status.changed
|
|
174
|
+
[sourceId]: status.changed,
|
|
175
175
|
}
|
|
176
176
|
return status
|
|
177
177
|
}
|