diff --git a/.github/scripts/check_icon_pr.py b/.github/scripts/check_icon_pr.py index dc5eec771..0f4ad67c7 100644 --- a/.github/scripts/check_icon_pr.py +++ b/.github/scripts/check_icon_pr.py @@ -1,11 +1,85 @@ +import colorsys +from pathlib import Path +import re from typing import List import xml.etree.ElementTree as et -from pathlib import Path - # pycharm complains that build_assets is an unresolved ref # don't worry about it, the script still runs -from build_assets import filehandler, arg_getters, util +from build_assets import arg_getters, filehandler, util + + +def hex_to_rgb(hex_str: str): + cleaned = hex_str.lstrip("#").strip() + if len(cleaned) in (3, 4): + cleaned = "".join(c * 2 for c in cleaned[:3]) + elif len(cleaned) >= 6: + cleaned = cleaned[:6] + else: + return None + try: + val = int(cleaned, 16) + return ( + ((val >> 16) & 255) / 255.0, + ((val >> 8) & 255) / 255.0, + (val & 255) / 255.0, + ) + except ValueError: + return None + + +def is_color_saturated(r: float, g: float, b: float, threshold: float = 0.20) -> bool: + _, _, s = colorsys.rgb_to_hls(r, g, b) + return s >= threshold + + +def svg_contains_saturated_colors(svg_path: Path) -> bool: + try: + content = svg_path.read_text(encoding="utf-8") + except Exception: + return False + + # Check hex colors: #rgb, #rrggbb, etc. + hex_matches = re.findall(r"#([0-9a-fA-F]{3,8})\b", content) + for h in hex_matches: + rgb = hex_to_rgb(h) + if rgb and is_color_saturated(*rgb): + return True + + # Check rgb(...) colors + rgb_matches = re.findall( + r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)", content + ) + for r_s, g_s, b_s in rgb_matches: + rgb = (int(r_s) / 255.0, int(g_s) / 255.0, int(b_s) / 255.0) + if is_color_saturated(*rgb): + return True + + # Check hsl(...) colors + hsl_matches = re.findall( + r"hsl\(\s*[\d.]+\s*,\s*([\d.]+)%\s*,\s*[\d.]+%?\s*\)", content + ) + for s_s in hsl_matches: + try: + if float(s_s) / 100.0 >= 0.20: + return True + except ValueError: + pass + + return False + + +def icon_svgs_have_saturated_colors(icon: dict, icons_folder_path="./icons") -> bool: + if not icon or "name" not in icon: + return False + icon_dir = Path(icons_folder_path) / icon["name"] + if not icon_dir.is_dir(): + return False + + for svg_file in icon_dir.glob("*.svg"): + if svg_contains_saturated_colors(svg_file): + return True + return False def main(): @@ -19,28 +93,36 @@ def main(): all_icons = filehandler.get_json_file_content(args.devicon_json_path) devicon_err_msg = [] - #First check if devicon.json is sorted - if sorted(all_icons, key=lambda d: d['name']) != all_icons: - devicon_err_msg.append(f"devicon.json is not sorted correctly.\nPlease make sure that your icon is added in the `devicon.json` file at the correct alphabetic position\nas seen here: https://github.com/devicons/devicon/wiki/Updating-%60devicon.json%60") + # First check if devicon.json is sorted + if sorted(all_icons, key=lambda d: d["name"]) != all_icons: + devicon_err_msg.append( + "devicon.json is not sorted correctly.\n" + "Please make sure that your icon is added in the `devicon.json` file at the correct alphabetic position\n" + "as seen here: https://github.com/devicons/devicon/wiki/Updating-%60devicon.json%60" + ) # get only the icon object that has the name matching the pr title filtered_icon = util.find_object_added_in_pr(all_icons, args.pr_title) print("Checking devicon.json object: " + str(filtered_icon)) - devicon_err_msg.append(check_devicon_object(filtered_icon)) + devicon_err_msg.append(check_devicon_object(filtered_icon, args.icons_folder_path)) # check the file names filename_err_msg = "" svgs = None try: - svgs = filehandler.get_svgs_paths([filtered_icon], args.icons_folder_path, as_str=False) - print("SVGs to check: ", *svgs, sep='\n') + svgs = filehandler.get_svgs_paths( + [filtered_icon], args.icons_folder_path, as_str=False + ) + print("SVGs to check: ", *svgs, sep="\n") except ValueError as e: filename_err_msg = "Error found regarding filenames:\n- " + e.args[0] # check the svgs if svgs is None or len(svgs) == 0: print("No SVGs to check, ending script.") - svg_err_msg = "Error checking SVGs: no SVGs to check. Might be caused by above issues." + svg_err_msg = ( + "Error checking SVGs: no SVGs to check. Might be caused by above issues." + ) else: svg_err_msg = check_svgs(svgs, filtered_icon) @@ -61,7 +143,7 @@ def main(): util.exit_with_err(e) -def check_devicon_object(icon: dict): +def check_devicon_object(icon: dict, icons_folder_path="./icons"): """ Check that the devicon object added is up to standard. :return a string containing the error messages if any. @@ -70,27 +152,33 @@ def check_devicon_object(icon: dict): try: if type(icon["name"]) != str: - err_msgs.append("- Property \"name\" value must be type string.") + err_msgs.append('- Property "name" value must be type string.') except KeyError: - err_msgs.append("- Missing property key: \"name\".") + err_msgs.append('- Missing property key: "name".') try: for altname in icon["altnames"]: if type(altname) != str: raise TypeError() if altname == icon["name"]: - err_msgs.append(f"- \"altnames\" should not contain the same name as \"name\" property. Please remove \"{altname}\" from \"altnames\"") + err_msgs.append( + f'- "altnames" should not contain the same name as "name" property. Please remove "{altname}" from "altnames"' + ) except TypeError: - err_msgs.append("- \"altnames\" must be an array of strings, not: " + str(icon["altnames"])) + err_msgs.append( + '- "altnames" must be an array of strings, not: ' + str(icon["altnames"]) + ) except KeyError: - err_msgs.append("- Missing property key: \"altnames\".") + err_msgs.append('- Missing property key: "altnames".') try: for tag in icon["tags"]: if type(tag) != str: raise TypeError() except TypeError: - err_msgs.append("- 'tags' must be an array of strings, not: " + str(icon["tags"])) + err_msgs.append( + "- 'tags' must be an array of strings, not: " + str(icon["tags"]) + ) except KeyError: err_msgs.append("- missing key: 'tags'.") @@ -101,66 +189,93 @@ def check_devicon_object(icon: dict): err_msgs.append("- missing key: 'versions'.") try: - if type(icon["versions"]["svg"]) != list or len(icon["versions"]["svg"]) == 0: + if ( + type(icon["versions"]["svg"]) != list + or len(icon["versions"]["svg"]) == 0 + ): err_msgs.append("- must contain at least 1 svg version in a list.") for version in icon["versions"]["svg"]: if not util.is_svg_version_valid(version): - err_msgs.append(f"- Invalid version name in versions['svg']: '{version}'. Must match regexp: (original|plain|line)(-wordmark)?") + err_msgs.append( + f"- Invalid version name in versions['svg']: '{version}'. Must match regexp: (original|plain|line)(-wordmark)?" + ) except KeyError: err_msgs.append("- missing key: 'svg' in 'versions'.") - + try: - if type(icon["versions"]["font"]) != list or len(icon["versions"]["svg"]) == 0: + if ( + type(icon["versions"]["font"]) != list + or len(icon["versions"]["svg"]) == 0 + ): err_msgs.append("- must contain at least 1 font version in a list.") for version in icon["versions"]["font"]: if not util.is_svg_version_valid(version): - err_msgs.append(f"- Invalid version name in versions['font']: '{version}'. Must match regexp: (original|plain|line)(-wordmark)?") + err_msgs.append( + f"- Invalid version name in versions['font']: '{version}'. Must match regexp: (original|plain|line)(-wordmark)?" + ) except KeyError: err_msgs.append("- missing key: 'font' in 'versions'.") try: - if type(icon["color"]) != str or "#" not in icon["color"]: + color = icon["color"] + if type(color) != str or "#" not in color: err_msgs.append("- 'color' must be a string in the format '#abcdef'") + else: + normalized_color = color.strip().lower() + if normalized_color in ["#000000", "#000", "#ffffff"]: + if icon_svgs_have_saturated_colors(icon, icons_folder_path): + err_msgs.append( + f"- 'color' property '{color}' appears to be a placeholder, but the icon's SVG contains colored fills/strokes. " + "Please specify the official brand color (e.g. hex code '#abcdef') instead of a placeholder." + ) except KeyError: err_msgs.append("- missing key: 'color'.") try: if type(icon["aliases"]) != list: - err_msgs.append("- \"aliases\" must be an array of objects.") + err_msgs.append('- "aliases" must be an array of objects.') except KeyError: - err_msgs.append("- missing key: 'aliases'.") + err_msgs.append('- missing key: "aliases".') try: for alias_objects in icon["aliases"]: if type(alias_objects) != dict: raise TypeError() except TypeError: - err_msgs.append("- \"aliases\" must be an array of objects, not: " + str(icon["aliases"])) + err_msgs.append( + '- "aliases" must be an array of objects, not: ' + str(icon["aliases"]) + ) try: for alias_objects in icon["aliases"]: if type(alias_objects["base"]) != str: err_msgs.append("- must contain at least 1 base in aliases.") - if not util.is_svg_version_valid(alias_objects['base']): - err_msgs.append(f"- Invalid base name in aliases[\"base\"]: \"{alias_objects['base']}\". Must match regexp: (original|plain|line)(-wordmark)?") + if not util.is_svg_version_valid(alias_objects["base"]): + err_msgs.append( + f'- Invalid base name in aliases["base"]: "{alias_objects["base"]}". Must match regexp: (original|plain|line)(-wordmark)?' + ) except KeyError: - err_msgs.append("- missing key: \"base\" in \"aliases\".") + err_msgs.append('- missing key: "base" in "aliases".') try: for alias_objects in icon["aliases"]: if type(alias_objects["alias"]) != str: err_msgs.append("- must contain at least 1 alias in aliases.") - if not util.is_svg_version_valid(alias_objects['alias']): - err_msgs.append(f"- Invalid alias name in aliases['alias']: \"{alias_objects['alias']}\". Must match regexp: (original|plain|line)(-wordmark)?") + if not util.is_svg_version_valid(alias_objects["alias"]): + err_msgs.append( + f"- Invalid alias name in aliases['alias']: \"{alias_objects['alias']}\". Must match regexp: (original|plain|line)(-wordmark)?" + ) except KeyError: - err_msgs.append("- missing key: \"alias\" in \"aliases\".") + err_msgs.append('- missing key: "alias" in "aliases".') if len(err_msgs) > 0: - message = "Error found in \"devicon.json\" for \"{}\" entry: \n{}".format(icon["name"], "\n".join(err_msgs)) + message = 'Error found in "devicon.json" for "{}" entry: \n{}'.format( + icon["name"], "\n".join(err_msgs) + ) return message - return "" + return "" def check_svgs(svg_file_paths: List[Path], devicon_object: dict): @@ -181,7 +296,9 @@ def check_svgs(svg_file_paths: List[Path], devicon_object: dict): # name check if not util.is_svg_name_valid(svg_path.name): - err_msg.append("- SVG file name didn't match our pattern of `name-(original|plain|line)(-wordmark)?.svg`") + err_msg.append( + "- SVG file name didn't match our pattern of `name-(original|plain|line)(-wordmark)?.svg`" + ) # svg check tree = et.parse(svg_path) @@ -192,13 +309,17 @@ def check_svgs(svg_file_paths: List[Path], devicon_object: dict): err_msg.append(f"- root is '{root.tag}'. Root must be an 'svg' element") if root.get("viewBox") != "0 0 128 128": - err_msg.append("- 'viewBox' is not '0 0 128 128' -> Set it or scale the file using https://www.iloveimg.com/resize-image/resize-svg.") + err_msg.append( + "- 'viewBox' is not '0 0 128 128' -> Set it or scale the file using https://www.iloveimg.com/resize-image/resize-svg." + ) # goes through all elems and check for strokes if util.is_svg_in_font_attribute(svg_path, devicon_object): for child in tree.iter(): if child.get("stroke") != None: - err_msg.append("- SVG contains `stroke` property. This will get ignored by Icomoon. Please convert them to fills.") + err_msg.append( + "- SVG contains `stroke` property. This will get ignored by Icomoon. Please convert them to fills." + ) break if len(err_msg) > 1: diff --git a/.github/workflows/check_site_pr.yml b/.github/workflows/check_site_pr.yml new file mode 100644 index 000000000..bb5064bf5 --- /dev/null +++ b/.github/workflows/check_site_pr.yml @@ -0,0 +1,37 @@ +name: Check Website PR + +on: + pull_request: + paths: + - 'site/**' + - '.github/workflows/check_site_pr.yml' + +jobs: + check: + name: Typecheck & Build Website + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: latest + + - name: Install dependencies + run: pnpm --dir site install --frozen-lockfile + + - name: Svelte check + run: pnpm --dir site check + + - name: Lint and format check + run: pnpm --dir site lint + + - name: Build static site + run: pnpm --dir site build diff --git a/.github/workflows/deploy_site.yml b/.github/workflows/deploy_site.yml new file mode 100644 index 000000000..b5c337e20 --- /dev/null +++ b/.github/workflows/deploy_site.yml @@ -0,0 +1,63 @@ +name: Deploy Website to GitHub Pages + +on: + push: + branches: [master] + paths: + - 'site/**' + - 'devicon.json' + - '.github/workflows/deploy_site.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: false + +jobs: + build: + name: Build SvelteKit Website + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup Node.js + uses: actions/setup-node@v5 + with: + node-version: '20' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: latest + + - name: Install dependencies + run: pnpm --dir site install --frozen-lockfile + + - name: Build static site + run: pnpm --dir site build + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./site/build + + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 8ffb181e1..cacb88833 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,18 @@ Add the following CSS rules in your stylesheet: We are happy with every contribution, whether it's new icons, features, or maintainers. Please have a look at our Wiki to see how you can contribute to this project.

+ +

Contributors

+

+ Thanks to all our contributors! Devicon would not be possible without you. +

+ + Devicon Contributors + +

+ Made with contrib.rocks. +

+

Discord community

We have a Discord community for Devicons. You can easily request icons, discuss, and have a good time talking with the community members! Join the official Discord Server here! @@ -296,21 +308,17 @@ This file is used to show all the new icons previously built.

npm run build-css ``` -

Setting up the web server

- -

Run the following command to start the web server with Python.

- -```bash -npm run dev # Will run on port 8000 -``` +

Setting up the website locally

-

Or this command, which does exactly the same, but the port can be customized.

+

Run the website locally with:

```bash -python -m http.server +npm run site:dev +# or +npm run dev ``` -

You're done now! :tada: Your build of Devicons should be available at https://localhost:8000 (or the desired port).

+

Your build of the Devicon website will be available at http://localhost:5173.


diff --git a/devicon.json b/devicon.json index d779a6685..91b1eec59 100644 --- a/devicon.json +++ b/devicon.json @@ -193,7 +193,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#955AE0", "aliases": [] }, { @@ -286,7 +286,7 @@ "original-wordmark" ] }, - "color": "#2d3441", + "color": "#77C1D2", "aliases": [ { "base": "original", @@ -517,7 +517,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#FFB71B", "aliases": [] }, { @@ -562,7 +562,7 @@ "original", "plain" ], - "font":[ + "font": [ "plain" ] }, @@ -1699,7 +1699,7 @@ "plain-wordmark" ] }, - "color": "#161414", + "color": "#F6A71A", "aliases": [] }, { @@ -2188,7 +2188,7 @@ "plain-wordmark" ] }, - "color": "#13171a", + "color": "#0079B8", "aliases": [] }, { @@ -2213,9 +2213,9 @@ "aliases": [] }, { - "name":"claudecode", + "name": "claudecode", "altnames": [], - "tags":[ + "tags": [ "ai", "coding", "assistant" @@ -2280,7 +2280,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#009AE5", "aliases": [] }, { @@ -2750,7 +2750,7 @@ "plain-wordmark" ] }, - "color": "#444", + "color": "#4CC2E4", "aliases": [] }, { @@ -3209,7 +3209,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#34A853", "aliases": [] }, { @@ -3811,7 +3811,7 @@ "plain" ] }, - "color": "#000000", + "color": "#FFF100", "aliases": [] }, { @@ -3876,7 +3876,7 @@ "line-wordmark" ] }, - "color": "#000000", + "color": "#1496FF", "aliases": [] }, { @@ -5073,7 +5073,7 @@ "line-wordmark" ] }, - "color": "#3581E5", + "color": "#3581E5", "aliases": [] }, { @@ -5319,12 +5319,14 @@ "plain" ] }, - "color": "#24292e", + "color": "#007ACC", "aliases": [] }, { "name": "githubcopilot", - "altnames": ["github copilot"], + "altnames": [ + "github copilot" + ], "tags": [ "automation", "AI", @@ -5346,7 +5348,7 @@ "line-wordmark" ] }, - "color": "#000", + "color": "#8534F3", "aliases": [] }, { @@ -5479,7 +5481,7 @@ "plain" ] }, - "color": "#000000", + "color": "#3333FF", "aliases": [] }, { @@ -5608,7 +5610,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#00ADD8", "aliases": [] }, { @@ -6168,11 +6170,11 @@ "svg": [ "original", "plain", - "line" + "line" ], "font": [ "plain", - "line" + "line" ] }, "color": "#997ff8", @@ -6384,7 +6386,7 @@ "line-wordmark" ] }, - "color": "#333333", + "color": "#3465A4", "aliases": [] }, { @@ -6766,7 +6768,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#FE315D", "aliases": [] }, { @@ -6977,7 +6979,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#CC0000", "aliases": [] }, { @@ -8137,7 +8139,7 @@ "plain" ] }, - "color": "#000000", + "color": "#FCC624", "aliases": [] }, { @@ -8258,7 +8260,7 @@ "plain" ] }, - "color": "#000", + "color": "#3492FF", "aliases": [] }, { @@ -8301,13 +8303,13 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#4B73FF", "aliases": [] }, { "name": "love2d", "altnames": [ - "lรถve" + "l\u00f6ve" ], "tags": [ "programming", @@ -9625,7 +9627,7 @@ "line" ] }, - "color": "#1d252c", + "color": "#00AC69", "aliases": [ { "base": "original", @@ -9954,7 +9956,7 @@ "line-wordmark" ] }, - "color": "#3d3b47", + "color": "#0977F7", "aliases": [] }, { @@ -10461,7 +10463,7 @@ "line" ] }, - "color": "#000", + "color": "#3DAE2B", "aliases": [ { "base": "original", @@ -11074,7 +11076,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#B41E8E", "aliases": [] }, { @@ -11197,7 +11199,7 @@ "line-wordmark" ] }, - "color": "#000", + "color": "#2B037A", "aliases": [] }, { @@ -11588,7 +11590,7 @@ "plain" ] }, - "color": "#000", + "color": "#DD0031", "aliases": [] }, { @@ -11957,7 +11959,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#21D789", "aliases": [] }, { @@ -12953,7 +12955,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#C41475", "aliases": [] }, { @@ -13147,7 +13149,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#D72B2B", "aliases": [] }, { @@ -13260,7 +13262,7 @@ "plain" ] }, - "color": "#0D0E12", + "color": "#F03E2F", "aliases": [] }, { @@ -13649,7 +13651,7 @@ { "name": "shadcn", "altnames": [], - "color":"#000000", + "color": "#000000", "tags": [ "ui", "components", @@ -13759,7 +13761,7 @@ "plain-wordmark" ] }, - "color": "#2D333A", + "color": "#4A154B", "aliases": [] }, { @@ -13984,7 +13986,7 @@ "original-wordmark" ] }, - "color": "#0C1724", + "color": "#EA005A", "aliases": [ { "base": "original-wordmark", @@ -14086,7 +14088,7 @@ "plain-wordmark" ] }, - "color": "#333333", + "color": "#CA2727", "aliases": [] }, { @@ -14291,7 +14293,7 @@ "plain-wordmark" ] }, - "color": "#000", + "color": "#FFFFFF", "aliases": [] }, { @@ -14681,7 +14683,7 @@ } ] }, - { + { "name": "systemverilog", "altnames": [ "system verilog", @@ -14801,7 +14803,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#00F076", "aliases": [] }, { @@ -14972,7 +14974,7 @@ ] }, "color": "#50d18d", - "aliases":[ + "aliases": [ { "base": "original", "alias": "plain" @@ -15163,7 +15165,7 @@ "plain" ] }, - "color": "#1d1d1b", + "color": "#00ADD8", "aliases": [ { "base": "plain", @@ -15387,7 +15389,7 @@ "line-wordmark" ] }, - "color": "#2d3136", + "color": "#CB3349", "aliases": [] }, { @@ -15549,7 +15551,7 @@ "line-wordmark" ] }, - "color": "#000000", + "color": "#F14A00", "aliases": [] }, { @@ -15931,7 +15933,7 @@ "plain-wordmark" ] }, - "color": "#141416", + "color": "#3DC4FF", "aliases": [] }, { @@ -16024,7 +16026,7 @@ } ] }, - { + { "name": "verilog", "altnames": [ "verilog hdl" @@ -16604,7 +16606,7 @@ "plain" ] }, - "color": "#000000", + "color": "#F5C842", "aliases": [] }, { @@ -16674,14 +16676,14 @@ "graphics", "web", "3d" - ], + ], "versions": { "svg": [ "original" ], - "font": [ + "font": [ "original" - ] + ] }, "color": "#990000", "aliases": [ @@ -16782,7 +16784,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#00CDD7", "aliases": [] }, { @@ -16932,10 +16934,10 @@ "plain-wordmark" ] }, - "color": "#494949", + "color": "#21759B", "aliases": [] }, - { + { "name": "xai", "altnames": [ "X AI", @@ -16945,7 +16947,7 @@ "AI", "assistant", "language-model", - "llm" + "llm" ], "versions": { "svg": [ @@ -17213,7 +17215,7 @@ "plain-wordmark" ] }, - "color": "#000000", + "color": "#FB406D", "aliases": [] }, { @@ -17368,7 +17370,7 @@ "line-wordmark" ] }, - "color": "#000", + "color": "#F15A24", "aliases": [] }, { @@ -17386,7 +17388,7 @@ "plain" ] }, - "color": "#443e38", + "color": "#685B4F", "aliases": [] } ] diff --git a/docs/CNAME b/docs/CNAME deleted file mode 100644 index 0d1b34d93..000000000 --- a/docs/CNAME +++ /dev/null @@ -1 +0,0 @@ -devicon.dev \ No newline at end of file diff --git a/docs/assets/css/discord-logo.svg b/docs/assets/css/discord-logo.svg deleted file mode 100644 index 36ca2de72..000000000 --- a/docs/assets/css/discord-logo.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css deleted file mode 100644 index 2ae904529..000000000 --- a/docs/assets/css/style.css +++ /dev/null @@ -1,484 +0,0 @@ -@charset "UTF-8"; -@import '//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css'; -@import url(https://fonts.googleapis.com/css?family=Lato:300,400,700); -@import url(https://fonts.googleapis.com/css?family=Damion); -:root { - /* color */ - --primaryColor: #3D9561; - --primaryColorDark: #2D804E; - --primaryColorLight: #4AB475; - --grey: #4c6857; - - /* sizing */ - --listItemPadding: 0.4rem; -} - -html { - box-sizing: border-box; } - -*, *::after, *::before { - box-sizing: inherit; } - -.discord-logo:before { - content: ""; - background-image: url("discord-logo.svg"); - height: 40px; - width: 40px; - display: inline-block; - background-size: cover; -} - -/* General Blueprint Style */ -*, -*:after, -*:before { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; } - -body, -html { - margin: 0; - padding: 0; - font-size: 100%; } - -.clearfix:before, -.clearfix:after { - display: table; - content: ' '; } - -.clearfix:after { - clear: both; } - -body { - font-family: 'Lato', Calibri, Arial, sans-serif; } - -a { - color: #f0f0f0; -} - -a:hover { - color: #000; } - -.cbp-ig-grid { - list-style: none; - padding: 0 0 50px; - margin: 0; } - -.cbp-ig-grid:before, -.cbp-ig-grid:after { - content: " "; - display: table; } - -.cbp-ig-grid:after { - clear: both; } - -.cbp-ig-grid li { - width: 25%; - float: left; - height: 200px; - text-align: center; } - -.cbp-ig-grid li > span { - display: block; - height: 100%; - color: var(--primaryColor); - -webkit-transition: background 0.2s; - -moz-transition: background 0.2s; - transition: background 0.2s; - cursor: pointer; } - -.cbp-ig-icon { - padding: 30px 0 0; - display: block; - -webkit-transition: -webkit-transform 0.2s; - transition: -moz-transform 0.2s; - transition: transform 0.2s; } - -.cbp-ig-icon:before { - font-family: 'devicon'; - font-size: 6em; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - -webkit-font-smoothing: antialiased; } - -.cbp-ig-grid .cbp-ig-title { - margin: 20px 0 10px; - padding: 20px 0 0; - font-size: 1em; - position: relative; - -webkit-transition: -webkit-transform 0.2s; - -moz-transition: -moz-transform 0.2s; - transition: transform 0.2s; } - -.cbp-ig-grid .cbp-ig-title:before { - content: ''; - position: absolute; - background: var(--primaryColor); - width: 60px; - height: 2px; - top: 0; - left: 50%; - margin: 0 0 0 -30px; - -webkit-transition: margin-top 0.2s; - -moz-transition: margin-top 0.2s; - transition: margin-top 0.2s; } - -.cbp-ig-grid li > span:hover { - background: var(--primaryColor); } - -.cbp-ig-grid li > span:hover .cbp-ig-icon { - -webkit-transform: translateY(10px); - -moz-transform: translateY(10px); - -ms-transform: translateY(10px); - transform: translateY(10px); } - -.cbp-ig-grid li > span:hover .cbp-ig-icon:before, -.cbp-ig-grid li > span:hover .cbp-ig-title { - color: #fff; } - -.cbp-ig-grid li > span:hover .cbp-ig-title { - -webkit-transform: translateY(-30px); - -moz-transform: translateY(-30px); - -ms-transform: translateY(-30px); - transform: translateY(-30px); } - -.cbp-ig-grid li > span:hover .cbp-ig-title:before { - background: #fff; - margin-top: 80px; } - -@media screen and (max-width: 62.75em) { - .cbp-ig-grid li { - width: 50%; } } - -@media screen and (max-width: 41.6em) { - .cbp-ig-grid li { - width: 100%; } } - -@media screen and (max-width: 25em) { - .cbp-ig-grid { - font-size: 80%; } } - -html { - background: whitesmoke; } - -html, -body { - color: #323232; - font-size: 14px; - font-family: "Lato", Calibri, Arial, sans-serif; - line-height: 1.5; } - -::-webkit-input-placeholder { - color: #4f755e; } - -:-moz-placeholder { - color: #4f755e; } - -::-moz-placeholder { - color: #4f755e; } - -:-ms-input-placeholder { - color: #4f755e; } - -html, -body { - min-width: 900px; } - -header { - position: fixed; - top: 0; - bottom: 0; - overflow: auto; - padding: 0 2rem; - width: 480px; - color: whitesmoke; - background: var(--primaryColor); } - header > h1, - header > h3 { - font-weight: 400; - font-family: "Damion", sans-serif; - text-align: center; } - header > h1 { - margin: 1rem 0 0; - font-size: 5rem; } - header > h1 > span { - font-size: 2rem; } - header > h2 { - font-weight: 400; - font-size: 1.3rem; } - header .buttonLink { - margin: 2rem 0 0; - font-weight: 900; - font-size: 1.3rem; - text-align: center; } - header .buttonLink i { - position: relative; - top: 12px; - left: -10px; - font-size: 3rem; } - header .buttonLink > a { - padding: 1.3rem 1.8rem; - border: 6px solid var(--primaryColor); - color: var(--primaryColor); - background: whitesmoke; - text-decoration: none;} - header .buttonLink a:hover { - border-color: whitesmoke; } - header > h3 { - margin: 4rem 0 0; - font-size: 3rem; - text-align: left; } - header > h5 { - margin: 0; - font-weight: 400; - font-style: italic; - font-size: 1.2rem; } - header > ul { - margin: 0; - padding: 0; - list-style: none; } - header > ul h4 { - margin: 0; - border-bottom: 1px solid whitesmoke; - font-size: 1.5rem; } - header > ul li { - margin: .8rem 0 2rem; } - header .icons-list { - margin: 1rem 0; - padding: 0; - list-style: none; - position: relative; } - header .icons-list > div { - display: inline; } - - header .icons-list li { - position: relative; - display: inline-block; - width: 6.5rem; - margin: .5rem .3rem; - padding: var(--listItemPadding); - cursor: pointer; - border: 5px solid var(--primaryColor); } - - header .icons-list li:hover { - border: 5px solid var(--primaryColorLight); - border-radius: 5px; } - - header .icons-list li:hover::before { - content: ' '; - position: absolute; - bottom: -18px; - left: 25px; - display: block; - width: 0px; - height: 0px; - border-left: 15px solid transparent; - border-right: 15px solid transparent; - border-top: 15px solid var(--primaryColorLight); } - - header .icons-list li.selected-version { - border: 5px solid var(--primaryColorLight); - border-radius: 5px; } - - header .icons-list li.selected-version::before { - position: absolute; - bottom: -19px; - left: 25px; - display: block; - width: 0; - height: 0; - border-top: 15px solid var(--primaryColorLight); - border-right: 15px solid transparent; - border-left: 15px solid transparent; - content: ' '; } - - header .icons-list i, - .svgLogo { - font-size: 5rem; - position: absolute; - left: 0; - top: 0; - margin: var(--listItemPadding); - } - - header .icons-list img { - max-width: 100%; } - - -/** color picker */ -.colorPicker { - height: 0.8em; - width: 0.8em; - border-radius: 50%; -} - -.colorPicker:hover { - cursor: pointer; - border: 1px solid whitesmoke; -} - -.colorPickerMenu { - position: absolute; - font-size: 1rem; - background-color: var(--primaryColor); - border-radius: 0.2em; - border: 1px solid var(--grey); - transform: translate(0, -30%); - padding: 1em; - margin-left: 1em; - font-weight: normal; - z-index: 1; - display: none; - -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.3); - box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.3); -} - -.colorPickerMenuCloseBtn > span:hover { - cursor: pointer; -} - -.colorPickerBtn { - margin: auto; - margin-top: 0.5em; - width: min-content; - background-color: var(--primaryColor); - border-radius: 1em; - border: 1px solid var(--grey); - display: inline-block; -} - -.colorPickerBtn:hover { - background-color: var(--grey); -} - -.checkerboardInput { - width: 1.25em; - height: 1.25em; - position: relative; - top: 0.2em; -} - -.checkerboardInput:hover { - cursor: pointer; -} - -input[type='color'] { - width: 1.25em; - height: 1.25em; - padding: 0; -} - -input[type='color']:hover { - cursor: pointer; -} - -/** Copy Btn */ - -.copyBtn { - height: 1.25em; - width: 1.25em; - margin-left: 0.25em; - position: relative; - top: 0.5em; - border-radius: 20%; - padding: 0.25em; - box-sizing: content-box; - transition: background-color 0.25s; -} - -.copyBtn:hover { - cursor: pointer; - background-color: var(--primaryColorDark); -} - -.tooltip { - visibility: hidden; - background-color: var(--primaryColorDark); - border-radius: 0.25em; - padding: 0.25em; -} - -.borders { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: -1; - border: 8px solid var(--primaryColor); } - -.main { - margin: 0 0 0 480px; } - -.result { - max-width: 75px; -} - -.computed-data { - color: var(--primaryColor); - font-weight: bold; - list-style: none; - padding: 0; - width: 80%; - margin: 25px auto; - display: flex; -} - -.computed-data li { - width: 33.33%; - text-align: center; } - -.search { - text-align: center; - margin: 4rem 0 1rem 0; } - - .search input { - padding: .5rem 1rem; - border: 0; - border-bottom: 2px solid var(--primaryColor); - color: var(--primaryColor); - background: none; - font-size: 1.2rem; - text-align: center; } - -::-webkit-input-placeholder { - color: var(--primaryColor); } - -:-moz-placeholder { - /* Firefox 18- */ - color: var(--primaryColor); } - -::-moz-placeholder { - /* Firefox 19+ */ - color: var(--primaryColor); } - -:-ms-input-placeholder { - color: var(--primaryColor); } - -/* Code related CSS */ - -.cde { - white-space: nowrap; - padding: 1rem; - border-radius: 4px; - color: var(--grey); - background: var(--primaryColorLight); - font-size: .9rem; - font-family: courier; - overflow: auto; } - -.cde-ind { - padding: 0 0 0 .5rem; } - -.cde-com { - color: var(--grey); - opacity: .5; } - -footer { - margin-top: 6rem; - text-align: center; } diff --git a/docs/assets/img/checkerboard.png b/docs/assets/img/checkerboard.png deleted file mode 100644 index 1ae989dd5..000000000 Binary files a/docs/assets/img/checkerboard.png and /dev/null differ diff --git a/docs/assets/img/color_wheel.png b/docs/assets/img/color_wheel.png deleted file mode 100644 index 65767af81..000000000 Binary files a/docs/assets/img/color_wheel.png and /dev/null differ diff --git a/docs/assets/img/copy.svg b/docs/assets/img/copy.svg deleted file mode 100644 index 2ee0e55f0..000000000 --- a/docs/assets/img/copy.svg +++ /dev/null @@ -1,5 +0,0 @@ - - -copy - - diff --git a/docs/assets/img/logo.png b/docs/assets/img/logo.png deleted file mode 100644 index 11c448ae7..000000000 Binary files a/docs/assets/img/logo.png and /dev/null differ diff --git a/docs/assets/js/script.js b/docs/assets/js/script.js deleted file mode 100644 index ded17c898..000000000 --- a/docs/assets/js/script.js +++ /dev/null @@ -1,321 +0,0 @@ -var devicon = angular.module('devicon', ['ngSanitize', 'ngAnimate']); - -/* -||============================================================== -|| Devicons controller -||============================================================== -*/ - -devicon.controller('IconListCtrl', function($scope, $http, $compile) { - - // Determination of the latest release tagging - // which is used for showing in the header of the page - // as well as for CDN links - var gitHubPath = 'devicons/devicon'; - var url = 'https://api.github.com/repos/' + gitHubPath + '/tags'; - - $scope.latestReleaseTagging = 'latest'; - $http.get(url).success(function (data) { - if(data.length > 0) { - $scope.latestReleaseTagging = data[0].name; - } - }).error(function () { - console.log('Unable to determine latest release version, fallback to master.') - }); - - var versionStr = '@' + $scope.latestReleaseTagging; - var baseUrl - - // Make sure one of the files exist, otherwise, use fallback link. - var path = '/devicon.json'; - var xhr = new XMLHttpRequest(); - xhr.open('HEAD', path, false); - xhr.send(); - if (xhr.status == "404") { - baseUrl = `https://cdn.jsdelivr.net/gh/${gitHubPath}${versionStr}/` - } else { - baseUrl = `/`; - } - - $scope.baseUrl = baseUrl - - // Get devicon.json - $http.get(baseUrl + 'devicon.json').success(function(data) { - - /* - | Re-format devicon.json - |----------------------------------------- - */ - - // icons related stuff - $scope.icons = []; - $scope.selectedIcon = {}; - - // Miscellaneous stuff - $scope.totalFonts = 0; // Total fonts and SVGs combined - - // background color related stuff - // default is the default site background color - $scope.DEFAULT_BACKGROUND = "#3D9561"; - $scope.fontBackground = $scope.DEFAULT_BACKGROUND; - $scope.svgBackground = $scope.DEFAULT_BACKGROUND; - - // whether to display the checkerboard img in the background - // for the font and svg respectively - $scope.fontDisplayChecker = false; - $scope.svgDisplayChecker = false; - - // Loop through devicon.json - angular.forEach(data, function(devicon, key) { - - // New icon format - var icon = { - name: devicon.name, - altnames: devicon.altnames, - svg: devicon.versions.svg, - font: devicon.versions.font, - main: "" - }; - - $scope.totalFonts += devicon.versions.font.length + devicon.versions.svg.length; - - // Loop through devicon.json icons - for (var i = 0; i < devicon.versions.font.length; i++) { - - // Store all versions that should become main in order - var mainVersionsArray = [ - "plain", - "line", - "original", - "plain-wordmark", - "line-wordmark", - "original-wordmark", - ]; - - // Loop through mainVersionsArray - for (var j = 0; j < mainVersionsArray.length; j++) { - - // Check if icon version can be "main", if not continue, if yes break the loops - if (devicon.name + devicon.versions.font[i] == devicon.name + mainVersionsArray[j]) { - icon.main = devicon.name + "-" + devicon.versions.font[i]; - i = 99999; // break first loop (and second) - } - } - } - - // Push new icon format to $scope.icons - $scope.icons.push(icon); - }); - - // Select first icon by default in scope - $scope.selectedIcon = $scope.icons[0]; - $scope.selectedFontIcon = $scope.icons[0].font[0]; - $scope.selectedSvgIcon = $scope.selectSvg($scope.icons[0].svg[0], 0); - $scope.selectedFontIndex = 0; - $scope.selectedSvgIndex = 0; - - // Computes miscellaneous data - $scope.avgFonts = $scope.totalFonts / $scope.icons.length // Avg number of fonts and SVGs combined - - /*------ End of "Re-format devicon.json" ------*/ - }); - - /* - | Change selected icon - | param icon: the new icon. - |-------------------------------- - */ - $scope.selectIcon = function(icon) { - $scope.selectedIcon = icon; - $scope.selectedFontIcon = icon.font[0]; - $scope.selectedFontIndex = 0; - $scope.selectedSvgIcon = $scope.selectSvg(icon.svg[0], 0); - $scope.selectedSvgIndex = 0; - - // reset color - $scope.fontBackground = $scope.DEFAULT_BACKGROUND; - $scope.svgBackground = $scope.DEFAULT_BACKGROUND; - } - /*---- End of "Change selected icon" ----*/ - - - /* - | Change selected icon font version - |-------------------------------- - */ - $scope.selectFont = function(fontVersion, colored, index) { - $scope.selectedFontIcon = fontVersion; - $scope.colored = colored ? true : false; - $scope.selectedFontIndex = index; - } - /*---- End of "Change selected font icon" ----*/ - - /* - | Change selected icon svg version - |-------------------------------- - */ - $scope.selectSvg = function(svgVersion, index) { - - $http.get(baseUrl + 'icons/' + $scope.selectedIcon.name + '/' + $scope.selectedIcon.name + '-' + svgVersion + '.svg').success(function(data){ - - var svgElement = angular.element(data); - var innerSvgElement = null; - - /** - * Loop trough svg image to find - * the actual svg content (not any comments or stuff - * we don't care for). - * See https://github.com/devicons/devicon/issues/444#issuecomment-753699913 - */ - for (const [key, value] of Object.entries(svgElement)) { - /** [object SVGSVGElement] ensures we have the actual svg content */ - if(value.toString() == '[object SVGSVGElement]') { - innerSvgElement = value; - break; - } - } - - if(innerSvgElement === null) { - console.error('Could not find content of given SVG.') - } else { - var innerSVG = (innerSvgElement.innerHTML); - $scope.selectedSvgIcon = innerSVG; - $scope.selectedSvgIndex = index; - } - }); - } - - /*---- End of "Change selected svg icon" ----*/ - - /** - * Copy the text located using `id` into the user's clipboard. - * @param {Event} event - a JS Event object. - * @param {String} id - id of the element we are copying its text - * content from. - */ - $scope.copyToClipboard = function(event, id) { - let text = document.getElementById(id).textContent - navigator.clipboard.writeText(text) - .then(() => { - $scope.displayTooltip("Copied", event.target) - }) - .catch(() => { - $scope.displayTooltip("Failed to copy", event.target) - }) - } - - /** - * Display a tooltip. - * @param {String} text - text the tooltip should have. - * @param {Element} copyBtn - the copyBtn element, which is an - */ - $scope.displayTooltip = function(text, copyBtn) { - let tooltip = copyBtn.parentElement.getElementsByClassName("tooltip")[0] - tooltip.textContent = text - // reset opacity (for some reason, default opacity is null) - tooltip.style.opacity = 1 - tooltip.style.visibility = "visible" - - // create fade out effect after 2 sec - setTimeout(() => { - let count = 10 - let intervalObj - intervalObj = setInterval(() => { - tooltip.style.opacity -= 0.1 - if (--count == 0) { - clearInterval(intervalObj) - tooltip.style.visibility = "hidden" - } - }, 50) - }, 2000) - } - - /** - * Display the color picker. - * @param {String} id - id of the menu we are showing. - */ - $scope.toggleColorPickerMenu = function(id) { - let menu = document.getElementById(id) - menu.style.display = menu.style.display == "none" || menu.style.display == "" ? "inherit" : "none" - } -}); - -/*================ End of "Devicons controller" ================*/ - -/* -||================================================================== -|| Convert icon img to svg -||================================================================== -*/ - -devicon.directive('imgToSvg', function ($http, $compile) { - - var baseUrl = window.location.href; - - return { - link : function($scope, $element, $attrs) { - - $attrs.$observe('src', function(val){ - - $http.get(baseUrl + val).success(function(data){ - - var svg = angular.element(data); - svg = svg.removeAttr('xmlns'); - svg = svg.addClass('not-colored'); - svg = svg.attr('svg-color', ''); - var $e = $compile(svg)($scope); - $element.replaceWith($e); - $element = $e; - }); - }); - } - }; -}); - -/*================ End of "Convert icon img to svg" ================*/ - -/* -||================================================================== -|| Add color to svg when hovering -||================================================================== -*/ - -devicon.directive('svgColor', function () { - - return { - link : function($scope, $element, $attrs) { - $element.on('mouseenter', function(){ - $element.removeClass('not-colored'); - }); - $element.on('mouseleave', function(){ - $element.addClass('not-colored'); - }); - } - }; -}); - -/*================ End of "Add color to svg when hovering" ================*/ - -/* -||================================================================== -|| Show all icons on click -||================================================================== -*/ - -devicon.directive('iconDetails', function ($http, $compile) { - - return { - template: '
{{icon.name}}
{{icon.name}}
', - replace: true, - scope: { - icon: "=" - }, - compile: function CompilingFunction($templateElement) { - $element.on('click', function(){ - $templateElement.replaceWith(this.template); - }); - } - }; -}); - -/*================ End of "Add color to svg when hovering" ================*/ diff --git a/docs/browserconfig.xml b/docs/browserconfig.xml deleted file mode 100644 index fe44cae80..000000000 --- a/docs/browserconfig.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - #da532c - - - diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index 80f1fbf8c..000000000 --- a/docs/index.html +++ /dev/null @@ -1,248 +0,0 @@ - - - - - - - - - - - - DEVICON | All programming languages and development tools related icons font - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
-

Devicon {{ latestReleaseTagging }}

-

Devicon is a set of icons representing programming languages, designing, and development tools. You can use it as a font or directly copy/paste the SVG code into your project.

- *Please read the legal conditions of using the logos here at the bottom of this header -

(Super) Quick Setup

-
First select an icon on the right, then select the version below and copy/paste the code into your project.
-
    -
  • -

    - Font versions - - - -
    - - Background Color -
    -
    - - -
    - - -
    -
    -

    -

    - Place this in your header (once per HTML file) - - - - -

    -
    - <link rel="stylesheet" type='text/css' href="{{baseUrl}}devicon.min.css" /> -
    - -
      -
      -
    • - - -
    • -
    • - - -
    • -
      -
    -

    - Place this in your body - - - - -

    -
    - <i class="devicon-{{selectedIcon.name}}-{{selectedFontIcon}} colored"></i>
    -
    -

    *To change the size, change the <i>'s `font-size`.

    -
  • -
  • -

    - SVG versions - - - -
    - - Background Color -
    -
    - - -
    - - -
    -
    -

    -
      -
    • - - -
    • -
    -

    - Using <img> element - - - - -

    -
    - <img src="{{baseUrl}}icons/{{selectedIcon.name}}/{{selectedIcon.name}}-{{selectedIcon.svg[selectedSvgIndex]}}.svg" />
    -
    -

    *To change the size, change the <img>'s `height` and `width`.

    - -
    - -

    - Using Pure SVG - - - - -

    -
    - <svg viewBox="0 0 128 128">
    -
    {{selectedSvgIcon}}
    - </svg> -
    -

    *To change the size, change the <svg>'s `height` and `width`.

    -
  • -
- -

GitHub repository

-
If you prefer a local install, you can download all the files on the GitHub repository.
-
Check out our ultimate guide in order to compile Devicon locally.
- - - - - -

Contact

-
If you have any legal concerns regarding copyrights or want to report an abuse, please reach out to us via info@devicon.dev or our Discord community. Any code/logo contributions should be made through our GitHub repository listed above.
- -
- Originally created by Konpa (under the MIT License) and
- supported by many contributors.
- Copyright © 2015 Konpa
-
- Final font built with IcoMoon
-
- *All product names, logos, and brandsare property of their respective owners. All company, product and service names used in this website are for identification purposes only. Usage of these names, logos, and brands does not imply endorsement of Devicon or its members. All icons/SVGs in this project are not monetized in anyway. It is up to the user to use the logo properly according to the company/group's brand policy. Usage of this site or any icons/SVGs from Devicon means acknowledgement of these conditions. -
-
- -
- - - -
    -
  • Total icons: {{ icons.length }}
  • -
  • Total icon version: {{ totalFonts }}
  • -
  • Average icon versions per icon: {{ avgFonts.toFixed(0) }}
  • -
- -
    -
  • - - -

    {{icon.name}}

    -
    -
  • -
-
-
- - - - - - - - - - - diff --git a/docs/manifest.json b/docs/manifest.json deleted file mode 100644 index eca47a649..000000000 --- a/docs/manifest.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "Devicon", - "icons": [ - { - "src": "\/logos/android-chrome-36x36.png", - "sizes": "36x36", - "type": "image\/png", - "density": "0.75" - }, - { - "src": "\/logos/android-chrome-48x48.png", - "sizes": "48x48", - "type": "image\/png", - "density": "1.0" - }, - { - "src": "\/logos/android-chrome-72x72.png", - "sizes": "72x72", - "type": "image\/png", - "density": "1.5" - }, - { - "src": "\/logos/android-chrome-96x96.png", - "sizes": "96x96", - "type": "image\/png", - "density": "2.0" - }, - { - "src": "\/logos/android-chrome-144x144.png", - "sizes": "144x144", - "type": "image\/png", - "density": "3.0" - }, - { - "src": "\/logos/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image\/png", - "density": "4.0" - } - ] -} diff --git a/docs/wiki/import_icomoon.PNG b/docs/wiki/import_icomoon.PNG deleted file mode 100644 index aa6f93064..000000000 Binary files a/docs/wiki/import_icomoon.PNG and /dev/null differ diff --git a/docs/wiki/issue_form.png b/docs/wiki/issue_form.png deleted file mode 100644 index 13b46b187..000000000 Binary files a/docs/wiki/issue_form.png and /dev/null differ diff --git a/docs/wiki/organization_rule.PNG b/docs/wiki/organization_rule.PNG deleted file mode 100644 index 3a8ec5d57..000000000 Binary files a/docs/wiki/organization_rule.PNG and /dev/null differ diff --git a/docs/wiki/svg_standards.PNG b/docs/wiki/svg_standards.PNG deleted file mode 100644 index 4df0e4a37..000000000 Binary files a/docs/wiki/svg_standards.PNG and /dev/null differ diff --git a/icons/githubcopilot/githubcopilot-original-wordmark.svg b/icons/githubcopilot/githubcopilot-original-wordmark.svg index 0e61c98ed..be5e6221f 100644 --- a/icons/githubcopilot/githubcopilot-original-wordmark.svg +++ b/icons/githubcopilot/githubcopilot-original-wordmark.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/icons/githubcopilot/githubcopilot-original.svg b/icons/githubcopilot/githubcopilot-original.svg index 4d3d0d75b..332bd024d 100644 --- a/icons/githubcopilot/githubcopilot-original.svg +++ b/icons/githubcopilot/githubcopilot-original.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/package.json b/package.json index aab7b26e7..88efa00fb 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,11 @@ "build-icons:linux:macOS": "python ./.github/scripts/icomoon_build_githubless.py ./.github/scripts/build_assets/geckodriver-v0.32.2-linux64/geckodriver ./icomoon.json ./devicon.json ./icons ./ --headless", "build-icons:windows": "python ./.github/scripts/icomoon_build_githubless.py ./.github/scripts/build_assets/geckodriver-v0.32.2-win64/geckodriver.exe ./icomoon.json ./devicon.json ./icons ./ --headless", "build-icons:default": "echo 'Unsupported OS' && exit 1", - "dev": "concurrently \"npm:open-browser\" \"npm:start-local-server\"", - "start-local-server": "python -m http.server 8000", - "open-browser": "node -e \"import('open').then(pkg => pkg.default('http://localhost:8000/docs'))\"" + "dev": "npm run site:dev", + "site:dev": "pnpm --dir site dev", + "site:build": "pnpm --dir site build", + "site:preview": "pnpm --dir site preview", + "site:check": "pnpm --dir site check" }, "repository": { "type": "git", diff --git a/site/.gitignore b/site/.gitignore new file mode 100644 index 000000000..4e120c1bc --- /dev/null +++ b/site/.gitignore @@ -0,0 +1,26 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +# Generated assets +/static/devicon.json diff --git a/site/.npmrc b/site/.npmrc new file mode 100644 index 000000000..dd75c6b69 --- /dev/null +++ b/site/.npmrc @@ -0,0 +1,2 @@ +engine-strict=true +verify-deps-before-run=false diff --git a/site/LICENSE b/site/LICENSE new file mode 100644 index 000000000..f306172b9 --- /dev/null +++ b/site/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) 2025 - present devicon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/site/README.md b/site/README.md new file mode 100644 index 000000000..c65b7f84c --- /dev/null +++ b/site/README.md @@ -0,0 +1,36 @@ +# Devicon.dev + +Website for devicon + +## Running locally + +You'll need node and pnpm setup. + +First install dependencies with: + +```bash +pnpm install +``` + +Then start server with: + +```bash +pnpm run dev + +# or start the server and open the app in a new browser tab +pnpm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +pnpm run build +``` + +You can preview the production build with `pnpm run preview`. + +## Acknowledgements + +Design for this webapp is heavily inspired by [@risbi0](https://github.com/risbi0)'s [work](https://risbi0.github.io/devicon-new-ui/) on revamping devicon website diff --git a/site/biome.json b/site/biome.json new file mode 100644 index 000000000..1dbde9f84 --- /dev/null +++ b/site/biome.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.0/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "src/**/*.ts", + "src/**/*.js", + "vite.config.ts", + "svelte.config.js" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "indentWidth": 2, + "semicolons": "asNeeded" + } + }, + "assist": { + "enabled": true, + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/site/package.json b/site/package.json new file mode 100644 index 000000000..a25065434 --- /dev/null +++ b/site/package.json @@ -0,0 +1,34 @@ +{ + "name": "devicons.dev", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "format": "biome format --write .", + "lint": "biome lint ." + }, + "devDependencies": { + "@biomejs/biome": "2.2.0", + "@sveltejs/adapter-auto": "^4.0.0", + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.20.6", + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "@types/node": "^26.6.6", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "typescript": "^5.0.0", + "vite": "^6.4.1", + "vitest": "^3.2.4" + }, + "dependencies": { + "@tailwindcss/vite": "^4.0.12", + "devicon": "^2.18.0", + "tailwindcss": "^4.0.12" + } +} diff --git a/site/pnpm-lock.yaml b/site/pnpm-lock.yaml new file mode 100644 index 000000000..fbafeefa0 --- /dev/null +++ b/site/pnpm-lock.yaml @@ -0,0 +1,1770 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tailwindcss/vite': + specifier: ^4.0.12 + version: 4.0.12(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + devicon: + specifier: ^2.18.0 + version: 2.18.0 + tailwindcss: + specifier: ^4.0.12 + version: 4.0.12 + devDependencies: + '@biomejs/biome': + specifier: 2.2.0 + version: 2.2.0 + '@sveltejs/adapter-auto': + specifier: ^4.0.0 + version: 4.0.0(@sveltejs/kit@2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))) + '@sveltejs/adapter-static': + specifier: ^3.0.10 + version: 3.0.10(@sveltejs/kit@2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))) + '@sveltejs/kit': + specifier: ^2.20.6 + version: 2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + '@sveltejs/vite-plugin-svelte': + specifier: ^5.0.0 + version: 5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + '@types/node': + specifier: ^26.6.6 + version: 26.6.6 + svelte: + specifier: ^5.0.0 + version: 5.20.5 + svelte-check: + specifier: ^4.0.0 + version: 4.1.4(picomatch@4.0.3)(svelte@5.20.5)(typescript@5.8.2) + typescript: + specifier: ^5.0.0 + version: 5.8.2 + vite: + specifier: ^6.4.1 + version: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + +packages: + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@biomejs/biome@2.2.0': + resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.0': + resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.0': + resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.0': + resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.0': + resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.0': + resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.0': + resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.0': + resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.0': + resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + cpu: [x64] + os: [win32] + + '@sveltejs/adapter-auto@4.0.0': + resolution: {integrity: sha512-kmuYSQdD2AwThymQF0haQhM8rE5rhutQXG4LNbnbShwhMO4qQGnKaaTy+88DuNSuoQDi58+thpq8XpHc1+oEKQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/adapter-static@3.0.10': + resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.20.6': + resolution: {integrity: sha512-ImUkSQ//Xf4N9r0HHAe5vRA7RyQ7U1Ue1YUT235Ig+IiIqbsixEulHTHrP5LtBiC8xOkJoPZQ1VZ/nWHNOaGGw==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + vite: ^5.0.3 || ^6.0.0 + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1': + resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^5.0.0 + svelte: ^5.0.0 + vite: ^6.0.0 + + '@sveltejs/vite-plugin-svelte@5.0.3': + resolution: {integrity: sha512-MCFS6CrQDu1yGwspm4qtli0e63vaPCehf6V7pIMP15AsWgMKrqDGCPFF/0kn4SP0ii4aySu4Pa62+fIRGFMjgw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.0.0 + + '@tailwindcss/node@4.0.12': + resolution: {integrity: sha512-a6J11K1Ztdln9OrGfoM75/hChYPcHYGNYimqciMrvKXRmmPaS8XZTHhdvb5a3glz4Kd4ZxE1MnuFE2c0fGGmtg==} + + '@tailwindcss/oxide-android-arm64@4.0.12': + resolution: {integrity: sha512-dAXCaemu3mHLXcA5GwGlQynX8n7tTdvn5i1zAxRvZ5iC9fWLl5bGnjZnzrQqT7ttxCvRwdVf3IHUnMVdDBO/kQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.0.12': + resolution: {integrity: sha512-vPNI+TpJQ7sizselDXIJdYkx9Cu6JBdtmRWujw9pVIxW8uz3O2PjgGGzL/7A0sXI8XDjSyRChrUnEW9rQygmJQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.0.12': + resolution: {integrity: sha512-RL/9jM41Fdq4Efr35C5wgLx98BirnrfwuD+zgMFK6Ir68HeOSqBhW9jsEeC7Y/JcGyPd3MEoJVIU4fAb7YLg7A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.0.12': + resolution: {integrity: sha512-7WzWiax+LguJcMEimY0Q4sBLlFXu1tYxVka3+G2M9KmU/3m84J3jAIV4KZWnockbHsbb2XgrEjtlJKVwHQCoRA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': + resolution: {integrity: sha512-X9LRC7jjE1QlfIaBbXjY0PGeQP87lz5mEfLSVs2J1yRc9PSg1tEPS9NBqY4BU9v5toZgJgzKeaNltORyTs22TQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': + resolution: {integrity: sha512-i24IFNq2402zfDdoWKypXz0ZNS2G4NKaA82tgBlE2OhHIE+4mg2JDb5wVfyP6R+MCm5grgXvurcIcKWvo44QiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.0.12': + resolution: {integrity: sha512-LmOdshJBfAGIBG0DdBWhI0n5LTMurnGGJCHcsm9F//ISfsHtCnnYIKgYQui5oOz1SUCkqsMGfkAzWyNKZqbGNw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.0.12': + resolution: {integrity: sha512-OSK667qZRH30ep8RiHbZDQfqkXjnzKxdn0oRwWzgCO8CoTxV+MvIkd0BWdQbYtYuM1wrakARV/Hwp0eA/qzdbw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.0.12': + resolution: {integrity: sha512-uylhWq6OWQ8krV8Jk+v0H/3AZKJW6xYMgNMyNnUbbYXWi7hIVdxRKNUB5UvrlC3RxtgsK5EAV2i1CWTRsNcAnA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': + resolution: {integrity: sha512-XDLnhMoXZEEOir1LK43/gHHwK84V1GlV8+pAncUAIN2wloeD+nNciI9WRIY/BeFTqES22DhTIGoilSO39xDb2g==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.0.12': + resolution: {integrity: sha512-I/BbjCLpKDQucvtn6rFuYLst1nfFwSMYyPzkx/095RE+tuzk5+fwXuzQh7T3fIBTcbn82qH/sFka7yPGA50tLw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.0.12': + resolution: {integrity: sha512-DWb+myvJB9xJwelwT9GHaMc1qJj6MDXRDR0CS+T8IdkejAtu8ctJAgV4r1drQJLPeS7mNwq2UHW2GWrudTf63A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.0.12': + resolution: {integrity: sha512-JM3gp601UJiryIZ9R2bSqalzcOy15RCybQ1Q+BJqDEwVyo4LkWKeqQAcrpHapWXY31OJFTuOUVBFDWMhzHm2Bg==} + peerDependencies: + vite: ^5.2.0 || ^6 + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@26.6.6': + resolution: {integrity: sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog==} + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + devalue@5.3.2: + resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==} + + devicon@2.18.0: + resolution: {integrity: sha512-jmLZPdTfO9d0GlnTX8Qw2SxZj71My6WAVEZtgmWErVju/uSUqmk2hxKzXo9Pw7IfqV9PEwcOKrywZA1etryRPA==} + + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + engines: {node: '>=18'} + hasBin: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esrap@1.4.5: + resolution: {integrity: sha512-CjNMjkBWWZeHn+VX+gS8YvFwJ5+NDhg8aWZBSFJPR8qQduDNjbJodA2WcwCm7uQa5Rjqj+nZvVmceg1RbHFB9g==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + lightningcss-darwin-arm64@1.29.2: + resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.29.2: + resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.29.2: + resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.29.2: + resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.29.2: + resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.29.2: + resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.29.2: + resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.29.2: + resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.29.2: + resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.29.2: + resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.29.2: + resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==} + engines: {node: '>= 12.0.0'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + svelte-check@4.1.4: + resolution: {integrity: sha512-v0j7yLbT29MezzaQJPEDwksybTE2Ups9rUxEXy92T06TiA0cbqcO8wAOwNUVkFW6B0hsYHA+oAX3BS8b/2oHtw==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte@5.20.5: + resolution: {integrity: sha512-dpu2lTPVsAAgZFKpF7A9741sBCdXGogfxFU4aQeVgun7GVNCSVheTzj0FsT7g9OsLhBaMX4lKLwVIvmzQGytmQ==} + engines: {node: '>=18'} + + tailwindcss@4.0.12: + resolution: {integrity: sha512-bT0hJo91FtncsAMSsMzUkoo/iEU0Xs5xgFgVC9XmdM9bw5MhZuQFjPNl6wxAE0SiQF/YTZJa+PndGWYSDtuxAg==} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.0.6: + resolution: {integrity: sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + vite: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 + + '@biomejs/biome@2.2.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.0 + '@biomejs/cli-darwin-x64': 2.2.0 + '@biomejs/cli-linux-arm64': 2.2.0 + '@biomejs/cli-linux-arm64-musl': 2.2.0 + '@biomejs/cli-linux-x64': 2.2.0 + '@biomejs/cli-linux-x64-musl': 2.2.0 + '@biomejs/cli-win32-arm64': 2.2.0 + '@biomejs/cli-win32-x64': 2.2.0 + + '@biomejs/cli-darwin-arm64@2.2.0': + optional: true + + '@biomejs/cli-darwin-x64@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64@2.2.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-x64@2.2.0': + optional: true + + '@biomejs/cli-win32-arm64@2.2.0': + optional: true + + '@biomejs/cli-win32-x64@2.2.0': + optional: true + + '@esbuild/aix-ppc64@0.25.11': + optional: true + + '@esbuild/android-arm64@0.25.11': + optional: true + + '@esbuild/android-arm@0.25.11': + optional: true + + '@esbuild/android-x64@0.25.11': + optional: true + + '@esbuild/darwin-arm64@0.25.11': + optional: true + + '@esbuild/darwin-x64@0.25.11': + optional: true + + '@esbuild/freebsd-arm64@0.25.11': + optional: true + + '@esbuild/freebsd-x64@0.25.11': + optional: true + + '@esbuild/linux-arm64@0.25.11': + optional: true + + '@esbuild/linux-arm@0.25.11': + optional: true + + '@esbuild/linux-ia32@0.25.11': + optional: true + + '@esbuild/linux-loong64@0.25.11': + optional: true + + '@esbuild/linux-mips64el@0.25.11': + optional: true + + '@esbuild/linux-ppc64@0.25.11': + optional: true + + '@esbuild/linux-riscv64@0.25.11': + optional: true + + '@esbuild/linux-s390x@0.25.11': + optional: true + + '@esbuild/linux-x64@0.25.11': + optional: true + + '@esbuild/netbsd-arm64@0.25.11': + optional: true + + '@esbuild/netbsd-x64@0.25.11': + optional: true + + '@esbuild/openbsd-arm64@0.25.11': + optional: true + + '@esbuild/openbsd-x64@0.25.11': + optional: true + + '@esbuild/openharmony-arm64@0.25.11': + optional: true + + '@esbuild/sunos-x64@0.25.11': + optional: true + + '@esbuild/win32-arm64@0.25.11': + optional: true + + '@esbuild/win32-ia32@0.25.11': + optional: true + + '@esbuild/win32-x64@0.25.11': + optional: true + + '@jridgewell/gen-mapping@0.3.8': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@polka/url@1.0.0-next.29': {} + + '@rollup/rollup-android-arm-eabi@4.52.5': + optional: true + + '@rollup/rollup-android-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.5': + optional: true + + '@rollup/rollup-darwin-x64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.52.5': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-linux-x64-musl@4.52.5': + optional: true + + '@rollup/rollup-openharmony-arm64@4.52.5': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.5': + optional: true + + '@sveltejs/adapter-auto@4.0.0(@sveltejs/kit@2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))': + dependencies: + '@sveltejs/kit': 2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + import-meta-resolve: 4.2.0 + + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))': + dependencies: + '@sveltejs/kit': 2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + + '@sveltejs/kit@2.20.6(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + '@types/cookie': 0.6.0 + cookie: 0.6.0 + devalue: 5.3.2 + esm-env: 1.2.2 + import-meta-resolve: 4.2.0 + kleur: 4.1.5 + magic-string: 0.30.19 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 2.7.1 + sirv: 3.0.2 + svelte: 5.20.5 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + debug: 4.4.0 + svelte: 5.20.5 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + transitivePeerDependencies: + - supports-color + + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)))(svelte@5.20.5)(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + debug: 4.4.0 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.17 + svelte: 5.20.5 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + vitefu: 1.0.6(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + transitivePeerDependencies: + - supports-color + + '@tailwindcss/node@4.0.12': + dependencies: + enhanced-resolve: 5.18.1 + jiti: 2.4.2 + tailwindcss: 4.0.12 + + '@tailwindcss/oxide-android-arm64@4.0.12': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.0.12': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.0.12': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.0.12': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.0.12': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.0.12': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.0.12': + optional: true + + '@tailwindcss/oxide@4.0.12': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.0.12 + '@tailwindcss/oxide-darwin-arm64': 4.0.12 + '@tailwindcss/oxide-darwin-x64': 4.0.12 + '@tailwindcss/oxide-freebsd-x64': 4.0.12 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.12 + '@tailwindcss/oxide-linux-arm64-gnu': 4.0.12 + '@tailwindcss/oxide-linux-arm64-musl': 4.0.12 + '@tailwindcss/oxide-linux-x64-gnu': 4.0.12 + '@tailwindcss/oxide-linux-x64-musl': 4.0.12 + '@tailwindcss/oxide-win32-arm64-msvc': 4.0.12 + '@tailwindcss/oxide-win32-x64-msvc': 4.0.12 + + '@tailwindcss/vite@4.0.12(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@tailwindcss/node': 4.0.12 + '@tailwindcss/oxide': 4.0.12 + lightningcss: 1.29.2 + tailwindcss: 4.0.12 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/cookie@0.6.0': {} + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.6': {} + + '@types/estree@1.0.8': {} + + '@types/node@26.6.6': + dependencies: + undici-types: 8.9.0 + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + acorn-typescript@1.4.13(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + + acorn@8.14.0: {} + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + axobject-query@4.1.0: {} + + cac@6.7.14: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + check-error@2.1.1: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + clsx@2.1.1: {} + + cookie@0.6.0: {} + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deepmerge@4.3.1: {} + + detect-libc@2.0.3: {} + + devalue@5.3.2: {} + + devicon@2.18.0: {} + + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + + es-module-lexer@1.7.0: {} + + esbuild@0.25.11: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 + + esm-env@1.2.2: {} + + esrap@1.4.5: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.2.2: {} + + fdir@6.4.3(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + import-meta-resolve@4.2.0: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.6 + + jiti@2.4.2: {} + + js-tokens@9.0.1: {} + + kleur@4.1.5: {} + + lightningcss-darwin-arm64@1.29.2: + optional: true + + lightningcss-darwin-x64@1.29.2: + optional: true + + lightningcss-freebsd-x64@1.29.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.29.2: + optional: true + + lightningcss-linux-arm64-gnu@1.29.2: + optional: true + + lightningcss-linux-arm64-musl@1.29.2: + optional: true + + lightningcss-linux-x64-gnu@1.29.2: + optional: true + + lightningcss-linux-x64-musl@1.29.2: + optional: true + + lightningcss-win32-arm64-msvc@1.29.2: + optional: true + + lightningcss-win32-x64-msvc@1.29.2: + optional: true + + lightningcss@1.29.2: + dependencies: + detect-libc: 2.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.29.2 + lightningcss-darwin-x64: 1.29.2 + lightningcss-freebsd-x64: 1.29.2 + lightningcss-linux-arm-gnueabihf: 1.29.2 + lightningcss-linux-arm64-gnu: 1.29.2 + lightningcss-linux-arm64-musl: 1.29.2 + lightningcss-linux-x64-gnu: 1.29.2 + lightningcss-linux-x64-musl: 1.29.2 + lightningcss-win32-arm64-msvc: 1.29.2 + lightningcss-win32-x64-msvc: 1.29.2 + + locate-character@3.0.0: {} + + loupe@3.2.1: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + readdirp@4.1.2: {} + + rollup@4.52.5: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + set-cookie-parser@2.7.1: {} + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + svelte-check@4.1.4(picomatch@4.0.3)(svelte@5.20.5)(typescript@5.8.2): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.3 + fdir: 6.4.3(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.20.5 + typescript: 5.8.2 + transitivePeerDependencies: + - picomatch + + svelte@5.20.5: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + acorn: 8.14.0 + acorn-typescript: 1.4.13(acorn@8.14.0) + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 1.4.5 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.17 + zimmerframe: 1.1.2 + + tailwindcss@4.0.12: {} + + tapable@2.2.1: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + totalist@3.0.1: {} + + typescript@5.8.2: {} + + undici-types@8.9.0: {} + + vite-node@3.2.4(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.5 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 26.6.6 + fsevents: 2.3.3 + jiti: 2.4.2 + lightningcss: 1.29.2 + + vitefu@1.0.6(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)): + optionalDependencies: + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + + vitest@3.2.4(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.1(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + vite-node: 3.2.4(@types/node@26.6.6)(jiti@2.4.2)(lightningcss@1.29.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.6.6 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + zimmerframe@1.1.2: {} diff --git a/site/pnpm-workspace.yaml b/site/pnpm-workspace.yaml new file mode 100644 index 000000000..5ed0b5af0 --- /dev/null +++ b/site/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +allowBuilds: + esbuild: true diff --git a/site/src/app.css b/site/src/app.css new file mode 100644 index 000000000..7e220c561 --- /dev/null +++ b/site/src/app.css @@ -0,0 +1,144 @@ +@import "tailwindcss"; +@custom-variant dark (&:where(.dark, .dark *)); + +@theme { + --font-sans: "Geist", "Geist Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + + --color-dark-bg: #09090b; + --color-surface-900: #11131a; + --color-surface-850: #181b24; + --color-surface-800: #222634; + + /* Devicon Brand Tokens */ + --color-devicon-primary: #10b981; + --color-devicon-primary-dark: #059669; + --color-devicon-primary-light: #34d399; + --color-devicon-accent: #3ecf8e; + --color-devicon-shield: #60be86; + --color-devicon-shield-light: #65d693; + --color-devicon-shield-dark: #558d6c; + --color-devicon-shield-mid: #5aa579; + + --color-devicon-emerald: #10b981; + --color-devicon-emerald-light: #34d399; + --color-devicon-emerald-dark: #059669; + + --color-border-subtle: rgba(255, 255, 255, 0.07); + --color-border-hover: rgba(255, 255, 255, 0.15); + + --primaryColor: #10b981; + --primaryColorDark: #059669; + --primaryColorLight: #34d399; +} + +html { + font-family: var(--font-sans); + scroll-behavior: smooth; +} + +/* Prevent full-page repaint/layout lag across 600+ icon cards during theme toggle */ +html.disable-transitions, +html.disable-transitions *:not(.theme-toggle-icon, .theme-toggle-icon *) { + transition: none !important; +} + +.base-container { + @apply mx-auto w-full px-4 sm:px-6 lg:px-8 max-w-7xl; +} + +.checkerboard { + background-image: repeating-conic-gradient(#d1d5db 0% 25%, #f3f4f6 0% 50%); + background-size: 16px 16px; + background-color: #f3f4f6; +} + +:global(.dark) .checkerboard, +.dark .checkerboard { + background-image: repeating-conic-gradient(#222634 0% 25%, #11131a 0% 50%); + background-size: 16px 16px; + background-color: #11131a; +} + +.no-scrollbar, +.code-snippet { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.no-scrollbar::-webkit-scrollbar, +.code-snippet::-webkit-scrollbar { + display: none; +} + +@keyframes marquee { + 0% { + transform: translateX(0%); + } + 100% { + transform: translateX(-50%); + } +} + +.animate-marquee { + display: flex; + width: max-content; + animation: marquee 40s linear infinite; +} + +.animate-marquee:hover { + animation-play-state: paused; +} + +@media (prefers-reduced-motion: reduce) { + *, + ::before, + ::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } + + .animate-marquee { + animation: none !important; + } +} + +/* Devicon Developer Grid Background */ +.bg-devicon-grid { + background-image: + linear-gradient(to right, rgba(16, 185, 129, 0.05) 1px, transparent 1px), + linear-gradient(to bottom, rgba(16, 185, 129, 0.05) 1px, transparent 1px); + background-size: 36px 36px; +} + +:global(.dark) .bg-devicon-grid, +.dark .bg-devicon-grid { + background-image: + linear-gradient(to right, rgba(16, 185, 129, 0.04) 1px, transparent 1px), + linear-gradient(to bottom, rgba(16, 185, 129, 0.04) 1px, transparent 1px); + background-size: 36px 36px; +} + +/* Devicon Engineering Margin Stripes */ +.bg-devicon-stripes { + background-image: repeating-linear-gradient( + -45deg, + rgba(16, 185, 129, 0.05) 0, + rgba(16, 185, 129, 0.05) 1px, + transparent 0, + transparent 10px + ); +} + +:global(.dark) .bg-devicon-stripes, +.dark .bg-devicon-stripes { + background-image: repeating-linear-gradient( + -45deg, + rgba(16, 185, 129, 0.07) 0, + rgba(16, 185, 129, 0.07) 1px, + transparent 0, + transparent 10px + ); +} diff --git a/site/src/app.d.ts b/site/src/app.d.ts new file mode 100644 index 000000000..c0c081684 --- /dev/null +++ b/site/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {} diff --git a/site/src/app.html b/site/src/app.html new file mode 100644 index 000000000..4bd62609c --- /dev/null +++ b/site/src/app.html @@ -0,0 +1,27 @@ + + + + + DEVICON | All programming languages and development tools related icons font + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/site/src/components/darkMode/DarkModeToggle.svelte b/site/src/components/darkMode/DarkModeToggle.svelte new file mode 100644 index 000000000..6d0f04d9c --- /dev/null +++ b/site/src/components/darkMode/DarkModeToggle.svelte @@ -0,0 +1,41 @@ + + + diff --git a/site/src/components/filters/CategoryChips.svelte b/site/src/components/filters/CategoryChips.svelte new file mode 100644 index 000000000..cfdb7c4a2 --- /dev/null +++ b/site/src/components/filters/CategoryChips.svelte @@ -0,0 +1,45 @@ + + +
+ {#each categories as category} + {@const isActive = $selectedCategory === category.id} + {@const count = $categoryCounts?.[category.id] ?? 0} + + {/each} +
diff --git a/site/src/components/filters/FiltersBar.svelte b/site/src/components/filters/FiltersBar.svelte new file mode 100644 index 000000000..2facd5795 --- /dev/null +++ b/site/src/components/filters/FiltersBar.svelte @@ -0,0 +1,63 @@ + + +
+
+ + + + +
+ +
+ +
+ + +
+ +
+ +
+ + + + {#if hasActiveFilters} + + {/if} +
+
+
+
diff --git a/site/src/components/filters/SearchBar.svelte b/site/src/components/filters/SearchBar.svelte new file mode 100644 index 000000000..27d244575 --- /dev/null +++ b/site/src/components/filters/SearchBar.svelte @@ -0,0 +1,40 @@ + + +
+
+ + + + +
+ + + + {#if $searchTerm} + + {/if} +
diff --git a/site/src/components/filters/SortDropdown.svelte b/site/src/components/filters/SortDropdown.svelte new file mode 100644 index 000000000..bfde8959b --- /dev/null +++ b/site/src/components/filters/SortDropdown.svelte @@ -0,0 +1,42 @@ + + +
+ + + +
diff --git a/site/src/components/filters/TagsDropdown.svelte b/site/src/components/filters/TagsDropdown.svelte new file mode 100644 index 000000000..978ecf239 --- /dev/null +++ b/site/src/components/filters/TagsDropdown.svelte @@ -0,0 +1,99 @@ + + +
+ + + {#if isDropdownOpen} + + + +
+ +
+ {#each $availableTags as tag} + {@const isChecked = $selectedTags.includes(tag)} + + {/each} +
+ + +
+ +
+
+ {/if} +
diff --git a/site/src/components/filters/VersionDropdown.svelte b/site/src/components/filters/VersionDropdown.svelte new file mode 100644 index 000000000..0178d7615 --- /dev/null +++ b/site/src/components/filters/VersionDropdown.svelte @@ -0,0 +1,45 @@ + + +
+ + + +
diff --git a/site/src/components/footer/Footer.svelte b/site/src/components/footer/Footer.svelte new file mode 100644 index 000000000..8249237f6 --- /dev/null +++ b/site/src/components/footer/Footer.svelte @@ -0,0 +1,156 @@ + + + diff --git a/site/src/components/header/Header.svelte b/site/src/components/header/Header.svelte new file mode 100644 index 000000000..3b5cec1d6 --- /dev/null +++ b/site/src/components/header/Header.svelte @@ -0,0 +1,34 @@ + + +
+
+ Devicon + + v{$latestVersion} + +
+

+ Icons about programming languages, software development, and graphic design + tools. +

+ + + + +
+ + diff --git a/site/src/components/header/HeaderSnippet.svelte b/site/src/components/header/HeaderSnippet.svelte new file mode 100644 index 000000000..487c7c810 --- /dev/null +++ b/site/src/components/header/HeaderSnippet.svelte @@ -0,0 +1,37 @@ + + +
+

For CSS usage, paste the code in the <head> of your HTML

+
+ {snippet} + + + +
+
diff --git a/site/src/components/header/HeaderStats.svelte b/site/src/components/header/HeaderStats.svelte new file mode 100644 index 000000000..03bfcb84d --- /dev/null +++ b/site/src/components/header/HeaderStats.svelte @@ -0,0 +1,19 @@ + +
+
+
Total Icons
+
+ {$icons.length} +
+
+
+
Total Versions
+
+ {$totalVersions} +
+
+
\ No newline at end of file diff --git a/site/src/components/hero/Hero.svelte b/site/src/components/hero/Hero.svelte new file mode 100644 index 000000000..e0a759907 --- /dev/null +++ b/site/src/components/hero/Hero.svelte @@ -0,0 +1,106 @@ + + +
+
+ +

+ Icons for developers, developers, developers! +

+ + +

+ {$icons.length > 0 ? $icons.length.toLocaleString() : "660"} technology icons and {$totalVersions > 0 ? $totalVersions.toLocaleString() : "2,100"} SVG variants for modern software engineering. Free and open source under MIT. +

+ + +
+ +
+ + +
+
+
+ + + + +
+ + + +
+ + +
+
+
+
+ + +
+ +
+
diff --git a/site/src/components/hero/HeroMosaic.svelte b/site/src/components/hero/HeroMosaic.svelte new file mode 100644 index 000000000..cc812f70a --- /dev/null +++ b/site/src/components/hero/HeroMosaic.svelte @@ -0,0 +1,158 @@ + + +
+ +
+ +
+
+ + Technology Mosaic +
+
+ v{$latestVersion} + โ€ข + {$icons.length > 0 ? $icons.length.toLocaleString() : "660"} ICONS +
+
+ + +
+ {#each mosaicItems as item (item.name)} + {#if item.isCenter} + + + {:else} + {@const iconObj = getIcon(item.name, item.version)} + {@const isDark = isDarkLogo(iconObj)} + {@const isWhite = isWhiteLogo(iconObj)} + + {/if} + {/each} +
+
diff --git a/site/src/components/hero/IconMarquee.svelte b/site/src/components/hero/IconMarquee.svelte new file mode 100644 index 000000000..fc16650e0 --- /dev/null +++ b/site/src/components/hero/IconMarquee.svelte @@ -0,0 +1,62 @@ + + +
+
+ {#each [...marqueeIcons, ...marqueeIcons] as item, idx (`${item.name}-${idx}`)} + + {/each} +
+
diff --git a/site/src/components/iconGrid/IconCard.svelte b/site/src/components/iconGrid/IconCard.svelte new file mode 100644 index 000000000..8bd93bdc6 --- /dev/null +++ b/site/src/components/iconGrid/IconCard.svelte @@ -0,0 +1,86 @@ + + + diff --git a/site/src/components/iconGrid/IconGrid.svelte b/site/src/components/iconGrid/IconGrid.svelte new file mode 100644 index 000000000..e7ede58a0 --- /dev/null +++ b/site/src/components/iconGrid/IconGrid.svelte @@ -0,0 +1,70 @@ + + +
+ +
+
+ {#if $isLoading} + Loading developer icons... + {:else} + Showing {icons.length} icons + {/if} +
+
+ + {#if $isLoading} + +
+ {#each Array(24) as _, i (i)} +
+
+
+
+
+ {/each} +
+ {:else if icons.length === 0} + +
+
+ + + + + +
+ +

+ No icons found matching your filters +

+

+ Try adjusting your search query, selecting a different category, or resetting all filters. +

+ + +
+ {:else} + +
+ {#each icons as icon (icon.name)} + + {/each} +
+ {/if} +
diff --git a/site/src/components/integrations/IntegrationsSection.svelte b/site/src/components/integrations/IntegrationsSection.svelte new file mode 100644 index 000000000..183f1f617 --- /dev/null +++ b/site/src/components/integrations/IntegrationsSection.svelte @@ -0,0 +1,191 @@ + + +
+
+ +
+

+ Integrations +

+

+ Official React component package and global CDN distribution for Devicon. +

+
+ + +
+
+

+ React Components +

+ + + Official Package + +
+ +
+
+ +
+

+ Type-safe React SVG components. Each icon is individually exported for zero runtime overhead and automatic tree-shaking. +

+ + +
+
+ {#each (["npm", "pnpm", "yarn", "bun"] as const) as pkg} + + {/each} +
+ +
+ {pkgCommands[activePkg]} + +
+
+ + + +
+ + +
+ +
+
+
+
+ + +
+
+

+ CDN & Webfont Distribution +

+ + + jsDelivr Global CDN + +
+ +
+
+
+

+ Include Devicon in any web application or static HTML file without build steps. Add the stylesheet to your <head> and use standard CSS icon classes. +

+
+ Tip: Append colored to display original brand colors, or omit it to inherit the parent CSS text color. +
+
+ +
+ + +
+
+
+
+
+
diff --git a/site/src/components/modal/IconModal.svelte b/site/src/components/modal/IconModal.svelte new file mode 100644 index 000000000..79f597356 --- /dev/null +++ b/site/src/components/modal/IconModal.svelte @@ -0,0 +1,735 @@ + + + + {#if $selectedIcon} + + + + +
+ +
+ +
+ +
+ {#if rawSvgText && !isLoadingSvg} + +
+ {@html rawSvgText} +
+ {:else if currentSvgUrl} + {`${$selectedIcon.name} handleImageError(e, selectedVersion)} + class="size-full object-contain transition-transform {isDark && bgMode === 'dark' ? 'invert' : ''} {isWhite && bgMode === 'light' ? 'invert' : ''}" + /> + {/if} +
+ + + {#if copiedFeedback} +
+ + + + {copiedFeedback} +
+ {/if} +
+ + +
+ +
+ +
+ Surface: + + + + +
+ + + +
+ + +
+ Scale: +
+ {#each previewSizes as size} + + {/each} +
+
+
+ + +
+
+ + Variants ({availableVariants.length}) + + + โ† + โ†’ cycle + +
+ +
+ {#each availableVariants as version} + {@const isSelected = selectedVersion === version} + + {/each} +
+
+ + +
+ +
+ + + + + +
+ + +
+ + + +
+
+
+ + +
+ +
+ + + +
+ + + {#if activeTab === "react"} + +
+ +
+ Install: +
+ {#each (["npm", "pnpm", "yarn", "bun"] as const) as pkg} + + {/each} +
+
+ + + + + + `} + language="tsx" + code={reactSnippet} + /> + + +
+ + Official package: react-devicons + + + Docs + โ†’ + +
+
+ {:else if activeTab === "html"} + +
+ +
+
+ + Webfont Live Preview + + {#if fontVersions.length > 1} +
+ Version: + {#each fontVersions as fVer} + + {/each} +
+ {/if} +
+ + +
+ +
+ +

+ Rendered with CSS class devicon-{$selectedIcon.name}-{effectiveFontVersion}{coloredSuffix}. +

+
+ + + + + +
+ + + Inherits currentColor to adapt seamlessly to light and dark backgrounds. + +
+ + +
+ + {#if isDark} + + Note: This icon has dark brand coloring and may have low contrast on dark backgrounds. Use the theme-adaptive snippet above for dark modes. + + {/if} +
+
+ {:else} + +
+ {#if isLoadingSvg} +
+ Loading SVG markup... +
+ {:else if svgFetchError} +
+ Failed to load raw SVG markup. You can download the SVG file directly using the button on the left. +
+ {:else} + + + + {/if} +
+ {/if} +
+
+ {/if} +
diff --git a/site/src/components/modal/IconModalHeader.svelte b/site/src/components/modal/IconModalHeader.svelte new file mode 100644 index 000000000..794d3a1c3 --- /dev/null +++ b/site/src/components/modal/IconModalHeader.svelte @@ -0,0 +1,34 @@ + + +
+
+ + {#if tags && tags.length > 0} + + {/if} +
+ + +
diff --git a/site/src/components/modal/IconSelector/FontVersionSelector.svelte b/site/src/components/modal/IconSelector/FontVersionSelector.svelte new file mode 100644 index 000000000..f935be49f --- /dev/null +++ b/site/src/components/modal/IconSelector/FontVersionSelector.svelte @@ -0,0 +1,100 @@ + + +
+

{title}

+ +
+ +{#if versions.length > 0} +
+ {#each versions as version} + + {/each} +
+ +
+ {codeSnippet} + + + +
+{:else} +

No font versions available

+{/if} diff --git a/site/src/components/modal/IconSelector/SVGVersionSelector.svelte b/site/src/components/modal/IconSelector/SVGVersionSelector.svelte new file mode 100644 index 000000000..4a546006d --- /dev/null +++ b/site/src/components/modal/IconSelector/SVGVersionSelector.svelte @@ -0,0 +1,306 @@ + + +

{title}

+ +{#if versions.length > 0} +
+ {#each versions as version} + + {/each} +
+ +
+
+ + +
+ +
+ {#if activeTab === "svg"} + + + + + + {:else} + + + + {/if} +
+
+ +
+ {#if activeTab === "svg"} + {#if isLoadingSvg} + Loading SVG content... + {:else if svgFetchError} + Failed to load SVG markup. + {:else} +
{formattedSvgText}
+ {/if} + {:else} +
{imgSnippet}
+ {/if} +
+{:else} +

No SVG versions available

+{/if} diff --git a/site/src/components/modal/Modal.svelte b/site/src/components/modal/Modal.svelte new file mode 100644 index 000000000..114c26cd0 --- /dev/null +++ b/site/src/components/modal/Modal.svelte @@ -0,0 +1,31 @@ + + +{#if isOpen} + +{/if} diff --git a/site/src/components/navigation/Navbar.svelte b/site/src/components/navigation/Navbar.svelte new file mode 100644 index 000000000..23af41b89 --- /dev/null +++ b/site/src/components/navigation/Navbar.svelte @@ -0,0 +1,195 @@ + + +
+ +
+ + + + {#if isMobileMenuOpen} + + {/if} +
diff --git a/site/src/components/navigation/ThemeToggle.svelte b/site/src/components/navigation/ThemeToggle.svelte new file mode 100644 index 000000000..0fad58132 --- /dev/null +++ b/site/src/components/navigation/ThemeToggle.svelte @@ -0,0 +1,52 @@ + + + diff --git a/site/src/components/widgets/code/SyntaxCodeBlock.svelte b/site/src/components/widgets/code/SyntaxCodeBlock.svelte new file mode 100644 index 000000000..cd9847f80 --- /dev/null +++ b/site/src/components/widgets/code/SyntaxCodeBlock.svelte @@ -0,0 +1,128 @@ + + +
+ +
+
+ {#if title} + {title} + {/if} + {#if language} + + {language} + + {/if} +
+ + +
+ + +
+
{@html highlightedHtml}
+
+
diff --git a/site/src/components/widgets/contact/ContactLinks.svelte b/site/src/components/widgets/contact/ContactLinks.svelte new file mode 100644 index 000000000..4aeba74d1 --- /dev/null +++ b/site/src/components/widgets/contact/ContactLinks.svelte @@ -0,0 +1,42 @@ + diff --git a/site/src/components/widgets/tooltip/Tooltip.svelte b/site/src/components/widgets/tooltip/Tooltip.svelte new file mode 100644 index 000000000..bb764fee0 --- /dev/null +++ b/site/src/components/widgets/tooltip/Tooltip.svelte @@ -0,0 +1,51 @@ + + + + + diff --git a/site/src/lib/common/copyToClipboard.ts b/site/src/lib/common/copyToClipboard.ts new file mode 100644 index 000000000..2c44184a2 --- /dev/null +++ b/site/src/lib/common/copyToClipboard.ts @@ -0,0 +1,2 @@ +export const copyToClipboard = async (text: string) => + navigator.clipboard?.writeText(text) diff --git a/site/src/lib/common/navigation.ts b/site/src/lib/common/navigation.ts new file mode 100644 index 000000000..dca85c744 --- /dev/null +++ b/site/src/lib/common/navigation.ts @@ -0,0 +1,16 @@ +export function scrollToSection(id: string, e?: Event): void { + if (e) { + e.preventDefault() + } + + if (typeof document !== "undefined") { + const el = document.getElementById(id) + if (el) { + el.scrollIntoView({ behavior: "smooth", block: "start" }) + } + } + + if (typeof window !== "undefined" && window.location.hash) { + window.history.replaceState(null, "", window.location.pathname + window.location.search) + } +} diff --git a/site/src/lib/index.ts b/site/src/lib/index.ts new file mode 100644 index 000000000..856f2b6c3 --- /dev/null +++ b/site/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/site/src/lib/stores/iconStore.ts b/site/src/lib/stores/iconStore.ts new file mode 100644 index 000000000..a2328f66d --- /dev/null +++ b/site/src/lib/stores/iconStore.ts @@ -0,0 +1,549 @@ +import { writable, derived, get, type Writable, type Readable } from "svelte/store" +import { base } from "$app/paths" +import { + CURRENT_VERSION, + type IIcon, + type IconVersions, + type SortOption, + type CategoryOption, +} from "$lib/types/types" + +export const icons: Writable = writable([]) +export const availableTags: Writable = writable([]) +export const searchTerm: Writable = writable("") +export const selectedTags: Writable = writable([]) +export const selectedCategory: Writable = writable("all") +export const selectedVersionFilter: Writable = writable("all") +export const isDarkMode: Writable = writable(true) +export const selectedIcon: Writable = writable(null) +export const isModalOpen: Writable = writable(false) +export const isLoading: Writable = writable(false) +export const error: Writable = writable(null) +export const latestVersion: Writable = writable(CURRENT_VERSION) +export const sortOption: Writable = writable("name-asc") + +export function setSortOption(option: SortOption): void { + sortOption.set(option) +} + +export function setSelectedCategory(category: CategoryOption): void { + selectedCategory.set(category) +} + +export function setSelectedVersionFilter(version: string): void { + selectedVersionFilter.set(version) +} + +const CANONICAL_ORDER = [ + "original", + "original-wordmark", + "plain", + "plain-wordmark", + "line", + "line-wordmark", +] + +export function getIconAllVariants(icon: IIcon): string[] { + if (!icon) return [] + const variants = new Set([ + ...(icon.versions?.svg || []), + ...(icon.versions?.font || []), + ]) + + for (const aliasObj of icon.aliases || []) { + if (aliasObj?.alias) { + variants.add(aliasObj.alias) + } + if (aliasObj?.base === "original" && aliasObj?.alias === "plain") { + if (variants.has("original-wordmark")) { + variants.add("plain-wordmark") + } + } + if (aliasObj?.base === "plain" && aliasObj?.alias === "original") { + if (variants.has("plain-wordmark")) { + variants.add("original-wordmark") + } + } + } + + // Second pass in case wordmark was added during alias processing + for (const aliasObj of icon.aliases || []) { + if (aliasObj?.base === "original" && aliasObj?.alias === "plain") { + if (variants.has("original-wordmark")) { + variants.add("plain-wordmark") + } + } + if (aliasObj?.base === "plain" && aliasObj?.alias === "original") { + if (variants.has("plain-wordmark")) { + variants.add("original-wordmark") + } + } + } + + return Array.from(variants).sort((a, b) => { + const idxA = CANONICAL_ORDER.indexOf(a) + const idxB = CANONICAL_ORDER.indexOf(b) + if (idxA !== -1 && idxB !== -1) return idxA - idxB + if (idxA !== -1) return -1 + if (idxB !== -1) return 1 + return a.localeCompare(b) + }) +} + +export function resolveSvgVersion(icon: IIcon, variant: string): string { + if (!icon) return "original" + + if (icon.versions?.svg?.includes(variant as IconVersions)) { + return variant + } + + for (const alias of icon.aliases || []) { + if ( + alias.alias === variant && + icon.versions?.svg?.includes(alias.base as IconVersions) + ) { + return alias.base + } + } + + if (variant.endsWith("-wordmark")) { + const root = variant.replace(/-wordmark$/, "") + for (const alias of icon.aliases || []) { + if (alias.alias === root) { + const baseWordmark = `${alias.base}-wordmark` as IconVersions + if (icon.versions?.svg?.includes(baseWordmark)) { + return baseWordmark + } + } + } + } + + return icon.versions?.svg?.[0] || "original" +} + +export function hexToHsl(hex: string): { h: number; s: number; l: number } { + if (!hex || typeof hex !== "string") { + return { h: 0, s: 0, l: 0 } + } + + let cleaned = hex.trim().replace(/^#/, "") + + // Support 3, 4, 6, or 8 digit hex codes + if (cleaned.length === 3 || cleaned.length === 4) { + cleaned = cleaned + .slice(0, 3) + .split("") + .map((c) => c + c) + .join("") + } else if (cleaned.length >= 6) { + cleaned = cleaned.slice(0, 6) + } else { + return { h: 0, s: 0, l: 0 } + } + + const num = parseInt(cleaned, 16) + if (Number.isNaN(num)) { + return { h: 0, s: 0, l: 0 } + } + + const r = ((num >> 16) & 255) / 255 + const g = ((num >> 8) & 255) / 255 + const b = (num & 255) / 255 + + const max = Math.max(r, g, b) + const min = Math.min(r, g, b) + let h = 0 + let s = 0 + const l = (max + min) / 2 + + if (max !== min) { + const d = max - min + s = l > 0.5 ? d / (2 - max - min) : d / (max + min) + + switch (max) { + case r: + h = (g - b) / d + (g < b ? 6 : 0) + break + case g: + h = (b - r) / d + 2 + break + case b: + h = (r - g) / d + 4 + break + } + h /= 6 + } + + return { h, s, l } +} + +export function isDarkLogo(icon: IIcon): boolean { + if (!icon) return false + if (icon.theme === "dark") return true + if (icon.theme === "light" || icon.theme === "color") return false + if (!icon.color) return false + const { s, l } = hexToHsl(icon.color) + return s < 0.20 && l < 0.35 +} + +export function isWhiteLogo(icon: IIcon): boolean { + if (!icon) return false + if (icon.theme === "light") return true + if (icon.theme === "dark" || icon.theme === "color") return false + if (!icon.color) return false + const { s, l } = hexToHsl(icon.color) + return s < 0.20 && l > 0.85 +} + +export function getIconSvgCandidates( + icon: IIcon, + version: IconVersions | string, + ver?: string, +): string[] { + const resolved = resolveSvgVersion(icon, version) + const v = ver || get(latestVersion) || CURRENT_VERSION + const localUrl = `${base}/icons/${icon.name}/${icon.name}-${resolved}.svg` + const npmUrl = `https://cdn.jsdelivr.net/npm/devicon@${v}/icons/${icon.name}/${icon.name}-${resolved}.svg` + const ghUrl = `https://cdn.jsdelivr.net/gh/devicons/devicon@master/icons/${icon.name}/${icon.name}-${resolved}.svg` + + if (import.meta.env.DEV) { + return [localUrl, npmUrl, ghUrl] + } + return [npmUrl, ghUrl, localUrl] +} + +export function getIconSvgUrl( + icon: IIcon, + version: IconVersions | string, + ver?: string, +): string { + const candidates = getIconSvgCandidates(icon, version, ver) + return candidates[0] +} + +export function getIconFallbackSvgUrl( + icon: IIcon, + version: IconVersions | string, + currentSrc?: string, +): string { + const candidates = getIconSvgCandidates(icon, version) + if (!currentSrc) return candidates[1] || candidates[0] + const index = candidates.findIndex( + (c) => currentSrc === c || currentSrc.endsWith(c), + ) + if (index >= 0 && index < candidates.length - 1) { + return candidates[index + 1] + } + return candidates[candidates.length - 1] +} + +async function fetchLatestVersion(): Promise { + try { + const npmResponse = await fetch("https://registry.npmjs.org/devicon/latest") + if (npmResponse.ok) { + const npmData = await npmResponse.json() + if (npmData?.version) { + return npmData.version + } + } + } catch { + // try github fallback + } + + try { + const response = await fetch( + "https://api.github.com/repos/devicons/devicon/releases/latest", + ) + if (response.ok) { + const data = await response.json() + if (data?.tag_name) { + return data.tag_name.replace(/^v/, "") + } + } + } catch { + // ignore + } + + return CURRENT_VERSION +} + +export async function fetchIconsData(): Promise { + isLoading.set(true) + error.set(null) + + try { + const version = await fetchLatestVersion() + latestVersion.set(version) + + let response: Response | null = null + + // 1. Try local devicon.json first (enables local dev without CDN latency) + try { + const localResponse = await fetch(`${base}/devicon.json`) + if (localResponse.ok) { + response = localResponse + } + } catch { + // fallback to CDN + } + + // 2. Fall back to jsdelivr npm CDN + if (!response) { + try { + const cdnResponse = await fetch( + `https://cdn.jsdelivr.net/npm/devicon@${version}/devicon.json`, + ) + if (cdnResponse.ok) { + response = cdnResponse + } + } catch { + // fallback to github master + } + } + + // 3. Fall back to GitHub master + if (!response) { + response = await fetch( + "https://raw.githubusercontent.com/devicons/devicon/master/devicon.json", + ) + } + + if (!response.ok) { + throw new Error(`Failed to fetch icons: ${response.statusText}`) + } + + const iconsData: IIcon[] = await response.json() + + const availableTagsSet = new Set() + iconsData.forEach((icon) => { + if (icon.tags && Array.isArray(icon.tags)) { + for (const tag of icon.tags) { + availableTagsSet.add(tag) + } + } + }) + + const sortedTags = Array.from(availableTagsSet).sort() + + icons.set(iconsData) + availableTags.set(sortedTags) + + initializeDarkMode() + } catch (err) { + const message = err instanceof Error ? err.message : String(err) + console.error("Error fetching icons:", err) + error.set(message) + icons.set([]) + availableTags.set([]) + } finally { + isLoading.set(false) + } +} + +function initializeDarkMode(): void { + if (typeof window !== "undefined") { + const storedDarkMode = localStorage.getItem("darkMode") + const isDark = storedDarkMode !== "false" + + isDarkMode.set(isDark) + + if (isDark) { + document.documentElement.classList.add("dark") + } else { + document.documentElement.classList.remove("dark") + } + } +} + +export function initializeStore( + initialIcons: IIcon[], + initialTags: string[], +): void { + icons.set(initialIcons) + availableTags.set(initialTags) + initializeDarkMode() +} + +const CATEGORY_TAG_MAP: Record = { + all: [], + language: ["language", "programming"], + framework: ["framework"], + database: ["database", "nosql", "sql"], + library: ["library"], + cloud: ["cloud", "devops", "hosting", "server", "cloud-computing"], + tool: [ + "tool", + "editor", + "ide", + "automation", + "build", + "vcs", + "version-control", + "design", + ], +} + +export const categoryCounts: Readable> = derived( + [icons], + ([$icons]) => { + const counts: Record = { + all: $icons.length, + language: 0, + framework: 0, + database: 0, + library: 0, + cloud: 0, + tool: 0, + } + + for (const icon of $icons) { + const lowerTags = (icon.tags || []).map((t) => t.toLowerCase()) + + for (const cat of [ + "language", + "framework", + "database", + "library", + "cloud", + "tool", + ] as const) { + const mapped = CATEGORY_TAG_MAP[cat] + const matches = lowerTags.some( + (tag) => tag === cat || mapped?.includes(tag), + ) + if (matches) { + counts[cat]++ + } + } + } + + return counts + }, +) + +export const filteredIcons: Readable = derived( + [icons, searchTerm, selectedTags, selectedCategory, selectedVersionFilter, sortOption], + ([ + $icons, + $searchTerm, + $selectedTags, + $selectedCategory, + $selectedVersionFilter, + $sortOption, + ]) => { + const query = $searchTerm.trim().toLowerCase() + + const filtered = $icons.filter((icon) => { + const matchesSearch = + query === "" || + icon.name.toLowerCase().includes(query) || + Boolean(icon.altnames?.some((alt) => alt.toLowerCase().includes(query))) + + const matchesTags = + $selectedTags.length === 0 || + Boolean(icon.tags && $selectedTags.every((tag) => icon.tags.includes(tag))) + + const matchesCategory = + $selectedCategory === "all" || + Boolean( + icon.tags?.some((tag) => { + const lowerTag = tag.toLowerCase() + if (lowerTag === $selectedCategory) return true + const mapped = CATEGORY_TAG_MAP[$selectedCategory] + return mapped?.includes(lowerTag) + }), + ) + + const allVariants = getIconAllVariants(icon) + const matchesVersion = + $selectedVersionFilter === "all" || + Boolean( + allVariants.some((v) => + $selectedVersionFilter === "wordmark" + ? v.includes("wordmark") + : v.startsWith($selectedVersionFilter), + ), + ) + + return matchesSearch && matchesTags && matchesCategory && matchesVersion + }) + + if ($sortOption === "name-asc") { + return [...filtered].sort((a, b) => a.name.localeCompare(b.name)) + } + if ($sortOption === "name-desc") { + return [...filtered].sort((a, b) => b.name.localeCompare(a.name)) + } + if ($sortOption === "most-versions") { + return [...filtered].sort((a, b) => { + return getIconAllVariants(b).length - getIconAllVariants(a).length + }) + } + if ($sortOption === "newest") { + return [...filtered].reverse() + } + + return filtered + }, +) + +export const totalVersions: Readable = derived([icons], ([$icons]) => + $icons.reduce( + (acc, icon) => acc + getIconAllVariants(icon).length, + 0, + ), +) + +export function setSearchTerm(term: string): void { + searchTerm.set(term) +} + +export function setSelectedTags(tags: string[]): void { + selectedTags.set(tags) +} + +export function clearFilters(): void { + searchTerm.set("") + selectedTags.set([]) + selectedCategory.set("all") + selectedVersionFilter.set("all") +} + +export function toggleDarkMode(): void { + if (typeof window !== "undefined") { + document.documentElement.classList.add("disable-transitions") + } + + isDarkMode.update((current) => { + const newValue = !current + + if (typeof window !== "undefined") { + localStorage.setItem("darkMode", newValue.toString()) + + if (newValue) { + document.documentElement.classList.add("dark") + } else { + document.documentElement.classList.remove("dark") + } + + // Flush computed style reflow so theme changes instantly without intermediate transitions + window.getComputedStyle(document.documentElement).opacity + + requestAnimationFrame(() => { + document.documentElement.classList.remove("disable-transitions") + }) + } + + return newValue + }) +} + +export function openIconModal(icon: IIcon): void { + selectedIcon.set(icon) + isModalOpen.set(true) +} + +export function closeIconModal(): void { + selectedIcon.set(null) + isModalOpen.set(false) +} diff --git a/site/src/lib/types/types.ts b/site/src/lib/types/types.ts new file mode 100644 index 000000000..423b4ff2b --- /dev/null +++ b/site/src/lib/types/types.ts @@ -0,0 +1,40 @@ +export const CURRENT_VERSION = "2.18.0" + +export type SortOption = "name-asc" | "name-desc" | "most-versions" | "newest" + +export type CategoryOption = + | "all" + | "language" + | "framework" + | "database" + | "library" + | "cloud" + | "tool" + +export type IntegrationTab = "react" | "html" | "svg" + +export type IconVersions = + | "plain" + | "line" + | "original" + | "plain-wordmark" + | "line-wordmark" + | "original-wordmark" + +export type IconType = "svg" | "font" + +export interface IIcon { + name: string + altnames: string[] + tags: string[] + versions: { + svg: IconVersions[] + font: IconVersions[] + } + color: string + aliases: { + base: IconVersions + alias: IconVersions + }[] + theme?: "dark" | "light" | "color" +} diff --git a/site/src/routes/+layout.svelte b/site/src/routes/+layout.svelte new file mode 100644 index 000000000..1ee51f86b --- /dev/null +++ b/site/src/routes/+layout.svelte @@ -0,0 +1,33 @@ + + +
+ + + + + + + + + + +
+ {@render children()} +
+
+
diff --git a/site/src/routes/+layout.ts b/site/src/routes/+layout.ts new file mode 100644 index 000000000..440bfd494 --- /dev/null +++ b/site/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const prerender = true +export const ssr = true diff --git a/site/src/routes/+page.svelte b/site/src/routes/+page.svelte new file mode 100644 index 000000000..b08bfb285 --- /dev/null +++ b/site/src/routes/+page.svelte @@ -0,0 +1,103 @@ + + + + DEVICON | All programming languages and development tools related icons font + + + +
+ + + + +
+ {#if $error} +
+
+

+ Failed to load icon library: {$error} +

+ +
+
+ {/if} + + + +
+ + + + + + {#if $isModalOpen} + + {/if} + + + {#if showBackToTop} + + {/if} +
diff --git a/site/static/CNAME b/site/static/CNAME new file mode 100644 index 000000000..08253f49a --- /dev/null +++ b/site/static/CNAME @@ -0,0 +1 @@ +devicon.dev diff --git a/docs/logos/android-chrome-144x144.png b/site/static/android-chrome-144x144.png similarity index 100% rename from docs/logos/android-chrome-144x144.png rename to site/static/android-chrome-144x144.png diff --git a/docs/logos/android-chrome-192x192.png b/site/static/android-chrome-192x192.png similarity index 100% rename from docs/logos/android-chrome-192x192.png rename to site/static/android-chrome-192x192.png diff --git a/docs/logos/android-chrome-36x36.png b/site/static/android-chrome-36x36.png similarity index 100% rename from docs/logos/android-chrome-36x36.png rename to site/static/android-chrome-36x36.png diff --git a/docs/logos/android-chrome-48x48.png b/site/static/android-chrome-48x48.png similarity index 100% rename from docs/logos/android-chrome-48x48.png rename to site/static/android-chrome-48x48.png diff --git a/docs/logos/android-chrome-72x72.png b/site/static/android-chrome-72x72.png similarity index 100% rename from docs/logos/android-chrome-72x72.png rename to site/static/android-chrome-72x72.png diff --git a/docs/logos/android-chrome-96x96.png b/site/static/android-chrome-96x96.png similarity index 100% rename from docs/logos/android-chrome-96x96.png rename to site/static/android-chrome-96x96.png diff --git a/docs/logos/apple-touch-icon-114x114.png b/site/static/apple-touch-icon-114x114.png similarity index 100% rename from docs/logos/apple-touch-icon-114x114.png rename to site/static/apple-touch-icon-114x114.png diff --git a/docs/logos/apple-touch-icon-120x120.png b/site/static/apple-touch-icon-120x120.png similarity index 100% rename from docs/logos/apple-touch-icon-120x120.png rename to site/static/apple-touch-icon-120x120.png diff --git a/docs/logos/apple-touch-icon-144x144.png b/site/static/apple-touch-icon-144x144.png similarity index 100% rename from docs/logos/apple-touch-icon-144x144.png rename to site/static/apple-touch-icon-144x144.png diff --git a/docs/logos/apple-touch-icon-152x152.png b/site/static/apple-touch-icon-152x152.png similarity index 100% rename from docs/logos/apple-touch-icon-152x152.png rename to site/static/apple-touch-icon-152x152.png diff --git a/docs/logos/apple-touch-icon-180x180.png b/site/static/apple-touch-icon-180x180.png similarity index 100% rename from docs/logos/apple-touch-icon-180x180.png rename to site/static/apple-touch-icon-180x180.png diff --git a/docs/logos/apple-touch-icon-57x57.png b/site/static/apple-touch-icon-57x57.png similarity index 100% rename from docs/logos/apple-touch-icon-57x57.png rename to site/static/apple-touch-icon-57x57.png diff --git a/docs/logos/apple-touch-icon-60x60.png b/site/static/apple-touch-icon-60x60.png similarity index 100% rename from docs/logos/apple-touch-icon-60x60.png rename to site/static/apple-touch-icon-60x60.png diff --git a/docs/logos/apple-touch-icon-72x72.png b/site/static/apple-touch-icon-72x72.png similarity index 100% rename from docs/logos/apple-touch-icon-72x72.png rename to site/static/apple-touch-icon-72x72.png diff --git a/docs/logos/apple-touch-icon-76x76.png b/site/static/apple-touch-icon-76x76.png similarity index 100% rename from docs/logos/apple-touch-icon-76x76.png rename to site/static/apple-touch-icon-76x76.png diff --git a/docs/logos/apple-touch-icon-precomposed.png b/site/static/apple-touch-icon-precomposed.png similarity index 100% rename from docs/logos/apple-touch-icon-precomposed.png rename to site/static/apple-touch-icon-precomposed.png diff --git a/docs/logos/apple-touch-icon.png b/site/static/apple-touch-icon.png similarity index 100% rename from docs/logos/apple-touch-icon.png rename to site/static/apple-touch-icon.png diff --git a/docs/logos/favicon-16x16.png b/site/static/favicon-16x16.png similarity index 100% rename from docs/logos/favicon-16x16.png rename to site/static/favicon-16x16.png diff --git a/docs/logos/favicon-32x32.png b/site/static/favicon-32x32.png similarity index 100% rename from docs/logos/favicon-32x32.png rename to site/static/favicon-32x32.png diff --git a/docs/logos/favicon-96x96.png b/site/static/favicon-96x96.png similarity index 100% rename from docs/logos/favicon-96x96.png rename to site/static/favicon-96x96.png diff --git a/docs/logos/favicon.ico b/site/static/favicon.ico similarity index 100% rename from docs/logos/favicon.ico rename to site/static/favicon.ico diff --git a/docs/logos/mstile-144x144.png b/site/static/mstile-144x144.png similarity index 100% rename from docs/logos/mstile-144x144.png rename to site/static/mstile-144x144.png diff --git a/docs/logos/mstile-150x150.png b/site/static/mstile-150x150.png similarity index 100% rename from docs/logos/mstile-150x150.png rename to site/static/mstile-150x150.png diff --git a/docs/logos/mstile-310x150.png b/site/static/mstile-310x150.png similarity index 100% rename from docs/logos/mstile-310x150.png rename to site/static/mstile-310x150.png diff --git a/docs/logos/mstile-310x310.png b/site/static/mstile-310x310.png similarity index 100% rename from docs/logos/mstile-310x310.png rename to site/static/mstile-310x310.png diff --git a/docs/logos/mstile-70x70.png b/site/static/mstile-70x70.png similarity index 100% rename from docs/logos/mstile-70x70.png rename to site/static/mstile-70x70.png diff --git a/docs/logos/safari-pinned-tab.svg b/site/static/safari-pinned-tab.svg similarity index 100% rename from docs/logos/safari-pinned-tab.svg rename to site/static/safari-pinned-tab.svg diff --git a/site/svelte.config.js b/site/svelte.config.js new file mode 100644 index 000000000..a3dc7e889 --- /dev/null +++ b/site/svelte.config.js @@ -0,0 +1,27 @@ +import adapter from "@sveltejs/adapter-static" +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte" + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://svelte.dev/docs/kit/integrations + // for more information about preprocessors + preprocess: vitePreprocess(), + + kit: { + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter({ + pages: "build", + assets: "build", + fallback: "404.html", + precompress: false, + strict: true, + }), + paths: { + base: process.env.BASE_PATH ?? "", + }, + }, +} + +export default config diff --git a/site/tsconfig.json b/site/tsconfig.json new file mode 100644 index 000000000..f4d0a0e16 --- /dev/null +++ b/site/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/site/vite.config.ts b/site/vite.config.ts new file mode 100644 index 000000000..f9a523f11 --- /dev/null +++ b/site/vite.config.ts @@ -0,0 +1,80 @@ +import { sveltekit } from "@sveltejs/kit/vite" +import { defineConfig, type Plugin, type Connect } from "vite" +import tailwindcss from "@tailwindcss/vite" +import fs from "node:fs" +import path from "node:path" +import { fileURLToPath } from "node:url" + +function deviconLocalPlugin(): Plugin { + const jsonPath = fileURLToPath(new URL("../devicon.json", import.meta.url)) + const iconsDir = fileURLToPath(new URL("../icons", import.meta.url)) + + const middleware: Connect.NextHandleFunction = (req, res, next) => { + if (!req.url) { + return next() + } + const cleanUrl = req.url.split("?")[0] + + if (cleanUrl === "/devicon.json" || cleanUrl.endsWith("/devicon.json")) { + if (fs.existsSync(jsonPath)) { + res.setHeader("Content-Type", "application/json") + res.setHeader("Cache-Control", "no-cache") + fs.createReadStream(jsonPath).on("error", next).pipe(res) + return + } + } + + const iconsPrefix = "/icons/" + const iconsIndex = cleanUrl.indexOf(iconsPrefix) + if (iconsIndex !== -1) { + const relative = decodeURIComponent(cleanUrl.slice(iconsIndex + iconsPrefix.length)) + const filePath = path.resolve(iconsDir, relative) + if ( + filePath.startsWith(iconsDir) && + fs.existsSync(filePath) && + fs.statSync(filePath).isFile() + ) { + let content = fs.readFileSync(filePath, "utf8") + if (!content.includes("xmlns=")) { + content = content.replace(", +})