smolcode / static /web_tui.js
seanpoyner's picture
Upload folder using huggingface_hub
daea45b verified
Raw
History Blame Contribute Delete
10.8 kB
(function () {
"use strict";
if (window.__smolcodeTuiInit) return;
window.__smolcodeTuiInit = true;
let leaderPending = false;
let leaderTimer = null;
function click(id) {
const root = document.getElementById(id);
if (!root) return;
const btn = root.tagName === "BUTTON" ? root : root.querySelector("button");
(btn || root).click();
}
function setHiddenValue(id, value) {
const root = document.getElementById(id);
if (!root) return;
const el = root.tagName === "TEXTAREA" || root.tagName === "INPUT"
? root
: root.querySelector("textarea, input");
if (!el) return;
el.value = value;
el.dispatchEvent(new Event("input", { bubbles: true }));
}
window.__smolcodePick = function (idx) {
setHiddenValue("sc-picker-pick", String(idx));
click("sc-picker-confirm");
};
function editor() {
const root = document.getElementById("sc-editor");
if (root) {
if (root.tagName === "TEXTAREA" || root.tagName === "INPUT") return root;
const inner = root.querySelector("textarea, input[type='text']");
if (inner) return inner;
}
const boxes = document.querySelectorAll("[data-testid='textbox']");
return boxes.length ? boxes[boxes.length - 1] : null;
}
function PopupController(popupEl, kind) {
this.popup = popupEl;
this.kind = kind || "slash";
this.matches = [];
this.sel = 0;
}
PopupController.prototype.hide = function () {
if (this.popup) this.popup.style.display = "none";
this.matches = [];
this.sel = 0;
};
PopupController.prototype.render = function (matches, ta, replaceFrom) {
this.matches = matches;
this.sel = 0;
this.replaceFrom = replaceFrom;
this.ta = ta;
if (!this.popup) return;
this.popup.innerHTML = "";
const self = this;
matches.slice(0, 12).forEach(function (item, i) {
const row = document.createElement("div");
row.className = "sc-popup-item" + (i === 0 ? " sc-popup-sel" : "");
row.textContent = item;
row.onclick = function () {
self.sel = i;
self.accept();
};
self.popup.appendChild(row);
});
const rect = ta.getBoundingClientRect();
this.popup.style.display = matches.length ? "block" : "none";
this.popup.style.left = rect.left + "px";
this.popup.style.top = Math.max(0, rect.top - 160) + "px";
this.popup.style.width = Math.max(220, rect.width) + "px";
this._highlight();
};
PopupController.prototype._highlight = function () {
if (!this.popup) return;
const items = this.popup.querySelectorAll(".sc-popup-item");
items.forEach(function (el, i) {
el.classList.toggle("sc-popup-sel", i === this.sel);
}, this);
};
PopupController.prototype.move = function (delta) {
if (!this.matches.length) return;
this.sel = (this.sel + delta + this.matches.length) % this.matches.length;
this._highlight();
};
PopupController.prototype.accept = function () {
if (!this.matches.length || !this.ta) return;
const val = this.ta.value;
if (this.kind === "file") {
const atMatch = val.match(/(?:^|\s)@(\S*)$/);
if (!atMatch) return;
const atPos = val.length - atMatch[0].length + (atMatch[0].charAt(0) === " " ? 1 : 0);
const item = this.matches[this.sel];
this.ta.value = val.slice(0, atPos) + "@" + item + " ";
} else {
const item = this.matches[this.sel];
const rest = val.slice(this.replaceFrom);
this.ta.value = item + rest;
}
this.ta.dispatchEvent(new Event("input", { bubbles: true }));
this.hide();
this.ta.focus();
};
PopupController.prototype.tabComplete = function () {
if (!this.matches.length) return false;
this.accept();
return true;
};
PopupController.prototype.visible = function () {
return this.popup && this.popup.style.display === "block" && this.matches.length > 0;
};
function ensurePopup(cls) {
let el = document.querySelector("." + cls);
if (!el) {
el = document.createElement("div");
el.className = cls + " sc-popup";
document.body.appendChild(el);
}
return el;
}
const slashPopup = new PopupController(ensurePopup("sc-slash-popup"), "slash");
const filePopup = new PopupController(ensurePopup("sc-file-popup"), "file");
function hidePopups() {
slashPopup.hide();
filePopup.hide();
}
function onEditorInput(ta) {
const val = ta.value;
const cmds = window.__smolcode_commands || [];
if (val.startsWith("/") && !val.includes(" ")) {
const m = cmds.filter(function (c) { return c.startsWith(val); });
slashPopup.render(m, ta, val.length);
filePopup.hide();
return;
}
slashPopup.hide();
const atMatch = val.match(/(?:^|\s)@(\S*)$/);
if (atMatch) {
const prefix = atMatch[1];
const files = window.__smolcode_files || [];
const m = files.filter(function (f) { return f.startsWith(prefix); });
const atPos = val.length - atMatch[0].length + (atMatch[0].charAt(0) === " " ? 1 : 0);
filePopup.render(m, ta, atPos);
return;
}
filePopup.hide();
}
function activePopup() {
if (slashPopup.visible()) return slashPopup;
if (filePopup.visible()) return filePopup;
return null;
}
function onEditorKeyDown(e) {
const ta = e.target;
const popup = activePopup();
if (popup && (e.key === "ArrowDown" || e.key === "ArrowUp")) {
e.preventDefault();
popup.move(e.key === "ArrowDown" ? 1 : -1);
return;
}
if (popup && e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
popup.accept();
return;
}
if (e.key === "Tab" && popup && !e.shiftKey) {
e.preventDefault();
popup.tabComplete();
return;
}
if (e.key === "Enter" && !e.shiftKey && !e.altKey) {
e.preventDefault();
hidePopups();
click("sc-submit");
return;
}
if (e.key === "Escape") {
hidePopups();
if (document.querySelector(".sc-overlay")) {
click("sc-close-overlay");
} else {
click("sc-interrupt");
}
return;
}
if (e.ctrlKey && (e.key === "l" || e.key === "L")) {
e.preventDefault();
click("sc-clear");
return;
}
if (e.ctrlKey && (e.key === "x" || e.key === "X")) {
e.preventDefault();
leaderPending = true;
if (leaderTimer) clearTimeout(leaderTimer);
leaderTimer = setTimeout(function () { leaderPending = false; }, 2000);
click("sc-whichkey");
return;
}
if (leaderPending && !e.ctrlKey && !e.metaKey && e.key.length === 1) {
leaderPending = false;
if (leaderTimer) clearTimeout(leaderTimer);
const map = {
m: "sc-open-picker-models",
a: "sc-open-picker-agents",
t: "sc-open-picker-themes",
l: "sc-open-picker-sessions",
n: "sc-new-session",
b: "sc-toggle-sidebar",
s: "sc-toggle-sidebar-view",
h: "sc-help",
o: "sc-cycle-mode",
e: "sc-cycle-think",
};
const btn = map[e.key.toLowerCase()];
if (btn) {
e.preventDefault();
click(btn);
}
return;
}
if (e.key === "Tab" && !e.shiftKey) {
if (trySlashTabComplete(ta, e)) return;
}
if (e.key === "Tab" && !e.shiftKey && !activePopup()) {
e.preventDefault();
click("sc-cycle-agent");
return;
}
if (e.key === "Tab" && e.shiftKey) {
e.preventDefault();
click("sc-cycle-mode");
return;
}
if (e.key === "F2") {
e.preventDefault();
click("sc-cycle-model");
return;
}
if (document.querySelector(".sc-picker") && !ta) {
if (e.key === "ArrowDown") {
e.preventDefault();
click("sc-picker-down");
} else if (e.key === "ArrowUp") {
e.preventDefault();
click("sc-picker-up");
} else if (e.key === "Enter") {
e.preventDefault();
click("sc-picker-confirm");
}
}
}
function onGlobalKeyDown(e) {
if (document.querySelector(".sc-picker") && document.activeElement !== editor()) {
if (e.key === "ArrowDown") {
e.preventDefault();
click("sc-picker-down");
} else if (e.key === "ArrowUp") {
e.preventDefault();
click("sc-picker-up");
} else if (e.key === "Enter") {
e.preventDefault();
click("sc-picker-confirm");
}
}
}
function bindEditor() {
const ta = editor();
if (!ta || ta.dataset.scBound) return;
ta.dataset.scBound = "1";
ta.addEventListener("input", function () { onEditorInput(ta); });
ta.addEventListener("keydown", onEditorKeyDown);
}
function bindChips() {
/* chips re-render with status HTML; use delegation in init() */
}
function onDocumentClick(e) {
const chip = e.target.closest("[data-picker]");
if (chip) {
const kind = chip.getAttribute("data-picker");
const map = {
models: "sc-open-picker-models",
agents: "sc-open-picker-agents",
themes: "sc-open-picker-themes",
sessions: "sc-open-picker-sessions",
};
if (map[kind]) {
e.preventDefault();
click(map[kind]);
}
return;
}
const modeBtn = e.target.closest("[data-action='cycle-mode']");
if (modeBtn) {
e.preventDefault();
click("sc-cycle-mode");
}
}
function slashMatches(val) {
if (!val.startsWith("/") || val.includes(" ")) return [];
const cmds = window.__smolcode_commands || [];
return cmds.filter(function (c) { return c.startsWith(val); });
}
function trySlashTabComplete(ta, e) {
const val = ta.value;
const matches = slashMatches(val);
if (!matches.length) return false;
e.preventDefault();
const popup = activePopup();
if (popup && popup.kind === "slash" && popup.matches.length) {
popup.tabComplete();
return true;
}
ta.value = matches[0];
ta.dispatchEvent(new Event("input", { bubbles: true }));
hidePopups();
return true;
}
function init() {
document.addEventListener("click", onDocumentClick);
document.addEventListener("click", function (e) {
const overlay = document.querySelector(".sc-overlay");
if (overlay && e.target === overlay) click("sc-close-overlay");
});
document.addEventListener("keydown", onGlobalKeyDown);
const obs = new MutationObserver(function () {
bindEditor();
});
obs.observe(document.body, { childList: true, subtree: true });
bindEditor();
setTimeout(bindEditor, 300);
setTimeout(bindEditor, 1500);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
})();