Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2795,6 +2795,12 @@ <h1 data-i18n="hero_title">Debug. Understand.<br><em>Ship faster.</em></h1>
<div class="editor-footer">
<div id="charCount">0 chars · 0 lines</div>
<div style="display:flex;gap:8px">
<button
type="button"
id="versionHistoryBtn"
class="mode-btn">
Version History
</button>
<div class="mode-row" role="tablist" aria-label="Mode selector">
<button class="mode-btn active" data-mode="analyze" aria-pressed="true" aria-keyshortcuts="Control+Alt+1" data-i18n="mode_full">Full</button>
<button class="mode-btn" data-mode="explain" aria-pressed="false" aria-keyshortcuts="Control+Alt+2" data-i18n="tab_explain">Explain</button>
Expand All @@ -2804,6 +2810,19 @@ <h1 data-i18n="hero_title">Debug. Understand.<br><em>Ship faster.</em></h1>
<span class="shortcut-hint" title="Use Control Alt 1 through 4 to switch analysis modes">Ctrl+Alt+1-4 modes</span>
</div>
</div>
<!-- Prompt Version History Panel -->
<div id="versionHistoryPanel" class="version-history-panel" hidden>
<div class="version-history-header">
<strong>Version History</strong>
<button type="button" id="closeVersionHistoryBtn" aria-label="Close version history">
×
</button>
</div>

<div id="versionHistoryList">
<p>No versions saved yet.</p>
</div>
</div>

<div class="analyze-row">
<button class="btn-analyze" id="analyzeBtn" title="Analyze Code" aria-label="Analyze Code" aria-keyshortcuts="Control+Enter Meta+Enter">
Expand Down Expand Up @@ -6275,6 +6294,7 @@ <h2 id="collabDrawerTitle">Live Collaboration</h2>
})();
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="js/prompt-versioning.js"></script>
<script src="js/syntax-highlighting.js"></script>
</body>

Expand Down
159 changes: 159 additions & 0 deletions frontend/js/prompt-versioning.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
(function () {
'use strict';

const STORAGE_KEY = 'qyx_prompt_versions';
const MAX_VERSIONS = 50;

/**
* Read all saved prompt versions from localStorage.
*/
function getVersions() {
try {
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
return Array.isArray(saved) ? saved : [];
} catch (error) {
console.warn('Could not load prompt versions:', error);
return [];
}
}

/**
* Save the complete version list to localStorage.
*/
function storeVersions(versions) {
try {
localStorage.setItem(
STORAGE_KEY,
JSON.stringify(versions.slice(0, MAX_VERSIONS))
);
return true;
} catch (error) {
console.warn('Could not save prompt versions:', error);
return false;
}
}

/**
* Create a new version of the editor content.
*/
function saveVersion(content) {
const text = String(content || '').trim();

// Don't save empty versions.
if (!text) {
return null;
}

const versions = getVersions();

// Don't create duplicate consecutive versions.
if (versions.length && versions[0].content === text) {
return versions[0];
}

const newVersion = {
id: `prompt-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
content: text,
timestamp: new Date().toISOString(),
name: '',
note: ''
};

versions.unshift(newVersion);

if (!storeVersions(versions)) {
return null;
}

return newVersion;
}
function initAutoSave() {
const editor = document.getElementById('codeEditor');

if (!editor) {
return;
}

let saveTimer = null;

editor.addEventListener('input', () => {
clearTimeout(saveTimer);

saveTimer = setTimeout(() => {
saveVersion(editor.value);
}, 2000);
});
}
function initVersionHistoryUI() {
const historyBtn = document.getElementById('versionHistoryBtn');
const historyPanel = document.getElementById('versionHistoryPanel');
const closeBtn = document.getElementById('closeVersionHistoryBtn');
const historyList = document.getElementById('versionHistoryList');

if (!historyBtn || !historyPanel || !closeBtn || !historyList) {
return;
}

function renderVersions() {
const versions = getVersions();

historyList.innerHTML = '';

if (versions.length === 0) {
const emptyMessage = document.createElement('p');
emptyMessage.textContent = 'No versions saved yet.';
historyList.appendChild(emptyMessage);
return;
}

versions.forEach((version, index) => {
const item = document.createElement('div');
item.className = 'prompt-version-item';

const title = document.createElement('strong');
title.textContent = version.name || `Version ${versions.length - index}`;

const time = document.createElement('small');
time.textContent = new Date(version.timestamp).toLocaleString();

const preview = document.createElement('p');
preview.textContent =
version.content.length > 80
? version.content.slice(0, 80) + '...'
: version.content;

item.appendChild(title);
item.appendChild(document.createElement('br'));
item.appendChild(time);
item.appendChild(preview);

historyList.appendChild(item);
});
}

historyBtn.addEventListener('click', () => {
renderVersions();
historyPanel.hidden = false;
});

closeBtn.addEventListener('click', () => {
historyPanel.hidden = true;
});
}

function init() {
initAutoSave();
initVersionHistoryUI();
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Expose a small API for the version-history UI.
window.QyxPromptVersioning = {
getVersions,
saveVersion
};
})();
53 changes: 53 additions & 0 deletions frontend/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -620,4 +620,57 @@ body {
text-align: center;
}
}
/* Prompt Version History */
.version-history-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: auto;
min-width: 140px;
height: 40px;
padding: 0 16px;
white-space: nowrap;
border-radius: 8px;
cursor: pointer;
}

.version-history-btn:hover {
border-color: #6c63ff;
}

.version-history-panel {
margin-top: 12px;
padding: 16px;
border: 1px solid var(--border);
border-radius: 10px;
background: var(--surface);
}

.version-history-panel[hidden] {
display: none;
}

.version-history-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
}

.version-history-header strong {
font-size: 16px;
}

#closeVersionHistoryBtn {
border: none;
background: transparent;
color: inherit;
cursor: pointer;
font-size: 20px;
padding: 4px 8px;
}

#versionHistoryList p {
margin: 0;
opacity: 0.7;
}
Loading