import { api } from "@rustpad/api";
import { copyText } from "@rustpad/clipboard";
import { getNickname, getPassword, setPassword } from "@rustpad/session";
import { askConfirm } from "./modal.js";
const parts = location.pathname.split("/").filter(Boolean);
const slug = parts[1];
let info;
let password = getPassword(slug);
const dialog = document.querySelector("#password-dialog");
const notesList = document.querySelector("#notes-list");
const notesViewKey = `rustpad:workspace:${slug}:notes-view`;
let notesView = localStorage.getItem(notesViewKey) === "table" ? "table" : "grid";
let notesCache = [];
function toast(text) {
const el = document.querySelector("#toast");
el.textContent = text;
el.classList.add("visible");
setTimeout(() => el.classList.remove("visible"), 1600);
}
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("pl-PL");
}
function setNotesView(view) {
notesView = view === "table" ? "table" : "grid";
localStorage.setItem(notesViewKey, notesView);
notesList.classList.toggle("notes-grid", notesView === "grid");
notesList.classList.toggle("notes-table", notesView === "table");
document.querySelectorAll("[data-notes-view]").forEach(button => {
const active = button.dataset.notesView === notesView;
button.classList.toggle("active", active);
button.setAttribute("aria-pressed", String(active));
});
}
function deleteButton(note, inline = false) {
const disabled = note.protected;
const classes = `note-delete-button${inline ? " note-delete-button--inline" : ""}`;
const reason = disabled ? "Protected notes cannot be deleted" : `Delete ${note.title}`;
return ``;
}
function renderNotes(notes = notesCache) {
notesCache = notes;
setNotesView(notesView);
if (!notes.length) {
notesList.innerHTML = '
No notes yet.
';
return;
}
if (notesView === "table") {
notesList.innerHTML = ``;
return;
}
notesList.innerHTML = notes.map(note => `
${escapeHtml(note.title)}
${note.protected ? 'Protected' : ''}
Created by: ${escapeHtml(note.created_by || "Unknown")}Updated: ${formatDate(note.updated_at)}
${deleteButton(note)}
`).join("");
}
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`;
notesCache = data.notes;
renderNotes();
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();
});
document.querySelector("#new-note-button").addEventListener("click", () => document.querySelector("#note-dialog").showModal());
document.querySelector("#cancel-note").addEventListener("click", () => document.querySelector("#note-dialog").close());
document.querySelector("#note-form").addEventListener("submit", async e => {
e.preventDefault();
const error = document.querySelector("#note-error"); error.textContent = "";
try {
const note = await api(`/api/workspaces/${encodeURIComponent(slug)}/notes`, {
method: "POST",
body: JSON.stringify({ name: document.querySelector("#note-name").value, password: password || null, protect: document.querySelector("#note-protect").checked, created_by: getNickname() || null })
});
location.assign(`${note.url}?view=split&mode=markdown`);
} catch (err) { error.textContent = err.message; }
});
notesList.addEventListener("click", async event => {
const button = event.target.closest("[data-delete-note]");
if (!button) return;
const title = button.dataset.noteTitle;
if (!await askConfirm(`Delete note “${title}”? This cannot be undone.`, { title: "Delete note", confirmText: "Delete", danger: true })) return;
button.disabled = true;
try {
await api(`/api/workspaces/${encodeURIComponent(slug)}/notes/${encodeURIComponent(button.dataset.deleteNote)}`, {
method: "DELETE", body: JSON.stringify({ password: password || null })
});
toast("Note deleted");
await openWorkspace();
} catch (error) { toast(error.message); button.disabled = false; }
});
document.querySelectorAll("[data-notes-view]").forEach(button => button.addEventListener("click", () => {
if (button.dataset.notesView === notesView) return;
notesView = button.dataset.notesView;
renderNotes();
}));
document.querySelector("#copy-workspace-link").addEventListener("click", async () => {
try { await copyText(new URL(location.pathname, location.origin).href); toast("Link copied"); }
catch (e) { toast(e.message); }
});
setNotesView(notesView);
init();