mirror of
https://github.com/ParisNeo/lollms-webui.git
synced 2024-12-18 20:17:50 +00:00
fixed
This commit is contained in:
parent
7a0290d6b6
commit
cc12ebe0c2
@ -36,9 +36,13 @@ Include the following in your HTML file:
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-javascript.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-python.min.js"></script>
|
||||
|
||||
<!-- needed for math rendering in markdown -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js"></script>
|
||||
<!-- MarkdownRenderer -->
|
||||
<script src="/lollms_assets/js/lollms_markdown_renderer"></script>
|
||||
<link rel="stylesheet" href="/lollms_assets/css/lollms_markdown_renderer">
|
||||
<link rel="stylesheet" href="/lollms_assets/css/lollms_styles">
|
||||
</head>
|
||||
<body>
|
||||
<div id="markdown-content"></div>
|
||||
|
@ -30,6 +30,9 @@
|
||||
// <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-java.min.js"></script>
|
||||
// <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-latex.min.js"></script>
|
||||
// When served with lollms, just use <script src="/lollms_assets/js/lollms_markdown_renderer"></script>
|
||||
// <!-- Render math -->
|
||||
// <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.css">
|
||||
// <script src="https://cdn.jsdelivr.net/npm/katex@0.13.11/dist/katex.min.js"></script>
|
||||
// Don't forget to get the css too <link rel="stylesheet" href="/lollms_assets/css/lollms_markdown_renderer">
|
||||
|
||||
// Make sure there is a global variable called mr that instanciate MarkdownRenderer
|
||||
@ -37,7 +40,7 @@
|
||||
|
||||
|
||||
class MarkdownRenderer {
|
||||
async renderMermaidDiagrams(text) {
|
||||
async renderMermaidDiagrams(text) {
|
||||
const mermaidCodeRegex = /```mermaid\n([\s\S]*?)```/g;
|
||||
const matches = text.match(mermaidCodeRegex);
|
||||
|
||||
@ -91,7 +94,7 @@ class MarkdownRenderer {
|
||||
return text;
|
||||
}
|
||||
|
||||
async renderSVG(text) {
|
||||
async renderSVG(text) {
|
||||
const svgCodeRegex = /```svg\n([\s\S]*?)```/g;
|
||||
const matches = text.match(svgCodeRegex);
|
||||
|
||||
@ -144,51 +147,61 @@ async renderSVG(text) {
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
async renderCodeBlocks(text) {
|
||||
if (typeof Prism === 'undefined') {
|
||||
throw new Error('Prism is not loaded. Please include Prism.js in your project.');
|
||||
}
|
||||
renderCodeBlocks(text) {
|
||||
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
|
||||
|
||||
return text.replace(codeBlockRegex, (match, language, code) => {
|
||||
language = language || 'plaintext';
|
||||
let highlightedCode;
|
||||
|
||||
try {
|
||||
highlightedCode = hljs.highlight(code.trim(), { language: language }).value;
|
||||
} catch (error) {
|
||||
console.warn(`Language '${language}' is not supported by highlight.js. Falling back to plaintext.`);
|
||||
highlightedCode = hljs.highlight(code.trim(), { language: 'plaintext' }).value;
|
||||
}
|
||||
|
||||
const lines = highlightedCode.split('\n');
|
||||
const numberedLines = lines.map((line, index) =>
|
||||
`<div class="code-line">
|
||||
<span class="line-number">${(index + 1).toString().padStart(2, '0')}</span>
|
||||
<span class="line-content">${line}</span>
|
||||
</div>`
|
||||
).join('');
|
||||
|
||||
return `
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span class="language">${language}</span>
|
||||
<button onclick="mr.copyCode(this)" class="copy-button">Copy</button>
|
||||
</div>
|
||||
<pre class="code-content"><code class="hljs language-${language}">${numberedLines}</code></pre>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
|
||||
copyCode(button) {
|
||||
const codeBlock = button.closest('.code-block');
|
||||
const codeLines = codeBlock.querySelectorAll('.line-content');
|
||||
const codeText = Array.from(codeLines).map(line => line.textContent).join('\n');
|
||||
|
||||
const renderedText = await text.replace(codeBlockRegex, (match, language, code) => {
|
||||
language = language || 'plaintext';
|
||||
navigator.clipboard.writeText(codeText).then(() => {
|
||||
button.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
button.textContent = 'Copy';
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy text: ', err);
|
||||
button.textContent = 'Failed';
|
||||
setTimeout(() => {
|
||||
button.textContent = 'Copy';
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!Prism.languages[language]) {
|
||||
console.warn(`Language '${language}' is not supported by Prism. Falling back to plaintext.`);
|
||||
language = 'plaintext';
|
||||
}
|
||||
|
||||
const highlightedCode = Prism.highlight(code.trim(), Prism.languages[language], language);
|
||||
|
||||
const lines = highlightedCode.split(/\r?\n/);
|
||||
const numberedLines = lines.map((line, index) =>
|
||||
`<span class="code-line"><span class="line-number">${index + 1}</span><span class="line-content">${line}</span></span>`
|
||||
).join('\n');
|
||||
|
||||
return `
|
||||
<div class="code-block-wrapper bg-gray-100 rounded-lg shadow-md overflow-hidden my-4">
|
||||
<div class="code-block-header bg-gray-200 px-4 py-2 flex justify-between items-center">
|
||||
<div class="language-label font-semibold text-gray-700">${language}</div>
|
||||
<button class="copy-button bg-blue-500 hover:bg-blue-600 text-white font-bold py-1 px-3 rounded" onclick="mr.copyCode(this)">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mr-1">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<div class="code-content max-h-[500px] overflow-auto">
|
||||
<pre class="line-numbers text-sm leading-tight"><code class="language-${language}">${numberedLines}</code></pre>
|
||||
</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
return renderedText;
|
||||
}
|
||||
|
||||
|
||||
handleInlineCode(text) {
|
||||
return text.replace(/`([^`]+)`/g, function(match, code) {
|
||||
return `<b>${code}</b>`;
|
||||
@ -196,11 +209,29 @@ async renderSVG(text) {
|
||||
}
|
||||
|
||||
handleMathEquations(text) {
|
||||
return text.replace(/\\\[([\s\S]*?)\\\]|\$\$([\s\S]*?)\$\$|\$([^\n]+?)\$/g, function(match, p1, p2, p3) {
|
||||
const equation = p1 || p2 || p3;
|
||||
return '<span class="math">' + equation + '</span>';
|
||||
});
|
||||
if (typeof katex === 'undefined') {
|
||||
console.error('KaTeX is not loaded. Make sure to include KaTeX scripts and CSS.');
|
||||
return text;
|
||||
}
|
||||
|
||||
return text.replace(/\\\[([\s\S]*?)\\\]|\$\$([\s\S]*?)\$\$|\$([^\n]+?)\$/g, function(match, p1, p2, p3) {
|
||||
const equation = p1 || p2 || p3;
|
||||
const isDisplayMode = match.startsWith('\\[') || match.startsWith('$$');
|
||||
|
||||
try {
|
||||
return katex.renderToString(equation, {
|
||||
displayMode: isDisplayMode,
|
||||
throwOnError: false,
|
||||
output: 'html'
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("KaTeX rendering error:", e);
|
||||
return `<span class="math-error">${match}</span>`; // Return error-marked original string if rendering fails
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
async handleTables(text) {
|
||||
let alignments = [];
|
||||
@ -298,6 +329,35 @@ async renderSVG(text) {
|
||||
return text;
|
||||
}
|
||||
|
||||
initMathJax() {
|
||||
// Configure MathJax
|
||||
window.MathJax = {
|
||||
tex: {
|
||||
inlineMath: [['$', '$']],
|
||||
displayMath: [['$$', '$$'], ['\\[', '\\]']]
|
||||
},
|
||||
svg: {
|
||||
fontCache: 'global'
|
||||
},
|
||||
startup: {
|
||||
ready: () => {
|
||||
MathJax.startup.defaultReady();
|
||||
MathJax.startup.promise.then(() => {
|
||||
console.log('MathJax is loaded and ready');
|
||||
// You can add any post-initialization logic here
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load MathJax
|
||||
if (!window.MathJax) {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';
|
||||
script.async = true;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
}
|
||||
async renderMarkdown(text) {
|
||||
// Handle Mermaid graphs first
|
||||
text = await this.renderMermaidDiagrams(text);
|
||||
@ -413,35 +473,7 @@ async renderSVG(text) {
|
||||
svg.style.transform = `scale(${newScale})`;
|
||||
}
|
||||
|
||||
copyCode(button) {
|
||||
const codeBlock = button.closest('.code-block-wrapper').querySelector('code');
|
||||
const codeLines = codeBlock.querySelectorAll('.code-line');
|
||||
let codeText = '';
|
||||
|
||||
codeLines.forEach((line) => {
|
||||
const lineNumber = line.querySelector('.line-number').textContent;
|
||||
const lineContent = line.querySelector('.line-content').textContent;
|
||||
codeText += `${lineNumber} ${lineContent}\n`;
|
||||
});
|
||||
|
||||
navigator.clipboard.writeText(codeText.trim()).then(() => {
|
||||
button.classList.add('copied');
|
||||
button.querySelector('svg').style.display = 'none';
|
||||
button.innerHTML = 'Copied!';
|
||||
setTimeout(() => {
|
||||
button.classList.remove('copied');
|
||||
button.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
Copy
|
||||
`;
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy text: ', err);
|
||||
});
|
||||
}
|
||||
async highlightCode(code, language) {
|
||||
// Make sure the language is supported by your highlighting library
|
||||
const supportedLanguage = Prism.languages[language] ? language : 'plaintext';
|
||||
|
@ -234,3 +234,68 @@ display: inline;
|
||||
.code-line:hover {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.math-display {
|
||||
display: block;
|
||||
margin: 1em 0;
|
||||
text-align: center;
|
||||
}
|
||||
.math-inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
.code-block {
|
||||
background-color: #f4f4f4;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin: 1em 0;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.code-header {
|
||||
background-color: #e0e0e0;
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
background-color: #4CAF50;
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.code-content {
|
||||
padding: 0.5em 0;
|
||||
overflow-x: auto;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.code-line {
|
||||
display: flex;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
.line-number {
|
||||
color: #999;
|
||||
text-align: right;
|
||||
padding-right: 1em;
|
||||
user-select: none;
|
||||
min-width: 2em;
|
||||
}
|
||||
|
||||
.line-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
339
endpoints/styles/lollms_styles.css
Normal file
339
endpoints/styles/lollms_styles.css
Normal file
@ -0,0 +1,339 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
html {
|
||||
@apply scroll-smooth;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: url('./fonts/Roboto/Roboto-Regular.ttf') format('truetype');
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'PTSans';
|
||||
src: url('./fonts/PTSans/PTSans-Regular.ttf') format('truetype');
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.no-scrollbar::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.display-none {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-5xl md:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-blue-700 dark:from-blue-400 dark:to-blue-500;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-3xl font-semibold text-gray-800 dark:text-gray-200;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-2xl font-semibold text-gray-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply text-xl font-semibold italic text-gray-600 dark:text-gray-400;
|
||||
}
|
||||
|
||||
p {
|
||||
@apply text-base text-gray-600 dark:text-gray-300 break-words;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply list-disc ml-0;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply list-disc ml-5;
|
||||
}
|
||||
|
||||
ol {
|
||||
@apply list-decimal ml-5;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-primary: #0e8ef0;
|
||||
--color-primary-light: #3dabff;
|
||||
--color-secondary: #0fd974;
|
||||
--color-accent: #f0700e;
|
||||
--color-light-text-panel: #ffffff;
|
||||
--color-dark-text-panel: #ffffff;
|
||||
--color-bg-light-panel: #7cb5ec;
|
||||
--color-bg-light: #e2edff;
|
||||
--color-bg-light-tone: #b9d2f7;
|
||||
--color-bg-light-code-block: #cad7ed;
|
||||
--color-bg-light-tone-panel: #8fb5ef;
|
||||
--color-bg-light-discussion: #c5d8f8;
|
||||
--color-bg-light-discussion-odd: #d6e7ff;
|
||||
--color-bg-dark: #132e59;
|
||||
--color-bg-dark-tone: #25477d;
|
||||
--color-bg-dark-tone-panel: #4367a3;
|
||||
--color-bg-dark-code-block: #2254a7;
|
||||
--color-bg-dark-discussion: #435E8A;
|
||||
--color-bg-dark-discussion-odd: #284471;
|
||||
}
|
||||
/* For both textarea and input elements */
|
||||
textarea, input, select {
|
||||
@apply bg-gray-100 dark:bg-gray-800;
|
||||
}
|
||||
|
||||
.background-color {
|
||||
@apply bg-gradient-to-br from-blue-100 to-blue-200 dark:from-blue-900 dark:to-blue-900 min-h-screen;
|
||||
}
|
||||
|
||||
.toolbar-color {
|
||||
@apply text-gray-700 dark:text-gray-50 bg-gradient-to-r from-blue-200 to-purple-200 dark:from-blue-800 dark:to-purple-800 rounded-full shadow-lg
|
||||
}
|
||||
.panels-color {
|
||||
@apply text-gray-700 dark:text-gray-50 bg-gradient-to-r from-blue-100 to-blue-200 dark:from-blue-800 dark:to-blue-900 rounded shadow-lg;
|
||||
}
|
||||
.unicolor-panels-color {
|
||||
@apply bg-blue-200 dark:bg-blue-800
|
||||
}
|
||||
.chatbox-color {
|
||||
@apply bg-gradient-to-br from-blue-200 to-blue-300 dark:from-blue-800 dark:to-blue-900
|
||||
}
|
||||
.message {
|
||||
@apply relative w-full rounded-lg m-2 shadow-lg border-2 border-transparent
|
||||
flex flex-col flex-grow flex-wrap overflow-visible p-4 pb-2;
|
||||
}
|
||||
.message:hover {
|
||||
@apply border-primary border-solid;
|
||||
}
|
||||
/* Light theme */
|
||||
.message:nth-child(even) {
|
||||
@apply bg-gradient-to-br from-blue-200 to-blue-300 dark:from-blue-800 dark:to-blue-800;
|
||||
}
|
||||
|
||||
.message:nth-child(odd) {
|
||||
@apply bg-gradient-to-br from-blue-300 to-blue-400 dark:from-blue-800 dark:to-blue-900;
|
||||
}
|
||||
|
||||
.discussion{
|
||||
@apply mr-2 bg-gradient-to-r from-blue-300 to-blue-400 dark:from-blue-800 dark:to-blue-900 hover:from-blue-100 hover:to-purple-100 hover:dark:from-blue-700 hover:dark:to-purple-700
|
||||
}
|
||||
.discussion-hilighted{
|
||||
@apply bg-gradient-to-r from-blue-200 to-purple-300 dark:from-blue-800 dark:to-purple-900 hover:from-blue-100 hover:to-purple-100 hover:dark:from-blue-700 hover:dark:to-purple-700
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
@apply bg-gradient-to-br from-blue-100 to-purple-100 dark:from-blue-900 dark:to-purple-900 min-h-screen;
|
||||
}
|
||||
|
||||
.bg-gradient-welcome {
|
||||
@apply bg-gradient-to-br from-blue-100 to-purple-100 dark:from-blue-900 dark:to-purple-900;
|
||||
}
|
||||
|
||||
.bg-gradient-progress {
|
||||
@apply bg-gradient-to-r from-blue-200 to-purple-200 dark:from-blue-800 dark:to-purple-800;
|
||||
}
|
||||
|
||||
.text-gradient-title {
|
||||
@apply text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-purple-600 dark:from-blue-400 dark:to-purple-400;
|
||||
}
|
||||
|
||||
.text-subtitle {
|
||||
@apply text-gray-600 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.text-author {
|
||||
@apply text-gray-500 dark:text-gray-400;
|
||||
}
|
||||
|
||||
.text-loading {
|
||||
@apply text-gray-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.text-progress {
|
||||
@apply text-blue-600 dark:text-blue-400;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-purple-500 hover:bg-purple-600 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
||||
|
||||
.card {
|
||||
@apply bg-white dark:bg-gray-800 rounded-lg shadow-md p-6;
|
||||
}
|
||||
|
||||
.input {
|
||||
@apply bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400;
|
||||
}
|
||||
|
||||
.label {
|
||||
@apply block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1;
|
||||
}
|
||||
|
||||
.link {
|
||||
@apply text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300;
|
||||
}
|
||||
.navbar-container {
|
||||
@apply text-gray-700 dark:text-gray-50 bg-gradient-to-r from-blue-200 to-blue-300 dark:from-blue-800 dark:to-blue-900 rounded shadow-lg
|
||||
}
|
||||
|
||||
.game-menu {
|
||||
@apply flex justify-center items-center relative;
|
||||
}
|
||||
|
||||
.text-shadow-custom {
|
||||
text-shadow: 1px 1px 0px white, -1px -1px 0px white, 1px -1px 0px white, -1px 1px 0px white;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
@apply mb-2 px-4 py-2 text-red-600 dark:text-red-300 font-bold text-lg transition-all duration-300 ease-in-out;
|
||||
@apply hover:text-gray-500 hover:dark:text-gray-50 hover:transform hover:-translate-y-1;
|
||||
}
|
||||
|
||||
.menu-item.active-link {
|
||||
@apply rounded-t-md border-red-500 text-shadow-custom text-red-600 font-bold text-lg transition-all duration-300 ease-in-out scale-105;
|
||||
@apply hover:text-red-600 hover:dark:text-gray-50 hover:transform hover:-translate-y-1;
|
||||
/* Glow effect on text */
|
||||
text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.menu-item.active-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 5px;
|
||||
/* Lightsaber colors */
|
||||
z-index: 10000;
|
||||
background: linear-gradient(to right, #00ff00, #00ff00, #00ff00); /* Normal mode */
|
||||
border-radius: 10px;
|
||||
animation: lightsaber 2s infinite;
|
||||
}
|
||||
|
||||
.dark .menu-item.active-link::after {
|
||||
background: linear-gradient(to right, #ff0000, #ff0000, #ff0000); /* Dark mode */
|
||||
}
|
||||
|
||||
@keyframes lightsaber {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.app-card {
|
||||
@apply transition-all duration-300 ease-in-out bg-gradient-to-br from-blue-200 to-blue-300 dark:from-blue-800 dark:to-blue-900 text-gray-800 dark:text-gray-100 shadow-md hover:shadow-lg;
|
||||
}
|
||||
|
||||
.app-card:hover {
|
||||
@apply transform -translate-y-1;
|
||||
}
|
||||
|
||||
button {
|
||||
@apply transition-all duration-300 ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@apply transform -translate-y-0.5;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: theme('colors.blue.300') theme('colors.blue.100');
|
||||
}
|
||||
|
||||
.dark .scrollbar-thin {
|
||||
scrollbar-color: theme('colors.blue.700') theme('colors.blue.900');
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
@apply w-2;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
@apply bg-blue-100 dark:bg-blue-900 rounded-full;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
@apply bg-blue-300 dark:bg-blue-700 rounded-full;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
@apply bg-blue-400 dark:bg-blue-600;
|
||||
}
|
||||
.btn {
|
||||
@apply font-semibold py-2 px-4 rounded-lg transition-all duration-300 ease-in-out shadow-md flex items-center;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-blue-500 text-white hover:bg-blue-600 focus:ring-4 focus:ring-blue-300 dark:focus:ring-blue-800;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
@apply bg-blue-200 text-gray-700 hover:bg-blue-300 focus:ring-4 focus:ring-blue-200 dark:bg-blue-700 dark:text-gray-200 dark:hover:bg-blue-600 dark:focus:ring-blue-600;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
@apply w-full border-b-2 border-blue-200 dark:border-blue-700 py-2 px-4 pl-10 transition-colors duration-300 ease-in-out focus:outline-none focus:border-blue-500 dark:focus:border-blue-400 bg-transparent text-gray-800 dark:text-gray-100;
|
||||
}
|
||||
|
||||
.scrollbar {
|
||||
@apply scrollbar-thin scrollbar-track-bg-light-tone scrollbar-thumb-bg-light-tone-panel hover:scrollbar-thumb-primary dark:scrollbar-track-bg-dark-tone dark:scrollbar-thumb-bg-dark-tone-panel dark:hover:scrollbar-thumb-primary active:scrollbar-thumb-secondary
|
||||
}
|
||||
|
||||
.card-title {
|
||||
@apply text-xl font-bold text-gray-900 dark:text-white mb-2;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
@apply text-gray-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
@apply mt-4 flex justify-between items-center;
|
||||
}
|
||||
|
||||
.card-footer-button {
|
||||
@apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
||||
|
||||
/* Subcard styles */
|
||||
.subcard {
|
||||
@apply bg-gray-100 dark:bg-gray-700 rounded-lg shadow-md p-4;
|
||||
}
|
||||
|
||||
.subcard-title {
|
||||
@apply text-lg font-bold text-gray-900 dark:text-white mb-2;
|
||||
}
|
||||
|
||||
.subcard-content {
|
||||
@apply text-gray-700 dark:text-gray-300;
|
||||
}
|
||||
|
||||
.subcard-footer {
|
||||
@apply mt-4 flex justify-between items-center;
|
||||
}
|
||||
|
||||
.subcard-footer-button {
|
||||
@apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
Loading…
Reference in New Issue
Block a user