added scheduler

This commit is contained in:
ION606
2025-09-26 14:28:04 -04:00
parent 5f535a61c1
commit 133ef3f48b
19 changed files with 3975 additions and 555 deletions
+258
View File
@@ -0,0 +1,258 @@
const FEATURE_DEFAULTS = {
image_generation: false,
code_interpreter: false,
web_search: false,
memory: true,
};
const FEATURE_METADATA = {
image_generation: {
label: 'Image Generation',
description: 'Request image outputs from supported models. When enabled the assistant may produce images alongside text responses.',
},
code_interpreter: {
label: 'Code Interpreter',
description: 'Grant access to the sandboxed runtime for running Python snippets and data transformations during the task.',
},
web_search: {
label: 'Web Search',
description: 'Allow the assistant to perform outbound web searches to enrich the response with current information.',
},
memory: {
label: 'Memory',
description: 'Persist relevant conversation context for future automations so runs can recall prior outcomes.',
},
};
const FEATURE_SECTION_TAG = 'Feature';
function renderFeatureList(featureState) {
const container = document.querySelector('#features-select');
const hiddenInput = document.querySelector('#features-select-input');
if (!container || !hiddenInput) return;
const baseState = normalizeFeatureState(featureState),
featureKeys = Object.keys(baseState);
container.innerHTML = '';
container.setAttribute('data-has-features', String(featureKeys.length > 0));
const selection = { ...baseState },
syncHidden = () => {
hiddenInput.value = JSON.stringify(selection);
hiddenInput.dispatchEvent(new Event('input', { bubbles: true }));
hiddenInput.dispatchEvent(new Event('change', { bubbles: true }));
};
featureKeys.forEach((id) => {
const meta = getFeatureMeta(id);
const pill = document.createElement('div');
pill.className = 'feature-pill';
pill.dataset.featureId = id;
pill.setAttribute('role', 'option');
pill.setAttribute('tabindex', '-1');
const toggleBtn = document.createElement('button');
toggleBtn.type = 'button';
toggleBtn.className = 'feature-pill-toggle';
toggleBtn.textContent = meta.label;
toggleBtn.setAttribute('aria-pressed', String(Boolean(selection[id])));
toggleBtn.addEventListener('click', () => {
selection[id] = !selection[id];
const isSelected = Boolean(selection[id]);
pill.classList.toggle('feature-pill--selected', isSelected);
pill.setAttribute('aria-selected', String(isSelected));
toggleBtn.setAttribute('aria-pressed', String(isSelected));
syncHidden();
});
const infoBtn = document.createElement('button');
infoBtn.type = 'button';
infoBtn.className = 'feature-pill-info';
infoBtn.setAttribute('aria-label', `Show info for ${meta.label}`);
infoBtn.innerHTML = '<span aria-hidden="true">i</span>';
infoBtn.addEventListener('click', (event) => {
event.stopPropagation();
generateFeatureCard({ id, ...meta, selected: Boolean(selection[id]) }, { trigger: infoBtn });
});
const isSelected = Boolean(selection[id]);
pill.classList.toggle('feature-pill--selected', isSelected);
pill.setAttribute('aria-selected', String(isSelected));
pill.appendChild(toggleBtn);
pill.appendChild(infoBtn);
container.appendChild(pill);
});
syncHidden();
}
function normalizeFeatureState(featureState) {
const normalized = { ...FEATURE_DEFAULTS };
if (featureState && typeof featureState === 'object' && !Array.isArray(featureState)) {
for (const [key, value] of Object.entries(featureState)) {
if (Object.prototype.hasOwnProperty.call(FEATURE_DEFAULTS, key) || Object.prototype.hasOwnProperty.call(FEATURE_METADATA, key)) {
normalized[key] = Boolean(value);
}
}
}
return normalized;
}
function getFeatureMeta(id) {
if (Object.prototype.hasOwnProperty.call(FEATURE_METADATA, id)) {
return FEATURE_METADATA[id];
}
const label = id.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
return {
label,
description: 'Toggle this capability for the scheduled run.',
};
}
function generateFeatureCard(feature, options = {}) {
const mount = options.mountSelector ? document.querySelector(options.mountSelector) : document.body;
if (!mount) throw new Error('mount element not found');
const overlay = document.createElement('div');
overlay.className = 'fc-overlay';
overlay.setAttribute('data-testid', 'feature-card-overlay');
const dialog = document.createElement('div');
dialog.className = 'fc-dialog';
dialog.setAttribute('role', 'dialog');
dialog.setAttribute('aria-modal', 'true');
const titleId = `fc-title-${Math.random().toString(36).slice(2)}`;
const descId = `fc-desc-${Math.random().toString(36).slice(2)}`;
dialog.setAttribute('aria-labelledby', titleId);
dialog.setAttribute('aria-describedby', descId);
const header = document.createElement('header');
header.className = 'fc-header';
const titleWrap = document.createElement('div');
titleWrap.className = 'fc-titlewrap';
const h1 = document.createElement('h1');
h1.id = titleId;
h1.className = 'fc-title';
h1.textContent = feature.label;
const tag = document.createElement('span');
tag.className = 'fc-tag';
tag.textContent = FEATURE_SECTION_TAG;
tag.setAttribute('data-testid', 'feature-tag');
titleWrap.appendChild(h1);
titleWrap.appendChild(tag);
const closeBtn = document.createElement('button');
closeBtn.className = 'fc-close';
closeBtn.type = 'button';
closeBtn.setAttribute('aria-label', 'close');
closeBtn.textContent = '×';
header.appendChild(titleWrap);
header.appendChild(closeBtn);
const body = document.createElement('div');
body.className = 'fc-body';
const desc = document.createElement('p');
desc.id = descId;
desc.className = 'fc-desc';
desc.textContent = feature.description;
const details = document.createElement('dl');
details.className = 'fc-details';
const pairs = [
['Identifier', feature.id],
['Current state', feature.selected ? 'Enabled' : 'Disabled'],
];
pairs.forEach(([label, value]) => {
const dt = document.createElement('dt');
dt.textContent = label;
const dd = document.createElement('dd');
dd.textContent = value;
details.appendChild(dt);
details.appendChild(dd);
});
body.appendChild(desc);
body.appendChild(details);
dialog.appendChild(header);
dialog.appendChild(body);
overlay.appendChild(dialog);
mount.appendChild(overlay);
const opener = options.trigger instanceof HTMLElement ? options.trigger : document.activeElement;
const focusableSelectors = [
'a[href]',
'button:not([disabled])',
'input:not([disabled])',
'select:not([disabled])',
'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
].join(',');
const getFocusable = () => /** @type {HTMLElement[]} */(Array.from(dialog.querySelectorAll(focusableSelectors)));
const focusFirst = () => {
const items = getFocusable();
(items[0] || closeBtn).focus();
};
const onKeydown = (event) => {
if (event.key === 'Tab') {
const items = getFocusable();
if (items.length === 0) {
event.preventDefault();
closeBtn.focus();
return;
}
const first = items[0];
const last = items[items.length - 1];
const active = /** @type {HTMLElement} */(document.activeElement);
if (!event.shiftKey && active === last) {
event.preventDefault();
first.focus();
} else if (event.shiftKey && active === first) {
event.preventDefault();
last.focus();
}
}
if (event.key === 'Escape') {
event.preventDefault();
close();
}
};
const onOverlayClick = (event) => {
if (event.target === overlay) close();
};
overlay.addEventListener('click', onOverlayClick);
document.addEventListener('keydown', onKeydown);
closeBtn.addEventListener('click', () => close());
const prevOverflow = document.documentElement.style.overflow;
document.documentElement.style.overflow = 'hidden';
setTimeout(focusFirst, 0);
function close() {
overlay.removeEventListener('click', onOverlayClick);
document.removeEventListener('keydown', onKeydown);
document.documentElement.style.overflow = prevOverflow;
overlay.remove();
if (opener && typeof opener.focus === 'function') opener.focus();
}
return { close, root: overlay };
}