Personalize styles and behavior. Your edits are stored locally in your browser. Nothing is uploaded.
+
+
+
+
+
+
+
+
+
Local-only storage
Auto-save
Syntax highlight (no libs)
@@ -469,7 +517,7 @@ document.addEventListener('DOMContentLoaded', () => {
};
const debounce = (fn, ms=200) => { let t; return (...a)=>{ clearTimeout(t); t=setTimeout(()=>fn(...a), ms); }; };
- // --------------------- Minimal formatter (quick & safe-ish) ---------------------
+ // --------------------- Minimal formatter ---------------------
function basicFormatCSS(src){
try{
return src
@@ -481,7 +529,6 @@ document.addEventListener('DOMContentLoaded', () => {
}
function basicFormatJS(src){
try{
- // very light touch to avoid breaking code
return src
.replace(/;\s*/g,';\n')
.replace(/\{\s*/g,'{\n ')
@@ -492,54 +539,53 @@ document.addEventListener('DOMContentLoaded', () => {
}
// --------------------- Syntax highlighter (no libraries) ---------------------
- // Tokenizers for CSS & JS using simple regex passes. Not perfect, but fast and dependency-free.
- const JS_KEYWORDS = /\\b(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|let|new|null|return|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\\b/g;
- const JS_BUILTINS = /\\b(?:Array|Object|String|Number|Boolean|Map|Set|WeakMap|WeakSet|Date|Math|JSON|Promise|RegExp|Error|TypeError|URL|Node|Element|document|window|console|fetch)\\b/g;
+ // IMPORTANT: use single backslashes in regex literals (previous version double-escaped, breaking highlighting)
+ const JS_KEYWORDS = /\b(?:await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|from|function|if|import|in|instanceof|let|new|null|return|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g;
+ const JS_BUILTINS = /\b(?:Array|Object|String|Number|Boolean|Map|Set|WeakMap|WeakSet|Date|Math|JSON|Promise|RegExp|Error|TypeError|URL|Node|Element|document|window|console|fetch)\b/g;
function escapeHTML(s){ return s.replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c])); }
function highlightJS(code){
let s = escapeHTML(code);
// comments
- s = s.replace(/(\\/\\*[\\s\\S]*?\\*\\/)/g, '');
- s = s.replace(/(\\/\\/.*?$)/gm, '');
+ s = s.replace(/(\/\*[\s\S]*?\*\/)/g, '');
+ s = s.replace(/(\/\/.*?$)/gm, '');
// strings + template literals
- s = s.replace(/([`][\\s\\S]*?[`])/g, '$1');
- s = s.replace(/('[^'\\\\]*(?:\\\\.[^'\\\\]*)*')/g, '$1');
- s = s.replace(/("[^"\\\\]*(?:\\\\.[^"\\\\]*)*")/g, '$1');
- // regex literals
- s = s.replace(/(\\/(?:\\\\.|[^\\\\\\/\\n])+\\/[gimsuy]*)/g, '$1');
- // numbers & booleans & null
- s = s.replace(/\\b(0x[\\da-fA-F]+|\\d+\\.\\d+|\\d+)\\b/g, '$1');
- s = s.replace(/\\b(true|false|null)\\b/g, '$1');
+ s = s.replace(/(`[\s\S]*?`)/g, '$1');
+ s = s.replace(/('[^'\\]*(?:\\.[^'\\]*)*')/g, '$1');
+ s = s.replace(/("[^"\\]*(?:\\.[^"\\]*)*")/g, '$1');
+ // regex literals (rough)
+ s = s.replace(/(\/(?:\\.|[^\\\/\n])+\/[gimsuy]*)/g, '$1');
+ // numbers/booleans/null
+ s = s.replace(/\b(0x[\da-fA-F]+|\d+\.\d+|\d+)\b/g, '$1');
+ s = s.replace(/\b(true|false|null)\b/g, '$1');
// keywords & builtins
s = s.replace(JS_KEYWORDS, '$&');
s = s.replace(JS_BUILTINS, '$&');
// operators
- s = s.replace(/([=+\\-/*<>!&|%^~?:]+)/g, '$1');
+ s = s.replace(/([=+\-/*<>!&|%^~?:]+)/g, '$1');
return s;
}
function highlightCSS(code){
let s = escapeHTML(code);
// block comments
- s = s.replace(/(\\/\\*[\\s\\S]*?\\*\\/)/g, '');
+ s = s.replace(/(\/\*[\s\S]*?\*\/)/g, '');
// at-rules
s = s.replace(/(@[a-zA-Z-]+)/g, '$1');
// selectors (naive: line before { )
- s = s.replace(/^([^{}@\\n][^{\\n]+)(?=\\s*\\{)/gm, '$1');
- // properties
- s = s.replace(/([a-zA-Z-]+)(\\s*:\\s*)([^;}{]+)/g, (m, p, c, v) => {
- v = v.replace(/(#[0-9a-fA-F]{3,8}|\\b\\d+(?:\\.\\d+)?(?:px|em|rem|%|vh|vw)?\\b)/g, '$1');
+ s = s.replace(/^([^{}@\n][^{\n]+)(?=\s*\{)/gm, '$1');
+ // properties/values
+ s = s.replace(/([a-zA-Z-]+)(\s*:\s*)([^;}{]+)/g, (m, p, c, v) => {
+ v = v.replace(/(#[0-9a-fA-F]{3,8}|\b\d+(?:\.\d+)?(?:px|em|rem|%|vh|vw)?\b)/g, '$1');
v = v.replace(/("[^"]*"|'[^']*')/g, '$1');
return ''+p+''+c+v;
});
return s;
}
- function makeEditor(edId, hlId, lnId, { lang='css', storageKey, defaultValue='' }){
- const ed = $(edId), hl = $(hlId), ln = $(lnId);
- const wrap = ed.parentElement;
+ function makeEditor(edSel, hlSel, lnSel, { lang='css', storageKey, defaultValue='' }){
+ const ed = $(edSel), hl = $(hlSel), ln = $(lnSel);
const load = () => {
const saved = localStorage.getItem(storageKey);
@@ -547,9 +593,9 @@ document.addEventListener('DOMContentLoaded', () => {
};
const setLines = (text) => {
- const lines = text.split('\\n').length;
+ const lines = text.split('\n').length;
let out = '';
- for (let i=1;i<=lines;i++) out += i + (i {
on(ed, 'scroll', syncScroll);
const update = () => render(ed.value);
- on(ed, 'input', debounce(update, 30));
+ on(ed, 'input', debounce(update, 20));
const save = () => { localStorage.setItem(storageKey, ed.value); toast('Saved'); };
const reset = () => { if (confirm('Clear the editor?')) { ed.value=''; update(); save(); } };
@@ -574,7 +620,7 @@ document.addEventListener('DOMContentLoaded', () => {
const exportFile = () => saveFile(storageKey + (lang==='css'?'.css':'.js'), ed.value, 'text/plain');
return { ed, hl, ln, save, reset, copy, exportFile, render, setLines,
- setWrap:(on)=>{ ed.style.whiteSpace = on?'pre-wrap':'pre'; ed.style.overflowWrap = on?'anywhere':'normal'; },
+ setWrap:(onwrap)=>{ ed.style.whiteSpace = onwrap?'pre-wrap':'pre'; ed.style.overflowWrap = onwrap?'anywhere':'normal'; },
format:()=>{ if (lang==='css') ed.value = basicFormatCSS(ed.value); else ed.value = basicFormatJS(ed.value); update(); },
importFrom:(file)=> new Promise(res=>{
const r = new FileReader();
@@ -584,7 +630,57 @@ document.addEventListener('DOMContentLoaded', () => {
};
}
+ // --------------------- Theme presets ---------------------
+ const THEME_STYLE = $('#theme-style');
+ const THEME_KEY = 'poke-custom-theme';
+ const THEMES = {
+ midnight: `
+:root{
+ --bg:#0c0c0f; --panel:#121218; --panel-2:#0f0f15; --text:#e9ecf1; --muted:#aab2c0;
+ --accent:#7cc7ff; --accent-2:#f97794; --border:#212230; --chip:#161824; --chipb:#0f111a;
+}`,
+ sakura: `
+:root{
+ --bg:#0e0a10; --panel:#1b1019; --panel-2:#160d14; --text:#ffeef4; --muted:#ffc8d8;
+ --accent:#ff9cc3; --accent-2:#ffd1e1; --border:#2a1622; --chip:#22111b; --chipb:#1a0d15;
+}`,
+ ocean: `
+:root{
+ --bg:#071017; --panel:#0b1b27; --panel-2:#091521; --text:#e6f3ff; --muted:#a9c4da;
+ --accent:#53b7ff; --accent-2:#3de1c5; --border:#102536; --chip:#0c1a26; --chipb:#0a1520;
+}`,
+ solarized: `
+:root{
+ --bg:#002b36; --panel:#073642; --panel-2:#062c35; --text:#eee8d5; --muted:#93a1a1;
+ --accent:#268bd2; --accent-2:#2aa198; --border:#0a3945; --chip:#0b2f38; --chipb:#0a2830;
+}`,
+ amoled: `
+:root{
+ --bg:#000; --panel:#050505; --panel-2:#0a0a0a; --text:#f2f2f2; --muted:#bdbdbd;
+ --accent:#8ab4ff; --accent-2:#ff8ab4; --border:#111; --chip:#0b0b0b; --chipb:#070707;
+}`,
+ highcontrast: `
+:root{
+ --bg:#000; --panel:#000; --panel-2:#000; --text:#fff; --muted:#e6e6e6;
+ --accent:#00ffff; --accent-2:#ff00ff; --border:#fff; --chip:#000; --chipb:#000;
+}`
+ };
+
+ function applyTheme(name){
+ const css = THEMES[name] || THEMES.midnight;
+ THEME_STYLE.textContent = css;
+ $('#themeSelect').value = name in THEMES ? name : 'midnight';
+ }
+ function saveTheme(){ localStorage.setItem(THEME_KEY, $('#themeSelect').value); toast('Theme saved'); }
+ function resetTheme(){ localStorage.removeItem(THEME_KEY); applyTheme('midnight'); toast('Theme reset'); }
+
// --------------------- Initialize per tab ---------------------
+ // Theme boot
+ applyTheme(localStorage.getItem(THEME_KEY) || 'midnight');
+ on($('#themeSelect'), 'change', (e)=> applyTheme(e.target.value));
+ on($('#saveThemeBtn'), 'click', saveTheme);
+ on($('#resetThemeBtn'), 'click', resetTheme);
+
// CSS TAB
<% if (!tab) { %>
const cssEditor = makeEditor('#cssEd', '#cssHl', '#cssLines', {
@@ -619,7 +715,7 @@ nav { backdrop-filter: blur(16px) saturate(120%); }
on($('#reloadPreviewCss'), 'click', applyCssPreview);
// Live preview while typing
- on(cssEditor.ed, 'input', debounce(applyCssPreview, 60));
+ on(cssEditor.ed, 'input', debounce(applyCssPreview, 50));
// Save on Ctrl/Cmd+S
on(document, 'keydown', (e)=>{