Feature/physics and tilemap enhancement (#247)

* feat(behavior-tree,tilemap): 修复编辑器连线缩放问题并增强插件系统

* feat(node-editor,blueprint): 新增通用节点编辑器和蓝图可视化脚本系统

* feat(editor,tilemap): 优化编辑器UI样式和Tilemap编辑器功能

* fix: 修复CodeQL安全警告和CI类型检查错误

* fix: 修复CodeQL安全警告和CI类型检查错误

* fix: 修复CodeQL安全警告和CI类型检查错误
This commit is contained in:
YHH
2025-11-29 23:00:48 +08:00
committed by GitHub
parent f03b73b58e
commit 359886c72f
198 changed files with 33879 additions and 13121 deletions

View File

@@ -0,0 +1,262 @@
/**
* Canvas Styles
* 画布样式
*/
/* ==================== Canvas Container 画布容器 ==================== */
.ne-canvas {
background: var(--ne-canvas-bg);
touch-action: none;
}
.ne-canvas:focus {
outline: none;
}
/* ==================== Canvas Content 画布内容 ==================== */
.ne-canvas-content {
will-change: transform;
}
/* ==================== Canvas Grid 画布网格 ==================== */
.ne-canvas-grid {
z-index: var(--ne-z-grid);
}
/* ==================== Selection Box 选择框 ==================== */
.ne-selection-box {
position: absolute;
border: 1px dashed var(--ne-node-border-selected);
background: rgba(0, 120, 212, 0.1);
pointer-events: none;
z-index: var(--ne-z-dragging);
}
/* ==================== Minimap 小地图 ==================== */
.ne-minimap {
position: absolute;
right: 16px;
bottom: 16px;
width: 200px;
height: 150px;
background: rgba(0, 0, 0, 0.6);
border: 1px solid var(--ne-node-border);
border-radius: 4px;
overflow: hidden;
z-index: var(--ne-z-menu);
}
.ne-minimap-viewport {
position: absolute;
border: 2px solid var(--ne-node-border-selected);
background: rgba(0, 120, 212, 0.2);
cursor: move;
}
.ne-minimap-node {
position: absolute;
background: var(--ne-node-bg);
border-radius: 2px;
}
/* ==================== Zoom Controls 缩放控制 ==================== */
.ne-zoom-controls {
position: absolute;
right: 16px;
top: 16px;
display: flex;
flex-direction: column;
gap: 4px;
z-index: var(--ne-z-menu);
}
.ne-zoom-btn {
width: 32px;
height: 32px;
padding: 0;
border: 1px solid var(--ne-node-border);
background: var(--ne-node-bg);
color: var(--ne-text-primary);
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
transition: background var(--ne-transition-fast),
border-color var(--ne-transition-fast);
}
.ne-zoom-btn:hover {
background: var(--ne-node-bg-hover);
border-color: var(--ne-node-border-selected);
}
.ne-zoom-btn:active {
background: var(--ne-node-bg-selected);
}
.ne-zoom-level {
padding: 4px 8px;
font-size: var(--ne-font-size-small);
color: var(--ne-text-secondary);
text-align: center;
background: var(--ne-node-bg);
border: 1px solid var(--ne-node-border);
border-radius: 4px;
}
/* ==================== Context Menu 右键菜单 ==================== */
.ne-context-menu {
position: fixed;
min-width: 180px;
background: var(--ne-node-bg);
border: 1px solid var(--ne-node-border);
border-radius: 6px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
padding: 4px 0;
z-index: var(--ne-z-menu);
font-family: var(--ne-font-family);
font-size: var(--ne-font-size-body);
}
.ne-context-menu-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
color: var(--ne-text-primary);
cursor: pointer;
transition: background var(--ne-transition-fast);
}
.ne-context-menu-item:hover {
background: var(--ne-node-bg-hover);
}
.ne-context-menu-item.disabled {
color: var(--ne-text-muted);
cursor: not-allowed;
}
.ne-context-menu-item.disabled:hover {
background: transparent;
}
.ne-context-menu-item-icon {
width: 16px;
height: 16px;
opacity: 0.8;
}
.ne-context-menu-item-label {
flex: 1;
}
.ne-context-menu-item-shortcut {
color: var(--ne-text-muted);
font-size: var(--ne-font-size-small);
}
.ne-context-menu-separator {
height: 1px;
background: var(--ne-node-border);
margin: 4px 0;
}
.ne-context-menu-submenu {
position: relative;
}
.ne-context-menu-submenu::after {
content: '▶';
position: absolute;
right: 8px;
font-size: 10px;
color: var(--ne-text-muted);
}
/* ==================== Search Popup 搜索弹窗 ==================== */
.ne-search-popup {
position: fixed;
width: 300px;
max-height: 400px;
background: var(--ne-node-bg);
border: 1px solid var(--ne-node-border);
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
z-index: var(--ne-z-menu);
overflow: hidden;
}
.ne-search-input {
width: 100%;
padding: 12px 16px;
border: none;
border-bottom: 1px solid var(--ne-node-border);
background: transparent;
color: var(--ne-text-primary);
font-size: var(--ne-font-size-title);
font-family: var(--ne-font-family);
outline: none;
}
.ne-search-input::placeholder {
color: var(--ne-text-muted);
}
.ne-search-results {
max-height: 320px;
overflow-y: auto;
}
.ne-search-category {
padding: 8px 16px 4px;
color: var(--ne-text-muted);
font-size: var(--ne-font-size-small);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.ne-search-result {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 16px;
cursor: pointer;
transition: background var(--ne-transition-fast);
}
.ne-search-result:hover,
.ne-search-result.selected {
background: var(--ne-node-bg-hover);
}
.ne-search-result-icon {
width: 24px;
height: 24px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
}
.ne-search-result-info {
flex: 1;
}
.ne-search-result-name {
color: var(--ne-text-primary);
font-weight: 500;
}
.ne-search-result-path {
color: var(--ne-text-muted);
font-size: var(--ne-font-size-small);
}
.ne-search-empty {
padding: 24px 16px;
text-align: center;
color: var(--ne-text-muted);
}

View File

@@ -0,0 +1,179 @@
/**
* Connection Line Styles
* 连接线样式
*/
/* ==================== Connection Layer 连接层 ==================== */
.ne-connection-layer {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: var(--ne-z-connections);
overflow: visible;
}
/* ==================== Connection Path 连接路径 ==================== */
.ne-connection {
fill: none;
stroke-width: var(--ne-connection-width);
stroke-linecap: round;
pointer-events: stroke;
cursor: pointer;
transition: stroke-width var(--ne-transition-fast);
}
.ne-connection:hover {
stroke-width: var(--ne-connection-width-hover);
}
.ne-connection.selected {
stroke: var(--ne-connection-selected);
stroke-width: var(--ne-connection-width-hover);
filter: drop-shadow(0 0 4px var(--ne-connection-selected));
}
/* ==================== Connection Type Colors 连接类型颜色 ==================== */
.ne-connection.exec {
stroke: var(--ne-pin-exec);
stroke-width: var(--ne-connection-width-exec);
}
.ne-connection.bool { stroke: var(--ne-pin-bool); }
.ne-connection.int { stroke: var(--ne-pin-int); }
.ne-connection.float { stroke: var(--ne-pin-float); }
.ne-connection.string { stroke: var(--ne-pin-string); }
.ne-connection.vector2 { stroke: var(--ne-pin-vector2); }
.ne-connection.vector3 { stroke: var(--ne-pin-vector3); }
.ne-connection.vector4 { stroke: var(--ne-pin-vector4); }
.ne-connection.color { stroke: var(--ne-pin-color); }
.ne-connection.object { stroke: var(--ne-pin-object); }
.ne-connection.array { stroke: var(--ne-pin-array); }
.ne-connection.map { stroke: var(--ne-pin-map); }
.ne-connection.struct { stroke: var(--ne-pin-struct); }
.ne-connection.enum { stroke: var(--ne-pin-enum); }
.ne-connection.delegate { stroke: var(--ne-pin-delegate); }
.ne-connection.any { stroke: var(--ne-pin-any); }
/* ==================== Connection Preview 连接预览 ==================== */
.ne-connection-preview {
fill: none;
stroke: var(--ne-connection-preview);
stroke-width: 3px;
stroke-dasharray: 8 4;
pointer-events: none;
animation: ne-connection-dash 0.3s linear infinite;
filter: drop-shadow(0 0 4px currentColor);
}
.ne-connection-preview.valid {
stroke: var(--ne-state-success);
stroke-dasharray: none;
stroke-width: 3px;
filter: drop-shadow(0 0 8px var(--ne-state-success));
}
.ne-connection-preview.invalid {
stroke: var(--ne-connection-invalid);
filter: drop-shadow(0 0 6px var(--ne-connection-invalid));
}
/* Preview type colors */
.ne-connection-preview.exec { stroke: var(--ne-pin-exec); }
.ne-connection-preview.bool { stroke: var(--ne-pin-bool); }
.ne-connection-preview.int { stroke: var(--ne-pin-int); }
.ne-connection-preview.float { stroke: var(--ne-pin-float); }
.ne-connection-preview.string { stroke: var(--ne-pin-string); }
.ne-connection-preview.vector2 { stroke: var(--ne-pin-vector2); }
.ne-connection-preview.vector3 { stroke: var(--ne-pin-vector3); }
.ne-connection-preview.vector4 { stroke: var(--ne-pin-vector4); }
.ne-connection-preview.color { stroke: var(--ne-pin-color); }
.ne-connection-preview.object { stroke: var(--ne-pin-object); }
.ne-connection-preview.array { stroke: var(--ne-pin-array); }
.ne-connection-preview.map { stroke: var(--ne-pin-map); }
.ne-connection-preview.struct { stroke: var(--ne-pin-struct); }
.ne-connection-preview.enum { stroke: var(--ne-pin-enum); }
.ne-connection-preview.delegate { stroke: var(--ne-pin-delegate); }
.ne-connection-preview.any { stroke: var(--ne-pin-any); }
@keyframes ne-connection-dash {
to {
stroke-dashoffset: -12;
}
}
/* ==================== Connection Hit Area 连接点击区域 ==================== */
.ne-connection-hit {
fill: none;
stroke: transparent;
stroke-width: 20px;
pointer-events: stroke;
cursor: pointer;
}
.ne-connection-hit:hover + .ne-connection-glow + .ne-connection {
stroke-width: var(--ne-connection-width-hover);
}
/* ==================== Connection Glow 连接发光 ==================== */
.ne-connection-glow {
fill: none;
stroke-width: 8px;
opacity: 0;
filter: blur(4px);
pointer-events: none;
transition: opacity var(--ne-transition-fast);
}
.ne-connection-glow.exec { stroke: var(--ne-pin-exec); }
.ne-connection-glow.bool { stroke: var(--ne-pin-bool); }
.ne-connection-glow.int { stroke: var(--ne-pin-int); }
.ne-connection-glow.float { stroke: var(--ne-pin-float); }
.ne-connection-glow.string { stroke: var(--ne-pin-string); }
.ne-connection-glow.vector2 { stroke: var(--ne-pin-vector2); }
.ne-connection-glow.vector3 { stroke: var(--ne-pin-vector3); }
.ne-connection-glow.vector4 { stroke: var(--ne-pin-vector4); }
.ne-connection-glow.color { stroke: var(--ne-pin-color); }
.ne-connection-glow.object { stroke: var(--ne-pin-object); }
.ne-connection-glow.array { stroke: var(--ne-pin-array); }
.ne-connection-glow.map { stroke: var(--ne-pin-map); }
.ne-connection-glow.struct { stroke: var(--ne-pin-struct); }
.ne-connection-glow.enum { stroke: var(--ne-pin-enum); }
.ne-connection-glow.delegate { stroke: var(--ne-pin-delegate); }
.ne-connection-glow.any { stroke: var(--ne-pin-any); }
.ne-connection-hit:hover + .ne-connection-glow {
opacity: 0.5;
}
/* ==================== Connection Flow Animation 连接流动动画 ==================== */
.ne-connection.exec.animated {
stroke-dasharray: 4 8;
animation: ne-exec-flow 0.4s linear infinite;
}
@keyframes ne-exec-flow {
to {
stroke-dashoffset: -12;
}
}
/* ==================== Reroute Node 重路由节点 ==================== */
.ne-reroute {
fill: var(--ne-node-bg);
stroke: currentColor;
stroke-width: 2px;
cursor: move;
transition: transform var(--ne-transition-fast);
}
.ne-reroute:hover {
transform: scale(1.2);
}
.ne-reroute.selected {
stroke: var(--ne-connection-selected);
filter: drop-shadow(0 0 4px var(--ne-connection-selected));
}

View File

@@ -0,0 +1,290 @@
/**
* Context Menu Styles
* 上下文菜单样式
*/
/* ==================== Menu Container 菜单容器 ==================== */
.ne-context-menu {
position: fixed;
z-index: var(--ne-z-menu);
min-width: 300px;
max-width: 400px;
max-height: 500px;
background: #1a1a1a;
border: 1px solid #3a3a3a;
border-radius: 4px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
font-family: var(--ne-font-family);
overflow: hidden;
display: flex;
flex-direction: column;
}
/* ==================== Menu Header 菜单头部 ==================== */
.ne-context-menu-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 10px;
background: #252525;
border-bottom: 1px solid #3a3a3a;
}
.ne-context-menu-title {
font-size: 12px;
font-weight: 500;
color: #c0c0c0;
}
.ne-context-menu-options {
display: flex;
align-items: center;
gap: 4px;
}
.ne-context-menu-checkbox {
display: flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: #909090;
cursor: pointer;
}
.ne-context-menu-checkbox input {
width: 12px;
height: 12px;
margin: 0;
}
/* ==================== Search Box 搜索框 ==================== */
.ne-context-menu-search {
padding: 6px 8px;
border-bottom: 1px solid #303030;
background: #1e1e1e;
}
.ne-context-menu-search-wrapper {
position: relative;
display: flex;
align-items: center;
}
.ne-context-menu-search-input {
width: 100%;
padding: 6px 28px 6px 8px;
font-size: 12px;
font-family: var(--ne-font-family);
background: #0a0a0a;
border: 1px solid #404040;
border-radius: 2px;
color: #e0e0e0;
outline: none;
box-sizing: border-box;
}
.ne-context-menu-search-input::placeholder {
color: #606060;
}
.ne-context-menu-search-input:focus {
border-color: #5080c0;
}
.ne-context-menu-search-clear {
position: absolute;
right: 6px;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
background: none;
border: none;
color: #606060;
cursor: pointer;
font-size: 14px;
padding: 0;
}
.ne-context-menu-search-clear:hover {
color: #a0a0a0;
}
/* ==================== Content Area 内容区域 ==================== */
.ne-context-menu-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 4px 0;
}
.ne-context-menu-content::-webkit-scrollbar {
width: 8px;
}
.ne-context-menu-content::-webkit-scrollbar-track {
background: #1a1a1a;
}
.ne-context-menu-content::-webkit-scrollbar-thumb {
background: #404040;
border-radius: 4px;
}
.ne-context-menu-content::-webkit-scrollbar-thumb:hover {
background: #505050;
}
/* ==================== Category Section 分类区域 ==================== */
.ne-context-menu-category {
user-select: none;
}
.ne-context-menu-category-header {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
font-size: 11px;
color: #808080;
cursor: pointer;
}
.ne-context-menu-category-header:hover {
background: rgba(255, 255, 255, 0.03);
}
.ne-context-menu-category-chevron {
width: 12px;
height: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
transition: transform 0.15s ease;
}
.ne-context-menu-category.expanded .ne-context-menu-category-chevron {
transform: rotate(90deg);
}
.ne-context-menu-category-title {
flex: 1;
}
.ne-context-menu-category-items {
display: none;
}
.ne-context-menu-category.expanded .ne-context-menu-category-items {
display: block;
}
/* ==================== Menu Item 菜单项 ==================== */
.ne-context-menu-item {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 8px 4px 24px;
cursor: pointer;
transition: background 0.1s ease;
}
.ne-context-menu-item:hover {
background: rgba(255, 255, 255, 0.05);
}
.ne-context-menu-item.highlighted {
background: #264f78;
}
.ne-context-menu-item-icon {
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.ne-context-menu-item-icon-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.ne-context-menu-item-icon-dot.event { background: var(--ne-category-event); }
.ne-context-menu-item-icon-dot.function { background: var(--ne-category-function); }
.ne-context-menu-item-icon-dot.pure { background: var(--ne-category-pure); }
.ne-context-menu-item-icon-dot.flow { background: var(--ne-category-flow); }
.ne-context-menu-item-icon-dot.variable { background: var(--ne-category-variable); }
.ne-context-menu-item-icon-dot.literal { background: var(--ne-category-literal); }
.ne-context-menu-item-icon-dot.comment { background: var(--ne-category-comment); }
.ne-context-menu-item-icon-dot.custom { background: var(--ne-category-custom); }
/* Function icon - f */
.ne-context-menu-item-icon-func {
font-size: 11px;
font-weight: bold;
font-style: italic;
color: var(--ne-category-function);
}
.ne-context-menu-item-info {
flex: 1;
min-width: 0;
}
.ne-context-menu-item-title {
font-size: 12px;
color: #d0d0d0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ne-context-menu-item-title .highlight {
background: #4a6a2a;
color: #90ff90;
padding: 0 1px;
border-radius: 1px;
}
.ne-context-menu-item-description {
font-size: 10px;
color: #707070;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 1px;
}
/* ==================== Empty State 空状态 ==================== */
.ne-context-menu-empty {
padding: 20px;
text-align: center;
color: #606060;
font-size: 12px;
}
/* ==================== Footer 底部 ==================== */
.ne-context-menu-footer {
padding: 6px 10px;
border-top: 1px solid #303030;
background: #1e1e1e;
font-size: 10px;
color: #606060;
display: flex;
gap: 12px;
}
.ne-context-menu-footer kbd {
display: inline-block;
padding: 1px 4px;
font-family: var(--ne-font-family-mono);
font-size: 9px;
background: #303030;
border-radius: 2px;
margin-right: 2px;
}

View File

@@ -0,0 +1,191 @@
/**
* Dialog Styles
* 对话框样式
*/
/* ==================== Overlay 遮罩层 ==================== */
.ne-dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
z-index: var(--ne-z-dialog);
display: flex;
align-items: center;
justify-content: center;
animation: ne-dialog-fade-in 0.15s ease;
}
@keyframes ne-dialog-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* ==================== Dialog Container 对话框容器 ==================== */
.ne-dialog {
min-width: 320px;
max-width: 450px;
background: #1e1e1e;
border: 1px solid #3a3a3a;
border-radius: 6px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
font-family: var(--ne-font-family);
animation: ne-dialog-slide-in 0.15s ease;
overflow: hidden;
}
@keyframes ne-dialog-slide-in {
from {
opacity: 0;
transform: scale(0.95) translateY(-10px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
/* ==================== Dialog Header 对话框头部 ==================== */
.ne-dialog-header {
padding: 12px 16px;
background: #252525;
border-bottom: 1px solid #3a3a3a;
}
.ne-dialog-title {
font-size: 14px;
font-weight: 500;
color: #e0e0e0;
}
/* Type-specific header colors */
.ne-dialog-danger .ne-dialog-header {
background: linear-gradient(90deg, #4a1a1a 0%, #252525 100%);
}
.ne-dialog-warning .ne-dialog-header {
background: linear-gradient(90deg, #4a3a1a 0%, #252525 100%);
}
.ne-dialog-info .ne-dialog-header {
background: linear-gradient(90deg, #1a3a4a 0%, #252525 100%);
}
/* ==================== Dialog Body 对话框主体 ==================== */
.ne-dialog-body {
padding: 16px;
}
.ne-dialog-message {
margin: 0;
font-size: 13px;
color: #b0b0b0;
line-height: 1.5;
}
/* ==================== Dialog Footer 对话框底部 ==================== */
.ne-dialog-footer {
display: flex;
justify-content: flex-end;
gap: 8px;
padding: 12px 16px;
background: #1a1a1a;
border-top: 1px solid #303030;
}
/* ==================== Dialog Buttons 对话框按钮 ==================== */
.ne-dialog-btn {
padding: 6px 16px;
font-size: 12px;
font-family: var(--ne-font-family);
font-weight: 500;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
}
.ne-dialog-btn:focus {
outline: none;
}
.ne-dialog-btn-cancel {
background: #2a2a2a;
border-color: #404040;
color: #b0b0b0;
}
.ne-dialog-btn-cancel:hover {
background: #353535;
border-color: #505050;
color: #d0d0d0;
}
.ne-dialog-btn-cancel:active {
background: #252525;
}
.ne-dialog-btn-confirm {
color: #fff;
}
.ne-dialog-btn-danger {
background: #8b2a2a;
border-color: #a03030;
}
.ne-dialog-btn-danger:hover {
background: #9b3535;
border-color: #b04040;
}
.ne-dialog-btn-danger:active {
background: #7b2525;
}
.ne-dialog-btn-danger:focus {
box-shadow: 0 0 0 2px rgba(160, 48, 48, 0.4);
}
.ne-dialog-btn-warning {
background: #8b6a2a;
border-color: #a08030;
}
.ne-dialog-btn-warning:hover {
background: #9b7535;
border-color: #b09040;
}
.ne-dialog-btn-warning:active {
background: #7b5a25;
}
.ne-dialog-btn-warning:focus {
box-shadow: 0 0 0 2px rgba(160, 128, 48, 0.4);
}
.ne-dialog-btn-info {
background: #2a5a8b;
border-color: #3070a0;
}
.ne-dialog-btn-info:hover {
background: #356a9b;
border-color: #4080b0;
}
.ne-dialog-btn-info:active {
background: #254a7b;
}
.ne-dialog-btn-info:focus {
box-shadow: 0 0 0 2px rgba(48, 112, 160, 0.4);
}

View File

@@ -0,0 +1,265 @@
/**
* Graph Node Styles
* 图节点样式
*/
/* ==================== Node Container 节点容器 ==================== */
.ne-node {
position: absolute;
min-width: var(--ne-node-min-width);
max-width: var(--ne-node-max-width);
background: var(--ne-node-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid var(--ne-node-border);
border-radius: var(--ne-node-border-radius);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
font-family: var(--ne-font-family);
user-select: none;
cursor: grab;
transition: box-shadow var(--ne-transition-normal),
border-color var(--ne-transition-normal);
}
.ne-node:hover {
border-color: rgba(80, 80, 100, 0.6);
}
.ne-node.selected {
border-color: var(--ne-node-border-selected);
box-shadow: 0 0 0 1px var(--ne-node-border-selected),
0 0 16px rgba(229, 160, 32, 0.4),
0 4px 12px rgba(0, 0, 0, 0.5);
}
.ne-node.dragging {
cursor: grabbing;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.6);
z-index: var(--ne-z-dragging);
}
/* ==================== Node Header 节点头部 ==================== */
.ne-node-header {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 10px;
min-height: var(--ne-node-header-height);
border-radius: calc(var(--ne-node-border-radius) - 1px) calc(var(--ne-node-border-radius) - 1px) 0 0;
color: var(--ne-text-title);
font-size: var(--ne-font-size-title);
font-weight: 500;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);
position: relative;
overflow: hidden;
}
/* Gradient overlay for header */
.ne-node-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.15;
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.3) 50%, transparent 100%);
pointer-events: none;
}
/* Node category header colors */
.ne-node-header.event {
background: linear-gradient(90deg, var(--ne-category-event) 0%, var(--ne-category-event-dark) 100%);
}
.ne-node-header.function {
background: linear-gradient(90deg, var(--ne-category-function) 0%, var(--ne-category-function-dark) 100%);
}
.ne-node-header.pure {
background: linear-gradient(90deg, var(--ne-category-pure) 0%, var(--ne-category-pure-dark) 100%);
}
.ne-node-header.flow {
background: linear-gradient(90deg, var(--ne-category-flow) 0%, var(--ne-category-flow-dark) 100%);
}
.ne-node-header.variable {
background: linear-gradient(90deg, var(--ne-category-variable) 0%, var(--ne-category-variable-dark) 100%);
}
.ne-node-header.literal {
background: linear-gradient(90deg, var(--ne-category-literal) 0%, var(--ne-category-literal-dark) 100%);
}
.ne-node-header.comment {
background: linear-gradient(90deg, var(--ne-category-comment) 0%, var(--ne-category-comment-dark) 100%);
}
.ne-node-header.custom {
background: linear-gradient(90deg, var(--ne-category-custom) 0%, var(--ne-category-custom-dark) 100%);
}
/* Header icon - diamond shape for events */
.ne-node-header-icon {
flex-shrink: 0;
width: 14px;
height: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.ne-node-header.event .ne-node-header-icon::before {
content: '';
width: 8px;
height: 8px;
background: #fff;
transform: rotate(45deg);
border-radius: 1px;
box-shadow: 0 0 4px rgba(255, 255, 255, 0.5);
}
.ne-node-header-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
letter-spacing: 0.2px;
}
.ne-node-header-subtitle {
font-size: var(--ne-font-size-small);
font-weight: 400;
opacity: 0.7;
margin-left: 4px;
}
/* Header exec pin */
.ne-node-header-exec {
width: 10px;
height: 10px;
background: transparent;
border: 2px solid #a04040;
cursor: pointer;
transition: all var(--ne-transition-fast);
margin-left: auto;
flex-shrink: 0;
}
.ne-node-header-exec:hover {
border-color: #ff6060;
box-shadow: 0 0 6px rgba(255, 96, 96, 0.6);
}
.ne-node-header-exec.connected {
background: #c04040;
border-color: #c04040;
}
/* ==================== Node Body 节点主体 ==================== */
.ne-node-body {
padding: 6px 0;
background: var(--ne-node-bg-body);
border-radius: 0 0 calc(var(--ne-node-border-radius) - 1px) calc(var(--ne-node-border-radius) - 1px);
}
.ne-node-content {
display: flex;
flex-direction: column;
gap: 1px;
}
/* ==================== Pin Row 引脚行 ==================== */
.ne-pin-row {
display: flex;
align-items: center;
gap: 8px;
min-height: 20px;
padding: 1px 0;
position: relative;
width: 100%;
box-sizing: border-box;
}
.ne-pin-row.input {
flex-direction: row;
justify-content: flex-start;
padding-left: 10px;
padding-right: 16px;
}
.ne-pin-row.output {
flex-direction: row-reverse;
justify-content: flex-start;
padding-right: 10px;
padding-left: 16px;
}
.ne-pin-label {
font-size: 11px;
color: #b0b0b0;
white-space: nowrap;
line-height: 1;
}
.ne-pin-row.output .ne-pin-label {
text-align: right;
}
/* ==================== Collapsed State 折叠状态 ==================== */
.ne-node.collapsed .ne-node-body {
display: none;
}
.ne-node.collapsed .ne-node-header {
border-radius: calc(var(--ne-node-border-radius) - 1px);
}
/* ==================== Comment Node 注释节点 ==================== */
.ne-node.comment {
background: rgba(50, 70, 50, 0.4);
border: 1px dashed rgba(100, 140, 100, 0.6);
min-width: 200px;
min-height: 100px;
}
.ne-node.comment .ne-node-header {
background: transparent;
color: #90a090;
}
.ne-node.comment .ne-node-body {
background: transparent;
color: #80a080;
font-style: italic;
}
/* ==================== Execution States 执行状态 ==================== */
.ne-node.running {
border-color: var(--ne-state-running);
box-shadow: 0 0 0 1px var(--ne-state-running),
0 0 12px rgba(255, 167, 38, 0.5);
animation: ne-pulse 1s ease-in-out infinite;
}
.ne-node.success {
border-color: var(--ne-state-success);
box-shadow: 0 0 8px rgba(76, 175, 80, 0.4);
}
.ne-node.error {
border-color: var(--ne-state-error);
box-shadow: 0 0 8px rgba(244, 67, 54, 0.4);
}
@keyframes ne-pulse {
0%, 100% {
box-shadow: 0 0 0 1px var(--ne-state-running),
0 0 12px rgba(255, 167, 38, 0.5);
}
50% {
box-shadow: 0 0 0 2px var(--ne-state-running),
0 0 20px rgba(255, 167, 38, 0.7);
}
}

View File

@@ -0,0 +1,104 @@
/**
* Node Pin Styles
* 节点引脚样式
*/
/* ==================== Pin Base 引脚基础 ==================== */
.ne-pin {
position: relative;
width: 12px;
height: 12px;
cursor: pointer;
transition: transform 0.1s ease;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.ne-pin:hover {
transform: scale(1.2);
}
.ne-pin.compatible {
animation: ne-pin-pulse 0.5s ease-in-out infinite;
}
/* ==================== Pin Type Colors 引脚类型颜色 ==================== */
.ne-pin.exec { color: var(--ne-pin-exec); }
.ne-pin.bool { color: var(--ne-pin-bool); }
.ne-pin.int { color: var(--ne-pin-int); }
.ne-pin.float { color: var(--ne-pin-float); }
.ne-pin.string { color: var(--ne-pin-string); }
.ne-pin.vector2 { color: var(--ne-pin-vector2); }
.ne-pin.vector3 { color: var(--ne-pin-vector3); }
.ne-pin.vector4 { color: var(--ne-pin-vector4); }
.ne-pin.color { color: var(--ne-pin-color); }
.ne-pin.object { color: var(--ne-pin-object); }
.ne-pin.array { color: var(--ne-pin-array); }
.ne-pin.map { color: var(--ne-pin-map); }
.ne-pin.struct { color: var(--ne-pin-struct); }
.ne-pin.enum { color: var(--ne-pin-enum); }
.ne-pin.delegate { color: var(--ne-pin-delegate); }
.ne-pin.any { color: var(--ne-pin-any); }
/* ==================== Pin States 引脚状态 ==================== */
.ne-pin.dragging-from {
transform: scale(1.3);
}
.ne-pin.drop-target {
transform: scale(1.4);
}
.ne-pin.invalid-target {
opacity: 0.3;
}
/* ==================== Pin Animations 引脚动画 ==================== */
@keyframes ne-pin-pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.25);
}
}
/* ==================== Pin Value Input 引脚值输入 ==================== */
.ne-pin-value {
display: flex;
align-items: center;
gap: 4px;
}
.ne-pin-value-input {
width: 50px;
padding: 2px 4px;
font-size: 10px;
font-family: var(--ne-font-family-mono);
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 2px;
color: #c0c0c0;
outline: none;
}
.ne-pin-value-input:focus {
border-color: rgba(255, 255, 255, 0.3);
}
.ne-pin-value-checkbox {
width: 12px;
height: 12px;
accent-color: var(--ne-pin-bool);
}
.ne-pin-value-color {
width: 20px;
height: 12px;
padding: 0;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 2px;
cursor: pointer;
}

View File

@@ -0,0 +1,12 @@
/**
* Node Editor Styles Entry Point
* 节点编辑器样式入口
*/
@import './variables.css';
@import './Canvas.css';
@import './GraphNode.css';
@import './NodePin.css';
@import './Connection.css';
@import './ContextMenu.css';
@import './Dialog.css';

View File

@@ -0,0 +1,136 @@
/**
* Node Editor Theme Variables
* 节点编辑器主题变量
*/
:root {
/* ==================== Background Colors 背景颜色 ==================== */
--ne-canvas-bg: #141419;
--ne-canvas-grid: #1a1a22;
--ne-canvas-grid-major: #222230;
--ne-node-bg: rgba(12, 12, 16, 0.75);
--ne-node-bg-body: rgba(8, 8, 12, 0.85);
--ne-node-border: rgba(40, 40, 50, 0.6);
--ne-node-border-selected: #e5a020;
/* ==================== Node Category Colors 节点类别颜色 ==================== */
/* Event - Red gradient (like UE BeginOverlap) */
--ne-category-event: #b81c1c;
--ne-category-event-dark: #6b1010;
/* Function - Blue gradient */
--ne-category-function: #1b6eb5;
--ne-category-function-dark: #0d3d66;
/* Pure - Green/Teal gradient */
--ne-category-pure: #3d8b3d;
--ne-category-pure-dark: #1f5a1f;
/* Flow - Gray gradient */
--ne-category-flow: #4a4a4a;
--ne-category-flow-dark: #2a2a2a;
/* Variable - Purple gradient */
--ne-category-variable: #7b3d9b;
--ne-category-variable-dark: #4a1f66;
/* Literal - Gold/Orange gradient */
--ne-category-literal: #9a7020;
--ne-category-literal-dark: #5a4010;
/* Comment - Green gradient */
--ne-category-comment: #3d6b3d;
--ne-category-comment-dark: #2a4a2a;
/* Custom - Dark Gray gradient */
--ne-category-custom: #3a3a3a;
--ne-category-custom-dark: #1a1a1a;
/* ==================== Pin Type Colors 引脚类型颜色 ==================== */
--ne-pin-exec: #ffffff;
--ne-pin-bool: #8c0000;
--ne-pin-int: #1cc4c4;
--ne-pin-float: #7ecd32;
--ne-pin-string: #e060e0;
--ne-pin-vector2: #f0c030;
--ne-pin-vector3: #f0c030;
--ne-pin-vector4: #f0c030;
--ne-pin-color: #e08030;
--ne-pin-object: #00a0e0;
--ne-pin-array: #7030c0;
--ne-pin-map: #e060a0;
--ne-pin-struct: #3060c0;
--ne-pin-enum: #10a090;
--ne-pin-delegate: #c01030;
--ne-pin-any: #707070;
/* ==================== Connection Colors 连接线颜色 ==================== */
--ne-connection-exec: #ffffff;
--ne-connection-data: #888888;
--ne-connection-hover: #00aaff;
--ne-connection-selected: #ffcc00;
--ne-connection-invalid: #ff4444;
--ne-connection-preview: rgba(255, 255, 255, 0.5);
/* ==================== Text Colors 文字颜色 ==================== */
--ne-text-primary: #c0c0c0;
--ne-text-secondary: #909090;
--ne-text-muted: #606060;
--ne-text-title: #ffffff;
--ne-text-subtitle: #cccccc;
/* ==================== State Colors 状态颜色 ==================== */
--ne-state-success: #4caf50;
--ne-state-warning: #ff9800;
--ne-state-error: #f44336;
--ne-state-info: #2196f3;
--ne-state-running: #ffa726;
/* ==================== Shadow 阴影 ==================== */
--ne-shadow-node: 0 4px 12px rgba(0, 0, 0, 0.5),
0 0 0 1px rgba(0, 0, 0, 0.8),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
--ne-shadow-node-hover: 0 6px 16px rgba(0, 0, 0, 0.6);
--ne-shadow-node-selected: 0 0 0 2px var(--ne-node-border-selected),
0 0 20px rgba(245, 166, 35, 0.3);
--ne-shadow-pin: 0 0 4px rgba(0, 0, 0, 0.5);
--ne-shadow-pin-hover: 0 0 8px currentColor;
/* ==================== Sizing 尺寸 ==================== */
--ne-node-min-width: 200px;
--ne-node-max-width: 350px;
--ne-node-border-radius: 6px;
--ne-node-header-height: 28px;
--ne-node-padding: 8px 0;
--ne-pin-size: 10px;
--ne-pin-size-exec: 12px;
--ne-pin-spacing: 22px;
--ne-pin-hit-area: 24px;
--ne-connection-width: 2px;
--ne-connection-width-hover: 3px;
--ne-connection-width-exec: 3px;
/* ==================== Typography 字体 ==================== */
--ne-font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, Roboto, sans-serif;
--ne-font-family-mono: 'Consolas', 'Monaco', 'Courier New', monospace;
--ne-font-size-title: 12px;
--ne-font-size-body: 11px;
--ne-font-size-small: 10px;
/* ==================== Animation 动画 ==================== */
--ne-transition-fast: 0.1s ease;
--ne-transition-normal: 0.15s ease;
--ne-transition-slow: 0.3s ease;
/* ==================== Z-Index 层级 ==================== */
--ne-z-grid: 0;
--ne-z-connections: 1;
--ne-z-nodes: 2;
--ne-z-dragging: 100;
--ne-z-menu: 1000;
--ne-z-tooltip: 1001;
--ne-z-dialog: 2000;
}