Spaces:
Paused
Paused
| (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(); | |
| } | |
| })(); | |