@@ -1070,59 +1070,52 @@ def _save_config_all(data: dict) -> None:
10701070 with open (config_file , "w" ) as f :
10711071 json .dump (cfg , f , indent = 2 )
10721072
1073+ # _v holds the live text for every field; on_change keeps it in sync.
1074+ # Reading tf.value without on_change returns the *initial* value only.
10731075 _cfg = _load_config ()
1074- tf_canvas_url = ft .TextField (
1075- value = _cfg .get ("CANVAS_URL" , "" ),
1076- hint_text = "canvas.yourschool.edu (https:// added automatically)" , expand = True , dense = True ,
1077- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1078- )
1079- tf_panopto = ft .TextField (
1080- value = _cfg .get ("PANOPTO_HOST" , "" ),
1081- hint_text = "mediaweb.ap.panopto.com" , expand = True , dense = True ,
1082- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1083- )
1084-
1085- conn_card = _card (ft .Column (controls = [
1086- ft .Text ("Connection" , size = 13 ,
1087- weight = ft .FontWeight .BOLD , color = ft .Colors .WHITE ),
1088- ft .Text ("Saved to config.json in the project directory." ,
1089- size = 11 , color = ft .Colors .with_opacity (0.5 , ft .Colors .WHITE )),
1090- ft .Container (height = 4 ),
1091- _field_row ("Canvas URL" , tf_canvas_url ),
1092- _field_row ("Panopto Host" , tf_panopto ),
1093- ], spacing = 10 ))
1094-
1095- # ── API Keys ──────────────────────────────────────────────────────────────
1096-
10971076 canvas_file = PROJECT_DIR / "canvas_token.txt"
10981077 openai_file = PROJECT_DIR / "openai_api.txt"
10991078 anthropic_file = PROJECT_DIR / "anthropic_key.txt"
11001079 gemini_file = PROJECT_DIR / "gemini_api.txt"
11011080
1102- tf_canvas = ft .TextField (
1103- value = canvas_file .read_text ().strip () if canvas_file .exists () else "" ,
1081+ _v = {
1082+ "canvas_url" : _cfg .get ("CANVAS_URL" , "" ),
1083+ "panopto" : _cfg .get ("PANOPTO_HOST" , "" ),
1084+ "canvas" : canvas_file .read_text ().strip () if canvas_file .exists () else "" ,
1085+ "openai" : openai_file .read_text ().strip () if openai_file .exists () else "" ,
1086+ "anthropic" : anthropic_file .read_text ().strip () if anthropic_file .exists () else "" ,
1087+ "gemini" : gemini_file .read_text ().strip () if gemini_file .exists () else "" ,
1088+ }
1089+
1090+ def _mk_tf (key : str , ** kwargs ) -> ft .TextField :
1091+ return ft .TextField (
1092+ value = _v [key ],
1093+ on_change = lambda e , k = key : _v .update ({k : e .control .value }),
1094+ ** kwargs ,
1095+ )
1096+
1097+ tf_canvas_url = _mk_tf ("canvas_url" ,
1098+ hint_text = "canvas.yourschool.edu (https:// added automatically)" ,
1099+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
1100+ tf_panopto = _mk_tf ("panopto" ,
1101+ hint_text = "mediaweb.ap.panopto.com" ,
1102+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
1103+ tf_canvas = _mk_tf ("canvas" ,
11041104 password = True , can_reveal_password = True ,
1105- hint_text = "Canvas API token" , expand = True , dense = True ,
1106- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1107- )
1108- tf_openai = ft .TextField (
1109- value = openai_file .read_text ().strip () if openai_file .exists () else "" ,
1105+ hint_text = "Canvas API token" ,
1106+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
1107+ tf_openai = _mk_tf ("openai" ,
11101108 password = True , can_reveal_password = True ,
1111- hint_text = "sk-… (no default)" , expand = True , dense = True ,
1112- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1113- )
1114- tf_anthropic = ft .TextField (
1115- value = anthropic_file .read_text ().strip () if anthropic_file .exists () else "" ,
1109+ hint_text = "sk-… (no default)" ,
1110+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
1111+ tf_anthropic = _mk_tf ("anthropic" ,
11161112 password = True , can_reveal_password = True ,
1117- hint_text = "sk-ant-… (no default)" , expand = True , dense = True ,
1118- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1119- )
1120- tf_gemini = ft .TextField (
1121- value = gemini_file .read_text ().strip () if gemini_file .exists () else "" ,
1113+ hint_text = "sk-ant-… (no default)" ,
1114+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
1115+ tf_gemini = _mk_tf ("gemini" ,
11221116 password = True , can_reveal_password = True ,
1123- hint_text = "AIza… (Google AI Studio key, no default)" , expand = True , dense = True ,
1124- bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 ,
1125- )
1117+ hint_text = "AIza… (Google AI Studio key, no default)" ,
1118+ expand = True , dense = True , bgcolor = C_OUTPUT_BG , border_color = C_PRIMARY , text_size = 12 )
11261119
11271120 refresh_status = ft .Text ("" , size = 11 ,
11281121 color = ft .Colors .with_opacity (0.6 , ft .Colors .WHITE ))
@@ -1270,6 +1263,7 @@ def _const_row(script: str, name: str, desc: str, default: str,
12701263 value = cur , expand = True , dense = True ,
12711264 bgcolor = C_OUTPUT_BG , border_color = accent ,
12721265 text_size = 12 , content_padding = ft .padding .symmetric (horizontal = 8 , vertical = 6 ),
1266+ on_change = lambda e : None , # ensures Flet syncs typed value to ctrl.value
12731267 )
12741268
12751269 _const_ctrls .append ((ctrl , script , name , default ))
@@ -1318,23 +1312,25 @@ def _save_all(_):
13181312 errors : list [str ] = []
13191313 try :
13201314 _save_config_all ({
1321- "CANVAS_URL" : tf_canvas_url . value .strip (),
1322- "PANOPTO_HOST" : tf_panopto . value .strip (),
1315+ "CANVAS_URL" : _v [ "canvas_url" ] .strip (),
1316+ "PANOPTO_HOST" : _v [ "panopto" ] .strip (),
13231317 })
13241318 except Exception as e :
13251319 errors .append (f"Connection: { e } " )
1326- for path , tf in [
1327- (canvas_file , tf_canvas ),
1328- (openai_file , tf_openai ),
1329- (anthropic_file , tf_anthropic ),
1330- (gemini_file , tf_gemini ),
1320+ for path , key in [
1321+ (canvas_file , "canvas" ),
1322+ (openai_file , "openai" ),
1323+ (anthropic_file , "anthropic" ),
1324+ (gemini_file , "gemini" ),
13311325 ]:
13321326 try :
1333- if tf .value .strip ():
1334- path .write_text (tf .value .strip ())
1327+ val = _v [key ].strip ()
1328+ if val :
1329+ path .write_text (val )
13351330 except Exception as e :
13361331 errors .append (str (e ))
13371332 for ctrl , script , name , default in _const_ctrls :
1333+ # Dropdowns sync via on_select; TextFields sync via on_change
13381334 val = (ctrl .value or default ) if isinstance (ctrl , ft .Dropdown ) \
13391335 else ctrl .value .strip ()
13401336 if not _write_constant (script , name , val ):
0 commit comments