Add responsive preview, editor themes, template gallery, devtools, and autocomplete
- Device breakpoint toggles (mobile 375px / tablet 768px / desktop 100%) - Editor theme selector with 6 themes (VS Dark/Light, High Contrast, Monokai, Dracula, GitHub Dark) - Starter template gallery with 8 pre-built templates (Todo, API Fetch, CSS Animation, etc.) - Code autocomplete with DOM/React type definitions and snippet completions - Devtools panels: console, network, elements, performance - Code formatter (Prettier), diff view, and linter integration
This commit is contained in:
@@ -1,13 +1,38 @@
|
||||
import { getPref } from './preferences.js';
|
||||
|
||||
const editorOpts = {
|
||||
minimap: { enabled: false },
|
||||
automaticLayout: true,
|
||||
fontSize: 13,
|
||||
lineNumbers: 'on',
|
||||
scrollBeyondLastLine: false,
|
||||
theme: 'vs-dark',
|
||||
theme: getPref('editorTheme') || 'vs-dark',
|
||||
tabSize: 2,
|
||||
renderWhitespace: 'none',
|
||||
padding: { top: 6 },
|
||||
quickSuggestions: { other: true, comments: false, strings: true },
|
||||
suggestOnTriggerCharacters: true,
|
||||
acceptSuggestionOnEnter: 'on',
|
||||
parameterHints: { enabled: true },
|
||||
wordBasedSuggestions: 'currentDocument',
|
||||
suggest: {
|
||||
snippetsPreventQuickSuggestions: false,
|
||||
showSnippets: true,
|
||||
showWords: true,
|
||||
showKeywords: true,
|
||||
showMethods: true,
|
||||
showFunctions: true,
|
||||
showVariables: true,
|
||||
showClasses: true,
|
||||
showInterfaces: true,
|
||||
showProperties: true,
|
||||
showEvents: true,
|
||||
showConstants: true,
|
||||
},
|
||||
autoClosingBrackets: 'always',
|
||||
autoClosingQuotes: 'always',
|
||||
autoSurround: 'languageDefined',
|
||||
bracketPairColorization: { enabled: true },
|
||||
};
|
||||
|
||||
export const MODE_TABS = {
|
||||
@@ -72,8 +97,10 @@ let currentMode = 'html-css-js';
|
||||
let activeTab = null;
|
||||
let cssType = 'css';
|
||||
let onChangeCallback = null;
|
||||
let onTabSwitchCallback = null;
|
||||
let onModeChangeCallback = null;
|
||||
let tabSwitchCallbacks = [];
|
||||
let modeChangeCallbacks = [];
|
||||
let onFormatCallback = null;
|
||||
let onDiffCallback = null;
|
||||
|
||||
const tabBar = () => document.getElementById('tab-bar');
|
||||
const editorArea = () => document.getElementById('editor-area');
|
||||
@@ -83,11 +110,19 @@ export function setOnChange(cb) {
|
||||
}
|
||||
|
||||
export function setOnTabSwitch(cb) {
|
||||
onTabSwitchCallback = cb;
|
||||
tabSwitchCallbacks.push(cb);
|
||||
}
|
||||
|
||||
export function setOnModeChange(cb) {
|
||||
onModeChangeCallback = cb;
|
||||
modeChangeCallbacks.push(cb);
|
||||
}
|
||||
|
||||
export function setOnFormat(cb) {
|
||||
onFormatCallback = cb;
|
||||
}
|
||||
|
||||
export function setOnDiff(cb) {
|
||||
onDiffCallback = cb;
|
||||
}
|
||||
|
||||
export function getActiveEditor() {
|
||||
@@ -124,20 +159,23 @@ export function setCssType(type) {
|
||||
|
||||
function configureJsxSupport(mode) {
|
||||
if (mode === 'react' || mode === 'react-ts') {
|
||||
// Enable JSX in JavaScript defaults
|
||||
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.ESNext,
|
||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
||||
jsxFactory: 'React.createElement',
|
||||
allowNonTsExtensions: true,
|
||||
allowJs: true,
|
||||
lib: ['esnext', 'dom', 'dom.iterable'],
|
||||
});
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.ESNext,
|
||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
||||
jsxFactory: 'React.createElement',
|
||||
allowNonTsExtensions: true,
|
||||
lib: ['esnext', 'dom', 'dom.iterable'],
|
||||
});
|
||||
// Add React type definitions for IntelliSense
|
||||
import('./autocomplete.js').then(m => m.addReactTypes());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +234,29 @@ function renderTabBar(tabs) {
|
||||
btn.addEventListener('click', () => switchTab(tab.id));
|
||||
bar.appendChild(btn);
|
||||
});
|
||||
|
||||
// Spacer + Format + Diff buttons
|
||||
const spacer = document.createElement('span');
|
||||
spacer.className = 'tab-bar-spacer';
|
||||
bar.appendChild(spacer);
|
||||
|
||||
const fmtBtn = document.createElement('button');
|
||||
fmtBtn.className = 'tab-bar-btn format-btn';
|
||||
fmtBtn.textContent = 'Format';
|
||||
fmtBtn.title = 'Format code (Shift+Alt+F)';
|
||||
fmtBtn.addEventListener('click', () => {
|
||||
if (onFormatCallback) onFormatCallback();
|
||||
});
|
||||
bar.appendChild(fmtBtn);
|
||||
|
||||
const diffBtn = document.createElement('button');
|
||||
diffBtn.className = 'tab-bar-btn diff-btn';
|
||||
diffBtn.textContent = 'Diff';
|
||||
diffBtn.title = 'Toggle diff view (Ctrl+D)';
|
||||
diffBtn.addEventListener('click', () => {
|
||||
if (onDiffCallback) onDiffCallback();
|
||||
});
|
||||
bar.appendChild(diffBtn);
|
||||
}
|
||||
|
||||
export function initEditors(mode = 'html-css-js') {
|
||||
@@ -248,7 +309,7 @@ export function switchMode(mode) {
|
||||
|
||||
switchTab(tabs[0].id);
|
||||
|
||||
if (onModeChangeCallback) onModeChangeCallback(mode);
|
||||
modeChangeCallbacks.forEach(cb => cb(mode));
|
||||
}
|
||||
|
||||
export function switchTab(tabId) {
|
||||
@@ -274,7 +335,7 @@ export function switchTab(tabId) {
|
||||
editors[tabId].focus();
|
||||
}
|
||||
|
||||
if (onTabSwitchCallback) onTabSwitchCallback(tabId, editors[tabId]);
|
||||
tabSwitchCallbacks.forEach(cb => cb(tabId, editors[tabId]));
|
||||
}
|
||||
|
||||
export function getEditorValues() {
|
||||
@@ -294,3 +355,8 @@ export function setEditorValues({ html = '', css = '', js = '' }) {
|
||||
export function getActiveTab() {
|
||||
return activeTab;
|
||||
}
|
||||
|
||||
export function setEditorTheme(themeId) {
|
||||
editorOpts.theme = themeId;
|
||||
monaco.editor.setTheme(themeId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user