@cat-factory/app 0.275.0 → 0.277.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/README.md +89 -2
- package/app/components/binaryCandidates/BinaryCandidatesWindow.vue +13 -1
- package/app/components/binaryOutput/BinaryOutputReport.vue +15 -1
- package/app/components/binaryOutput/StoredAssetView.vue +134 -0
- package/app/components/board/AddTaskModal.vue +9 -4
- package/app/components/board/LaneViewControl.vue +87 -0
- package/app/components/board/nodes/BlockNode.vue +24 -10
- package/app/components/board/nodes/FrameSwimlanes.vue +139 -0
- package/app/components/board/nodes/InitiativeCard.vue +9 -28
- package/app/components/board/nodes/LaneGroup.vue +93 -0
- package/app/components/board/nodes/LaneTask.vue +66 -0
- package/app/components/board/nodes/TaskCard.vue +16 -2
- package/app/components/board/nodes/TaskLane.vue +82 -0
- package/app/components/layout/BoardToolbar.vue +4 -0
- package/app/components/palettes/PipelinePurposeSelect.vue +1 -0
- package/app/components/panels/InspectorPanel.vue +15 -2
- package/app/components/panels/inspector/TaskStructure.vue +70 -3
- package/app/components/settings/WorkspaceSettingsPanel.vue +61 -1
- package/app/composables/api/visualConfirm.ts +11 -2
- package/app/composables/useArtifactBlobs.spec.ts +76 -0
- package/app/composables/useArtifactBlobs.ts +25 -4
- package/app/composables/useBlockDrag.ts +47 -17
- package/app/composables/useBlockQueries.ts +27 -24
- package/app/composables/useFrameLanes.ts +177 -0
- package/app/composables/useTaskExpansion.ts +1 -1
- package/app/stores/board/placement.ts +7 -0
- package/app/stores/board.spec.ts +119 -14
- package/app/stores/laneView.spec.ts +61 -0
- package/app/stores/laneView.ts +85 -0
- package/app/stores/taskExpansion.spec.ts +1 -1
- package/app/stores/taskExpansion.ts +1 -1
- package/app/stores/workspaceSettings.ts +4 -0
- package/app/utils/binaryCandidates.ts +19 -1
- package/app/utils/binaryOutput.ts +13 -0
- package/app/utils/catalog.ts +6 -0
- package/app/utils/framePlacement.ts +9 -4
- package/app/utils/laneGeometry.spec.ts +69 -0
- package/app/utils/laneGeometry.ts +104 -0
- package/app/utils/laneSort.spec.ts +236 -0
- package/app/utils/laneSort.ts +306 -0
- package/app/utils/swimlanes.spec.ts +259 -0
- package/app/utils/swimlanes.ts +355 -0
- package/i18n/locales/de.json +91 -5
- package/i18n/locales/en.json +91 -5
- package/i18n/locales/es.json +91 -5
- package/i18n/locales/fr.json +91 -5
- package/i18n/locales/he.json +91 -5
- package/i18n/locales/it.json +91 -5
- package/i18n/locales/ja.json +91 -5
- package/i18n/locales/pl.json +91 -5
- package/i18n/locales/tr.json +91 -5
- package/i18n/locales/uk.json +91 -5
- package/package.json +2 -2
- package/app/components/board/nodes/DraggableTask.vue +0 -58
- package/app/components/board/nodes/ModuleFrame.vue +0 -73
package/i18n/locales/tr.json
CHANGED
|
@@ -220,6 +220,7 @@
|
|
|
220
220
|
"bug": "Hata",
|
|
221
221
|
"document": "Belge",
|
|
222
222
|
"spike": "Spike",
|
|
223
|
+
"media": "Medya",
|
|
223
224
|
"recurring": "Yinelenen",
|
|
224
225
|
"review": "İnceleme",
|
|
225
226
|
"ralph": "Ralph döngüsü"
|
|
@@ -509,6 +510,71 @@
|
|
|
509
510
|
"label": "Epik",
|
|
510
511
|
"activeCount": "{count} etkin",
|
|
511
512
|
"noTasksYet": "Henüz görev yok"
|
|
513
|
+
},
|
|
514
|
+
"lanes": {
|
|
515
|
+
"viewTitle": "Düzenle",
|
|
516
|
+
"resetView": "Varsayılanlara sıfırla",
|
|
517
|
+
"notStarted": {
|
|
518
|
+
"label": "Başlamadı",
|
|
519
|
+
"empty": "Ele alınmayı bekleyen bir şey yok."
|
|
520
|
+
},
|
|
521
|
+
"inProgress": {
|
|
522
|
+
"label": "Sürüyor",
|
|
523
|
+
"empty": "Devam eden çalıştırma yok."
|
|
524
|
+
},
|
|
525
|
+
"needsYou": {
|
|
526
|
+
"label": "Seni bekliyor",
|
|
527
|
+
"empty": "Seni bekleyen bir şey yok."
|
|
528
|
+
},
|
|
529
|
+
"done": {
|
|
530
|
+
"label": "Tamamlandı",
|
|
531
|
+
"empty": "Henüz tamamlanan bir şey yok.",
|
|
532
|
+
"hiddenByCap": "+{count} gösterilmiyor",
|
|
533
|
+
"hiddenByAge": "+{count} saklama penceresinin dışında",
|
|
534
|
+
"undated": "{count} kayıtlı tarihi yok",
|
|
535
|
+
"allWithheld": "Tamamlanan görevler burada sayılır ancak bu panonun ayarları gereği listelenmez."
|
|
536
|
+
},
|
|
537
|
+
"reason": {
|
|
538
|
+
"unstarted": "Başlamaya hazır",
|
|
539
|
+
"dependencies": "Bir bağımlılığı bekliyor",
|
|
540
|
+
"running": "Çalışıyor",
|
|
541
|
+
"backgroundReview": "Arka planda inceleniyor",
|
|
542
|
+
"decision": "Karar gerekiyor",
|
|
543
|
+
"approval": "Onay gerekiyor",
|
|
544
|
+
"failed": "Çalıştırma başarısız",
|
|
545
|
+
"budgetPaused": "Harcama bütçesi nedeniyle duraklatıldı",
|
|
546
|
+
"prAwaitingMerge": "Pull request birleştirilmeyi bekliyor",
|
|
547
|
+
"parked": "Bir kişiyi bekliyor",
|
|
548
|
+
"unclassified": "Tanınmayan durum",
|
|
549
|
+
"merged": "Birleştirildi"
|
|
550
|
+
},
|
|
551
|
+
"sort": {
|
|
552
|
+
"heading": "Sıralama",
|
|
553
|
+
"smart": "Her kolon için önerilen",
|
|
554
|
+
"title": "Başlık (A–Z)",
|
|
555
|
+
"oldest_activity": "En sessizler önce",
|
|
556
|
+
"newest_activity": "En son etkin olanlar",
|
|
557
|
+
"longest_wait": "En uzun bekleyenler",
|
|
558
|
+
"severity_desc": "Önem (en yüksekten)",
|
|
559
|
+
"impact_desc": "Tahmini etki (en yüksekten)",
|
|
560
|
+
"task_type": "Görev türü"
|
|
561
|
+
},
|
|
562
|
+
"group": {
|
|
563
|
+
"heading": "Gruplama",
|
|
564
|
+
"none": "Gruplama yok",
|
|
565
|
+
"module": "Modül",
|
|
566
|
+
"task_type": "Görev türü",
|
|
567
|
+
"initiative": "Girişim",
|
|
568
|
+
"epic": "Epik",
|
|
569
|
+
"blocking_reason": "Neye ihtiyacı var",
|
|
570
|
+
"catchAll": {
|
|
571
|
+
"module": "Modül yok",
|
|
572
|
+
"task_type": "Tür yok",
|
|
573
|
+
"initiative": "Bir girişimde değil",
|
|
574
|
+
"epic": "Bir epikte değil",
|
|
575
|
+
"blocking_reason": "Sınıflandırılmadı"
|
|
576
|
+
}
|
|
577
|
+
}
|
|
512
578
|
}
|
|
513
579
|
},
|
|
514
580
|
"contextAttachments": {
|
|
@@ -1143,11 +1209,12 @@
|
|
|
1143
1209
|
"title": "Yapı",
|
|
1144
1210
|
"hint": "Bu görevin servisi içinde nerede bulunduğu ve ajanlarının hangi kod standartlarını izlediği.",
|
|
1145
1211
|
"module": "Modül",
|
|
1146
|
-
"modulePlaceholder": "
|
|
1147
|
-
"moduleHint": "Bu görevin ait olduğu servis modül
|
|
1212
|
+
"modulePlaceholder": "Bir modül seç veya yaz",
|
|
1213
|
+
"moduleHint": "Bu görevin ait olduğu servis modülü; pano kolonları buna göre gruplar. Henüz var olmayan bir modül, görev uygulandığında oluşturulur.",
|
|
1148
1214
|
"bestPractices": "En iyi uygulamalar",
|
|
1149
1215
|
"bestPracticesEmpty": "Yok. Ajanlar varsayılan yönergelerini izler.",
|
|
1150
|
-
"bestPracticesHint": "Servis düzeyindeki standartlara ek olarak bu görevin ajanlarının prompt'larına eklenen en iyi uygulama parçaları."
|
|
1216
|
+
"bestPracticesHint": "Servis düzeyindeki standartlara ek olarak bu görevin ajanlarının prompt'larına eklenen en iyi uygulama parçaları.",
|
|
1217
|
+
"moduleNone": "Modül yok"
|
|
1151
1218
|
},
|
|
1152
1219
|
"runSettings": {
|
|
1153
1220
|
"title": "Çalıştırma ayarları",
|
|
@@ -3720,7 +3787,8 @@
|
|
|
3720
3787
|
"document": "belge",
|
|
3721
3788
|
"spike": "inceleme",
|
|
3722
3789
|
"review": "inceleme",
|
|
3723
|
-
"ralph": "Ralph döngüsü"
|
|
3790
|
+
"ralph": "Ralph döngüsü",
|
|
3791
|
+
"media": "medya"
|
|
3724
3792
|
},
|
|
3725
3793
|
"reviewFriction": {
|
|
3726
3794
|
"heading": "İnceleme borcu sürtünmesi",
|
|
@@ -3806,6 +3874,15 @@
|
|
|
3806
3874
|
"advisory": "Bilgilendirici (kaydet, asla bekletme)",
|
|
3807
3875
|
"off": "Kapalı (denetimi atla)"
|
|
3808
3876
|
}
|
|
3877
|
+
},
|
|
3878
|
+
"doneLane": {
|
|
3879
|
+
"heading": "«Tamamlandı» kolonu",
|
|
3880
|
+
"body": "Her servis çerçevesinin «Tamamlandı» kolonunun ne kadar bitmiş işi görünür tuttuğu. İki sınır da yalnızca kartları gizler: gizlenen bir görev panoda kalır, aranabilir kalır ve kolonun toplamına dahil kalır.",
|
|
3881
|
+
"maxItems": "Gösterilen kartlar",
|
|
3882
|
+
"zeroHint": "Tamamlanan görevler sayılacak ama hiç listelenmeyecek.",
|
|
3883
|
+
"ageToggle": "Uzun süre önce tamamlanan görevleri de gizle",
|
|
3884
|
+
"days": "Sakla (gün)",
|
|
3885
|
+
"hidesOnlyHint": "Hiçbir şey silinmez. Tamamlanma tarihi kayıtlı olmayan görevler yaşları nedeniyle asla gizlenmez."
|
|
3809
3886
|
}
|
|
3810
3887
|
},
|
|
3811
3888
|
"localModelEndpoints": {
|
|
@@ -4887,7 +4964,8 @@
|
|
|
4887
4964
|
"document": "Dokümantasyon",
|
|
4888
4965
|
"review": "İnceleme",
|
|
4889
4966
|
"research": "Araştırma",
|
|
4890
|
-
"planning": "Planlama"
|
|
4967
|
+
"planning": "Planlama",
|
|
4968
|
+
"media": "Medya"
|
|
4891
4969
|
},
|
|
4892
4970
|
"strategyOption": {
|
|
4893
4971
|
"specialist-panel": "Uzman paneli",
|
|
@@ -6613,6 +6691,14 @@
|
|
|
6613
6691
|
"automatic": "{total} aday üretildi ve inceleme olmadan otomatik olarak tutuldu.",
|
|
6614
6692
|
"chosen": "Üretilen {total} adayın {kept} tanesi tutuldu.",
|
|
6615
6693
|
"awaiting": "{total} aday üretildi ve karşılaştırılmayı bekliyor."
|
|
6694
|
+
},
|
|
6695
|
+
"asset": {
|
|
6696
|
+
"loading": "Dosya yükleniyor…",
|
|
6697
|
+
"loadFailed": "Saklanan dosya yüklenemedi.",
|
|
6698
|
+
"retry": "Yeniden dene",
|
|
6699
|
+
"open": "Aç",
|
|
6700
|
+
"download": "Kopyasını kaydet",
|
|
6701
|
+
"unknownType": "Saklanan dosya"
|
|
6616
6702
|
}
|
|
6617
6703
|
},
|
|
6618
6704
|
"sandbox": {
|
package/i18n/locales/uk.json
CHANGED
|
@@ -220,6 +220,7 @@
|
|
|
220
220
|
"bug": "Помилка",
|
|
221
221
|
"document": "Документ",
|
|
222
222
|
"spike": "Spike",
|
|
223
|
+
"media": "Медіа",
|
|
223
224
|
"recurring": "Періодичне",
|
|
224
225
|
"review": "Огляд",
|
|
225
226
|
"ralph": "Цикл Ralph"
|
|
@@ -509,6 +510,71 @@
|
|
|
509
510
|
"label": "Епік",
|
|
510
511
|
"activeCount": "{count} активних",
|
|
511
512
|
"noTasksYet": "Поки немає завдань"
|
|
513
|
+
},
|
|
514
|
+
"lanes": {
|
|
515
|
+
"viewTitle": "Упорядкувати",
|
|
516
|
+
"resetView": "Скинути до типових значень",
|
|
517
|
+
"notStarted": {
|
|
518
|
+
"label": "Не розпочато",
|
|
519
|
+
"empty": "Немає нічого, що очікує на виконання."
|
|
520
|
+
},
|
|
521
|
+
"inProgress": {
|
|
522
|
+
"label": "У роботі",
|
|
523
|
+
"empty": "Немає активних запусків."
|
|
524
|
+
},
|
|
525
|
+
"needsYou": {
|
|
526
|
+
"label": "Потрібні ви",
|
|
527
|
+
"empty": "Ніщо не очікує на вас."
|
|
528
|
+
},
|
|
529
|
+
"done": {
|
|
530
|
+
"label": "Готово",
|
|
531
|
+
"empty": "Поки нічого не завершено.",
|
|
532
|
+
"hiddenByCap": "+{count} не показано",
|
|
533
|
+
"hiddenByAge": "+{count} поза вікном зберігання",
|
|
534
|
+
"undated": "{count} без записаної дати",
|
|
535
|
+
"allWithheld": "Завершені завдання тут підраховуються, але не перелічуються, згідно з налаштуваннями цієї дошки."
|
|
536
|
+
},
|
|
537
|
+
"reason": {
|
|
538
|
+
"unstarted": "Готове до старту",
|
|
539
|
+
"dependencies": "Очікує на залежність",
|
|
540
|
+
"running": "Виконується",
|
|
541
|
+
"backgroundReview": "Перевірка у фоні",
|
|
542
|
+
"decision": "Потрібне рішення",
|
|
543
|
+
"approval": "Потрібне схвалення",
|
|
544
|
+
"failed": "Запуск не вдався",
|
|
545
|
+
"budgetPaused": "Припинено через бюджет витрат",
|
|
546
|
+
"prAwaitingMerge": "Pull request очікує на злиття",
|
|
547
|
+
"parked": "Очікує на людину",
|
|
548
|
+
"unclassified": "Нерозпізнаний стан",
|
|
549
|
+
"merged": "Злито"
|
|
550
|
+
},
|
|
551
|
+
"sort": {
|
|
552
|
+
"heading": "Сортувати за",
|
|
553
|
+
"smart": "Рекомендовано для кожної колонки",
|
|
554
|
+
"title": "Назва (А–Я)",
|
|
555
|
+
"oldest_activity": "Найтихіші спершу",
|
|
556
|
+
"newest_activity": "Найактивніші останнім часом",
|
|
557
|
+
"longest_wait": "Ті, що чекають найдовше",
|
|
558
|
+
"severity_desc": "Критичність (від вищої)",
|
|
559
|
+
"impact_desc": "Оцінений вплив (від вищого)",
|
|
560
|
+
"task_type": "Тип завдання"
|
|
561
|
+
},
|
|
562
|
+
"group": {
|
|
563
|
+
"heading": "Групувати за",
|
|
564
|
+
"none": "Без групування",
|
|
565
|
+
"module": "Модуль",
|
|
566
|
+
"task_type": "Тип завдання",
|
|
567
|
+
"initiative": "Ініціатива",
|
|
568
|
+
"epic": "Епік",
|
|
569
|
+
"blocking_reason": "Що потрібно",
|
|
570
|
+
"catchAll": {
|
|
571
|
+
"module": "Без модуля",
|
|
572
|
+
"task_type": "Без типу",
|
|
573
|
+
"initiative": "Поза ініціативою",
|
|
574
|
+
"epic": "Поза епіком",
|
|
575
|
+
"blocking_reason": "Не класифіковано"
|
|
576
|
+
}
|
|
577
|
+
}
|
|
512
578
|
}
|
|
513
579
|
},
|
|
514
580
|
"contextAttachments": {
|
|
@@ -1143,11 +1209,12 @@
|
|
|
1143
1209
|
"title": "Структура",
|
|
1144
1210
|
"hint": "Де це завдання розташоване у своєму сервісі та яких стандартів коду дотримуються його агенти.",
|
|
1145
1211
|
"module": "Модуль",
|
|
1146
|
-
"modulePlaceholder": "
|
|
1147
|
-
"moduleHint": "Модуль сервісу, до якого належить це
|
|
1212
|
+
"modulePlaceholder": "Виберіть або введіть модуль",
|
|
1213
|
+
"moduleHint": "Модуль сервісу, до якого належить це завдання; дошка групує колонки за ним. Модуль, якого ще немає, створюється під час реалізації завдання.",
|
|
1148
1214
|
"bestPractices": "Найкращі практики",
|
|
1149
1215
|
"bestPracticesEmpty": "Немає. Агенти дотримуються типових інструкцій.",
|
|
1150
|
-
"bestPracticesHint": "Фрагменти найкращих практик, що додаються до промптів агентів цього завдання, на додачу до стандартів рівня сервісу."
|
|
1216
|
+
"bestPracticesHint": "Фрагменти найкращих практик, що додаються до промптів агентів цього завдання, на додачу до стандартів рівня сервісу.",
|
|
1217
|
+
"moduleNone": "Без модуля"
|
|
1151
1218
|
},
|
|
1152
1219
|
"runSettings": {
|
|
1153
1220
|
"title": "Налаштування запуску",
|
|
@@ -3578,7 +3645,8 @@
|
|
|
3578
3645
|
"document": "документ",
|
|
3579
3646
|
"spike": "spike",
|
|
3580
3647
|
"review": "огляд",
|
|
3581
|
-
"ralph": "Цикл Ralph"
|
|
3648
|
+
"ralph": "Цикл Ralph",
|
|
3649
|
+
"media": "медіа"
|
|
3582
3650
|
},
|
|
3583
3651
|
"reviewFriction": {
|
|
3584
3652
|
"heading": "Тертя через борг рев’ю",
|
|
@@ -3664,6 +3732,15 @@
|
|
|
3664
3732
|
"advisory": "Рекомендаційний (записувати, ніколи не зупиняти)",
|
|
3665
3733
|
"off": "Вимкнено (пропускати перевірку)"
|
|
3666
3734
|
}
|
|
3735
|
+
},
|
|
3736
|
+
"doneLane": {
|
|
3737
|
+
"heading": "Колонка «Готово»",
|
|
3738
|
+
"body": "Скільки завершеної роботи колонка «Готово» кожного сервісу тримає на видноті. Обидва обмеження лише приховують картки: приховане завдання залишається на дошці, залишається доступним для пошуку й залишається у загальній кількості колонки.",
|
|
3739
|
+
"maxItems": "Показані картки",
|
|
3740
|
+
"zeroHint": "Завершені завдання підраховуватимуться, але не перелічуватимуться.",
|
|
3741
|
+
"ageToggle": "Також приховувати завдання, завершені давно",
|
|
3742
|
+
"days": "Зберігати (днів)",
|
|
3743
|
+
"hidesOnlyHint": "Нічого не видаляється. Завдання без записаної дати завершення ніколи не приховуються за віком."
|
|
3667
3744
|
}
|
|
3668
3745
|
},
|
|
3669
3746
|
"localModelEndpoints": {
|
|
@@ -4887,7 +4964,8 @@
|
|
|
4887
4964
|
"document": "Документація",
|
|
4888
4965
|
"review": "Рецензування",
|
|
4889
4966
|
"research": "Дослідження",
|
|
4890
|
-
"planning": "Планування"
|
|
4967
|
+
"planning": "Планування",
|
|
4968
|
+
"media": "Медіа"
|
|
4891
4969
|
},
|
|
4892
4970
|
"strategyOption": {
|
|
4893
4971
|
"specialist-panel": "Панель фахівців",
|
|
@@ -6613,6 +6691,14 @@
|
|
|
6613
6691
|
"automatic": "Створено {total} кандидата й залишено автоматично, без перегляду.",
|
|
6614
6692
|
"chosen": "Залишено {kept} із {total} створених кандидатів.",
|
|
6615
6693
|
"awaiting": "Створено {total} кандидатів, які очікують на порівняння."
|
|
6694
|
+
},
|
|
6695
|
+
"asset": {
|
|
6696
|
+
"loading": "Завантаження файлу…",
|
|
6697
|
+
"loadFailed": "Не вдалося завантажити збережений файл.",
|
|
6698
|
+
"retry": "Спробувати ще раз",
|
|
6699
|
+
"open": "Відкрити",
|
|
6700
|
+
"download": "Зберегти копію",
|
|
6701
|
+
"unknownType": "Збережений файл"
|
|
6616
6702
|
}
|
|
6617
6703
|
},
|
|
6618
6704
|
"sandbox": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.277.0",
|
|
4
4
|
"description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"valibot": "^1.4.2",
|
|
41
41
|
"vue": "3.5.41",
|
|
42
42
|
"wretch": "^3.0.9",
|
|
43
|
-
"@cat-factory/contracts": "0.
|
|
43
|
+
"@cat-factory/contracts": "0.316.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@toad-contracts/testing": "0.3.2",
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import TaskCard from './TaskCard.vue'
|
|
3
|
-
import { useBlockDrag } from '~/composables/useBlockDrag'
|
|
4
|
-
|
|
5
|
-
const props = defineProps<{ taskId: string }>()
|
|
6
|
-
const board = useBoardStore()
|
|
7
|
-
const { t } = useI18n()
|
|
8
|
-
const expansion = useTaskExpansionStore()
|
|
9
|
-
const task = computed(() => board.getBlock(props.taskId))
|
|
10
|
-
const { draggingId, startDrag } = useBlockDrag()
|
|
11
|
-
|
|
12
|
-
// An expanded pipeline grows downward over its neighbours, so it must stack above the
|
|
13
|
-
// other (compact) task cards — never let a neighbour render on top of the pipeline. Reads
|
|
14
|
-
// the same predicate the pipeline itself renders on, so the two can't disagree.
|
|
15
|
-
const expanded = computed(() => expansion.isExpanded(props.taskId))
|
|
16
|
-
|
|
17
|
-
// Once a task is merged it stops being a unit of work and becomes part of the
|
|
18
|
-
// architecture: it no longer renders as a draggable card (arrows fall back to its
|
|
19
|
-
// container), so we never leave a zero-size anchor behind.
|
|
20
|
-
const merged = computed(() => task.value?.status === 'done')
|
|
21
|
-
|
|
22
|
-
function onHandle(e: PointerEvent) {
|
|
23
|
-
if (task.value) startDrag(task.value, e, { reparent: true })
|
|
24
|
-
}
|
|
25
|
-
</script>
|
|
26
|
-
|
|
27
|
-
<template>
|
|
28
|
-
<template v-if="task">
|
|
29
|
-
<!-- in-flight task → draggable work card (merged tasks render nothing) -->
|
|
30
|
-
<div
|
|
31
|
-
v-if="!merged"
|
|
32
|
-
class="absolute w-[210px]"
|
|
33
|
-
:style="{
|
|
34
|
-
left: task.position.x + 'px',
|
|
35
|
-
top: task.position.y + 'px',
|
|
36
|
-
zIndex: draggingId === taskId ? 60 : expanded ? 20 : 10,
|
|
37
|
-
// While this task is being dragged it must not capture hit-tests, so the
|
|
38
|
-
// drop-zone (service or module) beneath the cursor can be resolved on
|
|
39
|
-
// release — including the drag handle, which lives in this wrapper above
|
|
40
|
-
// the card and would otherwise mask the zone under it.
|
|
41
|
-
pointerEvents: draggingId === taskId ? 'none' : undefined,
|
|
42
|
-
}"
|
|
43
|
-
>
|
|
44
|
-
<!-- drag handle (`nopan` so the pane doesn't pan on a start-drag from here) -->
|
|
45
|
-
<div
|
|
46
|
-
class="nodrag nopan flex cursor-grab touch-none items-center justify-center rounded-t-lg border border-b-0 border-slate-700 bg-slate-800/80 py-px active:cursor-grabbing pointer-coarse:py-2"
|
|
47
|
-
:title="t('board.frame.dragTask')"
|
|
48
|
-
@pointerdown="onHandle"
|
|
49
|
-
>
|
|
50
|
-
<UIcon
|
|
51
|
-
name="i-lucide-grip-horizontal"
|
|
52
|
-
class="h-3 w-3 text-slate-500 pointer-coarse:h-5 pointer-coarse:w-5"
|
|
53
|
-
/>
|
|
54
|
-
</div>
|
|
55
|
-
<TaskCard :task-id="taskId" class="!rounded-t-none" />
|
|
56
|
-
</div>
|
|
57
|
-
</template>
|
|
58
|
-
</template>
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import DraggableTask from './DraggableTask.vue'
|
|
3
|
-
import ResizeGrips from './ResizeGrips.vue'
|
|
4
|
-
import { MODULE_META } from '~/utils/catalog'
|
|
5
|
-
import { useBlockDrag } from '~/composables/useBlockDrag'
|
|
6
|
-
|
|
7
|
-
const props = defineProps<{ moduleId: string }>()
|
|
8
|
-
const board = useBoardStore()
|
|
9
|
-
const ui = useUiStore()
|
|
10
|
-
const { t } = useI18n()
|
|
11
|
-
|
|
12
|
-
const mod = computed(() => board.getBlock(props.moduleId))
|
|
13
|
-
const tasks = computed(() => board.tasksOf(props.moduleId))
|
|
14
|
-
const size = computed(() => board.containerSize(props.moduleId))
|
|
15
|
-
const selected = computed(() => ui.selectedBlockId === props.moduleId)
|
|
16
|
-
|
|
17
|
-
// A module groups the tasks inside it. We label it by how many tasks are still in
|
|
18
|
-
// flight, falling back to the total task count once everything inside has merged.
|
|
19
|
-
const inflight = computed(() => tasks.value.filter((t) => t.status !== 'done').length)
|
|
20
|
-
const total = computed(() => tasks.value.length)
|
|
21
|
-
|
|
22
|
-
const { draggingId, startDrag } = useBlockDrag()
|
|
23
|
-
|
|
24
|
-
// modules move within their service but don't get reparented
|
|
25
|
-
function onHandle(e: PointerEvent) {
|
|
26
|
-
if (mod.value) startDrag(mod.value, e)
|
|
27
|
-
}
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<template>
|
|
31
|
-
<div
|
|
32
|
-
v-if="mod"
|
|
33
|
-
:data-block-id="mod.id"
|
|
34
|
-
class="absolute rounded-xl border border-violet-500/40 bg-violet-500/[0.06]"
|
|
35
|
-
:class="{ 'ring-1 ring-white': selected }"
|
|
36
|
-
:style="{
|
|
37
|
-
left: mod.position.x + 'px',
|
|
38
|
-
top: mod.position.y + 'px',
|
|
39
|
-
width: size.w + 'px',
|
|
40
|
-
height: size.h + 'px',
|
|
41
|
-
zIndex: draggingId === moduleId ? 50 : 5,
|
|
42
|
-
}"
|
|
43
|
-
>
|
|
44
|
-
<!-- module header / drag handle (`nopan` so a start-drag moves it, not the pane) -->
|
|
45
|
-
<div
|
|
46
|
-
class="nodrag nopan flex h-[30px] cursor-grab touch-none items-center gap-1 rounded-t-xl bg-violet-500/15 px-2 active:cursor-grabbing"
|
|
47
|
-
@pointerdown="onHandle"
|
|
48
|
-
@click.stop="ui.select(moduleId)"
|
|
49
|
-
>
|
|
50
|
-
<UIcon
|
|
51
|
-
:name="MODULE_META.icon"
|
|
52
|
-
class="h-3.5 w-3.5 shrink-0"
|
|
53
|
-
:style="{ color: MODULE_META.color }"
|
|
54
|
-
/>
|
|
55
|
-
<span class="truncate text-[11px] font-semibold text-violet-100">{{ mod.title }}</span>
|
|
56
|
-
<span v-if="inflight" class="ms-auto shrink-0 text-[9px] text-violet-300/70">
|
|
57
|
-
{{ t('board.frame.taskCount', { count: inflight }, inflight) }}
|
|
58
|
-
</span>
|
|
59
|
-
<span v-else-if="total" class="ms-auto shrink-0 text-[9px] text-violet-300/70">
|
|
60
|
-
{{ t('board.frame.taskCount', { count: total }, total) }}
|
|
61
|
-
</span>
|
|
62
|
-
</div>
|
|
63
|
-
|
|
64
|
-
<!-- drop zone for this module's tasks -->
|
|
65
|
-
<div :data-drop-zone="mod.id" class="relative" :style="{ height: size.h - 30 + 'px' }">
|
|
66
|
-
<DraggableTask v-for="t in tasks" :key="t.id" :task-id="t.id" />
|
|
67
|
-
</div>
|
|
68
|
-
|
|
69
|
-
<!-- Every border and corner of the module box is a resize grip, the same component (and so
|
|
70
|
-
the same hit bands + content-preserving north/west behaviour) the service frame uses. -->
|
|
71
|
-
<ResizeGrips :block="mod" tone="module" />
|
|
72
|
-
</div>
|
|
73
|
-
</template>
|