mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-10 14:28:43 +02:00
Add files via upload
This commit is contained in:
+216
-1
@@ -23,6 +23,9 @@
|
||||
let selectedElement = null;
|
||||
let workflowToolOptions = [];
|
||||
let workflowToolsLoaded = false;
|
||||
const WORKFLOW_TOOL_SELECT_ID = 'workflow-tool-name';
|
||||
let workflowToolSelectRegistry = null;
|
||||
let workflowToolSelectDocBound = false;
|
||||
|
||||
const KNOWN_NODE_LABELS = {
|
||||
start: ['开始', 'Start'],
|
||||
@@ -359,6 +362,217 @@
|
||||
return workflowToolOptions;
|
||||
}
|
||||
|
||||
function workflowToolOptionLabel(tool) {
|
||||
return tool.key + (tool.enabled ? '' : _t('workflows.config.toolDisabled'));
|
||||
}
|
||||
|
||||
function closeWorkflowToolSelect() {
|
||||
const reg = workflowToolSelectRegistry;
|
||||
if (!reg || !reg.wrapper) return;
|
||||
reg.wrapper.classList.remove('open');
|
||||
if (reg.trigger) reg.trigger.setAttribute('aria-expanded', 'false');
|
||||
if (reg.searchInput) reg.searchInput.value = '';
|
||||
}
|
||||
|
||||
function createWorkflowToolOptionButton(value, label, selectedValue) {
|
||||
const item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'workflow-tool-select-option';
|
||||
item.setAttribute('role', 'option');
|
||||
item.setAttribute('data-value', value);
|
||||
item.title = label;
|
||||
if (value === selectedValue) {
|
||||
item.classList.add('is-selected');
|
||||
item.setAttribute('aria-selected', 'true');
|
||||
} else {
|
||||
item.setAttribute('aria-selected', 'false');
|
||||
}
|
||||
const check = document.createElement('span');
|
||||
check.className = 'workflow-tool-select-check';
|
||||
check.setAttribute('aria-hidden', 'true');
|
||||
check.textContent = '✓';
|
||||
const labelEl = document.createElement('span');
|
||||
labelEl.className = 'workflow-tool-select-label';
|
||||
labelEl.textContent = label;
|
||||
labelEl.title = label;
|
||||
item.appendChild(check);
|
||||
item.appendChild(labelEl);
|
||||
return item;
|
||||
}
|
||||
|
||||
function renderWorkflowToolSelectOptions(reg, query) {
|
||||
const { select, optionsList } = reg;
|
||||
optionsList.innerHTML = '';
|
||||
const q = (query || '').trim().toLowerCase();
|
||||
let matchCount = 0;
|
||||
|
||||
Array.prototype.forEach.call(select.options, (opt) => {
|
||||
if (opt.value === '') {
|
||||
if (!q) {
|
||||
optionsList.appendChild(createWorkflowToolOptionButton(opt.value, opt.textContent || '', select.value));
|
||||
}
|
||||
return;
|
||||
}
|
||||
const label = opt.textContent || opt.value;
|
||||
if (q && !label.toLowerCase().includes(q) && !opt.value.toLowerCase().includes(q)) return;
|
||||
matchCount += 1;
|
||||
optionsList.appendChild(createWorkflowToolOptionButton(opt.value, label, select.value));
|
||||
});
|
||||
|
||||
if (matchCount === 0) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'workflow-tool-select-empty';
|
||||
empty.textContent = q
|
||||
? _t('workflows.config.noToolsFound')
|
||||
: _t('workflows.config.noToolsAvailable');
|
||||
optionsList.appendChild(empty);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureWorkflowToolSearchUi(reg) {
|
||||
if (reg.searchInput && reg.optionsList) return;
|
||||
const { dropdown } = reg;
|
||||
dropdown.innerHTML = '';
|
||||
|
||||
const searchWrap = document.createElement('div');
|
||||
searchWrap.className = 'workflow-tool-select-search';
|
||||
const searchInput = document.createElement('input');
|
||||
searchInput.type = 'search';
|
||||
searchInput.className = 'workflow-tool-select-search-input';
|
||||
searchInput.setAttribute('autocomplete', 'off');
|
||||
searchInput.setAttribute('data-i18n', 'workflows.config.searchTool');
|
||||
searchInput.setAttribute('data-i18n-attr', 'placeholder');
|
||||
searchInput.placeholder = _t('workflows.config.searchTool');
|
||||
searchWrap.appendChild(searchInput);
|
||||
dropdown.appendChild(searchWrap);
|
||||
reg.searchInput = searchInput;
|
||||
|
||||
const optionsList = document.createElement('div');
|
||||
optionsList.className = 'workflow-tool-select-options';
|
||||
dropdown.appendChild(optionsList);
|
||||
reg.optionsList = optionsList;
|
||||
|
||||
searchInput.addEventListener('input', () => renderWorkflowToolSelectOptions(reg, searchInput.value));
|
||||
searchInput.addEventListener('click', (e) => e.stopPropagation());
|
||||
searchInput.addEventListener('keydown', (e) => {
|
||||
e.stopPropagation();
|
||||
if (e.key === 'Escape') closeWorkflowToolSelect();
|
||||
});
|
||||
}
|
||||
|
||||
function syncWorkflowToolSelect() {
|
||||
const select = document.getElementById(WORKFLOW_TOOL_SELECT_ID);
|
||||
const reg = workflowToolSelectRegistry;
|
||||
if (!select || !reg || reg.select !== select) return;
|
||||
const selected = select.options[select.selectedIndex];
|
||||
reg.valueSpan.textContent = selected && selected.value
|
||||
? selected.textContent
|
||||
: _t('workflows.config.selectTool');
|
||||
if (reg.optionsList) {
|
||||
renderWorkflowToolSelectOptions(reg, reg.searchInput ? reg.searchInput.value : '');
|
||||
}
|
||||
reg.trigger.disabled = !!select.disabled;
|
||||
reg.wrapper.classList.toggle('is-disabled', !!select.disabled);
|
||||
}
|
||||
|
||||
function enhanceWorkflowToolSelect() {
|
||||
const select = document.getElementById(WORKFLOW_TOOL_SELECT_ID);
|
||||
if (!select) {
|
||||
workflowToolSelectRegistry = null;
|
||||
return;
|
||||
}
|
||||
if (select.dataset.workflowToolCustom === '1' && workflowToolSelectRegistry && workflowToolSelectRegistry.select === select) {
|
||||
syncWorkflowToolSelect();
|
||||
return;
|
||||
}
|
||||
workflowToolSelectRegistry = null;
|
||||
|
||||
select.dataset.workflowToolCustom = '1';
|
||||
select.classList.add('workflow-tool-native-select');
|
||||
select.tabIndex = -1;
|
||||
select.setAttribute('aria-hidden', 'true');
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'workflow-tool-select';
|
||||
|
||||
const trigger = document.createElement('button');
|
||||
trigger.type = 'button';
|
||||
trigger.className = 'workflow-tool-select-trigger';
|
||||
trigger.setAttribute('aria-haspopup', 'listbox');
|
||||
trigger.setAttribute('aria-expanded', 'false');
|
||||
const valueSpan = document.createElement('span');
|
||||
valueSpan.className = 'workflow-tool-select-value';
|
||||
trigger.appendChild(valueSpan);
|
||||
const caret = document.createElement('span');
|
||||
caret.className = 'workflow-tool-select-caret';
|
||||
caret.setAttribute('aria-hidden', 'true');
|
||||
caret.textContent = '▾';
|
||||
trigger.appendChild(caret);
|
||||
|
||||
const dropdown = document.createElement('div');
|
||||
dropdown.className = 'workflow-tool-select-dropdown';
|
||||
dropdown.setAttribute('role', 'listbox');
|
||||
|
||||
const parent = select.parentNode;
|
||||
parent.insertBefore(wrapper, select);
|
||||
wrapper.appendChild(trigger);
|
||||
wrapper.appendChild(dropdown);
|
||||
wrapper.appendChild(select);
|
||||
|
||||
workflowToolSelectRegistry = {
|
||||
wrapper,
|
||||
trigger,
|
||||
dropdown,
|
||||
select,
|
||||
valueSpan,
|
||||
searchInput: null,
|
||||
optionsList: null
|
||||
};
|
||||
|
||||
trigger.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
if (select.disabled) return;
|
||||
const open = wrapper.classList.contains('open');
|
||||
closeWorkflowToolSelect();
|
||||
if (!open) {
|
||||
wrapper.classList.add('open');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
ensureWorkflowToolSearchUi(workflowToolSelectRegistry);
|
||||
if (workflowToolSelectRegistry.searchInput) {
|
||||
workflowToolSelectRegistry.searchInput.value = '';
|
||||
renderWorkflowToolSelectOptions(workflowToolSelectRegistry, '');
|
||||
requestAnimationFrame(() => workflowToolSelectRegistry.searchInput.focus());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dropdown.addEventListener('click', (e) => {
|
||||
const opt = e.target.closest('.workflow-tool-select-option');
|
||||
if (!opt) return;
|
||||
e.stopPropagation();
|
||||
const val = opt.getAttribute('data-value');
|
||||
if (val === null) return;
|
||||
if (select.value !== val) {
|
||||
select.value = val;
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
closeWorkflowToolSelect();
|
||||
syncWorkflowToolSelect();
|
||||
});
|
||||
|
||||
select.addEventListener('change', () => syncWorkflowToolSelect());
|
||||
|
||||
if (!workflowToolSelectDocBound) {
|
||||
workflowToolSelectDocBound = true;
|
||||
document.addEventListener('click', closeWorkflowToolSelect);
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape') closeWorkflowToolSelect();
|
||||
});
|
||||
}
|
||||
|
||||
syncWorkflowToolSelect();
|
||||
}
|
||||
|
||||
function readWorkflowMetaFromForm() {
|
||||
const idEl = document.getElementById('workflow-id');
|
||||
const nameEl = document.getElementById('workflow-name');
|
||||
@@ -644,7 +858,7 @@
|
||||
wrap.innerHTML = `
|
||||
${joinStrategyHtml(cfg)}
|
||||
<div class="form-group">
|
||||
<label for="workflow-tool-name">${esc(_t('workflows.config.mcpTool'))}</label>
|
||||
<label>${esc(_t('workflows.config.mcpTool'))}</label>
|
||||
<select id="workflow-tool-name" onchange="updateWorkflowTypedConfig()">
|
||||
<option value="">${esc(_t('workflows.config.selectTool'))}</option>
|
||||
${workflowToolOptions.map(tool => `<option value="${esc(tool.key)}" ${tool.key === cfg.tool_name ? 'selected' : ''}>${esc(tool.key)}${tool.enabled ? '' : esc(_t('workflows.config.toolDisabled'))}</option>`).join('')}
|
||||
@@ -653,6 +867,7 @@
|
||||
${typedTextarea('workflow-tool-arguments', _t('workflows.config.argumentsStatic'), cfg.arguments, '{"target":"example.com"}')}
|
||||
${typedField('workflow-tool-timeout', _t('workflows.config.timeoutSeconds'), cfg.timeout_seconds, _t('workflows.config.optional'))}
|
||||
`;
|
||||
enhanceWorkflowToolSelect();
|
||||
if (!workflowToolsLoaded) {
|
||||
loadWorkflowTools().then(() => {
|
||||
if (selectedElement === ele) renderTypedConfig(ele);
|
||||
|
||||
Reference in New Issue
Block a user