awacke1 commited on
Commit
81120e5
·
verified ·
1 Parent(s): caabb57

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +275 -0
app.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import streamlit as st
4
+ import streamlit.components.v1 as components
5
+ from streamlit_javascript import st_javascript # pip install streamlit-javascript
6
+
7
+ SETTINGS_FILE = "settings.json"
8
+
9
+ # --- Helper functions for persistence ---
10
+
11
+ def load_settings():
12
+ if os.path.exists(SETTINGS_FILE):
13
+ with open(SETTINGS_FILE, "r") as f:
14
+ return json.load(f)
15
+ else:
16
+ # Create a default settings structure
17
+ default = {
18
+ "decks": [
19
+ {
20
+ "name": "Deck 1",
21
+ "columns": [
22
+ {
23
+ "id": 1,
24
+ "title": "Column 1",
25
+ "size": "medium",
26
+ "order": "latest",
27
+ "advanced_search": False
28
+ },
29
+ {
30
+ "id": 2,
31
+ "title": "Column 2",
32
+ "size": "medium",
33
+ "order": "top",
34
+ "advanced_search": False
35
+ },
36
+ {
37
+ "id": 3,
38
+ "title": "Column 3",
39
+ "size": "medium",
40
+ "order": "latest",
41
+ "advanced_search": False
42
+ }
43
+ ]
44
+ }
45
+ ]
46
+ }
47
+ save_settings(default)
48
+ return default
49
+
50
+ def save_settings(settings):
51
+ with open(SETTINGS_FILE, "w") as f:
52
+ json.dump(settings, f, indent=2)
53
+
54
+ def get_new_column_id(deck):
55
+ # Return a new id that is 1 higher than the max id in the deck’s columns
56
+ if deck["columns"]:
57
+ return max(col["id"] for col in deck["columns"]) + 1
58
+ else:
59
+ return 1
60
+
61
+ # --- Initialize session state ---
62
+ if "settings" not in st.session_state:
63
+ st.session_state.settings = load_settings()
64
+ if "current_deck_index" not in st.session_state:
65
+ st.session_state.current_deck_index = 0
66
+
67
+ def update_order(new_order):
68
+ # new_order is expected to be a list of column IDs (as numbers)
69
+ deck = st.session_state.settings["decks"][st.session_state.current_deck_index]
70
+ # Build a dictionary mapping id -> column
71
+ col_dict = {col["id"]: col for col in deck["columns"]}
72
+ # Reorder based on new_order list
73
+ new_cols = []
74
+ for cid in new_order:
75
+ # Make sure the id exists
76
+ if cid in col_dict:
77
+ new_cols.append(col_dict[cid])
78
+ deck["columns"] = new_cols
79
+ save_settings(st.session_state.settings)
80
+ st.success("Column order saved.")
81
+
82
+ def edit_column(deck_index, col_index, new_props):
83
+ st.session_state.settings["decks"][deck_index]["columns"][col_index].update(new_props)
84
+ save_settings(st.session_state.settings)
85
+
86
+ def delete_column(deck_index, col_index):
87
+ del st.session_state.settings["decks"][deck_index]["columns"][col_index]
88
+ save_settings(st.session_state.settings)
89
+
90
+ def add_column(deck_index, title):
91
+ deck = st.session_state.settings["decks"][deck_index]
92
+ new_id = get_new_column_id(deck)
93
+ new_col = {
94
+ "id": new_id,
95
+ "title": title,
96
+ "size": "medium",
97
+ "order": "latest",
98
+ "advanced_search": False
99
+ }
100
+ deck["columns"].append(new_col)
101
+ save_settings(st.session_state.settings)
102
+
103
+ # --- Sidebar: Deck selection and management ---
104
+ st.sidebar.title("Decks Sidebar")
105
+ decks = [deck["name"] for deck in st.session_state.settings["decks"]]
106
+ selected_deck_name = st.sidebar.selectbox("Select a Deck", decks, index=st.session_state.current_deck_index)
107
+ # Update current deck index based on selection
108
+ st.session_state.current_deck_index = decks.index(selected_deck_name)
109
+
110
+ # Allow adding a new deck
111
+ with st.sidebar.expander("Manage Decks"):
112
+ new_deck_name = st.text_input("New deck name", key="new_deck_name")
113
+ if st.button("Add Deck"):
114
+ new_deck = {"name": new_deck_name or f"Deck {len(decks)+1}", "columns": []}
115
+ st.session_state.settings["decks"].append(new_deck)
116
+ save_settings(st.session_state.settings)
117
+ st.experimental_rerun()
118
+ st.markdown("[Change Display Settings](#)")
119
+ st.markdown("[Discover Keyboard Shortcuts](#)")
120
+ st.markdown("[Take the Tour Again](#)")
121
+
122
+ # --- Main area: Display columns with drag-and-drop ---
123
+ current_deck = st.session_state.settings["decks"][st.session_state.current_deck_index]
124
+ st.header(f"Columns in {current_deck['name']}")
125
+
126
+ # Build HTML for the draggable column list.
127
+ # Each li includes a data-id (the column id) and displays its title.
128
+ # On drag end, we update a global JS variable window.currentOrder.
129
+ html_code = f"""
130
+ <!DOCTYPE html>
131
+ <html>
132
+ <head>
133
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>
134
+ <style>
135
+ body {{
136
+ font-family: Arial, sans-serif;
137
+ margin: 0;
138
+ padding: 10px;
139
+ background-color: #fff;
140
+ }}
141
+ .column-list {{
142
+ list-style: none;
143
+ padding: 0;
144
+ }}
145
+ .column-item {{
146
+ padding: 10px;
147
+ margin: 5px 0;
148
+ background-color: #f0f0f0;
149
+ border: 1px solid #ccc;
150
+ cursor: grab;
151
+ position: relative;
152
+ }}
153
+ .drag-handle {{
154
+ position: absolute;
155
+ right: 10px;
156
+ top: 10px;
157
+ font-size: 16px;
158
+ cursor: grab;
159
+ }}
160
+ .context-menu {{
161
+ display: none;
162
+ position: absolute;
163
+ left: 10px;
164
+ top: 40px;
165
+ background: white;
166
+ border: 1px solid #ccc;
167
+ padding: 5px;
168
+ z-index: 100;
169
+ }}
170
+ .column-item:hover .context-menu {{
171
+ display: block;
172
+ }}
173
+ .context-menu button {{
174
+ display: block;
175
+ width: 100%;
176
+ margin: 2px 0;
177
+ padding: 4px 8px;
178
+ font-size: 12px;
179
+ cursor: pointer;
180
+ }}
181
+ </style>
182
+ </head>
183
+ <body>
184
+ <ul id="columnList" class="column-list">
185
+ {"".join([f'''
186
+ <li class="column-item" data-id="{col["id"]}">
187
+ {col["title"]}
188
+ <span class="drag-handle">&#9776;</span>
189
+ <div class="context-menu">
190
+ <button onclick="alert('Change Column Size for {col["title"]}')">Change Column Size</button>
191
+ <button onclick="alert('Delete Column {col["title"]}')">Delete</button>
192
+ <button onclick="alert('Order by Top for {col["title"]}')">Order: Top</button>
193
+ <button onclick="alert('Order by Latest for {col["title"]}')">Order: Latest</button>
194
+ <button onclick="alert('Advanced Search Tools for {col["title"]}')">Advanced Search</button>
195
+ </div>
196
+ </li>
197
+ ''' for col in current_deck["columns"]])}
198
+ </ul>
199
+ <script>
200
+ // Initialize a global variable to capture order
201
+ window.currentOrder = [];
202
+ var el = document.getElementById('columnList');
203
+ var sortable = Sortable.create(el, {{
204
+ animation: 150,
205
+ onEnd: function (evt) {{
206
+ var order = Array.from(el.children).map(function(li) {{
207
+ return parseInt(li.getAttribute('data-id'));
208
+ }});
209
+ window.currentOrder = order;
210
+ console.log('New order:', order);
211
+ }}
212
+ }});
213
+ // Set initial order on load
214
+ window.currentOrder = Array.from(el.children).map(function(li) {{
215
+ return parseInt(li.getAttribute('data-id'));
216
+ }});
217
+ </script>
218
+ </body>
219
+ </html>
220
+ """
221
+
222
+ # Embed the HTML; adjust height if needed
223
+ components.html(html_code, height=400, scrolling=True)
224
+
225
+ # Button to save drag-and-drop order
226
+ if st.button("Save Order"):
227
+ # Use streamlit_javascript to retrieve the JS variable "window.currentOrder"
228
+ new_order = st_javascript("return window.currentOrder;")
229
+ if new_order:
230
+ update_order(new_order)
231
+ else:
232
+ st.error("Could not retrieve order. Make sure you have dragged at least once.")
233
+
234
+ st.markdown("---")
235
+
236
+ # --- Column Editing Section ---
237
+ st.subheader("Edit Columns")
238
+
239
+ # For each column in the current deck, allow editing
240
+ for idx, col in enumerate(current_deck["columns"]):
241
+ with st.expander(f"Edit Column {col['title']} (ID: {col['id']})", expanded=False):
242
+ new_title = st.text_input("Title", value=col["title"], key=f"title_{col['id']}")
243
+ new_size = st.selectbox("Size", options=["small", "medium", "large"], index=["small", "medium", "large"].index(col["size"]), key=f"size_{col['id']}")
244
+ new_order_type = st.selectbox("Order Posts", options=["top", "latest"], index=["top", "latest"].index(col["order"]), key=f"order_{col['id']}")
245
+ new_adv = st.checkbox("Enable Advanced Search", value=col["advanced_search"], key=f"adv_{col['id']}")
246
+ col_cols = st.columns([1,1])
247
+ if col_cols[0].button("Save Changes", key=f"save_{col['id']}"):
248
+ edit_column(st.session_state.current_deck_index, idx, {
249
+ "title": new_title,
250
+ "size": new_size,
251
+ "order": new_order_type,
252
+ "advanced_search": new_adv
253
+ })
254
+ st.success("Column updated.")
255
+ st.experimental_rerun()
256
+ if col_cols[1].button("Delete Column", key=f"delete_{col['id']}"):
257
+ delete_column(st.session_state.current_deck_index, idx)
258
+ st.warning("Column deleted.")
259
+ st.experimental_rerun()
260
+
261
+ st.markdown("---")
262
+
263
+ # --- Add New Column Section ---
264
+ st.subheader("Add New Column")
265
+ new_col_title = st.text_input("New Column Title", key="new_col_title")
266
+ if st.button("Add Column"):
267
+ if new_col_title.strip():
268
+ add_column(st.session_state.current_deck_index, new_col_title.strip())
269
+ st.success(f"Added column '{new_col_title.strip()}'.")
270
+ st.experimental_rerun()
271
+ else:
272
+ st.error("Please enter a valid column title.")
273
+
274
+ # Optionally, display current settings (for debugging)
275
+ # st.json(st.session_state.settings)