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
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>
|