refactor: replace all inline styles with CSS classes for better maintainability
- Moved 115+ inline styles from uncloseai-embed-modal.js to CSS classes - Cleaned all inline styles from page-reader.js, widget-library.js, chat.js, models.js, file-upload.js - Added CSS custom properties for theming support - Created reusable CSS classes for buttons, containers, and UI elements - Only retained legitimate dynamic styles (height calculations, position adjustments) - Improved consistency with PicoCSS overrides
This commit is contained in:
parent
4561b9edcc
commit
3f9cb2c40f
11 changed files with 905 additions and 665 deletions
29
src/chat.js
29
src/chat.js
|
|
@ -22,31 +22,10 @@ function addCodeBlockCopyButtons(element) {
|
|||
|
||||
// Create copy button
|
||||
const copyBtn = document.createElement("button");
|
||||
copyBtn.className = "code-copy-btn";
|
||||
copyBtn.className = "uncloseai-code-copy-btn";
|
||||
copyBtn.textContent = "📋";
|
||||
copyBtn.title = "Copy code";
|
||||
copyBtn.style.cssText = `
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
copyBtn.onmouseenter = () => {
|
||||
copyBtn.style.opacity = "1";
|
||||
};
|
||||
copyBtn.onmouseleave = () => {
|
||||
copyBtn.style.opacity = "0.8";
|
||||
};
|
||||
// Styling handled by CSS class
|
||||
|
||||
copyBtn.onclick = async () => {
|
||||
try {
|
||||
|
|
@ -162,7 +141,7 @@ export async function handleUserInput() {
|
|||
// Add play/pause button for TTS
|
||||
const playPauseButton = document.createElement("button");
|
||||
playPauseButton.textContent = "Generate TTS for AI Response";
|
||||
playPauseButton.style.margin = "5px";
|
||||
playPauseButton.className = "uncloseai-ui-button-margin";
|
||||
let aiAudio = null;
|
||||
let aiBlob = null;
|
||||
let isPaused = false;
|
||||
|
|
@ -186,7 +165,7 @@ export async function handleUserInput() {
|
|||
// Add download button
|
||||
const downloadButton = document.createElement("button");
|
||||
downloadButton.textContent = "Download MP3";
|
||||
downloadButton.style.margin = "5px";
|
||||
downloadButton.className = "uncloseai-ui-button-margin";
|
||||
downloadButton.onclick = () => {
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(aiBlob);
|
||||
|
|
|
|||
|
|
@ -136,12 +136,12 @@ export async function integrateMegafarceResponse(response) {
|
|||
export function addFileUploadButton() {
|
||||
const button = document.createElement("button");
|
||||
button.textContent = "Upload File";
|
||||
button.style.margin = "10px";
|
||||
button.className = "uncloseai-ui-button-margin";
|
||||
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.id = "file-input";
|
||||
fileInput.style.display = "none";
|
||||
fileInput.style.display = "none"; // Hidden input must remain inline
|
||||
|
||||
button.onclick = () => fileInput.click();
|
||||
fileInput.onchange = handleFileUpload;
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export async function createModelSelectionDropdown() {
|
|||
|
||||
const dropdown = document.createElement("select");
|
||||
dropdown.id = "model-selection";
|
||||
dropdown.style.margin = "10px";
|
||||
dropdown.className = "uncloseai-ui-button-margin";
|
||||
|
||||
models.forEach((model) => {
|
||||
const option = document.createElement("option");
|
||||
|
|
@ -145,7 +145,7 @@ export function addRefreshModelsButton() {
|
|||
// Create a button element
|
||||
const refreshButton = document.createElement("button");
|
||||
refreshButton.textContent = "Refresh Models";
|
||||
refreshButton.style.margin = "10px";
|
||||
refreshButton.className = "uncloseai-ui-button-margin";
|
||||
refreshButton.onclick = async () => {
|
||||
refreshButton.textContent = "Refreshing...";
|
||||
refreshButton.disabled = true;
|
||||
|
|
|
|||
|
|
@ -8,14 +8,7 @@ export async function readPageWithHermes() {
|
|||
|
||||
// Create status indicator
|
||||
const statusDiv = document.createElement("div");
|
||||
statusDiv.style.position = "fixed";
|
||||
statusDiv.style.top = "10px";
|
||||
statusDiv.style.right = "10px";
|
||||
statusDiv.style.padding = "10px";
|
||||
statusDiv.style.background = "rgba(0,0,0,0.8)";
|
||||
statusDiv.style.color = "white";
|
||||
statusDiv.style.borderRadius = "5px";
|
||||
statusDiv.style.zIndex = "1000";
|
||||
statusDiv.className = "uncloseai-page-reader-status";
|
||||
document.body.appendChild(statusDiv);
|
||||
|
||||
statusDiv.textContent = "Processing content and generating speech...";
|
||||
|
|
@ -32,13 +25,13 @@ export async function readPageWithHermes() {
|
|||
// Add pause/resume button
|
||||
const pauseButton = document.createElement("button");
|
||||
pauseButton.textContent = "Pause Reading";
|
||||
pauseButton.style.marginLeft = "10px";
|
||||
pauseButton.className = "uncloseai-btn-margin-left";
|
||||
statusDiv.appendChild(pauseButton);
|
||||
|
||||
// Add download button
|
||||
const downloadButton = document.createElement("button");
|
||||
downloadButton.textContent = "Download MP3";
|
||||
downloadButton.style.marginLeft = "10px";
|
||||
downloadButton.className = "uncloseai-btn-margin-left";
|
||||
downloadButton.onclick = async () => {
|
||||
try {
|
||||
const { generateTitleForTTS } = await import("./tts.js");
|
||||
|
|
|
|||
|
|
@ -184,8 +184,8 @@ export function openTranslateModal() {
|
|||
|
||||
// Visual styles are now handled by CSS classes
|
||||
|
||||
customContent.style.display = "block";
|
||||
pageContent.style.display = "none";
|
||||
customContent.classList.add("active");
|
||||
pageContent.classList.remove("active");
|
||||
translateButton.textContent = getUIText("translateTextButton");
|
||||
}
|
||||
};
|
||||
|
|
@ -198,7 +198,7 @@ export function openTranslateModal() {
|
|||
try {
|
||||
translateButton.textContent = getUIText("processingText");
|
||||
translateButton.disabled = true;
|
||||
resultContainer.style.display = "none";
|
||||
resultContainer.classList.remove("show");
|
||||
|
||||
// Get exact tokenization for the UI progress indicator
|
||||
const { extractPageContent } = await import("./translation.js");
|
||||
|
|
@ -232,12 +232,11 @@ ${preservedText}`;
|
|||
progressContainer.className = "translate-progress-container";
|
||||
|
||||
const progressText = document.createElement("div");
|
||||
progressText.style.cssText =
|
||||
"font-weight: bold; color: #4CAF50; margin-bottom: 10px;";
|
||||
progressText.className = "translate-progress-text";
|
||||
progressText.textContent = getUIText("processingTokens", { tokens: tokenInfo.totalTokens });
|
||||
|
||||
const etaText = document.createElement("div");
|
||||
etaText.style.cssText = "color: #666; margin-bottom: 15px;";
|
||||
etaText.className = "translate-eta-text";
|
||||
etaText.textContent = `Estimated time: ${estimatedSeconds} seconds`;
|
||||
|
||||
const progressBar = document.createElement("div");
|
||||
|
|
@ -248,7 +247,7 @@ ${preservedText}`;
|
|||
progressBar.appendChild(progressFill);
|
||||
|
||||
const statusText = document.createElement("div");
|
||||
statusText.style.cssText = "color: #777; font-size: 0.9em;";
|
||||
statusText.className = "translate-status-text";
|
||||
statusText.textContent = "Initializing translation...";
|
||||
|
||||
progressContainer.appendChild(progressText);
|
||||
|
|
@ -270,7 +269,7 @@ ${preservedText}`;
|
|||
(elapsed / (estimatedSeconds * 1000)) * 100,
|
||||
95,
|
||||
);
|
||||
progressFill.style.width = `${progress}%`;
|
||||
progressFill.style.width = `${progress}%`; // Dynamic width needs to stay inline
|
||||
|
||||
const remainingTime = Math.max(
|
||||
0,
|
||||
|
|
@ -362,8 +361,7 @@ ${preservedText}`;
|
|||
) {
|
||||
console.log("Adding AI notice to preview window");
|
||||
const notice = previewWindow.document.createElement("div");
|
||||
notice.style.cssText =
|
||||
"position: fixed; top: 10px; right: 10px; background: #4CAF50; color: white; padding: 8px 12px; border-radius: 4px; font-size: 12px; z-index: 10000; box-shadow: 0 2px 8px rgba(0,0,0,0.2);";
|
||||
notice.className = "translate-ai-notice";
|
||||
notice.textContent = `🤖 AI features should work in ${targetLanguageName}!`;
|
||||
previewWindow.document.body.appendChild(notice);
|
||||
|
||||
|
|
@ -386,7 +384,7 @@ ${preservedText}`;
|
|||
// Show raw HTML toggle in modal
|
||||
resultArea.innerHTML = `
|
||||
<div style="margin-bottom: 15px;">
|
||||
<button onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'; this.textContent = this.textContent.includes('Raw') ? 'Show Raw HTML' : 'Hide Raw HTML'" style="padding: 8px 16px; background: #666; color: white; border: none; border-radius: 4px; cursor: pointer;">Show Raw HTML</button>
|
||||
<button onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'; this.textContent = this.textContent.includes('Raw') ? 'Show Raw HTML' : 'Hide Raw HTML'" class="translate-raw-toggle-btn">Show Raw HTML</button>
|
||||
<div style="display: none; margin: 10px 0; padding: 15px; background: #f0f0f0; border-radius: 4px; white-space: pre-wrap; font-family: monospace; font-size: 0.85em; max-height: 300px; overflow-y: auto; border: 1px solid #ddd;">${translatedText.replace(/</g, "<").replace(/>/g, ">")}</div>
|
||||
</div>
|
||||
<div style="border: 2px solid #4CAF50; border-radius: 8px; background: #f0fff0; padding: 15px; text-align: center;">
|
||||
|
|
@ -395,13 +393,13 @@ ${preservedText}`;
|
|||
${previewWindow ? "" : '<p style="margin: 10px 0 0 0; color: #d32f2f;"><strong>Note:</strong> Please allow popups for preview window</p>'}
|
||||
</div>
|
||||
`;
|
||||
resultArea.style.whiteSpace = "normal";
|
||||
resultArea.className = "translate-result-area";
|
||||
} else {
|
||||
resultArea.textContent = translatedText;
|
||||
resultArea.style.whiteSpace = "pre-wrap";
|
||||
resultArea.className = "translate-result-area pre-wrap";
|
||||
}
|
||||
|
||||
resultContainer.style.display = "block";
|
||||
resultContainer.classList.add("show");
|
||||
|
||||
// Complete progress bar
|
||||
clearInterval(progressInterval);
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ export function openTTSModal() {
|
|||
|
||||
const result = await handleTTS(text, voice, speed);
|
||||
if (result.audio) {
|
||||
resultDiv.style.display = "block";
|
||||
resultDiv.classList.add("show");
|
||||
resultDiv.innerHTML = "";
|
||||
|
||||
// Create hidden audio element (no controls)
|
||||
|
|
|
|||
71
src/ui.js
71
src/ui.js
|
|
@ -90,28 +90,7 @@ function addCodeBlockCopyButtons(element) {
|
|||
const copyBtn = document.createElement("button");
|
||||
copyBtn.textContent = "📋";
|
||||
copyBtn.title = "Copy code";
|
||||
copyBtn.style.cssText = `
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 10;
|
||||
`;
|
||||
|
||||
copyBtn.onmouseenter = () => {
|
||||
copyBtn.style.opacity = "1";
|
||||
};
|
||||
copyBtn.onmouseleave = () => {
|
||||
copyBtn.style.opacity = "0.8";
|
||||
};
|
||||
copyBtn.className = "uncloseai-code-copy-btn";
|
||||
|
||||
copyBtn.onclick = async (e) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -268,8 +247,7 @@ export function initializeUncloseaiElements() {
|
|||
|
||||
// Create container for this uncloseai instance
|
||||
const container = document.createElement("div");
|
||||
container.className = "uncloseai-container";
|
||||
container.style.cssText = "width: 100%; margin: 10px 0;";
|
||||
container.className = "uncloseai-container uncloseai-ui-container";
|
||||
|
||||
if (features === "full" || type === "full") {
|
||||
createFullInterface(container);
|
||||
|
|
@ -326,12 +304,12 @@ export function addReadPageButton() {
|
|||
const button = document.createElement("button");
|
||||
button.textContent = "Read Page";
|
||||
button.onclick = readPageWithHermes;
|
||||
button.style.margin = "10px";
|
||||
button.className = "uncloseai-ui-button-margin";
|
||||
|
||||
const ttsButton = document.createElement("button");
|
||||
ttsButton.textContent = "TTS Anything";
|
||||
ttsButton.onclick = openTTSModal;
|
||||
ttsButton.style.margin = "10px";
|
||||
ttsButton.className = "uncloseai-ui-button-margin";
|
||||
|
||||
document.body.appendChild(button);
|
||||
document.body.appendChild(ttsButton);
|
||||
|
|
@ -366,25 +344,13 @@ export function createFloatingAIButton() {
|
|||
(window.matchMedia?.("(prefers-color-scheme: dark)").matches &&
|
||||
!document.documentElement.getAttribute("data-theme"));
|
||||
|
||||
floatingButton.style.cssText = `
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 10px;
|
||||
width: 110px;
|
||||
height: 55px;
|
||||
border-radius: 22px;
|
||||
background: ${isDark ? "#ffffff" : "#000000"};
|
||||
border: 2px solid ${isDark ? "#000000" : "#ffffff"};
|
||||
color: ${isDark ? "#000000" : "#ffffff"};
|
||||
font-family: 'ChunkFiveRegular', monospace;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
max-width: calc(100vw - 20px);
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
// Set floating button theme colors via CSS custom properties
|
||||
const root = document.documentElement;
|
||||
root.style.setProperty('--floating-button-bg', isDark ? "#ffffff" : "#000000");
|
||||
root.style.setProperty('--floating-button-border', isDark ? "#000000" : "#ffffff");
|
||||
root.style.setProperty('--floating-button-text', isDark ? "#000000" : "#ffffff");
|
||||
|
||||
floatingButton.className = "uncloseai-floating-button";
|
||||
}
|
||||
|
||||
// Initial theme setup
|
||||
|
|
@ -405,14 +371,7 @@ export function createFloatingAIButton() {
|
|||
}
|
||||
|
||||
// Hover effects
|
||||
floatingButton.onmouseenter = () => {
|
||||
floatingButton.style.transform = "scale(1.1)";
|
||||
floatingButton.style.boxShadow = "0 6px 16px rgba(0,0,0,0.4)";
|
||||
};
|
||||
floatingButton.onmouseleave = () => {
|
||||
floatingButton.style.transform = "scale(1)";
|
||||
floatingButton.style.boxShadow = "0 4px 12px rgba(0,0,0,0.3)";
|
||||
};
|
||||
// Hover effects now handled by CSS
|
||||
|
||||
// Set up self-contained click handler
|
||||
setupFloatingButtonHandler(floatingButton);
|
||||
|
|
@ -430,8 +389,7 @@ function setupFloatingButtonHandler(floatingButton) {
|
|||
const originalText = floatingButton.textContent;
|
||||
floatingButton.textContent = "loading...";
|
||||
floatingButton.disabled = true;
|
||||
floatingButton.style.opacity = "0.7";
|
||||
floatingButton.style.cursor = "wait";
|
||||
floatingButton.classList.add("loading");
|
||||
|
||||
try {
|
||||
console.log("Trying to toggle Hermes modal...");
|
||||
|
|
@ -480,8 +438,7 @@ function setupFloatingButtonHandler(floatingButton) {
|
|||
// Restore button state
|
||||
floatingButton.textContent = originalText;
|
||||
floatingButton.disabled = false;
|
||||
floatingButton.style.opacity = "1";
|
||||
floatingButton.style.cursor = "pointer";
|
||||
floatingButton.classList.remove("loading");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,26 +191,11 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Simple container with flex layout
|
||||
const container = document.createElement("div");
|
||||
container.style.cssText = `
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
`;
|
||||
container.className = "uncloseai-modal-container";
|
||||
|
||||
// Header
|
||||
const header = document.createElement("header");
|
||||
header.style.cssText = `
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px ${colors.lightShadow};
|
||||
`;
|
||||
header.className = "uncloseai-modal-header";
|
||||
|
||||
const titleContainer = document.createElement("div");
|
||||
titleContainer.className = "uncloseai-title-container";
|
||||
|
|
@ -218,18 +203,14 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const title = document.createElement("h2");
|
||||
title.innerHTML =
|
||||
"🤖 <span style=\"font-family: 'ChunkFiveRegular', monospace;\">uncloseai.</span>";
|
||||
title.style.cssText = `
|
||||
margin: 0;
|
||||
font-size: ${isMobile ? "18px" : "20px"};
|
||||
font-weight: 600;
|
||||
`;
|
||||
title.className = `uncloseai-modal-title ${isMobile ? "uncloseai-modal-title-mobile" : "uncloseai-modal-title-desktop"}`;
|
||||
|
||||
const subtitle = document.createElement("div");
|
||||
const pageTitle = document.title || window.location.hostname;
|
||||
subtitle.innerHTML = `
|
||||
<div style="font-size: 12px; opacity: 0.9; font-weight: normal; line-height: 1.3;">
|
||||
<div class="uncloseai-modal-subtitle">
|
||||
<span style="font-family: 'ChunkFiveRegular', monospace;">uncloseai.</span> ${getUIText("hermesIntro")}<br>
|
||||
<span style="opacity: 0.8;">${getUIText("discussingPage")}: ${pageTitle}</span>
|
||||
<span class="uncloseai-modal-subtitle-dim">${getUIText("discussingPage")}: ${pageTitle}</span>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
|
@ -260,36 +241,16 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Chat area
|
||||
const chatArea = document.createElement("div");
|
||||
chatArea.style.cssText = `
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
background: ${colors.contentBackground};
|
||||
-webkit-overflow-scrolling: touch;
|
||||
`;
|
||||
chatArea.className = "uncloseai-modal-chat-area";
|
||||
|
||||
const chatBox = document.createElement("div");
|
||||
chatBox.id = "modal-chat-box";
|
||||
chatBox.style.cssText = `
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
`;
|
||||
chatBox.className = "uncloseai-chat-box";
|
||||
chatArea.appendChild(chatBox);
|
||||
|
||||
// Settings panel (collapsible)
|
||||
const settingsPanel = document.createElement("div");
|
||||
settingsPanel.style.cssText = `
|
||||
flex-shrink: 0;
|
||||
background: ${colors.panelBackground};
|
||||
border-top: 1px solid ${colors.dividerColor};
|
||||
border-bottom: 1px solid ${colors.dividerColor};
|
||||
padding: 16px;
|
||||
display: none;
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
settingsPanel.className = "uncloseai-modal-settings";
|
||||
|
||||
// Model selection
|
||||
const modelSection = document.createElement("div");
|
||||
|
|
@ -300,15 +261,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
modelLabel.className = "uncloseai-label";
|
||||
|
||||
const modelSelect = document.createElement("select");
|
||||
modelSelect.style.cssText = `
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: ${colors.inputBackground};
|
||||
color: ${USE_CUSTOM_STYLING ? 'black' : colors.primaryText};
|
||||
`;
|
||||
modelSelect.className = "uncloseai-modal-select";
|
||||
|
||||
// Store models for selection handler
|
||||
let loadedModels = [];
|
||||
|
|
@ -400,15 +353,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
const voiceSelect = document.createElement("select");
|
||||
voiceSelect.id = "hermes-voice-select";
|
||||
voiceSelect.style.cssText = `
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: ${colors.inputBackground};
|
||||
color: ${USE_CUSTOM_STYLING ? 'black' : colors.primaryText};
|
||||
`;
|
||||
voiceSelect.className = "uncloseai-modal-select";
|
||||
|
||||
const voices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
|
||||
voices.forEach((voice, index) => {
|
||||
|
|
@ -448,15 +393,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
const languageSelect = document.createElement("select");
|
||||
languageSelect.id = "hermes-language-select";
|
||||
languageSelect.style.cssText = `
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: ${colors.inputBackground};
|
||||
color: ${USE_CUSTOM_STYLING ? 'black' : colors.primaryText};
|
||||
`;
|
||||
languageSelect.className = "uncloseai-modal-select";
|
||||
|
||||
// Add options for each supported language
|
||||
Object.entries(SUPPORTED_LANGUAGES).forEach(([code, englishName]) => {
|
||||
|
|
@ -500,15 +437,10 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
const actionsLabel = document.createElement("div");
|
||||
actionsLabel.textContent = getUIText("quickActions");
|
||||
actionsLabel.style.cssText =
|
||||
"margin-bottom: 8px; font-weight: 600; color: #495057;";
|
||||
actionsLabel.className = "uncloseai-label";
|
||||
|
||||
const actionsGrid = document.createElement("div");
|
||||
actionsGrid.style.cssText = `
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 8px;
|
||||
`;
|
||||
actionsGrid.className = "uncloseai-grid-actions";
|
||||
|
||||
const actions = [
|
||||
{ text: getUIText("refreshModels"), action: loadModels },
|
||||
|
|
@ -551,33 +483,19 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Load and save settings panel state
|
||||
let settingsOpen = localStorage.getItem("hermesSettingsOpen") === "true";
|
||||
settingsPanel.style.display = settingsOpen ? "block" : "none";
|
||||
menuBtn.style.background = settingsOpen
|
||||
? "rgba(255,255,255,0.3)"
|
||||
: "rgba(255,255,255,0.2)";
|
||||
if (settingsOpen) {
|
||||
settingsPanel.classList.add("open");
|
||||
}
|
||||
|
||||
menuBtn.onclick = () => {
|
||||
settingsOpen = !settingsOpen;
|
||||
settingsPanel.style.display = settingsOpen ? "block" : "none";
|
||||
menuBtn.style.background = settingsOpen
|
||||
? "rgba(255,255,255,0.3)"
|
||||
: "rgba(255,255,255,0.2)";
|
||||
settingsPanel.classList.toggle("open", settingsOpen);
|
||||
localStorage.setItem("hermesSettingsOpen", settingsOpen.toString());
|
||||
};
|
||||
|
||||
// Quick action buttons
|
||||
const controls = document.createElement("div");
|
||||
controls.style.cssText = `
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
background: ${colors.panelBackground};
|
||||
border-top: 1px solid ${colors.dividerColor};
|
||||
border-bottom: 1px solid ${colors.dividerColor};
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
`;
|
||||
controls.className = "uncloseai-modal-controls";
|
||||
|
||||
// HTML to Markdown converter function
|
||||
function htmlToMarkdown(html) {
|
||||
|
|
@ -722,11 +640,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
// Create download button next to the read button
|
||||
if (!readPageContainer) {
|
||||
readPageContainer = document.createElement("div");
|
||||
readPageContainer.style.cssText = `
|
||||
display: inline-flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
`;
|
||||
readPageContainer.className = "uncloseai-read-page-container";
|
||||
|
||||
// Move the read button into the container
|
||||
const parent = button.parentElement;
|
||||
|
|
@ -832,30 +746,10 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
// Create dropdown
|
||||
const dropdown = document.createElement("div");
|
||||
dropdown.className = "translate-dropdown";
|
||||
dropdown.style.cssText = `
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
background: ${colors.panelBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px ${colors.shadowColor};
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-top: 4px;
|
||||
`;
|
||||
|
||||
// Add current language indicator
|
||||
const currentLangDiv = document.createElement("div");
|
||||
currentLangDiv.style.cssText = `
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
color: ${colors.mutedText};
|
||||
border-bottom: 1px solid ${colors.dividerColor};
|
||||
background: ${colors.contentBackground};
|
||||
`;
|
||||
currentLangDiv.className = "translate-dropdown-current";
|
||||
const currentLangName = SUPPORTED_LANGUAGES[currentLang] || "Unknown";
|
||||
currentLangDiv.textContent = `Current: ${currentLangName} (${currentLang})`;
|
||||
dropdown.appendChild(currentLangDiv);
|
||||
|
|
@ -866,27 +760,16 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
if (code === currentLang) return;
|
||||
|
||||
const option = document.createElement("div");
|
||||
option.style.cssText = `
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
color: ${colors.primaryText};
|
||||
font-size: 14px;
|
||||
`;
|
||||
option.className = "translate-dropdown-option";
|
||||
option.textContent = `${name} (${code})`;
|
||||
|
||||
option.onmouseenter = () => {
|
||||
option.style.background = colors.buttonHover;
|
||||
};
|
||||
option.onmouseleave = () => {
|
||||
option.style.background = "transparent";
|
||||
};
|
||||
// Hover effects handled by CSS
|
||||
|
||||
option.onclick = async () => {
|
||||
try {
|
||||
// Show loading
|
||||
option.textContent = `Translating to ${name}...`;
|
||||
option.style.pointerEvents = "none";
|
||||
option.classList.add("translate-dropdown-option-disabled");
|
||||
|
||||
// Translate the page
|
||||
const translatedHtml = await translateCurrentPage(code);
|
||||
|
|
@ -902,7 +785,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
} catch (error) {
|
||||
alert(`Translation failed: ${error.message}`);
|
||||
option.textContent = `${name} (${code})`;
|
||||
option.style.pointerEvents = "auto";
|
||||
option.classList.remove("translate-dropdown-option-disabled");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -995,25 +878,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
controlActions.forEach(({ text, action }) => {
|
||||
const btn = document.createElement("button");
|
||||
btn.textContent = text;
|
||||
btn.style.cssText = `
|
||||
padding: 8px 16px;
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.buttonBorder};
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
color: ${colors.primaryText} !important;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
btn.onmouseover = () => {
|
||||
btn.style.background = colors.buttonHover;
|
||||
btn.style.borderColor = colors.buttonBorder;
|
||||
};
|
||||
btn.onmouseout = () => {
|
||||
btn.style.background = colors.buttonBackground;
|
||||
btn.style.borderColor = colors.buttonBorder;
|
||||
};
|
||||
btn.className = "uncloseai-btn-primary";
|
||||
btn.onclick = () => action(btn);
|
||||
controls.appendChild(btn);
|
||||
});
|
||||
|
|
@ -1026,37 +891,12 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Input area
|
||||
const inputArea = document.createElement("div");
|
||||
inputArea.style.cssText = `
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
background: ${colors.panelBackground};
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-end;
|
||||
box-shadow: 0 -2px 10px ${colors.lightShadow};
|
||||
`;
|
||||
inputArea.className = "uncloseai-modal-input-area";
|
||||
|
||||
const input = document.createElement("textarea");
|
||||
input.id = "modal-user-input";
|
||||
input.placeholder = getUIText("typePlaceholder");
|
||||
input.style.cssText = `
|
||||
flex: 1;
|
||||
min-height: 44px;
|
||||
max-height: 120px;
|
||||
padding: 12px;
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 24px;
|
||||
resize: none;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
line-height: 1.4;
|
||||
outline: none;
|
||||
background: ${colors.inputBackground};
|
||||
color: ${USE_CUSTOM_STYLING ? 'black' : colors.primaryText};
|
||||
transition: border-color 0.2s;
|
||||
`;
|
||||
input.onfocus = () => (input.style.borderColor = colors.focusBorder);
|
||||
input.onblur = () => (input.style.borderColor = colors.borderColor);
|
||||
input.className = "uncloseai-modal-input";
|
||||
|
||||
// Auto-resize textarea
|
||||
input.oninput = () => {
|
||||
|
|
@ -1066,25 +906,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
const sendBtn = document.createElement("button");
|
||||
sendBtn.textContent = "→";
|
||||
sendBtn.style.cssText = `
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.2s;
|
||||
flex-shrink: 0;
|
||||
`;
|
||||
sendBtn.onmouseover = () => (sendBtn.style.transform = "scale(1.05)");
|
||||
sendBtn.onmouseout = () => (sendBtn.style.transform = "scale(1)");
|
||||
sendBtn.className = "uncloseai-modal-send-btn";
|
||||
|
||||
// Handle message sending
|
||||
const sendMessageHandler = async () => {
|
||||
|
|
@ -1097,20 +919,11 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
// Add user message
|
||||
const userMsg = document.createElement("div");
|
||||
userMsg.className = "user-message";
|
||||
userMsg.style.cssText = `
|
||||
align-self: flex-end;
|
||||
background: ${colors.userMessageBg};
|
||||
color: ${colors.userMessageText};
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 4px 18px;
|
||||
max-width: 80%;
|
||||
word-wrap: break-word;
|
||||
`;
|
||||
|
||||
// Add message text and actions inside the bubble
|
||||
const messageText = document.createElement("div");
|
||||
messageText.textContent = message;
|
||||
messageText.style.cssText = "margin-bottom: 8px;";
|
||||
messageText.className = "message-text";
|
||||
|
||||
const userMessageActions = document.createElement("div");
|
||||
userMessageActions.className = "uncloseai-message-actions";
|
||||
|
|
@ -1158,18 +971,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.textContent = "💾";
|
||||
downloadBtn.title = "Download MP3";
|
||||
downloadBtn.className = "download-btn";
|
||||
downloadBtn.style.cssText = `
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.userMessageText};
|
||||
transition: all 0.2s;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
downloadBtn.className = "uncloseai-download-btn";
|
||||
downloadBtn.onclick = async () => {
|
||||
try {
|
||||
const { generateTitleForTTS } = await import("./tts.js");
|
||||
|
|
@ -1255,16 +1057,8 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Add AI response placeholder
|
||||
const aiMsg = document.createElement("div");
|
||||
aiMsg.style.cssText = `
|
||||
align-self: flex-start;
|
||||
background: ${colors.aiMessageBg};
|
||||
color: ${colors.aiMessageText};
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 18px 4px;
|
||||
max-width: 80%;
|
||||
box-shadow: 0 1px 2px ${colors.lightShadow};
|
||||
`;
|
||||
aiMsg.innerHTML = `<em style="color: ${colors.mutedText};">Thinking...</em>`;
|
||||
aiMsg.className = "ai-message";
|
||||
aiMsg.innerHTML = `<em>Thinking...</em>`;
|
||||
chatBox.appendChild(aiMsg);
|
||||
|
||||
chatArea.scrollTop = chatArea.scrollHeight;
|
||||
|
|
@ -1310,26 +1104,12 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
|
||||
// Add TTS and delete buttons to AI message
|
||||
const messageActions = document.createElement("div");
|
||||
messageActions.style.cssText = `
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
opacity: 0.7;
|
||||
`;
|
||||
messageActions.className = "uncloseai-message-actions";
|
||||
|
||||
const ttsBtn = document.createElement("button");
|
||||
ttsBtn.textContent = "🔊";
|
||||
ttsBtn.title = "Read aloud";
|
||||
ttsBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
ttsBtn.className = "uncloseai-btn-small";
|
||||
// Store audio for pause/resume functionality
|
||||
let currentAudio = null;
|
||||
let isPlaying = false;
|
||||
|
|
@ -1375,18 +1155,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.textContent = "💾";
|
||||
downloadBtn.title = "Download MP3";
|
||||
downloadBtn.className = "download-btn";
|
||||
downloadBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
downloadBtn.className = "uncloseai-btn-small";
|
||||
downloadBtn.onclick = async () => {
|
||||
try {
|
||||
const { generateTitleForTTS } = await import("./tts.js");
|
||||
|
|
@ -1449,16 +1218,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.textContent = "🗑️";
|
||||
deleteBtn.title = "Delete message";
|
||||
deleteBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
deleteBtn.className = "uncloseai-btn-small";
|
||||
deleteBtn.onclick = async () => {
|
||||
// Remove both user and AI messages from history and localStorage
|
||||
const { loadConversationHistory, saveConversationHistory } = await import("./storage.js");
|
||||
|
|
@ -1484,16 +1244,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const copyRawBtn = document.createElement("button");
|
||||
copyRawBtn.textContent = "📋";
|
||||
copyRawBtn.title = "Copy raw (Markdown)";
|
||||
copyRawBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyRawBtn.className = "uncloseai-btn-small";
|
||||
copyRawBtn.onclick = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(response);
|
||||
|
|
@ -1511,16 +1262,7 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
const copyHtmlBtn = document.createElement("button");
|
||||
copyHtmlBtn.textContent = "📄";
|
||||
copyHtmlBtn.title = "Copy HTML";
|
||||
copyHtmlBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyHtmlBtn.className = "uncloseai-btn-small";
|
||||
copyHtmlBtn.onclick = async () => {
|
||||
try {
|
||||
const htmlContent = marked.parse(response);
|
||||
|
|
@ -1588,61 +1330,20 @@ async function openUncloseaiEmbeddedModalNew() {
|
|||
root.style.setProperty('--uncloseai-action-border', newColors.actionBorder);
|
||||
root.style.setProperty('--uncloseai-action-text', newColors.primaryText);
|
||||
|
||||
// Update modal background and main color
|
||||
modal.style.background = newColors.modalBackground;
|
||||
modal.style.color = newColors.primaryText;
|
||||
// Update CSS variables for dynamic theming
|
||||
root.style.setProperty('--uncloseai-modal-background', newColors.modalBackground);
|
||||
root.style.setProperty('--uncloseai-content-background', newColors.contentBackground);
|
||||
root.style.setProperty('--uncloseai-panel-background', newColors.panelBackground);
|
||||
root.style.setProperty('--uncloseai-input-background', newColors.inputBackground);
|
||||
root.style.setProperty('--uncloseai-divider-color', newColors.dividerColor);
|
||||
root.style.setProperty('--uncloseai-border-color', newColors.borderColor);
|
||||
|
||||
// Update chat area background
|
||||
chatArea.style.background = newColors.contentBackground;
|
||||
// Update modal theme attribute
|
||||
modal.setAttribute('data-theme', detectCurrentTheme());
|
||||
|
||||
// Update settings panel background
|
||||
settingsPanel.style.background = newColors.panelBackground;
|
||||
settingsPanel.style.borderTopColor = newColors.dividerColor;
|
||||
settingsPanel.style.borderBottomColor = newColors.dividerColor;
|
||||
// Control buttons now use CSS classes and variables
|
||||
|
||||
// Update input area
|
||||
inputArea.style.background = newColors.panelBackground;
|
||||
input.style.background = newColors.inputBackground;
|
||||
input.style.color = newColors.primaryText;
|
||||
input.style.borderColor = newColors.borderColor;
|
||||
|
||||
// Update controls panel
|
||||
controls.style.background = newColors.panelBackground;
|
||||
controls.style.borderTopColor = newColors.dividerColor;
|
||||
controls.style.borderBottomColor = newColors.dividerColor;
|
||||
|
||||
// Update all control buttons
|
||||
controls.querySelectorAll("button").forEach((btn) => {
|
||||
btn.style.background = newColors.buttonBackground;
|
||||
btn.style.borderColor = newColors.buttonBorder;
|
||||
btn.style.color = `${newColors.primaryText} !important`;
|
||||
|
||||
// Re-attach hover handlers with new colors
|
||||
btn.onmouseover = () => {
|
||||
btn.style.background = newColors.buttonHover;
|
||||
btn.style.borderColor = newColors.buttonBorder;
|
||||
};
|
||||
btn.onmouseout = () => {
|
||||
btn.style.background = newColors.buttonBackground;
|
||||
btn.style.borderColor = newColors.buttonBorder;
|
||||
};
|
||||
});
|
||||
|
||||
// Update all message bubbles
|
||||
chatBox
|
||||
.querySelectorAll('div[style*="align-self: flex-end"]')
|
||||
.forEach((userMsg) => {
|
||||
userMsg.style.background = newColors.userMessageBg;
|
||||
userMsg.style.color = newColors.userMessageText;
|
||||
});
|
||||
|
||||
chatBox
|
||||
.querySelectorAll('div[style*="align-self: flex-start"]')
|
||||
.forEach((aiMsg) => {
|
||||
aiMsg.style.background = newColors.aiMessageBg;
|
||||
aiMsg.style.color = newColors.aiMessageText;
|
||||
aiMsg.style.boxShadow = `0 1px 2px ${newColors.lightShadow}`;
|
||||
});
|
||||
// Message bubbles now use CSS classes with theme support
|
||||
};
|
||||
|
||||
// Set up theme change listeners
|
||||
|
|
@ -1726,48 +1427,19 @@ You have complete knowledge of this page content and can reference any details,
|
|||
if (msg.role === "user") {
|
||||
const userMsg = document.createElement("div");
|
||||
userMsg.className = "user-message";
|
||||
userMsg.style.cssText = `
|
||||
align-self: flex-end;
|
||||
background: ${colors.userMessageBg};
|
||||
color: ${colors.userMessageText};
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 4px 18px;
|
||||
max-width: 80%;
|
||||
word-wrap: break-word;
|
||||
`;
|
||||
|
||||
// Add message text and actions inside the bubble
|
||||
const messageText = document.createElement("div");
|
||||
messageText.textContent = msg.content;
|
||||
messageText.style.cssText = "margin-bottom: 8px;";
|
||||
messageText.className = "message-text";
|
||||
|
||||
const userMessageActions = document.createElement("div");
|
||||
userMessageActions.style.cssText = `
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
opacity: 0.8;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
userMessageActions.className = "uncloseai-message-actions";
|
||||
|
||||
const userTtsBtn = document.createElement("button");
|
||||
userTtsBtn.textContent = "🔊";
|
||||
userTtsBtn.title = "Read aloud";
|
||||
userTtsBtn.style.cssText = `
|
||||
background: ${colors.actionBg};
|
||||
border: 1px solid ${colors.actionBorder};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.userMessageText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
userTtsBtn.onmouseenter = () => {
|
||||
userTtsBtn.style.background = "rgba(255, 255, 255, 0.3)";
|
||||
};
|
||||
userTtsBtn.onmouseleave = () => {
|
||||
userTtsBtn.style.background = "rgba(255, 255, 255, 0.2)";
|
||||
};
|
||||
userTtsBtn.className = "uncloseai-btn-small";
|
||||
|
||||
// Store audio for pause/resume functionality
|
||||
let historicalUserAudio = null;
|
||||
|
|
@ -1814,18 +1486,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.textContent = "💾";
|
||||
downloadBtn.title = "Download MP3";
|
||||
downloadBtn.className = "download-btn";
|
||||
downloadBtn.style.cssText = `
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.userMessageText};
|
||||
transition: all 0.2s;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
downloadBtn.className = "uncloseai-download-btn";
|
||||
downloadBtn.onclick = async () => {
|
||||
try {
|
||||
const { generateTitleForTTS } = await import("./tts.js");
|
||||
|
|
@ -1882,22 +1543,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const userDeleteBtn = document.createElement("button");
|
||||
userDeleteBtn.textContent = "🗑️";
|
||||
userDeleteBtn.title = "Delete message";
|
||||
userDeleteBtn.style.cssText = `
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.userMessageText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
userDeleteBtn.onmouseenter = () => {
|
||||
userDeleteBtn.style.background = "rgba(255, 255, 255, 0.3)";
|
||||
};
|
||||
userDeleteBtn.onmouseleave = () => {
|
||||
userDeleteBtn.style.background = "rgba(255, 255, 255, 0.2)";
|
||||
};
|
||||
userDeleteBtn.className = "uncloseai-btn-small";
|
||||
userDeleteBtn.onclick = async () => {
|
||||
// Remove from chat history by index
|
||||
const currentHistory = await loadConversationHistory();
|
||||
|
|
@ -1926,40 +1572,18 @@ You have complete knowledge of this page content and can reference any details,
|
|||
console.log("Added user message to chatBox");
|
||||
} else if (msg.role === "assistant") {
|
||||
const aiMsg = document.createElement("div");
|
||||
aiMsg.style.cssText = `
|
||||
align-self: flex-start;
|
||||
background: ${colors.aiMessageBg};
|
||||
color: ${colors.aiMessageText};
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 18px 4px;
|
||||
max-width: 80%;
|
||||
box-shadow: 0 1px 2px ${colors.lightShadow};
|
||||
`;
|
||||
aiMsg.className = "ai-message";
|
||||
aiMsg.innerHTML = marked.parse(msg.content);
|
||||
addCodeBlockCopyButtons(aiMsg);
|
||||
|
||||
// Add TTS and delete buttons to historical AI messages
|
||||
const messageActions = document.createElement("div");
|
||||
messageActions.style.cssText = `
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
opacity: 0.7;
|
||||
`;
|
||||
messageActions.className = "uncloseai-message-actions";
|
||||
|
||||
const ttsBtn = document.createElement("button");
|
||||
ttsBtn.textContent = "🔊";
|
||||
ttsBtn.title = "Read aloud";
|
||||
ttsBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
ttsBtn.className = "uncloseai-btn-small";
|
||||
// Store audio for pause/resume functionality
|
||||
let historicalAudio = null;
|
||||
|
||||
|
|
@ -2008,17 +1632,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const downloadBtn = document.createElement("button");
|
||||
downloadBtn.textContent = "💾";
|
||||
downloadBtn.title = "Download MP3";
|
||||
downloadBtn.className = "download-btn";
|
||||
downloadBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
downloadBtn.className = "uncloseai-btn-small";
|
||||
downloadBtn.onclick = async () => {
|
||||
try {
|
||||
const { generateTitleForTTS } = await import("./tts.js");
|
||||
|
|
@ -2081,15 +1695,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.textContent = "🗑️";
|
||||
deleteBtn.title = "Delete message";
|
||||
deleteBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
deleteBtn.className = "uncloseai-btn-small";
|
||||
deleteBtn.onclick = async () => {
|
||||
// Remove from chat history and localStorage by index
|
||||
const { loadConversationHistory, saveConversationHistory } = await import("./storage.js");
|
||||
|
|
@ -2111,15 +1717,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const copyRawBtn = document.createElement("button");
|
||||
copyRawBtn.textContent = "📋";
|
||||
copyRawBtn.title = "Copy raw (Markdown)";
|
||||
copyRawBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyRawBtn.className = "uncloseai-btn-small";
|
||||
copyRawBtn.onclick = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(msg.content);
|
||||
|
|
@ -2137,15 +1735,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const copyHtmlBtn = document.createElement("button");
|
||||
copyHtmlBtn.textContent = "📄";
|
||||
copyHtmlBtn.title = "Copy HTML";
|
||||
copyHtmlBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyHtmlBtn.className = "uncloseai-btn-small";
|
||||
copyHtmlBtn.onclick = async () => {
|
||||
try {
|
||||
const htmlContent = marked.parse(msg.content);
|
||||
|
|
@ -2183,15 +1773,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
|
||||
const addIntroMessage = async () => {
|
||||
const introMsg = document.createElement("div");
|
||||
introMsg.style.cssText = `
|
||||
align-self: flex-start;
|
||||
background: white;
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 18px 4px;
|
||||
max-width: 80%;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
|
||||
color: #495057;
|
||||
`;
|
||||
introMsg.className = "ai-message";
|
||||
|
||||
// Show loading message first
|
||||
introMsg.innerHTML =
|
||||
|
|
@ -2280,25 +1862,12 @@ You have complete knowledge of this page content and can reference any details,
|
|||
|
||||
// Add TTS and delete buttons to intro message
|
||||
const messageActions = document.createElement("div");
|
||||
messageActions.style.cssText = `
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
opacity: 0.7;
|
||||
`;
|
||||
messageActions.className = "uncloseai-message-actions";
|
||||
|
||||
const ttsBtn = document.createElement("button");
|
||||
ttsBtn.textContent = "🔊";
|
||||
ttsBtn.title = "Read aloud";
|
||||
ttsBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
ttsBtn.className = "uncloseai-btn-small";
|
||||
|
||||
// Store audio for pause/resume functionality
|
||||
let introAudio = null;
|
||||
|
|
@ -2367,15 +1936,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.textContent = "🗑️";
|
||||
deleteBtn.title = "Delete message";
|
||||
deleteBtn.style.cssText = `
|
||||
background: none;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
deleteBtn.className = "uncloseai-btn-small";
|
||||
deleteBtn.onclick = async () => {
|
||||
// Remove intro message from localStorage
|
||||
const { loadConversationHistory, saveConversationHistory } = await import("./storage.js");
|
||||
|
|
@ -2397,16 +1958,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const copyRawBtn = document.createElement("button");
|
||||
copyRawBtn.textContent = "📋";
|
||||
copyRawBtn.title = "Copy raw (Markdown)";
|
||||
copyRawBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyRawBtn.className = "uncloseai-btn-small";
|
||||
copyRawBtn.onclick = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(response);
|
||||
|
|
@ -2424,16 +1976,7 @@ You have complete knowledge of this page content and can reference any details,
|
|||
const copyHtmlBtn = document.createElement("button");
|
||||
copyHtmlBtn.textContent = "📄";
|
||||
copyHtmlBtn.title = "Copy HTML";
|
||||
copyHtmlBtn.style.cssText = `
|
||||
background: ${colors.buttonBackground};
|
||||
border: 1px solid ${colors.borderColor};
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
color: ${colors.primaryText};
|
||||
transition: all 0.2s;
|
||||
`;
|
||||
copyHtmlBtn.className = "uncloseai-btn-small";
|
||||
copyHtmlBtn.onclick = async () => {
|
||||
try {
|
||||
const htmlContent = marked.parse(response);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ dialog#uncloseai-embedded-modal[data-theme="light"] {
|
|||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
background: var(--uncloseai-content-background, #f8f9fa);
|
||||
}
|
||||
|
||||
/* Settings panel */
|
||||
|
|
@ -307,6 +308,60 @@ dialog#translate-modal {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.translate-progress-text {
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.translate-eta-text {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.translate-status-text {
|
||||
color: #777;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.translate-notice {
|
||||
background: #e7f3ff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.translate-ai-notice {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
z-index: 10000;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.translate-raw-toggle-btn {
|
||||
padding: 8px 16px;
|
||||
background: #666;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.translate-result-area {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.translate-result-area.pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.translate-tab-nav li {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
|
|
@ -481,6 +536,10 @@ dialog#tts-modal {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.tts-result.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* TTS time display */
|
||||
.tts-time-display {
|
||||
font-size: 12px;
|
||||
|
|
@ -683,18 +742,18 @@ dialog#tts-modal {
|
|||
.uncloseai-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid var(--uncloseai-button-border, #dee2e6);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: #ffffff;
|
||||
color: #495057;
|
||||
background: var(--uncloseai-button-background, #ffffff);
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
.uncloseai-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
.uncloseai-section {
|
||||
|
|
@ -738,6 +797,329 @@ dialog#tts-modal {
|
|||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* EMBED MODAL SPECIFIC STYLES */
|
||||
/* =================================================================== */
|
||||
|
||||
/* Main modal container and layout */
|
||||
.uncloseai-modal-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.uncloseai-modal-header {
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.uncloseai-modal-title {
|
||||
margin: 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.uncloseai-modal-title-mobile {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.uncloseai-modal-title-desktop {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.uncloseai-modal-subtitle {
|
||||
font-size: 12px;
|
||||
opacity: 0.9;
|
||||
font-weight: normal;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.uncloseai-modal-subtitle-dim {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Chat area */
|
||||
.uncloseai-modal-chat-area {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
background: var(--uncloseai-content-background, #f8f9fa);
|
||||
}
|
||||
|
||||
.uncloseai-chat-box {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
/* Settings panel */
|
||||
.uncloseai-modal-settings {
|
||||
flex-shrink: 0;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
padding: 16px;
|
||||
display: none;
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.uncloseai-modal-settings.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Form inputs in modal */
|
||||
.uncloseai-modal-select {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--uncloseai-button-border, #dee2e6);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: var(--uncloseai-button-background, #ffffff);
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
/* Action grid and controls */
|
||||
.uncloseai-modal-controls {
|
||||
flex-shrink: 0;
|
||||
padding: 8px 16px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Input area */
|
||||
.uncloseai-modal-input-area {
|
||||
flex-shrink: 0;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-end;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* Chat input */
|
||||
.uncloseai-modal-input {
|
||||
flex: 1;
|
||||
min-height: 44px;
|
||||
max-height: 120px;
|
||||
padding: 12px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 24px;
|
||||
resize: none;
|
||||
font-size: 16px;
|
||||
font-family: inherit;
|
||||
line-height: 1.4;
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
background: var(--uncloseai-button-background, #ffffff);
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
.uncloseai-modal-input:focus {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
/* Send button */
|
||||
.uncloseai-modal-send-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: transform 0.2s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.uncloseai-modal-send-btn:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
.user-message {
|
||||
align-self: flex-end;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 4px 18px;
|
||||
max-width: 80%;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.ai-message {
|
||||
align-self: flex-start;
|
||||
background: white;
|
||||
color: #495057;
|
||||
padding: 12px 16px;
|
||||
border-radius: 18px 18px 18px 4px;
|
||||
max-width: 80%;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.message-text {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* Dark theme message colors */
|
||||
dialog#uncloseai-embedded-modal[data-theme="dark"] .ai-message {
|
||||
background: #2d3748;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
dialog#uncloseai-embedded-modal[data-theme="dark"] .user-message {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Read page container */
|
||||
.uncloseai-read-page-container {
|
||||
display: inline-flex;
|
||||
position: relative;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Translate dropdown */
|
||||
.translate-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
min-width: 200px;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
margin-top: 4px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
background: var(--uncloseai-button-background, white);
|
||||
border: 1px solid var(--uncloseai-button-border, #ddd);
|
||||
}
|
||||
|
||||
.translate-dropdown-current {
|
||||
padding: 8px 12px;
|
||||
color: var(--uncloseai-primary-text, #333);
|
||||
background: var(--uncloseai-button-background, #f5f5f5);
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.translate-dropdown-option {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: var(--uncloseai-primary-text, #333);
|
||||
}
|
||||
|
||||
.translate-dropdown-option:hover {
|
||||
background: var(--uncloseai-button-hover, #f5f5f5);
|
||||
}
|
||||
|
||||
.translate-dropdown-option-disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Download button */
|
||||
.uncloseai-download-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
margin-left: 8px;
|
||||
padding: 4px 8px;
|
||||
text-decoration: none;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.uncloseai-download-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* Page reader status div */
|
||||
.uncloseai-page-reader-status {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
border-radius: 5px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Button with left margin */
|
||||
.uncloseai-btn-margin-left {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* UI.JS COMPONENT STYLES */
|
||||
/* =================================================================== */
|
||||
|
||||
/* Container styles */
|
||||
.uncloseai-ui-container {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* Button margin utilities */
|
||||
.uncloseai-ui-button-margin {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Floating AI button */
|
||||
.uncloseai-floating-button {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 10px;
|
||||
width: 110px;
|
||||
height: 55px;
|
||||
border-radius: 22px;
|
||||
font-family: 'ChunkFiveRegular', monospace;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
max-width: calc(100vw - 20px);
|
||||
box-sizing: border-box;
|
||||
/* Dynamic colors set by JavaScript */
|
||||
background: var(--floating-button-bg, #000000);
|
||||
border: 2px solid var(--floating-button-border, #ffffff);
|
||||
color: var(--floating-button-text, #ffffff);
|
||||
}
|
||||
|
||||
.uncloseai-floating-button:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.uncloseai-floating-button.loading {
|
||||
opacity: 0.7;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* WIDGET LIBRARY STYLES */
|
||||
/* =================================================================== */
|
||||
|
|
@ -793,6 +1175,10 @@ dialog#tts-modal {
|
|||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.widget-feature-container.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.widget-tts-container {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
|
|
@ -811,8 +1197,17 @@ dialog#tts-modal {
|
|||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
background: var(--uncloseai-content-background, white);
|
||||
border: 1px solid var(--uncloseai-divider-color, #ddd);
|
||||
}
|
||||
|
||||
.widget-translate-dropdown-current {
|
||||
padding: 8px 12px;
|
||||
color: var(--uncloseai-muted-text, #666);
|
||||
border-bottom: 1px solid var(--uncloseai-divider-color, #ddd);
|
||||
background: var(--uncloseai-content-background, #f5f5f5);
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.widget-translate-dropdown .option {
|
||||
|
|
@ -821,8 +1216,13 @@ dialog#tts-modal {
|
|||
transition: background-color 0.2s;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
color: var(--uncloseai-primary-text, #333);
|
||||
}
|
||||
|
||||
.widget-translate-dropdown .option:hover {
|
||||
background: #f5f5f5;
|
||||
background: var(--uncloseai-button-hover, #f5f5f5);
|
||||
}
|
||||
|
||||
.widget-translate-dropdown .option-disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -339,6 +339,60 @@ dialog#translate-modal {
|
|||
display: block;
|
||||
}
|
||||
|
||||
.translate-progress-text {
|
||||
font-weight: bold;
|
||||
color: #4CAF50;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.translate-eta-text {
|
||||
color: #666;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.translate-status-text {
|
||||
color: #777;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.translate-notice {
|
||||
background: #e7f3ff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin: 10px 0;
|
||||
border-left: 4px solid #007bff;
|
||||
}
|
||||
|
||||
.translate-ai-notice {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
z-index: 10000;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.translate-raw-toggle-btn {
|
||||
padding: 8px 16px;
|
||||
background: #666;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.translate-result-area {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.translate-result-area.pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.translate-tab-nav li {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
|
|
@ -532,6 +586,10 @@ dialog#tts-modal {
|
|||
display: none;
|
||||
}
|
||||
|
||||
.tts-result.show {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* TTS time display */
|
||||
.tts-time-display {
|
||||
font-size: 12px;
|
||||
|
|
@ -734,18 +792,18 @@ dialog#tts-modal {
|
|||
.uncloseai-input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #dee2e6;
|
||||
border: 1px solid var(--uncloseai-button-border, #dee2e6);
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
background: #ffffff;
|
||||
color: #495057;
|
||||
background: var(--uncloseai-button-background, #ffffff);
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
.uncloseai-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
color: var(--uncloseai-primary-text, #495057);
|
||||
}
|
||||
|
||||
.uncloseai-section {
|
||||
|
|
@ -789,6 +847,328 @@ dialog#tts-modal {
|
|||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* EMBED MODAL SPECIFIC STYLES - PICOCSS OVERRIDES */
|
||||
/* =================================================================== */
|
||||
|
||||
/* Main modal container and layout */
|
||||
.uncloseai-modal-container {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-header {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 16px !important;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
color: white !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between !important;
|
||||
align-items: center !important;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-title {
|
||||
margin: 0 !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-title-mobile {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-title-desktop {
|
||||
font-size: 20px !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-subtitle {
|
||||
font-size: 12px !important;
|
||||
opacity: 0.9 !important;
|
||||
font-weight: normal !important;
|
||||
line-height: 1.3 !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-subtitle-dim {
|
||||
opacity: 0.8 !important;
|
||||
}
|
||||
|
||||
/* Chat area */
|
||||
.uncloseai-modal-chat-area {
|
||||
flex: 1 !important;
|
||||
overflow-y: auto !important;
|
||||
padding: 16px !important;
|
||||
-webkit-overflow-scrolling: touch !important;
|
||||
}
|
||||
|
||||
.uncloseai-chat-box {
|
||||
max-width: 100% !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
gap: 12px !important;
|
||||
}
|
||||
|
||||
/* Settings panel */
|
||||
.uncloseai-modal-settings {
|
||||
flex-shrink: 0 !important;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1) !important;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important;
|
||||
padding: 16px !important;
|
||||
display: none !important;
|
||||
max-height: 40vh !important;
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-settings.open {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Form inputs in modal */
|
||||
.uncloseai-modal-select {
|
||||
width: 100% !important;
|
||||
padding: 8px 12px !important;
|
||||
border: 1px solid var(--uncloseai-button-border, #dee2e6) !important;
|
||||
border-radius: 6px !important;
|
||||
font-size: 14px !important;
|
||||
background: var(--uncloseai-button-background, #ffffff) !important;
|
||||
color: var(--uncloseai-primary-text, #495057) !important;
|
||||
}
|
||||
|
||||
/* Action grid and controls */
|
||||
.uncloseai-modal-controls {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 8px 16px !important;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1) !important;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important;
|
||||
display: flex !important;
|
||||
gap: 8px !important;
|
||||
overflow-x: auto !important;
|
||||
-webkit-overflow-scrolling: touch !important;
|
||||
}
|
||||
|
||||
/* Input area */
|
||||
.uncloseai-modal-input-area {
|
||||
flex-shrink: 0 !important;
|
||||
padding: 16px !important;
|
||||
display: flex !important;
|
||||
gap: 12px !important;
|
||||
align-items: flex-end !important;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
/* Chat input */
|
||||
.uncloseai-modal-input {
|
||||
flex: 1 !important;
|
||||
min-height: 44px !important;
|
||||
max-height: 120px !important;
|
||||
padding: 12px !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2) !important;
|
||||
border-radius: 24px !important;
|
||||
resize: none !important;
|
||||
font-size: 16px !important;
|
||||
font-family: inherit !important;
|
||||
line-height: 1.4 !important;
|
||||
outline: none !important;
|
||||
transition: border-color 0.2s !important;
|
||||
background: var(--uncloseai-button-background, #ffffff) !important;
|
||||
color: var(--uncloseai-primary-text, #495057) !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-input:focus {
|
||||
border-color: #667eea !important;
|
||||
}
|
||||
|
||||
/* Send button */
|
||||
.uncloseai-modal-send-btn {
|
||||
width: 44px !important;
|
||||
height: 44px !important;
|
||||
padding: 0 !important;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
color: white !important;
|
||||
border: none !important;
|
||||
border-radius: 50% !important;
|
||||
cursor: pointer !important;
|
||||
font-size: 20px !important;
|
||||
font-weight: bold !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
transition: transform 0.2s !important;
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.uncloseai-modal-send-btn:hover {
|
||||
transform: scale(1.05) !important;
|
||||
}
|
||||
|
||||
/* Messages */
|
||||
.user-message {
|
||||
align-self: flex-end !important;
|
||||
background: #007bff !important;
|
||||
color: white !important;
|
||||
padding: 12px 16px !important;
|
||||
border-radius: 18px 18px 4px 18px !important;
|
||||
max-width: 80% !important;
|
||||
word-wrap: break-word !important;
|
||||
}
|
||||
|
||||
.ai-message {
|
||||
align-self: flex-start !important;
|
||||
background: white !important;
|
||||
color: #495057 !important;
|
||||
padding: 12px 16px !important;
|
||||
border-radius: 18px 18px 18px 4px !important;
|
||||
max-width: 80% !important;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
/* Dark theme message colors */
|
||||
dialog#uncloseai-embedded-modal[data-theme="dark"] .ai-message {
|
||||
background: #2d3748 !important;
|
||||
color: #e2e8f0 !important;
|
||||
}
|
||||
|
||||
dialog#uncloseai-embedded-modal[data-theme="dark"] .user-message {
|
||||
background: #667eea !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
/* Read page container */
|
||||
.uncloseai-read-page-container {
|
||||
display: inline-flex !important;
|
||||
position: relative !important;
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
|
||||
/* Translate dropdown */
|
||||
.translate-dropdown {
|
||||
position: absolute !important;
|
||||
top: 100% !important;
|
||||
left: 0 !important;
|
||||
min-width: 200px !important;
|
||||
max-height: 300px !important;
|
||||
overflow-y: auto !important;
|
||||
margin-top: 4px !important;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2) !important;
|
||||
z-index: 1000 !important;
|
||||
background: var(--uncloseai-button-background, white) !important;
|
||||
border: 1px solid var(--uncloseai-button-border, #ddd) !important;
|
||||
}
|
||||
|
||||
.translate-dropdown-current {
|
||||
padding: 8px 12px !important;
|
||||
color: var(--uncloseai-primary-text, #333) !important;
|
||||
background: var(--uncloseai-button-background, #f5f5f5) !important;
|
||||
font-weight: bold !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.translate-dropdown-option {
|
||||
padding: 8px 12px !important;
|
||||
cursor: pointer !important;
|
||||
transition: background-color 0.2s !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 1.4 !important;
|
||||
color: var(--uncloseai-primary-text, #333) !important;
|
||||
}
|
||||
|
||||
.translate-dropdown-option:hover {
|
||||
background: var(--uncloseai-button-hover, #f5f5f5) !important;
|
||||
}
|
||||
|
||||
.translate-dropdown-option-disabled {
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* Download button */
|
||||
.uncloseai-download-btn {
|
||||
background: rgba(255, 255, 255, 0.2) !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3) !important;
|
||||
border-radius: 4px !important;
|
||||
color: white !important;
|
||||
cursor: pointer !important;
|
||||
font-size: 12px !important;
|
||||
margin-left: 8px !important;
|
||||
padding: 4px 8px !important;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.2s !important;
|
||||
}
|
||||
|
||||
.uncloseai-download-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.3) !important;
|
||||
}
|
||||
|
||||
/* Page reader status div */
|
||||
.uncloseai-page-reader-status {
|
||||
position: fixed !important;
|
||||
top: 10px !important;
|
||||
right: 10px !important;
|
||||
padding: 10px !important;
|
||||
background: rgba(0, 0, 0, 0.8) !important;
|
||||
color: white !important;
|
||||
border-radius: 5px !important;
|
||||
z-index: 1000 !important;
|
||||
}
|
||||
|
||||
/* Button with left margin */
|
||||
.uncloseai-btn-margin-left {
|
||||
margin-left: 10px !important;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* UI.JS COMPONENT STYLES - PICOCSS OVERRIDES */
|
||||
/* =================================================================== */
|
||||
|
||||
/* Container styles */
|
||||
.uncloseai-ui-container {
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
/* Button margin utilities */
|
||||
.uncloseai-ui-button-margin {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* Floating AI button */
|
||||
.uncloseai-floating-button {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 10px;
|
||||
width: 110px;
|
||||
height: 55px;
|
||||
border-radius: 22px;
|
||||
font-family: 'ChunkFiveRegular', monospace;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease;
|
||||
max-width: calc(100vw - 20px);
|
||||
box-sizing: border-box;
|
||||
/* Dynamic colors set by JavaScript */
|
||||
background: var(--floating-button-bg, #000000);
|
||||
border: 2px solid var(--floating-button-border, #ffffff);
|
||||
color: var(--floating-button-text, #ffffff);
|
||||
}
|
||||
|
||||
.uncloseai-floating-button:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.uncloseai-floating-button.loading {
|
||||
opacity: 0.7;
|
||||
cursor: wait;
|
||||
}
|
||||
|
||||
/* =================================================================== */
|
||||
/* WIDGET LIBRARY STYLES - PICOCSS OVERRIDES */
|
||||
/* =================================================================== */
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export function createFullInterface(container) {
|
|||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.setAttribute("data-uncloseai-file-input", "");
|
||||
fileInput.style.display = "none";
|
||||
fileInput.style.display = "none"; // Hidden file input
|
||||
fileInput.onchange = handleFileUpload;
|
||||
|
||||
container.appendChild(chatContainer);
|
||||
|
|
@ -239,7 +239,7 @@ export async function handleUploadFromElement(button) {
|
|||
|
||||
button.disabled = true;
|
||||
button.textContent = getUIText("processingText");
|
||||
resultDiv.style.display = "block";
|
||||
resultDiv.classList.add("show");
|
||||
resultDiv.innerHTML = "<em>Uploading and analyzing file...</em>";
|
||||
|
||||
try {
|
||||
|
|
@ -287,15 +287,11 @@ export async function handleSmartTranslate(button) {
|
|||
// Create dropdown
|
||||
const dropdown = document.createElement("div");
|
||||
dropdown.className = "widget-translate-dropdown";
|
||||
dropdown.style.background = colors.panelBackground;
|
||||
dropdown.style.borderColor = colors.borderColor;
|
||||
dropdown.style.boxShadow = `0 4px 12px ${colors.shadowColor}`;
|
||||
// Styling handled by CSS class
|
||||
|
||||
// Add current language indicator
|
||||
const currentLangDiv = document.createElement("div");
|
||||
currentLangDiv.style.color = colors.mutedText;
|
||||
currentLangDiv.style.borderBottom = `1px solid ${colors.dividerColor}`;
|
||||
currentLangDiv.style.background = colors.contentBackground;
|
||||
currentLangDiv.className = "widget-translate-dropdown-current";
|
||||
const currentLangName =
|
||||
NATIVE_LANGUAGE_NAMES[currentLang] ||
|
||||
SUPPORTED_LANGUAGES[currentLang] ||
|
||||
|
|
@ -314,7 +310,7 @@ export async function handleSmartTranslate(button) {
|
|||
|
||||
const option = document.createElement("div");
|
||||
option.className = "option";
|
||||
option.style.color = colors.primaryText;
|
||||
// Color handled by CSS class
|
||||
|
||||
// Format: "es • Spanish • Español"
|
||||
option.innerHTML = `
|
||||
|
|
@ -325,12 +321,7 @@ export async function handleSmartTranslate(button) {
|
|||
<span style="font-weight: 500;">${nativeName}</span>
|
||||
`;
|
||||
|
||||
option.onmouseenter = () => {
|
||||
option.style.background = colors.buttonHover;
|
||||
};
|
||||
option.onmouseleave = () => {
|
||||
option.style.background = "transparent";
|
||||
};
|
||||
// Hover effects handled by CSS
|
||||
|
||||
option.onclick = async () => {
|
||||
try {
|
||||
|
|
@ -340,7 +331,7 @@ export async function handleSmartTranslate(button) {
|
|||
<span style="color: ${colors.mutedText};">•</span>
|
||||
<span style="color: ${colors.secondaryText};">${getUIText("translatingTo")}</span>
|
||||
`;
|
||||
option.style.pointerEvents = "none";
|
||||
option.className = "option option-disabled";
|
||||
|
||||
// Translate the page
|
||||
const translatedHtml = await translateCurrentPage(code);
|
||||
|
|
@ -363,15 +354,14 @@ export async function handleSmartTranslate(button) {
|
|||
<span style="color: ${colors.mutedText};">•</span>
|
||||
<span style="font-weight: 500;">${nativeName}</span>
|
||||
`;
|
||||
option.style.pointerEvents = "auto";
|
||||
option.className = "option";
|
||||
}
|
||||
};
|
||||
|
||||
dropdown.appendChild(option);
|
||||
});
|
||||
|
||||
// Position dropdown relative to button
|
||||
button.style.position = "relative";
|
||||
// Position handled by CSS
|
||||
button.parentElement.appendChild(dropdown);
|
||||
|
||||
// Close dropdown when clicking outside
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue