@cat-factory/app 0.275.0 → 0.276.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 (44) hide show
  1. package/README.md +89 -2
  2. package/app/components/board/LaneViewControl.vue +87 -0
  3. package/app/components/board/nodes/BlockNode.vue +24 -10
  4. package/app/components/board/nodes/FrameSwimlanes.vue +139 -0
  5. package/app/components/board/nodes/InitiativeCard.vue +9 -28
  6. package/app/components/board/nodes/LaneGroup.vue +93 -0
  7. package/app/components/board/nodes/LaneTask.vue +66 -0
  8. package/app/components/board/nodes/TaskCard.vue +16 -2
  9. package/app/components/board/nodes/TaskLane.vue +82 -0
  10. package/app/components/layout/BoardToolbar.vue +4 -0
  11. package/app/components/panels/InspectorPanel.vue +15 -2
  12. package/app/components/panels/inspector/TaskStructure.vue +70 -3
  13. package/app/components/settings/WorkspaceSettingsPanel.vue +59 -0
  14. package/app/composables/useBlockDrag.ts +47 -17
  15. package/app/composables/useBlockQueries.ts +27 -24
  16. package/app/composables/useFrameLanes.ts +177 -0
  17. package/app/composables/useTaskExpansion.ts +1 -1
  18. package/app/stores/board/placement.ts +7 -0
  19. package/app/stores/board.spec.ts +119 -14
  20. package/app/stores/laneView.spec.ts +61 -0
  21. package/app/stores/laneView.ts +85 -0
  22. package/app/stores/taskExpansion.spec.ts +1 -1
  23. package/app/stores/taskExpansion.ts +1 -1
  24. package/app/stores/workspaceSettings.ts +4 -0
  25. package/app/utils/framePlacement.ts +9 -4
  26. package/app/utils/laneGeometry.spec.ts +69 -0
  27. package/app/utils/laneGeometry.ts +104 -0
  28. package/app/utils/laneSort.spec.ts +236 -0
  29. package/app/utils/laneSort.ts +306 -0
  30. package/app/utils/swimlanes.spec.ts +259 -0
  31. package/app/utils/swimlanes.ts +355 -0
  32. package/i18n/locales/de.json +78 -3
  33. package/i18n/locales/en.json +78 -3
  34. package/i18n/locales/es.json +78 -3
  35. package/i18n/locales/fr.json +78 -3
  36. package/i18n/locales/he.json +78 -3
  37. package/i18n/locales/it.json +78 -3
  38. package/i18n/locales/ja.json +78 -3
  39. package/i18n/locales/pl.json +78 -3
  40. package/i18n/locales/tr.json +78 -3
  41. package/i18n/locales/uk.json +78 -3
  42. package/package.json +2 -2
  43. package/app/components/board/nodes/DraggableTask.vue +0 -58
  44. package/app/components/board/nodes/ModuleFrame.vue +0 -73
@@ -509,6 +509,71 @@
509
509
  "label": "エピック",
510
510
  "activeCount": "{count} 件アクティブ",
511
511
  "noTasksYet": "タスクはまだありません"
512
+ },
513
+ "lanes": {
514
+ "viewTitle": "並べ替え",
515
+ "resetView": "既定に戻す",
516
+ "notStarted": {
517
+ "label": "未着手",
518
+ "empty": "着手待ちのものはありません。"
519
+ },
520
+ "inProgress": {
521
+ "label": "進行中",
522
+ "empty": "実行中のものはありません。"
523
+ },
524
+ "needsYou": {
525
+ "label": "対応が必要",
526
+ "empty": "あなたの対応を待っているものはありません。"
527
+ },
528
+ "done": {
529
+ "label": "完了",
530
+ "empty": "まだ完了したものはありません。",
531
+ "hiddenByCap": "+{count} 件は非表示",
532
+ "hiddenByAge": "+{count} 件は保持期間より前",
533
+ "undated": "{count} 件は日付の記録なし",
534
+ "allWithheld": "このボードの設定により、完了タスクはここで集計されますが一覧表示されません。"
535
+ },
536
+ "reason": {
537
+ "unstarted": "開始可能",
538
+ "dependencies": "依存関係を待機中",
539
+ "running": "実行中",
540
+ "backgroundReview": "バックグラウンドでレビュー中",
541
+ "decision": "判断が必要",
542
+ "approval": "承認が必要",
543
+ "failed": "実行が失敗",
544
+ "budgetPaused": "支出予算により一時停止",
545
+ "prAwaitingMerge": "プルリクエストがマージ待ち",
546
+ "parked": "人の対応を待機中",
547
+ "unclassified": "未知のステータス",
548
+ "merged": "マージ済み"
549
+ },
550
+ "sort": {
551
+ "heading": "並び順",
552
+ "smart": "レーンごとの推奨",
553
+ "title": "タイトル(A–Z)",
554
+ "oldest_activity": "動きの少ないものから",
555
+ "newest_activity": "最近動いたものから",
556
+ "longest_wait": "待機が長いものから",
557
+ "severity_desc": "重大度(高い順)",
558
+ "impact_desc": "推定インパクト(高い順)",
559
+ "task_type": "タスク種別"
560
+ },
561
+ "group": {
562
+ "heading": "グループ化",
563
+ "none": "グループ化しない",
564
+ "module": "モジュール",
565
+ "task_type": "タスク種別",
566
+ "initiative": "イニシアチブ",
567
+ "epic": "エピック",
568
+ "blocking_reason": "必要なこと",
569
+ "catchAll": {
570
+ "module": "モジュールなし",
571
+ "task_type": "種別なし",
572
+ "initiative": "イニシアチブ外",
573
+ "epic": "エピック外",
574
+ "blocking_reason": "未分類"
575
+ }
576
+ }
512
577
  }
513
578
  },
514
579
  "contextAttachments": {
@@ -1143,11 +1208,12 @@
1143
1208
  "title": "構造",
1144
1209
  "hint": "このタスクがサービス内のどこに属し、エージェントがどのコーディング標準に従うか。",
1145
1210
  "module": "モジュール",
1146
- "modulePlaceholder": "例: Sessions (新規の場合は実装時に作成)",
1147
- "moduleHint": "このタスクが属するサービスのモジュール。名前を入力してください。まだ存在しないモジュールはタスクの実装時に作成されます。",
1211
+ "modulePlaceholder": "モジュールを選択または入力",
1212
+ "moduleHint": "このタスクが属するサービスモジュールです。ボードはこれでレーンをグループ化します。まだ存在しないモジュールは、タスクの実装時に作成されます。",
1148
1213
  "bestPractices": "ベストプラクティス",
1149
1214
  "bestPracticesEmpty": "なし。エージェントは既定のガイダンスに従います。",
1150
- "bestPracticesHint": "サービスレベルの標準に加えて、このタスクのエージェントのプロンプトに組み込まれるベストプラクティスフラグメント。"
1215
+ "bestPracticesHint": "サービスレベルの標準に加えて、このタスクのエージェントのプロンプトに組み込まれるベストプラクティスフラグメント。",
1216
+ "moduleNone": "モジュールなし"
1151
1217
  },
1152
1218
  "runSettings": {
1153
1219
  "title": "実行設定",
@@ -3806,6 +3872,15 @@
3806
3872
  "advisory": "参考(記録するが保留しない)",
3807
3873
  "off": "オフ(チェックしない)"
3808
3874
  }
3875
+ },
3876
+ "doneLane": {
3877
+ "heading": "完了レーン",
3878
+ "body": "各サービスフレームの「完了」レーンに表示し続ける完了済み作業の量です。どちらの上限もカードを隠すだけで、隠れたタスクはボードに残り、検索でき、レーンの合計にも含まれます。",
3879
+ "maxItems": "表示するカード数",
3880
+ "zeroHint": "完了タスクは集計されますが、一覧表示はされません。",
3881
+ "ageToggle": "完了から時間が経ったタスクも隠す",
3882
+ "days": "保持期間(日)",
3883
+ "hidesOnlyHint": "削除は行われません。完了日が記録されていないタスクが期間によって隠されることはありません。"
3809
3884
  }
3810
3885
  },
3811
3886
  "localModelEndpoints": {
@@ -509,6 +509,71 @@
509
509
  "label": "Epik",
510
510
  "activeCount": "{count} aktywnych",
511
511
  "noTasksYet": "Brak zadań"
512
+ },
513
+ "lanes": {
514
+ "viewTitle": "Uporządkuj",
515
+ "resetView": "Przywróć ustawienia domyślne",
516
+ "notStarted": {
517
+ "label": "Nierozpoczęte",
518
+ "empty": "Nic nie czeka na podjęcie."
519
+ },
520
+ "inProgress": {
521
+ "label": "W toku",
522
+ "empty": "Brak trwających uruchomień."
523
+ },
524
+ "needsYou": {
525
+ "label": "Czeka na ciebie",
526
+ "empty": "Nic na ciebie nie czeka."
527
+ },
528
+ "done": {
529
+ "label": "Ukończone",
530
+ "empty": "Nic jeszcze nie zostało ukończone.",
531
+ "hiddenByCap": "+{count} niewyświetlonych",
532
+ "hiddenByAge": "+{count} poza okresem przechowywania",
533
+ "undated": "{count} bez zapisanej daty",
534
+ "allWithheld": "Ukończone zadania są tu liczone, ale nie wypisywane, zgodnie z ustawieniami tej tablicy."
535
+ },
536
+ "reason": {
537
+ "unstarted": "Gotowe do rozpoczęcia",
538
+ "dependencies": "Czeka na zależność",
539
+ "running": "W trakcie",
540
+ "backgroundReview": "Przegląd w tle",
541
+ "decision": "Potrzebna decyzja",
542
+ "approval": "Potrzebna zgoda",
543
+ "failed": "Uruchomienie nie powiodło się",
544
+ "budgetPaused": "Wstrzymane przez budżet wydatków",
545
+ "prAwaitingMerge": "Pull request czeka na scalenie",
546
+ "parked": "Czeka na człowieka",
547
+ "unclassified": "Nierozpoznany status",
548
+ "merged": "Scalone"
549
+ },
550
+ "sort": {
551
+ "heading": "Sortuj według",
552
+ "smart": "Zalecane dla każdej kolumny",
553
+ "title": "Tytuł (A–Z)",
554
+ "oldest_activity": "Najcichsze najpierw",
555
+ "newest_activity": "Ostatnio aktywne",
556
+ "longest_wait": "Najdłużej czekające",
557
+ "severity_desc": "Waga (od najwyższej)",
558
+ "impact_desc": "Szacowany wpływ (od najwyższego)",
559
+ "task_type": "Typ zadania"
560
+ },
561
+ "group": {
562
+ "heading": "Grupuj według",
563
+ "none": "Bez grupowania",
564
+ "module": "Moduł",
565
+ "task_type": "Typ zadania",
566
+ "initiative": "Inicjatywa",
567
+ "epic": "Epik",
568
+ "blocking_reason": "Czego wymaga",
569
+ "catchAll": {
570
+ "module": "Bez modułu",
571
+ "task_type": "Bez typu",
572
+ "initiative": "Poza inicjatywą",
573
+ "epic": "Poza epikiem",
574
+ "blocking_reason": "Niesklasyfikowane"
575
+ }
576
+ }
512
577
  }
513
578
  },
514
579
  "contextAttachments": {
@@ -1143,11 +1208,12 @@
1143
1208
  "title": "Struktura",
1144
1209
  "hint": "Gdzie w usłudze znajduje się to zadanie i jakich standardów kodu przestrzegają jego agenci.",
1145
1210
  "module": "Moduł",
1146
- "modulePlaceholder": "np. Sesje (tworzone przy implementacji, jeśli nowy)",
1147
- "moduleHint": "Moduł usługi, do którego należy to zadanie. Wpisz nazwę; moduł, który jeszcze nie istnieje, zostanie utworzony przy implementacji zadania.",
1211
+ "modulePlaceholder": "Wybierz lub wpisz moduł",
1212
+ "moduleHint": "Moduł usługi, do którego należy to zadanie; tablica grupuje po nim kolumny. Moduł, który jeszcze nie istnieje, powstaje w chwili wdrożenia zadania.",
1148
1213
  "bestPractices": "Dobre praktyki",
1149
1214
  "bestPracticesEmpty": "Brak. Agenci stosują swoje domyślne wytyczne.",
1150
- "bestPracticesHint": "Fragmenty dobrych praktyk dołączane do promptów agentów tego zadania, oprócz standardów na poziomie usługi."
1215
+ "bestPracticesHint": "Fragmenty dobrych praktyk dołączane do promptów agentów tego zadania, oprócz standardów na poziomie usługi.",
1216
+ "moduleNone": "Bez modułu"
1151
1217
  },
1152
1218
  "runSettings": {
1153
1219
  "title": "Ustawienia uruchamiania",
@@ -3664,6 +3730,15 @@
3664
3730
  "advisory": "Informacyjny (zapisuj, nigdy nie wstrzymuj)",
3665
3731
  "off": "Wyłączone (pomiń kontrolę)"
3666
3732
  }
3733
+ },
3734
+ "doneLane": {
3735
+ "heading": "Kolumna „Ukończone”",
3736
+ "body": "Ile ukończonej pracy pozostaje widoczne w kolumnie „Ukończone” każdej usługi. Oba limity tylko ukrywają karty: ukryte zadanie zostaje na tablicy, nadal da się je wyszukać i nadal liczy się do sumy kolumny.",
3737
+ "maxItems": "Wyświetlane karty",
3738
+ "zeroHint": "Ukończone zadania będą liczone, ale nigdy wypisywane.",
3739
+ "ageToggle": "Ukryj także zadania ukończone dawno temu",
3740
+ "days": "Przechowuj (dni)",
3741
+ "hidesOnlyHint": "Nic nie jest usuwane. Zadania bez zapisanej daty ukończenia nigdy nie są ukrywane z powodu wieku."
3667
3742
  }
3668
3743
  },
3669
3744
  "localModelEndpoints": {
@@ -509,6 +509,71 @@
509
509
  "label": "Epik",
510
510
  "activeCount": "{count} etkin",
511
511
  "noTasksYet": "Henüz görev yok"
512
+ },
513
+ "lanes": {
514
+ "viewTitle": "Düzenle",
515
+ "resetView": "Varsayılanlara sıfırla",
516
+ "notStarted": {
517
+ "label": "Başlamadı",
518
+ "empty": "Ele alınmayı bekleyen bir şey yok."
519
+ },
520
+ "inProgress": {
521
+ "label": "Sürüyor",
522
+ "empty": "Devam eden çalıştırma yok."
523
+ },
524
+ "needsYou": {
525
+ "label": "Seni bekliyor",
526
+ "empty": "Seni bekleyen bir şey yok."
527
+ },
528
+ "done": {
529
+ "label": "Tamamlandı",
530
+ "empty": "Henüz tamamlanan bir şey yok.",
531
+ "hiddenByCap": "+{count} gösterilmiyor",
532
+ "hiddenByAge": "+{count} saklama penceresinin dışında",
533
+ "undated": "{count} kayıtlı tarihi yok",
534
+ "allWithheld": "Tamamlanan görevler burada sayılır ancak bu panonun ayarları gereği listelenmez."
535
+ },
536
+ "reason": {
537
+ "unstarted": "Başlamaya hazır",
538
+ "dependencies": "Bir bağımlılığı bekliyor",
539
+ "running": "Çalışıyor",
540
+ "backgroundReview": "Arka planda inceleniyor",
541
+ "decision": "Karar gerekiyor",
542
+ "approval": "Onay gerekiyor",
543
+ "failed": "Çalıştırma başarısız",
544
+ "budgetPaused": "Harcama bütçesi nedeniyle duraklatıldı",
545
+ "prAwaitingMerge": "Pull request birleştirilmeyi bekliyor",
546
+ "parked": "Bir kişiyi bekliyor",
547
+ "unclassified": "Tanınmayan durum",
548
+ "merged": "Birleştirildi"
549
+ },
550
+ "sort": {
551
+ "heading": "Sıralama",
552
+ "smart": "Her kolon için önerilen",
553
+ "title": "Başlık (A–Z)",
554
+ "oldest_activity": "En sessizler önce",
555
+ "newest_activity": "En son etkin olanlar",
556
+ "longest_wait": "En uzun bekleyenler",
557
+ "severity_desc": "Önem (en yüksekten)",
558
+ "impact_desc": "Tahmini etki (en yüksekten)",
559
+ "task_type": "Görev türü"
560
+ },
561
+ "group": {
562
+ "heading": "Gruplama",
563
+ "none": "Gruplama yok",
564
+ "module": "Modül",
565
+ "task_type": "Görev türü",
566
+ "initiative": "Girişim",
567
+ "epic": "Epik",
568
+ "blocking_reason": "Neye ihtiyacı var",
569
+ "catchAll": {
570
+ "module": "Modül yok",
571
+ "task_type": "Tür yok",
572
+ "initiative": "Bir girişimde değil",
573
+ "epic": "Bir epikte değil",
574
+ "blocking_reason": "Sınıflandırılmadı"
575
+ }
576
+ }
512
577
  }
513
578
  },
514
579
  "contextAttachments": {
@@ -1143,11 +1208,12 @@
1143
1208
  "title": "Yapı",
1144
1209
  "hint": "Bu görevin servisi içinde nerede bulunduğu ve ajanlarının hangi kod standartlarını izlediği.",
1145
1210
  "module": "Modül",
1146
- "modulePlaceholder": "örn. Sessions (yeniyse uygulama sırasında oluşturulur)",
1147
- "moduleHint": "Bu görevin ait olduğu servis modülü. Bir ad yazın; henüz var olmayan bir modül, görev uygulanırken oluşturulur.",
1211
+ "modulePlaceholder": "Bir modül seç veya yaz",
1212
+ "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
1213
  "bestPractices": "En iyi uygulamalar",
1149
1214
  "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ı."
1215
+ "bestPracticesHint": "Servis düzeyindeki standartlara ek olarak bu görevin ajanlarının prompt'larına eklenen en iyi uygulama parçaları.",
1216
+ "moduleNone": "Modül yok"
1151
1217
  },
1152
1218
  "runSettings": {
1153
1219
  "title": "Çalıştırma ayarları",
@@ -3806,6 +3872,15 @@
3806
3872
  "advisory": "Bilgilendirici (kaydet, asla bekletme)",
3807
3873
  "off": "Kapalı (denetimi atla)"
3808
3874
  }
3875
+ },
3876
+ "doneLane": {
3877
+ "heading": "«Tamamlandı» kolonu",
3878
+ "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.",
3879
+ "maxItems": "Gösterilen kartlar",
3880
+ "zeroHint": "Tamamlanan görevler sayılacak ama hiç listelenmeyecek.",
3881
+ "ageToggle": "Uzun süre önce tamamlanan görevleri de gizle",
3882
+ "days": "Sakla (gün)",
3883
+ "hidesOnlyHint": "Hiçbir şey silinmez. Tamamlanma tarihi kayıtlı olmayan görevler yaşları nedeniyle asla gizlenmez."
3809
3884
  }
3810
3885
  },
3811
3886
  "localModelEndpoints": {
@@ -509,6 +509,71 @@
509
509
  "label": "Епік",
510
510
  "activeCount": "{count} активних",
511
511
  "noTasksYet": "Поки немає завдань"
512
+ },
513
+ "lanes": {
514
+ "viewTitle": "Упорядкувати",
515
+ "resetView": "Скинути до типових значень",
516
+ "notStarted": {
517
+ "label": "Не розпочато",
518
+ "empty": "Немає нічого, що очікує на виконання."
519
+ },
520
+ "inProgress": {
521
+ "label": "У роботі",
522
+ "empty": "Немає активних запусків."
523
+ },
524
+ "needsYou": {
525
+ "label": "Потрібні ви",
526
+ "empty": "Ніщо не очікує на вас."
527
+ },
528
+ "done": {
529
+ "label": "Готово",
530
+ "empty": "Поки нічого не завершено.",
531
+ "hiddenByCap": "+{count} не показано",
532
+ "hiddenByAge": "+{count} поза вікном зберігання",
533
+ "undated": "{count} без записаної дати",
534
+ "allWithheld": "Завершені завдання тут підраховуються, але не перелічуються, згідно з налаштуваннями цієї дошки."
535
+ },
536
+ "reason": {
537
+ "unstarted": "Готове до старту",
538
+ "dependencies": "Очікує на залежність",
539
+ "running": "Виконується",
540
+ "backgroundReview": "Перевірка у фоні",
541
+ "decision": "Потрібне рішення",
542
+ "approval": "Потрібне схвалення",
543
+ "failed": "Запуск не вдався",
544
+ "budgetPaused": "Припинено через бюджет витрат",
545
+ "prAwaitingMerge": "Pull request очікує на злиття",
546
+ "parked": "Очікує на людину",
547
+ "unclassified": "Нерозпізнаний стан",
548
+ "merged": "Злито"
549
+ },
550
+ "sort": {
551
+ "heading": "Сортувати за",
552
+ "smart": "Рекомендовано для кожної колонки",
553
+ "title": "Назва (А–Я)",
554
+ "oldest_activity": "Найтихіші спершу",
555
+ "newest_activity": "Найактивніші останнім часом",
556
+ "longest_wait": "Ті, що чекають найдовше",
557
+ "severity_desc": "Критичність (від вищої)",
558
+ "impact_desc": "Оцінений вплив (від вищого)",
559
+ "task_type": "Тип завдання"
560
+ },
561
+ "group": {
562
+ "heading": "Групувати за",
563
+ "none": "Без групування",
564
+ "module": "Модуль",
565
+ "task_type": "Тип завдання",
566
+ "initiative": "Ініціатива",
567
+ "epic": "Епік",
568
+ "blocking_reason": "Що потрібно",
569
+ "catchAll": {
570
+ "module": "Без модуля",
571
+ "task_type": "Без типу",
572
+ "initiative": "Поза ініціативою",
573
+ "epic": "Поза епіком",
574
+ "blocking_reason": "Не класифіковано"
575
+ }
576
+ }
512
577
  }
513
578
  },
514
579
  "contextAttachments": {
@@ -1143,11 +1208,12 @@
1143
1208
  "title": "Структура",
1144
1209
  "hint": "Де це завдання розташоване у своєму сервісі та яких стандартів коду дотримуються його агенти.",
1145
1210
  "module": "Модуль",
1146
- "modulePlaceholder": "напр. Сесії (створюється під час реалізації, якщо новий)",
1147
- "moduleHint": "Модуль сервісу, до якого належить це завдання. Введіть назву; модуль, якого ще немає, буде створено під час реалізації завдання.",
1211
+ "modulePlaceholder": "Виберіть або введіть модуль",
1212
+ "moduleHint": "Модуль сервісу, до якого належить це завдання; дошка групує колонки за ним. Модуль, якого ще немає, створюється під час реалізації завдання.",
1148
1213
  "bestPractices": "Найкращі практики",
1149
1214
  "bestPracticesEmpty": "Немає. Агенти дотримуються типових інструкцій.",
1150
- "bestPracticesHint": "Фрагменти найкращих практик, що додаються до промптів агентів цього завдання, на додачу до стандартів рівня сервісу."
1215
+ "bestPracticesHint": "Фрагменти найкращих практик, що додаються до промптів агентів цього завдання, на додачу до стандартів рівня сервісу.",
1216
+ "moduleNone": "Без модуля"
1151
1217
  },
1152
1218
  "runSettings": {
1153
1219
  "title": "Налаштування запуску",
@@ -3664,6 +3730,15 @@
3664
3730
  "advisory": "Рекомендаційний (записувати, ніколи не зупиняти)",
3665
3731
  "off": "Вимкнено (пропускати перевірку)"
3666
3732
  }
3733
+ },
3734
+ "doneLane": {
3735
+ "heading": "Колонка «Готово»",
3736
+ "body": "Скільки завершеної роботи колонка «Готово» кожного сервісу тримає на видноті. Обидва обмеження лише приховують картки: приховане завдання залишається на дошці, залишається доступним для пошуку й залишається у загальній кількості колонки.",
3737
+ "maxItems": "Показані картки",
3738
+ "zeroHint": "Завершені завдання підраховуватимуться, але не перелічуватимуться.",
3739
+ "ageToggle": "Також приховувати завдання, завершені давно",
3740
+ "days": "Зберігати (днів)",
3741
+ "hidesOnlyHint": "Нічого не видаляється. Завдання без записаної дати завершення ніколи не приховуються за віком."
3667
3742
  }
3668
3743
  },
3669
3744
  "localModelEndpoints": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.275.0",
3
+ "version": "0.276.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.314.0"
43
+ "@cat-factory/contracts": "0.315.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>