Spaces:
Sleeping
Sleeping
File size: 5,574 Bytes
13d012b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | /*
app.js β GPT-2 Playground
==========================
Handles all client-side behaviour:
1. Slider sync β live-update value badges next to each range input
2. Char counter β updates as the user types in the prompt textarea
3. Generation flow β POSTs to /generate, manages loading state on the button,
renders prompt + generated text in the output pane
4. Copy button β copies the full output text and shows brief confirmation
5. Error display β surfaces API or network errors in the error banner
No build step or framework required β plain ES2020, runs in any modern browser.
*/
const $ = id => document.getElementById(id);
// ββ Slider β value badge sync ββββββββββββββββββββββββββββββββββββββββββββββ
const sliders = [
{ input: $("maxTokens"), display: $("maxTokensVal"), decimals: 0 },
{ input: $("temperature"), display: $("temperatureVal"), decimals: 2 },
{ input: $("topP"), display: $("topPVal"), decimals: 2 },
{ input: $("repPenalty"), display: $("repPenaltyVal"), decimals: 2 },
];
function syncSlider({ input, display, decimals }) {
display.textContent = parseFloat(input.value).toFixed(decimals);
}
sliders.forEach(s => {
syncSlider(s);
s.input.addEventListener("input", () => syncSlider(s));
});
// ββ Character counter ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const promptInput = $("promptInput");
const charCount = $("charCount");
promptInput.addEventListener("input", () => {
charCount.textContent = `${promptInput.value.length} chars`;
});
// ββ Generation βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const generateBtn = $("generateBtn");
const outputPrompt = $("outputPrompt");
const outputGenerated = $("outputGenerated");
const errorBanner = $("errorBanner");
const copyBtn = $("copyBtn");
function setLoading(isLoading) {
generateBtn.disabled = isLoading;
generateBtn.classList.toggle("is-loading", isLoading);
// Apply the wider gradient background only during load so shimmer looks right
if (isLoading) {
generateBtn.style.backgroundImage =
"linear-gradient(90deg,#7c3aed,#6d28d9,#2563eb,#4f46e5,#7c3aed)";
generateBtn.style.backgroundSize = "300% 100%";
generateBtn.querySelector(".btn-label").textContent = "Generatingβ¦";
} else {
generateBtn.style.backgroundImage = "";
generateBtn.style.backgroundSize = "";
generateBtn.querySelector(".btn-label").textContent = "Generate";
}
}
function showError(message) {
errorBanner.textContent = message;
errorBanner.hidden = false;
}
function clearError() {
errorBanner.hidden = true;
errorBanner.textContent = "";
}
function renderOutput(prompt, generatedText) {
outputPrompt.textContent = prompt;
outputGenerated.textContent = generatedText;
outputGenerated.innerHTML = ""; // clear placeholder span
outputGenerated.textContent = generatedText;
copyBtn.disabled = false;
}
generateBtn.addEventListener("click", async () => {
const prompt = promptInput.value.trim();
if (!prompt) {
showError("Please enter a prompt before generating.");
return;
}
clearError();
setLoading(true);
copyBtn.disabled = true;
outputPrompt.textContent = "";
outputGenerated.innerHTML = '<span class="placeholder-text">Generatingβ¦</span>';
const payload = {
prompt,
max_new_tokens: parseInt($("maxTokens").value, 10),
temperature: parseFloat($("temperature").value),
top_p: parseFloat($("topP").value),
repetition_penalty: parseFloat($("repPenalty").value),
};
try {
const response = await fetch("/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
});
if (!response.ok) {
const err = await response.json().catch(() => ({}));
throw new Error(err.error || `Server error ${response.status}`);
}
const data = await response.json();
renderOutput(data.prompt, data.generated_text);
} catch (err) {
showError(`Generation failed: ${err.message}`);
outputGenerated.innerHTML = '<span class="placeholder-text">Generated text will appear hereβ¦</span>';
} finally {
setLoading(false);
}
});
// ββ Copy button βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
copyBtn.addEventListener("click", () => {
const fullText = (outputPrompt.textContent + outputGenerated.textContent).trim();
if (!fullText) return;
navigator.clipboard.writeText(fullText).then(() => {
copyBtn.textContent = "β Copied!";
copyBtn.classList.add("copied");
setTimeout(() => {
copyBtn.innerHTML = `
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</svg>
Copy`;
copyBtn.classList.remove("copied");
}, 2000);
});
}); |