diff --git a/scripts/TEMPLATE.md b/scripts/TEMPLATE.md new file mode 100644 index 00000000..4af14e0a --- /dev/null +++ b/scripts/TEMPLATE.md @@ -0,0 +1,12 @@ +![jsDelivr hits (GitHub)](https://img.shields.io/jsdelivr/gh/hy/walkxcode/dashboard-icons?style=flat-square&color=%23A020F0) + +## Dashboard Icons + +The best source for dashboard icons.
+[**← Back to repository**](https://github.com/walkxcode/dashboard-icons/) + +
+ + + +
\ No newline at end of file diff --git a/scripts/convert_svg_assets.py b/scripts/convert_svg_assets.py new file mode 100644 index 00000000..cad2a6f9 --- /dev/null +++ b/scripts/convert_svg_assets.py @@ -0,0 +1,157 @@ +import os +import re +import hashlib +from pathlib import Path +from PIL import Image +import cairosvg + +# Define paths +ROOT_DIR = Path(__file__).resolve().parent.parent +SVG_DIR = ROOT_DIR / "svg" +PNG_DIR = ROOT_DIR / "png" +WEBP_DIR = ROOT_DIR / "webp" + +# Ensure the output folders exist +PNG_DIR.mkdir(parents=True, exist_ok=True) +WEBP_DIR.mkdir(parents=True, exist_ok=True) + +# Track results +failed_files = [] +converted_pngs = 0 +converted_webps = 0 +total_icons = 0 + +def file_size_readable(size_bytes): + """Convert bytes to a human-readable format.""" + for unit in ['B', 'KB', 'MB', 'GB']: + if size_bytes < 1024: + return f"{size_bytes:.2f} {unit}" + size_bytes /= 1024 + +def hash_file(file_path): + """Generate an MD5 hash for a file.""" + hash_md5 = hashlib.md5() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash_md5.update(chunk) + return hash_md5.hexdigest() + +def convert_to_kebab_case(name): + """Convert a filename to kebab-case.""" + cleaned = re.sub(r'[^a-zA-Z0-9\s-]', '', name) + kebab_case_name = re.sub(r'[\s_]+', '-', cleaned).lower() + return kebab_case_name + +def rename_if_needed(file_path): + """Ensure the filename is in kebab-case; rename if necessary.""" + new_name = convert_to_kebab_case(file_path.stem) + file_path.suffix + new_path = file_path.parent / new_name + + if new_path != file_path: + if new_path.exists(): + raise FileExistsError(f"File conflict: {new_path} already exists.") + file_path.rename(new_path) + print(f"Renamed: {file_path} -> {new_path}") + + return new_path + +def needs_conversion(output_file, data=None): + """Check if a file needs to be converted or overwritten.""" + if output_file.exists(): + if data: + existing_hash = hash_file(output_file) + new_hash = hashlib.md5(data).hexdigest() + return existing_hash != new_hash + return False + return True + +def convert_svg_to_png(svg_path, png_path): + """Convert SVG to PNG.""" + global converted_pngs + try: + png_data = cairosvg.svg2png(url=str(svg_path), output_height=512) + + if needs_conversion(png_path, png_data): + with open(png_path, 'wb') as f: + f.write(png_data) + print(f"Converted PNG: {png_path} ({file_size_readable(png_path.stat().st_size)})") + converted_pngs += 1 + else: + print(f"PNG already up-to-date: {png_path}") + + except Exception as e: + print(f"Failed to convert {svg_path} to PNG: {e}") + failed_files.append(svg_path) + +def convert_png_to_webp(png_path, webp_path): + """Convert PNG to WEBP.""" + global converted_webps + try: + image = Image.open(png_path) + + if needs_conversion(webp_path): + image.save(webp_path, format='WEBP') + print(f"Converted WEBP: {webp_path} ({file_size_readable(webp_path.stat().st_size)})") + converted_webps += 1 + else: + print(f"WEBP already up-to-date: {webp_path}") + + except Exception as e: + print(f"Failed to convert {png_path} to WEBP: {e}") + failed_files.append(png_path) + +def clean_up_files(folder, valid_basenames): + """Remove files that no longer have corresponding SVG files.""" + removed_files = 0 + for file_path in folder.glob('*'): + if file_path.stem not in valid_basenames: + file_path.unlink() + print(f"Removed: {file_path}") + removed_files += 1 + return removed_files + +if __name__ == "__main__": + # Track valid basenames (existing SVG files) + valid_basenames = set() + + # Process all SVG files + for svg_file in SVG_DIR.glob("*.svg"): + total_icons += 1 + + # Ensure the filename is in kebab-case + try: + svg_path = rename_if_needed(svg_file) + except Exception as e: + print(f"Error renaming {svg_file}: {e}") + failed_files.append(svg_file) + continue + + valid_basenames.add(svg_path.stem) + + # Set paths for PNG and WEBP + png_path = PNG_DIR / f"{svg_path.stem}.png" + webp_path = WEBP_DIR / f"{svg_path.stem}.webp" + + # Convert SVG to PNG + convert_svg_to_png(svg_path, png_path) + + # Convert PNG to WEBP + if png_path.exists(): + convert_png_to_webp(png_path, webp_path) + + # Clean up unused files + removed_pngs = clean_up_files(PNG_DIR, valid_basenames) + removed_webps = clean_up_files(WEBP_DIR, valid_basenames) + + # Display summary + if converted_pngs == 0 and converted_webps == 0 and removed_pngs == 0 and removed_webps == 0: + print("\nAll icons are already up-to-date.") + else: + print(f"\nConverted {converted_pngs} PNGs and {converted_webps} WEBPs out of {total_icons} icons.") + print(f"Removed {removed_pngs} PNGs and {removed_webps} WEBPs.") + + # Display any failed conversions + if failed_files: + print("\nThe following files failed to convert:") + for file in failed_files: + print(file) \ No newline at end of file diff --git a/scripts/generate_file_tree.py b/scripts/generate_file_tree.py new file mode 100644 index 00000000..bc23aa73 --- /dev/null +++ b/scripts/generate_file_tree.py @@ -0,0 +1,37 @@ +import os +import json +import sys +from pathlib import Path + +def generate_folder_tree(paths): + tree = {} + for path in paths: + resolved_path = Path(path).resolve() + base_folder = resolved_path.name or Path.cwd().name + for root, _, files in os.walk(resolved_path): + relative_path = os.path.relpath(root, resolved_path) + key = base_folder if relative_path == '.' else os.path.join(base_folder, relative_path) + if files: + tree[key] = sorted(files) # Sort the list of files alphabetically + return tree + +if __name__ == "__main__": + # Adjust paths to be one level up + folder_paths = sys.argv[1:] + folder_paths = [str(Path(path).resolve()) for path in folder_paths] + + if not folder_paths: + print("Please provide at least one folder path.") + sys.exit(1) + + # Generate the combined folder tree + folder_tree = generate_folder_tree(folder_paths) + + # Write the JSON structure to 'tree.json' in the root folder + root_dir = Path(__file__).resolve().parent.parent # Assuming script is in 'scripts' folder + tree_json_path = root_dir / 'tree.json' + + with open(tree_json_path, 'w') as f: + json.dump(folder_tree, f, indent=4, sort_keys=True) # Sort the keys in the JSON output + + print(f"Folder tree successfully written to '{tree_json_path}'.") \ No newline at end of file diff --git a/scripts/generate_icons_page.py b/scripts/generate_icons_page.py new file mode 100644 index 00000000..945385e9 --- /dev/null +++ b/scripts/generate_icons_page.py @@ -0,0 +1,38 @@ +import pathlib +from pathlib import Path +import sys + +def generate_img_tag(file): + return ( + f'' + f'' + ) + +if __name__ == "__main__": + root = pathlib.Path(__file__).parent.resolve() + template_path = root / "TEMPLATE.md" + icons_md_path = root.parent / "ICONS.md" + + imgs = sorted((root.parent / "webp").glob("*.webp")) + img_tags = [generate_img_tag(x) for x in imgs] + + # Read the template file + with open(template_path, "r", encoding="UTF-8") as f: + lines = f.readlines() + + # Find the line that starts with "" + try: + line_number = lines.index("\n") + except ValueError: + print(" placeholder not found in TEMPLATE.md") + sys.exit(1) + + # Insert the icons after the placeholder + lines.insert(line_number + 1, " ".join(img_tags) + "\n") + + # Write the new ICONS.md file + with open(icons_md_path, "w", encoding="UTF-8") as f: + f.writelines(lines) + + print("ICONS.md has been successfully generated.") \ No newline at end of file