diff --git a/app.py b/app.py index 33b2c84..a52638f 100644 --- a/app.py +++ b/app.py @@ -140,7 +140,6 @@ HTML = """ --text: #111; --border: #ddd; --btn-bg: #fff; - --btn-hover: #f0f0f0; --gutter-bg: #f8fafc; --gutter-border: #eee; --gutter-color: #9ca3af; @@ -152,7 +151,6 @@ HTML = """ --text: #F0F0F0; --border: #3a3b45; --btn-bg: #2a2b33; - --btn-hover: #35363f; --gutter-bg: #1e1f28; --gutter-border: #2e2f3a; --gutter-color: #6b7280; @@ -164,9 +162,8 @@ HTML = """ body { font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji","Segoe UI Emoji"; max-width: 1000px; margin: 0 auto; padding: 1rem; display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; } header { display:flex; justify-content:space-between; align-items:center; margin-bottom: .5rem; flex-shrink: 0; } - a,button { padding:.35rem 0; text-decoration:none; border:1px solid var(--border); border-radius:4px; background:var(--btn-bg); color:var(--text); cursor:pointer; font-size:.9rem; font-weight:bold; min-width:2rem; text-align:center; display:inline-block; } - a:hover,button:hover { background:var(--btn-hover); } - #lock-btn { font-weight:normal; } + a,button { padding:.35rem .6rem; text-decoration:none; border:1px solid var(--border); border-radius:4px; background:var(--btn-bg); color:var(--text); cursor:pointer; } + #newpad { background:#000; color:#fff; border:1px solid #000; font-weight:bold; } #status { font-size:.9rem; opacity:.7; margin-left:.5rem; } #status::before { content: "●"; margin-right: .3rem; color: #ef4444; } #status.connected::before { color: #22c55e; } @@ -178,6 +175,8 @@ HTML = """ user-select:none; min-width: 3ch; white-space: pre; height: 100%; overflow: hidden; } #t { padding:.5rem .75rem; width:100%; height: 100%; resize: none; border:0; outline:0; overflow:auto; white-space: pre; background:var(--bg); color:var(--text); } + #newpad { margin-left:.5rem; } + #info { margin-left:.5rem; font-size: 0.8rem; } pre { margin: 0; } /* Password protection */ #lock-btn.locked { background:#dbeafe; border-color:#3b82f6; } @@ -207,11 +206,11 @@ HTML = """ disconnected
- - + - - - β“˜ + + + New pad + Info +
@@ -247,7 +246,7 @@ const rand = () => Math.random().toString(36).slice(2, 6); // 4 chars // Theme function applyTheme(dark) { document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light'); - $('#theme-btn').textContent = dark ? 'β˜€' : '☾'; + $('#theme-btn').textContent = dark ? 'Light' : 'Dark'; } function toggleTheme() { const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; @@ -274,7 +273,6 @@ $("#padname").textContent = "/"+docId+"/"; let ws, ver = 0, clientId = Math.random().toString(36).slice(2), debounce; let isProtected = false, isAuthed = false; -const urlPw = new URLSearchParams(location.search).get("pw") || ""; // --- Line numbers --- const ta = $("#t"); @@ -330,11 +328,11 @@ function removePassword() { function updateLockBtn() { const btn = $("#lock-btn"); if (isProtected) { - btn.textContent = "πŸ”’οΈŽ"; + btn.textContent = "Locked"; btn.classList.add("locked"); btn.title = "Password protected – click to manage"; } else { - btn.textContent = "πŸ”’οΈŽ"; + btn.textContent = "Lock"; btn.classList.remove("locked"); btn.title = "No password – click to set one"; } @@ -383,11 +381,7 @@ function connect(){ updateLockBtn(); if (msg.peers !== undefined) { const el = $("#peers"); el.textContent = msg.peers; el.style.display = "inline"; } if (isProtected && !isAuthed) { - if (urlPw) { - ws.send(JSON.stringify({type: "auth", password: urlPw})); - } else { - showOverlay(); - } + showOverlay(); } else { isAuthed = true; hideOverlay(); @@ -399,17 +393,15 @@ function connect(){ // Real init with content follows immediately from server } else if (msg.type === "error") { if (!isAuthed) { - showOverlay(); const errEl = $("#auth-error"); errEl.textContent = msg.message; errEl.style.display = "block"; } } else if (msg.type === "update" && msg.ver > ver && msg.clientId !== clientId) { const {selectionStart:s, selectionEnd:e} = ta; - const oldText = ta.value; ta.value = msg.text; ver = msg.ver; updateGutter(); - ta.selectionStart = adjustCursor(oldText, msg.text, s); - ta.selectionEnd = adjustCursor(oldText, msg.text, e); + ta.selectionStart = Math.min(s, ta.value.length); + ta.selectionEnd = Math.min(e, ta.value.length); } else if (msg.type === "peers_changed") { const el = $("#peers"); el.textContent = msg.count; @@ -434,7 +426,7 @@ async function copyToClipboard() { await navigator.clipboard.writeText(ta.value); const btn = $("#copy"); const original = btn.textContent; - btn.textContent = "βœ“"; + btn.textContent = "Copied!"; setTimeout(() => { btn.textContent = original; }, 1500); } catch (err) { // Fallback for older browsers @@ -442,30 +434,11 @@ async function copyToClipboard() { document.execCommand('copy'); const btn = $("#copy"); const original = btn.textContent; - btn.textContent = "βœ“"; + btn.textContent = "Copied!"; setTimeout(() => { btn.textContent = original; }, 1500); } } -// Adjust cursor position after a remote text update. -// Finds the single changed region (common prefix + suffix), -// then shifts the cursor accordingly: -// - change is after cursor β†’ no movement -// - change is before cursor β†’ shift by length delta -// - cursor was inside the changed region β†’ place at end of new content -function adjustCursor(oldText, newText, pos) { - let start = 0; - const minLen = Math.min(oldText.length, newText.length); - while (start < minLen && oldText[start] === newText[start]) start++; - if (pos <= start) return pos; // change is entirely after cursor - let oldEnd = oldText.length, newEnd = newText.length; - while (oldEnd > start && newEnd > start && oldText[oldEnd - 1] === newText[newEnd - 1]) { - oldEnd--; newEnd--; - } - if (pos >= oldEnd) return pos + (newEnd - oldEnd); // change is before cursor - return newEnd; // cursor was inside changed region -} - connect(); // Handle Tab key to insert 4 spaces instead of navigation