'use client' import { Node } from '@tiptap/core' import { ReactNodeViewRenderer, NodeViewWrapper, NodeViewContent } from '@tiptap/react' import { NoteChartFromCode } from './note-chart' import { useState } from 'react' import { Code, BarChart3, AlertCircle } from 'lucide-react' import { cn } from '@/lib/utils' /** * ChartExtension - TipTap Node extension for rendering chart blocks * * Detects
blocks and renders them
* as visual charts using the NoteChartFromCode component.
*/
export const ChartExtension = Node.create({
name: 'chartBlock',
group: 'block',
code: true,
defining: true,
addOptions() {
return {
HTMLAttributes: {},
}
},
addAttributes() {
return {
code: {
default: '',
parseHTML: element => {
if (element instanceof HTMLElement) {
const codeEl = element.querySelector('code')
return codeEl?.textContent || ''
}
return ''
},
renderHTML: () => ({})
},
language: {
default: 'chart',
parseHTML: element => {
if (element instanceof HTMLElement) {
const codeEl = element.querySelector('code')
// Check for class="language-chart" or data-language="chart"
if (codeEl?.classList.contains('language-chart')) return 'chart'
return element.getAttribute('data-language') || 'chart'
}
return 'chart'
},
renderHTML: attributes => ({
'data-language': attributes.language,
})
}
}
},
parseHTML() {
return [
{
tag: 'pre',
getAttrs: node => {
if (typeof node === 'string') return false
const element = node as HTMLElement
const codeEl = element.querySelector('code')
// Detect chart blocks by class="language-chart"
if (codeEl && codeEl.classList.contains('language-chart')) {
// Store the code content as an attribute
const codeContent = codeEl.textContent || ''
return {
code: codeContent,
language: 'chart'
}
}
return false
}
}
]
},
renderHTML({ HTMLAttributes, node }) {
// Get the code content from node attrs
const code = node.attrs.code || ''
return ['pre', { ...HTMLAttributes, class: 'language-chart' }, ['code', { class: 'language-chart' }, code]]
},
addNodeView() {
return ReactNodeViewRenderer(ChartBlockView, {
contentEditable: false,
})
}
})
/**
* ChartBlockView - React component for rendering chart blocks in TipTap
*
* Features:
* - Visual chart rendering with NoteChartFromCode
* - Toggle between visual and code view
* - Edit mode for modifying chart data
* - Error handling for invalid chart data
*/
function ChartBlockView(props: any) {
const [isEditing, setIsEditing] = useState(false)
const [parseError, setParseError] = useState(false)
const code = props.node?.attrs?.code || ''
// Check if chart code is valid when not editing
const isValidChart = !isEditing && code.trim().length > 0
if (isEditing) {
return (
Chart Code
)
}
return (
{/* Chart visual rendering */}
{isValidChart ? (
) : (
Invalid Chart
This chart block contains invalid or empty data.
)}
{/* Edit button - visible on hover */}
{/* Chart type indicator */}
{isValidChart && (
Chart
)}
)
}