mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-18 18:07:32 +02:00
Add files via upload
This commit is contained in:
@@ -85,12 +85,17 @@ async function fetchRoles(baseUrl, token, signal) {
|
||||
signal,
|
||||
});
|
||||
const list = (data && data.roles) || [];
|
||||
const out = [{ id: '', label: 'Default' }];
|
||||
const out = [];
|
||||
for (const r of list) {
|
||||
if (r.enabled === false) continue;
|
||||
if (!r.name) continue;
|
||||
// Server default role is named "默认"; map to empty id + English label (UI is EN).
|
||||
// Skip it here and prepend a single Default entry below — avoids Default + 默认.
|
||||
if (r.name === '默认') continue;
|
||||
out.push({ id: r.name, label: r.name });
|
||||
}
|
||||
out.sort((a, b) => a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }));
|
||||
out.unshift({ id: '', label: 'Default' });
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -816,6 +816,7 @@ body {
|
||||
background: var(--csai-surface);
|
||||
color: var(--csai-text);
|
||||
box-shadow: var(--csai-shadow-md);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#send-dialog::backdrop {
|
||||
@@ -823,7 +824,11 @@ body {
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
#send-dialog form { padding: 20px; margin: 0; }
|
||||
#send-dialog form {
|
||||
padding: 20px;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dialog-header {
|
||||
display: flex;
|
||||
@@ -849,6 +854,143 @@ body {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#send-dialog .field-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
color: var(--csai-text-muted);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
/* ── Custom select (Send dialog) ── */
|
||||
.cs-select {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cs-select-trigger {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 10px 0 12px;
|
||||
border: 1px solid var(--csai-border);
|
||||
border-radius: var(--csai-radius-sm);
|
||||
background: var(--csai-surface-2);
|
||||
color: var(--csai-text);
|
||||
font: inherit;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
|
||||
}
|
||||
|
||||
.cs-select-trigger:hover {
|
||||
border-color: var(--csai-border-strong);
|
||||
background: var(--csai-surface);
|
||||
}
|
||||
|
||||
.cs-select.open .cs-select-trigger {
|
||||
border-color: var(--csai-accent);
|
||||
box-shadow: 0 0 0 3px var(--csai-accent-ring);
|
||||
background: var(--csai-surface);
|
||||
}
|
||||
|
||||
.cs-select-value {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cs-select-caret {
|
||||
flex-shrink: 0;
|
||||
color: var(--csai-text-faint);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.cs-select.open .cs-select-caret {
|
||||
transform: rotate(180deg);
|
||||
color: var(--csai-accent);
|
||||
}
|
||||
|
||||
.cs-select-menu {
|
||||
position: absolute;
|
||||
z-index: 40;
|
||||
top: calc(100% + 6px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
list-style: none;
|
||||
max-height: 240px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
background: var(--csai-surface);
|
||||
border: 1px solid var(--csai-border);
|
||||
border-radius: var(--csai-radius);
|
||||
box-shadow: var(--csai-shadow-md);
|
||||
}
|
||||
|
||||
.cs-select-menu[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.cs-select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--csai-radius-sm);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--csai-text);
|
||||
cursor: pointer;
|
||||
line-height: 1.35;
|
||||
transition: background 0.1s, color 0.1s;
|
||||
}
|
||||
|
||||
.cs-select-option:hover {
|
||||
background: var(--csai-surface-2);
|
||||
}
|
||||
|
||||
.cs-select-option.is-selected {
|
||||
background: var(--csai-accent-soft);
|
||||
color: var(--csai-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cs-select-option-check {
|
||||
flex: 0 0 12px;
|
||||
width: 12px;
|
||||
font-size: 10px;
|
||||
line-height: 1;
|
||||
opacity: 0;
|
||||
color: var(--csai-accent);
|
||||
}
|
||||
|
||||
.cs-select-option.is-selected .cs-select-option-check {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cs-select-option-label {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#dlg-instruction {
|
||||
|
||||
@@ -117,9 +117,45 @@
|
||||
<h3>Send to CyberStrikeAI</h3>
|
||||
</div>
|
||||
<div class="send-row">
|
||||
<label class="field block">Project <select id="dlg-project"></select></label>
|
||||
<label class="field block">Role <select id="dlg-role"></select></label>
|
||||
<label class="field block">Agent <select id="dlg-agent"></select></label>
|
||||
<div class="field block">
|
||||
<span class="field-label">Project</span>
|
||||
<div class="cs-select" data-cs-select="dlg-project">
|
||||
<input type="hidden" id="dlg-project" value="">
|
||||
<button type="button" class="cs-select-trigger" aria-haspopup="listbox" aria-expanded="false">
|
||||
<span class="cs-select-value">Loading…</span>
|
||||
<svg class="cs-select-caret" width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<ul class="cs-select-menu" role="listbox" hidden></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field block">
|
||||
<span class="field-label">Role</span>
|
||||
<div class="cs-select" data-cs-select="dlg-role">
|
||||
<input type="hidden" id="dlg-role" value="">
|
||||
<button type="button" class="cs-select-trigger" aria-haspopup="listbox" aria-expanded="false">
|
||||
<span class="cs-select-value">Loading…</span>
|
||||
<svg class="cs-select-caret" width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<ul class="cs-select-menu" role="listbox" hidden></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field block">
|
||||
<span class="field-label">Agent</span>
|
||||
<div class="cs-select" data-cs-select="dlg-agent">
|
||||
<input type="hidden" id="dlg-agent" value="">
|
||||
<button type="button" class="cs-select-trigger" aria-haspopup="listbox" aria-expanded="false">
|
||||
<span class="cs-select-value">Eino Single (ADK)</span>
|
||||
<svg class="cs-select-caret" width="12" height="12" viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
||||
<path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<ul class="cs-select-menu" role="listbox" hidden></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<label class="field block">Test instruction (editable for this request)
|
||||
<textarea id="dlg-instruction" rows="6"></textarea>
|
||||
|
||||
@@ -757,26 +757,111 @@ async function onValidate() {
|
||||
}
|
||||
|
||||
function fillAgentSelect(sel, selected) {
|
||||
sel.innerHTML = '';
|
||||
for (const m of AGENT_MODES) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = m.id;
|
||||
opt.textContent = m.label;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
sel.value = selected || 'eino_single';
|
||||
fillSelect(sel, AGENT_MODES.map((m) => ({ id: m.id, label: m.label })), selected || 'eino_single');
|
||||
}
|
||||
|
||||
function csSelectWrap(input) {
|
||||
return input && input.closest ? input.closest('.cs-select') : null;
|
||||
}
|
||||
|
||||
function closeAllCsSelects(except) {
|
||||
document.querySelectorAll('.cs-select.open').forEach((wrap) => {
|
||||
if (except && wrap === except) return;
|
||||
wrap.classList.remove('open');
|
||||
const trigger = wrap.querySelector('.cs-select-trigger');
|
||||
const menu = wrap.querySelector('.cs-select-menu');
|
||||
if (trigger) trigger.setAttribute('aria-expanded', 'false');
|
||||
if (menu) menu.hidden = true;
|
||||
});
|
||||
}
|
||||
|
||||
function fillSelect(sel, items, value) {
|
||||
sel.innerHTML = '';
|
||||
for (const item of items) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.label;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
const wrap = csSelectWrap(sel);
|
||||
const has = items.some((i) => i.id === value);
|
||||
sel.value = has ? value : (items[0] && items[0].id) || '';
|
||||
const resolved = has ? value : (items[0] && items[0].id) || '';
|
||||
const selectedItem = items.find((i) => i.id === resolved) || items[0];
|
||||
|
||||
sel.value = resolved;
|
||||
|
||||
if (!wrap) {
|
||||
// Fallback for plain <select> if any remain
|
||||
sel.innerHTML = '';
|
||||
for (const item of items) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = item.id;
|
||||
opt.textContent = item.label;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
sel.value = resolved;
|
||||
return;
|
||||
}
|
||||
|
||||
const valueEl = wrap.querySelector('.cs-select-value');
|
||||
const menu = wrap.querySelector('.cs-select-menu');
|
||||
if (valueEl) valueEl.textContent = (selectedItem && selectedItem.label) || '—';
|
||||
if (!menu) return;
|
||||
|
||||
menu.innerHTML = '';
|
||||
for (const item of items) {
|
||||
const li = document.createElement('li');
|
||||
li.setAttribute('role', 'option');
|
||||
li.className = 'cs-select-option' + (item.id === resolved ? ' is-selected' : '');
|
||||
li.setAttribute('data-value', item.id);
|
||||
li.setAttribute('aria-selected', item.id === resolved ? 'true' : 'false');
|
||||
li.innerHTML =
|
||||
'<span class="cs-select-option-check" aria-hidden="true">✓</span>' +
|
||||
'<span class="cs-select-option-label"></span>';
|
||||
li.querySelector('.cs-select-option-label').textContent = item.label;
|
||||
li.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
sel.value = item.id;
|
||||
if (valueEl) valueEl.textContent = item.label;
|
||||
menu.querySelectorAll('.cs-select-option').forEach((opt) => {
|
||||
const on = (opt.getAttribute('data-value') || '') === item.id;
|
||||
opt.classList.toggle('is-selected', on);
|
||||
opt.setAttribute('aria-selected', on ? 'true' : 'false');
|
||||
});
|
||||
closeAllCsSelects();
|
||||
});
|
||||
menu.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
function setupCsSelects() {
|
||||
document.querySelectorAll('.cs-select').forEach((wrap) => {
|
||||
const trigger = wrap.querySelector('.cs-select-trigger');
|
||||
const menu = wrap.querySelector('.cs-select-menu');
|
||||
if (!trigger || !menu || trigger.dataset.csBound) return;
|
||||
trigger.dataset.csBound = '1';
|
||||
trigger.addEventListener('click', (ev) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const willOpen = !wrap.classList.contains('open');
|
||||
closeAllCsSelects();
|
||||
if (willOpen) {
|
||||
wrap.classList.add('open');
|
||||
trigger.setAttribute('aria-expanded', 'true');
|
||||
menu.hidden = false;
|
||||
const selected = menu.querySelector('.cs-select-option.is-selected');
|
||||
if (selected) selected.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('click', (ev) => {
|
||||
if (ev.target.closest && ev.target.closest('.cs-select')) return;
|
||||
closeAllCsSelects();
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', (ev) => {
|
||||
if (ev.key === 'Escape') closeAllCsSelects();
|
||||
});
|
||||
|
||||
const dlg = $('send-dialog');
|
||||
if (dlg) {
|
||||
dlg.addEventListener('close', () => closeAllCsSelects());
|
||||
}
|
||||
}
|
||||
|
||||
async function openSendDialog(entryOverride) {
|
||||
@@ -789,14 +874,15 @@ async function openSendDialog(entryOverride) {
|
||||
|
||||
$('dlg-instruction').value = config.lastInstruction || defaultInstruction();
|
||||
fillAgentSelect($('dlg-agent'), config.lastAgentMode);
|
||||
$('dlg-project').innerHTML = '<option>Loading…</option>';
|
||||
$('dlg-role').innerHTML = '<option>Loading…</option>';
|
||||
fillSelect($('dlg-project'), [{ id: '', label: 'Loading…' }], '');
|
||||
fillSelect($('dlg-role'), [{ id: '', label: 'Loading…' }], '');
|
||||
dlg.showModal();
|
||||
|
||||
try {
|
||||
const { projects, roles } = await fetchCatalogCached(baseUrl, token);
|
||||
fillSelect($('dlg-project'), projects, config.lastProjectId);
|
||||
fillSelect($('dlg-role'), roles, config.lastRole);
|
||||
const lastRole = config.lastRole === '默认' ? '' : config.lastRole;
|
||||
fillSelect($('dlg-role'), roles, lastRole);
|
||||
} catch (err) {
|
||||
if (await handleAuthFailure(err, 'Token invalid or expired — please Validate again')) {
|
||||
dlg.close();
|
||||
@@ -832,7 +918,8 @@ async function onSendConfirmed() {
|
||||
}
|
||||
|
||||
const modeLabel = (AGENT_MODES.find((m) => m.id === agentMode) || {}).label || agentMode;
|
||||
const roleLabel = role || 'Default';
|
||||
const roleValueEl = document.querySelector('[data-cs-select="dlg-role"] .cs-select-value');
|
||||
const roleLabel = (roleValueEl && roleValueEl.textContent) || role || 'Default';
|
||||
const run = createRun(e, modeLabel + ' · ' + roleLabel);
|
||||
activeRunId = run.id;
|
||||
prependRunItem(run);
|
||||
@@ -1149,6 +1236,7 @@ $('send-form').addEventListener('submit', (ev) => {
|
||||
$('dlg-cancel').addEventListener('click', () => $('send-dialog').close());
|
||||
|
||||
setupTabs();
|
||||
setupCsSelects();
|
||||
setupAuthProbeHooks();
|
||||
initConfig().then(() => {
|
||||
if (extensionAlive()) connectBackground();
|
||||
|
||||
Reference in New Issue
Block a user