You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ run_id }} — Diagram → TikZ</title>
<meta property="og:title" content="{{ run_id }} — Diagram → TikZ" />
<meta property="og:description" content="View and edit the LaTeX/TikZ code for diagram {{ run_id }}." />
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ base_path }}/d/{{ run_id }}" />
<meta property="og:image" content="{{ base_path }}/d/{{ run_id }}/files/preview.png" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ run_id }} — Diagram → TikZ" />
<meta name="twitter:description" content="View and edit the LaTeX/TikZ code for diagram {{ run_id }}." />
<meta name="twitter:image" content="{{ base_path }}/d/{{ run_id }}/files/preview.png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;700;800&family=JetBrains+Mono:wght@400;600&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />
<link rel="stylesheet" href="{{ base_path }}/public/styles.css" />
</head>
<body class="page page-edit">
<div class="topbar">
<a class="no-wrap" href="{{ base_path }}/">← New upload</a>
<div class="label run-id" title="{{ run_id }}">DIAGRAM: {{ run_id }}</div>
<div class="label no-wrap">MODEL: {{ convert_model }}</div>
</div>
<div class="container">
<div class="pane left">
<h2 class="pane-title">Standalone LaTeX/TikZ</h2>
<form action="{{ base_path }}/d/{{ run_id }}" method="post" class="edit-form">
<section class="field-group">
<h3 class="field-group-title">Edit instructions</h3>
<div class="prompt-row">
<input
id="instructions"
name="instructions"
autocomplete="off"
type="text"
placeholder="e.g. move labels closer, align boxes, add arrows"
/>
<button class="btn" type="submit">Apply edits</button>
</div>
<div class="hint">
Applies edits to the LaTeX only, then compiles. {% if last_edit_model %}Last edit model: {{
last_edit_model }}.{% endif %}
<br />Edit quota: {{ edit_remaining }}/{{ edit_limit }} left today.
</div>
</section>
<section class="field-group">
<h3 class="field-group-title">History</h3>
<select id="history-select">
<option value="current" selected>Current</option>
</select>
<div class="hint">Most recent first. Selecting an entry loads its LaTeX into the editor.</div>
</section>
<section class="field-group">
<h3 class="field-group-title">LaTeX Source</h3>
<textarea id="latex-source" name="latex" spellcheck="false" wrap="off">{{ tex }}</textarea>
</section>
<section class="field-group actions-group">
<div class="actions">
<button class="btn" type="submit" formaction="{{ base_path }}/d/{{ run_id }}/compile">
<i class="fa-solid fa-play icon" aria-hidden="true"></i>
<span>Compile</span>
</button>
{% if compile_warning %}
<div class="hint warning" id="compile-warning">{{ compile_warning }}</div>
{% endif %}
</div>
</section>
<section class="field-group danger-group">
<h3 class="field-group-title">Settings / Danger</h3>
<div class="actions">
<button class="btn danger" type="submit" formaction="{{ base_path }}/d/{{ run_id }}/delete">
<i class="fa-solid fa-trash-can icon" aria-hidden="true"></i>
<span>Delete Diagram</span>
</button>
</div>
</section>
</form>
</div>
<div class="pane right">
<h2 class="pane-title">Rendered preview</h2>
<div class="preview">
{% if png_url %}
<img src="{{ png_url }}" alt="Rendered TikZ preview" />
{% else %}
<div class="label">No preview available.</div>
{% endif %}
</div>
<div class="downloads">
<a class="button" href="{{ download_original_url }}" download>
<i class="fa-solid fa-image icon" aria-hidden="true"></i>
<span>Original Image</span>
</a>
<a class="button" href="{{ download_tex_url }}" download>
<i class="fa-solid fa-file-code icon" aria-hidden="true"></i>
<span>LaTeX (.tex)</span>
</a>
<a class="button" href="{{ download_pdf_url }}" download>
<i class="fa-solid fa-file-pdf icon" aria-hidden="true"></i>
<span>PDF</span>
</a>
<a class="button" href="{{ download_svg_url }}" download>
<i class="fa-solid fa-vector-square icon" aria-hidden="true"></i>
<span>SVG</span>
</a>
<a class="button" href="{{ download_png_url }}" download>
<i class="fa-solid fa-file-image icon" aria-hidden="true"></i>
<span>PNG</span>
</a>
</div>
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
</div>
</div>
</body>
<script>
;(() => {
const basePath = "{{ base_path }}"
const runId = "{{ run_id }}"
const editForm = document.querySelector(".edit-form")
const instructionsInput = document.getElementById("instructions")
const historySelect = document.getElementById("history-select")
const latexArea = document.getElementById("latex-source")
const compileWarning = document.getElementById("compile-warning")
const currentTex = latexArea ? latexArea.value : ""
let historyEntries = []
// Validate edit/compile/delete actions.
if (editForm && instructionsInput) {
editForm.addEventListener("submit", e => {
const submitter = e.submitter
const action = (submitter && submitter.getAttribute("formaction")) || ""
const isAlternateAction = !!action
const isDeleteAction = action.endsWith("/delete")
const val = (instructionsInput.value || "").trim()
if (isDeleteAction) {
if (!confirm("Delete this run and all its files?")) {
e.preventDefault()
}
return
}
if (!isAlternateAction && !val) {
e.preventDefault()
}
})
}
const formatTs = ts => {
if (!ts) return "unknown time"
const d = new Date(ts * 1000)
return d.toISOString().replace("T", " ").slice(0, 19)
}
const renderOptions = () => {
if (!historySelect) return
const frag = document.createDocumentFragment()
const current = document.createElement("option")
current.value = "current"
current.textContent = "Current"
frag.appendChild(current)
historyEntries.forEach((entry, idx) => {
const opt = document.createElement("option")
opt.value = String(idx)
const statusLabel = entry.status === "error" ? " (error)" : ""
const label = entry.summary && entry.summary.trim() ? entry.summary.trim() : entry.action || "edit"
opt.textContent = `${label} @ ${formatTs(entry.ts)}${statusLabel}`
frag.appendChild(opt)
})
historySelect.innerHTML = ""
historySelect.appendChild(frag)
}
const loadHistory = async () => {
if (!historySelect) return
try {
const resp = await fetch(`${basePath}/d/${runId}/history`)
if (!resp.ok) return
const data = await resp.json()
historyEntries = Array.isArray(data.entries) ? data.entries : []
renderOptions()
} catch (err) {
console.error("history.load.failed", err)
}
}
if (historySelect && latexArea) {
historySelect.addEventListener("change", () => {
const value = historySelect.value
if (value === "current") {
latexArea.value = currentTex
return
}
const idx = Number(value)
if (Number.isNaN(idx) || idx < 0 || idx >= historyEntries.length) {
return
}
const entry = historyEntries[idx]
latexArea.value = entry?.latex || ""
})
loadHistory()
}
if (compileWarning) {
setTimeout(() => {
compileWarning.style.display = "none"
}, 3500)
}
})()
</script>
</html>