bugpatrol 0.1.3 → 0.1.4

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.
@@ -1,7 +1,12 @@
1
1
  // Pure layout for the agent app map. Coordinates are SVG user units.
2
2
  export function layoutGraph(screens, edges, entryId) {
3
- const w = 190, h = 150, xGap = 130, yGap = 55, pad = 45;
4
- const xStep = w + xGap, yStep = h + yGap;
3
+ const w = 190,
4
+ h = 150,
5
+ xGap = 130,
6
+ yGap = 55,
7
+ pad = 45;
8
+ const xStep = w + xGap,
9
+ yStep = h + yGap;
5
10
  const ids = new Set(screens.map((screen) => screen.id));
6
11
  const virtual = new Set(screens.filter((screen) => screen.virtual).map((screen) => screen.id));
7
12
  const valid = edges.filter((edge) => ids.has(edge.from) && ids.has(edge.to) && edge.from !== edge.to);
@@ -27,10 +32,11 @@ export function layoutGraph(screens, edges, entryId) {
27
32
  seen.add(id);
28
33
  for (const current of queue) {
29
34
  members.push(current);
30
- for (const next of neighbors.get(current)) if (!seen.has(next)) {
31
- seen.add(next);
32
- queue.push(next);
33
- }
35
+ for (const next of neighbors.get(current))
36
+ if (!seen.has(next)) {
37
+ seen.add(next);
38
+ queue.push(next);
39
+ }
34
40
  }
35
41
  components.push(members.sort());
36
42
  }
@@ -43,10 +49,12 @@ export function layoutGraph(screens, edges, entryId) {
43
49
  if (memberSet.has(entryId)) {
44
50
  const queue = [entryId];
45
51
  reachedFromEntry.add(entryId);
46
- for (const id of queue) for (const to of outgoing.get(id)) if (!reachedFromEntry.has(to)) {
47
- reachedFromEntry.add(to);
48
- queue.push(to);
49
- }
52
+ for (const id of queue)
53
+ for (const to of outgoing.get(id))
54
+ if (!reachedFromEntry.has(to)) {
55
+ reachedFromEntry.add(to);
56
+ queue.push(to);
57
+ }
50
58
  }
51
59
  const roots = [
52
60
  ...(memberSet.has(entryId) ? [entryId] : []),
@@ -57,30 +65,42 @@ export function layoutGraph(screens, edges, entryId) {
57
65
  const visit = () => {
58
66
  for (let index = 0; index < queue.length; index++) {
59
67
  const id = queue[index];
60
- for (const to of outgoing.get(id)) if (memberSet.has(to) && !layer.has(to)) {
61
- layer.set(to, layer.get(id) + 1);
62
- queue.push(to);
63
- }
68
+ for (const to of outgoing.get(id))
69
+ if (memberSet.has(to) && !layer.has(to)) {
70
+ layer.set(to, layer.get(id) + 1);
71
+ queue.push(to);
72
+ }
64
73
  }
65
74
  };
66
75
  visit();
67
76
  // A directed cycle can have no zero-incoming root.
68
- for (const id of members) if (!layer.has(id)) { layer.set(id, 0); queue.push(id); visit(); }
77
+ for (const id of members)
78
+ if (!layer.has(id)) {
79
+ layer.set(id, 0);
80
+ queue.push(id);
81
+ visit();
82
+ }
69
83
 
70
84
  const columns = Array.from({ length: Math.max(...layer.values()) + 1 }, () => []);
71
85
  for (const id of members) columns[layer.get(id)].push(id);
72
86
  const order = new Map();
73
- columns.forEach((list) => list.forEach((id, index) => order.set(id, index)));
87
+ for (const list of columns) for (const [index, id] of list.entries()) order.set(id, index);
74
88
  const sweep = (column, neighbor) => {
75
89
  const list = columns[column];
76
90
  const center = (id) => {
77
- const positions = valid.filter((edge) => edge.kind !== 'back' &&
78
- ((edge.from === id && layer.get(edge.to) === neighbor) || (edge.to === id && layer.get(edge.from) === neighbor)))
79
- .map((edge) => order.get(edge.from === id ? edge.to : edge.from)).filter((value) => value !== undefined);
91
+ const positions = valid
92
+ .filter(
93
+ (edge) =>
94
+ edge.kind !== 'back' &&
95
+ ((edge.from === id && layer.get(edge.to) === neighbor) ||
96
+ (edge.to === id && layer.get(edge.from) === neighbor)),
97
+ )
98
+ .map((edge) => order.get(edge.from === id ? edge.to : edge.from))
99
+ .filter((value) => value !== undefined);
80
100
  return positions.length ? positions.reduce((sum, value) => sum + value, 0) / positions.length : order.get(id);
81
101
  };
82
102
  list.sort((a, b) => center(a) - center(b) || a.localeCompare(b));
83
- list.forEach((id, index) => order.set(id, index));
103
+ for (const [index, id] of list.entries()) order.set(id, index);
84
104
  };
85
105
  for (let pass = 0; pass < 4; pass++) {
86
106
  for (let col = 1; col < columns.length; col++) sweep(col, col - 1);
@@ -91,23 +111,31 @@ export function layoutGraph(screens, edges, entryId) {
91
111
  let xColumn = 0;
92
112
  for (const list of columns) {
93
113
  list.forEach((id, index) => {
94
- const x = (xColumn + Math.floor(index / 10)) * xStep, y = (index % 10) * yStep;
95
- nodes.push(virtual.has(id) ? { id, x: x + 35, y: y + 53, w: 120, h: 44 }
96
- : { id, x, y, w, h });
114
+ const x = (xColumn + Math.floor(index / 10)) * xStep,
115
+ y = (index % 10) * yStep;
116
+ nodes.push(virtual.has(id) ? { id, x: x + 35, y: y + 53, w: 120, h: 44 } : { id, x, y, w, h });
97
117
  });
98
118
  xColumn += Math.ceil(list.length / 10);
99
119
  }
100
- blocks.push({ nodes, width: xColumn * xStep - xGap,
101
- height: Math.max(...nodes.map((node) => node.y + node.h)), isolated: false });
120
+ blocks.push({
121
+ nodes,
122
+ width: xColumn * xStep - xGap,
123
+ height: Math.max(...nodes.map((node) => node.y + node.h)),
124
+ isolated: false,
125
+ });
102
126
  }
103
127
 
104
128
  if (isolated.length) {
105
- blocks.push({ nodes: isolated.map((id, index) => {
106
- const x = Math.floor(index / 6) * xStep, y = 35 + (index % 6) * yStep;
107
- return virtual.has(id) ? { id, x: x + 35, y: y + 53, w: 120, h: 44 } : { id, x, y, w, h };
108
- }),
109
- width: Math.ceil(isolated.length / 6) * xStep - xGap,
110
- height: 35 + (Math.min(6, isolated.length) - 1) * yStep + h, isolated: true });
129
+ blocks.push({
130
+ nodes: isolated.map((id, index) => {
131
+ const x = Math.floor(index / 6) * xStep,
132
+ y = 35 + (index % 6) * yStep;
133
+ return virtual.has(id) ? { id, x: x + 35, y: y + 53, w: 120, h: 44 } : { id, x, y, w, h };
134
+ }),
135
+ width: Math.ceil(isolated.length / 6) * xStep - xGap,
136
+ height: 35 + (Math.min(6, isolated.length) - 1) * yStep + h,
137
+ isolated: true,
138
+ });
111
139
  }
112
140
 
113
141
  const gap = 100;
@@ -119,9 +147,13 @@ export function layoutGraph(screens, edges, entryId) {
119
147
  widths.add(width);
120
148
  }
121
149
  }
122
- let targetWidth = 0, bestScore = Infinity;
150
+ let targetWidth = 0,
151
+ bestScore = Infinity;
123
152
  for (const width of widths) {
124
- let rowWidth = 0, rowHeight = 0, usedWidth = 0, usedHeight = 0;
153
+ let rowWidth = 0,
154
+ rowHeight = 0,
155
+ usedWidth = 0,
156
+ usedHeight = 0;
125
157
  for (const block of blocks) {
126
158
  if (rowWidth && rowWidth + gap + block.width > width) {
127
159
  usedHeight += rowHeight + gap;
@@ -134,15 +166,30 @@ export function layoutGraph(screens, edges, entryId) {
134
166
  }
135
167
  const ratio = (usedWidth + pad * 2) / (usedHeight + rowHeight + pad * 2);
136
168
  const score = Math.abs(Math.log(ratio / 1.6));
137
- if (score < bestScore) { bestScore = score; targetWidth = width; }
169
+ if (score < bestScore) {
170
+ bestScore = score;
171
+ targetWidth = width;
172
+ }
138
173
  }
139
174
  const nodes = [];
140
- let x = pad, y = pad, rowHeight = 0, maxRight = 0, maxBottom = 0;
141
- let unlinkedX = null, unlinkedY = null;
175
+ let x = pad,
176
+ y = pad,
177
+ rowHeight = 0,
178
+ maxRight = 0,
179
+ maxBottom = 0;
180
+ let unlinkedX = null,
181
+ unlinkedY = null;
142
182
  for (const block of blocks) {
143
- if (x > pad && x - pad + block.width > targetWidth) { x = pad; y += rowHeight + gap; rowHeight = 0; }
183
+ if (x > pad && x - pad + block.width > targetWidth) {
184
+ x = pad;
185
+ y += rowHeight + gap;
186
+ rowHeight = 0;
187
+ }
144
188
  nodes.push(...block.nodes.map((node) => ({ ...node, x: node.x + x, y: node.y + y })));
145
- if (block.isolated) { unlinkedX = x; unlinkedY = y; }
189
+ if (block.isolated) {
190
+ unlinkedX = x;
191
+ unlinkedY = y;
192
+ }
146
193
  maxRight = Math.max(maxRight, x + block.width);
147
194
  maxBottom = Math.max(maxBottom, y + block.height);
148
195
  rowHeight = Math.max(rowHeight, block.height);
@@ -151,16 +198,27 @@ export function layoutGraph(screens, edges, entryId) {
151
198
 
152
199
  const positions = new Map(nodes.map((node) => [node.id, node]));
153
200
  const paths = valid.map((edge, index) => {
154
- const from = positions.get(edge.from), to = positions.get(edge.to);
201
+ const from = positions.get(edge.from),
202
+ to = positions.get(edge.to);
155
203
  if (to.x > from.x) {
156
- const x1 = from.x + from.w, x2 = to.x, y1 = from.y + from.h / 2, y2 = to.y + to.h / 2;
204
+ const x1 = from.x + from.w,
205
+ x2 = to.x,
206
+ y1 = from.y + from.h / 2,
207
+ y2 = to.y + to.h / 2;
157
208
  const bend = Math.max(65, (x2 - x1) / 2);
158
209
  return { ...edge, path: `M ${x1} ${y1} C ${x1 + bend} ${y1}, ${x2 - bend} ${y2}, ${x2} ${y2}` };
159
210
  }
160
211
  const y = Math.min(from.y, to.y) - 25 - (index % 5) * 12;
161
- const x1 = from.x + from.w / 2, x2 = to.x + to.w / 2;
212
+ const x1 = from.x + from.w / 2,
213
+ x2 = to.x + to.w / 2;
162
214
  return { ...edge, path: `M ${x1} ${from.y} C ${x1} ${y}, ${x2} ${y}, ${x2} ${to.y}` };
163
215
  });
164
- return { nodes, edges: paths, width: Math.max(320, maxRight + pad),
165
- height: Math.max(320, maxBottom + pad), unlinkedX, unlinkedY };
216
+ return {
217
+ nodes,
218
+ edges: paths,
219
+ width: Math.max(320, maxRight + pad),
220
+ height: Math.max(320, maxBottom + pad),
221
+ unlinkedX,
222
+ unlinkedY,
223
+ };
166
224
  }
package/src/ui/index.html CHANGED
@@ -13,12 +13,12 @@
13
13
  <strong id="project-name">Bugpatrol</strong>
14
14
  </div>
15
15
  <nav class="tabs" id="tabs">
16
- <button data-view="overview" class="active">Overview</button>
17
- <button data-view="issues">Issues</button>
18
- <button data-view="activity">Activity</button>
19
- <button data-view="screens">Screens</button>
20
- <button data-view="memory">Memory</button>
21
- <button data-view="checks">Checks</button>
16
+ <button type="button" data-view="overview" class="active">Overview</button>
17
+ <button type="button" data-view="issues">Issues</button>
18
+ <button type="button" data-view="activity">Activity</button>
19
+ <button type="button" data-view="screens">Screens</button>
20
+ <button type="button" data-view="memory">Memory</button>
21
+ <button type="button" data-view="checks">Checks</button>
22
22
  </nav>
23
23
  <div class="status">
24
24
  <span id="live" class="live" title="Live connection to the local watcher">live</span>
@@ -30,7 +30,7 @@
30
30
  <main id="view"></main>
31
31
 
32
32
  <div id="lightbox" class="lightbox" hidden>
33
- <button class="lightbox-close" aria-label="Close">&times;</button>
33
+ <button type="button" class="lightbox-close" aria-label="Close">&times;</button>
34
34
  <img id="lightbox-img" alt="">
35
35
  <p id="lightbox-caption"></p>
36
36
  </div>