Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
85892d3
Add port labels store and keyboard shortcut (L)
Feb 4, 2026
601b15f
Add showPortLabels parameter to node dimension calculation
Feb 4, 2026
035856e
Implement port labels rendering in BaseNode
Feb 4, 2026
3f173d0
Add per-node port label toggles for input/output visibility
Feb 4, 2026
5f831d9
Fix port label menu icon to use existing type icon
Feb 4, 2026
3e7cbef
Add tag icon for port label menu items
Feb 4, 2026
27a1219
Only show port label menu items when node has inputs/outputs
Feb 4, 2026
b149f11
Fix port label grid layout for source/sink nodes
Feb 4, 2026
5840327
Fix show-labels class to only apply when labels actually displayed
Feb 4, 2026
76a6fdf
Add min-width:0 to node-inner and grid fallback for robustness
Feb 4, 2026
a992e9e
Consolidate dimension calculation and port label utilities
Feb 4, 2026
28fa973
Fix theme colors to match app.css and add textDisabled
Feb 4, 2026
c224db7
Refactor SVG export to use dom-to-svg library
Feb 4, 2026
65cceec
Update README with port labels docs and dom-to-svg dependency
Feb 4, 2026
b9d476e
Revert to manual SVG renderer (dom-to-svg incompatible with SvelteFlow)
Feb 4, 2026
01caaa1
Refactor: extract buildPortLabelItems() helper to deduplicate context…
milanofthe Feb 9, 2026
275d784
Refactor: replace 42 CSS grid selectors with inline styles from gridL…
milanofthe Feb 9, 2026
1307a38
Add port label rendering to SVG export with auto-resolve from store
milanofthe Feb 9, 2026
1608e6a
Add Adder operations as port labels via custom parser in portLabelParams
milanofthe Feb 9, 2026
8c2cff3
Fix Adder operations parser to strip surrounding Python quotes
milanofthe Feb 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ Press `?` to see all shortcuts in the app. Key shortcuts:
| | `X` / `Y` | Flip H/V |
| | `Arrows` | Nudge selection |
| **Wires** | `\` | Add waypoint to selected edge |
| **Labels** | `L` | Toggle port labels |
| **View** | `F` | Fit view |
| | `H` | Go to root |
| | `T` | Toggle theme |
Expand Down Expand Up @@ -621,6 +622,15 @@ Shapes are defined in `src/lib/nodes/shapes/registry.ts` and applied via CSS cla

Colors are CSS-driven - see `src/app.css` for variables and `src/lib/utils/colors.ts` for palettes.

### Port Labels

Port labels show the name of each input/output port alongside the node. Toggle globally with `L` key, or per-node via right-click menu.

- **Global toggle**: Press `L` to show/hide port labels for all nodes
- **Per-node override**: Right-click node → "Show Input Labels" / "Show Output Labels"
- **Truncation**: Labels are truncated to 5 characters for compact display
- **SVG export**: Port labels are included when exporting the graph as SVG

### Adding Custom Shapes

1. Register the shape in `src/lib/nodes/shapes/registry.ts`:
Expand Down
12 changes: 0 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 61 additions & 8 deletions src/lib/components/contextMenuBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,44 @@ import { hasExportableData, exportRecordingData } from '$lib/utils/csvExport';
import { exportToSVG } from '$lib/export/svg';
import { downloadSvg } from '$lib/utils/download';
import { plotSettingsStore, DEFAULT_BLOCK_SETTINGS } from '$lib/stores/plotSettings';
import { portLabelsStore } from '$lib/stores/portLabels';
import { getEffectivePortLabelVisibility } from '$lib/utils/portLabels';
import type { NodeInstance } from '$lib/types/nodes';

/** Divider menu item */
const DIVIDER: MenuItemType = { label: '', action: () => {}, divider: true };

/** Build port label toggle menu items for a node */
function buildPortLabelItems(nodeId: string, node: NodeInstance): MenuItemType[] {
const globalLabels = get(portLabelsStore);
const { inputs: showInputLabels, outputs: showOutputLabels } = getEffectivePortLabelVisibility(node, globalLabels);
const hasInputs = node.inputs && node.inputs.length > 0;
const hasOutputs = node.outputs && node.outputs.length > 0;

if (!hasInputs && !hasOutputs) return [];

const items: MenuItemType[] = [DIVIDER];
if (hasInputs) {
items.push({
label: showInputLabels ? 'Hide Input Labels' : 'Show Input Labels',
icon: 'tag',
action: () => historyStore.mutate(() =>
graphStore.updateNodeParams(nodeId, { _showInputLabels: !showInputLabels })
)
});
}
if (hasOutputs) {
items.push({
label: showOutputLabels ? 'Hide Output Labels' : 'Show Output Labels',
icon: 'tag',
action: () => historyStore.mutate(() =>
graphStore.updateNodeParams(nodeId, { _showOutputLabels: !showOutputLabels })
)
});
}
return items;
}

/** Show block code in preview dialog */
function showBlockCode(nodeId: string): void {
const node = graphStore.getNode(nodeId);
Expand Down Expand Up @@ -73,7 +107,7 @@ function buildNodeMenu(nodeId: string): MenuItemType[] {

// Interface blocks have limited options
if (isInterface) {
return [
const items: MenuItemType[] = [
{
label: 'Properties',
icon: 'settings',
Expand All @@ -84,19 +118,26 @@ function buildNodeMenu(nodeId: string): MenuItemType[] {
label: 'Exit Subsystem',
icon: 'exit',
action: () => graphStore.drillUp()
},
}
];

items.push(...buildPortLabelItems(nodeId, node));

items.push(
DIVIDER,
{
label: 'View Code',
icon: 'braces',
action: () => showBlockCode(nodeId)
}
];
);

return items;
}

// Subsystem blocks get "Enter" option
if (isSubsystem) {
return [
const items: MenuItemType[] = [
{
label: 'Properties',
icon: 'settings',
Expand All @@ -107,7 +148,12 @@ function buildNodeMenu(nodeId: string): MenuItemType[] {
icon: 'enter',
shortcut: 'Dbl-click',
action: () => graphStore.drillDown(nodeId)
},
}
];

items.push(...buildPortLabelItems(nodeId, node));

items.push(
DIVIDER,
{
label: 'View Code',
Expand Down Expand Up @@ -145,7 +191,9 @@ function buildNodeMenu(nodeId: string): MenuItemType[] {
shortcut: 'Del',
action: () => historyStore.mutate(() => graphStore.removeNode(nodeId))
}
];
);

return items;
}

// Check if this is a recording node (Scope or Spectrum)
Expand All @@ -159,14 +207,19 @@ function buildNodeMenu(nodeId: string): MenuItemType[] {
icon: 'settings',
shortcut: 'Dbl-click',
action: () => openNodeDialog(nodeId)
},
}
];

items.push(...buildPortLabelItems(nodeId, node));

items.push(
DIVIDER,
{
label: 'View Code',
icon: 'braces',
action: () => showBlockCode(nodeId)
}
];
);

// Add CSV export for recording nodes
if (isRecordingNode) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/dialogs/KeyboardShortcutsDialog.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
{ keys: ['H'], description: 'Go to root' },
{ keys: ['+'], description: 'Zoom in' },
{ keys: ['-'], description: 'Zoom out' },
{ keys: ['L'], description: 'Port labels' },
{ keys: ['T'], description: 'Theme' }
]
},
Expand Down
5 changes: 5 additions & 0 deletions src/lib/components/icons/Icon.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@
<circle cx="8.5" cy="8.5" r="1.5"/>
<polyline points="21 15 16 10 5 21"/>
</svg>
{:else if name === 'tag'}
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"/>
<line x1="7" y1="7" x2="7.01" y2="7"/>
</svg>
{:else if name === 'font-size-increase'}
<svg width={size} height={size} viewBox="0 0 24 24" fill="currentColor" stroke="none">
<text x="2" y="18" font-size="16" font-weight="700" font-family="system-ui, sans-serif">A</text>
Expand Down
Loading