improve copy & paste
Deploy / deploy (push) Successful in 38s

This commit is contained in:
2026-05-07 19:04:23 +02:00
parent 2e2f08e435
commit 4beb2e1715
+67 -2
View File
@@ -22,6 +22,7 @@ let dragHandle = null;
let dragOrig = null;
let nextShapeZ = 0;
let copyBuffer = null;
document.addEventListener('DOMContentLoaded', () => {
canvas = document.getElementById('networkCanvas');
@@ -49,6 +50,19 @@ document.addEventListener('DOMContentLoaded', () => {
window.addEventListener('resize', () => { resizeCanvas(); renderNetwork(); });
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
if (!document.activeElement || document.activeElement.tagName !== 'INPUT') {
if (selectedNodeId) copyNode(selectedNodeId);
else if (selectedShapeId) copyShape(selectedShapeId);
}
return;
}
if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
if (!document.activeElement || document.activeElement.tagName !== 'INPUT') {
if (copyBuffer) pasteItem();
}
return;
}
if (e.key === 'Delete') {
if (!document.activeElement || document.activeElement.tagName !== 'INPUT') {
if (selectedNodeId) deleteSelectedNode(selectedNodeId);
@@ -301,6 +315,57 @@ async function deleteSelectedShape(id) {
loadNetworkData();
}
function copyNode(id) {
const n = nodes.find(x => x.id == id);
if (!n) return;
copyBuffer = { type: 'node', data: { ...n } };
}
function copyShape(id) {
const s = shapes.find(x => x.id == id);
if (!s) return;
copyBuffer = { type: 'shape', data: { ...s } };
}
async function pasteItem() {
if (!copyBuffer) return;
const offset = 30;
if (copyBuffer.type === 'node') {
const d = copyBuffer.data;
await apiFetch('nodes', {
method: 'POST',
body: JSON.stringify({
label: d.label + ' (copy)',
ip_address: d.ip_address,
node_type: d.node_type,
status: d.status,
group_name: d.group_name,
pos_x: (parseFloat(d.pos_x) || 100) + offset,
pos_y: (parseFloat(d.pos_y) || 100) + offset
})
});
loadNetworkData();
} else if (copyBuffer.type === 'shape') {
const d = copyBuffer.data;
await apiFetch('shapes', {
method: 'POST',
body: JSON.stringify({
label: d.label + ' (copy)',
shape_type: d.shape_type,
pos_x: (parseFloat(d.pos_x) || 100) + offset,
pos_y: (parseFloat(d.pos_y) || 100) + offset,
width: d.width || 200,
height: d.height || 150,
color: d.color || '#1e3a5f',
border_color: d.border_color || '#3b82f6',
opacity: parseFloat(d.opacity) || 0.15,
z_index: nextShapeZ++
})
});
loadNetworkData();
}
}
function showConfirm(msg) {
return new Promise((resolve) => {
const modalEl = document.getElementById('confirmModal');
@@ -381,7 +446,7 @@ const NODE_FA_ICONS = {
server: { icon: '\uf233', color: '#8b5cf6' },
router: { icon: '\uf4d8', color: '#f59e0b' },
firewall: { icon: '\uf6ed', color: '#ef4444' },
switch: { icon: '\uf2d1', color: '#06b6d4' },
switch: { icon: '\uf0e8', color: '#06b6d4' },
cloud: { icon: '\uf0c2', color: '#22c55e' },
endpoint: { icon: '\uf109', color: '#ec4899' },
other: { icon: '\uf111', color: '#6b7280' }
@@ -501,7 +566,7 @@ function drawCanvasNode(n) {
// Draw icon using Font Awesome
ctx.save();
ctx.font = '500 22px "Font Awesome 6 Free", "FontAwesome", "Font Awesome 5 Free", sans-serif';
ctx.font = '900 22px "Font Awesome 6 Free", "FontAwesome", "Font Awesome 5 Free"';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = n.color;