2727
2828HEADER_FONT_FAMILY = "'Super Carnival', sans-serif"
2929BOARD_TILE_FONT = "Inter" # Set the desired Google Font for board tiles
30- BOARD_TILE_FONT_WEIGHT = "600 " # Default weight for board tiles; adjust as needed.
30+ BOARD_TILE_FONT_WEIGHT = "700 " # Default weight for board tiles; adjust as needed.
3131BOARD_TILE_FONT_STYLE = "normal" # Default font style for board tiles; for example, "normal" or "italic"
3232
33+ # UI Class Constants
34+ BOARD_CONTAINER_CLASS = "flex justify-center items-center w-full"
35+ HEADER_CONTAINER_CLASS = "w-full"
36+ CARD_CLASSES = "relative p-2 rounded-xl w-full h-full flex items-center justify-center"
37+ COLUMN_CLASSES = "flex flex-col items-center justify-center gap-0 w-full"
38+ GRID_CONTAINER_CLASS = "w-full aspect-square p-4"
39+ GRID_CLASSES = "gap-2 h-full grid-rows-5"
40+ ROW_CLASSES = "w-full"
41+ LABEL_SMALL_CLASSES = "fit-text-small text-center select-none"
42+ LABEL_CLASSES = "fit-text text-center select-none"
43+
44+ # Global dictionary to store board view instances.
45+ # Keys can be "home" and "stream". Each value is a tuple: (container, tile_buttons).
46+ board_views = {}
47+
3348def get_line_style_for_lines (line_count : int , default_text_color : str ) -> str :
3449 """
3550 Return a complete style string with an adjusted line-height based on the number of lines
@@ -157,51 +172,9 @@ def segment_length(segment):
157172 # fallback (should never happen)
158173 return [" " .join (words )]
159174
160- # Function to create the Bingo board UI
161- def create_bingo_board ():
162- # The header and seed label are handled outside this function.
163- logging .info ("Creating Bingo board" )
164-
165- with ui .element ("div" ).classes ("flex justify-center items-center w-full" ):
166- with ui .element ("div" ).classes ("w-full max-w-3xl aspect-square" ):
167- with ui .grid (columns = 5 ).classes ("gap-2 h-full grid-rows-5" ):
168- for row_idx , row in enumerate (board ):
169- for col_idx , phrase in enumerate (row ):
170- # Create a clickable card for this cell with reduced padding and centered content. Added 'relative' class for icon overlay.
171- card = ui .card ().classes ("relative p-2 rounded-lg w-full h-full flex items-center justify-center" ).style ("cursor: pointer;" )
172- labels_list = [] # initialize list for storing label metadata
173- with card :
174- with ui .column ().classes ("flex flex-col items-center justify-center gap-0 w-full" ):
175- # Set text color: free meat gets #FF7f33, others black
176- default_text_color = FREE_SPACE_TEXT_COLOR if phrase .upper () == FREE_SPACE_TEXT else TILE_UNCLICKED_TEXT_COLOR
177- lines = split_phrase_into_lines (phrase )
178- line_count = len (lines )
179- for line in lines :
180- with ui .row ().classes ("w-full" ):
181- if len (line ) <= 3 :
182- base_class = "fit-text-small text-center select-none"
183- else :
184- base_class = "fit-text text-center select-none"
185- # Create the label with initial inline style using get_line_style_for_lines().
186- lbl = ui .label (line ).classes (base_class ).style (get_line_style_for_lines (line_count , default_text_color ))
187- # Instead of just storing the label, store its metadata.
188- labels_list .append ({
189- "ref" : lbl ,
190- "base_classes" : base_class ,
191- "base_style" : get_line_style_for_lines (line_count , default_text_color )
192- })
193-
194- tile_buttons [(row_idx , col_idx )] = card
195-
196- if phrase .upper () == FREE_SPACE_TEXT :
197- clicked_tiles .add ((row_idx , col_idx ))
198- card .style ("color: #FF7f33; border: none;" )
199- else :
200- card .on ("click" , lambda e , r = row_idx , c = col_idx : toggle_tile (r , c ))
201-
202175# Toggle tile click state (for example usage)
203176def toggle_tile (row , col ):
204- # Do not allow toggling for the FREE MEAT cell (center cell)
177+ # Do not allow toggling for the FREE SPACE cell (center cell)
205178 if (row , col ) == (2 , 2 ):
206179 return
207180 key = (row , col )
@@ -271,12 +244,15 @@ def create_board_view(background_color: str, is_global: bool):
271244 global home_board_container , tile_buttons
272245 home_board_container = container
273246 tile_buttons = {} # Start with an empty dictionary.
274- build_board (home_board_container , tile_buttons , toggle_tile )
247+ build_board (container , tile_buttons , toggle_tile )
248+ board_views ["home" ] = (container , tile_buttons )
275249 # Add timers for synchronizing the global board.
276250 ui .timer (0.1 , sync_board_state )
277251 ui .timer (1 , check_phrases_file_change )
278252 else :
279- local_tile_buttons = build_board (container , {}, toggle_tile )
253+ local_tile_buttons = {}
254+ build_board (container , local_tile_buttons , toggle_tile )
255+ board_views ["stream" ] = (container , local_tile_buttons )
280256 ui .timer (0.1 , lambda : update_tile_styles (local_tile_buttons ))
281257 # Display the seed beneath the board.
282258 with ui .element ("div" ).classes ("w-full mt-4" ):
@@ -294,7 +270,7 @@ def stream_page():
294270def admin_page ():
295271 def reset_board ():
296272 clicked_tiles .clear ()
297- # Re-add FREE MEAT at the center (position (2,2))
273+ # Re-add FREE SPACE at the center (position (2,2))
298274 clicked_tiles .add ((2 , 2 ))
299275 sync_board_state ()
300276 build_admin_panel () # rebuild panel to reflect state changes
@@ -333,9 +309,17 @@ def setup_head(background_color: str):
333309 Set up common head elements: fonts, fitty JS, and background color.
334310 """
335311 ui .add_head_html (f'<link href="https://fonts.cdnfonts.com/css/super-carnival" rel="stylesheet">' )
336- ui .add_head_html (f'<link href="https://fonts.googleapis.com/css2?family={ BOARD_TILE_FONT .replace (" " , "+" )} &display=swap" rel="stylesheet">' )
312+ ui .add_head_html ("""
313+ <link rel="preconnect" href="https://fonts.googleapis.com">
314+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
315+ <link href="https://fonts.googleapis.com/css2?family={BOARD_TILE_FONT.replace(" ", "+")}:opsz@10..1000&display=swap" rel="stylesheet">
316+ """ )
317+ # Add CSS class for board tile fonts; you can later reference this class in your CSS.
318+ ui .add_head_html (get_google_font_css (BOARD_TILE_FONT , BOARD_TILE_FONT_WEIGHT , BOARD_TILE_FONT_STYLE , "board_tile" ))
319+
337320 ui .add_head_html ('<script src="https://cdn.jsdelivr.net/npm/fitty@2.3.6/dist/fitty.min.js"></script>' )
338321 ui .add_head_html (f'<style>body {{ background-color: { background_color } ; }}</style>' )
322+
339323 ui .add_head_html ("""<script>
340324 document.addEventListener('DOMContentLoaded', () => {
341325 fitty('.fit-text', { multiLine: true, minSize: 10, maxSize: 1000 });
@@ -353,42 +337,48 @@ def setup_head(background_color: str):
353337 with ui .element ("div" ).classes ("w-full" ):
354338 ui .label ("COMMIT !BINGO" ).classes ("fit-header text-center" ).style (f"font-family: { HEADER_FONT_FAMILY } ; color: { HEADER_TEXT_COLOR } ;" )
355339
340+ def get_google_font_css (font_name : str , weight : str , style : str , uniquifier : str ) -> str :
341+ """
342+ Returns a CSS style block defining a class for the specified Google font.
343+ 'uniquifier' is used as the CSS class name.
344+ """
345+ return f"""
346+ <style>
347+ .{ uniquifier } {{
348+ font-family: "{ font_name } ", serif;
349+ font-optical-sizing: auto;
350+ font-weight: { weight } ;
351+ font-style: { style } ;
352+ }}
353+ </style>
354+ """
355+
356356def build_board (parent , tile_buttons_dict : dict , on_tile_click ):
357357 """
358358 Build the common Bingo board in the given parent element.
359359 The resulting tile UI elements are added to tile_buttons_dict.
360360 """
361361 with parent :
362- # Use full width and add padding so the board touches the edges with a gap
363- with ui .element ("div" ).classes ("w-full aspect-square p-4" ):
364- with ui .grid (columns = 5 ).classes ("gap-2 h-full grid-rows-5" ):
362+ with ui .element ("div" ).classes (GRID_CONTAINER_CLASS ):
363+ with ui .grid (columns = 5 ).classes (GRID_CLASSES ):
365364 for row_idx , row in enumerate (board ):
366365 for col_idx , phrase in enumerate (row ):
367- card = ui .card ().classes (
368- "relative p-2 rounded-lg w-full h-full flex items-center justify-center"
369- ).style ("cursor: pointer;" )
366+ card = ui .card ().classes (CARD_CLASSES ).style ("cursor: pointer;" )
370367 labels_list = [] # initialize list for storing label metadata
371368 with card :
372369 with ui .column ().classes ("flex flex-col items-center justify-center gap-0 w-full" ):
373- # Set text color: FREE MEAT uses FREE_MEAT_TEXT_COLOR, others use TILE_UNCLICKED_TEXT_COLOR
374370 default_text_color = FREE_SPACE_TEXT_COLOR if phrase .upper () == FREE_SPACE_TEXT else TILE_UNCLICKED_TEXT_COLOR
375371 lines = split_phrase_into_lines (phrase )
376372 line_count = len (lines )
377373 for line in lines :
378374 with ui .row ().classes ("w-full" ):
379- if len (line ) <= 3 :
380- base_class = "fit-text-small text-center select-none"
381- else :
382- base_class = "fit-text text-center select-none"
383- # Create the label with initial inline style using get_line_style_for_lines().
375+ base_class = LABEL_SMALL_CLASSES if len (line ) <= 3 else LABEL_CLASSES
384376 lbl = ui .label (line ).classes (base_class ).style (get_line_style_for_lines (line_count , default_text_color ))
385- # Instead of just storing the label, store its metadata.
386377 labels_list .append ({
387378 "ref" : lbl ,
388379 "base_classes" : base_class ,
389380 "base_style" : get_line_style_for_lines (line_count , default_text_color )
390381 })
391- # Store both the card and its labels in the global tile_buttons dict.
392382 tile_buttons_dict [(row_idx , col_idx )] = {"card" : card , "labels" : labels_list }
393383 if phrase .upper () == FREE_SPACE_TEXT :
394384 clicked_tiles .add ((row_idx , col_idx ))
@@ -404,8 +394,6 @@ def update_tile_styles(tile_buttons_dict: dict):
404394 for (r , c ), tile in tile_buttons_dict .items ():
405395 # tile is a dict with keys "card" and "labels"
406396 phrase = board [r ][c ]
407- if phrase .upper () == FREE_SPACE_TEXT :
408- continue
409397
410398 if (r , c ) in clicked_tiles :
411399 new_card_style = f"background-color: { TILE_CLICKED_BG_COLOR } ; color: { TILE_CLICKED_TEXT_COLOR } ; border: none;"
@@ -442,7 +430,7 @@ def check_phrases_file_change():
442430 Check if phrases.txt has changed. If so, re-read the file, update the board,
443431 and re-render the board UI.
444432 """
445- global last_phrases_mtime , phrases , board , tile_buttons , home_board_container
433+ global last_phrases_mtime , phrases , board , board_views
446434 try :
447435 mtime = os .path .getmtime ("phrases.txt" )
448436 except Exception as e :
@@ -456,13 +444,14 @@ def check_phrases_file_change():
456444 phrases = [line .strip ().upper () for line in f if line .strip ()]
457445 # Rebuild board data: re-shuffle and re-create board structure.
458446 shuffled_phrases = random .sample (phrases , 24 )
459- shuffled_phrases .insert (12 , "FREE MEAT" )
447+ shuffled_phrases .insert (12 , FREE_SPACE_TEXT )
460448 board = [shuffled_phrases [i :i + 5 ] for i in range (0 , 25 , 5 )]
461- # Clear the board UI and rebuild it.
462- home_board_container .clear ()
463- tile_buttons .clear () # Clear global dictionary.
464- build_board (home_board_container , tile_buttons , toggle_tile )
465- home_board_container .update () # Force update so new styles are applied immediately.
449+ # Update all board views (both home and stream)
450+ for view , (container , tile_buttons_local ) in board_views .items ():
451+ container .clear ()
452+ tile_buttons_local .clear () # Clear local board dictionary.
453+ build_board (container , tile_buttons_local , toggle_tile )
454+ container .update () # Force update so new styles are applied immediately.
466455 ui .run_javascript (
467456 "fitty('.fit-text', { multiLine: true, minSize: 10, maxSize: 1000 });"
468457 "fitty('.fit-text-small', { multiLine: true, minSize: 10, maxSize: 72 });"
0 commit comments