schema.ts 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. /**
  2. * Minimal ProseMirror schema for markdown editor.
  3. *
  4. * Only contains structural nodes - no semantic marks (bold, italic, etc).
  5. * Markdown syntax is preserved as plain text and rendered via decorations.
  6. * hard_break preserves the \n vs \n\n distinction within block content.
  7. */
  8. import { type NodeSpec, Schema } from "prosemirror-model"
  9. type DOMNode = "doc" | "paragraph" | "hard_break" | "code_block" | "math_block" | "text"
  10. /** Node definitions */
  11. const nodes: Record<DOMNode, NodeSpec> = {
  12. /** Root document container — one or more paragraphs per block */
  13. doc: {
  14. content: "block+",
  15. },
  16. /** Paragraph — one per line within a block */
  17. paragraph: {
  18. group: "block",
  19. content: "inline*",
  20. parseDOM: [{ tag: "p" }],
  21. toDOM: () => ["p", 0],
  22. },
  23. /** Hard line break — preserves single \n within a paragraph */
  24. hard_break: {
  25. inline: true,
  26. group: "inline",
  27. selectable: false,
  28. parseDOM: [{ tag: "br" }],
  29. toDOM: () => ["br"],
  30. },
  31. /** Fenced code block — rendered via CodeMirror 6 node view */
  32. code_block: {
  33. group: "block",
  34. content: "text*",
  35. code: true,
  36. defining: true,
  37. attrs: { language: { default: "" } },
  38. parseDOM: [
  39. {
  40. tag: "pre",
  41. getAttrs(dom) {
  42. const code = dom.querySelector("code")
  43. const lang = code?.className?.match(/language-(\w+)/)?.[1] ?? ""
  44. return { language: lang }
  45. },
  46. },
  47. ],
  48. toDOM: (node) => [
  49. "pre",
  50. { class: node.attrs.language ? `language-${node.attrs.language}` : "" },
  51. ["code", 0],
  52. ],
  53. },
  54. /** Display math block — rendered via KaTeX node view */
  55. math_block: {
  56. group: "block",
  57. content: "text*",
  58. code: true,
  59. defining: true,
  60. toDOM: () => ["div", { "data-math-block": "" }, 0],
  61. },
  62. /** Text content */
  63. text: {
  64. group: "inline",
  65. },
  66. }
  67. /** No marks - all formatting handled via decorations */
  68. const marks = {}
  69. export const schema = new Schema<DOMNode, never>({ nodes, marks })