Hightlight prompt

This commit is contained in:
phamhungd 2025-11-28 13:20:48 +07:00
parent f1cd681398
commit f3f69baec1
3 changed files with 134 additions and 4 deletions

View file

@ -74,6 +74,8 @@ document.addEventListener('DOMContentLoaded', () => {
const generateBtn = document.getElementById('generate-btn'); const generateBtn = document.getElementById('generate-btn');
const promptInput = document.getElementById('prompt'); const promptInput = document.getElementById('prompt');
const promptNoteInput = document.getElementById('prompt-note'); const promptNoteInput = document.getElementById('prompt-note');
const promptHighlight = document.getElementById('prompt-highlight');
const promptPlaceholderText = promptInput?.getAttribute('placeholder') || '';
const aspectRatioInput = document.getElementById('aspect-ratio'); const aspectRatioInput = document.getElementById('aspect-ratio');
const resolutionInput = document.getElementById('resolution'); const resolutionInput = document.getElementById('resolution');
const apiKeyInput = document.getElementById('api-key'); const apiKeyInput = document.getElementById('api-key');
@ -197,6 +199,59 @@ document.addEventListener('DOMContentLoaded', () => {
return formData; return formData;
} }
const promptHighlightColors = [
'#34d399', // green
'#f97316', // orange
'#facc15', // yellow
'#38bdf8', // blue
'#fb7185', // pink
'#a855f7', // purple
];
function escapeHtml(value) {
return value.replace(/[&<>"']/g, (char) => {
switch (char) {
case '&': return '&amp;';
case '<': return '&lt;';
case '>': return '&gt;';
case '"': return '&quot;';
case "'": return '&#39;';
default: return char;
}
});
}
function buildPromptHighlightHtml(value) {
if (!promptHighlight) return '';
if (!value) {
return `<span class="prompt-placeholder">${escapeHtml(promptPlaceholderText)}</span>`;
}
const placeholderRegex = /(\{[^{}]*\}|\[[^\[\]]*\])/g;
let lastIndex = 0;
let match;
let colorIndex = 0;
let html = '';
while ((match = placeholderRegex.exec(value)) !== null) {
html += escapeHtml(value.slice(lastIndex, match.index));
const color = promptHighlightColors[colorIndex % promptHighlightColors.length];
html += `<span class="prompt-highlight-segment" style="color:${color}">${escapeHtml(match[0])}</span>`;
lastIndex = match.index + match[0].length;
colorIndex++;
}
html += escapeHtml(value.slice(lastIndex));
return html || `<span class="prompt-placeholder">${escapeHtml(promptPlaceholderText)}</span>`;
}
function refreshPromptHighlight() {
if (!promptHighlight || !promptInput) return;
promptHighlight.innerHTML = buildPromptHighlightHtml(promptInput.value);
promptHighlight.scrollTop = promptInput.scrollTop;
promptHighlight.scrollLeft = promptInput.scrollLeft;
}
// --- End Helper Functions --- // --- End Helper Functions ---
let zoomLevel = 1; let zoomLevel = 1;
@ -216,6 +271,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (template.prompt) { if (template.prompt) {
promptInput.value = i18n.getText(template.prompt); promptInput.value = i18n.getText(template.prompt);
persistSettings(); persistSettings();
refreshPromptHighlight();
} }
// Stay in template gallery view - don't auto-switch // Stay in template gallery view - don't auto-switch
// User will switch view by selecting image from history or generating // User will switch view by selecting image from history or generating
@ -271,6 +327,7 @@ document.addEventListener('DOMContentLoaded', () => {
const savedSettings = loadSettings(); const savedSettings = loadSettings();
slotManager.initialize(savedSettings.referenceImages || []); slotManager.initialize(savedSettings.referenceImages || []);
refreshPromptHighlight();
apiKeyInput.addEventListener('input', persistSettings); apiKeyInput.addEventListener('input', persistSettings);
let isApiKeyVisible = false; let isApiKeyVisible = false;
@ -293,7 +350,15 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
} }
refreshApiKeyVisibility(); refreshApiKeyVisibility();
promptInput.addEventListener('input', persistSettings); promptInput.addEventListener('input', () => {
refreshPromptHighlight();
persistSettings();
});
promptInput.addEventListener('scroll', () => {
if (!promptHighlight) return;
promptHighlight.scrollTop = promptInput.scrollTop;
promptHighlight.scrollLeft = promptInput.scrollLeft;
});
promptNoteInput.addEventListener('input', persistSettings); promptNoteInput.addEventListener('input', persistSettings);
aspectRatioInput.addEventListener('change', persistSettings); aspectRatioInput.addEventListener('change', persistSettings);
resolutionInput.addEventListener('change', persistSettings); resolutionInput.addEventListener('change', persistSettings);
@ -665,6 +730,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (data.refined_prompt) { if (data.refined_prompt) {
promptInput.value = data.refined_prompt; promptInput.value = data.refined_prompt;
refreshPromptHighlight();
persistSettings(); // Save the new prompt persistSettings(); // Save the new prompt
refineModal.classList.add('hidden'); refineModal.classList.add('hidden');
} }
@ -1540,7 +1606,10 @@ document.addEventListener('DOMContentLoaded', () => {
function applyMetadata(metadata) { function applyMetadata(metadata) {
if (!metadata) return; if (!metadata) return;
if (metadata.prompt) promptInput.value = metadata.prompt; if (metadata.prompt) {
promptInput.value = metadata.prompt;
refreshPromptHighlight();
}
// If metadata doesn't have 'note' field, set to empty string instead of keeping current value // If metadata doesn't have 'note' field, set to empty string instead of keeping current value
if (metadata.hasOwnProperty('note')) { if (metadata.hasOwnProperty('note')) {

View file

@ -411,6 +411,66 @@ textarea {
width: 100%; width: 100%;
} }
.prompt-wrapper.prompt-highlighting {
position: relative;
border: 1px solid var(--border-color);
/* border-radius: 0.5rem; */
/* background-color: var(--input-bg); */
backdrop-filter: blur(6px);
overflow: hidden;
}
.prompt-wrapper.prompt-highlighting:focus-within {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
.prompt-highlight {
position: absolute;
inset: 0;
padding: 0.75rem;
overflow: auto;
pointer-events: none;
white-space: pre-wrap;
word-break: break-word;
z-index: 1;
color: var(--text-primary);
font-family: inherit;
font-size: 0.875rem;
line-height: 1.4;
scrollbar-width: none;
}
.prompt-highlight::-webkit-scrollbar {
width: 0;
height: 0;
}
.prompt-highlight-segment {
font-weight: 700;
}
.prompt-highlight .prompt-placeholder {
color: var(--text-secondary);
}
.prompt-wrapper.prompt-highlighting textarea {
position: relative;
z-index: 2;
border: none;
outline: none;
background: transparent;
color: transparent;
caret-color: var(--accent-color);
padding: 0.75rem;
font-family: inherit;
font-size: 0.875rem;
line-height: 1.4;
resize: vertical;
min-height: 100px;
opacity:0.3
}
.prompt-actions { .prompt-actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;

View file

@ -40,7 +40,8 @@
<div class="controls-content"> <div class="controls-content">
<div class="input-group"> <div class="input-group">
<label for="prompt">Prompt</label> <label for="prompt">Prompt</label>
<div class="prompt-wrapper"> <div class="prompt-wrapper prompt-highlighting">
<div id="prompt-highlight" class="prompt-highlight" aria-hidden="true"></div>
<textarea id="prompt" placeholder="Nhập prompt miêu tả..." rows="6"></textarea> <textarea id="prompt" placeholder="Nhập prompt miêu tả..." rows="6"></textarea>
</div> </div>
<div class="prompt-actions"> <div class="prompt-actions">
@ -459,4 +460,4 @@
<script type="module" src="{{ url_for('static', filename='script.js') }}"></script> <script type="module" src="{{ url_for('static', filename='script.js') }}"></script>
</body> </body>
</html> </html>