Spaces:
Sleeping
Sleeping
import streamlit as st | |
from streamlit.components.v1 import html | |
from pathlib import Path | |
import json | |
import time | |
from datetime import datetime | |
import re | |
import pandas as pd | |
import yaml | |
from io import StringIO | |
import openpyxl | |
import csv | |
import base64 | |
# Supported file types and their handlers | |
FILE_TYPES = { | |
"md": "π Markdown", | |
"txt": "π Text", | |
"json": "π§ JSON", | |
"csv": "π CSV", | |
"xlsx": "π Excel", | |
"yaml": "βοΈ YAML", | |
"xml": "π XML" | |
} | |
# Enhanced state initialization | |
if 'file_data' not in st.session_state: | |
st.session_state.file_data = {} | |
if 'file_types' not in st.session_state: | |
st.session_state.file_types = {} | |
if 'md_outline' not in st.session_state: | |
st.session_state.md_outline = {} | |
if 'rendered_content' not in st.session_state: | |
st.session_state.rendered_content = {} | |
if 'file_history' not in st.session_state: | |
st.session_state.file_history = [] | |
if 'md_versions' not in st.session_state: | |
st.session_state.md_versions = {} | |
if 'md_files_history' not in st.session_state: | |
st.session_state.md_files_history = [] | |
def get_binary_file_downloader_html(bin_file, file_label='File'): | |
"""Generate a link allowing the data in a given file to be downloaded""" | |
b64 = base64.b64encode(bin_file.encode()).decode() | |
return f'<a href="data:text/plain;base64,{b64}" download="{file_label}">π₯ Download {file_label}</a>' | |
def encode_content(content): | |
"""Encode content to base64""" | |
return base64.b64encode(content.encode()).decode() | |
def decode_content(encoded_content): | |
"""Decode content from base64""" | |
return base64.b64decode(encoded_content.encode()).decode() | |
def add_to_history(filename, content, action="uploaded"): | |
"""Add a file action to the history with timestamp""" | |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
encoded_content = encode_content(content) | |
history_entry = { | |
"timestamp": timestamp, | |
"filename": filename, | |
"action": action, | |
"content": encoded_content | |
} | |
st.session_state.file_history.insert(0, history_entry) | |
# Store version if it's a markdown file | |
if filename.endswith('.md'): | |
if filename not in st.session_state.md_versions: | |
st.session_state.md_versions[filename] = [] | |
st.session_state.md_versions[filename].append({ | |
"timestamp": timestamp, | |
"content": encoded_content, | |
"action": action | |
}) | |
# Add to MD files history if not already present | |
if filename not in [f["filename"] for f in st.session_state.md_files_history]: | |
st.session_state.md_files_history.append({ | |
"filename": filename, | |
"timestamp": timestamp, | |
"content": encoded_content | |
}) | |
def show_sidebar_history(): | |
"""Display markdown file history in the sidebar""" | |
st.sidebar.markdown("### π Markdown Files History") | |
for md_file in st.session_state.md_files_history: | |
col1, col2, col3 = st.sidebar.columns([2, 1, 1]) | |
with col1: | |
st.markdown(f"**{md_file['filename']}**") | |
with col2: | |
if st.button("π Open", key=f"open_{md_file['filename']}"): | |
content = decode_content(md_file['content']) | |
st.session_state.file_data[md_file['filename']] = content | |
st.session_state.file_types[md_file['filename']] = "md" | |
st.session_state.md_outline[md_file['filename']] = parse_markdown_outline(content) | |
st.experimental_rerun() | |
with col3: | |
download_link = get_binary_file_downloader_html( | |
decode_content(md_file['content']), | |
md_file['filename'] | |
) | |
st.markdown(download_link, unsafe_allow_html=True) | |
def show_file_history(): | |
"""Display the file history in a collapsible section""" | |
if st.session_state.file_history: | |
with st.expander("π File History", expanded=False): | |
st.markdown("### Recent File Activities") | |
for entry in st.session_state.file_history: | |
col1, col2 = st.columns([2, 3]) | |
with col1: | |
st.markdown(f"**{entry['timestamp']}**") | |
with col2: | |
st.markdown(f"{entry['action'].title()}: {entry['filename']}") | |
def show_markdown_versions(filename): | |
"""Display previous versions of a markdown file""" | |
if filename in st.session_state.md_versions: | |
versions = st.session_state.md_versions[filename] | |
if len(versions) > 1: | |
st.markdown("### π Previous Versions") | |
version_idx = st.selectbox( | |
"Select version to view", | |
range(len(versions)-1), | |
format_func=lambda x: f"Version {len(versions)-1-x} - {versions[x]['timestamp']}" | |
) | |
if version_idx is not None: | |
version = versions[version_idx] | |
decoded_content = decode_content(version['content']) | |
st.text_area( | |
"Content", | |
decoded_content, | |
height=200, | |
key=f"version_{filename}_{version_idx}", | |
disabled=True | |
) | |
if st.button(f"Restore to this version", key=f"restore_{filename}_{version_idx}"): | |
st.session_state.file_data[filename] = decoded_content | |
st.session_state.md_outline[filename] = parse_markdown_outline(decoded_content) | |
add_to_history(filename, decoded_content, "restored") | |
st.experimental_rerun() | |
def parse_markdown_outline(content): | |
"""Generate an outline from markdown content""" | |
lines = content.split('\n') | |
outline = [] | |
for line in lines: | |
if line.strip().startswith('#'): | |
level = len(line.split()[0]) | |
title = line.strip('#').strip() | |
outline.append({ | |
'level': level, | |
'title': title, | |
'indent': ' ' * (level - 1) | |
}) | |
return outline | |
def create_markdown_tabs(content, filename): | |
"""Create tabs for markdown content viewing and editing""" | |
tab1, tab2, tab3 = st.tabs(["π Editor", "π Preview", "π History"]) | |
with tab1: | |
edited_content = st.text_area( | |
"Edit your markdown", | |
content, | |
height=300, | |
key=f"edit_{filename}" | |
) | |
if edited_content != content: | |
st.session_state.file_data[filename] = edited_content | |
st.session_state.md_outline[filename] = parse_markdown_outline(edited_content) | |
add_to_history(filename, edited_content, "edited") | |
content = edited_content | |
with tab2: | |
st.markdown("### Preview") | |
st.markdown(content) | |
if filename in st.session_state.md_outline: | |
st.markdown("---") | |
st.markdown("### π Document Outline") | |
for item in st.session_state.md_outline[filename]: | |
st.markdown(f"{item['indent']}β’ {item['title']}") | |
with tab3: | |
show_markdown_versions(filename) | |
return content | |
def read_file_content(uploaded_file): | |
"""Smart file reader with enhanced markdown handling""" | |
file_type = uploaded_file.name.split('.')[-1].lower() | |
try: | |
if file_type == 'md': | |
content = uploaded_file.getvalue().decode() | |
st.session_state.md_outline[uploaded_file.name] = parse_markdown_outline(content) | |
st.session_state.rendered_content[uploaded_file.name] = content | |
add_to_history(uploaded_file.name, content) | |
return content, "md" | |
elif file_type == 'csv': | |
df = pd.read_csv(uploaded_file) | |
return df.to_string(), "csv" | |
elif file_type == 'xlsx': | |
df = pd.read_excel(uploaded_file) | |
return df.to_string(), "xlsx" | |
elif file_type == 'json': | |
content = json.load(uploaded_file) | |
return json.dumps(content, indent=2), "json" | |
elif file_type == 'yaml': | |
content = yaml.safe_load(uploaded_file) | |
return yaml.dump(content), "yaml" | |
else: # Default text handling | |
return uploaded_file.getvalue().decode(), "txt" | |
except Exception as e: | |
st.error(f"π¨ Oops! Error reading {uploaded_file.name}: {str(e)}") | |
return None, None | |
def save_file_content(content, filename, file_type): | |
"""Smart file saver with enhanced markdown handling""" | |
try: | |
if file_type == "md": | |
with open(filename, 'w') as f: | |
f.write(content) | |
st.session_state.rendered_content[filename] = content | |
add_to_history(filename, content, "saved") | |
elif file_type in ["csv", "xlsx"]: | |
df = pd.read_csv(StringIO(content)) if file_type == "csv" else pd.read_excel(StringIO(content)) | |
if file_type == "csv": | |
df.to_csv(filename, index=False) | |
else: | |
df.to_excel(filename, index=False) | |
elif file_type == "json": | |
with open(filename, 'w') as f: | |
json.dump(json.loads(content), f, indent=2) | |
elif file_type == "yaml": | |
with open(filename, 'w') as f: | |
yaml.dump(yaml.safe_load(content), f) | |
else: # Default text handling | |
with open(filename, 'w') as f: | |
f.write(content) | |
return True | |
except Exception as e: | |
st.error(f"π¨ Error saving {filename}: {str(e)}") | |
return False | |
def main(): | |
st.title("πβ¨ Super Smart File Handler with Markdown Magic! β¨π") | |
# Show markdown history in sidebar | |
show_sidebar_history() | |
uploaded_file = st.file_uploader( | |
"π€ Upload your file!", | |
type=list(FILE_TYPES.keys()), | |
help="Supports: " + ", ".join([f"{v} (.{k})" for k, v in FILE_TYPES.items()]) | |
) | |
# Show file history at the top | |
show_file_history() | |
if uploaded_file: | |
content, file_type = read_file_content(uploaded_file) | |
if content is not None: | |
st.session_state.file_data[uploaded_file.name] = content | |
st.session_state.file_types[uploaded_file.name] = file_type | |
st.success(f"π Loaded {FILE_TYPES.get(file_type, 'π')} file: {uploaded_file.name}") | |
if st.session_state.file_data: | |
st.subheader("π Your Files") | |
for filename, content in st.session_state.file_data.items(): | |
file_type = st.session_state.file_types[filename] | |
with st.expander(f"{FILE_TYPES.get(file_type, 'π')} {filename}"): | |
if file_type == "md": | |
content = create_markdown_tabs(content, filename) | |
else: | |
edited_content = st.text_area( | |
"Content", | |
content, | |
height=300, | |
key=f"edit_{filename}" | |
) | |
if edited_content != content: | |
st.session_state.file_data[filename] = edited_content | |
content = edited_content | |
if st.button(f"πΎ Save {filename}"): | |
if save_file_content(content, filename, file_type): | |
st.success(f"β¨ Saved {filename} successfully!") | |
if __name__ == "__main__": | |
main() |