This commit is contained in:
root
2026-07-05 08:45:54 +00:00
parent c3596c12f3
commit 073d1dd383
8 changed files with 655 additions and 158 deletions
+4
View File
@@ -185,6 +185,7 @@ export class StudyElementDB {
"source": string;
"questionText": string;
"content": string;
"keyword": string;
"category": string;
/** Creates a new StudyElementDB instance. */
@@ -201,6 +202,9 @@ export class StudyElementDB {
if (!("content" in $$source)) {
this["content"] = "";
}
if (!("keyword" in $$source)) {
this["keyword"] = "";
}
if (!("category" in $$source)) {
this["category"] = "";
}
+4 -4
View File
@@ -63,15 +63,15 @@ export function DeleteUser(username: string): $CancellablePromise<void> {
* ExtractStudyElementsForCategory extracts study elements for all questions in the category.
* It skips questions that already have an extracted study element.
*/
export function ExtractStudyElementsForCategory(category: string): $CancellablePromise<string> {
return $Call.ByID(3340623770, category);
export function ExtractStudyElementsForCategory(category: string, useGeminiAPI: boolean, delaySeconds: number): $CancellablePromise<string> {
return $Call.ByID(3340623770, category, useGeminiAPI, delaySeconds);
}
/**
* GenerateGoPracticalExams creates new exam questions based on existing ones.
*/
export function GenerateGoPracticalExams(count: number): $CancellablePromise<string> {
return $Call.ByID(4059537052, count);
export function GenerateGoPracticalExams(count: number, useGeminiAPI: boolean, delaySeconds: number, examType: string): $CancellablePromise<string> {
return $Call.ByID(4059537052, count, useGeminiAPI, delaySeconds, examType);
}
/**
+37 -3
View File
@@ -1,5 +1,5 @@
<template>
<div class="drawer lg:drawer-open" data-theme="dark">
<div class="drawer lg:drawer-open" :data-theme="currentTheme">
<input id="main-drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content flex flex-col h-screen overflow-hidden bg-base-100 text-base-content">
@@ -121,6 +121,18 @@
<p class="text-[10px] mt-2 px-2 opacity-40 font-medium">문제 순서 무작위 배치</p>
</div>
</div>
<!-- Theme Settings Card -->
<div class="p-6 m-4 mt-0 bg-base-300 rounded-3xl border border-base-content/5 space-y-3">
<div class="flex items-center gap-2 px-2">
<PaletteIcon :size="16" class="text-secondary" />
<span class="text-xs font-black uppercase tracking-widest opacity-60">테마 설정</span>
</div>
<button @click="randomizeTheme" class="btn btn-outline btn-sm btn-secondary w-full text-xs font-bold gap-2">
<ShuffleIcon :size="14" />
랜덤 테마: {{ currentTheme }}
</button>
</div>
</aside>
</div>
@@ -244,13 +256,35 @@
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { ref, onMounted, computed, watch } from 'vue'
import { useRouter } from 'vue-router'
import { LayoutDashboardIcon, BookOpenIcon, KeyIcon, TrophyIcon, MenuIcon, ShuffleIcon, BookmarkIcon, UsersIcon, UserIcon, UserCogIcon, LockIcon, GraduationCapIcon } from 'lucide-vue-next'
import { LayoutDashboardIcon, BookOpenIcon, KeyIcon, TrophyIcon, MenuIcon, ShuffleIcon, BookmarkIcon, UsersIcon, UserIcon, UserCogIcon, LockIcon, GraduationCapIcon, PaletteIcon } from 'lucide-vue-next'
import { useQuizStore } from './store/quiz'
import { useAuthStore } from './store/auth'
import { GetGeminiKeys, ChangePassword, UpdateUserApiKey } from '../bindings/changeme/quizservice'
const themes = [
"light", "dark", "cupcake", "bumblebee", "emerald", "corporate",
"synthwave", "retro", "cyberpunk", "valentine", "halloween",
"garden", "forest", "aqua", "lofi", "pastel", "fantasy",
"wireframe", "black", "luxury", "dracula", "cmyk", "autumn",
"business", "acid", "lemonade", "night", "coffee", "winter",
"dim", "nord", "sunset", "caramellatte", "abyss", "silk"
]
const currentTheme = ref(localStorage.getItem('app-theme') || 'dark')
// Sync the theme to html element directly for DaisyUI scoping
watch(currentTheme, (newTheme) => {
document.documentElement.setAttribute('data-theme', newTheme)
}, { immediate: true })
function randomizeTheme() {
const randomIndex = Math.floor(Math.random() * themes.length)
const nextTheme = themes[randomIndex]
currentTheme.value = nextTheme
localStorage.setItem('app-theme', nextTheme)
}
const store = useQuizStore()
const authStore = useAuthStore()
const router = useRouter()
+167 -11
View File
@@ -40,9 +40,18 @@
</div>
</div>
</div>
<div class="flex flex-col items-end gap-1 shrink-0">
<span class="text-xs sm:text-sm font-medium opacity-70 text-base-content">{{ currentIndex + 1 }} / {{ questions.length }}</span>
<progress class="progress progress-primary w-20 sm:w-28 md:w-32" :value="currentIndex + 1" :max="questions.length"></progress>
<div class="flex items-center gap-4 shrink-0">
<!-- 중앙 정렬 옵션 스위치 -->
<label v-if="!loading && questions.length > 0 && !quizFinished" class="label cursor-pointer gap-2 bg-base-300/40 px-3 py-1.5 rounded-xl border border-base-content/5">
<span class="label-text text-[10px] font-bold opacity-70">중앙 정렬</span>
<input type="checkbox" v-model="centerAlign" class="checkbox checkbox-xs checkbox-primary" />
</label>
<div class="flex flex-col items-end gap-1">
<span class="text-xs sm:text-sm font-medium opacity-70 text-base-content">{{ currentIndex + 1 }} / {{ questions.length }}</span>
<progress class="progress progress-primary w-20 sm:w-28 md:w-32" :value="currentIndex + 1" :max="questions.length"></progress>
</div>
</div>
</div>
@@ -127,14 +136,17 @@
<!-- Text/Markdown Passage -->
<div
v-if="currentQuestion.passage"
:class="passageClass"
:class="[passageClass, centerAlign ? 'text-center' : 'text-left']"
class="markdown-content prose prose-sm prose-invert max-w-none"
v-html="renderedPassage"
>
</div>
</div>
<h3 class="text-base sm:text-lg md:text-xl lg:text-2xl font-bold leading-relaxed text-base-content markdown-content prose prose-sm prose-invert inline-markdown">
<h3
class="text-base sm:text-lg md:text-xl lg:text-2xl font-bold leading-relaxed text-base-content markdown-content prose prose-sm prose-invert inline-markdown"
:class="centerAlign ? 'text-center' : 'text-left'"
>
<span class="inline-flex items-center align-middle gap-1.5 mr-2">
<span class="text-primary">Q{{ currentIndex + 1 }}.</span>
<button
@@ -417,7 +429,11 @@
<div v-if="currentQuestion.explanation" class="card bg-base-100 shadow-sm">
<div class="card-body p-4 sm:p-5">
<h5 class="text-[10px] sm:text-xs font-black uppercase tracking-widest text-slate-500 mb-2">해설</h5>
<div class="text-xs sm:text-sm md:text-base leading-relaxed text-base-content markdown-content prose prose-sm prose-invert max-w-none" v-html="renderedExplanationHuman"></div>
<div
class="text-xs sm:text-sm md:text-base leading-relaxed text-base-content markdown-content prose prose-sm prose-invert max-w-none"
:class="centerAlign ? 'text-center' : 'text-left'"
v-html="renderedExplanationHuman"
></div>
</div>
</div>
@@ -453,8 +469,12 @@
<span class="loading loading-dots loading-md text-info"></span>
<p class="text-xs text-info font-medium italic">Gemini 생각중</p>
</div>
<div v-else-if="currentQuestion.geminiExplanation" class="text-xs sm:text-sm md:text-base leading-relaxed text-base-content markdown-content prose prose-sm prose-invert max-w-none" v-html="renderedExplanation">
</div>
<div
v-else-if="currentQuestion.geminiExplanation"
class="text-xs sm:text-sm md:text-base leading-relaxed text-base-content markdown-content prose prose-sm prose-invert max-w-none"
:class="centerAlign ? 'text-center' : 'text-left'"
v-html="renderedExplanation"
></div>
<p v-else-if="!geminiError" class="text-xs text-slate-500 italic">상세한 해설이 필요한가요~? Gemini를 불러보세요.</p>
</div>
</div>
@@ -523,7 +543,25 @@ import { marked } from 'marked'
import katex from 'katex'
import 'katex/dist/katex.min.css'
const renderer = new marked.Renderer()
renderer.code = function({ text, lang }) {
if (lang === 'mermaid') {
return `<div class="mermaid">${text}</div>`
}
return `<pre><code class="language-${lang}">${text}</code></pre>`
}
// Table wrapper to prevent collapsing and support horizontal scrolling
const originalTable = renderer.table.bind(renderer)
renderer.table = function(token) {
const rawTable = originalTable(token)
return `<div class="overflow-x-auto my-4 border border-base-content/15 rounded-xl bg-base-300/10 shadow-inner">${rawTable}</div>`
}
marked.use({
gfm: true,
breaks: true,
renderer,
tokenizer: {
del(src) {
const doubleMatch = src.match(/^~~(?=\S)([\s\S]*?\S)~~(?![~])/)
@@ -570,6 +608,11 @@ const quizFinished = ref(false)
const userTextAnswer = ref('')
const userOXAnswer = ref('')
const centerAlign = ref(localStorage.getItem('quiz_center_align') === 'true')
watch(centerAlign, (val) => {
localStorage.setItem('quiz_center_align', val ? 'true' : 'false')
})
const userDescriptiveAnswer = ref('')
const descriptiveGradingResult = ref<DescriptiveGradingRecord | null>(null)
const descriptiveHistory = ref<DescriptiveGradingRecord[]>([])
@@ -819,9 +862,37 @@ function applyHighlighting() {
})
}
// 문제 전환 시 구문 강조 다시 적용
function triggerMermaid() {
nextTick(() => {
// @ts-ignore
if (window.mermaid) {
try {
// @ts-ignore
window.mermaid.run({ querySelector: '.mermaid' })
} catch (e) {
console.error('Mermaid run error:', e)
}
}
})
}
// 문제 전환 시 구문 강조 및 mermaid 다시 적용
watch(currentIndex, () => {
applyHighlighting()
triggerMermaid()
})
// 피드백/해설이 노출되거나 AI 해설이 업데이트될 때도 mermaid 렌더링
watch(showFeedback, (newVal) => {
if (newVal) {
triggerMermaid()
}
})
watch(() => currentQuestion.value?.geminiExplanation, (newVal) => {
if (newVal) {
triggerMermaid()
}
})
const renderedPassage = computed(() => {
@@ -1021,6 +1092,24 @@ onUnmounted(() => {
})
onMounted(async () => {
// Load mermaid library if not present
if (!document.getElementById('mermaid-script')) {
const script = document.createElement('script')
script.id = 'mermaid-script'
script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'
script.onload = () => {
// @ts-ignore
if (window.mermaid) {
// @ts-ignore
window.mermaid.initialize({ startOnLoad: false, theme: 'dark' })
triggerMermaid()
}
}
document.head.appendChild(script)
} else {
triggerMermaid()
}
cancelExplanationDone = Events.On('ai:explanation:done', (event: any) => {
const data = event.data
const key = `${data.source}_${data.id}`
@@ -1352,15 +1441,50 @@ async function toggleBookmark() {
const touchStartX = ref(0)
const touchStartY = ref(0)
const touchThreshold = 75 // min horizontal drag threshold in pixels
const isSwipeIgnored = ref(false)
function shouldIgnoreSwipe(target: EventTarget | null): boolean {
if (!target) return false
let el = target as HTMLElement | null
while (el) {
// 1. 클래스 기반 예외 영역 감지
if (el.classList && (
el.classList.contains('katex') ||
el.classList.contains('katex-display') ||
el.classList.contains('mermaid') ||
el.classList.contains('vue-pdf-embed') ||
el.classList.contains('overflow-x-auto') ||
el.classList.contains('no-swipe')
)) {
return true
}
// 2. 태그 기반 예외 영역 감지
const tagName = el.tagName ? el.tagName.toLowerCase() : ''
if (tagName === 'table' || tagName === 'pre' || tagName === 'code' || tagName === 'img' || tagName === 'canvas' || tagName === 'svg') {
return true
}
el = el.parentElement
}
return false
}
function handleTouchStart(e: TouchEvent) {
if (e.touches.length > 0) {
const target = e.touches[0].target
if (shouldIgnoreSwipe(target)) {
isSwipeIgnored.value = true
return
}
isSwipeIgnored.value = false
touchStartX.value = e.touches[0].clientX
touchStartY.value = e.touches[0].clientY
}
}
function handleTouchEnd(e: TouchEvent) {
if (isSwipeIgnored.value) {
return
}
if (e.changedTouches.length > 0) {
const endX = e.changedTouches[0].clientX
const endY = e.changedTouches[0].clientY
@@ -1442,7 +1566,7 @@ function handleTouchEnd(e: TouchEvent) {
.markdown-content table {
width: 100% !important;
max-width: 100% !important;
display: block !important;
display: table !important;
overflow-x: auto !important;
-webkit-overflow-scrolling: touch !important;
border-collapse: collapse !important;
@@ -1476,11 +1600,26 @@ function handleTouchEnd(e: TouchEvent) {
white-space: pre !important;
word-wrap: normal !important;
word-break: normal !important;
padding: 1rem !important;
border-radius: var(--rounded-box, 0.5rem) !important;
background-color: var(--fallback-b3, oklch(var(--b3)/0.3)) !important;
}
.markdown-content code {
/* pre 태그 내부가 아닌 순수 인라인 code 태그는 줄바꿈을 허용함 */
.markdown-content :not(pre) > code {
white-space: pre-wrap !important;
word-break: break-all !important;
background-color: var(--fallback-b3, oklch(var(--b3)/0.3)) !important;
padding: 0.2rem 0.4rem !important;
border-radius: 0.25rem !important;
}
/* pre 태그 내부의 code 블록은 줄바꿈을 차단하여 터미널 한 줄 형식 그대로 스크롤되도록 보존 */
.markdown-content pre > code {
white-space: pre !important;
word-break: normal !important;
word-wrap: normal !important;
display: block !important;
}
/* 수식(KaTeX) 영역이 화면을 벗어나지 않게 하고 가로 스크롤 적용 */
@@ -1498,4 +1637,21 @@ function handleTouchEnd(e: TouchEvent) {
overflow-y: hidden !important;
vertical-align: middle;
}
/* Mermaid 차트가 모바일 화면에서 잘리지 않도록 강제 방어 및 가로스크롤 적용 */
.markdown-content .mermaid {
max-width: 100% !important;
overflow-x: auto !important;
-webkit-overflow-scrolling: touch !important;
margin: 1.5rem 0 !important;
padding: 0.5rem !important;
background-color: var(--fallback-b2, oklch(var(--b2)/0.3)) !important;
border-radius: var(--rounded-box, 0.5rem) !important;
}
.markdown-content .mermaid svg {
height: auto !important;
max-width: 100% !important;
min-width: 320px !important;
}
</style>
+125 -27
View File
@@ -49,6 +49,19 @@
</select>
</div>
<!-- Gemini API Toggle & Delay Option -->
<div class="space-y-3 pt-4 border-t border-base-content/5">
<label class="label cursor-pointer justify-between p-0">
<span class="text-xs font-bold opacity-80">Gemini API 직접 사용</span>
<input type="checkbox" class="toggle toggle-primary toggle-sm" v-model="useGeminiAPI" />
</label>
<div class="form-control w-full">
<label class="label font-bold text-[10px] opacity-70 p-0 mb-1">추출 간격 ()</label>
<input type="number" v-model.number="delaySeconds" min="1" max="120" class="input input-bordered input-sm w-full bg-base-100" />
<span class="text-[9px] opacity-40 mt-1 leading-normal">요청 제한(Rate Limit) DB 락을 예방하기 위해 딜레이가 적용됩니다.</span>
</div>
</div>
<!-- Progress and Operations -->
<div v-if="selectedCategory" class="space-y-4 pt-4 border-t border-base-content/5">
<div class="flex justify-between items-center text-xs gap-2 whitespace-nowrap">
@@ -114,11 +127,12 @@
class="card bg-base-200 border border-base-content/5 hover:border-primary/30 rounded-2xl p-5 cursor-pointer hover:shadow-md transition-all active:scale-[0.99] flex flex-row items-center justify-between gap-4"
>
<div class="space-y-1 min-w-0 flex-1">
<div class="flex items-center gap-2 whitespace-nowrap">
<div class="flex items-center gap-2 whitespace-nowrap flex-wrap">
<span class="badge badge-primary badge-sm font-bold shrink-0">문제 #{{ se.questionId }}</span>
<span class="text-[10px] opacity-40 uppercase tracking-widest font-black truncate max-w-[120px]">{{ se.source }}</span>
</div>
<h4 class="text-sm font-bold text-base-content truncate pr-4">{{ se.questionText }}</h4>
<h4 class="text-base font-black text-primary truncate pr-4">{{ se.keyword || '개념 학습' }}</h4>
<p class="text-xs opacity-60 truncate pr-4">{{ se.questionText }}</p>
</div>
<ChevronRightIcon :size="20" class="opacity-40 shrink-0" />
</div>
@@ -140,17 +154,40 @@
</div>
<div class="space-y-6">
<div class="form-control w-full">
<label class="label font-bold text-xs opacity-70">생성할 문제 개수</label>
<div class="flex items-center gap-4">
<input
v-model.number="examCount"
type="range"
min="1"
max="15"
class="range range-primary flex-1"
/>
<span class="badge badge-primary font-black text-sm px-3 py-3">{{ examCount }}</span>
<div class="flex items-center justify-between bg-base-300/30 p-4 rounded-2xl border border-base-content/5">
<span class="text-sm font-bold opacity-80">Gemini API 직접 사용</span>
<input type="checkbox" class="toggle toggle-primary" v-model="useGeminiAPI" />
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="space-y-2">
<label class="label font-bold text-xs opacity-70 p-0">생성할 문제 개수</label>
<div class="flex items-center gap-4">
<input
v-model.number="examCount"
type="range"
min="1"
max="15"
class="range range-primary flex-1"
/>
<span class="badge badge-primary font-black text-sm px-3 py-3">{{ examCount }}</span>
</div>
</div>
<div class="form-control w-full">
<label class="label font-bold text-xs opacity-70 p-0 mb-1">출제 유형</label>
<select v-model="examType" class="select select-bordered select-sm w-full bg-base-100 font-bold">
<option value="혼합형">혼합형 (단답/서술 섞음)</option>
<option value="단답형">단답형 (괄호 채우기)</option>
<option value="서술형">서술형 (시나리오 대응)</option>
</select>
<span class="text-[9px] opacity-40 mt-1 leading-normal">주관식 서술형 선택 퀴즈창에서 OCR 채점 기능이 열립니다.</span>
</div>
<div class="form-control w-full">
<label class="label font-bold text-xs opacity-70 p-0 mb-1">생성 간격 ()</label>
<input type="number" v-model.number="examDelaySeconds" min="1" max="120" class="input input-bordered input-sm w-full bg-base-100 font-bold" />
<span class="text-[9px] opacity-40 mt-1 leading-normal">제한 방지를 위해 문제 생성 사이에 지연이 적용됩니다.</span>
</div>
</div>
@@ -218,7 +255,7 @@
</div>
<!-- Extracted Concepts Markdown viewer -->
<div class="flex-1 overflow-y-auto prose max-w-none text-sm leading-relaxed pr-2" v-html="renderedContent"></div>
<div class="flex-1 overflow-y-auto prose max-w-none text-[13px] leading-relaxed pr-2 text-left" v-html="renderedContent"></div>
</div>
<form method="dialog" class="modal-backdrop">
<button @click="closeModal">close</button>
@@ -246,6 +283,7 @@ interface StudyElement {
source: string
questionText: string
content: string
keyword: string
category: string
}
@@ -285,6 +323,12 @@ const isGeneratingExams = ref(false)
const examStatus = ref('')
const examProgress = ref(0)
const showQuizRedirect = ref(false)
const examType = ref('혼합형')
// Config Settings
const useGeminiAPI = ref(false)
const delaySeconds = ref(10)
const examDelaySeconds = ref(2)
// Custom Markdown rendering with KaTeX and Mermaid setup
const renderer = new marked.Renderer()
@@ -294,7 +338,22 @@ renderer.code = function({ text, lang }) {
}
return `<pre><code class="language-${lang}">${text}</code></pre>`
}
marked.setOptions({ renderer })
// Table wrapper to prevent collapsing and support horizontal scrolling
const originalTable = renderer.table.bind(renderer)
renderer.table = function(token) {
const rawTable = originalTable(token)
return `<div class="overflow-x-auto my-4 border border-base-content/15 rounded-xl bg-base-300/10 shadow-inner">${rawTable}</div>`
}
marked.setOptions({ renderer, gfm: true, breaks: true })
// Backup registration for marked v4/v5/v9+ newer API
marked.use({
gfm: true,
breaks: true,
renderer,
})
function renderMarkdown(text: string): string {
if (!text) return ''
@@ -333,6 +392,7 @@ const filteredElements = computed(() => {
return studyElements.value.filter(se =>
se.questionId.toString().includes(q) ||
se.questionText.toLowerCase().includes(q) ||
se.keyword.toLowerCase().includes(q) ||
se.content.toLowerCase().includes(q)
)
})
@@ -433,7 +493,7 @@ async function startExtraction() {
extractionProgress.value = 0
extractionStatus.value = '추출 시작 중...'
try {
await ExtractStudyElementsForCategory(selectedCategory.value)
await ExtractStudyElementsForCategory(selectedCategory.value, useGeminiAPI.value, delaySeconds.value)
} catch (e) {
console.error(e)
isExtracting.value = false
@@ -448,7 +508,7 @@ async function startGeneratingExams() {
examStatus.value = '기존 기출문제 데이터 수집 중...'
showQuizRedirect.value = false
try {
await GenerateGoPracticalExams(examCount.value)
await GenerateGoPracticalExams(examCount.value, useGeminiAPI.value, examDelaySeconds.value, examType.value)
} catch (e) {
console.error(e)
isGeneratingExams.value = false
@@ -463,8 +523,15 @@ function openModal(se: StudyElement) {
// @ts-ignore
if (window.mermaid) {
setTimeout(() => {
// @ts-ignore
window.mermaid.run()
try {
// @ts-ignore
window.mermaid.run({
querySelector: '.mermaid',
suppressErrors: true
})
} catch (e) {
console.error('Mermaid render error:', e)
}
}, 100)
}
})
@@ -644,9 +711,8 @@ function goToQuiz() {
}
/* KaTeX and math layout overrides */
:deep(.math-display) {
width: 100%;
display: flex;
justify-content: center;
justify-content: flex-start;
margin: 1.5rem 0;
overflow-x: auto;
}
@@ -656,14 +722,17 @@ function goToQuiz() {
/* Markdown Table and Typography Styling */
:deep(.prose) {
color: var(--fallback-bc,oklch(var(--bc)/1));
font-size: 13px !important;
text-align: left !important;
line-height: 1.6;
}
:deep(.prose h1, .prose h2, .prose h3) {
font-weight: 800;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
}
:deep(.prose h2) { font-size: 1.25rem; color: oklch(var(--p)); }
:deep(.prose h3) { font-size: 1.1rem; }
:deep(.prose h2) { font-size: 1.2rem; color: oklch(var(--p)); }
:deep(.prose h3) { font-size: 1.05rem; }
:deep(.prose p) { margin-bottom: 1rem; }
:deep(.prose table) {
width: 100%;
@@ -701,10 +770,39 @@ function goToQuiz() {
}
:deep(.mermaid) {
background-color: #1a1f2c;
padding: 1.5rem;
padding: 1.25rem;
border-radius: 0.75rem;
margin: 1.5rem 0;
display: flex;
justify-content: center;
margin: 1.25rem 0;
overflow-x: auto;
max-width: 100%;
display: block;
text-align: left;
color: #e2e8f0;
}
:deep(.mermaid svg) {
display: inline-block;
min-width: 380px;
}
/* SVG interior elements style preservation */
:deep(.mermaid text) {
fill: #e2e8f0 !important;
font-size: 12px !important;
}
:deep(.mermaid .node rect),
:deep(.mermaid .node circle),
:deep(.mermaid .node polygon),
:deep(.mermaid .node path) {
fill: #2d3748 !important;
stroke: #4a5568 !important;
}
:deep(.mermaid .edgePath .path) {
stroke: #a0aec0 !important;
}
:deep(.mermaid .edgeLabel text) {
fill: #e2e8f0 !important;
}
:deep(.mermaid .marker) {
fill: #a0aec0 !important;
stroke: #a0aec0 !important;
}
</style>
+1 -1
View File
@@ -8,6 +8,6 @@ export default {
},
plugins: [require("daisyui")],
daisyui: {
themes: ["dark"],
themes: true,
},
}