@cravern/bpmn-flow-designer 1.0.0 → 1.0.2
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 +151 -13
- package/dist/index.css +1 -0
- package/dist/index.js +12037 -0
- package/dist/index.umd.js +242 -0
- package/package.json +25 -5
- package/App.tsx +0 -441
- package/index.html +0 -60
- package/index.tsx +0 -16
- package/metadata.json +0 -5
- package/tsconfig.json +0 -21
- package/vite.config.ts +0 -23
package/package.json
CHANGED
|
@@ -1,23 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cravern/bpmn-flow-designer",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"description": "A BPMN flow designer component built with React and Tailwind CSS",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/src/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/src/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./style.css": "./dist/style.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
6
19
|
"scripts": {
|
|
7
20
|
"dev": "vite",
|
|
8
|
-
"build": "vite build",
|
|
21
|
+
"build": "tsc && vite build",
|
|
9
22
|
"preview": "vite preview"
|
|
10
23
|
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
26
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
27
|
+
},
|
|
11
28
|
"dependencies": {
|
|
12
|
-
"react": "^19.2.3",
|
|
13
|
-
"react-dom": "^19.2.3",
|
|
14
29
|
"@google/genai": "1.3.0",
|
|
15
30
|
"lucide-react": "0.460.0"
|
|
16
31
|
},
|
|
17
32
|
"devDependencies": {
|
|
18
33
|
"@types/node": "^22.14.0",
|
|
19
34
|
"@vitejs/plugin-react": "^5.0.0",
|
|
35
|
+
"autoprefixer": "^10.4.16",
|
|
36
|
+
"postcss": "^8.4.31",
|
|
37
|
+
"tailwindcss": "^3.4.1",
|
|
20
38
|
"typescript": "~5.8.2",
|
|
21
|
-
"vite": "^6.2.0"
|
|
39
|
+
"vite": "^6.2.0",
|
|
40
|
+
"react": "^19.2.3",
|
|
41
|
+
"react-dom": "^19.2.3"
|
|
22
42
|
}
|
|
23
43
|
}
|
package/App.tsx
DELETED
|
@@ -1,441 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import React, { useState, useEffect, useRef } from 'react';
|
|
3
|
-
import {
|
|
4
|
-
Play,
|
|
5
|
-
Save,
|
|
6
|
-
FileCode,
|
|
7
|
-
Download,
|
|
8
|
-
Trash2,
|
|
9
|
-
Sparkles,
|
|
10
|
-
ChevronRight,
|
|
11
|
-
ChevronLeft,
|
|
12
|
-
Circle,
|
|
13
|
-
MessageCircle,
|
|
14
|
-
XCircle,
|
|
15
|
-
Layers,
|
|
16
|
-
Square,
|
|
17
|
-
ArrowLeft,
|
|
18
|
-
Briefcase,
|
|
19
|
-
Target,
|
|
20
|
-
RefreshCw,
|
|
21
|
-
FileText,
|
|
22
|
-
Plus,
|
|
23
|
-
GitCommit
|
|
24
|
-
} from 'lucide-react';
|
|
25
|
-
import { GoogleGenAI } from "@google/genai";
|
|
26
|
-
|
|
27
|
-
declare const BpmnJS: any;
|
|
28
|
-
|
|
29
|
-
// Updated Mock Data Structure: Practices are now children of Cycles
|
|
30
|
-
const BUSINESS_PROCESSES = [
|
|
31
|
-
{
|
|
32
|
-
id: 'bp1',
|
|
33
|
-
name: 'Order to Cash',
|
|
34
|
-
capabilities: [
|
|
35
|
-
{
|
|
36
|
-
id: 'cap1',
|
|
37
|
-
name: 'Order Management',
|
|
38
|
-
cycles: [
|
|
39
|
-
{
|
|
40
|
-
id: 'cyc1',
|
|
41
|
-
name: 'Standard Order Processing',
|
|
42
|
-
type: 'cycle',
|
|
43
|
-
practices: [
|
|
44
|
-
{ id: 'pra1', name: 'Validate Customer Credit', type: 'practice' },
|
|
45
|
-
{ id: 'pra3', name: 'Check Inventory Availability', type: 'practice' }
|
|
46
|
-
]
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
id: 'cyc2',
|
|
50
|
-
name: 'Returns Management',
|
|
51
|
-
type: 'cycle',
|
|
52
|
-
practices: [
|
|
53
|
-
{ id: 'pra4', name: 'Issue Return Material Auth', type: 'practice' }
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
id: 'cap2',
|
|
60
|
-
name: 'Invoicing & Collections',
|
|
61
|
-
cycles: [
|
|
62
|
-
{
|
|
63
|
-
id: 'cyc3',
|
|
64
|
-
name: 'Automated Billing Cycle',
|
|
65
|
-
type: 'cycle',
|
|
66
|
-
practices: []
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
}
|
|
70
|
-
]
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
id: 'bp2',
|
|
74
|
-
name: 'Hire to Retire',
|
|
75
|
-
capabilities: [
|
|
76
|
-
{
|
|
77
|
-
id: 'cap3',
|
|
78
|
-
name: 'Talent Acquisition',
|
|
79
|
-
cycles: [
|
|
80
|
-
{
|
|
81
|
-
id: 'cyc4',
|
|
82
|
-
name: 'Recruitment Funnel',
|
|
83
|
-
type: 'cycle',
|
|
84
|
-
practices: [
|
|
85
|
-
{ id: 'pra2', name: 'Interview Evaluation', type: 'practice' },
|
|
86
|
-
{ id: 'pra5', name: 'Background Verification', type: 'practice' }
|
|
87
|
-
]
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
}
|
|
91
|
-
]
|
|
92
|
-
}
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
const INITIAL_DIAGRAM = `<?xml version="1.0" encoding="UTF-8"?>
|
|
96
|
-
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn">
|
|
97
|
-
<bpmn:process id="Process_1" isExecutable="false">
|
|
98
|
-
<bpmn:startEvent id="StartEvent_1" name="Start" />
|
|
99
|
-
</bpmn:process>
|
|
100
|
-
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
|
|
101
|
-
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">
|
|
102
|
-
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
|
|
103
|
-
<dc:Bounds x="173" y="102" width="36" height="36" />
|
|
104
|
-
</bpmndi:BPMNShape>
|
|
105
|
-
</bpmndi:BPMNPlane>
|
|
106
|
-
</bpmndi:BPMNDiagram>
|
|
107
|
-
</bpmn:definitions>`;
|
|
108
|
-
|
|
109
|
-
const App: React.FC = () => {
|
|
110
|
-
const [view, setView] = useState<'dashboard' | 'designer'>('dashboard');
|
|
111
|
-
const [activeProcessTab, setActiveProcessTab] = useState(BUSINESS_PROCESSES[0].id);
|
|
112
|
-
const [editingItem, setEditingItem] = useState<{ id: string, name: string, type: string } | null>(null);
|
|
113
|
-
|
|
114
|
-
// Designer State
|
|
115
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
116
|
-
const modelerRef = useRef<any>(null);
|
|
117
|
-
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
|
118
|
-
const [prompt, setPrompt] = useState('');
|
|
119
|
-
const [isGenerating, setIsGenerating] = useState(false);
|
|
120
|
-
const [statusMsg, setStatusMsg] = useState('');
|
|
121
|
-
|
|
122
|
-
// Modeler initialization and cleanup
|
|
123
|
-
useEffect(() => {
|
|
124
|
-
if (view === 'designer' && containerRef.current) {
|
|
125
|
-
// Small timeout to ensure DOM is fully ready
|
|
126
|
-
const timer = setTimeout(() => {
|
|
127
|
-
if (!modelerRef.current && containerRef.current) {
|
|
128
|
-
modelerRef.current = new BpmnJS({ container: containerRef.current });
|
|
129
|
-
modelerRef.current.importXML(INITIAL_DIAGRAM).catch(console.error);
|
|
130
|
-
}
|
|
131
|
-
}, 50);
|
|
132
|
-
return () => clearTimeout(timer);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Cleanup when leaving designer or component unmounts
|
|
136
|
-
return () => {
|
|
137
|
-
if (modelerRef.current) {
|
|
138
|
-
try {
|
|
139
|
-
modelerRef.current.destroy();
|
|
140
|
-
} catch (e) {
|
|
141
|
-
console.warn("Modeler destruction warning", e);
|
|
142
|
-
}
|
|
143
|
-
modelerRef.current = null;
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
}, [view]);
|
|
147
|
-
|
|
148
|
-
const handleOpenDesigner = (item: { id: string, name: string, type: string }) => {
|
|
149
|
-
setEditingItem(item);
|
|
150
|
-
setView('designer');
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const handleBackToDashboard = () => {
|
|
154
|
-
// Navigate back immediately
|
|
155
|
-
setView('dashboard');
|
|
156
|
-
// Clear states
|
|
157
|
-
setEditingItem(null);
|
|
158
|
-
setPrompt('');
|
|
159
|
-
setStatusMsg('');
|
|
160
|
-
setIsGenerating(false);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
const handleDownloadXml = async () => {
|
|
164
|
-
if (!modelerRef.current) return;
|
|
165
|
-
try {
|
|
166
|
-
const { xml } = await modelerRef.current.saveXML({ format: true });
|
|
167
|
-
const blob = new Blob([xml], { type: 'application/xml' });
|
|
168
|
-
const a = document.createElement('a');
|
|
169
|
-
a.href = URL.createObjectURL(blob);
|
|
170
|
-
a.download = `${editingItem?.name.replace(/\s+/g, '_') || 'process'}.bpmn`;
|
|
171
|
-
a.click();
|
|
172
|
-
} catch (err) {
|
|
173
|
-
console.error('Error exporting BPMN', err);
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
const generateWithAi = async () => {
|
|
178
|
-
if (!prompt.trim() || !modelerRef.current) return;
|
|
179
|
-
setIsGenerating(true);
|
|
180
|
-
setStatusMsg('Drafting flow...');
|
|
181
|
-
try {
|
|
182
|
-
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY || '' });
|
|
183
|
-
const response = await ai.models.generateContent({
|
|
184
|
-
model: 'gemini-3-flash-preview',
|
|
185
|
-
contents: `Generate a valid BPMN 2.0 XML for a ${editingItem?.type} named "${editingItem?.name}". Specific requirements: "${prompt}". Return ONLY the raw XML code.`,
|
|
186
|
-
});
|
|
187
|
-
let xml = response.text || '';
|
|
188
|
-
xml = xml.replace(/^```xml\n/, '').replace(/\n```$/, '').trim();
|
|
189
|
-
if (xml.includes('<bpmn:definitions')) {
|
|
190
|
-
await modelerRef.current.importXML(xml);
|
|
191
|
-
setStatusMsg('Import successful!');
|
|
192
|
-
setPrompt('');
|
|
193
|
-
} else {
|
|
194
|
-
setStatusMsg('Generated invalid XML.');
|
|
195
|
-
}
|
|
196
|
-
} catch (err) {
|
|
197
|
-
console.error('AI Generation error', err);
|
|
198
|
-
setStatusMsg('Generation failed.');
|
|
199
|
-
} finally {
|
|
200
|
-
setIsGenerating(false);
|
|
201
|
-
setTimeout(() => setStatusMsg(''), 3000);
|
|
202
|
-
}
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
const handleDragStart = (event: React.MouseEvent, type: string) => {
|
|
206
|
-
if (!modelerRef.current) return;
|
|
207
|
-
const elementFactory = modelerRef.current.get('elementFactory');
|
|
208
|
-
const create = modelerRef.current.get('create');
|
|
209
|
-
let shape = elementFactory.createShape({ type });
|
|
210
|
-
if (shape) create.start(event, shape);
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
if (view === 'dashboard') {
|
|
214
|
-
const currentProcess = BUSINESS_PROCESSES.find(bp => bp.id === activeProcessTab);
|
|
215
|
-
|
|
216
|
-
return (
|
|
217
|
-
<div className="min-h-screen bg-slate-50 text-slate-900">
|
|
218
|
-
<header className="bg-white border-b border-slate-200 px-8 py-6">
|
|
219
|
-
<div className="max-w-7xl mx-auto flex justify-between items-center">
|
|
220
|
-
<div>
|
|
221
|
-
<h1 className="text-2xl font-bold tracking-tight text-slate-900 flex items-center gap-2">
|
|
222
|
-
<Briefcase className="text-indigo-600" />
|
|
223
|
-
Enterprise Blueprint
|
|
224
|
-
</h1>
|
|
225
|
-
<p className="text-slate-500 text-sm">Organize processes, capabilities, and work cycles.</p>
|
|
226
|
-
</div>
|
|
227
|
-
<button className="bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-semibold hover:bg-indigo-700 transition-colors flex items-center gap-2 shadow-sm">
|
|
228
|
-
<Plus size={18} /> New Process
|
|
229
|
-
</button>
|
|
230
|
-
</div>
|
|
231
|
-
</header>
|
|
232
|
-
|
|
233
|
-
<div className="bg-white border-b border-slate-200 sticky top-0 z-10">
|
|
234
|
-
<div className="max-w-7xl mx-auto flex gap-8 px-8">
|
|
235
|
-
{BUSINESS_PROCESSES.map(bp => (
|
|
236
|
-
<button
|
|
237
|
-
key={bp.id}
|
|
238
|
-
onClick={() => setActiveProcessTab(bp.id)}
|
|
239
|
-
className={`py-4 text-sm font-semibold border-b-2 transition-all ${
|
|
240
|
-
activeProcessTab === bp.id
|
|
241
|
-
? 'border-indigo-600 text-indigo-600'
|
|
242
|
-
: 'border-transparent text-slate-500 hover:text-slate-700'
|
|
243
|
-
}`}
|
|
244
|
-
>
|
|
245
|
-
{bp.name}
|
|
246
|
-
</button>
|
|
247
|
-
))}
|
|
248
|
-
</div>
|
|
249
|
-
</div>
|
|
250
|
-
|
|
251
|
-
<main className="max-w-7xl mx-auto p-8">
|
|
252
|
-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
253
|
-
{currentProcess?.capabilities.map(cap => (
|
|
254
|
-
<div key={cap.id} className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden flex flex-col h-fit">
|
|
255
|
-
<div className="p-6 border-b border-slate-100 bg-slate-50/50">
|
|
256
|
-
<h3 className="text-lg font-bold flex items-center gap-2 text-slate-800">
|
|
257
|
-
<Target className="text-indigo-500 w-5 h-5" />
|
|
258
|
-
{cap.name}
|
|
259
|
-
</h3>
|
|
260
|
-
<span className="text-xs font-semibold text-slate-400 uppercase tracking-widest mt-1 block">Capability</span>
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
<div className="p-6 space-y-4">
|
|
264
|
-
<h4 className="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2 flex items-center gap-2">
|
|
265
|
-
<RefreshCw size={14} /> Work Cycles & Practices
|
|
266
|
-
</h4>
|
|
267
|
-
|
|
268
|
-
<div className="space-y-4">
|
|
269
|
-
{cap.cycles.map(cycle => (
|
|
270
|
-
<div key={cycle.id} className="rounded-xl border border-slate-100 overflow-hidden bg-white shadow-sm">
|
|
271
|
-
{/* Cycle Header */}
|
|
272
|
-
<div className="flex items-center justify-between p-3 bg-slate-50/50 group border-b border-slate-100">
|
|
273
|
-
<div className="flex items-center gap-2">
|
|
274
|
-
<RefreshCw size={14} className="text-indigo-400" />
|
|
275
|
-
<span className="text-sm font-bold text-slate-800">{cycle.name}</span>
|
|
276
|
-
</div>
|
|
277
|
-
<button
|
|
278
|
-
onClick={() => handleOpenDesigner(cycle)}
|
|
279
|
-
className="text-[10px] font-bold text-indigo-600 bg-indigo-100 px-2 py-1 rounded hover:bg-indigo-200 transition-colors uppercase tracking-tight"
|
|
280
|
-
>
|
|
281
|
-
Design Cycle
|
|
282
|
-
</button>
|
|
283
|
-
</div>
|
|
284
|
-
|
|
285
|
-
{/* Nested Practices */}
|
|
286
|
-
<div className="p-2 space-y-1">
|
|
287
|
-
{cycle.practices.length > 0 ? (
|
|
288
|
-
cycle.practices.map(practice => (
|
|
289
|
-
<div key={practice.id} className="flex items-center justify-between p-2 pl-4 rounded-lg hover:bg-indigo-50/50 group transition-all">
|
|
290
|
-
<div className="flex items-center gap-2">
|
|
291
|
-
<GitCommit size={12} className="text-slate-300" />
|
|
292
|
-
<span className="text-xs font-medium text-slate-600">{practice.name}</span>
|
|
293
|
-
</div>
|
|
294
|
-
<button
|
|
295
|
-
onClick={() => handleOpenDesigner(practice)}
|
|
296
|
-
className="text-[10px] font-semibold text-slate-400 opacity-0 group-hover:opacity-100 hover:text-indigo-600 flex items-center gap-1 transition-all"
|
|
297
|
-
>
|
|
298
|
-
Design Practice <ChevronRight size={10}/>
|
|
299
|
-
</button>
|
|
300
|
-
</div>
|
|
301
|
-
))
|
|
302
|
-
) : (
|
|
303
|
-
<div className="py-2 px-4 text-[10px] italic text-slate-400">No practices defined for this cycle.</div>
|
|
304
|
-
)}
|
|
305
|
-
</div>
|
|
306
|
-
</div>
|
|
307
|
-
))}
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
|
-
))}
|
|
312
|
-
</div>
|
|
313
|
-
</main>
|
|
314
|
-
</div>
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Designer View
|
|
319
|
-
return (
|
|
320
|
-
<div className="flex h-screen w-full bg-white overflow-hidden text-slate-800">
|
|
321
|
-
<div className={`${isSidebarOpen ? 'w-80' : 'w-0'} transition-all duration-300 border-r border-slate-200 bg-slate-50 flex flex-col relative z-20 shadow-xl`}>
|
|
322
|
-
{isSidebarOpen && (
|
|
323
|
-
<div className="flex-1 flex flex-col p-6 overflow-y-auto w-80">
|
|
324
|
-
<div className="flex items-center gap-2 mb-8">
|
|
325
|
-
<button
|
|
326
|
-
onClick={(e) => {
|
|
327
|
-
e.preventDefault();
|
|
328
|
-
handleBackToDashboard();
|
|
329
|
-
}}
|
|
330
|
-
className="p-2 hover:bg-slate-200 rounded-lg transition-colors mr-1 flex items-center justify-center text-slate-600 hover:text-indigo-600 active:scale-95 cursor-pointer"
|
|
331
|
-
title="Back to Dashboard"
|
|
332
|
-
>
|
|
333
|
-
<ArrowLeft size={18} />
|
|
334
|
-
</button>
|
|
335
|
-
<h1 className="font-bold text-lg tracking-tight text-slate-900 truncate">
|
|
336
|
-
{editingItem?.name}
|
|
337
|
-
</h1>
|
|
338
|
-
</div>
|
|
339
|
-
|
|
340
|
-
<div className="space-y-6">
|
|
341
|
-
<section>
|
|
342
|
-
<h3 className="text-xs font-bold uppercase tracking-wider text-slate-400 mb-4 flex items-center gap-2">
|
|
343
|
-
Palette
|
|
344
|
-
</h3>
|
|
345
|
-
<div className="grid grid-cols-1 gap-2">
|
|
346
|
-
{[
|
|
347
|
-
{ type: 'bpmn:StartEvent', icon: <Circle size={18} />, label: 'Start', color: 'bg-green-50 text-green-600' },
|
|
348
|
-
{ type: 'bpmn:Task', icon: <Square size={18} />, label: 'Task', color: 'bg-slate-100 text-slate-600' },
|
|
349
|
-
{ type: 'bpmn:IntermediateCatchEvent', icon: <MessageCircle size={18} />, label: 'Event', color: 'bg-blue-50 text-blue-600' },
|
|
350
|
-
{ type: 'bpmn:EndEvent', icon: <XCircle size={18} />, label: 'End', color: 'bg-red-50 text-red-600' }
|
|
351
|
-
].map(item => (
|
|
352
|
-
<div
|
|
353
|
-
key={item.type}
|
|
354
|
-
onMouseDown={(e) => handleDragStart(e, item.type)}
|
|
355
|
-
className="flex items-center gap-3 p-3 bg-white border border-slate-200 rounded-xl cursor-grab hover:border-indigo-400 hover:shadow-sm transition-all group"
|
|
356
|
-
>
|
|
357
|
-
<div className={`p-2 rounded-lg ${item.color}`}>
|
|
358
|
-
{item.icon}
|
|
359
|
-
</div>
|
|
360
|
-
<span className="text-sm font-medium">{item.label}</span>
|
|
361
|
-
</div>
|
|
362
|
-
))}
|
|
363
|
-
</div>
|
|
364
|
-
</section>
|
|
365
|
-
|
|
366
|
-
<hr className="border-slate-200" />
|
|
367
|
-
|
|
368
|
-
<section>
|
|
369
|
-
<label className="block text-xs font-bold uppercase tracking-wider text-slate-400 mb-3 flex items-center gap-2">
|
|
370
|
-
<Sparkles className="w-3 h-3 text-indigo-500" />
|
|
371
|
-
AI Architect
|
|
372
|
-
</label>
|
|
373
|
-
<textarea
|
|
374
|
-
className="w-full h-24 p-3 text-sm border border-slate-300 rounded-xl focus:ring-2 focus:ring-indigo-500 bg-white outline-none resize-none transition-all"
|
|
375
|
-
placeholder="Ask AI to draft this process..."
|
|
376
|
-
value={prompt}
|
|
377
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
378
|
-
/>
|
|
379
|
-
<button
|
|
380
|
-
onClick={generateWithAi}
|
|
381
|
-
disabled={isGenerating || !prompt.trim()}
|
|
382
|
-
className="w-full mt-2 bg-indigo-600 hover:bg-indigo-700 disabled:bg-slate-300 text-white font-medium py-2 rounded-xl flex items-center justify-center gap-2 transition-all shadow-md active:scale-95"
|
|
383
|
-
>
|
|
384
|
-
{isGenerating ? "Drafting..." : "Generate Flow"}
|
|
385
|
-
</button>
|
|
386
|
-
{statusMsg && <p className="mt-2 text-[10px] text-center font-medium text-indigo-600">{statusMsg}</p>}
|
|
387
|
-
</section>
|
|
388
|
-
|
|
389
|
-
<section className="mt-auto space-y-3 pt-6 border-t border-slate-200">
|
|
390
|
-
<button
|
|
391
|
-
onClick={() => {
|
|
392
|
-
if (modelerRef.current) {
|
|
393
|
-
modelerRef.current.importXML(INITIAL_DIAGRAM);
|
|
394
|
-
}
|
|
395
|
-
}}
|
|
396
|
-
className="w-full flex items-center gap-2 p-2 text-red-500 hover:bg-red-50 rounded-lg text-xs font-medium transition-all"
|
|
397
|
-
>
|
|
398
|
-
<Trash2 size={14} /> Reset Canvas
|
|
399
|
-
</button>
|
|
400
|
-
</section>
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
)}
|
|
404
|
-
|
|
405
|
-
<button
|
|
406
|
-
onClick={() => setIsSidebarOpen(!isSidebarOpen)}
|
|
407
|
-
className="absolute -right-4 top-1/2 -translate-y-1/2 bg-white border border-slate-200 rounded-full p-1 shadow-md hover:text-indigo-600 transition-all z-50"
|
|
408
|
-
>
|
|
409
|
-
{isSidebarOpen ? <ChevronLeft size={20}/> : <ChevronRight size={20}/>}
|
|
410
|
-
</button>
|
|
411
|
-
</div>
|
|
412
|
-
|
|
413
|
-
<div className="flex-1 relative flex flex-col min-w-0">
|
|
414
|
-
<header className="h-14 border-b border-slate-200 flex items-center justify-between px-6 bg-white/80 backdrop-blur-md z-10">
|
|
415
|
-
<div className="flex items-center gap-2 text-xs font-medium">
|
|
416
|
-
<span className="text-slate-400">Architecture</span>
|
|
417
|
-
<ChevronRight size={12} className="text-slate-300" />
|
|
418
|
-
<span className="text-slate-400 uppercase tracking-tighter">{editingItem?.type === 'cycle' ? 'Cycle' : 'Practice'}</span>
|
|
419
|
-
<ChevronRight size={12} className="text-slate-300" />
|
|
420
|
-
<span className="text-slate-900 font-bold">{editingItem?.name}</span>
|
|
421
|
-
</div>
|
|
422
|
-
<div className="flex gap-2">
|
|
423
|
-
<button
|
|
424
|
-
onClick={handleDownloadXml}
|
|
425
|
-
className="text-xs font-bold text-white bg-indigo-600 hover:bg-indigo-700 px-4 py-2 rounded-lg shadow-sm flex items-center gap-2 transition-all active:scale-95"
|
|
426
|
-
>
|
|
427
|
-
<Download size={14} /> Export BPMN
|
|
428
|
-
</button>
|
|
429
|
-
</div>
|
|
430
|
-
</header>
|
|
431
|
-
|
|
432
|
-
<div className="flex-1 relative overflow-hidden bg-white">
|
|
433
|
-
<div className="absolute inset-0 bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] [background-size:24px_24px] pointer-events-none opacity-40" />
|
|
434
|
-
<div ref={containerRef} className="h-full w-full bpmn-io-style relative" />
|
|
435
|
-
</div>
|
|
436
|
-
</div>
|
|
437
|
-
</div>
|
|
438
|
-
);
|
|
439
|
-
};
|
|
440
|
-
|
|
441
|
-
export default App;
|
package/index.html
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
<!DOCTYPE html>
|
|
3
|
-
<html lang="en">
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>BPMN Flow Designer</title>
|
|
8
|
-
<script src="https://cdn.tailwindcss.com"></script>
|
|
9
|
-
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
10
|
-
|
|
11
|
-
<!-- BPMN-JS Styles -->
|
|
12
|
-
<link rel="stylesheet" href="https://unpkg.com/bpmn-js@18.1.0/dist/assets/diagram-js.css">
|
|
13
|
-
<link rel="stylesheet" href="https://unpkg.com/bpmn-js@18.1.0/dist/assets/bpmn-font/css/bpmn.css">
|
|
14
|
-
|
|
15
|
-
<!-- BPMN-JS Library -->
|
|
16
|
-
<script src="https://unpkg.com/bpmn-js@18.1.0/dist/bpmn-modeler.development.js"></script>
|
|
17
|
-
|
|
18
|
-
<style>
|
|
19
|
-
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
|
|
20
|
-
body {
|
|
21
|
-
font-family: 'Inter', sans-serif;
|
|
22
|
-
margin: 0;
|
|
23
|
-
padding: 0;
|
|
24
|
-
overflow: hidden;
|
|
25
|
-
height: 100vh;
|
|
26
|
-
}
|
|
27
|
-
#root {
|
|
28
|
-
height: 100%;
|
|
29
|
-
}
|
|
30
|
-
/* Hide the default bpmn-js palette to use our custom one */
|
|
31
|
-
.bpmn-io-style .djs-palette {
|
|
32
|
-
display: none !important;
|
|
33
|
-
}
|
|
34
|
-
.bpmn-io-style .djs-container {
|
|
35
|
-
background-color: transparent !important;
|
|
36
|
-
}
|
|
37
|
-
.bjs-powered-by {
|
|
38
|
-
display: none !important;
|
|
39
|
-
}
|
|
40
|
-
</style>
|
|
41
|
-
<script type="importmap">
|
|
42
|
-
{
|
|
43
|
-
"imports": {
|
|
44
|
-
"react": "https://esm.sh/react@19.0.0",
|
|
45
|
-
"react-dom": "https://esm.sh/react-dom@19.0.0",
|
|
46
|
-
"react-dom/client": "https://esm.sh/react-dom@19.0.0/client",
|
|
47
|
-
"@google/genai": "https://esm.sh/@google/genai@1.3.0",
|
|
48
|
-
"lucide-react": "https://esm.sh/lucide-react@0.460.0",
|
|
49
|
-
"react-dom/": "https://esm.sh/react-dom@^19.2.3/",
|
|
50
|
-
"react/": "https://esm.sh/react@^19.2.3/"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
</script>
|
|
54
|
-
<link rel="stylesheet" href="/index.css">
|
|
55
|
-
</head>
|
|
56
|
-
<body class="bg-slate-50">
|
|
57
|
-
<div id="root"></div>
|
|
58
|
-
<script type="module" src="/index.tsx"></script>
|
|
59
|
-
</body>
|
|
60
|
-
</html>
|
package/index.tsx
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import React from 'react';
|
|
3
|
-
import ReactDOM from 'react-dom/client';
|
|
4
|
-
import App from './App';
|
|
5
|
-
|
|
6
|
-
const rootElement = document.getElementById('root');
|
|
7
|
-
if (!rootElement) {
|
|
8
|
-
throw new Error("Could not find root element to mount to");
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const root = ReactDOM.createRoot(rootElement);
|
|
12
|
-
root.render(
|
|
13
|
-
<React.StrictMode>
|
|
14
|
-
<App />
|
|
15
|
-
</React.StrictMode>
|
|
16
|
-
);
|
package/metadata.json
DELETED
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"experimentalDecorators": true,
|
|
5
|
-
"useDefineForClassFields": false,
|
|
6
|
-
"module": "ESNext",
|
|
7
|
-
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"types": ["node"],
|
|
10
|
-
"moduleResolution": "bundler",
|
|
11
|
-
"isolatedModules": true,
|
|
12
|
-
"moduleDetection": "force",
|
|
13
|
-
"allowJs": true,
|
|
14
|
-
"jsx": "react-jsx",
|
|
15
|
-
"paths": {
|
|
16
|
-
"@/*": ["./*"]
|
|
17
|
-
},
|
|
18
|
-
"allowImportingTsExtensions": true,
|
|
19
|
-
"noEmit": true
|
|
20
|
-
}
|
|
21
|
-
}
|
package/vite.config.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import { defineConfig, loadEnv } from 'vite';
|
|
3
|
-
import react from '@vitejs/plugin-react';
|
|
4
|
-
|
|
5
|
-
export default defineConfig(({ mode }) => {
|
|
6
|
-
const env = loadEnv(mode, '.', '');
|
|
7
|
-
return {
|
|
8
|
-
server: {
|
|
9
|
-
port: 3000,
|
|
10
|
-
host: '0.0.0.0',
|
|
11
|
-
},
|
|
12
|
-
plugins: [react()],
|
|
13
|
-
define: {
|
|
14
|
-
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
|
|
15
|
-
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
|
|
16
|
-
},
|
|
17
|
-
resolve: {
|
|
18
|
-
alias: {
|
|
19
|
-
'@': path.resolve(__dirname, '.'),
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
});
|