@@ -48,6 +48,16 @@ def test_connection(host, port, timeout=5):
4848 return False , error_msg
4949
5050
51+ def camel_to_snake_case (name ):
52+ """Convert CamelCase to snake_case for Minecraft IDs"""
53+ import re
54+
55+ # Insert underscore before uppercase letters (except the first one)
56+ s1 = re .sub ("(.)([A-Z][a-z]+)" , r"\1_\2" , name )
57+ # Insert underscore before uppercase letters preceded by lowercase letters or digits
58+ return re .sub ("([a-z0-9])([A-Z])" , r"\1_\2" , s1 ).lower ()
59+
60+
5161def load_data ():
5262 data = {}
5363 for typename in ["item" , "effect" , "enchantment" ]: # Add more types here as needed
@@ -56,11 +66,27 @@ def load_data():
5666 try :
5767 with open (path , "r" ) as f :
5868 raw = json .load (f )
59- data [typename ] = [
60- (entry ["displayName" ], f"minecraft:{ entry ['name' ].lower ()} " )
61- for entry in raw
62- if "name" in entry and "displayName" in entry
63- ]
69+ if typename == "effect" :
70+ # For effects, include type information (good/bad)
71+ data [typename ] = [
72+ (
73+ entry ["displayName" ],
74+ f"minecraft:{ camel_to_snake_case (entry ['name' ])} " ,
75+ entry .get ("type" , "good" ),
76+ )
77+ for entry in raw
78+ if "name" in entry and "displayName" in entry
79+ ]
80+ else :
81+ # For items and enchantments, keep the old format
82+ data [typename ] = [
83+ (
84+ entry ["displayName" ],
85+ f"minecraft:{ camel_to_snake_case (entry ['name' ])} " ,
86+ )
87+ for entry in raw
88+ if "name" in entry and "displayName" in entry
89+ ]
6490 except FileNotFoundError :
6591 print (f"Warning: { filename } not found." )
6692 data [typename ] = []
@@ -249,6 +275,13 @@ def setup_gui(self):
249275 )
250276 self .effect_result_tree .configure (selectmode = "browse" , takefocus = False )
251277 self .effect_result_tree .column ("label" , anchor = "w" , stretch = True , width = 300 )
278+ # Add color tags for good/bad effects
279+ self .effect_result_tree .tag_configure (
280+ "good_effect" , foreground = "#4CAF50"
281+ ) # Green for good effects
282+ self .effect_result_tree .tag_configure (
283+ "bad_effect" , foreground = "#F44336"
284+ ) # Red for bad effects
252285 self .effect_result_tree .grid (
253286 row = 1 , column = 0 , columnspan = 5 , sticky = "ew" , pady = (5 , 0 )
254287 )
@@ -258,29 +291,33 @@ def setup_gui(self):
258291 items_container = tb .Frame (main_frame )
259292 items_container .grid (row = 1 , column = 0 , sticky = "nsew" )
260293 items_container .grid_rowconfigure (0 , weight = 1 )
261- items_container .grid_columnconfigure (0 , weight = 1 )
262- items_container .grid_columnconfigure (1 , weight = 1 )
294+ # Force equal column widths with uniform configuration
295+ items_container .grid_columnconfigure (0 , weight = 1 , uniform = "col" )
296+ items_container .grid_columnconfigure (1 , weight = 1 , uniform = "col" )
263297
264298 # Left side - Item selection
265299 items_frame = tb .LabelFrame (items_container , text = "Give Item" , padding = 15 )
266- items_frame .grid (row = 0 , column = 0 , sticky = "nsew" , padx = (0 , 5 ))
267- items_frame .grid_rowconfigure (1 , weight = 1 )
300+ items_frame .grid (row = 0 , column = 0 , sticky = "nsew" , padx = (0 , 2. 5 ))
301+ items_frame .grid_rowconfigure (2 , weight = 1 ) # Items list is now on row 2
268302 items_frame .grid_columnconfigure (1 , weight = 1 )
269303
270304 tb .Label (items_frame , text = "Item:" ).grid (
271305 row = 0 , column = 0 , sticky = "w" , pady = (0 , 5 )
272306 )
273307 self .item_search_entry = tb .Entry (items_frame )
274308 self .item_search_entry .grid (
275- row = 0 , column = 1 , sticky = "ew" , pady = (0 , 5 ), padx = (5 , 10 )
309+ row = 0 , column = 1 , sticky = "ew" , pady = (0 , 5 ), padx = (5 , 0 ), columnspan = 3
276310 )
277311 self .item_search_entry .bind ("<KeyRelease>" , self .update_item_list )
278312
313+ # Second row for amount and checkbox
279314 tb .Label (items_frame , text = "Amount:" ).grid (
280- row = 0 , column = 2 , sticky = "w" , pady = ( 0 , 5 ), padx = (0 , 5 )
315+ row = 1 , column = 0 , sticky = "w" , pady = (0 , 5 )
281316 )
282317 self .item_amount_entry = tb .Entry (items_frame , width = 8 )
283- self .item_amount_entry .grid (row = 0 , column = 3 , pady = (0 , 5 ), padx = (0 , 10 ))
318+ self .item_amount_entry .grid (
319+ row = 1 , column = 1 , sticky = "w" , pady = (0 , 5 ), padx = (5 , 10 )
320+ )
284321 self .item_amount_entry .insert (0 , "1" )
285322
286323 # Checkbox for applying enchantments
@@ -291,7 +328,7 @@ def setup_gui(self):
291328 variable = self .apply_enchants_var ,
292329 )
293330 self .apply_enchants_check .grid (
294- row = 0 , column = 4 , sticky = "w" , pady = (0 , 5 ), padx = (10 , 0 )
331+ row = 1 , column = 2 , sticky = "w" , pady = (0 , 5 ), padx = (10 , 0 ), columnspan = 2
295332 )
296333
297334 # Items suggestions list
@@ -304,7 +341,7 @@ def setup_gui(self):
304341 self .item_result_tree .configure (selectmode = "browse" , takefocus = False )
305342 self .item_result_tree .column ("label" , anchor = "w" , stretch = True , width = 300 )
306343 self .item_result_tree .grid (
307- row = 1 , column = 0 , columnspan = 5 , sticky = "nsew" , pady = (10 , 10 )
344+ row = 2 , column = 0 , columnspan = 4 , sticky = "nsew" , pady = (10 , 10 )
308345 )
309346 self .item_result_tree .bind ("<Double-Button-1>" , self .send_item_command )
310347
@@ -314,13 +351,13 @@ def setup_gui(self):
314351 command = self .send_item_command ,
315352 bootstyle = "primary" ,
316353 )
317- self .give_item_button .grid (row = 2 , column = 0 , columnspan = 5 , pady = (0 , 0 ))
354+ self .give_item_button .grid (row = 3 , column = 0 , columnspan = 4 , pady = (0 , 0 ))
318355
319356 # Right side - Enchantment Manager
320357 enchant_frame = tb .LabelFrame (
321358 items_container , text = "Enchantment Manager" , padding = 15
322359 )
323- enchant_frame .grid (row = 0 , column = 1 , sticky = "nsew" , padx = (5 , 0 ))
360+ enchant_frame .grid (row = 0 , column = 1 , sticky = "nsew" , padx = (2. 5 , 0 ))
324361
325362 # Configure enchant frame grid
326363 enchant_frame .grid_rowconfigure (
@@ -664,15 +701,30 @@ def update_effect_list(self, event=None):
664701 return
665702
666703 entries = TYPED_DATA .get ("effect" , [])
667- labels = [label for label , _ in entries ]
704+ # Extract labels and create a lookup for effect types
705+ labels = [entry [0 ] for entry in entries ] # displayName
706+ effect_type_map = {
707+ entry [0 ]: entry [2 ] for entry in entries
708+ } # displayName -> type
668709
669710 results = process .extractBests (
670711 query , labels , scorer = fuzz .partial_ratio , limit = 5
671712 )
672713
673- for label , _ in results :
674- if _ > 30 : # Only show reasonable matches
675- self .effect_result_tree .insert ("" , "end" , values = (label ,))
714+ for label , score in results :
715+ if score > 30 : # Only show reasonable matches
716+ effect_type = effect_type_map .get (label , "good" )
717+ # Add visual indicators: ✅ for good effects, ❌ for bad effects
718+ if effect_type == "good" :
719+ display_text = f"✅ { label } "
720+ tag = "good_effect"
721+ else :
722+ display_text = f"❌ { label } "
723+ tag = "bad_effect"
724+
725+ item_id = self .effect_result_tree .insert (
726+ "" , "end" , values = (display_text ,), tags = (tag ,)
727+ )
676728
677729 def update_item_list (self , event = None ):
678730 """Update the item suggestions list based on search input"""
@@ -704,7 +756,9 @@ def send_effect_command(self, event=None):
704756 self .set_status ("⚠️ Please select an effect" , "warning" )
705757 return
706758 else :
707- effect_name = self .effect_result_tree .item (selected [0 ], "values" )[0 ]
759+ display_name = self .effect_result_tree .item (selected [0 ], "values" )[0 ]
760+ # Remove the ✅/❌ icons from the display name
761+ effect_name = display_name .replace ("✅ " , "" ).replace ("❌ " , "" )
708762
709763 player = self .player_var .get ()
710764 duration = self .effect_duration_entry .get ()
@@ -714,8 +768,14 @@ def send_effect_command(self, event=None):
714768 return
715769
716770 # Resolve effect name to minecraft ID
717- entry_map = dict (TYPED_DATA .get ("effect" , []))
718- resolved = entry_map .get (effect_name , effect_name .lower ().replace (" " , "_" ))
771+ # Create entry map from the 3-tuple format: (displayName, minecraftId, type)
772+ entries = TYPED_DATA .get ("effect" , [])
773+ entry_map = {
774+ entry [0 ]: entry [1 ] for entry in entries
775+ } # displayName -> minecraftId
776+ resolved = entry_map .get (
777+ effect_name , f"minecraft:{ effect_name .lower ().replace (' ' , '_' )} "
778+ )
719779
720780 try :
721781 if self .mcr is None :
0 commit comments