v1.2.9: 라이브 에디터·홈 피드·메인 커버 개선
라이브 모드 코드/콜아웃/토글 편집, 슬래시 명령, 홈 Latest List·Compact·Cards 보기, 사이트 설정 메인 화면 커버(720px) 및 HomeHero 반영. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
185
components/content/ContentMarkdownCodeBlockEditor.vue
Normal file
185
components/content/ContentMarkdownCodeBlockEditor.vue
Normal file
@@ -0,0 +1,185 @@
|
||||
<script setup>
|
||||
import { buildCodeBlockLines } from '../../lib/markdown-code-block.js'
|
||||
import ContentMarkdownEditableInline from './ContentMarkdownEditableInline.vue'
|
||||
import ProseCodeBlock from './ProseCodeBlock.vue'
|
||||
|
||||
const props = defineProps({
|
||||
/** 코드 본문 */
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/** 언어(slug) */
|
||||
language: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/** 줄 번호 표시 */
|
||||
showLineNumbers: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/** 본문 첫 줄 source-line(0-based) */
|
||||
bodySourceLine: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['commit', 'insert-below'])
|
||||
|
||||
const languageDraft = ref(props.language)
|
||||
const lineNumbersEnabled = ref(props.showLineNumbers)
|
||||
const liveBody = ref(props.modelValue)
|
||||
|
||||
watch(() => props.language, (value) => {
|
||||
languageDraft.value = value
|
||||
})
|
||||
|
||||
watch(() => props.showLineNumbers, (value) => {
|
||||
lineNumbersEnabled.value = value
|
||||
})
|
||||
|
||||
watch(() => props.modelValue, (value) => {
|
||||
liveBody.value = value
|
||||
})
|
||||
|
||||
/** @type {import('vue').ComputedRef<string[]>} */
|
||||
const bodyLines = computed(() => {
|
||||
const text = String(liveBody.value ?? '')
|
||||
|
||||
if (!text.length) {
|
||||
return ['']
|
||||
}
|
||||
|
||||
return text.split('\n')
|
||||
})
|
||||
|
||||
/** @type {import('vue').ComputedRef<number[]>} */
|
||||
const gutterLines = computed(() => bodyLines.value.map((_, index) => index + 1))
|
||||
|
||||
/**
|
||||
* 마크다운에 코드 블록을 반영한다.
|
||||
* @param {string} body - 본문
|
||||
* @returns {void}
|
||||
*/
|
||||
const commitCodeBlock = (body) => {
|
||||
emit('commit', buildCodeBlockLines({
|
||||
language: languageDraft.value,
|
||||
showLineNumbers: lineNumbersEnabled.value,
|
||||
body
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* 본문 편집 반영
|
||||
* @param {string} body - 본문
|
||||
* @returns {void}
|
||||
*/
|
||||
const onBodyCommit = (body) => {
|
||||
liveBody.value = body
|
||||
commitCodeBlock(body)
|
||||
}
|
||||
|
||||
/**
|
||||
* 입력 중 줄 번호 갱신용 본문 동기화
|
||||
* @param {string} body - 본문
|
||||
* @returns {void}
|
||||
*/
|
||||
const onBodyInput = (body) => {
|
||||
liveBody.value = body
|
||||
}
|
||||
|
||||
/**
|
||||
* 코드 블록 아래로 이탈(다음 문단 생성)
|
||||
* @param {Object} payload - insert-below 페이로드
|
||||
* @returns {void}
|
||||
*/
|
||||
const onExitBelow = (payload) => {
|
||||
emit('insert-below', payload)
|
||||
}
|
||||
|
||||
/**
|
||||
* 언어 입력 반영
|
||||
* @returns {void}
|
||||
*/
|
||||
const onLanguageCommit = () => {
|
||||
commitCodeBlock(props.modelValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* 줄 번호 표시를 토글한다.
|
||||
* @returns {void}
|
||||
*/
|
||||
const toggleLineNumbers = () => {
|
||||
lineNumbersEnabled.value = !lineNumbersEnabled.value
|
||||
commitCodeBlock(props.modelValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ProseCodeBlock
|
||||
class="content-markdown-code-block-editor"
|
||||
:show-line-numbers="lineNumbersEnabled"
|
||||
:line-numbers="gutterLines"
|
||||
>
|
||||
<template #header-tools>
|
||||
<div
|
||||
class="content-markdown-code-block-editor__toolbar pointer-events-none flex items-center gap-1.5 opacity-0 transition-opacity group-hover:opacity-100 group-focus-within:opacity-100"
|
||||
>
|
||||
<button
|
||||
class="content-markdown-code-block-editor__line-numbers pointer-events-auto rounded border border-white/15 bg-white/10 px-2 py-0.5 text-xs font-medium text-white/70 transition-colors hover:bg-white/15 hover:text-white"
|
||||
type="button"
|
||||
:aria-pressed="lineNumbersEnabled"
|
||||
:title="lineNumbersEnabled ? '줄 번호 숨기기' : '줄 번호 표시'"
|
||||
@mousedown.prevent
|
||||
@click="toggleLineNumbers"
|
||||
>
|
||||
{{ lineNumbersEnabled ? '줄번호' : '줄번호 끔' }}
|
||||
</button>
|
||||
<input
|
||||
v-model="languageDraft"
|
||||
class="content-markdown-code-block-editor__language pointer-events-auto w-[7.5rem] rounded border border-white/15 bg-white/10 px-2 py-0.5 text-xs text-white outline-none transition-colors placeholder:text-white/35 focus:border-white/30 focus:bg-white/15"
|
||||
type="text"
|
||||
placeholder="Language..."
|
||||
spellcheck="false"
|
||||
@mousedown.stop
|
||||
@keydown.stop
|
||||
@blur="onLanguageCommit"
|
||||
@keydown.enter.prevent="onLanguageCommit"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ContentMarkdownEditableInline
|
||||
tag="pre"
|
||||
block-class="content-markdown-code-block-editor__editor m-0 min-w-0 border-0 bg-transparent p-0 font-mono text-sm leading-6 text-white outline-none"
|
||||
enter-mode="multiline"
|
||||
plain-text
|
||||
:source-line="bodySourceLine"
|
||||
:model-value="modelValue"
|
||||
@input="onBodyInput"
|
||||
@commit="onBodyCommit"
|
||||
@insert-below="onExitBelow"
|
||||
/>
|
||||
</ProseCodeBlock>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.content-markdown-code-block-editor :deep(.prose-code-block__content) {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.content-markdown-code-block-editor :deep(.prose-code-block__header) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content-markdown-code-block-editor__toolbar {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.content-markdown-code-block-editor__toolbar > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user