mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-04 03:27:54 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ad66d8b7e |
+366
-46
@@ -30170,15 +30170,23 @@ html[data-theme="dark"] .form-group select {
|
|||||||
/* Workflow drag editor */
|
/* Workflow drag editor */
|
||||||
.workflow-page-content {
|
.workflow-page-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 280px minmax(0, 1fr) 320px;
|
grid-template-columns:
|
||||||
|
clamp(200px, 16vw, 260px)
|
||||||
|
minmax(0, 1fr)
|
||||||
|
clamp(220px, 20vw, 300px);
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
min-height: calc(100vh - 150px);
|
min-height: calc(100vh - 150px);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-sidebar,
|
.workflow-sidebar,
|
||||||
.workflow-properties,
|
.workflow-properties,
|
||||||
.workflow-main {
|
.workflow-main {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-panel,
|
.workflow-panel,
|
||||||
@@ -30218,31 +30226,139 @@ html[data-theme="dark"] .form-group select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.workflow-list-item {
|
.workflow-list-item {
|
||||||
display: grid;
|
display: flex;
|
||||||
gap: 4px;
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
padding: 8px 8px 8px 10px;
|
||||||
padding: 10px 12px;
|
|
||||||
border: 1px solid var(--border-color);
|
border: 1px solid var(--border-color);
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
background: var(--bg-primary);
|
background: var(--bg-primary);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list-item.is-active {
|
||||||
|
border-color: var(--accent-color);
|
||||||
|
background: rgba(59, 130, 246, 0.08);
|
||||||
|
box-shadow: inset 3px 0 0 var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list-item:hover {
|
||||||
|
border-color: rgba(59, 130, 246, 0.45);
|
||||||
|
background: rgba(59, 130, 246, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list-main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
display: grid;
|
||||||
|
gap: 3px;
|
||||||
|
text-align: left;
|
||||||
|
padding: 2px 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-list-item:hover,
|
.workflow-list-actions {
|
||||||
.workflow-list-item.is-active {
|
display: flex;
|
||||||
border-color: var(--accent-color);
|
align-items: center;
|
||||||
background: rgba(59, 130, 246, 0.10);
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-list-edit {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
width: 34px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch input {
|
||||||
|
opacity: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch-slider {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(100, 116, 139, 0.35);
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch-slider::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
left: 3px;
|
||||||
|
top: 3px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.18);
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch input:checked + .workflow-switch-slider {
|
||||||
|
background: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch input:checked + .workflow-switch-slider::before {
|
||||||
|
transform: translateX(14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch input:focus-visible + .workflow-switch-slider {
|
||||||
|
outline: 2px solid rgba(59, 130, 246, 0.45);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch--modal {
|
||||||
|
width: 42px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch--modal .workflow-switch-slider::before {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
top: 3px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-switch--modal input:checked + .workflow-switch-slider::before {
|
||||||
|
transform: translateX(18px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-list-title {
|
.workflow-list-title {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
font-size: 13px;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-list-meta {
|
.workflow-list-meta {
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
font-size: 12px;
|
font-size: 11px;
|
||||||
|
line-height: 1.3;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-node-palette {
|
.workflow-node-palette {
|
||||||
@@ -30269,54 +30385,223 @@ html[data-theme="dark"] .form-group select {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: auto minmax(520px, 1fr);
|
grid-template-rows: auto minmax(520px, 1fr);
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-meta-bar {
|
.workflow-meta-bar {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0;
|
||||||
|
padding: 8px 10px 8px 12px;
|
||||||
|
min-height: 44px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
box-shadow: inset 0 -1px 0 rgba(15, 23, 42, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-canvas-header {
|
||||||
|
min-width: 0;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-canvas-heading {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: center;
|
||||||
justify-content: space-between;
|
gap: 8px;
|
||||||
gap: 12px;
|
min-width: 0;
|
||||||
padding: 12px;
|
max-width: 100%;
|
||||||
|
padding: 5px 12px;
|
||||||
|
min-height: 32px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 7px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-meta-fields {
|
.workflow-canvas-title {
|
||||||
display: grid;
|
margin: 0;
|
||||||
grid-template-columns: minmax(140px, 220px) minmax(180px, 260px) minmax(200px, 1fr) auto;
|
font-size: 15px;
|
||||||
gap: 10px;
|
font-weight: 700;
|
||||||
flex: 1;
|
color: var(--text-primary);
|
||||||
align-items: end;
|
line-height: 1.3;
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-width: 40%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-meta-fields label {
|
.workflow-canvas-subtitle {
|
||||||
display: grid;
|
font-size: 12px;
|
||||||
gap: 5px;
|
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
|
line-height: 1.3;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-canvas-subtitle::before {
|
||||||
|
content: '·';
|
||||||
|
margin-right: 8px;
|
||||||
|
opacity: 0.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-canvas-title.is-disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content {
|
||||||
|
max-width: 340px;
|
||||||
|
width: calc(100vw - 32px);
|
||||||
|
margin: 12vh auto auto;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .modal-header.workflow-meta-modal-header {
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
min-height: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .modal-header.workflow-meta-modal-header h2 {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .modal-body.workflow-meta-modal-body {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 12px !important;
|
||||||
|
flex: 0 0 auto !important;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .modal-footer.workflow-meta-modal-footer {
|
||||||
|
padding: 6px 12px !important;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-field.form-group {
|
||||||
|
gap: 2px;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-field label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-field .form-input {
|
||||||
|
padding: 5px 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.3;
|
||||||
|
min-height: 0;
|
||||||
|
height: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-id-hint {
|
||||||
|
margin: 1px 0 0;
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-id-locked {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 30px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border: 1px dashed var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-id-locked[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-id-input[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-id-locked code {
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
word-break: break-all;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-id-group.is-locked .workflow-meta-id-hint {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-enable-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 0 0;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-meta-enable-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
line-height: 1.2;
|
||||||
|
|
||||||
.workflow-meta-fields input[type="text"] {
|
|
||||||
width: 100%;
|
|
||||||
padding: 8px 10px;
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 7px;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-enabled-toggle {
|
.workflow-meta-modal-content .workflow-switch--modal {
|
||||||
display: flex !important;
|
width: 36px;
|
||||||
align-items: center;
|
height: 20px;
|
||||||
gap: 8px !important;
|
}
|
||||||
white-space: nowrap;
|
|
||||||
padding-bottom: 8px;
|
.workflow-meta-modal-content .workflow-switch--modal .workflow-switch-slider::before {
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
top: 3px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-meta-modal-content .workflow-switch--modal input:checked + .workflow-switch-slider::before {
|
||||||
|
transform: translateX(16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-required {
|
||||||
|
color: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-toolbar {
|
.workflow-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: 8px;
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding-left: 12px;
|
||||||
|
border-left: 1px solid var(--border-color);
|
||||||
|
min-height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-toolbar .btn-small {
|
||||||
|
min-height: 30px;
|
||||||
|
padding: 5px 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-toolbar .active {
|
.workflow-toolbar .active {
|
||||||
@@ -30352,10 +30637,27 @@ html[data-theme="dark"] .form-group select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.workflow-properties {
|
.workflow-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 0;
|
||||||
|
align-self: stretch;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workflow-properties > .workflow-panel-header {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-properties > .workflow-property-empty,
|
||||||
|
.workflow-properties > .workflow-property-form {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
min-width: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.workflow-property-empty {
|
.workflow-property-empty {
|
||||||
margin: 14px;
|
margin: 14px;
|
||||||
padding: 18px;
|
padding: 18px;
|
||||||
@@ -30425,18 +30727,36 @@ html[data-theme="dark"] .form-group select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1400px) {
|
||||||
.workflow-page-content {
|
.workflow-page-content {
|
||||||
grid-template-columns: 240px minmax(0, 1fr);
|
grid-template-columns: clamp(180px, 14vw, 220px) minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-properties {
|
.workflow-properties {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
|
grid-row: 2;
|
||||||
|
max-height: min(42vh, 420px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.workflow-meta-bar,
|
.workflow-sidebar {
|
||||||
.workflow-meta-fields {
|
grid-row: 1;
|
||||||
display: grid;
|
}
|
||||||
grid-template-columns: 1fr;
|
|
||||||
|
.workflow-main {
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.workflow-page-content {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-sidebar {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workflow-main {
|
||||||
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2937,6 +2937,14 @@
|
|||||||
"metaName": "Name",
|
"metaName": "Name",
|
||||||
"metaDescription": "Description",
|
"metaDescription": "Description",
|
||||||
"metaEnabled": "Enabled",
|
"metaEnabled": "Enabled",
|
||||||
|
"metaIdHint": "Cannot be changed after creation; used for API and role binding",
|
||||||
|
"metaModalTitle": "Workflow details",
|
||||||
|
"editMeta": "Edit",
|
||||||
|
"untitled": "Untitled workflow",
|
||||||
|
"toggleEnabled": "Enable/disable",
|
||||||
|
"metaEnabledHint": "Disabled workflows cannot be auto-triggered by roles",
|
||||||
|
"enabledUpdated": "Enabled status updated",
|
||||||
|
"enabledUpdateFailed": "Failed to update enabled status",
|
||||||
"namePlaceholder": "Basic Web scan",
|
"namePlaceholder": "Basic Web scan",
|
||||||
"descriptionPlaceholder": "Optional",
|
"descriptionPlaceholder": "Optional",
|
||||||
"connect": "Connect",
|
"connect": "Connect",
|
||||||
|
|||||||
@@ -2925,6 +2925,14 @@
|
|||||||
"metaName": "名称",
|
"metaName": "名称",
|
||||||
"metaDescription": "描述",
|
"metaDescription": "描述",
|
||||||
"metaEnabled": "启用",
|
"metaEnabled": "启用",
|
||||||
|
"metaIdHint": "创建后不可修改,用于 API 与角色绑定",
|
||||||
|
"metaModalTitle": "流程信息",
|
||||||
|
"editMeta": "编辑",
|
||||||
|
"untitled": "未命名流程",
|
||||||
|
"toggleEnabled": "启用/禁用",
|
||||||
|
"metaEnabledHint": "禁用后无法被角色自动触发",
|
||||||
|
"enabledUpdated": "启用状态已更新",
|
||||||
|
"enabledUpdateFailed": "更新启用状态失败",
|
||||||
"namePlaceholder": "基础 Web 扫描",
|
"namePlaceholder": "基础 Web 扫描",
|
||||||
"descriptionPlaceholder": "可选",
|
"descriptionPlaceholder": "可选",
|
||||||
"connect": "连线",
|
"connect": "连线",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
'agent-md-modal',
|
'agent-md-modal',
|
||||||
'batch-manage-modal',
|
'batch-manage-modal',
|
||||||
'create-group-modal',
|
'create-group-modal',
|
||||||
|
'workflow-meta-modal',
|
||||||
'login-overlay',
|
'login-overlay',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
+243
-32
@@ -45,6 +45,8 @@
|
|||||||
|
|
||||||
const AGENT_MODES = ['eino_single', 'deep', 'plan_execute', 'supervisor'];
|
const AGENT_MODES = ['eino_single', 'deep', 'plan_execute', 'supervisor'];
|
||||||
|
|
||||||
|
const WORKFLOW_EDIT_ICON = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>';
|
||||||
|
|
||||||
function esc(text) {
|
function esc(text) {
|
||||||
if (typeof escapeHtml === 'function') return escapeHtml(text == null ? '' : String(text));
|
if (typeof escapeHtml === 'function') return escapeHtml(text == null ? '' : String(text));
|
||||||
return String(text == null ? '' : text)
|
return String(text == null ? '' : text)
|
||||||
@@ -191,6 +193,19 @@
|
|||||||
empty.style.display = cy.nodes().length ? 'none' : 'flex';
|
empty.style.display = cy.nodes().length ? 'none' : 'flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let workflowResizeObserver = null;
|
||||||
|
|
||||||
|
function setupWorkflowResizeObserver(container) {
|
||||||
|
if (workflowResizeObserver || typeof ResizeObserver === 'undefined' || !container) return;
|
||||||
|
workflowResizeObserver = new ResizeObserver(function () {
|
||||||
|
if (cy) cy.resize();
|
||||||
|
});
|
||||||
|
const canvasWrap = container.closest('.workflow-canvas-wrap');
|
||||||
|
const pageContent = container.closest('.workflow-page-content');
|
||||||
|
if (canvasWrap) workflowResizeObserver.observe(canvasWrap);
|
||||||
|
if (pageContent) workflowResizeObserver.observe(pageContent);
|
||||||
|
}
|
||||||
|
|
||||||
function initCy() {
|
function initCy() {
|
||||||
const container = document.getElementById('workflow-canvas');
|
const container = document.getElementById('workflow-canvas');
|
||||||
if (!container || typeof cytoscape !== 'function') return;
|
if (!container || typeof cytoscape !== 'function') return;
|
||||||
@@ -291,6 +306,7 @@
|
|||||||
deleteWorkflowSelection();
|
deleteWorkflowSelection();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setupWorkflowResizeObserver(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadWorkflows(includeDisabled) {
|
async function loadWorkflows(includeDisabled) {
|
||||||
@@ -329,6 +345,79 @@
|
|||||||
return workflowToolOptions;
|
return workflowToolOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readWorkflowMetaFromForm() {
|
||||||
|
const idEl = document.getElementById('workflow-id');
|
||||||
|
const nameEl = document.getElementById('workflow-name');
|
||||||
|
const descEl = document.getElementById('workflow-description');
|
||||||
|
const enabledEl = document.getElementById('workflow-enabled');
|
||||||
|
return {
|
||||||
|
id: idEl ? idEl.value.trim() : '',
|
||||||
|
name: nameEl ? nameEl.value.trim() : '',
|
||||||
|
description: descEl ? descEl.value.trim() : '',
|
||||||
|
enabled: enabledEl ? enabledEl.checked : true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWorkflowCanvasTitle() {
|
||||||
|
const titleEl = document.getElementById('workflow-canvas-title');
|
||||||
|
const subtitleEl = document.getElementById('workflow-canvas-subtitle');
|
||||||
|
if (!titleEl) return;
|
||||||
|
const meta = readWorkflowMetaFromForm();
|
||||||
|
const wf = workflows.find(item => item.id === currentWorkflowId);
|
||||||
|
if (!meta.name && !meta.id) {
|
||||||
|
titleEl.textContent = _t('workflows.untitled');
|
||||||
|
} else {
|
||||||
|
titleEl.textContent = meta.name || meta.id;
|
||||||
|
}
|
||||||
|
titleEl.classList.toggle('is-disabled', !meta.enabled);
|
||||||
|
titleEl.title = meta.description || '';
|
||||||
|
if (subtitleEl) {
|
||||||
|
const parts = [];
|
||||||
|
if (meta.id) parts.push(meta.id);
|
||||||
|
if (wf && wf.version) parts.push(`v${wf.version}`);
|
||||||
|
parts.push(meta.enabled ? _t('workflows.statusEnabled') : _t('workflows.statusDisabled'));
|
||||||
|
subtitleEl.textContent = parts.join(' · ');
|
||||||
|
subtitleEl.hidden = !parts.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncWorkflowMetaIdField(locked, id) {
|
||||||
|
const idEl = document.getElementById('workflow-id');
|
||||||
|
const lockedEl = document.getElementById('workflow-id-locked');
|
||||||
|
const displayEl = document.getElementById('workflow-id-display');
|
||||||
|
const hintEl = document.querySelector('.workflow-meta-id-hint');
|
||||||
|
const idGroup = document.getElementById('workflow-meta-id-group');
|
||||||
|
if (!idEl) return;
|
||||||
|
idEl.value = id || '';
|
||||||
|
if (locked) {
|
||||||
|
idEl.hidden = true;
|
||||||
|
idEl.disabled = true;
|
||||||
|
if (lockedEl) lockedEl.hidden = false;
|
||||||
|
if (displayEl) displayEl.textContent = id || '';
|
||||||
|
if (hintEl) hintEl.hidden = true;
|
||||||
|
if (idGroup) idGroup.classList.add('is-locked');
|
||||||
|
} else {
|
||||||
|
idEl.hidden = false;
|
||||||
|
idEl.disabled = false;
|
||||||
|
if (lockedEl) lockedEl.hidden = true;
|
||||||
|
if (displayEl) displayEl.textContent = '';
|
||||||
|
if (hintEl) hintEl.hidden = false;
|
||||||
|
if (idGroup) idGroup.classList.remove('is-locked');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncWorkflowMetaForm(wf) {
|
||||||
|
const nameEl = document.getElementById('workflow-name');
|
||||||
|
const descEl = document.getElementById('workflow-description');
|
||||||
|
const enabledEl = document.getElementById('workflow-enabled');
|
||||||
|
if (!nameEl || !descEl || !enabledEl) return;
|
||||||
|
syncWorkflowMetaIdField(!!wf.id, wf.id || '');
|
||||||
|
nameEl.value = wf.name || '';
|
||||||
|
descEl.value = wf.description || '';
|
||||||
|
enabledEl.checked = wf.enabled !== false;
|
||||||
|
updateWorkflowCanvasTitle();
|
||||||
|
}
|
||||||
|
|
||||||
function renderWorkflowList() {
|
function renderWorkflowList() {
|
||||||
const list = document.getElementById('workflow-list');
|
const list = document.getElementById('workflow-list');
|
||||||
if (!list) return;
|
if (!list) return;
|
||||||
@@ -336,12 +425,28 @@
|
|||||||
list.innerHTML = '<div class="empty-state">' + esc(_t('workflows.emptyList')) + '</div>';
|
list.innerHTML = '<div class="empty-state">' + esc(_t('workflows.emptyList')) + '</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
list.innerHTML = workflows.map(wf => `
|
list.innerHTML = workflows.map(wf => {
|
||||||
<button type="button" class="workflow-list-item ${wf.id === currentWorkflowId ? 'is-active' : ''}" onclick="selectWorkflow(decodeURIComponent('${encodeURIComponent(wf.id)}'))">
|
const encodedId = encodeURIComponent(wf.id);
|
||||||
<span class="workflow-list-title">${esc(wf.name || wf.id)}</span>
|
const isActive = wf.id === currentWorkflowId;
|
||||||
<span class="workflow-list-meta">${esc(wf.id)} · v${wf.version || 1} · ${wf.enabled ? esc(_t('workflows.statusEnabled')) : esc(_t('workflows.statusDisabled'))}</span>
|
const toggleTitle = esc(_t('workflows.toggleEnabled'));
|
||||||
</button>
|
const editTitle = esc(_t('workflows.editMeta'));
|
||||||
`).join('');
|
const enabled = wf.enabled !== false;
|
||||||
|
return `
|
||||||
|
<div class="workflow-list-item ${isActive ? 'is-active' : ''}">
|
||||||
|
<button type="button" class="workflow-list-main" onclick="selectWorkflow(decodeURIComponent('${encodedId}'))">
|
||||||
|
<span class="workflow-list-title">${esc(wf.name || wf.id)}</span>
|
||||||
|
<span class="workflow-list-meta">${esc(wf.id)} · v${wf.version || 1}</span>
|
||||||
|
</button>
|
||||||
|
<div class="workflow-list-actions">
|
||||||
|
<label class="workflow-switch" title="${toggleTitle}" onclick="event.stopPropagation()">
|
||||||
|
<input type="checkbox" ${enabled ? 'checked' : ''} aria-label="${toggleTitle}" onchange="toggleWorkflowEnabled(decodeURIComponent('${encodedId}'), this.checked)">
|
||||||
|
<span class="workflow-switch-slider" aria-hidden="true"></span>
|
||||||
|
</label>
|
||||||
|
<button type="button" class="btn-icon workflow-list-edit" title="${editTitle}" aria-label="${editTitle}" onclick="event.stopPropagation(); editWorkflowFromList(decodeURIComponent('${encodedId}'))">${WORKFLOW_EDIT_ICON}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextNodeId(type) {
|
function nextNodeId(type) {
|
||||||
@@ -372,19 +477,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fillWorkflowForm(wf) {
|
function fillWorkflowForm(wf) {
|
||||||
|
const data = wf || {};
|
||||||
|
syncWorkflowMetaForm(data);
|
||||||
|
currentWorkflowId = data.id ? data.id : '';
|
||||||
initCy();
|
initCy();
|
||||||
const idEl = document.getElementById('workflow-id');
|
if (!cy) return;
|
||||||
const nameEl = document.getElementById('workflow-name');
|
const graph = parseGraph(data.graph_json || data.graph || defaultGraph());
|
||||||
const descEl = document.getElementById('workflow-description');
|
|
||||||
const enabledEl = document.getElementById('workflow-enabled');
|
|
||||||
if (!idEl || !nameEl || !descEl || !enabledEl || !cy) return;
|
|
||||||
idEl.value = wf.id || '';
|
|
||||||
idEl.disabled = !!wf.id;
|
|
||||||
nameEl.value = wf.name || '';
|
|
||||||
descEl.value = wf.description || '';
|
|
||||||
enabledEl.checked = wf.enabled !== false;
|
|
||||||
currentWorkflowId = wf.id || '';
|
|
||||||
const graph = parseGraph(wf.graph_json || wf.graph || defaultGraph());
|
|
||||||
resetSequences(graph);
|
resetSequences(graph);
|
||||||
cy.elements().remove();
|
cy.elements().remove();
|
||||||
cy.add(graphToElements(graph));
|
cy.add(graphToElements(graph));
|
||||||
@@ -411,8 +509,6 @@
|
|||||||
if (deleteBtn) deleteBtn.hidden = true;
|
if (deleteBtn) deleteBtn.hidden = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cy.elements().unselect();
|
|
||||||
selectedElement.select();
|
|
||||||
empty.hidden = true;
|
empty.hidden = true;
|
||||||
form.hidden = false;
|
form.hidden = false;
|
||||||
if (title) title.textContent = selectedElement.isNode() ? _t('workflows.nodeProperties') : _t('workflows.edgeProperties');
|
if (title) title.textContent = selectedElement.isNode() ? _t('workflows.nodeProperties') : _t('workflows.edgeProperties');
|
||||||
@@ -420,6 +516,8 @@
|
|||||||
deleteBtn.hidden = false;
|
deleteBtn.hidden = false;
|
||||||
deleteBtn.textContent = selectedElement.isNode() ? _t('workflows.deleteNode') : _t('workflows.deleteEdge');
|
deleteBtn.textContent = selectedElement.isNode() ? _t('workflows.deleteNode') : _t('workflows.deleteEdge');
|
||||||
}
|
}
|
||||||
|
cy.elements().unselect();
|
||||||
|
selectedElement.select();
|
||||||
const typeWrap = document.getElementById('workflow-prop-type-wrap');
|
const typeWrap = document.getElementById('workflow-prop-type-wrap');
|
||||||
const label = document.getElementById('workflow-prop-label');
|
const label = document.getElementById('workflow-prop-label');
|
||||||
const type = document.getElementById('workflow-prop-type');
|
const type = document.getElementById('workflow-prop-type');
|
||||||
@@ -699,12 +797,18 @@
|
|||||||
if (list) list.innerHTML = '<div class="loading-spinner">' + esc(_t('common.loading')) + '</div>';
|
if (list) list.innerHTML = '<div class="loading-spinner">' + esc(_t('common.loading')) + '</div>';
|
||||||
try {
|
try {
|
||||||
await loadWorkflows(true);
|
await loadWorkflows(true);
|
||||||
renderWorkflowList();
|
if (currentWorkflowId) {
|
||||||
if (!currentWorkflowId && workflows.length) {
|
const wf = workflows.find(item => item.id === currentWorkflowId);
|
||||||
|
if (wf) {
|
||||||
|
syncWorkflowMetaForm(wf);
|
||||||
|
}
|
||||||
|
} else if (workflows.length) {
|
||||||
fillWorkflowForm(workflows[0]);
|
fillWorkflowForm(workflows[0]);
|
||||||
} else if (!workflows.length) {
|
} else {
|
||||||
newWorkflowDraft();
|
newWorkflowDraft();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
renderWorkflowList();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (list) list.innerHTML = `<div class="empty-state">${esc(error.message)}</div>`;
|
if (list) list.innerHTML = `<div class="empty-state">${esc(error.message)}</div>`;
|
||||||
if (typeof showNotification === 'function') showNotification(error.message, 'error');
|
if (typeof showNotification === 'function') showNotification(error.message, 'error');
|
||||||
@@ -712,6 +816,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.newWorkflowDraft = function () {
|
window.newWorkflowDraft = function () {
|
||||||
|
currentWorkflowId = '';
|
||||||
fillWorkflowForm({
|
fillWorkflowForm({
|
||||||
id: '',
|
id: '',
|
||||||
name: '',
|
name: '',
|
||||||
@@ -719,6 +824,8 @@
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
graph_json: defaultGraph()
|
graph_json: defaultGraph()
|
||||||
});
|
});
|
||||||
|
syncWorkflowMetaIdField(false, '');
|
||||||
|
openWorkflowMetaModal();
|
||||||
};
|
};
|
||||||
|
|
||||||
window.selectWorkflow = function (id) {
|
window.selectWorkflow = function (id) {
|
||||||
@@ -726,6 +833,100 @@
|
|||||||
if (wf) fillWorkflowForm(wf);
|
if (wf) fillWorkflowForm(wf);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
window.openWorkflowMetaModal = function () {
|
||||||
|
const nameEl = document.getElementById('workflow-name');
|
||||||
|
const idEl = document.getElementById('workflow-id');
|
||||||
|
if (currentWorkflowId) {
|
||||||
|
syncWorkflowMetaIdField(true, currentWorkflowId);
|
||||||
|
} else {
|
||||||
|
syncWorkflowMetaIdField(false, idEl ? idEl.value.trim() : '');
|
||||||
|
}
|
||||||
|
if (typeof openAppModal === 'function') {
|
||||||
|
openAppModal('workflow-meta-modal', {
|
||||||
|
focusEl: currentWorkflowId ? nameEl : (idEl && !idEl.hidden ? idEl : nameEl)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.closeWorkflowMetaModal = function () {
|
||||||
|
if (typeof closeAppModal === 'function') {
|
||||||
|
closeAppModal('workflow-meta-modal');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.applyWorkflowMetaModal = function () {
|
||||||
|
const meta = readWorkflowMetaFromForm();
|
||||||
|
if (!meta.id || !meta.name) {
|
||||||
|
if (typeof showNotification === 'function') {
|
||||||
|
showNotification(_t('workflows.idNameRequired'), 'error');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateWorkflowCanvasTitle();
|
||||||
|
renderWorkflowList();
|
||||||
|
closeWorkflowMetaModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.editWorkflowFromList = function (id) {
|
||||||
|
if (id !== currentWorkflowId) {
|
||||||
|
selectWorkflow(id);
|
||||||
|
}
|
||||||
|
openWorkflowMetaModal();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.toggleWorkflowEnabled = async function (id, enabled) {
|
||||||
|
const wf = workflows.find(item => item.id === id);
|
||||||
|
if (!wf) return;
|
||||||
|
const previous = wf.enabled !== false;
|
||||||
|
wf.enabled = enabled;
|
||||||
|
if (id === currentWorkflowId) {
|
||||||
|
const enabledEl = document.getElementById('workflow-enabled');
|
||||||
|
if (enabledEl) enabledEl.checked = enabled;
|
||||||
|
updateWorkflowCanvasTitle();
|
||||||
|
}
|
||||||
|
renderWorkflowList();
|
||||||
|
let graph = defaultGraph();
|
||||||
|
if (id === currentWorkflowId && cy) {
|
||||||
|
graph = elementsToGraph();
|
||||||
|
} else {
|
||||||
|
graph = parseGraph(wf.graph_json || wf.graph || defaultGraph());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await apiFetch(`/api/workflows/${encodeURIComponent(id)}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
id: wf.id,
|
||||||
|
name: wf.name,
|
||||||
|
description: wf.description || '',
|
||||||
|
enabled,
|
||||||
|
graph
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const err = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(err.error || _t('workflows.enabledUpdateFailed'));
|
||||||
|
}
|
||||||
|
if (typeof showNotification === 'function') {
|
||||||
|
showNotification(_t('workflows.enabledUpdated'), 'success');
|
||||||
|
}
|
||||||
|
if (typeof loadWorkflowOptionsForRoleModal === 'function') {
|
||||||
|
await loadWorkflowOptionsForRoleModal();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
wf.enabled = previous;
|
||||||
|
if (id === currentWorkflowId) {
|
||||||
|
const enabledEl = document.getElementById('workflow-enabled');
|
||||||
|
if (enabledEl) enabledEl.checked = previous;
|
||||||
|
updateWorkflowCanvasTitle();
|
||||||
|
}
|
||||||
|
renderWorkflowList();
|
||||||
|
if (typeof showNotification === 'function') {
|
||||||
|
showNotification(error.message || _t('workflows.enabledUpdateFailed'), 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function validateWorkflowGraph(graph) {
|
function validateWorkflowGraph(graph) {
|
||||||
const errors = [];
|
const errors = [];
|
||||||
const nodes = graph.nodes || [];
|
const nodes = graph.nodes || [];
|
||||||
@@ -772,12 +973,12 @@
|
|||||||
|
|
||||||
window.saveWorkflowDraft = async function () {
|
window.saveWorkflowDraft = async function () {
|
||||||
initCy();
|
initCy();
|
||||||
const id = document.getElementById('workflow-id').value.trim();
|
const meta = readWorkflowMetaFromForm();
|
||||||
const name = document.getElementById('workflow-name').value.trim();
|
if (!meta.id || !meta.name) {
|
||||||
const description = document.getElementById('workflow-description').value.trim();
|
if (typeof showNotification === 'function') {
|
||||||
const enabled = document.getElementById('workflow-enabled').checked;
|
showNotification(_t('workflows.idNameRequired'), 'error');
|
||||||
if (!id || !name) {
|
}
|
||||||
showNotification(_t('workflows.idNameRequired'), 'error');
|
openWorkflowMetaModal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const graph = elementsToGraph();
|
const graph = elementsToGraph();
|
||||||
@@ -791,7 +992,13 @@
|
|||||||
const response = await apiFetch(url, {
|
const response = await apiFetch(url, {
|
||||||
method,
|
method,
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ id, name, description, enabled, graph })
|
body: JSON.stringify({
|
||||||
|
id: meta.id,
|
||||||
|
name: meta.name,
|
||||||
|
description: meta.description,
|
||||||
|
enabled: meta.enabled,
|
||||||
|
graph
|
||||||
|
})
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const err = await response.json().catch(() => ({}));
|
const err = await response.json().catch(() => ({}));
|
||||||
@@ -799,7 +1006,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
currentWorkflowId = data.workflow && data.workflow.id ? data.workflow.id : id;
|
currentWorkflowId = data.workflow && data.workflow.id ? data.workflow.id : meta.id;
|
||||||
|
syncWorkflowMetaIdField(true, currentWorkflowId);
|
||||||
|
closeWorkflowMetaModal();
|
||||||
showNotification(_t('workflows.saved'), 'success');
|
showNotification(_t('workflows.saved'), 'success');
|
||||||
await refreshWorkflows();
|
await refreshWorkflows();
|
||||||
if (typeof loadWorkflowOptionsForRoleModal === 'function') {
|
if (typeof loadWorkflowOptionsForRoleModal === 'function') {
|
||||||
@@ -808,7 +1017,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
window.deleteCurrentWorkflow = async function () {
|
window.deleteCurrentWorkflow = async function () {
|
||||||
const id = currentWorkflowId || document.getElementById('workflow-id').value.trim();
|
const meta = readWorkflowMetaFromForm();
|
||||||
|
const id = currentWorkflowId || meta.id;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
showNotification(_t('workflows.selectToDelete'), 'warning');
|
showNotification(_t('workflows.selectToDelete'), 'warning');
|
||||||
return;
|
return;
|
||||||
@@ -984,6 +1194,7 @@
|
|||||||
connectBtn.textContent = connectMode ? _t('workflows.connecting') : _t('workflows.connect');
|
connectBtn.textContent = connectMode ? _t('workflows.connecting') : _t('workflows.connect');
|
||||||
}
|
}
|
||||||
refreshCanvasLabels();
|
refreshCanvasLabels();
|
||||||
|
updateWorkflowCanvasTitle();
|
||||||
renderWorkflowList();
|
renderWorkflowList();
|
||||||
if (selectedElement && selectedElement.length) {
|
if (selectedElement && selectedElement.length) {
|
||||||
selectWorkflowElement(selectedElement);
|
selectWorkflowElement(selectedElement);
|
||||||
|
|||||||
@@ -2583,11 +2583,11 @@
|
|||||||
</aside>
|
</aside>
|
||||||
<main class="workflow-main">
|
<main class="workflow-main">
|
||||||
<section class="workflow-meta-bar">
|
<section class="workflow-meta-bar">
|
||||||
<div class="workflow-meta-fields">
|
<div class="workflow-canvas-header">
|
||||||
<label><span data-i18n="workflows.metaId">ID</span> <input type="text" id="workflow-id" placeholder="web-scan-basic" autocomplete="off"></label>
|
<div class="workflow-canvas-heading">
|
||||||
<label><span data-i18n="workflows.metaName">名称</span> <input type="text" id="workflow-name" data-i18n="workflows.namePlaceholder" data-i18n-attr="placeholder" placeholder="基础 Web 扫描" autocomplete="off"></label>
|
<h3 id="workflow-canvas-title" class="workflow-canvas-title" data-i18n="workflows.untitled">未命名流程</h3>
|
||||||
<label><span data-i18n="workflows.metaDescription">描述</span> <input type="text" id="workflow-description" data-i18n="workflows.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="可选" autocomplete="off"></label>
|
<span id="workflow-canvas-subtitle" class="workflow-canvas-subtitle" hidden></span>
|
||||||
<label class="workflow-enabled-toggle"><input type="checkbox" id="workflow-enabled" checked> <span data-i18n="workflows.metaEnabled">启用</span></label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="workflow-toolbar">
|
<div class="workflow-toolbar">
|
||||||
<button class="btn-secondary btn-small" type="button" onclick="toggleWorkflowConnectMode()" id="workflow-connect-btn" data-i18n="workflows.connect">连线</button>
|
<button class="btn-secondary btn-small" type="button" onclick="toggleWorkflowConnectMode()" id="workflow-connect-btn" data-i18n="workflows.connect">连线</button>
|
||||||
@@ -4037,6 +4037,44 @@
|
|||||||
window.ELK = elk;
|
window.ELK = elk;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<!-- 图编排流程信息 -->
|
||||||
|
<div id="workflow-meta-modal" class="modal" style="display: none;">
|
||||||
|
<div class="modal-content workflow-meta-modal-content">
|
||||||
|
<div class="modal-header workflow-meta-modal-header">
|
||||||
|
<h2 data-i18n="workflows.metaModalTitle">流程信息</h2>
|
||||||
|
<span class="modal-close" onclick="closeWorkflowMetaModal()">×</span>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body workflow-meta-modal-body">
|
||||||
|
<div class="form-group workflow-meta-field">
|
||||||
|
<label for="workflow-name"><span data-i18n="workflows.metaName">名称</span> <span class="form-required">*</span></label>
|
||||||
|
<input type="text" id="workflow-name" class="form-input" data-i18n="workflows.namePlaceholder" data-i18n-attr="placeholder" placeholder="基础 Web 扫描" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="form-group workflow-meta-field workflow-meta-id-group" id="workflow-meta-id-group">
|
||||||
|
<label for="workflow-id"><span data-i18n="workflows.metaId">ID</span> <span class="form-required">*</span></label>
|
||||||
|
<div id="workflow-id-locked" class="workflow-meta-id-locked" hidden>
|
||||||
|
<code id="workflow-id-display"></code>
|
||||||
|
</div>
|
||||||
|
<input type="text" id="workflow-id" class="form-input workflow-meta-id-input" placeholder="web-scan-basic" autocomplete="off">
|
||||||
|
<small class="workflow-meta-id-hint form-hint" data-i18n="workflows.metaIdHint">创建后不可修改,用于 API 与角色绑定</small>
|
||||||
|
</div>
|
||||||
|
<div class="form-group workflow-meta-field">
|
||||||
|
<label for="workflow-description" data-i18n="workflows.metaDescription">描述</label>
|
||||||
|
<input type="text" id="workflow-description" class="form-input" data-i18n="workflows.descriptionPlaceholder" data-i18n-attr="placeholder" placeholder="可选" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
<div class="workflow-meta-enable-row">
|
||||||
|
<span class="workflow-meta-enable-label" data-i18n="workflows.metaEnabled">启用</span>
|
||||||
|
<label class="workflow-switch workflow-switch--modal" data-i18n="workflows.metaEnabledHint" data-i18n-attr="title" title="禁用后无法被角色自动触发">
|
||||||
|
<input type="checkbox" id="workflow-enabled" checked>
|
||||||
|
<span class="workflow-switch-slider" aria-hidden="true"></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer workflow-meta-modal-footer">
|
||||||
|
<button type="button" class="btn-secondary btn-small" onclick="closeWorkflowMetaModal()" data-i18n="common.cancel">取消</button>
|
||||||
|
<button type="button" class="btn-primary btn-small" onclick="applyWorkflowMetaModal()" data-i18n="common.confirm">确认</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- 知识项编辑模态框 -->
|
<!-- 知识项编辑模态框 -->
|
||||||
<!-- Skill模态框 -->
|
<!-- Skill模态框 -->
|
||||||
<div id="skill-modal" class="modal">
|
<div id="skill-modal" class="modal">
|
||||||
|
|||||||
Reference in New Issue
Block a user