Add files via upload

This commit is contained in:
公明
2026-07-07 14:45:07 +08:00
committed by GitHub
parent b52f8990f3
commit 8bc137882b
4 changed files with 404 additions and 1 deletions
+182
View File
@@ -31778,6 +31778,188 @@ html[data-theme="dark"] .form-group select {
cursor: pointer;
}
.workflow-tool-native-select {
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
}
.workflow-tool-select {
position: relative;
width: 100%;
min-width: 0;
}
.workflow-tool-select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
min-height: 38px;
padding: 8px 10px;
border: 1px solid var(--border-color);
border-radius: 7px;
background: var(--bg-primary);
color: var(--text-primary);
font-size: 0.875rem;
font-family: inherit;
cursor: pointer;
text-align: left;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.workflow-tool-select-value {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.workflow-tool-select-caret {
flex: 0 0 auto;
color: var(--text-secondary);
font-size: 0.75rem;
line-height: 1;
transition: transform 0.2s ease;
}
.workflow-tool-select.open .workflow-tool-select-caret {
transform: rotate(180deg);
}
.workflow-tool-select-trigger:hover:not(:disabled) {
border-color: var(--accent-color);
}
.workflow-tool-select.open .workflow-tool-select-trigger {
border-color: var(--accent-color);
box-shadow: 0 0 0 3px rgba(0, 102, 255, 0.1);
}
.workflow-tool-select.is-disabled .workflow-tool-select-trigger {
cursor: not-allowed;
opacity: 0.56;
background: var(--bg-secondary, #f5f6f8);
}
.workflow-tool-select-dropdown {
display: none;
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
z-index: 300;
max-height: 280px;
overflow: hidden;
padding: 0;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: var(--shadow-lg);
flex-direction: column;
}
.workflow-tool-select.open .workflow-tool-select-dropdown {
display: flex;
}
.workflow-tool-select-search {
flex-shrink: 0;
padding: 8px;
border-bottom: 1px solid var(--border-color);
background: var(--bg-primary);
}
.workflow-tool-select-search-input {
width: 100%;
padding: 6px 10px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 0.8125rem;
background: var(--bg-primary);
color: var(--text-primary);
font-family: inherit;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.workflow-tool-select-search-input:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.1);
}
.workflow-tool-select-search-input::placeholder {
color: var(--text-muted);
}
.workflow-tool-select-options {
flex: 1;
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
padding: 4px;
}
.workflow-tool-select-option {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
min-width: 0;
padding: 8px 10px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--text-primary);
font-size: 0.8125rem;
font-family: inherit;
cursor: pointer;
text-align: left;
}
.workflow-tool-select-option:hover {
background: var(--bg-secondary);
}
.workflow-tool-select-option.is-selected {
color: var(--accent-color);
font-weight: 600;
background: rgba(0, 102, 255, 0.08);
}
.workflow-tool-select-check {
flex: 0 0 14px;
width: 14px;
color: var(--accent-color);
opacity: 0;
font-size: 0.75rem;
line-height: 1;
}
.workflow-tool-select-option.is-selected .workflow-tool-select-check {
opacity: 1;
}
.workflow-tool-select-label {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.workflow-tool-select-empty {
padding: 10px 12px;
text-align: center;
color: var(--text-muted);
font-size: 0.8125rem;
line-height: 1.4;
}
@media (max-width: 1400px) {
.workflow-page-content {
grid-template-columns: clamp(180px, 14vw, 220px) minmax(0, 1fr);
+3
View File
@@ -3079,6 +3079,9 @@
"inputKeys": "Input variables",
"mcpTool": "MCP tool",
"selectTool": "Select a tool",
"searchTool": "Search tools…",
"noToolsFound": "No matching tools",
"noToolsAvailable": "No tools available",
"toolDisabled": " (disabled)",
"argumentsTemplate": "Arguments template",
"argumentsStatic": "Tool arguments (JSON)",
+3
View File
@@ -3067,6 +3067,9 @@
"inputKeys": "输入变量",
"mcpTool": "MCP 工具",
"selectTool": "请选择工具",
"searchTool": "搜索工具…",
"noToolsFound": "未找到匹配的工具",
"noToolsAvailable": "暂无可用工具",
"toolDisabled": "(未启用)",
"argumentsTemplate": "参数模板",
"argumentsStatic": "工具参数(JSON",
+216 -1
View File
@@ -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);