Add auto complete, theme select, & full screen toggle in editors
This commit is contained in:
@@ -45,6 +45,19 @@ const Editor = {
|
||||
this.generateUrl = vnode.attrs.generateUrl;
|
||||
this.logsUrl = vnode.attrs.logsUrl; // Needed for debug feature
|
||||
|
||||
// Editor Features State
|
||||
this.isFullScreen = false;
|
||||
this.editorTheme = localStorage.getItem('editorTheme') || 'auto';
|
||||
this.availableThemes = [
|
||||
{ id: 'auto', name: 'Auto' },
|
||||
{ id: 'monokai', name: 'Monokai' },
|
||||
{ id: 'chrome', name: 'Chrome' },
|
||||
{ id: 'github', name: 'GitHub' },
|
||||
{ id: 'tomorrow', name: 'Tomorrow' },
|
||||
{ id: 'twilight', name: 'Twilight' },
|
||||
{ id: 'dracula', name: 'Dracula' }
|
||||
];
|
||||
|
||||
// AI State
|
||||
this.naturalLanguageQuery = "";
|
||||
this.aiLoading = false; // General AI loading state
|
||||
@@ -56,13 +69,23 @@ const Editor = {
|
||||
|
||||
oncreate() {
|
||||
this.editorJS = ace.edit("js-editor");
|
||||
this.editorJS.setOptions({ maxLines: 100 });
|
||||
|
||||
// Determine initial theme
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
const theme = isDark ? "ace/theme/monokai" : "ace/theme/chrome";
|
||||
// safe require of language tools
|
||||
try {
|
||||
ace.require("ace/ext/language_tools");
|
||||
} catch (e) {
|
||||
console.warn("Ace language tools not loaded");
|
||||
}
|
||||
|
||||
this.editorJS.setOptions({
|
||||
minLines: this.isFullScreen ? undefined : 15,
|
||||
maxLines: this.isFullScreen ? undefined : 100,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true
|
||||
});
|
||||
|
||||
this.editorJS.setTheme(theme);
|
||||
this.applyTheme();
|
||||
|
||||
this.editorJS.session.setMode(
|
||||
this.runtime === "python" ? "ace/mode/python" : "ace/mode/javascript"
|
||||
);
|
||||
@@ -74,8 +97,12 @@ const Editor = {
|
||||
});
|
||||
|
||||
this.editorJSON = ace.edit("json-editor");
|
||||
this.editorJSON.setOptions({ maxLines: 100 });
|
||||
this.editorJSON.setTheme(theme);
|
||||
this.editorJSON.setOptions({
|
||||
minLines: 15,
|
||||
maxLines: 100,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true
|
||||
});
|
||||
this.editorJSON.session.setMode("ace/mode/json");
|
||||
this.editorJSON.setValue(this.jsonValue, -1);
|
||||
|
||||
@@ -86,17 +113,68 @@ const Editor = {
|
||||
|
||||
// Listen for theme changes
|
||||
this.themeListener = (e) => {
|
||||
const newTheme = e.detail.theme === 'dark' ? "ace/theme/monokai" : "ace/theme/chrome";
|
||||
this.editorJS.setTheme(newTheme);
|
||||
this.editorJSON.setTheme(newTheme);
|
||||
if (this.editorTheme === 'auto') {
|
||||
this.applyTheme();
|
||||
}
|
||||
};
|
||||
window.addEventListener('themeChanged', this.themeListener);
|
||||
|
||||
// Listen for Escape key to exit full screen
|
||||
this.escListener = (e) => {
|
||||
if (this.isFullScreen && e.key === 'Escape') {
|
||||
this.toggleFullScreen();
|
||||
m.redraw();
|
||||
}
|
||||
};
|
||||
document.addEventListener('keydown', this.escListener);
|
||||
},
|
||||
|
||||
applyTheme() {
|
||||
let themeToSet = this.editorTheme;
|
||||
if (themeToSet === 'auto') {
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
themeToSet = isDark ? 'monokai' : 'chrome';
|
||||
}
|
||||
const aceTheme = `ace/theme/${themeToSet}`;
|
||||
if (this.editorJS) this.editorJS.setTheme(aceTheme);
|
||||
if (this.editorJSON) this.editorJSON.setTheme(aceTheme);
|
||||
},
|
||||
|
||||
toggleFullScreen() {
|
||||
this.isFullScreen = !this.isFullScreen;
|
||||
if (this.isFullScreen) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Wait for Mithril to redraw so the DOM is updated (classes/styles applied)
|
||||
// before resizing the editor.
|
||||
setTimeout(() => {
|
||||
if (this.editorJS) {
|
||||
this.editorJS.setOptions({
|
||||
minLines: this.isFullScreen ? undefined : 15,
|
||||
maxLines: this.isFullScreen ? undefined : 100
|
||||
});
|
||||
this.editorJS.resize();
|
||||
}
|
||||
}, 0);
|
||||
},
|
||||
|
||||
setTheme(themeId) {
|
||||
this.editorTheme = themeId;
|
||||
localStorage.setItem('editorTheme', themeId);
|
||||
this.applyTheme();
|
||||
},
|
||||
|
||||
onremove() {
|
||||
if (this.themeListener) {
|
||||
window.removeEventListener('themeChanged', this.themeListener);
|
||||
}
|
||||
if (this.escListener) {
|
||||
document.removeEventListener('keydown', this.escListener);
|
||||
}
|
||||
document.body.style.overflow = '';
|
||||
},
|
||||
|
||||
async execute() {
|
||||
@@ -298,12 +376,19 @@ const Editor = {
|
||||
},
|
||||
|
||||
view() {
|
||||
return m("div", { class: "" }, [
|
||||
const fullScreenStyle = this.isFullScreen
|
||||
? { position: "fixed", top: "0", left: "0", width: "100vw", height: "100vh", zIndex: "9999", backgroundColor: this.editorTheme === 'monokai' || this.editorTheme === 'twilight' || this.editorTheme === 'dracula' ? '#1a1a1a' : '#ffffff' }
|
||||
: {};
|
||||
|
||||
return m("div", {
|
||||
class: this.isFullScreen ? "flex flex-col" : "",
|
||||
style: fullScreenStyle
|
||||
}, [
|
||||
/* Header */
|
||||
m(
|
||||
"div",
|
||||
{
|
||||
class: "flex items-center justify-between pl-2",
|
||||
class: "flex items-center justify-between pl-2 " + (this.isFullScreen ? "p-4 border-b border-gray-200 dark:border-gray-800" : ""),
|
||||
},
|
||||
[
|
||||
this.showHeader
|
||||
@@ -417,7 +502,7 @@ const Editor = {
|
||||
: null,
|
||||
])
|
||||
: m("div"),
|
||||
m("div"),
|
||||
m("div", { class: "flex items-center space-x-2" }),
|
||||
]
|
||||
),
|
||||
|
||||
@@ -473,7 +558,35 @@ const Editor = {
|
||||
this.aiLoading && m("div", {class: "animate-spin h-4 w-4 border-2 border-blue-500 border-t-transparent rounded-full ml-2"})
|
||||
]),
|
||||
|
||||
m("div", { class: "flex items-center space-x-4" }, [
|
||||
m("div", { class: "flex items-center space-x-2" }, [
|
||||
// Theme Selector
|
||||
m("select", {
|
||||
key: "theme-selector",
|
||||
class: "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500",
|
||||
value: this.editorTheme,
|
||||
onchange: (e) => this.setTheme(e.target.value)
|
||||
}, this.availableThemes.map(theme =>
|
||||
m("option", { value: theme.id }, theme.name)
|
||||
)),
|
||||
|
||||
// Full Screen Toggle
|
||||
m("button", {
|
||||
key: "fullscreen-toggle",
|
||||
class: "p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-700 text-gray-600 dark:text-gray-400",
|
||||
onclick: () => this.toggleFullScreen(),
|
||||
title: this.isFullScreen ? "Exit Full Screen (Esc)" : "Full Screen"
|
||||
}, m("svg", {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
fill: "none",
|
||||
viewBox: "0 0 24 24",
|
||||
stroke: "currentColor",
|
||||
"stroke-width": 2,
|
||||
class: "w-5 h-5"
|
||||
}, this.isFullScreen
|
||||
? m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" })
|
||||
: m("path", { "stroke-linecap": "round", "stroke-linejoin": "round", d: "M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" })
|
||||
)),
|
||||
|
||||
m(
|
||||
"select",
|
||||
{
|
||||
@@ -570,7 +683,9 @@ const Editor = {
|
||||
]
|
||||
),
|
||||
|
||||
m("div", { id: "js-editor", class: "rounded shadow h-64" }),
|
||||
m("div", { class: "relative rounded shadow " + (this.isFullScreen ? "flex-1" : "") }, [
|
||||
m("div", { id: "js-editor", style: "width: 100%; height: 100%;" })
|
||||
]),
|
||||
|
||||
m(
|
||||
"div",
|
||||
@@ -607,7 +722,9 @@ const Editor = {
|
||||
]
|
||||
),
|
||||
|
||||
m("div", { id: "json-editor", class: "rounded shadow h-64" }),
|
||||
m("div", { class: "relative rounded shadow" }, [
|
||||
m("div", { id: "json-editor", style: "width: 100%; height: 100%;" })
|
||||
]),
|
||||
|
||||
m("input", { type: "hidden", name: "script", value: this.jsValue }),
|
||||
m("input", {
|
||||
|
||||
Reference in New Issue
Block a user