diff --git a/tools.py b/tools.py index 97f8800..1d6d5da 100644 --- a/tools.py +++ b/tools.py @@ -70,7 +70,6 @@ async def list_thumbnails_handler(args: Dict[str, Any]): exts = ('.png', '.jpg', '.jpeg', '.webp', '.bmp') - # Collect file info for sorting file_info_list = [] for f in os.listdir(dir_path): if f.lower().endswith(exts): @@ -89,12 +88,11 @@ async def list_thumbnails_handler(args: Dict[str, Any]): if not file_info_list: return {"text": "No supported images found in the directory."} - # Sorting logic if sort_by == "mtime": file_info_list.sort(key=lambda x: x["mtime"], reverse=True) elif sort_by == "size": file_info_list.sort(key=lambda x: x["size"], reverse=True) - else: # Default to name + else: file_info_list.sort(key=lambda x: x["name"].lower()) total_files = len(file_info_list) @@ -107,7 +105,7 @@ async def list_thumbnails_handler(args: Dict[str, Any]): thumb_size = 160 padding = 15 - label_height = 35 # Increased for larger font + label_height = 35 num_files = len(paged_files) cols = int(num_files**0.5) if num_files > 0 else 1 @@ -120,7 +118,6 @@ async def list_thumbnails_handler(args: Dict[str, Any]): canvas = Image.new('RGB', (canvas_w, canvas_h), (30, 30, 30)) draw = ImageDraw.Draw(canvas) - # Attempt to load a larger font font = None font_paths = [ "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", @@ -160,9 +157,7 @@ async def list_thumbnails_handler(args: Dict[str, Any]): size_kb = info["size"] / 1024 file_details.append(f"{i+1}. {filename} | {width}x{height} | {size_kb:.1f}KB | {mod_time}") - # Draw index number - add a small background rectangle for contrast text = str(i + 1) - # Simple text bounding box calculation (approximate) text_w = len(text) * 10 draw.rectangle([x, y + thumb_size + 2, x + text_w + 4, y + thumb_size + 24], fill=(60, 60, 60)) draw.text((x + 2, y + thumb_size + 2), text, fill=(255, 255, 255), font=font) @@ -186,6 +181,63 @@ async def list_thumbnails_handler(args: Dict[str, Any]): {"type": "image", "data": img_data, "mimeType": "image/png"} ] +async def list_directory_details_handler(args: Dict[str, Any]): + path = args.get("path") + if not path: + return {"text": "Error: Missing path argument"} + if not os.path.isdir(path): + return {"text": f"Error: {path} is not a directory."} + + try: + entries = os.listdir(path) + except Exception as e: + return {"text": f"Error reading directory: {e}"} + + dirs = [] + files = [] + + for entry in entries: + full_path = os.path.join(path, entry) + try: + stats = os.stat(full_path) + mtime = datetime.datetime.fromtimestamp(stats.st_mtime).strftime('%Y-%m-%d %H:%M') + + if os.path.isdir(full_path): + # Count items in dir (non-recursive) + try: + count = len(os.listdir(full_path)) + except: + count = 0 + dirs.append({ + "name": entry, + "info": f"{count} items", + "mtime": mtime + }) + else: + size_kb = stats.st_size / 1024 + files.append({ + "name": entry, + "info": f"{size_kb:.1f}KB", + "mtime": mtime + }) + except Exception as e: + logger.error(f"Could not stat {entry}: {e}") + + # Sort alphabetically + dirs.sort(key=lambda x: x["name"].lower()) + files.sort(key=lambda x: x["name"].lower()) + + output = [] + for d in dirs: + output.append(f"[DIR] {d['name']} | {d['info']} | {d['mtime']}") + for f in files: + output.append(f" {f['name']} | {f['info']} | {f['mtime']}") + + if not output: + return {"text": f"Directory {path} is empty."} + + return {"text": f"Listing of {path}:\n\n" + "\n".join(output)} + TOOL_REGISTRY = [ Tool( name="read_image", @@ -222,4 +274,16 @@ TOOL_REGISTRY = [ }, handler=list_thumbnails_handler ), + Tool( + name="list_directory", + description="Lists the contents of a directory in a Nemo-like format. Directories first, then files, with sizes/counts and mtime.", + schema={ + "type": "object", + "properties": { + "path": {"type": "string", "description": "Path to the directory to list"}, + }, + "required": ["path"], + }, + handler=list_directory_details_handler + ), ]