@ogabrielluiz/patchflow 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.
- package/dist/{chunk-4VUBNFI4.js → chunk-YONEHQMI.js} +1 -1
- package/dist/chunk-YONEHQMI.js.map +1 -0
- package/dist/index.cjs +32 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +33 -13
- package/dist/index.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +6 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-4VUBNFI4.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// ── Signal Types ──\n\nexport type SignalType = 'audio' | 'cv' | 'pitch' | 'gate' | 'trigger' | 'clock';\n\nexport const SIGNAL_OPERATORS: Record<string, SignalType> = {\n '->': 'audio',\n '>>': 'cv',\n 'p>': 'pitch',\n 'g>': 'gate',\n 't>': 'trigger',\n 'c>': 'clock',\n};\n\n// ── Graph Primitives ──\n\nexport interface Port {\n id: string; // normalized key (lowercase, trimmed)\n display: string; // original form for rendering\n direction: 'in' | 'out';\n}\n\nexport interface Param {\n key: string;\n value: string;\n}\n\nexport interface Block {\n id: string;\n label: string;\n subLabel: string | null;\n params: Param[];\n ports: Port[];\n parentModule: string | null; // null for top-level modules, module name for sections\n voice: string | null;\n}\n\nexport interface ConnectionEndpoint {\n blockId: string;\n portId: string; // normalized key\n portDisplay: string; // original form\n}\n\nexport interface Connection {\n id: string;\n source: ConnectionEndpoint;\n target: ConnectionEndpoint;\n signalType: SignalType;\n annotation: string | null;\n graphvizExtras: Record<string, string> | null;\n}\n\n// ── Patch Graph (Parser Output) ──\n\nexport interface PatchGraph {\n declaredBlocks: Block[];\n stubBlocks: Block[];\n connections: Connection[];\n feedbackEdges: Connection[];\n signalTypeStats: Partial<Record<SignalType, number>>;\n voices: string[];\n}\n\n// ── Layout Types ──\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport interface LayoutPort extends Port {\n position: Point;\n signalType: SignalType | null;\n}\n\nexport interface LayoutBlock {\n id: string;\n label: string;\n subLabel: string | null;\n params: Param[];\n ports: LayoutPort[];\n parentModule: string | null;\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface LayoutConnection {\n id: string;\n source: ConnectionEndpoint;\n target: ConnectionEndpoint;\n signalType: SignalType;\n annotation: string | null;\n path: string; // SVG path d attribute\n isFeedback: boolean;\n sourcePoint: Point;\n targetPoint: Point;\n}\n\nexport interface LayoutResult {\n blocks: LayoutBlock[];\n connections: LayoutConnection[];\n width: number;\n height: number;\n signalTypeStats: Partial<Record<SignalType, number>>;\n /**\n * Non-fatal diagnostics produced during layout. Currently used to surface\n * internal consistency issues (e.g. computed height doesn't cover the\n * actual block content) so future regressions are visible.\n */\n warnings?: string[];\n}\n\n// ── Theme Types ──\n\nexport interface CableColor {\n stroke: string;\n plugTip: string;\n}\n\nexport interface SocketColors {\n bezel: string;\n bezelStroke: string;\n ring: string;\n hole: string;\n pin: string;\n}\n\nexport interface Theme {\n background: string;\n panel: {\n fill: string;\n stroke: string;\n highlight: string;\n shadow: string;\n cornerRadius: number;\n shadowBlur: number;\n shadowOpacity: number;\n bevelWidth: number;\n };\n label: {\n fontFamily: string;\n color: string;\n subColor: string;\n plateFill: string;\n plateStroke: string;\n };\n param: {\n plateFill: string;\n plateStroke: string;\n textColor: string;\n };\n port: {\n fontFamily: string;\n fontSize: number;\n colors: SocketColors;\n hideSocket: boolean;\n labelColor: string;\n pill: {\n show: boolean;\n fontSize: number;\n textColor: string;\n cornerRadius: number;\n };\n };\n cable: {\n width: number;\n colors: Record<SignalType, CableColor>;\n plugTipRadius: number;\n };\n annotation: {\n fontFamily: string;\n fontSize: number;\n color: string;\n haloColor: string;\n };\n grid: {\n dotColor: string;\n dotRadius: number;\n spacing: number;\n opacity: number;\n } | null;\n}\n\n// ── Options ──\n\nexport interface RenderOptions {\n theme?: DeepPartial<Theme>;\n maxWidth?: number;\n padding?: number;\n legend?: boolean | 'auto';\n}\n\nexport interface LayoutOptions {\n direction?: 'LR' | 'TB';\n nodeSep?: number;\n rankSep?: number;\n feedbackSide?: 'top' | 'bottom';\n}\n\n// ── Parse Result ──\n\nexport type ErrorCode =\n | 'SYNTAX_ERROR'\n | 'UNKNOWN_OPERATOR'\n | 'MISSING_PORT'\n | 'UNCLOSED_PAREN'\n | 'DUPLICATE_MODULE'\n | 'UNKNOWN_MODULE'\n | 'INVALID_PORT'\n | 'AMBIGUOUS_PORT_DIRECTION';\n\nexport type ErrorSeverity = 'error' | 'warning';\n\nexport interface ParseDiagnostic {\n code: ErrorCode;\n message: string;\n line: number;\n column: number;\n length: number;\n severity: ErrorSeverity;\n}\n\nexport interface ParseResult {\n graph: PatchGraph;\n errors: ParseDiagnostic[];\n warnings: ParseDiagnostic[];\n}\n\n// ── Utility Types ──\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAIO,IAAM,mBAA+C;AAAA,EAC1D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -4333,14 +4333,33 @@ function layout(graph, options = {}) {
|
|
|
4333
4333
|
const feedbackSpace = hasFeedback ? diagramBottom - maxY + feedbackArcOffset + 30 + 16 : 0;
|
|
4334
4334
|
const width = maxX + margin;
|
|
4335
4335
|
const height = maxY + margin + feedbackSpace;
|
|
4336
|
+
const warnings = checkHeightInvariant({
|
|
4337
|
+
blocks: layoutBlocks,
|
|
4338
|
+
height,
|
|
4339
|
+
hasFeedback,
|
|
4340
|
+
feedbackBottom: diagramBottom + feedbackArcOffset
|
|
4341
|
+
});
|
|
4336
4342
|
return {
|
|
4337
4343
|
blocks: layoutBlocks,
|
|
4338
4344
|
connections: layoutConnections,
|
|
4339
4345
|
width,
|
|
4340
4346
|
height,
|
|
4341
|
-
signalTypeStats: graph.signalTypeStats
|
|
4347
|
+
signalTypeStats: graph.signalTypeStats,
|
|
4348
|
+
warnings
|
|
4342
4349
|
};
|
|
4343
4350
|
}
|
|
4351
|
+
function checkHeightInvariant(args) {
|
|
4352
|
+
const { blocks, height, hasFeedback, feedbackBottom } = args;
|
|
4353
|
+
const warnings = [];
|
|
4354
|
+
const blockBottomMax = blocks.length > 0 ? Math.max(...blocks.map((b) => b.y + b.height)) : 0;
|
|
4355
|
+
const contentBottom = hasFeedback ? Math.max(blockBottomMax, feedbackBottom) : blockBottomMax;
|
|
4356
|
+
if (height < contentBottom) {
|
|
4357
|
+
warnings.push(
|
|
4358
|
+
`layout: computed height (${height.toFixed(1)}) is below content bottom (${contentBottom.toFixed(1)}); legend/notes may overlap block content`
|
|
4359
|
+
);
|
|
4360
|
+
}
|
|
4361
|
+
return warnings;
|
|
4362
|
+
}
|
|
4344
4363
|
|
|
4345
4364
|
// src/renderer.ts
|
|
4346
4365
|
function genId() {
|
|
@@ -4580,7 +4599,7 @@ function buildAnnotations(theme, connections, layoutHeight) {
|
|
|
4580
4599
|
});
|
|
4581
4600
|
return parts.join("");
|
|
4582
4601
|
}
|
|
4583
|
-
function buildLegend(theme, layoutResult) {
|
|
4602
|
+
function buildLegend(theme, layoutResult, diagramBottom) {
|
|
4584
4603
|
const order = ["audio", "cv", "pitch", "gate", "trigger", "clock"];
|
|
4585
4604
|
const used = order.filter((t) => (layoutResult.signalTypeStats[t] ?? 0) > 0);
|
|
4586
4605
|
if (used.length === 0) return "";
|
|
@@ -4589,7 +4608,7 @@ function buildLegend(theme, layoutResult) {
|
|
|
4589
4608
|
const itemWidth = 70;
|
|
4590
4609
|
const totalWidth = used.length * itemWidth;
|
|
4591
4610
|
const legendStartX = layoutResult.width - totalWidth;
|
|
4592
|
-
const y =
|
|
4611
|
+
const y = diagramBottom - 20;
|
|
4593
4612
|
for (let i = 0; i < used.length; i++) {
|
|
4594
4613
|
const sig = used[i];
|
|
4595
4614
|
const color = theme.cable.colors[sig].stroke;
|
|
@@ -4621,6 +4640,14 @@ function renderSvg(layoutResult, theme) {
|
|
|
4621
4640
|
`<pattern id="${idPrefix}-dots" width="${spacing}" height="${spacing}" patternUnits="userSpaceOnUse"><circle cx="${spacing / 2}" cy="${spacing / 2}" r="${theme.grid.dotRadius}" fill="${theme.grid.dotColor}" opacity="${theme.grid.opacity}"/></pattern>`
|
|
4622
4641
|
);
|
|
4623
4642
|
}
|
|
4643
|
+
const labelPadX = 130;
|
|
4644
|
+
const vbWidth = width + labelPadX * 2;
|
|
4645
|
+
const topPad = 40;
|
|
4646
|
+
const noteCount = layoutResult.connections.filter((c) => c.annotation).length;
|
|
4647
|
+
const notesHeight = noteCount > 0 ? noteCount * 16 + 10 : 0;
|
|
4648
|
+
const bottomPad = Math.max(40, notesHeight + 10);
|
|
4649
|
+
const vbHeight = height + topPad + bottomPad;
|
|
4650
|
+
const diagramBottom = height + bottomPad;
|
|
4624
4651
|
const layers = [
|
|
4625
4652
|
`<g class="pf-layer-bg">${buildBackground(theme, idPrefix, width, height)}</g>`,
|
|
4626
4653
|
`<g class="pf-layer-cables">${buildCables(theme, layoutResult.connections)}</g>`,
|
|
@@ -4628,17 +4655,10 @@ function renderSvg(layoutResult, theme) {
|
|
|
4628
4655
|
`<g class="pf-layer-params">${buildParams(layoutResult.blocks, theme)}</g>`,
|
|
4629
4656
|
`<g class="pf-layer-jacks">${buildJacks(theme, idPrefix, layoutResult.blocks)}</g>`,
|
|
4630
4657
|
`<g class="pf-layer-labels">${buildLabels(theme, layoutResult.blocks, layoutResult.connections)}</g>`,
|
|
4631
|
-
`<g class="pf-layer-annotations">${buildAnnotations(theme, layoutResult.connections,
|
|
4632
|
-
`<g class="pf-layer-legend">${buildLegend(theme, layoutResult)}</g>`
|
|
4658
|
+
`<g class="pf-layer-annotations">${buildAnnotations(theme, layoutResult.connections, diagramBottom)}</g>`,
|
|
4659
|
+
`<g class="pf-layer-legend">${buildLegend(theme, layoutResult, diagramBottom)}</g>`
|
|
4633
4660
|
].join("");
|
|
4634
4661
|
const style = `<style>@media print { .pf-panel, .pf-jack { filter: none; } }</style>`;
|
|
4635
|
-
const labelPadX = 130;
|
|
4636
|
-
const vbWidth = width + labelPadX * 2;
|
|
4637
|
-
const topPad = 40;
|
|
4638
|
-
const noteCount = layoutResult.connections.filter((c) => c.annotation).length;
|
|
4639
|
-
const notesHeight = noteCount > 0 ? noteCount * 16 + 10 : 0;
|
|
4640
|
-
const bottomPad = Math.max(40, notesHeight + 10);
|
|
4641
|
-
const vbHeight = height + topPad + bottomPad;
|
|
4642
4662
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${-labelPadX} ${-topPad} ${vbWidth} ${vbHeight}" width="100%" data-pf-min-width="${minWidth + labelPadX * 2}" role="img" aria-labelledby="${idPrefix}-title ${idPrefix}-desc"><title id="${idPrefix}-title">Patch diagram</title><desc id="${idPrefix}-desc">${desc}</desc>` + style + `<defs>${defsParts.join("")}</defs>` + layers + `</svg>`;
|
|
4643
4663
|
return svg;
|
|
4644
4664
|
}
|