Hightlight prompt
This commit is contained in:
parent
f1cd681398
commit
f3f69baec1
3 changed files with 134 additions and 4 deletions
|
|
@ -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 '&';
|
||||||
|
case '<': return '<';
|
||||||
|
case '>': return '>';
|
||||||
|
case '"': return '"';
|
||||||
|
case "'": return ''';
|
||||||
|
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')) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue