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:
81
public/js/formatter.js
Normal file
81
public/js/formatter.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import { loadScript } from './utils.js';
|
||||
import { getActiveEditor, getActiveTab, getCurrentMode, getCssType } from './editors.js';
|
||||
|
||||
const PRETTIER_CDN = 'https://cdn.jsdelivr.net/npm/prettier@3';
|
||||
const PLUGINS = [
|
||||
'/standalone.min.js',
|
||||
'/plugins/babel.min.js',
|
||||
'/plugins/html.min.js',
|
||||
'/plugins/postcss.min.js',
|
||||
'/plugins/typescript.min.js',
|
||||
'/plugins/estree.min.js',
|
||||
];
|
||||
|
||||
let loaded = false;
|
||||
|
||||
async function ensurePrettier() {
|
||||
if (loaded) return;
|
||||
for (const plugin of PLUGINS) {
|
||||
await loadScript(PRETTIER_CDN + plugin);
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
function getParser(tabId, mode, cssType) {
|
||||
if (tabId === 'html') return { parser: 'html', plugins: [prettierPlugins.html] };
|
||||
if (tabId === 'css') {
|
||||
return { parser: 'css', plugins: [prettierPlugins.postcss] };
|
||||
}
|
||||
// JS tab — depends on mode
|
||||
if (mode === 'typescript' || mode === 'react-ts') {
|
||||
return { parser: 'typescript', plugins: [prettierPlugins.typescript, prettierPlugins.estree] };
|
||||
}
|
||||
if (mode === 'markdown') {
|
||||
return null; // Prettier markdown plugin not loaded
|
||||
}
|
||||
if (mode === 'vue' || mode === 'svelte') {
|
||||
return { parser: 'html', plugins: [prettierPlugins.html] };
|
||||
}
|
||||
return { parser: 'babel', plugins: [prettierPlugins.babel, prettierPlugins.estree] };
|
||||
}
|
||||
|
||||
export async function formatActiveEditor() {
|
||||
const editor = getActiveEditor();
|
||||
if (!editor) return;
|
||||
|
||||
await ensurePrettier();
|
||||
|
||||
const tabId = getActiveTab();
|
||||
const mode = getCurrentMode();
|
||||
const cssType = getCssType();
|
||||
const config = getParser(tabId, mode, cssType);
|
||||
if (!config) return;
|
||||
|
||||
const code = editor.getValue();
|
||||
if (!code.trim()) return;
|
||||
|
||||
try {
|
||||
// Save cursor position ratio
|
||||
const pos = editor.getPosition();
|
||||
const offset = editor.getModel().getOffsetAt(pos);
|
||||
const ratio = offset / Math.max(code.length, 1);
|
||||
|
||||
const formatted = await prettier.format(code, {
|
||||
parser: config.parser,
|
||||
plugins: config.plugins,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
printWidth: 100,
|
||||
});
|
||||
|
||||
editor.setValue(formatted);
|
||||
|
||||
// Restore cursor approximately
|
||||
const newOffset = Math.round(ratio * formatted.length);
|
||||
const newPos = editor.getModel().getPositionAt(newOffset);
|
||||
editor.setPosition(newPos);
|
||||
editor.revealPositionInCenter(newPos);
|
||||
} catch (e) {
|
||||
console.warn('Prettier format error:', e.message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user