@@ -86,6 +86,40 @@ function startSync() {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupCanvasClickCreate() {
|
||||||
|
const wrapper = document.getElementById('networkCanvasWrapper');
|
||||||
|
const bar = document.getElementById('nodeToolbar');
|
||||||
|
wrapper.addEventListener('click', async (e) => {
|
||||||
|
if (!pendingCanvasCreate) return;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const mx = e.clientX - rect.left - panX;
|
||||||
|
const my = e.clientY - rect.top - panY;
|
||||||
|
const type = pendingCanvasCreate;
|
||||||
|
pendingCanvasCreate = null;
|
||||||
|
if (bar) bar.querySelectorAll('.d-inline-flex').forEach(s => s.style.outline = 'none');
|
||||||
|
|
||||||
|
if (type.startsWith('shape:')) {
|
||||||
|
const shapeType = type.split(':')[1];
|
||||||
|
await apiFetch('shapes', { method: 'POST', body: JSON.stringify({
|
||||||
|
label: shapeType === 'rectangle' ? 'Box' : 'Ellipse',
|
||||||
|
shape_type: shapeType,
|
||||||
|
pos_x: mx - 100, pos_y: my - 75,
|
||||||
|
width: 200, height: 150,
|
||||||
|
color: '#1e3a5f', border_color: '#3b82f6',
|
||||||
|
opacity: 0.15, z_index: nextShapeZ++
|
||||||
|
})});
|
||||||
|
} else {
|
||||||
|
await apiFetch('nodes', { method: 'POST', body: JSON.stringify({
|
||||||
|
label: type.charAt(0).toUpperCase() + type.slice(1),
|
||||||
|
ip_address: '', node_type: type || 'host', status: 'unknown',
|
||||||
|
group_name: 'default', notes: '',
|
||||||
|
pos_x: mx, pos_y: my
|
||||||
|
})});
|
||||||
|
}
|
||||||
|
await loadNetworkData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const DOC_TYPE_ICONS = {
|
const DOC_TYPE_ICONS = {
|
||||||
deployment: { icon: 'fa-server', color: '#06b6d4' },
|
deployment: { icon: 'fa-server', color: '#06b6d4' },
|
||||||
attack: { icon: 'fa-bolt', color: '#ef4444' },
|
attack: { icon: 'fa-bolt', color: '#ef4444' },
|
||||||
@@ -783,6 +817,7 @@ async function loadNetworkData() {
|
|||||||
renderNodeList();
|
renderNodeList();
|
||||||
renderShapeList();
|
renderShapeList();
|
||||||
renderNodeToolbar();
|
renderNodeToolbar();
|
||||||
|
setupCanvasClickCreate();
|
||||||
startSync();
|
startSync();
|
||||||
if (shapes.length) nextShapeZ = Math.max(...shapes.map(x => x.z_index)) + 1;
|
if (shapes.length) nextShapeZ = Math.max(...shapes.map(x => x.z_index)) + 1;
|
||||||
buildCanvasGraph();
|
buildCanvasGraph();
|
||||||
@@ -1234,15 +1269,18 @@ function renderNodeToolbar() {
|
|||||||
|
|
||||||
function setupCanvasClickCreate() {
|
function setupCanvasClickCreate() {
|
||||||
const wrapper = document.getElementById('networkCanvasWrapper');
|
const wrapper = document.getElementById('networkCanvasWrapper');
|
||||||
|
if (wrapper.dataset.clickCreate) return;
|
||||||
|
wrapper.dataset.clickCreate = '1';
|
||||||
const bar = document.getElementById('nodeToolbar');
|
const bar = document.getElementById('nodeToolbar');
|
||||||
wrapper.addEventListener('click', async (e) => {
|
wrapper.addEventListener('click', async (e) => {
|
||||||
if (!pendingCanvasCreate) return;
|
if (!pendingCanvasCreate) return;
|
||||||
|
if (e.target !== canvas && e.target !== wrapper) return;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
const mx = e.clientX - rect.left - panX;
|
const mx = e.clientX - rect.left - panX;
|
||||||
const my = e.clientY - rect.top - panY;
|
const my = e.clientY - rect.top - panY;
|
||||||
const type = pendingCanvasCreate;
|
const type = pendingCanvasCreate;
|
||||||
pendingCanvasCreate = null;
|
pendingCanvasCreate = null;
|
||||||
bar.querySelectorAll('.d-inline-flex').forEach(s => s.style.outline = 'none');
|
if (bar) bar.querySelectorAll('.d-inline-flex').forEach(s => s.style.outline = 'none');
|
||||||
|
|
||||||
if (type.startsWith('shape:')) {
|
if (type.startsWith('shape:')) {
|
||||||
const shapeType = type.split(':')[1];
|
const shapeType = type.split(':')[1];
|
||||||
|
|||||||
Reference in New Issue
Block a user