feat(renderer): interactive table with sort, filter & CSV export (M10.9)

- InteractiveTable.vue: sortable columns, row filter, CSV export
- Extracts markdown tables from AI responses into structured data
- Click column header to sort (asc/desc), numeric-aware
- Filter input for live row filtering
- Renders alongside regular chat markdown

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-04 00:00:08 +00:00
co-authored by Claude Opus 4.6
parent 9404085668
commit 04a175171b
3 changed files with 199 additions and 0 deletions
@@ -0,0 +1,49 @@
/**
* Extract markdown tables from text into structured data
* for interactive table rendering.
*/
export interface TableData {
headers: string[]
rows: string[][]
raw: string
}
// Matches a markdown table: header row, separator row, data rows
const TABLE_RE = /^(\|[^\n]+\|)\n(\|[\s:|-]+\|)\n((?:\|[^\n]+\|\n?)+)/gm
export function extractTables(text: string): TableData[] {
const results: TableData[] = []
let m: RegExpExecArray | null
const re = new RegExp(TABLE_RE.source, TABLE_RE.flags)
while ((m = re.exec(text)) !== null) {
const headerLine = m[1]
const dataBlock = m[3]
const headers = parseRow(headerLine)
const rows = dataBlock
.trim()
.split('\n')
.map(parseRow)
.filter((r) => r.length > 0)
if (headers.length > 0 && rows.length > 0) {
results.push({ headers, rows, raw: m[0] })
}
}
return results
}
function parseRow(line: string): string[] {
return line
.replace(/^\|/, '')
.replace(/\|$/, '')
.split('|')
.map((cell) => cell.trim())
}
export function hasTables(text: string): boolean {
return TABLE_RE.test(text)
}