@@ -9,6 +9,12 @@ import {
99} from '@dolphin/lark'
1010import { v4 as uuidv4 } from 'uuid'
1111import { Second , waitForFunction } from '@dolphin/common'
12+ import {
13+ SettingKey ,
14+ Table as TableSetting ,
15+ Grid as GridSetting ,
16+ type Settings ,
17+ } from './settings'
1218
1319interface Ref < T > {
1420 current : T
@@ -101,37 +107,150 @@ export class UniqueFileName {
101107 }
102108}
103109
104- export const transformInvalidTablesToHtml = (
105- invalidTables : TableWithParent [ ] ,
110+ export const mapTableBySettings = (
111+ tables : TableWithParent [ ] ,
112+ settings : Pick < Settings , SettingKey . Table > ,
113+ ) : TableWithParent [ ] => {
114+ if ( settings [ SettingKey . Table ] === TableSetting . Filtered ) {
115+ return [ ]
116+ }
117+
118+ return tables
119+ . map ( table => {
120+ if ( ! table . inner . data ?. invalid ) return table
121+
122+ const tableIndex = table . parent ?. children . findIndex (
123+ child => child === table . inner ,
124+ )
125+
126+ if ( tableIndex !== undefined && tableIndex !== - 1 ) {
127+ const inner = {
128+ ...table . inner ,
129+ children : table . inner . children . map ( row => ( {
130+ ...row ,
131+ children : row . children . map ( cell => ( {
132+ ...cell ,
133+ children : cell . data ?. invalidChildren ?? cell . children ,
134+ } ) ) ,
135+ } ) ) ,
136+ } as mdast . Table
137+
138+ table . parent ?. children . splice ( tableIndex , 1 , inner )
139+
140+ return {
141+ ...table ,
142+ inner,
143+ }
144+ }
145+
146+ return table
147+ } )
148+ . filter ( table =>
149+ settings [ SettingKey . Table ] === TableSetting . NonPhrasingContentToHTML
150+ ? table . inner . data ?. invalid
151+ : true ,
152+ )
153+ }
154+
155+ /**
156+ * Filters out redundant cells that are covered by rowSpan/colSpan
157+ * and adds appropriate HTML properties to the spanning cells.
158+ *
159+ * @param table The Markdown AST table to process
160+ */
161+ const processTableSpans = ( table : mdast . Table ) : void => {
162+ const occupied : boolean [ ] [ ] = [ ]
163+
164+ for ( let rowIndex = 0 ; rowIndex < table . children . length ; rowIndex ++ ) {
165+ const row = table . children [ rowIndex ]
166+ const newCells : mdast . TableCell [ ] = [ ]
167+
168+ for (
169+ let columnIndex = 0 ;
170+ columnIndex < row . children . length ;
171+ columnIndex ++
172+ ) {
173+ // If this position is covered by a previous spanning cell, skip it
174+ if ( occupied [ rowIndex ] ?. [ columnIndex ] ) continue
175+
176+ const cell = row . children [ columnIndex ]
177+ newCells . push ( cell )
178+
179+ const rowSpan = cell . data ?. rowSpan ?? 1
180+ const colSpan = cell . data ?. colSpan ?? 1
181+
182+ if ( rowSpan > 1 || colSpan > 1 ) {
183+ // Ensure data and hProperties objects exist
184+ cell . data ??= { }
185+ cell . data . hProperties ??= { }
186+
187+ // Add HTML span properties for correct rendering
188+ if ( rowSpan > 1 ) cell . data . hProperties [ 'rowSpan' ] = rowSpan
189+ if ( colSpan > 1 ) cell . data . hProperties [ 'colSpan' ] = colSpan
190+
191+ // Mark the area covered by this spanning cell as occupied
192+ for ( let i = 0 ; i < rowSpan ; i ++ ) {
193+ for ( let j = 0 ; j < colSpan ; j ++ ) {
194+ // Skip the current cell itself
195+ if ( i === 0 && j === 0 ) continue
196+ const targetRow = rowIndex + i
197+ const targetCol = columnIndex + j
198+ occupied [ targetRow ] ??= [ ]
199+ occupied [ targetRow ] [ targetCol ] = true
200+ }
201+ }
202+ }
203+ }
204+
205+ // Update row children with only the non-redundant cells
206+ row . children = newCells
207+ }
208+ }
209+
210+ export const transformTableToHtml = (
211+ tables : TableWithParent [ ] ,
106212 options : { allowDangerousHtml : boolean } = { allowDangerousHtml : false } ,
107213) : void => {
108- invalidTables . forEach ( invalidTable => {
109- const invalidTableIndex = invalidTable . parent ?. children . findIndex (
110- child => child === invalidTable . inner ,
214+ tables . forEach ( table => {
215+ const tableIndex = table . parent ?. children . findIndex (
216+ child => child === table . inner ,
111217 )
112- if ( invalidTableIndex !== undefined && invalidTableIndex !== - 1 ) {
113- invalidTable . parent ?. children . splice ( invalidTableIndex , 1 , {
218+ if ( tableIndex !== undefined && tableIndex !== - 1 ) {
219+ processTableSpans ( table . inner )
220+
221+ const hastTable = toHast ( table . inner , {
222+ allowDangerousHtml : options . allowDangerousHtml ,
223+ } )
224+
225+ if ( hastTable . type === 'element' ) {
226+ const hastColGroup : hast . Element = {
227+ type : 'element' ,
228+ tagName : 'colgroup' ,
229+ properties : { } ,
230+ children :
231+ table . inner . data ?. colWidths ?. map ( width => ( {
232+ type : 'element' ,
233+ tagName : 'col' ,
234+ properties : {
235+ width :
236+ table . inner . data ?. type === BlockType . GRID
237+ ? `${ width . toFixed ( 2 ) } %`
238+ : width ,
239+ } ,
240+ children : [ ] ,
241+ } ) ) ?? [ ] ,
242+ }
243+
244+ hastTable . children = ( [ hastColGroup ] as hast . ElementContent [ ] ) . concat (
245+ hastTable . children ,
246+ )
247+ }
248+
249+ table . parent ?. children . splice ( tableIndex , 1 , {
114250 type : 'html' ,
115- value : toHtml (
116- toHast (
117- {
118- ...invalidTable . inner ,
119- children : invalidTable . inner . children . map ( row => ( {
120- ...row ,
121- children : row . children . map ( cell => ( {
122- ...cell ,
123- children : cell . data ?. invalidChildren ?? cell . children ,
124- } ) ) ,
125- } ) ) ,
126- } as mdast . Table ,
127- {
128- allowDangerousHtml : options . allowDangerousHtml ,
129- } ,
130- ) ,
131- {
132- allowDangerousHtml : options . allowDangerousHtml ,
133- } ,
134- ) ,
251+ value : toHtml ( hastTable , {
252+ allowDangerousHtml : options . allowDangerousHtml ,
253+ } ) ,
135254 } )
136255 }
137256 } )
@@ -245,36 +364,28 @@ export const transformMentionUsers = async (
245364 }
246365}
247366
248- export interface TransformTableWithParentsOptions {
249- transformGridToHtml : boolean
250- transformInvalidTablesToHtml : boolean
251- }
252-
253- export const transformTableWithParents = (
254- tableWithParents : TableWithParent [ ] ,
255- options : TransformTableWithParentsOptions ,
367+ export const transformTableBySettings = (
368+ tables : TableWithParent [ ] ,
369+ settings : Pick < Settings , SettingKey . Table | SettingKey . Grid > ,
256370) : void => {
257- if ( options . transformGridToHtml ) {
371+ if ( settings [ SettingKey . Grid ] === GridSetting . ToHTML ) {
258372 transformGridToHtml (
259- tableWithParents . filter ( item => item . inner . data ?. type === BlockType . GRID ) ,
373+ tables . filter ( item => item . inner . data ?. type === BlockType . GRID ) ,
260374 {
261375 allowDangerousHtml : true ,
262376 } ,
263377 )
264378 }
265379
266- if ( options . transformInvalidTablesToHtml ) {
267- transformInvalidTablesToHtml (
268- tableWithParents . filter (
269- item =>
270- item . inner . data ?. invalid &&
271- ( options . transformGridToHtml
272- ? item . inner . data . type !== BlockType . GRID
273- : true ) ,
274- ) ,
275- {
276- allowDangerousHtml : true ,
277- } ,
278- )
279- }
380+ transformTableToHtml (
381+ mapTableBySettings (
382+ settings [ SettingKey . Grid ] === GridSetting . ToHTML
383+ ? tables . filter ( item => item . inner . data ?. type !== BlockType . GRID )
384+ : tables ,
385+ settings ,
386+ ) ,
387+ {
388+ allowDangerousHtml : true ,
389+ } ,
390+ )
280391}
0 commit comments