Spaces:
Running
Running
Update index.html
Browse files- index.html +149 -26
index.html
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
-
<title>
|
| 7 |
<style>
|
| 8 |
/* --- Reset and Global Styles --- */
|
| 9 |
* {
|
|
@@ -55,22 +55,47 @@
|
|
| 55 |
}
|
| 56 |
|
| 57 |
.input-section h1 {
|
| 58 |
-
margin-bottom:
|
| 59 |
font-size: 20px;
|
| 60 |
font-weight: 500;
|
| 61 |
}
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
#jsonInput {
|
| 64 |
width: 100%;
|
| 65 |
-
|
| 66 |
-
padding: 10px;
|
| 67 |
border: 1px solid #ccc;
|
| 68 |
border-radius: 2px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
font-family: monospace;
|
| 70 |
font-size: 12px;
|
| 71 |
resize: vertical;
|
| 72 |
}
|
| 73 |
|
|
|
|
| 74 |
#parseBtn {
|
| 75 |
margin-top: 8px;
|
| 76 |
padding: 8px 16px;
|
|
@@ -80,12 +105,19 @@
|
|
| 80 |
border-radius: 2px;
|
| 81 |
cursor: pointer;
|
| 82 |
font-size: 13px;
|
|
|
|
| 83 |
}
|
| 84 |
|
|
|
|
| 85 |
#parseBtn:hover {
|
| 86 |
background: #555;
|
| 87 |
}
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
.error {
|
| 90 |
color: #d73a49;
|
| 91 |
margin-top: 8px;
|
|
@@ -238,12 +270,31 @@
|
|
| 238 |
<div class="app-container">
|
| 239 |
<div class="sidebar">
|
| 240 |
<div class="input-section">
|
| 241 |
-
<h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
<textarea
|
| 243 |
id="jsonInput"
|
| 244 |
-
placeholder="Paste
|
| 245 |
></textarea>
|
| 246 |
-
<button id="parseBtn">Parse</button>
|
|
|
|
| 247 |
<div id="error" class="error"></div>
|
| 248 |
</div>
|
| 249 |
<div class="header-info" id="headerInfo">
|
|
@@ -259,29 +310,110 @@
|
|
| 259 |
</div>
|
| 260 |
|
| 261 |
<script>
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
-
function
|
| 267 |
-
const
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
// Reset view on each parse attempt
|
| 273 |
errorDiv.style.display = "none";
|
| 274 |
headerInfo.style.display = "none";
|
| 275 |
mainContent.style.display = "none";
|
| 276 |
|
| 277 |
-
if (!
|
| 278 |
errorDiv.textContent = "Input cannot be empty.";
|
| 279 |
errorDiv.style.display = "block";
|
| 280 |
return;
|
| 281 |
}
|
| 282 |
|
| 283 |
try {
|
| 284 |
-
const data = JSON.parse(
|
| 285 |
// Show the components on successful parse
|
| 286 |
headerInfo.style.display = "block";
|
| 287 |
mainContent.style.display = "flex"; // Use flex as it's a flex container
|
|
@@ -331,7 +463,6 @@
|
|
| 331 |
}
|
| 332 |
|
| 333 |
function renderChat(chatParts, experience) {
|
| 334 |
-
const chatArea = document.getElementById("chatArea");
|
| 335 |
chatArea.innerHTML = "";
|
| 336 |
|
| 337 |
chatParts.forEach((part) => {
|
|
@@ -407,14 +538,6 @@
|
|
| 407 |
content = content.replace(/\n/g, "<br>");
|
| 408 |
return content;
|
| 409 |
}
|
| 410 |
-
|
| 411 |
-
document
|
| 412 |
-
.getElementById("jsonInput")
|
| 413 |
-
.addEventListener("keydown", function (e) {
|
| 414 |
-
if (e.ctrlKey && e.key === "Enter") {
|
| 415 |
-
parseConversation();
|
| 416 |
-
}
|
| 417 |
-
});
|
| 418 |
</script>
|
| 419 |
</body>
|
| 420 |
</html>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 6 |
+
<title>SOC Visualizer</title>
|
| 7 |
<style>
|
| 8 |
/* --- Reset and Global Styles --- */
|
| 9 |
* {
|
|
|
|
| 55 |
}
|
| 56 |
|
| 57 |
.input-section h1 {
|
| 58 |
+
margin-bottom: 16px;
|
| 59 |
font-size: 20px;
|
| 60 |
font-weight: 500;
|
| 61 |
}
|
| 62 |
|
| 63 |
+
.input-section h2 {
|
| 64 |
+
font-size: 14px;
|
| 65 |
+
font-weight: 500;
|
| 66 |
+
margin-top: 16px;
|
| 67 |
+
margin-bottom: 8px;
|
| 68 |
+
color: #555;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
.input-group {
|
| 72 |
+
margin-bottom: 12px;
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
.input-group label {
|
| 76 |
+
display: block;
|
| 77 |
+
font-size: 13px;
|
| 78 |
+
margin-bottom: 4px;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.input-group select,
|
| 82 |
#jsonInput {
|
| 83 |
width: 100%;
|
| 84 |
+
padding: 8px;
|
|
|
|
| 85 |
border: 1px solid #ccc;
|
| 86 |
border-radius: 2px;
|
| 87 |
+
font-size: 13px;
|
| 88 |
+
background-color: #fff;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
#jsonInput {
|
| 92 |
+
height: 120px;
|
| 93 |
font-family: monospace;
|
| 94 |
font-size: 12px;
|
| 95 |
resize: vertical;
|
| 96 |
}
|
| 97 |
|
| 98 |
+
#loadBtn,
|
| 99 |
#parseBtn {
|
| 100 |
margin-top: 8px;
|
| 101 |
padding: 8px 16px;
|
|
|
|
| 105 |
border-radius: 2px;
|
| 106 |
cursor: pointer;
|
| 107 |
font-size: 13px;
|
| 108 |
+
width: 100%;
|
| 109 |
}
|
| 110 |
|
| 111 |
+
#loadBtn:hover,
|
| 112 |
#parseBtn:hover {
|
| 113 |
background: #555;
|
| 114 |
}
|
| 115 |
|
| 116 |
+
#loadBtn:disabled {
|
| 117 |
+
background: #ccc;
|
| 118 |
+
cursor: not-allowed;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
.error {
|
| 122 |
color: #d73a49;
|
| 123 |
margin-top: 8px;
|
|
|
|
| 270 |
<div class="app-container">
|
| 271 |
<div class="sidebar">
|
| 272 |
<div class="input-section">
|
| 273 |
+
<h1>SOC Visualizer</h1>
|
| 274 |
+
|
| 275 |
+
<div class="input-group">
|
| 276 |
+
<label for="fileSelector">1. Select a file</label>
|
| 277 |
+
<select id="fileSelector">
|
| 278 |
+
<option value="">-- Choose a file --</option>
|
| 279 |
+
</select>
|
| 280 |
+
</div>
|
| 281 |
+
|
| 282 |
+
<div class="input-group">
|
| 283 |
+
<label for="lineSelector">2. Select a conversation</label>
|
| 284 |
+
<select id="lineSelector" disabled></select>
|
| 285 |
+
</div>
|
| 286 |
+
|
| 287 |
+
<button id="loadBtn" disabled>Load Conversation</button>
|
| 288 |
+
|
| 289 |
+
<hr style="margin: 24px 0" />
|
| 290 |
+
|
| 291 |
+
<h2>Or Paste Manually</h2>
|
| 292 |
<textarea
|
| 293 |
id="jsonInput"
|
| 294 |
+
placeholder="Paste a single JSON conversation object here..."
|
| 295 |
></textarea>
|
| 296 |
+
<button id="parseBtn">Parse Manual Input</button>
|
| 297 |
+
|
| 298 |
<div id="error" class="error"></div>
|
| 299 |
</div>
|
| 300 |
<div class="header-info" id="headerInfo">
|
|
|
|
| 310 |
</div>
|
| 311 |
|
| 312 |
<script>
|
| 313 |
+
// --- VIRTUAL FILE SYSTEM ---
|
| 314 |
+
// In a real application, you would fetch these files.
|
| 315 |
+
// For this tool, simply add your JSONL file content here.
|
| 316 |
+
// Each key is a filename, and the value is the file's content as a string.
|
| 317 |
+
// Each line in the string must be a complete, valid JSON object.
|
| 318 |
+
const virtualFiles = {
|
| 319 |
+
"soc_sample_1.jsonl": `{ "experience": { "persona1": { "id": "p1", "name": "Alex", "age": 28, "traits": ["tech-savvy", "impatient", "witty"], "background": "Software developer who loves sci-fi movies.", "chatting_style": "Uses short sentences and a lot of tech jargon." }, "persona2": { "id": "p2", "name": "Ben", "age": 30, "traits": ["patient", "thoughtful", "curious"], "background": "Librarian who enjoys classic literature.", "chatting_style": "Writes in full paragraphs and asks many questions." }, "relationship": "Friends", "situation": "Discussing a new movie they both saw.", "topic": "Movie review" }, "chat_parts": [ { "sender": "p1", "messages": ["Did you see 'Galaxy Runners'? The CGI was insane."] }, { "sender": "p2", "messages": ["I did! I found the plot to be a bit derivative of older films, though. What did you think of the story itself?"] }, { "sender": "p1", "messages": ["Story? lol who cares. The FTL jumps looked amazing.", "And that scene with the alien armada... <gif>mind_blown.gif</gif>"] }, { "sender": "p2", "messages": ["Haha, fair enough. The visual spectacle was certainly its strong suit. I just wish they had spent more time developing the main character's motivations."] } ] }
|
| 320 |
+
{ "experience": { "persona1": { "id": "p1", "name": "Chloe", "age": 22, "traits": ["energetic", "loves food", "uses emojis"], "background": "Culinary student exploring new cafes.", "chatting_style": "Very casual, lots of emojis and slang." }, "persona2": { "id": "p2", "name": "David", "age": 24, "traits": ["calm", "methodical", "minimalist"], "background": "Architecture student who prefers quiet places.", "chatting_style": "Concise and to the point." }, "relationship": "Siblings", "situation": "Making plans for the weekend.", "topic": "Weekend plans" }, "chat_parts": [ { "sender": "p1", "messages": ["heyyyy!! what r u up to this weekend? π€©"] }, { "sender": "p2", "messages": ["Not much. Probably finishing up my model for studio."] }, { "sender": "p1", "messages": ["omg nooo you have to come with me to this new brunch place downtown! π₯π§π₯", "the pics look amazing!! <image>brunch_spread.jpg</image>"] }, { "sender": "p2", "messages": ["Hah, looks intense. Maybe. What time?"] }, { "sender": "p1", "messages": ["11am! be there or be square! π"] } ] }`,
|
| 321 |
+
"soc_sample_2.jsonl": `{ "experience": { "persona1": { "id": "p1", "name": "Sarah", "age": 45, "traits": ["organized", "caring", "formal"], "background": "Project manager and mother of two.", "chatting_style": "Proper grammar and punctuation, plans everything." }, "persona2": { "id": "p2", "name": "Tom", "age": 46, "traits": ["forgetful", "easy-going", "humorous"], "background": "Graphic designer who works from home.", "chatting_style": "Casual, uses ellipses, often cracks jokes." }, "relationship": "Married Couple", "situation": "Coordinating grocery shopping.", "topic": "Groceries" }, "chat_parts": [ { "sender": "p1", "messages": ["Hi honey, I'm heading to the store after work. I've shared the updated grocery list with you. Please take a look."] }, { "sender": "p2", "messages": ["Hey! ok will check... pretty sure we still have milk tho?"] }, { "sender": "p1", "messages": ["The kids finished it this morning. Please double-check the list for quantities. I don't want to forget the baking soda for Maya's science project again."] }, { "sender": "p2", "messages": ["oops right! science fair volcano... classic.", "got it. list looks good. grab me some of those fancy chips? π", "the sea salt and vinegar ones"] } ] }`,
|
| 322 |
+
};
|
| 323 |
+
// --- END VIRTUAL FILE SYSTEM ---
|
| 324 |
+
|
| 325 |
+
// --- DOM Elements ---
|
| 326 |
+
const fileSelector = document.getElementById("fileSelector");
|
| 327 |
+
const lineSelector = document.getElementById("lineSelector");
|
| 328 |
+
const loadBtn = document.getElementById("loadBtn");
|
| 329 |
+
const parseBtn = document.getElementById("parseBtn");
|
| 330 |
+
const jsonInput = document.getElementById("jsonInput");
|
| 331 |
+
const errorDiv = document.getElementById("error");
|
| 332 |
+
const headerInfo = document.getElementById("headerInfo");
|
| 333 |
+
const mainContent = document.getElementById("mainContent");
|
| 334 |
+
const chatArea = document.getElementById("chatArea");
|
| 335 |
+
|
| 336 |
+
// --- Event Listeners ---
|
| 337 |
+
document.addEventListener("DOMContentLoaded", initialize);
|
| 338 |
+
fileSelector.addEventListener("change", handleFileSelection);
|
| 339 |
+
loadBtn.addEventListener("click", loadSelectedConversation);
|
| 340 |
+
parseBtn.addEventListener("click", parseManualInput);
|
| 341 |
+
jsonInput.addEventListener("keydown", function (e) {
|
| 342 |
+
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
| 343 |
+
parseManualInput();
|
| 344 |
+
}
|
| 345 |
+
});
|
| 346 |
+
|
| 347 |
+
// --- Functions ---
|
| 348 |
+
function initialize() {
|
| 349 |
+
// Populate the file selector on page load
|
| 350 |
+
Object.keys(virtualFiles).forEach((filename) => {
|
| 351 |
+
const option = document.createElement("option");
|
| 352 |
+
option.value = filename;
|
| 353 |
+
option.textContent = filename;
|
| 354 |
+
fileSelector.appendChild(option);
|
| 355 |
+
});
|
| 356 |
+
}
|
| 357 |
|
| 358 |
+
function handleFileSelection() {
|
| 359 |
+
const selectedFile = fileSelector.value;
|
| 360 |
+
lineSelector.innerHTML = ""; // Clear previous options
|
| 361 |
+
if (selectedFile) {
|
| 362 |
+
const content = virtualFiles[selectedFile];
|
| 363 |
+
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
| 364 |
+
|
| 365 |
+
lines.forEach((line, index) => {
|
| 366 |
+
const option = document.createElement("option");
|
| 367 |
+
option.value = index;
|
| 368 |
+
// Try to parse to get a topic for a better label
|
| 369 |
+
try {
|
| 370 |
+
const data = JSON.parse(line);
|
| 371 |
+
option.textContent = `Conversation ${index + 1}: ${data.experience.topic}`;
|
| 372 |
+
} catch (e) {
|
| 373 |
+
option.textContent = `Conversation ${index + 1}`;
|
| 374 |
+
}
|
| 375 |
+
lineSelector.appendChild(option);
|
| 376 |
+
});
|
| 377 |
+
|
| 378 |
+
lineSelector.disabled = false;
|
| 379 |
+
loadBtn.disabled = false;
|
| 380 |
+
} else {
|
| 381 |
+
lineSelector.disabled = true;
|
| 382 |
+
loadBtn.disabled = true;
|
| 383 |
+
}
|
| 384 |
+
}
|
| 385 |
|
| 386 |
+
function loadSelectedConversation() {
|
| 387 |
+
const fileName = fileSelector.value;
|
| 388 |
+
const lineIndex = lineSelector.value;
|
| 389 |
+
|
| 390 |
+
if (fileName && lineIndex !== null) {
|
| 391 |
+
const content = virtualFiles[fileName];
|
| 392 |
+
const lines = content.split("\n");
|
| 393 |
+
const jsonString = lines[lineIndex];
|
| 394 |
+
processAndRender(jsonString);
|
| 395 |
+
}
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
function parseManualInput() {
|
| 399 |
+
const jsonString = jsonInput.value.trim();
|
| 400 |
+
processAndRender(jsonString);
|
| 401 |
+
}
|
| 402 |
+
|
| 403 |
+
function processAndRender(jsonString) {
|
| 404 |
// Reset view on each parse attempt
|
| 405 |
errorDiv.style.display = "none";
|
| 406 |
headerInfo.style.display = "none";
|
| 407 |
mainContent.style.display = "none";
|
| 408 |
|
| 409 |
+
if (!jsonString) {
|
| 410 |
errorDiv.textContent = "Input cannot be empty.";
|
| 411 |
errorDiv.style.display = "block";
|
| 412 |
return;
|
| 413 |
}
|
| 414 |
|
| 415 |
try {
|
| 416 |
+
const data = JSON.parse(jsonString);
|
| 417 |
// Show the components on successful parse
|
| 418 |
headerInfo.style.display = "block";
|
| 419 |
mainContent.style.display = "flex"; // Use flex as it's a flex container
|
|
|
|
| 463 |
}
|
| 464 |
|
| 465 |
function renderChat(chatParts, experience) {
|
|
|
|
| 466 |
chatArea.innerHTML = "";
|
| 467 |
|
| 468 |
chatParts.forEach((part) => {
|
|
|
|
| 538 |
content = content.replace(/\n/g, "<br>");
|
| 539 |
return content;
|
| 540 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
</script>
|
| 542 |
</body>
|
| 543 |
</html>
|