Last active
February 25, 2026 18:29
-
-
Save kristovatlas/3760bbf9258ec825de85488b4f040b91 to your computer and use it in GitHub Desktop.
stuff to make claude code terminal output suitable for other formats
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import sys | |
| def is_separator(line): | |
| """Check if a line is a horizontal separator (e.g. ├───┼───┤, ╞═══╪═══╡, etc.)""" | |
| stripped = line.strip() | |
| # Separator lines are made up of box-drawing border characters and have no letter/digit content | |
| border_chars = set('─━═┼╪╫┬┴├┤┌┐└┘╔╗╚╝╠╣╦╩╬╞╡╥╨╤╧╟╢+|-=') | |
| return bool(stripped) and all(c in border_chars or c.isspace() for c in stripped) | |
| def parse_columns(line): | |
| """Split a │-delimited line into cell values.""" | |
| parts = line.split('│') | |
| if len(parts) >= 3: | |
| return [part.strip() for part in parts[1:-1]] | |
| return None | |
| def main(): | |
| if len(sys.argv) != 2: | |
| print("Usage: python script.py <input_file>") | |
| sys.exit(1) | |
| filename = sys.argv[1] | |
| with open(filename, 'r') as f: | |
| lines = f.read().splitlines() | |
| # Group lines between separators into logical rows | |
| logical_rows = [] | |
| current_group = [] | |
| for line in lines: | |
| if is_separator(line): | |
| if current_group: | |
| logical_rows.append(current_group) | |
| current_group = [] | |
| else: | |
| stripped = line.strip() | |
| if stripped.startswith('│') and '│' in stripped[1:]: | |
| current_group.append(line) | |
| # Don't forget the last group if the table doesn't end with a separator | |
| if current_group: | |
| logical_rows.append(current_group) | |
| if not logical_rows: | |
| print("No table found in the input file.") | |
| sys.exit(1) | |
| # Merge multi-line groups into single rows, joining cell content with spaces | |
| rows = [] | |
| for group in logical_rows: | |
| merged = None | |
| for line in group: | |
| cols = parse_columns(line) | |
| if cols is None: | |
| continue | |
| if merged is None: | |
| merged = cols | |
| else: | |
| # Pad to same length if needed | |
| while len(merged) < len(cols): | |
| merged.append('') | |
| while len(cols) < len(merged): | |
| cols.append('') | |
| # Append each cell's continuation text | |
| for i in range(len(merged)): | |
| if cols[i]: | |
| merged[i] = (merged[i] + ' ' + cols[i]).strip() | |
| if merged and any(merged): | |
| rows.append(merged) | |
| if not rows: | |
| print("No table found in the input file.") | |
| sys.exit(1) | |
| # First row is headers, rest are data | |
| headers = rows[0] | |
| data_rows = rows[1:] | |
| # Escape any pipes inside cell content so they don't break Markdown | |
| def escape_cell(cell): | |
| return cell.replace('|', '\\|') | |
| md_table = '| ' + ' | '.join(escape_cell(h) for h in headers) + ' |\n' | |
| md_table += '| ' + ' | '.join(['---'] * len(headers)) + ' |\n' | |
| for row in data_rows: | |
| # Pad short rows | |
| while len(row) < len(headers): | |
| row.append('') | |
| md_table += '| ' + ' | '.join(escape_cell(c) for c in row) + ' |\n' | |
| print(md_table) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Blank Line & Whitespace Remover</title> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| line-height: 1.6; | |
| } | |
| .container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| background: white; | |
| padding: 30px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| } | |
| h1 { | |
| color: #333; | |
| margin-bottom: 10px; | |
| } | |
| .description { | |
| color: #666; | |
| margin-bottom: 25px; | |
| font-size: 14px; | |
| } | |
| .section { | |
| margin-bottom: 25px; | |
| } | |
| label { | |
| display: block; | |
| font-weight: 600; | |
| margin-bottom: 8px; | |
| color: #444; | |
| } | |
| textarea { | |
| width: 100%; | |
| min-height: 250px; | |
| padding: 15px; | |
| border: 2px solid #ddd; | |
| border-radius: 6px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 14px; | |
| resize: vertical; | |
| transition: border-color 0.3s; | |
| } | |
| textarea:focus { | |
| outline: none; | |
| border-color: #4CAF50; | |
| } | |
| .button-group { | |
| display: flex; | |
| gap: 10px; | |
| margin-bottom: 25px; | |
| } | |
| button { | |
| padding: 12px 24px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| border: none; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .process-btn { | |
| background-color: #4CAF50; | |
| color: white; | |
| } | |
| .process-btn:hover { | |
| background-color: #45a049; | |
| } | |
| .copy-btn { | |
| background-color: #2196F3; | |
| color: white; | |
| } | |
| .copy-btn:hover { | |
| background-color: #0b7dda; | |
| } | |
| .clear-btn { | |
| background-color: #f44336; | |
| color: white; | |
| } | |
| .clear-btn:hover { | |
| background-color: #da190b; | |
| } | |
| .stats { | |
| background-color: #f9f9f9; | |
| padding: 15px; | |
| border-radius: 6px; | |
| margin-bottom: 25px; | |
| display: flex; | |
| gap: 30px; | |
| font-size: 14px; | |
| } | |
| .stat-item { | |
| color: #666; | |
| } | |
| .stat-item strong { | |
| color: #333; | |
| } | |
| .success-message { | |
| display: none; | |
| background-color: #4CAF50; | |
| color: white; | |
| padding: 12px; | |
| border-radius: 6px; | |
| margin-bottom: 20px; | |
| text-align: center; | |
| } | |
| .success-message.show { | |
| display: block; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Blank Line & Whitespace Remover</h1> | |
| <p class="description">Paste your text below to remove all blank lines and trailing whitespace from each line.</p> | |
| <div id="successMessage" class="success-message">Copied to clipboard!</div> | |
| <div class="section"> | |
| <label for="inputText">Input Text:</label> | |
| <textarea id="inputText" placeholder="Paste your text here..."></textarea> | |
| </div> | |
| <div class="button-group"> | |
| <button class="process-btn" onclick="processText()">Process Text</button> | |
| <button class="copy-btn" onclick="copyOutput()">Copy Output</button> | |
| <button class="clear-btn" onclick="clearAll()">Clear All</button> | |
| </div> | |
| <div class="stats" id="stats" style="display: none;"> | |
| <div class="stat-item"> | |
| Original length: <strong id="originalLength">0</strong> characters | |
| </div> | |
| <div class="stat-item"> | |
| Processed length: <strong id="processedLength">0</strong> characters | |
| </div> | |
| <div class="stat-item"> | |
| Removed: <strong id="removedChars">0</strong> characters | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <label for="outputText">Output Text:</label> | |
| <textarea id="outputText" placeholder="Processed text will appear here..." readonly></textarea> | |
| </div> | |
| </div> | |
| <script> | |
| function processText() { | |
| const input = document.getElementById('inputText').value; | |
| if (!input.trim()) { | |
| alert('Please paste some text first!'); | |
| return; | |
| } | |
| // Split by newlines, trim trailing whitespace from each line, | |
| // filter out blank lines, then join with newlines | |
| const processed = input | |
| .split('\n') | |
| .map(line => line.trimEnd()) | |
| .filter(line => line.length > 0) | |
| .join('\n'); | |
| document.getElementById('outputText').value = processed; | |
| // Update statistics | |
| const originalLength = input.length; | |
| const processedLength = processed.length; | |
| const removedChars = originalLength - processedLength; | |
| document.getElementById('originalLength').textContent = originalLength; | |
| document.getElementById('processedLength').textContent = processedLength; | |
| document.getElementById('removedChars').textContent = removedChars; | |
| document.getElementById('stats').style.display = 'flex'; | |
| } | |
| function copyOutput() { | |
| const output = document.getElementById('outputText'); | |
| if (!output.value) { | |
| alert('No output to copy! Process some text first.'); | |
| return; | |
| } | |
| output.select(); | |
| document.execCommand('copy'); | |
| // Show success message | |
| const successMsg = document.getElementById('successMessage'); | |
| successMsg.classList.add('show'); | |
| setTimeout(() => { | |
| successMsg.classList.remove('show'); | |
| }, 2000); | |
| } | |
| function clearAll() { | |
| document.getElementById('inputText').value = ''; | |
| document.getElementById('outputText').value = ''; | |
| document.getElementById('stats').style.display = 'none'; | |
| } | |
| // Auto-focus on input textarea when page loads | |
| window.onload = function() { | |
| document.getElementById('inputText').focus(); | |
| }; | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment