@dewtech/dare-cli 2.14.0 → 2.16.0
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/README.md +561 -561
- package/dist/commands/dag.d.ts.map +1 -1
- package/dist/commands/dag.js +27 -9
- package/dist/commands/dag.js.map +1 -1
- package/dist/utils/excalidraw-renderer.d.ts +79 -0
- package/dist/utils/excalidraw-renderer.d.ts.map +1 -0
- package/dist/utils/excalidraw-renderer.js +188 -0
- package/dist/utils/excalidraw-renderer.js.map +1 -0
- package/dist/utils/excalidraw-renderer.test.d.ts +2 -0
- package/dist/utils/excalidraw-renderer.test.d.ts.map +1 -0
- package/dist/utils/excalidraw-renderer.test.js +135 -0
- package/dist/utils/excalidraw-renderer.test.js.map +1 -0
- package/dist/utils/project-generator.js +12 -2
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/stack-bootstrap.js +18 -1
- package/dist/utils/stack-bootstrap.js.map +1 -1
- package/package.json +1 -1
- package/templates/DARE-dag-example.yaml +280 -0
- package/templates/ide/claude/.claude/commands/dare-dag-viz.md +197 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../../src/commands/dag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAEL,KAAK,GAAG,EAGT,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"dag.d.ts","sourceRoot":"","sources":["../../src/commands/dag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAEL,KAAK,GAAG,EAGT,MAAM,0BAA0B,CAAC;AAUlC;;;;GAIG;AACH,eAAO,MAAM,UAAU,SAEtB,CAAC;AA6FF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CA6CjD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAyC7C"}
|
package/dist/commands/dag.js
CHANGED
|
@@ -5,6 +5,7 @@ import path from 'path';
|
|
|
5
5
|
import { convertYamlToDag } from '../utils/dag-converter.js';
|
|
6
6
|
import { computeRanks, } from '../dag-runner/run_dag.js';
|
|
7
7
|
import { DEFAULT_STATE_PATH, loadAndApplyState, } from '../dag-runner/state-store.js';
|
|
8
|
+
import { renderDagExcalidraw, serializeExcalidraw, } from '../utils/excalidraw-renderer.js';
|
|
8
9
|
/**
|
|
9
10
|
* `dare dag` — inspect and visualize the static task DAG declared in
|
|
10
11
|
* `dare-dag.yaml`. Distinct from `dare graph`, which inspects the populated
|
|
@@ -13,10 +14,10 @@ import { DEFAULT_STATE_PATH, loadAndApplyState, } from '../dag-runner/state-stor
|
|
|
13
14
|
export const dagCommand = new Command('dag').description('Inspect and visualize the static task DAG (dare-dag.yaml)');
|
|
14
15
|
dagCommand
|
|
15
16
|
.command('viz')
|
|
16
|
-
.description('Render dare-dag.yaml as a Mermaid or
|
|
17
|
+
.description('Render dare-dag.yaml as a Mermaid, DOT, or Excalidraw diagram with status colors')
|
|
17
18
|
.option('--dag <file>', 'Path to dare-dag.yaml', 'DARE/dare-dag.yaml')
|
|
18
|
-
.option('-f, --format <fmt>', 'Output format: mermaid | dot', 'mermaid')
|
|
19
|
-
.option('-o, --output <file>', 'Write to file (defaults to stdout)')
|
|
19
|
+
.option('-f, --format <fmt>', 'Output format: mermaid | dot | excalidraw', 'mermaid')
|
|
20
|
+
.option('-o, --output <file>', 'Write to file (defaults to stdout or DARE/dag-graph.{fmt} for excalidraw)')
|
|
20
21
|
.action(async (options) => {
|
|
21
22
|
const cwd = process.cwd();
|
|
22
23
|
const dagPath = path.resolve(cwd, options.dag);
|
|
@@ -26,18 +27,35 @@ dagCommand
|
|
|
26
27
|
process.exit(1);
|
|
27
28
|
}
|
|
28
29
|
const format = options.format.toLowerCase();
|
|
29
|
-
if (format !== 'mermaid' && format !== 'dot') {
|
|
30
|
-
console.error(chalk.red(`Unsupported format "${options.format}". Use: mermaid | dot.`));
|
|
30
|
+
if (format !== 'mermaid' && format !== 'dot' && format !== 'excalidraw') {
|
|
31
|
+
console.error(chalk.red(`Unsupported format "${options.format}". Use: mermaid | dot | excalidraw.`));
|
|
31
32
|
process.exit(1);
|
|
32
33
|
}
|
|
33
34
|
const dag = convertYamlToDag(await fs.readFile(dagPath, 'utf-8'));
|
|
34
35
|
await loadAndApplyState(dag, path.resolve(cwd, DEFAULT_STATE_PATH));
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
let rendered;
|
|
37
|
+
let outputPath = options.output;
|
|
38
|
+
if (format === 'excalidraw') {
|
|
39
|
+
const excalidrawData = renderDagExcalidraw(dag);
|
|
40
|
+
rendered = serializeExcalidraw(excalidrawData);
|
|
41
|
+
// Default output for Excalidraw
|
|
42
|
+
if (!outputPath) {
|
|
43
|
+
outputPath = 'DARE/dag-graph.excalidraw';
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
rendered =
|
|
48
|
+
format === 'mermaid' ? renderDagMermaid(dag) : renderDagDot(dag);
|
|
49
|
+
}
|
|
50
|
+
if (outputPath) {
|
|
51
|
+
const out = path.resolve(cwd, outputPath);
|
|
38
52
|
await fs.ensureDir(path.dirname(out));
|
|
39
53
|
await fs.writeFile(out, rendered);
|
|
40
|
-
|
|
54
|
+
const formatLabel = format === 'excalidraw' ? 'Excalidraw diagram' : `${format} diagram`;
|
|
55
|
+
console.log(chalk.green(`✅ Generated ${formatLabel} with ${dag.tasks.length} task(s) and ${countEdges(dag)} dependency arrow(s) → ${outputPath}`));
|
|
56
|
+
if (format === 'excalidraw') {
|
|
57
|
+
console.log(chalk.cyan(' Open in https://excalidraw.com or via File → Open'));
|
|
58
|
+
}
|
|
41
59
|
}
|
|
42
60
|
else {
|
|
43
61
|
console.log(rendered);
|
package/dist/commands/dag.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dag.js","sourceRoot":"","sources":["../../src/commands/dag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,YAAY,GAIb,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"dag.js","sourceRoot":"","sources":["../../src/commands/dag.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excalidraw Renderer for DARE DAG Visualization
|
|
3
|
+
*
|
|
4
|
+
* Converts a DARE DAG (task graph) to an Excalidraw JSON file.
|
|
5
|
+
* - Tasks are rendered as colored rectangles based on complexity
|
|
6
|
+
* - Dependencies are rendered as arrows
|
|
7
|
+
* - Tasks are grouped by rank in swim lanes
|
|
8
|
+
*
|
|
9
|
+
* Design tokens: /docs/DESIGN-TOKENS-EXCALIDRAW.md
|
|
10
|
+
* License: AGPL v3 (part of DARE CLI)
|
|
11
|
+
*/
|
|
12
|
+
import { Dag } from '../dag-runner/run_dag.js';
|
|
13
|
+
/**
|
|
14
|
+
* Excalidraw element types
|
|
15
|
+
*/
|
|
16
|
+
interface ExcalidrawElement {
|
|
17
|
+
id: string;
|
|
18
|
+
type: string;
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
angle?: number;
|
|
24
|
+
strokeColor?: string;
|
|
25
|
+
backgroundColor?: string;
|
|
26
|
+
fillStyle?: string;
|
|
27
|
+
strokeWidth?: number;
|
|
28
|
+
strokeStyle?: string;
|
|
29
|
+
roughness?: number;
|
|
30
|
+
opacity?: number;
|
|
31
|
+
roundness?: {
|
|
32
|
+
type: number;
|
|
33
|
+
value: number;
|
|
34
|
+
};
|
|
35
|
+
text?: string;
|
|
36
|
+
fontSize?: number;
|
|
37
|
+
fontFamily?: number;
|
|
38
|
+
textAlign?: string;
|
|
39
|
+
verticalAlign?: string;
|
|
40
|
+
startBinding?: {
|
|
41
|
+
elementId: string;
|
|
42
|
+
};
|
|
43
|
+
endBinding?: {
|
|
44
|
+
elementId: string;
|
|
45
|
+
};
|
|
46
|
+
startArrowType?: string;
|
|
47
|
+
endArrowType?: string;
|
|
48
|
+
boundElements?: Array<{
|
|
49
|
+
id: string;
|
|
50
|
+
type: string;
|
|
51
|
+
}>;
|
|
52
|
+
updated?: number;
|
|
53
|
+
link?: null;
|
|
54
|
+
locked?: boolean;
|
|
55
|
+
seed?: number;
|
|
56
|
+
versionNonce?: number;
|
|
57
|
+
}
|
|
58
|
+
interface ExcalidrawData {
|
|
59
|
+
type: string;
|
|
60
|
+
version: number;
|
|
61
|
+
source: string;
|
|
62
|
+
elements: ExcalidrawElement[];
|
|
63
|
+
appState: {
|
|
64
|
+
gridMode: string;
|
|
65
|
+
gridSize: number;
|
|
66
|
+
viewBackgroundColor?: string;
|
|
67
|
+
};
|
|
68
|
+
files: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Render DAG to Excalidraw JSON
|
|
72
|
+
*/
|
|
73
|
+
export declare function renderDagExcalidraw(dag: Dag): ExcalidrawData;
|
|
74
|
+
/**
|
|
75
|
+
* Serialize Excalidraw data to JSON string
|
|
76
|
+
*/
|
|
77
|
+
export declare function serializeExcalidraw(data: ExcalidrawData): string;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=excalidraw-renderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excalidraw-renderer.d.ts","sourceRoot":"","sources":["../../src/utils/excalidraw-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,GAAG,EAAqC,MAAM,0BAA0B,CAAC;AAElF;;GAEG;AACH,UAAU,iBAAiB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,UAAU,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAyID;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,cAAc,CAyD5D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excalidraw Renderer for DARE DAG Visualization
|
|
3
|
+
*
|
|
4
|
+
* Converts a DARE DAG (task graph) to an Excalidraw JSON file.
|
|
5
|
+
* - Tasks are rendered as colored rectangles based on complexity
|
|
6
|
+
* - Dependencies are rendered as arrows
|
|
7
|
+
* - Tasks are grouped by rank in swim lanes
|
|
8
|
+
*
|
|
9
|
+
* Design tokens: /docs/DESIGN-TOKENS-EXCALIDRAW.md
|
|
10
|
+
* License: AGPL v3 (part of DARE CLI)
|
|
11
|
+
*/
|
|
12
|
+
import { computeRanks } from '../dag-runner/run_dag.js';
|
|
13
|
+
/**
|
|
14
|
+
* Color palette based on DARE Design Tokens
|
|
15
|
+
*/
|
|
16
|
+
const COLOR_BY_COMPLEXITY = {
|
|
17
|
+
LOW: {
|
|
18
|
+
bg: '#e3f2fd', // Azure blue
|
|
19
|
+
stroke: '#1976d2', // Dark blue
|
|
20
|
+
},
|
|
21
|
+
MED: {
|
|
22
|
+
bg: '#fff3e0', // Orange
|
|
23
|
+
stroke: '#e65100', // Dark orange
|
|
24
|
+
},
|
|
25
|
+
HIGH: {
|
|
26
|
+
bg: '#fce4ec', // Pink/Magenta
|
|
27
|
+
stroke: '#c2185b', // Dark pink
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const COLOR_BY_STATUS = {
|
|
31
|
+
PENDING: {
|
|
32
|
+
bg: '#f5f5f5', // Light gray
|
|
33
|
+
stroke: '#999999', // Medium gray
|
|
34
|
+
},
|
|
35
|
+
RUNNING: {
|
|
36
|
+
bg: '#e3f2fd', // Azure blue
|
|
37
|
+
stroke: '#1976d2', // Dark blue
|
|
38
|
+
dashed: true,
|
|
39
|
+
},
|
|
40
|
+
DONE: {
|
|
41
|
+
bg: '#e8f5e9', // Light green
|
|
42
|
+
stroke: '#388e3c', // Dark green
|
|
43
|
+
},
|
|
44
|
+
FAILED: {
|
|
45
|
+
bg: '#ffebee', // Light red
|
|
46
|
+
stroke: '#d32f2f', // Dark red
|
|
47
|
+
},
|
|
48
|
+
SKIPPED: {
|
|
49
|
+
bg: '#e5e7eb', // Gray
|
|
50
|
+
stroke: '#6b7280', // Dark gray
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Generate unique ID for Excalidraw elements
|
|
55
|
+
*/
|
|
56
|
+
function generateId() {
|
|
57
|
+
return Math.random().toString(36).substr(2, 9);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get status color, preferring status over complexity
|
|
61
|
+
*/
|
|
62
|
+
function getTaskColors(task) {
|
|
63
|
+
const status = task.status ?? 'PENDING';
|
|
64
|
+
// Status takes precedence, but if PENDING, use complexity for better visualization
|
|
65
|
+
if (status === 'PENDING' && task.complexity) {
|
|
66
|
+
return COLOR_BY_COMPLEXITY[task.complexity] || COLOR_BY_COMPLEXITY.MED;
|
|
67
|
+
}
|
|
68
|
+
return COLOR_BY_STATUS[status] || COLOR_BY_STATUS.PENDING;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create rectangle element for a task
|
|
72
|
+
*/
|
|
73
|
+
function createTaskElement(task, x, y) {
|
|
74
|
+
const colors = getTaskColors(task);
|
|
75
|
+
const complexity = task.complexity || 'MED';
|
|
76
|
+
const text = `${task.id}\n${task.title}\n[${complexity}]`;
|
|
77
|
+
return {
|
|
78
|
+
id: task.id,
|
|
79
|
+
type: 'rectangle',
|
|
80
|
+
x,
|
|
81
|
+
y,
|
|
82
|
+
width: 120,
|
|
83
|
+
height: 60,
|
|
84
|
+
angle: 0,
|
|
85
|
+
strokeColor: colors.stroke,
|
|
86
|
+
backgroundColor: colors.bg,
|
|
87
|
+
fillStyle: 'hachure',
|
|
88
|
+
strokeWidth: 2,
|
|
89
|
+
strokeStyle: colors.dashed ? 'dashed' : 'solid',
|
|
90
|
+
roughness: 0,
|
|
91
|
+
opacity: 100,
|
|
92
|
+
roundness: { type: 2, value: 6 },
|
|
93
|
+
text,
|
|
94
|
+
fontSize: 12,
|
|
95
|
+
fontFamily: 1, // Virgil (default Excalidraw font)
|
|
96
|
+
textAlign: 'center',
|
|
97
|
+
verticalAlign: 'middle',
|
|
98
|
+
boundElements: [],
|
|
99
|
+
updated: Date.now(),
|
|
100
|
+
link: null,
|
|
101
|
+
locked: false,
|
|
102
|
+
seed: Math.floor(Math.random() * 2147483647),
|
|
103
|
+
versionNonce: Math.floor(Math.random() * 2147483647),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create arrow element for a dependency
|
|
108
|
+
*/
|
|
109
|
+
function createArrowElement(fromTaskId, toTaskId) {
|
|
110
|
+
return {
|
|
111
|
+
id: generateId(),
|
|
112
|
+
type: 'arrow',
|
|
113
|
+
x: 0, // Will be computed from start/end binding
|
|
114
|
+
y: 0,
|
|
115
|
+
startBinding: { elementId: fromTaskId },
|
|
116
|
+
endBinding: { elementId: toTaskId },
|
|
117
|
+
startArrowType: 'dot',
|
|
118
|
+
endArrowType: 'arrow',
|
|
119
|
+
strokeColor: '#999999',
|
|
120
|
+
strokeWidth: 2,
|
|
121
|
+
strokeStyle: 'solid',
|
|
122
|
+
roughness: 0,
|
|
123
|
+
opacity: 100,
|
|
124
|
+
updated: Date.now(),
|
|
125
|
+
link: null,
|
|
126
|
+
locked: false,
|
|
127
|
+
seed: Math.floor(Math.random() * 2147483647),
|
|
128
|
+
versionNonce: Math.floor(Math.random() * 2147483647),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Render DAG to Excalidraw JSON
|
|
133
|
+
*/
|
|
134
|
+
export function renderDagExcalidraw(dag) {
|
|
135
|
+
const elements = [];
|
|
136
|
+
const ranks = computeRanks(dag.tasks);
|
|
137
|
+
// Group tasks by rank
|
|
138
|
+
const byRank = new Map();
|
|
139
|
+
for (const task of dag.tasks) {
|
|
140
|
+
const r = ranks.get(task.id) ?? 1;
|
|
141
|
+
if (!byRank.has(r))
|
|
142
|
+
byRank.set(r, []);
|
|
143
|
+
byRank.get(r).push(task);
|
|
144
|
+
}
|
|
145
|
+
// Position elements
|
|
146
|
+
const taskPositions = new Map();
|
|
147
|
+
const sortedRanks = [...byRank.keys()].sort((a, b) => a - b);
|
|
148
|
+
for (const rank of sortedRanks) {
|
|
149
|
+
const tasksInRank = byRank.get(rank);
|
|
150
|
+
for (let i = 0; i < tasksInRank.length; i++) {
|
|
151
|
+
const task = tasksInRank[i];
|
|
152
|
+
// Position: X based on task index in rank, Y based on rank
|
|
153
|
+
const x = 20 + i * 140;
|
|
154
|
+
const y = 20 + rank * 160;
|
|
155
|
+
taskPositions.set(task.id, { x, y });
|
|
156
|
+
// Create rectangle for this task
|
|
157
|
+
const taskElement = createTaskElement(task, x, y);
|
|
158
|
+
elements.push(taskElement);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Create arrows for dependencies
|
|
162
|
+
for (const task of dag.tasks) {
|
|
163
|
+
for (const depId of task.depends_on) {
|
|
164
|
+
const arrow = createArrowElement(depId, task.id);
|
|
165
|
+
elements.push(arrow);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const data = {
|
|
169
|
+
type: 'excalidraw',
|
|
170
|
+
version: 2,
|
|
171
|
+
source: 'dare-dag-viz',
|
|
172
|
+
elements,
|
|
173
|
+
appState: {
|
|
174
|
+
gridMode: 'grid',
|
|
175
|
+
gridSize: 20,
|
|
176
|
+
viewBackgroundColor: '#ffffff',
|
|
177
|
+
},
|
|
178
|
+
files: {},
|
|
179
|
+
};
|
|
180
|
+
return data;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Serialize Excalidraw data to JSON string
|
|
184
|
+
*/
|
|
185
|
+
export function serializeExcalidraw(data) {
|
|
186
|
+
return JSON.stringify(data, null, 2);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=excalidraw-renderer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excalidraw-renderer.js","sourceRoot":"","sources":["../../src/utils/excalidraw-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excalidraw-renderer.test.d.ts","sourceRoot":"","sources":["../../src/utils/excalidraw-renderer.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { renderDagExcalidraw, serializeExcalidraw } from './excalidraw-renderer.js';
|
|
3
|
+
describe('ExcalidrawRenderer', () => {
|
|
4
|
+
const mockDag = {
|
|
5
|
+
title: 'Test DAG',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
models: {},
|
|
8
|
+
tasks: [
|
|
9
|
+
{
|
|
10
|
+
id: 'task-001',
|
|
11
|
+
title: 'Setup',
|
|
12
|
+
complexity: 'LOW',
|
|
13
|
+
depends_on: [],
|
|
14
|
+
status: 'DONE',
|
|
15
|
+
subtask_prompt: 'Setup task',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'task-002',
|
|
19
|
+
title: 'Build',
|
|
20
|
+
complexity: 'MED',
|
|
21
|
+
depends_on: ['task-001'],
|
|
22
|
+
status: 'RUNNING',
|
|
23
|
+
subtask_prompt: 'Build task',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'task-003',
|
|
27
|
+
title: 'Test',
|
|
28
|
+
complexity: 'HIGH',
|
|
29
|
+
depends_on: ['task-002'],
|
|
30
|
+
status: 'PENDING',
|
|
31
|
+
subtask_prompt: 'Test task',
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
it('renders DAG to Excalidraw JSON', () => {
|
|
36
|
+
const data = renderDagExcalidraw(mockDag);
|
|
37
|
+
expect(data.type).toBe('excalidraw');
|
|
38
|
+
expect(data.version).toBe(2);
|
|
39
|
+
expect(data.source).toBe('dare-dag-viz');
|
|
40
|
+
expect(data.elements).toHaveLength(5); // 3 tasks + 2 arrows
|
|
41
|
+
});
|
|
42
|
+
it('creates rectangle elements for tasks', () => {
|
|
43
|
+
const data = renderDagExcalidraw(mockDag);
|
|
44
|
+
const taskElements = data.elements.filter((e) => e.type === 'rectangle');
|
|
45
|
+
expect(taskElements).toHaveLength(3);
|
|
46
|
+
expect(taskElements[0].id).toBe('task-001');
|
|
47
|
+
expect(taskElements[0].width).toBe(120);
|
|
48
|
+
expect(taskElements[0].height).toBe(60);
|
|
49
|
+
});
|
|
50
|
+
it('applies correct colors by complexity when PENDING', () => {
|
|
51
|
+
const data = renderDagExcalidraw(mockDag);
|
|
52
|
+
const task003 = data.elements.find((e) => e.id === 'task-003');
|
|
53
|
+
// PENDING + HIGH complexity should use HIGH complexity colors
|
|
54
|
+
expect(task003?.backgroundColor).toBe('#fce4ec'); // HIGH pink
|
|
55
|
+
expect(task003?.strokeColor).toBe('#c2185b');
|
|
56
|
+
});
|
|
57
|
+
it('applies correct colors by status when not PENDING', () => {
|
|
58
|
+
const data = renderDagExcalidraw(mockDag);
|
|
59
|
+
const task001 = data.elements.find((e) => e.id === 'task-001');
|
|
60
|
+
expect(task001?.backgroundColor).toBe('#e8f5e9'); // DONE green
|
|
61
|
+
expect(task001?.strokeColor).toBe('#388e3c');
|
|
62
|
+
const task002 = data.elements.find((e) => e.id === 'task-002');
|
|
63
|
+
expect(task002?.backgroundColor).toBe('#e3f2fd'); // RUNNING blue
|
|
64
|
+
expect(task002?.strokeColor).toBe('#1976d2');
|
|
65
|
+
expect(task002?.strokeStyle).toBe('dashed'); // RUNNING is dashed
|
|
66
|
+
});
|
|
67
|
+
it('creates arrow elements for dependencies', () => {
|
|
68
|
+
const data = renderDagExcalidraw(mockDag);
|
|
69
|
+
const arrows = data.elements.filter((e) => e.type === 'arrow');
|
|
70
|
+
expect(arrows).toHaveLength(2);
|
|
71
|
+
expect(arrows[0].startBinding?.elementId).toBe('task-001');
|
|
72
|
+
expect(arrows[0].endBinding?.elementId).toBe('task-002');
|
|
73
|
+
expect(arrows[1].startBinding?.elementId).toBe('task-002');
|
|
74
|
+
expect(arrows[1].endBinding?.elementId).toBe('task-003');
|
|
75
|
+
});
|
|
76
|
+
it('positions tasks correctly by rank', () => {
|
|
77
|
+
const data = renderDagExcalidraw(mockDag);
|
|
78
|
+
const task001 = data.elements.find((e) => e.id === 'task-001');
|
|
79
|
+
const task002 = data.elements.find((e) => e.id === 'task-002');
|
|
80
|
+
const task003 = data.elements.find((e) => e.id === 'task-003');
|
|
81
|
+
// Rank 0: y = 20 + 0 * 160 = 20
|
|
82
|
+
expect(task001?.y).toBe(20);
|
|
83
|
+
// Rank 1: y = 20 + 1 * 160 = 180
|
|
84
|
+
expect(task002?.y).toBe(180);
|
|
85
|
+
// Rank 2: y = 20 + 2 * 160 = 340
|
|
86
|
+
expect(task003?.y).toBe(340);
|
|
87
|
+
});
|
|
88
|
+
it('serializes to valid JSON', () => {
|
|
89
|
+
const data = renderDagExcalidraw(mockDag);
|
|
90
|
+
const json = serializeExcalidraw(data);
|
|
91
|
+
expect(() => JSON.parse(json)).not.toThrow();
|
|
92
|
+
const parsed = JSON.parse(json);
|
|
93
|
+
expect(parsed.type).toBe('excalidraw');
|
|
94
|
+
expect(parsed.elements).toHaveLength(5);
|
|
95
|
+
});
|
|
96
|
+
it('includes task title and complexity in text', () => {
|
|
97
|
+
const data = renderDagExcalidraw(mockDag);
|
|
98
|
+
const task001 = data.elements.find((e) => e.id === 'task-001');
|
|
99
|
+
expect(task001?.text).toContain('task-001');
|
|
100
|
+
expect(task001?.text).toContain('Setup');
|
|
101
|
+
expect(task001?.text).toContain('[LOW]');
|
|
102
|
+
});
|
|
103
|
+
it('handles parallel tasks (same rank)', () => {
|
|
104
|
+
const parallelDag = {
|
|
105
|
+
title: 'Parallel DAG',
|
|
106
|
+
version: '1.0.0',
|
|
107
|
+
models: {},
|
|
108
|
+
tasks: [
|
|
109
|
+
{
|
|
110
|
+
id: 'task-a',
|
|
111
|
+
title: 'Task A',
|
|
112
|
+
complexity: 'LOW',
|
|
113
|
+
depends_on: [],
|
|
114
|
+
status: 'PENDING',
|
|
115
|
+
subtask_prompt: '',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'task-b',
|
|
119
|
+
title: 'Task B',
|
|
120
|
+
complexity: 'LOW',
|
|
121
|
+
depends_on: [],
|
|
122
|
+
status: 'PENDING',
|
|
123
|
+
subtask_prompt: '',
|
|
124
|
+
},
|
|
125
|
+
],
|
|
126
|
+
};
|
|
127
|
+
const data = renderDagExcalidraw(parallelDag);
|
|
128
|
+
const taskA = data.elements.find((e) => e.id === 'task-a');
|
|
129
|
+
const taskB = data.elements.find((e) => e.id === 'task-b');
|
|
130
|
+
// Both should be at same Y (rank 1), different X
|
|
131
|
+
expect(taskA?.y).toBe(taskB?.y);
|
|
132
|
+
expect(taskA?.x).not.toBe(taskB?.x);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
//# sourceMappingURL=excalidraw-renderer.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"excalidraw-renderer.test.js","sourceRoot":"","sources":["../../src/utils/excalidraw-renderer.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -708,6 +708,16 @@ async function runStackBootstrap(config) {
|
|
|
708
708
|
backend === 'rust-axum' &&
|
|
709
709
|
(frontend === 'rust-leptos' || frontend === 'rust-leptos-csr');
|
|
710
710
|
const layout = rustWorkspaceLayout ?? 'single';
|
|
711
|
+
// For Rust monorepo the crate name must differ per member to avoid duplicate
|
|
712
|
+
// package name errors in the workspace. Single layout → "server"/"web";
|
|
713
|
+
// multi layout → "{prefix}-server" / "{prefix}-web".
|
|
714
|
+
const rustCrateName = (type) => {
|
|
715
|
+
if (!isRustMonorepo)
|
|
716
|
+
return name;
|
|
717
|
+
const prefix = cratePrefix?.trim() ||
|
|
718
|
+
name.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '') || 'app';
|
|
719
|
+
return layout === 'single' ? type : `${prefix}-${type}`;
|
|
720
|
+
};
|
|
711
721
|
// Backend / monorepo backend
|
|
712
722
|
if ((structure === 'backend' || structure === 'monorepo') && backend) {
|
|
713
723
|
if (!BACKEND_STACKS.has(backend)) {
|
|
@@ -722,7 +732,7 @@ async function runStackBootstrap(config) {
|
|
|
722
732
|
await bootstrapBackend({
|
|
723
733
|
stack: backend,
|
|
724
734
|
dir: backendDir,
|
|
725
|
-
projectName:
|
|
735
|
+
projectName: rustCrateName('server'),
|
|
726
736
|
toolchain,
|
|
727
737
|
isMonorepo: structure === 'monorepo',
|
|
728
738
|
});
|
|
@@ -741,7 +751,7 @@ async function runStackBootstrap(config) {
|
|
|
741
751
|
await bootstrapFrontend({
|
|
742
752
|
stack: frontend,
|
|
743
753
|
dir: frontendDir,
|
|
744
|
-
projectName:
|
|
754
|
+
projectName: rustCrateName('web'),
|
|
745
755
|
toolchain,
|
|
746
756
|
isMonorepo: structure === 'monorepo',
|
|
747
757
|
});
|