Spaces:
Sleeping
Sleeping
Debanjum
commited on
Commit
·
e6162a8
1
Parent(s):
d8a31af
Make url shareable with model, text set via url query params
Browse filesThis allows for deep link references to model and text visualization.
The URL is automatically updated with the model, text used to
tokenize. This should ease copy/paste url sharing from address bar.
- app.py +4 -1
- templates/index.html +79 -1
app.py
CHANGED
|
@@ -43,7 +43,10 @@ def load_tokenizer(model_id: str):
|
|
| 43 |
|
| 44 |
@app.route("/")
|
| 45 |
def index():
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
| 47 |
|
| 48 |
|
| 49 |
@app.route("/tokenize", methods=["POST"])
|
|
|
|
| 43 |
|
| 44 |
@app.route("/")
|
| 45 |
def index():
|
| 46 |
+
# Get query parameters
|
| 47 |
+
model_id = request.args.get("model", "").strip() or request.args.get("model_id", "").strip()
|
| 48 |
+
text = request.args.get("text", "").strip()
|
| 49 |
+
return render_template("index.html", model_id=model_id, text=text)
|
| 50 |
|
| 51 |
|
| 52 |
@app.route("/tokenize", methods=["POST"])
|
templates/index.html
CHANGED
|
@@ -304,6 +304,39 @@
|
|
| 304 |
word-break: break-all;
|
| 305 |
}
|
| 306 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
footer {
|
| 308 |
text-align: center;
|
| 309 |
margin-top: 30px;
|
|
@@ -330,6 +363,7 @@
|
|
| 330 |
id="model-id"
|
| 331 |
placeholder="e.g., openai-community/gpt2"
|
| 332 |
autocomplete="off"
|
|
|
|
| 333 |
>
|
| 334 |
<div class="suggestions" id="suggestions"></div>
|
| 335 |
</div>
|
|
@@ -340,7 +374,7 @@
|
|
| 340 |
<textarea
|
| 341 |
id="text-input"
|
| 342 |
placeholder="Enter text to tokenize..."
|
| 343 |
-
>Hello, world! This is
|
| 344 |
</div>
|
| 345 |
|
| 346 |
<button class="btn" id="tokenize-btn">Tokenize</button>
|
|
@@ -362,6 +396,9 @@
|
|
| 362 |
<div class="stat-value" id="char-count">0</div>
|
| 363 |
<div class="stat-label">Characters</div>
|
| 364 |
</div>
|
|
|
|
|
|
|
|
|
|
| 365 |
</div>
|
| 366 |
|
| 367 |
<label>Tokenized Output <span style="font-weight: normal; color: #888;">(hover for token IDs)</span></label>
|
|
@@ -399,6 +436,14 @@
|
|
| 399 |
let suggestions = [];
|
| 400 |
let currentTokens = [];
|
| 401 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
// Load model suggestions
|
| 403 |
fetch('/models/suggestions')
|
| 404 |
.then(res => res.json())
|
|
@@ -472,6 +517,12 @@
|
|
| 472 |
return;
|
| 473 |
}
|
| 474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
hideError();
|
| 476 |
resultsDiv.classList.add('hidden');
|
| 477 |
loadingDiv.classList.add('show');
|
|
@@ -545,6 +596,33 @@
|
|
| 545 |
showIdsCheckbox.addEventListener('change', () => {
|
| 546 |
tokenIdsDiv.classList.toggle('hidden', !showIdsCheckbox.checked);
|
| 547 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 548 |
</script>
|
| 549 |
</body>
|
| 550 |
</html>
|
|
|
|
| 304 |
word-break: break-all;
|
| 305 |
}
|
| 306 |
|
| 307 |
+
.btn-share {
|
| 308 |
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 309 |
+
color: white;
|
| 310 |
+
border: none;
|
| 311 |
+
padding: 8px 16px;
|
| 312 |
+
font-size: 14px;
|
| 313 |
+
font-weight: 600;
|
| 314 |
+
border-radius: 8px;
|
| 315 |
+
cursor: pointer;
|
| 316 |
+
transition: transform 0.2s, box-shadow 0.2s, opacity 0.2s;
|
| 317 |
+
display: inline-flex;
|
| 318 |
+
align-items: center;
|
| 319 |
+
gap: 6px;
|
| 320 |
+
margin-left: auto;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.btn-share:hover {
|
| 324 |
+
transform: translateY(-1px);
|
| 325 |
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
.btn-share.copied {
|
| 329 |
+
background: #48bb78;
|
| 330 |
+
}
|
| 331 |
+
|
| 332 |
+
.stats {
|
| 333 |
+
display: flex;
|
| 334 |
+
gap: 20px;
|
| 335 |
+
margin-bottom: 20px;
|
| 336 |
+
flex-wrap: wrap;
|
| 337 |
+
align-items: center;
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
footer {
|
| 341 |
text-align: center;
|
| 342 |
margin-top: 30px;
|
|
|
|
| 363 |
id="model-id"
|
| 364 |
placeholder="e.g., openai-community/gpt2"
|
| 365 |
autocomplete="off"
|
| 366 |
+
value="{{ model_id }}"
|
| 367 |
>
|
| 368 |
<div class="suggestions" id="suggestions"></div>
|
| 369 |
</div>
|
|
|
|
| 374 |
<textarea
|
| 375 |
id="text-input"
|
| 376 |
placeholder="Enter text to tokenize..."
|
| 377 |
+
>{% if text %}{{ text }}{% else %}Hello, world! This is how I see text tokens.{% endif %}</textarea>
|
| 378 |
</div>
|
| 379 |
|
| 380 |
<button class="btn" id="tokenize-btn">Tokenize</button>
|
|
|
|
| 396 |
<div class="stat-value" id="char-count">0</div>
|
| 397 |
<div class="stat-label">Characters</div>
|
| 398 |
</div>
|
| 399 |
+
<button class="btn-share" id="share-btn" title="Copy shareable URL to clipboard">
|
| 400 |
+
🔗 Share
|
| 401 |
+
</button>
|
| 402 |
</div>
|
| 403 |
|
| 404 |
<label>Tokenized Output <span style="font-weight: normal; color: #888;">(hover for token IDs)</span></label>
|
|
|
|
| 436 |
let suggestions = [];
|
| 437 |
let currentTokens = [];
|
| 438 |
|
| 439 |
+
// Auto-tokenize if both model and text are provided via URL
|
| 440 |
+
const hasModel = modelInput.value.trim() !== '';
|
| 441 |
+
const hasText = textInput.value.trim() !== '';
|
| 442 |
+
if (hasModel && hasText) {
|
| 443 |
+
// Trigger tokenization after page loads
|
| 444 |
+
setTimeout(() => tokenize(), 100);
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
// Load model suggestions
|
| 448 |
fetch('/models/suggestions')
|
| 449 |
.then(res => res.json())
|
|
|
|
| 517 |
return;
|
| 518 |
}
|
| 519 |
|
| 520 |
+
// Update URL with query parameters
|
| 521 |
+
const url = new URL(window.location);
|
| 522 |
+
url.searchParams.set('model', modelId);
|
| 523 |
+
url.searchParams.set('text', text);
|
| 524 |
+
window.history.pushState({}, '', url);
|
| 525 |
+
|
| 526 |
hideError();
|
| 527 |
resultsDiv.classList.add('hidden');
|
| 528 |
loadingDiv.classList.add('show');
|
|
|
|
| 596 |
showIdsCheckbox.addEventListener('change', () => {
|
| 597 |
tokenIdsDiv.classList.toggle('hidden', !showIdsCheckbox.checked);
|
| 598 |
});
|
| 599 |
+
|
| 600 |
+
// Share URL button
|
| 601 |
+
const shareBtn = document.getElementById('share-btn');
|
| 602 |
+
|
| 603 |
+
shareBtn.addEventListener('click', async () => {
|
| 604 |
+
const modelId = modelInput.value.trim();
|
| 605 |
+
const text = textInput.value;
|
| 606 |
+
|
| 607 |
+
// Build the share URL
|
| 608 |
+
const shareUrl = new URL(window.location.origin + window.location.pathname);
|
| 609 |
+
shareUrl.searchParams.set('model', modelId);
|
| 610 |
+
shareUrl.searchParams.set('text', text);
|
| 611 |
+
|
| 612 |
+
try {
|
| 613 |
+
await navigator.clipboard.writeText(shareUrl.toString());
|
| 614 |
+
shareBtn.textContent = '✓ Copied!';
|
| 615 |
+
shareBtn.classList.add('copied');
|
| 616 |
+
|
| 617 |
+
setTimeout(() => {
|
| 618 |
+
shareBtn.innerHTML = '🔗 Share';
|
| 619 |
+
shareBtn.classList.remove('copied');
|
| 620 |
+
}, 2000);
|
| 621 |
+
} catch (err) {
|
| 622 |
+
// Fallback: select text in a prompt
|
| 623 |
+
window.prompt('Copy this URL:', shareUrl.toString());
|
| 624 |
+
}
|
| 625 |
+
});
|
| 626 |
</script>
|
| 627 |
</body>
|
| 628 |
</html>
|