cooperation/markdown.js
2025-01-15 19:26:44 +08:00

149 lines
4.1 KiB
JavaScript

import { Marked } from "marked"
import { cursors, Cursor } from "./cursor"
// 示例 Markdown 文本
const markdown = `
# 2333
- [ ] 1111
- [ ] 1111
- [ ] 1111
- [x] 2222
- [ ] 233323
- [ ] 233323
- [ ] 233323
这是一段测试文本
`
const marked = new Marked()
const tokens = marked.lexer(markdown)
// 创建 textarea 接受输入
const textarea = document.createElement("textarea")
Object.assign(textarea.style, {
position: "fixed",
bottom: "10px",
left: "10px",
width: "300px",
height: "100px",
zIndex: "1000",
placeholder: "在这里输入文本或使用方向键调整位置",
})
document.body.appendChild(textarea)
// 处理输入事件
textarea.oninput = () => {
const inputText = textarea.value // 获取用户输入的文本
if (!inputText) return
cursors.forEach(cursor => {
if (!cursor.targetNode || cursor.insertIndex === null) return
// 更新文本节点内容
cursor.targetNode.textContent =
cursor.targetNode.textContent.slice(0, cursor.insertIndex) +
inputText +
cursor.targetNode.textContent.slice(cursor.insertIndex)
// 更新插入位置
cursor.insertIndex += inputText.length
})
// 清空输入框
textarea.value = ""
updateCursors()
}
// 处理方向键移动插入点
textarea.onkeydown = (event) => {
cursors.filter(cursor => cursor.targetNode && cursor.insertIndex !== null).forEach(cursor => {
cursor.move(event.key)
updateCursors()
})
}
// 更新所有光标的位置
const updateCursors = () => {
cursors.forEach(cursor => {
if (!cursor.targetNode || cursor.insertIndex === null) {
cursor.hide()
return
}
// 获取插入点位置
const rect = cursor.getBoundingClientRect()
// 更新光标位置
cursor.updatePosition(rect)
})
}
// 渲染 Markdown 并监听点击事件
const element = document.createElement("div")
element.innerHTML = marked.parser(tokens)
document.body.appendChild(element)
// 点击事件:记录插入位置
element.onclick = (event) => {
if (event.target.tagName !== "LI") return
const { clientX: x, clientY: y } = event
// 查找点击的文本节点
const textNodes = Array.from(event.target.childNodes).filter(
(node) => node.nodeType === Node.TEXT_NODE
)
const targetNode = textNodes.find((node) => {
const range = document.createRange()
range.selectNodeContents(node)
const rect = range.getBoundingClientRect()
return x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
})
if (!targetNode) return
// 计算插入位置索引
const positions = [...targetNode.textContent].map((_, i) => {
const range = document.createRange()
range.setStart(targetNode, i)
range.setEnd(targetNode, i + 1)
return { index: i, rect: range.getBoundingClientRect() }
})
const closest = positions.reduce((closest, pos) => {
const dist = Math.abs(x - pos.rect.left)
return dist < closest.distance ? { ...pos, distance: dist } : closest
}, { index: -1, distance: Infinity })
const rect = closest.rect
const insertBefore = x < rect.left + rect.width / 2
const insertIndex = closest.index + (insertBefore ? 0 : 1)
// 设置光标的目标节点和插入位置
cursors.forEach(cursor => cursor.setTarget(targetNode, insertIndex))
// 如果是点击多个光标的情况,增加新光标
if (event.ctrlKey || !cursors.length) {
const newCursor = new Cursor(targetNode, insertIndex)
cursors.push(newCursor)
console.log("新增光标")
}
// 更新光标位置
updateCursors()
// 聚焦输入框
textarea.value = ""
textarea.focus()
}
// 按删除键移除光标
document.addEventListener('keydown', (event) => {
if (event.key === 'Delete' && cursors.length > 0) {
const cursorToRemove = cursors.pop() // 移除最后一个光标
cursorToRemove.remove() // 移除对应的光标元素
console.log("移除光标")
}
})