awacke1's picture
Create app.py
81120e5 verified
raw
history blame
9.94 kB
import os
import json
import streamlit as st
import streamlit.components.v1 as components
from streamlit_javascript import st_javascript # pip install streamlit-javascript
SETTINGS_FILE = "settings.json"
# --- Helper functions for persistence ---
def load_settings():
if os.path.exists(SETTINGS_FILE):
with open(SETTINGS_FILE, "r") as f:
return json.load(f)
else:
# Create a default settings structure
default = {
"decks": [
{
"name": "Deck 1",
"columns": [
{
"id": 1,
"title": "Column 1",
"size": "medium",
"order": "latest",
"advanced_search": False
},
{
"id": 2,
"title": "Column 2",
"size": "medium",
"order": "top",
"advanced_search": False
},
{
"id": 3,
"title": "Column 3",
"size": "medium",
"order": "latest",
"advanced_search": False
}
]
}
]
}
save_settings(default)
return default
def save_settings(settings):
with open(SETTINGS_FILE, "w") as f:
json.dump(settings, f, indent=2)
def get_new_column_id(deck):
# Return a new id that is 1 higher than the max id in the deck’s columns
if deck["columns"]:
return max(col["id"] for col in deck["columns"]) + 1
else:
return 1
# --- Initialize session state ---
if "settings" not in st.session_state:
st.session_state.settings = load_settings()
if "current_deck_index" not in st.session_state:
st.session_state.current_deck_index = 0
def update_order(new_order):
# new_order is expected to be a list of column IDs (as numbers)
deck = st.session_state.settings["decks"][st.session_state.current_deck_index]
# Build a dictionary mapping id -> column
col_dict = {col["id"]: col for col in deck["columns"]}
# Reorder based on new_order list
new_cols = []
for cid in new_order:
# Make sure the id exists
if cid in col_dict:
new_cols.append(col_dict[cid])
deck["columns"] = new_cols
save_settings(st.session_state.settings)
st.success("Column order saved.")
def edit_column(deck_index, col_index, new_props):
st.session_state.settings["decks"][deck_index]["columns"][col_index].update(new_props)
save_settings(st.session_state.settings)
def delete_column(deck_index, col_index):
del st.session_state.settings["decks"][deck_index]["columns"][col_index]
save_settings(st.session_state.settings)
def add_column(deck_index, title):
deck = st.session_state.settings["decks"][deck_index]
new_id = get_new_column_id(deck)
new_col = {
"id": new_id,
"title": title,
"size": "medium",
"order": "latest",
"advanced_search": False
}
deck["columns"].append(new_col)
save_settings(st.session_state.settings)
# --- Sidebar: Deck selection and management ---
st.sidebar.title("Decks Sidebar")
decks = [deck["name"] for deck in st.session_state.settings["decks"]]
selected_deck_name = st.sidebar.selectbox("Select a Deck", decks, index=st.session_state.current_deck_index)
# Update current deck index based on selection
st.session_state.current_deck_index = decks.index(selected_deck_name)
# Allow adding a new deck
with st.sidebar.expander("Manage Decks"):
new_deck_name = st.text_input("New deck name", key="new_deck_name")
if st.button("Add Deck"):
new_deck = {"name": new_deck_name or f"Deck {len(decks)+1}", "columns": []}
st.session_state.settings["decks"].append(new_deck)
save_settings(st.session_state.settings)
st.experimental_rerun()
st.markdown("[Change Display Settings](#)")
st.markdown("[Discover Keyboard Shortcuts](#)")
st.markdown("[Take the Tour Again](#)")
# --- Main area: Display columns with drag-and-drop ---
current_deck = st.session_state.settings["decks"][st.session_state.current_deck_index]
st.header(f"Columns in {current_deck['name']}")
# Build HTML for the draggable column list.
# Each li includes a data-id (the column id) and displays its title.
# On drag end, we update a global JS variable window.currentOrder.
html_code = f"""
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
<style>
body {{
font-family: Arial, sans-serif;
margin: 0;
padding: 10px;
background-color: #fff;
}}
.column-list {{
list-style: none;
padding: 0;
}}
.column-item {{
padding: 10px;
margin: 5px 0;
background-color: #f0f0f0;
border: 1px solid #ccc;
cursor: grab;
position: relative;
}}
.drag-handle {{
position: absolute;
right: 10px;
top: 10px;
font-size: 16px;
cursor: grab;
}}
.context-menu {{
display: none;
position: absolute;
left: 10px;
top: 40px;
background: white;
border: 1px solid #ccc;
padding: 5px;
z-index: 100;
}}
.column-item:hover .context-menu {{
display: block;
}}
.context-menu button {{
display: block;
width: 100%;
margin: 2px 0;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
}}
</style>
</head>
<body>
<ul id="columnList" class="column-list">
{"".join([f'''
<li class="column-item" data-id="{col["id"]}">
{col["title"]}
<span class="drag-handle">&#9776;</span>
<div class="context-menu">
<button onclick="alert('Change Column Size for {col["title"]}')">Change Column Size</button>
<button onclick="alert('Delete Column {col["title"]}')">Delete</button>
<button onclick="alert('Order by Top for {col["title"]}')">Order: Top</button>
<button onclick="alert('Order by Latest for {col["title"]}')">Order: Latest</button>
<button onclick="alert('Advanced Search Tools for {col["title"]}')">Advanced Search</button>
</div>
</li>
''' for col in current_deck["columns"]])}
</ul>
<script>
// Initialize a global variable to capture order
window.currentOrder = [];
var el = document.getElementById('columnList');
var sortable = Sortable.create(el, {{
animation: 150,
onEnd: function (evt) {{
var order = Array.from(el.children).map(function(li) {{
return parseInt(li.getAttribute('data-id'));
}});
window.currentOrder = order;
console.log('New order:', order);
}}
}});
// Set initial order on load
window.currentOrder = Array.from(el.children).map(function(li) {{
return parseInt(li.getAttribute('data-id'));
}});
</script>
</body>
</html>
"""
# Embed the HTML; adjust height if needed
components.html(html_code, height=400, scrolling=True)
# Button to save drag-and-drop order
if st.button("Save Order"):
# Use streamlit_javascript to retrieve the JS variable "window.currentOrder"
new_order = st_javascript("return window.currentOrder;")
if new_order:
update_order(new_order)
else:
st.error("Could not retrieve order. Make sure you have dragged at least once.")
st.markdown("---")
# --- Column Editing Section ---
st.subheader("Edit Columns")
# For each column in the current deck, allow editing
for idx, col in enumerate(current_deck["columns"]):
with st.expander(f"Edit Column {col['title']} (ID: {col['id']})", expanded=False):
new_title = st.text_input("Title", value=col["title"], key=f"title_{col['id']}")
new_size = st.selectbox("Size", options=["small", "medium", "large"], index=["small", "medium", "large"].index(col["size"]), key=f"size_{col['id']}")
new_order_type = st.selectbox("Order Posts", options=["top", "latest"], index=["top", "latest"].index(col["order"]), key=f"order_{col['id']}")
new_adv = st.checkbox("Enable Advanced Search", value=col["advanced_search"], key=f"adv_{col['id']}")
col_cols = st.columns([1,1])
if col_cols[0].button("Save Changes", key=f"save_{col['id']}"):
edit_column(st.session_state.current_deck_index, idx, {
"title": new_title,
"size": new_size,
"order": new_order_type,
"advanced_search": new_adv
})
st.success("Column updated.")
st.experimental_rerun()
if col_cols[1].button("Delete Column", key=f"delete_{col['id']}"):
delete_column(st.session_state.current_deck_index, idx)
st.warning("Column deleted.")
st.experimental_rerun()
st.markdown("---")
# --- Add New Column Section ---
st.subheader("Add New Column")
new_col_title = st.text_input("New Column Title", key="new_col_title")
if st.button("Add Column"):
if new_col_title.strip():
add_column(st.session_state.current_deck_index, new_col_title.strip())
st.success(f"Added column '{new_col_title.strip()}'.")
st.experimental_rerun()
else:
st.error("Please enter a valid column title.")
# Optionally, display current settings (for debugging)
# st.json(st.session_state.settings)