|
import os |
|
import json |
|
import streamlit as st |
|
import streamlit.components.v1 as components |
|
from streamlit_javascript import st_javascript |
|
|
|
SETTINGS_FILE = "settings.json" |
|
|
|
|
|
|
|
def load_settings(): |
|
if os.path.exists(SETTINGS_FILE): |
|
with open(SETTINGS_FILE, "r") as f: |
|
return json.load(f) |
|
else: |
|
|
|
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): |
|
|
|
if deck["columns"]: |
|
return max(col["id"] for col in deck["columns"]) + 1 |
|
else: |
|
return 1 |
|
|
|
|
|
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): |
|
|
|
deck = st.session_state.settings["decks"][st.session_state.current_deck_index] |
|
|
|
col_dict = {col["id"]: col for col in deck["columns"]} |
|
|
|
new_cols = [] |
|
for cid in new_order: |
|
|
|
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) |
|
|
|
|
|
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) |
|
|
|
st.session_state.current_deck_index = decks.index(selected_deck_name) |
|
|
|
|
|
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](#)") |
|
|
|
|
|
current_deck = st.session_state.settings["decks"][st.session_state.current_deck_index] |
|
st.header(f"Columns in {current_deck['name']}") |
|
|
|
|
|
|
|
|
|
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">☰</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> |
|
""" |
|
|
|
|
|
components.html(html_code, height=400, scrolling=True) |
|
|
|
|
|
if st.button("Save Order"): |
|
|
|
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("---") |
|
|
|
|
|
st.subheader("Edit Columns") |
|
|
|
|
|
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("---") |
|
|
|
|
|
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.") |
|
|
|
|
|
|
|
|