@tduncan / Cortana / commits / c6a28d0

Added `tableBuilder` to fix scrollbar position on tables.

Co-authored-by: abdulmominsakib <abdulmominsakib@users.noreply.github.com>

opencode-agent[bot] committed May 27, 2026 at 17:23 UTC c6a28d0c8eb904d7fbb547cc7d9c399edde373fd
1 file changed +98 -1
lib/features/chat/views/components/chat_bubble.dart
+98 -1
@@ -467,7 +467,104 @@ class _ThemedGptMarkdown extends StatelessWidget {
467
468 return GptMarkdownTheme(
469 gptThemeData: gptTheme,
470 - child: GptMarkdown(content, style: style, followLinkColor: true),
470 + child: GptMarkdown(
471 + content,
472 + style: style,
473 + followLinkColor: true,
474 + tableBuilder: _buildTable,
475 + ),
476 + );
477 + }
478 +}
479 +
480 +Widget _buildTable(
481 + BuildContext context,
482 + List<CustomTableRow> tableRows,
483 + TextStyle textStyle,
484 + GptMarkdownConfig config,
485 +) {
486 + return _TableScrollView(
487 + tableRows: tableRows,
488 + config: config,
489 + );
490 +}
491 +
492 +class _TableScrollView extends StatefulWidget {
493 + const _TableScrollView({
494 + required this.tableRows,
495 + required this.config,
496 + });
497 +
498 + final List<CustomTableRow> tableRows;
499 + final GptMarkdownConfig config;
500 +
501 + @override
502 + State<_TableScrollView> createState() => _TableScrollViewState();
503 +}
504 +
505 +class _TableScrollViewState extends State<_TableScrollView> {
506 + final ScrollController _controller = ScrollController();
507 +
508 + @override
509 + void dispose() {
510 + _controller.dispose();
511 + super.dispose();
512 + }
513 +
514 + @override
515 + Widget build(BuildContext context) {
516 + return Scrollbar(
517 + controller: _controller,
518 + thumbVisibility: true,
519 + child: SingleChildScrollView(
520 + controller: _controller,
521 + scrollDirection: Axis.horizontal,
522 + child: Table(
523 + textDirection: widget.config.textDirection,
524 + defaultColumnWidth: const IntrinsicColumnWidth(),
525 + defaultVerticalAlignment: TableCellVerticalAlignment.middle,
526 + border: TableBorder.all(
527 + width: 1,
528 + color: Theme.of(context).colorScheme.onSurface,
529 + ),
530 + children: widget.tableRows.map((row) {
531 + return TableRow(
532 + decoration: row.isHeader
533 + ? BoxDecoration(
534 + color: Theme.of(context).colorScheme.surfaceContainerHighest,
535 + )
536 + : null,
537 + children: row.fields.map((field) {
538 + Widget content = Padding(
539 + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
540 + child: MdWidget(
541 + context,
542 + field.data,
543 + false,
544 + config: widget.config,
545 + ),
546 + );
547 + switch (field.alignment) {
548 + case TextAlign.center:
549 + content = Center(child: content);
550 + case TextAlign.right:
551 + content = Align(
552 + alignment: Alignment.centerRight,
553 + child: content,
554 + );
555 + case TextAlign.left:
556 + default:
557 + content = Align(
558 + alignment: Alignment.centerLeft,
559 + child: content,
560 + );
561 + }
562 + return content;
563 + }).toList(),
564 + );
565 + }).toList(),
566 + ),
567 + ),
568 );
569 }
570 }