|
13 | 13 | import { tick, onMount } from "svelte"; |
14 | 14 | import { Upload } from "@gradio/upload"; |
15 | 15 |
|
| 16 | + import { MarkdownCode } from "@gradio/markdown-code"; |
16 | 17 | import HeaderCell from "./HeaderCell.svelte"; |
17 | 18 | import DataCell from "./DataCell.svelte"; |
18 | 19 | import EmptyRowButton from "./EmptyRowButton.svelte"; |
|
263 | 264 | return Array.isArray(datatype) ? (datatype[col] ?? "str") : datatype; |
264 | 265 | } |
265 | 266 |
|
| 267 | + type SizingEntry = { val: string; col_idx: number; dtype: Datatype }; |
| 268 | +
|
| 269 | + // heading multipliers for markdown/html block elements that render |
| 270 | + // at a larger font size than body text. |
| 271 | + const HEADING_MULT = [2.2, 1.7, 1.4, 1.2, 1.1, 1.05]; |
| 272 | +
|
| 273 | + function md_visual_length(s: string): number { |
| 274 | + const heading = s.match(/^\s*(#{1,6})\s+(.+)/); |
| 275 | + if (heading) { |
| 276 | + const lvl = heading[1].length; |
| 277 | + const text = heading[2].replace(/[*_`[\]()]/g, ""); |
| 278 | + return text.length * HEADING_MULT[lvl - 1]; |
| 279 | + } |
| 280 | + return s.replace(/[*_`#[\]()]/g, "").length; |
| 281 | + } |
| 282 | +
|
| 283 | + function html_visual_length(s: string): number { |
| 284 | + const stripped = s.replace(/<[^>]+>/g, "").length; |
| 285 | + const h = s.match(/<h([1-6])\b/i); |
| 286 | + if (h) return stripped * HEADING_MULT[parseInt(h[1]) - 1]; |
| 287 | + return stripped; |
| 288 | + } |
| 289 | +
|
| 290 | + // mirror EditableCell.truncate_text so the sizing row reserves width |
| 291 | + // for the truncated ("…") string, not the full source |
| 292 | + function apply_truncation(s: string, dtype: Datatype): string { |
| 293 | + if ( |
| 294 | + max_chars && |
| 295 | + max_chars > 0 && |
| 296 | + dtype !== "image" && |
| 297 | + s.length > max_chars |
| 298 | + ) { |
| 299 | + return s.slice(0, max_chars) + "..."; |
| 300 | + } |
| 301 | + return s; |
| 302 | + } |
| 303 | +
|
| 304 | + // find the widest rendered value per visible column for the sizing row |
| 305 | + function compute_sizing_row(): SizingEntry[] { |
| 306 | + const headers = header_groups[0]?.headers ?? []; |
| 307 | + return headers.map((header) => { |
| 308 | + const col_idx = (header.column.columnDef.meta as any)?.colIndex ?? 0; |
| 309 | + const dtype = get_dtype(col_idx); |
| 310 | + const accessor = `col_${col_idx}`; |
| 311 | +
|
| 312 | + if (dtype === "bool") { |
| 313 | + return { val: "false", col_idx, dtype }; |
| 314 | + } |
| 315 | +
|
| 316 | + const visual_len = |
| 317 | + dtype === "markdown" |
| 318 | + ? md_visual_length |
| 319 | + : dtype === "html" |
| 320 | + ? html_visual_length |
| 321 | + : (s: string) => s.length; |
| 322 | +
|
| 323 | + let best = ""; |
| 324 | + let best_len = -1; |
| 325 | + for (const r of rows) { |
| 326 | + const row_idx = r.original._index; |
| 327 | + const rendered = editable |
| 328 | + ? r.original[accessor] |
| 329 | + : (display_value?.[row_idx]?.[col_idx] ?? |
| 330 | + values?.[row_idx]?.[col_idx]); |
| 331 | + if (rendered == null) continue; |
| 332 | + const v = apply_truncation(String(rendered), dtype); |
| 333 | + const len = visual_len(v); |
| 334 | + if (len > best_len) { |
| 335 | + best_len = len; |
| 336 | + best = v; |
| 337 | + } |
| 338 | + } |
| 339 | + return { val: best, col_idx, dtype }; |
| 340 | + }); |
| 341 | + } |
| 342 | +
|
266 | 343 | function get_display_value(row: number, col: number): string { |
267 | 344 | if (display_value?.[row]?.[col] !== undefined) |
268 | 345 | return display_value[row][col]; |
|
932 | 1009 |
|
933 | 1010 | <tbody class="sizing-body" aria-hidden="true"> |
934 | 1011 | {#if rows.length > 0} |
935 | | - {@const sizing_row = rows.reduce((widest, row) => { |
936 | | - const cells = row.getVisibleCells(); |
937 | | - cells.forEach((cell, i) => { |
938 | | - let val = String(cell.getValue() ?? ""); |
939 | | - if ( |
940 | | - max_chars && |
941 | | - max_chars > 0 && |
942 | | - val.length > max_chars && |
943 | | - get_dtype(i) !== "image" |
944 | | - ) { |
945 | | - val = val.slice(0, max_chars) + "..."; |
946 | | - } |
947 | | - if (!widest[i] || val.length > widest[i].length) { |
948 | | - widest[i] = val; |
949 | | - } |
950 | | - }); |
951 | | - return widest; |
952 | | - }, [] as string[])} |
| 1012 | + {@const sizing_row = compute_sizing_row()} |
953 | 1013 | <tr> |
954 | 1014 | {#if show_row_numbers} |
955 | 1015 | <td class="row-number-cell">{rows.length}</td> |
956 | 1016 | {/if} |
957 | | - {#each sizing_row as val, ci} |
958 | | - {@const dtype = get_dtype(ci)} |
| 1017 | + {#each sizing_row as entry (entry.col_idx)} |
959 | 1018 | <td |
960 | 1019 | ><div class="cell-wrap"> |
961 | | - {#if dtype === "html" || dtype === "markdown"}{@html val}{:else}{val}{/if} |
| 1020 | + {#if entry.dtype === "markdown"} |
| 1021 | + <MarkdownCode |
| 1022 | + message={entry.val} |
| 1023 | + {latex_delimiters} |
| 1024 | + {line_breaks} |
| 1025 | + chatbot={false} |
| 1026 | + /> |
| 1027 | + {:else if entry.dtype === "html"} |
| 1028 | + {@html entry.val} |
| 1029 | + {:else} |
| 1030 | + {entry.val} |
| 1031 | + {/if} |
962 | 1032 | </div></td |
963 | 1033 | > |
964 | 1034 | {/each} |
|
0 commit comments