@tscircuit/schematic-viewer 2.0.27 → 2.0.29

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/dist/index.js CHANGED
@@ -195,7 +195,30 @@ var useChangeSchematicTracesForMovedComponents = ({
195
195
  // lib/hooks/useSchematicGroupsOverlay.ts
196
196
  import { useEffect as useEffect3 } from "react";
197
197
  import { su as su3 } from "@tscircuit/soup-util";
198
- var useSchematicGroupsOverlay = (svgDivRef, circuitJson, circuitJsonKey, showGroups) => {
198
+ var GROUP_COLORS = [
199
+ "#8B0000",
200
+ // Dark Red
201
+ "#2F4F4F",
202
+ // Dark Slate Gray
203
+ "#191970",
204
+ // Midnight Blue
205
+ "#006400",
206
+ // Dark Green
207
+ "#FF4500",
208
+ // Dark Orange
209
+ "#800080",
210
+ // Purple
211
+ "#2E8B57",
212
+ // Sea Green
213
+ "#B8860B",
214
+ // Dark Goldenrod
215
+ "#C71585",
216
+ // Medium Violet Red
217
+ "#008B8B"
218
+ // Dark Cyan
219
+ ];
220
+ var useSchematicGroupsOverlay = (options) => {
221
+ const { svgDivRef, circuitJson, circuitJsonKey, showGroups } = options;
199
222
  useEffect3(() => {
200
223
  if (!svgDivRef.current || !showGroups || !circuitJson || circuitJson.length === 0) {
201
224
  if (svgDivRef.current) {
@@ -213,10 +236,40 @@ var useSchematicGroupsOverlay = (svgDivRef, circuitJson, circuitJsonKey, showGro
213
236
  const existingOverlays = svg.querySelectorAll(".schematic-group-overlay");
214
237
  existingOverlays.forEach((overlay) => overlay.remove());
215
238
  try {
216
- const sourceGroups = su3(circuitJson).source_group?.list() || [];
239
+ const sourceGroups = su3(circuitJson).source_group?.list().filter((x) => !!!x.is_subcircuit) || [];
217
240
  const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
241
+ const sourceGroupHierarchy = /* @__PURE__ */ new Map();
242
+ sourceGroups.forEach((group) => {
243
+ const groupWithParent = group;
244
+ if (groupWithParent.parent_source_group_id) {
245
+ const children = sourceGroupHierarchy.get(groupWithParent.parent_source_group_id) || [];
246
+ children.push(group.source_group_id);
247
+ sourceGroupHierarchy.set(
248
+ groupWithParent.parent_source_group_id,
249
+ children
250
+ );
251
+ }
252
+ });
253
+ const getAllDescendantSourceGroups = (sourceGroupId) => {
254
+ const descendants = [];
255
+ const children = sourceGroupHierarchy.get(sourceGroupId) || [];
256
+ for (const child of children) {
257
+ descendants.push(child);
258
+ descendants.push(...getAllDescendantSourceGroups(child));
259
+ }
260
+ return descendants;
261
+ };
262
+ const getGroupDepthLevel = (sourceGroupId) => {
263
+ const groupWithParent = sourceGroups.find(
264
+ (g) => g.source_group_id === sourceGroupId
265
+ );
266
+ if (!groupWithParent?.parent_source_group_id) {
267
+ return 0;
268
+ }
269
+ return 1 + getGroupDepthLevel(groupWithParent.parent_source_group_id);
270
+ };
271
+ const hasMeaningfulGroups = sourceGroups.length > 0 && sourceGroups.some((group) => group.name && group.name.trim() !== "");
218
272
  let groupsToRender = [];
219
- const hasMeaningfulGroups = sourceGroups.length > 0 && sourceGroups.some((group) => group.name && group.name !== "default" && group.name !== "");
220
273
  if (hasMeaningfulGroups) {
221
274
  const groupMap = /* @__PURE__ */ new Map();
222
275
  for (const comp of schematicComponents) {
@@ -230,84 +283,138 @@ var useSchematicGroupsOverlay = (svgDivRef, circuitJson, circuitJsonKey, showGro
230
283
  groupMap.get(sourceComp.source_group_id).push(comp);
231
284
  }
232
285
  }
233
- groupsToRender = Array.from(groupMap.entries()).map(
234
- ([groupId, components], index) => {
235
- const group = sourceGroups.find(
236
- (g) => g.source_group_id === groupId
237
- );
238
- return {
239
- id: groupId,
240
- name: group?.name || `Group ${index + 1}`,
241
- components,
242
- color: getGroupColor(index)
243
- };
244
- }
245
- );
246
- } else {
247
- const componentTypeGroups = /* @__PURE__ */ new Map();
248
- for (const comp of schematicComponents) {
249
- const sourceComp = su3(circuitJson).source_component.get(
250
- comp.source_component_id
286
+ sourceGroups.forEach((group, index) => {
287
+ let groupComponents = groupMap.get(group.source_group_id) || [];
288
+ const descendantGroups = getAllDescendantSourceGroups(
289
+ group.source_group_id
251
290
  );
252
- if (sourceComp) {
253
- const componentType = sourceComp.ftype || "other";
254
- if (!componentTypeGroups.has(componentType)) {
255
- componentTypeGroups.set(componentType, []);
256
- }
257
- componentTypeGroups.get(componentType).push(comp);
291
+ for (const descendantGroupId of descendantGroups) {
292
+ const descendantComponents = groupMap.get(descendantGroupId) || [];
293
+ groupComponents = [...groupComponents, ...descendantComponents];
258
294
  }
259
- }
260
- groupsToRender = Array.from(componentTypeGroups.entries()).map(
261
- ([type, components], index) => ({
262
- id: `type_${type}`,
263
- name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
264
- components,
265
- color: getGroupColor(index)
266
- })
267
- );
295
+ if (groupComponents.length > 0) {
296
+ const depthLevel = getGroupDepthLevel(group.source_group_id);
297
+ const hasChildren = getAllDescendantSourceGroups(group.source_group_id).length > 0;
298
+ if (group.name?.startsWith("unnamed_board")) return;
299
+ groupsToRender.push({
300
+ id: group.source_group_id,
301
+ name: group.name || `Group ${index + 1}`,
302
+ components: groupComponents,
303
+ color: GROUP_COLORS[index % GROUP_COLORS.length],
304
+ depthLevel,
305
+ hasChildren,
306
+ sourceGroupId: group.source_group_id
307
+ });
308
+ }
309
+ });
268
310
  }
269
- groupsToRender.forEach((group, groupIndex) => {
311
+ const viewBox = svg.viewBox.baseVal;
312
+ const svgRect = svg.getBoundingClientRect();
313
+ const scale = Math.min(
314
+ svgRect.width / viewBox.width,
315
+ svgRect.height / viewBox.height
316
+ ) || 1;
317
+ groupsToRender.sort((a, b) => a.depthLevel - b.depthLevel);
318
+ groupsToRender.forEach((group) => {
270
319
  if (group.components.length === 0) return;
271
320
  const groupBounds = calculateGroupBounds(group.components, svg);
272
321
  if (!groupBounds) return;
322
+ const basePadding = Math.max(8, Math.min(25, 15 / Math.max(scale, 0.3)));
323
+ const hierarchyPadding = group.hasChildren ? basePadding * 0.6 : 0;
324
+ const totalPadding = basePadding + hierarchyPadding;
325
+ const baseStrokeWidth = Math.max(1, 2 / Math.max(scale, 0.5));
326
+ const strokeWidth = group.depthLevel === 0 ? baseStrokeWidth : baseStrokeWidth * 0.7;
327
+ const baseDashSize = Math.max(4, 8 / Math.max(scale, 0.5));
328
+ const dashMultiplier = group.hasChildren ? 1.3 : 1;
329
+ const dashSize = baseDashSize * dashMultiplier;
330
+ const gapSize = dashSize * 0.5;
273
331
  const groupOverlay = document.createElementNS(
274
332
  "http://www.w3.org/2000/svg",
275
333
  "rect"
276
334
  );
277
335
  groupOverlay.setAttribute("class", "schematic-group-overlay");
278
- groupOverlay.setAttribute("x", (groupBounds.minX - 25).toString());
279
- groupOverlay.setAttribute("y", (groupBounds.minY - 25).toString());
336
+ groupOverlay.setAttribute(
337
+ "x",
338
+ (groupBounds.minX - totalPadding).toString()
339
+ );
340
+ groupOverlay.setAttribute(
341
+ "y",
342
+ (groupBounds.minY - totalPadding).toString()
343
+ );
280
344
  groupOverlay.setAttribute(
281
345
  "width",
282
- (groupBounds.maxX - groupBounds.minX + 50).toString()
346
+ (groupBounds.maxX - groupBounds.minX + totalPadding * 2).toString()
283
347
  );
284
348
  groupOverlay.setAttribute(
285
349
  "height",
286
- (groupBounds.maxY - groupBounds.minY + 50).toString()
350
+ (groupBounds.maxY - groupBounds.minY + totalPadding * 2).toString()
287
351
  );
288
352
  groupOverlay.setAttribute("fill", "none");
289
353
  groupOverlay.setAttribute("stroke", group.color);
290
- groupOverlay.setAttribute("stroke-width", "3");
291
- groupOverlay.setAttribute("stroke-dasharray", "8,4");
354
+ groupOverlay.setAttribute("stroke-width", strokeWidth.toString());
355
+ groupOverlay.setAttribute("stroke-dasharray", `${dashSize},${gapSize}`);
292
356
  groupOverlay.setAttribute("opacity", "0.8");
293
357
  groupOverlay.setAttribute("rx", "4");
294
358
  groupOverlay.setAttribute("ry", "4");
359
+ const baseFontSize = Math.max(
360
+ 6,
361
+ Math.min(20, 14 / Math.max(scale, 0.2))
362
+ );
363
+ const fontSizeReduction = group.depthLevel === 0 || group.depthLevel === 1 ? 0 : group.depthLevel * 0.2;
364
+ const fontSize = baseFontSize * (1 - fontSizeReduction);
365
+ const labelPadding = Math.max(1, fontSize * 0.2);
366
+ const labelText = group.name;
367
+ const tempText = document.createElementNS(
368
+ "http://www.w3.org/2000/svg",
369
+ "text"
370
+ );
371
+ tempText.setAttribute("font-size", fontSize.toString());
372
+ tempText.setAttribute("font-family", "Arial, sans-serif");
373
+ tempText.textContent = labelText;
374
+ svg.appendChild(tempText);
375
+ const textBBox = tempText.getBBox();
376
+ svg.removeChild(tempText);
377
+ const labelWidth = textBBox.width + labelPadding * 2;
378
+ const labelHeight = fontSize + labelPadding * 2;
379
+ const labelX = groupBounds.minX - totalPadding;
380
+ const labelY = groupBounds.minY - totalPadding - labelHeight;
381
+ const labelBg = document.createElementNS(
382
+ "http://www.w3.org/2000/svg",
383
+ "rect"
384
+ );
385
+ labelBg.setAttribute("class", "schematic-group-overlay");
386
+ labelBg.setAttribute("x", labelX.toString());
387
+ labelBg.setAttribute("y", (labelY - labelHeight).toString());
388
+ labelBg.setAttribute("width", labelWidth.toString());
389
+ labelBg.setAttribute("height", labelHeight.toString());
390
+ labelBg.setAttribute("fill", "transparent");
391
+ labelBg.setAttribute("rx", "3");
392
+ labelBg.setAttribute("ry", "3");
295
393
  const groupLabel = document.createElementNS(
296
394
  "http://www.w3.org/2000/svg",
297
395
  "text"
298
396
  );
299
397
  groupLabel.setAttribute("class", "schematic-group-overlay");
300
- groupLabel.setAttribute("x", (groupBounds.minX - 10).toString());
301
- groupLabel.setAttribute("y", (groupBounds.minY - 8).toString());
398
+ groupLabel.setAttribute("x", (labelX + labelPadding).toString());
399
+ groupLabel.setAttribute(
400
+ "y",
401
+ (labelY + labelHeight - labelPadding).toString()
402
+ );
302
403
  groupLabel.setAttribute("fill", group.color);
303
- groupLabel.setAttribute("font-size", "14");
404
+ groupLabel.setAttribute("font-size", fontSize.toString());
304
405
  groupLabel.setAttribute("font-family", "Arial, sans-serif");
305
- groupLabel.setAttribute("font-weight", "bold");
306
- groupLabel.setAttribute("stroke", "#fff");
307
- groupLabel.setAttribute("stroke-width", "0.5");
308
- groupLabel.setAttribute("paint-order", "stroke fill");
309
- groupLabel.textContent = group.name;
406
+ groupLabel.setAttribute(
407
+ "font-weight",
408
+ group.depthLevel === 0 ? "600" : "500"
409
+ );
410
+ groupLabel.setAttribute("stroke", group.color);
411
+ groupLabel.setAttribute(
412
+ "stroke-width",
413
+ Math.max(0.2, fontSize * 0.02).toString()
414
+ );
415
+ groupLabel.textContent = labelText;
310
416
  svg.appendChild(groupOverlay);
417
+ svg.appendChild(labelBg);
311
418
  svg.appendChild(groupLabel);
312
419
  });
313
420
  } catch (error) {
@@ -315,27 +422,6 @@ var useSchematicGroupsOverlay = (svgDivRef, circuitJson, circuitJsonKey, showGro
315
422
  }
316
423
  }, [svgDivRef, circuitJsonKey, showGroups]);
317
424
  };
318
- function getGroupColor(index) {
319
- const colors2 = [
320
- "#FF6B6B",
321
- // Red
322
- "#4ECDC4",
323
- // Teal
324
- "#45B7D1",
325
- // Blue
326
- "#96CEB4",
327
- // Green
328
- "#FF8C42",
329
- // Orange
330
- "#DDA0DD",
331
- // Plum
332
- "#98D8C8",
333
- // Mint
334
- "#F7DC6F"
335
- // Light Yellow
336
- ];
337
- return colors2[index % colors2.length];
338
- }
339
425
  function calculateGroupBounds(components, svg) {
340
426
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
341
427
  for (const component of components) {
@@ -1500,7 +1586,12 @@ var SchematicViewer = ({
1500
1586
  activeEditEvent,
1501
1587
  editEvents: editEventsWithUnappliedEditEvents
1502
1588
  });
1503
- useSchematicGroupsOverlay(svgDivRef, circuitJson, circuitJsonKey, showSchematicGroups);
1589
+ useSchematicGroupsOverlay({
1590
+ svgDivRef,
1591
+ circuitJson,
1592
+ circuitJsonKey,
1593
+ showGroups: showSchematicGroups
1594
+ });
1504
1595
  const svgDiv = useMemo2(
1505
1596
  () => /* @__PURE__ */ jsx9(
1506
1597
  "div",