@@ -368,6 +387,9 @@
주관식/논술형 문제
아래 모범답안 및 AI 상세 해설을 참고하여 학습을 진행하세요.
+
+ 정답:
+
@@ -768,7 +790,7 @@ const gradingDescriptive = computed({
async function loadGradingHistory() {
const q = currentQuestion.value
- if (!q || q.correctAnswerStr !== '주관식') {
+ if (!q || !isDescriptiveQuestion.value) {
descriptiveHistory.value = []
return
}
@@ -780,6 +802,11 @@ async function loadGradingHistory() {
}
}
+function submitTextAnswerWithAi() {
+ userDescriptiveAnswer.value = userTextAnswer.value
+ submitDescriptiveGrading()
+}
+
async function submitDescriptiveGrading() {
const q = currentQuestion.value
if (!q || !userDescriptiveAnswer.value.trim() || gradingDescriptive.value) return
@@ -962,6 +989,23 @@ const isOXQuestion = computed(() => {
return ans === 'O' || ans === 'X'
})
+// Check if the question is descriptive
+const isDescriptiveQuestion = computed(() => {
+ const q = currentQuestion.value
+ if (!q) return false
+
+ // 객관식 보기가 있는 경우 제외
+ if (choices.value.length > 0) return false
+
+ // OX 문항인 경우 제외
+ if (isOXQuestion.value) return false
+
+ const ans = q.correctAnswerStr?.trim() || ""
+
+ // 정답이 '주관식'이거나 10자 이상 긴 문장이거나 공백을 포함하는 경우 주관식 서술형 취급
+ return ans === '주관식' || ans.length > 8 || ans.includes(' ') || ans.includes('및') || ans.includes('와')
+})
+
const isPdf = computed(() => {
const url = currentQuestion.value.imageUrl
if (!url) return false
@@ -987,11 +1031,20 @@ function decodeHTMLEntities(text: string): string {
function renderMarkdownWithMath(text: string): string {
if (!text) return ''
+ // 0. Extract and protect Mermaid blocks
+ const mermaidBlocks: string[] = []
+ let workingText = text
+ workingText = workingText.replace(/```mermaid\r?\\n([\s\S]*?)\r?\\n```/g, (match, code) => {
+ const placeholder = `MERMAIDBLOCKPLACEHOLDER${mermaidBlocks.length}`
+ mermaidBlocks.push(code.trim())
+ return placeholder
+ })
+
const mathBlocks: { placeholder: string; isBlock: boolean; rawExpr: string; renderedHtml: string }[] = []
let placeholderCount = 0
// Normalize slash/backslash ctdot representations to standard cdots
- let workingText = text
+ workingText = workingText
.replace(/\\ctdot/g, '\\cdots')
.replace(/\/ctdot/g, '\\cdots')
@@ -1050,6 +1103,13 @@ function renderMarkdownWithMath(text: string): string {
html = html.replace(block.placeholder, block.renderedHtml)
}
+ // 5. Restore protected Mermaid blocks
+ mermaidBlocks.forEach((code, idx) => {
+ const placeholder = `MERMAIDBLOCKPLACEHOLDER${idx}`
+ const replacement = `
${code}
`
+ html = html.replace(new RegExp(`
\\s*${placeholder}\\s*
|${placeholder}`, 'g'), replacement)
+ })
+
return html
}
@@ -1565,24 +1625,30 @@ function handleTouchEnd(e: TouchEvent) {
}
/* 마크다운 표가 화면을 벗어나지 않게 하고 반응형 가로스크롤 및 폰트 축소 적용 */
-.markdown-content table {
- width: 100% !important;
- max-width: 100% !important;
+.markdown-content table,
+.prose table {
+ min-width: 100% !important;
+ width: max-content !important;
+ max-width: none !important;
display: table !important;
- overflow-x: auto !important;
- -webkit-overflow-scrolling: touch !important;
border-collapse: collapse !important;
margin-top: 1rem !important;
margin-bottom: 1rem !important;
font-size: 0.85em !important;
}
.markdown-content th,
-.markdown-content td {
+.markdown-content td,
+.prose th,
+.prose td {
padding: 0.5rem 0.75rem !important;
border: 1px solid var(--fallback-bc, oklch(var(--bc)/0.15)) !important;
- white-space: nowrap; /* 셀 내용 잘림 방지하고 깔끔한 스크롤 보장 */
+ white-space: normal !important;
+ word-break: keep-all !important;
+ word-wrap: break-word !important;
+ min-width: 120px !important;
}
-.markdown-content th {
+.markdown-content th,
+.prose th {
background-color: var(--fallback-b3, oklch(var(--b3)/0.5)) !important;
font-weight: bold !important;
}
diff --git a/frontend/src/views/StudyView.vue b/frontend/src/views/StudyView.vue
index d079373..6c5b87c 100644
--- a/frontend/src/views/StudyView.vue
+++ b/frontend/src/views/StudyView.vue
@@ -255,7 +255,7 @@