Download index.html from smlflg/ProjectIntakeTower: direct link, hf CLI and curl.
- Browser
- Download file 8.63 kB
-
https://huggingface.co/smlflg/ProjectIntakeTower/resolve/main/index.html
- Command line
-
hf download hf://smlflg/ProjectIntakeTower/index.html
-
curl -L -o index.html https://huggingface.co/smlflg/ProjectIntakeTower/resolve/main/index.html
8.63 kB
| <html lang="de"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Owner Packet Viewer</title> | |
| <style> | |
| :root { | |
| color-scheme: dark; | |
| font-family: | |
| Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| background: #111417; | |
| color: #eef2ef; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| min-height: 100vh; | |
| margin: 0; | |
| background: #111417; | |
| } | |
| button, | |
| textarea { | |
| font: inherit; | |
| } | |
| .shell { | |
| min-height: 100vh; | |
| display: grid; | |
| grid-template-rows: auto minmax(0, 1fr); | |
| gap: 16px; | |
| padding: 18px; | |
| } | |
| header, | |
| main { | |
| width: min(980px, 100%); | |
| margin: 0 auto; | |
| } | |
| header { | |
| display: grid; | |
| grid-template-columns: minmax(0, 1fr) auto; | |
| gap: 16px; | |
| align-items: end; | |
| border-bottom: 1px solid #2c3533; | |
| padding-bottom: 14px; | |
| } | |
| .eyebrow, | |
| .label { | |
| color: #93a09b; | |
| font-size: 0.73rem; | |
| font-weight: 800; | |
| letter-spacing: 0; | |
| text-transform: uppercase; | |
| } | |
| h1 { | |
| margin: 4px 0 0; | |
| font-size: clamp(1.5rem, 3vw, 2.35rem); | |
| line-height: 1.08; | |
| } | |
| .status { | |
| min-width: 150px; | |
| border: 1px solid #40514d; | |
| border-radius: 8px; | |
| padding: 10px 12px; | |
| color: #a7f0dd; | |
| text-align: center; | |
| font-weight: 800; | |
| } | |
| main { | |
| display: grid; | |
| grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); | |
| gap: 16px; | |
| align-items: start; | |
| } | |
| .packet, | |
| .side { | |
| border: 1px solid #293331; | |
| border-radius: 8px; | |
| background: #171d20; | |
| box-shadow: 0 20px 50px rgba(0, 0, 0, 0.24); | |
| } | |
| .packet { | |
| padding: 22px; | |
| } | |
| .question { | |
| margin: 8px 0 14px; | |
| font-size: 1.42rem; | |
| line-height: 1.22; | |
| } | |
| .context { | |
| margin: 0; | |
| color: #c7d0cb; | |
| line-height: 1.55; | |
| } | |
| .field { | |
| display: grid; | |
| gap: 7px; | |
| margin-top: 18px; | |
| } | |
| .value { | |
| border-left: 3px solid #6de0c9; | |
| padding: 11px 13px; | |
| background: #20322f; | |
| color: #f6fbf9; | |
| line-height: 1.45; | |
| } | |
| .risk { | |
| border-left-color: #ffc56f; | |
| background: #33291b; | |
| } | |
| .actions { | |
| display: grid; | |
| grid-template-columns: repeat(3, minmax(0, 1fr)); | |
| gap: 10px; | |
| margin-top: 22px; | |
| } | |
| .action { | |
| min-height: 44px; | |
| border: 0; | |
| border-radius: 8px; | |
| color: #06110e; | |
| font-weight: 850; | |
| cursor: pointer; | |
| } | |
| .action:disabled { | |
| cursor: default; | |
| opacity: 0.45; | |
| } | |
| .action.selected { | |
| box-shadow: 0 0 0 3px rgba(238, 242, 239, 0.32); | |
| } | |
| [data-action="Approve"] { | |
| background: #6de0c9; | |
| } | |
| [data-action="Reject"] { | |
| background: #ff8d8d; | |
| } | |
| [data-action="Ask"] { | |
| background: #ffc56f; | |
| } | |
| .side { | |
| display: grid; | |
| gap: 16px; | |
| padding: 18px; | |
| } | |
| textarea { | |
| min-height: 180px; | |
| resize: vertical; | |
| border: 1px solid #40514d; | |
| border-radius: 8px; | |
| padding: 12px; | |
| outline: none; | |
| background: #0d1113; | |
| color: #eef2ef; | |
| } | |
| textarea:focus { | |
| border-color: #6de0c9; | |
| box-shadow: 0 0 0 3px rgba(109, 224, 201, 0.13); | |
| } | |
| .decision { | |
| min-height: 48px; | |
| border: 1px solid #40514d; | |
| border-radius: 8px; | |
| padding: 12px; | |
| color: #c7d0cb; | |
| } | |
| .error { | |
| color: #ffabab; | |
| } | |
| @media (max-width: 760px) { | |
| header, | |
| main { | |
| grid-template-columns: 1fr; | |
| } | |
| .status { | |
| text-align: left; | |
| } | |
| .actions { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="shell"> | |
| <header> | |
| <div> | |
| <div class="eyebrow">Owner Packet</div> | |
| <h1 id="project">Loading packet</h1> | |
| </div> | |
| <div class="status" id="status">loading</div> | |
| </header> | |
| <main> | |
| <section class="packet" aria-label="Owner decision packet"> | |
| <div class="label">Question</div> | |
| <h2 class="question" id="question">Loading owner_packet.json...</h2> | |
| <p class="context" id="context"></p> | |
| <div class="field"> | |
| <div class="label">Recommended action</div> | |
| <div class="value" id="recommendedAction"></div> | |
| </div> | |
| <div class="field"> | |
| <div class="label">Risk</div> | |
| <div class="value risk" id="risk"></div> | |
| </div> | |
| <div class="actions" id="actions" aria-label="Decision actions"></div> | |
| </section> | |
| <aside class="side" aria-label="Local owner note"> | |
| <div> | |
| <div class="label">Local decision</div> | |
| <div class="decision" id="decision">No decision selected.</div> | |
| </div> | |
| <label class="label" for="note">Local note</label> | |
| <textarea id="note" placeholder="Private local note. Not written back to owner_packet.json."></textarea> | |
| </aside> | |
| </main> | |
| </div> | |
| <script> | |
| const noteKey = "project-intake-tower.owner-note"; | |
| const selectedKey = "project-intake-tower.owner-decision"; | |
| const elements = { | |
| project: document.getElementById("project"), | |
| status: document.getElementById("status"), | |
| question: document.getElementById("question"), | |
| context: document.getElementById("context"), | |
| recommendedAction: document.getElementById("recommendedAction"), | |
| risk: document.getElementById("risk"), | |
| actions: document.getElementById("actions"), | |
| decision: document.getElementById("decision"), | |
| note: document.getElementById("note") | |
| }; | |
| elements.note.value = localStorage.getItem(noteKey) || ""; | |
| elements.note.addEventListener("input", () => { | |
| localStorage.setItem(noteKey, elements.note.value); | |
| }); | |
| fetch("owner_packet.json", { cache: "no-store" }) | |
| .then((response) => { | |
| if (!response.ok) { | |
| throw new Error("owner_packet.json could not be loaded"); | |
| } | |
| return response.json(); | |
| }) | |
| .then(renderPacket) | |
| .catch((error) => { | |
| elements.status.textContent = "error"; | |
| elements.question.textContent = "Packet could not be loaded."; | |
| elements.context.textContent = error.message; | |
| elements.context.classList.add("error"); | |
| }); | |
| function renderPacket(packet) { | |
| elements.project.textContent = packet.project || "Unnamed project"; | |
| elements.status.textContent = packet.status || "unknown"; | |
| elements.question.textContent = packet.question || "No question provided."; | |
| elements.context.textContent = packet.context || "No context provided."; | |
| elements.recommendedAction.textContent = packet.recommended_action || "No recommendation provided."; | |
| elements.risk.textContent = packet.risk || "No risk provided."; | |
| renderActions(Array.isArray(packet.options) ? packet.options : ["Approve", "Reject", "Ask"]); | |
| } | |
| function renderActions(options) { | |
| elements.actions.innerHTML = ""; | |
| const savedDecision = localStorage.getItem(selectedKey); | |
| for (const option of options) { | |
| const button = document.createElement("button"); | |
| button.type = "button"; | |
| button.className = "action"; | |
| button.dataset.action = option; | |
| button.textContent = option; | |
| button.addEventListener("click", () => selectDecision(option)); | |
| elements.actions.appendChild(button); | |
| } | |
| if (savedDecision) { | |
| selectDecision(savedDecision); | |
| } | |
| } | |
| function selectDecision(decision) { | |
| localStorage.setItem(selectedKey, decision); | |
| elements.decision.textContent = decision; | |
| for (const button of elements.actions.querySelectorAll("button")) { | |
| const selected = button.dataset.action === decision; | |
| button.classList.toggle("selected", selected); | |
| button.disabled = true; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |