generate outside generated
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
512
generate.html
Normal file
512
generate.html
Normal file
@@ -0,0 +1,512 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Soleprint - Generate Configuration</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #1a1a2e;
|
||||
--surface: #16213e;
|
||||
--primary: #e94560;
|
||||
--text: #eaeaea;
|
||||
--text-muted: #8892b0;
|
||||
--border: #0f3460;
|
||||
--success: #4ecca3;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
font-family: "Segoe UI", system-ui, sans-serif;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 100vh;
|
||||
padding: 2rem;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
header h1 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
header p {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.card h2 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--primary);
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.3rem;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 0.6rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
.form-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
.section-title {
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-muted);
|
||||
margin: 1.5rem 0 0.8rem;
|
||||
padding-bottom: 0.3rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.checkbox-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.checkbox-group input {
|
||||
width: auto;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 0.7rem 1.5rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: #c73e54;
|
||||
}
|
||||
.btn-secondary {
|
||||
background: var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #1a4a7a;
|
||||
}
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
.preview {
|
||||
font-family: "Consolas", "Monaco", monospace;
|
||||
font-size: 0.85rem;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 1rem;
|
||||
max-height: 500px;
|
||||
overflow-y: auto;
|
||||
white-space: pre;
|
||||
}
|
||||
.preview .folder {
|
||||
color: #82aaff;
|
||||
}
|
||||
.preview .file {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.preview .comment {
|
||||
color: #546e7a;
|
||||
}
|
||||
.status {
|
||||
margin-top: 1rem;
|
||||
padding: 0.8rem;
|
||||
border-radius: 4px;
|
||||
display: none;
|
||||
}
|
||||
.status.success {
|
||||
display: block;
|
||||
background: rgba(78, 204, 163, 0.2);
|
||||
border: 1px solid var(--success);
|
||||
color: var(--success);
|
||||
}
|
||||
.status.error {
|
||||
display: block;
|
||||
background: rgba(233, 69, 96, 0.2);
|
||||
border: 1px solid var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
.help-text {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 0.3rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Soleprint Configuration Generator</h1>
|
||||
<p>
|
||||
Generate a new room configuration for wrapping a managed
|
||||
application
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h2>Configuration</h2>
|
||||
<form id="configForm">
|
||||
<div class="section-title">Room Settings</div>
|
||||
<div class="form-group">
|
||||
<label for="roomName">Room Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="roomName"
|
||||
placeholder="e.g., myproject"
|
||||
required
|
||||
/>
|
||||
<div class="help-text">
|
||||
Unique identifier for this configuration
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">Framework Branding</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="frameworkName"
|
||||
>Framework Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="frameworkName"
|
||||
value="soleprint"
|
||||
placeholder="soleprint"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="frameworkIcon">Icon</label>
|
||||
<input
|
||||
type="text"
|
||||
id="frameworkIcon"
|
||||
value=""
|
||||
placeholder="optional"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">System Names</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="arteryName">Data Flow System</label>
|
||||
<input
|
||||
type="text"
|
||||
id="arteryName"
|
||||
value="artery"
|
||||
placeholder="artery"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="atlasName"
|
||||
>Documentation System</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="atlasName"
|
||||
value="atlas"
|
||||
placeholder="atlas"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="stationName">Execution System</label>
|
||||
<input
|
||||
type="text"
|
||||
id="stationName"
|
||||
value="station"
|
||||
placeholder="station"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="section-title">
|
||||
Managed Application (Optional)
|
||||
</div>
|
||||
<div class="checkbox-group form-group">
|
||||
<input type="checkbox" id="hasManaged" />
|
||||
<label for="hasManaged"
|
||||
>Include managed application</label
|
||||
>
|
||||
</div>
|
||||
<div id="managedFields" style="display: none">
|
||||
<div class="form-group">
|
||||
<label for="managedName"
|
||||
>Managed App Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="managedName"
|
||||
placeholder="e.g., myapp"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="backendPath"
|
||||
>Backend Repo Path</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="backendPath"
|
||||
placeholder="/path/to/backend"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="frontendPath"
|
||||
>Frontend Repo Path</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="frontendPath"
|
||||
placeholder="/path/to/frontend"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
onclick="generateConfig()"
|
||||
>
|
||||
Generate Config
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
onclick="updatePreview()"
|
||||
>
|
||||
Preview Structure
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div id="status" class="status"></div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Preview</h2>
|
||||
<div id="preview" class="preview">
|
||||
<span class="comment"
|
||||
># Enter configuration details and click "Preview
|
||||
Structure"</span
|
||||
>
|
||||
|
||||
<span class="folder">gen/<room>/</span>
|
||||
<span class="folder"><managed>/</span>
|
||||
<span class="comment"># if managed app configured</span>
|
||||
<span class="folder">link/</span>
|
||||
<span class="folder"><soleprint>/</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const form = document.getElementById("configForm");
|
||||
const hasManaged = document.getElementById("hasManaged");
|
||||
const managedFields = document.getElementById("managedFields");
|
||||
const preview = document.getElementById("preview");
|
||||
const status = document.getElementById("status");
|
||||
|
||||
// Toggle managed fields
|
||||
hasManaged.addEventListener("change", () => {
|
||||
managedFields.style.display = hasManaged.checked
|
||||
? "block"
|
||||
: "none";
|
||||
updatePreview();
|
||||
});
|
||||
|
||||
// Live preview on input changes
|
||||
form.querySelectorAll("input").forEach((input) => {
|
||||
input.addEventListener("input", debounce(updatePreview, 300));
|
||||
});
|
||||
|
||||
function debounce(func, wait) {
|
||||
let timeout;
|
||||
return function (...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
}
|
||||
|
||||
function getFormData() {
|
||||
return {
|
||||
room_name:
|
||||
document.getElementById("roomName").value || "myroom",
|
||||
framework: {
|
||||
name:
|
||||
document.getElementById("frameworkName").value ||
|
||||
"soleprint",
|
||||
icon:
|
||||
document.getElementById("frameworkIcon").value ||
|
||||
null,
|
||||
},
|
||||
systems: {
|
||||
artery:
|
||||
document.getElementById("arteryName").value ||
|
||||
"artery",
|
||||
atlas:
|
||||
document.getElementById("atlasName").value ||
|
||||
"atlas",
|
||||
station:
|
||||
document.getElementById("stationName").value ||
|
||||
"station",
|
||||
},
|
||||
managed: hasManaged.checked
|
||||
? {
|
||||
name:
|
||||
document.getElementById("managedName")
|
||||
.value || "",
|
||||
repos: {
|
||||
backend:
|
||||
document.getElementById("backendPath")
|
||||
.value || "",
|
||||
frontend:
|
||||
document.getElementById("frontendPath")
|
||||
.value || "",
|
||||
},
|
||||
}
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
async function updatePreview() {
|
||||
const data = getFormData();
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/generate/preview", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
preview.innerHTML = result.tree;
|
||||
} else {
|
||||
const err = await response.json();
|
||||
preview.innerHTML = `<span class="comment"># Error: ${err.detail || "Unknown error"}</span>`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback to client-side preview
|
||||
preview.innerHTML = generateLocalPreview(data);
|
||||
}
|
||||
}
|
||||
|
||||
function generateLocalPreview(data) {
|
||||
const room = data.room_name || "room";
|
||||
const fw = data.framework.name || "soleprint";
|
||||
const managed = data.managed;
|
||||
|
||||
let tree = `<span class="folder">gen/${room}/</span>\n`;
|
||||
|
||||
if (managed && managed.name) {
|
||||
tree += ` <span class="folder">${managed.name}/</span>\n`;
|
||||
tree += ` <span class="folder">link/</span>\n`;
|
||||
}
|
||||
|
||||
tree += ` <span class="folder">${fw}/</span>\n`;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
async function generateConfig() {
|
||||
const data = getFormData();
|
||||
|
||||
if (!data.room_name) {
|
||||
showStatus("error", "Room name is required");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/generate", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const result = await response.json();
|
||||
|
||||
// Download config.json
|
||||
const blob = new Blob(
|
||||
[JSON.stringify(result.config, null, 2)],
|
||||
{ type: "application/json" },
|
||||
);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `config.json`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
showStatus(
|
||||
"success",
|
||||
`Configuration generated for "${data.room_name}". Save to cfg/${data.room_name}/config.json`,
|
||||
);
|
||||
} else {
|
||||
const err = await response.json();
|
||||
showStatus(
|
||||
"error",
|
||||
err.detail || "Failed to generate config",
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showStatus("error", `Error: ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function showStatus(type, message) {
|
||||
status.className = `status ${type}`;
|
||||
status.textContent = message;
|
||||
}
|
||||
|
||||
// Initial preview
|
||||
updatePreview();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user