@cat-factory/app 0.17.0 → 0.17.1

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.
@@ -35,6 +35,15 @@ export function useTaskExpansion(container: Ref<HTMLElement | null>) {
35
35
  const ui = useUiStore()
36
36
  const store = useTaskExpansionStore()
37
37
 
38
+ // Last-known expanded height per task. A card grows downward only while it's
39
+ // granted (its pipeline list is rendered), so its live height collapses the
40
+ // moment it's denied. Testing overlap with that collapsed height is what causes
41
+ // the flashing: a denied card no longer overlaps its neighbour, gets re-granted,
42
+ // expands, overlaps again, gets denied — every frame. We cache the expanded
43
+ // height while a card is granted and project the footprint with it, so a denied
44
+ // card is still tested at its expanded extent and stays denied. Stable.
45
+ const expandedHeight = new Map<string, number>()
46
+
38
47
  function rectOf(id: string): DOMRect | null {
39
48
  const el = document.querySelector(`[data-block-id="${id}"]`) as HTMLElement | null
40
49
  return el ? el.getBoundingClientRect() : null
@@ -51,26 +60,43 @@ export function useTaskExpansion(container: Ref<HTMLElement | null>) {
51
60
  const cx = view.left + view.width / 2
52
61
  const cy = view.top + view.height / 2
53
62
 
54
- const candidates: { id: string; rect: DOMRect; dist: number }[] = []
63
+ const candidates: { id: string; rect: Rect; dist: number }[] = []
64
+ const liveIds = new Set<string>()
55
65
  for (const t of board.allTasks) {
56
66
  // Only tasks whose run actually has steps would expand a pipeline list.
57
67
  if (!execution.getByBlock(t.id)?.steps.length) continue
58
68
  const rect = rectOf(t.id)
59
69
  if (!rect) continue
60
- // Visibility: the card must intersect the board viewport.
70
+ liveIds.add(t.id)
71
+ // While a card is granted it's rendered expanded, so its live height is its
72
+ // expanded footprint — cache it. A denied card keeps its last cached value.
73
+ if (store.allowed.has(t.id)) expandedHeight.set(t.id, rect.height)
74
+ // Visibility: the card must intersect the board viewport (live rect).
61
75
  if (!intersects(rect, view)) continue
76
+ // Project the footprint downward to the expanded extent so the overlap test
77
+ // is independent of the card's current (possibly collapsed) state.
78
+ const height = Math.max(rect.height, expandedHeight.get(t.id) ?? 0)
79
+ const footprint: Rect = {
80
+ left: rect.left,
81
+ right: rect.right,
82
+ top: rect.top,
83
+ bottom: rect.top + height,
84
+ }
62
85
  // Stable anchor: the card's top-centre. It doesn't move as the card grows
63
86
  // downward, so the ordering can't oscillate as cards expand / collapse.
64
87
  const ax = rect.left + rect.width / 2
65
88
  const ay = rect.top
66
89
  const dist = (ax - cx) ** 2 + (ay - cy) ** 2
67
- candidates.push({ id: t.id, rect, dist })
90
+ candidates.push({ id: t.id, rect: footprint, dist })
68
91
  }
92
+ // Drop cached heights for cards that are gone, so the map can't grow unbounded.
93
+ for (const id of expandedHeight.keys()) if (!liveIds.has(id)) expandedHeight.delete(id)
69
94
  candidates.sort((a, b) => a.dist - b.dist)
70
95
 
71
- // Greedy by distance to centre: a candidate is granted only if its rect clears
72
- // every rect already granted, so the centre-most card wins any overlap.
73
- const claimed: DOMRect[] = []
96
+ // Greedy by distance to centre: a candidate is granted only if its projected
97
+ // footprint clears every footprint already granted, so the centre-most card
98
+ // wins any overlap.
99
+ const claimed: Rect[] = []
74
100
  const next = new Set<string>()
75
101
  for (const c of candidates) {
76
102
  if (claimed.some((r) => intersects(c.rect, r))) continue
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
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",