@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
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
PreviewStatus,
|
|
12
12
|
} from '~/types/domain'
|
|
13
13
|
import FrontendBindingsResolved from '~/components/panels/inspector/FrontendBindingsResolved.vue'
|
|
14
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
14
15
|
|
|
15
16
|
// Frontend-frame (`type: 'frontend'`) configuration: how to build, serve, and mock this
|
|
16
17
|
// frontend for a self-contained UI test (+ an optional browsable preview on local/node),
|
|
@@ -96,6 +97,7 @@ function setBindingSource(index: number, value: string) {
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function addBinding() {
|
|
100
|
+
// InspectorSection expands its group when a header action is clicked, so the new row is visible.
|
|
99
101
|
save({ backendBindings: [...bindings.value, { envVar: '', source: { kind: 'mock' } }] })
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -209,14 +211,10 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
209
211
|
</script>
|
|
210
212
|
|
|
211
213
|
<template>
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
217
|
-
{{ t('inspector.frontendConfig.hint') }}
|
|
218
|
-
</p>
|
|
219
|
-
|
|
214
|
+
<InspectorSection
|
|
215
|
+
:title="t('inspector.frontendConfig.title')"
|
|
216
|
+
:hint="t('inspector.frontendConfig.hint')"
|
|
217
|
+
>
|
|
220
218
|
<!-- Detect from repo: propose a config the user reviews + applies. Non-binding — nothing is
|
|
221
219
|
persisted until Apply. Only shown when the frame is linked to a repo. -->
|
|
222
220
|
<div
|
|
@@ -289,218 +287,179 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
289
287
|
</div>
|
|
290
288
|
|
|
291
289
|
<!-- Build: package manager + frontend directory + install command + build script + output dir -->
|
|
292
|
-
<
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
290
|
+
<InspectorSection v-model:open="showBuild" :title="t('inspector.frontendConfig.groups.build')">
|
|
291
|
+
<div class="space-y-1">
|
|
292
|
+
<span class="text-[11px] text-slate-400">{{
|
|
293
|
+
t('inspector.frontendConfig.packageManager')
|
|
294
|
+
}}</span>
|
|
295
|
+
<div class="flex flex-wrap gap-1">
|
|
296
|
+
<UButton
|
|
297
|
+
v-for="pm in PACKAGE_MANAGERS"
|
|
298
|
+
:key="pm"
|
|
299
|
+
:color="packageManager === pm ? 'primary' : 'neutral'"
|
|
300
|
+
:variant="packageManager === pm ? 'soft' : 'ghost'"
|
|
301
|
+
size="xs"
|
|
302
|
+
@click="save({ packageManager: pm })"
|
|
303
|
+
>
|
|
304
|
+
{{ pm }}
|
|
305
|
+
</UButton>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
|
|
309
|
+
<div class="space-y-1">
|
|
310
|
+
<label class="text-[11px] text-slate-400">{{
|
|
311
|
+
t('inspector.frontendConfig.directory')
|
|
312
|
+
}}</label>
|
|
313
|
+
<UInput
|
|
314
|
+
:model-value="config.directory ?? ''"
|
|
315
|
+
size="xs"
|
|
316
|
+
class="font-mono"
|
|
317
|
+
maxlength="400"
|
|
318
|
+
placeholder="frontend/"
|
|
319
|
+
@blur="(e: FocusEvent) => saveText('directory', (e.target as HTMLInputElement).value)"
|
|
320
|
+
@keydown.enter="
|
|
321
|
+
(e: KeyboardEvent) => saveText('directory', (e.target as HTMLInputElement).value)
|
|
322
|
+
"
|
|
301
323
|
/>
|
|
302
|
-
|
|
303
|
-
|
|
324
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
325
|
+
{{ t('inspector.frontendConfig.directoryHint') }}
|
|
326
|
+
</p>
|
|
327
|
+
</div>
|
|
304
328
|
|
|
305
|
-
<div
|
|
306
|
-
<
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
329
|
+
<div class="space-y-1">
|
|
330
|
+
<label class="text-[11px] text-slate-400">{{
|
|
331
|
+
t('inspector.frontendConfig.installCommand')
|
|
332
|
+
}}</label>
|
|
333
|
+
<UInput
|
|
334
|
+
:model-value="config.installCommand ?? ''"
|
|
335
|
+
size="xs"
|
|
336
|
+
class="font-mono"
|
|
337
|
+
maxlength="400"
|
|
338
|
+
placeholder="pnpm install --frozen-lockfile"
|
|
339
|
+
@blur="
|
|
340
|
+
(e: FocusEvent) => saveText('installCommand', (e.target as HTMLInputElement).value)
|
|
341
|
+
"
|
|
342
|
+
@keydown.enter="
|
|
343
|
+
(e: KeyboardEvent) => saveText('installCommand', (e.target as HTMLInputElement).value)
|
|
344
|
+
"
|
|
345
|
+
/>
|
|
346
|
+
</div>
|
|
323
347
|
|
|
348
|
+
<div class="grid grid-cols-2 gap-2">
|
|
324
349
|
<div class="space-y-1">
|
|
325
350
|
<label class="text-[11px] text-slate-400">{{
|
|
326
|
-
t('inspector.frontendConfig.
|
|
351
|
+
t('inspector.frontendConfig.buildScript')
|
|
327
352
|
}}</label>
|
|
328
353
|
<UInput
|
|
329
|
-
:model-value="config.
|
|
354
|
+
:model-value="config.buildScript ?? ''"
|
|
330
355
|
size="xs"
|
|
331
356
|
class="font-mono"
|
|
332
|
-
maxlength="
|
|
333
|
-
placeholder="
|
|
334
|
-
@blur="(e: FocusEvent) => saveText('
|
|
357
|
+
maxlength="200"
|
|
358
|
+
placeholder="build"
|
|
359
|
+
@blur="(e: FocusEvent) => saveText('buildScript', (e.target as HTMLInputElement).value)"
|
|
335
360
|
@keydown.enter="
|
|
336
|
-
(e: KeyboardEvent) => saveText('
|
|
361
|
+
(e: KeyboardEvent) => saveText('buildScript', (e.target as HTMLInputElement).value)
|
|
337
362
|
"
|
|
338
363
|
/>
|
|
339
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
340
|
-
{{ t('inspector.frontendConfig.directoryHint') }}
|
|
341
|
-
</p>
|
|
342
364
|
</div>
|
|
343
|
-
|
|
344
365
|
<div class="space-y-1">
|
|
345
366
|
<label class="text-[11px] text-slate-400">{{
|
|
346
|
-
t('inspector.frontendConfig.
|
|
367
|
+
t('inspector.frontendConfig.outputDir')
|
|
347
368
|
}}</label>
|
|
348
369
|
<UInput
|
|
349
|
-
:model-value="config.
|
|
370
|
+
:model-value="config.outputDir ?? ''"
|
|
350
371
|
size="xs"
|
|
351
372
|
class="font-mono"
|
|
352
373
|
maxlength="400"
|
|
353
|
-
placeholder="
|
|
354
|
-
@blur="
|
|
355
|
-
(e: FocusEvent) => saveText('installCommand', (e.target as HTMLInputElement).value)
|
|
356
|
-
"
|
|
374
|
+
placeholder="dist"
|
|
375
|
+
@blur="(e: FocusEvent) => saveText('outputDir', (e.target as HTMLInputElement).value)"
|
|
357
376
|
@keydown.enter="
|
|
358
|
-
(e: KeyboardEvent) => saveText('
|
|
377
|
+
(e: KeyboardEvent) => saveText('outputDir', (e.target as HTMLInputElement).value)
|
|
359
378
|
"
|
|
360
379
|
/>
|
|
361
380
|
</div>
|
|
362
|
-
|
|
363
|
-
<div class="grid grid-cols-2 gap-2">
|
|
364
|
-
<div class="space-y-1">
|
|
365
|
-
<label class="text-[11px] text-slate-400">{{
|
|
366
|
-
t('inspector.frontendConfig.buildScript')
|
|
367
|
-
}}</label>
|
|
368
|
-
<UInput
|
|
369
|
-
:model-value="config.buildScript ?? ''"
|
|
370
|
-
size="xs"
|
|
371
|
-
class="font-mono"
|
|
372
|
-
maxlength="200"
|
|
373
|
-
placeholder="build"
|
|
374
|
-
@blur="
|
|
375
|
-
(e: FocusEvent) => saveText('buildScript', (e.target as HTMLInputElement).value)
|
|
376
|
-
"
|
|
377
|
-
@keydown.enter="
|
|
378
|
-
(e: KeyboardEvent) => saveText('buildScript', (e.target as HTMLInputElement).value)
|
|
379
|
-
"
|
|
380
|
-
/>
|
|
381
|
-
</div>
|
|
382
|
-
<div class="space-y-1">
|
|
383
|
-
<label class="text-[11px] text-slate-400">{{
|
|
384
|
-
t('inspector.frontendConfig.outputDir')
|
|
385
|
-
}}</label>
|
|
386
|
-
<UInput
|
|
387
|
-
:model-value="config.outputDir ?? ''"
|
|
388
|
-
size="xs"
|
|
389
|
-
class="font-mono"
|
|
390
|
-
maxlength="400"
|
|
391
|
-
placeholder="dist"
|
|
392
|
-
@blur="(e: FocusEvent) => saveText('outputDir', (e.target as HTMLInputElement).value)"
|
|
393
|
-
@keydown.enter="
|
|
394
|
-
(e: KeyboardEvent) => saveText('outputDir', (e.target as HTMLInputElement).value)
|
|
395
|
-
"
|
|
396
|
-
/>
|
|
397
|
-
</div>
|
|
398
|
-
</div>
|
|
399
381
|
</div>
|
|
400
|
-
</
|
|
382
|
+
</InspectorSection>
|
|
401
383
|
|
|
402
384
|
<!-- Serve: mode (static vs command) + serve script (command mode) + port -->
|
|
403
|
-
<
|
|
404
|
-
<
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
/>
|
|
413
|
-
{{ t('inspector.frontendConfig.groups.serve') }}
|
|
414
|
-
</button>
|
|
415
|
-
|
|
416
|
-
<div v-if="showServe" class="mt-2 space-y-3">
|
|
417
|
-
<div class="space-y-1">
|
|
418
|
-
<span class="text-[11px] text-slate-400">{{
|
|
419
|
-
t('inspector.frontendConfig.serveMode')
|
|
420
|
-
}}</span>
|
|
421
|
-
<div class="flex flex-wrap gap-1">
|
|
422
|
-
<UButton
|
|
423
|
-
:color="serveMode === 'static' ? 'primary' : 'neutral'"
|
|
424
|
-
:variant="serveMode === 'static' ? 'soft' : 'ghost'"
|
|
425
|
-
size="xs"
|
|
426
|
-
@click="save({ serveMode: 'static' })"
|
|
427
|
-
>
|
|
428
|
-
{{ t('inspector.frontendConfig.serveStatic') }}
|
|
429
|
-
</UButton>
|
|
430
|
-
<UButton
|
|
431
|
-
:color="serveMode === 'command' ? 'primary' : 'neutral'"
|
|
432
|
-
:variant="serveMode === 'command' ? 'soft' : 'ghost'"
|
|
433
|
-
size="xs"
|
|
434
|
-
@click="save({ serveMode: 'command' })"
|
|
435
|
-
>
|
|
436
|
-
{{ t('inspector.frontendConfig.serveCommand') }}
|
|
437
|
-
</UButton>
|
|
438
|
-
</div>
|
|
439
|
-
<!-- Explain each mode, and disambiguate from the separate envInjection axis. -->
|
|
440
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
441
|
-
<span class="text-slate-400">{{ t('inspector.frontendConfig.serveStatic') }}:</span>
|
|
442
|
-
{{ t('inspector.frontendConfig.serveStaticDesc') }}
|
|
443
|
-
</p>
|
|
444
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
445
|
-
<span class="text-slate-400">{{ t('inspector.frontendConfig.serveCommand') }}:</span>
|
|
446
|
-
{{ t('inspector.frontendConfig.serveCommandDesc') }}
|
|
447
|
-
</p>
|
|
448
|
-
<p class="text-[11px] leading-snug text-slate-500/80">
|
|
449
|
-
{{ t('inspector.frontendConfig.serveEnvAxisNote') }}
|
|
450
|
-
</p>
|
|
451
|
-
</div>
|
|
452
|
-
|
|
453
|
-
<div v-if="serveMode === 'command'" class="space-y-1">
|
|
454
|
-
<label class="text-[11px] text-slate-400">{{
|
|
455
|
-
t('inspector.frontendConfig.serveScript')
|
|
456
|
-
}}</label>
|
|
457
|
-
<UInput
|
|
458
|
-
:model-value="config.serveScript ?? ''"
|
|
385
|
+
<InspectorSection v-model:open="showServe" :title="t('inspector.frontendConfig.groups.serve')">
|
|
386
|
+
<div class="space-y-1">
|
|
387
|
+
<span class="text-[11px] text-slate-400">{{
|
|
388
|
+
t('inspector.frontendConfig.serveMode')
|
|
389
|
+
}}</span>
|
|
390
|
+
<div class="flex flex-wrap gap-1">
|
|
391
|
+
<UButton
|
|
392
|
+
:color="serveMode === 'static' ? 'primary' : 'neutral'"
|
|
393
|
+
:variant="serveMode === 'static' ? 'soft' : 'ghost'"
|
|
459
394
|
size="xs"
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
"
|
|
467
|
-
/>
|
|
468
|
-
</div>
|
|
469
|
-
|
|
470
|
-
<div class="space-y-1">
|
|
471
|
-
<label class="text-[11px] text-slate-400">{{
|
|
472
|
-
t('inspector.frontendConfig.servePort')
|
|
473
|
-
}}</label>
|
|
474
|
-
<UInput
|
|
475
|
-
:model-value="config.servePort != null ? String(config.servePort) : ''"
|
|
476
|
-
type="number"
|
|
477
|
-
min="1"
|
|
478
|
-
max="65535"
|
|
479
|
-
step="1"
|
|
395
|
+
@click="save({ serveMode: 'static' })"
|
|
396
|
+
>
|
|
397
|
+
{{ t('inspector.frontendConfig.serveStatic') }}
|
|
398
|
+
</UButton>
|
|
399
|
+
<UButton
|
|
400
|
+
:color="serveMode === 'command' ? 'primary' : 'neutral'"
|
|
401
|
+
:variant="serveMode === 'command' ? 'soft' : 'ghost'"
|
|
480
402
|
size="xs"
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
403
|
+
@click="save({ serveMode: 'command' })"
|
|
404
|
+
>
|
|
405
|
+
{{ t('inspector.frontendConfig.serveCommand') }}
|
|
406
|
+
</UButton>
|
|
485
407
|
</div>
|
|
408
|
+
<!-- Explain each mode, and disambiguate from the separate envInjection axis. -->
|
|
409
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
410
|
+
<span class="text-slate-400">{{ t('inspector.frontendConfig.serveStatic') }}:</span>
|
|
411
|
+
{{ t('inspector.frontendConfig.serveStaticDesc') }}
|
|
412
|
+
</p>
|
|
413
|
+
<p class="text-[11px] leading-snug text-slate-500">
|
|
414
|
+
<span class="text-slate-400">{{ t('inspector.frontendConfig.serveCommand') }}:</span>
|
|
415
|
+
{{ t('inspector.frontendConfig.serveCommandDesc') }}
|
|
416
|
+
</p>
|
|
417
|
+
<p class="text-[11px] leading-snug text-slate-500/80">
|
|
418
|
+
{{ t('inspector.frontendConfig.serveEnvAxisNote') }}
|
|
419
|
+
</p>
|
|
486
420
|
</div>
|
|
487
|
-
</div>
|
|
488
421
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
422
|
+
<div v-if="serveMode === 'command'" class="space-y-1">
|
|
423
|
+
<label class="text-[11px] text-slate-400">{{
|
|
424
|
+
t('inspector.frontendConfig.serveScript')
|
|
425
|
+
}}</label>
|
|
426
|
+
<UInput
|
|
427
|
+
:model-value="config.serveScript ?? ''"
|
|
428
|
+
size="xs"
|
|
429
|
+
class="font-mono"
|
|
430
|
+
maxlength="200"
|
|
431
|
+
placeholder="preview"
|
|
432
|
+
@blur="(e: FocusEvent) => saveText('serveScript', (e.target as HTMLInputElement).value)"
|
|
433
|
+
@keydown.enter="
|
|
434
|
+
(e: KeyboardEvent) => saveText('serveScript', (e.target as HTMLInputElement).value)
|
|
435
|
+
"
|
|
499
436
|
/>
|
|
500
|
-
|
|
501
|
-
</button>
|
|
437
|
+
</div>
|
|
502
438
|
|
|
503
|
-
<div
|
|
439
|
+
<div class="space-y-1">
|
|
440
|
+
<label class="text-[11px] text-slate-400">{{
|
|
441
|
+
t('inspector.frontendConfig.servePort')
|
|
442
|
+
}}</label>
|
|
443
|
+
<UInput
|
|
444
|
+
:model-value="config.servePort != null ? String(config.servePort) : ''"
|
|
445
|
+
type="number"
|
|
446
|
+
min="1"
|
|
447
|
+
max="65535"
|
|
448
|
+
step="1"
|
|
449
|
+
size="xs"
|
|
450
|
+
class="font-mono"
|
|
451
|
+
placeholder="4173"
|
|
452
|
+
@blur="(e: FocusEvent) => saveServePort((e.target as HTMLInputElement).value)"
|
|
453
|
+
/>
|
|
454
|
+
</div>
|
|
455
|
+
</InspectorSection>
|
|
456
|
+
|
|
457
|
+
<!-- Mocking: WireMock mappings dir -->
|
|
458
|
+
<InspectorSection
|
|
459
|
+
v-model:open="showMocking"
|
|
460
|
+
:title="t('inspector.frontendConfig.groups.mocking')"
|
|
461
|
+
>
|
|
462
|
+
<div class="space-y-1">
|
|
504
463
|
<label class="text-[11px] text-slate-400">{{
|
|
505
464
|
t('inspector.frontendConfig.mockMappingsPath')
|
|
506
465
|
}}</label>
|
|
@@ -521,23 +480,14 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
521
480
|
{{ t('inspector.frontendConfig.mockMappingsHint') }}
|
|
522
481
|
</p>
|
|
523
482
|
</div>
|
|
524
|
-
</
|
|
483
|
+
</InspectorSection>
|
|
525
484
|
|
|
526
485
|
<!-- Env injection: build-time env vars vs a runtime window.env shim -->
|
|
527
|
-
<
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
>
|
|
533
|
-
<UIcon
|
|
534
|
-
:name="showEnv ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
535
|
-
class="h-3.5 w-3.5"
|
|
536
|
-
/>
|
|
537
|
-
{{ t('inspector.frontendConfig.groups.envInjection') }}
|
|
538
|
-
</button>
|
|
539
|
-
|
|
540
|
-
<div v-if="showEnv" class="mt-2 space-y-1">
|
|
486
|
+
<InspectorSection
|
|
487
|
+
v-model:open="showEnv"
|
|
488
|
+
:title="t('inspector.frontendConfig.groups.envInjection')"
|
|
489
|
+
>
|
|
490
|
+
<div class="space-y-1">
|
|
541
491
|
<div class="flex flex-wrap gap-1">
|
|
542
492
|
<UButton
|
|
543
493
|
:color="envInjection === 'build' ? 'primary' : 'neutral'"
|
|
@@ -560,39 +510,25 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
560
510
|
{{ t('inspector.frontendConfig.envInjectionHint') }}
|
|
561
511
|
</p>
|
|
562
512
|
</div>
|
|
563
|
-
</
|
|
513
|
+
</InspectorSection>
|
|
564
514
|
|
|
565
515
|
<!-- Backend bindings: env var → upstream. These double as the board's frontend→service links. -->
|
|
566
|
-
<
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
516
|
+
<InspectorSection
|
|
517
|
+
v-model:open="showBindings"
|
|
518
|
+
:title="t('inspector.frontendConfig.groups.bindings')"
|
|
519
|
+
:hint="t('inspector.frontendConfig.bindings.hint')"
|
|
520
|
+
:count="bindings.length"
|
|
521
|
+
>
|
|
522
|
+
<template #actions>
|
|
523
|
+
<UButton
|
|
524
|
+
size="xs"
|
|
525
|
+
variant="ghost"
|
|
526
|
+
color="neutral"
|
|
527
|
+
icon="i-lucide-plus"
|
|
528
|
+
@click="addBinding"
|
|
575
529
|
/>
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
>({{ bindings.length }})</span
|
|
579
|
-
>
|
|
580
|
-
</button>
|
|
581
|
-
|
|
582
|
-
<div v-if="showBindings" class="mt-2 space-y-2">
|
|
583
|
-
<div class="flex items-center justify-between">
|
|
584
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
585
|
-
{{ t('inspector.frontendConfig.bindings.hint') }}
|
|
586
|
-
</p>
|
|
587
|
-
<UButton
|
|
588
|
-
size="xs"
|
|
589
|
-
variant="ghost"
|
|
590
|
-
color="neutral"
|
|
591
|
-
icon="i-lucide-plus"
|
|
592
|
-
@click="addBinding"
|
|
593
|
-
/>
|
|
594
|
-
</div>
|
|
595
|
-
|
|
530
|
+
</template>
|
|
531
|
+
<div class="space-y-2">
|
|
596
532
|
<div v-if="bindings.length" class="space-y-1.5">
|
|
597
533
|
<div v-for="(b, i) in bindings" :key="i" class="flex items-center gap-1">
|
|
598
534
|
<UInput
|
|
@@ -634,23 +570,14 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
634
570
|
<FrontendBindingsResolved :config="config" />
|
|
635
571
|
</div>
|
|
636
572
|
</div>
|
|
637
|
-
</
|
|
573
|
+
</InspectorSection>
|
|
638
574
|
|
|
639
575
|
<!-- Browsable preview (local/node only; the Worker reports it unsupported). -->
|
|
640
|
-
<
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
>
|
|
646
|
-
<UIcon
|
|
647
|
-
:name="showPreview ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
648
|
-
class="h-3.5 w-3.5"
|
|
649
|
-
/>
|
|
650
|
-
{{ t('inspector.frontendConfig.groups.preview') }}
|
|
651
|
-
</button>
|
|
652
|
-
|
|
653
|
-
<div v-if="showPreview" class="mt-2">
|
|
576
|
+
<InspectorSection
|
|
577
|
+
v-model:open="showPreview"
|
|
578
|
+
:title="t('inspector.frontendConfig.groups.preview')"
|
|
579
|
+
>
|
|
580
|
+
<div>
|
|
654
581
|
<UCheckbox
|
|
655
582
|
:model-value="previewSupported && config.previewEnabled === true"
|
|
656
583
|
:label="t('inspector.frontendConfig.previewEnabled')"
|
|
@@ -744,6 +671,6 @@ onUnmounted(() => preview.stopPolling(props.block.id))
|
|
|
744
671
|
</p>
|
|
745
672
|
</div>
|
|
746
673
|
</div>
|
|
747
|
-
</
|
|
748
|
-
</
|
|
674
|
+
</InspectorSection>
|
|
675
|
+
</InspectorSection>
|
|
749
676
|
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Shared collapsible section shell for the inspector panel: a chevron header with an
|
|
3
|
+
// optional count, warning flag, and header-actions slot (kept OUTSIDE the toggle button
|
|
4
|
+
// so clicking a "+" / menu never collapses the section — it only ever expands it, so a
|
|
5
|
+
// row added via the header control is never dropped into a hidden body), plus an optional
|
|
6
|
+
// explanatory hint rendered at the top of the body — what the section means and what it
|
|
7
|
+
// is used for.
|
|
8
|
+
//
|
|
9
|
+
// Open state is a `defineModel` so a parent that needs programmatic control (e.g.
|
|
10
|
+
// FrontendConfig opening its Build group after a detect run) can bind `v-model:open`;
|
|
11
|
+
// without a binding the model acts as local state and `defaultOpen` picks the initial
|
|
12
|
+
// value (controlled callers should drive their own ref instead of passing it).
|
|
13
|
+
const props = defineProps<{
|
|
14
|
+
title: string
|
|
15
|
+
icon?: string
|
|
16
|
+
hint?: string
|
|
17
|
+
count?: number
|
|
18
|
+
warning?: boolean
|
|
19
|
+
defaultOpen?: boolean
|
|
20
|
+
}>()
|
|
21
|
+
|
|
22
|
+
const open = defineModel<boolean>('open', { default: false })
|
|
23
|
+
if (props.defaultOpen) open.value = true
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<template>
|
|
27
|
+
<section class="border-t border-slate-800 pt-2" data-testid="inspector-section">
|
|
28
|
+
<div class="flex items-center gap-1.5">
|
|
29
|
+
<button
|
|
30
|
+
type="button"
|
|
31
|
+
class="flex min-w-0 flex-1 items-center gap-1.5 text-start text-[11px] font-semibold uppercase tracking-wide text-slate-400 hover:text-slate-200"
|
|
32
|
+
:aria-expanded="open"
|
|
33
|
+
data-testid="inspector-section-toggle"
|
|
34
|
+
@click="open = !open"
|
|
35
|
+
>
|
|
36
|
+
<UIcon
|
|
37
|
+
:name="open ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'"
|
|
38
|
+
class="h-3.5 w-3.5 shrink-0 text-slate-500"
|
|
39
|
+
/>
|
|
40
|
+
<UIcon v-if="icon" :name="icon" class="h-3.5 w-3.5 shrink-0" />
|
|
41
|
+
<span class="truncate">{{ title }}</span>
|
|
42
|
+
<span v-if="count" class="font-normal normal-case text-slate-500">({{ count }})</span>
|
|
43
|
+
<UIcon
|
|
44
|
+
v-if="warning"
|
|
45
|
+
name="i-lucide-triangle-alert"
|
|
46
|
+
class="h-3.5 w-3.5 shrink-0 text-amber-400"
|
|
47
|
+
/>
|
|
48
|
+
</button>
|
|
49
|
+
<!-- An action ("+", attach, menu) mutates the body list, so expand (never collapse)
|
|
50
|
+
on interaction — otherwise the new row lands inside the collapsed body. -->
|
|
51
|
+
<div v-if="$slots.actions" class="contents" @click="open = true">
|
|
52
|
+
<slot name="actions" />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<div v-if="open" class="mt-2 space-y-3">
|
|
56
|
+
<p v-if="hint" class="text-[11px] leading-snug text-slate-500">{{ hint }}</p>
|
|
57
|
+
<slot />
|
|
58
|
+
</div>
|
|
59
|
+
</section>
|
|
60
|
+
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import type { Block, ServiceConnection } from '~/types/domain'
|
|
4
|
+
import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
|
|
4
5
|
|
|
5
6
|
// Service-frame (`type: 'service'`) connections: the other services this one USES
|
|
6
7
|
// (consumer→provider edges, stored on this frame — the consumer end). Each row picks a
|
|
@@ -78,11 +79,13 @@ const usedBy = computed(() =>
|
|
|
78
79
|
</script>
|
|
79
80
|
|
|
80
81
|
<template>
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
82
|
+
<InspectorSection
|
|
83
|
+
:title="t('inspector.serviceConnections.title')"
|
|
84
|
+
:hint="t('inspector.serviceConnections.hint')"
|
|
85
|
+
:count="connections.length"
|
|
86
|
+
data-testid="service-connections"
|
|
87
|
+
>
|
|
88
|
+
<template #actions>
|
|
86
89
|
<UButton
|
|
87
90
|
size="xs"
|
|
88
91
|
variant="ghost"
|
|
@@ -92,10 +95,7 @@ const usedBy = computed(() =>
|
|
|
92
95
|
data-testid="service-connection-add"
|
|
93
96
|
@click="addConnection"
|
|
94
97
|
/>
|
|
95
|
-
</
|
|
96
|
-
<p class="text-[11px] leading-snug text-slate-500">
|
|
97
|
-
{{ t('inspector.serviceConnections.hint') }}
|
|
98
|
-
</p>
|
|
98
|
+
</template>
|
|
99
99
|
|
|
100
100
|
<div v-if="connections.length" class="space-y-1.5">
|
|
101
101
|
<div
|
|
@@ -147,5 +147,5 @@ const usedBy = computed(() =>
|
|
|
147
147
|
</UBadge>
|
|
148
148
|
</div>
|
|
149
149
|
</div>
|
|
150
|
-
</
|
|
150
|
+
</InspectorSection>
|
|
151
151
|
</template>
|