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""" """ # 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)