Spaces:
Running
Running
File size: 5,603 Bytes
5e99d47 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
import tkinter as tk
from tkinter import scrolledtext
from tkinterdnd2 import DND_FILES, TkinterDnD
import re
import os
# --- Conversion Logic ---
def convert_md_to_smf(markdown_text):
"""
Converts a Markdown string to an SMF-compatible BBCode string.
The order of replacements is important to avoid conflicts.
"""
smf_text = markdown_text
# 1. Block elements (Code blocks, blockquotes)
# Code blocks (```...```) - DOTALL flag allows `.` to match newlines
smf_text = re.sub(r'```(.*?)```', r'[code]\1[/code]', smf_text, flags=re.DOTALL)
# Blockquotes (>)
smf_text = re.sub(r'^\> (.*)', r'[quote]\1[/quote]', smf_text, flags=re.MULTILINE)
# 2. Headings (must be at the start of a line)
smf_text = re.sub(r'^# (.*)', r'[size=18pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^## (.*)', r'[size=16pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^#### (.*)', r'[size=14pt][b]\1[/b][/size]', smf_text, flags=re.MULTILINE)
# 3. Lists (must be at the start of a line)
# This is a simplified conversion. It doesn't wrap the whole block in [list] tags,
# as that requires more complex state management, but it's often sufficient.
smf_text = re.sub(r'^\* (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^- (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE)
smf_text = re.sub(r'^\d+\. (.*)', r'[li]\1[/li]', smf_text, flags=re.MULTILINE) # Note: SMF may need [list type=decimal] wrapper
# 4. Images and Links
smf_text = re.sub(r'!\[.*?\]\((.*?)\)', r'[img]\1[/img]', smf_text) # Images
smf_text = re.sub(r'\[(.*?)\]\((.*?)\)', r'[url=\2]\1[/url]', smf_text) # Links
# 5. Inline formatting
smf_text = re.sub(r'\*\*(.*?)\*\*', r'[b]\1[/b]', smf_text) # Bold (**)
smf_text = re.sub(r'__(.*?)__', r'[b]\1[/b]', smf_text) # Bold (__)
smf_text = re.sub(r'\*(.*?)\*', r'[i]\1[/i]', smf_text) # Italic (*)
smf_text = re.sub(r'_(.*?)_', r'[i]\1[/i]', smf_text) # Italic (_)
smf_text = re.sub(r'~~(.*?)~~', r'[s]\1[/s]', smf_text) # Strikethrough
smf_text = re.sub(r'`(.*?)`', r'[code]\1[/code]', smf_text) # Inline code
# 6. Horizontal Rule
smf_text = re.sub(r'^\s*---\s*$', '\n[hr]\n', smf_text, flags=re.MULTILINE)
return smf_text
# --- GUI Application ---
class App(TkinterDnD.Tk):
def __init__(self):
super().__init__()
self.title("Markdown to SMF Converter")
self.geometry("500x400")
# Main frame
main_frame = tk.Frame(self)
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# Label for instructions
self.drop_label = tk.Label(
main_frame,
text="Drag and Drop .md files here",
font=("Arial", 14),
bg="lightblue",
relief="solid",
bd=2
)
self.drop_label.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
# Scrolled text for status logs
self.status_log = scrolledtext.ScrolledText(
main_frame,
height=10,
state='disabled' # Make it read-only
)
self.status_log.pack(fill=tk.BOTH, expand=True)
# Register the drop zone
self.drop_target_register(DND_FILES)
self.dnd_bind('<<Drop>>', self.handle_drop)
self.log("Ready to convert files.")
def log(self, message):
"""Adds a message to the status log."""
self.status_log.config(state='normal')
self.status_log.insert(tk.END, message + "\n")
self.status_log.see(tk.END) # Auto-scroll to the bottom
self.status_log.config(state='disabled')
def handle_drop(self, event):
"""Handles the file drop event."""
# The event.data is a string of file paths, sometimes in curly braces
# We clean it up and split it into a list of individual paths
file_paths = self.tk.splitlist(event.data)
self.log("\n--- New Drop Detected ---")
for file_path in file_paths:
if file_path.lower().endswith('.md'):
self.process_file(file_path)
else:
self.log(f"Skipped: '{os.path.basename(file_path)}' is not a .md file.")
self.log("--- All files processed ---")
def process_file(self, file_path):
"""Reads, converts, and saves a single markdown file."""
filename = os.path.basename(file_path)
self.log(f"Processing: '{filename}'...")
try:
# Read the markdown file
with open(file_path, 'r', encoding='utf-8') as f:
markdown_content = f.read()
# Convert the content
smf_content = convert_md_to_smf(markdown_content)
# Define the output path
base_name = os.path.splitext(file_path)[0]
output_path = f"{base_name}_smf.txt"
# Save the converted content
with open(output_path, 'w', encoding='utf-8') as f:
f.write(smf_content)
self.log(f"Success! Saved to: '{os.path.basename(output_path)}'")
except Exception as e:
self.log(f"Error processing '{filename}': {e}")
# --- Main Execution ---
if __name__ == "__main__":
app = App()
app.mainloop() |