11import  {  useEffect ,  useState  }  from  "react" ; 
22import  {  useDiagram ,  useEnums ,  useLayout  }  from  "../../hooks" ; 
33import  {  toDBML  }  from  "../../utils/exportAs/dbml" ; 
4+ import  {  fromDBML  }  from  "../../utils/importFrom/dbml" ; 
45import  {  Button ,  Tooltip  }  from  "@douyinfe/semi-ui" ; 
56import  {  IconTemplate  }  from  "@douyinfe/semi-icons" ; 
67import  {  useTranslation  }  from  "react-i18next" ; 
78import  CodeEditor  from  "../CodeEditor" ; 
89
910export  default  function  DBMLEditor ( )  { 
10-   const  {  tables : currentTables ,  relationships }  =  useDiagram ( ) ; 
1111  const  diagram  =  useDiagram ( ) ; 
12-   const  {  enums }  =  useEnums ( ) ; 
12+   const  { 
13+     tables : currentTables , 
14+     relationships, 
15+     setTables, 
16+     setRelationships, 
17+     database, 
18+     externalIssues, 
19+   }  =  diagram ; 
20+   const  {  enums,  setEnums }  =  useEnums ( ) ; 
1321  const  [ value ,  setValue ]  =  useState ( ( )  =>  toDBML ( {  ...diagram ,  enums } ) ) ; 
1422  const  {  setLayout }  =  useLayout ( ) ; 
23+   const  {  setExternalIssues }  =  diagram ; 
1524  const  {  t }  =  useTranslation ( ) ; 
1625
26+   // Translate DBML parse errors to issues and Monaco markers 
27+   const  [ markers ,  setMarkers ]  =  useState ( [ ] ) ; 
28+ 
1729  const  toggleDBMLEditor  =  ( )  =>  { 
1830    setLayout ( ( prev )  =>  ( {  ...prev ,  dbmlEditor : ! prev . dbmlEditor  } ) ) ; 
1931  } ; 
2032
2133  useEffect ( ( )  =>  { 
22-     setValue ( toDBML ( {  tables : currentTables ,  enums,  relationships } ) ) ; 
23-   } ,  [ currentTables ,  enums ,  relationships ] ) ; 
34+     const  normalized  =  toDBML ( { 
35+       tables : currentTables , 
36+       enums, 
37+       relationships, 
38+       database, 
39+     } ) ; 
40+     setValue ( normalized ) ; 
41+   } ,  [ currentTables ,  enums ,  relationships ,  database ] ) ; 
42+ 
43+   useEffect ( ( )  =>  { 
44+     const  currentDbml  =  toDBML ( { 
45+       tables : currentTables , 
46+       enums, 
47+       relationships, 
48+       database, 
49+     } ) ; 
50+ 
51+     if  ( value  ===  currentDbml )  { 
52+       // If editor content already matches diagram state, 
53+       // ensure any lingering external issues/markers are cleared 
54+       if  ( externalIssues ?. length )  setExternalIssues ( [ ] ) ; 
55+       if  ( markers . length )  setMarkers ( [ ] ) ; 
56+       return ; 
57+     } 
58+ 
59+     const  handle  =  setTimeout ( ( )  =>  { 
60+       try  { 
61+         const  parsed  =  fromDBML ( value ) ; 
62+         // Preserve coordinates when table names match existing ones 
63+         const  nameToExisting  =  new  Map ( 
64+           currentTables . map ( ( t )  =>  [ t . name ,  {  x : t . x ,  y : t . y  } ] ) , 
65+         ) ; 
66+         parsed . tables  =  parsed . tables . map ( ( t )  =>  { 
67+           const  coords  =  nameToExisting . get ( t . name ) ; 
68+           return  coords  ? {  ...t ,  ...coords  }  : t ; 
69+         } ) ; 
70+         setTables ( parsed . tables ) ; 
71+         setRelationships ( parsed . relationships ) ; 
72+         setEnums ( parsed . enums ) ; 
73+         // Clear any previous external issues on success 
74+         setExternalIssues ( [ ] ) ; 
75+         setMarkers ( [ ] ) ; 
76+       }  catch  ( err )  { 
77+         const  {  issues : parsedIssues ,  markers : parsedMarkers  }  = 
78+           produceDiagnostics ( err ) ; 
79+         setExternalIssues ( parsedIssues ) ; 
80+         setMarkers ( parsedMarkers ) ; 
81+       } 
82+     } ,  700 ) ; 
83+ 
84+     return  ( )  =>  clearTimeout ( handle ) ; 
85+   } ,  [ 
86+     value , 
87+     currentTables , 
88+     enums , 
89+     relationships , 
90+     database , 
91+     setTables , 
92+     setRelationships , 
93+     setEnums , 
94+     setExternalIssues , 
95+     externalIssues ?. length , 
96+     markers . length , 
97+   ] ) ; 
98+ 
99+   const  produceDiagnostics  =  ( err )  =>  { 
100+     // Prefer diagnostics from @dbml /core if present 
101+     if  ( Array . isArray ( err ?. diags )  &&  err . diags . length  >  0 )  { 
102+       const  issues  =  err . diags . map ( ( d )  =>  { 
103+         const  ln  =  d ?. location ?. start ?. line ; 
104+         const  col  =  d ?. location ?. start ?. column ; 
105+         const  code  =  d ?. code  ? ` [${ d . code }   : "" ; 
106+         if  ( ln  &&  col )  return  `line ${ ln } ${ col } ${ d . message } ${ code }  ; 
107+         return  d . message  +  code ; 
108+       } ) ; 
109+ 
110+       const  markers  =  err . diags . map ( ( d )  =>  { 
111+         const  start  =  d ?. location ?. start  ||  { } ; 
112+         const  end  =  d ?. location ?. end  ||  { } ; 
113+         const  startLineNumber  =  start . line  ||  1 ; 
114+         const  startColumn  =  start . column  ||  1 ; 
115+         const  endLineNumber  =  end . line  ||  startLineNumber ; 
116+         const  endColumn  =  end . column  ||  startColumn  +  1 ; 
117+         return  { 
118+           startLineNumber, 
119+           startColumn, 
120+           endLineNumber, 
121+           endColumn, 
122+           message : d . message , 
123+         } ; 
124+       } ) ; 
125+       return  {  issues,  markers } ; 
126+     } 
127+ 
128+     // Fallbacks 
129+     const  message  = 
130+       ( typeof  err ?. message  ===  "string"  &&  err . message )  || 
131+       ( typeof  err ?. description  ===  "string"  &&  err . description )  || 
132+       ( ( )  =>  { 
133+         try  { 
134+           return  JSON . stringify ( err ) ; 
135+         }  catch  { 
136+           return  String ( err ) ; 
137+         } 
138+       } ) ( ) ; 
139+ 
140+     // Try to extract line/column from string messages 
141+     const  m  = 
142+       / l i n e \s + ( \d + ) \s * , \s * c o l u m n \s * ( \d + ) / i. exec ( message )  || 
143+       / \( ( \d + ) \s * [: | , ] \s * ( \d + ) \) / . exec ( message ) ; 
144+     const  ln  =  m  ? parseInt ( m [ 1 ] ,  10 )  : 1 ; 
145+     const  col  =  m  ? parseInt ( m [ 2 ] ,  10 )  : 1 ; 
146+     return  { 
147+       issues : [ message ] , 
148+       markers : [ 
149+         { 
150+           startLineNumber : ln , 
151+           startColumn : col , 
152+           endLineNumber : ln , 
153+           endColumn : col  +  1 , 
154+           message, 
155+         } , 
156+       ] , 
157+     } ; 
158+   } ; 
24159
25160  return  ( 
26161    < CodeEditor 
@@ -29,8 +164,9 @@ export default function DBMLEditor() {
29164      language = "dbml" 
30165      onChange = { setValue } 
31166      height = "100%" 
167+       markers = { markers } 
32168      options = { { 
33-         readOnly : true , 
169+         readOnly : false , 
34170        minimap : {  enabled : false  } , 
35171      } } 
36172      extraControls = { 
0 commit comments