boothifier/test/edit.html
2025-09-28 23:18:18 -07:00

266 lines
7.8 KiB
HTML

<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Edit file</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/css/global-style.css" rel="stylesheet">
<link href="/css/nav.css" rel="stylesheet">
<style>
/* --- Shared theme (matches File Manager) --- */
:root{
--primary-color:#007bff;
--primary-hover:#0056b3;
--background-color:#f4f7f6;
--card-background:#ffffff;
--text-color:#333;
--border-color:#ddd;
--border-radius:8px;
--box-shadow:0 4px 6px rgba(0,0,0,.1);
}
html, body {
height: 100%;
}
body{
margin:0;
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
background:var(--background-color);
color:var(--text-color);
display:flex;
flex-direction:column;
}
/* Keep the page breathable but allow full-width editor */
.main-container{
flex:1;
padding: 1rem;
display:flex;
flex-direction:column;
gap:1rem;
}
h1{
text-align:center;
color:var(--primary-color);
margin: .25rem 0 0;
font-size:1.5rem;
}
/* Card that fills the viewport height */
.card{
background:var(--card-background);
border-radius:var(--border-radius);
box-shadow:var(--box-shadow);
border:1px solid var(--border-color);
display:flex;
flex-direction:column;
min-height: 0; /* Allow children to flex */
height: calc(100vh - 160px); /* fills most of the viewport; adjust if navbar taller */
/* On very small screens fallback a bit shorter */
}
.card-header{
font-weight:600;
padding: .9rem 1.2rem;
border-bottom:1px solid var(--border-color);
background:#f9f9f9;
border-top-left-radius:var(--border-radius);
border-top-right-radius:var(--border-radius);
}
/* Editor shell: sticky toolbar + flex textarea */
.editor-toolbar{
display:flex;
gap:.75rem;
align-items:center;
padding: .75rem 1rem;
border-bottom:1px solid var(--border-color);
background: #fff;
position: sticky;
top: 0; /* Sticks under the navbar within the card */
z-index: 2;
}
.editor-toolbar label{
font-size:.95rem;
white-space:nowrap;
}
.input-text{
flex:1;
min-width: 0;
padding:.6rem .7rem;
border:1px solid var(--border-color);
border-radius:var(--border-radius);
font-size: .95rem;
background:#fff;
}
.btn{
padding:.65rem 1.1rem;
border:none;
border-radius:var(--border-radius);
background:var(--primary-color);
color:#fff;
font-weight:600;
cursor:pointer;
transition: background-color .15s ease-in-out;
}
.btn:hover{ background:var(--primary-hover); }
.btn.secondary{
background:#e9ecef; color:#222;
}
.btn.secondary:hover{ background:#dfe3e7; }
/* The editor itself fills remaining space */
.editor-area{
flex:1;
min-height: 0;
display:flex;
padding: 0 1rem 1rem;
}
textarea{
width:100%;
height:100%;
resize:none; /* Keep layout stable; user gets max space already */
border:1px solid var(--border-color);
border-radius:var(--border-radius);
padding: .9rem 1rem;
box-sizing:border-box;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 14px;
line-height: 1.5;
outline:none;
background:#fff;
}
/* Compact helper row for mobile */
@media (max-width: 640px){
.editor-toolbar{
flex-wrap:wrap;
}
.btn{
flex:0 0 auto;
}
}
</style>
</head>
<body>
<div id="navbar"></div>
<div class="main-container">
<h1>Edit File</h1>
<div class="card">
<div class="card-header">Editor</div>
<form name="edit-file" action="/files/save" onsubmit="return onSubmitEdit(event)" style="display:flex; flex-direction:column; min-height:0; flex:1;">
<!-- Sticky toolbar -->
<div class="editor-toolbar">
<label for="save-path">File Name:</label>
<input type="text" id="save-path" class="input-text" value="{{SAVE_PATH_INPUT}}" autocomplete="off" spellcheck="false">
<button type="submit" class="btn" id="submit-edit">Save</button>
<button type="button" class="btn secondary" id="cancel">Cancel</button>
</div>
<!-- Stretchy editor space -->
<div class="editor-area">
<textarea name="edit-textarea" id="edit-textarea" wrap="off" placeholder="File contents will appear here..."></textarea>
</div>
</form>
</div>
</div>
<script>
// Load navbar
fetch('/www/navbar.html')
.then(r => r.text())
.then(html => { document.getElementById('navbar').innerHTML = html; });
// Elements
const txt = document.getElementById('edit-textarea');
const pathInput = document.getElementById('save-path');
const btnCancel = document.getElementById('cancel');
// Track unsaved changes
let dirty = false;
txt.addEventListener('input', () => dirty = true);
pathInput.addEventListener('input', () => dirty = true);
window.addEventListener('beforeunload', (e) => {
if (dirty) {
e.preventDefault();
e.returnValue = '';
}
});
// Keyboard shortcut: Ctrl/Cmd + S
window.addEventListener('keydown', (e) => {
const sKey = (e.key === 's' || e.key === 'S');
if (sKey && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
document.getElementById('submit-edit').click();
}
});
// Cancel -> back to files
btnCancel.addEventListener('click', () => {
if (!dirty || confirm('Discard unsaved changes?')) {
window.location.href = '/files';
}
});
// Initial file load
window.addEventListener('load', loadEditFile);
function loadEditFile(){
const savePath = pathInput.value;
if (savePath !== "/new.txt") {
fetch(savePath)
.then(response => {
if (!response.ok) throw new Error('Failed to fetch file');
// Try to keep binary files from corrupting layout; still load as text.
return response.text();
})
.then(contents => {
txt.value = contents;
pathInput.disabled = true;
dirty = false;
})
.catch(console.error);
} else {
// New file: keep editable path
pathInput.disabled = false;
}
}
// Validation used by submit
function validateFilename(){
const allowedExtensions = "{{ALLOWED_EXTENSIONS_EDIT}}";
const val = pathInput.value || "";
const dotIndex = val.lastIndexOf(".") + 1;
const ext = val.substring(dotIndex);
const beginsWithSlash = val.startsWith("/");
if (!val) { alert("Enter the file name!\n(e.g. /new.txt)"); return false; }
if (!beginsWithSlash) { alert("The slash at the beginning of the file is missing!"); return false; }
if (dotIndex === 0 || !ext) { alert("The extension is missing at the end of the file!"); return false; }
if (allowedExtensions.indexOf(ext) === -1) { alert("Extension not supported!"); return false; }
return true;
}
// Submit handler: POST with a standard form submit (keeps server behavior)
function onSubmitEdit(e){
if (!validateFilename()) { e.preventDefault(); return false; }
// Create a transient hidden textarea named exactly as the backend expects if needed.
// Here the main <textarea> already has the correct name attribute, so just proceed.
dirty = false; // prevent unload warning on successful submit
return true;
}
</script>
</body>
</html>