diff --git a/src/js/app/components/icons.js b/src/js/app/components/icons.js index 67e3d46..bed6501 100644 --- a/src/js/app/components/icons.js +++ b/src/js/app/components/icons.js @@ -1,11 +1,30 @@ -export const icon_sun = `` +const icon_sun = `` -export const icon_moon = `` +const icon_moon = `` -export const icon_moon_full = `` +const icon_moon_full = `` -export const icon_settings = `` +const icon_settings = `` -export const icon_paint = `` +const icon_paint = `` -export const icon_palette = `` +const icon_palette = `` + +const icon_full_width = `` + +const icon_sync = `` + +const icon_lock = `` +// const icon_lock = `` + +export { + icon_sun, + icon_moon, + icon_moon_full, + icon_settings, + icon_paint, + icon_palette, + icon_full_width, + icon_sync, + icon_lock, +} diff --git a/src/js/app/components/renderButtons.js b/src/js/app/components/renderButtons.js new file mode 100644 index 0000000..7d2b74e --- /dev/null +++ b/src/js/app/components/renderButtons.js @@ -0,0 +1,7 @@ +export function renderButton({ name, className, id, content, disabled = false }) { + return ` + + ` +} diff --git a/src/js/app/components/renderFonts.js b/src/js/app/components/renderFonts.js index 16eea1b..89209cb 100644 --- a/src/js/app/components/renderFonts.js +++ b/src/js/app/components/renderFonts.js @@ -91,11 +91,3 @@ export function renderFontBigCard({ // return cardHtml // } - -export function renderButton({ name, className, id, content, disabled = false }) { - return ` - - ` -} diff --git a/src/js/app/components/renderSwitch.js b/src/js/app/components/renderSwitch.js new file mode 100644 index 0000000..68b7842 --- /dev/null +++ b/src/js/app/components/renderSwitch.js @@ -0,0 +1,105 @@ +import { icon_lock } from './icons' + +function renderSwitchOption({ inputId, isChecked = false, icon, textTitle, textSubtitle }) { + const sanitizedInputId = sanitizeString(inputId) + const sanitizedTextTitle = sanitizeString(textTitle) + const sanitizedTextSubtitle = sanitizeString(textSubtitle) + + return ` + ` +} + +function renderSmallCardOption({ + name, + inputId, + inputType, + inputValue, + inputPlaceholder, + min = 10, + max = 100, + unit = '%', + isLocked = false, +}) { + const sanitizedName = sanitizeString(name) + const sanitizedInputId = sanitizeString(inputId) + const sanitizedInputValue = sanitizeString(inputValue) + const sanitizedInputPlaceholder = sanitizeString(inputPlaceholder) + + const lockIcon = isLocked ? icon_lock : '' + + return ` +
+ + ${lockIcon} +
` +} + +function sanitizeString(str) { + const div = document.createElement('div') + div.textContent = str + return div.innerHTML +} + +export { renderSwitchOption, renderSmallCardOption } diff --git a/src/js/app/floatingBtn.js b/src/js/app/floatingBtn.js index 3e73a6e..077cde6 100644 --- a/src/js/app/floatingBtn.js +++ b/src/js/app/floatingBtn.js @@ -4,8 +4,8 @@ import browser from 'webextension-polyfill' import { icon_sun, icon_moon, icon_moon_full, icon_settings, icon_paint } from './components/icons.js' import { hexToHSL } from '../utils/hexToHSL' -// import { fontHtmlCode, addFontsEventHandlers } from './customFonts' import { fontHtmlCode, handleFontsListeners } from './mainFonts' +import { assetsHtmlCode, handleAssetsListeners } from './mainAssets' // console.log(fontHtmlCode) // let isOptionsShown = false @@ -203,8 +203,7 @@ function renderSettings() { @@ -225,8 +224,8 @@ function renderSettings() { $settings.querySelector('#resetAllSettings').addEventListener('click', resetAllSettings) - // addFontsEventHandlers() handleFontsListeners() + handleAssetsListeners() } function openSettings() { diff --git a/src/js/app/mainAssets.js b/src/js/app/mainAssets.js new file mode 100644 index 0000000..0bca22a --- /dev/null +++ b/src/js/app/mainAssets.js @@ -0,0 +1,382 @@ +import browser from 'webextension-polyfill' +import { renderSwitchOption, renderSmallCardOption } from './components/renderSwitch' +import { icon_full_width, icon_sync } from './components/icons' +import { renderButton } from './components/renderButtons' + +// Configuration object +const CONFIG = { + RESIZING_BREAKPOINT: 768, + FW_DEFAULTS: { + w_chat_user: 'max-content', + max_w_chat_user: '70%', + w_chat_gpt: '49rem', + w_prompt_textarea: '48rem', + chat_user_edit_icon_right: '100%', + chat_user_edit_icon_top: '0', + chat_user_edit_icon_transform: 'unset', + }, + FW_OPTIONS: { + w_chat_gpt: '100%', + w_chat_user: '100%', + max_w_chat_user: '100%', + chat_user_edit_icon_right: 'calc(0% + 2rem)', + chat_user_edit_icon_top: '100%', + chat_user_edit_icon_transform: 'translateY(-1.25rem)', + }, +} + +// Utility functions +const ensureValidPercentage = (value) => { + const numValue = parseInt(value, 10) + return isNaN(numValue) ? '0' : Math.max(0, Math.min(100, numValue)).toString() +} + +const removePercentAndRem = (str) => str?.replace(/%|rem/g, '') + +const isChatWidthModified = (settings) => settings.w_chat_gpt !== CONFIG.FW_DEFAULTS.w_chat_gpt + +const debounce = (func, delay) => { + let timeoutId + return (...args) => { + clearTimeout(timeoutId) + timeoutId = setTimeout(() => func(...args), delay) + } +} + +// State management +let currentSettings = { ...CONFIG.FW_DEFAULTS } +let isSyncEnabled = false + +// DOM manipulation +const setElementProperty = (selector, property, value) => { + const element = document.querySelector(selector) + if (element) element[property] = value +} + +const applySettings = (settings) => { + const isNarrowScreen = window.innerWidth <= CONFIG.RESIZING_BREAKPOINT + + if (isChatWidthModified(settings) && isNarrowScreen) { + Object.entries(CONFIG.FW_OPTIONS).forEach(([key, value]) => { + document.documentElement.style.setProperty(`--${key}`, value) + }) + // Ensure textarea is full width on narrow screens + document.documentElement.style.setProperty('--w_prompt_textarea', '100%') + } else { + Object.entries(settings).forEach(([key, value]) => { + // For wider screens, apply the stored setting or default to full width for narrow screens + const appliedValue = isNarrowScreen && key === 'w_prompt_textarea' ? '100%' : value + document.documentElement.style.setProperty(`--${key}`, appliedValue) + }) + } +} + +const updateUI = (settings) => { + const isFullWidth = settings.w_chat_gpt === '100%' + const textareaWidthSlider = document.querySelector('#gpth-textarea-width-custom') + const textareaWidthCard = textareaWidthSlider?.closest('.card') + + const chatGptUnit = settings.w_chat_gpt.includes('rem') ? 'REM' : '%' + const promptUnit = settings.w_prompt_textarea.includes('rem') ? 'REM' : '%' + + setElementProperty('.gpth-settings #gpth-full-width', 'checked', isFullWidth) + setElementProperty('.gpth-settings #gpth-full-width-custom', 'value', removePercentAndRem(settings.w_chat_gpt)) + setElementProperty( + '.gpth-settings #range-output-gpth-full-width-custom', + 'textContent', + removePercentAndRem(settings.w_chat_gpt) + ) + setElementProperty('.gpth-settings #unit-gpth-full-width-custom', 'textContent', chatGptUnit) + + setElementProperty('.gpth-settings #gpth-sync-textarea-chat-width', 'checked', isSyncEnabled) + setElementProperty( + '.gpth-settings #gpth-textarea-width-custom', + 'value', + removePercentAndRem(settings.w_prompt_textarea) + ) + setElementProperty( + '.gpth-settings #range-output-gpth-textarea-width-custom', + 'textContent', + removePercentAndRem(settings.w_prompt_textarea) + ) + setElementProperty('.gpth-settings #unit-gpth-textarea-width-custom', 'textContent', promptUnit) + + if (textareaWidthSlider) textareaWidthSlider.disabled = isSyncEnabled + + if (textareaWidthCard) textareaWidthCard.classList.toggle('is-locked', isSyncEnabled) + + updateEditIconPosition(settings.w_chat_gpt) +} + +const updateEditIconPosition = (chatWidth) => { + const chatWidthValue = parseInt(removePercentAndRem(chatWidth)) + const iconSettings = chatWidthValue > 48 ? CONFIG.FW_OPTIONS : CONFIG.FW_DEFAULTS + + Object.assign(currentSettings, { + chat_user_edit_icon_right: iconSettings.chat_user_edit_icon_right, + chat_user_edit_icon_top: iconSettings.chat_user_edit_icon_top, + chat_user_edit_icon_transform: iconSettings.chat_user_edit_icon_transform, + }) + + applySettings(currentSettings) +} + +// Event handlers +const toggleChatFullWidth = (e) => { + const isFullWidth = e.target.checked + const { w_chat_gpt, w_chat_user, max_w_chat_user } = isFullWidth ? CONFIG.FW_OPTIONS : CONFIG.FW_DEFAULTS + + Object.assign(currentSettings, { + w_chat_gpt, + w_chat_user, + max_w_chat_user, + w_prompt_textarea: isSyncEnabled ? w_chat_gpt : currentSettings.w_prompt_textarea, + }) + + if (isChatWidthModified(currentSettings)) { + addResizeListener() + } else if (window.resizeListenerAdded) { + window.removeEventListener('resize', window.resizeListener) + window.resizeListenerAdded = false + } + + applySettings(currentSettings) + debouncedSaveSettings(currentSettings) + updateUI(currentSettings) +} + +const toggleSyncTextareaWithChatWidth = (e) => { + isSyncEnabled = e.target.checked + + if (isSyncEnabled) currentSettings.w_prompt_textarea = currentSettings.w_chat_gpt + + applySettings(currentSettings) + debouncedSaveSettings(currentSettings) + updateUI(currentSettings) +} + +const handleWidthChange = (key, e) => { + const value = `${ensureValidPercentage(e.target.value)}${e.target.dataset.unit || '%'}` + currentSettings[key] = value + + if (key === 'w_chat_gpt') { + currentSettings.w_chat_user = value + currentSettings.max_w_chat_user = value + + if (isSyncEnabled) currentSettings.w_prompt_textarea = value + + setElementProperty('.gpth-settings #gpth-full-width', 'checked', e.target.value === '100') + } else if (key === 'w_prompt_textarea' && isSyncEnabled && value !== currentSettings.w_chat_gpt) { + isSyncEnabled = false + setElementProperty('.gpth-settings #gpth-sync-textarea-chat-width', 'checked', false) + } + + setElementProperty(`.gpth-settings #unit-${e.target.id}`, 'textContent', e.target.dataset.unit || '%') + + if (isChatWidthModified(currentSettings)) addResizeListener() + + applySettings(currentSettings) + debouncedSaveSettings(currentSettings) + updateUI(currentSettings) +} + +// Update the resetWidths function +const resetWidths = async () => { + try { + // Get keys from FW_DEFAULTS + const resettableKeys = Object.keys(CONFIG.FW_DEFAULTS) + + // Remove resettable items from storage + await browser.storage.sync.remove(resettableKeys) + + // Reset current settings in memory + currentSettings = { ...CONFIG.FW_DEFAULTS } + isSyncEnabled = false + + // Remove resize listener if it exists + if (window.resizeListenerAdded) { + window.removeEventListener('resize', window.resizeListener) + window.resizeListenerAdded = false + } + + // Apply default settings + applySettings(currentSettings) + + // Update UI + updateUI(currentSettings) + + console.log('Settings reset successfully', currentSettings) + } catch (error) { + console.error('Failed to reset settings:', error) + // TODO: Implement user-friendly error message + } +} + +// Storage management +const saveSettings = async (settings) => { + try { + await browser.storage.sync.set(settings) + } catch (error) { + console.error('Failed to save settings:', error) + // TODO: Implement user-friendly error message + } +} + +const debouncedSaveSettings = debounce(saveSettings, 300) + +const loadSettings = async () => { + try { + const settings = await browser.storage.sync.get(null) + + // Only load settings for keys that are in FW_DEFAULTS + const filteredSettings = Object.fromEntries( + Object.entries(settings).filter(([key]) => key in CONFIG.FW_DEFAULTS) + ) + + currentSettings = { ...CONFIG.FW_DEFAULTS, ...filteredSettings } + isSyncEnabled = currentSettings.w_chat_gpt === currentSettings.w_prompt_textarea + + if (isChatWidthModified(currentSettings)) { + addResizeListener() + } + + applySettings(currentSettings) + updateUI(currentSettings) + } catch (error) { + console.error('Failed to load settings:', error) + // TODO: Implement user-friendly error message + } +} + +// Resize handling +const addResizeListener = () => { + if (!window.resizeListenerAdded) { + window.resizeListener = debounce(() => { + applySettings(currentSettings) + }, 250) + window.addEventListener('resize', window.resizeListener) + window.resizeListenerAdded = true + } +} +/* const confirmReset = () => { + return new Promise((resolve) => { + const confirmed = window.confirm('Are you sure you want to reset all width and layout settings to default?') + resolve(confirmed) + }) +} */ + +// Event listeners +const handleAssetsListeners = () => { + document.querySelector('.gpth-settings #gpth-full-width')?.addEventListener('change', toggleChatFullWidth) + + document + .querySelector('.gpth-settings #gpth-sync-textarea-chat-width') + ?.addEventListener('change', toggleSyncTextareaWithChatWidth) + document + .querySelector('.gpth-settings #gpth-full-width-custom') + ?.addEventListener('input', (e) => handleWidthChange('w_chat_gpt', e)) + + document + .querySelector('.gpth-settings #gpth-textarea-width-custom') + ?.addEventListener('input', (e) => handleWidthChange('w_prompt_textarea', e)) + + document.querySelector('.gpth-settings #resetWidths')?.addEventListener('click', resetWidths) + + // Then update the click handler for the reset button + /* document.querySelector('.gpth-settings #resetWidths')?.addEventListener('click', async () => { + if (await confirmReset()) { + resetWidths() + } + }) */ +} + +// HTML template +const assetsHtmlCode = ` +
+
+ ${renderSmallCardOption({ + name: 'Chats Width', + inputId: 'gpth-full-width-custom', + inputType: 'range', + inputValue: removePercentAndRem(CONFIG.FW_DEFAULTS.w_chat_gpt), + inputPlaceholder: '100%', + min: 0, + max: 100, + unit: 'REM', + })} + ${renderSmallCardOption({ + name: 'Prompt Width', + inputId: 'gpth-textarea-width-custom', + inputType: 'range', + inputValue: removePercentAndRem(CONFIG.FW_DEFAULTS.w_prompt_textarea), + inputPlaceholder: '100%', + min: 0, + max: 100, + unit: 'REM', + isLocked: true, + })} +
+ +
+ ${renderSwitchOption({ + inputId: 'gpth-full-width', + isChecked: false, + icon: icon_full_width, + textTitle: 'Chat Full Width', + textSubtitle: "Expand chats to screen's edge for wider conversation view", + })} + ${renderSwitchOption({ + inputId: 'gpth-sync-textarea-chat-width', + isChecked: false, + icon: icon_sync, + textTitle: 'Sync Prompt Width', + textSubtitle: 'Adjust prompt field to match the chat width for a more streamlined and consistent view', + })} +
+ + +
+` + +// Initialization +const init = () => { + loadSettings() +} + +export { assetsHtmlCode, handleAssetsListeners, init } + +/* // ? =============== DEV ONLY fn =============== +const assetsStorageKeys = Object.keys(CONFIG.FW_DEFAULTS) // ? DEV ONLY var - Get the keys from FW_DEFAULTS object + +async function getAllStorageItems(itemsToGet = null) { + //assetsStorageKeys + try { + const items = await browser.storage.sync.get(itemsToGet) + console.log(items) + return items + } catch (error) { + console.error('Error getting storage items:', error) + } +} +// ? =============== DEV ONLY fn =============== +// Function to remove specific named items from sync storage +async function removeSpecificStorageItems(keys) { + try { + // Remove the items by keys from the sync storage + await browser.storage.sync.remove(keys) + console.log('Specified items removed from sync storage:', keys) + } catch (error) { + console.error('Error removing storage items:', error) + } +} + +const init = () => { + // removeSpecificStorageItems(assetsStorageKeys) + getAllStorageItems(assetsStorageKeys) + loadSettings() + // getAllStorageItems() + getAllStorageItems(assetsStorageKeys) +} +export { assetsHtmlCode, handleAssetsListeners, init } */ diff --git a/src/js/app/mainFonts.js b/src/js/app/mainFonts.js index ab02749..01933cf 100644 --- a/src/js/app/mainFonts.js +++ b/src/js/app/mainFonts.js @@ -1,6 +1,7 @@ // main.js import browser from 'webextension-polyfill' import { renderFontSmallCard, renderFontBigCard, renderButton } from './components/renderFonts' +import { renderButton } from './components/renderButtons' // Constants const DEFAULTS = { diff --git a/src/js/content.js b/src/js/content.js index f88a6d0..226ddf2 100644 --- a/src/js/content.js +++ b/src/js/content.js @@ -1,2 +1,5 @@ import './app/floatingBtn' import './app/mainFonts' +import { init as initAssets } from './app/mainAssets' + +initAssets() diff --git a/src/sass/abstract/_vars.scss b/src/sass/abstract/_vars.scss index 237043e..4571c73 100644 --- a/src/sass/abstract/_vars.scss +++ b/src/sass/abstract/_vars.scss @@ -12,9 +12,15 @@ --lineHeight: 28; /* ? --- Widths and Heights --- */ + --min-w-btn: 5.5rem; --h-header: calc(3.5rem + 0.5rem + 0.375rem); - --max-w-chat-bubble: 48rem; --w-scrollbar: 0.22rem; + // --max-w-chat-bubble: 48rem; + --w_chat_user: max-content; + --max_w_chat_user: 70%; // 70% + --w_chat_gpt: 48rem; + // --w_prompt_textarea: var(--w_chat_gpt); + --w_prompt_textarea: initial; /* ? --- Border-radius --- */ --br: 1rem; @@ -32,6 +38,7 @@ /* ? --- Paddings --- */ --p-chat-bubble: 1.2rem; + --px-chat-bubble-edge-gap: 4vw; --p-contextmenu: 0.8rem; --p-contextmenu-item: 0.9rem 1rem; --p-prompt-textarea: 0.7rem; @@ -56,14 +63,26 @@ --blur-sticky: 20px; --blur-modal: 8px; - /* ? --- Width --- */ - --min-w-btn: 5.5rem; - /* ? --- Shadow things --- */ --box-shadow-values: 0px 0px 100px 0px; --z-modal: 100; + --c-bg-gradient: linear-gradient(135deg, + hsla(var(--accent-hsl) / 0.45) 0%, + hsla(var(--accent-hsl) / 0.1) 40%, + hsla(var(--accent-hsl) / 0.05) 60%, + hsla(var(--accent-hsl) / 0.45) 100%); + + /* ? --- OTHERER --- */ + --chat_user_edit_icon_right: 100%; + // --chat_user_edit_icon_top: translateY(100%); + --chat_user_edit_icon_top: 0; + --chat_user_edit_icon_transform: unset; + + // --chat_user_edit_icon_top: initial; + + /* .bg-token-sidebar-surface-primary - sidebar bg - nav item bg on hover */ @@ -109,30 +128,22 @@ - "ChatGPT 3.5 - Fastest" in 4o menu when hover on it in chat bubbles*/ --text-quaternary: var(--c-subtext-2) !important; --text-error: var(--c-danger) !important; + --box-shadow-primary: inset 0 -20px 80px -20px #ffffff1f; @include dev(tab) { - // --h-header: 40px; --p-chat-bubble: .8rem; --br-chat-bubble: calc(var(--br) * 1.55); - --p-contextmenu: .5rem; - // --p-contextmenu-item: 0.9rem 1rem; --p-prompt-textarea: 0.5rem; - // --p-tooltips: 0.2rem; - // --p-btn: 0.7rem; - // --py-btn: 1rem; - // --px-btn: 1rem; - - // --p-sidebar-nav: 0.5rem; - // --p-sidebar-nav-a-new-chat: 1.8rem; - // --p-sidebar-nav-a: 0.6rem; --p-dialog: 0.3rem; --br-dialog: calc(var(--br) * 1.5); - // --br: .7rem; --p-sidebar-nav: 0.3rem; } - --box-shadow-primary: inset 0 -20px 80px -20px #ffffff1f; + @include dev(mob) { + --px-chat-bubble-edge-gap: 0.45rem; + } + // --color-error: #fe8bbb; // --color-three: #9e7aff; diff --git a/src/sass/elements/_chats-textarea-max-width.scss b/src/sass/elements/_chats-textarea-max-width.scss index 8b664e9..bbd6cc5 100644 --- a/src/sass/elements/_chats-textarea-max-width.scss +++ b/src/sass/elements/_chats-textarea-max-width.scss @@ -1,19 +1,40 @@ -main:has(#prompt-textarea) [class*='lg:max-w-[40rem]'] { - // border: 1px solid red; +// main:has(#prompt-textarea) [class*='lg:max-w-[40rem]'] { +main:has(#prompt-textarea) { + .px-3.text-base.m-auto { + padding-left: var(--px-chat-bubble-edge-gap) !important; + padding-right: var(--px-chat-bubble-edge-gap) !important; - max-width: var(--max-w-chat-bubble) !important; - - @include dev(laptop) { - --max-w-chat-bubble: 40rem; - // border-color: lightblue; + /* Textarea and chats wrapper */ + // .mx-auto.flex.flex-1.text-base { + // max-width: var(--w_prompt_textarea) !important; + // } + /* Only textarea wrapper */ + .mx-auto.flex.flex-1.text-base:has(>form) { + max-width: var(--w_prompt_textarea) !important; + } } - // @include dev(tab-land) { - // --max-w-chat-bubble: 40rem; - // border-color: lightpink; + + // [class*='xl:max-w-[48rem]'] { + // // [class*='lg:max-w-[40rem]'] { + // border: 1px solid greenyellow; + + // max-width: var(--w_prompt_textarea) !important; + // // max-width: var(--w_chat_gpt) !important; + + // // @include dev(laptop) { + // // --w_prompt_textarea: 40rem; + // // // border-color: lightblue; + // // } + + // // // @include dev(tab-land) { + // // // --w_prompt_textarea: 40rem; + // // // border-color: lightpink; + // // // } + + // // @include dev(big-desktop) { + // // --w_prompt_textarea: 52rem; + // // // border-color: lightgreen; + // // } // } - @include dev(big-desktop) { - --max-w-chat-bubble: 52rem; - // border-color: lightgreen; - } -} +} \ No newline at end of file diff --git a/src/sass/elements/_right--main.scss b/src/sass/elements/_right--main.scss index f60c746..fc4da39 100644 --- a/src/sass/elements/_right--main.scss +++ b/src/sass/elements/_right--main.scss @@ -36,22 +36,34 @@ main [role='presentation'] { // margin-bottom: var(--mb-chat-txt) !important; // } + /* @ === RIGHT - CHATS BUBBLES ===*/ main [data-testid^='conversation-turn-'] { - /* Edit icon in user chat for example. Edit state user chat bg */ --main-surface-tertiary: var(--c-surface-3) !important; - // --main-surface-tertiary: hsla(var(--accent-hsl) / 0.5) !important; margin-bottom: var(--mb-chat-bubble) !important; + .mx-auto.flex.flex-1.text-base, + [class*='xl:max-w-[48rem]'] { + max-width: var(--w_chat_gpt) !important; + } + + // TODO proveriti ovo sve kad ima #prompt-textarea i kad nema (tipa u shared link layout-u) + /* .px-3.text-base.m-auto { + padding-left: var(--px-chat-bubble-edge-gap) !important; + padding-right: var(--px-chat-bubble-edge-gap) !important; + } */ + /* Horizontal gap from chat bubble to screen-edge */ & > div { + // padding-left: var(--px-chat-bubble-edge-gap); + // padding-right: var(--px-chat-bubble-edge-gap); /* Original is px-4 or 1rem */ - @include dev(mob) { - padding-left: 0.45rem; - padding-right: 0.45rem; - } + // @include dev(mob) { + // padding-left: 0.45rem; + // padding-right: 0.45rem; + // } } /* RIGHT - CHATS BUBBLES DIV */ @@ -129,6 +141,8 @@ main [data-testid^='conversation-turn-'] { &:has([data-message-author-role='user']) { line-height: calc(var(--lineHeight) / 16) !important; + + /* RIGHT - CHATS BUBBLE BG */ // & > div:first-child > div:first-child { /* ? gpt4o - User message */ @@ -136,6 +150,10 @@ main [data-testid^='conversation-turn-'] { background-color: var(--c-bg-msg-user); padding: var(--p-chat-bubble) calc(var(--p-chat-bubble) * 1.2); border-radius: var(--br-chat-bubble) !important; + // width: var(--w_chat_user) !important; + // width: max-content !important; + width: var(--max_w_chat_user) !important; + max-width: var(--w_chat_user) !important; } // } @@ -143,11 +161,19 @@ main [data-testid^='conversation-turn-'] { /* Flex-end the Reply in user chat bubble */ [data-message-author-role='user'] { align-items: flex-end !important; + /* TODO: Make this only when full-width! See what this can break since the origitnal is overflow-x-hidden, and when i make chats full-width, edit icon for user chat is cut-of so i need to fix it by this and editing the position of the edit icon. BUT HAVE TO TEST THIS!! */ + overflow: visible !important; /* Edit icon in User chat */ .absolute.bottom-0.top-0.right-full { + /* TODO: Make this only when full-width! - EDIT ICON */ + right: var(--chat_user_edit_icon_right) !important; // TODO: this + top: var(--chat_user_edit_icon_top) !important; // TODO: this + transform: var(--chat_user_edit_icon_transform) !important; // TODO: this + button { background-color: hsla(var(--accent-hsl) / 0.1); + backdrop-filter: blur(8px); } } @@ -393,4 +419,14 @@ html[data-gptheme='oled'] main [data-testid^='conversation-turn-']:has([data-mes background-color: var(--c-surface-2); } } +} + +main { + + [data-testid^='conversation-turn-'] .mx-auto.flex.flex-1.text-base, + [data-testid^='conversation-turn-']:has([data-message-author-role='user']) [class*='bg-[#f4f4f4]'], + &:has(#prompt-textarea) [class*='lg:max-w-[40rem]'], + &:has(#prompt-textarea) .px-3.text-base.m-auto .mx-auto.flex.flex-1.text-base:has(>form) { + transition: 0.3s ease-in-out; + } } \ No newline at end of file diff --git a/src/sass/elements/_right--textarea.scss b/src/sass/elements/_right--textarea.scss index 14c53e7..db7ce36 100644 --- a/src/sass/elements/_right--textarea.scss +++ b/src/sass/elements/_right--textarea.scss @@ -64,18 +64,6 @@ main form { color: var(--c-accent) !important; } - /* 📎 "Attach files" button parent oof parent which has items-end */ - // div:has(& > div[type='button'][aria-haspopup='dialog']:has(button[aria-label='Attach files'])) { - // div[type='button'][aria-haspopup='dialog']:has(button[aria-label='Attach files']) { - // margin-bottom: calc(var(--p-prompt-textarea) / 2); - // } - - /* 📎 Attach files svg */ - // button[aria-label='Attach files'] svg { - // color: var(--c-accent) !important; - // } - /* Moram ovako jer imamo dva div[type='button'][aria-haspopup='dialog'] */ - div:has(> input[type='file']) { /* 📎 "Attach files" svg */ @@ -92,22 +80,10 @@ main form { /* 🖼️ "Attach Files" - attached image wrapper */ div[type='button'][aria-haspopup='dialog'] { - // border: 2px solid lightgreen !important; /* "Attach Files" attached image */ span.bg-cover { border-radius: 10px !important; } - - // div[type='button'][aria-haspopup='dialog']:has(input[type='file']) { - // // border: 2px solid red !important; - - // /* 📎 "Attach files" svg */ - // // button:has(> svg) { - // // svg { - // // color: var(--c-accent) !important; - // // } - // // } - // } } } diff --git a/src/sass/gpthemes/_gpth-v2.scss b/src/sass/gpthemes/_gpth-floating-btn.scss similarity index 97% rename from src/sass/gpthemes/_gpth-v2.scss rename to src/sass/gpthemes/_gpth-floating-btn.scss index b23c9b9..9702f90 100644 --- a/src/sass/gpthemes/_gpth-v2.scss +++ b/src/sass/gpthemes/_gpth-floating-btn.scss @@ -42,11 +42,7 @@ width: var(--floating-icon-size); height: var(--floating-icon-size); color: var(--c-accent); - background-image: linear-gradient(135deg, - hsla(var(--accent-hsl) / 0.45) 0%, - hsla(var(--accent-hsl) / 0.1) 40%, - hsla(var(--accent-hsl) / 0.05) 60%, - hsla(var(--accent-hsl) / 0.45) 100%); + background-image: var(--c-bg-gradient); // border: 1px solid hsla(var(--accent-hsl) / 0.2); backdrop-filter: blur(10px); diff --git a/src/sass/gpthemes/theme-manager/_gpth-settings.scss b/src/sass/gpthemes/_gpth-settings.scss similarity index 70% rename from src/sass/gpthemes/theme-manager/_gpth-settings.scss rename to src/sass/gpthemes/_gpth-settings.scss index 25b0f05..25c2c44 100644 --- a/src/sass/gpthemes/theme-manager/_gpth-settings.scss +++ b/src/sass/gpthemes/_gpth-settings.scss @@ -1,26 +1,15 @@ -@import 'components/_colorpicker'; -@import 'layouts/_header'; -@import 'components/_tabs'; -@import 'components/_cards'; -@import 'components/_fonts'; -@import 'components/_error-msg'; - .gpth-settings { overflow: clip; --top: calc(var(--h-header) + 0.5rem + 3rem); --right: 1.3rem; --shadow-color: hsla(var(--accent-hsl) / 0.09); --shadow-values: inset 0 0 20px 10px; - // @extend [role='dialog']; opacity: 0; pointer-events: none; padding: 2.2rem 2.8rem; border-radius: var(--br-dialog); top: var(--top); right: var(--right); - // width: 26rem; - // height: 50vh; - // max-width: 30rem; width: clamp(25rem, 25vw, 30rem); max-height: 70vh; transform: translateX(calc(100% + 1.5rem)) !important; @@ -52,11 +41,4 @@ max-height: 70vh; padding: 2rem; } -} - -// html.light { -// .gpth-settings { -// // background-color: hsla(var(--accent-h) var(--surface-s) var(--surface-l) / 0.7); -// background-color: hsla(0 0% calc(var(--surface-l) / 1.18) / 0.7); -// } -// } \ No newline at end of file +} \ No newline at end of file diff --git a/src/sass/gpthemes/_notifications.scss b/src/sass/gpthemes/_notifications.scss deleted file mode 100644 index 92cc15d..0000000 --- a/src/sass/gpthemes/_notifications.scss +++ /dev/null @@ -1,66 +0,0 @@ -.gpth-notification { - font-size: 0.9rem; - position: fixed; - top: 3rem; - left: 50%; - transform: translateX(-50%) translateY(-10%); - visibility: hidden; - opacity: 0; - width: clamp(80vw, 40vw, 40rem); - background-color: hsla(var(--accent-hsl) / 0.2); - backdrop-filter: blur(1.5rem); - border: 1px solid hsla(var(--accent-hsl) / 0.2); - z-index: calc(var(--z-modal) - 4); - pointer-events: none; - display: grid; - gap: 1rem; - padding: var(--p-dialog); - border-radius: var(--br-dialog); - overflow-y: clip; - transition: opacity 0.3s linear, transform 0.3s linear; - - h2, - strong { - color: var(--c-accent); - } - - &--open { - transform: translateX(-50%); - visibility: visible; - opacity: 1; - pointer-events: all; - } - - ul { - list-style-type: disc; - padding: calc(var(--p-btn) * 0.5) calc(var(--p-btn) * 2); - - li { - padding: 0.2rem 0; - } - } - - h3 { - font-size: 1.2em; - } - - &__version { - background-color: var(--c-accent); - color: var(--c-on-accent); - border-radius: calc(var(--br-btn) * 0.5); - font-size: 0.7em; - } - - &__changes { - overflow-y: scroll; - height: 50vh; - } - - footer { - a { - font-weight: bold; - @extend %link_hover_underlined; - // color: var(--c-on-accent) !important; - } - } -} \ No newline at end of file diff --git a/src/sass/gpthemes/gpth-index.scss b/src/sass/gpthemes/gpth-index.scss new file mode 100644 index 0000000..5b8bc89 --- /dev/null +++ b/src/sass/gpthemes/gpth-index.scss @@ -0,0 +1,15 @@ +@import 'theme-manager/components/_colorpicker'; +@import 'theme-manager/components/_range'; +@import 'theme-manager/components/_tabs'; +@import 'theme-manager/components/_cards'; +@import 'theme-manager/components/_error-msg'; +@import 'theme-manager/components/_fonts'; +@import 'theme-manager/components/_switch'; + +@import 'theme-manager/sections/_header'; +@import 'theme-manager/sections/_main'; +@import 'theme-manager/sections/_header'; +@import 'theme-manager/layouts/_gpth-assets'; + +@import '_gpth-floating-btn'; +@import '_gpth-settings'; \ No newline at end of file diff --git a/src/sass/gpthemes/theme-manager/components/_cards.scss b/src/sass/gpthemes/theme-manager/components/_cards.scss index c317fe9..3195bb6 100644 --- a/src/sass/gpthemes/theme-manager/components/_cards.scss +++ b/src/sass/gpthemes/theme-manager/components/_cards.scss @@ -106,6 +106,7 @@ appearance: textfield; } + /* _________________ SMALL CARDS _________________ */ &--small { label { @@ -148,7 +149,7 @@ font-size: 0.42em; } - &:has(input:focus) { + &:has(input:focus):not(&[type="range"]) { .card__unitname-wrapper { display: none; } @@ -268,4 +269,122 @@ } } + + /* _________________ RANGE CARDS _________________ */ + &--range { + label { + // height: 5.5rem; + // padding: 0.3rem; + background-color: var(--c-surface-2); + } + + input { + width: 85% !important; + margin: 0 auto; + align-self: flex-end; + } + + .card__unit { + --px: 0.5rem; + background-color: hsla(var(--accent-hsl) / 0.12); + color: var(--c-accent); + font-size: 0.5em; + width: max-content; + line-height: 1; + padding: 0.2rem var(--px); + translate: -0.15rem 0; + } + + .card__name { + font-size: 0.42em; + } + + .card__output { + --output-size: 2.5rem; + // width: var(--output-size); + // height: var(--output-size); + aspect-ratio: 1 / 1; + background-color: hsla(var(--accent-hsl) / 0.2); + color: var(--c-accent); + font-size: 0.8em; + line-height: 1; + } + + &.is-locked { + position: relative; + cursor: not-allowed !important; + + label { + transform: .3s ease; + pointer-events: none !important; + opacity: 0.7 !important; + + &::after { + will-change: backdrop-filter; + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-image: radial-gradient(circle at 100% 0%, hsla(var(--accent-hsl) / 0.02) 5%, hsla(var(--accent-hsl) / 0.4) 100%); + z-index: 8; + border-radius: inherit; + backdrop-filter: blur(2px); + /* font-size: .5em; + font-weight: bold; + text-transform: uppercase; + text-align: center; + vertical-align: center; + display: grid; + place-items: center; */ + } + } + + &:hover { + label::after { + // content: '' !important; + backdrop-filter: blur(0px) !important; + background-image: radial-gradient(circle at 100% 0%, hsla(var(--accent-hsl) / 0.4) 5%, hsla(var(--accent-hsl) / 0.02) 42%) !important; + } + } + + + // .icon-lock-wrapper { + // display: block; + // position: absolute; + // top: 50%; + // left: 50%; + // transform: translate(-50%, -50%); + // background-color: hsla(var(--accent-hsl) / 0.5); + // width: 100%; + // height: 100%; + // display: grid; + // place-items: center; + // z-index: 10; + // border-radius: inherit; + + // .icon-lock { + // width: 1.5rem; + // height: 1.5rem; + // color: var(--c-accent); + // } + // } + + .icon-locked { + position: absolute; + top: 6%; + right: 7%; + width: 1.8rem; + color: var(--c-accent); + @extend %animScaleIn; + z-index: 10; + } + } + + &:not(.is-locked) .icon-locked { + display: none; + } + } + } \ No newline at end of file diff --git a/src/sass/gpthemes/theme-manager/components/_range.scss b/src/sass/gpthemes/theme-manager/components/_range.scss new file mode 100644 index 0000000..3e4364d --- /dev/null +++ b/src/sass/gpthemes/theme-manager/components/_range.scss @@ -0,0 +1,74 @@ +.gpth-settings { + input[type="range"] { + --track-bg: hsla(var(--accent-hsl) / 0.5); + --thumb-bg: var(--c-accent); + --thumb-focus-outline: var(--c-accent); + --track-height: 0.75rem; + --track-radius: 50vw; + --track-outline: 3px solid hsla(var(--accent-hsl) / 0.8); + --thumb-size-multiplier: 0.8; + --thumb-size: calc(var(--track-height) * var(--thumb-size-multiplier)); + --thumb-radius: 50vw; + --thumb-margin-top: calc((var(--track-height) - var(--thumb-size)) / 2); + --focus-outline-size: 3px; + --focus-outline-offset: 0.125rem; + + -webkit-appearance: none; + appearance: none; + background-color: transparent; + cursor: pointer; + // width: var(--input-width) + + &:hover, + &:focus { + // --track-height: 0.8rem; + --thumb-size-multiplier: 1.4; + --track-outline: 3px solid transparent; + } + + &:focus { + + &::-webkit-slider-thumb, + &::-moz-range-thumb { + outline: var(--focus-outline-size) solid var(--thumb-focus-outline); + outline-offset: var(--focus-outline-offset); + // outline: var(--focus-outline-size) solid var(--thumb-focus-outline); + // outline-offset: var(--focus-outline-offset); + } + } + + &::-webkit-slider-runnable-track { + background-color: var(--track-bg); + border-radius: var(--track-radius); + height: var(--track-height); + width: 100%; + transition: .25s; + outline: var(--track-outline); + // outline-offset: 2px; + } + + &::-webkit-slider-thumb, + &::-moz-range-thumb { + background-color: var(--thumb-bg); + border-radius: var(--thumb-radius); + height: var(--thumb-size); + width: var(--thumb-size); + transition: .25s; + + // &:focus { + // outline: var(--focus-outline-size) solid var(--thumb-focus-outline); + // outline-offset: var(--focus-outline-offset); + // } + } + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + margin-top: var(--thumb-margin-top); + } + + &::-moz-range-thumb { + border: none; + } + } +} \ No newline at end of file diff --git a/src/sass/gpthemes/theme-manager/components/_switch.scss b/src/sass/gpthemes/theme-manager/components/_switch.scss new file mode 100644 index 0000000..502f8dd --- /dev/null +++ b/src/sass/gpthemes/theme-manager/components/_switch.scss @@ -0,0 +1,112 @@ +.gpth-switch { + display: flex; + align-items: center; + flex-wrap: wrap; + justify-content: space-between; + margin-bottom: 0.5rem; + cursor: pointer; + font-size: 1rem; + gap: 0.8rem; + border-radius: calc(var(--br-btn) * 1.5); + padding: var(--p-btn); + border: 1px solid transparent; + transition: .25s ease-in; + + &:hover { + // background-image: var(--c-bg-gradient); + background-color: hsla(var(--accent-hsl) / 0.1); + border-color: hsla(var(--accent-hsl) / 0.1) !important; + transform: scale(0.98); + } + + &__icon { + --icon-size: 2.8em; + --svg-muliplicator: 0.6; + width: var(--icon-size); + height: var(--icon-size); + display: grid; + place-items: center; + border-radius: 1rem; + background-color: hsla(var(--accent-hsl) / 0.2); + color: var(--c-accent); + + svg { + width: calc(var(--icon-size) * var(--svg-muliplicator)); + height: calc(var(--icon-size) * var(--svg-muliplicator)); + } + } + + &__text { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.1rem; + + .title { + font-size: 0.8em; + font-weight: bold; + color: var(--c-accent); + text-transform: uppercase; + line-height: 1; + } + + .subtitle { + font-size: 0.7em; + line-height: 1.1; + font-weight: 500; + } + } + + &__checkbox { + --chb-w: 3rem; + --chb-h: 1.8rem; + --chb-edge-gap: 0.25rem; + --chb-slider-circle-size: calc(var(--chb-h) - 2 * var(--chb-edge-gap)); + position: relative; + display: inline-block; + width: var(--chb-w); + height: var(--chb-h); + + input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + display: flex; + align-items: center; + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: hsla(var(--accent-hsl) / 0.3); + border: 2px solid hsla(var(--accent-hsl) / 0.5); + transition: .4s; + border-radius: var(--chb-w); + + &::before { + position: absolute; + content: ""; + height: var(--chb-slider-circle-size); + aspect-ratio: 1/1; + left: var(--chb-edge-gap); + background-color: hsla(var(--accent-hsl) / 0.5); + transition: .4s $easeInOutCirc; + border-radius: 50%; + } + } + + input:checked + .slider { + background-color: var(--c-accent); + border-color: transparent !important; + + &::before { + transform: translateX(calc(var(--chb-w) - 145%)); + background-color: var(--c-on-accent); + } + } + } +} \ No newline at end of file diff --git a/src/sass/gpthemes/theme-manager/layouts/_gpth-assets.scss b/src/sass/gpthemes/theme-manager/layouts/_gpth-assets.scss new file mode 100644 index 0000000..b0a0770 --- /dev/null +++ b/src/sass/gpthemes/theme-manager/layouts/_gpth-assets.scss @@ -0,0 +1,7 @@ +.gpth-assets { + &__custom-width { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); + gap: 1rem 0.5rem; + } +} \ No newline at end of file diff --git a/src/sass/gpthemes/theme-manager/layouts/_footer.scss b/src/sass/gpthemes/theme-manager/sections/_footer.scss similarity index 100% rename from src/sass/gpthemes/theme-manager/layouts/_footer.scss rename to src/sass/gpthemes/theme-manager/sections/_footer.scss diff --git a/src/sass/gpthemes/theme-manager/layouts/_header.scss b/src/sass/gpthemes/theme-manager/sections/_header.scss similarity index 100% rename from src/sass/gpthemes/theme-manager/layouts/_header.scss rename to src/sass/gpthemes/theme-manager/sections/_header.scss diff --git a/src/sass/gpthemes/theme-manager/layouts/_main.scss b/src/sass/gpthemes/theme-manager/sections/_main.scss similarity index 100% rename from src/sass/gpthemes/theme-manager/layouts/_main.scss rename to src/sass/gpthemes/theme-manager/sections/_main.scss diff --git a/src/sass/index.scss b/src/sass/index.scss index 33423a0..4228848 100644 --- a/src/sass/index.scss +++ b/src/sass/index.scss @@ -46,6 +46,4 @@ // @import 'layouts/_old-ui'; @import 'layouts/_old-ui-index'; -@import 'gpthemes/_gpth-v2'; -@import 'gpthemes/_notifications'; -@import 'gpthemes/theme-manager/_gpth-settings'; \ No newline at end of file +@import 'gpthemes/gpth-index'; \ No newline at end of file