From 94a8907f025ffc4bef58a6ea03e1f0623bb68b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 20 Jul 2026 22:58:41 +0200 Subject: [PATCH] fixes in ux --- src/app.rs | 32 ++++++++++++++++++++++++++------ static/home.html | 10 +++++----- static/js/note.js | 6 +++--- static/js/pad.js | 6 +++--- static/js/workspace.js | 3 ++- static/note.html | 4 ++-- static/pad.html | 4 ++-- static/styles.css | 19 +++++++++++++++---- static/workspace.html | 4 ++-- 9 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/app.rs b/src/app.rs index cbb9c9e..098fbdf 100644 --- a/src/app.rs +++ b/src/app.rs @@ -55,6 +55,8 @@ pub fn router(state: SharedState, static_dir: &str, upload_max_size_bytes: usize "/ws/{workspace_slug}/{note_slug}", get(websocket::upgrade), ) + .route("/static", get(static_not_found)) + .route("/static/{*path}", get(static_not_found)) .nest_service("/assets", ServeDir::new(static_dir)) .fallback(not_found) .layer(DefaultBodyLimit::max(upload_max_size_bytes.saturating_add(1024 * 1024))) @@ -75,7 +77,11 @@ async fn pad( Path(slug): Path, ) -> Response { match db::find_pad(&state.db, &slug).await { - Ok(Some(_)) => versioned_html(include_str!("../static/pad.html"), &state.asset_version), + Ok(Some(pad)) => { + let html = include_str!("../static/pad.html") + .replace("__PAD_TITLE__", &escape_html(&pad.title)); + versioned_html(&html, &state.asset_version) + }, Ok(None) => error_response( StatusCode::NOT_FOUND, "404", @@ -119,10 +125,11 @@ async fn workspace( Path(workspace_slug): Path, ) -> Response { match db::find_workspace(&state.db, &workspace_slug).await { - Ok(Some(_)) => versioned_html( - include_str!("../static/workspace.html"), - &state.asset_version, - ), + Ok(Some(workspace)) => { + let html = include_str!("../static/workspace.html") + .replace("__WORKSPACE_TITLE__", &escape_html(&workspace.title)); + versioned_html(&html, &state.asset_version) + }, Ok(None) => error_response( StatusCode::NOT_FOUND, "404", @@ -163,7 +170,11 @@ async fn note( }; match db::find_note(&state.db, workspace.id, ¬e_slug).await { - Ok(Some(_)) => versioned_html(include_str!("../static/note.html"), &state.asset_version), + Ok(Some(note)) => { + let html = include_str!("../static/note.html") + .replace("__NOTE_TITLE__", &escape_html(¬e.title)); + versioned_html(&html, &state.asset_version) + }, Ok(None) => error_response( StatusCode::NOT_FOUND, "404", @@ -180,6 +191,15 @@ async fn note( } } +async fn static_not_found() -> Response { + let mut response = (StatusCode::NOT_FOUND, "404").into_response(); + response.headers_mut().insert( + header::CONTENT_TYPE, + HeaderValue::from_static("text/plain; charset=utf-8"), + ); + response +} + async fn not_found(State(state): State) -> Response { error_response( StatusCode::NOT_FOUND, diff --git a/static/home.html b/static/home.html index 44e58c1..9f80dcd 100644 --- a/static/home.html +++ b/static/home.html @@ -8,12 +8,12 @@ - - + +
-

New workspace

-

Create a quick note or a workspace with multiple notes.

+

Write. Share. Collaborate.

+

Create a standalone note or organize multiple notes in a workspace.

@@ -59,6 +59,6 @@
- + diff --git a/static/js/note.js b/static/js/note.js index f3159c8..906cdd2 100644 --- a/static/js/note.js +++ b/static/js/note.js @@ -16,8 +16,8 @@ function toast(text){const el=document.querySelector("#toast");el.textContent=te function setStatus(kind,text){document.querySelector("#status-dot").className=`status__dot${kind?` is-${kind}`:""}`;document.querySelector("#status-text").textContent=text;} function updateAddressLabel(){document.querySelector("#note-url").textContent=`${location.pathname}${location.search}`;} async function renderMermaid(){const nodes=preview.querySelectorAll(".mermaid");if(!nodes.length)return;try{const {default:mermaid}=await import("https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs");mermaid.initialize({startOnLoad:false,theme:"dark",securityLevel:"strict"});await mermaid.run({nodes:[...nodes]});}catch{nodes.forEach(n=>n.insertAdjacentHTML("beforebegin",'

Failed to load Mermaid.

'));}} -function renderGutter(){const lines=editor.value.split("\n");owners=owners.slice(0,lines.length);while(owners.length`
${i+1}
`).join("");document.body.classList.toggle("hide-line-numbers",!lineToggle.checked);} -function escapeHtml(v){return String(v).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));} +function renderGutter(){const lines=editor.value.split("\n");owners=owners.slice(0,lines.length);while(owners.length{const owner=owners[i]||"";const startsBlock=Boolean(owner)&&owner!==owners[i-1];const label=startsBlock?`${escapeHtml(owner)}`:"";return `
${label}${i+1}
`;}).join("");document.body.classList.toggle("hide-line-numbers",!lineToggle.checked);} +function escapeHtml(v){return String(v).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));}function formatDate(value){if(value==null||value==="")return "—";let raw=String(value).trim();if(/^\d+$/.test(raw)){const number=Number(raw);raw=String(raw).length<=10?number*1000:number;}else if(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)){raw=raw.replace(" ","T")+"Z";}const date=new Date(raw);return Number.isNaN(date.getTime())?"—":date.toLocaleString("en-US");} function render(){if(uiState.mode==="markdown"){preview.classList.remove("preview--raw");preview.innerHTML=renderMarkdown(editor.value);document.querySelector("#preview-label").textContent="Markdown + Mermaid preview";renderMermaid();}else{preview.classList.add("preview--raw");preview.textContent=editor.value;document.querySelector("#preview-label").textContent="Source text";}document.querySelector("#characters").textContent=`${editor.value.length} characters`;document.querySelector("#words").textContent=`${editor.value.trim()?editor.value.trim().split(/\s+/).length:0} words`;renderGutter();} function applyUi({write=false,replace=false}={}){editorWorkspace.className=`workspace view-${uiState.view}`;document.querySelectorAll("[data-view]").forEach(b=>{const a=b.dataset.view===uiState.view;b.classList.toggle("active",a);b.setAttribute("aria-pressed",String(a));});const markdown=uiState.mode==="markdown";modeToggle.classList.toggle("active",markdown);modeToggle.textContent=markdown?"Markdown":"Text";render();if(write)writeEditorState(uiState,{replace});updateAddressLabel();} function applyRemote(content,ownerMap){if(content===editor.value&&ownerMap==null)return;const start=editor.selectionStart,end=editor.selectionEnd;applyingRemote=true;editor.value=content;try{owners=JSON.parse(ownerMap||"[]");}catch{owners=[];}editor.setSelectionRange(Math.min(start,content.length),Math.min(end,content.length));applyingRemote=false;render();} @@ -29,7 +29,7 @@ window.addEventListener("popstate",()=>{uiState=readEditorState();applyUi();});w document.querySelector("#publish-page").addEventListener("click",async()=>{try{const result=await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/publish`,{method:"POST",body:JSON.stringify({password:password||null})});const url=new URL(result.url,location.origin).href;await copyText(url);toast("Page link copied");window.open(url,"_blank","noopener");}catch(error){toast(error.message);}}); editor.addEventListener("scroll",()=>{gutter.scrollTop=editor.scrollTop;});editor.addEventListener("input",()=>{const newLines=editor.value.split("\n").length;const cursorLine=editor.value.slice(0,editor.selectionStart).split("\n").length-1;while(owners.lengthsocket?.update(editor.value,JSON.stringify(owners)),250);}); document.querySelector("#password-form").addEventListener("submit",e=>{e.preventDefault();password=document.querySelector("#open-password").value;setPassword(workspaceSlug,password);document.querySelector("#password-error").textContent="";connect();}); -const historyPanel=document.querySelector("#history-panel");document.querySelector("#history-button").addEventListener("click",async()=>{historyPanel.classList.add("open");historyPanel.setAttribute("aria-hidden","false");document.body.classList.add("history-open");const list=document.querySelector("#history-list");list.innerHTML='

Loading…

';try{const revisions=await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/history`,{method:"POST",body:JSON.stringify({password:password||null})});list.innerHTML=revisions.length?revisions.map((r,i)=>{const snippet=escapeHtml(r.content.trim().split("\n").slice(0,3).join(" · ").slice(0,150)||"Empty note");const author=r.author||"Unknown author";return `
${escapeHtml(author)}

${snippet}

`;}).join(""):'

No history yet.

';for(const r of revisions){list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click",()=>{const el=list.querySelector(`#preview-${r.id}`);el.hidden=!el.hidden;el.textContent=r.content;});list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click",async()=>{await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/restore`,{method:"POST",body:JSON.stringify({password:password||null,revision_id:r.id})});toast("Version restored");});}}catch(e){list.innerHTML=`

${escapeHtml(e.message)}

`;}});document.querySelector("#close-history").addEventListener("click",()=>{historyPanel.classList.remove("open");historyPanel.setAttribute("aria-hidden","true");document.body.classList.remove("history-open");}); +const historyPanel=document.querySelector("#history-panel");document.querySelector("#history-button").addEventListener("click",async()=>{historyPanel.classList.add("open");historyPanel.setAttribute("aria-hidden","false");document.body.classList.add("history-open");const list=document.querySelector("#history-list");list.innerHTML='

Loading…

';try{const revisions=await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/history`,{method:"POST",body:JSON.stringify({password:password||null})});list.innerHTML=revisions.length?revisions.map((r,i)=>{const snippet=escapeHtml(r.content.trim().split("\n").slice(0,3).join(" · ").slice(0,150)||"Empty note");const author=r.author||"Unknown author";return `
${escapeHtml(author)}

${snippet}

`;}).join(""):'

No history yet.

';for(const r of revisions){list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click",()=>{const el=list.querySelector(`#preview-${r.id}`);el.hidden=!el.hidden;el.textContent=r.content;});list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click",async()=>{await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/restore`,{method:"POST",body:JSON.stringify({password:password||null,revision_id:r.id})});toast("Version restored");});}}catch(e){list.innerHTML=`

${escapeHtml(e.message)}

`;}});document.querySelector("#close-history").addEventListener("click",()=>{historyPanel.classList.remove("open");historyPanel.setAttribute("aria-hidden","true");document.body.classList.remove("history-open");}); document.querySelector("#upload-button").addEventListener("click",()=>document.querySelector("#file-input").click());document.querySelector("#file-input").addEventListener("change",async e=>{const file=e.target.files[0];if(!file)return;const form=new FormData();form.append("password",password||"");form.append("file",file);try{const result=await api(`/api/workspaces/${encodeURIComponent(workspaceSlug)}/notes/${encodeURIComponent(noteSlug)}/files`,{method:"POST",body:form,headers:{}});const image=file.type.startsWith("image/");const text=image?`![${file.name}](${result.url})`:`[${file.name}](${result.url})`;editor.setRangeText(text,editor.selectionStart,editor.selectionEnd,"end");editor.dispatchEvent(new Event("input"));toast("File uploaded");}catch(err){toast(err.message);}e.target.value="";}); window.addEventListener("error",event=>{setStatus("offline","Application error");console.error(event.error||event.message);}); window.addEventListener("unhandledrejection",event=>{setStatus("offline","Application error");console.error(event.reason);}); diff --git a/static/js/pad.js b/static/js/pad.js index 8055e22..19eab58 100644 --- a/static/js/pad.js +++ b/static/js/pad.js @@ -16,8 +16,8 @@ function toast(text){const el=document.querySelector("#toast");el.textContent=te function setStatus(kind,text){document.querySelector("#status-dot").className=`status__dot${kind?` is-${kind}`:""}`;document.querySelector("#status-text").textContent=text;} function updateAddressLabel(){document.querySelector("#pad-url").textContent=`${location.pathname}${location.search}`;} async function renderMermaid(){const nodes=preview.querySelectorAll(".mermaid");if(!nodes.length)return;try{const {default:mermaid}=await import("https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs");mermaid.initialize({startOnLoad:false,theme:"dark",securityLevel:"strict"});await mermaid.run({nodes:[...nodes]});}catch{nodes.forEach(n=>n.insertAdjacentHTML("beforebegin",'

Failed to load Mermaid.

'));}} -function renderGutter(){const lines=editor.value.split("\n");owners=owners.slice(0,lines.length);while(owners.length`
${i+1}
`).join("");document.body.classList.toggle("hide-line-numbers",!lineToggle.checked);} -function escapeHtml(v){return String(v).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));} +function renderGutter(){const lines=editor.value.split("\n");owners=owners.slice(0,lines.length);while(owners.length{const owner=owners[i]||"";const startsBlock=Boolean(owner)&&owner!==owners[i-1];const label=startsBlock?`${escapeHtml(owner)}`:"";return `
${label}${i+1}
`;}).join("");document.body.classList.toggle("hide-line-numbers",!lineToggle.checked);} +function escapeHtml(v){return String(v).replace(/[&<>"']/g,c=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));}function formatDate(value){if(value==null||value==="")return "—";let raw=String(value).trim();if(/^\d+$/.test(raw)){const number=Number(raw);raw=String(raw).length<=10?number*1000:number;}else if(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)){raw=raw.replace(" ","T")+"Z";}const date=new Date(raw);return Number.isNaN(date.getTime())?"—":date.toLocaleString("en-US");} function render(){if(uiState.mode==="markdown"){preview.classList.remove("preview--raw");preview.innerHTML=renderMarkdown(editor.value);document.querySelector("#preview-label").textContent="Markdown + Mermaid preview";renderMermaid();}else{preview.classList.add("preview--raw");preview.textContent=editor.value;document.querySelector("#preview-label").textContent="Source text";}document.querySelector("#characters").textContent=`${editor.value.length} characters`;document.querySelector("#words").textContent=`${editor.value.trim()?editor.value.trim().split(/\s+/).length:0} words`;renderGutter();} function applyUi({write=false,replace=false}={}){editorWorkspace.className=`workspace view-${uiState.view}`;document.querySelectorAll("[data-view]").forEach(b=>{const a=b.dataset.view===uiState.view;b.classList.toggle("active",a);b.setAttribute("aria-pressed",String(a));});const markdown=uiState.mode==="markdown";modeToggle.classList.toggle("active",markdown);modeToggle.textContent=markdown?"Markdown":"Text";render();if(write)writeEditorState(uiState,{replace});updateAddressLabel();} function applyRemote(content,ownerMap){if(content===editor.value&&ownerMap==null)return;const start=editor.selectionStart,end=editor.selectionEnd;applyingRemote=true;editor.value=content;try{owners=JSON.parse(ownerMap||"[]");}catch{owners=[];}editor.setSelectionRange(Math.min(start,content.length),Math.min(end,content.length));applyingRemote=false;render();} @@ -29,6 +29,6 @@ window.addEventListener("popstate",()=>{uiState=readEditorState();applyUi();});w document.querySelector("#publish-page").addEventListener("click",async()=>{try{const result=await api(`/api/pads/${encodeURIComponent(slug)}/publish`,{method:"POST",body:JSON.stringify({password:password||null})});const url=new URL(result.url,location.origin).href;await copyText(url);toast("Page link copied");window.open(url,"_blank","noopener");}catch(error){toast(error.message);}}); editor.addEventListener("scroll",()=>{gutter.scrollTop=editor.scrollTop;});editor.addEventListener("input",()=>{const newLines=editor.value.split("\n").length;const cursorLine=editor.value.slice(0,editor.selectionStart).split("\n").length-1;while(owners.lengthsocket?.update(editor.value,JSON.stringify(owners)),250);}); document.querySelector("#password-form").addEventListener("submit",e=>{e.preventDefault();password=document.querySelector("#open-password").value;sessionStorage.setItem(`rustpad:pad:${slug}:password`,password);document.querySelector("#password-error").textContent="";connect();}); -const historyPanel=document.querySelector("#history-panel");document.querySelector("#history-button").addEventListener("click",async()=>{historyPanel.classList.add("open");historyPanel.setAttribute("aria-hidden","false");document.body.classList.add("history-open");const list=document.querySelector("#history-list");list.innerHTML='

Loading…

';try{const revisions=await api(`/api/pads/${encodeURIComponent(slug)}/history`,{method:"POST",body:JSON.stringify({password:password||null})});list.innerHTML=revisions.length?revisions.map((r,i)=>{const snippet=escapeHtml(r.content.trim().split("\n").slice(0,3).join(" · ").slice(0,150)||"Empty note");const author=r.author||"Unknown author";return `
${escapeHtml(author)}

${snippet}

`;}).join(""):'

No history yet.

';for(const r of revisions){list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click",()=>{const el=list.querySelector(`#preview-${r.id}`);el.hidden=!el.hidden;el.textContent=r.content;});list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click",async()=>{await api(`/api/pads/${encodeURIComponent(slug)}/restore`,{method:"POST",body:JSON.stringify({password:password||null,revision_id:r.id})});toast("Version restored");});}}catch(e){list.innerHTML=`

${escapeHtml(e.message)}

`;}});document.querySelector("#close-history").addEventListener("click",()=>{historyPanel.classList.remove("open");historyPanel.setAttribute("aria-hidden","true");document.body.classList.remove("history-open");}); +const historyPanel=document.querySelector("#history-panel");document.querySelector("#history-button").addEventListener("click",async()=>{historyPanel.classList.add("open");historyPanel.setAttribute("aria-hidden","false");document.body.classList.add("history-open");const list=document.querySelector("#history-list");list.innerHTML='

Loading…

';try{const revisions=await api(`/api/pads/${encodeURIComponent(slug)}/history`,{method:"POST",body:JSON.stringify({password:password||null})});list.innerHTML=revisions.length?revisions.map((r,i)=>{const snippet=escapeHtml(r.content.trim().split("\n").slice(0,3).join(" · ").slice(0,150)||"Empty note");const author=r.author||"Unknown author";return `
${escapeHtml(author)}

${snippet}

`;}).join(""):'

No history yet.

';for(const r of revisions){list.querySelector(`[data-preview="${r.id}"]`)?.addEventListener("click",()=>{const el=list.querySelector(`#preview-${r.id}`);el.hidden=!el.hidden;el.textContent=r.content;});list.querySelector(`[data-revision="${r.id}"]`)?.addEventListener("click",async()=>{await api(`/api/pads/${encodeURIComponent(slug)}/restore`,{method:"POST",body:JSON.stringify({password:password||null,revision_id:r.id})});toast("Version restored");});}}catch(e){list.innerHTML=`

${escapeHtml(e.message)}

`;}});document.querySelector("#close-history").addEventListener("click",()=>{historyPanel.classList.remove("open");historyPanel.setAttribute("aria-hidden","true");document.body.classList.remove("history-open");}); document.querySelector("#upload-button").addEventListener("click",()=>document.querySelector("#file-input").click());document.querySelector("#file-input").addEventListener("change",async e=>{const file=e.target.files[0];if(!file)return;const form=new FormData();form.append("password",password||"");form.append("file",file);try{const result=await api(`/api/pads/${encodeURIComponent(slug)}/files`,{method:"POST",body:form,headers:{}});const image=file.type.startsWith("image/");const text=image?`![${file.name}](${result.url})`:`[${file.name}](${result.url})`;editor.setRangeText(text,editor.selectionStart,editor.selectionEnd,"end");editor.dispatchEvent(new Event("input"));toast("File uploaded");}catch(err){toast(err.message);}e.target.value="";}); initialize(); diff --git a/static/js/workspace.js b/static/js/workspace.js index 502b50c..0511080 100644 --- a/static/js/workspace.js +++ b/static/js/workspace.js @@ -1,8 +1,9 @@ import { api } from "@rustpad/api"; import { copyText } from "@rustpad/clipboard"; import { getPassword, setPassword } from "@rustpad/session"; const parts = location.pathname.split("/").filter(Boolean), slug = parts[1]; let info, password = getPassword(slug); const dialog = document.querySelector("#password-dialog"), notesList = document.querySelector("#notes-list"); function toast(text) { const el = document.querySelector("#toast"); el.textContent = text; el.classList.add("visible"); setTimeout(() => el.classList.remove("visible"), 1600); } -function renderNotes(notes) { notesList.innerHTML = notes.length ? notes.map(note => `

${escapeHtml(note.title)}

Updated: ${new Date(note.updated_at).toLocaleString("en-US")}

`).join("") : '

No notes yet.

'; } +function renderNotes(notes) { notesList.innerHTML = notes.length ? notes.map(note => `

${escapeHtml(note.title)}

Updated: ${formatDate(note.updated_at)}

`).join("") : '

No notes yet.

'; } function escapeHtml(v) { const e = document.createElement("div"); e.textContent = v; return e.innerHTML; } +function formatDate(value) { if (value == null || value === "") return "—"; let raw = String(value).trim(); if (/^\d+$/.test(raw)) { const number = Number(raw); raw = raw.length <= 10 ? number * 1000 : number; } else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?$/.test(raw)) { raw = raw.replace(" ", "T") + "Z"; } const date = new Date(raw); return Number.isNaN(date.getTime()) ? "—" : date.toLocaleString("en-US"); } async function openWorkspace() { try { const data = await api(`/api/workspaces/${encodeURIComponent(slug)}/open`, { method: "POST", body: JSON.stringify({ password: password || null }) }); info = data.workspace; document.querySelector("#workspace-title").textContent = info.title; document.querySelector("#workspace-url").textContent = location.pathname; document.title = `${info.title} · RustPad`; renderNotes(data.notes); if (dialog.open) dialog.close(); } catch (e) { if (info?.protected || e.message.toLowerCase().includes("password")) { document.querySelector("#password-error").textContent = e.message; if (!dialog.open) dialog.showModal(); } else document.querySelector("#workspace-error").textContent = e.message; } } async function init() { try { info = await api(`/api/workspaces/${encodeURIComponent(slug)}`); document.querySelector("#workspace-title").textContent = info.title; document.querySelector("#workspace-url").textContent = location.pathname; if (info.protected && !password) dialog.showModal(); else openWorkspace(); } catch (e) { document.querySelector("#workspace-error").textContent = e.message; } } document.querySelector("#password-form").addEventListener("submit", e => { e.preventDefault(); password = document.querySelector("#open-password").value; setPassword(slug, password); openWorkspace(); }); diff --git a/static/note.html b/static/note.html index 3eaf26b..a267c95 100644 --- a/static/note.html +++ b/static/note.html @@ -1,5 +1,5 @@ -Note · RustPad -
RustPad

Loading…

Connecting…
+__NOTE_TITLE__ · RustPad +
RustPad

__NOTE_TITLE__

Connecting…
Editor
Markdown preview
0 characters · 0 words
Changes are saved automatically

What should we call you?

Your name will be shown next to changes and remembered on this device.

Protected workspace

Back
diff --git a/static/pad.html b/static/pad.html index e383171..7ba4dab 100644 --- a/static/pad.html +++ b/static/pad.html @@ -1,5 +1,5 @@ -Note · RustPad -
RustPad

Loading…

Connecting…
+__PAD_TITLE__ · RustPad +
RustPad

__PAD_TITLE__

Connecting…
Editor
Markdown preview
0 characters · 0 words
Changes are saved automatically

What should we call you?

Your name will be shown next to changes and remembered on this device.

Protected note

Back
diff --git a/static/styles.css b/static/styles.css index 2b42177..2c11e24 100644 --- a/static/styles.css +++ b/static/styles.css @@ -244,7 +244,18 @@ dialog::backdrop { background: rgba(4,6,9,.82); } /* Layout safeguards and home footer */ .app-header__main, .document-heading { min-width: 0; } .document-url { overflow-wrap: anywhere; } -.site-footer { width: min(1040px, calc(100% - 32px)); margin: -36px auto 28px; color: var(--muted-2); font-size: .76rem; text-align: center; } -.site-footer a { color: var(--muted); text-decoration: none; } -.site-footer a:hover { color: white; } -@media (max-width: 760px) { .site-footer { width: min(100% - 24px, 560px); margin-top: -20px; } } +.home-page { display: flex; min-height: 100vh; flex-direction: column; } +.home-page .home-layout { flex: 1 0 auto; } +.home-footer { flex: 0 0 auto; width: min(1040px, calc(100% - 32px)); margin: auto auto 28px; padding-top: 24px; color: var(--muted-2); font-size: .76rem; text-align: center; } +.home-footer a { color: var(--muted); text-decoration: none; } +.home-footer a:hover { color: white; } +.home-header { border-bottom-color: rgba(195, 91, 54, .28); background: linear-gradient(135deg, rgba(126, 48, 27, .28), rgba(51, 25, 18, .08) 58%, transparent); } +.home-brand { background: linear-gradient(110deg, #f0aa74 0%, #cf633d 48%, #8f3827 100%); -webkit-background-clip: text; background-clip: text; color: transparent; } +@media (max-width: 760px) { .home-footer { width: min(100% - 24px, 560px); margin-bottom: 20px; } } + + +/* Show an author once at the start of each contiguous ownership block. */ +.line-gutter div { position: relative; } +.line-owner-label { position: absolute; left: 7px; top: 50%; max-width: 88px; overflow: hidden; padding: 2px 6px; border: 1px solid color-mix(in srgb, var(--owner) 65%, transparent); border-radius: 999px; background: color-mix(in srgb, var(--owner) 18%, #0d1015); color: #eef1f5; font: 600 10px/1.2 system-ui, sans-serif; text-overflow: ellipsis; white-space: nowrap; transform: translateY(-50%); } +@media (min-width: 721px) { .line-gutter { width: 132px; } } +@media (max-width: 720px) { .line-owner-label { display: none; } } diff --git a/static/workspace.html b/static/workspace.html index fb3ead0..5522b18 100644 --- a/static/workspace.html +++ b/static/workspace.html @@ -1,5 +1,5 @@ -Workspace · RustPad -
RustPad

Loading…

+__WORKSPACE_TITLE__ · RustPad +
RustPad

__WORKSPACE_TITLE__

Notes

Select a note or create a new one.

Protected workspace

Cancel

New note