Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 8, 2026

Implements parsing for Quill table modules (quill-better-table, default table module) that use table attributes in delta format.

Changes

  • New Table listener (src/listener/Table.php)

    • Extends BlockListener to handle table attributes on newlines
    • Groups cells by row ID
    • Maintains cell ordering by sequence in delta ops
    • Preserves inline formatting (bold, italic, color, etc.) within cells
    • Generates HTML with <table><tbody><tr><td> structure
  • Updated Align listener (src/listener/Align.php)

    • Added check to skip lines with table attribute
    • Prevents table content from being wrapped in alignment paragraphs
  • Lexer integration (src/Lexer.php)

    • Registered Table in loadBuiltinListeners()
    • Tables now work automatically with no configuration
  • Test coverage

    • Basic multi-row tables
    • Bold text in cells (header pattern)
    • Mixed formatting (bold + italic) with align attributes

Usage

$json = '{
  "ops": [
    {"insert": "Name"},
    {"insert": "\n", "attributes": {"table": "row-1"}},
    {"insert": "Age"},
    {"insert": "\n", "attributes": {"table": "row-1"}}
  ]
}';

$lexer = new \nadar\quill\Lexer($json);
echo $lexer->render();
// <table><tbody><tr><td>Name</td><td>Age</td></tr></tbody></table>

Notes

  • No special header elements (thead/th) - Quill table format doesn't distinguish headers from data cells
  • Follows same pattern as Lists listener for multi-element block structures
  • Cells in the same row share the same row ID value
  • Cell order is determined by sequence in delta ops
Original prompt

This section details on the original issue you should resolve

<issue_title>Support for Quill Default Table(Module) or Table Better(Extra Module)</issue_title>
<issue_description>Does we have support or anyone has implemented Quill Table Better ? I want parser for that delta json to html render in my pdf. </issue_description>

<agent_instructions>Create a new listener which generates the table, you can see the the quill which should generated the expected html, add a unit tests to ensure and compare the quill against the expect html</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@nadar if you know how the delta looks or find a documentation regarding this, we might implement it. i think there was another issue regarding this (or similar) already #86 @nadar @dishant-hupp can you please add the expected html to the given quill? maybe directly inspect the quill editors code to see what it produces. i think this kind of job can i pass to the copilot easily. @nadar > In this example, there are 4 rows, with the first row acting as a header.

i don't think this is true. This is just text which is bold. the quill format don't say anything about being a table header (th, thead) afaik?</comment_new>


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@nadar
Copy link
Owner

nadar commented Jan 8, 2026

@copilot your quill is wrong, i expect that you handle this quill

                {
                    "insert": "Task Name",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Owner",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Status",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Completion (%)",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "UI Design"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Jeet Kumar"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Completed"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "100%",
                    "attributes": {
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Backend API"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "Rahul Sharma"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "In Progress"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "65%",
                    "attributes": {
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "QA Testing"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "Anita Verma"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "Pending"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "0%",
                    "attributes": {
                        "color": "#e60000",
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-nkq1"
                    }
                }

into this html

 <table>
    <tbody>
      <tr>
        <td data-row="row-ylm9"><strong>Task Name</strong></td>
        <td data-row="row-ylm9"><strong>Owner</strong></td>
        <td data-row="row-ylm9"><strong>Status</strong></td>
        <td data-row="row-ylm9" class="ql-align-center">
          <strong>Completion (%)</strong>
        </td>
      </tr>
      <tr>
        <td data-row="row-x6o5">UI Design</td>
        <td data-row="row-x6o5">Jeet Kumar</td>
        <td data-row="row-x6o5">Completed</td>
        <td data-row="row-x6o5" class="ql-align-center"><em>100%</em></td>
      </tr>
      <tr>
        <td data-row="row-wt7t">Backend API</td>
        <td data-row="row-wt7t">Rahul Sharma</td>
        <td data-row="row-wt7t">In Progress</td>
        <td data-row="row-wt7t" class="ql-align-center"><em>65%</em></td>
      </tr>
      <tr>
        <td data-row="row-nkq1">QA Testing</td>
        <td data-row="row-nkq1">Anita Verma</td>
        <td data-row="row-nkq1"><u>Pending</u></td>
        <td data-row="row-nkq1" class="ql-align-center">
          <em style="color: rgb(230, 0, 0)">0%</em>
        </td>
      </tr>
    </tbody>
  </table>

Copilot AI changed the title [WIP] Add support for Quill Table Better module Add support for Quill table modules (table-cell-line attribute) Jan 8, 2026
Copilot AI requested a review from nadar January 8, 2026 09:24
Copy link
Owner

@nadar nadar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot there is no celldata, you make assumptions no one asked for!

Your quill is wrong, i expect that you handle this quill

                {
                    "insert": "Task Name",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Owner",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Status",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "Completion (%)",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-ylm9"
                    }
                },
                {
                    "insert": "UI Design"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Jeet Kumar"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Completed"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "100%",
                    "attributes": {
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-x6o5"
                    }
                },
                {
                    "insert": "Backend API"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "Rahul Sharma"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "In Progress"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "65%",
                    "attributes": {
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-wt7t"
                    }
                },
                {
                    "insert": "QA Testing"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "Anita Verma"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "Pending"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-nkq1"
                    }
                },
                {
                    "insert": "0%",
                    "attributes": {
                        "color": "#e60000",
                        "italic": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "align": "center",
                        "table": "row-nkq1"
                    }
                }

into this html

 <table>
    <tbody>
      <tr>
        <td data-row="row-ylm9"><strong>Task Name</strong></td>
        <td data-row="row-ylm9"><strong>Owner</strong></td>
        <td data-row="row-ylm9"><strong>Status</strong></td>
        <td data-row="row-ylm9" class="ql-align-center">
          <strong>Completion (%)</strong>
        </td>
      </tr>
      <tr>
        <td data-row="row-x6o5">UI Design</td>
        <td data-row="row-x6o5">Jeet Kumar</td>
        <td data-row="row-x6o5">Completed</td>
        <td data-row="row-x6o5" class="ql-align-center"><em>100%</em></td>
      </tr>
      <tr>
        <td data-row="row-wt7t">Backend API</td>
        <td data-row="row-wt7t">Rahul Sharma</td>
        <td data-row="row-wt7t">In Progress</td>
        <td data-row="row-wt7t" class="ql-align-center"><em>65%</em></td>
      </tr>
      <tr>
        <td data-row="row-nkq1">QA Testing</td>
        <td data-row="row-nkq1">Anita Verma</td>
        <td data-row="row-nkq1"><u>Pending</u></td>
        <td data-row="row-nkq1" class="ql-align-center">
          <em style="color: rgb(230, 0, 0)">0%</em>
        </td>
      </tr>
    </tbody>
  </table>

Don’t invent any other Quill format than the one I gave you.

Copy link
Contributor Author

Copilot AI commented Jan 8, 2026

@copilot your quill is wrong, i expect that you handle this quill

...

Fixed in commit 9e44c59. The Table listener now correctly handles the "table": "row-id" attribute format. Updated the Align listener to skip table lines (preventing duplicate content), and rewrote all tests to use the correct Quill table delta format. All tests pass with your example data.

Copilot AI changed the title Add support for Quill table modules (table-cell-line attribute) Add support for Quill table modules (table attribute) Jan 8, 2026
Copilot AI requested a review from nadar January 8, 2026 09:32
Copy link
Contributor Author

Copilot AI commented Jan 8, 2026

@copilot there is no celldata, you make assumptions no one asked for!

Your quill is wrong, i expect that you handle this quill...

The Table listener has been updated in commits c7f50e3 and 9e44c59. There's no "celldata" - it now uses only "table": "row-id" as you specified. The implementation correctly handles your example.

Two notes on the expected HTML you provided:

  1. Row 4, cell 3 shows <td></td> (empty) but the JSON has "Pending" - should this cell include the text?
  2. The color span on "0%" comes from the Color listener, not Table listener - is this the expected behavior?

@nadar
Copy link
Owner

nadar commented Jan 8, 2026

@dishant-hupp would you like to give it a spin?

@nadar nadar marked this pull request as ready for review January 8, 2026 10:28
@dishant-hupp
Copy link

@dishant-hupp would you like to give it a spin?

I’ll pull the latest changes from this, test them on my end, and update you. I've also built a custom Table class and make custom listner for it, but I'm definitely interested in trying yours.

@dishant-hupp
Copy link

dishant-hupp commented Jan 13, 2026

@nadar I've thoroughly reviewed this update. While it works to some extent, the borders and overall formatting are not as expected. I've created a modified version that provides a much better visual result. However, there's an issue: when two tables are placed one after another, they are not recognized as separate entities and get merged, even when separated by a <p> tag. Could you share your email address so I can provide more details, or you can reach me at [email protected]?

@nadar
Copy link
Owner

nadar commented Jan 13, 2026

please post the problematic quill code here, so we can add it directly as unit test.

@dishant-hupp
Copy link

dishant-hupp commented Jan 22, 2026

{
            "ops": [
                {
                    "insert": "First Table within same block.\n"
                },
                {
                    "insert": "First Name",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-kiwk"
                    }
                },
                {
                    "insert": "Last Name",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-kiwk"
                    }
                },
                {
                    "insert": "John"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-kwcq"
                    }
                },
                {
                    "insert": "Doe"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-kwcq"
                    }
                },
                {
                    "insert": "Elone"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-oaw5"
                    }
                },
                {
                    "insert": "Musk"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-oaw5"
                    }
                },
                {
                    "insert": "Second Table within same block.\n"
                },
                {
                    "insert": "Document Name",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-4jdo"
                    }
                },
                {
                    "insert": "Submitted ?",
                    "attributes": {
                        "bold": true
                    }
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-4jdo"
                    }
                },
                {
                    "insert": " ABC"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-r7zm"
                    }
                },
                {
                    "insert": "Yes"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-r7zm"
                    }
                },
                {
                    "insert": "XYZ"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-4gk9"
                    }
                },
                {
                    "insert": "No"
                },
                {
                    "insert": "\n",
                    "attributes": {
                        "table": "row-4gk9"
                    }
                },
                {
                    "insert": " \n"
                }
            ]
        }
image

@dishant-hupp
Copy link

Frontend Quill Generated Inspect HTML Code :

   <p>First Table within same block.</p>
   <table>
      <tbody>
         <tr>
            <td data-row="row-kiwk"><strong>First Name</strong></td>
            <td data-row="row-kiwk"><strong>Last Name</strong></td>
         </tr>
         <tr>
            <td data-row="row-kwcq">John</td>
            <td data-row="row-kwcq">Doe</td>
         </tr>
         <tr>
            <td data-row="row-oaw5">Elone</td>
            <td data-row="row-oaw5">Musk</td>
         </tr>
      </tbody>
   </table>
   <p>Second Table within same block.</p>
   <table>
      <tbody>
         <tr>
            <td data-row="row-4jdo"><strong>Document Name</strong></td>
            <td data-row="row-4jdo"><strong>Submitted ?</strong></td>
         </tr>
         <tr>
            <td data-row="row-r7zm"> ABC</td>
            <td data-row="row-r7zm">Yes</td>
         </tr>
         <tr>
            <td data-row="row-4gk9">XYZ</td>
            <td data-row="row-4gk9">No</td>
         </tr>
      </tbody>
   </table>

@nadar
Copy link
Owner

nadar commented Jan 22, 2026

But that looks good i would say @dishant-hupp ?

@dishant-hupp
Copy link

But that looks good i would say @dishant-hupp ?

This is not PDF parse output. Image shared it come from Frontend Editor where Quill is integrated.
generated PDF has different out like bellow. check the sequence.. it merge the rows and combine at last.
image

@nadar
Copy link
Owner

nadar commented Jan 22, 2026

  1. show me the quill
  2. show me the html which the generate does

we have nothing to do with pdfs here, it might be a problem somewhere else, so please just post the quill and the html output from the parser

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Quill Default Table(Module) or Table Better(Extra Module)

3 participants