@tscircuit/schematic-viewer 2.0.28 → 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,6 +195,28 @@ 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 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
+ ];
198
220
  var useSchematicGroupsOverlay = (options) => {
199
221
  const { svgDivRef, circuitJson, circuitJsonKey, showGroups } = options;
200
222
  useEffect3(() => {
@@ -214,10 +236,40 @@ var useSchematicGroupsOverlay = (options) => {
214
236
  const existingOverlays = svg.querySelectorAll(".schematic-group-overlay");
215
237
  existingOverlays.forEach((overlay) => overlay.remove());
216
238
  try {
217
- const sourceGroups = su3(circuitJson).source_group?.list() || [];
239
+ const sourceGroups = su3(circuitJson).source_group?.list().filter((x) => !!!x.is_subcircuit) || [];
218
240
  const schematicComponents = su3(circuitJson).schematic_component?.list() || [];
219
- let groupsToRender = [];
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
+ };
220
271
  const hasMeaningfulGroups = sourceGroups.length > 0 && sourceGroups.some((group) => group.name && group.name.trim() !== "");
272
+ let groupsToRender = [];
221
273
  if (hasMeaningfulGroups) {
222
274
  const groupMap = /* @__PURE__ */ new Map();
223
275
  for (const comp of schematicComponents) {
@@ -231,84 +283,138 @@ var useSchematicGroupsOverlay = (options) => {
231
283
  groupMap.get(sourceComp.source_group_id).push(comp);
232
284
  }
233
285
  }
234
- groupsToRender = Array.from(groupMap.entries()).map(
235
- ([groupId, components], index) => {
236
- const group = sourceGroups.find(
237
- (g) => g.source_group_id === groupId
238
- );
239
- return {
240
- id: groupId,
241
- name: group?.name || `Group ${index + 1}`,
242
- components,
243
- color: getGroupColor(index)
244
- };
245
- }
246
- );
247
- } else {
248
- const componentTypeGroups = /* @__PURE__ */ new Map();
249
- for (const comp of schematicComponents) {
250
- const sourceComp = su3(circuitJson).source_component.get(
251
- 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
252
290
  );
253
- if (sourceComp) {
254
- const componentType = sourceComp.ftype || "other";
255
- if (!componentTypeGroups.has(componentType)) {
256
- componentTypeGroups.set(componentType, []);
257
- }
258
- componentTypeGroups.get(componentType).push(comp);
291
+ for (const descendantGroupId of descendantGroups) {
292
+ const descendantComponents = groupMap.get(descendantGroupId) || [];
293
+ groupComponents = [...groupComponents, ...descendantComponents];
259
294
  }
260
- }
261
- groupsToRender = Array.from(componentTypeGroups.entries()).map(
262
- ([type, components], index) => ({
263
- id: `type_${type}`,
264
- name: `${type.charAt(0).toUpperCase() + type.slice(1)}s`,
265
- components,
266
- color: getGroupColor(index)
267
- })
268
- );
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
+ });
269
310
  }
270
- 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) => {
271
319
  if (group.components.length === 0) return;
272
320
  const groupBounds = calculateGroupBounds(group.components, svg);
273
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;
274
331
  const groupOverlay = document.createElementNS(
275
332
  "http://www.w3.org/2000/svg",
276
333
  "rect"
277
334
  );
278
335
  groupOverlay.setAttribute("class", "schematic-group-overlay");
279
- groupOverlay.setAttribute("x", (groupBounds.minX - 25).toString());
280
- 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
+ );
281
344
  groupOverlay.setAttribute(
282
345
  "width",
283
- (groupBounds.maxX - groupBounds.minX + 50).toString()
346
+ (groupBounds.maxX - groupBounds.minX + totalPadding * 2).toString()
284
347
  );
285
348
  groupOverlay.setAttribute(
286
349
  "height",
287
- (groupBounds.maxY - groupBounds.minY + 50).toString()
350
+ (groupBounds.maxY - groupBounds.minY + totalPadding * 2).toString()
288
351
  );
289
352
  groupOverlay.setAttribute("fill", "none");
290
353
  groupOverlay.setAttribute("stroke", group.color);
291
- groupOverlay.setAttribute("stroke-width", "3");
292
- groupOverlay.setAttribute("stroke-dasharray", "8,4");
354
+ groupOverlay.setAttribute("stroke-width", strokeWidth.toString());
355
+ groupOverlay.setAttribute("stroke-dasharray", `${dashSize},${gapSize}`);
293
356
  groupOverlay.setAttribute("opacity", "0.8");
294
357
  groupOverlay.setAttribute("rx", "4");
295
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");
296
393
  const groupLabel = document.createElementNS(
297
394
  "http://www.w3.org/2000/svg",
298
395
  "text"
299
396
  );
300
397
  groupLabel.setAttribute("class", "schematic-group-overlay");
301
- groupLabel.setAttribute("x", (groupBounds.minX - 10).toString());
302
- 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
+ );
303
403
  groupLabel.setAttribute("fill", group.color);
304
- groupLabel.setAttribute("font-size", "14");
404
+ groupLabel.setAttribute("font-size", fontSize.toString());
305
405
  groupLabel.setAttribute("font-family", "Arial, sans-serif");
306
- groupLabel.setAttribute("font-weight", "bold");
307
- groupLabel.setAttribute("stroke", "#fff");
308
- groupLabel.setAttribute("stroke-width", "0.5");
309
- groupLabel.setAttribute("paint-order", "stroke fill");
310
- 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;
311
416
  svg.appendChild(groupOverlay);
417
+ svg.appendChild(labelBg);
312
418
  svg.appendChild(groupLabel);
313
419
  });
314
420
  } catch (error) {
@@ -316,27 +422,6 @@ var useSchematicGroupsOverlay = (options) => {
316
422
  }
317
423
  }, [svgDivRef, circuitJsonKey, showGroups]);
318
424
  };
319
- function getGroupColor(index) {
320
- const colors2 = [
321
- "#FF6B6B",
322
- // Red
323
- "#4ECDC4",
324
- // Teal
325
- "#45B7D1",
326
- // Blue
327
- "#96CEB4",
328
- // Green
329
- "#FF8C42",
330
- // Orange
331
- "#DDA0DD",
332
- // Plum
333
- "#98D8C8",
334
- // Mint
335
- "#F7DC6F"
336
- // Light Yellow
337
- ];
338
- return colors2[index % colors2.length];
339
- }
340
425
  function calculateGroupBounds(components, svg) {
341
426
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
342
427
  for (const component of components) {