mirror of
https://github.com/dergigi/boris.git
synced 2025-12-17 14:44:26 +01:00
Merge pull request #38 from dergigi/render-tables
Add markdown table rendering support
This commit is contained in:
2
.env
2
.env
@@ -1,2 +0,0 @@
|
|||||||
# Default article to display on app load
|
|
||||||
VITE_DEFAULT_ARTICLE_NADDR=naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew
|
|
||||||
17
.env.example
17
.env.example
@@ -1,3 +1,14 @@
|
|||||||
# Default article to display on app load
|
# Nostr configuration for publish-markdown.sh script
|
||||||
# This should be a valid naddr1... string (NIP-19 encoded address pointer to a kind:30023 long-form article)
|
# Copy this file to .env and fill in your values
|
||||||
VITE_DEFAULT_ARTICLE_NADDR=naddr1qvzqqqr4gupzqmjxss3dld622uu8q25gywum9qtg4w4cv4064jmg20xsac2aam5nqqxnzd3cxqmrzv3exgmr2wfesgsmew
|
|
||||||
|
# Your Nostr secret key (nsec, ncryptsec, or hex format)
|
||||||
|
# You can also set this via environment variable: export NOSTR_SECRET_KEY=your_key
|
||||||
|
NOSTR_SECRET_KEY=
|
||||||
|
|
||||||
|
# Space-separated list of relay URLs to publish to
|
||||||
|
# If not provided, events will be created but not published
|
||||||
|
RELAYS="ws://localhost:10547 ws://localhost:4869 wss://relay.primal.net wss://wot.dergigi.com wss://relay.dergigi.com wss://nostr.einundzwanzig.space wss://relay.damus.io wss://relay.nostr.bg wss://nos.lol wss://eden.nostr.land"
|
||||||
|
|
||||||
|
# Test account used for publishing markdown test documents:
|
||||||
|
# npub: npub1marky39a9qmadyuux9lr49pdhy3ddxrdwtmd9y957kye66qyu3vq7spdm2
|
||||||
|
# Profile: https://read.withboris.com/p/npub1marky39a9qmadyuux9lr49pdhy3ddxrdwtmd9y957kye66qyu3vq7spdm2/writings
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@ applesauce
|
|||||||
primal-web-app
|
primal-web-app
|
||||||
Amber
|
Amber
|
||||||
|
|
||||||
|
.env
|
||||||
|
scripts/.env
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||||
|
"publish:test:markdown": "./scripts/publish-markdown.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
"@fortawesome/fontawesome-svg-core": "^7.1.0",
|
||||||
|
|||||||
202
scripts/publish-markdown.sh
Executable file
202
scripts/publish-markdown.sh
Executable file
@@ -0,0 +1,202 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to publish markdown files from test/markdown/ to Nostr using nak
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/publish-markdown.sh [filename] [relay1] [relay2] ...
|
||||||
|
# ./scripts/publish-markdown.sh # Interactive mode
|
||||||
|
# ./scripts/publish-markdown.sh tables.md # Publish specific file
|
||||||
|
# ./scripts/publish-markdown.sh tables.md wss://relay.example.com # With relay
|
||||||
|
#
|
||||||
|
# Environment:
|
||||||
|
# The script reads .env from the project root directory ($PROJECT_ROOT/.env)
|
||||||
|
# Required: NOSTR_SECRET_KEY (your nsec, ncryptsec, or hex format key)
|
||||||
|
# Optional: RELAYS (space-separated list of relay URLs)
|
||||||
|
#
|
||||||
|
# Test account for markdown test documents:
|
||||||
|
# npub: npub1marky39a9qmadyuux9lr49pdhy3ddxrdwtmd9y957kye66qyu3vq7spdm2
|
||||||
|
# Profile: https://read.withboris.com/p/npub1marky39a9qmadyuux9lr49pdhy3ddxrdwtmd9y957kye66qyu3vq7spdm2/writings
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
MARKDOWN_DIR="$PROJECT_ROOT/test/markdown"
|
||||||
|
ENV_FILE="$PROJECT_ROOT/.env"
|
||||||
|
|
||||||
|
# Load .env file if it exists
|
||||||
|
if [ -f "$ENV_FILE" ]; then
|
||||||
|
# Source the .env file, handling quoted values properly
|
||||||
|
set -a # Automatically export all variables
|
||||||
|
# Use eval to properly handle quoted values (safe since we control the file)
|
||||||
|
# This handles both unquoted and quoted values correctly
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
# Skip comments and empty lines
|
||||||
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
[[ -z "$line" ]] && continue
|
||||||
|
# Remove leading/trailing whitespace
|
||||||
|
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
|
||||||
|
# Export the variable (handles quoted values)
|
||||||
|
eval "export $line"
|
||||||
|
done < "$ENV_FILE"
|
||||||
|
set +a # Stop automatically exporting
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if nak is installed
|
||||||
|
if ! command -v nak &> /dev/null; then
|
||||||
|
echo "Error: nak is not installed or not in PATH"
|
||||||
|
echo "Install from: https://github.com/fiatjaf/nak"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Function to publish a markdown file
|
||||||
|
publish_file() {
|
||||||
|
local file_path="$1"
|
||||||
|
shift # Remove first argument, rest are relay URLs
|
||||||
|
local relays=("$@")
|
||||||
|
local filename=$(basename "$file_path")
|
||||||
|
local identifier="${filename%.md}" # Remove .md extension
|
||||||
|
|
||||||
|
echo "📝 Publishing: $filename"
|
||||||
|
echo " Identifier: $identifier"
|
||||||
|
|
||||||
|
# Extract title from first H1 if available, otherwise use filename
|
||||||
|
local title=$(grep -m 1 "^# " "$file_path" | sed 's/^# //' || echo "$identifier")
|
||||||
|
|
||||||
|
# Add relays if provided
|
||||||
|
if [ ${#relays[@]} -gt 0 ]; then
|
||||||
|
echo " Relays: ${relays[*]}"
|
||||||
|
else
|
||||||
|
echo " Note: No relays specified. Event will be created but not published."
|
||||||
|
echo " Add relay URLs as arguments to publish, e.g.: wss://relay.example.com"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Publish as kind 30023 (NIP-23 blog post)
|
||||||
|
# The "d" tag is required for replaceable events (kind 30023)
|
||||||
|
# Using the filename (without extension) as the identifier
|
||||||
|
# Build command array to avoid eval issues
|
||||||
|
# Use @filename syntax to read content from file (nak supports this)
|
||||||
|
local cmd_args=(
|
||||||
|
"event"
|
||||||
|
"-k" "30023"
|
||||||
|
"-d" "$identifier"
|
||||||
|
"-t" "title=$title"
|
||||||
|
"--content" "@$file_path"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add relays if provided
|
||||||
|
if [ ${#relays[@]} -gt 0 ]; then
|
||||||
|
cmd_args+=("${relays[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
nak "${cmd_args[@]}"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "✅ Successfully published: $filename"
|
||||||
|
else
|
||||||
|
echo "❌ Failed to publish: $filename"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check for NOSTR_SECRET_KEY
|
||||||
|
if [ -z "$NOSTR_SECRET_KEY" ]; then
|
||||||
|
echo "⚠️ Warning: NOSTR_SECRET_KEY environment variable not set"
|
||||||
|
echo " Set it in .env file or with: export NOSTR_SECRET_KEY=your_key_here"
|
||||||
|
echo " Or use --prompt-sec flag (nak will prompt for key)"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse RELAYS from environment if set
|
||||||
|
default_relays=()
|
||||||
|
if [ -n "$RELAYS" ]; then
|
||||||
|
# Split RELAYS string into array
|
||||||
|
read -ra default_relays <<< "$RELAYS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Main logic
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
# No arguments: list all markdown files and let user choose
|
||||||
|
echo "Available markdown files:"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
files=("$MARKDOWN_DIR"/*.md)
|
||||||
|
if [ ! -e "${files[0]}" ]; then
|
||||||
|
echo "No markdown files found in $MARKDOWN_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display files with numbers
|
||||||
|
declare -a file_array
|
||||||
|
i=1
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
filename=$(basename "$file")
|
||||||
|
echo " $i) $filename"
|
||||||
|
file_array[$i]="$file"
|
||||||
|
((i++))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Enter file number(s) to publish (space-separated), or 'all' for all files:"
|
||||||
|
read -r selection
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
if [ ${#default_relays[@]} -gt 0 ]; then
|
||||||
|
echo "Enter relay URLs (space-separated, or press Enter to use defaults from .env):"
|
||||||
|
echo " Defaults: ${default_relays[*]}"
|
||||||
|
else
|
||||||
|
echo "Enter relay URLs (space-separated, or press Enter to skip):"
|
||||||
|
fi
|
||||||
|
read -r relay_input
|
||||||
|
|
||||||
|
# Parse relay URLs
|
||||||
|
relays=()
|
||||||
|
if [ -n "$relay_input" ]; then
|
||||||
|
read -ra relays <<< "$relay_input"
|
||||||
|
elif [ ${#default_relays[@]} -gt 0 ]; then
|
||||||
|
# Use defaults from .env
|
||||||
|
relays=("${default_relays[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$selection" = "all" ]; then
|
||||||
|
# Publish all files
|
||||||
|
for file in "${files[@]}"; do
|
||||||
|
publish_file "$file" "${relays[@]}"
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# Publish selected files
|
||||||
|
for num in $selection; do
|
||||||
|
if [ -n "${file_array[$num]}" ]; then
|
||||||
|
publish_file "${file_array[$num]}" "${relays[@]}"
|
||||||
|
echo ""
|
||||||
|
else
|
||||||
|
echo "⚠️ Invalid selection: $num"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Argument provided: publish specific file
|
||||||
|
filename="$1"
|
||||||
|
shift # Remove filename, rest are relay URLs
|
||||||
|
relays=("$@")
|
||||||
|
|
||||||
|
# If no relays provided as arguments, use defaults from .env
|
||||||
|
if [ ${#relays[@]} -eq 0 ] && [ ${#default_relays[@]} -gt 0 ]; then
|
||||||
|
relays=("${default_relays[@]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If filename doesn't end with .md, add it
|
||||||
|
if [[ ! "$filename" =~ \.md$ ]]; then
|
||||||
|
filename="${filename}.md"
|
||||||
|
fi
|
||||||
|
|
||||||
|
file_path="$MARKDOWN_DIR/$filename"
|
||||||
|
|
||||||
|
if [ ! -f "$file_path" ]; then
|
||||||
|
echo "Error: File not found: $file_path"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
publish_file "$file_path" "${relays[@]}"
|
||||||
|
fi
|
||||||
|
|
||||||
@@ -170,6 +170,49 @@
|
|||||||
.reader-html pre { background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: 8px; padding: 1rem; overflow-x: auto; margin: 1rem 0; font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace; }
|
.reader-html pre { background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: 8px; padding: 1rem; overflow-x: auto; margin: 1rem 0; font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace; }
|
||||||
.reader-html code { background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: 4px; padding: 0.15rem 0.4rem; font-size: 0.9em; font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace; }
|
.reader-html code { background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: 4px; padding: 0.15rem 0.4rem; font-size: 0.9em; font-family: 'Monaco', 'Menlo', 'Consolas', 'Courier New', monospace; }
|
||||||
.reader-html pre code { background: transparent; border: none; padding: 0; display: block; }
|
.reader-html pre code { background: transparent; border: none; padding: 0; display: block; }
|
||||||
|
/* Tables - subtle styling that matches the app theme */
|
||||||
|
.reader-markdown table, .reader-html table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--color-bg);
|
||||||
|
}
|
||||||
|
.reader-markdown thead, .reader-html thead {
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
}
|
||||||
|
.reader-markdown th, .reader-html th {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text);
|
||||||
|
border-bottom: 2px solid var(--color-border);
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
.reader-markdown td, .reader-html td {
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--color-border-subtle);
|
||||||
|
color: var(--color-text);
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.reader-markdown tbody tr:last-child td, .reader-html tbody tr:last-child td {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
/* Subtle row striping for better readability */
|
||||||
|
.reader-markdown tbody tr:nth-child(even), .reader-html tbody tr:nth-child(even) {
|
||||||
|
background: var(--color-bg-subtle);
|
||||||
|
}
|
||||||
|
/* Table alignment support */
|
||||||
|
.reader-markdown th[align="center"], .reader-html th[align="center"],
|
||||||
|
.reader-markdown td[align="center"], .reader-html td[align="center"] {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.reader-markdown th[align="right"], .reader-html th[align="right"],
|
||||||
|
.reader-markdown td[align="right"], .reader-html td[align="right"] {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
/* Mobile: prevent code blocks from causing horizontal overflow */
|
/* Mobile: prevent code blocks from causing horizontal overflow */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.reader-markdown pre, .reader-html pre {
|
.reader-markdown pre, .reader-html pre {
|
||||||
@@ -190,6 +233,12 @@
|
|||||||
display: block;
|
display: block;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
/* Reduce padding on mobile for better fit */
|
||||||
|
.reader-markdown table td, .reader-html table td,
|
||||||
|
.reader-markdown table th, .reader-html table th {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reader-markdown img, .reader-html img {
|
.reader-markdown img, .reader-html img {
|
||||||
|
|||||||
152
test/markdown/tables.md
Normal file
152
test/markdown/tables.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Markdown Tables Test
|
||||||
|
|
||||||
|
This file contains various markdown table examples to test table parsing and rendering.
|
||||||
|
|
||||||
|
## Basic Table
|
||||||
|
|
||||||
|
This is a simple two-column table with two data rows. It tests basic table structure and rendering without any special formatting or alignment.
|
||||||
|
|
||||||
|
| Column 1 | Column 2 |
|
||||||
|
| ------------- | ------------- |
|
||||||
|
| Cell 1, Row 1 | Cell 2, Row 1 |
|
||||||
|
| Cell 1, Row 2 | Cell 2, Row 2 |
|
||||||
|
|
||||||
|
## Table with Alignment
|
||||||
|
|
||||||
|
This table demonstrates text alignment options in markdown tables. The first column is left-aligned (default), the second is centered using `:---:`, and the third is right-aligned using `---:`. This tests that the CSS alignment rules work correctly.
|
||||||
|
|
||||||
|
| Left | Centered | Right |
|
||||||
|
| :----------- | :--------------: | -------------------------: |
|
||||||
|
| This is left | Text is centered | And this is right-aligned |
|
||||||
|
| More text | Even more text | And even more to the right |
|
||||||
|
|
||||||
|
## Table with Formatting
|
||||||
|
|
||||||
|
This table contains various markdown formatting within cells: italic text using asterisks, bold text using double asterisks, and inline code using backticks. This tests that formatting is preserved and rendered correctly within table cells.
|
||||||
|
|
||||||
|
| Name | Location | Food |
|
||||||
|
| ------- | ------------ | ------- |
|
||||||
|
| *Alice* | **New York** | `Pizza` |
|
||||||
|
| Bob | Paris | Crepes |
|
||||||
|
|
||||||
|
## Table with Links
|
||||||
|
|
||||||
|
This table includes markdown links within cells. It tests that hyperlinks are properly rendered and clickable within table cells, and that link styling matches the app's theme.
|
||||||
|
|
||||||
|
| Name | Website | Description |
|
||||||
|
| ----- | -------------------------- | --------------------- |
|
||||||
|
| Alice | [GitHub](https://github.com) | Code repository |
|
||||||
|
| Bob | [Nostr](https://nostr.com) | Decentralized network |
|
||||||
|
|
||||||
|
## Table with Code Blocks
|
||||||
|
|
||||||
|
This table contains inline code examples in cells. It tests that code formatting (monospace font, background, borders) is properly applied within table cells and doesn't conflict with table styling.
|
||||||
|
|
||||||
|
| Language | Example |
|
||||||
|
| -------- | -------------------------- |
|
||||||
|
| Python | `print("Hello, World!")` |
|
||||||
|
| JavaScript | `console.log("Hello")` |
|
||||||
|
| SQL | `SELECT * FROM users` |
|
||||||
|
|
||||||
|
## Wide Table (Testing Horizontal Scroll)
|
||||||
|
|
||||||
|
This table has eight columns to test horizontal scrolling behavior on mobile devices and smaller screens. The table should allow users to scroll horizontally to view all columns while maintaining proper styling and readability.
|
||||||
|
|
||||||
|
| Column 1 | Column 2 | Column 3 | Column 4 | Column 5 | Column 6 | Column 7 | Column 8 |
|
||||||
|
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||||
|
| Data 1 | Data 2 | Data 3 | Data 4 | Data 5 | Data 6 | Data 7 | Data 8 |
|
||||||
|
| More | Content | Here | To | Test | Scrolling| Behavior | Mobile |
|
||||||
|
|
||||||
|
## Table with Mixed Content
|
||||||
|
|
||||||
|
This table combines various content types: currency values, emoji indicators, and descriptive text. It tests how different content types render together within table cells and ensures proper spacing and alignment.
|
||||||
|
|
||||||
|
| Item | Price | Status | Notes |
|
||||||
|
| ---- | ----- | ------ | ------------------------------ |
|
||||||
|
| Apple | $1.00 | ✅ In stock | Fresh from the farm |
|
||||||
|
| Banana | $0.50 | ⚠️ Low stock | Last few left |
|
||||||
|
| Orange | $1.25 | ❌ Out of stock | Coming next week |
|
||||||
|
|
||||||
|
## Table with Empty Cells
|
||||||
|
|
||||||
|
This table contains empty cells to test how the table styling handles missing data. Empty cells should still maintain proper borders and spacing, ensuring the table structure remains intact.
|
||||||
|
|
||||||
|
| Name | Email | Phone |
|
||||||
|
| ---- | ----- | ----- |
|
||||||
|
| Alice | alice@example.com | |
|
||||||
|
| Bob | | 555-1234 |
|
||||||
|
| Charlie | charlie@example.com | 555-5678 |
|
||||||
|
|
||||||
|
## Table with Long Text
|
||||||
|
|
||||||
|
This table tests text wrapping behavior with varying column widths. The third column contains a long paragraph that should wrap to multiple lines within the cell while maintaining proper padding and readability. This is especially important for responsive design.
|
||||||
|
|
||||||
|
| Short | Medium Length Column | Very Long Column That Contains A Lot Of Text And Should Wrap Properly |
|
||||||
|
| ----- | -------------------- | -------------------------------------------------------------------- |
|
||||||
|
| A | This is medium text | This is a very long piece of text that should wrap to multiple lines when displayed in the table cell. It should maintain proper formatting and readability. |
|
||||||
|
|
||||||
|
## Table with Numbers
|
||||||
|
|
||||||
|
This table contains 21 rows of ranked data with numeric scores and percentages. It's useful for testing row striping, scrolling behavior with longer tables, and ensuring that numeric alignment and formatting remain consistent throughout a larger dataset.
|
||||||
|
|
||||||
|
| Rank | Name | Score | Percentage |
|
||||||
|
| ---- | ---- | ----- | ---------- |
|
||||||
|
| 1 | Alice | 95 | 95% |
|
||||||
|
| 2 | Bob | 87 | 87% |
|
||||||
|
| 3 | Charlie | 82 | 82% |
|
||||||
|
| 4 | David | 78 | 78% |
|
||||||
|
| 5 | Emma | 75 | 75% |
|
||||||
|
| 6 | Frank | 72 | 72% |
|
||||||
|
| 7 | Grace | 70 | 70% |
|
||||||
|
| 8 | Henry | 68 | 68% |
|
||||||
|
| 9 | Ivy | 65 | 65% |
|
||||||
|
| 10 | Jack | 63 | 63% |
|
||||||
|
| 11 | Kate | 60 | 60% |
|
||||||
|
| 12 | Liam | 58 | 58% |
|
||||||
|
| 13 | Mia | 55 | 55% |
|
||||||
|
| 14 | Noah | 53 | 53% |
|
||||||
|
| 15 | Olivia | 50 | 50% |
|
||||||
|
| 16 | Paul | 48 | 48% |
|
||||||
|
| 17 | Quinn | 45 | 45% |
|
||||||
|
| 18 | Ryan | 43 | 43% |
|
||||||
|
| 19 | Sarah | 40 | 40% |
|
||||||
|
| 20 | Tom | 38 | 38% |
|
||||||
|
| 21 | Uma | 35 | 35% |
|
||||||
|
|
||||||
|
## Table with Special Characters
|
||||||
|
|
||||||
|
This table contains escaped special characters that have meaning in markdown syntax. It tests that these characters are properly escaped and displayed as literal characters rather than being interpreted as markdown syntax.
|
||||||
|
|
||||||
|
| Symbol | Name | Usage |
|
||||||
|
| ------ | ---- | ----- |
|
||||||
|
| `\|` | Pipe | Used in markdown tables |
|
||||||
|
| `\*` | Asterisk | Used for bold/italic |
|
||||||
|
| `\#` | Hash | Used for headings |
|
||||||
|
|
||||||
|
## Table with Headers Only
|
||||||
|
|
||||||
|
This table contains only header rows with no data rows. It tests edge case handling for tables without content, ensuring that the header styling is still applied correctly even when there's no body content.
|
||||||
|
|
||||||
|
| Header 1 | Header 2 | Header 3 |
|
||||||
|
| -------- | -------- | -------- |
|
||||||
|
|
||||||
|
## Single Column Table
|
||||||
|
|
||||||
|
This is a minimal table with only one column. It tests how table styling handles narrow tables and ensures that single-column layouts are properly formatted with appropriate borders and spacing.
|
||||||
|
|
||||||
|
| Item |
|
||||||
|
| ---- |
|
||||||
|
| First |
|
||||||
|
| Second |
|
||||||
|
| Third |
|
||||||
|
|
||||||
|
## Table with Nested Formatting
|
||||||
|
|
||||||
|
This table demonstrates complex nested formatting combinations within cells, including bold and italic text together, code blocks containing links, and strikethrough text. It tests that multiple formatting types can coexist properly within table cells.
|
||||||
|
|
||||||
|
| Description | Example |
|
||||||
|
| ----------- | ------- |
|
||||||
|
| Bold and italic | ***Important*** |
|
||||||
|
| Code and link | `[Click here](https://example.com)` |
|
||||||
|
| Strikethrough | ~~Old price~~ |
|
||||||
|
|
||||||
Reference in New Issue
Block a user